Solución:
El propósito de meshgrid
es crear una cuadrícula rectangular a partir de una matriz de valores xy una matriz de valores y.
Entonces, por ejemplo, si queremos crear una cuadrícula donde tengamos un punto en cada valor entero entre 0 y 4 en las direcciones x e y. Para crear una cuadrícula rectangular, necesitamos cada combinación de los x
y y
puntos.
Serán 25 puntos, ¿verdad? Entonces, si quisiéramos crear una matriz xey para todos estos puntos, podría Haz lo siguiente.
x[0,0] = 0 y[0,0] = 0
x[0,1] = 1 y[0,1] = 0
x[0,2] = 2 y[0,2] = 0
x[0,3] = 3 y[0,3] = 0
x[0,4] = 4 y[0,4] = 0
x[1,0] = 0 y[1,0] = 1
x[1,1] = 1 y[1,1] = 1
...
x[4,3] = 3 y[4,3] = 4
x[4,4] = 4 y[4,4] = 4
Esto daría como resultado lo siguiente x
y y
matrices, de modo que el emparejamiento del elemento correspondiente en cada matriz da las coordenadas xey de un punto en la cuadrícula.
x = 0 1 2 3 4 y = 0 0 0 0 0
0 1 2 3 4 1 1 1 1 1
0 1 2 3 4 2 2 2 2 2
0 1 2 3 4 3 3 3 3 3
0 1 2 3 4 4 4 4 4 4
Luego podemos trazarlos para verificar que sean una cuadrícula:
plt.plot(x,y, marker=".", color="k", linestyle="none")
Obviamente, esto se vuelve muy tedioso, especialmente para grandes rangos de x
y y
. En lugar de, meshgrid
realmente puede generar esto para nosotros: todo lo que tenemos que especificar son los únicos x
y y
valores.
xvalues = np.array([0, 1, 2, 3, 4]);
yvalues = np.array([0, 1, 2, 3, 4]);
Ahora, cuando llamamos meshgrid
, obtenemos la salida anterior automáticamente.
xx, yy = np.meshgrid(xvalues, yvalues)
plt.plot(xx, yy, marker=".", color="k", linestyle="none")
La creación de estas cuadrículas rectangulares es útil para varias tareas. En el ejemplo que ha proporcionado en su publicación, es simplemente una forma de probar una función (sin(x**2 + y**2) / (x**2 + y**2)
) sobre un rango de valores para x
y y
.
Debido a que esta función se ha muestreado en una cuadrícula rectangular, la función ahora se puede visualizar como una “imagen”.
Además, el resultado ahora se puede pasar a funciones que esperan datos en una cuadrícula rectangular (es decir, contourf
)
Cortesía de Microsoft Excel:
En realidad, el propósito de np.meshgrid
ya se menciona en la documentación:
np.meshgrid
Devuelve las matrices de coordenadas de los vectores de coordenadas.
Cree matrices de coordenadas ND para evaluaciones vectorizadas de campos escalares / vectoriales ND sobre cuadrículas ND, dadas matrices de coordenadas unidimensionales x1, x2, …, xn.
Entonces, su propósito 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 entre las coordenadas y los 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.
Suponga que sus datos son:
1 2 1
2 5 2
1 2 1
Sin embargo, cada valor representa un área de 3 x 2 kilómetros (horizontal x vertical). 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 x
y y
(es por eso que el valor de retorno de meshgrid
es usualmente xx
o xs
en lugar de x
en este caso elegí h
para horizontalmente!) 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 realizar un seguimiento de las coordenadas. y (aún más importante) puede pasarlos a funciones que necesitan conocer las coordenadas.
Una explicación un poco más larga
Sin embargo, np.meshgrid
en sí mismo no se usa a menudo directamente, principalmente uno solo usa uno de similar objetos np.mgrid
o np.ogrid
. Aquí np.mgrid
representa el sparse=False
y np.ogrid
los sparse=True
caso (me refiero al sparse
argumento de np.meshgrid
). Tenga en cuenta que existe una diferencia significativa entre
np.meshgrid
y np.ogrid
y np.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.imshow
tiene sentido nombrar el primer elemento devuelto de np.meshgrid
x
y el segundo y
mientras que es al revés para np.mgrid
y 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
, es por eso que lo desempaqué como yy, xx
en lugar 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 xey 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]])
diferente a ogrid
estas matrices contienen todos xx
y yy
coordenadas en el -5 <= xx <= 5; -5 <= yy <= 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 permite 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.
Incluso si estos también funcionan para 1D, hay dos (mucho más comunes) funciones de creación de cuadrículas 1D:
np.arange
np.linspace
junto al start
y stop
argumento también apoya el step
argumento (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
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 cuadrícula densa en lugar de una cuadrícula abierta. ¡La retransmisió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 es también cuando NumPys mgrid
y ogrid
resulta 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, desde imshow
no es compatible x
y y
entradas hay que cambiar los ticks a mano. Sería realmente conveniente que aceptara la x
y y
coordenadas, ¿verdad?
Es fácil escribir funciones con NumPy que se ocupen naturalmente de las cuadrículas. Además, hay varias funciones en NumPy, SciPy, matplotlib que esperan que pases 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 acabara de pasar en el 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 algunos red):
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 los modelos funcionales y el ajuste (por ejemplo scipy.interpolate.interp2d
,
scipy.interpolate.griddata
incluso mostrar ejemplos usando np.mgrid
) en Scipy, etc. requieren cuadrículas. La mayoría de estos funcionan con cuadrículas abiertas y cuadrículas densas, sin embargo, algunos solo funcionan con uno de ellos.