W rzeczywistości cel np.meshgrid
jest już wspomniany w dokumentacji:
np.meshgrid
Zwraca macierze współrzędnych z wektorów współrzędnych.
Twórz tablice współrzędnych ND dla wektoryzowanych ocen pól skalarnych / wektorowych ND na siatkach ND, biorąc pod uwagę jednowymiarowe tablice współrzędnych x1, x2, ..., xn.
Więc głównym celem jest utworzenie macierzy współrzędnych.
Prawdopodobnie po prostu zadałeś sobie pytanie:
Dlaczego musimy tworzyć macierze współrzędnych?
Powodem, dla którego potrzebujesz macierzy współrzędnych w Pythonie / NumPy, jest brak bezpośredniej relacji między współrzędnymi a wartościami, z wyjątkiem sytuacji, gdy współrzędne zaczynają się od zera i są czysto dodatnimi liczbami całkowitymi. Następnie możesz po prostu użyć indeksów tablicy jako indeksu. Jednak gdy tak nie jest, musisz jakoś przechowywać współrzędne obok swoich danych. Tam właśnie wchodzą siatki.
Załóżmy, że Twoje dane to:
1 2 1
2 5 2
1 2 1
Każda wartość reprezentuje jednak obszar o szerokości 2 kilometrów w poziomie i 3 kilometry w pionie. Załóżmy, że twoim początkiem jest lewy górny róg i potrzebujesz tablic reprezentujących odległość, którą możesz użyć:
import numpy as np
h, v = np.meshgrid(np.arange(3)*3, np.arange(3)*2)
gdzie v jest:
array([[0, 0, 0],
[2, 2, 2],
[4, 4, 4]])
i H:
array([[0, 3, 6],
[0, 3, 6],
[0, 3, 6]])
Więc jeśli masz dwa wskaźniki, powiedzmy x
i y
(dlatego wartość zwracana meshgrid
jest zwykle xx
lub xs
zamiast x
w tym przypadku wybrałem h
poziomo!), Możesz uzyskać współrzędną x punktu, współrzędną y punktu i wartość w tym punkcie przy użyciu:
h[x, y] # horizontal coordinate
v[x, y] # vertical coordinate
data[x, y] # value
To znacznie ułatwia śledzenie współrzędnych i (co ważniejsze) można przekazać je do funkcji, które muszą znać współrzędne.
Nieco dłuższe wyjaśnienie
Jednak np.meshgrid
sam nie jest często używany bezpośrednio, głównie używa się jednego z podobnych obiektów np.mgrid
lub np.ogrid
. Tutaj np.mgrid
reprezentuje sparse=False
i np.ogrid
ten sparse=True
przypadek (odnoszę się do sparse
argumentu np.meshgrid
). Należy pamiętać, że istnieje znaczna różnica między
np.meshgrid
i np.ogrid
a np.mgrid
: Pierwsze dwie zwracane wartości (jeśli są dwa lub więcej) są odwrócone. Często nie ma to znaczenia, ale powinieneś podać sensowne nazwy zmiennych w zależności od kontekstu.
Na przykład w przypadku siatki 2D matplotlib.pyplot.imshow
sensowne jest nazwanie pierwszego zwróconego elementu np.meshgrid
x
i drugiego, y
gdy jest odwrotnie dla np.mgrid
i np.ogrid
.
np.ogrid
i rzadkie siatki
>>> 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]])
Jak już powiedziano, wynik jest odwrócony w porównaniu do np.meshgrid
, dlatego rozpakowałem go jako yy, xx
zamiast 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]])
To już wygląda jak współrzędne, szczególnie linie xiy dla wykresów 2D.
Wizualizowane:
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
i gęste / rozwinięte siatki
>>> 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]])
To samo dotyczy tutaj: Wyjście jest odwrócone w porównaniu do 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]])
W przeciwieństwie do ogrid
tych tablic zawierają wszystkie xx
i yy
współrzędne w -5 <= xx <= 5; -5 <= yy <= 5 siatki.
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")
Funkcjonalność
Te funkcje nie ograniczają się tylko do 2D, te funkcje działają dla dowolnych wymiarów (cóż, maksymalna liczba argumentów podanych do działania w Pythonie i maksymalna liczba wymiarów dozwolona przez 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.
Nawet jeśli działają one również dla 1D, istnieją dwie (znacznie bardziej powszechne) funkcje tworzenia siatki 1D:
Oprócz argumentu start
i stop
obsługuje również step
argument (nawet złożone kroki reprezentujące liczbę kroków):
>>> 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.]])
Aplikacje
Pytałeś konkretnie o cel, a te siatki są niezwykle przydatne, jeśli potrzebujesz układu współrzędnych.
Na przykład, jeśli masz funkcję NumPy, która oblicza odległość w dwóch wymiarach:
def distance_2d(x_point, y_point, x, y):
return np.hypot(x-x_point, y-y_point)
I chcesz poznać odległość każdego punktu:
>>> 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]])
Wynik byłby identyczny, gdyby przejść przez gęstą siatkę zamiast otwartej siatki. Nadawanie NumPys umożliwia!
Wizualizujmy wynik:
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()
I to również wtedy, gdy NumPys mgrid
i ogrid
stają się bardzo wygodne, ponieważ pozwala łatwo zmienić rozdzielczość twoich siatek:
ys, xs = np.ogrid[-5:5:200j, -5:5:200j]
# otherwise same code as above
Ponieważ jednak imshow
nie obsługuje x
i y
danych wejściowych, należy ręcznie zmieniać tiki. Byłoby naprawdę wygodne, gdyby zaakceptował współrzędne x
i y
, prawda?
NumPy ułatwia pisanie funkcji, które w naturalny sposób radzą sobie z siatkami. Ponadto istnieje kilka funkcji w NumPy, SciPy, matplotlib, które oczekują przejścia w siatce.
Lubię obrazy, więc zbadajmy 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)
Zwróć uwagę, jak współrzędne są już poprawnie ustawione! Nie byłoby tak, gdybyś właśnie zdał density
.
Albo dać inny przykład zabawy przy użyciu modeli astropy (tym razem nie obchodzi wiele o współrzędnych, po prostu wykorzystać je do stworzenia jakiegoś siatkę):
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)
Chociaż jest to tylko „wygląd”, kilka funkcji związanych z modelami funkcjonalnymi i dopasowaniem (na przykład scipy.interpolate.interp2d
,
scipy.interpolate.griddata
nawet pokazuj przykłady użycia np.mgrid
) w Scipy itp. Wymaga siatek. Większość z nich działa z otwartymi i gęstymi siatkami, jednak niektóre działają tylko z jedną z nich.
xx
iyy
. Tajemnicze dla mnie było to, dlaczego zwraca tę parę wyników i jak wyglądają. Odpowiedź Hai Phana jest do tego przydatna. Myślę, że robi to dla wygody, ponieważ fabuła chce dwóch takich parametrów.