估计两个时间序列之间的小时间偏移

时间:2012-12-11 18:31:18

标签: python statistics scipy signal-processing correlation

我有两个时间序列,我怀疑他们之间有时间转换,我想估计这个时间变化。

此问题之前已被问过: Find phase difference between two (inharmonic) wavesfind time shift between two similar waveforms但在我的情况下,时移小于数据的分辨率。例如,数据以小时分辨率提供,时移只有几分钟(见图)。

原因在于,用于测量其中一个系列的数据记录器的时间间隔几分钟。

那里可以估计这种偏移的任何算法,最好不使用插值?

solar irradiation forecast and solar irradiation measurement

6 个答案:

答案 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

enter image description here

可能需要检查

  1. Scipy Correlate
  2. Time Delay Analaysis
  3. 请注意,相关性的argmax()显示了对齐的位置,它必须按b-a = 10-0 = 10的长度和N来缩放以获得实际值。

    检查关联来源Source,来自sigtools的导入函数的行为并不完全明显。对于大型数据集,循环相关(通过快速傅里叶变换)比直接方法快得多。我怀疑这是在sigtools中实现的,但我无法确定。在python2.7文件夹中搜索文件只返回已编译的C pyd文件。

答案 2 :(得分:2)

这是一个非常有趣的问题。最初,我打算建议一个类似于user948652的基于互相关的解决方案。但是,从您的问题描述中,该解决方案存在两个问题:

  1. 数据的分辨率大于时移,
  2. 在某些日子里,预测值和测量值之间的相关性非常低
  3. 由于这两个问题,我认为直接应用互相关解决方案可能实际上会增加您的时移,特别是在预测值和测量值彼此之间的相关性非常低的日子。

    在上面的评论中,我问你是否在两个时间序列中都发生了任何事件,而你说你没有。但是,根据您的域名,我认为您实际上有两个:

    1. 日出
    2. 日落
    3. 即使信号的其余部分相关性很差,日出和日落也应该有些相关,因为它们将单调增加/减少到夜间基线。因此,基于这两个事件,这是一个潜在的解决方案,既可以最小化所需的插值,又不依赖于不良相关信号的互相关。

      <强> 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