Erzeugen Sie Zufallszahlen mit einer gegebenen (numerischen) Verteilung

Lesezeit: 9 Minuten

Ich habe eine Datei mit einigen Wahrscheinlichkeiten für verschiedene Werte, zB:

1 0.1
2 0.05
3 0.05
4 0.2
5 0.4
6 0.2

Ich möchte mit dieser Verteilung Zufallszahlen generieren. Gibt es ein vorhandenes Modul, das dies handhabt? Es ist ziemlich einfach, selbst zu codieren (erstellen Sie die kumulative Dichtefunktion, generieren Sie einen Zufallswert [0,1] und wählen Sie den entsprechenden Wert aus), aber es scheint, als ob dies ein häufiges Problem sein sollte und wahrscheinlich hat jemand eine Funktion/ein Modul dafür erstellt.

Ich brauche das, weil ich eine Liste von Geburtstagen generieren möchte (die keiner Verteilung im Standard folgen random Modul).

  • Außer random.choice()? Sie erstellen die Hauptliste mit der richtigen Anzahl von Vorkommen und wählen eines aus. Dies ist natürlich eine doppelte Frage.

    – S. Lott

    24. November 2010 um 11:03 Uhr

  • mögliches Duplikat von Random Weighted Choice

    – S. Lott

    24. November 2010 um 11:03 Uhr

  • @S.Lott ist das nicht sehr speicherintensiv für große Unterschiede in der Distribution?

    – Lucas Möskops

    24. November 2010 um 11:05 Uhr

  • @S.Lott: Ihre Wahlmethode wäre wahrscheinlich für eine kleine Anzahl von Vorkommen in Ordnung, aber ich würde lieber vermeiden, riesige Listen zu erstellen, wenn dies nicht erforderlich ist.

    – pafcu

    24. November 2010 um 11:10 Uhr

  • @S.Lott: OK, ungefähr 10000 * 365 = 3650000 = 3,6 Millionen Elemente. Ich bin mir über die Speichernutzung in Python nicht sicher, aber es sind mindestens 3,6 M * 4 B = 14,4 MB. Keine große Menge, aber auch nichts, was Sie ignorieren sollten, wenn es eine ebenso einfache Methode gibt, die keinen zusätzlichen Speicher benötigt.

    – pafcu

    24. November 2010 um 11:25 Uhr

Benutzer-Avatar
Sven Marnach

scipy.stats.rv_discrete könnte das sein, was du willst. Sie können Ihre Wahrscheinlichkeiten über angeben values Parameter. Sie können dann die verwenden rvs() -Methode des Verteilungsobjekts zum Generieren von Zufallszahlen.

Wie Eugene Pakhomov in den Kommentaren darauf hingewiesen hat, können Sie auch a weitergeben p Schlüsselwortparameter zu numpy.random.choice()z.B

numpy.random.choice(numpy.arange(1, 7), p=[0.1, 0.05, 0.05, 0.2, 0.4, 0.2])

Wenn Sie Python 3.6 oder höher verwenden, können Sie verwenden random.choices() aus der Standardbibliothek – siehe die Antwort von Mark Dickinson.

  • Auf meiner Maschine numpy.random.choice() ist fast 20 mal schneller.

    – Eugen Pachomov

    18. Juni 2016 um 6:26 Uhr

  • @EugenePakhomov Ich verstehe deinen Kommentar nicht ganz. Eine Funktion, die etwas völlig anderes macht, ist also schneller als die von mir vorgeschlagene. Meine Empfehlung wäre immer noch, die Funktion zu verwenden, die das tut, was Sie wollen, und nicht eine Funktion, die etwas anderes tut, auch wenn die Funktion, die etwas anderes tut, schneller ist.

    – Sven Marnach

    19. Juni 2016 um 10:58 Uhr

  • es macht genau das gleiche bezüglich der ursprünglichen Frage. Z.B: numpy.random.choice(numpy.arange(1, 7), p=[0.1, 0.05, 0.05, 0.2, 0.4, 0.2])

    – Eugen Pachomov

    20. Juni 2016 um 12:17 Uhr


  • Überraschenderweise arbeitet rv_discrete.rvs() in O(len(p) * size) Zeit und Speicher! Während choice() in optimaler O(len(p) + log(len(p)) * size) Zeit zu laufen scheint.

    – alyaxey

    9. Oktober 2017 um 16:16 Uhr

  • Wenn Sie verwenden Python 3.6 oder neuere gibt’s eine andere Antwort das erfordert keine Addon-Pakete.

    – Markieren Sie Lösegeld

    4. April 2018 um 18:47 Uhr

Seit Python 3.6 gibt es dafür eine Lösung in der Standardbibliothek von Python, nämlich random.choices.

Beispielverwendung: Lassen Sie uns eine Population und Gewichtungen einrichten, die denen in der OP-Frage entsprechen:

>>> from random import choices
>>> population = [1, 2, 3, 4, 5, 6]
>>> weights = [0.1, 0.05, 0.05, 0.2, 0.4, 0.2]

Jetzt choices(population, weights) erzeugt ein einzelnes Sample:

>>> choices(population, weights)
4

Das optionale Nur-Schlüsselwort-Argument k ermöglicht es, mehr als eine Probe gleichzeitig anzufordern. Das ist wertvoll, weil es einige Vorarbeiten gibt random.choices muss jedes Mal, wenn es aufgerufen wird, vor dem Generieren von Samples ausgeführt werden; Indem wir viele Proben auf einmal generieren, müssen wir diese Vorbereitungsarbeit nur einmal erledigen. Hier generieren wir eine Million Samples und verwenden sie collections.Counter um zu überprüfen, ob die Verteilung, die wir erhalten, ungefähr mit den von uns angegebenen Gewichten übereinstimmt.

>>> million_samples = choices(population, weights, k=10**6)
>>> from collections import Counter
>>> Counter(million_samples)
Counter({5: 399616, 6: 200387, 4: 200117, 1: 99636, 3: 50219, 2: 50025})

  • Gibt es eine Python 2.7-Version dazu?

    – abbas786

    5. September 2018 um 1:06 Uhr

  • @abbas786: Nicht integriert, aber die anderen Antworten auf diese Frage sollten alle auf Python 2.7 funktionieren. Sie können auch in der Python 3-Quelle nach random.choices suchen und diese kopieren, wenn Sie möchten.

    – Mark Dickinson

    8. November 2018 um 18:55 Uhr

Benutzer-Avatar
sdcvc

Ein Vorteil beim Generieren der Liste mit CDF besteht darin, dass Sie die binäre Suche verwenden können. Während Sie O(n) Zeit und Platz für die Vorverarbeitung benötigen, können Sie k Zahlen in O(k log n) erhalten. Da normale Python-Listen ineffizient sind, können Sie verwenden array Modul.

Wenn Sie auf konstantem Speicherplatz bestehen, können Sie Folgendes tun; O(n) Zeit, O(1) Raum.

def random_distr(l):
    r = random.uniform(0, 1)
    s = 0
    for item, prob in l:
        s += prob
        if s >= r:
            return item
    return item  # Might occur because of floating point inaccuracies

  • Die Reihenfolge der (item, prob)-Paare in der Liste spielt bei Ihrer Implementierung eine Rolle, richtig?

    – Stackoverflowuser2010

    6. Juni 2013 um 22:37 Uhr

  • @stackoverflowuser2010: Es sollte keine Rolle spielen (Modulo-Fehler in Gleitkommazahlen)

    – sdcvc

    7. Juni 2013 um 12:52 Uhr

  • Nett. Ich fand, dass dies 30% schneller ist als scipy.stats.rv_discrete.

    – Espe

    3. Mai 2015 um 3:07 Uhr


  • Nicht selten löst diese Funktion einen KeyError aus, weil die letzte Zeile.

    – imrek

    9. September 2015 um 20:02 Uhr

  • @DrunkenMaster: Ich verstehe nicht. Ist Ihnen bewusst l[-1] gibt das letzte Element der Liste zurück?

    – sdcvc

    9. September 2015 um 20:33 Uhr

(OK, ich weiß, dass Sie nach Schrumpffolie fragen, aber vielleicht waren diese hausgemachten Lösungen einfach nicht prägnant genug für Ihren Geschmack. 🙂

pdf = [(1, 0.1), (2, 0.05), (3, 0.05), (4, 0.2), (5, 0.4), (6, 0.2)]
cdf = [(i, sum(p for j,p in pdf if j < i)) for i,_ in pdf]
R = max(i for r in [random.random()] for i,c in cdf if c <= r)

Ich habe pseudo-bestätigt, dass dies funktioniert, indem ich die Ausgabe dieses Ausdrucks angeschaut habe:

sorted(max(i for r in [random.random()] for i,c in cdf if c <= r)
       for _ in range(1000))

Benutzer-Avatar
Heberto Mayorquin

Vielleicht ist es etwas spät. Aber Sie können verwenden numpy.random.choice()vorbei an der p Parameter:

val = numpy.random.choice(numpy.arange(1, 7), p=[0.1, 0.05, 0.05, 0.2, 0.4, 0.2])

  • Das OP will nicht verwenden random.choice() – Siehe die Kommentare.

    – pobrelkey

    1. Dezember 2013 um 1:17 Uhr

  • numpy.random.choice() ist völlig anders als random.choice() und unterstützt die Wahrscheinlichkeitsverteilung.

    – Eugen Pachomov

    18. Juni 2016 um 6:25 Uhr

  • Kann ich keine Funktion verwenden, um p zu definieren? Warum sollte ich es mit Zahlen definieren wollen?

    – rjurney

    17. Februar 2021 um 21:17 Uhr

  • Wenn Sie aus einer bestimmten Verteilung Stichproben ziehen möchten, sollten Sie ein Statistikpaket wie verwenden scipy.statsoder statsmodels und holen Sie sich dann Stichproben aus der spezifischen Wahrscheinlichkeitsverteilung, aus der Sie Stichproben ziehen möchten. Diese Frage betrifft den Fall einer benutzerdefinierten diskreten Verteilung.

    – Heberto Mayorquin

    13. April um 6:15 Uhr

Benutzer-Avatar
Markus Dutschke

Ich habe eine Lösung für geschrieben Ziehen von Zufallsstichproben aus einer benutzerdefinierten kontinuierlichen Verteilung.

Ich brauchte dies für einen ähnlichen Anwendungsfall wie Ihren (dh Generieren zufälliger Daten mit einer bestimmten Wahrscheinlichkeitsverteilung).

Sie brauchen nur die Funktion random_custDist und die Linie samples=random_custDist(x0,x1,custDist=custDist,size=1000). Der Rest ist Dekoration ^^.

import numpy as np

#funtion
def random_custDist(x0,x1,custDist,size=None, nControl=10**6):
    #genearte a list of size random samples, obeying the distribution custDist
    #suggests random samples between x0 and x1 and accepts the suggestion with probability custDist(x)
    #custDist noes not need to be normalized. Add this condition to increase performance. 
    #Best performance for max_{x in [x0,x1]} custDist(x) = 1
    samples=[]
    nLoop=0
    while len(samples)<size and nLoop<nControl:
        x=np.random.uniform(low=x0,high=x1)
        prop=custDist(x)
        assert prop>=0 and prop<=1
        if np.random.uniform(low=0,high=1) <=prop:
            samples += [x]
        nLoop+=1
    return samples

#call
x0=2007
x1=2019
def custDist(x):
    if x<2010:
        return .3
    else:
        return (np.exp(x-2008)-1)/(np.exp(2019-2007)-1)
samples=random_custDist(x0,x1,custDist=custDist,size=1000)
print(samples)

#plot
import matplotlib.pyplot as plt
#hist
bins=np.linspace(x0,x1,int(x1-x0+1))
hist=np.histogram(samples, bins )[0]
hist=hist/np.sum(hist)
plt.bar( (bins[:-1]+bins[1:])/2, hist, width=.96, label="sample distribution")
#dist
grid=np.linspace(x0,x1,100)
discCustDist=np.array([custDist(x) for x in grid]) #distrete version
discCustDist*=1/(grid[1]-grid[0])/np.sum(discCustDist)
plt.plot(grid,discCustDist,label="custom distribustion (custDist)", color="C1", linewidth=4)
#decoration
plt.legend(loc=3,bbox_to_anchor=(1,0))
plt.show()

Kontinuierliche benutzerdefinierte Verteilung und diskrete Probenverteilung

Die Leistung dieser Lösung ist sicherlich verbesserungswürdig, aber ich bevorzuge die Lesbarkeit.

  • Das OP will nicht verwenden random.choice() – Siehe die Kommentare.

    – pobrelkey

    1. Dezember 2013 um 1:17 Uhr

  • numpy.random.choice() ist völlig anders als random.choice() und unterstützt die Wahrscheinlichkeitsverteilung.

    – Eugen Pachomov

    18. Juni 2016 um 6:25 Uhr

  • Kann ich keine Funktion verwenden, um p zu definieren? Warum sollte ich es mit Zahlen definieren wollen?

    – rjurney

    17. Februar 2021 um 21:17 Uhr

  • Wenn Sie aus einer bestimmten Verteilung Stichproben ziehen möchten, sollten Sie ein Statistikpaket wie verwenden scipy.statsoder statsmodels und holen Sie sich dann Stichproben aus der spezifischen Wahrscheinlichkeitsverteilung, aus der Sie Stichproben ziehen möchten. Diese Frage betrifft den Fall einer benutzerdefinierten diskreten Verteilung.

    – Heberto Mayorquin

    13. April um 6:15 Uhr

Benutzer-Avatar
Gemeinschaft

Erstellen Sie eine Liste der Artikel, basierend auf ihrer weights:

items = [1, 2, 3, 4, 5, 6]
probabilities= [0.1, 0.05, 0.05, 0.2, 0.4, 0.2]
# if the list of probs is normalized (sum(probs) == 1), omit this part
prob = sum(probabilities) # find sum of probs, to normalize them
c = (1.0)/prob # a multiplier to make a list of normalized probs
probabilities = map(lambda x: c*x, probabilities)
print probabilities

ml = max(probabilities, key=lambda x: len(str(x)) - str(x).find('.'))
ml = len(str(ml)) - str(ml).find('.') -1
amounts = [ int(x*(10**ml)) for x in probabilities]
itemsList = list()
for i in range(0, len(items)): # iterate through original items
  itemsList += items[i:i+1]*amounts[i]

# choose from itemsList randomly
print itemsList

Eine Optimierung kann darin bestehen, Beträge durch den größten gemeinsamen Teiler zu normalisieren, um die Zielliste kleiner zu machen.

Auch das könnte interessant sein.

  • Wenn die Liste der Elemente groß ist, kann dies viel zusätzlichen Speicher beanspruchen.

    – pafcu

    24. November 2010 um 11:39 Uhr

  • @pafcu Einverstanden. Nur eine Lösung, die zweite, die mir in den Sinn kam (die erste war, nach etwas wie “Gewichtswahrscheinlichkeitspython” zu suchen :)).

    – Chatschik

    24. November 2010 um 11:46 Uhr

1034280cookie-checkErzeugen Sie Zufallszahlen mit einer gegebenen (numerischen) Verteilung

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy