Matplotlib 中文用戶指南 3.6 圖例指南

圖例指南

原文:Legend guide

譯者:飛龍

協議:CC BY-NC-SA 4.0

此圖例指南是legend()中可用文檔的擴展 - 請在繼續閱讀本指南之前確保你熟悉該文檔(見篇尾)的內容。

本指南使用一些常見術語,為了清楚起見,這些術語在此處進行說明:

圖例條目

圖例由一個或多個圖例條目組成。 一個條目由一個鍵和一個標簽組成。

圖例鍵

每個圖例標簽左側的彩色/圖案標記。

圖例標簽

描述由鍵表示的句柄的文本。

圖例句柄

用于在圖例中生成適當條目的原始對象。

控制圖例條目

不帶參數調用legend()會自動獲取圖例句柄及其相關標簽。 此函數等同于:

handles, labels = ax.get_legend_handles_labels()
ax.legend(handles, labels)

get_legend_handles_labels()函數返回軸域上存在的句柄/藝術家的列表,這些句柄/藝術家可以用于為結果圖例生成條目 - 但值得注意的是,并非所有藝術家都可以添加到圖例中, 這種情況下會創建『代理』(請參閱特地為添加到圖例創建藝術家(也稱為代理藝術家),來了解更多詳細信息)。

為了完全控制要添加到圖例的內容,通常將適當的句柄直接傳遞給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'])

特地為添加到圖例創建藝術家(也稱為代理藝術家)

并非所有的句柄都可以自動轉換為圖例條目,因此通常需要創建一個可轉換的藝術家。 圖例句柄不必存在于被用到的圖像或軸域上。

假設我們想創建一個圖例,其中有一些數據表示為紅色:

import matplotlib.patches as mpatches
import matplotlib.pyplot as plt

red_patch = mpatches.Patch(color='red', label='The red data')
plt.legend(handles=[red_patch])

plt.show()

除了創建一個色塊之外,有許多受支持的圖例句柄,我們可以創建一個帶有標記的線條:

import matplotlib.lines as mlines
import matplotlib.pyplot as plt

blue_line = mlines.Line2D([], [], color='blue', marker='*',
                          markersize=15, label='Blue stars')
plt.legend(handles=[blue_line])

plt.show()

圖例位置

圖例的位置可以通過關鍵字參數loc指定。 詳細信息請參閱legend()的文檔。

bbox_to_anchor關鍵字可讓用戶手動控制圖例布局。 例如,如果你希望軸域圖例位于圖像的右上角而不是軸域的邊角,則只需指定角的位置以及該位置的坐標系:

plt.legend(bbox_to_anchor=(1, 1),
           bbox_transform=plt.gcf().transFigure)

自定義圖例位置的更多示例:

import matplotlib.pyplot as plt


plt.subplot(211)
plt.plot([1,2,3], label="test1")
plt.plot([3,2,1], label="test2")
# 將圖例放到這個子圖上方,
# 擴展自身來完全利用提供的邊界框。
plt.legend(bbox_to_anchor=(0., 1.02, 1., .102), loc=3,
           ncol=2, mode="expand", borderaxespad=0.)

plt.subplot(223)
plt.plot([1,2,3], label="test1")
plt.plot([3,2,1], label="test2")
# 將圖例放到這個小型子圖的右側
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)

plt.show()

相同軸域內的多個圖例

有時,在多個圖例之間分割圖例條目會更加清晰。 雖然直覺上的做法可能是多次調用legend()函數,但你會發現軸域上只存在一個圖例。 這樣做是為了可以重復調用legend(),將圖例更新為軸域上的最新句柄,因此要保留舊的圖例實例,我們必須將它們手動添加到軸域中:

import matplotlib.pyplot as plt

line1, = plt.plot([1,2,3], label="Line 1", linestyle='--')
line2, = plt.plot([3,2,1], label="Line 2", linewidth=4)

# 為第一個線條創建圖例
first_legend = plt.legend(handles=[line1], loc=1)

# 手動將圖例添加到當前軸域
ax = plt.gca().add_artist(first_legend)

# 為第二個線條創建另一個圖例
plt.legend(handles=[line2], loc=4)

plt.show()

圖例處理器

為了創建圖例條目,將句柄作為參數提供給適當的HandlerBase子類。 處理器子類的選擇由以下規則確定:

  • 使用handler_map關鍵字中的值更新get_legend_handler_map()
  • 檢查句柄是否在新創建的handler_map中。
  • 檢查句柄的類型是否在新創建的handler_map中。
  • 檢查句柄的mro中的任何類型是否在新創建的handler_map中。

處于完整性,這個邏輯大多在get_legend_handler()中實現。

所有這些靈活性意味著我們可以使用一些必要的鉤子,為我們自己的圖例鍵類型實現自定義處理器。

使用自定義處理器的最簡單的例子是,實例化一個現有的HandlerBase子類。 為了簡單起見,讓我們選擇matplotlib.legend_handler.HandlerLine2D,它接受numpoints參數(出于便利,注意numpointslegend()函數上的一個關鍵字)。 然后我們可以將實例的字典作為關鍵字handler_map傳給legend

import matplotlib.pyplot as plt
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)})

如你所見,Line 1現在有 4 個標記點,Line 2有兩個(默認值)。 嘗試上面的代碼,只需將字典的鍵從line1更改為type(line)。 注意現在兩個Line2D`實例都擁有了 4 個標記。

除了用于復雜的繪圖類型的處理器,如誤差條,莖葉圖和直方圖,默認的handler_map有一個特殊的元組處理器(HandlerTuple),它簡單地在頂部一一繪制給定元組中每個項目的句柄。 以下示例演示如何將兩個圖例的鍵相互疊加:

import matplotlib.pyplot as plt
from numpy.random import randn

z = randn(10)

red_dot, = plt.plot(z, "ro", markersize=15)
# 將白色十字放置在一些數據上
white_cross, = plt.plot(z[:5], "w+", markeredgewidth=3, markersize=15)

plt.legend([red_dot, (red_dot, white_cross)], ["Attr A", "Attr A+B"])

實現自定義圖例處理器

可以實現自定義處理器,將任何句柄轉換為圖例的鍵(句柄不必要是matplotlibartist)。 處理器必須實現legend_artist方法,該方法為要使用的圖例返回單個藝術家。 有關legend_artist的詳細信息,請參閱legend_artist()

import matplotlib.pyplot as plt
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()})

或者,如果我們想要接受全局的AnyObject實例,而不想一直手動設置handler_map關鍵字,我們可以注冊新的處理器:

from matplotlib.legend import Legend
Legend.update_default_handler_map({AnyObject: AnyObjectHandler()})

雖然這里的功能十分清楚,請記住,有很多已實現的處理器,你想實現的目標可能易于使用現有的類實現。 例如,要生成橢圓的圖例鍵,而不是矩形鍵:

from matplotlib.legend_handler import HandlerPatch
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches


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

使用圖例的現有示例

這里是一個不太詳盡的示例列表,涉及以各種方式使用的圖例:

matplotlib.pyplot.legend(*args, **kwargs) 文檔

在軸域上放置一個圖例。

為了為軸域上已經存在的線條(例如通過繪圖)制作圖例,只需使用字符串的可迭代對象(每個圖例條目對應一個字符串)調用此函數。 例如:

ax.plot([1, 2, 3])
ax.legend(['A simple line'])

但是,為了使『標簽』和圖例元素實例保持一致,最好在藝術家創建時指定標簽,或者通過調用藝術家的set_label()方法:

line, = ax.plot([1, 2, 3], label='Inline label')
# 通過調用該方法覆寫標簽
line.set_label('Label via method')
ax.legend()

通過定義以下劃線開頭的標簽,可以從圖例元素自動選擇中排除特定線條。 這對于所有藝術家都是默認的,因此不帶任何參數調用legend(),并且沒有手動設置標簽會導致沒有繪制圖例。

為了完全控制哪些藝術家擁有圖例條目,可以傳遞擁有圖例的藝術家的可迭代對象,然后是相應圖例標簽的可迭代對象:

legend((line1, line2, line3), ('label1', 'label2', 'label3'))

參數

loc:整數、字符串或者浮點偶對,默認為'upper right'

圖例的位置。 可能的代碼是:

位置字符串 位置代碼
'best' 0
'upper right' 1
'upper left' 2
'lower left' 3
'lower right' 4
'right' 5
'center left' 6
'center right' 7
'lower center' 8
'upper center' 9
'center' 10

或者,可以是一個二元組,提供圖例的距離左下角的x, y坐標(在這種情況下,bbox_to_anchor將被忽略)。

bbox_to_anchormatplotlib.transforms.BboxBase示例或者浮點元組。

bbox_transform坐標(默認軸域坐標)中為圖例指定任意位置。

例如,要將圖例的右上角放在軸域中心,可以使用以下關鍵字:

loc='upper right', bbox_to_anchor=(0.5, 0.5)

ncol:整數。

圖例的列數,默認為 1。

propNonematplotlib.font_manager.FontProperties或者字典。

圖例的字體屬性,如果為None(默認),會使用當前的matplotlib.rcParams

fontsize:整數、浮點或者{‘xx-small’, ‘x-small’, ‘small’, ‘medium’, ‘large’, ‘x-large’, ‘xx-large’}

控制圖例的字體大小。 如果值為數字,則大小將為絕對字體大小(以磅為單位)。 字符串值相對于當前默認字體大小。 此參數僅在未指定prop的情況下使用。

numpointsNone或者整數。

為線條/matplotlib.lines.Line2D創建圖例條目時,圖例中的標記點數。 默認值為None,它將從legend.numpoints rcParam中獲取值。

scatterpointsNone或者整數。

為散點圖/matplotlib.collections.PathCollection創建圖例條目時,圖例中的標記點數。 默認值為None,它將從legend.scatterpoints rcParam中獲取值。

scatteryoffsets:浮點的可迭代對象。

為散點圖圖例條目創建的標記的垂直偏移量(相對于字體大小)。 0.0 是在圖例文本的底部,1.0 是在頂部。 為了將所有標記繪制在相同的高度,請設置為[0.5]。 默認值為[0.375,0.5,0.3125]

markerscaleNone、整數或者浮點。

圖例標記對于原始繪制的標記的相對大小。 默認值為None,它將從legend.markerscale rcParam中獲取值。

markerfirst: [ True | False ]

如果為True,則圖例標記位于圖例標簽的左側,如果為False,圖例標記位于圖例標簽的右側。

frameonNone或布爾值

控制是否應在圖例周圍繪制框架。 默認值為None,它將從legend.frameon rcParam中獲取值。

fancyboxNone或布爾值

控制是否應在構成圖例背景的FancyBboxPatch周圍啟用圓邊。 默認值為None,它將從legend.fancybox rcParam中獲取值。

shadowNone或布爾值

控制是否在圖例后面畫一個陰影。 默認值為None,它將從legend.shadow rcParam中獲取值。

framealphaNone或浮點

控制圖例框架的 Alpha 透明度。 默認值為None,它將從legend.framealpha rcParam中獲取值。

mode{"expand", None}

如果mode設置為"expand",圖例將水平擴展來填充軸域區域(如果定義圖例的大小,則為bbox_to_anchor)。

bbox_transformNone或者matplotlib.transforms.Transform

邊界框的變換(bbox_to_anchor)。 對于None值(默認),將使用AxestransAxes變換。

title:字符串或者None

圖例的標題,默認沒有標題(None)。

borderpad:浮點或None

圖例邊框的內邊距。 以字體大小為單位度量。 默認值為None,它將從legend.borderpad rcParam中獲取值。

labelspacing:浮點或None

圖例條目之間的垂直間距。 以字體大小為單位度量。 默認值為None,它將從legend.labelspacing rcParam中獲取值。

handlelength:浮點或None

圖例句柄的長度。 以字體大小為單位度量。 默認值為None,它將從legend.handlelength rcParam取值。

handletextpad:浮點或None

圖例句柄和文本之間的間距。 以字體大小為單位度量。 默認值為None,它將從legend.handletextpad rcParam中獲取值。

borderaxespad:浮點或None

軸和圖例邊框之間的間距。 以字體大小為單位度量。 默認值為None,它將從legend.borderaxespad rcParam中獲取值。

columnspacing:浮點或None

列間距。以字體大小為單位度量。 默認值為None,它將從legend.columnspacing rcParam中獲取值。

handler_map:字典或None

自定義字典,用于將實例或類型映射到圖例處理器。 這個handler_map會更新在matplotlib.legend.Legend.get_legend_handler_map()中獲得的默認處理器字典。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容