|
|
matplotlib Legend guide. |
H.Kamifuji . |
Matplotlib で凡例を柔軟に生成する。 この凡例は、legend() に記載されているドキュメントを拡張したものです。このガイドを進める前に、そのドキュメントの内容をよく理解してください。 このガイドでは、一般的な用語を使用しています。
|
引数なしで legend() を呼び出すと、凡例ハンドルとそれに関連するラベルが自動的に取得されます。 この機能は次のものと同等です。handles, labels = ax.get_legend_handles_labels() ax.legend(handles, labels)get_legend_handles_labels() 関数は、Axes 上に存在するハンドル/アーティストのリストを返します。これは、結果の凡例のエントリを生成するために使用できます。しかし、すべてのアーティストを凡例に追加することはできません。 "proxy" を作成する必要があります(詳しくは、凡例に追加するアーティストを作成する(別名プロキシアーティスト) を参照してください)。 凡例に何が追加されているかを完全に制御するには、適切なハンドルを legend() に直接渡すのが一般的です。 line_up, = plt.plot([1,2,3], label='Line 2') line_down, = plt.plot([3,2,1], label='Line 1') plt.legend(handles=[line_up, line_down])場合によっては、ハンドルのラベルを設定することができないので、ラベルのリストを legend() に渡すことができます: line_up, = plt.plot([1,2,3], label='Line 2') line_down, = plt.plot([3,2,1], label='Line 1') plt.legend([line_up, line_down], ['Line Up', 'Line Down']) |
凡例の位置はキーワード引数 loc で指定できます。 詳細は legend() のドキュメントを参照してください。 bbox_to_anchor キーワードは、マニュアル凡例の配置を大幅に制御します。 たとえば、軸の凡例を軸の角の代わりに図の右上隅に配置する場合は、コーナーの位置とその位置の座標系を指定するだけです。 plt.legend(bbox_to_anchor=(1, 1), bbox_transform=plt.gcf().transFigure)カスタム凡例の配置例の例: import matplotlib.patches as mpatches import matplotlib.pyplot as plt import matplotlib.lines as mlines plt.subplot(211) plt.plot([1, 2, 3], label="test1") plt.plot([3, 2, 1], label="test2") # Place a legend above this subplot, expanding itself to # fully use the given bounding box. plt.legend(bbox_to_anchor=(0., 1.02, 1., .102), loc='lower left', ncol=2, mode="expand", borderaxespad=0.) plt.subplot(223) plt.plot([1, 2, 3], label="test1") plt.plot([3, 2, 1], label="test2") # Place a legend to the right of this smaller subplot. plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0.) plt.show() ![]() ![]() |
凡例を複数の凡例にまたがって分割するほうが明確な場合もあります。 これを行う本能的なアプローチは、legend() 関数を複数回呼び出すことですが、Axes には唯一の凡例しか存在しません。 これは、凡例を Axes 上の最新のハンドルに更新するために legend() を繰り返し呼び出すことができるように行われているため、古い凡例を永続化するために、Axes に手動で追加する必要があります。import matplotlib.patches as mpatches import matplotlib.pyplot as plt import matplotlib.lines as mlines line1, = plt.plot([1, 2, 3], label="Line 1", linestyle='--') line2, = plt.plot([3, 2, 1], label="Line 2", linewidth=8) # Create a legend for the first line. first_legend = plt.legend(handles=[line1], loc='upper right') # Add the legend manually to the current Axes. ax = plt.gca().add_artist(first_legend) # Create another legend for the second line. plt.legend(handles=[line2], loc='lower right') plt.show() ![]() ![]() |
凡例エントリを作成するために、ハンドルは適切な HandlerBase サブクラスの引数として与えられます。 ハンドラのサブクラスの選択は、次の規則によって決定されます。
この柔軟性はすべて、独自のタイプの凡例キーのカスタムハンドラを実装するために必要なフックを備えていることを意味します。 カスタムハンドラを使用する最も簡単な例は、既存の HandlerBase サブクラスの 1 つをインスタンス化することです。 簡潔にするために、numpoints 引数を受け入れる matplotlib.legend_handler.HandlerLine2D を選択しましょう( numpoints は便宜上 legend() 関数のキーワードです)。 次に、Handler へのインスタンスのマッピングを凡例のキーワードとして渡すことができます。 import matplotlib.patches as mpatches import matplotlib.pyplot as plt import matplotlib.lines as mlines from matplotlib.legend_handler import HandlerLine2D line1, = plt.plot([3, 2, 1], marker='o', label='Line 1') line2, = plt.plot([1, 2, 3], marker='o', label='Line 2') plt.legend(handler_map={line1: HandlerLine2D(numpoints=4)}) plt.show() ![]() ![]() ご覧のとおり、 "line1" には 4 つのマーカーポイントがあり、 "line2" は2(デフォルト)です。 上記のコードを試してください。地図のキーを line1 から type(line1)に変更するだけです。 両方の Line2D インスタンスが4つのマーカーを取得する方法に注目してください。 エラーバー、ステムプロット、ヒストグラムなどの複雑なプロットタイプのハンドラに加えて、デフォルトの handler_map には特別なタプルハンドラ( HandlerTuple )があり、指定されたタプルの各アイテムのハンドルを単純に重ねて表示します。 次の例は、2 つの凡例キーを互いに組み合わせて使用する方法を示しています。 import matplotlib.patches as mpatches import matplotlib.pyplot as plt import matplotlib.lines as mlines from numpy.random import randn z = randn(10) red_dot, = plt.plot(z, "ro", markersize=15) # Put a white cross over some of the data. white_cross, = plt.plot(z[:5], "w+", markeredgewidth=3, markersize=15) plt.legend([red_dot, (red_dot, white_cross)], ["Attr A", "Attr A+B"]) plt.show() ![]() ![]() HandlerTuple クラスを使用して、複数の凡例キーを同じエントリに割り当てることもできます。 import matplotlib.patches as mpatches import matplotlib.pyplot as plt import matplotlib.lines as mlines from matplotlib.legend_handler import HandlerLine2D, HandlerTuple p1, = plt.plot([1, 2.5, 3], 'r-d') p2, = plt.plot([3, 2, 1], 'k-o') l = plt.legend([(p1, p2)], ['Two keys'], numpoints=1, handler_map={tuple: HandlerTuple(ndivide=None)}) plt.show() ![]() ![]() |
任意のハンドルを凡例キーに変換するカスタムハンドラを実装できます(ハンドルは必ずしも matplotlib アーティストである必要はありません)。 ハンドラは、legend_artist メソッドを実装して、凡例が使用する単一のアーティストを返す必要があります。 legend_artist() に>ついての署名の詳細は legend_artist() で文書化されています。import matplotlib.patches as mpatches import matplotlib.pyplot as plt import matplotlib.lines as mlines import matplotlib.patches as mpatches class AnyObject(object): pass class AnyObjectHandler(object): def legend_artist(self, legend, orig_handle, fontsize, handlebox): x0, y0 = handlebox.xdescent, handlebox.ydescent width, height = handlebox.width, handlebox.height patch = mpatches.Rectangle([x0, y0], width, height, facecolor='red', edgecolor='black', hatch='xx', lw=3, transform=handlebox.get_transform()) handlebox.add_artist(patch) return patch plt.legend([AnyObject()], ['My first handler'], handler_map={AnyObject: AnyObjectHandler()}) plt.show() ![]() ![]() あるいは、ハンドラ・マップ・キーワードを常に手動で設定せずに、AnyObject インスタンスをグローバルに受け入れたい場合は、次のように新しいハンドラを登録できました。 from matplotlib.legend import Legend Legend.update_default_handler_map({AnyObject: AnyObjectHandler()})ここでのパワーは明確ですが、すでに実装されているハンドラが多数あり、既存のクラスでは実現したいことがすでに簡単にできることに注意してください。 たとえば、長方形ではなく楕円形の凡例キーを生成するには、次のようにします。 import matplotlib.patches as mpatches import matplotlib.pyplot as plt import matplotlib.lines as mlines from matplotlib.legend_handler import HandlerPatch class HandlerEllipse(HandlerPatch): def create_artists(self, legend, orig_handle, xdescent, ydescent, width, height, fontsize, trans): center = 0.5 * width - 0.5 * xdescent, 0.5 * height - 0.5 * ydescent p = mpatches.Ellipse(xy=center, width=width + xdescent, height=height + ydescent) self.update_prop(p, orig_handle, legend) p.set_transform(trans) return [p] c = mpatches.Circle((0.5, 0.5), 0.25, facecolor="green", edgecolor="red", linewidth=3) plt.gca().add_patch(c) plt.legend([c], ["An ellipse, not a rectangle"], handler_map={mpatches.Circle: HandlerEllipse()}) plt.show() ![]() ![]() |
Legend guide |
|