使用 fig.transFigure 在 ylabel 上绘制补丁

Posted

技术标签:

【中文标题】使用 fig.transFigure 在 ylabel 上绘制补丁【英文标题】:Using fig.transFigure to draw a patch on ylabel 【发布时间】:2021-12-13 13:38:34 【问题描述】:

我创建了一系列点,我想将其转换为补丁。

然后目标是在 y-label 的左侧绘制补丁(参见图中的红色部分),或在图中的任何其他部分绘制它。

虽然可以用 Gridspec 完成,但我想用 Patch 来完成。

import matplotlib.pyplot as plt
import numpy as np

plt.figure()
npoints = 100
td = np.linspace(np.pi*3/4, np.pi*5/4, npoints)
xd = np.cos(td)
yd = np.sin(td)
plt.plot(xd,yd)

EDIT1:

我现在可以制作补丁(只需将其移到轴外):

import matplotlib.pyplot as plt
import numpy as np
import matplotlib.path as mpath
import matplotlib.patches as mpatches

npoints = 100
td = np.linspace(np.pi*3/4, np.pi*5/4, npoints)
xd = np.cos(td)
yd = np.sin(td)

fig, ax = plt.subplots()
ax.axis([-2, 0, -1, 1])
verts=np.c_[xd,yd]
codes = np.ones(len(xd))*2 # Path.LINETO for all points except the first
codes[0] = 1 #Path.MOVETO only for the first point
path1 = mpath.Path(verts, codes)
patch = mpatches.PathPatch(path1, facecolor='none')
ax.add_patch(patch)

结果:

现在,我只需要将它移到轴外,可能使用平移或缩放。

我确信实现它的关键在 Matplotlib Transforms tutorial 的某个地方,更具体地说,我很确定解决方案是使用 fig.transFigure

编辑 2:快到了!

为了使用 图形坐标(在 [0,1] 之间),我对定义路径的点进行了标准化。我没有使用ax.add_patch() 向轴添加补丁,而是使用fig.add_artist() 在轴上向图形添加补丁。

import matplotlib.pyplot as plt
import numpy as np
import matplotlib.path as mpath
import matplotlib.patches as mpatches

#Normalized Data
def normalize(x):
    return (x - min(x)) / (max(x) - min(x))

#plt.figure()
npoints = 100
td = np.linspace(np.pi*3/4, np.pi*5/4, npoints)
xd = np.cos(td)
yd = np.sin(td)
#plt.plot(xd,yd)

xd = normalize(xd)
yd = normalize(yd)

fig, ax = plt.subplots()
ax.axis([-2, 2, -1, 1])
verts=np.c_[xd,yd]
codes = np.ones(len(xd))*2 # Path.LINETO for all points except the first
codes[0] = 1 #Path.MOVETO only for the first point
path1 = mpath.Path(verts, codes)
patch1 = mpatches.PathPatch(path1, facecolor='none')
ax.add_patch(patch1)

patch2 = mpatches.PathPatch(path1, fc='none', ec='red', transform=fig.transFigure)
fig.add_artist(patch2)

到目前为止的结果:

这样做,我只需要缩放和翻译补丁,也许使用 Affine2D。

编辑 3:完成!

我终于做到了!我在scale()translate() 参数中使用了Try and Error,因为我没有得到它们使用的坐标系。但是,最好能得到准确的 y 中心(图坐标中的 0.5)。

完整代码如下:

import numpy as np
import matplotlib.path as mpath
import matplotlib.patches as mpatches

#Normalized Data
def normalize(x):
    return (x - min(x)) / (max(x) - min(x))

npoints = 100
td = np.linspace(np.pi*3/4, np.pi*5/4, npoints)
xd = np.cos(td)
yd = np.sin(td)

xd = normalize(xd)
yd = normalize(yd)

fig, ax = plt.subplots()
ax.axis([-2, 2, -1, 1])
verts=np.c_[xd,yd]
codes = np.ones(len(xd))*2 # Path.LINETO for all points except the first
codes[0] = 1 #Path.MOVETO only for the first point
path1 = mpath.Path(verts, codes)
patch1 = mpatches.PathPatch(path1, fc='none', ec='green')
ax.add_patch(patch1) #draw inside axis

patch2 = mpatches.PathPatch(path1, fc='none', ec='C0', transform=fig.transFigure)
fig.add_artist(patch2) #this works! Draw on figure    

import matplotlib.transforms as mtransforms
tt = fig.transFigure + mtransforms.Affine2D().scale(0.02, 0.8).translate(10,45)
patch3 = mpatches.PathPatch(path1, fc='none', ec='red', transform=tt)
fig.add_artist(patch3)

结果图:

【问题讨论】:

这个Path tutorial 可能是一个很好的帮助。 既然我已经完成了,我应该回答我自己的问题还是将解决方案留在问题中? 佩德罗,你永远不应该用答案更新问题。请恢复最后的编辑并将您的解决方案作为答案发布。 (是的,这是允许的,请参阅 Can I answer my own question?) 【参考方案1】:

正如@Pedro 所指出的,大部分内容都可以在他链接的教程中找到。不过,这里有一个简短的回答。

基本上,这几乎就像是在创建线图。只需指定要通过的点,将它们添加到列表中即可。

在这个例子中,我想通过绘图上的一些点,然后“将笔从纸上抬起”并从另一个点继续。所以我们创建了两个列表——一个包含我想要使用的点,第二个列表描述了我想对这些点做什么。 Path.MOVETO 将把你的“笔”移动到给定的点而不画一条线,所以我们用它来设置我们的初始起点。 Path.LINETO 创建一条从当前笔位置开始到列表中下一行的直线。

import matplotlib.pyplot as plt
from matplotlib.path import Path
import matplotlib.patches as patches

# Points you want to "pass through"
pts = [
    (0, 0),
    (0.2, 0.2),
    (0.4, 0.2),
    (0.4, 0.4),
    (0.4, 0.6),
    (0.6, 0.6),
    (0.8, 0.8)
]

# What you want to "do" with each point
codes = [
    Path.MOVETO, # inital point
    Path.LINETO,
    Path.LINETO,
    Path.LINETO,
    Path.MOVETO, # pick up the pen
    Path.LINETO,
    Path.LINETO
]

# Create path object
# https://matplotlib.org/stable/tutorials/advanced/path_tutorial.html
path = Path(pts, codes)
patch = patches.PathPatch(path, lw='2', color='r', fill=False)

# patch the path to the figure
fig, ax = plt.subplots()
ax.add_patch(patch)
plt.show()

代码执行结果:

【讨论】:

好东西。我已经思考了一段时间如何在轴“后面”绘制,但我无法弄清楚。我现在唯一的建议是您将轴移动/居中在绘图中间的某个位置,而不是让它们保持在左下角。我不能在 cmets 中粘贴代码,但是看看这篇文章,有一些很好的答案。我希望这对你有用。 ***.com/questions/31556446/…

以上是关于使用 fig.transFigure 在 ylabel 上绘制补丁的主要内容,如果未能解决你的问题,请参考以下文章

R语言 条形图

R语言 图表

R 语言画图

将希腊字符添加到轴标题

R语言作图之一

R语言低级绘图函数-title