Подтвердить что ты не робот

Иллитор с подвижным или скользящим окном?

Мне нужно окно для катания (ака скользящее окно), которое можно повторить по последовательности/итератору/генератору. Итерацию по умолчанию 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]
"""
4b9b3361

Ответ 1

Там один в старой версии документов Python с примерами itertools:

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 для большего эффекта, который я себе представляю.

Ответ 2

Это кажется специально для 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 (итерация от одного элемента к следующему выполняется быстро, поэтому этот цикл пробежал на 20% быстрее, чем следующий самый быстрый метод, pillmuncher's). Когда я изменил его для индивидуального поиска и добавления элементов в окно из десяти, таблицы были развернуты, а метод tee был на 20% быстрее. Я смог восстановить некоторую скорость, используя отрицательные индексы для последних пяти терминов в добавлении, но tee был все еще немного быстрее. В целом, я бы оценил, что для большинства целей достаточно быстро, и если вам нужно немного больше производительности, профиля и выберите тот, который лучше всего работает.

Ответ 3

Мне нравится 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]

Ответ 4

Здесь обобщение, которое добавляет поддержку параметров 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 см. Обработка большого файла .txt в python эффективно.

Ответ 5

Просто быстрый вклад.

Так как текущие документы python не имеют "окна" в примерах itertool (т.е. внизу 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)

В принципе, мы создаем серию изрезанных итераторов, каждая из которых имеет начальную точку на одно место вперед. Затем мы запишем их вместе. Обратите внимание: эта функция возвращает генератор (он не является непосредственно самим генератором).

Как и предыдущие версии append-element и advancing-iterator, производительность (т.е. лучше) варьируется в зависимости от размера списка и размера окна. Мне нравится этот, потому что это двухстрочный (это может быть однострочный, но я предпочитаю наименования).

Оказалось, что приведенный выше код неверен. Он работает, если параметр, переданный в iterable, является последовательностью, но не является итератором. Если это итератор, тот же самый итератор является общим (но не tee'd) среди вызовов islice, и это сильно нарушает ситуацию.

Вот некоторый фиксированный код:

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)

Ответ 6

Просто чтобы показать, как вы можете комбинировать рецепты itertools, я расширяю рецепт 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, он просто заменяет отдельный элемент "потребление" на втором итераторе tee -ed на постепенно увеличивающиеся потребления на итераторах n - 1. Использование consume вместо переноса каждого итератора в islice незначительно быстрее (для достаточно больших итераций), поскольку вы оплачиваете только издержки переноса islice во время фазы consume, а не во время извлечения каждого окна -ed значение (поэтому оно ограничено n, а не количеством элементов в iterable).

С точки зрения производительности, по сравнению с некоторыми другими решениями, это довольно хорошо (и лучше, чем любое другое решение, которое я тестировал по мере масштабирования). Протестировано на Python 3.5.0, Linux x86-64, с использованием магии ipython %timeit.

добро пожаловать в решение deque, настроенное на производительность/правильность, используя islice вместо выражения собственного генератора и проверив полученную длину, чтобы оно не дало результатов, когда итерация короче окна, а также при прохождении maxlen deque позиционно, а не по ключевому слову (имеет удивительную разницу для небольших входных данных):

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

То же, что и в предыдущем адаптированном решении kindall, но с каждым 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 -based Решение, показанное выше:

>>> %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, чтобы избежать вызова функции, и тест 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, который использует tee с аргументом по умолчанию, равным 2, для создания вложенных объектов tee, поэтому любой данный итератор продвигается только один раз, а не потребляет независимо растущее число раз, аналогично , ответ MrDrFenner аналогичен не встроенному consume и медленнее, чем встроенный consume во всех тестах, поэтому я опускаю его для краткости).

Как вы можете видеть, , если вы не заботитесь о том, что вызывающей стороне необходимо сохранять результаты, моя оптимизированная версия решения kindall выигрывает большую часть времени, за исключением "случая большого итерируемого небольшого размера окна" (где встроенный consume побеждает); он быстро ухудшается при увеличении итеративного размера, но не уменьшается вообще при увеличении размера окна (любое другое решение ухудшается медленнее при увеличении итеративного размера, но также ухудшается при увеличении размера окна). Его даже можно адаптировать к случаю "нужных кортежей", обернув его в map(tuple, ...), который работает немного медленнее, чем добавление кортежей в функцию, но он тривиален (занимает на 1-5% больше) и позволяет сохранять гибкость работать быстрее, когда вы можете терпеть многократное возвращение одного и того же значения.

Если вам нужна защита от возврата возвратов, встроенный consume выигрывает на всех входных размерах, кроме самых маленьких (при этом не встроенный consume немного медленнее, но масштабируется аналогично). deque & решение на основе кортежей выигрывает только для наименьших входных данных из-за меньших затрат на настройку и небольшого усиления; он сильно ухудшается, так как повторяемое становится длиннее.

Для справки, адаптированная версия решения kindall, которую yield 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 в каждом yield, чтобы получить более быструю, но менее безопасную версию.

Ответ 7

Я использую следующий код как простое скользящее окно, которое использует генераторы для резкого повышения удобочитаемости. Его скорость до сих пор была достаточной для использования в анализе последовательности биоинформатики в моем опыте.

Я включаю его здесь, потому что пока не использовал этот метод. Опять же, я не претендую на его сравнительную производительность.

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]

Ответ 8

Существует библиотека, которая делает именно то, что вам нужно:

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

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

Ответ 10

слегка измененная версия окна 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]

Ответ 11

Несколько итераторов!

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 или collections, кроме решения @dansalmo:)

Ответ 12

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

Сделано для скользящей средней функции

Ответ 13

почему не

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

Документировано в Python doc. Вы можете легко расширить его до более широкого окна.

Ответ 15

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

"""

Ответ 16

Пусть ленится!

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

Ответ 17

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

Ответ 18

Как использовать следующее:

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

Ответ 19

Это старый вопрос, но для тех, кто по-прежнему заинтересован, существует большая реализация слайдера окна с использованием генераторов в этой странице (автор 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 окна при повторном запуске генератора, чтобы отбросить те, которые не соответствуют вашим требованиям.

Приветствия

Ответ 20

Измененный ответ DiPaolo, позволяющий произвольно заполнять и изменять размер шага

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

Ответ 21

здесь один лайнер. Я рассчитал время, и оно сравнимо с показателями топового ответа и постепенно улучшается с увеличением seq от 20% медленнее с len (seq) = 20 и на 7% медленнее с len (seq) = 10000

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