Python中针对列表的滑动窗口

8

有没有一种高效或优雅的方法来在Python中检索列表的所有k大小的子列表?例如:

arr = [2, 3, 5, 7, 11, 13]

我想要所有的三元子列表:
result = [[2, 3, 5],
          [3, 5, 7],
          [5, 7, 11],
          [7, 11, 13]]

我知道可以使用for循环和切片arr[i:i+3]来创建这个,但是我处理的列表非常巨大,所以希望有一种高效的机制,或者至少一种优雅或Pythonic的机制。

我也在使用Pandas,所以很乐意使用Pandas的机制。


2
这会有所帮助:https://codereview.stackexchange.com/questions/196350/slice-a-list-or-numpy-array-into-consecutive-tuples - Pygirl
4个回答

9

如果您真的想构建列表,我认为没有比这个基本列表推导更好的选择:

arr = [2, 3, 5, 7, 11, 13]
result = [arr[i:i+k] for i in range(len(arr)-k+1)]

如果你想要最小化内存使用,可以使用生成器:

arr = [2, 3, 5, 7, 11, 13]
def window(arr, k):
    for i in range(len(arr)-k+1):
        yield arr[i:i+k]

for group in window(arr, 3):
    ...  # do something with group

你也可以将k个偏移一个位置的列表zip在一起,不过这将花费和第一种解决方案相同的内存,而且可能没有太多性能优势。

在numpy或pandas中可能有一些快速高效的方法,但你需要展示更多关于输入输出应该如何的信息。

这里还有一些其他的想法(链接),但它们集中于一般的可迭代对象(只能一次性地取出项目),而不是列表(可以通过索引访问项目,可能重复)。


1
如果您不需要使用生成器,这里有一个一行代码的解决方案:slidingwindows = lambda L, n: [L[i:i+n] for i in range(len(L)-n+1)] - Basj

7

您可以使用more_itertools

import more_itertools
list(more_itertools.windowed(arr,3))

[(2, 3, 5), (3, 5, 7), (5, 7, 11), (7, 11, 13)]

或者

使用 itertools

from itertools import islice

def pairwise(iterable, n):
    "s -> (s0,s1,..s(n-1)), (s1,s2,.., sn), (s2, s3,..,s(n+1)), ..."
    iters = iter(iterable)
    result = tuple(islice(iters, n))
    if len(result) == n:
        yield result
    for elem in iters:
        result = result[1:] + (elem,)
        yield result

6

您可以使用步幅

arr = [2, 3, 5, 7, 11, 13]

def rolling_window(a, window):
    shape = a.shape[:-1] + (a.shape[-1] - window + 1, window)
    strides = a.strides + (a.strides[-1],)
    return np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides)

a = rolling_window(np.array(arr), 3)
print (a)
[[ 2  3  5]
 [ 3  5  7]
 [ 5  7 11]
 [ 7 11 13]]


print (a.tolist())
[[2, 3, 5], 
 [3, 5, 7], 
 [5, 7, 11], 
 [7, 11, 13]]

0
如果你的源代码(列表)非常庞大,那么源代码提供者应该按需生成值。实现这一点的方法是创建一个生成器。
从文件中创建假设性的源代码生成器;
def gen_value():
    with open('big-file.txt') as f:
        for line in f:
            for x in line.split():
                yield int(x)

可以使用grouper函数recipe来消耗生成器:

def grouper(iterable, n, fillvalue=None):
    "Collect data into fixed-length chunks or blocks"
    # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return zip_longest(*args, fillvalue=fillvalue)

因此,您可以调用list(grouper(gen(), 3))


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