matplotlib 子图之间的箭头

k3bvogb1  于 2023-05-23  发布在  其他
关注(0)|答案(1)|浏览(148)

我决定用this示例代码来做一点尝试。我能够想出如何在两个子情节之间画一条直线,即使这条线在其中一个子情节的界限之外。

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)

是的,添加的线是丑陋的,但我只是想让它的功能。
然而,我真正想做的是在次要情节之间做一个箭头,我不知道如果没有陪审团操纵我自己的箭头尾巴。有没有一种方法可以使用matplotlib.pyplot.arrow类来实现这一点?

wd2eg0qa

wd2eg0qa1#

我还想在两个次要情节之间画一个箭头,但我甚至不知道从哪里开始!然而,原题中次要情节之间的线例子给了我足够的线索开始...
首先,我将原始问题中的代码简化为一个最小的工作示例:

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()

这将生成以下输出:

然后,使用this blog post,我认为答案是创建一个matplotlib.patches.FancyArrowPatch并将其附加到fig.patches(而不是创建一个matplotlib.lines.Line2D并将其附加到fig.lines)。在参考了matplotlib.patches.FancyArrowPatch documentation之后,加上一些试验和错误,我想出了一些在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()

然而,根据下面的注解,这在matplotlib 3.4.2中不起作用,在那里你得到了这个:

请注意,箭头的两端没有与目标点(橙子圆圈)对齐,它们应该这样做。
这个matplotlib版本更改也会以相同的方式导致原始行示例失败。
不过,还有更好的补丁!使用ConnectionPatchdocs),它是FancyArrowPatch的子类,而不是直接使用FancyArrowPatch,因为ConnectionPatch是专门为此用例设计的,并且更正确地处理转换,如matplotlib documentation example所示:

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()

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

要在matplotlib 3.4.2中绘制一条连接两个子图的正确定位的线,请使用如上所述的ConnectionPatch,但使用arrowstyle="-"(即没有箭头,所以只有一条线)。
注意:您不能用途:

  • plt.arrow,因为它会自动添加到当前轴,因此仅出现在一个子图中
  • matplotlib.patches.Arrow作为轴-图形变换偏斜箭头
  • matplotlib.patches.FancyArrow,因为这也会导致箭头歪斜

相关问题