Genel bir zaman serisinin periyod tespiti


53

Bu gönderi, zaman serilerinde aykırı değer tespiti için genel bir yöntemle ilgili başka bir gönderinin devamıdır . Temel olarak, bu noktada, çok fazla gürültüden etkilenen jenerik bir zaman serisinin periyodikliğini / mevsimselliğini keşfetmek için güçlü bir yolla ilgileniyorum. Bir geliştirici bakış açısından, basit bir arayüz istiyorum:

unsigned int discover_period(vector<double> v);

vÖrnekleri içeren dizi nerede ve dönüş değeri sinyalin süresidir. Asıl nokta, yine, analiz edilen sinyalle ilgili herhangi bir varsayımda bulunamamamdır. Sinyal otokorelasyonuna dayanan bir yaklaşım denedim (bir korelogramın doruklarını saptadı), ancak istediğim kadar sağlam değil.


1
Xts :: periodicity denediniz mi?
Fabrício

Yanıtlar:


49

Periyodikliğin ne olduğu hakkında gerçekten bir fikriniz yoksa, muhtemelen en iyi yaklaşım spektral yoğunluğun maksimumuna karşılık gelen frekansı bulmaktır. Bununla birlikte, düşük frekanslardaki spektrum trendden etkileneceğinden, önce seriyi bozmanız gerekir. Aşağıdaki R işlevi çoğu seri için işi yapmalıdır. Mükemmel olmaktan uzak, ama birkaç düzine örnekte denedim ve iyi görünüyor. Güçlü periyodikliği olmayan veriler için 1, aksi takdirde dönem uzunluğu için 1 değerini döndürür.

Güncelleme: İşlevin 2. sürümü. Bu çok daha hızlı ve daha sağlam görünüyor.

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

Teşekkür ederim. Yine, bu yaklaşımı en kısa zamanda deneyeceğim ve sonuçları kesin olarak buraya yazacağım.
gianluca

2
Fikriniz oldukça iyi, ama benim durumumda, dl.dropbox.com/u/540394/chart.png gibi gerçekten basit (ve çok gürültülü olmayan) bir zaman serisinin periyodikliğini tespit edememektedir . Benim “ampirik” yaklaşımımla (otokorelasyona dayanarak) yazdığım basit algoritma 1008'lik bir kesin dönem döndürüyor (her 10 dakikada bir örneklem var, bu 1008/24/6 = 7, yani haftalık bir periyodiklik anlamına geliyor). Temel problemlerim: 1) Birleşmek için çok yavaş (çok fazla tarihsel veri gerektiriyor) ve reaktif, çevrimiçi bir yaklaşıma ihtiyacım var; 2) Hafıza kullanım açısından cehennem gibi verimsiz; 3) Hiç sağlam değil;
gianluca

Teşekkür ederim. Ne yazık ki, bu hala beklediğim gibi çalışmıyor. Önceki yorumun aynı zaman serisi için, yalnızca kısmen doğru olan 166 değerini döndürür (benim açımdan, haftalık haftalık dönem daha ilginçtir). Ve çok gürültülü bir zaman serisi kullanarak, bunun gibi dl.dropbox.com/u/540394/chart2.png (bir TCP alıcı penceresi analizi), işlev 10 beklerken 1 beklerdim (bariz bir şey göremiyorum) periyodiklik). BTW Çok farklı sinyallerle uğraştığım için aradığımı bulmanın gerçekten zor olacağını biliyorum.
gianluca

166, 168 için kötü bir tahmin değildir. Verilerin haftalık olarak saatlik bir saatle gözlemlendiğini biliyorsanız, neden sıklığı hiç tahmin etmiyorsunuz?
Rob Hyndman,

5
Geliştirilmiş bir sürüm olarak öngörme paketinde olduğu gibifindfrequency
Rob Hyndman

10

Sürecin durağan olacağını düşünüyorsanız - periyodiklik / mevsimsellik zamanla değişmeyecek - o zaman Ki-kare periodogramı gibi bir şey (bakınız örneğin Sokolove ve Bushell, 1978) iyi bir seçim olabilir. Yaygın olarak yüksek miktarda gürültüye sahip olabilen, ancak çok kararlı periyodikliklere sahip olması beklenen sirkadiyen verilerin analizinde kullanılır.

Bu yaklaşım, dalga biçiminin şekli hakkında hiçbir varsayımda bulunmaz (bunun döngüden döngüye tutarlı olması dışında), ancak herhangi bir gürültünün sabit ortalama ve sinyale ilgisiz olmasını gerektirir.

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)

Son iki satır, çok fazla ilave gürültüyle bile, saf bir trigonometrik fonksiyonun periyodunu tanımlayabildiğini gösteren bir örnektir.

alphaYazıldığı gibi, çağrıdaki son argüman ( ) gereksizdir, fonksiyon basitçe bulabileceği 'en iyi' dönemi döndürür; birinci returnifadeyi yorumlamaktan kaçının ve ikinciyi, seviye olarak anlamlı olan tüm dönemlerin bir listesini geri getirmesini sağlayın alpha.

Bu işlev, tanımlanabilir dönemler koyduğunuzdan emin olmak için herhangi bir akıl sağlığı denetimi yapmaz ve kesirli dönemlerle de çalışmaz ya da karar verirseniz yerleşik herhangi bir çoklu karşılaştırma kontrolü yoktur. Birden fazla döneme bakın. Ancak bunun dışında oldukça sağlam olmalıdır.


İlginç görünüyor ama çıktısını anlamıyorum, bana dönemin nereden başladığını ve 1'in çoğu değerini söylemiyor.
Herman Toothrot

3

Ne istediğinizi daha net bir şekilde tanımlamak isteyebilirsiniz (burada değilse, kendiniz için). Aradığınız şey gürültülü verilerinizde bulunan en istatistiksel olarak önemli durağan dönem ise, alınması gereken iki yol vardır:

1) sağlam bir otokorelasyon tahminini hesaplayın ve maksimum katsayıyı alın
2) sağlam bir güç spektral yoğunluğu tahminini hesaplayın ve maksimum spektrumun değerini alın

2 numaralı problem, gürültülü zaman serileri için düşük frekanslarda büyük miktarda güç elde etmenizi ve böylece ayırt etmeyi zorlaştırmanızı sağlar. Bu sorunu çözmek için bazı teknikler var (örn. Önceden beyazlatmak, sonra PSD'yi tahmin etmek), ancak verilerinizdeki gerçek süre yeterince uzunsa, otomatik algılama etkili olacaktır.

Muhtemelen en iyi bahis, Maronna, Martin ve Yohai'nin Sağlam İstatistikler - Teori ve Yöntemler bölümünde 8.6, 8.7 . Google’ı "sağlam durbin-levinson" olarak aramak da bazı sonuçlar verecektir.

Sadece basit bir cevap arıyorsanız, var olduğundan emin değilim. Zaman serilerinde dönem tespiti karmaşık olabilir ve sihir yapabilen otomatik bir rutin istemek çok fazla olabilir.


Değerli bilgileriniz için teşekkür ederim, kesinlikle bu kitaba bakacağım.
gianluca

3

Verilerinizin anlık frekansını ölçmek için DSP teorisindeki Hilbert Dönüşümünü kullanabilirsiniz. Http://ta-lib.org/ sitesi , finansal verinin baskın döngü dönemini ölçmek için açık kaynak koduna sahiptir; ilgili işlev HT_DCPERIOD olarak adlandırılır; Bunu kullanabilir veya kodu amaçlarınıza uyarlayabilirsiniz.


3

Farklı bir yaklaşım ampirik mod ayrıştırması olabilir. R paketi, yöntemin mucidi tarafından geliştirilen EMD olarak adlandırılır :

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

Bu yöntem iyi bir sebepten dolayı 'Ampirical' olarak markalandı ve İçsel Mod Fonksiyonlarının (bireysel katkı bileşenleri) karışması riski var. Öte yandan, yöntem çok sezgiseldir ve döngüselliğin hızlı bir şekilde görsel olarak incelenmesi için yardımcı olabilir.


0

Rob Hyndman'ın yukarıdaki gönderisine atıfta https://stats.stackexchange.com/a/1214/70282

Find.freq işlevi mükemmel çalışır. Kullanıyorum günlük veri setinde, 7 olması sıklığı doğru çalıştı.

Sadece hafta günlerinde denediğimde, sıklık 23.4'tür, ki bu, bir aydaki ortalama çalışma günü sayısı olan 21.42857 = 29.6 * 5 / 7'ye oldukça yakındır. (Ya da tam tersi 23 * 7/5 32'dir.)

Günlük verilerime baktığımda, ilk periyodu alma, bunun ortalamasını alma ve bir sonraki periyodu bulma vs. ile ilgili deneyler yaptım. Aşağıya bakınız:

find.freq.all = fonksiyonu (x) {  
  f = find.freq (x);
  frekanslardan = C (f) '  
  ise (f> 1) {
    = 1 başlar; #also try start = f;
    X = period.apply (x, devamı (başlangıç, uzunluk (x), f), ortalama); 
    f = find.freq (x);
    frekanslardan = C (frekanslardan, f);
  }
  if (uzunluk (sıklık)) == 1) {return (sıklık); }
  (i 2'de: uzunluk (fraks)) {
    frekanslardan [i] = frekanslardan [i] * frekanslardan [I-1] ';
  }
  frekanslardan [1: (uzunluk (frekanslardan) -1)];
}
find.freq.all (dailyts) # günlük verileri kullanma

Yukarıdaki, sekansın 1 veya f ile başlamasına bağlı olarak (7,28) veya (7,35) verir. (Yukarıdaki yoruma bakınız.)

Bu da, ilkelerin (...) mevsimsel dönemlerinin (7,28) veya (7,35) olması gerektiği anlamına gelir.

Mantık, algoritma parametrelerinin duyarlılığı verilen başlangıç ​​koşullarına duyarlı görünüyor. Ortalama 28 ve 35, bir ayın ortalama uzunluğuna yakın olan 31.5'tir.

Tekerleği yeniden icat ettiğimi sanıyorum, bu algoritmanın adı nedir? R'de daha iyi bir uygulama var mı?

Daha sonra, 1 ile 7 arasındaki tüm başlangıçları denemek için yukarıdaki kodu koştum ve ikinci dönem için 35,35,28,28,28,28,28 elde ettim. Ortalama, bir aydaki ortalama gün sayısı olan 30’a kadar çıkar. İlginç...

Herhangi bir düşünce veya yorumunuz var mı?


0

Ayrıca hangi mevsimsel farkın en iyi durağanlığa ulaştığını bulmak için Ljung-Box testi de kullanılabilir. Farklı bir konu üzerinde çalışıyordum ve bunu aynı amaçlar için kullandım. Aylık veriler için 3 ila 24 gibi farklı süreler deneyin. Ve her birini Ljung-Box ile test edin ve Chi-Square sonuçlarını saklayın. Ve en düşük ki-kare değerine sahip dönemi seçin.

İşte bunu yapmak için basit bir kod.

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.