如何根据特定概率选择列表中的项目?

时间:2017-04-20 21:30:02

标签: python-3.x random probability-density

我们假设我们得到了如下列表:

list = [[A,10,3],[B,5,2],[C,8,1]]

对于列表中的每个项目,可以选择可以通过softmax计算的概率。例如,对于第一个元素(A),我们有:

from math import exp

A_probability = exp(list[0][2]/list[0][1] /
                     (exp(list[0][2]/list[0][1]) +
                      exp(list[1][2]/list[1][1]) +
                      exp(list[2][2]/list[2][1])))

如何根据每个项目的可能性随机选择列表中的项目?

1 个答案:

答案 0 :(得分:3)

我假设您有一个预先计算的概率列表(比如probs),列表中的每个索引(比如data)都要选择。

此外,probsdata显然必须具有相同的长度,probs的条目必须是非负数,总计为1

根据data中被称为轮盘赌轮的分布,有一种简洁而简单的技术可以随机选择probs中的索引。在Python中,我相信,它应该看起来像这样

import random

data = ['A', 'B', 'C', 'D']

probs = [0.2, 0.4, 0.3, 0.1]

def roulette_wheel(probs):
    rand = random.random()
    for slot, prob in enumerate(probs):
        rand -= prob
        if rand < 0.0:
            return slot

请注意,这可以通过将1乘以术语rand推广到非负权重列表(不必加sum(weights))。我相信,我第一次在一本关于Pascal编程的书中看到了这个可爱的想法。

修改

正如MadPhysicist在comment中所建议的那样,如果需要从同一数据中反复绘制,这可以提高效率。在这种情况下,可以预先计算累积分布函数,然后只对索引执行二进制搜索,使cumulative prob. <= rand ~ U(0, 1)。在Python中,这可能看起来像以下

from random import random
from bisect import bisect_right


def cdf(probs):
    cdf = []
    total = 0.0
    for p in probs:
        total += p
        cdf.append(total)
    return cdf


def roulette_wheel_bisect(cdf):
    return bisect_right(cdf, random())

# compute cdf
cumsum = cdf(probs)

# randomly draw 10 indexes 
for i in range(0, 10):
    print(roulette_wheel_bisect(cumsum))

免责声明:我不是一个交易的Python程序员,所以上面的代码应该只说明一般的想法。对于实际应用来说,它可能不是很强大。例如,如果可以,您应该始终使用经过良好测试的标准库numpy

<强> EDIT2

我刚刚了解到numpynumpy.random.choice,它正是您所需要的。例如:

from numpy import random

data = ['A', 'B', 'C', 'D']
probs = [0.2, 0.4, 0.3, 0.1]

# randomly draw 10 list elements with replacement
for i in range(0, 10):
    print(random.choice(data, p=probs))