Différences entre numpy.random et random.random en Python

100

J'ai un gros script en Python. Je me suis inspiré du code des autres donc j'ai fini par utiliser le numpy.randommodule pour certaines choses (par exemple pour créer un tableau de nombres aléatoires tirés d'une distribution binomiale) et dans d'autres endroits j'utilise le module random.random.

Quelqu'un peut-il me dire les principales différences entre les deux? En regardant la page Web de documentation pour chacun des deux, il me semble qu'il y a numpy.randomjuste plus de méthodes, mais je ne sais pas en quoi la génération des nombres aléatoires est différente.

La raison pour laquelle je pose la question est que je dois amorcer mon programme principal à des fins de débogage. Mais cela ne fonctionne que si j'utilise le même générateur de nombres aléatoires dans tous les modules que j'importe, est-ce correct?

Aussi, j'ai lu ici, dans un autre article, une discussion sur NE PAS utiliser numpy.random.seed(), mais je ne comprenais pas vraiment pourquoi c'était une si mauvaise idée. J'apprécierais vraiment que quelqu'un m'explique pourquoi c'est le cas.

Laura
la source

Réponses:

121

Vous avez déjà fait de nombreuses observations correctes!

À moins que vous ne souhaitiez amorcer les deux générateurs aléatoires, il est probablement plus simple à long terme de choisir un générateur ou l'autre. Mais si vous devez utiliser les deux, alors oui, vous devrez également les semer tous les deux, car ils génèrent des nombres aléatoires indépendamment les uns des autres.

Car numpy.random.seed(), la principale difficulté est qu'il n'est pas thread-safe - c'est-à-dire qu'il n'est pas sûr à utiliser si vous avez de nombreux threads d'exécution différents , car il n'est pas garanti de fonctionner si deux threads différents exécutent la fonction en même temps. Si vous n'utilisez pas de threads, et si vous pouvez raisonnablement vous attendre à ce que vous n'ayez pas besoin de réécrire votre programme de cette façon à l'avenir, cela numpy.random.seed()devrait aller. S'il y a une raison de soupçonner que vous pourriez avoir besoin de threads à l'avenir, il est beaucoup plus sûr à long terme de faire comme suggéré et de créer une instance locale de la numpy.random.Randomclasse . Autant que je sache, random.random.seed()est thread-safe (ou du moins, je n'ai trouvé aucune preuve du contraire).

La numpy.randombibliothèque contient quelques distributions de probabilités supplémentaires couramment utilisées dans la recherche scientifique, ainsi que quelques fonctions pratiques pour générer des tableaux de données aléatoires. La random.randombibliothèque est un peu plus légère et devrait convenir si vous ne faites pas de recherche scientifique ou d'autres types de travail en statistique.

Sinon, ils utilisent tous les deux la séquence de torsions de Mersenne pour générer leurs nombres aléatoires, et ils sont tous deux complètement déterministes - c'est-à-dire que si vous connaissez quelques informations clés, il est possible de prédire avec une certitude absolue le nombre qui suivra . Pour cette raison, ni numpy.random ni random.random ne conviennent à des utilisations cryptographiques sérieuses . Mais comme la séquence est très très longue, les deux sont parfaits pour générer des nombres aléatoires dans les cas où vous ne craignez pas que les gens essaient de procéder à une ingénierie inverse de vos données. C'est aussi la raison pour laquelle il est nécessaire de semer la valeur aléatoire - si vous commencez au même endroit à chaque fois, vous obtiendrez toujours la même séquence de nombres aléatoires!

Comme une note de côté, si vous avez besoin de niveau cryptographique aléatoire, vous devez utiliser le secret module, ou quelque chose comme Crypto.Random si vous utilisez une version Python plus tôt que Python 3.6.

Hannele
la source
14
Pour rappel, il est parfois nécessaire de n'utiliser ni l'un ni l'autre , car le twister de Mersenne ne produit pas de séquences aléatoires d'entropie suffisantes à des fins cryptographiques (et scientifiques inhabituelles). Dans ces rares cas, vous avez souvent besoin de Crypto.Random , qui est capable d'utiliser des sources d'entropie spécifiques au système d'exploitation pour générer des séquences aléatoires non déterministes de bien meilleure qualité que celles disponibles random.randomseules. Cependant, vous n'en avez généralement pas besoin.
SingleNegationElimination
Merci Hannnele. Vos idées ont été vraiment très utiles! Il s'avère que je ne peux pas m'en sortir en utilisant UNIQUEMENT un seul générateur de nombres aléatoires, (qui doit être numpy car aléatoire ne produit pas de distributions binomiales) parce que certaines parties de mon programme appellent un autre programme qui utilise random. Je vais devoir semer les deux générateurs.
Laura
2
"si vous savez quel numéro vous avez maintenant, il est possible de prédire avec une certitude absolue quel numéro viendra ensuite." Je pense que cette déclaration pourrait nécessiter des éclaircissements. Ce que cela signifie, c'est que si vous connaissez l' état interne du générateur, vous pouvez reproduire la séquence - ce que vous faites lorsque vous amorcez le générateur. Étant donné une sortie de nombre unique du générateur, vous ne pouvez pas prédire le nombre suivant. La période est si grande que vous auriez probablement besoin d'une longue séquence de nombres avant de pouvoir calculer où vous vous trouvez sur la séquence pseudo-aléatoire et ainsi prédire la suivante.
Kaushik Ghose
12

À partir de Python pour l'analyse des données , le module numpy.randomcomplète Python randomavec des fonctions permettant de générer efficacement des tableaux entiers de valeurs d'échantillons à partir de nombreux types de distributions de probabilités.

En revanche, le randommodule intégré de Python n'échantillonne qu'une seule valeur à la fois, alors qu'il numpy.randompeut générer un très grand échantillon plus rapidement. En utilisant la fonction magique d'IPython, %timeiton peut voir quel module fonctionne le plus rapidement:

In [1]: from random import normalvariate
In [2]: N = 1000000

In [3]: %timeit samples = [normalvariate(0, 1) for _ in xrange(N)]
1 loop, best of 3: 963 ms per loop

In [4]: %timeit np.random.normal(size=N)
10 loops, best of 3: 38.5 ms per loop
lmiguelvargasf
la source
1
Ce n'est pas le cas pour les autres méthodes. par rapport np.random.randint(2)à random.randrange(2)et NumPy était plus lent . NumPy: 1,25 us et aléatoire: 891 ns. Et aussi la même relation pour np.random.rand()et random.random().
Shayan Amani
3

La source de la graine et le profil de distribution utilisé vont affecter les sorties - si vous recherchez le caractère aléatoire cryptographique, l'amorçage à partir de os.urandom () obtiendra presque de vrais octets aléatoires à partir du bavardage de l'appareil (c'est-à-dire Ethernet ou disque) (ie / dev / aléatoire sur BSD)

cela vous évitera de donner une graine et donc de générer des nombres aléatoires déterministes. Cependant, les appels aléatoires vous permettent ensuite d'ajuster les nombres à une distribution (ce que j'appelle le hasard scientifique - finalement tout ce que vous voulez est une distribution en courbe en cloche de nombres aléatoires, numpy est le meilleur pour résoudre cela.

Alors oui, restez avec un seul générateur, mais décidez quel aléatoire vous voulez - aléatoire, mais sans aucun doute à partir d'une courbe de distrubtuion, ou aussi aléatoire que vous pouvez obtenir sans dispositif quantique.

vous cad monsieur - prenez ça
la source
Merci beaucoup Paul, ta réponse a été vraiment utile! Je ne recherche pas le hasard cryptographique, je fais de la modélisation mathématique et les nombres pseudo-aléatoires me suffisent. Il s'avère que je ne peux pas m'en tenir à un générateur comme je le voulais car j'ai besoin de numpy pour la distribution binomiale et mon programme appelle un autre programme qui utilise random :(
Laura