matplotlib是否有在坐标轴中绘制对角线的函数?

80 投票
6 回答
95544 浏览
提问于 2025-04-17 20:23

Matplotlib 的 Axes 有两个函数,分别是 axhlineaxvline,可以用来在指定的 y 坐标或 x 坐标上画水平线或垂直线,这些线的绘制不受数据比例的影响。

那么,有没有类似的函数可以用来画一条固定的对角线呢?比如说,如果我有一个散点图,里面的变量范围差不多,知道这些点是在 y = x 这条线的上面还是下面是很有用的:

mean, cov = [0, 0], [(1, .6), (.6, 1)]
x, y = np.random.multivariate_normal(mean, cov, 100).T
y += x + 1
f, ax = plt.subplots(figsize=(6, 6))
ax.scatter(x, y, c=".3")
ax.plot([-3, 3], [-3, 3], ls="--", c=".3")
ax.set(xlim=(-3, 3), ylim=(-3, 3))

enter image description here

当然,你可以通过编程的方式来获取坐标轴的范围(比如用 ax.get_xlim() 等),但是这样做有两个问题:一是步骤比较多,二是如果后面数据增加了,可能会改变坐标轴的范围,这样就不太可靠了。(实际上,有时候仅仅是添加这条常数线本身就会拉伸坐标轴)。

最好能直接用类似 ax.axdline(ls="--", c=".3") 的方式来画,但目前不太清楚在 matplotlib 的代码里是否有这样的功能。其实,只需要修改 axhline 的代码,让它在坐标轴的范围内从 [0, 1] 画出 x 和 y 的线,我觉得就可以了。

6 个回答

37

在屏幕上从左下角到右上角画一条对角线其实很简单,你只需要用 ax.plot(ax.get_xlim(), ax.get_ylim(), ls="--", c=".3") 这行代码就可以了。这里的 ax.get_xlim() 方法会返回当前 x 轴的值(y 轴也是一样)。

不过,如果你想在图表上进行缩放,那么事情就变得有点复杂了,因为你画的对角线不会随着新的 x 轴和 y 轴的范围而改变。

在这种情况下,你可以使用回调函数来检查 x 轴(或 y 轴)的范围是否发生了变化,并相应地更新对角线的数据(如下所示)。我在 这个例子 中找到了关于回调函数的方法。更多信息也可以在 这里 找到。

import numpy as np
import matplotlib.pyplot as plt

mean, cov = [0, 0], [(1, .6), (.6, 1)]
x, y = np.random.multivariate_normal(mean, cov, 100).T
y += x + 1

f, ax = plt.subplots(figsize=(6, 6))

ax.scatter(x, y, c=".3")
ax.set(xlim=(-3, 3), ylim=(-3, 3))

# Plot your initial diagonal line based on the starting
# xlims and ylims.
diag_line, = ax.plot(ax.get_xlim(), ax.get_ylim(), ls="--", c=".3")

def on_change(axes):
    # When this function is called it checks the current
    # values of xlim and ylim and modifies diag_line
    # accordingly.
    x_lims = ax.get_xlim()
    y_lims = ax.get_ylim()
    diag_line.set_data(x_lims, y_lims)

# Connect two callbacks to your axis instance.
# These will call the function "on_change" whenever
# xlim or ylim is changed.
ax.callbacks.connect('xlim_changed', on_change)
ax.callbacks.connect('ylim_changed', on_change)

plt.show()

注意,如果你不想让对角线在缩放时改变,那么只需删除 diag_line, = ax.plot(... 下面的所有内容即可。

65

从图的左下角到右上角画一条对角线,可以用下面的代码来实现:

ax.plot([0, 1], [0, 1], transform=ax.transAxes)

这里使用了 transform=ax.transAxes,这意味着你给的 xy 坐标会被当作图表的坐标来理解,而不是数据的坐标。

正如 @fqq 提到的,只有当你的 xy 的范围相等时,这条线才是身份线(也就是45度的对角线)。如果想要画出 y=x 这条线,并且让它总是延伸到图的边界,可以使用类似 @Ffisegydd 提供的方法,下面是一个可以实现这个功能的函数。

def add_identity(axes, *line_args, **line_kwargs):
    identity, = axes.plot([], [], *line_args, **line_kwargs)
    def callback(axes):
        low_x, high_x = axes.get_xlim()
        low_y, high_y = axes.get_ylim()
        low = max(low_x, low_y)
        high = min(high_x, high_y)
        identity.set_data([low, high], [low, high])
    callback(axes)
    axes.callbacks.connect('xlim_changed', callback)
    axes.callbacks.connect('ylim_changed', callback)
    return axes

示例用法:

import numpy as np
import matplotlib.pyplot as plt

mean, cov = [0, 0], [(1, .6), (.6, 1)]
x, y = np.random.multivariate_normal(mean, cov, 100).T
y += x + 1

f, ax = plt.subplots(figsize=(6, 6))
ax.scatter(x, y, c=".3")
add_identity(ax, color='r', ls='--')

plt.show()
35

从matplotlib 3.3.0版本开始,它将会: https://matplotlib.org/3.3.0/api/_as_gen/matplotlib.axes.Axes.axline.html

Axes.axline(self, xy1, xy2=None, *, slope=None, **kwargs) 可以添加一条无限长的直线。

这条线可以通过两个点xy1和xy2来定义,或者通过一个点xy1和一个斜率来定义。

这条直线会在“屏幕上”绘制出来,不受x轴和y轴的比例影响,因此也适合在半对数图中绘制指数衰减,在对数对数图中绘制幂律等。不过,斜率只应该在线性坐标系中使用;在其他坐标系中,斜率没有明确的意义,因此行为是不可预测的。对于非线性坐标系,请使用点xy1和xy2来指定这条线。

撰写回答