Filter penghalusan Savitzky-Golay untuk data yang tidak berjarak sama

16

Saya memiliki sinyal yang diukur pada 100Hz dan saya perlu menerapkan filter smoothing Savitzky-Golay pada sinyal ini. Namun, pada pemeriksaan lebih dekat sinyal saya tidak diukur pada laju konstan sempurna, delta antara pengukuran berkisar antara 9,7 dan 10,3 ms.

Apakah ada cara untuk menggunakan filter Savitzky-Golay pada data yang tidak berjarak sama? Apakah ada metode lain yang bisa saya terapkan?

VLC
sumber
Makalah tahun 1991 oleh Gorry ada di cukup banyak subjek yang tepat ini datapdf.com/… . Tapi tldr, jawaban datageist adalah ide utama yang tepat (local-square). Yang diamati Gorry adalah bahwa koefisien hanya bergantung pada variabel independen dan linear dalam variabel dependen (seperti Savitzky-Golay). Lalu dia memberikan cara untuk menghitungnya, tetapi jika Anda tidak menulis perpustakaan yang dioptimalkan, bugar kuadrat terkecil apa pun dapat digunakan.
Dave Pritchard

Jawaban:

5

Salah satu metode adalah dengan menguji ulang data Anda sehingga spasi sama, maka Anda dapat melakukan pemrosesan apa pun yang Anda suka. Resampling tanpa batas menggunakan filter linear tidak akan menjadi pilihan yang baik karena data tidak diberi spasi secara seragam, jadi Anda bisa menggunakan semacam interpolasi polinomial lokal (misalnya splines kubik) untuk memperkirakan berapa nilai sinyal yang mendasarinya "tepat" Interval 10 milidetik.

Jason R
sumber
Saya memiliki solusi ini dalam pikiran sebagai pilihan terakhir. Saya bertanya-tanya apakah pada akhirnya pendekatan ini memberikan solusi yang lebih baik daripada hanya mengasumsikan bahwa sinyal saya diukur pada tingkat yang konstan.
VLC
Saya pikir bahkan jika itu non-seragam sampel Anda masih dapat menggunakan interpolasi sinc () (atau filter low pass sangat berbeda sampel). Ini mungkin memberikan hasil yang lebih baik daripada spline atau pchip
Hilmar
1
@Hilmar: Anda benar. Ada beberapa cara Anda bisa melakukan resample data; perkiraan interpolasi tulus akan menjadi metode "ideal" untuk resampling bandlimited.
Jason R
15

Karena cara Savitzky-Golay filter diturunkan (yaitu sebagai cocok polinomial kuadrat-lokal), ada generalisasi alami untuk pengambilan sampel tidak seragam - itu jauh lebih mahal secara komputasi.

Savitzky-Golay Filter secara Umum

Untuk filter standar, idenya adalah untuk menyesuaikan polinomial ke set sampel lokal [menggunakan kuadrat terkecil], lalu ganti sampel pusat dengan nilai polinomial pada indeks tengah (yaitu pada 0). Itu berarti koefisien filter SG standar dapat dihasilkan dengan membalikkan matriks Vandermonde indeks sampel. Misalnya, untuk menghasilkan parabola fit lokal di lima sampel (dengan indicies lokal -2, -1,0,1,2), sistem persamaan desain A c = y akan menjadi sebagai berikut:y0y4Ac=y

[202122101112000102101112202122][c0c1c2]=[y0y1y2y3y4].

Dalam contoh di atas, adalah koefisien yang tidak diketahui dari polinomial kuadrat terkecil c 0 + c 1 x + c 2 x 2 . Karena nilai polinomial pada x = 0 hanya c 0 , menghitung pseudoinverse dari matriks desain (yaitu c = ( A T A ) - 1 A T y ) akan menghasilkan koefisien filter SG di baris atas. Dalam hal ini, mereka akan menjadic0c2c0+c1x+c2x2x=0c0c=(ATA)1ATy 

[c0c1c2]=[312171237404753535][y0y1y2y3y4].

Perhatikan bahwa karena turunan dari adalah c 1 + 2 c 2 x , baris kedua dari matriks (yang mengevaluasi c 1 ) akan menjadi filter derivatif yang dihaluskan. Argumen yang sama berlaku untuk baris berturut-turut - mereka memberikan turunan tingkat tinggi yang dihaluskan. Perhatikan bahwa saya menskalakan matriks dengan 35 sehingga baris pertama akan cocok dengan koefisien smoothing yang diberikan di Wikipedia (di atas). Filter derivatif masing-masing berbeda oleh faktor penskalaan lainnya.c0+c1x+c2x2c1+2c2xc1

Pengambilan sampel yang tidak seragam

Ketika sampel ditempatkan secara merata, koefisien filter adalah terjemahan-invarian, sehingga hasilnya hanyalah filter FIR. Untuk sampel tidak seragam, koefisien akan berbeda berdasarkan jarak sampel lokal, sehingga matriks desain perlu dibangun dan dibalik di setiap sampel. Jika waktu sampel tidak seragam adalah , dan kami membangun koordinat lokal t n dengan setiap waktu sampel pusat ditetapkan pada 0 , yaituxntn0

t2=x2x0t1=x1x0t0=x0x0t1=x1x0t2=x2x0

maka setiap matriks desain akan berbentuk sebagai berikut:

A=[t20t21t22t10t11t12t00t01t02t10t11t12t20t21t22]=[1t2t221t1t121001t1t121t2t22].

The first row of the pseudoinverse of A dotted with the local sample values will yield c0, the smoothed value at that sample.

datageist
sumber
sounds like it moves from O(log(n)) to O(n^2).
EngrStudent - Reinstate Monica
Here's an implementation of Scala described by datageist upwards.
Medium core
1
@Mediumcore You didn't add a link to your original post. Also, I deleted it because it didn't provide an answer to the question. Please try to edit datageist's post to add a link; it'll be moderated in after review.
Peter K.
4

"As a cheap alternative, one can simply pretend that the data points are equally spaced ...
if the change in f across the full width of the N point window is less than N/2 times the measurement noise on a single point, then the cheap method can be used."
Numerical Recipes pp. 771-772

(derivation anyone ?)

("Pretend equally spaced" means:
take the nearest ±N/2 points around each t where you want SavGol(t),
not snap all tii . That may be obvious, but got me for a while.)

denis
sumber
1

I found out, that there are two ways to use the savitzky-golay algorithm in Matlab. Once as a filter, and once as a smoothing function, but basically they should do the same.

  1. yy = sgolayfilt(y,k,f): Here, the values y=y(x) are assumed to be equally spaced in x.
  2. yy = smooth(x,y,span,'sgolay',degree): Here you can have x as an extra input and referring to the Matlab help x does not have to be equally spaced!
Jochen
sumber
0

If it's of any help, I've made a C implementation of the method described by datageist. Free to use at your own risk.

/**
 * @brief smooth_nonuniform
 * Implements the method described in  /signals/1676/savitzky-golay-smoothing-filter-for-not-equally-spaced-data
 * free to use at the user's risk
 * @param n the half size of the smoothing sample, e.g. n=2 for smoothing over 5 points
 * @param the degree of the local polynomial fit, e.g. deg=2 for a parabolic fit
 */
bool smooth_nonuniform(uint deg, uint n, std::vector<double>const &x, std::vector<double> const &y, std::vector<double>&ysm)
{
    if(x.size()!=y.size()) return false; // don't even try
    if(x.size()<=2*n)      return false; // not enough data to start the smoothing process
//    if(2*n+1<=deg+1)       return false; // need at least deg+1 points to make the polynomial

    int m = 2*n+1; // the size of the filter window
    int o = deg+1; // the smoothing order

    std::vector<double> A(m*o);         memset(A.data(),   0, m*o*sizeof(double));
    std::vector<double> tA(m*o);        memset(tA.data(),  0, m*o*sizeof(double));
    std::vector<double> tAA(o*o);       memset(tAA.data(), 0, o*o*sizeof(double));

    std::vector<double> t(m);           memset(t.data(),   0, m*  sizeof(double));
    std::vector<double> c(o);           memset(c.data(),   0, o*  sizeof(double));

    // do not smooth start and end data
    int sz = y.size();
    ysm.resize(sz);           memset(ysm.data(), 0,sz*sizeof(double));
    for(uint i=0; i<n; i++)
    {
        ysm[i]=y[i];
        ysm[sz-i-1] = y[sz-i-1];
    }

    // start smoothing
    for(uint i=n; i<x.size()-n; i++)
    {
        // make A and tA
        for(int j=0; j<m; j++)
        {
            t[j] = x[i+j-n] - x[i];
        }
        for(int j=0; j<m; j++)
        {
            double r = 1.0;
            for(int k=0; k<o; k++)
            {
                A[j*o+k] = r;
                tA[k*m+j] = r;
                r *= t[j];
            }
        }

        // make tA.A
        matMult(tA.data(), A.data(), tAA.data(), o, m, o);

        // make (tA.A)-¹ in place
        if (o==3)
        {
            if(!invert33(tAA.data())) return false;
        }
        else if(o==4)
        {
            if(!invert44(tAA.data())) return false;
        }
        else
        {
            if(!inverseMatrixLapack(o, tAA.data())) return false;
        }

        // make (tA.A)-¹.tA
        matMult(tAA.data(), tA.data(), A.data(), o, o, m); // re-uses memory allocated for matrix A

        // compute the polynomial's value at the center of the sample
        ysm[i] = 0.0;
        for(int j=0; j<m; j++)
        {
            ysm[i] += A[j]*y[i+j-n];
        }
    }

    std::cout << "      x       y       y_smoothed" << std::endl;
    for(uint i=0; i<x.size(); i++) std::cout << "   " << x[i] << "   " << y[i]  << "   "<< ysm[i] << std::endl;

    return true;
}

smoothing

techwinder
sumber