将列表拆分为 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 是一个元组,三个引用指向同一个迭代器。我们使用星号表达式*(...) 将三个对迭代器的引用作为参数传递给zipzip 基本上是对给定参数进行矩阵转置。【参考方案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 组的替代方法 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

将 Pandas 单元格中的列表拆分为多列 [重复]

将嵌套列表拆分为两个列表[重复]

将熊猫数据框列列表值拆分为重复行[重复]

将列表拆分为 3 个部分 [重复]

将数据框列中的列表拆分为多列[重复]

将列表列表拆分为熊猫数据框[重复]