我一直在将一些代码从Matlab转换为Python,用于分析实验室中的数据。我们有两个时间戳列表,我们想用一个来预示另一个时间戳:对于第一个列表中的每个元素,我们在第二个列表中寻找具有精确时间间隔的时间戳。如果有的话,我们将它们放在单独的列表中。
这是我使用的带有随机数据的Matlab代码的可运行示例。这可能是非常粗糙的,因为我不熟悉Matlab。在下面的 Ctrigger 是触发列表,而 Csignal 是我们要预示的信号列表。对于 Ctrigger 的每个元素,我们查看 Csignal 中是否存在位于以 offset 为中心且宽度为 gate的窗口内的元素。所选事件将放置在 Hsignal 中。
% Matlab code
Ctrigger = linspace(0, 3000000, (3000000-1)/3);
length_t = length(Ctrigger);
Bsignal = linspace(0, 3000000, (3000000-1)/10);
length_s = length(Bsignal);
noise = reshape(20*rand(length_s,1)-10,[1,length_s]);
Csignal = Bsignal + noise;
offset = 3;
gate = 1;
Hsignal=zeros(length_s,1);
marker = 1;
tic
for j=1:length_t-1
m = marker;
tstart=Ctrigger(j)+offset-gate/2;
tstop=Ctrigger(j)+offset+gate/2;
while(m <= length_s-1)
if(Csignal(m)<tstart)
marker=m;
m=m+1;
end
if(Csignal(m)>=tstart && Csignal(m)<=tstop)
Hsignal(m)=Csignal(m);
m = m+1;
end
if(Csignal(m)>tstop)
break;
end
end
end
toc
Hsignal=Hsignal(Hsignal~=0);
Hsignal = unique(Hsignal);
大约有90'000个事件被选择放置在 Hsignal 中,而Matlab大约需要0.05秒来运行它。我已经介绍了 marker 计数器,因为两个列表 Csignal 和 Ctrigger 区域已经按时排序了。 marker 设置在一个先驱窗口的开始处:当我移至下一个触发器时,我不会再次查看所有 Csignal ,而只会从该窗口的开始。为避免重复计算,我在最后删除了重复项。
如果您想对代码有所了解,下面是输入和输出的简化版本:
Ctrigger = [1, 10, 11, 20, 30, 40, 50, 60]
Csignal = [4, 11, 13, 17, 25, 34, 41, 42, 50, 57, 65]
print(Hsignal)
# [4, 11, 13, 41, 42]
现在,我已经从Matlab复制了此代码,只是对其进行了少许调整以适合python(再次,可能非常粗糙):
# Python code
Ctrigger = range(0, 3000000, 3)
length_t = len(Ctrigger)
Bsignal = range(0, 3000000, 10)
length_s = len(Bsignal)
noise = 1e-05*np.asarray(random.sample(range(-1000000,1000000), int(length_s)))
Csignal = list(np.sort(np.asarray(Bsignal) + noise))
offset = 3
gate = 1
Hsignal = list()
marker = 0
length_t = len(Ctrigger)
length_s = len(Csignal)
start = time.time()
for j in range(length_t):
m = marker
t_start = Ctrigger[j] + offset - gate/2
t_stop = Ctrigger[j] + offset + gate/2
while m < length_s:
if (Csignal[m] < t_start):
marker = m
m = m + 1
elif (Csignal[m] >= t_start and Csignal[m] <= t_stop):
Hsignal.append(Csignal[m])
m = m + 1
elif (Csignal[m] > t_stop):
break
end = time.time()
Hsignal = np.sort(np.asarray(list(set(Hsignal))))
print(end-start)
类似地,在 Hsignal 中放置了大约90'000个元素。关键问题是python大约需要1.7秒来运行它!我什至尝试了这种替代方法,它消除了一些循环(这里我仍然使用数组,因为我必须将元素添加到整个列表中):
start = time.time()
result = list()
for event in Ctrigger:
c = Csignal - event - offset
d = Csignal[abs(c) <= gate/2]
result.append(list(d))
flat = [item for sublist in result for item in sublist]
flat = np.sort(np.asarray(list(set(flat))))
end = time.time()
print(end-start)
但更糟的是,将近10分钟。
我真的不明白问题出在哪里。对于我的应用程序, Ctrigger 的长度为100e06,而 Csignal 的长度为20e06。在matlab中,相同的代码需要1.06秒,而在python中则需要10分钟以上。似乎同时删除循环并加快处理速度并非易事。
答案 0 :(得分:3)
可能会使您的算法变慢的原因是在{p>中使用np.append
Hsignal = np.append(Hsignal, Csignal[m])
您应该使用列表,而不是NumPy数组:
Ctrigger = [1, 10, 11, 20, 30, 40, 50, 60]
Csignal = [4, 11, 13, 17, 25, 34, 41, 42, 50, 57, 65]
offset = 2
gate = 2
Hsignal = []
marker = 0
for j in range(len(Ctrigger)):
m = marker
t_start = Ctrigger[j] + offset - gate/2
t_stop = Ctrigger[j] + offset + gate/2
while m < len(Csignal):
if Csignal[m] < t_start:
marker = m
m = m + 1
elif Csignal[m] <= t_stop:
Hsignal.append(Csignal[m])
m = m + 1
else:
break
Hsignal = sorted(set(Hsignal))
列表建立后,您可以将其转换为数组:
Hsignal = np.array(Hsignal)
答案 1 :(得分:3)
您已经看到Python循环非常慢。默认情况下,没有像Matlab一样的jit-Compiler可以加快循环速度。因此,您有以下可能性:
在下面的示例中,我使用Numba,因为在这种情况下使用起来非常简单。
示例
import numpy as np
import numba as nb
@nb.njit()
def main_nb(Ctrigger, Csignal, offset, gate):
Hsignal = np.zeros(Ctrigger.shape[0])
marker = 1
for j in range(Ctrigger.shape[0]):
m = marker
t_star = Ctrigger[j] + offset - gate/2
t_sto = Ctrigger[j] + offset + gate/2
while m < Csignal.shape[0]:
if (Csignal[m] < t_star):
marker = m
m = m + 1
elif (Csignal[m] >= t_star and Csignal[m] <= t_sto):
Hsignal[m] = Csignal[m]
m = m + 1
elif (Csignal[m] > t_sto):
break
return Hsignal
还请注意避免使用列表。像在Matlab中一样使用简单的数组。
时间
import time
#Use simple numpy arrays if possible, not lists
Ctrigger = np.arange(0, 3000000, 3)
length_t = Ctrigger.shape[0]
Bsignal = np.arange(0, 3000000, 10)
noise = 1e-05*np.random.rand(Bsignal.shape[0])
Csignal = np.sort(np.asarray(Bsignal) + noise)
offset = 3
gate = 1
start = time.time()
Hsignal=main(Ctrigger, Csignal, offset, gate)
print("Pure Python takes:" +str(time.time()-start))
#Pure Python takes:6.049151659011841
#First call takes longer (compilation overhead)
#The same may be the case in matlab
start = time.time()
Hsignal=main_nb(Ctrigger, Csignal, offset, gate)
print("First Numba run takes:" +str(time.time()-start))
#First Numba run takes:0.16272664070129395
start = time.time()
Hsignal=main_nb(Ctrigger, Csignal, offset, gate)
print("All further Numba calls run takes:" +str(time.time()-start))
#All further Numba calls run takes:0.006016731262207031
Hsignal = np.unique(Hsignal)