全文共6023字,预计学习时长20分钟或更长
离群值监测和处理是数据预处理中最重要的环节之一。机器学习算法注重数据点的范围和分布,而数据离群值掩盖训练进程,导致训练时间加长、模型准确性降低。离群值是与其余数据明显不同的数据点,这些数值分布于整体模型之外。而平均值、方差、相关量等统计度量均易受离群值的影响。
以下是一个简单的离群值举例,可以看到有一个数据点明显偏离整体模型:
离群值属性
数据集中出现离群值可能是以下原因之一:
1. 某个数据集中确实存在的极高值或极低值
2. 由于人类或机器的错误而产生
3. 由替换缺失值产生
有时候,离群值的存在可以反映很多信息,需要进一步研究。例如,离群值在交易管理相关的用例中就很重要,因为离群值在交易管理中可以识别潜在的诈骗交易。
本文将对以下识别和处理数据集中离群值的方法进行讨论。
离群值检验
· 极值分析
· Z分数法(Z-score)
· k均值聚类算法
· 可视化数据
离群值处理
· 平均值/中位数或随机缺失值处理
· 修整
· 顶端、底端编码和零编码
· 离散化
然而,以上的几种方法均不能证实哪个观测值是真正的离群值。目前关于离群值的构成还没有严格的数学定义,确定某个观测值是否为离群值基本上也是我们的主观行为,这很大程度上取决于某个特定的商业问题。因此,本文讨论的这些方法只是检测过程的开端,帮助大家识别数据中应处理为离群值的数据点。
检验离群值的方法
有很多方法可以确定数据中的离群值。本文将对以下方法进行讨论:
1. 极值分析
2. Z分数法(Z-score)
3. k均值聚类算法
有一点需要重申,这些方法不应机械地使用,而应该用这些方法深究数据本身。这些方法可以让你知道哪些数据点需要进一步研究。
数据集:
本文利用Kaggle中LendingClub Loan Dataset来演示本文的例子。
导入库
importpandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline
导入数据集
现在,从csv文件中输入年收入(annual_inc)列,识别离群值。
use_cols = ['annual_inc']
data = pd.read_csv('loan.csv', usecols=use_cols, nrows = 30000)
极值分析
离群值检验最基础的方法就是极值分析。该法关键在于确定变量基本分布的统计低点,找出极低值。
就高斯分布而言,离群值会高于或低于平均值3倍变量标准差。
若变量非正态分布(不是高斯分布),那么就可以采取计算分位数和四分位数间距范围的方法。
IQR (Inter quantiles range)= 75th quantile — 25th quantile
离群值出现在上下边界上限和下限中:
Upper Boundary = 75th quantile +(IQR * 1.5)
Lower Boundary = 25th quantile — (IQR * 1.5)
或为极值:
Upper Boundary = 75th quantile +(IQR * 3)
Lower Boundary = 25th quantile — (IQR * 3)
如若数据点在上限之上或下限之下,即可视为离群值。
代码:
首先,计算数据集四分位数间距范围:
IQR = data.annual_inc.quantile(0.75) - data.annual_inc.quantile(0.25)
依据上述提到的公式,利用四分位数间距(IQR),计算上界:
upper_limit = data.annual_inc.quantile(0.75) + (IQR * 1.5)
upper_limit_extreme = data.annual_inc.quantile(0.75) + (IQR * 3)
upper_limit, upper_limit_extreme
现在,来看看上限和极上限之上的数据点比率,例如:离群值。
total = np.float(data.shape[0])
print('Total borrowers:{}'.format(data.annual_inc.shape[0]/total))
print('Borrowers that earn > 178k:
{}'.format(data[data.annual_inc>178000].shape[0]/total))
print('Borrowers that earn > 256k:
{}'.format(data[data.annual_inc>256000].shape[0]/total))
可以看出,有5%的数据在上限之上,有1%的数据在极上限上。
Z分数法
Z分数(或称标准分数)表示有多少标准差(即所给出的偏离平均值的度量)。换言之,其仅仅是将数据重新调节或标准化。利用Z分数可确定分布内观测值的精准定位。Z分数可以显示数值是在平均数之上还是之下。
利用Z分数法旨在消除数据位置和大小的影响,以便直接比较不同的数据集。Z分数法检验离群值的途径就是先集中并重调数据,然后任何偏离零(通常以Z分数3或-3为临界值)太远的数值都应视为一个离群值。
计算Z分数的公式如下:
代码:
导入库
from scipy import stats
计算Z分数
z = stats.zscore(data)
print(z)
临界值>3
threshold = 3
print(np.where(z > 3))
据上述输出,第一序列包含行数字,第二序列表示列数字。
聚类分析法
聚类法是常见的方法,用于对相似数据点或同组、同集群的对象进行分组。这是离群值分析中十分重要的方法。运用此方法前,应先将相似的这类对象分组。
利用K均值聚类法帮助我们汇集数据(即我们的年度收入值),然后采用欧几里得距离(Euclidean distance)将相似对象分类。现在来试试吧!
代码:
导入库
现在,从scipy.cluster.vq.中导入K均值模数。SciPy支持科学性Python,为科学实验的实施提供各种各样的便利程序。
from scipy.cluster.vq import kmeans
from scipy.cluster.vq import vq
将数据转换为numpy的对象Array,并应用K均值函数。同时,需给出两个输入数据,然后,集群数便形成了。
data_raw = data['disbursed_amount'].value
centroids, avg_distance = kmeans(data_raw, 4)
groups, cdist = vq(data_raw, centroids)
距心是由kmeans()形成的集群中心,avg_distance是各数据点和距心之间的欧几里得距离平均值。接下来,启动vq()法。返回数据组(集群)和数据点及其最近数据组的距离。
现在一起绘制所获得的各组数。
y = np.arange(0,30000)
plt.scatter(data_raw, y , c=groups)
plt.xlabel('Salaries')
plt.ylabel('Indices')
plt.show()
相信你肯定可以通过上方的图片识别出离群值。
图示法
如之前文章中提到的,箱线图、直方图、散点图都是一些用于识别数据集中离群值的主要方法。
箱线图
箱线图,又称Whisker图,是由分位数和四分位数间距来进行描述的图表法。它可以帮助定义上限和下限,任何超出上限和下限的数据都是离群值。
总而言之,分位数是在分布内与值顺序排列相关的点。在给出的例子中,通过样本排序即可找到分位数。所挑出样本的中间值就在分位数中间或在第50个百分位数上(又称样本中位数)。
箱线法的目的是在任何更进一步的分析之前识别出数据序列中的离群值,以便通过研究得出更加准确的结果,并不受任何极值和非常态值的影响。
sns.boxplot(y='annual_inc', data = data)
这里,离群值是那些数值上不同于其他数据的观测值。回看箱线图,离群值位于箱线图内栅栏线外(“whiskers”)。
直方图
直方图是显示数据数值以及发现数据集的分布最常用的图表之一。离群值位于整体分布模型之外。
fig = data.annual_inc.hist(bins=500)
fig.set_xlim(0,500000)
在这里,位于X轴最右侧的数据点就是离群值。
散点图
散点图用于找到两个变量之间的联系,并且此联系常常有一种模型。不符合该模型的数据点就是离群值。
data_raw = data['annual_inc'].values
y = np.arange(0,30000)
plt.scatter(data_raw, y)
plt.xlabel('Annual Income')
plt.ylabel('Indices')
plt.show()
预处理离群值的方法
1. 平均值/中位数/随机缺失值处理
如果有理由相信离群值是由机器错误或是计量纰漏导致的,那么就意味着离群值本质与缺失值类似,任何数据缺失处理的方法都可用于替换离群值。离群值的数字很小(否则它们不会成为离群值),用中位数、中位数、随机缺失值处理来替换离群值是合理的。
2. 修整
此法完全摒弃离群值。也就是说,消除视为离群值的数据点。由于不可能去除数据集中的大量数值,所以修整是较好较快的方法。
index = data[(data['annual_inc'] >= 256000)].index
data.drop(index, inplace=True)
这里,先运用极值分析确定高于上限的数值,然后运用pandas的drop函数去除去除这些值。
3. 顶端编码/底端编码/零编码
顶端编码即对分布内最大值设置任意数值。顶端编码变量是超过上限且已被删除的数据点。通过执行顶端编码,离群值被限制在一个特定的最大值,该值与很多观测结果相近。
底端编码与此类似,但是在分布右侧。也就是说,所有值均在某临界值之下,受临界值限制。如若临界值为零,那么称为零编码。举个例子,像“年龄”、“收入”这类变量是没有负值的。因此,将最低值设为零也是说得通的。
代码:
print('Annual Income > 256000: {}'.format(data[data.annual_inc>256000].shape[0]))
print('Percentage of outliers: {}'.format(data[data.annual_inc>256000].shape[0]/np.float(data.shape[0])))
此步,设置数据点值大于256000。
data.loc[data.annual_inc>256000,'annual_inc'] = 256000
data.annual_inc.max()
现在,最大值为256000。
4. 离散化
离散化是连续变量转换为非连续变量的过程,这个过程通过创建一组跨越变量值范围的连续区间完成。因此,这些离群观测值和其余分布尾端的值不再相同,它们都在同一个区间/范围中。
连续变量转换为非连续变量的方法有很多。此法也称为数据分箱技术,每个箱子分属于各自的区间。
离散化分析的方法:
(1)等频离散化
等频分箱将可能的变量值分为N个箱,每个箱都有相同数量的观测值。这个方法尤其对交错变量十分有用,因为它把观测值平均地置于不同的箱中。通常,通过分位数确定区间临界,这有助于减少信息缺失,得到更好的结果。
这里,我们利用pandas的qcut函数(基于分位数的离散函数)创建5个分箱。
income_discretised, intervals = pd.qcut(data.annual_inc, 5,
labels=None, retbins=True, precision=3, duplicates='raise')
pd.concat([income_discretised, data.annual_inc], axis=1).head(5)
区间是:
intervals
下面,可以看到每个区间都有几乎相同数量的观测值。
temp = pd.concat([income_discretised, data.annual_inc], axis=1)
temp.columns = ['income_discretised', 'annual_inc']
temp.groupby('income_discretised')['annual_inc'].count()
(2)等宽离散化
等宽离散分箱将可能值的范围分为N个等宽箱。宽度取决于变量值的范围和我们期望变量的分箱数。
宽=(最大值-最小值)/N
举个例子,如果变量的值在0~100,就要创建5个这样的宽度的箱子:宽=(100-0)/5=20。此时,第一个箱子0~20和最后一个箱子80~100可扩展用于放置离群值(也就是说,0以下和100以上的值可以放置在这两个箱子中)。目前无法凭经验来定义N,仍需依赖实际用例来定义。
代码:
income_range = data.annual_inc.max() - data.annual_inc.min()
min_value = int(np.floor(data.annual_inc.min()))
max_value = int(np.ceil(data.annual_inc.max()))
# let's round the bin width
inter_value = int(np.round(income_range/5))
min_value, max_value, inter_value
现在,计算区间:
intervals = [i for i in range(min_value, max_value+inter_value, inter_value)]
labels = ['Bin_'+str(i) for i in range(1,len(intervals))]print(intervals)
print(labels)
最后,利用pandas的cut函数切割整理数据值到各箱中:
data['annual_inc_labels'] = pd.cut(x = data.annual_inc,
bins=intervals, labels=labels, include_lowest=True)
data['annual_inc_interval'] = pd.cut(x = data.annual_inc,
bins=intervals, include_lowest=True)
data.head(5)
可利用如下所示的统计图计量每个箱中的数据。可以看出,给定的样本数据中,大多数人的收入都低于10000。
sns.countplot(data.annual_inc_labels)
留言 点赞 关注
我们一起分享AI学习与发展的干货
欢迎关注全平台AI垂类自媒体 “读芯术”