我们在书上看到的数据,譬如常见的iris数据集,房价数据,电影评分数据集等等,数据质量都很高,没有缺失值,没有异常点,也没有噪音,而在真实数据中,我们拿到的数据可能包含了大量的缺失值,可能包含大量的噪音,也可能因为人工录入错误导致有异常点存在,对我们挖据出有效信息造成了一定的困扰,所以我们需要通过一些方法,尽量提高数据的质量。数据清洗一般包括以下几个步骤:
一.分析数据
二.缺失值处理
三.异常值处理
四.去重处理
五.噪音数据处理
六.一些实用的数据处理小工具
一.分析数据
在实际项目中,当我们确定需求后就会去找相应的数据,拿到数据后,首先要对数据进行描述性统计分析,查看哪些数据是不合理的,也可以知道数据的基本情况。如果是销售额数据可以通过分析不同商品的销售总额、人均消费额、人均消费次数等,同一商品的不同时间的消费额、消费频次等等,了解数据的基本情况。此外可以通过作图的方式了解数据的质量,有无异常点,有无噪音等。
python中也包含了大量的统计命令,其中主要的统计特征函数如下图所示:
image.png
二.缺失值处理
缺失值在实际数据中是不可避免的问题,有的人看到有缺失的数据就直接删除了,有的人直接赋予0值或者某一个特殊的值,那么到底该怎么处理呢?对于不同的数据场景应该采取不同的策略,首先应该判断缺失值的分布情况
import scipy as sp
data = sp.genfromtxt("web_traffic.tsv",delimiter = "\t")
数据情况如下:
>>>data
array([[ 1.00000000e+00, 2.27200000e+03],
[ 2.00000000e+00, nan],
[ 3.00000000e+00, 1.38600000e+03],
...,
[ 7.41000000e+02, 5.39200000e+03],
[ 7.42000000e+02, 5.90600000e+03],
[ 7.43000000e+02, 4.88100000e+03]])
>>> print data[:10]
[[ 1.00000000e+00 2.27200000e+03]
[ 2.00000000e+00 nan]
[ 3.00000000e+00 1.38600000e+03]
[ 4.00000000e+00 1.36500000e+03]
[ 5.00000000e+00 1.48800000e+03]
[ 6.00000000e+00 1.33700000e+03]
[ 7.00000000e+00 1.88300000e+03]
[ 8.00000000e+00 2.28300000e+03]
[ 9.00000000e+00 1.33500000e+03]
[ 1.00000000e+01 1.02500000e+03]]
>>> data.shape
(743, 2)
>>> x = data[:,0]
>>> y = data[:,1]
>>> sp.sum(sp.isnan(y))
8
在743个数据里只有8个数据缺失,所以删除它们对于整体数据情况影响不大。当然,这是缺失值少的情况下,在缺失值值比较多,而这个维度的信息还很重要的时候(因为缺失值如果占了95%以上,可以直接去掉这个维度的数据了),直接删除会对后面的算法跑的结果造成不好的影响。我们常用的方法有以下几种:
1.直接删除----适合缺失值数量较小,并且是随机出现的,删除它们对整体数据影响不大的情况
2.使用一个全局常量填充---譬如将缺失值用“Unknown”等填充,但是效果不一定好,因为算法可能会把它识别为一个新的类别,一般很少用
3.使用均值或中位数代替----优点:不会减少样本信息,处理简单。缺点:当缺失数据不是随机数据时会产生偏差.对于正常分布的数据可以使用均值代替,如果数据是倾斜的,使用中位数可能更好。
4.插补法
1)随机插补法----从总体中随机抽取某个样本代替缺失样本
2)多重插补法----通过变量之间的关系对缺失数据进行预测,利用蒙特卡洛方法生成多个完整的数据集,在对这些数据集进行分析,最后对分析结果进行汇总处理
3)热平台插补----指在非缺失数据集中找到一个与缺失值所在样本相似的样本(匹配样本),利用其中的观测值对缺失值进行插补。
优点:简单易行,准去率较高
缺点:变量数量较多时,通常很难找到与需要插补样本完全相同的样本。但我们可以按照某些变量将数据分层,在层中对缺失值实用均值插补
4)拉格朗日差值法和牛顿插值法(简单高效,数值分析里的内容,数学公式以后再补 = =)
5.建模法
可以用回归、使用贝叶斯形式化方法的基于推理的工具或决策树归纳确定。例如,利用数据集中其他数据的属性,可以构造一棵判定树,来预测缺失值的值。
以上方法各有优缺点,具体情况要根据实际数据分分布情况、倾斜程度、缺失值所占比例等等来选择方法。一般而言,建模法是比较常用的方法,它根据已有的值来预测缺失值,准确率更高。
三.异常值处理
异常值我们通常也称为“离群点”。在讲分析数据时,我们举了个例子说明如何发现离群点,除了画图(画图其实并不常用,因为数据量多时不好画图,而且慢),还有很多其他方法:
1.简单的统计分析
拿到数据后可以对数据进行一个简单的描述性统计分析,譬如最大最小值可以用来判断这个变量的取值是否超过了合理的范围,如客户的年龄为-20岁或200岁,显然是不合常理的,为异常值。
在python中可以直接用pandas的describe():
>>> import pandas as pd
>>> data = pd.read_table("web_traffic.tsv",header = None)
>>> data.describe()
0 1
count 743.000000 735.000000
mean 372.000000 1962.165986
std 214.629914 860.720997
min 1.000000 472.000000
25% 186.500000 1391.000000
50% 372.000000 1764.000000
75% 557.500000 2217.500000
max 743.000000 5906.000000
2.3∂原则
如果数据服从正态分布,在3∂原则下,异常值为一组测定值中与平均值的偏差超过3倍标准差的值。如果数据服从正态分布,距离平均值3∂之外的值出现的概率为P(|x-u| > 3∂) <= 0.003,属于极个别的小概率事件。如果数据不服从正态分布,也可以用远离平均值的多少倍标准差来描述。
3.箱型图分析
箱型图提供了识别异常值的一个标准:如果一个值小于QL01.5IQR或大于OU-1.5IQR的值,则被称为异常值。QL为下四分位数,表示全部观察值中有四分之一的数据取值比它小;QU为上四分位数,表示全部观察值中有四分之一的数据取值比它大;IQR为四分位数间距,是上四分位数QU与下四分位数QL的差值,包含了全部观察值的一半。箱型图判断异常值的方法以四分位数和四分位距为基础,四分位数具有鲁棒性:25%的数据可以变得任意远并且不会干扰四分位数,所以异常值不能对这个标准施加影响。因此箱型图识别异常值比较客观,在识别异常值时有一定的优越性。
4.基于模型检测
首先建立一个数据模型,异常是那些同模型不能完美拟合的对象;如果模型是簇的集合,则异常是不显著属于任何簇的对象;在使用回归模型时,异常是相对远离预测值的对象
优缺点:1.有坚实的统计学理论基础,当存在充分的数据和所用的检验类型的知识时,这些检验可能非常有效;2.对于多元数据,可用的选择少一些,并且对于高维数据,这些检测可能性很差。
5.基于距离
通常可以在对象之间定义邻近性度量,异常对象是那些远离其他对象的对象
优缺点:1.简单;2.缺点:基于邻近度的方法需要O(m2)时间,大数据集不适用;3.该方法对参数的选择也是敏感的;4.不能处理具有不同密度区域的数据集,因为它使用全局阈值,不能考虑这种密度的变化。
6.基于密度
当一个点的局部密度显著低于它的大部分近邻时才将其分类为离群点。适合非均匀分布的数据。
优缺点:1.给出了对象是离群点的定量度量,并且即使数据具有不同的区域也能够很好的处理;2.与基于距离的方法一样,这些方法必然具有O(m2)的时间复杂度。对于低维数据使用特定的数据结构可以达到O(mlogm);3.参数选择困难。虽然算法通过观察不同的k值,取得最大离群点得分来处理该问题,但是,仍然需要选择这些值的上下界。
7.基于聚类:
基于聚类的离群点:一个对象是基于聚类的离群点,如果该对象不强属于任何簇。离群点对初始聚类的影响:如果通过聚类检测离群点,则由于离群点影响聚类,存在一个问题:结构是否有效。为了处理该问题,可以使用如下方法:对象聚类,删除离群点,对象再次聚类(这个不能保证产生最优结果)。
优缺点:1.基于线性和接近线性复杂度(k均值)的聚类技术来发现离群点可能是高度有效的;2.簇的定义通常是离群点的补,因此可能同时发现簇和离群点;3.产生的离群点集和它们的得分可能非常依赖所用的簇的个数和数据中离群点的存在性;4.聚类算法产生的簇的质量对该算法产生的离群点的质量影响非常大。
处理方法:
1.删除异常值----明显看出是异常且数量较少可以直接删除
2.不处理---如果算法对异常值不敏感则可以不处理,但如果算法对异常值敏感,则最好不要用,如基于距离计算的一些算法,包括kmeans,knn之类的。
3.平均值替代----损失信息小,简单高效。
4.视为缺失值----可以按照处理缺失值的方法来处理
四.去重处理
以DataFrame数据格式为例:
#创建数据,data里包含重复数据
>>> data = pd.DataFrame({'v1':['a']*5+['b']* 4,'v2':[1,2,2,2,3,4,4,5,3]})
>>> data
v1 v2
0 a 1
1 a 2
2 a 2
3 a 2
4 a 3
5 b 4
6 b 4
7 b 5
8 b 3
#DataFrame的duplicated方法返回一个布尔型Series,表示各行是否是重复行
>>> data.duplicated()
0 False
1 False
2 True
3 True
4 False
5 False
6 True
7 False
8 False
dtype: bool
#drop_duplicates方法用于返回一个移除了重复行的DataFrame
>>> data.drop_duplicates()
v1 v2
0 a 1
1 a 2
4 a 3
5 b 4
7 b 5
8 b 3
#这两个方法默认会判断全部列,你也可以指定部分列进行重复项判断。假设你还有一列值,且只希望根据v1列过滤重复项:
>>> data['v3']=range(9)
>>> data
v1 v2 v3
0 a 1 0
1 a 2 1
2 a 2 2
3 a 2 3
4 a 3 4
5 b 4 5
6 b 4 6
7 b 5 7
8 b 3 8
>>> data.drop_duplicates(['v1'])
v1 v2 v3
0 a 1 0
5 b 4 5
#duplicated和drop_duplicates默认保留的是第一个出现的值组合。传入take_last=True则保留最后一个:
>>> data.drop_duplicates(['v1','v2'],take_last = True)
v1 v2 v3
0 a 1 0
3 a 2 3
4 a 3 4
6 b 4 6
7 b 5 7
8 b 3 8
如果数据是列表格式的,有以下几种方法可以删除:
list0=['b','c', 'd','b','c','a','a']
方法1:使用set()
list1=sorted(set(list0),key=list0.index) # sorted output
print( list1)
方法2:使用 {}.fromkeys().keys()
list2={}.fromkeys(list0).keys()
print(list2)
方法3:set()+sort()
list3=list(set(list0))
list3.sort(key=list0.index)
print(list3)
方法4:迭代
list4=[]
for i in list0:
if not i in list4:
list4.append(i)
print(list4)
方法5:排序后比较相邻2个元素的数据,重复的删除
def sortlist(list0):
list0.sort()
last=list0[-1]
for i in range(len(list0)-2,-1,-1):
if list0[i]==last:
list0.remove(list0[i])
else:
last=list0[i]
return list0
print(sortlist(list0))
网友评论