Deteksi sinyal puncak dalam data deret waktu nyata


243

Pembaruan: Algoritma berkinerja terbaik sejauh ini adalah yang ini .


Pertanyaan ini mengeksplorasi algoritma yang kuat untuk mendeteksi puncak tiba-tiba dalam data time-time real-time.

Pertimbangkan dataset berikut:

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

(Format Matlab tetapi ini bukan tentang bahasa tetapi tentang algoritma)

Plot data

Anda dapat dengan jelas melihat bahwa ada tiga puncak besar dan beberapa puncak kecil. Dataset ini adalah contoh spesifik dari kumpulan dataset deret waktu yang menjadi pertanyaan. Kelas kumpulan data ini memiliki dua fitur umum:

  1. Ada suara dasar dengan rata-rata umum
  2. Ada ' puncak ' besar atau ' titik data lebih tinggi ' yang secara signifikan menyimpang dari kebisingan.

Mari kita asumsikan juga sebagai berikut:

  • lebar puncak tidak dapat ditentukan sebelumnya
  • ketinggian puncak jelas dan signifikan menyimpang dari nilai-nilai lainnya
  • algoritma yang digunakan harus menghitung waktu nyata (jadi ubah dengan setiap titik data baru)

Untuk situasi seperti itu, nilai batas perlu dibangun yang memicu sinyal. Namun, nilai batas tidak boleh statis dan harus ditentukan realtime berdasarkan suatu algoritma.


Pertanyaan Saya: apakah algoritma yang baik untuk menghitung ambang seperti itu secara realtime? Apakah ada algoritma khusus untuk situasi seperti itu? Algoritme apa yang paling terkenal?


Algoritma yang kuat atau wawasan yang bermanfaat semuanya sangat dihargai. (dapat menjawab dalam bahasa apa pun: ini tentang algoritme)


5
Ada harus ada beberapa persyaratan tinggi mutlak untuk menjadi puncak di samping persyaratan Anda telah diberikan. Kalau tidak, puncak pada waktu 13 harus dianggap puncak. (Dengan kata lain: jika di masa mendatang, puncak mencapai 1000 atau lebih, maka dua puncak pada 25 dan 35 tidak boleh dianggap puncak.)
j_random_hacker

Saya setuju. Mari kita asumsikan puncak-puncak ini adalah yang hanya perlu kita pertimbangkan.
Jean-Paul

Anda mungkin mengajukan pertanyaan yang salah. Alih-alih bertanya bagaimana Anda dapat mendeteksi tanpa penundaan, Anda mungkin bertanya apakah mungkin untuk mendeteksi jenis sinyal tertentu tanpa penundaan hanya diberikan apa yang diketahui sebelum waktu itu, atau apa yang perlu diketahui tentang sinyal untuk mendeteksi sesuatu dengan beberapa yang diberikan menunda.
hotpaw2

2
Saya biasa melakukan ini untuk mendeteksi perubahan intensitas cahaya yang tiba-tiba pada sensor foto. Saya melakukan ini dengan memindahkan rata-rata, dan mengabaikan titik data yang lebih besar dari ambang. Perhatikan bahwa ambang ini berbeda dari ambang yang menentukan puncak. Jadi, katakanlah Anda hanya memasukkan titik data yang berada dalam satu stddev ke moving average Anda, dan pertimbangkan titik-titik data dengan lebih dari tiga stddev sebagai puncak. Algoritma ini sangat baik untuk konteks aplikasi kami saat itu.
justhalf

1
Ah, begitu. Saya tidak mengharapkannya dalam bentuk kode. Jika saya telah melihat pertanyaan ini sebelumnya mungkin Anda akan mendapatkan jawaban itu lebih cepat = D. Pokoknya, aplikasi saya waktu itu adalah untuk mendeteksi apakah fotosensor terhalang dari sumber cahaya sekitar (ini sebabnya kami membutuhkan rata-rata bergerak, karena sumber cahaya sekitar mungkin berubah secara bertahap dari waktu ke waktu). Kami menciptakan ini sebagai permainan di mana Anda harus mengarahkan tangan Anda pada sensor mengikuti pola tertentu. = D
justhalf

Jawaban:


334

Algoritma pendeteksian puncak yang kuat (menggunakan skor-z)

Saya datang dengan algoritma yang bekerja sangat baik untuk tipe dataset ini. Ini didasarkan pada prinsip dispersi : jika datapoint baru adalah x jumlah standar deviasi yang diberikan jauh dari rata-rata bergerak, sinyal algoritma (juga disebut skor-z ). Algoritma ini sangat kuat karena membangun sebuah terpisah berarti bergerak dan deviasi, sehingga sinyal tidak korup ambang batas. Oleh karena itu sinyal masa depan diidentifikasi dengan akurasi yang kira-kira sama, terlepas dari jumlah sinyal sebelumnya. Algoritma ini mengambil 3 input: lag = the lag of the moving window, threshold = the z-score at which the algorithm signalsdan influence = the influence (between 0 and 1) of new signals on the mean and standard deviation. Misalnya, a lagdari 5 akan menggunakan 5 pengamatan terakhir untuk memuluskan data. SEBUAHthreshold3,5 akan memberi sinyal jika datapoint berjarak 3,5 deviasi standar dari moving average. Dan influencedari 0,5 memberikan sinyal setengah dari pengaruh yang dimiliki titik data normal. Demikian juga, suatu influence0 mengabaikan sinyal sepenuhnya untuk menghitung ulang ambang baru. Karena itu pengaruh 0 adalah opsi yang paling kuat (tetapi mengasumsikan stasioneritas ); menempatkan opsi pengaruh pada 1 paling tidak kuat. Untuk data yang tidak stasioner, karena itu opsi pengaruh harus diletakkan di suatu tempat antara 0 dan 1.

Ia bekerja sebagai berikut:

Kodesemu

# 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

Aturan praktis untuk memilih parameter yang baik untuk data Anda dapat ditemukan di bawah.


Demo

Demonstrasi algoritma thresholding yang kuat

Kode Matlab untuk demo ini dapat ditemukan di sini . Untuk menggunakan demo, jalankan saja dan buat sendiri seri waktu dengan mengklik pada grafik atas. Algoritme mulai bekerja setelah menggambar lagsejumlah pengamatan.


Hasil

Untuk pertanyaan awal, algoritme ini akan memberikan output berikut saat menggunakan pengaturan berikut lag = 30, threshold = 5, influence = 0::

Contoh algoritma ambang batas


Implementasi dalam berbagai bahasa pemrograman:


Aturan praktis untuk mengkonfigurasi algoritma

lag: parameter lag menentukan berapa banyak data Anda akan dihaluskan dan seberapa adaptif algoritma terhadap perubahan rata-rata jangka panjang data. Semakin stasioner data Anda, semakin banyak keterlambatan yang harus Anda sertakan (ini akan meningkatkan kekokohan algoritma). Jika data Anda berisi tren yang bervariasi waktu, Anda harus mempertimbangkan seberapa cepat Anda ingin algoritma beradaptasi dengan tren ini. Yaitu, jika Anda memasukkan lag10, dibutuhkan 10 'periode' sebelum treshold algoritma disesuaikan dengan perubahan sistematis dalam rata-rata jangka panjang. Jadi pilih lagparameter berdasarkan perilaku tren data Anda dan seberapa adaptif algoritma yang Anda inginkan.

influence: parameter ini menentukan pengaruh sinyal pada ambang pendeteksian algoritma. Jika diletakkan pada 0, sinyal tidak memiliki pengaruh pada ambang, sehingga sinyal di masa depan terdeteksi berdasarkan ambang yang dihitung dengan mean dan standar deviasi yang tidak dipengaruhi oleh sinyal masa lalu. Cara lain untuk memikirkan hal ini adalah bahwa jika Anda meletakkan pengaruhnya pada 0, Anda secara implisit mengasumsikan stasioneritas (yaitu tidak peduli berapa banyak sinyal yang ada, deret waktu selalu kembali ke rata-rata yang sama dalam jangka panjang). Jika ini bukan masalahnya, Anda harus meletakkan parameter pengaruh di suatu tempat antara 0 dan 1, tergantung pada sejauh mana sinyal secara sistematis dapat mempengaruhi tren data yang bervariasi waktu. Misalnya, jika sinyal mengarah ke jeda struktural dari rata-rata jangka panjang dari deret waktu, parameter pengaruh harus diletakkan tinggi (mendekati 1) sehingga ambang batas dapat menyesuaikan dengan perubahan ini dengan cepat.

threshold: parameter threshold adalah jumlah standar deviasi dari moving average di atas yang mana algoritma akan mengklasifikasikan titik data baru sebagai sinyal. Misalnya, jika datapoint baru adalah 4,0 standar deviasi di atas rata-rata bergerak dan parameter ambang batas ditetapkan sebagai 3,5, algoritma akan mengidentifikasi datapoint sebagai sinyal. Parameter ini harus ditetapkan berdasarkan berapa banyak sinyal yang Anda harapkan. Misalnya, jika data Anda didistribusikan secara normal, ambang (atau: skor-z) 3,5 sesuai dengan probabilitas pensinyalan 0,00047 (dari tabel ini), yang menyiratkan bahwa Anda mengharapkan sinyal sekali setiap 2128 titik data (1 / 0,00047). Ambang karena itu secara langsung mempengaruhi seberapa sensitif algoritma dan dengan demikian juga seberapa sering sinyal algoritma. Periksa data Anda sendiri dan tentukan ambang masuk akal yang membuat sinyal algoritme saat Anda menginginkannya (beberapa percobaan-dan-kesalahan mungkin diperlukan di sini untuk mencapai ambang yang baik untuk tujuan Anda).


PERINGATAN: Kode di atas selalu berulang pada semua titik data setiap kali dijalankan. Saat menerapkan kode ini, pastikan untuk membagi perhitungan sinyal menjadi fungsi terpisah (tanpa loop). Kemudian ketika datapoint baru tiba, perbarui filteredY, avgFilterdan stdFiltersekali. Jangan menghitung ulang sinyal untuk semua data setiap kali ada datapoint baru (seperti dalam contoh di atas), itu akan sangat tidak efisien dan lambat!

Cara lain untuk memodifikasi algoritma (untuk perbaikan potensial) adalah:

  1. Gunakan median, bukan rata-rata
  2. Gunakan ukuran skala yang kuat , seperti MAD, alih-alih standar deviasi
  3. Gunakan margin pensinyalan, sehingga sinyal tidak terlalu sering berganti
  4. Ubah cara kerja parameter pengaruh
  5. Perlakukan sinyal naik dan turun secara berbeda (pengobatan asimetris)
  6. Buat influenceparameter terpisah untuk mean dan std ( seperti yang dilakukan dalam terjemahan Swift ini )

(Diketahui) kutipan akademik untuk jawaban StackOverflow ini:

Pekerjaan lain menggunakan algoritma

Aplikasi lain dari algoritma ini

Tautan ke algoritma deteksi puncak lainnya


Jika Anda menggunakan fungsi ini di suatu tempat, harap beri saya kredit atau jawaban ini. Jika Anda memiliki pertanyaan mengenai algoritme ini, poskan di komentar di bawah atau hubungi saya di LinkedIn .



Tautan ke movingstd rusak, tetapi Anda dapat menemukan uraiannya di sini
Phylliida

@reasra Ternyata fungsinya tidak memerlukan standar deviasi bergerak setelah menulis ulang. Sekarang dapat digunakan dengan fungsi-fungsi Matlab bawaan yang sederhana :)
Jean-Paul

1
Saya mencoba kode Matlab untuk beberapa data accelerometer, tetapi untuk beberapa alasan thresholdgrafik hanya menjadi garis hijau datar setelah lonjakan besar hingga 20 dalam data, dan tetap seperti itu selama sisa grafik ... Jika Saya menghapus sike, ini tidak terjadi, jadi sepertinya disebabkan oleh lonjakan data. Adakah yang tahu apa yang sedang terjadi? Saya seorang pemula di Matlab, jadi saya tidak bisa mengetahuinya ...
Magnus W

@BadCash Bisakah Anda memberikan contoh (dengan data)? Mungkin ajukan pertanyaan Anda sendiri di sini di SO dan beri tahu saya tautannya?
Jean-Paul

2
Ada banyak cara untuk meningkatkan algo ini, jadi jadilah kreatif (perlakuan berbeda atas / bawah; median bukan rata-rata; std kuat; menulis kode sebagai fungsi hemat-memori; margin ambang sehingga sinyal tidak terlalu sering berpindah, dll. .).
Jean-Paul

41

Berikut adalah Python/ numpyimplementasi dari algoritma z-score yang dihaluskan (lihat jawaban di atas ). Anda dapat menemukan intinya di sini .

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

Di bawah ini adalah tes pada dataset yang sama yang menghasilkan plot yang sama seperti pada jawaban asli untuk R/Matlab

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

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

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

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

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

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

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

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

Di sini 'y' sebenarnya adalah sinyal dan 'sinyal' adalah himpunan titik data, apakah saya benar dalam memahami?
TheTank

1
@TheTank yadalah larik data yang Anda lewati, signalsadalah larik +1atau -1keluaran yang menunjukkan untuk setiap titik data y[i]apakah titik data itu merupakan "puncak signifikan" mengingat pengaturan yang Anda gunakan.
Jean-Paul

23

Salah satu pendekatan adalah mendeteksi puncak berdasarkan pengamatan berikut:

  • Waktu t adalah puncak jika (y (t)> y (t-1)) && (y (t)> y (t + 1))

Ini menghindari positif palsu dengan menunggu sampai tren naik berakhir. Ini bukan "real-time" dalam arti bahwa ia akan kehilangan puncaknya dengan satu dt. sensitivitas dapat dikontrol dengan membutuhkan margin untuk perbandingan. Ada pertukaran antara deteksi bising dan waktu tunda deteksi. Anda dapat memperkaya model dengan menambahkan lebih banyak parameter:

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

di mana dt dan m adalah parameter untuk mengontrol sensitivitas vs penundaan waktu

Inilah yang Anda dapatkan dengan algoritma yang disebutkan: masukkan deskripsi gambar di sini

di sini adalah kode untuk mereproduksi plot dengan python:

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

Dengan mengatur m = 0.5, Anda bisa mendapatkan sinyal yang lebih bersih dengan hanya satu false positive: masukkan deskripsi gambar di sini


Sebelumnya = lebih baik sehingga semua puncak signifikan. Terima kasih! Sangat keren!
Jean-Paul

Bagaimana saya akan mengubah sensitivitas?
Jean-Paul

Saya dapat memikirkan dua pendekatan: 1: atur m ke nilai yang lebih besar sehingga hanya puncak yang lebih besar yang terdeteksi. 2: alih-alih menghitung y (t) - y (t-dt) (dan y (t) - y (t + dt)), Anda mengintegrasikan dari t-dt ke t (dan t ke t + dt).
aha

2
Dengan kriteria apa Anda menolak 7 puncak lainnya?
hotpaw2

4
Ada masalah dengan puncak datar, karena apa yang Anda lakukan pada dasarnya adalah deteksi tepi 1-D (seperti membelitkan sinyal dengan [1 0 -1])
ben

18

Dalam pemrosesan sinyal, deteksi puncak sering dilakukan melalui transformasi wavelet. Anda pada dasarnya melakukan transformasi wavelet diskrit pada data deret waktu Anda. Persimpangan nol dalam koefisien detail yang dikembalikan akan sesuai dengan puncak dalam sinyal deret waktu. Anda mendapatkan amplitudo puncak yang berbeda terdeteksi pada level koefisien detail yang berbeda, yang memberi Anda resolusi multi-level.


1
Jawaban Anda izinkan saya untuk artikel ini dan jawaban ini yang akan membantu saya membangun algoritma yang baik untuk implementasi saya. Terima kasih!
Jean-Paul

@cklin Bisakah Anda menjelaskan bagaimana Anda menghitung penyilangan nol dari koefisien wavelet, karena mereka tidak pada skala waktu yang sama dengan seri waktu asli. Adakah referensi tentang penggunaan ini?
horaceT

11

Kami telah berusaha menggunakan algoritma skor-z yang dihaluskan pada set data kami, yang menghasilkan sensitivitas yang berlebihan atau kurang sensitivitas (tergantung pada bagaimana parameter disetel), dengan sedikit jalan tengah. Dalam sinyal lalu lintas situs kami, kami telah mengamati baseline frekuensi rendah yang mewakili siklus harian dan bahkan dengan parameter terbaik yang mungkin (ditampilkan di bawah), itu masih tertinggal terutama pada hari ke-4 karena sebagian besar titik data diakui sebagai anomali .

Membangun di atas algoritma z-score asli, kami menemukan cara untuk menyelesaikan masalah ini dengan pemfilteran terbalik. Detail algoritma yang dimodifikasi dan aplikasinya pada atribusi trafik komersial TV diposting di blog tim kami .

masukkan deskripsi gambar di sini


Keren untuk melihat bahwa algoritme adalah titik awal untuk versi yang lebih canggih. Data Anda memiliki pola yang sangat khusus, jadi memang lebih masuk akal untuk menghapus pola menggunakan teknik lain dan kemudian menerapkan algo pada residu. Atau, Anda mungkin ingin menggunakan jendela tengah bukannya jendela lagging untuk menghitung rata-rata / st.dev. Komentar lain: solusi Anda bergerak dari kanan ke kiri untuk mengidentifikasi lonjakan, tetapi ini tidak mungkin dalam aplikasi waktu nyata (itu sebabnya algo asli sangat sederhana, karena informasi di masa depan tidak dapat diakses).
Jean-Paul

10

Dalam topologi komputasi, gagasan homologi persisten mengarah pada solusi yang efisien - secepat pengurutan angka -. Itu tidak hanya mendeteksi puncak, itu mengukur "signifikansi" puncak secara alami yang memungkinkan Anda untuk memilih puncak yang penting bagi Anda.

Ringkasan algoritma. Dalam pengaturan 1 dimensi (seri waktu, sinyal bernilai nyata), algoritme dapat dengan mudah dijelaskan oleh gambar berikut:

Puncak paling persisten

Pikirkan grafik fungsi (atau sub-level set) sebagai lanskap dan pertimbangkan penurunan ketinggian air mulai dari level infinity (atau 1,8 pada gambar ini). Sementara tingkat menurun, di pulau-pulau maxima lokal muncul. Di minimum lokal pulau-pulau ini bergabung bersama. Satu detail dalam ide ini adalah bahwa pulau yang muncul kemudian digabung menjadi pulau yang lebih tua. "Kegigihan" suatu pulau adalah waktu kelahirannya dikurangi waktu kematiannya. Panjang batang biru menggambarkan kegigihan, yang merupakan "signifikansi" puncak yang disebutkan di atas.

Efisiensi. Tidak terlalu sulit untuk menemukan implementasi yang berjalan dalam waktu linier - sebenarnya itu adalah loop tunggal yang sederhana - setelah nilai fungsi diurutkan. Jadi implementasi ini harus cepat dalam praktiknya dan juga mudah diimplementasikan.

Referensi. Tulisan seluruh cerita dan referensi untuk motivasi dari homologi persisten (bidang dalam topologi aljabar komputasi) dapat ditemukan di sini: https://www.sthu.org/blog/13-perstopology-peakdetection/index.html


Algoritma ini jauh lebih cepat dan lebih akurat daripada, misalnya, scipy.signal.find_peaks. Untuk rangkaian waktu "nyata" dengan 1053896 titik data, ia mendeteksi 137516 puncak (13%). Urutan puncak (paling signifikan pertama) memungkinkan puncak paling signifikan untuk diekstraksi. Ini memberikan awal, puncak, dan akhir setiap puncak. Bekerja dengan baik dengan data yang berisik.
vinh

Dengan data real-time yang Anda maksud adalah algoritma online, di mana poin data diterima dari waktu ke waktu. Pentingnya puncak mungkin ditentukan oleh nilai-nilai di masa depan. Alangkah baiknya untuk memperluas algoritma untuk menjadi online dengan memodifikasi hasil masa lalu tanpa mengorbankan kompleksitas waktu.
S. Huber

9

Menemukan algoritma lain oleh GH Palshikar di Algoritma Sederhana untuk Deteksi Puncak dalam Time-Series .

Algoritmanya seperti ini:

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

Keuntungan

  • Makalah ini menyediakan 5 algoritma berbeda untuk deteksi puncak
  • Algoritma bekerja pada data time-series mentah (tidak diperlukan perataan)

Kekurangan

  • Sulit untuk menentukan kdan hterlebih dahulu
  • Puncak tidak boleh datar (seperti puncak ketiga dalam data pengujian saya)

Contoh:

masukkan deskripsi gambar di sini


Makalah yang sebenarnya menarik. S4 sepertinya fungsi yang lebih baik untuk digunakan dalam pendapatnya. Tetapi yang lebih penting adalah mengklarifikasi ketika k <i <Nk tidak benar. Bagaimana seseorang mendefinisikan fungsi S1 (S2, ..) untuk i = 0 saya tidak membagi 2 dan mengabaikan operan pertama dan untuk setiap lainnya saya memasukkan kedua operan tetapi untuk i <= k ada operan yang kurang di sebelah kiri kemudian di sebelah kanan
daniels_pa

8

Berikut ini adalah implementasi dari algoritma Smoothed z-score (di atas) di Golang. Ini mengasumsikan sepotong []int16(sampel PCM 16bit). Anda dapat menemukan intinya di sini .

/*
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 Saya tidak sepenuhnya yakin semuanya benar, jadi mungkin ada bug.
Xeoncross

1
Sudahkah Anda mencoba mereplikasi output contoh demo dari Matlab / R? Itu harus menjadi konfirmasi kualitas yang baik.
Jean-Paul

7

Berikut ini adalah implementasi C ++ dari algoritma z-score yang dihaluskan dari jawaban ini

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
Peringatan: Implementasi ini sebenarnya tidak menyediakan metode untuk menghitung mean dan standar deviasi. Untuk C ++ 11, metode mudah dapat ditemukan di sini: stackoverflow.com/a/12405793/3250829
rayryeng

6

Masalah ini terlihat mirip dengan yang saya temui dalam kursus sistem hybrid / embedded, tapi itu terkait dengan mendeteksi kesalahan ketika input dari sensor berisik. Kami menggunakan filter Kalman untuk memperkirakan / memprediksi keadaan tersembunyi dari sistem, kemudian menggunakan analisis statistik untuk menentukan kemungkinan bahwa suatu kesalahan telah terjadi . Kami bekerja dengan sistem linier, tetapi ada varian nonlinear. Saya ingat pendekatan itu secara mengejutkan adaptif, tetapi itu membutuhkan model dinamika sistem.


Filter Kalman menarik, tetapi sepertinya saya tidak dapat menemukan algoritma yang berlaku untuk tujuan saya. Saya sangat menghargai jawabannya dan saya akan melihat beberapa makalah pendeteksian puncak seperti ini untuk melihat apakah saya dapat belajar dari salah satu algoritma. Terima kasih!
Jean-Paul

6

Implementasi C ++

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

using namespace std;

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

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

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

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

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

    ld mean() {
        return m1;
    }

    ld standard_deviation() {
        return m2;
    }

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

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

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

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

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

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

    return output;
};

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

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

6

Sebagai lanjutan dari solusi yang diusulkan oleh Jean-Paul, saya telah mengimplementasikan algoritmanya dalam 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;
    }
}

Contoh penggunaan:

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
Hei @ Jean-Paul. Bersulang. Ya, saya sudah menguji output terhadap versi R Anda untuk memastikannya cocok. Sekali lagi terima kasih atas solusi Anda untuk masalah ini.
Ocean Airdrop

Hai, Saya pikir ada kesalahan dalam kode itu, dalam metode StdDev Anda mengambil nilai.Count () - 1, haruskah bergantung pada -1? Saya pikir Anda ingin jumlah item dan itulah yang Anda dapatkan dari values.Count ().
Viktor

1
Hmm .. Tempat yang bagus. Meskipun saya awalnya porting algoritma ke C #, saya tidak pernah akhirnya menggunakannya. Saya mungkin akan mengganti seluruh fungsi itu dengan panggilan ke perpustakaan nuget MathNet. "Instal-Paket MathNet.Numerics" Ini memiliki fungsi prebuilt untuk PopulationStandardDeviation () dan StandardDeviation (); misalnya. var populasiStdDev = Daftar baru <double> (1,2,3,4) .PopulationStandardDeviation (); var sampleStdDev = Daftar baru <double> (1,2,3,4) .StandardDeviation ();
Ocean Airdrop

6

Berikut adalah implementasi C dari skor Z Jean-Paul's Smoothed untuk mikrokontroler Arduino yang digunakan untuk mengambil pembacaan accelerometer dan memutuskan apakah arah dampak telah datang dari kiri atau kanan. Ini berkinerja sangat baik karena perangkat ini mengembalikan sinyal yang dipantulkan. Berikut ini input untuk algoritme deteksi puncak ini dari perangkat - menunjukkan dampak dari kanan diikuti oleh dan dampak dari kiri. Anda dapat melihat lonjakan awal kemudian osilasi sensor.

masukkan deskripsi gambar di sini

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

Miliknya hasilnya dengan pengaruh = 0

masukkan deskripsi gambar di sini

Tidak hebat tapi di sini dengan pengaruh = 1

masukkan deskripsi gambar di sini

itu sangat bagus.


5

Berikut ini adalah implementasi Java aktual berdasarkan jawaban Groovy yang diposting sebelumnya. (Saya tahu sudah ada implementasi Groovy dan Kotlin yang diposting, tetapi untuk orang seperti saya yang hanya melakukan Java, sungguh merepotkan untuk mengetahui bagaimana mengkonversi antara bahasa lain dan Jawa).

(Hasil cocok dengan grafik orang lain)

Implementasi algoritma

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
}

Metode utama

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

Hasil

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

Grafik yang menunjukkan data dan hasil eksekusi java


5

Lampiran 1 untuk jawaban asli: Matlabdan Rterjemahan

Kode matlab

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

Contoh:

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

Kode r

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

Contoh:

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

Kode ini (kedua bahasa) akan menghasilkan hasil berikut untuk data dari pertanyaan awal:

Contoh Thresholding dari kode Matlab


Lampiran 2 untuk jawaban asli: Matlabkode demonstrasi

(klik untuk membuat data)

Demo Matlab

function [] = RobustThresholdingDemo()

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

end

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

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


4

Berikut ini adalah upaya saya untuk membuat solusi Ruby untuk "Algo z-score dihaluskan" dari jawaban yang diterima:

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

Dan contoh penggunaan:

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

Luar biasa, terima kasih sudah berbagi! Saya akan menambahkan Anda ke daftar. Pastikan untuk aplikasi realtime Anda membuat fungsi terpisah untuk memperbarui sinyal ketika datapoint baru tiba (alih-alih mengulang semua datapoints setiap kali).
Jean-Paul

4

Versi berulang dalam python / numpy untuk jawaban https://stackoverflow.com/a/22640362/6029703 ada di sini. Kode ini lebih cepat dari rata-rata komputasi dan standar deviasi setiap jeda untuk data besar (100000+).

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

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

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

4

Saya pikir saya akan memberikan implementasi algoritma Julia saya untuk orang lain. Intinya dapat ditemukan di sini

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)

Hasil


3

Berikut ini adalah implementasi Groovy (Java) dari algoritma z-score yang dihaluskan ( lihat jawaban di atas ).

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

Di bawah ini adalah tes pada dataset yang sama yang menghasilkan hasil yang sama seperti implementasi Python / numpy di atas .

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

Berikut ini adalah versi Scala (non-idiomatis) dari algoritma z-score yang dihaluskan :

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

Berikut ini adalah tes yang mengembalikan hasil yang sama dengan versi Python dan Groovy:

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

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

smoothedZScore(y, lag, threshold, influence)

bagan hasil vegas

Intinya di sini


1 mewakili puncak, -1 mewakili lembah.
Mike Roberts

3

Saya membutuhkan sesuatu seperti ini di proyek android saya. Kupikir aku akan mengembalikan implementasi Kotlin .

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

proyek sampel dengan grafik verifikasi dapat ditemukan di github .

masukkan deskripsi gambar di sini


Luar biasa! Terima kasih telah berbagi. Untuk aplikasi realtime, pastikan untuk membuat fungsi terpisah yang menghitung sinyal baru dengan setiap titik data yang masuk. Jangan mengulang-ulang data lengkap setiap kali datapoint baru tiba, itu akan sangat tidak efisien :)
Jean-Paul

1
Poin baiknya, tidak memikirkan hal itu, karena windows yang saya gunakan tidak tumpang tindih.
leonardkraemer

3

Berikut adalah versi Fortran yang diubah dari algoritma z-score . Itu diubah khusus untuk deteksi puncak (resonansi) dalam fungsi transfer dalam ruang frekuensi (Setiap perubahan memiliki komentar kecil dalam kode).

Modifikasi pertama memberikan peringatan kepada pengguna jika ada resonansi di dekat batas bawah dari vektor input, ditunjukkan oleh standar deviasi yang lebih tinggi dari ambang tertentu (10% dalam kasus ini). Ini berarti bahwa sinyal tidak cukup datar untuk deteksi menginisialisasi filter dengan benar.

Modifikasi kedua adalah bahwa hanya nilai puncak tertinggi yang ditambahkan ke puncak yang ditemukan. Ini dicapai dengan membandingkan setiap nilai puncak yang ditemukan dengan besarnya (lag) pendahulunya dan (lag) penggantinya.

Perubahan ketiga adalah untuk menghormati bahwa puncak resonansi biasanya menunjukkan beberapa bentuk simetri di sekitar frekuensi resonansi. Jadi wajar untuk menghitung rata-rata dan std secara simetris di sekitar titik data saat ini (bukan hanya untuk pendahulunya). Ini menghasilkan perilaku deteksi puncak yang lebih baik.

Modifikasi memiliki efek bahwa seluruh sinyal harus diketahui fungsi sebelumnya yang merupakan kasus biasa untuk deteksi resonansi (sesuatu seperti Contoh Matlab dari Jean-Paul di mana titik data yang dihasilkan dengan cepat tidak akan berfungsi).

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

Untuk aplikasi saya, algoritma ini bekerja seperti pesona! masukkan deskripsi gambar di sini


3

Jika Anda sudah mendapatkan data dalam tabel database, berikut ini adalah versi SQL dari algoritma z-score sederhana:

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)

Kode Anda melakukan sesuatu yang lain dari algoritma yang saya usulkan. Kueri Anda hanya menghitung skor-z ([titik data - rata-rata] / std), tetapi tidak memasukkan logika algoritma saya yang mengabaikan sinyal masa lalu saat menghitung ambang sinyal baru. Anda juga mengabaikan tiga parameter (lag, pengaruh, ambang batas). Bisakah Anda merevisi jawaban Anda untuk memasukkan logika yang sebenarnya?
Jean-Paul

1
Ya benar Pada awalnya saya pikir saya bisa lolos dengan versi sederhana di atas .. Saya telah mengambil solusi lengkap Anda dan porting ke C #. Lihat jawaban saya di bawah ini. Ketika saya memiliki lebih banyak waktu, saya akan mengunjungi kembali versi SQL ini dan memasukkan algoritma Anda. Omong-omong, terima kasih atas jawaban yang luar biasa & penjelasan visual.
Ocean Airdrop

Tidak ada masalah dan senang algoritma ini bisa membantu Anda! Terima kasih atas kiriman C # Anda, yang masih hilang. Saya akan menambahkannya ke daftar terjemahan!
Jean-Paul

3

Versi python yang bekerja dengan aliran waktu nyata (tidak menghitung ulang semua titik data pada saat kedatangan setiap titik data baru). Anda mungkin ingin mengubah apa fungsi kelas kembali - untuk tujuan saya, saya hanya perlu sinyal.

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]

Terima kasih telah memposting, saya telah menambahkan terjemahan Anda ke daftar.
Jean-Paul

3

Saya membiarkan diri saya membuat versi javascript. Akan sangat membantu. Javascript harus berupa transkripsi langsung dari Pseudocode yang diberikan di atas. Tersedia sebagai paket npm dan repo github:

Terjemahan Javascript:

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

Terima kasih telah memposting terjemahan Anda. Saya telah menambahkan kode Anda ke jawaban Anda sehingga orang dapat dengan cepat melihatnya. Saya akan menambahkan terjemahan Anda ke daftar.
Jean-Paul

Sekarang, saya telah mem-porting beberapa algoritma lain ke javascript. Kali ini dari python numercial, yang memberi saya lebih banyak kontrol dan bekerja lebih baik untuk saya. Juga dikemas dalam npm dan Anda dapat menemukan informasi lebih lanjut tentang algo dari washington state university di halaman jupyter mereka miliki. npmjs.com/package/@joe_six/duarte-watanabe-peak-detection
Dirk Lüsebrink

2

Jika nilai batas atau kriteria lain bergantung pada nilai masa depan, maka satu-satunya solusi (tanpa mesin waktu, atau pengetahuan lain tentang nilai masa depan) adalah menunda keputusan apa pun sampai seseorang memiliki nilai masa depan yang memadai. Jika Anda ingin level di atas rata-rata yang mencakup, misalnya, 20 poin, maka Anda harus menunggu hingga Anda memiliki setidaknya 19 poin di depan setiap keputusan puncak, atau titik baru berikutnya dapat benar-benar membuang ambang Anda 19 poin yang lalu .

Plot Anda saat ini tidak memiliki puncak ... kecuali Anda entah bagaimana tahu sebelumnya bahwa poin berikutnya bukan 1e99, yang setelah mengubah dimensi Y plot Anda, akan datar sampai titik itu.


Seperti yang saya katakan sebelumnya, kita dapat mengasumsikan bahwa JIKA puncak terjadi, itu sama besar dengan puncak pada gambar dan menyimpang secara signifikan dari nilai-nilai 'normal'.
Jean-Paul

Jika Anda tahu seberapa besar puncaknya di muka, maka tentukan terlebih dahulu nilai rata-rata dan / atau ambang batas Anda di bawah nilai itu.
hotpaw2

1
Dan itulah yang tidak saya ketahui sebelumnya.
Jean-Paul

1
Anda hanya bertentangan dengan diri Anda sendiri dan menulis bahwa puncaknya diketahui sebagai ukuran dalam gambar. Entah Anda tahu itu atau tidak.
hotpaw2

2
Saya mencoba menjelaskannya kepada Anda. Anda mendapatkan idenya sekarang? 'Cara mengidentifikasi puncak yang sangat besar'. Anda dapat mendekati masalah baik secara statistik atau dengan algoritma cerdas. Dengan .. As large as in the pictureAku berarti: untuk situasi yang sama di mana ada puncak yang signifikan dan kebisingan dasar.
Jean-Paul

2

Dan inilah implementasi PHP dari algo ZSCORE:

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

?>

Terima kasih telah memposting, saya telah menambahkan terjemahan Anda ke daftar.
Jean-Paul

1
Satu komentar: mengingat bahwa algoritma ini sebagian besar akan digunakan pada data sampel, saya sarankan Anda menerapkan standar deviasi sampel dengan membaginya dengan ($len - 1)bukannya $lendalamstddev()
Jean-Paul

1

Alih-alih membandingkan maxima dengan rata-rata, seseorang juga dapat membandingkan maxima dengan minima yang berdekatan di mana minima hanya didefinisikan di atas ambang batas noise. Jika maksimum lokal> 3 kali (atau faktor kepercayaan lainnya) baik minimum minimum, maka maksimum itu adalah puncak. Penentuan puncak lebih akurat dengan jendela bergerak yang lebih luas. Omong-omong di atas menggunakan perhitungan yang berpusat di tengah jendela, daripada perhitungan di akhir jendela (== lag).

Perhatikan bahwa maxima harus dilihat sebagai peningkatan sinyal sebelum dan penurunan setelah.


1

Fungsi scipy.signal.find_peaks, seperti namanya, berguna untuk ini. Tapi itu penting untuk memahami dengan baik parameter width, threshold, distance dan di atas semuaprominence untuk mendapatkan ekstraksi puncak yang baik.

Menurut tes dan dokumentasi saya, konsep menonjol adalah "konsep yang berguna" untuk menjaga puncak yang baik, dan membuang puncak yang bising.

Apa yang menonjol (topografi) ? Ini adalah "ketinggian minimum yang diperlukan untuk turun dari puncak ke medan yang lebih tinggi" , seperti yang dapat dilihat di sini:

Idenya adalah:

Semakin tinggi keunggulannya, semakin "penting" puncaknya.


1

Versi berorientasi objek dari algoritma z-score menggunakan mordern C +++

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
Terjemahan yang bagus. Ini akan menjadi sedikit lebih bagus jika objek juga menyimpan filtered_signal, signal, avg_filtereddan std_filteredvariabel sebagai pribadi dan hanya akan memperbarui array sekali ketika datapoint baru tiba (sekarang loop kode atas semua datapoints setiap kali itu disebut). Itu akan meningkatkan kinerja kode Anda dan lebih cocok dengan struktur OOP.
Jean-Paul
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.