はじめMath! Javaでコンピュータ数学

第63回 統計の数学 相関係数とは[後編]

この記事を読むのに必要な時間:およそ 5.5 分

ソースコード:Sample Correlation.java

/*
 * filename : Sample_Correlation.java
 *            CVS ファイルを読み込んで,回帰直線の
 *            定数値を求め,グラフ化し,相関係数を求めます。
 * compile  : c:\>javac Sample_RegLine.java -Xlint:unchecked
 * usage    : c:\>java Sample_RegLine ./data002.csv
 */

import java.io.*;
import java.util.*;
import java.awt.*;


public class Sample_Correlation extends Frame {

  //データファイル名
  static String DATAFILE = "./data001.csv";

  //表示するウインドウの最大・最小座標値
  static int SCREEN_MAX_X = 300;
  static int SCREEN_MIN_X = 0;
  static int SCREEN_MAX_Y = 300;
  static int SCREEN_MIN_Y = 0;
  //
  //グラフ中にデータをプロットする丸のサイズ
  static int DATA_PLOT_OVAL_WIDTH = 4;
  static int DATA_PLOT_OVAL_HEIGHT = 4;
  //図形を描画するオブジェクト
  MyCanvas mc;
  /**
   * メインメソッド
   * 簡略のためすべてここから呼び出し
   */
  public static void main (String args []) {

    //線形最小二乗法によって得られた定数
    //y=ax+b のa,b を格納する。val[0]=a,val[1]=b
    //基本型は参照渡しできないのでこうしてみる
    double val[] = new double[2];
    //ファイルから読み込んだデータの一時保管用
    Vector v1 = new Vector();
    //ファイルから読み込んだ元データは文字列であるため,
    //数値に変換したものをこちらに格納する。
    Vector DataA = new Vector();


    //データファイル読み込み
    try{
      if (args.length ==0 ){
        readTextFromFile_AndSetVector(DATAFILE,v1);
      } else {
        readTextFromFile_AndSetVector(args[0],v1);
      }
    }
    catch(Exception e){
      System.out.println(e.toString());
      System.exit(-1);
    }// of try catch

    //一時読み込みしたデータは文字列なので数値に変換し,
    //配列にセットする
      KataHenkan(v1,DataA);
    //得られた数値データに最小二乗法を適用し,近似
    //直線の定数を得る
      SenkeiSaishouJijyouHou(DataA,val);

    //相関係数を求める
      double r = getCorrelationCoefficient(DataA);

    //標準出力に計算結果を出力
    System.out.println("y=ax+b");
    System.out.println("value of a = "+val[0]);
    System.out.println("value of b = "+val[1]);
    //相関係数を出力
    System.out.println("r = " + r);

    //ウインドウを作成し,結果を表示
    new Sample_Correlation(DataA,val).show();

  }// end of main()


  /*
   * 目的  : 相関係数を求める
   * 引数  : data 数値データの配列への参照
   * 戻り値 : 相関計数値
   */
  static double getCorrelationCoefficient(Vector data){
    //相関係数を求めるために用意する一時的な変数
    double XAve = 0; //観測値のx 成分の平均値
    double YAve = 0; //観測値のy 成分の平均値
    double XVari = 0; //x の分散
    double YVari = 0; //y の分散
    double XYVari = 0; //xy の共分散

    XAve = getAverage(data,"x");
    YAve = getAverage(data,"y");

    XVari = getVariance(data,"x",XAve,YAve);
    YVari = getVariance(data,"y",XAve,YAve);
    XYVari = getVariance(data,"xy",XAve,YAve);

    return XYVari / (Math.sqrt(XVari * YVari));
  }// end of getCorrelationCoefficient


  /*
   * 目的   : 分散や共分散を計算する
   * 引数   : data 数値データの配列への参照
   *       : axis "x" or "y" or "xy"
   *       : xave x の平均値
   *       : yave y の平均値
   * 戻り値 : 分散(または共分散)
   */
  static double getVariance(Vector data,String axis,
                            double xave,double yave){
    double xvari = 0;
    double yvari = 0;
    double xyvari = 0;

    double tempvalX = 0;
    double tempvalY = 0;
    double tempvalXY = 0;
    double x,y;
    Point temppos;
    for (int i=0; i<data.size(); ++i){
      temppos = (Point)data.get(i);
      x = (double) temppos.getX();
      tempvalX += Math.pow(x - xave,2);
      y = (double) temppos.getY();
      tempvalY += Math.pow(y - yave,2);
      tempvalXY += (x - xave) * (y - yave);
    }
    if (axis =="x") {
      return tempvalX / data.size();
    } else if (axis =="y") {
      return tempvalY / data.size();
    } else if (axis =="xy") {
      return tempvalXY / data.size();
    } else {
      return Double.NaN;
    }
  }// end of getVariance


  /*
   * 目的   : x 座標かy 座標かのデータの平均値を計算する
   * 引数   : data 数値データの配列への参照
   *       : axis "x" or "y"
   * 戻り値 : 平均値
   */
  static double getAverage(Vector data,String axis){
    double tempvalX = 0;
    double tempvalY = 0;
    double x,y;
    Point temppos;
    for (int i=0; i<data.size(); ++i){
      temppos = (Point)data.get(i);
      x = (double) temppos.getX();
      tempvalX += x;
      y = (double) temppos.getY();
      tempvalY += y;
    }
    if (axis =="x") {
      return tempvalX / data.size();
    } else if (axis =="y") {
      return tempvalY / data.size();
    } else {
      return Double.NaN;
    }
  }// end of getAverage


  /*
   * 目的 : 線形最小二乗法を実行
   * 引数 : data 数値データの配列への参照
   *       val[] 戻り値用 回帰直線の定数値
   */
  static void SenkeiSaishouJijyouHou(Vector Data,
                                     double val[]){
    double x,y,x_sum=0,y_sum=0, xx_sum=0,xy_sum=0;
    int n;
    Point temp;
    n = Data.size();
    for(int i=0;i<Data.size();i++){
      temp = (Point)Data.get(i);
      x=(double)temp.getX();
      y=(double)temp.getY();
      x_sum+=x;
      y_sum+=y;
      xx_sum+=x*x;
      xy_sum+=x*y;
    }// of for i
    val[0]= (double) (n*xy_sum-x_sum*y_sum)
           / (double) (n*xx_sum-x_sum*x_sum);
    val[1]= (double) (xx_sum*y_sum - xy_sum * x_sum)
           / (double) (n*xx_sum-x_sum*x_sum);
  }// end of SenkeiSaishouJijyouHou


  /*
   * 目的 : CSV の数値データを数値型に型変換
   * 引数 : v 文字列のデータを格納したVector
   *       Data 変換後のデータを格納したVector
   */
  static void KataHenkan(Vector v, Vector Data){
    for(int i=0; i <= (v.size()-1); i++){
      String str = (String)v.get(i);
      StringTokenizer st
                 = new StringTokenizer(str, ",");
      Point pos =
        new Point(
          Integer.parseInt((String)st.nextToken()),
          Integer.parseInt((String)st.nextToken())
        );
      Data.add(pos);
    }// of for i
  }// end of static void KataHenkan


  /*
   * 目的 : CSV ファイルから1 行ずつデータを読み込む
   * 引数 : filename データファイルのファイル名
   *       v データファイルから読み込んだデータ
   */
  static void readTextFromFile_AndSetVector
                      (String filename,Vector v) {
    try {
      FileReader fr = new FileReader(filename);
      BufferedReader br = new BufferedReader(fr);
      String rdata;
      String alldata = "";
      while((rdata = br.readLine()) != null) {
        v.add(rdata);
      }// of while
      fr.close();
    }catch(Exception e){
      System.out.println(e);
    }// of try catch
  }// of readTextFromFile_AndSetVector


  /*
   * 目的 : コンストラクタ
   * 引数 : Data プロットするデータ
   *       val 回帰直線の定数
   */
  public Sample_Correlation
                     (Vector Data,double val[]) {
    super();
    setTitle("最小二乗法のグラフをプロットする");
    setSize(SCREEN_MAX_X-SCREEN_MIN_X,
            SCREEN_MAX_Y-SCREEN_MIN_Y);
    setLayout(null);

    mc = new MyCanvas();
    mc.setBounds(SCREEN_MIN_X,SCREEN_MIN_Y, //左上隅の座標値
                 SCREEN_MAX_X,SCREEN_MAX_Y);//Width とHeight

    mc.setData(Data);
    mc.setVals(val);
    this.add(mc);
  }// end of Sample_RegLine(コンストラクタ)


  /**
   *目的 : 描画関係をまとめた。
   */
  class MyCanvas extends Canvas {

    Vector plotData;
    double val[];

    //ウインドウが再描画されるときにデータと
    //直線を再描画
    public void paint(Graphics g) {
      plotPoints();
      plotLine();
    }// end of paint

    //描画するデータへの参照を受け取る
    public void setData(Vector Data){
      plotData = Data;
    }//end of setData

    //描画する近似直線式の定数を受け取る
    public void setVals(double vals[]){
      val = vals;
    }// end of setVals

    //近似直線を描画する
    void plotLine(){
      Graphics g=getGraphics();
      g.setColor(Color.blue);
      g.drawLine(0,-(int)(val[1]) + SCREEN_MAX_Y,
        SCREEN_MAX_X,
        - (int)(SCREEN_MAX_X*val[0]+val[1]) + SCREEN_MAX_Y);
    }// end of plotLine

    //データを画面に点をプロットする。
    void plotPoints(){
      Graphics g=getGraphics();
      g.setColor(Color.red);

      Point temp = new Point();
      for (int i=0; i< plotData.size() ; i++){
        temp = (Point)plotData.get(i);
        g.drawOval((int)temp.getX(),
                   -(int)temp.getY() + SCREEN_MAX_Y,
                   DATA_PLOT_OVAL_WIDTH,
                   DATA_PLOT_OVAL_HEIGHT);
      }//end of for i
    }//end of plotPoints

  }// end of class MyCanvas


}// end of class Sample_Correlation

著者プロフィール

平田敦(ひらたあつし)

地方都市の公立工業高等学校教諭。趣味はプログラミングと日本の端っこ踏破旅行。2010年のLotYはRuby。結城浩氏のような仕事をしたいと妄想する30代後半♂。