我需要一个可在序列/迭代器/生成器上迭代的滚动窗口(也称为滑动窗口)。默认的Python迭代可以被认为是一种特殊情况,窗口长度为1.我目前正在使用以下代码。有没有人有更多的Pythonic,更简洁,更有效的方法呢?
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]
"""
答案 0 :(得分:106)
旧版本的Python文档中有一个itertools
examples:
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
来实现我想象的更大效果。
答案 1 :(得分:44)
这似乎是为collections.deque
量身定做的,因为你基本上有一个FIFO(添加到一端,从另一端删除)。但是,即使您使用list
,也不应该切两次;相反,您应该只从列表中pop(0)
和append()
新项目。
这是一个优化的基于deque的实现,在您的原始版本之后构建:
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)
;这对deque的强度起作用(从一个项目到另一个项目的迭代速度很快,所以这个循环比下一个最快的方法,即pillmuncher的速度快20%)。当我将其更改为单独查找并在十个窗口中添加项目时,表格已转动,tee
方法的速度提高了20%。通过在添加中使用最后五个术语的负索引,我能够恢复一些速度,但tee
仍然快一点。总的来说,我估计,对于大多数用途来说,任何一个都是快速的,如果你需要更多的性能,可以选择最适合的那个。
答案 2 :(得分:32)
我喜欢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]
答案 3 :(得分:18)
以下是对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
参数的用例示例,请参阅Processing a large .txt file in python efficiently。
答案 4 :(得分:9)
快速贡献。
由于当前的python docs在itertool示例中没有“window”(即http://docs.python.org/library/itertools.html的底部),这里是一个基于 石斑鱼的代码,这是给出的例子之一:
import itertools as it
def window(iterable, size):
shiftedStarts = [it.islice(iterable, s, None) for s in xrange(size)]
return it.izip(*shiftedStarts)
基本上,我们创建了一系列切片迭代器,每个迭代器的起点都向前一个点。然后,我们将这些拉链在一起。注意,这个函数返回一个生成器(它不直接是一个生成器本身)。
与上面的appending-element和advance-iterator版本非常相似,性能(即最佳)随列表大小和窗口大小而变化。我喜欢这个,因为它是一个双线程(它可能是一个单行,但我更喜欢命名概念)。
事实证明上述代码错误。如果传递给 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)
答案 5 :(得分:8)
为了展示如何合并itertools
recipes,我使用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
相同,它只是替换单个元素&#34;消耗&#34;在第二个tee
- ed迭代器上,对n - 1
迭代器的消耗逐渐增加。使用consume
代替在islice
中包装每个迭代器的速度稍慢(对于足够大的迭代),因为您只在islice
阶段支付consume
包装开销,而不是在提取每个窗口值的过程(因此它被n
限制,而不是iterable
中的项目数。)
性能方面,与其他一些解决方案相比,这是非常好的(并且比我测试的任何其他解决方案都要好)。在Python 3.5.0,Linux x86-64上使用ipython
%timeit
magic进行测试。
kindall's the deque
solution,通过使用islice
而不是自制滚动的生成器表达式来调整性能/正确性并测试结果长度,这样当迭代时间短于时,它就不会产生结果窗口,以及maxlen
的{{1}}位置而不是关键字(对于较小的输入产生惊人的差异):
deque
与先前适用的kindall解决方案相同,但每个>>> %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
更改为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
测试以减少运行时间,特别是对于设置开销为a的小输入有意义的工作部分:
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
重复默认参数2来制作嵌套的tee
对象,因此任何给定的迭代器只提前一次,而不是相似于MrDrFenner's answer类似于{{3}}类似于非内联tee
并且在所有测试中都比内联consume
慢,所以我省略了这些结果为简洁起见。)
正如您所看到的,如果您不关心调用者需要存储结果的可能性,我的优化版本的kindall解决方案大部分时间都会获胜,除了& #34;大型可迭代,小窗口大小的情况&#34; (内联consume
获胜);随着可迭代大小的增加,它会迅速降级,而随着窗口大小的增加,它不会降级(对于可迭代的大小增加,每个其他解决方案的降级速度都会降低,但窗口大小的增加也会降低)。它甚至可以适应“需要元组”和#34;通过包裹在consume
中的情况,其运行速度比将函数中的元组稍微慢一点,但它的重要性(需要1-5%的时间)并且让你在运行时保持更快的运行灵活性可以忍受反复返回相同的值。
如果您需要安全抵御存储的返回,则内联map(tuple, ...)
会赢取除最小输入大小之外的所有内容(非内联consume
略慢但缩放类似) 。 consume
&amp;由于设置成本较低,基于tupling的解决方案仅针对最小输入获胜,且增益很小;随着迭代次数变长,它会严重降级。
为了记录,我使用的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
以获得更快但不太安全的版本。
答案 6 :(得分:6)
我使用以下代码作为一个简单的滑动窗口,它使用生成器来大幅提高可读性。到目前为止,根据我的经验,它的速度足以用于生物信息学序列分析。
我在此处加入,因为我还没有看到这种方法。同样,我对其比较的表现没有任何说法。
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]
答案 7 :(得分:5)
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 :(得分:5)
有一个库可以满足您的需求:
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)]
答案 9 :(得分:5)
deque窗口的略微修改版本,使其成为真正的滚动窗口。所以它开始只用一个元素填充,然后增长到它的最大窗口大小,然后收缩,因为它的左边缘接近结束:
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]
答案 10 :(得分:3)
我测试了一些解决方案,然后提出了一个解决方案,发现我提出的解决方案是最快的,因此我想与大家分享。
.container {
max-width: 960px;
}
答案 11 :(得分:2)
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
或{{ 1}}除了@dansalmo的解决方案:))
答案 12 :(得分:2)
def rolling_window(list, degree):
for i in range(len(list)-degree+1):
yield [list[i+o] for o in range(degree)]
将其设为滚动平均功能
答案 13 :(得分:2)
为什么不
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None)
return zip(a, b)
它在Python doc中有记录。 您可以轻松地将其扩展到更宽的窗口。
答案 14 :(得分:1)
#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]
“”“
答案 15 :(得分:1)
让我们变得懒惰吧!
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)]
答案 16 :(得分:1)
深度学习中滑动窗口数据的优化功能
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)
答案 17 :(得分:0)
修改后的DiPaolo's answer以允许任意填充和可变步长
SELECT NAME || ' ' || SURNAME "Employee"
FROM Schema1.Table1
LEFT JOIN Schema1.Table2 u
ON Manager = u.ID
ORDER BY ID.Table1;
答案 18 :(得分:0)
我最终使用的(保持简单)解决方案:
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
序列需要是可切片的。使用索引并不理想,但考虑到替代方案,它似乎是最不坏的选择......这也可以很容易地更改为生成器:只需将 [...]
替换为 (...)
。
答案 19 :(得分:0)
另一种从列表中生成固定长度窗口的简单方法
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)
答案 20 :(得分:0)
我的 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
答案 21 :(得分:0)
使用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在所有数组上进行迭代。
答案 22 :(得分:0)
这是一个班轮。我对它进行了计时,它与最佳答案的性能相当,并且随着seq的增大,性能会逐渐好转,len(seq)= 20的速度慢20%,len(seq)= 10000的速度慢7%
zip(*[seq[i:(len(seq) - n - 1 + i)] for i in range(n)])
答案 23 :(得分:0)
这是一个老问题,但对于那些仍然感兴趣的人来说,使用this页面中的生成器(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
以丢弃那些不符合您要求的生成器
干杯
答案 24 :(得分:0)
如何使用以下内容:
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)]
答案 25 :(得分:0)
>>> n, m = 6, 3
>>> k = n - m+1
>>> print ('{}\n'*(k)).format(*[range(i, i+m) for i in xrange(k)])
[0, 1, 2]
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]