Альтернативные решения, которые уменьшают временные переменные (но учтите, если серьезно не RAM ограничен или генерировать неоправданно огромные полосы, создавая временные ресурсы быстрее минимальных временных альтернатив). Не надо, просто иллюстрирующую другой способ объединения средств, связанных с итератора для достижения того же результата:
from itertools import groupby, tee, zip_longest
from operator import itemgetter, sub
def longeststreak(streaks, dates):
# Create parallel iterators over the first index of each new group
s, e = tee(map(next, map(itemgetter(1), groupby(range(len(streaks)), key=streaks.__getitem__))))
# Advance end iterator so we can zip at offset to create start/end index pairs
next(e, None)
# Find greatest difference between start and end
longend, longstart = max(zip_longest(e, s, fillvalue=len(streaks)), key=lambda es: sub(*es))
# return dates for those indices (must subtract one from end since end index is exclusive)
return dates[longend-1], dates[longstart]
Или другой подход:
from collections import deque
from itertools import groupby
from operator import itemgetter, sub
def longeststreak(streaks, dates):
# Generator of grouped indices for each streak
streakgroups = map(itemgetter(1), groupby(range(len(streaks)), streaks.__getitem__))
# Get first and last index of each streak without storing intermediate indices
streakranges = ((next(iter(deque(g, 1)), start), start) for g in streakgroups for start in (next(g),))
# As before, find greatest difference and return range
longend, longstart = max(streakranges, key=lambda es: sub(*es))
# End index is inclusive in this design, so don't subtract 1
return dates[longend], dates[longstart]
В обоих случаях, если на py2, вам необходимо импортировать map
от future_builtins
, и для первых, используйте izip_longest
.
Кроме того, только для полноты картины, оптимизированная версия Colonel Beauvel's answer для минимизации выполнения байт-код (медленно в CPython) в пользу более исполнения на уровне C (быстрый в CPython):
def longeststreak(streaks, dates):
# Use map with C-level builtins to reduce bytecode use
streakgroups = list(map(list, map(itemgetter(1), groupby(streaks))))
# Use max with key instead of sorted followed by indexing at -1, to turn
# O(n log n) work into O(n) work
longeststreak = max(streakgroups, key=len)
# Replace lambda with C layer built-in comparator
ix = len(list(chain.from_iterable(takewhile(longeststreak.__ne__, streakgroups))))
# Added -1 missing in original answer; end index should be exclusive,
# so we need to subtract 1; not noticeable on sample data because sample
# data had same data at end of longest streak and beginning of next
return dates[ix+len(longeststreak)-1], dates[ix]
Для записи, спасибо к различным битам накладных расходов, связанных с попытками избежать создания list
/tuple
s, содержащих всю полосу в виде группы, когда нам нужно только начало и конец, мои два альтернативных решения работают медленнее в основном по всем данным реального мира; тестовый пример, включающий рандомизированные длины штрихов, всего 450 тыс. записей, на ipython3
(Python 3.5 x86-64 для Linux), на моей машине потребовалось около 35 мс для обработки с оптимизированной версией ответа полковника, ~ 50 мс с моим сначала, tee
, используя решение, и ~ 77 мс с моим вторым, deque
с использованием решения.
просто для того, чтобы указать, что ваши даты «инвертированы» с конца списка до начала ... Я принял это во внимание в своем ответе. –