等間隔ではないデータ用のSavitzky-Golay平滑化フィルター


16

100Hzで測定される信号があり、この信号にSavitzky-Golay平滑化フィルターを適用する必要があります。ただし、綿密な検査では、信号は完全に一定の速度で測定されず、測定間のデルタは9.7〜10.3 msの範囲です。

等間隔ではないデータにSavitzky-Golayフィルターを使用する方法はありますか?他に適用できる方法はありますか?


Gorryによる1991年の論文は、まさにこの正確な主題datapdf.com/…に掲載されています。しかし、tldr、datageistの答えは正しいメインアイデアです(ローカル最小二乗法)。Gorryが観察しているのは、係数が独立変数のみに依存し、従属変数では線形であるということです(Savitzky-Golayなど)。その後、彼はそれらを計算する方法を提供しますが、最適化されたライブラリを作成していない場合は、古い最小二乗フィッタを使用できます。
デイブプリチャード

回答:


5

1つの方法は、等間隔になるようにデータをリサンプリングすることです。その後、任意の処理を実行できます。データの間隔が均一ではないため、線形フィルタリングを使用した帯域制限リサンプリングは適切なオプションではありません。そのため、ある種のローカル多項式補間(キュービックスプラインなど)を使用して、基になる信号の値が「正確」であるものを推定できます10ミリ秒間隔。


最後の手段として、このソリューションを念頭に置いていました。私の信号が一定の速度で測定されると仮定するよりも、このアプローチが最終的により良い解決策を提供するかどうか疑問に思っています。
VLC

不均一にサンプリングされたとしても、sinc()補間(または別の高度にサンプリングされたローパスフィルター)を使用できると思います。これにより、スプラインやチップよりも良い結果が得られる場合があります
ヒルマー

1
@ヒルマー:あなたは正しい。データをリサンプリングできる方法はいくつかあります。近似sinc補間は、帯域制限リサンプリングの「理想的な」方法です。
ジェイソンR

15

方法のためサビツキー・ゴーレイフィルタが導出される(つまり、ローカル最小二乗として多項式フィット)、自然な一般化は、不均一なサンプリングにあります-それだけではるかに計算コストが高いのです。

一般的なSavitzky-Golayフィルター

y0y4Ac=y

[202122101112000102101112202122][c0c1c2]=[y0y1y2y3y4]

c0c2c0+c1バツ+c2バツ2バツ=0c0c=ATA1ATy 

[c0c1c2]=[312171237404753535][y0y1y2y3y4]

c0+c1バツ+c2バツ2c1+2c2バツc1

不均一サンプリング

バツntn0

t2=バツ2バツ0t1=バツ1バツ0t0=バツ0バツ0t1=バツ1バツ0t2=バツ2バツ0

各設計マトリックスは次の形式になります。

A=[t20t21t22t10t11t12t00t01t02t10t11t12t20t21t22]=[1t2t221t1t121001t1t121t2t22]

A c0


O(log(n))からO(n ^ 2)に移動するように聞こえます。
EngrStudent-モニカの復活

以下に、datageistが上向きに記述したScalaの実装を示します。
中コア

1
@Mediumcore元の投稿へのリンクを追加しませんでした。また、質問への回答を提供しなかったため、削除しました。データガイストの投稿を編集してリンクを追加してください。レビュー後にモデレートされます。
ピーターK。

4

「安い代わりに、一つは単純にデータポイントがあることふりをすることができている 等間隔...
で変更した場合f の全幅にわたって N ポイントウィンドウが N/2 単一のポイントで測定ノイズを掛けると、安価な方法を使用できます。」
数値レシピ pp。771-772

(派生は誰ですか?)

(「等間隔のふりをする」という意味:
最も近い±N/2 それぞれの周りのポイント t SavGol(t)、
すべてをスナップしないt。それは明らかかもしれませんが、しばらくの間私を手に入れました。


1

Matlabでsavitzky-golayアルゴリズムを使用するには2つの方法があることがわかりました。1回はフィルターとして、1回は平滑化機能として使用しますが、基本的には同じことを行う必要があります。

  1. yy = sgolayfilt(y、k、f):ここで、値y = y(x)はxで等間隔であると想定されます。
  2. yy = smooth(x、y、span、 'sgolay'、degree):ここで、xを追加入力として使用でき、Matlabヘルプを参照するとxを等間隔にする必要はありません!

0

何らかの助けがあれば、datageistによって記述されたメソッドのC実装を作成しました。ご自身の責任で自由に使用してください。

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

スムージング

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.