拟合曲线趋势

算法背景

很多仪表设备都能定时产生一些数据,称之为时间序列

下图是一张时间序列的走势图,横轴是时间(dd hh:mm),纵轴是数值。

..

数据的波动很大,看局部会有点杂乱无章的感觉,但观察整张图又隐约可以看出曲线的走势(平稳——下降——平稳),能不能用数学方法把曲线的总体走势描述出来呢?这样将有利于分析设备仪表的工作状况。

答案是可以的。

下面就来介绍如何利用原始数据拟合出曲线的趋势线。

算法原理

我们把描述时间序列趋势的曲线称为主线。主线上相邻点之间的距离称为主线距离,同一时刻主线上的点与原值的距离称为原值距离。我们当然希望主线是平滑的,否则就和原值一样看起来杂乱无章了,所以所有的主线距离之和要尽量小,也就是说主线相邻点的值变化小,这样主线就会少一些剧变,更平滑一些。同时主线要能表征原值的特点,所以也要与原值接近,否则只考虑平滑时主线就成一条平直线了,这时候就要求所有的原值距离之和也要尽量小,这样主线偏离原值就更少。以这两者加权后的总和最小为目标,就可以拟合出一条主线,而调整权重则可以使主线更平滑或者更接近原值。

算法过程

时间序列用X表示:

X=[x1,x2,…,xn]

其中xi是序列X的第i个元素。

设主线为M

M=[m1,m2,…,mn]

其中mi是序列M的第i个元素。

1.      计算主线距离

主线距离用MD表示

..

原值距离用VD表示。

..

2.      平衡两类距离的最小值

设置平衡系数k来平衡两类距离的权重,则总距离D

D=VD+k*MD

3.      优化mi使总距离D最短

对每个mi求偏导:

..

转换后的问题变为解多元线性方程问题。

系数矩阵用C表示

..

矩阵中未填写数值处都是0

C*M=X

解上述方程就得到了主线M

M=linefit(C,X)

其中linefit()是解线性方程的函数。

这里需要说明两点:

1.      平衡系数调节两类距离的权重,k越大,主线距离MD占比越大,主线越平滑,k越小,原值距离VD占比越大,主线越接近原值。当k=0时,主线就是原值。当k=∞时,主线是一个常数,常数值等于原值的平均值。

2.      系数矩阵C是一个n*n方阵,因此它的计算受到了这个方阵大小的限制(n很大时,计算机内存很可能存不下这个n*n方阵)。仔细观察系数矩阵C,可以将其转换一个n*3的矩阵,利用矩阵变换规则同样可以求得主线M,这样就可以求n很大时的主线。

写出代码


A

B

C

D

E

1

=seq

/输入参数:时间序列 X




2

=k

/平衡系数:k




3

=func(A6,A1,A2)

/拟合主线


4

return A3




5

/拟合主线 2,参数:时间序列 X,参数 k



6

func





7


1e15




8


=if(B6==0,A6,if(B6>=B7,((avg=round(A6.avg(),6),A6.(avg))),func(A11,A6,B6)))

9


return B8

/k=0时,主线=原值。k=∞时,主线=常数=原值的平均值。

10

/拟合主线(相邻两数之差的平方和 + 原值与该数的差的平方和最小),参数:序列,参数 k

11

func





12


=transpose(A11)

/x



13


=B12.len()



14


=B13.(if(#==1,[B11+1,-B11,0],if(#==B13,[0,-B11,B11+1],[-B11,2*B11+1,-B11])))

/系数矩阵 C

15


=func(A18,B14,B12)

/矩阵变换



16


=func(A37,B15(1),B15(2))

/解方程



17

/矩阵变换,参数:系数矩阵 C,原值 X



18

func





19


for A18.len()

1e-7



20



if B19==1

=A18(B19).select@1(abs(~)>C19)

/除数

21



else if B19==A18.len()

22




=A18(B19).m(2)

23




=A18(B19)=A18(B19)++A18(B19-1).(~*(-D22))

/更新 c=c+c[-1]

24




=B18(B19)=B18(B19)++B18(B19-1).(~*(-D22))

/更新 x=x+c[-1]

25




=D20=D23.m(-1)

/除数

26



else

=A18(B19-1).pselect@a(abs(~)>C19)

/错位

27




=A18(B19).~

28




=A18(B19)(to(2))=A18(B19)(to(2))++A18(B19-1)(D26).(~*(-D27))

/更新 c=c+c[-1]

29




=B18(B19)=B18(B19)++B18(B19-1).(~*(-D27))

/更新 x=x+c[-1]

30




=D20=D28.m(-1)

/除数

31



=A18(B19).(~/D20)

/c除以除数

32



=A18(B19)=C31

/更新 c


33



=B18(B19).(~/D20)

/x除以除数

34



=B18(B19)=C33

/更新 x


35


return [A18,B18]



36

/解方程,参数:系数矩阵 C,X




37

func





38


=A37.(if(#==1,~.m(:2),~.m(2:)))

/第一行是前两个元素,其他是后两个元素

39


=B38.rvs()

/c倒序



40


=B37.rvs().conj()

/x倒序



41


=B40.iterate@a(if(#==1,~~,~-(B39(#).m(-1)*~~)),B40.~)

/迭代更新 c

42


=B41.rvs().(round(~,6))

/恢复原序


输入参数说明:

1.      seq:时间序列X

2.      k:平衡系数kk越大,主线越平滑。

3.      代码是用矩阵变化的方式解方程的,因此n可以很大。

 

应用举例

时间序列X如下图:

..

图中横轴是时间(dd hh:mm),纵轴是序列中的值。

不同k的主线如下图:

..

M_10k=10时的主线。

M_100k=100时的主线。

M_1000k=1000时的主线。

k越大时,主线越平滑。