谷歌不是我的朋友——自从我上大学的统计课以来已经很久了……我需要计算图表上趋势线的起点和终点——有没有简单的方法可以做到这一点?(在 C# 中工作,但任何语言都适合你)
12 回答
感谢大家的帮助-我已经解决了这个问题几天,然后才回到它-能够将它拼凑在一起-不是最优雅的代码,但它适用于我的目的-如果我想我会分享其他人遇到这个问题:
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 = (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 是点数
假设趋势线是直的,通过选择任意两个点并计算得出斜率:
(A) 斜率 = (y1-y2)/(x1-x2)
然后你需要找到线的偏移量。该线由以下等式指定:
(B) y = 偏移 + 斜率*x
所以你需要解决偏移量。选择线上的任意一点,并求解偏移量:
(C) 偏移量 = y - (斜率*x)
现在您可以将斜率和偏移量插入直线方程 (B) 并获得定义直线的方程。如果您的线路有噪音,您将不得不决定平均算法,或使用某种曲线拟合。
如果您的线不直,那么您需要研究曲线拟合或最小二乘拟合- 非平凡但可行。如果您知道自己想要哪种拟合,您将在最小二乘拟合网页(指数、多项式等)的底部看到各种类型的曲线拟合。
此外,如果这是一次性的,请使用 Excel。
这是Bedwyr Humphreys 答案的一个非常快速(和半脏)的实现。该界面也应该与@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) y = 偏移 + 斜率*x
那么(C) offset = y/(slope*x)是错误的
(C) 应该是:
偏移量 = y-(斜率*x)
如果您可以访问 Excel,请查看帮助中函数参考的“统计函数”部分。对于直线最佳拟合,您需要 SLOPE 和 INTERCEPT 并且方程就在那里。
哦,等等,它们也在此处在线定义:http: //office.microsoft.com/en-us/excel/HP052092641033.aspx用于 SLOPE,并且有一个指向 INTERCEPT 的链接。当然,假设 MS 不移动页面,在这种情况下,请尝试使用谷歌搜索类似“斜率截距方程 Excel 站点:microsoft.com”的内容 - 刚才给出的链接是第三个。
我将 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;
}
}
这是我计算斜率的方式:来源: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;
}
这是我最终使用的。
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。更改这些数据类型以满足您的需要。我在一次迭代中完成所有总和计算,以获得最佳性能。
非常感谢您的解决方案,我正在摸不着头脑。
这是我在 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。
如果有人需要 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 函数 玩得开心:)
这是 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)
}