간격이 동일하지 않은 데이터를위한 Savitzky-Golay 스무딩 필터


16

100Hz로 측정 된 신호가 있으며이 신호에 Savitzky-Golay 스무딩 필터를 적용해야합니다. 그러나 면밀히 검사하면 신호가 완벽하게 일정한 속도로 측정되지 않고 측정 간 델타의 범위는 9.7 ~ 10.3ms입니다.

간격이 동일하지 않은 데이터에 대해 Savitzky-Golay 필터를 사용하는 방법이 있습니까? 적용 할 수있는 다른 방법이 있습니까?


Gorry의 1991 년 논문은이 정확한 주제 datapdf.com/…에 거의 있습니다. 그러나 데이터 게스트의 대답 인 tldr이 올바른 주요 아이디어입니다 (로컬 최소 자승법). Gorry가 관찰 한 것은 계수가 독립 변수에만 의존하고 종속 변수 (Savitzky-Golay와 같은)에서 선형이라는 점입니다. 그런 다음 계산 방법을 제공하지만 최적화 된 라이브러리를 작성하지 않으면 오래된 최소 제곱 피팅을 사용할 수 있습니다.
Dave Pritchard

답변:


5

한 가지 방법은 동일한 간격으로 데이터를 리샘플링하여 원하는 처리를 수행하는 것입니다. 선형 필터링을 사용한 대역 제한 리샘플링은 데이터의 간격이 균일하지 않기 때문에 좋은 옵션이되지 않습니다. 따라서 일종의 로컬 다항식 보간 (예 : 입방 스플라인)을 사용하여 기본 신호의 값이 "정확한"값을 추정 할 수 있습니다 10 밀리 초 간격


최후의 수단 으로이 솔루션을 염두에 두었습니다. 결국이 방법이 내 신호가 일정한 속도로 측정된다고 가정하는 것보다 더 나은 솔루션을 제공하는지 궁금합니다.
VLC

불균일하게 샘플링 된 경우에도 sinc () 보간 (또는 다른 고 샘플링 저역 통과 필터)을 계속 사용할 수 있다고 생각합니다. 스플라인이나 pchip보다 더 나은 결과를 얻을 수 있습니다
Hilmar

1
@ Hilmar : 맞습니다. 데이터를 리샘플링 할 수있는 여러 가지 방법이 있습니다. 근사 sinc 보간은 대역 제한 리샘플링을위한 "이상적인"방법입니다.
Jason R

15

길의 때문에 Savitzky - 골 레이 필터가 유도된다 (즉, 지역의 최소 자승 다항식 맞는로), 비 균일 샘플링을 자연스럽게 일반화있다 - 그것은 단지 더 많은 계산 비용입니다.

일반적인 Savitzky-Golay 필터

표준 필터의 경우, 아이디어는 다항식을 로컬 샘플 집합 (최소 제곱 사용)에 맞추고 중심 샘플을 중심 인덱스 (즉, 0)의 다항식 값으로 대체하는 것입니다. 이는 표준 SG 필터 계수 가 샘플 지수 의 Vandermonde 매트릭스 를 반전시켜 생성 될 수 있음을 의미합니다 . 예를 들어, 5 개의 샘플 (로컬 인덱스 -2, -1,0,1,2)에 대해 로컬 포물선 피팅을 생성 하려면 설계 방정식 시스템 는 다음과 같습니다. A c = y와이0와이4=와이

[202122101112000102101112202122][012]=[와이0와이1와이2와이와이4].

위의 는 최소 제곱 다항식 의 알려지지 않은 계수입니다 . 에서의 다항식의 값 은 단지 이므로 설계 행렬 의 의사 를 계산하면 (예 : ) SG 필터 계수가 맨 윗줄. 이 경우에는c 0 + c 1 x + c 2 x 2 x = 0 c 0 c = ( A T A ) 1 A T y020+1엑스+2엑스2엑스=00=()1와이 

[012]=[12171274047555][와이0와이1와이2와이와이4].

의 미분 은 이므로 행렬의 두 번째 행 ( 평가 )은 스무딩 된 미분 필터입니다. 연속적인 행에 대해서도 같은 주장이 적용됩니다. 첫 번째 행이 위의 Wikipedia에 주어진 평활 계수와 일치하도록 행렬의 크기를 35로 조정했습니다. 미분 필터는 각각 다른 스케일링 계수에 따라 다릅니다.c 1 + 2 c 2 x c 10+1엑스+2엑스21+22엑스1

비 균일 샘플링

샘플의 간격이 균일하면 필터 계수가 변환 불변 값이므로 결과는 FIR 필터 일뿐입니다. 균일하지 않은 샘플의 경우 계수는 로컬 샘플 간격에 따라 달라 지므로 각 샘플에서 디자인 매트릭스를 구성하고 반전해야합니다. 비 균일 샘플 시간 인 경우 , 우리는 로컬 좌표를 구성 고정 각 센터의 샘플 시간 , 즉,엑스0

2=엑스2엑스01=엑스1엑스00=엑스0엑스01=엑스1엑스02=엑스2엑스0

각 디자인 매트릭스는 다음과 같은 형식입니다.

=[202122101112000102101112202122]=[1222111210011121222].

로컬 샘플 값 으로 점으로 구분 된 의 의사 역행의 첫 번째 행은 해당 샘플에서 평활화 된 값인 을 생성 합니다. 0


O (log (n))에서 O (n ^ 2)로 이동하는 것처럼 들립니다.
EngrStudent-복직 모니카

다음은 datageist가 설명하는 Scala의 구현입니다.
중간 코어

1
@Mediumcore 원본 게시물에 대한 링크를 추가하지 않았습니다. 또한 질문에 대한 답변을 제공하지 않았기 때문에 삭제했습니다. 링크를 추가하려면 datageist의 게시물을 편집하십시오. 검토 후 검토됩니다.
Peter K.

4

"저렴한 대안으로서, 데이터 포인트 동일한 간격을두고 있다고 가정 할 수 있습니다 . 포인트 창의 전체 폭에 걸친
의 변화 가 측정 노이즈의 배 보다 작은 경우 단일 지점이라면 저렴한 방법을 사용할 수 있습니다. " qquad- 수치 레시피 pp. 771-772 에프/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;
}

다듬기

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.