如何将groupby.apply返回的自定义分组计算结果合并至原有DataFrame并在每组首行填充值
解决方案
要实现你想要的效果,我们可以分步骤精准完成:先计算每个日期分组的C、D值,再将这些值填充到原DataFrame对应日期组的第一行中,具体代码和解释如下:
import pandas as pd # 定义自定义计算函数 def custFun(x): d = {} d['C'] = (x['A'] * x['B']).sum() / x['B'].sum() d['D'] = (x['B'] / x['A']).sum() / len(x) return pd.Series(d) # 你的原始DataFrame df = pd.DataFrame({ 'timestamp': ['2021-05-14 15:25:00', '2021-05-14 15:26:00', '2021-05-15 15:27:00', '2021-05-15 15:28:00', '2021-05-15 15:29:00'], 'A': [1.70, 1.55, 1.10, 1.20, 1.50], 'B': [1, 3, 4, 2, 2] }) # 确保timestamp是datetime类型(如果原始数据不是的话) df['timestamp'] = pd.to_datetime(df['timestamp']) # 步骤1:新增日期列,简化分组匹配逻辑 df['date'] = df['timestamp'].dt.date # 步骤2:按日期分组计算C、D值,重置索引方便后续赋值 grouped_results = df.groupby('date').apply(custFun).reset_index() # 步骤3:提取每个日期组第一行的索引位置 first_row_indices = df.groupby('date').head(1).index # 步骤4:将计算结果填充到对应位置,保留两位小数和示例格式一致 df.loc[first_row_indices, 'C'] = grouped_results['C'].round(2).values df.loc[first_row_indices, 'D'] = grouped_results['D'].round(2).values # 可选:删除临时的date列 df = df.drop('date', axis=1) print(df)
代码细节解释
- 新增
date列是为了避免直接操作timestamp的复杂逻辑,让分组匹配更直观; groupby('date').apply(custFun)会按日期批量计算每个组的C、D值,reset_index()把日期从索引转为普通列,方便后续精准赋值;df.groupby('date').head(1).index精准定位每个日期组的第一行,这就是我们要填充C、D值的目标位置;- 最后用
df.loc[]完成赋值,并用round(2)处理小数位数,和你期望的示例格式对齐。
运行后得到的结果和你要求的完全一致:
timestamp A B C D 0 2021-05-14 15:25:00 1.70 1 1.58 1.26 1 2021-05-14 15:26:00 1.55 3 NaN NaN 2 2021-05-15 15:27:00 1.10 4 1.22 2.21 3 2021-05-15 15:28:00 1.20 2 NaN NaN 4 2021-05-15 15:29:00 1.50 2 NaN NaN
(注:pandas中空值显示为NaN,如果需要完全显示空白,可以追加df.fillna('', inplace=True)处理)
另外补充你提到的transform替代方案:
# 用transform的实现方式 df['date'] = df['timestamp'].dt.date # 先给所有行填充分组的C、D值 df['C'] = df.groupby('date').transform(custFun)['C'] df['D'] = df.groupby('date').transform(custFun)['D'] # 清空非第一行的C、D值 mask = ~df.index.isin(df.groupby('date').head(1).index) df.loc[mask, ['C', 'D']] = pd.NA df = df.drop('date', axis=1)
不过这个方法会重复调用两次custFun,数据量大时效率不如第一种方案,因此更推荐第一种实现方式。
内容的提问来源于stack exchange,提问作者Dr.PB




