1. 概述
本文将介绍如何借助 Apache Commons 的 Frequency
类来生成数据的直方图(Histogram)。
Frequency
类属于 Apache Commons Math 库的一部分,该库在这篇文章中有更全面的介绍。
✅ 直方图 是一种由相邻柱状条组成的图表,用于展示数据集中某个数值区间内数据出现的频次。
⚠️ 它与普通柱状图(Bar Chart)的关键区别在于:
- 直方图用于展示连续型、定量数据的分布情况
- 柱状图则用于展示分类数据(Categorical Data)
举个例子:统计学生年龄分布适合用直方图;统计不同专业人数则适合用柱状图。
2. 项目依赖
我们使用 Maven 管理项目,核心依赖如下:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-math3</artifactId>
<version>3.6.1</version>
</dependency>
<dependency>
<groupId>org.knowm.xchart</groupId>
<artifactId>xchart</artifactId>
<version>3.8.2</version>
</dependency>
commons-math3
:提供Frequency
类,用于计算数据频次统计xchart
:轻量级 Java 图表库,用于可视化直方图
📌 最新版本可在 Maven Central 查询:
3. 计算变量频次
本例使用某学校学生的年龄数据集,目标是统计不同年龄段的学生人数,并绘制分布直方图。
数据准备
List<Integer> datasetList = Arrays.asList(
36, 25, 38, 46, 55, 68,
72, 55, 36, 38, 67, 45, 22,
48, 91, 46, 52, 61, 58, 55);
Frequency frequency = new Frequency();
datasetList.forEach(d -> frequency.addValue(Double.parseDouble(d.toString())));
⚠️ 注意:Frequency
接收 double
类型,因此需做类型转换。
分组统计(分箱 Bin)
我们设定每组宽度(class width)为 10 岁,例如 0-10、10-20……以此类推。
int classWidth = 10;
Map<Integer, Long> distributionMap = new HashMap<>();
datasetList.stream()
.map(d -> Double.parseDouble(d.toString()))
.distinct()
.forEach(observation -> {
long observationFrequency = frequency.getCount(observation);
int upperBoundary = (observation > classWidth)
? Math.multiplyExact((int) Math.ceil(observation / (double) classWidth), classWidth)
: classWidth;
int lowerBoundary = (upperBoundary > classWidth)
? upperBoundary - classWidth
: 0;
String bin = lowerBoundary + "-" + upperBoundary;
updateDistributionMap(lowerBoundary, bin, observationFrequency);
});
辅助方法:更新分布映射
private void updateDistributionMap(int lowerBoundary, String bin, long frequency) {
if (distributionMap.containsKey(lowerBoundary)) {
distributionMap.put(lowerBoundary, distributionMap.get(lowerBoundary) + frequency);
} else {
distributionMap.put(lowerBoundary, frequency);
}
}
📌 核心逻辑说明:
frequency.getCount(observation)
:获取某个年龄值的出现次数- 动态计算所属区间(bin)的上下界
- 使用
Map
按区间汇总频次,避免重复计算
✅ Frequency
类还支持以下统计方法:
getPct(value)
:返回该值占总体的百分比getCumPct(value)
:返回累计百分比(Cumulative Percentage)
4. 绘制直方图
将分组后的数据通过 xchart
可视化为柱状图。
CategoryChart chart = new CategoryChartBuilder()
.width(800)
.height(600)
.title("Age Distribution")
.xAxisTitle("Age Group")
.yAxisTitle("Frequency")
.build();
chart.getStyler().setLegendPosition(Styler.LegendPosition.InsideNW);
chart.getStyler().setAvailableSpaceFill(0.99);
chart.getStyler().setOverlapped(true);
List<?> yData = new ArrayList<>(distributionMap.values());
List<?> xData = new ArrayList<>(distributionMap.keySet().stream()
.sorted()
.map(k -> k + "-" + (k + 10))
.toList());
chart.addSeries("age group", xData, yData);
new SwingWrapper<>(chart).displayChart();
📌 关键配置说明:
配置项 | 作用 |
---|---|
setLegendPosition |
图例位置设为左上角 |
setAvailableSpaceFill |
提高绘图区域占比 |
setOverlapped(true) |
允许数据重叠显示(可选) |
输出图表
图表分析
从图中可直观看出:
- ❌ 80–90 岁区间无学生
- ✅ 50–60 岁学生最多 → 很可能是博士或博士后研究人员
- 分布近似正态分布(钟形曲线)
💡 踩坑提醒:
若 xData
未排序,可能导致柱子顺序错乱。建议对 key 排序后再生成 label。
5. 总结
本文演示了如何结合 Apache Commons Math
的 Frequency
类与 xchart
实现数据频次统计与直方图可视化。
✅ 核心优势:
Frequency
提供了简单粗暴的频次、百分比、累计百分比计算能力xchart
零依赖、API 简洁,适合快速出图
📌 该库还包含许多实用模块:
- 统计分析(Statistics)
- 几何计算(Geometry)
- 遗传算法(Genetic Algorithms)
- 线性代数、插值、优化等
📚 官方文档:Apache Commons Math User Guide
💻 源码地址:GitHub - libraries-apache-commons