前回の記事で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())
下記、流れで処理されています。
- 【 i 】は、Enumerable.Range()の数値になります。(0,1,2,….,num – sec)
- 【 i 】の分だけデータ元からスキップさせます。《 dts.Skip(i) 》
- データ元の【 i 】の位置から区間数分のデータを抜き取ります。《 Take(sec) 》
- 抜き取られたデータの平均を求めます。《 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); //代入する
}
業務でプログラミング(C#/VB/Python)を作っている。
挫折を何回も繰り返し、幾度の壁を乗り越えてきた。
乗り越えてきた事を忘れないように記録に残す。
同じ思いをしている人への情報提供になれたらと思う。
基本は初心者に向けたプログラムの情報を提供する。
コメント