W3docs

Istogrammi Matplotlib in Python — Guida Completa

Impara a creare e personalizzare istogrammi in Python con Matplotlib. Copre bin, densità, sovrapposizione KDE, cumulativi, affiancati e salvataggio su file.

La funzione hist() di Matplotlib rende semplice visualizzare come un dataset è distribuito su un intervallo numerico. Questo capitolo tratta tutto, dal primo istogramma minimo alle tecniche pratiche che utilizzerai nei progetti reali: scegliere il numero giusto di bin, tracciare la densità invece dei conteggi grezzi, sovrapporre una curva KDE, confrontare distribuzioni multiple e salvare il risultato in un file.

Prima di iniziare, assicurati che Matplotlib e NumPy siano installati:

pip install matplotlib numpy

Se sei nuovo alla libreria, consulta prima i capitoli Introduzione a Matplotlib e Per Iniziare.

Cos'è un Istogramma?

Un istogramma divide una variabile numerica continua in intervalli equidistanti chiamati bin e disegna una barra per ciascun bin la cui altezza è uguale al numero di osservazioni che ricadono in quell'intervallo. A differenza di un grafico a barre, che confronta categorie discrete, un istogramma rivela la forma di una distribuzione — se è simmetrica, asimmetrica, bimodale o presenta valori anomali.

Usa un istogramma quando vuoi rispondere a domande come:

  • Dove è concentrata la maggior parte dei dati?
  • La distribuzione è approssimativamente normale, o è asimmetrica?
  • Ci sono lacune o picchi multipli (dati bimodali)?
  • Ci sono valori anomali lontani dalla massa dei dati?

Creare un Istogramma di Base

Passa un array o una lista monodimensionale a plt.hist() e Matplotlib sceglie automaticamente il numero di bin:

import matplotlib.pyplot as plt
import numpy as np

# Reproducible example: 1 000 values from a normal distribution
rng = np.random.default_rng(42)
data = rng.normal(loc=0, scale=1, size=1000)

plt.hist(data)

plt.title('Basic Histogram')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.tight_layout()
plt.show()

plt.tight_layout() impedisce che le etichette degli assi vengano tagliate — una buona abitudine da aggiungere prima di ogni chiamata a show() o savefig().

Scegliere il Numero di Bin

Il parametro bins è il principale parametro di regolazione per un istogramma. Troppo pochi bin nascondono la struttura; troppi bin creano rumore.

import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(42)
data = rng.normal(loc=0, scale=1, size=1000)

fig, axes = plt.subplots(1, 3, figsize=(12, 4))

for ax, n_bins in zip(axes, [5, 30, 100]):
    ax.hist(data, bins=n_bins, color='steelblue', edgecolor='white')
    ax.set_title(f'bins={n_bins}')
    ax.set_xlabel('Value')
    ax.set_ylabel('Frequency')

plt.suptitle('Effect of Bin Count', y=1.02)
plt.tight_layout()
plt.show()

Linee guida pratiche:

  • 5–10 bin — utili per dataset molto piccoli (n < 50) o panoramiche rapide.
  • 20–50 bin — buon valore predefinito per la maggior parte dei dataset (n = 100–10 000).
  • 50–100+ bin — appropriato per dataset di grandi dimensioni (n > 10 000) dove la struttura fine è importante.
  • Passa una regola come stringa invece di un numero: bins='auto', bins='fd' (Freedman–Diaconis), o bins='sturges' — Matplotlib delega a np.histogram_bin_edges() di NumPy.

Puoi anche passare una lista esplicita di bordi di bin per una spaziatura non uniforme:

import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(42)
data = rng.exponential(scale=2, size=1000)

# Finer bins near 0, coarser bins in the tail
edges = [0, 0.5, 1, 1.5, 2, 3, 4, 6, 8, 12]

plt.hist(data, bins=edges, color='darkorange', edgecolor='white')
plt.title('Custom Bin Edges (Exponential Data)')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.tight_layout()
plt.show()

Personalizzare l'Aspetto

Colore, Trasparenza e Bordo

import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(42)
data = rng.normal(loc=5, scale=1.5, size=800)

plt.hist(
    data,
    bins=30,
    color='steelblue',
    edgecolor='white',   # thin white line between bars
    linewidth=0.5,
    alpha=0.85,          # slight transparency
)

plt.title('Styled Histogram')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.tight_layout()
plt.show()

alpha (0 = completamente trasparente, 1 = completamente opaco) è particolarmente utile quando si sovrappongono più istogrammi in modo che le barre non si nascondano a vicenda.

Rimuovere il Rumore Visivo

Rimuovere i bordi superiore e destro conferisce all'istogramma un aspetto più pulito:

import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(42)
data = rng.normal(loc=0, scale=1, size=1000)

fig, ax = plt.subplots(figsize=(8, 4))
ax.hist(data, bins=30, color='cornflowerblue', edgecolor='white')

ax.set_title('Clean Histogram')
ax.set_xlabel('Value')
ax.set_ylabel('Frequency')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)

plt.tight_layout()
plt.show()

Istogrammi di Densità

Per impostazione predefinita hist() mostra i conteggi grezzi sull'asse y. Passa density=True per normalizzare l'asse y in modo che l'area totale di tutte le barre sia uguale a 1. Questo trasforma l'istogramma in una stima della densità di probabilità, rendendo facile confrontare dataset di dimensioni diverse.

import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(42)
data = rng.normal(loc=0, scale=1, size=1000)

plt.hist(data, bins=30, density=True, color='mediumseagreen', edgecolor='white')

plt.title('Density Histogram')
plt.xlabel('Value')
plt.ylabel('Probability Density')
plt.tight_layout()
plt.show()

Nota: i valori dell'asse y sono densità, non probabilità. Moltiplica una densità per la larghezza del bin per ottenere la probabilità per quel bin.

Sovrapporre una Curva KDE

Una stima di densità del kernel (KDE) è una curva regolare che approssima la distribuzione di probabilità sottostante. Sovrapporla a un istogramma di densità fornisce un quadro intuitivo della forma della distribuzione. Usa scipy.stats.gaussian_kde per calcolarla:

import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import gaussian_kde

rng = np.random.default_rng(42)
data = rng.normal(loc=0, scale=1, size=1000)

fig, ax = plt.subplots(figsize=(8, 4))

# Density histogram
ax.hist(data, bins=30, density=True,
        color='steelblue', edgecolor='white', alpha=0.6, label='Histogram')

# KDE curve
xs = np.linspace(data.min(), data.max(), 300)
kde = gaussian_kde(data)
ax.plot(xs, kde(xs), color='navy', linewidth=2, label='KDE')

ax.set_title('Histogram with KDE Overlay')
ax.set_xlabel('Value')
ax.set_ylabel('Probability Density')
ax.legend()
plt.tight_layout()
plt.show()

Installa SciPy se non è già disponibile:

pip install scipy

Confrontare Distribuzioni Multiple

Per confrontare due o più distribuzioni sugli stessi assi, chiama hist() più volte e usa alpha per mantenere le barre semi-trasparenti. Imposta gli stessi bins per entrambe in modo che le larghezze delle barre siano confrontabili:

import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(42)
group_a = rng.normal(loc=0,   scale=1,   size=500)
group_b = rng.normal(loc=2,   scale=1.5, size=500)

shared_bins = np.linspace(-5, 8, 40)

plt.hist(group_a, bins=shared_bins, alpha=0.6, color='steelblue',   label='Group A')
plt.hist(group_b, bins=shared_bins, alpha=0.6, color='darkorange',  label='Group B')

plt.title('Overlapping Histograms')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.legend()
plt.tight_layout()
plt.show()

Definire shared_bins tramite np.linspace() garantisce che entrambi gli istogrammi usino bordi di bin identici e che le loro barre si allineino visivamente.

Istogrammi Affiancati (In Pila)

Quando la sovrapposizione rende difficile leggere le distribuzioni individuali, usa plt.subplots() per posizionarle affiancate:

import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(42)
group_a = rng.normal(loc=0,   scale=1,   size=500)
group_b = rng.normal(loc=2,   scale=1.5, size=500)

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4), sharey=True)

ax1.hist(group_a, bins=30, color='steelblue',  edgecolor='white')
ax1.set_title('Group A')
ax1.set_xlabel('Value')
ax1.set_ylabel('Frequency')

ax2.hist(group_b, bins=30, color='darkorange', edgecolor='white')
ax2.set_title('Group B')
ax2.set_xlabel('Value')

plt.suptitle('Side-by-Side Histograms')
plt.tight_layout()
plt.show()

sharey=True blocca entrambi i subplot sulla stessa scala dell'asse y in modo che le altezze delle barre siano direttamente confrontabili. Consulta il capitolo Subplot di Matplotlib per ulteriori opzioni di layout.

Istogrammi Cumulativi

Passa cumulative=True per disegnare un istogramma in cui ogni barra rappresenta il conteggio totale delle osservazioni fino a e incluso quel bin. È utile per rispondere a domande come "quale frazione di valori è inferiore a X?":

import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(42)
data = rng.normal(loc=0, scale=1, size=1000)

plt.hist(data, bins=30, cumulative=True, density=True,
         color='mediumpurple', edgecolor='white')

plt.title('Cumulative Density Histogram')
plt.xlabel('Value')
plt.ylabel('Cumulative Probability')
plt.tight_layout()
plt.show()

Combinato con density=True, l'istogramma cumulativo diventa un'approssimazione a funzione a gradini della CDF empirica (funzione di distribuzione cumulativa). L'asse y va da 0 a 1.

Orientamento dell'Istogramma

Passa orientation='horizontal' per disegnare barre che si estendono verso sinistra dall'asse y. È raramente la scelta predefinita, ma rispecchia il layout dei grafici di distribuzione marginale di un grafico a dispersione:

import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(42)
data = rng.normal(loc=0, scale=1, size=500)

plt.hist(data, bins=20, orientation='horizontal',
         color='tomato', edgecolor='white')

plt.title('Horizontal Histogram')
plt.ylabel('Value')
plt.xlabel('Frequency')
plt.tight_layout()
plt.show()

Salvare un Istogramma su File

Usa plt.savefig() prima di plt.show() (o al suo posto). Specifica il formato tramite l'estensione del file:

import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(42)
data = rng.normal(loc=0, scale=1, size=1000)

plt.hist(data, bins=30, color='steelblue', edgecolor='white')
plt.title('Distribution of Sample Data')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.tight_layout()

plt.savefig('histogram.png', dpi=150)   # raster, good for web
plt.savefig('histogram.pdf')            # vector, good for print/publication
plt.show()

Formati comuni: png, pdf, svg, eps. Usa svg o pdf quando hai bisogno di un'immagine scalabile e di qualità per la stampa.

Parametri Principali di hist() — Riferimento Rapido

ParametroTipoDescrizione
binsint, list, o strNumero di bin, bordi espliciti, o nome di regola ('auto', 'fd', 'sturges')
densityboolNormalizza in modo che l'area totale = 1 (densità di probabilità)
cumulativeboolOgni barra mostra il conteggio/densità cumulativa fino a quel bin
orientationstr'vertical' (predefinito) o 'horizontal'
colorstrColore di riempimento delle barre
edgecolorstrColore del bordo delle barre; 'white' fornisce un separatore pulito
alphafloat 0–1Trasparenza; impostare sotto 1 quando si sovrappongono istogrammi
labelstrEtichetta della legenda per questo istogramma
histtypestr'bar' (predefinito), 'step', 'stepfilled'
range(min, max)Limita i dati a questo intervallo prima del binning

Errori Comuni

  • Semi casuali. np.random.randn() produce valori diversi ad ogni esecuzione. Usa np.random.default_rng(seed) per esempi riproducibili.
  • density vs normed. Il vecchio parametro normed=True è stato rimosso in Matplotlib 3.x. Usa sempre density=True.
  • Confrontare istogrammi con dimensioni di campione diverse. Le barre di frequenza grezze non sono confrontabili quando le dimensioni dei gruppi differiscono — usa density=True per normalizzare entrambe.
  • Dati interi discreti. Per i valori interi (es. lanci di dadi, valutazioni di sondaggi), imposta i bordi dei bin a mezzi interi — bins=[0.5, 1.5, 2.5, ..., 6.5] — in modo che ogni intero rientri in esattamente un bin senza ambiguità sui bordi.
  • plt.show() cancella la figura. Se chiami show() e poi savefig(), ottieni un file vuoto. Chiama sempre savefig() prima di show().

Capitoli Correlati

Was this page helpful?