Saltar al contenido

¿Cuál es el propósito de meshgrid en Python / NumPy?

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")

ingrese la descripción de la imagen aquí

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")

ingrese la descripción de la imagen aquí

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”.

ingrese la descripción de la imagen aquí

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:

ingrese la descripción de la imagen aquí

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")

ingrese la descripción de la imagen aquí

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")

ingrese la descripción de la imagen aquí

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()

ingrese la descripción de la imagen aquí

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

ingrese la descripción de la imagen aquí

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)

ingrese la descripción de la imagen aquí

¡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)
    

ingrese la descripción de la imagen aquí

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.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *