An simple multi-line chart that pivots from raw values to percent change using separate CSVs.
Last active
February 12, 2021 16:31
-
-
Save hobbes7878/d315122a1759cba1f41b to your computer and use it in GitHub Desktop.
#NICAR15: D3 stock chart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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", "+%" ); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment