有个作业需要用 Java 展示图表。这里记录一下我是如何把 XYChart.BarChart 调教成 Histogram 的。

写在前面

为什么不用 JFreeChart?

太丑了。

Workaround

ALevel 数学老师已经教过,Histogram 和 Bar chart 在外观的区别在于 bar 之间有没有间隔(histogram 的 bar 之间没有间隔),以及 Y 轴代表的是 frequency(histogram) 还是具体的数值(bar chart)。XYCharts 允许我们设置 Bar 和 Category 的间隔,这就好说了。

BarChart histogram = new BarChart<>(xAxis, yAxis);
histogram.setBarGap(0);
histogram.setCategoryGap(0);

下一个问题就是统计正确的数据。我们可以用一个简单的 enhanced for loop 来统计不同 bin 的 frequency. 直接上代码:

double binCap = binSize; // 这是 bin 的大小(间隔)。比如 0.2
double binBottom = 0; // 最小的 bin 的起始点,这里设置的是 0.
double frequency = 0; // 初始化 frequency
XYChart.Series<String, Double> series = new XYChart.Series<>();
double[] data = new double[]{1,2,3,4,5,6,6,7}// 源数据。需要从小到大排列。

for (double i : data) {
    if (i <= binCap) { 
        // 这个值在当前 bin 的范围内。增加 frequency.
        frequency++;
    } else {
        // 这个值比当前 bin 大了。把当前 bin 的 frequency 信息及 interval 加入到 series 里面。
        series.getData().add(new XYChart.Data<>(binBottom + " - " + binCap,frequency));
        while (i > binCap) {
            // 更新 bin 的 interval,跳过空的 bin
            binBottom = binCap;
            binCap += binSize;
            if (i > binCap) {
                series.getData().add(new XYChart.Data<>(binBottom + " - " + binCap, 0.0));
            }
        }
        frequency = 1; // 重新开始计算 frequency.
    }
}
// 把最后一个 bin 的相关数据加入 series
series.getData().add(new XYChart.Data<>(binBottom + " - " + binCap,frequency));

histogram.getData().clear(); // 防止 histogram 里面已经存了数据,清空一下
histogram.getData().add(series);

为什么不直接操作 XYChart.Series?

简单来说,只有在读取一个 Series 的内容的时候,这个 Series 的内容才会被生成。所以当我们还在往 Series 里面存入数据的同时要读取里面的内容是做不到的。

成果

https://storage.pajilabs.com/api/raw/?path=/java_April_06_BVmlLNezO7.png

除另有声明外,本博客文章均采用 知识共享(Creative Commons) 署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。