web-dev-qa-db-fra.com

Créer une grande matrice booléenne aléatoire avec numpy

J'essaie de créer une énorme matrice boolean qui est remplie de façon aléatoire avec True et False avec une probabilité donnée p. Au début, j'ai utilisé ce code:

N = 30000
p = 0.1
np.random.choice(a=[False, True], size=(N, N), p=[p, 1-p])  

Mais malheureusement, cela ne semble pas se terminer pour ce gros N. J'ai donc essayé de le scinder en plusieurs générations: 

N = 30000
p = 0.1
mask = np.empty((N, N))
for i in range (N):
     mask[i] = np.random.choice(a=[False, True], size=N, p=[p, 1-p])            
     if (i % 100 == 0):
          print(i)

Maintenant, il se passe quelque chose d'étrange (du moins sur le périphérique): les premières ~ 1100 lignes sont générées très rapidement - mais après cela, le code devient horriblement lent. Pourquoi cela arrive-t-il? Qu'est-ce qui me manque ici? Existe-t-il de meilleurs moyens de créer une grande matrice comportant des entrées True avec une probabilité p et des entrées False avec une probabilité 1-p?

Edit : Comme beaucoup d’entre vous avez supposé que la RAM poserait un problème: comme l’appareil qui exécutera le code dispose de presque 500 Go de RAM, cela ne posera pas de problème.

15
FlashTek

J'ai donc essayé de le scinder en plusieurs générations:

La façon dont fonctionne np.random.choice consiste tout d'abord à générer un float64 dans [0, 1) pour chaque cellule de vos données, puis à le convertir en un index de votre tableau à l'aide de np.search_sorted. Cette représentation intermédiaire est 8 fois plus grande que le tableau booléen!

Puisque vos données sont booléennes, vous pouvez obtenir un facteur de deux accélération avec

np.random.Rand(N, N) > p

Ce que vous pourriez naturellement utiliser dans votre solution de bouclage

Il semble que np.random.choice pourrait faire quelque chose de tampon ici - vous voudrez peut-être déposer un problème contre numpy.

Une autre option serait d’essayer de générer float32s au lieu de float64s. Je ne sais pas si numpy peut le faire maintenant, mais vous pouvez demander la fonctionnalité.

3
Eric

Une autre possibilité pourrait être de le générer par lot (c’est-à-dire calculer plusieurs sous-tableaux et les empiler à la fin). Mais envisagez de ne pas mettre à jour un tableau (mask) dans une boucle for comme le fait OP. Cela obligerait l'ensemble du tableau à se charger en mémoire principale lors de chaque mise à jour d'indexation.

Par exemple, pour obtenir 30000x30000, créez 9000 100x100 tableaux séparés, mettez à jour chacun de ces tableaux 100x100 dans une boucle for et empilez ces 9000 tableaux dans un tableau géant. Cela n’aurait certainement pas besoin de plus de 4 Go de RAM et serait également très rapide.

Exemple minimal:

In [9]: a
Out[9]: 
array([[0, 1],
       [2, 3]])

In [10]: np.hstack([np.vstack([a]*5)]*5)
Out[10]: 
array([[0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
       [2, 3, 2, 3, 2, 3, 2, 3, 2, 3],
       [0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
       [2, 3, 2, 3, 2, 3, 2, 3, 2, 3],
       [0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
       [2, 3, 2, 3, 2, 3, 2, 3, 2, 3],
       [0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
       [2, 3, 2, 3, 2, 3, 2, 3, 2, 3],
       [0, 1, 0, 1, 0, 1, 0, 1, 0, 1],
       [2, 3, 2, 3, 2, 3, 2, 3, 2, 3]])

In [11]: np.hstack([np.vstack([a]*5)]*5).shape
Out[11]: (10, 10)
0
kmario23