Adding image to legend in matplotlib returns error: AttributeError: 'BarContainer' object has no attribute '_transform'
Adding image to legend in matplotlib returns error: AttributeError: 'BarContainer' object has no attribute '_transform'
给定以下代码:
import numpy as np
import matplotlib.pyplot as plt
import os, sys
labels = ['G1', 'G2', 'G3', 'G4', 'G5']
men_means = [20, 34, 30, 35, 27]
women_means = [25, 32, 34, 20, 25]
x = np.arange(len(labels)) # the label locations
width = 0.35
fig, ax = plt.subplots()
rects1 = ax.bar(x - width/2, men_means, width, label='Men')
rects2 = ax.bar(x + width/2, women_means, width, label='Women')
# Add some text for labels, title and custom x-axis tick labels, etc.
ax.set_ylabel('Scores')
ax.set_title('Scores by group and gender')
ax.set_xticks(x)
ax.legend() # oringinal legend
ax.bar_label(rects1, padding=3)
ax.bar_label(rects2, padding=3)
fig.tight_layout()
plt.savefig('bar.png')
哪个 returns 这个条形图:
我想用 and which are saved in my directory from emojipedia 的表情符号替换图例 ['Men', 'Women']
。
为此,我将以下 class ImageHandler
添加到我的脚本中,摘自 here:
from matplotlib.transforms import Bbox, TransformedBbox
from matplotlib.legend_handler import HandlerBase
from matplotlib.image import BboxImage
class ImageHandler(HandlerBase):
def create_artists(self, legend, orig_handle, Xd_, Yd_, W_, H_, fontsize, trans):
# enlarge the image by these margins
sx, sy = self.image_stretch
# create a bounding box to house the image
bb = Bbox.from_bounds(Xd_ - sx, Yd_ - sy, W_ + sx, H_ + sy )
tbb = TransformedBbox(bb, trans)
image = BboxImage(tbb)
image.set_data(self.image_data)
self.update_prop(image, orig_handle, legend)
return [image]
def set_image(self, image_path, image_stretch=(0, 0)):
self.image_data = plt.imread(image_path)
self.image_stretch = image_stretch
因此,我将 ax.legend()
替换为:
emoji_dataset = os.path.join( os.environ['HOME'], 'Datasets', 'Emojis')
h1 = ImageHandler()
h2 = ImageHandler()
h1.set_image(os.path.join(emoji_dataset, 'man.png'), image_stretch=(0, 20))
h2.set_image(os.path.join(emoji_dataset, 'woman.png'), image_stretch=(0, 20))
ax.legend( handler_map={rects1: h1, rects2: h2},
handlelength=2, labelspacing=0.0,
fontsize=36, borderpad=0.15, loc='best',
handletextpad=0.2, borderaxespad=0.15)
但是,我收到以下错误:
Traceback (most recent call last):
File "img_in_legend.py", line 117, in <module>
handletextpad=0.2, borderaxespad=0.15)
File "/home/xenial/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/pyplot.py", line 2886, in legend
return gca().legend(*args, **kwargs)
File "/home/xenial/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/axes/_axes.py", line 290, in legend
self.legend_ = mlegend.Legend(self, handles, labels, **kwargs)
File "/home/xenial/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/legend.py", line 503, in __init__
self._init_legend_box(handles, labels, markerfirst)
File "/home/xenial/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/legend.py", line 767, in _init_legend_box
fontsize, handlebox))
File "/home/xenial/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/legend_handler.py", line 117, in legend_artist
fontsize, handlebox.get_transform())
File "img_in_legend.py", line 48, in create_artists
self.update_prop(image, orig_handle, legend)
File "/home/xenial/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/legend_handler.py", line 74, in update_prop
self._update_prop(legend_handle, orig_handle)
File "/home/xenial/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/legend_handler.py", line 65, in _update_prop
self._default_update_prop(legend_handle, orig_handle)
File "/home/xenial/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/legend_handler.py", line 70, in _default_update_prop
legend_handle.update_from(orig_handle)
File "/home/xenial/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/artist.py", line 1133, in update_from
self._transform = other._transform
AttributeError: 'BarContainer' object has no attribute '_transform'
在我的示例中,class ImageHandler
似乎不适用于 ax.bar()
。
如何将图像添加到 matplotlib
中 barplot
的图例中?
干杯,
看来您是对的,ImageHandler
不适用于 ax.bar()
。
一个非常 hacky 的解决方法是创建两个占位符 line2d
对象,以便使用 HandlerLineImage
代码形式 Trenton McKinney 的 。您可以定义它们:
line1, = ax.plot([],[],label='men',color='tab:blue',lw=15)
line2, = ax.plot([],[],label='women',color='tab:orange',lw=15)
[],[]
确保您的条形图上不会绘制任何内容。
总体而言,代码如下所示:
import matplotlib.pyplot as plt
import matplotlib.lines
from matplotlib.transforms import Bbox, TransformedBbox
from matplotlib.legend_handler import HandlerBase
from matplotlib.image import BboxImage
import numpy as np
import os, sys
class HandlerLineImage(HandlerBase):
def __init__(self, path, space=15, offset = 10 ):
self.space=space
self.offset=offset
self.image_data = plt.imread(path)
super(HandlerLineImage, self).__init__()
def create_artists(self, legend, orig_handle,
xdescent, ydescent, width, height, fontsize, trans):
l = matplotlib.lines.Line2D([xdescent+self.offset,xdescent+(width-self.space)/3.+self.offset],
[ydescent+height/2., ydescent+height/2.])
l.update_from(orig_handle)
l.set_clip_on(False)
l.set_transform(trans)
bb = Bbox.from_bounds(xdescent +(width+self.space)/3.+self.offset,
ydescent,
height*self.image_data.shape[1]/self.image_data.shape[0],
height)
tbb = TransformedBbox(bb, trans)
image = BboxImage(tbb)
image.set_data(self.image_data)
self.update_prop(image, orig_handle, legend)
return [l,image]
plt.figure(figsize=(4.8,3.2))
#line, = plt.plot([1,2],[1.5,3], color="#1f66e0", lw=1.3)
#line2, = plt.plot([1,2],[1,2], color="#efe400", lw=1.3)
labels = ['G1', 'G2', 'G3', 'G4', 'G5']
men_means = [20, 34, 30, 35, 27]
women_means = [25, 32, 34, 20, 25]
x = np.arange(len(labels)) # the label locations
width = 0.35
fig, ax = plt.subplots()
rects1 = ax.bar(x - width/2, men_means, width,color='tab:blue')
rects2 = ax.bar(x + width/2, women_means, width,color='tab:orange')
line1, = ax.plot([],[],label='men',color='tab:blue',lw=15)
line2, = ax.plot([],[],label='women',color='tab:orange',lw=15)
# Add some text for labels, title and custom x-axis tick labels, etc.
ax.set_ylabel('Scores')
ax.set_title('Scores by group and gender')
ax.set_xticks(x)
ax.legend() # oringinal legend
ax.bar_label(rects1, padding=3)
ax.bar_label(rects2, padding=3)
fig.tight_layout()
leg=plt.legend([line1, line2], ["", ""],
handler_map={line1: HandlerLineImage("man.png"), line2: HandlerLineImage("woman.png")},
handlelength=2, labelspacing=0.0, fontsize=36, borderpad=0.15, loc=2,
handletextpad=0.2, borderaxespad=0.15)
plt.show()
此代码的输出为:
给定以下代码:
import numpy as np
import matplotlib.pyplot as plt
import os, sys
labels = ['G1', 'G2', 'G3', 'G4', 'G5']
men_means = [20, 34, 30, 35, 27]
women_means = [25, 32, 34, 20, 25]
x = np.arange(len(labels)) # the label locations
width = 0.35
fig, ax = plt.subplots()
rects1 = ax.bar(x - width/2, men_means, width, label='Men')
rects2 = ax.bar(x + width/2, women_means, width, label='Women')
# Add some text for labels, title and custom x-axis tick labels, etc.
ax.set_ylabel('Scores')
ax.set_title('Scores by group and gender')
ax.set_xticks(x)
ax.legend() # oringinal legend
ax.bar_label(rects1, padding=3)
ax.bar_label(rects2, padding=3)
fig.tight_layout()
plt.savefig('bar.png')
哪个 returns 这个条形图:
我想用 ['Men', 'Women']
。
为此,我将以下 class ImageHandler
添加到我的脚本中,摘自 here:
from matplotlib.transforms import Bbox, TransformedBbox
from matplotlib.legend_handler import HandlerBase
from matplotlib.image import BboxImage
class ImageHandler(HandlerBase):
def create_artists(self, legend, orig_handle, Xd_, Yd_, W_, H_, fontsize, trans):
# enlarge the image by these margins
sx, sy = self.image_stretch
# create a bounding box to house the image
bb = Bbox.from_bounds(Xd_ - sx, Yd_ - sy, W_ + sx, H_ + sy )
tbb = TransformedBbox(bb, trans)
image = BboxImage(tbb)
image.set_data(self.image_data)
self.update_prop(image, orig_handle, legend)
return [image]
def set_image(self, image_path, image_stretch=(0, 0)):
self.image_data = plt.imread(image_path)
self.image_stretch = image_stretch
因此,我将 ax.legend()
替换为:
emoji_dataset = os.path.join( os.environ['HOME'], 'Datasets', 'Emojis')
h1 = ImageHandler()
h2 = ImageHandler()
h1.set_image(os.path.join(emoji_dataset, 'man.png'), image_stretch=(0, 20))
h2.set_image(os.path.join(emoji_dataset, 'woman.png'), image_stretch=(0, 20))
ax.legend( handler_map={rects1: h1, rects2: h2},
handlelength=2, labelspacing=0.0,
fontsize=36, borderpad=0.15, loc='best',
handletextpad=0.2, borderaxespad=0.15)
但是,我收到以下错误:
Traceback (most recent call last):
File "img_in_legend.py", line 117, in <module>
handletextpad=0.2, borderaxespad=0.15)
File "/home/xenial/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/pyplot.py", line 2886, in legend
return gca().legend(*args, **kwargs)
File "/home/xenial/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/axes/_axes.py", line 290, in legend
self.legend_ = mlegend.Legend(self, handles, labels, **kwargs)
File "/home/xenial/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/legend.py", line 503, in __init__
self._init_legend_box(handles, labels, markerfirst)
File "/home/xenial/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/legend.py", line 767, in _init_legend_box
fontsize, handlebox))
File "/home/xenial/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/legend_handler.py", line 117, in legend_artist
fontsize, handlebox.get_transform())
File "img_in_legend.py", line 48, in create_artists
self.update_prop(image, orig_handle, legend)
File "/home/xenial/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/legend_handler.py", line 74, in update_prop
self._update_prop(legend_handle, orig_handle)
File "/home/xenial/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/legend_handler.py", line 65, in _update_prop
self._default_update_prop(legend_handle, orig_handle)
File "/home/xenial/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/legend_handler.py", line 70, in _default_update_prop
legend_handle.update_from(orig_handle)
File "/home/xenial/anaconda3/envs/py37/lib/python3.7/site-packages/matplotlib/artist.py", line 1133, in update_from
self._transform = other._transform
AttributeError: 'BarContainer' object has no attribute '_transform'
在我的示例中,class ImageHandler
似乎不适用于 ax.bar()
。
如何将图像添加到 matplotlib
中 barplot
的图例中?
干杯,
看来您是对的,ImageHandler
不适用于 ax.bar()
。
一个非常 hacky 的解决方法是创建两个占位符 line2d
对象,以便使用 HandlerLineImage
代码形式 Trenton McKinney 的
line1, = ax.plot([],[],label='men',color='tab:blue',lw=15)
line2, = ax.plot([],[],label='women',color='tab:orange',lw=15)
[],[]
确保您的条形图上不会绘制任何内容。
总体而言,代码如下所示:
import matplotlib.pyplot as plt
import matplotlib.lines
from matplotlib.transforms import Bbox, TransformedBbox
from matplotlib.legend_handler import HandlerBase
from matplotlib.image import BboxImage
import numpy as np
import os, sys
class HandlerLineImage(HandlerBase):
def __init__(self, path, space=15, offset = 10 ):
self.space=space
self.offset=offset
self.image_data = plt.imread(path)
super(HandlerLineImage, self).__init__()
def create_artists(self, legend, orig_handle,
xdescent, ydescent, width, height, fontsize, trans):
l = matplotlib.lines.Line2D([xdescent+self.offset,xdescent+(width-self.space)/3.+self.offset],
[ydescent+height/2., ydescent+height/2.])
l.update_from(orig_handle)
l.set_clip_on(False)
l.set_transform(trans)
bb = Bbox.from_bounds(xdescent +(width+self.space)/3.+self.offset,
ydescent,
height*self.image_data.shape[1]/self.image_data.shape[0],
height)
tbb = TransformedBbox(bb, trans)
image = BboxImage(tbb)
image.set_data(self.image_data)
self.update_prop(image, orig_handle, legend)
return [l,image]
plt.figure(figsize=(4.8,3.2))
#line, = plt.plot([1,2],[1.5,3], color="#1f66e0", lw=1.3)
#line2, = plt.plot([1,2],[1,2], color="#efe400", lw=1.3)
labels = ['G1', 'G2', 'G3', 'G4', 'G5']
men_means = [20, 34, 30, 35, 27]
women_means = [25, 32, 34, 20, 25]
x = np.arange(len(labels)) # the label locations
width = 0.35
fig, ax = plt.subplots()
rects1 = ax.bar(x - width/2, men_means, width,color='tab:blue')
rects2 = ax.bar(x + width/2, women_means, width,color='tab:orange')
line1, = ax.plot([],[],label='men',color='tab:blue',lw=15)
line2, = ax.plot([],[],label='women',color='tab:orange',lw=15)
# Add some text for labels, title and custom x-axis tick labels, etc.
ax.set_ylabel('Scores')
ax.set_title('Scores by group and gender')
ax.set_xticks(x)
ax.legend() # oringinal legend
ax.bar_label(rects1, padding=3)
ax.bar_label(rects2, padding=3)
fig.tight_layout()
leg=plt.legend([line1, line2], ["", ""],
handler_map={line1: HandlerLineImage("man.png"), line2: HandlerLineImage("woman.png")},
handlelength=2, labelspacing=0.0, fontsize=36, borderpad=0.15, loc=2,
handletextpad=0.2, borderaxespad=0.15)
plt.show()
此代码的输出为: