本文主要整理了几种常用的异常值检测方法。笔者对于非常学术的理论知识介绍无能为力, 具体理论知识可以看最后的参考资料中的内容。
1. 可视化方法
一般可以采用可视化方法进行异常值的检测,常用工具包括箱线图,直方图、散点图等。以鸢尾花数据集为例进行说明,具体代码如下:
import pandas as pd
import numpy as np
from sklearn.datasets import load_iris
import seaborn as sns
from matplotlib import pyplot as plt
from scipy import stats
X,y=load_iris().data,load_iris().target
X=pd.DataFrame(X,columns=load_iris().feature_names)
y=pd.Series(y)
#使用箱线图进行异常值检测
fig=plt.figure(figsize=(10,8),dpi=75)
for i in range(len(X.columns)):
plt.subplot(2,2,i+1)
sns.boxplot(X.iloc[:,i],orient='v',width=0.5)
plt.ylabel(X.columns[i])
plt.show()
#使用散点图+直方图对两两特征进行异常值检测
pd.plotting.scatter_matrix(X,alpha = 0.5,
figsize =(10,8) ,grid = False,
diagonal = 'hist',marker = 'o',
range_padding = 0.01)
plt.show()
箱线图可以发现单个维度数据中的异常值,而散点图可以样本在任意连个特征组合上的异常值。以上代码的画图结果如下:
2. 3 σ \sigma σ法则
假定某个维度的数据服从正态分布,3 σ \sigma σ法则将距离平均值3倍标准差的数据当作异常值。(Z-score方法类似)
#先用Q-Q图依次判断单个维度是否服从正态分布
fig=plt.figure(figsize=(10,8),dpi=75)
for i in range(len(X.columns)):
plt.subplot(2,2,i+1)
stats.probplot(X.iloc[:,i],plot=plt)
plt.ylabel(X.columns[i])
plt.show()
#然后再前2个特征上使用该法则
for col in X.columns[:2]:
print("正进行异常值检测的字段:",col)
col_mean=X[col].mean()
col_std=X[col].std()
#异常值数目
tmp=((X[col]-col_mean)/col_std).abs()
outlier_num=(tmp>3).sum()
#异常值索引
if outlier_num>0:
print("{0}字段共有异常值{1}个,其索引值如下:".format(col,outlier_num))
print(list(X[tmp>3].index))
else:
print("{}字段没有异常值。".format(col))
Q-Q图是指数据的分位数和正态分布的分位数对比参照的图,如果数据符合正态分布,则所有的点都会落在直线上。以上4个字段的Q-Q图结果如下:
3. 孤立森林(Isolation Forest)
孤立森林是由周志华教授等人提出的适用于连续数据的异常值检测算法。该方法不需要定义数学模型也不需要有标记的训练。孤立森林来源于随机森林的思想,其内部也由大量的二叉树组成,其每一棵二叉树的构建是一个完全随机的过程,如下:
- 假设数据集中有 N N N个样本,采用无放回抽样从中抽取 n n n个样本,作为该二叉树的训练样本.
- 在样本中,随机选出一个特征,并在这个特征的取值范围内随机选一个值,对样本进行二叉划分,将样本中小于该值的划分到节点的左边,大于等于该值的划分到节点的右边。由此得到一个分裂条件和左右两边的数据集。
- 分别对上述两个子集重复上述过程,直到达到树的限定深度或子集中只剩下一条记录。
一般异常数据较少且与正常数据的特征值差别较大,所以离根节点较近的点是异常点的概率较大。孤立森林算法将多棵二叉树的结果进行整合,以所有树分裂的平均深度作为最终的输出深度。sklearn中提供了该算法的包,具体实现如下:
from sklearn.ensemble import IsolationForest
clf=IsolationForest(max_samples=100,random_state=0,
contamination=0.01)
#contamination:异常数据所占比例。
clf.fit(X)
y_pred_train=clf.predict(X)
#y_pred_train中1表示正常数据,-1为异常数据
fig=plt.figure(figsize=(10,8),dpi=75)
k=1
for i in range(len(X.columns)):
for j in range(i+1,len(X.columns)):
plt.subplot(2,3,k)
plt.scatter(X.iloc[:,i],X.iloc[:,j],c=y_pred_train)
k=k+1
plt.show()
鸢尾花数据集结果:
4.One Class SVM
One Class SVM是一种单分类学习算法。其思路就是寻找一个超平面将样本中的正例圈出来,预测就是用这个超平面做决策,在圈内的样本就认为是正样本。所以使用One Class SVM方法作异常值检测的时候要确保训练数据中不能包含异常点。
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.font_manager
from sklearn import svm
xx, yy = np.meshgrid(np.linspace(-5, 5, 500), np.linspace(-5, 5, 500))
X = 0.3 * np.random.randn(50, 2)
X_train = np.r_[X + 2, X - 2]
X_test = np.r_[X + 2, X-2]
X_outliers = np.random.uniform(low=0.1, high=4, size=(20, 2))
clf = svm.OneClassSVM(nu=0.1, kernel='rbf', gamma=0.1)
clf.fit(X_train)
y_pred_train = clf.predict(X_train)
y_pred_test = clf.predict(X_test)
y_pred_outliers = clf.predict(X_outliers)
Z = clf.decision_function(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
plt.title("Novelty Detection")
plt.contourf(xx, yy, Z, levels=np.linspace(Z.min(), 0, 7), cmap=plt.cm.PuBu)
a = plt.contour(xx, yy, Z, levels=[0, Z.max()], colors='palevioletred')
s =40
X_plot=np.r_[X_train,X_test,X_outliers]
y_plot=np.r_[y_pred_train,y_pred_test,y_pred_outliers]
X_plot_in=X_plot[y_plot==1]
X_plot_out=X_plot[y_plot==-1]
plt.scatter(X_plot_in[:,0],X_plot_in[:,1],c='blueviolet',
s=s,label='inline')
plt.scatter(X_plot_out[:,0],X_plot_out[:,1],c='white',
s=s,label='outlier')
plt.legend()
plt.show()