Public
Edited
Nov 6, 2023
Insert cell
Insert cell
Julia Kieserman, jbk392
Insert cell
Insert cell
Insert cell
Insert cell
data = (await FileAttachment('film_permits_reduced.csv').csv())
.map(permit => {
// create a function to parese strings into Dates
const parser = d3.timeParse('%Y-%m-%d %H:%M:%S');
permit['StartDateTime'] = parser(permit['StartDateTime']);
permit['EndDateTime'] = parser(permit['EndDateTime']);
// a permit can be for multiple zip codes
permit['ZipCodes'] = permit['ZipCode(s)'].split(', ');

//NEW STUFF//
permit
// remove columns that we are not using it
delete permit['SubCategoryName'];
delete permit['ZipCode(s)'];
return permit;
})
Insert cell
Insert cell
Insert cell
Insert cell
margin = ({ top: 5, bottom: 45, left: 75, right: 5 })
Insert cell
visWidth = width - margin.left - margin.right
Insert cell
visHeight = 400 - margin.top - margin.bottom
Insert cell
hoursByCategory = data.map(d => ({
'hours': d3.timeHour.count( d.StartDateTime, d.EndDateTime ),
'category': d.Category
}))
Insert cell
hoursByCategoryAggregated = d3.rollup(hoursByCategory, v => d3.sum(v, d => d.hours), d => d.category)
Insert cell
data1 = Array.from(hoursByCategoryAggregated)
Insert cell
Insert cell
maxDuration = d3.max(data1, d => d[1])
Insert cell
categories = Array.from(new Set(data.map( d => d.Category)))
Insert cell
x = d3.scaleBand()
.range([0, visWidth])
.domain(categories)
.padding(0.2)
Insert cell
y = d3.scaleLinear()
.domain([0, maxDuration])
.range([visHeight, 0])
Insert cell
Insert cell
xAxis = d3.axisBottom(x)
Insert cell
yAxis = d3.axisLeft(y)
Insert cell
Insert cell
{
const svg = d3.create('svg')
.attr('width', visWidth + margin.left + margin.right)
.attr('height', visHeight + margin.top + margin.bottom)

const g = svg.append('g')
.attr('transform', `translate(${margin.left}, ${margin.top})`)

g.selectAll('rect')
.data(data1)
.join('rect')
.attr('x', d => x(d[0]))
.attr('y', d => y(d[1]))
.attr('width', x.bandwidth())
.attr('height', d => visHeight - y(d[1]))
.attr('fill', 'steelblue')

// add axes
g.append('g')
.call(yAxis)
.call(g => g.select('.domain'))
// add a label for the y-axis
.append('text')
.attr('fill', 'black')
.attr('font-family', 'sans-serif')
.attr('y', margin.top)
.attr('x', -10)
.text('Hours');
g.append('g')
.attr('transform', `translate(0, ${visHeight})`)
.call(xAxis)
// add a label for the x-axis
.append('text')
.attr('fill', 'black')
.attr('font-family', 'sans-serif')
.attr('x', visWidth / 2)
.attr('y', 40)
.text('Categories');

return svg.node()
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// load GEoJSON data for NYC
nycGeo
Insert cell
Insert cell
mapMargin = ({ top: 10, right: 0, bottom: 0, left: 10})
Insert cell
mapWidth = 640 - margin.left - margin.right
Insert cell
mapHeight = 640 - margin.top - margin.bottom
Insert cell
path = d3.geoPath().projection(projection)
Insert cell
projection = d3.geoAlbers()
.fitSize( [mapWidth, mapHeight], nycGeo)
Insert cell
nycPath = d3.geoPath().projection(projection)
Insert cell
listOfZips = data.flatMap(d=> { return d.ZipCodes })
Insert cell
permitsByZip = d3.rollup(listOfZips, d => d.length, d => d)
Insert cell
getZip = feature => feature.properties.zcta
Insert cell
maxPermitsForZip = d3.max(permitsByZip, d => d[1])
Insert cell
zipColor = d3.scaleSequential()
.domain([0, maxPermitsForZip])
.interpolator(d3.interpolateBlues)
Insert cell
{
const svg = d3.create('svg')
.attr('width', mapHeight + mapMargin.left + mapMargin.right)
.attr('height', mapHeight + mapMargin.top + mapMargin.bottom);

const g = svg.append('g')
.attr('transform', `translate(${mapMargin.left}, ${mapMargin.top})`);

g.selectAll('path')
.data(nycGeo.features)
.join('path')
.attr('d', nycPath)
.attr('fill', d=> {
console.log('this is d');
console.log(d);
zipColor(permitsByZip.get(getZip(d)))})
.attr('stroke', 'white')
return svg.node();
}
Insert cell
Insert cell
topFiveCategoriesBySize = Array.from(d3.rollup(data, v => v.length, d => d.Category)).sort((a, b) => d3.descending(a[1], b[1])).splice(0,5)
Insert cell
keys = topFiveCategoriesBySize.map( data => data[0])
Insert cell
boroughsAndCategories = data.map( d => ({
borough: d.Borough,
category: d.Category
})).filter(d => keys.includes(d.category))
Insert cell
aggregatedData = d3.rollup(boroughsAndCategories, v => v.length, d => d.borough, d => d.category)
Insert cell
formattedData = Array.from(aggregatedData, (([borough, item]) => {
item.set('borough', borough)
item.set('total', d3.sum(item.values()))
return Object.fromEntries(item)
}))
Insert cell
Insert cell
maxPermits = d3.max(formattedData, d => d.total)
Insert cell
boroughs = [...new Set( data.map( d=> d.Borough))]
Insert cell
stack = d3.stack()
.keys(keys)(formattedData)
Insert cell
boroughX = d3.scaleBand()
.range([0, visWidth])
.domain(boroughs)
.padding(0.2)
Insert cell
boroughY = d3.scaleLinear()
.domain([0, maxPermits])
.range([visHeight, 0])
Insert cell
Insert cell
boroughXAxis = d3.axisBottom(boroughX)
Insert cell
boroughYAxis = d3.axisLeft(boroughY)
Insert cell
colorStack = d3.scaleOrdinal()
.domain(keys)
.range(d3.schemeSpectral[keys.length])
Insert cell
import {legend} from "@d3/color-legend"
Insert cell
Insert cell
{
const svg = d3.create('svg')
.attr('width', visWidth + margin.left + margin.right)
.attr('height', visHeight + margin.top + margin.bottom);

const g = svg.append('g')
.attr('transform', `translate(${margin.left}, ${margin.top})`);

g.append('g')
.attr('transform', `translate(0, ${visHeight})`)
.call(boroughXAxis)

g.append('g')
.call(boroughYAxis)

// data
g.append('g')
.selectAll('g')
.data(stack)
.join('g')
.attr('fill', d => colorStack(d.key))
.selectAll('rect')
.data( d => d)
.join('rect')
.attr('x', d => {
console.log('what did I do here?');
console.log(d);
return boroughX(d.data.borough)})
.attr('y', d => boroughY(d[1]))
.attr('height', d=>boroughY(d[0]) - boroughY(d[1]))
.attr('width', boroughX.bandwidth())

//legend
g.append('g')
.attr('class', 'legend')
//.attr('transform', `translate`)
.selectAll('rect')
.data(stack)
.enter()
.append('rect')
.attr('x', 0)
.attr('y', function(d,i){
return i * 18;
})
.attr('width', 12)
.attr('height', 12)
.attr('fill', function(d,i){
console.log(colorStack(i));
return colorStack(i);
})
.selectAll('text')
.data(stack)
.enter()
.append('text')
.text(function(d){
return d.key;
})
.attr('x', 15)
.attr('y', function(d, i){
return i * 18;
})
.attr('text-anchor', 'start')
.attr('alignment-baseline', 'hanging');

return Object.assign(svg.node(), {scales: {colorStack}});
}
Insert cell
Insert cell
// add cells here
Insert cell
Insert cell
// add cells here
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more