Skip to content

第 4 章 散点图:scatter 与颜色映射

学习目标

  • 会用 ax.scatter 画散点图观察两个变量的关系
  • s 让点的大小携带信息
  • c + cmap 让点的颜色携带信息
  • fig.colorbar 添加颜色条
  • 理解散点图可以同时表达最多四维信息

4.1 scatter:看两个变量的关系

散点图(scatter plot)把每个样本画成一个点,横轴一个变量、纵轴另一个变量,用来观察两个变量的关系:是正相关、负相关,还是没有关系。

ax.scatter(x, y) 是散点图的函数。以「40 名学生的身高与体重」为例:

python
import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(11)
n = 40
height = rng.normal(168, 7, n)          # 身高(cm)
weight = rng.normal(58, 8, n) + 0.6 * (height - 168)  # 体重(kg)
age = rng.integers(12, 18, n)           # 年龄
score = rng.integers(60, 100, n)        # 期末成绩

fig, ax = plt.subplots()
ax.scatter(height, weight)
ax.set_title("40 名学生身高与体重")
ax.set_xlabel("身高(cm)")
ax.set_ylabel("体重(kg)")
plt.show()

图 4-1 真实输出:点云从左下到右上倾斜,说明身高越高体重越重——正相关。这就是散点图的核心价值:

图 4-1 身高与体重的散点图

散点与折线的区别:plot 强调「顺序与走势」,scatter 强调「点与点的分布」。

4.2 让点携带更多信息:s 与 c

散点图能比折线图多表达两维信息:点的大小点的颜色

s(size) 控制每个点的大小。传入一个与数据等长的数组,每个点的大小就不同:

python
ax.scatter(height, weight, s=(age - 10) * 18, c=score, cmap="viridis", alpha=0.8)

c(color) 控制颜色。传入一个数值数组时,每个点根据数值取不同颜色,cmap 指定颜色映射(colormap)。

alpha 控制整体透明度(0~1),点很多重叠时很有用。

图 4-2 是真实输出:点越大年龄越大,颜色越亮成绩越高:

图 4-2 点的大小与颜色携带额外信息

4.3 颜色映射与 colorbar

颜色映射(colormap) 是一组「数值 → 颜色」的规则,比如数值小的用深色、大的用亮色。c 传入数值数组、cmap 指定映射,散点图就自动按数值着色。但读者怎么知道颜色对应多少数值?需要 fig.colorbar(散点对象) 添加一个颜色条:

python
import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(11)
n = 40
height = rng.normal(168, 7, n)
weight = rng.normal(58, 8, n) + 0.6 * (height - 168)
score = rng.integers(60, 100, n)

fig, ax = plt.subplots()
sc = ax.scatter(height, weight, c=score, cmap="plasma", s=60, alpha=0.9)
ax.set_title("身高 vs 体重,颜色表示期末成绩")
ax.set_xlabel("身高(cm)")
ax.set_ylabel("体重(kg)")
cb = fig.colorbar(sc, ax=ax)
cb.set_label("期末成绩")
plt.show()

图 4-3 真实输出:c 必须存回变量(sc),colorbar 才能引用它:

图 4-3 颜色映射与颜色条

常用 colormap:"viridis"(通用、色盲友好)、"plasma""coolwarm"(冷热对比)、"Reds"(单色深浅)。分类数据用 "tab10"(每类一个颜色)。

4.4 气泡图:一张图看四维信息

大小颜色都用上,散点图最多能同时表达四维:横轴、纵轴、大小、颜色。这种「大小携带数值」的散点图叫气泡图(bubble chart)

以 2023 年 10 个主要城市为例:横轴 GDP、纵轴人均可支配收入、气泡大小=人口、颜色=人口:

python
import matplotlib.pyplot as plt
import numpy as np

gdp = np.array([4.2, 4.7, 3.4, 2.9, 2.2, 1.9, 1.2, 1.9, 1.7, 2.9])          # 万亿元
pop = np.array([2185, 2487, 1768, 1881, 2140, 1374, 1299, 1237, 949, 3213])  # 万人
income = np.array([7.8, 8.2, 7.1, 6.7, 5.5, 5.2, 4.8, 6.3, 6.5, 4.1])       # 万元
cities = ["北京", "上海", "深圳", "广州", "成都", "武汉", "西安", "杭州", "南京", "重庆"]

fig, ax = plt.subplots(figsize=(8, 5.5))
sc = ax.scatter(gdp, income, s=pop / 8, c=pop, cmap="viridis", alpha=0.75,
                edgecolors="white", linewidths=1)
for i, city in enumerate(cities):
    ax.annotate(city, (gdp[i], income[i]), fontsize=9, textcoords="offset points", xytext=(6, 6))
ax.set_title("2023 年主要城市:横轴 GDP,纵轴人均收入,气泡大小=人口")
ax.set_xlabel("GDP(万亿元)")
ax.set_ylabel("人均可支配收入(万元)")
cb = fig.colorbar(sc, ax=ax)
cb.set_label("人口(万人)")
plt.show()

图 4-4 真实输出(annotate 是第 8 章的文本注释,这里先用起来):

图 4-4 气泡图:四个维度同时呈现

一眼可读的信息:重庆人口最多(最大的气泡)、北京上海人均收入最高(位置最高)、深圳人口少但 GDP 高。

4.5 scatter 与 plot 的区别

ax.plotax.scatter
强调数据的顺序与走势点的分布与关系
大小 s只能统一可以逐点不同
颜色 c只能统一可以逐点不同(配合 cmap)
典型用途时间序列、趋势相关关系、分布、聚类

画点的方式也不同:plot(..., marker="o") 画的点是「折线上的点」,适合加线;scatter 是纯点,适合大数据量点云。

4.6 读懂报错:size 传了字符串

s 必须是数字或数字数组。传字符串会报错:

>>> ax.scatter([1, 2, 3], [1, 2, 3], s="big")
Traceback (most recent call last):
  ...
  File "matplotlib/axes/_axes.py", line 5337, in scatter
    raise ValueError(f"s must be float, but has type {s.dtype}")
ValueError: s must be float, but has type <U3

解读:s 会被转成 NumPy 数组,<U3 表示「长度 3 的 Unicode 字符串」类型。大小只能是数值;「big、small」这类词 Matplotlib 不认识。想表达大小含义,自己算成数字:s=(age - 10) * 18

动手实践

  1. 用固定种子生成 50 个点的身高、体重数据,画散点图,观察相关性。
  2. 给散点加上 s(按年龄)与 c(按成绩)+ cmap="viridis",复现图 4-2。
  3. 给散点加 colorbar 并设置标签,复现图 4-3。
  4. 用你自己的数据(比如同学身高 vs 鞋码)画气泡图,分别用大小和颜色表示两个变量。
  5. 故意把 s 传成 "big",读报错,说出错误信息里的 <U3 是什么意思。

常见错误

错误写法现象原因
s="big"ValueError: s must be float, but has type <U3大小必须是数值,不能用文字
c 传数值数组但忘了 cmap颜色很怪或全黑c 为数值时需配 cmap;或 c 传具体颜色
colorbar 传了 ax.scatter 的返回值而不是存下来的对象报错/条不对scatter 结果存变量再传给 colorbar
s 数组长度与 x 不同ValueError: s must be a scalar, or float array-like with the same size as x and ys 数组必须与数据等长
大量点重叠看不出密度alpha 降低透明度

章末练习

基础

  1. 画 20 个随机点的散点图,横轴为身高、纵轴为体重,加标题与轴标签。
  2. s 让点的大小随一个变量变化。
  3. c + cmap="viridis" + colorbar 表达第三个变量。

提高

  1. 生成两组相关数据(正相关、负相关),画两个子图对比,解释相关性的方向。
  2. 用固定种子生成 200 个点,画 alpha=1.0alpha=0.3 两张对比图,解释为什么重叠时透明很重要。

挑战

  1. 画一张「城市气泡图」:横轴人口、纵轴面积、气泡大小=GDP、颜色=人均收入,并为每个城市加名字标注(参考 4.4)。
  2. 思考:什么时候用散点、什么时候用折线?给出一个「数据形态决定图表类型」的判断流程,并用代码各画一个例子。

章末自测

每题选择一个最佳答案。本书不附答案:完成后交由老师或 AI 老师批改讲解。

  1. 散点图的函数是?
    • A. ax.plot
    • B. ax.scatter
    • C. ax.bar
    • D. ax.hist
  2. 让每个点大小不同的参数是?
    • A. size
    • B. s
    • C. lw
    • D. alpha
  3. 让每个点颜色按数值变化的参数组合是?
    • A. color="red"
    • B. c=数值数组, cmap=...
    • C. s=数值数组
    • D. alpha=0.5
  4. 添加颜色条的方法是?
    • A. ax.colorbar()
    • B. fig.colorbar(sc, ax=ax)
    • C. plt.color()
    • D. ax.legend()
  5. cmap="viridis" 的作用是?
    • A. 设置画布背景
    • B. 指定「数值 → 颜色」的映射
    • C. 设置点的大小
    • D. 设置坐标轴颜色
  6. 散点图的 alpha=0.3 主要用于?
    • A. 让点变小
    • B. 让重叠的点显示密度
    • C. 加快渲染
    • D. 改变点形状
  7. 气泡图额外用哪个维度承载信息?
    • A. 线型
    • B. 点的大小
    • C. 坐标轴颜色
    • D. 刻度
  8. s"big" 会?
    • A. 正常显示大点
    • B. 报 ValueError: s must be float
    • C. 忽略这个参数
    • D. 显示为文字
  9. 一组身高与体重数据「左下到右上」分布,说明?
    • A. 负相关
    • B. 正相关
    • C. 无关
    • D. 无法判断
  10. plotscatter 的区别,正确的是?
    • A. scatter 强调顺序与走势
    • B. plot 强调点的分布
    • C. scatter 支持逐点不同大小与颜色
    • D. 两者完全一样