将Python列表分成重叠块的列表

28

这个问题类似于 将列表切片为子列表的列表,但在我的情况下,我希望将每个前一个子列表的最后一个元素作为下一个子列表的第一个元素。并且我必须考虑到最后一个子列表总是至少有两个元素。

例如:

list_ = ['a','b','c','d','e','f','g','h']

一个大小为3的子列表的结果:

resultant_list = [['a','b','c'],['c','d','e'],['e','f','g'],['g','h']]
3个回答

33

你提供的答案中的列表推导式可以很容易地适应重叠块,只需缩短传递给范围的“步骤”参数即可:

>>> list_ = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
>>> n = 3  # group size
>>> m = 1  # overlap size
>>> [list_[i:i+n] for i in range(0, len(list_), n-m)]
[['a', 'b', 'c'], ['c', 'd', 'e'], ['e', 'f', 'g'], ['g', 'h']]

其他访问此问题的用户可能没有使用输入列表(可切片,已知长度,有限)的奢侈条件。这里提供了一种基于生成器的解决方案,可以处理任意可迭代对象:

from collections import deque

def chunks(iterable, chunk_size=3, overlap=0):
    # we'll use a deque to hold the values because it automatically
    # discards any extraneous elements if it grows too large
    if chunk_size < 1:
        raise Exception("chunk size too small")
    if overlap >= chunk_size:
        raise Exception("overlap too large")
    queue = deque(maxlen=chunk_size)
    it = iter(iterable)
    i = 0
    try:
        # start by filling the queue with the first group
        for i in range(chunk_size):
            queue.append(next(it))
        while True:
            yield tuple(queue)
            # after yielding a chunk, get enough elements for the next chunk
            for i in range(chunk_size - overlap):
                queue.append(next(it))
    except StopIteration:
        # if the iterator is exhausted, yield any remaining elements
        i += overlap
        if i > 0:
            yield tuple(queue)[-i:]

注意: 我已经在wimpy.util.chunks中发布了这个实现。如果您不介意添加依赖项,您可以pip install wimpy并使用from wimpy import chunks而不是复制粘贴代码。


3
这种方法可能会导致不必要的“存根”残留,例如如果您在['a', 'b', 'c', 'd', 'e', 'f', 'g']上运行第一个示例,则会产生[['a', 'b', 'c'], ['c', 'd', 'e'], ['e', 'f', 'g'], ['g']]。为了避免包含仅包含先前块中已捕获元素的不必要块,计算范围时从列表长度中减去重叠大小m,即[list_[i:i+n] for i in range(0, len(list_)-m, n-m)] - Jojanzing

14

more_itertools.windowed 是一个用于重叠可迭代对象的滑动窗口工具。

给定

import more_itertools as mit


iterable = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']

代码

windows = list(mit.windowed(iterable, n=3, step=2))
windows
# [('a', 'b', 'c'), ('c', 'd', 'e'), ('e', 'f', 'g'), ('g', 'h', None)]

如果需要的话,你可以通过过滤窗口来去掉None填充值。
[list(filter(None, w)) for w in windows]
# [['a', 'b', 'c'], ['c', 'd', 'e'], ['e', 'f', 'g'], ['g', 'h']]

我真的很喜欢它填充组None的事实,有没有用标准库实现的方法? - Mattwmaster58
@MattM。好的。这里有一个使用itertools的替代方案:list(itertools.islice(itertools.zip_longest(s, s[1:], s[2:]), None, None, 2)),其中s =“abcdefgh”。请注意,None也由zip_longest中的fillvalue参数控制。 - pylang
这个版本比我的列表推导式版本更简洁。谢谢。 - Mattwmaster58

2

这是我想到的:

最初的回答:

l = [1, 2, 3, 4, 5, 6]
x = zip (l[:-1], l[1:])
for i in x:
    print (i)

(1, 2)
(2, 3)
(3, 4)
(4, 5)
(5, 6)

Zip函数可以接受任意数量的可迭代对象,还有一个zip_longest函数。

最初的回答


这个回答如何解决问题?O.P.想要大小为3,步长为2的块。 - wim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接