第5章

数据处理基础




在Excel中,无论是用函数,还是用VBA做数据处理,都需要用到运算符、分支判断、循环和数组等技术,以及一些常见的数据统计方法。本章将讲解这些技术,并且在Pandas中的表示方法更加简洁。
本章主要讲解Pandas中的常用运算符,使用这些运算符对单值、Series和DataFrame数据做运算,条件分支判断,对Series和DataFrame这两种数据的遍历以及常用的数据统计函数。
5.1数据运算处理
在Excel中写公式时,如果要用到数组运算,一般是单值、一维数组和二维数组这3种数据之间的运算。在Pandas中如果要进行数组运算,一般是单值、Series(一维数组)和DataFrame(二维数组)之间的运算。
在Pandas中进行数据运算时,尽量做矩阵运算(数组运算),避免单值与单值之间进行运算,这样才能体现Pandas处理速度快的优势。
5.1.1运算符与运算函数
想要学习Pandas中的数组运算,首先要学习一下常用的运算符,下面罗列了在Pandas中提供的常用运算符,见表51。


表51Pandas常用运算符



运算符类型运 算 符 号函数注释

算术运算符
+add加
-sub减
*mul乘
/div除

求模运算符%mod求余数
求幂运算符**pow幂次方

比较运算符
==等于
!=不等于
>大于
>=大于或等于

续表


运算符类型运 算 符 号函数注释

比较运算符
<小于
<=小于或等于

连接运算符+连接

逻辑运算符
&and与
|or或
~not非

在5.1.2~5.1.6节中,以加运算为例,使用符号和函数两种方法演示运算符是如何在不同数据结构中进行运算的。
提示: 读者在测试符号和函数这两种方法时,必须注释其中的一种方法,否则结果会有累加。
5.1.2Series与单值的运算
如图51(a)所示为原表格,对语文列中的每个数字加1,分别用符号和函数两种方法完成,示例代码如下: 


#chapter5\5-1\5-1-1.ipynb

import pandas as pd			#导入Pandas库,并命名为pd

df=pd.read_excel('5-1-1.xlsx','1月')	#读取Excel中的1月工作表数据

df['语文']=df['语文']+1		#用符号进行相加运算(运算符)

df['语文']=df['语文'].add(1)		#用函数进行相加运算(函数)

df					#输出df表数据

运行结果如图51(b)所示。


图51Series与单值运算


在上面的代码中,df['语文']是Series数据,与单个值1相加,其运算结果也是Series数据,然后赋值给语文列。
5.1.3DataFrame与单值运算
如图52(a)所示为原表格,对语文、数学两列中的每个数字加1,分别用符号和函数两种方法完成,示例代码如下: 


#chapter5\5-1\5-1-2.ipynb

import pandas as pd			#导入Pandas库,并命名为pd

df=pd.read_excel('5-1-2.xlsx','1月')	#读取Excel中的1月数据




df[['语文','数学']]=df[['语文','数学']]+1	#用符号做相加运算(运算符)

df[['语文','数学']]=df[['语文','数学']].add(1)	#用函数做相加运算(函数)

df					#输出df表数据

运行结果如图52(b)所示。


图52DataFrame与单值运算


在上面的代码中,df[['语文','数学']]是DataFrame数据,与单个值1相加,其运算结果也是DataFrame数据,然后赋值给语文、数学两列。
5.1.4Series与Series运算
如图53(a)所示为原表格,将两个表的语文列进行相加运算,分别用符号和函数两种方法完成,示例代码如下: 


#chapter5\5-1\5-1-3.ipynb

import pandas as pd			#导入Pandas库,并命名为pd

df1=pd.read_excel('5-1-3.xlsx','1月')	#读取Excel中的1月数据

df2=pd.read_excel('5-1-3.xlsx','2月')	#读取Excel中的2月数据

df1['语文']=df1['语文']+df2['语文']	#用符号做相加运算(运算符)

df1['语文']=df1['语文'].add(df2['语文'])	#用函数做相加运算(函数)

df1					#输出df1表数据

运行结果如图53(b)所示。


图53两个Series相加


上面代码中,df1['语文']和df2['语文']都是Series数据,两个Series相加的结果也是Series数据,然后根据要求赋值给df1的语文列或者df2的语文列。
5.1.5DataFrame与DataFrame运算
如图54(a)所示为原表格,将原表格中的两个表进行相加运算,分别用符号和函数两种方法完成,示例代码如下: 


#chapter5\5-1\5-1-4.ipynb

import pandas as pd			#导入Pandas库,并命名为pd




df1=pd.read_excel('5-1-4.xlsx','1月')	#读取Excel中的1月数据

df2=pd.read_excel('5-1-4.xlsx','2月')	#读取Excel中的2月数据

df2=df1+df2				#用符号做相加运算(运算符)

df2=df1.add(df2)			#用函数做相加运算(函数)

df2					#输出df2表数据

运行结果如图54(b)所示。


图54两个DataFrame相加


上面代码中,df1和df2都是DataFrame数据,两个DataFrame数据相加的结果也是DataFrame数据,然后赋值给df1或者df2。观察相加运算之后的DataFrame表格可以发现,数字是对应相加的,而字符串是对应相连接的。
5.1.6DataFrame与Series运算
1. 列方向相加
如图55(a)所示为原表格,将表格与Series数据进行相加运算,分别用符号和函数两种方法完成,示例代码如下: 


#chapter5\5-1\5-1-5.ipynb

import pandas as pd			#导入Pandas库,并命名为pd

df=pd.read_excel('5-1-5.xlsx','1月')	#读取Excel中的1月数据

df=df+pd.Series(['A',200,100],index=['姓名','语文','数学'])#用符号做相加运算(运算符)

df=df.add(pd.Series(['A',200,100],index=['姓名','语文','数学']))#用函数做相加运算(函数)

df					#输出df表数据

运行结果如图55(b)所示。


图55DataFrame与Series列方向相加


上面代码中,df是DataFrame数据,与Series数据相加的结果也是DataFrame数据,但要记住DataFrame与Series做运算时,是DataFrame的每列数据与Series每个元素对应做运算,所以Series的元素个数必须与DataFrame的列数相同,否则不能正确运算。
2. 行方向相加
如图56(a)所示为原表格,将表格与Series数据做相加运算,只能用函数方法完成,示例代码如下: 


#chapter5\5-1\5-1-6.ipynb

import pandas as pd			#导入Pandas库,并命名为pd

df=pd.read_excel('5-1-6.xlsx','1月')	#读取Excel中的1月数据

df[['语文','数学']]=df[['语文','数学']].add(pd.Series([10,100,1000],index=[0,1,2]),axis=0) 
#用函数做相加运算(函数)

df					#输出df表数据

运行结果如图56(b)所示。


图56DataFrame与Series行方向相加


上面代码中,df是DataFrame数据,与Series数据相加的结果也是DataFrame数据,但此时如果直接用符号(加号)做运算,不能正确运算,原因在于DataFrame与Series默认在列方向做运算,如果希望在行方向做运算,则只能使用对应的函数,add()函数的axis参数可以指定运算方向,默认值为1,表示列方向,设置为0表示行方向。
5.1.7数据运算时的对齐特性
两个Series或者两个DataFrame在进行运算时,是具有对齐特性的,也就是它们的运算不是按位置对齐运算,而是按照索引对齐运算,下面以加运算为例做演示。
1. Series的对齐特性
如图57(a)所示为原表格,将两个表的数学列相加,分别用符号和函数两种方法完成,使用符号相加的代码为df1['数学']+df2['数学'],示例代码如下: 


#chapter5\5-1\5-1-7.ipynb

import pandas as pd		#导入Pandas库,并命名为pd

df1=pd.read_excel('5-1-7.xlsx','1月',index_col=0) #读取Excel中的1月数据

df2=pd.read_excel('5-1-7.xlsx','2月',index_col=0) #读取Excel中的2月数据

df1['数学']+df2['数学']	#用符号做相加运算(运算符)

运行结果如图57(b)所示。


图57两个Series相加(1)


通过返回结果发现,索引标签相同的值会相加,例如'王二'分别处在不同位置,但一样能够正确相加。同时也发现一个新问题,'小曾'与'小新'两个标签并没有同时出现在两个Series中,返回的结果是缺失值(NaN)。如果希望即使另一个Series中是缺失值,也能相加出结果,则只能使用对应的add()函数,代码为df1['数学'].add(df2['数学'],fill_value=0),其中fill_value=0表示如果有缺失值,则填充为0,示例代码如下: 


#chapter5\5-1\5-1-8.ipynb

import pandas as pd			#导入Pandas库,并命名为pd

df1=pd.read_excel('5-1-8.xlsx','1月',index_col=0) #读取Excel中的1月数据

df2=pd.read_excel('5-1-8.xlsx','2月',index_col=0) #读取Excel中的2月数据

df1['数学'].add(df2['数学'],fill_value=0)	#用函数做相加运算(函数)

如图58(a)所示为原表格,运行结果如图58(b)所示。


图58两个Series相加(2)


2. DataFrame的对齐特性
如图59(a)所示为原表格,将两个表相加,分别用符号和函数两种方法完成,使用符号相加的代码为df1+df2,示例代码如下: 


#chapter5\5-1\5-1-9.ipynb

import pandas as pd	#导入Pandas库,并命名为pd

df1=pd.read_excel('5-1-9.xlsx','1月',index_col=0) #读取Excel中的1月数据

df2=pd.read_excel('5-1-9.xlsx','2月',index_col=0) #读取Excel中的2月数据

df1+df2		#用符号进行相加运算(运算符)

运行结果如图59(b)所示。


图59两个DataFrame相加(1)


在原表格中,两个表格的行索引是姓名,列索引是科目,即使两个表的姓名和科目位置都不相同,也正确相加。同样,'小曾'和'小新'没有同时出现在两个表中,所以返回的结果是缺失值(NaN),如果希望能相加出结果,则需使用对应的add()函数,代码为df1.add(df2,fill_value=0),其中fill_value=0表示如果有缺失值,则填为0,示例代码如下: 


#chapter5\5-1\5-1-10.ipynb

import pandas as pd		#导入Pandas库,并命名为pd

df1=pd.read_excel('5-1-10.xlsx','1月',index_col=0)#读取Excel中的1月数据

df2=pd.read_excel('5-1-10.xlsx','2月',index_col=0) #读取Excel中的2月数据

df1.add(df2,fill_value=0)	#用函数做相加运算(函数)

如图510(a)所示为原表格,运行结果如图510(b)所示。


图510两个DataFrame相加(2)


5.2数据分支判断
在数据处理时,判断处理必不可少,可能需要对数字进行比较判断,对字符串进行匹配判断等。判断结果会产生布尔值 True 或者 False,我们需要根据判断的结果来决定不同的数据处理方式。在Excel中有if()函数可以进行判断,而在Pandas中主要介绍mask()函数、where()函数和np.where()函数对Series、DataFrame两种数据的判断。
5.2.1条件判断处理1(mask()与where())
mask()函数与where()函数结构相同,含义相反。mask()函数在条件成立时做处理,where()函数在条件不成立时做处理,这两个函数均可以对Series和DataFrame进行判断处理,下面以mask()函数为例进行演示说明。
1. Series数据条件判断
例如对pd.Series([99,84,100,79,91])中的元素做判断,如果元素大于90,则对元素加1,否则不进行任何处理,示例代码如下: 


#chapter5\5-2\5-2-1.ipynb

import pandas as pd		#导入Pandas库,并命名为pd

s=pd.Series([99,84,100,79,91])	#Series数据

s.mask(s>90,s+1)		#Series数据中的元素如果大于90,则加1

运行结果如下: 


0100

184

2101

379

492

dtype: int64

下面给出一个实际的应用案例,如图511(a)所示为原表格。如果民族不是'汉',则总分加10分,示例代码如下: 


#chapter5\5-2\5-2-2.ipynb

import pandas as pd			#导入Pandas库,并命名为pd

df=pd.read_excel('5-2-2.xlsx','成绩表')	#读取Excel中的成绩表数据

df['总分'].mask(df['民族']!='汉',df['总分']+10,inplace=True) 

#民族不是汉族,总分加10分

df					#输出df表数据

运行结果如图511(b)所示。


图511对Series进行判断处理


2. DataFrame数据条件判断
例如对下面df表中的数字进行判断,如果数字小于60,则返回0,否则不进行任何处理,示例代码如下: 


#chapter5\5-2\5-2-3.ipynb

import pandas as pd	#导入Pandas库,并命名为pd

df=pd.DataFrame({

 '分数1':[89,59,92,58],

 '分数2':[52,86,71,96]

})			#DataFrame数据

df.mask(df<60,0)	#DataFrame数据中的元素如果小于60,则返回0

运行结果如下: 


分数1分数2

0890

1086

29271

3096

同样,给出一个实际的应用案例,如图512(a)所示为原表格,如果各列的评价等级为差,则返回问号(?),示例代码如下: 


#chapter5\5-2\5-2-4.ipynb

import pandas as pd	#导入Pandas库,并命名为pd

df=pd.read_excel('5-2-4.xlsx','评级表')	#读取Excel中的评级表数据

df.iloc[:,1:]=df.iloc[:,1:].mask(df.iloc[:,1:]=='差','?') 

#如果评价等级为差,则返回问号(?)

df					#输出df表数据

运行结果如图512(b)所示。


图512对DataFrame进行判断处理(1)


5.2.2条件判断处理2(np.where())
通过5.2.1节的学习,我们知道mask()函数只能在条件成立时做处理,where()函数只能在条件不成立时做处理。如果希望可以同时处理,则只能用np.where()函数,它是NumPy库中的函数,所以返回值的数据类型也是数组。np.where()函数同样可以对Series和DataFrame进行判断处理。
1. Series数据条件判断
例如对pd.Series([80,79,97,86,93])中的数字做判断,数字大于或等于90时返回'优',否则返回'差',返回结果为一维数组,示例代码如下: 


#chapter5\5-2\5-2-5.ipynb

import pandas as pd,numpy as np	#导入Pandas库和NumPy库,并命名为pd和np

s=pd.Series([80,79,97,86,93])		#Series数据

np.where(s>=90,'优','差')		#值大于或等于90时返回优,否则返回差

运行结果如下: 


array(['差', '差', '优', '差', '优'], dtype='<U1')

如图513(a)所示为原表格,对分数进行判断。如果分数大于或等于90,则返回'已达标',否则返回'未达标',最后将返回结果添加到新列,示例代码如下: 


#chapter5\5-2\5-2-6.ipynb

import pandas as pd,numpy as np	#导入Pandas库和NumPy库,并命名为pd和np

df=pd.read_excel('5-2-6.xlsx','分数表')	#读取Excel中的分数表数据

df['是否达标']=np.where(df['分数']>=90,'已达标','未达标') 

#分数大于或等于90时返回'已达标',否则返回'未达标'

df					#输出df表结果

运行结果如图513(b)所示。


图513对DataFrame进行判断处理(2)


2. DataFrame数据条件判断
例如对下面df表中的数字进行判断,如果数字大于或等于80,则返回'优',否则返回'差',返回结果为二维数组,示例代码如下: 


#chapter5\5-2\5-2-7.ipynb

import pandas as pd,numpy as np	#导入Pandas库和NumPy库,并命名为pd和np

df=pd.DataFrame({

 '分数1':[89,59,92,58],

 '分数2':[52,86,71,96]

})					#DataFrame数据

np.where(df>=80,'优','差')		#值大于或等于90时返回'优',否则返回'差'

运行结果如下: 


array([	['优', '差'],

['差', '优'],

['优', '差'],

['差', '优']], dtype='<U1')

如图514(a)所示为原表格,对业绩进行判断。如果大于或等于40000,则返回'已达标',否则返回'未达标',示例代码如下:


#chapter5\5-2\5-2-8.ipynb

import pandas as pd,numpy as np	#导入Pandas库和NumPy库,并命名为pd和np

df=pd.read_excel('5-2-8.xlsx','业绩表')	#读取Excel中的业绩表数据

df.iloc[:,1:]=np.where(df.iloc[:,1:]>=40000,'已达标','未达标') 

#业绩大于或等于40000时返回'已达标',否则返回'未达标'

df					#输出df表结果

运行结果如图514(b)所示。


图514对DataFrame进行判断处理(3)


5.3数据遍历处理
遍历是指对一个序列中的所有元素都执行相同操作。遍历也可以通过循环语句来完成,但一般效率比较低。Excel中的VBA编程就有for循环、do循环等语句,当然在Python中也有对应的for循环、while循环。在Pandas中,要遍历Series和DataFrame中的数据,也可以通过循环语句实现。不过,Pandas提供了一些更高效的遍历函数。本节讲解map()、apply()及applymap()这3个最常用的遍历函数。
5.3.1遍历Series元素(map())
map()函数对Series中的每个元素执行遍历处理,该函数的参数可以是字典,也可以是函数。
1. map()的参数为字典
Series中有优、中、差3种值,如果希望'优'返回10,'中'返回5,'差'返回1,则在map()函数中写入{'优':10,'中':5,'差':1},示例代码如下: 


#chapter5\5-3\5-3-1.ipynb

import pandas as pd			#导入Pandas库,并命名为pd

s=pd.Series(['优','中','优','中','差'])	#Series数据

s.map({'优':10,'中':5,'差':1})		#map()的参数为字典

运行结果如下: 


010

15

210

35

41

dtype: int64

2. map()的参数为内置函数
如果Series中的每个元素都是数字,并且希望格式化每个数字,则在map()函数中写入'{}分'.format,其中format()函数是Python中的内置函数,示例代码如下: 


#chapter5\5-3\5-3-2.ipynb

import pandas as pd		#导入Pandas库,并命名为pd

s=pd.Series([80,79,97,86,93])	#Series数据

s.map('{}分'.format)		#map()的参数为内置函数

运行结果如下: 


080分

179分

297分

386分

493分

dtype: object

3. map()的参数为自定义函数
如果Series中的元素是数字,并且希望对每个数字除以2,则需要自定义一个除以2的函数,然后将自定义的函数名称写入map()函数中,示例代码如下: 

#chapter5\5-3\5-3-3.ipynb

import pandas as pd		#导入Pandas库,并命名为pd

s=pd.Series([80,79,97,86,93])	#Series数据

def fun(x):			#自定义fun()函数

return x/2

s.map(fun)			#map()的参数为自定义函数

运行结果如下: 


040.0

139.5

248.5

343.0

446.5

dtype: float64

4. map()的参数为匿名函数
在用自定义函数作为map()函数的参数时,如果自定义函数的处理比较简单,则可以使用匿名函数,将匿名函数写在map()函数中即可。同样,要求将Series中的每个数字除以2,示例代码如下: 


#chapter5\5-3\5-3-4.ipynb

import pandas as pd		#导入Pandas库,并命名为pd

s=pd.Series([80,79,97,86,93])	#Series数据

s.map(lambda x:x/2)		#map()的参数为匿名函数

运行结果如下: 


040.0

139.5

248.5

343.0

446.5

dtype: float64

在s.map(lambda x:x/2)中x代表获取Series中的每个元素,然后将其除以2。返回的结果也是Series类型数据。
5.3.2遍历DataFrame行和列(apply())
apply()函数可以像map()函数一样遍历Series中的每个元素,但主要是使用apply()函数来遍历DatFrame的行或列,遍历出来每行或每列均是Series数据。apply()函数也可以接收内置函数、自定义函数和匿名函数作为参数。
1. 遍历DataFrame的每列
如图515原表格所示,将表格中所有科目的分数按列求和,示例代码如下: 


#chapter5\5-3\5-3-5.ipynb

import pandas as pd			#导入Pandas库,并命名为pd

df=pd.read_excel('5-3-5.xlsx','成绩表')	#读取Excel中的成绩表数据

df.loc[:,'语文':].apply(sum,axis=0)	#按列遍历并求和

运行结果如下: 


语文480

数学438

英语422

dtype: int64

在代码df.loc[:,'语文':].apply(sum,axis=0)中,sum对遍历出来的每列数据(Series数据)做求和计算,axis=1表示按行方向遍历,axis=0表示按列方向遍历,默认值为0。因为求和的表格中只有3列数据,所以结果也是由3个数字组成的Series数据。
如果希望将计算出的结果添加到原表格下面,示例代码如下: 


#chapter5\5-3\5-3-6.ipynb

import pandas as pd	#导入Pandas库,并命名为pd

df=pd.read_excel('5-3-6.xlsx','成绩表')	#读取Excel中的成绩表数据

df.loc[len(df)]=df.loc[:,'语文':].apply(sum,axis=0) #按列遍历并求和

df					#输出df表数据

如图515(a)所示为原表格,运行结果如图515(b)所示。


图515对DataFrame按列进行遍历处理(1)


2. 遍历DataFrame的每行
如图516所示为原表格,将表格中所有科目的分数按行求和,也就是对每个人的各科成绩求和,示例代码如下: 


#chapter5\5-3\5-3-7.ipynb

import pandas as pd			#导入Pandas库,并命名为pd

df=pd.read_excel('5-3-7.xlsx','成绩表')	#读取Excel中的成绩表数据

df.loc[:,'语文':].apply(sum,axis=1)	#按行遍历并求和

运行结果如下: 


0338

1349

2304

3349

dtype: int64

与之前遍历列的代码基本相同,唯一要修改的是axis=1,因为现在执行按行遍历。如果希望将计算出的结果添加到原表格后面,示例代码如下: 


#chapter5\5-3\5-3-8.ipynb

import pandas as pd				#导入Pandas库,并命名为pd

df=pd.read_excel('5-3-8.xlsx','成绩表')		#读取Excel中的成绩表数据

df['总分']=df.loc[:,'语文':].apply(sum,axis=1)	#按行遍历并求和

df						#输出df表数据

运行结果如图516(b)所示。


图516对DataFrame按行进行遍历处理(2)


5.3.3遍历DataFrame元素(applymap())
applymap()函数的用法比较简单,是对DataFrame中的每个元素执行指定函数的处理,虽然用途不如apply()广泛,但在某些场合下还是比较有用的,例如将数字保留到小数点后两位,示例代码如下: 


#chapter5\5-3\5-3-9.ipynb

import pandas as pd			#导入Pandas库,并命名为pd

df=pd.read_excel('5-3-9.xlsx','成绩表')	#读取Excel中的成绩表数据

df.iloc[:,1:].applymap('{:.2f}'.format).astype(float) 

#遍历处理DataFrame表格的每个元素

如图517(a)所示为原表格,运行结果如图517(b)所示。


图517对DataFrame的每个元素进行遍历处理


值得注意的是,使用format()函数格式化数字后,是字符串类型,所以还要在后面加上astype(float),转换为小数类型。
如果要将修改后的表格区域写回原表格,示例代码如下: 


#chapter5\5-3\5-3-10.ipynb

import pandas as pd			#导入Pandas库,并命名为pd

df=pd.read_excel('5-3-10.xlsx','成绩表')	#读取Excel中的成绩表数据

df.iloc[:,1:]=df.iloc[:,1:].applymap('{:.2f}'.format).astype(float)

#遍历处理DataFrame表格的每个元素

df					#输出df表数据

如图518(a)所示为原表格,运行结果如图518(b)所示。


图518对DataFrame的每个元素进行遍历处理


5.4数据统计处理
在做数据处理时,数据统计是必不可少的,所以需要学习一些常用的统计函数。例如关于聚合的统计、多个布尔值的逻辑统计,以及极值统计、排名统计等。这些统计需求在日常工作中可能比较常见。绝大多数统计需求在Excel中有对应的函数,在Pandas中这些函数功能更强大。
5.4.1聚合统计
聚合统计是最常见的统计方式,下面列出在Python、Pandas和NumPy中常用的聚合函数,见表52。


表52Python、Pandas与NumPy中常用的聚合函数



统 计 方 式PythonPandasNumPy

求和sum()sum()np.sum()
求最大值max()max()np.max()
求最小值min()min()np.min()
求平均值无mean()np.mean()
求计数值len()count()无

上面3种不同聚合函数的区别在于: Python中的聚合函数可以对一组任意形式的序列值做统计; Pandas中的聚合函数只能对Series和DataFrame做统计; NumPy中的聚合函数可以对数组、Series和DataFrame做统计,并且Pandas和NumPy中的聚合函数在对二维数据统计时可以指定聚合方向。
1. Series数据统计
现在分别使用Python、Pandas和NumPy的聚合函数对Series数据进行统计,示例代码如下: 


#chapter5\5-4\5-4-1.ipynb

import pandas as pd,numpy as np	#导入Pandas库和NumPy库,并命名为pd和np

s=pd.Series([1,10,100])	#Series数据

print(sum(s),s.sum(),np.sum(s))	#求和

print(max(s),s.max(),np.max(s)) 	#求最大值

print(min(s),s.min(),np.min(s))	#求最小值

print(s.mean(),np.mean(s))		#求平均值

print(len(s),s.count())		#求计数值

运行结果如下: 


111111111

100100100

111

37.037.0

33

2. DataFrame数据统计
在对DataFrame做数据统计时,聚合函数并不是将整个DataFrame表格的数据计算为一个值,而是对每行或每列执行计算,最后以Series数据结构返回聚合结果。接下来以sum()函数为例,分别演示按行和按列的统计方法。
1) 按行统计
如图519(a)所示为原表格,对表格中的分数按行执行求和统计,在sum()函数的参数中设置axis=1即可,示例代码如下: 


#chapter5\5-4\5-4-2.ipynb

import pandas as pd,numpy as np#导入Pandas库和NumPy库,并命名为pd和np

df=pd.read_excel('5-4-2.xlsx','成绩表')	#读取Excel中的成绩表数据

df.iloc[:,1:4].sum(axis=1)		#按行求和(Pandas函数)

np.sum(df.iloc[:,1:4],axis=1)		#按行求和(NumPy函数)

运行结果如下: 


0338

1349

2304

3349

dtype: int64

上面代码运算的结果是Series数据,存储的是每行求和的结果,接下来将该数据添加为新列,列名为总分,示例代码如下: 


#chapter5\5-4\5-4-3.ipynb

import pandas as pd,numpy as np	#导入Pandas库和NumPy库,并命名为pd和np

df=pd.read_excel('5-4-3.xlsx','成绩表')	#读取Excel中的成绩表数据

df['总分']=df.iloc[:,1:4].sum(axis=1)	#按行求和(Pandas函数)

df['总分']=np.sum(df.iloc[:,1:4],axis=1)	#按行求和(NumPy函数)

df					#输出df表数据

运行结果如图519(b)所示。


图519对DataFrame按行求和


2) 按列统计
如图520(a)所示为原表格,对表格中的分数按列执行求和统计,在sum()函数的参数中设置axis=0即可,因为0是默认值,所以可以忽略不写,示例代码如下: 


#chapter5\5-4\5-4-4.ipynb

import pandas as pd,numpy as np	#导入Pandas库和NumPy库,并命名为pd和np

df=pd.read_excel('5-4-4.xlsx','成绩表')	#读取Excel中的成绩表数据

df.iloc[:4,1:].sum(axis=0)		#按列求和(Pandas函数)

np.sum(df.iloc[:4,1:],axis=0)		#按列求和(NumPy函数)

运行结果如下: 


语文480

数学438

英语422

dtype: int64

上面代码运算的结果同样也是Series数据,接下来将该Series数据添加为新行,示例代码如下: 


#chapter5\5-4\5-4-5.ipynb

import pandas as pd,numpy as np#导入Pandas库和NumPy库,命名为pd和np

df=pd.read_excel('5-4-5.xlsx','成绩表')	#读取Excel中的成绩表数据

df.loc[4]=df.iloc[:4,1:].sum(axis=0)	#按列求和(pandas函数)

df.loc[4]=np.sum(df.iloc[:4,1:],axis=0)	#按列求和(NumPy函数)

df					#输出df表数据

如图520(a)所示为原表格,运行结果如图520(b)所示。


图520对DataFrame按列求和


5.4.2逻辑统计
前面学习过逻辑运算符“&”和“|”,专门应用于多个逻辑布尔值之间的运算,但如果布尔值比较多,就会产生一些问题,先看一个简单的例子,判断Series中的所有元素是否大于60,分别使用4种方法,示例代码如下: 


#chapter5\5-4\5-4-6.ipynb

import pandas as pd,numpy as np	#导入Pandas库和NumPy库,命名为pd和np

s=pd.Series([79,80,91])		#Series数据

print((s[0]>60) & (s[1]>60) & (s[2]>60))	#所有元素是否大于60(方法1)

print(s[0]>60 and s[1]>60 and s[2]>60)	#所有元素是否大于60(方法2)

print((s>60).all())			#所有元素是否大于60(方法3)

print(np.all(s>60))			#所有元素是否大于60(方法4)

运行结果如下: 


True

True

True

True

分析一下上面的代码,Series中有3个数字,所以方法1和方法2分别做了3次判断,而方法3和方法4只做了1次判断。假设Series中有更多的数字,方法1和方法2就会做更多的判断,而方法3和方法4依然只判断1次,原因在于使用了all()函数。
Pandas和NumPy中均有对多个布尔值进行逻辑与、逻辑或运算的函数,对应的函数见表53。


表53Pandas与NumPy中的逻辑与、逻辑或运算



逻辑运算方式PandasNumPy注释

逻辑与allnp.all如果Series中的所有布尔值为True,则返回值为True,否则返回值为False

逻辑或anynp.any如果Series中有1个及以上布尔值为True,则返回值为True; 如果全部为False,则返回值为False

1. Series逻辑统计
首先,判断pd.Series([79,80,91])中的元素是否大于或等于80,如果条件成立,则返回值为True; 如果不成立,则返回值为False,示例代码如下: 


#chapter5\5-4\5-4-7.ipynb

import pandas as pd,numpy as np	#导入Pandas库和NumPy库,并命名为pd和np

s=pd.Series([79,80,91])	#Series数据

print(s>=80)			#打印Series中的布尔值元素

运行结果如下: 


0False

1True

2True

dtype: bool

再分别使用Pandas和NumPy中的all()函数、any()函数进行逻辑统计,示例代码如下: 


#chapter5\5-4\5-4-8.ipynb

import pandas as pd,numpy as np	#导入Pandas库和NumPy库,并命名为pd和np

s=pd.Series([79,80,91])		#Series数据

print((s>=80).any(),np.any(s>=80))	#Series元素是布尔值的或运算

print((s>=80).all(),np.all(s>=80))	#Series元素是布尔值的与运算

运行结果如下: 


True True

False False

2. DataFrame逻辑统计
如果布尔值在DataFrame表格中,则可使用all()函数和any()函数对整个表格进行逻辑统计也是一样的,但如果要对DataFrame表格中的布尔值按行或接列判断统计,则需要在函数中使用axis参数来确定方向。
1) 按行统计
如图521(a)所示为原表格,在每个人的3个科目中,如果至少有1个及以上科目的分数大于或等于130,则返回'√',否则返回'×',示例代码如下: 


#chapter5\5-4\5-4-9.ipynb

import pandas as pd,numpy as np	#导入Pandas库和NumPy库,并命名为pd和np

df=pd.read_excel('5-4-9.xlsx','成绩表')	#读取Excel中的成绩表数据

(df.iloc[:,1:]>=130).any(axis=1)	#按行方向做any的逻辑统计(方法1)

np.any(df.iloc[:,1:]>=130,axis=1)	#按行方向做np.any的逻辑统计(方法2)

运行结果如下: 


0True

1False

2True



3True

dtype: bool

分析一下关键代码,df.iloc[:,1:]>=130的运行结果是一个由布尔值组成的DataFrame表格,any()函数中的参数axis=1表示按行做逻辑统计。
将统计的布尔值结果再做判断,如果条件成立,则返回'√',否则返回'×',最后将判断的结果写入新列,示例代码如下: 

#chapter5\5-4\5-4-10.ipynb

import pandas as pd,numpy as np	#导入Pandas库和NumPy库,并命名为pd和np

df=pd.read_excel('5-4-10.xlsx','成绩表')	#读取Excel中的成绩表数据

arr=np.where((df.iloc[:,1:]>=130).any(axis=1),'√','×')#按行方向判断(方法1)

arr=np.where(np.any(df.iloc[:,1:]>=130,axis=1),'√','×')#按行方向判断(方法2)

df['是否达标']=arr			#将判断结果添加到新列

df					#输出df表数据

运行结果如图521(b)所示。


图521对DataFrame按行进行逻辑统计


2) 按列统计
如图522(a)所示为原表格,如果每个科目列的元素全部大于或等于120,则返回'√',否则返回'×',示例代码如下: 


#chapter5\5-4\5-4-11.ipynb

import pandas as pd,numpy as np #导入Pandas库和NumPy库,并命名为pd和np

df=pd.read_excel('5-4-11.xlsx','成绩表') #读取Excel中的成绩表数据

(df.iloc[:,1:]>=120).all(axis=0) #按列方向做all的逻辑统计(方法1)

np.all(df.iloc[:,1:]>=120,axis=0) #按列方向做np.all的逻辑统计(方法2)

运行结果如下: 


语文False

数学True

英语False

dtype: bool

分析一下关键代码,df.iloc[:,1:]>=120的运行结果是一个由布尔值组成的DataFrame表格,all()函数中的参数axis=0表示按列做逻辑统计。
将统计的布尔值结果再做判断,如果条件成立,则返回'√',否则返回'×',最后将判断的结果写入新行,示例代码如下: 

#chapter5\5-4\5-4-12.ipynb

import pandas as pd,numpy as np	#导入Pandas库和NumPy库,并命名为pd和np

df=pd.read_excel('5-4-12.xlsx','成绩表')	#读取Excel中的成绩表数据

arr=np.concatenate([np.array(['是否达标']),np.where((df.iloc[:,1:]>=120).all(axis=0),'√','×')]) #按列判断(方法1)

arr=np.concatenate([np.array(['是否达标']),np.where(np.all(df.iloc[:,1:]>=120,axis=0),'√','×')])#按列判断(方法2)

df.loc[len(df)]=arr			#将判断结果添加到新行

df					#输出df表数据

如图522(a)所示为原表格,运行结果如图522(b)所示。


图522对DataFrame按列进行逻辑统计


使用np.where()函数进行判断之后,返回的是数组结果['×' '√' '×'],如果直接写入新行,将会出错。因为每行是4个值,而数组中只有3个元素,所以应先使用np.concatenate()函数再添加'是否达标',最后arr变量返回的值是['是否达标' '×' '√' '×'],这样便可以成功将数据添加到新行。
注意: 上面讲解的添加到新行的方法,从数据类型的角度讲是不科学的,在没有添加新行之前,每个科目下都是纯数字; 添加新行后,每个科目下既有数字,又有字符串,不利于后续对每列数据进行统计。
5.4.3极值统计
在数据统计中,我们经常需要求最大值或者最小值,但都只能取得一个值,如果希望获取前几个最大或最小值,则需要使用极值函数,极值分为极大值与极小值。在Excel中求极大值的函数是LARGE()函数,求极小值的函数是SMALL()函数; 在Pandas中对应的函数是nlargest()函数和nsmallest()函数。极大值、极小值函数对应的参数见表54。


表54Series与DataFrame中的极值函数



极值函数及参数

Series极小值s. nsmallest(n, keep='first')

Series极大值s. nlargest(n, keep='first')

DataFrame极小值
df.nsmallest(n, columns,keep='first')

DataFrame极大值df.nlargest(n, columns,keep='first')


下面解释一下对应的几个参数的含义。 
n: 指定极值个数。
column: 如果求极值的是DataFrame表格,则要指定求极值的列,可以指定多列。
keep: 参数分别有first、last和all 3个值,含义分别如下。 
 first: 优先选取原始表格里排在前面的值,即默认值。
 last: 优先选取原始表格里排在后面的值。
 all: 选取所有的值,即使选取的个数超过了指定的极值个数。
1. Series极值统计
对Series数据做极值处理之后,返回结果也是Series数据,下面以pd.Series([57,91,87,46,87,99])为例,对求极大值和极小值分别做演示。
统计前3个最大的数字和前3个最小的数字,示例代码如下: 


#chapter5\5-4\5-4-13.ipynb

import pandas as pd			#导入Pandas库,并命名为pd

s=pd.Series([57,91,87,46,87,99])	#Series数据

print(s.nlargest(3,'first'))		#统计前3个最大的数字

print(s.nsmallest(3,'first'))		#统计前3个最小的数字

运行结果如下: 


#统计前3个最大的数字,结果如下所示

599

191

287

dtype: int64



#统计前3个最小的数字,结果如下所示

346

057

287

dtype: int64

接下来还是统计前3个最大的数字和前3个最小的数字,但是如果有重复值,则全部获取,示例代码如下: 


#chapter5\5-4\5-4-14.ipynb

import pandas as pd			#导入Pandas库,并命名为pd

s=pd.Series([57,91,87,46,87,99])	#Series数据

print(s.nlargest(3,'all'))		#统计前3个最大的数字

print(s.nsmallest(3,'all'))		#统计前3个最小的数字

运行结果如下: 


#统计前3个最大的数字,结果如下所示

599

191



287

487

dtype: int64



#统计前3个最小的数字,结果如下所示

346

057

287

487

dtype: int64

注意: 上面的代码演示中,没有对keep参数为'last'做演示,原因在于对Series数据进行极值统计时,虽然结果都相同,但是取值位置不同,读者可以自行测试keep参数为'last'时,索引序号与参数为'first'时的不同。
2. DataFrame极值统计
对DataFrame表格做极值处理,返回结果不是极值,而是极值对应的整条记录,最终结果也是DataFrame表格,可以对DataFrame表格的单列或多列进行极值统计,下面对极大值和极小值分别进行演示。
1) 统计单列极值
如图523(a)所示为原表格,在理论列的分数中,统计前3个最大数字对应的记录,示例代码如下: 


#chapter5\5-4\5-4-15.ipynb

import pandas as pd			#导入Pandas库,并命名为pd

df=pd.read_excel('5-4-15.xlsx','成绩表')	#读取Excel中的成绩表数据

df.nlargest(3,'理论')			#统计理论列中前3个最大数字对应的记录

运行结果如图523(b)所示。


图523统计理论分数最大的3条记录


如图524(a)所示为原表格,在理论列的分数中,统计前3个最小数字对应的记录,示例代码如下: 


#chapter5\5-4\5-4-16.ipynb

import pandas as pd			#导入Pandas库,并命名为pd

df=pd.read_excel('5-4-16.xlsx','成绩表')	#读取Excel中的成绩表数据

df.nsmallest(3,'理论')			#统计理论列中前3个最小数字对应的记录

运行结果如图524(b)所示。


图524统计理论分数最小的3条记录


2) 统计多列极值
对多列统计极值,在columns参数中以列表形式写入列名称,并且以列名称在列表中的先后顺序确定关键字顺序,例如columns=['理论','实操'],表示以理论为第1关键字,以实操为第2关键字,也就是在理论列的数字相同的情况下,再对实操列的数字进行极值排列。
如图525(a)所示为原表格,对理论和实操两列的分数进行极值统计,统计前3个最大数字对应的记录,示例代码如下: 


#chapter5\5-4\5-4-17.ipynb

import pandas as pd			#导入Pandas库,并命名为pd

df=pd.read_excel('5-4-17.xlsx','成绩表')	#读取Excel中的成绩表数据

df.nlargest(3,['理论','实操'])		#统计理论、实操两列前3个最大值对应的记录

运行结果如图525(b)所示。


图525统计理论、实操分数最大的3条记录


如图526(a)所示为原表格,对理论和实操两列的分数进行极值统计,统计前3个最小数字对应的记录,示例代码如下: 


#chapter5\5-4\5-4-18.ipynb

import pandas as pd			#导入Pandas库,并命名为pd

df=pd.read_excel('5-4-18.xlsx','成绩表')	#读取Excel中的成绩表数据

df.nsmallest(3,['理论','实操'])		#统计理论、实操两列前3个最小值对应的记录

运行结果如图526(b)所示。


图526统计理论、实操分数最小的3条记录


5.4.4排名统计
排名是同类事物客观实力的反映,相互之间带有比较性质,所以排名是很常见的统计方式。Excel中的排名函数是RANK(),Pandas中的排名函数是rank(),并且有更多的排名方式,先举一个简单的例子,示例代码如下: 


#chapter5\5-4\5-4-19.ipynb

import pandas as pd		#导入Pandas库,并命名为pd

s=pd.Series([84,99,93,65])	#Series数据

s.rank()			#排名统计

运行结果如下: 


02.0

14.0

23.0

31.0

dtype: float64

通过运行结果会发现,数字越大,排名越低,这是因为rank()函数的ascending参数默认值为True,也就是默认为升序排列。大多数排名统计是数字越大,排名越高,如果希望这样,则应将ascending设置为False,示例代码如下: 


#chapter5\5-4\5-4-20.ipynb

import pandas as pd		#导入Pandas库,并命名为pd

s=pd.Series([84,99,93,65])	#Series数据

s.rank(ascending=False)	#排名统计

运行结果如下: 


03.0

11.0

22.0

34.0

dtype: float64

关于排名,还有一个重要的问题需要了解,就是相同值。rank()函数的method参数提供了对相同值的5种处理方法,见表55。


表55rank()函数的method参数说明



常量注释

average默认值,对相同值做平均排名
min对相同值做最小排名
max对相同值做最大排名
first对相同值出现的顺序做排名
dense与最小排名类似,但不同名次之间差值为1

下面对每种常量做一个案例演示,以增强理解。假定pd.Series([99,93,84,84,65])是一组考试分数,现在需要对其中的每个分数做排名。
当method参数为average时,示例代码如下: 


#chapter5\5-4\5-4-21.ipynb

import pandas as pd				#导入Pandas库,并命名为pd

s=pd.Series([99,93,84,84,65])			#Series数据

s.rank(method='average',ascending=False)	#method参数为average的排名统计

运行结果如图527所示。


图527method参数为average


当method参数为min时,这种排名方式通常叫作美式排名,是应用得比较多的一种方式,示例代码如下: 


#chapter5\5-4\5-4-22.ipynb

import pandas as pd	#导入Pandas库,并命名为pd

s=pd.Series([99,93,84,84,65])#Series数据

s.rank(method='min',ascending=False)	#method参数为min的排名统计

运行结果如图528所示。 


图528method参数为min


当method参数为max时,示例代码如下: 


#chapter5\5-4\5-4-23.ipynb

import pandas as pd			#导入Pandas库,并命名为pd

s=pd.Series([99,93,84,84,65])		#Series数据

s.rank(method='max',ascending=False)	#method参数为max的排名统计

运行结果如图529所示。 


图529method参数为max


当method参数为first时,示例代码如下: 


#chapter5\5-4\5-4-24.ipynb

import pandas as pd			#导入Pandas库,并命名为pd

s=pd.Series([99,93,84,84,65])		#Series数据

s.rank(method='first',ascending=False)	#method参数为first的排名统计

运行结果如图530所示。 


图530method参数为first


当method参数为dense时,这种排名方式通常叫作中式排名,也是应用得比较多的一种方式,示例代码如下: 


#chapter5\5-4\5-4-25.ipynb

import pandas as pd			#导入Pandas库,并命名为pd

s=pd.Series([99,93,84,84,65])		#Series数据

s.rank(method='dense',ascending=False)	#method参数为dense的排名统计

运行结果如图531所示。


图531method参数为dense


1. Series排名统计
直接获取表格的某列做排名统计,就相当于对指定的Series数据做排名,如图532(a)所示为原表格,对分数列分别做美式和中式排名,示例代码如下: 

#chapter5\5-4\5-4-26.ipynb

import pandas as pd			#导入Pandas库,并命名为pd

df=pd.read_excel('5-4-26.xlsx','分数表')	#读取Excel中的分数表数据

df['美式排名']=df['分数'].rank(method='min',ascending=False)	#美式排名

df['中式排名']=df['分数'].rank(method='dense',ascending=False)	#中式排名

df					#输出df表数据

如图532(a)所示为原表格,运行结果如图532(b)所示。


图532单列的美式和中式排名


注意: rank()函数统计出的名次是小数类型,如果需要转换为整数类型,则在rank()函数之后加astype('int')即可。
2. DataFrame排名统计
如果需要对DataFrame表格做排名,并且排名方式是各行、各列单独排名,用rank()函数也可以实现,不过需要指定函数的axis参数,1为按行排名,0为按列排名。
1) 按行排名
如图533(a)所示为原表格,需要对1~6月的销量数据按行排名,也就是对每个人1~6月的销量做排名,示例代码如下: 


#chapter5\5-4\5-4-27.ipynb

import pandas as pd			#导入Pandas库,并命名为pd

df=pd.read_excel('5-4-27.xlsx','销量表')	#读取Excel中的销量表数据

df.iloc[:,1:]=df.iloc[:,1:].rank(method='min',ascending=False,axis=1).astype('int')				
#按行做美式排名

df					#输出df表数据

运行结果如图533(b)所示。


图533按行进行美式排名


2) 按列排名
如图534(a)所示为原表格,如果需要按列排名,则可统计所有人在每个月的排名情况,示例代码如下: 


#chapter5\5-4\5-4-28.ipynb

import pandas as pd			#导入Pandas库,并命名为pd

df=pd.read_excel('5-4-28.xlsx','销量表')	#读取Excel中的销量表数据

df.iloc[:,1:]=df.iloc[:,1:].rank(method='min',ascending=False,axis=0).astype('int')				
#按列进行美式排名

df					#输出df表数据

运行结果如图534(b)所示。


图534按列进行美式排名


5.5巩固案例
本章通过对运算、分支、遍历和统计知识点的学习,对Pandas的数据处理方式有了更深刻的理解,接下来本节通过实例应用对所学知识加以巩固,以达到学以致用的目的。
5.5.1根据不同蔬菜的采购数量统计每天采购金额
如图535(a)所示为原表格,分别有净白菜、小油菜和丝瓜这3种蔬菜的采购数量,现在给出这3种蔬菜每千克的价格分别为1.3、2.4和5.6元,各蔬菜的单价数据存储在s=pd.Series([5.6,1.3,2.4],index=['丝瓜','净白菜','小油菜'])中,并且存储顺序并没有按原表格中的蔬菜顺序排列,现在需要统计出每天的采购金额,示例代码如下: 


#chapter5\5-5\5-5-1.ipynb

import pandas as pd			#导入Pandas库,并命名为pd

df=pd.read_excel('5-5-1.xlsx','采购表')	#读取Excel中的采购表数据

s=pd.Series([5.6,1.3,2.4],index=['丝瓜','净白菜','小油菜']) 

#用Series数据结构存储蔬菜价格

df['金额']=(df.iloc[:,1:]*s).sum(axis=1)	#统计每天采购金额

df					#输出df表数据

运行结果如图 535(b)所示。


图535计算每天金额的前后效果对比


分析一下关键代码,df['金额']=(df.iloc[:,1:]*s).sum(axis=1),其中df.iloc[:,1:]选择的是所有蔬菜的采购数量,返回的是DataFrame数据,然后与s相乘,s是Series数据,本质就是DataFrame与Series的运算。可以参考5.1.6节DataFrame与Series运算的讲解,返回结果也是DataFrame数据,运算结果如下:


丝瓜净白菜小油菜

0	28.0	13.0	4.8

122.4	10.4	2.4

2	33.6	3.9	0.0

3	84.0	9.1	7.2

之后sum(axis=1)表示按行求和,运算结果如下:


045.8

135.2

237.5

3100.3

dtype: float64

最后将计算结果写入原表格的新列中即可。
如果蔬菜的价格顺序与原表格蔬菜的顺序相同,也可以将价格存储在列表中,与蔬菜采购数量直接相乘即可,示例代码如下: 


#chapter5\5-5\5-5-2.ipynb

import pandas as pd			#导入Pandas库,并命名为pd

df=pd.read_excel('5-5-2.xlsx','采购表')	#读取Excel中的采购表数据

df['金额']=(df.iloc[:,1:]*[1.3,2.4,5.6]).sum(axis=1) 
#统计每天采购金额

df	#输出df表数据

运行结果与图535(b)添加金额列之后的表格相同。
5.5.2筛选出成绩表中各科目均大于或等于100的记录
如图536(a)所示为原表格,要求筛选出每个人3个科目的分数均大于或等于100的记录,示例代码如下: 


#chapter5\5-5\5-5-3.ipynb

import pandas as pd,numpy as np	#导入Pandas库和NumPy库,并命名为pd和np

df=pd.read_excel('5-5-3.xlsx','成绩表')	#读取Excel中的成绩表数据

df[np.all(df.iloc[:,1:]>=100,axis=1)]	#输出筛选结果

运行结果如图536(b)所示。


图536成绩筛选的前后效果对比(1)


分析一下关键代码,np.all(df.iloc[:,1:]>=100,axis=1),其中df.iloc[:,1:]>=100表示判断所有科目的分数是否大于或等于100,返回的结果是由布尔值构成的DataFrame数据,结果如下:


语文数学英语

0	True	True	False

1	True	True	True

2	True	False	True

3	True	False	True

4	True	True	True

然后使用an.all()函数对判断的结果按行做逻辑与运算,最后返回结果是由布尔值组成的Series数据,结果如下:


0False

1True

2False

3False

4True

dtype: bool

观察发现,只有索引为1和4的返回结果为True,所以最后筛选出行索引为1和4对应的两条记录。
5.5.3筛选出成绩表中各科目的和大于或等于300的记录
如图537(a)所示为原表格,对每个人的各科分数求和,然后筛选出总分大于或等于300的记录,示例代码如下: 


#chapter5\5-5\5-5-4.ipynb

import pandas as pd			#导入Pandas库,并命名为pd

df=pd.read_excel('5-5-4.xlsx','成绩表')	#读取Excel中的成绩表数据

df[df.iloc[:,1:].sum(axis=1)>=330]	#输出筛选结果

运行结果如图537(b)所示。


图537成绩筛选的前后效果对比(2)


分析一下关键代码,df.iloc[:,1:].sum(axis=1)>=330,其中df.iloc[:,1:]表示所有科目的分数,sum(axis=1)表示按行求和,求和结果是Series数据,结果如下:


0346

1312

2336

3320

4351

dtype: int64

Series数据存放的是每个人的总分,再判断是否大于或等于330,结果如下:


0True

1False

2True

3False

4True

dtype: bool

观察发现,索引为0、2、4的返回结果为True,所以最后筛选出行索引为0、2、4对应的3条记录。
5.5.4统计每个人各科目总分之和的排名
如图538(a)所示为原表格,对每个人的各科分数求和,然后按总分的大小做中式排名,示例代码如下: 


#chapter5\5-5\5-5-5.ipynb

import pandas as pd			#导入Pandas库,并命名为pd

df=pd.read_excel('5-5-5.xlsx','成绩表')	#读取Excel中的成绩表数据

df['排名']=df.iloc[:,1:].sum(axis=1).rank(method='dense',ascending=False).astype('int')		
		#中式排名统计

df					#输出df表数据

运行结果如图538(b)所示。


图538排名设置的前后效果对比


分析一下关键代码,df.iloc[:,1:].sum(axis=1).rank(method='dense',ascending=False).astype('int'),其中df.iloc[:,1:].sum(axis=1)表示对所有科目的分数按行求和,求和结果是Series数据,存放的是每个人的总分,结果如下:


0346

1312

2336

3320

4351

dtype: int64

rank(method='dense',ascending=False)表示对求和结果做排名统计,astype('int')表示将名次转换为整数类型。
5.5.5统计每个人所有考试科目的最优科目
如图539(a)所示为原表格,统计每个人最高分对应的科目,示例代码如下: 


#chapter5\5-5\5-5-6.ipynb

import pandas as pd					#导入Pandas库,命名为pd

df=pd.read_excel('5-5-6.xlsx','成绩表')			#读取Excel中的成绩表数据

df['最优科目']=df.iloc[:,1:].apply(lambda s:s.nlargest(1,keep='all').index.to_list(),axis=1)	#统计每个人最高分对应的科目

df							#输出df表数据

运行结果如图539(b)所示。


图539最优科目统计的前后效果对比


分析一下关键代码,df['最优科目']=df.iloc[:,1:].apply(lambda s:s.nlargest(1,keep='all').index.to_list(),axis=1),使用apply()函数对所有科目的分数区域按行遍历,s变量就是从表格每行遍历出来的Series数据,然后统计该Series数据第1个极大值,要知道统计出来的极大值不是一个单纯的值,而是存储在Series数据结构中的,然后在nlargest()函数后写入index属性,表示获取每个极大值Series的索引,结果如下:


0Index(['数学'], dtype='object')

1Index(['英语'], dtype='object')

2Index(['英语'], dtype='object')

3Index(['英语'], dtype='object')

4Index(['数学'], dtype='object')

dtype: object

最后,to_list()函数表示将极大值的索引标签转换为列表,也就是最高分对应的科目名称,结果如下:


0[数学]

1[英语]

2[英语]

3[英语]

4[数学]

dtype: object

如果有多个科目获得了最高分,同样会统计出多个最高分对应的科目名称。