第4章 数据准备与清洗
可视化的时间 80% 花在数据上:口径对齐、缺失处理、长宽表转换。
本章学习目标
- 能检查并处理缺失值、异常值、重复值
- 能做数值归一化与分组聚合(分箱)
- 能区分宽表与长表并按工具需要转换
- 能对齐指标口径,保证图与结论一致
本章导览
核心内容
4.1
常见数据问题
五类高频问题
| 问题 | 表现 | 处理方式 |
|---|---|---|
| 缺失值 | 空、NULL、— | 删除 / 填均值中位数 / 填业务默认值 |
| 异常值 | 明显离群的极值 | 先确认是否录入错误,再用箱线图规则处理 |
| 重复行 | 同一条记录出现多次 | 按主键去重(保留最新) |
| 口径不一致 | 同名指标算法不同 | 统一口径并写在图注里 |
| 单位不一致 | 元/万元、% / 小数 | 统一单位后再画 |
4.2
清洗与转换
Excel、Python、SQL 三套工具做的是同一件事,思路完全一致。
4.2.1
缺失与异常处理
# Python:缺失值统计与填充
df.isna().sum() # 每列缺失数
df['sales'] = df['sales'].fillna(df['sales'].median())
# 异常值:按 1.5 倍四分位距识别
q1, q3 = df['sales'].quantile([0.25, 0.75])
iqr = q3 - q1
df = df[(df['sales'] >= q1 - 1.5 * iqr) & (df['sales'] <= q3 + 1.5 * iqr)]-- SQL 等价写法
SELECT province, coalesce(sum(sales), 0) AS sales
FROM orders GROUP BY province;4.2.2
聚合、分箱与归一化
# 分组聚合
g = df.groupby('province', as_index=False)['sales'].sum().sort_values('sales', ascending=False)
# 数值分箱(把连续值变成有序类别)
df['price_band'] = pd.cut(df['price'], bins=[0, 50, 100, 500, 1e9],
labels=['<50', '50-100', '100-500', '>500'])
# 归一化(0~1),用于气泡大小、颜色深浅
df['s_norm'] = (df['sales'] - df['sales'].min()) / (df['sales'].max() - df['sales'].min())4.2.3
宽表与长表
宽表:一行一个对象,多列是不同指标;长表:一行一个"对象+指标+值"。多数图表库(seaborn、pyecharts 多系列)更适长表。
# 宽表 → 长表
long = df.melt(id_vars=['province'], value_vars=['2024', '2025'],
var_name='year', value_name='sales')
# 长表 → 宽表
wide = long.pivot_table(index='province', columns='year', values='sales')💡 判断标准:要画的系列在列名里 → 需要转长表;要画的系列在行里 → 需要转宽表。
本章小结
本章解决"图画不出来或画出来对不上"的根源问题:先识别缺失、异常、重复、口径与单位五类问题,再用填充/箱线图规则/去重/统一口径处理;随后讲分组聚合、分箱、归一化三个高频转换,最后讲清宽表与长表的区别与互转方法。
思考与练习 先自己做,再点开答案
01
销售额有少量极端值,画柱状图时把其它省份都压扁了,怎么处理?
查看参考答案
先核对是否为录入错误;确认是真实业务极值后有两种做法:① 改用对数刻度(并在轴上明确标注);② 拆成"Top N + 其它"合并展示。不要直接删掉真实数据。
02
什么情况下需要把宽表转成长表?
查看参考答案
当要画的多个系列存放在多个列里时(如 2024 列、2025 列),绘图库通常要求"系列"作为一列的取值,就需要 melt 成长表。
03
两张图的指标名称一样但数值差很多,可能是什么原因?
查看参考答案
口径不一致:统计范围(含税/不含税)、时间窗口(自然月/滚动 30 天)、去重规则(按用户/按订单)不同。必须先统一口径,并在图注中写清楚。