HOWTO · Matplotlib

Crea un grafico di superficie in Matplotlib

Possiamo creare un grafico di superficie in Matplotlib usando il metodo plot_surface().

In questa pagina

Per creare un grafico di superficie 3D in Matplotlib, prepara array bidimensionali X, Y e Z con forme compatibili, crea un oggetto axes con projection="3d" e passa gli array a Axes3D.plot_surface. L’esempio seguente costruisce una griglia di coordinate a partire da una funzione matematica, assegna etichette agli assi, aggiunge una barra dei colori e salva la figura. Lo stesso procedimento funziona con valori misurati, purché le coordinate e le altezze possano essere rappresentate su una griglia rettangolare.

Configurare l’ambiente

Installa NumPy per le operazioni con array e griglie e Matplotlib per creare i grafici. In un terminale, esegui python -m pip install numpy matplotlib nell’ambiente Python che intendi usare. Se gestisci già i pacchetti tramite un ambiente o un notebook, installali lì, così saranno disponibili all’interprete che esegue lo script.

L’esempio importa NumPy e matplotlib.pyplot. Matplotlib include il supporto per i grafici 3D: projection="3d" crea gli axes tridimensionali, quindi non serve importare separatamente Axes3D. Il metodo usato su questi axes è Axes3D.plot_surface.

Preparare i dati

Un grafico di superficie assegna un’altezza a ogni posizione definita da due coordinate. Per coordinate equidistanti, np.linspace() genera valori di coordinate unidimensionali e np.meshgrid() li espande in griglie bidimensionali X e Y. Calcola Z a partire da queste griglie oppure fornisci altezze misurate disposte nelle stesse posizioni.

I tre array devono avere forme compatibili: ogni Z[i, j] è l’altezza della coppia di coordinate X[i, j], Y[i, j]. Le versioni attuali di Matplotlib accettano anche vettori di coordinate unidimensionali x e y e li espandono rispetto a una Z bidimensionale, ma l’array delle altezze deve comunque corrispondere alle dimensioni della griglia: una Z calcolata su una griglia diversa da X e Y solleva un ValueError, come mostra la sezione di risoluzione dei problemi qui sotto con un’esecuzione reale. Crea prima le griglie di coordinate, poi calcola o rimodella le altezze sulle stesse griglie. Se le misurazioni sparse non formano una griglia rettangolare, prepara o interpola i dati su una griglia prima di usare plot_surface.

Creare il grafico di superficie

Questo esempio completo usa la funzione radiale regolare sin(sqrt(x² + y²)). Crea le griglie, disegna la superficie, assegna un’etichetta a tutte e tre le dimensioni, aggiunge una scala di colori e salva il risultato come surface-plot.png nella cartella corrente. Eseguilo in un ambiente con NumPy e Matplotlib installati. L’esempio non stampa testo nella console: salva un’immagine e apre una finestra con la figura Matplotlib se il backend attivo supporta la visualizzazione. Le etichette degli assi definite nel codice restano in inglese nel grafico, poiché l’esempio è conservato byte per byte.

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(-5, 5, 100)
y = np.linspace(-5, 5, 100)
X, Y = np.meshgrid(x, y)
Z = np.sin(np.sqrt(X**2 + Y**2))

fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")
surface = ax.plot_surface(X, Y, Z, cmap="viridis")
ax.set_xlabel("X")
ax.set_ylabel("Y")
ax.set_zlabel("Z")
ax.set_title("Surface Plot")
fig.colorbar(surface, ax=ax, label="Z value")
fig.savefig("surface-plot.png", dpi=150, bbox_inches="tight")
plt.show()

Grafico di superficie 3D della funzione seno radiale con la mappa di colori viridis e una barra dei colori che mostra valori di Z compresi tra -1 e 1.

Poiché lo script non stampa testo, non c’è un blocco di output della console. La barra dei colori associa i colori della superficie ai rispettivi valori; la griglia ne definisce la geometria, mentre la mappa di colori ne controlla l’aspetto. In un notebook, di norma la figura viene visualizzata dall’ambiente di plotting; in uno script, plt.show() chiede al backend Matplotlib configurato di mostrarla.

Personalizzare il grafico di superficie

L’esempio seguente applica le personalizzazioni più comuni alla stessa superficie. Cambia la mappa di colori in plasma, rimuove i bordi della griglia e ruota la vista a un’elevazione e un azimut fissi, prima di aggiungere la barra dei colori e salvare la figura come customized-surface-plot.png.

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(-5, 5, 100)
y = np.linspace(-5, 5, 100)
X, Y = np.meshgrid(x, y)
Z = np.sin(np.sqrt(X**2 + Y**2))

fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")
surface = ax.plot_surface(X, Y, Z, cmap="plasma", edgecolor="none")
ax.set_xlabel("X")
ax.set_ylabel("Y")
ax.set_zlabel("Z")
ax.set_title("Customized Surface Plot")
ax.view_init(elev=30, azim=45)
fig.colorbar(surface, ax=ax, label="Z value")
fig.savefig("customized-surface-plot.png", dpi=150, bbox_inches="tight")
plt.show()

Lo stesso grafico di superficie con la mappa di colori plasma, senza bordi della griglia e con la vista ruotata a un’elevazione di 30 gradi e un azimut di 45 gradi.

Scegli una mappa di colori che renda più facile leggere le variazioni di Z. In questo esempio, cmap="plasma" sostituisce la progressione viridis del primo esempio e la barra dei colori aiuta a interpretare questa corrispondenza. L’opzione edgecolor="none" rimuove i bordi disegnati della griglia, che su una griglia fitta possono distrarre dalla continuità della superficie; mantieni i bordi predefiniti quando la griglia di campionamento deve restare visibile.

Per cambiare il punto di osservazione, chiama ax.view_init(elev=30, azim=45) prima di salvare o visualizzare la figura. elev modifica l’elevazione della vista, mentre azim la ruota attorno all’asse verticale. Regola questi valori finché la forma e gli assi risultano chiari: l’angolazione migliore dipende dai dati e da ciò che vuoi mostrare. Rispetto alla prima figura, la vista ruotata della seconda figura mostra meglio il versante lontano del picco centrale.

Per una griglia fitta, plot_surface consente di controllare il campionamento di righe e colonne, ad esempio con rcount e ccount. Valori più bassi tracciano meno righe e colonne campionate e possono rendere più rapida la visualizzazione di una superficie grande, a scapito dei dettagli visibili. Mantieni un numero di campioni sufficiente a preservare le variazioni importanti dei dati. Salva un’immagine da condividere con fig.savefig("surface-plot.png", dpi=150, bbox_inches="tight"); per compatibilità con i backend che chiudono la figura dopo averla mostrata, salva prima di chiamare plt.show(). Se vuoi conservare più versioni, usa nomi di file o cartelle diversi.

Confronto con un grafico wireframe

Un grafico wireframe disegna solo le linee della griglia della stessa superficie, senza facce riempite né mappa di colori. Axes3D.plot_wireframe accetta le stesse griglie X, Y e Z, quindi puoi passare da una vista all’altra senza preparare nuovi dati. Il wireframe è utile quando conta la struttura stessa della griglia, quando una superficie piena nasconderebbe il lato lontano della forma, oppure come controllo strutturale rapido di dati sconosciuti prima di generare una superficie completa.

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(-5, 5, 50)
y = np.linspace(-5, 5, 50)
X, Y = np.meshgrid(x, y)
Z = np.sin(np.sqrt(X**2 + Y**2))

fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")
ax.plot_wireframe(X, Y, Z, rstride=2, cstride=2, color="tab:blue")
ax.set_xlabel("X")
ax.set_ylabel("Y")
ax.set_zlabel("Z")
ax.set_title("Wireframe Plot")
fig.savefig("wireframe-plot.png", dpi=150, bbox_inches="tight")
plt.show()

Grafico wireframe della stessa superficie seno radiale, disegnato con linee di griglia blu e senza facce riempite.

Gli argomenti rstride e cstride saltano righe e colonne in modo che le linee restino leggibili; l’esempio disegna una riga e una colonna ogni due di una griglia 50 per 50. A differenza di plot_surface, un wireframe non ha colori delle facce, quindi non c’è nulla che una barra dei colori possa spiegare. Per una presentazione finale, la superficie piena di solito comunica meglio le differenze di altezza, mentre il wireframe mostra più chiaramente come viene campionata la griglia.

Correggere un errore di forma non corrispondente

L’errore più comune di plot_surface è un array Z la cui forma non corrisponde alle griglie di coordinate. Lo script seguente calcola Z su una griglia 50 per 50, ma la passa insieme alle griglie X e Y da 100 per 100:

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(-5, 5, 100)
y = np.linspace(-5, 5, 100)
X, Y = np.meshgrid(x, y)

x_small = np.linspace(-5, 5, 50)
y_small = np.linspace(-5, 5, 50)
X_small, Y_small = np.meshgrid(x_small, y_small)
Z = np.sin(np.sqrt(X_small**2 + Y_small**2))

fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")
ax.plot_surface(X, Y, Z, cmap="viridis")
plt.show()

L’esecuzione dello script si interrompe alla chiamata a plot_surface con questo errore:

ValueError: shape mismatch: objects cannot be broadcast to a single shape.  Mismatch is between arg 0 with shape (100, 100) and arg 2 with shape (50, 50).

Per correggerlo, calcola Z dalle stesse griglie che passi a plot_surface, come fa il primo esempio, in modo che Z.shape sia uguale a X.shape e Y.shape. Se non sei sicuro, stampa le tre forme prima di disegnare e confrontale: ogni elemento di Z ha bisogno di esattamente una coppia di coordinate, né di più né di meno.

Conclusione

I passaggi principali sono creare le griglie di coordinate, fare in modo che Z corrisponda a tali griglie, aggiungere un axes 3D e chiamare Axes3D.plot_surface. Una mappa e una barra dei colori aiutano a interpretare i valori di altezza, mentre i controlli della vista e del campionamento rendono più chiara la forma. Quando devi esaminare la griglia stessa, plot_wireframe rappresenta gli stessi dati come linee. Se ti serve un file immagine, salva la figura; poi regola la risoluzione della griglia o l’angolo di visuale in base ai dati.

Domande frequenti

  1. Quali dati posso usare per un grafico di superficie?
    Usa valori definiti su due coordinate, con un’altezza associata a ogni posizione di una griglia rettangolare. Una funzione è comoda per un esempio; i valori misurati vanno bene se sono disposti su griglie di coordinate compatibili. I punti sparsi devono prima essere convertiti in una griglia.

  2. Come installo le librerie necessarie?
    Installa NumPy e Matplotlib nell’ambiente che esegue lo script con python -m pip install numpy matplotlib.

  3. Posso cambiare i colori della superficie?
    Sì. Imposta l’argomento cmap di plot_surface, poi aggiungi una barra dei colori con fig.colorbar(surface, ax=ax) per mostrare la corrispondenza tra colori e valori.

  4. Come cambio l’angolo di visuale?
    Usa ax.view_init(elev=..., azim=...). L’elevazione cambia l’altezza della visuale, mentre l’azimut ne modifica la rotazione attorno al grafico.

  5. Perché X, Y e Z devono avere forme compatibili?
    Matplotlib ha bisogno di una coppia di coordinate e di un’altezza per ogni posizione della griglia. Se Z è stata calcolata su una griglia diversa, plot_surface solleva un ValueError relativo a forme che non possono essere trasmesse insieme. Crea griglie di coordinate bidimensionali con np.meshgrid() e calcola Z a partire da esse, così gli elementi corrispondenti saranno allineati.