-
-
Save daluu/5bb59ef3f3fed3de227535da367649ba to your computer and use it in GitHub Desktop.
Sample trendline and linear, polynomial, exponential, logarithmic, and power regression analysis using regression.js
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
// d3.legend.js | |
// (C) 2012 [email protected] | |
// MIT licence | |
(function() { | |
d3.legend = function(g) { | |
g.each(function() { | |
var g= d3.select(this), | |
items = {}, | |
svg = d3.select(g.property("nearestViewportElement")), | |
legendPadding = g.attr("data-style-padding") || 5, | |
lb = g.selectAll(".legend-box").data([true]), | |
li = g.selectAll(".legend-items").data([true]) | |
lb.enter().append("rect").classed("legend-box",true) | |
li.enter().append("g").classed("legend-items",true) | |
svg.selectAll("[data-legend]").each(function() { | |
var self = d3.select(this) | |
items[self.attr("data-legend")] = { | |
pos : self.attr("data-legend-pos") || this.getBBox().y, | |
color : self.attr("data-legend-color") != undefined ? self.attr("data-legend-color") : self.style("fill") != 'none' ? self.style("fill") : self.style("stroke") | |
} | |
}) | |
items = d3.entries(items).sort(function(a,b) { return a.value.pos-b.value.pos}) | |
li.selectAll("text") | |
.data(items,function(d) { return d.key}) | |
.call(function(d) { d.enter().append("text")}) | |
.call(function(d) { d.exit().remove()}) | |
.attr("y",function(d,i) { return i+"em"}) | |
.attr("x","1em") | |
.text(function(d) { ;return d.key}) | |
li.selectAll("circle") | |
.data(items,function(d) { return d.key}) | |
.call(function(d) { d.enter().append("circle")}) | |
.call(function(d) { d.exit().remove()}) | |
.attr("cy",function(d,i) { return i-0.25+"em"}) | |
.attr("cx",0) | |
.attr("r","0.4em") | |
.style("fill",function(d) { console.log(d.value.color);return d.value.color}) | |
// Reposition and resize the box | |
var lbbox = li[0][0].getBBox() | |
lb.attr("x",(lbbox.x-legendPadding)) | |
.attr("y",(lbbox.y-legendPadding)) | |
.attr("height",(lbbox.height+2*legendPadding)) | |
.attr("width",(lbbox.width+2*legendPadding)) | |
}) | |
return g | |
} | |
})() |
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> | |
<title>Trendline (linear/polynomial/exponential/logarithmic/power) regressions analysis</title> | |
<style> | |
.chart { | |
} | |
.main text { | |
font: 8px sans-serif; | |
} | |
.axis line, .axis path { | |
shape-rendering: crispEdges; | |
stroke: black; | |
fill: none; | |
} | |
circle { | |
fill: steelblue; | |
} | |
.legend rect { | |
fill:white; | |
stroke:black; | |
opacity:0.8; | |
font: 12px sans-serif; | |
} | |
</style> | |
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script> | |
</head> | |
<body> | |
<div class='content'> | |
<!-- /the chart goes here --> | |
</div> | |
<script type="text/javascript" src="regression.min.js"></script> | |
<script type="text/javascript" src="d3.legend.js"></script> | |
<script type="text/javascript" src="scatterchart.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
/** | |
* @license | |
* | |
* Regression.JS - Regression functions for javascript | |
* http://tom-alexander.github.com/regression-js/ | |
* | |
* copyright(c) 2013 Tom Alexander | |
* Licensed under the MIT license. | |
* | |
* @module regression - Least-squares regression functions for JavaScript | |
**/ | |
!function(a,b){var c;return c="function"==typeof define&&define.amd?define("regression",b):"undefined"!=typeof module?module.exports=b():a.regression=b()}(this,function(){"use strict";function a(a,b){var c=a.reduce(function(a,b){return a+b[1]},0),d=c/a.length,e=a.reduce(function(a,b){var c=b[1]-d;return a+c*c},0),f=a.reduce(function(a,c,d){var e=b[d],f=c[1]-e[1];return a+f*f},0);return 1-f/e}function b(a,b){var c=0,d=0,e=0,f=0,g=0,h=a.length-1,i=new Array(b);for(c=0;h>c;c++){for(f=c,d=c+1;h>d;d++)Math.abs(a[c][d])>Math.abs(a[c][f])&&(f=d);for(e=c;h+1>e;e++)g=a[e][c],a[e][c]=a[e][f],a[e][f]=g;for(d=c+1;h>d;d++)for(e=h;e>=c;e--)a[e][d]-=a[e][c]*a[c][d]/a[c][c]}for(d=h-1;d>=0;d--){for(g=0,e=d+1;h>e;e++)g+=a[e][d]*i[e];i[d]=(a[h][d]-g)/a[d][d]}return i}function c(a,b){var c=Math.pow(10,b);return Math.round(a*c)/c}var d,e=2,f={linear:function(b,d,e){for(var f,g,h,i=[0,0,0,0,0],j=b.length,k=0;j>k;k++)null!==b[k][1]&&(i[0]+=b[k][0],i[1]+=b[k][1],i[2]+=b[k][0]*b[k][0],i[3]+=b[k][0]*b[k][1],i[4]+=b[k][1]*b[k][1]);return g=(j*i[3]-i[0]*i[1])/(j*i[2]-i[0]*i[0]),h=i[1]/j-g*i[0]/j,f=b.map(function(a){var b=a[0];return[b,g*b+h]}),{r2:a(b,f),equation:[g,h],points:f,string:"y = "+c(g,e.precision)+"x + "+c(h,e.precision)}},linearthroughorigin:function(b,d,e){for(var f,g,h=[0,0],i=0;i<b.length;i++)null!==b[i][1]&&(h[0]+=b[i][0]*b[i][0],h[1]+=b[i][0]*b[i][1]);return f=h[1]/h[0],g=b.map(function(a){var b=a[0];return[b,f*b]}),{r2:a(b,g),equation:[f],points:g,string:"y = "+c(f,e.precision)+"x"}},exponential:function(b,d,e){for(var f,g,h,i,j=[0,0,0,0,0,0],k=0;k<b.length;k++)null!==b[k][1]&&(j[0]+=b[k][0],j[1]+=b[k][1],j[2]+=b[k][0]*b[k][0]*b[k][1],j[3]+=b[k][1]*Math.log(b[k][1]),j[4]+=b[k][0]*b[k][1]*Math.log(b[k][1]),j[5]+=b[k][0]*b[k][1]);return f=j[1]*j[2]-j[5]*j[5],g=Math.exp((j[2]*j[3]-j[5]*j[4])/f),h=(j[1]*j[4]-j[5]*j[3])/f,i=b.map(function(a){var b=a[0];return[b,g*Math.exp(h*b)]}),{r2:a(b,i),equation:[g,h],points:i,string:"y = "+c(g,e.precision)+"e^("+c(h,e.precision)+"x)"}},logarithmic:function(b,d,e){for(var f,g,h,i=[0,0,0,0],j=b.length,k=0;j>k;k++)null!==b[k][1]&&(i[0]+=Math.log(b[k][0]),i[1]+=b[k][1]*Math.log(b[k][0]),i[2]+=b[k][1],i[3]+=Math.pow(Math.log(b[k][0]),2));return g=(j*i[1]-i[2]*i[0])/(j*i[3]-i[0]*i[0]),f=(i[2]-g*i[0])/j,h=b.map(function(a){var b=a[0];return[b,f+g*Math.log(b)]}),{r2:a(b,h),equation:[f,g],points:h,string:"y = "+c(f,e.precision)+" + "+c(g,e.precision)+" ln(x)"}},power:function(b,d,e){for(var f,g,h,i=[0,0,0,0],j=b.length,k=0;j>k;k++)null!==b[k][1]&&(i[0]+=Math.log(b[k][0]),i[1]+=Math.log(b[k][1])*Math.log(b[k][0]),i[2]+=Math.log(b[k][1]),i[3]+=Math.pow(Math.log(b[k][0]),2));return g=(j*i[1]-i[2]*i[0])/(j*i[3]-i[0]*i[0]),f=Math.exp((i[2]-g*i[0])/j),h=b.map(function(a){var b=a[0];return[b,f*Math.pow(b,g)]}),{r2:a(b,h),equation:[f,g],points:h,string:"y = "+c(f,e.precision)+"x^"+c(g,e.precision)}},polynomial:function(d,e,f){var g,h,i,j,k,l,m,n,o=[],p=[],q=0,r=0,s=d.length;for(h="undefined"==typeof e?3:e+1,i=0;h>i;i++){for(k=0;s>k;k++)null!==d[k][1]&&(q+=Math.pow(d[k][0],i)*d[k][1]);for(o.push(q),q=0,g=[],j=0;h>j;j++){for(k=0;s>k;k++)null!==d[k][1]&&(r+=Math.pow(d[k][0],i+j));g.push(r),r=0}p.push(g)}for(p.push(o),m=b(p,h),l=d.map(function(a){var b=a[0],c=m.reduce(function(a,c,d){return a+c*Math.pow(b,d)},0);return[b,c]}),n="y = ",i=m.length-1;i>=0;i--)n+=i>1?c(m[i],f.precision)+"x^"+i+" + ":1===i?c(m[i],f.precision)+"x + ":c(m[i],f.precision);return{r2:a(d,l),equation:m,points:l,string:n}},lastvalue:function(b,d,e){for(var f=[],g=null,h=0;h<b.length;h++)null!==b[h][1]&&isFinite(b[h][1])?(g=b[h][1],f.push([b[h][0],b[h][1]])):f.push([b[h][0],g]);return{r2:a(b,f),equation:[g],points:f,string:""+c(g,e.precision)}}};return d=function(a,b,c,d){var g="object"==typeof c&&"undefined"==typeof d?c:d||{};return g.precision||(g.precision=e),"string"==typeof a?f[a.toLowerCase()](b,c,g):null}}); |
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 data = [[500,2.5], [1000,3], [1500,3], [2000,3.3], [3000,3.6], [4000,4], [5500,4.8], [6000,5], [7000,5], [8000,5.5], [9000,6], [12000,7], [14000,8], [15000,8], [18000,9], [20000,10], [21000,10], [24000,11], [28000,12], [30000,13], [50000,18]]; | |
var margin = {top: 20, right: 80, bottom: 30, left: 50} | |
, width = 960 - margin.left - margin.right | |
, height = 500 - margin.top - margin.bottom; | |
var x = d3.scale.linear() | |
.domain([0, d3.max(data, function(d) { return d[0]; })]) | |
.range([ 0, width ]); | |
var y = d3.scale.linear() | |
.domain([0, d3.max(data, function(d) { return d[1]; })]) | |
.range([ height, 0 ]); | |
var chart = d3.select('body') | |
.append('svg:svg') | |
.attr('width', width + margin.right + margin.left) | |
.attr('height', height + margin.top + margin.bottom) | |
.attr('class', 'chart') | |
var main = chart.append('g') | |
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')') | |
.attr('width', width) | |
.attr('height', height) | |
.attr('class', 'main') | |
// draw the x axis | |
var xAxis = d3.svg.axis() | |
.scale(x) | |
.orient('bottom'); | |
main.append('g') | |
.attr('transform', 'translate(0,' + height + ')') | |
.attr('class', 'main axis date') | |
.call(xAxis); | |
// draw the y axis | |
var yAxis = d3.svg.axis() | |
.scale(y) | |
.orient('left'); | |
main.append('g') | |
.attr('transform', 'translate(0,0)') | |
.attr('class', 'main axis date') | |
.call(yAxis); | |
var g = main.append("svg:g"); | |
g.selectAll("scatter-dots") | |
.data(data) | |
.enter().append("svg:circle") | |
.attr("cx", function (d,i) { return x(d[0]); } ) | |
.attr("cy", function (d) { return y(d[1]); } ) | |
.attr("r", 8); | |
//do trend analysis | |
var linReg = regression('linear', data); | |
var polyReg = regression('polynomial', data, 2); | |
var expoReg = regression('exponential', data); | |
var powReg = regression('power', data); | |
var logReg = regression('logarithmic', data); | |
var linRegEq = "Lin: y = " + linReg.equation[0].toFixed(4) + "x + " + linReg.equation[1].toFixed(2) + ", r2 = " + linReg.r2.toFixed(3); | |
var polyRegEq = "Poly: y = " + polyReg.equation[2].toFixed(4) + "x^2 + " + polyReg.equation[1].toFixed(4) + "x + " + polyReg.equation[0].toFixed(2) + ", r2 = " + polyReg.r2.toFixed(3); | |
var expoRegEq = "Exp: y = " + expoReg.equation[0].toFixed(4) + "e^(" + expoReg.equation[1].toFixed(4) + "x), r2 = " + expoReg.r2.toFixed(3); | |
var powRegEq = "Pow: y = " + powReg.equation[0].toFixed(4) + "x^" + powReg.equation[1].toFixed(2) + ", r2 = " + powReg.r2.toFixed(3); | |
var logRegEq = "Log: y = " + logReg.string + ", r2 = " + logReg.r2.toFixed(3); | |
var allEqs = "Trends: " + linRegEq + "; " + polyRegEq + "; " + expoRegEq + "; " + powRegEq + "; " + logRegEq; | |
var color = d3.scale.category20(); | |
var coloredLines = ["linear", "polynomial", "exponential", "power", "logarithmic"]; | |
color.domain(coloredLines); | |
var regressionLinesToPlot = color.domain().map(function(name) { | |
return { | |
name: name, | |
values: function() { | |
var extrapolatedPts = []; | |
for(var i = 0; i < data.length; i++){ | |
var val = data[i][0]; | |
switch(name){ | |
case "polynomial": | |
extrapolatedPts.push({x: val, y: polyReg.equation[2] * Math.pow(val,2) + polyReg.equation[1] * val + polyReg.equation[0]}); | |
break; | |
case "exponential": | |
extrapolatedPts.push({x: val, y: expoReg.equation[0] * Math.exp(val * expoReg.equation[1])}); //or use numbers.js per https://gist.github.com/zikes/4279121, var regression = numbers.statistic.exponentialRegression(pts); | |
break; | |
case "power": | |
extrapolatedPts.push({x: val, y: powReg.equation[0] * Math.pow(val,powReg.equation[1])}); | |
break; | |
case "logarithmic": | |
extrapolatedPts.push({x: val, y: logReg.equation[0] + logReg.equation[1] * Math.log(val)}); | |
break; | |
case "linear": | |
default: | |
extrapolatedPts.push({x: val, y: linReg.equation[0] * val + linReg.equation[1]}); | |
} | |
} | |
return extrapolatedPts; | |
}() | |
}; | |
}); | |
var line = d3.svg.line() | |
.interpolate("basis") | |
.x(function(d) { return x(d.x); }) | |
.y(function(d) { return y(d.y); }); | |
var plotFields = main.selectAll(".lines-to-plot") | |
.data(regressionLinesToPlot) | |
.enter().append("g") | |
.attr("class", "lines-to-plot"); | |
plotFields.append("path") | |
.attr("class", "line") | |
.attr("d", function(d) { return line(d.values); }) | |
.attr("data-legend", function(d) { return d.name}) | |
.style("stroke", function(d) { return color(d.name); }) | |
.style("stroke-width", "1.5px") | |
.style("fill", "none"); | |
plotFields.append("text") | |
.datum(function(d) { return {name: d.name, value: d.values[d.values.length - 1]}; }) | |
.attr("transform", function(d) { return "translate(" + x(d.value.x) + "," + y(d.value.y) + ")"; }) | |
.attr("x", 3) | |
.attr("dy", ".35em") | |
.text(function(d) { return d.name; }); | |
var legend = main.append("g") | |
.attr("class","legend") | |
.attr("transform","translate(50,30)") | |
.style("font-size","12px") | |
.call(d3.legend); | |
var regLineInfo = main.append("g") | |
.attr("class","legend") | |
.attr("transform","translate(20,0)") | |
.style("font-size","8px") | |
.append("text") | |
.text(function() { return allEqs; }); |
Any way to contact you for these charts and how you've created them? I love the work you've done (5bb59ef3f3fed3de227535da367649ba).
@valon90, pardon me, didn't see your comment until now. Find my contact info from my profile.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
UI rendering could be cleaned up, improved, but this gives you an idea of trendline plotting and regression function analysis of the data points. Comparable to doing the same trendline analysis in Microsoft Excel, I verified at least for linear regressions.