【C#】LINQを使って移動平均を算出

スポンサーリンク

前回の記事でLINQ内の「Enumerableクラス」を使ってpython風の繰り返し文を作ってみました!
【C#】Python風の繰り返し文 | 育児パパの人生備忘録 (t19488sns.com)

今回は「Enumerableクラス」を使って移動平均を算出する関数を作ってみようと思います。

移動平均とは

そもそも移動平均とは何でしょうか?

移動平均は、時系列データ(より一般的には時系列に限らず系列データ)を平滑化する手法である。音声や画像等のデジタル信号処理に留まらず、金融(特にテクニカル分析)分野、気象水象を含む計測分野等、広い技術分野で使われる。有限インパルス応答に対するローパスフィルタデジタルフィルタ)の一種であり、分野によっては移動積分とも呼ばれる。

主要なものは、単純移動平均と加重移動平均と指数移動平均の3種類である。普通、移動平均といえば、単純移動平均のことをいう。

移動平均 – Wikipedia

説明では分かりづらいのですが、これだけは分かりました。
自分が知っているのは、単純移動平均だという事を!!

【図解】単純移動平均

例え話をすると、『16個のランダムな自然数があるとします。』

このデータをグラフ化すると、

数値のバラつきが大きいため、グラフが山あり谷ありの状態です。
そこで移動平均を使って、グラフを滑らかにします。(今回は、区間4でやります)
データ元から過去4つのデータの平均を求めます。

するとグラフは下記のようになります。(オレンジの線)

平均を計算する分だけデータ数は少なくなります。
『傾向』を分析する事に使われることが多いです。

定義

まずは、NameSpaceの定義です。
[System.Linq]を使います。

using System.Linq;

移動平均の算出

移動平均を算出する関数を紹介します。
サンプルとしては
『整数のリストクラス(dts)から指定区間(sec)』で移動平均を計算します。
計算結果は、出力(return)する。

private List<double> CalculationMovingAverage(List<int> dts, int sec)
{
    int num = dts.Count;             //データ数取得

    //移動平均計算(第2引数は区間)
    return Enumerable.Range(0, num - sec + 1)
        .Select(i => dts.Skip(i).Take(sec).Average()).ToList();
}

解説

最初に必要なのは、『データの数』と『区間』です。
『データの数』は、データ元の個数です。(第1引数のリストに登録されているデータ数:num)
『区間』は、何個のデータで平均を算出するかです。(第2引数:sec)

ココから移動平均した時のデータ数が決まります。
この移動平均のデータ数が繰り返し数となります。
その為、下記のように表現されます。

Enumerable.Range(0, num - sec + 1)

繰り返されている間の計算は、指定区間の平均を計算する事です。
Selectメソッドを使って、データを加工しましょう。

Select(i => dts.Skip(i).Take(sec).Average())

下記、流れで処理されています。

  1. 【 i 】は、Enumerable.Range()の数値になります。(0,1,2,….,num – sec)
  2. 【 i 】の分だけデータ元からスキップさせます。《 dts.Skip(i)
  3. データ元の【 i 】の位置から区間数分のデータを抜き取ります。《 Take(sec)
  4. 抜き取られたデータの平均を求めます。《 Average()

最後に、今のままでは使いづらいので、リストクラスに変換します。

ToList()

まとめ

今回は、Enumerableを使って単純移動平均を求める関数を紹介しました。
移動平均を求める関数はコレが絶対ではありませんが、1つの手段として紹介します。
LINQやラムダ式を少し理解していないと分かりずらいです。。。
ただLINQやラムダ式を使わなければ、数十行は書かないといけません。
そう思うと1行で完結している事はメリットとして大きいと考えています。
比較しているわけではないので一概に言えませんが、データ数が多くなると処理速度は遅い可能性はあります。

余談

ランダムな整数のリスト(データ元)を作る関数

せっかくなので、これもEnumerableを使って作ってみました。
ほかにもいい方法はあるのかもしれません。
この関数を使用する事で、
指定のデータ数でランダムな数字のリストを作成する事ができます
※ランダムな整数の範囲は0から99にしています。

private void CreateDataSource(int num, out List<int> dts)
{
    List<int> tmp = new List<int>();    //ラムダ式内用に生成
    Random rd = new Random();   //ランダムのオブジェクト生成

    //リストにランダムな数字を登録する
    Enumerable.Range(0, num).ToList().ForEach(i => tmp.Add(rd.Next(0, 100)));
    dts = new List<int>(tmp);   //代入する
}

コメント

タイトルとURLをコピーしました