Dönen veya sürgülü pencere yineleyicisi mi?


159

Bir dizi / yineleyici / oluşturucu üzerinde yinelenebilen bir kayan pencereye (diğer bir deyişle kayan pencere) ihtiyacım var. Varsayılan Python yinelemesi, pencere uzunluğunun 1 olduğu özel bir durum olarak kabul edilebilir. Şu anda aşağıdaki kodu kullanıyorum. Bunu yapmak için daha Pythonic, daha az ayrıntılı veya daha verimli bir yöntemi olan var mı?

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]
"""

4
Yinelediğinizde (örneğin sum()veya max()) her pencerede bir tür işlem gerçekleştirmek istiyorsanız, her pencere için yeni değeri sabit zamanda (pencere boyutundan bağımsız olarak) hesaplamak için etkili algoritmalar olduğunu unutmamak gerekir . Bu algoritmalardan bazılarını bir Python kitaplığında topladım: yuvarlanma .
Alex Riley

Yanıtlar:


124

Python belgelerinin eski bir sürümünde itertoolsörneklerle birlikte bir tane var :

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

Dokümanlardaki yazı biraz daha özlü ve itertoolssanırım daha büyük etki için kullanıyor .


2
Güzel cevap, ama (ve tarifi bağlantılı olarak yeniden ürettiğinizi biliyorum), merak ediyorum neden varsayılan pencere boyutu 2 olmalıdır? Hiç bir varsayılanı olmalı mı?
SingleNegationElimination

19
@TakenMacGuy: Bu tarifin yazarının mantığının ne olduğunu bilmiyorum, ama ben de 2. 2 en küçük kullanışlı pencere boyutu (aksi takdirde sadece yineliyorsunuz ve pencereye ihtiyacınız yok) ve bu da yaygın önceki (veya sonraki) öğeyi, muhtemelen diğer herhangi bir belirli n'den daha fazla bilmeye ihtiyaç duymak.
lütfen

27
Bu örneğin neden belgelerden kaldırıldığını bilen var mı? Bunda bir sorun mu var yoksa daha kolay bir alternatif mi var?
2013

12
örnek kaldırmayı merak etti ve rhettinger'ın 26 Ekim 2003'te yapıldığını
ikinci

2
Döngüye ne zaman girilir for elem in it?
Glassjawed

48

Bu, bir collections.deque aslında bir FIFO'ya sahip olduğunuz için (bir uca ekleyin, diğerinden çıkarın) . Ancak, a kullansanız bile listiki kez dilimlememelisiniz; bunun yerine, muhtemelen pop(0)listeden ve append()yeni öğeden yapmalısınız .

Aşağıda, orijinalinizden sonra düzenlenmiş, optimize edilmiş bir sekme tabanlı uygulama verilmiştir:

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

Testlerimde, çoğu zaman burada yayınlanan diğer her şeyi kolayca yener, ancak pillmuncher'ın teesürümü büyük yinelemeler ve küçük pencereler için onu yener. Daha büyük pencerelerdedeque çekişler tekrar ham hızda öne çıkar.

İçindeki tek tek öğelere erişim deque, liste veya tuple'lardan daha hızlı veya daha yavaş olabilir. (Başa yakın öğeler daha hızlıdır veya negatif indeks kullanırsanız sona yakın öğeler.) Döngümün sum(w)gövdesine bir koyarım; bu, deque'in gücüne etki eder (bir öğeden diğerine yineleme hızlıdır, bu nedenle bu döngü, bir sonraki en hızlı yöntem olan pillmuncher'dan% 20 daha hızlı koştu). Onluk bir pencerede tek tek bakmak ve öğeleri eklemek için değiştirdiğimde, tablolar döndü ve teeyöntem% 20 daha hızlıydı. Ekteki son beş dönem için negatif indeksler kullanarak biraz hız kazanmayı başardım, ancak teeyine de biraz daha hızlıydım. Genel olarak, birinin çoğu kullanım için çok hızlı olduğunu ve biraz daha fazla performansa ihtiyacınız varsa, profilinizi çıkarın ve en iyi olanı seçin.


11
yield winolmalı yield tuple(win)ya da yield list(win)aynı referanslar arasında bir yineleyici dönmesini önlemek için dequebir nesne.
Joel Cornett

1
Bunu PyPI'ye gönderdim . İle yükleyin pip install sliding_windowve çalıştırın from sliding_window import window.
Thomas Levine

1
list(window(range(10)))[[0,1], [1,2], [2,3], ...] gibi bir şey üretmesi gerektiğini düşünüyorsanız şoka
Paul

1
Açıkçası olmayacak; buna benzer bir şey yapmanız list(list(x) for x in window(range(10)))veya bunu yineleyiciye eklemeniz gerekir . Bazı uygulamalar için bu Başkaları için değil, önemli olacak ve ben hız için gidiyordu beri seçildi değil ve gerekirse pencereyi kopyalamak için arayan üzerine bir görev yüklediği.
kindall

1
Verimden tuple()önce ihtiyaç duyulan miktarı geri eklerseniz bu yöntemin diğerlerine göre bir avantajı yoktur.
kawing-chiu

36

Ben beğendim 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)

verir:

[0, 1, 2]
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]

Hızlı timeittestlerime göre, bu Daniel DePaolo'nunkinden (yaklaşık 2: 1 oranında) çok daha yavaş ve "daha hoş" hissetmiyor.
David B.

@David B .: Kutumda Daniel DePaolo'dan sadece% 8 daha yavaş.
pillmuncher

@pillmuncher: Python 2.7 veya 3.x? 2.7 kullanıyordum. Oran ayrıca değerine oldukça duyarlıdır size. Arttırırsanız (örneğin, yinelenebilir 100000 öğe uzunluğundaysa, pencere boyutunu 1000 yapın), bir artış görebilirsiniz.
David B.

2
@David B .: Söyledikleriniz mantıklı. Benim kodumda kurulum süresi itersO (boyut!) Ve next()birçok kez arama (inç izip()) muhtemelen bir demeti iki kez kopyalamaktan çok daha fazla zaman alıyor. Python 2.6.5, BTW kullanıyordum.
pillmuncher

@pillmuncher: Yani, kurulum zamanı itersO (boyut ^ 2), değil mi?
David B.

20

İşte için destek ekler bir genelleme step, fillvalueparametreler:

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))

Gerektiğinde her bir parçayı dolduran yineleme başına sizedönme steppozisyonlarında bir seferde yığın öğeleri verir fillvalue. Örnek 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 * *]

stepParametrenin kullanım örneği için bkz. Python'da büyük bir .txt dosyasını verimli bir şekilde işleme .


18

Tam olarak ihtiyacınız olanı yapan bir kütüphane var:

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)]

1
step=3aslında OP'nin talebiyle eşleşecek şekilde kaldırılmalıdır:list(more_itertools.windowed(range(6), 3))
teichert

10

Sadece hızlı bir katkı.

Mevcut python belgelerinde itertool örneklerinde "pencere" bulunmadığından (yani, http://docs.python.org/library/itertools.html'nin altında ), işte gruplayıcı için kodu temel alan bir pasaj verilen örneklerden biridir:

import itertools as it
def window(iterable, size):
    shiftedStarts = [it.islice(iterable, s, None) for s in xrange(size)]
    return it.izip(*shiftedStarts)

Temel olarak, her biri bir nokta ileride başlangıç ​​noktasına sahip bir dizi dilimlenmiş yineleyici oluşturuyoruz. Sonra bunları birlikte fermuarlıyoruz. Bu işlevin bir jeneratör döndürdüğüne dikkat edin (doğrudan bir jeneratör değildir).

Yukarıdaki ek eleman ve ilerleyen yineleyici sürümlerine çok benzer şekilde, performans (yani en iyisi) liste boyutuna ve pencere boyutuna göre değişir. Bunu beğendim çünkü iki satırlı (tek satırlı olabilir, ancak kavramları adlandırmayı tercih ederim).

Yukarıdaki kodun yanlış olduğu ortaya çıktı . Parametre yinelenebilir duruma geçerse çalışır , ancak bir yineleyici değilse çalışır. Eğer bir yineleyiciyse, aynı yineleyici islice çağrıları arasında paylaşılır (ancak tee'd edilmez) ve bu, işleri kötü bir şekilde bozar.

İşte bazı sabit kodlar:

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)

Ayrıca kitaplar için bir versiyon daha. Bir yineleyiciyi kopyalamak ve ardından kopyaları birçok kez ilerletmek yerine, bu sürüm, başlangıç ​​konumunu ileriye doğru hareket ettirirken her yineleyicinin ikili kopyalarını oluşturur. Böylece, yineleyici t hem "tam" yineleyiciyi başlangıç ​​noktası t'de hem de yineleyici t + 1 oluşturmak için temel sağlar:

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)

9

Sadece itertoolstarifleri nasıl birleştirebileceğinizi göstermek pairwiseiçin, windowtarifi kullanarak tarifi mümkün olduğunca doğrudan tarife genişletiyorum 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)

windowTarifi aynıdır pairwisesadece ikinci tek eleman "tüketmek" yerine, teekademeli üzerinde tüketir artan -ed yineleyici n - 1Yineleyicilerin. consumeHer yineleyiciyi sarmak yerine kullanmak islice, marjinal olarak daha hızlıdır (yeterince büyük yinelemeler için), çünkü her pencereli değeri ayıklama işlemi sırasında değil, yalnızca aşama islicesırasında sarma ek yükünü ödersiniz consume(bu nedenle n, içindeki öğelerin sayısı ile sınırlı değildir)iterable ).

Performans açısından, diğer bazı çözümlerle karşılaştırıldığında, bu oldukça iyi (ve ölçeklenirken test ettiğim diğer çözümlerden daha iyi). ipython %timeitSihir kullanılarak Python 3.5.0, Linux x86-64 üzerinde test edilmiştir .

kindall bu dequeçözelti kullanılarak performansı / doğruluğu tweaked, islicebunun yerine bir ev haddelenmiş jeneratör ifade ve iterable penceresinden daha kısa olduğunda sonuç vermezse bu nedenle ortaya çıkan uzunluğu test olarak geçen maxlenbir dequepozisyon olarak yerine anahtar kelimeye göre (daha küçük girdiler için şaşırtıcı bir fark yaratır):

>>> %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

Önceki uyarlanmış genel çözümle aynıdır, ancak her biri yield windeğiştirildiğinde yield tuple(win), jeneratörden alınan sonuçların depolanması, depolanan tüm sonuçlar gerçekten en son sonucun bir görünümü olmadan çalışır (bu senaryoda diğer tüm makul çözümler güvenlidir) ve tuple=tupleişlev tanımına ekleme yapılır. kullanımını taşımak için tuplegelen Biçinde LEGBiçin 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-yukarıda gösterilen tabanlı çözüm:

>>> %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

Aynı consumefakat inlining elsevaka consumeönlemek işlev çağrısı ve n is Nonetestin özellikle kurulum havai işin anlamlı bir parçası olan küçük girişler için, çalışma zamanı azaltmak için:

>>> %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

(Yan not: İç içe nesneler yapmak için 2 varsayılan argümanını tekrar tekrar pairwisekullanan bir varyant, bu nedenle herhangi bir yineleyici yalnızca bir kez geliştirilir, bağımsız olarak artan sayıda tüketilmez, MrDrFenner'ın cevabına benzer şekilde satır içi olmayanlara benzer ve tüm testlerde satır içi belirtilenden daha yavaştır , bu yüzden kısalık için bu sonuçları göz ardı ettim).teeteeconsumeconsume

Gördüğünüz gibi , arayanın sonuçları saklamaya ihtiyaç duyma olasılığını umursamıyorsanız, "büyük yinelenebilir, küçük pencere boyutu durumu" dışında (satır içi consumekazançların olduğu yerde) , kindall çözümünün optimize edilmiş sürümü çoğu zaman kazanır. ); yinelenebilir boyut arttıkça hızla küçülür, ancak pencere boyutu büyüdükçe küçülmez (diğer tüm çözümler yinelenebilir boyut artışları için daha yavaş küçülür, aynı zamanda pencere boyutu büyüdükçe küçülür). Hatta sarmalayarak "ihtiyaç demetleri" durumuna göre uyarlanabilir map(tuple, ...), bu da tupling'i işleve yerleştirmekten çok daha yavaş çalışır, ancak önemsizdir (% 1-5 daha uzun sürer) ve daha hızlı koşma esnekliğini korumanıza izin verir Tekrar tekrar aynı değeri döndürmeyi tolere edebildiğinizde.

İadelerin depolanmasına karşı güvenliğe ihtiyacınız varsa consume, en küçük girdi boyutları dışında satır içi kazanır (satır içi olmayanlar consumebiraz daha yavaştır, ancak benzer şekilde ölçeklenir). deque& Sadece nedeniyle küçük kurulum maliyetlerine en küçük girişler için esaslı çözüm kazanıyor tupling ve kazanç küçüktür; yinelenebilir süre uzadıkça kötü bir şekilde bozulur.

Kayıt, o kindall çözümünü uygun uyarlanan versiyonu için yields adlı tupleler benim kullandığım oldu:

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)

Daha hızlı ancak daha az güvenli sürümü elde etmek tupleiçin işlev tanımı satırına önbelleğe almayı ve tupleher birinin kullanımını bırakın yield.


Açıkçası, bu olabileceğinden daha az etkilidir; consumegenel amaçlıdır (eksiksiz yapma yeteneği dahil consume) ve bu nedenle ekstra bir içe aktarma ve kullanım başına test gerektirir n is None. Gerçek kodda, yalnızca ve ancak performansın bir sorun olduğunu belirleseydim veya gerçekten daha kısa bir koda ihtiyacım olsaydı , başka bir şey için kullanmadığımı varsayarak elsedurumunu consumeiçine aktarmayı düşünürdüm . Ancak performansın bir sorun olduğu gösterilmediyse, ayrı tanımları tutarım; adlandırılmış işlev, işlemi daha az sihirli / kendi kendini belgeleyen hale getirir. windowconsumeconsume
ShadowRanger

7

Aşağıdaki kodu, okunabilirliği büyük ölçüde artırmak için jeneratörleri kullanan basit bir kayan pencere olarak kullanıyorum. Deneyimlerime göre hızı, biyoinformatik sekans analizinde kullanım için şimdiye kadar yeterli olmuştur.

Buraya dahil ettim çünkü bu yöntemin henüz kullanıldığını görmedim. Yine, karşılaştırılan performansı hakkında hiçbir iddiada bulunmuyorum.

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]

3
Buradaki en büyük dezavantaj len(sequence)çağrıdır. sequenceYineleyici veya oluşturucu ise bu işe yaramaz . Giriş belleğe sığdığında, bu yineleyicilerden daha okunabilir bir çözüm sunar.
David B.

Evet haklısın. Bu özel durum, başlangıçta, genellikle dizeler olarak temsil edilen DNA dizilerinin taranması içindir. Bahsettiğiniz sınırlamalar kesinlikle VAR. İsterseniz, her bir dilimi hala doğru uzunlukta olduğundan emin olmak için test edebilir ve ardından tüm dizinin uzunluğunu bilmek zorunda kalmayı unutabilirsiniz. Ancak biraz daha fazla ek yük (her yinelemede bir len () testi) ekler.
Gus

6
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] ]

Python'da "menzil" i gördüğünüz anda bu bir kod kokusudur.
Mark Lawrence

@MarkLawrence Python'da range(lenkötü bir model olduğunu düşündüren nedir ?
duhaime

5

gerçek bir kayan pencere yapmak için, dekor penceresinin biraz değiştirilmiş bir versiyonu. Böylece sadece bir elementle doldurulmaya başlar, sonra maksimum pencere boyutuna büyür ve sonra sol kenarı sona yaklaştıkça küçülür:

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))

bu verir

[0]
[0, 1]
[0, 1, 2]
[1, 2, 3]
[2, 3, 4]
[3, 4]
[4]

3
def rolling_window(list, degree):
    for i in range(len(list)-degree+1):
        yield [list[i+o] for o in range(degree)]

Bunu hareketli ortalama işlevi için yaptı


3

neden olmasın

def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = tee(iterable)
    next(b, None)
    return zip(a, b)

Bu Python belgelenmiştir doc . Daha geniş bir pencereye kolayca genişletebilirsiniz.


3

Tembel yapalım!

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)]

2

Birden çok yineleyici!

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)StopIterationsıra bittiğinde yükselir ve benim dışımda olan bazı hoş nedenlerden dolayı, buradaki verim ifadesi onu hariç tutar ve fonksiyon, tam bir pencere oluşturmayan kalan değerleri göz ardı ederek geri döner.

Neyse, bu kimin tek gereksinim olduğunu az hatları çözüm henüz seqya uygulamak __iter__veya __getitem__ve dayanmaz itertoolsveya collections@ dansalmo çözümüyle yanında :)


not: kademeli adım O (n ^ 2) olup, burada n, pencerenin boyutudur ve yalnızca ilk çağrıda gerçekleşir. O (n) 'ye kadar optimize edilebilir, ancak kodu biraz daha
karmaşık

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]

"" "


3
Lütfen cevabınız hakkında bir metin yazın.
jrswgtr

1

Birkaç çözümü ve bulduğum bir çözümü test ettim ve bulduğum çözümü en hızlı buldum, bu yüzden paylaşacağımı düşündüm.

import itertools
import sys

def windowed(l, stride):
    return zip(*[itertools.islice(l, i, sys.maxsize) for i in range(stride)])

1
Bu yanıttaki ilk çözüme benziyor: stackoverflow.com/a/11249883/7851470
Georgy

@georgy Sanırım bu cevabı Python2 ile yazıldığı için atladım ama katılıyorum, aslında aynı!
Ryan Codrai

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]

0

Aşağıdakileri kullanmaya ne dersiniz:

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)

Çıktı:

[(1, 2, 3), (2, 3, 4), (3, 4, 5), (4, 5, 6), (5, 6, 7)]

@ keocra zip (* t) ne anlama geliyor? Bu tür bir ifadeyle ilgili bazı belgeleri nerede bulabilirim?
Shejo284

1
Python 2.7: docs.python.org/2/library/functions.html#zip , yıldız listeyi açar ve tek tek öğeleri
zip'e

0

Bu eski bir sorudur, ancak hala ilgilenenler için bu sayfada jeneratörlerin kullanıldığı harika bir pencere kaydırıcısı uygulaması vardır (Adrian Rosebrock tarafından).

OpenCV için bir uygulamadır, ancak başka herhangi bir amaç için kolayca kullanabilirsiniz. Hevesli olanlar için kodu buraya yapıştıracağım, ancak daha iyi anlamak için orijinal sayfayı ziyaret etmenizi öneririm.

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]])

İpucu:.shape Gereksinimlerinizi karşılamayanları atmak için jeneratörü yinelerken pencereyi kontrol edebilirsiniz .

Şerefe


0

Modifiye DiPaolo yanıtını keyfi dolgu ve değişken adım boyutu sağlamak için

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

0

işte tek astar. Zamanladım ve en iyi cevabın performansıyla karşılaştırılabilir ve len (seq) = 20 ile% 20 ve len (seq) = 10000 ile% 7 daha yavaş olan daha büyük seq ile giderek daha iyi hale geliyor

zip(*[seq[i:(len(seq) - n - 1 + i)] for i in range(n)])

Lütfen cevabınızla birlikte açıklayıcı bir metin ekleyin. Bu iş parçacığına rastlayan herkes bir Python Ninja değildir.
Abhijit Sarkar

bu 2 farkla kapalı, bu işe yarar: zip (* [seq [i: (len (seq) - n + 1 + i)], aralıktaki i için (n)])
Gösta Forsum

0

Benim rolümü denemek, basit, tek astarlı, islice kullanarak pitonik yol. Ancak optimum düzeyde verimli olmayabilir.

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]]

Explanation: pencere boyutunun bir bölümünü kullanarak pencere oluşturun ve bu işlemi tüm dizi üzerinde eşleme kullanarak yineleyin.


0

Derin öğrenmede kayan pencere verileri için Optimize Edilmiş İşlev

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)

çok boyutlu diziye uygulamak için

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)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.