Considérant une fonction ci-dessous:
import numpy as np a = np.ones(16).reshape(4,4) def fn(a): b = np.array(a) for i in range(b.shape[0]): for j in range(b.shape[1] - 1): b[i][j+1] += b[i][j] return b print(fn(a))
C'est-à-dire pour une fonction générale qui calcule t + 1
basé sur t
dans un tableau, puis-je rendre cela plus rapide? Je sais qu'il existe un np.vectorize
mais ne semble pas approprié pour ce cas.
3 Réponses :
Vous pouvez utiliser cumsum
Je pense que ce serait utile.
np.cumsum(a,axis=1)
Ou vous pouvez utiliser np.cumsum ()
:
import numpy as np import pandas as pd a = np.ones(16).reshape(4,4) df =pd.DataFrame(a) df.cumsum(axis=1)
Existe-t-il une solution plus générale? Si la fonction ne fait pas de somme cumulative. J'ai trouvé que numba
ressemble à ce que je demande mais je ne l'ai jamais utilisé.
Ce que vous recherchez s'appelle accumulate voici un exemple:
import numpy as np from itertools import accumulate def fn(a): acc = accumulate(a, lambda prev, row: prev + row) return np.array(list(acc)) a = np.arange(16).reshape(4, 4) print(fn(a)) # [[ 0 1 2 3] # [ 4 6 8 10] # [12 15 18 21] # [24 28 32 36]]
Il n'y a pas de fonction d'accumulation optimisée dans numpy car il n'est pas vraiment possible d'écrire accumulate d'une manière à la fois performante et générale. L'implémentation python est générale, mais fonctionnera un peu comme un lok codé à la main.
Pour obtenir des performances optimales, vous aurez probablement besoin de trouver ou d'écrire une implémentation de bas niveau de la fonction d'accumulation spécifique dont vous avez besoin. Vous avez déjà mentionné numba et vous pouvez également vous pencher sur cython.
Il est possible de réduire les deux boucles for
à une boucle for
avec un peu de surcharge de copie en plus.
In [100]: a = np.ones(625).reshape(25, 25) In [101]: %timeit fn(a) 303 µs ± 2.05 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) In [102]: b = a.copy() In [103]: %timeit slightly_vectorized(b) 99.8 µs ± 501 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
Pour que cela fonctionne pour une fonction générique , vous pouvez rechercher une fonction équivalente dans numpy ou en implémenter une en utilisant des opérations numpy (une vectorisée). Pour l'exemple que vous avez fourni, je viens d'utiliser numpy.sum ()
qui fait le travail pour nous.
En termes de performances, cette approche serait bien meilleure que d'utiliser deux pour
boucles au niveau des index, en particulier pour les tableaux plus grands. Dans l'approche que j'ai utilisée ci-dessus, nous travaillons avec des tranches de colonnes.
Voici les minutages qui suggèrent plus de accélération 3X par rapport à l'implémentation native de python.
Python natif:
In [104]: def slightly_vectorized(b): ...: for col in range(b.shape[1]-1): ...: b[:, col+1] = np.sum(a[:, :col+2], axis=1) ...: return b
Légèrement vectorisé:
def fn(a): b = np.array(a) for i in range(b.shape[0]): for j in range(b.shape[1] - 1): b[i][j+1] += b[i][j] return b
In [86]: a Out[86]: array([[1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.], [1., 1., 1., 1.]]) In [87]: b = a.copy() In [88]: for col in range(b.shape[1]-1): ...: b[:, col+1] = np.sum(a[:, :col+2], axis=1) In [89]: b Out[89]: array([[1., 2., 3., 4.], [1., 2., 3., 4.], [1., 2., 3., 4.], [1., 2., 3., 4.]])
Cependant, vous ne pouvez pas toujours trouver une fonction équivalente.
@ knh190 Je suis d'accord! Mais presque toutes les fonctions peuvent être implémentées en utilisant des ufuncs numpy basiques. Vous pouvez ensuite appliquer cette fonction nouvellement implémentée
vectorize
ne promet pas de performances, même là où cela fonctionne.@hpaulj J'ai testé une boucle for sur un tableau 2d, 1. boucle i, j pour les deux dimensions et calcul en place 2. définir une fonction vectorisée et appeler
v_fn (arr)
, la deuxième solution est 2x plus vite.Je faisais spécifiquement référence à la fonction
np.vectorize
, pas au concept général devectorisation
. Clairement, dans ce cas, vous n'avez pas besoin d'itérer sur la dimensioni
. La dimensionj
a besoin de quelque chose comme unufunc.accumulate
, ounumba
pour gagner beaucoup de vitesse.@hpaulj la "fonction vectorisée" est implémentée avec
np.vectorize
. Un exemple stupide estfn = lambda x: x * x
, avecv_fn = np.vectorize (fn)
. C'est en effet plus rapide qu'une boucle intégrée. Mais j'ai votre idée.