引言
可视化最本质的任务不是「把数据画得好看」,而是降低读者从数据中提取结论的认知成本。一个被误用的饼图会让读者在十二个扇区之间反复比较角度,而同样数据的条形图能让人一眼看出排序。选错图表造成的损失是隐性的:结论依然能得出,但速度慢、精度低、且容易产生系统性偏差。
工程实践中的难点在于,图表选型从来不是纯粹的美学问题,而是数据类型、分析任务、读者背景、输出介质四个约束的交集。同一个「各地区销售额」数据集,给高管看要的是一张按销售额排序的横向条形图,给运营看要的是带同比的分组柱状图,给财务看要的可能是瀑布图。数据没变,任务变了,图表就该变。
本文按「先建立映射模型、再分任务展开、最后给决策流程」的顺序组织。前四节建立理论基础,中间五节按分析任务分类给出具体图表与代码,最后三节处理选型决策、反模式与工程落地的库选型。文中涉及的图表库 API 均以 ECharts 5.4、D3 v7、G2 5.x 的真实配置为准。
目录
- 可视化的目标与信息传递模型
- 数据类型到视觉通道的映射
- 比较任务:柱状图与条形图
- 趋势任务:折线图与面积图
- 分布任务:直方图、箱线图与密度图
- 相关任务:散点图与气泡图
- 构成任务:饼图、堆叠与矩形树图
- 高维数据的降维与平行坐标
- 图表选型的决策流程
- 反模式识别与纠偏
- 图表库的工程选型
- 无障碍与色彩无关表达
1. 可视化的目标与信息传递模型
可视化的信息传递可以拆成三段:数据空间 → 视觉空间 → 感知空间。数据空间是原始的多维记录,视觉空间是图形属性的组合(位置、长度、角度、面积、颜色、形状),感知空间是读者大脑中重建的结论。好的可视化让三段的映射损失最小。
衡量一次映射是否有效的标准有三条:准确性(读者能否读出接近真值的数值)、可判别性(两个不同数据点能否被区分)、效率(提取结论需要多长时间)。这三条经常互相冲突——散点图准确性高但读单个值慢,数据表读值精确但看不出整体趋势。
实践中更实用的判断是问一句:读者要回答的具体问题是什么。问题决定了任务类型,任务类型决定了图形通道。把「哪个区域卖得最好」(比较 + 排序)和「销售额怎么随时间变化」(趋势)用同一张图表达,就是典型的任务与图形错配。
另一个常被忽略的约束是数据墨水比(data-ink ratio)。Tufte 提出,图中每一点墨水都应该编码数据,装饰性的渐变、阴影、3D 透视、冗余网格线都在稀释信息密度。工程上最常见的反例是给每个柱子加渐变填充和投影——视觉上更「精致」,但读者的注意力被引向了与数据无关的属性。
需要强调的是,效率不等于信息量最大化。把 20 条序列塞进一张图确实「信息量大」,但读者的提取成本也线性上升。可视化的目标是单位认知成本下的信息增益最大,而不是把数据全部倒出来。这就是为什么交互式图表值得投入:静态图必须一次呈现全部,而交互图可以按需展开细节(悬浮看数值、点击下钻层级),在默认状态下保持简洁。
1.1 三种可视化层次的定位
区分探索性分析(EDA)、解释性呈现(explanatory)、监控性看板(monitoring)三个层次,是避免选型混乱的前提。探索性分析面向分析师自己,追求信息密度和快速迭代,可以容忍粗糙的默认样式(Matplotlib 默认图完全够用)。解释性呈现面向他人,需要叙事结构——标题直接写结论、关键点直接标注、去除无关网格。监控性看板面向持续观察,核心指标是「异常是否能在 3 秒内被发现」,因此需要固定的布局、稳定的颜色语义、明确的阈值线。
三个层次对同一份数据会产出完全不同的图。把 EDA 阶段的图直接贴进汇报是最常见的偷懒,读者面对一张没有标题结论、坐标轴没有单位、颜色没有图例的图,只能自己重新做一遍分析。判断标准很简单:这张图如果离开你的口头讲解,还能被读懂吗。
2. 数据类型到视觉通道的映射
Cleveland 与 McGill 在 1984 年的实验给出了视觉通道编码精度的经典排序:位置 > 长度 > 角度 > 斜率 > 面积 > 体积 > 颜色明度 > 颜色色相。这个排序是图表选型的理论基石——它解释了为什么条形图比饼图精确(长度 vs 角度),为什么气泡图不适合精确比较(面积感知有系统性低估)。
数据本身还有类型之分,映射规则不同:
| 数据类型 | 特征 | 可用通道 | 不可用通道 |
|---|---|---|---|
| 定类 nominal | 无序类别 | 位置、颜色色相、形状 | 长度、大小(无自然序) |
| 定序 ordinal | 有顺序类别 | 位置、明度渐变、大小 | 色相(无序感) |
| 定量 quantitative | 连续数值 | 位置、长度、面积、明度 | 形状、色相 |
| 时序 temporal | 时间轴 | 位置(水平轴优先) | 角度 |
一个高频错误是用色相表达定序数据(如「低/中/高」用红/绿/蓝),这会让读者看不出顺序。正确做法是用同一色相的明度或饱和度渐变。另一个错误是用面积表达定类数据的「重要性」,面积没有自然零点,读者无法建立比例直觉。
通道之间还存在掩蔽效应:当多个通道同时编码不同变量时,强势通道会压制弱势通道。位置是最强势的通道,如果既用位置编码数值又用颜色编码另一个数值,读者几乎只会读位置。因此冗余编码(同一变量同时用位置和颜色表达)只在需要无障碍支持时使用,而不应用来塞进额外维度。
2.1 通道的分离性与格式塔原则
选择通道时还要考虑通道分离性(separability)。位置与颜色是可分离的(可以独立感知),而颜色色相与饱和度是不可分离的(一起感知为一个整体色)。用色相和饱和度分别编码两个变量,读者无法独立读出任何一个。可分离的组合包括:位置 × 颜色、位置 × 形状、大小 × 颜色;不可分离的组合包括:色相 × 饱和度、宽度 × 高度(对矩形而言)。
格式塔原则进一步解释了分组的感知机制。接近性让靠近的元素被视为一组——这决定了分组柱状图内部两根柱子的间距必须小于组间间距,否则读者会把不同组的柱子配成对。相似性让相同颜色/形状的元素被归为一组,这就是为什么系列颜色必须在整个仪表盘内保持一致语义。连续性让读者沿平滑路径追踪,这是折线图有效的原因。闭合性让读者补全不完整的形状,可以用在强调框上。
3. 比较任务:柱状图与条形图
当任务是「比较不同类别的量值」时,柱状图(垂直)与条形图(水平)是首选,因为长度编码的精度仅次于位置。选择垂直还是水平取决于类别标签的长度与数量:类别名长(如中文部门全称)或类别多于 8 个时用水平条形图,标签可以横排而无需旋转。
排序是这类图表的关键增强。未排序的柱状图迫使读者自己做排序运算,而排序后的图让结论直接可见。ECharts 中通过 dataset.sort 或预处理实现:
const option = {
dataset: {
source: [
['区域', '销售额'],
['华东', 4820], ['华南', 3610], ['华北', 2980], ['西南', 1740],
],
// 按销售额降序,排除首行表头
sort: (a, b) => b[1] - a[1],
},
xAxis: { type: 'category' },
yAxis: { type: 'value', name: '万元' },
series: [{
type: 'bar',
encode: { x: '区域', y: '销售额' },
itemStyle: { borderRadius: [0, 4, 4, 0] },
}],
};
基线必须从零开始。柱状图截断 Y 轴是严重误导——长度编码的语义就是「与零的比值」,起点非零会让 2 倍差距看起来像 10 倍。折线图可以截断 Y 轴(因为它编码的是斜率而非长度),柱状图不可以。
分组柱状图用于「两个类别维度 × 一个数值」的场景,例如「各区域各季度的销售额」。此时要把组内间距压到组间距的三分之一以内,让接近性正确地传达分组关系:
const option = {
xAxis: { type: 'category', data: ['Q1', 'Q2', 'Q3', 'Q4'] },
yAxis: { type: 'value' },
// barGap 是组内间距(相对柱宽的比例),barCategoryGap 是组间距
series: [
{ name: '华东', type: 'bar', barGap: '15%', data: [820, 932, 901, 1290] },
{ name: '华南', type: 'bar', data: [620, 732, 701, 934] },
],
};
分组柱状图的序列数上限是 4 组。超过之后柱子变细、图例与柱子的对应关系需要读者来回扫视,此时应改为小倍数图或折线图。另一个陷阱是组数过多时 X 轴标签旋转 45 度——旋转标签会显著降低阅读速度,宁可改成横向条形图。
4. 趋势任务:折线图与面积图
时间序列的首选是折线图。X 轴必须是等距的连续时间轴,而不是把日期当作定类字符串——后者会让不等长的时间间隔看起来等长。ECharts 中应使用 type: 'time' 而非 'category':
xAxis: {
type: 'time',
axisLabel: { formatter: '{MM}-{dd}' },
// 自动选择 tick 间隔,避免密集标签重叠
splitNumber: 6,
}
多条序列时线条数量控制在 5 条以内,超过后改用小倍数图(small multiples)——把每个序列画到独立的小图里共享坐标轴。面积图只在需要强调「累积量」或「占比随时间的结构变化」时使用:堆叠面积图能看出总量与结构,但中间层的趋势难以读出(每层基线都在动),所以不适合比较中间层。
对数坐标是处理跨数量级趋势的利器。当序列从 100 增长到 100000 时,线性坐标会让早期变化压成一条直线。yAxis: { type: 'log', logBase: 10 } 能把乘性增长变成直线,但要注意对数轴不能表示零或负值。
小倍数图(small multiples,又称 trellis)是折线图序列过多时的标准解法。它的做法是把同一坐标系复制成 N 份,每份画一个序列,共享 X 轴范围与 Y 轴范围,让读者在相同尺度下横向比较:
// ECharts 用 grid + 多个 xAxis/yAxis 实现 2x2 小倍数
const grids = [], xAxes = [], yAxes = [], series = [];
for (let i = 0; i < 4; i++) {
const col = i % 2, row = Math.floor(i / 2);
grids.push({ left: `${col * 50 + 5}%`, top: `${row * 50 + 8}%`, width: '42%', height: '34%' });
xAxes.push({ gridIndex: i, type: 'time', show: row === 1 });
yAxes.push({ gridIndex: i, type: 'value', show: col === 0, min: 0, max: 1000 });
series.push({ type: 'line', xAxisIndex: i, yAxisIndex: i, showSymbol: false, data: seriesData[i] });
}
共享 Y 轴范围是关键。如果每个小图各自缩放,读者会看到四条形状相似的曲线,却看不出它们的绝对量级差异——这正是小倍数图要解决的问题。
4.1 时间粒度的聚合与平滑
原始时序数据往往带有高频噪声,直接绘制会呈现锯齿。三种处理方式的取舍不同:移动平均(rolling mean)平滑但会滞后真实拐点;指数加权(EMA)对近期变化更敏感;降采样(LTTB 算法)保留极值点同时减少点数。ECharts 内置 sampling: 'lttb' 可以在保持视觉形状的前提下把点数降到屏幕像素量级:
series: [{ type: 'line', sampling: 'lttb', showSymbol: false, data: raw }]
聚合粒度要与读者的问题匹配。看年度趋势用月粒度,看活动效果用小时粒度,看系统故障用秒粒度。粒度过细会让趋势淹没在噪声里,粒度过粗会抹掉关键拐点。一条实用规则是:屏幕上每个数据点至少占 1 到 2 像素,否则就是无效的过度采样。
5. 分布任务:直方图、箱线图与密度图
分布类问题的核心是「数据的形状是什么样」——集中趋势、离散程度、偏态、多峰。直方图是基础:把连续值分箱后统计频次。分箱数量用 Freedman-Diaconis 规则确定:binWidth = 2 * IQR / n^(1/3),比 Sturges 规则对偏态数据更稳健。
箱线图用五数概括(最小值、Q1、中位数、Q3、最大值)压缩分布,特别适合多组分布并排比较。它能暴露离群点,但看不出多峰——两个双峰分布可能有完全相同的箱线图。小提琴图(violin plot)用核密度估计弥补这一点,代价是读者需要理解密度曲线的含义。
import numpy as np
import matplotlib.pyplot as plt
rng = np.random.default_rng(42)
# 三个不同分布的分组
groups = [
rng.normal(50, 8, 500),
rng.gamma(2.0, 12.0, 500), # 右偏
np.concatenate([rng.normal(35, 4, 250), rng.normal(70, 4, 250)]), # 双峰
]
fig, axes = plt.subplots(1, 2, figsize=(11, 4))
axes[0].boxplot(groups, tick_labels=['A', 'B', 'C'], showfliers=True)
axes[0].set_title('Box plot')
axes[1].violinplot(groups, showmedians=True)
axes[1].set_xticks([1, 2, 3], ['A', 'B', 'C'])
axes[1].set_title('Violin plot')
plt.tight_layout()
plt.savefig('dist.png', dpi=140)
分组 C 的箱线图与 A 几乎一致,但小提琴图会清楚暴露它的双峰结构。这是选择箱线图还是小提琴图的唯一判据:是否关心多峰。
分箱宽度的选择会直接改变直方图的结论。分箱过宽会把双峰糊成单峰,分箱过窄会让每个箱只剩几个点、呈现虚假的锯齿。稳妥做法是同时画 3 到 5 个不同分箱数的版本对照,确认结论稳健。另一个常被忽略的点是直方图的 Y 轴含义:频次(count)和密度(density)是不同的量,样本量不同的两组数据比较时应该用密度而非频次,否则样本多的那组会显得「处处更高」。
5.1 经验累积分布函数
当需要精确比较两组分布的百分位数时,经验累积分布函数(ECDF)比直方图更清晰——它没有分箱参数,单调递增,两组曲线的垂直距离直接对应「在该阈值以下的占比差」:
import numpy as np
import matplotlib.pyplot as plt
a = np.random.default_rng(1).normal(100, 15, 2000)
b = np.random.default_rng(2).normal(112, 18, 2000)
def ecdf(x):
xs = np.sort(x)
ys = np.arange(1, len(xs) + 1) / len(xs)
return xs, ys
plt.figure(figsize=(7, 4.5))
for data, label in [(a, '对照组'), (b, '实验组')]:
xs, ys = ecdf(data)
plt.step(xs, ys, where='post', label=label)
plt.axhline(0.5, color='#999', lw=0.8, ls='--')
plt.xlabel('响应时间 (ms)'); plt.ylabel('累积占比'); plt.legend()
plt.savefig('ecdf.png', dpi=140)
ECDF 的读数方式很直观:在 Y 轴上找 0.5(中位数),横向看两条曲线的 X 值差;或反过来,在某个阈值处横向看两条曲线的占比差。它比「看箱线图的中位数线」提供的信息更多,且不需要假设分布形状。
6. 相关任务:散点图与气泡图
散点图把两个定量变量映射到 X、Y 位置,是发现相关性与聚类的最强工具。它天然支持上千个点,但一旦超过约 5000 点就会过度绘制(overplotting)——密集区域糊成一片,读者看到的密度是假的。对策有三:降低不透明度(opacity: 0.3)、用二维分箱热力图替代、或用 hexbin 聚合。
series: [{
type: 'scatter',
symbolSize: 6,
itemStyle: { opacity: 0.45, color: '#5470c6' },
// 大屏/大数据量下开启渐进渲染
large: true,
largeThreshold: 2000,
}]
气泡图在散点基础上用第三维(面积)编码第三个变量,最多再加颜色编码第四个。但面积感知有系统性偏差,读者会低估大圆与小圆的面积比。经验法则是气泡图只用于「大致看出量级差异」,不用于精确比较。绝对不要在气泡图里做「哪个气泡大 20%」这类判断。
气泡图的符号大小映射必须用面积而非半径。如果直接用数值设置半径,视觉面积会按平方增长,2 倍的数据看起来像 4 倍。ECharts 的 symbolSize 是直径,因此要开平方:
series: [{
type: 'scatter',
data: rows, // rows[i] = [x, y, value]
symbolSize: (val) => Math.sqrt(val[2]) * 2,
}]
趋势线(trend line)和置信区间能显著提升散点图的可读性,但要注意相关不等于因果,也不要用一条直线去拟合明显非线性的关系。当数据呈现分组结构(如两个聚簇)时,整体拟合线会给出误导性的斜率,应该分组着色后分别观察。
7. 构成任务:饼图、堆叠与矩形树图
构成类问题的正确工具排序是:100% 堆叠条形图 > 堆叠柱状图 > 矩形树图 > 饼图/环形图。饼图排名靠后是因为角度编码精度低,且人类对角度的比较能力弱于长度——尤其是扇区数量超过 5 个、或存在相近数值时。
饼图唯一合适的场景是:只有 2 到 3 个类别,且其中一个是明显的多数。此时饼图能快速传达「大部分是 A」的直觉。超过 5 个类别应改用横向条形图或矩形树图。
矩形树图(treemap)用嵌套矩形的面积编码层级构成,适合展示「层级 + 占比」,如磁盘占用、代码仓库行数分布。它的面积感知同样有偏差,但胜在能在一屏内展示上百个节点。ECharts 的 treemap 支持 leafDepth 控制展开层级,避免一次渲染全部节点。
100% 堆叠条形图是构成比较的最优解,它把每组都归一化到 100%,让「结构占比」而非「绝对量」成为比较对象。这里有一个关键取舍:绝对值比较用分组柱状图,占比比较用 100% 堆叠条形图。两者不可混用——堆叠图里读者只能读出每段的高度差,读不出总量。
const option = {
tooltip: { trigger: 'axis' },
xAxis: { type: 'value', max: 100, axisLabel: { formatter: '{value}%' } },
yAxis: { type: 'category', data: ['移动端', '桌面端', '平板'] },
series: [
{ name: '新客', type: 'bar', stack: 'total', data: [42, 31, 25] },
{ name: '老客', type: 'bar', stack: 'total', data: [38, 45, 40] },
{ name: '流失召回', type: 'bar', stack: 'total', data: [20, 24, 35] },
],
};
堆叠顺序也有讲究:把最稳定、方差最小的分段放在底部,因为它构成了其他分段的基线;如果底部段波动大,上面所有分段的视觉位置都会跟着晃,读者会误以为它们也在变。通常按「其他 > 次要 > 主要」从下到上排列,或按业务重要性排序。
8. 高维数据的降维与平行坐标
当维度超过 4 个时,散点图矩阵(SPLOM)和平行坐标是两条主要路径。散点图矩阵把每对变量画成一个小散点图,n 个变量产生 n² 个格子,实用上限约 6 到 8 个变量。平行坐标把每个维度画成一根垂直轴,每个样本是一条穿过所有轴的折线——它适合发现维度间的相关模式(平行则正相关,交叉则负相关),但样本多时线条会缠绕成一片。
// ECharts parallel:4 个维度 + 1 个分类色
const option = {
parallelAxis: [
{ dim: 0, name: '收入' },
{ dim: 1, name: '年龄' },
{ dim: 2, name: '活跃天数' },
{ dim: 3, name: '客单价' },
],
parallel: { left: 60, right: 60, bottom: 40, top: 40 },
series: [{
type: 'parallel',
lineStyle: { width: 1, opacity: 0.15 },
data: samples, // [[12000, 32, 18, 320], ...]
}],
};
降维投影(PCA、t-SNE、UMAP)把高维点压到二维后仍用散点图呈现,是向量检索、聚类分析的标准可视化手段。注意 t-SNE 的簇间距离没有意义,UMAP 保留了更多全局结构但参数敏感。可视化只应作为诊断工具,不要用 t-SNE 图上的距离下结论。
from sklearn.decomposition import PCA
from sklearn.manifold import TSNE
import numpy as np
X = np.load('embeddings.npy') # shape: (n_samples, 768)
# PCA 用于快速线性投影,保留方差最大的两维
pca = PCA(n_components=2, random_state=0)
X_pca = pca.fit_transform(X)
print('前两主成分解释方差比:', pca.explained_variance_ratio_.round(3))
# t-SNE 用于局部结构,perplexity 建议取 5~50,样本多时调大
X_tsne = TSNE(n_components=2, perplexity=30, init='pca',
learning_rate='auto', random_state=0).fit_transform(X)
平行坐标的高维解读有一个实用技巧:按某个维度排序样本。线条缠绕时读者只能看出整体密度,但若按某一维(如收入)排序,就能看出其他维度随它的变化趋势,缠绕也随之变成有结构的束。ECharts 的 parallel 支持 visualMap 给线条按某一维着色,这比纯线束的可读性高得多。维度顺序也影响可读性——把相关的维度放在相邻位置,线条会更平行,模式更易识别。
9. 图表选型的决策流程
把上面的规则收敛成一个可执行的决策树,按顺序问四个问题:
- 分析任务是什么——比较量值、看趋势、看分布、看相关、看构成?
- 变量是什么类型——定类、定序、定量、时序?
- 有多少维度——1 到 2 维直接映射,3 到 4 维加颜色/大小,5 维以上考虑降维或平行坐标。
- 输出介质与读者——大屏(远距离、秒级识别)、报表(近距离、可读精确值)、移动端(窄屏、点击交互)?
对应到具体图表的速查表:
| 分析任务 | 首选 | 次选 | 避免 |
|---|---|---|---|
| 比较量值 | 条形图(排序) | 分组柱状图 | 饼图 |
| 趋势 | 折线图 | 面积图(强调累积) | 柱状图(多序列) |
| 分布 | 直方图 | 箱线图/小提琴图 | 饼图 |
| 相关 | 散点图 | 气泡图 | 折线图 |
| 构成 | 100% 堆叠条形 | 矩形树图 | 3D 饼图 |
| 流向 | 桑基图 | 和弦图 | 折线图 |
| 层级 | 矩形树图 | 旭日图 | 饼图嵌套 |
| 地理 | 分级统计图 | 点密度图 | 普通柱状图 |
10. 反模式识别与纠偏
双 Y 轴是最常见的误导性设计。两条不同量纲的曲线放在同一坐标系,读者会下意识比较斜率,而斜率完全取决于两个轴的缩放比例——调整轴范围就能制造出任意「相关性」。如果两个指标需要同时看,用上下两个共享 X 轴的小图。
3D 图表几乎总是错的。3D 饼图的远端扇区被透视压缩,3D 柱状图的深度维度不编码任何数据。它的唯一价值是「看起来炫」,而这恰好是数据大屏最容易被滥用的地方。
彩虹色阶用于连续量是另一个经典错误。Jet/rainbow 色阶在明度上不是单调的,读者会看到虚假的边界(如青色到黄色之间的跳变)。连续量应使用感知均匀的色阶,如 Viridis、Cividis、或单色相明度渐变。
截断柱状图 Y 轴、面积编码做精确比较、用红绿作为唯一区分(色盲人群占男性约 8%)也都属于需要主动规避的模式。这些反模式的共同特征是:它们让图形看起来更有信息量,但实际降低了可读性。
11. 图表库的工程选型
选库的维度是声明式程度、渲染方式、包体积、扩展能力四项。
| 库 | 范式 | 渲染 | 体积(gz) | 适合场景 |
|---|---|---|---|---|
| ECharts | 配置对象 | Canvas/SVG | ~330KB 全量 | 通用业务图表、大屏 |
| Chart.js | 配置对象 | Canvas | ~70KB | 轻量简单图表 |
| D3 | 数据驱动 DOM | SVG | 按需 | 高度定制、图形语法 |
| G2/G2Plot | 图形语法 | Canvas | ~180KB | 统计图表、声明式 |
| Vega-Lite | 图形语法 JSON | Canvas/SVG | ~250KB | 规格化、可复现 |
| Recharts | React 组件 | SVG | ~120KB | React 生态内嵌 |
判断标准很直接:需求能在现有图表类型里找到 80% 匹配就用配置式库(ECharts/G2),需要发明新图形或做数据驱动的布局算法才上 D3。用 D3 画一个普通柱状图是典型的过度工程,用 ECharts 硬做力导向网络图的定制交互则是另一种浪费。关于声明式图形语法的深入展开,可以对照 D3.js 与图形语法 与 AntV G2 声明式图表体系 两篇。
12. 无障碍与色彩无关表达
可访问性要求图表在不依赖颜色的前提下仍可读。三条落地规则:其一,区分编码(颜色之外再加形状、线型、直接标注);其二,文本与背景对比度达到 WCAG AA 的 4.5:1;其三,为图表提供文本替代,如 <table> 或 aria-label 概括结论。
色盲安全的分类色板推荐 Okabe-Ito 八色(#000000, #E69F00, #56B4E9, #009E73, #F0E442, #0072B2, #D55E00, #CC79A7),它在红绿色盲下仍可区分。前端工程侧的无障碍细节可参考 前端无障碍与国际化实践
。
权衡取舍
| 决策点 | 选项 A | 选项 B | 何时选 A | 何时选 B |
|---|---|---|---|---|
| 表达精度 | 位置/长度 | 面积/角度 | 需要精确比较 | 只需看大致量级 |
| 序列数量 | 折线图叠加 | 小倍数图 | ≤5 条且需要交叉比较 | >5 条或需要独立读数 |
| 数据量 | 全量散点 | 分箱聚合 | <5000 点 | 密集区域糊成一片时 |
| 定制深度 | 配置式库 | D3 手写 | 标准图表类型 | 需要新图形或自定义布局 |
| 色彩 | 多色相分类 | 单色相明度 | 定类数据 | 定序/连续数据 |
| 交互 | 悬浮提示 | 直接标注 | 点密集 | 结论点少、强调可读 |
常见坑清单
- 柱状图 Y 轴不从零开始——长度编码失去比例语义,差距被夸大数倍;柱状图必须零基线。
- 饼图扇区超过 5 个——角度比较精度骤降,读者只能看出最大的那块;改用排序条形图。
- 时间轴用定类字符串——不等长间隔被画成等长,趋势失真;用
type: 'time'连续轴。 - 双 Y 轴制造假相关——两条曲线斜率对比完全由轴缩放决定;改为共享 X 轴的上下双图。
- 连续量用彩虹色阶——明度非单调产生虚假边界;改用 Viridis 或单色相渐变。
- 散点图超量绘制——5000 点以上密集区糊成色块,密度失真;降低不透明度或改热力分箱。
- 堆叠图中间层趋势误读——每层基线在动,中间层斜率无意义;比较中间层改小倍数图。
- 颜色作为唯一区分手段——色盲读者无法区分序列;叠加形状/线型/直接标注。
- 对数轴用于含零数据——零与负值无法表示,会出现断点或异常;先确认数据全为正。
- 大屏用细线小字——远距离观看下 1px 线与 12px 字不可读;大屏字号至少 24px。
小结
图表选型的本质是把分析任务映射到视觉通道,而映射的质量由编码精度决定:位置和长度最精确,面积和角度最模糊。比较用条形、趋势用折线、分布用直方/箱线、相关用散点、构成用堆叠——这张对应表覆盖了 90% 的业务场景,剩下的 10% 才需要图形语法层面的定制。
工程落地上,先按「标准图表能否满足」筛选库,能就用 ECharts 或 G2 这类配置式方案,不能才引入 D3。同时把反模式清单当作代码评审的一部分——双 Y 轴、截断基线、彩虹色阶这类问题一旦进入生产报表,纠错成本远高于写代码时避免。
下一步建议按顺序阅读:先看 视觉编码与感知原理 补齐通道精度的实验依据,再按你的技术栈进入 ECharts 图表工程实战 或 AntV G2 声明式图表体系 ,最后在 仪表盘与数据大屏设计 中把这些单图组合成完整的信息界面。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。