如何在 Matplotlib 中将自定义标记与形状内的文本一起使用?
How to use a cutom marker in Matplotlib with text inside a shape?
背景
在 Matplotlib 中,我们可以使用 $ ..... $
(Reference 1)
使用 mathtext 作为标记来渲染字符串
问题
有什么方法可以将此文本包含在圆形或矩形框内,或任何不同的形状中?类似注册符号如图here
我想在图上使用这个标记,如下所示:
此图中使用了文本“$T$”,我希望将文本括在圆形或矩形中。
解决方案
正如答案评论中所建议的,我在文本标记之前绘制了一个稍大的方形标记。这解决了这个问题。
最终图如下图:
编辑:最简单的方法是将所需的 "frames" 补丁简单地放置在与标记相同的位置。只需确保它们具有较低的 zorder
,这样它们就不会覆盖数据点。
下面更复杂的方法:
你可以制作补丁。这是我用来制作自定义问号的示例:
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.markers as m
fig, ax = plt.subplots()
lim = -5.8, 5.7
ax.set(xlim = lim, ylim = lim)
marker_obj = m.MarkerStyle('$?$') #Here you place your letter
path = marker_obj.get_path().transformed(marker_obj.get_transform())
path._vertices = np.array(path._vertices)*8 #To make it larger
patch = mpl.patches.PathPatch(path, facecolor="cornflowerblue", lw=2)
ax.add_patch(patch)
def translate_verts(patch, i=0, j=0, z=None):
patch._path._vertices = patch._path._vertices + [i, j]
def rescale_verts(patch, factor = 1):
patch._path._vertices = patch._path._vertices * factor
#translate_verts(patch, i=-0.7, j=-0.1)
circ = mpl.patches.Arc([0,0], 11, 11,
angle=0.0, theta1=0.0, theta2=360.0,
lw=10, facecolor = "cornflowerblue",
edgecolor = "black")
ax.add_patch(circ)#One of the rings around the questionmark
circ = mpl.patches.Arc([0,0], 10.5, 10.5,
angle=0.0, theta1=0.0, theta2=360.0,
lw=10, edgecolor = "cornflowerblue")
ax.add_patch(circ)#Another one of the rings around the question mark
circ = mpl.patches.Arc([0,0], 10, 10,
angle=0.0, theta1=0.0, theta2=360.0,
lw=10, edgecolor = "black")
ax.add_patch(circ)
if __name__ == "__main__":
ax.axis("off")
ax.set_position([0, 0, 1, 1])
fig.canvas.draw()
#plt.savefig("question.png", dpi=40)
plt.show()
编辑,第二个答案:
创建由其他补丁组成的自定义补丁:
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import mpl_toolkits.mplot3d.art3d as art3d
class PlanetPatch(mpl.patches.Circle):
"""
This class combines many patches to make a custom patch
The best way to reproduce such a thing is to read the
source code for all patches you plan on combining.
Also make use of ratios as often as possible to maintain
proportionality between patches of different sizes"""
cz = 0
def __init__(self, xy, radius,
color = None, linewidth = 20,
edgecolor = "black", ringcolor = "white",
*args, **kwargs):
ratio = radius/6
mpl.patches.Circle.__init__(self, xy, radius,
linewidth = linewidth*ratio,
color = color,
zorder = PlanetPatch.cz,
*args, **kwargs)
self.set_edgecolor(edgecolor)
xy_ringcontour = np.array(xy)+[0, radius*-0.2/6]
self.xy_ringcontour = xy_ringcontour - np.array(xy)
self.ring_contour = mpl.patches.Arc(xy_ringcontour,
15*radius/6, 4*radius/6,
angle =10, theta1 = 165,
theta2 = 14.5,
fill = False,
linewidth = 65*linewidth*ratio/20,
zorder = 1+PlanetPatch.cz)
self.ring_inner = mpl.patches.Arc(xy_ringcontour,
15*radius/6, 4*radius/6,
angle = 10, theta1 = 165 ,
theta2 = 14.5,fill = False,
linewidth = 36*linewidth*ratio/20,
zorder = 2+PlanetPatch.cz)
self.top = mpl.patches.Wedge([0,0], radius, theta1 = 8,
theta2 = 192,
zorder=3+PlanetPatch.cz)
self.xy_init = xy
self.top._path._vertices=self.top._path._vertices+xy
self.ring_contour._edgecolor = self._edgecolor
self.ring_inner.set_edgecolor(ringcolor)
self.top._facecolor = self._facecolor
def add_to_ax(self, ax):
ax.add_patch(self)
ax.add_patch(self.ring_contour)
ax.add_patch(self.ring_inner)
ax.add_patch(self.top)
def translate(self, dx, dy):
self._center = self.center + [dx,dy]
self.ring_inner._center = self.ring_inner._center +[dx, dy]
self.ring_contour._center = self.ring_contour._center + [dx,dy]
self.top._path._vertices = self.top._path._vertices + [dx,dy]
def set_xy(self, new_xy):
"""As you can see all patches have different ways
to have their positions updated"""
new_xy = np.array(new_xy)
self._center = new_xy
self.ring_inner._center = self.xy_ringcontour + new_xy
self.ring_contour._center = self.xy_ringcontour + new_xy
self.top._path._vertices += new_xy - self.xy_init
fig = plt.figure(figsize=(6, 6))
ax = fig.add_subplot()
lim = -8.5, 8.6
ax.set(xlim = lim, ylim = lim,
facecolor = "black")
planets = []
colors = mpl.colors.cnames
colors = [c for c in colors]
for x in range(100):
xy = np.random.randint(-7, 7, 2)
r = np.random.randint(1, 15)/30
color = np.random.choice(colors)
planet = PlanetPatch(xy, r, linewidth = 20,
color = color,
ringcolor = np.random.choice(colors),
edgecolor = np.random.choice(colors))
planet.add_to_ax(ax)
planets.append(planet)
fig.canvas.draw()
#plt.savefig("planet.png", dpi=10)
plt.show()
背景
在 Matplotlib 中,我们可以使用 $ ..... $
(Reference 1)
问题
有什么方法可以将此文本包含在圆形或矩形框内,或任何不同的形状中?类似注册符号如图here
我想在图上使用这个标记,如下所示:
此图中使用了文本“$T$”,我希望将文本括在圆形或矩形中。
解决方案
正如答案评论中所建议的,我在文本标记之前绘制了一个稍大的方形标记。这解决了这个问题。
最终图如下图:
编辑:最简单的方法是将所需的 "frames" 补丁简单地放置在与标记相同的位置。只需确保它们具有较低的 zorder
,这样它们就不会覆盖数据点。
下面更复杂的方法:
你可以制作补丁。这是我用来制作自定义问号的示例:
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.markers as m
fig, ax = plt.subplots()
lim = -5.8, 5.7
ax.set(xlim = lim, ylim = lim)
marker_obj = m.MarkerStyle('$?$') #Here you place your letter
path = marker_obj.get_path().transformed(marker_obj.get_transform())
path._vertices = np.array(path._vertices)*8 #To make it larger
patch = mpl.patches.PathPatch(path, facecolor="cornflowerblue", lw=2)
ax.add_patch(patch)
def translate_verts(patch, i=0, j=0, z=None):
patch._path._vertices = patch._path._vertices + [i, j]
def rescale_verts(patch, factor = 1):
patch._path._vertices = patch._path._vertices * factor
#translate_verts(patch, i=-0.7, j=-0.1)
circ = mpl.patches.Arc([0,0], 11, 11,
angle=0.0, theta1=0.0, theta2=360.0,
lw=10, facecolor = "cornflowerblue",
edgecolor = "black")
ax.add_patch(circ)#One of the rings around the questionmark
circ = mpl.patches.Arc([0,0], 10.5, 10.5,
angle=0.0, theta1=0.0, theta2=360.0,
lw=10, edgecolor = "cornflowerblue")
ax.add_patch(circ)#Another one of the rings around the question mark
circ = mpl.patches.Arc([0,0], 10, 10,
angle=0.0, theta1=0.0, theta2=360.0,
lw=10, edgecolor = "black")
ax.add_patch(circ)
if __name__ == "__main__":
ax.axis("off")
ax.set_position([0, 0, 1, 1])
fig.canvas.draw()
#plt.savefig("question.png", dpi=40)
plt.show()
编辑,第二个答案: 创建由其他补丁组成的自定义补丁:
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import mpl_toolkits.mplot3d.art3d as art3d
class PlanetPatch(mpl.patches.Circle):
"""
This class combines many patches to make a custom patch
The best way to reproduce such a thing is to read the
source code for all patches you plan on combining.
Also make use of ratios as often as possible to maintain
proportionality between patches of different sizes"""
cz = 0
def __init__(self, xy, radius,
color = None, linewidth = 20,
edgecolor = "black", ringcolor = "white",
*args, **kwargs):
ratio = radius/6
mpl.patches.Circle.__init__(self, xy, radius,
linewidth = linewidth*ratio,
color = color,
zorder = PlanetPatch.cz,
*args, **kwargs)
self.set_edgecolor(edgecolor)
xy_ringcontour = np.array(xy)+[0, radius*-0.2/6]
self.xy_ringcontour = xy_ringcontour - np.array(xy)
self.ring_contour = mpl.patches.Arc(xy_ringcontour,
15*radius/6, 4*radius/6,
angle =10, theta1 = 165,
theta2 = 14.5,
fill = False,
linewidth = 65*linewidth*ratio/20,
zorder = 1+PlanetPatch.cz)
self.ring_inner = mpl.patches.Arc(xy_ringcontour,
15*radius/6, 4*radius/6,
angle = 10, theta1 = 165 ,
theta2 = 14.5,fill = False,
linewidth = 36*linewidth*ratio/20,
zorder = 2+PlanetPatch.cz)
self.top = mpl.patches.Wedge([0,0], radius, theta1 = 8,
theta2 = 192,
zorder=3+PlanetPatch.cz)
self.xy_init = xy
self.top._path._vertices=self.top._path._vertices+xy
self.ring_contour._edgecolor = self._edgecolor
self.ring_inner.set_edgecolor(ringcolor)
self.top._facecolor = self._facecolor
def add_to_ax(self, ax):
ax.add_patch(self)
ax.add_patch(self.ring_contour)
ax.add_patch(self.ring_inner)
ax.add_patch(self.top)
def translate(self, dx, dy):
self._center = self.center + [dx,dy]
self.ring_inner._center = self.ring_inner._center +[dx, dy]
self.ring_contour._center = self.ring_contour._center + [dx,dy]
self.top._path._vertices = self.top._path._vertices + [dx,dy]
def set_xy(self, new_xy):
"""As you can see all patches have different ways
to have their positions updated"""
new_xy = np.array(new_xy)
self._center = new_xy
self.ring_inner._center = self.xy_ringcontour + new_xy
self.ring_contour._center = self.xy_ringcontour + new_xy
self.top._path._vertices += new_xy - self.xy_init
fig = plt.figure(figsize=(6, 6))
ax = fig.add_subplot()
lim = -8.5, 8.6
ax.set(xlim = lim, ylim = lim,
facecolor = "black")
planets = []
colors = mpl.colors.cnames
colors = [c for c in colors]
for x in range(100):
xy = np.random.randint(-7, 7, 2)
r = np.random.randint(1, 15)/30
color = np.random.choice(colors)
planet = PlanetPatch(xy, r, linewidth = 20,
color = color,
ringcolor = np.random.choice(colors),
edgecolor = np.random.choice(colors))
planet.add_to_ax(ax)
planets.append(planet)
fig.canvas.draw()
#plt.savefig("planet.png", dpi=10)
plt.show()