第四章 Pandas统计分析
4.1 数据计算
Pandas提供了大量的数据计算函数,可以实现求和、求均值、求最大值、求最小值、求中位数、求众数、求方差、标准差等,从而使得数据统计变得简单高效。
4.1.1求和函数(sum())
DataFrame.sum(axis=None, skipna=None, level=None, numeric_only=None, min_count=0, **kwargs)
参数说明:
axis:axis=1表示行,axis=0表示列,默认值为None(无)。 skipna:布尔型,表示计算结果是否排除NaN/Null值,默认值为None。
level:表示索引层级,默认值为None。
numeric_only:仅数字,布尔型,默认值为None。
min_count:表示执行操作所需的数目,整型,默认值为0。 **kwargs:要传递给函数的附加关键字参数。
返回值:返回Series对象或DataFrame对象。行或列求和数据。
【示例01】 计算语文、数学和英语三科的总成绩。(示例位置:资源包\MR\Code\04\01)
首先,创建一组DataFrame类型的数据,包括语文、数学和英语三科的成绩,如图4.1所示。
程序代码如下:
下面使用sum()函数计算三科的总成绩,代码如下:
df[‘总成绩’]=df.sum(axis=1)
运行程序,输出结果如图4.2所示。
图4.1 DataFrame数据
图4.2 sum()函数计算三科的总成绩
4.1.2 求均值(mean()函数)
DataFrame.mean(axis=None, skipna=None, level=None, numeric_only=None, kwargs)
参数说明:
axis:axis=1表示行,axis=0表示列,默认值为None(无)。
skipna:布尔型,表示计算结果是否排除NaN/Null值,默认值为None。
level:表示索引层级,默认值为None。
numeric_only:仅数字,布尔型,默认值为None。
kwargs:要传递给函数的附加关键字参数。
返回值:返回Series对象或DataFrame对象。行或列平均值数据。
【示例02】 计算语文、数学和英语各科的平均分。(示例位置:资源包\MR\Code\04\02)
计算语文、数学和英语各科成绩的平均值,程序代码如下:
运行程序,输出结果如图4.3所示。
图4.3 mean()函数计算三科成绩的平均值
从运行结果得知:语文平均分109,数学平均分107,英语平均分114.667。
4.1.3 求最大值(max()函数)
在Python中通过调用DataFrame对象的max()函数实现行/列数据最大值运算,语法如下:
DataFrame.max(axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
参数说明: **
**axis:axis=1表示行,axis=0表示列,默认值为None(无)。 skipna:布尔型,表示计算结果是否排除NaN/Null值,默认值为None。
**level:表示索引层级,默认值为None。
**numeric_only:仅数字,布尔型,默认值为None。
kwargs:要传递给函数的附加关键字参数。
返回值:返回Series对象或DataFrame对象。行或列最大值数据。
【示例03】 计算语文、数学和英语各科的最高分。(示例位置:资源包\MR\Code\04\03)
计算语文、数学和英语各科成绩的最大值,程序代码如下:
运行程序,输出结果如图4.4所示。
图4.4 max()函数计算三科成绩的最大值
4.1.4 求最小值(min()函数)
在Python中通过调用DataFrame对象的min()函数实现行/列数据最小值运算,语法如下:
DataFrame.min(axis=None, skipna=None, level=None, numeric_only=None, **kwargs)
参数说明: **
**axis:axis=1表示行,axis=0表示列,默认值为None(无)。 skipna:布尔型,表示计算结果是否排除NaN/Null值,默认值为None。
**level:表示索引层级,默认值为None。
**numeric_only:仅数字,布尔型,默认值为None。
kwargs:要传递给函数的附加关键字参数。
返回值:返回Series对象或DataFrame对象。行或列最小值数据。
【示例04】 计算语文、数学和英语各科的最低分。(示例位置:资源包\MR\Code\04\04)
计算语文、数学和英语各科成绩的最小值,程序代码如下:
运行程序,输出结果如图4.5所示。
4.1.5 求中位数(median()函数)
中位数又称中值,是统计学专有名词,是指按顺序排列的一组数据中位于中间位置的数,其不受异常值的影响。例如,年龄23、45、35、25、22、34、28这7个数,中位数就是排序后位于中间的数字,即28;而年龄23、45、35、25、22、34、28、27这8个数,中位数则是排序后位于中间两个数的平均值,即27.5。在Python中直接调用DataFrame对象的median()函数就可以轻松实现中位数的运算,语法如下:
DataFrame.median(axis=None,skipna=None,level=None,numeric_only=None,**kwargs)
参数说明: **
**axis:axis=1表示行,axis=0表示列,默认值为None(无)。 skipna:布尔型,表示计算结果是否排除NaN/Null值,默认值为None。
**level:表示索引层级,默认值为None。
**numeric_only:仅数字,布尔型,默认值为None。
kwargs:要传递给函数的附加关键字参数。
返回值:返回Series对象或DataFrame对象。
【示例05】 计算学生各科成绩的中位数1。(示例位置:资源包\MR\Code\04\05)
下面给出一组数据(3条记录),然后使用median()函数计算语文、数学和英语各科成绩的中位数,程序代码如下:
运行程序,控制台输出结果如下:
语文 130.0数学 120.0英语 130.0
【示例06】 计算学生各科成绩的中位数2。(示例位置:资源包\MR\Code\04\06)
下面再给出一组数据(4条记录),同样使用median()函数计算语文、数学和英语各科成绩的中位数,程序代码如下:
运行程序,控制台输出结果如下:
语文 121.5数学 121.5英语 120.0
4.1.6 求众数(mode()函数)
什么是众数?众数的众字有多的意思,顾名思义,众数就是一组数据中出现最多的数称为众数,它代表了数据的一般水平。
在Python中通过调用DataFrame对象的mode()函数可以实现众数运算,语法如下:
DataFrame.mode(axis=0,numeric_only=False,dropna=True)
参数说明:
axis:axis=1表示行,axis=0表示列,默认值为0。
numeric_only:仅数字,布尔型,默认值为False。如果值为True,则仅适用于数字列。
dropna:是否删除缺失值,布尔型,默认值为True。
返回值:返回Series对象或DataFrame对象。
首先看一组原始数据,如图4.6所示。
【示例07】 计算学生各科成绩的众数。(示例位置:资源包\MR\Code\04\07)
计算语文、数学和英语三科成绩的众数、每一行的众数和“数学”成绩的众数,程序代码如下:
三科成绩的众数:
每一行的众数:
0 110
1 130
2 130
数学成绩的众数:
0 120
4.1.7 求方差(var()函数)
方差用于衡量一组数据的离散程度,即各组数据与它们的平均数的差的平方,那么我们用这个结果来衡量这组数据的波动大小,并把它叫作这组数据的方差,方差越小越稳定。通过方差可以了解一个问题的波动性。下面简单介绍下方差的意义,相信通过一个简单的举例您就会了解。
例如,某校两名同学的物理成绩都很优秀,而参加物理竞赛的名额只有一个,那么选谁去获得名次的机率更大呢?于是根据历史数据计算出了两名同学的平均成绩,但结果是实力相当,平均成绩都是107.6,怎么办呢?这时让方差帮决定,看看谁的成绩更稳定。首先汇总物理成绩,如图4.7所示。
图4.7 物理成绩
通过方差对比两名同学物理成绩的波动,如图4.8所示。
图4.8 方差
接着来看一下总体波动(方差和),“小黑”的数据是73.2,“小白”的数据是949.2,很明显“小黑”的物理成绩波动较小,发挥更稳定,所以应该选“小黑”参加物理竞赛。
以上举例就是方差的意义。大数据时代,它能够帮助我们解决很多身边的问题、协助我们做出合理的决策。
在Python中通过调用DataFrame对象的var()函数可以实现方差运算,语法如下:
DataFrame.var(axis=None,skipna=None,level=None,ddof=1,numeric_only=None,kwargs)
参数说明:
axis:axis=1表示行,axis=0表示列,默认值为None(无)。 skipna:布尔型,表示计算结果是否排除NaN/Null值,默认值为None。
level:表示索引层级,默认值为None。
ddof:整型,默认值为1。自由度,计算中使用的除数是N-ddof,其中N表示元素的数量。
numeric_only:仅数字,布尔型,默认值为None。
kwargs:要传递给函数的附加关键字参数。
返回值:返回Series对象或DataFrame对象。
【示例08】 通过方差判断谁的物理成绩更稳定。(示例位置:资源包\MR\Code\04\08)
计算“小黑”和“小白”物理成绩的方差,程序代码如下:
运行程序,控制台输出结果如下:
小黑 18.3小白 237.3
从运行结果得知:“小黑”的物理成绩波动较小,发挥更稳定。这里需要注意的是,Pandas中计算的方差为无偏样本方差(即方差和/样本数-1),NumPy中计算的方差就是样本方差本身(即方差和/样本数)。
4.1.8 标准差(数据标准化std()函数)
标准差又称均方差,是方差的平方根,用来表示数据的离散程度。
在Python中通过调用DataFrame对象的std()函数求标准差,语法如下:
DataFrame.std(axis=None,skipna=None,level=None,ddof=1,numeric_only=None,**kwargs)
std()函数的参数与var()函数一样,这里不再赘述。
【示例09】 计算各科成绩的标准差。(示例位置:资源包\MR\Code\04\09)
使用std()函数计算标准差,程序代码如下:
运行程序,控制台输出结果如下:
语文 11.547005数学 5.773503英语 11.5547005
4.1.9 求分位数(quantile()函数)
分位数也称分位点,它以概率依据将数据分割为几个等份,常用的有中位数(即二分位数)、四分位数、百分位数等。分位数是数据分析中常用的一个统计量,经过抽样得到一个样本值。例如,经常会听老师说:“这次考试竟然有20%的同学不及格!”,那么这句话就体现了分位数的应用。在Python中通过调用DataFrame对象的quantile()函数求分位数,语法如下:
DataFrame.quantile(q=0.5,axis=0,numeric_only=True, interpolation=’linear’)
参数说明:
q:浮点型或数组,默认为0.5(50%分位数),其值为0~1。
axis:axis=1表示行,axis=0表示列。默认值为0。
numeric_only:仅数字,布尔型,默认值为True。
interpolation:内插值,可选参数[{‘linear’,‘lower’,‘higher’,‘midpoint’,‘nearest’}]用于指定要使用的插值方法,当期望的分位数为数据点i~j时。
linear’:i+(j-i)×分数,其中分数是指数被i和j包围的小数部分。
lower:i。
higher:j。
nearest:i或j二者以最近者为准。
midpoint:(i+j)/2。
返回值:返回Series或DataFrame对象。
【示例10】 通过分位数确定被淘汰的35%的学生。(示例位置:资源包\MR\Code\04\10)
以学生成绩为例,数学成绩分别为120、89、98、78、65、102、112、56、79、45的10名同学,现根据分数淘汰35%的学生,该如何处理?首先使用quantile()函数计算35%的分位数,然后将学生成绩与分位数比较,筛选小于等于分位数的学生,程序代码如下:
运行程序,控制台输出结果如下:
从运行结果得知:即将被淘汰的学生有4名,分数分别为78、65、56和45。
【示例11】 计算日期、时间和时间增量数据的分位数。(示例位置:资源包\MR\Code\04\11)
如果参数numeric_only=False,将计算日期、时间和时间增量数据的分位数,程序代码如下:
运行程序,控制台输出结果如下:
A 1.5
B 2019-07-02 12:00:00
C 1 days 12:00:00
Name: 0.5, dtype: object
4.2 数据格式化
在进行数据处理时,尤其是在数据计算中应用求均值(mean()函数)后,发现结果中的小数位数增加了许多。此时就需要对数据进行格式化,以增加数据的可读性。例如,保留小数点位数、百分号、千位分隔符等。首先来看一组数据,如图4.9所示。
图4.9 原始数据
4.2.1 设置小数位数
设置小数位数主要使用DataFrame对象的round()函数,该函数可以实现四舍五入,而它的decimals参数则用于设置保留小数的位数,设置后数据类型不会发生变化,依然是浮点型。语法如下:
DataFrame.round(decimals=0, *args, **kwargs) **
decimals:每一列四舍五入的小数位数,整型、字典或Series对象。如果是整数,则将每一列四舍五入到相同的位置。否则,将字典和Series舍入到可变数目的位置;如果小数是类似于字典的,那么列名应该在键中;如果小数是级数,列名应该在索引中。没有包含在小数中的任何列都将保持原样。非输入列的小数元素将被忽略。
args:附加的关键字参数。
kwargs:附加的关键字参数。
返回值:返回DataFrame对象。
【示例12】 四舍五入保留指定的小数位数。(示例位置:资源包\MR\Code\04\12)
使用round()函数四舍五入保留小数位数,程序代码如下:
运行程序,控制台输出结果如下:
当然,保留小数位数也可以用自定义函数,例如,为DataFrame对象中的各个浮点值保留两位小数,主要代码如下:
df.applymap(lambda x: ‘%.2f’%x)
注意
经过自定义函数处理过的数据将不再是浮点型而是对象型,如果后续计算需要数据,则应先进行数据类型转换。
4.2.2 设置百分比
在数据分析过程中,有时需要百分比数据。那么,利用自定义函数将数据进行格式化处理,处理后的数据就可以从浮点型转换成带指定小数位数的百分比数据,主要使用apply()函数与format()函数。
【示例13】 将指定数据格式化为百分比数据。(示例位置:资源包\MR\Code\04\13)
将A1列的数据格式化为百分比数据,程序代码如下:
运行程序,控制台输出结果如下:
4.2.3 设置千位分隔符
由于业务需要,有时需要将数据格式化为带千位分隔符的数据。那么,处理后的数据将不再是浮点型而是对象型。
【示例14】 将金额格式化为带千位分隔符的数据。(示例位置:资源包\MR\Code\04\14)
将图书销售码洋格式化为带千位分隔符的数据,程序代码如下:
运行程序,控制台输出结果如下:
注意
设置千位分隔符后,对于程序来说,这些数据将不再是数值型,而是数字和逗号组成的字符串,如果由于程序需要再变成数值型就会很麻烦,因此设置千位分隔符要慎重。
4.3 数据分组统计
本节主要介绍分组统计函数groupby()的各种应用。
4.3.1 分组统计groupby()函数
对数据进行分组统计,主要使用DataFrame对象的groupby()函数,其功能如下。
(1)根据给定的条件将数据拆分成组。
(2)每个组都可以独立应用函数(如求和函数sum()、求平均值函数mean()等)。
(3)将结果合并到一个数据结构中。
groupby()函数用于将数据按照一列或多列进行分组,一般与计算函数结合使用,实现数据的分组统计,语法如下:
DataFrame.groupby(by=None,axis=0,level=None,as_index=True,sort=True,group_keys=True,squeeze=False,observed=False)
参数说明:
by:映射、字典或Series对象、数组、标签或标签列表。如果by是一个函数,则对象索引的每个值都调用它;如果传递了一个字典或Series对象,则使用该字典或Series对象值来确定组;如果传递了数组ndarray,则按原样使用这些值来确定组。
axis:axis=1表示行,axis=0表示列。默认值为0。
level:表示索引层级,默认值为None(无)。
as_index:布尔型,默认值为True,返回以组标签为索引的对象。 sort:对组进行排序,布尔型,默认值为True。
group_keys:布尔型,默认值为True,调用apply()函数时,将分组的键添加到索引以标识片段。
squeeze:布尔型,默认值为False。如果可能,减少返回类型的维度;否则返回一致类型。
observed: bool, default False 布尔型, 观察者, 默认为False。当以石斑鱼为分类时,才会使用该参数。如果参数值为True,则仅显示分类石斑鱼的观测值;如果参数值为False,则显示分类石斑鱼的所有值。
返回值:返回DataFrameGroupBy,返回包含有关组的信息的groupby对象。
1.按照一列分组统计
【示例15】 根据“一级分类”统计订单数据。(示例位置:资源包\MR\Code\04\15)
按照图书“一级分类”对订单数据进行分组统计求和,程序代码如下:
运行程序,输出结果如图4.10所示。
图4.10 按照一列分组统计
2.按照多列分组统计
多列分组统计,以列表形式指定列。
【示例16】 根据两级分类统计订单数据。(示例位置:资源包\MR\Code\04\16)
按照图书“一级分类”和“二级分类”对订单数据进行分组统计求和,主要代码如下:
01 #抽取数据
02 df1=df[[‘一级分类’,’二级分类’,’7天点击量’,’订单预定’]]
03 df1=df1.groupby([‘一级分类’,’二级分类’]).sum() #分组统计求和
运行程序,输出结果如图4.11所示。
图4.11 按照多列分组统计
3.分组并按指定列进行数据计算
前面介绍的分组统计是按照所有列进行汇总计算的,那么如何按照指定列汇总计算呢?
【示例17】 统计各编程语言的7天点击量。(示例位置:资源包\MR\Code\04\17)
统计各编程语言的7天点击量,首先按“二级分类”分组,然后抽取“7天点击量”列并对该列进行求和运算,主要代码如下:
df1=df1.groupby(‘二级分类’)[‘7天点击量’].sum()
运行程序,输出结果如图4.12所示。
图4.12 分组并按指定列进行数据计算
4.3.2 对分组数据进行迭代
通过for循环对分组统计数据进行迭代(遍历分组数据)。
【示例18】 迭代一级分类的订单数据。(示例位置:资源包\MR\Code\04\18)
按照“一级分类”分组,并输出每一分类中的订单数据,主要代码如下:
运行程序,控制台输出结果如图4.13所示。
上述代码中,name是groupby()函数中“一级分类”的值,group是分组后的数据。如果groupby()函数对多列进行分组,那么需要在for循环中指定多列。
【示例19】 迭代两级分类的订单数据。(示例位置:资源包\MR\Code\04\19)
迭代“一级分类”和“二级分类”的订单数据,主要代码如下:
图4.13 对分组数据进行迭代
4.3.3 对分组的某列或多列使用聚合函数(agg()函数)
Python也可以实现像SQL中的分组聚合运算操作,主要通过groupby()函数与agg()函数实现。【示例20】 对分组统计结果使用聚合函数。(示例位置:资源包\MR\Code\04\20)
按“一级分类”分组统计“7天点击量”“订单预定”的平均值和总和,主要代码如下:
print(df1.groupby(‘一级分类’).agg([‘mean’,’sum’]))
运行程序,控制台输出结果如图4.14所示。
图4.14 分组统计“7天点击量”“订单预定”的平均值和总和
【示例21】 针对不同的列使用不同的聚合函数。(示例位置:资源包\MR\Code\04\21)
在上述示例中,还可以针对不同的列使用不同的聚合函数。例如,按“一级分类”分组统计“7天点击量”的平均值和总和、“订单预定”的总和,主要代码如下:
print(df1.groupby(‘一级分类’).agg({‘7天点击量’:[‘mean’,’sum’], ‘订单预定’:[‘sum’]}))
运行程序,控制台输出结果如图4.15所示。
图4.15 分组统计“7天点击量”的平均值和总和、“订单预定”的总和
【示例22】 通过自定义函数实现分组统计。(示例位置:资源包\MR\Code\04\22)
通过自定义函数也可以实现数据分组统计。例如,统计1月份销售数据中,购买次数最多的产品,主要代码如下:
运行程序,控制台输出结果如图4.16所示。
图4.16 统计购买次数最多的产品
从运行结果得知:“零基础学Python”是用户购买次数最多的产品。实用技巧
在图4.16显示的输出结果中可以看到,lambda()函数名称
max.name = “购买次数最多”
运行程序,控制台输出结果如图4.17所示。
图4.17 使用__name__方法修改函数名称
4.3.4 通过字典和Series对象进行分组统计
1.通过字典进行分组统计
首先创建字典建立对应关系,然后将字典传递给groupby()函数从而实现数据分组统计。
【示例23】 通过字典分组统计“北上广”销量。(示例位置:资源包\MR\Code\04\23)
统计各地区销量,业务要求将“北京”“上海”“广州”3个一线城市放在一起统计。那么首先创建一个字典将“北京出库销量”“上海出库销量”“广州出库销量”都对应“北上广”;然后使用groupby()函数进行分组统计。主要代码如下:
运行程序,控制台输出结果如图4.18所示。
图4.18 通过字典进行分组统计
2.通过Series对象进行分组统计
通过Series对象进行分组统计与字典的方法类似。
【示例24】 通过Series对象分组统计“北上广”销量。(示例位置:资源包\MR\Code\04\24)
首先,创建一个Series对象,主要代码如下:
运行程序,输出结果如图4.19所示。
然后,将Series对象传递给groupby()函数实现数据分组统计,主要代码如下:
01 df1=df.groupby(s1,axis=1).sum()
02 print(df1)
运行程序,控制台输出结果如图4.20所示。
图4.19 通过Series对象进行分组统计
图4.20 分组统计结果
4.4 数据移位
什么是数据移位?例如,分析数据时需要上一条数据怎么办?当然是移动至上一条,从而得到该条数据,这就是数据移位。在Pandas中,使用shift()方法可以获得上一条数据,该方法返回向下移位后的结果,从而得到上一条数据。例如,获取某学生上一次英语成绩,如图4.21所示。
图4.21 获取学生上一次英语成绩
shift()方法是一个非常有用的方法,用于数据位移与其他方法结合,能实现很多难以想象的功能,语法格式如下:
DataFrame.shift(periods=1, freq=None, axis=0)
参数说明:
periods:表示移动的幅度,可以是正数,也可以是负数,默认值是1,1表示移动一次。注意这里移动的都是数据,而索引是不移动的,移动之后是没有对应值的,赋值为NaN。
freq:可选参数,默认值为None,只适用于时间序列,如果这个参数存在,那么会按照参数值移动时间索引,而数据值没有发生变化。
axis:axis=1表示行,axis=0表示列。默认值为0。
【示例25】 统计学生英语周测成绩的升降情况。(示例位置:资源包\MR\Code\04\25)
使用shift()方法统计学生每周英语测试成绩的升降情况,程序代码如下:
运行程序,控制台输出结果如图4.22所示。
从运行结果得知:第2次比第1次下降5分,第3次比第2次下降6分,第4次比第3次提升21分,第5次比第4次下降5分。
这里再扩展下,通过10次周测来一看下学生整体英语成绩的升降情况,如图4.23和图4.24所示。
图4.22 英语升降情况
图4.23 10次周测英语成绩升降情况
图4.24 图表展示英语成绩升降情况
说明
有关图表的知识将在第6章介绍,这里先简单了解。
shift()方法还有很多方面的应用。例如这样一个场景:分析股票数据,获取的股票数据中有股票的实时价格,也有每日的收盘价“close”,此时需要将实时价格和上一个工作日的收盘价进行对比,那么通过shift()方法就可以轻松解决。shift()方法还可以应用于时间序列,感兴趣的读者可以在学习完成后续章节进行尝试和探索。
4.5 数据转换
数据转换一般包括一列数据转换为多列数据、行列转换、DataFrame转换为字典、DataFrame转换为列表和DataFrame转换为元组等。
4.5.1 一列数据转换为多列数据
一列数据转换为多列数据的情况在日常工作中经常会用到,从各种系统中导出的订单号、名称、地址很多都是复合组成的(即由多项内容组成),那么,这些列在查找、统计、合并时就没办法使用,需要将它们拆分开。例如,地址信息由省市区街道门牌号等信息组成,如果按省、市或区统计数据,就需要将地址信息中的“省”“市”“区”拆分开,此时就应用到了一列数据转多列数据,通常使用以下方法。
1.split()方法
Pandas的DataFrame对象中的str.split()内置方法可以实现分割字符串,语法如下:
Series.str.split(pat=None, n=-1, expand=False)
参数说明:
pat:字符串、符号或正则表达式,字符串分割的依据,默认以空格分割字符串。
n:整型,分割次数,默认值是-1,0或-1都将返回所有拆分。 expand:布尔型,分割后的结果是否转换为DataFrame,默认值是False。
返回值:系列、索引、DataFrame或多重索引。
首先,我们来看一组淘宝销售订单数据(部分数据),如图4.25所示。
图4.25 淘宝销售订单数据(部分数据)
从图4-25中数据得知:不仅“收货地址”是复合的,“宝贝标题”也是复合的,即由多种产品组成。
【示例26】 分割“收货地址”数据中的“省、市、区”。(示例位置:资源包\MR\Code\04\26)
使用split()方法先对“收货地址”进行分割,程序代码如下:
运行程序,输出结果如图4.26所示。
图4.26 分割后的收货地址
2.join()方法与split()方法结合
【示例27】 以逗号分隔多种产品数据。(示例位置:资源包\MR\Code\04\27)
通过join()方法与split()方法结合,以逗号“,”分隔“宝贝标题”,主要代码如下:
df = df.join(df[‘宝贝标题’].str.split(‘, ‘, expand=True))
运行程序,输出结果如图4.27所示。
图4.27 分隔后的“宝贝标题”
从运行结果得知:“宝贝标题”中含有多种产品的数据被拆分开,这样操作便于日后对每种产品的销量进行统计。
将DataFrame中的tuple(元组)类型数据分隔成多列
【示例28】 对元组数据进行分隔。(示例位置:资源包\MR\Code\04\28)
首先,创建一组包含元组的数据,程序代码如下:
01 import pandas as pd
02 df = pd.DataFrame({‘a’:[1,2,3,4,5], ‘b’:[(1,2), (3,4),(5,6),(7,8),(9,10)]})
03 print(df)
然后,使用apply()函数对元组进行分隔,主要代码如下:
df[[‘b1’, ‘b2’]] = df[‘b’].apply(pd.Series)
或者使用join()方法结合apply()函数,主要代码如下:
df= df.join(df[‘b’].apply(pd.Series))
运行程序,控制台输出原始数据如图4.28所示,结果如图4.29和图4.30所示。
图4.28 原始数据
图4.29 apply()函数分隔元组
图4.30 join()方法结合apply()函数分隔元组
4.5.2 行列转换
在Pandas处理数据过程中,有时需要对数据进行行列转换或重排,主要使用stack()方法、unstack()方法和pivot()方法,下面介绍这3种方法的应用。
1.stack()方法
stack()方法用于将原来的列索引转换成最内层的行索引,转换效果对比示意图如图4.31所示。
图4.31 转换效果对比示意图
stack()方法的语法如下:
DataFrame.stack(level=-1, dropna=True)
参数说明:
level:索引层级,定义为一个索引或标签,或索引或标签列表,默认值是-1。
dropna:布尔型,默认值是True,
返回值:DataFrame对象或Series对象。
【示例29】 对英语成绩表进行行列转换。(示例位置:资源包\MR\Code\04\29)
将学生英语成绩表进行行列转换,程序代码如下:
2.unstack()方法
unstack()方法与stack()方法相反,它是stack()方法的逆操作,即将最内层的行索引转换成列索引,转换效果对比如图4.32所示。
图4.32 unstack()方法转换数据示意图
unstack()方法的语法如下:
DataFrame.unstack(level=-1, fill_value=None)
参数说明:
level:索引层级,定义为一个索引或标签,或索引或标签列表,默认值是-1。
fill_value:整型、字符串或字典,如果unstack()方法产生丢失值,则用这个值替换NaN。 返回值:DataFrame对象或Series对象。
【示例30】 使用unstack()方法转换学生成绩表。(示例位置:资源包\MR\Code\04\30)
同样转换学生成绩表,主要代码如下:
01 df=pd.read_excel(‘grade.xls’,sheet_name=’英语2’) #导入Excel文件
02 df = df.set_index([‘班级’,’序号’,’Unnamed: 2’]) #设置多级索引
03 print(df.unstack())
unstack()方法中有一个参数可以指定转换第几层索引,例如,unstack(0)就是把第一层行索引转换为列索引,默认是将最内层索引转换为列索引。
3.pivot()方法
pivot()方法针对列的值,即指定某列的值作为行索引,指定某列的值作为列索引,然后再指定哪些列作为索引对应的值。unstack()方法针对索引进行操作,pivot()方法针对值进行操作。但实际上,二者的功能往往可以互相实现。
pivot()方法的语法如下:
DataFrame.pivot(index=None, columns=None, values=None)
参数说明:
index:字符串或对象,可选参数。列用于创建新DataFrame数据的索引。如果没有,则使用现有索引。
columns:字符串或对象,列用于创建新DataFrame的列。
values:列用于填充新DataFrame的值,如果未指定,则将使用所有剩余的列,结果将具有分层索引列。
返回值:DataFrame对象或Series对象。
【示例31】 使用pivot()方法转换学生成绩表。(示例位置:资源包\MR\Code\04\31)
使用pivot()方法转换学生成绩表,主要代码如下:
01 df=pd.read_excel(‘grade.xls’,sheet_name=’英语3’) #导入Excel文件
02 df1=df.pivot(index=’序号’,columns=’班级’,values=’得分’)
运行程序,输出结果如图4.33所示。
图4.33 使用pivot()方法转换学生成绩表
4.5.3 DataFrame转换为字典
DataFrame转换为字典主要使用DataFrame对象的to_dict()方法,以索引作为字典的键(key),以列作为字典的值(value)。例如,有一个DataFrame对象(索引为“类别”、列为“数量”),通过to_dict()方法就会生成一个字典,示意图如图4.34所示。如果DataFrame对象包含两列,那么to_dict()方法就会生成一个两层的字典(dict),第一层是列名作为字典的键(key),第二层以索引列的值作为字典的键(key),以列值作为字典的值(value)。
【示例32】 将Excel销售数据转换为字典。(示例位置:资源包\MR\Code\04\32)
使用to_dict()方法将按“宝贝标题”分组统计后的部分数据转换为字典,程序代码如下:
运行程序,控制台输出结果如图4.35所示。
图4.34 DataFrame转换为字典示意图
图4.35 DataFrame转换为字典
4.5.4 DataFrame转换为列表
DataFrame转换为列表主要使用DataFrame对象的tolist()方法。
【示例33】 将电商数据转换为列表。(示例位置:资源包\MR\Code\04\33)
将淘宝销售数据中的“买家会员名”转换为列表,程序代码如下:
运行程序,控制台输出结果如图4.36所示。
图4.36 DataFrame转换为列表
4.5.5 DataFrame转换为元组
DataFrame转换为元组,首先通过循环语句按行读取DataFrame数据,然后使用元组函数tuple()将其转换为元组。
【示例34】 将Excel数据转换为元组。(示例位置:资源包\MR\Code\04\34)
将Excel表中的人物关系部分数据转换成元组,程序代码如下:
运行程序,控制台输出结果如图4.37所示。
图4.37 DataFrame转换为元组
4.5.6 Excel转换为HTML网页格式
日常工作中,有时会涉及财务数据的处理,而Excel应用最为广泛,但是对于展示数据来说,Excel并不友好,如果你想用其他格式的文件来向用户展示,那么,HTML网页格式是不错的选择。首先使用read_excel()方法导入Excel文件,然后使用to_html()方法将DataFrame数据导出为HTML格式,这样便实现了Excel转换为HTML格式。
【示例35】 将Excel订单数据转换为HTML网页格式。(示例位置:资源包\MR\Code\04\35)
将淘宝部分订单数据转换为HTML网页格式,效果如图4.38所示。
图4.38 Excel转换为HTML网页格式
程序代码如下:
01 import pandas as pd
02 df=pd.read_excel(‘mrbooks.xls’)
03 df.to_html(‘mrbook.html’,header = True,index = False)
4.6 数据合并
DataFrame数据合并主要使用merge()方法和concat()方法。
4.6.1 数据合并(merge()方法)
Pandas模块的merge()方法是按照两个DataFrame对象列名相同的列进行连接合并,两个DataFrame对象必须具有同名的列。merge()方法的语法如下:
pandas.merge(right,how=’inner’,on=None,left_on=None,right_on=None,left_index=False,right_index=False,sort=
False,suffixes=(‘_x’,’_y’),copy=True,indicator=False,validate=None)
参数说明:
right:合并对象,DataFrame对象或Series对象。
how:合并类型,参数值可以是left(左合并)、right(右合并)、outer(外部合并)或inner(内部合并),默认值为inner。各个值的说明如下。
left:只使用来自左数据集的键,类似于SQL左外部联接,保留键的顺序。
right:只使用来自右数据集的键,类似于SQL右外部联接,保留键的顺序。
outer:使用来自两个数据集的键,类似于SQL外部联接,按字典顺序对键进行排序。
inner:使用来自两个数据集的键的交集,类似于SQL内部连接,保持左键的顺序。
on:标签、列表或数组,默认值为None。DataFrame对象连接的列或索引级别名称。也可以是DataFrame对象长度的数组或数组列表。
left_on:标签、列表或数组,默认值为None。要连接的左数据集的列或索引级名称,也可以是左数据集长度的数组或数组列表。
right_on:标签、列表或数组,默认值为None。要连接的右数据集的列或索引级名称,也可以是右数据集长度的数组或数组列表。 left_index:布尔型,默认值为False。使用左数据集的索引作为连接键。如果是多重索引,则其他数据中的键数(索引或列数)必须匹配索引级别数。
right_index:布尔型,默认值为False,使用右数据集的索引作为连接键。
sort:布尔型,默认值为False,在合并结果中按字典顺序对连接键进行排序。如果值为False,则连接键的顺序取决于连接类型how参数。
suffixes:元组类型,默认值为(‘_x’,’_y’)。当左侧数据集和右侧数据集的列名相同时,数据合并后列名将带上“_x”和“_y”后缀。
copy:是否复制数据,默认值为True。如果值为False,则不复制数据。
indicator:布尔型或字符串,默认值为False。如果值为True,则添加一个列以输出名为_Merge的DataFrame对象,其中包含每一行的信息。如果是字符串,将向输出的DataFrame对象中添加包含每一行信息的列,并将列命名为字符型的值。
validate:字符串,检查合并数据是否为指定类型。可选参数,其值说明如下。 one_to_one或“1:1”:检查合并键在左、右数据集中是否都是唯一的。 one_to_many或“1:m”:检查合并键在左数据集中是否唯一。 many_to_one或“m:1”:检查合并键在右数据集中是否唯一。 many_to_many或“m:m”:允许,但不检查。
返回值:DataFrame对象,两个合并对象的数据集。
1.常规合并
【示例36】 合并学生成绩表。(示例位置:资源包\MR\Code\04\36)
假设一个DataFrame对象包含了学生的“语文”“数学”“英语”成绩,而另一个DataFrame对象则包含了学生的“体育”成绩,现在将它们合并,示意图如图4.39所示。
图4.39 数据合并效果对比示意图
程序代码如下:
运行程序,控制台输出结果如图4.40所示。
【示例37】 通过索引合并数据。(示例位置:资源包\MR\Code\04\37)
如果通过索引列合并,则需要设置right_index参数和left_index参数值为True。例如,上述举例,通过列索引合并,主要代码如下:
01 df_merge=pd.merge(df1,df2,right_index=True,left_index=True)
02 print(df_merge)
运行程序,控制台输出结果如图4.41所示。
图4.40 合并结果
图4.41 通过索引列合并
【示例38】 对合并数据去重。(示例位置:资源包\MR\Code\04\38)
从图4.41中的运行结果得知:数据中存在重复列(如编号),如果不想要重复列,可以设置按指定列和列索引合并数据,主要代码如下:
df_merge=pd.merge(df1,df2,on=’编号’,left_index=True,right_index=True)
还可以通过how参数解决这一问题。例如,设置该参数值为left,就是让df1保留所有的行列数据,df2则根据df1的行列进行补全,主要代码如下:
df_merge=pd.merge(df1,df2,on=’编号’,how=’left’)
运行程序,控制台输出结果如图4.42所示。
图4.42 合并结果
2.多对一的数据合并
多对一是指两个数据集(df1、df2)的共有列中的数据不是一对一的关系,例如,df1中的“编号”是唯一的,而df2中的“编号”有重复的编号,类似这种就是多对一的关系,示意图如图4.43所示。【示例39】 根据共有列进行合并数据。(示例位置:资源包\MR\Code\04\39)
根据共有列中的数据进行合并,df2根据df1的行列进行补全,程序代码如下:
运行程序,控制台输出结果如图4.44所示。
图4.43 多对一合并示意图
图4.44 合并结果
3.多对多的数据合并
多对多是指两个数据集(df1、df2)的共有列中的数据不全是一对一的关系,都有重复数据,例如“编号”,示图如图4.45所示。
图4.45 多对多示意图
【示例40】 合并数据并相互补全。(示例位置:资源包\MR\Code\04\40)
根据共有列中的数据进行合并,df2、df1相互补全,程序代码如下:
运行程序,控制台输出结果如图4.46所示。
图4.46 合并结果
4.6.2 数据合并(concat()方法)
concat()方法可以根据不同的方式将数据合并,语法如下:
pandas.concat(objs,axis=0,join=’outer’,ignore_index: bool = False, keys=None, levels=None, names=None,
verify_integrity: bool = False, sort: bool = False, copy: bool = True)
参数说明:
objs:Series、DataFrame或Panel对象的序列或映射。如果传递一个字典,则排序的键将用作键参数。
axis:axis=1表示行,axis=0表示列。默认值为0。
join:值为inner(内连接)或outer(外连连接),处理其他轴上的索引方式。默认值为outer。
ignore_index:布尔值,默认值为False,保留索引,索引值为0,…,n-1。如果值为True,则忽略索引。
keys:序列,默认值为None。使用传递的键作为最外层构建层次索引。如果为多索引,应该使用元组。
levels:序列列表,默认值为None。用于构建MultiIndex的特定级别(唯一值);否则,它们将从键推断。
names:list列表,默认值为None。结果层次索引中的级别的名称。
verify_integrity:布尔值,默认值为False。检查新连接的轴是否包含重复项。
sort:布尔值,默认值为True(1.0.0以后版本默认值为False,即不排序)。如果连接为外连接(join=’outer’),则对未对齐的非连接轴进行排序;如果连接为内连接(join=’inner’),则该参数不起作用。
copy:是否复制数据,默认值为True,如果值为False,则不复制数据。
下面介绍concat()方法不同的合并方式,其中dfs代表合并后的DataFrame对象,df1、df2等代表单个DataFrame对象,result代表合并后的结果(DataFrame对象)。
1.相同字段的表首尾相接
表结构相同的数据将直接合并,表首尾相接,主要代码如下:
01 dfs= [df1, df2, df3]
02 result = pd.concat(dfs)
例如,表df1、df2和df3结构相同,如图4.47所示。合并后的效果如图4.48所示。如果想要在合并数据时标记源数据来自哪张表,则需要在代码中加入参数keys,例如表名分别为“1月”“2月”“3月”,合并后的效果如图4.49所示。
图4.47 3张相同字段的表
图4.48 首尾相接合并后的效果
图4.49 合并后带标记(月份)的效果
主要代码如下:
result = pd.concat(dfs, keys=[‘1月’, ‘2月’, ‘3月’])
2.横向表合并(行对齐)
当合并的数据列名称不一致时,可以设置参数axis=1,concat()方法将按行对齐,然后将不同列名的两组数据进行合并,缺失的数据用NaN填充,df1和df4合并前后效果如图4.50和图4.51所示。
图4.50 横向表合并前
图4.51 横向表合并后
主要代码如下:
result = pd.concat([df1, df4], axis=1)
3.交叉合并
交叉合并,需要在代码中加上join参数,如果值为inner,结果是两张表的交集;如果值为outer,结果是两张表的并集。例如两张表交集,表df1和df4合并前后的效果如图4.52和图4.53所示。
图4.52 交叉合并前
图4.53 交叉合并后
主要代码如下:
result = pd.concat([df1, df4], axis=1, join=’inner’)
4.指定表对齐数据(行对齐)
如果指定参数join_axes,就可以指定根据哪张表来对齐数据。例如,根据df4对齐数据,就会保留表df4的数据,然后将表df1的数据与之合并,行数不变,合并前后的效果与如图4.54和图4.55所示。
图4.54 指定表对齐数据合并前
图4.55 指定表对齐数据合并后
主要代码如下:
result = pd.concat([df1, df4], axis=1, join_axes=[df4.index])
4.7 数据导出
4.7.1 导出为.xlsx文件
导出数据为Excel,主要使用DataFrame对象的to_excel()方法,语法如下:
DataFrame.to_excel(excel_writer,sheet_name=’Sheet1’,na_rep=’’,float_format=None,columns=None,header=True,
index=True,index_label=None,startrow=0,startcol=0,engine=None,merge_cells=True, encoding=None, inf_rep=’inf’,
verbose=True, freeze_panes=None)
参数说明:
excel_writer:字符串或ExcelWriter对象。
sheet_name:字符串,默认值为Sheet1,包含DataFrame的表的名称。
na_rep:字符串,默认值为’ ‘。缺失数据的表示方式。
float_format:字符串,默认值为None,格式化浮点数的字符串。
columns:序列,可选参数,要编辑的列。
header:布尔型或字符串列表,默认值为True。列名称,如果给定字符串列表,则表示它是列名称的别名。
index:布尔型,默认值为True,行名(索引)。
index_label:字符串或序列,默认值为None。如果需要,可以使用索引列的列标签;如果没有给出,标题和索引为True,则使用索引名称;如果数据文件使用多索引,则需使用序列。
startrow:指定从哪一行开始写入数据。
startcol:指定从哪一列开始写入数据。
engine:字符串,默认值为None,指定要使用的写引擎,如openpyxl或xlsxwriter。也可以通过io.excel.xlsx.writer、io.excel.xls.writer和io.excel.xlsm.writer进行设置。
merge_cells:布尔型,默认值为True。
encoding:指定Excel文件的编码方式,默认值为None。
inf_rep:字符串,默认值为“正”,表示无穷大。
verbose:布尔型,默认值为True。在错误日志中显示更多信息。 freeze_panes:整数的元组,长度2,默认值为None。指定要冻结的行列。
【示例41】 将处理后的数据导出为Excel文件。(示例位置:资源包\MR\Code\04\41)
将数据合并后的结果导出为Excel文件,主要代码如下:
df_merge.to_excel(‘merge.xlsx’)
运行程序,数据将导出为Excel文件,如图4.56所示。
图4.56 导出为Excel文件
上述举例,如果需要指定Sheet页名称,可以通过sheet_name参数指定,主要代码如下:
df1.to_excel(‘df1.xlsx’,sheet_name=’df1’)
4.7.2 导出为.csv文件
导出数据为.csv文件,主要使用DataFrame对象的to_csv()方法,语法如下:
DataFrame.to_csv(path_or_buf=None,sep=’,’,na_rep=’’,float_format=None,columns=None,header=True,index=
True,index_label=None,mode=’w’,encoding=None,compression=’infer’,quoting=None,quotechar=’”‘,line_terminator=
None,chunksize=None,date_format=None,doublequote=True, escapechar=None, decimal=’.’,errors=’strict’)
参数说明:
path_or_buf:要保存的路径及文件名。
sep:分隔符,默认值为”,”。
na_rep:指定空值的输出方式,默认值为空字符串。
float_format:浮点数的输出格式,要用双引号括起来。
columns:指定要导出的列,用列名列表表示,默认值为None。 header:是否输出列名,默认值为True。
index:是否输出索引,默认值为True。
index_label:索引列的列名,默认值为None。
mode:Python写入模式,默认值为w。
encoding:编码方式,默认值为utf-8。
compression:压缩模式,默认值为infer。
quoting:导出.csv文件是否用引号,默认值为0,表示不加双引号;如果值为1,则每个字段都会加上引号,数值也会被当作字符串看待。
quotechar:引用字符,当quoting=1时可以指定引号字符为双引号(” “)或单引号(’ ‘)。
line_terminator:换行符,默认值为\n。
chunksize:一次写入.csv文件的行数,当DataFrame对象数据特别大时需要分批写入。
date_format:日期输出格式。
doublequote:是否添加双引用符,默认值为True。
escapechar:设置转义字符。
decimal:可识别十进制分隔符的字符。
errors:指定如何处理编码和解码错误,默认值为strict(严格的)。
【示例42】 将处理后的数据导出为.csv文件。(示例位置:资源包\MR\Code\04\42)
下面介绍to_csv()方法常用功能,举例如下,df为DataFrame对象。
(1)相对位置,保存在程序所在路径下,代码如下:
df.to_csv(‘Result.csv’)
(2)绝对位置,代码如下:
df.to_csv(‘d:\Result.csv’)
(3)分隔符。使用问号(?)分隔符分隔需要保存的数据,代码如下:
df.to_csv(‘Result.csv’,sep=’?’)``
(4)替换空值,缺失值保存为NA,代码如下:
df.to_csv(‘Result1.csv’,na_rep=’NA’)
(5)格式化数据,保留两位小数,代码如下:
df.to_csv(‘Result1.csv’,float_format=’%.2f’)
(6)保留某列数据,保存索引列和name列,代码如下:
df.to_csv(‘Result.csv’,columns=[‘name’])
(7)是否保留列名,不保留列名,代码如下:
df.to_csv(‘Result.csv’,header=False)
(8)是否保留行索引,不保留行索引,代码如下:
df.to_csv(‘Result.csv’,index=False)
4.7.3 导出多个Sheet
导出多个Sheet,应首先使用pd.ExcelWriter()方法打开一个Excel文件,然后再使用to_excel()方法导出指定的Sheet。
【示例43】 导出Excel中多个Sheet页的数据。(示例位置:资源包\MR\Code\04\43)
导出指定Sheet页中的数据,主要代码如下:
4.8 日期数据处理
4.8.1 DataFrame的日期数据转换
日常工作中,有一个非常麻烦的事情就是日期的格式可以有很多种表达,我们看到同样是2020年2月14日,可以有很多种格式,如图4.57所示。那么,我们需要先将这些格式统一后才能进行后续的工作。Pandas提供了to_datetime()方法可以帮助我们解决这一问题。
to_datetime()方法可以用来批量处理日期数据转换,对于处理大数据非常实用和方便,它可以将日期数据转换成你需要的各种格式。例如,将2/14/20和14-2-2020转换为日期格式2020-02-14。to_datetime()方法的语法如下:
pandas.to_datetime(arg,errors=’ignore’,dayfirst=False,yearfirst=False,utc=None,box=True,format=None,exact=
True,unit=None,infer_datetime_format=False,origin=’unix’,cache=False)
参数说明:
arg:字符串、日期时间、字符串数组。
errors:值为ignore、raise或coerce,具体说明如下,默认值为ignore,即忽略错误。
ignore:无效的解析将返回原值。
raise:无效的解析将引发异常。
coerce:无效的解析将被设置为NaT,即无法转换为日期的数据将被转换为NaT。
dayfirst:第一个为天,布尔型,默认值为False。例如02/09/2020,如果值为True,则解析日期的第一个为天,即2020-09-02;如果值为False,则解析日期与原日期一致,即2020-02-09。
yearfirst:第一个为年,布尔型,默认值为False。例如14-Feb-20,如果值为True,则解析日期的第一个为年,即2014-02-20;如果值为False,则解析日期与原日期一致,即2020-02-14。
utc:默认值为None。返回utc即协调世界时间。
box:布尔值,默认值为True,如果值为True,则返回DatetimeIndex;如果值为False,则返回ndarray。
format:格式化显示时间的格式。字符串,默认值为None。
exact:布尔值,默认值为True。如果为True,则要求格式完全匹配;如果为False,则允许格式与目标字符串中的任何位置匹配。
unit:默认值为None,参数的单位(D、s、、ms、μs、ns)表示时间的单位。
infer_datetime_format:默认值为False。如果没有格式,则尝试根据第一个日期时间字符串推断格式。
origin:默认值为unix。定义参考日期。数值将被解析为单位数。
cache:默认值为False。如果值为True,则使用唯一、转换日期的缓存应用日期时间转换。在解析重复日期字符串,特别是带有时区偏移的字符串时,可能会产生明显的加速。只有在至少有50个值时才使用缓存。越界值的存在将使缓存不可用,并可能减慢解析速度。
返回值:日期时间。
【示例44】 将各种日期字符串转换为指定的日期格式。(示例位置:资源包\MR\Code\04\44)
将2020年2月14日的各种格式转换为日期格式,程序代码如下:
运行程序,控制台输出结果如图4.58所示。
图4.57 日期的多种格式转换
图4.58 2020年2月14日的各种格式转换为日期格式
还可以实现从DataFrame对象中的多列,如年、月、日各列组合成一列日期。键值是常用的日期缩略语。
组合要求:
必选:year、month、day。
可选:hour、minute、second、millisecond(毫秒)、microsecond(微秒)、nanosecond(纳秒)。
【示例45】 将一组数据组合为日期数据。(示例位置:资源包\MR\Code\04\45)
将一组数据组合为日期数据,主要代码如下:
运行程序,控制台输出结果如图4.59所示。
图4.59 日期组合
4.8.2 dt对象的使用
dt对象是Series对象中用于获取日期属性的一个访问器对象,通过它可以获取日期中的年、月、日、星期数、季节等,还可以判断日期是否处在年底。语法如下:
Series.dt()
参数说明:
返回值:返回与原始系列相同的索引系列。如果Series不包含类日期值,则引发错误。
dt对象提供了year、month、day、dayofweek、dayofyear、is_leap_year、quarter、weekday_name等属性和方法。
例如,year可以获取“年”、month可以获取“月”、quarter可以直接得到每个日期分别是第几个季度,weekday_name可以直接得到每个日期对应的是周几。
【示例46】 获取日期中的年、月、日、星期数等。(示例位置:资源包\MR\Code\04\46)
使用dt对象获取日期中的年、月、日、星期数、季节等。
(1)获取年、月、日,代码如下:
df[‘年’],df[‘月’],df[‘日’]=df[‘日期’].dt.year,df[‘日期’].dt.month,df[‘日期’].dt.day
(2)从日期判断出所处星期数,代码如下:
df[‘星期几’]=df[‘日期’].dt.day_name()
(3)从日期判断所处季度,代码如下:
df[‘季度’]=df[‘日期’].dt.quarter
(4)从日期判断是否为年底最后一天,代码如下:
df[‘是否年底’]=df[‘日期’].dt.is_year_end
运行程序,控制台输出结果如图4.60所示。
图4.60 dt对象日期转换
4.8.3 获取日期区间的数据
获取日期区间的数据的方法是直接在DataFrame对象中输入日期或日期区间,但前提必须设置日期为索引,举例如下。
获取2018年的数据。
df1[‘2018’]
获取2017—2018年的数据。
df1[‘2017’:’2018’]
获取某月(2018年7月)的数据。
df1[‘2018-07’]
获取具体某天(2018年5月6日)的数据。
df1[‘2018-05-06’:’2018-05-06’]
【示例47】 获取指定日期区间的订单数据。(示例位置:资源包\MR\Code\04\47)
获取2018年5月11日至6月10日的订单,结果如图4.61所示。
程序代码如下:
图4.61 2018年5月11日至6月10日的订单(省略部分数据)
4.8.4 按不同时期统计并显示数据
1.按时期统计数据
按时期统计数据主要通过DataFrame对象的resample()方法结合数据计算函数实现。resample()方法主要应用于时间序列频率转换和重采样,它可以从日期中获取年、月、日、星期、季节等,结合数据计算函数就可以实现按年、月、日、星期或季度等不同时期统计数据。举例如下所示。
(1)按年统计数据,代码如下:
df1=df1.resample(‘AS’).sum()
(2)按季度统计数据,代码如下:
df2.resample(‘Q’).sum()
(3)按月度统计数据,代码如下:
df1.resample(‘M’).sum()
(4)按星期统计数据,代码如下:
df1.resample(‘W’).sum()
(5)按天统计数据,代码如下:
df1.resample(‘D’).sum()
实用技巧
按日期统计数据过程中,可能会出现如图4.62所示的错误提示。
图4.62 错误提示
完整错误描述:
TypeError: Only valid with DatetimeIndex, TimedeltaIndex or PeriodIndex, but got an instance of’Index’
出现上述错误,是由于resample()函数要求索引必须为日期型。
解决方法:将数据的索引转换为datetime类型,主要代码如下:
df1.index = pd.to_datetime(df1.index)
2.按时期显示数据
DataFrame对象的to_period()方法可以将时间戳转换为时期,从而实现按时期显示数据,前提是日期必须设置为索引。语法如下:
DataFrame.to_period(freq=None, axis=0, copy=True)
参数说明:
freq:字符串,周期索引的频率,默认值为None。
axis:行列索引,axis=0表示行索引,axis=1表示列索引。默认值为0,即表示行索引。
copy:是否复制数据,默认值为True,如果值为False,则不复制数据。 返回值:带周期索引的时间序列。
【示例48】 从日期中获取不同的时期。(示例位置:资源包\MR\Code\04\48)
从日期中获取不同的时期,主要代码如下:
3.按时期统计并显示数据
(1)按年统计并显示数据,代码如下:
df2.resample(‘AS’).sum().to_period(‘A’)
控制台输出结果如图4.63所示。
(2)按季度统计并显示数据,代码如下:
Q_df=df2.resample(‘Q’).sum().to_period(‘Q’)
控制台输出结果如图4.64所示。
图4.63 按年统计并显示数据
图4.64 按季度统计并显示数据
(3)按月统计并显示数据,代码如下:
df2.resample(‘M’).sum().to_period(‘M’)
控制台输出结果如图4.65所示。
(4)按星期统计并显示数据(前5条数据),代码如下:
df2.resample(‘W’).sum().to_period(‘W’).head()
控制台输出结果如图4.66所示。
图4.65 按月统计并显示数据
图4.66 按星期统计并显示数据
4.9 时间序列
4.9.1 重采样(Resample()方法)
通过前面的学习,我们学会了如何生成不同频率的时间索引,按小时、按天、按周、按月等,如果想对数据做不同频率的转换,该怎么办?在Pandas中对时间序列的频率的调整称为重新采样,即将时间序列从一个频率转换到另一个频率的处理过程。例如,每天一个频率转换为每5天一个频率,如图4.67所示。
图4.67 时间频率
重采样主要使用resample()方法,该方法用于对常规时间序列重新采样和频率转换,包括降采样和升采样两种。首先了解下resample()方法,语法如下:
DataFrame.resample(rule,how=None,axis=0,fill_method=None,closed=None,label=None,convention=’start’,kind=
None,loffset=None,limit=None,base=0,on=None,level=None)
参数说明:
rule:字符串,偏移量表示目标字符串或对象转换。
how:用于产生聚合值的函数名或数组函数。例如mean、ohlc和np.max等,默认值为mean,其他常用的值为first、last、median、max和min。
axis:整型,表示行列,axis=0表示列,axis=1表示行。默认值为0,即表示列。 fill_method:升采样时所使用的填充方法,ffill()方法(用前值填充)或bfill()方法(用后值填充),默认值为None。
closed:降采样时,时间区间的开和闭,与数学里区间的概念一样,其值为right或left,right表示左开右闭(即左边值不包括在内),left表示左闭右开(即右边值不包括在内),默认值为right左开右闭。
label:降采样时,如何设置聚合值的标签。例如,10:30—10:35会被标记成10:30还是10:35,默认值为None。
convention:当重采样时,将低频率转换到高频率所采用的约定,其值为start或end,默认值为start。
kind:聚合到时期(period)或时间戳(timestamp),默认聚合到时间序列的索引类型,默认值为None。
loffset:聚合标签的时间校正值,默认值为None。例如,-1s或Second(-1)用于将聚合标签调早1秒。
limit:向前或向后填充时,允许填充的最大时期数,默认值为None。
base:整型,默认值为0。对于均匀细分1天的频率,聚合间隔的“原点”。例如,对于5min频率,base的范围可以是0~4。 on:字符串,可选参数,默认值为None。对DataFrame对象使用列代替索引进行重新采样。列必须与日期时间类似。
level:字符串或整型,可选参数,默认值为None。用于多索引,重新采样的级别名称或级别编号,级别必须与日期时间类似。
返回值:重新采样对象。
【示例49】 一分钟的时间序列转换为3分钟的时间序列。(示例位置:资源包\MR\Code\04\49)
首先创建一个包含9个一分钟的时间序列,然后使用resample()方法转换为3分钟的时间序列,并对索引列进行求和计算,如图4.68所示。
图4.68 时间序列转换
程序代码如下:
01 import pandas as pd
02 index = pd.date_range(‘02/02/2020’, periods=9, freq=’T’)
03 series = pd.Series(range(9), index=index)
04 print(series)
05 print(series.resample(‘3T’).sum())
4.9.2 降采样处理
降采样是周期由高频率转向低频率。例如,将5min股票交易数据转换为日交易,按天统计的销售数据转换为按周统计。
数据降采样会涉及数据的聚合。例如,天数据变成周数据,那么就要对1周7天的数据进行聚合,聚合的方式主要包括求和、求均值等。例如,淘宝店铺每天销售数据(部分数据),如图4.69所示。
【示例50】 按周统计销售数据。(示例位置:资源包\MR\Code\04\50)
使用resample()方法来做降采样处理,频率为“周”,也就是将上述销售数据处理为每周(每7天)求和一次数据,程序代码如下:
01 import pandas as pd
02 df=pd.read_excel(‘time.xls’)
03 df1 = df.set_index(‘订单付款时间’) #设置“订单付款时间”为索引
04 print(df1.resample(‘W’).sum().head())
运行程序,控制台输出结果如图4.70所示。
在参数说明中,我们列出了closed参数的解释,如果把closed参数值设置为left,结果如图4.71所示。
图4.69 淘宝店铺每天销售数据(部分数据)
图4.70 周数据统计1
图4.71 周数据统计2
4.9.3 升采样处理
升采样是周期由低频率转向高频率。将数据从低频率转换到高频率时,就不需要聚合了,将其重采样到日频率,默认会引入缺失值。
例如,原来是按周统计的数据,现在变成按天统计。升采样会涉及数据的填充,根据填充的方法不同,填充的数据也不同。下面介绍3种填充方法。 不填充。空值用NaN代替,使用asfreq()方法。 用前值填充。用前面的值填充空值,使用ffill()方法或者pad()方法。为了方便记忆,ffill()方法可以使用它的第一个字母“f”代替,代表forward,向前的意思。 用后值填充,使用bfill()方法,可以使用字母“b”代替,代表back,向后的意思。
【示例51】 每6小时统计一次数据。(示例位置:资源包\MR\Code\04\51)
下面创建一个时间序列,起始日期是2020-02-02,一共两天,每天对应的数值分别是1和2,通过升采样处理为每6小时统计一次数据,空值以不同的方式填充,程序代码如下:
01 import pandas as pd
02 import numpy as np
03 rng = pd.date_range(‘20200202’, periods=2)
05 s1_6h_asfreq = s1.resample(‘6H’).asfreq()
06 print(s1_6h_asfreq)
07 s1_6h_pad = s1.resample(‘6H’).pad()
08 print(s1_6h_pad)
09 s1_6h_ffill = s1.resample(‘6H’).ffill()
10 print(s1_6h_ffill)
11 s1_6h_bfill = s1.resample(‘6H’).bfill()
12 print(s1_6h_bfill)
运行程序,控制台输出结果如图4.72所示。
图4.72 6小时数据统计
4.9.4 时间序列数据汇总(ohlc()函数)
在金融领域,经常会看到开盘(open)、收盘(close)、最高价(high)和最低价(low)数据,而在Pandas中经过重新采样的数据也可以实现这样的结果,通过调用ohlc()函数得到数据汇总结果,即开始值(open)、结束值(close)、最高值(high)和最低值(low)。ohlc()函数的语法如下:
resample.ohlc()
ohlc()函数返回DataFrame对象,每组数据的open(开)、high(高)、low(低)和close(关)值。
【示例52】 统计数据的open、high、low和close值。(示例位置:资源包\MR\Code\04\52)
下面是一组5分钟的时间序列,通过ohlc()函数获取该时间序列中每组时间的开始值、最高值、最低值和结束值,程序代码如下:
01 import pandas as pd
02 import numpy as np
03 rng = pd.date_range(‘2/2/2020’,periods=12,freq=’T’)
04 s1 = pd.Series(np.arange(12),index=rng)
05 print(s1.resample(‘5min’).ohlc())
运行程序,控制台输出结果如图4.73所示。
图4.73 时间序列数据汇总
4.9.5 移动窗口数据计算(rolling()函数)
通过重采样可以得到想要的任何频率的数据,但是这些数据也是一个时点的数据,那么就存在这样一个问题:时点的数据波动较大,某一点的数据就不能很好地表现它本身的特性,于是就有了“移动窗口”的概念,简单地说,为了提升数据的可靠性,将某个点的取值扩大到包含这个点的一段区间,用区间来进行判断,这个区间就是窗口。
下面举例说明,图4.74显示了移动窗口数据示意图,其中时间序列代表1号到15号每天的销量数据,接下来以3天为一个窗口,将该窗口从左至右依次移动,统计出3天的平均值作为这个点的值,如3号的销量是1号、2号和3号的平均值。
图4.74 移动窗口数据示意图
通过上述示意图相信您已经理解了移动窗口,在Pandas中可以通过rolling()函数实现移动窗口数据的计算,语法如下:
DataFrame.rolling(window, min_periods=None, center=False, win_type=None, on=None, axis=0, closed=None)
参数说明:
window:时间窗口的大小,有两种形式,即int或offset。如果使用int,则数值表示计算统计量的观测值的数量,即向前几个数据;如果使用offset,则表示时间窗口的大小。
min_periods:每个窗口最少包含的观测值数量,小于这个值的窗口结果为NA。值可以是int,默认值为None。offset情况下,默认值为1。
center:把窗口的标签设置为居中。布尔型,默认值为False,居右。
win_type:窗口的类型。截取窗的各种函数。字符串类型,默认值为None。
on:可选参数。对于DataFrame对象,是指定要计算移动窗口的列,值为列名。
axis:整型,axis=0表示列,axis=1表示行。默认值为0,即对列进行计算。
closed:定义区间的开闭,支持int类型的窗口。对于offset类型默认是左开右闭(默认值为right)。可以根据情况指定left。
返回值:为特定操作而生成的窗口或移动窗口子类。
【示例53】 创建淘宝每日销量数据。(示例位置:资源包\MR\Code\04\53)
首先创建一组淘宝每日销量数据,程序代码如下:
01 import pandas as pd
02 index=pd.date_range(‘20200201’,’20200215’)
03 data=[3,6,7,4,2,1,3,8,9,10,12,15,13,22,14]
04 s1_data=pd.Series(data,index=index)
05 print(s1_data)
运行程序,控制台输出结果如图4.75所示。
【示例54】 使用rolling()函数计算3天的均值。(示例位置:资源包\MR\Code\04\54)
下面使用rolling()函数计算2020-02-01至2020-02-15中每3天的均值,窗口个数为3,代码如下:
s1_data.rolling(3).mean()
运行程序,看下rolling()函数是如何计算的?在图4.76中,当窗口开始移动时,第一个时间点2020-02-01和第二个时间点2020-02-02的数值为空,这是因为窗口个数为3,它们前面有空数据,所以均值为空;而到第三个时间点2020-02-03时,它前面的数据是2020-02-01至2020-02-03,所以3天的均值是5.333333;以此类推。
图4.75 原始数据
图4.76 2020-02-01至2020-02-15移动窗口均值1
【示例55】 用当天的数据代表窗口数据。(示例位置:资源包\MR\Code\04\55)
在计算第一个时间点2020-02-01的窗口数据时,虽然数据不够窗口长度3,但是至少有当天的数据,那么能否用当天的数据代表窗口数据呢?答案是肯定的,通过设置min_periods参数即可,它表示窗口最少包含的观测值,小于这个值的窗口长度显示为空,等于或大于时都有值,主要代码如下:
s1_data.rolling(3,min_periods=1).mean()
运行程序,对比效果如图4.77所示。
上述举例,我们再扩展下,通过图表观察原始数据与移动窗口数据的平稳性,如图4.78所示。其中实线代表移动窗口数据,其走向更平稳,这也是学习移动窗口rolling()函数的原因。
图4.77 2020-02-01至2020-02-15移动窗口均值2
图4.78 移动窗口数据的平稳性说明
虚线代表原始数据,实线代表移动窗口数据。
4.10 综合应用
4.10.1 案例1:Excel多表合并
案例位置:资源包\MR\Code\04\example\01
在日常工作中,几乎我们每天都有大量的数据需要处理,桌面上总是布满密密麻麻的Excel表,这样看上去非常凌乱,其实我们完全可以将其中类别相同的Excel表合并到一起,这样不但不会丢失数据,而且还可以有效地分析数据。下面使用concat()方法将指定文件夹内的所有Excel表合并,程序代码如下:
4.10.2 案例2:股票行情数据分析
案例位置:资源包\MR\Code\04\example\02
股票数据包括开盘价、收盘价、最高价、最低价、成交量等多个指标。其中,收盘价是当日行情的标准,也是下一个交易日开盘价的依据,可以预测未来证券市场行情,因此当投资者对行情分析时,一般采用收盘价作为计算依据。
下面使用rolling()函数计算某股票20天、50天和200天的收盘价均值并生成走势图(也称K线图),如图4.79所示。
图4.79 股票行情分析
程序代码如下:
实用技巧
默认情况下,图表嵌入控制台(IPython console)中并以静态方式显示,无法进行移动、放大、缩小等操作。此时需要在工具中的首选项窗口进行设置,方法为选择Tools→Preferences命令,打开Preferences(首选项)窗口,在左侧列表中选择IPython console(IPython控制台),在右侧窗口选择Graphics(图形)选项卡,然后在Backend(后端)下拉列表框中选择Automatic(自动)或者Qt5,单击Apply(应用)按钮。设置完成后重新启动Spyder才生效。
4.11 小结
本章是Pandas的进阶学习,有一定难度,但同时也更能够体现Pandas的强大之处,不仅可以完成数据处理工作,而且还能够实现数据的统计分析。Pandas提供的大量函数使统计分析工作变得简单高效。别具特色的“数据位移”是一个非常有用的方法,与其他方法结合,能够实现很多难以想象的功能,数据转换将DataFrame与Python数据类型之间进行灵活转换。不仅如此,对于日期数据的处理、时间序列也都提供了专门的函数和方法,使得量化数据得心应手。
- 本文作者: 不想
- 本文链接: https://evacat.top/2020/05/16/第四章-Pandas统计分析/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!