webentwicklung-frage-antwort-db.com.de

numpy - werte die Funktion auf einem Punktegitter aus

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

20
DanielSank

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.

22
usethedeathstar
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)
9
DanielSank

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. 

3
Yibo Yang

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)
2
grabantot

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)
0
omehoque