情報地球科学演習 (2024年前学期) 第07回

今日のテーマ: 移動平均 (その1)

次のテーマは、データの「平滑化」の一種である「移動平均」。 今回は移動平均の計算だけでなく、その準備となる

という作業も合わせて行ってみる。

「2次元」データの「1次元」への並べ替え (その1)

もともとのデータは

並んでいる、いわば「2次元の表」になっていた。 (有名な「表計算ソフト」である Excel の画面を思い出してほしい。) この「表」の形式は、それはそれで便利ではあるのだが、今回のテーマである「移動平均」を考えるには少々不便である。 というのも例えば というような違いがあるからである。 そのおかげで、移動平均をとろうとする際に必要な、「1つ前」とか「1つ後」の指し方に統一感がなくなり、プログラミングが面倒になってしまうのだった。

こういった面倒さを避ける簡単な方法は、いったんデータを「古い順番に一列に」なるように並べ直すことである。 そのためには以下のような プログラム を用いればよい。 このプログラムを (例えば) enshu06a.f90 という名前で保存し、例の如く gfortran enshu06a.f90 とした後に、

$ cat matsuyama-kion.txt | ./a.out
と実行せよ。 またこのプログラムが正しく動作しているかどうかを、以下の手順により確認せよ。

program enshu06a                                          
implicitnone
integer::seireki,tsuki,joutai,bangou
real,dimension(12)::kion
bangou=1
do
read(*,'(i4,12f5.1)',iostat=joutai)seireki,kion
if(joutai/=0)exit
dotsuki=1,12
write(*,'(2i5,i2.2,f6.1)')bangou,seireki,tsuki,&
kion(tsuki)
bangou=bangou+1
enddo
enddo
endprogramenshu06a

なお、このプログラムのミソは (あえて言うなら)、

正しい出力が得られていることが確認できたら、
$ cat matsuyama-kion.txt | ./a.out > matsuyama-kion2.txt
と実行してみよ。 ここで「>」は出力をリダイレクト (redirect; 向け直す) する命令である。 この操作により、計算結果は画面に出力されることなく、matsuyama-kion2.txt というファイルの中に書き出される。 なおこの操作では、既にあるファイルの中身を上書き してしまうので、リダイレクトする先のファイル名の指定に注意せよ。

既に気付いていると思うが、このプログラムは 第1回で作ったデータの読み書きプログラム を基にして、書き出しの形式を変更しただけのものである。 これによって生成された matsuyama-kion2.txt と、もともとのデータである matsuyama-kion.txt の中身の違いを確認せよ。

「2次元」データの「1次元」への並べ替え (その2)

次は matsuyama-kion2.txt から、一列に並べ直されたデータを読み込むプログラムを作ってみよう。 そのためには以下のような プログラム を用いればよい。 このプログラムを (例えば) enshu06b.f90 という名前で保存し、例の如く gfortran enshu06b.f90 とした後に、

$ cat matsuyama-kion2.txt | ./a.out
と実行してみよ。

program enshu06b                                                    
implicitnone
integer::seireki,tsuki,joutai,bangou,kosuu
real,dimension(1800)::kion1retsu
kosuu=0
do
read(*,'(2i5,i2.2,f6.1)',iostat=joutai)bangou,seireki,tsuki,&
kion1retsu(bangou)
if(joutai/=0)exit
kosuu=kosuu+1
enddo
dobangou=1,kosuu
write(*,'(i4,f6.1)')bangou,kion1retsu(bangou)
enddo
endprogramenshu06b

なお、このプログラムのミソは以下の通り。

またこのプログラムでは、データを読み込む部分と、データを処理する部分とが完全に分離されている。 そのため、実際に移動平均を計算するための改造をする場合には、最初に データの読み込みに使っている do ループの内部には一切手を加える必要がない ようになっている。

移動平均をとる (その1)

さていよいよ実際に移動平均を計算する方法を考えてみよう。 最初の練習として、先に並べ替えた各月の平均気温データを用いて、「自分自身とその前後1つずつ」を使った移動平均 をとってみよう。 必要な手順 (の要点だけ) を以下に示す。

これらを参考にして、先に作ったプログラム enshu06b.f90 に、各月の気温の移動平均を計算する機能を追加したプログラムを作成せよ [みほん]。 そのプログラムを用いて (例の如く gfortran enshu06c.f90 などとして a.out を新たに作った後に)

$ cat matsuyama-kion2.txt | ./a.out
と実行せよ。 また、移動平均の計算が正しくできているかどうかを、以下の手順により確認せよ。 もし上の手順で得られる出力の書式が違う場合には、プログラム中で不必要な write 文を飛ばすよう修正 しておいてください。 これは、次の作業 (グラフを描く) がスムーズにいくようにしたいからである。

正しい出力が得られていることが確認できたら、

$ cat matsuyama-kion2.txt | ./a.out > matsuyama-kion3.txt
と実行することにより、出力を matsuyama-kion3.txt という名前のファイルに書き出させておくこと。 また、本日の演習の出席確認のため、このプログラムを 末尾のフォーム 経由で提出せよ。

移動平均のグラフを描く (その1)

次は Gnuplot を使って、移動平均をとった結果を目で見てみよう。

まずは端末から Gnuplot を起動し、以下のように入力してみよう (gnuplot>Gnuplot のプロンプト)。

gnuplot> plot "matsuyama-kion3.txt" using 1:2 with lines
これにより、134年(=1608ヶ月) 分の毎月の平均気温が折れ線グラフで表示される。 当然ながら、季節変化に対応する「ギザギザ」がよく見えているはずだ。 なおこの命令は、matsuyama-kion3.txt の1列目 (要するに、「最初から何か月めか」の番号) を横軸に、2列目 (要するに「その月の平均気温」) を縦軸にしたグラフを描け、というものである。 また、
gnuplot> plot [1488:1608] "matsuyama-kion3.txt" using 1:2 with lines
とすると、横軸の範囲が 1488 から 1608 の間 (要するに最近の120ヶ月=10年) 分のみになったグラフが表示されるはずだ。 この手順にならって、横軸の範囲をとり直したグラフをいくつか描かせてみよ。

これに加えて今度は、計算した移動平均の値も同時に表示させてみよう。

gnuplot> plot [1488:1608] "matsuyama-kion3.txt" using 1:2 with lines, "matsuyama-kion3.txt" using 1:3 with lines
として表示されるグラフをよく観察してみよ。 もともとの平均気温のグラフと、移動平均のグラフとをよく見比べることにより、両者の間にはどのような違いがあるかを考えよ。

先のグラフでみたように、移動平均をとることによって 「ギザギザ」がなまる という効果が得られる。 これは専門用語でいうところの ローパスフィルター (low-pass filter) に通したことに相当する。 ローパスフィルターとは、データに含まれる時間変動のうち、速く変動する (高周波) 成分を弱め、ゆっくり変動する (低周波) 成分をとり出す 性質、あるいは (同じことだが) 短い周期で変動する成分を弱め、長い周期で変動する成分をとり出す 性質を持っている。 今回の例では、真夏や真冬に対応する1ヶ月単位の鋭い「ギザギザ」が (ほんの少しだけだが) なまり、「トゲ」が小さくなるとともに「幅」が広がっていることが分かる。

ここで念のため、移動平均をとることによって取り除かれた成分がどんなものか、グラフで確認してみよう。

gnuplot> plot [1488:1608] "matsuyama-kion3.txt" using 1:($2-$3) with lines
この命令は、matsuyama-kion3.txt の1列目 (要するに、「最初から何か月めか」の番号) を横軸に、縦軸に2列目と3列目の差をとったグラフを描け、というものである。 ではこのグラフに示されているものは具体的には何だろうか?

もともとのデータから、移動平均された値を引き算してやると、移動平均によってなまらされた「ギザギザ」の成分を取り出すことができる。 この操作の意味をもう少し「学問的」に言えば、データに含まれる時間変動のうち、長い周期で変動する成分を弱め、短い周期で変動する成分をとり出す、あるいは (同じことだが) ゆっくり変動する (低周波) 成分を弱め、速く変動する (高周波) 成分をとり出す という性質を持っている。 このような性質をもったフィルターを専門用語では ハイパスフィルター (high-pass filter) という。

移動平均をとる (その2)

次は、移動平均をとる際に使う点の数を変えることで、「ギザギザ」のなまり方がどう変わるかを調べてみよう。 これまでに作った「自分自身とその前後1つずつを使った移動平均」をとるプログラムを改造し、「自分自身とその前後2つずつを使った移動平均」を計算する機能を追加 してみよう。 必要な手順 (の要点だけ) を以下に示す。

これらを参考にして、各月の気温の移動平均を計算する機能を追加したプログラムを作成せよ [みほん]。 そのプログラムを用いて (例の如く gfortran enshu06d.f90 などとして a.out を新たに作った後に)

$ cat matsuyama-kion2.txt | ./a.out
と実行し、正しい (言い換えれば「自分の意図した通りの」) 出力が得られているかどうかを確認せよ。 なお、正しく計算できていれば、出力の先頭5行と末尾5行はそれぞれ以下のようになっているはずである。
   1  4.80  6.15  7.03
   2  7.50  7.03  8.93
   3  8.80 10.30 10.58
   4 14.60 13.53 13.94
   5 17.20 17.80 17.44
1604 28.90 28.07 25.34
1605 27.30 25.20 23.68
1606 19.40 20.50 19.94
1607 14.80 14.50 17.70
1608  9.30 12.05 14.50

正しい出力が得られていることが確認できたら、

$ cat matsuyama-kion2.txt | ./a.out > matsuyama-kion4.txt
と実行することにより、出力を matsuyama-kion4.txt という名前のファイルに書き出させておくこと。

移動平均のグラフを描く (その2)

上で計算した2種類の移動平均の違いを、Gnuplot でグラフを描かせて調べてみよう。 再び端末から Gnuplot を起動し、以下のように入力してみよう (gnuplot>Gnuplot のプロンプト)。

gnuplot> plot [1488:1608] "matsuyama-kion4.txt" using 1:2 with lines, "matsuyama-kion4.txt" using 1:3 with lines, "matsuyama-kion4.txt" using 1:4 with lines
これにより表示されるグラフをよく観察してみよ。 もともとの平均気温のグラフと、2種類の移動平均のグラフとをよく見比べることにより、これらの間にはどのような違いがあるかを考えよ。 特に、2種類の移動平均のグラフの違いに注目し、「移動平均をとる際に含めるデータ」を多くとることによって「ギザギザ」のなまり方がどう変わっているかを検討せよ。 また、さらに
gnuplot> plot [1488:1608] "matsuyama-kion4.txt" using 1:($2-$3) with lines, "matsuyama-kion4.txt" using 1:($2-$4) with lines
をとして表示される2つのグラフを比較することによって、移動平均によってなまらされた「ギザギザ」の成分の特徴がどう変わっているかについても考えてみよ。 その際、「たて軸」方向に見える「ギザギザ」の成分の大きさ の違いだけでなく、「よこ軸」方向に見える「ギザギザ」の成分の時間方向の広がりの程度 の違いにも注目してみること。

余力がある人向きのおまけ: もっと「実用的」な移動平均のプログラミング

上で作成した移動平均のプログラムからも分かるように、移動平均をとるのに用いるデータの個数が大きくなるほど、より複雑な場合分けが必要になってくる。 これだと「前後1つ」や「前後2つ」の移動平均ならまだしも、例えば「前後5個」くらいまでとった移動平均のプログラミングなんて、とてもやる気にならなくなる。 しかし、なぜ場合分けをする必要があったかに注目してやれば、もっとシンプルかつ実用的なプログラミングが可能になる。

このプログラム (コピペ可) は、場合分けを長々と書き並べなくてもよいように作った移動平均のプログラムの例である。 ここでは整数型変数の mado_haba_half の値により、自分とその前後いくつずつを使った移動平均を計算するかを指定できるようになっている。 加えて、実際に移動平均を計算する際には、別の整数型変数 bangou_hajimebangou_owari を用いて、何番目から何番目のデータの総和をとることが可能か、また別の整数型変数 souwa_no_kosuu を用いて 何個のデータの総和をとったか をプログラム中で自動的に調べるようにしてある。 なお maxmin は、カッコ内の値からそれぞれ最大値と最小値を選び出す組み込み関数である。 組み込み関数の説明は ここ を、また他の組み込み関数の例はここ を参照のこと。

余力のある人は、移動平均をとる前後のデータの個数を 2 よりも大きくした場合に、移動平均の性質がどう変化していくかを調べてみよ。 また元のデータには12ヶ月を単位とする周期的な変動があるはずだが、その周期と比べて十分大きな幅 (例えば前後6個ずつなど) で移動平均をとってみると、どんな出力が得られるかを確認してみよ。

なお既に気付いている人もいるであろうが、上で作成した移動平均のプログラムには、「移動平均をとるデータの総数が奇数」に限られているという (実はけっこう残念な) 制約があった。 まだまだ余裕のある人は、このプログラム あるは このプログラム (いずれもコピペ可) を参考にして、「移動平均をとるデータの総数が奇数でも偶数でもOK」というプログラムを作成してみよ。 また元のデータには12ヶ月を単位とする周期的な変動があるはずだが、その周期と全く同じ幅 (合計12個のデータ) あるいはその整数倍で移動平均をとってみると、どんな出力が得られるかを確認してみよ。

出席確認用プログラム提出フォーム

Moodle のコースページ 以下の 課題ファイル提出 により、各自で作成した (enshu06c.f90 に相当する) 「自分とその前後1つずつ」を使った移動平均を計算する Fortran 90 プログラムの最終版を提出せよ。