将列表拆分为 n 组的替代方法 [重复]
Posted
技术标签:
【中文标题】将列表拆分为 n 组的替代方法 [重复]【英文标题】:Alternative way to split a list into groups of n [duplicate] 【发布时间】:2010-12-10 03:21:51 【问题描述】:假设我有一个任意长度的列表,L:
L = list(range(1000))
将该列表分成n
组的最佳方法是什么?这是我能想到的最好的结构,但由于某种原因,它感觉不是完成任务的最佳方式:
n = 25
for i in range(0, len(L), n):
chunk = L[i:i+25]
有没有内置的功能可以做到这一点?
编辑: 早期的答案是将我的 for 循环改造成一个 listcomp,这不是想法;你基本上是以不同的形式给我我的确切答案。我正在查看是否有其他方法可以实现此目的,例如列表中的假设 .split
或其他东西。我也在昨晚写的一些代码中使用它作为生成器:
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]
【问题讨论】:
为更多 Pythonic 创建一个生成器。但我们对我来说 - 这是正常的代码:) @Jurily 查看***.com/questions/58968/… 这个问题让我觉得我们需要itertools.split(iterable, itervallen)
@Jed:这不是答案。没有人争辩说您的代码不是 pythonic,但是,列表理解版本简单、惯用并且相当可扩展。虽然发布的其他技巧有效并且可能更有效,但我看不出它们中的任何一个是pythonic。附言如果您要回复评论,请不要编辑您的答案,这会使对话很难理解。
@SilentGhost,我同意这些技巧可能看起来不像 Python 并且对于初学者来说很难理解。但它们在 python 文档中被建议作为一个秘诀。这很重要。
【参考方案1】:
给你:
list_of_groups = zip(*(iter(the_list),) * group_size)
例子:
print zip(*(iter(range(10)),) * 3)
[(0, 1, 2), (3, 4, 5), (6, 7, 8)]
如果元素的数量不能被 N 整除,但您仍想包含它们,则可以使用 izip_longest,但它仅在 python 2.6 后可用
izip_longest(*(iter(range(10)),) * 3)
结果是一个生成器,所以如果你想打印它,你需要将它转换成一个列表。
最后,如果您没有 python 2.6 并且坚持使用旧版本,但您仍然希望获得相同的结果,您可以使用 map:
print map(None, *(iter(range(10)),) * 3)
[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, None, None)]
我想在目前介绍的不同方法之间添加一些速度比较:
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
list comprehension 和 group by 方法明显比 zip、izip_longest 和 map 慢
【讨论】:
你的 Python 技能让我有点害怕。 :) 好答案! 这应该是公认的答案。我不记得地图和 zip 习惯用语,但我记得它们是“正确”的方式,而且这出现在网络搜索中——谢谢。 +1 用于所有解决方案的基准测试。这是人们在 Python 答案中经常错过的东西,正如人们所看到的,使用哪种方法会产生很大的不同。 这通过将多个引用传递给同一个迭代器来实现 zip。这是一个疯狂的黑客攻击。 一点解释:(iter(the_list),)
是一个元组。 (iter(the_list),)*3
是一个元组,三个引用指向同一个迭代器。我们使用星号表达式*(...)
将三个对迭代器的引用作为参数传递给zip
。 zip
基本上是对给定参数进行矩阵转置。【参考方案2】:
怎么样:
>>> 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]]
【讨论】:
查看我最近的编辑。 不错的一个。在我的用例中,更喜欢这个,比 zip 更好。 这可能不是最快的方法,但它肯定是最 Pythonic 的。 当您需要从 shell 执行此操作时,这很容易记住并且可以快速执行。 干净整洁。【参考方案3】:Python recipe(在 Python 2.6 中,使用 itertools.izip_longest
):
def grouper(n, iterable, fillvalue=None):
"grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
args = [iter(iterable)] * n
return itertools.zip_longest(*args, fillvalue=fillvalue)
示例用法:
>>> 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)]
如果您希望最后一组比其他组短而不是用fillvalue
填充,那么您可以例如像这样更改代码:
>>> 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]]
【讨论】:
接受了,因为你先得到它。我不确定哪个更 Pythonic,因为可读性也很重要;但是,这个问题确实提出了一些不错的选择。 如果实际列表中间有 None ,您的最后一个函数也会删除它们。 这对 zip_longest 的实现进行了假设(即:它严格按循环顺序访问可迭代对象),这可能不是严格安全的。使用起来可能是安全的,但有点气味。【参考方案4】:Itertools.groupby 是一个很好的工具,这里有一种简单地使用整数除法来拆分整数列表的方法:
>>> 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 开头才能以完整组开头。)
【讨论】:
您的代码仅适用于range(i)
,其中 i 是某个整数,不适用于指定的“任意长度列表”。你的答案不正确。【参考方案5】:
n = 25
list_of_lists = [L[i:i+n] for i in range(0, len(L), n)]
它为您提供列表列表[[0..24], [25..49], ..]
如果 len(L) % n
不为 0,则最后一个元素 (list_of_lists[-1]
) 的长度将为 len(L) % n。
【讨论】:
查看我最近的编辑。【参考方案6】:这里是递归版本。由于 Python 有递归限制,效率低下,但这个版本说明了每个任务都可以通过递归来解决。
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
【讨论】:
以上是关于将列表拆分为 n 组的替代方法 [重复]的主要内容,如果未能解决你的问题,请参考以下文章