我使用的是用Cython在我的Python程序中的相关计算。 我有两个音频数据集,我需要知道它们之间的时间差。 第二组是基于起效时间切割,然后穿过第一组滑动。 有两个for循环:一个滑动组和内循环,在这一点上计算相关。 此方法效果非常好,这是不够准确。
问题是,与纯Python这需要超过一分钟。 随着我用Cython代码,它需要17秒左右。 这仍然是太多了。 你有任何提示如何来加速这个代码:
import numpy as np
cimport numpy as np
cimport cython
FTYPE = np.float
ctypedef np.float_t FTYPE_t
@cython.boundscheck(False)
def delay(np.ndarray[FTYPE_t, ndim=1] f, np.ndarray[FTYPE_t, ndim=1] g):
cdef int size1 = f.shape[0]
cdef int size2 = g.shape[0]
cdef int max_correlation = 0
cdef int delay = 0
cdef int current_correlation, i, j
# Move second data set frame by frame
for i in range(0, size1 - size2):
current_correlation = 0
# Calculate correlation at that point
for j in range(size2):
current_correlation += f[<unsigned int>(i+j)] * g[j]
# Check if current correlation is highest so far
if current_correlation > max_correlation:
max_correlation = current_correlation
delay = i
return delay
编辑:
现在有scipy.signal.fftconvolve
这将是做基于FFT的卷积方法,我下面介绍的首选方法。 我会离开原来的答案解释的速度问题,但在实践中使用scipy.signal.fftconvolve
。
原来的答案:
使用FFT和的卷积定理将由为O(n ^ 2)转换问题为O(n log n)的为您提供显着的速度提升。 这对于长的数据集,像您特别有用,并且可以给1000速度增益或更多,这取决于长度。 这也是很容易做到:只要FFT两个信号,乘,逆FFT的产品。 numpy.correlate
不会在交叉相关例程使用FFT的方法,并且具有非常小的粒更好地使用。
下面是一个例子
from timeit import Timer
from numpy import *
times = arange(0, 100, .001)
xdata = 1.*sin(2*pi*1.*times) + .5*sin(2*pi*1.1*times + 1.)
ydata = .5*sin(2*pi*1.1*times)
def xcorr(x, y):
return correlate(x, y, mode='same')
def fftxcorr(x, y):
fx, fy = fft.fft(x), fft.fft(y[::-1])
fxfy = fx*fy
xy = fft.ifft(fxfy)
return xy
if __name__ == "__main__":
N = 10
t = Timer("xcorr(xdata, ydata)", "from __main__ import xcorr, xdata, ydata")
print 'xcorr', t.timeit(number=N)/N
t = Timer("fftxcorr(xdata, ydata)", "from __main__ import fftxcorr, xdata, ydata")
print 'fftxcorr', t.timeit(number=N)/N
这使每个周期的运行时间(以秒为10,000长波形)
xcorr 34.3761689901
fftxcorr 0.0768054962158
很明显的fftxcorr方法要快得多。
如果打印出来的结果,你会看到,他们是接近零时间偏移非常相似。 但是请注意,当你渐行渐远的xcorr会下降,fftxcorr不会。 这是因为它是一个有点暧昧如何处理时,波形移位不重叠的波形的部位做。 xcorr将其视为零和FFT把波形为周期性的,但如果它是可以通过零填充固定的问题。
有这样的事情,关键是要找到一种方法,分而治之。
目前,你滑动到每一个位置,在每个位置上检查每一个点-有效地为O(n ^ 2)操作。
你需要减少每个点的检查和每一个位置的东西,做更少的工作,以确定不匹配的比较。
例如,你可以有一个更短的“这是甚至接近?” 过滤器检查的前几个位置。 如果相关性高于某个阈值,然后继续前进,否则放弃,继续前进。
你可以有一个“检查每一个第8位”您通过8乘以如果太低,跳过它,继续前进。 如果这是足够高,然后检查所有值,看是否你已经找到了最大值。
问题是做所有这些乘法所需要的时间- ( f[<unsigned int>(i+j)] * g[j]
实际上,你填充所有这些产品一个很大的矩阵和采摘行与最大总和。 你不想计算“全能”的产品。 只是足够的产品,以确保你已经找到了最大总和。
与发现最大的问题是,你必须要总结的一切 ,看它是否是最大的。 如果你可以把它变成一个最小化问题,它更容易放弃计算产品,一旦中间结果超过阈值总结。
(我认为这可能会奏效。我have't试了一下。)
如果您使用max(g)-g[j]
负数的工作,你会寻找最小的,不是最大的。 你可以计算的第一位置的相关性。 凡是相加更大的价值能够立即停止 - 没有更多的乘法或增加了对偏移,转移到另一个。