web-dev-qa-db-ja.com

ジェネレーターでrandom.shuffle()を使用するにはどうすればよいですか? python

ジェネレーターからリストを初期化せずにジェネレーターで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()は、各スクリプトの実行後に同じ出力を返すように設計されていますか?

15
alvas

シーケンスを均一にシャッフルするために、random.shuffle()は入力の長さを知る必要があります。ジェネレータはこれを提供できません。 you haveそれをリストに具体化するには:

_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]
_
29
Martijn Pieters

すべての要素を一時的にどこかに保存せずにジェネレーターの歩留まりをランダム化することはできません。幸い、これはPythonでは非常に簡単です。

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

list()の呼び出しに注意してください。これにより、すべての項目が読み取られ、リストに入れられます。

すべての要素を保存したくない、または保存できない場合は、ジェネレーターを変更してランダムな順序で生成する必要があります。

3
Aaron Digulla

値を生成して計算を無駄にすることなく、シャッフルされた順序で要素を計算するのに費用がかかるように、この問題の解決策を見つける必要がありました。これは私があなたの例のために思いついたものです。これには、最初の配列にインデックスを付ける別の関数を作成することが含まれます。

Numpyをインストールする必要があります

pip install numpy

コード:

import numpy as np
x = [1, 2, 3, 4, 5, 6, 7, 8, 9]

def shuffle_generator(lst):
    return (lst[idx] for idx in np.random.permutation(len(lst)))

def yielding(ls):
    for i in ls:
        yield i

# for i in random.shuffle(yielding(x)):
#    print i

for i in yielding(shuffle_generator(x)):
    print(i)
1
beiller

任意の結果からサンプリングして、完全にランダムではなく、範囲内でいくらかシャッフルされたセットを生成できます。上記の@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)
1
Erik Aronesty

場合によっては、事前にデータの量がわかっている場合は、データにインデックスを付け、シャッフルされたインデックスに基づいてデータから計算/読み取りを行うことができます。これは次のようになります。「この問題にジェネレーターを使用しないでください」。特定のユースケースがなければ、一般的な方法を思い付くのは困難です。

または...ジェネレーターを使用する必要がある場合...

データが必要な「シャッフル」によって異なります。もちろん、人々が指摘しているように、ジェネレーターには長さがないので、ある時点でジェネレーターを評価する必要がありますが、これは高価になる可能性があります。完全なランダム性が必要ない場合は、シャッフルバッファを導入できます。

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のチャンクでシャッフルされるため、それが制限要因である場合は、メモリの問題を回避できます。もちろん、これは真にランダムなシャッフルではないため、sortedのようなものには使用しないでください。ただし、データにランダム性を追加する必要がある場合は、これが適切な解決策になる可能性があります。

1
sturgemeister

どうですか:

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
0
shacharf