matplotlib是否有在坐标轴中绘制对角线的函数?
Matplotlib 的 Axes 有两个函数,分别是 axhline
和 axvline
,可以用来在指定的 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))
当然,你可以通过编程的方式来获取坐标轴的范围(比如用 ax.get_xlim()
等),但是这样做有两个问题:一是步骤比较多,二是如果后面数据增加了,可能会改变坐标轴的范围,这样就不太可靠了。(实际上,有时候仅仅是添加这条常数线本身就会拉伸坐标轴)。
最好能直接用类似 ax.axdline(ls="--", c=".3")
的方式来画,但目前不太清楚在 matplotlib 的代码里是否有这样的功能。其实,只需要修改 axhline
的代码,让它在坐标轴的范围内从 [0, 1]
画出 x 和 y 的线,我觉得就可以了。
6 个回答
在屏幕上从左下角到右上角画一条对角线其实很简单,你只需要用 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(...
下面的所有内容即可。
从图的左下角到右上角画一条对角线,可以用下面的代码来实现:
ax.plot([0, 1], [0, 1], transform=ax.transAxes)
这里使用了 transform=ax.transAxes
,这意味着你给的 x
和 y
坐标会被当作图表的坐标来理解,而不是数据的坐标。
正如 @fqq 提到的,只有当你的 x
和 y
的范围相等时,这条线才是身份线(也就是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()
从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来指定这条线。