如何将图像添加到轴中的条形图(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.xlim
和 plt.ylim
(也是因为 imshow
与它们混淆。)
下面的示例代码使用了 'ada.png',因为它是 matplotlib 的标准配置,因此可以独立测试代码。现在它正在从 countryflags.io, following this post.
加载标志
请注意,图像被放置在 data coordinates 中的一个框中(在本例中为 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()
PS:正如 Ernest 在评论和 this post 中所解释的那样,使用 OffsetImage
图像的纵横比保持不变。 (此外,xlim
和 ylim
保持不变。)当有更多条形图时,图像不会缩小,因此您可能需要试验 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()
要完成@johanC 的回答,可以使用 GNU/linux 下的 iso-flags-png
和 iso3166
python 包中的标志:
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()
给出:
我想将如下所示的旗帜图像添加到我的条形图中:
我试过 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.xlim
和 plt.ylim
(也是因为 imshow
与它们混淆。)
下面的示例代码使用了 'ada.png',因为它是 matplotlib 的标准配置,因此可以独立测试代码。现在它正在从 countryflags.io, following this post.
加载标志请注意,图像被放置在 data coordinates 中的一个框中(在本例中为 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()
PS:正如 Ernest 在评论和 this post 中所解释的那样,使用 OffsetImage
图像的纵横比保持不变。 (此外,xlim
和 ylim
保持不变。)当有更多条形图时,图像不会缩小,因此您可能需要试验 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()
要完成@johanC 的回答,可以使用 GNU/linux 下的 iso-flags-png
和 iso3166
python 包中的标志:
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()
给出: