Генератор Python, который группирует другую итерацию в группы N [дубликат]
этот вопрос уже есть ответ здесь:
Я ищу функцию, которая принимает iterable i
и n
и дает кортежи длины n
это последовательные значения из i
:
x = [1,2,3,4,5,6,7,8,9,0]
[z for z in TheFunc(x,3)]
дает
[(1,2,3),(4,5,6),(7,8,9),(0)]
существует ли такая функция в стандартной библиотеке?
если он существует как часть стандартной библиотеки, я не могу его найти, и у меня закончились термины для поиска. Я могла бы написать свою, но не хочу.
9 ответов
посмотреть grouper
рецепт документы на itertools
пакета
def grouper(n, iterable, fillvalue=None):
"grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
args = [iter(iterable)] * n
return izip_longest(fillvalue=fillvalue, *args)
(однако это дубликат несколько вопросов.)
когда вы хотите сгруппировать итератор в куски n
без подклада конечная группа со значением заполнения, используйте iter(lambda: list(IT.islice(iterable, n)), [])
:
import itertools as IT
def grouper(n, iterable):
"""
>>> list(grouper(3, 'ABCDEFG'))
[['A', 'B', 'C'], ['D', 'E', 'F'], ['G']]
"""
iterable = iter(iterable)
return iter(lambda: list(IT.islice(iterable, n)), [])
seq = [1,2,3,4,5,6,7]
print(list(grouper(3, seq)))
доходность
[[1, 2, 3], [4, 5, 6], [7]]
есть объяснение, как это работает во второй половине ответ.
когда вы хотите сгруппировать итератор в куски n
и pad конечная группа со значением заполнения, используйте рецепт окунь zip_longest(*[iterator]*n)
:
например, в Python2:
>>> list(IT.izip_longest(*[iter(seq)]*3, fillvalue='x'))
[(1, 2, 3), (4, 5, 6), (7, 'x', 'x')]
в Python3, что было izip_longest
теперь называется zip_longest
:
>>> list(IT.zip_longest(*[iter(seq)]*3, fillvalue='x'))
[(1, 2, 3), (4, 5, 6), (7, 'x', 'x')]
если вы хотите, чтобы группа последовательность в куски n
можно использовать chunks
рецепт:
def chunks(seq, n):
# https://stackoverflow.com/a/312464/190597 (Ned Batchelder)
""" Yield successive n-sized chunks from seq."""
for i in xrange(0, len(seq), n):
yield seq[i:i + n]
обратите внимание, что в отличии от итераторов в целом последовательности по определению иметь длину (т. е. __len__
is определенный.)
как насчет этого? Однако у него нет значения заполнения.
>>> def partition(itr, n):
... i = iter(itr)
... res = None
... while True:
... res = list(itertools.islice(i, 0, n))
... if res == []:
... break
... yield res
...
>>> list(partition([1, 2, 3, 4, 5, 6, 7, 8, 9], 3))
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
>>>
он использует копию исходного iterable, который он исчерпывает для каждого последовательного соединения. Единственный другой способ, который мог придумать мой усталый мозг, - это генерировать конечные точки соединения с диапазоном.
может быть, я должен изменить list()
to tuple()
поэтому он лучше соответствует вашему выходу.
Это очень распространенный запрос в Python. Достаточно распространенный, чтобы он попал в Болтоны единый пакет утилит. Во-первых, здесь есть обширные документы. Более того,модуль разработан и протестирован, чтобы полагаться только на стандартную библиотеку (Python 2 и 3 совместимы), то есть вы можете просто загрузите файл прямо в свой проект.
# if you downloaded/embedded, try:
# from iterutils import chunked
# with `pip install boltons` use:
from boltons.iterutils import chunked
print(chunked(range(10), 3))
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
существует форма итератора / генератора для неопределенного / длинного последовательности также:
print(list(chunked_iter(range(10), 3, fill=None)))
# [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, None, None]]
Как вы можете видеть, вы также можете заполнить последовательность значением по вашему выбору. Наконец, как сопровождающий, я могу заверить вас, что, хотя код был загружен / протестирован тысячами разработчиков, если у вас возникнут какие-либо проблемы, вы получите самую быструю поддержку через boltons GitHub выпускает страницу. Надеюсь, это (и/или любой из других рецептов 150+ boltons) помогло!
Я использую функция chunked из пакета more_itertools.
$ pip install more_itertools
$ python
>>> x = [1,2,3,4,5,6,7,8,9,0]
>>> [tuple(z) for z in more_itertools.more.chunked(x, 3)]
[(1, 2, 3), (4, 5, 6), (7, 8, 9), (0,)]
Это очень старый вопрос, но я думаю, будет полезно упомянуть следующий подход для общего случая. Его главное достоинство заключается в том, что ему нужно только один раз повторить данные, поэтому он будет работать с курсорами базы данных или другими последовательностями, которые могут использоваться только один раз. Я также нахожу его более читаемым.
def chunks(n, iterator):
out = []
for elem in iterator:
out.append(elem)
if len(out) == n:
yield out
out = []
yield out
Я знаю, что на это ответили несколько раз, но я добавляю свое решение, которое должно улучшить как общую применимость к последовательностям и итераторам, читаемость (без условия выхода невидимого цикла исключением StopIteration), так и производительность по сравнению с рецептом grouper. Это больше всего похоже на последний ответ Свейн.
def chunkify(iterable, n):
iterable = iter(iterable)
n_rest = n - 1
for item in iterable:
rest = itertools.islice(iterable, n_rest)
yield itertools.chain((item,), rest)
вот другое решение, которое не использует itertools и, хотя у него есть еще пара строк, оно, по-видимому, превосходит данные ответы, когда куски намного короче, чем длина итерации. Однако для больших кусков другие ответы гораздо быстрее.
def batchiter(iterable, batch_size):
"""
>>> list(batchiter('ABCDEFG', 3))
[['A', 'B', 'C'], ['D', 'E', 'F'], ['G']]
"""
next_batch = []
for element in iterable:
next_batch.append(element)
if len(next_batch) == batch_size:
batch, next_batch = next_batch, []
yield batch
if next_batch:
yield next_batch
In [19]: %timeit [b for b in batchiter(range(1000), 3)]
1000 loops, best of 3: 644 µs per loop
In [20]: %timeit [b for b in grouper(3, range(1000))]
1000 loops, best of 3: 897 µs per loop
In [21]: %timeit [b for b in partition(range(1000), 3)]
1000 loops, best of 3: 890 µs per loop
In [22]: %timeit [b for b in batchiter(range(1000), 333)]
1000 loops, best of 3: 540 µs per loop
In [23]: %timeit [b for b in grouper(333, range(1000))]
10000 loops, best of 3: 81.7 µs per loop
In [24]: %timeit [b for b in partition(range(1000), 333)]
10000 loops, best of 3: 80.1 µs per loop
def grouper(iterable, n):
while True:
yield itertools.chain((next(iterable),), itertools.islice(iterable, n-1))