I need to replicate this Excel graph in code
Given a list of [x, y] values, how can I obtain a new list of values to graph the power trendline?
I've found people referring to this http://mathworld.wolfram.com/LeastSquaresFittingPowerLaw.html formula. But don't know how to generate a new list of values from this.
Follow the formula from the link:
function getFittedPoints(data) {
var log = Math.log,
pow = Math.pow,
sums = [
0, // sum of the logarithms of x ( sum(log(x)) )
0, // sum of the logarithms of y ( sum(log(y)) )
0, // sum of the logarithms of the products of x and y ( sum(log(x) * log(y)) )
0 // sum of the powers of the logarithms of x ( sum((log(x))^2 )
],
fittedPoints = [], // return fitted points
a, // a coefficient
b, // b coefficient
dataLen = data.length,
i,
logX,
logY;
for (i = 0; i < dataLen; i++) {
sums[0] += logX = log(data[i][0]);
sums[1] += logY = log(data[i][1]);
sums[2] += logX * logY;
sums[3] += pow(logX, 2);
}
b = (i * sums[2] - sums[0] * sums[1]) / (i * sums[3] - pow(sums[0], 2));
a = pow(Math.E, (sums[1] - b * sums[0]) / i);
for (i = 0; i < dataLen; i++) {
fittedPoints.push([
data[i][0],
a * pow(data[i][0], b)
]);
}
return fittedPoints;
}
And then apply the function to the data.
example: http://jsfiddle.net/fa3m4Lvf/
Of course if your data are not clean then you can improve the function with handling null values,etc.
And for those like me who are looking for the C# version of morganfree's answer above, here it is translated:
public static IEnumerable<double> GetPowerTrendline(IList<double> knownY, IList<double> knownX, IList<double> newX)
{
var sums = new double[4];
var trendlinePoints = new List<double>();
var dataLen = knownX.Count;
for (var i = 0; i < dataLen; i++)
{
var logX = Math.Log(knownX[i]);
var logY = Math.Log(knownY[i]);
sums[0] += logX;
sums[1] += logY;
sums[2] += logX * logY;
sums[3] += Math.Pow(logX, 2);
}
var b = (dataLen * sums[2] - sums[0] * sums[1]) / (dataLen * sums[3] - Math.Pow(sums[0], 2));
var a = Math.Pow(Math.E, (sums[1] - b * sums[0]) / dataLen);
foreach (var x in newX)
{
var pointY = a * Math.Pow(x, b);
trendlinePoints.Add(pointY);
}
return trendlinePoints;
}
Note that it is modified so that it takes a list of desired x points instead of using the provided ones.
I followed the example calculation based on this: http://www.statisticshowto.com/how-to-find-a-linear-regression-equation/
Modified Adams example based on this and came upp with this solution for C#. This is assumes you have all the existing scatter plots. The result is a number of arraylists with the all the x and y values for the trendline that you can directly insert into highcharts.
public static List<ArrayList> GetPowerTrendline(List<KeyValuePair<int,double>> xyValues)
{
var trendlinePoints = new List<ArrayList>();
var dataLen = xyValues.Count;
var xSum = xyValues.Sum(h => h.Key);
var ySum = xyValues.Sum(h => h.Value);
var XYSum = xyValues.Sum(h => h.Key * h.Value);
var xp2Sum = xyValues.Sum(x => Math.Pow(x.Key, 2));
var a = (ySum * xp2Sum - xSum * XYSum) / (dataLen * xp2Sum - Math.Pow(xSum, 2));
var b = ((dataLen * XYSum) - (xSum * ySum)) / (dataLen * xp2Sum - Math.Pow(xSum,2));
foreach (var x in xyValues.OrderBy(h => h.Key))
{
var pointY = a + b * x.Key;
var rounded = Math.Round(pointY, 2);
trendlinePoints.Add(new ArrayList { x.Key, rounded });
}
return trendlinePoints;
}
And in my HighCharts method like this:
series: [
{
type: 'line',
name: 'Trendlinje',
data: data.RegressionLine,
color: '#444444',
marker: {
enabled: false
},
states: {
hover: {
lineWidth: 0
}
},
enableMouseTracking: false
},
Related
I would like to minimize the following function
with constraints
in C#. I tried to do it with Math.Net's Newton Method, but I can't figure out how to do it. How can I minimize the function programmatically in C# for given $F_1, F_2$?
Update:
After the comment of #MinosIllyrien I tried the following, but I don't get the syntax:
_f1 = 0.3; // Global fields.
_f2 = 0.7;
var minimizer = new NewtonMinimizer(1E-4, 100, false);
var objectiveFunction = ObjectiveFunction.ScalarDerivative(FunctionToMinimize, GradientOfFunctionToMinimize);
var firstGuess = CreateVector.DenseOfArray(new[] {0.5});
var minimalWeight1 = minimizer.FindMinimum(objectiveFunction, firstGuess).MinimizingPoint;
private double GradientOfFunctionToMinimize(double w1){
return _f1 - (w1 * _f2) / Math.Sqrt(1 - Math.Pow(w1, 2));
}
private double FunctionToMinimize(double w1){
return w1 * _f1 + Math.Sqrt(1 - Math.Pow(w1, 2)) * _f2;
}
This does not work, because FindMinimum method requires IObjectiveFunction as function and not IScalarObjectiveFunction...
Update 2:
I tried a solution from Google:
var solver = Solver.CreateSolver("GLOP");
Variable w1 = solver.MakeNumVar(0.0, double.PositiveInfinity, "w1");
Variable w2 = solver.MakeNumVar(0.0, double.PositiveInfinity, "w2");
solver.Add(Math.Sqrt(w1*w1 + w2*w2) == 1);
This throws the error that *-operator cannot be used for "Variable" and "Variable". Someone any ideas?
w₁² + w₂² = 1 is basically the unit circle.
The unit circle can also be described by the following parametric equation:
(cos t, sin t)
In other words, for every pair (w₁, w₂),
there is an angle t for which w₁ = cos t and w₂ = sin t.
With that substitution, the function becomes:
y = F₁ cos t + F₂ sin t
w₁ ≥ 0, w₂ ≥ 0 restricts t to a single quadrant. This leaves you with a very simple constraint, that consists of a single variable:
0 ≤ t ≤ ½π
By the way, the function can be simplified to:
y = R cos(t - α)
where R = √(F₁² + F₂²) and α = atan2(F₂, F₁)
This is a simple sine wave. Without the constraint on t, its range would be [-R, R], making the minimum -R. But the constraint limits the domain and thereby the range:
If F₁ < 0 and F₂ < 0, then the minimum is at w₁ = -F₁ / R, w₂ = -F₂ / R, with y = -R
For 0 < F₁ ≤ F₂, a minimum is at w₁ = 1, w₂ = 0, with y = F₁
For 0 < F₂ ≤ F₁, a minimum is at w₁ = 0, w₂ = 1, with y = F₂
Notes:
if F₁ = F₂ > 0, then you have two minima.
if F₁ = F₂ = 0, then y is just flat zero everywhere.
In code:
_f1 = 0.3;
_f2 = 0.7;
if (_f1 == 0.0 && _f2 == 0.0) {
Console.WriteLine("Constant y = 0 across the entire domain");
}
else if (_f1 < 0.0 && _f2 < 0.0) {
var R = Math.sqrt(_f1 * _f1 + _f2 * _f2);
Console.WriteLine($"Minimum y = {-R} at w1 = {-_f1 / R}, w2 = {-_f2 / R}");
}
else {
if (_f1 <= _f2) {
Console.WriteLine($"Minimum y = {_f1} at w1 = 1, w2 = 0");
}
if (_f1 >= _f2) {
Console.WriteLine($"Minimum y = {_f2} at w1 = 0, w2 = 1");
}
}
Im working on a heatmap where i have to display zones on a map filled by a color calculated by a range of values.
I got the zone color part figgured out, but i also have to display 5 steps, each step providing a color and a value, explaining what the color means.
So, lets say i have a minimum value of 2654 and a maximum value of 6947.
This provides me with a range of 5 numbers, which are:
2654
3727,25
4800,5
5873,75
6947
Now what i want is to display these values as nice intervals, like this:
0 - 3000
3000 - 4000
4000 - 5000
5000 - 6000
> 6000
These values are ofcourse dynamic.
So it could be either huge numbers or small numbers.
Is there any way to do this?
Update
#roberto-carlos i tried your example below:
var min = 900;
var max = 12541;
var steps = 5;
var range = Enumerable.Range(0, steps).Select(i => min + (max - min) * ((double)i / (steps - 1)));
foreach (var d in range)
{
var power = Math.Floor(Math.Log10(d));
var inf = d - d % Math.Pow(10, power);
var sup = d + (Math.Pow(10, power) - d % Math.Pow(10, power));
if (d == min)
{
Console.WriteLine("0-" + sup);
}
else if (d == max)
{
Console.WriteLine(">" + sup);
}
else
{
Console.WriteLine(inf + "-" + sup);
}
}
However, this provides me with a result like this:
0-1000
3000-4000
6000-7000
9000-10000
>20000
Is there any way to make the intervals "stick together" numberwise?
You can try this:
var list = new List<double> { 2654, 3727.25, 4800.5, 5873.75, 6947 };
foreach (var d in list)
{
var power = Math.Floor(Math.Log10(d));
var min = d - d % Math.Pow(10, power);
var max = d + (Math.Pow(10, power) - d % Math.Pow(10, power));
Console.WriteLine(min + "-" + max);
}
/*
Result:
2000 - 3000
3000 - 4000
4000 - 5000
5000 - 6000
6000 - 7000
/*
Version 2:
var list = new List<double> { 2654, 3727.25, 4800.5, 5873.75, 6947 };
var min = list.Min();
var max = list.Max();
foreach (var d in list)
{
var power = Math.Floor(Math.Log10(d));
var inf = d - d % Math.Pow(10, power);
var sup = d + (Math.Pow(10, power) - d % Math.Pow(10, power));
if (d == min)
{
Console.WriteLine("0-" + sup);
}
else if (d == max)
{
Console.WriteLine(">" + sup);
}
else
{
Console.WriteLine(inf + "-" + sup);
}
}
/*
Result:
0-3000
3000-4000
4000-5000
5000-6000
>7000
/*
Using linq putting into predetermined groups
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace ConsoleApplication42
{
class Program
{
static void Main(string[] args)
{
int[] intervals = {3000, 4000, 5000,6000};
List<List<int?>> data = new List<List<int?>>() {
new List<int?>() {2654},
new List<int?>() {3727,25},
new List<int?>() {3730,25},
new List<int?>() {4800,5},
new List<int?>() {5873,75},
new List<int?>() {6947}
};
var results = data.Select(x => new { index = intervals.Select((y, i) => new { index = i, max = y }).Where(z => x[0] < z.max).FirstOrDefault(), data = x }).ToList();
var groups = results.GroupBy(x => x.index != null ? x.index.index : intervals.Length).Select(x => new { key = x.Key, data = x.Select(y => y.data).ToList() }).ToList();
}
}
}
Ok, I initially misunderstood your problem.
The following implementation should do:
static IEnumerable<Interval> GetIntervals(IEnumerable<double> values, int intervalsCount)
{
if (values == null)
throw new ArgumentNullException(nameof(values));
if (intervalsCount <= 1)
yield break;
var orderedValues = values.OrderBy(v => v);
var frontiers = orderedValues.Select(
v =>
{
var scale = Math.Pow(10, (int)Math.Log10(v));
return Math.Floor(v / scale) * scale;
}).ToArray();
for (var i = 0; i < frontiers.Length - 1; i++)
{
yield return new Interval(frontiers[i], frontiers[i + 1]);
}
yield return new Interval(frontiers[frontiers.Length - 1], double.PositiveInfinity);
}
And Interval is a simple helper struct:
struct Interval
{
public Interval(double lower, double upper)
{
if (upper <= lower)
throw new ArgumentException("The upper bound must be greater than the specified lower bound", nameof(upper));
LowerBound = lower;
UpperBound = upper;
}
public double LowerBound { get; }
public double UpperBound { get; }
public override string ToString()
{
if (UpperBound == double.PositiveInfinity)
return $" > {LowerBound}";
return $"{LowerBound} - {UpperBound}";
}
}
This outputs for 2654.0, 3727.25, 4800.5, 5873.75, 6947:
2000 - 3000
3000 - 4000
4000 - 5000
5000 - 6000
> 6000
And for example, in case of a logarithmic scaled input 2.0, 56, 321, 5674, 12764 the output is:
2 - 50
50 - 300
300 - 5000
5000 - 10000
> 10000
Note that this implementation will not create uniform intervals, it will create intervals, such that each one includes one concrete input value. I'm not really sure if this is what you want, your requirements aren't all that clear.
I use the following code to attempt to do a few calculations using values from two separate arrays. I've been trying to aggregate a list of values as a final step but every time I run the program it only has one set of values in the list. abcXLoopVars is a custom class to store the different variables which I aggregate later.
Parallel.For<abcXLoopVars>(0, colX.Count(),
() => { return new abcXLoopVars(); },
(i, pls, state) =>
{
state = new abcXLoopVars();
double x = Math.Abs(colX[i]);
double y = Math.Abs(colY[i]);
double lnx = Math.Log(x);
double lny = Math.Log(y);
double xminxbarsq = Math.Pow(colX[i] - xbar, 2);
double xminxbarcub = Math.Pow(colX[i] - xbar, 4);
state.sumxminxbarsq = xminxbarsq;
state.sumxminxbarcub = xminxbarcub;
state.sumlnxxminxbarsq = lnx * xminxbarsq;
state.sumlnxlny = lnx * lny;
state.sumlnxsq = Math.Pow(lnx, 2);
state.sumlnx = sumlnx + lnx;
state.sumlnyxminxbarsq = lny * xminxbarsq;
state.sumlny = lny;
state.posneg = colY[i] / colX[i];
return state;
},
(state) => { lock (lockMe) abxList.Add(state);}
);
I suggest you to rewrite it as PLINQ:
colX.Zip(colY, (x, y) =>
{
var state = new abcXLoopVars();
double lnx = Math.Log(x);
double lny = Math.Log(y);
double xminxbarsq = Math.Pow(x - xbar, 2);
double xminxbarcub = Math.Pow(x - xbar, 4);
state.sumxminxbarsq = xminxbarsq;
state.sumxminxbarcub = xminxbarcub;
state.sumlnxxminxbarsq = lnx * xminxbarsq;
state.sumlnxlny = lnx * lny;
state.sumlnxsq = Math.Pow(lnx, 2);
state.sumlnx = sumlnx + lnx;
state.sumlnyxminxbarsq = lny * xminxbarsq;
state.sumlny = lny;
state.posneg = y / x;
return state;
}).AsParallel().ToList();
This is the formula that I want to add as a constraint to the Microsoft solver foundation.
I am using the SumTermBuilder class in this code but solver gives me the wrong result and there is no compilation error for this code. Why am I getting the wrong result?
Code:
var N_Available_Total = new SumTermBuilder(decisions.Count());
var OM_Available_Total = new SumTermBuilder(decisions.Count());
var Hourly = new SumTermBuilder(96);
for (int i = 1; i < 97; i++)
{
foreach (var item in newfeed)
{
float x = item.N_ED_Available_at_time_Total[i]/100;
float y = item.OM_ED_Available_at_time_Total[i]/100;
//get decision variables list
var feeddecision = model.Decisions.First(it => it.Name == item.feed);
//summation of all decision variables
N_Available_Total.Add(feeddecision * x); // For N
OM_Available_Total.Add(feeddecision * y); // for OM
}
Hourly.Add((Model.Sqrt(Model.Power((25 - ((N_Available_Total.ToTerm() / OM_Available_Total.ToTerm()))), 2)))/96);
}
var SIConstraint = (25 - ((Hourly.ToTerm())) / 25) >= 0.9;
model.AddConstraint("SI", SIConstraint);
Google is not being my friend - it's been a long time since my stats class in college...I need to calculate the start and end points for a trendline on a graph - is there an easy way to do this? (working in C# but whatever language works for you)
Thanks to all for your help - I was off this issue for a couple of days and just came back to it - was able to cobble this together - not the most elegant code, but it works for my purposes - thought I'd share if anyone else encounters this issue:
public class Statistics
{
public Trendline CalculateLinearRegression(int[] values)
{
var yAxisValues = new List<int>();
var xAxisValues = new List<int>();
for (int i = 0; i < values.Length; i++)
{
yAxisValues.Add(values[i]);
xAxisValues.Add(i + 1);
}
return new Trendline(yAxisValues, xAxisValues);
}
}
public class Trendline
{
private readonly IList<int> xAxisValues;
private readonly IList<int> yAxisValues;
private int count;
private int xAxisValuesSum;
private int xxSum;
private int xySum;
private int yAxisValuesSum;
public Trendline(IList<int> yAxisValues, IList<int> xAxisValues)
{
this.yAxisValues = yAxisValues;
this.xAxisValues = xAxisValues;
this.Initialize();
}
public int Slope { get; private set; }
public int Intercept { get; private set; }
public int Start { get; private set; }
public int End { get; private set; }
private void Initialize()
{
this.count = this.yAxisValues.Count;
this.yAxisValuesSum = this.yAxisValues.Sum();
this.xAxisValuesSum = this.xAxisValues.Sum();
this.xxSum = 0;
this.xySum = 0;
for (int i = 0; i < this.count; i++)
{
this.xySum += (this.xAxisValues[i]*this.yAxisValues[i]);
this.xxSum += (this.xAxisValues[i]*this.xAxisValues[i]);
}
this.Slope = this.CalculateSlope();
this.Intercept = this.CalculateIntercept();
this.Start = this.CalculateStart();
this.End = this.CalculateEnd();
}
private int CalculateSlope()
{
try
{
return ((this.count*this.xySum) - (this.xAxisValuesSum*this.yAxisValuesSum))/((this.count*this.xxSum) - (this.xAxisValuesSum*this.xAxisValuesSum));
}
catch (DivideByZeroException)
{
return 0;
}
}
private int CalculateIntercept()
{
return (this.yAxisValuesSum - (this.Slope*this.xAxisValuesSum))/this.count;
}
private int CalculateStart()
{
return (this.Slope*this.xAxisValues.First()) + this.Intercept;
}
private int CalculateEnd()
{
return (this.Slope*this.xAxisValues.Last()) + this.Intercept;
}
}
OK, here's my best pseudo math:
The equation for your line is:
Y = a + bX
Where:
b = (sum(x*y) - sum(x)sum(y)/n) / (sum(x^2) - sum(x)^2/n)
a = sum(y)/n - b(sum(x)/n)
Where sum(xy) is the sum of all x*y etc. Not particularly clear I concede, but it's the best I can do without a sigma symbol :)
... and now with added Sigma
b = (Σ(xy) - (ΣxΣy)/n) / (Σ(x^2) - (Σx)^2/n)
a = (Σy)/n - b((Σx)/n)
Where Σ(xy) is the sum of all x*y etc. and n is the number of points
Given that the trendline is straight, find the slope by choosing any two points and calculating:
(A) slope = (y1-y2)/(x1-x2)
Then you need to find the offset for the line. The line is specified by the equation:
(B) y = offset + slope*x
So you need to solve for offset. Pick any point on the line, and solve for offset:
(C) offset = y - (slope*x)
Now you can plug slope and offset into the line equation (B) and have the equation that defines your line. If your line has noise you'll have to decide on an averaging algorithm, or use curve fitting of some sort.
If your line isn't straight then you'll need to look into Curve fitting, or Least Squares Fitting - non trivial, but do-able. You'll see the various types of curve fitting at the bottom of the least squares fitting webpage (exponential, polynomial, etc) if you know what kind of fit you'd like.
Also, if this is a one-off, use Excel.
Here is a very quick (and semi-dirty) implementation of Bedwyr Humphreys's answer. The interface should be compatible with #matt's answer as well, but uses decimal instead of int and uses more IEnumerable concepts to hopefully make it easier to use and read.
Slope is b, Intercept is a
public class Trendline
{
public Trendline(IList<decimal> yAxisValues, IList<decimal> xAxisValues)
: this(yAxisValues.Select((t, i) => new Tuple<decimal, decimal>(xAxisValues[i], t)))
{ }
public Trendline(IEnumerable<Tuple<Decimal, Decimal>> data)
{
var cachedData = data.ToList();
var n = cachedData.Count;
var sumX = cachedData.Sum(x => x.Item1);
var sumX2 = cachedData.Sum(x => x.Item1 * x.Item1);
var sumY = cachedData.Sum(x => x.Item2);
var sumXY = cachedData.Sum(x => x.Item1 * x.Item2);
//b = (sum(x*y) - sum(x)sum(y)/n)
// / (sum(x^2) - sum(x)^2/n)
Slope = (sumXY - ((sumX * sumY) / n))
/ (sumX2 - (sumX * sumX / n));
//a = sum(y)/n - b(sum(x)/n)
Intercept = (sumY / n) - (Slope * (sumX / n));
Start = GetYValue(cachedData.Min(a => a.Item1));
End = GetYValue(cachedData.Max(a => a.Item1));
}
public decimal Slope { get; private set; }
public decimal Intercept { get; private set; }
public decimal Start { get; private set; }
public decimal End { get; private set; }
public decimal GetYValue(decimal xValue)
{
return Intercept + Slope * xValue;
}
}
Regarding a previous answer
if (B) y = offset + slope*x
then (C) offset = y/(slope*x) is wrong
(C) should be:
offset = y-(slope*x)
See:
http://zedgraph.org/wiki/index.php?title=Trend
If you have access to Excel, look in the "Statistical Functions" section of the Function Reference within Help. For straight-line best-fit, you need SLOPE and INTERCEPT and the equations are right there.
Oh, hang on, they're also defined online here: http://office.microsoft.com/en-us/excel/HP052092641033.aspx for SLOPE, and there's a link to INTERCEPT. OF course, that assumes MS don't move the page, in which case try Googling for something like "SLOPE INTERCEPT EQUATION Excel site:microsoft.com" - the link given turned out third just now.
I converted Matt's code to Java so I could use it in Android with the MPAndroidChart library. Also used double values instead of integer values:
ArrayList<Entry> yValues2 = new ArrayList<>();
ArrayList<Double > xAxisValues = new ArrayList<Double>();
ArrayList<Double> yAxisValues = new ArrayList<Double>();
for (int i = 0; i < readings.size(); i++)
{
r = readings.get(i);
yAxisValues.add(r.value);
xAxisValues.add((double)i + 1);
}
TrendLine tl = new TrendLine(yAxisValues, xAxisValues);
//Create the y values for the trend line
double currY = tl.Start;
for (int i = 0; i < readings.size(); ++ i) {
yValues2.add(new Entry(i, (float) currY));
currY = currY + tl.Slope;
}
...
public class TrendLine
{
private ArrayList<Double> xAxisValues = new ArrayList<Double>();
private ArrayList<Double> yAxisValues = new ArrayList<Double>();
private int count;
private double xAxisValuesSum;
private double xxSum;
private double xySum;
private double yAxisValuesSum;
public TrendLine(ArrayList<Double> yAxisValues, ArrayList<Double> xAxisValues)
{
this.yAxisValues = yAxisValues;
this.xAxisValues = xAxisValues;
this.Initialize();
}
public double Slope;
public double Intercept;
public double Start;
public double End;
private double getArraySum(ArrayList<Double> arr) {
double sum = 0;
for (int i = 0; i < arr.size(); ++i) {
sum = sum + arr.get(i);
}
return sum;
}
private void Initialize()
{
this.count = this.yAxisValues.size();
this.yAxisValuesSum = getArraySum(this.yAxisValues);
this.xAxisValuesSum = getArraySum(this.xAxisValues);
this.xxSum = 0;
this.xySum = 0;
for (int i = 0; i < this.count; i++)
{
this.xySum += (this.xAxisValues.get(i)*this.yAxisValues.get(i));
this.xxSum += (this.xAxisValues.get(i)*this.xAxisValues.get(i));
}
this.Slope = this.CalculateSlope();
this.Intercept = this.CalculateIntercept();
this.Start = this.CalculateStart();
this.End = this.CalculateEnd();
}
private double CalculateSlope()
{
try
{
return ((this.count*this.xySum) - (this.xAxisValuesSum*this.yAxisValuesSum))/((this.count*this.xxSum) - (this.xAxisValuesSum*this.xAxisValuesSum));
}
catch (Exception e)
{
return 0;
}
}
private double CalculateIntercept()
{
return (this.yAxisValuesSum - (this.Slope*this.xAxisValuesSum))/this.count;
}
private double CalculateStart()
{
return (this.Slope*this.xAxisValues.get(0)) + this.Intercept;
}
private double CalculateEnd()
{
return (this.Slope*this.xAxisValues.get(this.xAxisValues.size()-1)) + this.Intercept;
}
}
This is the way i calculated the slope:
Source: http://classroom.synonym.com/calculate-trendline-2709.html
class Program
{
public double CalculateTrendlineSlope(List<Point> graph)
{
int n = graph.Count;
double a = 0;
double b = 0;
double bx = 0;
double by = 0;
double c = 0;
double d = 0;
double slope = 0;
foreach (Point point in graph)
{
a += point.x * point.y;
bx = point.x;
by = point.y;
c += Math.Pow(point.x, 2);
d += point.x;
}
a *= n;
b = bx * by;
c *= n;
d = Math.Pow(d, 2);
slope = (a - b) / (c - d);
return slope;
}
}
class Point
{
public double x;
public double y;
}
Here's what I ended up using.
public class DataPoint<T1,T2>
{
public DataPoint(T1 x, T2 y)
{
X = x;
Y = y;
}
[JsonProperty("x")]
public T1 X { get; }
[JsonProperty("y")]
public T2 Y { get; }
}
public class Trendline
{
public Trendline(IEnumerable<DataPoint<long, decimal>> dataPoints)
{
int count = 0;
long sumX = 0;
long sumX2 = 0;
decimal sumY = 0;
decimal sumXY = 0;
foreach (var dataPoint in dataPoints)
{
count++;
sumX += dataPoint.X;
sumX2 += dataPoint.X * dataPoint.X;
sumY += dataPoint.Y;
sumXY += dataPoint.X * dataPoint.Y;
}
Slope = (sumXY - ((sumX * sumY) / count)) / (sumX2 - ((sumX * sumX) / count));
Intercept = (sumY / count) - (Slope * (sumX / count));
}
public decimal Slope { get; private set; }
public decimal Intercept { get; private set; }
public decimal Start { get; private set; }
public decimal End { get; private set; }
public decimal GetYValue(decimal xValue)
{
return Slope * xValue + Intercept;
}
}
My data set is using a Unix timestamp for the x-axis and a decimal for the y. Change those datatypes to fit your need. I do all the sum calculations in one iteration for the best possible performance.
Thank You so much for the solution, I was scratching my head.
Here's how I applied the solution in Excel.
I successfully used the two functions given by MUHD in Excel:
a = (sum(x*y) - sum(x)sum(y)/n) / (sum(x^2) - sum(x)^2/n)
b = sum(y)/n - b(sum(x)/n)
(careful my a and b are the b and a in MUHD's solution).
- Made 4 columns, for example:
NB: my values y values are in B3:B17, so I have n=15;
my x values are 1,2,3,4...15.
1. Column B: Known x's
2. Column C: Known y's
3. Column D: The computed trend line
4. Column E: B values * C values (E3=B3*C3, E4=B4*C4, ..., E17=B17*C17)
5. Column F: x squared values
I then sum the columns B,C and E, the sums go in line 18 for me, so I have B18 as sum of Xs, C18 as sum of Ys, E18 as sum of X*Y, and F18 as sum of squares.
To compute a, enter the followin formula in any cell (F35 for me):
F35=(E18-(B18*C18)/15)/(F18-(B18*B18)/15)
To compute b (in F36 for me):
F36=C18/15-F35*(B18/15)
Column D values, computing the trend line according to the y = ax + b:
D3=$F$35*B3+$F$36, D4=$F$35*B4+$F$36 and so on (until D17 for me).
Select the column datas (C2:D17) to make the graph.
HTH.
If anyone needs the JS code for calculating the trendline of many points on a graph, here's what worked for us in the end:
/**#typedef {{
* x: Number;
* y:Number;
* }} Point
* #param {Point[]} data
* #returns {Function} */
function _getTrendlineEq(data) {
const xySum = data.reduce((acc, item) => {
const xy = item.x * item.y
acc += xy
return acc
}, 0)
const xSum = data.reduce((acc, item) => {
acc += item.x
return acc
}, 0)
const ySum = data.reduce((acc, item) => {
acc += item.y
return acc
}, 0)
const aTop = (data.length * xySum) - (xSum * ySum)
const xSquaredSum = data.reduce((acc, item) => {
const xSquared = item.x * item.x
acc += xSquared
return acc
}, 0)
const aBottom = (data.length * xSquaredSum) - (xSum * xSum)
const a = aTop / aBottom
const bTop = ySum - (a * xSum)
const b = bTop / data.length
return function trendline(x) {
return a * x + b
}
}
It takes an array of (x,y) points and returns the function of a y given a certain x
Have fun :)
Here's a working example in golang. I searched around and found this page and converted this over to what I needed. Hope someone else can find it useful.
// https://classroom.synonym.com/calculate-trendline-2709.html
package main
import (
"fmt"
"math"
)
func main() {
graph := [][]float64{
{1, 3},
{2, 5},
{3, 6.5},
}
n := len(graph)
// get the slope
var a float64
var b float64
var bx float64
var by float64
var c float64
var d float64
var slope float64
for _, point := range graph {
a += point[0] * point[1]
bx += point[0]
by += point[1]
c += math.Pow(point[0], 2)
d += point[0]
}
a *= float64(n) // 97.5
b = bx * by // 87
c *= float64(n) // 42
d = math.Pow(d, 2) // 36
slope = (a - b) / (c - d) // 1.75
// calculating the y-intercept (b) of the Trendline
var e float64
var f float64
e = by // 14.5
f = slope * bx // 10.5
intercept := (e - f) / float64(n) // (14.5 - 10.5) / 3 = 1.3
// output
fmt.Println(slope)
fmt.Println(intercept)
}