HOWTO · Matplotlib

Graphique de densité de Matplotlib

Pour générer un diagramme de densité en Python, nous estimons d'abord la fonction de densité à partir des données fournies en utilisant la méthode `gaussian_kde()` du module `scipy.stats` et ensuite nous traçons la fonction de densité pour générer le diagramme de densité.

Sur cette page

Un graphique de densité présente une estimation lissée de la façon dont des données d’échantillon unidimensionnelles sont réparties. En Python, la voie habituelle est l’estimation de densité par noyau (kernel density estimation, KDE) : on estime une fonction de densité à partir des observations avec gaussian_kde() du module scipy.stats, on l’évalue sur une grille de valeurs de x, puis on trace la courbe obtenue avec Matplotlib. Vous pouvez obtenir le même résultat avec moins de code grâce à kdeplot() du paquet seaborn ou en définissant kind="density" dans pandas.DataFrame.plot(). Les exemples ci-dessous utilisent NumPy, SciPy et Matplotlib ; les variantes Seaborn et pandas nécessitent en outre l’installation de ces paquets, et le tracé pandas requiert SciPy car il appelle gaussian_kde en interne.

Créer un graphique de densité avec scipy.stats.gaussian_kde

gaussian_kde construit l’estimation en plaçant un noyau — une petite bosse de forme gaussienne — sur chaque observation, puis en faisant la moyenne de ces bosses. Le résultat est une fonction de densité appelable : passez-lui une grille de positions x et elle renvoie la densité estimée à chaque position, c’est-à-dire exactement la courbe que Matplotlib dessine.

Commencez par ajuster l’estimation sur l’échantillon, puis évaluez-la sur une grille :

Le code et les libellés ou sorties affichés dans cette section restent en anglais.

import numpy as np
from scipy.stats import gaussian_kde

data = [2, 3, 3, 4, 2, 1, 5, 6, 4, 3, 3, 3, 6, 4, 5, 4, 3, 2]
density = gaussian_kde(data)
x = np.linspace(-2, 10, 300)
y = density(x)

print(f"Peak estimated density: {y.max():.4f} at x = {x[np.argmax(y)]:.2f}")
Peak estimated density: 0.2635 at x = 3.14

Le programme ajuste gaussian_kde sur les 18 valeurs de l’échantillon et évalue l’estimation en 300 positions régulièrement espacées entre -2 et 10 ; l’estimation culmine près de x = 3, là où la plupart des observations se concentrent. Choisissez la grille en fonction de l’étendue de vos propres données, par exemple d’un peu en dessous du minimum jusqu’à un peu au-dessus du maximum ; la fenêtre de -2 à 10 utilisée ici ne convient qu’à cet échantillon.

Le script de tracé ci-dessous fixe le backend non interactif Agg de Matplotlib afin qu’il s’exécute de la même façon, qu’un écran soit connecté ou non, et réduit au silence la journalisation de la bibliothèque pour qu’un message de police au premier lancement n’apparaisse pas dans la sortie d’erreur standard. Exécutez ceci pour voir quel backend est sélectionné :

import logging
import matplotlib
matplotlib.use("Agg")
logging.getLogger("matplotlib").setLevel(logging.ERROR)
import matplotlib.pyplot as plt

print("Backend:", matplotlib.get_backend())
Backend: Agg

Tracez maintenant la courbe évaluée avec le script complet :

import logging
import matplotlib
matplotlib.use("Agg")
logging.getLogger("matplotlib").setLevel(logging.ERROR)
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import gaussian_kde

data = [2, 3, 3, 4, 2, 1, 5, 6, 4, 3, 3, 3, 6, 4, 5, 4, 3, 2]
density = gaussian_kde(data)
x = np.linspace(-2, 10, 300)
y = density(x)

plt.plot(x, y)
plt.title("Density Plot of the data")
plt.xlabel("Value")
plt.ylabel("Estimated density")

print("Plotted", len(plt.gca().get_lines()), "density line")
Plotted 1 density line

plt.plot() dessine la densité estimée sous la forme d’une seule ligne sur les axes courants, et plt.gca().get_lines() rapporte l’unique ligne qu’il a ajoutée. Dans une session interactive, vous pouvez retirer l’appel matplotlib.use("Agg") et ajouter plt.show() après les appels de tracé pour ouvrir la fenêtre de la figure.

Courbe de densité estimée produite par scipy.stats gaussian_kde pour les données d’échantillon

La courbe affichée est la densité estimée : plus haute là où les observations sont concentrées, plus basse là où elles sont éparses. Comme il s’agit d’une estimation, considérez sa forme comme un aperçu de cet échantillon plutôt que comme une mesure de la distribution réelle.

Ajuster la largeur de bande de la KDE

La largeur de bande contrôle le degré de lissage de l’estimation. Une largeur de bande importante élargit chaque noyau et produit une courbe plus plate et plus lisse qui peut masquer une structure réelle ; une petite largeur de bande resserre les noyaux, si bien que la courbe suit l’échantillon de près et peut montrer des ondulations qui ne sont que du bruit dans ce jeu de données particulier. Par défaut, gaussian_kde choisit la largeur de bande avec la règle de Scott ; vous pouvez aussi passer "scott" ou "silverman" explicitement, ou fournir un scalaire via l’argument bw_method, qui fixe la largeur du noyau par rapport à la dispersion des données. Le même réglage est disponible ensuite via la méthode publique set_bandwidth(), il n’est donc pas nécessaire d’appeler des fonctions internes privées.

Le code et les libellés ou sorties affichés dans cette section restent en anglais.

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import gaussian_kde

data = [2, 3, 3, 4, 2, 1, 5, 6, 4, 3, 3, 3, 6, 4, 5, 4, 3, 2]
density = gaussian_kde(data, bw_method=0.25)
x = np.linspace(-2, 10, 300)
y = density(x)

plt.plot(x, y)
plt.title("Density Plot with a smaller bandwidth")
plt.xlabel("Value")
plt.ylabel("Estimated density")

print(f"Peak estimated density: {y.max():.4f}")
Peak estimated density: 0.3913

Avec bw_method=0.25, chaque noyau est plus étroit que la valeur par défaut, donc la courbe monte plus haut à son pic et suit de plus près la concentration de l’échantillon. Augmentez la valeur pour lisser davantage, diminuez-la pour lisser moins, et comparez les formes obtenues plutôt que de considérer une largeur de bande particulière comme universellement correcte.

Courbe de densité estimée calculée avec une plus petite largeur de bande, montrant un pic plus élevé

Créer un graphique de densité avec Seaborn

seaborn encapsule la même idée en un seul appel : kdeplot() estime la densité en interne et la dessine sur des axes Matplotlib. Passez l’échantillon via x, et mettez à l’échelle la largeur de bande choisie automatiquement avec bw_adjust — les valeurs supérieures à 1 lissent davantage, les valeurs inférieures à 1 lissent moins. L’ancienne fonction distplot() est dépréciée ; utilisez kdeplot() pour la courbe seule ou displot(kind="kde") pour la variante au niveau de la figure.

Le code et les libellés ou sorties affichés dans cette section restent en anglais.

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import seaborn as sns

data = [2, 3, 3, 4, 2, 1, 5, 6, 4, 3, 3, 3, 6, 4, 5, 4, 3, 2]
ax = sns.kdeplot(x=data, bw_adjust=0.5)
ax.set_title("Density Plot of the data")
ax.set_xlabel("Value")

print("kdeplot drew", len(ax.get_lines()), "density line")
kdeplot drew 1 density line

kdeplot renvoie les Axes Matplotlib sur lesquels il a dessiné, si bien que les titres et les étiquettes s’appliquent avec les méthodes habituelles des axes ; ajoutez plt.show() pour afficher la fenêtre de la figure en mode interactif.

Courbe de densité dessinée par seaborn kdeplot pour les données d’échantillon

Créer un graphique de densité avec pandas

Quand l’échantillon se trouve déjà dans un DataFrame ou une Series, pandas.DataFrame.plot peut produire la courbe directement : passez kind="density" (l’alias kind="kde" fonctionne aussi). pandas calcule l’estimation avec gaussian_kde, donc SciPy doit être installé pour ce type de tracé.

Le code et les libellés ou sorties affichés dans cette section restent en anglais.

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import pandas as pd

data = [2, 3, 3, 4, 2, 1, 5, 6, 4, 3, 3, 3, 6, 4, 5, 4, 3, 2]
df = pd.DataFrame(data, columns=["value"])
ax = df.plot(kind="density")
ax.set_title("Density Plot of the data")
ax.set_xlabel("Value")

print("pandas labeled the axis:", ax.get_ylabel())
pandas labeled the axis: Density

Appelée sur un DataFrame, la méthode trace une courbe par colonne et étiquette automatiquement l’axe des y Density ; appelez-la sur une seule Series pour une variable nommée. Comme pour l’approche SciPy, la douceur de la courbe dépend de la largeur de bande de l’estimateur, que pandas déduit des données.