Wie kann ein numpy-Array mit den Werten einer Funktion erstellt werden, die in einem n-dimensionalen Punktegitter ausgewertet wird?
Nehmen wir beispielsweise an, ich möchte die durch ausgewertete Funktion auswerten
def func(x, y):
return <some function of x and y>
Angenommen, ich möchte es auf einem zweidimensionalen Feld von Punkten auswerten, wobei die x-Werte in zehn Schritten von 0 bis 4 und die y-Werte in zwanzig Schritten von -1 bis 1 gehen. Was ist ein guter Weg, um dies in numpy zu tun?
P.S. Dies wurde in StackOverflow oft in verschiedenen Formen gefragt, aber ich konnte keine prägnante Frage und Antwort finden. Ich habe dies veröffentlicht, um eine prägnante einfache Lösung bereitzustellen (unten).
kürzere, schnellere und klarere Antwort, Vermeidung von Netzgittern:
import numpy as np
def func(x, y):
return np.sin(y * x)
xaxis = np.linspace(0, 4, 10)
yaxis = np.linspace(-1, 1, 20)
result = func(xaxis[:,None], yaxis[None,:])
Dies ist schneller im Speicher, wenn Sie etwas wie x ^ 2 + y als Funktion erhalten, da x ^ 2 auf einem 1D-Array (und nicht auf einem 2D-Array) ausgeführt wird und die Vergrößerung nur dann erfolgt, wenn Sie das " + ". Für ein Netzgitter wird x ^ 2 auf einem 2D-Array durchgeführt, in dem im Wesentlichen jede Zeile gleich ist, was massive Zeitsteigerungen verursacht.
Bearbeiten: Das "x [:, None]" macht x zu einem 2D-Array, jedoch mit einer leeren zweiten Dimension. Dieses "None" ist dasselbe wie das Verwenden von "x [:, numpy.newaxis]". Das Gleiche geschieht mit Y, jedoch mit einer leeren ersten Dimension.
Editieren: in 3 Dimensionen:
def func2(x, y, z):
return np.sin(y * x)+z
xaxis = np.linspace(0, 4, 10)
yaxis = np.linspace(-1, 1, 20)
zaxis = np.linspace(0, 1, 20)
result2 = func2(xaxis[:,None,None], yaxis[None,:,None],zaxis[None,None,:])
Auf diese Weise können Sie problemlos auf n Dimensionen erweitern, indem Sie so viele None
oder :
verwenden, wie Sie Dimensionen haben. Jeder :
macht eine Dimension und jede None
eine "leere" Dimension. Das nächste Beispiel zeigt etwas mehr, wie diese leeren Dimensionen funktionieren. Wie Sie sehen, ändert sich die Form, wenn Sie None
verwenden. Dies zeigt, dass es sich im nächsten Beispiel um ein 3D-Objekt handelt. Die leeren Bemaßungen werden jedoch immer gefüllt, wenn Sie sich mit einem Objekt multiplizieren, das tatsächlich etwas in diesen Dimensionen hat. aber das nächste Beispiel zeigt, was ich meine)
In [1]: import numpy
In [2]: a = numpy.linspace(-1,1,20)
In [3]: a.shape
Out[3]: (20,)
In [4]: a[None,:,None].shape
Out[4]: (1, 20, 1)
In [5]: b = a[None,:,None] # this is a 3D array, but with the first and third dimension being "empty"
In [6]: c = a[:,None,None] # same, but last two dimensions are "empty" here
In [7]: d=b*c
In [8]: d.shape # only the last dimension is "empty" here
Out[8]: (20, 20, 1)
edit: ohne das None selbst eingeben zu müssen
def ndm(*args):
return [x[(None,)*i+(slice(None),)+(None,)*(len(args)-i-1)] for i, x in enumerate(args)]
x2,y2,z2 = ndm(xaxis,yaxis,zaxis)
result3 = func2(x2,y2,z2)
Auf diese Weise erstellen Sie die None
- Slicing, um die zusätzlichen leeren Dimensionen zu erstellen, indem Sie das erste Argument, das Sie angeben, als erste vollständige Dimension angeben, die zweite als zweite vollständige Dimension usw. Es tut das gleiche wie das 'hardcoded'. typisierte Syntax, die zuvor verwendet wurde.
Kurze Erklärung: x2, y2, z2 = ndm(xaxis, yaxis, zaxis)
zu tun ist dasselbe wie zu tun
x2 = xaxis[:,None,None]
y2 = yaxis[None,:,None]
z2 = zaxis[None,None,:]
die ndm-Methode sollte jedoch auch für mehr Dimensionen funktionieren, ohne dass die None
- Slices in mehreren Zeilen, wie eben gezeigt, hartcodiert werden müssen. Dies funktioniert auch in numpy-Versionen vor 1.8, während numpy.meshgrid nur für Dimensionen größer als 2 funktioniert, wenn Sie mindestens 1.8 haben.
import numpy as np
def func(x, y):
return np.sin(y * x)
xaxis = np.linspace(0, 4, 10)
yaxis = np.linspace(-1, 1, 20)
x, y = np.meshgrid(xaxis, yaxis)
result = func(x, y)
Wenn Ihre Funktion tatsächlich ein Tuple von d
-Elementen enthält, dh f((x1,x2,x3,...xd))
(zum Beispiel die Funktion scipy.stats.multivariate_normal ), und Sie f
auf N ^ d Kombinationen/Raster von N Variablen auswerten möchten, könnten Sie dies tun mach auch folgendes (2D-Fall):
x=np.arange(-1,1,0.2) # each variable is instantiated N=10 times
y=np.arange(-1,1,0.2)
Z=f(np.dstack(np.meshgrid(x,y))) # result is an NxN (10x10) matrix, whose entries are f((xi,yj))
Hier erstellt np.dstack(np.meshgrid(x,y))
eine 10 x 10 "Matrix" (technisch ein 10 x 10 x 2-numpy-Array), deren Einträge die von f
auszuwertenden 2-dimensionalen Tupel sind.
Ich verwende diese Funktion, um X-, Y- und Z-Werte für das Plotten zu erhalten:
def npmap2d(fun, x_spec, y_spec, doPrint=False):
xs = np.linspace(*x_spec)
ys = np.linspace(*y_spec)
Z = np.empty(len(xs) * len(ys))
i = 0
for y in ys:
for x in xs:
Z[i] = fun(x, y)
if doPrint: print([i, x, y, Z[i]])
i += 1
X, Y = np.meshgrid(xs, ys)
Z.shape = X.shape
return X, Y, Z
Verwendungszweck:
def f(x, y):
# ...some function that can't handle numpy arrays
X, Y, Z = npmap2d(f, (0, 0.5, 21), (0.6, 0.4, 41))
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_wireframe(X, Y, Z)
Das gleiche Ergebnis kann mit map erreicht werden:
xs = np.linspace(0, 4, 10)
ys = np.linspace(-1, 1, 20)
X, Y = np.meshgrid(xs, ys)
Z = np.fromiter(map(f, X.ravel(), Y.ravel()), X.dtype).reshape(X.shape)
Meine zwei Cent:
import numpy as np
x = np.linspace(0, 4, 10)
y = np.linspace(-1, 1, 20)
[X, Y] = np.meshgrid(x, y, indexing = 'ij', sparse = 'true')
def func(x, y):
return x*y/(x**2 + y**2 + 4)
# I have defined a function of x and y.
func(X, Y)