Traffic Stop Visualization Using D3
January 28, 2014 Leave a comment
One of the comments I got from the TRINUG Data SIG was that the data and analysis were exciting but the results were, well, boring. So I went back to the traffic stop data and thought about how I could sexy it up. Since lots of people have been using D3 to present the data. I thought it would be a good place to start.
My 1st step was to look at their samples page and they have a simple bar chart that seems like a good introduction to the library. I created an endpoint on my webapi for the summary data like so:
- [HttpGet]
- [Route("api/TrafficStopSearch/StopsByMonth/")]
- public dynamic StopsByMonth()
- {
- return ChickenSoftware.RoadAlert.Analysis.AnalysisEngine.TrafficStopsByMonth;
- }
I then spun up an empty asp.net website and created an index page based on their sample. I then added an ajax call to the controller and replaced the reference to the data.tsv file:
- $.ajax({
- url: "http://localhost:17680/api/TrafficStopSearch/StopsByMonth/",
- dataType: "json",
- success: function (data) {
- x.domain(data.map(function (d) { return d.m_Item1; }));
- y.domain([0, d3.max(data, function (d) { return d.m_Item6; })]);
- svg.append("g")
- .attr("class", "x axis")
- .attr("transform", "translate(0," + height + ")")
- .call(xAxis);
- svg.append("g")
- .attr("class", "y axis")
- .call(yAxis)
- .append("text")
- .attr("transform", "rotate(-90)")
- .attr("y", 6)
- .attr("dy", ".71em")
- .style("text-anchor", "end")
- .text("Frequency");
- svg.selectAll(".bar")
- .data(data)
- .enter().append("rect")
- .attr("class", "bar")
- .attr("x", function (d) { return x(d.m_Item1); })
- .attr("width", x.rangeBand())
- .attr("y", function (d) { return y(d.m_Item6); })
- .attr("height", function (d) { return height – y(d.m_Item6); });
- },
- error: function(e){
- alert("error");
- }
- });
On thing to note is that the tuple that was created in F# and then passed though via the C# controller had its name changed. Specially, Tuple.Item1 became Tuple.m_Item1. I think that passing out tuple.anything is a horrible idea, so I created a POCO that actually lets the consumer know what each field means:
- public class OutputValue
- {
- public Int32 Month { get; set; }
- public Int32 ExpectedStops { get; set; }
- public Int32 ActualStops { get; set; }
- public Double DifferenceBetweenExpectedAndActual { get; set; }
- public Double PercentDifferenceBetweenExpectedAndActual { get; set; }
- public Double Frequency { get; set; }
- }
and then I adjusted the controller like so:
- [HttpGet]
- [Route("api/TrafficStopSearch/StopsByMonth/")]
- public dynamic StopsByMonth()
- {
- var outputs = new List<OutputValue>();
- var resultSet= ChickenSoftware.RoadAlert.Analysis.AnalysisEngine.TrafficStopsByMonth;
- foreach (var tuple in resultSet)
- {
- var outputValue = new OutputValue()
- {
- Month = tuple.Item1,
- ExpectedStops = tuple.Item2,
- ActualStops = tuple.Item3,
- DifferenceBetweenExpectedAndActual = tuple.Item4,
- PercentDifferenceBetweenExpectedAndActual = tuple.Item5,
- Frequency = tuple.Item6
- };
- outputs.Add(outputValue);
- }
- return outputs;
- }
So I adjusted the javascript and voila: a bar chart:
Up next – some real charts…