如何计算图表的趋势线?

How do I calculate a trendline for a graph?

提问人:matt 提问时间:9/4/2008 最后编辑:Steven Vmatt 更新时间:5/31/2023 访问量:136904

问:

谷歌不是我的朋友——我已经很久没有上大学统计课了......我需要计算图表上趋势线的起点和终点 - 有没有一种简单的方法可以做到这一点?(使用 C# 工作,但任何语言都适合您)

C# 数学

评论

0赞 duffymo 4/17/2021
最小二乘拟合是您最好的选择。

答:

31赞 blank 9/4/2008 #1

好的,这是我最好的伪数学:

您的线路的方程式为:

Y = a + bX

哪里:

b = (sum(x*y) - sum(x)sum(y)/n) / (sum(x^2) - sum(x)^2/n)

a = 总和(y)/n - b(总和(x)/n)

其中 sum(xy) 是所有 x*y 等的总和。我承认不是特别清楚,但这是我在没有西格玛符号的情况下能做的最好的事情:)

...现在增加了 Sigma

b = (Σ(xy) - (ΣxΣy)/n) / (Σ(x^2) - (Σx)^2/n)

a = (Σy)/n - b((Σx)/n)

其中 Σ(xy) 是所有 x*y 等的总和,n 是点数

评论

1赞 Praesagus 5/7/2011
如果我能弄清楚 n 代表什么,这将是一个很大的帮助。
0赞 Praesagus 5/7/2011
数学课又回来了 - n 是轴上的点数,对吧?
1赞 Sparafusile 5/19/2011
在您的 b 方程中,第二个 = 符号应该是什么?
0赞 Muhd 6/8/2011
@Sparafusile:最受欢迎的答案在那里使用减号,所以我相应地编辑了这个答案。
2赞 TecHunter 9/17/2012
这是真正的答案,尽管我更喜欢 b*n/n 来删除 n 分数
19赞 Adam Davis 9/4/2008 #2

假设趋势线是直的,通过选择任意两个点并计算来找到斜率:

(A) 斜率 = (y1-y2)/(x1-x2)

然后,您需要找到线的偏移量。该行由以下公式指定:

(B) y = 偏移量 + 斜率*x

所以你需要求解偏移量。选取线上的任意点,并求解偏移量:

(C) 偏移量 = y - (斜率*x)

现在,您可以将斜率和偏移量代入线方程 (B) 中,并拥有定义线的方程。如果您的线路有噪声,则必须决定平均算法,或使用某种曲线拟合。

如果你的线不是直的,那么你需要研究曲线拟合,或最小二乘拟合 - 不平凡,但可行。如果您知道自己想要哪种拟合,您将在最小二乘拟合网页的底部看到各种类型的曲线拟合(指数、多项式等)。

此外,如果这是一次性的,请使用 Excel。

评论

32赞 Jay Stevens 12/29/2009
我相当确定这不会产生拟合的趋势线;在(A)中计算的斜率将根据您选择的“两点”而有很大不同。这不是拟合的趋势线。
3赞 TecHunter 5/30/2013
我更喜欢我找到的这个
1赞 Adam Davis 2/24/2015
@Jay 因此,这句话,“如果你的线路有噪声,你必须决定平均算法或使用某种曲线拟合。
2赞 Mike Woodhouse 9/4/2008 #3

如果您有权访问 Excel,请查看“帮助”中“函数参考”的“统计函数”部分。对于直线最佳拟合,您需要 SLOPE 和 INTERCEPT,方程就在那里。

哦,等等,它们也在这里在线定义:http://office.microsoft.com/en-us/excel/HP052092641033.aspx SLOPE,还有一个指向 INTERCEPT 的链接。当然,这是假设MS不移动页面,在这种情况下,请尝试在谷歌上搜索“斜率截距方程Excel站点:微软.COM”之类的内容 - 刚才给出的链接是第三个。

37赞 matt 9/6/2008 #4

感谢大家的帮助 - 我离开了这个问题几天,然后又回到了它 - 能够把它拼凑在一起 - 不是最优雅的代码,但它适用于我的目的 - 如果其他人遇到这个问题,我想我会分享:

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;
    }
}

评论

2赞 TecHunter 9/17/2012
考虑 Bedwyr Humphreys 提出的解决方案
0赞 Thymine 4/4/2013
我相信这是假设数据被排序(由于使用 和xAxisVales.First().Last())
0赞 user1575120 2/7/2020
斜率值应为 Double,而不是整数
3赞 user86663 4/3/2009 #5

关于之前的答案

如果 (B) y = 偏移量 + 斜率*x

(C) 偏移量 = y/(斜率*x) 是错误的

(三)应当:

偏移量 = y-(斜率*x)

请参见:http://zedgraph.org/wiki/index.php?title=Trend

0赞 franchsesko 1/9/2013 #6

非常感谢你的解决方案,我挠了挠头。
以下是我在Excel中应用解决方案的方法。
我在Excel中成功使用了MUHD给出的两个函数:
a = (sum(x*y) - sum(x)sum(y)/n) / (sum(x^2) - sum(x)^2/n) b = sum(y)/n - b(sum(x)/n)

(小心我的a和b是MUHD解决方案中的b和a)。

- 制作了 4 列,例如: 注意:我的值 y 值在 B3:
B17 中,所以我有 n=15;
我的 x 值是 1,2,3,4...15。
1. B 列:已知的
x 2.C 列:已知 y 的
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) 以制作图形。
HTH。

18赞 Thymine 4/4/2013 #7

这是 Bedwyr Humphreys 答案的一个非常快速(和半脏)的实现。该接口也应该与 @matt 的答案兼容,但使用而不是使用更多的 IEnumerable 概念,希望使其更易于使用和阅读。decimalint

Slope是,是bIntercepta

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;
    }
}
1赞 Magn3144 1/7/2018 #8

这是我计算斜率的方法: 来源: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;
    }
1赞 Todd Skelton 7/3/2018 #9

这是我最终使用的内容。

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;
    }
}

我的数据集在 x 轴上使用 Unix 时间戳,对 y 使用小数点。更改这些数据类型以满足您的需要。我在一次迭代中完成所有总和计算,以获得最佳性能。

0赞 jonyB 1/29/2020 #10

如果有人需要 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) 点数组,并返回给定某个 x 的 y 的函数 玩得开心:)

0赞 user13889781 7/8/2020 #11

这是 golang 中的一个工作示例。我四处搜索并找到了这个页面并将其转换为我需要的内容。希望其他人能发现它有用。

// 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)

}
2赞 Gregg Reno 4/17/2021 #12

我将 Matt 的代码转换为 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;
    }
}

评论

0赞 stolorma 8/1/2021
我花了几个小时寻找解决方案,最终回到了你的答案,它奏效了!谢谢!
0赞 han.k 5/31/2023 #13

除以 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