除了缺失值外,异常值也是数据中常有的噪音,但并非异常值都需要被处理,异常值出现的原因有很多,结合实际业务,他们往往可以被分为“真异常”和“假异常”。
有时特定业务动作的变化会引发“真异常”,此时异常值反应的是真实情况,如果直接处理掉,可能就错失了信息。与缺失值处理的过程类似,在进行异常分析之前,需要进行数据读取与简单描述统计。
笔记大纲:
~~·数据读取
~~·数据概览
~~·异常值识别
~~·异常值处理
1、数据读取
导入excel数据,这里的数据来源于“猴子聊人物”所发布的数据资料。
百度网盘:https://pan.baidu.com/s/14Ulh_S1JqZUAD02-M_3Zlw
提取码:wg18
>>> import pandas as pd
>>> import numpy as np
>>> data = pd.read_excel('朝阳医院2018年销售数据.xlsx', header=0, sheetname=0)
# header表示读取第一行作为列名,sheetname为读取sheet1,你也可以直接用sheet的名字
2、数据概览
在进行数据读取之后,我们需要查看数据的基本结构,包含但不限于行列数(名)、数据类型、数据分布等。
# 查看行列数
>>> data.shape
(6578, 7)
# 查看前10行数据
>>> data.head(10)
购药时间 社保卡号 商品编码 商品名称 销售数量 应收金额 实收金额
0 2018-01-01 星期五 1.616528e+06 236701.0 强力VC银翘片 6.0 82.8 69.00
1 2018-01-02 星期六 1.616528e+06 236701.0 清热解毒口服液 1.0 28.0 24.64
2 2018-01-06 星期三 1.260283e+07 236701.0 感康 2.0 16.8 15.00
3 2018-01-11 星期一 1.007034e+10 236701.0 三九感冒灵 1.0 28.0 28.00
4 2018-01-15 星期五 1.015543e+08 236701.0 三九感冒灵 8.0 224.0 208.00
5 2018-01-20 星期三 1.338953e+07 236701.0 三九感冒灵 1.0 28.0 28.00
6 2018-01-31 星期日 1.014649e+08 236701.0 三九感冒灵 2.0 56.0 56.00
7 2018-02-17 星期三 1.117733e+07 236701.0 三九感冒灵 5.0 149.0 131.12
8 2018-02-22 星期一 1.006569e+10 236701.0 三九感冒灵 1.0 29.8 26.22
9 2018-02-24 星期三 1.338953e+07 236701.0 三九感冒灵 4.0 119.2 104.89
# 数值型数据的分布情况
>>> data.describe()
社保卡号 商品编码 销售数量 应收金额 实收金额
count 6.575000e+03 6.575000e+03 6575.000000 6575.000000 6575.000000
mean 6.092179e+09 1.015458e+06 2.385095 50.478935 46.321582
std 4.889081e+09 5.126518e+05 2.373702 87.607883 80.987682
min 1.616528e+06 2.367010e+05 -10.000000 -374.000000 -374.000000
25% 1.014234e+08 8.614560e+05 1.000000 14.000000 12.320000
50% 1.001650e+10 8.615070e+05 2.000000 28.000000 26.600000
75% 1.004882e+10 8.689265e+05 2.000000 59.600000 53.000000
max 1.283612e+10 2.367012e+06 50.000000 2950.000000 2650.000000
销售数量、应收金额和实收金额存在负数,且金额字段离散程度较大。
3、异常值识别
异常值的测量标准有很多,比较常见的是描述性统计法、三西格玛法、箱型图等:
①描述性统计:比如在之前的数据概览步骤中,基于描述性统计方法,发现销售数量等字段存在负数,这与基本认知是不符的。
>>> neg_list = ['销售数量', '应收金额', '实收金额']
>>> for item in neg_list:
... neg_item = data[item]<0
... print(item + '小于0的有' + str(neg_item.sum()) + '个')
...
销售数量小于0的有16个
应收金额小于0的有16个
实收金额小于0的有16个
# 此处将小于0的记录删除
>>> for item in neg_list:
... for i in range(0, len(data)):
... if data[item][i]<0:
... data = data.drop(i)
... neg_item = data[item]<0
... print(item + '小于0的有' + str(neg_item.sum()) + '个')
...
销售数量小于0的有0个
应收金额小于0的有0个
实收金额小于0的有0个
②三西格玛:当数据服从正态分布时,99%的数值应该位于距离均值3个标准差之内的距离,P(|x−μ|>3σ)≤0.003,当数值超出这个距离,可以认为它是异常值。
>>> for item in neg_list:
... data[item + '_zscore'] = (data[item] - data[item].mean())/data[item].std()
... z_abnormal = abs(data[item + '_zscore'])>3
... print(item + '中有' + str(z_abnormal.sum()) + '个异常值')
...
销售数量中有190个异常值
应收金额中有98个异常值
实收金额中有106个异常值
③箱型图:IQR(差值) = U(上四分位数) - L(下四分位数)
上界 = U + 1.5IQR
下界 = L-1.5IQR
>>> for item in neg_list:
... iqr = data[item].quantile(0.75) - data[item].quantile(0.25)
... q_abnormal_L = data[item] < data[item].quantile(0.25) - 1.5 * iqr
... q_abnormal_U = data[item] > data[item].quantile(0.75) + 1.5 * iqr
... print(item + '中有' + str(q_abnormal_L.sum() + q_abnormal_U.sum()) + '个异常值')
...
销售数量中有974个异常值
应收金额中有513个异常值
实收金额中有537个异常值
4、异常值处理
对于异常值,可以删除、可以不处理,也可以将其当做缺失值,采用缺失值的处理办法。
对于离散程度过大的字段,也可以采取对数转化、分类数据转化等方法,减轻或消除异常值带来的影响,但同时这也意味着可能损失部分数据信息。