第 4 章 散点图:scatter 与颜色映射
学习目标
- 会用
ax.scatter画散点图观察两个变量的关系 - 用
s让点的大小携带信息 - 用
c+cmap让点的颜色携带信息 - 用
fig.colorbar添加颜色条 - 理解散点图可以同时表达最多四维信息
4.1 scatter:看两个变量的关系
散点图(scatter plot)把每个样本画成一个点,横轴一个变量、纵轴另一个变量,用来观察两个变量的关系:是正相关、负相关,还是没有关系。
ax.scatter(x, y) 是散点图的函数。以「40 名学生的身高与体重」为例:
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 真实输出:点云从左下到右上倾斜,说明身高越高体重越重——正相关。这就是散点图的核心价值:

散点与折线的区别:plot 强调「顺序与走势」,scatter 强调「点与点的分布」。
4.2 让点携带更多信息:s 与 c
散点图能比折线图多表达两维信息:点的大小和点的颜色。
s(size) 控制每个点的大小。传入一个与数据等长的数组,每个点的大小就不同:
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.3 颜色映射与 colorbar
颜色映射(colormap) 是一组「数值 → 颜色」的规则,比如数值小的用深色、大的用亮色。c 传入数值数组、cmap 指定映射,散点图就自动按数值着色。但读者怎么知道颜色对应多少数值?需要 fig.colorbar(散点对象) 添加一个颜色条:
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 才能引用它:

常用 colormap:"viridis"(通用、色盲友好)、"plasma"、"coolwarm"(冷热对比)、"Reds"(单色深浅)。分类数据用 "tab10"(每类一个颜色)。
4.4 气泡图:一张图看四维信息
把大小与颜色都用上,散点图最多能同时表达四维:横轴、纵轴、大小、颜色。这种「大小携带数值」的散点图叫气泡图(bubble chart)。
以 2023 年 10 个主要城市为例:横轴 GDP、纵轴人均可支配收入、气泡大小=人口、颜色=人口:
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 章的文本注释,这里先用起来):

一眼可读的信息:重庆人口最多(最大的气泡)、北京上海人均收入最高(位置最高)、深圳人口少但 GDP 高。
4.5 scatter 与 plot 的区别
ax.plot | ax.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。
动手实践
- 用固定种子生成 50 个点的身高、体重数据,画散点图,观察相关性。
- 给散点加上
s(按年龄)与c(按成绩)+cmap="viridis",复现图 4-2。 - 给散点加
colorbar并设置标签,复现图 4-3。 - 用你自己的数据(比如同学身高 vs 鞋码)画气泡图,分别用大小和颜色表示两个变量。
- 故意把
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 y | s 数组必须与数据等长 |
| 大量点重叠 | 看不出密度 | 用 alpha 降低透明度 |
章末练习
基础
- 画 20 个随机点的散点图,横轴为身高、纵轴为体重,加标题与轴标签。
- 用
s让点的大小随一个变量变化。 - 用
c+cmap="viridis"+colorbar表达第三个变量。
提高
- 生成两组相关数据(正相关、负相关),画两个子图对比,解释相关性的方向。
- 用固定种子生成 200 个点,画
alpha=1.0与alpha=0.3两张对比图,解释为什么重叠时透明很重要。
挑战
- 画一张「城市气泡图」:横轴人口、纵轴面积、气泡大小=GDP、颜色=人均收入,并为每个城市加名字标注(参考 4.4)。
- 思考:什么时候用散点、什么时候用折线?给出一个「数据形态决定图表类型」的判断流程,并用代码各画一个例子。
章末自测
每题选择一个最佳答案。本书不附答案:完成后交由老师或 AI 老师批改讲解。
- 散点图的函数是?
- A.
ax.plot - B.
ax.scatter - C.
ax.bar - D.
ax.hist
- A.
- 让每个点大小不同的参数是?
- A.
size - B.
s - C.
lw - D.
alpha
- A.
- 让每个点颜色按数值变化的参数组合是?
- A.
color="red" - B.
c=数值数组, cmap=... - C.
s=数值数组 - D.
alpha=0.5
- A.
- 添加颜色条的方法是?
- A.
ax.colorbar() - B.
fig.colorbar(sc, ax=ax) - C.
plt.color() - D.
ax.legend()
- A.
cmap="viridis"的作用是?- A. 设置画布背景
- B. 指定「数值 → 颜色」的映射
- C. 设置点的大小
- D. 设置坐标轴颜色
- 散点图的
alpha=0.3主要用于?- A. 让点变小
- B. 让重叠的点显示密度
- C. 加快渲染
- D. 改变点形状
- 气泡图额外用哪个维度承载信息?
- A. 线型
- B. 点的大小
- C. 坐标轴颜色
- D. 刻度
s传"big"会?- A. 正常显示大点
- B. 报
ValueError: s must be float - C. 忽略这个参数
- D. 显示为文字
- 一组身高与体重数据「左下到右上」分布,说明?
- A. 负相关
- B. 正相关
- C. 无关
- D. 无法判断
plot与scatter的区别,正确的是?- A.
scatter强调顺序与走势 - B.
plot强调点的分布 - C.
scatter支持逐点不同大小与颜色 - D. 两者完全一样
- A.
