如何从Python的Counter类中获得加权随机选择?

mat*_*tdm 12 python random counter iterator weighted

我有一个程序,我保持的各种成功的轨迹东西使用collections.Counter-的每个成功的事情递增相应的计数器:

import collections
scoreboard = collections.Counter()

if test(thing):
    scoreboard[thing]+ = 1
Run Code Online (Sandbox Code Playgroud)

然后,为了将来的测试,我想倾向于取得最大成功的事情.Counter.elements()似乎是理想的,因为它返回的元素(以任意顺序)重复多次等于计数.所以我想我可以做到:

import random
nextthing=random.choice(scoreboard.elements())
Run Code Online (Sandbox Code Playgroud)

但是,不会引发TypeError:'itertools.chain'类型的对象没有len().好的,所以random.choice无法使用迭代器.但是,在这种情况下,长度是已知的(或可知的) - 它是sum(scoreboard.values()).

我知道迭代未知长度列表并随机选择元素的基本算法,但我怀疑有更优雅的东西.我该怎么办?

Fel*_*her 8

您可以通过使用itertools.islice获取可迭代的第N项来轻松地执行此操作:

>>> import random
>>> import itertools
>>> import collections
>>> c = collections.Counter({'a': 2, 'b': 1})
>>> i = random.randrange(sum(c.values()))
>>> next(itertools.islice(c.elements(), i, None))
'a'
Run Code Online (Sandbox Code Playgroud)


pbs*_*sds 5

给定具有相应相对概率的选择字典(在您的情况下可以是计数),您可以使用random.choicesPython 3.6 中添加的新功能,如下所示:

import random

my_dict = {
    "choice a" : 1, # will in this case be chosen 1/3 of the time
    "choice b" : 2, # will in this case be chosen 2/3 of the time
}

choice = random.choices(*zip(*my_dict.items()))[0]
Run Code Online (Sandbox Code Playgroud)

对于使用 的代码Counter,您可以做同样的事情,因为Counter也有items()getter。

import collections
import random

my_dict = collections.Counter(a=1, b=2, c=3)
choice = random.choices(*zip(*my_dict.items()))[0]
Run Code Online (Sandbox Code Playgroud)

解释:my_dict.items()[('a', 1), ('b', 2), ('c', 3)]
所以zip(*my_dict.items())[('a', 'b', 'c'), (1, 2, 3)]
random.choices(('a', 'b', 'c'), (1, 2, 3))正是您想要的。