Как рассчитать линию тренда для графика?

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) неверно

(С) должно быть:

смещение = у-(наклон * х)

Смотрите: http://zedgraph.org/wiki/index.php?title=Trend

Если у вас есть доступ к 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)

}
Другие вопросы по тегам