计算重复列表值之间间隔的快速/Python方法
我想做一个列表中重复值之间所有间隔的柱状图。我写了一些有效的代码,但它在if语句中使用for循环。我经常发现,如果能够使用巧妙的切片和/或预定义的python(numpy)方法编写一个版本,那么可以得到比使用for循环快得多的python代码,但在这种情况下,我想不出任何方法来做到这一点。有人能建议一种更快或更具蟒蛇风格的方法吗计算重复列表值之间间隔的快速/Python方法,python,Python,我想做一个列表中重复值之间所有间隔的柱状图。我写了一些有效的代码,但它在if语句中使用for循环。我经常发现,如果能够使用巧妙的切片和/或预定义的python(numpy)方法编写一个版本,那么可以得到比使用for循环快得多的python代码,但在这种情况下,我想不出任何方法来做到这一点。有人能建议一种更快或更具蟒蛇风格的方法吗 # make a 'histogram'/count of all the intervals between repeated values def hist_int
# make a 'histogram'/count of all the intervals between repeated values
def hist_intervals(a):
values = sorted(set(a)) # get list of which values are in a
# setup the dict to hold the histogram
hist, last_index = {}, {}
for i in values:
hist[i] = {}
last_index[i] = -1 # some default value
# now go through the array and find intervals
for i in range(len(a)):
val = a[i]
if last_index[val] != -1: # do nothing if it's the first time
interval = i - last_index[val]
if interval in hist[val]:
hist[val][interval] += 1
else:
hist[val][interval] = 1
last_index[val] = i
return hist
# example list/array
a = [1,2,3,1,5,3,2,4,2,1,5,3,3,4]
histdict = hist_intervals(a)
print("histdict = ",histdict)
# correct answer for this example
answer = { 1: {3:1, 6:1},
2: {2:1, 5:1},
3: {1:1, 3:1, 6:1},
4: {6:1},
5: {6:1}
}
print("answer = ",answer)
样本输出:
histdict = {1: {3: 1, 6: 1}, 2: {5: 1, 2: 1}, 3: {3: 1, 6: 1, 1: 1}, 4: {6: 1}, 5: {6: 1}}
answer = {1: {3: 1, 6: 1}, 2: {2: 1, 5: 1}, 3: {1: 1, 3: 1, 6: 1}, 4: {6: 1}, 5: {6: 1}}
^注意:我不关心dict中的顺序,所以这个解决方案是可以接受的,但我希望能够在非常大的数组/列表上运行,我怀疑我当前的方法会很慢 您可以通过精心构造的。然后,您只需对输入列表进行一次扫描,这是最好的。在这里,我将结果
defaultdict
更改回常规的Dict[int,Dict[int,int]]
,但这只是为了让它打印得很好
from collections import defaultdict
def count_intervals(iterable):
# setup
last_seen = {}
hist = defaultdict(lambda: defaultdict(int))
# The actual work
for i, x in enumerate(iterable):
if x in last_seen:
hist[x][i-last_seen[x]] += 1
last_seen[x] = i
return hist
a = [1,2,3,1,5,3,2,4,2,1,5,3,3,4]
hist = count_intervals(a)
for k, v in hist.items():
print(k, dict(v))
# 1 {3: 1, 6: 1}
# 3 {3: 1, 6: 1, 1: 1}
# 2 {5: 1, 2: 1}
# 5 {6: 1}
# 4 {6: 1}
在数据结构方面有一个明显的变化。使用
计数器的defaultdict
而不是使用hist
的字典字典,这会使代码变为
from collections import defaultdict, Counter
# make a 'histogram'/count of all the intervals between repeated values
def hist_intervals(a):
values = sorted(set(a)) # get list of which values are in a
# setup the dict to hold the histogram
hist, last_index = defaultdict(Counter), {}
# now go through the array and find intervals
for i, val in enumerate(a):
if val in last_index
interval = i - last_index[val]
hist[val].update((interval,))
last_index[val] = i
return hist
如果
是用C编写的,会更快,也会更干净。我认为计数器的defaultdict在这里更有意义,但它非常相似。@OscarSmith如果你不使用
计数器的好构造函数,如果我们编辑@PatrickHaugh的解决方案,让它返回一个实际的dict,而不是仅仅打印到控制台,那么基准测试是。。。原始解决方案耗时0.712121740999919奥斯卡的解决方案耗时3.247460176999936帕特里克的解决方案耗时0.47523080800010575有趣的是,我的原始解决方案并没有那么慢,但仍然比需要的慢。@sh37211如果不转换为dicts,基准测试是什么样子?我这样问是因为在大多数情况下,defaultdict
s可以像dict一样处理。你为什么要做sorted(set(a))
?这似乎是不必要的,将soolution从O(n)
转换为O(nlog(n))
@OscarSmith谢谢你的回答,但我对你我的进行了1000000次迭代,而你的似乎慢了5倍:“原始解决方案耗时0.7265636739998627奥斯卡的解决方案耗时3.999124227998005”嗯,这很奇怪。您使用的是什么版本的python?我认为计数器在一个较新的版本中可能得到了很大的速度提升。如果我在Mac笔记本电脑上使用Python 3.6,数字是:“原始解决方案耗时0.8187828499940224奥斯卡的解决方案耗时3.2331941279699095帕特里克的解决方案耗时0.5057042780099437”,即使用“从时间导入默认计时器作为计时器”