Как разбить список на куски одинакового размера? - PullRequest
1920 голосов
/ 23 ноября 2008

У меня есть список произвольной длины, и мне нужно разделить его на куски одинакового размера и обработать его. Есть несколько очевидных способов сделать это, например, сохранить счетчик и два списка, и когда второй список заполнится, добавить его в первый список и очистить второй список для следующего раунда данных, но это потенциально чрезвычайно дорого.

Мне было интересно, есть ли у кого-нибудь хорошее решение для списков любой длины, например, используя генераторы.

Я искал что-то полезное в itertools, но не смог найти что-то полезное. Возможно, я пропустил это.

Смежный вопрос: Какой самый «питонный» способ перебрать список по частям?

Ответы [ 58 ]

0 голосов
/ 23 мая 2019

Если вы не заботитесь о заказе:

> 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 голосов
/ 20 апреля 2019

Ленивая загрузка версии

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

Это работает, потому что команда slice будет возвращать меньше / нет элементов, если вы передали конец итерируемого:

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

Теперь мы используем этот результат среза и вычисляем длину этого сгенерированного фрагмента. Если это меньше, чем мы ожидаем, мы знаем, что можем закончить итерацию.

Таким образом, итератор не будет выполняться, пока не получен доступ.

0 голосов
/ 13 апреля 2019

Вы можете использовать Dask, чтобы разбить список на куски одинакового размера. 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 голосов
/ 07 декабря 2012

Никто не использует функцию tee () под itertools?

http://docs.python.org/2/library/itertools.html#itertools.tee

>>> import itertools
>>> itertools.tee([1,2,3,4,5,6],3)
(<itertools.tee object at 0x02932DF0>, <itertools.tee object at 0x02932EB8>, <itertools.tee object at 0x02932EE0>)

Это разделит список на 3 итератора, цикл итератор получит подсписок равной длины

0 голосов
/ 17 апреля 2018
def split(arr, size):
    L = len(arr)
    assert 0 < size <= L
    s, r = divmod(L, size)
    t = s + 1
    a = ([arr[p:p+t] for p in range(0, r*t, t)] + [arr[p:p+s] for p in range(r*t, L, s)])
    return a

вдохновлено http://wordaligned.org/articles/slicing-a-list-evenly-with-python

0 голосов
/ 26 мая 2013

с использованием списка понимания Python

[range(t,t+10) for t in range(1,1000,10)]

[[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],....
 ....[981, 982, 983, 984, 985, 986, 987, 988, 989, 990],
 [991, 992, 993, 994, 995, 996, 997, 998, 999, 1000]]

Посетите эту ссылку чтобы узнать о списках

0 голосов
/ 27 ноября 2013

Да, это старый вопрос, но мне пришлось опубликовать этот, потому что он даже немного короче аналогичных. Да, результат выглядит зашифрованным, но если он примерно равен длине ...

>>> n = 3 # number of groups
>>> biglist = range(30)
>>>
>>> [ biglist[i::n] for i in xrange(n) ]
[[0, 3, 6, 9, 12, 15, 18, 21, 24, 27],
 [1, 4, 7, 10, 13, 16, 19, 22, 25, 28],
 [2, 5, 8, 11, 14, 17, 20, 23, 26, 29]]
0 голосов
/ 24 сентября 2013
def chunked(iterable, size):
    chunk = ()

    for item in iterable:
        chunk += (item,)
        if len(chunk) % size == 0:
            yield chunk
            chunk = ()

    if chunk:
        yield chunk
...