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
相关产品推荐
相关产品推荐

