Public
Edited
Feb 24, 2023
Insert cell
Insert cell
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(', ');
// remove columns that we are not using it
delete permit['SubCategoryName'];
delete permit['ZipCode(s)'];
return permit;
})
Insert cell
Insert cell
import {legend} from "@d3/color-legend"
Insert cell
Insert cell
tmp_data = d3.rollup(data, agg => d3.mean(agg, f => d3.timeHour.count(f.StartDateTime, f.EndDateTime)), cat => cat.Category)

Insert cell
totalWidth = 1150
Insert cell
totalHeight = 750
Insert cell
margin = ({top: 20, bottom: 45, left: 75, right: 10})
Insert cell
visWidth = totalWidth - margin.left - margin.right
Insert cell
visHeight = totalHeight - margin.top - margin.bottom
Insert cell
categories = Array.from(tmp_data.keys())
Insert cell
x = d3.scaleBand()
.domain(categories)
.range( [0, visWidth])
.padding(0.2)
Insert cell
avg_dur = Array.from(tmp_data.values())
Insert cell
max_avg = d3.max(avg_dur)
Insert cell
y = d3.scaleLinear()
.domain([0, max_avg])
.range([visHeight, margin.top])
Insert cell
xAxis = d3.axisBottom(x)
Insert cell
yAxis = d3.axisLeft(y)
Insert cell
vis = {

const svg = d3.create('svg')
.attr('width', totalWidth)
.attr('height', totalHeight);

const g = svg.append("g")
.attr('transform', `translate(${margin.left}, ${margin.top})`);
// bind our data to rectangles
g.selectAll('rect')
.data(tmp_data)
.join('rect')
// set attributes for each bar
//vertical rectangles need to have baseline modified since d3 y baseline starts at the top
.attr('x', ([categories, val]) => x(categories))
.attr('y', ([categories, val]) => y(val))
.attr('width', x.bandwidth())
.attr('height', ([categories, val]) => y(0) - y(val))
.attr('fill', 'steelblue');
// add a group for the x-axis
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("Category")

g.append('g')
.append('text')
.attr('fill', 'black')
.attr('font-family', 'sans-serif')
.attr('y', 0)
.attr('x', 400)
.text("Average Permit Duration per Category");

g.append('g')
.call(yAxis)
.append('text')
.attr('fill', 'black')
.attr('font-family', 'sans-serif')
.attr('y', visHeight/ 2)
.attr('x', 0)
.text("Duration (Hr)");


return svg.node();
}
Insert cell
Insert cell
Insert cell
tele_only = d3.filter(data, d => d.Category == "Television")
Insert cell
tele_zip = d3.merge(tele_only.map(d=>d.ZipCodes))
Insert cell
zip_count = d3.sort(d3.rollup(tele_zip, v => v.length, d => d), d=>d[1]).reverse()
Insert cell
getZip = feature => feature.properties.zcta
Insert cell
zip_data = {
var zip_data = new Object()
for (var i = 0; i < zip_count.length; i++) {
zip_data[zip_count[i][0]] = zip_count[i][1];
}

return zip_data
}
Insert cell
zip_data
Insert cell
//params taken from barchart walkthrough
// the setup for this map is largely taken from the NYU Map visualization tutorial
Insert cell
path = d3.geoPath().projection(d3.geoAlbers().fitSize([visWidth, visHeight], nycGeo))
Insert cell
color = d3.scaleSequential()
.domain([0, d3.max(Object.values(zip_data))])
.interpolator(d3.interpolateBlues)
.unknown(lightgray)
Insert cell
Insert cell
{
const svg = d3.create('svg')
.attr('width', visWidth)
.attr('height', visHeight);

const g = svg.append("g")
.attr("transform", `translate(${margin.left}, ${margin.top})`);
// draw map
g.selectAll("path")
.data(nycGeo.features)
.join("path")
.attr("d", path)
.attr("fill", d => color(zip_data[getZip(d)]))
.attr("stroke", "white")
.attr("legend", legendk)

return svg.node();
}
Insert cell
Insert cell
Insert cell
//cat vs cat vs measure, matrix
data_boro = d3.rollup(data, g => g.length , d => d.Borough, d => d.Category)
Insert cell
boroughs = Array.from(data_boro.keys())
Insert cell
//categories are defined from a previous code block (from question 1), we can redefine it but it is easier to draw from that example
categories
Insert cell
//here we define the rollup map as an array of arrays...
//should make it easier to manipulate than maps of keys
data_boro_arr = d3.rollups(data, g=>g.length, d=>d.Borough, d=>d.Category)
Insert cell
//redefine the rollup map to generate an array of objects to be used in the visualization generation
arr = {
var arr = new Array()
for(let i = 0; i < data_boro_arr.length; i++){
var boro = data_boro_arr[i][0]
for(let j = 0; j < data_boro_arr[i][1].length; j++){
var obj = {'Borough': boro, 'Category': data_boro_arr[i][1][j][0], 'Value': data_boro_arr[i][1][j][1]}
arr.push(obj)
}
}
return arr
}
Insert cell
Insert cell
Insert cell
Insert cell
//this section is similar to the bar chart where we scale the values to the size of the figure
xCat = d3.scalePoint().domain(categories).range([0, catWidth]).padding(0.5)
Insert cell
yBoro = d3.scalePoint().domain(boroughs).range([0, boroHeight]).padding(0.7)
Insert cell
Insert cell
Insert cell
//scaling the value of the array to the size of each circle
radius = d3.scaleSqrt().domain([0, highest_val]).nice().range([0, maxRadius])
Insert cell
xAxisCat = d3.axisBottom(xCat)
Insert cell
yAxisBoro = d3.axisLeft(yBoro)
Insert cell
//legend is taken directly from the SVG and D3 basics practice tutorial notebook

sizeLegend = g => {
g.attr("transform", `translate(900 ,20)`)
.attr("font-family", "sans-serif")
.attr("font-size", 12);
g.append('text')
.attr('font-weight', 'bold')
.text('Num Permit');
const legend = g.selectAll("g")
.data([1000, 2500, 5000, 7500])
.join("g")
.attr("transform", (d, i) => `translate(0, ${(i + 1) * 2 * radius(d)})`);
legend.append("circle")
.attr("r", d => radius(d))
.attr("fill", "steelblue");

legend.append("text")
.attr("x", maxRadius + 5)
.attr("dominant-baseline", "middle")
.text(d => d);
}
Insert cell
{
// we will use the same margins as defined in Question 1.

const svg = d3.create('svg')
.attr('width', catWidth + 200 + 250)
.attr('height', boroHeight + 100+ 50);

const g = svg.append("g")
.attr("transform", `translate(70, 70)`);
const matrix = g.append('g');
//generate circles whose sizes are scaled off of the value in the array
matrix.selectAll("circle")
.data(arr)
.join("circle")
.attr("cy", d => yBoro(d.Borough))
.attr("cx", d => xCat(d.Category))
.attr("r", d => radius(d.Value))
.attr("fill", "steelblue");
// legend
g.append("g").call(sizeLegend)


g.append('g')
.append('text')
.attr('fill', 'black')
.attr('font-family', 'sans-serif')
.attr('y', -20)
.attr('x', 400)
.text("Number of Permits per Categories per Borough");
// axes
g.append("g").call(xAxisCat);
g.append("g").call(yAxisBoro);
return svg.node();
}
Insert cell
Insert cell
Insert cell
// add code 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