Мне нужно скользящее окно (иначе скользящее окно), повторяемое по последовательности / итератору / генератору. По умолчанию итерацию 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]
"""
sum()
илиmax()
), стоит иметь в виду, что существуют эффективные алгоритмы для вычисления нового значения для каждого окна за постоянное время (независимо от размера окна). Я собрал некоторые из этих алгоритмов в библиотеке Python: Rolling .Ответы:
В старой версии документации по Python есть один
itertools
пример :Один из документов немного более краткий и использует
itertools
для большего эффекта, я думаю.источник
for elem in it
цикл?Это кажется специально для
collections.deque
вас, так как у вас по существу есть FIFO (добавить с одного конца, удалить с другого). Тем не менее, даже если вы используете,list
вы не должны разрезать дважды; вместо этого вы, вероятно, должны простоpop(0)
из списка иappend()
нового элемента.Вот оптимизированная реализация на основе deque, созданная по образцу вашего оригинала:
В моих тестах он легко превосходит все остальное, размещенное здесь, большую часть времени, хотя
tee
версия pillmuncher превосходит его для больших итераций и маленьких окон. На больших окнахdeque
тянет вперед с большой скоростью.Доступ к отдельным элементам в
deque
может быть быстрее или медленнее, чем с помощью списков или кортежей. (Элементы в начале быстрее или элементы в конце, если вы используете отрицательный индекс.) Я помещаюsum(w)
в тело моего цикла; это играет на прочность deque (итерация от одного элемента к другому выполняется быстро, поэтому этот цикл выполняется на 20% быстрее, чем следующий самый быстрый метод, pillmuncher's). Когда я изменил его, чтобы индивидуально искать и добавлять элементы в окне из десяти, таблицы поворачивались, иtee
метод был на 20% быстрее. Я смог восстановить некоторую скорость, используя отрицательные индексы для последних пяти слагаемых в добавлении, ноtee
все еще был немного быстрее. В целом, я бы оценил, что любой из них достаточно быстр для большинства применений, и если вам нужно немного больше производительности, выберите профиль и выберите тот, который работает лучше всего.источник
yield win
должно бытьyield tuple(win)
илиyield list(win)
для предотвращения возврата итератора ссылок на один и тот жеdeque
объект.pip install sliding_window
и запустить сfrom sliding_window import window
.list(window(range(10)))
должны произвести что-то вроде [[0,1], [1,2], [2,3], ...]list(list(x) for x in window(range(10)))
или добавить это к итератору. Для некоторых приложений это будет иметь значение, для других - нет, и, поскольку я шел на скорость, я не выбрал и взял на вызывающего абонента бремя, чтобы при необходимости скопировать окно.tuple()
доходности, этот метод не будет иметь никакого преимущества перед другими.Мне нравится
tee()
:дает:
источник
timeit
тестов, это намного медленнее, чем у Дэниела Депаоло (примерно в соотношении 2: 1), и не выглядит намного лучше.size
. Если вы увеличите его (например, если длина повторяемого элемента составляет 100000 элементов, размер окна будет равен 1000), вы можете увидеть увеличение.iters
равно O (размер!), Иnext()
многократный вызов (inizip()
), вероятно, занимает гораздо больше времени, чем копирование кортежа дважды. Я использовал Python 2.6.5, кстати.iters
O (размер ^ 2), верно?Вот обобщение , которое добавляет поддержку
step
,fillvalue
параметры:Это приводит к тому, что
size
элементы чанков за раз перемещаютсяstep
на каждую итерацию, дополняя каждый чанк,fillvalue
если это необходимо. Пример дляsize=4, step=3, fillvalue='*'
:Пример использования этого
step
параметра см. В разделе Эффективная обработка большого файла .txt в python .источник
Есть библиотека, которая делает именно то, что вам нужно:
источник
step=3
на самом деле должен быть удален, чтобы соответствовать запросу ОП:list(more_itertools.windowed(range(6), 3))
Просто быстрый вклад.
Поскольку текущие документы по Python не имеют «окна» в примерах itertool (т. Е. В нижней части http://docs.python.org/library/itertools.html ), вот фрагмент кода, основанный на коде для grouper, который один из приведенных примеров:
По сути, мы создаем серию нарезанных итераторов, каждый с отправной точкой на одну точку дальше. Затем мы соединяем их вместе. Обратите внимание, что эта функция возвращает генератор (это не сам генератор).
Подобно версиям appending-element и advising-iterator, приведенным выше, производительность (т. Е. Лучшая) зависит от размера списка и размера окна. Мне нравится этот, потому что это двухстрочный (он может быть однострочным, но я предпочитаю концепции именования).
Оказывается, приведенный выше код неверен . Это работает, если параметр, переданный в iterable, является последовательностью, но не если это итератор. Если это итератор, один и тот же итератор разделяется (но не tee'd) между вызовами islice, и это сильно нарушает работу.
Вот некоторый фиксированный код:
Также еще одна версия для книг. Вместо того, чтобы копировать итератор и затем многократно продвигать копии, эта версия делает попарные копии каждого итератора, когда мы перемещаем начальную позицию вперед. Таким образом, итератор t обеспечивает как «полный» итератор начальной точкой в t, так и основой для создания итератора t + 1:
источник
Просто чтобы показать, как вы можете комбинировать
itertools
рецепты , я расширяюpairwise
рецепт как можно напрямую обратно вwindow
рецепт, используяconsume
рецепт:window
Рецепт такой же , как дляpairwise
, он просто заменяет один элемент «потребить» на второмtee
-ED итератора с прогрессивно возрастающей потребляет наn - 1
итераторы. Использованиеconsume
вместо переноса каждого итератораislice
происходит немного быстрее (для достаточно больших итераций), поскольку вы платите толькоislice
издержки переноса во времяconsume
фазы, а не во время извлечения каждого значения, редактируемого в окне (поэтому оно ограниченоn
, а не количеством элементов вiterable
).С точки зрения производительности, по сравнению с некоторыми другими решениями, это довольно хорошо (и лучше, чем любое другое решение, которое я тестировал по мере масштабирования). Протестировано на Python 3.5.0, Linux x86-64, с использованием
ipython
%timeit
магии.Kindall это на
deque
решение , оптимальное для производительности / корректности, используяislice
вместо выражения генератора домашнего проката и тестирования полученной длины , так что не дают результаты , когда итерируемое короче , чем окно, а также прохождениямmaxlen
изdeque
позиционно вместо по ключевому слову (имеет удивительное значение для небольших входных данных):То же, что и предыдущее адаптированное решение kindall, но с каждым
yield win
изменением наyield tuple(win)
так, что сохранение результатов из генератора работает без того, чтобы все сохраненные результаты действительно являлись просмотром самого последнего результата (все другие разумные решения безопасны в этом сценарии), и добавлениеtuple=tuple
в определение функции для перемещения использоватьtuple
отB
вLEGB
кL
:consume
решение, показанное выше:То же, что
consume
и во встроенномelse
случае,consume
чтобы избежать вызова функции иn is None
тестирования для сокращения времени выполнения, особенно для небольших входов, где накладные расходы на установку являются значимой частью работы:(Примечание: вариант для
pairwise
этого, использующийtee
аргумент по умолчанию 2 для создания вложенныхtee
объектов несколько раз, поэтому любой данный итератор продвигается только один раз, а не потребляется независимо увеличивающееся количество раз, аналогично ответу MrDrFenner, аналогичному не встроенномуconsume
и медленнее, чем встроенныеconsume
во всех тестах, поэтому я опускаю эти результаты для краткости).Как вы можете видеть, если вы не заботитесь о том, что вызывающей стороне нужно сохранять результаты, моя оптимизированная версия решения kindall выигрывает большую часть времени, за исключением «случая большого итерируемого малого размера окна» (где встроенные
consume
выигрыши ); он быстро ухудшается при увеличении итеративного размера, но не уменьшается вообще при увеличении размера окна (любое другое решение ухудшается медленнее при увеличении повторяемого размера, но также ухудшается при увеличении размера окна). Его можно даже адаптировать к случаюmap(tuple, ...)
«нужных кортежей», заключив его в оболочку, которая работает немного медленнее, чем добавление кортежей в функцию, но она тривиальна (занимает на 1-5% больше) и позволяет сохранить гибкость работы быстрее когда вы можете терпеть неоднократно возвращая одно и то же значение.Если вам нужна защита от возврата возвратов, встроенные
consume
выигрыши будут у всех, кроме самых маленьких входных размеров (при не встроенномconsume
немного медленнее, но с одинаковым масштабированием).deque
& Кортежи выигрывает решение , основанное только для самых маленьких входов, из - за меньшие затраты установки, и коэффициент усиление мало; это ухудшается плохо, поскольку повторяемое становится длиннее.Для записи, адаптированной версии решения Kindall, что
yield
Stuple
S я использовал:Отбросьте кеширование
tuple
в строке определения функции и использованиеtuple
в каждой,yield
чтобы получить более быструю, но менее безопасную версию.источник
consume
является общим назначением (включая возможность сделать полныйconsume
) и, следовательно, нуждается в дополнительном импорте и тесте для каждого использованияn is None
. В реальном коде, если и только если бы я определил, что производительность была проблемой, или мне действительно нужен более лаконичный код, я бы рассмотрел возможность включенияelse
случаяconsume
вwindow
, предполагая, что я не используюconsume
для чего-то еще. Но если бы не было проблемы с производительностью, я бы оставил отдельные определения; названнаяconsume
функция делает операцию менее волшебной / самодокументируемой.Я использую следующий код в качестве простого скользящего окна, которое использует генераторы для значительного повышения читабельности. По моему опыту, его скорость была достаточной для использования в анализе последовательности биоинформатики.
Я включил его сюда, потому что я еще не видел этот метод. Опять же, я не претендую на его сравнительную производительность.
источник
len(sequence)
звонок. Это не будет работать, еслиsequence
итератор или генератор. Когда входные данные помещаются в память, это предлагает более читаемое решение, чем с итераторами.источник
range(len
это плохой паттерн в питоне?немного измененная версия окна deque, чтобы сделать его действительно скользящим окном. Так что он начинает заполняться только одним элементом, затем увеличивается до максимального размера окна, а затем сжимается, когда его левый край приближается к концу:
это дает
источник
Сделано это для функции скользящего среднего
источник
почему нет
Это задокументировано в Python doc . Вы можете легко расширить его до более широкого окна.
источник
Несколько итераторов!
next(it)
Возникает,StopIteration
когда последовательность завершена, и по какой-то классной причине, которая мне не подходит, оператор yield здесь исключает ее, и функция возвращает значение, игнорируя оставшиеся значения, которые не образуют полное окно.В любом случае, это решение с наименьшим количеством строк, единственное требование которого заключается в том, чтобы
seq
реализовывать__iter__
или__getitem__
не полагаться на решение @ dansalmoitertools
или,collections
кроме него, :)источник
Давайте сделаем это ленивым!
источник
«»»
источник
Я протестировал несколько решений, один из которых я нашел, и нашел тот, который я нашел, как самый быстрый, поэтому я решил поделиться им.
источник
источник
Как насчет использования следующего:
Вывод:
источник
Это старый вопрос, но для тех, кто все еще заинтересован, на этой странице есть отличная реализация ползунка с использованием генераторов (Адриан Роузброк).
Это реализация для OpenCV, однако вы можете легко использовать ее для любых других целей. Для желающих я вставлю сюда код, но для лучшего понимания рекомендую посетить исходную страницу.
Совет: Вы можете проверить
.shape
окно при итерации генератора, чтобы отменить те, которые не соответствуют вашим требованиямура
источник
Модифицированный ответ DiPaolo в разрешить произвольную заливку и переменный размер шага
источник
здесь один лайнер. Я рассчитал время, и это сравнимо с показателями топового ответа и постепенно улучшается с увеличением seq от 20% медленнее с len (seq) = 20 и на 7% медленнее с len (seq) = 10000
источник
Пытаюсь со своей стороны, простым, одним вкладышем, питоническим способом с использованием islice. Но не может быть оптимально эффективным.
Объяснение: Создайте окно с помощью islice из window_size и повторите эту операцию, используя map по всему массиву.
источник
Оптимизированная функция для скользящего окна данных в глубоком обучении
источник