2016-02-13

JavaFX: チャートの重ね合わせ

JavaFX で棒グラフ (BarChart) と折れ線グラフ (LineChart) を合わせて表示したい場合、StackPane を用いてそれぞれを重ねて表示することで簡単に実現できます。Jewelsea (John Smith) 氏が GitHub Gist でサンプルを公開していますので、その実行例を紹介します [1]

動作を確認した環境は次の通りです。

  • OS: Fedora 23 (x86_64)
  • Java: Java SE 1.8.0_72 (jdk1.8.0_72-1.8.0_72-fcs.x86_64)
  • IDE: NetBeans IDE 8.1

パレート図(もどき)への応用

StackPane を用いたチャートの重ね合わせを応用して、BarChart の y 軸が 左側、LineChart の y 軸が右側にあるパレート図を描画するサンプルを紹介します。ただし、QC七つ道具のパレート図で定義されている累積比率の折れ線と、プロットの仕方が異なっていることをご了承ください [2]

とある会社で品証部門に在籍していたことがあり、そこには日本の品質管理の手法を大切にする気風がありました。何かの時に R で作成したパレート図を自慢気に披露したところ、これはパレート図ではない、と指摘されて赤面したことがあるのです。それが、ここで紹介するパレート図と同じ書き方でしたので、自戒をこめて(もどき)と入れています。個人的には、定義から逸れているからダメだという議論より、このチャートから何を読み取るべきかが大切だとは思うのですが、間違いを間違いだと認めなければなりません。

リスト:Sample_Pareto.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package sample_pareto;
 
import javafx.application.Application;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Pos;
import javafx.geometry.Side;
import javafx.scene.Scene;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
 
public class Sample_Pareto extends Application {
 
    Boolean first = true;
 
    BarChart<String, Number> barchart;
    LineChart<String, Number> linechart;
 
    // X data
    String category1 = "A";
    String category2 = "B";
    String category3 = "C";
    String category4 = "D";
    String category5 = "E";
    String category6 = "Others";
    // Y data
    ObservableList<XYChart.Data> data1 = FXCollections.observableArrayList(
            new XYChart.Data(category1, 34),
            new XYChart.Data(category2, 19),
            new XYChart.Data(category3, 12),
            new XYChart.Data(category4, 9),
            new XYChart.Data(category5, 6),
            new XYChart.Data(category6, 10)
    );
    // Y2 data
    ObservableList<XYChart.Data> data2 = FXCollections.observableArrayList(
            new XYChart.Data(category1, 37.8),
            new XYChart.Data(category2, 58.9),
            new XYChart.Data(category3, 72.2),
            new XYChart.Data(category4, 82.2),
            new XYChart.Data(category5, 88.9),
            new XYChart.Data(category6, 100.0)
    );
    // chart title
    String gTitle = "パレート図のサンプル  ";
    String gXTitle = "カテゴリ";
    String gYTitle = "件  数";
    String gY2Title = "累積比率 (%)";
    // geometry
    double sizeX = 600;
    double sizeY = 500;
    double offsetX = 60;
 
    @Override
    public void start(Stage stage) {
        // X axis
        CategoryAxis xAxis = new CategoryAxis();
        xAxis.setLabel(gXTitle);
 
        // barchart
        barchart = createBarChart(xAxis, data1);
        barchart.setTitle(""); // ダミー
        barchart.getStylesheets().addAll(getClass().getResource("BaseChart.css").toExternalForm());
        // linechart
        linechart = createLineChart(xAxis, data2);
        linechart.setTitle(gTitle);
        // set chart width
        updateChartWidth();
 
        // stackpane for overlay
        StackPane pane = new StackPane();
        pane.getChildren().addAll(barchart, linechart);
        pane.setAlignment(Pos.BOTTOM_LEFT);
 
        Scene scene = new Scene(pane, sizeX, sizeY);
        scene.widthProperty().addListener(
                (ObservableValue<? extends Number> observableValue, Number oldSceneWidth, Number newSceneWidth) -> {
                    if (!first) {
                        sizeX = (double) newSceneWidth;
                        updateChartWidth();
                    } else {
                        first = false;
                    }
                }
        );
        scene.getStylesheets().add(getClass().getResource("ParetoChart.css").toExternalForm());
        stage.setScene(scene);
        stage.show();
    }
 
    BarChart<String, Number> createBarChart(CategoryAxis xAxis, ObservableList<XYChart.Data> data) {
        NumberAxis yAxis = createYaxis(0, 90, 10, 5);
        yAxis.setLabel(gYTitle);
 
        BarChart<String, Number> chart = new BarChart<>(xAxis, yAxis);
        chart.setLegendVisible(false);
 
        XYChart.Series series = new XYChart.Series(data);
        series.setName("カテゴリ");
        chart.getData().add(series);
 
        return chart;
    }
 
    LineChart<String, Number> createLineChart(CategoryAxis xAxis, ObservableList<XYChart.Data> data) {
        NumberAxis y2Axis = createYaxis(0, 100, 10, 2);
        y2Axis.setSide(Side.RIGHT);
        y2Axis.setLabel(gY2Title);
 
        LineChart<String, Number> chart = new LineChart<>(xAxis, y2Axis);
        chart.setLegendVisible(false);
        chart.setHorizontalGridLinesVisible(false);
        chart.setVerticalGridLinesVisible(false);
 
        XYChart.Series series = new XYChart.Series(data);
        series.setName("累積");
        chart.getData().add(series);
 
        return chart;
    }
 
    NumberAxis createYaxis(double min, double max, int major, int minor) {
        final NumberAxis axis = new NumberAxis(min, max, major);
        axis.setMinorTickCount(minor);
        axis.setPrefWidth(offsetX);
        return axis;
    }
 
    void updateChartWidth() {
        barchart.setLayoutX(0);
        barchart.setTranslateX(0);
        barchart.setMaxWidth(sizeX - offsetX);
 
        linechart.setLayoutX(0);
        linechart.setTranslateX(offsetX);
        linechart.setMaxWidth(sizeX - offsetX);
    }
 
    public static void main(String[] args) {
        launch(args);
    }
}
リスト:ParetoChart.css
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
.chart {
    -fx-padding: 10px;
}
 
.chart-content {
    -fx-padding: 10px;   
}
 
.chart-title {
    -fx-font-size: 16pt;   
}
 
.chart-plot-background {
    -fx-background-color: transparent;
}
 
.axis-label {
    -fx-font-size: 14pt;
}
 
.axis {
    AXIS_COLOR: #888;
    -fx-tick-label-font: 12pt system;
    -fx-tick-label-fill: derive(-fx-text-background-color, 30%);
}
 
.chart-bar {
    -fx-border-color: rgba(0, 0, 0, 0.4) rgba(0, 0, 0, 0.4) transparent rgba(0, 0, 0, 0.4);
}
 
.series0.chart-bar {
    -fx-background-color: lightskyblue;
}
 
.default-color0.chart-series-line {
    -fx-stroke: #f44;
}
 
.default-color0.chart-line-symbol {
    -fx-background-color: #f44, #f88;
    -fx-background-radius: 6px;
    -fx-padding: 6px;
}
リスト:BaseChart.css
1
2
3
.chart-plot-background {
    -fx-background-color: white;
}

実行例

参考サイト

  1. Uses JavaFX to draw layers of XYCharts.
  2. (株)日科技研:パレート図とは(QC七つ道具)|製品案内

0 件のコメント: