2013-09-04 4 views
6

Lors de l'écriture d'un script, j'ai découvert la fonction numpy.random.choice. Je l'ai implémenté car il était beaucoup plus propre que l'instruction if. Cependant, après l'exécution du script, je me suis rendu compte qu'il est significativement plus lent que l'instruction if.Pourquoi random.choice est-il si lent?

Ce qui suit est un MWE. La première méthode prend 0.0 s, tandis que la seconde prend 7.2 s. Si vous agrandissez la boucle i, vous verrez à quelle vitesse random.choice ralentit.

Quelqu'un peut-il commenter pourquoi random.choice est tellement plus lent?

import numpy as np 
import numpy.random as rand 
import time as tm 

#------------------------------------------------------------------------------- 

tStart = tm.time() 
for i in xrange(100): 
    for j in xrange(1000): 
     tmp = rand.rand() 
     if tmp < 0.25: 
      var = 1 
     elif tmp < 0.5: 
      var = -1 
print('Time: %.1f s' %(tm.time() - tStart)) 

#------------------------------------------------------------------------------- 

tStart = tm.time() 
for i in xrange(100): 
    for j in xrange(1000): 
     var = rand.choice([-1, 0, 1], p = [0.25, 0.5, 0.25]) 
print('Time: %.1f s' %(tm.time() - tStart)) 
+3

Ce n'est pas vraiment une comparaison équitable. Chaque fois, numpy doit prendre la somme cumulative de la liste p, la mettre dans un nouveau vecteur, puis itérer dessus. Vous faites effectivement le prétraitement en sachant qu'il n'y a que trois variables, et que la somme du premier et du troisième est 0,5. Au-delà de cela, comme indiqué ci-dessous, numpy est optimisé pour les opérations vectorisées, pas pour faire une seule opération simple des milliers de fois. –

+1

Utilisez également 'timeit', pas' time' tout seul. – Marcin

Répondre

12

Vous l'utilisez incorrectement. Vectoriser l'opération ou numpy offrira aucun avantage:

var = numpy.random.choice([-1, 0, 1], size=1000, p=[0.25, 0.5, 0.25]) 

données de synchronisation:

>>> timeit.timeit('''numpy.random.choice([-1, 0, 1], 
...          size=1000, 
...          p=[0.25, 0.5, 0.25])''', 
...    'import numpy', number=10000) 
2.380380242513752 

>>> timeit.timeit(''' 
... var = [] 
... for i in xrange(1000): 
...  tmp = rand.rand() 
...  if tmp < 0.25: 
...   var.append(1) 
...  elif tmp < 0.5: 
...   var.append(-1) 
...  else: 
...   var.append(0)''', 
... setup='import numpy.random as rand', number=10000) 
5.673041396894519 
+2

+1 Ceci est environ 7 fois plus rapide que la première boucle. –

+0

Comme écrit, comparez-vous les pommes aux pommes? Le premier calcule 10^3 * 10^4 = 10^7 nombres aléatoires, mais le second calcule 10^2 * 10^3 * 10^4 = 10^9 nombres aléatoires, non? – DSM

+0

@DSM: Oups. Copié la mauvaise chose au temps. Correction ... – user2357112

1

Je soupçonne que la généralité de np.random.choice ralentit vers le bas, plus pour les petits échantillons que les grandes.

Un vectorisation brut de la version if est:

def foo(n): 
    x = np.random.rand(n) 
    var = np.zeros(n) 
    var[x<.25] = -1 
    var[x>.75] = 1 
    return var 

Rodage ipython je reçois:

timeit np.random.choice([-1,0,1],size=1000,p=[.25,.5,.25]) 
1000 loops, best of 3: 293 us per loop 

timeit foo(1000) 
10000 loops, best of 3: 83.4 us per loop 

timeit np.random.choice([-1,0,1],size=100000,p=[.25,.5,.25]) 
100 loops, best of 3: 11 ms per loop 

timeit foo(100000) 
100 loops, best of 3: 8.12 ms per loop 

Donc, pour la taille 1000, choice est 3-4x plus lent, mais avec des vecteurs plus grands , la différence commence à disparaître.

Questions connexes