这一个知识点感觉是目前接触的Pandas中最难的了,故写篇博客记录一下,这一节有点函数式编程的味道~

(一)groupby

先说一下goupby,顾名思义,就是分组的意思,给你一个DataFrame,以某一列为标准,分成若干个“子DataFrame”,这些个“子DataFram”由两部分组成,一个是索引index,即类别,一个是“子DataFrame”的内容,数据类型也是DataFrame,不过行数少点罢了,说白了,就是把那一列相同类别的所有行单独提出来,凑成一个DataFrame,该列有N种类别就有N个“子DataFrame”

下面说的所有例子,都遵循这个图

顺序为 1. 分组 -> 2. 对每个子数据帧进行某种操作,并返回操作后的子数据帧 -> 3. 将返回后的子数据帧进行合并

先看一个例子

df = pd.DataFrame({'Country':['China','China', 'India', 'India', 'America', 'Japan', 'China', 'India'],'Income':[10000, 10000, 5000, 5002, 40000, 50000, 8000, 5000],'Age':[5000, 4321, 1234, 4010, 250, 250, 4500, 4321]})
print(df)#    Country  Income   Age
# 0    China   10000  5000
# 1    China   10000  4321
# 2    India    5000  1234
# 3    India    5002  4010
# 4  America   40000   250
# 5    Japan   50000   250
# 6    China    8000  4500
# 7    India    5000  4321

上面是一个DataFrame,以后用数据帧来称呼DataFrame

现在根据国家来分类,可想而知,结果应该有四个子数据帧

groups属性返回一个字典,key为刚才说的索引index,value为子数据帧

print(df.groupby('Country'))
# <pandas.core.groupby.generic.DataFrameGroupBy object at 0x000001F536989888>print(df.groupby('Country').groups)
# {'America': Int64Index([4], dtype='int64'),
# 'China': Int64Index([0, 1, 6], dtype='int64'),
# 'India': Int64Index([2, 3, 7], dtype='int64'),
# 'Japan': Int64Index([5], dtype='int64')}

groupby返回的对象是可迭代的,因此可以用迭代的方法去遍历输出它,迭代的方式和上面说的不谋而合,输出的就是索引index和data子数据帧

# 遍历该对象输出
for index, data in df.groupby('Country'):print(index)print(data)# America
#    Country  Income  Age
# 4  America   40000  250
# China
#   Country  Income   Age
# 0   China   10000  5000
# 1   China   10000  4321
# 6   China    8000  4500
# India
#   Country  Income   Age
# 2   India    5000  1234
# 3   India    5002  4010
# 7   India    5000  4321
# Japan
#   Country  Income  Age
# 5   Japan   50000  250

当然,我们也可以按多个列为标准进行分组,如下

# 根据国家和收入分组
for (index1, index2), data in df.groupby(['Country', 'Income']):print(index1, index2)print(data)# America 40000
#    Country  Income  Age
# 4  America   40000  250
# China 8000
#   Country  Income   Age
# 6   China    8000  4500
# China 10000
#   Country  Income   Age
# 0   China   10000  5000
# 1   China   10000  4321
# India 5000
#   Country  Income   Age
# 2   India    5000  1234
# 7   India    5000  4321
# India 5002
#   Country  Income   Age
# 3   India    5002  4010
# Japan 50000
#   Country  Income  Age
# 5   Japan   50000  250

(二)aggregate 聚合

下面来讲聚合操作

上面说到,分组之后返回多个子数据帧,如果我想知道每个子数据帧的某些列的某些信息,如方差,极差,最值等,就可以用这个聚合操作

aggregate(函数 / lambda表达式 / 函数列表 / 字典) -> return 一个数(即标量,注意这里是标量,而不是向量),然后各个子数据帧都变成一行,再合并

(1)当传入函数的时候,这个函数的形参为每个子数据帧的所有列,可以这么理解,相当于这个函数对每个子数据帧的每一列都进行了相同操作,这个操作指的是函数体本身的内容,因此这个函数传进来的是一个列向量,这个列向量为“每个子数据帧的每一列”。返回的结果是一个数,因此,在用了这个函数之后,每个子数据帧的每一列,都会变成一个数。

def add(x):return np.max(x)print(df.groupby('Country').agg(add))or
print(df.groupby('Country').agg(np.max))

(2)lambda表达式即匿名函数,道理和(1)相同,不再赘述。

print(df.groupby('Country').agg(lambda : np.max(x)))

(3)如果传入的是函数列表,假设每个列表里面装了M个函数,代表要对每个子数据帧的每一列,进行M个操作,每个操作也是返回一个数。因此,以这种方式用了聚合函数之后,每个子数据帧的每一列,都会变成M个数。

print(df.groupby('Country').aggregate([np.max, np.min, np.mean]))

(4)传入的字典,key为列名,value为函数 / 函数列表,这样做的好处是不用对每一列都进行操作,有时候我们只关心某几列,因此这样就能对某几列进行某些操作。

print(df.groupby('Country').aggregate({'Age' : np.max, np.sum}))
print(df.groupby('Country').aggregate({'Age' : [np.max, np.sum]}))

(5)当然,因为函数传入的是列向量,我们当然可以认为的选择我们要的列向量,再进行聚合,如下,下述方法可以代替传入字典的方法,更加直观,高效。

print(df.groupby('Country')['Age'].aggregate(np.max))

(6)如果只对某一个分组感兴趣,可以用get_group单独获取你想要的那个子数据帧,再对其进行操作。

print(df.groupby('Country').get_group('China').aggregate({'Income' : [np.mean, np.sum]}))
#             Income
# mean   9333.333333
# sum   28000.000000

(7)此外,这个aggregate的输出,列为所有列,只不过会把分类的那一列提到第一列的位置上,剩下的顺着排。

(三)transform

这个函数和aggregate函数有点类似,但是又不完全一样,我们来看看

transform(函数 / lambda表达式) -> 返回一个列向量,该列向量和原子数据帧等长,然后各个子数据帧还是和原来一样的行数,再合并,合并之后的新数据帧和原始数据帧等行数

(1)这个函数只能传入函数和匿名函数,这个函数的形参和aggregate一样,也是每个子数据帧的每个列,但是输出的却是向量。因此这个用了transform函数之后,每个子数据帧返回每个子数据帧原有的行数,而上面的聚合就不一样了,上面的聚合每个子数据帧最后都只能返回一行,因为聚合函数传入的每一列都只能返回一个数。比如下面这个代码,transform就不报错,aggregate就报错。因为transorm的函数,传入的是列向量,返回的也是等长的列向量,所以不报错,而aggregate本应该返回一个数,他也返回一个列向量,注定是出错的。

print(df.groupby('order').transform(lambda x : x - np.mean(x)))
# print(df.groupby('order').aggregate(lambda x : x - np.mean(x)) # 报错

(2)如果要返回标量呢?如果每个子数据帧的每一列都返回一个标量,那么这个标量会广播,广播成一个列向量,填充成和原子数据帧一样行数的列向量。

(3)和聚合一样,这里函数传入的依然是列向量,因此可以指定对哪几列进行操作

print(df.groupby('order')['ext price'].transform(lambda x : x - np.mean(x)))

(4)transform的输出和aggregate不太一样,因为等行数,所以没

(四)apply

这个用法非常灵活

apply(函数 / lambda表达式) -> 返回标量或者列向量,然后各个子数据帧合并成新的数据帧

(1)apply只能传入函数或者lambda表达式,这个函数和上面的aggregate以及transform有区别,上面两者的函数传入的是每个子数据帧的每一列,而这里的apply的函数,传入的是“每个子数据帧的所有列 / 行”,一般默认为列。对比一下,上面两个函数传入的是“每个子数据帧的每个列”,这个是“每个子数据帧的所有列 / 行(可以理解为整个子数据帧)”,是不一样的仔细体味一下。所以,apply函数可以对多列进行操作,这是上面这两个函数做不到的。

如果是自定义函数 /  自定义lambda,函数传入的是整个子数据帧,那么可以用['列名']的形式选中数据帧的某几行进行操作

def add(x): # 这里的x指的是整个数据帧return x['quantity'] + x['unit price']print(df.groupby('order').apply(add)) # apply的第一个形参是self,传入的是分好后的每组or
print(df.groupby('order').apply(lambda x : x['quantity'] + x['unit price']))

(2)如果传入的是numpy自带的函数,默认axis = 0,即对每一列进行操作,这一点不要搞混,因为numpy中,如果不写axis的话默认是对所有数进操作。

print(df.groupby('order').apply(np.min))

(3)apply返回的结果根据传入函数的返回值决定。

如果传入函数的返回值是标量,则和aggregate很类似,只不过aggregate最后把分类的那一列提到第一列,而apply是在原来列顺序的基础上,在最前面的一列补充了分类的那一列,即分类的那一列展示了两次。

如果传入函数的返回值是向量,会返回若干个子数据帧,类似这样

print(df.groupby('order').apply(lambda x : x['quantity'] + x['unit price']))# order
# 10001  0      40.69
#        1      32.12
#        2      38.99
# 10005  3     103.82
#        4      34.62
#        5     101.55
#        6     122.91
#        7      61.42
# 10006  8     127.66
#        9      45.55
#        10     75.30
#        11     71.18
# dtype: float64

如果不想要每个子数据帧的index,想把这些子数据帧合并的话,可以将groupby参数中的 group_keys = False

print(df.groupby('order', group_keys = False).apply(lambda x : x['quantity'] + x['unit price']))
# 0      40.69
# 1      32.12
# 2      38.99
# 3     103.82
# 4      34.62
# 5     101.55
# 6     122.91
# 7      61.42
# 8     127.66
# 9      45.55
# 10     75.30
# 11     71.18
# dtype: float64

(4)尽管apply传入的函数的形参不是列向量而是整个子数据帧,但是apply仍能用df.group('XXX')['列名'].apply的形式,可以这么理解,apply传入的是数据帧的所有列,如果这个子数据帧只有一列,那他也是子数据帧啊,所以就算指定列名,传入的列向量也可以看作是子数据帧。

(五)filter

这个有点像SQL里的where,这个filter接在groupby的后面,得到满足条件的那些子数据帧,并且将这些子数据帧合并再返回

filter(函数 / lambda表达式) -> return 得到满足条件的那些子数据帧,并且将这些子数据帧合并再返回

(1)filter里面传入的函数或者lambda表达式,即满足条件,这个函数返回一个布尔表达式,注意这里是“一个”布尔表达式,即要么是True,要么是False,而不是“布尔表达式组成的列表”

这个函数或者lambda表达式的输入形参,和apply一样,都是整个子数据帧(也可以叫做子数据帧的所有列),返回True or False

print(df.groupby('order').filter(lambda x : np.max(x['quantity']) <= 32))

(2)整个filter返回的结果,是那些满足条件的子数据帧,这些子数据帧要么全返回,要么一个都不返回,所以,groupby后面用filter,是挑选“我想要的那些子数据帧”,最小单位为子数据帧,而不是子数据帧里面的某些行,这点要尤其注意!

(3)filter是唯一一个不能用df.group('XXX')['列名'].filter来指定列的函数,因为filter挑选出来的是子数据帧,跟列无关。

Pandas:细说groupby和aggregate、transform、apply以及filter相关推荐

  1. pandas数据分组聚合——groupby()、aggregate()、apply()、transform()和filter()方法详解

    数据分组 数据分组就是根据一个或多个键(可以是函数.数组或df列名)将数据分成若干组,然后对分组后的数据分别进行汇总计算,并将汇总计算后的结果进行合并,被用作汇总计算的函数称为聚合函数.数据分组的具体 ...

  2. pandas分组groupby(agg,transform),apply

    快速浏览 pandas简单介绍和本文说明 一.SAC过程 二.groupby函数 1. groupby分组函数的基本内容 单/多列分组 组容量与组数 组的遍历 level参数(用于多级索引)和axis ...

  3. Pandas数据分析——超好用的Groupby与map、apply、applymap详解

    Groupby详解 在日常的数据分析中,经常需要将数据根据某个(多个)字段划分为不同的群体(group)进行分析,如电商领域将全国的总销售额根据省份进行划分,分析各省销售额的变化情况,社交领域将用户根 ...

  4. Pandas DataFrame GroupBy.Apply

    https://pandas.pydata.org/docs/reference/api/pandas.core.groupby.GroupBy.apply.html?highlight=apply# ...

  5. 理解pandas的groupby().apply()

    在<利用Python进行数据分析>第2章1880-2011年间全美婴儿姓名例子中,有一段代码: def add_prop(group):births = group.births.asty ...

  6. python:dataframe groupby后agg、apply、transfrom用法

    import pandas as pd data = pd.DataFrame({'name':['wencky','stany','barbio','barbio'],'age':[29,29,3, ...

  7. 玩转 Pandas 的 Groupby 操作

    作者:Lemon 来源:Python数据之道 玩转 Pandas 的 Groupby 操作 大家好,我是 Lemon,今天来跟大家分享下 pandas 中 groupby 的用法. Pandas 的 ...

  8. pandas之groupby分组与pivot_table透视

    一.groupby 类似excel的数据透视表,一般是按照行进行分组,使用方法如下. df.groupby(by=None, axis=0, level=None, as_index=True, so ...

  9. Pandas数据分析groupby函数深度总结(1)

    Pandas数据分析groupby函数深度总结(1) groupby分组数据 加载数据 数据分组 按'Sales Rep'列分组 显示所有分组 选择一个特定的组 计算每组中的行数 按'Sales Re ...

最新文章

  1. 浅显易懂 Makefile 入门 (06)— 文件名操作函数(dir、notdir、suffix、basename、addsuffix、addperfix、join、wildcard)
  2. iOS 13 如何删除SceneDelegate
  3. button,submit, image的区别 点onclick后隐藏行
  4. 第六章-Hadoop优化与发展
  5. php写入大文件内容_用PHP读取超大文件的实例代码
  6. Delphi第三方组件安装DCU.PAS.DPK.BPL.ActiveX控件
  7. Spark SQL之案例实战(四)
  8. LeetCode MySQL 1543. Fix Product Name Format(trim去空格+upper/lower大小写)
  9. 计算机中隐藏的文件找不到了怎么办,隐藏文件夹找不到了怎么办【图解】
  10. [转]Win XP常遇网络故障分析:局域网问题
  11. python英文情绪识别_Python能识别文字情绪?
  12. Oracle数据库管理(一):创建和删除数据库
  13. 实验 3 熟悉常用的 HDFS 操作
  14. python分割图片数字_python实现图片中文字分割效果
  15. 硬件知识:电源开关上的“1“和“0“分别是什么意思
  16. SQL语句(增删改查)
  17. 你必须知道的家庭急救常识
  18. 网站白名单可行性分析
  19. oracle数据库系统学习,Oracle数据库学习第二天
  20. Remote Development Tips and Tricks

热门文章

  1. USTC信院集群使用
  2. linux虚拟机如何创建文件夹,Linux虚拟机命令如何创建新的文件和文件夹
  3. 爬取猫眼电影《一出好戏》数据并分析
  4. PostgreSQL如何限制用户查看函数内容
  5. 双系统删除其中一个系统
  6. MCS-51模拟量输入输出
  7. verilog将100mhz分频为1hz_使用verilog语言实现分频器 将50MHZ分为1hz和5hz
  8. [深度学习论文笔记]A Volumetric Transformer for Accurate 3D Tumor Segmentation
  9. 施密特触发器原理图解详细分析
  10. Mac升级系统后SecureCRT意外退出