在对信号做频域分析时,如果有趋势项的存在,会对分析形成干扰。仿真一个有斜率的线性信号,仿真信号及其经过傅里叶转换后的频谱如下图(左图为时域图、右图为频域图)。由此可见斜坡信号中低频信号幅值较大,随着频率增加,幅值减小。
我们再来仿真一个线性信号与正弦信号的叠加信号。正弦信号周期为100s,幅值为0.2。该信号的时域图和频谱图如下图所示(左图为时域图、右图为频域图)。由于我们已知正弦信号的周期,因此这个信号应该在频率为0.01Hz处,如红圈标注所示。但是我们会发现红圈处的幅值并不是0.2,对比上图可知,这其实是由于线性信号在0.01Hz处也有一个分量,所以两个信号叠加之后,幅值比0.2大。
由上述示例可知,趋势项的存在会影响频谱分析,因此需要对信号或者数据去趋势。
Maltab对该函数的官方介绍detrend, Remove polynomial trend。detrend函数通过设置参数,不仅可以移除线性趋势,也能移除多阶趋势。
下面是一个官方示例:
t = 0:20;
x = 3*sin(t) + t;
y = detrend(x);
plot(t,x,t,y,t,x-y,':k')
legend('Input Data','Detrended Data','Trend','Location','northwest')
Python中也有这个函数,官方介绍scipy.signal.detrend,不过只能移除线性趋势。如果要移除多阶趋势,需要使用obspy.signal.detrend.polynomial。
在博客 预处理丨去趋势(Matlab和C++)中提到了来自lppier 的C代码实现,不过这里的detrend函数目前只能移除线性趋势,代码如下。
/************************************************************************************
Function : void detrend_IP(T *y, T *x, int m)
Description : Remove the linear trend of the input floating point data. Note that this
will initialize a work buffer inside the function. So if you are calling
this many, many times, create your work buffer in the calling scope and call
detrend(T *y, T*x, int m) instead to avoid initializing memory over and over
again.
Inputs : y - Floating point input data
m - Input data length
Outputs : y - Data with linear trend removed
Copyright : DSO National Laboratories
History : 01/02/2008, TCK, Adapted from HYC code
01/12/2008, TCK, Added in return value
25/01/2016, Pier, Changed into template type, removed need for work buffer
*************************************************************************************/
template
void Detrend::detrend_IP(T *y, int m)
{
T xmean, ymean;
int i;
T temp;
T Sxy;
T Sxx;
T grad;
T yint;
std::unique_ptr x(new T[m]);
/********************************
Set the X axis Liner Values
*********************************/
for (i = 0; i < m; i++)
x[i] = i;
/********************************
Calculate the mean of x and y
*********************************/
xmean = 0;
ymean = 0;
for (i = 0; i < m; i++)
{
xmean += x[i];
ymean += y[i];
}
xmean /= m;
ymean /= m;
/********************************
Calculate Covariance
*********************************/
temp = 0;
for (i = 0; i < m; i++)
temp += x[i] * y[i];
Sxy = temp / m - xmean * ymean;
temp = 0;
for (i = 0; i < m; i++)
temp += x[i] * x[i];
Sxx = temp / m - xmean * xmean;
/********************************
Calculate Gradient and Y intercept
*********************************/
grad = Sxy / Sxx;
yint = -grad * xmean + ymean;
/********************************
Removing Linear Trend
*********************************/
for (i = 0; i < m; i++)
y[i] = y[i] - (grad * i + yint);
}
该段代码的实现原理实际使用了简单线性回归模型。
趋势项模型如下:
优化的指标函数设置为残差的平方,残差越小,则找了效果最好的线性回归:
为了使残差最小,通过对b求偏导,使其导数为0,则:
对a求偏导,使其导数为0,则: