2008. 3.11.
石立 喬

Visual C++ 2008 Express Edition の易しい数値計算(4)

――― ファイルからデータを読み込んで統計処理をする ―――


 コンマ区切りのデータをテキストファイルとして格納しておき、これを読み込んで処理をする例を示す。
すでに紹介した文字列の区切り方法と数字(文字列)の数値への変換を使用し、得られた数値を基に、標準偏差と相関係数を求める。結果は、表計算ソフトExcelでの結果と比較する。

新たに使用した統計量
◎不偏分散(unbiased variance)と不偏標準偏差(unbiased standard deviation)
 「易しい数値計算(3)」では、分散(σ2)の計算に下記を使用し、標準偏差(σ)は、その平方根であった。ただし、Nは入力したデータの個数であり、xの上にバーが付いたものはxiの平均を意味する。

    

 これ自身、誤りではないが、より多くのデータ(母集団)からN個だけ抜き出されたサンプルデータの場合には、母集団のバラツキを推定するのに、Nの代わりにN-1で割った、より大きい値がが使用される。これによって求まる分散を「不偏分散」(データの個数に依存しない値の意)と呼ぶ。これに対して、Nで割ったものを「標本分散」(限られたサンプルについてのみの値の意)として区別する。同様に、「不偏標
準偏差」と「標本標準偏差」がある。ただし、Nが大きくなると、これらの違いは限りなくゼロになる。表計算ソフトのExcelでは、不偏標準偏差の方が使用されているので、Excelの結果との比較のために、ここではN-1で割る方を使用する。
 また、変数として、xやyを使用するので、これらのサフィックスを付加する。下記は、ここで使用する不偏分散の定義であり、数値計算には変形後の式を用いる。この値は負になることはなく、平方根をとって不偏標準偏差σxとすることができる。

  

◎不偏共分散(unbiased covariance)
 xとyとの共分散は、これらのデーが比例的に近いかどうかの目安になる値で、下記で定義される。変形後の式の導出については、各自で確認されたい。この値は、正負の両方をとることがある。次元としては二乗であるが、平方根をとることはないのでσ2のような形ではなく、Sを用いる。

  

◎相関係数(correlation coefficient)
 共分散だけでも、xのデータとyのデータの相対的な関係を知り得るが、それを規準化して−1から1までの間に収めたのが、下記の相関係数である。相関係数では、Nで割ったかN-1で割ったかは関係がなくなる。

    


データ用ファイルの準備
 ここで紹介するプログラムは、ファイルからデータを読み込んで処理をするので、あらかじめデータ用ファイルを作成しておく必要がある。図1は、ここで使用したファイルの内容を確認したもので、1行目にはxのデータが、2行目にはyのデータが、それぞれコンマ区切りで並んでいる。


図1 データ入力のために用意したファイルの内容


プログラム
 StreamReaderを使用するために、名前空間System::IOを指定しておく。
 xのデータを入れる配列x[]と、yのデータを入れる配列y[]は、それぞれ50個ずつ用意する(実際に使ったのは10個だったが)。
 StreamReaderクラスのsreader1にファイル名を与えても、そのファイルが存在しないときはエラーを表示するようにtry〜catch文を使用した。これにより、エラーの場合には、「ファイル’*******’が見つかりませんでした。」と表示される。
 ファイルから1行ごとの読み込みは、sreader1->ReadLine()で行う。念のため、読み込んだ結果を表示する。最初の行はxのデータであるので、それをコンマで分割しながら文字列配列sub_string[]に順次入れて行く。これをConvert::ToInt32()で数値に変換しながらxのデータの配列x[]に入れて行く。
 ファイルから読み込まれる次の行は、yのデータであるので、同様にy[]に入れて行く。
 あとは、標準偏差や相関係数の式にしたがって計算し、表示する。

#include "stdafx.h"

using namespace System;
using namespace System::IO;

int main()
{

   Console::Title="相関係数";

   array<int>^ x=gcnew array<int>(50);
   array<int>^ y=gcnew array<int>(50);
   double average_x,average_y;
   double stddev_x,stddev_y;
   double correl_xy;
   int count;
   int sum_x=0,sum_y=0,sum_xx=0,sum_yy=0,sum_xy=0;

   String^ file_name;
   String^ string1;
   StreamReader^ sreader1;
   array<String^>^ sub_string;

   Console::Write("読み込むファイル名を入力して下さい。");

   //指定したファイル名でStreamReaderを設定する
   file_name=Console::ReadLine();
   try{
      sreader1=gcnew StreamReader(file_name);
   }catch(Exception^ ex){
      Console::WriteLine(ex->Message);
      return 0;
   }

   //x[i]の読み込み
   string1=sreader1->ReadLine();   //StreamReaderに1行読み込む
   Console::WriteLine("Xのデータ:" + string1);
   sub_string=string1->Split(',');    //コンマで分割する
   count=sub_string->Length;     //データ個数を取得する
   for(int i=0;i<count;i++)
      x[i]=Convert::ToInt32(sub_string[i]);

   //y[i]の読み込み
   string1=sreader1->ReadLine();   //StreamReaderに1行読み込む
   Console::WriteLine("Yのデータ:" + string1);
   sub_string=string1->Split(',');    //コンマで分割する
   for(int i=0;i<count;i++)
      y[i]=Convert::ToInt32(sub_string[i]);

   //統計計算と表示
   for(int i=0;i<count;i++){
      sum_x+=x[i];
      sum_y+=y[i];
      sum_xx+=x[i]*x[i];
      sum_yy+=y[i]*y[i];
      sum_xy+=x[i]*y[i];
   }
   average_x=(double)sum_x/count;
   average_y=(double)sum_y/count;
   stddev_x=Math::Sqrt((double)sum_xx/(count-1)-(double)average_x*average_x*count/(count-1));
   stddev_y=Math::Sqrt((double)sum_yy/(count-1)-(double)average_y*average_y*count/(count-1));
   correl_xy=((double)sum_xy/(count-1)-(double)average_x*average_y*count/(count-1))/(stddev_x*stddev_y);
   Console::WriteLine();
   string1=String::Format("      Xの平均:{0,5:F2}",average_x);
   Console::WriteLine(string1);
   string1=String::Format("      Yの平均:{0,5:F2}",average_y);
   Console::WriteLine(string1);
   string1=String::Format("   Xの標準偏差:{0,10:F7}",stddev_x);
   Console::WriteLine(string1);
   string1=String::Format("   Yの標準偏差:{0,10:F7}",stddev_y);
   Console::WriteLine(string1);
   string1=String::Format(" XとYの相関係数:{0,10:F7}",correl_xy);
   Console::WriteLine(string1);

   Console::ReadLine();

   return 0;
}

得られた結果
 図3は、ファイルからデータを読み込み、数値計算して得られた結果である。


図2 得られた結果


表計算ソフトExcelによる結果との比較
 同様の計算を、表計算ソフトExcel 2003を使って行った結果を図3に示す。体裁を整えるために、「学籍番号」、「英語」、「数学」などと入れてみたが、これに特別な意味はない。
 「平均」は=AVERAGE(B2:B11)など、「標準偏差」は=STDEV(B2:B11)など、「相関係数」は=CORREL(B2:B11,C2:C11)を使用した。
 上記の計算結果と表計算ソフトの結果とは、良く一致している。これならば、何も「Visual C++ 2008」でやる必要はないではないか、との声が聞こえてきそうであるが、計算の仕組みの学習と、よりカスタマイズしたソフトの作成への参考になれば幸いである。



図3 Excelによる計算結果



「Visual C++ の勉強部屋」(目次)へ