2013-10-19 54 views
1

我正在嘗試編寫類似於this example的matplotlib錯誤欄圖的傳奇選擇器。我希望能夠點擊圖例中的錯誤條/數據點來切換軸的可見性。問題是由plt.legend()返回的圖例對象不包含用於創建圖例的藝術家的任何數據。如果我爲例如。這樣做:python matplotlib錯誤條圖例拾取

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,它指向錯誤欄行,但不是數據點,也不是結尾帽。如果您可以選擇圖例,則可以使用字典將它們映射到原始圖,並使用以下功能打開/關閉錯誤欄。

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

我的問題是,有一種解決方法,可以讓我做採摘活動上errorbar /其他複雜的傳奇?

+0

您使用的是什麼版本的matplotlib?這在我的機器上按預期工作(非常接近當前主機)。 – tacaswell

+0

我使用python 3.2與matplotlib 1.3.0 – astroMonkey

+0

nm,我讀得太快了。要清楚,你在傳奇中獲得了藝術家,但現在正試圖挑選他們。你應該包括證明你的實際問題的代碼,而不僅僅是鍋爐板。 – tacaswell

回答

1

這使得標記可揀選:

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,使其成爲默認行爲。

+0

真棒!感謝這個答案!我想要的實際行爲是打開/關閉錯誤欄。我已經在上面包含了一小段代碼。唯一的麻煩是,你仍然只能選擇Line2D作爲圖例中的標記。沒有其他東西似乎趕上。理想情況下,我希望獨立選擇圖例中的標記和錯誤條,但我會解決這個問題! 再次感謝! – astroMonkey

+0

@apodemus編輯被拒絕。你應該把這些代碼放在你的問題中(toggle_errorbar和你想使用的'on_pick'函數)。 – tacaswell

+0

加入問題... – astroMonkey