时间序列专题之三 时间序列的分段线性表示
在研究如何对时间序列进行线性分段的时候,浏览了60篇左右论文和教材的片段,对其中的6篇仔细阅读并编写程序和单元测试实现相应的算法。同时为了直观的看到分段效果,还制作简易的曲线图呈现原始序列和分段序列。这种超负荷的工作,是在一周之内完成的,目的只有一个:选择算法。
作为程序员,实际上并不能算是研究人员,多数情况下,他只需要不同的苹果中选择一个苹果而已,没有必要去种苹果树。
但凡需要“选择”的时候,工作步骤如下:1、确定你想要达到的目的,这个最为重要,你的目的贯穿整个工作,千万不要在相亲的时候,突然对对方的妹妹格外关注;2、区分关注的层次比如,简要的阅读能够排除很多不需深究的东西,上面说到的60篇论文中的54篇要么是作者本身显得不妥、要么是某种方式的抄袭、要么其提供的分段图形本身就不符合要求,简单的五分钟你就能够排除,无需浪费时间。3、你感兴趣的算法各有优势和缺陷的时候,有无可能对某种主要的算法进行调整,或者组合应用其他算法的某些概念?4、实在找不到合适的算法,或者组合相应算法也无力达成的时候,能否基于你的需要而自行设计新的算法?当然,到这个层面,你也变成了那群做研究的书呆子之中的一员,不过一定要确定一点,至少你的目的明确,这和他们混稿费、混基金、呆在实验室空想是不同的,身为程序员你其实很有优势的。
下面对算法的描述,并没有采用那些很精确的命名,而只是从算法的特征来分类。事实上大约有十来种主流的算法和近百种各类扩展、调整、优化的算法,每个都号称自己效果如何好、效率如何高、怎样支持在线划分等,但我们没有必要陷入他们的战争。选择到最后确定几种分段算法,我个人用的时间是一周,过于沉湎细节的话,恐怕一个月都无法做决断。例图中使用深圳A股深发展在2009年和2010年的实际收盘价走势,黑线为原始数据,红线为拟合线段,红点为分段点。
一、对时间序列分段,是什么意思?
时间序列,在二维平面上实际上是一条曲线,所谓分段,就是用一系列首尾相接的线段,近似的表达一条曲线。
下图是将误差设为5级的分段图,这个粗略一些,用于长期趋势的分析。实际曲线有482个点,压缩后用22个点描绘实际曲线的趋势变化:
下面是误差设为3级的分段图,和原有曲线走势更加吻合一些,用于短期波动的分析:
二、为什么要对时间序列分段?
第一个原因,很多时候,我们关注时间序列的主要趋势变化,而不太关注具体的数值和少量的异常点,对序列分段,我们可以抓住重点。比如某个公司更关心其在各城市销售数量增长没有,今天1百万,明天90万,后天110万,大后天130万,增长了没有?所以分段实际上是表达趋势,你可以将时间序列用自然语言表达,即在10月1日到11月5日之间,销售量大致保持每天增长0.5%的趋势,11月5日到11月9日之间,销售量以每日平均下跌0.02%的趋势。
第二个原因,提高时间序列的运算性能。时间序列往往很长,数据量比较大,而序列的查询操作、聚类操作,往往涉及的计算量惊人,举例来说,2000个接近4000条记录的时间序列,遍历搜索一遍,在性能不错的笔记本上往往需要10个小时左右的时间。而缩小时间序列的长度,将大幅提高计算的效率,这个提高往往是50倍甚至100倍这样的概念。这往往让不可能的事情变得可能,比如你的一项分析需要运算一周才能出数据,届时黄花菜肯定是分外的凉,估计没有人会需要这样的软件系统。
第三个原因,在不同精度层面上搜索。我们经常需要对同一时间序列,做长期趋势、中期趋势和短期趋势的分析。比如我们可能需要搜索60天的数据,也可能需要搜索5天的数据。通过控制划分时间序列的压缩度,我们可以在不同层次上运算,一些时候考虑长远趋势,图像会平滑一些。一些时候考虑短期趋势,图像会灵敏一些。固然,我们能够通过使用移动平均、或者将时间周期提升一个层次,来对原始序列做预处理,但在不同的压缩度的情况下划分时间序列,则是比较通用的办法。明明可以这么简单,又何须额外考虑许多数据预处理之类的工作?
三、在选择分段算法的时候,我们的核心要求是什么?
1、可以调整搜索的精度,以获取更精细的分段和更粗略的分段。
2、用于调整精度的阀值,要易于理解,且与不同时间序列的具体数值无关。
3、同样的压缩比,我们希望分段的效果和原来的曲线更加吻合。
4、次要目标:希望性能较好。这个很多时候可以放弃。
5、次要目标:希望支持在线划分,也就是增量划分,这样我们可以保存分段的结果,新增数据后只要做少量的调整就行了。支持增量划分,则性能会达到顶点,不过要注意,前提是你要支持多少个不同精度的划分?如果算法参数过于复杂,则增量搜索要保存很多不同的分段结果,这是不合适的。所以,即使支持增量划分的算法,也要考虑你实际应用有没有可能使用的可能。
所以对算法优劣的评估标准,应是:在相同拟合误差的情形下,压缩比越高越好;算法的阀值,不应与具体序列的数值有关。
四、先说说我们结论:
1、分段算法,可粗略的分为全局算法和局部算法。全局算法在整个时间序列的基础上,寻找最优的分段集合。局部算法根据局部的特征,从左到右寻找符合要求的分段。
2、如果要精确表达,同时获得较好的压缩比,采用BU算法,当然这种算法的计算量较大,且不支持在线划分。如果要支持增量划分,采用SW或者层次聚类的算法。
3、多数情况下,极值点和特征点算法,由于是局部算法,其精度和压缩比不能很好的平衡,不予考虑。
4、分段和降噪应区分开来考虑,即先分段,然后根据需要对分段的结果降噪。这有助于简化思维,因为在分段的时候,精度和压缩比之间,本来存在矛盾,分段算法的关键在于两者间的平衡。而降噪,则是和精度、压缩比有关的,降噪会导致精度下降同时提高压缩比。
5、阀值的确定,应该容易理解,同时也要容易选择,目前尚没有发现能够自行根据曲线的统计特征确定阀值的算法。所以我们不妨对阀值做一定的变换,比如我们将拟合误差变换成一个百分比,即误差越大则分段越少,取值在0到100之间。基于距离的,我们可以将其变换为与原始点的值的比例。算法应尽可能避免和序列的实际值有关,要保证相同的阀值针对不同序列具备相同的分段效果。
6、永远不要考虑多维时间序列的问题,因为你能处理一维,就意味着你可以处理多维,此时,你仅需要考虑不同维度的先后秩序问题和权重问题,这个并不复杂。许多从事多维数据序列分析研究的专家,其实并不适合于做研究,因为既然涉及到这个领域并投入精力,本身说明其抽象思维方面素质太差。
四、全局算法
1、自顶向下TD算法:
时间序列的开始点和结束点,是首先选中的分段点。然后,遍历两点之间的所有点,找出和这两点连成的直线距离最大的点,如果这个点到直线的距离“大于”预先给定的阀值,我们将其称为R,则将它作为第三个分段点。这样我们就有了两个线段,做了最初步的划分。
之后,这个新增点到左边相邻点和右边相邻点构成的两条线段,继续寻找距离最大的点,然后,找到的两个点,谁与相应的线段距离最大,且这个距离“大于”阀值R,则该点作为第四个分段点….如此循环,直到再也找不到距离大于R的点,分段完成。
这个阀值,也就是点到线段的距离,可以使用正交距离(原始点和分段线段在该点的值的差的绝对值)、垂直距离(原始点到分段线段的直线的长度)和欧式距离,当然也可以设置其他的特性作为阀值,比如拟合误差、又比如弧度、角度、余弦等,由此可以引申很多种不同的算法,这也是多数教授们所乐于从事的研究,简单。我们一般选择垂直距离就行了。
这个阀值不太好理解,且与不同的时间序列具体取值有关,直接应用完全没有通用性。我本人在项目中,将其做一定的变换处理的。
2、自底向上BU算法:
这是TD算法的逆过程,首先将时间序列,划分为相邻点的短序列,当然此时的拟合误差为0,因为第一点和第二点的连线,原始点都落在线段上。将相邻两个线段连接起来,此时每条线段包含三个原始点,计算中间那个点的拟合误差。这样,所有这些三个点的线段中的中间点的拟合误差计算出来后,找出误差最小且误差小于阀值R的分段,作为第一条包含三个点的线段。
在上面的基础上,第一条分段同样的和相邻线段连接,然后计算每一条分段的拟合误差,再找出误差最小且小于阀值R的分段,作为第二个分段。
依次方式循环,直到所有分段的拟合误差都小于阀值R,分段结束。
当然,你同样可以使用正交距离、垂直距离等其他属性,由此算法又演变成多种不同的算法。
五、局部算法
1、固定窗口PAA算法:
这个最简易,阀值为R,表示你要将序列分成多少段。然后使用线段的长度除以这个值,得到窗口的长度L。从序列的第一点开始,到第L-1点作为第一段,用其平均值表示。这种算法实际上类似股票日线中的5日均价、10日均量。非常粗略,拟合不够精确。但这种算法,好象是时间序列分段研究最早的成果。
2、滑动窗口SW算法:
给一个窗口长度的最小值N和最大值M,然后从序列第一点开始,与第N点连线,计算各点拟合误差,如果误差总值小于给定的阀值R,拟合成功,增加窗口的长度,连线再计算拟合误差,如果拟合误差小于R,继续增加窗口长度一直到窗口长度为M。如果拟合误差大于R,则第一段结束,该点作为新的窗口的起始点,继续同样的过程,直到序列划分完毕。
这种算法属于局部算法,有方向性,从算法的原理来看,就容易漏报和将本应拟合的线段划分为两条线段。代码实现的效果来看,划分的不太理想。
3、极值点:
所谓极值点,是指曲线中的转折点,寻找极值点的算法很简单,从序列第二点开始遍历,没点如果同时比前后两点大,或者同时比前后两点小,那么这个点就是时间序列的一个极值点。找出所有极值点的集合,就得到了所有分段点。这种算法,是基于人类视角的关注点而来,但很显然存在两个问题:1、是局部算法,只考虑与相邻两点的问题,所以划分的时候容易过于精确而忽略趋势。2、只考虑了趋势反向问题,即从上升到下降和从下降到上升,没有考虑趋势加速问题,比如先是15%角度上升,然后是40%角度上升,之间的结合点显然漏掉了。这也意味着此种算法从原理上就不能精确表达趋势。优点也是有的,即计算简单,性能很好,支持在线划分,没有阀值。
4、特征点:
在极值点的基础上,加上判断,比如两个极值点之间的距离必须大于某个阀值,或者该极值点除了比前后点同时大或同时小之外,还需要大于某个比例阀值R。这样的改进意义不大,只是进一步减少极值点而已。
5、趋势点:
在极值点的基础上,考虑了趋势加速问题,即计算某点和相邻点的角度,或者弧度、或者余弦等,大于某个特定阀值R的称为趋势点,按照这种算法基本上不会遗漏掉极值点,趋势加速或减速的关键点也不会遗漏掉。不过这同样是一种局部算法,极值点方法其他毛病,这种算法并没有避免。
6、层次聚类:实际上仅仅运用了KMean算法的中心和半径的概念,其他没多少区别。这种算法的阀值比较多,而且看起来分类的效果不好,没有看点。不过这种算法,其分类的效果好于SW算法,也同样的支持在线划分。
六、时间序列自身特征问题:
一部分类型的时间序列,遵循一定的规律,价格逐日波动的过程相对平稳,另一些很可能出现前后两天变动数倍的情形。
这样,首先我们面临的问题,是能否针对不同业务含义的数据调整精度,以正常的分段;第二,是能否对数据进行预处理,以支持分段。
这两种方式,事实上都与业务系统相关。
反过来说,我们需要研究,能否通过自动分析数据本身的特征,由算法来决定是否需要预处理、基于何种模式设置何种精度合适。这里涉及到的序列特征,包括序列的极值(最大与最小的差距)、序列的标准差(反映离散程度)、序列的最大取值范围(出现最多的区域,出现百分比多少),当然,这仅仅是一些统计学方面的概念。
事实上,对于波幅巨大的序列,如果分段,则很可能出现全部原始点都是分段点的情形,这种情形下的趋势价值有限。使用移动平均的方式来平滑是一个办法,但这里的相似性度量仍然会存在问题。
七、进一步关注的内容:
时间序列方面的具体应用,无非落实在识别、趋势分析和异常检测,在趋势分析方面,基于拟合的思路远不如使用时间序列基于搜索、聚类和统计的思路。基于拟合的思路,做决策支持分析的时候,是无法说服客户的。比如神经网络之类的方式,本质上就是用公式表示现在的走势,同时认为未来的走势也符合这个公式的规律,这个显然是期望使用数学来统治世界,很难令人信服。其他诸如ARMA之类的方法,大体上也是同样的路子。 只有基于搜索、聚类和统计的方法,才能够有一定的说服力,因为这里找出的是一些历史上已经发现的模式,期间蕴藏着我们不容易表达的规律。
由于时间序列,或者说有向序列,通常的需求无非在趋势和异常两点。而表达趋势方面,需要一定的模糊性,那么模糊数学、机器学习方面的一些概念,能否用于这个领域,目前尚没有特别有效的方法。
现有的时间序列工具,当图像被当作二维平面上的点的时候,按照先左后右获得第一条序列,从上到下获得所有序列这样的顺序,是能够进行分析的,比如找出相片中有多少人、找出相片中的人是不是你本人、找出相片中出现了一辆车没有、指纹是否符合等、雷达中获取的反射信息构成的模糊形状是否可能是一架飞机?当然,同样的扩展到三维空间,则对3d图形实际上也能够处理,无非是确定序列数量和方向、确定各序列权重的问题。