I'm writing some analytics modules for the site I'm working on and I need to estimate the total views after the current hour. I have data for every minute up to the current minute, so if the time is 12:28, I will have an array that looks something like this:
0: "21410"
1: "21886"
2: "21837"
3: "21895"
4: "21564"
5: "21714"
6: "21571"
7: "21324"
8: "21310"
9: "21390"
10: "21764"
11: "21598"
12: "21493"
13: "21352"
14: "21478"
15: "21058"
16: "20942"
17: "20825"
18: "21321"
19: "20950"
20: "21039"
21: "21117"
22: "20733"
23: "20773"
24: "20929"
25: "20900"
26: "20687"
27: "20999"
Currently I am projecting the hour's value like this:
(60/minsSoFar)*totalSoFar
This works reasonably well, but I'd rather do it a bit more mathematically. I'd like to calculate the line of best fit for the data I have so far and project that up to the 60th minute. This would take into account acceleration and deceleration.
With the method I'm currently using, I'm effectively assuming the trend is a straight line. How would I calculate the formula for a polynomial or power trend?
I'm writing this in NodeJS so JavaScript would be ideal, but I'll take pseudocode too!
Here's the array in an easier format in case you want it:
[21410, 21886, 21837, 21895, 21564, 21714, 21571, 21324, 21310, 21390, 21764, 21598, 21493, 21352, 21478, 21058, 20942, 20825, 21321, 20950, 21039, 21117, 20733, 20773, 20929, 20900, 20687, 20999]
Thanks for any help!
I'm writing some analytics modules for the site I'm working on and I need to estimate the total views after the current hour. I have data for every minute up to the current minute, so if the time is 12:28, I will have an array that looks something like this:
0: "21410"
1: "21886"
2: "21837"
3: "21895"
4: "21564"
5: "21714"
6: "21571"
7: "21324"
8: "21310"
9: "21390"
10: "21764"
11: "21598"
12: "21493"
13: "21352"
14: "21478"
15: "21058"
16: "20942"
17: "20825"
18: "21321"
19: "20950"
20: "21039"
21: "21117"
22: "20733"
23: "20773"
24: "20929"
25: "20900"
26: "20687"
27: "20999"
Currently I am projecting the hour's value like this:
(60/minsSoFar)*totalSoFar
This works reasonably well, but I'd rather do it a bit more mathematically. I'd like to calculate the line of best fit for the data I have so far and project that up to the 60th minute. This would take into account acceleration and deceleration.
With the method I'm currently using, I'm effectively assuming the trend is a straight line. How would I calculate the formula for a polynomial or power trend?
I'm writing this in NodeJS so JavaScript would be ideal, but I'll take pseudocode too!
Here's the array in an easier format in case you want it:
[21410, 21886, 21837, 21895, 21564, 21714, 21571, 21324, 21310, 21390, 21764, 21598, 21493, 21352, 21478, 21058, 20942, 20825, 21321, 20950, 21039, 21117, 20733, 20773, 20929, 20900, 20687, 20999]
Thanks for any help!
Share Improve this question edited Aug 3, 2012 at 13:39 Nick Brunt asked Aug 3, 2012 at 13:34 Nick BruntNick Brunt 10.1k11 gold badges57 silver badges84 bronze badges 1- You are probably going to want to start here en.wikipedia/wiki/Curve_fitting – bigbenbt Commented Aug 3, 2012 at 14:08
1 Answer
Reset to default 13You can do a least-squares fit of a line.
function LineFitter()
{
this.count = 0;
this.sumX = 0;
this.sumX2 = 0;
this.sumXY = 0;
this.sumY = 0;
}
LineFitter.prototype = {
'add': function(x, y)
{
this.count++;
this.sumX += x;
this.sumX2 += x*x;
this.sumXY += x*y;
this.sumY += y;
},
'project': function(x)
{
var det = this.count * this.sumX2 - this.sumX * this.sumX;
var offset = (this.sumX2 * this.sumY - this.sumX * this.sumXY) / det;
var scale = (this.count * this.sumXY - this.sumX * this.sumY) / det;
return offset + x * scale;
}
};
function linearProject(data, x)
{
var fitter = new LineFitter();
for (var i = 0; i < data.length; i++)
{
fitter.add(i, data[i]);
}
return fitter.project(x);
}
Example:
>>> linearProject([
21410, 21886, 21837, 21895, 21564, 21714, 21571, 21324, 21310, 21390,
21764, 21598, 21493, 21352, 21478, 21058, 20942, 20825, 21321, 20950,
21039, 21117, 20733, 20773, 20929, 20900, 20687, 20999
], 60);
19489.614121510676
Doing something similar for a square polynomial is a little more plicated:
function SquareFitter()
{
this.count = 0;
this.sumX = 0;
this.sumX2 = 0;
this.sumX3 = 0;
this.sumX4 = 0;
this.sumY = 0;
this.sumXY = 0;
this.sumX2Y = 0;
}
SquareFitter.prototype = {
'add': function(x, y)
{
this.count++;
this.sumX += x;
this.sumX2 += x*x;
this.sumX3 += x*x*x;
this.sumX4 += x*x*x*x;
this.sumY += y;
this.sumXY += x*y;
this.sumX2Y += x*x*y;
},
'project': function(x)
{
var det = this.count*this.sumX2*this.sumX4 - this.count*this.sumX3*this.sumX3 - this.sumX*this.sumX*this.sumX4 + 2*this.sumX*this.sumX2*this.sumX3 - this.sumX2*this.sumX2*this.sumX2;
var offset = this.sumX*this.sumX2Y*this.sumX3 - this.sumX*this.sumX4*this.sumXY - this.sumX2*this.sumX2*this.sumX2Y + this.sumX2*this.sumX3*this.sumXY + this.sumX2*this.sumX4*this.sumY - this.sumX3*this.sumX3*this.sumY;
var scale = -this.count*this.sumX2Y*this.sumX3 + this.count*this.sumX4*this.sumXY + this.sumX*this.sumX2*this.sumX2Y - this.sumX*this.sumX4*this.sumY - this.sumX2*this.sumX2*this.sumXY + this.sumX2*this.sumX3*this.sumY;
var accel = this.sumY*this.sumX*this.sumX3 - this.sumY*this.sumX2*this.sumX2 - this.sumXY*this.count*this.sumX3 + this.sumXY*this.sumX2*this.sumX - this.sumX2Y*this.sumX*this.sumX + this.sumX2Y*this.count*this.sumX2;
return (offset + x*scale + x*x*accel)/det;
}
};
function squareProject(data)
{
var fitter = new SquareFitter();
for (var i = 0; i < data.length; i++)
{
fitter.add(i, data[i]);
}
return fitter.project(60);
}
Example2:
>>> squareProject([
21410, 21886, 21837, 21895, 21564, 21714, 21571, 21324, 21310, 21390,
21764, 21598, 21493, 21352, 21478, 21058, 20942, 20825, 21321, 20950,
21039, 21117, 20733, 20773, 20929, 20900, 20687, 20999
], 60);
19282.85862700518
I could do this for higher degree polynomials, but the expressions would get even longer. For arbitrary degree, you would have to look at matrices.