Eşit aralıklı olmayan veriler için Savitzky-Golay yumuşatma filtresi


16

100Hz'de ölçülen bir sinyalim var ve bu sinyale Savitzky-Golay yumuşatma filtresini uygulamam gerekiyor. Ancak, daha yakından incelemede sinyalim tam olarak sabit bir oranda ölçülmez, ölçümler arasındaki delta 9.7 ila 10.3 ms arasında değişir.

Savitzky-Golay filtresini eşit aralıklı olmayan verilerde kullanmanın bir yolu var mı? Uygulayabileceğim başka yöntemler var mı?


Gorry'nin 1991 tarihli bir makalesi hemen hemen bu konuda datapdf.com/… . Ama tldr, datageist'in cevabı doğru ana fikirdir (yerel en küçük kareler). Gorry'nin gözlemlediği şey, katsayıların sadece bağımsız değişkenlere bağlı olması ve bağımlı değişkenlerde (Savitzky-Golay gibi) doğrusal olmasıdır. Sonra onları hesaplamak için bir yol verir, ancak optimize edilmiş bir kütüphane yazmıyorsanız, herhangi bir eski en küçük kareler tesisatçısı kullanılabilir.
Dave Pritchard

Yanıtlar:


5

Bir yöntem, verilerinizi eşit aralıklarla yerleştirilecek şekilde yeniden örneklemektir, o zaman istediğiniz işlemi yapabilirsiniz. Doğrusal filtreleme kullanarak bant sınırında yeniden örnekleme, veriler eşit aralıklı olmadığından iyi bir seçenek olmayacaktır, bu nedenle, temel sinyalin değerlerinin "tam olarak" ne olduğunu tahmin etmek için bir çeşit yerel polinom enterpolasyonu (örn. Kübik spline) kullanabilirsiniz. 10 milisaniye aralıklarla.


Bu çözümü son çare olarak düşündüm. Sonunda bu yaklaşımın sadece sinyalimin sabit bir oranda ölçüldüğünü varsaymaktan daha iyi bir çözüm sağlayıp sağlamadığını merak ediyorum.
VLC

Düzgün olmayan bir şekilde örneklenmiş olsa bile, yine de sinc () enterpolasyonunu kullanabilirsiniz (veya farklı bir yüksek düzeyde örneklenmiş alçak geçiren filtre). Bu spline veya pchip'ten daha iyi sonuçlar verebilir
Hilmar

1
@Hilmar: Haklısın. Verileri yeniden örneklemenin birkaç yolu vardır; yaklaşık samimi enterpolasyon, sınırsız yeniden örnekleme için "ideal" yöntem olacaktır.
Jason R

15

Yol nedeniyle Savitzky-Golay filtre türetilmektedir (yani, lokal en küçük kareler çokterimli gibi), düzgün olmayan örnekleme doğal genelleme vardır - sadece çok fazla hesaplama pahalıdır.

Genel olarak Savitzky-Golay Filtreleri

Standart filtre için, fikir bir polinomu yerel bir örnek grubuna (en az kareler kullanarak) takmak, daha sonra merkez numuneyi merkez indeksindeki (yani 0'da) polinom değeriyle değiştirmektir. Bu, standart SG filtre katsayılarının örnek göstergelerin bir Vandermonde matrisini ters çevirerek üretilebileceği anlamına gelir . Örneğin, beş numunesi (yerel göstergeler -2, -1,0,1,2 ile) arasında yerel bir parabolik uyum oluşturmak için A c = y tasarım denklemleri sistemi aşağıdaki gibi olacaktır:y0y4Ac=y

[202122101112000102101112202122][c0c1c2]=[y0y1y2y3y4].

Yukarıda, polinom en küçük kareler bilinmeyen katsayıları c 0 + c 1 x + c 2 x 2 . En polinom değeri yana x = 0 , sadece bir Cı- 0 işlem, pseudoinverse (yani matrisinin c = ( A , T bir ) - 1 bir T y üst üste SG filtre katsayılarını verecektir). Bu durumda,c0c2c0+c1x+c2x2x=0c0c=(ATA)1ATy 

[c0c1c2]=[312171237404753535][y0y1y2y3y4].

Not bu türevi yana olduğu Cı- 1 + 2 , c 2 x (değerlendirir matris ikinci sıra c 1 ) düz bir türevi filtre olacaktır. Aynı argüman ardışık satırlar için de geçerlidir - düzgünleştirilmiş yüksek mertebeden türevler verir. İlk satırın Wikipedia'da verilen düzeltme katsayılarıyla (yukarıda) eşleşmesi için matrisi 35 oranında ölçeklendirdiğimi unutmayın. Türev filtrelerin her biri diğer ölçeklendirme faktörlerine göre farklılık gösterir.c0+c1x+c2x2c1+2c2xc1

Düzgün Olmayan Örnekleme

Numuneler eşit aralıklarla yerleştirildiğinde, filtre katsayıları çeviri-değişmezdir, bu nedenle sonuç sadece bir FIR filtresidir. Düzgün olmayan numuneler için katsayılar, yerel numune aralığına bağlı olarak farklılık gösterecektir, bu nedenle tasarım matrisinin her örnekte oluşturulması ve ters çevrilmesi gerekecektir. Üniform olmayan örnek süreleri ise , ve yerel koordinat oluşturmak t , n sabit her bir merkez numune süresi ile , 0 , yanixntn0

t2=x2x0t1=x1x0t0=x0x0t1=x1x0t2=x2x0

her bir tasarım matrisi aşağıdaki biçimde olacaktır:

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.


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


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!

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.