7.2 折线图、柱状图、散点图与分布图
对象模型告诉我们在哪里画,分析问题决定画什么。每一种图表都对数据结构作出声明:折线声明观测相邻,柱形从共同基线比较长度,散点用成对位置编码变量,直方图则把连续变量放入区间。
让视觉编码匹配比较任务
折线图(line plot)适合有意义的有序序列,最常见的是时间:
daily = daily.sort_values("date")
fig, ax = plt.subplots(layout="constrained")
ax.plot(daily["date"], daily["orders"], marker="o")
ax.set(
title="每日完成订单数",
xlabel="日期",
ylabel="订单数(笔)",
)必须先按顺序变量排序。按任意行顺序连接会制造一条不存在的路径。还要决定缺失日期的含义:直接删除会让相邻日期跨过缺口连接,可能暗示采集中断期间仍连续变化。根据业务过程,可以补全日历并留下断线、填入确有含义的零,或注释采集缺失。
柱状图(bar plot)使用共同基线上的长度比较类别:
region_sales = (
orders.groupby("region")["amount"]
.sum()
.sort_values()
)
fig, ax = plt.subplots(layout="constrained")
ax.barh(region_sales.index, region_sales.values)
ax.set(
title="各区域已完成销售额",
xlabel="销售额(元)",
ylabel="区域",
xlim=(0, None),
)柱长编码数量,所以通常应从零开始。截断基线会放大差异。如果比较需要非零参考线或更紧凑的布局,点图可能更合适。
散点图(scatter plot)适合两个连续变量的配对关系:
fig, ax = plt.subplots(layout="constrained")
ax.scatter(
orders["amount"],
orders["delivery_minutes"],
alpha=0.55,
)
ax.set(
title="订单金额与配送时间",
xlabel="订单金额(元)",
ylabel="配送时间(分钟)",
)每个点代表一个观测。不要按任意顺序连接这些点,那会额外制造顺序。大量点重叠时,可以减小点和透明度,改用六边形分箱或二维直方图,或展示有规则的代表性样本并公开抽样方法。
视觉相关不等于因果。点云趋势可能来自第三个变量、选择过程或共同时间变化。第 8 章会进一步区分探索证据与预测评估。
用不止一种视角检查分布
直方图(histogram)把连续变量分箱,并绘制每个区间的计数或密度:
values = deliveries["minutes"].dropna()
fig, ax = plt.subplots(layout="constrained")
counts, edges, patches = ax.hist(
values,
bins="fd",
edgecolor="white",
)
ax.set(
title="配送时间分布",
xlabel="配送时间(分钟)",
ylabel="订单数(笔)",
)返回的 edges 会揭示真实区间。箱选择本身属于分析决策:宽箱可能隐藏多峰或缺口,窄箱可能把抽样噪声画成尖峰。Freedman–Diaconis 规则使用
作为数据驱动的箱宽 ,但它只是起点,不是真实保证。应在相同横轴范围下比较几种可辩护箱宽。
经验累积分布函数(Empirical Cumulative Distribution Function, ECDF)不需要分箱。对于每个观测值 ,它显示不大于 的样本比例:
需要兼容不同 Matplotlib 版本时,可以直接构造:
import numpy as np
sorted_values = np.sort(values.to_numpy())
cumulative = np.arange(1, len(sorted_values) + 1) / len(sorted_values)
ax.step(sorted_values, cumulative, where="post")ECDF 便于读取分位数与尾部比例,但聚类不一定像直方图那样直观。箱线图可以紧凑显示中位数、四分位数和规则化须线,却会隐藏分布形状与样本密度。应使用互补视图,而不是期待一张图回答所有分布问题。
已核实的极端观测应保留,或放进明确标注的敏感性面板。静默删除会同时改变总体和直方图值域。
最合适的图表,是对当前问题最简单而忠实的编码。7.3 节将通过标签、尺度、注释和不确定性,让这套编码更易读、更诚实。