情報基礎A 「Cプログラミング」(ステップ7・統計計算・ファイル入力と相関係数)
このページでは、Cを使った基本的な統計計算の方法について考える。
ファイルからデータを読み込んで配列にセットする
コンピュータに統計処理を行わせる場合、プログラムの中に数値データを書き込んでしまうよりは、 データを別ファイルに保存しておいて、プログラムはその内容を読んで、結果を出力する、といった流れのほうが何かと便利だ。 例えば、1万件のデータを処理しなければならない場合、エディタを使ってプログラムにそれを書き込むだけで、相当な手間になるはずだ。 ここでは、データファイルの形式として、データ1件分(1レコード)が1行ごとに、以下のように並んでいる場合を考えよう。
身長と体重データの例
ダウンロード
186 87 180 76 175 67 178 78 174 74 185 86
このデータが、height-vs-weight.txt
というファイル名で、Cプログラムと同じディレクトリ(フォルダ)に保存されていると想定する。
以下は、そこからデータを読み出して、値を配列にセットするプログラムの例である:
#include <stdio.h> #include <math.h> #define NMAX 1000 main() { FILE *fp ; int n=0 ; float x, y, x_vs_y[NMAX][2] ; fp=fopen("height-vs-weight.txt","r") ; if (fp==NULL) return ; for (;;) { if (fscanf(fp,"%f%f",&x,&y)==2 && n<NMAX) { printf("n= %d x= %f y= %f\n",n,x,y) ; x_vs_y[n][0]=x ; x_vs_y[n][1]=y ; n=n+1 ; } else break ; } fclose(fp) ; }
このプログラム中に登場した新しい記法や関数について、以下で説明する:
#define NMAX 1000
...
float x, y, x_vs_y[NMAX][2] ;
- あらかじめ読み込まれるデータの件数は分からないので、先頭部で
NMAX
という記号を1000と定義し、 配列x_vs_y
の要素数をNMAX
を使って設定している。 件数が多い場合は、1000のところを10000などに(その一箇所だけを)変更すればよい。 さらに、以降のプログラム中で、読み込まれたデータ件数がNMAX
を越えたら、データ読み込みを中止するようプログラムされている。
FILE *fp ;
...
fp=fopen("height-vs-weight.txt","r") ;
- ファイルにアクセスするための経路(ストリーム)を
fp
という名前で用意し、fopen()
関数によって、経路を「接続」する。fopen()
関数の最初の引数はファイル名である。二番目の"r"
は、データ読み込み用にその経路を設定することを意味する。 概念的には、ファイルにデータの吸い出し口と注入口があって、それらを区別して使うようなイメージである。 ただし、データを読み出して(吸い出して)も、ファイルのデータが消えることは無い。
if (fp==NULL) return ;
- ファイルが正常に開かれないと
fopen()
関数はNULL
を返すので、その場合はmain()
関数から 出て(returnして)、プログラムを終了する。
if (fscanf(fp,"%f%f",&x,&y)==2 && n<NMAX)
fscanf(fp,...)
は、fp
で関係づけられたファイルからデータをから読み込む以外は、既出のscanf()
と同じ動作をする。 繰り返しfscanf()
が呼ばれる度に、ファイルの先頭から順に、末尾に向かってデータが読み込まれる(今どこまで読み出したか、は、Cの処理系が自動的に管理してくれる)。fscanf()
関数から読み込んだデータの数が返されるので、上のプログラムでは、それが2件あったかどうかチェックしている。データが正しく2件ずつ読み込まれなかった(データファイルに間違いがあるか、あるいは、ファイルを最後まで読んで、これ以上読み出しができなくなった)場合は、if文のelse節が実行され、反復が終了する。
fclose(fp) ;
- データの読み出しが終わったら、ファイルを閉じておく。
データ間の関係性を調べる:相関係数
二つの量$X$と$Y$の間の関係性を特徴づける量として、相関係数(さらに詳しくは、ここで述べるのはピアソンの相関係数)が広く使われている。 $n$ 点のデータのペア $(x_i,y_i)$ (ただし、$n=0,1,\cdots,n-1$)が与えられたとして、それぞれの平均 $$ E(X) = \frac{1}{n} \sum_{i=0}^{n-1} x_i,\ \ \ E(Y) = \frac{1}{n} \sum_{i=0}^{n-1} y_i $$ および、二乗平均 $$ E(XX) = \frac{1}{n} \sum_{i=0}^{n-1} {x_i}^2,\ \ \ E(YY) = \frac{1}{n} \sum_{i=0}^{n-1} {y_i}^2 $$ を求める。これらを用いると、$X$と$Y$の分散は $$ V_X = E(XX) - E(X)^2 ,\ \ \ V_Y = E(YY) - E(Y)^2 , $$ となる。
同様に、$X$と$Y$の積の平均 $$ E(XY) = \frac{1}{n} \sum_{i=0}^{n-1} x_i y_i $$ から、共分散(covariance) $$ V_{XY} = E(XY) - E(X) E(Y) $$ が得られる。共分散を分散で規格化した量が相関係数で、 $$ r = \frac{V_{XY}}{\sqrt{V_X V_Y}} $$ で与えられる。相関係数は $-1 \le r \le +1$ の値を取る。
横軸と縦軸のスケールをデータのばらつきに合わせ調整した上で、$X$, $Y$を散布図で表現した際に、点が斜め上45度方向の直線に沿って分布すると$r$は1に近く、 右下がり45度方向の直線に沿って分布すると$r$は-1に近い値を取る。 反対に、$r$が0に近い場合は、$X$が増えても$Y$は同じようには(直線的には)増えない、という状況に対応しているが、 このことは、必ずしも$X$と$Y$の間に関係性がないことを意味するわけではない。
練習:相関係数
プロ野球球団の選手の身長(cm)、体重(kg)、年齢、推定年俸(万円)などのデータがプロ野球データFreakで公開されている。それを元に、2015年のある球団について、
身長(cm) 体重(kg) 年齢 推定年俸(万円)
の数値を並べたファイルをこちらに用意した。 このファイル(rakuten.txt)を読み込み、 W:身長 X:体重 Y:年齢 Z:年俸 としたとき、年俸との相関係数($R_{WZ}, R_{XZ}, R_{YZ}$)を計算するプログラムを作成せよ。
なお、年俸は、他の変量と較べて、選手(働き)によって桁が違ってくるような量である。
すなわち、金額そのものよりも、その「桁」のほうに注目するのが自然であろう。
そのような観点から、年俸については、log()
関数を使い、金額の対数を変量とした解析を行うこと。
年俸と最も相関が強い考えられるのは、身長か、体重か、年齢か?
ヒント
データを4件ずつ読み込むためには、このページで示したサンプルプログラムの中で
float x_vs_y[NMAX][2] → float wxyz[NMAX][4] fscanf(fp,"%f%f",&x,&y)==2 → fscanf(fp,"%f%f%f%f",&w,&x,&y,&z)==4
といった書き換えが必要となる。
解説: 疑似相関
$X$と$Y$の相関係数が大きいからと言って、$X$と$Y$の間に直接的な因果関係があるとは限らない。 たとえば、Spurious Correlationsのサイトには、 疑似相関の例がいくつも紹介されている。
例えば、第三の(隠れた)要因$Z$があって、$X, Y$が共に$Z$に影響されているような場合、$X$と$Y$の間に強い相関が見られる可能性がある。