53

如果我在列表中有一组项目。我想根据另一个权重列表从该列表中进行选择。

例如,我的收藏是['one', 'two', 'three'],权重是[0.2, 0.3, 0.5],我希望该方法在大约一半的抽奖中给我“三”。

最简单的方法是什么?

4

7 回答 7

94

1.7 版开始,您可以使用numpy.random.choice()

elements = ['one', 'two', 'three'] 
weights = [0.2, 0.3, 0.5]

from numpy.random import choice
print(choice(elements, p=weights))
于 2013-04-09T16:21:04.033 回答
43

从 Python 3.6 开始,您可以使用random.choices.

随机的。选择人口,权重=无,*,cum_weights=无,k=1

示例用法:

import random
random.choices(['one', 'two', 'three'], [0.2, 0.3, 0.5], k=10)
# ['three', 'two', 'three', 'three', 'three',
#  'three', 'three', 'two', 'two', 'one']
于 2017-02-03T15:07:17.277 回答
11

这个函数有两个参数:一个权重列表和一个包含可供选择的对象的列表:

from numpy import cumsum
from numpy.random import rand
def weightedChoice(weights, objects):
    """Return a random item from objects, with the weighting defined by weights 
    (which must sum to 1)."""
    cs = cumsum(weights) #An array of the weights, cumulatively summed.
    idx = sum(cs < rand()) #Find the index of the first weight over a random value.
    return objects[idx]

它不使用任何 python 循环。

于 2012-05-29T16:30:10.027 回答
5

您可以使用多项分布(来自 numpy)来做您想做的事。例如

elements = ['one', 'two', 'three'] 
weights = [0.2, 0.3, 0.5]


import numpy as np

indices = np.random.multinomial( 100, weights, 1)
#=> array([[20, 32, 48]]), YMMV

results = [] #A list of the original items, repeated the correct number of times.
for i, count in enumerate(indices[0]):
    results.extend( [elements[i]]*count )

因此,第一个位置的元素出现了 20 次,第二个位置的元素出现了 32 次,第三个位置的元素出现了 48 次,这与你对权重的预期大致相同。

如果你很难理解多项分布,我发现文档真的很有帮助。

于 2012-05-29T17:01:19.267 回答
4

如果您不想使用numpy,您可以使用相同的方法,如下所示:

from random import random
from itertools import takewhile

def accumulate(iterator):
    """Returns a cumulative sum of the elements.
    accumulate([1, 2, 3, 4, 5]) --> 1 3 6 10 15"""
    current = 0
    for value in iterator:
        current += value
        yield current

def weightedChoice(weights, objects):
    """Return a random item from objects, with the weighting defined by weights 
    (which must sum to 1)."""
    limit = random()
    return objects[sum(takewhile(bool, (value < limit for value in accumulate(weights))))]

一旦我们itertools.takewhile()到达我们想要停止的点,我们就会避免检查值,否则,这与Mischa Obrecht的答案基本相同,只是没有numpy

于 2012-05-29T17:01:02.370 回答
2

如何初始化您的列表以使您的选择与预期的权重相匹配。在这里,我列出了 100 个值,代表您想要的“拉动”百分比。

>>> import random
>>> elements = ['one', 'two', 'three'] 
>>> weights = [0.2, 0.3, 0.5]
>>>
>>> # get "sum" of result list of lists (flattens list)
>>> choices = sum([[element] * int(weight * 100)for element, weight in zip(elements, weights)], [])
>>> random.choice(choices)
three

它不是累积的,但看起来它可能是您正在寻找的。

于 2012-06-11T05:15:23.307 回答
1

为了建立Maus 的答案,如果您想重复获得加权随机值,这非常好,如果您只想要一个值,您可以通过组合numpy.random.multinomial()和非常简单地做到这一点itertools.compress()

from itertools import compress
from numpy.random import multinomial

def weightedChoice(weights, objects):
    """Return a random item from objects, with the weighting defined by weights 
    (which must sum to 1)."""
    return next(compress(objects, multinomial(1, weights, 1)[0]))
于 2012-05-29T17:14:41.143 回答