HOWTO · Matplotlib

Grafico densità Matplotlib

Per generare un grafico di densità usando Python, inizialmente stimiamo la funzione di densità dai dati forniti usando il metodo gaussian_kde() dal modulo scipy.stats e poi tracciamo la funzione di densità per generare il grafico di densità.

In questa pagina

Un grafico di densità mostra una stima smussata di come sono distribuiti i dati campione unidimensionali. In Python il percorso abituale è la stima di densità kernel (KDE): si stima una funzione di densità dalle osservazioni con gaussian_kde() del modulo scipy.stats, la si valuta su una griglia di valori x e si disegna la curva risultante con Matplotlib. Puoi ottenere lo stesso risultato con meno codice tramite kdeplot() del pacchetto seaborn oppure impostando kind="density" in pandas.DataFrame.plot(). Gli esempi seguenti usano NumPy, SciPy e Matplotlib; le varianti con Seaborn e pandas richiedono inoltre l’installazione di quei pacchetti, e il grafico con pandas richiede SciPy perché chiama internamente gaussian_kde.

Crea un grafico di densità con scipy.stats.gaussian_kde

gaussian_kde costruisce la stima posizionando un kernel — una piccola gobba a forma di gaussiana — sopra ogni osservazione e facendo la media delle gobbe. Il risultato è una funzione di densità richiamabile: passandole una griglia di posizioni x, restituisce la densità stimata in ciascuna posizione, che è esattamente la linea disegnata da Matplotlib.

Per prima cosa, adatta la stima al campione e valutala su una griglia:

Il codice e le etichette o gli output visibili in questa sezione restano in inglese.

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

Il programma adatta gaussian_kde ai 18 valori campione e valuta la stima in 300 posizioni equidistanti tra -2 e 10; la stima raggiunge il picco vicino a x = 3, dove si concentrano la maggior parte delle osservazioni. Scegli la griglia in base all’intervallo dei tuoi dati, ad esempio da poco sotto il minimo a poco sopra il massimo; la finestra da -2 a 10 qui si adatta solo a questo campione.

Lo script di plotting qui sotto fissa il backend non interattivo Agg di Matplotlib in modo che venga eseguito allo stesso modo con o senza un display collegato, e silenzia il logging della libreria affinché un messaggio sui font al primo avvio non finisca sullo standard error. Esegui questo per vedere quale backend viene selezionato:

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

Ora disegna la curva valutata con lo script completo:

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() disegna la densità stimata come una linea sugli assi correnti, e plt.gca().get_lines() riporta l’unica linea che ha aggiunto. In una sessione interattiva puoi omettere la chiamata matplotlib.use("Agg") e aggiungere plt.show() dopo le chiamate di plotting per aprire la finestra della figura.

Curva di densità stimata prodotta da scipy.stats gaussian_kde per i dati campione

La curva visualizzata è la densità stimata: più alta dove le osservazioni sono concentrate, più bassa dove sono rade. Poiché è una stima, considera la sua forma come una visione di questo campione piuttosto che una misura della distribuzione vera.

Regola la larghezza di banda della KDE

La larghezza di banda controlla quanto la stima viene smussata. Una larghezza di banda grande allarga ogni kernel e produce una curva più piatta e liscia che può nascondere la struttura reale; una larghezza di banda piccola restringe i kernel, quindi la curva segue il campione da vicino e può mostrare ondulazioni che sono solo rumore in questo particolare dataset. Per impostazione predefinita gaussian_kde sceglie la larghezza di banda con la regola di Scott; puoi anche passare "scott" o "silverman" esplicitamente, oppure fornire uno scalare tramite l’argomento bw_method, che imposta l’ampiezza del kernel rispetto alla dispersione dei dati. La stessa regolazione è disponibile in seguito tramite il metodo pubblico set_bandwidth(), quindi non è necessario chiamare helper privati.

Il codice e le etichette o gli output visibili in questa sezione restano in inglese.

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

Con bw_method=0.25 ogni kernel è più stretto rispetto al valore predefinito, quindi la curva sale più in alto nel suo picco e segue più da vicino la concentrazione del campione. Aumenta il valore per smussare di più, riducilo per smussare di meno, e confronta le forme invece di trattare una singola larghezza di banda come universalmente corretta.

Curva di densità stimata calcolata con una larghezza di banda più piccola, che mostra un picco più alto

Crea un grafico di densità con Seaborn

seaborn racchiude la stessa idea in un’unica chiamata: kdeplot() stima la densità internamente e la disegna su un axes di Matplotlib. Passa il campione tramite x e scala la larghezza di banda scelta automaticamente con bw_adjust — i valori sopra 1 smussano di più, i valori sotto 1 smussano di meno. La vecchia funzione distplot() è deprecata; usa kdeplot() per la sola curva oppure displot(kind="kde") per la variante a livello di figura.

Il codice e le etichette o gli output visibili in questa sezione restano in inglese.

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 restituisce gli Axes di Matplotlib su cui ha disegnato, quindi titoli ed etichette si applicano con i consueti metodi degli assi; aggiungi plt.show() per visualizzare la finestra della figura in modo interattivo.

Curva di densità disegnata da kdeplot di seaborn per i dati campione

Crea un grafico di densità con pandas

Quando il campione si trova già in un DataFrame o in una Series, pandas.DataFrame.plot può produrre la curva direttamente: passa kind="density" (funziona anche l’alias kind="kde"). pandas calcola la stima con gaussian_kde, quindi SciPy deve essere installato per questo tipo di grafico.

Il codice e le etichette o gli output visibili in questa sezione restano in inglese.

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

Chiamando il metodo su un DataFrame si traccia una curva per colonna e l’asse y viene etichettato automaticamente come Density; l’etichetta resta in inglese perché la genera pandas. Chiamalo su una singola Series per una variabile con nome. Come nell’approccio con SciPy, la morbidezza della curva segue la larghezza di banda dello stimatore, che pandas deriva dai dati.