Mit numpy zu bauen, die ein array aller Kombinationen von zwei arrays
-
05-07-2019 - |
Frage
Ich versuche zu laufen und lief über die Parameter Raum, der von einer 6-parameter-Funktion, um es zu studieren die numerische Verhalten, bevor Sie versuchen, etwas zu tun-Komplex, so dass ich bin auf der Suche nach einem effizienten Weg, dies zu tun.
Meine Funktion nimmt float-Werte bei einer 6-dim-numpy-array als Eingabe.Was ich versuchte zu tun, war zunächst dies:
Zuerst erstellte ich eine Funktion, die 2 arrays und erstellen Sie ein array mit allen Kombinationen von Werten aus den beiden arrays
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 verwendet reduce()
zu gelten, dass m Kopien des gleichen array:
def combs(a,m):
return reduce(comb,[a]*m)
Und dann bewerte ich meine Funktion wie folgt:
values = combs(np.arange(0,1,0.1),6)
for val in values:
print F(val)
Dies funktioniert, aber es ist viiiiel zu langsam.Ich weiß, der Raum der Parameter ist riesig, aber dies sollte nicht so langsam.Ich habe Sie nur abgetastet 106 (eine Millionen) Punkte in diesem Beispiel, und es dauerte mehr als 15 Sekunden lang, um das array erstellen values
.
Kennen Sie eine effizientere Möglichkeit, dies zu tun mit numpy?
Kann ich ändern die Art und Weise der Funktion F
braucht es Argumente, wenn es notwendig ist.
Lösung
In neuerer Version von numpy
(> 1.8.x), numpy.meshgrid()
bietet eine viel schnellere Implementierung:
@ pv-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()
2D verwenden nur zu sein, jetzt es ist in der Lage ND. 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 die Reihenfolge der endgültigen resultierend etwas anders.
Andere Tipps
Hier ist eine rein numpy Umsetzung. Es ist ca. 5 x schneller als itertools verwendet wird.
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 in der Regel der schnellste Weg, Kombinationen zu erhalten aus einem Python-Containern (wenn Sie tatsächlich tun wollen Kombinationen, dh Vereinbarungen ohne Wiederholungen und unabhängig von Ordnung, das ist nicht das, was Ihr Code zu tun scheint, aber ich kann nicht sagen, ob das ist, weil Ihr Code Buggy oder weil Sie ist‘ re die falsche Terminologie).
Wenn Sie etwas wollen, anders als Kombinationen vielleicht andere Iteratoren in itertools, product
oder permutations
, könnten Sie besser dienen. Zum Beispiel, es sieht aus wie der Code in etwa die gleiche wie:
for val in itertools.product(np.arange(0, 1, 0.1), repeat=6):
print F(val)
Alle diese Iteratoren Tupeln ergeben, keine Listen oder numpy Arrays, also, wenn Ihr F pingelig ist über speziell ein numpy Array erhalten werden Sie den zusätzlichen Aufwand für den Bau oder das Clearing und die Wieder Füllen eines an jedem Schritt in Kauf nehmen.
Die folgende numpy Implementierung sollte ca. 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 aus wie Sie möchten, ein raster für die Bewertung Ihrer Funktion, in diesem Fall können Sie Sie nutzen numpy.ogrid
(open) oder numpy.mgrid
(ausgearbeitet):
import numpy
my_grid = numpy.mgrid[[slice(0,1,0.1)]*6]
Sie können 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])
das 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 verwenden können np.array(itertools.product(a, b))
Hier ist noch eine weitere Möglichkeit, mit reinem NumPy, keine Rekursion, keine Liste Verständnis und keine explizite für Schleifen. Es ist etwa 20% langsamer als die ursprüngliche Antwort, und es 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
Beispiel:
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 Umsetzung kartesischen Produkts von 1D-Arrays (oder flacher Python-Listen), nur meshgrid()
verwenden rollt die Achsen mit transpose()
und Umformen auf die gewünschte ouput:
def cartprod(*arrays):
N = len(arrays)
return transpose(meshgrid(*arrays, indexing='ij'),
roll(arange(N + 1), -1)).reshape(-1, N)
Hinweis: Dies ist die Konvention der letzten Achse schnellsten ( "C-Stil" oder "row-major") zu ändern.
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 ändern möchten die erste Achse am schnellsten ( "Fortran-Stil" oder "column-major"), sondern nur die order
Parameter reshape()
ändern wie folgt aus: reshape((-1, N), order='F')