关于python:如何使用matplotlib使图的xtick标签成为简单图形?

How can I make the xtick labels of a plot be simple drawings using matplotlib?

我想用一个简单的图形(由线和圆组成)作为每个x刻度的标签,而不是用单词或数字作为x轴的刻度标签。 这可能吗? 如果是这样,在matplotlib中进行处理的最佳方法是什么?


我将删除刻度线标签,并用补丁替换文本。这是执行此任务的简短示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import matplotlib.pyplot as plt
import matplotlib.patches as patches


# define where to put symbols vertically
TICKYPOS = -.6

fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(range(10))

# set ticks where your images will be
ax.get_xaxis().set_ticks([2,4,6,8])
# remove tick labels
ax.get_xaxis().set_ticklabels([])


# add a series of patches to serve as tick labels
ax.add_patch(patches.Circle((2,TICKYPOS),radius=.2,
                            fill=True,clip_on=False))
ax.add_patch(patches.Circle((4,TICKYPOS),radius=.2,
                            fill=False,clip_on=False))
ax.add_patch(patches.Rectangle((6-.1,TICKYPOS-.05),.2,.2,
                               fill=True,clip_on=False))
ax.add_patch(patches.Rectangle((8-.1,TICKYPOS-.05),.2,.2,
                               fill=False,clip_on=False))

结果如下图:

enter image description here

clip_on设置为False是关键,否则将不显示轴外的patches。面片的坐标和大小(半径,宽度,高度等)将取决于轴在图中的位置。例如,如果您考虑对子图进行此操作,则需要对色标位置敏感,以免与其他轴重叠。您可能需要花时间研究转换,并在其他单位(轴,图形或显示)中定义位置和大小。

如果您有要用于符号的特定图像文件,则可以使用BboxImage类创建要添加到轴而不是色块的艺术家。例如,我使用以下脚本制作了一个简单的图标:

1
2
3
4
5
6
7
8
9
10
import matplotlib.pyplot as plt

fig = plt.figure(figsize=(1,1),dpi=400)
ax = fig.add_axes([0,0,1,1],frameon=False)
ax.set_axis_off()

ax.plot(range(10),linewidth=32)
ax.plot(range(9,-1,-1),linewidth=32)

fig.savefig('thumb.png')

产生此图像:

enter image description here

然后,我在想要刻度线标签的位置创建了一个BboxImage,并设置了所需的大小:

1
2
3
4
5
6
7
8
9
10
11
12
lowerCorner = ax.transData.transform((.8,TICKYPOS-.2))
upperCorner = ax.transData.transform((1.2,TICKYPOS+.2))

bbox_image = BboxImage(Bbox([lowerCorner[0],
                             lowerCorner[1],
                             upperCorner[0],
                             upperCorner[1],
                             ]),
                       norm = None,
                       origin=None,
                       clip_on=False,
                       )

注意我如何使用transData转换将数据单元转换为显示单元,这是Bbox的定义所必需的。

现在,我使用imread例程读取图像,并将其结果(一个numpy数组)设置为bbox_image的数据,并将艺术家添加到轴:

1
2
bbox_image.set_data(imread('thumb.png'))
ax.add_artist(bbox_image)

结果是一个更新的数字:
enter image description here

如果您确实直接使用图像,请确保导入所需的类和方法:

1
2
from matplotlib.image import BboxImage,imread
from matplotlib.transforms import Bbox

另一个答案有一些缺点,因为它使用静态坐标。因此,在更改图形大小或缩放和平移绘图时,它将不起作用。

更好的选择是直接在所选坐标系中定义位置。对于x轴,将数据坐标用于x位置,将轴坐标用于y位置是有意义的。

使用matplotlib.offsetbox es使其变得相当简单。下面的代码分别将一个带圆圈的框和一个带图像的框放置在坐标(-5,0)和(5,0)处,并将它们向下方偏移一点,以使它们看起来像是刻度标签。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.offsetbox import (DrawingArea, OffsetImage,AnnotationBbox)

fig, ax = plt.subplots()
ax.plot([-10,10], [1,3])

# Annotate the 1st position with a circle patch
da = DrawingArea(20, 20, 10, 10)
p = mpatches.Circle((0, 0), 10)
da.add_artist(p)

ab = AnnotationBbox(da, (-5,0),
                    xybox=(0, -7),
                    xycoords=("data","axes fraction"),
                    box_alignment=(.5, 1),
                    boxcoords="offset points",
                    bboxprops={"edgecolor" :"none"})

ax.add_artist(ab)

# Annotate the 2nd position with an image
arr_img = plt.imread("https://i.stack.imgur.com/FmX9n.png", format='png')

imagebox = OffsetImage(arr_img, zoom=0.2)
imagebox.image.axes = ax

ab = AnnotationBbox(imagebox, (5,0),
                    xybox=(0, -7),
                    xycoords=("data","axes fraction"),
                    boxcoords="offset points",
                    box_alignment=(.5, 1),
                    bboxprops={"edgecolor" :"none"})

ax.add_artist(ab)

plt.show()

enter image description here

请注意,许多形状以unicode符号的形式存在,因此人们可以简单地用这些符号设置刻度标签。有关这种解决方案,请参见如何在matplotlib或seaborn中使用彩色形状作为yticks?