Savitzky-Golay चौरसाई करने के लिए समान रूप से डेटा नहीं छनन फ़िल्टर


16

मेरे पास एक सिग्नल है जिसे 100Hz पर मापा जाता है और मुझे इस सिग्नल पर सविट्ज़की-गोलय स्मूथिंग फ़िल्टर लागू करने की आवश्यकता है। हालांकि, करीब निरीक्षण पर मेरा संकेत पूरी तरह से स्थिर दर पर मापा नहीं गया है, माप के बीच डेल्टा 9.7 और 10.3 एमएस के बीच है।

वहाँ समान रूप से स्थानिक डेटा पर Savitzky-Golay फ़िल्टर का उपयोग करने का एक तरीका है? क्या अन्य तरीके हैं जो मैं लागू कर सकता हूं?


1991 में गोरी द्वारा किया गया एक पेपर बहुत ही सटीक विषय datapdf.com/ पर है । लेकिन tldr, datageist का उत्तर सही मुख्य विचार (स्थानीय न्यूनतम-वर्ग) है। गोर्री क्या देखती है कि गुणांक केवल स्वतंत्र चर पर निर्भर करते हैं और निर्भर चर (जैसे सविट्ज़की-गोलय) में रैखिक होते हैं। फिर वह उन्हें गणना करने का एक तरीका देता है, लेकिन यदि आप एक अनुकूलित पुस्तकालय नहीं लिख रहे हैं, तो किसी भी पुराने न्यूनतम वर्ग के फिटर का उपयोग किया जा सकता है।
डेव प्रिचर्ड

जवाबों:


5

एक तरीका यह होगा कि आप अपने डेटा को फिर से तैयार कर लें ताकि यह समान रूप से फ़ैल जाए, फिर आप जो चाहें प्रोसेसिंग कर सकते हैं। रेखीय फ़िल्टरिंग का उपयोग करते हुए बैंडलेडेड रेज़म्पलिंग एक अच्छा विकल्प नहीं है क्योंकि डेटा समान रूप से नहीं दिया गया है, इसलिए आप अनुमान लगाने के लिए स्थानीय बहुपद इंटरपोलेशन (जैसे क्यूबिक स्प्लिन) का उपयोग कर सकते हैं ताकि यह अनुमान लगाया जा सके कि अंतर्निहित संकेत के मान "सटीक" हैं। 10-मिलीसेकंड अंतराल।


मेरे पास यह उपाय अंतिम उपाय के रूप में था। मैं सोच रहा था कि अंत में यह दृष्टिकोण सिर्फ यह मानने की तुलना में बेहतर समाधान देता है कि मेरा संकेत एक स्थिर दर पर मापा जाता है।
VLC

मुझे लगता है कि भले ही यह गैर-समान रूप से सैंपल किया गया हो, फिर भी आप sinc () इंटरपोलेशन (या अलग-अलग अत्यधिक सैंपल वाले कम फिल्टर) का उपयोग कर सकते हैं। यह एक पट्टी या एक pchip तुलना में बेहतर परिणाम दे सकता है
Hilmar

1
@ हिलमार: आप सही हैं। ऐसे कई तरीके हैं जिनसे आप डेटा को फिर से खोल सकते हैं; अनुमानित सिन इंटरपोलेशन, बैंडलेडेड रेज़म्पलिंग के लिए "आदर्श" विधि होगी।
जेसन आर

15

सवित्स्की-गोले फ़िल्टर को जिस तरह से व्युत्पन्न किया गया है (यानी स्थानीय कम-वर्ग बहुपद फिट बैठता है) के कारण, गैर-वर्दी नमूनाकरण के लिए एक प्राकृतिक सामान्यीकरण है - यह सिर्फ अधिक कम्प्यूटेशनल रूप से महंगा है।

सविट्ज़की-गॉले फिल्टर जनरल में

y0y4Ac=y

[202122101112000102101112202122][c0c1c2]=[y0y1y2y3y4].

In the above, the c0c2 are the unknown coefficients of the least squares polynomial c0+c1x+c2x2. Since the value of the polynomial at x=0 is just c0, computing the pseudoinverse of the design matrix (i.e. c=(ATA)1ATy ) will yield the SG filter coefficients in the top row. In this case, they would be

[c0c1c2]=[312171237404753535][y0y1y2y3y4].

Note that since the derivative of c0+c1x+c2x2 is c1+2c2x, the second row of the matrix (which evaluates c1) will be a smoothed derivative filter. The same argument applies for successive rows--they give smoothed higher-order derivatives. Note that I scaled the matrix by 35 so the first row would match the smoothing coefficients given on Wikipedia (above). The derivative filters each differ by other scaling factors.

Nonuniform Sampling

When the samples are evenly spaced, the filter coefficients are translation-invariant, so the result is just an FIR filter. For nonuniform samples, the coefficients will differ based on the local sample spacing, so the design matrix will need to be constructed and inverted at each sample. If the nonuniform sample times are xn, and we construct local coordinates tn with each center sample time fixed at 0, i.e.

t2=x2x0t1=x1x0t0=x0x0t1=x1x0t2=x2x0

then each design matrix will be of the following form:

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.