前言:经过前面两篇文章的阅读,我们已经对数据的获取有了一定的概念。在数据挖掘的工作中,我们很大一部分的时间都会花在数据处理和特征工程上面,真正的可视化和建模反而时间占比不大。这是因为,建模要求我们传入的数据是机器可识别的数据,并且数据不包括异常、缺失值。因此,这篇文章主要内容是常见的数据处理的套路总结,下一篇文章是常见的特征工程套路的总结。我将会按照原理、自己的理解、代码实现、案例这四个维度进行常见套路的总结,如果是你需要的话,就继续阅读下去吧。
一:为什么要做数据处理
首先,我们思考一下为什么要做数据处理。要说清楚这个问题,我们首先要对数据建模有一个认知。数据建模是基于已有的数据进行分类、聚合的算法。无论是有监督的数据还是无监督的数据,都是基于数据本身的规律通过算法实现模型构造。因此,当我们的数据中出现缺失值、异常值、无意义的重复值时,就会导致建模精度不高,如此自然也就无法达成我们想要的效果。而这也是数据处理之所以重要的原因。相当于大厦的根基,根基不稳,自然建造不出稳定的房子。
二:一些常见的数据清洗套路
2.1 安装包需求
需要安装有python、并且安装有pandas、numpy、matplotlib、sklearn、jupyter lab三方包。后续要使用到或者推荐到其它的三方包会进行说明。强烈建议安装Anaconda解决上述安装问题。再在Anaconda里面安装jupyter lab即可解决问题。
ps:如果对数据如何读入,以及索引等感兴趣,可以到哔哩哔哩观看我之前录制的pandas和numpy的教学视频:https://www.bilibili.com/video/BV1A7411D758/
2.2 数据
我们的数据来源于kaggle:https://www.kaggle.com/c/titanic
或者也可以访问天池:https://tianchi.aliyun.com/dataset/dataDetail?dataId=58756
二者都可以得到我们要使用的titanic数据。接下来我们为这个数据集添加一些缺失值、重复值和异常值吧。
import pandas as pd
import numpy as np
import os,random
os.chdir(r'D:\Data\Datas Analysis\learn\official\base\titanic')
# 首先要获得我们的泰坦尼克数据
datas = pd.read_csv('train.csv')
# 明确我们的缺失值、重复值、异常值
na_value = np.nan
duplicate_vlaues = []
exception_values = [99999999,0.0000001,'卯月']
# 找到我们待会儿要使用的随机索引
rows = random.sample(range(891),50)
columns = random.sample(range(12),6)
# step1 : 获得我们的重复值
for _ in range(10):
i = random.choice(rows)
duplicate_value = datas.iloc[i,:]
duplicate_vlaues.append(duplicate_value)
duplicate_vlaues = pd.DataFrame(duplicate_vlaues)
# 覆盖原数据并且重置索引
datas = pd.concat((datas,duplicate_vlaues),axis=0,ignore_index=True)
# step2 :设定我们的缺失值
for _ in range(50):
i = random.choice(rows)
j = random.choice(columns)
datas.iloc[i,j] = np.nan
# step3 : 设定我们的异常值
for _ in range(50):
i = random.choice(rows)
j = random.choice(columns)
exception_value = random.choice(exception_values)
datas.iloc[i,j] = exception_value
# 将我们修改后的数据保存到一个新的csv文件中
datas.to_csv('matt_train.csv')
2.3 统计性数据
假设我们从别的地方得到了一份数据,这份数据就是我们刚才处理后得到的matt_train.csv。设定这是我们第一次见到这个数据。通常的做法和数据提供人员交流,获知每一列代表什么意思。以及异常值的判定、行业相关的常识等。第二步是自己使用一些统计性数据方法查看更多的信息。
2.3.1 万能的info属性
我们拿到数据的第一眼,应该看看有哪些信息。因此,我们可以使用info属性查看信息(设定我们才拿到数据,因此也必须重新导包。当然,如果你进行了上一步的操作并且没有退出jupyter,可以不用再导包。)
import pandas as pd
import numpy as np
import os
os.chdir(r'D:\Data\Datas Analysis\learn\official\base\titanic')
datas = pd.read_csv('matt_train.csv',index_col=0)
datas.info
通过info属性,我们发现了age这一列的数据有着nan缺失值。那么,我们下一步就来看一看哪些列有缺失值。
2.3.2 查看各列缺失值的比例
# 首先根据isnull()方法获得我们的是否是缺失值的901行12列的布尔数组
# 之后再针对布尔数组True=1,False=0的性质求平均值,得到各列缺失值所占的比例。
datas.isnull().mean()
通过数据展示后我们发现,有‘Pclass’,‘Age’,‘Parch’,‘Fare’,‘Cabin’,‘Embarked’这些列有缺失,并且其中‘Age’,‘Cabin’这两列缺失非常严重。这个时候我们应当结合我们的业务需求决定是删除、替换还是推导。此处讲统计信息,因此不展开,下面的缺失值模块会介绍到该内容。
2.3.3 查看重复值信息
# 查看有多少重复行
datas.duplicated().sum()
# 有哪些重复行数据
datas.loc[datas.duplicated(),:]
2.3.4 查看异常值
我们通过上诉方法已经找到了缺失值和重复值,然而我们的异常值还没有找到。name,我们使用什么方法来找到我们的异常值呢?答案是可以使用describe方法查看。
# 查看数值型数据的异常值
datas.describe([.01,.10,.25,.75,.99]).T
# 查看object对象的异常值
datas.describe(include='object').T
2.3.5 一个神奇的三方包
如果你不想记忆上诉的相关代码,又想快速对数据有一个大致的了解,并且找不到谁来告诉你数据代表了什么,那么这个神奇的第三方包你值得拥有。
首先,需要安装该安装包
pip install pandas-profiling
安装完毕之后,直接导入使用即可
import pandas as pd
import numpy as np
import os,pandas_profiling
os.chdir(r'D:\Data\Datas Analysis\learn\official\base\titanic')
datas = pd.read_csv('matt_train.csv',index_col=0)
pandas_profiling.ProfileReport(datas)
绝对值得拥有,强烈推荐!!
2.3.6 可视化寻找异常值
我们也可以通过matplotlib或者seaborn或者plotly画图寻找到异常值和缺失值,不过这个内容放到我们的数据可视化再来进行说明,这里先有这个概念就行了。
2.4 重复值处理
2.3部分我们介绍到了如何快速的了解你的数据,但是没有提及找到了这些不需要的数据如何处理。其实总体上来讲,方法就那么几个。你要么丢弃这些不正常的值,要么使用某种值替换掉这些不正常的值,要么使用算法来推断某些不正常的值(缺失值)(较少使用,且当前还没提到建模,因此不会有实例和演示)。我们根据业务需求基于丢弃和替换这两种方法来进行说明。
2.4.1 pandas处理重复值
首先我们要找到重复值在哪,上文已有介绍,因此直接进入重复值的处理流程。
import pandas as pd
import numpy as np
import os,pandas_profiling
os.chdir(r'D:\Data\Datas Analysis\learn\official\base\titanic')
datas = pd.read_csv('matt_train.csv',index_col=0)
datas_pd = datas.copy()
display(datas_pd.shape)
datas_pd.drop_duplicates(inplace=True,ignore_index=True)
display(datas_pd.shape)
datas_pd.info
现在我们有了处理了重复值的数据,这个时候我的建议是将数据保存为新的一份文件,为了防止之后可能需要重新处理数据或者业务逻辑有问题需要重新进行处理等情况发生。我们每处理完一次数据进行一次保存是非常好的一个习惯。在数据处理、分析、挖掘领域,随时保存是良好的习惯。
datas_pd.to_csv('matt_train_duplicated.csv')
2.5 异常值处理
当我们处理完重复值信息之后,接下来我们可以对异常值进行处理。
2.5.1 pandas处理异常值
# 首先我们查看有哪些异常值
datas_pd.describe([.01,.25,.50,.75,.99]).T
# 我们再查看一下object对象有哪些缺失值
datas_pd.describe(include='object').T
# 我们发现船费‘Fare’明显出现了极大的异常值,同时我们的船上的隔间‘Cabin’出现了‘卯月’明显不对。
# 我们对船费分析,船费的高低和Pclass(船舱等级)有关,因此可以根据船舱等级求得平均数或者众数作为异常值的替换。
# 而对于隔间而言,能否存活和隔间的关系不大,更何况隔间缺失严重,高达76%,因此可以直接丢弃。
datas_pd.drop('Cabin',axis=1,inplace=True)
# 填补船费
# 我们首先根据‘Pclass’查看一下各个等级船舱的平均值
datas_pd.groupby(by='Pclass').mean()
# 查看数据之后我们发现Pclass这一列中也存在3个异常值,因此,我们首先要对这些异常值进行处理。
# 这个时候再使用平均值就没有办法进行查看了,因此我们使用median中位数来查看‘Pclass’这一列的信息
datas_pd.groupby(by='Pclass').median()
# 查看数据后发现,卯月的中位数和2.0的中位数接近,99999999的中位数和2.0的中位数接近,1e-07的中位数和2.0的中位数接近。
# 因此,我们将‘Pclass’的三个异常值全部判定为2.0,在填充之前我们再看一些各个‘Pclass’的类型
datas_pd.loc[:,'Pclass'].unique()
我们发现各个值的类型为string类型,同时对于nan的缺失值我们暂时不处理,等到缺失值处理模块再进行讲解。
# 异常值处理
datas_pd.loc[datas_pd.loc[:,'Pclass']=='1e-07','Pclass']='2.0'
datas_pd.loc[datas_pd.loc[:,'Pclass']=='99999999','Pclass']='2.0'
datas_pd.loc[datas_pd.loc[:,'Pclass']=='卯月','Pclass']='2.0'
# 查看是否还有异常值
datas_pd.groupby(by='Pclass').median()
如此,我们就处理完了Pclass的异常值了,还记得我们的Fare船费异常值么?这里我们使用分类后的median作为船费的异常值填充(不选用平均值是因为平均值收到极大值的影响,非常不准确)
data1 = datas_pd.loc[datas_pd.loc[:,'Fare']>263,:]
index1 = data1.loc[data1.loc[:,'Pclass']=='1.0',:].index.tolist()
datas_pd.iloc[index1,9]=datas_pd.loc[datas_pd.loc[:,'Pclass']=='1.0','Fare'].median()
同理处理Pclass='2.0'和Pclass='3.0’即可
如此我们就处理完了整个Fare和Pclass的异常值。记得保存数据啊!!
2.5.2 一些问题
在处理‘Fare’的异常值时,我们发现‘Pclass’里面也有异常值,但是我们的describe方法并没有反馈给我们。从而使得我们在处理‘Fare’值的情况下必须得先处理‘Pclass’的异常值。那么,有没有办法可以直接把所有不同的列的异常值全部找到呢?
答案是没有,目前还只能自己去寻找各种各样的异常值(再次说明和数据提供人员交流的重要性,他可以告诉我们标准的特征是什么样子),这也是为什么我说要保存好我们的每一步的数据的原因,因为你不知道什么时候就发现了之前处理的数据居然还有异常值。另外一种方法就是使用我们介绍到的pandas_profiling 包,可以找到我们详细的描述信息,再次推荐。
2.5.3 重要提示
我们的异常值并没有处理完毕,事实上,根据我们的异常值添加算法,现在肯定还有没有处理到的异常值,而这个时候就需要靠自己寻找了。我们在处理缺失值得时候肯定会遇上异常值,到时候处理一下异常值。另外,如果和业务人员进行过交流的话,也能找出一些缺失值。不过这里就不再进行详细的操作说明了。如何处理异常值上面的分析案例中已经囊括了。所以,这一部分目前就到此为止。
2.6 缺失值处理
我们的缺失值处理模块,有两个三方包都提供功能,一个是我们的pandas包,一个是我们的sklearn包。因此,这个地方的讲解就分为pandas包处理缺失值和sklearn处理缺失值。
2.6.1 pandas处理缺失值
首先判断哪些列有缺失值
datas_pd.isnull().sum()
还记得我们分析过的船舱隔间‘Cabin’么,属于不需要用上的特征量,并且缺失值实在是太多了。所以我们直接去掉我们的‘Cabin’列。
到现在我们还剩下‘Age’和‘Pclass’,‘Parch’,‘Embarked’这些列有缺失值。接下来我们一个一个的处理。
-
处理Pclass
船舱等级,很显然这个特征和我们的Fare有关联,因此我们首先看一看对于Pclass为nan的值的Fare是多少。
我们观察数据发现值都小于10,和我们之前得到的中位数进行比较后可以判定为3.0等级的船舱,对于其中844对应的异常值,数据量实在太少了,我们可以直接删除。
- 处理'Parch'缺失值
‘Parch’为船上的非直系亲属的亲属数量,对于是否能存活有着一定的影响,但是如何填值呢?观察全表的特征量,我们依旧不能发现有什么相关的其余特征可以用于判定,所以我们尝试使用众数进行填充
填充缺失值后,我们观察数据发现Parch上依旧存有缺失值,因此我们可以把整个Parch列有哪些不重复的特征拿出来看一下。
果然,这该死的异常值,得,又得继续处理异常值先。
由于我们的Parch列没有办法从其他地方拿到相关的数据,因此对于‘卯月’和‘99999999’这两个异常值的列我们也可以使用众数填充,当然在样本量充足的时候直接删除是最快捷的方式。我们采用众数的方式填充。
当我们修改了异常值之后,发现无论怎么处理我们的nan异常值还是无法修改,并且我们的缺失值还上升了,从6变成了12,翻了一倍。这种情况下,要么继续死磕,要么直接删除异常值。
进行了语句检查之后,发现不是逻辑的原因。那我们尝试直接删除缺失值吧。
记得恢复索引哈
PS:之所以我们一直不能填充值进去的原因是我们使用的是一个object对象去填充,然而我们索引取出来的是一个值。因此我们要使用一个值去填充我们的数据,所以应当使用.values属性取到众数的值。 -
处理Fare薪资的缺失值
我们的Fare薪资明显和我们的船舱等级有关,因此利用我们的船舱等级进行分类填补
-
处理Embarked缺失值
登船港口的缺失值和其余的列的特征没有任何关系,一次我们也可以要么丢弃,要么使用众数填充。我这里使用众数填充一下。
-
处理Age的缺失值
我们的Age和什么有关呢?找了找,唯一能有点关联的就是Sex这个特征了,因此我们根据性别不同进行不同的数据填充。
2.6.2 dataframe.fillna()方法
fillna方法是pandas自带的方法,支持按照指定的列填充指定的值,但是无法做到像我们上面那样的针对不同的分类进行填充。我们可以看一下它的源码,有非常详尽的解释和案例。
datas_pd.fillna(
value=None,
method=None,
axis=None,
inplace=False,
limit=None,
downcast=None,
) -> Union[ForwardRef('DataFrame'), NoneType]
Docstring:
Fill NA/NaN values using the specified method.
Parameters
----------
value : scalar, dict, Series, or DataFrame
Value to use to fill holes (e.g. 0), alternately a
dict/Series/DataFrame of values specifying which value to use for
each index (for a Series) or column (for a DataFrame). Values not
in the dict/Series/DataFrame will not be filled. This value cannot
be a list.填充值,不能是列表,可以使标量值(确定的值),Series或者DataFrame。也可以是字典,列名作为键,填充方法值作为value。
method : {'backfill', 'bfill', 'pad', 'ffill', None}, default None
Method to use for filling holes in reindexed Series
pad / ffill: propagate last valid observation forward to next valid
backfill / bfill: use next valid observation to fill gap.
axis : {0 or 'index', 1 or 'columns'}
Axis along which to fill missing values.
inplace : bool, default False
If True, fill in-place. Note: this will modify any
other views on this object (e.g., a no-copy slice for a column in a
DataFrame).
limit : int, default None
If method is specified, this is the maximum number of consecutive
NaN values to forward/backward fill. In other words, if there is
a gap with more than this number of consecutive NaNs, it will only
be partially filled. If method is not specified, this is the
maximum number of entries along the entire axis where NaNs will be
filled. Must be greater than 0 if not None.
downcast : dict, default is None
A dict of item->dtype of what to downcast if possible,
or the string 'infer' which will try to downcast to an appropriate
equal type (e.g. float64 to int64 if possible).
2.6.3 sklearn的缺失值填充
在sklearn中,可以使用impute.SimpleImputer进行缺失值的填充。源码如下:
class sklearn.impute.SimpleImputer (missing_values=nan, strategy=’mean’, fill_value=None, verbose=0,
copy=True)
每个属性对应的效果如下
参数 | 含义 |
---|---|
missing_values | 告诉SimpleImputer,数据的缺失值长什么样子,默认是控制np.nan |
strategy | 我们填补缺失值的策略,默认是均值。 输入‘mean’针对数值型使用均值填充 输入median针对数值型使用中位数填充 输入most_frequent针对数值型和字符型用众数填充 输入‘constant’填充特定值,和fill_value搭配使用 |
fil_value | 当strategy的参数为constant的时候起作用,可输入字符串或者数字 |
copy | 默认为True,创建副本,否则会将缺失值填补到原有的数据中。 |
三:一些重点
目前我们进行的都只是数据的清理,也就是说我们现在操作的数据都是原本数据集本来的结构。不涉及到特征量的选择,不涉及到降维、升维、也不涉及到无量纲化等,更不涉及转换特征。不过即便如此,数据的清洗依旧是重要的一步。需要结合我们实际的业务需求来进行清洗,多和数据提供人员交流是非常好的一个习惯,记得随时保存处理过得数据也是很好的一个习惯。
这就是数据清理入门的教程了,下一份教程和特征工程相关,欢迎继续阅读。
说明
- 本文由我本人原创,发布于简书卯月七账号、知乎卯月七账号、CSDN卯月七账号。
- 本文允许转载、学习,转载请注明出处,谢谢。
- 作者邮箱mcj6989@163.com,有问题可以联系。
- 本文为我整理的数据清洗的入门文章,更多知识可以购买专业书籍学习。
- 创作不易,如果对你有帮助,希望能给我一些反馈,包括不限于点赞,评论,转发,非常感谢!!