滚动还是滑动窗口迭代器?
- 2024-11-21 08:33:00
- admin 原创
- 7
问题描述:
我需要一个可遍历序列/迭代器/生成器的滚动窗口(又称滑动窗口)。(默认 Python 迭代可以视为一种特殊情况,其中窗口长度为 1。)我目前正在使用以下代码。我怎样才能更优雅和/或更高效地做到这一点?
def rolling_window(seq, window_size):
it = iter(seq)
win = [it.next() for cnt in xrange(window_size)] # First window
yield win
for e in it: # Subsequent windows
win[:-1] = win[1:]
win[-1] = e
yield win
if __name__=="__main__":
for w in rolling_window(xrange(6), 3):
print w
"""Example output:
[0, 1, 2]
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
"""
对于特定情况window_size == 2
(即,迭代序列中相邻、重叠的对),另请参阅如何迭代列表中重叠(当前、下一个)值对?。
解决方案 1:
旧版本的 Python 文档中有一个带有itertools
示例:
from itertools import islice
def window(seq, n=2):
"Returns a sliding window (of width n) over data from the iterable"
" s -> (s0,s1,...s[n-1]), (s1,s2,...,sn), ... "
it = iter(seq)
result = tuple(islice(it, n))
if len(result) == n:
yield result
for elem in it:
result = result[1:] + (elem,)
yield result
itertools
我认为文档中的说法更简洁一些,而且效果更好。
如果您的迭代器是一个简单的列表/元组,那么使用指定的窗口大小滑动它的简单方法是:
seq = [0, 1, 2, 3, 4, 5]
window_size = 3
for i in range(len(seq) - window_size + 1):
print(seq[i: i + window_size])
输出:
[0, 1, 2]
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
解决方案 2:
这似乎是为 量身定制的,collections.deque
因为您本质上有一个 FIFO(添加到一端,从另一端移除)。但是,即使您使用 ,list
您也不应该进行两次切片;相反,您可能应该只pop(0)
从列表和append()
新项目中进行切片。
这是一个基于双端队列的优化实现,仿照您的原始实现:
from collections import deque
def window(seq, n=2):
it = iter(seq)
win = deque((next(it, None) for _ in xrange(n)), maxlen=n)
yield win
append = win.append
for e in it:
append(e)
yield win
在我的测试中,它大多数时候都轻松击败了这里发布的其他所有程序,不过 pillmuncher 的tee
版本在大型可迭代对象和小型窗口方面胜过它。在较大的窗口上,deque
原始速度再次领先。
访问 中的各个项目deque
可能比使用列表或元组更快或更慢。(靠近开头的项目更快,如果使用负索引,则靠近结尾sum(w)
的项目更快。)我在循环主体中放置了 ;这发挥了双端队列的优势(从一个项目迭代到下一个项目很快,因此此循环比下一个最快的方法 pillmuncher 快 20%)。当我将其更改为在十个窗口中单独查找和添加项目时,情况发生了变化,该tee
方法快了 20%。我能够通过在加法中的最后五个项中使用负索引来恢复一些速度,但tee
仍然快一点。总的来说,我估计这两种方法对于大多数用途来说都足够快,如果您需要更高的性能,请分析并选择效果最好的方法。
解决方案 3:
我喜欢tee()
:
from itertools import tee, izip
def window(iterable, size):
iters = tee(iterable, size)
for i in xrange(1, size):
for each in iters[i:]:
next(each, None)
return izip(*iters)
for each in window(xrange(6), 3):
print list(each)
给出:
[0, 1, 2]
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
解决方案 4:
有一个库可以满足您的需要:
import more_itertools
list(more_itertools.windowed([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15],n=3, step=3))
Out: [(1, 2, 3), (4, 5, 6), (7, 8, 9), (10, 11, 12), (13, 14, 15)]
解决方案 5:
step
以下是添加对,参数支持的概括fillvalue
:
from collections import deque
from itertools import islice
def sliding_window(iterable, size=2, step=1, fillvalue=None):
if size < 0 or step < 1:
raise ValueError
it = iter(iterable)
q = deque(islice(it, size), maxlen=size)
if not q:
return # empty iterable or size == 0
q.extend(fillvalue for _ in range(size - len(q))) # pad to size
while True:
yield iter(q) # iter() to avoid accidental outside modifications
try:
q.append(next(it))
except StopIteration: # Python 3.5 pep 479 support
return
q.extend(next(it, fillvalue) for _ in range(step - 1))
它每次产生块状size
项目,每次迭代滚动step
位置,必要时填充每个块fillvalue
。例如size=4, step=3, fillvalue='*'
:
[a b c d]e f g h i j k l m n o p q r s t u v w x y z
a b c[d e f g]h i j k l m n o p q r s t u v w x y z
a b c d e f[g h i j]k l m n o p q r s t u v w x y z
a b c d e f g h i[j k l m]n o p q r s t u v w x y z
a b c d e f g h i j k l[m n o p]q r s t u v w x y z
a b c d e f g h i j k l m n o[p q r s]t u v w x y z
a b c d e f g h i j k l m n o p q r[s t u v]w x y z
a b c d e f g h i j k l m n o p q r s t u[v w x y]z
a b c d e f g h i j k l m n o p q r s t u v w x[y z * *]
有关该参数的用例示例step
,请参阅在 python 中高效处理大型 .txt 文件。
解决方案 6:
只是一个快速的贡献。
由于当前 python 文档中的 itertool 示例中没有“window”(即,在http://docs.python.org/library/itertools.html的底部),因此这里有一个基于 grouper 代码的片段,它是给出的示例之一:
import itertools as it
def window(iterable, size):
shiftedStarts = [it.islice(iterable, s, None) for s in xrange(size)]
return it.izip(*shiftedStarts)
基本上,我们创建一系列切片迭代器,每个迭代器的起点都向前一个位置。然后,我们将它们压缩在一起。请注意,此函数返回一个生成器(它本身并不是一个生成器)。
与上面的附加元素和前进迭代器版本非常相似,性能(即哪个最好)随列表大小和窗口大小而变化。我喜欢这个,因为它是两行代码(也可以是一行代码,但我更喜欢命名概念)。
事实证明,上述代码是错误的。如果传递给iterable 的参数是序列,则它可以工作,但如果是迭代器,则不行。如果是迭代器,则 islice 调用之间会共享同一个迭代器(但不会 tee'd),这会严重破坏事情。
以下是一些修复的代码:
import itertools as it
def window(iterable, size):
itrs = it.tee(iterable, size)
shiftedStarts = [it.islice(anItr, s, None) for s, anItr in enumerate(itrs)]
return it.izip(*shiftedStarts)
另外,书中还有另一个版本。此版本不是复制迭代器然后多次前进复制,而是在我们将起始位置向前移动时对每个迭代器进行成对复制。因此,迭代器 t 既提供了起始点为 t 的“完整”迭代器,也提供了创建迭代器 t + 1 的基础:
import itertools as it
def window4(iterable, size):
complete_itr, incomplete_itr = it.tee(iterable, 2)
iters = [complete_itr]
for i in xrange(1, size):
incomplete_itr.next()
complete_itr, incomplete_itr = it.tee(incomplete_itr, 2)
iters.append(complete_itr)
return it.izip(*iters)
解决方案 7:
def GetShiftingWindows(thelist, size):
return [ thelist[x:x+size] for x in range( len(thelist) - size + 1 ) ]
>> a = [1, 2, 3, 4, 5]
>> GetShiftingWindows(a, 3)
[ [1, 2, 3], [2, 3, 4], [3, 4, 5] ]
解决方案 8:
为了展示如何组合itertools
食谱,我使用以下食谱pairwise
尽可能直接地将食谱扩展回食谱中:window
`consume`
def consume(iterator, n):
"Advance the iterator n-steps ahead. If n is none, consume entirely."
# Use functions that consume iterators at C speed.
if n is None:
# feed the entire iterator into a zero-length deque
collections.deque(iterator, maxlen=0)
else:
# advance to the empty slice starting at position n
next(islice(iterator, n, n), None)
def window(iterable, n=2):
"s -> (s0, ...,s(n-1)), (s1, ...,sn), (s2, ..., s(n+1)), ..."
iters = tee(iterable, n)
# Could use enumerate(islice(iters, 1, None), 1) to avoid consume(it, 0), but that's
# slower for larger window sizes, while saving only small fixed "noop" cost
for i, it in enumerate(iters):
consume(it, i)
return zip(*iters)
配方window
与 相同pairwise
,只是将第二个迭代器上的单个元素“消耗”替换tee
为迭代器上逐渐增加的消耗n - 1
。使用consume
而不是将每个迭代器包装在 中islice
会稍微快一些(对于足够大的迭代器),因为您只需islice
在 阶段支付包装开销consume
,而不是在提取每个窗口值的过程中支付(因此它受 的限制n
,而不是 中的项目数iterable
)。
性能方面,与其他一些解决方案相比,这个解决方案相当不错(而且比我测试过的任何其他解决方案都好,因为它具有可扩展性)。在 Python 3.5.0、Linux x86-64 上使用ipython
%timeit
magic 进行了测试。
kindall 的deque
解决方案islice
,通过使用而不是家庭滚动生成器表达式并测试结果长度来调整性能/正确性,以便当可迭代对象短于窗口时不会产生结果,以及传递maxlen
位置deque
而不是关键字(对于较小的输入产生令人惊讶的差异):
>>> %timeit -r5 deque(windowkindall(range(10), 3), 0)
100000 loops, best of 5: 1.87 μs per loop
>>> %timeit -r5 deque(windowkindall(range(1000), 3), 0)
10000 loops, best of 5: 72.6 μs per loop
>>> %timeit -r5 deque(windowkindall(range(1000), 30), 0)
1000 loops, best of 5: 71.6 μs per loop
与之前改编的 kindall 解决方案相同,但每个都yield win
更改为yield tuple(win)
,因此存储来自生成器的结果不需要所有存储的结果实际上都是最新结果的视图(在这种情况下,所有其他合理的解决方案都是安全的),并添加tuple=tuple
到函数定义中以将使用tuple
从B
移动LEGB
到L
:
>>> %timeit -r5 deque(windowkindalltupled(range(10), 3), 0)
100000 loops, best of 5: 3.05 μs per loop
>>> %timeit -r5 deque(windowkindalltupled(range(1000), 3), 0)
10000 loops, best of 5: 207 μs per loop
>>> %timeit -r5 deque(windowkindalltupled(range(1000), 30), 0)
1000 loops, best of 5: 348 μs per loop
consume
基于上述的解决方案:
>>> %timeit -r5 deque(windowconsume(range(10), 3), 0)
100000 loops, best of 5: 3.92 μs per loop
>>> %timeit -r5 deque(windowconsume(range(1000), 3), 0)
10000 loops, best of 5: 42.8 μs per loop
>>> %timeit -r5 deque(windowconsume(range(1000), 30), 0)
1000 loops, best of 5: 232 μs per loop
与相同consume
,但内联的else
情况consume
以避免函数调用和n is None
测试来减少运行时间,特别是对于设置开销是工作中有意义的一部分的小输入:
>>> %timeit -r5 deque(windowinlineconsume(range(10), 3), 0)
100000 loops, best of 5: 3.57 μs per loop
>>> %timeit -r5 deque(windowinlineconsume(range(1000), 3), 0)
10000 loops, best of 5: 40.9 μs per loop
>>> %timeit -r5 deque(windowinlineconsume(range(1000), 30), 0)
1000 loops, best of 5: 211 μs per loop
(旁注:pairwise
使用tee
默认参数 2 反复创建嵌套tee
对象的变体,因此任何给定的迭代器仅前进一次,而不是独立使用越来越多的次数,类似于MrDrFenner 的答案与非内联类似consume
并且在所有测试中都比内联慢consume
,所以为了简洁起见我省略了这些结果)。
如您所见,如果您不关心调用者需要存储结果的可能性,那么我的 kindall 解决方案的优化版本在大多数情况下都会获胜,除了“大可迭代对象、小窗口大小的情况”(内联consume
获胜);它会随着可迭代对象大小的增加而迅速降级,而随着窗口大小的增加则根本不会降级(其他所有解决方案在可迭代对象大小增加时降级更慢,但也会随着窗口大小的增加而降级)。它甚至可以通过包装来适应“需要元组”的情况map(tuple, ...)
,这比将元组放入函数中运行速度稍慢,但它很简单(花费的时间长 1-5%),并且当您可以容忍重复返回相同的值时,可以让您保持更快运行的灵活性。
如果您需要确保返回值不会被存储,则内联consume
在除最小输入大小之外的所有情况下都胜出(非内联consume
速度稍慢,但扩展性类似)。deque
基于 & 元组的解决方案仅在最小输入时胜出,因为设置成本较小,而且收益很小;随着可迭代对象变长,其性能会严重下降。
yield
需要说明的是,我使用的kindall 解决方案的改编版本tuple
是:
def windowkindalltupled(iterable, n=2, tuple=tuple):
it = iter(iterable)
win = deque(islice(it, n), n)
if len(win) < n:
return
append = win.append
yield tuple(win)
for e in it:
append(e)
yield tuple(win)
删除函数定义行中的缓存以及每个行中tuple
的使用以获得更快但安全性较差的版本。tuple
`yield`
解决方案 9:
我使用以下代码作为一个简单的滑动窗口,使用生成器来大幅提高可读性。根据我的经验,它的速度到目前为止足以用于生物信息学序列分析。
我之所以将它包含在这里,是因为我还没有看到过这种方法的使用。同样,我对它的性能不作任何评价。
def slidingWindow(sequence,winSize,step=1):
"""Returns a generator that will iterate through
the defined chunks of input sequence. Input sequence
must be sliceable."""
# Verify the inputs
if not ((type(winSize) == type(0)) and (type(step) == type(0))):
raise Exception("**ERROR** type(winSize) and type(step) must be int.")
if step > winSize:
raise Exception("**ERROR** step must not be larger than winSize.")
if winSize > len(sequence):
raise Exception("**ERROR** winSize must not be larger than sequence length.")
# Pre-compute number of chunks to emit
numOfChunks = ((len(sequence)-winSize)/step)+1
# Do the work
for i in range(0,numOfChunks*step,step):
yield sequence[i:i+winSize]
解决方案 10:
对双端队列窗口进行稍微修改,使其成为真正的滚动窗口。这样它一开始只填充一个元素,然后增长到最大窗口大小,然后在左边缘接近末尾时缩小:
from collections import deque
def window(seq, n=2):
it = iter(seq)
win = deque((next(it, None) for _ in xrange(1)), maxlen=n)
yield win
append = win.append
for e in it:
append(e)
yield win
for _ in xrange(len(win)-1):
win.popleft()
yield win
for wnd in window(range(5), n=3):
print(list(wnd))
这给出
[0]
[0, 1]
[0, 1, 2]
[1, 2, 3]
[2, 3, 4]
[3, 4]
[4]
解决方案 11:
为什么不呢
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None)
return zip(a, b)
它记录在 Python文档中。您可以轻松地将其扩展到更宽的窗口。
解决方案 12:
让我们变得懒惰吧!
from itertools import islice, tee
def window(iterable, size):
iterators = tee(iterable, size)
iterators = [islice(iterator, i, None) for i, iterator in enumerate(iterators)]
yield from zip(*iterators)
list(window(range(5), 3))
# [(0, 1, 2), (1, 2, 3), (2, 3, 4)]
解决方案 13:
在 Python 3.10 中,我们有一个itertools.pairwise(iterable)
可以滑动两个元素的窗口的函数:
这是文档:
返回从输入可迭代对象中获取的连续重叠对。
输出迭代器中的 2 元组数量将比输入数量少一个。如果输入迭代器的值少于两个,则它将为空。
大致相当于:
def pairwise(iterable): # pairwise('ABCDEFG') --> AB BC CD DE EF FG a, b = tee(iterable) next(b, None) return zip(a, b)
解决方案 14:
def rolling_window(list, degree):
for i in range(len(list)-degree+1):
yield [list[i+o] for o in range(degree)]
为滚动平均函数而制作的
解决方案 15:
我测试了几个解决方案以及我想出的解决方案。我发现我想出的方案是最快的,所以我想分享我的 python3 实现。
import itertools
import sys
def windowed(l, stride):
return zip(*[itertools.islice(l, i, sys.maxsize) for i in range(stride)])
解决方案 16:
多个迭代器!
def window(seq, size, step=1):
# initialize iterators
iters = [iter(seq) for i in range(size)]
# stagger iterators (without yielding)
[next(iters[i]) for j in range(size) for i in range(-1, -j-1, -1)]
while(True):
yield [next(i) for i in iters]
# next line does nothing for step = 1 (skips iterations for step > 1)
[next(i) for i in iters for j in range(step-1)]
next(it)
当序列完成时引发StopIteration
,并且由于一些我无法理解的酷原因,这里的yield语句将其排除并且函数返回,忽略未形成完整窗口的剩余值。
无论如何,这是迄今为止最少行数的解决方案,其唯一要求是seq
实现__iter__
或__getitem__
并且不依赖于itertools
或collections
除了@dansalmo 的解决方案:)
解决方案 17:
#Importing the numpy library
import numpy as np
arr = np.arange(6) #Sequence
window_size = 3
np.lib.stride_tricks.as_strided(arr, shape= (len(arr) - window_size +1, window_size),
strides = arr.strides*2)
"""Example output:
[0, 1, 2]
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
“””
解决方案 18:
toolz / cytoolz包有一个sliding_window函数。
>>> from cytoolz import sliding_window
>>> list(sliding_window(3, range(6))) # returns [(0, 1, 2), (1, 2, 3), (2, 3, 4), (3, 4, 5)]
解决方案 19:
>>> n, m = 6, 3
>>> k = n - m+1
>>> print ('{}
'*(k)).format(*[range(i, i+m) for i in xrange(k)])
[0, 1, 2]
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
解决方案 20:
如何使用以下内容:
mylist = [1, 2, 3, 4, 5, 6, 7]
def sliding_window(l, window_size=2):
if window_size > len(l):
raise ValueError("Window size must be smaller or equal to the number of elements in the list.")
t = []
for i in xrange(0, window_size):
t.append(l[i:])
return zip(*t)
print sliding_window(mylist, 3)
输出:
[(1, 2, 3), (2, 3, 4), (3, 4, 5), (4, 5, 6), (5, 6, 7)]
解决方案 21:
这是一个老问题,但对于那些仍然感兴趣的人来说,这个页面中使用生成器实现了一个很棒的窗口滑块(由 Adrian Rosebrock 编写)。
这是 OpenCV 的一个实现,但您可以轻松地将其用于任何其他目的。对于渴望了解的人,我将在此处粘贴代码,但为了更好地理解它,我建议访问原始页面。
def sliding_window(image, stepSize, windowSize):
# slide a window across the image
for y in xrange(0, image.shape[0], stepSize):
for x in xrange(0, image.shape[1], stepSize):
# yield the current window
yield (x, y, image[y:y + windowSize[1], x:x + windowSize[0]])
提示:你可以.shape
在迭代生成器时检查窗口的,以丢弃那些不符合你要求的
干杯
解决方案 22:
修改了DiPaolo 的答案,允许任意填充和可变步长
import itertools
def window(seq, n=2,step=1,fill=None,keep=0):
"Returns a sliding window (of width n) over data from the iterable"
" s -> (s0,s1,...s[n-1]), (s1,s2,...,sn), ... "
it = iter(seq)
result = tuple(itertools.islice(it, n))
if len(result) == n:
yield result
while True:
# for elem in it:
elem = tuple( next(it, fill) for _ in range(step))
result = result[step:] + elem
if elem[-1] is fill:
if keep:
yield result
break
yield result
解决方案 23:
尝试我的部分,使用 islice 的简单、一行、pythonic 方式。但是,可能不是最有效的。
from itertools import islice
array = range(0, 10)
window_size = 4
map(lambda i: list(islice(array, i, i + window_size)), range(0, len(array) - window_size + 1))
# output = [[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6], [4, 5, 6, 7], [5, 6, 7, 8], [6, 7, 8, 9]]
解释:使用 window_size 的 islice 创建窗口,并使用 map 在整个数组上迭代此操作。
解决方案 24:
深度学习中滑动窗口数据的优化函数
def SlidingWindow(X, window_length, stride):
indexer = np.arange(window_length)[None, :] + stride*np.arange(int(len(X)/stride)-window_length+4)[:, None]
return X.take(indexer)
应用于多维数组
import numpy as np
def SlidingWindow(X, window_length, stride1):
stride= X.shape[1]*stride1
window_length = window_length*X.shape[1]
indexer = np.arange(window_length)[None, :] + stride1*np.arange(int(len(X)/stride1)-window_length-1)[:, None]
return X.take(indexer)
解决方案 25:
我的两个版本的window
实现
from typing import Sized, Iterable
def window(seq: Sized, n: int, strid: int = 1, drop_last: bool = False):
for i in range(0, len(seq), strid):
res = seq[i:i + n]
if drop_last and len(res) < n:
break
yield res
def window2(seq: Iterable, n: int, strid: int = 1, drop_last: bool = False):
it = iter(seq)
result = []
step = 0
for i, ele in enumerate(it):
result.append(ele)
result = result[-n:]
if len(result) == n:
if step % strid == 0:
yield result
step += 1
if not drop_last:
yield result
解决方案 26:
从列表生成固定长度窗口的另一种简单方法
from collections import deque
def window(ls,window_size=3):
window = deque(maxlen=window_size)
for element in ls:
if len(window)==window_size:
yield list(window)
window.append(element)
ls = [0,1,2,3,4,5]
for w in window(ls):
print(w)
解决方案 27:
我最终使用的解决方案(保持简单):
def sliding_window(items, size):
return [items[start:end] for start, end
in zip(range(0, len(items) - size + 1), range(size, len(items) + 1))]
不用说,该items
序列需要可切片。使用索引并不理想,但考虑到替代方案,这似乎是最不坏的选择……这也可以轻松更改为生成器:只需替换[...]
为(...)
。
解决方案 28:
更新
正如凯利发现的那样,这是一个重复的答案。但我将其留在这里作为反例,因为我添加了一个毫无意义的min
。
因此,如果您想使用 min 来避免IndexError
不要这样做,则没有必要,range
将为您处理这种情况。
旧答案
n > len(l)
奇怪的是,返回时会自动处理以下内容[]
,这在语义上是正确的。
>>> l = [0, 1, 2, 3, 4]
>>> n = 2
>>> [l[i: i + min(n, len(l)-i)] for i in range(len(l)-n+1)]
>>> [[0, 1], [1, 2], [2, 3], [3, 4]]
>>>
>>> n = 3
>>> [l[i: i + min(n, len(l)-i)] for i in range(len(l)-n+1)]
>>> [[0, 1, 2], [1, 2, 3], [2, 3, 4]]
>>>
>>> n = 4
>>> [l[i: i + min(n, len(l)-i)] for i in range(len(l)-n+1)]
>>> [[0, 1, 2, 3], [1, 2, 3, 4]]
>>>
>>> n = 5
>>> [l[i: i + min(n, len(l)-i)] for i in range(len(l)-n+1)]
>>> [[0, 1, 2, 3, 4]]
>>>
>>> n = 10 # n > len(l)
>>> [l[i: i + min(n, len(l)-i)] for i in range(len(l)-n+1)]
>>> []
解决方案 29:
我发现这个解决方案比使用内置函数更优雅。
words = ["this", "is", "an", "example"]
def get_sliding_windows(doc, sliding_window, padded=False):
all_windows = []
for i in range(sliding_window):
front = sliding_window-i
all_windows.append(front*['']+doc+i*[''])
if padded:
return np.array(all_windows).transpose()[1:]
else:
return np.array(all_windows).transpose()[sliding_window:-1]
>>> get_sliding_windows(words,3)
>>> array([['this', 'is', 'an'],
['is', 'an', 'example'],
['an', 'example', '']], dtype='<U7')
>>> get_padded_sliding_windows(words,3, True)
>>> array([['', '', 'this'],
['', 'this', 'is'],
['this', 'is', 'an'],
['is', 'an', 'example'],
['an', 'example', ''],
['example', '', '']], dtype='<U7')
解决方案 30:
这是一行代码。我计算了时间,它的表现与最佳答案相当,并且随着 seq 的增大而逐渐变好,len(seq) = 20 时慢了 20%,len(seq) = 10000 时慢了 7%
zip(*[seq[i:(len(seq) - n + 1 + i)] for i in range(n)])
这相当于:
win_list = []
for win_start_idx in range(n):
win_end_idx = (len(seq) - n + 1 + i)
win_list.append(seq[win_start_idx:win_end_idx])
win_list = zip(*win_list) # transpose rows of columns to columns of rows
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件