sortList, DescriptiveStatistics stats, ThresholdRule rule) {
83 | Double compareValue = null;
84 | if (rule.getThresholdType() == ThresholdType.CONSTANT) {
85 | compareValue = rule.getFactor();
86 | } else if (rule.getThresholdType() == ThresholdType.MIN) {
87 | compareValue = rule.getFactor() * stats.getMin();
88 | } else if (rule.getThresholdType() == ThresholdType.MAX) {
89 | compareValue = rule.getFactor() * stats.getMax();
90 | } else if (rule.getThresholdType() == ThresholdType.MEAN) {
91 | compareValue = rule.getFactor() * stats.getMean();
92 | } else if (rule.getThresholdType() == ThresholdType.STD) {
93 | compareValue = rule.getFactor() * stats.getStandardDeviation();
94 | } else if (rule.getThresholdType() == ThresholdType.VAR) {
95 | compareValue = rule.getFactor() * stats.getVariance();
96 | } else if (rule.getThresholdType() == ThresholdType.QUANTILE) {
97 | compareValue = IndicatorCalculateUtil.quantile(sortList, rule.getFactor());
98 | }
99 |
100 | return compareValue;
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/src/main/java/org/algorithmtools/ad4j/model/adm/ADM_ZScore.java:
--------------------------------------------------------------------------------
1 | package org.algorithmtools.ad4j.model.adm;
2 |
3 | import org.algorithmtools.ad4j.config.ADMConfigs;
4 | import org.algorithmtools.ad4j.enumtype.AnomalyDictType;
5 | import org.algorithmtools.ad4j.pojo.*;
6 | import org.algorithmtools.ad4j.utils.IndicatorCalculateUtil;
7 | import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;
8 |
9 | import java.util.List;
10 |
11 | /**
12 | * Anomaly detection model: Z-score
13 | * Suitable for normal distribution, few outliers, outliers may affect the mean and standard deviation and thus the detection rate,high computational efficiency
14 | * @author mym
15 | */
16 | public class ADM_ZScore extends AbstractADM {
17 | private double scoreThreshold = 2;
18 | public ADM_ZScore() {
19 | super(AnomalyDictType.MODEL_ADM_ZScore, AnomalyDictType.TYPE_OUTLIERS_VALUE);
20 | }
21 |
22 | @Override
23 | public void init(AnomalyDetectionContext context) {
24 | this.scoreThreshold = Double.parseDouble((String) context.getConfig(ADMConfigs.ADM_ZSCORE_THRESHOLD));
25 | }
26 |
27 | @Override
28 | public IndicatorEvaluateInfo evaluate(List indicatorSeries, AnomalyDetectionLog log) {
29 | DescriptiveStatistics stats = IndicatorCalculateUtil.initStatistic(null, indicatorSeries, null);
30 | // calculate mean and std
31 | double mean = stats.getMean();
32 | double stdDev = stats.getStandardDeviation();
33 | IndicatorEvaluateInfo result = buildDefaultEvaluateInfo();
34 | for (int i = 0; i < indicatorSeries.size(); i++) {
35 | double zScore = (indicatorSeries.get(i).getValue() - mean) / stdDev;
36 | if (Math.abs(zScore) > scoreThreshold) {
37 | IndicatorSeries anomalyIndicatorSeries = indicatorSeries.get(i);
38 | result.add(new AnomalyIndicatorSeries(anomalyIndicatorSeries.getValue() > mean ? AnomalyDictType.INFLUENCE_POSITIVE : AnomalyDictType.INFLUENCE_NEGATIVE, anomalyIndicatorSeries));
39 | result.setHasAnomaly(true);
40 | }
41 | }
42 |
43 | // TODO calculate range
44 | result.setNormalRangeMin(0d);
45 | result.setNormalRangeMax(0d);
46 | return result;
47 | }
48 |
49 | @Override
50 | public boolean checkCompatibility(List indicatorSeries, AnomalyDetectionLog log) {
51 | // TODO 正态分布检测
52 | return true;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/org/algorithmtools/ad4j/model/adm/AbstractADM.java:
--------------------------------------------------------------------------------
1 | package org.algorithmtools.ad4j.model.adm;
2 |
3 | import org.algorithmtools.ad4j.enumtype.AnomalyDictType;
4 | import org.algorithmtools.ad4j.pojo.AnomalyDetectionContext;
5 | import org.algorithmtools.ad4j.pojo.AnomalyDetectionLog;
6 | import org.algorithmtools.ad4j.pojo.IndicatorEvaluateInfo;
7 | import org.algorithmtools.ad4j.pojo.IndicatorSeries;
8 |
9 | import java.util.List;
10 |
11 | /**
12 | * Anomaly detection model abstract interface
13 | * @author mym
14 | */
15 | public abstract class AbstractADM {
16 |
17 | /**
18 | * model name
19 | */
20 | protected AnomalyDictType anomalyDetectionModel;
21 |
22 | /**
23 | * anomaly type
24 | */
25 | protected AnomalyDictType anomalyType;
26 |
27 | public AbstractADM(AnomalyDictType anomalyDetectionModel, AnomalyDictType anomalyType) {
28 | this.anomalyDetectionModel = anomalyDetectionModel;
29 | this.anomalyType = anomalyType;
30 | }
31 |
32 | /**
33 | * init model
34 | * @param context context env
35 | */
36 | public abstract void init(AnomalyDetectionContext context);
37 |
38 | /**
39 | * evaluate anomaly
40 | * @param indicatorSeries indicator series
41 | * @param log log
42 | */
43 | public abstract IndicatorEvaluateInfo evaluate(List indicatorSeries, AnomalyDetectionLog log);
44 |
45 | /**
46 | * check compatibility
47 | * @param indicatorSeries indicator series
48 | * @param log log
49 | * @return boolean true/false
50 | */
51 | public abstract boolean checkCompatibility(List indicatorSeries, AnomalyDetectionLog log);
52 |
53 | public IndicatorEvaluateInfo buildDefaultEvaluateInfo(){
54 | return new IndicatorEvaluateInfo(anomalyDetectionModel, anomalyType, false);
55 | }
56 |
57 | public AnomalyDictType getAnomalyDetectionModel() {
58 | return anomalyDetectionModel;
59 | }
60 |
61 | public AnomalyDictType getAnomalyType() {
62 | return anomalyType;
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/src/main/java/org/algorithmtools/ad4j/pojo/AnomalyDetectionContext.java:
--------------------------------------------------------------------------------
1 | package org.algorithmtools.ad4j.pojo;
2 |
3 | import com.alibaba.fastjson.JSONArray;
4 | import com.alibaba.fastjson.JSONObject;
5 | import org.algorithmtools.ad4j.config.ADMConfigs;
6 | import org.algorithmtools.ad4j.config.ConfigOption;
7 | import org.algorithmtools.ad4j.enumtype.AnomalyDictType;
8 | import org.algorithmtools.ad4j.enumtype.CompareType;
9 | import org.algorithmtools.ad4j.enumtype.LogicType;
10 | import org.algorithmtools.ad4j.enumtype.ThresholdType;
11 |
12 | import java.io.Serializable;
13 | import java.math.BigDecimal;
14 | import java.util.*;
15 |
16 | public class AnomalyDetectionContext implements Serializable {
17 |
18 | private List configAnomalyDetectionModelList = new ArrayList<>();
19 |
20 | public List getConfigAnomalyDetectionModelList() {
21 | return configAnomalyDetectionModelList;
22 | }
23 |
24 | public void addConfigAnomalyDetectionModel(AnomalyDictType anomalyDictType) {
25 | if (this.configAnomalyDetectionModelList == null){
26 | this.configAnomalyDetectionModelList = new ArrayList<>();
27 | }
28 |
29 | this.getConfigAnomalyDetectionModelList().add(anomalyDictType);
30 | }
31 |
32 | private Map admConfigMap = new HashMap<>();
33 |
34 | public void putConfig(String key, Object value){
35 | if (!ADMConfigs.configKeyMap.containsKey(key)) {
36 | throw new IllegalArgumentException("["+key+"] not exist!");
37 | }
38 | putConfig(ADMConfigs.configKeyMap.get(key), value);
39 | }
40 |
41 | public void putConfig(ConfigOption configOption, Object value){
42 | if(value != null && value.toString().endsWith("s")){
43 | // end with "s", then sensitivityConvert
44 | String sensitivityValue = value.toString();
45 | admConfigMap.put(configOption,sensitivityConvert(configOption, sensitivityValue.substring(0, sensitivityValue.length() - 1)));
46 | } else {
47 | admConfigMap.put(configOption,
48 | (value instanceof BigDecimal || value instanceof Double || value instanceof Float || value instanceof Integer) ? value.toString() : value);
49 | }
50 | }
51 |
52 | public Object getConfig(String key){
53 | if (ADMConfigs.configKeyMap.containsKey(key)) {
54 | return getConfig(ADMConfigs.configKeyMap.get(key));
55 | } else {
56 | return null;
57 | }
58 | }
59 |
60 | public Object getConfig(ConfigOption configOption){
61 | return Optional.ofNullable(admConfigMap.get(configOption)).orElseGet(configOption::getDefaultValue);
62 | }
63 |
64 | public static AnomalyDetectionContext createDefault(){
65 | AnomalyDetectionContext anomalyDetectionContext = new AnomalyDetectionContext();
66 | defaultConfig(anomalyDetectionContext);
67 | return anomalyDetectionContext;
68 | }
69 |
70 | public static AnomalyDetectionContext create(JSONObject propertiesJson){
71 | AnomalyDetectionContext anomalyDetectionContext = new AnomalyDetectionContext();
72 | defaultConfig(anomalyDetectionContext);
73 |
74 | if(Objects.nonNull(propertiesJson)){
75 | for (Map.Entry e : propertiesJson.entrySet()) {
76 | if (e.getKey().equalsIgnoreCase(ADMConfigs.ADM_THRESHOLD_RULE_SET.getKey())) {
77 | anomalyDetectionContext.putConfig(e.getKey(), generalThresholdRule((JSONObject) e.getValue()));
78 | } else {
79 | anomalyDetectionContext.putConfig(e.getKey(), e.getValue());
80 | }
81 | }
82 | }
83 |
84 | return anomalyDetectionContext;
85 | }
86 |
87 | /**
88 | * data case:{"logicType":"and","ruleGroup":[{"factor":100,"thresholdType":"std","compareType":">"}]}
89 | * @param thresholdRuleSetJson adm.threshold_rule.set config json
90 | * @return ThresholdRuleBase
91 | */
92 | public static ThresholdRuleBase generalThresholdRule(JSONObject thresholdRuleSetJson) {
93 | if (Objects.isNull(thresholdRuleSetJson)) {
94 | return null;
95 | }
96 |
97 | if (thresholdRuleSetJson.containsKey("ruleGroup")) {
98 | ThresholdRuleGroup ruleGroup = new ThresholdRuleGroup(LogicType.parse(thresholdRuleSetJson.getString("logicType")));
99 | JSONArray jsonArr = thresholdRuleSetJson.getJSONArray("ruleGroup");
100 | for (int i = 0; i < jsonArr.size(); i++) {
101 | ThresholdRuleBase rb = generalThresholdRule(jsonArr.getJSONObject(i));
102 | if(Objects.nonNull(rb)){
103 | ruleGroup.addRules(rb);
104 | }
105 | }
106 | return ruleGroup;
107 | } else {
108 | return new ThresholdRule(ThresholdType.parse(thresholdRuleSetJson.getString("thresholdType")), CompareType.parse(thresholdRuleSetJson.getString("compareType")), thresholdRuleSetJson.getDouble("factor"));
109 | }
110 | }
111 |
112 | private static void defaultConfig(AnomalyDetectionContext anomalyDetectionContext) {
113 | for (Map.Entry entry : ADMConfigs.configKeyMap.entrySet()) {
114 | anomalyDetectionContext.putConfig(entry.getValue(), entry.getValue().getDefaultValue());
115 | }
116 | }
117 |
118 | /**
119 | * Some anomaly model support the specification of configuration values in the form of sensitivities. If not support, return config default value.
120 | *
121 | * @param config
122 | * @param sensitivity 0.1-> low sensitivity; 0.05-> normal sensitivity; 0.01-> high sensitivity
123 | * @return config value
124 | */
125 | public static String sensitivityConvert(ConfigOption config, String sensitivity) {
126 | if (config == null) {
127 | return null;
128 | }
129 | if (config.getKey().equals(ADMConfigs.ADM_QUANTILE_IQR_MULTIPLIER.getKey())) {
130 | if ("0.1".equals(sensitivity)) {
131 | return "3.0"; // confidence level 90% (more bigger then detection more less)
132 | } else if ("0.05".equals(sensitivity)) {
133 | return "1.5"; // confidence level 95%
134 | } else if ("0.01".equals(sensitivity)) {
135 | return "1.0"; // confidence level 99%
136 | }
137 | }
138 | if (config.getKey().equals(ADMConfigs.ADM_ZSCORE_THRESHOLD.getKey())) {
139 | if ("0.1".equals(sensitivity)) {
140 | return "1.5"; // confidence level 86.6% (more bigger then detection more less)
141 | } else if ("0.05".equals(sensitivity)) {
142 | return "2.0"; // confidence level 95%
143 | } else if ("0.01".equals(sensitivity)) {
144 | return "3.0"; // confidence level 99.7%
145 | }
146 | }
147 | if (config.getKey().equals(ADMConfigs.ADM_GESD_ALPHA.getKey())) {
148 | if ("0.1".equals(sensitivity)) {
149 | return "0.1"; // confidence level 90% (more bigger then detection more less)
150 | } else if ("0.05".equals(sensitivity)) {
151 | return "0.05"; // confidence level 95%
152 | } else if ("0.01".equals(sensitivity)) {
153 | return "0.01"; // confidence level 99%
154 | }
155 | }
156 | if (config.getKey().equals(ADMConfigs.ADM_2ED_DERIVATION_MBP_THRESHOLD_FACTOR.getKey())) {
157 | // only support when ADM_2ED_DERIVATION_MBP_EVALUATE_TYPE = 1
158 | if ("0.1".equals(sensitivity)) {
159 | return "3.0"; // fitting 99.7%(more bigger then detection more less)
160 | } else if ("0.05".equals(sensitivity)) {
161 | return "2.0"; // fitting 95%
162 | } else if ("0.01".equals(sensitivity)) {
163 | return "1.5"; // fitting 86.6%
164 | }
165 | }
166 | if (config.getKey().equals(ADMConfigs.ADM_MANNKENDALL_CRITICALZ.getKey())) {
167 | if ("0.1".equals(sensitivity)) {
168 | return "1.64"; // confidence level 90% (more bigger then detection more less)
169 | } else if ("0.05".equals(sensitivity)) {
170 | return "1.96"; // confidence level 95%
171 | } else if ("0.01".equals(sensitivity)) {
172 | return "2.58"; // confidence level 99%
173 | }
174 | }
175 |
176 | return String.valueOf(config.getDefaultValue());
177 | }
178 |
179 |
180 | }
181 |
--------------------------------------------------------------------------------
/src/main/java/org/algorithmtools/ad4j/pojo/AnomalyDetectionLog.java:
--------------------------------------------------------------------------------
1 | package org.algorithmtools.ad4j.pojo;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 |
6 | import java.util.LinkedList;
7 |
8 | public class AnomalyDetectionLog {
9 |
10 | private static final Logger LOGGER = LoggerFactory.getLogger(AnomalyDetectionLog.class);
11 |
12 | private final LinkedList logList;
13 |
14 | public AnomalyDetectionLog() {
15 | this.logList = new LinkedList<>();
16 | }
17 |
18 | public void add(String level, String topic, String text){
19 | this.logList.add(new Log(level,"[" + topic + "]", text));
20 | }
21 |
22 | public void info(String msg){
23 | LOGGER.info(msg);
24 | }
25 |
26 | public void error(String msg){
27 | LOGGER.error(msg);
28 | }
29 |
30 | public void warn(String msg){
31 | LOGGER.warn(msg);
32 | }
33 |
34 | public void print(){
35 | for (Log log : logList) {
36 | if("debug".equals(log.level)){
37 | LOGGER.debug("{}: {}", log.topic, log.text);
38 | } else if("warn".equals(log.level)){
39 | LOGGER.warn("{}: {}", log.topic, log.text);
40 | } else if("error".equals(log.level)){
41 | LOGGER.error("{}: {}", log.topic, log.text);
42 | } else {
43 | LOGGER.info("{}: {}", log.topic, log.text);
44 | }
45 | }
46 | }
47 |
48 | public static class Log{
49 | private String level;
50 | private String topic;
51 | private String text;
52 |
53 | public Log(String level, String topic, String text) {
54 | this.level = level;
55 | this.topic = topic;
56 | this.text = text;
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/java/org/algorithmtools/ad4j/pojo/AnomalyDetectionProcessCollector.java:
--------------------------------------------------------------------------------
1 | package org.algorithmtools.ad4j.pojo;
2 |
3 | import org.algorithmtools.ad4j.model.adm.AbstractADM;
4 |
5 | import java.util.LinkedHashMap;
6 | import java.util.Map;
7 | import java.util.Objects;
8 |
9 | public class AnomalyDetectionProcessCollector {
10 | private AnomalyDetectionLog log;
11 | private AnomalyDetectionContext context;
12 | private LinkedHashMap processLinked;
13 |
14 | public AnomalyDetectionProcessCollector(AnomalyDetectionContext context) {
15 | this.log = new AnomalyDetectionLog();
16 | this.context = context;
17 | this.processLinked = new LinkedHashMap<>(8);
18 | }
19 |
20 | public void addProcess(AbstractADM anomalyDetection){
21 | this.processLinked.put(anomalyDetection, null);
22 | }
23 |
24 | public boolean hasAnomaly(){
25 | if(Objects.isNull(processLinked) || processLinked.isEmpty()){
26 | return false;
27 | }
28 | for (Map.Entry entry : processLinked.entrySet()) {
29 | if(entry.getValue() != null){
30 | return true;
31 | }
32 | }
33 | return false;
34 | }
35 |
36 | public AnomalyDetectionLog getLog() {
37 | return log;
38 | }
39 |
40 | public AnomalyDetectionContext getContext() {
41 | return context;
42 | }
43 |
44 | public LinkedHashMap getProcessLinked() {
45 | return processLinked;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/org/algorithmtools/ad4j/pojo/AnomalyDetectionResult.java:
--------------------------------------------------------------------------------
1 | package org.algorithmtools.ad4j.pojo;
2 |
3 | import org.algorithmtools.ad4j.enumtype.AnomalyDictType;
4 |
5 | import java.io.Serializable;
6 | import java.util.ArrayList;
7 | import java.util.Map;
8 | import java.util.concurrent.ConcurrentHashMap;
9 |
10 | /**
11 | * Anomaly detection result pojo
12 | */
13 | public class AnomalyDetectionResult implements Serializable {
14 |
15 | /**
16 | * indicator info
17 | */
18 | private IndicatorInfo indicatorInfo;
19 |
20 | /** indicator evaluate result. key=AnomalyType, value=List */
21 | public Map> indicatorEvaluateMap = new ConcurrentHashMap<>();
22 |
23 | public AnomalyDetectionResult(IndicatorInfo indicatorInfo) {
24 | this.indicatorInfo = indicatorInfo;
25 | }
26 |
27 | public AnomalyDetectionResult() {
28 | }
29 |
30 | public void addIndicatorEvaluateInfo(AnomalyDictType anomalyType, IndicatorEvaluateInfo evaluateInfo){
31 | if (evaluateInfo == null) {
32 | return;
33 | }
34 | this.indicatorEvaluateMap.compute(anomalyType, (k,v) -> v == null ? new ArrayList<>() : v).add(evaluateInfo);
35 | }
36 |
37 | public ArrayList getIndicatorEvaluateInfo(AnomalyDictType anomalyType){
38 | return this.indicatorEvaluateMap.get(anomalyType);
39 | }
40 |
41 | public Map> getIndicatorEvaluateMap() {
42 | return indicatorEvaluateMap;
43 | }
44 |
45 | public void setIndicatorEvaluateMap(Map> indicatorEvaluateMap) {
46 | this.indicatorEvaluateMap = indicatorEvaluateMap;
47 | }
48 |
49 | public IndicatorInfo getIndicatorInfo() {
50 | return indicatorInfo;
51 | }
52 |
53 | public void setIndicatorInfo(IndicatorInfo indicatorInfo) {
54 | this.indicatorInfo = indicatorInfo;
55 | }
56 |
57 | public boolean hasAnomaly(){
58 | return indicatorEvaluateMap != null && indicatorEvaluateMap.size() == 0;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/java/org/algorithmtools/ad4j/pojo/AnomalyIndicatorSeries.java:
--------------------------------------------------------------------------------
1 | package org.algorithmtools.ad4j.pojo;
2 |
3 | import org.algorithmtools.ad4j.enumtype.AnomalyDictType;
4 |
5 | public class AnomalyIndicatorSeries {
6 | /**
7 | * anomaly influence
8 | */
9 | private AnomalyDictType anomalyInfluence;
10 |
11 | /**
12 | * indicator series
13 | */
14 | private IndicatorSeries indicatorSeries;
15 |
16 | public AnomalyIndicatorSeries() {
17 | }
18 |
19 | public AnomalyIndicatorSeries(IndicatorSeries indicatorSeries) {
20 | this.indicatorSeries = indicatorSeries;
21 | }
22 |
23 | public AnomalyIndicatorSeries(AnomalyDictType anomalyInfluence, IndicatorSeries indicatorSeries) {
24 | this.anomalyInfluence = anomalyInfluence;
25 | this.indicatorSeries = indicatorSeries;
26 | }
27 |
28 | public AnomalyDictType getAnomalyInfluence() {
29 | return anomalyInfluence;
30 | }
31 |
32 | public void setAnomalyInfluence(AnomalyDictType anomalyInfluence) {
33 | this.anomalyInfluence = anomalyInfluence;
34 | }
35 |
36 | public IndicatorSeries getIndicatorSeries() {
37 | return indicatorSeries;
38 | }
39 |
40 | public void setIndicatorSeries(IndicatorSeries indicatorSeries) {
41 | this.indicatorSeries = indicatorSeries;
42 | }
43 |
44 | @Override
45 | public String toString() {
46 | return "["+anomalyInfluence + ":" + indicatorSeries.getTime()+", "+indicatorSeries.getValue()+", "+indicatorSeries.getLogicalIndex()+"]";
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/main/java/org/algorithmtools/ad4j/pojo/IndicatorEvaluateInfo.java:
--------------------------------------------------------------------------------
1 | package org.algorithmtools.ad4j.pojo;
2 |
3 | import org.algorithmtools.ad4j.enumtype.AnomalyDictType;
4 |
5 | import java.io.Serializable;
6 | import java.util.ArrayList;
7 | import java.util.List;
8 |
9 | public class IndicatorEvaluateInfo implements Serializable {
10 | private boolean hasAnomaly;
11 | /**
12 | * model name
13 | */
14 | private AnomalyDictType anomalyDetectionModel;
15 |
16 | /**
17 | * anomaly type
18 | */
19 | private AnomalyDictType anomalyType;
20 |
21 | /**
22 | * anomaly trend
23 | */
24 | private AnomalyDictType anomalyTrend;
25 | /**
26 | * anomaly indicator point
27 | */
28 | private List anomalySeriesList;
29 | /**
30 | * Normal range:min
31 | */
32 | private Double normalRangeMin;
33 | /**
34 | * Normal range:max
35 | */
36 | private Double normalRangeMax;
37 |
38 | public IndicatorEvaluateInfo(AnomalyDictType anomalyDetectionModel, AnomalyDictType anomalyType, boolean hasAnomaly) {
39 | if (hasAnomaly) {
40 | this.anomalySeriesList = new ArrayList<>();
41 | }
42 | this.anomalyType = anomalyType;
43 | this.anomalyDetectionModel = anomalyDetectionModel;
44 | this.hasAnomaly = hasAnomaly;
45 | }
46 |
47 | public void add(AnomalyIndicatorSeries series) {
48 | if(getAnomalySeriesList() == null){
49 | this.anomalySeriesList = new ArrayList<>();
50 | }
51 | getAnomalySeriesList().add(series);
52 | }
53 |
54 | public boolean isHasAnomaly() {
55 | return hasAnomaly;
56 | }
57 |
58 | public void setHasAnomaly(boolean hasAnomaly) {
59 | this.hasAnomaly = hasAnomaly;
60 | }
61 |
62 | public List getAnomalySeriesList() {
63 | return anomalySeriesList;
64 | }
65 |
66 | public void setAnomalySeriesList(List anomalySeriesList) {
67 | this.anomalySeriesList = anomalySeriesList;
68 | }
69 |
70 | public Double getNormalRangeMin() {
71 | return normalRangeMin;
72 | }
73 |
74 | public void setNormalRangeMin(Double normalRangeMin) {
75 | this.normalRangeMin = normalRangeMin;
76 | }
77 |
78 | public Double getNormalRangeMax() {
79 | return normalRangeMax;
80 | }
81 |
82 | public void setNormalRangeMax(Double normalRangeMax) {
83 | this.normalRangeMax = normalRangeMax;
84 | }
85 |
86 | public AnomalyDictType getAnomalyDetectionModel() {
87 | return anomalyDetectionModel;
88 | }
89 |
90 | public void setAnomalyDetectionModel(AnomalyDictType anomalyDetectionModel) {
91 | this.anomalyDetectionModel = anomalyDetectionModel;
92 | }
93 |
94 | public AnomalyDictType getAnomalyType() {
95 | return anomalyType;
96 | }
97 |
98 | public void setAnomalyType(AnomalyDictType anomalyType) {
99 | this.anomalyType = anomalyType;
100 | }
101 |
102 | public AnomalyDictType getAnomalyTrend() {
103 | return anomalyTrend;
104 | }
105 |
106 | public void setAnomalyTrend(AnomalyDictType anomalyTrend) {
107 | this.anomalyTrend = anomalyTrend;
108 | }
109 |
110 | @Override
111 | public String toString() {
112 | return "IndicatorEvaluateInfo{" +
113 | "anomalyDetectionModel='" + anomalyDetectionModel + '\'' +
114 | ", anomalyType=" + anomalyType +
115 | ", hasAnomaly=" + hasAnomaly +
116 | ", anomalySeriesList=" + anomalySeriesList +
117 | ", anomalyTrend=" + anomalyTrend +
118 | ", range=[" + normalRangeMin + ", " + normalRangeMax + "]" +
119 | '}';
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/src/main/java/org/algorithmtools/ad4j/pojo/IndicatorInfo.java:
--------------------------------------------------------------------------------
1 | package org.algorithmtools.ad4j.pojo;
2 |
3 | import java.io.Serializable;
4 | import java.util.List;
5 |
6 | public class IndicatorInfo implements Serializable {
7 |
8 | /**
9 | * indicator en
10 | */
11 | private String indicator;
12 | /**
13 | * indicator name
14 | */
15 | private String indicatorName;
16 | /**
17 | * indicator series
18 | */
19 | private List indicatorSeries;
20 |
21 | public IndicatorInfo() {
22 | }
23 |
24 | public IndicatorInfo(String indicator, String indicatorName, List indicatorSeries) {
25 | this.indicator = indicator;
26 | this.indicatorName = indicatorName;
27 | this.indicatorSeries = indicatorSeries;
28 | }
29 |
30 | public String getIndicator() {
31 | return indicator;
32 | }
33 |
34 | public void setIndicator(String indicator) {
35 | this.indicator = indicator;
36 | }
37 |
38 | public String getIndicatorName() {
39 | return indicatorName;
40 | }
41 |
42 | public void setIndicatorName(String indicatorName) {
43 | this.indicatorName = indicatorName;
44 | }
45 |
46 | public List getIndicatorSeries() {
47 | return indicatorSeries;
48 | }
49 |
50 | public void setIndicatorSeries(List indicatorSeries) {
51 | this.indicatorSeries = indicatorSeries;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/org/algorithmtools/ad4j/pojo/IndicatorSeries.java:
--------------------------------------------------------------------------------
1 | package org.algorithmtools.ad4j.pojo;
2 |
3 | import java.io.Serializable;
4 |
5 |
6 | public class IndicatorSeries implements Serializable {
7 | /** indicator time, you can set other number for Timing Significance Values */
8 | private final long time;
9 | /** indicator value */
10 | private final double value;
11 | /** business logical index,facilitate business tracking. for example: ID */
12 | private final String logicalIndex;
13 |
14 | public IndicatorSeries(long time, double value, String logicalIndex) {
15 | this.time = time;
16 | this.value = value;
17 | this.logicalIndex = logicalIndex;
18 | }
19 |
20 | public long getTime() {
21 | return time;
22 | }
23 |
24 | public double getValue() {
25 | return value;
26 | }
27 |
28 | public String getLogicalIndex() {
29 | return logicalIndex;
30 | }
31 |
32 | @Override
33 | public String toString() {
34 | return "["+time+", "+value+", "+logicalIndex+"]";
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/org/algorithmtools/ad4j/pojo/ThresholdRule.java:
--------------------------------------------------------------------------------
1 | package org.algorithmtools.ad4j.pojo;
2 |
3 | import org.algorithmtools.ad4j.enumtype.CompareType;
4 | import org.algorithmtools.ad4j.enumtype.ThresholdType;
5 |
6 | import java.util.List;
7 |
8 | /**
9 | * Threshold Rule: Indicator compareType (factor * thresholdType)
10 | */
11 | public class ThresholdRule implements ThresholdRuleBase{
12 |
13 | ThresholdType thresholdType;
14 |
15 | CompareType compareType;
16 |
17 | Double factor;
18 |
19 | public ThresholdRule(ThresholdType thresholdType, CompareType compareType, Double factor) {
20 | this.thresholdType = thresholdType;
21 | this.compareType = compareType;
22 | this.factor = factor;
23 | }
24 |
25 | public ThresholdType getThresholdType() {
26 | return thresholdType;
27 | }
28 |
29 | public CompareType getCompareType() {
30 | return compareType;
31 | }
32 |
33 | public Double getFactor() {
34 | return factor;
35 | }
36 |
37 | @Override
38 | public List getThresholdRules() {
39 | return null;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/org/algorithmtools/ad4j/pojo/ThresholdRuleBase.java:
--------------------------------------------------------------------------------
1 | package org.algorithmtools.ad4j.pojo;
2 |
3 | import java.io.Serializable;
4 | import java.util.List;
5 |
6 | public interface ThresholdRuleBase extends Serializable {
7 |
8 | List getThresholdRules();
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/org/algorithmtools/ad4j/pojo/ThresholdRuleGroup.java:
--------------------------------------------------------------------------------
1 | package org.algorithmtools.ad4j.pojo;
2 |
3 | import org.algorithmtools.ad4j.enumtype.LogicType;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | /**
9 | * Threshold Rule Group
10 | */
11 | public class ThresholdRuleGroup implements ThresholdRuleBase {
12 |
13 | List thresholdRules;
14 |
15 | LogicType logicType;
16 |
17 | public ThresholdRuleGroup(List thresholdRules, LogicType logicType) {
18 | this.thresholdRules = thresholdRules;
19 | this.logicType = logicType;
20 | }
21 |
22 | public ThresholdRuleGroup(LogicType logicType) {
23 | this.logicType = logicType;
24 | }
25 |
26 | public void addRules(ThresholdRuleBase rule) {
27 | if (this.thresholdRules == null) {
28 | thresholdRules = new ArrayList<>();
29 | }
30 | thresholdRules.add(rule);
31 | }
32 |
33 | @Override
34 | public List getThresholdRules() {
35 | return thresholdRules;
36 | }
37 |
38 | public LogicType getLogicType() {
39 | return logicType;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/org/algorithmtools/ad4j/utils/BandwidthUtil.java:
--------------------------------------------------------------------------------
1 | package org.algorithmtools.ad4j.utils;
2 |
3 | import java.util.Arrays;
4 |
5 | public class BandwidthUtil {
6 |
7 | /**
8 | * calculate bandwidth with Silverman's Rule of Thumb
9 | * @param data data
10 | * @return bandwidth h
11 | */
12 | public static double calculateBandwidth(double[] data) {
13 | int n = data.length;
14 |
15 | // calculate std
16 | double mean = Arrays.stream(data).average().orElse(0.0);
17 | double variance = Arrays.stream(data)
18 | .map(x -> (x - mean) * (x - mean))
19 | .sum() / n;
20 | double stdDev = Math.sqrt(variance);
21 |
22 | // calculate IQR
23 | Arrays.sort(data);
24 | double q1 = getPercentile(data, 25); // 25%
25 | double q3 = getPercentile(data, 75); // 75%
26 | double iqr = q3 - q1; // IQR = Q3 - Q1
27 |
28 | // calculate h
29 | double factor = Math.min(stdDev, iqr / 1.34);
30 | return 0.9 * factor * Math.pow(n, -1.0 / 5.0);
31 | }
32 |
33 | /**
34 | * Calculate array percentile value
35 | * @param data after sorted array
36 | * @param percentile percentile(0-100)
37 | * @return percentile value
38 | */
39 | private static double getPercentile(double[] data, double percentile) {
40 | double index = (percentile / 100.0) * (data.length - 1);
41 | int lower = (int) Math.floor(index);
42 | int upper = (int) Math.ceil(index);
43 | if (lower == upper) {
44 | return data[lower];
45 | }
46 | double weight = index - lower;
47 | return data[lower] * (1 - weight) + data[upper] * weight;
48 | }
49 |
50 | public static void main(String[] args) {
51 | // double[] data = {1.2, 1.5, 2.0, 2.1, 3.3, 3.9, 4.8, 10.0, 10.5, 12.0};
52 | // double[] data = {10.0, 12.0, 85.0, 70.0, 100.0, 14.0, 14.0, 12.0, 40.0, 20.0};
53 | // double[] data = {100, 102, 101, 180, 105, 108, 182, 110, 110,182,121};
54 | double[] data = {45.29, 30.85, 40.23, 15.57, 13.14, 32.53, 44.34, 33.92, 25.31, 31.12, 33.23, 40.65, 32.88, 31.14};
55 | double bandwidth = calculateBandwidth(data);
56 | System.out.printf("Optimal Bandwidth: %.4f%n", bandwidth);
57 | }
58 |
59 | }
60 |
61 |
--------------------------------------------------------------------------------
/src/main/java/org/algorithmtools/ad4j/utils/CollectionUtil.java:
--------------------------------------------------------------------------------
1 | package org.algorithmtools.ad4j.utils;
2 |
3 | import java.util.List;
4 |
5 | public class CollectionUtil {
6 |
7 | public static boolean isEmpty(List list){
8 | return list == null || list.isEmpty();
9 | }
10 |
11 | public static boolean isNotEmpty(List list){
12 | return !isEmpty(list);
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/org/algorithmtools/ad4j/utils/IndicatorCalculateUtil.java:
--------------------------------------------------------------------------------
1 | package org.algorithmtools.ad4j.utils;
2 |
3 | import org.algorithmtools.ad4j.pojo.IndicatorSeries;
4 | import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;
5 |
6 | import java.util.List;
7 | import java.util.Objects;
8 | import java.util.stream.Collectors;
9 |
10 | public class IndicatorCalculateUtil {
11 |
12 | /**
13 | * calculate define range
14 | * @param data indicator series
15 | * @param iqrMultiplier iqrMultiplier
16 | * @param lowerQuantile lowerQuantile
17 | * @param upperQuantile upperQuantile
18 | * @return [lowerBound, upperBound]
19 | */
20 | public static double[] quantileBound(List data, double iqrMultiplier, double lowerQuantile, double upperQuantile){
21 | // sort
22 | List sortList = sortAsc(data);
23 |
24 | // calculate quantile value
25 | double Q1 = quantile(sortList, lowerQuantile);
26 | double Q3 = quantile(sortList, upperQuantile);
27 |
28 | // calculate IQR
29 | double IQR = Q3 - Q1;
30 |
31 | // calculate bound
32 | double lowerBound = Q1 - iqrMultiplier * IQR;
33 | double upperBound = Q3 + iqrMultiplier * IQR;
34 | return new double[]{lowerBound, upperBound};
35 | }
36 |
37 | /**
38 | * interquartile range
39 | * @param data indicator series
40 | * @return [lowerBound, upperBound]
41 | */
42 | public static double[] quantileIQR(List data){
43 | return quantileBound(data, 1.5, 0.25, 0.75);
44 | }
45 |
46 | public static List excludeOutlier(List data){
47 | double[] bound = quantileIQR(data);
48 | double lowerBound = bound[0];
49 | double upperBound = bound[1];
50 |
51 | // find normal series
52 | return data.stream().filter(v -> v.getValue() <= upperBound && v.getValue() >= lowerBound).collect(Collectors.toList());
53 | }
54 |
55 | public static double quantile(List sortedData, double quantile) {
56 | int n = sortedData.size();
57 | double index = quantile * (n - 1);
58 | int lowerIndex = (int) Math.floor(index);
59 | int upperIndex = (int) Math.ceil(index);
60 |
61 | if (lowerIndex == upperIndex) {
62 | return sortedData.get(lowerIndex).getValue();
63 | }
64 |
65 | double weight = index - lowerIndex;
66 | return (1 - weight) * sortedData.get(lowerIndex).getValue() + weight * sortedData.get(upperIndex).getValue();
67 | }
68 |
69 | public static DescriptiveStatistics initStatistic(DescriptiveStatistics stats, List dataList, List excludeIndexList) {
70 | if (Objects.isNull(stats)) {
71 | stats = new DescriptiveStatistics();
72 | } else {
73 | stats.clear();
74 | }
75 |
76 | for (int i = 0; i < dataList.size(); i++) {
77 | if (excludeIndexList == null || !excludeIndexList.contains(i)) {
78 | stats.addValue(dataList.get(i).getValue());
79 | }
80 | }
81 | return stats;
82 | }
83 |
84 | public static List sortAsc(List data){
85 | return data.stream().sorted(new IndicatorSeriesComparator()).collect(Collectors.toList());
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/main/java/org/algorithmtools/ad4j/utils/IndicatorSeriesComparator.java:
--------------------------------------------------------------------------------
1 | package org.algorithmtools.ad4j.utils;
2 |
3 | import org.algorithmtools.ad4j.pojo.IndicatorSeries;
4 |
5 | import java.util.Comparator;
6 |
7 | public class IndicatorSeriesComparator implements Comparator {
8 | @Override
9 | public int compare(IndicatorSeries o1, IndicatorSeries o2) {
10 | if (o1.getValue() > o2.getValue()) {
11 | return 1;
12 | } else if (o1.getValue() < o2.getValue()) {
13 | return -1;
14 | } else {
15 | return Long.compare(o1.getTime(), o2.getTime());
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/org/algorithmtools/ad4j/utils/IndicatorSeriesUtil.java:
--------------------------------------------------------------------------------
1 | package org.algorithmtools.ad4j.utils;
2 |
3 | import com.alibaba.fastjson.JSONObject;
4 | import org.algorithmtools.ad4j.enumtype.AnomalyDictType;
5 | import org.algorithmtools.ad4j.pojo.AnomalyDetectionResult;
6 | import org.algorithmtools.ad4j.pojo.IndicatorEvaluateInfo;
7 | import org.algorithmtools.ad4j.pojo.IndicatorSeries;
8 |
9 | import java.util.ArrayList;
10 | import java.util.List;
11 | import java.util.Map;
12 |
13 | public class IndicatorSeriesUtil {
14 |
15 | public static List transferFromArray(double[] array){
16 | List list = new ArrayList();
17 | for (int i = 0; i < array.length; i++) {
18 | list.add(i, new IndicatorSeries(i, array[i], String.valueOf(i)));
19 | }
20 | return list;
21 | }
22 |
23 | public static double[] transferToArray(List series){
24 | double[] resultArray = new double[series.size()];
25 | for (int i = 0; i < series.size(); i++) {
26 | resultArray[i] = series.get(i).getValue();
27 | }
28 | return resultArray;
29 | }
30 |
31 | public static void print(IndicatorEvaluateInfo evaluateInfo){
32 | System.out.println(evaluateInfo);
33 | }
34 |
35 | public static void print(AnomalyDetectionResult result){
36 | System.out.println("==============Anomaly Detection Result=============");
37 | StringBuilder printString = new StringBuilder();
38 | printString.append("1.Anomaly detection original data:").append(result.getIndicatorInfo().getIndicatorSeries());
39 | printString.append("\n");
40 | printString.append("2.Overview: has ").append(result.getIndicatorEvaluateMap().size()).append(" types anomaly detected.");
41 | int i = 1;
42 | for (Map.Entry> entry : result.getIndicatorEvaluateMap().entrySet()) {
43 | printString.append("\n");
44 | printString.append("3.").append(i).append(".[Anomaly: ").append(entry.getKey().getEnName()).append("] ").append(JSONObject.toJSONString(entry.getValue()));
45 | i++;
46 | }
47 |
48 | System.out.println(printString);
49 |
50 | System.out.println("==============Anomaly Detection Result End=========");
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/src/test/java/org/algorithmtools/ad4j/model/ADEngineTest.java:
--------------------------------------------------------------------------------
1 | package org.algorithmtools.ad4j.model;
2 |
3 | import com.alibaba.fastjson.JSONObject;
4 | import org.algorithmtools.ad4j.engine.AnomalyDetectionEngine;
5 | import org.algorithmtools.ad4j.pojo.*;
6 | import org.algorithmtools.ad4j.utils.IndicatorSeriesUtil;
7 | import org.algorithmtools.chart.JFreeChartUtil;
8 | import org.junit.After;
9 | import org.junit.Before;
10 | import org.junit.Test;
11 |
12 | import java.util.List;
13 | import java.util.Scanner;
14 |
15 | public class ADEngineTest {
16 |
17 | public AnomalyDetectionLog log;
18 |
19 | @Before
20 | public void before(){
21 | log = new AnomalyDetectionLog();
22 | }
23 |
24 | @Test
25 | public void testEngin(){
26 | double[] data = new double[]{10.0, 12.0, 12.5, 133.0, 13.0, 10.5, 100.0, 14.0, 15.0, 14.5, 15.5};
27 | List indicatorSeries = IndicatorSeriesUtil.transferFromArray(data);
28 | JFreeChartUtil.drawLineChart("TestAD_Engin", indicatorSeries);
29 |
30 | IndicatorInfo info = new IndicatorInfo("Test", "Test-name", indicatorSeries);
31 | AnomalyDetectionEngine engine = new AnomalyDetectionEngine();
32 | AnomalyDetectionResult detect = engine.detect(info);
33 | IndicatorSeriesUtil.print(detect);
34 | }
35 |
36 | @Test
37 | public void testEnginByConfig(){
38 | double[] data = new double[]{11.5, 12, 10, 36, 13.0, 10.5, 5, 14.0, 15.0, 14.5, 15.5};
39 | List indicatorSeries = IndicatorSeriesUtil.transferFromArray(data);
40 | JFreeChartUtil.drawLineChart("TestAD_Engin", indicatorSeries);
41 |
42 | IndicatorInfo info = new IndicatorInfo("Test", "Test-name", indicatorSeries);
43 |
44 | JSONObject propertiesJson = new JSONObject();
45 | propertiesJson.put("adm.zscore.use", false);
46 | propertiesJson.put("adm.gesd.use", false);
47 | propertiesJson.put("adm.gesd.alpha", 0.1);
48 | propertiesJson.put("adm.quantile.use", false);
49 | propertiesJson.put("adm.quantile.iqr.multiplier", 2);
50 | propertiesJson.put("adm.2nd_derivation_mbp.use", true);
51 | propertiesJson.put("adm.2nd_derivation_mbp.threshold_factor", "0.1s");
52 | propertiesJson.put("adm.mannkendall.use", false);
53 | propertiesJson.put("adm.mannkendall.criticalz", "0.01s");
54 | propertiesJson.put("adm.threshold_rule.use", false);
55 |
56 | AnomalyDetectionContext anomalyDetectionContext = AnomalyDetectionContext.create(propertiesJson);
57 | AnomalyDetectionEngine engine = new AnomalyDetectionEngine(anomalyDetectionContext);
58 |
59 | AnomalyDetectionResult detect = engine.detect(info);
60 | IndicatorSeriesUtil.print(detect);
61 | }
62 |
63 | @After
64 | public void afterSleep() throws InterruptedException {
65 | System.out.println("click Enter to close window...");
66 | Scanner scanner = new Scanner(System.in);
67 | scanner.nextLine();
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/src/test/java/org/algorithmtools/ad4j/model/ADMTest.java:
--------------------------------------------------------------------------------
1 | package org.algorithmtools.ad4j.model;
2 |
3 | import com.alibaba.fastjson.JSONArray;
4 | import com.alibaba.fastjson.JSONObject;
5 | import org.algorithmtools.ad4j.config.ADMConfigs;
6 | import org.algorithmtools.ad4j.enumtype.CompareType;
7 | import org.algorithmtools.ad4j.enumtype.LogicType;
8 | import org.algorithmtools.ad4j.enumtype.ThresholdType;
9 | import org.algorithmtools.ad4j.model.adm.*;
10 | import org.algorithmtools.ad4j.pojo.*;
11 | import org.algorithmtools.ad4j.utils.IndicatorSeriesUtil;
12 | import org.algorithmtools.chart.JFreeChartUtil;
13 | import org.junit.After;
14 | import org.junit.Before;
15 | import org.junit.Test;
16 |
17 | import java.util.ArrayList;
18 | import java.util.List;
19 | import java.util.Scanner;
20 |
21 | public class ADMTest {
22 |
23 | public AnomalyDetectionLog log;
24 |
25 | @Before
26 | public void before(){
27 | log = new AnomalyDetectionLog();
28 | }
29 |
30 | @Test
31 | public void testADM_2ndDerivationMBP(){
32 | double[] data = {10.0, 12.0, 85.0, 70, 100.0, 14.0, 14.0, 12.0, 40, 20, 20};
33 | // List indicatorSeries = IndicatorSeriesUtil.transferFromArray(data);
34 | List indicatorSeries = parseData("[{\"logicalIndex\":\"0\",\"time\":0,\"value\":1104.0},{\"logicalIndex\":\"1\",\"time\":1,\"value\":976.0},{\"logicalIndex\":\"2\",\"time\":2,\"value\":949.0},{\"logicalIndex\":\"3\",\"time\":3,\"value\":895.0},{\"logicalIndex\":\"4\",\"time\":4,\"value\":810.0},{\"logicalIndex\":\"5\",\"time\":5,\"value\":975.0},{\"logicalIndex\":\"6\",\"time\":6,\"value\":1152.0},{\"logicalIndex\":\"7\",\"time\":7,\"value\":818.0},{\"logicalIndex\":\"8\",\"time\":8,\"value\":766.0},{\"logicalIndex\":\"9\",\"time\":9,\"value\":502.0},{\"logicalIndex\":\"10\",\"time\":10,\"value\":396.0},{\"logicalIndex\":\"11\",\"time\":11,\"value\":468.0},{\"logicalIndex\":\"12\",\"time\":12,\"value\":592.0},{\"logicalIndex\":\"13\",\"time\":13,\"value\":769.0}]");
35 | JFreeChartUtil.drawLineChart("testADM_2ndDerivationMBP", indicatorSeries);
36 |
37 | AbstractADM model = new ADM_2ndDerivationMBP();
38 | model.init(AnomalyDetectionContext.createDefault());
39 | model.checkCompatibility(indicatorSeries, log);
40 |
41 | IndicatorEvaluateInfo evaluate = model.evaluate(indicatorSeries, log);
42 | IndicatorSeriesUtil.print(evaluate);
43 | }
44 |
45 | @Test
46 | public void testADM_GESD(){
47 | double[] data = new double[]{10.0,12.0,12.0,13.0,12.0,11.0,50.0};
48 | List indicatorSeries = IndicatorSeriesUtil.transferFromArray(data);
49 | AbstractADM model = new ADM_GESD();
50 | model.init(AnomalyDetectionContext.createDefault());
51 | model.checkCompatibility(indicatorSeries, log);
52 |
53 | IndicatorEvaluateInfo evaluate = model.evaluate(indicatorSeries, log);
54 | IndicatorSeriesUtil.print(evaluate);
55 | }
56 |
57 | @Test
58 | public void testADM_MannKendall(){
59 | double[] data = new double[]{10.0, 12.0, 12.5, 13.0, 55.0, 10.5, 14.0, 15.0, 14.5, 16.0};
60 | List indicatorSeries = IndicatorSeriesUtil.transferFromArray(data);
61 | AbstractADM model = new ADM_MannKendall();
62 | model.init(AnomalyDetectionContext.createDefault());
63 | model.checkCompatibility(indicatorSeries, log);
64 |
65 | IndicatorEvaluateInfo evaluate = model.evaluate(indicatorSeries, log);
66 | IndicatorSeriesUtil.print(evaluate);
67 | }
68 |
69 | @Test
70 | public void testADM_Quantile(){
71 | double[] data = new double[]{10.0, 12.0, 12.5, 133.0, 13.0, 10.5, 100.0, 14.0, 15.0, 14.5, 15.5};
72 | List indicatorSeries = IndicatorSeriesUtil.transferFromArray(data);
73 | JFreeChartUtil.drawLineChart("TestADM_Quantile_Line", indicatorSeries);
74 | JFreeChartUtil.drawScatterChart("TestADM_Quantile_Scatter", indicatorSeries);
75 |
76 | AbstractADM model = new ADM_Quantile();
77 | model.init(AnomalyDetectionContext.createDefault());
78 | model.checkCompatibility(indicatorSeries, log);
79 |
80 | IndicatorEvaluateInfo evaluate = model.evaluate(indicatorSeries, log);
81 | IndicatorSeriesUtil.print(evaluate);
82 | }
83 |
84 | @Test
85 | public void testADM_ZScore(){
86 | double[] data = new double[]{10.0, 12.0, 12.5, 133.0, 13.0, 10.5, 100.0, 14.0, 15.0, 14.5, 15.5};
87 | data = new double[]{
88 | 1.26
89 | ,1.10
90 | ,1.54
91 | ,2.58
92 | ,3.48
93 | ,1.64
94 | ,1.74
95 | ,1.36
96 | ,2.53
97 | ,2.47
98 | ,1.56
99 | ,0.91
100 | ,2.00
101 | };
102 | List indicatorSeries = IndicatorSeriesUtil.transferFromArray(data);
103 | AbstractADM model = new ADM_ZScore();
104 | model.init(AnomalyDetectionContext.createDefault());
105 | model.checkCompatibility(indicatorSeries, log);
106 |
107 | IndicatorEvaluateInfo evaluate = model.evaluate(indicatorSeries, log);
108 | IndicatorSeriesUtil.print(evaluate);
109 | }
110 |
111 | @Test
112 | public void testADM_ThresholdRule(){
113 | double[] data = new double[]{1.0,2.0,3.0,4.0};
114 | List indicatorSeries = IndicatorSeriesUtil.transferFromArray(data);
115 | AbstractADM model = new ADM_ThresholdRule();
116 | AnomalyDetectionContext adContext = AnomalyDetectionContext.createDefault();
117 | adContext.putConfig(ADMConfigs.ADM_THRESHOLD_RULE_USE, true);
118 |
119 | ThresholdRuleGroup ruleGroupL2 = new ThresholdRuleGroup(LogicType.AND);
120 | ThresholdRuleBase ruleBaseL2_1 = new ThresholdRule(ThresholdType.MIN, CompareType.GREATER, 1.0);
121 | ThresholdRuleBase ruleBaseL2_2 = new ThresholdRule(ThresholdType.MAX, CompareType.LESS, 1.0);
122 | ruleGroupL2.addRules(ruleBaseL2_1);
123 | ruleGroupL2.addRules(ruleBaseL2_2);
124 | ThresholdRuleBase ruleBaseL1_1 = new ThresholdRule(ThresholdType.CONSTANT, CompareType.GREATER_OR_EQ, 5.0);
125 | ThresholdRuleBase ruleBaseL1_2 = new ThresholdRule(ThresholdType.CONSTANT, CompareType.LESS_OR_EQ, 1.0);
126 | ThresholdRuleGroup ruleGroupL1 = new ThresholdRuleGroup(LogicType.OR);
127 | ruleGroupL1.addRules(ruleBaseL1_1);
128 | ruleGroupL1.addRules(ruleBaseL1_2);
129 | ruleGroupL1.addRules(ruleGroupL2);
130 | adContext.putConfig(ADMConfigs.ADM_THRESHOLD_RULE_SET, ruleGroupL1);
131 | model.init(adContext);
132 | model.checkCompatibility(indicatorSeries, log);
133 |
134 | IndicatorEvaluateInfo evaluate = model.evaluate(indicatorSeries, log);
135 | IndicatorSeriesUtil.print(evaluate);
136 | }
137 |
138 | @Test
139 | public void testADM_ThresholdRule2(){
140 | double[] data = new double[]{1.0,2.0,3.0,4.0};
141 | List indicatorSeries = IndicatorSeriesUtil.transferFromArray(data);
142 | AbstractADM model = new ADM_ThresholdRule();
143 |
144 | String propertiesJsonStr = "{\"adm.quantile.use\":false,\"adm.threshold_rule.use\":true,\"adm.threshold_rule.set\":{\"logicType\":\"or\",\"ruleGroup\":[{\"factor\":5.0,\"thresholdType\":\"constant\",\"compareType\":\">=\"},{\"factor\":1.0,\"thresholdType\":\"constant\",\"compareType\":\"<=\"}]}}";
145 | JSONObject propertiesJson = JSONObject.parseObject(propertiesJsonStr);
146 | AnomalyDetectionContext adContext = AnomalyDetectionContext.create(propertiesJson);
147 |
148 | model.init(adContext);
149 | model.checkCompatibility(indicatorSeries, log);
150 |
151 | IndicatorEvaluateInfo evaluate = model.evaluate(indicatorSeries, log);
152 | IndicatorSeriesUtil.print(evaluate);
153 | }
154 |
155 | public List parseData(String s){
156 | s = s != null ? s :
157 | "[{\"logicalIndex\":\"0\",\"time\":0,\"value\":45.29},{\"logicalIndex\":\"1\",\"time\":1,\"value\":30.85},{\"logicalIndex\":\"2\",\"time\":2,\"value\":40.23},{\"logicalIndex\":\"3\",\"time\":3,\"value\":15.57},{\"logicalIndex\":\"4\",\"time\":4,\"value\":13.14},{\"logicalIndex\":\"5\",\"time\":5,\"value\":32.53},{\"logicalIndex\":\"6\",\"time\":6,\"value\":44.34},{\"logicalIndex\":\"7\",\"time\":7,\"value\":33.92},{\"logicalIndex\":\"8\",\"time\":8,\"value\":25.31},{\"logicalIndex\":\"9\",\"time\":9,\"value\":31.12},{\"logicalIndex\":\"10\",\"time\":10,\"value\":33.23},{\"logicalIndex\":\"11\",\"time\":11,\"value\":40.65},{\"logicalIndex\":\"12\",\"time\":12,\"value\":32.88},{\"logicalIndex\":\"13\",\"time\":13,\"value\":31.14}]";
158 | JSONArray jsonArray = JSONArray.parseArray(s);
159 | List indicatorSeries = new ArrayList<>();
160 | for (Object o : jsonArray) {
161 | JSONObject json = (JSONObject) o;
162 | indicatorSeries.add(new IndicatorSeries(json.getLong("time"), json.getDoubleValue("value"), json.getString("logicalIndex")));
163 | }
164 | return indicatorSeries;
165 | }
166 |
167 | @After
168 | public void afterSleep() throws InterruptedException {
169 | System.out.println("click Enter to close window...");
170 | Scanner scanner = new Scanner(System.in);
171 | scanner.nextLine();
172 | }
173 |
174 | }
175 |
--------------------------------------------------------------------------------
/src/test/java/org/algorithmtools/ad4j/model/VolatilityAnalysisTest.java:
--------------------------------------------------------------------------------
1 | package org.algorithmtools.ad4j.model;
2 |
3 | import org.algorithmtools.ad4j.utils.BandwidthUtil;
4 | import org.algorithmtools.ad4j.utils.IndicatorSeriesUtil;
5 | import org.algorithmtools.chart.JFreeChartUtil;
6 | import smile.stat.distribution.KernelDensity;
7 |
8 | import java.util.*;
9 |
10 | /**
11 | * Test Volatility Anomaly
12 | */
13 | public class VolatilityAnalysisTest {
14 |
15 | public static void main(String[] args) {
16 | // double[] data = {10.0, 12.0, 85.0, 70.0, 100.0, 14.0, 14.0, 12.0, 40.0, 20.0};
17 | // double[] data = {45.29, 30.85, 40.23, 15.57, 13.14, 32.53, 44.34, 33.92, 25.31, 31.12, 33.23, 40.65, 32.88, 31.14};
18 | double[] data = {1104.0, 976.0, 949.0, 895.0, 810.0, 975.0, 1152.0, 818.0, 766.0, 502.0, 396.0, 468.0, 592.0, 769.0};
19 | JFreeChartUtil.drawLineChart("data", IndicatorSeriesUtil.transferFromArray(data));
20 |
21 | // volatility
22 | double[] volatility = calculateVolatility(data);
23 | JFreeChartUtil.drawLineChart("volatility", IndicatorSeriesUtil.transferFromArray(volatility));
24 |
25 | // volatility to distribution points
26 | double[][] distributionPoints = transferToDistributionPoints(calculateVolatility(data), 100);
27 | double[] x = distributionPoints[0];
28 | double[] y = distributionPoints[1];
29 | JFreeChartUtil.drawLineChart("KDE", x, y);
30 |
31 | double[] secondDerivatives = calculateSecondDerivatives(x, y);
32 | JFreeChartUtil.drawLineChart("secondDerivative", x, secondDerivatives);
33 |
34 | // find MBP
35 | int[] inflectionPoints = findInflectionPoints(secondDerivatives, x, y);
36 | double minBound = x[inflectionPoints[0]];
37 | double maxBound = x[inflectionPoints[1]];
38 | System.out.println("0-axis left inflection point threshold:" + minBound + "\t 0-axis right inflection point threshold:" + maxBound);
39 |
40 | // check anomaly
41 | checkAnomalies(data, volatility, minBound, maxBound);
42 | }
43 |
44 | public static double[] calculateVolatility(double[] data) {
45 | double[] volatility = new double[data.length];
46 | volatility[0] = 0;
47 | for (int i = 1; i < data.length; i++) {
48 | volatility[i] = (data[i] - data[i - 1]) / data[i - 1];
49 | }
50 | return volatility;
51 | }
52 |
53 | public static double[][] transferToDistributionPoints(double[] data, int points) {
54 | KernelDensity volatilityDistribution = new KernelDensity(data, BandwidthUtil.calculateBandwidth(data) * 1);
55 |
56 | double min = Arrays.stream(data).min().orElse(0.0);
57 | double max = Arrays.stream(data).max().orElse(1.0);
58 | double step = (max - min) / points;
59 |
60 | double[] x = new double[points];
61 | double[] y = new double[points];
62 | for (int i = 0; i < points; i++) {
63 | x[i] = min + i * step;
64 | y[i] = volatilityDistribution.p(x[i]); // KDE estimate
65 | }
66 |
67 | return new double[][]{x, y};
68 | }
69 |
70 | public static double[] calculateSecondDerivatives(double[] x, double[] y) {
71 | int n = x.length;
72 | if (n < 3) {
73 | throw new IllegalArgumentException("At least three points are required to compute second derivatives.");
74 | }
75 |
76 | double h;
77 | double[] secondDerivatives = new double[n];
78 | // Compute second derivatives
79 | for (int i = 0; i < n; i++) {
80 | h = x[1] - x[0];
81 | if (i == 0) {
82 | // Left boundary
83 | secondDerivatives[i] = (y[i + 2] - 2 * y[i + 1] + y[i]) / (h * h);
84 | } else if (i == n - 1) {
85 | // Right boundary
86 | secondDerivatives[i] = (y[i] - 2 * y[i - 1] + y[i - 2]) / (h * h);
87 | } else {
88 | // Internal points
89 | secondDerivatives[i] = (y[i + 1] - 2 * y[i] + y[i - 1]) / (h * h);
90 | }
91 | }
92 |
93 | return secondDerivatives;
94 | }
95 |
96 |
97 | // find max bend point
98 | public static int[] findInflectionPoints(double[] secondDerivative, double[] distributionX, double[] distributionY) {
99 | int zeroLeftInflectionPointIndex = 0;
100 | double zeroLeftInflectionPointMaxDistance = -1;
101 | int zeroRightInflectionPointIndex = distributionX.length - 1;
102 | double zeroRightInflectionPointMaxDistance = -1;
103 | for (int i = 1; i < secondDerivative.length; i++) {
104 | // A change in sign could be an inflection point
105 | if (secondDerivative[i - 1] * secondDerivative[i] < 0) {
106 | double distance = Math.sqrt(Math.pow(distributionX[i], 2) + Math.pow(distributionY[i], 2));
107 | if(distributionX[i] >= 0){
108 | // zero right
109 | if(distance >= zeroRightInflectionPointMaxDistance){
110 | zeroRightInflectionPointIndex = i;
111 | zeroRightInflectionPointMaxDistance = distance;
112 | }
113 | } else {
114 | // zero left
115 | if(distance >= zeroLeftInflectionPointMaxDistance){
116 | zeroLeftInflectionPointIndex = i;
117 | zeroLeftInflectionPointMaxDistance = distance;
118 | }
119 | }
120 | }
121 | }
122 |
123 | return new int[]{zeroLeftInflectionPointIndex, zeroRightInflectionPointIndex};
124 | }
125 |
126 | public static void checkAnomalies(double[] data, double[] volatility, double minBound, double maxBound) {
127 | Map anomalyList = new HashMap<>();
128 | int continualMaxIndex = -1;
129 | int continualLastIndex = -1;
130 | for (int i = 0; i < volatility.length; i++) {
131 | if(volatility[i] > maxBound || volatility[i] < minBound){
132 | // continual isotropic volatilises, select the largest
133 | if(continualLastIndex < 0){
134 | continualMaxIndex = i;
135 | continualLastIndex = i;
136 | } else if(i - 1 == continualLastIndex && volatility[i] * volatility[continualLastIndex] > 0){
137 | if(Math.abs(volatility[i]) > Math.abs(volatility[continualMaxIndex])){
138 | continualMaxIndex = i;
139 | }
140 | continualLastIndex = i;
141 | } else {
142 | anomalyList.put(continualMaxIndex, data[continualMaxIndex]);
143 | continualLastIndex = i;
144 | continualMaxIndex = i;
145 | }
146 | }
147 | }
148 | if(continualMaxIndex >= 0){
149 | anomalyList.put(continualMaxIndex, data[continualMaxIndex]);
150 | }
151 |
152 | anomalyList.forEach((k,v) -> System.out.println("anomaly:" + k + " --> " + v));
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/src/test/java/org/algorithmtools/chart/JFreeChartUtil.java:
--------------------------------------------------------------------------------
1 | package org.algorithmtools.chart;
2 |
3 | import org.algorithmtools.ad4j.pojo.IndicatorSeries;
4 | import org.jfree.chart.ui.UIUtils;
5 | import org.jfree.data.xy.DefaultXYDataset;
6 | import org.jfree.data.xy.XYDataset;
7 |
8 | import javax.swing.*;
9 | import java.util.List;
10 |
11 | public class JFreeChartUtil {
12 |
13 | public static XYDataset transfer(DefaultXYDataset dataset, String series, double[] x, double[] y){
14 | dataset = dataset == null ? new DefaultXYDataset() : dataset;
15 | dataset.addSeries(series, new double[][] {x, y});
16 | return dataset;
17 | }
18 |
19 | public static XYDataset transfer(double[] x, double[] y){
20 | return transfer(null, "data", x, y);
21 | }
22 |
23 | public static void drawLineChart(String title, double[] xData, double[] yData){
24 | SwingUtilities.invokeLater(() -> {
25 | LineChart chart = new LineChart(JFreeChartUtil.transfer(xData, yData), title, "X", "Y", 800, 600);
26 | chart.pack();
27 | UIUtils.centerFrameOnScreen(chart);
28 | chart.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
29 | chart.setVisible(true);
30 | });
31 | }
32 |
33 | public static void drawLineChart(String title, List xData, List yData){
34 | double[] x = new double[xData.size()];
35 | double[] y = new double[yData.size()];
36 | for (int i = 0; i < xData.size(); i++) {
37 | x[i] = xData.get(i);
38 | y[i] = yData.get(i);
39 | }
40 |
41 | drawLineChart(title, x, y);
42 | }
43 |
44 | public static void drawLineChart(String title, List data){
45 | double[] x = new double[data.size()];
46 | double[] y = new double[data.size()];
47 | for (int i = 0; i < data.size(); i++) {
48 | x[i] = data.get(i).getTime();
49 | y[i] = data.get(i).getValue();
50 | }
51 |
52 | drawLineChart(title, x, y);
53 | }
54 |
55 | public static void drawScatterChart(String title, List data){
56 | double[] x = new double[data.size()];
57 | double[] y = new double[data.size()];
58 | for (int i = 0; i < data.size(); i++) {
59 | x[i] = data.get(i).getTime();
60 | y[i] = data.get(i).getValue();
61 | }
62 |
63 | SwingUtilities.invokeLater(() -> {
64 | ScatterChart chart = new ScatterChart(JFreeChartUtil.transfer(x, y), title, "X", "Y", 800, 600);
65 | chart.pack();
66 | UIUtils.centerFrameOnScreen(chart);
67 | chart.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
68 | chart.setVisible(true);
69 | });
70 | }
71 |
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/src/test/java/org/algorithmtools/chart/LineChart.java:
--------------------------------------------------------------------------------
1 | package org.algorithmtools.chart;
2 |
3 |
4 | import org.jfree.chart.ChartFactory;
5 | import org.jfree.chart.ChartPanel;
6 | import org.jfree.chart.JFreeChart;
7 | import org.jfree.chart.axis.NumberAxis;
8 | import org.jfree.chart.plot.XYPlot;
9 | import org.jfree.chart.renderer.xy.XYItemRenderer;
10 | import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
11 | import org.jfree.chart.ui.ApplicationFrame;
12 | import org.jfree.chart.ui.RectangleInsets;
13 | import org.jfree.chart.ui.UIUtils;
14 | import org.jfree.data.time.Month;
15 | import org.jfree.data.time.TimeSeries;
16 | import org.jfree.data.time.TimeSeriesCollection;
17 | import org.jfree.data.xy.XYDataset;
18 |
19 | import javax.swing.*;
20 | import java.awt.*;
21 |
22 | /**
23 | * An example of a time series chart create using JFreeChart. For the most
24 | * part, default settings are used, except that the renderer is modified to
25 | * show filled shapes (as well as lines) at each data point.
26 | */
27 | public class LineChart extends ApplicationFrame {
28 |
29 | private static final long serialVersionUID = 1L;
30 |
31 | private final XYDataset xyDataset;
32 |
33 | private final String panelTitle;
34 | private final String xAxisLabel;
35 | private final String yAxisLabel;
36 |
37 | /**
38 | * A demonstration application showing how to create a simple time series
39 | * chart. This example uses monthly data.
40 | *
41 | * @param xyDataset xyDataset.
42 | * @param title the frame title.
43 | * @param xAxisLabel xAxisLabel.
44 | * @param yAxisLabel yAxisLabel.
45 | * @param width width.
46 | * @param height height.
47 | */
48 | public LineChart(XYDataset xyDataset, String title, String xAxisLabel, String yAxisLabel, int width, int height) {
49 | super("Time series chart frame");
50 | this.xyDataset = xyDataset;
51 | this.panelTitle = title;
52 | this.xAxisLabel = xAxisLabel;
53 | this.yAxisLabel = yAxisLabel;
54 | ChartPanel chartPanel = (ChartPanel) createDemoPanel();
55 | chartPanel.setPreferredSize(new java.awt.Dimension(width, height));
56 | setContentPane(chartPanel);
57 | }
58 |
59 | /**
60 | * Creates a panel for the demo (used by SuperDemo.java).
61 | *
62 | * @return A panel.
63 | */
64 | public JPanel createDemoPanel() {
65 | JFreeChart chart = createChart(xyDataset);
66 | ChartPanel panel = new ChartPanel(chart, false);
67 | panel.setFillZoomRectangle(true);
68 | panel.setMouseWheelEnabled(true);
69 | return panel;
70 | }
71 |
72 | /**
73 | * Creates a chart.
74 | *
75 | * @param dataset a dataset.
76 | *
77 | * @return A chart.
78 | */
79 | private JFreeChart createChart(XYDataset dataset) {
80 |
81 | JFreeChart chart = ChartFactory.createTimeSeriesChart(
82 | panelTitle, // title
83 | xAxisLabel, // x-axis label
84 | yAxisLabel, // y-axis label
85 | dataset);
86 |
87 | chart.setBackgroundPaint(Color.WHITE);
88 |
89 | XYPlot plot = (XYPlot) chart.getPlot();
90 | plot.setBackgroundPaint(Color.LIGHT_GRAY);
91 | plot.setDomainGridlinePaint(Color.WHITE);
92 | plot.setRangeGridlinePaint(Color.WHITE);
93 | plot.setAxisOffset(new RectangleInsets(5.0, 5.0, 5.0, 5.0));
94 | plot.setDomainCrosshairVisible(true);
95 | plot.setRangeCrosshairVisible(true);
96 |
97 | XYItemRenderer r = plot.getRenderer();
98 | if (r instanceof XYLineAndShapeRenderer) {
99 | XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) r;
100 | renderer.setDefaultShapesVisible(true);
101 | renderer.setDefaultShapesFilled(true);
102 | renderer.setDrawSeriesLineAsPath(true);
103 | }
104 |
105 | NumberAxis xAxis = new NumberAxis("X");
106 | plot.setDomainAxis(xAxis);
107 | // DateAxis axis = (DateAxis) plot.getDomainAxis();
108 | // axis.setDateFormatOverride(new SimpleDateFormat("YYYY-MM-dd"));
109 |
110 | return chart;
111 | }
112 |
113 | /**
114 | * Creates a dataset, consisting of two series of monthly data.
115 | *
116 | * @return The dataset.
117 | */
118 | private XYDataset createDataset() {
119 |
120 | TimeSeries s1 = new TimeSeries("L&G European Index Trust");
121 | s1.add(new Month(2, 2001), 181.8);
122 | s1.add(new Month(3, 2001), 167.3);
123 | s1.add(new Month(4, 2001), 153.8);
124 | s1.add(new Month(5, 2001), 167.6);
125 | s1.add(new Month(6, 2001), 158.8);
126 | s1.add(new Month(7, 2001), 148.3);
127 | s1.add(new Month(8, 2001), 153.9);
128 | s1.add(new Month(9, 2001), 142.7);
129 | s1.add(new Month(10, 2001), 123.2);
130 | s1.add(new Month(11, 2001), 131.8);
131 | s1.add(new Month(12, 2001), 139.6);
132 | s1.add(new Month(1, 2002), 142.9);
133 | s1.add(new Month(2, 2002), 138.7);
134 | s1.add(new Month(3, 2002), 137.3);
135 | s1.add(new Month(4, 2002), 143.9);
136 | s1.add(new Month(5, 2002), 139.8);
137 | s1.add(new Month(6, 2002), 137.0);
138 | s1.add(new Month(7, 2002), 132.8);
139 |
140 | TimeSeries s2 = new TimeSeries("L&G UK Index Trust");
141 | s2.add(new Month(2, 2001), 129.6);
142 | s2.add(new Month(3, 2001), 123.2);
143 | s2.add(new Month(4, 2001), 117.2);
144 | s2.add(new Month(5, 2001), 124.1);
145 | s2.add(new Month(6, 2001), 122.6);
146 | s2.add(new Month(7, 2001), 119.2);
147 | s2.add(new Month(8, 2001), 116.5);
148 | s2.add(new Month(9, 2001), 112.7);
149 | s2.add(new Month(10, 2001), 101.5);
150 | s2.add(new Month(11, 2001), 106.1);
151 | s2.add(new Month(12, 2001), 110.3);
152 | s2.add(new Month(1, 2002), 111.7);
153 | s2.add(new Month(2, 2002), 111.0);
154 | s2.add(new Month(3, 2002), 109.6);
155 | s2.add(new Month(4, 2002), 113.2);
156 | s2.add(new Month(5, 2002), 111.6);
157 | s2.add(new Month(6, 2002), 108.8);
158 | s2.add(new Month(7, 2002), 101.6);
159 |
160 | TimeSeriesCollection dataset = new TimeSeriesCollection();
161 | dataset.addSeries(s1);
162 | dataset.addSeries(s2);
163 |
164 | return dataset;
165 |
166 | }
167 |
168 | /**
169 | * Starting point for the demonstration application.
170 | *
171 | * @param args ignored.
172 | */
173 | public static void main(String[] args) {
174 | double[] xData = {1.0, 2.0, 3.0, 4.0, 5.0};
175 | double[] yData = {5.0, 7.0, 6.0, 8.0, 4.0};
176 | LineChart demo = new LineChart(JFreeChartUtil.transfer(xData, yData), "Test Chart", "X", "Y", 500, 350);
177 | demo.pack();
178 | UIUtils.centerFrameOnScreen(demo);
179 | demo.setVisible(true);
180 | }
181 |
182 | }
--------------------------------------------------------------------------------
/src/test/java/org/algorithmtools/chart/ScatterChart.java:
--------------------------------------------------------------------------------
1 | package org.algorithmtools.chart;
2 |
3 |
4 | import org.jfree.chart.ChartFactory;
5 | import org.jfree.chart.ChartPanel;
6 | import org.jfree.chart.JFreeChart;
7 | import org.jfree.chart.axis.NumberAxis;
8 | import org.jfree.chart.plot.XYPlot;
9 | import org.jfree.chart.renderer.xy.XYItemRenderer;
10 | import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
11 | import org.jfree.chart.ui.ApplicationFrame;
12 | import org.jfree.chart.ui.RectangleInsets;
13 | import org.jfree.chart.ui.UIUtils;
14 | import org.jfree.data.xy.XYDataset;
15 |
16 | import javax.swing.*;
17 | import java.awt.*;
18 |
19 | /**
20 | * An example of a time series chart create using JFreeChart. For the most
21 | * part, default settings are used, except that the renderer is modified to
22 | * show filled shapes (as well as lines) at each data point.
23 | */
24 | public class ScatterChart extends ApplicationFrame {
25 |
26 | private static final long serialVersionUID = 1L;
27 |
28 | private final XYDataset xyDataset;
29 |
30 | private final String panelTitle;
31 | private final String xAxisLabel;
32 | private final String yAxisLabel;
33 |
34 | /**
35 | * A demonstration application showing how to create a simple time series
36 | * chart. This example uses monthly data.
37 | *
38 | * @param xyDataset xyDataset.
39 | * @param title the frame title.
40 | * @param xAxisLabel xAxisLabel.
41 | * @param yAxisLabel yAxisLabel.
42 | * @param width width.
43 | * @param height height.
44 | */
45 | public ScatterChart(XYDataset xyDataset, String title, String xAxisLabel, String yAxisLabel, int width, int height) {
46 | super("Time series chart frame");
47 | this.xyDataset = xyDataset;
48 | this.panelTitle = title;
49 | this.xAxisLabel = xAxisLabel;
50 | this.yAxisLabel = yAxisLabel;
51 | ChartPanel chartPanel = (ChartPanel) createDemoPanel();
52 | chartPanel.setPreferredSize(new Dimension(width, height));
53 | setContentPane(chartPanel);
54 | }
55 |
56 | /**
57 | * Creates a panel for the demo (used by SuperDemo.java).
58 | *
59 | * @return A panel.
60 | */
61 | public JPanel createDemoPanel() {
62 | JFreeChart chart = createChart(xyDataset);
63 | ChartPanel panel = new ChartPanel(chart, false);
64 | panel.setFillZoomRectangle(true);
65 | panel.setMouseWheelEnabled(true);
66 | return panel;
67 | }
68 |
69 | /**
70 | * Creates a chart.
71 | *
72 | * @param dataset a dataset.
73 | *
74 | * @return A chart.
75 | */
76 | private JFreeChart createChart(XYDataset dataset) {
77 |
78 | JFreeChart chart = ChartFactory.createScatterPlot(
79 | panelTitle, // title
80 | xAxisLabel, // x-axis label
81 | yAxisLabel, // y-axis label
82 | dataset);
83 |
84 | chart.setBackgroundPaint(Color.WHITE);
85 |
86 | XYPlot plot = (XYPlot) chart.getPlot();
87 | plot.setBackgroundPaint(Color.LIGHT_GRAY);
88 | plot.setDomainGridlinePaint(Color.WHITE);
89 | plot.setRangeGridlinePaint(Color.WHITE);
90 | plot.setAxisOffset(new RectangleInsets(5.0, 5.0, 5.0, 5.0));
91 | plot.setDomainCrosshairVisible(true);
92 | plot.setRangeCrosshairVisible(true);
93 |
94 | XYItemRenderer r = plot.getRenderer();
95 | if (r instanceof XYLineAndShapeRenderer) {
96 | XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer) r;
97 | renderer.setDefaultShapesVisible(true);
98 | renderer.setDefaultShapesFilled(true);
99 | renderer.setDrawSeriesLineAsPath(true);
100 | }
101 |
102 | NumberAxis xAxis = new NumberAxis("X");
103 | plot.setDomainAxis(xAxis);
104 | // DateAxis axis = (DateAxis) plot.getDomainAxis();
105 | // axis.setDateFormatOverride(new SimpleDateFormat("YYYY-MM-dd"));
106 |
107 | return chart;
108 | }
109 |
110 |
111 | /**
112 | * Starting point for the demonstration application.
113 | *
114 | * @param args ignored.
115 | */
116 | public static void main(String[] args) {
117 | double[] xData = {1.0, 2.0, 3.0, 4.0, 5.0};
118 | double[] yData = {5.0, 7.0, 6.0, 8.0, 4.0};
119 | ScatterChart demo = new ScatterChart(JFreeChartUtil.transfer(xData, yData), "Test Chart", "X", "Y", 800, 600);
120 | demo.pack();
121 | UIUtils.centerFrameOnScreen(demo);
122 | demo.setVisible(true);
123 | }
124 |
125 | }
--------------------------------------------------------------------------------
/src/test/java/org/algorithmtools/example/BizUseExample.java:
--------------------------------------------------------------------------------
1 | package org.algorithmtools.example;
2 |
3 | import org.algorithmtools.ad4j.engine.AnomalyDetectionEngine;
4 | import org.algorithmtools.ad4j.pojo.AnomalyDetectionResult;
5 | import org.algorithmtools.ad4j.pojo.IndicatorInfo;
6 | import org.algorithmtools.ad4j.pojo.IndicatorSeries;
7 | import org.algorithmtools.ad4j.utils.IndicatorSeriesUtil;
8 |
9 | import java.util.ArrayList;
10 | import java.util.List;
11 |
12 | public class BizUseExample {
13 |
14 | public static void main(String[] args) {
15 | indicatorDetect();
16 | }
17 |
18 | public static void indicatorDetect(){
19 | // 1. Transfer biz data to indicator series info
20 | long currentTime = System.currentTimeMillis();
21 | List indicatorSeries = new ArrayList<>();
22 | indicatorSeries.add(new IndicatorSeries(currentTime + 1, 1d, "logicalIndex-1"));
23 | indicatorSeries.add(new IndicatorSeries(currentTime + 2, 2d, "logicalIndex-2"));
24 | indicatorSeries.add(new IndicatorSeries(currentTime + 3, 3d, "logicalIndex-3"));
25 | indicatorSeries.add(new IndicatorSeries(currentTime + 4, 4d, "logicalIndex-4"));
26 | indicatorSeries.add(new IndicatorSeries(currentTime + 5, 40d, "logicalIndex-5"));
27 | indicatorSeries.add(new IndicatorSeries(currentTime + 6, 6d, "logicalIndex-6"));
28 | indicatorSeries.add(new IndicatorSeries(currentTime + 7, 7d, "logicalIndex-7"));
29 | indicatorSeries.add(new IndicatorSeries(currentTime + 8, 8d, "logicalIndex-8"));
30 | indicatorSeries.add(new IndicatorSeries(currentTime + 9, 9d, "logicalIndex-9"));
31 | indicatorSeries.add(new IndicatorSeries(currentTime + 10, 10d, "logicalIndex-10"));
32 |
33 | IndicatorInfo info = new IndicatorInfo("Example", "Example-Name", indicatorSeries);
34 |
35 | // 2. New AnomalyDetectionEngine to detect
36 | AnomalyDetectionEngine engine = new AnomalyDetectionEngine();
37 | AnomalyDetectionResult detectionResult = engine.detect(info);
38 |
39 | // 3. Business process detect result. Like Records,Alarms,Print
40 | IndicatorSeriesUtil.print(detectionResult);
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------