堅牢なステップ関数を時系列に適合させる方法は?


7

いくつかのレベルをホバリングする、やや騒々しい時系列があります。

たとえば、次のデータ:

ここに画像の説明を入力してください

実線のデータがあり、破線の見積もりを取得したい。区分的に一定でなければなりません。

ここで試すのに適切なアルゴリズムは何ですか?

これまでのところ、私のアイデアは0度のPスプライン(ただし、ノットを配置する場所を見つける方法は?)または構造破壊モデルを中心に扱っています。回帰木は現在私が持っている最高のアイデアですが、理想的には、y = 250の2つのレベルが等しいy値にあるという事実を考慮した方法を探しています。私が正しく理解していれば、回帰ツリーはこれらの2つの区間を2つの異なるグループに分割し、それぞれの平均は異なり​​ます。

それを生成したRコードはこれです:

set.seed(20181118)
true_fct = stepfun(c(100, 200, 250), c(200, 250, 300, 250))
x = 1:400
y = true_fct(x) + rt(length(x), df=1)
plot(x, y, type="l")
lines(x, true_fct(x), lty=2, lwd=3)

2
データが実際にシミュレートされたもののように見える場合は、非常に小さなウィンドウでウィンドウ化された中央値を計算するよりも優れた方法はありません。これにより、すべてのジャンプが確実に検出されます。検出された各間隔内の応答の中央値を使用してレベルを推定します。したがって、シミュレーションの暗黙的な仮定(大きなジャンプ、区分的に一定の中央値、スチューデントのtエラー)が正確に私たちが行うべき仮定であるかどうかを示すことができますか?
whuber

1
ご意見ありがとうございます!私は2つの意見があります。(2)仮定は区分的に一定の中央値と顕著なジャンプですが、大きな外れ値が発生する可能性があるという事実以外は、エラー分布については何も知りません。
Alexander Engelhardt

問題が単純な場合、単純なノンパラメトリック手法が機能することがあります。埋め込まれた有馬構造とおそらく1つまたは2つの季節的パルスがある、より困難で現実的なデータセットをシミュレートしてください。このような問題への包括的なアプローチでは、処理中に自己回帰構造と異常を考慮して分離する必要があります。別の質問を投稿して、もう少し現実的なデータセットを含めることができます。
IrishStat

エラープロセスに対してレベル/ステップシフトが非常に大きい場合も追加する必要があります。ノンパラメトリックメソッドは有用な役割を果たす可能性があり、比率が小さくなるほど少なくなります
IrishStat

回答:


7

このようなノイズを処理するための単純で堅牢な方法は、中央値を計算することです。

短いウィンドウでのローリング中央値は、最小のジャンプを除くすべてを検出しますが、検出されたジャンプ間の間隔内の応答の中央値は、それらのレベルを確実に推定します。(この後者の推定値は、外れ値の影響を受けない強力な推定値に置き換えることができます。)

許容可能なエラー率を実現するには、このアプローチを実際のデータまたはシミュレートしたデータで調整する必要があります。たとえば、問題のシミュレーションでは、ジャンプを検出するためのしきい値を設定するために、2番目と98番目のパーセンタイルを使用するのが良いことがわかりました。他の状況では(多くのジャンプが発生する可能性がある場合など)、中央パーセンタイルがより適切に機能します。

これは、(a)3つのジャンプを赤い点で示し、(b)4つの推定レベルを水色の線で示した結果です。

図

ジャンプは、インデックス100、200、250(シミュレーションで発生する場所)で発生すると推定され、結果のレベルは199.6、249.8、300.0、250.2と推定されます。これらはすべて、実際の基になる値の0.4以内です。

この優れた動作は、シミュレーションを繰り返し実行しても持続します(set.seed最初にコマンドを削除します)。

これがRコードです。

#
# Rolling medians.
#
rollmed <- function(x, k=3) {
  n <- length(x)
  x.med <- sapply(1:(n-k+10), function(i) median(x[i + 0:(k-1)]))
  l <- floor(k/2)
  c(rep(NA, l), x.med, rep(NA, k-l))
}
y.med <- rollmed(y, k=5)
#
# Changepoint analysis.
#
dy <- diff(y.med)
fourths <- quantile(dy, c(1,49)/50, na.rm=TRUE)
thresholds <- fourths + diff(fourths)*2.5*c(-1,1)
jumps <- which(dy < thresholds[1] | dy > thresholds[2]) + 1

points(jumps, y.med[jumps], pch=21, bg="Red")
#
# Plotting.
#
limits <- c(1, jumps, length(y)+1)
y.hat <- rep(NA, length(jumps)+1)
for (i in 1:(length(jumps)+1)) {
  j0 <- limits[i]
  j1 <- limits[i+1]-1
  y.hat[i] <- median(y[j0:j1])
  lines(x[j0:j1], rep(y.hat[i], j1-j0+1), col="skyblue", lwd=2)
}

+1ですが、一部のユーザーにとってコードの「変化点分析」の部分が完全に明確でない場合があるので、そこで何が起こっているのかコメントできますか?
ティム

@ティム提案ありがとうございます。最初の段落の目的は、そのアルゴリズムを説明することです。それらの実装は重要ではないので、実装の詳細を軽視したいと思います。堅牢な外れ値スクリーニング手法を残差に適用するだけで十分です。
whuber

zoo::rollmedianコードを簡略化するために、同様の関数を検討する必要がある場合があります。
usεr11852

@usεr11852ありがとうございます。 私は承知してzooいますが、怠惰なので使用しないことを選択しました!rollmedすでに利用できる可能性のある関数への引数呼び出しを確認するよりも、書くのが速くて簡単でした。また、rollmedブラックボックスの背後にある詳細を隠すのではなく、自分がやっていることをいかに明確に説明するのが好きです。
whuber

問題ない。:)(私はあなたが知っていたと確信していましたがzoo、あなたがそれを選択または偶然に使用しなかったかどうかは
わかり

3

それでもL0ペナルティによるスムージングに関心がある場合は、次のリファレンスを参照してください。「L0ペナルティを使用したセグメント化されたスムージングによるゲノム変化の視覚化」-DOI:10.1371 / journal.pone.0038230( Whittakerのスムーザーは、P。Eilersの論文「A perfect perfecter」-DOI:10.1021 / ac034173tにあります。もちろん、あなたの目的を達成するためには、メソッドの周りに少し取り組む必要があります。

原則として、3つの成分が必要です。

  1. よりスムーズ-Whittakerをよりスムーズに使用します。また、行列拡張を使用します(Eilers and Marx、1996-「Bスプラインとペナルティによる柔軟な平滑化」、p.101を参照)。
  2. クォンタイル回帰-怠惰にはRパッケージのquantreg(rho = 0.5)を使用します:-)
  3. L0-ペナルティ-私は前述の「L0ペナルティを使用したセグメント化されたスムージングによるゲノム変化の可視化」に従います-DOI:10.1371 / journal.pone.0038230

もちろん、最適な平滑化量を選択する方法も必要です。これは、この例では大工の目で行われます。DOIの基準を使用することができます:10.1371 / journal.pone.0038230(5ページですが、私はあなたの例では試しませんでした)。

以下に小さなコードがあります。ガイドとしてコメントを残しました。

# Cross Validated example
rm(list = ls()); graphics.off(); cat("\014")

library(splines)
library(Matrix)
library(quantreg)

# The data
set.seed(20181118)
n = 400
x = 1:n
true_fct = stepfun(c(100, 200, 250), c(200, 250, 300, 250))
y = true_fct(x) + rt(length(x), df = 1)

# Prepare bases - Identity matrix (Whittaker)
# Can be changed for B-splines
B = diag(1, n, n)

# Prepare penalty - lambda parameter fix
nb = ncol(B)
D = diff(diag(1, nb, nb), diff = 1)
lambda = 1e2

# Solve standard Whittaker - for initial values
a = solve(t(B) %*% B + crossprod(D), t(B) %*% y, tol = 1e-50)    

# est. loop with L0-Diff penalty as in DOI: 10.1371/journal.pone.0038230
p = 1e-6
nit = 100
beta = 1e-5

for (it in 1:nit) {
  ao = a

  # Penalty weights
  w = (c(D %*% a) ^ 2  + beta ^ 2) ^ ((p - 2)/2)
  W = diag(c(w))

  # Matrix augmentation
  cD = lambda * sqrt(W) %*% D
  Bp = rbind(B, cD)
  yp =  c(y, 1:nrow(cD)*0)

  # Update coefficients - rq.fit from quantreg
  a = rq.fit(Bp, yp, tau = 0.5)$coef

  # Check convergence and update
  da = max(abs((a - ao)/ao))
  cat(it, da, '\n')
  if (da < 1e-6) break
}

# Fit 
v = B %*% a

# Show results
plot(x, y, pch = 16, cex = 0.5)
lines(x, y, col = 8, lwd = 0.5)
lines(x, v, col = 'blue', lwd = 2)
lines(x, true_fct(x), col = 'red', lty = 2, lwd = 2)
legend("topright", legend = c("True Signal", "Smoothed signal"), 
       col = c("red", "blue"), lty = c(2, 1))

ここに画像の説明を入力してください PS。これは、相互検証に関する私の最初の回答です。私はそれが便利で十分明確であることを願っています:-)


1

Ruey TsayのペーパーOutliers、レベルシフト、およびAR1と21の外れ値を持つ時系列差分モデルの分散変化を使用することを検討します。

ここに画像の説明を入力してください

ここに画像の説明を入力してください

差分をオフにし、レベルシフトが明確に呼び出されます。

ここに画像の説明を入力してください


1
3つの実際のジャンプに加えて18の疑似パラメータ(シミュレーションで導入された外れ値に対応)を特定する方法は、堅牢(または節約)その問題)。
whuber

これは堅牢なソリューションです。外れ値を特定して調整することに反対する理由はわかりませんが、それを行うことをサポートする研究の世界と、もちろん私たちの経験があります。これらの他の変数は外れ値です。履歴データを示すグラフと、違いを対比するためにクレンジングしたバージョンを追加しました。
トムライリー

1
ステップ関数の推定値を明確にできますか?
whuber

1
期間100(x3)、200(x2)、250(x4)に、ステップを示すフラグがあります。差分演算子を使用すると、表示が少し難しくなりますが、効果は同じです。差分のないモデルを追加しました。
トムライリー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.