子图之间的箭头

For*_*t17 3 python matplotlib subplot

我决定尝试一下这个示例代码。我能够弄清楚如何在两个子图之间绘制一条直线,即使该线位于其中一个子图的边界之外。

import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as np

fig = plt.figure(figsize=(10, 5))
ax1 = fig.add_subplot(121)
ax2 = fig.add_subplot(122)
axs = [ax1, ax2]

# Fixing random state for reproducibility
np.random.seed(19680801)

# generate some random test data
all_data = [np.random.normal(0, std, 100) for std in range(6, 10)]

# plot violin plot
axs[0].violinplot(all_data,
                  showmeans=False,
                  showmedians=True)
axs[0].set_title('Violin plot')

# plot box plot
axs[1].boxplot(all_data)
axs[1].set_title('Box plot')

# adding horizontal grid lines
for ax in axs:
    ax.yaxis.grid(True)
    ax.set_xticks([y + 1 for y in range(len(all_data))])
    ax.set_xlabel('Four separate samples')
    ax.set_ylabel('Observed values')

for tick in ax.xaxis.get_major_ticks():
    tick.label.set_fontsize(20)
plt.setp(axs[0], xticklabels=['x1', 'x2', 'x3', 'x4'])

transFigure = fig.transFigure.inverted()
coord1 = transFigure.transform(ax1.transData.transform([5,10]))
coord2 = transFigure.transform(ax2.transData.transform([2,-10]))
line = mpl.lines.Line2D((coord1[0],coord2[0]),(coord1[1],coord2[1]),
                        c='k', lw=5, transform=fig.transFigure)
fig.lines.append(line)
Run Code Online (Sandbox Code Playgroud)

是的,添加的线很丑,但我只是想让它发挥作用。

然而,我真正想做的是在子图之间制作一个箭头,如果不临时调整我自己的箭头尾部,我无法弄清楚如何做。有没有办法使用matplotlib.pyplot.arrow类来做到这一点?

Big*_*gsy 6

我还想在两个子图之间画一个箭头,但我什至不知道从哪里开始!然而,原始问题中子图示例之间的界限给了我足够的线索来开始......

首先,我将原始问题中的代码简化为最小的工作示例:

from matplotlib import lines, pyplot as plt

fig = plt.figure()

# First subplot
ax1 = fig.add_subplot(121)
plt.plot([0, 1], [0, 1])

# Second subplot
ax2 = fig.add_subplot(122)
plt.plot([0, 1], [0, 1])

# Add line from one subplot to the other
xyA = [0.5, 1.0]
ax1.plot(*xyA, "o")
xyB = [0.75, 0.25]
ax2.plot(*xyB, "o")
transFigure = fig.transFigure.inverted()
coord1 = transFigure.transform(ax1.transData.transform(xyA))
coord2 = transFigure.transform(ax2.transData.transform(xyB))
line = lines.Line2D(
    (coord1[0], coord2[0]),  # xdata
    (coord1[1], coord2[1]),  # ydata
    transform=fig.transFigure,
    color="black",
)
fig.lines.append(line)

# Show figure
plt.show()
Run Code Online (Sandbox Code Playgroud)

这会产生以下输出:

子图之间的线

然后,使用这篇博文,我认为答案是创建 amatplotlib.patches.FancyArrowPatch并将其附加到fig.patches(而不是创建 amatplotlib.lines.Line2D并将其附加到fig.lines)。在查阅matplotlib.patches.FancyArrowPatch文档并进行一些试验和错误之后,我想出了一些可以在 matplotlib 3.1.2 中使用的东西:

from matplotlib import patches, pyplot as plt

fig = plt.figure()

# First subplot
ax1 = fig.add_subplot(121)
plt.plot([0, 1], [0, 1])

# Second subplot
ax2 = fig.add_subplot(122)
plt.plot([0, 1], [0, 1])

# Add line from one subplot to the other
xyA = [0.5, 1.0]
ax1.plot(*xyA, "o")
xyB = [0.75, 0.25]
ax2.plot(*xyB, "o")
transFigure = fig.transFigure.inverted()
coord1 = transFigure.transform(ax1.transData.transform(xyA))
coord2 = transFigure.transform(ax2.transData.transform(xyB))
arrow = patches.FancyArrowPatch(
    coord1,  # posA
    coord2,  # posB
    shrinkA=0,  # so tail is exactly on posA (default shrink is 2)
    shrinkB=0,  # so head is exactly on posB (default shrink is 2)
    transform=fig.transFigure,
    color="black",
    arrowstyle="-|>",  # "normal" arrow
    mutation_scale=30,  # controls arrow head size
    linewidth=3,
)
fig.patches.append(arrow)

# Show figure
plt.show()
Run Code Online (Sandbox Code Playgroud)

但是,根据下面的评论,这在 中不起作用matplotlib 3.4.2,您会得到以下结果:

子图之间的箭头 - 不正确

请注意,箭头的末端没有与目标点(橙色圆圈)对齐,而它们应该对齐。

matplotlib版本更改还会导致原始行示例以同样的方式失败。

不过,还有一个更好的补丁!使用ConnectionPatch( docs ),它是 的子类FancyArrowPatch,而不是FancyArrowPatch直接使用,因为ConnectionPatch是专门为此用例设计的,可以更正确地处理转换,如本matplotlib文档示例所示:

fig = plt.figure()

# First subplot
ax1 = fig.add_subplot(121)
plt.plot([0, 1], [0, 1]) 

# Second subplot
ax2 = fig.add_subplot(122)
plt.plot([0, 1], [0, 1]) 

# Add line from one subplot to the other
xyA = [0.5, 1.0]
ax1.plot(*xyA, "o")
xyB = [0.75, 0.25]
ax2.plot(*xyB, "o")
# ConnectionPatch handles the transform internally so no need to get fig.transFigure
arrow = patches.ConnectionPatch(
    xyA,
    xyB,
    coordsA=ax1.transData,
    coordsB=ax2.transData,
    # Default shrink parameter is 0 so can be omitted
    color="black",
    arrowstyle="-|>",  # "normal" arrow
    mutation_scale=30,  # controls arrow head size
    linewidth=3,
)
fig.patches.append(arrow)

# Show figure
plt.show()
Run Code Online (Sandbox Code Playgroud)

matplotlib 3.1.2这会在和中产生正确的输出matplotlib 3.4.2,如下所示:

在此输入图像描述

要绘制连接 中两个子图的正确定位的线matplotlib 3.4.2,请使用ConnectionPatch上面的 a,但使用 with arrowstyle="-"(即没有箭头,所以只是一条线)。

注意:您不能使用:

  • plt.arrow因为它会自动添加到当前轴,所以只出现在一个子图中

  • matplotlib.patches.Arrow当轴图变换使箭头倾斜时

  • matplotlib.patches.FancyArrow因为这也会导致箭头倾斜