ML 学习站
跳到正文

用图表探索数据

频数分布表、直方图、散点图、相关与回归可视化。

35 分钟3 / 43,053
加载中...

本章探讨了利用图表进行数据探索的核心方法,强调了可视化在数据分析中的重要性。核心概念包括频数分布表、直方图和散点图。频数分布表通过将数据归类并统计每个区间的频数,帮助理解数据分布。直方图则是频数分布的可视化展示,通过调整区间数、宽度和纵轴类型,可以更直观地观察数据特征。散点图用于分析两个变量之间的关系,通过观察方向、形态、强度和异常点,可以初步判断变量间的相关性。相关分析通过相关系数r量化线性关系,但需注意非线性关系、异常值和分层混杂等陷阱。回归线则进一步从“看关系”发展到“做预测”,通过最小二乘法拟合最合适的直线。此外,本章还警示了截断Y轴、不等宽频率分布和面积感知偏差等常见误导性图表问题,并提供了识别和避免这些陷阱的方法。学完本章后,读者能够有效地运用图表进行数据探索,识别数据中的模式和关系,并避免常见的图表误导。

用图表探索数据

本章问题: 你拿到 1000 个顾客的消费数据, 第一件事做什么? 答: 不是算均值, 是画图。一个直方图能告诉你均值永远告诉不了的事。

1. 频数分布表:把数据装进抽屉

面对一堆原始数据 (比如 50 个学生成绩), 第一步是归类。把数据按区间划分, 统计每个区间有多少个 — 这就是频数分布表

例: 50 个学生成绩

分数区间频数相对频率累积相对频率
50-5936%6%
60-69816%22%
70-791530%52%
80-891836%88%
90-99612%100%
合计50100%

💡 关键规则: 区间边界要"互斥且完整" (MECE), 不要重叠也不要漏。

import pandas as pd
import numpy as np

scores = np.array([62, 65, 70, 71, 73, 75, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87,
                   88, 89, 90, 91, 92, 94, 95, 96, 58, 60, 64, 68, 72, 76, 77, 83,
                   84, 85, 86, 87, 88, 89, 90, 92, 93, 95, 96, 98, 55, 65, 75, 85, 95, 99])

bins = [50, 60, 70, 80, 90, 100]
labels = ["50-59", "60-69", "70-79", "80-89", "90-99"]
df = pd.DataFrame({"score": scores})
df["bucket"] = pd.cut(df["score"], bins=bins, right=False, labels=labels)

freq_table = df["bucket"].value_counts().sort_index()
freq_table_pct = df["bucket"].value_counts(normalize=True).sort_index() * 100
cum_pct = freq_table_pct.cumsum()

result = pd.DataFrame({
    "频数": freq_table,
    "相对频率(%)": freq_table_pct.round(1),
    "累积相对频率(%)": cum_pct.round(1),
})
print(result)

2. 直方图:频数分布的图形化

直方图 = 频数分布表的可视化, 看起来像"柱子"。

import matplotlib.pyplot as plt

fig, axes = plt.subplots(1, 2, figsize=(14, 5))

# 左图: 默认频率
axes[0].hist(scores, bins=10, edgecolor="black", color="steelblue")
axes[0].set_title("直方图: 10 个等宽区间")
axes[0].set_xlabel("分数"); axes[0].set_ylabel("频数")

# 右图: 频率密度 (面积=1)
axes[1].hist(scores, bins=10, density=True, edgecolor="black", color="salmon")
axes[1].set_title("频率密度直方图 (面积=1)")
axes[1].set_xlabel("分数"); axes[1].set_ylabel("密度")

plt.tight_layout(); plt.show()

直方图的 3 个关键参数

  • 区间数: Sturges 公式 k = 1 + log2(n), 50 个数据约用 6-7 个区间
  • 区间宽度: 等宽 vs 不等宽, 数据有长尾时用对数尺度
  • 纵轴: 频数 vs 频率 vs 密度 — 密度才能跨数据集比较

3. 散点图:看两个变量的关系

研究"广告费 vs 销售额"的关系, 直方图就无能为力了, 这时用散点图

import numpy as np
import matplotlib.pyplot as plt

np.random.seed(42)
ads = np.random.uniform(1, 10, 50)              # 广告费 (万元)
sales = 5 + 2.5 * ads + np.random.normal(0, 3, 50)  # 销售额 (有噪声)
size = np.random.uniform(20, 200, 50)            # 门店面积 (用于气泡大小)

plt.figure(figsize=(8, 6))
plt.scatter(ads, sales, s=size, alpha=0.6, c=size, cmap="viridis")
plt.colorbar(label="门店面积 (m²)")
plt.xlabel("广告费 (万元)"); plt.ylabel("销售额 (万元)")
plt.title("广告费 vs 销售额 (气泡大小 = 门店面积)")
plt.show()

散点图能告诉你的 4 件事

  1. 方向: 正相关 / 负相关
  2. 形态: 线性 / 曲线 / 簇状
  3. 强度: 点越紧, 关系越强
  4. 异常点: 远离主群的点

⚠️ 强相关 ≠ 因果: 冰淇淋销量和溺水率高度相关 — 都因为夏天人多。

4. 相关分析:把"关系"量化

散点图给的是"感觉", 想量化两变量的关系, 用相关系数 r (Pearson):

r 范围解释
|r| < 0.3弱相关
0.3 ≤ |r| < 0.7中等相关
|r| ≥ 0.7强相关
from scipy.stats import pearsonr

r, p_value = pearsonr(ads, sales)
print(f"皮尔逊 r = {r:.3f}, p = {p_value:.4f}")
# r = 0.86, p &lt; 0.001 → 强正相关, 统计显著

相关的 3 个常见陷阱

  1. 非线性关系: r 只测线性关系, U 形关系 r ≈ 0
  2. 异常值敏感: 1 个离群点就能把 r 拉高
  3. 分层混杂: Simpson 悖论 — 整体相关和分层相关方向相反

5. 回归线:从"看关系"到"做预测"

散点图是"看", 回归是"算"。最简单的线性回归就是找一条最拟合的直线:

其中:

  • b1 (斜率) = r × (sy / sx), 即"x 变 1 单位, y 变多少"
  • b0 (截距) = ȳ - b1 × x̄

最小化残差平方和 (RSS) 求解, 这就是最小二乘法 (OLS)

from sklearn.linear_model import LinearRegression
import matplotlib.pyplot as plt

model = LinearRegression()
model.fit(ads.reshape(-1, 1), sales)

# 画图
plt.figure(figsize=(8, 6))
plt.scatter(ads, sales, alpha=0.6, label="数据点")
plt.plot(ads, model.predict(ads.reshape(-1, 1)),
         color="red", linewidth=2, label=f"y = {model.intercept_:.2f} + {model.coef_[0]:.2f}x")
plt.xlabel("广告费 (万元)"); plt.ylabel("销售额 (万元)")
plt.title("简单线性回归")
plt.legend(); plt.show()

print(f"斜率 b1 = {model.coef_[0]:.3f}")
print(f"截距 b0 = {model.intercept_:.3f}")
print(f"R² = {model.score(ads.reshape(-1, 1), sales):.3f}")

6. 误导性图表:3 个常见陷阱

图表会撒谎。下面 3 种最常见:

6.1 截断 Y 轴

柱状图从 5 起步, 而不是 0, 差异看起来放大 10 倍。

# 错: 截断 y 轴, 看起来差异巨大
fig, axes = plt.subplots(1, 2, figsize=(12, 4))
axes[0].bar(["A", "B"], [50, 51], color=["gray", "red"])
axes[0].set_ylim(50, 52)        # ❌ 截断!
axes[0].set_title("截断 Y 轴 (误导)")

# 对: Y 轴从 0 开始
axes[1].bar(["A", "B"], [50, 51], color=["gray", "red"])
axes[1].set_ylim(0, 60)         # ✅ 0 起点
axes[1].set_title("Y 轴从 0 开始 (正确)")

6.2 不等宽频率分布

把"200 人的 0-10 段"和"20 人的 10-20 段"画一样宽, 看起来 0-10 段人数是 10-20 的 10 倍 — 但其实比例差不多。

6.3 面积感知偏差

饼图用面积编码比例, 但人眼对角度更敏感。3D 饼图会加剧这个问题, 永远不要用 3D 饼图

7. 启发性图表 vs 误导性图表

启发性 (好的)误导性 (坏的)
标轴 0 起点截断 Y 轴
标轴单位明确无单位
标题说清数据来源标题主观 ("惊人增长")
多图用同一尺度跨图不同尺度
数据点直接画出只画平均, 不画分布

8. Python 实战:完整 EDA 流水线

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from scipy.stats import pearsonr

# 1. 加载真实数据集
df = sns.load_dataset("iris")  # 经典鸢尾花数据集

# 2. 一行查看概况
print(df.describe())  # 均值、标准差、分位数

# 3. 数值列分布 (4 张子图)
fig, axes = plt.subplots(1, 4, figsize=(16, 4))
for ax, col in zip(axes, df.select_dtypes("number").columns):
    ax.hist(df[col], bins=20, edgecolor="black")
    ax.set_title(col)
plt.suptitle("数值变量分布")
plt.tight_layout(); plt.show()

# 4. 相关性热力图
corr = df.select_dtypes("number").corr()
sns.heatmap(corr, annot=True, cmap="coolwarm", center=0, vmin=-1, vmax=1)
plt.title("变量间 Pearson 相关系数")
plt.show()

# 5. 散点图矩阵 (pairplot) — 一图看所有两两关系
sns.pairplot(df, hue="species", diag_kind="kde")
plt.suptitle("鸢尾花: 散点图矩阵", y=1.02)
plt.show()

9. 小结

你学到了关键点
频数分布表数据的"第一次整理"
直方图单个变量分布, 3 个参数: 区间数/宽度/纵轴
散点图两个变量关系: 方向/形态/强度/异常
相关系数 r量化线性关系, -1 ≤ r ≤ 1
简单线性回归OLS 找最拟合直线, R² 衡量解释力
误导性图表截断 Y 轴、不等宽、3D 饼图

10. 习题

  1. df = sns.load_dataset("tips") (餐厅小费数据), 计算:

    • total_billtip 的 Pearson r
    • 画散点图 + 回归线
    • 比较午餐晚餐的回归斜率, 哪个时段"加菜 → 多给小费"效应更强?
  2. 直方图和柱状图 (bar plot) 区别是什么? 什么情况下不能互相替代?

👉 查看参考答案
  1. 提示: 用 df.groupby("time").apply(lambda g: LinearRegression().fit(g[["total_bill"]], g["tip"])) 比较两组的 coef_。一般晚餐的斜率更大 (晚餐更"按比例"给小费)。

  2. 区别:

    • 直方图 (histogram): 用于连续定量数据, x 轴是连续区间, 柱子之间无空隙
    • 柱状图 (bar plot): 用于离散分类数据, x 轴是离散类别, 柱子之间有空隙
    • 不能互相替代: 用柱状图画连续数据会"假装"数据离散; 用直方图画分类数据会画出没有意义的连续轴

11. 下一章


📚 本章来源: 改编自 Triola《基础统计学》第 14 版 第 2 章, 加入 Seaborn/Matplotlib EDA 流水线。

章末小测验

检验你对《用图表探索数据》的掌握程度。

1

以下关于频数分布表的说法中,哪一项是正确的?

2

关于直方图的参数设置,下列哪些说法是正确的?

3

以下关于散点图的描述,哪一项是错误的?

4

关于相关系数 r,下列哪些说法是正确的?

5

以下关于图表误导性的说法中,哪一项是正确的?

讨论区(0)

加载评论中...