上士闻道,勤而行之;中士闻道,若存若亡;下士闻道,大笑之。
本文基于Github :ad4j:Anomaly Detection For Java
项目中有完整代码示例,欢迎Star、Issue、提交PR,获得Contributor勋章,共同成长。
系列文章导航:
- 指标检测(一):绝对值/离群值异常检测
- 指标检测(二):波动异常检测-基于二阶导和距离寻找最大弯曲点
- 指标检测(三):趋势异常检测-基于Mann-Kendall检验
- 指标检测(四):业务阈值检测-基于规则引擎的业务异常检测
一、概述
概述请参考系列文章(一)的概述
本文将重点介绍 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=1∑n−1j=i+1∑nsgn(xj−xi)
其中符号函数:
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(xj−xi)=⎩
⎨
⎧+1,0,−1,if xj−xi>0if xj−xi=0if xj−xi<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)S−1,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(n−1)(2n+5)−∑tt(t−1)(2t+5)
其中
−
∑
t
t
(
t
−
1
)
(
2
t
+
5
)
-\sum_{t} t(t-1)(2t+5)
−∑tt(t−1)(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 \\ ... x2−x1=12.0−10.0>0→+1x3−x1=12.5−10.0>0→+1x4−x1=13.0−10.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(10−1)(2⋅10+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)S−1=12528−1≈2.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算法使用与所有分布的时序数据的趋势分析,对数据较为鲁棒,在气象、环境监测等趋势监测领域较为常用。也能够应用到我们应用系统的指标检测中。