如何在Python中从大字典中选择随机抽样而不转换为列表



我在Python 3.10中有一个(可能)巨大的字典,我想随机抽取一些值。唉,random.sample(my_dict, k)说:

TypeError: Population must be a sequence.  For dicts or sets, use sorted(d).

random.sample(my_dict.keys(), k)给出

DeprecationWarning: Sampling from a set deprecated
since Python 3.9 and will be removed in a subsequent version.

我不想支付将字典键转换为列表的成本,也不需要对它们进行排序。

有一个类似的老问题,但那是在Python弃用之前的东西,问这个问题的人不介意先转换成列表。

我也尝试多次运行random.choice来模拟random.sample。但更糟糕的是:当你在字典上使用它时,它只会抛出一个异常。(而不是给你一个合理的错误信息)

您需要使用sample(list(dct))(示例代码从原始字典中随机选择2个条目)

from random import sample
dct = {'a':1, 'b':2, 'c':3, 'd':4}
rnd_keys = sample(list(dct), 2)
# rnd_keys -> ['c', 'b']
rnd_dct = dict(sample(list(dct.items()), 2))
print(rnd_dct)

{'c': 3, 'b': 2}

不将huge dict转换为list(此转换使用O(n)空间和问题说,不要这样做)。您可以生成随机数基数len(dict)并使用enumerate,并且只获得idxrandom_idx匹配的k,v,并在到达zero基数random_number时从for循环中中断,我们想要选择(此中断有助于您不看到所有字典)。

from random import sample
dct = {'a':1, 'b':2, 'c':3, 'd':4}
# idx -^0^----^1^----^2^----^3^---
number_rnd = 2
rnd_idx = set(sample(range(len(dct)), number_rnd))
print(rnd_idx)
# {0, 3}
res = {}
for idx, (k,v) in enumerate(dct.items()):
if idx in rnd_idx:
res[k] = v
number_rnd -= 1
if number_rnd == 0:
break
print(res)
# {'b': 2, 'c': 3}

第三种方法感谢Tomerikoo,我们可以使用flip a coin的想法,在每次迭代items()时,我们可以生成一个随机的01,如果随机数是1,则将项目保存在结果dict中。(也许我们看到所有的dict项,但不选择所有的随机数,因为,也许我们得到许多随机的0.)

import random
dct = {'a':1, 'b':2, 'c':3, 'd':4}
number_rnd = 2
res = {}
for k,v in dct.items():
rnd_ch = random.getrandbits(1)
if rnd_ch:
res[k] = v
number_rnd -= 1
if number_rnd == 0:
break
print(res)

如果您可以将键放入列表中,则可以使用列表中的随机整数集来选择键。如果您不想将它们存储到列表中,则可以根据字典大小生成随机整数,对它们进行排序,然后遍历字典并采样,以获得与随机整数选择相匹配的索引。

最新更新