HOWTO · Matplotlib

如何在 Matplotlib 中绘制对数轴

我们可以使用 set_yscale(),semilogy()和 loglog()函数在 Matplotlib 中绘制对数轴。

本页内容

使用 set_xscale() 或 set_yscale() 可以分别设置任一坐标轴的尺度。若要让两个坐标轴都采用对数尺度,请使用 loglog()。semilogx() 和 semilogy() 分别为 x 轴和 y 轴应用对数尺度。 对数轴以 10 的幂次间隔排列刻度,因此跨越数个数量级的数据仍清晰可读,不会挤在线性轴最低的刻度区间里。
下面的所有代码示例均已成功执行。

set_xscale() 或 set_yscale() 函数

调用 set_xscale("log") 可将 x 轴设为对数尺度,调用 set_yscale("log") 可将 y 轴设为对数尺度;同时调用两者即可设置两个坐标轴。 由于这两个调用彼此独立,您可以只转换其中一个坐标轴,让另一个保持线性。

若只想让 x 轴使用对数尺度,请照常绘制数据,然后对坐标轴调用 set_xscale("log"):

import matplotlib.pyplot as plt

x = [1, 10, 100, 1000]
y = [2, 5, 20, 80]

fig, ax = plt.subplots()
ax.plot(x, y, marker="o")
ax.set_xscale("log")
ax.set_xlabel("x")
ax.set_ylabel("y")
print(f"x-axis: {ax.get_xscale()}, y-axis: {ax.get_yscale()}")
plt.close(fig)

打印出的结果证实只有 x 轴改变:x-axis: log, y-axis: linear。1、10、100、1000 的刻度等距排列,尽管数值本身每次都增加为 10 倍。

使用 set_xscale 创建的折线图,x 轴为对数尺度、y 轴为线性尺度

若只想让 y 轴使用对数尺度,请改为调用 set_yscale("log"),并让 x 轴保持不变:

import matplotlib.pyplot as plt

x = [1, 10, 100, 1000]
y = [2, 5, 20, 80]

fig, ax = plt.subplots()
ax.plot(x, y, marker="o")
ax.set_yscale("log")
ax.set_xlabel("x")
ax.set_ylabel("y")
print(f"x-axis: {ax.get_xscale()}, y-axis: {ax.get_yscale()}")
plt.close(fig)

这次打印出的结果是 x-axis: linear, y-axis: log,等距的刻度出现在 y 轴上。

使用 set_yscale 创建的折线图,x 轴为线性尺度、y 轴为对数尺度

pyplot 的状态机接口通过顶层函数 plt.xscale() 和 plt.yscale() 提供相同的变更,这两个函数作用于当前的坐标轴。同一个图表的 yscale() 版本如下:

import matplotlib.pyplot as plt

x = [1, 10, 100, 1000]
y = [2, 5, 20, 80]

plt.plot(x, y, marker="o")
plt.yscale("log")
plt.xlabel("x")
plt.ylabel("y")
ax = plt.gca()
print(f"x-axis: {ax.get_xscale()}, y-axis: {ax.get_yscale()}")
plt.close()

结果是相同的对数 y 轴;在保留明确 ax 引用的程序中,坐标轴对象的方法通常更清楚,因为目标坐标轴在调用中被直接指名,而不是由当前的图形隐含决定。

使用 pyplot 的 yscale 函数创建的折线图,y 轴为对数尺度

下面的完整示例使用正数值数据绘图,并将两个坐标轴都设为对数尺度。

import matplotlib.pyplot as plt

x = [1, 10, 100, 1000]
y = [2, 5, 20, 80]

fig, ax = plt.subplots()
ax.plot(x, y, marker="o")
ax.set_xscale("log")
ax.set_yscale("log")
ax.set_xlabel("x")
ax.set_ylabel("y")
print(f"x-axis: {ax.get_xscale()}, y-axis: {ax.get_yscale()}")
plt.close(fig)

此时打印出的结果是 x-axis: log, y-axis: log,相同的比例在两个坐标轴上都占据相同的距离。

使用 set_xscale 与 set_yscale 创建的折线图,两个坐标轴均为对数尺度

标准的 log 尺度表示正值。如果数据包含正负值,请对相应坐标轴使用 symlog,例如 ax.set_yscale("symlog");它可以显示零的两侧的数值。 最后一节会以经过验证的示例重新讨论这种情况。

semilogx() 或 semilogy() 函数

如果要让 x 轴采用对数尺度而 y 轴保持线性尺度,请使用 semilogx()。如果要让 y 轴采用对数尺度而 x 轴保持线性尺度,请使用 semilogy()。这两个辅助函数都接受 x 和 y 数据作为参数,因此绘图调用会明确指定所选的尺度。如果两个坐标轴都需要对数尺度,可以使用 loglog(),也可以在同一组坐标轴上组合使用 semilogx() 和 semilogy()。

下面的 semilogx() 调用会一步完成数据绘制与对数 x 轴的设置:

import matplotlib.pyplot as plt

x = [1, 10, 100, 1000]
y = [2, 5, 20, 80]

fig, ax = plt.subplots()
ax.semilogx(x, y, marker="o", label="logarithmic x")
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.legend()
print(f"x-axis: {ax.get_xscale()}, y-axis: {ax.get_yscale()}")
plt.close(fig)

这个辅助函数会返回绘制的线条,之后坐标轴报告 x-axis: log, y-axis: linear,与第一节中明确调用 set_xscale("log") 的结果完全相同。

使用 semilogx 创建的折线图,x 轴为对数尺度、y 轴为线性尺度

在同一组坐标轴上结合这两个辅助函数,两个坐标轴都会使用对数尺度。请注意,尺度属于坐标轴,而不是单一条绘制的线条:一旦 semilogy() 设置了 y 轴,该尺度就会应用到坐标轴中的所有数据系列,包括先前由 semilogx() 绘制的那一条。

import matplotlib.pyplot as plt

x = [1, 10, 100, 1000]
y = [2, 5, 20, 80]
y2 = [3, 8, 30, 120]

fig, ax = plt.subplots()
ax.semilogx(x, y, marker="o", label="first series")
ax.semilogy(x, y2, marker="s", label="second series")
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.legend()
print(f"x-axis: {ax.get_xscale()}, y-axis: {ax.get_yscale()}")
plt.close(fig)

两个数据系列最终都绘制在对数 x 轴与 y 轴上,打印出的结果证实了最终状态:x-axis: log, y-axis: log。

结合 semilogx 与 semilogy 创建的两条折线系列,共用对数 x 轴与 y 轴

loglog() 函数

如果两个坐标轴都需要对数尺度,请使用 loglog()。它会绘制给定的 x 和 y 数据,并将两个坐标轴设为标准对数尺度。下面是一个使用正数值的完整示例:

import matplotlib.pyplot as plt

x = [1, 10, 100, 1000]
y = [2, 5, 20, 80]

fig, ax = plt.subplots()
ax.loglog(x, y, marker="o")
ax.set_xlabel("x")
ax.set_ylabel("y")
print(f"x-axis: {ax.get_xscale()}, y-axis: {ax.get_yscale()}")
plt.close(fig)

渲染结果与第一节中 set_xscale("log") 加上 set_yscale("log") 的组合一致;loglog() 只是把尺度选择并入绘图调用本身。

使用 loglog 创建的折线图,两个坐标轴均为对数尺度

非正值与无效的尺度名称

有两件事经常让对数轴的使用者感到意外,在调试一个看起来不对的图表之前,两者都值得先了解。第一,尺度名称会立即被验证:像 ax.set_yscale("logg") 这样的拼写错误会引发 ValueError: 'logg' is not a valid value for scale. Did you mean one of: 'log', 'logit', 'symlog'?(消息在 Matplotlib 3.11 上逐字捕获)。错误消息会列出可接受的值,因此拼写错误会立刻失败,而不是悄悄产生一个线性轴。

第二,对数尺度无法表示零或负值,而且这种情况不会引发错误。Matplotlib 会悄悄丢弃非正值的数据点:它们会被从绘图中屏蔽,经过这些点的线段会消失,坐标轴范围只涵盖正值。下面的示例在对数 y 轴上绘制 y = [2, 0, -5, 80]:

import matplotlib.pyplot as plt

x = [1, 10, 100, 1000]
y = [2, 0, -5, 80]

fig, ax = plt.subplots()
ax.plot(x, y, marker="o")
ax.set_yscale("log")
ax.set_xlabel("x")
ax.set_ylabel("y")
print(f"x-axis: {ax.get_xscale()}, y-axis: {ax.get_yscale()}")
plt.close(fig)

调用会正常完成并报告 y-axis: log,但只有 y = 2 和 y = 80 的数据点能显示;在经过验证的执行中,得到的 y 轴范围约为 1.66 到 96.2,只涵盖正值数据。如果把某个坐标轴切换为 log 之后,数据系列似乎少了点,请先检查数据中是否有零和负值,再怀疑绘图代码。

当数据确实包含带正负号的值时,symlog(对称对数)尺度是合适的替代方案:它在远离零的范围使用对数尺度,在零附近的小范围使用线性尺度,因此每个数据点都能保留显示。

import matplotlib.pyplot as plt

x = [1, 10, 100, 1000]
y = [2, 0, -5, 80]

fig, ax = plt.subplots()
ax.plot(x, y, marker="o")
ax.set_yscale("symlog")
ax.set_xlabel("x")
ax.set_ylabel("y")
print(f"x-axis: {ax.get_xscale()}, y-axis: {ax.get_yscale()}")
plt.close(fig)

四个数据点(包括 0 和 -5)都会绘制在 symlog 的 y 轴上。只要数值的正负号本身带有信息,就使用这种尺度;对于本质上为正值的数据,例如计数、大小和物理测量值,则保持使用单纯的 log 尺度。