当前位置: 首页 > 后端技术 > Python

Python数据清洗(二):缺失值识别与处理

时间:2023-03-26 17:26:14 Python

作者|刘顺祥来源|数据分析1480前言在《Python数据清洗(一):类型转换和冗余数据删除》分享了两个关于数据类型转换和冗余信息删除的知识点,然后继续讲解识别和处理缺失值。缺失值是指由于人为或机器原因造成的数据记录的丢失或隐匿。缺失值的存在会在一定程度上影响后续数据分析和挖掘的结果,因此其处理会显得尤为重要。缺失值的识别判断数据集中是否存在缺失观测值通常从两个方面入手,一个是变量的角度,即判断每个变量是否包含缺失值;另一种是数据行的角度,即判断每一行的数据是否存在缺失值。关于缺失值的判断可以使用isnull方法。接下来使用isnull方法判断data3数据(中间可以下载数据),统计输出的结果如下表所示。#判断每个变量中是否有缺失值data3.isnull().any(axis=0)#每个变量中缺失值个数data3.isnull().sum(axis=0)#所占比例ofmissingvaluesineachvariabledata3.isnull().sum(axis=0)/data3.shape[0]如上结果所示,数据集data3中有三个变量存在缺失值,即性别、年龄和学历,缺失数分别为136、100和1927,缺失率分别为4.53%、3.33%和64.23%。需要注意的是,判断数据是否为缺失值NaN,可以使用isnull“方法”,该方法会返回一个与原始数据行数列数相同的矩阵,矩阵的元素为bool类型值。为了得到每一列的判断结果,仍然需要任何“方法”(并且“方法”中的axis参数设置为0);统计每个变量缺失值的个数,可以在isnull的基础上使用sum“法”(也需要将axis参数设置为0);计算缺失率是根据缺失数除以总样本量(shape方法返回数据集的行数和列数,[0]表示取出相应的数据行数)。读者可能会对代码中的“axis=0”一头雾水,它代表什么?为什么是0?是否可以写入其他值?下面以图表的形式说明axis参数的用法:假设上图是学生的考试成绩表,如果直接把成绩表中的分数相加,就会得到所有学生的成绩总和(显然没有意义),如果按学生单独计算总分,就是上图中从左到右的变换。这种转换的特点是列数发生了变化(可以是列数减少,也可以是列数增加),类似于水平方向的外压或拉力,这样的外力理解为axisaxis为1Effect(容易理解,可以想象飞机在通电时可以保持水平飞行状态)。同样对于上面的学生成绩表,如果直接对成绩表中的分数进行平均计算,得到的是所有学生的平均分(显然是没有意义的)。如果按科目计算平均分,就是上图从上到下的Transition。这种转换的特点是行数发生了变化(可以是行数减少,也可以是行数增加),类似于垂直方向被外界挤压或拉伸。这样的外力可以理解为axis轴为0的效果(容易理解,可以想象飞机在没有动力的情况下处于下降趋势)。以上就是判断变量缺失值的过程,下面的代码也可以用来识别缺失值在数据行中的分布情况:#判断数据行中是否存在缺失值如图以上结果,返回一个True值,说明data3中的数据行存在缺失值。any“方法”在代码中使用了两次,第一次用于判断True(即行中有缺失值)或False值(即行中不存在缺失值))对应每一行;第二次用于综合判断所有数据行是否包含缺失值。同理,可以进一步判断缺失行的具体数量和比例。代码如下:#缺失行数data3.isnull().any(axis=1).sum()#缺失观测值比例data3.isnull().any(axis=1).sum()/data3.shape[0]如上结果所示,3000行数据集中有2024行缺失值,缺失行的比例约为67.47%。不管是从变量的角度判断缺失值,还是从数据行的角度判断缺失值,一旦发现缺失值,就需要进行相应的处理,否则数据分析或挖掘的准确性会受到一定程度的影响。缺失值的处理方法通常是针对缺失值的处理,最常用的方法无非是删除法、替换法和插补法。删除法是指删除缺失值所在的观察行(前提是缺失行的比例很低,比如在5%以内),或者删除缺失值对应的变量(前提是缺失的比例变量包含的值很高,比如70%左右);替换法是指直接用缺失变量的均值、中位数或众数来替换变量中的缺失值。缺失值替换精度下降;插补法使用有监督的机器学习方法(如回归模型、树模型、网络模型等)来预测缺失值。缺失值的计算会大大降低处理速度。下面将选择删除法、替换法和插值法对缺失值进行处理。代码如下:#删除字段——比如删除缺失率非常高的edu变量data3.drop(labels='edu',axis=1,inplace=True)#数据预览data3.head()As如上结果所示,表中的edu变量已被成功删除。对于字段删除,可以选择drop“方法”,其中labels参数用于指定要删除的变量名。如果有多个变量,需要将这些变量名写在一对方括号中(如['var1','var2','var3']);删除一个变量,axis参数必须设置为1,因为变量个数发生了变化(所以观察线也可以借助axis参数删除);inplace表示是否就地修改,即是否直接删除原表中的字段,这里设置为True,如果设置为False,则输出删除变量的预览效果,而不是实际改变原始数据。#删除观测值,——比如删除年龄变量对应的缺失观测值data3_new=data3.drop(labels=data3.index[data3['age'].isnull()],axis=0)#查看scaleof数据data3_new.shapeout:(2900,5)如上结果所示,数据行的删除是使用drop“方法”实现的,但是axis参数必须设置为0,此时labels参数需要指定要删除的行号。这里的行号是通过index“方法”(用于返回原始数据的行号)和isnull“方法”(用于判断数据是否缺失,缺失则返回True)实现的.逻辑是将True对应的行号取出来,传递给labels参数。如果变量的缺失率很大,或者缺失行的比例很小,使用删除法是一个不错的选择。否则会丢失大量数据信息,得不偿失。接下来,我将解释如何使用替换方法来处理缺失值。代码如下:#处理缺失值的替换方法data3.fillna(value={'gender':data3['gender'].mode()[0],#使用性别数字替换缺失的性别'age':data3['age'].mean()#用年龄的均值代替缺失的年龄},inplace=True#原地修改数据)#再次检查每个变量的缺失率data3.isnull().sum(axis=0)如上结果所示,使用替换方法后,原始数据中的变量不再包含缺失值。缺失值的填充使用fillna“方法”,其中value参数可以以字典的形式为不同的变量指定不同的值。需要强调的是,如果计算一个变量的众数,必须使用索引技术,比如代码中的[0],表示取出众数序列中的第一个(我们知道众数是指最频繁出现的值,假设一个变量中有多个值共享频率最高,那么Python会以序列的形式存储这些值,所以必须使用索引来检索指定的多数值).上面说到,替换法虽然思想简单,效率高,但是它替换的值往往不是很准确,所以插值法出现了。这种方法需要使用机器学习算法。以KNN算法为例,对Titanic数据集中的Age变量进行插补,完成缺失值的处理。代码如下:#读取数据titanic=pd.read_csv('Titanic.csv')#删除严重缺失的Cabin变量titanic.drop(labels='Cabin',axis=1,inplace=True)#根据Embarked变量,删除对应的缺失行titanic.dropna(subset=['Embarked'],inplace=True)#删除不相关的变量(这些变量对后面预测年龄帮助不大)titanic.drop(labels=['PassengerId','Name','Ticket','Embarked'],axis=1,inplace=True)#将角色性别变量映射到数值变量titanic.Sex=titanic.Sex.map({'male':1,'female':0})#把数据分成两组,一组是年龄缺失组,一组是年龄非缺失组,然后根据非缺失值建立KNN模型,然后预测缺失组nomissing=titanic.loc[~titanic.Age.isnull(),]missing=titanic.loc[titanic.Age.isnull(),]#从sklearnimportneighbors导入机器学习的第三方包#提取所有自变量X=nomissing.columns[nomissing.columns!='Age']#构建模型knn=neighbors.KNeighborsRegressor()#模型拟合knn.fit(nomissing[X],nomissing.Age)#年龄预测pred_age=knn.predict(missing[X])