一、初识Pandas
主要简单了解Pandas及如何安装Pandas,通过“牛刀小试”使读者能够快速体验Pandas。
1.1 Pandas概述
Pandas是数据分析的三大剑客之一,是Python的核心数据分析库,它提供了快速、灵活、明确的数据结构,能够简单、直观、快速地处理各种类型的数据,具体介绍如下:
Pandas能够处理以下类型的数据:
■ 与SQL或Excel表类似的数据。
■ 有序和无序(非固定频率)的时间序列数据。
■ 带行列标签的矩阵数据。
■ 任意其他形式的观测、统计数据集。
Pandas提供的两个主要数据结构Series(一维数组结构)与DataFrame(二维数组结构),可以处理金融、统计、社会科学、工程等领域里的大多数典型案例,并且Pandas是基于NumPy开发的,它可以与其他第三方科学计算库完美集成。
Pandas的功能很多,它的优势如下:
■ 处理浮点与非浮点数据里的缺失数据,表示为NaN。
■ 大小可变,例如插入或删除DataFrame等多维对象的列。
■ 自动、显式数据对齐,显式地将对象与一组标签对齐,也可以忽略标签,在Series、DataFrame计算时自动与数据对齐。
■ 强大、灵活的分组统计(groupby)功能,即数据聚合、数据转换。
■ 可以把Python和NumPy数据结构里不规则、不同索引的数据轻松地转换为DataFrame对象。
■ 智能标签,对大型数据集进行切片、花式索引、子集分解等操作。
■ 直观地合并(merge)、连接(join)数据集。
■ 灵活地重塑(reshape)、透视(pivot)数据集。
■ 成熟的导入导出工具,导入文本文件(CSV等支持分隔符的文件)、Excel文件、数据库等来源的数据;导出Excel文件、文本文件等,利用超快的HDF5格式保存或加载数据。
■ 时间序列:支持日期范围生成、频率转换、移动窗口统计、移动窗口线性回归、日期位移等时间序列功能。
综上所述,Pandas是处理数据时最理想的工具。
1.2 安装Pandas
下面介绍两种安装Pandas的方法。
1.2.1 通过PyPI的pip工具安装
在系统搜索框中输入cmd,单击“命令提示符”,打开“命令提示符”窗口,在命令提示符后输入安装命令。
Pandas可以通过PyPI的pip工具安装,安装命令如下:
pip install Pandas
知识胶囊:pip是开发人员经常使用,却又不知来历的一个工具,下面简单介绍一下它。pip是一个现代的、通用的Python包管理工具,英文全称是python install packages。
PyPI(Python Package Index)是python官方的第三方库的仓库,所有人都可以下载第三方库或上传自己开发的库到PyPI上。PyPI可帮助用户查找和安装Python社区开发和共享的软件。PyPI推荐使用pip包管理器来下载第三方库,python 2.7.9以后的版本已经内置了pip,所以不需要安装。
1.2.2 通过Pycharm开发环境安装
除了通过pip工具安装以外,还可以通过Pycharm开发环境安装。运行Pycharm,选择File→ Settings菜单项,打开“Settings”窗口,选择Project Interpreter选项,然后单击添加(+)按钮,如图1所示。这里要注意的是,在Project Interprter选项中应选择当前工程项目使用的Python版本。

图1 Settings窗口
单击添加(+)按钮,打开Available Packages窗口,在搜索文本框中输入需要添加的模块名称,例如“pandas”,然后在列表中选择需要安装的模块,如图2所示,单击Install Package按钮,即可实现Pandas模块的安装。

图2 在PyCharm开发环境中安装Pandas模块
另外,还需要注意一点:Pandas有一些依赖库。
例如,当通过Pandas读取Excel文件时,如果只安装Pandas模块,就会出现如图3所示的错误,意思是缺少依赖库xlrd。当通过Pandas导出Excel文件时,也同样会出现缺少依赖库xlwt的错误,如图4所示。

图3 缺少依赖库xlrd

图4 缺少依赖库xlwt
解决办法:
安装xlrd模块和xlwt模块,方法如下:
执行pip install xlrd命令或通过PyCharm开发环境安装xlrd模块。
执行pip install xlwt命令或通过PyCharm开发环境安装xlwt模块。
由于后面举例时经常会用到这两项操作,因此需要同时安装xlrd和xlwt两个模块。
1.2.3 牛刀小试——轻松导入Excel数据
了解Pandas模块后,接下使用Pandas导入Excel数据。
快速示例01 导入英超射手榜数据
以英超射手榜数据为例,导入英超射手榜数据,按照惯例首先导入模块,代码如下:
01 import pandas as pd #导入pandas模块
02 #解决数据输出时列名不对齐的问题
03 pd.set_option('display.unicode.east_asian_width', True)
04 df=pd.read_excel('data.xlsx') #读取Excel文件
05 print(df.head()) #显示前5条数据
运行程序,输出结果如图3.5所示。

图 5 英超射手榜TOP5
技巧:Pandas默认输出结果,会出现列不对齐或者多行多列显示不全的问题,使用set_option函数可以解决这两个问题。
■ 解决列名不对齐
通过将display.unicode.east_asian_width设置为True,使列名对齐。例如:
pd.set_option('display.unicode.east_asian_width', True)
■ 行列显示不全
通过将display.max_rows和display.max_columns修改为默认输出最大的行数和列数。例如:
pd.set_option('display.max_rows',1000)
pd.set_option('display.max_columns',1000)
二、 Series对象
Pandas是Python数据分析重要的库,而Series和DataFrame是Pandas库中两个重要的对象,也是Pandas中两个重要的数据结构,如图 6所示。

图 6 Pandas中两个重要的数据结构
2.1 图解Series对象
Series是Python的Pandas库中的一种数据结构,它类似一维数组,由一组数据以及与这组数据相关的标签(即索引)组成,或者仅有一组数据而没有索引也可以创建一个简单的Series对象。Series可以存储整数、浮点数、字符串、Python对象等多种类型的数据。
例如,在成绩表(如图 7所示)中包含了Series对象和DataFrame对象,其中“语文”“数学”和“英语”每一列都是一个Series对象,而“语文”“数学”和“英语”三列组成了一个DataFrame对象,如图 8所示。

图 7 原始数据(成绩表)

图 8 Series对象图解
2.2 创建一个Series对象
创建Series对象时,主要使用Pandas的Series方法,语法如下:
s=pd.Series(data,index=index)
参数说明:
■ data:表示数据,支持Python字典、多维数组、标量值(即只有大小、没有方向的量。也就是说,只是一个数值,如s=pd.Series(5))。
■ index:表示行标签(索引)。
■ 返回值:Series对象。
说明:当data参数是多维数组时,index长度必须与data长度一致。如果没有指定index参数,将自动创建数值型索引(从0~data的数据长度减1)。
快速示例02 为成绩表添加一列“物理”成绩
创建一个Series对象,为成绩表添加一列“物理”成绩,程序代码如下:
01 import pandas as pd
02 s1=pd.Series([88,60,75])
03 print(s1)
运行程序,输出结果为:
08
160
275
上述举例,如果通过pandas模块引入Series对象,那么就可以直接在程序中使用Series对象了,关键代码如下:
01 from pandas import Series
02 s1=Series([88,60,75])
2.3 手动设置Series索引
创建Series对象时会自动生成整数索引,默认值从0开始至数据长度减1。例如,在上一节示例中使用的就是默认索引,如0、1、2。除了使用默认索引,还可以通过index参数手动设置索引。
快速示例03 手动设置索引
下面手动设置索引,将上一节添加的“物理”成绩的索引设置为1、2、3,也可以是“明日同学”“高同学”“七月流火”,程序代码如下:
01 import pandas as pd
02 s1=pd.Series([88,60,75],index=[1,2,3])
03 s2=pd.Series([88,60,75],index=['明日同学','高同学','七月流火'])
04 print(s1)
05 print(s2)
运行程序,输出结果为:
188
260
375
dtype: int64
明日同学88
高同学60
七月流火75
dtype: int64
说明:上述结果中输出的dtype,是DataFrame数据的数据类型,int为整型,后面的数字表示位数。
2.4 Series的索引
2.4.1 Series位置索引
位置索引是从0开始,[0]是Series的第一个数;[1]是series的第二个数,依次类推。
快速示例04 通过位置索引获取学生的物理成绩
获取第一个学生的物理成绩,程序代码如下:
01 import pandas as pd
02 s1=pd.Series([88,60,75])
03 print(s1[0])
运行程序,输出结果为:
88
注意:Series对象不能使用[-1]定位索引。
2.4.2 Series标签索引
Series标签索引与位置索引方法类似,用“[ ]”表示,里面是索引名称,注意index的数据类型是字符串,如果需要获取多个标签索引值,则用“[[ ]]”表示(相当于在“[ ]”中包含一个列表)。
快速示例05 通过标签索引获取学生的物理成绩
通过标签索引“明日同学”和“七月流火”获取物理成绩,程序代码如下:
01 import pandas as pd
02 s1=pd.Series([88,60,75],index=['明日同学','高同学','七月流火'])
03 print(s1['明日同学']) #通过一个标签索引获取索引值
04 print(s1[['明日同学','七月流火']]) #通过多个标签索引获取索引值
运行程序,输出结果为:
88
明日同学88
七月流火75
2.4.3 Series切片索引
用标签索引做切片,可以包头包尾(即包含了索引开始位置的数据,也包含了索引结束位置的数据)。
快速示例06 通过切片获取数据
通过标签切片索引“明日同学”“七月流火”获取数据,程序代码如下:
print(s1['明日同学':'七月流火']) #通过切片获取索引值
运行程序,输出结果为:
明日同学8
高同学60
七月流火75
用位置索引做切片,和list列表的用法一样,可以包头不包尾(即包含了索引开始位置的数据,但不包含索引结束位置的数据)。
快速示例07 通过位置切片获取数据
通过位置切片1~4获取数据,程序代码如下:
01 s2=pd.Series([88,60,75,34,68])
02 print(s2[1:4])
运行程序,输出结果为:
160
275
334
2.5 获取Series的索引和值
获取Series的索引和值主要使用Series对象的index方法和values方法。
快速示例08 获取物理成绩的索引和值
下面使用Series的index方法和values方法获取物理成绩的索引和值,程序代码如下:
01 import pandas as pd
02 s1=pd.Series([88,60,75])
03 print(s1.index)
04 print(s1.values)
运行程序,输出结果为:
RangeIndex(start=0, stop=3, step=1)
[88 60 75]
三、DataFrame对象
DataFrame是Pandas库中的一种数据结构,它是由多种类型的列组成的二维表数据结构,类似于Excel、SQL或Series对象构成的字典。DataFrame是最常用的Pandas对象,它与Series对象一样支持多种类型的数据。
3.1 图解DataFrame对象
DataFrame是一个二维表数据结构,即由行列数据组成的表格。DataFrame既有行索引也有列索引,它可以看作是由Series对象组成的字典,不过这些Series对象共用一个索引,如图 9所示。

图 9 DataFrame对象的结构
处理DataFrame表格数据时,用index表示行或用columns表示列更直观,而且用这种方式迭代DataFrame对象的列,代码更易读懂。
快速示例09 遍历DataFrame数据
遍历DataFrame数据,输出成绩表的每一列数据,程序代码如下:
01 import pandas as pd
02 #解决数据输出时列名不对齐的问题
03 pd.set_option('display.unicode.east_asian_width', True)
04 data = [[110,105,99],[105,88,115],[109,120,130]]
05 index = [0,1,2]
06 columns = ['语文','数学','英语']
07 #创建DataFrame数据
08 df = pd.DataFrame(data=data, index=index,columns=columns)
09 print(df)
10 #遍历DataFrame数据的每一列
11 for col in df.columns:
12 series = df[col]
13 print(series)
运行程序,输出结果为:
0110
1105
2109
Name: 语文, dtype: int64
0105
188
2120
Name: 数学, dtype: int64
099
1115
2130
Name: 英语, dtype: int64
从运行结果得知:上述代码返回的其实是Series,如图 10所示。Pandas之所以提供多种数据结构,其目的就是为了代码易读,操作更加方便。

图 10 Series对象
3.2 创建一个DataFrame对象
创建DataFrame主要使用Pandas模块的DataFrame方法,语法如下:
pandas.DataFrame(data,index,columns,dtype,copy)
参数说明:
■ data:表示数据,可以是ndarray数组、series对象、列表、字典等。
■ index:表示行标签(索引)。
■ columns:列标签(索引)。
■ dtype:每一列数据的数据类型,其与Python数据类型有所不同,如object数据类型对应的是Python的字符型。如表 1所示,是Pandas数据类型与Python数据类型的对应。
表 1 数据类型对应表

■ copy:用于复制数据。
■ 返回值:DataFrame。
下面通过两种方法来创建DataFrame对象,即二维数组和字典。
3.2.1 通过二维数组创建DataFrame对象
快速示例10 通过二维数组创建成绩表
通过二维数组创建成绩表,包括语文、数学和英语,程序代码如下:
01 import pandas as pd
02 #解决数据输出时列名不对齐的问题
03 pd.set_option('display.unicode.east_asian_width', True)
04 data = [[110,105,99],[105,88,115],[109,120,130]]
05 columns = ['语文','数学','英语']
06 df = pd.DataFrame(data=data, columns=columns)
07 print(df)
运行程序,输出结果为:
语文数学英语
011010599
110588115
2109120130
3.2.2 通过字典创建DataFrame对象
通过字典创建DataFrame,需要注意:字典中的value值只能是一维数组或单个的简单数据类型,如果是数组,则要求所有的数组长度一致;如果是单个数据,则每行都需要添加相同数据。
快速示例11 通过字典创建成绩表
通过字典创建成绩表,包括语文、数学、英语和班级,程序代码如下:
01 import pandas as pd
02 #解决数据输出时列名不对齐的问题
03 pd.set_option('display.unicode.east_asian_width', True)
04 df = pd.DataFrame({
05 '语文':[110,105,99],
06 '数学':[105,88,115],
07 '英语':[109,120,130],
08 '班级':'高一7班'
09 },index=[0,1,2])
10 print(df)
运行程序,输出结果为:
语文数学英语班级
010105109高一7班
110588120高一7班
299115130高一7班
在上述代码中,“班级”的value值是单个数据,所以每一行都添加了相同的数据“高一7班”。
3.3 DataFrame的重要属性和函数
DataFrame是Pandas中一个重要的对象,它的属性和函数有很多,下面先简单了解一下DataFrame对象的几个重要属性和函数,重要属性及描述如表 2所示。
表 2 重要属性及描述

重要函数及描述如表 3所示。
表 3 重要函数及描述

四、导入外部数据
数据分析首先就要有数据。对于多种多样的数据类型,本节介绍如何导入不同类型的外部数据。
4.1 导入.xls或.xlsx文件
导入.xls或.xlsx文件主要使用Pandas的read_excel方法,语法如下:
pandas.read_excel(io,sheet_name=0,header=0,names=None,index_col=None,usecols=None,squeeze=False,dtype=None,engine=None,converters=None,true_values=None,false_values=None,skiprows=None,nrow=None,na_values=None,keep_default_na=True,verbose=False,parse_dates=False,date_parser=None,thousands=None,comment=None,skipfooter=0,conver_float=True,mangle_dupe_cols=True,**kwds)
常用参数说明:
■ io:字符串,xls或xlsx文件路径或类文件对象。
■ sheet_name:None、字符串、整数、字符串列表或整数列表,默认值为0。字符串用于工作表名称;整数为索引,表示工作表位置,字符串列表或整数列表用于请求多个工作表,为None时则获取所有的工作表。参数值如表 4所示。
表 4 sheet_name参数值及说明

■ header:指定作为列名的行,默认值为0,即取第一行的值为列名。数据为除列名以外的数据;若数据不包含列名,则设置为header=None。
■ names:默认值为None,要使用的列名列表。
■ index_col:指定列为索引列,默认值为None,索引0是DataFrame对象的行标签。
■ usecols:int、list或字符串,默认值为None。
● 如果为None,则解析所有列。
● 如果为int,则解析最后一列。
● 如果为list列表,则解析列号和列表的列。
● 如果为字符串,则表示以逗号分隔的Excel列字母和列范围列表(例如,“A:E”或“A,C,E:F”),范围包括双方。
■ squeeze:布尔值,默认值为False,如果解析的数据只包含一列,则返回一个Series。
■ dtype:列的数据类型名称或字典,默认值为None。例如,{为'a':np.float64,'b':np.int32}。
■ skiprows:省略指定行数的数据,从第一行开始。
■ skipfooter:省略指定行数的数据,从尾部数的行开始。
下面将详细介绍如何导入.xlsx文件。
4.1.1 常规导入
快速示例12 导入Excel文件
导入“1月.xlsx”的Excel文件,程序代码如下:
01 import pandas as pd
02 #解决数据输出时列名不对齐的问题
03 pd.set_option('display.unicode.east_asian_width', True)
04 df=pd.read_excel('1月.xlsx')
05 print(df.head()) #输出前5条数据
运行程序,输出部分数据,结果如图 11所示。

图 11 前5条的淘宝销售数据(部分数据)
知识胶囊:
导入外部数据,必然要涉及路径问题,下面来复习一下相对路径和绝对路径的知识。
■ 相对路径
相对路径就是以当前文件为基准,从而一级级目录指向被引用的资源文件。以下是常用的表示当前目录和当前目录的父级目录的标识符。
● ../:表示当前文件所在目录的上一级目录。
● ./:表示当前文件所在的目录(可以省略)。
● /:表示当前文件的根目录(域名映射或硬盘目录)。
如果使用系统默认文件路径“\”,那么在Python中则需要在路径最前面加一个r,以避免路径里面的“\”被转义。
■ 绝对路径
绝对路径是文件真正存在的路径,是指从硬盘的根目录(盘符)开始,从而一级级目录指向文件。
4.1.2 导入指定的Sheet页
一个Excel文件包含多个Sheet页,通过设置sheet_name参数就可以导入指定Sheet页的数据。
快速示例13 导入指定Sheet页的数据
一个Excel文件中包含多家店铺的销售数据,导入其中一家店铺(如莫寒)的销售数据,如图 12所示。

图 12 原始数据
程序代码如下:
01 import pandas as pd
02 #解决数据输出时列名不对齐的问题
03 pd.set_option('display.unicode.east_asian_width', True)
04 df=pd.read_excel('1月.xlsx',sheet_name='莫寒')
05 print(df.head()) #输出前5条数据
运行程序 ,输出部分数据,结果如图 13所示。

图 13 导入指定的Sheet页(部分数据)
除了指定Sheet页的名字,还可以指定Sheet页的顺序,从0开始。例如,“sheet_name=0”表示导入第一个Sheet页的数据,“sheet_name=1”表示导入第二个Sheet页的数据,以此类推。
如果不指定sheet_name参数,则默认导入第一个Sheet页的数据。
4.1.3 通过行列索引导入指定行列数据
DataFrame是二维数据结构,因此它既有行索引又有列索引。当导入Excel数据时,行索引会自动生成,如0、1、2,而列索引则默认将第0行作为列索引(如A,B,…,J),如图 14所示。

图 14 DataFrame行列索引示意图
快速示例14 指定行索引导入Excel数据
如果通过指定行索引导入Excel数据,则需要设置index_col参数。下面将“买家会员名”作为行索引(位于第0列),导入Excel数据,程序代码如下:
01 import pandas as pd
02 #解决数据输出时列名不对齐的问题
03 pd.set_option('display.unicode.east_asian_width', True)
04 df1=pd.read_excel('1月.xlsx',index_col=0) #设置“买家会员名”为行索引
05 print(df1.head()) #输出前5条数据
运行程序,输出结果如图 15所示。

图 15 通过设置行索引导入Excel数据
如果通过指定列索引导入Excel数据,则需要设置header参数,关键代码如下:
df2=pd.read_excel('1月.xlsx',header=1) #设置第1行为列索引
运行程序,输出结果如图 16所示。

图 16 通过设置列索引导入Excel数据
如果将数字作为列索引,可以设置header参数为None,关键代码如下:
df3=pd.read_excel('1月.xlsx',header=None) #列索引为数字
运行程序,输出结果如图 17所示。

图 17 设置列索引
那么,为什么要指定索引呢?因为通过索引可以快速地检索数据,例如根据df3[0],就可以快速检索到“买家会员名”这一列数据。
4.1.4 导入指定列数据
一个Excel表中往往包含多列数据,如果只需要其中的几列,可以通过usecols参数指定需要的列,从0开始(表示第1列,依次类推)。
快速示例15 导入第一列数据
下面导入第一列数据(索引为0),程序代码如下:
01 import pandas as pd
02 #解决数据输出时列名不对齐的问题
03 pd.set_option('display.unicode.east_asian_width', True)
04 df1=pd.read_excel('1月.xlsx',usecols=[0]) #导入第一列数据
05 print(df1.head())
运行程序,输出结果如图 18所示。

图 18 导入第一列数据
如果导入多列,则可以在列表中指定多个值。例如,导入第一列和第四列,关键代码如下:
df1=pd.read_excel('1月.xlsx',usecols=[0,3])
也可以指定列名称,关键代码如下:
df1=pd.read_excel('1月.xlsx',usecols=['买家会员名','宝贝标题'])
运行程序,输出结果如图 19所示。

图 19 导入第一列和第四列的数据
4.2 导入.csv文件
导入.csv文件时主要使用Pandas的read_csv方法,语法如下:
pandas.read_csv(filepath_or_buffer,sep=',',delimiter=None,header='infer',names=None,index_col=None,usecols=None,squeeze=False,prefix=None,mangle_dupe_cols=True,dtype=None,engine=None,converters=None,true_values=None,false_values=None,skipinitialspace=False,skiprows=None,nrows=None,na_values=None,keep_default_na=True,na_filter=True,verbose=False,skip_blank_lines=True,parse_dates=False,infer_datetime_format=False,keep_date_col=False,date_parser=None,dayfirst=False,iterator=False,chunksize=None,compression='infer',thousands=None,decimal=b'.',lineterminator=None,quotechar='"',quoting=0,escapechar=None,comment=None, encoding=None)
常用参数说明:
■ filepath_or_buffer:字符串,文件路径,也可以是URL链接。
■ sep、delimiter:字符串,分隔符。
■ header:指定作为列名的行,默认值为0,即取第一行的值为列名。数据为除列名以外的数据;若数据不包含列名,则设置header=None。
■ names:默认值为None,要使用的列名列表。
■ index_col:指定列为索引列,默认值为None,索引0是DataFrame对象的行标签。
■ usecols:int、list或字符串,默认值为None。
● 如果为None,则解析所有列。
● 如果为int,则解析最后一列。
● 如果为list列表,则解析列号、列表的列。
● 如果为字符串,则表示以逗号分隔的Excel列字母和列范围列表(例如,“A:E”或“A,C,E:F”),范围包括双方。
■ dtype:列的数据类型名称或字典,默认值为None。例如,{'a':np.float64,'b':np.int32}。
■ parse_dates:布尔类型值、int类型值的列表、列表或字典,默认值为False。可以通过parse_dates参数直接将某列转换成datetime64的日期类型。例如,“df1=pd.read_csv('1月.csv', parse_dates=['订单付款时间'])”。
● parse_dates为True时,尝试解析索引。
● parse_dates为int类型值组成的列表时,如[1,2,3],则解析1、2、3列的值作为独立的日期列。
● parse_date为列表组成的列表,如[[1,3]],则将1、3列合并,作为一个日期列使用。
● parse_date为字典时,如{'总计':[1, 3]},则将1、3列合并,合并后的列名为“总计”。
■ encoding:字符串,默认值为None,文件的编码格式。
■ 返回值:返回一个DataFrame对象。
快速示例16 导入.csv文件
导入.csv文件,程序代码如下:
01 import pandas as pd
02 #设置数据显示的最大列数和宽度
03 pd.set_option('display.max_columns',500)
04 pd.set_option('display.width',1000)
05 #解决数据输出时列名不对齐的问题
06 pd.set_option('display.unicode.east_asian_width', True)
07 df1=pd.read_csv('1月.csv',encoding='gbk') #导入.csv文件,并指定编码格式
08 print(df1.head()) #输出前5条数据
运行程序,输出结果如图 20所示。

图 20 导入.csv文件
注意:上述代码中指定了编码格式,即encoding='gbk'。Python常用的编码格式是UTF-8和gbk格式,默认编码格式为UTF-8。导入.csv文件时,需要通过encoding参数指定编码格式。当我们将Excel文件另存为.csv文件时,默认编码格式为gbk,此时编写代码导入.csv文件时,就需要设置编码格式为gbk,与原文件的编码格式保持一致,否则会提示错误。
4.3 导入.txt文本文件
导入.txt文件同样使用Pandas模块的read_csv方法,不同的是需要指定sep参数(如制表符/t)。read_csv方法读取.txt文件后将返回一个DataFrame对象,像表格一样的二维数据结构,如图 21所示。

快速示例17 导入.txt文本文件
下面使用read_csv方法导入1月的.txt文件,关键代码如下:
01 import pandas as pd
02 df1=pd.read_csv('1月.txt',sep='\t',encoding='gbk')
03 print(df1.head())
运行程序,输出结果如图 22所示。

图 22 导入.txt文本的效果
4.4 导入HTML网页
导入HTML网页数据主要使用Pandas的read_html方法,该方法用于导入带有table标签的网页表格数据,语法如下:
pandas.read_html(io,match='.+',flavor=None,header=None,index_col=None,skiprows=None,attrs=None,parse_dates=False,thousands=',',encoding=None,decimal='.',converters=None,na_values=None,keep_default_na=True,displayed_only=True)
常用参数说明:
■ io:字符串,文件路径,也可以是URL链接。网址不接受https,可以尝试去掉https中的s后爬取,如http://www.mingribook.com。
■ match:正则表达式,返回与正则表达式匹配的表格。
■ flavor:解析器默认为“lxml”。
■ header:指定列标题所在的行,列表list为多重索引。
■ index_col:指定行标题对应的列,列表list为多重索引。
■ encoding:字符串,默认为None,文件的编码格式。
■ 返回值:返回一个DataFrame对象。
使用read_html方法前,首先要确定网页表格是否为table标签。例如,NBA球员薪资网页(http://www.espn.com/nba/salaries),右键单击该网页中的表格,在弹出的菜单中选择“检查元素”,查看代码中是否含有表格标签<table>…</table>的字样,如图 23所示,确定后才可以使用read_html方法。

图 23 <table>…</table>表格标签页面
快速示例18 导入NBA球员的薪资数据
下面使用read_html方法导入NBA球员的薪资数据,程序代码如下:
01 import pandas as pd
02 df = pd.DataFrame()
03 url_list = ['http://www.espn.com/nba/salaries/_/seasontype/4']
04 for i in range(2, 13):
05 url = 'http://www.espn.com/nba/salaries/_/page/%s/seasontype/4' % i
06 url_list.append(url)
07 #遍历网页中的table标签读取网页表格数据
08 for url in url_list:
09 df = df.append(pd.read_html(url), ignore_index=True)
10 #列表解析:遍历dataframe对象的第3列,以子字符串$开头
11 df = df[[x.startswith('$') for x in df[3]]]
12 print(df)
13 df.to_csv('NBA.csv',header=['RK','NAME','TEAM','SALARY'], index=False) #导出.csv文件
运行程序,输出结果如图 24所示。

图 24 导入网页数据
注意:运行程序,如果出现“ImportError: lxml not found, please install it”的错误提示信息,则需要安装lxml模块。
五、数据抽取
在数据分析过程中,并不是所有的数据都是我们想要的,此时可以抽取部分数据,主要使用DataFrame对象中的loc属性和iloc属性,如图 25所示。

图 25 loc属性和iloc属性示意图
DataFrame对象中的loc属性和iloc属性都可以抽取数据,区别如下:
■ loc属性:以列名(columns)和行名(index)作为参数,当只有一个参数时,默认是行名,即抽取整行数据,包括所有列,如df.loc['A']
■ iloc属性:以行和列位置索引(即0,1,2,…)作为参数,0表示第一行;1表示第二行,以此类推。当只有一个参数时,默认是行索引,即抽取整行数据,包括所有列,如抽取第一行数据,df.iloc[0]。
5.1 抽取一行数据
抽取一行数据需要使用loc属性。
快速示例19 抽取一行考试成绩数据
抽取1行名为“明日”的考试成绩数据(包括所有列),程序代码如下:
01 import pandas as pd
02 #解决数据输出时列名不对齐的问题
03 pd.set_option('display.unicode.east_asian_width', True)
04 data = [[110,105,99],[105,88,115],[109,120,130],[112,115]]
05 name = ['明日','七月流火','高袁圆','二月二']
06 columns = ['语文','数学','英语']
07 df = pd.DataFrame(data=data, index=name, columns=columns)
08 print(df.loc['明日'])
运行程序,输出结果如图 26所示。

图 26 抽取一行数据
想要使用iloc属性抽取第一行数据,指定行索引即可,如df.iloc[0],输出结果同图3.26一样。
5.2 抽取多行数据
■ 抽取任意多行数据
通过loc属性和iloc属性指定行名和行索引即可实现抽取任意多行数据。
快速示例20 抽取多行考试成绩数据
抽取行名为“明日”和“高袁圆”(即第1行和第3行数据)的考试成绩数据,关键代码如下:
01 print(df.loc[['明日','高袁圆']])
02 print(df.iloc[[0,2]])
运行程序,输出结果如图 27所示。

图 27 抽取多行数据
■ 抽取连续任意多行数据
在loc属性和iloc属性中合理使用冒号“:”,即可抽取连续任意多行数据。
快速示例21 抽取连续几个学生的考试成绩
实现抽取连续几个学生的考试成绩,关键代码如下:
01 print(df.loc['明日':'二月二']) #从“明日”到“二月二”
02 print(df.loc[:'七月流火':]) #第1行到“七月流火”
03 print(df.iloc[0:4]) #第1行到第4行
04 print(df.iloc[1::]) #第2行到最后1行
运行程序,输出结果如图 28所示。

图 28 抽取连续几个学生的考试成绩
5.3 抽取指定列数据
想要抽取指定列数据,可以直接使用列名,也可以使用loc属性和iloc属性。
■ 直接使用列名
快速示例22 抽取“语文”和“数学”的考试成绩
抽取列名为“语文”和“数学”的考试成绩数据,程序代码如下:
01 import pandas as pd
02 #解决数据输出时列名不对齐的问题
03 pd.set_option('display.unicode.east_asian_width', True)
04 data = [[110,105,99],[105,88,115],[109,120,130],[112,115]]
05 name = ['明日','七月流火','高袁圆','二月二']
06 columns = ['语文','数学','英语']
07 df = pd.DataFrame(data=data, index=name, columns=columns)
08 print(df[['语文','数学']])
运行程序,输出结果如图 29所示。

图 29 直接使用列名
■ 使用loc属性和iloc属性
前面介绍loc属性和iloc属性都包含了两个参数,第一个参数代表行;第二个参数代表列,那么这里在抽取指定列数据时,行参数不能省略。
快速示例23 抽取指定学科的考试成绩
下面使用loc属性和iloc属性抽取指定列数据,关键代码如下:
01 print(df.loc[:,['语文','数学']]) #抽取“语文”和“数学”
02 print(df.iloc[:,[0,1]]) #抽取第1列和第2列
03 print(df.loc[:,'语文':]) #抽取从“语文”开始到最后一列
04 print(df.iloc[:,:2]) #连续抽取从1列开始到第3列,但不包括第3列
运行程序,输出结果如图 30所示。

图 30 使用loc属性和iloc属性抽取指定列数据
5.4 抽取指定行列数据
抽取指定行列数据主要使用loc属性和iloc属性,这两个方法中的两个参数都指定后,就可以实现指定行列数据的抽取。
快速示例24 抽取指定学科和指定学生的考试成绩
使用loc属性和iloc属性抽取指定学科和指定学生的考试成绩,程序代码如下:
01 import pandas as pd
02 #解决数据输出时列名不对齐的问题
03 pd.set_option('display.unicode.east_asian_width', True)
04 data = [[110,105,99],[105,88,115],[109,120,130],[112,115]]
05 name = ['明日','七月流火','高袁圆','二月二']
06 columns = ['语文','数学','英语']
07 df = pd.DataFrame(data=data, index=name, columns=columns)
08 print(df.loc['七月流火','英语']) #“英语”成绩
09 print(df.loc[['七月流火'],['英语']]) #“七月流火”的“英语”成绩
10 print(df.loc[['七月流火'],['数学','英语']]) #“七月流火”的“数学”和“英语”成绩
11 print(df.iloc[[1],[2]]) #第2行第3列
12 print(df.iloc[1:,[2]]) #第2行到最后一行的第3列
13 print(df.iloc[1:,[0,2]]) #第2行到最后一行的第1列和第3列
14 print(df.iloc[:,2]) #所有行,第3列
运行程序,输出结果如图 31所示。

图 31 抽取指定行列数据
在上述结果中,第一个输出结果是一个数字,不是数据,这是由于“df.loc['七月流火','英语']”语句中没有使用方括号[],导致输出的数据不是DataFrame对象。
5.5 按指定条件抽取数据
使用DataFrame对象实现数据查询有以下3种方式:
(1)取其中的一个元素,如.iat[x,x]。
(2)基于位置的查询,如.iloc[]、iloc[2,1]。
(3)基于行列名称的查询,如.loc[x]。
快速示例25 抽取指定学科和指定分数的数据
抽取语文成绩大于105分,数学成绩大于88分的数据,程序代码如下:
01 import pandas as pd
02 #解决数据输出时列名不对齐的问题
03 pd.set_option('display.unicode.east_asian_width', True)
04 data = [[110,105,99],[105,88,115],[109,120,130],[112,115]]
05 name = ['明日','七月流火','高袁圆','二月二']
06 columns = ['语文','数学','英语']
07 df = pd.DataFrame(data=data, index=name, columns=columns)
08 print(df.loc[(df['语文'] > 105) & (df['数学'] >88)])
运行程序,输出结果如图 32所示。

图 32 按指定条件抽取数据
六、数据的增加、修改和删除
本节主要介绍如何操纵DataFrame对象中的各种数据。例如,数据的增加、修改和删除等。
6.1 增加数据
在DataFrame对象中增加数据主要包括列数据和行数据的增加。首先看下原始数据,如图 33所示。

图 33 原始数据
6.1.1 按列增加数据
按列增加数据,可以通过以下3种方式实现:
(1)直接为DataFrame对象赋值。
快速示例26 增加一列“物理”成绩
增加一列“物理”成绩,程序代码如下:
01 import pandas as pd
02 #解决数据输出时列名不对齐的问题
03 pd.set_option('display.unicode.east_asian_width', True)
04 data = [[110,105,99],[105,88,115],[109,120,130],[112,115,140]]
05 name = ['明日','七月流火','高袁圆','二月二']
06 columns = ['语文','数学','英语']
07 df = pd.DataFrame(data=data, index=name, columns=columns)
08 df['物理']=[88,79,60,50]
09 print(df)
运行程序,输出结果如图 34所示。

图 34 按列增加数据
(2)使用loc属性在DataFrame对象的最后增加一列。
快速示例27 使用loc属性增加一列“物理”成绩
使用loc属性在DataFrame对象的最后增加一列。例如,增加“物理”一列,关键代码如下:
df.loc[:,'物理'] = [88,79,60,50]
在DataFrame对象的最后增加一列“物理”,其值为等号的右边数据。
(3)在指定位置插入一列。
在指定位置插入一列,主要使用insert方法。
快速示例28 在第一列的后面插入“物理”成绩
例如,在第一列的后面插入“物理”,其值为wl的数值,关键代码如下:
01 wl =[88,79,60,50]
02 df.insert(1,'物理',wl)
03 print(df)
运行程序,输出结果如图 35所示。

图 35 使用insert方法在第一列的后面增加数据
6.1.2 按行增加数据
按行增加数据,可以通过以下两种方式实现:
(1)增加一行数据。
增加一行数据主要使用loc属性实现。
快速示例29 在成绩表中增加一行数据
在成绩表中增加一行数据,即“钱多多”同学的成绩,关键代码如下:
df.loc['钱多多'] = [100,120,99]
(2)增加多行数据
增加多行数据主要使用字典并结合append方法实现。
快速示例30 在原有数据中增加几名同学的考试成绩
在原有数据中增加“钱多多”“童年”和“无名”同学的考试成绩,关键代码如下:
01 df_insert=pd.DataFrame({'语文':[100,123,138],'数学':[99,142,60],'英语':[98,139,99]},index = ['钱多多','童年','无名'])
02 df1 = df.append(df_insert)
运行程序,输出结果如图 36和图 37所示。

图 36 增加一行数据

图 37 增加多行数据
6.2 修改数据
修改数据包括行列标题和数据的修改,首先看下原始数据,如图 38所示。

图 38 原始数据
6.2.1 修改列标题
修改列标题主要使用DataFrame对象中的cloumns属性,直接赋值即可。
快速示例31 修改“数学”的列名
将“数学”修改为“数学(上)”,关键代码如下:
df.columns=['语文','数学(上)','英语']
在上述代码中,即使只修改“数学”为“数学(上)”,但是也要将所有列的标题全部写上,否则将报错。
下面再介绍一种方法,使用DataFrame对象中的rename方法修改列标题。
快速示例32 修改多个学科的列名
将“语文”修改为“语文(上)”“数学”修改为“数学(上)”“英语”修改为“英语(上)”,关键代码如下:
df.rename(columns = {'语文':'语文(上)','数学':'数学(上)','英语':'英语(上)'},inplace = True)
在上述代码中,参数inplace为True,表示直接修改df;否则不修改df,只返回修改后的数据。
运行程序,输出结果如图 39和图 40所示。

图 39 修改列标题1

图 40 修改列标题2
6.2.2 修改行标题
修改行标题主要使用DataFrame对象中的index属性,直接赋值即可。
快速示例33 将行标题统一修改为数字编号
将行标题统一修改为数字编号,关键代码如下:
df.index=list('1234')
使用DataFrame对象中的rename方法也可以修改行标题。例如,将行标题统一修改为数字编号,关键代码如下:
df.rename({'明日':1,'七月流火':2,'高袁圆':3,'二月二':4},axis=0,inplace = True)
6.2.3 修改数据
修改数据主要使用DataFrame对象中的loc属性和iloc属性。
快速示例34 修改学生成绩数据
(1)修改整行数据
例如,修改“明日”同学的各科成绩,关键代码如下:
df.loc['明日']=[120,115,109]
如果各科成绩均加10分,可以直接在原有值加10,关键代码如下:
df.loc['明日']=df.loc['明日']+10
(2)修改整列数据
例如,修改所有同学的“语文”成绩,关键代码如下:
df.loc[:,'语文']=[115,108,112,118]
(3)修改某一处数据
例如,修改“明日”同学的“语文”成绩,关键代码如下:
df.loc['明日','语文']=115
(4)使用iloc属性修改数据
通过iloc属性指定行列位置实现修改数据,关键代码如下:
01df.iloc[0,0]=115 #修改某一处数据
02df.iloc[:,0]=[115,108,112,118] #修改整列数据
03df.iloc[0,:]=[120,115,109] #修改整行数据
6.3 删除数据
删除数据主要使用DataFrame对象中的drop方法。语法如下:
DataFrame.drop(labels=None, axis=0, index=None, columns=None, level=None, inplace=False, errors='raise')
参数说明:
■ labels:表示行标签或列标签。
■ axis:axis = 0,表示按行删除;axis = 1,表示按列删除,默认值为0。
■ index:删除行,默认值为None。
■ columns:删除列,默认值为None。
■ level:针对有两级索引的数据。level = 0,表示按第1级索引删除整行;level = 1,表示按第2级索引删除整行,默认值为None。
■ inplace:可选参数,对原数组作出修改并返回一个新数组。默认值为False,如果值为True,那么原数组直接就将被替换。
■ errors:参数值为ignore或raise,默认值为raise,如果值为ignore(忽略),则取消错误。
6.3.1 删除行列数据
快速示例35 删除指定的学生成绩数据
删除指定的学生成绩数据,关键代码如下:
01 df.drop(['数学'],axis=1,inplace=True) #删除某列
02 df.drop(columns='数学',inplace=True) #删除columns为“数学”的列
03 df.drop(labels='数学', axis=1,inplace=True) #删除列标签为“数学”的列
04 df.drop(['明日','二月二'],inplace=True) #删除某一行
05 df.drop(index='明日',inplace=True) #删除index为“明日”的行
06 df.drop(labels='明日', axis=0,inplace=True) #删除行标签为“明日”的行
6.3.2 删除特定条件的行
删除满足特定条件的行,首先找到满足该条件的行索引,然后再使用drop方法将其删除。
快速示例36 删除符合条件的学生成绩数据
删除“数学”中包含分数88的行“语文”小于分数110的行,关键代码如下:
01 df.drop(index=df[df['数学'].isin([88])].index[0],inplace=True)#删除“数学”包含分数88的行
02 df.drop(index=df[df['语文']<110].index[0],inplace=True) #删除“语文”小于分数110的行
说明:以上代码中的方法都可以实现删除指定的行列数据,读者自行选择一种就可以。
七、 数据清洗
7.1 查看与处理缺失值
缺失值指的是由于某种原因导致数据为空,这种情况一般有四种处理方式:一是不处理;二是删除;三是填充或替换,四是插值(以均值、中位数、众数等填补)。
7.1.1 查看缺失值
首先需要找到缺失值,主要使用DataFrame对象中的info方法。
快速示例37 查看数据概况
以淘宝销售数据为例,首先输出数据,然后使用info方法查看数据,程序代码如下:
01 import pandas as pd
02 df=pd.read_excel('TB2018.xls')
03 print(df)
04 print(df.info())
运行程序,输出结果如图 41所示。

图 41 查看缺失值
在Python中,缺失值一般以NaN表示,如图3.41所示,通过info方法可以看到“买家会员名”“买家实际支付金额”“宝贝标题”和“订单付款时间”的非空数量是10,而“宝贝总数量”和“类别”的非空数量是8,则说明这两项存在空值。
快速示例38 判断数据是否存在缺失值
现在判断数据是否存在缺失值,还可以使用isnull方法和notnull方法,关键代码如下:
01 print(df.isnull())
02 print(df.notnull())
运行程序,输出结果如图 42所示。

图 42 判断是否存在缺失值
使用isnull方法,缺失值返回True;非缺失值返回False;而notnull方法与isnull方法正好相反,即缺失值返回False;非缺失值返回True。
如果使用“df[df.isnull() == False]”语句,则会将所有不是缺失值的数据找出来,但是只针对Series对象。
7.1.2 缺失值删除处理
通过前面的判断得知了数据缺失情况,下面将缺失值删除,主要使用dropna方法,该方法用于删除含有缺失值的行,关键代码如下:
df.dropna()
运行程序,输出结果如图 43所示。

图43 缺失值删除处理效果1
说明:有些时候,数据可能存在整行为空的情况,此时可以在dropna方法中指定参数how='all',删除所有空行。
从运行结果得知:dropna方法将所有包含缺失值的数据全部删除了。那么,此时如果认为有些数据虽然存在缺失值,但是不影响数据分析,那么可以使用以下方法进行处理。例如,在上述数据中只保留“宝贝总数量”中不存在缺失值的数据,而类别是否缺失无所谓,则可以使用notnull方法判断,关键代码如下:
df1=df[df['宝贝总数量'].notnull()]
运行程序,输出结果如图 44所示。

图 44 缺失值删除处理效果2
7.1.3 缺失值填充处理
对于缺失数据,如果比例高于30%,则可以选择放弃这个指标,进行删除处理;低于30%时,尽量不要删除,而是选择将这部分数据填充,一般以0、均值、众数(大多数)填充。DataFrame对象中的fillna函数可以实现填充缺失数据,pad/ffill函数表示用前一个非缺失值去填充该缺失值;backfill/bfill函数表示用下一个非缺失值填充该缺失值;None用于指定一个值去替换缺失值。
快速示例39 将NaN填充为0
对于用于计算的数值型数据,如果为空,可以选择用“0”填充。例如,将“宝贝总数量”为空的数据填充为“0”,关键代码如下:
df['宝贝总数量'] = df['宝贝总数量'].fillna(0)
运行程序,输出结果如图 45所示。

图 45 缺失值填充处理
7.2 重复值处理
对于数据中存在的重复数据,包括重复的行或者某几行中某几列的值重复,一般做删除处理,主要使用DataFrame对象中的drop_duplicates方法。
快速示例40 处理淘宝电商销售数据中的重复数据
下面以“1月.xlsx”的淘宝销售数据为例,对其中的重复数据进行处理。
■ 判断每一行数据是否重复(完全相同)
df1.duplicated()
如果返回值为False,表示不重复;返回值为True,表示重复。
■ 去除全部的重复数据
df1.drop_duplicates()
■ 去除指定列的重复数据
df1.drop_duplicates(['买家会员名'])
■ 保留重复行中的最后一行
df1.drop_duplicates(['买家会员名'],keep='last')
说明:以上代码中参数keep的值有三个。当keep='first'表示保留第一次出现的重复行时,是默认值;当keep为另外两个取值last和False时,分别表示保留最后一次出现的重复行和去除所有的重复行。
■ 直接删除,保留一个副本
df1.drop_duplicates(['买家会员名','买家支付宝账号'],inplace=Fasle)
inplace=True表示直接在原来的DataFrame对象上删除重复项,而默认值False表示删除重复项后再生成一个副本。
7.3 异常值的检测与处理
首先了解一下什么是异常值。在数据分析中,异常值是指超出或低于正常范围的值,如年龄大于200、身高大于3米、宝贝总数量为负数等类似数据。那么这些数据如何检测呢?主要有以下几种方法:
■ 根据给定的数据范围进行判断,不在范围内的数据视为异常值。
■ 均方差。
在统计学中,如果一个数据分布近似正态分布(数据分布的一种形式,呈钟型,两头低,中间高,左右对称,因其曲线呈钟形),那么大约68%的数据值都会在均值的一个标准差范围内,大约95%的数据值会在两个标准差范围内,大约99.7%的数据值会在三个标准差范围内。
■ 箱形图。
箱形图是显示一组数据分散情况资料的统计图。它可以将数据通过四分位数的形式进行图形化描述,箱形图通过上限和下限作为数据分布的边界。任何高于上限或低于下限的数据都可以认为是异常值,如图 46所示。

图 46 箱形图
说明:有关箱形图的介绍以及如何通过箱形图识别异常值可参见第5章的内容。
了解异常值的检测后,接下来介绍如何处理异常值,主要包括以下几种处理方式:
(1)最常用的方式是删除。
(2)将异常值当缺失值处理,以某个值填充。
(3)将异常值当特殊情况进行分析,研究异常值出现的原因。
八、索引设置
索引能够快速查询数据,本节主要介绍索引的作用以及应用。
8.1 索引的作用
索引的作用相当于图书的目录,可以根据目录中的页码快速找到所需的内容。Pandas索引的作用如下:
■ 更方便地查询数据。
■ 使用索引可以提升查询性能。
● 如果索引是唯一的,Pandas会使用哈希表优化,查找数据的时间复杂度为O(1)。
● 如果索引不是唯一的,但是有序,Pandas会使用二分查找算法,查找数据的时间复杂度为O(logN)。
● 如果索引是完全随机的,那么每次查询都要扫描数据表,查找数据的时间复杂度为O(N)。
利用索引实现自动的数据对齐功能,如图 47所示。

图 47 自动数据对齐示意图
实现上述效果,程序代码如下:
01 import pandas as pd
02 s1 = pd.Series([10,20,30],index= list("abc"))
03 s2 = pd.Series([2,3,4],index=list("bcd"))
04 print(s1 + s2)
■ 强大的数据结构。
● 基于分类数的索引,提升性能。
● 多维索引,用于group by多维聚合结果等。
● 时间类型索引,强大的日期和时间的方法支持。
8.2 重新设置索引
Pandas有一个很重要的方法是reindex,它的作用是创建一个适应新索引的新对象。语法如下:
DataFrame.reindex(labels = None,index = None,column = None,axis = None,method = None,copy = True,level = None,fill_value = nan,limit = None,tolerance = None)
常用参数说明:
■ labels:标签,可以是数组,默认值为None。
■ index:行索引,默认值为None。
■ columns:列索引,默认值为None。
■ axis:轴,0表示行;1表示列,默认值为None。
■ method:默认值为None,重新设置索引时,选择插值(用来填充缺失数据)方法,其值可以是None、bfill/backfill(向后填充)、ffill/pad(向前填充)等。
■ fill_value:缺失值要填充的数据。如缺失值不用NaN填充,用0填充,则设置“fill_value=0”即可。
8.2.1 对Series对象重新设置索引
快速示例41 重新设置物理成绩的索引
在3.2.3小节已经建立了一组学生的物理成绩,下面重新设置索引,程序代码如下:
01 import pandas as pd
02 s1=pd.Series([88,60,75],index=[1,2,3])
03 print(s1)
04 print(s1.reindex([1,2,3,4,5]))
运行程序,对比效果如图 48和图 49所示。

图 48 原数据

图 49 重新设置索引
从运行结果得知:reindex方法根据新索引进行了重新排序,并且对缺失值自动填充NaN。如果不想用NaN填充,可以为fill_value参数指定值,例如0,关键代码如下:
s1.reindex([1,2,3,4,5],fill_value=0)
而对于有一定顺序的数据,则可能需要插值来填充缺失的数据,这时可以使用method参数。
快速示例42 向前和向后填充数据
实现向前填充(和前面数据一样)、向后填充(和后面数据一样),关键代码如下:
01 print(s1.reindex([1,2,3,4,5],method='ffill')) #向前填充
02 print(s1.reindex([1,2,3,4,5],method='bfill')) #向后填充
8.2.2 对DataFrame对象重新设置索引
对于DataFrame对象,reindex方法用于修改行索引和列索引。
快速示例43 创建成绩表并重新设置索引
通过二维数组创建成绩表,程序代码如下:
01 timport pandas as pd
02 #解决数据输出时列名不对齐的问题
03 pd.set_option('display.unicode.east_asian_width', True)
04 data = [[110,105,99],[105,88,115],[109,120,130]]
05 index=['mr001','mr003','mr005']
06 columns = ['语文','数学','英语']
07 tdf = pd.DataFrame(data=data, index=index,columns=columns)
08 print(df)
通过reindex方法重新设置行索引,关键代码如下:
df.reindex(['mr001','mr002','mr003','mr004','mr005'])
通过reindex方法重新设置列索引,关键代码如下:
df.reindex(columns=['语文','物理','数学','英语'])
通过reindex方法重新设置行索引和列索引,关键代码如下:
df.reindex(index=['mr001','mr002','mr003','mr004','mr005'],columns=['语文','物理','数学','英语'])
运行程序,如图 50所示,为原始数据、重新设置行索引、重新设置列索引、重新设置行列索引。

图 50 原始数据、重新设置行索引、重新设置列索引、重新设置行列索引
8.3 设置某列为索引
设置某列为行索引主要使用set_index方法。
快速示例44 设置“买家会员名”为行索引
首先,导入“1月.xlsx”的Excel文件,程序代码如下:
01 import pandas as pd
02 #解决数据输出时列名不对齐的问题
03 pd.set_option('display.unicode.east_asian_width', True)
04 df=pd.read_excel('1月.xlsx')
05 print(df.head())
运行程序,输出结果如图 51所示。

图 51 1月淘宝销售数据(部分数据)
此时默认行索引为0、1、2、3、4,下面将“买家会员名”作为行索引,关键代码如下:
df=df.set_index(['买家会员名'])
运行程序,输出结果如图 52所示。

图 52 设置“买家会员名”为索引
如果在set_index方法中传入参数“drop=True”,则会删除“买家会员名”;如果传入“drop=False”,则会保留“买家会员名”,默认为False。
8.4 数据清洗后重新设置连续的行索引
当我们对Dataframe对象进行数据清洗之后,例如,去掉含NaN的行之后,发现行索引没有变化,对比效果如图 53和图 54所示。

图 53 原始数据效果

图 54 数据清洗后索引还是原来的索引
快速示例45 删除数据后重新设置索引
如果要重新设置索引则可以使用reset_index方法,在删除缺失数据后重新设置索引,关键代码如下:
df=df.dropna().reset_index(drop=True)
运行程序,输出结果如图 55所示。

图 55 数据清洗后重新设置连续的行索引
另外,对于分组统计后的数据,有时也需要重新设置连续的行索引,方法同上。
九、数据排序与排名
本节主要介绍数据的各种排序和排名方法。
9.1 数据排序
DataFrame数据排序时主要使用sort_values方法,该方法类似于SQL中的order by方法。sort_values方法可以根据指定行/列进行排序,语法如下:
DataFrame.sort_values(by,axis=0,ascending=True,inplace=False,kind='quicksort',na_position='last',ignore_index=False)
参数说明:
■ by:要排序的名称列表。
■ axis:轴,0表示行;1表示列,默认按行排序。
■ ascending:升序或降序排序,布尔值,指定多个排序可以使用布尔值列表,降序。
■ inplace:布尔值,默认值为False,如果值为True,则就地排序。
■ kind:指定排序算法,值为“quicksort”(快速排序)、“mergesort”(混合排序)或“heapsort”(堆排),默认值为“quicksort”。
■ na_position:空值(NaN)的位置,值为“first”空值在数据开头,值为“last”空值在数据最后,默认值为“last”。
■ ignore_index:布尔值,是否忽略索引,值为True标记索引(从0开始按顺序的整数值),值为False则忽略索引。
9.1.1 按一列数据排序
快速示例46 按“销量”进行降序排序
按“销量”降序排序,排序的对比效果如图 56和图 57所示。

图 56 原始数据

图 57 按“销量”降序排序
程序代码如下:
01 import pandas as pd
02 excelFile = 'mrbook.xlsx'
03 df = pd.DataFrame(pd.read_excel(excelFile))
04 #设置数据显示的列数和宽度
05 pd.set_option('display.max_columns',500)
06 pd.set_option('display.width',1000)
07 #解决数据输出时列名不对齐的问题
08 pd.set_option('display.unicode.ambiguous_as_wide', True)
09 pd.set_option('display.unicode.east_asian_width', True)
10 #按“销量”列升序排序
11 df=df.sort_values(by='销量',ascending=False)
12 print(df)
9.1.2 按多列数据排序
多列排序是按照给定列的先后顺序进行排序。
快速示例47 按照“图书名称”和“销量”降序排序
按照“图书名称”和“销量”进行降序排序,首先按“图书名称”降序排序,然后再按“销量”降序排序,关键代码如下:
df.sort_values(by=['图书名称','销量'])
排序后的效果如图 58所示。

图 58 按照“图书名称”和“销量”降序排序
9.1.3 对统计结果排序
快速示例48 对分组统计数据进行排序
按“类别”分组统计销量并进行降序排序,排序后的效果如图 59所示。

图 59 按“类别”分组统计销量并降序排序
关键代码如下:
01 df1=df.groupby(["类别"])["销量"].sum().reset_index()
02 df2=df1.sort_values(by='销量',ascending=False)
9.1.4 按行数据排序
快速示例49 按行数据进行排序
按行排序,关键代码如下:
dfrow.sort_values(by=0,ascending=True,axis=1)
注意:按行排序的数据类型要一致,否则会出现错误提示。
9.2 数据排名
排名是根据Series或DataFrame对象的某几列的值进行排名,主要使用rank方法,语法如下:
DataFrame.rank(axis=0,method='average',numeric_only=None,na_option='keep',ascending=True,pct=False)
参数说明:
■ axis:轴,0表示行;1表示列,默认按行排序。
■ method:表示在具有相同值的情况下所使用的排序方法。设置值如下:
● average:默认值,平均排名。
● min:最小值排名。
● max:最大值排名。
● first:按值在原始数据中的出现的顺序分配排名。
● dense:密集排名,类似最小值排名,但是排名每次只增加1,即排名相同的数据只占一个名次。
■ numeric_only:对于DataFrame对象,如果设置值为True,则只对数字列进行排序。
■ na_option:空值的排序方式,设置值如下:
● keep:保留,将空值等级赋值给NaN值。
● top:如果按升序排序,则将最小排名赋值给NaN值。
● bottom:如果按升序排序,则将最大排名赋值给NaN值。
■ ascending:升序或降序排序,布尔值,指定多个排序可以使用布尔值列表,默认值为True。
■ pct:布尔值,是否以百分比形式返回排名,默认值为False。
9.2.1 顺序排名
快速示例50 对产品销量按顺序进行排名
下面对销量相同的产品,按照出现的先后顺序进行排名,程序结果如图所示,程序代码如下:
01 import pandas as pd
02 excelFile = 'mrbook.xlsx'
03 df = pd.DataFrame(pd.read_excel(excelFile))
04 #设置数据显示的列数和宽度
05 pd.set_option('display.max_columns',500)
06 pd.set_option('display.width',1000)
07 #解决数据输出时列名不对齐的问题
08 pd.set_option('display.unicode.ambiguous_as_wide', True)
09 pd.set_option('display.unicode.east_asian_width', True)
10 #按“销量”列降序排序
11 df=df.sort_values(by='销量',ascending=False)
12 # 顺序排名
13 df['顺序排名'] = df['销量'].rank(method="first", ascending=False)
14 print(df[['图书名称', '销量', '顺序排名']])
9.2.2 平均排名
快速示例51 对产品销量进行平均排名
现在对销量相同的产品,按照顺序排名的平均值进行平均排名,关键代码如下:
df['平均排名']=df['销量'].rank(ascending=False)
运行程序,下面对比一下顺序排名与平均值排名的不同,效果如图 60和 61所示。

图 60 销量相同按出现的先后顺序排名

图 61 销量相同按顺序排名的平均值排名
9.2.3 最小值排名
销量相同的,按顺序排名并取最小值作为排名,关键代码如下:
df['销量'].rank(method="min",ascending=False)
9.2.4 最大值排名
销量相同的,按顺序排名并取最大值作为排名,关键代码如下:
df['销量'].rank(method="max",ascending=False)

