如何将图像添加到轴中的条形图(matplotlib)

Has*_*eer 7 python plot axes matplotlib figure

我想将如下所示的标志图像添加到我的条形图中:

在此输入图像描述

我尝试过 AnnotationBbox,但它显示为方形轮廓。谁能告诉我如何实现如上图所示的效果?

编辑:

下面是我的代码

ax.barh(y = y, width = values, color = r, height = 0.8)

height = 0.8
for i, (value, url) in enumerate(zip(values, image_urls)):
    response = requests.get(url)
    img = Image.open(BytesIO(response.content))

    width, height = img.size
    left = 10
    top = 10
    right = width-10
    bottom = height-10
    im1 = img.crop((left, top, right, bottom)) 
    print(im1.size)
    im1

    ax.imshow(im1, extent = [value - 6, value, i - height / 2, i + height / 2], aspect = 'auto', zorder = 2)
Run Code Online (Sandbox Code Playgroud)

编辑2:

height = 0.8
for j, (value, url) in enumerate(zip(ww, image_urls)):
    response = requests.get(url)
    img = Image.open(BytesIO(response.content))
    ax.imshow(img, extent = [value - 6, value - 2, j - height / 2, j + height / 2], aspect = 'auto', zorder = 2)

ax.set_xlim(0, max(ww)*1.05)
ax.set_ylim(-0.5, len(yy) - 0.5)
plt.tight_layout()
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

Joh*_*anC 6

.png您需要具有透明背景的格式的图像。(如果图像还没有所需的背景, Gimp 或ImageMagick等软件可以提供帮助。)

有了这样的图像,plt.imshow()就可以将其放置在情节中。该位置通过 给出extent=[x0, x1, y0, y1]。为了防止imshow强制使用相等的纵横比,请添加aspect='auto'. zorder=2有助于将图像置于条形顶部。之后, 和plt.xlim需要plt.ylim显式设置(也是因为imshow与它们混淆。)

下面的示例代码使用“ada.png”,因为它是 matplotlib 的标准配置,因此可以独立测试代码。现在它正在从countryflags.io加载标志,紧接着这篇文章

请注意,图像被放置在数据坐标中的框中(在本例中为 6 宽,0.9 高)。该框将被拉伸,例如当绘图大小调整时。您可能需要将 6 更改为另一个值,具体取决于 x 比例和图形大小。

import numpy as np
import matplotlib.pyplot as plt
# import matplotlib.cbook as cbook
import requests
from io import BytesIO

labels = ['CW', 'CV', 'GW', 'SX', 'DO']
colors = ['crimson', 'dodgerblue', 'teal', 'limegreen', 'gold']
values = 30 + np.random.randint(5, 20, len(labels)).cumsum()

height = 0.9
plt.barh(y=labels, width=values, height=height, color=colors, align='center')

for i, (label, value) in enumerate(zip(labels, values)):
    # load the image corresponding to label into img
    # with cbook.get_sample_data('ada.png') as image_file:
    #    img = plt.imread(image_file)
    response = requests.get(f'https://www.countryflags.io/{label}/flat/64.png')
    img = plt.imread(BytesIO(response.content))
    plt.imshow(img, extent=[value - 8, value - 2, i - height / 2, i + height / 2], aspect='auto', zorder=2)
plt.xlim(0, max(values) * 1.05)
plt.ylim(-0.5, len(labels) - 0.5)
plt.tight_layout()
plt.show()
Run Code Online (Sandbox Code Playgroud)

示例图

PS:正如欧内斯特在评论和这篇文章中所解释的那样,使用OffsetImage图像的长宽比保持不变。(此外,xlimylim保持不变。)当有更多条形时,图像不会缩小,因此您可能需要试验 中的因子OffsetImage(img, zoom=0.65)和 中的 x 偏移量AnnotationBbox(..., xybox=(-25, 0))

对于太短的条,一个额外的选项可以将标志放置在条之外。或者在 y 轴的左侧。

适用于水平条的代码可能如下所示:

import numpy as np
import requests
from io import BytesIO
import matplotlib.pyplot as plt
from matplotlib.offsetbox import OffsetImage, AnnotationBbox

def offset_image(x, y, label, bar_is_too_short, ax):
    response = requests.get(f'https://www.countryflags.io/{label}/flat/64.png')
    img = plt.imread(BytesIO(response.content))
    im = OffsetImage(img, zoom=0.65)
    im.image.axes = ax
    x_offset = -25
    if bar_is_too_short:
        x = 0
    ab = AnnotationBbox(im, (x, y), xybox=(x_offset, 0), frameon=False,
                        xycoords='data', boxcoords="offset points", pad=0)
    ax.add_artist(ab)

labels = ['CW', 'CV', 'GW', 'SX', 'DO']
colors = ['crimson', 'dodgerblue', 'teal', 'limegreen', 'gold']
values = 2 ** np.random.randint(2, 10, len(labels))

height = 0.9
plt.barh(y=labels, width=values, height=height, color=colors, align='center', alpha=0.8)

max_value = values.max()
for i, (label, value) in enumerate(zip(labels, values)):
    offset_image(value, i, label, bar_is_too_short=value < max_value / 10, ax=plt.gca())
plt.subplots_adjust(left=0.15)
plt.show()
Run Code Online (Sandbox Code Playgroud)

使用 OffsetImage 的示例

  • 检查现有的问答。例如[this](/sf/answers/3098483601/)建议更好地使用“OffsetImage”,从而规避数据坐标问题。 (2认同)