Mann

关注
Mannwww.shan-machinery.com

之前在研究用机器学习库Sci-kit做计算指标(特征值)和金融产品趋势(分类)关系学习的时候,对于如何判断趋势,是直接使用当前之后5根k线close值做线性回归,如果拟合的P值可信的直线斜率向上则是上涨,斜率向下则是下跌。具体代码可以见之前我之前blog。

在vnpy有个网友讨论,为什么用这样方法判断趋势的时候;我做了些搜索,才发现判断一组时序队列的趋势并不是一个简单的事情,虽然人眼看很直接,但是数学分析并不简单,尤其考虑置信范围和变点存在。

所谓置信范围一两句说不清,可以看看这个问答,我的理解95%置信区间就是在这个一个时序队列里面95%都是符合上涨趋势,但是存在5%可能不符合:

突变点:变点理论是统计学中的一个经典分支,其基本定义是在一个序列或过程中,当某个统计特性(分布类型、分布参数)在某时间点受系统性因素而非偶然性因素影响发生变化,我们就称该时间点为变点。变点识别即利用统计量或统计方法将该变点位置估计出来。这里主要是用数学方法,过滤出偶尔出现的异常点,和真正改变规律的变点。

其实趋势检测和突变点检测是很数据挖掘很经典的话题,尤其在气象,水文,医药验证等,使用方法很多,从简单的均值,方差,到概率线性回归等。这里主要讲Mann-Kendall检验法。

其他的,网上搜到的好多:

1。均值突变的检测方法有:(1)低通滤波法;(2)t-检验法;(3)Cramer法;(4)滑动t-检验法;(5)Yamaoto法;(6)M-K法;(7)最小二乘法;(8)连续滑动t-检验法;(9)Pttitt法;(10)Lepage法;(11)局部比较法。等等。在均值突变分析中,除了突变点、突变个数的估计外,一般还要分析跃变度等。 2。方差突变的检测方法有:(1)F-检验法;(2)滑动F-检验法;(3)连续滑动F-检验法。等等。 3。线性回归突变(也称趋势突变)的检测方法有:(1)最小二乘法;(2)局部比较法;滑动参数法等等。 4。概率突变的检测方法有:(1)极大似然法;(2)累计次数法;(3)Bayes法;等等。

曼-肯德尔法又称Mann—Kenddall 检验法,是一种气候诊断与预测技术,应用Mann-Kendall检验法可以判断气候序列中是否存在气候突变,如果存在,可确定出突变发生的时间。Mann-Kendall检验法也经常用于气候变化影响下的降水、干旱频次趋势检测。Mann—Kenddall的检验方法是非参数方法。非参数检验方法亦称无分布检验,其优点是不需要样本遵从一定的分布,也不受少数异常值的干扰,更适用于类型变量和顺序变量,计算也比较简便。

具体算法推导不讲了,主要有两种用法,一个是计算这个时序队列的趋势;还有一个是算这个时序队列是否有变点,如果有,是队列中几个点。

代码之前自己写了一些,后来在这个链接发现一个非常全的,修改引用了。他还有其他几个突变点算法的python程序化,可以学习。

https://blog.csdn.net/u012111465/article/details/82016757

一,趋势校验,算法如下

def Mann_Kenddall_Trend_desc(inputdata):    # 计算总趋势秩次和    inputdata = np.array(inputdata)    n = inputdata.shape[0]    sum_sgn = 0    for i in np.arange(n):        if i  inputdata[i]:                    sum_sgn = sum_sgn+1                elif inputdata[j]  +—2.576    # 95% ——> +—1.96    # 90% ——> +—1.645    if np.abs(Z_value) > 1.96 and np.abs(Z_value)  0:            result_desc = u"95% up"        else:            result_desc = u"95% down"    elif np.abs(Z_value) > 2.576:        if Z_value > 0:            result_desc = u"99% up"        else:            result_desc = u"99% down"    else:        result_desc     = u"not trendency"    return result_desc

二,突变点校验

def Kendall_change_point_detection(inputdata):    inputdata = np.array(inputdata)    n=inputdata.shape[0]    # 正序列计算---------------------------------    # 定义累计量序列Sk,初始值=0    Sk             = [0]    # 定义统计量UFk,初始值 =0    UFk            = [0]    # 定义Sk序列元素s,初始值 =0    s              =  0    Exp_value      = [0]    Var_value      = [0]    # i从1开始,因为根据统计量UFk公式,i=0时,Sk(0)、E(0)、Var(0)均为0    # 此时UFk无意义,因此公式中,令UFk(0)=0    for i in range(1,n):        for j in range(i):            if inputdata[i] > inputdata[j]:                s = s+1            else:                s = s+0        Sk.append(s)        Exp_value.append((i+1)*(i+2)/4 )                     # Sk[i]的均值        Var_value.append((i+1)*i*(2*(i+1)+5)/72 )            # Sk[i]的方差        UFk.append((Sk[i]-Exp_value[i])/np.sqrt(Var_value[i]))    # ------------------------------正序列计算    # 逆序列计算---------------------------------    # 定义逆序累计量序列Sk2,长度与inputdata一致,初始值=0    Sk2             = [0]    # 定义逆序统计量UBk,长度与inputdata一致,初始值=0    UBk             = [0]    UBk2            = [0]    # s归0    s2              =  0    Exp_value2      = [0]    Var_value2      = [0]    # 按时间序列逆转样本y    inputdataT = list(reversed(inputdata))    # i从2开始,因为根据统计量UBk公式,i=1时,Sk2(1)、E(1)、Var(1)均为0    # 此时UBk无意义,因此公式中,令UBk(1)=0    for i in range(1,n):        for j in range(i):            if inputdataT[i] > inputdataT[j]:                s2 = s2+1            else:                s2 = s2+0        Sk2.append(s2)        Exp_value2.append((i+1)*(i+2)/4 )                     # Sk[i]的均值        Var_value2.append((i+1)*i*(2*(i+1)+5)/72 )            # Sk[i]的方差        UBk.append((Sk2[i]-Exp_value2[i])/np.sqrt(Var_value2[i]))        UBk2.append(-UBk[i])    # 由于对逆序序列的累计量Sk2的构建中,依然用的是累加法,即后者大于前者时s加1,    # 则s的大小表征了一种上升的趋势的大小,而序列逆序以后,应当表现出与原序列相反    # 的趋势表现,因此,用累加法统计Sk2序列,统计量公式(S(i)-E(i))/sqrt(Var(i))    #也不应改变,但统计量UBk应取相反数以表征正确的逆序序列的趋势    #  UBk(i)=0-(Sk2(i)-E)/sqrt(Var)    # ------------------------------逆序列计算    # 此时上一步的到UBk表现的是逆序列在逆序时间上的趋势统计量    # 与UFk做图寻找突变点时,2条曲线应具有同样的时间轴,因此    # 再按时间序列逆转结果统计量UBk,得到时间正序的UBkT,    UBkT = list(reversed(UBk2))    diff = np.array(UFk) - np.array(UBkT)    K    = list()    # 找出交叉点    for k in range(1,n):        if diff[k-1]*diff[k] 1.96 or UFk[k] https://www.shan-machinery.com