Capire i Grafici a Dispersione in Python
Crea e interpreta grafici a dispersione in Python con Matplotlib e Seaborn. Correlazione, codifica colori, linee di regressione e usi nel ML.
Un grafico a dispersione posiziona un punto per ogni coppia (x, y) in un dataset. La nuvola di punti risultante rivela se due variabili numeriche sono correlate, quanto strettamente e in quale direzione — rendendo i grafici a dispersione indispensabili per l'analisi esplorativa dei dati e i flussi di lavoro di machine learning.
Questo capitolo tratta:
- Cosa mostrano i grafici a dispersione e come leggerli
- Creare grafici a dispersione con Matplotlib e Seaborn
- Personalizzare colori, dimensioni e trasparenza
- Codificare una terza variabile con colore o dimensione (bubble chart)
- Tracciare più gruppi con una legenda
- Aggiungere una linea di tendenza di regressione
- Casi d'uso comuni nel machine learning
Cosa Mostra un Grafico a Dispersione
Ogni punto rappresenta un'osservazione. L'asse orizzontale contiene una variabile, l'asse verticale ne contiene un'altra. La forma complessiva della nuvola indica la correlazione tra le due variabili.
Leggere il Pattern
| Pattern | Significato |
|---|---|
| I punti salgono da sinistra a destra | Correlazione positiva — all'aumentare di X, Y tende ad aumentare |
| I punti scendono da sinistra a destra | Correlazione negativa — all'aumentare di X, Y tende a diminuire |
| Nessuna forma riconoscibile | Nessuna correlazione lineare tra le variabili |
| Banda stretta e compatta | Correlazione forte |
| Nuvola ampia e diffusa | Correlazione debole |
| Punti lontani dalla nuvola principale | Outlier — vale la pena indagarli |
Il coefficiente di correlazione di Pearson r riassume questo pattern in un singolo numero da -1 (perfettamente negativo) a +1 (perfettamente positivo). Un valore vicino a 0 indica nessuna relazione lineare. I grafici a dispersione ti permettono di vedere ciò che r non può dirti — ad esempio, due dataset possono condividere lo stesso r pur avendo forme completamente diverse (vedi il quartetto di Anscombe).
Creare un Grafico a Dispersione con Matplotlib
plt.scatter() di Matplotlib è l'opzione più flessibile. Installa Matplotlib se non lo hai già fatto:
pip install matplotlib numpyEsempio Base
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(seed=42)
# Simulate hours studied vs exam score
hours = rng.uniform(1, 10, 40)
score = 5 * hours + rng.normal(scale=8, size=40)
plt.scatter(hours, score)
plt.xlabel('Hours Studied')
plt.ylabel('Exam Score')
plt.title('Hours Studied vs Exam Score')
plt.tight_layout()
plt.show()La pendenza positiva nella nuvola risultante mostra che un maggior numero di ore di studio è correlato a punteggi d'esame più alti.
Personalizzare il Colore e la Dimensione dei Marker
I tre parametri più utili di plt.scatter() sono:
c— nome del colore, stringa esadecimale, o array di valori (mappati tramite una colormap)s— dimensione del marker in punti quadrati (default 20); passa uno scalare o un arrayalpha— trasparenza da 0 (invisibile) a 1 (solido); usa 0.4–0.7 per punti sovrapposti
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(seed=7)
x = rng.normal(loc=5, scale=2, size=60)
y = rng.normal(loc=5, scale=2, size=60)
plt.scatter(x, y, c='steelblue', s=80, alpha=0.6, edgecolors='white', linewidths=0.5)
plt.xlabel('X')
plt.ylabel('Y')
plt.title('Customized Scatter Plot')
plt.tight_layout()
plt.show()edgecolors='white' con linewidths=0.5 aggiunge un sottile bordo bianco attorno a ogni punto, rendendo più facile distinguere i singoli punti quando si sovrappongono.
Codificare una Terza Variabile con il Colore
Passa un array a c per colorare ogni punto in base a una terza variabile numerica. Aggiungi plt.colorbar() affinché i lettori sappiano cosa rappresentano i colori:
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(seed=3)
x = rng.random(50)
y = rng.random(50)
temperature = rng.uniform(15, 35, 50) # third variable, e.g. temperature in °C
scatter = plt.scatter(x, y, c=temperature, cmap='coolwarm', s=80, alpha=0.8)
plt.colorbar(scatter, label='Temperature (°C)')
plt.xlabel('Longitude')
plt.ylabel('Latitude')
plt.title('Sensor Readings by Temperature')
plt.tight_layout()
plt.show()Usa colormap percettivamente uniformi — 'viridis', 'plasma', 'cividis' o 'coolwarm' — anziché 'jet' o 'rainbow', che distorcono la percezione e non sono adatte ai daltonici.
Codificare una Terza Variabile con la Dimensione delle Bolle
Passa un array a s per rendere l'area di ogni marker proporzionale a una terza variabile — questo si chiama bubble chart:
import matplotlib.pyplot as plt
import numpy as np
countries = ['USA', 'China', 'Japan', 'Germany', 'UK']
gdp = [25.5, 18.0, 4.2, 4.1, 3.1] # trillion USD
life_exp = [76.4, 77.1, 84.3, 80.6, 81.3] # years
population = [334, 1412, 125, 84, 67] # millions — encoded as size
# Scale population to a visible marker area range
sizes = [p * 1.5 for p in population]
plt.scatter(gdp, life_exp, s=sizes, alpha=0.6, edgecolors='black', linewidths=0.8)
for i, name in enumerate(countries):
plt.annotate(name, (gdp[i], life_exp[i]), textcoords='offset points',
xytext=(6, 4), fontsize=9)
plt.xlabel('GDP (trillion USD)')
plt.ylabel('Life Expectancy (years)')
plt.title('GDP vs Life Expectancy (bubble size = population)')
plt.tight_layout()
plt.show()Creare un Grafico a Dispersione con Seaborn
sns.scatterplot() di Seaborn funziona direttamente con i DataFrame di Pandas e aggiunge funzionalità come il raggruppamento automatico per colonna categoriale e un parametro hue integrato per la codifica a colori.
Installa Seaborn prima:
pip install seaborn pandasGrafico a Dispersione Base con Seaborn
import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt
data = pd.DataFrame({
'hours': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
'score': [45, 50, 55, 60, 65, 70, 72, 80, 85, 92],
})
sns.scatterplot(data=data, x='hours', y='score')
plt.xlabel('Hours Studied')
plt.ylabel('Exam Score')
plt.title('Hours Studied vs Exam Score')
plt.tight_layout()
plt.show()Codifica dei Gruppi con i Colori tramite hue
Il parametro hue assegna automaticamente un colore diverso a ogni categoria e aggiunge una legenda:
import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt
data = pd.DataFrame({
'sepal_length': [5.1, 4.9, 6.3, 5.8, 7.0, 6.4, 6.3, 5.8, 7.1, 6.3],
'sepal_width': [3.5, 3.0, 2.9, 2.7, 3.2, 3.2, 3.3, 2.7, 3.0, 2.9],
'species': ['setosa', 'setosa', 'versicolor', 'versicolor',
'virginica', 'virginica', 'virginica', 'versicolor',
'virginica', 'virginica'],
})
sns.scatterplot(data=data, x='sepal_length', y='sepal_width', hue='species')
plt.title('Iris: Sepal Length vs Sepal Width')
plt.tight_layout()
plt.show()Seaborn crea automaticamente la legenda. Questo equivale a chiamare plt.scatter() più volte con colori diversi.
Aggiungere una Linea di Regressione con sns.regplot()
sns.regplot() combina un grafico a dispersione con una linea di regressione adattata e una banda di confidenza:
import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(seed=10)
x = np.linspace(1, 10, 30)
y = 3 * x + rng.normal(scale=4, size=30)
data = pd.DataFrame({'x': x, 'y': y})
sns.regplot(data=data, x='x', y='y', scatter_kws={'alpha': 0.6}, line_kws={'color': 'red'})
plt.xlabel('X')
plt.ylabel('Y')
plt.title('Scatter Plot with Regression Line')
plt.tight_layout()
plt.show()L'area ombreggiata attorno alla linea è un intervallo di confidenza al 95%. Usa ci=None per rimuoverla.
Tracciare Più Gruppi
Con Matplotlib
Chiama plt.scatter() una volta per gruppo e imposta label= su ogni chiamata:
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(seed=0)
groups = {
'Group A': (2, 3),
'Group B': (6, 6),
'Group C': (9, 2),
}
for name, (cx, cy) in groups.items():
x = rng.normal(loc=cx, scale=0.6, size=30)
y = rng.normal(loc=cy, scale=0.6, size=30)
plt.scatter(x, y, s=50, alpha=0.7, label=name)
plt.legend()
plt.xlabel('X')
plt.ylabel('Y')
plt.title('Three Distinct Clusters')
plt.tight_layout()
plt.show()Ogni chiamata a scatter() sceglie automaticamente il colore successivo dal ciclo di colori predefinito di Matplotlib.
Con Seaborn
Passa un DataFrame e usa hue= e facoltativamente style= per distinguere i gruppi:
import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(seed=1)
rows = []
for group, (cx, cy) in [('A', (2, 3)), ('B', (6, 6)), ('C', (9, 2))]:
for _ in range(25):
rows.append({'x': rng.normal(cx, 0.6), 'y': rng.normal(cy, 0.6), 'group': group})
df = pd.DataFrame(rows)
sns.scatterplot(data=df, x='x', y='y', hue='group', style='group')
plt.title('Three Clusters — Seaborn Multi-Group')
plt.tight_layout()
plt.show()style='group' assegna una forma di marker distinta a ogni gruppo in aggiunta al colore, utile per i lettori che stampano in bianco e nero.
Grafici a Dispersione nel Machine Learning
I grafici a dispersione non servono solo per l'esplorazione — fanno parte del flusso di lavoro ML fondamentale.
1. Verificare le Relazioni Lineari Prima della Regressione
Prima di addestrare un modello di regressione lineare, traccia le feature di input rispetto al target. Una dispersione approssimativamente lineare indica che la regressione lineare è appropriata:
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(seed=5)
house_size = rng.uniform(50, 300, 60) # square metres
house_price = 2000 * house_size + rng.normal(scale=40000, size=60) # EUR
plt.scatter(house_size, house_price, alpha=0.6, s=50)
plt.xlabel('House Size (m²)')
plt.ylabel('Price (EUR)')
plt.title('House Size vs Price — linear pattern suggests linear regression')
plt.tight_layout()
plt.show()Se la dispersione mostra una curva anziché una linea, potresti aver bisogno di feature polinomiali o di un modello diverso.
2. Visualizzare i Cluster Dopo il K-Means
Dopo aver eseguito un algoritmo di clustering come il k-means, colora ogni punto in base alla sua etichetta di cluster per confermare la separazione:
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(seed=8)
# Simulate cluster assignments from k-means
centers = [(1, 1), (5, 5), (9, 1)]
X, labels = [], []
for i, (cx, cy) in enumerate(centers):
X.extend(zip(rng.normal(cx, 0.7, 30), rng.normal(cy, 0.7, 30)))
labels.extend([i] * 30)
X = np.array(X)
labels = np.array(labels)
colors = ['tab:blue', 'tab:orange', 'tab:green']
for k in range(3):
mask = labels == k
plt.scatter(X[mask, 0], X[mask, 1], c=colors[k], s=50, alpha=0.7, label=f'Cluster {k}')
plt.legend()
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')
plt.title('K-Means Cluster Assignments')
plt.tight_layout()
plt.show()Nuvole ben separate confermano che l'algoritmo ha trovato raggruppamenti significativi.
3. Valutare le Previsioni del Modello di Regressione
Traccia i valori effettivi vs previsti dopo aver addestrato un modello. Un modello perfetto produce punti lungo la diagonale y = x:
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(seed=2)
# Simulate actual and predicted values from a trained model
actual = rng.uniform(10, 100, 50)
predicted = actual + rng.normal(scale=8, size=50) # model with some noise
plt.scatter(actual, predicted, alpha=0.6, s=60, edgecolors='black', linewidths=0.5)
# Draw the ideal y = x line
lim = [min(actual.min(), predicted.min()) - 5, max(actual.max(), predicted.max()) + 5]
plt.plot(lim, lim, 'r--', linewidth=1.5, label='Perfect prediction')
plt.xlim(lim)
plt.ylim(lim)
plt.xlabel('Actual Values')
plt.ylabel('Predicted Values')
plt.title('Actual vs Predicted — regression model evaluation')
plt.legend()
plt.tight_layout()
plt.show()Punti distribuiti casualmente attorno alla diagonale (senza forma ad arco o a ventaglio sistematica) indicano che gli errori del modello sono non distorti.
4. Visualizzare la Riduzione della Dimensionalità (PCA / t-SNE)
Dopo aver ridotto dati ad alta dimensionalità a due dimensioni con PCA o t-SNE, un grafico a dispersione è il modo naturale per visualizzare il risultato. Ogni punto è un'osservazione; il colore indica la sua etichetta di classe:
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(seed=20)
# Simulate 2-D PCA output for three classes
class_data = {
'Class 0': ((-3, 0), 0.8),
'Class 1': ((0, 3), 0.8),
'Class 2': ((3, 0), 0.8),
}
for label, ((cx, cy), spread) in class_data.items():
x = rng.normal(cx, spread, 40)
y = rng.normal(cy, spread, 40)
plt.scatter(x, y, s=30, alpha=0.7, label=label)
plt.legend()
plt.xlabel('PC 1')
plt.ylabel('PC 2')
plt.title('PCA Projection — 2D visualization of high-dimensional data')
plt.tight_layout()
plt.show()Cluster che si separano nettamente dopo la riduzione suggeriscono che le classi sono genuinamente distinguibili dalle feature originali.
Salvare i Grafici a Dispersione su File
Usa plt.savefig() prima di plt.show() — chiamare show() prima cancella la figura:
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(seed=99)
x = rng.random(50)
y = rng.random(50)
plt.scatter(x, y, alpha=0.7, s=60)
plt.xlabel('X')
plt.ylabel('Y')
plt.title('Scatter Plot')
plt.tight_layout()
plt.savefig('scatter.png', dpi=150) # raster — good for web
plt.savefig('scatter.pdf') # vector — best for publications
plt.show()Usa dpi=300 per immagini PNG di qualità da stampa.
Quando Usare Ciascuna Libreria
| Situazione | Strumento consigliato |
|---|---|
| Grafico rapido una tantum con array NumPy | matplotlib.pyplot.scatter() |
| Lavoro con DataFrame Pandas | seaborn.scatterplot() |
| Codifica colore o dimensione per punto | matplotlib.pyplot.scatter() |
| Raggruppamento automatico per colonna | seaborn.scatterplot(hue=...) |
| Linea di regressione integrata | seaborn.regplot() |
| Personalizzazione avanzata di Matplotlib | fig, ax = plt.subplots() poi ax.scatter() |
Per un approfondimento completo dei parametri di scatter plot di Matplotlib — incluse scale logaritmiche, annotazioni, forme dei marker e confronti tra scatter() e plot() — vedi il capitolo Matplotlib Scatter Plots.
Errori Comuni
Variabile non definita. Ogni frammento di codice in questo capitolo è autonomo. Se combini più frammenti, assicurati che x e y siano definiti nello stesso script prima di chiamare plt.scatter().
Figura non azzerata tra i grafici. Dopo plt.show(), Matplotlib azzera la figura. Se esegui frammenti in un notebook Jupyter, ogni cella crea automaticamente una nuova figura. In uno script Python normale, chiama plt.figure() per iniziare un nuovo grafico se vuoi più chart separati.
Sovrapposizione dei punti. Con molti punti tutti sovrapposti, il grafico appare come un blob riempito. Risolvi il problema con alpha=0.3 per mostrare la densità, oppure passa a plt.hexbin() per il binning istogramma 2D.
Nessuna colorbar. Se passi un array a c, aggiungi sempre plt.colorbar() — senza di essa i lettori non possono decodificare la scala dei colori.
Capitoli Correlati
- Matplotlib Scatter Plots — riferimento completo per scatter plot Matplotlib: assi logaritmici, annotazioni, bubble chart, colori dei bordi,
scatter()vsplot() - Linear Regression — addestrare e interpretare un modello lineare in Python
- K-Means Clustering — suddividere i dati in gruppi e visualizzarli con grafici a dispersione
- Data Distribution — comprendere la forma dei tuoi dati prima della modellazione
- Matplotlib Histograms — visualizzare la distribuzione di una singola variabile
- Matplotlib Line Plots — tendenze su una variabile ordinata continua
- Train / Test Split — suddividere i dati prima di addestrare e valutare i modelli