本文介绍: 深入理解Pandas groupby

BaseGroupBy类中有一个grouper属性,是ops.BaseGrouper类型,但BaseGroupBy类没有__init__方法,因此进入GroupBy类,该类重写父类grouper属性,在__init__方法调用grouper.pyget_grouper,下面是抽取出来的伪代码

groupby.py文件

class GroupBy(BaseGroupBy[NDFrameT]):
	grouper: ops.BaseGrouper
	
	def __init__(self, ...):
		# ...
		if grouper is None:
			from pandas.core.groupby.grouper import get_grouper
			grouper, exclusions, obj = get_grouper(...)

grouper.py文件

def get_grouper(...) -> tuple[ops.BaseGrouper, frozenset[Hashable], NDFrameT]:
	# ...
	# create the internals grouper
    grouper = ops.BaseGrouper(
        group_axis, groupings, sort=sort, mutated=mutated, dropna=dropna
    )
	return grouper, frozenset(exclusions), obj

class Grouping"""
	obj : DataFrame or Series
	"""
	def __init__(
        self,
        index: Index,
        grouper=None,
        obj: NDFrame | None = None,
        level=None,
        sort: bool = True,
        observed: bool = False,
        in_axis: bool = False,
        dropna: bool = True,
    ):
    	pass

ops.py文件

class BaseGrouper:
    """
    This is an internal Grouper class, which actually holds
    the generated groups
    
    ......
    """
    def __init__(self, axis: Index, groupings: Sequence[grouper.Grouping], ...):
    	# ...
    	self._groupings: list[grouper.Grouping] = list(groupings)
    
    @property
    def groupings(self) -> list[grouper.Grouping]:
        return self._groupings

BaseGrouper中包含了最终生成分组信息,是一个list,其中的元素类型为grouper.Grouping,每个分组对应一个Grouping,而Grouping中的obj对象分组后的DataFrame或者Series

在第一部分写了一个函数展示groupby返回对象这里再来探究一下原理,对于可迭代对象,会实现__iter__()方法,先定位到BaseGroupBy的对应方法

class BaseGroupBy:
	grouper: ops.BaseGrouper
	
	@final
    def __iter__(self) -> Iterator[tuple[Hashable, NDFrameT]]:
        return self.grouper.get_iterator(self._selected_obj, axis=self.axis)

接下来进入BaseGrouper类中

class BaseGrouper:
    def get_iterator(
        self, data: NDFrameT, axis: int = 0
    ) -> Iterator[tuple[Hashable, NDFrameT]]:
        splitter = self._get_splitter(data, axis=axis)
        keys = self.group_keys_seq
        for key, group in zip(keys, splitter):
            yield key, group.__finalize__(data, method="groupby")

Debug模式进入group.finalize()方法发现返回的确实是DataFrame对象
BaseGroupBy的__iter__()方法Debug详情

三、4大函数

有了上面的基础,接下来再看groupby之后的处理函数,就简单多了

agg

聚合操作groupby后最常见的操作,常用来数据分析
比如,要查看不同category分组最大值,以下三种写法可以实现,并且grouped.aggregate和grouped.agg完全等价,因为在SelectionMixin类中有这样的定义:agg = aggregate
在这里插入图片描述
但是要聚合多个字短时,就只能用aggregate或者agg了,比如要获取不同category分组下price最大,count最小的记录
在这里插入图片描述
可以结合numpy里的聚合函数

import numpy as np
grouped.agg({'price': np.max, 'count': np.min})

常见的聚合函数如下

聚合函数 功能
max 最大值
mean 平均值
median 中位数
min 最小值
sum 求和
std 标准差
var 方差
count 计数

其中,count在numpy中对应的调用方式为np.size

transform

现在需要新增一列price_mean,展示每个分组的平均价格

transform函数刚好可以实现这个功能,在指定分组上产生一个与原df相同索引的DataFrame,返回与原对象有相同索引且已填充了转换后的值的DataFrame,然后可以转换结果新增到原来的DataFrame上
示例代码如下

grouped = df.groupby('category', sort=False)
df['price_mean'] = grouped['price'].transform('mean')
print(df)

输出结果如下
在这里插入图片描述

apply

现在需要获取各个分组下价格最高的数据调用apply可以实现这个功能apply可以传入任意自定义的函数,实现复杂的数据操作

from pandas import DataFrame
grouped = df.groupby('category', as_index=False, sort=False)

def get_max_one(the_df: DataFrame):
    sort_df = the_df.sort_values(by='price', ascending=True)
    return sort_df.iloc[-1, :]
max_price_df = grouped.apply(get_max_one)
max_price_df

输出结果如下
在这里插入图片描述

filter

filter函数可以对分组后数据做进一步筛选,该函数在每一个分组内,根据筛选函数排除不满足条件数据返回一个新的DataFrame

假设现在要把平均价格低于4的分组排除掉,根据transform小节的数据,会把蔬菜分类过滤掉

grouped = df.groupby('category', as_index=False, sort=False)
filtered = grouped.filter(lambda sub_df: sub_df['price'].mean() > 4)
print(filtered)

输出结果如下
在这里插入图片描述

四、总结

groupby的过程就是将原有的DataFrame/Series按照groupby的字段划分为若干个分组DataFrame/Series,分成多少个组就有多少个分组DataFrame/Series。因此,在groupby之后的一系列操作(如agg、apply等),均是基于子DataFrame/Series的操作。理解了这点,就理解了Pandas中groupby操作的主要原理

五、参考文档

Pandas官网关于pandas.DateFrame.groupby的介绍
Pandas官网关于pandas.Series.groupby的介绍

原文地址:https://blog.csdn.net/u013481793/article/details/127158683

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。

如若转载,请注明出处:http://www.7code.cn/show_6931.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注