Skip to content

Instantly share code, notes, and snippets.

@yi-ye-zhi-qiu
Created February 15, 2020 23:08
Show Gist options
  • Save yi-ye-zhi-qiu/2adfe254669d650b0e9509019a9761b0 to your computer and use it in GitHub Desktop.
Save yi-ye-zhi-qiu/2adfe254669d650b0e9509019a9761b0 to your computer and use it in GitHub Desktop.
fresh block
license: mit
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
margin: auto;
position: relative;
width: 960px;
}
text {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
form {
position: absolute;
right: 10px;
top: 10px;
}
</style>
<form>
<label><input type="radio" name="mode" value="grouped"> Grouped</label>
<label><input type="radio" name="mode" value="stacked" checked> Stacked</label>
</form>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
d3.csv("police_killings.csv", function(err, data) {
var allRaces = ["Black","Hispanic/Latino", "White", "Unknown", "Asian/Pacific Islander", "Native American"];
var months = ["January", "February", "March", "April", "May", "June"]
// we want to "pivot" our data into deaths by month by race
// this is a rather ugly way to do it in javascript. would probably be easier
// to group the data in another tool (excel, google sheets, etc) and load that
var groups = {}
var races = {};
var xkey = "raceethnicity" // the x axis
var gkey = "month" // what we group by
// first we group all the events by race
data.forEach(function(d) {
if(!groups[d[gkey]]) {
groups[d[gkey]] = [d];
} else {
groups[d[gkey]].push(d)
}
})
var processed = [];
//Next we count how many incidents happended for each month
months.forEach(function(month,i) {
var xdata = {};
groups[month].forEach(function(event) {
if(!xdata[event[xkey]]) {
xdata[event[xkey]] = 1
} else {
xdata[event[xkey]]++;
}
})
// our "result" is an ordered array with a count for each month
// (for the race we are currently working on)
var result = {};
allRaces.forEach(function(g) {
result[g]= xdata[g]||0;
})
processed.push(result)
})
var n = allRaces.length, // number of layers
m = processed.length, // number of samples per layer
stack = d3.stack().keys(allRaces);
var layers = stack(processed); // calculate the stack layout
layers.forEach(function(d,i) { //adding keys to every datapoint
d.forEach(function(dd,j){
dd.month = months[j];
dd.race = allRaces[i];
})
});
var yGroupMax = d3.max(layers, function(layer) {
return d3.max(layer, function(d) {
return d[1] - d[0];
});
}),
yStackMax = d3.max(layers, function(layer) {
return d3.max(layer, function(d) {
return d[1];
});
});
var margin = {top: 40, right: 10, bottom: 20, left: 10},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scaleBand()
.domain(months)
.rangeRound([0, width])
.padding(0.08);
var y = d3.scaleLinear()
.domain([0, yStackMax])
.range([height, 0]);
var z = d3.scaleBand().domain(allRaces).rangeRound([0, x.bandwidth()]);
var color = d3.scaleOrdinal(d3.schemeCategory20c)
.domain([0, n-1])
var svg = d3.select("body").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 layer = svg.selectAll(".layer")
.data(layers)
.enter().append("g")
.attr("class", "layer")
.style("fill", function(d, i) { return color(i); });
var rect = layer.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("x", function(d) {
return x(d.month); })
.attr("y", height)
.attr("width", x.bandwidth())
.attr("height", 0);
rect.transition()
.delay(function(d, i) { return i * 10; })
.attr("y", function(d) {
return y(d[1]);
})
.attr("height", function(d) {
return y(d[0]) - y(d[1]);
});
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x).tickSizeOuter(0));
var legend = svg.selectAll(".legend")
.data(allRaces)
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d,i) { return color(i) });
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
d3.selectAll("input").on("change", change);
var timeout = setTimeout(function() {
d3.select("input[value=\"grouped\"]").property("checked", true).each(change);
}, 2000);
function change() {
clearTimeout(timeout);
if (this.value === "grouped") transitionGrouped();
else transitionStacked();
}
function transitionGrouped() {
y.domain([0, yGroupMax]);
rect.transition()
.duration(500)
.delay(function(d, i) { return i * 10; })
.attr("x", function(d) {
return x(d.month)+ z(d.race);
})
.attr("width", x.bandwidth() / m)
.transition()
.attr("y", function(d) {
return y(d.data[d.race]);
})
.attr("height", function(d) {
return height - y(d.data[d.race]);
});
}
function transitionStacked() {
y.domain([0, yStackMax]);
rect.transition()
.duration(500)
.delay(function(d, i) { return i * 10; })
.attr("y", function(d) {
return y(d[1]);
})
.attr("height", function(d) {
return y(d[0]) - y(d[1]);
})
.transition()
.attr("x", function(d) { return x(d.month); })
.attr("width", x.bandwidth());
}
});
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment