Composition de figures complexes et sémantiques #

Avertissement

Ce tutoriel documente l'API expérimentale/provisoire. Nous le publions dans la v3.3 pour obtenir les commentaires des utilisateurs. Nous pouvons apporter des modifications avec rupture dans les futures versions sans avertissement.

Disposer les axes d'une figure dans une grille non uniforme peut être à la fois fastidieux et verbeux. Pour les grilles denses et uniformes que nous avons, Figure.subplotsmais pour les mises en page plus complexes, telles que les axes qui s'étendent sur plusieurs colonnes/lignes de la mise en page ou qui laissent certaines zones de la figure vides, vous pouvez utiliser gridspec.GridSpec(voir Disposition de plusieurs axes dans une figure ) ou placer manuellement votre axes. Figure.subplot_mosaicvise à fournir une interface pour présenter visuellement vos axes (sous forme d'art ASCII ou de listes imbriquées) afin de rationaliser ce processus.

Cette interface prend naturellement en charge le nommage de vos axes. Figure.subplot_mosaicrenvoie un dictionnaire indexé sur les étiquettes utilisées pour mettre en page la figure. En renvoyant des structures de données avec des noms, il est plus facile d'écrire du code de traçage indépendant de la disposition de la figure.

Ceci est inspiré d'un MEP proposé et de la bibliothèque patchwork pour R. Bien que nous n'implémentions pas le style de surcharge d'opérateur, nous fournissons une API Pythonic pour spécifier les dispositions d'axes (imbriquées).

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)

Si nous voulons une grille 2x2, nous pouvons utiliser Figure.subplotsqui renvoie un tableau 2D dans axes.Axeslequel nous pouvons indexer pour effectuer notre tracé.

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


fig = plt.figure(constrained_layout=True)
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)},
)
mosaïque

En utilisant Figure.subplot_mosaicnous pouvons produire la même mosaïque mais donner aux axes des noms sémantiques

fig = plt.figure(constrained_layout=True)
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)
mosaïque

Une différence clé entre Figure.subplotset Figure.subplot_mosaicest la valeur de retour. Alors que le premier renvoie un tableau pour l'accès à l'index, le second renvoie un dictionnaire mappant les étiquettes aux axes.Axesinstances créées

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

Chaîne abrégée #

En limitant nos étiquettes d'axes à des caractères uniques, nous pouvons "dessiner" les axes que nous voulons comme "art ASCII". Ce qui suit

mosaic = """
    AB
    CD
    """

nous donnera 4 axes disposés dans une grille 2x2 et générera la même mosaïque de figures que ci-dessus (mais maintenant étiquetée avec plutôt que ).{"A", "B", "C", "D"}{"bar", "plot", "hist", "image"}

fig = plt.figure(constrained_layout=True)
ax_dict = fig.subplot_mosaic(mosaic)
identify_axes(ax_dict)
mosaïque

Alternativement, vous pouvez utiliser la notation de chaîne plus compacte

mosaic = "AB;CD"

vous donnera la même composition, où le ";"est utilisé comme séparateur de ligne au lieu de nouvelle ligne.

fig = plt.figure(constrained_layout=True)
ax_dict = fig.subplot_mosaic(mosaic)
identify_axes(ax_dict)
mosaïque

Axes couvrant plusieurs lignes/colonnes #

Quelque chose que nous pouvons faire avec Figure.subplot_mosaicce que vous ne pouvez pas faire Figure.subplotsest de spécifier qu'un Axe doit s'étendre sur plusieurs lignes ou colonnes.

Si nous voulons réorganiser nos quatre axes pour avoir "C"une portée horizontale en bas et "D"une portée verticale à droite, nous ferions

axd = plt.figure(constrained_layout=True).subplot_mosaic(
    """
    ABD
    CCD
    """
)
identify_axes(axd)
mosaïque

Si nous ne voulons pas remplir tous les espaces de la figure avec des axes, nous pouvons spécifier que certains espaces de la grille soient vides

axd = plt.figure(constrained_layout=True).subplot_mosaic(
    """
    A.C
    BBB
    .D.
    """
)
identify_axes(axd)
mosaïque

Si nous préférons utiliser un autre caractère (plutôt qu'un point ".") pour marquer l'espace vide, nous pouvons utiliser empty_sentinel pour spécifier le caractère à utiliser.

axd = plt.figure(constrained_layout=True).subplot_mosaic(
    """
    aX
    Xb
    """,
    empty_sentinel="X",
)
identify_axes(axd)
mosaïque

En interne, il n'y a aucune signification attachée aux lettres que nous utilisons, tout point de code Unicode est valide !

axd = plt.figure(constrained_layout=True).subplot_mosaic(
    """αб
       ℝ☢"""
)
identify_axes(axd)
mosaïque

Il n'est pas recommandé d'utiliser un espace blanc comme étiquette ou comme sentinelle vide avec le raccourci de chaîne car il peut être supprimé lors du traitement de l'entrée.

Contrôler la création de mosaïques et de sous-parcelles #

Cette fonctionnalité est construite au-dessus de gridspecet vous pouvez transmettre les arguments de mots-clés au sous-jacent gridspec.GridSpec (identique à Figure.subplots).

Dans ce cas, nous voulons utiliser l'entrée pour spécifier la disposition, mais définir les largeurs relatives des lignes/colonnes via gridspec_kw .

axd = plt.figure(constrained_layout=True).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)
mosaïque

Ou utilisez les arguments de mot clé { left , right , bottom , top } pour positionner la mosaïque globale afin de mettre plusieurs versions de la même mosaïque dans une figure

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)
mosaïque

Alternativement, vous pouvez utiliser la fonctionnalité de sous-figure :

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

axd = right.subplot_mosaic(mosaic)
identify_axes(axd)
mosaïque

Nous pouvons également passer par les arguments utilisés pour créer les sous-parcelles (encore une fois, identiques à Figure.subplots).

axd = plt.figure(constrained_layout=True).subplot_mosaic(
    "AB", subplot_kw={"projection": "polar"}
)
identify_axes(axd)
mosaïque

Entrée de liste imbriquée #

Tout ce que nous pouvons faire avec le raccourci de chaîne, nous pouvons également le faire lors du passage dans une liste (en interne, nous convertissons le raccourci de chaîne en une liste imbriquée), par exemple en utilisant des plages, des blancs et gridspec_kw :

axd = plt.figure(constrained_layout=True).subplot_mosaic(
    [
        ["main", "zoom"],
        ["main", "BLANK"],
    ],
    empty_sentinel="BLANK",
    width_ratios=[2, 1],
)
identify_axes(axd)
mosaïque

De plus, en utilisant l'entrée de liste, nous pouvons spécifier des mosaïques imbriquées. Tout élément de la liste interne peut être un autre ensemble de listes imbriquées :

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

outer_nested_mosaic = [
    ["main", inner],
    ["bottom", "bottom"],
]
axd = plt.figure(constrained_layout=True).subplot_mosaic(
    outer_nested_mosaic, empty_sentinel=None
)
identify_axes(axd, fontsize=36)
mosaïque

Nous pouvons également passer un tableau NumPy 2D pour faire des choses comme

mosaic = np.zeros((4, 4), dtype=int)
for j in range(4):
    mosaic[j, j] = j + 1
axd = plt.figure(constrained_layout=True).subplot_mosaic(
    mosaic,
    empty_sentinel=0,
)
identify_axes(axd)
mosaïque

Durée totale d'exécution du script : (0 minutes 9,170 secondes)

Galerie générée par Sphinx-Gallery