You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

R/Tidyverse用户转用Pandas:实现分组聚合与非聚合操作的优雅方案

Pandas中实现类似Tidyverse的分组Mutate操作

我是R/Tidyverse用户,刚接触Python/Pandas,想知道能不能像在tidyverse里一样优雅实现下面的分组转换操作:

(
    dat
    %>% group_by(grp)
    %>% mutate(
        value = value/max(value)
    )
)

这是个分组mutate操作,核心是用分组内的聚合结果(max(value))对原列做非聚合运算(除法)。我目前知道可以这样实现:

import pandas as pd
import numpy as np

df = pd.DataFrame({'grp': np.random.randint(0,5, 10), 'value': np.random.randn(10)}).sort_values('grp')

tmp = (
    df
    .groupby('grp')
    .agg('max')
)

(
    df
    .merge(tmp,on='grp')
    .assign(
        value = lambda x: x.value_x / x.value_y
    )
)

但我觉得应该能像tidyverse那样,不用创建临时变量tmp,用单表达式完成,是不是有更简洁的写法?


更新:保留额外列的贴近Tidyverse实现

我之前标记了@PaulS的答案为正确,它解决了初始问题,但实际使用中发现Tidyverse有个隐式行为:未参与操作的列会被保留,而之前的方案会丢弃这些列。下面是更贴近Tidyverse行为的示例和解决方案:

首先构造包含额外列的数据集:

df = (
    pd.DataFrame({
        'grp': np.random.randint(0,5, 10), # 分组列
        'time': np.random.normal(0,1,10), # 不参与计算的额外列
        'value': np.random.randn(10) # 计算列
    })
    .sort_values(['grp','time'])
    .reset_index()
)

实现分组后用聚合结果调整列值,同时保留所有列:

(
    df
    .groupby('grp', group_keys=False)
    .apply(
        lambda x: (
            x.assign(
                value = (
                    x.value
                    / x.value.max()
                )
            )
        )
    )
    .reset_index()
    .drop(['index','level_0'],axis=1)
)

进阶:基于复杂索引的分组计算

如果需要在赋值时通过索引获取特定值(比如取time最接近0时对应的value),代码需要稍作调整:

# 用time最接近0时的value值作为分母做除法
(
    df
    .groupby('grp', group_keys=False)
    .apply(
        lambda x: (
            x.assign(
                value = (
                    x.value
                    / x.value.values[np.argmin(np.abs(x.time))] # 注意使用.values[]
                )
            )
        )
    )
    .reset_index()
    .drop(['index','level_0'],axis=1)
)

内容的提问来源于stack exchange,提问作者Mike Lawrence

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 11:20:26