Alternative way to split a list into groups of n
本问题已经有最佳答案,请猛点这里访问。
假设我有一个任意长度的列表,l:
1 | L = list(range(1000)) |
将该列表分成多个组的最佳方法是什么?这是我能想到的最好的结构,出于某种原因,我觉得这不是完成任务的最佳方式:
1 2 3 | n = 25 for i in range(0, len(L), n): chunk = L[i:i+25] |
有没有一个内置的来做这个我不见了?
编辑:早期的答案是把我的for循环改写成一个listcomp,这不是一个主意;你基本上是以不同的形式给我一个准确的答案。我在看是否有其他的方法来实现这一点,比如清单上的一个假设的
1 2 3 4 | def split_list(L, n): assert type(L) is list,"L is not a list" for i in range(0, len(L), n): yield L[i:i+n] |
干得好:
1 | list_of_groups = zip(*(iter(the_list),) * group_size) |
例子:
1 2 | print zip(*(iter(range(10)),) * 3) [(0, 1, 2), (3, 4, 5), (6, 7, 8)] |
如果元素的数量不能被n整除,但您仍然希望包含它们,那么可以使用izip_longst,但它仅在python 2.6之后可用。
1 | izip_longest(*(iter(range(10)),) * 3) |
结果是一个生成器,因此如果要打印它,需要将其转换为列表。
最后,如果您没有python2.6,并且坚持使用旧版本,但仍然希望获得相同的结果,那么可以使用map:
1 2 | print map(None, *(iter(range(10)),) * 3) [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, None, None)] |
我想在目前介绍的不同方法之间添加一些速度比较:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | python -m timeit -s 'from itertools import izip_longest; L = range(1000)' 'list(izip_longest(*(iter(L),) * 3))' 10000 loops, best of 3: 47.1 usec per loop python -m timeit -s 'L = range(1000)' 'zip(*(iter(L),) * 3)' 10000 loops, best of 3: 50.1 usec per loop python -m timeit -s 'L = range(1000)' 'map(None, *(iter(L),) * 3)' 10000 loops, best of 3: 50.7 usec per loop python -m timeit -s 'L = range(1000)' '[L[i:i+3] for i in range(0, len(L), 3)]' 10000 loops, best of 3: 157 usec per loop python -m timeit -s 'import itertools; L = range(1000)' '[list(group) for key, group in itertools.groupby(L, lambda k: k//3)]' 1000 loops, best of 3: 1.41 msec per loop |
列表理解和分组方法明显慢于zip、izip-long和map。
怎么样:
1 2 3 4 | >>> n = 2 >>> l = [1,2,3,4,5,6,7,8,9] >>> [ l[i:i+n] for i in range(0, len(l), n) ] [[1, 2], [3, 4], [5, 6], [7, 8], [9]] |
一个python配方(在python 2.6中,使用
1 2 3 4 | def grouper(n, iterable, fillvalue=None): "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx" args = [iter(iterable)] * n return itertools.zip_longest(*args, fillvalue=fillvalue) |
示例用法:
1 2 3 4 | >>> list(grouper(3, range(9))) [(0, 1, 2), (3, 4, 5), (6, 7, 8)] >>> list(grouper(3, range(10))) [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, None, None)] |
如果您希望最后一组比其他组短,而不是用
1 2 3 4 5 6 7 8 | >>> def mygrouper(n, iterable): ... args = [iter(iterable)] * n ... return ([e for e in t if e != None] for t in itertools.zip_longest(*args)) ... >>> list(mygrouper(3, range(9))) [[0, 1, 2], [3, 4, 5], [6, 7, 8]] >>> list(mygrouper(3, range(10))) [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]] |
itertools.groupby是一个很好的工具,下面是一种简单地使用整数除法拆分整数列表的方法:
1 2 3 4 5 6 7 | >>> for key, group in itertools.groupby(range(10), lambda k: k//3): ... print key, list(group) ... 0 [0, 1, 2] 1 [3, 4, 5] 2 [6, 7, 8] 3 [9] |
(列表必须以0开头,才能以完整组开头。)
1 2 | n = 25 list_of_lists = [L[i:i+n] for i in range(0, len(L), n)] |
它为您提供了列表
如果
这里是递归版本。它效率很低,因为Python有递归限制,但是这个版本说明了每个任务都可以通过递归来解决。
1 2 3 4 5 6 7 8 | def split_to_groups(l, n): assert (len(l) / n) < 998,"Can't split to {} groups".format(len(l) / n) if l == []: return [] else: f = [l[:n]] f.extend(split_to_groups(l[n:], n)) return f |