Как рассчитать линию тренда для графика? - PullRequest
48 голосов
/ 04 сентября 2008

Google не является моим другом - прошло уже много времени с моего класса статистики в колледже ... Мне нужно рассчитать начальную и конечную точки для линии тренда на графике - есть ли простой способ сделать это? (работает на C #, но на любом языке, который вам подходит)

Ответы [ 9 ]

33 голосов
/ 06 сентября 2008

Спасибо всем за вашу помощь - я не решал эту проблему в течение нескольких дней и только вернулся к ней - смог собрать ее вместе - не самый элегантный код, но он работает для моих целей - подумал я Если кто-нибудь еще столкнется с этой проблемой, поделитесь:

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;
    }
}
27 голосов
/ 04 сентября 2008

ОК, вот моя лучшая псевдоматематика:

Уравнение для вашей линии:

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 = (& Sigma; (xy) - (& Sigma; x & Sigma; y) / n) / (& Sigma; (x ^ 2) - (& Sigma; x) ^ 2 / n)

a = (& Sigma; y) / n - b ((& Sigma; x) / n)

Где & Sigma; (xy) - сумма всех x * y и т. Д., А n - количество баллов

15 голосов
/ 04 апреля 2013

Вот очень быстрая (и немного грязная) реализация ответа Бедвира Хамфриза . Интерфейс должен быть совместим также с ответом @ 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;
    }
}
15 голосов
/ 04 сентября 2008

Учитывая, что линия тренда прямая, найдите наклон, выбрав любые две точки и рассчитав:

(A) наклон = (y1-y2) / (x1-x2)

Тогда вам нужно найти смещение для линии. Линия задается уравнением:

(B) y = смещение + наклон * x

Так что вам нужно решить для смещения. Выберите любую точку на линии и решите для смещения:

(C) смещение = y - (наклон * x)

Теперь вы можете включить наклон и смещение в уравнение линии (B) и получить уравнение, которое определяет вашу линию. Если на вашей линии есть шум, вам придется выбрать алгоритм усреднения или использовать какой-либо пример кривой.

Если ваша линия не прямолинейна, вам нужно изучить Подгонка кривой , или Подгонка наименьших квадратов - нетривиально, но выполнимо. Вы увидите различные типы подгонки кривой в нижней части веб-страницы подгонки по методу наименьших квадратов (экспоненциальная, полиномиальная и т. Д.), Если знаете, какой тип подгонки вам нужен.

Также, если это единовременно, используйте Excel.

3 голосов
/ 03 апреля 2009

Относительно предыдущего ответа

если (B) y = смещение + наклон * x

тогда (C) смещение = y / (наклон * x) неверно

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

смещение = y- (наклон * x)

См: http://zedgraph.org/wiki/index.php?title=Trend

2 голосов
/ 04 сентября 2008

Если у вас есть доступ к Excel, обратитесь к разделу «Статистические функции» Справочника по функциям в Справке. Для наилучшего соответствия с прямой линией вам нужны НАКЛОН и ИНТЕРЦЕПТ, и уравнения прямо здесь.

Ой, подождите, они также определены здесь: http://office.microsoft.com/en-us/excel/HP052092641033.aspx для НАКЛОНА, и есть ссылка на ИНТЕРЦЕПТ. Конечно, это предполагает, что MS не перемещает страницу, и в этом случае попробуйте Googling для чего-то вроде «НАКЛОНИТЬ ИНТЕРЦЕПТ УРАВНЕНИЯ Excel-сайт: microsoft.com» - ссылка, указанная третьим только сейчас.

1 голос
/ 02 июля 2018

Вот что я в итоге использовал.

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. Измените эти типы данных в соответствии с вашими потребностями. Я делаю все вычисления суммы за одну итерацию для достижения наилучшей производительности.

1 голос
/ 07 января 2018

Так я рассчитал наклон: Источник: 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;
    }
0 голосов
/ 09 января 2013

Большое спасибо за решение, я почесал голову.
Вот как я применил решение в 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: значения x в квадрате
Затем я суммирую столбцы 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), чтобы построить график.
НТН.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...