block by zanarmstrong d5380c855e887b113e4b

Seasonality of ER products, small multiples

Full Screen

Exploring other views of: http://flowingdata.com/2016/02/09/why-people-visit-the-emergency-room/. Code copied from Nathan Yau’s work, and adapted.

index.html

<!DOCTYPE html>
<head>
  <meta charset="utf-8">
  <link rel="stylesheet" href="stl.css">
  <link href='//fonts.googleapis.com/css?family=Raleway:400,700' rel='stylesheet' type='text/css'>
</head>
<body>

<div class="projectTitle">Seasonality of products involved in ER visits, sorted by most to least seasonal</div>
<div class="projectDesc">For each product type and month, the chart shows the percent of accidents involving that product that took place in that month. For example, in 2014, just over 70% of all ER visits related to Fireworks occured in July. The products are sorted from most to least seasonal, ending with "stair/steps" accidents which are essentially equally likely in any month.</div>
<div class="projectDesc">The dashed line at 8.3% shows an "average" month. The y-scale emphasizes values between 0 and 20%, to better reveal the differences in seasonality between 0 occurances and 2x an average month.</div>
<div class="productDesc">Inspired by and adapted from Nathan Yau's <a href="//flowingdata.com/2016/02/09/why-people-visit-the-emergency-room/">Why People Visit the Emergency Room</a>.</div>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
  <script src="stl.js"></script>
</body>

stl.css


body {
    font: 14px 'Inconsolata', Monaco, "Lucida Console", Consolas, "Courier New";
}
.projectTitle {
    margin-left: 40px;
}
.projectDesc{
    margin-left: 40px;
    font-size: 12px;
    max-width: 800px;
    margin-top: 14px;
}
.hidden {
    display: none;
}
.product path {
    fill: none;
    stroke: blue;
    stroke-linejoin: round;
    stroke-linecap: round;
    stroke-width: 0.8px;
}
.axis path {
    stroke: grey;
    stroke-width: .4px;
}
.title {
    font-size: 12px;
}
.tick line {
    stroke: grey;
    stroke-width: .4px;
}

.tick text {
    fill: grey;
    stroke: none;
    font-size: 8px;
}

stl.js

"use strict";

var selected = false;

var months,
	monthFormat = d3.time.format("%m");

var chartWidth = 200;
var chartHeight = 100;
var chartHeightMargin = 30;
var chartWidthMargin = 30;
var margin = {
		top: 25,
		right: 0,
		bottom: 50,
		left: 40
	},
	width = 1500 - margin.left - margin.right;
var numWide = Math.floor(width / (chartWidth + chartWidthMargin))
var height = margin.top + margin.bottom + (chartHeight + chartHeightMargin) * 250 / numWide;
var max_radius = 14;

var countFormat = d3.format(",.1%");

var x = d3.time.scale()
	.range([0, chartWidth]);


//var y = d3.scale.linear().domain([-1, 7]).range([100, 0])
//var y = d3.scale.linear().domain([-1, 1, 5, 8]).range([100, 60, 35, 0])
var y = d3.scale.linear().domain([0, 1 / 6, .8]).range([chartHeight, chartHeight / 2, 0])

var xAxis = d3.svg.axis()
	.scale(x)
	.orient('bottom')
	.tickSize('4')
	.tickFormat(d3.time.format("%b"));

var yAxis = d3.svg.axis()
	.scale(y)
	.orient('left')
	.tickSize('4')
	.tickValues([1 / 12, 1 / 6, .3, .5, .7])
	.tickFormat(d3.format(".1%"));


var yCat = d3.scale.linear().domain([0, 250 / numWide]).range([0, height - (chartHeight)])
var xCat = d3.scale.linear().domain([0, 4]).range([0, (chartWidth + chartWidthMargin) * 4])

var line = d3.svg.line()
	.x(function(d) {
		return x(d.date);
	})
	.y(function(d) {
		return y(d.valueSeasonal);
	});

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 + ")");


d3.tsv("products-monthly-2014-top250.tsv", type, function(error, products) {
	console.log(products)
	products.sort(function(a, b) {
		return b.std - a.std
	})

	x.domain(d3.extent(months));

	// Container for a product line and circles.

	var product_container = svg.selectAll('.product')
		.data(products)
		.enter().append("g")
		.attr("class", "product")
		.attr("transform", function(d, i) {
			return "translate(" + xCat(i % numWide) + "," + yCat(Math.floor(i / numWide)) + ")"
		})
		.attr("id", function(d) {
			return "p" + d.code;
		});


	//
	// Lines.
	//

	product_container.append('g')
		.attr("transform", "translate(0," + y(0) + ")")
		.attr("class", function(d) {
			return "xAxis axis hidden id-" + d.code
		})
		.append('g').call(xAxis);


	product_container.append('g')
		//.attr("transform", "translate(0," + (chartHeight - 25) + ")")
		.attr("class", function(d) {
			return "hidden axis id-" + d.code
		})
		.append('g').call(yAxis);


	product_container.append("path")
		.attr("d", function(d) {
			return line(d.values);
		})
		.attr("class", function(d) {
			return d.code;
		});

	product_container.append("line")
		.attr({
			'x2': chartWidth,
			'y1': y(1 / 12),
			'y2': y(1 / 12),
			'stroke-dasharray': '5,5',
			'stroke': 'grey',
			'stroke-width': '.5px'
		})

	product_container.append("line")
		.attr({
			'x2': chartWidth,
			'y1': y(0),
			'y2': y(0),
			'stroke': 'grey',
			'stroke-width': '.5px'
		})

	// 
	// Text
	// 

	product_container.append('text')
		.text(function(d) {
			return d.name
		})
		.attr({
			'x': 2,
			'y': y(.73),
			'class': function(d) {
				return 'title hidden id-' + d.code
			}
		})

	product_container.append("rect")
		.attr({
			'height': chartHeight,
			'width': chartWidth,
			'opacity': 0
		})
		.on('mouseover', function(d) {
			d3.selectAll('.id-' + d.code).classed("hidden", false)
		})
		.on('mouseout', function(d) {
			d3.selectAll('.id-' + d.code).classed("hidden", true)
		});;

}); // @end d3.tsv()



function type(d, i) {
	var calculateStd = function(data, mean) {
		conslole.log('here')
		var sumSquaredDiff = 0;

		return
	}

	months = d3.range(1, 13).map(function(d) {
		return monthFormat.parse(String(d));
	}).filter(Number);

	var product = {
		code: d.Code,
		name: d.Title,
		values: null,
		annualTotal: 0,
		seasonalArray: []
	};
	for (var i = 1; i <= 12; i++) {
		product.annualTotal = product.annualTotal + +d['m' + i]
	}
	product.mean = product.annualTotal / 12

	product.values = months.map(function(m) {
		var value = Number(d['m' + Number(monthFormat(m))])
		var seasonal = value / product.annualTotal
			//var seasonal = (value / (product.annualTotal / 12)) - 1
		product.seasonalArray.push(seasonal)
		return {
			product: product,
			date: m,
			value: value,
			valueSeasonal: seasonal
		}
	});


	product.std = d3.deviation(product.seasonalArray)

	return product;
}