Home

Awesome

<p align="center"> <img width="150" src="./docs/VUS_logo.png"/> </p> <h1 align="center">Volume Under the Surface</h1> <h2 align="center">A New Accuracy Evaluation Measure for Time-Series Anomaly Detection</h2> <div align="center"> <p> <img alt="PyPI - Downloads" src="https://pepy.tech/badge/vus"> <img alt="PyPI" src="https://img.shields.io/pypi/v/vus"> <img alt="GitHub issues" src="https://img.shields.io/github/issues/TheDatumOrg/vus"> <img alt="PyPI - Python Version" src="https://img.shields.io/pypi/pyversions/vus"> </p> </div>

The receiver operator characteristic (ROC) curve and the area under the curve (AUC) are widely used to compare the performance of different anomaly detectors. They mainly focus on point-based detection. However, the detection of collective anomalies concerns two factors: whether this outlier is detected and what percentage of this outlier is detected. The first factor is not reflected in the AUC. Another problem is the possible shift between the anomaly score and the real outlier due to the application of the sliding window. To tackle these problems, we incorporate the idea of range-based precision and recall, and suggest the range-based ROC and its counterpart in the precision-recall space, which provides a new evaluation for the collective anomalies. We finally introduce a new measure VUS (Volume Under the Surface) which corresponds to the averaged range-based measure when we vary the range size. We demonstrate in a large experimental evaluation that the proposed measures are significantly more robust to important criteria (such as lag and noise) and also significantly more useful to separate correctly the accurate from the the inaccurate methods.

<p align="center"> <img width="500" src="./docs/RangeAUCandVUS.png"/> </p>

Related Repository:

If you use VUS in your project or research, cite the following two papers:

References

"Volume Under the Surface: A New Accuracy Evaluation Measure for Time-Series Anomaly Detection"<br/> John Paparrizos, Paul Boniol, Themis Palpanas, Ruey Tsay, Aaron Elmore, and Michael Franklin<br/> Proceedings of the VLDB Endowment (PVLDB 2022) Journal, Volume 15, pages 2774‑2787<br/>

@article{paparrizos2022volume,
  title={{Volume Under the Surface: A New Accuracy Evaluation Measure for Time-Series Anomaly Detection}},
  author={Paparrizos, John and Boniol, Paul and Palpanas, Themis and Tsay, Ruey S and Elmore, Aaron and Franklin, Michael J},
  journal={Proceedings of the VLDB Endowment},
  volume={15},
  number={11},
  pages={2774--2787},
  year={2022},
  publisher={VLDB Endowment}
}

"TSB-UAD: An End-to-End Benchmark Suite for Univariate Time-Series Anomaly Detection"<br/> John Paparrizos, Yuhao Kang, Paul Boniol, Ruey Tsay, Themis Palpanas, and Michael Franklin.<br/> Proceedings of the VLDB Endowment (PVLDB 2022) Journal, Volume 15, pages 1697–1711<br/>

@article{paparrizos2022tsb,
  title={Tsb-uad: an end-to-end benchmark suite for univariate time-series anomaly detection},
  author={Paparrizos, John and Kang, Yuhao and Boniol, Paul and Tsay, Ruey S and Palpanas, Themis and Franklin, Michael J},
  journal={Proceedings of the VLDB Endowment},
  volume={15},
  number={8},
  pages={1697--1711},
  year={2022},
  publisher={VLDB Endowment}
}

"The Elephant in the Room: Towards A Reliable Time-Series Anomaly Detection Benchmark"<br/> Qinghua Liu and John Paparrizos<br/> NeurIPS 2024<br/>

@inproceedings{liu2024elephant,
  title={The Elephant in the Room: Towards A Reliable Time-Series Anomaly Detection Benchmark},
  author={Liu, Qinghua and Paparrizos, John},
  booktitle={NeurIPS 2024},
  year={2024}
}

Data

To ease reproducibility, we share our results over TSB-UAD benchmark dataset

Installation

Install from pip

$ pip install vus

Create Environment and Install Dependencies

$ conda env create --file environment.yml
$ conda activate VUS-env
$ pip install -r requirements.txt

Install from source

$ git clone https://github.com/johnpaparrizos/VUS
$ cd VUS/
$ python setup.py install

Experiments

Analysis of the Ranks over different Accuracy Measures for all Methods:

AUC_PRAUC_ROCR_AUC_PRR_AUC_ROCVUS_PRVUS_ROCPrecision@kRecallPrecisionRrecallRprecisionFRF
NormA4.2537734.1036234.2986024.3799064.2930084.3008584.2104854.2498894.7873664.5599224.4637384.4250604.650611
POLY4.6869584.7047034.5354065.0507374.4733944.9832835.3844824.9039714.9940085.1095594.8554654.9207865.006390
IForest4.5409554.3014714.5703414.4060664.6211004.4064585.0422055.1142035.0754455.8495494.8205065.1035985.547707
AE4.9132904.8255404.8428534.6847164.8476604.6503594.8805524.9536874.6407315.2792244.7408624.8385074.919577
OCSVM5.4540065.5016065.3242055.3681125.3215745.4490865.6975305.7535135.0648165.5591305.5036055.5958935.493684
MatrixProfile5.5657795.2647885.1365235.0870605.1969175.1732785.1459455.1910285.5891285.3793955.7073885.3903215.671893
LOF4.6486094.7155783.9113824.2095173.9446754.3085224.6615084.7068214.4918744.7605644.4817984.6994444.886699
LSTM5.7057586.1623796.5814566.3489496.5594466.2887005.0890405.1632195.3630244.3458315.3395335.1222154.496773
CNN5.2308725.4203125.7992315.4649375.7422265.4394564.8882534.9636684.9936084.1568255.0871054.9041764.326666

Robustness to Lag:

Top figure depicts the average standard deviation for ten different lag values over the AD methods applied on the MBA(805) time series. Bottom figure depicts the accuracy (measured 10 times) with random lag ℓ ∈ [−0.25 ∗ ℓ, 0.25 ∗ ℓ ] injected in the anomaly score with average accuracy centered to 0.

<p align="center"> <img width="500" src="https://raw.githubusercontent.com/bogireddytejareddy/VUS/patch-1/docs/lag.png"/> </p>

Separability Analysis: :

Applied on 8 pairs of accurate (in green) and inaccurate (in red) methods on MBA(805) data.

<p align="center"> <img width="500" height="550" src="https://github.com/bogireddytejareddy/VUS/blob/patch-1/docs/separability_analysis.png"/> </p>

Also see notebooks in experiments folder for more analysis on Roubstness, Separability and Entropy.

Usage

We depicts below a code snippet demonstrating how to use our measure.

Compute measure

import math
import numpy as np
import pandas as pd
from vus.models.feature import Window
from vus.metrics import get_metrics
from sklearn.preprocessing import MinMaxScaler


def anomaly_results(X_data):
    # Isolation Forest
    from vus.models.iforest import IForest
    IF_clf = IForest(n_jobs=1)
    x = X_data
    IF_clf.fit(x)
    IF_score = IF_clf.decision_scores_

    return IF_score


def scoring(score, labels, slidingWindow):
    # Score normalization
    score = MinMaxScaler(feature_range=(0,1)).fit_transform(score.reshape(-1,1)).ravel()
    score = np.array([score[0]]*math.ceil((slidingWindow-1)/2) + list(score) + [score[-1]]*((slidingWindow-1)//2))

    results = get_metrics(score, labels, metric='all', slidingWindow=slidingWindow) # default metric='vus'

    for metric in results.keys():
        print(metric, ':', results[metric])


# Data Preprocessing
slidingWindow = 100 # user-defined subsequence length
dataset = pd.read_csv('./data/MBA_ECG805_data.out', header=None).to_numpy()
data = dataset[:, 0]
labels = dataset[:, 1]
X_data = Window(window = slidingWindow).convert(data).to_numpy()

if_score = anomaly_results(X_data)
print('Isolation Forest')
scoring(if_score, labels, slidingWindow)
Isolation Forest
AUC_ROC : 0.9263301592778213
AUC_PR : 0.6973053569232922
Precision : 0.8200118413262285
Recall : 0.41138613861386136
F : 0.5479000461528318
Precision_at_k : 0.41138613861386136
Rprecision : 0.7840823900854653
Rrecall : 0.29697975549209193
RF : 0.4307922489855251
R_AUC_ROC : 0.9892262823191413
R_AUC_PR : 0.946785752183305
VUS_ROC : 0.9744692546344875
VUS_PR : 0.8988057857811111
Affiliation_Precision : 0.9657740519979081
Affiliation_Recall : 0.9877594760008377

SlidingWindow parameter

Note that Range_auc and VUS measures need a slidingWindow parameter. This parameter corresponds to the buffer length for Range_auc and the maximal buffer length for VUS. This parameter can be set using the following strategies:

SlidingWindow parameter: External Knowledge

Example on how to set the slidingWindow parameter to the median anomaly length of the time series:

import numpy as np
import pandas as pd
from vus.models.feature import Window
from vus.utils.utility import get_list_anomaly

# Data Preprocessing
dataset = pd.read_csv('./data/MBA_ECG805_data.out', header=None).to_numpy()
data = dataset[:, 0]
labels = dataset[:, 1]

# set to the mean anoamly length in the time series
slidingWindow = int(np.median(get_list_anomaly(labels)))
print("slidingWindow parameter: ",slidingWindow)

#Build dataset
X_data = Window(window = slidingWindow).convert(data).to_numpy()

slidingWindow parameter:  102

SlidingWindow parameter: Automatic estimation

Example on how to set the slidingWindow parameter to the period of the time series:

import numpy as np
import pandas as pd
from vus.models.feature import Window
from vus.utils.slidingWindows import find_length

# Data Preprocessing
dataset = pd.read_csv('./data/MBA_ECG805_data.out', header=None).to_numpy()
data = dataset[:, 0]
labels = dataset[:, 1]

# set to the mean anoamly length in the time series
slidingWindow = find_length(data)
print("slidingWindow parameter: ",slidingWindow)

#Build dataset
X_data = Window(window = slidingWindow).convert(data).to_numpy()

slidingWindow parameter:  99

Contributors