フィルター型プログラムの例

小目次

フィルター

Unixでは、標準入力から読める限りのデータを読み、処理結果を 標準出力に書き出すようなプログラムがよく使われる [標準入出力の解説参照]。 このようなプログラムを、フィルター型のプログラムという。 (フィルターという用語は、統計学や制御工学に関係する時系列解析の 分野でも使われ、その場合の意味とこれとは関係はあるが別の概念である。)

データ

フィルター型プログラムで扱うデータはどんなものでもよいのだが、 この授業では、テキストファイル[別記事参照]に限る。 特に、1行に2つかそれ以上の数値が書かれ、いちばん左の列の 数値が時刻(t)を表わしているものを扱うことにしよう。 その例として、 東京(大手町)の月単位の気温と降水量のデータ を用意した。

つまらないフィルター

プログラムの例として、まず、入力データと同じ値を書き出す (書式の桁数は変わる)だけの簡単なものを用意した。 プログラムの順を追って説明をつけたので、下のリンクを選択して 見てほしい(言語名からのリンクが説明、プログラムファイルからのリンクは ソースプログラム)。

Pascal
filter.p
Fortran
filter.f
C
filter.c
これを、
% [filter.pを用意する]
% [データファイルkion.tokyo.1979を用意する]
% pc filter.p                                       # コンパイル
% a.out < kion.tokyo.1979                           # 実行  
のようにしてためしてみてほしい。ただし、コンパイルを行なうコマンドは、 Pascalではpc、Fortranではf77、Cではccである。また、結果を画面では なくファイル(例、outfile1)に取るには、
% a.out  < kion.tokyo.1979  > outfile1
のようにする。 実行結果をファイルに取って、gnuplotで表示してみよう。

選ぶ

上の例文filter.{p,f,c} (filter.p, filter.f, filter.c がそれぞれあることをこのように省略して書く)は、 わざと、ある条件が真(TRUE)になった場合に 書き出し(writeln, WRITE, printf)を実行するという形にして、 しかも、その条件が常に真になるように書いてある。 ここの条件を変えれば、条件に合ったものだけを抜き出す、フィルター らしいプログラムができる。

気候データの場合、特定の月の値だけがほしいことが多い。 今のサンプルデータでは、欠落なしに1月から12月まで値がならんで いるので、t の列の値を使わずに、データの個数を数えて、12個おきに 書き出す方法をとることができる。その方法で特定の月の値だけを 抜き出すフィルターの例文が、

に用意してある。

プログラムの初めに、定数 ncycle と ksel (C言語の例文では大文字)が設定してある。ncycleは12か月のデータから 特定の月を選ぶので12である。kselは選ぶ月の番号で、例では4にして あるが、エディタで変更してコンパイル、実行してみてほしい。 コンパイルしなおさなくても選択対象を変えられるプログラムにした ほうがよいが、フィルター型で実現するのが簡単ではないので今回は このようにした。

フィルター型にこだわらずに、 標準入出力以外のファイルも使う [別記事参照] 形で実現した例文は、 erabu1.p, erabu1.f, erabu1.c 。 この場合、kselは定数ではなく変数になる(したがってC言語でも 名前を小文字にした)。

このような簡単な修正は、mule (emacs)のようなスクリーンエディタ よりもラインエディタのほうが向いていることもあるので、 edを利用してerabu.fを修正する場合について 実行例を示す。

平均

データの個数と平均値を書き出す例文を用意した。

平均値からの偏差、配列変数

時系列を見るとき、長時間の平均値からのずれに注目することが多い。 気候データの場合「平年値」というものが定義されているが、ここでは それはあまり気にせず、入力データにある限りの値の平均値をとり、 各時刻の値のその平均値からの偏差を求め、時刻 t とともに書き出すことに する。プログラム例は

である。

気候学的にも意味のある処理としては、まず erabu で特定の月の値 だけを書き出しておき、それの長期平均からの偏差を計算するとよい。 それには、Unixのパイプライン処理 [別記事参照]が便利である。

hensaのプログラムは、使い方としては、標準入力を読んで標準出力に 書き出すフィルター型であって、最初の例文filterと変わらないが、 プログラムの内部構造には違いがある。

filter, erabu, heikin のそれぞれの例文では、データを1レコード (厳密ではないがこの例文の場合は「1行」と同じ)ぶんたくわえる 変数(計算機の記憶領域)だけを用意している。次のレコードを読むと その変数の値は変わってしまうから、データを読みこむループの外では、 個別のデータの値は利用できない。

平均値からの偏差を計算しようとすると、1番めのデータ値の偏差を 計算するとき、1番めのデータと平均値の両方を知らなければならず、 平均値を知るためには、すべてのデータを読みおえていなければならない。 これは、「filter」のような単純なプログラムでは不可能である。 対策としては、次のいくつかの形がある。

  1. 入力データファイルを複数回(この場合2回)読む。
  2. 平均値を別のプログラム(「heikin」)で計算し、その出力と もとのデータを合わせて読む。
  3. プログラム内に「配列型」変数をとり、読み込んだデータ値を たくわえておいてまた利用する。
このうち、1, 2の方法では、プログラムをきれいなフィルター型 (つまり入力として標準入力だけを使う形)で書くことがむずかしい。 そこで、3を採用する。

なお、2の方法を、配列を使わないフィルター型コマンドをcatコマンドと パイプラインで組み合わせる形で実現することもできる。これは 別記事で紹介する。

配列(array)というのは、1つの名前で複数の数値を記憶する一連の 記憶場所を確保するための変数の型である。(多次元の配列もあるが、ここでは 1次元の配列に限って説明する。) 配列の中の場所を区別するためには 通し番号のようなものを使う。PascalとCでは角かっこ、Fortranでは ふつうのかっこの中に整数値を書くが、数式の下つき添え字に対応 するので、添え字という。配列を確保するときには、(原則としてプログラムの 宣言部で)添え字の値の上限と下限を決めておかなければならない。

宣言のしかたは hensa の例文を参照。 なお、この例文では配列の上限を定数 ndim (Cでは大文字)として 与えてある。 データの個数が多いと、添え字が配列の上限を越えることが 起こりうるが、その場合の対策は上の例文ではしていない。 実用的なプログラムでは、意味のわかるメッセージを出して 止まるようにすべきである。その対策をした例は、 別記事で紹介する。

変数変換

データを目で見たり統計処理をするときに、いろいろな変数変換が 行なわれる。たとえば、桁違いに大きい値や小さい値が混ざっている 場合には、対数をとることが多い。(ただし、対数は正の数に対してしか 定義されていないので、ゼロや負になりうるデータを扱うときには注意。) そのほか、3乗根・平方根、逆に2乗・3乗・指数関数などの変換をすることが ある。こういった変換は、データの度数分布を左右対称に近づける向きに 行なわれることが多い。

また、基準位置からの距離だけに関心があり符号はどうでもよい場合は、 絶対値をとったり、2乗したりする。あるしきい値以下(あるいは以上)の 値には関心がない場合は、そのデータを消してしまう(書き出さない)こと もあり、また、(特に時系列の間隔を維持したい場合)しきい値で置きかえる こともある。

フィルター型の例文の、データを書き出す直前に、 何か変数変換を考えて組みこんでみてほしい。 それぞれのプログラム言語に常備されている関数があるので、それを 使うとよい。ここでていねいに説明するゆとりがないが、質問してほしい。

なお、C言語の場合、数学関数を使う場合は、プログラムに

#include <math.h>
を入れ(例文filter.cには入れてある)、 ccコマンドでプログラム名のあとに -lm を指定する。

対数について補足しておくと、 多くのプログラム言語で、「log」は自然対数(eを底とする対数)である。 (Pascalでは関数名は「ln」となっている。) 常用対数は、「log10」として用意されているか、または、 「log(x)/log(10)」のようにして計算して求める。


1996-05-20; 2000-04-18; 2002-07-11
増田 耕一 (MASUDA Kooiti)

[小目次へ] [フィルター型プログラムの教材全体の目次へ] [コンピュータ関係教材の目次へ]