block by hobbes7878 d315122a1759cba1f41b

#NICAR15: D3 stock chart

Full Screen

An simple multi-line chart that pivots from raw values to percent change using separate CSVs.

script.js

var margin = {top: 20, right: 20, bottom: 35, left: 50},
    width = 700 - margin.left - margin.right,
    height = 300 - margin.top - margin.bottom;

var parseDate = d3.time.format("%Y-%m-%d").parse;

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

var y = d3.scale.linear()
    .range([height, 0]);


// Our style for this chart is to display 4-digit year for January and then 3-letter 
// abbreviation for every other month. We can use d3's multi formatter to give us mixed
// formatting. Check out the docs for this one: 
// https://github.com/mbostock/d3/wiki/Time-Formatting#format_multi
var xAxisFormat = d3.time.format.multi([
  ["%b.", function(d) { return d.getMonth(); }],
  ["%Y", function() { return true; }]
]);

var xAxis = d3.svg.axis()
    .scale(x)
    .tickFormat( xAxisFormat )
    .orient("bottom");

var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left");

// Color scales work just like other d3 scales in that they map
// a domain of data values to a range of discrete colors.
// https://github.com/mbostock/d3/wiki/Ordinal-Scales#category10
var color = d3.scale.category10();

var line = d3.svg.line()
	// We're going to "interpolate"/draw a "spline"/curve which will smooth the line a bit. 
	// A "cardinal" spline gives us a little smoothing without diminishing 
	// our peaks and troughs.
	// https://github.com/mbostock/d3/wiki/SVG-Shapes#line_interpolate
	.interpolate("cardinal") 
    .x(function(d) { return x(d.date); })
    .y(function(d) { return y(d.price); });

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

svg.append("g")
	  .attr("class", "x axis")
	  .attr("transform", "translate(0," + height + ")");

svg.append("g")
	  .attr("class", "y axis");

// An axis line at zero on the Y axis.
svg.append("line")
	.attr("class","zeroAxis");

svg.append("text")
	.text("SOURCE: Yahoo! Finance")
	.attr({
		class: "source",
		x:0,
		y:height + 30
	});




// Whenever we want to make a d3 chart update with new data, we need to put the
// elements of our chart that are going to change with the new data into a function.
// That way we can simply call it to redraw these elements.
// In our case, those elements will be the axes, lines and labels, all of which depend
// our data.

function draw(dataFile, axisFormat){


	// Because our data is changeing type (from a share price in dollars to a percentage)
	// we need to reformat our yAxis. In our draw function we're passing a d3 format string:
	// https://github.com/mbostock/d3/wiki/Formatting
	yAxis.tickFormat(d3.format(axisFormat));


	// d3 has special methods for dealing with data in comma-delimited files, CSVs.
	// In this case, we have two CSVs with different data but which we want to share
	// the same chart, so we're passing the filename as an argument to the draw func. 
	// https://github.com/mbostock/d3/wiki/CSV
	d3.csv(dataFile, function(error, data) {


	  // Our color scale domain is going to be the values in the header row of our CSV,
	  // excluding the "date" column.
	  color.domain( d3.keys( data[0] ).filter( function(key) { return key !== "date"; }) );

	  data.forEach(function(d) {
	    d.date = parseDate(d.date);
	  });


	  // Since we'll have multiple companies in our data, we need to create a data array 
	  // that has multiple objects, one for AHC and one for NWS. We'll use javascript's map
	  // function to relate all the price and date data to their respective companies.
	  var companies = color.domain().map(function(name) {
	    return {
	      name: name,
	      values: data.map(function(d) {
	        return {date: d.date, price: +d[name]};
	      })
	    };
	  });

	  /*
	  	After we're done the companies array looks like this:
		companies = [
			{name: "AHC"
			 values: [
				{date: Mon Feb 23 2015 00:00:00 GMT-0600 (CST)
				 price: 8.69 },
				 etc...
				]
			},
			{name: "NWS"
			 values: [
				{date: Mon Feb 23 2015 00:00:00 GMT-0600 (CST)
				 price: 16.82 },
				 etc...
				]
			},
		]
	  */
	  // You can print the companies data to the console and take a look.
	  console.log(companies);


	  x.domain(d3.extent(data, function(d) { return d.date; }));


	  // To get our Y domain, we'll take the min/max of each price for each company object in the companies array
	  // and then take the final min/max of all those mins/maxs.
	  y.domain([
	    d3.min(companies, function(company) { return d3.min(company.values, function(value) { return value.price; }); }),
	    d3.max(companies, function(company) { return d3.max(company.values, function(value) { return value.price; }); })
	  ]);

	  // Update our zero axis.
	  svg.select(".zeroAxis")
	  	.transition().duration(1000)
	  	.attr({
	  		x1:0,
	  		x2:width,
	  		y1:y(0),
	  		y2:y(0)
	  	});


	  // Company lines

	  // JOIN
	  var company = svg.selectAll(".company")
	      .data(companies);

	  // ENTER
	  company.enter().append("path")
	      .attr("class", "company line")
	      .style("stroke", function(d) { return color(d.name); });

	  // UPDATE
	  company
	  	  .transition().duration(1000)
	      .attr("d", function(d) { return line(d.values); });


	  // EXIT ??? Nope, won't need it. We'll always be dealing with the same lines. No need
	  // to remove anything.


	  // D3 makes updating our axes REALLY easy. All we do is select the axis and call them again.
	  svg.select(".x.axis")
	  	  .transition().duration(1000)
	      .call(xAxis);
	  svg.select(".y.axis")
	  	  .transition().duration(1000)
	      .call(yAxis);



	  //Technically we don't need to follow the update pattern for our labels in this chart
	  // since we know what two companies are in our data. But we'll do it anyway, that way
	  // we can easily reuse this chart for any two other companies!
	  var labels = svg.selectAll(".labels")
	  		// Data is the array of headers in our CSV, excluding the date column.
	  		// Helpfully, we already have that array. It's our color domain!
	 		.data(color.domain());

	 	labels.enter()
	 		.append("g")
	 		.attr("class", "labels");
	 	
	 	labels.append("rect")
	 		.attr({
	 			fill: function(d){return color(d);},
	 			height: 20,
	 			width: 42,
	 			// A little math to automatically place our labeling.
	 			// Remember, "i" is the index number of the data element "d".
	 			// We can use it to space our labels! 
	 			x: function(d, i){return width - 23 - (42 * i) ;},
	 			y: -10
	 		});

	 	labels.append("text")
	 		.text(function(d){return d;})
	 		.attr({
	 			x: function(d, i){return width - 20 - (42 * i) ;},
	 			y: 5,
	 		});

	});


}


// Draw the chart when the page loads.
draw("wk_prices.csv","$");

// Bind the draw function to our two buttons with the correct arguments.
$("#priceBtn").click(function(){ 
	draw("wk_prices.csv", "$" ); 
});
$("#changeBtn").click(function(){ 
	draw("wk_changes.csv", "+%" ); 
});

index.html

<!DOCTYPE html>
<html>
    <head>

        <meta charset="utf-8">
        <title>Scatterplot Pivot</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <script src="https://code.jquery.com/jquery-1.9.1.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.2/js/bootstrap.min.js"></script>
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.2/css/bootstrap.css">

    </head>
<body>



<div id="chart">
	<h1>Stock History</h1>
	<h4>News Corp. (NWS) & A. H. Belo Corp. (AHC)</h4>
	<div class="btn-group" data-toggle="buttons">
	  <label id="priceBtn" class="btn btn-primary active">
	    <input type="radio" name="options"  autocomplete="off" checked>Weekly Price
	  </label>
	  <label id="changeBtn" class="btn btn-primary">
	    <input type="radio" name="options" autocomplete="off">Weekly Change
	  </label>
	</div>

</div>


<link rel="stylesheet" type="text/css" href="style.css">
<script src="script.js"></script>

</body>
</html>

style.css

body {
  font: 10px arial, sans-serif;
}
.btn-group{
	float:right;
}
#chart{
	width:700px;
	margin-top: 20px;
}

.axis path,
.axis line {
  fill: none;
  stroke: #000;
  shape-rendering: crispEdges;
}

.x.axis path {
  display: none;
}

.line {
  fill: none;
  stroke: steelblue;
  stroke-width: 1.5px;
}
.zeroAxis{
	stroke:black;
	shape-rendering: crispEdges;
}
.labels text{
	font-size: 16px;
	font-weight: bold;
	fill:white;
}

wk_changes.csv

date,AHC,NWS
2015-02-23,-0.0125,0.0089982004
2015-02-17,-0.0361445783,0.0158439976
2015-02-09,-0.0097613883,0.0405833862
2015-02-02,0.0313199105,0.0921052632
2015-01-26,-0.0407725322,-0.0150068213
2015-01-20,-0.0371900826,0.0082530949
2015-01-12,-0.0638297872,-0.0228494624
2015-01-05,0.0009680542,-0.0066755674
2014-12-29,-0.0254716981,-0.0039893617
2014-12-22,0.021194605,0.0308430432
2014-12-15,0.0318091451,-0.0040955631
2014-12-08,-0.0098425197,-0.0380827315
2014-12-01,0.0314720812,0.0119601329
2014-11-24,0.0478723404,0.0148347943
2014-11-17,-0.009483667,-0.0100133511
2014-11-10,0.0042328042,0.0094339623
2014-11-03,0.0074626866,-0.0139534884
2014-10-27,-0.0010649627,-0.0176240209
2014-10-20,0.0184381779,0.0743338008
2014-10-13,0.0720930233,-0.0474281897
2014-10-06,-0.0486725664,-0.0501269036
2014-09-29,0.0145903479,-0.0523150932
2014-09-22,-0.0011210762,-0.0124703088
2014-09-15,0.0194285714,0.0029779631
2014-09-08,-0.0621650589,-0.0345025877
2014-09-02,-0.0063897764,0.0075318656
2014-08-25,0.0184381779,0.0070011669
2014-08-18,0.0076502732,0.0196311719
2014-08-11,0.0021905805,0.0035820896
2014-08-04,0.0830367734,-0.0351382488
2014-07-28,-0.061247216,-0.0141964793
2014-07-21,-0.0228509249,-0.002266289
2014-07-14,-0.0086299892,0.0051252847
2014-07-07,-0.050204918,-0.0118176702
2014-06-30,0.0051493306,0.0125356125
2014-06-23,0.0440860215,0.0257159556
2014-06-16,-0.01378579,0.022713688
2014-06-09,-0.0298353909,-0.031268095
2014-06-02,0.0199370409,0.0409885473
2014-05-27,0.0449561404,-0.0089605735
2014-05-19,0.0961538462,0.0023952096
2014-05-12,0.0060459492,-0.0402298851
2014-05-05,0.046835443,0.0338680927
2014-04-28,-0.0050377834,0.0029797378
2014-04-21,0.0298313878,0.0188221008
2014-04-14,-0.006443299,0.0129151292
2014-04-07,-0.0064020487,-0.026929982
2014-03-31,-0.0533333333,0.0102781137
2014-03-24,-0.0873893805,-0.0276308054
2014-03-17,0.0261066969,-0.0023460411
2014-03-10,0.0704738761,-0.027936146
2014-03-03,0.0510855683,-0.0195640022
2014-02-24,0.0711354309,0.0389082462
2014-02-18,0.0781710914,0
2014-02-10,0.2372262774,0.0129411765
2014-02-03,-0.0231729055,0.0890454837
2014-01-27,0.0181488203,-0.0334365325
2014-01-21,-0.0247787611,-0.0511163337
2014-01-13,0.0721062619,-0.0184544406
2014-01-06,-0.0037807183,-0.0219966159
2014-01-02,0,0

wk_prices.csv

date,AHC,NWS
2015-02-23,8.69,16.82
2015-02-17,8.8,16.67
2015-02-09,9.13,16.41
2015-02-02,9.22,15.77
2015-01-26,8.94,14.44
2015-01-20,9.32,14.66
2015-01-12,9.68,14.54
2015-01-05,10.34,14.88
2014-12-29,10.33,14.98
2014-12-22,10.6,15.04
2014-12-15,10.38,14.59
2014-12-08,10.06,14.65
2014-12-01,10.16,15.23
2014-11-24,9.85,15.05
2014-11-17,9.4,14.83
2014-11-10,9.49,14.98
2014-11-03,9.45,14.84
2014-10-27,9.38,15.05
2014-10-20,9.39,15.32
2014-10-13,9.22,14.26
2014-10-06,8.6,14.97
2014-09-29,9.04,15.76
2014-09-22,8.91,16.63
2014-09-15,8.92,16.84
2014-09-08,8.75,16.79
2014-09-02,9.33,17.39
2014-08-25,9.39,17.26
2014-08-18,9.22,17.14
2014-08-11,9.15,16.81
2014-08-04,9.13,16.75
2014-07-28,8.43,17.36
2014-07-21,8.98,17.61
2014-07-14,9.19,17.65
2014-07-07,9.27,17.56
2014-06-30,9.76,17.77
2014-06-23,9.71,17.55
2014-06-16,9.3,17.11
2014-06-09,9.43,16.73
2014-06-02,9.72,17.27
2014-05-27,9.53,16.59
2014-05-19,9.12,16.74
2014-05-12,8.32,16.7
2014-05-05,8.27,17.4
2014-04-28,7.9,16.83
2014-04-21,7.94,16.78
2014-04-14,7.71,16.47
2014-04-07,7.76,16.26
2014-03-31,7.81,16.71
2014-03-24,8.25,16.54
2014-03-17,9.04,17.01
2014-03-10,8.81,17.05
2014-03-03,8.23,17.54
2014-02-24,7.83,17.89
2014-02-18,7.31,17.22
2014-02-10,6.78,17.22
2014-02-03,5.48,17
2014-01-27,5.61,15.61
2014-01-21,5.51,16.15
2014-01-13,5.65,17.02
2014-01-06,5.27,17.34
2014-01-02,5.29,17.73