Créer des histogrammes avec des hachures à l'aide de Matplotlib

PythonPythonBeginner
Pratiquer maintenant

This tutorial is from open-source community. Access the source code

💡 Ce tutoriel est traduit par l'IA à partir de la version anglaise. Pour voir la version originale, vous pouvez cliquer ici

Introduction

Dans ce laboratoire, nous allons apprendre à créer des histogrammes remplis de motifs à l'aide de Matplotlib. Un histogramme est une représentation graphique des données qui utilise des barres pour montrer la fréquence des données numériques. Un histogramme rempli de motifs est un histogramme dans lequel les barres sont remplies d'un motif de lignes, de points ou d'autres symboles.

Conseils sur la machine virtuelle

Une fois le démarrage de la machine virtuelle terminé, cliquez sur le coin supérieur gauche pour basculer vers l'onglet Carnet d'étude pour accéder au carnet Jupyter pour la pratique.

Parfois, vous devrez peut-être attendre quelques secondes pour que le carnet Jupyter ait fini de charger. La validation des opérations ne peut pas être automatisée en raison des limitations du carnet Jupyter.

Si vous rencontrez des problèmes pendant l'apprentissage, n'hésitez pas à demander à Labby. Donnez des commentaires après la session, et nous résoudrons rapidement le problème pour vous.

Importez les bibliothèques nécessaires

Nous allons importer les bibliothèques nécessaires pour ce laboratoire. Nous avons besoin des bibliothèques suivantes :

  • numpy pour générer des données aléatoires
  • matplotlib.pyplot pour créer des graphiques
  • matplotlib.ticker pour définir les emplacements des graduations d'axe
  • cycler pour créer des cycles de style
  • functools.partial pour créer une fonction partielle
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
from cycler import cycler
from functools import partial

Définissez la fonction d'histogramme

Nous allons définir une fonction pour tracer un histogramme sous forme d'un patch en escalier. La fonction prendra les paramètres suivants :

  • ax : les Axes sur lesquels tracer
  • edges : un tableau de longueur n+1 donnant les bords gauche de chaque boîte et le bord droit de la dernière boîte
  • values : un tableau de longueur n des comptes ou des valeurs des boîtes
  • bottoms : un nombre ou un tableau, optionnel, un tableau de longueur n du bas des barres. Si None, on utilise zéro.
  • orientation : une chaîne de caractères, optionnelle, l'orientation de l'histogramme. 'v' (par défaut) a les barres croissant dans la direction y positive.
def filled_hist(ax, edges, values, bottoms=None, orientation='v', **kwargs):
    """
    Trace un histogramme sous forme d'un patch en escalier.

    Paramètres
    ----------
    ax : Axes
        Les axes sur lesquels tracer

    edges : tableau
        Un tableau de longueur n+1 donnant les bords gauche de chaque boîte et le
        bord droit de la dernière boîte.

    values : tableau
        Un tableau de longueur n des comptes ou des valeurs des boîtes

    bottoms : nombre ou tableau, optionnel
        Un tableau de longueur n du bas des barres.  Si None, on utilise zéro.

    orientation : {'v', 'h'}
       Orientation de l'histogramme.  'v' (par défaut) a
       les barres croissant dans la direction y positive.

    **kwargs
        Les autres arguments clés sont transmis à `.fill_between`.

    Retours
    -------
    ret : PolyCollection
        Artiste ajouté aux Axes
    """
    if orientation not in 'hv':
        raise ValueError(f"orientation doit être dans {{'h', 'v'}} et non {orientation}")

    kwargs.setdefault('step', 'post')
    kwargs.setdefault('alpha', 0.7)
    edges = np.asarray(edges)
    values = np.asarray(values)
    if len(edges) - 1!= len(values):
        raise ValueError(f'Doit fournir un bord de boîte de plus que de valeur et non : {len(edges)=} {len(values)=}')

    if bottoms is None:
        bottoms = 0
    bottoms = np.broadcast_to(bottoms, values.shape)

    values = np.append(values, values[-1])
    bottoms = np.append(bottoms, bottoms[-1])
    if orientation == 'h':
        return ax.fill_betweenx(edges, values, bottoms, **kwargs)
    elif orientation == 'v':
        return ax.fill_between(edges, values, bottoms, **kwargs)
    else:
        raise AssertionError("vous ne devriez jamais être ici")

Définissez la fonction d'histogramme empilé

Nous allons définir une fonction pour créer un histogramme empilé. La fonction prendra les paramètres suivants :

  • ax : les axes sur lesquels ajouter des artistes
  • stacked_data : un tableau de forme (M, N). La première dimension sera itérée pour calculer les histogrammes ligne par ligne
  • sty_cycle : un Cycler ou un opérable de dict, le style à appliquer à chaque ensemble
  • bottoms : un tableau, valeur par défaut : 0, les positions initiales des fonds
  • hist_func : une fonction appelable, optionnelle. Doit avoir la signature bin_vals, bin_edges = f(data). bin_edges est supposé être d'une longueur supérieure de 1 à bin_vals
  • labels : une liste de chaînes de caractères, optionnelle, l'étiquette pour chaque ensemble. Si non fourni et que les données empilées sont un tableau, la valeur par défaut est 'ensemble par défaut {n}'. Si stacked_data est un mapping et labels est None, la valeur par défaut est les clés. Si stacked_data est un mapping et labels est donné, alors seulement les colonnes listées seront tracées.
  • plot_func : une fonction appelable, optionnelle, fonction à appeler pour tracer l'histogramme. Doit avoir la signature ret = plot_func(ax, edges, top, bottoms=bottoms, label=label, **kwargs)
  • plot_kwargs : un dictionnaire, optionnel, tous les autres arguments clés à passer à la fonction de tracé. Ceci sera le même pour tous les appels à la fonction de tracé et remplacera les valeurs dans sty_cycle.
def stack_hist(ax, stacked_data, sty_cycle, bottoms=None, hist_func=None, labels=None, plot_func=None, plot_kwargs=None):
    """
    Paramètres
    ----------
    ax : axes.Axes
        Les axes sur lesquels ajouter des artistes

    stacked_data : array ou Mapping
        Un tableau de forme (M, N).  La première dimension sera itérée pour
        calculer les histogrammes ligne par ligne

    sty_cycle : Cycler ou opérable de dict
        Style à appliquer à chaque ensemble

    bottoms : array, valeur par défaut: 0
        Les positions initiales des fonds.

    hist_func : callable, optionnel
        Doit avoir la signature `bin_vals, bin_edges = f(data)`.
        `bin_edges` est supposé être d'une longueur supérieure de 1 à `bin_vals`

    labels : list of str, optionnel
        L'étiquette pour chaque ensemble.

        Si non fourni et que les données empilées sont un tableau, la valeur par défaut est 'ensemble par défaut {n}'

        Si *stacked_data* est un mapping, et *labels* est None, la valeur par défaut est les clés.

        Si *stacked_data* est un mapping et *labels* est donné, alors seulement les
        colonnes listées seront tracées.

    plot_func : callable, optionnel
        Fonction à appeler pour tracer l'histogramme doit avoir la signature :

          ret = plot_func(ax, edges, top, bottoms=bottoms,
                          label=label, **kwargs)

    plot_kwargs : dict, optionnel
        Tous les autres arguments clés à passer à la fonction de tracé.
        Ceci sera le même pour tous les appels à la fonction de tracé et remplacera les valeurs dans *sty_cycle*.

    Retours
    -------
    arts : dict
        Dictionnaire d'artistes indexé sur leurs étiquettes
    """
    ## gérer la fonction de découpage par défaut
    if hist_func is None:
        hist_func = np.histogram

    ## gérer la fonction de tracé par défaut
    if plot_func is None:
        plot_func = filled_hist

    ## gérer la valeur par défaut
    if plot_kwargs is None:
        plot_kwargs = {}

    try:
        l_keys = stacked_data.keys()
        label_data = True
        if labels is None:
            labels = l_keys

    except AttributeError:
        label_data = False
        if labels is None:
            labels = itertools.repeat(None)

    if label_data:
        loop_iter = enumerate((stacked_data[lab], lab, s) for lab, s in zip(labels, sty_cycle))
    else:
        loop_iter = enumerate(zip(stacked_data, labels, sty_cycle))

    arts = {}
    for j, (data, label, sty) in loop_iter:
        if label is None:
            label = f'dflt set {j}'
        label = sty.pop('label', label)
        vals, edges = hist_func(data)
        if bottoms is None:
            bottoms = np.zeros_like(vals)
        top = bottoms + vals
        sty.update(plot_kwargs)
        ret = plot_func(ax, edges, top, bottoms=bottoms, label=label, **sty)
        bottoms = top
        arts[label] = ret
    ax.legend(fontsize=10)
    return arts

Configurez la fonction d'histogramme avec des bins fixes

Nous allons configurer une fonction d'histogramme avec des bins fixes en utilisant numpy.histogram. Nous allons créer 20 bins allant de -3 à 3.

edges = np.linspace(-3, 3, 20, endpoint=True)
hist_func = partial(np.histogram, bins=edges)

Configurez les cycles de style

Nous allons configurer des cycles de style pour les histogrammes en utilisant cycler. Nous allons créer trois cycles de style : l'un pour la couleur de fond, l'un pour l'étiquette et l'un pour le motif de hachure.

color_cycle = cycler(facecolor=plt.rcParams['axes.prop_cycle'][:4])
label_cycle = cycler(label=[f'ensemble {n}' for n in range(4)])
hatch_cycle = cycler(hatch=['/', '*', '+', '|'])

Générez des données aléatoires

Nous allons générer des données aléatoires en utilisant numpy.random.randn. Nous allons générer 4 ensembles de données avec 12250 points chacun.

np.random.seed(19680801)
stack_data = np.random.randn(4, 12250)

Créez l'histogramme avec des hachures

Nous allons créer un histogramme avec des hachures en utilisant la fonction stack_hist que nous avons définie précédemment. Nous allons utiliser les stack_data, color_cycle et hist_func que nous avons définis précédemment. Nous allons également définir plot_kwargs pour inclure la couleur de bord et l'orientation.

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(9, 4.5), tight_layout=True)
arts = stack_hist(ax1, stack_data, color_cycle + label_cycle + hatch_cycle, hist_func=hist_func)

arts = stack_hist(ax2, stack_data, color_cycle, hist_func=hist_func, plot_kwargs=dict(edgecolor='w', orientation='h'))
ax1.set_ylabel('comptes')
ax1.set_xlabel('x')
ax2.set_xlabel('comptes')
ax2.set_ylabel('x')

Créez l'histogramme avec des hachures et des étiquettes

Nous allons créer un histogramme avec des hachures et des étiquettes en utilisant la fonction stack_hist que nous avons définie précédemment. Nous allons utiliser les dict_data, color_cycle et hist_func que nous avons définis précédemment. Nous allons également définir labels sur ['ensemble 0', 'ensemble 3'] pour tracer seulement le premier et le dernier ensemble.

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(9, 4.5), tight_layout=True, sharey=True)
dict_data = dict(zip((c['label'] for c in label_cycle), stack_data))
arts = stack_hist(ax1, dict_data, color_cycle + hatch_cycle, hist_func=hist_func)

arts = stack_hist(ax2, dict_data, color_cycle + hatch_cycle, hist_func=hist_func, labels=['ensemble 0', 'ensemble 3'])
ax1.xaxis.set_major_locator(mticker.MaxNLocator(5))
ax1.set_xlabel('comptes')
ax1.set_ylabel('x')
ax2.set_ylabel('x')

Récapitulatif

Dans ce laboratoire, nous avons appris à créer des histogrammes avec des hachures à l'aide de Matplotlib. Nous avons défini deux fonctions : filled_hist pour tracer un histogramme sous forme d'un patch en escalier, et stack_hist pour créer un histogramme empilé. Nous avons également configuré une fonction d'histogramme avec des bins fixes à l'aide de numpy.histogram, et défini trois cycles de style pour les histogrammes à l'aide de cycler. Enfin, nous avons généré des données aléatoires et créé deux histogrammes avec des hachures à l'aide de la fonction stack_hist.