簡體   English   中英

如何將圖像添加到軸中的條形圖(matplotlib)

[英]How can I add images to bars in axes (matplotlib)

我想在我的條形圖中添加如下標志圖像:

在此處輸入圖像描述

我試過 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)

編輯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()

在此處輸入圖像描述

您需要具有透明背景的.png格式的圖像。 (如 Gimp 或ImageMagick等軟件可以在圖像沒有所需背景的情況下提供幫助。)

有了這樣的圖像, plt.imshow()可以將它放在 plot 中。 該位置通過extent=[x0, x1, y0, y1]給出。 為防止imshow強制使用相等的縱橫比,請添加aspect='auto' zorder=2有助於將圖像放在條形圖的頂部。 之后, plt.xlimplt.ylim需要明確設置(也因為imshow與它們混淆。)

下面的示例代碼使用了 matplotlib 標配的“ada.png”,因此可以單獨測試代碼。 現在它正在從countryflags.io加載標志,跟隨這篇文章

請注意,圖像被放置在數據坐標中的框中(在本例中為 6 寬和 0.9 高)。 例如,當 plot 調整大小時,此框將被拉伸。 您可能希望將 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()

示例圖

PS:正如 Ernest 在評論和這篇文章中所解釋的,使用OffsetImage圖像的縱橫比保持不變。 (此外, xlimylim保持不變。)當有更多條時,圖像不會縮小,因此您可能需要試驗OffsetImage(img, zoom=0.65)中的因子和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()

使用 <code>OffsetImage</code> 的示例

要完成@johanC 的回答,可以使用 GNU/linux 下的iso-flags-pngiso3166 python package 中的標志:

import matplotlib.pyplot as plt
from iso3166 import countries
import matplotlib.image as mpimg


def pos_image(x, y, pays, haut):
    pays = countries.get(pays).alpha2.lower()
    fichier = "/usr/share/iso-flags-png-320x240"
    fichier += f"/{pays}.png"
    im = mpimg.imread(fichier)
    ratio = 4 / 3
    w = ratio * haut
    ax.imshow(im,
              extent=(x - w, x, y, y + haut),
              zorder=2)


plt.style.use('seaborn')
fig, ax = plt.subplots()

liste_pays = [('France', 10), ('USA', 9), ('Spain', 5), ('Italy', 5)]

X = [p[1] for p in liste_pays]
Y = [p[0] for p in liste_pays]

haut = .8
r = ax.barh(y=Y, width=X, height=haut, zorder=1)
y_bar = [rectangle.get_y() for rectangle in r]
for pays, y in zip(liste_pays, y_bar):
    pos_image(pays[1], y, pays[0], haut)


plt.show()

這使: 在此處輸入圖像描述

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM