Skip to content

Instantly share code, notes, and snippets.

@benvandyke
Created January 16, 2014 17:50
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save benvandyke/8459843 to your computer and use it in GitHub Desktop.
Save benvandyke/8459843 to your computer and use it in GitHub Desktop.
Plotting a trendline with D3.js
<!DOCTYPE html>
<meta charset="utf-8">
<title>Plotting a Trendline with D3.js</title>
<style>
.line {
stroke: blue;
fill:none;
stroke-width: 3;
}
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-size: 10px;
font-family: sans-serif;
}
.text-label {
font-size: 10px;
font-family: sans-serif;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var height = 300;
var width = 600;
var margin = {top: 20, right:20, bottom: 50, left: 20};
// formatters for axis and labels
var currencyFormat = d3.format("$0.2f");
var decimalFormat = d3.format("0.2f");
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("g")
.attr("class", "y axis");
svg.append("g")
.attr("class", "x axis");
var xScale = d3.scale.ordinal()
.rangeRoundBands([margin.left, width], .1);
var yScale = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
d3.csv("usd_euro.csv", function(data) {
// extract the x labels for the axis and scale domain
var xLabels = data.map(function (d) { return d['yearmonth']; })
xScale.domain(xLabels);
yScale.domain([0, Math.round(d3.max(data, function(d) { return parseFloat(d['rate']); }))]);
var line = d3.svg.line()
.x(function(d) { return xScale(d['yearmonth']); })
.y(function(d) { return yScale(d['rate']); });
svg.append("path")
.datum(data)
.attr("class","line")
.attr("d", line);
svg.select(".x.axis")
.attr("transform", "translate(0," + (height) + ")")
.call(xAxis.tickValues(xLabels.filter(function(d, i) {
if (i % 12 == 0)
return d;
})))
.selectAll("text")
.style("text-anchor","end")
.attr("transform", function(d) {
return "rotate(-45)";
});
svg.select(".y.axis")
.attr("transform", "translate(" + (margin.left) + ",0)")
.call(yAxis.tickFormat(currencyFormat));
// chart title
svg.append("text")
.attr("x", (width + (margin.left + margin.right) )/ 2)
.attr("y", 0 + margin.top)
.attr("text-anchor", "middle")
.style("font-size", "16px")
.style("font-family", "sans-serif")
.text("USD/EURO Exhange Rate");
// x axis label
svg.append("text")
.attr("x", (width + (margin.left + margin.right) )/ 2)
.attr("y", height + margin.bottom)
.attr("class", "text-label")
.attr("text-anchor", "middle")
.text("Year-Month");
// get the x and y values for least squares
var xSeries = d3.range(1, xLabels.length + 1);
var ySeries = data.map(function(d) { return parseFloat(d['rate']); });
var leastSquaresCoeff = leastSquares(xSeries, ySeries);
// apply the reults of the least squares regression
var x1 = xLabels[0];
var y1 = leastSquaresCoeff[0] + leastSquaresCoeff[1];
var x2 = xLabels[xLabels.length - 1];
var y2 = leastSquaresCoeff[0] * xSeries.length + leastSquaresCoeff[1];
var trendData = [[x1,y1,x2,y2]];
var trendline = svg.selectAll(".trendline")
.data(trendData);
trendline.enter()
.append("line")
.attr("class", "trendline")
.attr("x1", function(d) { return xScale(d[0]); })
.attr("y1", function(d) { return yScale(d[1]); })
.attr("x2", function(d) { return xScale(d[2]); })
.attr("y2", function(d) { return yScale(d[3]); })
.attr("stroke", "black")
.attr("stroke-width", 1);
// display equation on the chart
svg.append("text")
.text("eq: " + decimalFormat(leastSquaresCoeff[0]) + "x + " +
decimalFormat(leastSquaresCoeff[1]))
.attr("class", "text-label")
.attr("x", function(d) {return xScale(x2) - 60;})
.attr("y", function(d) {return yScale(y2) - 30;});
// display r-square on the chart
svg.append("text")
.text("r-sq: " + decimalFormat(leastSquaresCoeff[2]))
.attr("class", "text-label")
.attr("x", function(d) {return xScale(x2) - 60;})
.attr("y", function(d) {return yScale(y2) - 10;});
});
// returns slope, intercept and r-square of the line
function leastSquares(xSeries, ySeries) {
var reduceSumFunc = function(prev, cur) { return prev + cur; };
var xBar = xSeries.reduce(reduceSumFunc) * 1.0 / xSeries.length;
var yBar = ySeries.reduce(reduceSumFunc) * 1.0 / ySeries.length;
var ssXX = xSeries.map(function(d) { return Math.pow(d - xBar, 2); })
.reduce(reduceSumFunc);
var ssYY = ySeries.map(function(d) { return Math.pow(d - yBar, 2); })
.reduce(reduceSumFunc);
var ssXY = xSeries.map(function(d, i) { return (d - xBar) * (ySeries[i] - yBar); })
.reduce(reduceSumFunc);
var slope = ssXY / ssXX;
var intercept = yBar - (xBar * slope);
var rSquare = Math.pow(ssXY, 2) / (ssXX * ssYY);
return [slope, intercept, rSquare];
}
</script>
</body>
yearmonth rate
1999-01 1.1591
1999-02 1.1203
1999-03 1.0886
1999-04 1.0701
1999-05 1.063
1999-06 1.0377
1999-07 1.037
1999-08 1.0605
1999-09 1.0497
1999-10 1.0706
1999-11 1.0328
1999-12 1.011
2000-01 1.0131
2000-02 0.9834
2000-03 0.9643
2000-04 0.9449
2000-05 0.9059
2000-06 0.9505
2000-07 0.9386
2000-08 0.9045
2000-09 0.8695
2000-10 0.8525
2000-11 0.8552
2000-12 0.8983
2001-01 0.9376
2001-02 0.9205
2001-03 0.9083
2001-04 0.8925
2001-05 0.8753
2001-06 0.853
2001-07 0.8615
2001-08 0.9014
2001-09 0.9114
2001-10 0.905
2001-11 0.8883
2001-12 0.8912
2002-01 0.8832
2002-02 0.8707
2002-03 0.8766
2002-04 0.886
2002-05 0.917
2002-06 0.9561
2002-07 0.9935
2002-08 0.9781
2002-09 0.9806
2002-10 0.9812
2002-11 1.0013
2002-12 1.0194
2003-01 1.0622
2003-02 1.0785
2003-03 1.0797
2003-04 1.0862
2003-05 1.1556
2003-06 1.1674
2003-07 1.1365
2003-08 1.1155
2003-09 1.1267
2003-10 1.1714
2003-11 1.171
2003-12 1.2298
2004-01 1.2638
2004-02 1.264
2004-03 1.2261
2004-04 1.1989
2004-05 1.2
2004-06 1.2146
2004-07 1.2266
2004-08 1.2191
2004-09 1.2224
2004-10 1.2507
2004-11 1.2997
2004-12 1.3406
2005-01 1.3123
2005-02 1.3013
2005-03 1.3185
2005-04 1.2943
2005-05 1.2697
2005-06 1.2155
2005-07 1.2041
2005-08 1.2295
2005-09 1.2234
2005-10 1.2022
2005-11 1.1789
2005-12 1.1861
2006-01 1.2126
2006-02 1.194
2006-03 1.2028
2006-04 1.2273
2006-05 1.2767
2006-06 1.2661
2006-07 1.2681
2006-08 1.281
2006-09 1.2722
2006-10 1.2617
2006-11 1.2888
2006-12 1.3205
2007-01 1.2993
2007-02 1.308
2007-03 1.3246
2007-04 1.3513
2007-05 1.3518
2007-06 1.3421
2007-07 1.3726
2007-08 1.3626
2007-09 1.391
2007-10 1.4233
2007-11 1.4683
2007-12 1.4559
2008-01 1.4728
2008-02 1.4759
2008-03 1.552
2008-04 1.5754
2008-05 1.5554
2008-06 1.5562
2008-07 1.5759
2008-08 1.4955
2008-09 1.4342
2008-10 1.3266
2008-11 1.2744
2008-12 1.3511
2009-01 1.3244
2009-02 1.2797
2009-03 1.305
2009-04 1.3199
2009-05 1.3646
2009-06 1.4014
2009-07 1.4092
2009-08 1.4266
2009-09 1.4575
2009-10 1.4821
2009-11 1.4908
2009-12 1.4579
2010-01 1.4266
2010-02 1.368
2010-03 1.357
2010-04 1.3417
2010-05 1.2563
2010-06 1.2223
2010-07 1.2811
2010-08 1.2903
2010-09 1.3103
2010-10 1.3901
2010-11 1.3654
2010-12 1.3221
2011-01 1.3371
2011-02 1.3656
2011-03 1.402
2011-04 1.446
2011-05 1.4335
2011-06 1.4403
2011-07 1.4275
2011-08 1.4333
2011-09 1.3747
2011-10 1.3732
2011-11 1.3558
2011-12 1.3155
2012-01 1.291
2012-02 1.3238
2012-03 1.3208
2012-04 1.316
2012-05 1.2806
2012-06 1.2541
2012-07 1.2278
2012-08 1.2406
2012-09 1.2885
2012-10 1.2974
2012-11 1.2837
2012-12 1.3119
2013-01 1.3304
2013-02 1.3347
2013-03 1.2953
2013-04 1.3025
2013-05 1.2983
2013-06 1.3197
2013-07 1.3088
2013-08 1.3314
2013-09 1.3364
2013-10 1.3646
2013-11 1.3491
2013-12 1.3708
2014-01 1.3624
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment