Fits a straight line to data using the Least Squares method.
"Least squares" means that the overall solution minimizes the sum of the squares of the errors made in the results of every single equation.
Least squares function by Ben van Dyke.
Fits a straight line to data using the Least Squares method.
"Least squares" means that the overall solution minimizes the sum of the squares of the errors made in the results of every single equation.
Least squares function by Ben van Dyke.
x | y | |
---|---|---|
0 | 0 | |
1 | 1 | |
2 | 3.5 | |
3 | 6 | |
4 | 7 | |
5 | 9 | |
6 | 10.5 | |
7 | 11 | |
8 | 11.5 | |
9 | 11 | |
10 | 17.5 | |
11 | 23 | |
12 | 20 | |
13 | 27 | |
14 | 30.5 | |
15 | 37 | |
16 | 46 | |
17 | 53 | |
18 | 56.5 | |
19 | 47 | |
20 | 54 | |
21 | 51 | |
22 | 43.5 | |
23 | 40 | |
24 | 40 | |
25 | 44 | |
26 | 46 | |
27 | 53 | |
28 | 46.5 | |
29 | 40 | |
30 | 49.5 | |
31 | 53 | |
32 | 55.5 | |
33 | 47 | |
34 | 57 | |
35 | 50 | |
36 | 52 | |
37 | 58 | |
38 | 62.5 | |
39 | 66 | |
40 | 72 | |
41 | 66 | |
42 | 60 | |
43 | 56 | |
44 | 63 | |
45 | 63 | |
46 | 56 | |
47 | 59 | |
48 | 61 | |
49 | 51 | |
50 | 58.5 | |
51 | 60 | |
52 | 65.5 | |
53 | 68 | |
54 | 66.5 | |
55 | 68 | |
56 | 75.5 | |
57 | 87 | |
58 | 85.5 | |
59 | 79 | |
60 | 77.5 | |
61 | 82 | |
62 | 75.5 | |
63 | 69 | |
64 | 71.5 | |
65 | 73 | |
66 | 75.5 | |
67 | 76 | |
68 | 88.5 | |
69 | 79 | |
70 | 70 | |
71 | 75 | |
72 | 83.5 | |
73 | 91 | |
74 | 84.5 | |
75 | 85 | |
76 | 78 | |
77 | 92 | |
78 | 98 | |
79 | 97 | |
80 | 99 | |
81 | 100 | |
82 | 102.5 | |
83 | 102 | |
84 | 112 | |
85 | 121 | |
86 | 112.5 | |
87 | 115 | |
88 | 111 | |
89 | 113 | |
90 | 128 | |
91 | 135 | |
92 | 126.5 | |
93 | 131 | |
94 | 120 | |
95 | 119 | |
96 | 130.5 | |
97 | 130 | |
98 | 130 | |
99 | 131 | |
100 | 143.5 |
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<html> | |
<head> | |
<title>Least Squares Fit</title> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<style> | |
body { | |
font-family: "Helvetica", sans-serif; | |
font-size: 10px; | |
margin: 8px; | |
} | |
.axis { | |
font: 10px sans-serif; | |
} | |
.axis path, | |
.axis line { | |
fill: none; | |
stroke: #000; | |
shape-rendering: crispEdges; | |
} | |
.path { | |
fill: none; | |
stroke: rgba(189,54,19,1); | |
stroke-width: 2px; | |
} | |
.trendline { | |
stroke: rgba(208,199,198,1); | |
stroke-width: 1px; | |
stroke-opacity: 0.75; | |
} | |
</style> | |
<body> | |
<script> | |
(function() { | |
var margin = {top: 20, right: 20, bottom: 40, left: 40}, | |
width = 944 - margin.left - margin.right, | |
height = 480 - margin.top - margin.bottom; | |
var numSteps = 100; | |
var yMax = 150; | |
// X scale | |
var xScale = d3.scale.linear() | |
.domain([0, numSteps]) | |
.range([0, width]); | |
// Y scale | |
var yScale = d3.scale.linear() | |
.domain([0, yMax]) | |
.range([height, 0]); | |
// Draw SVG | |
var svg = d3.select("body").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 + ")"); | |
// Draw axes | |
var xAxis = d3.svg.axis() | |
.scale(xScale) | |
.ticks(5) | |
.orient("bottom"); | |
svg.append("g") | |
.attr("class", "x axis") | |
.attr("transform", "translate(0," + height + ")") | |
.call(xAxis); | |
var yAxis = d3.svg.axis() | |
.scale(yScale) | |
.ticks(3) | |
.orient("left"); | |
svg.append("g") | |
.attr("class", "y axis") | |
.call(yAxis); | |
// equ text in top left | |
var eqText = svg.append("text") | |
.attr("id", "eq-text") | |
.style("text-anchor", "start") | |
.attr("x", 8) | |
.attr("y", 8); | |
// Get data from CSV and draw line | |
d3.csv("data.csv", function(error, data) { | |
var lineFunction = d3.svg.line() | |
.x(function(d) { return xScale(d['x']); }) | |
.y(function(d) { return yScale(d['y']); }); | |
svg.append("path") | |
.datum(data) | |
.attr("class", "path") | |
.attr("d", lineFunction); | |
// get the x and y values for least squares | |
var xSeries = data.map(function(d) { return parseFloat(d['x']); }); | |
var ySeries = data.map(function(d) { return parseFloat(d['y']); }); | |
console.log('xSeries: ' + xSeries); | |
console.log('ySeries: ' + ySeries); | |
var leastSquaresCoeff = leastSquares(xSeries, ySeries); | |
var slope = leastSquaresCoeff[0]; | |
var intercept = leastSquaresCoeff[1]; | |
var rSquare = leastSquaresCoeff[2]; | |
console.log("slope: " + slope); | |
console.log("intercept: " + intercept); | |
var trendline = svg.append("line") | |
.attr("x1", xScale(0.)) | |
.attr("y1", yScale(intercept)) | |
.attr("x2", xScale(numSteps)) | |
.attr("y2", yScale(numSteps*slope + intercept)) | |
.classed("trendline", true); | |
d3.select('#eq-text').text("y = " + format2d(slope) | |
+ "x + " + format2d(intercept)); | |
}) | |
// returns slope, intercept and r-square of the line | |
// from: http://bl.ocks.org/benvandyke/8459843 | |
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]; | |
} | |
var format2d = d3.format("0.1f"); | |
})() | |
</script> | |
</body> | |
</html> |