Source code for pyod.models.mad

# -*- coding: utf-8 -*-
Median Absolute deviation (MAD) Algorithm.
Strictly for Univariate Data.
# Author: Yahya Almardeny <>
# License: BSD 2 clause

from __future__ import division
from __future__ import print_function

import numpy as np
from sklearn.utils import check_array

from .base import BaseDetector

def _check_dim(X):
    Internal function to assert univariate data
    if X.shape[1] != 1:
        raise ValueError('MAD algorithm is just for univariate data. '
                         'Got Data with {} Dimensions.'.format(X.shape[1]))

[docs] class MAD(BaseDetector): """Median Absolute Deviation: for measuring the distances between data points and the median in terms of median distance. See :cite:`iglewicz1993detect` for details. Parameters ---------- threshold : float, optional (default=3.5) The modified z-score to use as a threshold. Observations with a modified z-score (based on the median absolute deviation) greater than this value will be classified as outliers. Attributes ---------- decision_scores_ : numpy array of shape (n_samples,) The outlier scores of the training data. The higher, the more abnormal. Outliers tend to have higher scores. This value is available once the detector is fitted. threshold_ : float The modified z-score to use as a threshold. Observations with a modified z-score (based on the median absolute deviation) greater than this value will be classified as outliers. labels_ : int, either 0 or 1 The binary labels of the training data. 0 stands for inliers and 1 for outliers/anomalies. It is generated by applying ``threshold_`` on ``decision_scores_``. """ def __init__(self, threshold=3.5, contamination=0.1): super(MAD, self).__init__(contamination=contamination) if not isinstance(threshold, (float, int)): raise TypeError( 'threshold must be a number. Got {}'.format(type(threshold))) self.threshold = threshold
[docs] def fit(self, X, y=None): """Fit detector. y is ignored in unsupervised methods. Parameters ---------- X : numpy array of shape (n_samples, n_features) The input samples. Note that `n_features` must equal 1. y : Ignored Not used, present for API consistency by convention. Returns ------- self : object Fitted estimator. """ X = check_array(X, ensure_2d=False, force_all_finite=False) _check_dim(X) self._set_n_classes(y) self.threshold_ = self.threshold self.median_ = None # reset median after each call self.median_diff_ = None # reset median_diff after each call self.decision_scores_ = self.decision_function(X) self._process_decision_scores() return self
[docs] def decision_function(self, X): """Predict raw anomaly score of X using the fitted detector. The anomaly score of an input sample is computed based on different detector algorithms. For consistency, outliers are assigned with larger anomaly scores. Parameters ---------- X : numpy array of shape (n_samples, n_features) The training input samples. Sparse matrices are accepted only if they are supported by the base estimator. Note that `n_features` must equal 1. Returns ------- anomaly_scores : numpy array of shape (n_samples,) The anomaly score of the input samples. """ X = check_array(X, ensure_2d=False, force_all_finite=False) _check_dim(X) return self._mad(X)
def _mad(self, X): """ Apply the robust median absolute deviation (MAD) to measure the distances of data points from the median. Returns ------- numpy array containing modified Z-scores of the observations. The greater the score, the greater the outlierness. """ obs = np.reshape(X, (-1, 1)) # `self.median` will be None only before `fit()` is called self.median_ = np.nanmedian(obs) if self.median_ is None else self.median_ diff = np.abs(obs - self.median_) self.median_diff_ = np.nanmedian(diff) if self.median_diff_ is None else self.median_diff_ return np.nan_to_num(np.ravel(0.6745 * diff / self.median_diff_)) def _process_decision_scores(self): """This overrides PyOD base class function in order to use the proper `threshold_` which is quite different in the base class. Internal function to calculate key attributes: - labels_: binary labels of training data. - _mu: mean of decision scores. - _sigma: standard deviation of decision scores. Returns ------- self """ self.labels_ = (self.decision_scores_ > self.threshold).astype('int').ravel() # calculate for predict_proba() self._mu = np.nanmean(self.decision_scores_) self._sigma = np.nanstd(self.decision_scores_) return self