Matplotlib有一个在坐标轴上绘制对角线的函数吗?

80

Matplotlib的Axes有axhlineaxvline函数,用于在Axes上独立于数据比例绘制给定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代码,以在axes坐标中将xy都绘制为[0, 1]


看看这个链接:https://dev59.com/Tmsz5IYBdhLWcg3wYGoz#14348481,我认为那可能是你想要的。 - EL_DON
Matplotlib 很可能会在 3.3.0 版本中包含此功能:https://github.com/matplotlib/matplotlib/pull/15330 - Eric
6个回答

62

通过以下代码可以在绘图中从左下角到右上角画出一条对角线:

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

使用transform=ax.transAxes,提供的xy坐标被解释为坐标轴坐标,而不是数据坐标。

正如@fqq所指出的那样,当你的xy限制相等时,这只是一个单位线。要绘制线性方程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()

8
这并没有画出y=x这条线,这正是原帖提出的要求。 - fqq
3
没错,就像我在回答中所说的那样,它会在图形的左下到右上绘制一条线。我正在考虑是否要加上一个警告,即您仍然需要确保图形的x和y轴限制相等。如果您正在绘制单位线,则通常最好设置相等的x和y轴限制。 - JaminSore
1
谢谢您的详细说明。在所有这些情况下,甚至包括您的回调解决方案,都会出现另一件事情。那就是添加对角线将更改自动轴限制。因此,我更喜欢访问xlim / ylim,绘制线条,然后恢复xlim / ylim! - creanion
小心,这并不是画一条斜率为1的Y=X直线。要画出没有边界的X=Y直线,请使用plt.axline((0,0), slope=1) - JZ1
@JZ1请告诉我,如果add_identity回调解决方案没有涵盖某个特殊情况,请让我知道。自从我发布这个解决方案以来,近八年来我还没有遇到过这种情况,所以我真的很好奇你是否遇到过。顺便说一下,是的,现在plt.axline是首选的解决方案 :)。 - JaminSore

37
在屏幕的左下角到右上角绘制一条对角线非常简单,您只需使用ax.plot(ax.get_xlim(), ax.get_ylim(), ls="--", c=".3")。方法ax.get_xlim()将返回当前x轴的值(y轴同理)。
但是,如果您想要能够使用图表进行缩放,那么情况就会稍微复杂一些,因为您绘制的对角线将不会更改以匹配新的xlims和ylims。
在这种情况下,您可以使用回调来检查xlims(或ylims)何时更改,并相应地更改对角线中的数据(如下所示)。我在此示例中找到了回调方法。更多信息也可以在这里找到。
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(...以下的所有内容。

1
抱歉,我知道如何以编程方式完成这个任务,但我想知道 matplotlib 是否有什么方法可以避免获取限制等操作,因为这些操作可能很脆弱,会发生变化。我不太担心交互式缩放,只是一般情况下,随着数据添加到图中,数据限制会发生变化,或者如果您想要添加一个与任何其他要绘制的数据无关的对角线。我将编辑我的问题以使其更清晰。 - mwaskom
只是提醒一下,对角线不仅会在交互式缩放时改变,而且当您向绘图添加数据并通过例如ax.set_xlim(a, b)更改它时也会改变。我不明白您所说的“脆弱因为它们可能会改变”的意思?我的回答的整个想法是,每当它们发生变化时,对角线都将被修改以考虑到这一点。 - Ffisegydd
明白了。回调提示很有用。我的意思是在初始绘制时只使用get_xlim()get_ylim() - mwaskom

33

从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和一个斜率slope定义。

该函数在屏幕上绘制一条直线,不受x和y轴比例尺的影响,因此适用于在semilog图中绘制指数衰减曲线,在loglog图中绘制幂律曲线等。但是,斜率只适用于线性比例尺;对于所有其他比例尺,其含义并不清晰,因此行为未定义。请对非线性比例尺使用点xy1、xy2来指定直线。


7
确实,ax.axline((1, 1), slope=1)可以实现此目的。 - Emanuel
5
对我来说,这个不起作用。我遇到了一个AttributeError错误:'AxesSubplot'对象没有'axline'属性。如果我稍微改变一下设置,我会得到:AttributeError:模块“matplotlib.pyplot”没有'axline'属性。在这两种设置中,我都可以使用hline和vline而没有问题。 - Linda
如果你的matplotlib足够新,这显然比更受欢迎的答案要好得多。谢谢! - user1601093
对于和@Linda遇到相同问题的任何人:请将您的matplotlib版本更新至至少3.3.0。 - Peiffap
1
如果你收到以下错误信息:TypeError: 'slope' cannot be used with non-linear scales,那么很可能是因为你已经使用了对数刻度等非线性刻度来缩放至少一个绘图轴。为避免此错误,请在更改绘图轴的比例尺之前创建对角线,并且这样可以正确地工作。 - Miguel

12

如果轴在范围[0,1]内,可以按照以下方式解决:

ident = [0.0, 1.0]
plt.plot(ident,ident)

2

这将始终起作用,因为它动态调整轴比例

ax.axline([ax.get_xlim()[0], ax.get_ylim()[0]], [ax.get_xlim()[1], ax.get_ylim()[1]])

0
根据 https://matplotlib.org/stable/gallery/pyplots/axline.html,您可以使用 plt.axline。下面是文档中的示例:
import matplotlib.pyplot as plt

t = np.linspace(-10, 10, 100)
sig = 1 / (1 + np.exp(-t))

plt.axhline(y=0, color="black", linestyle="--")
plt.axhline(y=0.5, color="black", linestyle=":")
plt.axhline(y=1.0, color="black", linestyle="--")
plt.axvline(color="grey")
plt.axline((0, 0.5), slope=0.25, color="black", linestyle=(0, (5, 5)))
plt.plot(t, sig, linewidth=2, label=r"$\sigma(t) = \frac{1}{1 + e^{-t}}$")
plt.xlim(-10, 10)
plt.xlabel("t")
plt.legend(fontsize=14)
plt.show()```

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接