¿Cuál es el propósito de meshgrid en Python/NumPy?
numpy meshgrid (5)
¿Alguien puede explicarme cuál es el propósito de la función meshgrid en Numpy? Sé que crea algún tipo de cuadrícula de coordenadas para trazar, pero realmente no puedo ver el beneficio directo de ello.
Estoy estudiando "Python Machine Learning" de Sebastian Raschka, y él lo está usando para trazar los límites de decisión. Vea la entrada 11 here .
También probé este código de la documentación oficial, pero, una vez más, el resultado realmente no tiene sentido para mí.
x = np.arange(-5, 5, 1)
y = np.arange(-5, 5, 1)
xx, yy = np.meshgrid(x, y, sparse=True)
z = np.sin(xx**2 + yy**2) / (xx**2 + yy**2)
h = plt.contourf(x,y,z)
Por favor, si es posible, muéstrame muchos ejemplos del mundo real.
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 todas las combinaciones de los puntos
x
e
y
.
Esto va a ser 25 puntos, ¿verdad? Entonces, si quisiéramos crear una matriz x e y para todos estos puntos, podríamos hacer 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 las siguientes matrices
x
e
y
, de modo que el emparejamiento del elemento correspondiente en cada matriz da las coordenadas x e y 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 trazar estos para verificar que son una cuadrícula:
plt.plot(x,y, marker=''.'', color=''k'', linestyle=''none'')
Obviamente, esto se vuelve muy tedioso, especialmente para grandes rangos de
x
e
y
.
En cambio,
meshgrid
puede generar esto para nosotros: todo lo que tenemos que especificar son los valores únicos de
x
e
y
.
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 una serie de tareas.
En el ejemplo que ha proporcionado en su publicación, es simplemente una forma de muestrear una función (
sin(x**2 + y**2) / (x**2 + y**2)
) en un rango de valores para
x
e
y
.
Debido a que esta función se ha muestreado en una cuadrícula rectangular, 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
)
En realidad, el propósito de
np.meshgrid
ya se menciona en la documentación:
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:
0 2 4
0 2 4
0 2 4
y h:
0 0 0
3 3 3
6 6 6
Entonces, si tiene dos índices, digamos
x
e
y
(es por eso que el valor de retorno de
meshgrid
generalmente es
xx
o
xs
lugar de
x
en este caso, ¡elegí
h
horizontalmente!), Entonces puede obtener la coordenada x del punto, el 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.meshgrid
sí no se usa directamente, generalmente uno solo usa uno de los objetos
similares
np.mgrid
o
np.ogrid
.
Aquí
np.mgrid
representa
sparse=False
y
np.ogrid
el caso
sparse=True
(me refiero al argumento
sparse
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
, por eso lo desempaqué como
yy, xx
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 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
np.mgrid
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
ogrid
estas matrices contienen
todas las
coordenadas
xx
e
yy
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 de
start
y
stop
, también admite el argumento de
step
(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]])
El resultado sería idéntico si uno 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
mgrid
y
ogrid
vuelven muy convenientes 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 que
imshow
no admite entradas
x
e
y
uno tiene que cambiar los ticks a mano.
Sería realmente conveniente si aceptara las coordenadas
y
, ¿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 solo pasaras la
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 "para el aspecto", varias funciones relacionadas con modelos funcionales y ajuste (por ejemplo,
scipy.interpolate.griddata
,
scipy.interpolate.griddata
incluso muestran 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.
Supongamos que tiene una función:
def sinus2d(x, y):
return np.sin(x) + np.sin(y)
y desea, por ejemplo, ver cómo se ve en el rango de 0 a 2 * pi.
¿Como lo harias?
Allí entra
np.meshgrid
:
xx, yy = np.meshgrid(np.linspace(0,2*np.pi,100), np.linspace(0,2*np.pi,100))
z = sinus2d(xx, yy) # Create the image on this grid
y esa trama se vería así:
import matplotlib.pyplot as plt
plt.imshow(z, origin=''lower'', interpolation=''none'')
plt.show()
Entonces
np.meshgrid
es solo una conveniencia.
En principio, lo mismo podría hacerse mediante:
z2 = sinus2d(np.linspace(0,2*np.pi,100)[:,None], np.linspace(0,2*np.pi,100)[None,:])
pero allí debe conocer sus dimensiones (suponga que tiene más de dos ...) y la transmisión correcta.
np.meshgrid
hace todo esto por ti.
También meshgrid le permite eliminar coordenadas junto con los datos si, por ejemplo, desea hacer una interpolación pero excluye ciertos valores:
condition = z>0.6
z_new = z[condition] # This will make your array 1D
Entonces, ¿cómo harías la interpolación ahora?
Puede
scipy.interpolate.interp2d
x
e
y
a una función de interpolación como
scipy.interpolate.interp2d
por lo que necesita una forma de saber qué coordenadas se eliminaron:
x_new = xx[condition]
y_new = yy[condition]
y aún puede interpolar con las coordenadas "correctas" (pruébelo sin la malla y tendrá un montón de código adicional):
from scipy.interpolate import interp2d
interpolated = interp2(x_new, y_new, z_new)
y el meshgrid original le permite obtener la interpolación en el grid original nuevamente:
interpolated_grid = interpolated(xx, yy)
Estos son solo algunos ejemplos en los que utilicé la
meshgrid
podría haber muchos más.
meshgrid ayuda a crear una cuadrícula rectangular a partir de dos matrices 1-D de todos los pares de puntos de las dos matrices.
x = np.array([0, 1, 2, 3, 4])
y = np.array([0, 1, 2, 3, 4])
Ahora, si ha definido una función f (x, y) y desea aplicar esta función a todas las combinaciones posibles de puntos de las matrices ''x'' e ''y'', puede hacer esto:
f(*np.meshgrid(x, y))
Digamos, si su función solo produce el producto de dos elementos, entonces así es como se puede lograr un producto cartesiano, de manera eficiente para matrices grandes.
Referido desde here