3

この例のような matplotlib errorbar プロットの凡例ピッカーをコーディングしようとしています。凡例のエラーバー/データポイントをクリックして、軸の表示を切り替えられるようにしたいと考えています。問題は、 によって返される凡例オブジェクトに、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ものは、エラーバーの線を指すものでしたが、データ ポイントやエンド キャップは指していませんでした。凡例を選択できる場合は、辞書を使用してそれらを元のプロットにマップし、次の関数を使用してエラーバーのオン/オフを切り替えることができます。

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

私の質問は、エラーバー/その他の複雑な凡例でイベントピッキングを実行できる回避策はありますか??

4

1 に答える 1

4

これにより、マーカーが選択可能になります。

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)

ここで行われているのは、 の標準ハンドラーがErrorbarContainers4 人のアーティストを使用して凡例エントリ (LineCollection棒、LineCollectionキャップ、Line2D接続線、およびLine2Dマーカー) を生成することです。アーティストを生成するコードは、凡例に追加されたアーティストのリストの最初のアーティストのみを返します (「 」を参照matplotlib.legend_handler.HandlerBase.__call__)。エラーバーのリストの最初のアーティストは、たまたま垂直線であるライン コレクションであり、最終的にleg.legendHandles. ピッキングがうまくいかなかったのは、他のアーティストに隠されていたためのようです (私はそう思います)。

解決策は、保存されるアーティストがマーカーのオブジェクトにHandlerErrorbarなるように、アーティスト リストを並べ替えるローカル サブクラスを作成することです。leg.legendHandlesLine2D

これをデフォルトの動作にするために、おそらく PR を開きます。

于 2013-10-19T21:56:18.017 に答える