In realtà lo scopo di np.meshgrid
è già menzionato nella documentazione:
np.meshgrid
Restituisce le matrici di coordinate dai vettori di coordinate.
Crea array di coordinate ND per valutazioni vettoriali di campi scalari / vettoriali ND su griglie ND, dati gli array di coordinate unidimensionali x1, x2, ..., xn.
Quindi il suo scopo principale è quello di creare una matrice di coordinate.
Probabilmente ti sei appena chiesto:
Perché è necessario creare matrici di coordinate?
La ragione per cui hai bisogno di matrici di coordinate con Python / NumPy è che non esiste una relazione diretta tra coordinate e valori, tranne quando le tue coordinate iniziano con zero e sono numeri puramente positivi. Quindi puoi semplicemente usare gli indici di un array come indice. Tuttavia, in caso contrario, è necessario in qualche modo memorizzare le coordinate accanto ai dati. Ecco dove arrivano le griglie.
Supponiamo che i tuoi dati siano:
1 2 1
2 5 2
1 2 1
Tuttavia, ogni valore rappresenta una regione larga 2 chilometri in orizzontale e 3 chilometri in verticale. Supponiamo che la tua origine sia l'angolo in alto a sinistra e desideri array che rappresentino la distanza che potresti usare:
import numpy as np
h, v = np.meshgrid(np.arange(3)*3, np.arange(3)*2)
dove v è:
array([[0, 0, 0],
[2, 2, 2],
[4, 4, 4]])
e h:
array([[0, 3, 6],
[0, 3, 6],
[0, 3, 6]])
Quindi, se hai due indici, diciamo x
e y
(ecco perché il valore di ritorno meshgrid
è di solito xx
o xs
invece che x
in questo caso ho scelto h
orizzontalmente!) Allora puoi ottenere la coordinata x del punto, la coordinata y del punto e il valore a quel punto usando:
h[x, y] # horizontal coordinate
v[x, y] # vertical coordinate
data[x, y] # value
Ciò rende molto più semplice tenere traccia delle coordinate e (ancora più importante) è possibile passarle a funzioni che devono conoscere le coordinate.
Una spiegazione leggermente più lunga
Tuttavia, di per np.meshgrid
sé non viene spesso utilizzato direttamente, per lo più si utilizza solo uno di oggetti similinp.mgrid
o np.ogrid
. Qui np.mgrid
rappresenta il sparse=False
e np.ogrid
il sparse=True
caso (mi riferisco alla sparse
tesi di np.meshgrid
). Si noti che esiste una differenza significativa tra
np.meshgrid
e np.ogrid
e np.mgrid
: i primi due valori restituiti (se ce ne sono due o più) vengono invertiti. Spesso questo non ha importanza, ma è necessario assegnare nomi di variabili significativi a seconda del contesto.
Ad esempio, nel caso di una griglia 2D e matplotlib.pyplot.imshow
ha senso nominare il primo elemento restituito np.meshgrid
x
e il secondo y
mentre è il contrario per np.mgrid
e np.ogrid
.
np.ogrid
e griglie sparse
>>> import numpy as np
>>> yy, xx = np.ogrid[-5:6, -5:6]
>>> xx
array([[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5]])
>>> yy
array([[-5],
[-4],
[-3],
[-2],
[-1],
[ 0],
[ 1],
[ 2],
[ 3],
[ 4],
[ 5]])
Come già detto, l'output è invertito rispetto a np.meshgrid
, ecco perché l'ho decompresso yy, xx
invece di xx, yy
:
>>> xx, yy = np.meshgrid(np.arange(-5, 6), np.arange(-5, 6), sparse=True)
>>> xx
array([[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5]])
>>> yy
array([[-5],
[-4],
[-3],
[-2],
[-1],
[ 0],
[ 1],
[ 2],
[ 3],
[ 4],
[ 5]])
Sembra già coordinate, in particolare le linee xey per i grafici 2D.
visualizzati:
yy, xx = np.ogrid[-5:6, -5:6]
plt.figure()
plt.title('ogrid (sparse meshgrid)')
plt.grid()
plt.xticks(xx.ravel())
plt.yticks(yy.ravel())
plt.scatter(xx, np.zeros_like(xx), color="blue", marker="*")
plt.scatter(np.zeros_like(yy), yy, color="red", marker="x")
np.mgrid
e griglie dense / rafforzate
>>> yy, xx = np.mgrid[-5:6, -5:6]
>>> xx
array([[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5]])
>>> yy
array([[-5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5],
[-4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4],
[-3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3],
[-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2],
[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
[ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3],
[ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4],
[ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]])
Lo stesso vale qui: l'output è invertito rispetto a np.meshgrid
:
>>> xx, yy = np.meshgrid(np.arange(-5, 6), np.arange(-5, 6))
>>> xx
array([[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],
[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5]])
>>> yy
array([[-5, -5, -5, -5, -5, -5, -5, -5, -5, -5, -5],
[-4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4],
[-3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3],
[-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2],
[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
[ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3],
[ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4],
[ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5]])
A differenza di ogrid
questi array contengono tutto xx
e le yy
coordinate in -5 <= xx <= 5; -5 <= aa <= 5 griglia.
yy, xx = np.mgrid[-5:6, -5:6]
plt.figure()
plt.title('mgrid (dense meshgrid)')
plt.grid()
plt.xticks(xx[0])
plt.yticks(yy[:, 0])
plt.scatter(xx, yy, color="red", marker="x")
Funzionalità
Non è limitato solo al 2D, queste funzioni funzionano per dimensioni arbitrarie (beh, c'è un numero massimo di argomenti dati per funzionare in Python e un numero massimo di dimensioni consentite da NumPy):
>>> x1, x2, x3, x4 = np.ogrid[:3, 1:4, 2:5, 3:6]
>>> for i, x in enumerate([x1, x2, x3, x4]):
... print('x{}'.format(i+1))
... print(repr(x))
x1
array([[[[0]]],
[[[1]]],
[[[2]]]])
x2
array([[[[1]],
[[2]],
[[3]]]])
x3
array([[[[2],
[3],
[4]]]])
x4
array([[[[3, 4, 5]]]])
>>> # equivalent meshgrid output, note how the first two arguments are reversed and the unpacking
>>> x2, x1, x3, x4 = np.meshgrid(np.arange(1,4), np.arange(3), np.arange(2, 5), np.arange(3, 6), sparse=True)
>>> for i, x in enumerate([x1, x2, x3, x4]):
... print('x{}'.format(i+1))
... print(repr(x))
# Identical output so it's omitted here.
Anche se funzionano anche per 1D, ci sono due (molto più comuni) funzioni di creazione della griglia 1D:
Oltre all'argomento start
e stop
supporta anche l' step
argomento (anche passaggi complessi che rappresentano il numero di passaggi):
>>> x1, x2 = np.mgrid[1:10:2, 1:10:4j]
>>> x1 # The dimension with the explicit step width of 2
array([[1., 1., 1., 1.],
[3., 3., 3., 3.],
[5., 5., 5., 5.],
[7., 7., 7., 7.],
[9., 9., 9., 9.]])
>>> x2 # The dimension with the "number of steps"
array([[ 1., 4., 7., 10.],
[ 1., 4., 7., 10.],
[ 1., 4., 7., 10.],
[ 1., 4., 7., 10.],
[ 1., 4., 7., 10.]])
applicazioni
Hai specificamente chiesto lo scopo e, infatti, queste griglie sono estremamente utili se hai bisogno di un sistema di coordinate.
Ad esempio se si dispone di una funzione NumPy che calcola la distanza in due dimensioni:
def distance_2d(x_point, y_point, x, y):
return np.hypot(x-x_point, y-y_point)
E vuoi sapere la distanza di ogni punto:
>>> ys, xs = np.ogrid[-5:5, -5:5]
>>> distances = distance_2d(1, 2, xs, ys) # distance to point (1, 2)
>>> distances
array([[9.21954446, 8.60232527, 8.06225775, 7.61577311, 7.28010989,
7.07106781, 7. , 7.07106781, 7.28010989, 7.61577311],
[8.48528137, 7.81024968, 7.21110255, 6.70820393, 6.32455532,
6.08276253, 6. , 6.08276253, 6.32455532, 6.70820393],
[7.81024968, 7.07106781, 6.40312424, 5.83095189, 5.38516481,
5.09901951, 5. , 5.09901951, 5.38516481, 5.83095189],
[7.21110255, 6.40312424, 5.65685425, 5. , 4.47213595,
4.12310563, 4. , 4.12310563, 4.47213595, 5. ],
[6.70820393, 5.83095189, 5. , 4.24264069, 3.60555128,
3.16227766, 3. , 3.16227766, 3.60555128, 4.24264069],
[6.32455532, 5.38516481, 4.47213595, 3.60555128, 2.82842712,
2.23606798, 2. , 2.23606798, 2.82842712, 3.60555128],
[6.08276253, 5.09901951, 4.12310563, 3.16227766, 2.23606798,
1.41421356, 1. , 1.41421356, 2.23606798, 3.16227766],
[6. , 5. , 4. , 3. , 2. ,
1. , 0. , 1. , 2. , 3. ],
[6.08276253, 5.09901951, 4.12310563, 3.16227766, 2.23606798,
1.41421356, 1. , 1.41421356, 2.23606798, 3.16227766],
[6.32455532, 5.38516481, 4.47213595, 3.60555128, 2.82842712,
2.23606798, 2. , 2.23606798, 2.82842712, 3.60555128]])
L'output sarebbe identico se si passasse in una griglia densa anziché in una griglia aperta. La trasmissione NumPys lo rende possibile!
Visualizziamo il risultato:
plt.figure()
plt.title('distance to point (1, 2)')
plt.imshow(distances, origin='lower', interpolation="none")
plt.xticks(np.arange(xs.shape[1]), xs.ravel()) # need to set the ticks manually
plt.yticks(np.arange(ys.shape[0]), ys.ravel())
plt.colorbar()
E questo è anche quando NumPys mgrid
e ogrid
diventa molto conveniente perché ti consente di cambiare facilmente la risoluzione delle tue griglie:
ys, xs = np.ogrid[-5:5:200j, -5:5:200j]
# otherwise same code as above
Tuttavia, poiché imshow
non supporta x
e y
input, è necessario modificare manualmente i tick. Sarebbe davvero conveniente se accettasse le coordinate x
e y
, giusto?
È facile scrivere funzioni con NumPy che gestiscono naturalmente le griglie. Inoltre, ci sono diverse funzioni in NumPy, SciPy, matplotlib che prevedono di passare nella griglia.
Mi piacciono le immagini, quindi esploriamo matplotlib.pyplot.contour
:
ys, xs = np.mgrid[-5:5:200j, -5:5:200j]
density = np.sin(ys)-np.cos(xs)
plt.figure()
plt.contour(xs, ys, density)
Nota come le coordinate sono già impostate correttamente! Non sarebbe il caso se si fosse appena passati in density
.
O per fare un altro esempio divertente usando i modelli di astropia (questa volta non mi interessa molto le coordinate, le uso semplicemente per creare una griglia):
from astropy.modeling import models
z = np.zeros((100, 100))
y, x = np.mgrid[0:100, 0:100]
for _ in range(10):
g2d = models.Gaussian2D(amplitude=100,
x_mean=np.random.randint(0, 100),
y_mean=np.random.randint(0, 100),
x_stddev=3,
y_stddev=3)
z += g2d(x, y)
a2d = models.AiryDisk2D(amplitude=70,
x_0=np.random.randint(0, 100),
y_0=np.random.randint(0, 100),
radius=5)
z += a2d(x, y)
Anche se questo è solo "per gli sguardi", diverse funzioni relative ai modelli funzionali e all'adattamento (ad esempio scipy.interpolate.interp2d
,
scipy.interpolate.griddata
mostrano anche gli esempi che utilizzano np.mgrid
) in Scipy, ecc. Richiedono delle griglie. La maggior parte di questi funziona con griglie aperte e griglie dense, tuttavia alcune funzionano solo con una di esse.
xx
eyy
. La parte misteriosa per me è stata la ragione per cui restituisce quella coppia di risultati e che aspetto hanno. La risposta di Hai Phan è utile per questo. Immagino che lo faccia per comodità, poiché la trama vuole due parametri del genere.