一般的な時系列の期間検出


53

この投稿は時系列の外れ値検出のための一般的な方法に関連する別の投稿の続きです。基本的に、この時点で、多くのノイズの影響を受ける一般的な時系列の周期性/季節性を発見するための堅牢な方法に興味があります。開発者の観点から、次のようなシンプルなインターフェースが欲しいです。

unsigned int discover_period(vector<double> v);

vサンプルを含む配列はどこにあり、戻り値は信号の周期です。主なポイントは、やはり、分析された信号に関して仮定を立てることができないということです。信号の自己相関に基づいたアプローチ(コレログラムのピークの検出)を既に試しましたが、希望どおりに堅牢ではありません。


1
xts :: periodicityを試しましたか?
ファブリシオ

回答:


49

周期性が本当にわからない場合、おそらく最良のアプローチは、スペクトル密度の最大値に対応する周波数を見つけることです。ただし、低周波数のスペクトルはトレンドの影響を受けるため、最初に系列のトレンドを減らす必要があります。次のR関数は、ほとんどのシリーズで機能します。完璧とはほど遠いですが、数十の例でテストしましたが、うまくいくようです。強い周期性を持たないデータの場合は1を返し、そうでない場合は期間の長さを返します。

更新:機能のバージョン2。これははるかに高速で、より堅牢なようです。

find.freq <- function(x)
{
    n <- length(x)
    spec <- spec.ar(c(x),plot=FALSE)
    if(max(spec$spec)>10) # Arbitrary threshold chosen by trial and error.
    {
        period <- round(1/spec$freq[which.max(spec$spec)])
        if(period==Inf) # Find next local maximum
        {
            j <- which(diff(spec$spec)>0)
            if(length(j)>0)
            {
                nextmax <- j[1] + which.max(spec$spec[j[1]:500])
                period <- round(1/spec$freq[nextmax])
            }
            else
                period <- 1
        }
    }
    else
        period <- 1
    return(period)
}

ありがとうございました。繰り返しますが、私はできるだけ早くこのアプローチを試み、最終結果をここに書きます。
ジャンルカ

2
あなたのアイデアは非常に良いですが、私の場合、dl.dropbox.com/u/540394/chart.pngのような本当に単純な(それほど騒々しい)時系列の周期性を検出できません。私の「経験的」アプローチ(自己相関に基づく)で、私が書いた単純なアルゴリズムは1008の正確な期間を返します(10分ごとにサンプルがあるため、これは1008/24/6 = 7なので、毎週の周期性です)。私の主な問題は次のとおりです。1)収束するには遅すぎて(多くの履歴データが必要)、事後対応のオンラインアプローチが必要です。2)メモリ使用量の観点から見ると、非効率です。3)まったく堅牢ではありません。
ジャンルカ

ありがとうございました。残念ながら、これはまだ期待どおりに機能しません。前のコメントと同じ時系列で166を返しますが、これは部分的にしか正しくありません(私の観点からは、明らかな週単位の期間のほうが興味深い)。dl.dropbox.com/u/540394/chart2.png(TCP受信ウィンドウ分析)のような非常にノイズの多い時系列を使用すると、関数は10を返しますが、1を期待します(明らかなものは何も表示されません)周期性)。ところで、私はあまりにも異なる信号を扱っているため、探しているものを見つけるのは本当に難しいことを知っています。
ジャンルカ

166は168の悪い推定値ではありません。データが毎週のパターンで1時間ごとに観測されることがわかっている場合、なぜ頻度をまったく推定するのですか?
ロブハインドマン

5
改善されたバージョンは、予測パッケージにありますfindfrequency
ロブハインドマン

10

プロセスが定常的であると予想される場合-周期性/季節性は時間とともに変化しません-カイ二乗ピリオドグラムのようなもの(たとえば、Sokolove and Bushell、1978を参照)が適切な選択である可能性があります。通常、非常に大量のノイズが含まれる概日データの分析に使用されますが、非常に安定した周期性が期待されます。

このアプローチでは、波形の形状については想定していません(サイクルごとに一貫していることを除きます)が、ノイズは一定の平均値であり、信号とは無関係である必要があります。

chisq.pd <- function(x, min.period, max.period, alpha) {
N <- length(x)
variances = NULL
periods = seq(min.period, max.period)
rowlist = NULL
for(lc in periods){
    ncol = lc
    nrow = floor(N/ncol)
    rowlist = c(rowlist, nrow)
    x.trunc = x[1:(ncol*nrow)]
    x.reshape = t(array(x.trunc, c(ncol, nrow)))
    variances = c(variances, var(colMeans(x.reshape)))
}
Qp = (rowlist * periods * variances) / var(x)
df = periods - 1
pvals = 1-pchisq(Qp, df)
pass.periods = periods[pvals<alpha]
pass.pvals = pvals[pvals<alpha]
#return(cbind(pass.periods, pass.pvals))
return(cbind(periods[pvals==min(pvals)], pvals[pvals==min(pvals)]))
}

x = cos( (2*pi/37) * (1:1000))+rnorm(1000)
chisq.pd(x, 2, 72, .05)

最後の2行は単なる例であり、多くの加法性ノイズがある場合でも、純粋な三角関数の周期を識別できることを示しています。

書かれalphaているように、呼び出しの最後の引数()は不要です。関数は、見つけることができる「最良の」期間を単に返します。最初のreturnステートメントのコメントを外し、2番目のステートメントをコメントアウトして、レベルで重要なすべての期間のリストを返しますalpha

この関数は、識別可能な期間に入れていることを確認するための健全性チェックを行いません。複数の期間を見てください。しかし、それ以外は合理的に堅牢でなければなりません。


面白そうだが、私はそれが私に教えてくれない、出力を理解していないところ1の期間が開始され、最もpvalues
ハーマンToothrot

3

必要なものをより明確に定義することができます(ここではない場合は、自分自身に対して)。探しているのが、ノイズの多いデータに含まれる最も統計的に有意な定常期間である場合、基本的に2つの方法があります。

1)ロバストな自己相関推定値を計算し、最大係数を取得
2)ロバストなパワースペクトル密度推定値を計算し、スペクトルの最大値を取得

#2の問題は、ノイズの多い時系列では、低周波数で大量の電力が得られるため、区別が困難になることです。この問題を解決するための手法がいくつかあります(つまり、事前に白くしてからPSDを推定します)が、データの真の期間が十分に長い場合、自動検出は不確かになります。

マロナ、マーティン、ヨハイによるロバスト統計-理論と方法の 8.6、8.7 章に見られるような堅牢な自己相関ルーチンを実装するのがおそらく最善の策です。Googleで「堅牢なダービンレビンソン」を検索しても、いくつかの結果が得られます。

単純な答えを探しているだけなら、その答えが存在するかどうかわかりません。時系列での周期検出は複雑になる可能性があり、魔法を実行できる自動化されたルーチンを求めるのは多すぎるかもしれません。


あなたの貴重な情報をありがとう、私は確かにその本を見ていきます。
ジャンルカ

3

DSP理論からのヒルベルト変換を使用して、データの瞬時周波数を測定できます。サイトhttp://ta-lib.org/には、財務データの支配的なサイクル期間を測定するためのオープンソースコードがあります。関連する関数はHT_DCPERIODと呼ばれます。これを使用したり、目的に合わせてコードを調整したりできます。


3

別のアプローチとして、経験的モード分解があります。Rパッケージは、メソッドの発明者によって開発されたEMDと呼ばれます。

require(EMD)
ndata <- 3000  
tt2 <- seq(0, 9, length = ndata)  
xt2 <- sin(pi * tt2) + sin(2* pi * tt2) + sin(6 * pi * tt2) + 0.5 * tt2  
try <- emd(xt2, tt2, boundary = "wave")  
### Ploting the IMF's  
par(mfrow = c(try$nimf + 1, 1), mar=c(2,1,2,1))  
rangeimf <- range(try$imf)  
for(i in 1:try$nimf) {  
plot(tt2, try$imf[,i], type="l", xlab="", ylab="", ylim=rangeimf, main=paste(i, "-th IMF", sep="")); abline(h=0)  
}  
plot(tt2, try$residue, xlab="", ylab="", main="residue", type="l", axes=FALSE); box()

この方法は正当な理由で「経験的」とブランド化されており、固有モード機能(個々の添加成分)が混同されるリスクがあります。一方、この方法は非常に直感的であり、周期性をすばやく視覚的に検査するのに役立ちます。


0

上記のRob Hyndmanの投稿を参照https://stats.stackexchange.com/a/1214/70282

find.freq関数は見事に機能します。私が使用している日次データセットでは、頻度が7になるように正しく計算されました。

平日のみで試してみると、頻度は23で、これは1か月の平均就業日数である21.42857 = 29.6 * 5/7に非常に近いと述べています。(または逆に23 * 7/5は32です。)

毎日のデータを振り返って、最初の期間を取り、それによって平均化してから次の期間を見つけるなどの試行錯誤を繰り返しました。以下を参照してください。

find.freq.all = function(x){  
  f = find.freq(x);
  freqs = c(f);  
  while(f> 1){
    start = 1; #start = fも試してください。
    x = period.apply(x、seq(start、length(x)、f)、mean); 
    f = find.freq(x);
    freqs = c(freqs、f);
  }
  if(length(freqs)== 1){return(freqs); }
  for(i in 2:length(freqs)){
    freqs [i] = freqs [i] * freqs [i-1];
  }
  freqs [1:(length(freqs)-1)];
}
find.freq.all(dailyts)#毎日のデータを使用

上記は、seqが1またはfで始まるかどうかに応じて、(7,28)または(7,35)を与えます。(上記のコメントを参照してください。)

これは、msts(...)の季節期間が(7,28)または(7,35)であることを意味します。

アルゴリズムは、アルゴリズムパラメーターの感度を考慮すると、初期条件に敏感に見えます。28と35の平均は31.5で、1か月の平均の長さに近いです。

私は車輪を再発明したと思われますが、このアルゴリズムの名前は何ですか?Rのどこかにもっと良い実装がありますか?

後で、1から7までのすべての開始を試行して上記のコードを実行し、2番目の期間で35,35,28,28,28,28,28を得ました。平均は、1か月の平均日数である30になります。面白い...

考えやコメントはありますか?


0

Ljung-Boxテストを使用して、どの季節差が最高の定常性に達するかを把握することもできます。私は別のテーマに取り組んでおり、実際に同じ目的でこれを使用しました。毎月のデータに対して3〜24などのさまざまな期間を試してください。そして、それぞれをLjung-Boxでテストし、カイ二乗結果を保存します。そして、カイ二乗値が最小の期間を選択します。

これを行う簡単なコードを次に示します。

minval0 <- 5000 #assign a big number to be sure Chi values are smaller
minindex0 <- 0
periyot <- 0

for (i in 3:24) { #find optimum period by Qtests over original data

        d0D1 <- diff(a, lag=i)

        #store results
        Qtest_d0D1[[i]] <- Box.test(d0D1, lag=20, type = "Ljung-Box")

        #store Chi-Square statistics
        sira0[i] <- Qtest_d0D1[[i]][1]
}
#turn list to a data frame, then matrix
datam0 <- data.frame(matrix(unlist(sira0), nrow=length(Qtest_d0D1)-2, byrow = T))
datamtrx0 <- as.matrix(datam0[])
#get min value's index
minindex0 <- which(datamtrx0 == min(datamtrx0), arr.ind = F)
periyot <- minindex0 + 2
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.