Python数据清洗综合案例
文章目录
干净整洁的数据是后续进行研究和分析的基础。数据科学家们会花费大量的时间来清理数据集,毫不夸张地说,数据清洗会占据80%的工作时间,而真正用来分析数据的时间只占到20%左右。 数据清洗到底是在清洗些什么?通常来说,所获取到的原始数据不能直接用来分析,因为它们会有各种各样的问题,如包含无效信息,列名不规范、格式不一致,存在重复值,缺失值,异常值等…
本例设计一个log.txt⽂件,该文件记录了某个项⽬中某个 api 的调⽤情况,采样时间为每分钟⼀次,包括调⽤次数、响应时间等信息,⼤约18万条数据。下⾯进⾏探索性数据分析。
一. 导⼊数据
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
plt.rc('font', **{'family':'SimHei'})
# 从log.txt导⼊数据
data = pd.read_table('log.txt', header=None,names=['id', 'api', 'count', 'res_time_sum', 'res_time_min','res_time_max', 'res_time_avg', 'interval', 'created_at'])
# 或者分开来
data = pd.read_table('log.txt', header=None)
data.columns = ['id', 'api', 'count', 'res_time_sum', 'res_time_min','res_time_max', 'res_time_avg', 'interval', 'created_at']
# 看⼀下前⾯⼏个
print( data.head() )
'''
id api count res_time_sum res_time_min res_time_max res_time_avg interval created_at
0 162542 /front-api/bill/create 8 1057.31 88.75 177.72 132.0 60 2017-11-01 00:00:07
1 162644 /front-api/bill/create 5 749.12 103.79 240.38 149.0 60 2017-11-01 00:01:07
2 162742 /front-api/bill/create 5 845.84 136.31 225.73 169.0 60 2017-11-01 00:02:07
3 162808 /front-api/bill/create 9 1305.52 90.12 196.61 145.0 60 2017-11-01 00:03:07
4 162943 /front-api/bill/create 3 568.89 138.45 232.02 189.0 60 2017-11-01 00:04:07
'''
# 随机抽取5个查看
print( data.sample(5) )
'''
id api count res_time_sum res_time_min res_time_max res_time_avg interval created_at
46438 3928038 /front-api/bill/create 3 469.92 129.04 206.80 156.0 60 2017-12-25 00:49:45
88401 6731328 /front-api/bill/create 3 467.23 115.77 182.61 155.0 60 2018-02-12 13:13:08
63324 5060491 /front-api/bill/create 8 1044.43 93.58 276.91 130.0 60 2018-01-13 20:00:19
132049 9789375 /front-api/bill/create 3 433.51 95.90 173.21 144.0 60 2018-04-07 00:15:22
169689 12678652 /front-api/bill/create 9 2070.40 76.71 768.48 230.0 60 2018-05-19 22:16:10
'''
二. 了解数据
拿到一个全新的数据集,应该从哪里入手?没错,我们需要先了解数据,看看它长什么样子。常用的方法和属性如下:
如果上面这些操作还不够直观的话,就作图看看,需要先导入Python可视化库matplotlib。
三. 清洗数据的基本方法
了解数据集之后,我们就可以开始对数据集进行清洗了,通常要处理的问题包括无效信息,列名不规范、格式不一致,存在重复值,缺失值,异常值等,下面我们一个一个来看。
第1步,去除不需要的行、列。
在分析一个数据集的时候,很多信息其实是用不到的,因此,需要去除不必要的行或列。在导入的时候就可以通过设置pd.read_xxx()里面的参数来实现列的选择目的。
如果在数据导入之后,还想删除某些行和列,可以用 .drop() 方法。先创建一个列表list,把不需要的列名放进去,再调用.drop() 方法,参数axis为1时代表列,为0时代表行,参数inplace=True表示不创建新的对象,直接对原始对象进行修改。
第2步,重新命名列。
当原始数据的列名不好理解,或者不够简洁时,可以用.rename()方法进行修改。这把英文的列名改成中文,先创建一个字典,把要修改的列名定义好,然后调用rename()方法。
第3步,重新设置索引。
数据默认的索引是从0开始的有序整数,但如果想把某一列设置为新的索引,可以用.set_index()方法实现。
第4步,用字符串操作规范列。
字符串str操作是非常实用的,因为列中总是会包含不必要的字符,常用的方法如下:
第5步,用函数规范列。
在某些情况下,数据不规范的情况并不局限于某一列,而是更广泛地分布在整个表格中。因此,自定义函数并应用于整个表格中的每个元素会更加高效。用applymap()方法可以实现这个功能,它类似于内置的map()函数,只不过它是将函数应用于整个表格中的所有元素。
第6步,删除重复数据。
重复数据会消耗不必要的内存,在处理数据时执行不必要的计算,还会使分析结果出现偏差。因此,我们有必要学习如何删除重复数据。
第7步,填充缺失值。
数据集中经常会存在缺失值,学会正确处理它们很重要,因为在计算的时候,有些无法处理缺失值,有些则在默认情况下跳过缺失值。而且,了解缺失的数据,并思考用什么值来填充它们,对做出无偏的数据分析至关重要。
四. 案例:检查异常
针对上面导入的log.txt数据,下面编写程序来检查导入异常。完整示例代码
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
plt.rc('font', **{'family':'SimHei'})
# 从log.txt导⼊数据
data = pd.read_table('log.txt', header=None,names=['id', 'api', 'count', 'res_time_sum', 'res_time_min','res_time_max', 'res_time_avg', 'interval', 'created_at'])
# 检查是否有重复值
print( data.duplicated().sum() ) # 0
# 检查是否有空值
print( data.isnull().sum() )
'''
id 0
api 0
count 0
res_time_sum 0
res_time_min 0
res_time_max 0
res_time_avg 0
interval 0
created_at 0
dtype: int64
'''
# 分析 api 和 interval 这两列的数据是否对分析有⽤
print( len(data) ) # 得到 179496
print( len(data[data['interval'] == 60]) ) # 得到 179496
print( len(data[data['api'] == '/front-api/bill/create']) ) # 得到 179496
# 查看api字段信息,可以发现unique=1,也就是说只有⼀个值,所以是没有意义的
print( data['api'].describe() )
'''
count 179496
unique 1
top /front-api/bill/create
freq 179496
Name: api, dtype: object
'''
# 删除api⼀列
data = data.drop('api', axis=1)
# 还发现 interval 的值全是60
print( data.interval.unique() ) # [60]
# 把 id 字段都删掉
data = data.drop(['id'], axis=1)
# 发现数据中每⼀⾏的 interval 字段的值都⼀样,所以丢弃这列
data2 = data.drop(columns=['interval'])
print( data2.head() )
'''
count res_time_sum res_time_min res_time_max res_time_avg created_at
0 8 1057.31 88.75 177.72 132.0 2017-11-01 00:00:07
1 5 749.12 103.79 240.38 149.0 2017-11-01 00:01:07
2 5 845.84 136.31 225.73 169.0 2017-11-01 00:02:07
3 9 1305.52 90.12 196.61 145.0 2017-11-01 00:03:07
4 3 568.89 138.45 232.02 189.0 2017-11-01 00:04:07
'''
# 查看维度信息
print( data2.shape ) # (179496, 6)
# 查看字段类型
print( data2.dtypes )
'''
count int64
res_time_sum float64
res_time_min float64
res_time_max float64
res_time_avg float64
created_at object
dtype: object
'''
print( data2.info() )
'''
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 179496 entries, 0 to 179495
Data columns (total 6 columns):
count 179496 non-null int64
res_time_sum 179496 non-null float64
res_time_min 179496 non-null float64
res_time_max 179496 non-null float64
res_time_avg 179496 non-null float64
created_at 179496 non-null object
dtypes: float64(4), int64(1), object(1)
memory usage: 7.5+ MB
None
'''
print( data2.describe() )
'''
count res_time_sum res_time_min res_time_max res_time_avg
count 179496.000000 179496.000000 179496.000000 179496.000000 179496.000000
mean 7.175909 1393.177370 108.419620 359.880351 187.812208
std 4.325160 1499.485881 79.640559 638.919769 224.464813
min 1.000000 36.550000 3.210000 36.550000 36.000000
25% 4.000000 607.707500 83.410000 198.280000 144.000000
50% 7.000000 1154.905000 97.120000 256.090000 167.000000
75% 10.000000 1834.117500 116.990000 374.410000 202.000000
max 31.000000 142650.550000 18896.640000 142468.270000 71325.000000
'''
通过上面操作,可以发现,这份数据其实已经很规整了!
五. 案例:时间索引
为⽅便分析,使⽤ created_at 这⼀列的数据作为时间索引。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
plt.rc('font', **{'family':'SimHei'})
# 从log.txt导⼊数据
data = pd.read_table('log.txt', header=None,names=['id', 'api', 'count', 'res_time_sum', 'res_time_min','res_time_max', 'res_time_avg', 'interval', 'created_at'])
# 检查是否有重复值
print( data.duplicated().sum() ) # 0
# 检查是否有空值
print( data.isnull().sum() )
# 分析 api 和 interval 这两列的数据是否对分析有⽤
print( len(data) ) # 得到 179496
print( len(data[data['interval'] == 60]) ) # 得到 179496
print( len(data[data['api'] == '/front-api/bill/create']) ) # 得到 179496
# 查看api字段信息,可以发现unique=1,也就是说只有⼀个值,所以是没有意义的
print( data['api'].describe() )
# 删除api⼀列
data = data.drop('api', axis=1)
# 还发现 interval 的值全是60
print( data.interval.unique() ) # [60]
# 把 id 字段都删掉
data = data.drop(['id'], axis=1)
# 发现数据中每⼀⾏的 interval 字段的值都⼀样,所以丢弃这列
data2 = data.drop(columns=['interval'])
print( data2.head() )
# 查看维度信息
print( data2.shape ) # (179496, 6)
# 查看字段类型
print( data2.dtypes )
print( data2.info() )
print( data2.describe() )
print( "------------------------------------------" )
# 查看时间字段,会发现count=unique=179496,说明没有重复值
data2['created_at'].describe()
# 选取 2018-05-01 的数据,但是没有显⽰
print( data2[data2.created_at == '2018-05-01'] )
'''
Empty DataFrame
Columns: [count, res_time_sum, res_time_min, res_time_max, res_time_avg, created_at]
Index: []
'''
# 这样就可以,但是这样选取毕竟挺⿇烦的
print( data2[(data2.created_at >= '2018-05-01') & (data2.created_at < '2018-05-01')] )
'''
Empty DataFrame
Columns: [count, res_time_sum, res_time_min, res_time_max, res_time_avg, created_at]
Index: []
'''
print( "------------------------------------------" )
# 所以,将时间序列作为索引
data2.index = data2['created_at']
# 为了能 data['2018-05-01'] 这样选取数据,我们还要将时间序列由字符串转为时间索引
data2.index = pd.to_datetime(data2['created_at'])
# 有了时间索引,后⾯的操作就⽅便多了
print( data2['2018-05-01'] )
'''
count res_time_sum res_time_min res_time_max res_time_avg created_at
created_at
2018-05-01 00:00:48 6 2105.08 125.74 992.46 350.0 2018-05-01 00:00:48
2018-05-01 00:01:48 7 2579.11 76.55 987.47 368.0 2018-05-01 00:01:48
2018-05-01 00:02:48 7 1277.79 109.65 236.73 182.0 2018-05-01 00:02:48
2018-05-01 00:03:48 7 2137.20 131.55 920.52 305.0 2018-05-01 00:03:48
2018-05-01 00:04:48 13 2948.70 86.42 491.31 226.0 2018-05-01 00:04:48
... ... ... ... ... ... ...
2018-05-01 23:55:49 6 1083.97 70.85 262.22 180.0 2018-05-01 23:55:49
2018-05-01 23:56:49 4 840.00 117.31 382.63 210.0 2018-05-01 23:56:49
2018-05-01 23:57:49 2 295.51 101.71 193.80 147.0 2018-05-01 23:57:49
2018-05-01 23:58:49 2 431.99 84.43 347.56 215.0 2018-05-01 23:58:49
2018-05-01 23:59:49 3 428.84 103.58 206.57 142.0 2018-05-01 23:59:49
[884 rows x 6 columns]
'''
作者:Token_w