سؤال

لنفترض أن لدي مجموعة من احتمالات العناصر [0.1, 0.2, 0.5, 0.2].مجموع الصفيف يصل إلى 1.0.

باستخدام Python أو numpy، أريد رسم عناصر تتناسب مع احتماليتها:العنصر الأول حوالي 10% من الوقت، والثاني 20%، والثالث 50%، وما إلى ذلك.يجب أن يُرجع "الرسم" فهرس العنصر المرسوم.

خطرت لي هذه:

def draw(probs):
    cumsum = numpy.cumsum(probs / sum(probs)) # sum up to 1.0, just in case
    return len(numpy.where(numpy.random.rand() >= cumsum)[0])

إنه يعمل، لكنه معقد جدًا، يجب أن تكون هناك طريقة أفضل.شكرًا.

هل كانت مفيدة؟

المحلول

import numpy as np
def random_pick(choices, probs):
    '''
    >>> a = ['Hit', 'Out']
    >>> b = [.3, .7]
    >>> random_pick(a,b)
    '''
    cutoffs = np.cumsum(probs)
    idx = cutoffs.searchsorted(np.random.uniform(0, cutoffs[-1]))
    return choices[idx]

كيف تعمل:

In [22]: import numpy as np
In [23]: probs = [0.1, 0.2, 0.5, 0.2]

حساب المبلغ التراكمي:

In [24]: cutoffs = np.cumsum(probs)
In [25]: cutoffs
Out[25]: array([ 0.1,  0.3,  0.8,  1. ])

حساب رقم عشوائي موزع بشكل موحد في الفترة نصف المفتوحة [0, cutoffs[-1]):

In [26]: np.random.uniform(0, cutoffs[-1])
Out[26]: 0.9723114393023948

يستخدم com.searchsorted للعثور على الفهرس الذي سيتم إدراج الرقم العشوائي فيه cutoffs:

In [27]: cutoffs.searchsorted(0.9723114393023948)
Out[27]: 3

يعود choices[idx], ، أين idx هو هذا المؤشر.

نصائح أخرى

تريد أخذ عينة من التوزيع الفئوي، والذي لم يتم تنفيذه في numpy.ومع ذلك، متعدد الحدود التوزيع هو تعميم لل قاطع التوزيع ويمكن استخدامها لهذا الغرض.

>>> import numpy as np
>>> 
>>> def sampleCategory(p):
...     return np.flatnonzero( np.random.multinomial(1,p,1) )[0]
... 
>>> sampleCategory( [0.1,0.5,0.4] )
1

يستخدم numpy.random.multinomial - الاكثر فعاليه

لم أستخدم numpy مطلقًا، لكنني أفترض أن الكود الخاص بي أدناه (python فقط) يفعل نفس الشيء الذي أنجزته في سطر واحد.أنا أضعه هنا فقط في حال كنت تريد ذلك.

يبدو c-ish جدًا لذا أعتذر عن عدم كونه بيثونيًا جدًا.

الوزن_المجموع سيكون 1 بالنسبة لك.

def draw(probs)
    r = random.randrange(weight_total)
    running_total = 0
    for i, p in enumerate(probs)
        running_total += p
        if running_total > r:
            return i

يستخدم ينصف

import bisect
import random
import numpy 
def draw(probs):
    cumsum=numpy.cumsum(probs/sum(probs))
    return bisect.bisect_left(cumsum, numpy.random.rand())

يجب أن تفعل الخدعة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top