Question

J'essaie de parcourir l'espace de paramètres d'une fonction à 6 paramètres pour étudier son comportement numérique avant d'essayer de faire quelque chose de complexe avec elle. Je recherche donc un moyen efficace de le faire.

Ma fonction prend des valeurs float pour un tableau numpy de 6 dim en entrée. Au départ, j’ai essayé de faire 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

Ensuite, j'ai utilisé reduction () pour l'appliquer à 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)

Cela fonctionne mais c’est trop lent. Je sais que l'espace des paramètres est énorme, mais cela ne devrait pas être si lent. Dans cet exemple, je n’ai échantillonné que 10 6 (un million) de points et il a fallu plus de 15 secondes pour créer le tableau valeurs .

Connaissez-vous un moyen plus efficace de le faire avec numpy?

Je peux modifier la façon dont la fonction F utilise ses arguments si nécessaire.

Était-ce utile?

La solution

Dans les versions plus récentes de numpy (> 1.8.x), numpy.meshgrid () fournit une implémentation beaucoup plus rapide:

La solution de @ pv

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 () utilisé 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érent.

Autres conseils

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

itertools.combinations est en général le moyen le plus rapide d'obtenir des combinaisons. depuis un conteneur Python (si vous voulez réellement des combinaisons, c'est-à-dire des arrangements SANS répétition et indépendants 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 ' en utilisant la mauvaise terminologie).

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

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

Tous ces itérateurs génèrent des n-uplets et non des listes ou des tableaux numpy. Par conséquent, si votre F est difficile à obtenir un tableau numpy spécifique, vous devrez accepter la surcharge supplémentaire que représente la construction ou la suppression et le remplissage à chaque étape.

L'implémentation numpy suivante devrait être d'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

Il semble que vous souhaitiez une grille pour évaluer votre fonction. Dans ce cas, vous pouvez utiliser numpy.ogrid (open) ou numpy.mgrid (étoffé):

import numpy
my_grid = numpy.mgrid[[slice(0,1,0.1)]*6]

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

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

Voici encore un autre moyen d’utiliser NumPy pur, aucune récursion, aucune compréhension de la liste et aucune boucle for explicite. Elle est environ 20% plus lente que la réponse initiale et est basée 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]]

Pour une implémentation purement numérique du produit cartésien de tableaux 1D (ou de listes python plates), utilisez simplement meshgrid () , faites rouler les axes avec transpose () et remodeler à la sortie désirée:

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

Notez que cela a la convention du dernier axe qui change 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 souhaitez modifier l’axe premier le plus rapidement ("Style FORTRAN" ou "Colonne-majeur"), il vous suffit de modifier le paramètre order de . reshape () comme ceci: reshape ((- 1, N), commande = 'F')

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top