另外,从此文开始,我把编辑器换为了jupyter notebook,不得不说,当我接触到这个工具的时候我真的是爱上了它,有了一种享受记录的感觉,当然工具也要用在专业的方向,至少在目前的数据分析来说,我觉得它相当完美
数据预处理
常见的不规整数据主要有缺失数据,重复数据,异常数据几种,在正式开始的数据分析之前,我们要把不规整的数据处理掉
缺失值处理
缺失值就是由某些原因导致部分数据为空,对于为空的这部分数据我们一般有两种处理方式
- (1)删除:即把含有缺失值的数据删除
- (2)填充:即把缺失的那部分数据用某个值代替
缺失值的查看
在Excel中我们先选中一列没有缺失值得数据,看一下这一列数据共有多少个,然后把其他列的计数与这一列进行对比,小于这一列数据的代表有缺失值,差值就是缺失个数
在python中直接调用info()方法就会返回每一列的缺失情况。关于info()在前面就用过,但是没有详细说明判断缺失值情况
import pandas as pd
# 在CSV文件中我已经把7月销量删除了一个值
df = pd.read_csv('train-pivot.csv', encoding='GBK')
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 7 entries, 0 to 6
Data columns (total 7 columns):
用户ID 6 non-null float64
客户分类 6 non-null object
区域 6 non-null object
是否省会 6 non-null object
7月销量 5 non-null float64
8月销量 6 non-null float64
9月销量 6 non-null float64
dtypes: float64(4), object(3)
memory usage: 520.0+ bytes
在Python中缺失值一般用NaN表示,从用info()方法的结果来看,7月销量这一列是5 non-null float64,表示这一列有5个非null值,而其他列有6个非null值,说明7月销量这一列有1个null值。
这里也可以用isnull()方法来判断哪个是缺失值,如果是缺失值返回Ture,如果不是缺失值则返回False
df = pd.read_csv('train-pivot.csv', encoding='GBK')
df.isnull()
用户ID | 客户分类 | 区域 | 是否省会 | 7月销量 | 8月销量 | 9月销量 | |
0 | False | False | False | False | False | False | False |
1 | True | True | True | True | True | True | True |
2 | False | False | False | False | False | False | False |
3 | False | False | False | False | False | False | False |
4 | False | False | False | False | True | False | False |
5 | False | False | False | False | False | False | False |
6 | False | False | False | False | False | False | False |
缺失值删除
缺失值分为两种:
- 一行中某个字段是缺失值
- 字段全部为缺失值,即为一个空白行
在Python中,利用dropna()方法,dropna()方法默认删除含有缺失值行,也就是只要某一行有缺失值就把这一行删除
df = pd.read_csv('train-pivot.csv', encoding='GBK')
df
用户ID | 客户分类 | 区域 | 是否省会 | 7月销量 | 8月销量 | 9月销量 | |
0 | 59224.0 | A类 | 一线城市 | 是 | 6.0 | 20.0 | 0.0 |
1 | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
2 | 55295.0 | B类 | 三线城市 | 否 | 37.0 | 27.0 | 35.0 |
3 | 46035.0 | A类 | 二线城市 | 是 | 8.0 | 1.0 | 8.0 |
4 | 2459.0 | C类 | 一线城市 | 是 | NaN | 8.0 | 14.0 |
5 | 22179.0 | B类 | 三线城市 | 否 | 9.0 | 12.0 | 4.0 |
6 | 22557.0 | A类 | 二线城市 | 是 | 42.0 | 20.0 | 55.0 |
df.dropna()
用户ID | 客户分类 | 区域 | 是否省会 | 7月销量 | 8月销量 | 9月销量 | |
0 | 59224.0 | A类 | 一线城市 | 是 | 6.0 | 20.0 | 0.0 |
2 | 55295.0 | B类 | 三线城市 | 否 | 37.0 | 27.0 | 35.0 |
3 | 46035.0 | A类 | 二线城市 | 是 | 8.0 | 1.0 | 8.0 |
5 | 22179.0 | B类 | 三线城市 | 否 | 9.0 | 12.0 | 4.0 |
6 | 22557.0 | A类 | 二线城市 | 是 | 42.0 | 20.0 | 55.0 |
运行dropna()方法以后,删除含有NaN值得行,返回删除后的数据
如果想删除空白行,只要给dropna()方法传入一个参数 how='all’即可,这样就会删除那些全为空值得行,不全为空值得行就不会被删除
df
用户ID | 客户分类 | 区域 | 是否省会 | 7月销量 | 8月销量 | 9月销量 | |
0 | 59224.0 | A类 | 一线城市 | 是 | 6.0 | 20.0 | 0.0 |
1 | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
2 | 55295.0 | B类 | 三线城市 | 否 | 37.0 | 27.0 | 35.0 |
3 | 46035.0 | A类 | 二线城市 | 是 | 8.0 | 1.0 | 8.0 |
4 | 2459.0 | C类 | 一线城市 | 是 | NaN | 8.0 | 14.0 |
5 | 22179.0 | B类 | 三线城市 | 否 | 9.0 | 12.0 | 4.0 |
6 | 22557.0 | A类 | 二线城市 | 是 | 42.0 | 20.0 | 55.0 |
df.dropna(how='all')
用户ID | 客户分类 | 区域 | 是否省会 | 7月销量 | 8月销量 | 9月销量 | |
0 | 59224.0 | A类 | 一线城市 | 是 | 6.0 | 20.0 | 0.0 |
2 | 55295.0 | B类 | 三线城市 | 否 | 37.0 | 27.0 | 35.0 |
3 | 46035.0 | A类 | 二线城市 | 是 | 8.0 | 1.0 | 8.0 |
4 | 2459.0 | C类 | 一线城市 | 是 | NaN | 8.0 | 14.0 |
5 | 22179.0 | B类 | 三线城市 | 否 | 9.0 | 12.0 | 4.0 |
6 | 22557.0 | A类 | 二线城市 | 是 | 42.0 | 20.0 | 55.0 |
上表第四行中只有7月销量这个字段是空值,所以在利用dropna(how=‘all’)的时候并没有删除第四行,只把全部为NaN的第一行删掉了
缺失值填充
上面介绍了缺失值的删除,但是数据是宝贵的,一般情况下只要数据缺失的不是很多,尽量不要删除,而是选择填充
在python中,利用fillna()方法对数据表的所有缺失值进行填充,在fillna括号里的参数,输入要填充的值即可
df.fillna(0)
用户ID | 客户分类 | 区域 | 是否省会 | 7月销量 | 8月销量 | 9月销量 | |
0 | 59224.0 | A类 | 一线城市 | 是 | 6.0 | 20.0 | 0.0 |
1 | 0.0 | 0 | 0 | 0 | 0.0 | 0.0 | 0.0 |
2 | 55295.0 | B类 | 三线城市 | 否 | 37.0 | 27.0 | 35.0 |
3 | 46035.0 | A类 | 二线城市 | 是 | 8.0 | 1.0 | 8.0 |
4 | 2459.0 | C类 | 一线城市 | 是 | 0.0 | 8.0 | 14.0 |
5 | 22179.0 | B类 | 三线城市 | 否 | 9.0 | 12.0 | 4.0 |
6 | 22557.0 | A类 | 二线城市 | 是 | 42.0 | 20.0 | 55.0 |
df.fillna({'区域':'待定'})
用户ID | 客户分类 | 区域 | 是否省会 | 7月销量 | 8月销量 | 9月销量 | |
0 | 59224.0 | A类 | 一线城市 | 是 | 6.0 | 20.0 | 0.0 |
1 | NaN | NaN | 待定 | NaN | NaN | NaN | NaN |
2 | 55295.0 | B类 | 三线城市 | 否 | 37.0 | 27.0 | 35.0 |
3 | 46035.0 | A类 | 二线城市 | 是 | 8.0 | 1.0 | 8.0 |
4 | 2459.0 | C类 | 一线城市 | 是 | NaN | 8.0 | 14.0 |
5 | 22179.0 | B类 | 三线城市 | 否 | 9.0 | 12.0 | 4.0 |
6 | 22557.0 | A类 | 二线城市 | 是 | 42.0 | 20.0 | 55.0 |
上述只是对一列进行修改,也可以同时对多列进行修改,只需要在字典里加入其它列的键值对就可以了,这里不再赘述
重复值处理
重复数据就是同样的数据记录多条,对于这样的数据我们一般做删除处理
- 情景:
假如你是一名数据分析师,你的主要工作是分析公司的销售情况,现有公司2018年12月销售明细一份,你想看一下8月整体成交量是多少,最简单的方式就是看一下有多少条成交明细。但是可能会有城府的成交记录存在,所以要先删除重复值。
python实现
df = pd.read_excel('Financial statement.xlsx')
在python中,利用drop_duplicates()方法,该方法默认对所有值进行重复值判断,且默认保留第一个(行)值
df
订单编号 | 客户姓名 | SN码 | 成交时间 | |
0 | A1 | 周一 | 101 | 2018-08-08 |
1 | A2 | 吴二 | 102 | 2018-08-09 |
2 | A3 | 郑三 | 103 | 2018-08-10 |
3 | A3 | 郑三 | 103 | 2018-08-10 |
4 | A4 | 赵四 | 104 | 2018-08-11 |
5 | A5 | 赵四 | 104 | 2018-08-12 |
df.drop_duplicates() # 可以发现A3 的重复值已经消失了
订单编号 | 客户姓名 | SN码 | 成交时间 | |
0 | A1 | 周一 | 101 | 2018-08-08 |
1 | A2 | 吴二 | 102 | 2018-08-09 |
2 | A3 | 郑三 | 103 | 2018-08-10 |
4 | A4 | 赵四 | 104 | 2018-08-11 |
5 | A5 | 赵四 | 104 | 2018-08-12 |
上述代码是针对所有字段进行重复值的判断,我们同样也可以只针对某一列或某几列进行重复值删除的判断,只需要在drop_duplicates()方法中指明要判断的列名即可
df
订单编号 | 客户姓名 | SN码 | 成交时间 | |
0 | A1 | 周一 | 101 | 2018-08-08 |
1 | A2 | 吴二 | 102 | 2018-08-09 |
2 | A3 | 郑三 | 103 | 2018-08-10 |
3 | A3 | 郑三 | 103 | 2018-08-10 |
4 | A4 | 赵四 | 104 | 2018-08-11 |
5 | A5 | 赵四 | 104 | 2018-08-12 |
df.drop_duplicates(subset='客户姓名')
订单编号 | 客户姓名 | SN码 | 成交时间 | |
0 | A1 | 周一 | 101 | 2018-08-08 |
1 | A2 | 吴二 | 102 | 2018-08-09 |
2 | A3 | 郑三 | 103 | 2018-08-10 |
4 | A4 | 赵四 | 104 | 2018-08-11 |
也可以利用多列去重,只需要把多个列名以列表的形式传给参数subset即可
还可以自定义删除重复项时保留哪个,默认保留第一个,也可以设置保留最后一个,或者全部不保留,通过传入参数keep进行设置,参数keep默认值是first,即保留第一个值,也可以是last,保留最后一个值,还可以是False,即把重复值全部删除
# 保留最后一个重复值
df.drop_duplicates(subset=['客户姓名', 'SN码'], keep='last')
订单编号 | 客户姓名 | SN码 | 成交时间 | |
0 | A1 | 周一 | 101 | 2018-08-08 |
1 | A2 | 吴二 | 102 | 2018-08-09 |
3 | A3 | 郑三 | 103 | 2018-08-10 |
5 | A5 | 赵四 | 104 | 2018-08-12 |
# 不保留任何重复值
df.drop_duplicates(subset=['客户姓名', 'SN码'], keep=False)
订单编号 | 客户姓名 | SN码 | 成交时间 | |
0 | A1 | 周一 | 101 | 2018-08-08 |
1 | A2 | 吴二 | 102 | 2018-08-09 |
异常值得检测与处理
异常值就是相比正常数据而言过高或过低的数据,比如一个人的年龄是0岁或者300岁都是一个异常值,因为和实际情况差距太大
异常值检测
要处理异常值首先要检测,也就是发现异常值,发现异常值的方式只要有一下三种
- 根据业务经验划定不同指标的正常范围,超过该范围的值算作异常值
- 通过绘制箱形图,把大于(小于)箱形图上边缘(下边缘)的点称为异常值
- 如果数据服从正态分布
(箱形图会在后边介绍,目前没学画不出来)
异常值处理
对异常值一般有以下几种处理方式
- 最常用的处理方式就是删除
- 把异常值当做缺失值来填充
- 把异常值当做特殊情况,研究异常值出现的原因
Excel实现
在Excel中,删除异常值只要通过筛选把异常值对应的行找出来,然后单击鼠标右键选择删除即可。
对异常值进行填充,其实就是对异常值进行替换,同样通过筛选功能吧异常值先找出来,然后把这些异常值替换成要填充的值即可
Python实现
在python中,删除异常值用到的方法和Excel中的方法原理类似,Python中是通过过滤的方法对异常值进行删除。比如df表中有年龄这个指标,要把年龄大于200的值删掉,你可以通过筛选把年龄不大于200的筛选出来,筛选出来的部分就是删除大于200的值以后的新表。
对异常值进行填充,就是对异常值进行替换,利用replace()方法可以对特定的值进行替换
数据类型转换
Excel实现
在Excel中常用的数据类型就是在菜单栏中数字选项下面的几种,也可以选择其他数据格式。
在Excel中只要选中某一列就可以在菜单栏看到这一列的数据类型
当选中成交时间这一列时,菜单栏就会显示日期,表示成交时间这一列的数据类型是日期格式
Python实现
pandas不像Excel分的那么详细,主要有6种数据类型
类型 | 说明 |
int | 整型数,即整数 |
float | 浮点数,即含有小数点的数 |
boject | python对象类型,用O表示 |
string | 字符串类型,经常用S表示,S10表示长度为10的字符串 |
unicode | 固定长度的Unicode类型,跟字符串定义方式一样 |
datetime64[ns] | 表示时间格式 |
在Python中,不仅可以用info()方法获取每一列的数据类型还可以通过dtype方法来获取某一列的数据类型
df
订单编号 | 客户姓名 | SN码 | 成交时间 | |
0 | A1 | 周一 | 101 | 2018-08-08 |
1 | A2 | 吴二 | 102 | 2018-08-09 |
2 | A3 | 郑三 | 103 | 2018-08-10 |
3 | A3 | 郑三 | 103 | 2018-08-10 |
4 | A4 | 赵四 | 104 | 2018-08-11 |
5 | A5 | 赵四 | 104 | 2018-08-12 |
print(df['订单编号'].dtype)
object
print(df['SN码'].dtype)
int64
类型转换
Excel实现(吐槽一下,书上是带图的,我懒得截了)
在Excel中如果想要更改某一列数据类型,只要选中这一列,然后在数字菜单栏中通过下拉菜单选择你想要的的转换目标类型即可实现
Python实现
在python中,我们利用astype()方法对数据类型进行转换,astype后边的括号里要指明要转换的目标类型即可
df
订单编号 | 客户姓名 | SN码 | 成交时间 | |
0 | A1 | 周一 | 101 | 2018-08-08 |
1 | A2 | 吴二 | 102 | 2018-08-09 |
2 | A3 | 郑三 | 103 | 2018-08-10 |
3 | A3 | 郑三 | 103 | 2018-08-10 |
4 | A4 | 赵四 | 104 | 2018-08-11 |
5 | A5 | 赵四 | 104 | 2018-08-12 |
print(df['SN码'].dtype)
int64
print(df['SN码'].astype('float64'))
0 101.0
1 102.0
2 103.0
3 103.0
4 104.0
5 104.0
Name: SN码, dtype: float64
索引设置
索引是查找数据的依据,设置索引的目的是便于我们查找数据。
为无索引表添加索引
有的表没有索引,这时候要给这类表加一个索引
python实现
df = pd.read_excel('02.xlsx', encoding='GBK')
在python中,如果没有索引,会默认用从0开始的自然数做索引,如下
df
0 | 1 | 2 | 3 | |
0 | A1 | 周一 | 101 | 2018-08-08 |
1 | A2 | 吴二 | 102 | 2018-08-09 |
2 | A3 | 郑三 | 103 | 2018-08-10 |
3 | A3 | 郑三 | 103 | 2018-08-10 |
4 | A4 | 赵四 | 104 | 2018-08-11 |
5 | A5 | 赵四 | 104 | 2018-08-12 |
通过给df的columns参数传入列索引值,index参数传入行索引值达到为无索引表添加索引的目的
df.columns = ['订单编号','客户姓名', 'SN码', '成交时间']
df
订单编号 | 客户姓名 | SN码 | 成交时间 | |
0 | A1 | 周一 | 101 | 2018-08-08 |
1 | A2 | 吴二 | 102 | 2018-08-09 |
2 | A3 | 郑三 | 103 | 2018-08-10 |
3 | A3 | 郑三 | 103 | 2018-08-10 |
4 | A4 | 赵四 | 104 | 2018-08-11 |
5 | A5 | 赵四 | 104 | 2018-08-12 |
# 为表添加行索引
df.index = [1, 2, 3, 4, 5, 6]
df
# 这里注意原来从0开始的行索引现在变为1开始
订单编号 | 客户姓名 | SN码 | 成交时间 | |
1 | A1 | 周一 | 101 | 2018-08-08 |
2 | A2 | 吴二 | 102 | 2018-08-09 |
3 | A3 | 郑三 | 103 | 2018-08-10 |
4 | A3 | 郑三 | 103 | 2018-08-10 |
5 | A4 | 赵四 | 104 | 2018-08-11 |
6 | A5 | 赵四 | 104 | 2018-08-12 |
重新设置索引
重新设置索引,一般指行索引的设置。有的表虽然有索引,但不是我们想要的索引,比如现在有一个表是把序号作为行索引,而我们想要把订单编号作为行索引,该如何实现呢
Excel实现
在Excel中重新设置行索引比较简单,你想让那一列做行索引,直接把这一列拖到第一列的位置即可
python实现
在python中可以利用set_index()方法重新设置索引列,在set_index()里指明要用作行索引的列的名称即可
df
订单编号 | 客户姓名 | SN码 | 成交时间 | |
1 | A1 | 周一 | 101 | 2018-08-08 |
2 | A2 | 吴二 | 102 | 2018-08-09 |
3 | A3 | 郑三 | 103 | 2018-08-10 |
4 | A3 | 郑三 | 103 | 2018-08-10 |
5 | A4 | 赵四 | 104 | 2018-08-11 |
6 | A5 | 赵四 | 104 | 2018-08-12 |
df.set_index('订单编号')
客户姓名 | SN码 | 成交时间 | |
订单编号 | |||
A1 | 周一 | 101 | 2018-08-08 |
A2 | 吴二 | 102 | 2018-08-09 |
A3 | 郑三 | 103 | 2018-08-10 |
A3 | 郑三 | 103 | 2018-08-10 |
A4 | 赵四 | 104 | 2018-08-11 |
A5 | 赵四 | 104 | 2018-08-12 |
在重新设置索引时,还可以给set_index()方法传入两个或多个列名,我们把这种一个表中用多列来做索引的方式称之为层次化索引,层次化索引一般用在某一列中含有多个重复值的情况下。
层次化索引的例子:如,其中a, b, c, d分别有多个重复值
重命名索引
重命名索引时针对现有索引名进行修改的,就是改了字段名
Excel
在Excel中直接修改即可
python
df = pd.read_excel('03-重命名索引.xlsx')
在python中重命名索引,我们利用的是rename()方法,在rename后边的括号里指明要修改的行索引及列索引名
# 重命名列索引
df
订单编号 | 客户姓名 | SN码 | 成交时间 | |
0 | A1 | 周一 | 101 | 2018-08-08 |
1 | A2 | 吴二 | 102 | 2018-08-09 |
2 | A3 | 郑三 | 103 | 2018-08-10 |
3 | A3 | 郑三 | 103 | 2018-08-10 |
4 | A4 | 赵四 | 104 | 2018-08-11 |
5 | A5 | 赵四 | 104 | 2018-08-12 |
df.rename(columns={'订单编号':'新订单编号', '客户姓名':'新客户姓名'})
新订单编号 | 新客户姓名 | SN码 | 成交时间 | |
0 | A1 | 周一 | 101 | 2018-08-08 |
1 | A2 | 吴二 | 102 | 2018-08-09 |
2 | A3 | 郑三 | 103 | 2018-08-10 |
3 | A3 | 郑三 | 103 | 2018-08-10 |
4 | A4 | 赵四 | 104 | 2018-08-11 |
5 | A5 | 赵四 | 104 | 2018-08-12 |
# 重命名行索引
df
订单编号 | 客户姓名 | SN码 | 成交时间 | |
0 | A1 | 周一 | 101 | 2018-08-08 |
1 | A2 | 吴二 | 102 | 2018-08-09 |
2 | A3 | 郑三 | 103 | 2018-08-10 |
3 | A3 | 郑三 | 103 | 2018-08-10 |
4 | A4 | 赵四 | 104 | 2018-08-11 |
5 | A5 | 赵四 | 104 | 2018-08-12 |
df.rename(index={0:'零', 1:'一', 2:'二'})
# 同时命名行索引和列索引
df.rename(columns={'订单编号':'新订单编号', '客户姓名':'新客户姓名'},
index={0:'零', 1:'一', 2:'二'})
新订单编号 | 新客户姓名 | SN码 | 成交时间 | |
零 | A1 | 周一 | 101 | 2018-08-08 |
一 | A2 | 吴二 | 102 | 2018-08-09 |
二 | A3 | 郑三 | 103 | 2018-08-10 |
3 | A3 | 郑三 | 103 | 2018-08-10 |
4 | A4 | 赵四 | 104 | 2018-08-11 |
5 | A5 | 赵四 | 104 | 2018-08-12 |
重置索引
重置索引主要用于在层次化索引表中,重置索引是将索引当做一个columns进行返回
在python中利用reset_index()方法,参数如下
reset_index(level=None, drop=False, inplace=False)
- level参数用来指定要将层次化所用的第几级别转化为columns,第一个索引为0级,第二个索引为1级别,默认为全部索引,即默认把索引全部转化为columns
- drop参数用来指定是否将原索引删掉,即不作为一个新的columns,默认为False,即不删除原索引
- inplace参数用来指定是否修改原数据表