Na verdade, o objetivo de np.meshgridjá está mencionado na documentação:
np.meshgrid
Retorne matrizes de coordenadas a partir de vetores de coordenadas.
Faça matrizes de coordenadas ND para avaliações vetorizadas de campos escalares / vetor ND sobre grades ND, considerando matrizes de coordenadas unidimensionais x1, x2, ..., xn.
Portanto, seu principal objetivo é criar matrizes de coordenadas.
Você provavelmente só se perguntou:
Por que precisamos criar matrizes de coordenadas?
O motivo pelo qual você precisa de matrizes de coordenadas com Python / NumPy é que não há relação direta entre coordenadas e valores, exceto quando suas coordenadas começam com zero e são inteiros puramente positivos. Então você pode apenas usar os índices de uma matriz como o índice. No entanto, quando esse não for o caso, você precisará armazenar coordenadas de alguma forma com seus dados. É aí que as redes entram.
Suponha que seus dados sejam:
1 2 1
2 5 2
1 2 1
No entanto, cada valor representa uma região de 2 km de largura horizontalmente e 3 km de vertical. Suponha que sua origem seja o canto superior esquerdo e você deseje matrizes que representem a distância que você poderia usar:
import numpy as np
h, v = np.meshgrid(np.arange(3)*3, np.arange(3)*2)
onde v é:
array([[0, 0, 0],
[2, 2, 2],
[4, 4, 4]])
e h:
array([[0, 3, 6],
[0, 3, 6],
[0, 3, 6]])
Portanto, se você tiver dois índices, digamos xe y(é por isso que o valor de retorno meshgridé geralmente xxou em xsvez de x, neste caso, eu escolhi hhorizontalmente!), Então você pode obter a coordenada x do ponto, a coordenada y do ponto e a valor nesse ponto usando:
h[x, y] # horizontal coordinate
v[x, y] # vertical coordinate
data[x, y] # value
Isso facilita muito o controle das coordenadas e (ainda mais importante) você pode transmiti-las para funções que precisam conhecer as coordenadas.
Uma explicação um pouco mais longa
No entanto, np.meshgridele próprio não costuma ser usado diretamente, principalmente se usa apenas objetos semelhantesnp.mgrid ou np.ogrid. Aqui np.mgridrepresenta o sparse=Falsee np.ogrido sparse=Truecaso (refiro-me ao sparseargumento de np.meshgrid). Observe que há uma diferença significativa entre
np.meshgride np.ogride np.mgrid: Os dois primeiros valores retornados (se houver dois ou mais) são revertidos. Geralmente isso não importa, mas você deve fornecer nomes significativos de variáveis, dependendo do contexto.
Por exemplo, no caso de uma grade 2D e matplotlib.pyplot.imshowfaz sentido nomear o primeiro item retornado np.meshgrid xe o segundo yenquanto é o contrário de np.mgride np.ogrid.
np.ogrid e grades esparsas
>>> 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 já foi dito, a saída é revertida quando comparada a np.meshgrid, é por isso que eu a descompactei como em yy, xxvez 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]])
Isso já parece coordenadas, especificamente as linhas x e y para plotagens 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 e grades densas / polidas
>>> 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]])
O mesmo se aplica aqui: A saída é revertida em comparação com 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]])
Diferentemente ogriddessas matrizes, contém todas as coordenadas xxe yy-5 <= xx <= 5; -5 <= aa <= 5 grade.
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")

Funcionalidade
Não se limita apenas ao 2D, essas funções funcionam para dimensões arbitrárias (bem, há um número máximo de argumentos dados para funcionar no Python e um número máximo de dimensões que o 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.
Mesmo que isso também funcione para 1D, existem duas (muito mais comuns) funções de criação de grade 1D:
Além do argumento starte, stopele também suporta o stepargumento (mesmo etapas complexas que representam o número de etapas):
>>> 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.]])
Formulários
Você perguntou especificamente sobre o objetivo e, de fato, essas grades são extremamente úteis se você precisar de um sistema de coordenadas.
Por exemplo, se você tiver uma função NumPy que calcula a distância em duas dimensões:
def distance_2d(x_point, y_point, x, y):
return np.hypot(x-x_point, y-y_point)
E você quer saber a distância de cada ponto:
>>> 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]])
A saída seria idêntica se alguém passasse em uma grade densa em vez de uma grade aberta. A transmissão do NumPys torna isso possível!
Vamos visualizar o 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()

E este é também quando NumPys mgride ogridtornar-se muito conveniente, pois permite que você altere facilmente a resolução de suas grades:
ys, xs = np.ogrid[-5:5:200j, -5:5:200j]
# otherwise same code as above

No entanto, como imshownão suporta xe yinsere, é necessário alterar os ticks manualmente. Seria realmente conveniente se ele aceitasse as coordenadas xe y, certo?
É fácil escrever funções com o NumPy que lidam naturalmente com grades. Além disso, existem várias funções no NumPy, SciPy, matplotlib que esperam que você passe na grade.
Eu gosto de imagens, então vamos explorar 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 como as coordenadas já estão definidas corretamente! Esse não seria o caso se você apenas passasse no density.
Ou, para dar outro exemplo divertido usando modelos de astropia (desta vez não me importo muito com as coordenadas, apenas as uso para criar uma grade):
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)

Embora isso seja apenas "para a aparência", várias funções relacionadas a modelos funcionais e acessórios (por exemplo scipy.interpolate.interp2d,
scipy.interpolate.griddataaté mostram exemplos usando np.mgrid) no Scipy etc. requerem grades. A maioria deles trabalha com grades abertas e grades densas, no entanto, algumas funcionam apenas com uma delas.
xxeyy. A parte misteriosa para mim foi por que ele retorna esse par de resultados e como eles são. A resposta de Hai Phan é útil para isso. Eu acho que faz isso por conveniência, já que o enredo quer dois parâmetros assim.