Gerçek zamanlı zamanlama verilerinde tepe sinyali tespiti


242

Güncelleme: en iyi performans gösteren algoritma şu ana kadar bu biridir .


Bu soru, gerçek zamanlı zaman verileri verilerinde ani tepeleri tespit etmek için sağlam algoritmaları araştırmaktadır.

Aşağıdaki veri kümesini düşünün:

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];

(Matlab formatı ancak dil ile ilgili değil, algoritma ile ilgili)

Veri grafiği

Üç büyük tepe ve bazı küçük tepe noktaları olduğunu açıkça görebilirsiniz. Bu veri kümesi, sorunun söz konusu olduğu zaman çizelgeleri veri kümelerinin özel bir örneğidir. Bu veri kümesi sınıfının iki genel özelliği vardır:

  1. Genel ortalama ile temel gürültü var
  2. Gürültüden önemli ölçüde sapan büyük ' zirveler ' veya ' daha yüksek veri noktaları ' vardır.

Ayrıca aşağıdakileri de varsayalım:

  • piklerin genişliği önceden belirlenemez
  • piklerin yüksekliği açıkça ve diğer değerlerden önemli ölçüde sapar
  • kullanılan algoritma gerçek zamanı hesaplamalıdır (bu yüzden her yeni veri noktasıyla değiştirin)

Böyle bir durum için, sinyalleri tetikleyen bir sınır değerinin oluşturulması gerekir. Ancak, sınır değeri statik olamaz ve bir algoritmaya dayalı olarak gerçek zamanlı olarak belirlenmelidir.


Sorum: Bu eşikleri gerçek zamanlı olarak hesaplamak için iyi bir algoritma nedir? Bu gibi durumlar için özel algoritmalar var mı? En iyi bilinen algoritmalar nelerdir?


Sağlam algoritmalar veya faydalı bilgiler çok takdir edilmektedir. (herhangi bir dilde cevap verebilir: algoritma ile ilgilidir)


5
Daha önce vermiş olduğunuz gerekliliklere ek olarak, tepe noktası olabilmek için mutlak yükseklik gereksinimi olmalıdır . Aksi takdirde, 13. zamandaki tepe noktası bir tepe noktası sayılmalıdır. (Eşdeğer olarak: gelecekte, zirveler 1000'e kadar
çıkmışsa

Katılıyorum. Diyelim ki bu zirveler sadece dikkate almamız gerekenlerden.
Jean-Paul

Yanlış soruyu soruyor olabilirsiniz. Gecikmeden nasıl tespit edebileceğinizi sormak yerine, yalnızca o zamandan önce bilinen veya belirli bir sinyalle ilgili bir şeyi tespit etmek için bir sinyal hakkında bilinmesi gereken belirli bir tür sinyali gecikmeden tespit etmenin mümkün olup olmadığını sorabilirsiniz. gecikme.
hotpaw2

2
Bunu bir fotosensörde ani bir ışık yoğunluğu değişikliğini tespit etmek için yapardım. Bunu ortalamayı hareket ettirerek ve bir eşik değerden daha büyük veri noktalarını yoksayarak yaptım . Bu eşiğin, bir zirve belirleyen eşikten farklı olduğuna dikkat edin. Bu nedenle, yalnızca hareketli ortalamanıza bir stddev içinde olan veri noktalarını eklediğinizi ve üçten fazla stddev içeren veri noktalarını zirve olarak değerlendirdiğinizi varsayalım. Bu algoritma o zamanki uygulama bağlamımız için çok başarılı oldu.
adalet

1
Ah, anlıyorum. Kod biçiminde beklemiyordum. Bu soruyu daha önce görmüş olsaydım muhtemelen bu cevabı çok daha hızlı alırdınız = D. Her neyse, o zamanki uygulamam, fotosensörün ortam ışık kaynağından engellenip engellenmediğini tespit etmekti (bu nedenle, ortam ışık kaynağı zaman içinde kademeli olarak değişebileceğinden hareketli ortalamaya ihtiyacımız var). Bunu, belirli bir deseni izleyerek elinizi sensörlerin üzerine getirmeniz gereken bir oyun olarak yarattık. = D
Mart'ta adalet

Yanıtlar:


334

Sağlam tepe algılama algoritması (z skorları kullanarak)

Bu tür veri kümeleri için çok iyi çalışan bir algoritma buldum. Dispersiyon prensibine dayanır : eğer yeni bir veri noktası belirli bir hareketli ortalamadan x verilen standart sapma sayısı ise, algoritma sinyalleri ( z-skoru olarak da adlandırılır ). Algoritma çok sağlamdır, çünkü sinyaller eşiği bozmayacak şekilde ayrı bir hareketli ortalama ve sapma oluşturur. Dolayısıyla gelecekteki sinyaller, önceki sinyallerin miktarına bakılmaksızın yaklaşık olarak aynı doğrulukla tanımlanır. Algoritma 3 giriş alır: lag = the lag of the moving window, threshold = the z-score at which the algorithm signalsve influence = the influence (between 0 and 1) of new signals on the mean and standard deviation. Örneğin, lag5'ten bir tanesi verileri düzeltmek için son 5 gözlemi kullanacaktır. birthresholdEğer bir veri noktası hareketli ortalamadan 3.5 standart sapma ise 3.5 değeri sinyal verir. Ve influence0.5 değeri yarısına sinyaller verir ); Etki seçeneğini 1 olarak koymak en az sağlamdır. Durağan olmayan veriler için, etki seçeneği 0 ile 1 arasında bir yere konulmalıdır.normal veri noktalarının etkisi. Benzer şekilde, influence0'dan bir tanesi yeni eşiği yeniden hesaplamak için sinyalleri tamamen göz ardı eder. Bu nedenle 0 etkisi en sağlam seçenektir (ancak durağanlığı varsayar)

Aşağıdaki gibi çalışır:

pseudocode

# 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

Verileriniz için iyi parametreler seçmek için temel kuralları aşağıda bulabilirsiniz.


gösteri

Güçlü eşik algoritmasının gösterilmesi

Bu demo için Matlab kodu burada bulunabilir . Demoyu kullanmak için, çalıştırın ve üstteki grafiğe tıklayarak kendiniz bir zaman serisi oluşturun. Algoritma lag, gözlem sayısının çizilmesinden sonra çalışmaya başlar .


Sonuç

Orijinal soru için, bu algoritma aşağıdaki ayarları kullanırken aşağıdaki çıktıyı verecektir lag = 30, threshold = 5, influence = 0:

Eşikleme algoritması örneği


Farklı programlama dillerindeki uygulamalar:


Algoritmayı yapılandırmak için temel kurallar

lag: lag parametresi, verilerinizin ne kadar düzgünleştirileceğini ve algoritmanın verilerin uzun vadeli ortalamasındaki değişikliklere ne kadar uyarlanabilir olacağını belirler. Verileriniz ne kadar durağan olursa, o kadar fazla gecikme eklemeniz gerekir (bu algoritmanın sağlamlığını geliştirmelidir). Verileriniz zamanla değişen eğilimler içeriyorsa, algoritmanın bu eğilimlere ne kadar hızlı adapte olmasını istediğinizi göz önünde bulundurmalısınız. Yani, lag10'a koyarsanız , algoritmanın eşiği uzun vadeli ortalamadaki herhangi bir sistematik değişikliğe ayarlanmadan önce 10 'periyot' sürer. Bu nedenle lag, verilerinizin trend davranışına ve algoritmanın ne kadar uyarlanabilir olmasını istediğinize bağlı olarak parametreyi seçin .

influence: bu parametre sinyallerin algoritmanın algılama eşiği üzerindeki etkisini belirler. 0'a getirilirse, sinyallerin eşik üzerinde hiçbir etkisi yoktur, böylece gelecekteki sinyaller, geçmiş sinyallerden etkilenmeyen bir ortalama ve standart sapma ile hesaplanan bir eşik temelinde tespit edilir. Bunu düşünmenin başka bir yolu da, etkiyi 0'a koyarsanız, dolaylı olarak durağanlığı kabul edersiniz (yani, kaç sinyal olursa olsun, zaman serisi uzun vadede her zaman aynı ortalamaya geri döner). Aksi takdirde, sinyallerin verilerin zaman içinde değişen eğilimini sistematik olarak ne ölçüde etkileyebileceğine bağlı olarak, etki parametresini 0 ile 1 arasında bir yere koymalısınız. Örneğin, sinyaller yapısal bir kopmaya yol açarsa zaman serisinin uzun vadeli ortalamasında, etki parametresi yüksek (1'e yakın) konulmalıdır, böylece eşik bu değişikliklere hızlı bir şekilde uyum sağlayabilir.

threshold: eşik parametresi, algoritmanın yeni bir veri noktasını bir sinyal olarak sınıflandıracağı hareketli ortalamadan standart sapmaların sayısıdır. Örneğin, yeni bir veri noktası hareketli ortalamanın 4.0 standart sapması ise ve eşik parametresi 3.5 olarak ayarlanırsa, algoritma veri noktasını bir sinyal olarak tanımlar. Bu parametre, kaç sinyal beklediğinize göre ayarlanmalıdır. Örneğin, verileriniz normal olarak dağıtılırsa, 3,5'lik bir eşik (veya: z-puanı), 0.00047'lik ( bu tablodan)), yani her 2128 veri noktasında bir sinyal beklediğiniz anlamına gelir (1 / 0.00047). Bu nedenle eşik, algoritmanın ne kadar hassas olduğunu ve dolayısıyla algoritmanın ne sıklıkta sinyal verdiğini doğrudan etkiler. Kendi verilerinizi inceleyin ve istediğiniz zaman algoritma sinyalini veren mantıklı bir eşik belirleyin (amacınız için iyi bir eşik değere ulaşmak için burada bazı deneme yanılma gerekebilir).


UYARI: Yukarıdaki kod her çalıştığında tüm veri noktalarının üzerinden geçer. Bu kodu uygularken, sinyalin hesaplamasını ayrı bir işleve (döngü olmadan) bölmeyi unutmayın. Sonra yeni bir veri noktası geldiğinde, güncelleme filteredY, avgFilterve stdFilterbir kez. Her zaman yeni bir veri noktası (yukarıdaki örnekte olduğu gibi) olduğunda, son derece verimsiz ve yavaş olacak şekilde tüm veriler için sinyalleri yeniden hesaplamayın!

Algoritmayı değiştirmenin diğer yolları (potansiyel iyileştirmeler için):

  1. Ortalama yerine medyan kullanın
  2. Standart sapma yerine MAD gibi sağlam bir ölçek ölçüsü kullanın
  3. Sinyal marjı kullanın, böylece sinyal çok sık değişmez
  4. İnfluence parametresinin çalışma şeklini değiştirme
  5. Tedavi yukarı ve aşağı farklı sinyalleri (asimetrik tedavi)
  6. influenceOrtalama ve std için ayrı bir parametre oluşturun ( bu Swift çevirisinde yapıldığı gibi )

Bu StackOverflow yanıtına (bilinen) akademik atıflar:

Algoritmayı kullanan diğer çalışmalar

Bu algoritmanın diğer uygulamaları

Diğer tepe algılama algoritmalarına bağlantılar


Bu işlevi bir yerde kullanırsanız, lütfen bana veya bu cevaba kredi verin. Bu algoritma ile ilgili herhangi bir sorunuz varsa, bunları aşağıdaki yorumlara gönderin veya LinkedIn üzerinden bana ulaşın .




@reasra Yeniden yazdıktan sonra işlevin hareketli bir standart sapmaya ihtiyaç duymadığı ortaya çıkıyor. Artık basit yerleşik Matlab işlevleriyle kullanılabilir :)
Jean-Paul

1
Bazı ivmeölçer verileri için Matlab kodunu deniyorum, ancak bir nedenden dolayı thresholdgrafik, verilerde 20'ye kadar büyük bir artıştan sonra düz yeşil bir çizgi haline geliyor ve grafiğin geri kalanı için böyle kalıyor ... Dönüşü kaldırırım, bu gerçekleşmez, bu yüzden verilerdeki artıştan kaynaklanıyor gibi görünüyor. Neler olabileceğine dair bir fikrin var mı? Matlab'da yeniyim, bu yüzden
Magnus W

@BadCash (Verilerle) bir örnek verebilir misiniz? Belki burada SO üzerinde kendi sorunuzu sorun ve bana bağlantı söyle?
Jean-Paul

2
Bu algoyu geliştirmenin birçok yolu vardır, bu yüzden yaratıcı olun (farklı tedavi yukarı / aşağı; ortalama yerine medyan; sağlam std; kodu bellek verimli bir işlev olarak yazma; eşik marjı, böylece sinyal çok sık değişmez, vb. .).
Jean-Paul

41

İşte düzgünleştirilmiş z-skor algoritmasının Python/ numpyuygulaması ( yukarıdaki cevaba bakınız ). Buradaki özü burada bulabilirsiniz .

#!/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))

Aşağıda, R/ için orijinal yanıttakiyle aynı grafiği veren aynı veri kümesindeki testtir.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()

Burada 'y' aslında sinyal ve 'sinyaller' veri noktaları kümesidir, anlamada doğru muyum?
TheTank

1
@TheTank yyönteme geçen veri dizisi olan, signalsbir +1ya da -1her bir veri noktası için işaret çıktı dizisi y[i]bu veri noktası kullanmak ayarları belirli bir "önemli zirve" olup olmadığı.
Jean-Paul

23

Bir yaklaşım, aşağıdaki gözlem temelinde zirveleri tespit etmektir:

  • (Y (t)> y (t-1)) && (y (t)> y (t + 1)) ise t zamanı bir pik olur

Yükseliş trendi bitene kadar bekleyerek yanlış pozitifleri önler. Zirveyi bir dt kadar özleyeceği anlamında tam olarak "gerçek zamanlı" değildir. duyarlılık, karşılaştırma için bir marj gerektirerek kontrol edilebilir. Gürültülü algılama ile algılamanın zaman gecikmesi arasında bir denge vardır. Daha fazla parametre ekleyerek modeli zenginleştirebilirsiniz:

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

burada dt ve m , zaman gecikmesine karşı hassasiyeti kontrol eden parametrelerdir

Bahsedilen algoritma ile elde ettiğiniz şey budur: resim açıklamasını buraya girin

python'da çizimi yeniden oluşturmak için kod:

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

Ayarlayarak m = 0.5, yalnızca bir yanlış pozitif ile daha temiz bir sinyal alabilirsiniz: resim açıklamasını buraya girin


Daha önce = daha iyi, böylece tüm zirveler önemli. Teşekkürler! Çok havalı!
Jean-Paul

Hassasiyeti nasıl değiştirebilirim?
Jean-Paul

İki yaklaşımı düşünebilirim: 1: m'yi daha büyük bir değere ayarlayın, böylece sadece daha büyük pikler algılanır. 2: y (t) - y (t-dt) (ve y (t) - y (t + dt)) hesaplamak yerine, t-dt'den t'ye (ve t'den t + dt'ye) entegre olursunuz.
aha

2
Diğer 7 zirveyi hangi kriterlerle reddediyorsunuz?
hotpaw2

4
Düz piklerle ilgili bir sorun var, çünkü yaptığınız temel olarak 1-B kenar algılama (sinyali [1 0 -1] ile
ben

18

Sinyal işlemede pik tespiti genellikle dalgacık dönüşümü ile yapılır. Temel olarak zaman serisi verilerinizde ayrı bir dalgacık dönüşümü yaparsınız. Döndürülen ayrıntı katsayılarındaki sıfır geçişler, zaman serisi sinyalindeki piklere karşılık gelecektir. Farklı ayrıntı katsayısı seviyelerinde algılanan farklı tepe genliklerine sahip olursunuz, bu da size çok seviyeli çözünürlük sağlar.


1
Yanıtınız için bana izin bu makalede ve bu cevap benim uygulanması için iyi bir algoritma oluşturmak yardımcı olacaktır. Teşekkürler!
Jean-Paul

@cklin Dalgacık katsayılarının sıfır geçişlerini nasıl hesapladığınızı açıklayabilir misiniz, çünkü bunlar orijinal zaman serileriyle aynı zaman ölçeğinde değildir. Bu kullanım hakkında herhangi bir referans var mı?
horaceT

11

Veri setimizde yumuşatılmış z skoru algoritmasını kullanmaya çalıştık, bu da orta hassasiyetle aşırı duyarlılık veya düşük hassasiyet (parametrelerin nasıl ayarlandığına bağlı olarak) ile sonuçlanıyor. Sitemizin trafik sinyalinde, günlük çevrimi temsil eden düşük bir frekans taban çizgisi gözlemledik ve mümkün olan en iyi parametrelerle (aşağıda gösterilmiştir) bile, özellikle 4. günde izlendi, çünkü veri noktalarının çoğu anomali olarak kabul edildi .

Orijinal z-skor algoritmasının üzerine inşa ederek, bu sorunu ters filtreleme ile çözmenin bir yolunu bulduk. Değiştirilmiş algoritmanın detayları ve TV ticari trafik ilişkilendirmesine uygulanması takım blogumuzda yayınlanmaktadır .

resim açıklamasını buraya girin


Algoritmanın daha gelişmiş sürümünüz için bir başlangıç ​​noktası olduğunu görmek güzel. Verileriniz çok özel bir örüntüye sahiptir, bu nedenle öncelikle başka bir teknik kullanarak örüntüyü kaldırmak ve daha sonra algo'yu artıklara uygulamak daha mantıklı olacaktır. Alternatif olarak, ortalama / st.dev değerini hesaplamak için gecikmeli bir pencere yerine ortalanmış kullanmak isteyebilirsiniz. Başka bir yorum: çözümünüz sivri uçları belirlemek için sağdan sola hareket eder, ancak bu gerçek zamanlı uygulamalarda mümkün değildir (bu yüzden orijinal algo çok basittir, çünkü gelecekteki bilgilere erişilemez).
Jean-Paul

10

Hesaplamalı topolojide kalıcı homoloji fikri verimli - sıralama sayıları kadar hızlı - bir çözüme yol açar. Sadece zirveleri tespit etmekle kalmaz, aynı zamanda zirvelerin "önemini" sizin için önemli olan zirveleri seçmenize izin veren doğal bir şekilde ölçür.

Algoritma özeti. 1 boyutlu bir ortamda (zaman serisi, gerçek değerli sinyal) algoritma aşağıdaki şekilde kolayca tanımlanabilir:

En kalıcı zirveler

Fonksiyon grafiğini (veya alt seviye setini) bir manzara olarak düşünün ve seviye sonsuzluğundan başlayarak (veya bu resimde 1.8) azalan bir su seviyesini düşünün. Seviye düşerken, yerel maksi adalarda açılır. Yerel minima'da bu adalar bir araya geliyor. Bu fikrin bir detayı, daha sonra ortaya çıkan adanın daha eski olan adaya birleştirilmesidir. Bir adanın "sürekliliği" doğum zamanı eksi ölüm zamanıdır. Mavi çubukların uzunlukları, bir zirvenin yukarıda belirtilen "önemi" olan kalıcılığı gösterir.

Verimlilik. İşlev değerleri sıralandıktan sonra doğrusal zamanda çalışan bir uygulamayı bulmak zor değildir - aslında tek, basit bir döngüdür. Dolayısıyla bu uygulama pratikte hızlı olmalı ve kolayca uygulanabilmelidir.

Referanslar. Tüm hikayenin bir yazımı ve kalıcı homolojiden (hesaplamalı cebir topolojisinde bir alan) motivasyona referanslar burada bulunabilir: https://www.sthu.org/blog/13-perstopology-peakdetection/index.html


Bu algoritma, örneğin scipy.signal.find_peaks'ten çok daha hızlı ve daha doğrudur. 1053896 veri noktalı "gerçek" bir zaman serisi için 137516 tepe noktası (% 13) tespit etti. Zirvelerin sırası (en önemlisi önce) en önemli zirvelerin çıkarılmasını sağlar. Her bir tepe noktasının başlangıcını, tepe noktasını ve sonunu sağlar. Gürültülü verilerle iyi çalışır.
vinh

Gerçek zamanlı verilerle, veri noktalarının her seferinde alındığı çevrimiçi bir algoritmayı kastediyorsunuz. Bir zirvenin önemi gelecekteki değerlerle belirlenebilir. Zaman karmaşıklığından çok fazla ödün vermeden geçmiş sonuçları değiştirerek algoritmayı çevrimiçi olacak şekilde genişletmek iyi olurdu.
S. Huber

9

GH Palshikar tarafından Zaman Serisinde Tepe Algılama için Basit Algoritmalar'da başka bir algoritma bulundu .

Algoritma şöyle gider:

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

Avantajları

  • Kağıt, tepe tespiti için 5 farklı algoritma sağlar
  • Algoritmalar ham zaman serisi verileri üzerinde çalışır (düzleştirme gerekmez)

Dezavantajları

  • Belirlenmesi zor kve hönceden
  • Zirveler düz olamaz (test verilerimdeki üçüncü zirve gibi)

Misal:

resim açıklamasını buraya girin


Aslında ilginç bir kağıt. S4, bence kullanmak için daha iyi bir işlev gibi görünüyor. Ancak daha da önemlisi k <i <Nk doğru olmadığında açıklığa kavuşturmaktır. Biri i = 0 için S1 (S2, ..) fonksiyonunu nasıl tanımlar? 2'ye bölmedim ve ilk işleneni görmezden geldim ve her biri için her iki işleneni de dahil ettim ama i <= k için solda daha az işlenen vardı sonra sağda
daniels_pa

8

İşte Golang'daki Smoothed z skor algoritmasının (yukarıda) bir uygulaması. Bir dilim olduğunu varsayar []int16(PCM 16bit örnekleri). Burada bir öz bulabilirsin .

/*
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 Her şeyin doğru olduğundan emin değilim, bu yüzden hatalar olabilir.
Xeoncross

1
Matlab / R'den demo örnek çıktısını kopyalamayı denediniz mi? Bu kalitenin iyi bir teyidi olmalıdır.
Jean-Paul

7

İşte bu cevaptan düzeltilmiş z-skor algoritmasının bir C ++ uygulaması

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
Uyarı: Bu uygulama aslında ortalama ve standart sapmayı hesaplamak için bir yöntem sağlamaz. C ++ 11 için kolay bir yöntem burada bulunabilir: stackoverflow.com/a/12405793/3250829
rayryeng

6

Bu sorun, karma / katıştırılmış sistemler dersinde karşılaştığım soruna benziyor, ancak bu, bir sensörden gelen giriş gürültülü olduğunda hataları tespit etmekle ilgiliydi. Sistemin gizli durumunu tahmin etmek / tahmin etmek için bir Kalman filtresi kullandık, ardından bir hatanın meydana gelme olasılığını belirlemek için istatistiksel analiz kullandık . Doğrusal sistemlerle çalışıyorduk, ancak doğrusal olmayan varyantlar var. Yaklaşımın şaşırtıcı bir şekilde uyarlanabilir olduğunu hatırlıyorum, ancak sistemin dinamiklerinin bir modelini gerektiriyordu.


Kalman filtresi ilginç, ancak amacım için uygun bir algoritma bulamıyorum. Yine de cevabı çok takdir ediyorum ve algoritmalardan herhangi birini öğrenip öğrenemeyeceğimi görmek için bunun gibi bazı zirve tespit kağıtlarına bakacağım . Teşekkürler!
Jean-Paul

6

C ++ Uygulaması

#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'un önerdiği çözümden sonra, algoritmasını 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;
    }
}

Örnek kullanım:

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
Hey, Jean-Paul. Şerefe. Evet, çıktıyı eşleştiğinden emin olmak için R sürümünüze göre test ettim. Bu soruna çözümünüz için tekrar teşekkürler.
Ocean Airdrop

Merhaba, ben bu kodda bir hata olduğunu düşünüyorum, StdDev yönteminde değerleri almak.Count () - 1, -1 güvenmelidir? Öğelerin sayısını istediğinizi düşünüyorum ve değerlerden aldığınız şey budur.Count ().
Viktor

1
Hmm .. Güzel yer. Başlangıçta algoritmayı C # 'a taşıdım, ancak onu hiç kullanmadım. Muhtemelen bu işlevi nuget kütüphanesi MathNet çağrısı ile değiştiririm. "Install-Package MathNet.Numerics" PopulationStandardDeviation () ve StandardDeviation () için önceden oluşturulmuş işlevlere sahiptir; Örneğin. var nüfusStdDev = yeni Liste <çift> (1,2,3,4) .PopulationStandardDeviation (); var sampleStdDev = yeni Liste <çift> (1,2,3,4) .StandardDeviation ();
Ocean Airdrop

6

İşte ivmeölçer okumaları yapmak ve bir çarpma yönünün soldan veya sağdan gelip gelmediğine karar vermek için kullanılan Arduino mikrodenetleyici için @ Jean-Paul'ün Smoothed Z skorunun C uygulaması . Bu cihaz geri dönen bir sinyal döndürdüğü için bu gerçekten iyi bir performans sergiliyor. İşte cihazdan gelen bu zirve algılama algoritmasına giriş - sağdan ve ardından soldan gelen bir darbe. İlk dikeni, sonra sensörün salınımını görebilirsiniz.

resim açıklamasını buraya girin

#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;
}

Etkisi olan sonuç = 0

resim açıklamasını buraya girin

Büyük değil ama burada etkisi = 1

resim açıklamasını buraya girin

ki bu çok iyi.


5

İşte Groovy cevabına dayanan gerçek bir Java uygulaması daha önce yayınlanan . (Yayınlanmış Groovy ve Kotlin uygulamaları olduğunu biliyorum, ancak benim gibi sadece Java'yı yapan biri için, diğer diller ve Java arasında nasıl dönüştürüleceğini anlamak gerçekten zor bir iş).

(Sonuçlar başkalarının grafikleriyle eşleşir)

Algoritma uygulaması

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
}

Ana yöntem

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

Sonuçlar

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 yürütme verilerini ve sonuçlarını gösteren grafikler


5

Orijinal cevaba Ek 1: Matlabve Rçeviriler

Matlab kodu

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

Misal:

% 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 kodu

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

Misal:

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

Bu kod (her iki dil) orijinal sorunun verileri için aşağıdaki sonucu verecektir:

Matlab kodundan eşikleme örneği


Orijinal cevaba Ek 2: MatlabGösteri kodu

(veri oluşturmak için tıklayın)

Matlab demosu

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

Kabul edilen yanıttan "Smoothed z-score algo" için Ruby çözümü oluşturma girişimim:

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

Ve örnek kullanım:

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
# ]

Harika, paylaştığın için teşekkürler! Sizi listeye ekleyeceğim. Gerçek zamanlı uygulamalar için, yeni bir veri noktası geldiğinde sinyalleri güncellemek için ayrı bir işlev oluşturduğunuzdan emin olun (tüm veri noktalarını her seferinde döngü yapmak yerine).
Jean-Paul

4

Https://stackoverflow.com/a/22640362/6029703 cevabı için python / numpy'de yinelenen bir sürüm burada. Bu kod, büyük veriler (100000+) için her gecikmeyi hesaplamaktan ortalama ve standart sapmadan daha hızlıdır.

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'nın algoritmayı başkaları için uygulamasını sağlayacağımı düşündüm. Buradaki özeti burada bulabilirsiniz

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)

Sonuçlar


3

Düzgün z-skor algoritmasının Groovy (Java) uygulaması ( yukarıdaki cevaba bakını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)
 */

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

Aşağıda, yukarıdaki Python / numpy uygulamasıyla aynı sonuçları veren aynı veri kümesinde bir test bulunmaktadır .

    // 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

Düzgün z puanı skor algoritmasının (deyimsel olmayan) Scala versiyonu :

/**
  * 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
}

İşte Python ve Groovy sürümleriyle aynı sonuçları döndüren bir test:

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)

sonuç vegas şeması

Buraya gelin


1 zirveleri, -1 vadileri temsil eder.
Mike Roberts

3

Android projemde böyle bir şeye ihtiyacım vardı. Kotlin uygulamasını geri verebileceğimi düşündüm .

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

doğrulama grafikleri ile örnek proje github'da bulunabilir .

resim açıklamasını buraya girin


Müthiş! Paylaşım için teşekkürler. Gerçek zamanlı uygulamalar için, gelen her veri noktasıyla yeni sinyali hesaplayan ayrı bir işlev oluşturduğunuzdan emin olun. Yeni bir veri noktası her geldiğinde tam veri üzerinde döngü yapmayın, bu son derece verimsiz olur :)
Jean-Paul

1
İyi bir nokta, bunu düşünmedim, çünkü kullandığım pencereler örtüşmüyor.
leonardkraemer

3

İşte z skor algoritmasının değiştirilmiş bir Fortran versiyonu . Frekans uzayındaki transfer fonksiyonlarında pik (rezonans) tespiti için özel olarak değiştirilir (Her değişikliğin kodda küçük bir yorumu vardır).

İlk değişiklik, giriş vektörünün alt sınırının yakınında, belirli bir eşikten daha yüksek bir standart sapma (bu durumda% 10) ile gösterilen bir rezonans varsa kullanıcıya bir uyarı verir. Bu, sinyalin filtreleri düzgün şekilde başlatan algılama için yeterince düz olmadığı anlamına gelir.

İkinci değişiklik, bulunan zirvelere sadece bir zirvenin en yüksek değerinin ilave edilmesidir. Bu, bulunan her bir tepe değerinin (gecikme) öncüllerinin ve (gecikme) ardıllarının büyüklüğüyle karşılaştırılmasıyla elde edilir.

Üçüncü değişiklik, rezonans piklerinin genellikle rezonans frekansı etrafında bir tür simetri göstermesi gerektiğidir. Bu nedenle, ortalama ve std'yi mevcut veri noktası çevresinde simetrik olarak hesaplamak doğaldır (sadece öncekiler için değil). Bu, daha iyi bir tepe saptama davranışı ile sonuçlanır.

Değişiklikler, tüm sinyalin önceden fonksiyonla bilinmesi gereken etkiye sahiptir; bu, rezonans tespiti için olağan durumdur (veri noktalarının anında üretildiği Matlab Örneği Matlab Örneği gibi bir şey çalışmaz).

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

Benim uygulama için algoritma bir cazibe gibi çalışır! resim açıklamasını buraya girin


3

Verilerinizi bir veritabanı tablosunda aldıysanız, basit bir z-puanı algoritmasının SQL sürümü aşağıdadır:

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)

Kodunuz önerdiğim algoritmadan başka bir şey yapıyor. Sorgunuz yalnızca z skorlarını ([veri noktası - ortalama] / std) hesaplar, ancak algoritmamın yeni sinyal eşikleri hesaplanırken geçmiş sinyalleri yok sayan mantığını içermez. Ayrıca üç parametreyi (gecikme, etki, eşik) yoksayarsınız. Gerçek mantığı dahil etmek için cevabınızı gözden geçirebilir misiniz?
Jean-Paul

1
Evet sen haklisin. İlk başta yukarıdaki basitleştirilmiş sürümü ile kurtulmak düşündüm .. O zamandan beri tam çözümünüzü aldım ve C # için taşıdık. Cevabımı aşağıda görebilirsiniz. Daha fazla zamanım olduğunda bu SQL sürümünü tekrar ziyaret edip algoritmanızı dahil edeceğim. Bu arada, böyle harika bir cevap ve görsel açıklama için teşekkür ederim.
Ocean Airdrop

Sorun değil ve algoritma size yardımcı olabilir sevindim! C # gönderiminiz için teşekkür ederiz, biri hala eksikti. Çeviriler listesine ekleyeceğim!
Jean-Paul

3

Gerçek zamanlı akışlarla çalışan Python sürümü (her yeni veri noktasının varışında tüm veri noktalarını yeniden hesaplamaz). Sınıf işlevinin ne döndürdüğünü değiştirmek isteyebilirsiniz - benim için sinyallere ihtiyacım vardı.

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]

Gönderdiğiniz için teşekkürler, çevirinizi listeye ekledim.
Jean-Paul

3

Kendime bir javascript sürümü oluşturmasına izin verdim. Yararlı olabilir. Javascript, yukarıda verilen Sahte kodun doğrudan transkripsiyonu olmalıdır. Npm paketi ve github repo olarak mevcuttur:

Javascript çevirisi:

// 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

Çevirinizi gönderdiğiniz için teşekkür ederiz. Kodunuzu yanıtınıza ekledim, böylece insanlar hızlı bir şekilde görebilir. Çevirinizi listeye ekleyeceğim.
Jean-Paul

Şimdiye kadar, javascript için başka bir algoritma taşıdık. Bu sefer bana daha fazla kontrol sağlayan ve benim için daha iyi çalışan sayısal pitondan. Ayrıca npm olarak paketlenmiş ve kendi jupyter sayfalarında washington devlet üniversitesinden algo hakkında daha fazla bilgi bulabilirsiniz. npmjs.com/package/@joe_six/duarte-watanabe-peak-detection
Dirk Lüsebrink

2

Sınır değeri veya diğer kriterler gelecekteki değerlere bağlıysa, tek çözüm (zaman makinesi veya gelecekteki değerlerin başka bilgisi olmadan), yeterli gelecekteki değerlere sahip olana kadar herhangi bir kararı ertelemektir. Örneğin, 20 puanı kapsayan bir ortalamanın üzerinde bir seviye istiyorsanız, herhangi bir en yüksek karardan en az 19 puan alana kadar beklemeniz gerekir, aksi takdirde bir sonraki yeni nokta 19 puan önce eşiğinizi tamamen atabilir .

Mevcut arsada herhangi bir zirve yok ... bir şekilde önceden bir sonraki noktanın 1e99 olmadığını biliyorsunuz, ki bu da arsanızın Y boyutunu yeniden ölçekledikten sonra o noktaya kadar düz olacaktır.


Daha önce söylediğim gibi, bir zirve meydana gelirse, resimdeki zirveler kadar büyük olduğunu ve 'normal' değerlerden önemli ölçüde saptığını varsayabiliriz.
Jean-Paul

Piklerin ne kadar büyük olacağını önceden biliyorsanız, ortalama ve / veya eşik değerlerinizi bu değerin hemen altına ayarlayın.
hotpaw2

1
Ve tam olarak önceden bilmediğim şey bu.
Jean-Paul

1
Sadece kendinizle çeliştiniz ve zirvelerin resimdeki boyut olarak bilindiğini yazdınız. Ya bunu biliyorsun ya da bilmiyorsun.
hotpaw2

2
Sana açıklamaya çalışıyorum. Şimdi fikri anladın değil mi? 'Önemli ölçüde büyük tepe noktaları nasıl belirlenir'. Soruna istatistiksel olarak veya akıllı bir algoritmayla yaklaşabilirsiniz. İle .. As large as in the picturekastettiğim: önemli zirveleri ve temel gürültü vardır benzer durumlar için.
Jean-Paul

2

Ve işte ZSCORE algo'nun PHP uygulaması geliyor :

<?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>";

?>

Gönderdiğiniz için teşekkür ederiz, çevirinizi listeye ekledim.
Jean-Paul

1
Bir Yorum: Bu algoritma çoğunlukla örneklenmiş verilere kullanılacak göz önüne alındığında, ben uygulamak önermek örnek standart sapması bölerek ($len - 1)yerine $leniçindestddev()
Jean-Paul

1

Bir maxima'yı ortalamayla karşılaştırmak yerine, maxima'yı sadece bir gürültü eşiğinin üzerinde tanımlandığı bitişik minima ile de karşılaştırabiliriz. Yerel maksimum, bitişik minimadan> 3 kat (veya başka bir güven faktörü) ise, bu maksimum değer bir zirve olur. Pik belirleme, daha geniş hareketli pencerelerde daha doğrudur. Yukarıda, pencerenin sonunda bir hesaplama yerine (== gecikme), pencerenin ortasında ortalanmış bir hesaplama kullanılır.

Bir maksimumun, sinyalde bir artış ve sonrasında bir azalma olarak görülmesi gerektiğini unutmayın.


1

İşlev scipy.signal.find_peaks, adından da anlaşılacağı gibi, bunun için yararlıdır. Ama iyi parametrelerini anlamak önemlidir width, threshold, distance ve her şeyden önceprominence iyi bir zirve çıkarımı alır.

Testlerime ve belgelere göre, önem kavramı iyi zirveleri tutmak ve gürültülü zirveleri atmak için "yararlı konsept" tir.

Nedir (topografik) önem ? Öyle "gerekli minimum yükseklik herhangi bir yüksek araziye zirveden almak için inmek için" burada görülebileceği gibi,:

Fikir:

Önem ne kadar yüksek olursa, zirve o kadar "önemli" olur.


1

Mordern C +++ kullanarak z-skor algoritmasının nesne yönelimli versiyonu

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
Güzel çeviri. Nesne de kurtarırsa biraz daha güzel olurdu filtered_signal, signal, avg_filteredve std_filteredde özel değişkenleri ve sadece bu diziler günceller kez yeni bir veri noktası geldiğinde (şimdi tüm datapoints üzerinde kod döngüler denir hep). Bu, kodunuzun performansını artıracak ve OOP yapısına daha iyi uyacaktır.
Jean-Paul
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.