Просто щоб показати, як можна поєднувати 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
Те саме, що і попереднє адаптоване подібне рішення, але з кожним 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на основі & tupling виграє лише за найменші входи через менші витрати на налаштування, а коефіцієнт підсилення невеликий; вона погано деградує, оскільки тривалість ітерабельності стає довшою.
Для запису, адаптованої версії рішення 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: прокат .