5.4.1 查看异常情况

异常值的检测方法有基于统计的方法、基于聚类的方法,以及一些专门检测异常值的方法等。异常值检测的可视化常使用散点图和箱型图(详见5.2节)。下面对这些方法进行详细介绍。

1.异常值检测

异常值统计检测方法可直接使用pandas.describe。从图5-4中可以明显看出fare票价字段有0值,而票价为0不太符合基本的认知,实际工作中需要核实具体原因。

当数据服从正态分布时可使用三西格玛检测异常值:99%的数值应该位于均值3个标准差之内,即P(|x-μ|>3σ)≤0.003,如果数值超出这个范围,可以认为它是异常值。在判断异常值之前需要计算z分数(Z-score),它也叫标准分数(Standard Score)。标准分数是一个观测或数据点的值高于被观测值或测量值的平均值的标准偏差的符号数,在平均数之上的分数会得到一个正的标准分数,在平均数之下的分数会得到一个负的标准分数,通过标准分数可以看出数据点在分布中的相对位置。三西格玛检测异常值代码如下:


#只选取上述泰坦尼克数据集的3个字段作为案例展示
columns=['pclass','age','fare']
for var in columns:
    titanic_df[var + '_zscore'] = (titanic_df[var] - titanic_df[var].mean()) / titanic_df[var].std()
    z_normal = abs(titanic_df[var + '_zscore']) > 3
    print(var + '中有' + str(z_normal.sum()) + '个异常值')

输出结果为:


pclass中有0个异常值
age中有2个异常值
fare中有20个异常值

箱线图中,上下界之外的值可视为异常值。IQR(差值)=U(上四分位数)-L(下四分位数),上界=U+1.5IQR,下界=L-1.5IQR。这也是Tukey异常值检测方法,其代码如下:


for var in columns:
    iqr = titanic_df[var].quantile(0.75) - titanic_df[var].quantile(0.25)
    q_abnormal_L = titanic_df[var] < titanic_df[var].quantile(0.25) - 1.5 * iqr
    q_abnormal_U = titanic_df[var] > titanic_df[var].quantile(0.75) + 1.5 * iqr
    print(var + '中有' + str(q_abnormal_L.sum() + q_abnormal_U.sum()) + '个异常值')

输出结果为:


pclass中有0个异常值
age中有11个异常值
fare中有116个异常值

2.异常点检测

异常点检测与异常值检测主要的区别在于:异常值针对单一变量,而异常点则是针对多变量。异常点检测(又称离群点检测)是通过多种检测方法找出数据集中与大多数数据有明显差异的数据点,这些数据点被称为异常点或者离群点。异常点检测在日常生活有着广泛的应用,比如风控领域的信用卡欺诈、网络通信领域的异常信息流检测等。

常见的异常点检测算法有基于统计的概率模型、聚类算法、专门的异常点检测算法等。下面以聚类算法为例介绍异常点的检测。

异常点检测和聚类分析是两项高度相似的任务,但目的不同。聚类分析发现数据集中的模式,而异常点检测则试图捕捉那些显著偏离多数模式的异常情况。基于聚类的异常点检测是用聚类方式将数据划分为不同的簇,计算簇内每个点与簇中心的相对距离(相对距离等于点到簇中心的距离除以这个簇所有点到簇中心距离的中位数),相对距离较大的点被视为异常点。注意,距离度量使用的是所有点到簇中心距离的中位数,而不是平均值,因为异常值对中位数的影响很小,但是对均值的影响较大。


#导入sklearn.cluster.KMeans
from sklearn.cluster import KMeans

#修改Matplotlib配置参数支持中文显示
plt.rcParams['font.family']='SimHei'

#聚类的类别
k = 3 
#异常点阈值
threshold = 3 
#读取已经填充过空值的数据
data = titanic[['pclass','age','fare']].copy() 
#数据标准化
data_zs = 1.0*(data - data.mean())/data.std() 

#使用聚类模型聚类
model = KMeans(n_clusters = 3, max_iter = 500) 
model.fit(data_zs) 
#标准化数据及其类别
r = pd.concat([data_zs, pd.Series(model.labels_, index = data.index)], axis = 1)  
r.columns = list(data.columns) + ['聚类类别'] 

#计算相对距离
norm = []
for i in range(k): 
    norm_tmp = r[['pclass','age','fare']][r['聚类类别'] == i]-model.cluster_centers_[i]
    norm_tmp = norm_tmp.apply(np.linalg.norm, axis = 1) 
    norm.append(norm_tmp/norm_tmp.median()) 
norm = pd.concat(norm)

#正常点,相对距离小于或等于异常点阈值
norm[norm <= threshold].plot(style = 'go')
#异常点,相对距离大于异常点阈值
discrete_points = norm[norm > threshold] 
discrete_points.plot(style = 'ro')
plt.xlabel('编号')
plt.ylabel('相对距离')
plt.show()

输出如图5-22所示。

图5-22 异常点聚类检测

获取大于异常点阈值的索引,方便后续处理:


discrete_points = norm[norm>threshold]
discrete_points.index