# -*- coding: utf-8 -*- import numpy as np import pandas as pd import matplotlib.pyplot as plt from math import log,floor import tqdm, os, pickle from scipy.optimize import minimize # colors for plot deep_saffron = '#FF9933' air_force_blue = '#5D8AA8' def backMean(X,d): M = [] w = X[:d].sum() M.append(w/d) for i in range(d,len(X)): w = w - X[i-d] + X[i] M.append(w/d) return np.array(M) class momSPOT: """ This class allows to run biSPOT algorithm on univariate dataset (upper and lower bounds) Attributes ---------- proba : float Detection level (risk), chosen by the user extreme_quantile : float current threshold (bound between normal and abnormal events) init_threshold : float initial threshold computed during the calibration step peaks : numpy.array array of peaks (excesses above the initial threshold) n : int number of observed values Nt : int number of observed peaks """ def __init__(self, q = 1e-4): """ Constructor Parameters ---------- q Detection level (risk) Returns ---------- biSPOT object """ self.proba = q self.n = 0 nonedict = {'up':None,'down':None} self.extreme_quantile = dict.copy(nonedict) self.init_threshold = dict.copy(nonedict) self.history_peaks = dict.copy(nonedict) self.peaks = dict.copy(nonedict) self.gamma = dict.copy(nonedict) self.sigma = dict.copy(nonedict) self.Nt = {'up':0,'down':0} def __str__(self): s = '' s += 'Streaming Peaks-Over-Threshold Object\n' s += 'Detection level q = %s\n' % self.proba if self.n == 0: s += 'Algorithm initialized : No\n' else: s += 'Algorithm initialized : Yes\n' s += '\t initial threshold : %s\n' % self.init_threshold s += '\t number of peaks : %s\n' % self.Nt s += '\t upper extreme quantile : %s\n' % self.extreme_quantile['up'] s += '\t lower extreme quantile : %s\n' % self.extreme_quantile['down'] return s def fit(self,train_data, verbose = True): """ Run the calibration (initialization) step Parameters ---------- train_data : list, numpy.array or pandas.Series initial batch to calibrate the algorithm () verbose : bool (default = True) If True, gives details about the batch initialization """ if isinstance(train_data,list): init_data = np.array(train_data) elif isinstance(train_data, np.ndarray): init_data = train_data elif isinstance(train_data, pd.Series): init_data = train_data.values else: raise TypeError("This train_data format (%s) is not supported." % type(train_data)) n_init = init_data.size S = np.sort(init_data) # we sort X to get the empirical quantile self.init_threshold['up'] = S[int(0.98*n_init)] # t is fixed for the whole algorithm self.init_threshold['down'] = S[int(0.02*n_init)] # t is fixed for the whole algorithm # initial peaks self.history_peaks['up'] = init_data[init_data>self.init_threshold['up']] self.history_peaks['down'] = init_data[init_data self.history_peaks[side][min_index]: weight = 0.99 if self.history_peaks[side][min_index] > 0 else 1.01 self.init_threshold[side] = weight * self.history_peaks[side][min_index] self.history_peaks[side][min_index] = value init_data = self.history_peaks[side] self.history_peaks[side] = init_data[init_data>self.init_threshold[side]] self.peaks[side] = self.history_peaks[side] - self.init_threshold[side] elif side == 'down': max_index = np.argmax(self.history_peaks[side], axis=0) if value < self.history_peaks[side][max_index]: weight = 1.01 if self.history_peaks[side][max_index] > 0 else 0.99 self.init_threshold[side] = weight * self.history_peaks[side][max_index] self.history_peaks[side][max_index] = value init_data = self.history_peaks[side] self.history_peaks[side] = init_data[init_dataself.extreme_quantile['up'] : # if we want to alarm, we put it in the alarm list if with_alarm: alarm.append(i) # otherwise we add it in the peaks else: self._update_one_side('up', stream_data[i]) #self.peaks['up'] = np.append(self.peaks['up'],stream_data[i]-self.init_threshold['up']) #self.Nt['up'] += 1 #self.n += 1 # and we update the thresholds #g,s,l = self._MOM('up') #self.extreme_quantile['up'] = self._quantile('up',g,s) # case where the value exceeds the initial threshold but not the alarm ones elif stream_data[i]>self.init_threshold['up']: # we add it in the peaks self._update_one_side('up', stream_data[i]) #self.peaks['up'] = np.append(self.peaks['up'],stream_data[i]-self.init_threshold['up']) #self.Nt['up'] += 1 #self.n += 1 # and we update the thresholds #g,s,l = self._MOM('up') #self.extreme_quantile['up'] = self._quantile('up',g,s) elif stream_data[i]