【Java初心者】Wekaによる機械学習 08-主成分分析による次元削減

今回も、自分の試行錯誤の結果を記録しておくようなものです。
間違いがありましたらご指摘をお願いします。

次元圧縮と次元削減って厳密には意味が違うんですかねー。適当に「次元圧縮」と言っていましたが。

# 主成分分析とは

多くの変数を持つデータをから、主成分を作成する統計的分析手法です。前処理の中で使われますが、3次元以上のデータを2次元以下にして、グラフなどに可視化する際にも利用されます。

# 使用するデータと参考にしたページ

データ処理ならばPythonだろうと思いますが、これまでのシリーズでは、Javaによって実現することを目指しています。データや手法については、以下のページを参考にしました。大変貴重な情報をありがとうございます。

https://bellcurve.jp/statistics/blog/12184.html

公開されているエクセルデータをもとに、以下のCSVを作りました。(raw.csvという名前で保存しました)

```csv
選手,100m,400m,110m障害,1500m
Bryan Clay,989,865,984,522
Andrei Krauchanka,870,943,948,761
Leonel Suarez,883,913,955,750
Alexander Pogorelov,845,773,915,551
Romain Barras,804,837,948,749
Roman Sebrle,814,836,885,621
Oleksiy Kasyanov,968,924,927,752
Andre Niklaus,834,831,927,726
Maurice Smith,894,911,964,734
Michael Schrader,906,886,885,766
Mikk Pahapill,827,774,910,637
Aleksey Drozdov,856,744,789,672
Andres Raja,885,862,967,621
Eugene Martineau,819,815,882,693
Yordani Garcia,942,830,987,557
Mikalai Shubianok,793,814,908,692
Aliaksandr Parkhomenka,797,782,842,648
Haifeng Qi,827,843,899,685
Massimo Bertocchi,861,875,934,666
Jangy Addy,915,885,935,491
Daniel Awde,847,950,887,650
Hadi Sepehrzad,878,780,894,522
Damjan Sitar,814,810,846,697
Slaven Dizdarevic,825,724,777,610
David Gomez,834,849,897,740
Mikko Halvari,821,787,705,449
```

# 利用するソフト(ライブラリー)

自分はJavaアプリから利用するので、今回もmini-wekaを使います。https://mvnrepository.com/artifact/com.github.fracpete/mini-weka

# 目指すこと

* CSVをWekaで使用するためArffというファイル形式にする
* 最初の項目(Arff)は分析データではないので削除
* 主成分分析を行う。この際、累積寄与も計算し、いくつの主成分があれば分析が可能であるか調べる

# 実際のコード

データ(Instances)を、何度も上書きしています。本当は別の変数にしないといけないかもしれませんけどね。

```java
package example;

import java.io.File;
import weka.core.Instances;
import weka.core.converters.CSVLoader;
import weka.filters.Filter;
import weka.filters.unsupervised.attribute.PrincipalComponents;
import weka.filters.unsupervised.attribute.Remove;

public class PCATest {

    public static void main(String[] args) throws Exception {
        CSVLoader csv = new CSVLoader();
        csv.setSource(new File("raw.csv"));
        Instances data = csv.getDataSet();
        System.out.println("--------CSVから変換したInstances------------");
        System.out.println(data.toString());

        Remove remove = new Remove();
        remove.setAttributeIndices("1");
        remove.setInputFormat(data);
        data = Filter.useFilter(data, remove);
        System.out.println("");
        System.out.println("--------最初のAttrを削除------------");
        System.out.println(data.toString());

        PrincipalComponents pca = new PrincipalComponents();
        pca.setInputFormat(data);

        Instances pcaData = Filter.useFilter(data, pca);

        double tvariance = 0.0; // 寄与率の合計

        Double[] tname = new Double[pcaData.numAttributes()]; // 累積寄与率
        System.out.println("");
        System.out.println("--------主成分分析開始---------------");
        System.out.println("---------------ATTR-------------\t\t-----累積寄与率---------");
        // 主成分分析結果の表示
        for (int i = 0; i < pcaData.numAttributes(); i++) {
            double variance = pcaData.variance(i);
            tvariance += variance;
            tname[i] = tvariance;
        }

        for (int i = 0; i < pcaData.numAttributes(); i++) {
            System.out.println(pcaData.attribute(i).name() + "\t" + (tname[i] / tvariance * 100));
        }
        System.out.println("");
        System.out.println("--------主成分分析結果---------------");
        System.out.println(pcaData.toString());
    }
}
```

# 出力結果

```
--------CSVから変換したInstances------------
@relation raw

@attribute 選手 {'Bryan Clay','Andrei Krauchanka','Leonel Suarez','Alexander Pogorelov','Romain Barras','Roman Sebrle','Oleksiy Kasyanov','Andre Niklaus','Maurice Smith','Michael Schrader','Mikk Pahapill','Aleksey Drozdov','Andres Raja','Eugene Martineau','Yordani Garcia','Mikalai Shubianok','Aliaksandr Parkhomenka','Haifeng Qi','Massimo Bertocchi','Jangy Addy','Daniel Awde','Hadi Sepehrzad','Damjan Sitar','Slaven Dizdarevic','David Gomez','Mikko Halvari'}
@attribute 100m numeric
@attribute 400m numeric
@attribute 110m障害 numeric
@attribute 1500m numeric

@data
'Bryan Clay',989,865,984,522
'Andrei Krauchanka',870,943,948,761
'Leonel Suarez',883,913,955,750
'Alexander Pogorelov',845,773,915,551
'Romain Barras',804,837,948,749
'Roman Sebrle',814,836,885,621
'Oleksiy Kasyanov',968,924,927,752
'Andre Niklaus',834,831,927,726
'Maurice Smith',894,911,964,734
'Michael Schrader',906,886,885,766
'Mikk Pahapill',827,774,910,637
'Aleksey Drozdov',856,744,789,672
'Andres Raja',885,862,967,621
'Eugene Martineau',819,815,882,693
'Yordani Garcia',942,830,987,557
'Mikalai Shubianok',793,814,908,692
'Aliaksandr Parkhomenka',797,782,842,648
'Haifeng Qi',827,843,899,685
'Massimo Bertocchi',861,875,934,666
'Jangy Addy',915,885,935,491
'Daniel Awde',847,950,887,650
'Hadi Sepehrzad',878,780,894,522
'Damjan Sitar',814,810,846,697
'Slaven Dizdarevic',825,724,777,610
'David Gomez',834,849,897,740
'Mikko Halvari',821,787,705,449

--------最初のAttrを削除------------
@relation raw-weka.filters.unsupervised.attribute.Remove-R1

@attribute 100m numeric
@attribute 400m numeric
@attribute 110m障害 numeric
@attribute 1500m numeric

@data
989,865,984,522
870,943,948,761
883,913,955,750
845,773,915,551
804,837,948,749
814,836,885,621
968,924,927,752
834,831,927,726
894,911,964,734
906,886,885,766
827,774,910,637
856,744,789,672
885,862,967,621
819,815,882,693
942,830,987,557
793,814,908,692
797,782,842,648
827,843,899,685
861,875,934,666
915,885,935,491
847,950,887,650
878,780,894,522
814,810,846,697
825,724,777,610
834,849,897,740
821,787,705,449

--------主成分分析開始---------------
---------------ATTR-------------		-----累積寄与率---------
0.599400m+0.584110m障害+0.477100m+0.27 1500m	53.27105199525689
0.8121500m-0.565100m+0.137400m-0.055110m障害	82.27876052967731
-0.802110m障害+0.511400m+0.302100m+0.07 1500m	92.60913606468674
-0.602100m+0.602400m-0.5131500m+0.112110m障害	100.0

--------主成分分析結果---------------
@relation 'raw-weka.filters.unsupervised.attribute.Remove-R1_principal components-weka.filters.unsupervised.attribute.PrincipalComponents-R0.95-A5-M-1'

@attribute '0.599400m+0.584110m障害+0.477100m+0.27 1500m' numeric
@attribute 0.8121500m-0.565100m+0.137400m-0.055110m障害 numeric
@attribute '-0.802110m障害+0.511400m+0.302100m+0.07 1500m' numeric
@attribute -0.602100m+0.602400m-0.5131500m+0.112110m障害 numeric

@data
1.800057,-2.593374,-0.161757,-0.373194
1.872076,1.055331,0.430513,0.375035
1.722737,0.740631,0.157542,-0.001991
-0.969658,-0.916871,-0.917174,0.097328
0.173203,1.424731,-0.863497,0.149143
-0.68645,0.217715,-0.142613,0.640314
2.372266,-0.119313,1.089738,-0.938764
0.134551,0.895247,-0.500842,-0.1651
1.836546,0.465193,0.082445,-0.043835
1.090967,0.629992,0.929453,-0.749621
-0.913719,0.057118,-0.886448,-0.179132
-1.917071,0.087052,0.532401,-1.221215
0.955842,-0.564758,-0.509565,0.21552
-0.661506,0.763555,-0.199888,-0.040861
1.149662,-1.849717,-0.742618,-0.37042
-0.682725,1.014026,-0.678183,0.30132
-1.682549,0.557509,-0.154379,0.071097
-0.181745,0.654465,-0.129243,0.220728
0.704698,0.157551,-0.101413,0.313268
0.789105,-1.978592,0.151541,0.778736
0.856461,0.377036,1.015685,1.236858
-0.86916,-1.503161,-0.431242,-0.087696
-1.065625,0.872419,0.171254,-0.116934
-2.693257,-0.166969,0.280854,-0.730476
0.088591,1.086887,0.029169,-0.115495
-3.223297,-1.363703,1.548266,0.735386
```

参考にしたサイトの結果と似たような結果が出力できました。
累積寄与率は、主成分1と2で、82.28%となりますので、その2つで分析、可視化できそうです。

PrincipalComponentsには、いくつもパラメーターが設定できます。
今回はデフォルトでOKみたいでしたが、パラメーターについてはもう少し勉強したいです。