我正在组合一些我希望展示团队最长连胜纪录以及这一特殊连胜的开始和结束日期的东西。例如,如果我有以下两个列表:
streak = ["W", "W", "W", "L","W", "W", "W", "W", "W", "L"]
dates = ["2016-06-15", "2016-06-14", "2016-06-13", "2016-06-10", "2016-06-09", "2016-06-08", "2016-06-05", "2016-06-03", "2016-06-02", "2016-06-02"]
然后,如果我想获得最长的连胜,我可以做类似的事情:
from itertools import groupby
longest = sorted([list(y) for x, y in groupby(streak)], key = len)[-1]
print longest
["W", "W", "W", "W", "W"]
现在我的想法是(让我知道这是否可以做得更好)以某种方式得到这个最长连胜的开始和结束指数,所以在这种情况下:
start, end = get_indices(streak, longest) # 8, 4
print "The longest streak of {}{} was from {} to {}.".format(len(longest), longest[0], dates[start], dates[end])
"The longest streak of 5W was from 2016-06-02 to 2016-06-09.
我该怎么做?或者有更好的方法来做到这一点,例如将列表压缩在一起并用它做一些事情吗?
答案 0 :(得分:3)
考虑到您的代码,您仍然可以继续使用itertools
并使用失败者takewhile
:
from itertools import takewhile, groupby
import itertools
L = [list(y) for x, y in groupby(streak)]
l = sorted(L, key=len)[-1]
ix = len(list(itertools.chain.from_iterable(takewhile(lambda x: x!=l, L))))
print("the longest streak goes from " + dates[ix+len(l)] + " to " + dates[ix])
#the longest streak goes from 2016-06-02 to 2016-06-09
答案 1 :(得分:1)
减少临时性的替代解决方案(但请注意,除非严重的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
作为一个组时所涉及的各种开销,当我们只需要开始和结束时,我的两个替代基本上所有真实数据的解决方案运行速度较慢一个涉及随机条纹长度的测试用例,总共450K条目,在ipython3
(Linux 3.5 x86-64)上,在我的机器上花了大约35毫秒来处理优化版的Colonel'回答,我的第一个tee
使用解决方案约50毫秒,我的第二个deque
使用解决方案约77毫秒。