У меня есть список произвольной длины, и мне нужно разделить его на куски одинакового размера и обработать его. Есть несколько очевидных способов сделать это, например, сохранить счетчик и два списка, и, когда второй список заполнится, добавить его в первый список и очистить второй список для следующего раунда данных, но это потенциально чрезвычайно дорого.
Мне было интересно, есть ли у кого-нибудь хорошее решение для списков любой длины, например, с использованием генераторов.
Я искал что-то полезное в, itertools
но я не мог найти ничего явно полезного. Возможно, я пропустил это.
Смежный вопрос: Какой самый «питонный» способ перебирать список по частям?
Ответы:
Вот генератор, который дает нужные вам куски:
Если вы используете Python 2, вы должны использовать
xrange()
вместоrange()
:Также вы можете просто использовать списочное понимание вместо написания функции, хотя было бы неплохо инкапсулировать подобные операции в именованных функциях, чтобы ваш код было легче понять. Python 3:
Версия Python 2:
источник
Если вы хотите что-то супер простое:
Используйте
xrange()
вместоrange()
Python 2.xисточник
max()
.Непосредственно из (старой) документации Python (рецепты для itertools):
Текущая версия, предложенная JFSebastian:
Я предполагаю, что машина времени Гвидо работает - работала - будет работать - будет работать - снова работала.
Эти решения работают, потому что
[iter(iterable)]*n
(или эквивалент в более ранней версии) создает один итератор, повторяющийсяn
раз в списке.izip_longest
затем эффективно выполняет циклический перебор «каждого» итератора; поскольку это один и тот же итератор, он продвигается при каждом таком вызове, в результате чего каждый такой zip-циклический цикл генерирует один кортежn
элементов.источник
list(grouper(3, range(10)))
возвращается[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, None, None)]
, и все кортежи имеют длину 3. Пожалуйста, опишите ваш комментарий, потому что я не могу его понять; что вы называете вещью, и как вы определяете, что она кратна 3 в «ожидании, что ваша вещь будет кратна 3»? Заранее спасибо.itertools
функционального подхода, превращающего некоторый нечитаемый шлам по сравнению с простой и наивной чистой реализацией Pythonl==[1, 2, 3]
тогдаf(*l)
эквивалентноf(1, 2, 3)
. Смотрите этот вопрос и официальную документацию .Я знаю, что это старое, но никто еще не упомянул
numpy.array_split
:источник
Я удивлен, что никто не думал об использовании формы
iter
с двумя аргументами :Демо-версия:
Это работает с любой итерацией и производит ленивый вывод. Он возвращает кортежи, а не итераторы, но, тем не менее, он имеет определенную элегантность. Это также не дополняет; если вы хотите заполнить, достаточно простого варианта выше:
Демо-версия:
Как и
izip_longest
решения на основе, выше всегда колодки. Насколько я знаю, не существует одно- или двухстрочного рецепта itertools для функции, которая может быть дополнена . Комбинируя два вышеупомянутых подхода, этот подход довольно близок:Демо-версия:
Я полагаю, что это самый короткий предложенный кусок, который предлагает дополнительное заполнение.
Как заметил Томаш Гандор , два чанкера заполнения неожиданно остановятся, если встретят длинную последовательность значений пэдов. Вот последний вариант, который разумным образом решает эту проблему:
Демо-версия:
источник
islice(it, size)
выражение и встроили его (как я сделал) в конструкцию цикла. Только вы подумали о версии с двумя аргументамиiter()
(я совершенно не знал), что делает ее супер-элегантной (и, вероятно, наиболее эффективной с точки зрения производительности). Я понятия не имел, что первый аргумент дляiter
изменения функции с 0 аргументами при задании дозорного. Вы возвращаете (пот. Бесконечный) итератор чанков, можете использовать (пот. Бесконечный) итератор в качестве входных данных, не иметьlen()
и не иметь срезов массива. Потрясающие!it
итератору. Во-вторых, и самое важное - вы преждевременно прекратите работу, еслиpadval
в вашей итерируемой части действительно существует кусок, который должен быть обработан.izip_longest
подходом, например - я подозреваю, что это может быть сложным компромиссом. Но ... развеpadval
проблема не разделена каждым ответом здесь, который предлагаетpadval
параметр?()
качестве дозорных, делает работу правильно Это происходит потому,tuple(islice(it, size))
урожаи ,()
когдаit
пусто.)Вот генератор, который работает с произвольными итерациями:
Пример:
источник
источник
map(None, iter)
равноizip_longest(iter)
.*
перед вами кортеж итератора? Возможно, в вашем тексте ответа, но я заметил, что*
раньше это использовалось в Python. Спасибо!Простой, но элегантный
или если вы предпочитаете:
источник
1
иl
неразличимы. Как есть0
иO
. А иногда дажеI
и1
.print [l[x:x+10] for x in xrange(1, len(l), 10)]
range
.Критика других ответов здесь:
Ни один из этих ответов не является кусками одинакового размера, все они в конце оставляют кусочек с разбуханием, поэтому они не полностью сбалансированы. Если бы вы использовали эти функции для распределения работы, вы встроили перспективу того, что одна из них, вероятно, закончится задолго до других, поэтому она будет сидеть сложа руки, ничего не делая, в то время как другие продолжат усердно работать.
Например, текущий верхний ответ заканчивается:
Я просто ненавижу этот ворчать в конце!
Другие, вроде
list(grouper(3, xrange(7)))
, иchunk(xrange(7), 3)
оба возвращаются[(0, 1, 2), (3, 4, 5), (6, None, None)]
. ОниNone
просто набивают, и, на мой взгляд, довольно не элегантны. Они НЕ равномерно разбивают фрагменты.Почему мы не можем разделить это лучше?
Мое решение (я)
Вот сбалансированное решение, адаптированное из функции я использовал в производстве (Примечание в Python 3 , чтобы заменить
xrange
сrange
):И я создал генератор, который делает то же самое, если вы поместите его в список:
И наконец, поскольку я вижу, что все вышеперечисленные функции возвращают элементы в непрерывном порядке (как они были заданы):
Вывод
Чтобы проверить их:
Который распечатывает:
Обратите внимание, что непрерывный генератор обеспечивает фрагменты в тех же шаблонах длины, что и два других, но все элементы расположены по порядку, и они разделены настолько равномерно, что можно разделить список дискретных элементов.
источник
list(grouper(3, xrange(7)))
и второй,chunk(xrange(7), 3)
и возвращение:[(0, 1, 2), (3, 4, 5), (6, None, None)]
. ОниNone
просто набивают, и, на мой взгляд, довольно не элегантны. Они НЕ равномерно разбивают фрагменты. Спасибо за ваш голос!import pandas as pd; [pd.DataFrame(np.arange(7))[i::3] for i in xrange(3)]
Я видел самый удивительный ответ Python-ish в дубликате этого вопроса:
Вы можете создать n-кортеж для любого n. Если
a = range(1, 15)
, то результат будет:Если список делится поровну, то вы можете заменить
zip_longest
сzip
, в противном случае триплет(13, 14, None)
будет утрачена. Python 3 используется выше. Для Python 2 используйтеizip_longest
.источник
zip(i, i, i, ... i)
с «chunk_size» аргументы молнии () можно записать в виде Являетсяzip(*[i]*chunk_size)
ли это хорошая идея или нет , это вопрос спорный, конечно.zip_longest
следует использовать, как это сделано в: stackoverflow.com/a/434411/1959808range(1, 15)
уже пропущенными элементами, потому что есть 14 элементовrange(1, 15)
, а не 15.Если вы знаете размер списка:
Если нет (итератор):
В последнем случае его можно перефразировать более красивым способом, если вы можете быть уверены, что последовательность всегда содержит целое количество фрагментов заданного размера (т.е. не существует неполного последнего фрагмента).
источник
Библиотека инструментов имеет
partition
функцию для этого:источник
Например, если у вас размер куска 3, вы можете сделать:
источник: http://code.activestate.com/recipes/303060-group-a-list-into-sequential-n-tuples/
Я бы использовал это, когда размер моего чанка является фиксированным числом, которое я могу напечатать, например, «3», и никогда не изменится.
источник
Мне очень нравится версия документа Python, предложенная tzot и JFSebastian, но у нее есть два недостатка:
Я использую это много в моем коде:
ОБНОВЛЕНИЕ: версия ленивых кусков:
источник
while True
цикла?StopIteration
когдаtuple
пустой иiterable.next()
выполняется. Не работает должным образом в современном Python, где выход из генератора должен быть сделанreturn
, а не повышениеStopIteration
.try/except StopIteration: return
Вокруг всего контура (и изменениеiterable.next()
вnext(iterable)
кросс-версии Compat) фиксирует это с минимальными затратами , по крайней мере.Где AA - массив, SS - размер чанка. Например:
источник
Мне было интересно узнать о производительности различных подходов, и вот оно:
Протестировано на Python 3.5.1
Результаты:
источник
time
библиотеки не оченьtimeit
код:
результат:
источник
Вы также можете использовать
get_chunks
функциюutilspie
библиотеки как:Вы можете установить
utilspie
через pip:Отказ от ответственности: я создатель библиотеки utilspie .
источник
На данный момент, я думаю, нам нужен рекурсивный генератор , на всякий случай ...
В питоне 2:
В питоне 3:
Кроме того, в случае массового вторжения инопланетян может пригодиться декорированный рекурсивный генератор :
источник
С помощью Assignment Expressions в Python 3.8 это становится довольно приятно:
Это работает на произвольной итерации, а не только на списке.
источник
хех, однострочная версия
источник
def chunk
вместо вместоchunk=lambda
.__ name__ атрибут 'chunk' вместо '<lambda>'. Конкретное имя более полезно в трассировках.<lamba>
или нет, по крайней мере, заметная разница.Применение:
источник
Еще одна более явная версия.
источник
Без вызова len (), что хорошо для больших списков:
И это для повторяемости:
Функциональный аромат вышеперечисленного:
ИЛИ:
ИЛИ:
источник
len()
больших списков; это операция с постоянным временем.Вот список дополнительных подходов:
Данный
Код
Стандартная библиотека
more_itertools
+Ссылки
zip_longest
( связанный пост , связанный пост )setdefault
(упорядоченные результаты требуют Python 3.6+)collections.defaultdict
(упорядоченные результаты требуют Python 3.6+)more_itertools.chunked
( связанные размещены )more_itertools.sliced
more_itertools.grouper
( связанный пост )more_itertools.windowed
(смотри такжеstagger
,zip_offset
)+ Сторонняя библиотека, которая реализует рецепты itertools и многое другое.
> pip install more_itertools
источник
Смотрите эту ссылку
python3
источник
zip(*[iter(range(7))]*3)
только возвращает[(0, 1, 2), (3, 4, 5)]
и забывает6
входные данные.Поскольку все здесь говорят об итераторах.
boltons
имеет идеальный метод для этого, называетсяiterutils.chunked_iter
.Вывод:
Но если вы не хотите пощадить память, вы можете использовать old-way и хранить все
list
в первую очередь с помощьюiterutils.chunked
.источник
Еще одно решение
источник
источник
Рассмотрите возможность использования кусочков matplotlib.cbook
например:
источник