com.yahoo.egads.models.adm.KSigmaModel.java Source code

Java tutorial

Introduction

Here is the source code for com.yahoo.egads.models.adm.KSigmaModel.java

Source

/*
 * Copyright 2015, Yahoo Inc.
 * Copyrights licensed under the GPL License.
 * See the accompanying LICENSE file for terms.
 */

// A simple thresholding model that returns an anomaly if it is above/below a certain threashold.

package com.yahoo.egads.models.adm;

import java.util.Properties;
import java.util.Map;
import java.util.HashMap;
import java.util.ArrayList;

import com.yahoo.egads.data.Anomaly.IntervalSequence;
import com.yahoo.egads.data.Anomaly.Interval;
import com.yahoo.egads.data.TimeSeries.DataSequence;
import com.yahoo.egads.utilities.AutoSensitivity;
import com.yahoo.egads.data.AnomalyErrorStorage;

import org.json.JSONObject;
import org.json.JSONStringer;

public class KSigmaModel extends AnomalyDetectionAbstractModel {

    // The constructor takes a set of properties
    // needed for the simple model. This includes the sensitivity.
    private Map<String, Float> threshold;
    private int maxHrsAgo;
    // modelName.
    public String modelName = "KSigmaModel";
    public AnomalyErrorStorage aes = new AnomalyErrorStorage();

    public KSigmaModel(Properties config) {
        super(config);

        if (config.getProperty("MAX_ANOMALY_TIME_AGO") == null) {
            throw new IllegalArgumentException("MAX_ANOMALY_TIME_AGO is NULL");
        }
        this.maxHrsAgo = new Integer(config.getProperty("MAX_ANOMALY_TIME_AGO"));

        this.threshold = parseMap(config.getProperty("THRESHOLD"));

        if (config.getProperty("THRESHOLD") != null && this.threshold.isEmpty() == true) {
            throw new IllegalArgumentException("THRESHOLD PARSE ERROR");
        }
    }

    public void toJson(JSONStringer json_out) {

    }

    public void fromJson(JSONObject json_obj) {

    }

    public String getModelName() {
        return modelName;
    }

    @Override
    public String getType() {
        return "point_outlier";
    }

    @Override
    public void reset() {
        // At this point, reset does nothing.
    }

    @Override
    public void tune(DataSequence observedSeries, DataSequence expectedSeries, IntervalSequence anomalySequence)
            throws Exception {
        HashMap<String, ArrayList<Float>> allErrors = aes.initAnomalyErrors(observedSeries, expectedSeries);

        for (int i = 0; i < (aes.getIndexToError().keySet()).size(); i++) {
            // Add a new error metric if the error metric has not been
            // defined by the user.
            if (!threshold.containsKey(aes.getIndexToError().get(i))) {
                Float[] fArray = (allErrors.get(aes.getIndexToError().get(i)))
                        .toArray(new Float[(allErrors.get(aes.getIndexToError().get(i))).size()]);
                threshold.put(aes.getIndexToError().get(i),
                        AutoSensitivity.getKSigmaSensitivity(fArray, sDAutoSensitivity));
            }
        }
    }

    // Returns true this point is identified as a potential anomaly.
    public boolean isAnomaly(Float[] errors, Map<String, Float> threshold) {
        // Cycle through all available thresholds and return
        // true if any of them matches.
        for (Map.Entry<String, Float> entry : threshold.entrySet()) {
            // disable mapee and mape.
            if (aes.getErrorToIndex().containsKey(entry.getKey()) == true
                    && Math.abs(errors[aes.getErrorToIndex().get(entry.getKey())]) >= Math.abs(entry.getValue())) {
                return true;
            }
        }
        return false;
    }

    @Override
    public IntervalSequence detect(DataSequence observedSeries, DataSequence expectedSeries) throws Exception {

        // At detection time, the anomaly thresholds shouldn't all be 0.
        Float threshSum = (float) 0.0;
        for (Map.Entry<String, Float> entry : this.threshold.entrySet()) {
            threshSum += Math.abs(entry.getValue());
        }

        // Get an array of thresholds.
        Float[] thresholdErrors = new Float[aes.getErrorToIndex().size()];
        for (Map.Entry<String, Float> entry : this.threshold.entrySet()) {
            thresholdErrors[aes.getErrorToIndex().get(entry.getKey())] = Math.abs(entry.getValue());
        }

        IntervalSequence output = new IntervalSequence();
        int n = observedSeries.size();
        long unixTime = System.currentTimeMillis() / 1000L;

        for (int i = 0; i < n; i++) {
            Float[] errors = aes.computeErrorMetrics(expectedSeries.get(i).value, observedSeries.get(i).value);
            logger.debug("TS:" + observedSeries.get(i).time + ",E:" + arrayF2S(errors) + ",TE:"
                    + arrayF2S(thresholdErrors) + ",OV:" + observedSeries.get(i).value + ",EV:"
                    + expectedSeries.get(i).value);
            if (observedSeries.get(i).value != expectedSeries.get(i).value && threshSum > (float) 0.0
                    && isAnomaly(errors, threshold) == true
                    && ((((unixTime - observedSeries.get(i).time) / 3600) < maxHrsAgo)
                            || (maxHrsAgo == 0 && i == (n - 1)))) {
                output.add(new Interval(observedSeries.get(i).time, i, errors, thresholdErrors,
                        observedSeries.get(i).value, expectedSeries.get(i).value));
            }
        }
        return output;
    }
}