# -*- coding: utf-8 -*-"""Quasi-Monte Carlo Discrepancy outlier detection (QMCD)"""# Author: D Kulik# License: BSD 2 clauseimportnumpyasnpimportscipy.statsasstatsfromnumbaimportnjit,prangefromsklearn.preprocessingimportMinMaxScalerfromsklearn.utilsimportcheck_arrayfromsklearn.utils.validationimportcheck_is_fittedfrom.baseimportBaseDetector@njit(fastmath=True,parallel=True)def_wrap_around_discrepancy(data,check):"""Wrap-around Quasi-Monte Carlo discrepancy method"""n=data.shape[0]d=data.shape[1]p=check.shape[0]disc=np.zeros(p)foriinprange(p):dc=0.0forjinprange(n):prod=1.0forkinprange(d):x_kikj=abs(check[i,k]-data[j,k])prod*=3.0/2.0-x_kikj+x_kikj**2dc+=proddisc[i]=dcreturn-(4.0/3.0)**d+1.0/(n**2)*disc
[docs]classQMCD(BaseDetector):"""The Wrap-around Quasi-Monte Carlo discrepancy is a uniformity criterion which is used to assess the space filling of a number of samples in a hypercube. It quantifies the distance between the continuous uniform distribution on a hypercube and the discrete uniform distribution on distinct sample points. Therefore, lower discrepancy values for a sample point indicates that it provides better coverage of the parameter space with regard to the rest of the samples. This method is kernel based and a higher discrepancy score is relative to the rest of the samples, the higher the likelihood of it being an outlier. Read more in the :cite:`fang2001wrap`. Parameters ---------- 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,contamination=0.1):super(QMCD,self).__init__(contamination=contamination)
[docs]deffit(self,X,y=None):"""Fit detector Parameters ---------- X : numpy array of shape (n_samples, n_features) The input samples. y : Ignored Not used, present for API consistency by convention. """# validate inputs X and y (optional)X=check_array(X)self._set_n_classes(y)# Normalize data between 0 and 1self._scaler=MinMaxScaler()X_norm=self._scaler.fit_transform(X)self._fitted_data=X_norm.copy()# Calculate WD QMCD scoresscores=_wrap_around_discrepancy(X_norm,X_norm)# Get criterion for inverting scoresself._is_flipped=Falseskew=stats.skew(scores)kurt=stats.kurtosis(scores)# Invert score order based on criterionif(skew<0)or((skew>=0)&(kurt<0)):scores=scores.max()+scores.min()-scoresself._is_flipped=Trueself.decision_scores_=scoresself._process_decision_scores()returnself
[docs]defdecision_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 independent and dependent/target samples with the target samples being the last column of the numpy array such that eg: X = np.append(x, y.reshape(-1,1), axis=1). Sparse matrices are accepted only if they are supported by the base estimator. Returns ------- anomaly_scores : numpy array of shape (n_samples,) The anomaly score of the input samples. """check_is_fitted(self,['decision_scores_','threshold_','labels_'])X=check_array(X)# Scale data to fitted dataX_norm=self._scaler.transform(X)# Calculate WD QMCD scoresscores=_wrap_around_discrepancy(self._fitted_data,X_norm)# Invert score order based on criterionifself._is_flipped:scores=self.decision_scores_.max()+self.decision_scores_.min()-scoresreturnscores