Skip to content

Instantly share code, notes, and snippets.

@daluu
Forked from bunkat/index.html
Last active March 10, 2020 20:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save daluu/5bb59ef3f3fed3de227535da367649ba to your computer and use it in GitHub Desktop.
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
// d3.legend.js
// (C) 2012 ziggy.jonsson.nyc@gmail.com
// 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
}
})()
<!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>
/**
* @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}});
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; });
@daluu
Copy link
Author

daluu commented Jul 12, 2017

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.

@valon90
Copy link

valon90 commented Nov 23, 2018

Any way to contact you for these charts and how you've created them? I love the work you've done (5bb59ef3f3fed3de227535da367649ba).

@daluu
Copy link
Author

daluu commented Mar 10, 2020

@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