web-dev-qa-db-ja.com

2つの時系列間の小さな時間シフトの推定

私は2つの時系列を持っていますが、それらの間に時間シフトがあるのではないかと思います。この時間シフトを推定したいと思います。

この質問は以前に尋ねられました: 2つの(非調和)波の間の位相差を見つける および 2つの類似した波形の間の時間シフトを見つける しかし私の場合、時間シフトはよりも小さいデータの解像度。たとえば、データは1時間ごとの解像度で利用でき、タイムシフトはわずか数分です(画像を参照)。

これの原因は、シリーズの1つを測定するために使用されるデータロガーの時間に数分のシフトがあることです。

できれば補間を使用せずに、このシフトを推定できるアルゴリズムはありますか?

solar irradiation forecast and solar irradiation measurement

23
omar

これは非常に興味深い問題です。これは、フーリエ変換を使用した部分解の試みです。これは、適度に周期的なデータに依存しています。それがあなたのデータで機能するかどうかはわかりません(エンドポイントの導関数が一致していないようです)。

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)が妥当な推定値を示し、絶対値(np.absolute)を見ると、N = 3でも問題ありません。なぜそうなるのか説明するのに途方に暮れるm。

たぶん、数学に精通している誰かが、より良い答えを与えるためにここからそれを取ることができます...

5
mgilson

あなたが提供したリンクの1つは正しい考えを持っています(実際、私はここでほとんど同じことをしています)

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. 時間遅延分析

相関のargmax()はアライメントの位置を示していることに注意してください。実際の値を取得するには、b-a = 10-0 = 10とNの長さでスケーリングする必要があります。

相関のソースをチェックする ソース sigtoolsからインポートされた関数がどのように動作するかは完全には明らかではありません。大規模なデータセットの場合、(高速フーリエ変換を介した)循環相関は単純な方法よりもはるかに高速です。これがsigtoolsに実装されているものだと思いますが、はっきりとはわかりません。 python2.7フォルダー内のファイルを検索すると、コンパイルされたCpydファイルのみが返されました。

2
arynaq

これは非常に興味深い問題です。当初、私はuser948652と同様の相互相関ベースのソリューションを提案するつもりでした。ただし、問題の説明から、その解決策には2つの問題があります。

  1. データの解像度はタイムシフトよりも大きく、
  2. ある日、予測値と測定値の相関は非常に低くなります。

これら2つの問題の結果として、相互相関ソリューションを直接適用すると、特に予測値と測定値の相関が非常に低い日に、実際にタイムシフトが増加する可能性が高いと思います。

上記のコメントで、両方の時系列で発生するイベントがあるかどうかを尋ねましたが、発生しないとのことでした。ただし、ドメインに基づいて、実際には2つあると思います。

  1. 日の出
  2. 日没

信号の残りの部分の相関が不十分な場合でも、日の出と日の入りは、夜間のベースラインから単調に増加/減少するため、ある程度相関している必要があります。したがって、これら2つのイベントに基づいて、必要な補間を最小限に抑え、相関の低い信号の相互相関に依存しない、潜在的な解決策を次に示します。

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)
_

または、3次スプラインを使用してデータを補間することもできます( ここ を参照)。

3。ガウス畳み込み

いくつかの補間があるため、実際の日の出と日の入りがどれほど正確に予測されたかはわかりません。したがって、この不確実性を表すために、信号をガウス関数で畳み込むことができます。

_gaussian_window = scipy.signal.gaussian(M, std)
sunrise_sunset_g = scipy.signal.convolve(sunrise_sunset, gaussian_window)
_

4。相互相関

User948652の回答で相互相関法を使用して、タイムシフトを取得します。

この方法には、日の出/日の入りを特定するための最良の方法、ガウスウィンドウの幅など、データを調べて実験する必要がある未回答の質問がたくさんあります。しかし、それはどのように私が問題を攻撃し始めるのか。幸運を!

2
brentlance

確かに、興味深い問題ですが、まだ満足のいく答えはありません。それを変えてみましょう...

補間を使用したくないとおっしゃっていますが、コメントからわかるように、実際には、より高い解像度へのアップサンプリングを避けたいということです。基本的な解決策は、線形補間関数を使用した最小二乗適合を利用しますが、より高い解像度にアップサンプリングすることはありません。

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
1
kadee

最適なソリューションに最適化

与えられた制約、つまり、解がサンプリング方法よりも少量だけ位相シフトされるという制約の場合、単純な下り坂シンプレックスアルゴリズムが適切に機能します。 @mgilsonのサンプル問題を変更して、これを行う方法を示しました。このソリューションは、ノイズを処理できるという点で堅牢であることに注意してください。

エラー関数:最適化するのに最適なものがあるかもしれませんが、これは驚くほどうまく機能します:

np.sqrt((X1-X2+delta_x)**2+(Y1-Y2)**2).sum()

つまり、x軸(位相)を調整するだけで、2つの曲線間のユークリッド距離を最小化します。

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
0
Hooked

私は(awgnチャネルで)整合フィルターアプローチをうまく使用しました。これは、インデックスnでピークエネルギーm [n]を与えます。次に、2次多項式f(n)をm [n-1]、m [n]、m [n + 1]に当てはめ、f '(n)=を設定して最小値を見つけます。 = 0。

特に信号の自己相関がm [n-1]、m [n + 1]で消失しない場合、応答は必ずしも完全に線形であるとは限りません。

0
Aki Suihkonen