我有一个很长的python生成器,我想通过随机选择值的子集来“精简”它。不幸的是,random.sample()
不能处理任意的iterable。显然,它需要一些支持len()
操作的东西(也许还有对序列的非顺序访问,但这并不清楚)。我不想建立一个庞大的清单,只是为了把它精简。在
事实上,在不知道序列长度的情况下,一次从序列中均匀采样是可能的——在Programming perl
中有一个很好的算法可以做到这一点(编辑:“油藏采样”,谢谢@user2357112!)。但是有人知道有一个标准的python模块提供这个功能吗?在
问题演示(Python3)
>>> import itertools, random
>>> random.sample(iter("abcd"), 2)
...
TypeError: Population must be a sequence or set. For dicts, use list(d).
在Python 2上,错误更加透明:
^{pr2}$如果没有random.sample()
的替代方案,我会试试运气,将生成器包装到一个提供__len__
方法的对象中(我可以提前找到长度)。所以我会接受一个明确的答案。在
使用
O(n)
算法Rhttps://en.wikipedia.org/wiki/Reservoir_sampling,从iterable
中选择k
随机元素:示例:
^{pr2}${cd4{2}来自^代码。在
因为您知道iterable返回的数据的长度,所以可以使用
xrange()
快速生成iterable的索引。然后可以运行iterable,直到获取所有数据:另一种方法是使用“算法R”实现保留采样:
^{pr2}$注意,算法R没有为结果提供随机顺序。在给定的示例中,
'b'
永远不会在结果中的'a'
之前。如果您需要一个固定频率的原始迭代器的子集(即,如果生成器生成10000个数字,您希望“统计地”生成100个数字,如果它生成1000000个数字,则需要10000个数字—始终为1%),那么您应该将迭代器包装在一个构造中,生成概率为1%的内循环结果。在
因此,我想您需要的是一个固定数量的样本,这些样本来自于一个未知基数的源,就像您提到的Perl算法一样。在
您可以将迭代器包装在一个拥有自己的小内存的结构中,以便跟踪库,并以降低概率的方式循环它。在
所以
^{pr2}$可能会打印出来
我已经尝试生成100万发子弹,并用这个滤波器比较了三列的分布(我预期是高斯分布)。在
虽然还不是真正的高斯分布,但在我看来已经足够好了。在
相关问题 更多 >
编程相关推荐