如何在生成器上使用random.shuffle()?蟒蛇

时间:2014-01-17 13:22:57

标签: python list random generator shuffle

如何在生成器上使用random.shuffle()而不从生成器初始化列表? 这甚至可能吗?如果没有,我还应该在列表中使用random.shuffle()

>>> import random
>>> random.seed(2)
>>> x = [1,2,3,4,5,6,7,8,9]
>>> def yielding(ls):
...     for i in ls:
...             yield i
... 
>>> for i in random.shuffle(yielding(x)):
...     print i
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/random.py", line 287, in shuffle
    for i in reversed(xrange(1, len(x))):
TypeError: object of type 'generator' has no len()

注意:random.seed()的设计是为了在每个脚本运行后返回相同的输出?

5 个答案:

答案 0 :(得分:27)

为了统一调整序列,random.shuffle()需要知道输入的时间长度。发电机不能提供这个;你 将其具体化为一个列表:

lst = list(yielding(x))
random.shuffle(lst)
for i in lst:
    print i

您可以使用sorted()作为关键字来使用random.random()

for i in sorted(yielding(x), key=lambda k: random.random()):
    print i

但是因为这个会产生一个列表,所以走这条路是没有意义的。

演示:

>>> import random
>>> x = [1,2,3,4,5,6,7,8,9]
>>> sorted(iter(x), key=lambda k: random.random())
[9, 7, 3, 2, 5, 4, 6, 1, 8]

答案 1 :(得分:3)

如果不暂时保存所有元素,则无法随机化生成器的产量。幸运的是,在Python中这很容易:

tmp = list(yielding(x))
random.shuffle(tmp)
for i in tmp:
    print i

请注意对list()的调用,该调用将读取所有项目并将其放入列表中。

如果您不想或不能存储所有元素,则需要将生成器更改为以随机顺序生成。

答案 2 :(得分:0)

根据情况,如果您提前知道有多少数据,则可以对数据进行索引并根据改组后的索引进行计算/读取。这等于:“不要为这个问题使用生成器”,而且如果没有特定的用例,很难提出一种通用方法。

或者...如果您需要使用发电机...

这取决于您想要数据的“混洗程度”。当然,就像人们指出的那样,生成器没有长度,因此您需要在某个时候评估生成器,这可能会很昂贵。如果您不需要完美的随机性,则可以引入随机播放缓冲区:

from itertools import islice

import numpy as np


def shuffle(generator, buffer_size):
    while True:
        buffer = list(islice(generator, buffer_size))
        if len(buffer) == 0:
            break
        np.random.shuffle(buffer)
        for item in buffer:
            yield item


shuffled_generator = shuffle(my_generator, 256)

这将以buffer_size的块为单位对数据进行混洗,因此如果这是您的限制因素,则可以避免出现内存问题。当然,这不是真正的随机混洗,因此不应将其用于排序上,但是,如果您只需要在数据中添加一些随机性,则这可能是一个很好的解决方案。 / p>

答案 3 :(得分:0)

您可以从任意产生的结果中采样,从而在一个范围内生成一个不完全随机但略有混乱的集合。类似于上面的@sturgemeister代码,但没有分块。...没有定义的随机性边界。

例如:

def scramble(gen, buffer_size):
    buf = []
    i = iter(gen)
    while True:
        try:
            e = next(i)
            buf.append(e)
            if len(buf) >= buffer_size:
                choice = random.randint(0, len(buf)-1)
                buf[-1],buf[choice] = buf[choice],buf[-1]
                yield buf.pop()
        except StopIteration:
            random.shuffle(buf)
            yield from buf
            return

结果应该在buffer_size窗口内完全随机:

for e in scramble(itertools.count(start=0, step=1), 1000):
    print(e)

对于此流中的任意1000个元素,它们是随机出现的。但是纵观整体趋势(超过1000),它显然正在增加。

要进行测试,断言这将返回1000个唯一元素:

for e in scramble(range(1000), 100):
    print(e)

答案 4 :(得分:0)

对于非常大的序列,如果您事先知道序列大小:

class subset_iterator:
    """
    an iterator class that returns K random samples from another sequence
    that has no random-access. Requires: the sequence length as input

    similar to random.sample

    :param it: iterator to the sequence
    :param seqlen: size of the sequence of :param it:
    :param K: output sequence size (number of samples in the subset)
    """

    def __init__(self, it, seqlen, K):
        self.it = it
        self.N = seqlen
        self.K = K

    def __iter__(self):
        return self

    def __next__(self):
        while True:
            r = random()
            nextitem = next(self.it)
            if r <= float(self.K) / self.N:
                self.K -= 1
                self.N -= 1
                return nextitem
            else:
                self.N -= 1