前言
身边有许多正在学习 Python 的 pandas 库做数据处理的小伙伴们都遇到一个问题——分组聚合。
网上很多这方面的资料,几乎都是列出一系列诸如 "xx方法不能用 Python 内置函数" 之类的规则。小伙伴都说记不住啊。
本文尝试把内部原理机制教会你,让你无需记忆这么多死板的规则即可灵活运用。
本文主要涉及的函数和要的:
groupby
apply
agg
transform
总结这些函数的特点,说明解决思路。
原创不容易,请
数据
本文大部分例子的数据,如下图定义:
分组
物以类聚,人以群分。数据处理时同样需要按类别分组处理,面对这样的高频功能需求, pandas 中提供 groupby 方法进行分组。
按 class 进行分组 如下图的代码:
17-19行,两行的写法是一样的。
注意一点,只是调用 groupby 方法,没有进行任何的处理,只返回一个迭代器。
行21,只有当你需要数据时,才会真正执行分组的运算
返回结果是一个元组(key,每个组的记录的DataFrame)。
你还可以传入具体的数据,他实际会按你传入的数据的值进行分组。
怎么处理这些组?
分组只是处理的第一步,一般来说,我们不应该用遍历去处理每个组。
在pandas中,为我们提供了一些聚合方法用于处理组数据。
apply
apply 只是一种对每个分组进行处理的通用方式。来看看流程动图:
apply 方法中传入一个用于处理的方法。
apply 会把每个分组以一个DataFrame的形式,传入处理方法的首个参数中。
因此,为什么很多文章说,apply 不能使用 python 内置函数,实际是 python 内置函数不能处理 DataFrame 而已。
apply 在处理的最后一步,把每个分组的处理结果合并成一个 DataFrame 返回。
apply 中还可以传入自定义函数,比如我们希望用 value 减去 age 。如下:
注意一点,每个分组的处理结果同样可以是一个多行的 DataFrame 。
合并后,由于同个分组有多行数据,为了区别开来,合并结果的索引部分会带上数据源的索引。
有时候,自定义函数也需要额外的参数。 比如,希望返回 value 列减去指定值的新列
在调用 apply 时,传入命名参数值即可。
因为自定义首个参数是 DataFrame ,因此可以指定列表名,以此针对某列进行处理。
agg
agg 的处理流程与 apply 基本一致。当注意 agg 的处理函数的首个参数是 Series。
注意,处理函数是分别处理每个字段(Series)。
因此,不要在自定义函数中指定字段。
为什么很多文章说 agg 可以使用 python 内置函数,就是因为 python 内置函数可以处理 Series 。
下面是 agg 的自定义函数例子。
transform
当我们需要分组处理的中间结果,但不需要分组后的结果,则可以使用 transform 。 看其流程机制:
transform 是为了保持结果的记录行数与原数据保持一致。
transform 流程机制与 agg 几乎一样。区别在于最后的合并。
如果 transform 的处理函数返回是一个值,那么为了与原数据行数保持一致,因此会把组内的值在组内复制(广播)。
transform 的处理函数还可以返回一个列(也就是有多行),但必须要求最终合并结果与原数据行数一致。
返回的结果不会出现分组的 key 字段。
看起来 transform 有不少规则需要记住。其实记住2点即可。
transform 是为了保持结果的记录行数与原数据保持一致
处理函数的首个参数是字段(Series)
至于处理函数可以返回什么东西,完全是遵从点1。
特点
即使你学会了上述的知识点,但当你遇上问题时,还是会觉得无从入手。因为没有归纳他们的的特点。我们一起来看看。
groupby 分组本质上是为了按某个组别分别处理。而分组处理的结果无非3种:
结果会被压缩。比如原数据有100行2个组,分组后的结果就只有2行了。
结果保持原样。比如希望用每行的年龄减去所在组的平均年龄。处理结果还是100行,只是中间过程需要分组的计算结果。
结果部分被压缩。比如,求出每组的 top 2 的人选。
针对 apply,agg,transform 的特点可以归纳如下:
如果需要按组压缩结果,那么首选考虑 agg 。
如果需要保持原样,那么考虑 transform 和 apply 。
如果需要部分被压缩,比如 top n 问题,那么考虑使用 apply 。
例子
例子1:使用本文的例子数据,如果 value 存在缺失值则用组内均值填充。
从所需结果的情况分析,是完全保持原样,因此选用 transform 。
一般在使用 transform 时,在 groupby 之后指定一列。
自定义函数中可以很容易求得 value 的均值。
例子2:使用本文的例子数据,以 value 列为标准,得出每个分组的 top 2的人。
这是部分被压缩的需求,因此选用 apply 。
自定义函数中的首个参数是整块分组的数据,因此可以进行任意字段排序。然后进行选取返回即可。