Как сделать ярлыки xtick на графике простыми рисунками с использованием matplotlib?

Вместо слов или цифр, являющихся метками метки оси x, я хочу нарисовать простой рисунок (сделанный из линий и кругов) в качестве метки для каждого x tick. Это возможно? Если да, то каков наилучший способ сделать это в matplotlib?

Ответ 1

Я бы удалил метки тика и заменил текст патчи. Вот краткий пример выполнения этой задачи:

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 вне осей не будет отображаться. Координаты и размеры (радиус, ширина, высота и т.д.) Патчей будут зависеть от того, где ваши оси находятся на рисунке. Например, если вы планируете делать это с помощью подзаголовков, вам нужно быть чувствительным к размещению патчей, чтобы не перекрывать другие оси. Возможно, вам стоит исследовать Transformations и определить позиции и размеры в другом блоке (оси, рисунок или дисплей).

Если у вас есть определенные файлы изображений, которые вы хотите использовать для символов, вы можете использовать класс BboxImage для создания художников, которые будут добавлены к осям вместо патчей. Например, я сделал простую иконку со следующим script:

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 в месте, где я хочу метку ярлыка, и размера, который я хочу:

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 и добавьте художника к осям:

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

В результате получается обновленная цифра: enter image description here

Если вы используете непосредственно изображения, обязательно импортируйте необходимые классы и методы:

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

Ответ 2

Другой ответ имеет некоторые недостатки, поскольку он использует статические координаты. Это, следовательно, не будет работать при изменении размера фигуры или масштабирования и панорамирования графика.

Лучшим вариантом является прямое определение позиций в выбранных системах координат. Для xaxis имеет смысл использовать координаты данных для координат x и координат осей для y-позиции.

Использование matplotlib.offsetbox es делает это довольно простым. Следующее разместило бы коробку с кругом и коробку с изображением в координатах (-5, 0) и (5,0) соответственно и смещает их немного к нижнему, чтобы они выглядели так, как будто они были метками,

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