Excel 2013 on Windows 8.1 pro (64bit)
C# Online Snippet Compiler
Excelの関数を見ていく中、Trend()なるものを見つけた。
回帰直線による予測値を返します。 R-2 乗値のメソッドを使って、直線を既知の y と既知の x の配列に当てはめます。 その直線上で、指定した新しい x の配列に対応する y の値が返されます。
サンプルとなるデータ例と、C#での実装は以下で記載されている。
http://stackoverflow.com/questions/7437660/how-do-i-recreate-an-excel-formula-which-calls-trend-in-c
answered May 23 '13 at 18:40
Mohgeroth
によるコードはC# Online Snippet Compilerで実行しようとしたらPointF型が見つからないエラーとなった。
考え方だけ参考にしておこう。
上記リンクに記載データでExcel上で計算した例。
650に対して6.1が得られた。
code v0.2 > C#実装
協力者のおかげでC#実装できました。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Drawing;
public class Program
{
/// <summary>
/// Gets the value at a given X using the line of best fit (Least Square Method) to determine the equation
/// </summary>
/// <param name="points">Points to calculate the value from</param>
/// <param name="x">Function input</param>
/// <returns>Value at X in the given points</returns>
public static float LeastSquaresValueAtX(List<PointF> points, float x)
{
float slope = SlopeOfPoints(points);
float yIntercept = YInterceptOfPoints(points, slope);
return (slope * x) + yIntercept;
}
/// <summary>
/// Gets the slope for a set of points using the formula:
/// m = ? (x-AVG(x)(y-AVG(y)) / ? (x-AVG(x))²
/// </summary>
/// <param name="points">Points to calculate the Slope from</param>
/// <returns>SlopeOfPoints</returns>
private static float SlopeOfPoints(List<PointF> points)
{
float xBar = points.Average(p => p.X);
float yBar = points.Average(p => p.Y);
float dividend = points.Sum(p => (p.X - xBar) * (p.Y - yBar));
float divisor = (float)points.Sum(p => Math.Pow(p.X - xBar, 2));
return dividend / divisor;
}
/// <summary>
/// Gets the Y-Intercept for a set of points using the formula:
/// b = AVG(y) - m( AVG(x) )
/// </summary>
/// <param name="points">Points to calculate the intercept from</param>
/// <returns>Y-Intercept</returns>
private static float YInterceptOfPoints(List<PointF> points, float slope)
{
float xBar = points.Average(p => p.X);
float yBar = points.Average(p => p.Y);
return yBar - (slope * xBar);
}
public static void Main()
{
var P = new PointF[]
{
new PointF { X = 600, Y = 6},
new PointF { X = 800, Y = 7},
new PointF { X = 1000, Y = 10},
new PointF { X = 1200, Y = 12},
new PointF { X = 1400, Y = 14},
};
var lst = new List<PointF>();
foreach(var element in P) {
lst.Add(element);
}
var res = LeastSquaresValueAtX(lst, 650);
Console.WriteLine(res);
}
}
6.125
Excelの結果と整合した。
PointFの初期値リストを与えてforeachでAddするのは、もっと別にいい方法があるかもしれない。
code v0.3
@ozwk さんのコメントを参考にコードを以下のようにしました。
情報感謝です。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Drawing;
public class Program
{
/// <summary>
/// Gets the value at a given X using the line of best fit (Least Square Method) to determine the equation
/// </summary>
/// <param name="points">Points to calculate the value from</param>
/// <param name="x">Function input</param>
/// <returns>Value at X in the given points</returns>
public static float LeastSquaresValueAtX(List<PointF> points, float x)
{
float slope = SlopeOfPoints(points);
float yIntercept = YInterceptOfPoints(points, slope);
return (slope * x) + yIntercept;
}
/// <summary>
/// Gets the slope for a set of points using the formula:
/// m = ? (x-AVG(x)(y-AVG(y)) / ? (x-AVG(x))²
/// </summary>
/// <param name="points">Points to calculate the Slope from</param>
/// <returns>SlopeOfPoints</returns>
private static float SlopeOfPoints(List<PointF> points)
{
float xBar = points.Average(p => p.X);
float yBar = points.Average(p => p.Y);
float dividend = points.Sum(p => (p.X - xBar) * (p.Y - yBar));
float divisor = (float)points.Sum(p => Math.Pow(p.X - xBar, 2));
return dividend / divisor;
}
/// <summary>
/// Gets the Y-Intercept for a set of points using the formula:
/// b = AVG(y) - m( AVG(x) )
/// </summary>
/// <param name="points">Points to calculate the intercept from</param>
/// <returns>Y-Intercept</returns>
private static float YInterceptOfPoints(List<PointF> points, float slope)
{
float xBar = points.Average(p => p.X);
float yBar = points.Average(p => p.Y);
return yBar - (slope * xBar);
}
public static void Main()
{
var lst = new List<PointF>{
new PointF(600, 6), // X,Y
new PointF(800, 7),
new PointF(1000, 10),
new PointF(1200, 12),
new PointF(1400, 14),
};
var res = LeastSquaresValueAtX(lst, 650);
Console.WriteLine(res);
}
}
6.125