指标检测(三):趋势异常检测-基于Mann-Kendall检验

上士闻道,勤而行之;中士闻道,若存若亡;下士闻道,大笑之。

本文基于Github :ad4j:Anomaly Detection For Java
项目中有完整代码示例,欢迎Star、Issue、提交PR,获得Contributor勋章,共同成长。

系列文章导航:

一、概述

概述请参考系列文章(一)的概述
本文将重点介绍 ad4j:Anomaly Detection For Java 项目中的趋势异常检测算法。

Mann-Kendall (MK) 算法是一种非参数统计方法,常用于检测时间序列数据中的单调趋势,并判断其显著性。

二、趋势异常检测

1. 原理

Mann-Kendall 算法通过对时间序列数据的各个值之间的差异进行比较,统计正负变化的总和,用以判断数据的趋势是否显著。

(1)假设检验

  • 假设数据具有显著的上升或下降趋势。

(2)计算检验统计量 S S S
S = ∑ i = 1 n − 1 ∑ j = i + 1 n sgn ( x j − x i ) S = \sum_{i=1}^{n-1} \sum_{j=i+1}^{n} \text{sgn}(x_j - x_i) S=i=1n1j=i+1nsgn(xjxi)
其中符号函数:
sgn ( x j − x i ) = { + 1 , if  x j − x i > 0 0 , if  x j − x i = 0 − 1 , if  x j − x i < 0 \text{sgn}(x_j - x_i) = \begin{cases} +1, & \text{if } x_j - x_i > 0 \\ 0, & \text{if } x_j - x_i = 0 \\ -1, & \text{if } x_j - x_i < 0 \end{cases} sgn(xjxi)= +1,0,1,if xjxi>0if xjxi=0if xjxi<0

(3)统计量 S S S标准化
Z = { S − 1 Var ( S ) , if  S > 0 0 , if  S = 0 S + 1 Var ( S ) , if  S < 0 Z = \begin{cases} \frac{S - 1}{\sqrt{\text{Var}(S)}}, & \text{if } S > 0 \\ 0, & \text{if } S = 0 \\ \frac{S + 1}{\sqrt{\text{Var}(S)}}, & \text{if } S < 0 \end{cases} Z= Var(S) S1,0,Var(S) S+1,if S>0if S=0if S<0
其中 V a r ( S ) Var(S) Var(S)计算方式如下:
Var ( S ) = n ( n − 1 ) ( 2 n + 5 ) − ∑ t t ( t − 1 ) ( 2 t + 5 ) 18 \text{Var}(S) = \frac{n(n-1)(2n+5) - \sum_{t} t(t-1)(2t+5)}{18} Var(S)=18n(n1)(2n+5)tt(t1)(2t+5)
其中 − ∑ t t ( t − 1 ) ( 2 t + 5 ) -\sum_{t} t(t-1)(2t+5) tt(t1)(2t+5)是去除重复值

(4)趋势判定
通过标准正态分布计算 Z Z Z值对应的p-value:如果 p < α (通常 α = 0.05 ) p<α(通常α=0.05) p<α(通常α=0.05,则认为存在显著趋势。

2.计算过程

(1) 数据准备:时间序列数据: X = x 1 , x 2 , … , x n X={x_1,x_2,…,x_n} X=x1,x2,,xn
(2) 计算 S S S V a r ( S ) Var(S) Var(S)

  • 遍历每对数据 x i , x j ( j > i ) x_i,x_j(j>i) xi,xj(j>i),计算差异符号并求和得 S S S
  • 计算重复值的修正项,并求 V a r ( S ) Var(S) Var(S)

(3) 计算 Z Z Z值: 使用标准化公式计算 Z Z Z,并基于正态分布查找 p-value。
(4) 判断结果:

  • 如果 Z > 0 Z>0 Z>0:数据存在上升趋势。
  • 如果 Z < 0 Z<0 Z<0:数据存在下降趋势。
  • 如果 Z Z Z 的绝对值较小,则趋势不显著。

3.数据示例

(1) 数据准备:时间序列数据: X = 10.0 , 12.0 , 12.5 , 13.0 , 55.0 , 10.5 , 14.0 , 15.0 , 14.5 , 16.0 X={10.0,12.0,12.5,13.0,55.0,10.5,14.0,15.0,14.5,16.0} X=10.0,12.0,12.5,13.0,55.0,10.5,14.0,15.0,14.5,16.0
(2) 计算 S S S V a r ( S ) Var(S) Var(S)

  • 计算 S S S:逐步比较每对数据:
    x 2 ​ − x 1 ​ = 12.0 − 10.0 > 0 → + 1 x 3 ​ − x 1 ​ = 12.5 − 10.0 > 0 → + 1 x 4 ​ − x 1 ​ = 13.0 − 10.0 > 0 → + 1 . . . x_2​−x_1​=12.0−10.0>0 → +1 \\ x_3​−x_1​=12.5−10.0>0 → +1 \\ x_4​−x_1​=13.0−10.0>0 → +1 \\ ... x2x1=12.010.0>0+1x3x1=12.510.0>0+1x4x1=13.010.0>0+1...
    通过逐一比较计算所有正负符号,得到: S = + 28 S=+28 S=+28
  • 计算 V a r ( S ) Var(S) Var(S)
    V a r ( S ) = 10 ( 10 − 1 ) ( 2 ⋅ 10 + 5 ) 18 = 125 Var(S)= \frac{10(10−1)(2⋅10+5)}{18}=125 Var(S)=1810(101)(210+5)=125

**(3) 计算 Z Z Z值:
S − 1 Var ( S ) = 28 − 1 125 ≈ 2.41 \frac{S - 1}{\sqrt{\text{Var}(S)}}=\frac{28 - 1}{\sqrt{\text{125}}}≈2.41 Var(S) S1=125 2812.41
通过正态分布表, Z = 2.41 Z=2.41 Z=2.41 的 p-value 约为 0.016。

(4) 判断结果:

  • p = 0.016 < 0.05 p=0.016<0.05 p=0.016<0.05,数据存在显著上升趋势。

4.代码示例

代码来自Github开源项目 ad4j:Anomaly Detection For Java 欢迎star,欢迎提issue交流。

关键计算过程代码:完整代码可去项目中查看ADM_MannKendall.java

    private MannKendallResult mannKendall(List<IndicatorSeries> indicatorSeries, AnomalyDetectionLog log) {
        int n = indicatorSeries.size();

        // calculate S
        int S = 0;
        for (int i = 0; i < n - 1; i++) {
            for (int j = i + 1; j < n; j++) {
                S += Double.compare(indicatorSeries.get(j).getValue(), indicatorSeries.get(i).getValue());  // sign(X_j - X_i)
            }
        }

        // calculate Var(S)
        double varianceS = n * (n - 1) * (2 * n + 5) / 18.0;

        // calculate Z
        double Z;
        if (S > 0) {
            Z = (S - 1) / Math.sqrt(varianceS);
        } else if (S == 0) {
            Z = 0;
        } else {
            Z = (S + 1) / Math.sqrt(varianceS);
        }

        // judge trend with Z
        String trend;
        double alpha = criticalZ;
        if (Math.abs(Z) > alpha) {
            if (Z > 0) {
                trend = AnomalyDictType.TREND_UP.getCode();
            } else {
                trend = AnomalyDictType.TREND_DOWN.getCode();
            }
        } else {
            trend = AnomalyDictType.TREND_NO_OBVIOUS.getCode();
        }

        return new MannKendallResult(S, varianceS, Z, trend);
    }

测试示例

    @Test
    public void testADM_MannKendall(){
        double[] data = new double[]{10.0, 12.0, 12.5, 13.0, 55.0, 10.5, 14.0, 15.0, 14.5, 16.0};
        List<IndicatorSeries> indicatorSeries = IndicatorSeriesUtil.transferFromArray(data);
        AbstractADM model = new ADM_MannKendall();
        model.init(AnomalyDetectionContext.createDefault());
        model.checkCompatibility(indicatorSeries, log);

        IndicatorEvaluateInfo evaluate = model.evaluate(indicatorSeries, log);
        IndicatorSeriesUtil.print(evaluate);
    }

输出:如预期监测出上升趋势。

IndicatorEvaluateInfo{
anomalyDetectionModel='MODEL_ADM_ManKendall'
, anomalyType=TYPE_TREND
, hasAnomaly=true
, anomalyTrend=TREND_UP
}

三、总结

  • Mann-Kendall算法使用与所有分布的时序数据的趋势分析,对数据较为鲁棒,在气象、环境监测等趋势监测领域较为常用。也能够应用到我们应用系统的指标检测中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值