複雜且語意的圖形組成 (subplot_mosaic)#

在圖形中以非均勻網格佈局軸可能會很繁瑣且冗長。對於密集的均勻網格,我們有 Figure.subplots,但對於更複雜的佈局,例如跨越佈局的多個欄/列的軸,或在圖形中留空某些區域,您可以使用 gridspec.GridSpec (請參閱 在圖形中排列多個軸) 或手動放置您的軸。Figure.subplot_mosaic 旨在提供一個介面,讓您以視覺方式佈局您的軸(使用 ASCII 圖或巢狀清單),以簡化此過程。

此介面自然支援命名您的軸。Figure.subplot_mosaic 會傳回一個字典,該字典的鍵是以用於佈局圖形的標籤。透過傳回帶有名稱的資料結構,可以更輕鬆地編寫獨立於圖形佈局的繪圖程式碼。

這受到了 建議的 MEP 和 R 語言的 patchwork 程式庫的啟發。雖然我們不實作運算子多載風格,但我們確實為指定(巢狀)軸佈局提供了一個 Python 式 API。

import matplotlib.pyplot as plt
import numpy as np


# Helper function used for visualization in the following examples
def identify_axes(ax_dict, fontsize=48):
    """
    Helper to identify the Axes in the examples below.

    Draws the label in a large font in the center of the Axes.

    Parameters
    ----------
    ax_dict : dict[str, Axes]
        Mapping between the title / label and the Axes.
    fontsize : int, optional
        How big the label should be.
    """
    kw = dict(ha="center", va="center", fontsize=fontsize, color="darkgrey")
    for k, ax in ax_dict.items():
        ax.text(0.5, 0.5, k, transform=ax.transAxes, **kw)

如果我們想要一個 2x2 的網格,可以使用 Figure.subplots,它會傳回一個 axes.Axes 的二維陣列,我們可以索引到其中進行繪圖。

np.random.seed(19680801)
hist_data = np.random.randn(1_500)


fig = plt.figure(layout="constrained")
ax_array = fig.subplots(2, 2, squeeze=False)

ax_array[0, 0].bar(["a", "b", "c"], [5, 7, 9])
ax_array[0, 1].plot([1, 2, 3])
ax_array[1, 0].hist(hist_data, bins="auto")
ax_array[1, 1].imshow([[1, 2], [2, 1]])

identify_axes(
    {(j, k): a for j, r in enumerate(ax_array) for k, a in enumerate(r)},
)
mosaic

使用 Figure.subplot_mosaic 我們可以產生相同的鑲嵌,但為軸指定語意的名稱

fig = plt.figure(layout="constrained")
ax_dict = fig.subplot_mosaic(
    [
        ["bar", "plot"],
        ["hist", "image"],
    ],
)
ax_dict["bar"].bar(["a", "b", "c"], [5, 7, 9])
ax_dict["plot"].plot([1, 2, 3])
ax_dict["hist"].hist(hist_data)
ax_dict["image"].imshow([[1, 2], [2, 1]])
identify_axes(ax_dict)
mosaic

Figure.subplotsFigure.subplot_mosaic 之間的一個主要區別是傳回值。前者傳回用於索引存取的陣列,而後者傳回一個字典,該字典將標籤對應到建立的 axes.Axes 執行個體

print(ax_dict)
{'bar': <Axes: label='bar'>, 'plot': <Axes: label='plot'>, 'hist': <Axes: label='hist'>, 'image': <Axes: label='image'>}

字串速記#

透過將我們的軸標籤限制為單一字元,我們可以以「ASCII 圖」的形式「繪製」我們想要的軸。以下程式碼

mosaic = """
    AB
    CD
    """

將為我們提供 4 個以 2x2 網格佈局的軸,並產生與上述相同的圖形鑲嵌(但現在標籤為 {"A", "B", "C", "D"} 而不是 {"bar", "plot", "hist", "image"})。

fig = plt.figure(layout="constrained")
ax_dict = fig.subplot_mosaic(mosaic)
identify_axes(ax_dict)
mosaic

或者,您可以使用更精簡的字串表示法

mosaic = "AB;CD"

會為您提供相同的組成,其中 ";" 用作列分隔符號,而不是換行符號。

fig = plt.figure(layout="constrained")
ax_dict = fig.subplot_mosaic(mosaic)
identify_axes(ax_dict)
mosaic

跨越多列/列的軸#

我們可以使用 Figure.subplot_mosaic 做到的一件事,而我們無法使用 Figure.subplots 做到的是,指定一個軸應跨越多個列或欄。

如果我們想要重新排列我們的四個軸,使 "C" 成為底部的水平跨度,而 "D" 成為右側的垂直跨度,我們可以使用

axd = plt.figure(layout="constrained").subplot_mosaic(
    """
    ABD
    CCD
    """
)
identify_axes(axd)
mosaic

如果我們不想使用軸填滿圖形中的所有空間,我們可以在網格中指定一些空間為空白

axd = plt.figure(layout="constrained").subplot_mosaic(
    """
    A.C
    BBB
    .D.
    """
)
identify_axes(axd)
mosaic

如果我們偏好使用另一個字元(而不是句點 ".")來標記空白空間,我們可以使用 empty_sentinel 來指定要使用的字元。

axd = plt.figure(layout="constrained").subplot_mosaic(
    """
    aX
    Xb
    """,
    empty_sentinel="X",
)
identify_axes(axd)
mosaic

在內部,我們使用的字母沒有附加任何意義,任何 Unicode 碼位都是有效的!

axd = plt.figure(layout="constrained").subplot_mosaic(
    """αб
       ℝ☢"""
)
identify_axes(axd)
mosaic

不建議使用空白字元作為字串速記中的標籤或空白哨兵,因為它可能會在處理輸入時被移除。

控制鑲嵌建立#

此功能建立在 gridspec 之上,您可以將關鍵字引數傳遞給底層的 gridspec.GridSpec (與 Figure.subplots 相同)。

在這種情況下,我們想要使用輸入來指定排列,但設定列/欄的相對寬度。為了方便起見,gridspec.GridSpecheight_ratioswidth_ratios 會在 Figure.subplot_mosaic 呼叫序列中公開。

axd = plt.figure(layout="constrained").subplot_mosaic(
    """
    .a.
    bAc
    .d.
    """,
    # set the height ratios between the rows
    height_ratios=[1, 3.5, 1],
    # set the width ratios between the columns
    width_ratios=[1, 3.5, 1],
)
identify_axes(axd)
mosaic

其他 gridspec.GridSpec 關鍵字可以透過 gridspec_kw 傳遞。例如,使用 {left, right, bottom, top} 關鍵字引數來定位整個鑲嵌,以便在一個圖形中放入相同鑲嵌的多個版本。

mosaic = """AA
            BC"""
fig = plt.figure()
axd = fig.subplot_mosaic(
    mosaic,
    gridspec_kw={
        "bottom": 0.25,
        "top": 0.95,
        "left": 0.1,
        "right": 0.5,
        "wspace": 0.5,
        "hspace": 0.5,
    },
)
identify_axes(axd)

axd = fig.subplot_mosaic(
    mosaic,
    gridspec_kw={
        "bottom": 0.05,
        "top": 0.75,
        "left": 0.6,
        "right": 0.95,
        "wspace": 0.5,
        "hspace": 0.5,
    },
)
identify_axes(axd)
mosaic

或者,您可以使用子圖形功能

mosaic = """AA
            BC"""
fig = plt.figure(layout="constrained")
left, right = fig.subfigures(nrows=1, ncols=2)
axd = left.subplot_mosaic(mosaic)
identify_axes(axd)

axd = right.subplot_mosaic(mosaic)
identify_axes(axd)
mosaic

控制子圖建立#

我們也可以傳遞用於建立子圖的引數 (同樣,與 Figure.subplots 相同),這將套用到建立的所有軸。

axd = plt.figure(layout="constrained").subplot_mosaic(
    "AB", subplot_kw={"projection": "polar"}
)
identify_axes(axd)
mosaic

每個軸的子圖關鍵字引數#

如果您需要個別控制傳遞給每個子圖的參數,請使用 per_subplot_kw 來傳遞軸識別碼 (或軸識別碼的元組) 與要傳遞的關鍵字字典之間的對應。

在 3.7 版中新增。

fig, axd = plt.subplot_mosaic(
    "AB;CD",
    per_subplot_kw={
        "A": {"projection": "polar"},
        ("C", "D"): {"xscale": "log"}
    },
)
identify_axes(axd)
mosaic

如果佈局是使用字串速記指定的,那麼我們知道軸標籤將是一個字元,並且可以明確地解譯 per_subplot_kw 中較長的字串,以指定要套用關鍵字的一組軸

fig, axd = plt.subplot_mosaic(
    "AB;CD",
    per_subplot_kw={
        "AD": {"projection": "polar"},
        "BC": {"facecolor": ".9"}
    },
)
identify_axes(axd)
mosaic

如果同時使用 subplot_kwper_subplot_kw,則會將它們合併,並以 per_subplot_kw 的優先順序為準

axd = plt.figure(layout="constrained").subplot_mosaic(
    "AB;CD",
    subplot_kw={"facecolor": "xkcd:tangerine"},
    per_subplot_kw={
        "B": {"facecolor": "xkcd:water blue"},
        "D": {"projection": "polar", "facecolor": "w"},
    }
)
identify_axes(axd)
mosaic

巢狀清單輸入#

我們可以使用字串速記做的一切,也可以在傳入清單時做到 (在內部,我們會將字串速記轉換為巢狀清單),例如使用跨度、空白和 gridspec_kw

axd = plt.figure(layout="constrained").subplot_mosaic(
    [
        ["main", "zoom"],
        ["main", "BLANK"],
    ],
    empty_sentinel="BLANK",
    width_ratios=[2, 1],
)
identify_axes(axd)
mosaic

此外,使用清單輸入,我們可以指定巢狀鑲嵌。內部清單的任何元素都可以是另一組巢狀清單

inner = [
    ["inner A"],
    ["inner B"],
]

outer_nested_mosaic = [
    ["main", inner],
    ["bottom", "bottom"],
]
axd = plt.figure(layout="constrained").subplot_mosaic(
    outer_nested_mosaic, empty_sentinel=None
)
identify_axes(axd, fontsize=36)
mosaic

我們也可以傳入 2D NumPy 陣列來執行如下操作

mosaic = np.zeros((4, 4), dtype=int)
for j in range(4):
    mosaic[j, j] = j + 1
axd = plt.figure(layout="constrained").subplot_mosaic(
    mosaic,
    empty_sentinel=0,
)
identify_axes(axd)
mosaic

指令碼的總執行時間: (0 分鐘 15.573 秒)

由 Sphinx-Gallery 產生