文章目录

  • 写在前面
  • 一、长宽表的变形
    • 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,因此原表中的indexcolumns对应两个列的行组合必须唯一。

pandas1.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)

  1. 将数据转为如下的形式:

    解:
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笔记:(五)变形相关推荐

  1. 系统重温Pandas笔记:(十)时序数据

    文章目录 写在前面 一.时序中的基本对象 二.时间戳 1. Timestamp的构造与属性 2. Datetime序列的生成 [练一练] 3. dt对象 4. 时间戳的切片与索引 三.时间差 1. T ...

  2. 系统重温Pandas笔记:(七)缺失数据

    文章目录 写在前面 一.缺失值的统计和删除 1. 缺失信息的统计 2. 缺失信息的删除 二.缺失值的填充和插值 1. 利用fillna进行填充 [练一练] 2. 插值函数 三.Nullable类型 1 ...

  3. 论文阅读笔记(五)——狐猴识别系统:一种便于狐猴个体识别的面部识别系统

    论文阅读笔记(五)--狐猴识别系统:一种便于狐猴个体识别的面部识别系统 论文简介 论文中文翻译:狐猴识别系统:一种便于狐猴个体识别的面部识别系统 论文名称:<LemurFaceID: a fac ...

  4. [系统安全] 四十五.APT系列(10)Metasploit后渗透技术信息收集、权限提权和功能模块详解

    您可能之前看到过我写的类似文章,为什么还要重复撰写呢?只是想更好地帮助初学者了解病毒逆向分析和系统安全,更加成体系且不破坏之前的系列.因此,我重新开设了这个专栏,准备系统整理和深入学习系统安全.逆向分 ...

  5. 后台系统可扩展性学习笔记

    整理了一下笔记目录,将以前学习的一些知识串联起来了,比如cdn.负载均衡.中间件,以前只是各自了解了一点,现在大概理清了后台系统设计中他们各自的作用. 后台系统可扩展性学习笔记(一)概要 后台系统可扩 ...

  6. Linux系统详解 第五篇:Linux的安装-4:Fedora 16的安装

    Linux系统详解 第五篇:Linux的安装-4:Fedora 16的安装 前言: 本系列文章取材广泛,有来自于互联网的,有来自教科书的,有来自自己的笔记的,也有来自自己对Linux的经验积累的.此系 ...

  7. ROS学习笔记五:理解ROS topics

    ROS学习笔记五:理解ROS topics 本节主要介绍ROS topics并且使用rostopic和rqt_plot命令行工具. 例子展示 roscore 首先运行roscore系列服务,这是使用R ...

  8. 《MFC游戏开发》笔记五 定时器和简单动画

    本系列文章由七十一雾央编写,转载请注明出处. http://blog.csdn.net/u011371356/article/details/9332377 作者:七十一雾央 新浪微博:http:// ...

  9. Pandas进阶伍 变形

    Pandas进阶伍 变形 pandas进阶系列根据datawhale远昊大佬的joyful pandas教程写一些自己的心得和补充,本文部分引用了原教程,并参考了<利用Python进行数据分析& ...

  10. Spring Boot 框架学习笔记(五)( SpringSecurity安全框架 )

    Spring Boot 框架学习笔记(五) SpringSecurity安全框架 概述 作用 开发示例: 1. 新建项目 2. 引入依赖 3. 编写`SecurityConfig`类,实现认证,授权, ...

最新文章

  1. 别骂了,拼多多不挣钱(Doge)
  2. CentOS7 64位下 MySQL5.7的安装与配置(YUM)
  3. [推荐算法]ItemCF,基于物品的协同过滤算法
  4. hmi开发软件c语言,组态,HMI,软件,VC++,源代码
  5. Unity——Shader
  6. 永辉发布元宵数据:汤圆销售明显提升,多个民生产品增长超150%
  7. java注册系统服务_奇葩需求:springboot项目注册为windows系统服务并设置开机自启...
  8. PHP利用memcache缓存技术提高响应速度
  9. Atitit 常见dj机功能 目录 1.1. PLAY/PAUSE:播放及暂停,这个和普通的音乐播放设备的功能都是一样的。 1 1.2. Direction:FWD正方向转动、REV反方向转动,音乐
  10. FFmpeg 转码压缩
  11. time stamp convert
  12. MacBook 管理员账户无法删除 解决方法
  13. 听课记录高中计算机,高中语文听课记录
  14. 如何让OpenwrtX86和win7双系统共存在一块硬盘
  15. 阿里云服务器租用费用清单表(CPU内存带宽磁盘)
  16. python通用管理系统_Python实例---CRM管理系统分析180331
  17. OSG中使用png图片显示透明效果
  18. mysql安装后,连接报错
  19. office 2007 1706错误解决办法
  20. 计算机辅助编程可分为,东大18春学期《计算机辅助数控编程》在线作业123【辅导资料100分】...

热门文章

  1. 中通快递api,中通快递一件代发api,中通快递礼品商城api,中通快递空包api
  2. Python面试题目:输入某年某月某日,判断这一天是这一年的第几天?
  3. java分发器 及(注解 + 反射机制)—————— 开开开山怪
  4. maven的pom出现cannot reconnect错误
  5. 自学Java之基础篇——使用switch,输出今天到底是礼拜几(case的穿透)
  6. 星期几计算函数参考 周几计算 礼拜几计算函数 C语言 C++函数参考 用公式计算 通用函数 Linux开发函数 Windows开发函数参考
  7. C语言递归函数 写一个程序实现一个函数PrintN,使得传入一个正整数为N的参数后,能顺序打印从1到N的全部正整数
  8. 1960-征战的Loy
  9. 奈奎斯特与香农定理_大神带你理解奈奎斯特定理和香农定理
  10. 通用输入/输出接口 GPIO