Python 之 异常检测/异常点搜索

 

异常点搜索

继续话题:对离群值的处理。

已经开发了一套Python工具,用于检测数据中的排放和搜索异常点--Python异常点检测(PyOD)

PyOD是一个全面的、可扩展的Python工具包,用于检测多维数据中的外部对象(排放)。

PyOD的优势。

1. 统一的API,详细的文档和不同算法的交互式例子。
2. 高级模型,包括神经网络/深度训练和发射集合。
3.使用JIT优化性能,并在可能的情况下使用numba进行并行。
和joblib。
4. 同时兼容Python 2和Python 3。
协议:线性模型、基于近似的、概率算法、离群组、神经网络。

算法

1.导入所需模块

import matplotlib
import matplotlib.pyplot as plt
%matplotlib inline

import numpy as np
import pandas as pd
from pyod.utils.data import generate_data, get_outliers_inliers

from scipy import stats

import itertools

itertools模块实现了创建迭代器以有效组织循环的函数。

2.使用函数generate_data创建排放量为10%的数据

contamination = 0.1 # percentage of outliers
n_train = 500       # number of training points
n_test = 500        # number of testing points
n_features = 2      # number of features
X_train, y_train, X_test, y_test = generate_data(n_train=n_train, 
                                                 n_test=n_test, 
                                                 n_features=n_features, 
                                                 contamination=contamination)

你可以分别计算离群值(不排放)和离群值(排放)的数量。

# store outliers and inliers in different numpy arrays
x_outliers, x_inliers = get_outliers_inliers(X_train, y_train)

n_inliers = len(x_inliers)
n_outliers = len(x_outliers)

print('inliers: ', n_inliers, ' outliers: ', n_outliers)

3.在散点图上显示数据。

# Plot
plt.scatter(X_train[:, 0], X_train[:, 1], alpha=0.8)
plt.title('Scatter plot')
plt.xlabel('x')
plt.ylabel('y')
plt.show()

图中清晰地显示了云--一组离群者(不是排放)和云附近的少量排放点。

我们使用算法k最近的邻居(k-NN)来识别异常。

k-NN算法是一种非参数化的方法,可以识别出最接近的教程例子𝑘。任何孤立的数据点都有可能被归类为排放。

以下代码行为k-NN模型提供训练,并将模型存储为clf。

# train kNN detector
from pyod.models.knn import KNN
clf_name = 'KNN'
clf = KNN()
clf.fit(X_train)
# If you want to see the predictions of the training data, you can use this way:
y_train_scores = clf.decision_scores_

让我们在图上显示教程数据集的收到的 "分数"(估计每个数据点有多少是离群值或不是)。

plt.plot(y_train_scores, linestyle='--', color='blue', linewidth=3)
plt.plot(y_train, linestyle='-', color='red', linewidth=3)
plt.legend(['estimated scores on train data', 'set for train data'])
plt.show();

通过训练好的k-NN模型,你可以预测一个点是否是测试数据集的离群值。decision_functions函数根据模型预测生成对异常情况的估计。

# Now we have the trained K-NN model, let's apply to the test data to get the predictions
y_test_pred = clf.predict(X_test) # outlier labels (0 or 1)

让我们绘制数据集的获得的估计值。

plt.plot(y_test_pred, linestyle='--', color='blue', linewidth=3)
plt.plot(y_test, linestyle='-', color='red', linewidth=3)
plt.legend(['estimated value on test data', 'set for test data'])
plt.show();

我们将计算模型所估计的排放量百分比。

 

# 因为是'0'和'1',我们可以进行一个计数统计。异常的数量大概是百分之十,我们之前已经产生了。
unique, counts = np.unique(y_test_pred, return_counts=True)
result = dict(zip(unique, counts))
print(result)
print(100 * result[1] / (result[0] + result[1]), '% of anomalies')

模型估计的排放百分比为10%(我们原来设定的排放百分比)。

让我们绘制测试样本的 "分数"(对 "异常 "观测值的估计)。

# And you can generate the anomaly score using clf.decision_function:
y_test_scores = clf.decision_function(X_test)
plt.plot(y_test_scores, linestyle='--', color='blue', linewidth=3)
plt.plot(y_test, linestyle='-', color='red', linewidth=3)
plt.legend(['estimated scores on test data', 'set for test data'])
plt.show();

你可以使用mlxtend包将k-NN算法的结果可视化。

Mlxtend(机器学习扩展)是一个Python工具库,用于日常数据处理任务。

from mlxtend.plotting import plot_decision_regions

# Plotting decision region
plot_decision_regions(X_train, y_train.astype('int'), clf=clf, legend=2)  # y only as int variables
# Adding axes annotations
plt.xlabel('X')
plt.ylabel('Y')
plt.title('Knn decision regions results')
plt.show();

如何界定 "异常评估"?回想一下,k-NN模型使用欧几里得距离来衡量距离。排放是相邻点的远距离点,所以排放估计是由距离值决定的。我们的任务是找到高离群值的点。我们可以用直方图来寻找这些点。

plt.hist(y_test_scores, bins='auto')  # arguments are passed to np.histogram
plt.title("Histogram with 'auto' bins")
plt.show();

此外,您还可以计算正常和异常聚类的统计。

在我们的案例中,我们有两个特征(标记为 "0 "和 "1")。第1群组的平均异常估计值远高于第0群组,汇总统计也显示出这两个群组之间的显著差异。因此,我们认为群组1中的数据点可能存在异常,值得进一步分析。

# Let's see how many '0's and '1's.
df_test = pd.DataFrame(X_test)
df_test['score'] = y_test_scores
df_test['cluster'] = np.where(df_test['score'] < 1, 0, 1)
df_test['cluster'].value_counts()

# Now let's show the summary statistics:
df_test.groupby('score').mean()
df_test.groupby('cluster').mean()

4.在pyod的帮助下,可以同时实现多个模型来搜索排放。

让我们在分类器字典中设置所选模型。

from pyod.models.abod import ABOD

classifiers = {
     'Angle-based Outlier Detector (ABOD)' : ABOD(contamination=contamination),
     'K Nearest Neighbors (KNN)' :  KNN(contamination=contamination)
}

让我们创建一个网格显示可视化结果。

# create a meshgrid 
xx, yy = np.meshgrid(np.linspace(-10, 10, 200), np.linspace(-10, 10, 200))

以下代码行是按顺序保护所选模型,以及展示可视化所获得的结果。

# set the figure size
plt.figure(figsize=(10, 10))

for i, (clf_name, clf) in enumerate(classifiers.items()) :
    # fit the dataset to the model
    clf.fit(X_train)

    # predict raw anomaly score
    scores_pred = clf.decision_function(X_train)*-1

    # prediction of a datapoint category outlier or inlier
    y_pred = clf.predict(X_train)

    # no of errors in prediction
    n_errors = (y_pred != y_train).sum()
    print('No of Errors : ', clf_name, n_errors)

    # rest of the code is to create the visualization

    # threshold value to consider a datapoint inlier or outlier
    threshold = stats.scoreatpercentile(scores_pred, 100 * contamination)

    # decision function calculates the raw anomaly score for every point
    Z = clf.decision_function(np.c_[xx.ravel(), yy.ravel()]) * -1
    Z = Z.reshape(xx.shape)

    subplot = plt.subplot(1, 2, i + 1)

    # fill blue colormap from minimum anomaly score to threshold value
    subplot.contourf(xx, yy, Z, levels = np.linspace(Z.min(), threshold, 10),cmap=plt.cm.Blues_r)

    # draw red contour line where anomaly score is equal to threshold
    a = subplot.contour(xx, yy, Z, levels=[threshold], linewidths=2, colors='red')

    # fill orange contour lines where range of anomaly score is from threshold to maximum anomaly score
    subplot.contourf(xx, yy, Z, levels=[threshold, Z.max()], colors='orange')

    # scatter plot of inliers with white dots
    b = subplot.scatter(X_train[:-n_outliers, 0], X_train[:-n_outliers, 1], c='white', s=20, edgecolor='k') 
    # scatter plot of outliers with black dots
    c = subplot.scatter(X_train[-n_outliers:, 0], X_train[-n_outliers:, 1], c='black', s=20, edgecolor='k')
    subplot.axis('tight')

    subplot.legend(
        [a.collections[0], b, c],
        ['learned decision function', 'true inliers', 'true outliers'],
        prop=matplotlib.font_manager.FontProperties(size=10),
        loc='lower right')

    subplot.set_title(clf_name)
    subplot.set_xlim((-10, 10))
    subplot.set_ylim((-10, 10))
    
plt.show(); 

5.实验

在有些情况下,需要在一行中找到一个点,从这个点开始,所研究的过程会急剧改变其特征。

例如,在工业厂房中,需要根据测量仪器的读数提前(尽可能多的时间)预测紧急情况。

data_with_noise = pd.read_csv('data/samples/add_noise_1_7603.csv')
data_with_trend = pd.read_csv('data/samples/change_trend_10_7945.csv')
data_with_decreased_dispersion = pd.read_csv('data/samples/decrease_dispersion_190_8980.csv')
data_with_increased_dispersion = pd.read_csv('data/samples/increase_dispersion_190_9497.csv')
data_with_shifted_trend = pd.read_csv('data/samples/shift_trend_10_8709.csv')

data = pd.concat([data_with_noise, data_with_trend, data_with_shifted_trend, 
                  data_with_decreased_dispersion, data_with_increased_dispersion], axis=1)
data.columns = ['with_noise', 'change_trend', 'shift_trend', 'decrease_dispersion', 'increase_dispersion']
points = [7603, 7945, 8980, 9497, 8709]

智能化数据

data['with_noise'].plot();

增加趋势数据

data['change_trend'].plot();

变化趋势数据

data['shift_trend'].plot();

降低数据的分散度

data['decrease_dispersion'].plot();

增加数据的分散性

data['increase_dispersion'].plot();

让我们试着确保k-NN。

fig, axes = plt.subplots(nrows=data.shape[1], ncols=2, figsize=(10,10))

for idx, column in enumerate(data.columns):
    print(column, ' is processed')
    print('anomaly point is ', points[idx])
    clf_tsu_data = KNN()
    clf_tsu_data.fit(data[[column]])
    scores = clf_tsu_data.decision_scores_
    predictions = clf_tsu_data.predict(data[[column]])
    
    try:
        print('found anomaly point is ', list(predictions[int(data.shape[0] / 2):]).index(1) + int(data.shape[0] / 2))
    except:
        print('no anomaly point')
    
    axes[idx, 0].plot(scores, linestyle='--', color='blue', linewidth=3)
    axes[idx, 0].set_title(column + '_scores')
    
    axes[idx, 1].plot(predictions, linestyle='-', color='red', linewidth=3)
    axes[idx, 1].set_title(column + '_predictions')
    
    fig.tight_layout();

 

让我们来考虑各种数据转换和其中的结构搜索吧

我们使用另一个Python库的工具来检测异常--luminol

import luminol
from luminol.anomaly_detector import AnomalyDetector
from luminol.correlator import Correlator

6.取第一差值和滚动中位数

帮助寻找有噪声和变化趋势的数据的异常点。

diff_data = data.diff()
diff_data.dropna(inplace=True)

window = 100
diff_data = diff_data.rolling(window).median()
diff_data.dropna(inplace=True)
fig, axes = plt.subplots(nrows=data.shape[1], ncols=1, figsize=(5,10))
for idx, column in enumerate(diff_data.columns):
    axes[idx].plot(diff_data[column])
    axes[idx].set_title(column + '_diff_rolling_median')
    
    fig.tight_layout(); 

diff_data_dict = diff_data.to_dict()  

fig, axes = plt.subplots(nrows=data.shape[1], ncols=1, figsize=(5,10))

for idx, column in enumerate(diff_data.columns):
    detector = AnomalyDetector(diff_data_dict[column])
    anomalies = detector.get_anomalies()
    scores = detector.get_all_scores()
    
    axes[idx].plot(scores.values)
    axes[idx].set_title(column + '_scores')
    
    print(column, ' is processed')
    print('true anomaly point is ', points[idx])
    try:
        print('found anomaly point is ', next(x[0] for x in enumerate(scores.values) if x[1] > 0)) 
    except:
        print('no anomaly point')
    
    fig.tight_layout(); 

7.取第一个差值并滚动变异 rolling var

有助于找到噪声数据的异常点,提高分散度。

diff_data_var = data.diff()
diff_data_var.dropna(inplace=True)

window = 100
diff_data_var = diff_data_var.rolling(window).var()
diff_data_var.dropna(inplace=True)

fig, axes = plt.subplots(nrows=data.shape[1], ncols=1, figsize=(5,10))
for idx, column in enumerate(diff_data_var.columns):
    axes[idx].plot(diff_data_var[column])
    axes[idx].set_title(column + '_diff_rolling_var')
    
    fig.tight_layout(); 

 

diff_data_dict = diff_data_var.to_dict()  

fig, axes = plt.subplots(nrows=data.shape[1], ncols=1, figsize=(5,10))

for idx, column in enumerate(diff_data_var.columns):
    detector = AnomalyDetector(diff_data_dict[column])
    anomalies = detector.get_anomalies()
    scores = detector.get_all_scores()
    
    axes[idx].plot(scores.values)
    axes[idx].set_title(column + '_scores')
    
    print(column, ' is processed')
    print('true anomaly point is ', points[idx])
    try:
        print('found anomaly point is ', next(x[0] for x in enumerate(scores.values) if x[1] > 0)) 
    except:
        print('no anomaly point')
    
    fig.tight_layout(); 

8.取二次差值和滚动中位数

有助于发现分散度和移动趋势增加的数据的异常情况。

diff_data_ = data.diff()
diff_data_.dropna(inplace=True)

diff_data_ = diff_data_.diff()
diff_data_.dropna(inplace=True)

window = 100
diff_data_ = diff_data_var.rolling(window).var()
diff_data_.dropna(inplace=True)

ig, axes = plt.subplots(nrows=data.shape[1], ncols=1, figsize=(5,10))
for idx, column in enumerate(diff_data_.columns):
    axes[idx].plot(diff_data_[column])
    axes[idx].set_title(column + '_diff_diff')
    
    fig.tight_layout(); 

diff_data_dict = diff_data_.to_dict()  

fig, axes = plt.subplots(nrows=data.shape[1], ncols=1, figsize=(5,10))

for idx, column in enumerate(diff_data_.columns):
    detector = AnomalyDetector(diff_data_dict[column])
    anomalies = detector.get_anomalies()
    scores = detector.get_all_scores()
    
    axes[idx].plot(scores.values)
    axes[idx].set_title(column + '_scores')
    
    print(column, ' is processed')
    print('true anomaly point is ', points[idx])
    try:
        print('found anomaly point is ', next(x[0] for x in enumerate(scores.values) if x[1] > 0)) 
    except:
        print('no anomaly point')
    
    fig.tight_layout(); 

 

 

 

 

 

  • 3
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值