실시간 시계열 데이터에서 피크 신호 ​​감지


242

업데이트 : 지금까지 가장 성능이 좋은 알고리즘 은 이것 입니다.


이 질문은 실시간 시계열 데이터에서 갑작스러운 피크를 감지하기위한 강력한 알고리즘을 탐구합니다.

다음 데이터 세트를 고려하십시오.

p = [1 1 1.1 1 0.9 1 1 1.1 1 0.9 1 1.1 1 1 0.9 1 1 1.1 1 1 1 1 1.1 0.9 1 1.1 1 1 0.9 1, ...
     1.1 1 1 1.1 1 0.8 0.9 1 1.2 0.9 1 1 1.1 1.2 1 1.5 1 3 2 5 3 2 1 1 1 0.9 1 1 3, ... 
     2.6 4 3 3.2 2 1 1 0.8 4 4 2 2.5 1 1 1];

(매트랩 형식이지만 언어가 아니라 알고리즘에 관한 것입니다)

데이터 도표

세 개의 큰 피크와 약간의 작은 피크가 있음을 분명히 알 수 있습니다. 이 데이터 세트는 질문과 관련된 시계열 데이터 세트 클래스의 특정 예입니다. 이 클래스의 데이터 세트에는 두 가지 일반적인 기능이 있습니다.

  1. 일반적인 평균과 기본 소음이 있습니다
  2. 노이즈에서 크게 벗어난 큰 ' 피크 '또는 ' 높은 데이터 포인트 '가 있습니다.

다음을 가정 해 봅시다.

  • 피크의 폭은 미리 결정될 수 없다
  • 피크의 높이가 다른 값과 명확하고 크게 벗어납니다.
  • 사용 된 알고리즘은 실시간을 계산해야합니다 (따라서 새로운 데이터 포인트마다 변경)

이러한 상황에서는 신호를 트리거하는 경계 값을 구성해야합니다. 그러나 경계 값은 정적 일 수 없으며 알고리즘을 기반으로 실시간으로 결정해야합니다.


내 질문 : 실시간으로 그러한 임계 값을 계산하는 좋은 알고리즘은 무엇입니까? 그러한 상황에 대한 특정 알고리즘이 있습니까? 가장 잘 알려진 알고리즘은 무엇입니까?


강력한 알고리즘 또는 유용한 통찰력은 모두 높이 평가됩니다. (어떤 언어로든 대답 할 수 있습니다 : 알고리즘에 관한 것입니다)


5
이미 지정한 요구 사항 외에도 피크 가 되려면 절대 높이 요구 사항 이 있어야합니다 . 그렇지 않으면, 시간 13의 피크는 피크로 간주되어야합니다. (동일하게 : 장래에 피크가 1000 정도까지 올라간 경우 25와 35의 두 피크는 피크로 간주 되지 않아야 합니다.)
j_random_hacker

나는 동의한다. 이 피크가 우리가 고려해야 할 피크라고 가정 해 봅시다.
Jean-Paul

당신은 잘못된 질문을 할 수 있습니다. 지연없이 탐지 할 수있는 방법을 묻는 대신, 그 시간 이전에 알려진 것만으로도 특정 신호 유형을 지연없이 탐지 할 수 있는지 또는 어떤 신호로 무언가를 탐지하기 위해 신호에 대해 알아야하는 것이 있는지 물어볼 수 있습니다 지연.
hotpaw2

2
나는 이것을 포토 센서의 급격한 빛의 세기 변화를 감지하기 위해 사용했습니다. 나는 이동 평균에 의해 이런 짓을 하고 임계 값보다 큰 데이터 포인트를 무시. 이 임계 값은 피크를 결정하는 임계 값과 다릅니다. 따라서 이동 평균에 대해 1 stddev 내에있는 데이터 포인트 만 포함하고 stddev가 3 개 이상인 데이터 포인트를 피크로 고려하십시오. 이 알고리즘은 당시 우리의 응용 상황에 매우 적합했습니다.
justhalf

1
아, 알겠습니다 코드 형식으로 기대하지 않았습니다. 이 질문을 더 일찍 본 적이 있다면 아마도 그 대답이 훨씬 빠를 것입니다 = D. 어쨌든, 내 응용 프로그램은 포토 센서가 주변 광원으로부터 차단되는지 여부를 감지하는 것이 었습니다 (시간이 지남에 따라 주변 광원이 서서히 변할 수 있기 때문에 이동 평균이 필요한 이유입니다). 우리는 특정 패턴에 따라 센서 위에 손을 올려야하는 게임으로 이것을 만들었습니다. = D
justhalf

답변:


334

강력한 피크 감지 알고리즘 (z- 스코어 사용)

이러한 유형의 데이터 세트에 매우 잘 작동하는 알고리즘을 생각해 냈습니다. 이는 분산 원리를 기반으로합니다 . 만약 새로운 데이터 포인트가 어떤 이동 평균과 거리를두고 주어진 x 수의 표준 편차라면, 알고리즘 신호 (또는 z- 점수 라고도 함 ). 이 알고리즘은 신호가 임계 값을 손상시키지 않도록 별도의 이동 평균 및 편차를 구성하기 때문에 매우 강력 합니다. 따라서 미래의 신호는 이전 신호의 양에 관계없이 거의 동일한 정확도로 식별됩니다. 알고리즘은 lag = the lag of the moving window, threshold = the z-score at which the algorithm signals및의 3 가지 입력을받습니다 influence = the influence (between 0 and 1) of new signals on the mean and standard deviation. 예를 들어, a lagof 5는 마지막 5 개의 관측 값을 사용하여 데이터를 평활화합니다. ㅏthreshold데이터 포인트가 이동 평균에서 3.5 표준 편차 떨어져있는 경우 3.5의 신호는 신호를 보냅니다. 그리고 influence0.5의 값은 신호 가 정상 데이터 포인트에 미치는 영향의 절반 을 나타냅니다. 마찬가지로, influence0은 새 임계 값을 다시 계산하기 위해 신호를 완전히 무시합니다. 따라서 0의 영향이 가장 강력한 옵션입니다 (그러나 정상 성을 가정 함 ). 영향 옵션을 1로 설정하는 것이 가장 강력하지 않습니다. 고정 데이터가 아닌 경우 영향 옵션은 0과 1 사이에 있어야합니다.

다음과 같이 작동합니다.

의사 코드

# Let y be a vector of timeseries data of at least length lag+2
# Let mean() be a function that calculates the mean
# Let std() be a function that calculates the standard deviaton
# Let absolute() be the absolute value function

# Settings (the ones below are examples: choose what is best for your data)
set lag to 5;          # lag 5 for the smoothing functions
set threshold to 3.5;  # 3.5 standard deviations for signal
set influence to 0.5;  # between 0 and 1, where 1 is normal influence, 0.5 is half

# Initialize variables
set signals to vector 0,...,0 of length of y;   # Initialize signal results
set filteredY to y(1),...,y(lag)                # Initialize filtered series
set avgFilter to null;                          # Initialize average filter
set stdFilter to null;                          # Initialize std. filter
set avgFilter(lag) to mean(y(1),...,y(lag));    # Initialize first value
set stdFilter(lag) to std(y(1),...,y(lag));     # Initialize first value

for i=lag+1,...,t do
  if absolute(y(i) - avgFilter(i-1)) > threshold*stdFilter(i-1) then
    if y(i) > avgFilter(i-1) then
      set signals(i) to +1;                     # Positive signal
    else
      set signals(i) to -1;                     # Negative signal
    end
    # Reduce influence of signal
    set filteredY(i) to influence*y(i) + (1-influence)*filteredY(i-1);
  else
    set signals(i) to 0;                        # No signal
    set filteredY(i) to y(i);
  end
  # Adjust the filters
  set avgFilter(i) to mean(filteredY(i-lag),...,filteredY(i));
  set stdFilter(i) to std(filteredY(i-lag),...,filteredY(i));
end

데이터에 적합한 매개 변수를 선택하기위한 경험 법칙은 아래에서 확인할 수 있습니다.


데모

강력한 임계 값 알고리즘 시연

이 데모의 Matlab 코드는 여기 에서 찾을 수 있습니다 . 데모를 사용하려면 데모를 실행하고 상단 차트를 클릭하여 시계열을 직접 만드십시오. lag여러 관측 값 을 그린 후 알고리즘이 작동하기 시작합니다 .


결과

원래 질문에 대해이 알고리즘은 다음 설정을 사용할 때 다음과 같은 출력을 제공합니다. lag = 30, threshold = 5, influence = 0:

임계 값 알고리즘 예


다른 프로그래밍 언어로 구현 :


알고리즘 구성을위한 경험 법칙

lag: lag 매개 변수는 데이터를 얼마나 부드럽게 할 것인지와 알고리즘이 데이터의 장기 평균 변화에 얼마나 적응할 수 있는지를 결정합니다. 더 많은 고정 데이터, 당신은 포함해야 더 시차 (이 알고리즘의 견고성을 개선해야한다)입니다. 데이터에 시변 추세가 포함 된 경우 알고리즘이 이러한 추세에 얼마나 빨리 적용되기를 원하는지 고려해야합니다. 즉, lag10 을 입력 하면 알고리즘의 임계 값이 장기 평균의 체계적인 변경에 맞게 조정되기 전에 10 개의 '기간'이 걸립니다. 따라서 lag데이터의 트 렌딩 동작과 알고리즘의 적응성을 기반으로 매개 변수를 선택하십시오 .

influence:이 파라미터는 알고리즘의 감지 임계 값에 대한 신호의 영향을 결정합니다. 0으로 설정하면 신호는 임계 값에 영향을 미치지 않으므로 미래 신호는 과거 신호의 영향을받지 않는 평균 및 표준 편차로 계산 된 임계 값을 기반으로 감지됩니다. 이것에 대해 생각하는 또 다른 방법은 영향을 0으로 설정하면, 고정도를 암시 적으로 가정한다는 것입니다 (즉, 신호의 수에 관계없이 시계열은 항상 장기적으로 동일한 평균으로 돌아갑니다). 그렇지 않은 경우 신호가 데이터의 시변 추세에 체계적으로 영향을 줄 수있는 정도에 따라 영향 매개 변수를 0과 1 사이에 놓아야합니다. 예를 들어 신호가 구조적 중단으로 이어질 경우 시계열의 장기 평균에서 영향 매개 변수를 높게 (1에 가깝게) 설정하여 임계 값이 이러한 변화에 빠르게 적응할 수 있도록해야합니다.

threshold: 임계 값 매개 변수는 알고리즘이 새로운 데이터 포인트를 신호로 분류하는 이동 평균과의 표준 편차 수입니다. 예를 들어, 새 데이터 포인트가 이동 평균보다 4.0 표준 편차이고 임계 값 매개 변수가 3.5로 설정된 경우 알고리즘은 데이터 포인트를 신호로 식별합니다. 이 매개 변수는 예상되는 신호 수에 따라 설정해야합니다. 예를 들어, 데이터가 정규 분포 인 경우 임계 값 (또는 z- 점수) 3.5는 이 표의 신호 확률 0.00047에 해당 합니다.)는 2128 개의 데이터 포인트 (1 / 0.00047)마다 한 번씩 신호를 예상한다는 의미입니다. 따라서 임계 값은 알고리즘의 민감도와 알고리즘 신호 빈도에 직접적인 영향을줍니다. 자신의 데이터를 검사하고 필요할 때 알고리즘 신호를 만드는 합리적인 임계 값을 결정하십시오 (여러 목적에 적합한 임계 값에 도달하려면 일부 시행 착오가 필요할 수 있습니다).


경고 : 위 코드는 항상 모든 데이터 포인트가 실행될 때마다 반복됩니다. 이 코드를 구현할 때는 신호 계산을 별도의 함수 (루프없이)로 분할해야합니다. 그런 다음 새로운 데이터 포인트가 도착하면, 갱신 filteredY, avgFilter그리고 stdFilter한 번. 새 데이터 포인트가있을 때마다 (위의 예와 같이) 모든 데이터에 대한 신호를 다시 계산하지 마십시오. 이는 매우 비효율적이고 느립니다!

잠재적 인 개선을 위해 알고리즘을 수정하는 다른 방법은 다음과 같습니다.

  1. 평균 대신 중앙값 사용
  2. 용도 규모의 강력한 조치를 대신 표준 편차, 같은 MAD로,
  3. 신호 마진을 사용하면 신호가 너무 자주 전환되지 않습니다
  4. 영향 매개 변수 작동 방식 변경
  5. 치료 다운 다른 신호 (비대칭 처리)
  6. influence평균과 표준에 대한 별도의 매개 변수를 만듭니다 ( 이 Swift 변환에서 수행 된 것처럼 )

이 StackOverflow 답변에 대한 (알려진) 학술 인용 :

알고리즘을 사용하는 다른 작업

이 알고리즘의 다른 응용

다른 피크 검출 알고리즘에 대한 링크


이 기능을 어딘가에 사용하면 저 또는이 답변을 알려주십시오. 이 알고리즘에 대해 궁금한 점이 있으면 아래 의견에 게시하거나 LinkedIn 에서 문의하십시오 .



movingstd에 대한 링크가 끊어졌지만 여기서
Phylliida

@reasra 함수를 다시 쓴 후 이동 표준 편차가 필요하지 않습니다. 이제 간단한 내장 Matlab 함수와 함께 사용할 수 있습니다 :)
Jean-Paul

1
일부 가속도계 데이터에 대해 Matlab 코드를 사용하려고하지만 어떤 이유로 든 threshold데이터에서 최대 20 번 급증 후 그래프가 평평한 녹색 선이되고 나머지 그래프에서는 그대로 유지됩니다 ... 나는 sike를 제거하지만, 이것은 일어나지 않으므로 데이터의 급증으로 인한 것 같습니다. 무슨 일이 일어날 지 아십니까? 나는 Matlab의 초보자이기 때문에 이해할 수 없습니다 ...
Magnus W

@BadCash (데이터와 함께) 예제를 제공 할 수 있습니까? 아마도 여기에 자신의 질문을하고 링크를 알려주십시오.
Jean-Paul

2
이 알고리즘을 향상시키는 방법에는 여러 가지가 있으므로 창의력을 발휘하십시오 (다른 처리 위 / 아래; 평균 대신 중간; 강력한 표준; 코드를 메모리 효율적인 함수로 작성; 임계 값 마진으로 신호가 너무 자주 전환되지 않도록하는 등). ).
Jean-Paul 17

41

여기서이다 Python/의 numpy평활화 Z 점수 알고리즘의 구현 (참조 : 상기 답 ). 여기서 요점을 찾을 수 있습니다 .

#!/usr/bin/env python
# Implementation of algorithm from https://stackoverflow.com/a/22640362/6029703
import numpy as np
import pylab

def thresholding_algo(y, lag, threshold, influence):
    signals = np.zeros(len(y))
    filteredY = np.array(y)
    avgFilter = [0]*len(y)
    stdFilter = [0]*len(y)
    avgFilter[lag - 1] = np.mean(y[0:lag])
    stdFilter[lag - 1] = np.std(y[0:lag])
    for i in range(lag, len(y)):
        if abs(y[i] - avgFilter[i-1]) > threshold * stdFilter [i-1]:
            if y[i] > avgFilter[i-1]:
                signals[i] = 1
            else:
                signals[i] = -1

            filteredY[i] = influence * y[i] + (1 - influence) * filteredY[i-1]
            avgFilter[i] = np.mean(filteredY[(i-lag+1):i+1])
            stdFilter[i] = np.std(filteredY[(i-lag+1):i+1])
        else:
            signals[i] = 0
            filteredY[i] = y[i]
            avgFilter[i] = np.mean(filteredY[(i-lag+1):i+1])
            stdFilter[i] = np.std(filteredY[(i-lag+1):i+1])

    return dict(signals = np.asarray(signals),
                avgFilter = np.asarray(avgFilter),
                stdFilter = np.asarray(stdFilter))

아래는 R/ 에 대한 원래 답변과 동일한 플롯을 생성하는 동일한 데이터 세트에 대한 테스트입니다.Matlab

# Data
y = np.array([1,1,1.1,1,0.9,1,1,1.1,1,0.9,1,1.1,1,1,0.9,1,1,1.1,1,1,1,1,1.1,0.9,1,1.1,1,1,0.9,
       1,1.1,1,1,1.1,1,0.8,0.9,1,1.2,0.9,1,1,1.1,1.2,1,1.5,1,3,2,5,3,2,1,1,1,0.9,1,1,3,
       2.6,4,3,3.2,2,1,1,0.8,4,4,2,2.5,1,1,1])

# Settings: lag = 30, threshold = 5, influence = 0
lag = 30
threshold = 5
influence = 0

# Run algo with settings from above
result = thresholding_algo(y, lag=lag, threshold=threshold, influence=influence)

# Plot result
pylab.subplot(211)
pylab.plot(np.arange(1, len(y)+1), y)

pylab.plot(np.arange(1, len(y)+1),
           result["avgFilter"], color="cyan", lw=2)

pylab.plot(np.arange(1, len(y)+1),
           result["avgFilter"] + threshold * result["stdFilter"], color="green", lw=2)

pylab.plot(np.arange(1, len(y)+1),
           result["avgFilter"] - threshold * result["stdFilter"], color="green", lw=2)

pylab.subplot(212)
pylab.step(np.arange(1, len(y)+1), result["signals"], color="red", lw=2)
pylab.ylim(-1.5, 1.5)
pylab.show()

여기에서 'y'는 실제로 신호이고 '신호'는 데이터 포인트 세트입니다. 이해하는 것이 맞습니까?
TheTank

1
@TheTank는 y당신이 전달하는 데이터 배열, signals는 IS +1또는 -1각 데이터 포인트에 대한 표시 출력 배열 y[i]이 데이터 포인트가 사용하는 설정 주어진 "중요한 피크"인지.
Jean-Paul

23

한 가지 방법은 다음 관찰을 기반으로 피크를 감지하는 것입니다.

  • 시간 t는 (y (t)> y (t-1)) && (y (t)> y (t + 1) 인 경우 피크 임)

상승 추세가 끝날 때까지 기다림으로써 오 탐지를 피합니다. 피크가 1 dt만큼 빠질 것이라는 점에서 정확히 "실시간"은 아닙니다. 비교를 위해 마진을 요구함으로써 감도를 제어 할 수 있습니다. 시끄러운 감지와 감지 지연 사이에는 상충 관계가 있습니다. 더 많은 매개 변수를 추가하여 모델을 보강 할 수 있습니다.

  • (y (t)-y (t-dt)> m) && (y (t)-y (t + dt)> m) 피크

여기서 dtm 은 감도 대 시간 지연을 제어하기위한 매개 변수입니다.

이것은 언급 된 알고리즘으로 얻는 것입니다. 여기에 이미지 설명을 입력하십시오

파이썬으로 줄거리를 재현하는 코드는 다음과 같습니다.

import numpy as np
import matplotlib.pyplot as plt
input = np.array([ 1. ,  1. ,  1. ,  1. ,  1. ,  1. ,  1. ,  1.1,  1. ,  0.8,  0.9,
    1. ,  1.2,  0.9,  1. ,  1. ,  1.1,  1.2,  1. ,  1.5,  1. ,  3. ,
    2. ,  5. ,  3. ,  2. ,  1. ,  1. ,  1. ,  0.9,  1. ,  1. ,  3. ,
    2.6,  4. ,  3. ,  3.2,  2. ,  1. ,  1. ,  1. ,  1. ,  1. ])
signal = (input > np.roll(input,1)) & (input > np.roll(input,-1))
plt.plot(input)
plt.plot(signal.nonzero()[0], input[signal], 'ro')
plt.show()

을 설정 m = 0.5하면 오 탐지가 하나만있는 더 깨끗한 신호를 얻을 수 있습니다. 여기에 이미지 설명을 입력하십시오


이전 = 더 좋을수록 모든 피크가 중요합니다. 감사! 아주 멋지다!
Jean-Paul

감도 변경은 어떻게합니까?
Jean-Paul

두 가지 접근 방식을 생각할 수 있습니다. 1 : 더 큰 피크 만 감지되도록 m 을 더 큰 값으로 설정 합니다. 2 : y (t)-y (t-dt) (및 y (t)-y (t + dt))를 계산하는 대신 t-dt에서 t (및 t에서 t + dt)로 통합합니다.
aha

2
다른 7 개의 피크를 거부하는 기준은 무엇입니까?
hotpaw2

4
평평한 피크에는 문제가 있습니다. 기본적으로 1D 에지 감지 ([1 0 -1]로 신호를 변환하는 것과 같은)이므로
ben

18

신호 처리에서 피크 검출은 종종 웨이블릿 변환을 통해 수행됩니다. 기본적으로 시계열 데이터에서 이산 웨이블릿 변환을 수행합니다. 반환되는 세부 계수의 제로 크로싱은 시계열 신호의 피크에 해당합니다. 다른 세부 계수 레벨에서 다른 피크 진폭을 감지하여 다중 레벨 분해능을 제공합니다.


1
당신의 대답은 나를 보자 이 문서이 답변 내 구현을위한 좋은 알고리즘을 구성하는 도움이됩니다. 감사!
Jean-Paul

@cklin 웨이블릿 코프의 제로 크로싱을 계산하는 방법은 원래 시계열과 동일한 시간 스케일이 아니기 때문에 어떻게 설명 할 수 있습니까? 이 사용법에 대한 언급이 있습니까?
horaceT

11

우리는 데이터 세트에서 평활화 된 z- 점수 알고리즘을 사용하려고 시도했습니다. 이는 중간 정도가 거의없이 과민성 또는 과민성 (매개 변수 조정 방법에 따라 다름)을 초래합니다. 사이트의 트래픽 신호에서 일일주기를 나타내는 저주파 기준선을 관찰했으며, 가능한 최상의 매개 변수 (아래 그림 참조)를 사용하더라도 대부분의 데이터 포인트가 비정상으로 인식되기 때문에 특히 4 일째에 계속 이어집니다. .

원래의 z- 점수 알고리즘을 기반으로 리버스 필터링을 통해이 문제를 해결하는 방법을 찾았습니다. 수정 된 알고리즘 및 TV 광고 트래픽 속성에 대한 애플리케이션의 세부 사항은 팀 블로그 에 게시되어 있습니다.

여기에 이미지 설명을 입력하십시오


알고리즘이 고급 버전의 시작점임을 알 수 있습니다. 데이터에는 매우 특정한 패턴이 있으므로 먼저 다른 기술을 사용하여 패턴을 제거한 다음 잔차에 알고리즘을 적용하는 것이 더 합리적입니다. 또는 평균 /st.dev를 계산하기 위해 지연 창 대신 가운데를 사용하는 것이 좋습니다. 또 다른 의견 : 귀하의 솔루션은 스파이크를 식별하기 위해 오른쪽에서 왼쪽으로 이동하지만 실시간 응용 프로그램에서는 불가능합니다 (그래서 미래의 정보에 액세스 할 수 없기 때문에 원래 알고리즘이 그렇게 간단합니다).
Jean-Paul

10

전산 토폴로지에서 지속적인 상 동성이라는 아이디어는 효율적으로 (정렬 숫자만큼 빠름) 솔루션으로 이어집니다. 피크를 감지 할뿐만 아니라 피크의 "유의"를 자연스럽게 정량화하여 중요한 피크를 선택할 수 있습니다.

알고리즘 요약. 1 차원 설정 (시계열, 실수 신호)에서 알고리즘은 다음 그림으로 쉽게 설명 할 수 있습니다.

가장 지속적인 피크

함수 그래프 (또는 하위 수준 세트)를 가로로 생각하고 수위가 무한대 (또는이 그림에서 1.8)에서 시작하는 것을 고려하십시오. 레벨이 감소하는 동안 로컬 최대 섬에서 팝업이 나타납니다. 지역 최소에서이 섬들은 합쳐집니다. 이 아이디어의 세부 사항 중 하나는 나중에 나타난 섬이 더 오래된 섬으로 병합된다는 것입니다. 섬의 "지속성"은 출생 시간에서 사망 시간을 뺀 것입니다. 파란색 막대의 길이는 지속성을 나타내며 위에서 언급 한 피크의 "의의"입니다.

능률. 함수 값이 정렬 된 후 선형 시간으로 실행되는 구현 (실제로 단일 루프 임)을 찾는 것은 그리 어렵지 않습니다. 따라서이 구현은 실제로 빠르며 쉽게 구현됩니다.

참고 문헌. 전체 스토리를 작성하고 지속적인 상 동성 (전산 적 대수 토폴로지의 필드)의 동기에 대한 언급은 https://www.sthu.org/blog/13-perstopology-peakdetection/index.html 에서 확인할 수 있습니다.


이 알고리즘은 예를 들어 scipy.signal.find_peaks보다 훨씬 빠르고 정확합니다. 1053896 개의 데이터 포인트가있는 "실제"시계열의 경우 137516 개의 피크 (13 %)가 감지되었습니다. 피크의 순서 (가장 중요한 첫 번째)를 통해 가장 중요한 피크를 추출 할 수 있습니다. 각 피크의 시작, 피크 및 끝을 제공합니다. 시끄러운 데이터와 잘 작동합니다.
vinh

실시간 데이터 란 데이터 포인트가 매번 수신되는 소위 온라인 알고리즘을 의미합니다. 피크의 중요성은 미래의 가치에 의해 결정될 수 있습니다. 시간 복잡성을 크게 줄이지 않고 과거 결과를 수정하여 알고리즘을 온라인 상태로 만들면 좋을 것입니다.
S. Huber

9

시계열의 피크 검출을위한 단순 알고리즘 에서 GH Palshikar의 또 다른 알고리즘을 찾았습니다 .

알고리즘은 다음과 같습니다.

algorithm peak1 // one peak detection algorithms that uses peak function S1 

input T = x1, x2, …, xN, N // input time-series of N points 
input k // window size around the peak 
input h // typically 1 <= h <= 3 
output O // set of peaks detected in T 

begin 
O = empty set // initially empty 

    for (i = 1; i < n; i++) do
        // compute peak function value for each of the N points in T 
        a[i] = S1(k,i,xi,T); 
    end for 

    Compute the mean m' and standard deviation s' of all positive values in array a; 

    for (i = 1; i < n; i++) do // remove local peaks which are “small” in global context 
        if (a[i] > 0 && (a[i] – m') >( h * s')) then O = O + {xi}; 
        end if 
    end for 

    Order peaks in O in terms of increasing index in T 

    // retain only one peak out of any set of peaks within distance k of each other 

    for every adjacent pair of peaks xi and xj in O do 
        if |j – i| <= k then remove the smaller value of {xi, xj} from O 
        end if 
    end for 
end

장점

  • 이 논문은 피크 검출을위한 5 가지 알고리즘을 제공합니다
  • 알고리즘은 원시 시계열 데이터에서 작동합니다 (스무딩 필요 없음)

단점

  • 결정하기 kh어렵고 미리
  • 피크 평평 할 수 없습니다 (테스트 데이터의 세 번째 피크처럼)

예:

여기에 이미지 설명을 입력하십시오


실제로 흥미로운 종이. 그의 의견으로는 S4가 더 나은 기능인 것 같습니다. 그러나 더 중요한 것은 k <i <Nk가 true가 아닌 경우를 명확하게하는 것입니다. i = 0에 대해 함수 S1 (S2, ..)을 정의하는 방법은 단순히 2로 나뉘 지 않고 첫 번째 피연산자를 무시하고 다른 모든 피연산자를 포함했지만 i <= k의 경우 왼쪽에는 피연산자가 적었습니다. 오른쪽에
daniels_pa

8

다음은 Golang에서 Smoothed z-score 알고리즘의 구현입니다. []int16(PCM 16 비트 샘플) 조각을 가정합니다 . 여기서 요점을 찾을 수 있습니다 .

/*
Settings (the ones below are examples: choose what is best for your data)
set lag to 5;          # lag 5 for the smoothing functions
set threshold to 3.5;  # 3.5 standard deviations for signal
set influence to 0.5;  # between 0 and 1, where 1 is normal influence, 0.5 is half
*/

// ZScore on 16bit WAV samples
func ZScore(samples []int16, lag int, threshold float64, influence float64) (signals []int16) {
    //lag := 20
    //threshold := 3.5
    //influence := 0.5

    signals = make([]int16, len(samples))
    filteredY := make([]int16, len(samples))
    for i, sample := range samples[0:lag] {
        filteredY[i] = sample
    }
    avgFilter := make([]int16, len(samples))
    stdFilter := make([]int16, len(samples))

    avgFilter[lag] = Average(samples[0:lag])
    stdFilter[lag] = Std(samples[0:lag])

    for i := lag + 1; i < len(samples); i++ {

        f := float64(samples[i])

        if float64(Abs(samples[i]-avgFilter[i-1])) > threshold*float64(stdFilter[i-1]) {
            if samples[i] > avgFilter[i-1] {
                signals[i] = 1
            } else {
                signals[i] = -1
            }
            filteredY[i] = int16(influence*f + (1-influence)*float64(filteredY[i-1]))
            avgFilter[i] = Average(filteredY[(i - lag):i])
            stdFilter[i] = Std(filteredY[(i - lag):i])
        } else {
            signals[i] = 0
            filteredY[i] = samples[i]
            avgFilter[i] = Average(filteredY[(i - lag):i])
            stdFilter[i] = Std(filteredY[(i - lag):i])
        }
    }

    return
}

// Average a chunk of values
func Average(chunk []int16) (avg int16) {
    var sum int64
    for _, sample := range chunk {
        if sample < 0 {
            sample *= -1
        }
        sum += int64(sample)
    }
    return int16(sum / int64(len(chunk)))
}

@ Jean-Paul 나는 모든 것이 정확하다는 것을 확신하지 못하므로 버그가있을 수 있습니다.
Xeoncross

1
Matlab / R의 데모 예제 출력을 복제 해 보셨습니까? 품질을 잘 확인해야합니다.
Jean-Paul

7

다음은 이 답변 의 평활 z 점수 알고리즘의 C ++ 구현입니다.

std::vector<int> smoothedZScore(std::vector<float> input)
{   
    //lag 5 for the smoothing functions
    int lag = 5;
    //3.5 standard deviations for signal
    float threshold = 3.5;
    //between 0 and 1, where 1 is normal influence, 0.5 is half
    float influence = .5;

    if (input.size() <= lag + 2)
    {
        std::vector<int> emptyVec;
        return emptyVec;
    }

    //Initialise variables
    std::vector<int> signals(input.size(), 0.0);
    std::vector<float> filteredY(input.size(), 0.0);
    std::vector<float> avgFilter(input.size(), 0.0);
    std::vector<float> stdFilter(input.size(), 0.0);
    std::vector<float> subVecStart(input.begin(), input.begin() + lag);
    avgFilter[lag] = mean(subVecStart);
    stdFilter[lag] = stdDev(subVecStart);

    for (size_t i = lag + 1; i < input.size(); i++)
    {
        if (std::abs(input[i] - avgFilter[i - 1]) > threshold * stdFilter[i - 1])
        {
            if (input[i] > avgFilter[i - 1])
            {
                signals[i] = 1; //# Positive signal
            }
            else
            {
                signals[i] = -1; //# Negative signal
            }
            //Make influence lower
            filteredY[i] = influence* input[i] + (1 - influence) * filteredY[i - 1];
        }
        else
        {
            signals[i] = 0; //# No signal
            filteredY[i] = input[i];
        }
        //Adjust the filters
        std::vector<float> subVec(filteredY.begin() + i - lag, filteredY.begin() + i);
        avgFilter[i] = mean(subVec);
        stdFilter[i] = stdDev(subVec);
    }
    return signals;
}

2
주의 사항 :이 구현은 실제로 평균 및 표준 편차를 계산하는 방법을 제공하지 않습니다. C ++ 11의 경우 쉬운 방법을 찾을 수 있습니다. stackoverflow.com/a/12405793/3250829
rayryeng

6

이 문제는 하이브리드 / 임베디드 시스템 과정에서 발생한 것과 비슷해 보이지만 센서의 입력에 노이즈가있을 때 오류를 감지하는 것과 관련이 있습니다. 칼만 필터 를 사용 하여 시스템의 숨겨진 상태를 추정 / 예측 한 다음 통계 분석을 사용 하여 결함이 발생했을 가능성을 확인했습니다 . 우리는 선형 시스템으로 작업했지만 비선형 변형이 존재합니다. 접근 방식이 놀랍도록 적응적임을 기억하지만 시스템의 역학 모델이 ​​필요했습니다.


칼만 필터는 흥미롭지 만 목적에 맞는 알고리즘을 찾지 못하는 것 같습니다. 나는 대답을 매우 높이 평가 하며 알고리즘과 같은 알고리즘으로 배울 수 있는지 확인하기 위해이 같은 피크 감지 논문을 살펴볼 것 입니다. 감사!
Jean-Paul

6

C ++ 구현

#include <iostream>
#include <vector>
#include <algorithm>
#include <unordered_map>
#include <cmath>
#include <iterator>
#include <numeric>

using namespace std;

typedef long double ld;
typedef unsigned int uint;
typedef std::vector<ld>::iterator vec_iter_ld;

/**
 * Overriding the ostream operator for pretty printing vectors.
 */
template<typename T>
std::ostream &operator<<(std::ostream &os, std::vector<T> vec) {
    os << "[";
    if (vec.size() != 0) {
        std::copy(vec.begin(), vec.end() - 1, std::ostream_iterator<T>(os, " "));
        os << vec.back();
    }
    os << "]";
    return os;
}

/**
 * This class calculates mean and standard deviation of a subvector.
 * This is basically stats computation of a subvector of a window size qual to "lag".
 */
class VectorStats {
public:
    /**
     * Constructor for VectorStats class.
     *
     * @param start - This is the iterator position of the start of the window,
     * @param end   - This is the iterator position of the end of the window,
     */
    VectorStats(vec_iter_ld start, vec_iter_ld end) {
        this->start = start;
        this->end = end;
        this->compute();
    }

    /**
     * This method calculates the mean and standard deviation using STL function.
     * This is the Two-Pass implementation of the Mean & Variance calculation.
     */
    void compute() {
        ld sum = std::accumulate(start, end, 0.0);
        uint slice_size = std::distance(start, end);
        ld mean = sum / slice_size;
        std::vector<ld> diff(slice_size);
        std::transform(start, end, diff.begin(), [mean](ld x) { return x - mean; });
        ld sq_sum = std::inner_product(diff.begin(), diff.end(), diff.begin(), 0.0);
        ld std_dev = std::sqrt(sq_sum / slice_size);

        this->m1 = mean;
        this->m2 = std_dev;
    }

    ld mean() {
        return m1;
    }

    ld standard_deviation() {
        return m2;
    }

private:
    vec_iter_ld start;
    vec_iter_ld end;
    ld m1;
    ld m2;
};

/**
 * This is the implementation of the Smoothed Z-Score Algorithm.
 * This is direction translation of https://stackoverflow.com/a/22640362/1461896.
 *
 * @param input - input signal
 * @param lag - the lag of the moving window
 * @param threshold - the z-score at which the algorithm signals
 * @param influence - the influence (between 0 and 1) of new signals on the mean and standard deviation
 * @return a hashmap containing the filtered signal and corresponding mean and standard deviation.
 */
unordered_map<string, vector<ld>> z_score_thresholding(vector<ld> input, int lag, ld threshold, ld influence) {
    unordered_map<string, vector<ld>> output;

    uint n = (uint) input.size();
    vector<ld> signals(input.size());
    vector<ld> filtered_input(input.begin(), input.end());
    vector<ld> filtered_mean(input.size());
    vector<ld> filtered_stddev(input.size());

    VectorStats lag_subvector_stats(input.begin(), input.begin() + lag);
    filtered_mean[lag - 1] = lag_subvector_stats.mean();
    filtered_stddev[lag - 1] = lag_subvector_stats.standard_deviation();

    for (int i = lag; i < n; i++) {
        if (abs(input[i] - filtered_mean[i - 1]) > threshold * filtered_stddev[i - 1]) {
            signals[i] = (input[i] > filtered_mean[i - 1]) ? 1.0 : -1.0;
            filtered_input[i] = influence * input[i] + (1 - influence) * filtered_input[i - 1];
        } else {
            signals[i] = 0.0;
            filtered_input[i] = input[i];
        }
        VectorStats lag_subvector_stats(filtered_input.begin() + (i - lag), filtered_input.begin() + i);
        filtered_mean[i] = lag_subvector_stats.mean();
        filtered_stddev[i] = lag_subvector_stats.standard_deviation();
    }

    output["signals"] = signals;
    output["filtered_mean"] = filtered_mean;
    output["filtered_stddev"] = filtered_stddev;

    return output;
};

int main() {
    vector<ld> input = {1.0, 1.0, 1.1, 1.0, 0.9, 1.0, 1.0, 1.1, 1.0, 0.9, 1.0, 1.1, 1.0, 1.0, 0.9, 1.0, 1.0, 1.1, 1.0,
                        1.0, 1.0, 1.0, 1.1, 0.9, 1.0, 1.1, 1.0, 1.0, 0.9, 1.0, 1.1, 1.0, 1.0, 1.1, 1.0, 0.8, 0.9, 1.0,
                        1.2, 0.9, 1.0, 1.0, 1.1, 1.2, 1.0, 1.5, 1.0, 3.0, 2.0, 5.0, 3.0, 2.0, 1.0, 1.0, 1.0, 0.9, 1.0,
                        1.0, 3.0, 2.6, 4.0, 3.0, 3.2, 2.0, 1.0, 1.0, 0.8, 4.0, 4.0, 2.0, 2.5, 1.0, 1.0, 1.0};

    int lag = 30;
    ld threshold = 5.0;
    ld influence = 0.0;
    unordered_map<string, vector<ld>> output = z_score_thresholding(input, lag, threshold, influence);
    cout << output["signals"] << endl;
}

6

@ Jean-Paul의 제안 된 솔루션에 이어 C #에서 알고리즘을 구현했습니다.

public class ZScoreOutput
{
    public List<double> input;
    public List<int> signals;
    public List<double> avgFilter;
    public List<double> filtered_stddev;
}

public static class ZScore
{
    public static ZScoreOutput StartAlgo(List<double> input, int lag, double threshold, double influence)
    {
        // init variables!
        int[] signals = new int[input.Count];
        double[] filteredY = new List<double>(input).ToArray();
        double[] avgFilter = new double[input.Count];
        double[] stdFilter = new double[input.Count];

        var initialWindow = new List<double>(filteredY).Skip(0).Take(lag).ToList();

        avgFilter[lag - 1] = Mean(initialWindow);
        stdFilter[lag - 1] = StdDev(initialWindow);

        for (int i = lag; i < input.Count; i++)
        {
            if (Math.Abs(input[i] - avgFilter[i - 1]) > threshold * stdFilter[i - 1])
            {
                signals[i] = (input[i] > avgFilter[i - 1]) ? 1 : -1;
                filteredY[i] = influence * input[i] + (1 - influence) * filteredY[i - 1];
            }
            else
            {
                signals[i] = 0;
                filteredY[i] = input[i];
            }

            // Update rolling average and deviation
            var slidingWindow = new List<double>(filteredY).Skip(i - lag).Take(lag+1).ToList();

            var tmpMean = Mean(slidingWindow);
            var tmpStdDev = StdDev(slidingWindow);

            avgFilter[i] = Mean(slidingWindow);
            stdFilter[i] = StdDev(slidingWindow);
        }

        // Copy to convenience class 
        var result = new ZScoreOutput();
        result.input = input;
        result.avgFilter       = new List<double>(avgFilter);
        result.signals         = new List<int>(signals);
        result.filtered_stddev = new List<double>(stdFilter);

        return result;
    }

    private static double Mean(List<double> list)
    {
        // Simple helper function! 
        return list.Average();
    }

    private static double StdDev(List<double> values)
    {
        double ret = 0;
        if (values.Count() > 0)
        {
            double avg = values.Average();
            double sum = values.Sum(d => Math.Pow(d - avg, 2));
            ret = Math.Sqrt((sum) / (values.Count() - 1));
        }
        return ret;
    }
}

사용법 예 :

var input = new List<double> {1.0, 1.0, 1.1, 1.0, 0.9, 1.0, 1.0, 1.1, 1.0, 0.9, 1.0,
    1.1, 1.0, 1.0, 0.9, 1.0, 1.0, 1.1, 1.0, 1.0, 1.0, 1.0, 1.1, 0.9, 1.0, 1.1, 1.0, 1.0, 0.9,
    1.0, 1.1, 1.0, 1.0, 1.1, 1.0, 0.8, 0.9, 1.0, 1.2, 0.9, 1.0, 1.0, 1.1, 1.2, 1.0, 1.5, 1.0,
    3.0, 2.0, 5.0, 3.0, 2.0, 1.0, 1.0, 1.0, 0.9, 1.0, 1.0, 3.0, 2.6, 4.0, 3.0, 3.2, 2.0, 1.0,
    1.0, 0.8, 4.0, 4.0, 2.0, 2.5, 1.0, 1.0, 1.0};

int lag = 30;
double threshold = 5.0;
double influence = 0.0;

var output = ZScore.StartAlgo(input, lag, threshold, influence);

1
안녕하세요 @ Jean-Paul. 건배. 예, R 버전과 비교하여 출력을 테스트하여 일치하는지 확인했습니다. 이 문제에 대한 귀하의 해결책에 다시 한번 감사드립니다.
Ocean Airdrop

안녕하세요, 그 코드에 오류가 있다고 생각합니다. StdDev 메소드에서 값을 가져옵니다 .Count ()-1, -1이 있어야합니까? 나는 당신이 아이템의 수를 원할 것이라고 생각합니다. 그것이 당신이 values.Count ()에서 얻는 것입니다.
빅토르

1
흠 .. 좋은 자리. 원래 알고리즘을 C #으로 이식했지만 사용하지는 않았습니다. 아마도 전체 함수를 너겟 라이브러리 MathNet에 대한 호출로 바꿀 것입니다. "Install-Package MathNet.Numerics"PopulationStandardDeviation () 및 StandardDeviation ()에 대한 사전 빌드 함수가 있습니다. 예. var 채우기 StdDev = 새 목록 <double> (1,2,3,4) .PopulationStandardDeviation (); var sampleStdDev = 새 목록 <double> (1,2,3,4) .StandardDeviation ();
Ocean Airdrop

6

다음 은 가속도계 판독을 수행하고 영향의 방향이 왼쪽에서 오른쪽인지 여부를 결정하는 데 사용되는 Arduino 마이크로 컨트롤러에 대한 @ Jean-Paul의 Smoothed Z- 점수 C 구현입니다 . 이 장치는 바운스 된 신호를 반환하기 때문에 실제로 잘 수행됩니다. 다음은 장치에서이 피크 검출 알고리즘에 대한 입력입니다. 오른쪽에서 영향을, 왼쪽에서 영향을 나타냅니다. 초기 스파이크와 센서의 진동을 볼 수 있습니다.

여기에 이미지 설명을 입력하십시오

#include <stdio.h>
#include <math.h>
#include <string.h>


#define SAMPLE_LENGTH 1000

float stddev(float data[], int len);
float mean(float data[], int len);
void thresholding(float y[], int signals[], int lag, float threshold, float influence);


void thresholding(float y[], int signals[], int lag, float threshold, float influence) {
    memset(signals, 0, sizeof(float) * SAMPLE_LENGTH);
    float filteredY[SAMPLE_LENGTH];
    memcpy(filteredY, y, sizeof(float) * SAMPLE_LENGTH);
    float avgFilter[SAMPLE_LENGTH];
    float stdFilter[SAMPLE_LENGTH];

    avgFilter[lag - 1] = mean(y, lag);
    stdFilter[lag - 1] = stddev(y, lag);

    for (int i = lag; i < SAMPLE_LENGTH; i++) {
        if (fabsf(y[i] - avgFilter[i-1]) > threshold * stdFilter[i-1]) {
            if (y[i] > avgFilter[i-1]) {
                signals[i] = 1;
            } else {
                signals[i] = -1;
            }
            filteredY[i] = influence * y[i] + (1 - influence) * filteredY[i-1];
        } else {
            signals[i] = 0;
        }
        avgFilter[i] = mean(filteredY + i-lag, lag);
        stdFilter[i] = stddev(filteredY + i-lag, lag);
    }
}

float mean(float data[], int len) {
    float sum = 0.0, mean = 0.0;

    int i;
    for(i=0; i<len; ++i) {
        sum += data[i];
    }

    mean = sum/len;
    return mean;


}

float stddev(float data[], int len) {
    float the_mean = mean(data, len);
    float standardDeviation = 0.0;

    int i;
    for(i=0; i<len; ++i) {
        standardDeviation += pow(data[i] - the_mean, 2);
    }

    return sqrt(standardDeviation/len);
}

int main() {
    printf("Hello, World!\n");
    int lag = 100;
    float threshold = 5;
    float influence = 0;
    float y[]=  {1,1,1.1,1,0.9,1,1,1.1,1,0.9,1,1.1,1,1,0.9,1,1,1.1,1,1,1,1,1.1,0.9,1,1.1,1,1,0.9,
  ....
1,1.1,1,1,1.1,1,0.8,0.9,1,1.2,0.9,1,1,1.1,1.2,1,1.5,1,3,2,5,3,2,1,1,1,0.9,1,1,3,       2.6,4,3,3.2,2,1,1,0.8,4,4,2,2.5,1,1,1,1.2,1,1.5,1,3,2,5,3,2,1,1,1,0.9,1,1,3,
       2.6,4,3,3.2,2,1,1,0.8,4,4,2,2.5,1,1,1}

    int signal[SAMPLE_LENGTH];

    thresholding(y, signal,  lag, threshold, influence);

    return 0;
}

그녀의 영향 = 0의 결과

여기에 이미지 설명을 입력하십시오

좋지는 않지만 여기에 영향이있는 = 1

여기에 이미지 설명을 입력하십시오

아주 좋습니다.


5

다음은 Groovy 답변을 기반으로 한 실제 Java 구현입니다. 앞서 게시 한 입니다. (나는 이미 Groovy 및 Kotlin 구현이 게시되어 있음을 알고 있지만 Java 만 수행 한 저와 같은 사람에게는 다른 언어와 Java 사이에서 변환하는 방법을 알아내는 것이 번거 롭습니다.)

(결과는 다른 사람들의 그래프와 일치합니다)

알고리즘 구현

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;

import org.apache.commons.math3.stat.descriptive.SummaryStatistics;

public class SignalDetector {

    public HashMap<String, List> analyzeDataForSignals(List<Double> data, int lag, Double threshold, Double influence) {

        // init stats instance
        SummaryStatistics stats = new SummaryStatistics();

        // the results (peaks, 1 or -1) of our algorithm
        List<Integer> signals = new ArrayList<Integer>(Collections.nCopies(data.size(), 0));

        // filter out the signals (peaks) from our original list (using influence arg)
        List<Double> filteredData = new ArrayList<Double>(data);

        // the current average of the rolling window
        List<Double> avgFilter = new ArrayList<Double>(Collections.nCopies(data.size(), 0.0d));

        // the current standard deviation of the rolling window
        List<Double> stdFilter = new ArrayList<Double>(Collections.nCopies(data.size(), 0.0d));

        // init avgFilter and stdFilter
        for (int i = 0; i < lag; i++) {
            stats.addValue(data.get(i));
        }
        avgFilter.set(lag - 1, stats.getMean());
        stdFilter.set(lag - 1, Math.sqrt(stats.getPopulationVariance())); // getStandardDeviation() uses sample variance
        stats.clear();

        // loop input starting at end of rolling window
        for (int i = lag; i < data.size(); i++) {

            // if the distance between the current value and average is enough standard deviations (threshold) away
            if (Math.abs((data.get(i) - avgFilter.get(i - 1))) > threshold * stdFilter.get(i - 1)) {

                // this is a signal (i.e. peak), determine if it is a positive or negative signal
                if (data.get(i) > avgFilter.get(i - 1)) {
                    signals.set(i, 1);
                } else {
                    signals.set(i, -1);
                }

                // filter this signal out using influence
                filteredData.set(i, (influence * data.get(i)) + ((1 - influence) * filteredData.get(i - 1)));
            } else {
                // ensure this signal remains a zero
                signals.set(i, 0);
                // ensure this value is not filtered
                filteredData.set(i, data.get(i));
            }

            // update rolling average and deviation
            for (int j = i - lag; j < i; j++) {
                stats.addValue(filteredData.get(j));
            }
            avgFilter.set(i, stats.getMean());
            stdFilter.set(i, Math.sqrt(stats.getPopulationVariance()));
            stats.clear();
        }

        HashMap<String, List> returnMap = new HashMap<String, List>();
        returnMap.put("signals", signals);
        returnMap.put("filteredData", filteredData);
        returnMap.put("avgFilter", avgFilter);
        returnMap.put("stdFilter", stdFilter);

        return returnMap;

    } // end
}

주요 방법

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;

public class Main {

    public static void main(String[] args) throws Exception {
        DecimalFormat df = new DecimalFormat("#0.000");

        ArrayList<Double> data = new ArrayList<Double>(Arrays.asList(1d, 1d, 1.1d, 1d, 0.9d, 1d, 1d, 1.1d, 1d, 0.9d, 1d,
                1.1d, 1d, 1d, 0.9d, 1d, 1d, 1.1d, 1d, 1d, 1d, 1d, 1.1d, 0.9d, 1d, 1.1d, 1d, 1d, 0.9d, 1d, 1.1d, 1d, 1d,
                1.1d, 1d, 0.8d, 0.9d, 1d, 1.2d, 0.9d, 1d, 1d, 1.1d, 1.2d, 1d, 1.5d, 1d, 3d, 2d, 5d, 3d, 2d, 1d, 1d, 1d,
                0.9d, 1d, 1d, 3d, 2.6d, 4d, 3d, 3.2d, 2d, 1d, 1d, 0.8d, 4d, 4d, 2d, 2.5d, 1d, 1d, 1d));

        SignalDetector signalDetector = new SignalDetector();
        int lag = 30;
        double threshold = 5;
        double influence = 0;

        HashMap<String, List> resultsMap = signalDetector.analyzeDataForSignals(data, lag, threshold, influence);
        // print algorithm params
        System.out.println("lag: " + lag + "\t\tthreshold: " + threshold + "\t\tinfluence: " + influence);

        System.out.println("Data size: " + data.size());
        System.out.println("Signals size: " + resultsMap.get("signals").size());

        // print data
        System.out.print("Data:\t\t");
        for (double d : data) {
            System.out.print(df.format(d) + "\t");
        }
        System.out.println();

        // print signals
        System.out.print("Signals:\t");
        List<Integer> signalsList = resultsMap.get("signals");
        for (int i : signalsList) {
            System.out.print(df.format(i) + "\t");
        }
        System.out.println();

        // print filtered data
        System.out.print("Filtered Data:\t");
        List<Double> filteredDataList = resultsMap.get("filteredData");
        for (double d : filteredDataList) {
            System.out.print(df.format(d) + "\t");
        }
        System.out.println();

        // print running average
        System.out.print("Avg Filter:\t");
        List<Double> avgFilterList = resultsMap.get("avgFilter");
        for (double d : avgFilterList) {
            System.out.print(df.format(d) + "\t");
        }
        System.out.println();

        // print running std
        System.out.print("Std filter:\t");
        List<Double> stdFilterList = resultsMap.get("stdFilter");
        for (double d : stdFilterList) {
            System.out.print(df.format(d) + "\t");
        }
        System.out.println();

        System.out.println();
        for (int i = 0; i < signalsList.size(); i++) {
            if (signalsList.get(i) != 0) {
                System.out.println("Point " + i + " gave signal " + signalsList.get(i));
            }
        }
    }
}

결과

lag: 30     threshold: 5.0      influence: 0.0
Data size: 74
Signals size: 74
Data:           1.000   1.000   1.100   1.000   0.900   1.000   1.000   1.100   1.000   0.900   1.000   1.100   1.000   1.000   0.900   1.000   1.000   1.100   1.000   1.000   1.000   1.000   1.100   0.900   1.000   1.100   1.000   1.000   0.900   1.000   1.100   1.000   1.000   1.100   1.000   0.800   0.900   1.000   1.200   0.900   1.000   1.000   1.100   1.200   1.000   1.500   1.000   3.000   2.000   5.000   3.000   2.000   1.000   1.000   1.000   0.900   1.000   1.000   3.000   2.600   4.000   3.000   3.200   2.000   1.000   1.000   0.800   4.000   4.000   2.000   2.500   1.000   1.000   1.000   
Signals:        0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   1.000   0.000   1.000   1.000   1.000   1.000   1.000   0.000   0.000   0.000   0.000   0.000   0.000   1.000   1.000   1.000   1.000   1.000   1.000   0.000   0.000   0.000   1.000   1.000   1.000   1.000   0.000   0.000   0.000   
Filtered Data:  1.000   1.000   1.100   1.000   0.900   1.000   1.000   1.100   1.000   0.900   1.000   1.100   1.000   1.000   0.900   1.000   1.000   1.100   1.000   1.000   1.000   1.000   1.100   0.900   1.000   1.100   1.000   1.000   0.900   1.000   1.100   1.000   1.000   1.100   1.000   0.800   0.900   1.000   1.200   0.900   1.000   1.000   1.100   1.200   1.000   1.000   1.000   1.000   1.000   1.000   1.000   1.000   1.000   1.000   1.000   0.900   1.000   1.000   1.000   1.000   1.000   1.000   1.000   1.000   1.000   1.000   0.800   0.800   0.800   0.800   0.800   1.000   1.000   1.000   
Avg Filter:     0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   1.003   1.003   1.007   1.007   1.003   1.007   1.010   1.003   1.000   0.997   1.003   1.003   1.003   1.000   1.003   1.010   1.013   1.013   1.013   1.010   1.010   1.010   1.010   1.010   1.007   1.010   1.010   1.003   1.003   1.003   1.007   1.007   1.003   1.003   1.003   1.000   1.000   1.007   1.003   0.997   0.983   0.980   0.973   0.973   0.970   
Std filter:     0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.000   0.060   0.060   0.063   0.063   0.060   0.063   0.060   0.071   0.073   0.071   0.080   0.080   0.080   0.077   0.080   0.087   0.085   0.085   0.085   0.083   0.083   0.083   0.083   0.083   0.081   0.079   0.079   0.080   0.080   0.080   0.077   0.077   0.075   0.075   0.075   0.073   0.073   0.063   0.071   0.080   0.078   0.083   0.089   0.089   0.086   

Point 45 gave signal 1
Point 47 gave signal 1
Point 48 gave signal 1
Point 49 gave signal 1
Point 50 gave signal 1
Point 51 gave signal 1
Point 58 gave signal 1
Point 59 gave signal 1
Point 60 gave signal 1
Point 61 gave signal 1
Point 62 gave signal 1
Point 63 gave signal 1
Point 67 gave signal 1
Point 68 gave signal 1
Point 69 gave signal 1
Point 70 gave signal 1

Java 실행의 데이터 및 결과를 보여주는 그래프


5

원래 답변에 대한 부록 1 MatlabR번역

MATLAB 코드

function [signals,avgFilter,stdFilter] = ThresholdingAlgo(y,lag,threshold,influence)
% Initialise signal results
signals = zeros(length(y),1);
% Initialise filtered series
filteredY = y(1:lag+1);
% Initialise filters
avgFilter(lag+1,1) = mean(y(1:lag+1));
stdFilter(lag+1,1) = std(y(1:lag+1));
% Loop over all datapoints y(lag+2),...,y(t)
for i=lag+2:length(y)
    % If new value is a specified number of deviations away
    if abs(y(i)-avgFilter(i-1)) > threshold*stdFilter(i-1)
        if y(i) > avgFilter(i-1)
            % Positive signal
            signals(i) = 1;
        else
            % Negative signal
            signals(i) = -1;
        end
        % Make influence lower
        filteredY(i) = influence*y(i)+(1-influence)*filteredY(i-1);
    else
        % No signal
        signals(i) = 0;
        filteredY(i) = y(i);
    end
    % Adjust the filters
    avgFilter(i) = mean(filteredY(i-lag:i));
    stdFilter(i) = std(filteredY(i-lag:i));
end
% Done, now return results
end

예:

% Data
y = [1 1 1.1 1 0.9 1 1 1.1 1 0.9 1 1.1 1 1 0.9 1 1 1.1 1 1,...
    1 1 1.1 0.9 1 1.1 1 1 0.9 1 1.1 1 1 1.1 1 0.8 0.9 1 1.2 0.9 1,...
    1 1.1 1.2 1 1.5 1 3 2 5 3 2 1 1 1 0.9 1,...
    1 3 2.6 4 3 3.2 2 1 1 0.8 4 4 2 2.5 1 1 1];

% Settings
lag = 30;
threshold = 5;
influence = 0;

% Get results
[signals,avg,dev] = ThresholdingAlgo(y,lag,threshold,influence);

figure; subplot(2,1,1); hold on;
x = 1:length(y); ix = lag+1:length(y);
area(x(ix),avg(ix)+threshold*dev(ix),'FaceColor',[0.9 0.9 0.9],'EdgeColor','none');
area(x(ix),avg(ix)-threshold*dev(ix),'FaceColor',[1 1 1],'EdgeColor','none');
plot(x(ix),avg(ix),'LineWidth',1,'Color','cyan','LineWidth',1.5);
plot(x(ix),avg(ix)+threshold*dev(ix),'LineWidth',1,'Color','green','LineWidth',1.5);
plot(x(ix),avg(ix)-threshold*dev(ix),'LineWidth',1,'Color','green','LineWidth',1.5);
plot(1:length(y),y,'b');
subplot(2,1,2);
stairs(signals,'r','LineWidth',1.5); ylim([-1.5 1.5]);

R 코드

ThresholdingAlgo <- function(y,lag,threshold,influence) {
  signals <- rep(0,length(y))
  filteredY <- y[0:lag]
  avgFilter <- NULL
  stdFilter <- NULL
  avgFilter[lag] <- mean(y[0:lag], na.rm=TRUE)
  stdFilter[lag] <- sd(y[0:lag], na.rm=TRUE)
  for (i in (lag+1):length(y)){
    if (abs(y[i]-avgFilter[i-1]) > threshold*stdFilter[i-1]) {
      if (y[i] > avgFilter[i-1]) {
        signals[i] <- 1;
      } else {
        signals[i] <- -1;
      }
      filteredY[i] <- influence*y[i]+(1-influence)*filteredY[i-1]
    } else {
      signals[i] <- 0
      filteredY[i] <- y[i]
    }
    avgFilter[i] <- mean(filteredY[(i-lag):i], na.rm=TRUE)
    stdFilter[i] <- sd(filteredY[(i-lag):i], na.rm=TRUE)
  }
  return(list("signals"=signals,"avgFilter"=avgFilter,"stdFilter"=stdFilter))
}

예:

# Data
y <- c(1,1,1.1,1,0.9,1,1,1.1,1,0.9,1,1.1,1,1,0.9,1,1,1.1,1,1,1,1,1.1,0.9,1,1.1,1,1,0.9,
       1,1.1,1,1,1.1,1,0.8,0.9,1,1.2,0.9,1,1,1.1,1.2,1,1.5,1,3,2,5,3,2,1,1,1,0.9,1,1,3,
       2.6,4,3,3.2,2,1,1,0.8,4,4,2,2.5,1,1,1)

lag       <- 30
threshold <- 5
influence <- 0

# Run algo with lag = 30, threshold = 5, influence = 0
result <- ThresholdingAlgo(y,lag,threshold,influence)

# Plot result
par(mfrow = c(2,1),oma = c(2,2,0,0) + 0.1,mar = c(0,0,2,1) + 0.2)
plot(1:length(y),y,type="l",ylab="",xlab="") 
lines(1:length(y),result$avgFilter,type="l",col="cyan",lwd=2)
lines(1:length(y),result$avgFilter+threshold*result$stdFilter,type="l",col="green",lwd=2)
lines(1:length(y),result$avgFilter-threshold*result$stdFilter,type="l",col="green",lwd=2)
plot(result$signals,type="S",col="red",ylab="",xlab="",ylim=c(-1.5,1.5),lwd=2)

이 코드 (둘 다 언어)는 원래 질문의 데이터에 대해 다음 결과를 생성합니다.

Matlab 코드의 임계 값 예제


원래 답변에 대한 부록 2 : Matlab데모 코드

(데이터를 만들려면 클릭하십시오)

MATLAB 데모

function [] = RobustThresholdingDemo()

%% SPECIFICATIONS
lag         = 5;       % lag for the smoothing
threshold   = 3.5;     % number of st.dev. away from the mean to signal
influence   = 0.3;     % when signal: how much influence for new data? (between 0 and 1)
                       % 1 is normal influence, 0.5 is half      
%% START DEMO
DemoScreen(30,lag,threshold,influence);

end

function [signals,avgFilter,stdFilter] = ThresholdingAlgo(y,lag,threshold,influence)
signals = zeros(length(y),1);
filteredY = y(1:lag+1);
avgFilter(lag+1,1) = mean(y(1:lag+1));
stdFilter(lag+1,1) = std(y(1:lag+1));
for i=lag+2:length(y)
    if abs(y(i)-avgFilter(i-1)) > threshold*stdFilter(i-1)
        if y(i) > avgFilter(i-1)
            signals(i) = 1;
        else
            signals(i) = -1;
        end
        filteredY(i) = influence*y(i)+(1-influence)*filteredY(i-1);
    else
        signals(i) = 0;
        filteredY(i) = y(i);
    end
    avgFilter(i) = mean(filteredY(i-lag:i));
    stdFilter(i) = std(filteredY(i-lag:i));
end
end

% Demo screen function
function [] = DemoScreen(n,lag,threshold,influence)
figure('Position',[200 100,1000,500]);
subplot(2,1,1);
title(sprintf(['Draw data points (%.0f max)      [settings: lag = %.0f, '...
    'threshold = %.2f, influence = %.2f]'],n,lag,threshold,influence));
ylim([0 5]); xlim([0 50]);
H = gca; subplot(2,1,1);
set(H, 'YLimMode', 'manual'); set(H, 'XLimMode', 'manual');
set(H, 'YLim', get(H,'YLim')); set(H, 'XLim', get(H,'XLim'));
xg = []; yg = [];
for i=1:n
    try
        [xi,yi] = ginput(1);
    catch
        return;
    end
    xg = [xg xi]; yg = [yg yi];
    if i == 1
        subplot(2,1,1); hold on;
        plot(H, xg(i),yg(i),'r.'); 
        text(xg(i),yg(i),num2str(i),'FontSize',7);
    end
    if length(xg) > lag
        [signals,avg,dev] = ...
            ThresholdingAlgo(yg,lag,threshold,influence);
        area(xg(lag+1:end),avg(lag+1:end)+threshold*dev(lag+1:end),...
            'FaceColor',[0.9 0.9 0.9],'EdgeColor','none');
        area(xg(lag+1:end),avg(lag+1:end)-threshold*dev(lag+1:end),...
            'FaceColor',[1 1 1],'EdgeColor','none');
        plot(xg(lag+1:end),avg(lag+1:end),'LineWidth',1,'Color','cyan');
        plot(xg(lag+1:end),avg(lag+1:end)+threshold*dev(lag+1:end),...
            'LineWidth',1,'Color','green');
        plot(xg(lag+1:end),avg(lag+1:end)-threshold*dev(lag+1:end),...
            'LineWidth',1,'Color','green');
        subplot(2,1,2); hold on; title('Signal output');
        stairs(xg(lag+1:end),signals(lag+1:end),'LineWidth',2,'Color','blue');
        ylim([-2 2]); xlim([0 50]); hold off;
    end
    subplot(2,1,1); hold on;
    for j=2:i
        plot(xg([j-1:j]),yg([j-1:j]),'r'); plot(H,xg(j),yg(j),'r.');
        text(xg(j),yg(j),num2str(j),'FontSize',7);
    end
end
end


4

다음은 허용 된 답변에서 "Smoothed z-score algo"에 대한 Ruby 솔루션을 작성하려는 시도입니다.

module ThresholdingAlgoMixin
  def mean(array)
    array.reduce(&:+) / array.size.to_f
  end

  def stddev(array)
    array_mean = mean(array)
    Math.sqrt(array.reduce(0.0) { |a, b| a.to_f + ((b.to_f - array_mean) ** 2) } / array.size.to_f)
  end

  def thresholding_algo(lag: 5, threshold: 3.5, influence: 0.5)
    return nil if size < lag * 2
    Array.new(size, 0).tap do |signals|
      filtered = Array.new(self)

      initial_slice = take(lag)
      avg_filter = Array.new(lag - 1, 0.0) + [mean(initial_slice)]
      std_filter = Array.new(lag - 1, 0.0) + [stddev(initial_slice)]
      (lag..size-1).each do |idx|
        prev = idx - 1
        if (fetch(idx) - avg_filter[prev]).abs > threshold * std_filter[prev]
          signals[idx] = fetch(idx) > avg_filter[prev] ? 1 : -1
          filtered[idx] = (influence * fetch(idx)) + ((1-influence) * filtered[prev])
        end

        filtered_slice = filtered[idx-lag..prev]
        avg_filter[idx] = mean(filtered_slice)
        std_filter[idx] = stddev(filtered_slice)
      end
    end
  end
end

그리고 사용법 예 :

test_data = [
  1, 1, 1.1, 1, 0.9, 1, 1, 1.1, 1, 0.9, 1, 1.1, 1, 1, 0.9, 1,
  1, 1.1, 1, 1, 1, 1, 1.1, 0.9, 1, 1.1, 1, 1, 0.9, 1, 1.1, 1,
  1, 1.1, 1, 0.8, 0.9, 1, 1.2, 0.9, 1, 1, 1.1, 1.2, 1, 1.5,
  1, 3, 2, 5, 3, 2, 1, 1, 1, 0.9, 1, 1, 3, 2.6, 4, 3, 3.2, 2,
  1, 1, 0.8, 4, 4, 2, 2.5, 1, 1, 1
].extend(ThresholdingAlgoMixin)

puts test_data.thresholding_algo.inspect

# Output: [
#   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
#   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0,
#   0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1,
#   1, 1, 0, 0, 0, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0
# ]

굉장합니다, 공유해 주셔서 감사합니다! 목록에 추가하겠습니다. 실시간 애플리케이션의 경우 매번 모든 데이터 포인트를 반복하는 대신 새 데이터 포인트가 도착할 때 신호를 업데이트하는 별도의 함수를 작성해야합니다.
Jean-Paul

4

https://stackoverflow.com/a/22640362/6029703 에 대한 python / numpy의 반복 버전 이 여기 있습니다. 이 코드는 큰 데이터 (100000+)에 대해 지연마다 계산 평균 및 표준 편차보다 빠릅니다.

def peak_detection_smoothed_zscore_v2(x, lag, threshold, influence):
    '''
    iterative smoothed z-score algorithm
    Implementation of algorithm from https://stackoverflow.com/a/22640362/6029703
    '''
    import numpy as np
    labels = np.zeros(len(x))
    filtered_y = np.array(x)
    avg_filter = np.zeros(len(x))
    std_filter = np.zeros(len(x))
    var_filter = np.zeros(len(x))

    avg_filter[lag - 1] = np.mean(x[0:lag])
    std_filter[lag - 1] = np.std(x[0:lag])
    var_filter[lag - 1] = np.var(x[0:lag])
    for i in range(lag, len(x)):
        if abs(x[i] - avg_filter[i - 1]) > threshold * std_filter[i - 1]:
            if x[i] > avg_filter[i - 1]:
                labels[i] = 1
            else:
                labels[i] = -1
            filtered_y[i] = influence * x[i] + (1 - influence) * filtered_y[i - 1]
        else:
            labels[i] = 0
            filtered_y[i] = x[i]
        # update avg, var, std
        avg_filter[i] = avg_filter[i - 1] + 1. / lag * (filtered_y[i] - filtered_y[i - lag])
        var_filter[i] = var_filter[i - 1] + 1. / lag * ((filtered_y[i] - avg_filter[i - 1]) ** 2 - (
            filtered_y[i - lag] - avg_filter[i - 1]) ** 2 - (filtered_y[i] - filtered_y[i - lag]) ** 2 / lag)
        std_filter[i] = np.sqrt(var_filter[i])

    return dict(signals=labels,
                avgFilter=avg_filter,
                stdFilter=std_filter)

4

Julia의 알고리즘 구현을 다른 사람들에게 제공하겠다고 생각했습니다. 요지는 여기 에서 찾을 수 있습니다

using Statistics
using Plots
function SmoothedZscoreAlgo(y, lag, threshold, influence)
    # Julia implimentation of http://stackoverflow.com/a/22640362/6029703
    n = length(y)
    signals = zeros(n) # init signal results
    filteredY = copy(y) # init filtered series
    avgFilter = zeros(n) # init average filter
    stdFilter = zeros(n) # init std filter
    avgFilter[lag - 1] = mean(y[1:lag]) # init first value
    stdFilter[lag - 1] = std(y[1:lag]) # init first value

    for i in range(lag, stop=n-1)
        if abs(y[i] - avgFilter[i-1]) > threshold*stdFilter[i-1]
            if y[i] > avgFilter[i-1]
                signals[i] += 1 # postive signal
            else
                signals[i] += -1 # negative signal
            end
            # Make influence lower
            filteredY[i] = influence*y[i] + (1-influence)*filteredY[i-1]
        else
            signals[i] = 0
            filteredY[i] = y[i]
        end
        avgFilter[i] = mean(filteredY[i-lag+1:i])
        stdFilter[i] = std(filteredY[i-lag+1:i])
    end
    return (signals = signals, avgFilter = avgFilter, stdFilter = stdFilter)
end


# Data
y = [1,1,1.1,1,0.9,1,1,1.1,1,0.9,1,1.1,1,1,0.9,1,1,1.1,1,1,1,1,1.1,0.9,1,1.1,1,1,0.9,
       1,1.1,1,1,1.1,1,0.8,0.9,1,1.2,0.9,1,1,1.1,1.2,1,1.5,1,3,2,5,3,2,1,1,1,0.9,1,1,3,
       2.6,4,3,3.2,2,1,1,0.8,4,4,2,2.5,1,1,1]

# Settings: lag = 30, threshold = 5, influence = 0
lag = 30
threshold = 5
influence = 0

results = SmoothedZscoreAlgo(y, lag, threshold, influence)
upper_bound = results[:avgFilter] + threshold * results[:stdFilter]
lower_bound = results[:avgFilter] - threshold * results[:stdFilter]
x = 1:length(y)

yplot = plot(x,y,color="blue", label="Y",legend=:topleft)
yplot = plot!(x,upper_bound, color="green", label="Upper Bound",legend=:topleft)
yplot = plot!(x,results[:avgFilter], color="cyan", label="Average Filter",legend=:topleft)
yplot = plot!(x,lower_bound, color="green", label="Lower Bound",legend=:topleft)
signalplot = plot(x,results[:signals],color="red",label="Signals",legend=:topleft)
plot(yplot,signalplot,layout=(2,1),legend=:topleft)

결과


3

다음은 스무딩 된 z- 점수 알고리즘의 Groovy (Java) 구현입니다 ( 위의 답변 참조 ).

/**
 * "Smoothed zero-score alogrithm" shamelessly copied from https://stackoverflow.com/a/22640362/6029703
 *  Uses a rolling mean and a rolling deviation (separate) to identify peaks in a vector
 *
 * @param y - The input vector to analyze
 * @param lag - The lag of the moving window (i.e. how big the window is)
 * @param threshold - The z-score at which the algorithm signals (i.e. how many standard deviations away from the moving mean a peak (or signal) is)
 * @param influence - The influence (between 0 and 1) of new signals on the mean and standard deviation (how much a peak (or signal) should affect other values near it)
 * @return - The calculated averages (avgFilter) and deviations (stdFilter), and the signals (signals)
 */

public HashMap<String, List<Object>> thresholdingAlgo(List<Double> y, Long lag, Double threshold, Double influence) {
    //init stats instance
    SummaryStatistics stats = new SummaryStatistics()

    //the results (peaks, 1 or -1) of our algorithm
    List<Integer> signals = new ArrayList<Integer>(Collections.nCopies(y.size(), 0))
    //filter out the signals (peaks) from our original list (using influence arg)
    List<Double> filteredY = new ArrayList<Double>(y)
    //the current average of the rolling window
    List<Double> avgFilter = new ArrayList<Double>(Collections.nCopies(y.size(), 0.0d))
    //the current standard deviation of the rolling window
    List<Double> stdFilter = new ArrayList<Double>(Collections.nCopies(y.size(), 0.0d))
    //init avgFilter and stdFilter
    (0..lag-1).each { stats.addValue(y[it as int]) }
    avgFilter[lag - 1 as int] = stats.getMean()
    stdFilter[lag - 1 as int] = Math.sqrt(stats.getPopulationVariance()) //getStandardDeviation() uses sample variance (not what we want)
    stats.clear()
    //loop input starting at end of rolling window
    (lag..y.size()-1).each { i ->
        //if the distance between the current value and average is enough standard deviations (threshold) away
        if (Math.abs((y[i as int] - avgFilter[i - 1 as int]) as Double) > threshold * stdFilter[i - 1 as int]) {
            //this is a signal (i.e. peak), determine if it is a positive or negative signal
            signals[i as int] = (y[i as int] > avgFilter[i - 1 as int]) ? 1 : -1
            //filter this signal out using influence
            filteredY[i as int] = (influence * y[i as int]) + ((1-influence) * filteredY[i - 1 as int])
        } else {
            //ensure this signal remains a zero
            signals[i as int] = 0
            //ensure this value is not filtered
            filteredY[i as int] = y[i as int]
        }
        //update rolling average and deviation
        (i - lag..i-1).each { stats.addValue(filteredY[it as int] as Double) }
        avgFilter[i as int] = stats.getMean()
        stdFilter[i as int] = Math.sqrt(stats.getPopulationVariance()) //getStandardDeviation() uses sample variance (not what we want)
        stats.clear()
    }

    return [
        signals  : signals,
        avgFilter: avgFilter,
        stdFilter: stdFilter
    ]
}

아래는 위의 Python / numpy 구현 과 동일한 결과를 생성하는 동일한 데이터 세트에 대한 테스트입니다 .

    // Data
    def y = [1d, 1d, 1.1d, 1d, 0.9d, 1d, 1d, 1.1d, 1d, 0.9d, 1d, 1.1d, 1d, 1d, 0.9d, 1d, 1d, 1.1d, 1d, 1d,
         1d, 1d, 1.1d, 0.9d, 1d, 1.1d, 1d, 1d, 0.9d, 1d, 1.1d, 1d, 1d, 1.1d, 1d, 0.8d, 0.9d, 1d, 1.2d, 0.9d, 1d,
         1d, 1.1d, 1.2d, 1d, 1.5d, 1d, 3d, 2d, 5d, 3d, 2d, 1d, 1d, 1d, 0.9d, 1d,
         1d, 3d, 2.6d, 4d, 3d, 3.2d, 2d, 1d, 1d, 0.8d, 4d, 4d, 2d, 2.5d, 1d, 1d, 1d]

    // Settings
    def lag = 30
    def threshold = 5
    def influence = 0


    def thresholdingResults = thresholdingAlgo((List<Double>) y, (Long) lag, (Double) threshold, (Double) influence)

    println y.size()
    println thresholdingResults.signals.size()
    println thresholdingResults.signals

    thresholdingResults.signals.eachWithIndex { x, idx ->
        if (x) {
            println y[idx]
        }
    }

3

다음은 스무딩 된 z- 점수 알고리즘 의 (동일하지 않은) 스칼라 버전입니다 .

/**
  * Smoothed zero-score alogrithm shamelessly copied from https://stackoverflow.com/a/22640362/6029703
  * Uses a rolling mean and a rolling deviation (separate) to identify peaks in a vector
  *
  * @param y - The input vector to analyze
  * @param lag - The lag of the moving window (i.e. how big the window is)
  * @param threshold - The z-score at which the algorithm signals (i.e. how many standard deviations away from the moving mean a peak (or signal) is)
  * @param influence - The influence (between 0 and 1) of new signals on the mean and standard deviation (how much a peak (or signal) should affect other values near it)
  * @return - The calculated averages (avgFilter) and deviations (stdFilter), and the signals (signals)
  */
private def smoothedZScore(y: Seq[Double], lag: Int, threshold: Double, influence: Double): Seq[Int] = {
  val stats = new SummaryStatistics()

  // the results (peaks, 1 or -1) of our algorithm
  val signals = mutable.ArrayBuffer.fill(y.length)(0)

  // filter out the signals (peaks) from our original list (using influence arg)
  val filteredY = y.to[mutable.ArrayBuffer]

  // the current average of the rolling window
  val avgFilter = mutable.ArrayBuffer.fill(y.length)(0d)

  // the current standard deviation of the rolling window
  val stdFilter = mutable.ArrayBuffer.fill(y.length)(0d)

  // init avgFilter and stdFilter
  y.take(lag).foreach(s => stats.addValue(s))

  avgFilter(lag - 1) = stats.getMean
  stdFilter(lag - 1) = Math.sqrt(stats.getPopulationVariance) // getStandardDeviation() uses sample variance (not what we want)

  // loop input starting at end of rolling window
  y.zipWithIndex.slice(lag, y.length - 1).foreach {
    case (s: Double, i: Int) =>
      // if the distance between the current value and average is enough standard deviations (threshold) away
      if (Math.abs(s - avgFilter(i - 1)) > threshold * stdFilter(i - 1)) {
        // this is a signal (i.e. peak), determine if it is a positive or negative signal
        signals(i) = if (s > avgFilter(i - 1)) 1 else -1
        // filter this signal out using influence
        filteredY(i) = (influence * s) + ((1 - influence) * filteredY(i - 1))
      } else {
        // ensure this signal remains a zero
        signals(i) = 0
        // ensure this value is not filtered
        filteredY(i) = s
      }

      // update rolling average and deviation
      stats.clear()
      filteredY.slice(i - lag, i).foreach(s => stats.addValue(s))
      avgFilter(i) = stats.getMean
      stdFilter(i) = Math.sqrt(stats.getPopulationVariance) // getStandardDeviation() uses sample variance (not what we want)
  }

  println(y.length)
  println(signals.length)
  println(signals)

  signals.zipWithIndex.foreach {
    case(x: Int, idx: Int) =>
      if (x == 1) {
        println(idx + " " + y(idx))
      }
  }

  val data =
    y.zipWithIndex.map { case (s: Double, i: Int) => Map("x" -> i, "y" -> s, "name" -> "y", "row" -> "data") } ++
    avgFilter.zipWithIndex.map { case (s: Double, i: Int) => Map("x" -> i, "y" -> s, "name" -> "avgFilter", "row" -> "data") } ++
    avgFilter.zipWithIndex.map { case (s: Double, i: Int) => Map("x" -> i, "y" -> (s - threshold * stdFilter(i)), "name" -> "lower", "row" -> "data") } ++
    avgFilter.zipWithIndex.map { case (s: Double, i: Int) => Map("x" -> i, "y" -> (s + threshold * stdFilter(i)), "name" -> "upper", "row" -> "data") } ++
    signals.zipWithIndex.map { case (s: Int, i: Int) => Map("x" -> i, "y" -> s, "name" -> "signal", "row" -> "signal") }

  Vegas("Smoothed Z")
    .withData(data)
    .mark(Line)
    .encodeX("x", Quant)
    .encodeY("y", Quant)
    .encodeColor(
      field="name",
      dataType=Nominal
    )
    .encodeRow("row", Ordinal)
    .show

  return signals
}

다음은 Python 및 Groovy 버전과 동일한 결과를 반환하는 테스트입니다.

val y = List(1d, 1d, 1.1d, 1d, 0.9d, 1d, 1d, 1.1d, 1d, 0.9d, 1d, 1.1d, 1d, 1d, 0.9d, 1d, 1d, 1.1d, 1d, 1d,
  1d, 1d, 1.1d, 0.9d, 1d, 1.1d, 1d, 1d, 0.9d, 1d, 1.1d, 1d, 1d, 1.1d, 1d, 0.8d, 0.9d, 1d, 1.2d, 0.9d, 1d,
  1d, 1.1d, 1.2d, 1d, 1.5d, 1d, 3d, 2d, 5d, 3d, 2d, 1d, 1d, 1d, 0.9d, 1d,
  1d, 3d, 2.6d, 4d, 3d, 3.2d, 2d, 1d, 1d, 0.8d, 4d, 4d, 2d, 2.5d, 1d, 1d, 1d)

val lag = 30
val threshold = 5d
val influence = 0d

smoothedZScore(y, lag, threshold, influence)

라스베가스 결과 차트

여기 요점


1은 피크를 나타내고 -1은 밸리를 나타냅니다.
마이크 로버츠

3

내 안드로이드 프로젝트에서 이와 같은 것이 필요했습니다. Kotlin 구현을 되돌려 줄 수 있다고 생각했습니다 .

/**
* Smoothed zero-score alogrithm shamelessly copied from https://stackoverflow.com/a/22640362/6029703
* Uses a rolling mean and a rolling deviation (separate) to identify peaks in a vector
*
* @param y - The input vector to analyze
* @param lag - The lag of the moving window (i.e. how big the window is)
* @param threshold - The z-score at which the algorithm signals (i.e. how many standard deviations away from the moving mean a peak (or signal) is)
* @param influence - The influence (between 0 and 1) of new signals on the mean and standard deviation (how much a peak (or signal) should affect other values near it)
* @return - The calculated averages (avgFilter) and deviations (stdFilter), and the signals (signals)
*/
fun smoothedZScore(y: List<Double>, lag: Int, threshold: Double, influence: Double): Triple<List<Int>, List<Double>, List<Double>> {
    val stats = SummaryStatistics()
    // the results (peaks, 1 or -1) of our algorithm
    val signals = MutableList<Int>(y.size, { 0 })
    // filter out the signals (peaks) from our original list (using influence arg)
    val filteredY = ArrayList<Double>(y)
    // the current average of the rolling window
    val avgFilter = MutableList<Double>(y.size, { 0.0 })
    // the current standard deviation of the rolling window
    val stdFilter = MutableList<Double>(y.size, { 0.0 })
    // init avgFilter and stdFilter
    y.take(lag).forEach { s -> stats.addValue(s) }
    avgFilter[lag - 1] = stats.mean
    stdFilter[lag - 1] = Math.sqrt(stats.populationVariance) // getStandardDeviation() uses sample variance (not what we want)
    stats.clear()
    //loop input starting at end of rolling window
    (lag..y.size - 1).forEach { i ->
        //if the distance between the current value and average is enough standard deviations (threshold) away
        if (Math.abs(y[i] - avgFilter[i - 1]) > threshold * stdFilter[i - 1]) {
            //this is a signal (i.e. peak), determine if it is a positive or negative signal
            signals[i] = if (y[i] > avgFilter[i - 1]) 1 else -1
            //filter this signal out using influence
            filteredY[i] = (influence * y[i]) + ((1 - influence) * filteredY[i - 1])
        } else {
            //ensure this signal remains a zero
            signals[i] = 0
            //ensure this value is not filtered
            filteredY[i] = y[i]
        }
        //update rolling average and deviation
        (i - lag..i - 1).forEach { stats.addValue(filteredY[it]) }
        avgFilter[i] = stats.getMean()
        stdFilter[i] = Math.sqrt(stats.getPopulationVariance()) //getStandardDeviation() uses sample variance (not what we want)
        stats.clear()
    }
    return Triple(signals, avgFilter, stdFilter)
}

검증 그래프가있는 샘플 프로젝트는 github 에서 찾을 수 있습니다 .

여기에 이미지 설명을 입력하십시오


대박! 공유해 주셔서 감사합니다. 실시간 애플리케이션의 경우 각 수신 데이터 포인트로 새 신호를 계산하는 별도의 함수를 작성하십시오. 새로운 데이터 포인트가 도착할 때마다 전체 데이터를 반복하지 마십시오. 이는 매우 비효율적입니다.)
Jean-Paul

1
내가 사용하는 창이 겹치지 않기 때문에 좋은 지적은 그것에 대해 생각하지 않았습니다.
leonardkraemer

3

다음은 ztran 점수 알고리즘 의 변경된 포트란 버전 입니다 . 주파수 공간의 전달 함수에서 피크 (공명) 감지를 위해 특별히 변경됩니다 (각 변경에는 코드에 작은 설명이 있습니다).

첫 번째 수정은 입력 벡터의 하한 근처에 특정 임계 값 (이 경우 10 %)보다 높은 표준 편차로 표시되는 공진이있는 경우 사용자에게 경고를 표시합니다. 이는 단순히 신호를 감지하여 필터를 올바르게 초기화하기에 충분하지 않은 신호를 의미합니다.

두 번째 수정은 가장 높은 피크 값만 발견 된 피크에 추가된다는 것입니다. 이것은 각각의 발견 된 피크 값을 그것의 (지연) 선행 작업과 (후속) 후임의 크기와 비교함으로써 달성됩니다.

세 번째 변화는 공명 피크가 일반적으로 공명 주파수 주위에 어떤 형태의 대칭을 나타내는 것입니다. 따라서 이전 데이터가 아닌 현재 데이터 포인트를 중심으로 대칭 적으로 평균과 표준 값을 계산하는 것이 당연합니다. 이로 인해 피크 감지 동작이 향상됩니다.

수정 사항은 전체 신호를 미리 공명 감지 기능으로 알고 있어야하는 효과가 있습니다 (공통 감지의 일반적인 사례 인 데이터 폴이 즉시 생성되는 Jean-Paul의 Matlab 예와 같이 작동하지 않음).

function PeakDetect(y,lag,threshold, influence)
    implicit none
    ! Declaring part
    real, dimension(:), intent(in) :: y
    integer, dimension(size(y)) :: PeakDetect
    real, dimension(size(y)) :: filteredY, avgFilter, stdFilter
    integer :: lag, ii
    real :: threshold, influence

    ! Executing part
    PeakDetect = 0
    filteredY = 0.0
    filteredY(1:lag+1) = y(1:lag+1)
    avgFilter = 0.0
    avgFilter(lag+1) = mean(y(1:2*lag+1))
    stdFilter = 0.0
    stdFilter(lag+1) = std(y(1:2*lag+1))

    if (stdFilter(lag+1)/avgFilter(lag+1)>0.1) then ! If the coefficient of variation exceeds 10%, the signal is too uneven at the start, possibly because of a peak.
        write(unit=*,fmt=1001)
1001        format(1X,'Warning: Peak detection might have failed, as there may be a peak at the edge of the frequency range.',/)
    end if
    do ii = lag+2, size(y)
        if (abs(y(ii) - avgFilter(ii-1)) > threshold * stdFilter(ii-1)) then
            ! Find only the largest outstanding value which is only the one greater than its predecessor and its successor
            if (y(ii) > avgFilter(ii-1) .AND. y(ii) > y(ii-1) .AND. y(ii) > y(ii+1)) then
                PeakDetect(ii) = 1
            end if
            filteredY(ii) = influence * y(ii) + (1 - influence) * filteredY(ii-1)
        else
            filteredY(ii) = y(ii)
        end if
        ! Modified with respect to the original code. Mean and standard deviation are calculted symmetrically around the current point
        avgFilter(ii) = mean(filteredY(ii-lag:ii+lag))
        stdFilter(ii) = std(filteredY(ii-lag:ii+lag))
    end do
end function PeakDetect

real function mean(y)
    !> @brief Calculates the mean of vector y
    implicit none
    ! Declaring part
    real, dimension(:), intent(in) :: y
    integer :: N
    ! Executing part
    N = max(1,size(y))
    mean = sum(y)/N
end function mean

real function std(y)
    !> @brief Calculates the standard deviation of vector y
    implicit none
    ! Declaring part
    real, dimension(:), intent(in) :: y
    integer :: N
    ! Executing part
    N = max(1,size(y))
    std = sqrt((N*dot_product(y,y) - sum(y)**2) / (N*(N-1)))
end function std

내 응용 프로그램의 알고리즘은 매력처럼 작동합니다! 여기에 이미지 설명을 입력하십시오


3

데이터베이스 테이블에 데이터가있는 경우 간단한 z- 점수 알고리즘의 SQL 버전은 다음과 같습니다.

with data_with_zscore as (
    select
        date_time,
        value,
        value / (avg(value) over ()) as pct_of_mean,
        (value - avg(value) over ()) / (stdev(value) over ()) as z_score
    from {{tablename}}  where datetime > '2018-11-26' and datetime < '2018-12-03'
)


-- select all
select * from data_with_zscore 

-- select only points greater than a certain threshold
select * from data_with_zscore where z_score > abs(2)

귀하의 코드는 내가 제안한 알고리즘 이외의 작업을 수행합니다. 쿼리는 단순히 z- 점수 ([데이터 포인트-평균] / std)를 계산하지만 새로운 신호 임계 값을 계산할 때 과거 신호를 무시하는 알고리즘의 논리를 포함하지 않습니다. 또한 세 가지 매개 변수 (지연, 영향, 임계 값)를 무시합니다. 실제 논리를 통합하기 위해 답변을 수정할 수 있습니까?
Jean-Paul

1
그래, 너가 맞아. 처음에는 위의 단순화 된 버전으로 벗어날 수 있다고 생각했습니다. 그 이후로 전체 솔루션을 가져 와서 C #으로 이식했습니다. 아래 답변을 참조하십시오. 시간이 더 있으면이 SQL 버전을 다시 방문하여 알고리즘을 통합합니다. 그건 그렇고, 훌륭한 답변과 시각적 설명에 감사드립니다.
Ocean Airdrop

문제없이 알고리즘이 도움이 될 수 있습니다. C # 제출에 여전히 감사합니다. 번역 목록에 추가하겠습니다!
Jean-Paul

3

실시간 스트림에서 작동하는 Python 버전 (각 새로운 데이터 포인트가 도착할 때 모든 데이터 포인트를 다시 계산하지는 않음). 클래스 함수가 ​​반환하는 것을 조정하고 싶을 수도 있습니다. 내 목적을 위해 신호가 필요했습니다.

import numpy as np

class real_time_peak_detection():
    def __init__(self, array, lag, threshold, influence):
        self.y = list(array)
        self.length = len(self.y)
        self.lag = lag
        self.threshold = threshold
        self.influence = influence
        self.signals = [0] * len(self.y)
        self.filteredY = np.array(self.y).tolist()
        self.avgFilter = [0] * len(self.y)
        self.stdFilter = [0] * len(self.y)
        self.avgFilter[self.lag - 1] = np.mean(self.y[0:self.lag]).tolist()
        self.stdFilter[self.lag - 1] = np.std(self.y[0:self.lag]).tolist()

    def thresholding_algo(self, new_value):
        self.y.append(new_value)
        i = len(self.y) - 1
        self.length = len(self.y)
        if i < self.lag:
            return 0
        elif i == self.lag:
            self.signals = [0] * len(self.y)
            self.filteredY = np.array(self.y).tolist()
            self.avgFilter = [0] * len(self.y)
            self.stdFilter = [0] * len(self.y)
            self.avgFilter[self.lag - 1] = np.mean(self.y[0:self.lag]).tolist()
            self.stdFilter[self.lag - 1] = np.std(self.y[0:self.lag]).tolist()
            return 0

        self.signals += [0]
        self.filteredY += [0]
        self.avgFilter += [0]
        self.stdFilter += [0]

        if abs(self.y[i] - self.avgFilter[i - 1]) > self.threshold * self.stdFilter[i - 1]:
            if self.y[i] > self.avgFilter[i - 1]:
                self.signals[i] = 1
            else:
                self.signals[i] = -1

            self.filteredY[i] = self.influence * self.y[i] + (1 - self.influence) * self.filteredY[i - 1]
            self.avgFilter[i] = np.mean(self.filteredY[(i - self.lag):i])
            self.stdFilter[i] = np.std(self.filteredY[(i - self.lag):i])
        else:
            self.signals[i] = 0
            self.filteredY[i] = self.y[i]
            self.avgFilter[i] = np.mean(self.filteredY[(i - self.lag):i])
            self.stdFilter[i] = np.std(self.filteredY[(i - self.lag):i])

        return self.signals[i]

게시 해 주셔서 감사합니다. 번역 내용을 목록에 추가했습니다.
Jean-Paul

3

나는 자바 스크립트 버전을 만들 수있었습니다. 도움이 될 수 있습니다. 자바 스크립트는 위에 제공된 의사 코드의 직접적인 전사 여야합니다. npm 패키지 및 github 저장소로 사용 가능 :

자바 스크립트 번역 :

// javascript port of: /programming/22583391/peak-signal-detection-in-realtime-timeseries-data/48895639#48895639

function sum(a) {
    return a.reduce((acc, val) => acc + val)
}

function mean(a) {
    return sum(a) / a.length
}

function stddev(arr) {
    const arr_mean = mean(arr)
    const r = function(acc, val) {
        return acc + ((val - arr_mean) * (val - arr_mean))
    }
    return Math.sqrt(arr.reduce(r, 0.0) / arr.length)
}

function smoothed_z_score(y, params) {
    var p = params || {}
    // init cooefficients
    const lag = p.lag || 5
    const threshold = p.threshold || 3.5
    const influence = p.influece || 0.5

    if (y === undefined || y.length < lag + 2) {
        throw ` ## y data array to short(${y.length}) for given lag of ${lag}`
    }
    //console.log(`lag, threshold, influence: ${lag}, ${threshold}, ${influence}`)

    // init variables
    var signals = Array(y.length).fill(0)
    var filteredY = y.slice(0)
    const lead_in = y.slice(0, lag)
    //console.log("1: " + lead_in.toString())

    var avgFilter = []
    avgFilter[lag - 1] = mean(lead_in)
    var stdFilter = []
    stdFilter[lag - 1] = stddev(lead_in)
    //console.log("2: " + stdFilter.toString())

    for (var i = lag; i < y.length; i++) {
        //console.log(`${y[i]}, ${avgFilter[i-1]}, ${threshold}, ${stdFilter[i-1]}`)
        if (Math.abs(y[i] - avgFilter[i - 1]) > (threshold * stdFilter[i - 1])) {
            if (y[i] > avgFilter[i - 1]) {
                signals[i] = +1 // positive signal
            } else {
                signals[i] = -1 // negative signal
            }
            // make influence lower
            filteredY[i] = influence * y[i] + (1 - influence) * filteredY[i - 1]
        } else {
            signals[i] = 0 // no signal
            filteredY[i] = y[i]
        }

        // adjust the filters
        const y_lag = filteredY.slice(i - lag, i)
        avgFilter[i] = mean(y_lag)
        stdFilter[i] = stddev(y_lag)
    }

    return signals
}

module.exports = smoothed_z_score

번역을 게시 해 주셔서 감사합니다. 사람들이 신속하게 볼 수 있도록 답변에 코드를 추가했습니다. 번역을 목록에 추가하겠습니다.
Jean-Paul

지금까지 다른 알고리즘을 자바 스크립트로 이식했습니다. 이번에는 Numercial pyhon에서 나에게 더 많은 통제력을 부여하고 나에게 더 잘 작동합니다. 또한 npm에 패키지되어 있으며 워싱턴 주립 대학의 주피터 페이지에서 알고에 대한 자세한 정보를 찾을 수 있습니다. npmjs.com/package/@joe_six/duarte-watanabe-peak-detection
Dirk Lüsebrink

2

경계 값이나 다른 기준이 미래 가치에 의존한다면, 유일한 해결책은 (시간 기계 나 미래 가치에 대한 다른 지식이없는) 미래 가치가 충분할 때까지 결정을 연기하는 것입니다. 예를 들어 20 포인트에 이르는 평균 이상의 레벨을 원하면 피크 결정보다 최소 19 포인트 앞당길 때까지 기다려야합니다. .

다음 플롯이 1e99가 아니라는 것을 미리 알지 않는 한 현재 플롯에는 피크가 없습니다. 플롯의 Y 치수를 재조정 한 후 해당 포인트까지 평평해질 것입니다.


앞서 말했듯이 피크가 발생하면 피크가 그림의 피크만큼 크며 '정상적인'값과 크게 차이가 있다고 가정 할 수 있습니다.
Jean-Paul

피크가 얼마나 큰지 미리 알고 있다면 평균 및 / 또는 임계 값을 해당 값 바로 아래로 미리 설정하십시오.
hotpaw2

1
그리고 그것은 내가 미리 알지 못하는 것입니다.
Jean-Paul

1
당신은 방금 자신과 모순되고 봉우리가 그림의 크기로 알려져 있다고 썼습니다. 당신은 그것을 알거나 알지 못합니다.
hotpaw2

2
나는 당신에게 그것을 설명하려고합니다. 지금 아이디어를 얻었습니까? '큰 피크를 식별하는 방법'. 통계적으로 또는 스마트 알고리즘을 사용하여 문제에 접근 할 수 있습니다. 로 .. As large as in the pictureI 의미 : 중요한 봉우리와 기본 노이즈가 비슷한 상황.
Jean-Paul

2

ZSCORE 알고리즘 의 PHP 구현 은 다음과 같습니다.

<?php
$y = array(1,7,1.1,1,0.9,1,1,1.1,1,0.9,1,1.1,1,1,0.9,1,1,1.1,1,1,1,1,1.1,0.9,1,1.1,1,1,0.9,
       1,1.1,1,1,1.1,1,0.8,0.9,1,1.2,0.9,1,1,1.1,1.2,1,1.5,10,3,2,5,3,2,1,1,1,0.9,1,1,3,
       2.6,4,3,3.2,2,1,1,0.8,4,4,2,2.5,1,1,1);

function mean($data, $start, $len) {
    $avg = 0;
    for ($i = $start; $i < $start+ $len; $i ++)
        $avg += $data[$i];
    return $avg / $len;
}

function stddev($data, $start,$len) {
    $mean = mean($data,$start,$len);
    $dev = 0;
    for ($i = $start; $i < $start+$len; $i++) 
        $dev += (($data[$i] - $mean) * ($data[$i] - $mean));
    return sqrt($dev / $len);
}

function zscore($data, $len, $lag= 20, $threshold = 1, $influence = 1) {

    $signals = array();
    $avgFilter = array();
    $stdFilter = array();
    $filteredY = array();
    $avgFilter[$lag - 1] = mean($data, 0, $lag);
    $stdFilter[$lag - 1] = stddev($data, 0, $lag);

    for ($i = 0; $i < $len; $i++) {
        $filteredY[$i] = $data[$i];
        $signals[$i] = 0;
    }


    for ($i=$lag; $i < $len; $i++) {
        if (abs($data[$i] - $avgFilter[$i-1]) > $threshold * $stdFilter[$lag - 1]) {
            if ($data[$i] > $avgFilter[$i-1]) {
                $signals[$i] = 1;
            }
            else {
                $signals[$i] = -1;
            }
            $filteredY[$i] = $influence * $data[$i] + (1 - $influence) * $filteredY[$i-1];
        } 
        else {
            $signals[$i] = 0;
            $filteredY[$i] = $data[$i];
        }

        $avgFilter[$i] = mean($filteredY, $i - $lag, $lag);
        $stdFilter[$i] = stddev($filteredY, $i - $lag, $lag);
    }
    return $signals;
}

$sig = zscore($y, count($y));

print_r($y); echo "<br><br>";
print_r($sig); echo "<br><br>";

for ($i = 0; $i < count($y); $i++) echo $i. " " . $y[$i]. " ". $sig[$i]."<br>";

?>

게시 해 주셔서 감사합니다. 번역을 목록에 추가했습니다.
장 폴

1
한 의견은이 알고리즘은 대부분 샘플링 된 데이터에 사용됩니다 주어, 나는 당신이 구현 제안 표본 표준 편차 로 나누어 ($len - 1)대신 $lenstddev()
장 폴

1

최대 값을 평균과 비교하는 대신, 최소값이 잡음 임계 값 이상으로 만 정의되는 인접한 최소값과 최대 값을 비교할 수도 있습니다. 로컬 최대 값이 인접한 최소값보다 3 배 이상 (또는 다른 신뢰 계수) 인 경우 해당 최대 값은 피크입니다. 더 넓은 이동 창에서 피크 결정이 더 정확합니다. 위의 방법은 창 끝의 계산 (= = 지연) 대신 창 중간을 중심으로 한 계산을 사용합니다.

최대 값은 이전의 신호 증가와 이후의 감소로 간주되어야합니다.


1

scipy.signal.find_peaks이름에서 알 수 있듯이 함수 가이 기능에 유용합니다. 그러나 잘 매개 변수를 이해하는 것이 중요하다 width, threshold, distance 그리고 무엇보다도prominence 좋은 피크 추출을 얻을 수 있습니다.

내 테스트와 문서에 따르면, 저명한 개념은 좋은 피크를 유지하고 시끄러운 피크를 버리는 "유용한 개념"입니다.

(토포 그래피) 눈에 띄는 것은 무엇입니까 ? 그것은이다 "필요한 최소한의 높이가 어느 높은 지형 정상 회담에서 얻을 하강" 가 여기 볼 수 있듯이, :

아이디어는 다음과 같습니다.

중요도가 높을수록 피크가 더 "중요"합니다.


1

mordern C +++를 사용하는 객체 지향 버전의 z- 점수 알고리즘

template<typename T>
class FindPeaks{
private:
    std::vector<T> m_input_signal;                      // stores input vector
    std::vector<T> m_array_peak_positive;               
    std::vector<T> m_array_peak_negative;               

public:
    FindPeaks(const std::vector<T>& t_input_signal): m_input_signal{t_input_signal}{ }

    void estimate(){
        int lag{5};
        T threshold{ 5 };                                                                                       // set a threshold
        T influence{ 0.5 };                                                                                    // value between 0 to 1, 1 is normal influence and 0.5 is half the influence

        std::vector<T> filtered_signal(m_input_signal.size(), 0.0);                                             // placeholdered for smooth signal, initialie with all zeros
        std::vector<int> signal(m_input_signal.size(), 0);                                                          // vector that stores where the negative and positive located
        std::vector<T> avg_filtered(m_input_signal.size(), 0.0);                                                // moving averages
        std::vector<T> std_filtered(m_input_signal.size(), 0.0);                                                // moving standard deviation

        avg_filtered[lag] = findMean(m_input_signal.begin(), m_input_signal.begin() + lag);                         // pass the iteartor to vector
        std_filtered[lag] = findStandardDeviation(m_input_signal.begin(), m_input_signal.begin() + lag);

        for (size_t iLag = lag + 1; iLag < m_input_signal.size(); ++iLag) {                                         // start index frm 
            if (std::abs(m_input_signal[iLag] - avg_filtered[iLag - 1]) > threshold * std_filtered[iLag - 1]) {     // check if value is above threhold             
                if ((m_input_signal[iLag]) > avg_filtered[iLag - 1]) {
                    signal[iLag] = 1;                                                                               // assign positive signal
                }
                else {
                    signal[iLag] = -1;                                                                                  // assign negative signal
                }
                filtered_signal[iLag] = influence * m_input_signal[iLag] + (1 - influence) * filtered_signal[iLag - 1];        // exponential smoothing
            }
            else {
                signal[iLag] = 0;                                                                                         // no signal
                filtered_signal[iLag] = m_input_signal[iLag];
            }

            avg_filtered[iLag] = findMean(filtered_signal.begin() + (iLag - lag), filtered_signal.begin() + iLag);
            std_filtered[iLag] = findStandardDeviation(filtered_signal.begin() + (iLag - lag), filtered_signal.begin() + iLag);

        }

        for (size_t iSignal = 0; iSignal < m_input_signal.size(); ++iSignal) {
            if (signal[iSignal] == 1) {
                m_array_peak_positive.emplace_back(m_input_signal[iSignal]);                                        // store the positive peaks
            }
            else if (signal[iSignal] == -1) {
                m_array_peak_negative.emplace_back(m_input_signal[iSignal]);                                         // store the negative peaks
            }
        }
        printVoltagePeaks(signal, m_input_signal);

    }

    std::pair< std::vector<T>, std::vector<T> > get_peaks()
    {
        return std::make_pair(m_array_peak_negative, m_array_peak_negative);
    }

};


template<typename T1, typename T2 >
void printVoltagePeaks(std::vector<T1>& m_signal, std::vector<T2>& m_input_signal) {
    std::ofstream output_file("./voltage_peak.csv");
    std::ostream_iterator<T2> output_iterator_voltage(output_file, ",");
    std::ostream_iterator<T1> output_iterator_signal(output_file, ",");
    std::copy(m_input_signal.begin(), m_input_signal.end(), output_iterator_voltage);
    output_file << "\n";
    std::copy(m_signal.begin(), m_signal.end(), output_iterator_signal);
}

template<typename iterator_type>
typename std::iterator_traits<iterator_type>::value_type findMean(iterator_type it, iterator_type end)
{
    /* function that receives iterator to*/
    typename std::iterator_traits<iterator_type>::value_type sum{ 0.0 };
    int counter = 0;
    while (it != end) {
        sum += *(it++);
        counter++;
    }
    return sum / counter;
}

template<typename iterator_type>
typename std::iterator_traits<iterator_type>::value_type findStandardDeviation(iterator_type it, iterator_type end)
{
    auto mean = findMean(it, end);
    typename std::iterator_traits<iterator_type>::value_type sum_squared_error{ 0.0 };
    int counter{ 0 };
    while (it != end) {
        sum_squared_error += std::pow((*(it++) - mean), 2);
        counter++;
    }
    auto standard_deviation = std::sqrt(sum_squared_error / (counter - 1));
    return standard_deviation;
}

2
좋은 번역. 이 개체는 또한 저장하면 약간 더 좋은 것 filtered_signal, signal, avg_filteredstd_filtered같은 개인 변수와 만 배열 업데이트 한 번 새로운 데이터 포인트가 도착하면 (지금은 모든 데이터 포인트 상에 코드 루프가 호출 될 때마다 참조). 이는 코드 성능을 향상시키고 OOP 구조에 더 적합합니다.
Jean-Paul
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.