数据整理与可视化:从一堆凌乱的记录到一张可信的图
同一批数据,只要把纵轴起点从 0 改成 90,5 个百分点的差距就能看起来像 5 倍。数据整理与可视化既是让你把故事讲清楚的工具,也可能是别人用来骗你的武器——这一页教你把两者都握在手里。
一、数据不是数字:先把变量认清楚
假设你在奥克兰(Auckland)的一所中学做了一项调查,问了 60 名 Year 9 学生两个问题:「你今天怎么来上学?」和「路上花了多少分钟?」拿回来的结果长得像这样:
- 通勤方式:步行、公交、骑车、家长开车、步行、公交……
- 通勤时间:12、25、8、40、18、14、22……
这样一堆没有加工过的记录,叫原始数据(raw data)。在动手算任何东西之前,先要认清三样东西:
- 观测单位(individual / case):被记录的对象。这里是每一名学生。
- 变量(variable):在每个对象身上记录下来的特征。这里是「通勤方式」和「通勤时间」。
- 数据(data):所有观测单位在某个变量上取到的值的全体。
为什么「类型」这一步不能跳过?因为你不能对「步行」求平均。数据的类型决定了哪些整理方式、哪些图形、哪些统计量是合法的。常见的分类是这样的:
graph TD A["变量 variable"] --> B["类别型 categorical:取值是标签"] A --> C["数值型 numerical:取值是数字"] B --> B1["名义型 nominal:通勤方式、血型"] B --> B2["有序型 ordinal:满意度、年级等级"] C --> C1["离散型 discrete:兄弟姐妹人数、每周去超市次数"] C --> C2["连续型 continuous:通勤时间、身高、下午三点的气温"]
换成新西兰(New Zealand)日常生活中的例子,你会更容易记住这几类:
- 类别型(categorical):取值是标签。其中「满意度:很不满/一般/满意/很满意」有自然的先后顺序,叫有序型(ordinal);「通勤方式」「最喜欢的橄榄球队」没有顺序,叫名义型(nominal)。
- 数值型(numerical) 中的离散型(discrete):取值是一个一个分开的数,中间没有别的东西,比如「你有几个兄弟姐妹」。
- 数值型 中的连续型(continuous):理论上可以取到任意精度,比如通勤时间(15.3 分钟、15.34 分钟都说得通)、身高、气温。
一个很实用的判据是问自己两句话:「能不能算出一个有意义的平均?」「能不能说出一个有意义的差?」 通勤时间可以平均,也可以说「多花了 7 分钟」;通勤方式两样都不行,所以它只能是类别型。
还有一点容易忽略:类型不只由问题决定,也由你怎么记录决定。
同一个问题「你最近一周运动了几次?」,可以记录成哪几种变量类型?
- 记录具体次数(0、1、2、3……):离散数值型。
- 记录成「从不/1–2 次/3–4 次/5 次以上」:有序类别型——有顺序,但类别之间的距离不一定相等。
- 改问「每周运动多少小时」并允许填 3.5:连续数值型。
所以拿到一张数据表时,第一件事是看清表头是怎么记录的,而不是急着画图。
二、整理:频数表与分组
整理的第一步,是把「一堆记录」变成「一张能一眼看懂的清单」。
如果变量是类别型的,最有效的办法就是数一数每一类出现了多少次。这个次数叫频数(frequency),数数的过程可以先用划线记数(tally)。把类别和频数排在一起,就是一张频数表(frequency table)。
| 通勤方式 | 频数 | 相对频率 | 饼图对应的圆心角 |
|---|---|---|---|
| 步行 | 9 | 0.15 | 54° |
| 骑车 | 6 | 0.10 | 36° |
| 公交 | 24 | 0.40 | 144° |
| 家长开车 | 21 | 0.35 | 126° |
| 合计 | 60 | 1.00 | 360° |
右边两列其实是同一件事的两种说法:相对频率(relative frequency)告诉你「占多大比例」,圆心角告诉你「如果要画饼图,这一块该切多大」。为什么一定要有相对频率?因为如果另一所学校只调查了 30 人,其中 12 人坐公交,你拿 24 和 12 直接比较是不公平的;比较 0.40 和 0.40 才公平。
数值型变量怎么办?如果取值不多(比如兄弟姐妹人数只有 0、1、2、3),直接按取值列频数表就行。但通勤时间有 60 个几乎各不相同的数字,一个一个数毫无意义——于是我们把数值轴切成若干首尾相接的区间,每个区间叫一个组(class),区间的宽度叫组距(class interval / bin width),落在某一组里的观测个数就是这一组的频数。
分组有几条经验规则:
- 不重不漏:组与组首尾相接,不重叠也不留缝。
- 组距一致:宽度相同,柱子的高度才可以直接比较。
- 组数取 5–12:太少会把分布压成一块砖,太多会把结构碎成噪声。
- 边界取整齐的数(0、10、20、30 分钟),并明确约定边界上的数据归哪一组;常用左闭右开 :正好 10 分钟的学生归入 ,不归入 。
这里有一条要老实承认的原则:分组会丢信息。一旦把 12 分钟和 19 分钟塞进同一个 ,你就再也看不出它们其实差 7 分钟。所以数据量不大时,最好把保留每个数据真实位置的点图(dot plot)和直方图一起看。下面这个实验台就是让你亲手体会这一点的。
同一批数据,在不同组距下可以呈现出完全不同的「形状」:
- 组距太大(比如 60 个通勤时间只用 2 根柱子):分布被压扁,什么都看不出来。
- 组距太小(柱子高度大多是 0 和 1):锯齿状的随机起伏会被误读成「结构」。
正确做法是用两三个不同组距各画一次,只有当结论在多个组距下都成立时,才可以放心写进报告。如果结论随组距变化而翻转,就诚实地写:「该结论依赖于分组方式」。
相对频率 = 频数 ÷ 观测总数 = 9 ÷ 45 = 0.20,也就是 20%。选 5 是把除法算反了(总数 ÷ 频数);0.45 把总数当成了分子;0.09 是把 9 误当成了百分数。
三、选图:让图形匹配数据的类型
任何一张统计图,本质上都是把数字「映射」成视觉特征:位置、长度、角度、面积、体积、颜色深浅。而人眼判读这些特征的精度是有高低顺序的:
位置(数轴上的点)≈ 长度(柱子的高)> 角度(扇形)> 面积 > 体积 > 颜色深浅
这条顺序解释了为什么点图、直方图、条形图通常最可靠,而 3D 饼图几乎总是最差的:读者要用被透视扭曲的角度去估计比例。所以选图时先看变量类型,再看你想让读者回答哪个问题。
graph TD Q["你要显示什么?"] --> A["一个类别型变量的分布"] Q --> B["一个数值型变量的分布"] Q --> C["两个数值型变量之间的关系"] Q --> D["某个量随时间的变化"] A --> A1["条形图 bar chart;类别很少时可用饼图 pie chart"] B --> B1["点图 dot plot(数据少)/直方图 histogram(数据多)/箱线图 box plot(要比较多组)"] C --> C1["散点图 scatter plot"] D --> D1["折线图 line graph(时间间隔等距)"]
条形图(bar chart)——类别型变量:每一类一根柱子,柱与柱之间留空隙,高度表示频数或百分比。把柱子按高低排序,读者比较起来会快很多。
直方图(histogram)——数值型分组数据:横轴是连续的数值区间,柱与柱相接,因为区间之间没有间断。它最擅长显示分布的形状:左右对称(symmetric)、向右拖出长尾的右偏(right-skewed)、出现两个峰的双峰(bimodal)。注意:直方图不能用来画类别型数据——「步行」和「公交」之间没有中点,接不起来。
点图(dot plot)——数据量不大时,每个观测画一个点,堆在数轴上。它保留每个数据的真实位置,最适合「看见原始数据」。
饼图(pie chart)——只在一种情况下有用:显示一个整体被分成几部分,而且类别很少(4–6 个)、占比差别明显。类别一多、差别一小,扇形角度就无法辨认了,这时换成条形图几乎总是更好。另外,气温、成绩这类不能相加成整体的量根本不能用饼图。
折线图(line graph)——横轴是时间且间隔一致时,用线段连接相邻时间点来显示趋势。前提是数据确实连续、没有大段缺失,否则线段会「编造」出一段不存在的上升或下降。
散点图(scatter plot)——两个数值型变量,每个观测是一个点 。用它看关系: 增大时 也增大叫正相关(positive correlation),反过来叫负相关(negative correlation),看不出规律叫无相关;还要留意非线性的形状(比如先升后降),一条直线会把它掩盖掉。
箱线图(box plot)——用五个数概括一个数值型变量的分布,最适合并排比较好几组数据(例如四个班级的通勤时间),细节见第四节。
无论选哪种图,一张负责任的图必须自带这四样东西:
- 标题,说明「这张图在讲什么」;
- 两条轴各自的名字与单位(分钟?次数?百分比?纽元?);
- 必要的刻度值,而且纵轴是否从 0 开始要能一眼看出;
- 数据来源与样本量(例如:来源:本校 Year 9 通勤调查,n = 60)。
新西兰统计局(Stats NZ)等机构发布的图表都遵循这套规范,因为缺了任何一项,读者就无法独立判断你的结论是否站得住。
月份有固定顺序,读者关心的是「升还是降、什么时候最冷」,折线图用线段把这个顺序直接画了出来。选项 0 逻辑不成立:气温不能相加成 100%,画饼图没有意义。选项 3 的条形图虽然能看出谁高谁低,但打乱时间顺序后就看不出季节的起伏节奏了。散点图倒是可以用来研究「气温与某量」的关系,但这里只有一个随时间的变量。
四、中心与离散:用很少的几个数把分布说清楚
整理一批数值型数据时,最常要回答两个问题:「数据大致在哪儿?」和「数据散得有多开?」前者叫中心(centre),后者叫离散程度(spread)。只报中心不报离散,等于只说了一半。
三个常用的中心量:
- 平均数(mean):所有数据加起来除以个数。它是数据的「平衡点」——每个数据与平均数的差(偏差)加起来正好为 0。
- 中位数(median):把数据从小到大排好,位置正中的那个数;若个数是偶数,取正中两个数的平均。
- 众数(mode):出现次数最多的值。它是唯一对类别型变量也适用的中心量(例如「最常被选中的通勤方式是公交」)。
什么时候用哪一个? 关键要看数据里有没有离群值(outlier)——明显远离其他大多数观测的值。
平均数把每一个数据都算了进去,这既是优点也是弱点:它会被长尾拉走。看这 10 名学生的每周运动时长(小时):
平均数是 10 小时,可没有一个学生接近 10 小时;中位数是 小时,一听就像「典型学生」。那个 61(也许是校队运动员,也许是记录时多打了一个数字)把平均数拽向了它自己。这就是为什么新西兰的房价与收入报告常常报中位数:少数几百万纽元(NZD)的豪宅会把平均数抬得离普通家庭非常远。
离散程度常用的量:
- 极差(range) = 最大值 − 最小值。算得快,但完全由两个极端的值决定,很脆弱。
- 四分位数(quartiles):把排序后的数据四等分, 是第 25% 位置的数, 就是中位数, 是第 75% 位置的数。
- 四分位数间距(interquartile range, IQR) :中间一半数据的宽度,不受两端极端值的影响。
五数概括(five-number summary):最小值、、中位数、、最大值——这五个数正好画成一张箱线图(box plot):箱子的两端是 和 ,箱内横线是中位数,两端的须伸到 1.5 倍 IQR 范围内最远的观测,超出这个范围的点单独画成离群值。
提醒一句:不同教材和软件对四分位数的取法不完全相同(「含中位数」与「不含中位数」两种惯例),小数点后会出现微小差别。重要结论通常不受影响,但报告时要说明你用的是哪一种。
为什么离散和中心一样重要? 两个班某次数学测验的平均分都是 65 分,但 A 班的分数集中在 60–70 分之间,B 班一半学生 30 分、一半学生 100 分。教学上的含义完全不同:A 班需要一个统一的推进节奏,B 班需要分层辅导。只看平均分,你什么也看不出来。
(同样的道理还有一句老话:平均水深 1 米的河,一样能把人淹死。)
一句话的选用规则:分布大致对称、没有离群值 → 用平均数配极差或标准差;分布偏斜或有离群值 → 用中位数配 IQR。 报告时把中心量和离散量一起给出,才算对分布负责。
平均数 10 小时被 61 拉高了,没有一个学生接近它,用它描述「典型」会严重误导。中位数 4.5 小时更可靠。选项 2 的极差描述的是离散程度,不是中心位置,而且它同样被 61 支配。选项 3 也不成立:这组数据的众数根本不唯一(3、4、5 都出现两次),而且「典型水平」本来就应该用中心位置的量来回答。完整、负责任的报告应该是:中位数约 4.5 小时,IQR 约 2.5 小时,并说明存在一个 61 小时的离群值。
五、图会骗人:可视化中的陷阱与自保
数据可视化既是工具,也可以是武器。下面清单里的每一种手法,在真实的新闻、广告和政治宣传中都出现过。
先记住最有用的一条区分:
- 比较「长度」的图(条形图、直方图):纵轴必须从 0 开始。 因为读者是在比长度,截断纵轴会让长度之比与数值之比不再一致。
- 比较「变化」的图(折线图):允许不截断纵轴,但必须在图上明确标出起点。因为这时读者关心的是坡度和转折,而不是绝对长度。
这条区分之所以关键,是因为很多人把「折线图可以截断」的习惯错误地搬到了条形图上。
常见的陷阱:
- 截断纵轴(truncated axis):条形图纵轴从 95 开始,96 与 91 的两根柱子看起来可以差好几倍。
- 面积或体积夸大:用一个小图标代表 10 人,再用一个「宽两倍、高两倍」的图标代表 40 人——面积变成了 4 倍,读者的眼睛被骗了两次。
- 3D 效果与透视:3D 饼图里靠前的扇形在透视下显得更大,角度再也无法可靠估读。
- 双纵轴(dual axis):给两条曲线各自拉伸刻度,几乎任何两组数据都能被摆成「看起来一起涨」。
- 挑选时间段:只画对自己有利的年份区间,把不利的部分截掉。
- 不一致的组距:直方图里有一组的宽度是其他组的两倍,高度却按频数画——柱子的面积就不再与频数成比例。
- 没有基数:「增长了 200%」听起来很多,可从 2 例到 6 例也正好是 200%。
- 抽样偏差(sampling bias):图再漂亮,如果问卷只发在运动队的群里,结论就只能适用于运动队成员。图不会修好坏数据。
最后还有一种最隐蔽的误导,它甚至不算「画图技巧」,却天天出现:用平均数掩盖整个分布。 第四节里两个平均分都是 65 的班级,就是最好的例子。
作为读者,看到一张图时问自己四个问题:
- 纵轴的起点在哪?单位是什么? 柱子的长度与数值成比例吗?
- 样本是谁、有多少(n = ?)? 这个结论能推广到谁身上?
- 绝对数量是多少? 百分比之外的原始数字,常常会改变整个故事。
- 换一种画法,结论还在吗?(换个时间段、换成中位数、换成条形图再试一次。)
作为作者,准则更简单:画一张你愿意让别人用同一份数据来反驳你的图。 用真实的刻度、说明组距、给出 n 和数据来源,并主动说出结论的局限。
柱子表达的是长度,而长度之比必须与数值之比一致。当纵轴从 90(或任意非 0 值)开始时,96 与 91 的柱长之比会变成 6 : 1,5 个百分点的小差异就被画成了巨大差异。正确的改法是把纵轴拉回 0(此时两柱几乎一样高),或者干脆用点图、直接标数字。选项 2 不成立:饼图的角度在类别接近时同样难以分辨,再叠上 3D 透视只会更糟。样本量也不会让柱子自动变高。
六、总结:把一次数据整理走完一个循环
把前面五节串起来,你做的其实是同一件事,一遍又一遍——新西兰的课程里常把这个循环叫做统计调查循环(statistical enquiry cycle),用它的五个英文步骤首字母简称 PPDAC。
graph TD P["问题 Problem:我们到底要回答什么?"] --> Pl["计划 Plan:调查谁、记录哪些变量、怎么记录?"] Pl --> D["数据 Data:收集、清理、编码(类型在这里定型)"] D --> A["分析 Analysis:频数表、分组、选图、中心与离散"] A --> C["结论 Conclusion:用数据说话,并说明局限"] C --> P
这一页走下来,你的工具箱里有:
- 认变量:类别型(名义/有序)还是数值型(离散/连续)?类型决定哪些操作是合法的。
- 整理:类别型 → 频数表与相对频率;数值型 → 分组与频数表,并且牢记分组会丢信息。
- 选图:一个类别变量 → 条形图;一个数值变量 → 点图/直方图;两个数值变量 → 散点图;随时间变化 → 折线图;需要比较多组 → 箱线图。
- 描述:中心(平均数/中位数/众数)+ 离散(极差/IQR)。分布偏斜或有离群值时,优先用中位数配 IQR。
- 反思:纵轴起点、单位、样本量、绝对数量——这张图有没有在骗我?
下面这段代码把其中好几步自动化了:它从一列原始数据生成分组频数表、算出五数概括与 IQR,并自动标出离群值。你可以把数据换成自己班的调查结果。
import statistics as st
times = [8, 25, 15, 40, 12, 18, 22, 9, 31, 17, 26, 14, 20, 35, 11]
# 1) 分组频数表:组距 10 分钟,区间为 [10k, 10k+10)
groups = {}
for t in times:
lo = t // 10 * 10
groups[lo] = groups.get(lo, 0) + 1
for lo in sorted(groups):
print(f"[{lo:>2},{lo + 10:>2}) {'#' * groups[lo]} {groups[lo]}")
# 2) 五数概括与 IQR
q1, med, q3 = st.quantiles(times, n=4, method="inclusive")
iqr = q3 - q1
print("min, max =", min(times), max(times))
print("Q1, median, Q3 =", q1, med, q3)
print("mean = " + str(round(st.mean(times), 2)) + " IQR = " + str(iqr))
# 3) 用 1.5 x IQR 判据标出离群值
low, high = q1 - 1.5 * iqr, q3 + 1.5 * iqr
outliers = [t for t in times if t < low or t > high]
print("outliers =", outliers)数据整理与可视化不是「把数字画得漂亮」,而是一条链:
问题 → 变量类型 → 整理 → 图形 → 描述 → 反思
链条上任何一环偷懒,结论都会松动。反过来,只要每一环都交代清楚(用哪种组距、n 是多少、纵轴从哪里开始、中位数还是平均数),你的图就不只是好看,而是可信。