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) 允许数据重叠显示(可选)

输出图表

xchart histogram

图表分析

从图中可直观看出:

  • ❌ 80–90 岁区间无学生
  • ✅ 50–60 岁学生最多 → 很可能是博士或博士后研究人员
  • 分布近似正态分布(钟形曲线)

💡 踩坑提醒:
xData 未排序,可能导致柱子顺序错乱。建议对 key 排序后再生成 label。


5. 总结

本文演示了如何结合 Apache Commons MathFrequency 类与 xchart 实现数据频次统计与直方图可视化。

✅ 核心优势:

  • Frequency 提供了简单粗暴的频次、百分比、累计百分比计算能力
  • xchart 零依赖、API 简洁,适合快速出图

📌 该库还包含许多实用模块:

  • 统计分析(Statistics)
  • 几何计算(Geometry)
  • 遗传算法(Genetic Algorithms)
  • 线性代数、插值、优化等

📚 官方文档:Apache Commons Math User Guide
💻 源码地址:GitHub - libraries-apache-commons


原始标题:Histograms with Apache Commons Frequency

» 下一篇: 学习JUnit