web-dev-qa-db-ja.com

どのようにしてリストを均等なサイズのチャンクに分割しますか?

私は任意の長さのリストを持っています、そして私はそれを等しいサイズの塊に分割してそれを操作する必要があります。これを行うには、カウンターと2つのリストを保持し、2番目のリストがいっぱいになったら最初のリストに追加し、次のラウンドのデータ用に2番目のリストを空にするなど、明らかな方法があります。

長さのリストに対して誰かがこれに対する良い解決策を持っているかどうか私は思っていました。発電機を使用する。

私はitertoolsで便利なものを探していましたが、明らかに便利なものは見つかりませんでした。それを逃したかもしれません。

関連する質問: リストをチャンクで繰り返す最も「Pythonic」な方法は何ですか?

1886
jespern

これがあなたが望むチャンクを生み出すジェネレータです。

def chunks(l, n):
    """Yield successive n-sized chunks from l."""
    for i in range(0, len(l), n):
        yield l[i:i + n]

import pprint
pprint.pprint(list(chunks(range(10, 75), 10)))
[[10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
 [20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
 [30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
 [40, 41, 42, 43, 44, 45, 46, 47, 48, 49],
 [50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
 [60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
 [70, 71, 72, 73, 74]]

Python 2を使用している場合は、xrange()ではなくrange()を使用してください。

def chunks(l, n):
    """Yield successive n-sized chunks from l."""
    for i in xrange(0, len(l), n):
        yield l[i:i + n]

コードを理解しやすくするために、このような操作を名前付き関数にカプセル化することをお勧めしますが、関数を書く代わりに単にリスト内包表記を使用することもできます。 Python 3:

[l[i:i + n] for i in range(0, len(l), n)]

Python 2バージョン

[l[i:i + n] for i in xrange(0, len(l), n)]
2553
Ned Batchelder

あなたが超簡単なものが欲しいならば:

def chunks(l, n):
    n = max(1, n)
    return (l[i:i+n] for i in xrange(0, len(l), n))

Python 3.xの場合はrange()の代わりにxrange()を使用してください。

511
oremj

直接(古い)Pythonドキュメント(itertoolsのレシピ)から:

from itertools import izip, chain, repeat

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)

J.F.Sebastianによって提案された現在のバージョン:

#from itertools import izip_longest as Zip_longest # for Python 2.x
from itertools import Zip_longest # for Python 3.x
#from six.moves import Zip_longest # for both (uses the six compat library)

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

私はGuidoのタイムマシーンの仕事 - 仕事 - 仕事 - 仕事 - 仕事 - 再び仕事をすることになったと思います。

これらの解決策は、[iter(iterable)]*n(または以前のバージョンの同等物)が one iteratorを作成し、リスト内でn回繰り返されたために機能します。 izip_longestは、その後、 "each"イテレータのラウンドロビンを効果的に実行します。これは同じ反復子であるため、そのような呼び出しごとに進められ、結果としてそのようなZip-roundrobinはそれぞれn項目のTupleを1つ生成します。

266
tzot

私はこれがちょっと古いのを知っていますが、私は誰もnumpy.array_splitを言及しなかった理由はありません:

lst = range(50)
In [26]: np.array_split(lst,5)
Out[26]: 
[array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
 array([10, 11, 12, 13, 14, 15, 16, 17, 18, 19]),
 array([20, 21, 22, 23, 24, 25, 26, 27, 28, 29]),
 array([30, 31, 32, 33, 34, 35, 36, 37, 38, 39]),
 array([40, 41, 42, 43, 44, 45, 46, 47, 48, 49])]
145
Moj

iter2引数形式 を使うことを考えた人がいないのは驚きです。

from itertools import islice

def chunk(it, size):
    it = iter(it)
    return iter(lambda: Tuple(islice(it, size)), ())

デモ:

>>> list(chunk(range(14), 3))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13)]

これは任意のイテラブルに対して機能し、遅延的に出力を生成します。それはイテレータではなくタプルを返しますが、それでもやはりある優雅さを持っていると思います。パッドもありません。パディングが必要な場合は、上記の単純なバリエーションで十分です。

from itertools import islice, chain, repeat

def chunk_pad(it, size, padval=None):
    it = chain(iter(it), repeat(padval))
    return iter(lambda: Tuple(islice(it, size)), (padval,) * size)

デモ:

>>> list(chunk_pad(range(14), 3))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, None)]
>>> list(chunk_pad(range(14), 3, 'a'))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 'a')]

izip_longestベースのソリューションのように、上記の always padです。私の知る限りでは、 オプションで padという関数のための1行または2行のitertoolsレシピはありません。上記の2つのアプローチを組み合わせることで、これはかなり近づきます。

_no_padding = object()

def chunk(it, size, padval=_no_padding):
    if padval == _no_padding:
        it = iter(it)
        sentinel = ()
    else:
        it = chain(iter(it), repeat(padval))
        sentinel = (padval,) * size
    return iter(lambda: Tuple(islice(it, size)), sentinel)

デモ:

>>> list(chunk(range(14), 3))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13)]
>>> list(chunk(range(14), 3, None))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, None)]
>>> list(chunk(range(14), 3, 'a'))
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 'a')]

私はこれがオプションのパディングを提供していると提案されている最も短いチャンクだと思います。

Tomasz Gandor 観察された のように、2つのパディングチャンクが長い一連のパッド値に遭遇した場合、それらは突然停止します。これが、合理的な方法でその問題を回避する最終的なバリエーションです。

_no_padding = object()
def chunk(it, size, padval=_no_padding):
    it = iter(it)
    chunker = iter(lambda: Tuple(islice(it, size)), ())
    if padval == _no_padding:
        yield from chunker
    else:
        for ch in chunker:
            yield ch if len(ch) == size else ch + (padval,) * (size - len(ch))

デモ:

>>> list(chunk([1, 2, (), (), 5], 2))
[(1, 2), ((), ()), (5,)]
>>> list(chunk([1, 2, None, None, 5], 2, None))
[(1, 2), (None, None), (5, None)]
97
senderle

これは任意のイテラブルを処理するジェネレータです。

def split_seq(iterable, size):
    it = iter(iterable)
    item = list(itertools.islice(it, size))
    while item:
        yield item
        item = list(itertools.islice(it, size))

例:

>>> import pprint
>>> pprint.pprint(list(split_seq(xrange(75), 10)))
[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
 [10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
 [20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
 [30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
 [40, 41, 42, 43, 44, 45, 46, 47, 48, 49],
 [50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
 [60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
 [70, 71, 72, 73, 74]]
87
Markus Jarderot
def chunk(input, size):
    return map(None, *([iter(input)] * size))
49
Tomasz Wysocki

シンプルだがエレガント

l = range(1, 1000)
print [l[x:x+10] for x in xrange(0, len(l), 10)]

またはあなたが好めば:

chunks = lambda l, n: [l[x: x+n] for x in xrange(0, len(l), n)]
chunks(l, 10)
43
lebenf

私はこの質問の 重複 で最も素晴らしいPython風の答えを見ました。

from itertools import Zip_longest

a = range(1, 16)
i = iter(a)
r = list(Zip_longest(i, i, i))
>>> print(r)
[(1, 2, 3), (4, 5, 6), (7, 8, 9), (10, 11, 12), (13, 14, 15)]

任意のnに対してnタプルを作成できます。 a = range(1, 15)の場合、結果は次のようになります。

[(1, 2, 3), (4, 5, 6), (7, 8, 9), (10, 11, 12), (13, 14, None)]

リストが均等に分割されている場合は、Zip_longestZipに置き換えることができます。そうしないと、トリプレット(13, 14, None)が失われる可能性があります。 Python 3が上で使われています。 Python 2の場合はizip_longestを使用してください。

32
Noich

ここに他の答えの批評:

これらの答えはどれも均等な大きさのまとまりではありません、それらはすべて最後にラントのまとまりを残すので、それらは完全にバランスがとれていません。もしあなたがこれらの関数を使って仕事を分配していたのなら、あなたは他の人よりもずっと前に仕上げるという見込みを組み込んでいたので、他の人が懸命に働いている間は何もしないでしょう。

たとえば、現在のトップアンサーは次のように終わります。

[60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
[70, 71, 72, 73, 74]]

最後にあのラントが大嫌いです!

list(grouper(3, xrange(7)))chunk(xrange(7), 3)のような他のものは両方[(0, 1, 2), (3, 4, 5), (6, None, None)]を返します。 Noneは単にパディングされていて、私の意見ではむしろエレガントではありません。それらはイテラブルを均等にチャンクしていません。

どうしてこれらをうまく分割できないのでしょうか。

私の解決策

これは私が本番環境で使用した関数を基にしたバランスの取れた解決策です(Python 3ではxrangerangeに置き換えるための注意)。

def baskets_from(items, maxbaskets=25):
    baskets = [[] for _ in xrange(maxbaskets)] # in Python 3 use range
    for i, item in enumerate(items):
        baskets[i % maxbaskets].append(item)
    return filter(None, baskets) 

リストに入れても同じことができるジェネレータを作成しました。

def iter_baskets_from(items, maxbaskets=3):
    '''generates evenly balanced baskets from indexable iterable'''
    item_count = len(items)
    baskets = min(item_count, maxbaskets)
    for x_i in xrange(baskets):
        yield [items[y_i] for y_i in xrange(x_i, item_count, baskets)]

そして最後に、上記のすべての関数が(与えられたとおりに)連続した順序で要素を返すことがわかります。

def iter_baskets_contiguous(items, maxbaskets=3, item_count=None):
    '''
    generates balanced baskets from iterable, contiguous contents
    provide item_count if providing a iterator that doesn't support len()
    '''
    item_count = item_count or len(items)
    baskets = min(item_count, maxbaskets)
    items = iter(items)
    floor = item_count // baskets 
    ceiling = floor + 1
    stepdown = item_count % baskets
    for x_i in xrange(baskets):
        length = ceiling if x_i < stepdown else floor
        yield [items.next() for _ in xrange(length)]

出力

テストするには

print(baskets_from(xrange(6), 8))
print(list(iter_baskets_from(xrange(6), 8)))
print(list(iter_baskets_contiguous(xrange(6), 8)))
print(baskets_from(xrange(22), 8))
print(list(iter_baskets_from(xrange(22), 8)))
print(list(iter_baskets_contiguous(xrange(22), 8)))
print(baskets_from('ABCDEFG', 3))
print(list(iter_baskets_from('ABCDEFG', 3)))
print(list(iter_baskets_contiguous('ABCDEFG', 3)))
print(baskets_from(xrange(26), 5))
print(list(iter_baskets_from(xrange(26), 5)))
print(list(iter_baskets_contiguous(xrange(26), 5)))

どのプリントアウト:

[[0], [1], [2], [3], [4], [5]]
[[0], [1], [2], [3], [4], [5]]
[[0], [1], [2], [3], [4], [5]]
[[0, 8, 16], [1, 9, 17], [2, 10, 18], [3, 11, 19], [4, 12, 20], [5, 13, 21], [6, 14], [7, 15]]
[[0, 8, 16], [1, 9, 17], [2, 10, 18], [3, 11, 19], [4, 12, 20], [5, 13, 21], [6, 14], [7, 15]]
[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11], [12, 13, 14], [15, 16, 17], [18, 19], [20, 21]]
[['A', 'D', 'G'], ['B', 'E'], ['C', 'F']]
[['A', 'D', 'G'], ['B', 'E'], ['C', 'F']]
[['A', 'B', 'C'], ['D', 'E'], ['F', 'G']]
[[0, 5, 10, 15, 20, 25], [1, 6, 11, 16, 21], [2, 7, 12, 17, 22], [3, 8, 13, 18, 23], [4, 9, 14, 19, 24]]
[[0, 5, 10, 15, 20, 25], [1, 6, 11, 16, 21], [2, 7, 12, 17, 22], [3, 8, 13, 18, 23], [4, 9, 14, 19, 24]]
[[0, 1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16, 17, 18, 19, 20], [21, 22, 23, 24, 25]]

連続したジェネレータが他の2つと同じ長さパターンで塊を提供することに注意してください、しかし、項目はすべて正しいです、そして、1つが離散要素のリストを分けることができるのと同じくらい均等に分けられます。

32
Aaron Hall

あなたがリストサイズを知っているならば:

def SplitList(mylist, chunk_size):
    return [mylist[offs:offs+chunk_size] for offs in range(0, len(mylist), chunk_size)]

そうでなければ(イテレータ):

def IterChunks(sequence, chunk_size):
    res = []
    for item in sequence:
        res.append(item)
        if len(res) >= chunk_size:
            yield res
            res = []
    if res:
        yield res  # yield the last, incomplete, portion

後者の場合、シーケンスに常に一定サイズの整数個のチャンクが含まれていることを確認できれば(つまり、不完全な最後のチャンクはありません)、より美しい方法で言い換えることができます。

23
atzz

toolz ライブラリには、このためのpartition関数があります。

from toolz.itertoolz.core import partition

list(partition(2, [1, 2, 3, 4]))
[(1, 2), (3, 4)]
16
zach

たとえば、チャンクサイズが3の場合は、次のようにします。

Zip(*[iterable[i::3] for i in range(3)]) 

ソース: http://code.activestate.com/recipes/303060-group-a-list-into-sequential-n-tuples/ /

私のチャンクサイズが私がタイプできる固定数であるとき、私はこれを使うでしょう。 '3'、そして決して変わらないでしょう。

16
ninjagecko

ジェネレータ式:

def chunks(seq, n):
    return (seq[i:i+n] for i in xrange(0, len(seq), n))

例えば。

print list(chunks(range(1, 1000), 10))
16
jamylak

私はtzotとJ.F.Sebastianによって提案されたPythonドキュメントのバージョンが大好きですが、それには2つの欠点があります。

  • それほど明示的ではありません
  • 私は通常最後のチャンクにfill値を欲しくありません

私は私のコードでこれをたくさん使っています:

from itertools import islice

def chunks(n, iterable):
    iterable = iter(iterable)
    while True:
        yield Tuple(islice(iterable, n)) or iterable.next()

更新:怠惰なチャンクバージョン:

from itertools import chain, islice

def chunks(n, iterable):
   iterable = iter(iterable)
   while True:
       yield chain([next(iterable)], islice(iterable, n-1))
14
nikipore

現時点では、念のために 再帰的ジェネレータ が必要だと思います。

Python 2では:

def chunks(li, n):
    if li == []:
        return
    yield li[:n]
    for e in chunks(li[n:], n):
        yield e

Python 3の場合:

def chunks(li, n):
    if li == []:
        return
    yield li[:n]
    yield from chunks(li[n:], n)

また、大規模なエイリアンの侵入の場合、 装飾された再帰的ジェネレータ が便利になるかもしれません:

def dec(gen):
    def new_gen(li, n):
        for e in gen(li, n):
            if e == []:
                return
            yield e
    return new_gen

@dec
def chunks(li, n):
    yield li[:n]
    for e in chunks(li[n:], n):
        yield e
12
mazieres

私はさまざまなアプローチのパフォーマンスに興味を持っていました。

Python 3.5.1でテスト済み

import time
batch_size = 7
arr_len = 298937

#---------slice-------------

print("\r\nslice")
start = time.time()
arr = [i for i in range(0, arr_len)]
while True:
    if not arr:
        break

    tmp = arr[0:batch_size]
    arr = arr[batch_size:-1]
print(time.time() - start)

#-----------index-----------

print("\r\nindex")
arr = [i for i in range(0, arr_len)]
start = time.time()
for i in range(0, round(len(arr) / batch_size + 1)):
    tmp = arr[batch_size * i : batch_size * (i + 1)]
print(time.time() - start)

#----------batches 1------------

def batch(iterable, n=1):
    l = len(iterable)
    for ndx in range(0, l, n):
        yield iterable[ndx:min(ndx + n, l)]

print("\r\nbatches 1")
arr = [i for i in range(0, arr_len)]
start = time.time()
for x in batch(arr, batch_size):
    tmp = x
print(time.time() - start)

#----------batches 2------------

from itertools import islice, chain

def batch(iterable, size):
    sourceiter = iter(iterable)
    while True:
        batchiter = islice(sourceiter, size)
        yield chain([next(batchiter)], batchiter)


print("\r\nbatches 2")
arr = [i for i in range(0, arr_len)]
start = time.time()
for x in batch(arr, batch_size):
    tmp = x
print(time.time() - start)

#---------chunks-------------
def chunks(l, n):
    """Yield successive n-sized chunks from l."""
    for i in range(0, len(l), n):
        yield l[i:i + n]
print("\r\nchunks")
arr = [i for i in range(0, arr_len)]
start = time.time()
for x in chunks(arr, batch_size):
    tmp = x
print(time.time() - start)

#-----------grouper-----------

from itertools import Zip_longest # for Python 3.x
#from six.moves import Zip_longest # for both (uses the six compat library)

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

arr = [i for i in range(0, arr_len)]
print("\r\ngrouper")
start = time.time()
for x in grouper(arr, batch_size):
    tmp = x
print(time.time() - start)

結果:

slice
31.18285083770752

index
0.02184295654296875

batches 1
0.03503894805908203

batches 2
0.22681021690368652

chunks
0.019841909408569336

grouper
0.006506919860839844
11
Alex T

以下のように utilspie ライブラリの get_chunks 関数を使うこともできます。

>>> from utilspie import iterutils
>>> a = [1, 2, 3, 4, 5, 6, 7, 8, 9]

>>> list(iterutils.get_chunks(a, 5))
[[1, 2, 3, 4, 5], [6, 7, 8, 9]]

Pipを使って utilspie をインストールできます。

Sudo pip install utilspie

免責事項:私は utilspie library の作成者です。

11
[AA[i:i+SS] for i in range(len(AA))[::SS]]

AAが配列の場合、SSはチャンクサイズです。例えば:

>>> AA=range(10,21);SS=3
>>> [AA[i:i+SS] for i in range(len(AA))[::SS]]
[[10, 11, 12], [13, 14, 15], [16, 17, 18], [19, 20]]
# or [range(10, 13), range(13, 16), range(16, 19), range(19, 21)] in py3
10
Riaz Rizvi

コード:

def split_list(the_list, chunk_size):
    result_list = []
    while the_list:
        result_list.append(the_list[:chunk_size])
        the_list = the_list[chunk_size:]
    return result_list

a_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

print split_list(a_list, 3)

結果:

[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]
9
Art B

もう一つのより明確なバージョン。

def chunkList(initialList, chunkSize):
    """
    This function chunks a list into sub lists 
    that have a length equals to chunkSize.

    Example:
    lst = [3, 4, 9, 7, 1, 1, 2, 3]
    print(chunkList(lst, 3)) 
    returns
    [[3, 4, 9], [7, 1, 1], [2, 3]]
    """
    finalList = []
    for i in range(0, len(initialList), chunkSize):
        finalList.append(initialList[i:i+chunkSize])
    return finalList
7
Ranaivo
def split_seq(seq, num_pieces):
    start = 0
    for i in xrange(num_pieces):
        stop = start + len(seq[i::num_pieces])
        yield seq[start:stop]
        start = stop

使用法:

seq = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

for seq in split_seq(seq, 3):
    print seq
7
Corey Goldberg

ええと、1行バージョン

In [48]: chunk = lambda ulist, step:  map(lambda i: ulist[i:i+step],  xrange(0, len(ulist), step))

In [49]: chunk(range(1,100), 10)
Out[49]: 
[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
 [11, 12, 13, 14, 15, 16, 17, 18, 19, 20],
 [21, 22, 23, 24, 25, 26, 27, 28, 29, 30],
 [31, 32, 33, 34, 35, 36, 37, 38, 39, 40],
 [41, 42, 43, 44, 45, 46, 47, 48, 49, 50],
 [51, 52, 53, 54, 55, 56, 57, 58, 59, 60],
 [61, 62, 63, 64, 65, 66, 67, 68, 69, 70],
 [71, 72, 73, 74, 75, 76, 77, 78, 79, 80],
 [81, 82, 83, 84, 85, 86, 87, 88, 89, 90],
 [91, 92, 93, 94, 95, 96, 97, 98, 99]]
7
slav0nic

大きなリストに適しているlen()を呼び出さずに:

def splitter(l, n):
    i = 0
    chunk = l[:n]
    while chunk:
        yield chunk
        i += n
        chunk = l[i:i+n]

そしてこれはイテラブルのためのものです:

def isplitter(l, n):
    l = iter(l)
    chunk = list(islice(l, n))
    while chunk:
        yield chunk
        chunk = list(islice(l, n))

上記の機能的な風味:

def isplitter2(l, n):
    return takewhile(bool,
                     (Tuple(islice(start, n))
                            for start in repeat(iter(l))))

または:

def chunks_gen_sentinel(n, seq):
    continuous_slices = imap(islice, repeat(iter(seq)), repeat(0), repeat(n))
    return iter(imap(Tuple, continuous_slices).next,())

または:

def chunks_gen_filter(n, seq):
    continuous_slices = imap(islice, repeat(iter(seq)), repeat(0), repeat(n))
    return takewhile(bool,imap(Tuple, continuous_slices))
7
Mars

もう一つの解決策

def make_chunks(data, chunk_size): 
    while data:
        chunk, data = data[:chunk_size], data[chunk_size:]
        yield chunk

>>> for chunk in make_chunks([1, 2, 3, 4, 5, 6, 7], 2):
...     print chunk
... 
[1, 2]
[3, 4]
[5, 6]
[7]
>>> 

誰もがイテレータについて話しているので。 boltonsiterutils.chunked_iter と呼ばれるそのための完璧な方法を持っています。

from boltons import iterutils

list(iterutils.chunked_iter(list(range(50)), 11))

出力:

[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
 [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21],
 [22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32],
 [33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43],
 [44, 45, 46, 47, 48, 49]]

しかし、メモリを浪費したくないのであれば、古い方法で iterutils.chunked を使って最初の場所に完全なlistを格納することができます。

5
vishes_shell

この参照を参照してください

>>> orange = range(1, 1001)
>>> otuples = list( Zip(*[iter(orange)]*10))
>>> print(otuples)
[(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), ... (991, 992, 993, 994, 995, 996, 997, 998, 999, 1000)]
>>> olist = [list(i) for i in otuples]
>>> print(olist)
[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], ..., [991, 992, 993, 994, 995, 996, 997, 998, 999, 1000]]
>>> 

Python 3

5
macm

現時点では、必須の匿名再帰関数が必要だと思います。

Y = lambda f: (lambda x: x(x))(lambda y: f(lambda *args: y(y)(*args)))
chunks = Y(lambda f: lambda n: [n[0][:n[1]]] + f((n[0][n[1]:], n[1])) if len(n[0]) > 0 else [])
5
Julien Palard

matplotlib.cbook piecesの使用を検討してください。

例えば:

import matplotlib.cbook as cbook
segments = cbook.pieces(np.arange(20), 3)
for s in segments:
     print s
5
schwater
a = [1, 2, 3, 4, 5, 6, 7, 8, 9]
CHUNK = 4
[a[i*CHUNK:(i+1)*CHUNK] for i in xrange((len(a) + CHUNK - 1) / CHUNK )]
5
AdvilUser

これが追加のアプローチのリストです:

与えられた

import itertools as it
import collections as ct

import more_itertools as mit


iterable = range(11)
n = 3

コード

標準ライブラリ

list(it.Zip_longest(*[iter(iterable)] * n))
# [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, None)]
d = {}
for i, x in enumerate(iterable):
    d.setdefault(i//n, []).append(x)

list(d.values())
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10]]
dd = ct.defaultdict(list)
for i, x in enumerate(iterable):
    dd[i//n].append(x)

list(dd.values())
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10]]

more_itertools+

list(mit.chunked(iterable, n))
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10]]

list(mit.sliced(iterable, n))
# [range(0, 3), range(3, 6), range(6, 9), range(9, 11)]

list(mit.grouper(n, iterable))
# [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, None)]

list(mit.windowed(iterable, len(iterable)//n, step=n))
# [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, None)]

参考文献

+ itertoolsレシピを実装する - その他のサードパーティ製ライブラリ。 > pip install more_itertools

4
pylang

あなたはnumpyのarray_split関数、例えばnp.array_split(np.array(data), 20)を使って20個のほぼ等しいサイズのチャンクに分割することができます。

チャンクのサイズが正確に等しいことを確認するには、np.splitを使用してください。

4
AlexG
def chunks(iterable,n):
    """assumes n is an integer>0
    """
    iterable=iter(iterable)
    while True:
        result=[]
        for i in range(n):
            try:
                a=next(iterable)
            except StopIteration:
                break
            else:
                result.append(a)
        if result:
            yield result
        else:
            break

g1=(i*i for i in range(10))
g2=chunks(g1,3)
print g2
'<generator object chunks at 0x0337B9B8>'
print list(g2)
'[[0, 1, 4], [9, 16, 25], [36, 49, 64], [81]]'
4
robert king

リスト内包表記を使う:

l = [1,2,3,4,5,6,7,8,9,10,11,12]
k = 5 #chunk size
print [Tuple(l[x:y]) for (x, y) in [(x, x+k) for x in range(0, len(l), k)]]
4
Saksham Varma

私はこの質問は古くなっていることを認識しています(Googleでつまずいています)が、確かに次のようなものは非常に複雑で複雑な提案よりもはるかに単純で明確です。

def chunker(iterable, chunksize):
    for i,c in enumerate(iterable[::chunksize]):
        yield iterable[i*chunksize:(i+1)*chunksize]

>>> for chunk in chunker(range(0,100), 10):
...     print list(chunk)
... 
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
[20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
... etc ...
4
user1628890

この答え のように、一番上に投票された答えは、最後に 'おまけ'を残します。これは私の解決策です。それは基本的にそれがリストを分割するべきである小数点を正確に選ぶことを試みますが、最も近い整数にそれを丸めるだけです:

from __future__ import division  # not needed in Python 3
def n_even_chunks(l, n):
    """Yield n as even chunks as possible from l."""
    last = 0
    for i in range(1, n+1):
        cur = int(round(i * (len(l) / n)))
        yield l[last:cur]
        last = cur

デモンストレーション:

>>> pprint.pprint(list(n_even_chunks(list(range(100)), 9)))
[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
 [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21],
 [22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32],
 [33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43],
 [44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55],
 [56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66],
 [67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77],
 [78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88],
 [89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]]
>>> pprint.pprint(list(n_even_chunks(list(range(100)), 11)))
[[0, 1, 2, 3, 4, 5, 6, 7, 8],
 [9, 10, 11, 12, 13, 14, 15, 16, 17],
 [18, 19, 20, 21, 22, 23, 24, 25, 26],
 [27, 28, 29, 30, 31, 32, 33, 34, 35],
 [36, 37, 38, 39, 40, 41, 42, 43, 44],
 [45, 46, 47, 48, 49, 50, 51, 52, 53, 54],
 [55, 56, 57, 58, 59, 60, 61, 62, 63],
 [64, 65, 66, 67, 68, 69, 70, 71, 72],
 [73, 74, 75, 76, 77, 78, 79, 80, 81],
 [82, 83, 84, 85, 86, 87, 88, 89, 90],
 [91, 92, 93, 94, 95, 96, 97, 98, 99]]

トップ投票のchunks回答と比較してください:

>>> pprint.pprint(list(chunks(list(range(100)), 100//9)))
[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
 [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21],
 [22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32],
 [33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43],
 [44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54],
 [55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65],
 [66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76],
 [77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87],
 [88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98],
 [99]]
>>> pprint.pprint(list(chunks(list(range(100)), 100//11)))
[[0, 1, 2, 3, 4, 5, 6, 7, 8],
 [9, 10, 11, 12, 13, 14, 15, 16, 17],
 [18, 19, 20, 21, 22, 23, 24, 25, 26],
 [27, 28, 29, 30, 31, 32, 33, 34, 35],
 [36, 37, 38, 39, 40, 41, 42, 43, 44],
 [45, 46, 47, 48, 49, 50, 51, 52, 53],
 [54, 55, 56, 57, 58, 59, 60, 61, 62],
 [63, 64, 65, 66, 67, 68, 69, 70, 71],
 [72, 73, 74, 75, 76, 77, 78, 79, 80],
 [81, 82, 83, 84, 85, 86, 87, 88, 89],
 [90, 91, 92, 93, 94, 95, 96, 97, 98],
 [99]]
4
Claudiu

私はこの目的のために明示的に小さなライブラリを書きました。利用可能な ここ 。ライブラリのchunked関数は generator として実装されているので特に効率的です。そのため、状況によってはかなりの量のメモリを節約できます。スライス表記にも依存しないので、任意のイテレータを使用できます。

import iterlib

print list(iterlib.chunked(xrange(1, 1000), 10))
# prints [(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), (11, 12, 13, 14, 15, 16, 17, 18, 19, 20), ...]
3
rectangletangle

チャンクサイズをr、初期リストをLとすると、そうすることができます。

chunkL = [ [i for i in L[r*k:r*(k+1)] ] for k in range(len(L)/r)] 
3
Be Wake Pandey

私はその下でうまくいく解決策を一つ持っていますが、その解決策よりもっと重要なことは他のアプローチについてのいくつかのコメントです。まず、優れた解決策として、サブイテレータを順番に1回ループ処理する必要はありません。走れば

g = paged_iter(list(range(50)), 11))
i0 = next(g)
i1 = next(g)
list(i1)
list(i0)

最後のコマンドの適切な出力は次のとおりです。

 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

ではない

 []

Itertoolsベースのソリューションのほとんどがここに戻ってきます。これは、イテレータに順番にアクセスすることに関する通常の退屈な制限だけではありません。適切に入力された5のブロックの順序を逆にした、入力が不十分なデータをクリーンアップしようとしている消費者を想像してみてください。つまり、データは[B5、A5、D5、C5]のように見えます。 A5はサブリストではなく5つの要素です。このコンシューマは、グループ化関数の要求された動作を調べ、次のようなループを書くことを躊躇しません。

i = 0
out = []
for it in paged_iter(data,5)
    if (i % 2 == 0):
         swapped = it
    else: 
         out += list(it)
         out += list(swapped)
    i = i + 1

サブイテレータが常に完全に順番に使用されているとあなたがこっそりと仮定した場合、これは不思議なことに間違った結果を生むでしょう。チャンクの要素をインターリーブしたい場合はさらに悪くなります。

第二に、かなりの数の提案された解決策は、イテレータが決定論的順序を持つ(例えば設定されない)という事実に黙示的に依存しています。

第三に、itertoolsグルーパーのアプローチは機能しますが、レシピはZip_longest(またはZip)関数の内部動作に依存しますが、これは公開されている動作の一部ではありません。特に、grouper関数は、Zip_longest(i0 ... in)ではnext関数が常にnext(i0)、next(i1)、... next(in)の順で最初から呼び出されるためにのみ機能します。 grouperが同じ反復子オブジェクトのn個のコピーを渡すとき、それはこの振る舞いに依存します。

最後に、サブイテレータは順番にアクセスされ、この仮定なしで完全に熟読されるという上記の批判をした場合、暗黙のうちに(call chainを介して)または明示的に(dequesまたは他のデータ構造を介して)各副題についてはどこかで。ですから、(賢いやり方で)巧妙なトリックでこれを回避できると仮定しても、時間を無駄にする必要はありません。

def paged_iter(iterat, n):
    itr = iter(iterat)
    deq = None
    try:
        while(True):
            deq = collections.deque(maxlen=n)
            for q in range(n):
                deq.append(next(itr))
            yield (i for i in deq)
    except StopIteration:
        yield (i for i in deq)
3
Peter Gerdes

上記の答え(koffeinによる)には少し問題があります。リストはパーティションごとに等しい数の項目ではなく、常に同じ数の分割に分割されるということです。これは私のバージョンです。 "// chs + 1"は、アイテムの数がパーティションサイズで正確に分割できないことを考慮しているため、最後のパーティションは部分的にしか埋められません。

# Given 'l' is your list

chs = 12 # Your chunksize
partitioned = [ l[i*chs:(i*chs)+chs] for i in range((len(l) // chs)+1) ]
3
Flo

私はこのオプションを見たとは思わないので、別のものを追加するためだけに:)):

def chunks(iterable, chunk_size):
  i = 0;
  while i < len(iterable):
    yield iterable[i:i+chunk_size]
    i += chunk_size
3
George B

これがitertools.groupbyを使ったアイデアです。

def chunks(l, n):
    c = itertools.count()
    return (it for _, it in itertools.groupby(l, lambda x: next(c)//n))

これはジェネレータのジェネレータを返します。リストのリストが欲しい場合は、最後の行を単に置き換えるだけです。

    return [list(it) for _, it in itertools.groupby(l, lambda x: next(c)//n)]

リストのリストを返す例:

>>> chunks('abcdefghij', 4)
[['a', 'b', 'c', 'd'], ['e', 'f', 'g', 'h'], ['i', 'j']]

(そうです、これは「ラント問題」を起こします。それは与えられた状況では問題になるかもしれないし、そうでないかもしれません。)

3
itub
def chunk(lst):
    out = []
    for x in xrange(2, len(lst) + 1):
        if not len(lst) % x:
            factor = len(lst) / x
            break
    while lst:
        out.append([lst.pop(0) for x in xrange(factor)])
    return out
3
J.T. Hurley
>>> f = lambda x, n, acc=[]: f(x[n:], n, acc+[(x[:n])]) if x else acc
>>> f("Hallo Welt", 3)
['Hal', 'lo ', 'Wel', 't']
>>> 

あなたが括弧に入っているならば - 私はErlangの本を手に入れました:)

3
hcvst

魔法ではありませんが、シンプルで正しい:

def chunks(iterable, n):
    """Yield successive n-sized chunks from iterable."""
    values = []
    for i, item in enumerate(iterable, 1):
        values.append(item)
        if i % n == 0:
            yield values
            values = []
    if values:
        yield values
2
guyskk

私は、任意の繰り返し可能なオブジェクトで動作するはずの作成一時リストオブジェクトを使わずに、以下の解決策を見つけました。 Python 2.x用のこのバージョンに注意してください。

def chunked(iterable, size):
    stop = []
    it = iter(iterable)
    def _next_chunk():
        try:
            for _ in xrange(size):
                yield next(it)
        except StopIteration:
            stop.append(True)
            return

    while not stop:
        yield _next_chunk()

for it in chunked(xrange(16), 4):
   print list(it)

出力:

[0, 1, 2, 3]
[4, 5, 6, 7]
[8, 9, 10, 11]
[12, 13, 14, 15] 
[]

Len(iterable)%size == 0であればわかるように、空のイテレータオブジェクトが追加されています。でもそれが大きな問題だとは思わない。

2
Mikhail Lyundin
  • 任意のイテラブルで動作
  • 内部データはジェネレータオブジェクトです(リストではありません)
  • ワンライナー
 [259]:get_in_chunks =λitr、n:((vは_、vはg)、gはitertools.groupby(enumerate(itr)、lambda(ind、_):ind/n))
 
 [260]:list(get_in_chunksのxに対するlist(x)(範囲(30)、7))
 Out [260]:[.____ ] [[0、1、2、3、4、5、6]、[...] [7、8、9、10、11、12、13]、[...] [14、15 、. 16、17、18、19、20]、
 [21、22、23、24、25、26、27]、
 [28、29]] 
2
balki

@AaronHallのように、ここでは、ほぼ均等なサイズのチャンクを探しています。それについてはさまざまな解釈があります。私の場合は、希望するサイズがNの場合、各グループのサイズが> = Nになるようにします。したがって、上記のほとんどで作成された孤児は他のグループに再配布されるべきです。

これを使用して行うことができます。

def nChunks(l, n):
    """ Yield n successive chunks from l.
    Works for lists,  pandas dataframes, etc
    """
    newn = int(1.0 * len(l) / n + 0.5)
    for i in xrange(0, n-1):
        yield l[i*newn:i*newn+newn]
    yield l[n*newn-newn:]

(from リストをnChunks(l、l/n)またはnChunks(l、floor(l/n))として呼び出すことで、リストをほぼ等しい長さのN部分に分割します

2
CPBL

私はこのようなことをしなければならなかったので、これがジェネレータとバッチサイズを与えられた私の解決策です:

def pop_n_elems_from_generator(g, n):
    elems = []
    try:
        for idx in xrange(0, n):
            elems.append(g.next())
        return elems
    except StopIteration:
        return elems
2
Evan Zamir

まったく同じではないが、それでもニース

def chunks(l, chunks):
    return Zip(*[iter(l)]*chunks)

l = range(1, 1000)
print chunks(l, 10) -> [ ( 1..10 ), ( 11..20 ), .., ( 991..999 ) ]
2
Moss

これはv2/v3で動作し、インライン化でき、ジェネレータベースで、標準ライブラリのみを使用します。

import itertools
def split_groups(iter_in, group_size):
    return ((x for _, x in item) for _, item in itertools.groupby(enumerate(iter_in), key=lambda x: x[0] // group_size))
1
Andrey Cizov

チャンクサイズで要素を分割するという考えは嫌いです。スクリプトは101から3チャンクを[50、50、1]として分割できます。私のニーズのために、私は比例して分割し、順番を同じに保つ必要がありました。最初に私は自分自身のスクリプトを書きましたが、それはうまくいきます、そしてそれはとても簡単です。しかし、私は後で見ました この答え /スクリプトが私のものより優れているところで、私はそれをお勧めします。これが私のスクリプトです:

def proportional_dividing(N, n):
    """
    N - length of array (bigger number)
    n - number of chunks (smaller number)
    output - arr, containing N numbers, diveded roundly to n chunks
    """
    arr = []
    if N == 0:
        return arr
    Elif n == 0:
        arr.append(N)
        return arr
    r = N // n
    for i in range(n-1):
        arr.append(r)
    arr.append(N-r*(n-1))

    last_n = arr[-1]
    # last number always will be r <= last_n < 2*r
    # when last_n == r it's ok, but when last_n > r ...
    if last_n > r:
        # ... and if difference too big (bigger than 1), then
        if abs(r-last_n) > 1:
            #[2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 7] # N=29, n=12
            # we need to give unnecessary numbers to first elements back
            diff = last_n - r
            for k in range(diff):
                arr[k] += 1
            arr[-1] = r
            # and we receive [3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2]
    return arr

def split_items(items, chunks):
    arr = proportional_dividing(len(items), chunks)
    splitted = []
    for chunk_size in arr:
        splitted.append(items[:chunk_size])
        items = items[chunk_size:]
    print(splitted)
    return splitted

items = [1,2,3,4,5,6,7,8,9,10,11]
chunks = 3
split_items(items, chunks)
split_items(['a','b','c','d','e','f','g','h','i','g','k','l', 'm'], 3)
split_items(['a','b','c','d','e','f','g','h','i','g','k','l', 'm', 'n'], 3)
split_items(range(100), 4)
split_items(range(99), 4)
split_items(range(101), 4)

そして出力:

[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11]]
[['a', 'b', 'c', 'd'], ['e', 'f', 'g', 'h'], ['i', 'g', 'k', 'l', 'm']]
[['a', 'b', 'c', 'd', 'e'], ['f', 'g', 'h', 'i', 'g'], ['k', 'l', 'm', 'n']]
[range(0, 25), range(25, 50), range(50, 75), range(75, 100)]
[range(0, 25), range(25, 50), range(50, 75), range(75, 99)]
[range(0, 25), range(25, 50), range(50, 75), range(75, 101)]
1
Arthur Sult

python pydashパッケージは良い選択です。

from pydash.arrays import chunk
ids = ['22', '89', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '1']
chunk_ids = chunk(ids,5)
print(chunk_ids)
# output: [['22', '89', '2', '3', '4'], ['5', '6', '7', '8', '9'], ['10', '11', '1']]

より多くのチェックアウトのため pydash chunk list

0
Ravi Anand

遅延読み込みバージョン

import pprint
pprint.pprint(list(chunks(range(10, 75), 10)))
[range(10, 20),
 range(20, 30),
 range(30, 40),
 range(40, 50),
 range(50, 60),
 range(60, 70),
 range(70, 75)]

この実装の結果に 認められた答え の使用例の結果を渡します。

上記の関数の多くは、イテラブル全体の長さが前もってわかっているか、少なくとも計算が安価であると仮定しています。

一部のストリームオブジェクトでは、長さ情報を取得するために最初に全データをメモリにロードする(ファイル全体をダウンロードするなど)ことを意味します。

フルサイズがまだわからない場合は、代わりにこのコードを使用できます。

def chunks(iterable, size):
    """
    Yield successive chunks from iterable, being `size` long.

    https://stackoverflow.com/a/55776536/3423324
    :param iterable: The object you want to split into pieces.
    :param size: The size each of the resulting pieces should have.
    """
    i = 0
    while True:
        sliced = iterable[i:i + size]
        if len(sliced) == 0:
            # to suppress stuff like `range(max, max)`.
            break
        # end if
        yield sliced
        if len(sliced) < size:
            # our slice is not the full length, so we must have passed the end of the iterator
            break
        # end if
        i += size  # so we start the next chunk at the right place.
    # end while
# end def

Iterableの終わりを渡した場合、sliceコマンドは少ない/ no要素を返すのでこれはうまくいきます:

"abc"[0:2] == 'ab'
"abc"[2:4] == 'c'
"abc"[4:6] == ''

スライスの結果を使用して、生成されたチャンクの長さを計算します。それが予想よりも少なければ、繰り返しを終了できることがわかります。

そのようにすれば、アクセスしない限り、反復子は実行されません。

0
luckydonald

この質問は、Perl 6 .comb(n)メソッドを思い出させます。文字列をnサイズのチャンクに分割します。 (それだけではありませんが、詳細は省略します。)

Python3で同様の関数をラムダ式として実装するのは簡単です。

comb = lambda s,n: (s[i:i+n] for i in range(0,len(s),n))

その後、次のように呼び出すことができます。

some_list = list(range(0, 20))  # creates a list of 20 elements
generator = comb(some_list, 4)  # creates a generator that will generate lists of 4 elements
for sublist in generator:
    print(sublist)  # prints a sublist of four elements, as it's generated

もちろん、ジェネレーターを変数に割り当てる必要はありません。次のように直接ループすることができます:

for sublist in comb(some_list, 4):
    print(sublist)  # prints a sublist of four elements, as it's generated

ボーナスとして、このcomb()関数は文字列でも動作します:

list( comb('catdogant', 3) )  # returns ['cat', 'dog', 'ant']
0
J-L

あなたが順序を気にしないならば:

> from itertools import groupby
> batch_no = 3
> data = 'abcdefgh'

> [
    [x[1] for x in x[1]] 
    for x in 
    groupby(
      sorted(
        (x[0] % batch_no, x[1]) 
        for x in 
        enumerate(data)
      ),
      key=lambda x: x[0]
    )
  ]

[['a', 'd', 'g'], ['b', 'e', 'h'], ['c', 'f']]

このソリューションでは、同じサイズのセットは生成されませんが、生成されたバッチの数を維持しながらバッチができるだけ大きくなるように値が分配されます。

0
ajaest

Daskを使用して、リストを均等なサイズのチャンクに分割することができます。 Daskには、非常に大きなデータに最適なメモリー節約という追加の利点があります。リストが非常に大きい場合は、最良の結果を得るために、リストをダスクデータフレームに直接ロードしてメモリを節約する必要があります。あなたがまさにあなたがリストをどうしたいかによって、Daskはあなたが使うことができる機能の全体のAPIを持っています: http://docs.dask.org/en/latest/dataframe-api.html

import pandas as pd
import dask.dataframe as dd 

split = 4
my_list = range(100)
df = dd.from_pandas(pd.DataFrame(my_list), npartitions = split)
my_list = [ df.get_partition(n).compute().iloc[:,0].tolist() for n in range(split) ]

# [[1,2,3,..],[26,27,28...],[51,52,53...],[76,77,78...]]
0
N.Bell