此功能允许估计时间序列的熵。它基于Lempel-Ziv压缩算法。对于长度为n的时间序列,熵估计为:
E =(1 / n SUM_i L_i)^ - 1 ln(n)
其中L_i是从位置i开始的最短子串的长度,其先前不从位置1到i-1出现。当n接近无穷大时,估计的熵会收敛到时间序列的实熵。
MATLAB函数中已经有一个实现: https://cn.mathworks.com/matlabcentral/fileexchange/51042-entropy-estimator-based-on-the-lempel-ziv-algorithm?s_tid=prof_contriblnk
我想在Python中实现,我这样做:
def contains(small, big):
for i in range(len(big)-len(small)+1):
if big[i:i+len(small)] == small:
return True
return False
def actual_entropy(l):
n = len(l)
sequence = [l[0]]
sum_gamma = 0
for i in range(1, n):
for j in range(i+1, n+1):
s = l[i:j]
if contains(s, sequence) != True:
sum_gamma += len(s)
sequence.append(l[i])
break
ae = 1 / (sum_gamma / n ) * math.log(n)
return ae
但是,我发现当数据量越来越大时计算速度太慢。例如,我使用23832个元素列表作为输入,消耗的时间如下:(可以找到数据here)
0-1000: 1.7068431377410889 s
1000-2000: 18.561192989349365 s
2000-3000: 84.82257103919983 s
3000-4000: 243.5819959640503 s
...
我有数千个这样的列表要计算,这么长的时间是难以忍受的。我该如何优化此功能并使其更快地工作?
答案 0 :(得分:3)
我玩了一下,在StackOverflow上尝试了另一个thread的不同方法。这就是我提出的代码:
def contains(small, big):
try:
big.tostring().index(small.tostring())//big.itemsize
return True
except ValueError:
return False
def actual_entropy(l):
n = len(l)
sum_gamma = 0
for i in range(1, n):
sequence = l[:i]
for j in range(i+1, n+1):
s = l[i:j]
if contains(s, sequence) != True:
sum_gamma += len(s)
break
ae = 1 / (sum_gamma / n) * math.log(n)
return ae
有趣的是,将numpy数组转换为字符串比直接使用字符串更快。我的机器上的代码与您提供的数据的非常粗略的基准是:
N: my code - your code
1000: 0.039s - 1.039s
2000: 0.266s - 18.490s
3000: 0.979s - 74.761s
4000: 2.891s - 285.488s
如果你并行化外循环,你可能会更快地实现这一点。