我有一个Python字典,其中键表示一些项目,值表示所述项目的一些(标准化)加权。例如:
d = {'a': 0.0625, 'c': 0.625, 'b': 0.3125}
# Note that sum([v for k,v in d.iteritems()]) == 1 for all `d`
鉴于项目与权重的这种相关性,我如何从d
中选择一个密钥,使得结果为'a'的时间为6.25%,结果为'b'的时间为32.25%,以及62.5%的结果是'c'?
答案 0 :(得分:8)
def weighted_random_by_dct(dct):
rand_val = random.random()
total = 0
for k, v in dct.items():
total += v
if rand_val <= total:
return k
assert False, 'unreachable'
应该做的伎俩。遍历每个密钥并保持运行总和,如果随机值(介于0和1之间)落入槽中,则返回该密钥
答案 1 :(得分:6)
从Python 3.6开始,您可以使用内置的random.choices()
,而不必使用Numpy。
然后,如果我们要从字典中采样(替换)25个键,其中值是被采样的权重/概率,我们可以简单地写:
import random
random.choices(list(my_dict.keys()), weights=my_dict.values(), k=25)
这将输出采样键列表:
['c', 'b', 'c', 'b', 'b', 'c', 'c', 'c', 'b', 'c', 'b', 'c', 'b', 'c', 'c', 'c', 'c', 'c', 'a', 'b']
如果只需要一个键,请将k
设置为1,然后从random.choices
返回的列表中提取单个元素:
random.choices(list(my_dict.keys()), weights=my_dict.values(), k=1)[0]
(如果不将my_dict.keys()
转换为列表,则会收到有关TypeType不可下标的TypeError信息。)
以下是docs中的相关代码段:
random.choices(人口,体重=无,*,cum_weights =无,k = 1)
返回从总体中选择的k个大小的元素列表,并进行替换。如果填充为空,则引发IndexError。
如果指定了权重序列,则会根据相对权重进行选择。或者,如果给出了cum_weights序列,则根据累积权重(可能使用itertools.accumulate()计算)进行选择。例如,相对权重[10、5、30、5]等于累积权重[10、15、45、50]。在内部,相对权重会在进行选择之前转换为累积权重,因此提供累积权重可以节省工作。
如果未指定权重或cum_weights,则选择的可能性均等。如果提供了权重序列,则其长度必须与总体序列的长度相同。同时指定权重和cum_weights是TypeError。
权重或cum_weights可以使用与random()返回的浮点值(包括整数,浮点和小数,但不包括小数)互操作的任何数值类型。权重假定为非负。
对于给定的种子,具有相同权重的choices()函数通常产生与重复调用choice()不同的序列。 choices()使用的算法使用浮点算法来实现内部一致性和速度。 choice()所使用的算法默认为带有重复选择的整数算术,以避免舍入误差产生小的偏差。
根据https://stackoverflow.com/a/39976962/5139284处的注释,对于小型数组,random.choices
更快,对于大型数组,numpy.random.choice
更快。 numpy.random.choice
还提供了一个无需替换即可进行采样的选项,而对此却没有内置的Python标准库功能。
答案 2 :(得分:4)
如果您计划大量执行此操作,则可以使用numpy
从使用np.random.choice()
的加权概率列表中选择您的密钥。以下示例将使用加权概率选择您的密钥10,000次。
import numpy as np
probs = [0.0625, 0.625, 0.3125]
keys = ['a', 'c', 'b']
choice_list = np.random.choice(keys, 10000, replace=True, p=probs)
答案 3 :(得分:2)
不确定您的用例是什么,但您可以查看NLTK包中的频率分布/概率分布类,它们处理所有细节。
FreqDist是计数器的扩展,可以传递给ProbDistI接口。 ProbDistI接口公开了一个“generate()”方法,该方法可用于对分布进行采样,以及可用于获取给定密钥概率的“prob(sample)”方法。
对于您的情况,您需要使用最大似然估计,因此MLEProbDist。如果你想平滑分布,可以试试LaplaceProbDist或SimpleGoodTuringProbDist。
例如:
from nltk.probability import FreqDist, MLEProbDist
d = {'a': 6.25, 'c': 62.5, 'b': 31.25}
freq_dist = FreqDist(d)
prob_dist = MLEProbDist(freq_dist)
print prob_dist.prob('a')
print prob_dist.prob('b')
print prob_dist.prob('c')
print prob_dist.prob('d')
将打印“0.0625 0.3125 0.625 0.0”。
要生成新样本,您可以使用:
prob_dist.generate()
答案 4 :(得分:0)
我所理解的:您需要一个简单的随机函数,它将在0和1之间统一生成一个随机数。如果该值介于0 to 0.0625
之间,您将选择键a
,如果它在0.0625 and (0.0625 + 0.625)
之间,那么您将选择键c
等。这就是answer中实际提到的内容。
由于随机数将统一生成,预期与较大权重相关的关键字与其他关键字相比将被选择得更多。
答案 5 :(得分:0)
如果你能够使用numpy,你可以使用numpy.random.choice功能,如下所示:
import numpy as np
d = {'a': 0.0625, 'c': 0.625, 'b': 0.3125}
def pick_by_weight(d):
d_choices = []
d_probs = []
for k,v in d.iteritems():
d_choices.append(k)
d_probs.append(v)
return np.random.choice(d_choices, 1, p=d_probs)[0]
d = {'a': 0.0625, 'c': 0.625, 'b': 0.3125}
choice = pick_by_weight(d)
答案 6 :(得分:0)
保留“倒置”字典可能很有用,其中键是权重值,值是您可以获得的键列表。这样,如果更多的键具有相同的权重,则更容易分发它:
from collections import defaultdict
import random
dict = {'a': 0.0625, 'd': 0.0625, 'c': 0.625, 'b': 0.3125}
inverted_dict = defaultdict(list)
for k, v in dict.items():
inverted_dict[v].append(k)
# Here first you get a random value between 0 and 1, which is your weigth
# Then, you choose a random value from the list of keys that have the same weight
print(random.choice(inverted_dict[random.choice(inverted_dict.keys())]))