机器学习基础与实践(一)----数据清洗
------------------------------------本博客所有内容以学习、研究和分享为主,如需转载,请联系本人,标明作者和出处,并且是非商业用途,谢谢!--------------------------------
想写这个系列很久了,最近刚好项目结束了闲下来有点时间,于是决定把之前学过的东西做个总结。之前看过一些机器学习方面的书,每本书都各有侧重点,机器学习实战和集体智慧编程更偏向与实战,侧重于对每个算法的实际操作过程,但是没有对整个数据挖掘项目做介绍,李航老师的统计学习方法和周志华老师的机器学习这两本书侧重对原理的讲解和公式的推导,但是实战方面可能会少一点。我结合之前看过的书,以及自己的一些项目经验做了一些总结,一是回顾自己还有哪些遗漏,二是希望给新入门的同学一个参考。至于编程语言,主要用python,也会有少部分R,java和scala之类,毕竟实际项目中也不可能使用一种语言。此外,本系列所用到的所有数据我会传到Github上,需要的同学可以自行下载。为保证文章质量,每周二周四更新,下面是主要的目录(可能会根据实际情况调整):
第一部分 模型的评估与数据处理
机器学习基础与实践(一)----数据清洗
机器学习基础与实践(二)----数据转换
机器学习基础与实践(三)----数据降维
第二部分 特征工程
机器学习基础与实践(四)----特征选择
机器学习基础与实践(五)----特征提取
机器学习基础与实践(六)----模型选择与评估
第三部分 算法基础之有监督算法
机器学习基础与实践(七)----广义线性模型
机器学习基础与实践(八)----最小二乘法
机器学习基础与实践(九)----LDA
机器学习基础与实践(十)----SGD
机器学习基础与实践(十一)----K近邻
机器学习基础与实践(十二)----高斯过程
机器学习基础与实践(十三)----决策树(ID3,C4.5,C5.0,CART)
机器学习基础与实践(十四)----朴素贝叶斯
机器学习基础与实践(十五)----支持向量机
机器学习基础与实践(十六)----集成学习(Bagging,RF,AdaBoost,Gradient Tree Boosting,Voting Classifier)
机器学习基础与实践(十七)----感知机模型
机器学习基础与实践(十八)----多分类算法
第四部分 算法基础之无监督算法
机器学习基础与实践(十九)----K-means
机器学习基础与实践(二十)----Affinity propagation
机器学习基础与实践(二十一)----Mean-shift
机器学习基础与实践(二十二)----Spectral clustering
机器学习基础与实践(二十三)----Ward hierachical
机器学习基础与实践(二十四)----Agglomerative clustering
机器学习基础与实践(二十五)----DBSCAN
机器学习基础与实践(二十六)----Gaussian mixtures
机器学习基础与实践(二十七)----Birch
第五部分 算法基础之推荐算法
机器学习基础与实践(二十八)----相似度计算
机器学习基础与实践(二十九)----Arules关联规则
机器学习基础与实践(三十)----Fp-Growth
机器学习基础与实践(三十一)----User-based or Item-based
第六部分 算法基础之半监督模型
机器学习基础与实践(三十二)----Label Propagation
第七部分 算法基础之其他模型
机器学习基础与实践(三十三)----概率图模型
机器学习基础与实践(三十四)----最大熵模型
机器学习基础与实践(三十五)----规则学习
机器学习基础与实践(三十六)----强化学习
机器学习基础与实践(三十七)----条件随机场
机器学习基础与实践(三十八)----保序回归(Isotonic regression)
机器学习基础与实践(三十九)----Probability calibration
正文:
按照我做项目的经验,来了项目,首先是分析项目的目的和需求,了解这个项目属于什么问题,要达到什么效果。然后提取数据,做基本的数据清洗。第三步是特征工程,这个属于脏活累活,需要耗费很大的精力,如果特征工程做的好,那么,后面选择什么算法其实差异不大,反之,不管选择什么算法,效果都不会有突破性的提高。第四步,是跑算法,通常情况下,我会把所有能跑的算法先跑一遍,看看效果,分析一下precesion/recall和f1-score,看看有没有什么异常(譬如有好几个算法precision特别好,但是recall特别低,这就要从数据中找原因,或者从算法中看是不是因为算法不适合这个数据),如果没有异常,那么就进行下一步,选择一两个跑的结果最好的算法进行调优。调优的方法很多,调整参数的话可以用网格搜索、随机搜索等,调整性能的话,可以根据具体的数据和场景进行具体分析。调优后再跑一边算法,看结果有没有提高,如果没有,找原因,数据 or 算法?是数据质量不好,还是特征问题还是算法问题。一个一个排查,找解决方法。特征问题就回到第三步再进行特征工程,数据质量问题就回到第一步看数据清洗有没有遗漏,异常值是否影响了算法的结果,算法问题就回到第四步,看算法流程中哪一步出了问题。如果实在不行,可以搜一下相关的论文,看看论文中有没有解决方法。这样反复来几遍,就可以出结果了,写技术文档和分析报告,再向业务人员或产品讲解我们做的东西,然后他们再提建议/该需求,不断循环,最后代码上线,改bug,直到结项。
直观来看,可以用一个流程图来表示:
今天讲数据清洗,为什么要进行数据清洗呢?我们在书上看到的数据,譬如常见的iris数据集,房价数据,电影评分数据集等等,数据质量都很高,没有缺失值,没有异常点,也没有噪音,而在真实数据中,我们拿到的数据可能包含了大量的缺失值,可能包含大量的噪音,也可能因为人工录入错误导致有异常点存在,对我们挖据出有效信息造成了一定的困扰,所以我们需要通过一些方法,尽量提高数据的质量。数据清洗一般包括以下几个步骤:
1 #一组年薪超过10万元的经理收入 2 pay=c(11,19,14,22,14,28,13,81,12,43,11,16,31,16,23.42,22,26,17,22,13,27,180,16,43,82,14,11,51,76,28,66,29,14,14,65,37,16,37,35,39,27,14,17,13,38,28,40,85,32,25,26,16,12,54,40,18,27,16,14,33,29,77,50,19,34) 3 par(mfrow=c(2,2))#将绘图窗口改成2*2,可同时显示四幅图 4 hist(pay)#绘制直方图 5 dotchart(pay)#绘制点图 6 barplot(pay,horizontal=T)#绘制箱型图 7 qqnorm(pay);qqline(pay)#绘制Q-Q图
1 import scipy as sp 2 data = sp.genfromtxt("web_traffic.tsv",delimiter = "\t")
数据情况如下:
1 >>>data 2 array([[ 1.00000000e+00, 2.27200000e+03], 3 [ 2.00000000e+00, nan], 4 [ 3.00000000e+00, 1.38600000e+03], 5 ..., 6 [ 7.41000000e+02, 5.39200000e+03], 7 [ 7.42000000e+02, 5.90600000e+03], 8 [ 7.43000000e+02, 4.88100000e+03]]) 9 10 >>> print data[:10] 11 [[ 1.00000000e+00 2.27200000e+03] 12 [ 2.00000000e+00 nan] 13 [ 3.00000000e+00 1.38600000e+03] 14 [ 4.00000000e+00 1.36500000e+03] 15 [ 5.00000000e+00 1.48800000e+03] 16 [ 6.00000000e+00 1.33700000e+03] 17 [ 7.00000000e+00 1.88300000e+03] 18 [ 8.00000000e+00 2.28300000e+03] 19 [ 9.00000000e+00 1.33500000e+03] 20 [ 1.00000000e+01 1.02500000e+03]] 21 22 >>> data.shape 23 (743, 2)
可以看到,第2列已经出现了缺失值,现在我们来看一下缺失值的数量:
1 >>> x = data[:,0] 2 >>> y = data[:,1] 3 >>> sp.sum(sp.isnan(y)) 4 8
在743个数据里只有8个数据缺失,所以删除它们对于整体数据情况影响不大。当然,这是缺失值少的情况下,在缺失值值比较多,而这个维度的信息还很重要的时候(因为缺失值如果占了95%以上,可以直接去掉这个维度的数据了),直接删除会对后面的算法跑的结果造成不好的影响。我们常用的方法有以下几种:
1.直接删除----适合缺失值数量较小,并且是随机出现的,删除它们对整体数据影响不大的情况
2.使用一个全局常量填充---譬如将缺失值用“Unknown”等填充,但是效果不一定好,因为算法可能会把它识别为一个新的类别,一般很少用
3.使用均值或中位数代替----优点:不会减少样本信息,处理简单。缺点:当缺失数据不是随机数据时会产生偏差.对于正常分布的数据可以使用均值代替,如果数据是倾斜的,使用中位数可能更好。
4.插补法
1)随机插补法----从总体中随机抽取某个样本代替缺失样本2)多重插补法----通过变量之间的关系对缺失数据进行预测,利用蒙特卡洛方法生成多个完整的数据集,在对这些数据集进行分析,最后对分析结果进行汇总处理3)热平台插补----指在非缺失数据集中找到一个与缺失值所在样本相似的样本(匹配样本),利用其中的观测值对缺失值进行插补。优点:简单易行,准去率较高缺点:变量数量较多时,通常很难找到与需要插补样本完全相同的样本。但我们可以按照某些变量将数据分层,在层中对缺失值实用均值插补4)拉格朗日差值法和牛顿插值法(简单高效,数值分析里的内容,数学公式以后再补 = =)5.建模法可以用回归、使用贝叶斯形式化方法的基于推理的工具或决策树归纳确定。例如,利用数据集中其他数据的属性,可以构造一棵判定树,来预测缺失值的值。
1 >>> import pandas as pd 2 >>> data = pd.read_table("web_traffic.tsv",header = None) 3 >>> data.describe() 4 0 1 5 count 743.000000 735.000000 6 mean 372.000000 1962.165986 7 std 214.629914 860.720997 8 min 1.000000 472.000000 9 25% 186.500000 1391.000000 10 50% 372.000000 1764.000000 11 75% 557.500000 2217.500000 12 max 743.000000 5906.000000
处理方法:
1.删除异常值----明显看出是异常且数量较少可以直接删除
2.不处理---如果算法对异常值不敏感则可以不处理,但如果算法对异常值敏感,则最好不要用,如基于距离计算的一些算法,包括kmeans,knn之类的。
3.平均值替代----损失信息小,简单高效。
4.视为缺失值----可以按照处理缺失值的方法来处理
1 #创建数据,data里包含重复数据 2 >>> data = pd.DataFrame({'v1':['a']*5+['b']* 4,'v2':[1,2,2,2,3,4,4,5,3]}) 3 >>> data 4 v1 v2 5 0 a 1 6 1 a 2 7 2 a 2 8 3 a 2 9 4 a 3 10 5 b 4 11 6 b 4 12 7 b 5 13 8 b 3 14 15 #DataFrame的duplicated方法返回一个布尔型Series,表示各行是否是重复行 16 >>> data.duplicated() 17 0 False 18 1 False 19 2 True 20 3 True 21 4 False 22 5 False 23 6 True 24 7 False 25 8 False 26 dtype: bool 27 28 #drop_duplicates方法用于返回一个移除了重复行的DataFrame 29 >>> data.drop_duplicates() 30 v1 v2 31 0 a 1 32 1 a 2 33 4 a 3 34 5 b 4 35 7 b 5 36 8 b 3 37 38 #这两个方法默认会判断全部列,你也可以指定部分列进行重复项判断。假设你还有一列值,且只希望根据v1列过滤重复项: 39 >>> data['v3']=range(9) 40 >>> data 41 v1 v2 v3 42 0 a 1 0 43 1 a 2 1 44 2 a 2 2 45 3 a 2 3 46 4 a 3 4 47 5 b 4 5 48 6 b 4 6 49 7 b 5 7 50 8 b 3 8 51 >>> data.drop_duplicates(['v1']) 52 v1 v2 v3 53 0 a 1 0 54 5 b 4 5 55 56 #duplicated和drop_duplicates默认保留的是第一个出现的值组合。传入take_last=True则保留最后一个: 57 >>> data.drop_duplicates(['v1','v2'],take_last = True) 58 v1 v2 v3 59 0 a 1 0 60 3 a 2 3 61 4 a 3 4 62 6 b 4 6 63 7 b 5 7 64 8 b 3 8
1 list0=['b','c', 'd','b','c','a','a'] 2 3 方法1:使用set() 4 5 list1=sorted(set(list0),key=list0.index) # sorted output 6 print( list1) 7 8 方法2:使用 {}.fromkeys().keys() 9 10 list2={}.fromkeys(list0).keys() 11 print(list2) 12 13 方法3:set()+sort() 14 15 list3=list(set(list0)) 16 list3.sort(key=list0.index) 17 print(list3) 18 19 方法4:迭代 20 21 list4=[] 22 for i in list0: 23 if not i in list4: 24 list4.append(i) 25 print(list4) 26 27 方法5:排序后比较相邻2个元素的数据,重复的删除 28 29 def sortlist(list0): 30 list0.sort() 31 last=list0[-1] 32 for i in range(len(list0)-2,-1,-1): 33 if list0[i]==last: 34 list0.remove(list0[i]) 35 else: 36 last=list0[i] 37 return list0 38 39 print(sortlist(list0))
五.噪音处理
Outlier: you are enumerating meticulously everything you have. You found 3 dimes, 1 quarter and wow a 100 USD bill you had put there last time you bought some booze and had totally forgot there. The 100 USD bill is an outlier, as it's not commonly expected in a pocket.
Noise: you have just come back from that club and are pretty much wasted. You try to find some money to buy something to sober up, but you have trouble reading the figures correctly on the coins. You found 3 dimes, 1 quarter and wow a 100 USD bill. But in fact, you have mistaken the quarter for a dime: this mistake introduces noise in the data you have access to.
To put it otherwise, data = true signal + noise. Outliers are part of the data.
离群点: 你正在从口袋的零钱包里面穷举里面的钱,你发现了3个一角,1个五毛,和一张100元的毛爷爷向你微笑。这个100元就是个离群点,因为并不应该常出现在口袋里..
噪声: 你晚上去三里屯喝的酩酊大醉,很需要买点东西清醒清醒,这时候你开始翻口袋的零钱包,嘛,你发现了3个一角,1个五毛,和一张100元的毛爷爷向你微笑。但是你突然眼晕,把那三个一角看成了三个1元...这样错误的判断使得数据集中出现了噪声
- 用箱均值光滑:箱中每一个值被箱中的平均值替换。
- 用箱中位数平滑:箱中的每一个值被箱中的中位数替换。
- 用箱边界平滑:箱中的最大和最小值同样被视为边界。箱中的每一个值被最近的边界值替换。
一般而言,宽度越大,光滑效果越明显。箱也可以是等宽的,其中每个箱值的区间范围是个常量。分箱也可以作为一种离散化技术使用.
2. 回归法
可以用一个函数拟合数据来光滑数据。线性回归涉及找出拟合两个属性(或变量)的“最佳”直线,使得一个属性能够预测另一个。多线性回归是线性回归的扩展,它涉及多于两个属性,并且数据拟合到一个多维面。使用回归,找出适合数据的数学方程式,能够帮助消除噪声。
1 #-*- coding :utf-8 -*- 2 #文本格式化处理,过滤掉空行 3 4 file = open('123.txt') 5 6 i = 0 7 while 1: 8 line = file.readline().strip() 9 if not line: 10 break 11 i = i + 1 12 line1 = line.replace('\r','') 13 f1 = open('filename.txt','a') 14 f1.write(line1 + '\n') 15 f1.close() 16 print str(i)
2.如何判断文件的编码格式
1 #-*- coding:utf8 -*- 2 #批量处理编码格式转换(优化) 3 import os 4 import chardet 5 6 path1 = 'E://2016txtutf/' 7 def dirlist(path): 8 filelist = os.listdir(path) 9 for filename in filelist: 10 filepath = os.path.join(path, filename) 11 if os.path.isdir(filepath): 12 dirlist(filepath) 13 else: 14 if filepath.endswith('.txt'): 15 f = open(filepath) 16 data = f.read() 17 if chardet.detect(data)['encoding'] != 'utf-8': 18 print filepath + "----"+ chardet.detect(data)['encoding'] 19 20 dirlist(path1)
3.文件编码格式转换,gbk与utf-8之间的转换
这个主要是在一些对文件编码格式有特殊需求的时候,需要批量将gbk的转utf-8的或者将utf-8编码的文件转成gbk编码格式的。
1 #-*- coding:gbk -*- 2 #批量处理编码格式转换 3 import codecs 4 import os 5 path1 = 'E://dir/' 6 def ReadFile(filePath,encoding="utf-8"): 7 with codecs.open(filePath,"r",encoding) as f: 8 return f.read() 9 10 def WriteFile(filePath,u,encoding="gbk"): 11 with codecs.open(filePath,"w",encoding) as f: 12 f.write(u) 13 14 def UTF8_2_GBK(src,dst): 15 content = ReadFile(src,encoding="utf-8") 16 WriteFile(dst,content,encoding="gbk") 17 18 def GBK_2_UTF8(src,dst): 19 content = ReadFile(src,encoding="gbk") 20 WriteFile(dst,content,encoding="utf-8") 21 22 def dirlist(path): 23 filelist = os.listdir(path) 24 for filename in filelist: 25 filepath = os.path.join(path, filename) 26 if os.path.isdir(filepath): 27 dirlist(filepath) 28 else: 29 if filepath.endswith('.txt'): 30 print filepath 31 #os.rename(filepath, filepath.replace('.txt','.doc')) 32 try: 33 UTF8_2_GBK(filepath,filepath) 34 except Exception,ex: 35 f = open('error.txt','a') 36 f.write(filepath + '\n') 37 f.close() 38 39 dirlist(path1)