根据序列中缺失数字拆分列表

28 投票
6 回答
12739 浏览
提问于 2025-04-16 00:37

我想找一种最符合Python风格的方法,把一串数字分成几个小列表,分割的依据是序列中缺少的数字。比如,如果最开始的列表是:

seq1 = [1, 2, 3, 4, 6, 7, 8, 9, 10]

那么这个函数会输出:

[[1, 2, 3, 4], [6, 7, 8, 9, 10]]

或者

seq2 = [1, 2, 4, 5, 6, 8, 9, 10]

会得到:

[[1, 2], [4, 5, 6], [8, 9, 10]]

6 个回答

6

还有一种选择,不需要用到 itertools 等库:

>>> data = [1, 4, 5, 6, 10, 15, 16, 17, 18, 22, 25, 26, 27, 28]
>>> spl = [0]+[i for i in range(1,len(data)) if data[i]-data[i-1]>1]+[None]
>>> [data[b:e] for (b, e) in [(spl[i-1],spl[i]) for i in range(1,len(spl))]]
... [[1], [4, 5, 6], [10], [15, 16, 17, 18], [22], [25, 26, 27, 28]]
9

这是一个在Python 3中有效的解决方案(之前的答案只适用于Python 2)。

>>> from operator import itemgetter
>>> from itertools import *
>>> groups = []
>>> for k, g in groupby(enumerate(seq2), lambda x: x[0]-x[1]):
>>>     groups.append(list(map(itemgetter(1), g)))
... 
>>> print(groups)
[[1, 2], [4, 5, 6], [8, 9, 10]]

或者可以用列表推导的方式来写

>>> [list(map(itemgetter(1), g)) for k, g in groupby(enumerate(seq2), lambda x: x[0]-x[1])]
[[1, 2], [4, 5, 6], [8, 9, 10]]

需要做出这些改变是因为

  • 去掉了元组参数解包的功能 PEP 3113
  • map现在返回的是一个迭代器,而不是列表
54

这是旧版Python文档中代码的Python 3版本:

>>> # Find runs of consecutive numbers using groupby.  The key to the solution
>>> # is differencing with a range so that consecutive numbers all appear in
>>> # same group.
>>> from itertools import groupby
>>> from operator import itemgetter
>>> data = [ 1,  4,5,6, 10, 15,16,17,18, 22, 25,26,27,28]
>>> for k, g in groupby(enumerate(data), lambda i_x: i_x[0] - i_x[1]):
...     print(list(map(itemgetter(1), g)))
...
[1]
[4, 5, 6]
[10]
[15, 16, 17, 18]
[22]
[25, 26, 27, 28]

groupby函数来自itertools模块,每当关键函数的返回值发生变化时,它就会生成一个分组。这里的窍门是,返回值是列表中的数字减去该元素在列表中的位置。当数字之间有间隔时,这个差值就会改变。

itemgetter函数来自operator模块,为了让这个例子能够运行,你需要导入这个模块和itertools模块。

另外,你也可以用列表推导式来实现:

>>> [map(itemgetter(1), g) for k, g in groupby(enumerate(seq2), lambda i_x: i_x[0] - i_x[1])]
[[1, 2], [4, 5, 6], [8, 9, 10]]

撰写回答