グーグルは私の友達ではありません-大学の統計学の授業から久しぶりです...グラフのトレンドラインの開始点と終了点を計算する必要があります-これを行う簡単な方法はありますか?(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;
}
}
OK、これが私の最高の疑似数学です:
あなたのラインの方程式は次のとおりです。
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などの合計です。特に明確ではありませんが、シグマ記号なしで実行できる最善の方法です:)
...そして今シグマが追加されています
b =(Σ(xy)-(ΣxΣy)/ n)/(Σ(x ^ 2)-(Σx)^ 2 / n)
a =(Σy)/ n-b((Σx)/ n)
ここで、Σ(xy)はすべてのx * yなどの合計であり、nはポイントの数です。
トレンドラインが直線であるとすると、任意の2つのポイントを選択し、次のように計算して、勾配を見つけます。
(A)勾配=(y1-y2)/(x1-x2)
次に、ラインのオフセットを見つける必要があります。この線は次の式で指定されます。
(B)y=オフセット+勾配*x
したがって、オフセットを解決する必要があります。線上の任意の点を選択し、オフセットを求めます。
(C)オフセット= y-(勾配* x)
これで、傾きとオフセットを線の方程式(B)に代入して、直線を定義する方程式を作成できます。ラインにノイズがある場合は、平均化アルゴリズムを決定するか、ある種のカーブフィッティングを使用する必要があります。
直線でない場合は、カーブフィッティングまたは最小二乗フィッティングを調べる必要があります。これは簡単ではありませんが、実行可能です。必要なフィットの種類がわかっている場合は、最小二乗フィッティングWebページの下部にさまざまなタイプのカーブフィッティング(指数、多項式など)が表示されます。
また、これが1回限りの場合は、Excelを使用してください。
これは、 Bedwyr Humphreys's answerの非常に迅速な (そして半汚い) 実装です。インターフェイスは@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) オフセット = y/(勾配*x)は間違っています
(C) は次のとおりです。
オフセット = y-(勾配*x)
Excelにアクセスできる場合は、ヘルプ内の関数リファレンスの「統計関数」セクションを参照してください。直線の最適化には、SLOPEとINTERCEPTが必要であり、方程式はすぐそこにあります。
ああ、ちょっと待ってください。オンラインでもここで定義されています:http: //office.microsoft.com/en-us/excel/HP052092641033.aspx for SLOPE、そしてINTERCEPTへのリンクがあります。もちろん、これはMSがページを移動しないことを前提としています。その場合は、「SLOPE INTERCEPT EQUATIONExcelsite:microsoft.com」のようなものをグーグルで試してみてください。
これは私が勾配を計算した方法です: ソース: 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;
}
}
私のデータ セットは、x 軸に Unix タイムスタンプを使用し、y 軸に 10 進数を使用しています。必要に応じてこれらのデータ型を変更してください。可能な限り最高のパフォーマンスを得るために、すべての合計計算を 1 回の繰り返しで行います。
解決策をありがとう、私は頭を悩ませていました。
これが、Excelでソリューションを適用する方法です。
ExcelでMUHDが提供する2つの関数を正常に使用しました:
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 2 乗値
次に、列 B、C、および E を合計します。合計は 18 行目にあるので、B18 は X の合計、C18 は Y の合計、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。