我有两个时间序列,我怀疑他们之间有时间转换,我想估计这个时间变化。
此问题之前已被问过: Find phase difference between two (inharmonic) waves和find time shift between two similar waveforms但在我的情况下,时移小于数据的分辨率。例如,数据以小时分辨率提供,时移只有几分钟(见图)。
原因在于,用于测量其中一个系列的数据记录器的时间间隔几分钟。
那里可以估计这种偏移的任何算法,最好不使用插值?
答案 0 :(得分:4)
这是一个非常有趣的问题。这是使用傅里叶变换的部分解决方案的尝试。这依赖于数据是适度周期性的。我不确定它是否适用于您的数据(端点处的衍生物似乎不匹配)。
import numpy as np
X = np.linspace(0,2*np.pi,30) #some X values
def yvals(x):
return np.sin(x)+np.sin(2*x)+np.sin(3*x)
Y1 = yvals(X)
Y2 = yvals(X-0.1) #shifted y values
#fourier transform both series
FT1 = np.fft.fft(Y1)
FT2 = np.fft.fft(Y2)
#You can show that analyically, a phase shift in the coefficients leads to a
#multiplicative factor of `exp(-1.j * N * T_d)`
#can't take the 0'th element because that's a division by 0. Analytically,
#the division by 0 is OK by L'hopital's<sp?> rule, but computers don't know calculus :)
print np.log(FT2[1:]/FT1[1:])/(-1.j*np.arange(1,len(X)))
快速检查打印输出显示频率最高 功率(N = 1,N = 2)给出合理的估计,如果你看一下,N = 3也可以 绝对值(np.absolute),虽然我无法解释为什么会这样。
也许更熟悉数学的人可以从这里拿出来给出更好的答案......
答案 1 :(得分:2)
您提供的其中一个链接有正确的想法(事实上我在这里做了几乎相同的事情)
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import correlate
a,b, N = 0, 10, 1000 #Boundaries, datapoints
shift = -3 #Shift, note 3/10 of L = b-a
x = np.linspace(a,b,N)
x1 = 1*x + shift
time = np.arange(1-N,N) #Theoritical definition, time is centered at 0
y1 = sum([np.sin(2*np.pi*i*x/b) for i in range(1,5)])
y2 = sum([np.sin(2*np.pi*i*x1/b) for i in range(1,5)])
#Really only helps with large irregular data, try it
# y1 -= y1.mean()
# y2 -= y2.mean()
# y1 /= y1.std()
# y2 /= y2.std()
cross_correlation = correlate(y1,y2)
shift_calculated = time[cross_correlation.argmax()] *1.0* b/N
y3 = sum([np.sin(2*np.pi*i*(x1-shift_calculated)/b) for i in range(1,5)])
print "Preset shift: ", shift, "\nCalculated shift: ", shift_calculated
plt.plot(x,y1)
plt.plot(x,y2)
plt.plot(x,y3)
plt.legend(("Regular", "Shifted", "Recovered"))
plt.savefig("SO_timeshift.png")
plt.show()
这有以下输出:
Preset shift: -3
Calculated shift: -2.99
可能需要检查
请注意,相关性的argmax()显示了对齐的位置,它必须按b-a = 10-0 = 10
的长度和N来缩放以获得实际值。
检查关联来源Source,来自sigtools的导入函数的行为并不完全明显。对于大型数据集,循环相关(通过快速傅里叶变换)比直接方法快得多。我怀疑这是在sigtools中实现的,但我无法确定。在python2.7文件夹中搜索文件只返回已编译的C pyd文件。
答案 2 :(得分:2)
这是一个非常有趣的问题。最初,我打算建议一个类似于user948652的基于互相关的解决方案。但是,从您的问题描述中,该解决方案存在两个问题:
由于这两个问题,我认为直接应用互相关解决方案可能实际上会增加您的时移,特别是在预测值和测量值彼此之间的相关性非常低的日子。
在上面的评论中,我问你是否在两个时间序列中都发生了任何事件,而你说你没有。但是,根据您的域名,我认为您实际上有两个:
即使信号的其余部分相关性很差,日出和日落也应该有些相关,因为它们将单调增加/减少到夜间基线。因此,基于这两个事件,这是一个潜在的解决方案,既可以最小化所需的插值,又不依赖于不良相关信号的互相关。
<强> 1。查找近似日出/日落
这应该很容易,只需要取出高于夜间平线的第一个和最后一个数据点,然后标记那些近似的日出和日落。然后,我将集中讨论这些数据,以及任何一方的立即点,即:
width=1
sunrise_index = get_sunrise()
sunset_index = get_sunset()
# set the data to zero, except for the sunrise/sunset events.
bitmap = zeros(data.shape)
bitmap[sunrise_index - width : sunrise_index + width] = 1
bitmap[sunset_index - width : sunset_index + width] = 1
sunrise_sunset = data * bitmap
根据您在分析中需要多少严谨性,有多种方法可以实现get_sunrise()
和get_sunset()
。我会使用numpy.diff
,将其阈值设置为特定值,并将第一个和最后一个点放在该值之上。您还可以从大量文件中读取夜间数据,计算平均值和平均值。标准偏差,并查找超过夜间数据0.5 * st_dev
的第一个和最后一个数据点。您还可以进行某种基于群集的模板匹配,特别是如果不同的一天类别(即,晴天与部分多云与非常多云)具有高度刻板的日出/日落事件。
<强> 2。重新采样数据
我认为没有任何插值可以解决这个问题。我会使用重新采样数据来获得比移位更高的采样率。如果班次是以分钟为单位,则上采样为1分钟或30秒。
num_samples = new_sample_rate * sunrise_sunset.shape[0]
sunrise_sunset = scipy.signal.resample(sunrise_sunset, num_samples)
或者,我们可以使用三次样条来插值数据(参见here)。
第3。高斯卷积
由于存在一些插值,我们不知道实际的日出和日落是如何精确预测的。因此,我们可以用高斯卷积信号来表示这种不确定性。
gaussian_window = scipy.signal.gaussian(M, std)
sunrise_sunset_g = scipy.signal.convolve(sunrise_sunset, gaussian_window)
<强> 4。互相关强>
使用user948652的答案中的互相关方法来获得时移。
这种方法中存在许多未解决的问题,需要对数据进行检查和实验,以便更具体地确定,例如识别日出/日落的最佳方法,高斯窗应该有多宽等等。但这就是我开始攻击这个问题的方法。 祝你好运!
答案 3 :(得分:1)
确实,有趣的问题,但还没有令人满意的答案。让我们试着改变它......
你说你不想使用插值,但是,正如我从你的评论中所理解的那样,你真正的意思是你想避免上采样到更高的分辨率。一个基本的解决方案是使用最小二乘拟合线性插值函数,但没有上采样到更高的分辨率:
import numpy as np
from scipy.interpolate import interp1d
from scipy.optimize import leastsq
def yvals(x):
return np.sin(x)+np.sin(2*x)+np.sin(3*x)
dx = .1
X = np.arange(0,2*np.pi,dx)
Y = yvals(X)
unknown_shift = np.random.random() * dx
Y_shifted = yvals(X + unknown_shift)
def err_func(p):
return interp1d(X,Y)(X[1:-1]+p[0]) - Y_shifted[1:-1]
p0 = [0,] # Inital guess of no shift
found_shift = leastsq(err_func,p0)[0][0]
print "Unknown shift: ", unknown_shift
print "Found shift: ", found_shift
示例运行提供了非常准确的解决方案:
Unknown shift: 0.0695701123582
Found shift: 0.0696105501967
如果在移位的Y中包含噪声:
Y_shifted += .1*np.random.normal(size=X.shape)
结果不太精确:
Unknown shift: 0.0695701123582
Found shift: 0.0746643381744
当有更多数据可用时,存在噪声时的准确度会提高,例如:用:
X = np.arange(0,200*np.pi,dx)
典型的结果是:
Unknown shift: 0.0695701123582
Found shift: 0.0698527939193
答案 4 :(得分:0)
我已成功使用(在awgn通道中)匹配滤波器方法,它在索引n处给出峰值能量m [n];然后拟合二次多项式f(n)到m [n-1],m [n],m [n + 1]并通过设置f'(n)== 0找到最小值。
响应不一定是绝对线性的,特别是如果信号的自相关在m [n-1],m [n + 1]处没有消失。
答案 5 :(得分:0)
对于给定的约束,即解的相移比采样方法少一些,简单的下坡单纯形算法效果很好。我已经修改了@mgilson的示例问题以显示如何执行此操作。请注意,此解决方案非常强大,因为它可以处理噪声。
错误功能:可能有更优化的事情需要优化,但效果非常好:
np.sqrt((X1-X2+delta_x)**2+(Y1-Y2)**2).sum()
也就是说,通过仅调整x轴(相位)来最小化两条曲线之间的欧几里德距离。
import numpy as np
def yvals(x):
return np.sin(x)+np.sin(2*x)+np.sin(3*x)
dx = .1
unknown_shift = .03 * np.random.random() * dx
X1 = np.arange(0,2*np.pi,dx) #some X values
X2 = X1 + unknown_shift
Y1 = yvals(X1)
Y2 = yvals(X2) # shifted Y
Y2 += .1*np.random.normal(size=X1.shape) # now with noise
def err_func(p):
return np.sqrt((X1-X2+p[0])**2+(Y1-Y2)**2).sum()
from scipy.optimize import fmin
p0 = [0,] # Inital guess of no shift
found_shift = fmin(err_func, p0)[0]
print "Unknown shift: ", unknown_shift
print "Found shift: ", found_shift
print "Percent error: ", abs((unknown_shift-found_shift)/unknown_shift)
示例运行给出:
Optimization terminated successfully.
Current function value: 4.804268
Iterations: 6
Function evaluations: 12
Unknown shift: 0.00134765446268
Found shift: 0.001375
Percent error: -0.0202912082305