質問
Google は友達ではありません。大学で統計の授業をして以来、長い時間が経ちました。グラフ上の傾向線の開始点と終了点を計算する必要があります。これを行う簡単な方法はありますか?(C# で作業しますが、どの言語でも機能します)
解決
傾向線が直線であることを考慮して、任意の 2 つの点を選択し、次の計算を行って傾きを見つけます。
(A) 傾き = (y1-y2)/(x1-x2)
次に、ラインのオフセットを見つける必要があります。直線は次の方程式で指定されます。
(B) y = オフセット + 傾き*x
したがって、オフセットを解決する必要があります。線上の任意の点を選択し、オフセットを求めます。
(C) オフセット = y - (傾き*x)
これで、傾きとオフセットを直線の方程式 (B) に代入して、直線を定義する方程式を得ることができます。ラインにノイズがある場合は、平均化アルゴリズムを決定するか、ある種のカーブ フィッティングを使用する必要があります。
線がまっすぐでない場合は、次のことを確認する必要があります。 カーブフィッティング, 、 または 最小二乗フィッティング - 簡単ではありませんが、実行可能です。希望する近似の種類がわかっている場合は、最小二乗近似 Web ページの下部にさまざまなタイプの曲線近似 (指数関数、多項式など) が表示されます。
また、これが 1 回限りの場合は、Excel を使用してください。
他のヒント
皆さんのご協力に感謝します - 数日間この問題から離れていましたが、ちょうど戻ってきました - これをまとめることができました - 最もエレガントなコードではありませんが、私の目的には機能します - もしあれば共有したいと思いました他の人もこの問題に遭遇します。
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 = 合計(y)/n - b(合計(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 は点の数です
これは非常に簡単な (そして半分汚い) 実装です。 ベッドワイヤー・ハンフリーズの答え. 。インターフェースは以下と互換性がある必要があります @マットの答えも同様ですが、使用します 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 SLOPE には INTERCEPT へのリンクがあります。もちろん、これは MS がページを移動しないことを前提としています。その場合は、「SLOPE INTERCEPT EQUATION Excel site:microsoft.com」などでグーグル検索してみてください。指定されたリンクが 3 番目に表示されました。
これは私が傾きを計算する方法です:ソース: 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 = 合計(y)/n - b(合計(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 の 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。