Skip to content

Instantly share code, notes, and snippets.

@pa-w
Last active June 9, 2016 00:12
Show Gist options
  • Save pa-w/ee6da80443276e33af29754afd1caf45 to your computer and use it in GitHub Desktop.
Save pa-w/ee6da80443276e33af29754afd1caf45 to your computer and use it in GitHub Desktop.
Boston Stop and Frisk: Frisks, Searches and Summons issued by race

Boston Stop and Frisk visualization

zip total black_total white_total hispanic_total search_total summons_total frisked_total search_black search_white search_hispanic summons_black summons_white summons_hispanic frisked_black frisked_white frisked_hispanic
02119 2560 2161 229 122 371 79 765 289 35 26 73 5 4 628 72 44
02121 2003 1717 136 89 304 25 753 254 21 23 21 3 5 667 34 36
02124 1937 1616 198 77 305 20 749 233 50 17 18 1 0 624 83 25
1537 985 202 76 158 8 441 107 32 11 4 3 1 330 71 30
02118 1249 837 316 205 91 5 291 48 34 17 0 4 1 181 82 50
02125 1145 795 220 78 163 9 391 108 32 14 1 5 0 282 67 26
00000 1018 797 153 69 108 15 275 77 23 11 4 11 1 217 32 21
02120 993 777 113 107 92 2 214 73 9 9 2 0 0 159 34 33
02130 903 625 210 92 129 20 283 81 39 22 13 7 4 168 95 46
02122 699 473 137 32 55 8 185 37 9 2 7 1 2 128 31 9
02111 688 340 303 51 292 203 273 161 113 26 120 76 20 150 101 23
02128 586 80 440 74 83 14 190 19 54 27 3 10 4 43 123 46
02126 540 469 29 16 51 7 174 44 4 3 7 0 0 158 8 2
02127 522 156 330 38 52 0 115 20 30 4 0 0 0 47 63 9
02116 377 179 179 33 110 40 115 49 59 6 25 14 4 44 66 10
02108 365 109 225 22 122 57 101 46 67 8 28 26 1 43 51 6
02129 150 50 94 6 59 18 61 19 38 1 4 14 1 23 37 1
02115 146 111 29 16 23 1 44 15 4 2 0 1 0 31 11 7
02136 140 92 38 15 12 4 36 5 6 3 0 4 1 24 11 4
02131 135 81 45 20 11 4 35 6 5 4 2 2 2 22 11 12
02114 115 34 79 1 22 6 23 4 17 0 1 5 0 6 16 0
02135 112 41 59 8 18 3 40 7 9 1 1 2 0 16 20 2
02215 84 42 37 3 10 0 21 4 6 1 0 0 0 11 8 2
02132 80 25 51 5 4 1 14 3 1 1 1 0 1 7 7 2
02110 76 19 54 2 30 6 31 9 21 2 1 4 0 9 21 1
02134 54 14 31 2 5 0 25 1 4 0 0 0 0 8 13 2
02109 38 17 20 2 8 4 14 3 5 0 4 0 0 6 8 0
02113 18 4 14 0 5 1 6 0 5 0 0 1 0 1 5 0
02199 16 6 3 0 7 0 9 2 2 0 0 0 0 3 3 0
02210 10 1 8 0 1 0 3 0 1 0 0 0 0 1 2 0
02203 8 1 6 0 3 0 4 0 3 0 0 0 0 0 3 0
02446 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0
02026 2 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0
02188 2 0 2 0 0 0 1 0 0 0 0 0 0 0 1 0
0 1 0 1 0 1 0 0 0 1 0 0 0 0 0 0 0
01218 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
01810 1 1 0 0 0 0 1 0 0 0 0 0 0 1 0 0
02062 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0
02150 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
02169 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
02186 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
02205 1 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0
02351 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
02368 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
02467 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
<!DOCTYPE html>
<html lang="en-us">
<head>
<meta charset="UTF-8">
<title>Stop and Frisk in Boston</title>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="//dist.ant-project.com/jquery.min.js"></script>
<script src="//dist.ant-project.com/d3.min.js"></script>
<script src="//dist.ant-project.com/d3.geo.projection.v0.min.js"></script>
<script src="//dist.ant-project.com/queue.min.js"></script>
<script src="//dist.ant-project.com/topojson.js"></script>
<script src="//dist.ant-project.com/ant.js"></script>
<script src="ant.js"></script>
<link rel="stylesheet" href="style.css"/>
</head>
<body>
<a data-onload id="download" data-download="data.csv" data-download_id="saf" data-download_processor="process" data-download_parse=".quantifier" data-debug="Start download"></a>
<div class="col">
<h2>Stops by Zipcode</h2>
<h3>Total</h3>
<div id="total" data-chart="lines" class="chart columns"></div>
<a class="quantifier" data-control_chart="total" data-quantify="saf" data-quantifier="total" data-quantifier_args='{"cols": ["total"], "comp": "total"}'></a>
<h3>Frisks</h3>
<div id="total_frisks" data-chart="lines" class="chart columns"></div>
<a class="quantifier" data-control_chart="total_frisks" data-quantify="saf" data-quantifier="total" data-quantifier_args='{"cols": ["frisked_total"], "comp": "total"}'></a>
<h3>Searches</h3>
<div id="total_searches" data-chart="lines" class="chart columns"></div>
<a class="quantifier" data-control_chart="total_searches" data-quantify="saf" data-quantifier="total" data-quantifier_args='{"cols": ["search_total"], "comp": "total"}'></a>
<h3>Summons Issued</h3>
<div id="total_summons" data-chart="lines" class="chart columns"></div>
<a class="quantifier" data-control_chart="total_summons" data-quantify="saf" data-quantifier="total" data-quantifier_args='{"cols": ["summons_total"], "comp": "total"}'></a>
</div>
<div class="col">
<h2>Black, Hispanic and White</h2>
<h3>Total</h3>
<div id="race_total" data-chart="lines" class="chart columns"></div>
<a class="quantifier" data-control_chart="race_total" data-quantify="saf" data-quantifier="total" data-quantifier_args='{"cols": ["black_total", "white_total", "hispanic_total"], "comp": "total"}'></a>
<h3>Frisks</h3>
<div id="race_frisked" data-chart="lines" class="chart columns"></div>
<a class="quantifier" data-control_chart="race_frisked" data-quantify="saf" data-quantifier="total" data-quantifier_args='{"cols": ["frisked_black", "frisked_white", "frisked_hispanic"], "comp": "total"}'></a>
<h3>Searches</h3>
<div id="race_search" data-chart="lines" class="chart columns"></div>
<a class="quantifier" data-control_chart="race_search" data-quantify="saf" data-quantifier="total" data-quantifier_args='{"cols": ["search_black", "search_white", "search_hispanic"], "comp": "total"}'></a>
<h3>Summons Issued</h3>
<div id="race_summons" data-chart="lines" class="chart columns"></div>
<a class="quantifier" data-control_chart="race_summons" data-quantify="saf" data-quantifier="total" data-quantifier_args='{"cols": ["summons_black", "summons_white", "summons_hispanic"], "comp": "total"}'></a>
</div>
<div class="col">
<h2>Relationships</h2>
<h3>Stops vs. frisks</h3>
<div id="stops_frisks" data-chart="lines" class="chart scatterplot"></div>
<a class="quantifier" data-control_chart="stops_frisks" data-quantify="saf" data-quantifier="total" data-quantifier_args='{"comp": ["total", "total"], "cols": [["total", "frisked_total"], ["black_total", "frisked_black"], ["white_total", "frisked_white"], ["hispanic_total", "frisked_hispanic"]], "scatterplot": true}' data-debug="total_frisks"></a>
<h3>Frisks vs. searches</h3>
<div id="frisks_searches" data-chart="lines" class="chart scatterplot"></div>
<a class="quantifier" data-control_chart="frisks_searches" data-quantify="saf" data-quantifier="total" data-quantifier_args='{"comp": ["total", "frisked_total"], "cols": [["frisked_total", "search_total"], ["frisked_black", "search_black"], ["frisked_white", "search_white"], ["frisked_hispanic", "search_hispanic"]], "scatterplot": true}' data-debug="total_frisks"></a>
<h3>Searches vs Summons issued</h3>
<div id="searches_summons" data-chart="lines" class="chart scatterplot"></div>
<a class="quantifier" data-control_chart="searches_summons" data-quantify="saf" data-quantifier="total" data-quantifier_args='{"comp": ["frisked_total", "search_total"], "cols": [["search_total", "summons_total"], ["search_black", "summons_black"], ["search_white", "summons_white"], ["search_hispanic", "summons_hispanic"]], "scatterplot": true}' data-debug="total_frisks"></a>
</div>
<div class="col">
<div>
<table>
<tr><th>Zip:</th><td colspan="3"><span class="zip_text"></span></td></tr>
<tr><th></th><th>White</th><th>Black</th><th>Hispanic</th><th>All</th></tr>
<tr><th>Stops:</th>
<td><span class="white_total_text"></span></td>
<td><span class="black_total_text"></span></td>
<td><span class="hispanic_total_text"></span></td>
<td><span class="total_text"></span></td>
</tr>
<tr><th>Frisks:</th>
<td><span class="frisked_white_text"></span></td>
<td><span class="frisked_black_text"></span></td>
<td><span class="frisked_hispanic_text"></span></td>
<td><span class="frisked_total_text"></span></td>
</tr>
<tr><th>Searches:</th>
<td><span class="search_white_text"></span></td>
<td><span class="search_black_text"></span></td>
<td><span class="search_hispanic_text"></span></td>
<td><span class="search_total_text"></span></td>
</tr>
<tr><th>Summons:</th>
<td><span class="summons_white_text"></span></td>
<td><span class="summons_black_text"></span></td>
<td><span class="summons_hispanic_text"></span></td>
<td><span class="summons_total_text"></span></td>
</tr>
</table>
</div>
</div>
<script>
$(document).ready(function () {
var conf = {
prequantifiers: {
total: function (args) {
var comp = args.comp,
scale = {};
if (args.scatterplot) {
scale.x = d3.scale.pow ().exponent (.15).domain (this.data.saf.extent (function (a) { return parseInt (a.values [args.comp [1]].value); }));
comp = args.comp [0];
}
var ext = this.data.saf.extent (function (a) { return parseInt (a.values [comp].value);})
scale.y = d3.scale.linear ().domain ([ext [1], ext [0]])
var items = this.data.saf.items (),
data = [];
for (var i = 0; i < args.cols.length; i++) {
data.push ({"values": items, "col": args.cols [i] });
}
return {
"data": data,
"scale": scale
}
}
},
quantifiers: {
lines: {
total: function (zip, args, a, series ) {
/* highlights and updates the labels */
var parse = [
{"control_element": ".zip", "element_remove_class": "highlight"},
{"control_element": ".zip_" + zip.key, "element_add_class": "highlight"},
], ret = {
"class": "zip " + series.col + " zip_" + zip.key,
"data": {"parse": parse}
}
var its = zip.values.items ();
for (var x in its) {
parse.push ({
"control_element": "." + its[x].key + "_text",
"element_text": its [x].values.value
});
}
if (args.scatterplot) {
ret ["class"] = series.col [0] + " " + series.col [1] + " zip zip_" + zip.key;
ret.y = a.scale.y (parseInt (zip.values [series.col [0]].value));
ret.x = a.scale.x (parseInt (zip.values [series.col [1]].value));
ret.r = (zip.values [series.col [1]].value / 30) + 2;
if (ret.x == 0) {
ret ["class"] = "hide";
}
} else {
ret.y = a.scale.y (parseInt (zip.values [series.col].value));
if (parseInt (zip.values [series.col].value) == 0) {
}
}
return ret;
}
}
},
callbacks: {
process: function (rows, id) {
return new Nestify (rows, ["zip"], ["zip","total","black_total","white_total","hispanic_total","search_total","summons_total","search_black","search_white","search_hispanic","summons_black","summons_white","summons_hispanic", "frisked_total", "frisked_black", "frisked_white", "frisked_hispanic"]).data;
}
}
};
new Ant (conf);
});
</script>
</body>
</html>
COPY (
SELECT distinct
split_part("ZIP", '-', 1) as zip,
count (*) as total,
count (*) filter (WHERE people."RACE" = 'Black') as black_total,
count (*) filter (WHERE people."RACE" = 'White') as white_total,
count (*) filter (WHERE people."Ethnicity" = 'Hispanic Origin') as hispanic_total,
count (*) filter (WHERE stop."SearchPerson" = 'Y') as search_total,
count (*) filter (WHERE stop."SummonsIssued" = 'Y') as summons_total,
count (*) filter (WHERE stop."Frisked" = 'Y') as frisked_total,
count (*) filter (WHERE stop."SearchPerson" = 'Y' AND people."RACE" = 'Black') as search_black,
count (*) filter (WHERE stop."SearchPerson" = 'Y' AND people."RACE" = 'White') as search_white,
count (*) filter (WHERE stop."SearchPerson" = 'Y' AND people."Ethnicity" = 'Hispanic Origin') as search_hispanic,
count (*) filter (WHERE stop."SummonsIssued" = 'Y' AND people."RACE" = 'Black') as summons_black,
count (*) filter (WHERE stop."SummonsIssued" = 'Y' AND people."RACE" = 'White') as summons_white,
count (*) filter (WHERE stop."SummonsIssued" = 'Y' AND people."Ethnicity" = 'Hispanic Origin') as summons_hispanic,
count (*) filter (WHERE stop."Frisked" = 'Y' AND people."RACE" = 'Black') as frisked_black,
count (*) filter (WHERE stop."Frisked" = 'Y' AND people."RACE" = 'White') as frisked_white,
count (*) filter (WHERE stop."Frisked" = 'Y' AND people."Ethnicity" = 'Hispanic Origin') as frisked_hispanic
FROM
"FieldContacts_For_Public_2015-Apr_30_2016" as stop LEFT JOIN
"FieldContacts_Name_For_Public_2015-Apr_30_2016" people
ON stop."FC_NUM" = people."FC_NUM"
GROUP BY
zip
ORDER BY
total DESC
) TO '/tmp/fios.csv' CSV HEADER;
body {
width: 99vw;
height: 99vh;
margin: 0;
padding: 0;
color: gray;
}
h1,h2,h3,h4,h5,h6 {
margin: 0;
}
h2 { font-size: 1.1em; text-align: center; margin-bottom: 1%; }
h3 { font-size: 1em; }
div.col {
float: left;
width: 25%;
height: 100%;
}
.chart, .columns path, .chart path {
fill: none;
}
.axis path {
display: none;
}
.axis text {
fill: red;
font-size: 0.3em;
}
.axis line {
stroke: #777;
stroke-dasharray: 1,1;
}
div.chart {
height: 20%;
}
.chart.columns rect.column {
fill: rgba(0,123,123,0.5);
fill-opacity: 1 !important;
stroke: white;
stroke-width: 0.5;
}
.chart.scatterplot circle {
stroke: white;
stroke-width: 0.5;
}
.black_total, .search_black, .frisked_black, .summons_black {
fill: rgba(0,123,0,0.5) !important;
}
.white_total, .search_white, .frisked_white, .summons_white {
fill: rgba(0,0,123,0.5) !important;
}
.hispanic_total, .search_hispanic, .frisked_hispanic, .summons_hispanic {
fill: rgba(255,140,0,1) !important;
}
cirle.total, circle.search_total, circle.frisked_total, circle.summons_total {
}
circle.hide, rect.hide {
display: none !important;
}
.square {
display: none !important;
}
/*
*/
.columns .highlight.background {
fill: rgba(255,0,0,0.6) !important;
}
circle.highlight {
stroke: rgba(255,0,0,0.6) !important;
stroke-width: 3 !important;
}
.background, .square, .line, .scatterplot .column {
fill: rgba(0,0,0,0) !important;
stroke: none !important;
}
.scatterplot .column, .scatterplot .background {
fill: none !important;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment