Necesito una ventana móvil (también conocida como ventana deslizante) iterable sobre una secuencia / iterador / generador. La iteración predeterminada de Python puede considerarse un caso especial, donde la longitud de la ventana es 1. Actualmente estoy usando el siguiente código. ¿Alguien tiene un método más pitónico, menos detallado o más eficiente para hacer esto?
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()
omax()
) vale la pena tener en cuenta que existen algoritmos eficientes para calcular el nuevo valor de cada ventana en tiempo constante (independientemente del tamaño de la ventana). He reunido algunos de estos algoritmos en una biblioteca de Python: rolling .Respuestas:
Hay uno en una versión antigua de los documentos de Python con
itertools
ejemplos :El de los documentos es un poco más sucinto y se usa
itertools
con mayor efecto, imagino.fuente
for elem in it
bucle?Esto parece hecho a medida para un,
collections.deque
ya que esencialmente tiene un FIFO (agregar a un extremo, eliminar del otro). Sin embargo, incluso si usa unlist
no debería cortar dos veces; en su lugar, probablemente debería solopop(0)
de la lista yappend()
el nuevo elemento.Aquí hay una implementación optimizada basada en deque modelada después de su original:
En mis pruebas, supera fácilmente todo lo demás publicado aquí la mayor parte del tiempo, aunque la
tee
versión de Pillmuncher lo supera para iterables grandes y ventanas pequeñas. En ventanas más grandes, ladeque
fuerza se adelanta nuevamente a toda velocidad.El acceso a elementos individuales en el
deque
puede ser más rápido o más lento que con listas o tuplas. (Los elementos cerca del principio son más rápidos, o los elementos cerca del final si usa un índice negativo). Puse unsum(w)
en el cuerpo de mi bucle; esto juega con la fuerza de la deque (la iteración de un elemento al siguiente es rápida, por lo que este ciclo se ejecutó un 20% más rápido que el siguiente método más rápido, pillmuncher's). Cuando lo cambié para buscar individualmente y agregar elementos en una ventana de diez, las tablas cambiaron y eltee
método fue un 20% más rápido. Pude recuperar algo de velocidad usando índices negativos para los últimos cinco términos en la adición, perotee
aún así fue un poco más rápido. En general, estimaría que cualquiera de los dos es bastante rápido para la mayoría de los usos y si necesita un poco más de rendimiento, perfile y elija el que mejor funcione.fuente
yield win
debe seryield tuple(win)
oyield list(win)
para evitar devolver un iterador de referencias al mismodeque
objeto.pip install sliding_window
y ejecutar confrom sliding_window import window
.list(window(range(10)))
debería producir algo como [[0,1], [1,2], [2,3], ...]list(list(x) for x in window(range(10)))
o agregarlo al iterador. Para algunas aplicaciones, esto importará, para otras no, y dado que iba por la velocidad, elegí no y puse la responsabilidad en la persona que llama para copiar la ventana si es necesario.tuple()
rendimiento necesario antes, este método no tiene ninguna ventaja sobre los demás.Me gusta
tee()
:da:
fuente
timeit
pruebas rápidas , esto es mucho más lento que el de Daniel DePaolo (en una proporción de 2: 1) y no se siente mucho más "agradable".size
. Si lo aumenta (por ejemplo, si el iterable tiene una longitud de 100000 elementos, haga que el tamaño de la ventana sea 1000), puede ver un aumento.iters
es O (¡tamaño!), Y llamarnext()
muchas veces (enizip()
) probablemente consuma mucho más tiempo que copiar una tupla dos veces. Estaba usando Python 2.6.5, por cierto.iters
es O (tamaño ^ 2), verdad?Aquí está una generalización que añade soporte para
step
,fillvalue
parámetros:Produce en trozos
size
elementos a la vez,step
colocando posiciones por iteración rellenando cada trozo confillvalue
si es necesario. Ejemplo parasize=4, step=3, fillvalue='*'
:Para ver un ejemplo de caso de uso para el
step
parámetro, vea Procesar un archivo .txt grande en python de manera eficiente .fuente
Hay una biblioteca que hace exactamente lo que necesita:
fuente
step=3
en realidad debe eliminarse para que coincida con la solicitud del OP:list(more_itertools.windowed(range(6), 3))
Solo una contribución rápida.
Dado que los documentos actuales de Python no tienen "ventana" en los ejemplos de itertool (es decir, en la parte inferior de http://docs.python.org/library/itertools.html ), aquí hay un fragmento basado en el código para el agrupador que Es uno de los ejemplos dados:
Básicamente, creamos una serie de iteradores en rodajas, cada uno con un punto de partida un punto más adelante. Luego, los juntamos. Tenga en cuenta que esta función devuelve un generador (no es directamente un generador en sí).
Al igual que las versiones anteriores de elemento anexador e iterador avanzado, el rendimiento (es decir, el mejor) varía según el tamaño de la lista y el tamaño de la ventana. Me gusta este porque es de dos líneas (podría ser de una línea, pero prefiero nombrar conceptos).
Resulta que el código anterior está mal . Funciona si el parámetro pasó a iterable es una secuencia pero no si es un iterador. Si se trata de un iterador, el mismo iterador se comparte (pero no está en tee) entre las llamadas de islice y esto rompe las cosas mal.
Aquí hay un código fijo:
Además, una versión más para los libros. En lugar de copiar un iterador y luego avanzar copias muchas veces, esta versión hace copias en pares de cada iterador a medida que avanzamos la posición inicial. Por lo tanto, el iterador t proporciona tanto el iterador "completo" con un punto de partida en t como también la base para crear el iterador t + 1:
fuente
Solo para mostrar cómo puede combinar
itertools
recetas , estoy extendiendo lapairwise
receta lo más directamente posible a lawindow
receta usando laconsume
receta:La
window
receta es la misma que parapairwise
, simplemente reemplaza el elemento individual "consumo" en eltee
iterador de segunda capa con consumos progresivamente crecientes en losn - 1
iteradores. Usar enconsume
lugar de ajustar cada iteradorislice
es marginalmente más rápido (para iterables lo suficientemente grandes) ya que solo paga laislice
sobrecarga de ajuste durante laconsume
fase, no durante el proceso de extracción de cada valor editado en ventana (por lo que está limitadon
, no por el número de elementos eniterable
)En cuanto al rendimiento, en comparación con algunas otras soluciones, esto es bastante bueno (y mejor que cualquiera de las otras soluciones que probé a medida que se escala). Probado en Python 3.5.0, Linux x86-64, usando
ipython
%timeit
magia.es la
deque
solución , ajustada para el rendimiento / corrección mediante el usoislice
de una expresión generadora en casa y probando la longitud resultante para que no produzca resultados cuando el iterable es más corto que la ventana, así como pasar elmaxlen
de ladeque
posición en lugar de por palabra clave (hace una diferencia sorprendente para entradas más pequeñas):Igual que la solución adaptada adaptada anterior, pero con cada
yield win
cambio parayield tuple(win)
que los resultados de almacenamiento del generador funcionen sin que todos los resultados almacenados sean realmente una vista del resultado más reciente (todas las otras soluciones razonables son seguras en este escenario) y se agregantuple=tuple
a la definición de la función para mover el uso detuple
de laB
enLEGB
laL
:consume
basada en la solución que se muestra arriba:Igual que
consume
, pero en elelse
caso deconsume
evitar llamadas a funciones yn is None
pruebas para reducir el tiempo de ejecución, particularmente para entradas pequeñas donde la sobrecarga de configuración es una parte significativa del trabajo:(Nota al margen: una variante
pairwise
que se usatee
con el argumento predeterminado de 2 repetidamente para hacertee
objetos anidados , por lo que cualquier iterador dado solo se avanza una vez, no se consume independientemente un número creciente de veces, similar a la respuesta de MrDrFenner es similar a no en líneaconsume
y más lento que el en líneaconsume
en todas las pruebas, por lo que he omitido esos resultados por brevedad).Como puede ver, si no le importa la posibilidad de que la persona que llama necesite almacenar resultados, mi versión optimizada de la solución de kindall gana la mayor parte del tiempo, excepto en el "caso de tamaño de ventana pequeño iterativo grande" (donde en línea
consume
gana ); se degrada rápidamente a medida que aumenta el tamaño iterable, mientras que no se degrada en absoluto a medida que aumenta el tamaño de la ventana (cualquier otra solución se degrada más lentamente cuando aumenta el tamaño iterable, pero también se degrada cuando aumenta el tamaño de la ventana). Incluso se puede adaptar para el caso de "necesidad de tuplas" envolviéndolomap(tuple, ...)
, que funciona un poco más lento que poner la tupla en la función, pero es trivial (toma 1-5% más tiempo) y le permite mantener la flexibilidad de correr más rápido cuando puede tolerar que se devuelva repetidamente el mismo valor.Si necesita seguridad contra el retorno de las devoluciones almacenadas, las
consume
ganancias en línea se obtienen en todos los tamaños de entrada, excepto en los más pequeños (el no en líneaconsume
es ligeramente más lento pero se escala de manera similar). Ladeque
solución basada en tupling gana solo para las entradas más pequeñas, debido a los menores costos de configuración, y la ganancia es pequeña; se degrada mucho a medida que el iterable se alarga.Para el registro, la versión adaptada de la solución de kindall que
yield
stuple
usé fue:Suelte el almacenamiento
tuple
en caché de la línea de definición de funciones y el uso detuple
en cada unayield
para obtener la versión más rápida pero menos segura.fuente
consume
es de propósito general (incluida la capacidad de hacer un completoconsume
) y, por lo tanto, necesita una importación adicional y una prueba por uson is None
. En el código real, si y solo si hubiera determinado que el rendimiento era un problema, o si realmente necesitara un código más conciso, consideraría incluir elelse
casoconsume
enwindow
, suponiendo que no estuviera usandoconsume
para otra cosa. Pero si no se ha demostrado que el rendimiento sea un problema, mantendría las definiciones separadas; laconsume
función nombrada hace que la operación sea menos mágica / autodocumentada.Utilizo el siguiente código como una simple ventana deslizante que usa generadores para aumentar drásticamente la legibilidad. Su velocidad hasta ahora ha sido suficiente para su uso en el análisis de secuencias de bioinformática en mi experiencia.
Lo incluyo aquí porque todavía no veía este método utilizado. Una vez más, no hago afirmaciones sobre su rendimiento comparado.
fuente
len(sequence)
llamada. Esto no funcionará sisequence
es un iterador o generador. Cuando la entrada cabe en la memoria, ofrece una solución más legible que con los iteradores.fuente
range(len
es un mal patrón en Python?Una versión ligeramente modificada de la ventana de deque, para convertirla en una verdadera ventana móvil. Para que comience a llenarse con un solo elemento, luego crezca hasta su tamaño máximo de ventana y luego se encoja a medida que su borde izquierdo se acerca al final:
esto da
fuente
Hecho esto para una función promedio móvil
fuente
Por qué no
Está documentado en Python doc . Puede extenderlo fácilmente a una ventana más amplia.
fuente
Múltiples iteradores!
next(it)
surgeStopIteration
cuando finaliza la secuencia, y por alguna razón genial que está más allá de mí, la declaración de rendimiento aquí la exceptúa y la función regresa, ignorando los valores sobrantes que no forman una ventana completa.De todos modos, esta es la solución de líneas mínimas aún cuyo único requisito es
seq
implementar__iter__
o__getitem__
no y no se basa enitertools
ocollections
además de la solución de @ dansalmo :)fuente
¡Hagámoslo perezoso!
fuente
"" "
fuente
Probé algunas soluciones y encontré una que encontré como la más rápida, así que pensé en compartirla.
fuente
fuente
¿Qué tal usar lo siguiente:
Salida:
fuente
Esta es una vieja pregunta, pero para aquellos que todavía están interesados, hay una gran implementación de un control deslizante de ventana que utiliza generadores en este página (por Adrian Rosebrock).
Es una implementación para OpenCV, sin embargo, puede usarla fácilmente para cualquier otro propósito. Para los ansiosos, pegaré el código aquí, pero para entenderlo mejor, recomiendo visitar la página original.
Consejo: Puede verificar la
.shape
ventana al iterar el generador para descartar aquellos que no cumplan con sus requisitosSalud
fuente
Se modificó la respuesta de DiPaolo para permitir un relleno arbitrario y un tamaño de paso variable
fuente
Aquí hay un trazador de líneas. Lo cronometré y es comparable al rendimiento de la respuesta principal y mejora progresivamente con una secuencia mayor de 20% más lento con len (seq) = 20 y 7% más lento con len (seq) = 10000
fuente
Intentando mi parte, simple, un trazador de líneas, forma pitónica usando islice. Pero, puede no ser óptimamente eficiente.
Explicación: Cree una ventana usando islice de window_size e itere esta operación usando map over all array.
fuente
Función optimizada para datos de ventana deslizante en aprendizaje profundo
fuente