Ich versuche, den Parameterraum einer 6-Parameter-Funktion zu durchlaufen, um das numerische Verhalten zu untersuchen, bevor ich versuche, irgendetwas Komplexes damit zu machen, also suche ich nach einem effizienten Weg, dies zu tun.
Meine Funktion verwendet Float-Werte bei einem 6-Dim-Numpy-Array als Eingabe. Was ich anfangs versuchte, war Folgendes:
Zuerst habe ich eine Funktion erstellt, die 2 Arrays verwendet und ein Array mit allen Wertekombinationen aus den beiden Arrays generiert
from numpy import *
def comb(a,b):
c = []
for i in a:
for j in b:
c.append(r_[i,j])
return c
Dann habe ich reduce()
verwendet, um dies auf m Kopien desselben Arrays anzuwenden:
def combs(a,m):
return reduce(comb,[a]*m)
Und dann bewerte ich meine Funktion so:
values = combs(np.arange(0,1,0.1),6)
for val in values:
print F(val)
Das funktioniert, aber es ist zu langsam. Ich weiß, der Parameterraum ist riesig, aber das sollte nicht so langsam sein. Ich habe nur 10 probiert6 (eine Million) Punkte in diesem Beispiel und es dauerte mehr als 15 Sekunden, um das Array values
zu erstellen.
Kennen Sie eine effizientere Möglichkeit, dies mit Numpy zu tun?
Ich kann die Art und Weise ändern, in der die Funktion F
ihre Argumente verwendet, falls dies erforderlich ist.
In neueren Versionen von numpy
(> 1.8.x) bietet numpy.meshgrid()
eine viel schnellere Implementierung:
@ pv's Lösung
In [113]:
%timeit cartesian(([1, 2, 3], [4, 5], [6, 7]))
10000 loops, best of 3: 135 µs per loop
In [114]:
cartesian(([1, 2, 3], [4, 5], [6, 7]))
Out[114]:
array([[1, 4, 6],
[1, 4, 7],
[1, 5, 6],
[1, 5, 7],
[2, 4, 6],
[2, 4, 7],
[2, 5, 6],
[2, 5, 7],
[3, 4, 6],
[3, 4, 7],
[3, 5, 6],
[3, 5, 7]])
numpy.meshgrid()
Nur 2D verwenden, jetzt ist ND möglich. In diesem Fall 3D:
In [115]:
%timeit np.array(np.meshgrid([1, 2, 3], [4, 5], [6, 7])).T.reshape(-1,3)
10000 loops, best of 3: 74.1 µs per loop
In [116]:
np.array(np.meshgrid([1, 2, 3], [4, 5], [6, 7])).T.reshape(-1,3)
Out[116]:
array([[1, 4, 6],
[1, 5, 6],
[2, 4, 6],
[2, 5, 6],
[3, 4, 6],
[3, 5, 6],
[1, 4, 7],
[1, 5, 7],
[2, 4, 7],
[2, 5, 7],
[3, 4, 7],
[3, 5, 7]])
Beachten Sie, dass sich die Reihenfolge der Endergebnisse geringfügig unterscheidet.
Hier ist eine reine Numpy-Implementierung. Es ist ca. 5x schneller als mit itertools.
import numpy as np
def cartesian(arrays, out=None):
"""
Generate a cartesian product of input arrays.
Parameters
----------
arrays : list of array-like
1-D arrays to form the cartesian product of.
out : ndarray
Array to place the cartesian product in.
Returns
-------
out : ndarray
2-D array of shape (M, len(arrays)) containing cartesian products
formed of input arrays.
Examples
--------
>>> cartesian(([1, 2, 3], [4, 5], [6, 7]))
array([[1, 4, 6],
[1, 4, 7],
[1, 5, 6],
[1, 5, 7],
[2, 4, 6],
[2, 4, 7],
[2, 5, 6],
[2, 5, 7],
[3, 4, 6],
[3, 4, 7],
[3, 5, 6],
[3, 5, 7]])
"""
arrays = [np.asarray(x) for x in arrays]
dtype = arrays[0].dtype
n = np.prod([x.size for x in arrays])
if out is None:
out = np.zeros([n, len(arrays)], dtype=dtype)
m = n / arrays[0].size
out[:,0] = np.repeat(arrays[0], m)
if arrays[1:]:
cartesian(arrays[1:], out=out[0:m,1:])
for j in xrange(1, arrays[0].size):
out[j*m:(j+1)*m,1:] = out[0:m,1:]
return out
itertools.combinations ist im Allgemeinen der schnellste Weg, um Kombinationen aus einem Python Container zu erhalten (wenn Sie tatsächlich Kombinationen wünschen, dh Arrangements OHNE Wiederholungen und unabhängig von der Reihenfolge ; das ist nicht das, was dein Code zu tun scheint, aber ich kann nicht sagen, ob das daran liegt, dass dein Code fehlerhaft ist oder dass du die falsche Terminologie verwendest).
Wenn Sie etwas anderes als Kombinationen möchten, sind möglicherweise andere Iteratoren in itertools, product
oder permutations
, besser für Sie geeignet. Zum Beispiel sieht es so aus, als ob Ihr Code ungefähr so ist wie:
for val in itertools.product(np.arange(0, 1, 0.1), repeat=6):
print F(val)
Alle diese Iteratoren liefern Tupel, keine Listen oder Numpy-Arrays. Wenn Ihr F also genau ein Numpy-Array haben möchte, müssen Sie den zusätzlichen Aufwand in Kauf nehmen, indem Sie bei jedem Schritt eines erstellen oder löschen und neu füllen.
Die folgende numpy Implementierung sollte ca. sein. 2x die Geschwindigkeit der gegebenen Antwort:
def cartesian2(arrays):
arrays = [np.asarray(a) for a in arrays]
shape = (len(x) for x in arrays)
ix = np.indices(shape, dtype=int)
ix = ix.reshape(len(arrays), -1).T
for n, arr in enumerate(arrays):
ix[:, n] = arrays[n][ix[:, n]]
return ix
Es sieht so aus, als ob Sie möchten, dass ein Raster Ihre Funktion bewertet. In diesem Fall können Sie numpy.ogrid
(offen) oder numpy.mgrid
(konkretisiert):
import numpy
my_grid = numpy.mgrid[[slice(0,1,0.1)]*6]
Sie können so etwas tun
import numpy as np
def cartesian_coord(*arrays):
grid = np.meshgrid(*arrays)
coord_list = [entry.ravel() for entry in grid]
points = np.vstack(coord_list).T
return points
a = np.arange(4) # fake data
print(cartesian_coord(*6*[a])
was gibt
array([[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 2],
...,
[3, 3, 3, 3, 3, 1],
[3, 3, 3, 3, 3, 2],
[3, 3, 3, 3, 3, 3]])
sie können np.array(itertools.product(a, b))
verwenden
Hier ist noch eine andere Möglichkeit: Verwenden Sie reines NumPy, keine Rekursion, kein Listenverständnis und keine expliziten for-Schleifen. Es ist ungefähr 20% langsamer als die ursprüngliche Antwort und basiert auf np.meshgrid.
def cartesian(*arrays):
mesh = np.meshgrid(*arrays) # standard numpy meshgrid
dim = len(mesh) # number of dimensions
elements = mesh[0].size # number of elements, any index will do
flat = np.concatenate(mesh).ravel() # flatten the whole meshgrid
reshape = np.reshape(flat, (dim, elements)).T # reshape and transpose
return reshape
Beispielsweise,
x = np.arange(3)
a = cartesian(x, x, x, x, x)
print(a)
gibt
[[0 0 0 0 0]
[0 0 0 0 1]
[0 0 0 0 2]
...,
[2 2 2 2 0]
[2 2 2 2 1]
[2 2 2 2 2]]
Für eine reine Numpy-Implementierung des kartesischen Produkts von 1D-Arrays (oder flachen python Listen)) verwenden Sie einfach meshgrid()
, rollen Sie die Achsen mit transpose()
und auf die gewünschte Ausgabe umformen:
def cartprod(*arrays):
N = len(arrays)
return transpose(meshgrid(*arrays, indexing='ij'),
roll(arange(N + 1), -1)).reshape(-1, N)
Beachten Sie, dass die Konvention lautet, dass sich die letzte Achse am schnellsten ändert ("C-Stil" oder "Zeilensprung").
In [88]: cartprod([1,2,3], [4,8], [100, 200, 300, 400], [-5, -4])
Out[88]:
array([[ 1, 4, 100, -5],
[ 1, 4, 100, -4],
[ 1, 4, 200, -5],
[ 1, 4, 200, -4],
[ 1, 4, 300, -5],
[ 1, 4, 300, -4],
[ 1, 4, 400, -5],
[ 1, 4, 400, -4],
[ 1, 8, 100, -5],
[ 1, 8, 100, -4],
[ 1, 8, 200, -5],
[ 1, 8, 200, -4],
[ 1, 8, 300, -5],
[ 1, 8, 300, -4],
[ 1, 8, 400, -5],
[ 1, 8, 400, -4],
[ 2, 4, 100, -5],
[ 2, 4, 100, -4],
[ 2, 4, 200, -5],
[ 2, 4, 200, -4],
[ 2, 4, 300, -5],
[ 2, 4, 300, -4],
[ 2, 4, 400, -5],
[ 2, 4, 400, -4],
[ 2, 8, 100, -5],
[ 2, 8, 100, -4],
[ 2, 8, 200, -5],
[ 2, 8, 200, -4],
[ 2, 8, 300, -5],
[ 2, 8, 300, -4],
[ 2, 8, 400, -5],
[ 2, 8, 400, -4],
[ 3, 4, 100, -5],
[ 3, 4, 100, -4],
[ 3, 4, 200, -5],
[ 3, 4, 200, -4],
[ 3, 4, 300, -5],
[ 3, 4, 300, -4],
[ 3, 4, 400, -5],
[ 3, 4, 400, -4],
[ 3, 8, 100, -5],
[ 3, 8, 100, -4],
[ 3, 8, 200, -5],
[ 3, 8, 200, -4],
[ 3, 8, 300, -5],
[ 3, 8, 300, -4],
[ 3, 8, 400, -5],
[ 3, 8, 400, -4]])
Wenn Sie die erste - Achse am schnellsten ändern möchten ("FORTRAN style" oder "column-major"), ändern Sie einfach den order
-Parameter von reshape()
wie folgt: reshape((-1, N), order='F')