Random.choice का एक भारित संस्करण


245

मुझे random.choice का भारित संस्करण लिखने की आवश्यकता थी (सूची में प्रत्येक तत्व को चयनित होने की एक अलग संभावना है)। मैंने ये ढूंढ निकाला:

def weightedChoice(choices):
    """Like random.choice, but each element can have a different chance of
    being selected.

    choices can be any iterable containing iterables with two items each.
    Technically, they can have more than two items, the rest will just be
    ignored.  The first item is the thing being chosen, the second item is
    its weight.  The weights can be any numeric values, what matters is the
    relative differences between them.
    """
    space = {}
    current = 0
    for choice, weight in choices:
        if weight > 0:
            space[current] = choice
            current += weight
    rand = random.uniform(0, current)
    for key in sorted(space.keys() + [current]):
        if rand < key:
            return choice
        choice = space[key]
    return None

यह कार्य मुझे जटिल लगता है, और बदसूरत। मैं उम्मीद कर रहा हूं कि यहां हर कोई इसे बेहतर बनाने या इसे करने के वैकल्पिक तरीकों पर कुछ सुझाव दे सकता है। दक्षता मेरे लिए उतनी महत्वपूर्ण नहीं है जितनी कि कोड सफाई और पठनीयता।

जवाबों:


297

संस्करण 1.7.0 के बाद से, NumPy में एक choiceफ़ंक्शन है जो प्रायिकता वितरण का समर्थन करता है।

from numpy.random import choice
draw = choice(list_of_candidates, number_of_items_to_pick,
              p=probability_distribution)

ध्यान दें कि probability_distributionउसी क्रम में एक क्रम है list_of_candidates। आप replace=Falseव्यवहार को बदलने के लिए कीवर्ड का भी उपयोग कर सकते हैं ताकि तैयार की गई वस्तुओं को प्रतिस्थापित न किया जाए।


11
मेरे परीक्षण से, यह random.choicesव्यक्तिगत कॉल की तुलना में परिमाण का एक क्रम है । यदि आपको बहुत सारे यादृच्छिक परिणामों की आवश्यकता है, तो एक बार समायोजन करके उन सभी को चुनना वास्तव में महत्वपूर्ण है number_of_items_to_pick। यदि आप ऐसा करते हैं, तो यह तीव्रता का एक क्रम है।
jpmc26

2
यह tuples आदि के साथ काम नहीं करता है ("ValueError: a-होना चाहिए 1-आयामी"), इसलिए उस स्थिति में कोई भी व्यक्ति को सूची में सूचकांक को चुनने के लिए कह सकता है len(list_of_candidates), या फिरlist_of_candidates[draw]
xjcl

218

पायथन 3.6 के बाद choicesसे randomमॉड्यूल से एक विधि है ।

Python 3.6.1 (v3.6.1:69c0db5050, Mar 21 2017, 01:21:04)
Type 'copyright', 'credits' or 'license' for more information
IPython 6.0.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import random

In [2]: random.choices(
...:     population=[['a','b'], ['b','a'], ['c','b']],
...:     weights=[0.2, 0.2, 0.6],
...:     k=10
...: )

Out[2]:
[['c', 'b'],
 ['c', 'b'],
 ['b', 'a'],
 ['c', 'b'],
 ['c', 'b'],
 ['b', 'a'],
 ['c', 'b'],
 ['b', 'a'],
 ['c', 'b'],
 ['c', 'b']]

ध्यान दें कि डॉक्स के अनुसार, प्रतिस्थापन के साथrandom.choices नमूना होगा :

kप्रतिस्थापन के साथ आबादी से चुने गए तत्वों की एक आकार सूची लौटाएं ।

यदि आपको प्रतिस्थापन के बिना नमूना करने की आवश्यकता है, तो @ रॉन-पैक्सो के शानदार उत्तर के रूप में , आप उपयोग कर सकते हैं numpy.choice, जिसका replaceतर्क इस तरह के व्यवहार को नियंत्रित करता है।


4
यह numpy.random.choice की तुलना में बहुत तेज है। 10,000 भारित वस्तुओं की सूची से 10,000 गुना, numpy.random.choice ने 0.3286 सेकंड लिया, जहां random.choices ने 0.0416 सेकंड लिया, लगभग 8x तेज़ी से।
एंटोन कोड्स

@AntonCodes यह उदाहरण चेरी द्वारा उठाया गया है। numpy में कुछ निरंतर-समय ओवरहेड होने वाला है random.choices, जो निश्चित रूप से नहीं है, इसलिए यह 8 आइटमों की छोटी सूची पर धीमा है, और यदि आप ऐसी सूची से 10k बार चुनते हैं, तो आप सही हैं। लेकिन ऐसे मामलों के लिए जब सूची बड़ी होती है (आप कैसे परीक्षण कर रहे हैं, इसके आधार पर, मुझे 100-300 तत्वों के बीच विराम बिंदु दिखाई देते हैं), काफी चौड़े अंतराल से np.random.choiceबेहतर प्रदर्शन करना शुरू करता है random.choices। उदाहरण के लिए, सुन्न कॉल के साथ सामान्यीकरण कदम सहित, मुझे random.choices10k तत्वों की सूची के लिए लगभग 4x स्पीडअप मिलता है ।
गोरगलेन

यह प्रदर्शन में सुधार के आधार पर नया उत्तर होना चाहिए जो @AntonCodes ने रिपोर्ट किया है।
वेन कर्मकार

132
def weighted_choice(choices):
   total = sum(w for c, w in choices)
   r = random.uniform(0, total)
   upto = 0
   for c, w in choices:
      if upto + w >= r:
         return c
      upto += w
   assert False, "Shouldn't get here"

10
आप एक ऑपरेशन को ड्रॉप कर सकते हैं और लूप के लिए स्टेटमेंट्स को उलट-पलट कर समय के एक upto +=w; if upto > r
थपकी को बचा सकते हैं

5
हर बार वेट को हटाकर एक वेरिएबल को सेव करें और हर बार वेट द्वारा r को घटाएं। तुलना फिर हैif r < 0
JnBrymn

@JnBrymn आपको जांचने की आवश्यकता है r <= 0। 1 आइटम के इनपुट सेट और 1.0 के रोल पर विचार करें। फिर दावा विफल हो जाएगा। मैंने उत्तर में उस त्रुटि को ठीक किया।
मूओइपाइप

1
@ सारथ्रियन आप आंशिक रूप में लूप के लिए चिह्नित करने के लिए एक प्रज्ञा का उपयोग कर सकते हैं:# pragma: no branch
नेड बाथेल्डर

1
@ mLstudent33 मैं उर्वशी का उपयोग नहीं करता।
एंटोन कोड्स

70
  1. भार को एक संचयी वितरण में व्यवस्थित करें।
  2. एक यादृच्छिक फ्लोट लेने के लिए random.random () का उपयोग करें 0.0 <= x < total
  3. उदाहरण के रूप में http://docs.python.org/dev/library/bisect.html#other-examples पर दिखाए गए अनुसार bisect.bisect का उपयोग करके वितरण खोजें ।
from random import random
from bisect import bisect

def weighted_choice(choices):
    values, weights = zip(*choices)
    total = 0
    cum_weights = []
    for w in weights:
        total += w
        cum_weights.append(total)
    x = random() * total
    i = bisect(cum_weights, x)
    return values[i]

>>> weighted_choice([("WHITE",90), ("RED",8), ("GREEN",2)])
'WHITE'

यदि आपको एक से अधिक विकल्प बनाने की आवश्यकता है, तो इसे दो कार्यों में विभाजित करें, एक संचयी भार बनाने के लिए और दूसरा एक यादृच्छिक बिंदु पर बाइसेक्ट करने के लिए।


5
यह नेड के उत्तर की तुलना में अधिक कुशल है। असल में, विकल्पों के माध्यम से एक रैखिक (O (n)) खोज करने के बजाय, वह एक द्विआधारी खोज (O (log n)) कर रहा है। +1!
NHDaly

यदि यादृच्छिक () 1.0
जॉन वॉन

10
O(n)संचयी वितरण गणना के कारण यह अभी भी चलता है ।
लेव लेवित्स्की

6
यह समाधान उस स्थिति में बेहतर है, जहां समान विकल्पों के सेट के लिए भारित_कॉपी के लिए कई कॉल की आवश्यकता होती है। उस स्थिति में आप एक बार संचयी योग बना सकते हैं और प्रत्येक कॉल पर एक द्विआधारी खोज कर सकते हैं।
आमोस

1
@JonVaughan 1.0 random() नहीं लौटा सकता । डॉक्स के अनुसार, यह आधे-खुले अंतराल में एक परिणाम देता है [0.0, 1.0), जो यह कहता है कि यह ठीक 0.0 पर वापस आ सकता है , लेकिन ठीक 1.0 नहीं हो सकता है । सबसे बड़ा मूल्य जो वापस आ सकता है वह है 0.9999999999999988887769753748434595763683319091796875 (जो कि पायथन को 0.99999999999999 के रूप में छापता है, और 1 से कम 64-बिट फ्लोट है)।
मार्क अमेरी

21

आप numpy का उपयोग कर कोई आपत्ति नहीं है, तो आप उपयोग कर सकते हैं numpy.random.choice

उदाहरण के लिए:

import numpy

items  = [["item1", 0.2], ["item2", 0.3], ["item3", 0.45], ["item4", 0.05]
elems = [i[0] for i in items]
probs = [i[1] for i in items]

trials = 1000
results = [0] * len(items)
for i in range(trials):
    res = numpy.random.choice(items, p=probs)  #This is where the item is selected!
    results[items.index(res)] += 1
results = [r / float(trials) for r in results]
print "item\texpected\tactual"
for i in range(len(probs)):
    print "%s\t%0.4f\t%0.4f" % (items[i], probs[i], results[i])

यदि आप जानते हैं कि आपको पहले से कितने चयन करने की आवश्यकता है, तो आप इसे इस तरह से बिना लूप के कर सकते हैं:

numpy.random.choice(items, trials, p=probs)

15

क्रूड, लेकिन पर्याप्त हो सकता है:

import random
weighted_choice = lambda s : random.choice(sum(([v]*wt for v,wt in s),[]))

क्या यह काम करता है?

# define choices and relative weights
choices = [("WHITE",90), ("RED",8), ("GREEN",2)]

# initialize tally dict
tally = dict.fromkeys(choices, 0)

# tally up 1000 weighted choices
for i in xrange(1000):
    tally[weighted_choice(choices)] += 1

print tally.items()

प्रिंटों:

[('WHITE', 904), ('GREEN', 22), ('RED', 74)]

मानता है कि सभी भार पूर्णांक हैं। उन्हें 100 तक जोड़ना नहीं है, मैंने सिर्फ इतना किया कि परीक्षण के परिणामों को व्याख्या के लिए आसान बना दिया। (यदि वज़न फ़्लोटिंग पॉइंट संख्या है, तो उन सभी को 10 से गुणा करें जब तक कि सभी वज़न> = 1.)

weights = [.6, .2, .001, .199]
while any(w < 1.0 for w in weights):
    weights = [w*10 for w in weights]
weights = map(int, weights)

1
अच्छा, मुझे यकीन नहीं है कि मैं मान सकता हूं कि सभी भार पूर्णांक हैं, हालांकि।
कॉलिन

1
इस उदाहरण में आपकी वस्तुओं की तरह नकल की जाएगी। यह अक्षम होगा (और इसलिए पूर्णांक को भार में बदलने का कार्य है)। फिर भी, यह समाधान एक अच्छा लाइनर है यदि पूर्णांक वजन छोटा है।
wei2912

प्रिमिटिव को डुप्लिकेट किया जाएगा, लेकिन ऑब्जेक्ट्स को केवल डुप्लिकेट किए गए संदर्भ होंगे, न कि ऑब्जेक्ट्स स्वयं। (यही कारण है कि आप का उपयोग करके सूचियों की एक सूची नहीं बना सकते हैं [[]]*10- बाहरी सूची के सभी तत्व एक ही सूची को इंगित करते हैं।
पॉलमेक्स

@PaulMcG नहीं; कुछ भी नहीं लेकिन संदर्भों को कभी भी दोहराया जाएगा। पायथन की प्रकार प्रणाली में आदिम की कोई अवधारणा नहीं है। आप इस बात की पुष्टि कर सकते हैं कि उदाहरण के लिए, intआप अभी भी एक ही वस्तु के बहुत सारे संदर्भ प्राप्त कर रहे हैं जैसे कि कुछ करना [id(x) for x in ([99**99] * 100)]और निरीक्षण करना कि idहर कॉल पर एक ही मेमोरी पता मिलता है।
मार्क अमेरी ने

14

यदि आपके पास एक सूची के बजाय एक भारित शब्दकोश है तो आप यह लिख सकते हैं

items = { "a": 10, "b": 5, "c": 1 } 
random.choice([k for k in items for dummy in range(items[k])])

ध्यान दें कि [k for k in items for dummy in range(items[k])]यह सूची तैयार करता है['a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'c', 'b', 'b', 'b', 'b', 'b']


10
यह छोटे कुल जनसंख्या मूल्यों के लिए काम करता है, लेकिन बड़े डेटासेट के लिए नहीं (उदाहरण के लिए राज्य द्वारा अमेरिकी आबादी इसमें 300 मिलियन आइटमों के साथ एक कार्य सूची बनाना समाप्त करेगी)।
रयान

@ रयान तथ्य। यह गैर-पूर्णांक वज़न के लिए भी काम नहीं करता है, जो एक और यथार्थवादी परिदृश्य हैं (जैसे कि यदि आपके पास चयन की संभावनाओं के रूप में व्यक्त किए गए आपके वज़न हैं)।
मार्क अमेरी

12

अजगर के रूप में v3.6, वैकल्पिक भार के साथ दी गई आबादी से निर्दिष्ट आकार के तत्वों random.choicesको वापस करने के लिए इस्तेमाल किया जा सकता है list

random.choices(population, weights=None, *, cum_weights=None, k=1)

  • आबादी : listअद्वितीय टिप्पणियों से युक्त। (यदि खाली है, उठाता है IndexError)

  • वजन : चयन करने के लिए अधिक सटीक सापेक्ष भार की आवश्यकता होती है।

  • cum_weights : चयन करने के लिए आवश्यक संचयी भार।

  • k : size ( len) का listआउटपुट होना। (डिफ़ॉल्ट len()=1)


कुछ कैविट्स:

1) यह प्रतिस्थापन के साथ भारित नमूने का उपयोग करता है इसलिए तैयार वस्तुओं को बाद में बदल दिया जाएगा। वज़न अनुक्रम में मूल्य अपने आप में मायने नहीं रखते हैं, लेकिन उनका सापेक्ष अनुपात करता है।

इसके विपरीत np.random.choiceजो केवल संभावनाओं को भार के रूप में ले सकता है और यह भी कि 1 मानदंडों तक व्यक्तिगत संभावनाओं का योग सुनिश्चित करना चाहिए, यहां कोई नियम नहीं हैं। जब तक वे संख्यात्मक प्रकार (प्रकार int/float/fractionको छोड़कर Decimal) से संबंधित हैं, तब भी ये प्रदर्शन करेंगे।

>>> import random
# weights being integers
>>> random.choices(["white", "green", "red"], [12, 12, 4], k=10)
['green', 'red', 'green', 'white', 'white', 'white', 'green', 'white', 'red', 'white']
# weights being floats
>>> random.choices(["white", "green", "red"], [.12, .12, .04], k=10)
['white', 'white', 'green', 'green', 'red', 'red', 'white', 'green', 'white', 'green']
# weights being fractions
>>> random.choices(["white", "green", "red"], [12/100, 12/100, 4/100], k=10)
['green', 'green', 'white', 'red', 'green', 'red', 'white', 'green', 'green', 'green']

2) यदि न तो वज़न और न ही cum_weights निर्दिष्ट हैं, तो चयन समान संभावना के साथ किए जाते हैं। यदि एक वेट सीक्वेंस की आपूर्ति की जाती है, तो जनसंख्या अनुक्रम के समान लंबाई होनी चाहिए ।

दोनों वज़न और cum_weights निर्दिष्ट करना एक उठाता है TypeError

>>> random.choices(["white", "green", "red"], k=10)
['white', 'white', 'green', 'red', 'red', 'red', 'white', 'white', 'white', 'green']

3) सह_वाइट्स आमतौर पर itertools.accumulateफ़ंक्शन का एक परिणाम है जो वास्तव में ऐसी स्थितियों में काम करते हैं।

लिंक किए गए दस्तावेज़ से:

आंतरिक रूप से, सापेक्ष वजन चयन करने से पहले संचयी भार में बदल जाते हैं, इसलिए संचयी भार की आपूर्ति से काम बच जाता है।

तो, या तो आपूर्ति weights=[12, 12, 4]या cum_weights=[12, 24, 28]हमारे वंचित मामले के लिए एक ही परिणाम पैदा करता है और बाद वाला अधिक तेज / कुशल लगता है।


11

यहाँ वह संस्करण है जिसे पायथन 3.6 के लिए मानक पुस्तकालय में शामिल किया जा रहा है:

import itertools as _itertools
import bisect as _bisect

class Random36(random.Random):
    "Show the code included in the Python 3.6 version of the Random class"

    def choices(self, population, weights=None, *, cum_weights=None, k=1):
        """Return a k sized list of population elements chosen with replacement.

        If the relative weights or cumulative weights are not specified,
        the selections are made with equal probability.

        """
        random = self.random
        if cum_weights is None:
            if weights is None:
                _int = int
                total = len(population)
                return [population[_int(random() * total)] for i in range(k)]
            cum_weights = list(_itertools.accumulate(weights))
        elif weights is not None:
            raise TypeError('Cannot specify both weights and cumulative weights')
        if len(cum_weights) != len(population):
            raise ValueError('The number of weights does not match the population')
        bisect = _bisect.bisect
        total = cum_weights[-1]
        return [population[bisect(cum_weights, random() * total)] for i in range(k)]

स्रोत: https://hg.python.org/cpython/file/tip/Lib/random.py#l340


2
import numpy as np
w=np.array([ 0.4,  0.8,  1.6,  0.8,  0.4])
np.random.choice(w, p=w/sum(w))

2

मुझे शायद उपयोगी कुछ भी योगदान करने में बहुत देर हो गई है, लेकिन यहाँ एक सरल, संक्षिप्त और बहुत ही कुशल स्निपेट है:

def choose_index(probabilies):
    cmf = probabilies[0]
    choice = random.random()
    for k in xrange(len(probabilies)):
        if choice <= cmf:
            return k
        else:
            cmf += probabilies[k+1]

अपनी संभावनाओं को छाँटने या अपने cmf के साथ एक वेक्टर बनाने की आवश्यकता नहीं है, और यह अपनी पसंद का पता लगाने के बाद समाप्त हो जाता है। मेमोरी: ओ (1), समय: ओ (एन), औसत चलने के समय के साथ ~ एन / 2।

यदि आपके पास वज़न है, तो बस एक पंक्ति जोड़ें:

def choose_index(weights):
    probabilities = weights / sum(weights)
    cmf = probabilies[0]
    choice = random.random()
    for k in xrange(len(probabilies)):
        if choice <= cmf:
            return k
        else:
            cmf += probabilies[k+1]

1
इसके साथ कई चीजें गलत हैं। सतही तौर पर, कुछ टाइप किए गए चर नाम हैं और इस ओवर का उपयोग करने के लिए कोई तर्क नहीं दिया गया है, कहते हैं np.random.choice,। लेकिन अधिक दिलचस्प बात यह है कि एक विफलता मोड है जहां यह एक अपवाद को जन्म देता है। ऐसा करने की probabilities = weights / sum(weights)गारंटी नहीं है कि probabilities1 का योग होगा; उदाहरण के लिए, यदि weightsहै [1,1,1,1,1,1,1]तो probabilitiesकेवल .9999999999999998 का योग होगा, के सबसे बड़े संभावित वापसी मान से छोटा random.random(जो .9999999999999999 है)। फिर choice <= cmfकभी संतुष्ट नहीं होता।
मार्क अमेरी

2

यदि आपकी भारित विकल्पों की सूची अपेक्षाकृत स्थिर है, और आप बार-बार नमूना लेना चाहते हैं, तो आप इस संबंधित उत्तर में कार्यों का उपयोग करके O (N) प्रीप्रोसेसिंग चरण कर सकते हैं, और फिर O (1) में चयन कर सकते हैं ।

# run only when `choices` changes.
preprocessed_data = prep(weight for _,weight in choices)

# O(1) selection
value = choices[sample(preprocessed_data)][0]

1

मैंने नुकीले अन्य धागे को देखा और अपनी कोडिंग शैली में इस बदलाव के साथ आया, यह टैलींग के उद्देश्य के लिए पसंद का सूचकांक देता है, लेकिन स्ट्रिंग को लौटाना सरल है (टिप्पणी वापसी विकल्प):

import random
import bisect

try:
    range = xrange
except:
    pass

def weighted_choice(choices):
    total, cumulative = 0, []
    for c,w in choices:
        total += w
        cumulative.append((total, c))
    r = random.uniform(0, total)
    # return index
    return bisect.bisect(cumulative, (r,))
    # return item string
    #return choices[bisect.bisect(cumulative, (r,))][0]

# define choices and relative weights
choices = [("WHITE",90), ("RED",8), ("GREEN",2)]

tally = [0 for item in choices]

n = 100000
# tally up n weighted choices
for i in range(n):
    tally[weighted_choice(choices)] += 1

print([t/sum(tally)*100 for t in tally])

1

यह इस बात पर निर्भर करता है कि आप वितरण का नमूना कितनी बार लेना चाहते हैं।

मान लीजिए कि आप वितरण K समय का नमूना लेना चाहते हैं। फिर, np.random.choice()प्रत्येक समय का उपयोग करने में समय की जटिलता O(K(n + log(n)))तब nहोती है जब वितरण में वस्तुओं की संख्या होती है।

मेरे मामले में, मुझे 10 ^ 3 के क्रम के समान वितरण को कई बार नमूना करने की आवश्यकता थी जहां n 10 ^ 6 के क्रम का है। मैंने नीचे दिए गए कोड का उपयोग किया है, जो संचयी वितरण को पूर्वनिर्धारित करता है और इसमें नमूने लेता है O(log(n))। कुल मिलाकर समय जटिलता है O(n+K*log(n))

import numpy as np

n,k = 10**6,10**3

# Create dummy distribution
a = np.array([i+1 for i in range(n)])
p = np.array([1.0/n]*n)

cfd = p.cumsum()
for _ in range(k):
    x = np.random.uniform()
    idx = cfd.searchsorted(x, side='right')
    sampled_element = a[idx]

1

यदि आपके पास पायथन 3 है, और numpyअपने स्वयं के लूप स्थापित करने या लिखने से डरते हैं , तो आप कर सकते हैं:

import itertools, bisect, random

def weighted_choice(choices):
   weights = list(zip(*choices))[1]
   return choices[bisect.bisect(list(itertools.accumulate(weights)),
                                random.uniform(0, sum(weights)))][0]

क्योंकि आप प्लंबिंग एडेप्टर के एक बैग से कुछ भी बना सकते हैं ! हालांकि ... मुझे मानना ​​होगा कि नेड का जवाब, जबकि थोड़ा लंबा है, समझना आसान है।


0

एक सामान्य समाधान:

import random
def weighted_choice(choices, weights):
    total = sum(weights)
    treshold = random.uniform(0, total)
    for k, weight in enumerate(weights):
        total -= weight
        if total < treshold:
            return choices[k]

0

यहाँ भारित_कॉपी का एक और संस्करण है जो सुन्न का उपयोग करता है। वेट वेक्टर में पास करें और यह 0 के एक सरणी को लौटाएगा जिसमें 1 का संकेत होगा जो बिन चुना गया था। कोड केवल एक ड्रॉ बनाने में चूक करता है लेकिन आप बनाए जाने वाले ड्रॉ की संख्या में पास हो सकते हैं और प्रति बिन ड्रा की गणना वापस आ जाएगी।

यदि वेट वेक्टर 1 के योग नहीं है, तो इसे सामान्य किया जाएगा ताकि यह हो।

import numpy as np

def weighted_choice(weights, n=1):
    if np.sum(weights)!=1:
        weights = weights/np.sum(weights)

    draws = np.random.random_sample(size=n)

    weights = np.cumsum(weights)
    weights = np.insert(weights,0,0.0)

    counts = np.histogram(draws, bins=weights)
    return(counts[0])

0

ऐसा करने का एक अन्य तरीका, यह मानते हुए कि हमारे पास तत्व सरणी में तत्वों के समान सूचकांक में वजन है।

import numpy as np
weights = [0.1, 0.3, 0.5] #weights for the item at index 0,1,2
# sum of weights should be <=1, you can also divide each weight by sum of all weights to standardise it to <=1 constraint.
trials = 1 #number of trials
num_item = 1 #number of items that can be picked in each trial
selected_item_arr = np.random.multinomial(num_item, weights, trials)
# gives number of times an item was selected at a particular index
# this assumes selection with replacement
# one possible output
# selected_item_arr
# array([[0, 0, 1]])
# say if trials = 5, the the possible output could be 
# selected_item_arr
# array([[1, 0, 0],
#   [0, 0, 1],
#   [0, 0, 1],
#   [0, 1, 0],
#   [0, 0, 1]])

अब मान लेते हैं, हमें 1 परीक्षण में 3 वस्तुओं का नमूना लेना है। आप मान सकते हैं कि तीन गेंदें आर, जी, बी हैं जो बड़ी मात्रा में वजन भार द्वारा दिए गए वजन के अनुपात में मौजूद हैं, निम्नलिखित संभव परिणाम हो सकते हैं:

num_item = 3
trials = 1
selected_item_arr = np.random.multinomial(num_item, weights, trials)
# selected_item_arr can give output like :
# array([[1, 0, 2]])

आप एक सेट के भीतर द्विपद / बहुराष्ट्रीय परीक्षणों की संख्या के रूप में चयनित होने वाली वस्तुओं की संख्या के बारे में भी सोच सकते हैं। तो, उपरोक्त उदाहरण अभी भी काम कर सकता है

num_binomial_trial = 5
weights = [0.1,0.9] #say an unfair coin weights for H/T
num_experiment_set = 1
selected_item_arr = np.random.multinomial(num_binomial_trial, weights, num_experiment_set)
# possible output
# selected_item_arr
# array([[1, 4]])
# i.e H came 1 time and T came 4 times in 5 binomial trials. And one set contains 5 binomial trails.

0

रोबोटिक्स के लिए फ्री यूडनेस कोर्स AI में सेबेस्टियन थर्न द्वारा इस पर व्याख्यान दिया गया है। मूल रूप से वह मॉड ऑपरेटर का उपयोग करके अनुक्रमित वज़न का एक गोलाकार सरणी बनाता है %, एक चर बीटा को 0 पर सेट करता है, बेतरतीब ढंग से एक सूचकांक चुनता है, एन के माध्यम से छोरों के लिए जहां एन सूचकांकों की संख्या है और लूप में पहले सूत्र द्वारा बीटा वृद्धि होती है:

{0 ... 2 * वेट_मैक्स} से बीटा = बीटा + एकसमान नमूना

और फिर लूप के लिए नेस्टेड, एक लूप प्रति नीचे:

while w[index] < beta:
    beta = beta - w[index]
    index = index + 1

select p[index]

फिर संभावनाओं (या पाठ्यक्रम में प्रस्तुत मामले में सामान्यीकृत संभावना) के आधार पर फिर से शुरू करने के लिए अगले सूचकांक पर।

व्याख्यान लिंक: https://classroom.udacity.com/courses/cs373/lessons/48704330/concepts/487480820923

मैं अपने स्कूल खाते के साथ यूडेसिटी में लॉग इन हूं, इसलिए यदि लिंक काम नहीं करता है, तो वह है 8, रोबोटिक्स के लिए आर्टिफिशियल इंटेलिजेंस का वीडियो नंबर 21, जहां वह कण फिल्टर पर व्याख्यान दे रहा है।


-1

एक तरीका यह है कि सभी भारों के कुल पर यादृच्छिकता और फिर प्रत्येक संस्करण के लिए सीमा बिंदुओं के रूप में मूल्यों का उपयोग करें। यहां एक जनरेटर के रूप में एक क्रूड कार्यान्वयन है।

def rand_weighted(weights):
    """
    Generator which uses the weights to generate a
    weighted random values
    """
    sum_weights = sum(weights.values())
    cum_weights = {}
    current_weight = 0
    for key, value in sorted(weights.iteritems()):
        current_weight += value
        cum_weights[key] = current_weight
    while True:
        sel = int(random.uniform(0, 1) * sum_weights)
        for key, value in sorted(cum_weights.iteritems()):
            if sel < value:
                break
        yield key

-1

सुन्न का उपयोग करना

def choice(items, weights):
    return items[np.argmin((np.cumsum(weights) / sum(weights)) < np.random.rand())]

NumPy के पास पहले से ही है np.random.choice, जैसा कि 2014 से स्वीकार किए गए उत्तर में उल्लेख किया गया है। अपना खुद का रोल करने की बात क्या है?
मार्क अमेरी

-1

मुझे इस तरह से वास्तव में तेजी से वास्तव में सरल कुछ करने की ज़रूरत थी, विचारों की खोज से मैंने आखिरकार इस टेम्पलेट का निर्माण किया। आइपीआई से एक जोंस के रूप में इस विचार को भारित मान प्राप्त होता है, जिसे यहां तानाशाह द्वारा अनुकरण किया जाता है।

फिर इसे एक सूची में अनुवादित करें, जिसमें प्रत्येक मान वजन के अनुपात में दोहराता है, और सूची से एक मूल्य का चयन करने के लिए बस random.choice का उपयोग करें।

मैंने इसे 10, 100 और 1000 पुनरावृत्तियों के साथ चलाने की कोशिश की। वितरण बहुत ठोस लगता है।

def weighted_choice(weighted_dict):
    """Input example: dict(apples=60, oranges=30, pineapples=10)"""
    weight_list = []
    for key in weighted_dict.keys():
        weight_list += [key] * weighted_dict[key]
    return random.choice(weight_list)

-1

मुझे उनमें से किसी का भी वाक्यविन्यास पसंद नहीं था। मैं वास्तव में यह बताना चाहता था कि आइटम क्या थे और प्रत्येक का भार कितना था। मुझे लगता है कि मैं इस्तेमाल कर सकता था random.choicesलेकिन इसके बजाय मैंने जल्दी से नीचे की कक्षा लिखी।

import random, string
from numpy import cumsum

class randomChoiceWithProportions:
    '''
    Accepts a dictionary of choices as keys and weights as values. Example if you want a unfair dice:


    choiceWeightDic = {"1":0.16666666666666666, "2": 0.16666666666666666, "3": 0.16666666666666666
    , "4": 0.16666666666666666, "5": .06666666666666666, "6": 0.26666666666666666}
    dice = randomChoiceWithProportions(choiceWeightDic)

    samples = []
    for i in range(100000):
        samples.append(dice.sample())

    # Should be close to .26666
    samples.count("6")/len(samples)

    # Should be close to .16666
    samples.count("1")/len(samples)
    '''
    def __init__(self, choiceWeightDic):
        self.choiceWeightDic = choiceWeightDic
        weightSum = sum(self.choiceWeightDic.values())
        assert weightSum == 1, 'Weights sum to ' + str(weightSum) + ', not 1.'
        self.valWeightDict = self._compute_valWeights()

    def _compute_valWeights(self):
        valWeights = list(cumsum(list(self.choiceWeightDic.values())))
        valWeightDict = dict(zip(list(self.choiceWeightDic.keys()), valWeights))
        return valWeightDict

    def sample(self):
        num = random.uniform(0,1)
        for key, val in self.valWeightDict.items():
            if val >= num:
                return key

-1

पूर्व-भारित सूची के साथ random.choice () प्रदान करें:

समाधान और परीक्षण:

import random

options = ['a', 'b', 'c', 'd']
weights = [1, 2, 5, 2]

weighted_options = [[opt]*wgt for opt, wgt in zip(options, weights)]
weighted_options = [opt for sublist in weighted_options for opt in sublist]
print(weighted_options)

# test

counts = {c: 0 for c in options}
for x in range(10000):
    counts[random.choice(weighted_options)] += 1

for opt, wgt in zip(options, weights):
    wgt_r = counts[opt] / 10000 * sum(weights)
    print(opt, counts[opt], wgt, wgt_r)

आउटपुट:

['a', 'b', 'b', 'c', 'c', 'c', 'c', 'c', 'd', 'd']
a 1025 1 1.025
b 1948 2 1.948
c 5019 5 5.019
d 2008 2 2.008
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.