跳到主要内容

数据可视化基础

什么是数据可视化

数据可视化是通过图形化手段,将数据转换为可视元素(位置、长度、颜色、形状等),利用人类视觉系统的高效感知能力来理解数据中的模式、趋势和异常。

可视化的核心概念

数据类型

数据类型说明示例适合图表
定量数据(Quantitative)可度量的数值温度、价格、销量折线图、柱状图、散点图
分类数据(Categorical)离散的类别性别、地区、产品类型柱状图、饼图、环形图
时间数据(Temporal)时间序列日期、时间戳折线图、面积图、甘特图
地理数据(Geographical)地理坐标经纬度、行政区地图、热力图
关系数据(Relational)节点与边社交网络、组织架构力导向图、桑基图
层级数据(Hierarchical)树形结构文件系统、分类目录树图、旭日图

视觉编码(Visual Encoding)

视觉编码是将数据属性映射到视觉通道(Visual Channel)的过程,是可视化设计的核心:

视觉通道的精确度排序(由高到低):

排名定量数据分类数据
1位置(最精确)位置
2长度颜色色相
3角度/斜率形状
4面积纹理
5颜色饱和度/明度尺寸
设计原则
  • 定量数据优先用位置和长度编码(如柱状图、折线图)
  • 分类数据优先用颜色色相和形状编码
  • 避免用面积和角度表示精确数值(人类感知不准确)

图表类型选择

场景推荐图表避免使用
比较各类别数值柱状图、条形图饼图(>5 类)
展示时间趋势折线图、面积图柱状图(数据点多时)
展示部分占整体比例饼图(≤5 类)、堆叠图3D 饼图
展示两变量关系散点图折线图(非时序)
展示数据分布直方图、箱线图柱状图
展示层级关系树图、旭日图柱状图
展示地理数据地图、热力图柱状图

数据处理与转换

可视化前通常需要对原始数据进行预处理:

// 常见的数据转换操作

// 1. 分组聚合
interface SalesRecord {
category: string;
amount: number;
date: string;
}

function groupBy<T>(data: T[], key: keyof T): Map<string, T[]> {
return data.reduce((map, item) => {
const groupKey = String(item[key]);
const group = map.get(groupKey) || [];
group.push(item);
map.set(groupKey, group);
return map;
}, new Map<string, T[]>());
}

// 2. 数据归一化 (Min-Max Normalization) — 将值映射到 [0, 1] 区间
function normalize(values: number[]): number[] {
const min = Math.min(...values);
const max = Math.max(...values);
const range = max - min;
if (range === 0) return values.map(() => 0.5);
return values.map((v) => (v - min) / range);
}

// 3. 数据分箱(Binning)— 将连续数据离散化为区间
function bin(values: number[], binCount: number): { range: string; count: number }[] {
const min = Math.min(...values);
const max = Math.max(...values);
const binWidth = (max - min) / binCount;

const bins = Array.from({ length: binCount }, (_, i) => ({
range: `${(min + i * binWidth).toFixed(1)} - ${(min + (i + 1) * binWidth).toFixed(1)}`,
count: 0,
}));

values.forEach((v) => {
const index = Math.min(Math.floor((v - min) / binWidth), binCount - 1);
bins[index].count++;
});

return bins;
}

比例尺(Scale)

比例尺是可视化中最重要的概念之一,它建立了数据空间视觉空间的映射关系:

// 线性比例尺:等比例映射数值到像素
function linearScale(
domain: [number, number], // 数据范围 [最小值, 最大值]
range: [number, number] // 视觉范围 [起始像素, 结束像素]
): (value: number) => number {
const [d0, d1] = domain;
const [r0, r1] = range;
const ratio = (r1 - r0) / (d1 - d0);
return (value: number) => r0 + (value - d0) * ratio;
}

// 对数比例尺:适合数据范围跨越多个数量级(收入分布、地震强度等)
function logScale(
domain: [number, number],
range: [number, number]
): (value: number) => number {
const [d0, d1] = domain;
const [r0, r1] = range;
const logD0 = Math.log10(d0);
const logD1 = Math.log10(d1);
return (value: number) => {
const t = (Math.log10(value) - logD0) / (logD1 - logD0);
return r0 + t * (r1 - r0);
};
}

// 序数比例尺:将离散类别映射到离散位置
function ordinalScale<T>(domain: string[], range: T[]): (value: string) => T | undefined {
const map = new Map(domain.map((d, i) => [d, range[i % range.length]]));
return (value: string) => map.get(value);
}

// 使用示例
const xScale = linearScale([0, 100], [0, 800]); // 数据 0-100 → 像素 0-800
console.log(xScale(50)); // 400

const colorScale = ordinalScale(
['Apple', 'Google', 'Microsoft'],
['#ff6384', '#36a2eb', '#ffce56']
);
console.log(colorScale('Apple')); // '#ff6384'

色彩理论

配色方案类型

类型场景示例
连续色(Sequential)单一数值从低到高热力图、密度图
发散色(Diverging)有中心值的正负数据温差图、涨跌幅
分类色(Categorical)不同类别多系列折线图、饼图

色彩无障碍

色盲友好
  • 约 8% 的男性有色觉障碍
  • 避免仅用红绿区分数据
  • 同时使用颜色 + 形状/纹理/标签
  • 工具:Color Oracle、Chrome DevTools 色觉模拟
// 色盲友好的分类色板
const colorBlindSafePalette = [
'#0072B2', // 蓝色
'#E69F00', // 橙色
'#009E73', // 绿色
'#F0E442', // 黄色
'#56B4E9', // 浅蓝
'#D55E00', // 深橙
'#CC79A7', // 粉色
'#000000', // 黑色
];

前端可视化技术栈

技术层级优势劣势适用场景
SVG底层DOM 事件、无损缩放大数据量性能差交互式图表、图标
Canvas 2D底层高性能、像素操作无 DOM 事件大数据量、动画
WebGL底层GPU 加速、3D学习曲线高3D 可视化、超大数据
D3.js灵活性极高学习曲线高定制化图表
ECharts开箱即用、功能丰富定制化受限快速开发
Three.js强大的 3D 能力包体积大3D 可视化
RechartsReact 组件声明式、易上手功能有限React 项目
AntV套件G2/G6/L7 生态完整配置复杂蚂蚁系

可视化设计原则

1. 数据墨水比(Data-Ink Ratio)

Edward Tufte 提出的概念:最大化数据墨水比,去除非数据元素。

图表中用于展示数据的"墨水"占总"墨水"的比例应尽可能高。

❌ 反面示例:3D 饼图、过度装饰、网格线过多 ✅ 正面示例:简洁的柱状图、清晰的标签

2. 格式塔原则

原则说明可视化应用
临近性距离近的元素被视为一组分组柱状图
相似性相似的元素被视为一组颜色编码
封闭性倾向看到封闭形状面积图
连续性倾向沿着曲线/直线看折线图
图底关系区分前景与背景高亮/灰化

3. 常见可视化陷阱

避免这些错误
  1. 截断 Y 轴:不从 0 开始,夸大差异
  2. 双 Y 轴误导:两个 Y 轴刻度不同,暗示不存在的相关性
  3. 3D 效果:透视导致面积/角度失真
  4. 饼图滥用:超过 5 个类别难以比较
  5. 彩虹色:色盲不友好,且无法表示有序数据

常见面试问题

Q1: SVG 和 Canvas 如何选择?

答案

维度SVGCanvas
渲染方式保留模式(DOM 树)即时模式(像素绘制)
事件处理原生 DOM 事件需手动实现命中检测
性能元素 < 1000可渲染数万图形
缩放无损缩放(矢量)放大失真
动画CSS/SMIL/WAAPIrequestAnimationFrame
可访问性支持(ARIA、title)不支持
适用场景图标、简单图表、交互多大数据量、动画、游戏

选择建议:

  • 数据量 < 1000、需要丰富交互 → SVG
  • 数据量 > 1000、频繁动画更新 → Canvas
  • 3D 场景、超大数据量 → WebGL
  • 详见 Canvas 与 SVG

Q2: 什么是视觉编码?如何选择合适的视觉通道?

答案

视觉编码是将数据维度映射到图形的视觉属性。选择原则:

  1. 精确度优先:定量数据用位置 > 长度 > 角度 > 面积
  2. 类别区分:分类数据用颜色色相 > 形状 > 纹理
  3. 认知负荷:同时使用的视觉通道不宜超过 3-4 个
  4. 冗余编码:重要信息用多种通道同时编码(如颜色+标签)

Q3: 如何处理可视化中的大数据量?

答案

  1. 数据层:聚合(分箱、采样)、降维、LOD(层次细节)
  2. 渲染层:Canvas/WebGL 替代 SVG、离屏渲染、Web Worker 数据处理
  3. 交互层:渐进式加载、视窗内渲染、交互时降低精度
  4. 详见 大数据量渲染优化

Q4: 可视化中的色彩设计要注意什么?

答案

  1. 语义一致:红色=危险/下跌、绿色=安全/上涨(注意中西文化差异)
  2. 色盲友好:避免红绿对比,使用蓝橙等安全色对
  3. 对比度:背景与前景对比度 ≥ 4.5:1(WCAG AA)
  4. 数量限制:分类色 ≤ 8 种,太多颜色无法区分
  5. 连续色:单色相渐变(浅蓝→深蓝)而非彩虹色

Q5: 什么是数据墨水比?为什么重要?

答案

数据墨水比 = 用于展示数据的图形元素 / 图表中所有图形元素。Tufte 主张最大化此比例,移除不传达信息的装饰元素(如 3D 效果、多余网格线、装饰性背景)。在实际开发中,这意味着:删除不必要的网格线、简化图例、去掉 3D 效果、让数据成为视觉焦点。

Q6: 前端可视化技术栈如何选型?

答案

Q7: 解释比例尺的概念和类型

答案

比例尺建立了数据空间到视觉空间的映射。常用类型:

  • 线性比例尺:数值等比映射,最常用(柱状图高度、折线图坐标)
  • 对数比例尺:跨数量级数据(股价、地震强度)
  • 序数比例尺:类别 → 颜色/位置
  • 时间比例尺:日期 → 像素位置
  • 幂比例尺:面积映射(气泡图,面积 ∝ 数据²)

Q8: 如何保证可视化的可访问性?

答案

  1. 不仅靠颜色:同时提供形状、纹理、标签
  2. 文字替代:图表添加 aria-label<title><desc>
  3. 键盘导航:图表支持 Tab 键和方向键操作
  4. 对比度:前景色与背景色对比度 ≥ 4.5:1
  5. 数据表格:提供图表数据的文本表格替代
  6. 屏幕阅读器:SVG 添加 role="img" 和 aria 属性

相关链接