30 多维数据分析:jquick-pdf 热力图、雷达图 PDF 实战
引入
客服中心既关心“小时 × 月份”的忙闲分布,也关心部门在质量、效率、成本、满意度上的综合表现。两类问题分别对应热力图和雷达图:热力图看矩阵中的局部密度,雷达图看多个指标围成的轮廓。
本文的目标是把两种图各自跑通一遍最小可运行链路,重点在数据结构与绑定方式,不涉及多图组合、复杂配置与页面编排(多图注册到同一份 PDF 的做法见第 31 篇)。依赖坐标与模板基础语法见入门篇《01 jquick-pdf 超详细入门教程》。
核心讲解
选型依据
热力图的表现形式是二维矩阵:两个分类轴定义行列,单元格的值决定颜色深浅,适合表达时段分布、地域分布、渠道矩阵这类“两个维度交叉后的强度”。雷达图的表现形式是若干条从中心辐射的指标轴,适合表达同一对象在多个维度上的相对强弱,前提是各维度的最大值已经明确。
两者的共同点是需要先定义维度,不同点在于维度的处理方式:热力图的两个维度都是离散类别,值只影响颜色;雷达图的每个维度都有尺度上限,值同时影响轮廓形状。把这两点弄清楚,选型就不会错。
热力图:坐标索引数据
HeatMapCharTest 已验证 JHeatmap、两个 JCategoryAxis 与 JHeatMapChartRenderer,数据项按 {xIndex, yIndex, value} 组织。索引是从 0 开始的整数,指向分类轴上的位置,而不是类别名称本身:
option.xAxis(new JCategoryAxis().data("周一", "周二", "周三", "周四"));
option.yAxis(new JCategoryAxis().data("上午", "下午", "晚上"));
option.series(new JHeatmap().data(new Object[]{0, 0, 12}, new Object[]{1, 0, 18},
new Object[]{2, 1, 32}, new Object[]{3, 2, 25}));
示例中 {2, 1, 32} 表示周三下午的工单数为 32。索引必须落在轴标签数量范围内,否则色块会与标签错位。数据本身是稀疏的:没有出现的坐标代表“该格子无记录”,这与“值为 0”是两种不同状态,需要分别处理。
雷达图:指标维度与尺度
RadarCharTest 已验证 JRadar、JRadar.Indicator、JRadarSeries 与 JRadarChartsRenderer。指标定义维度与最大值,系列定义被比较对象:
JRadar radar = new JRadar().indicator(new JRadar.Indicator().name("质量").max(100));
JRadarSeries series = new JRadarSeries().name("本期").type(JSeriesType.radar).data(82);
option.radar(radar); option.series(series);
指标数量决定轮廓的顶点数,max 决定该维度上“满值”的位置。同一张雷达图上的多个指标若 max 不同,轮廓形状就无法直接比较,因此比较前必须统一各维度尺度,或在报告中说明归一化方法。系列的数据个数要与指标个数一致,缺项会让轮廓变形。
各自的 SVG 绑定方式
两种图都遵循“先生成 SVG,再嵌入模板”的链路。最小做法是变量绑定:renderer 输出 SVG 文件,读取为字符串后通过 bind("svg", svg) 注入,模板用 <svg>${svg}</svg> 引用。两张图分两次生成、两次绑定、两次执行模板,互不干扰,也不会出现同名变量覆盖。若要在一份文档中同时放两张图,需要注册图表资源后用 &{名称} 引用,这部分属于多图编排,放在下一篇展开。
关键细节
- 热力数据索引越界会使色块与标签错位,写入前要校验索引范围。
- 空值与零值要区分,不能都显示为冷色。空值代表无记录,零值代表有记录但值为 0。
- 雷达指标最大值不同会造成比较误导,应先归一化或统一 max。
- 维度太多会让雷达标签重叠,指标建议控制在五到八个。
- 两张 SVG 若使用同名变量会造成上下文覆盖,本次采用两次独立绑定即可规避。
- 网格规模要限制。行乘列超过可读范围时,颜色差异无法分辨,应先聚合。
- 图表尺寸要显式控制,热力图过宽会挤压正文,雷达图过小则标签重叠。
- 输出目录必须可写,SVG 中间产物要及时清理。
实战说明
下面程序输出当前目录的 multi-report.pdf,演示热力图的最小完整链路:
import com.github.paohaijiao.JOption;
import com.github.paohaijiao.axis.JCategoryAxis;
import com.github.paohaijiao.config.JGraphConfig;
import com.github.paohaijiao.config.JPdfConfig;
import com.github.paohaijiao.data.JGraphContainer;
import com.github.paohaijiao.enums.JChartType;
import com.github.paohaijiao.executor.JQuickPdfFactory;
import com.github.paohaijiao.heatMap.JHeatMapChartRenderer;
import com.github.paohaijiao.series.JHeatmap;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.charset.StandardCharsets;
public class MultiDimensionDemo {
public static void main(String[] args) throws Exception {
JOption option = new JOption();
option.title().text("客服忙闲热力图");
option.xAxis(new JCategoryAxis().data("周一", "周二", "周三", "周四"));
option.yAxis(new JCategoryAxis().data("上午", "下午", "晚上"));
option.series(new JHeatmap().data(new Object[]{0, 0, 12}, new Object[]{1, 0, 18},
new Object[]{2, 1, 32}, new Object[]{3, 2, 25}));
String template = "<pdf><body><h1>'客服运营多维分析'</h1><svg>&{svg}</svg>"
+ "<p>'热力值为进入工单数;雷达图建议由独立 SVG 绑定。'</p></body></pdf>";
JGraphContainer graphContainer = new JGraphContainer();
graphContainer.setType(JChartType.HEATMAP);
graphContainer.setOption(option);
JGraphConfig graphConfig = new JGraphConfig();
graphConfig.put("svg", graphContainer);
JPdfConfig config = new JPdfConfig();
config.setGraphConfig(graphConfig);
byte[] pdf = new JQuickPdfFactory(config).executeContent(template);
Files.write(Paths.get("d://test//heat-report.pdf"), pdf);
}
}

代码按步骤拆解:第一步用两个 JCategoryAxis 建立网格,横轴是星期、纵轴是班次,顺序决定索引 0 的位置;第二步构造 JHeatmap 并写入四个稀疏数据点,未出现的坐标保持无记录;第三步由 JHeatMapChartRenderer 输出 heatmap.svg;第四步读取 SVG 字符串并绑定为变量 svg,模板用 <svg>${svg}</svg> 嵌入,说明段落同时提示雷达图的绑定方式。辅助方法 read 只是把文件读取收敛成一行,便于后续复用。
雷达图按同样方式独立跑通:构造 JRadar 与 JRadarSeries,由 JRadarChartsRenderer 输出 radar.svg,再用同一个 <svg>${svg}</svg> 模板绑定即可,两个文档之间没有任何共享状态。
结果预期:multi-report.pdf 中出现标题、一张三行四列的网格图(仅有四个格子着色,其余为无记录状态)以及说明段落;雷达图单独生成时会呈现五边形轮廓。若热力图颜色全部一致,通常是数据点索引都落在同一格,或值被当成字符串传入。
常见失败案例:一是把“周三”这类标签直接写进数据点,导致索引解析异常;二是雷达系列数据个数与指标个数不一致,轮廓缺角;三是两张图共用一个变量名,后一次绑定覆盖前一次;四是忘记清理 SVG,批量任务中堆积大量中间文件。
性能与监控方面,建议统一指标口径与归一化策略,限制热力网格规模,为每张图生成唯一文件名,缓存计算结果并记录统计时间。对敏感客服数据做权限与脱敏。上线后关注生成耗时、图表数据点数与输出文件大小。
总结
热力图与雷达图解决的是不同的表达问题:前者用颜色编码矩阵强度,后者用轮廓表达多维强弱。跑通它们只需三步——定义维度、组织数据、绑定 SVG;真正的风险来自索引越界、空值与零值混淆、以及雷达维度尺度不统一。适用边界是单图场景;需要在一份文档中组合多张图时,应转向资源注册方式。
版本基线:jquick-pdfx 4.0.0、JDK 8+;更多示例见 GitHub 仓库。
网硕互联帮助中心







评论前必须登录!
注册