Disons que j'ai un tableau 2D. Comment puis-je appliquer une fonction à chaque élément du tableau et remplacer cet élément par le retour? De plus, le retour de la fonction sera un tuple, donc le tableau deviendra 3d.
Voici le code en tête.
def filter_func(item):
if 0 <= item < 1:
return (1, 0, 1)
elif 1 <= item < 2:
return (2, 1, 1)
elif 2 <= item < 3:
return (5, 1, 4)
else:
return (4, 4, 4)
myarray = np.array([[2.5, 1.3], [0.4, -1.0]])
# Apply the function to an array
print(myarray)
# Should be array([[[5, 1, 4],
# [2, 1, 1]],
# [[1, 0, 1],
# [4, 4, 4]]])
Des idées comment je pourrais le faire? Une façon est de faire np.array (list (map (filter_func, myarray.reshape ((12,))))). Reshape ((2, 2, 3)) mais c'est assez lent, surtout quand j'ai besoin de le faire sur un tableau de formes (1024, 1024).
J'ai aussi vu des gens utiliser np.vectorize, mais cela finit en quelque sorte par (array ([[ 5, 2], [1, 4]]), tableau ([[1, 1], [0, 4]]), tableau ([[4, 1], [1, 4]])). code> Ensuite, il a la forme (3, 2, 2).
3 Réponses :
vous pouvez utiliser cette fonction, avec l'implémentation vectorisée
myarray = func(myarray)
myarray
>>> [[[5, 1, 4],
[2, 1, 1]],
[[1, 0, 1],
[4, 4, 4]]]
vous ne pourrez pas réécrire votre tableau en raison d'une incompatibilité de forme
mais vous pouvez écraser la variable myarray
def func(arr):
elements = np.array([
[1, 0, 1],
[2, 1, 1],
[5, 1, 4],
[4, 4, 4],
])
arr = arr.astype(int)
mask = (arr != 0) & (arr != 1) & (arr != 2)
arr[mask] = -1
return elements[arr]
Euh, comment le ferais-je si j'avais déjà une fonction? Je ne comprends pas vraiment ce que fait votre code
Pas besoin de changer quoi que ce soit dans votre fonction.
Il suffit d'appliquer la version vectorisée de votre fonction à votre tableau et empiler le résultat:
array([[[5, 1, 4],
[2, 1, 1]],
[[1, 0, 1],
[4, 4, 4]]])
Le résultat est:
np.stack(np.vectorize(filter_func)(myarray), axis=2)
Ce paramètre spécifie, le long de quel axe l'empilement doit avoir lieu (décrit dans la documentation Numpy ). Essayez mon code avec axis == 1 et 0 pour voir la différence.
Merci! C'est exactement ce dont j'avais besoin. C'est environ 8 fois plus rapide que mon premier essai!
Dans mes horaires, cette vectorisation est plus lente que votre liste (carte ...) . J'ai toujours trouvé que vectorize était plus lent qu'une itération simple.
Votre list-map:
In [32]: timeit func(myarray) 25 µs ± 60.8 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Une variante utilisant la compréhension de liste imbriquée:
In [29]: timeit np.stack(np.frompyfunc(filter_func, 1,3)(myarray),2).astype(int) 28.7 µs ± 125 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Utilisation de vectorize , le résultat est un tableau pour chaque élément renvoyé par la fonction.
In [8]: timeit np.array(list(map(filter_func, myarray.reshape((4,))))).reshape((2, 2, 3)) 17.2 µs ± 47.7 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each) In [9]: timeit np.array([[filter_func(j) for j in row] for row in myarray]) 20.5 µs ± 78.1 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each) In [10]: timeit np.stack(np.vectorize(filter_func)(myarray),2) 75.2 µs ± 297 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Comme @Vladi le montre, ils peuvent être combinés avec stack (ou np.array suivi d'une transposition):
In [7]: np.stack(np.vectorize(filter_func)(myarray),2)
Out[7]:
array([[[5, 1, 4],
[2, 1, 1]],
[[1, 0, 1],
[4, 4, 4]]])
Votre liste-map est la plus rapide. Je n'ai jamais trouvé vectorize plus rapide:
In [6]: np.vectorize(filter_func)(myarray)
Out[6]:
(array([[5, 2],
[1, 4]]),
array([[1, 1],
[0, 4]]),
array([[4, 1],
[1, 4]]))
En supprimant np.vectorize (filter_func) la boucle de synchronisation aide juste un peu.
frompyfunc est similaire à vectorize , mais renvoie l'objet dtype. C'est généralement plus rapide:
In [5]: np.array([[filter_func(j) for j in row] for row in myarray])
Out[5]:
array([[[5, 1, 4],
[2, 1, 1]],
[[1, 0, 1],
[4, 4, 4]]])
En général, si vous avez une fonction qui ne prend que des entrées scalaires, il est difficile de faire mieux qu'une simple itération. vectorize / frompyfunc ne s’améliore pas. L'utilisation optimale de numpy nécessite de réécrire la fonction pour qu'elle fonctionne directement avec des tableaux, comme le montre @Hammad.
Bien qu'avec ce petit exemple, même ce numpy approprié la solution n'est pas plus rapide. Je pense qu'il évoluera mieux:
In [4]: np.array(list(map(filter_func, myarray.reshape((4,))))).reshape((2, 2, 3))
Out[4]:
array([[[5, 1, 4],
[2, 1, 1]],
[[1, 0, 1],
[4, 4, 4]]])
La carte de liste a pris 6,34 secondes sur un tableau de 1024 x 1024, mais la vectorisation n'a pris que 1,18 secondes. Mabye, le list-map est meilleur pour les petits tableaux.