比如有的时间序列的周期是按天,有的按周,有的按两周,有什么方法可以自动找到时间序列的周期?
3个回答
算周期简单的方法是计算自相关(autocorrelation)。如果要计算所有可能位移的自相关系数,计算量为$\mathcal{O}(n^2)$。这时通常用换到频域,根据时域卷积等于频域相乘,利用FFT和IFFT把计算量减小到$\mathcal{O}(n\log{}n)$。如果只计算个别位移的自相关系数,可以直接计算。
做了个实验。
import numpy as np
from numpy.fft import fft,ifft
import matplotlib.pyplot as plt
from sklearn import linear_model
N=1024 # days
m=8 # cycles
# np.random.seed(0)
x = np.linspace(0,m*2*np.pi, N) # N days= m cycles, N/m days/cycle
y0=np.sin(x+1.)
SNR=1./10
noise_std=y0.std()*np.sqrt(1/SNR)
y=y0+np.random.randn(N)*noise_std+x*.5
# y=y-y.mean()
# y=y/y.std()
regr = linear_model.LinearRegression()
regr.fit(x.reshape([-1,1]), y.reshape([-1,1]))
y_slow = regr.predict(x.reshape([-1,1])).flatten()
# y_slow=0
y_residual=y-y_slow
plt.plot(y,label='y')
plt.plot(y_slow,label='y slow')
plt.plot(y_residual,label='y residual')
plt.title('y')
plt.legend()
plt.show()
# FFT to calculate auto-correlation
y_fft=fft(y_residual)
S=y_fft*y_fft
y_corr=ifft(S)
plt.plot(np.absolute(S),'-*')
plt.xlim([0,N/2])
plt.title('|S|')
plt.show()
cycles=np.argmax(np.absolute(S)[:N/2])
print ('# of cycles=%d'%cycles)
print ('%d days/cycle'%(N*1./cycles))
print ('frequency =%.5f'%(cycles*1./N))
# temp=S*0
# temp[max_index]=1
# y_corr=ifft(temp)
# plt.plot(y_corr)
# plt.show()
# of cycles=8
128 days/cycle
frequency =0.00781
谢谢
-
zhaijing
2019-03-13 10:14
你这个例子是个确定性信号吧,如果是随机信号,是不是先求自相关函数,然后在傅里叶变换到功率谱密度。
-
shilongcn
2019-03-13 11:00
加了噪声,已经是随机信号。不求自相关函数,直接通过fft求功率谱密度函数。
-
Zealing
2019-03-13 11:36
谢谢,受教了,频率谱转功率谱的时候,频率范围缩小为了N/2,为什么呢?有没有相关资料呀?
-
shilongcn
2019-03-13 16:06
那是因为当输入为实数时,DFT系数共轭偶对称$X_k=X^*_{N-k}$。
-
Zealing
2019-03-13 16:55
Thank you!
-
shilongcn
2019-03-14 14:05
一个是方法是画图,直接肉眼观察
还有一个方法就是利用自相关系数来确定周期(什么是自相关系数)
SofaSofa数据科学社区DS面试题库 DS面经
谢谢,学习了
-
zhaijing
2019-03-12 11:07
我猜你是想把时序数据分成趋势数据、周期数据、残差数据,好多工具呢,比如facebook家就开源了一个。
SofaSofa数据科学社区DS面试题库 DS面经
谢谢
-
zhaijing
2019-03-13 10:14