2つの信号を整列/同期するにはどうすればよいですか?


21

私はいくつかの研究を行っていますが、分析段階で立ち往生しています(統計の講義にもっと注意を払うべきでした)。

私は2つの同時信号を収集しました:体積に統合された流量と胸部拡張の変化。信号を比較し、最終的に胸部拡張信号からボリュームを導き出したいと思います。しかし、最初にデータを調整/同期する必要があります。

記録が正確に同時に開始されず、胸部拡張がより長い期間キャプチャされるため、胸部拡張データセット内でボリュームデータに対応するデータを見つけ、それらがどれだけ適切に調整されているかを測定する必要があります。2つの信号がまったく同じ時間に開始しない場合、または異なるスケールと異なる解像度のデータ間でこれを実行する方法がわからない。

2つの信号の例(https://docs.google.com/spreadsheet/ccc?key=0As4oZTKp4RZ3dFRKaktYWEhZLXlFbFVKNmllbGVXNHc)を添付しました。さらに提供できるものがあればお知らせください。


私はこれを答えるほどよく知らず、これが問題に対処するかどうかは確かではありませんが、信号を同期する1つのアプローチは「登録」と呼ばれます。これは機能データ分析のサブセットです。このトピックは、Ramsey and SilvermanのFDA本で説明されています。基本的な考え方は、観測された信号が「ゆがむ」可能性があることです(たとえば、人々が噛む方法のメカニズムに興味があるが、異なる速度で噛む人々に関するデータがある場合-この場合、時間軸は「ゆがむ」)。登録は、共通の「非ワープ」スケールで基になる信号を定義しようとします。
マクロ

1
すべてのデータをすでに収集しましたか?これはパイロットの対象ですか?始めたばかりの場合は、ベルトからの信号を分割し、それをトリガーとして(または単にタイムスタンプをマークするために)フロー記録を使用することを検討します。通常、収集システムには、補助ポートまたはトリガーポートを備えたこの機能があります。Macroが示唆したように、データを使用するだけでそれを区別する方法があると確信していますが、この余分なステップを追加すると、多くの推測作業がなくなります。
jonsca

1
一定の遅延のみを推定したいと思います。:あなたはここで概説として相互相関を使用することができますstats.stackexchange.com/questions/16121/...
thias

1
dsp.SEでこの質問をして、信号の同期について考えることもできます。
ディリップサルワテ

1
@Thiasは正しいですが、最初の1つのシリーズは共通の間隔を持つようにリサンプリングする必要があるようです。
whuber

回答:


24

質問は、時系列が定期的ではあるが異なる間隔でサンプリングされるときに、ある時系列(「拡大」)が別の時系列(「ボリューム」)にどれだけ遅れているかを見つける方法を尋ねます。

この場合、図が示すように、両方のシリーズは適度に連続的な動作を示します。これは、(1)初期スムージングがほとんどまたはまったく必要ない可能性があり、(2)リサンプリングが線形または二次補間と同じくらい簡単である可能性があることを意味します。滑らかさのために、Quadraticはわずかに優れている場合があります。 リサンプリング後、スレッドに示されているように、相互相関を最大化することによって遅延が検出されます。2つのオフセットのサンプリングデータシリーズの場合、それらの間のオフセットの最適な推定値はどれですか。


を説明するために、質問で提供されたデータを使用Rして、擬似コードに使用できます。基本機能、相互相関、リサンプリングから始めましょう。

cor.cross <- function(x0, y0, i=0) {
  #
  # Sample autocorrelation at (integral) lag `i`:
  # Positive `i` compares future values of `x` to present values of `y`';
  # negative `i` compares past values of `x` to present values of `y`.
  #
  if (i < 0) {x<-y0; y<-x0; i<- -i}
  else {x<-x0; y<-y0}
  n <- length(x)
  cor(x[(i+1):n], y[1:(n-i)], use="complete.obs")
}

これは粗雑なアルゴリズムです。FFTベースの計算の方が高速です。ただし、これらのデータ(約4000の値を含む)については十分です。

resample <- function(x,t) {
  #
  # Resample time series `x`, assumed to have unit time intervals, at time `t`.
  # Uses quadratic interpolation.
  #
  n <- length(x)
  if (n < 3) stop("First argument to resample is too short; need 3 elements.")
  i <- median(c(2, floor(t+1/2), n-1)) # Clamp `i` to the range 2..n-1
  u <- t-i
  x[i-1]*u*(u-1)/2 - x[i]*(u+1)*(u-1) + x[i+1]*u*(u+1)/2
}

データをコンマ区切りのCSVファイルとしてダウンロードし、ヘッダーを削除しました。(ヘッダーが原因でRに問題が発生しましたが、私は診断を気にしませんでした。)

data <- read.table("f:/temp/a.csv", header=FALSE, sep=",", 
                    col.names=c("Sample","Time32Hz","Expansion","Time100Hz","Volume"))

NB このソリューションは、データの各シリーズが一時的な順序であり、どちらにもギャップがないことを前提としています。 これにより、値のインデックスを時間のプロキシとして使用し、一時的なサンプリング周波数でインデックスをスケーリングして時間に変換できます。

これらの機器の一方または両方が時間の経過とともに少しドリフトすることがわかりました。先に進む前に、このような傾向を削除することをお勧めします。また、最後にボリューム信号のテーパーがあるため、クリップアウトする必要があります。

n.clip <- 350      # Number of terminal volume values to eliminate
n <- length(data$Volume) - n.clip
indexes <- 1:n
v <- residuals(lm(data$Volume[indexes] ~ indexes))
expansion <- residuals(lm(data$Expansion[indexes] ~ indexes)

結果から最高の精度を得るために、頻度の少ない系列をリサンプリングします。

e.frequency <- 32  # Herz
v.frequency <- 100 # Herz
e <- sapply(1:length(v), function(t) resample(expansion, e.frequency*t/v.frequency))

相互相関を計算できるようになりました-効率のために、妥当な時間枠のみを検索します-最大値が見つかったラグを特定できます。

lag.max <- 5       # Seconds
lag.min <- -2      # Seconds (use 0 if expansion must lag volume)
time.range <- (lag.min*v.frequency):(lag.max*v.frequency)
data.cor <- sapply(time.range, function(i) cor.cross(e, v, i))
i <- time.range[which.max(data.cor)]
print(paste("Expansion lags volume by", i / v.frequency, "seconds."))

出力から、膨張によりボリュームが1.85秒遅れていることがわかります。(データの最後の3.5秒がクリップされなかった場合、出力は1.84秒になります。)

できれば視覚的に、いくつかの方法ですべてをチェックすることをお勧めします。まず、相互相関関数

plot(time.range * (1/v.frequency), data.cor, type="l", lwd=2,
     xlab="Lag (seconds)", ylab="Correlation")
points(i * (1/v.frequency), max(data.cor), col="Red", cex=2.5)

相互相関プロット

次に、2つのシリーズを時間的に登録し、同じ軸に一緒にプロットします

normalize <- function(x) {
  #
  # Normalize vector `x` to the range 0..1.
  #
  x.max <- max(x); x.min <- min(x); dx <- x.max - x.min
  if (dx==0) dx <- 1
  (x-x.min) / dx
}
times <- (1:(n-i))* (1/v.frequency)
plot(times, normalize(e)[(i+1):n], type="l", lwd=2, 
     xlab="Time of volume measurement, seconds", ylab="Normalized values (volume is red)")
lines(times, normalize(v)[1:(n-i)], col="Red", lwd=2)

登録済みプロット

それはかなりよさそうです! ただし、scatterplotを使用すると、登録品質のより良い感覚を得ることができます。進行状況を示すために、時間によって色を変えます。

colors <- hsv(1:(n-i)/(n-i+1), .8, .8)
plot(e[(i+1):n], v[1:(n-i)], col=colors, cex = 0.7,
     xlab="Expansion (lagged)", ylab="Volume")

散布図

線に沿って前後に追跡する点を探しています。それからの変化は、体積に対する膨張の時間遅れ応答の非線形性を反映しています。いくつかのバリエーションがありますが、かなり小さいです。それでも、これらの変動が時間とともにどのように変化するは、生理学的に興味深いものです。統計、特にその探索的および視覚的側面についてのすばらしいことは、有用な回答とともに良い質問アイデアを作成する傾向があることです


1
神聖な地獄、あなたは素晴らしいです。相互相関はまさに私が想像していたものです(名前があることはわかっていました)が、あなたの答え/説明はそれ以上でした。どうもありがとう!
157

今は完全な説明をする時間はありませんが、「数値レシピ」の本にはすばらしい説明が載っています。たとえば、Cの数値レシピの 13.2章「FFTを使用した相関と自己相関」を参照してください。Rのacf関数を調べることもできます。
whuber

「r」は初めてです。親切にしてください。結合プロット(最後から2番目のプロット)で使用される「正規化」関数は機能しません。この回答が投稿されてから、この関数に更新はありますか?
CmKndy

1
@CmKndy私もRこの回答を投稿したときに初めてで、その関数の定義を提供するのを忘れていました。ここでは、元のです:normalize <- function(x) { x.max <- max(x); x.min <- min(x); dx <- x.max - x.min; if (dx==0) dx <- 1; (x-x.min) / dx }
whuber

パーフェクト、@ whuberありがとう。Rが初めてのときにこのような回答を投稿できれば、私は思っていたよりもさらに新しいです;)
CmKndy
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.