系统重温Pandas笔记:(五)变形
文章目录
- 写在前面
- 一、长宽表的变形
- 1. pivot
- 2. pivot_table
- 【练一练】
- 3. melt
- 4. wide_to_long
- 二、索引的变形
- 1. stack与unstack
- 2. 聚合与变形的关系
- 三、其他变形函数
- 1. crosstab
- 【练一练】
- 2. explode
- 3. get_dummies
- 四、练习
- Ex1:美国非法药物数据集
- Ex2:特殊的wide_to_long方法
写在前面
本文内容源自Datawhale 组队学习教程,并结合了部分自己的笔记和感悟。对Datawhale感兴趣且想进一步了解:https://github.com/datawhalechina/joyful-pandas
一、长宽表的变形
以性别列为例:
- 长表:
- 宽表:
1. pivot
pivot
是一种典型的长表变宽表的函数。
e.g.
对于一个基本的长变宽的操作而言,最重要的有三个要素:
- 变形后的行索引
- 需要转到列索引的列
- 这些列和行索引对应的数值
它们分别对应了pivot
方法中的index, columns, values
参数。
新生成表的列索引是columns
对应列的unique
值,而新表的行索引是index
对应列的unique
值,而values
对应了想要展示的数值列。
df.pivot(index='Name', columns='Subject', values='Grade')
即:
利用pivot
进行变形操作需要满足唯一性的要求,即由于在新表中的行列索引对应了唯一的value
,因此原表中的index
和columns
对应两个列的行组合必须唯一。
pandas
从1.1.0
开始,pivot
相关的三个参数允许被设置为列表,这也意味着会返回多级索引。
e.g.
现在想要把测试类型和科目联合组成的四个类别(期中语文、期末语文、期中数学、期末数学)转到列索引,并且同时统计成绩和排名:
pivot_multi = df.pivot(index = ['Class', 'Name'],columns = ['Subject','Examination'],values = ['Grade','rank'])
pivot_multi
根据唯一性原则,新表的行索引等价于对index
中的多列使用drop_duplicates
,而列索引的长度为values
中的元素个数乘以columns
的唯一组合数量(与index
类似)。
即:
2. pivot_table
pivot
的使用依赖于唯一性条件,那如果不满足唯一性条件,那么必须通过聚合操作使得相同行列组合对应的多个值变为一个值。
例如,张三和李四都参加了两次语文考试和数学考试,按照学院规定,最后的成绩是两次考试分数的平均值,此时就无法通过pivot
函数来完成。
pandas
中提供了pivot_table
来实现,其中的aggfunc
参数就是使用的聚合函数。上述场景可以如下写出:
df.pivot_table(index = 'Name',columns = 'Subject',values = 'Grade',aggfunc = 'mean')
这里传入aggfunc
包含了上一章中介绍的所有合法聚合字符串,此外还可以传入以序列为输入,标量为输出的聚合函数来实现自定义操作,上述功能可以等价写出:
df.pivot_table(index = 'Name',columns = 'Subject',values = 'Grade',aggfunc = lambda x:x.mean())
此外,pivot_table
具有边际汇总的功能,可以通过设置margins=True
来实现,其中边际的聚合方式与aggfunc
中给出的聚合方法一致。
下面就分别统计了语文均分和数学均分、张三均分和李四均分,以及总体所有分数的均分:
df.pivot_table(index = 'Name',columns = 'Subject',values = 'Grade',aggfunc='mean',margins=True)
【练一练】
在上面的边际汇总例子中,行或列的汇总为新表中行元素或者列元素的平均值,而总体的汇总为新表中四个元素的平均值。这种关系一定成立吗?若不成立,请给出一个例子来说明。
解:
不成立,因为这和每个成绩的权重有关系。在上面的例子中,每个成绩的权重是相同的,下面举一个反例:我增加一个San Zhang
同学的Chinese
成绩:
df = pd.DataFrame({'Name':['San Zhang', 'San Zhang', 'San Zhang', 'San Zhang', 'San Zhang','Si Li', 'Si Li', 'Si Li', 'Si Li'],'Subject':['Chinese', 'Chinese','Chinese', 'Math', 'Math','Chinese', 'Chinese', 'Math', 'Math'],'Grade':[80, 90,90, 100, 90, 70, 80, 85, 95]})
df
然后再进行相应的边际汇总:
df.pivot_table(index = 'Name',columns = 'Subject',values = 'Grade',aggfunc='mean',margins=True)
如上图中的结果所示:San Zhang
同学的五个成绩平均分为90:
这与表中All
列里San Zhang
的90对应,但并不是86.666667和95.0的平均数。
综上所述,这种关系不成立。
3. melt
pivot
的逆操作:把宽表转为长表。
e.g.
使用melt
函数:
df_melted = df.melt(id_vars = ['Class', 'Name'],value_vars = ['Chinese', 'Math'],var_name = 'Subject',value_name = 'Grade')
df_melted
即:
通过pivot
操作把df_melted
转回df
的形式:
df_unmelted = df_melted.pivot(index = ['Class', 'Name'],columns='Subject',values='Grade')
df_unmelted
恢复索引,并且重命名列索引名称:
df_unmelted = df_unmelted.reset_index().rename_axis(columns={'Subject':''})
df_unmelted
检验是否相同:
df_unmelted.equals(df)True
4. wide_to_long
melt
方法中,在列索引中被压缩的一组值对应的列元素只能代表同一层次的含义,即values_name
。
现在如果列中包含了交叉类别,比如期中期末的类别和语文数学的类别,那么想要把values_name
对应的Grade
扩充为两列分别对应语文分数和数学分数,只把期中期末的信息压缩,这种需求下就要使用wide_to_long
函数来完成。
pd.wide_to_long(df,stubnames=['Chinese', 'Math'],i = ['Class', 'Name'],j='Examination',sep='_',suffix='.+')
即:
下面给出一个比较复杂的案例,把之前在pivot
一节中多列操作的结果(产生了多级索引),利用wide_to_long
函数,将其转为原来的形态。其中,使用了第八章的str.split
函数,目前暂时只需将其理解为对序列按照某个分隔符进行拆分即可。
原表:
通过如下操作:
res = pivot_multi.copy()
res.columns = res.columns.map(lambda x:'_'.join(x))
res = res.reset_index()
res = pd.wide_to_long(res, stubnames=['Grade', 'rank'],i = ['Class', 'Name'],j = 'Subject_Examination',sep = '_',suffix = '.+')
res = res.reset_index()
res[['Subject', 'Examination']] = res['Subject_Examination'].str.split('_', expand=True)
res = res[['Class', 'Name', 'Examination', 'Subject', 'Grade', 'rank']].sort_values('Subject')
res = res.reset_index(drop=True)
res
变为:
二、索引的变形
1. stack与unstack
在第二章中提到了利用swaplevel
或者reorder_levels
进行索引内部的层交换,下面就要讨论行列索引之间\color{red}{行列索引之间}行列索引之间的交换,由于这种交换带来了DataFrame
维度上的变化,因此属于变形操作。在第一节中提到的4种变形函数与其不同之处在于,它们都属于某一列或几列元素\color{red}{元素}元素和列索引\color{red}{列索引}列索引之间的转换,而不是索引之间的转换。
unstack
函数的作用是把行索引转为列索引:
df.unstack()
unstack
的主要参数是移动的层号,默认转化最内层,移动到列索引的最内层,同时支持同时转化多个层:
df.unstack(1)
df.unstack([0,2])
类似于pivot
中的唯一性要求,在unstack
中必须保证被转为列索引的行索引层\color{red}{被转为列索引的行索引层}被转为列索引的行索引层和被保留的行索引层\color{red}{被保留的行索引层}被保留的行索引层构成的组合是唯一的,否则会报错。
- 与
unstack
相反,stack
的作用就是把列索引的层压入行索引,其用法完全类似。
df.stack()
df.stack([1, 2])
2. 聚合与变形的关系
在上面介绍的所有函数中,除了带有聚合效果的pivot_table
以外,所有的函数在变形前后并不会带来values
个数的改变,只是这些值在呈现的形式上发生了变化。
在上一章讨论的分组聚合操作,由于生成了新的行列索引,因此必然也属于某种特殊的变形操作,但由于聚合之后把原来的多个值变为了一个值,因此values
的个数产生了变化,这也是分组聚合与变形函数的最大区别。
三、其他变形函数
1. crosstab
crosstab
并不是一个值得推荐使用的函数,因为它能实现的所有功能pivot_table
都能完成,并且速度更快。在默认状态下,crosstab
可以统计元素组合出现的频数,即count
操作。
例如统计learn_pandas
数据集中学校和转系情况对应的频数:
df = pd.read_csv('data/learn_pandas.csv')
pd.crosstab(index = df.School, columns = df.Transfer)
这等价于如下crosstab
的如下写法,这里的aggfunc
即聚合参数:
pd.crosstab(index = df.School, columns = df.Transfer, values = [0]*df.shape[0], aggfunc = 'count')
同样,可以利用pivot_table
进行等价操作,由于这里统计的是组合的频数,因此values
参数无论传入哪一个列都不会影响最后的结果:
df.pivot_table(index = 'School',columns = 'Transfer',values = 'Name',aggfunc = 'count')
从上面可以看出这两个函数的区别在于,
crosstab
的对应位置传入的是具体的序列,而pivot_table
传入的是被调用表对应的名字,若传入序列对应的值则会报错。
除了默认状态下的count
统计,所有的聚合字符串和返回标量的自定义函数都是可用的,例如统计对应组合的身高均值:
pd.crosstab(index = df.School, columns = df.Transfer, values = df.Height, aggfunc = 'mean')
【练一练】
前面提到了crosstab
的性能劣于pivot_table
,请选用多个聚合方法进行验证。
解:
1.比较count
的性能:
%timeit -n 30 pd.crosstab(index = df.School, columns = df.Transfer, values = [0]*df.shape[0], aggfunc = 'count')
6.33 ms ± 277 µs per loop (mean ± std. dev. of 7 runs, 30 loops each)%timeit -n 30 df.pivot_table(index = 'School',columns = 'Transfer',values = 'Name',aggfunc = 'count')
5.74 ms ± 130 µs per loop (mean ± std. dev. of 7 runs, 30 loops each)
pivot_table
胜出。
2.比较mean
的性能:
%timeit -n 30 pd.crosstab(index = df.School, columns = df.Transfer, values = df.Height, aggfunc = 'mean')
6.25 ms ± 252 µs per loop (mean ± std. dev. of 7 runs, 30 loops each)%timeit -n 30 df.pivot_table(index = 'School',columns = 'Transfer',values = 'Height', aggfunc = 'mean')
5.77 ms ± 218 µs per loop (mean ± std. dev. of 7 runs, 30 loops each)
pivot_table
胜出。
3.比较max
的性能:
%timeit -n 30 pd.crosstab(index = df.School, columns = df.Transfer, values = df.Height, aggfunc = 'max')
6.96 ms ± 1.53 ms per loop (mean ± std. dev. of 7 runs, 30 loops each)%timeit -n 30 df.pivot_table(index = 'School',columns = 'Transfer',values = 'Height', aggfunc = 'max')
5.94 ms ± 193 µs per loop (mean ± std. dev. of 7 runs, 30 loops each)
pivot_table
胜出。
4.比较min
的性能:
%timeit -n 30 pd.crosstab(index = df.School, columns = df.Transfer, values = df.Height, aggfunc = 'min')
6.93 ms ± 588 µs per loop (mean ± std. dev. of 7 runs, 30 loops each)%timeit -n 30 df.pivot_table(index = 'School',columns = 'Transfer',values = 'Height', aggfunc = 'min')
6.19 ms ± 255 µs per loop (mean ± std. dev. of 7 runs, 30 loops each)
pivot_table
胜出。
2. explode
explode
参数能够对某一列的元素进行纵向的展开,被展开的单元格必须存储list, tuple, Series, np.ndarray
中的一种类型。
e.g.
df_ex = pd.DataFrame({'A': [[1, 2], 'my_str', {1, 2}, pd.Series([3, 4])],'B': 1})
df_ex
df_ex.explode('A')
3. get_dummies
get_dummies
是用于特征构建的重要函数之一,其作用是把类别特征转为指示变量。
例如,对年级一列转为指示变量,属于某一个年级的对应列标记为1,否则为0:
pd.get_dummies(df.Grade).head()
这在统计分析前处理数据设置哑变量可太好用了!!!!
四、练习
Ex1:美国非法药物数据集
现有一份关于美国非法药物的数据集,其中SubstanceName, DrugReports
分别指药物名称和报告数量:
df = pd.read_csv('data/drugs.csv').sort_values(['State','COUNTY','SubstanceName'],ignore_index=True)
df.head(3)
- 将数据转为如下的形式:
解:
df1 = df.pivot(index=['State','COUNTY','SubstanceName'], columns='YYYY', values='DrugReports')
df1
df2 = df1.reset_index().rename_axis(columns={'YYYY':''})
df2
2. 将第1问中的结果恢复为原表。
解:
df2_melted = df2.melt(id_vars = ['State','COUNTY','SubstanceName'],value_vars = df2.columns[-8:],var_name = 'YYYY',value_name = 'DrugReports').dropna(subset=['DrugReports'])
df2_melted
df2_melted = df2_melted[df.columns].sort_values(['State','COUNTY','SubstanceName'],ignore_index=True).astype({'YYYY':'int64', 'DrugReports':'int64'})
df2_melted
df2_melted.equals(df)True
3.按State
分别统计每年的报告数量总和,其中State, YYYY
分别为列索引和行索引,要求分别使用pivot_table
函数与groupby+unstack
两种不同的策略实现,并体会它们之间的联系。
解:
- 使用
pivot_table
函数:
df3 = df.pivot_table(index='YYYY', columns='State', values='DrugReports', aggfunc='sum')
df3
- 使用
groupby+unstack
:
df4 = df.groupby(['State', 'YYYY'])['DrugReports'].sum().to_frame().unstack(0).droplevel(0,axis=1)
df4
Ex2:特殊的wide_to_long方法
从功能上看,melt
方法应当属于wide_to_long
的一种特殊情况,即stubnames
只有一类。请使用wide_to_long
生成melt
一节中的df_melted
。(提示:对列名增加适当的前缀)
df = pd.DataFrame({'Class':[1,2],'Name':['San Zhang', 'Si Li'],'Chinese':[80, 90],'Math':[80, 75]})
df
解:
目标变为:
具体操作:
先把索引变成多级索引压缩后的样式:
df = df.rename(columns={'Chinese':'qianzhui_Chinese', 'Math':'qianzhui_Math'})
df5 = pd.wide_to_long(df, stubnames=['qianzhui'],i = ['Class', 'Name'],j = 'Subject',sep = '_',suffix = '.+')
df5
df6 = df5.reset_index().rename(columns={'qianzhui':'Grade'})
df6
按照学科排序:
df6.sort_values(['Subject'])
系统重温Pandas笔记:(五)变形相关推荐
- 系统重温Pandas笔记:(十)时序数据
文章目录 写在前面 一.时序中的基本对象 二.时间戳 1. Timestamp的构造与属性 2. Datetime序列的生成 [练一练] 3. dt对象 4. 时间戳的切片与索引 三.时间差 1. T ...
- 系统重温Pandas笔记:(七)缺失数据
文章目录 写在前面 一.缺失值的统计和删除 1. 缺失信息的统计 2. 缺失信息的删除 二.缺失值的填充和插值 1. 利用fillna进行填充 [练一练] 2. 插值函数 三.Nullable类型 1 ...
- 论文阅读笔记(五)——狐猴识别系统:一种便于狐猴个体识别的面部识别系统
论文阅读笔记(五)--狐猴识别系统:一种便于狐猴个体识别的面部识别系统 论文简介 论文中文翻译:狐猴识别系统:一种便于狐猴个体识别的面部识别系统 论文名称:<LemurFaceID: a fac ...
- [系统安全] 四十五.APT系列(10)Metasploit后渗透技术信息收集、权限提权和功能模块详解
您可能之前看到过我写的类似文章,为什么还要重复撰写呢?只是想更好地帮助初学者了解病毒逆向分析和系统安全,更加成体系且不破坏之前的系列.因此,我重新开设了这个专栏,准备系统整理和深入学习系统安全.逆向分 ...
- 后台系统可扩展性学习笔记
整理了一下笔记目录,将以前学习的一些知识串联起来了,比如cdn.负载均衡.中间件,以前只是各自了解了一点,现在大概理清了后台系统设计中他们各自的作用. 后台系统可扩展性学习笔记(一)概要 后台系统可扩 ...
- Linux系统详解 第五篇:Linux的安装-4:Fedora 16的安装
Linux系统详解 第五篇:Linux的安装-4:Fedora 16的安装 前言: 本系列文章取材广泛,有来自于互联网的,有来自教科书的,有来自自己的笔记的,也有来自自己对Linux的经验积累的.此系 ...
- ROS学习笔记五:理解ROS topics
ROS学习笔记五:理解ROS topics 本节主要介绍ROS topics并且使用rostopic和rqt_plot命令行工具. 例子展示 roscore 首先运行roscore系列服务,这是使用R ...
- 《MFC游戏开发》笔记五 定时器和简单动画
本系列文章由七十一雾央编写,转载请注明出处. http://blog.csdn.net/u011371356/article/details/9332377 作者:七十一雾央 新浪微博:http:// ...
- Pandas进阶伍 变形
Pandas进阶伍 变形 pandas进阶系列根据datawhale远昊大佬的joyful pandas教程写一些自己的心得和补充,本文部分引用了原教程,并参考了<利用Python进行数据分析& ...
- Spring Boot 框架学习笔记(五)( SpringSecurity安全框架 )
Spring Boot 框架学习笔记(五) SpringSecurity安全框架 概述 作用 开发示例: 1. 新建项目 2. 引入依赖 3. 编写`SecurityConfig`类,实现认证,授权, ...
最新文章
- 别骂了,拼多多不挣钱(Doge)
- CentOS7 64位下 MySQL5.7的安装与配置(YUM)
- [推荐算法]ItemCF,基于物品的协同过滤算法
- hmi开发软件c语言,组态,HMI,软件,VC++,源代码
- Unity——Shader
- 永辉发布元宵数据:汤圆销售明显提升,多个民生产品增长超150%
- java注册系统服务_奇葩需求:springboot项目注册为windows系统服务并设置开机自启...
- PHP利用memcache缓存技术提高响应速度
- Atitit 常见dj机功能 目录 1.1. PLAY/PAUSE:播放及暂停,这个和普通的音乐播放设备的功能都是一样的。	1 1.2. Direction:FWD正方向转动、REV反方向转动,音乐
- FFmpeg 转码压缩
- time stamp convert
- MacBook 管理员账户无法删除 解决方法
- 听课记录高中计算机,高中语文听课记录
- 如何让OpenwrtX86和win7双系统共存在一块硬盘
- 阿里云服务器租用费用清单表(CPU内存带宽磁盘)
- python通用管理系统_Python实例---CRM管理系统分析180331
- OSG中使用png图片显示透明效果
- mysql安装后,连接报错
- office 2007 1706错误解决办法
- 计算机辅助编程可分为,东大18春学期《计算机辅助数控编程》在线作业123【辅导资料100分】...
热门文章
- 中通快递api,中通快递一件代发api,中通快递礼品商城api,中通快递空包api
- Python面试题目:输入某年某月某日,判断这一天是这一年的第几天?
- java分发器 及(注解 + 反射机制)—————— 开开开山怪
- maven的pom出现cannot reconnect错误
- 自学Java之基础篇——使用switch,输出今天到底是礼拜几(case的穿透)
- 星期几计算函数参考 周几计算 礼拜几计算函数 C语言 C++函数参考 用公式计算 通用函数 Linux开发函数 Windows开发函数参考
- C语言递归函数 写一个程序实现一个函数PrintN,使得传入一个正整数为N的参数后,能顺序打印从1到N的全部正整数
- 1960-征战的Loy
- 奈奎斯特与香农定理_大神带你理解奈奎斯特定理和香农定理
- 通用输入/输出接口 GPIO