我正在尝试为类似于此示例的matplotlib 误差条图编写图例选择器。我希望能够单击图例中的错误栏/数据点来切换它们在轴中的可见性。问题在于,返回的图例对象plt.legend()不包含创建图例时使用的艺术家的任何数据。如果我例如。做:
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
x = np.linspace(0,10,100)
y = np.sin(x) + np.random.rand(100)
yerr = np.random.rand(100)
erbpl1 = ax.errorbar(x, y, yerr=yerr, fmt='o', label='A')
erbpl2 = ax.errorbar(x, 0.02*y, yerr=yerr, fmt='o', label='B')
leg = ax.legend()
Run Code Online (Sandbox Code Playgroud)
从这里似乎不可能通过使用该leg对象来访问传奇的艺术家。通常,可以使用更简单的图例来做到这一点,例如:
plt.plot(x, y, label='whatever')
leg = plt.legend()
proxy_lines = leg.get_lines()
Run Code Online (Sandbox Code Playgroud)
为您提供图例中使用的 Line2D 对象。然而,对于误差条图,leg.get_lines()返回一个空列表。这是有意义的,因为plt.errorbar返回一个matplotlib.container.ErrorbarContainer对象(其中包含数据点、误差条端盖、误差条线)。我希望图例有一个类似的数据容器,但我看不到这一点。我能管理的最接近的是leg.legendHandles指向误差条线,但不是数据点或端盖。如果您可以选择图例,则可以使用字典将它们映射到原始图,并使用以下函数打开/关闭错误栏。
def toggle_errorbars(erb_pl):
points, caps, bars = erb_pl
vis = bars[0].get_visible()
for line in caps:
line.set_visible(not vis)
for bar in bars:
bar.set_visible(not vis)
return vis
def onpick(event):
# on the pick event, find the orig line corresponding to the
# legend proxy line, and toggle the visibility
legline = event.artist
origline = lined[legline]
vis = toggle_errorbars(origline)
## Change the alpha on the line in the legend so we can see what lines
## have been toggled
if vis:
legline.set_alpha(.2)
else:
legline.set_alpha(1.)
fig.canvas.draw()
Run Code Online (Sandbox Code Playgroud)
我的问题是,是否有一种解决方法可以让我在错误栏/其他复杂图例上进行事件选择?
这使得标记可以选择:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.legend_handler
from matplotlib.container import ErrorbarContainer
class re_order_errorbarHandler(matplotlib.legend_handler.HandlerErrorbar):
"""
Sub-class the standard error-bar handler
"""
def create_artists(self, *args, **kwargs):
# call the parent class function
a_list = matplotlib.legend_handler.HandlerErrorbar.create_artists(self, *args, **kwargs)
# re-order the artist list, only the first artist is added to the
# legend artist list, this is the one that corresponds to the markers
a_list = a_list[-1:] + a_list[:-1]
return a_list
my_handler_map = {ErrorbarContainer: re_order_errorbarHandler(numpoints=2)}
fig, ax = plt.subplots()
x = np.linspace(0,10,100)
y = np.sin(x) + np.random.rand(100)
yerr = np.random.rand(100)
erbpl1 = ax.errorbar(x, y, yerr=yerr, fmt='o', label='A')
erbpl2 = ax.errorbar(x, 0.02*y, yerr=yerr, fmt='o', label='B')
leg = ax.legend(handler_map=my_handler_map)
lines = [erbpl1, erbpl2]
lined = dict()
# not strictly sure about ordering, but
for legline, origline in zip(leg.legendHandles, lines):
legline.set_picker(5) # 5 pts tolerance
lined[legline] = origline
def onpick(event):
# on the pick event, find the orig line corresponding to the
# legend proxy line, and toggle the visibility
legline = event.artist
origline = lined[legline]
for a in origline.get_children():
vis = not a.get_visible()
a.set_visible(vis)
# Change the alpha on the line in the legend so we can see what lines
# have been toggled
if vis:
legline.set_alpha(1.0)
else:
legline.set_alpha(0.2)
fig.canvas.draw()
fig.canvas.mpl_connect('pick_event', onpick)
Run Code Online (Sandbox Code Playgroud)
这里发生的事情是,标准处理程序ErrorbarContainers使用 4 位艺术家来生成图例条目(LineCollection用于条形图、LineCollection大写字母、Line2D连接线和Line2D标记)。生成艺术家的代码仅返回添加到图例的艺术家列表中的第一个艺术家(请参阅matplotlib.legend_handler.HandlerBase.__call__)。误差线列表中的第一个艺术家恰好是垂直线的线条集合,这就是最终的leg.legendHandles. 挑选不起作用的原因似乎是它们被其他艺术家隐藏了(我认为)。
解决方案是创建一个本地子类HandlerErrorbar,对艺术家列表进行重新排序,以便保存的艺术家leg.legendHandles是Line2D标记的对象。
我可能会打开一个 PR 使其成为默认行为。