Z-score与修正的Z-score评分识别异常

z-score

对于一维数据,最常用评价异常的方法就是z-score方法,它的定义如下:
z i = x i − u δ z_i=\frac{x_i-u}{\delta} zi=δxiu
其中, x i x_i xi是样本值, u u u是均值, δ \delta δ是样本标准差。因此 z i z_i zi就是衡量该样本点距离样本均值有多少个标准差,用来表示各原始数据在数据组中的相对位置。
另外,若样本服从正态分布,它可以表示该数据以下或以上数据的比例,即具有了概率的意义;

比如样本服从正态分布,如果设置z-score的阈值为-2(低于的为异常,如果为正,高于的为异常),那么距离均值2个标准差范围的数据量有95%(正态分布的性质),则有2.5%的数据会被标记为异常)

修正的z-score

Motivation:因为均值与标准差对于异常值都很敏感,有时候得到的z-score不是可靠的。
修正的z-score的定义如下:
z i = x i − X ~ M A D z_i=\frac{x_i-\widetilde{X}}{MAD} zi=MADxiX
其中, x i x_i xi是样本值, X ~ \widetilde{X} X 是整个样本的中位数,MAD(Median Absolute deviation)是中位数绝对偏差,定义如下:
M A D = m e d i a n ∣ x i − X ~ ∣ MAD=median{|x_i-\widetilde{X}|} MAD=medianxiX
标准差的定义是与均值距离的平方和,对异常值更敏感,比如一个较大的样本值在样本内,则会直接影响到样本的标准差,而MAD不会,它具有更好的鲁棒性。
MAD的用法类似于样本标准差,为了使用MAD作为一致估计量来估计标准差,我们可以有(具体推导见MAD):
δ ^ = k ∗ M A D \hat{\delta}=k*MAD δ^=kMAD
其中, k k k只是一个常量因子,与样本分布有关,如果样本服从正态分布, k k k=1.4826.

代码实现

例子:这里有一个数据集,包含2012年康涅狄格州学区SAT的学生参与率,我们的任务是找到低参与率的学校,可以看做一个异常检测任务。由于我们要找低参与率的学校,所以阈值是一个负数,这里我们设为-2。
ps: 对于较大的数据集,较大的绝对值 z z z(通常为 z = 3 z=3 z=3)通常用作阈值。因为我们的数据集很小, z z z的大值可能导致没有数据被标记为异常。另外,我们在选择z$时比较保守,因为我们想帮助尽可能多的学校。

z-score

import scipy.stats as ss
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import pandas as pd
import random
#用于展示检测结果
def plot_anomaly(score_data, threshold):
    # Mask to plot values above and below threshold in different colors
    score_data = score_data.copy().sort_values(ascending=False).values
    ranks = np.linspace(1, len(score_data), len(score_data))
    mask_outlier = (score_data < threshold)
    plt.figure(dpi=150)
    plt.plot(ranks[~mask_outlier], score_data[~mask_outlier],'o', color='b',label='OK schools')
    plt.plot(ranks[mask_outlier], score_data[mask_outlier],'o', color='r', label='anomalies')
    plt.axhline(threshold,color='r',label='threshold', alpha=0.5)
    plt.legend(loc = 'lower left')
    plt.title('Z-score vs. school district', fontweight='bold')
    plt.xlabel('Ranked School district')
    plt.ylabel('Z-score')
    plt.show()
data = pd.read_csv('SAT_CT_District_Participation_2012.csv') 
zscore_rate = ss.zscore(ct_test['Participation Rate'], ddof=0)#ddof是标准差计算中的自由度修正,默认为0,即标准差分母是n,而不是n-1
data=data.assign(zscore=zscore_rate)
plot_anomaly(data['zscore'], -2)
anomalies = data[(data['zscore'] < -2)]
anomalies

效果:
在这里插入图片描述
异常值

修正的z-score

def plot_anomaly(score_data, threshold):
    # Mask to plot values above and below threshold in different colors
    score_data = score_data.copy().sort_values(ascending=False).values
    ranks = np.linspace(1, len(score_data), len(score_data))
    mask_outlier = (score_data < threshold)
    plt.figure(dpi=150)
    plt.plot(ranks[~mask_outlier], score_data[~mask_outlier],'o', color='b',label='OK schools')
    plt.plot(ranks[mask_outlier], score_data[mask_outlier],'o', color='r', label='anomalies')
    plt.axhline(threshold,color='r',label='threshold', alpha=0.5)
    plt.legend(loc = 'lower left')
    plt.title('Z-score vs. school district', fontweight='bold')
    plt.xlabel('Ranked School district')
    plt.ylabel('Z-score')
    plt.show()
#修正z-score方法
def modify_zscore(data,k=1.4826):
    data_median=np.median(data)
    dev_from_med=np.array(data)-data_median
    MAD=np.median(np.abs(dev_from_med))
    mod_zscore=dev_from_med/(k*MAD)#使用的是标准差的一致性估计
    return mod_zscore,MAD
    
data = pd.read_csv('SAT_CT_District_Participation_2012.csv') 
mod_zscore,MAD=modified_zscore(data['Participation Rate'])
data = data.assign(mod_zscore=mod_zscore)
plot_anomaly(data['mod_zscore'],-2)
anomalies = data[(data['zscore'] < -2)]
anomalies

效果:
在这里插入图片描述
异常点

效果比较:
我们可以发现,z-score方法识别出的异常点一共4项,而修正后的z-score方法识别结果除此之外,还识别出第55项0.47为低参与率。另外,通过计算,可以发现 k ∗ M A D k*MAD kMAD比样本标准差更小,也体现MAD统计量受异常点影响较小,具有更好的鲁棒性。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值