从Python中的多集(计数器)中高效采样

时间:2014-04-13 05:21:32

标签: python

令人讨厌的是,以下内容并不奏效:

from collections import Counter
import random

c = Counter([1,1,1,1,0,0])
random.choice(c) # I expect this to return 1 with probability 2/3, 
                 # and 0 with probability 1/3.
                 # It actually returns 4 or 2, with probability 1/2

从Python(2.7)中的多集合中采样的惯用方法是什么?

编辑是的,我确实需要使用multiset。我的实际数据要大得多,只是将它存储在一个列表中是不切实际的。

编辑2 我需要以合理的效率执行此操作,因为我的代码会反复执行此操作。 Counter对象中将存储大量数据,任何涉及将所有这些数据复制到新数据结构中的内容都不是一个可行的解决方案。

4 个答案:

答案 0 :(得分:3)

来自docs

  

一个常见的任务是使用加权生成random.choice()   概率。

     

如果权重是小整数比,则一种简单的技术就是   用重复建立一个样本群:

>>> weighted_choices = [('Red', 3), ('Blue', 2), ('Yellow', 1), ('Green', 4)]
>>> population = [val for val, cnt in weighted_choices for i in range(cnt)]
>>> random.choice(population)
'Green'
     

更通用的方法是将权重排列为累积   使用itertools.accumulate()进行分发,然后找到随机数   bisect.bisect()的值:

>>> choices, weights = zip(*weighted_choices)
>>> cumdist = list(itertools.accumulate(weights))
>>> x = random.random() * cumdist[-1]
>>> choices[bisect.bisect(cumdist, x)]
'Blue'

对于您的应用程序,您可能希望使用计数器来构建选项列表和累积概率列表,然后使用第二种技术进行采样。

答案 1 :(得分:2)

您可以在python> = 3.6中使用the built in random.choices进行此操作

from collections import Counter
import random

c = Counter([1,1,1,1,0,0])
random.choices(list(c.keys()), weights=list(c.values()), k=1)

注释:保证dict键在python> = 3.7中的顺序,因此示例代码将在python> = 3.7中运行。但是在python 3.6中也可以使用类似的解决方案。

答案 2 :(得分:1)

我有类似的问题,但我的计数器反复变化,计数器中的元素数量通常很少(不超过100)

我最终使用以下内容作为更有效的解决方案

c = Counter([1,1,1,1,0,0])
random.choice(list(c.elements()))

答案 3 :(得分:1)

由于这个问题最近得到了一些关注,我想我会回答一下自己的问题。在Python中有效地执行此操作似乎涉及滚动您自己的代码,但我发现在machine learning blog上描述的算法即使集合的内容不断变化也是有效的,并且可以非常容易地实现。该博客文章包含一个基本的Python实现和指向快速Cython implementation的链接。