如果我有一个列表中的项目集合,我想根据另一个权重列表从该列表中进行选择。
例如,我的集合是
最简单的方法是什么?
例如,我的集合是
['one','two','three']
,权重为[0.2,0.3,0.5]
,那么我希望该方法在大约一半的抽样中给我 'three'。最简单的方法是什么?
自从 numpy 版本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))
random.choices
进行带权重的随机选择(有替换)。
random.choices(population, weights=None, *, cum_weights=None, 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']
这个函数有两个参数:一个权重列表和一个包含要选择的对象的列表:
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循环。
cumsum()
给出的是累积值,而不是布尔值。要明确一点,这确实有效,但评论与实际发生的情况不符。 - Gareth Lattyelements = ['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 )
itertools.chain.from_iterable([elements[i]]*count, for i, count in enumerate(indices[0]))
пјЊиї™ж ·дјљж›ґеї«гЂ‚ - Gareth Lattyitertools.repeat(elements[i], count)
替换列表乘法来进一步改进它。 - Gareth Latty如果您不想使用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
。你可以将列表初始化为与期望权重匹配的选择。这里我创建了一个包含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
在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]))
l = [choice(elements, p=weights) for _ in range(1000)]
和from collections import Counter; Counter(l)
提供了以下结果:Counter({'three': 498, 'two': 281, 'one': 221})
。 - user2016508