Просто чтобы показать, как вы можете комбинировать 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фазы, а не во время извлечения каждого значения, редактируемого в окне (поэтому оно ограничено n, а не количеством элементов в iterable).
С точки зрения производительности, по сравнению с некоторыми другими решениями, это довольно хорошо (и лучше, чем любое другое решение, которое я тестировал по мере масштабирования). Протестировано на Python 3.5.0, Linux x86-64, с использованием ipython %timeitмагии.
Kindall это на 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решение, показанное выше:
>>> %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, что yieldS tupleS я использовал:
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чтобы получить более быструю, но менее безопасную версию.
sum()илиmax()), стоит иметь в виду, что существуют эффективные алгоритмы для вычисления нового значения для каждого окна за постоянное время (независимо от размера окна). Я собрал некоторые из этих алгоритмов в библиотеке Python: Rolling .