En realidad, el propósito de np.meshgridya se menciona en la documentación:
np.meshgrid
Devuelve matrices de coordenadas de vectores de coordenadas.
Realice matrices de coordenadas ND para evaluaciones vectorizadas de campos escalares / vectoriales ND sobre cuadrículas ND, dados los conjuntos de coordenadas unidimensionales x1, x2, ..., xn.
Por lo tanto, su objetivo principal es crear matrices de coordenadas.
Probablemente te hayas preguntado a ti mismo:
¿Por qué necesitamos crear matrices de coordenadas?
La razón por la que necesita matrices de coordenadas con Python / NumPy es que no hay una relación directa de coordenadas a valores, excepto cuando sus coordenadas comienzan con cero y son enteros puramente positivos. Entonces puede usar los índices de una matriz como índice. Sin embargo, cuando ese no es el caso, de alguna manera necesita almacenar coordenadas junto con sus datos. Ahí es donde entran las cuadrículas.
Supongamos que sus datos son:
1 2 1
2 5 2
1 2 1
Sin embargo, cada valor representa una región de 2 kilómetros de ancho horizontalmente y 3 kilómetros verticalmente. Suponga que su origen es la esquina superior izquierda y desea matrices que representen la distancia que podría usar:
import numpy as np
h, v = np.meshgrid(np.arange(3)*3, np.arange(3)*2)
donde v es:
array([[0, 0, 0],
[2, 2, 2],
[4, 4, 4]])
y h:
array([[0, 3, 6],
[0, 3, 6],
[0, 3, 6]])
Entonces, si tiene dos índices, digamos xy y(es por eso que el valor de retorno de meshgridusualmente xxo en xslugar de xen este caso elijo hhorizontalmente), entonces puede obtener la coordenada x del punto, la coordenada y del punto y el valor en ese punto usando:
h[x, y] # horizontal coordinate
v[x, y] # vertical coordinate
data[x, y] # value
Eso hace que sea mucho más fácil hacer un seguimiento de las coordenadas y (aún más importante) puede pasarlas a funciones que necesitan conocer las coordenadas.
Una explicación un poco más larga.
Sin embargo, np.meshgrida menudo no se usa directamente, en su mayoría solo se usa uno de objetos similaresnp.mgrid o np.ogrid. Aquí np.mgridrepresenta el sparse=Falsey np.ogridel sparse=Truecaso (me refiero al sparseargumento del np.meshgrid). Tenga en cuenta que hay una diferencia significativa entre
np.meshgridy np.ogridynp.mgrid : Los dos primeros valores devueltos (si hay dos o más) se invierten. A menudo, esto no importa, pero debe dar nombres de variables significativos según el contexto.
Por ejemplo, en el caso de una cuadrícula 2D y matplotlib.pyplot.imshowtiene sentido nombrar el primer elemento devuelto de np.meshgrid xy el segundo ymientras es al revés para np.mgridy np.ogrid.
np.ogrid y rejillas dispersas
>>> 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]])
Como ya se dijo, la salida se invierte en comparación con np.meshgrid, por eso lo desempaqué como en yy, xxlugar de 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]])
Esto ya parece coordenadas, específicamente las líneas x e y para gráficos 2D.
Visualizado:
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 y rejillas densas / desarrolladas
>>> 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 mismo se aplica aquí: la salida se invierte en comparación con 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 diferencia de ogridestas matrices contienen todas xx y yycoordenadas en -5 <= xx <= 5; -5 <= aa <= 5 cuadrícula.
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")

Funcionalidad
No solo se limita a 2D, estas funciones funcionan para dimensiones arbitrarias (bueno, hay un número máximo de argumentos dados para funcionar en Python y un número máximo de dimensiones que NumPy permite):
>>> 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.
Incluso si estos también funcionan para 1D, hay dos funciones de creación de cuadrícula 1D (mucho más comunes):
Además del argumento starty stop, también admite el stepargumento (incluso pasos complejos que representan el número de pasos):
>>> 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.]])
Aplicaciones
Usted preguntó específicamente sobre el propósito y, de hecho, estas cuadrículas son extremadamente útiles si necesita un sistema de coordenadas.
Por ejemplo, si tiene una función NumPy que calcula la distancia en dos dimensiones:
def distance_2d(x_point, y_point, x, y):
return np.hypot(x-x_point, y-y_point)
Y quieres saber la distancia de cada 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]])
La salida sería idéntica si se pasara en una grilla densa en lugar de una grilla abierta. ¡La transmisión de NumPys lo hace posible!
Visualicemos el resultado:
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()

Y esto también es cuando NumPys mgridy se ogridvuelve muy conveniente porque le permite cambiar fácilmente la resolución de sus cuadrículas:
ys, xs = np.ogrid[-5:5:200j, -5:5:200j]
# otherwise same code as above

Sin embargo, dado imshowque no admite xe yingresa, uno tiene que cambiar los ticks a mano. Sería realmente conveniente si aceptara el xyy coordenadas , ¿verdad?
Es fácil escribir funciones con NumPy que tratan naturalmente con cuadrículas. Además, hay varias funciones en NumPy, SciPy, matplotlib que esperan que pase en la cuadrícula.
Me gustan las imágenes, así que exploremos 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)

¡Observe cómo las coordenadas ya están configuradas correctamente! Ese no sería el caso si acabaras de pasar density.
O para dar otro ejemplo divertido usando modelos de astropía (esta vez no me importan mucho las coordenadas, solo las uso para crear una cuadrícula):
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)

Aunque eso es solo "por el aspecto", varias funciones relacionadas con modelos funcionales y ajuste (por ejemplo scipy.interpolate.interp2d,
scipy.interpolate.griddataincluso mostrar ejemplos usando np.mgrid) en Scipy, etc. requieren cuadrículas. La mayoría de estos funcionan con rejillas abiertas y rejillas densas, sin embargo, algunos solo funcionan con uno de ellos.
xxyyy. La parte misteriosa para mí fue por qué devuelve ese par de resultados y cómo se ven. La respuesta de Hai Phan es útil para eso. Supongo que lo hace por conveniencia, ya que plot quiere dos parámetros como ese.