在处理任何数据以前,咱们的第一任务是理解数据以及数据是干什么用的。咱们尝试去理解数据的列/行、记录、数据格式、语义错误、缺失的条目以及错误的格式,这样咱们就能够大概了解数据分析以前要作哪些“清理”工做。html
本次咱们须要一个 patient_heart_rate.csv (连接:https://pan.baidu.com/s/1geX8oYf 密码:odj0)的数据文件,这个数据很小,可让咱们一目了然。这个数据是 csv 格式。数据是描述不一样个体在不一样时间的心跳状况。数据的列信息包括人的年龄、体重、性别和不一样时间的心率。app
import pandas as pd df = pd.read_csv('../data/patient_heart_rate.csv') df.head()
下面咱们就针对上面的问题一一击破。优化
1. 没有列头编码
若是咱们拿到的数据像上面的数据同样没有列头,Pandas 在读取 csv 提供了自定义列头的参数。下面咱们就经过手动设置列头参数来读取 csv,代码以下:spa
import pandas as pd # 增长列头 column_names= ['id', 'name', 'age', 'weight','m0006','m0612','m1218','f0006','f0612','f1218'] df = pd.read_csv('../data/patient_heart_rate.csv', names = column_names) df.head()
上面的结果展现了咱们自定义的列头。咱们只是在此次读取 csv 的时候,多了传了一个参数 names = column_names,这个就是告诉 Pandas 使用咱们提供的列头。code
2. 一个列有多个参数orm
在数据中不难发现,Name 列包含了两个参数 Firtname 和 Lastname。为了达到数据整洁目的,咱们决定将 name 列拆分红 Firstname 和 Lastnamehtm
从技术角度,咱们能够使用 split 方法,完成拆分工做。blog
咱们使用 str.split(expand=True),将列表拆成新的列,再将原来的 Name 列删除索引
# 切分名字,删除源数据列 df[['first_name','last_name']] = df['name'].str.split(expand=True) df.drop('name', axis=1, inplace=True)
上面就是执行执行代码以后的结果。
3. 列数据的单位不统一
若是仔细观察数据集能够发现 Weight 列的单位不统一。有的单位是 kgs,有的单位是 lbs
# 获取 weight 数据列中单位为 lbs 的数据 rows_with_lbs = df['weight'].str.contains('lbs').fillna(False) df[rows_with_lbs]
为了解决这个问题,将单位统一,咱们将单位是 lbs 的数据转换成 kgs。
# 将 lbs 的数据转换为 kgs 数据 for i,lbs_row in df[rows_with_lbs].iterrows(): weight = int(float(lbs_row['weight'][:-3])/2.2) df.at[i,'weight'] = '{}kgs'.format(weight)
4. 缺失值
在数据集中有些年龄、体重、心率是缺失的。咱们又遇到了数据清洗最多见的问题——数据缺失。通常是由于没有收集到这些信息。咱们能够咨询行业专家的意见。典型的处理缺失数据的方法:
5. 空行
仔细对比会发现咱们的数据中一行空行,除了 index 以外,所有的值都是 NaN。
Pandas 的 read_csv() 并无可选参数来忽略空行,这样,咱们就须要在数据被读入以后再使用 dropna() 进行处理,删除空行.
# 删除全空的行 df.dropna(how='all',inplace=True)
6. 重复数据
有的时候数据集中会有一些重复的数据。在咱们的数据集中也添加了重复的数据。
首先咱们校验一下是否存在重复记录。若是存在重复记录,就使用 Pandas 提供的 drop_duplicates() 来删除重复数据。
# 删除重复数据行 df.drop_duplicates(['first_name','last_name'],inplace=True)
7. 非 ASCII 字符
在数据集中 Fristname 和 Lastname 有一些非 ASCII 的字符。
处理非 ASCII 数据方式有多种
咱们使用删除的方式:
# 删除非 ASCII 字符 df['first_name'].replace({r'[^\x00-\x7F]+':''}, regex=True, inplace=True) df['last_name'].replace({r'[^\x00-\x7F]+':''}, regex=True, inplace=True)
8. 有些列头应该是数据,而不该该是列名参数
有一些列头是有性别和时间范围组成的,这些数据有多是在处理收集的过程当中进行了行列转换,或者收集器的固定命名规则。这些值应该被分解为性别(m,f),小时单位的时间范围(00-06,06-12,12-18)
# 切分 sex_hour 列为 sex 列和 hour 列 sorted_columns = ['id','age','weight','first_name','last_name'] df = pd.melt(df, id_vars=sorted_columns,var_name='sex_hour',value_name='puls_rate').sort_values(sorted_columns) df[['sex','hour']] = df['sex_hour'].apply(lambda x:pd.Series(([x[:1],'{}-{}'.format(x[1:3],x[3:])])))[[0,1]] df.drop('sex_hour', axis=1, inplace=True) # 删除没有心率的数据 row_with_dashes = df['puls_rate'].str.contains('-').fillna(False) df.drop(df[row_with_dashes].index, inplace=True)
import pandas as pd # 增长列头 column_names= ['id', 'name', 'age', 'weight','m0006','m0612','m1218','f0006','f0612','f1218'] df = pd.read_csv('../data/patient_heart_rate.csv', names = column_names) # 切分名字,删除源数据列 df[['first_name','last_name']] = df['name'].str.split(expand=True) df.drop('name', axis=1, inplace=True) # 获取 weight 数据列中单位为 lbs 的数据 rows_with_lbs = df['weight'].str.contains('lbs').fillna(False) df[rows_with_lbs] # 将 lbs 的数据转换为 kgs 数据 for i,lbs_row in df[rows_with_lbs].iterrows(): weight = int(float(lbs_row['weight'][:-3])/2.2) df.at[i,'weight'] = '{}kgs'.format(weight) # 删除全空的行 df.dropna(how='all',inplace=True) # 删除重复数据行 df.drop_duplicates(['first_name','last_name'],inplace=True) # 删除非 ASCII 字符 df['first_name'].replace({r'[^\x00-\x7F]+':''}, regex=True, inplace=True) df['last_name'].replace({r'[^\x00-\x7F]+':''}, regex=True, inplace=True) # 切分 sex_hour 列为 sex 列和 hour 列 sorted_columns = ['id','age','weight','first_name','last_name'] df = pd.melt(df, id_vars=sorted_columns,var_name='sex_hour',value_name='puls_rate').sort_values(sorted_columns) df[['sex','hour']] = df['sex_hour'].apply(lambda x:pd.Series(([x[:1],'{}-{}'.format(x[1:3],x[3:])])))[[0,1]] df.drop('sex_hour', axis=1, inplace=True) # 删除没有心率的数据 row_with_dashes = df['puls_rate'].str.contains('-').fillna(False) df.drop(df[row_with_dashes].index, inplace=True) # 重置索引,不作也不要紧,主要是为了看着美观一点 df = df.reset_index(drop=True) print(df)
还有一些问题在本例中没有说起内容,下面有两个比较重要,也比较通用的问题:
本次又介绍了一些关于 Pandas 清洗数据的技能。至少用这几回介绍的处理方法,应该能够对数据作不少清洗工做。
更多关于数据清洗的内容能够关注知乎上的专栏“数据清洗”