LINQでStdev (標準偏差)

#背景
LINQの標準ライブラリには標準偏差を求めるメソッドがない。
なので拡張メソッドを自作
詳しくは[こちら](https://qiita.com/c60evaporator/items/bb49cc539ed7315a8d23)
※ちなみに、ここで求めているのは母集団標準偏差（ExcelのStdev.P）です

##コード
アルゴリズムは[こちら](https://imagingsolution.blog.fc2.com/blog-entry-62.html)を参照させていただきました。
自乗和を先に求めるのがポイント

```cs
public static double Stdev<T>(this IEnumerable<T> src)
{
    if(!src.Any()) throw new InvalidOperationException("Cannot compute median for an empty set.");
    //Doubleにキャストして処理を進める
    var doubleList = src.Select(a => Convert.ToDouble(a)).ToArray();

    //平均値算出
    double mean = doubleList.Average();
    //自乗和算出
    double sum2 = doubleList.Select(a => a * a).Sum();
    //分散 = 自乗和 / 要素数 - 平均値^2
    double variance = sum2 / doubleList.Count - mean * mean;
    //標準偏差 = 分散の平方根
    return Math.Sqrt(variance);
}
```

※以前のコードからキャスト法を変更しました（詳細は下記）
※四則演算可否確認処理が冗長だったので、削除しました

##結果
```cs
List<int> iList = new List<int> { 1, 2, 3, 4, 5 };
List<double> dList = new List<double> { 3.2, 3.5, 3.6, 4 };
//標準偏差(int)
Console.WriteLine(iList.Stdev().ToString());
//標準偏差(double)
Console.WriteLine(dList.Stdev().ToString());
```

```
1.4142135623731
0.28613807855649
```
正常に標準偏差が出せていそうです

##苦労したところ
**・Generic → doubleへのキャスト**
平均や二乗和を求めるためのAverage()やSum()がGeneric型では使えない。
doubleにキャストしようとしたが、[ここ](http://var.blog.jp/archives/67580859.html)に書いてあるobjectキャストを挟む方法ではエラー発生
[ここ](https://cms.shise.net/2014/10/csharp-object-cast/)に書いてるConvert.ToDouble()でキャストできた

（foreachの中なので、キャスト部分の動作速度が若干不安ではありますが）


##追記　キャストの速度について

キャスト法を変更＆ジェネリックからのキャストの速度が気になったので、測定してみました。

**テスト1：ジェネリックなしで処理を直打ち（下のテストコード参照）**

**テスト2：上の記事でのキャスト法**

**テスト3：以前のキャスト法**

```cs:以前のキャスト法
public static double Stdev2<T>(this IEnumerable<T> src)
        {
            if(!src.Any()) throw new InvalidOperationException("Cannot compute median for an empty set.");
            //Doubleにキャストして処理を進める
            var doubleList = new List<double>();
            foreach (var srcRow in src) doubleList.Add(Convert.ToDouble(srcRow));

            //平均値算出
            double mean = doubleList.Average();
            //自乗和算出
            double sum2 = doubleList.Select(a => a * a).Sum();
            //分散 = 自乗和 / 要素数 - 平均値^2
            double variance = sum2 / doubleList.Count - mean * mean;
            //標準偏差 = 分散の平方根
            return Math.Sqrt(variance);
        }
```

```cs:テストコード（100万要素の標準偏差計算）
var testList = Enumerable.Range(0, 1000000).ToArray();
for (int i = 0; i < 10; i++)
{
    //テストその1（ジェネリックからキャスト）
    var sw = new System.Diagnostics.Stopwatch();
    sw.Start();
    var test1 = testList.Stdev();
    sw.Stop();
    Console.WriteLine(sw.Elapsed.TotalMilliseconds);
    //テストその2（ジェネリックからキャスト（旧））
    sw = new System.Diagnostics.Stopwatch();
    sw.Start();
    var test2 = testList.Stdev2();
    sw.Stop();
    Console.WriteLine(sw.Elapsed.TotalMilliseconds);
    //テストその3（ジェネリックなしでキャスト）
    sw = new System.Diagnostics.Stopwatch();
    sw.Start();
    var doubleList = testList.Select(c => (double)c).ToArray();
    //平均値算出
    double mean = doubleList.Average();
    //自乗和算出
    double sum2 = doubleList.Select(c => c * c).Sum();
    //分散 = 自乗和 / 要素数 - 平均値^2
    double variance = sum2 / doubleList.Length - mean * mean;
    //標準偏差 = 分散の平方根
    double stdev = Math.Sqrt(variance);
    sw.Stop();
    Console.WriteLine(sw.Elapsed.TotalMilliseconds);
}
```
<br/>

```:結果
テスト1 | テスト2 | テスト3
31.6994 |57.3438 |48.5285
32.7673 |45.8568 |49.1214
32.5674 |45.5782 |49.4263
32.9343 |41.131 |49.9514
32.1911 |44.8735 |49.0177
32.3129 |41.1157 |46.1472
33.2736 |43.896 |59.9206
36.8521 |40.2454 |45.7436
39.7002 |43.5034 |48.8821
33.6299 |42.0819 |47.0015
```
テスト1（ジェネリックのキャストなし）：平均33.8ミリ秒
テスト2（新キャスト法）：平均44.6ミリ秒
テスト3（旧キャスト法）：平均49.4ミリ秒

キャストに若干の時間は掛かっていそうですが、
標準偏差の計算時間の方が大きいので、
それほど気にせずともよいレベルと思われます。