度数分布の描画
Frequency Polygon
度数分布は、ヒストグラム(histogram)あるいは度数多角形(frequency polygon)として描かれる(G. A. Ferguson (1976). Statistical
analysis in psychology & education, fourth edition. McGraw-Hill Book
Company)。ここでは、度数多角形によって度数分布を表してみる(図1)。図1の度数多角形は、リスト1のプログラム(JavaFX)によって描いたものである。リスト1のプログラムでは、データは配列の初期値として設定されているが、データをファイルから読み込むプログラム例はこのウェブサイト後半で説明している。
図1
========================= リスト1(始まり) =======================
package
FreqPolygonS;
import
java.io.*;
import
javafx.application.Application;
import
javafx.scene.*;
import
javafx.scene.chart.*;
import
javafx.scene.layout.*;
import
javafx.stage.*;
public
class FreqPolygonS extends Application {
@Override
public
void start(Stage stage){
String
VarLabel = "点数"; // 変数名
double
data[] = { // データ格納用配列
66,
59, 62, 64, 63,
68,
65, 59, 68, 64,
65,
51, 67, 64, 83,
59,
61, 62, 57, 72,
65,
64, 54, 60, 53,
65,
67, 60, 53, 79,
74,
53, 61, 68, 75,
50,
57, 55, 66, 56,
55,
61, 70, 71, 49,
69,
70, 80, 73, 72
};
int
n = data.length;
int
n_intvl = 0;
double
x_value[] = {};
int
IntvlFreq[] = {};
try{
//
// キーボード入力をバッファー付きで設定
//
BufferedReader
cin = new BufferedReader( new InputStreamReader(System.in) );
double
minX = data[0], maxX = data[0];
for
(int i = 0; i < n; i++){
//
// データの確認用出力
//
System.out.println("data["
+ (i + 1) + "] = " + data[i]);
//
// 最小値、最大値を求める
//
if
(minX > data[i]) minX = data[i];
if
(maxX < data[i]) maxX = data[i];
}
System.out.println("\nmin
= " + minX + "
max = " + maxX);
double
LB; // 度数分布の左端の値
double
UB; // 度数分布の右端の値
while(true){
System.out.print("Lower
Boundary Value = ");
LB
= Double.parseDouble(cin.readLine());
System.out.print("Upper
Boundary Value = ");
UB
= Double.parseDouble(cin.readLine());
if
((LB < minX) && (UB >
maxX)) break;
System.out.println("LB
or UB is not valid.");
}
System.out.print("Number
of intervals = ");
n_intvl
= Integer.parseInt(cin.readLine()); // 度数分布の階級数
double
w_intvl = (UB - LB) / n_intvl; // 階級の区間の幅
n_intvl
+= 2; // 両端に度数0の区間を追加
x_value
= new double[n_intvl];
for
(int i = 0; i < n_intvl; i++){
x_value[i]
= LB - (0.5 * w_intvl) + i * w_intvl; // 階級の区間の中央値
System.out.println("x_value["
+ i + "] = " + x_value[i]);
}
double
cx_value[] = new double[n_intvl - 1];
for
(int i = 0; i < n_intvl - 1; i++){
cx_value[i]
= x_value[i] + 0.5 * w_intvl; // 階級の境界値
System.out.println("cx_value["
+ i + "] = " + cx_value[i]);
}
//
// 階級の度数を数える
//
IntvlFreq
= new int[n_intvl];
for
(int i = 0; i < n_intvl; i++)
IntvlFreq[i]
= 0;
for
(int i = 0; i < n; i++){
int
id = 0;
while((data[i]
> cx_value[id]) && (id < (n_intvl - 1))) id++;
if
(data[i] > cx_value[id]) id++;
IntvlFreq[id]++; // データの所属階級の度数を1増やす
}
System.out.println();
int
sum = 0;
for
(int i = 0; i < n_intvl; i++){
System.out.println("IntvlFreq["
+ i + "] = " + IntvlFreq[i]);
sum
+= IntvlFreq[i];
}
System.out.println("sum
= " + sum);
}
catch(Exception
e){
System.out.println("Exception..."
+ e);
System.exit(1);
}
/*---------------------------------------------------
度数多角形の描画
-----------------------------------------------------*/
stage.setTitle("度数多角形");
NumberAxis
x_axis = new NumberAxis();
x_axis.setLabel("階級の中央値");
NumberAxis
y_axis = new NumberAxis();
y_axis.setLabel("度 数");
XYChart.Series<Number,
Number> xy_series = new XYChart.Series<>();
xy_series.setName("階級の度数");
for
(int i = 0; i < n_intvl; i++)
xy_series.getData().add(new
XYChart.Data<>(x_value[i], IntvlFreq[i]));
LineChart<Number,
Number> freqPoly = new LineChart<>(x_axis, y_axis);
freqPoly.setTitle(VarLabel
+ "の度数多角形");
freqPoly.getData().add(xy_series);
HBox
root = new HBox();
root.getChildren().add(freqPoly);
stage.setScene(
new Scene(root, 800, 600) );
stage.show();
}
}
========================= リスト1(終わり) =======================
データは、プログラム内において配列 data の初期値として設定している。他のデータを分析するときは、この初期値の部分とデータの変数名
String VarLabel = "点数"; // 変数名。
の部分を書き換えればよい。
キーボードからの入力を行うため、ストリーム cin を次のように用意している。
BufferedReader cin = new BufferedReader( new InputStreamReader(System.in) );
ストリームcinの使用の範囲は try文で囲んで例外に対処できるようにしている。
プログラムを実行すると、データの最大値と最小値が表示され、階級の左端の値と右端の値、および階級の数の入力が求められる(図2)。
図2
階級の左端と右端、および階級の数の入力が終わると計算が始まり、図3のように階級の中央値、階級の境界値、階級内の度数が表示される。
図3
続いて度数の総数 sum が表示され(図4)、しばらくして図1の度数多角形が表示される。
図4
リスト1のプログラムは圧縮ファイルFreqPolygonS.zipに用意した。圧縮ファイル名FreqPolygonS.zipをクリック・ダウンロード・展開すればエディタで開くことが出来る。なお、プログラムはWindows上で開発されたものなので、ubuntuなどで利用するときは日本語コードの変換が必要である。日本語コードの変換については、別のウェブサイトで説明している。
リスト1のプログラムにおいてデータの入力をファイルからの読み込みに変えたものがリスト2のプログラムである。
========================= リスト2(始まり) ========================
package FreqPolygon;
import java.io.*;
import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.chart.*;
import javafx.scene.layout.*;
import javafx.stage.*;
public class FreqPolygon extends
Application {
@Override
public
void start(Stage stage){
int
n_intvl = 0;
double
x_value[] = {};
int
IntvlFreq[] = {};
String
VarLabel = "";
try{
//
// キーボード入力をバッファー付きで設定
//
BufferedReader
cin = new BufferedReader( new InputStreamReader(System.in) );
System.out.print("Input
Data File = ");
//
// 入力データファイルの設定
//
String
fin_nm = cin.readLine();
BufferedReader
fin = new BufferedReader( new FileReader(fin_nm));
//
// 出力用テキストファイルの設定
//
System.out.print("Output
File Name = ");
String
fout_nm = cin.readLine();
PrintWriter
fout = new PrintWriter( new BufferedWriter( new FileWriter(fout_nm)));
double
data[] = new double[10000]; // データ格納用配列
int
n = 0;
while(true){
String
s = fin.readLine();
if(s.length()
> 0)
if
(s.charAt(0) == '/') // スラッシュ / で始まる行まで読み進む
break;
}
VarLabel
= fin.readLine(); // 変数名の読み込み
while(true){
String
s = fin.readLine();
if
(s.charAt(0) == '/') break; // スラッシュ / で始まる行で読み込みの終了
data[n++]
= Double.parseDouble(s);
}
fin.close(); // 入力ストリームを閉じる
double
minX = data[0], maxX = data[0];
fout.println("Data...");
for
(int i = 0; i < n; i++){
//
// 読み込んだデータの確認用出力
//
System.out.println("data["
+ (i + 1) + "] = " + data[i]);
fout.println("data["
+ (i + 1) + "] = " + data[i]);
//
// 最小値、最大値を求める
//
if
(minX > data[i]) minX = data[i];
if
(maxX < data[i]) maxX = data[i];
}
fout.println();
fout.println("min
= " + minX + "
max = " + maxX);
System.out.println("\nmin
= " + minX + "
max = " + maxX);
double
LB; // 度数分布の左端の値
double
UB; // 度数分布の右端の値
while(true){
System.out.print("Lower
Boundary Value = ");
LB
= Double.parseDouble(cin.readLine());
System.out.print("Upper
Boundary Value = ");
UB
= Double.parseDouble(cin.readLine());
if
((LB < minX) && (UB >
maxX)) break;
System.out.println("LB
or UB is not valid.");
}
System.out.print("Number
of intervals = ");
n_intvl
= Integer.parseInt(cin.readLine()); // 度数分布の階級数
double
w_intvl = (UB - LB) / n_intvl; // 階級の区間の幅
n_intvl
+= 2; // 両端に度数0の区間を追加
x_value
= new double[n_intvl];
for
(int i = 0; i < n_intvl; i++){
x_value[i]
= LB - (0.5 * w_intvl) + i * w_intvl; // 階級の区間の中央値
System.out.println("x_value["
+ i + "] = " + x_value[i]);
}
double
cx_value[] = new double[n_intvl - 1];
for
(int i = 0; i < n_intvl - 1; i++){
cx_value[i]
= x_value[i] + 0.5 * w_intvl; // 階級の境界値
System.out.println("cx_value["
+ i + "] = " + cx_value[i]);
}
//
// 階級の度数を数える
//
IntvlFreq
= new int[n_intvl];
for
(int i = 0; i < n_intvl; i++)
IntvlFreq[i]
= 0;
for
(int i = 0; i < n; i++){
int
id = 0;
while((data[i]
> cx_value[id]) && (id < (n_intvl - 1))) id++;
if
(data[i] > cx_value[id]) id++;
IntvlFreq[id]++; // データの所属階級の度数を1増やす
}
System.out.println();
fout.println();
int
sum = 0;
for
(int i = 0; i < n_intvl; i++){
System.out.println("IntvlFreq["
+ i + "] = " + IntvlFreq[i]);
fout.println("IntvlFreq["
+ i + "] = " + IntvlFreq[i]);
sum
+= IntvlFreq[i];
}
System.out.println("sum
= " + sum);
fout.close(); // 出力ファイルを閉じる(バッファー内の書き出し)
}
catch(Exception
e){
System.out.println("Exception..."
+ e);
System.exit(1);
}
/*---------------------------------------------------
度数多角形の描画
-----------------------------------------------------*/
stage.setTitle("度数多角形");
NumberAxis
x_axis = new NumberAxis();
x_axis.setLabel("階級の中央値");
NumberAxis
y_axis = new NumberAxis();
y_axis.setLabel("度 数");
XYChart.Series<Number,
Number> xy_series = new XYChart.Series<>();
xy_series.setName("階級の度数");
for
(int i = 0; i < n_intvl; i++)
xy_series.getData().add(new
XYChart.Data<>(x_value[i], IntvlFreq[i]));
LineChart<Number,
Number> freqPoly = new LineChart<>(x_axis, y_axis);
freqPoly.setTitle(VarLabel
+ "の度数多角形");
freqPoly.getData().add(xy_series);
HBox
root = new HBox();
root.getChildren().add(freqPoly);
stage.setScene(
new Scene(root, 800, 600) );
stage.show();
}
}
========================= リスト2(終わり) ========================
プログラムでは、まず、キーボードからの入力を行うストリーム cin を次のように用意している。
BufferedReader cin = new BufferedReader( new InputStreamReader(System.in) );
System.out.print("Input Data File = ");
続いてファイルからの読み込み用ストリーム fin を次のコードで設定している。
String fin_nm = cin.readLine();
BufferedReader fin = new BufferedReader( new FileReader(fin_nm));
入力用データファイルは、図5に示す形式で用意する。
図5
先頭がスラッシュ / で始まる行(図5の2行目)の次の行に、変数名を書く。その次の行(44行目)から、一行に1つずつデータを書いていき、最後のデータの行(53行目)の次の行はスラッシュ / で始めてデータの終わりであることを示す。
計算結果などをファイルに出力する出力用ストリームは次のコードで行っている。
System.out.print("Output File Name = ");
String fout_nm = cin.readLine();
PrintWriter fout = new PrintWriter( new BufferedWriter( new FileWriter(fout_nm)));
データの読み込み後、最小値と最大値が求められ、度数を数える階級の左端の値 LB と右端の値 UB の設定が求められる。左端の値は最小値より小さいもの、右端の値は最大値より大きいものであるように設定する。
次に、階級の数の設定が求められる。設定された値に対して、階級の幅が算出され、その後、度数0の階級を分布の左右に加えるため、左右に1つずつ階級が追加されて、実際の階級数は設定した数より2個多くなる。度数0の階級が左右に付けられているので、折れ線を描くと左右の端が横軸と接し、折れ線と横軸とで多角形が描かれることになる。
図6のようにプログラムを実行すると、まず、図7のように入力データファイル名と出力用テキストファイル名の入力が求められる。
図6
図7
ファイル名の設定後、データが読み込まれ、最大値と最小値が表示され、階級の左端の値と右端の値、および階級の数の入力が求められる(図8)。
図8
階級の左端と右端、および階級の数の入力が終わると計算が始まり、図9のように階級の中央値、階級の境界値、階級内の度数が表示される。
図9
度数の総数 sum の表示後(図10)、しばらくして図1の度数多角形が表示される。
図10
リスト2のプログラムは圧縮ファイルFreqPolygon.zipに用意した。圧縮ファイル名FreqPolygon.zipをクリック・ダウンロード・展開すればエディタで開くことが出来る。なお、プログラムはWindows上で開発されたものなので、ubuntuなどで利用するときは日本語コードの変換が必要である。日本語コードの変換については、別のウェブサイトで説明している。