Pandas高级GroupBy技术详解

引言

虽然 groupby().sum()groupby().mean() 用于快速检查没有问题,但生产级别的指标需要更健壮的解决方案。现实世界的表格通常涉及多个键、时间序列数据、权重以及各种条件,如促销、退货或异常值。

这意味着你经常需要计算总额和比率、在每个细分市场中对项目进行排名、按日历时段汇总数据,然后将分组统计数据合并回原始行以进行建模。本文将指导你使用Pandas库中的高级分组技术来有效处理这些复杂场景。

选择正确的模式

使用 agg 将组缩减为一行

当需要每组一条记录(例如总计、均值、中位数、最小值/最大值和自定义向量化聚合)时,使用 agg

out = (df.groupby(['store', 'cat'], as_index=False, sort=False).agg(sales=('rev', 'sum'),orders=('order_id', 'nunique'),avg_price=('price', 'mean'))
)

这适用于关键绩效指标(KPI)表、每周汇总和多指标摘要。

使用 transform 将统计量广播回各行

transform 方法返回一个与输入形状相同的结果。它非常适合创建每行需要的特征,例如Z分数、组内份额或分组填充。

g = df.groupby('store')['rev']
df['rev_z'] = (df['rev'] - g.transform('mean')) / g.transform('std')
df['rev_share'] = df['rev'] / g.transform('sum')

这适用于建模特征、质量保证比率和插补。

使用 apply 实现自定义的组内逻辑

仅当所需逻辑无法用内置函数表达时,才使用 apply。它速度较慢且难以优化,因此应首先尝试使用 aggtransform

def capped_mean(s):q1, q3 = s.quantile([.25, .75])return s.clip(q1, q3).mean()df.groupby('store')['rev'].apply(capped_mean)

这适用于定制规则和小型分组。

使用 filter 保留或丢弃整个组

filter 方法允许整个组通过或失败某个条件。这对于数据质量规则和阈值设定非常方便。

big = df.groupby('store').filter(lambda g: g['order_id'].nunique() >= 100)

这适用于最小规模队列,以及在聚合前移除稀疏类别。

多键分组和命名聚合

按多个键分组

你可以控制输出形状和顺序,以便结果可以直接放入商业智能工具。

g = df.groupby(['store', 'cat'], as_index=False, sort=False, observed=True)
  • as_index=False 返回一个扁平的DataFrame,更容易连接和导出
  • sort=False 避免重新排序分组,在顺序无关紧要时可以节省工作量
  • observed=True(与分类列一起使用)会丢弃未使用的类别组合

使用命名聚合

命名聚合会产生可读的、类似SQL的列名。

out = (df.groupby(['store', 'cat']).agg(sales=('rev', 'sum'),orders=('order_id', 'nunique'),    # 在这里使用你的ID列avg_price=('price', 'mean'))
)

整理列

如果堆叠多个聚合,你将得到一个多级索引(MultiIndex)。将其扁平化一次并标准化列的顺序。

out = out.reset_index()
out.columns = ['_'.join(c) if isinstance(c, tuple) else cfor c in out.columns
]
# 可选:确保业务友好的列顺序
cols = ['store', 'cat', 'orders', 'sales', 'avg_price']
out = out[cols]

无需 apply 的条件聚合

在 agg 中使用布尔掩码数学

当掩码依赖于其他列时,通过其索引对齐数据。

# 按 (store, cat) 的促销销售额和促销率
cond = df['is_promo']
out = df.groupby(['store', 'cat']).agg(promo_sales=('rev', lambda s: s[cond.loc[s.index]].sum()),promo_rate=('is_promo', 'mean')  # 促销行的比例
)

计算比率和比例

比率简单地等于 sum(mask) / size,这等价于布尔列的平均值。

df['is_return'] = df['status'].eq('returned')
rates = df.groupby('store').agg(return_rate=('is_return', 'mean'))

创建队列式窗口

首先,使用日期边界预计算掩码,然后聚合数据。

# 示例:每位客户队列首次购买后30天内的重复购买
first_ts = df.groupby('customer_id')['ts'].transform('min')
within_30 = (df['ts'] <= first_ts + pd.Timedelta('30D')) & (df['ts'] > first_ts)# 客户队列 = 首次购买的月份
df['cohort'] = first_ts.dt.to_period('M').astype(str)repeat_30_rate = (df.groupby('cohort').agg(repeat_30_rate=('within_30', 'mean')).rename_axis(None)
)

每组的加权指标

实现加权平均模式

向量化数学计算,并防范零权重的除法。

import numpy as nptmp = df.assign(wx=df['price'] * df['qty'])
agg = tmp.groupby(['store', 'cat']).agg(wx=('wx', 'sum'), w=('qty', 'sum'))# 按 (store, cat) 的加权平均价格
agg['wavg_price'] = np.where(agg['w'] > 0, agg['wx'] / agg['w'], np.nan)

安全地处理 NaN 值

决定为空组或全NaN值返回什么。两种常见选择是:

# 1) 返回 NaN(透明,对于下游统计最安全)
agg['wavg_price'] = np.where(agg['w'] > 0, agg['wx'] / agg['w'], np.nan)# 2) 如果所有权重为零,则回退到未加权平均值(明确的策略)
mean_price = df.groupby(['store', 'cat'])['price'].mean()
agg['wavg_price_safe'] = np.where(agg['w'] > 0, agg['wx'] / agg['w'], mean_price.reindex(agg.index).to_numpy()
)

时间感知分组

使用带频率的 pd.Grouper

通过将时间序列数据分组到特定间隔来尊重KPI的日历边界。

weekly = df.groupby(['store', pd.Grouper(key='ts', freq='W')], observed=True).agg(sales=('rev', 'sum'), orders=('order_id', 'nunique')
)

对每个分组应用滚动/扩展窗口

始终先对数据进行排序,并基于时间戳列对齐。

df = df.sort_values(['customer_id', 'ts'])
df['rev_30d_mean'] = (df.groupby('customer_id').rolling('30D', on='ts')['rev'].mean().reset_index(level=0, drop=True)
)

避免数据泄漏

保持时间顺序,并确保窗口只能“看到”过去的数据。不要打乱时间序列数据,也不要在为训练和测试分割数据集之前计算全数据集的组统计量。

组内排名和前N名

查找每组的前k行

以下是选择每组前N行的两种实用选项。

# 排序 + head
top3 = (df.sort_values(['cat', 'rev'], ascending=[True, False]).groupby('cat').head(3))# 对单一指标进行组内 nlargest
top3_alt = (df.groupby('cat', group_keys=False).apply(lambda g: g.nlargest(3, 'rev')))

使用辅助函数

Pandas提供了几个用于排名和选择的辅助函数。

  • rank — 控制如何处理并列情况(例如,method='dense''first'),并可以使用 pct=True 计算百分位排名。
    df['rev_rank_in_cat'] = df.groupby('cat')['rev'].rank(method='dense', ascending=False)
    
  • cumcount — 提供每行在其组内的基于0的位置。
    df['pos_in_store'] = df.groupby('store').cumcount()
    
  • nth — 在不排序整个DataFrame的情况下,选取每组中的第k行。
    second_row = df.groupby('store').nth(1)  # 每家店的第二行(按出现顺序)
    

使用 transform 广播特征

执行组内标准化

在每个组内标准化一个指标,使得不同组间的行具有可比性。

g = df.groupby('store')['rev']
df['rev_z'] = (df['rev'] - g.transform('mean')) / g.transform('std')

插补缺失值

用组统计量填充缺失值。这通常比使用全局填充值更能保持接近现实的分布。

df['price'] = df['price'].fillna(df.groupby('cat')['price'].transform('median'))

创建组内份额特征

将原始数字转换为组内比例,以便进行更清晰的比较。

df['rev_share_in_store'] = df['rev'] / df.groupby('store')['rev'].transform('sum')

处理类别、空组和缺失数据

使用分类类型提高速度

如果你的键来自一个固定的集合(例如,商店、地区、产品类别),将它们转换为分类类型一次。这会使GroupBy操作更快、更节省内存。

from pandas.api.types import CategoricalDtypestore_type = CategoricalDtype(categories=sorted(df['store'].dropna().unique()), ordered=False)
df['store'] = df['store'].astype(store_type)cat_type = CategoricalDtype(categories=['Grocery', 'Electronics', 'Home', 'Clothing', 'Sports'])
df['cat'] = df['cat'].astype(cat_type)

丢弃未使用的组合

在分类列上进行分组时,设置 observed=True 会排除数据中实际未出现的类别组合,从而得到更干净、干扰更少的输出。

out = df.groupby(['store', 'cat'], observed=True).size().reset_index(name='n')

使用 NaN 键进行分组

明确说明如何处理缺失键。默认情况下,Pandas会删除NaN组;仅当它有助于你的质量保证过程时才保留它们。

# 默认:NaN 键被删除
by_default = df.groupby('region').size()# 当你需要审计缺失键时,将 NaN 保留为其自己的组
kept = df.groupby('region', dropna=False).size()

快速备忘单

计算每组的条件比率

# 布尔值的平均值就是比率
df.groupby(keys).agg(rate=('flag', 'mean'))
# 或显式地:sum(mask)/size
df.groupby(keys).agg(rate=('flag', lambda s: s.sum() / s.size))

计算加权平均值

df.assign(wx=df[x] * df[w]).groupby(keys).apply(lambda g: g['wx'].sum() / g[w].sum() if g[w].sum() else np.nan).rename('wavg')

查找每组的前k名

(df.sort_values([key, metric], ascending=[True, False]).groupby(key).head(k))
# 或
df.groupby(key, group_keys=False).apply(lambda g: g.nlargest(k, metric))

计算每周指标

df.groupby([key, pd.Grouper(key='ts', freq='W')], observed=True).agg(...)

执行组内填充

df[col] = df[col].fillna(df.groupby(keys)[col].transform('median'))

计算组内份额

df['share'] = df[val] / df.groupby(keys)[val].transform('sum')

总结

首先,为你的任务选择正确的模式:使用 agg 进行缩减,使用 transform 进行广播,并仅在无法向量化时才保留使用 apply。依靠 pd.Grouper 处理基于时间的时段,依靠排名辅助函数进行前N名选择。通过倾向于清晰、向量化的模式,你可以保持输出扁平化、命名清晰且易于测试,确保你的指标保持正确,你的笔记本运行快速。
更多精彩内容 请关注我的个人公众号 公众号(办公AI智能小助手)或者 我的个人博客 https://blog.qife122.com/
对网络安全、黑客技术感兴趣的朋友可以关注我的安全公众号(网络安全技术点滴分享)

公众号二维码

公众号二维码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/986234.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2025杭州单位保洁哪家强?这份杭州单位保洁公司推荐清单合集

2025杭州单位保洁哪家强?这份杭州单位保洁公司推荐清单合集!对于企业、院校等单位而言,优质的保洁服务不仅能营造整洁舒适的办公与经营环境,提升单位形象,更能为员工与访客的健康提供基础保障。但面对杭州市场上众…

2025杭州石材保养公司推荐:杭州石材保养公司哪家强大盘点

2025杭州石材保养公司推荐:杭州石材保养公司哪家强大盘点!大理石的温润、花岗岩的坚硬、砂岩的质朴,让石材成为杭州众多写字楼、酒店、企业办公区的装饰首选。但石材在长期使用中,难免面临磨损失光、污渍渗透、潮湿返…

2025年下半年内蒙古坑道钻机企业深度评测与选购指南

摘要 2025年下半年,内蒙古坑道钻机行业随着矿业发展持续升温,企业竞争加剧。本文基于第三方调研,提供一份坑道钻机企业推荐榜单,排名仅作参考,不区分先后顺序,旨在帮助用户了解行业概况。榜单包含五家推荐企业,…

2025下半年徐州MPP电力管优质厂商综合推荐指南

摘要 随着城市电网改造和新能源建设的快速发展,MPP电力管作为电力电缆保护的重要材料,在2025年下半年徐州市场的需求持续增长。本文基于行业调研和用户反馈,为您推荐五家优质的MPP电力管厂商,排名不分先后,仅供参…

avascript如何判断是触摸屏还是PC端

avascript如何判断是触摸屏还是PC端在Web开发中,判断设备是触摸屏还是PC端(鼠标操作)通常涉及到检测用户输入的类型。JavaScript提供了几种方式来实现这一功能,以下是一些常见的方法: 1. 使用 ontouchstart 属性 …

2025年下半年内蒙古探水钻机企业综合评测与选购指南

摘要 随着内蒙古矿产资源开发的持续深入,探水钻机作为矿山勘探的核心设备,其市场需求在2025年下半年呈现稳步增长态势。本文基于行业调研数据及用户反馈,为您推荐五家值得关注的探水钻机企业(排名不分先后),重点…

2025年下半年内蒙古探水钻机企业综合推荐指南:十大优质供应商盘点

摘要 随着内蒙古矿产资源开发的不断深入,探水钻机作为矿山勘探的关键设备,其市场需求在2025年下半年持续增长。本文基于行业调研数据,为您推荐十家在该领域表现突出的企业,排名不分先后,仅作参考。特别说明:本推…

杭州拱墅区初中初一初二初三中考辅导机构推荐——德易辅导班李秋老师

随着中考竞争的日益激烈,以及对孩子未来学业长远发展的关注,高效、系统化的考试提分辅导成为杭州家庭的核心考量。然而,市场上补习机构众多,真正的核心能力、提分效果、学习方法论却良莠不齐,家长在选择时常常面临…

2025年下半年内蒙古履带钻机品牌综合推荐与选购指南

摘要 2025年下半年,内蒙古地区的履带钻机行业在矿产资源开发和基础设施建设推动下持续发展,品牌竞争加剧。本文基于第三方调研和用户反馈,整理了五家推荐品牌,并提供简要表单供参考。排名仅为推荐,不区分先后顺序…

2025年下半年内蒙古履带钻机品牌综合评估与选购指南

摘要 随着内蒙古矿产资源开发的持续深入,履带钻机作为矿山勘探的核心装备,其市场需求在2025年下半年呈现稳步增长态势。本文基于行业调研数据,为读者推荐10家在本地区表现优异的履带钻机品牌(排名不分先后),重点…

图片的秘密

将文件放进随波逐流里面用binwalk分离获得一个压缩包,打开压缩包发现这个文件里面有一个pass.txt打开发现秘钥,在返回上一个文件打开word,在打开media将里面的图片解压出来在https://tool.zootu.cn/tools/fun/pixel…

tailscale与macos shadow火箭,chrome浏览器proxyswitchomega并存问题

问题:开magicdns chrome下switchomega direct连接访问谷歌会报证书错误, 这是因为switchomega direct模式下,dns被magicdns接管的,不走火箭的,导致ip错误了(被污染) 。可以:1 关magicdns2 tailscale后台开over…

RSI 2

将文件放进随波逐流里面,用binwalk分离,发现一个压缩包将压缩包里面的文件提取出来,打开文件发现大量数据借用大佬的脚本,将数据转成图片点击查看代码 import matplotlib.pyplot as pltdef validate_coord(coord):…

RSI 1

将文件放进随波逐流里面,使用binwalk分离文件获得一个压缩包,打开压缩包将里面的文件解压出来打开文件获得flag

CVE-2016-7124

CVE-2016-7124复现分析代码,当执行反序列化时触发__wakeup(),导致返回默认页面,但是当序列化字符串中对象属性数量大于实际属性数量时,__wakeup()不会执行,从而绕过。生成payload,将O:8:"xiaorang":1:{…

接口加密

接口加密一、业务场景介绍 数据安全性 - 抓包工具系统明文传输的数据会被不明身份的人用抓包工具抓取,从而威胁系统和数据的安全性。 二、加密方式 1. 摘要算法消息摘要是把任意长度的输入揉和而产生长度固定的信息。…

订单服务(Ordering.API)之领域层

前言 领域层(Ordering.Domain) 1.领域层职责职责 说明 示例1️⃣ 表达领域模型 通过实体(Entity)、值对象(Value Object)建模业务对象及其行为。 Order、OrderItem、Address2️⃣ 封装业务规则 在模型中定义业务不…

订单服务(Ordering.API)之应用层

前言 应用层(Ordering.API) 1.应用层职责职责 说明1️⃣ 编排业务流程(流程协调者) 调用多个领域对象(聚合根、领域服务),协调它们完成一个完整的用例。例如:下单时,依次校验库存、计算价格、保存订单、发布领域…

订单服务(Ordering.API)之基础设施层

前言 基础设施层(Ordering.Infrastructure) 1.基础设施层职责职责类型 说明1️⃣ 持久化实现(Repository 实现) 实现领域层定义的仓储接口(IOrderRepository、IBuyerRepository等),与数据库交互(通常使用 ORM,如…

订单服务(Ordering.API)之表现层

前言 表现层(Ordering.API) 1.表现层职责职责 说明1️⃣ 用户交互接口 提供给用户或外部系统的访问入口,如 Web API、MVC Controller、Razor 页面、Blazor 页面等。2️⃣ 请求验证 对输入数据进行基础校验,如必填字段…