Savitzky-Golay平滑滤波器,用于间隔不相等的数据


16

我有一个在100Hz下测量的信号,我需要对该信号应用Savitzky-Golay平滑滤波器。但是,仔细检查后,我的信号并不是以完全恒定的速率测量的,测量之间的差值介于9.7到10.3 ms之间。

有没有办法对不等距的数据使用Savitzky-Golay滤波器?还有其他可以应用的方法吗?


戈里(Gorry)于1991年发表的一篇论文几乎就是关于这个确切的主题的。但是,数据专家的答案是正确的主要思想(局部最小二乘)。Gorry观察到的是,系数仅取决于自变量,并且在因变量中是线性的(例如Savitzky-Golay)。然后,他给出了一种计算方法,但是,如果您没有编写优化的库,则可以使用任何旧的最小二乘拟合器。
Dave Pritchard

Answers:


5

一种方法是对数据进行重新采样,以使其等距分布,然后您可以执行所需的任何处理。使用线性滤波的带限重采样将不是一个好的选择,因为数据不是均匀间隔的,因此您可以使用某种局部多项式插值(例如三次样条)来估计底层信号的值“精确”间隔为10毫秒。


作为最后的选择,我想到了这个解决方案。我想知道最终这种方法是否能提供比仅假设以恒定速率测量我的信号更好的解决方案。
VLC 2012年

我认为,即使采样不均匀,您仍然可以使用sinc()插值(或其他不同的高采样低通滤波器)。这可能比花键或pchip产生更好的结果
Hilmar

1
@希尔玛:你是对的。您可以通过多种方法对数据进行重新采样。近似Sinc插值将是带限重采样的“理想”方法。
杰森R

15

由于Savitzky-Golay滤波器的导出方式(即作为局部最小二乘多项式拟合),非自然采样得到了自然的概括,这在计算上要昂贵得多。

一般的Savitzky-Golay滤波器

对于标准滤波器,其想法是将多项式拟合到局部样本集(使用最小二乘法),然后将中心样本替换为位于中心索引处(即0)的多项式值。这意味着可以通过对样本索引的范德蒙德矩阵求逆来生成标准SG滤波器系数。例如,要在五个样本(具有局部指数-2,-1,0,1,2)上生成局部抛物线拟合,设计等式A c = y的系统如下:y0y4Ac=y

[202122101112000102101112202122][c0c1c2]=[y0y1y2y3y4].

在上面,是最小二乘多项式c 0 + c 1 x + c 2 x 2的未知系数。由于x = 0处的多项式的值仅为c 0,因此计算设计矩阵的伪逆(即c = A T A 1 A T y)将在顶部行产生SG滤波器系数。在这种情况下,c0c2c0+c1x+c2x2x=0c0c=(ATA)1ATy 

[c0c1c2]=[312171237404753535][y0y1y2y3y4].

请注意,由于c 1 + 2 c 2 x,矩阵的第二行(计算c 1)将是平滑的导数滤波器。相同的论点适用于连续的行-它们给出平滑的高阶导数。请注意,我将矩阵缩放了35,因此第一行将与Wikipedia(上)上给出的平滑系数匹配。导数滤波器各自因其他比例因子而不同。c0+c1x+c2x2c1+2c2xc1

非均匀采样

当样本均匀分布时,滤波器系数是平移不变的,因此结果只是一个FIR滤波器。对于非均匀样本,系数将基于局部样本间距而有所不同,因此将需要构造设计矩阵并在每个样本处对其进行求逆。如果非均匀采样时间为,则构造局部坐标t n,每个中心采样时间固定为0,即xntn0

t2=x2x0t1=x1x0t0=x0x0t1=x1x0t2=x2x0

那么每个设计矩阵将具有以下形式:

A=[t20t21t22t10t11t12t00t01t02t10t11t12t20t21t22]=[1t2t221t1t121001t1t121t2t22].

A c0


听起来像是从O(log(n))移到O(n ^ 2)。
EngrStudent-恢复莫妮卡

这是datageist向上描述的Scala实现。
中核

1
@Mediumcore您未在原始帖子中添加链接。另外,我删除了它,因为它没有提供问题的答案。请尝试编辑datageist的帖子以添加链接;审核后将进行审核。
Peter K.

4


Fññ/2
-

(派生任何人?)


±ñ/2ŤŤ
Ť一世一世


1

我发现,在Matlab中有两种使用savitzky-golay算法的方法。一次作为过滤器,一次作为平滑函数,但基本上它们应该做同样的事情。

  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;
}

平滑

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.