27

シーケンスの要素を慣例的にバッチで処理するにはどうすればよいですか?

たとえば、シーケンス「abcdef」とバッチサイズが2の場合、次のようなことを行います。

for x, y in "abcdef":
    print "%s%s\n" % (x, y)
ab
cd
ef

もちろん、これは、それ自体が2つの要素を含むリストから1つの要素を想定しているため、機能しません。

リストの次のn個の要素をバッチで処理する、またはより大きな文字列からの長さnのサブ文字列を処理するための、短くてクリーンなpythonicの方法は何ですか(2つの同様の問題)。

4

17 に答える 17

15

長さが不明なイテラブルに対して機能する別のアプローチがあります。

   
def groupsgen(seq, size):
    it = iter(seq)
    while True:
        values = ()        
        for n in xrange(size):
            values += (it.next(),)        
        yield values    

サイズのグループでシーケンス (または他の反復子) を反復処理し、値をタプルに収集することによって機能します。各グループの最後に、タプルを生成します。

イテレータが値を使い果たすと、StopIteration 例外が生成されます。この例外は伝播され、groupsgen が値を使い果たしたことを示します。

値はサイズのセット (2、3 のセットなど) であると想定しています。そうでない場合、残った値は破棄されます。

于 2009-04-17T16:24:48.190 に答える
10

誰かがもっと「Pythonic」を考え出すと確信していますが、どうですか:

for y in range(0, len(x), 2):
    print "%s%s" % (x[y], x[y+1])

これは、あなたがそれを知っている場合にのみ機能することに注意してくださいlen(x) % 2 == 0;

于 2009-04-17T15:13:44.790 に答える
6

しかし、より一般的な方法は次のようになります(この回答に触発されました):

for i in zip(*(seq[i::size] for i in range(size))):
    print(i)                            # tuple of individual values
于 2009-04-17T15:30:53.390 に答える
6

そして、常にドキュメンテーションがあります。

def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = tee(iterable)
    try:
        b.next()
    except StopIteration:
        pass
    return izip(a, b)

def grouper(n, iterable, padvalue=None):
    "grouper(3, 'abcdefg', 'x') --> ('a','b','c'), ('d','e','f'), ('g','x','x')"
    return izip(*[chain(iterable, repeat(padvalue, n-1))]*n)

注: これらは、入力として文字列シーケンスが与えられると、部分文字列ではなくタプルを生成します。

于 2009-04-24T18:59:15.690 に答える
4
>>> a = "abcdef"
>>> size = 2
>>> [a[x:x+size] for x in range(0, len(a), size)]
['ab', 'cd', 'ef']

..または、リスト内包表記としてではありません:

a = "abcdef"
size = 2
output = []
for x in range(0, len(a), size):
    output.append(a[x:x+size])

または、ジェネレーターとして、複数回使用する場合に最適です (1 回使用する場合は、リスト内包表記がおそらく「最適」です)。

def chunker(thelist, segsize):
    for x in range(0, len(thelist), segsize):
            yield thelist[x:x+segsize]

..そしてそれは使用法です:

>>> for seg in chunker(a, 2):
...     print seg
... 
ab
cd
ef
于 2009-04-23T15:41:37.513 に答える
3

次のジェネレーターを作成できます

def chunks(seq, size):
    a = range(0, len(seq), size)
    b = range(size, len(seq) + 1, size)
    for i, j in zip(a, b):
        yield seq[i:j]

次のように使用します。

for i in chunks('abcdef', 2):
    print(i)
于 2009-04-17T15:14:36.283 に答える
2

more_itertoolsのドキュメントから:more_itertools.chunked()

more_itertools.chunked(iterable, n)

イテラブルを指定された長さのリストに分割します。

>>> list(chunked([1, 2, 3, 4, 5, 6, 7], 3))
[[1, 2, 3], [4, 5, 6], [7]]

iterable の長さが n で割り切れない場合、最後に返されるリストは短くなります。

于 2015-12-06T06:38:02.410 に答える
1

2つの回答を除いて、バッチで多くの時期尚早の実体化と添字付け(すべてのイテレータで機能するわけではありません)を見ました。したがって、私はこの代替案を思いつきました:

def iter_x_and_n(iterable, x, n):
    yield x
    try:
        for _ in range(n):
            yield next(iterable)
    except StopIteration:
        pass

def batched(iterable, n):
    if n<1: raise ValueError("Can not create batches of size %d, number must be strictly positive" % n)
    iterable = iter(iterable)
    try:
        for x in iterable:
            yield iter_x_and_n(iterable, x, n-1)
    except StopIteration:
        pass

これに対するワンライナーまたは少数ライナーのソリューションがないことは、私を打ち負かします(私の知る限りでは)。重要な問題は、外側のジェネレーターと内側のジェネレーターの両方が StopIteration を正しく処理する必要があることです。外部ジェネレーターは、少なくとも 1 つの要素が残っている場合にのみ何かを生成する必要があります。これを確認する直感的な方法は、next(...) を実行して StopIteration をキャッチすることです。

于 2015-09-30T10:03:23.963 に答える
1

itertools docには、このためのレシピがあります。

from itertools import izip_longest

def grouper(iterable, n, fillvalue=None):
    "Collect data into fixed-length chunks or blocks"
    # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx
    args = [iter(iterable)] * n
    return izip_longest(fillvalue=fillvalue, *args)

使用法:

>>> l = [1,2,3,4,5,6,7,8,9]
>>> [z for z in grouper(l, 3)]
[(1, 2, 3), (4, 5, 6), (7, 8, 9)]
于 2014-06-03T04:28:50.830 に答える
1

s = 'abcdefgh'
for e in (s[i:i+2] for i in range(0,len(s),2)):
  print(e)
于 2009-09-09T09:52:09.897 に答える
0

itertools はどうですか?

from itertools import islice, groupby

def chunks_islice(seq, size):
    while True:
        aux = list(islice(seq, 0, size))
        if not aux: break
        yield "".join(aux)

def chunks_groupby(seq, size):
    for k, chunk in groupby(enumerate(seq), lambda x: x[0] / size):
        yield "".join([i[1] for i in chunk])
于 2009-04-23T15:10:44.917 に答える