2015-07-02

【備忘録】Rで主成分分析 | キャスレーコンサルティング 技術ブログ

成分分析 (PCA, Principal Component Analysis) とは、多変量のデータを、互いに相関が無い、より少ない個数の特性値(主成分)にまとめる手法です。この手法について、分かり易い例を探していたところ、素晴らしい解説を見つけました。パクってしまいそうですので、そうならないうちにそのまま紹介してしまいます。ご興味のある方は、下記をご覧になってください。

主成分分析を利用した多変量解析に取り組んでいますが、紹介可能なサンプルができれば、あらためて紹介します。


2015-06-28

【まとめ】Google Chromebooks の今

Chromebook(クロームブック)は、Google が開発しているオペレーティングシステム Google Chrome OS を搭載しているノートパソコンのシリーズです。2011 年 5 月に、Google がサムスン電子との共同開発で 1 号機を発表、同 6 月より市販されています。日本では、法人ユーザーと教育関係機関向けに先行販売されていましたが、個人向けに 2014 年 11 月 11 日より発売されています。

個人向けに販売されて半年以上経っていますが、現状はどのようになっているかをまとめました。

まずは、古い記事ですが、Chromebook に実際に使い込んだ経験を元に書かれている記事です。執筆業に携る人の視点でまとめられています。

以下の記事も、半年程度実際に使った経験をベースとした記事です。これも編集部員やライター氏の視点でまとめられています。

以下の記事も、どちらかと言えば編集部員やライター氏の視点で、肯定的な表う論をしていますが、Chromebook に満足するには、自身が Chrome 化していなければ、と最後に結論付けています。

以下の記事も、ライター氏の視点ですが、製品の宣伝も兼ねているのか、写真やビデオの映像が豊富です。

こちらは、一般的なユーザーに近い視点で書かれています。Chromebook の長所/短所がわかりやすくまとめられています。

Amazon.jp の Chromebook ストアをみてみましょう。

Chromebook の特徴が簡潔に 6 つにまとめられており、購入できる Chromebook が並んでいます。CPU は Intel Celeron/Atom を搭載したものが大半ですが、Intel 系の CPU が必須というわけではなく、Samsung 製はもちろんのこと一部の HP の製品でも ARM 系の Samsung Exynos を搭載しているものもあります。個人的には ARM を搭載している機種に興味津々ですが、バッテリーの持ちで Intel の CPU に較べて圧倒的に優位でもない限り、CPU にこだわる必要は無いのかもしれません。


2015-06-26

JavaFX: チャートのカスタマイズ (1)

JavaFX のチャートを使いこなせるようになりたいと、いくつかチャートのサンプルを紹介してきました。前回は ScatterChart を利用した散布図のサンプルを紹介しましたが、やはり、点と線をチャートでは自由に扱いたいと感じました。データ点(シンボル)と線を別々に扱いたければ、LineChart で、データ点については点と点を結ぶ線を透明にすれば良いのですが、なんだかとても無駄な気がします。

X と Y 座標でデータ表現する ScatterChart などのクラスは、抽象クラス XYChart を継承しています。JavaFX のチャートをカスタマイズしたければ、XYChart を継承して好きなようにチャートを作れば良いのですが、いきなりは敷居が高いです。そこで、線も点(シンボル)も扱える LineChart を継承したクラスで、部分的に機能をカスタマイズしてみます。

動作環境は次の通りです。

  • OS: Fedora 22 x86_64
  • jdk1.8.0_45-1.8.0_45-fcs.x86_64 (Oracle)
  • IDE: NetBeans IDE 8.0.2
  • commons-math3-3.5

今回は、回帰計算をするために、Apache Commons Math ライブラリを使用しています

さて、まず LineChart を継承するクラス MyChart1 です。ここでは、最初のデータ群 (series) をデータ点(シンボル)のみ、二番目以降のデータ群はシンボルなしで直線で結ぶだけ、という仕様にしました。

MyChart1 では、メソッド layoutPlotChildren をオーバーライドしています。参考サイト [1] にある LineChart のメソッド layoutPlotChildren のコードに手を加えました。なお、JavaFX Demos and Samples の CandleStickChart にある layoutPlotChildren メソッドを参考にしています。

リスト:MyChart1.java 
package mychartsample;

import java.util.Iterator;
import javafx.scene.Node;
import javafx.scene.chart.Axis;
import javafx.scene.chart.LineChart;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;

/**
 * MyChart1
 *
 * @author bitwalk
 * @param <X>
 * @param <Y>
 */
public class MyChart1<X, Y> extends LineChart<X, Y> {

    // -------------- CONSTRUCTORS ----------------------------------------------
    /**
     * Construct a new MyChart with the given axis.
     *
     * @param xAxis The x axis to use
     * @param yAxis The y axis to use
     */
    public MyChart1(Axis<X> xAxis, Axis<Y> yAxis) {
        super(xAxis, yAxis);
    }

    // -------------- METHODS ------------------------------------------------------------------------------------------
    /**
     *
     */
    @Override
    protected void layoutPlotChildren() {
        for (int seriesIndex = 0; seriesIndex < getData().size(); seriesIndex++) {
            Series<X, Y> series = getData().get(seriesIndex);
            Iterator<Data<X, Y>> iter = getDisplayedDataIterator(series);
            boolean isFirst = true;

            if (series.getNode() instanceof Path) {
                Path seriesLine = (Path) series.getNode();
                seriesLine.getElements().clear();

                while (iter.hasNext()) {
                    Data<X, Y> item = iter.next();
                    double x = getXAxis().getDisplayPosition(getCurrentDisplayedXValue(item));
                    double y = getYAxis().getDisplayPosition(getCurrentDisplayedYValue(item));

                    if (seriesIndex == 0) {
                        // 最初のデータ群はシンボルのみ
                        Node symbol = item.getNode();
                        if (symbol != null) {
                            final double w = symbol.prefWidth(-1);
                            final double h = symbol.prefHeight(-1);
                            symbol.resizeRelocate(x - (w / 2), y - (h / 2), w, h);
                        }
                    } else {
                        // 最初のデータ群でない場合は線のみ
                        if (isFirst) {
                            isFirst = false;
                            seriesLine.getElements().add(new MoveTo(x, y));
                        } else {
                            seriesLine.getElements().add(new LineTo(x, y));
                        }
                    }
                }
            }
        }
    }
}

MyChart1 を使ってチャートを表示するサンプル MyChart1Sample.java では、[2] で紹介したサンプルをベースにして、同じデータ点に、Commons Math ライブラリで計算した回帰直線と三次の重回帰式を加えてみました。

リスト:MyChart1Sample.java 
package mychartsample;

import java.util.ArrayList;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.chart.XYChart.Series;
import javafx.stage.Stage;
import org.apache.commons.math3.stat.regression.OLSMultipleLinearRegression;
import org.apache.commons.math3.stat.regression.SimpleRegression;

/**
 * MyChart1Sample - for sample of MyChart1, customized LineChart
 *
 * @author bitwalk
 */
public class MyChart1Sample extends Application {

    @Override
    public void start(Stage stage) {
        stage.setTitle("MyChart Sample");

        final NumberAxis xAxis = new NumberAxis();
        final NumberAxis yAxis = new NumberAxis();
        xAxis.setLabel("X軸ラベル(時間)");
        yAxis.setLabel("Y軸ラベル(数値)");

        final MyChart1<Number, Number> myChart = new MyChart1<>(xAxis, yAxis);
        myChart.setTitle("最小二乗法のサンプル");

        myChart.setAnimated(false);
        myChart.setCreateSymbols(true);

        myChart.setData(getChartData());

        Scene scene = new Scene(myChart, 600, 400);
        scene.getStylesheets().add(getClass().getResource("MyChart1.css").toExternalForm());

        stage.setScene(scene);
        stage.show();
    }

    /**
     * getChartData
     *
     * @return ObservableList<XYChart.Series<String, Double>>
     */
    private ObservableList<XYChart.Series<Number, Number>> getChartData() {
        Series<Number, Number> series1 = new Series<>();
        Series<Number, Number> series2 = new Series<>();
        Series<Number, Number> series3 = new Series<>();

        // raw data
        ArrayList<PairData> dataArr = new ArrayList<>();
        dataArr.add(setPair(1d, 3d));
        dataArr.add(setPair(2d, 7d));
        dataArr.add(setPair(3d, 14d));
        dataArr.add(setPair(4d, 10d));
        dataArr.add(setPair(5d, 17d));

        // data range used for line/curve
        ArrayList<Double> xRange = new ArrayList<>();
        xRange.add(0.5);
        xRange.add(5.5);

        // for simple regression
        SimpleRegression regression = new SimpleRegression();

        // for multiple regression
        OLSMultipleLinearRegression mulreg = new OLSMultipleLinearRegression();
        double[] y_mulreg = new double[dataArr.size()];
        double[][] x_mulreg = new double[dataArr.size()][];

        // raw data setting and regression
        series1.setName("観測データ");
        for (int i = 0; i < dataArr.size(); i++) {
            double x = dataArr.get(i).X;
            double y = dataArr.get(i).Y;
            series1.getData().add(new XYChart.Data(x, y));

            // simple regression
            regression.addData(x, y);

            // muliple regression for cubic
            y_mulreg[i] = y;
            x_mulreg[i] = new double[]{x, x * x, x * x * x};
        }

        // regression
        series2.setName("単回帰(直線)");
        xRange.stream().forEach((xr) -> {
            series2.getData().add(new XYChart.Data(xr, regression.predict(xr)));
        });

        // multiple requression
        series3.setName("重回帰(三次式)");
        mulreg.newSampleData(y_mulreg, x_mulreg);
        double[] beta = mulreg.estimateRegressionParameters();

        double x0 = xRange.get(0);
        while (x0 <= xRange.get(1)) {
            series3.getData().add(new XYChart.Data(x0, getPolynomialValue(beta, x0)));
            x0 = x0 + 0.01;
        }

        // set series data to collection
        ObservableList<XYChart.Series<Number, Number>> seriesList = FXCollections.observableArrayList();
        seriesList.addAll(series1, series2, series3);

        return seriesList;
    }

    /**
     * getPolynomialValue
     *
     * @param coef coefficient
     * @param x x value
     * @return calculated polynomial
     */
    private double getPolynomialValue(double[] coef, double x) {
        double y = 0d;
        for (int i = 0; i < coef.length; i++) {
            y = y + coef[i] * Math.pow(x, i);
        }
        return y;
    }

    /**
     * setPair - set pair of data to the PairData structure
     *
     * @param x
     * @param y
     * @return PairData
     */
    private PairData setPair(double x, double y) {
        PairData data = new PairData();
        data.X = x;
        data.Y = y;

        return data;
    }

    /**
     * PairData - structure for handling (x, y) data
     */
    private class PairData {

        double X;
        double Y;
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }

}

CSS は、チャートの主要部分は今まで紹介したサンプルで使用した CSS をベースとしていますが、シンボルとラインの修飾については、今回の仕様に合うように書き直しています。

リスト:MyChart.css 
.chart {
    -fx-padding: 10px;
    -fx-background-color: white;
}

.chart-content {
    -fx-padding: 10px;    
}

.chart-title {
    -fx-font-size: 18pt;    
}

.chart-vertical-grid-lines {
    -fx-stroke: #c1e0fd;
}
.chart-horizontal-grid-lines {
    -fx-stroke: #c1e0fd;
}

.chart-legend {
    -fx-font-size: 12pt;    
    -fx-background-color:  transparent;
    -fx-padding: 10px;
}

.axis-label {
    -fx-font-size: 14pt;
}

.axis {
    -fx-tick-label-font: 12pt system;
}

.chart-series-line {
    -fx-stroke-width: 1px;
    -fx-effect: null;
}

.default-color0.chart-series-line { -fx-stroke: transparent; }
.default-color1.chart-series-line { -fx-stroke: blue; }

.default-color0.chart-line-symbol { 
    -fx-background-color: red;
    -fx-background-radius: 0;
    -fx-background-insets: 0;
    -fx-shape: "M2,0 L5,4 L8,0 L10,0 L10,2 L6,5 L10,8 L10,10 L8,10 L5,6 L2,
        10 L0,10 L0,8 L4,5 L0,2 L0,0 Z";
}

.default-color1.chart-line-symbol { 
    -fx-background-color: transparent, transparent; 
}

.default-color1.chart-legend-item-symbol{
    -fx-background-color: blue;
    -fx-background-radius: 0;
    -fx-background-insets: 0;
    -fx-shape: "M0,5 L0,7 L12,7 L12,5 Z";
    -fx-scale-shape: false;
}

実行例を以下に示します。

今回はほんの僅かですが、すこしずつカスタマイズできる範囲を広げて行く予定です。

参考サイト

  1. openjfx/8/master/rt: 88d3bef80ffc modules/controls/src/main/java/javafx/scene/chart/LineChart.java
  2. bitWalk's: JavaFX: LineChart を使いこなそう (3)
  3. JavaFX CSS Reference Guide
  4. Using JavaFX Charts: Styling Charts with CSS | JavaFX 2 Tutorials and Documentation