用 plt.scatter() 画出有灵魂的散点图

2026-05-25 33 预计阅读时间:1 分钟
来源:realpython.com AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:9 分钟

一张散点图能同时呈现两组数值的分布与关联,这是折线图和柱状图做不到的。Python 里 matplotlib.pyplot.scatter() 是画散点图的主力函数,但它不只是把点画上去——每个点的尺寸、颜色、形状、透明度都可以单独控制,这意味着你能在一张图里编码四五个维度的信息。

下面从最基础的调用开始,逐步把散点图从"能看"变成"好看且有用"。

最简调用:先让点出现在画布上

plt.scatter() 的两个核心参数是 xy,即每个点的横纵坐标。最简单的用法:

import matplotlib.pyplot as plt

x = [1, 2, 3, 4, 5]
y = [2, 4, 1, 3, 5]

plt.scatter(x, y)
plt.show()

这会生成一张默认蓝色圆点散点图。能跑,但信息密度很低——所有点长得一模一样,看不出谁重谁轻。

用尺寸编码第三个维度

s 参数控制每个点的大小,单位是点的平方(points²)。传入一个与数据等长的列表,就能让点的大小随某个变量变化:

import matplotlib.pyplot as plt
import numpy as np

np.random.seed(42)
x = np.random.rand(50)
y = np.random.rand(50)
sizes = np.random.rand(50) * 500  # 随机大小,范围 0~500

plt.scatter(x, y, s=sizes)
plt.show()

sizes 的值越大,点越醒目。实际场景里,你可以把城市人口、销售额、文件体积等映射到 s,让读者一眼看出"谁是大玩家"。

注意:s 的值是面积的量级,不是直径。从 10 到 100 的变化在视觉上远没有从 10 到 1000 那么夸张,调参时建议先画出来看效果,再决定数值范围。

用颜色编码第四个维度

c 参数接受颜色值,可以是单个颜色字符串,也可以是一个与数据等长的数值列表。数值列表会配合 cmap 色彩映射,自动把数值映射到渐变色带:

import matplotlib.pyplot as plt
import numpy as np

np.random.seed(7)
x = np.random.rand(80)
y = np.random.rand(80)
colors = np.random.rand(80)  # 0~1 的随机值,映射到 colormap

plt.scatter(x, y, c=colors, cmap="viridis")
plt.colorbar(label="某个指标")  # 加色条,让读者知道颜色对应什么值
plt.show()

cmap 有几十种可选,常用的有 viridis(感知均匀、色盲友好)、plasmacoolwarm(适合正负对比)。选 colormap 不是审美问题——如果你的数据有零点,coolwarm 比纯渐变更直观;如果要打印黑白,gray 更安全。

透明度:解决点重叠的杀手锏

数据量大时,点会叠在一起,中心区域变成一团黑,看不出密度差异。alpha 参数控制透明度,0 为全透明,1 为全不透明:

plt.scatter(x, y, s=80, alpha=0.3)

alpha 设到 0.2~0.5 之间,重叠区域自然变深,稀疏区域保持浅色,相当于免费获得了一张密度热力图。这是处理大规模散点数据最实用的一个参数。

标记形状:区分类别而非数值

marker 参数改变点的形状。默认是 "o"(圆圈),还可以用 "^"(三角形)、"s"(方形)、"D"(菱形)、"*"(星形)等。形状适合编码类别信息(比如不同物种、不同实验组),而尺寸和颜色更适合编码连续数值。

如果只有两三个类别,可以分次调用 plt.scatter(),每次用不同 marker,再配合 labelplt.legend()

group_a = np.random.rand(20, 2)
group_b = np.random.rand(20, 2) + 0.5

plt.scatter(group_a[:, 0], group_a[:, 1], marker="o", label="组 A")
plt.scatter(group_b[:, 0], group_b[:, 1], marker="^", label="组 B")
plt.legend()
plt.show()

一张图编码五个维度:完整示例

把上面所有参数组合起来,就能在一张散点图里同时展示横轴、纵轴、点大小、点颜色、点形状。下面是一个可直接运行的完整示例,模拟城市数据——横轴为经度、纵轴为纬度、大小为人口、颜色为年均气温、形状区分沿海与内陆:

import matplotlib.pyplot as plt
import numpy as np

np.random.seed(2024)

n = 60
longitude = np.random.uniform(100, 130, n)    # 经度
latitude  = np.random.uniform(20, 45, n)       # 纬度
population = np.random.uniform(50, 2000, n)    # 人口(万)
temperature = np.random.uniform(5, 25, n)      # 年均气温(℃)
is_coastal = np.random.choice([True, False], n)  # 是否沿海

# 人口映射到点面积,加一个下限避免点太小看不见
sizes = population / 2000 * 600 + 20

# 沿海用圆点,内陆用方形
coastal_mask = is_coastal
inland_mask  = ~is_coastal

plt.figure(figsize=(10, 7))

plt.scatter(
    longitude[coastal_mask],
    latitude[coastal_mask],
    s=sizes[coastal_mask],
    c=temperature[coastal_mask],
    cmap="coolwarm",
    alpha=0.6,
    marker="o",
    label="沿海城市",
    edgecolors="grey",
    linewidths=0.5,
)

plt.scatter(
    longitude[inland_mask],
    latitude[inland_mask],
    s=sizes[inland_mask],
    c=temperature[inland_mask],
    cmap="coolwarm",
    alpha=0.6,
    marker="s",
    label="内陆城市",
    edgecolors="grey",
    linewidths=0.5,
)

cbar = plt.colorbar(label="年均气温 (℃)")
plt.xlabel("经度")
plt.ylabel("纬度")
plt.title("模拟城市分布:大小 = 人口,颜色 = 气温,形状 = 沿海/内陆")
plt.legend(loc="upper left")
plt.tight_layout()
plt.show()

运行后你会看到一张信息密度很高的图:大城市一目了然,气温冷暖靠色带区分,沿海和内陆靠形状区分,重叠区域因透明度自然呈现密度。改掉 np.random 部分换成真实数据(比如 pandas DataFrame),就能直接用在报告里。

实用调参清单

画散点图时容易踩的坑和对应策略:

问题 对策
点太密集看不清分布 降低 alpha 到 0.2~0.4
大点遮住小点 先画大点、后画小点(调用顺序即绘制层级),或给点加 edgecolors 描边
颜色映射看不懂 plt.colorbar() 并设 label
图例只显示一个标记 分组多次调用 scatter(),每次传 label
点太小在演示时看不见 s 最低值不要低于 15,演示场景建议整体放大
打印后黑白图丢失信息 避免用红绿对比,选 viridis 或直接用形状区分

最后一点:plt.scatter() 每次调用都会创建一个独立的 PathCollection,数据量上万时性能会明显下降。如果只是画纯散点不需要逐点定制大小和颜色,plt.plot(x, y, "o") 更快——它把所有点当成一条线的标记来渲染,速度能快几倍。需要逐点定制时才用 scatter(),这是它真正的价值所在。


相关推荐