wake-up-neo.com

Verwenden Sie numpy, um ein Array aller Kombinationen von zwei Arrays zu erstellen

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.

122

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.

92
CT Zhu

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
150
pv.

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.

32
Alex Martelli

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
8

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]
7
steabert

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]])
6
felippe

sie können np.array(itertools.product(a, b)) verwenden

4
William Song

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]]
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')

1
RBF06