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
'''

二. 了解数据

拿到一个全新的数据集,应该从哪里入手?没错,我们需要先了解数据,看看它长什么样子。常用的方法和属性如下:

  • .head():查看前n行数据,默认值是5。
  • .tail():查看后n行数据,默认值是5。
  • .shape:查看数据维数。
  • .columns:查看所有列名。
  • .info():查看索引、数据类型和内存信息。
  • .describe():查看每列数据的基本统计值,包括计数值、均值、标准差、最小最大值、1/4、1/2、3/4分位数。
  • .value_counts():查看Series对象的唯一值和计数值。

如果上面这些操作还不够直观的话,就作图看看,需要先导入Python可视化库matplotlib。

  • 直方图
  • 箱型图
  • 散点图

三. 清洗数据的基本方法

了解数据集之后,我们就可以开始对数据集进行清洗了,通常要处理的问题包括无效信息,列名不规范、格式不一致,存在重复值,缺失值,异常值等,下面我们一个一个来看。

第1步,去除不需要的行、列。

在分析一个数据集的时候,很多信息其实是用不到的,因此,需要去除不必要的行或列。在导入的时候就可以通过设置pd.read_xxx()里面的参数来实现列的选择目的。

如果在数据导入之后,还想删除某些行和列,可以用 .drop() 方法。先创建一个列表list,把不需要的列名放进去,再调用.drop() 方法,参数axis为1时代表列,为0时代表行,参数inplace=True表示不创建新的对象,直接对原始对象进行修改。

第2步,重新命名列。

当原始数据的列名不好理解,或者不够简洁时,可以用.rename()方法进行修改。这把英文的列名改成中文,先创建一个字典,把要修改的列名定义好,然后调用rename()方法。

第3步,重新设置索引。

数据默认的索引是从0开始的有序整数,但如果想把某一列设置为新的索引,可以用.set_index()方法实现。

第4步,用字符串操作规范列。

字符串str操作是非常实用的,因为列中总是会包含不必要的字符,常用的方法如下:

  • lower():把大写转换成小写。
  • upper():把小写转换成大写。
  • capitalize():设置首字母大写。
  • replace():替换特定字符。
  • strip():去除字符串中的头尾空格、以及\n \t。
  • split():使用字符串中的’x’字符作为分隔符,将字符串分隔成列表。
  • get():选取列表中某个位置的值。
  • contains():判断是否存在某个字符,返回的是布尔值。
  • find():检测字符串中是否包含子字符串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]
'''
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Token_w

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值