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