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

第61回 統計の数学 回帰直線[後編]

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

それでは,以下に未完成のソースコードを示します。83行目からにコードを追加しましょう※4⁠。コードの足りないのは,最小二乗法を実行して回帰直線の定数を求めるメソッドです。

ソースコード:Sample RegLine.java未完成版

001: /*
002:  * filename : Sample_RegLine.java
003:  * CVSファイルを読み込んで,回帰直線の
004:  * 定数値を求め,グラフ化します。
005:  * compile : c:\>javac Sample_RegLine.java -Xlint:unchecked
006:  * usage : c:\>java Sample_RegLine
007:  */
008: 
009: import java.io.*;
010: import java.util.*;
011: import java.lang.*;
012: import java.awt.*;
013: 
014: 
015: public class Sample_RegLine extends Frame {
016: 
017:   //データファイル名
018:   static String DATAFILE = "./data001.csv";
019: 
020:   //表示するウインドウの最大・最小座標値
021:   static int SCREEN_MAX_X = 300;
022:   static int SCREEN_MIN_X = 0;
023:   static int SCREEN_MAX_Y = 300;
024:   static int SCREEN_MIN_Y = 0;
025:   //
026:   //グラフ中にデータをプロットする丸のサイズ
027:   static int DATA_PLOT_OVAL_WIDTH = 4;
028:   static int DATA_PLOT_OVAL_HEIGHT = 4;
029:   //図形を描画するオブジェクト
030:   MyCanvas mc;
031: 
032:   /**
033:    * メインメソッド
034:    * 簡略のためすべてここから呼び出し
035:    */
036:   public static void main (String args []) {
037: 
038:     //線形最小二乗法によって得られた定数
039:     //y=ax+b のa,b を格納する。val[0]=a,val[1]=b
040:     //基本型は参照渡しできないのでこうしてみる
041:     double val[] = new double[2];
042:     //ファイルから読み込んだデータの一時保管用
043:     Vector v1 = new Vector();
044:     //ファイルから読み込んだ元データは文字列であるため,
045:     //数値に変換したものをこちらに格納する。
046:     Vector DataA = new Vector();
047: 
048:     //データファイル読み込み
049:     try{
050:       readTextFromFile_AndSetVector(DATAFILE,v1);
051:     }
052:     catch(Exception e){
053:       System.out.println(e.toString());
054:       System.exit(-1);
055:     }// of try catch
056: 
057:     //一時読み込みしたデータは文字列なので数値に変換し,
058:     //配列にセットする
059:     KataHenkan(v1,DataA);
060:     //得られた数値データに最小二乗法を適用し,近似
061:     //直線の定数を得る
062:       SenkeiSaishouJijyouHou(DataA,val);
063: 
064:     System.out.println("y=ax+b");
065:     System.out.println("value of a = "+val[0]);
066:     System.out.println("value of b = "+val[1]);
067: 
068:     //ウインドウを作成し,結果を表示
069:     new Sample_RegLine(DataA,val).show();
070: 
071:   }// end of main()
072: 
073: 
074: 
075:   /*
076:    * 目的: 線形最小二乗法を実行
077:    * 引数: data 数値データの配列への参照
078:    *      val[] 戻り値用 回帰直線の定数値
079:    */
080:   static void SenkeiSaishouJijyouHou(Vector Data,
081:                                      double val[]){
082: 
083:   //----ここから
084: 
085:   //----ここまで
086: 
087:   }// end of SenkeiSaishouJijyouHou
088: 
089:   /*
090:    * 目的: CSVの数値データを数値型に型変換
091:    * 引数: v文字列のデータを格納したVector
092:    *      Data変換後のデータを格納したVector
093:    */
094:   static void KataHenkan(Vector v, Vector Data){
095:     for(int i=0; i <= (v.size()-1); i++){
096:       String str = (String)v.get(i);
097:       StringTokenizer st
098:               = new StringTokenizer(str, ",");
099:       Point pos =
100:         new Point(
101:           Integer.parseInt((String)st.nextToken()),
102:           Integer.parseInt((String)st.nextToken())
103:         );
104:       Data.add(pos);
105:     }// of for i
106:   }// end of static void KataHenkan
107: 
108: 
109: 
110:   /*
111:    * 目的: CSVファイルから1行ずつデータを読み込む
112:    * 引数: filenameデータファイルのファイル名
113:    *     vデータファイルから読み込んだデータ
114:    */
115:   static void readTextFromFile_AndSetVector
116:                       (String filename,Vector v) {
117:     try {
118:       FileReader fr = new FileReader(filename);
119:       BufferedReader br = new BufferedReader(fr);
120:       String rdata;
121:       String alldata = "";
122:       while((rdata = br.readLine()) != null) {
123:         v.add(rdata);
124:       }// of while
125:       fr.close();
126:     }catch(Exception e){
127:       System.out.println(e);
128:     }// of try catch
129:   }// of readTextFromFile_AndSetVector
130: 
131:   /*
132:    * 目的: コンストラクタ
133:    * 引数: Dataプロットするデータ
134:    *       val回帰直線の定数
135:    */
136:   public Sample_RegLine
137:                      (Vector Data,double val[]) {
138:     super();
139:     setTitle("最小二乗法のグラフをプロットする");
140:     setSize(SCREEN_MAX_X-SCREEN_MIN_X,
141:             SCREEN_MAX_Y-SCREEN_MIN_Y);
142:     setLayout(null);
143: 
144:     mc = new MyCanvas();
145:     mc.setBounds(SCREEN_MIN_X,SCREEN_MIN_Y, //左上隅の座標値
146:                  SCREEN_MAX_X,SCREEN_MAX_Y);//Width とHeight
147: 
148:     mc.setData(Data);
149:     mc.setVals(val);
150:     this.add(mc);
151:   }// end of Sample_RegLine(コンストラクタ)
152: 
153:   /**
154:    *目的: 描画関係をまとめた。
155:    */
156:   class MyCanvas extends Canvas {
157: 
158:     Vector plotData;
159:     double val[];
160: 
161:     //ウインドウが再描画されるときにデータと
162:     //直線を再描画
163:     public void paint(Graphics g) {
164:       plotPoints();
165:       plotLine();
166:     }// end of paint
167: 
168:     //描画するデータへの参照を受け取る
169:     public void setData(Vector Data){
170:       plotData = Data;
171:     }//end of setData
172: 
173:    //描画する近似直線式の定数を受け取る
174:     public void setVals(double vals[]){
175:       val = vals;
176:     }// end of setVals
177: 
178:     //近似直線を描画する
179:     void plotLine(){
180:       Graphics g=getGraphics();
181:       g.setColor(Color.blue);
182:       g.drawLine(0,-(int)(val[1]) + SCREEN_MAX_Y,
183:         SCREEN_MAX_X,
184:         - (int)(SCREEN_MAX_X*val[0]+val[1]) + SCREEN_MAX_Y);
185:     }// end of plotLine
186: 
187:     //データを画面に点をプロットする。
188:     void plotPoints(){
189:       Graphics g=getGraphics();
190:       g.setColor(Color.red);
191: 
192:       Point temp = new Point();
193:       for (int i=0; i< plotData.size() ; i++){
194:         temp = (Point)plotData.get(i);
195:         g.drawOval((int)temp.getX(),
196:                   -(int)temp.getY() + SCREEN_MAX_Y,
197:                   DATA_PLOT_OVAL_WIDTH,
198:                   DATA_PLOT_OVAL_HEIGHT);
199:       }// of for i
200:     }//end fo plotPoints()
201:   }// end of MyCanvas
202: }// end of this file...
※4)
このサンプルコードは,数年前に別の目的で作成したものを利用しました。そのため,現在は使用が推奨されないVectorクラスを利用しています。ArrayListを用いて書き直すことも考えたのですが,Javaでプログラミングをする人たちにとって,過去のコードに出会い,利用していく機会が少なくはないのでは?と思い,あえてほぼそのままの形で掲載しました。もし,時間が許せば,ArrayListを使って,またほかの部分もあたらしいJavaの規格に沿って書き直してみるのが良い練習となるでしょう。

解説

問題 最小二乗法を用いて,回帰直線の定数を求め,データにフィットするグラフを描きましょう。

コードの不足していた部分を示します。いかがでしょう。前回までに学んだややこしい数式の羅列も,コードにしてしまうと,こんなにシンプルです。結局,肝心な部分はこれだけなのです。

ソースコード:Sample RegLine.java完成版

01:   /*
02:    * 目的: 線形最小二乗法を実行
03:    * 引数: data数値データの配列への参照
04:    *      val[] 戻り値用 回帰直線の定数値
05:    */
06:   static void SenkeiSaishouJijyouHou(Vector Data,
07:                                      double val[]){
08:     double x,y,x_sum=0,y_sum=0, xx_sum=0,xy_sum=0;
09:     int n;
10:     Point temp;
11:     n = Data.size();
12:     for(int i=0;i<Data.size();i++){
13:       temp = (Point)Data.get(i);
14:       x=(double)temp.getX();
15:       y=(double)temp.getY();
16:       x_sum+=x;
17:       y_sum+=y;
18:       xx_sum+=x*x;
19:       xy_sum+=x*y;
20:     }// of for i
21:     val[0]= (double) (n*xy_sum-x_sum*y_sum)
22:             / (double) (n*xx_sum-x_sum*x_sum);
23:     val[1]= (double) (xx_sum*y_sum - xy_sum * x_sum)
24:             / (double) (n*xx_sum-x_sum*x_sum);
25:   }// end of SenkeiSaishouJijyouHou

今回はここまで

Java言語を使って回帰直線を得る実習は,首尾よくこなせたでしょうか。表示されたグラフは,Officeソフトで表示したグラフと一致したでしょうか。回帰直線の定数は正しかったでしょうか。Officeソフトで得た定数よりも,精度の高い値が得られたはずです。プログラミング言語を利用する価値は,ここにあります。市販のソフトウエアにできないところに「手を届かせる」ことができるのです。

お疲れ様でした。長いコードを打ち込むことは大変だったことでしょう。

今回のまとめ

  • 最小二乗法をつかって回帰直線の定数とグラフを得るプログラムを作りました。

著者プロフィール

平田敦(ひらたあつし)

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