Savitzky-Golay-Glättungsfilter für nicht gleichmäßig verteilte Daten


16

Ich habe ein Signal, das bei 100 Hz gemessen wird, und ich muss das Savitzky-Golay-Glättungsfilter auf dieses Signal anwenden. Bei näherer Betrachtung wird mein Signal jedoch nicht mit einer vollkommen konstanten Rate gemessen, das Delta zwischen den Messungen liegt zwischen 9,7 und 10,3 ms.

Gibt es eine Möglichkeit, den Savitzky-Golay-Filter für nicht gleichmäßig verteilte Daten zu verwenden? Gibt es andere Methoden, die ich anwenden könnte?


Ein Artikel von Gorry aus dem Jahr 1991 befasst sich mit genau diesem Thema: datapdf.com/… . Aber die Antwort von tldr, datageist ist die richtige Grundidee (lokale kleinste Quadrate). Was Gorry bemerkt, ist, dass die Koeffizienten nur von den unabhängigen Variablen abhängen und in den abhängigen Variablen linear sind (wie Savitzky-Golay). Dann gibt er eine Möglichkeit, sie zu berechnen, aber wenn Sie keine optimierte Bibliothek schreiben, kann jeder alte Fitter mit den kleinsten Quadraten verwendet werden.
Dave Pritchard

Antworten:


5

Eine Methode wäre, Ihre Daten so neu abzutasten, dass sie gleichmäßig verteilt sind, und dann können Sie jede beliebige Verarbeitung durchführen. Das bandbegrenzte Resampling mit linearer Filterung ist keine gute Option, da die Daten nicht gleichmäßig verteilt sind. Sie können daher eine lokale Polynominterpolation (z. B. kubische Splines) verwenden, um die Werte des zugrunde liegenden Signals auf "genau" zu schätzen. 10-Millisekunden-Intervalle.


Ich hatte diese Lösung als letzten Ausweg im Sinn. Ich frage mich, ob dieser Ansatz letztendlich eine bessere Lösung bietet, als nur anzunehmen, dass mein Signal mit einer konstanten Rate gemessen wird.
VLC

Ich denke, auch wenn es ungleichmäßig abgetastet ist, können Sie immer noch sinc () - Interpolation (oder ein anderes hoch abgetastetes Tiefpassfilter) verwenden. Dies kann zu besseren Ergebnissen führen als ein Spline oder ein Chip
Hilmar

1
@Hilmar: Du hast recht. Es gibt eine Reihe von Möglichkeiten, die Daten erneut abzutasten. Eine ungefähre Sinusinterpolation wäre die "ideale" Methode für bandbegrenztes Resampling.
Jason R

15

Aufgrund der Art und Weise, wie das Savitzky-Golay-Filter abgeleitet wird (dh als lokale Polynomanpassungen der kleinsten Quadrate), gibt es eine natürliche Verallgemeinerung der ungleichmäßigen Abtastung - es ist nur viel rechenintensiver.

Savitzky-Golay-Filter im Allgemeinen

Für den Standardfilter besteht die Idee darin, ein Polynom an eine lokale Menge von Abtastwerten anzupassen [unter Verwendung der kleinsten Quadrate] und dann das mittlere Abtastwert durch den Wert des Polynoms am mittleren Index (dh bei 0) zu ersetzen. Das heißt, die Standard-SG-Filterkoeffizienten können durch Invertieren einer Vandermonde-Matrix von Abtastwertangaben erzeugt werden. Um beispielsweise eine lokale parabolische Anpassung über fünf Stichproben (mit lokalen Angaben -2, -1, 0 , 1, 2) zu erzeugen, wäre das System der Entwurfsgleichungen A c = y wie folgt:y0y4EINc=y

[-20-21-22-10-11-12000102101112202122][c0c1c2]=[y0y1y2y3y4].

Oben sind die die unbekannten Koeffizienten des Polynoms der kleinsten Quadrate c 0 + c 1 x + c 2 x 2 . Da der Wert des Polynoms bei x = 0 gerade c 0 ist , ergibt die Berechnung der Pseudoinverse der Entwurfsmatrix (dh c = ( A T A ) - 1 A T y ) die SG-Filterkoeffizienten in der oberen Reihe. In diesem Fall wären siec0c2c0+c1x+c2x2x=0c0c=(EINTEIN)-1EINTy 

[c0c1c2]=[-3121712-3-7-40475-3-5-35][y0y1y2y3y4].

c0+c1x+c2x2c1+2c2xc1

Ungleichmäßige Probenahme

xntn0

t-2=x-2-x0t-1=x-1-x0t0=x0-x0t1=x1-x0t2=x2-x0

dann hat jede Entwurfsmatrix die folgende Form:

EIN=[t-20t-21t-22t-10t-11t-12t00t01t02t10t11t12t20t21t22]=[1t-2t-221t-1t-121001t1t121t2t22].

EIN c0


klingt wie es bewegt sich von O (log (n)) nach O (n ^ 2).
EngrStudent - Wiedereinsetzung von Monica

Hier ist eine Implementierung von Scala, die von datageist aufwärts beschrieben wird.
Mittlerer Kern

1
@Mediumcore Sie haben Ihrem ursprünglichen Beitrag keinen Link hinzugefügt. Außerdem habe ich es gelöscht, weil es keine Antwort auf die Frage lieferte. Bitte versuchen Sie, den Beitrag des Datagisten zu bearbeiten, um einen Link hinzuzufügen. Es wird nach Überprüfung moderiert.
Peter K.

4


fNN/2
-

(Ableitung jemand?)


±N/2tt
tichich


1

Ich habe herausgefunden, dass es zwei Möglichkeiten gibt, den Savitzky-Golay-Algorithmus in Matlab zu verwenden. Einmal als Filter und einmal als Glättungsfunktion, aber im Grunde sollten sie dasselbe tun.

  1. yy = sgolayfilt (y, k, f): Hier wird angenommen, dass die Werte y = y (x) in x gleich beabstandet sind.
  2. yy = smooth (x, y, span, 'sgolay', degree): Hier können Sie x als zusätzliche Eingabe haben und in Bezug auf die Matlab-Hilfe muss x nicht gleichmässig verteilt sein!

0

Wenn es hilfreich ist, habe ich eine C-Implementierung der von datageist beschriebenen Methode durchgeführt. Die Benutzung erfolgt auf eigenes Risiko.

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

Glättung

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.