异常值是指样本中的个别值明显偏离其余的观测值。异常值的存在会对数据分析、建模产生干扰,因此需要对数据集进行异常值检测并进行异常值删除或修正,以便后续更好地进行数据分析和挖掘。对于异常值检测,有描述性统计、3σ原则方法、箱线图、基于聚类的方法等,而3σ原则是最常使用的异常值检测方法之一。在3σ原则下,一般认为数据的取值99.7%的概率集中在(μ-3σ,μ+3σ)区间内(μ为平均值,σ为标准差),超出这个范围的可能性仅占0.3%,属于极个别的小概率事件,因此将超出(μ-3σ,μ+3σ)范围的值认为是异常值。3σ原则要求数据服从正态或近似正态分布,且样本数据大于10。若数据不服从正态分布,也可以用远离平均值的多少倍标准差来描述。现利用Python实现基于3σ原则的异常值检测,并对从网络上查询的中国1952年-2018年中国GDP(亿元)、GDP指数((上年=100)) 人均GDP(元)、GDP增长率(%)、第二产业增加值(亿元) 、第三产业增加值(亿元)等24个指标数据进行异常值检测。
在这里插入图片描述

#基于3sigma的异常值检测
#-*- coding: utf-8 -*-
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt #导入绘图库

##3sigma原则
def threesigma(data,n):
    '''
    data:表示时间序列,包括时间和数值两列;
    n:表示几倍的标准差
    '''
    data_x = data.ix[:,0].tolist()  ##获取时间序列的时间
    #print (data_x)
    #print ("**********",j)
    mintime=data_x[0]    ##获取时间序列的起始年份
    maxtime=data_x[-1]   ##获取时间序列的结束年份
    
    data_y = data.ix[:,1].tolist()  ##获取时间序列数值
    ymean = np.mean(data_y)         ##求时间序列平均值
    ystd = np.std(data_y)           ##求时间序列标准差
    down = ymean - n * ystd        ##计算下界
    up = ymean + n * ystd          ##计算上界

    outlier = [] #将异常值保存
    outlier_x = []

    for i in range(0, len(data_y)):
        if (data_y[i] < down)|(data_y[i] > up):
            outlier.append(data_y[i])
            outlier_x.append(data_x[i])
        else:
            continue           
    return mintime,maxtime,outlier,outlier_x

#设置列表,用于记录结果
indicator=[]   ##指标
flag=[]        ##是否为异常值
outlier_data=[]  ##异常值
outlier_time=[]  ##出现异常值的对应时间
max_time=[]      ##时间序列的开始时间
min_time=[]      ##时间序列的结束时间
time_flag=[]     ##异常值是否为起始时间

#读取数据
data = pd.read_csv( 'data.csv', index_col = False,encoding='gb18030') #读取数据
#print(data.head())
col_name=data.columns.tolist()
#print (data.columns.tolist())

#设置参数
n = 3 # n*sigma
print ("******************:n=",n)

##依次检测每一个指标
for j in col_name[1:]:
    indicator.append(j)
    temp_data=data.ix[:,['时间',j]]
    #print ("删除空值前",len(temp_data))
    temp_data=temp_data.dropna()     #删除空值
    #print("删除空值后",len(temp_data))
    temp_data=temp_data.sort_values(by = ['时间'],axis = 0,ascending = True)    #按时间排序
    #print (temp_data)
    
    mintime,maxtime,outlier,outlier_x=threesigma(temp_data,n)  #调用3sigma函数
    min_time.append(mintime)     ##获取时间序列的起始年份
    max_time.append(maxtime)    ##获取时间序列的结束年份
    
    outlier_data.append(outlier)
    outlier_time.append(outlier_x)
    
    if (maxtime in outlier_x) or (mintime in outlier_x):
        time_flag.append('异常值为起始端')
        #print (time_flag)
    else:
        time_flag.append("")
    
    if len(outlier)>0:
        flag.append('异常')
        print("***************************")
        print ("异常指标:",j)
        print('\n异常数据如下:',outlier,outlier_x)
        ##画出存在异常值的时间序列的折线图,异常值处特殊标注
        plt.figure(figsize=(12,5))
        plt.plot(temp_data.ix[:,0], temp_data.ix[:,1])
        plt.plot(outlier_x, outlier, 'ro')
        for j in range(len(outlier)):
            plt.annotate(outlier[j], xy=(outlier_x[j], outlier[j]), xytext=(outlier_x[j],outlier[j]))
        plt.show()
        
    else:
        flag.append('正常')
            
result=pd.DataFrame()
result['指标']=indicator
result['开始时间']=min_time
result['结束时间']=max_time
result['是否异常']=flag
result['异常数值']=outlier_data
result['异常时间']=outlier_time
result['异常值所处时间标识']=time_flag
result

在这里插入图片描述
对于抛出来的异常值要重点关注,并且结合实际的业务情况来判断数据是否真的存在异常,以及何种原因导致的异常,进而确定异常值的处理方式。

  • GDP指数(上年=100):1961年我国刚刚经过三年自然灾害,查询多方面资料证实数据没有问题,不作处理。
    在这里插入图片描述
  • 第三产业占GDP比重(%):查询多方面资料证实数据确实存在问题,进行修正。
    在这里插入图片描述
  • 第三产业增加值(亿元):异常值出现在时间序列的起始端,通过折线图和多方面资料证实数据没有问题,不作处理。
    在这里插入图片描述
    附数据和源程序。链接:https://pan.baidu.com/s/1RLC6769nJLKUG3woWsv17w 提取码:rmjc

ps:初衷是通过撰写博文记录自己所学所用,实现知识的梳理与积累;将其分享,希望能够帮到面临同样困惑的小伙伴儿。如发现博文中存在问题,欢迎随时交流~~

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐