Itérateur de fenêtre roulante ou coulissante?

151

J'ai besoin d'une fenêtre déroulante (aka fenêtre glissante) itérable sur une séquence / itérateur / générateur. L'itération Python par défaut peut être considérée comme un cas spécial, où la longueur de la fenêtre est 1. J'utilise actuellement le code suivant. Quelqu'un a-t-il une méthode plus pythonique, moins verbeuse ou plus efficace pour faire cela?

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]
"""
David B.
la source
3
Si vous cherchez à effectuer une sorte d'opération sur chaque fenêtre pendant que vous itérez (par exemple sum()ou max()), il convient de garder à l'esprit qu'il existe des algorithmes efficaces pour calculer la nouvelle valeur pour chaque fenêtre en temps constant (quelle que soit la taille de la fenêtre). J'ai rassemblé certains de ces algorithmes dans une bibliothèque Python: rolling .
Alex Riley

Réponses:

123

Il y en a un dans une ancienne version de la documentation Python avec des itertoolsexemples :

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

Celui de la documentation est un peu plus succinct et utilise itertoolsplus efficacement j'imagine.

Daniel DiPaolo
la source
2
Bonne réponse, mais (et je sais que vous ne faites que reproduire la recette sous forme de lien), je me demande pourquoi la taille de la fenêtre par défaut devrait être de 2? Devrait-il avoir un défaut du tout?
SingleNegationElimination
19
@TakenMacGuy: Je ne sais pas quel est l'auteur du raisonnement de cette recette, mais je choisirais également 2. 2 est la plus petite taille de fenêtre utile (sinon, vous ne faites qu'itérer et n'avez pas besoin de la fenêtre), et c'est aussi courant avoir besoin de connaître l'élément précédent (ou suivant), sans doute plus que tout autre n spécifique.
kindall
27
Quelqu'un sait-il pourquoi cet exemple a été supprimé de la documentation? Y avait-il quelque chose qui n'allait pas, ou il existe maintenant une alternative plus simple?
wim
12
s'est intéressé à la suppression de l'exemple et a trouvé rhettinger commis le 26 octobre 2003: Remplacez l'exemple window () par pairwise () qui démontre tee ().
deuxième
2
Quand entrerait-on dans la for elem in itboucle?
Glassjawed
47

Cela semble fait sur mesure pour un collections.dequepuisque vous avez essentiellement un FIFO (ajouter à une extrémité, supprimer de l'autre). Cependant, même si vous utilisez un, listvous ne devriez pas trancher deux fois; au lieu de cela, vous devriez probablement juste à pop(0)partir de la liste et append()du nouvel élément.

Voici une implémentation optimisée basée sur deque calquée sur votre original:

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

Dans mes tests, il bat facilement tout le reste affiché ici la plupart du temps, bien que la teeversion de pillmuncher le bat pour les grandes itérables et les petites fenêtres. Sur les fenêtres plus grandes, le dequetire à nouveau en vitesse brute.

L'accès aux éléments individuels dans le dequepeut être plus rapide ou plus lent qu'avec des listes ou des tuples. (Les éléments proches du début sont plus rapides, ou les éléments proches de la fin si vous utilisez un index négatif.) J'ai mis un sum(w)dans le corps de ma boucle; cela joue à la force du deque (l'itération d'un élément à l'autre est rapide, donc cette boucle s'est déroulée 20% plus vite que la méthode la plus rapide suivante, pillmuncher). Lorsque je l'ai changé pour rechercher et ajouter des éléments individuellement dans une fenêtre de dix, les tables ont tourné et la teeméthode était 20% plus rapide. J'ai pu récupérer de la vitesse en utilisant des indices négatifs pour les cinq derniers termes de l'addition, mais j'étais teeencore un peu plus rapide. Dans l'ensemble, je dirais que l'un ou l'autre est très rapide pour la plupart des utilisations et si vous avez besoin d'un peu plus de performances, profilez et choisissez celui qui fonctionne le mieux.

kindall
la source
11
yield windevrait être yield tuple(win)ou yield list(win)pour éviter de renvoyer un itérateur de références au même dequeobjet.
Joel Cornett
1
J'ai soumis ceci à PyPI . Installez avec pip install sliding_windowet exécutez avec from sliding_window import window.
Thomas Levine
1
Vous êtes sous le choc si vous pensez qu'il list(window(range(10)))devrait produire quelque chose comme [[0,1], [1,2], [2,3], ...]
Paul
1
Ce ne sera évidemment pas le cas; vous devez faire quelque chose comme list(list(x) for x in window(range(10)))ou ajouter cela à l'itérateur. Pour certaines applications, cela importera, pour d'autres pas, et comme je voulais la vitesse, j'ai choisi de ne pas le faire et j'ai mis la responsabilité de l'appelant de copier la fenêtre si nécessaire.
kindall
1
Si vous rajoutez le tuple()rendement avant nécessaire , cette méthode n'a aucun avantage sur les autres.
kawing-chiu
35

J'aime 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)

donne:

[0, 1, 2]
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
pillmuncher
la source
D'après mes timeittests rapides , c'est beaucoup plus lent que celui de Daniel DePaolo (d'un rapport d'environ 2: 1) et ne me semble pas beaucoup plus "agréable".
David B.
@David B.: Sur ma boîte, c'est seulement environ 8% plus lent que celui de Daniel DePaolo.
pillmuncher
@pillmuncher: Python 2.7 ou 3.x? J'utilisais 2.7. Le ratio est également assez sensible à la valeur de size. Si vous l'augmentez (par exemple, si l'itérable fait 100 000 éléments, augmentez la taille de la fenêtre de 1 000), vous pouvez voir une augmentation.
David B.
2
@David B.: Ce que vous dites a du sens. Dans mon code, le temps de configuration pour itersest O (taille!), Et appeler next()plusieurs fois (in izip()) prend probablement beaucoup plus de temps que de copier un tuple deux fois. J'utilisais Python 2.6.5, BTW.
pillmuncher
@pillmuncher: Vous voulez dire, le temps de configuration itersest O (taille ^ 2), non?
David B.
20

Voici une généralisation qui ajoute le support pour step, fillvalueparamètres:

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

Il donne des sizeéléments par blocs à la fois, des steppositions de roulement par itération en complétant chaque bloc fillvaluesi nécessaire. Exemple pour 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 * *]

Pour obtenir un exemple de cas d'utilisation du stepparamètre, consultez Traitement efficace d'un fichier .txt volumineux en python .

jfs
la source
17

Il existe une bibliothèque qui fait exactement ce dont vous avez besoin:

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)]
Nikolay Frick
la source
step=3devrait en fait être supprimé pour correspondre à la demande de l'OP:list(more_itertools.windowed(range(6), 3))
user3780389
10

Juste une contribution rapide.

Étant donné que les documents python actuels n'ont pas de "fenêtre" dans les exemples d'itertool (c'est-à-dire au bas de http://docs.python.org/library/itertools.html ), voici un extrait de code basé sur le code du groupeur qui est l'un des exemples donnés:

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

Fondamentalement, nous créons une série d'itérateurs découpés, chacun avec un point de départ un point plus loin. Ensuite, nous les compressons ensemble. Notez que cette fonction renvoie un générateur (ce n'est pas directement un générateur en lui-même).

Tout comme les versions d'élément d'ajout et d'itérateur avancé ci-dessus, les performances (c'est-à-dire, ce qui est le meilleur) varient en fonction de la taille de la liste et de la taille de la fenêtre. J'aime celui-ci car il s'agit d'un bi-liner (ça pourrait être un one-liner, mais je préfère nommer les concepts).

Il s'avère que le code ci-dessus est incorrect . Cela fonctionne si le paramètre est passé à iterable est une séquence mais pas s'il s'agit d'un itérateur. S'il s'agit d'un itérateur, le même itérateur est partagé (mais pas tee'd) entre les appels islice et cela casse gravement les choses.

Voici un code fixe:

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)

Aussi, une autre version pour les livres. Au lieu de copier un itérateur puis de faire avancer les copies plusieurs fois, cette version fait des copies par paires de chaque itérateur lorsque nous avançons la position de départ. Ainsi, l'itérateur t fournit à la fois l'itérateur "complet" avec le point de départ en t et aussi la base pour créer l'itérateur 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)
MrDrFenner
la source
9

Juste pour montrer comment vous pouvez combiner des itertoolsrecettes , je prolonge la pairwiserecette aussi directement que possible dans la windowrecette en utilisant la consumerecette:

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)

La windowrecette est la même que pour pairwise, elle remplace simplement l'élément unique «consommer» sur le deuxième teeitérateur avec des consommations progressivement croissantes sur les n - 1itérateurs. Utiliser consumeau lieu d'encapsuler chaque itérateur dans isliceest légèrement plus rapide (pour des itérables suffisamment volumineux) puisque vous ne payez les islicefrais généraux d'encapsulation que pendant la consumephase, pas pendant le processus d'extraction de chaque valeur fenêtrée (il est donc limité par n, pas par le nombre d'éléments dans iterable).

En termes de performances, par rapport à certaines autres solutions, c'est plutôt bon (et meilleur que toutes les autres solutions que j'ai testées à mesure qu'elle évolue). Testé sur Python 3.5.0, Linux x86-64, en utilisantipython %timeit magie.

kindall est la dequesolution , ajustée pour la performance / l'exactitude en utilisant isliceau lieu d'une expression de générateur lancée à la maison et en testant la longueur résultante afin qu'elle ne donne pas de résultats lorsque l'itérable est plus court que la fenêtre, ainsi que le passage maxlende la dequeposition au lieu de par mot-clé (fait une différence surprenante pour les entrées plus petites):

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

Identique à la solution kindall adaptée précédente, mais avec chacune des yield winmodifications apportées, le yield tuple(win)stockage des résultats du générateur fonctionne sans que tous les résultats stockés ne soient vraiment une vue du résultat le plus récent (toutes les autres solutions raisonnables sont sûres dans ce scénario), et en ajoutant tuple=tupleà la définition de la fonction pour déplacer l'utilisation de tuplede l' Bentrée LEGBvers la 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

consumesolution à base illustrée ci-dessus:

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

Identique à consume, mais dans le elsecas de consumepour éviter l'appel de fonction et de n is Nonetester pour réduire le temps d'exécution, en particulier pour les petites entrées où la surcharge de configuration est une partie significative du travail:

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

(Note latérale: une variante pairwisequi utilise teel'argument par défaut de 2 à plusieurs reprises pour créer des teeobjets imbriqués , de sorte que tout itérateur donné n'est avancé qu'une seule fois, pas consommé indépendamment un nombre croissant de fois, similaire à la réponse de MrDrFenner est similaire à non-inline consumeet plus lent que l'inline consumesur tous les tests, j'ai donc omis ces résultats par souci de concision).

Comme vous pouvez le voir, si vous ne vous souciez pas de la possibilité que l'appelant ait besoin de stocker les résultats, ma version optimisée de la solution de kindall l'emporte la plupart du temps, sauf dans le cas du "grand itérable, petite taille de fenêtre" (où l'inline consumegagne ); il se dégrade rapidement à mesure que la taille itérable augmente, sans se dégrader du tout à mesure que la taille de la fenêtre augmente (chaque solution sur deux se dégrade plus lentement lorsque la taille itérable augmente, mais se dégrade également lorsque la taille de la fenêtre augmente). Il peut même être adapté pour le cas "besoin de tuples" en enveloppant map(tuple, ...), ce qui est un peu plus lent que de mettre le tupling dans la fonction, mais c'est trivial (prend 1-5% de plus) et vous permet de garder la flexibilité de courir plus vite lorsque vous pouvez tolérer le renvoi répété de la même valeur.

Si vous avez besoin de sécurité contre le stockage des retours, les entrées en ligne l' consumeemportent sur toutes les tailles d'entrée sauf les plus petites (la non-ligne consumeétant légèrement plus lente mais mise à l'échelle de la même manière). ledeque solution basée sur le & tupling ne gagne que pour les plus petites entrées, en raison de coûts de configuration plus faibles, et le gain est faible; il se dégrade gravement à mesure que l'itérable s'allonge.

Pour mémoire, la version adaptée de la solution de Kindall qui yields tuplede j'étais:

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)

Supprimez la mise tupleen cache de dans la ligne de définition de fonction et l'utilisation de tupledans chacune yieldpour obtenir la version la plus rapide mais la moins sûre.

ShadowRanger
la source
De toute évidence, c'est moins efficace que cela ne pourrait l'être; consumeest un usage général (y compris la possibilité de faire un complet consume) et nécessite donc une importation supplémentaire et un test par utilisation pour n is None. Dans le vrai code, si et seulement si j'avais déterminé que les performances étaient un problème, ou si j'avais vraiment besoin d'un code plus concis, j'envisagerais d'inclure le elsecas de consumeinto window, en supposant que je n'utilise pasconsume pour rien d'autre. Mais si la performance ne s'est pas révélée être un problème, je conserverais les définitions séparées; la consumefonction nommée rend l'opération moins magique / auto-documentée.
ShadowRanger
7

J'utilise le code suivant comme une simple fenêtre coulissante qui utilise des générateurs pour augmenter considérablement la lisibilité. Sa vitesse a jusqu'à présent été suffisante pour une utilisation dans l'analyse de séquence bioinformatique d'après mon expérience.

Je l'inclus ici car je n'ai pas encore vu cette méthode utilisée. Encore une fois, je ne fais aucune déclaration sur ses performances comparées.

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]
Gus
la source
3
Le principal inconvénient ici est l' len(sequence)appel. Cela ne fonctionnera pas s'il sequences'agit d'un itérateur ou d'un générateur. Lorsque l'entrée tient dans la mémoire, cela offre une solution plus lisible qu'avec les itérateurs.
David B.
Oui tu as raison. Ce cas particulier était à l'origine destiné à scanner des séquences d'ADN qui sont généralement représentées sous forme de chaînes. Il a certainement la limitation que vous mentionnez. Si vous le souhaitez, vous pouvez simplement tester chaque tranche pour vous assurer qu'elle a toujours la bonne longueur, puis oublier d'avoir à connaître la longueur de toute la séquence. Mais cela ajouterait un peu plus de surcharge (un test len ​​() à chaque itération).
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] ]
heyyou482
la source
À l'instant où vous voyez "range (len" en Python, c'est une odeur de code.
Mark Lawrence
@MarkLawrence Qu'est-ce qui vous fait penser range(lenqu'un mauvais modèle en python?
duhaime
5

une version légèrement modifiée de la fenêtre deque, pour en faire une véritable fenêtre déroulante. Pour qu'il commence à être rempli avec un seul élément, puis augmente jusqu'à sa taille de fenêtre maximale, puis se rétrécit à mesure que son bord gauche approche de la fin:

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

cela donne

[0]
[0, 1]
[0, 1, 2]
[1, 2, 3]
[2, 3, 4]
[3, 4]
[4]
Dmitry Avtonomov
la source
3
def rolling_window(list, degree):
    for i in range(len(list)-degree+1):
        yield [list[i+o] for o in range(degree)]

Fait ceci pour une fonction moyenne mobile

Yazdmich
la source
3

pourquoi pas

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

Il est documenté dans la doc Python . Vous pouvez facilement l'étendre à une fenêtre plus large.

WeiChing 林 煒 清
la source
2

Plusieurs itérateurs!

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)déclenche StopIterationlorsque la séquence est terminée, et pour une raison intéressante qui me dépasse, l'instruction yield l'excepte et la fonction retourne, ignorant les valeurs restantes qui ne forment pas une fenêtre complète.

Quoi qu'il en soit, c'est la solution des moindres lignes dont la seule exigence est d' seqimplémenter __iter__ou __getitem__et ne repose pas sur itertoolsou en collectionsplus de la solution de @ dansalmo :)

jameh
la source
note: le pas de décalage est O (n ^ 2) où n est la taille de la fenêtre, et ne se produit que lors du premier appel. Il pourrait être optimisé jusqu'à O (n), mais cela rendrait le code un peu plus compliqué: P
jameh
2

Faisons paresseux!

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)]
Gramme
la source
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]

"" "

FAYAZ
la source
3
Veuillez écrire un texte sur votre réponse.
jrswgtr
1

J'ai testé quelques solutions et celle que j'ai trouvée et j'ai trouvé celle que j'avais proposée pour être la plus rapide, alors j'ai pensé la partager.

import itertools
import sys

def windowed(l, stride):
    return zip(*[itertools.islice(l, i, sys.maxsize) for i in range(stride)])
Ryan Codrai
la source
1
Ressemble à la première solution de cette réponse: stackoverflow.com/a/11249883/7851470
Georgy
@georgy Je pense que j'ai sauté cette réponse car elle a été écrite en Python2 mais je suis d'accord, c'est essentiellement la même chose!
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]
dansalmo
la source
0

Que diriez-vous d'utiliser ce qui suit:

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)

Production:

[(1, 2, 3), (2, 3, 4), (3, 4, 5), (4, 5, 6), (5, 6, 7)]
keocra
la source
@ keocra que signifie zip (* t)? Où puis-je trouver de la documentation sur ce genre de déclaration?
Shejo284
1
Python 2.7: docs.python.org/2/library/functions.html#zip , l'étoile décompresse la liste et fournit les éléments individuels en entrée de zip ( déballage des arguments )
keocra
0

C'est une vieille question, mais pour ceux qui sont toujours intéressés, il existe une excellente implémentation d'un curseur de fenêtre utilisant des générateurs dans ce page (par Adrian Rosebrock).

C'est une implémentation pour OpenCV mais vous pouvez facilement l'utiliser à d'autres fins. Pour les plus désireux, je vais coller le code ici mais pour mieux le comprendre, je recommande de visiter la page d'origine.

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

Astuce: vous pouvez vérifier la .shapede la fenêtre lors de l'itération du générateur pour éliminer ceux qui ne répondent pas à vos besoins

À votre santé

DarkCygnus
la source
0

Modification de la réponse de DiPaolo pour permettre un remplissage arbitraire et une taille de pas variable

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
devrait voir
la source
0

voici une seule ligne. Je l'ai chronométré et il est compatible avec les performances de la réponse supérieure et s'améliore progressivement avec un seq plus grand de 20% plus lent avec len (seq) = 20 et 7% plus lent avec len (seq) = 10000

zip(*[seq[i:(len(seq) - n - 1 + i)] for i in range(n)])
kkawabat
la source
Veuillez ajouter un texte explicatif avec votre réponse. Tout le monde ne trébuche pas sur ce fil n'est pas un Python Ninja.
Abhijit Sarkar
qui est désactivé par 2, cela fonctionne: zip (* [seq [i: (len (seq) - n + 1 + i)] pour i dans la plage (n)])
Gösta Forsum
0

Essayer ma part, simple, une doublure, façon pythonique en utilisant islice. Mais, peut ne pas être d'une efficacité optimale.

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

Explication: Créez une fenêtre en utilisant islice de window_size et répétez cette opération en utilisant map sur tout le tableau.

Paras Mishra
la source
0

Fonction optimisée pour les données de fenêtre glissante dans le Deep Learning

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)
Naga Kiran
la source