Public
Edited
Jan 1, 2024
Insert cell
Insert cell
data = FileAttachment("311_email_export.json").json()
Insert cell
function precinct(location) {
location = location.replace(/\s\s+/g, ' ')
if (["WEST 44 STREET",
"WEST 45 STREET",
"WEST 46 STREET",
"WEST 47 STREET",
"WEST 48 STREET",
"WEST 49 STREET",
"WEST 50 STREET",
"WEST 52 STREET",
"WEST 54 STREET",
"WEST 55 STREET",
"WEST 56 STREET",
"BROADWAY",
"WEST 57 STREET",
"SEVENTH AVENUE",
"EIGHTH AVENUE",
"NINTH AVENUE",
"TENTH AVENUE",
"10 AVENUE",
"11 AVENUE",
"12 AVENUE",
"COLUMBUS CIRCLE"
].some(s => location.includes(s))) {
return "Midtown North Precinct"
}

if (["CENTRAL PARK WEST",
"WEST 83 STREET",
"WEST 84 STREET",
"COLUMBUS AVENUE"
].some(s => location.includes(s))) {
return "20th Precinct"
}

return "Other Precincts"
}
Insert cell
data2 = data.filter(d => d.Agency == "New York City Police Department" && !d.ResolutionAction.includes("below")).map(d => {
d.dt = new Date(d.DateTimeSubmitted);
["The Police Department responded to the complaint and determined that",
"The Police Department responded to the complaint and with the information available",
"The Police Department responded to the complaint and ",
"The Police Department reviewed your complaint and",
"The Police Department ",
"The Police Department responded and", "This complaint "].forEach(prefix => {
if (d.ResolutionAction && d.ResolutionAction.startsWith(prefix)) {
d.ResolutionAction = d.ResolutionAction.slice(prefix.length)
}
if (d.ResolutionAction) {
d.ResolutionAction = d.ResolutionAction.replace(/\.$/, '')
}
});
d.precinct = precinct(d.Address.FullAddress)
return d})
Insert cell
data2
X
dt
Y
ResolutionAction
Color
ResolutionAction
Size
Facet X
Facet Y
Mark
Auto
Type Chart, then Shift-Enter. Ctrl-space for more options.

Insert cell
data2
X
dt
Y
ResolutionAction
Color
ResolutionAction
Size
Facet X
Facet Y
precinct
Mark
Auto
Type Chart, then Shift-Enter. Ctrl-space for more options.

Insert cell
Plot.plot({
color: { legend: true },
y: {axis: null},
x: {"grid":true},
// fy: {anchor:"top"},
width,
marks: [
Plot.dot(data2, {
fy: "precinct",
x: "dt",
y: "ResolutionAction",
stroke: "ResolutionAction",
tip: true
})
]
})
Insert cell
chart = {
const width = 950;
const height = 300;
const margin = {top: 20, right: 30, bottom: 30, left: 15};
let dateMinMax = d3.extent(data2, d => d.dt)
dateMinMax[0] = d3.utcMonth(dateMinMax[0])
const x = d3.scaleUtc()
.domain(dateMinMax)
.range([margin.left, width - margin.right]);

const reasons = d3.group(data2, d => d.ResolutionAction);
const reasonsSet = new Set(reasons.keys());

const y = d3.scalePoint()
.domain(d3.sort(reasons.keys()))
.rangeRound([margin.top, height - margin.bottom])
.padding(1);
const color = d3.scaleOrdinal()
.domain(reasonsSet)
.range(d3.schemeCategory10)// [reasonsSet.size])
.unknown("#ccc");

const svg = d3.create("svg")
.attr("width", width)
.attr("height", height);

svg.append("g")
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x));

// svg.append("g")
// .attr("transform", `translate(${margin.left},0)`)
// .call(d3.axisLeft(y));


svg.append("g")
.selectAll("text")
.data(reasonsSet)
.join("text")
.attr("transform", reason => `translate(${margin.left},${y(reason) - 10})`)
.style("font", "12px sans-serif")
.text(reason=>reason)

// Add a g container for each reason.
const g = svg.append("g")
.attr("text-anchor", "end")
.style("font", "10px sans-serif")
.selectAll()
.data(reasons)
.join("g")
.attr("transform", ([reason]) => `translate(0,${y(reason)})`);

// Append a grey line to each container.
g.append("line")
.attr("stroke", "#bbb")
.attr("x1", x(dateMinMax[0]))
.attr("x2", x(dateMinMax[1]));

// Append the dots to each container.
g.append("g")
.selectAll()
.data(([, values]) => values)
.join("circle")
.attr("cx", (d) => x(d.dt))
.attr("fill", (d) => color(d.ResolutionAction))
.attr("r", 4.5);

return svg.node();
}
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more