Как мне рассчитать линию тренда для графика?
Вопрос
Google мне не друг - прошло много времени с тех пор, как я изучал статистику в колледже...Мне нужно рассчитать начальную и конечную точки линии тренда на графике - есть ли простой способ сделать это?(работаете на C #, но любой язык подходит именно вам)
Решение
Учитывая, что линия тренда прямая, найдите наклон, выбрав любые две точки и рассчитав:
(A) наклон = (y1-y2)/(x1-x2)
Затем вам нужно найти смещение для линии.Линия задается уравнением:
(B) y = смещение + наклон *x
Итак, вам нужно решить проблему смещения.Выберите любую точку на линии и вычислите смещение:
(C) смещение = y - (наклон*x)
Теперь вы можете включить наклон и смещение в уравнение линии (B) и получить уравнение, определяющее вашу линию.Если в вашей линии есть шум, вам придется выбрать алгоритм усреднения или использовать какую-либо подгонку кривой.
Если ваша линия не прямая, то вам нужно будет изучить Подгонка кривой, или Подгонка по методу наименьших квадратов - нетривиально, но выполнимо.Вы увидите различные типы подгонки кривой в нижней части веб-страницы "Подгонка по методу наименьших квадратов" (экспоненциальная, полиномиальная и т.д.), Если знаете, какую подгонку вы хотели бы получить.
Кроме того, если это одноразовый метод, используйте Excel.
Другие советы
Спасибо всем за вашу помощь - я отключился от этой проблемы на пару дней и только что вернулся к ней - смог собрать это воедино - не самый элегантный код, но он работает для моих целей - подумал, что поделюсь, если кто-нибудь еще столкнется с этой проблемой:
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;
}
}
Ладно, вот моя лучшая псевдоматематика:
Уравнение для вашей линии выглядит следующим образом:
Y = a + bX
Где:
b = (сумма(x*y) - сумма(x), сумма(y)/n) / (сумма(x^2) - сумма (x)^2/n)
a = сумма (y)/n - b(сумма(x)/n)
Где sum(xy) - это сумма всех x * y и т.д.Признаю, не совсем понятно, но это лучшее, что я могу сделать без символа сигмы :)
...и теперь с добавлением Сигмы
b = (Σ(xy) - (ΣxΣy)/n) / (Σ(x^2) - (Σx)^2/n)
a = (Σy)/n - b((Σx)/n)
Где Σ (xy) - сумма всех x * y и т.д.а n - количество точек
Вот очень быстрая (и полу-грязная) реализация Ответ Бедуира Хамфриса.Интерфейс должен быть совместим с @мэттэто тоже ответ, но использует decimal
вместо того, чтобы int
и использует больше IEnumerable концепций, чтобы, как мы надеемся, упростить его использование и чтение.
Slope
является b
, Intercept
является 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;
}
}
Что касается предыдущего ответа
если (B) y = смещение + наклон *x
тогда (C) смещение = y/(наклон*x) это неправильно
(C) должно быть:
смещение = y-(наклон*x)
Если у вас есть доступ к Excel, загляните в раздел "Статистические функции" справочника по функциям в справке.Для наилучшего соответствия прямой линии вам нужны НАКЛОН и ПЕРЕСЕЧЕНИЕ, и уравнения находятся прямо здесь.
О, подождите, они также определены онлайн здесь: http://office.microsoft.com/en-us/excel/HP052092641033.aspx для SLOPE, и там есть ссылка для ПЕРЕХВАТА.Конечно, это предполагает, что MS не перемещает страницу, и в этом случае попробуйте поискать в Google что-то вроде "УРАВНЕНИЕ ПЕРЕСЕЧЕНИЯ НАКЛОНА Excel site:microsoft.com" - приведенная ссылка только что стала третьей.
Вот как я рассчитал наклон:Источник: 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;
}
Вот что я в итоге использовал.
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;
}
}
Мой набор данных использует временную метку Unix для оси x и десятичную дробь для y.Измените эти типы данных в соответствии с вашими потребностями.Я выполняю все вычисления суммы за одну итерацию для достижения наилучшей возможной производительности.
Большое Вам спасибо за решение, я ломал голову.
Вот как я применил это решение в Excel.
Я успешно использовал две функции, предоставленные MUHD в Excel:
a = (сумма (x*y) - сумма (x), сумма(y)/n) / (сумма(x^2) - сумма (x)^2/n)
b = сумма (y)/n - b(сумма(x)/n)
(осторожно, мои a и b - это b и a в решении MUHD).
- Сделал 4 колонки, например:
Примечание:мои значения y находятся в B3: B17, так что у меня n = 15;
мои значения x равны 1,2,3,4 ... 15.
1.Колонка В:Известные x's
2.Колонка С:Известные y's
3.Столбец D:Вычисленная линия тренда
4.Колонка E:Значения B * Значения C (E3=B3*C3, E4=B4*C4, ..., E17=B17*C17)
5.Столбец F:x квадратов значений
Затем я суммирую столбцы B, C и E, суммы для меня идут в строке 18, так что у меня есть B18 как сумма Xs, C18 как сумма Ys, E18 как сумма X * Y и F18 как сумма квадратов.
Чтобы вычислить a, введите следующую формулу в любую ячейку (для меня F35).:
F35=(E18-(B18*C18)/15)/(F18-(B18*B18)/15)
Чтобы вычислить b (в F36 для меня):
F36=C18/15-F35*(B18/15)
Значения столбца D, вычисляющие линию тренда в соответствии с y = ax + b:
D3 = $ F $ 35 * B3 + $ F $ 36, D4 = $ F $ 35 * B4 + $ F $ 36 и так далее (до D17 для меня).
Выберите данные столбца (C2: D17), чтобы построить график.
ХТХ.