2017-10-21 35 views
2

En ce qui concerne Why NumPy instead of Python lists?Pourquoi utiliser numpy sur la liste en fonction de la vitesse?

tom10 dit:

Vitesse: Voici un test à faire une somme sur une liste et un tableau numpy, montrant que la somme sur le tableau NumPy est 10 fois plus rapide (dans ce test - le kilométrage peut varier).

Mais mon test en utilisant le code suivant:

import numpy as np 
import time as time 

N = 100000 

#using numpy 
start = time.time() 
array = np.array([]) 

for i in range(N): 
    array = np.append(array, i) 

end = time.time() 
print ("Using numpy: ", round(end-start, 2), end="\n") 

#using list 
start = time.time() 
list = [] 

for i in range(N): 
    list.append(i) 

list = np.array(list) 
end = time.time() 
print ("Using list : ", round(end-start, 2), end="\n") 

Donnez le résultat:

Using numpy: 8.35 
Using list : 0.02 

Il est vrai que lorsque vous utilisez "append", la liste est mieux que numpy?

+3

Oui, '.append' est un temps constant constant (amorti) avec des objets' list', avec des objets 'numpy.ndarray' il est linéaire ti.e –

+0

Y at-il un moyen de faire' .append' à 'numpy .ndarray' de la même manière que 'list'? –

+0

Les tableaux Numpy sont conçus pour contenir des matrices potentiellement multidimensionnelles où l'ajout ne peut pas être aussi efficace que dans le cas simple 1D. Généralement dans le code, vous verriez des appels np.zeros (forme) en allouant suffisamment d'éléments à l'avance, où vous connaissez déjà la taille de vos données. Si vous avez besoin d'ajouter trop souvent, vous devriez probablement vous en tenir aux listes intégrées. – lomereiter

Répondre

1

Pour répondre à votre question, oui. L'ajout à un tableau est une opération coûteuse, tandis que les listes le rendent relativement bon marché (voir Internals of Python list, access and resizing runtimes pour la raison). Cependant, ce n'est pas une raison pour abandonner numpy. Il existe d'autres façons d'ajouter facilement des données à un tableau numérique.

Il existe des moyens surprenants (pour moi, de toute façon) de le faire. Passer en bas pour voir les repères pour chacun d'entre eux.

Probablement est tout simplement à preallocate le plus commun du tableau, et l'indice dans ce,

#using preallocated numpy 
start = time.time() 
array = np.zeros(N) 

for i in range(N): 
    array[i] = i 

end = time.time() 
print ("Using preallocated numpy: ", round(end-start, 5), end="\n") 

Bien sûr, vous pouvez preallocate la mémoire pour une liste aussi, donc permet d'inclure que pour une comparaison de référence .

#using preallocated list 
start = time.time() 
res = [None]*N 

for i in range(N): 
    res[i] = i 

res = np.array(res) 
end = time.time() 
print ("Using preallocated list : ", round(end-start, 5), end="\n") 

En fonction de votre code, il peut également être utile d'utiliser la fonction de numpy fromiter, qui utilise les résultats d'une iterator pour initialiser le tableau.

#using numpy fromiter shortcut 
start = time.time() 

res = np.fromiter(range(N), dtype='float64') # Use same dtype as other tests 

end = time.time() 
print ("Using fromiter : ", round(end-start, 5), end="\n") 

Bien sûr, en utilisant un construit en iterator est pas très souple pour nous allons essayer un itérateur personnalisé ainsi,

#using custom iterator 
start = time.time() 
def it(N): 
    i = 0 
    while i < N: 
     yield i 
     i += 1 

res = np.fromiter(it(N), dtype='float64') # Use same dtype as other tests 

end = time.time() 
print ("Using custom iterator : ", round(end-start, 5), end="\n") 

Voilà deux façons très souples d'utilisation numpy. Le premier, utilisant un tableau pré-alloué, est le plus flexible. Voyons voir comment ils se comparent:

Using numpy: 2.40017 
Using list : 0.0164 
Using preallocated numpy: 0.01604 
Using preallocated list : 0.01322 
Using fromiter : 0.00577 
Using custom iterator : 0.01458 

Juste à côté, vous pouvez voir que Préallocation fait numpy beaucoup plus rapide que d'utiliser des listes, bien que la liste préallocation apporte à la fois à la même vitesse. L'utilisation d'un itérateur intégré est extrêmement rapide, bien que les performances de l'itérateur retombent dans la plage de la matrice préallouée et se lisent lorsqu'un itérateur personnalisé est utilisé.

La conversion du code directement au numpy a souvent des performances médiocres, comme avec append. Trouver une approche en utilisant les méthodes de numpy peut presque toujours donner une amélioration substantielle. Dans ce cas, préallouer le tableau ou exprimer le calcul de chaque élément comme un itérateur pour obtenir des performances similaires aux listes python. Ou utilisez une liste de vanille python puisque la performance est à peu près la même.

EDIT: La réponse originale est également np.fromfunction.Cela a été supprimé car il ne correspondait pas au cas d'utilisation d'un élément à la fois, fromfunction initialise réellement le tableau et utilise la diffusion de numpy pour effectuer un seul appel de fonction. Il est environ cent fois plus rapide, donc si vous pouvez résoudre votre problème en utilisant la radiodiffusion ne vous embêtez pas avec ces autres méthodes.

+0

'fromfunction' ne fait pas réellement d'appels de fonction séparés pour chaque entrée; il suppose la fonction broadcasts, et il appelle la fonction une fois avec des tableaux en entrée, s'attendant à ce que la fonction produise un seul tableau qui sera renvoyé directement. (Les docs ne sont pas très clairs à ce sujet, mais au moins ils sont plus clairs en 1.13, ils étaient bien pires.) – user2357112

+0

@ user2357112 Merci, je n'avais pas réalisé que ça fonctionnait. C'est beaucoup moins utile que je pensais. J'ai mis à jour le post. – user2699