171 votes

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

Je suis en train de courir sur les paramètres de l'espace de 6 paramètre de la fonction pour l'étude du comportement numérique avant d'essayer de faire quelque chose de complexe avec elle, donc je suis à la recherche d'un moyen efficace pour ce faire.

Ma fonction prend des valeurs float donné un 6-dim tableau numpy comme entrée. Ce que j'ai essayé de faire au départ était celle-ci:

1) d'Abord, j'ai créé une fonction qui prend 2 tableaux et de générer 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

La j'ai utilisé de la réduire à l'appliquer à m copies de la même matrice:

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

Puis-je évaluer ma fonction comme ceci:

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

Cela fonctionne, mais c'est waaaay trop lent. Je sais que l'espace des paramètres est énorme, mais cela ne devrait pas être si lent. J'ai seulement goûté (10)^6 = un million de points dans cet exemple et il a fallu plus de 15 secondes juste pour créer le tableau des "valeurs".

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

Je peux modifier la façon dont la fonction F prendre c'est des arguments si c'est nécessaire.

166voto

pv. Points 9935

Voici une implémentation purement numérique. C'est ca. 5 × plus rapide que d'utiliser 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
 

37voto

Alex Martelli Points 330805

itertools.combinaisons est en général le moyen le plus rapide pour obtenir des combinaisons à partir d'un Python conteneur (si vous voulez bien de combinaisons, c'est à dire, des arrangements SANS répétition et indépendant de l'ordre; ce n'est pas ce que votre code s'affiche à faire, mais je ne peux pas dire si c'est parce que votre code est buggé ou parce que vous utilisez la mauvaise terminologie).

Si vous voulez quelque chose de différent que les combinaisons peut-être d'autres itérateurs dans itertools, product ou permutations, pourrait vous servir au mieux. Par exemple, il ressemble à votre code est à peu près la même chose que:

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

L'ensemble de ces itérateurs rendement de n-uplets, et non pas des listes ou des tableaux numpy, donc si votre F est pointilleux sur l'obtention spécifiquement un tableau numpy, vous aurez à accepter une charge supplémentaire de la construction ou de compensation et de re-remplissage d'un à chaque étape.

11voto

felippe Points 73

Vous pouvez faire quelque chose comme ça

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

9voto

steabert Points 2537

Il semble que vous souhaitiez 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]
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X