Skip to content

Instantly share code, notes, and snippets.

@jbcrail
Last active August 29, 2015 14:02
Show Gist options
  • Save jbcrail/928bfd1882df644e0ba1 to your computer and use it in GitHub Desktop.
Save jbcrail/928bfd1882df644e0ba1 to your computer and use it in GitHub Desktop.
Louisville crimes by day/hour

Forked from tjdecke’s block #5558084.

Example data shows Louisville crimes between 2012 and 2014.

from datetime import datetime
import csv
import sys
# Generates input data from Louisville's crime data
# Source: http://api.louisvilleky.gov/api/File/DownloadFile?fileName=CrimeData.txt
if __name__ == '__main__':
crimes = {}
with open(sys.argv[1], "rb") as f:
reader = csv.reader(f, delimiter="\t")
reader.next() # skip header row
for row in reader:
ts = datetime.strptime("%s %s" % (row[3], row[4].strip()), "%Y-%m-%d %I:%M%p")
tstuple = ts.timetuple()
key = (tstuple.tm_wday+1, tstuple.tm_hour+1)
if key in crimes:
crimes[key] += 1
else:
crimes[key] = 1
with open(sys.argv[2], 'wb') as f:
writer = csv.writer(f, delimiter='\t', quoting=csv.QUOTE_MINIMAL)
writer.writerow(["day", "hour", "value"])
for key in sorted(crimes.keys()):
writer.writerow([key[0], key[1], crimes[key]])
day hour value
1 1 214
1 2 200
1 3 154
1 4 129
1 5 120
1 6 120
1 7 153
1 8 328
1 9 563
1 10 678
1 11 752
1 12 818
1 13 798
1 14 841
1 15 904
1 16 939
1 17 895
1 18 813
1 19 755
1 20 649
1 21 524
1 22 463
1 23 345
1 24 333
2 1 211
2 2 202
2 3 126
2 4 140
2 5 113
2 6 139
2 7 164
2 8 289
2 9 482
2 10 584
2 11 676
2 12 660
2 13 704
2 14 752
2 15 831
2 16 812
2 17 820
2 18 762
2 19 644
2 20 572
2 21 507
2 22 416
2 23 329
2 24 291
3 1 213
3 2 195
3 3 167
3 4 114
3 5 108
3 6 115
3 7 138
3 8 256
3 9 474
3 10 571
3 11 628
3 12 644
3 13 688
3 14 708
3 15 781
3 16 864
3 17 796
3 18 788
3 19 697
3 20 537
3 21 474
3 22 430
3 23 297
3 24 318
4 1 206
4 2 160
4 3 161
4 4 148
4 5 137
4 6 125
4 7 206
4 8 286
4 9 455
4 10 567
4 11 638
4 12 660
4 13 739
4 14 728
4 15 770
4 16 797
4 17 774
4 18 714
4 19 627
4 20 522
4 21 476
4 22 442
4 23 356
4 24 307
5 1 190
5 2 169
5 3 155
5 4 129
5 5 142
5 6 114
5 7 182
5 8 303
5 9 475
5 10 567
5 11 624
5 12 635
5 13 657
5 14 699
5 15 794
5 16 769
5 17 802
5 18 670
5 19 579
5 20 574
5 21 497
5 22 487
5 23 391
5 24 341
6 1 279
6 2 294
6 3 226
6 4 296
6 5 189
6 6 182
6 7 154
6 8 178
6 9 310
6 10 460
6 11 496
6 12 542
6 13 588
6 14 584
6 15 573
6 16 547
6 17 588
6 18 545
6 19 511
6 20 506
6 21 460
6 22 414
6 23 408
6 24 374
7 1 316
7 2 322
7 3 302
7 4 285
7 5 258
7 6 160
7 7 151
7 8 186
7 9 250
7 10 344
7 11 457
7 12 500
7 13 570
7 14 566
7 15 601
7 16 594
7 17 562
7 18 543
7 19 517
7 20 495
7 21 471
7 22 461
7 23 339
7 24 319
<!DOCTYPE html>
<meta charset="utf-8">
<html>
<head>
<style>
rect.bordered {
stroke: #E6E6E6;
stroke-width:2px;
}
text.mono {
font-size: 9pt;
font-family: Consolas, courier;
fill: #aaa;
}
text.axis-workweek {
fill: #000;
}
text.axis-worktime {
fill: #000;
}
</style>
<script src="http://d3js.org/d3.v3.js"></script>
</head>
<body>
<div id="chart"></div>
<script type="text/javascript">
var margin = { top: 50, right: 0, bottom: 100, left: 30 },
width = 960 - margin.left - margin.right,
height = 430 - margin.top - margin.bottom,
gridSize = Math.floor(width / 24),
legendElementWidth = gridSize*2,
buckets = 9,
colors = ["#ffffd9","#edf8b1","#c7e9b4","#7fcdbb","#41b6c4","#1d91c0","#225ea8","#253494","#081d58"], // alternatively colorbrewer.YlGnBu[9]
days = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
times = ["1a", "2a", "3a", "4a", "5a", "6a", "7a", "8a", "9a", "10a", "11a", "12p", "1p", "2p", "3p", "4p", "5p", "6p", "7p", "8p", "9p", "10p", "11p", "12a"];
d3.tsv("data.tsv",
function(d) {
return {
day: +d.day,
hour: +d.hour,
value: +d.value
};
},
function(error, data) {
var colorScale = d3.scale.quantile()
.domain([0, buckets - 1, d3.max(data, function (d) { return d.value; })])
.range(colors);
var svg = d3.select("#chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var dayLabels = svg.selectAll(".dayLabel")
.data(days)
.enter().append("text")
.text(function (d) { return d; })
.attr("x", 0)
.attr("y", function (d, i) { return i * gridSize; })
.style("text-anchor", "end")
.attr("transform", "translate(-6," + gridSize / 1.5 + ")")
.attr("class", function (d, i) { return ((i >= 0 && i <= 4) ? "dayLabel mono axis axis-workweek" : "dayLabel mono axis"); });
var timeLabels = svg.selectAll(".timeLabel")
.data(times)
.enter().append("text")
.text(function(d) { return d; })
.attr("x", function(d, i) { return i * gridSize; })
.attr("y", 0)
.style("text-anchor", "middle")
.attr("transform", "translate(" + gridSize / 2 + ", -6)")
.attr("class", function(d, i) { return ((i >= 7 && i <= 16) ? "timeLabel mono axis axis-worktime" : "timeLabel mono axis"); });
var heatMap = svg.selectAll(".hour")
.data(data)
.enter().append("rect")
.attr("x", function(d) { return (d.hour - 1) * gridSize; })
.attr("y", function(d) { return (d.day - 1) * gridSize; })
.attr("rx", 4)
.attr("ry", 4)
.attr("class", "hour bordered")
.attr("width", gridSize)
.attr("height", gridSize)
.style("fill", colors[0]);
heatMap.transition().duration(1000)
.style("fill", function(d) { return colorScale(d.value); });
heatMap.append("title").text(function(d) { return d.value; });
var legend = svg.selectAll(".legend")
.data([0].concat(colorScale.quantiles()), function(d) { return d; })
.enter().append("g")
.attr("class", "legend");
legend.append("rect")
.attr("x", function(d, i) { return legendElementWidth * i; })
.attr("y", height)
.attr("width", legendElementWidth)
.attr("height", gridSize / 2)
.style("fill", function(d, i) { return colors[i]; });
legend.append("text")
.attr("class", "mono")
.text(function(d) { return "≥ " + Math.round(d); })
.attr("x", function(d, i) { return legendElementWidth * i; })
.attr("y", height + gridSize);
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment