Eigentlich ist der Zweck von np.meshgrid
bereits in der Dokumentation erwähnt:
np.meshgrid
Koordinatenmatrizen von Koordinatenvektoren zurückgeben.
Erstellen Sie ND-Koordinatenarrays für vektorisierte Auswertungen von ND-Skalar- / Vektorfeldern über ND-Gitter mit gegebenen eindimensionalen Koordinatenarrays x1, x2, ..., xn.
Der Hauptzweck besteht also darin, Koordinatenmatrizen zu erstellen.
Sie haben sich wahrscheinlich gerade gefragt:
Warum müssen wir Koordinatenmatrizen erstellen?
Der Grund, warum Sie Koordinatenmatrizen mit Python / NumPy benötigen, ist, dass es keine direkte Beziehung von Koordinaten zu Werten gibt, außer wenn Ihre Koordinaten mit Null beginnen und rein positive ganze Zahlen sind. Dann können Sie einfach die Indizes eines Arrays als Index verwenden. Wenn dies jedoch nicht der Fall ist, müssen Sie die Koordinaten neben Ihren Daten speichern. Hier kommen Gitter ins Spiel.
Angenommen, Ihre Daten sind:
1 2 1
2 5 2
1 2 1
Jeder Wert repräsentiert jedoch einen 2 Kilometer breiten Bereich horizontal und 3 Kilometer vertikal. Angenommen, Ihr Ursprung ist die obere linke Ecke und Sie möchten Arrays, die die Entfernung darstellen, die Sie verwenden könnten:
import numpy as np
h, v = np.meshgrid(np.arange(3)*3, np.arange(3)*2)
wo v ist:
array([[0, 0, 0],
[2, 2, 2],
[4, 4, 4]])
und h:
array([[0, 3, 6],
[0, 3, 6],
[0, 3, 6]])
Wenn Sie also zwei Indizes haben, sagen wir x
und y
(deshalb ist der Rückgabewert von meshgrid
normalerweise xx
oder xs
anstelle von x
in diesem Fall h
horizontal gewählt!), Können Sie die x-Koordinate des Punkts, die y-Koordinate des Punkts und die erhalten Wert an diesem Punkt durch Verwendung von:
h[x, y] # horizontal coordinate
v[x, y] # vertical coordinate
data[x, y] # value
Das macht es viel einfacher, die Koordinaten zu verfolgen, und (was noch wichtiger ist) Sie können sie an Funktionen übergeben, die die Koordinaten kennen müssen.
Eine etwas längere Erklärung
Es wird jedoch np.meshgrid
nicht oft direkt verwendet, meistens verwendet man nur eines von ähnlichen Objekten np.mgrid
oder np.ogrid
. Hier np.mgrid
stellt der sparse=False
und np.ogrid
der sparse=True
Fall dar (ich beziehe mich auf das sparse
Argument von np.meshgrid
). Beachten Sie, dass zwischen np.meshgrid
und np.ogrid
und ein signifikanter Unterschied besteht
np.mgrid
: Die ersten beiden zurückgegebenen Werte (wenn zwei oder mehr vorhanden sind) werden umgekehrt. Oft spielt dies keine Rolle, aber Sie sollten je nach Kontext aussagekräftige Variablennamen angeben.
Zum Beispiel im Fall eines 2D-Gitters und matplotlib.pyplot.imshow
es ist sinnvoll, das erste zurückgegebene Element np.meshgrid
x
und das zweite Element zu benennen, y
während es für np.mgrid
und umgekehrt ist np.ogrid
.
np.ogrid
und spärliche Gitter
>>> 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]])
Wie bereits gesagt, ist die Ausgabe im Vergleich zu umgekehrt np.meshgrid
, deshalb habe ich sie entpackt als yy, xx
statt 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]])
Dies sieht bereits nach Koordinaten aus, insbesondere nach den x- und y-Linien für 2D-Diagramme.
Visualisiert:
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
und dichte / ausgearbeitete Gitter
>>> 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]])
Gleiches gilt hier: Die Ausgabe ist umgekehrt im Vergleich zu 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]])
Im Gegensatz zu ogrid
diesen Arrays enthalten alle xx
und yy
Koordinaten in -5 <= xx <= 5; -5 <= yy <= 5 Gitter.
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")
Funktionalität
Diese Funktionen sind nicht nur auf 2D beschränkt, sondern funktionieren auch für beliebige Dimensionen (es gibt eine maximale Anzahl von Argumenten für die Funktion in Python und eine maximale Anzahl von Dimensionen, die NumPy zulässt):
>>> 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.
Auch wenn diese auch für 1D funktionieren, gibt es zwei (weitaus häufigere) Funktionen zur Erstellung von 1D-Gittern:
Neben dem start
und stop
Argumente unterstützt es auch das step
Argument (auch komplexe Schritte , die die Anzahl der Schritte dar):
>>> 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.]])
Anwendungen
Sie haben speziell nach dem Zweck gefragt, und tatsächlich sind diese Gitter äußerst nützlich, wenn Sie ein Koordinatensystem benötigen.
Zum Beispiel, wenn Sie eine NumPy-Funktion haben, die den Abstand in zwei Dimensionen berechnet:
def distance_2d(x_point, y_point, x, y):
return np.hypot(x-x_point, y-y_point)
Und Sie möchten die Entfernung jedes Punktes wissen:
>>> 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]])
Die Ausgabe wäre identisch, wenn man in einem dichten Gitter anstelle eines offenen Gitters passieren würde. NumPys Broadcasting macht es möglich!
Lassen Sie uns das Ergebnis visualisieren:
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()
Und dies ist auch bei NumPys der Fall mgrid
und ogrid
wird sehr praktisch, da Sie damit die Auflösung Ihrer Gitter einfach ändern können:
ys, xs = np.ogrid[-5:5:200j, -5:5:200j]
# otherwise same code as above
Da dies imshow
jedoch nicht unterstützt x
und y
eingegeben wird, müssen die Ticks von Hand geändert werden. Es wäre wirklich praktisch, wenn es die x
und y
Koordinaten akzeptieren würde , oder?
Mit NumPy ist es einfach, Funktionen zu schreiben, die sich auf natürliche Weise mit Gittern befassen. Darüber hinaus gibt es in NumPy, SciPy und matplotlib mehrere Funktionen, die erwarten, dass Sie das Raster passieren.
Ich mag Bilder, also lasst uns Folgendes erkunden 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)
Beachten Sie, wie die Koordinaten bereits richtig eingestellt sind! Das wäre nicht der Fall, wenn Sie nur in der density
.
Oder um ein weiteres lustiges Beispiel mit Astropiemodellen zu geben (diesmal interessieren mich die Koordinaten nicht sonderlich, ich verwende sie nur, um ein Raster zu erstellen ):
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)
Obwohl das ist nur „für die Looks“ mehr Funktionen im Zusammenhang mit Funktionsmodellen und Einpassen (zum Beispiel scipy.interpolate.interp2d
,
scipy.interpolate.griddata
zeigt auch Beispiele unter Verwendung np.mgrid
) in Scipy usw. erfordern Gitter. Die meisten davon arbeiten mit offenen und dichten Gittern, einige arbeiten jedoch nur mit einem von ihnen.
xx
und nicht erklärtyy
. Der mysteriöse Teil für mich war, warum es diese beiden Ergebnisse zurückgibt und wie sie aussehen. Hai Phans Antwort ist dafür praktisch. Ich denke, das macht es der Einfachheit halber, da die Handlung zwei solche Parameter will.