일반적인 시계열의주기 감지


53

이 포스트는 시계열에서 이상치 탐지 를위한 일반적인 방법 과 관련된 다른 포스트의 연속입니다 . 기본적 으로이 시점에서 나는 많은 노이즈의 영향을받는 일반적인 시계열의 주기성 / 계절성을 발견하는 강력한 방법에 관심이 있습니다. 개발자 관점에서 다음과 같은 간단한 인터페이스를 원합니다.

unsigned int discover_period(vector<double> v);

v샘플을 포함하는 배열은 어디에 있고 리턴 값은 신호의주기입니다. 요점은 다시 한 번 분석 된 신호에 대한 가정을 할 수 없다는 것입니다. 이미 신호 자기 상관 (상호 관계의 피크를 감지)을 기반으로 접근을 시도했지만 원하는만큼 강력하지 않습니다.


1
xts :: periodicity를 사용해 보셨습니까?
Fabrício

답변:


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

감사합니다. 다시, 나는 가능한 빨리이 접근법을 시도하고 여기에 최종 결과를 쓸 것이다.
gianluca

2
당신의 아이디어는 꽤 좋지만, 제 경우에는 dl.dropbox.com/u/540394/chart.png 와 같이 매우 간단하고 시끄러운 시계열의주기를 감지하지 못합니다 . 내 "임시적"접근 방식 (자기 상관 관계에 기반 함)을 사용하여 작성한 간단한 알고리즘은 1008의 정확한주기를 반환합니다 (10 분마다 샘플을 가짐, 이는 1008/24/6 = 7을 의미하므로 매주주기 성임). 내 주요 문제는 다음과 같습니다. 1) 수렴하기에 너무 느립니다 (많은 기록 데이터가 필요함). 2) 메모리 사용 관점에서 보면 지옥만큼 비효율적입니다. 3) 전혀 견고하지 않습니다.
gianluca

감사합니다. 불행히도, 이것은 여전히 ​​예상대로 작동하지 않습니다. 이전 주석과 동일한 시계열의 경우 166을 반환합니다 .166은 부분적으로 만 옳습니다 (제 관점에서 볼 때 주 단위 기간이 더 흥미 롭습니다). 그리고이 dl.dropbox.com/u/540394/chart2.png (TCP 수신기 창 분석) 와 같이 매우 시끄러운 시계열을 사용 하면 함수는 10을 반환하지만 1을 기대합니다 (명백한 것을 볼 수는 없습니다) 주기성). BTW 나는 너무 다른 신호를 다루기 때문에 내가 찾고있는 것을 찾기가 정말 어려울 것임을 알고 있습니다.
gianluca

166은 168의 나쁜 추정치가 아닙니다. 데이터가 매주 패턴으로 매시간 관찰된다는 것을 알고 있다면 왜 빈도를 추정합니까?
Rob Hyndman

5
개선 된 버전은 다음과 같이 예측 패키지에 있습니다.findfrequency
Rob Hyndman

10

공정이 정지 될 것으로 예상되면 (주기 / 시즌이 시간이 지나도 변하지 않음) 카이-제곱 주기도와 같은 것이 좋습니다 (예 : Sokolove and Bushell, 1978 참조). 그것은 매우 많은 양의 노이즈를 가질 수 있지만 매우 안정적인 주기성을 가질 것으로 예상되는 24 시간주기 데이터 분석에 일반적으로 사용됩니다.

이 접근 방식은 파형의 모양에 대해 가정하지 않지만 (사이클마다 일관된 것 이외) 모든 잡음이 신호의 평균과 상관 관계가 없어야합니다.

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)

마지막 두 줄은 예제 일 뿐이며, 부가적인 노이즈가 많은 경우에도 순수한 삼각 함수의주기를 식별 할 수 있음을 보여줍니다.

작성된 바와 같이, alpha호출 의 마지막 인수 ( )는 불필요하며, 함수는 단순히 찾을 수있는 '최상의'기간을 반환합니다. 첫 번째 return문장의 주석을 해제하고 두 번째 문장을 주석 처리하여 레벨에서 중요한 모든 기간의 목록을 리턴하십시오 alpha.

이 함수는 식별 가능한 기간을 입력했는지 또는 분수 기간과 함께 작동하는지 (또는 가능) 또는 확인하려는 경우 내장 된 다중 비교 제어가 없는지 확인하기 위해 일종의 온 전성 검사를 수행하지 않습니다. 여러 기간을보십시오. 그러나 그 외에는 합리적으로 견고해야합니다.


흥미롭게 보이지만 출력을 이해하지 못합니다. 기간이 시작되는 위치와 대부분의 p 값이 1인지는 알 수 없습니다.
Herman Toothrot

3

원하는 것을 더 명확하게 정의하고 싶을 수도 있습니다 (여기서는 안 될 경우 자신에게). 원하는 데이터가 노이즈 데이터에 포함 된 통계 상 가장 중요한 정지 기간 인 경우 기본적으로 두 가지 경로를 선택해야합니다.

1) 강력한 자기 상관 추정값을 계산하고 최대 계수를 취합니다.
2) 강력한 전력 스펙트럼 밀도 추정값을 계산하고 스펙트럼의 최대 값을 취합니다.

# 2의 문제점은 시끄러운 시계열의 경우 저주파에서 많은 양의 전력을 얻으므로 구별하기가 어렵다는 것입니다. 이 문제를 해결하는 몇 가지 기술이 있습니다 (예 : 사전 미백, PSD 추정). 데이터의 실제 기간이 충분히 길면 자동 감지가 가능합니다.

가장 좋은 방법 은 Maronna, Martin 및 Yohai의 강력한 통계-이론 및 방법 장 8.6, 8.7에서 확인할 수있는 강력한 자기 상관 루틴을 구현하는 것입니다 . "robust durbin-levinson"을 Google에서 검색하면 결과가 나타납니다.

간단한 답변을 찾고 있다면 답변이 있는지 확실하지 않습니다. 시계열의주기 감지는 복잡 할 수 있으며 마술을 수행 할 수있는 자동화 된 루틴을 요청하는 것이 너무 많을 수 있습니다.


당신의 소중한 정보에 감사드립니다, 나는 그 책을 확실히 볼 것입니다.
gianluca

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

이 방법은 적절한 이유로 'Empirical'으로 분류되었으며 고유 모드 기능 (개별 첨가제 구성 요소)이 혼동 될 위험이 있습니다. 다른 한편으로,이 방법은 매우 직관적이며 주기적으로 빠른 육안 검사에 도움이 될 수 있습니다.


0

위의 Rob Hyndman의 게시물을 참조하여 https://stats.stackexchange.com/a/1214/70282

find.freq 함수는 훌륭하게 작동합니다. 내가 사용하는 일일 데이터 세트에서 빈도가 7로 올바르게 계산되었습니다.

일주일 만에 시험해 보았을 때 빈도는 23이며, 이는 한 달의 평균 근무일 수인 21.42857 = 29.6 * 5 / 7에 매우 가깝습니다. (또는 반대로 23 * 7 / 5는 32입니다.)

나는 매일의 데이터를 되돌아 보면서 첫 번째 기간을 취하여 평균을 내고 다음 기간을 찾는 등의 실험을 수행했습니다. 아래를 참조하십시오.

find.freq.all = function (x) {  
  f = 찾기 주파수 (x);
  주파수 = c (f);  
  while (f> 1) {
    시작 = 1; # 또한 시도하십시오 시작 = f;
    x = 기간 적용 (x, seq (시작, 길이 (x), f), 평균); 
    f = 찾기 주파수 (x);
    주파수 = c (주파수, f);
  }
  if (length (freqs) == 1) {반환 (freqs); }
  for (i in 2 : length (freqs)) {
    주파수 [i] = 주파수 [i] * 주파수 [i-1];
  }
  freqs [1 :( 길이 (freqs) -1)];
}
find.freq.all (dailyts) # 일별 데이터 사용

위의 내용은 seq가 1 또는 f로 시작하는지에 따라 (7,28) 또는 (7,35)를 나타냅니다. (위의 의견 참조)

msts (...)의 계절 기간이 (7,28) 또는 (7,35) 여야 함을 의미합니다.

로직은 알고리즘 파라미터의 감도가 주어지면 초기 조건에 민감하게 보입니다. 28과 35의 평균은 31.5이며 한 달의 평균 길이에 가깝습니다.

나는 바퀴를 재발 명했다고 생각합니다.이 알고리즘의 이름은 무엇입니까? R 어딘가에 더 나은 구현이 있습니까?

나중에 1에서 7까지의 모든 시작을 시도하면서 위의 코드를 실행했으며 두 번째 기간 동안 35,35,28,28,28,28,28을 얻었습니다. 평균은 30 일이며 한 달의 평균 일수입니다. 흥미로운 ...

생각이나 의견이 있습니까?


0

Ljung-Box 테스트를 사용하여 계절성 차이가 가장 좋은 문구성에 도달하는지 파악할 수 있습니다. 나는 다른 주제로 일하고 있었고 실제로 같은 목적으로 이것을 사용했습니다. 월간 데이터에 대해 3 ~ 24와 같은 다른 기간을 시도하십시오. 그리고 Ljung-Box로 각각을 테스트하고 Chi-Square 결과를 저장하십시오. 카이-제곱 값이 가장 낮은 기간을 선택하십시오.

간단한 코드는 다음과 같습니다.

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.