We utilized only Javascript and SVG to produce this data analysis / visualization report.
This report is prepared by
For this assignment the authors opted to spend time and search for Public Safety Open Data available online. We found several good resources at the state level for several states and also for cities at a more local level. In particular, New York City maintains an easy-to-use open data website at (https://nycopendata.socrata.com/)
The data for this exercise/report came from here and looks like:
[
{
"Incident Type": "Fire-Brush",
"Borough": "Staten Island"
},
{
"Incident Type": "Law Enforcement-Suspicious Package",
"Borough": "Manhattan"
},
{
"Incident Type": "Fire-1st Alarm",
"Borough": "Manhattan"
},
{
"Incident Type": "Structural-Other",
"Borough": "Bronx"
},
{
"Incident Type": "Fire-2nd Alarm",
"Borough": "Queens"
},
{
"Incident Type": "Utility-Other",
"Borough": "Queens"
},
{
"Incident Type": "Utility-Gas High Pressure",
"Borough": "Brooklyn"
},
{
"Incident Type": "Fire-1st Alarm",
"Borough": "Brooklyn"
},
{
"Incident Type": "Fire-2nd Alarm",
"Borough": "Staten Island"
},
{
"Incident Type": "Fire-3rd Alarm",
"Borough": "Manhattan"
},
{
"Incident Type": "Utility-Steam Main",
"Borough": "Manhattan"
},
{
"Incident Type": "Utility-Steam Main",
"Borough": "Manhattan"
},
{
"Incident Type": "Structural-Collapse",
"Borough": "Manhattan"
},
{
"Incident Type": "Fire-2nd Alarm",
"Borough": "Brooklyn"
},
{
"Incident Type": "Law Enforcement-Other",
"Borough": "Brooklyn"
},
{
"Incident Type": "Utility-Network Condition",
"Borough": "Manhattan"
},
{
"Incident Type": "Utility-Water Service Line",
"Borough": "Queens"
},
{
"Incident Type": "Fire-2nd Alarm",
"Borough": "Bronx"
},
{
"Incident Type": "Utility-Water Service Line",
"Borough": "Manhattan"
},
{
"Incident Type": "Utility-Water Main",
"Borough": "Bronx"
},
{
"Incident Type": "Fire-2nd Alarm",
"Borough": "Queens"
},
{
"Incident Type": "Law Enforcement-Other",
"Borough": "Manhattan"
},
{
"Incident Type": "Utility-Gas Service Line",
"Borough": "Manhattan"
},
{
"Incident Type": "Utility-Steam Main",
"Borough": "Manhattan"
},
{
"Incident Type": "Fire-Sensitive Location",
"Borough": "Manhattan"
},
{
"Incident Type": "Fire-2nd Alarm",
"Borough": "Queens"
},
{
"Incident Type": "Fire-2nd Alarm",
"Borough": "Brooklyn"
},
{
"Incident Type": "Fire-2nd Alarm",
"Borough": "Bronx"
},
{
"Incident Type": "Fire-Residential",
"Borough": "Queens"
},
{
"Incident Type": "Transportation-Other",
"Borough": "Staten Island"
},
{
"Incident Type": "Fire-Collapse",
"Borough": "Bronx"
},
{
"Incident Type": "Fire-10-77 (Residential High Rise Fire)",
"Borough": "Bronx"
},
{
"Incident Type": "Fire-2nd Alarm",
"Borough": "Brooklyn"
},
{
"Incident Type": "Fire-4th Alarm",
"Borough": "Queens"
},
{
"Incident Type": "Fire-2nd Alarm",
"Borough": "Bronx"
},
{
"Incident Type": "Utility-Water Main",
"Borough": "Queens"
},
{
"Incident Type": "Fire-2nd Alarm",
"Borough": "Manhattan"
},
{
"Incident Type": "Structural-Scaffold",
"Borough": "Brooklyn"
},
{
"Incident Type": "Marine-Freight Wet Goods",
"Borough": "Staten Island"
},
{
"Incident Type": "Structural-Parapet",
"Borough": "Queens"
},
{
"Incident Type": "Structural-Parapet",
"Borough": "Queens"
},
{
"Incident Type": "Utility-Water Service Line",
"Borough": "Brooklyn"
},
{
"Incident Type": "Fire-4th Alarm",
"Borough": "Staten Island"
},
{
"Incident Type": "Administration-Meeting",
"Borough": "Manhattan"
},
{
"Incident Type": "Utility-Other",
"Borough": "Brooklyn"
},
{
"Incident Type": "Rescue-Confined Space",
"Borough": "Bronx"
},
{
"Incident Type": "HazMat-Fuel",
"Borough": "Manhattan"
},
{
"Incident Type": "Fire-1st Alarm",
"Borough": "Queens"
},
{
"Incident Type": "Structural-Other",
"Borough": "Brooklyn"
},
{
"Incident Type": "Utility-Power Outage",
"Borough": "Brooklyn"
},
{
"Incident Type": "HazMat-Fuel",
"Borough": "Brooklyn"
},
{
"Incident Type": "Fire-Other",
"Borough": "Bronx"
},
{
"Incident Type": "Fire-School",
"Borough": "Brooklyn"
},
{
"Incident Type": "HazMat-High Carbon Monoxide",
"Borough": "Brooklyn"
},
{
"Incident Type": "Structural-Scaffold",
"Borough": "Manhattan"
},
{
"Incident Type": "Fire-10-77 (Residential High Rise Fire)",
"Borough": "Manhattan"
},
{
"Incident Type": "HazMat-Fuel",
"Borough": "Bronx"
},
{
"Incident Type": "Law Enforcement-Suspicious Package",
"Borough": "Brooklyn"
},
{
"Incident Type": "Fire-Manhole",
"Borough": "Manhattan"
},
{
"Incident Type": "Fire-3rd Alarm",
"Borough": "Manhattan"
},
{
"Incident Type": "Fire-3rd Alarm",
"Borough": "Staten Island"
},
{
"Incident Type": "Structural-Partial Collapse",
"Borough": "Brooklyn"
},
{
"Incident Type": "Fire-Other",
"Borough": "Manhattan"
},
{
"Incident Type": "Utility-Other",
"Borough": "Brooklyn"
},
{
"Incident Type": "Utility-Water Main",
"Borough": "Manhattan"
},
{
"Incident Type": "Utility-Other",
"Borough": "Bronx"
},
{
"Incident Type": "Law Enforcement-Suspicious Package",
"Borough": "Manhattan"
},
{
"Incident Type": "Fire-4th Alarm",
"Borough": "Brooklyn"
},
{
"Incident Type": "Utility-Gas Main Rupture",
"Borough": "Brooklyn"
},
{
"Inciden
<rect x="0"
y="345.5571635311143"
height="54.44283646888567"
width="80"
style="fill:red;
stroke-width:3;
stroke:rgb(0,0,0)" />
<text transform="translate(0 345.5571635311143)">
Staten Island
</text>
<rect x="90"
y="20"
height="380"
width="80"
style="fill:red;
stroke-width:3;
stroke:rgb(0,0,0)" />
<text transform="translate(90 20)">
Manhattan
</text>
<rect x="180"
y="277.9160636758321"
height="122.08393632416788"
width="80"
style="fill:red;
stroke-width:3;
stroke:rgb(0,0,0)" />
<text transform="translate(180 277.9160636758321)">
Bronx
</text>
<rect x="270"
y="221.8234442836469"
height="178.1765557163531"
width="80"
style="fill:red;
stroke-width:3;
stroke:rgb(0,0,0)" />
<text transform="translate(270 221.8234442836469)">
Queens
</text>
<rect x="360"
y="142.63386396526772"
height="257.3661360347323"
width="80"
style="fill:red;
stroke-width:3;
stroke:rgb(0,0,0)" />
<text transform="translate(360 142.63386396526772)">
Brooklyn
</text>
<rect x="450"
y="396.15050651230104"
height="3.849493487698987"
width="80"
style="fill:red;
stroke-width:3;
stroke:rgb(0,0,0)" />
<text transform="translate(450 396.15050651230104)">
Citywide
</text><rect x="${d.x}"
y="${d.y}"
height="${d.height}"
width="${d.width}"
style="fill:${d.color};
stroke-width:3;
stroke:rgb(0,0,0)" />
<text transform="translate(${d.x} ${d.y})">
${d.label}
</text>var groups = _.groupBy(data, 'Borough')
var counts = _.map(groups, function(value, key) {
return {'name': key, 'count': _.pluck(value, 'Incident Type').length}
})
console.log(counts)
// Produce a bottom-aligned bar chart with margins
function computeX(d, i) {
return i * 90
}
function computeHeight(d, i) {
var value = _.max(_.pluck(counts, 'count'))
return (d.count/(value/380))
}
function computeWidth(d, i) {
return 80
}
function computeY(d, i) {
return 400 - computeHeight(d, i)
}
function computeColor(d, i) {
return 'red'
}
function computeLabel(d, i) {
return d.name
}
var viz = _.map(counts, function(d, i) {
return {
x: computeX(d, i),
y: computeY(d, i),
height: computeHeight(d, i),
width: computeWidth(d, i),
color: computeColor(d, i),
label: computeLabel(d, i)
}
})
console.log(viz)
var result = _.map(viz, function(d) {
// invoke the compiled template function on each viz data
return template({d: d})
})
return result.join('\n')<rect x="0"
y="20"
height="380"
width="50"
style="fill:red;
stroke-width:3;
stroke:rgb(0,0,0)" />
<text transform="translate(0 20)">
Law Enforcement-Suspicious Package
</text>
<rect x="60"
y="241.5075376884422"
height="158.4924623115578"
width="50"
style="fill:red;
stroke-width:3;
stroke:rgb(0,0,0)" />
<text transform="translate(60 241.5075376884422)">
Fire-1st Alarm
</text>
<rect x="120"
y="178.49246231155777"
height="221.50753768844223"
width="50"
style="fill:red;
stroke-width:3;
stroke:rgb(0,0,0)" />
<text transform="translate(120 178.49246231155777)">
Fire-2nd Alarm
</text>
<rect x="180"
y="329.3467336683417"
height="70.6532663316583"
width="50"
style="fill:red;
stroke-width:3;
stroke:rgb(0,0,0)" />
<text transform="translate(180 329.3467336683417)">
Utility-Other
</text>
<rect x="240"
y="285.42713567839195"
height="114.57286432160805"
width="50"
style="fill:red;
stroke-width:3;
stroke:rgb(0,0,0)" />
<text transform="translate(240 285.42713567839195)">
Fire-3rd Alarm
</text>
<rect x="300"
y="331.2562814070352"
height="68.74371859296483"
width="50"
style="fill:red;
stroke-width:3;
stroke:rgb(0,0,0)" />
<text transform="translate(300 331.2562814070352)">
Structural-Collapse
</text>
<rect x="360"
y="333.16582914572865"
height="66.83417085427136"
width="50"
style="fill:red;
stroke-width:3;
stroke:rgb(0,0,0)" />
<text transform="translate(360 333.16582914572865)">
Utility-Water Service Line
</text>
<rect x="420"
y="144.12060301507535"
height="255.87939698492465"
width="50"
style="fill:red;
stroke-width:3;
stroke:rgb(0,0,0)" />
<text transform="translate(420 144.12060301507535)">
Utility-Water Main
</text>
<rect x="480"
y="336.9849246231156"
height="63.015075376884425"
width="50"
style="fill:red;
stroke-width:3;
stroke:rgb(0,0,0)" />
<text transform="translate(480 336.9849246231156)">
Structural-Scaffold
</text>
<rect x="540"
y="296.88442211055275"
height="103.11557788944724"
width="50"
style="fill:red;
stroke-width:3;
stroke:rgb(0,0,0)" />
<text transform="translate(540 296.88442211055275)">
Utility-Power Outage
</text>
<rect x="600"
y="300.7035175879397"
height="99.29648241206031"
width="50"
style="fill:red;
stroke-width:3;
stroke:rgb(0,0,0)" />
<text transform="translate(600 300.7035175879397)">
HazMat-High Carbon Monoxide
</text>
<rect x="660"
y="336.9849246231156"
height="63.015075376884425"
width="50"
style="fill:red;
stroke-width:3;
stroke:rgb(0,0,0)" />
<text transform="translate(660 336.9849246231156)">
Structural-Stability Problem
</text><rect x="${d.x}"
y="${d.y}"
height="${d.height}"
width="${d.width}"
style="fill:${d.color};
stroke-width:3;
stroke:rgb(0,0,0)" />
<text transform="translate(${d.x} ${d.y})">
${d.label}
</text>var groups = _.groupBy(data, 'Incident Type')
// Using _.map and _.filter enables easy composition of unsorted output for viz purposes
var counts = _.map(groups, function(value, key) {
return {'name': key, 'count': _.pluck(value, 'Incident Type').length}
})
console.log(counts)
var filtered_counts = _.filter(counts, function(d) {
return d.count > 30
})
console.log(filtered_counts)
// Produce a bottom-aligned bar chart with margins
function computeX(d, i) {
return i * 60
}
function computeHeight(d, i) {
var value = _.max(_.pluck(counts, 'count'))
return (d.count/(value/380))
}
function computeWidth(d, i) {
return 50
}
function computeY(d, i) {
return 400 - computeHeight(d, i)
}
function computeColor(d, i) {
return 'red'
}
function computeLabel(d, i) {
return d.name
}
var viz = _.map(filtered_counts, function(d, i) {
return {
x: computeX(d, i),
y: computeY(d, i),
height: computeHeight(d, i),
width: computeWidth(d, i),
color: computeColor(d, i),
label: computeLabel(d, i)
}
})
console.log(viz)
var result = _.map(viz, function(d) {
// invoke the compiled template function on each viz data
return template({d: d})
})
return result.join('\n')<rect x="0"
y="20"
height="380"
width="90"
style="fill:red;
stroke-width:3;
stroke:rgb(0,0,0)" />
<text transform="translate(0 20)">
Manhattan
</text>
<rect x="100"
y="72.41379310344826"
height="327.58620689655174"
width="90"
style="fill:red;
stroke-width:3;
stroke:rgb(0,0,0)" />
<text transform="translate(100 72.41379310344826)">
Brooklyn
</text>
<rect x="200"
y="190.3448275862069"
height="209.6551724137931"
width="90"
style="fill:red;
stroke-width:3;
stroke:rgb(0,0,0)" />
<text transform="translate(200 190.3448275862069)">
Queens
</text>
<rect x="300"
y="334.48275862068965"
height="65.51724137931035"
width="90"
style="fill:red;
stroke-width:3;
stroke:rgb(0,0,0)" />
<text transform="translate(300 334.48275862068965)">
Bronx
</text>
<rect x="400"
y="295.17241379310343"
height="104.82758620689656"
width="90"
style="fill:red;
stroke-width:3;
stroke:rgb(0,0,0)" />
<text transform="translate(400 295.17241379310343)">
Staten Island
</text><rect x="${d.x}"
y="${d.y}"
height="${d.height}"
width="${d.width}"
style="fill:${d.color};
stroke-width:3;
stroke:rgb(0,0,0)" />
<text transform="translate(${d.x} ${d.y})">
${d.label}
</text>var flist = _.filter(data, function(f) {
return f['Incident Type'] == "Fire-1st Alarm"
})
var groups = _.groupBy(flist, 'Borough')
// Using _.map and _.filter enables easy composition of unsorted output for viz purposes
var counts = _.map(groups, function(value, key) {
return {'name': key, 'count': _.pluck(value, 'Incident Type').length}
})
console.log(counts)
// Produce a bottom-aligned bar chart with margins
function computeX(d, i) {
return i * 100
}
function computeHeight(d, i) {
var value = _.max(_.pluck(counts, 'count'))
return (d.count/(value/380))
}
function computeWidth(d, i) {
return 90
}
function computeY(d, i) {
return 400 - computeHeight(d, i)
}
function computeColor(d, i) {
return 'red'
}
function computeLabel(d, i) {
return d.name
}
var viz = _.map(counts, function(d, i) {
return {
x: computeX(d, i),
y: computeY(d, i),
height: computeHeight(d, i),
width: computeWidth(d, i),
color: computeColor(d, i),
label: computeLabel(d, i)
}
})
console.log(viz)
var result = _.map(viz, function(d) {
// invoke the compiled template function on each viz data
return template({d: d})
})
return result.join('\n')