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]
"""
sum()
oumax()
), 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 .Réponses:
Il y en a un dans une ancienne version de la documentation Python avec des
itertools
exemples :Celui de la documentation est un peu plus succinct et utilise
itertools
plus efficacement j'imagine.la source
for elem in it
boucle?Cela semble fait sur mesure pour un
collections.deque
puisque vous avez essentiellement un FIFO (ajouter à une extrémité, supprimer de l'autre). Cependant, même si vous utilisez un,list
vous ne devriez pas trancher deux fois; au lieu de cela, vous devriez probablement juste àpop(0)
partir de la liste etappend()
du nouvel élément.Voici une implémentation optimisée basée sur deque calquée sur votre original:
Dans mes tests, il bat facilement tout le reste affiché ici la plupart du temps, bien que la
tee
version de pillmuncher le bat pour les grandes itérables et les petites fenêtres. Sur les fenêtres plus grandes, ledeque
tire à nouveau en vitesse brute.L'accès aux éléments individuels dans le
deque
peut ê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 unsum(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 latee
mé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'étaistee
encore 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.la source
yield win
devrait êtreyield tuple(win)
ouyield list(win)
pour éviter de renvoyer un itérateur de références au mêmedeque
objet.pip install sliding_window
et exécutez avecfrom sliding_window import window
.list(window(range(10)))
devrait produire quelque chose comme [[0,1], [1,2], [2,3], ...]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.tuple()
rendement avant nécessaire , cette méthode n'a aucun avantage sur les autres.J'aime
tee()
:donne:
la source
timeit
tests 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".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.iters
est O (taille!), Et appelernext()
plusieurs fois (inizip()
) prend probablement beaucoup plus de temps que de copier un tuple deux fois. J'utilisais Python 2.6.5, BTW.iters
est O (taille ^ 2), non?Voici une généralisation qui ajoute le support pour
step
,fillvalue
paramètres:Il donne des
size
éléments par blocs à la fois, desstep
positions de roulement par itération en complétant chaque blocfillvalue
si nécessaire. Exemple poursize=4, step=3, fillvalue='*'
:Pour obtenir un exemple de cas d'utilisation du
step
paramètre, consultez Traitement efficace d'un fichier .txt volumineux en python .la source
Il existe une bibliothèque qui fait exactement ce dont vous avez besoin:
la source
step=3
devrait en fait être supprimé pour correspondre à la demande de l'OP:list(more_itertools.windowed(range(6), 3))
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:
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:
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:
la source
Juste pour montrer comment vous pouvez combiner des
itertools
recettes , je prolonge lapairwise
recette aussi directement que possible dans lawindow
recette en utilisant laconsume
recette:La
window
recette est la même que pourpairwise
, elle remplace simplement l'élément unique «consommer» sur le deuxièmetee
itérateur avec des consommations progressivement croissantes sur lesn - 1
itérateurs. Utiliserconsume
au lieu d'encapsuler chaque itérateur dansislice
est légèrement plus rapide (pour des itérables suffisamment volumineux) puisque vous ne payez lesislice
frais généraux d'encapsulation que pendant laconsume
phase, pas pendant le processus d'extraction de chaque valeur fenêtrée (il est donc limité parn
, pas par le nombre d'éléments dansiterable
).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 utilisant
ipython
%timeit
magie.kindall est la
deque
solution , ajustée pour la performance / l'exactitude en utilisantislice
au 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 passagemaxlen
de ladeque
position au lieu de par mot-clé (fait une différence surprenante pour les entrées plus petites):Identique à la solution kindall adaptée précédente, mais avec chacune des
yield win
modifications apportées, leyield 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 ajoutanttuple=tuple
à la définition de la fonction pour déplacer l'utilisation detuple
de l'B
entréeLEGB
vers laL
:consume
solution à base illustrée ci-dessus:Identique à
consume
, mais dans leelse
cas deconsume
pour éviter l'appel de fonction et den is None
tester 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:(Note latérale: une variante
pairwise
qui utilisetee
l'argument par défaut de 2 à plusieurs reprises pour créer destee
objets 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-inlineconsume
et plus lent que l'inlineconsume
sur 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
consume
gagne ); 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 enveloppantmap(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'
consume
emportent sur toutes les tailles d'entrée sauf les plus petites (la non-ligneconsume
é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
yield
stuple
de j'étais:Supprimez la mise
tuple
en cache de dans la ligne de définition de fonction et l'utilisation detuple
dans chacuneyield
pour obtenir la version la plus rapide mais la moins sûre.la source
consume
est un usage général (y compris la possibilité de faire un completconsume
) et nécessite donc une importation supplémentaire et un test par utilisation pourn 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 leelse
cas deconsume
intowindow
, 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; laconsume
fonction nommée rend l'opération moins magique / auto-documentée.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.
la source
len(sequence)
appel. Cela ne fonctionnera pas s'ilsequence
s'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.la source
range(len
qu'un mauvais modèle en python?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:
cela donne
la source
Fait ceci pour une fonction moyenne mobile
la source
pourquoi pas
Il est documenté dans la doc Python . Vous pouvez facilement l'étendre à une fenêtre plus large.
la source
Plusieurs itérateurs!
next(it)
déclencheStopIteration
lorsque 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'
seq
implémenter__iter__
ou__getitem__
et ne repose pas suritertools
ou encollections
plus de la solution de @ dansalmo :)la source
Faisons paresseux!
la source
"" "
la source
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.
la source
la source
Que diriez-vous d'utiliser ce qui suit:
Production:
la source
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.
Astuce: vous pouvez vérifier la
.shape
de la fenêtre lors de l'itération du générateur pour éliminer ceux qui ne répondent pas à vos besoinsÀ votre santé
la source
Modification de la réponse de DiPaolo pour permettre un remplissage arbitraire et une taille de pas variable
la source
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
la source
Essayer ma part, simple, une doublure, façon pythonique en utilisant islice. Mais, peut ne pas être d'une efficacité optimale.
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.
la source
Fonction optimisée pour les données de fenêtre glissante dans le Deep Learning
la source