数据分析入门系列教程-数据清洗

公众号后台回复“图书“,了解更多号主新书内容

 作者:周萝卜

 来源:萝卜大杂烩

从今天开始,我们再一起来学习数据分析,共同进步!
首先先来进行一个数据清洗的实战,使用比较经典的数据集,泰坦尼克号生存预测数据。

数据集下载地址

https://github.com/zhouwei713/DataAnalyse/tree/master/Titanic_dataset

导入数据

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
df = pd.read_csv('titanic_data.csv')
df

数据集信息如下:

数据分析入门系列教程-数据清洗_第1张图片

各字段含义

pclass:船票等级

sibsp:一同登船的兄弟姐妹或配偶数量

parch:一同登船的父母或子女数量

ticket:船票号码

fare:船票价格

cabin:船舱

embarked:登船地点

数据整体查看

拿到数据之后,我们先整体查看下数据信息

df.describe()

数据分析入门系列教程-数据清洗_第2张图片

还有些列是非数值的,所以没有展示。

这里得到的各项指标,我们先保存不动,在后面处理缺失值时会有用到。

处理缺失值

首先查看缺失值

df.isnull().sum()
>>>
pclass         1
survived       1
name           1
sex            1
age          264
sibsp          1
parch          1
ticket         1
fare           2
cabin       1015
embarked       3
dtype: int64

可以看到,缺失值比较多的是 cabin 和 age

年龄处理

查看缺失占比

print('缺失占比 %.2f%%' %((df['age'].isnull().sum()/df.shape[0])*100))
>>>
缺失占比 20.15%

下面查看下年龄的分布情况

age = df['age'].hist(bins=15, color='teal', alpha=0.6)
age.set(xlabel='age')
plt.xlim(-10,85)
plt.show()

数据分析入门系列教程-数据清洗_第3张图片

从图中我们可以看出,整体数据是向左偏的,即大多数数据是小于平均值的,故而我们可以采用中位数来填补空值,而不是平均数。

从上面的 describe 函数的输出值也可以看出,平均值是 29.88,中位数是 28,显然中位数更加接近于大部分数据所在的区域。

使用中位数填充空缺的年龄值

data = df.copy()
data['age'].fillna(df['age'].median(skipna=True), inplace=True)

仓位处理

查看缺失百分比

print('缺失百分比 %.2f%%' %((df['cabin'].isnull().sum()/df.shape[0])*100))
>>>
缺失百分比 77.48%

由于仓位信息已经缺失了大部分,所以这里选择直接删除处理

data.drop(columns=['cabin'], inplace=True)

登船地点处理

我们先来查看下登船地点这列数据的形式

print(df['embarked'].value_counts())
sns.countplot(x='embarked', data=df, palette='Set2')
plt.show()
>>>
S    914
C    270
Q    123
Name: embarked, dtype: int64

数据分析入门系列教程-数据清洗_第4张图片

可以看到,登船地点总共包含三类数据,S、C 和 Q,他们出现的次数分别为 914、270 和 123。

又因为该列数据总共缺失 3 个,缺失率很低,使用众数来填充这三个缺失值应该是没问题的。

使用众数填充

data['embarked'].fillna(df['embarked'].value_counts().idxmax(), inplace=True)

其他缺失值处理

对于其他列,只是缺失了一到两个,可以采用众数的方式来填充缺失值,也可以选择直接删除掉缺失的部分,不影响整体数据分布

data.dropna(axis=0, how='any', inplace=True)

最后,再查看确认下是否不存在缺失值了

data.isnull().sum()
>>>
pclass      0
survived    0
name        0
sex         0
age         0
sibsp       0
parch       0
ticket      0
fare        0
embarked    0
dtype: int64

其他特征列处理

对于 sibsp 和 parch 两列,我们可以抽象成是否是独自登船,这样就能够把两列合并为一列,并用 0,1 来表示是否独自登船。

我们新增一列 alone,把两列都是 0 的数据添加到新列中并设置为 0,把两列相加不为 0 的数据添加到新列中,并设置数值为 1。那么原来的两列就可以删除了。

data['alone']=np.where((data["sibsp"]+data["parch"])>0, 0, 1)
data.drop('sibsp', axis=1, inplace=True)
data.drop('parch', axis=1, inplace=True)
data.head()

数据分析入门系列教程-数据清洗_第5张图片

对于 embarked 和 sex 这两列,都是字符串类型的数据,需要转化为数字才能被算法模型分析处理。

这里可以采用独热编码的方式,来转换数据

data =pd.get_dummies(data, columns=["embarked","sex"])
data.head()

数据分析入门系列教程-数据清洗_第6张图片

独热编码(one-hot encoding),是一种常用的数据转换方式,对于每一个特征,如果它有 m 个可能值,那么经过独热编码后,就变成了 m 个二元特征,这些特征互斥,每次只有一个激活。

对于 name 和 ticket 两列,由于他们的存在,对于我们的数据分析没有任何意义,往往会直接删除掉这样无意义的数据

data.drop('name', axis=1, inplace=True)
data.drop('ticket', axis=1, inplace=True)

至此,我们就把一份原始的数据,处理成了比较标准的,易于数据分析的数据。

透视表分析

在处理数据之后,我们还可以使用透视表,整体分析下数据

这里主要查看下各个特征(船票等级,性别,仓位等)对于存活率的影响

注意数据集 df 与 data 的区别

性别透视表

首先来看下,不同性别,存活率的情况

sex_sur_table = pd.pivot_table(df, index=['sex'], values='survived')
print(sex_sur_table)
>>>
        survived
sex             
female  0.727468
male    0.190985

女性存活率是远远高于男性的,ladies first。

船票等级与存活率

pclass_sur_table = pd.pivot_table(df, index=['sex'], columns=['pclass'], values='survived')
print(pclass_sur_table)
>>>
pclass       1.0       2.0       3.0
sex                                 
female  0.965278  0.886792  0.490741
male    0.340782  0.146199  0.152130

可以看到,一等船票的女性存活率是非常高的,同时船票等级越高,无论男女,存活率都越高

不同年龄存活率

将年龄离散化处理

data['age_cut'] = pd.cut(data['age'], [0, 18, 90])
data['sex'] = df['sex']
print(data.head())
>>>
   pclass  survived      age      fare  alone  embarked_C  embarked_Q  \
0     1.0       1.0  29.0000  211.3375      1           0           0   
1     1.0       1.0   0.9167  151.5500      0           0           0   
2     1.0       0.0   2.0000  151.5500      0           0           0   
3     1.0       0.0  30.0000  151.5500      0           0           0   
4     1.0       0.0  25.0000  151.5500      0           0           0      embarked_S  sex_female  sex_male   age_cut     sex  
0           1           1         0  (18, 90]  female  
1           1           0         1   (0, 18]    male  
2           1           1         0   (0, 18]  female  
3           1           0         1  (18, 90]    male  
4           1           1         0  (18, 90]  female  

年龄段与存活率

age_cut_sur_table = pd.pivot_table(data, index=['sex'], columns=['pclass', 'age_cut'], values='survived')
print(age_cut_sur_table)
>>>
pclass        1.0                 2.0                 3.0          
age_cut   (0, 18]  (18, 90]   (0, 18]  (18, 90]   (0, 18]  (18, 90]
sex                                                                
female   0.923077  0.969466  0.952381  0.870588  0.534483  0.474684
male     0.750000  0.321637  0.523810  0.093333  0.208333  0.142857

当然,透视表还有很多强大的功能,你可以试着探索更多。

数据清洗的重要性

要知道,一个好的数据分析师必定是一名数据清洗高手。在数据分析的过程中,数据清洗是最占用时间与精力的步骤。数据质量的高低,直接影响我们最后分析的结果,千万马虎不得。

数据质量的准则

那么既然数据清洗这么重要,我需要把原始数据处理到什么程度,才算是合格的待分析数据呢?如下我总结了一些业界的标准,可以供你参考。

  • 完整性:数据集中是否存在空值,统计的字段是否完善。

  • 全面性:某列数据,是否能够全面的反应真实的情况,是否只包含一部分情况。

  • 合法性:数据的类型,内容,大小等是否合理。比如:是否有年龄超过 150 的,是否有成绩超过 1 万的,数据单位是否统一等等。

  • 唯一性:数据是否存在重复记录。

在进行数据清洗的时候,一定要先耐心的观察数据,充分的理解每列数据的意义,从真实的情况出发分析数据是否有真实的含义,再根据生活工作中的经验,来逐一处理数据。

我们再用一个小例子来理解下

姓名 身高 体重 年龄 年龄
张飞 180
500
500
关羽 181 100 28
28
刘备 1.78 160 30K
30k
赵云 175 140 23
23
曹操 180 150 37
37
赵云 175
140
23
曹操



把数据转化成 Pandas 数据结构

mydata = pd.read_csv('mydata.csv', index_col='name')
print(mydata)
>>>
      height  weight  age age.1
name                           
张飞    180.00     NaN  500   500
关羽    181.00   100.0   28    28
刘备      1.78   160.0  30K   30K
赵云    175.00   140.0   23    23
曹操    180.00   150.0   37    37
赵云    175.00   140.0   23    23
典韦       NaN     NaN  NaN   NaN

完整性

查看缺失值

mydata1 = mydata.copy()  # copy
mydata1.isnull().sum()  # 查看总体缺失值
>>>
height    1
weight    2
age       1
age.1     1
dtype: int64

一般是处理空值,空行等

mydata1['weight'].fillna(mydata1['weight'].mean(), inplace=True)  # 使用平均值填充
#mydata1['height'].fillna(mydata['height'].value_counts().index[0], inplace=True)  # 使用众数填充
mydata1.dropna(how='any', inplace=True)  # 删除空行

一定要先执行空值填充,再执行删除空行的代码,否则含有空值的行都会被删除。

全面性

刘备的身高是“米”的单位,我们需要转换为“厘米”

mydata1.loc[mydata1['height']<100, 'height'] = mydata1[mydata1['height']<100]['height']*100

合理性

张飞的年龄是 500,显然不合理,需要处理。因为张飞是三弟,年龄需要比刘备和关羽小,就设置为 27 吧

mydata1.loc['张飞', 'age'] = 27

同时刘备的年龄还存在一个 K 字符,需要去掉

mydata1['age'].replace({r'[K]': ''}, regex=True, inplace=True)

唯一性

数据中还存在重复的行和列,也需要删除,保证数据的唯一性

mydata1.drop_duplicates(inplace=True)  # 删除重复行
mydata1.drop('age.1', axis=1, inplace=True)  # 删除不需要的列

最终我们的数据为

print(mydata1)
>>>
      height  weight age
name                    
张飞     180.0   138.0  27
关羽     181.0   100.0  28
刘备     178.0   160.0  30
赵云     175.0   140.0  23
曹操     180.0   150.0  37

总结

本节我们共同完成了一个数据清洗的实战和一个练习小例子。对于缺失值,需要根据其缺失的百分比及数据分布情况,来决定如何填充缺失值。对于一些非数字类型的数据,可以选择独热编码等方式转换数据。还总结了数据清洗的准则,只要你遵循这些准则来处理数据,那么得到的数据基本就是“好”的数据了。

数据分析入门系列教程-数据清洗_第7张图片

练习题

对于本节的例子,你还有哪些观点,对于缺失值的填充,是否还有其他的方式呢?对于 pclass,sex 等数据的缺失值,还可以怎么处理呢,欢迎留言讨论啊

◆ ◆ ◆  ◆ ◆
麟哥新书已经在当当上架了,我写了本书:《拿下Offer-数据分析师求职面试指南》,目前当当正在举行400-240活动,大家可以用相当于原价4折的预购价格购买,还是非常划算的,扫描下方小程序即可进入购买页面:

数据森麟公众号的交流群已经建立,许多小伙伴已经加入其中,感谢大家的支持。大家可以在群里交流关于数据分析&数据挖掘的相关内容,还没有加入的小伙伴可以扫描下方管理员二维码,进群前一定要关注公众号奥,关注后让管理员帮忙拉进群,期待大家的加入。

管理员二维码:


猜你喜欢

● 麟哥拼了!!!亲自出镜推荐自己新书《数据分析师求职面试指南》● 厉害了!麟哥新书登顶京东销量排行榜!● 笑死人不偿命的知乎沙雕问题排行榜
● 用Python扒出B站那些“惊为天人”的阿婆主!● 你相信逛B站也能学编程吗

你可能感兴趣的:(数据分析,数据挖掘,分布式存储,统计学,大数据)