0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Excel > TREND() > C#実装の参考 (ビルドはできなかった) > C#実装した

Last updated at Posted at 2016-08-30
動作確認
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上で計算した例。

qiita.png

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
0
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?