51

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

4

12 回答 12

36

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

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;
    }
}
于 2008-09-06T06:23:20.590 回答
30

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

您的线的方程式是:

Y = a + bX

在哪里:

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

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

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

...现在添加了 Sigma

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

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

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

于 2008-09-04T05:55:58.117 回答
19

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

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

然后你需要找到线的偏移量。该线由以下等式指定:

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

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

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

现在您可以将斜率和偏移量插入直线方程 (B) 并获得定义直线的方程。如果您的线路有噪音,您将不得不决定平均算法,或使用某种曲线拟合。

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

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

于 2008-09-04T06:07:00.727 回答
17

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

SlopebIntercepta

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;
    }
}
于 2013-04-03T22:27:21.043 回答
3

关于之前的回答

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

那么(C) offset = y/(slope*x)是错误的

(C) 应该是:

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

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

于 2009-04-03T09:48:29.227 回答
2

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

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

于 2008-09-04T08:24:10.210 回答
2

我将 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;
    }
}
于 2021-04-16T18:53:01.107 回答
1

这是我计算斜率的方式:来源: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;
    }
于 2018-01-07T09:51:58.730 回答
1

这是我最终使用的。

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。更改这些数据类型以满足您的需要。我在一次迭代中完成所有总和计算,以获得最佳性能。

于 2018-07-02T16:31:07.630 回答
0

非常感谢您的解决方案,我正在摸不着头脑。
这是我在 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 列,例如:
NB:我的值 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。

于 2013-01-09T13:06:45.683 回答
0

如果有人需要 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 的 ay 函数 玩得开心:)

于 2020-01-29T09:40:36.087 回答
0

这是 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)

}
于 2020-07-08T06:47:57.917 回答