Utilisation de numpy pour construire un tableau de toutes les combinaisons de deux tableaux

j'essaie de parcourir l'espace paramètres d'une fonction à 6 paramètres pour étudier son comportement numérique avant d'essayer de faire quoi que ce soit de complexe avec elle donc je cherche un moyen efficace de le faire.

ma fonction prend en entrée des valeurs float données par un tableau de numpy 6-dim. Ce que j'ai essayé de faire au départ était ceci:

J'ai d'abord créé une fonction qui prend 2 Tableaux et génère un tableau avec toutes les combinaisons de valeurs des deux tableaux

from numpy import *
def comb(a,b):
    c = []
    for i in a:
        for j in b:
            c.append(r_[i,j])
    return c

puis j'ai utilisé reduce() pour appliquer cela à m copies du même tableau:

def combs(a,m):
    return reduce(comb,[a]*m)

et puis j'évalue ma fonction comme ceci:

values = combs(np.arange(0,1,0.1),6)
for val in values:
    print F(val)

ça marche mais c'est trop lent. Je sais que l'espace des paramètres est énorme, mais cela ne devrait pas être si lent. J'ai seulement échantillonné 10 6 (un million) points dans cet exemple et il a fallu plus de 15 secondes juste pour créer le tableau values .

connais-tu un moyen plus efficace de le faire avec numpy?

je peux modifier la façon la fonction F prend ses arguments si c'est nécessaire.

95
demandé sur Fermi paradox 2009-07-30 21:32:27

9 réponses

dans la nouvelle version de numpy (>1.8.x), np.meshgrid fournit une mise en œuvre beaucoup plus rapide:

@pv de la solution

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 utiliser pour être 2D seulement, maintenant il est capable de ND. Dans ce cas, 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]])

Notez que l'ordre de la résultante finale est légèrement différente.

62
répondu CT Zhu 2016-02-24 17:14:46

Voici une implémentation pure et simple. C'est ca. 5× plus rapide que l'utilisation d'itértools.


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
143
répondu pv. 2009-08-05 19:48:42

itertools.combinaisons est en général le moyen le plus rapide pour obtenir des combinaisons à partir d'un conteneur Python (Si vous voulez en fait des combinaisons, c'est-à-dire des arrangements sans répétitions et indépendamment de l'ordre; ce n'est pas ce que votre code semble faire, mais je ne peux pas dire si c'est parce que votre code est bogué ou parce que vous utilisez la mauvaise terminologie).

si vous voulez quelque chose de différent que des combinaisons peut-être d'autres itérateurs dans itertools, product ou permutations , pourrait mieux vous servir. Par exemple, il semble que votre code soit à peu près le même que:

for val in itertools.product(np.arange(0, 1, 0.1), repeat=6):
    print F(val)

tous ces itérateurs produisent des tuples, pas des listes ou des tableaux numpy, donc si votre F est difficile à obtenir spécifiquement un tableau numpy, vous devrez accepter la charge supplémentaire de construire ou de nettoyer et remplir un à chaque étape.

28
répondu Alex Martelli 2009-07-30 19:19:11

numpy mise en œuvre doit être env. 2x la vitesse de la réponse donnée:

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
répondu Stefan van der Walt 2014-09-03 23:22:41

il semble que vous vouliez une grille pour évaluer votre fonction, dans ce cas vous pouvez utiliser numpy.ogrid (ouvert) ou numpy.mgrid (étoffé):

import numpy
my_grid = numpy.mgrid[[slice(0,1,0.1)]*6]
6
répondu steabert 2011-09-20 07:37:36

Vous pouvez faire quelque chose comme cela

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

qui donne

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
répondu felippe 2014-08-23 14:38:44

Voici encore une autre façon, en utilisant pur NumPy, pas de récursion, pas de compréhension de liste, et pas d'explicite pour les boucles. Il est environ 20% plus lent que la réponse originale, et il est basé sur 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

par exemple,

x = np.arange(3)
a = cartesian(x, x, x, x, x)
print(a)

donne

[[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
répondu étale-cohomology 2016-01-21 18:33:29

vous pouvez utiliser np.array(itertools.product(a, b))

1
répondu William Song 2018-04-17 05:58:27

pour une pure implémentation numpy du produit cartésien des tableaux 1D (ou des listes Python plates), il suffit d'utiliser meshgrid() , de rouler les axes avec transpose() , et de remodeler vers l'ouput désiré:

 def cartprod(*arrays):
     N = len(arrays)
     return transpose(meshgrid(*arrays, indexing='ij'), 
                      roll(arange(N + 1), -1)).reshape(-1, N)

Note Ceci a la convention du dernier axe changeant le plus rapidement ("style C" ou "ligne-majeur").

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

si vous voulez changer le premier axe le plus rapide ("FORTRAN style" ou "colonne-major"), il suffit de changer le order paramètre de reshape() comme ceci: reshape((-1, N), order='F')

0
répondu RBF06 2018-03-02 14:28:12