python matplotlib错误栏传奇选择

时间:2013-10-19 19:23:28

标签: python matplotlib

我正在尝试为类似于this example的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()

从这里开始,似乎无法使用leg对象访问图例的艺术家。通常,人们可以用更简单的图例来做到这一点,例如:

plt.plot(x, y, label='whatever')
leg = plt.legend()
proxy_lines = leg.get_lines()

为您提供图例中使用的Line2D对象。但是,使用错误栏图表,leg.get_lines()会返回一个空列表。这种情况很有意义,因为plt.errorbar返回一个matplotlib.container.ErrorbarContainer对象(包含数据点,错误栏结束,错误栏行)。我希望传奇有一个类似的数据容器,但我看不到这一点。我能管理的最接近的是leg.legendHandles,它指向错误栏行,但不是数据点,也不是结束。如果您可以选择图例,可以使用dict将它们映射到原始图,并使用以下函数打开/关闭错误栏。

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

我的问题是,是否有一种解决方法可以让我在错误栏/其他复杂的传奇上进行事件挑选?

1 个答案:

答案 0 :(得分:3)

这使得标记可以选择:

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)

这里发生的是ErrorbarContainers的标准处理程序使用4位艺术家来生成图例条目(LineCollection表示条形图,LineCollection表示大写字母,Line2D表示连接线,标记为Line2D。生成艺术家的代码仅返回添加到图例中的艺术家列表中的第一批艺术家(请参阅matplotlib.legend_handler.HandlerBase.__call__)。错误栏列表中的第一个艺术家恰好是垂直线的行集合,最后是leg.legendHandles。采摘不起作用的原因似乎是他们被其他艺术家隐藏(我认为)。

解决方案是创建一个HandlerErrorbar的本地子类,重新排序艺术家列表,以便在leg.legendHandles中保存的艺术家是标记的Line2D对象

我可能会打开PR以使其成​​为默认行为。