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
이것은 튜플 등 ( "ValueError : a must a 1-dimensional")에서 작동 하지 len(list_of_candidates)list_of_candidates[draw]
않으므로이

218

Python 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대체물로 모집단에서 선택한 크기가 지정된 요소 목록을 반환합니다 .

당신은 다음과 같이 교체없이 샘플을해야하는 경우 @ 로난 - paixão의 화려한 응답 상태, 당신은 사용할 수 있습니다 numpy.choice누구의 replace인수 제어 등의 동작을.


4
이것은 numpy.random.choice보다 훨씬 빠릅니다. numpy.random.choice는 8 개의 가중치 항목을 10,000 번 목록에서 선택하는 데 0.3286 초가 걸리고 무작위로 0.0416 초가 걸리고 약 8 배 더 빠릅니다.
Anton 코드

@AntonCodes이 예제는 체리 피킹입니다. numpy는 일정한 시간 오버 헤드를 random.choices가지지 않을 것입니다. 물론 8 개의 항목으로 구성된 미니리스트 목록에서는 속도가 느리고 그러한 목록에서 10k 번을 선택하면 맞습니다. 그러나 목록이 더 큰 경우 (테스트 방법에 따라 100-300 요소 사이의 중단 점이 있음) 상당히 넓은 간격으로 np.random.choice성능 random.choices이 향상 되기 시작합니다 . 예를 들어, numpy 호출과 함께 정규화 단계를 포함하여 random.choices10k 요소 목록에 대해 거의 4 배의 속도 향상을 얻습니다 .
ggorlen

@AntonCodes가보고 한 성능 개선을 기반으로 한 새로운 답변이어야합니다.
Wayne Workman

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
for 루프 내부의 명령문을 반대로하여 작업을 중단하고 시간을 절약 할 수 있습니다.upto +=w; if upto > r
knite

5
최대를 삭제하고 매번 가중치만큼 r을 줄임으로써 변수를 저장하십시오. 비교는 다음과 같습니다if r < 0
JnBrymn

@JnBrymn 확인해야합니다 r <= 0. 1 개 항목의 입력 세트와 1.0의 롤을 고려하십시오. 그러면 어설 션이 실패합니다. 답변에서 해당 오류를 수정했습니다.
moooeeeep

1
@Sardathrion pragma를 사용하여 for 루프를 부분적으로 표시 할 수 있습니다.# pragma: no branch
Ned Batchelder

1
@ mLstudent33 나는 Udacity를 사용하지 않습니다.
Anton 코드

70
  1. 가중치를 누적 분포로 배열합니다.
  2. random.random () 을 사용 하여 임의의 float을 선택하십시오 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
이것은 Ned의 답변보다 효율적입니다. 기본적으로 선택을 통해 선형 (O (n)) 검색을 수행하는 대신 이진 검색 (O (log n))을 수행합니다. +1!
NHDaly

random ()이 1.0을 반환하면 튜플 인덱스가 범위를 벗어남
Jon Vaughan

10
이것은 여전히 ​​실행 O(n)누적 분포 계산으로 인해 .
레프 레 비츠 키

6
이 솔루션은 동일한 선택 세트에 대해 weighted_choice를 여러 번 호출해야하는 경우에 더 좋습니다. 이 경우 누적 합계를 한 번 만들고 각 호출마다 이진 검색을 수행 할 수 있습니다.
Amos

1
@JonVaughan random() 1.0을 반환 할 수 없습니다 . 문서별로 반 열기 간격으로 결과를 반환합니다. [0.0, 1.0)즉 , 정확히 0.0을 반환 할 있지만 정확히 1.0을 반환 할 수는 없습니다 . 반환 할 수있는 가장 큰 값은 0.99999999999999988897769753748434595763683319091796875입니다 (파이썬은 0.9999999999999999로 인쇄하고 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을 더할 필요가 없으며 테스트 결과를 더 쉽게 해석하기 위해 그렇게했습니다. 가중치가 부동 소수점 숫자 인 경우 모든 가중치가 1보다 크거나 같을 때까지 반복적으로 10을 곱합니다.

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
이 예제에서는 객체가 복제 된 것 같습니다. 비효율적입니다 (무게를 정수로 변환하는 기능도 마찬가지입니다). 그럼에도 불구하고,이 솔루션은 정수 무게가 작은 경우 좋은 1 라이너입니다.
wei2912

프리미티브는 복제되지만 객체 자체는 아닌 참조가 복제됩니다. ( [[]]*10따라서 외부 목록의 모든 요소가 같은 목록을 가리키는 경우를 사용하여 목록의 목록을 만들 수 없습니다 .
PaulMcG

@PaulMcG 아니오; 참조 이외의 것은 중복되지 않습니다. 파이썬의 타입 시스템에는 프리미티브 개념이 없습니다. 예를 들어 int, 동일한 호출을 수행하여 동일한 객체에 대한 많은 참조를 얻고 있음을 확인할 수 있으며 모든 호출에서 동일한 메모리 주소 [id(x) for x in ([99**99] * 100)]id반환합니다.
Mark Amery

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
이것은 작은 전체 인구 값에는 적용되지만 큰 데이터 세트에는 적용되지 않습니다 (예 : 주별 미국 인구는 3 억 개의 항목으로 작업 목록을 작성하게됩니다).
Ryan

@Ryan 참으로. 또한 다른 현실적인 시나리오 인 정수가 아닌 가중치에 대해서도 작동하지 않습니다 (예 : 가중치가 선택 확률로 표시되는 경우).
Mark Amery

12

파이썬으로 v3.6, random.choices반환하는 데 사용할 수있는 list옵션 가중치 주어진 인구에서 지정된 크기의 요소를.

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

  • 인구 : list고유 한 관측치 포함. (비어 있으면 IndexError)

  • 가중치 : 선택에 필요한보다 정확한 상대 가중치.

  • cum_weights : 선택에 필요한 누적 가중치입니다.

  • k : 출력 할 크기 ( len) list. (기본 len()=1)


몇 가지주의 사항 :

1) 추첨 된 품목을 나중에 교체 할 수 있도록 가중 샘플링을 교체와 함께 사용합니다. 가중치 시퀀스 자체의 값 자체는 중요하지 않지만 상대적 비율은 중요합니다.

np.random.choice확률을 가중치로만 취할 수 있고 개별 확률을 1 기준까지 합산해야하는 것과는 달리 , 여기에는 그러한 규정이 없습니다. 숫자 유형 ( type 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 를 지정 하지 않으면 동일한 확률로 선택됩니다. 경우 무게 시퀀스가 공급되고,이 같은 길이이어야 인구 순서.

weightscum_weights를 모두 지정 하면 a가 발생합니다 TypeError.

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

3) cum_weights 는 일반적으로 itertools.accumulate그러한 상황에서 실제로 유용한 기능 의 결과입니다 .

링크 된 문서에서 :

내부적으로, 상대 가중치는 선택하기 전에 누적 가중치로 변환되므로 누적 가중치를 제공하면 작업이 줄어 듭니다.

따라서 우리의 사례를 제공 weights=[12, 12, 4]하거나 cum_weights=[12, 24, 28]우리의 사례에 대해 동일한 결과를 산출하고 후자는 더 빠르거나 효율적인 것으로 보입니다.


11

다음은 Python 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로 벡터를 만들 필요가 없으며 선택을 찾으면 종료됩니다. 메모리 : O (1), 시간 : O (N), 평균 실행 시간 ~ N / 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단의 최대 수 리턴 값보다 작은, 0.9999999999999998로 합산한다 random.random(0.9999999999999999이다). 그렇다면 choice <= cmf결코 만족하지 않습니다.
Mark Amery

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

Python 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

다음은 numpy를 사용하는 다른 버전의 weighted_choice입니다. 가중치 벡터를 전달하면 선택된 빈을 나타내는 1을 포함하는 0의 배열을 반환합니다. 코드는 기본적으로 하나의 그리기를 수행하지만 그리기 수를 전달할 수 있으며 빈당 카운트가 반환됩니다.

가중치 벡터의 합이 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]])

이제 한 번의 시도로 3 개의 항목을 샘플링해야한다고 가정 해 봅시다. 가중치 배열로 주어진 가중치의 비율로 많은 양의 볼 R, G, B가 있다고 가정 할 수 있으며 다음과 같은 결과가 발생할 수 있습니다.

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

로봇 공학을위한 무료 Udacity 코스 AI에서 Sebastien Thurn의 강의가 있습니다. 기본적으로 그는 mod 연산자를 사용하여 인덱스 가중치의 원형 배열을 만들고, %변수 베타를 0으로 설정하고, N을 통한 루프의 경우, N은 인덱스 수이고, for 루프는 먼저 공식에 의해 베타를 증가시키는 인덱스를 임의로 선택합니다.

베타 = 베타 + {0 ... 2 * Weight_max}의 균일 샘플

그런 다음 for 루프에 아래에 while 루프가 중첩됩니다.

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

select p[index]

다음 확률로 확률 (또는 코스에 제시된 경우 정규화 된 확률)을 기반으로 다시 샘플링하려면 다음 인덱스로 이동하십시오.

강의 링크 : https://classroom.udacity.com/courses/cs373/lessons/48704330/concepts/487480820923

학교 계정으로 Udacity에 로그인 했으므로 링크가 작동하지 않으면 입자 필터를 강의하는 인공 지능 로봇 공학의 비디오 번호 21 인 8과입니다.


-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

numpy 사용

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

NumPy는 이미 np.random.choice2014 년 이후로 받아 들여진 답변에서 언급 했듯이을 (를) 보유 하고 있습니다.
Mark Amery

-1

마지막으로이 템플릿을 구축 한 아이디어를 검색하여 이처럼 매우 빠르고 간단한 작업을 수행해야했습니다. 아이디어는 api에서 json 형태의 가중치 값을 수신하며 여기에서 dict에 의해 시뮬레이트됩니다.

그런 다음 각 값이 가중치에 비례하여 반복되는 목록으로 변환하고 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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.