Category Archives: Statistics

Abraham Wald — 数弹孔的数学大师

亚伯拉罕·瓦尔德(Abraham Wald,1902 – 1950)是罗马尼亚裔美国统计学家,1902 年 10 月 31 日生于罗马尼亚克卢日的一个正统犹太世家。瓦尔德先就读于克罗日大学,1927 年进入入维也纳大学学习并且很快就获得博士学位(1931年)。瓦尔德的求学生涯恰好赶在第一次世界大战和第二次世界大战之间。在这个时期的奥地利,由于政治上的因素使他无法从事学术工作,瓦尔德只好接受一个私人职位,职责是帮助一位银行家增加高等数学知识,这导致他对经济学产生了浓厚兴趣,后来他成为经济学家摩根斯坦 (Oskar Morgenstern) 的亲信助理。

瓦尔德在二次大战前到达美国(1938年),不幸的是他的父母和姐妹没有逃出来,最终都死于纳粹的瓦斯房。瓦尔德在哥伦比亚大学做统计推断理论方面的研究工作,写出了许多有开创性的学术论文。他于 1943 年任副教授,1944 年任正教授,1946 年被任命为数理统计系的执行官员。在第二次世界大战的大部分时间里,瓦尔德在哥伦比亚大学的统计研究小组里面工作。该研究小组是为了二战而服务的,只不过研究的内容不是各种各样的炸药,而是各式各样的数学问题。

瓦尔德所在的统计研究小组每周都从事着高强度的学术研究活动,但瓦尔德在从事学术研究的时候,由于身份的原因,美国军方会禁止他阅读他自己完成的机密报告。传闻,每次瓦尔德在撰写完一页机密报告,旁边的秘书就会拿走这一页,并且不让瓦尔德重新阅读这份资料。虽然数学研究存在着不少的客观困难,但是,这并不影响瓦尔德的工作热情和尽情地展现自己的聪明才智。

在二战的时候,瓦尔德解决了一个关于飞机装甲的问题,让他从诸多数学家中脱颖而出。这个问题是美国军方提出的,从美国军方的角度来讲,我们都不希望自己的战斗机被敌人击落,因此我们需要为战斗机加厚装甲。但是,增加装甲又会增加战斗机的质量,影响飞机的机动性,并且还会消耗更多的燃油。过多的防御是没有必要的,但是完全不防御则是更加不可取的。那么究竟在飞机的哪些部位增加装甲,能够做到增加防御,但是又不影响飞机的战斗效率呢?军方就把这一问题交给了统计研究小组。

为了给数学家和统计学家们构建理论基础,军方为他们提供了一些可能需要的数据。军方收集了己方战斗机与敌方战斗机交火之后返回基地的数据,己方的战斗机身上都会留下许多弹孔,但是这些弹孔的分布并不是均匀的,机身上的弹孔比引擎上的弹孔明显多很多。

飞机部位每平方英尺的平均弹孔数
引擎1.11
机身1.73
油料系统1.55
其余部位1.80

这张表就是军方得到的统计数据,从表格上看,军方领导一致认为,装甲应该装在飞机最需要保护和受攻击最高的部分,那么就应该装在机身上,因为机身平均弹孔数是最多的。但是,统计学家瓦尔德给出的答案却与他们的答案相差甚远。

瓦尔德说,需要加装装甲的地方并不是留有弹孔的部位,而是没有弹孔的地方或者弹孔数较少的地方,那就是飞机的引擎。为此,瓦尔德继续解释道,飞机各个部位受到损坏的概率应该是一样的,但是引擎的弹孔数明显比其余部分少,那么这些失踪的子弹和弹孔去哪里了呢?原因只有一个,那就是这些弹孔应该都在那些未能返航的飞机上。胜利返航的飞机引擎弹孔较少,其原因正是被击中引擎的飞机未能返航。大量飞机在机身被打击的情况下仍然能够回到基地,说明机身可以承受很大的打击,于是无需加装装甲。

考虑一个极端的例子,假设敌军只要击中飞机引擎,飞机就会坠落且无法返航,那么我们会得到什么样的数据呢?我们会发现,在返航的战斗机上,机身,油料系统,其余部分均有弹孔,但是引擎上面找不到任何弹孔。那么只有两个可能,第一敌军打中了飞机的各个部位,但是没有打中引擎;第二就是打中引擎的飞机无法返航。

最终,沃尔德的论文题目是:A Method of Estimating Plane Vulnerability Based on Damage of Survivors,翻译出来就是一种根据幸存飞机损伤情况推测飞机要害部位的方法。在论文中,他应用详细的数据来证明他自己的观点。弹孔最稀疏处就是战斗机的要害处,因为没有被击中要害处的飞机才有更多的机会返航,进入军方的统计样本。这就是幸存者偏差。

通过详细的数学论证和统计推断,这一数学结论最终说服了军队的高层领导,军队高层领导迅速将瓦尔德的建议进行实践。瓦尔德拥有的空战知识和部队实战都不如军官,为什么却能够看到这个问题的本质原因呢?根本原因是瓦尔德在长期的科研过程中所养成的习惯。在这个例子中,军官们默认了一个假设,那就是返航的飞机是所有飞机的随机样本。正是这一个幸存者偏差的假设,导致军官们得到了错误的结论。

关于幸存者偏差(survivorship bias),有不少有趣的笑话。例如:老师在课堂上点名,要求没有到的学生举手,结果无一人举手,然后就得到了全体学生都来上课的结论。又例如:记者在春运火车上采访群众,问今年的火车票是否容易买,群众说很快就买到了,其实真正的原因是没有票的人无法上车。

但是数学家瓦尔德的结局令人惋惜。1950 年,四十八岁的他受印度政府邀请,携妻前往印度讲学。不料航行至印度的尼尔吉里丘陵时,飞机坠毁,沃尔德和妻子都未能成为幸存者。

Advertisement

无处不在的数学 — 从疫情谈起

数学在生活中是无处不在的。

每个人从出生开始,都会跟数学打交道。从最开始的认数字,到加减乘除,方程,几何,再到微积分和线性代数,数学几乎贯穿了教育的所有阶段。但是,数学真的在生活中是无处不在的么?对于这个问题,不同的人肯定有着自己完全不同的见解,本文就从近些年最常见的疫情开始谈起吧。

从 2019 年末开始,新冠病毒就开始在社会上悄悄传播,医疗系统开始逐渐被占满。2020 年初,武汉的疫情已经处于比较严重的状态,不少人都去医院治疗疾病,但同时检测系统和治疗系统还没有完全跟上。当时并没有办法在短时间内对几百万甚至上千万人口进行快速的疫情检测,只能够采取封城的策略来应对当时的疫情。

到了 2022 年,很多城市都有了丰富的应对疫情的策略,可以在很短的时间内对几百万甚至上千万人口同时进行核酸检测。根据检测结果,防疫部门就可以进行精准防控,以避免疫情的进一步扩散。能够进行大规模的核酸检测,除了检测能力本身的提升,检测时间的缩短之外,有没有数学办法将其进行优化就是一个关键问题。

核酸检测,其目的就是从大量的人中找到有问题的那一批人,然后进行必要的医疗救治,防止病毒的进一步扩散。近期如果做过大规模核酸的朋友仔细观察的话,其实在检测的时候会将十个人或者多个人的样本进行混合,放入同一个试管中,最终一起对这十个人或者多个人进行检测。如果有问题的话,再去对这十个人进行单独检测。从直觉上讲,多个人混合检测的效率肯定是优于一个人单独检测的,那么其背后的数学依据究竟是什么呢?为什么不进行二十个人,三十个人,一百个人的混合检测呢?

在回答这个问题之前,我们可以看一个更加简单一点的案例。相比大家都知道高校的图书馆,在图书馆的门口都会放有闸机,其目的是防止图书馆的书籍被大家不小心带出去。如果有人不小心把图书馆的书籍放入闸机,那么闸机就会报警。通常情况下,大家去借书的时候都会同时借阅好几本甚至十几本书籍,如果此时有一本书没有借阅成功,而出现了闸机报警的情况,那么如何快速的找到没有借阅成功的那本书呢?

最直接的方式是一本一本将这些书籍进行扫描,然后如果出现了报警,那就找到了这本没有借阅成功的书籍。但这种扫描的方式无疑是效率最低的,如果每次都要扫描所有的书籍,那么图书馆的管理员都会觉得效率低下。

假设某个同学一次借阅了十六本书,但是有一本书没有借阅成功,其实完全可以使用“二分法”来解决问题。将这十六本书分成两份,每一份有八本书,第一次先测试其中的八本,第二次再测试剩下的八本,于是得到了有报警的那八本书;于是再将这八本书分成两份,每份四本,重新在闸机上进行测试,得到有报警的那四本书;再将这四本书分成两份,每份二本,同样的操作之后得到有问题的两本书。最后,将这两本书在闸机上测试,就得到了那本没有扫码成功的书籍。通过这样的方法,可以将检测次数变成 2 + 2 + 2 + 2 = 8 次,测试的次数变成了原来的一半。扩展到一般的形式,如果书籍有 2^n 本,那么一本一本检测的话需要 2^n 次,但是如果使用图书馆阿姨的方法,则可以将检测次数变成 2n 次;当 n 很大的时候,可以清楚地知道 2n \ll 2^n,其检测的次数会呈现明显的减少。

不巧的是,这个方案并不能够完全照搬在传染病的检测中。因为在图书馆这个场景中,我们已经假设只有一本书是没有扫码成功的;但是在现实生活的传染病检测中,患病者的数量肯定不止一个,同时有多少个患病者其实也是未知的,我们只是知道患病者的人数肯定远远小于城市的总人口数。而在图书馆检测中,可以通过一次扫描就得到多本书的结果,在核酸检测的时候其实是做不到这一点的。不过图书馆这个场景倒是给提供了一个有效地思路,那就是将所有的书籍进行分桶。所谓的分桶就是将一个全体分成多份,且每一份的样本数是一致的,每次检测的时候只需要同时检测每一份样本就可以知道一个整体的情况了。

不过,核酸检测可以转换成一个抽象的数学问题。假设城市 A 的人口数是 n,当前感染传染病的人数是 m,并且 m 是远小于 n 的(m \ll n)。令 p = m / n 表示患病率且 p < 0.01。为了减少检测量,检测人员想对人群进行分桶操作,每 k 个人的样本进行混合,并且对这个混合样本进行检测。如果该混合样本没有问题,则这 k 个人都没有问题;如果该混合样本有问题,则再次对这 k 个人进行逐一检测。通过这样的方法,就可以得到最终的结果。那么,这个 k 该如何选择呢?

k = 1 的时候就相当于对每个人都进行检测,k = n 的时候就相当于对城市 A 中所有的人都进行检测,这两种极端情况的效率都很低。那么 k 值究竟如何选择,才能够让每个人的平均检测次数最少呢?

在数学上,这种混合检测的方法叫做 Group Testing,最早是美国的统计学家 Robert Dorfman 在 1943 年提出的。这种方法可以非常有效地提升大规模检测的效率和能力。在成本有限的前提下,达到最优的效果。其中心思想就跟核酸检测的方法是一样的,将全体人群按照一定的数量分成多个组,然后对每一个分组进行检测,如果该分组的检测结果没有问题,则这个分组里面的人就没有问题;如果该分组的检测结果有问题,则将这个分组里面的所有人重新进行单独测试。

根据以上方法,每个人的检测次数有可能是 1 次,也有可能是 2 次。只要这个人所在的混合样本没有问题,那么这个人就是 1 次;如果这个人所在的混合样本有问题,那么这个人就需要检测 2 次,那么这 k 个人都需要重新检测 1 次,这 k 个人的总检测次数就是 k + 1。这 k 个人的混合样本没有问题的概率是 (1-p)^k,有问题的概率是 1-(1-p)^{k}。那么,这 k 个人总检测次数的期望值就是 (1-p)^{k}+(1+k)(1-(1-p)^{k})。由于城市 A 总共有 n 个人,那么就需要 n / k 个桶,因此,总检测次数的期望是 \frac{n}{k}\cdot {(1-p)^{k}+(1+k)(1-(1-p)^{k})}=n\cdot\bigg(1+\frac{1}{k}-(1-p)^{k}\bigg)

为了解决这个函数的极值问题,最常见的就是使用微积分的方法。当 p < 0.01 时,通过 Taylor 公式可以得到 (1-p)^{k}\approx 1-pk,因此,总检测次数的期望值约等于 n\cdot\bigg(\frac{1}{k}+pk\bigg) 。当k=\frac{1}{\sqrt{p}} 时,上述公式达到最小值 2\cdot \sqrt{p}\cdot n。如果 p=0.01 时,k 取 10 可以达到最小值 0.2\cdot n;当 p=0.001 时,k 取 32 可以达到最小值 0.06\cdot n。通过混合采样的方法,可以将总检测次数的期望变成原来的五分之一甚至十分之一以下。

那么,在什么条件下,混合采样的方法会优于逐一检测呢?

此时,总检测次数的期望就要小于逐一检测,用数学公式来描述就是:n\cdot\bigg(1+\frac{1}{k}-(1-p)^{k}\bigg)<n,化简之后得到 p<1-\frac{1}{\sqrt[k]{k}}

如果进行十混一的混合检测方法,也就是当 k = 10 时,需要保证 p < 0.2;如果要进行二十混一的混合检测方法,也就是当 k = 20 时,需要保证 p < 0.14;如果要进行五十混一的混合检测方法,也就是当 k = 50 时,需要保证 p < 0.08。在绝大多数情况下,感染率 p 还是会小于 0.2 的,因此采用十混一或者二十混一的方法在现实生活中是可靠的。

在疫情防控的过程中,核酸检测是可以做到降本增效的,而降本增效的背后并不是拍脑袋的决定,其背后有着数学的理论支持。

参考资料

1. 参考知乎文章:https://zhuanlan.zhihu.com/p/103974270

2. 参考知乎提问:https://www.zhihu.com/question/524417189,https://www.zhihu.com/question/523901192/answer/2442100447

3. 核酸检测数学模型:https://www.zhihu.com/zvideo/1495452303112572928

4. 核酸检测的真假性:https://zhuanlan.zhihu.com/p/452598672,Bayes 公式。

5. https://en.wikipedia.org/wiki/Group_testing

6. https://zhuanlan.zhihu.com/p/104645873

样本量与置信区间

在现实生活中,如何从一份海量的数据中选择合适的样本数量进行调研反馈,从而反映总体的情况是一个常见的问题。例如,在千万人口的城市进行某项调研如何选择样本;在某些国家的大选活动中,该发放多少问卷才能够反映整体情况;在机器学习模型的精确率抽检过程中,究竟应该选择多少样本数进行抽检,才能够反馈其模型的精确率;这些都是生活和工作中常遇到的问题。

通常来说,在这里会有两种比较常见的情况:
1. 通过置信度(confidence level),置信区间(confidence interval),总体数量(population),来计算样本数(sample size);
2. 通过置信度(confidence level),总体数量(population),样本数量(sample size),比例(percentage),来计算置信区间(confidence interval);

样本数的计算

当一个调研是随机抽样的时候,可以通过统计的方法来确定最小的样本数。一般情况下,样本数可以按照以下简单规则来进行选取:

  • 最小样本数是 100:对于一份抽样数据而言,至少要抽取 100 个样本来进行评估;而当全体总数小于 100 的时候,只需要全部抽取出来进行调研和分析即可;
  • 一个合适的最大样本数(maximum sample size)可以用以下公式简单计算:\min(1000, 0.1 * N), 其中 N 表示总量;例如,当 N = 5000 时,最大样本数可以选择 500;而当 N = 200000 时,最大样本数只需要选择 1000 即可;
  • 在调研和抽样的时候,可以在最小样本数和最大样本数之间选择一个合适的值;
    (1)选择一个靠近最小样本数的值是因为:有限的资金和时间;只需要一个粗糙的估计;不需要对总体做类别分析且只需要一个整体的结论即可;大家对这个结论并不会有过多的质疑;这个分析结果并不会过多影响下游的分析决策。
    (2)选择一个靠近最大样本数的值是因为:充足的资金和时间;想要得到发一个精确的估计;有可能会对总体进行分组分析;大家会对这个结论有质疑;这个分析结果会导致下游的很多重要决策。
  • 对于样本数的选择,可以简单的参考下表:
总量总量总量总量总量总量
误差范围(Margin of Error)> 5000500025001000500200
\pm 10\%969493888165
\pm 7.5\%17116516014612792
\pm 5\%384357333278217132
\pm 3\%1067880748516341169
置信度 95% 的前提下
  • 从表格中可以看出,如果只需要保证置信度在 95%,当总量很大的时候,其实只需要抽取 1067 个样本进行分析即可。
  • 在线计算样本量的网站有两个,分别是:
    (1)https://www.surveysystem.com/sscalc.htm
    (2)https://www.calculator.net/sample-size-calculator.html?
置信度 95%
置信度 99%

通过以上的案例可以看出,在设置了置信度(Confidence Level),置信区间(Confidence Interval),总量(Population)之后,就可以得到一个样本数量(Sample Size Needed)。其中,置信区间也就是误差范围(Margin of Error)。

例如,如果置信度是 95%,置信区间(confidence interval)= 4,在样本中有 47% 的比例选择了某个选项,那么就表示 95% 的置信度,在所有数据中有(47%-4%, 47%+4%)=(43%,51%) 的比例选择了某个选项。95% 置信度表示这句话正确的概率是 95%,99% 置信度表示这句话正确的概率是 99%。

置信区间的计算

置信区间的计算是由三个因素决定的,分别是:置信度(Confidence Level),样本数量(Sample Size),总量(Population)。一般来说,

  • 置信度:置信度越大,表示置信区间将会越大。相对 99% 的置信度而言,95% 的置信度所产生的置信区间会小;
  • 样本大小:样本越多,越能够反映总体情况,置信区间将会越小;
  • 占比(Percentage):样本中选择某个结果的比例;由于选择 Percentage(p) 和 1-p 所得到的置信区间(Confidence Interval)是一样的,因此:
    (1)Percentage(p) 越接近 0 或者 1,则置信区间越小;
    (2)Percentage(p) 越接近 50%,则置信区间越大,因为此时的不确定性最高;
置信区间的计算 1
置信区间的计算 2
置信区间的计算 3
置信区间的计算 4

统计理论分析

假设 N 表示总体的数量,X 表示总体中满足某个选项的数量,n 表示抽样的数量,x 表示样本中满足某个选项的数量。令 P=X/N, \hat{p}=x/n.

定理. 一个总体满足某个选项的比例可以通过样本中满足某个选项的比例来进行估计。如果置信度是 C,且正态分布的概率用 Prob 表示,那么

Prob\bigg(-z^{*}<\frac{\hat{p}-P}{\sqrt{\frac{\hat{p}(1-\hat{p})}{n}}}<z^{*}\bigg)=C,

换言之,\hat{p}-z^{*}\sqrt{\frac{\hat{p}(1-\hat{p})}{n}}<P<\hat{p}+\sqrt{\frac{\hat{p}(1-\hat{p})}{n}}. 其中 z^{*}C 的关系如下图所示。其置信区间是 w = z^{*}\sqrt{\frac{\hat{p}(1-\hat{p})}{n}}.

正态分布

由于置信区间是 z^{*}\cdot\sqrt{\frac{\hat{p}(1-\hat{p})}{n}}, 所以,n=(z^{*}/w)^{2}\cdot\hat{p}(1-\hat{p})\leq (z^{*}/w)^{2}/4. 最大样本数可以选择为 (z^{*}/w)^{2}/4.

同时,根据正态分布的定义可以得到:

\frac{1}{\sqrt{2\pi}}\int_{-z^{*}}^{z^{*}}e^{-t^{2}/2}dt=C,

从而,

C=\frac{2}{\sqrt{\pi}}\int_{0}^{z^{*}/\sqrt{2}}e^{-t^{2}}dt=erf(z^{*}/\sqrt{2}),

其中,erf(z)=\frac{2}{\sqrt{\pi}}\int_{0}^{z}e^{-t^{2}}dt.

因此,最大样本数是 (z^{*}/w)^{2}/4=\bigg(\frac{erf^{-1}(C)}{\sqrt{2}w}\bigg)^{2}, w 表示置信区间,C 表示置信度。

置信区间的公式是:z^{*}\cdot\sqrt{\frac{\hat{p}(1-\hat{p})}{n}}=\sqrt{2}\cdot erf^{-1}(C)\cdot\sqrt{\frac{\hat{p}(1-\hat{p})}{n}}.

从 WolframAlpha 可以得到:erf^{-1}(0.95)=1.38590, erf^{-1}(0.99)=1.82139.

  • 如果置信区间是 3\%, 置信度 C=95\% 的时候,最大样本数是:(1.38590 / (\sqrt{2} * 0.03))^2=1067;
  • 如果置信区间是 3\%, 置信度 C=99\% 的时候,最大样本数是:(1.82139 / (\sqrt{2} * 0.03))^2=1843;
  • 如果置信度是 C=95\%, \hat{p}=50\%, n=1000, 置信区间是 \sqrt{2} * 1.38590 * \sqrt{0.5 * 0.5 /1000}=0.031=3.1\%;
  • 如果置信度是 C=99\%, \hat{p}=50\%, n=1000, 置信区间是 \sqrt{2} * 1.82139* \sqrt{0.5 * 0.5 /1000}=0.0407=4.1\%.

参考资料

  1. 在线计算工具(1):https://www.surveysystem.com/sscalc.htm
  2. 在线计算工具(2):https://www.calculator.net/sample-size-calculator.html
  3. http://www.tools4dev.org/resources/how-to-choose-a-sample-size/
  4. https://www.chedong.com/blog/archives/001462.html
  5. https://www.afenxi.com/23249.html
  6. https://www.zhihu.com/question/23017185
  7. https://blog.csdn.net/liangzuojiayi/article/details/78044780
  8. :Sample Size Determination: https://en.wikipedia.org/wiki/Sample_size_determination#Estimation_of_a_proportion
  9. Population proportion: https://en.wikipedia.org/wiki/Population_proportion#cite_note-:0-4
  10. 张伟平:中文讲义:http://staff.ustc.edu.cn/~zwp/teach/Math-Stat/mathstat.htm
  11. http://staff.ustc.edu.cn/~zwp/teach/Prob-Stat/probstat.htm

统计距离(statistical Distance)

统计距离(Statistical Distance)

统计距离的定义

在欧式空间,如果要衡量两个 n 维空间中的点 \bold{x}=(x_{1},\cdots,x_{n})\bold{y} = (y_{1}, \cdots, y_{n})\in\mathbb{R}^{n} 之间的距离,通常可以使用 L^{p}- 范数来进行描述,其数学公式是:

d(\bold{x},\bold{y})=(\sum_{i=1}^{n}|x_{i}-y_{i}|^{p})^{1/p}, 1\leq p <+\infty.

d(\bold{x},\bold{y})=\max_{1\leq i\leq n}|x_{i}-y_{i}|, p=+\infty.

在统计学中,如果需要衡量两个统计对象之间的“距离”,在离散的场景下除了可以使用欧式距离之外,还可以使用其他的统计距离来描述这两个统计对象之间的距离。与之对应的,在连续的场景下,同样可以定义出两个统计对象之间的距离。

首先,我们来回顾一下距离的定义;

距离是定义在集合 X 的函数 d:X\times X \rightarrow [0,+\infty), 并且满足以下条件:

  1. d(x,y)\geq 0 对于所有的 x,y\in X 都成立;
  2. d(x,y) = 0 \iff x=y;
  3. d(x,y) = d(y,x) 对于所有的 x,y\in X 都成立;
  4. d(x,y) + d(y,z) \geq d(x,z) 对于所有的 x,y,z\in X 都成立。

而广义的距离会缺少其中一个或者多个条件,例如时间序列领域中的 DTW 距离就不满足三角不等式。

在微积分中,凸函数(convex 函数)f: \mathbb{D}\rightarrow \mathbb{R} 指的是在其定义域内的任意两个点 x_{1},x_{2}\in\mathbb{D}, 满足
\frac{f(x_{1})+f(x_{2})}{2} \geq f\bigg(\frac{x_{1}+x_{2}}{2}\bigg). 换言之,如果凸函数 f(x) 存在二阶连续导数,那么 f'(x) 是增函数,f''(x)\geq 0.

其次,在统计距离中,通常会基于一个函数 f:\mathbb{R}\rightarrow\mathbb{R} 来定义两个概率分布之间的距离。该函数 f 是一个凸函数(convex function),并且满足 f(1)=0. 对于空间 X 中的两个概率分布 PQ 而言,

D_{f}(P||Q)=\int_{X}f\bigg(\frac{dP}{dQ}\bigg)dQ=\int_{X}f\bigg(\frac{p(x)}{q(x)}\bigg)q(x)dx

定义了概率分布 PQf- 散度(f-divergence),其中 p(x),q(x) 分别对应了 P,Q 的概率密度函数。不同的函数 f(x) 对应了不同的散度,常见的散度包括但不限于:

  • KL – 散度(KL – Divergence):f(x)=x\ln(x);
  • Reverse KL -散度(Reverse KL – Divergence):f(x)=-\ln(x);
  • Hellinger 距离(Hellinger Distance):f(x)=(\sqrt{x}-1)^{2} 或者 f(x)=2(1-\sqrt{x});
  • 变分距离(Total Variation Distance):f(x)=|x-1|/2;
  • Pearson \chi^{2} – 散度(Pearson \chi^{2} – Divergence):f(x)=(x-1)^{2} 或者 f(x)=x^{2}-1 或者 f(x)=x^{2}-x;
  • Reverse Pearson \chi^{2} – 散度(Reverse Pearson \chi^{2} – Divergence):f(x)=\frac{1}{x}-1 或者 f(x)=\frac{1}{x}-x;
  • Jensen-Shannon-Divergence:f(x)=\bigg\{(x+1)\ln\bigg(\frac{2}{x+1}\bigg)+x\ln(x)\bigg\}/2;
  • L1 – 范数(L1 – Norm):f(x)=|x-1|;

在这样的定义下,D_{f} 是非负函数,i.e. D_{f}(P||Q)\geq 0. 事实上,

D_{f}(P||Q)=\int_{X} f\bigg(\frac{dP}{dQ}\bigg)dQ\geq f\bigg(\int_{X}\frac{dP}{dQ}dQdx\bigg)=f(1)=0.

在数学中有如下定理:如果 f 是凸函数,那么 g(x,t)=tf(x/t) 在定义域 \{(x,t)|x/t\in Dom(f),t>0\} 也是凸函数。

根据以上定理,可以得到:对于 \forall 0\leq \lambda\leq 1,

D_{f}(\lambda P_{1}+(1-\lambda)P_{2} ||\lambda Q_{1}+(1-\lambda)Q_{2})\leq \lambda D_{f}(P_{1}||Q_{1}) + (1-\lambda)D_{f}(P_{2}||Q_{2}).

除了 f- 散度之外,直接使用 L^{p}- 范数也可以定义两个概率空间的距离,特别地,当 p=1,2,\infty 时,其距离公式是:

||P-Q||_{1}=\int_{X}|p(x)-q(x)|dx,

||P-Q||_{2}=\sqrt{\int_{X}|p(x)-q(x)|^{2}dx},

||P-Q||_{\infty}=\sup_{x\in X}|p(x)-q(x)|.

统计距离的函数分析

事实上,对于 KL 散度和 Reverse KL 散度而言,令 f_{1}(x)=x\ln(x), f_{2}(x)=-\ln(x),

\text{KL}_{f_{1}}(P||Q)=\int_{X}f_{1}\bigg(\frac{p(x)}{q(x)}\bigg)q(x)dx=\int_{X}p(x)\ln\frac{p(x)}{q(x)}dx

=\int_{X}-p(x)\ln\frac{q(x)}{p(x)}dx=\int_{X}f_{2}\bigg(\frac{q(x)}{p(x)}\bigg)p(x)dx

=\text{Reverse KL}_{f_{2}}(Q||P),

这就是函数 f_{1}, f_{2} 分别对应着 KL-散度和 Reverse KL-散度相应函数的原因。

类似地,对于函数 f_{3}(x)=x^{2}-1, f_{4}(x)=x^{2}-xg_{3}(x)=\frac{1}{x}-x, g_{4}(x)=\frac{1}{x}-1 而言,可以直接证明得到:

\text{Pearson}_{f_{3}}(P||Q)=\int_{X}\bigg(\bigg(\frac{p(x)}{q(x)}\bigg)^{2}-1\bigg)q(x)dx

=\int_{X}\bigg(\frac{p(x)}{q(x)}-\frac{q(x)}{p(x)}\bigg)p(x)dx=\int_{X}g_{3}\bigg(\frac{q(x)}{p(x)}\bigg)p(x)dx

=\text{Reverse Pearson}_{g_{3}}(Q||P),

\text{Pearson}_{f_{4}}(P||Q)=\int_{X}\bigg(\bigg(\frac{p(x)}{q(x)}\bigg)^{2}-\frac{p(x)}{q(x)}\bigg)q(x)dx

=\int_{X}\bigg(\frac{p(x)}{q(x)}-1\bigg)p(x)dx=\int_{X}g_{4}\bigg(\frac{q(x)}{p(x)}\bigg)p(x)dx

=\text{Reverse Pearson}_{g_{4}}(Q||P).

对于 Jensen-Shannon Divergence(简写为 JSD)而言,f(x)=\frac{1}{2}\bigg\{(x+1)\ln\bigg(\frac{2}{x+1}\bigg)+x\ln(x)\bigg\},

\text{JSD}_{f}(P||Q)=\frac{1}{2}\int_{X}f\bigg(\frac{p(x)}{q(x)}\bigg)q(x)dx

=\frac{1}{2}\int_{X}\bigg\{\bigg(\frac{p(x)}{q(x)}+1\bigg)\ln\bigg(\frac{2q(x)}{p(x)+q(x)}\bigg)+\frac{p(x)}{q(x)}\ln\bigg(\frac{p(x)}{q(x)}\bigg)q(x)\bigg\}dx

=\frac{1}{2}\int_{X}\bigg\{p(x)\ln\frac{2p(x)}{p(x)+q(x)}+q(x)\ln\frac{2q(x)}{p(x)+q(x)}\bigg\}dx

=\frac{1}{2}\text{KL}(P||M)+\frac{1}{2}\text{KL}(Q||M),

其中 M=(P+Q)/2, i.e. m(x)=(p(x)+q(x))/2.

对于 Hellinger Distance 而言,f_{1}(x)=(\sqrt{x}-1)^{2}, f_{2}(x)=2(1-\sqrt{x}). 其实这两个函数是等价的,因为

\text{Hellinger Distance}_{f_{1}}(P||Q)=\int_{X}\bigg(\sqrt{\frac{p(x)}{q(x)}}-1\bigg)^{2}q(x)dx

=\int_{X}\bigg(p(x)+q(x)-2\sqrt{p(x)q(x)}\bigg)dx

=2-2\int_{X}\sqrt{p(x)q(x)}dx

=\int_{X}2\bigg(1-\sqrt{\frac{p(x)}{q(x)}}\bigg)q(x)dx

=\text{Hellinger Distance}_{f_{2}}(P||Q).

其中 BC(P,Q)=\int_{X}p(x)q(x)dx 被称为 Bhattacharyya 系数(Bhattacharyya Coefficient),Bhattacharyya 距离则定义为

D_{B}(P||Q)=-\ln(BC(P,Q))=-\ln\bigg(\int_{X}p(x)q(x)dx\bigg).

统计距离的上下界分析

对于以上函数而言,由于凸函数 f(1)=0, 因此当 P=Q 时,D_{f}(P||Q)=0.

KL 散度是没有上界的,但是 Jensen Shannon Divergence 是具有上界的。事实上,如果 M=(P+Q)/2, 则有

KL(P||M)=\int_{X}p(x)\ln\frac{2p(x)}{p(x)+q(x)}dx\leq\int_{X}p(x)\ln 2 dx=\ln2,

同样地,KL(Q||M)\leq \ln2, 所以可以得到 JSD(P||Q)\leq \ln2.

根据 Hellinger 距离的公式,可以得到:\text{Hellinger Distance}(P||Q)=2-\int_{X}\sqrt{p(x)q(x)}dx\leq 2. 同时,Bhattacharyya 距离 D_{B}(P||Q)=-\ln(BC(P||Q)) 是没有上界的,因为 BC(P||Q) 可以取值到零。

考虑 L^{p}- 范数中 p=1,2,\infty 三种情况:

||P-Q||_{1}=\int_{X}|p(x)-q(x)|dx\leq\int_{X}(p(x)+q(x))dx=2, 并且上界 2 是可以取到的。

||P-Q||_{2}=\sqrt{\int_{X}(p(x)-q(x))^{2}}=\sqrt{\int_{X}((p(x))^{2}-2p(x)q(x)+(q(x))^{2})dx}

\leq\sqrt{\int_{X}((p(x))^{2}+(q(x))^{2})dx}

\leq\sqrt{\int_{X}(p(x)+q(x))dx}=\sqrt{2}.

||P-Q||_{\infty}=\sup_{X}|p(x)-q(x)|dx\leq 1.

证明以上不等式使用了性质 0\leq p(x),q(x)\leq 1, \int_{X}p(x)dx=\int_{X}q(x)dx=1.

多重集合

多重集合的定义与性质

在数学中,集合(set)中不能够包含重复的元素,但一个多重集合(multiset)中则可以包含重复的元素,并且计算了元素的重数。例如,

  1. a\neq b 时,\{a,b\} 可以看成集合,也可以看成重数为 1 的多重集合,可以记为 \{a^{1},b^{1}\} 或者 \{1/a,1/b\}.
  2. 在多重集合 \{a,a,b\} 中,a 的重数是 2,b 的重数是 1,可以记为 \{a^{2},b^{1}\} 或者 \{2/a,1/b\}.
  3. 在多重集合 \{a,a,a,b,b,b\} 中,a,b 的重数都是 3。

对于一个有限集合 \{a_{1},\cdots,a_{n}\} 而言,其多重集合可以记为 \{a_{1}^{m(a_{1})},\cdots,a_{n}^{m(a_{n})}\} 或者 \{m(a_{1})/a_{1},\cdots,m(a_{n})/a_{n}), 其中 m(a_{i}) 表示元素 a_{i} 的重数。多重集合的一个典型例子就是质因数分解,例如:100=2^{2}\cdot 5^{2}.

假设多重集合 A,B 的元素都属于集合 U,

  1. 子集:如果对于所有的 x\in Um_{A}(x)\leq m_{B}(x), 则称多重集合 A 是多重集合 B 的子集;
  2. 交集:如果 \forall x\in U,m_{C}(x)=\min(m_{A}(x),m_{B}(x)), 则称多重集合 C 是多重集合 A,B 的交集,记为 C=A\cap B;
  3. 并集:如果 \forall x\in U,m_{C}(x)=\max(m_{A}(x),m_{B}(x)), 则称多重集合 C 是多重集合 A,B 的并集,记为 C=A\cup B;
  4. 求和:如果 \forall x\in U, m_{C}(x)=m_{A}(x)+m_{B}(x), 则称多重集合 C 是多重集合 A,B 的和,记为 C=A\oplus B;
  5. 求差:如果 \forall x\in U, m_{C}(x)=\max(0,m_{A}(x)-m_{B}(x)), 则称多重集合 C 是多重集合 A,B 的差,记为 C=A\ominus B.

假设 A=\{x^{2},y^{1},z^{3}\}, B=\{x^{1},y^{4},w^{3}\}, 那么

A\oplus B=\{x^{3},y^{5},z^{3},w^{3}\},

A\ominus B=\{x^{1},z^{3}\},

A\cap B=\{x^{1},y^{1}\},

A\cup B=\{x^{2},y^{4},z^{3},w^{3}\}.

多重集合的相似度和距离

由于已经定义了多重集合的交集和并集,因此集合相似度中的 Jaccard 相似度,Overlap 相似度都可以应用到多重集合中。

对于多重集合 A=\{a_{1}^{m(a_{1})},\cdots,a_{n}^{m(a_{n})}\} 而言,令 p_{i}=m(a_{i})/\sum_{i=1}^{n}m(a_{i}), 1\leq i\leq n. 因此,多重集合 A 对应了一个离散的概率分布 \{p_{1},\cdots,p_{n}\}. 于是,可以使用以上的统计距离(Statistical Distance)来计算两个多重集合之间的距离。

参考资料

  1. 统计距离:https://en.wikipedia.org/wiki/Statistical_distance
  2. f – divergence:https://en.wikipedia.org/wiki/F-divergence:包括了 KL 散度的其余变形方式。
  3. Bregman distance:https://en.wikipedia.org/wiki/Bregman_divergence
  4. Jensen Shannon Divergence:https://en.wikipedia.org/wiki/Jensen%E2%80%93Shannon_divergence
  5. KL Divergence:https://en.wikipedia.org/wiki/Kullback%E2%80%93Leibler_divergence
  6. Bhattacharyya distance:https://en.wikipedia.org/wiki/Bhattacharyya_distance
  7. Wasserstein metric:https://en.wikipedia.org/wiki/Wasserstein_metric
  8. 多重集合:multiset:https://en.wikipedia.org/wiki/Multiset

描述统计学

描述统计学(descriptive statistics)又称为叙述统计,是统计学中用于描述和总结所观察到对象的基本统计信息的一门学科。描述统计的结果是对当前已知的数据进行更精确的描述和刻画,分析已知数据的集中性和离散型。描述统计学通过一些数理统计方法来反映数据的特点,并通过图表形式对所收集的数据进行必要的可视化,进一步综合概括和分析得出数据的客观规律。

与之相对应的是推断统计学(statistical inference),又称为推断统计,是统计学中研究如何用样本数据来推断总体特征的一门学科。推断统计学是在对样本数据描述的基础上,对总体的未知数据做出以概率形式来描述的推断。推断统计的结果通常是为了得到下一步的行动策略。

本篇文章将会集中讲解描述统计学中的一些常见变量及其含义。

数据类型:

总体population),又称为全体或者整体,是指由多个具有某种共同性质的事物的集合。

样本sample),是指全体中随机抽取的个体。通过对样本的调查,可以大概的了解总体的情况。从总体抽样的时候,需要抽取一定数量的样本,如果样本太少,则不足以反映总体的情况。

population_and_sample
总体和样本

案例 1:一亿张图片所组成的图片集可以称之为一个总体,我们希望分析在这个图片集中包含汽车的图片有多少张。一种方法是一亿张图片每一张都看一遍,从而可以获得包含汽车的图片数量,这样就可以得到一个精确的数字。但是这样的工作量可能相对较大。另外一种方法是从一亿张图片中随机选择十万张或者一百万张,也就是获得了一个样本集。在这个样本集中,把每一张都看一遍,获得这个样本集中包含汽车的图片数量,进一步估算出总体中包含汽车的图片数量。这样的话,工作量相对较少,但是得到的则是一个估算数字。

ImageNet
ImageNet

案例 2:我们想知道某个国家居民的平均身高和体重,一种方法是将所有的居民都测量一遍,但是这样做的效果就是耗费的人力成本巨大。而另外一种办法就是随机抽样,抽取一定数量的居民进行身高和体重的测量。即可估算出这个国家居民的平均身高和体重。

特征类型

在机器学习领域,特征是被观测对象的某种特性和度量。一般情况下,事物的特征很多,但是提取的特征应该尽量要服从于我们的目的,如果提取了很多无效的特征,那么在机器学习实战中的价值也不会很大。通常来说,特征包括两类,第一种是离散型特征,第二种是连续型特征

discrete_and_continuous
连续与离散

离散型特征指的是该特征的数据类型是离散的(discrete)。例如人的性别,有男女两个选择,可以用 0 或者 1,或者其他记号来表示。例如某个城市是否属于某个省份,如果是的话该特征就是 1,如果否的话该特征就是 0。例如某只股票近期属于上涨还是下跌,上涨用 1 表示,下降用 0 表示。某个人当前处于婴儿,少年,青年,成年,老年的哪个阶段,分别用记号 0,1,2,3,4,5 表示,这种也是离散型特征。离散型特征的数值之间的大小关系(实数域比较)有的时候是没有意义的。例如人的性别,男(0)女(1)两个值,在实数域中 0 < 1,但是却没有意义。

连续性特征指的是该特征的数据类型是连续的(continuous)。例如某个国家一年的天气温度,温度是可以连续变化的,可以从 30 摄氏度连续地下降到 20 摄氏度,也可以连续地上升到 35 摄氏度。某个人的身高,可以从 170 cm 逐渐长高到 175 cm,这也属于连续的特征。连续特征的数值之间有大小关系(实数域比较),比如通过气温特征的值,是可以反映这个地区的温度情况。通过某个人的身高则可以反映出这个人距离上一次测量有没有变化。

特征统计量

集中趋势的度量(measure of central tendency)

集中趋势(central tendency)指的是某种平均的指标,通过这种指标可以反映一组数据的整体分布情况。在这里,这组数据并不需要有先后关系,只要是一个集合即可。对于 n 个数据所组成的集合,可以表示为 X=\{x_{1},x_{2},\cdots,x_{n}\}.

算术平均数(Arithmetic Mean)

数据的总和除以数据的个数,也就是

A_{n}=\mu=\sum_{i=1}^{n}x_{i}/n.

几何平均数(Geometric Mean)

如果该集合里面的数字都是非负数,那么可以定义其几何平均数为

G_{n}=\sqrt[n]{x_{1}\cdots x_{n}}.

从高中的数学知识可以得到几何平均数不大于算术平均数。

调和平均数(Harmonic Mean)

如果该集合里面的数字都是正数,那么可以定义其调和平均数为

H_{n}=n/(x_{1}^{-1}+\cdots+x_{n}^{-1}).

平方平均数(Quadratic Mean)

平方平均数指的是

Q_{n}=\sqrt{\frac{\sum_{i=1}^{n}x_{i}^{2}}{n}}.

Theorem. 如果 x_{1},\cdots, x_{n} 都是正数,那么 H_{n}\leq G_{n}\leq A_{n}\leq Q_{n}. 也就是说,调和平均数\leq几何平均数\leq算术平均数\leq平方平均数。

proof. n=2 的情形证明如下图。其余可以用数学归纳法等多种方法证明。

二维均值不等式的几何证明
二维均值不等式的几何证明

方差(Variance),标准差(Standard Deviation)

方差和标准差反映了数据的波动情况,方差指的是 \sigma^{2}=\sum_{i=1}^{n}(x_{i}-\mu)^{2}/n. 而标准差则有两种情况,第一种是总体的样本差(population standard deviation),总体的标准差定义为方差正的平方根,记为 SD,

SD = \sigma = \sqrt{\frac{1}{n}(x_{i}-\overline{x})^{2}},

其中 \overline{x}=\sum_{i=1}^{n}x_{i}/n.

第二种是样本的标准差(sample standard deviation),此时集合 \{x_{1},\cdots,x_{n}\} 是从一个更大的总体抽样出来的部分数据。样本的标准差记为 s, s 的定义为

s = \sqrt{\frac{1}{n-1}\sum_{i=1}^{n}(x_{i}-\overline{x})^{2}},

其中 \overline{x}=\sum_{i=1}^{n}x_{i}/n.

众数(Mode)

众数指的是这个集合 \{x_{1},\cdots,x_{n}\} 中出现得最多的数字。

k 阶矩(k Moment),k 阶中心矩(k Central Moment)

k 阶矩指的是

m_{k} = \sum_{i=1}^{n}x_{i}^{k}/n,

它称为样本的 k 阶矩,它反映了样本总体的信息。显然,m_{1} 就是算术平均数。

k 阶中心矩指的是

\mu_{k} = \sum_{i=1}^{n}(x_{i}-\overline{x})^{k}/n,

它称为样本的 k 阶中心矩,它反映了样本距离均值的情况。显然,\mu_{2} 就是样本方差。

偏度(Skewness)

偏度定义为

\sum_{i=1}^{n}\frac{1}{n}\cdot\frac{(x_{i}-\overline{x})^{3}}{\sigma^{3}}=\frac{\mu_{3}}{\sigma^{3}}.

n 个样本的样本偏度(sample skewness)定义为 \mu_{3}/s^{3}, 其中 s 是样本的标准差,i.e s = \sqrt{\frac{1}{n-1}\sum_{i=1}^{n}(x_{i}-\overline{x})^{2}}.

而另外常见的一种样本偏度定义为 \frac{n^{2}}{(n-1)(n-2)}\cdot \frac{\mu_{3}}{s^{3}}. 而偏度的结果可以是正数,负数,或者零。分别被称为 Positive Skew(右侧的尾巴更长),  Negative Skew(左侧的尾巴更长) 和 Zero Skew。当均值等于中位数等于众数的时候,该概率分布是对称的。Median(中位数)相对于 Mean(均值)是更加接近 Mode(众数)的数字,因此根据 Median 和 Mean 的大小关系也能够大致判断 Skew(偏度)的趋势。

skewness_1
偏度的两种类型

skewness_2
中位数,众数,平均数,偏度

峰度(Kurtosis)

n 个样本的样本峰度(sample kurtosis)可以定义为:\frac{\mu_{4}}{\mu_{2}^{2}} - 3, 其中 \mu_{4} = \sum_{i=1}^{n}(x_{i}-\overline{x})^{4}/n, \mu_{2}=\sum_{i=1}^{n}(x_{i}-\overline{x})^{2}/n. 减去 3 的目的是为了让正态分布的峰度为零。

Theorem. 正态分布 4 阶距的值是 3。

Proof. 需要计算 \frac{1}{\sqrt{2\pi}}\int_{-\infty}^{+\infty}x^{4}e^{-\frac{x^{2}}{2}}dx 的值。可以使用极坐标的方法来解决,首先通过坐标变换可以得到原式子等于 \frac{4}{\sqrt{\pi}}\int_{-\infty}^{+\infty}x^{4}e^{-x^{2}}dx. 其次,令 A=\int_{-\infty}^{+\infty}x^{4}e^{-x^{2}}dx, 可以得到

A^{2}=\int_{-\infty}^{+\infty}\int_{-\infty}^{+\infty}x^{4}y^{4}e^{-x^{2}-y^{2}}dxdy

= \int_{0}^{2\pi}\cos^{4}(\theta)\sin^{4}(\theta)d\theta \cdot \int_{0}^{+\infty}r^{9}e^{-r^{2}}dr

= \frac{3\pi}{64}\cdot 12=\frac{9}{16}\pi.

进一步得到 A=\frac{3}{4}\sqrt{\pi}. 从而原式子等于 3。i.e. 正态分布 4 阶距的值是 3。

中位数(Median)

中位数指的是将集合中的数字从小到大排序之后得到的有序数列,中间的那个数字。如果的数列的长度是偶数,则取中间两个数的平均值。

median
中位数的计算案例

带权重的算术平均数(Weighted Arithmetic Mean)

对于一组数据 \{x_{1},x_{2},\cdots,x_{n}\}, 可以设置其一组正数权重 \{w_{1},\cdots,w_{n}\}, 然后得到其带权重的算术平均数为

\sum_{i=1}^{n}w_{i}x_{i}/\sum_{i=1}^{n}w_{i}.

截断平均数(Truncated Mean)

截断平均数是舍弃掉样本中最高和最低的一些样本之后再计算得到的平均值,并且最高和最低两端舍弃的样本数量一致。舍弃的样本数量可以是整体资料数量的占比,也可以是一个固定的数量。

发散度量(measure of dispersion)

四分位距(interquartile range,IQR)

四分位距(IQR),也被称为 midspread,middle 50%,H-spread,它等于 75th 百分位数与 25th 百分位数的差值,也就是

IQR = Q_{3}-Q_{1}.

其中,对于长度为 2n 或者 2n+1 的数列而言,Q_{1} 就是 n 个最小数的中位数,也就是 Q_{1} 在有序数列从小到大排序的 25% 位置。Q_{3} 就是 n 个最大数的中位数,也就是 Q_{3} 在有序数列从小到大排序的 75% 的位置。IQR 反映了数据的集中程度,IQR 越小,表示数据越集中于 median 附近;IQR 越大,表示数据越发散于两端。

IQR_1
正态分布的箱形图

用箱形图(boxplot)作异常检测的时候,上下界分别定义为 Q_{3}+1.5 \cdot IQR, Q_{1}-1.5 \cdot IQR.

IQR_2
四分位距的案例

在上述案例中,Q_{1} = 31, Q_{2}=87, Q_{3} = 119, 从而四分位距 IQR = Q_{3}-Q_{1}=88. 异常检测的上下界分别是 Q_{3}+1.5\cdot IQR = 251, Q_{1}-1.5\cdot IQR = -101.

四分位发散系数(quartile coefficient of dispersion)

四分位发散系数也是用于衡量数据集中程度的,对于不同的序列而言,IQR 并没有在一个尺度下进行衡量,无法通过直接对比两个序列的 IQR 来判断它们之间的发散程度(需要先对两个序列进行归一化才行)。于是,有学者提出了另外一种衡量方法,就是四分位发散系数,它的定义就是

(Q_{3}-Q_{1})/(Q_{3}+Q_{1}).

例如:X=\{2, 4, 6, 8, 10, 12, 14\}Y=\{1.8, 2, 2.1, 2.4, 2.6, 2.9, 3\} 两个集合。对于 X 而言,Q_{1}=4,Q_{2}=8,Q_{3}=12, 它的 IQR=Q_{3}-Q_{1}=8, 四分位发散系数为 (Q_{3}-Q_{1})/(Q_{3}+Q_{1})=0.5; 对于 Y 而言,Q_{1}=2,Q_{2}=2.4,Q_{3}=2.9, 它的 IQR=Q_{3}-Q_{1}=0.9,四分位发散系数为 (Q_{3}-Q_{1})/(Q_{3}+Q_{1})=0.1837. 因此集合 X 的四分位发散系数比 Y 的四分位发散系数要大,XY 更加发散。

范围(range)

在统计学中,对于集合 \{x_{1},\cdots,x_{n}\} 而言,它的最大值减去最小值的差值就是范围。i.e.

range = \max_{1\leq i\leq n}\{x_{1},\cdots,x_{n}\}-\min_{1\leq i\leq n}\{x_{1},\cdots,x_{n}\}.

该值越大,表示集合的最大值与最小值的差异越大,数据更加发散;该值越小,表示集合的最大值与最小值的差异越小,数据就更加集中。

平均绝对偏差(Mean Absolute Difference)

对于集合 X=\{x_{1},\cdots,x_{n}\} 而言,平均绝对偏差(Mean Absolute Difference)定义为:

MD(X)=\frac{\sum_{i=1}^{n}\sum_{j=1}^{n}|x_{i}-x_{j}|}{n(n-1)}.

相对平均绝对偏差(Relative Mean Absolute Difference)则定义为:

RMD(X) = \frac{\sum_{i=1}^{n}\sum_{j=1}^{n}|x_{i}-x_{j}|}{(n-1)\sum_{i=1}^{n}x_{i}}.

通过相对平均绝对偏差可以对比两个集合之间的偏差程度。

中位数绝对偏差(median absolute deviation)

中位数绝对偏差定义为 MAD=median(\{|x_{i}-\tilde{x}|,1\leq i\leq n\}), 其中 \tilde{x}=median(\{x_{1},\cdots,x_{n}\}), 可以看出数据的偏移程度。

变异系数(coefficient of variation)

变异系数指的是标准差除以均值,i.e.

cv=\frac{\sigma}{\mu},

它表示了集合数据相对于均值的波动程度。

例如:X=\{10,10,10\}, Y=\{9,10,11\}, Z=\{1, 5, 6, 8, 10, 40, 65, 88\}, 通过定义可以计算出它们的变异系数 cv(X)=0, cv(Y)=0.1, cv(Z)=32.9/27.9=1.18. 变异系数越大,表示集合的数据波动程度越大。变异系数越小,表示集合的数据波动程度越小。

参考资料

  1. 集中趋势:https://en.wikipedia.org/wiki/Central_tendency
  2. 离散程度:https://en.wikipedia.org/wiki/Statistical_dispersion
  3. 描述统计学:https://zh.wikipedia.org/wiki/%E6%8F%8F%E8%BF%B0%E7%BB%9F%E8%AE%A1%E5%AD%A6
  4. 数据分析的基础—统计学之描述性统计(一):https://zhuanlan.zhihu.com/p/33544707
  5. 数据分析的基础—统计学之描述性统计(二):https://zhuanlan.zhihu.com/p/34073898

 

A Guide to Time Series Forecasting with ARIMA in Python 3

https://www.digitalocean.com/community/tutorials/a-guide-to-time-series-forecasting-with-arima-in-python-3#step-2-—-importing-packages-and-loading-data

 

Introduction

Time series provide the opportunity to forecast future values. Based on previous values, time series can be used to forecast trends in economics, weather, and capacity planning, to name a few. The specific properties of time-series data mean that specialized statistical methods are usually required.

In this tutorial, we will aim to produce reliable forecasts of time series. We will begin by introducing and discussing the concepts of autocorrelation, stationarity, and seasonality, and proceed to apply one of the most commonly used method for time-series forecasting, known as ARIMA.

One of the methods available in Python to model and predict future points of a time series is known as SARIMAX, which stands for Seasonal AutoRegressive Integrated Moving Averages with eXogenous regressors. Here, we will primarily focus on the ARIMA component, which is used to fit time-series data to better understand and forecast future points in the time series.

Prerequisites

This guide will cover how to do time-series analysis on either a local desktop or a remote server. Working with large datasets can be memory intensive, so in either case, the computer will need at least 2GB of memory to perform some of the calculations in this guide.

To make the most of this tutorial, some familiarity with time series and statistics can be helpful.

For this tutorial, we’ll be using Jupyter Notebook to work with the data. If you do not have it already, you should follow our tutorial to install and set up Jupyter Notebook for Python 3.

Step 1 — Installing Packages

To set up our environment for time-series forecasting, let’s first move into our local programming environment or server-based programming environment:

  • cd environments
  • . my_env/bin/activate

From here, let’s create a new directory for our project. We will call it ARIMA and then move into the directory. If you call the project a different name, be sure to substitute your name for ARIMA throughout the guide

  • mkdir ARIMA
  • cd ARIMA

This tutorial will require the warningsitertoolspandasnumpymatplotlib and statsmodels libraries. The warnings and itertools libraries come included with the standard Python library set so you shouldn’t need to install them.

Like with other Python packages, we can install these requirements with pip.
We can now install pandasstatsmodels, and the data plotting package matplotlib. Their dependencies will also be installed:

  • pip install pandas numpy statsmodels matplotlib

At this point, we’re now set up to start working with the installed packages.

Step 2 — Importing Packages and Loading Data

To begin working with our data, we will start up Jupyter Notebook:

  • jupyter notebook

To create a new notebook file, select New > Python 3 from the top right pull-down menu:

Create a new Python 3 notebook

This will open a notebook.

As is best practice, start by importing the libraries you will need at the top of your notebook:

import warnings
import itertools
import pandas as pd
import numpy as np
import statsmodels.api as sm
import matplotlib.pyplot as plt
plt.style.use('fivethirtyeight')

We have also defined a matplotlib style of fivethirtyeight for our plots.

We’ll be working with a dataset called “Atmospheric CO2 from Continuous Air Samples at Mauna Loa Observatory, Hawaii, U.S.A.,” which collected CO2 samples from March 1958 to December 2001. We can bring in this data as follows:

data = sm.datasets.co2.load_pandas()
y = data.data

Let’s preprocess our data a little bit before moving forward. Weekly data can be tricky to work with since it’s a briefer amount of time, so let’s use monthly averages instead. We’ll make the conversion with the resample function. For simplicity, we can also use the fillna() function to ensure that we have no missing values in our time series.

# The 'MS' string groups the data in buckets by start of the month
y = y['co2'].resample('MS').mean()

# The term bfill means that we use the value before filling in missing values
y = y.fillna(y.bfill())

print(y)
Output
co2
1958-03-01  316.100000
1958-04-01  317.200000
1958-05-01  317.433333
...
2001-11-01  369.375000
2001-12-01  371.020000

Let’s explore this time series e as a data visualization:

y.plot(figsize=(15, 6))
plt.show()

Figure 1: CO2 Levels Time Series

Some distinguishable patterns appear when we plot the data. The time series has an obvious seasonality pattern, as well as an overall increasing trend.

To learn more about time series pre-processing, please refer to “A Guide to Time Series Visualization with Python 3,” where the steps above are described in much more detail.

Now that we’ve converted and explored our data, let’s move on to time series forecasting with ARIMA.

Step 3 — The ARIMA Time Series Model

One of the most common methods used in time series forecasting is known as the ARIMA model, which stands for AutoregRessive Integrated Moving Average. ARIMA is a model that can be fitted to time series data in order to better understand or predict future points in the series.

There are three distinct integers (pdq) that are used to parametrize ARIMA models. Because of that, ARIMA models are denoted with the notation ARIMA(p, d, q). Together these three parameters account for seasonality, trend, and noise in datasets:

  • p is the auto-regressive part of the model. It allows us to incorporate the effect of past values into our model. Intuitively, this would be similar to stating that it is likely to be warm tomorrow if it has been warm the past 3 days.
  • d is the integrated part of the model. This includes terms in the model that incorporate the amount of differencing (i.e. the number of past time points to subtract from the current value) to apply to the time series. Intuitively, this would be similar to stating that it is likely to be same temperature tomorrow if the difference in temperature in the last three days has been very small.
  • q is the moving average part of the model. This allows us to set the error of our model as a linear combination of the error values observed at previous time points in the past.

When dealing with seasonal effects, we make use of the seasonal ARIMA, which is denoted as ARIMA(p,d,q)(P,D,Q)s. Here, (p, d, q) are the non-seasonal parameters described above, while (P, D, Q) follow the same definition but are applied to the seasonal component of the time series. The term s is the periodicity of the time series (4 for quarterly periods, 12 for yearly periods, etc.).

The seasonal ARIMA method can appear daunting because of the multiple tuning parameters involved. In the next section, we will describe how to automate the process of identifying the optimal set of parameters for the seasonal ARIMA time series model.

Step 4 — Parameter Selection for the ARIMA Time Series Model

When looking to fit time series data with a seasonal ARIMA model, our first goal is to find the values of ARIMA(p,d,q)(P,D,Q)s that optimize a metric of interest. There are many guidelines and best practices to achieve this goal, yet the correct parametrization of ARIMA models can be a painstaking manual process that requires domain expertise and time. Other statistical programming languages such as R provide automated ways to solve this issue, but those have yet to be ported over to Python. In this section, we will resolve this issue by writing Python code to programmatically select the optimal parameter values for our ARIMA(p,d,q)(P,D,Q)s time series model.

We will use a “grid search” to iteratively explore different combinations of parameters. For each combination of parameters, we fit a new seasonal ARIMA model with the SARIMAX() function from the statsmodels module and assess its overall quality. Once we have explored the entire landscape of parameters, our optimal set of parameters will be the one that yields the best performance for our criteria of interest. Let’s begin by generating the various combination of parameters that we wish to assess:

# Define the p, d and q parameters to take any value between 0 and 2
p = d = q = range(0, 2)

# Generate all different combinations of p, q and q triplets
pdq = list(itertools.product(p, d, q))

# Generate all different combinations of seasonal p, q and q triplets
seasonal_pdq = [(x[0], x[1], x[2], 12) for x in list(itertools.product(p, d, q))]

print('Examples of parameter combinations for Seasonal ARIMA...')
print('SARIMAX: {} x {}'.format(pdq[1], seasonal_pdq[1]))
print('SARIMAX: {} x {}'.format(pdq[1], seasonal_pdq[2]))
print('SARIMAX: {} x {}'.format(pdq[2], seasonal_pdq[3]))
print('SARIMAX: {} x {}'.format(pdq[2], seasonal_pdq[4]))
Output
Examples of parameter combinations for Seasonal ARIMA...
SARIMAX: (0, 0, 1) x (0, 0, 1, 12)
SARIMAX: (0, 0, 1) x (0, 1, 0, 12)
SARIMAX: (0, 1, 0) x (0, 1, 1, 12)
SARIMAX: (0, 1, 0) x (1, 0, 0, 12)

We can now use the triplets of parameters defined above to automate the process of training and evaluating ARIMA models on different combinations. In Statistics and Machine Learning, this process is known as grid search (or hyperparameter optimization) for model selection.

When evaluating and comparing statistical models fitted with different parameters, each can be ranked against one another based on how well it fits the data or its ability to accurately predict future data points. We will use the AIC (Akaike Information Criterion) value, which is conveniently returned with ARIMA models fitted using statsmodels. The AIC measures how well a model fits the data while taking into account the overall complexity of the model. A model that fits the data very well while using lots of features will be assigned a larger AIC score than a model that uses fewer features to achieve the same goodness-of-fit. Therefore, we are interested in finding the model that yields the lowest AIC value.

The code chunk below iterates through combinations of parameters and uses the SARIMAX function from statsmodels to fit the corresponding Seasonal ARIMA model. Here, the order argument specifies the (p, d, q) parameters, while the seasonal_order argument specifies the (P, D, Q, S) seasonal component of the Seasonal ARIMA model. After fitting each SARIMAX()model, the code prints out its respective AICscore.

warnings.filterwarnings("ignore") # specify to ignore warning messages

for param in pdq:
    for param_seasonal in seasonal_pdq:
        try:
            mod = sm.tsa.statespace.SARIMAX(y,
                                            order=param,
                                            seasonal_order=param_seasonal,
                                            enforce_stationarity=False,
                                            enforce_invertibility=False)

            results = mod.fit()

            print('ARIMA{}x{}12 - AIC:{}'.format(param, param_seasonal, results.aic))
        except:
            continue

Because some parameter combinations may lead to numerical misspecifications, we explicitly disabled warning messages in order to avoid an overload of warning messages. These misspecifications can also lead to errors and throw an exception, so we make sure to catch these exceptions and ignore the parameter combinations that cause these issues.

The code above should yield the following results, this may take some time:

Output
SARIMAX(0, 0, 0)x(0, 0, 1, 12) - AIC:6787.3436240402125
SARIMAX(0, 0, 0)x(0, 1, 1, 12) - AIC:1596.711172764114
SARIMAX(0, 0, 0)x(1, 0, 0, 12) - AIC:1058.9388921320026
SARIMAX(0, 0, 0)x(1, 0, 1, 12) - AIC:1056.2878315690562
SARIMAX(0, 0, 0)x(1, 1, 0, 12) - AIC:1361.6578978064144
SARIMAX(0, 0, 0)x(1, 1, 1, 12) - AIC:1044.7647912940095
...
...
...
SARIMAX(1, 1, 1)x(1, 0, 0, 12) - AIC:576.8647112294245
SARIMAX(1, 1, 1)x(1, 0, 1, 12) - AIC:327.9049123596742
SARIMAX(1, 1, 1)x(1, 1, 0, 12) - AIC:444.12436865161305
SARIMAX(1, 1, 1)x(1, 1, 1, 12) - AIC:277.7801413828764

The output of our code suggests that SARIMAX(1, 1, 1)x(1, 1, 1, 12) yields the lowest AIC value of 277.78. We should therefore consider this to be optimal option out of all the models we have considered.

Step 5 — Fitting an ARIMA Time Series Model

Using grid search, we have identified the set of parameters that produces the best fitting model to our time series data. We can proceed to analyze this particular model in more depth.

We’ll start by plugging the optimal parameter values into a new SARIMAX model:

mod = sm.tsa.statespace.SARIMAX(y,
                                order=(1, 1, 1),
                                seasonal_order=(1, 1, 1, 12),
                                enforce_stationarity=False,
                                enforce_invertibility=False)

results = mod.fit()

print(results.summary().tables[1])
Output
==============================================================================
                 coef    std err          z      P>|z|      [0.025      0.975]
------------------------------------------------------------------------------
ar.L1          0.3182      0.092      3.443      0.001       0.137       0.499
ma.L1         -0.6255      0.077     -8.165      0.000      -0.776      -0.475
ar.S.L12       0.0010      0.001      1.732      0.083      -0.000       0.002
ma.S.L12      -0.8769      0.026    -33.811      0.000      -0.928      -0.826
sigma2         0.0972      0.004     22.634      0.000       0.089       0.106
==============================================================================

The summary attribute that results from the output of SARIMAX returns a significant amount of information, but we’ll focus our attention on the table of coefficients. The coef column shows the weight (i.e. importance) of each feature and how each one impacts the time series. The P>|z| column informs us of the significance of each feature weight. Here, each weight has a p-value lower or close to 0.05, so it is reasonable to retain all of them in our model.

When fitting seasonal ARIMA models (and any other models for that matter), it is important to run model diagnostics to ensure that none of the assumptions made by the model have been violated. The plot_diagnostics object allows us to quickly generate model diagnostics and investigate for any unusual behavior.

results.plot_diagnostics(figsize=(15, 12))
plt.show()

Figure 2: Model Diagnostics

Our primary concern is to ensure that the residuals of our model are uncorrelated and normally distributed with zero-mean. If the seasonal ARIMA model does not satisfy these properties, it is a good indication that it can be further improved.

In this case, our model diagnostics suggests that the model residuals are normally distributed based on the following:

  • In the top right plot, we see that the red KDE line follows closely with the N(0,1) line (where N(0,1)) is the standard notation for a normal distribution with mean 0 and standard deviation of 1). This is a good indication that the residuals are normally distributed.
  • The qq-plot on the bottom left shows that the ordered distribution of residuals (blue dots) follows the linear trend of the samples taken from a standard normal distribution with N(0, 1). Again, this is a strong indication that the residuals are normally distributed.
  • The residuals over time (top left plot) don’t display any obvious seasonality and appear to be white noise. This is confirmed by the autocorrelation (i.e. correlogram) plot on the bottom right, which shows that the time series residuals have low correlation with lagged versions of itself.

Those observations lead us to conclude that our model produces a satisfactory fit that could help us understand our time series data and forecast future values.

Although we have a satisfactory fit, some parameters of our seasonal ARIMA model could be changed to improve our model fit. For example, our grid search only considered a restricted set of parameter combinations, so we may find better models if we widened the grid search.

Step 6 — Validating Forecasts

We have obtained a model for our time series that can now be used to produce forecasts. We start by comparing predicted values to real values of the time series, which will help us understand the accuracy of our forecasts. The get_prediction() and conf_int() attributes allow us to obtain the values and associated confidence intervals for forecasts of the time series.

pred = results.get_prediction(start=pd.to_datetime('1998-01-01'), dynamic=False)
pred_ci = pred.conf_int()

The code above requires the forecasts to start at January 1998.

The dynamic=False argument ensures that we produce one-step ahead forecasts, meaning that forecasts at each point are generated using the full history up to that point.

We can plot the real and forecasted values of the CO2 time series to assess how well we did. Notice how we zoomed in on the end of the time series by slicing the date index.

ax = y['1990':].plot(label='observed')
pred.predicted_mean.plot(ax=ax, label='One-step ahead Forecast', alpha=.7)

ax.fill_between(pred_ci.index,
                pred_ci.iloc[:, 0],
                pred_ci.iloc[:, 1], color='k', alpha=.2)

ax.set_xlabel('Date')
ax.set_ylabel('CO2 Levels')
plt.legend()

plt.show()

Figure 3: CO2 Levels Static Forecast

Overall, our forecasts align with the true values very well, showing an overall increase trend.

It is also useful to quantify the accuracy of our forecasts. We will use the MSE (Mean Squared Error), which summarizes the average error of our forecasts. For each predicted value, we compute its distance to the true value and square the result. The results need to be squared so that positive/negative differences do not cancel each other out when we compute the overall mean.

y_forecasted = pred.predicted_mean
y_truth = y['1998-01-01':]

# Compute the mean square error
mse = ((y_forecasted - y_truth) ** 2).mean()
print('The Mean Squared Error of our forecasts is {}'.format(round(mse, 2)))
Output
The Mean Squared Error of our forecasts is 0.07

The MSE of our one-step ahead forecasts yields a value of 0.07, which is very low as it is close to 0. An MSE of 0 would that the estimator is predicting observations of the parameter with perfect accuracy, which would be an ideal scenario but it not typically possible.

However, a better representation of our true predictive power can be obtained using dynamic forecasts. In this case, we only use information from the time series up to a certain point, and after that, forecasts are generated using values from previous forecasted time points.

In the code chunk below, we specify to start computing the dynamic forecasts and confidence intervals from January 1998 onwards.

pred_dynamic = results.get_prediction(start=pd.to_datetime('1998-01-01'), dynamic=True, full_results=True)
pred_dynamic_ci = pred_dynamic.conf_int()

Plotting the observed and forecasted values of the time series, we see that the overall forecasts are accurate even when using dynamic forecasts. All forecasted values (red line) match pretty closely to the ground truth (blue line), and are well within the confidence intervals of our forecast.

ax = y['1990':].plot(label='observed', figsize=(20, 15))
pred_dynamic.predicted_mean.plot(label='Dynamic Forecast', ax=ax)

ax.fill_between(pred_dynamic_ci.index,
                pred_dynamic_ci.iloc[:, 0],
                pred_dynamic_ci.iloc[:, 1], color='k', alpha=.25)

ax.fill_betweenx(ax.get_ylim(), pd.to_datetime('1998-01-01'), y.index[-1],
                 alpha=.1, zorder=-1)

ax.set_xlabel('Date')
ax.set_ylabel('CO2 Levels')

plt.legend()
plt.show()

Figure 4: CO2 Levels Dynamic Forecast

Once again, we quantify the predictive performance of our forecasts by computing the MSE:

# Extract the predicted and true values of our time series
y_forecasted = pred_dynamic.predicted_mean
y_truth = y['1998-01-01':]

# Compute the mean square error
mse = ((y_forecasted - y_truth) ** 2).mean()
print('The Mean Squared Error of our forecasts is {}'.format(round(mse, 2)))
Output
The Mean Squared Error of our forecasts is 1.01

The predicted values obtained from the dynamic forecasts yield an MSE of 1.01. This is slightly higher than the one-step ahead, which is to be expected given that we are relying on less historical data from the time series.

Both the one-step ahead and dynamic forecasts confirm that this time series model is valid. However, much of the interest around time series forecasting is the ability to forecast future values way ahead in time.

Step 7 — Producing and Visualizing Forecasts

In the final step of this tutorial, we describe how to leverage our seasonal ARIMA time series model to forecast future values. The get_forecast() attribute of our time series object can compute forecasted values for a specified number of steps ahead.

# Get forecast 500 steps ahead in future
pred_uc = results.get_forecast(steps=500)

# Get confidence intervals of forecasts
pred_ci = pred_uc.conf_int()

We can use the output of this code to plot the time series and forecasts of its future values.

ax = y.plot(label='observed', figsize=(20, 15))
pred_uc.predicted_mean.plot(ax=ax, label='Forecast')
ax.fill_between(pred_ci.index,
                pred_ci.iloc[:, 0],
                pred_ci.iloc[:, 1], color='k', alpha=.25)
ax.set_xlabel('Date')
ax.set_ylabel('CO2 Levels')

plt.legend()
plt.show()

Figure 5: Time Series and Forecast of Future Values

Both the forecasts and associated confidence interval that we have generated can now be used to further understand the time series and foresee what to expect. Our forecasts show that the time series is expected to continue increasing at a steady pace.

As we forecast further out into the future, it is natural for us to become less confident in our values. This is reflected by the confidence intervals generated by our model, which grow larger as we move further out into the future.

Conclusion

In this tutorial, we described how to implement a seasonal ARIMA model in Python. We made extensive use of the pandas and statsmodels libraries and showed how to run model diagnostics, as well as how to produce forecasts of the CO2 time series.

Here are a few other things you could try:

  • Change the start date of your dynamic forecasts to see how this affects the overall quality of your forecasts.
  • Try more combinations of parameters to see if you can improve the goodness-of-fit of your model.
  • Select a different metric to select the best model. For example, we used the AIC measure to find the best model, but you could seek to optimize the out-of-sample mean square error instead.

For more practice, you could also try to load another time series dataset to produce your own forecasts.

9 Comments

  • B
  • I
  • UL
  • OL
  • Code
  • Highlight
  • Table
  • 0

    Hi! Thanks for sharing this.
    I was trying to forecast hourly values. The seasonality to capture should be similar as the 168th previous value. This means, Friday 9PM of this week should be similar than Friday 9PM of the past week.
    That is why I decided to use 168 seasionality (24*7) but it takes very long and consumes lots of memory. I’ve tried several times using 7 and 24 seasionality but it wasn’t doing it well when forecasting (previous fitting with dynamic set to False was working perfectly). Do you have any advice for this situation? Thanks in advance.

  • 0

    Thanks for the Guide.

    I tried this with my own data. And at the model result summary part, I got ma.L1 having p-value over 0.88. So, I definitely want to get rid of this feature from the model. But how do I do that? How to remove a feature from the model??

    • 0

      Hi!
      Thanks for taking the time to read through this tutorial! Yes, a p-value of 0.88 would suggest that your ma.L1 feature is not very informative. The simplest way to start would be to try and remove the MA features from your model. You can achieve this by refitting your time-series models while explicitly setting the Q parameter to zero, this will ensure that no MA components are used when you fit your model.

  • 0

    very nice tutorial. thanks! I am a new one to ARIMA model, I want to ask you some questions.
    1) I found you use all the historical data to fit an ARIMA Time Series Model, and use part of all the historical data to validate mode, with code: pred = results.getprediction(start=pd.todatetime(‘1998-01-01’), dynamic=False) predci = pred.confint()
    But why the data to validate model is one part of data for fitting model before. You know in machine learning, the train data and test data is split, I don’t why here is different.
    2) how about data stationary, could you tell me why you set the enforce_stationary is false.

    3) how about days data not month data(average) for fit mode to predict, how about week data for prediction, could you tell me how to do it

    thanks!

  • 0

    I got an error on line:
    pred = results.getprediction(start=pd.todatetime(‘1998-02-01’), dynamic=False)

    File “pandas_libs\tslib.pyx”, line 1080, in pandas.libs.tslib.Timestamp.richcmp (pandas_libs\tslib.c:20281)
    TypeError: Cannot compare type ‘Timestamp’ with type ‘int’

    How can I solve it?

  • 1

    Hi.
    Thank you so much for your wonderful sharing. Is there are any way to catch the minimum value of AIC automatically?
    It would be wonderful, if the best set for ARIMAX was stored on a external variable and pass them to next step.
    Is it possible? how?
    Thanks you

    • 0

      Use this code

      warnings.filterwarnings("ignore") # specify to ignore warning messages
      AIC_list = pd.DataFrame({}, columns=['pram','param_seasonal','AIC'])
      for param in pdq:
          for param_seasonal in seasonal_pdq:
              try:
                  mod = sm.tsa.statespace.SARIMAX(y,
                                                  order=param,
                                                  seasonal_order=param_seasonal,
                                                  enforce_stationarity=False,
                                                  enforce_invertibility=False)
      
                  results = mod.fit()
      
                  print('ARIMA{}x{} - AIC:{}'.format(param, param_seasonal, results.aic))
                  temp = pd.DataFrame([[ param ,  param_seasonal , results.aic ]], columns=['pram','param_seasonal','AIC'])
                  AIC_list = AIC_list.append( temp, ignore_index=True)  # DataFrame append 는 일반 list append 와 다르게 이렇게 지정해주어야한다.
                  del temp
      
              except:
                  continue
      
      m = np.amin(AIC_list['AIC'].values) # Find minimum value in AIC
      l = AIC_list['AIC'].tolist().index(m) # Find index number for lowest AIC
      Min_AIC_list = AIC_list.iloc[l,:]
      
      print("### Min_AIC_list ### \n{}".format(Min_AIC_list))
      
      mod = sm.tsa.statespace.SARIMAX(y,
                                      order=Min_AIC_list['pram'],
                                      seasonal_order=Min_AIC_list['pram_seasonal'],
                                      enforce_stationarity=False,
                                      enforce_invertibility=False)
      
      results = mod.fit()
      
      print(results.summary().tables[1])
      
      results.plot_diagnostics(figsize=(15, 12))
      plt.show()
      
      
    • 0

      Revised code (sorry in the previous code, I was missing one thing.)

      
      warnings.filterwarnings("ignore") # specify to ignore warning messages
      AIC_list = pd.DataFrame({}, columns=['param','param_seasonal','AIC'])
      for param in pdq:
          for param_seasonal in seasonal_pdq:
              try:
                  mod = sm.tsa.statespace.SARIMAX(y,
                                                  order=param,
                                                  seasonal_order=param_seasonal,
                                                  enforce_stationarity=False,
                                                  enforce_invertibility=False)
      
                  results = mod.fit()
      
                  print('ARIMA{}x{} - AIC:{}'.format(param, param_seasonal, results.aic))
                  temp = pd.DataFrame([[ param ,  param_seasonal , results.aic ]], columns=['param','param_seasonal','AIC'])
                  AIC_list = AIC_list.append( temp, ignore_index=True)  # DataFrame append 는 일반 list append 와 다르게 이렇게 지정해주어야한다.
                  del temp
      
              except:
                  continue
      
      
      m = np.amin(AIC_list['AIC'].values) # Find minimum value in AIC
      l = AIC_list['AIC'].tolist().index(m) # Find index number for lowest AIC
      Min_AIC_list = AIC_list.iloc[l,:]
      
      
      
      mod = sm.tsa.statespace.SARIMAX(y,
                                      order=Min_AIC_list['param'],
                                      seasonal_order=Min_AIC_list['param_seasonal'],
                                      enforce_stationarity=False,
                                      enforce_invertibility=False)
      results = mod.fit()
      
      print("### Min_AIC_list ### \n{}".format(Min_AIC_list))
      
      print(results.summary().tables[1])
      
      results.plot_diagnostics(figsize=(15, 12))
      plt.show()
      
      

交叉验证(Cross Validation)

交叉验证的定义

交叉验证(Cross Validation),有的时候也称作循环估计(Rotation Estimation),是一种统计学上将数据样本切割成较小子集的实用方法,该理论是由Seymour Geisser提出的。

在模式识别(Pattern Recognition)和机器学习(Machine Learning)的相关研究中,经常会将整个数据集合分成两个部分,分别是训练集合和测试集合。假设X是集合全体,A\subseteq X是全集X的非空真子集,那么非空集合X\setminus A\neq \emptyset则是集合A在全集X中的补集。于是可以先在A上面做训练和分析,而集合X\setminus A则用来做测试和验证。一开始的集合A被称作训练集,而它的补集X\setminus A被称作验证集或者测试集。这里有一个重要的观点就是:只有训练集才可以使用在模型的训练之中,而测试集必须在模型训练完成之后才被用来评估模型的误差。

HoldOut检验(Hold-Out Method)

这个方法是将原始的数据集合X随机分成两个集合AX\setminus A,其中A作为训练集,X\setminus A作为测试集。先使用训练集训练模型,然后利用测试集验证模型的效果,记录最后的分类准确率作为Hold-Out下该模型的性能指标。比方说,处理时间序列模型是否准确的时候,把整个数据集合分成前后两部分,前部分占比70%,后部分占比30%。前部分来进行时间序列模型的训练,后部分用来测试改时间序列的准确性。其准确性可以用MAE,MAPE之类的统计指标来衡量。综上所述,该方法的好处就是处理起来简单,只需要把原始数据分成两个部分即可。但是从严格意义上来说,Hold-Out检验并不算是交叉检验(Cross Validation),因为该方法没有达到交叉检验的思想,而且最后验证准确性的高低和原始数组的分类有很大的关系,所以该方法得到的结果在某些场景中并不具备特别大的说服力。 在Hold-Out检验不够有说服力的情形下,有人提出了交叉验证这一个重要思想。

交叉检验的常见形式

假设有一个未知模型有一个或者多个未知的参数,并且有一个训练集。操作的过程就是对该模型的参数进行调整,使得该模型能够最大的反映训练集的特征。如果模型因为训练集过小或者参数不合适而产生过度拟合的情况,测试集的测试效果就可以得到验证。交叉验证是一种能够预测模型拟合性能的有效方法。

彻底的交叉验证(Exhaustive Cross Validation)

彻底的交叉验证方法指的是遍历全集X的所有非空真子集A。换句话说也就是把A当作训练集,X\setminus A是测试集。如果X中有n个元素,那么非空真子集A的选择方法则是2^{n}-2,这个方法的时间复杂度是指数级别的。

留P验证(Leave-p-out Cross Validation)

p验证(LpO CV)指的是使用全集X中的p个元素作为测试集,然后剩下的n-p个元素作为训练集。根据数学上的定理可以得到,p个元素的选择方法有C_{n}^{p}=n!/(p!\cdot(n-p)!)个,其中n!表示n的阶乘。在这个意义下,留p验证的时间复杂度也是非常高的。当p=1的时候,留1验证(Leave-one-out Cross Validation)的复杂度恰好是C_{n}^{1}=n

不彻底的交叉验证(Non-exhaustive Cross Validation)

不彻底的交叉验证不需要考虑全集X的所有划分情况,这种方法是留p验证的一个近似验证算法。

k-fold交叉验证(K-fold Cross Validation)

在k-fold交叉验证中,全集X被随机的划分成k个同等大小的集合A_{1},\cdot\cdot\cdot,A_{k},换句话说也就是X=A_{1}\cup\cdot\cdot\cdot\cup A_{k},并且|A_{1}|=\cdot\cdot\cdot=|A_{k}|。这里的|A_{i}|指的是集合A_{i}的元素个数,也就是集合的势。这个时候需要遍历i1k,把X\setminus A当作训练集合,A_{i}当作测试集合。根据模型的测试统计,可以得到A_{i}集合中测试错误的结果数量n_{i}。如果全集X的势是n的话,可以得到该模型的错误率是E=\sum_{i=1}^{k}n_{i}/n 为了提高模型的精确度,可以将k-fold交叉验证的上述步骤重复t次,每一次都是随机划分全集X。在t次测试中,会得到t个模型的错误率E_{1},\cdot\cdot\cdot, E_{t}。定义e=\sum_{j=1}^{t}E_{j}/t, V=\sum_{j=1}^{t}(E_{j}-e)^{2}/(t-1)\sigma=\sqrt{V}。这样该模型的错误率就是e,其方差是\sigma

注释:

1. 一般来说,10-fold交叉验证的情况使用得最多。

2. k=2的时候,也就是最简单的k-fold交叉验证,2-fold交叉验证。这个时候X=A_{1}\cup A_{2},首先A_{1}当训练集并且A_{2}当测试集,然后A_{2}当训练集并且A_{1}当测试集。2-fold交叉验证的好处就是训练集和测试集的势都非常大,每个数据要么在训练集中,要么在测试集中。

3. k=n的时候,也就是n-fold交叉验证。这个时候就是上面所说的留一验证(Leave-one-out Cross Validation)。 综上所述,交叉验证(Cross Validation)的好处是可以从有限的数据中获得尽可能多的有效信息,从而可以从多个角度去学习样本,避免陷入局部的极值。在这个过程中,无论是训练样本还是测试样本都得到了尽可能多的学习。

一般模型的选择过程

在了解了交叉验证的方法之后,可以来介绍一般模型的选择过程。通过采用不同的输入训练样本,来决定机器学习算法中包含的各个参数值,称作模型选择。下面伪代码表示了模型选择的一般流程。在这个算法中,最重要的就是第三个步骤中的误差评价。

(1)准备候选的\ell个模型:M_{1},\cdot\cdot\cdot,M_{\ell}

(2)对每个模型M_{1},\cdot\cdot\cdot, M_{\ell}求解它的学习结果。

(3)对每个学习结果的误差e_{1},\cdot\cdot\cdot,e_{\ell}进行计算。这里可以使用上面所说的k-fold交叉验证方法。

(4)选择误差e_{1},\cdot\cdot\cdot,e_{\ell}最小的模型作为最终的模型。

 

时间序列模型之相空间重构模型

一般的时间序列主要是在时间域中进行模型的研究,而对于混沌时间序列,无论是混沌不变量的计算,混沌模型的建立和预测都是在所谓的相空间中进行,因此相空间重构就是混沌时间序列处理中非常重要的一个步骤。所谓混沌序列,可以看作是考察混沌系统所得到的一组随着时间而变化的观察值。假设时间序列是\{ x(i): i=1,\cdot \cdot \cdot, n\}, 那么吸引子的结构特性就包含在这个时间序列之中。为了从时间序列中提取出更多有用的信息,1980年Packard等人提出了时间序列重构相空间的两种方法:导数重构法和坐标延迟重构法。而后者的本质则是通过一维的时间序列\{x(i)\}的不同延迟时间\tau来构建d维的相空间矢量

y(i)=(x(i),...,x(i+(d-1)\tau)), 1\leq i\leq n-(d-1)\tau.

1981年Takens提出嵌入定理:对于无限长,无噪声的d^{'}维混沌吸引子的一维标量时间序列\{x(i): 1\leq i \leq n\}都可以在拓扑不变的意义下找到一个d维的嵌入相空间,只要维数d\geq 2d^{'}+1. 根据Takens嵌入定理,我们可以从一维混沌时间序列中重构一个与原动力系统在拓扑意义下一样的相空间,混沌时间序列的判定,分析和预测都是在这个重构的相空间中进行的,因此相空间的重构就是混沌时间序列研究的关键。

1. 相空间重构

相空间重构技术有两个关键的参数:嵌入的维数d和延迟时间\tau. 在Takens嵌入定理中,嵌入维数和延迟时间都只是理论上证明了其存在性,并没有给出具体的表达式,而且实际应用中时间序列都是有噪声的有限序列,嵌入维数和时间延迟必须要根据实际的情况来选取合适的值。

关于嵌入维数d和延迟时间\tau的取值,通常有两种观点:第一种观点认为d\tau是互不相关的,先求出延迟时间之后再根据它求出合适的嵌入维数。求出延迟时间\tau比较常用的方法主要有自相关法,平均位移法,复自相关法和互信息法等,关键的地方就是使得原时间序列经过时间延迟之后可以作为独立的坐标来使用。同时寻找嵌入维数的方法主要是几何不变量方法,虚假最临近法(False Nearest Neighbors)和它改进之后的Cao方法等。第二种观点则是认为延迟时间和嵌入维数是相关的。1996年Kugiumtzis提出的时间窗长度则是综合考虑两者的重要参数。1999年,Kim等人提出了C-C方法,该方法使用关联积分同时估计出延迟时间和时间窗。

(1) 延迟时间\tau的确定:

如果延迟时间\tau太小,则相空间向量

y(i)=(x(i),\cdot\cdot\cdot, x(i+(d-1)\tau), 1\leq i\leq n-(d-1)\tau

中的两个坐标分量x(i+j\tau)x(i+(j+1)\tau)在数值上非常接近,以至于无法相互区分,从而无法提供两个独立的坐标分量;但是如果延迟时间\tau太大的话,则两个坐标分量又会出现一种完全独立的情况,混沌吸引子的轨迹在两个方向上的投影就毫无相关性可言。因此需要合适的方法来确定一个合适的延迟时间\tau, 从而在独立和相关两者之间达到一种平衡。

(1.1) 自相关系数法:

自相关函数是求延迟时间\tau比较简单的一种方法,它的主要理念就是提取序列之间的线性相关性。对于混沌序列x(1), x(2),\cdot \cdot \cdot, x(n), 可以写出其自相关函数如下:

R(\tau)=\frac{1}{n}\sum_{i=1}^{n-\tau}x(i)x(i+\tau).

此时就可以使用已知的数据x(1),\cdot \cdot\cdot x(n)来做出自相关函数R(\tau)随着延迟时间\tau变化的图像,当自相关函数下降到初始值R(0)1-e^{-1}时,i.e. R(\tau)=(1-e^{-1})R(0), 所得到的时间\tau也就是重构相空间的延迟时间。虽然自相关函数法是一种简便有效的计算延迟时间的方法,但是它仅仅能够提取时间序列的线性相关性。根据自相关函数法可以让x(i), x(i+\tau)以及x(i+\tau), x(i+2\tau)之间不相关,但是x(i), x(i+2\tau)之间的相关性可能会很强。这一点意味着这种方法并不能够有效的推广到高维的研究。而且选择下降系数1-e^{-1}时,这一点可能有点主观性,需要根据具体的情况做适当的调整。因此再总结了自相关法的不足之后,下面介绍一种判断系统非线性关系性的一种方法:交互信息法。

(1.2) 交互信息法:

在考虑了以上方法的局限性之后,Fraser和Swinney提出了交互信息法(Mutual Information Method)。假设两个离散信息系统\{ s_{1},\cdot \cdot \cdot, s_{m}\}, \{q_{1},\cdot\cdot\cdot, q_{n}\}所构成的系统SQ。通过信息论和遍历论的知识,从两个系统中获得的信息熵分别是:

H(S)=-\sum_{i=1}^{m}P_{S}(s_{i})log_{2}P_{S}(s_{i}),

H(Q)=-\sum_{j=1}^{n}P_{Q}(q_{j})log_{2}P_{Q}(q_{j}).

其中P_{S}(s_{i}), P_{Q}(q_{i})分别是SQ中事件s_{i}q_{i}的概率。交互信息的计算公式是:

I(S,Q)=H(S)+H(Q)-H(S,Q),

其中H(S,Q)=-\sum_{i=1}^{m}\sum_{j=1}^{n}P_{S,Q}(s_{i},q_{j})log_{2}P_{S,Q}(s_{i},q_{j}).

这里的P_{S,Q}(s_{i},q_{j})称为事件s_{i}和事件q_{j}的联合分布概率。交互信息标准化就是

I(S,Q)=I(S,Q)/\sqrt{H(S)\times H(Q)}.

现在,我们通过信息论的方法来计算延迟时间\tau. 定义(S,Q)=(x(i), x(i+\tau)), 1\leq i\leq n-\tau, 也就是S代表时间x(i), Q代表时间x(i+\tau). 那么I(S,Q)则是关于延迟时间\tau的函数,可以写成I(\tau). I(\tau)的大小表示在已知系统S,也就是x(i)的情况下,系统Q也就是x(i+\tau)的确定性的大小。I(\tau)=0表示x(i)x(i+\tau)是完全不可预测的,也就是说两者完全不相关。而I(\tau)的第一个极小值,则表示了x(i)x(i+\tau)是最大可能的不相关,重构时使用I(\tau)的第一个极小值最为最优的延迟时间\tau.

交互信息法的关键在于怎么计算联合概率分布P_{S,Q}(s_{i},q_{j})以及SQ系统的概率分布P_{S}(s_{i})P_{Q}(q_{j}). 这里采取的方法是等间距格子法,其方法简要概述如下。

(S,Q)=(x(i), x(i+\tau)), 1\leq i \leq n-\tau,(S,Q)平面用一个矩形包含上面所有的点。将矩形S方向上等分成M_{1}份,Q方向等分成M_{2}份(注:M_{1}, M_{2}取值100~200之间即可)。那么在S方向上格子的长度就是\epsilon_{1}, Q方向上格子的长度就是\epsilon_{2}. 假设(a,b)(S,Q)平面矩形左下角的顶点坐标。

如果(i-1)\epsilon_{1}\leq s-a <i\epsilon_{1}, 那么s在第i个格子中,对Row[i]做一次记录;

如果(j-1)\epsilon_{2}\leq s-b <j\epsilon_{2}, 那么q在第j个格子中,对Col[j]做一次记录。x(i)x(i+\tau)序列的总数都是n-\tau, 那么

P_{S}(i)=Row[i]/(n-\tau), 1\leq i\leq M_{1},

P_{Q}(j)=Col[j]/(n-\tau), 1\leq j\leq M_{2}.

如果(i-1)\epsilon_{1}\leq s-a<i\epsilon_{1}(j-1)\epsilon_{2}\leq q-b < j\epsilon_{2},(s,q)在标号为(i,j)的格子中,对Together[i][j]做一次记录。那么

P_{S,Q}(i,j)=Together[i][j]/(n-\tau)^{2}, 1\leq i\leq M_{1}, 1\leq j\leq M_{2}.

根据以上信息论的公式,可以得到:

H(S)=-\sum_{i=1}^{M_{1}}P_{S}(i)log_{2}P_{S}(i),

H(Q)=-\sum_{j=1}^{M_{2}}P_{Q}(j)log_{2}P_{Q}(j),

H(S,Q)=-\sum_{i=1}^{M_{1}}\sum_{j=1}^{M_{2}}P_{S,Q}(i,j)log_{2}P_{S,Q}(i,j).

I(S,Q)=H(S)+H(Q)-H(S,Q),

I(S,Q)=I(S,Q)/\sqrt{H(S)\times H(Q)}.

交互信息曲线I(\tau)=I(S,Q)第一次下降到极小值所对应的延迟时间\tau则是最佳延时时间。

(2) 嵌入维数d的确定:

(2.1) 几何不变量法:

为了确定嵌入维数d, 实际应用中通常的方法是计算吸引子的某些几何不变量(如关联维数,Lyapunov指数等)。选择好延迟时间\tau之后逐渐增加维数d, 直到他们停止变化为止。从Takens嵌入定理分析可知,这些几何不变量具有吸引子的几何性质,当维数d大于最小嵌入维数的时候,几何结构已经被完全打开,此时这些几何不变量与嵌入的维数无关。基于此理论,可以选择吸引子的几何不变量停止变化时的嵌入维数d作为重构的相空间维数。

(2.2) 虚假最临近点法:

从几何的观点来看,混沌时间序列是高维相空间混沌运动的轨迹在一维空间的投影,在这个投影的过程中,混沌运动的轨迹就会被扭曲。高维相空间并不相邻的两个点投影到一维空间上有的时候就会成为相邻的两点,也就是虚假邻点。重构相空间,实际上就是从混沌时间序列中恢复混沌运动的轨迹,随着嵌入维数的增大,混沌运动的轨道就会被打开,虚假邻点就会被逐渐剔除,从而整个混沌运动的轨迹得到恢复,这个思想就是虚假最临近点法的关键。

d维相空间中,每一个矢量

y_{i}(d)=(x(i),\cdot \cdot \cdot, x(i+(d-1)\tau), 1\leq i\leq n-(d-1)\tau

都有一个欧几里德距离的最邻近点y_{n(i,d)}(d), (n(i,d)\neq i, 1\leq n(i,d)\leq n-(d-1)\tau) 其距离是

R_{i}(d)=||y_{i}(d)-y_{n(i,d)}(d)||_{2}.

当相空间的维数从d变成d+1时,这两个点的距离就会发生变化,新的距离是R_{i}(d+1), 并且

(R_{i}(d+1))^{2}=(R_{i}(d))^{2}+||x(i+d\tau)-x(n(i,d)+d\tau)||_{2}^{2}.

如果R_{i}(d+1)R_{i}(d)大很多,那么就可以认为这是由于高维混沌吸引子中两个不相邻得点投影到低维坐标上变成相邻的两点造成的,这样的临近点是虚假的,令

a_{1}(i,d)=||x(i+d\tau)-x(n(i,d)+d\tau)||_{2}/R_{d}(i).

如果a_{1}(i,d)>R_{\tau} \in [10,50], 那么y_{n(i,d)}(d)就是y_{i}(d)的虚假最临近点。这里的R_{\tau}是阀值。

对于实际的混沌时间序列,从嵌入维数的最小值2开始,计算虚假最临近点的比例,然后逐渐增加维数d, 直到虚假最临近点的比例小于5%或者虚假最临近点的不再随着d的增加而减少时,可以认为混沌吸引子已经完全打开,此时的d就是嵌入维数。在相空间重构方面,虚假最临近点法(FNN)被认为是计算嵌入维数很有效的方法之一。

(2.3) 虚假最临近点法的改进-Cao方法:

虚假最临近点法对数据的噪声比较敏感,而且实际操作中需要选择阀值R_{\tau}, 具有很强的主观性。此时Cao Liangyue教授提出了改进的FNN方法,此方法计算时只需要延迟时间\tau一个参数,用较小的数据量就可以求的嵌入维数d.

假设我们有时间序列x(1),\cdot\cdot\cdot, x(n). 那么基于延迟时间\tau所构造的向量空间就是:y_{i}(d)=(x(i),\cdot\cdot\cdot x(i+(d-1)\tau)), i=1,2, \cdot\cdot\cdot, n-(d-1)\tau, 这里的d是作为嵌入维数,\tau是作为嵌入时间。y_{i}(d)则表示第id维重构向量。与虚假最临近点法类似,定义变量

a(i,d)=||y_{i}(d+1)-y_{n(i,d)}(d+1)||_{\infty}/||y_{i}(d)-y_{n(i,d)}(d)||_{\infty}, i=1,2,\cdot\cdot\cdot n-d\tau,

这里的||\cdot ||_{\infty}是最大模范数。其中y_{i}(d+1)是第id+1维的重构向量,i.e. y_{i}(d+1)=(x(i),\cdot\cdot\cdot, x(i+d\tau)). n(i,d) \in \{1,\cdot\cdot\cdot, n-d\tau\}是使得y_{n(i,d)}(d)d维相空间里面,在最大模范数下与y_{i}(d)最近的向量,并且n(i,d)\neq i,显然n(i,d)id所决定。

定义

E(d)=(n-d\tau)^{-1}\cdot\sum_{i=1}^{n-d\tau} a(i,d).

E(d)与延迟时间\tau和嵌入维数d有关。为了发现从dd+1,该函数变化了多少,可以考虑E1(d)=E(d+1)/E(d).d大于某个值d_{0}时,E1(d)不再变化,那么d_{0}+1则是我们所寻找的最小嵌入维数。除了E1(d), 还可以定义一个变量E2(d)如下。令

E^{*}(d)=(n-d\tau)^{-1}\cdot \sum_{i=1}^{n-d\tau}|x(i+d\tau)-x(n(i,d)+d\tau)|,

这里的n(i,d)和上面的定义一样,也就是使得y_{n(i,d)}(d)y_{i}(d)最近的整数,并且n(i,d)\neq i. 定义E2(d)=E^{*}(d+1)/E^{*}(d). 对于随机序列,数据之间没有相关性,E2(d)\equiv 1. 对于确定性的序列,数据点之间的关系依赖于嵌入维数d的变化,总存在一些值使得E2(d)\neq 1.

2. 相空间的预测

通过前面的相空间重构过程,一个混沌时间序列x(1),\cdot \cdot \cdot, x(n)可以确定其延迟时间\tau和嵌入维数d, 形成n-(d-1)\taud维向量:

\vec{y}_{1}=y_{1}(d)=(x(1),\cdot\cdot\cdot, x(1+(d-1)\tau)),

\vec{y}_{2}=y_{2}(d)=(x(2),\cdot\cdot\cdot, x(2+(d-1)\tau)),

\cdot \cdot \cdot

\vec{y}_{i}=y_{i}(d)=(x(i),\cdot\cdot\cdot, x(i+(d-1)\tau)),

\cdot \cdot \cdot

\vec{y}_{n-(d-1)\tau}=y_{n-(d-1)\tau}(d)=(x(n-(d-1)\tau),\cdot\cdot\cdot, x(n)).

这样我们就可以在d维欧式空间\mathbb{R}^{d}建立一个动力系统的模型如下:

\vec{y}_{i+1}=F(\vec{y}_{i}), 其中F是一个连续函数。

(1) 局部预测法:

假设N=n-(d-1)\tau, 根据连续函数的性质可以知道:如果\vec{y}_{N}\vec{y}_{j}非常接近,那么\vec{y}_{N+1}\vec{y}_{j+1}也是非常接近的,可以用x(j+1+(d-1)\tau)作为x(N+1+(d-1)\tau)=x(n+1)的近似值。

(1.1) 局部平均预测法:

考虑向量\vec{y}_{N}k个最临近向量\vec{y}_{t_{1}},\cdot\cdot\cdot, \vec{y}_{t_{k}}. i.e. 也就是从其他的N-1个向量中选取前k个与\vec{y}_{N}最临近的向量,此处用欧几里德范数||\cdot ||_{2}或者最大模范数||\cdot ||_{\infty}. 根据局部预测法的观点,可以得到x(n+1)的近似值:

x(n+1)\approx k^{-1}\cdot \sum_{j=1}^{k}x(t_{j}+1+(d-1)\tau) .

也可以引入权重的概念来计算近似值:

x(n+1)\approx \sum_{j=1}^{k}x(t_{j}+1+(d-1)\tau)\cdot \omega(\vec{y}_{t_{j}},\vec{y}_{N}).

其中\omega(\vec{y}_{t_{j}}, \vec{y}_{N})=K_{h}(||\vec{y}_{t_{j}}-\vec{y}_{N}||) / \sum_{j=1}^{k} K_{h}(||\vec{y}_{t_{j}}-\vec{y}_{N}||), 1\leq j \leq k,

K(x)=exp(-x^{2}/2),

K_{h}(x)=h^{-1}K(h^{-1}x)=h^{-1}exp(-x^{2}/(2*h^{2})).

(1.2) 局部线性预测法:

假设N=n-(d-1)\tau, 则有y_{N}(d)=(x(N),\cdot\cdot\cdot, x(n)).

局部线性预测模型为

\hat{x}(n+1)=c_{0}x(N)+c_{1}x(N+\tau)+\cdot\cdot\cdot+c_{d-1}x(N+(d-1)\tau)+c_{d},

其中c_{i}, 0\leq i\leq d是待定系数。假设向量\vec{y}_{N}k个最临近向量是\vec{y}_{t_{1}},\cdot\cdot\cdot,\vec{y}_{t_{k}}. 此处可以用欧几里德范数||\cdot ||_{2}或者最大模范数||\cdot ||_{\infty}. 这里使用最小二乘法来求出c_{i}, 0\leq i\leq d的值。也就是求c_{i}使得

||Ab-Y||_{2}^{2}=\sum_{j=1}^{k}(\hat{x}(t_{j}+1+(d-1)\tau)-x(t_{j}+1+(d-1)\tau))^{2}

=\sum_{j=1}^{k}(c_{0}x(t_{j})+c_{1}x(t_{j}+\tau)+\cdot\cdot\cdot+c_{d-1}x(t_{j}+(d-1)\tau)+c_{d}-x(t_{j}+1+(d-1)\tau))^{2}

取得最小值,其中Y=(x(t_{1}+1+(d-1)\tau), x(t_{2}+1+(d-1)\tau),\cdot\cdot\cdot x(t_{k}+1+(d-1)\tau))^{T}, b=(c_{0},c_{1},\cdot\cdot\cdot, c_{d})^{T}, 矩阵A的第j行是d+1维向量(x(t_{j}),\cdot\cdot\cdot, x(t_{j}+(d-1)\tau), 1), 1\leq j\leq k. 根据最小二乘法可以得到待定系数向量b=(A^{T}A)^{-1}A^{T}Y是最小二乘解。

(1.3) 局部多项式预测法:

(2) 全局预测法:

(2.1) 神经网络
(2.2) 小波网络
(2.3) 遗传算法

3. 实验数据

这里使用Lorenz模型来作为测试数据,

dx/dt=\sigma(y-x),

dy/dt=x(\rho-z)-y,

dz/dt=xy-\beta z,

其中x, y, z是系统的三个坐标,\sigma, \rho, \beta是三个系数。在这里,我们取\rho=28, \sigma=10, \beta=8/3.在解这个常微分方程的时候,使用了经典的Runge-Kutta数值方法。

测试数据1:

使用1300个点,预测未来100个点,绿色曲线表示Lorenz模型在z坐标上的实际数据,红线右侧表示开始预测,蓝色曲线表示使用相空间重构模型所预测的数据。根据统计学分析可以得到:在红色线条的右侧,蓝色曲线的点和绿色曲线的点的Normalized Root Mean Square Error<8%, Mean Absolute Percentage Error<5.3%, 相关系数>97%.

n=1300 1

把红线附近的图像放大可以看的更加清楚:

n=1300 2

测试数据2:

使用1800个点,预测未来100个点,绿色曲线表示Lorenz模型在x坐标上的实际数据,红线右侧表示开始预测,蓝色曲线表示使用相空间重构模型所预测的数据。根据统计学分析可以得到:在红色线条的右侧,蓝色曲线的点和绿色曲线的点的Normalized Root Mean Square Error<1%, Mean Absolute Percentage Error<2%, 相关系数>99%.

n=1800

把红线附近的图像放大:

n=1800 2

使用同样的数据量,也就是n=1800,预测未来100个点,绿色曲线表示Lorenz模型在z坐标上的实际数据,红线右侧表示开始预测,蓝色曲线表示使用相空间重构模型所预测的数据。根据统计学分析可以得到:在红色线条的右侧,蓝色曲线的点和绿色曲线的点的Normalized Root Mean Square Error<1%, Mean Absolute Percentage Error<1%, 相关系数>99%.

n=1800 y

把红线附近的图像放大:

n=1800 y2

参考文献:

  1. https://en.wikipedia.org/wiki/Lorenz_system
  2. https://en.wikipedia.org/wiki/Root-mean-square_deviation
  3. https://en.wikipedia.org/wiki/Mean_absolute_percentage_error
  4. Practical method for determining the minimum embedding dimension of a scalar time series
  5. 基于混沌理论的往复式压缩机故障诊断
  6. determining embedding dimension for phase-space reconstruction using a geometric construction
  7. Time Series Prediction by Chaotic Modeling of Nonlinear Dynamical Systems
  8. nonlinear dynamics delay times and embedding windows

时间序列模型之灰度模型

灰度预测法是一种对含有不确定因素的系统的预测方法,灰色系统是位于白色系统和黑色系统之间的一种系统。

白色系统指的是一个系统内部的特征是完全已知的,使用者不仅知道系统的输入-输出关系,还知道实现输入-输出的具体方式,譬如函数表达式,微分方程的变化公式,或者物理学的基本定律。比方说牛顿第二定律F=ma, 使用者只需要知道物体的质量和加速度,就可以通过牛顿第二定律求出所使用的力F的具体值。或者说当物体的质量固定之后,已知不同的加速度,就可以求出不同的力的值,力和加速度之间有明确的关系表达式。这种系统就称为白色系统。

黑色系统和白色系统就有鲜明的对比,使用者完全不清楚黑色系统的内部特征,只是知道一些输入和相应的输出。使用者需要通过这一系列的输入和输出的对应关系来判断这个黑色系统的内部特征,对于没有办法打开的黑箱,使用者需要通过输入和输出来建立模型来了解这个黑箱。比方说:高校在进行招生的时候,其实对考生是几乎不了解的,这个时候就需要通过高考或者自主招生考试来判断学生的知识掌握程度和运用知识解决问题的能力。通过测试的结果来判断学生的综合能力的大小,进而判断是不是符合高校的专业需求。

灰度系统恰好位于白色系统和黑色系统之间,灰度系统内部一部分是已知的,但是另一部分却是未知的。比方说:经济学中的模型,通过数学或者统计学的方法,可以判断某些因素之间确实有关系,但是却不能够完全的判断整个系统的情况,对于很多未知的情况,这些模型就有一定的局限性。于是,灰度系统就需要通过判断系统各个因素之间的发展规律,进行相应的关联分析。通过原始的数列来生成规律性更强的数列,通过生成的数列来建立相应的微分方程模型,从而预测数列的未来发展趋势。灰度模型使用等时间距观测到的数据值来构造灰色预测模型,从而达到能够预测未来某一时刻的数值的目的。

尽管灰度系统的现象不够清楚,内部的结构并不为人所知,数据是杂乱的,但是却是互相关联的,有整体功效的,因而可以对它的变化过程进行预测。其中灰度模型是灰度系统理论的重要组成部分,将杂乱的原始数据整理成规律性较强的数据,然后再利用离散的灰度方程建立连续的微分模型,从而对系统的发展做出全面的观察分析,并做出长期预测。

下面来介绍下灰度模型的几种分类:

已知序列 x^{(0)}(1),...,x^{(0)}(n),其中n是数组的长度。通过分析这组序列的内在结构,从而建立相应的微分方程模型,达到预测未来序列x^{(0)}(n+1), x^{(0)}(n+2),...的目的。

(1) 灰度模型GM(1,1)

第一步需要通过累加形成新序列 x^{(1)}(1),..., x^{(1)}(n),其中

x^{(1)}(1)=x^{(0)}(1),

x^{(1)}(2)=x^{(0)}(1)+x^{(0)}(2), \cdot \cdot \cdot

x^{(1)}(n)=x^{(0)}(1)+\cdot\cdot\cdot+x^{(0)}(n).

对上面的累加序列取均值,就可以得到序列

z^{(1)}(1)=x^{(1)}(1),

z^{(1)}(2)=(x^{(1)}(1)+x^{(1)}(2))/2, \cdot \cdot \cdot

z^{(1)}(n)=(x^{(1)}(n-1)+x^{(1)}(n))/2.

那么GM(1,1)灰度模型相应的微分方程就是\frac{dx^{(1)}}{dt}+a x^{(1)}(t)=b, 它的离散方程是x^{(0)}(k)+az^{(1)}(k)=b, 这里2\leq k\leq n. 其中a成为发展系数,b称为控制系数。通过最小二乘法的推导,可以得到 (a,b)^{T}=(B^{T}B)^{-1}B^{T}Y. 其中B和Y都是矩阵,定义如下:

B=\begin{pmatrix} -z^{(1)}(2) & 1 \\ -z^{(1)}(3) & 1 \\ . & . \\ -z^{(1)}(n) & 1 \end{pmatrix},

Y= (x^{(0)}(2),\cdot \cdot \cdot, x^{(0)}(n))^{T}.

通过常微分方程的求解,可以得到预测模型的解析表达式。这里分两种情况:

第一种情况:a\neq 0

x^{(1)}_{p}(k)=(x^{(0)}(1)-\frac{b}{a})e^{-a(k-1)}+\frac{b}{a}, k \geq 1, 从而得到序列的预测值:

x^{(0)}_{p}(k)=x^{(1)}(k)-x^{(1)}(k-1)=(x^{(0)}(1)-\frac{b}{a})\cdot e^{-a(k-1)}\cdot (1-e^{a}), k\geq 2, x^{(0)}_{p}(1)=x^{(0)}(1).

第二种情况:a=0

x^{(1)}_{p}(k)=bk+x^{(0)}(1)-b, k\geq 1, 从而得到的序列的预测值:

x^{(0)}_{p}(k)=x^{(0)}(1).

(2) 灰度Verhulst模型

灰度Verhulst模型和前面的灰度模型GM(1,1)相似,也是计算累加生成数列,然后计算中值数列。不过Verhulst模型是建立非线性的常微分方程,所以相应的矩阵B和Y就需要做更改。微分方程是\frac{dx^{1}(t)}{dt}+ax^{(1)}(t)=b(x^{(1)}(t))^{2}. 离散方程则是x^{(0)}(k)+az^{(1)}(k)=b(z^{(1)}(k))^{2} \text{ for } 2\leq k\leq n. 通过最小二乘法的推导,可以得到 (a,b)^{T}=(B^{T}B)^{-1}B^{T}Y. 其中B和Y都是矩阵,定义如下:

B=\begin{pmatrix} -z^{(1)}(2) & (z^{(1)}(2))^{2} \\ -z^{(1)}(3) & (z^{(1)}(3))^{2} \\ . & . \\ -z^{(1)}(n) & (z^{(1)}(n))^{2} \end{pmatrix},

Y= (x^{(0)}(2),\cdot \cdot \cdot, x^{(0)}(n))^{T}.

通过微分方程的求解,可以得到

x_{p}^{(1)}(k)=\frac{ax^{(0)}(1)}{bx^{(0)}(1)+(a-bx^{(0)}(1))e^{a(k-1)}} \text{ for } k\geq 1.从而可以得到预测的序列x_{p}^{(0)}(k)=x_{p}^{(1)}(k)-x_{p}^{(1)}(k-1) \text{ for }k\geq 2, x_{p}^{(0)}(1)=x^{(0)}(1).

注:如果a<0, 那么\lim_{k\rightarrow \infty}x_{p}^{(1)}(k)=a/b, \lim_{k\rightarrow \infty}x_{p}^{(0)}(k)=0.

(3) 灰度模型DGM(2,1)

这个DGM(2,1)灰度模型并不需要累加生成序列,直接使用原序列就可以进行操作。微分方程是\frac{d^{2}x^{(1)}}{dt^{2}}+a\frac{dx^{(1)}}{dt}=b, 通过最小二乘法的推导,可以得到 (a,b)^{T}=(B^{T}B)^{-1}B^{T}Y. 其中B和Y都是矩阵,定义如下:

B=\begin{pmatrix} -x^{(0)}(2) & 1 \\ -x^{(0)}(3) & 1 \\ . & . \\ -x^{(0)}(n) & 1 \end{pmatrix},

Y= (x^{(0)}(2)-x^{(0)}(1),\cdot \cdot \cdot, x^{(0)}(n)-x^{(0)}(n-1))^{T}.

通过微分方程的求解,可以得到:x^{(1)}_{p}(k)=(\frac{b}{a^{2}}-\frac{x^{(0)}(1)}{a})e^{-a(k-1)}+\frac{b}{a}k+(x^{(0)}(1)-\frac{b}{a})\frac{1+a}{a} \text{ for } k\geq 1.

求解原序列的预测值就是:x^{(0)}_{p}(k)=(\frac{b}{a^{2}}-\frac{x^{(0)}(1)}{a})(1-e^{a})e^{-a(k-1)}+\frac{b}{a} \text{ for } k\geq 2, x^{(0)}_{p}(1)=x^{(0)}(1).

案例1:灰度模型在电力行业的应用。

某市1984~1990年用电量数据如下:

年份                  1984             1985           1986           1987            1988             1989            1990

用电量/GWh    2783.20  3028.26  3290.55  3477.77 3685.02      3935.09    4210.29

这里用n=4,也就是前四个点建立模型,然后后面三个点用来测试模型的正确性。注:这里使用了一些根据上面模型进行改进的灰度模型,进行了模型的融合。x^{(0)}(1)=2783.20, x^{(0)}(2)=3028.26, x^{(0)}(3)=3290.55, x^{(0)}(4)=3477.77. 通过计算累积生成序列,可以进一步求的系数a和b的值,从而根据上面的模型进行模拟。通过模型可以算出

x^{(0)}_{p}(1)=2783.20, x^{(0)}_{p}(2)=3042.19, x^{(0)}_{p}(3)=3258.00, x^{(0)}_{p}(4)=3489.12, x^{(0)}_{p}(5)=3736.64, x^{(0)}_{p}(6)=4001.72, x^{(0)}_{p}(7)=4285.60.

通过平均绝对百分比误差(Mean Absolute Percentage Error)的定义得到:前四项的MAPE<0.005, 所有七项的MAPE<0.01. 通过标准均方根误差(Normalized root mean square error)的定义可以求出前四项的NRMSE<0.027, 所有七项的NRMSE<0.031.

作图如下:

eletric energy

其中绿色线条表示实际的用电量数据,蓝色线条表示用灰度模型模拟的用电量数据。红线左侧表示用来测试的前四项,红线右侧表示预测的未来三年的走势。

案例2:某油藏原始数据

年份              1972             1973           1974           1975             1976             1977            1978              1979

综合含水     31.8              39.1            43.2             48.6              49.8                   53.3               58.6               61.7

这里用n=5,也就是前五个点建立模型,然后后面三个点用来测试模型的正确性。

x^{(0)}(1)=31.8, x^{(0)}(2)=39.1, x^{(0)}(3)=43.2, x^{(0)}(4)=48.6, x^{(0)}(5)=49.8. 通过计算累积生成序列,可以进一步求的系数a和b的值,从而根据上面的模型进行模拟。通过模型可以算出

x^{(0)}_{p}(1)=31.8, x^{(0)}_{p}(2)=39.72, x^{(0)}_{p}(3)=43.10, x^{(0)}_{p}(4)=46.78, x^{(0)}_{p}(5)=50.77, x^{(0)}_{p}(6)=55.10, x^{(0)}_{p}(7)=59.80, x^{(0)}_{p}(8)=64.89.

通过平均绝对百分比误差(Mean Absolute Percentage Error)的定义得到:前五项的MAPE<0.015, 所有八项的MAPE<0.023. 通过标准均方根误差(Normalized root mean square error)的定义可以求出前五项的NRMSE<0.054, 所有八项的NRMSE<0.052.

作图如下:

raw data of reservior

其中绿色线条表示实际的综合含水数据,蓝色线条表示用灰度模型模拟的综合含水数据。红线左侧表示用来测试的前五项,红线右侧表示预测的未来三年的走势。

参考文献:

  1. https://en.wikipedia.org/wiki/Root-mean-square_deviation
  2. https://en.wikipedia.org/wiki/Mean_absolute_percentage_error