Pandas中将MultiIndex Series映射回DataFrame的问题求解
问题背景
我需要从较大的DataFrame中生成合并报表,目前遇到MultiIndex Series映射回DataFrame的问题,单索引Series的映射已经可以正常实现。希望明确使用DealNum、FlowDate两列作为groupby的判断条件,再通过这两列将生成的Series映射回原DataFrame,确保单个DealNum对应Interest、Premium两类FlowType的多个FlowDate时映射结果准确。曾尝试使用pivot_table实现,但在更大的数据集下部分场景效果不佳,询问正确实现方式和更优思路。
问题代码
报错的代码行如下:
report['Interest-FlowDate-Daily_PnL'] = report[['DealNum','FlowDate']].map(interest_flowdate_series)
完整可复现代码
import pandas as pd data = { 'DealNum': [1,1,1,2,2,2,3,3,3,4,4,4,5,5,5,6,6,6], 'FlowType': ['Interest', 'Premium', 'Fees', 'Interest', 'Premium', 'Fees', 'Interest', 'Premium', 'Fees', 'Interest', 'Premium', 'Fees', 'Interest', 'Premium', 'Fees', 'Interest', 'Premium', 'Fees'], 'FlowDate': ['12/31/2021', '12/31/2021', '1/10/2021', '11/30/2021', '11/30/2021', '2/8/2021', '1/31/2022', '1/31/2022', '5/10/2021', '2/28/2022', '2/28/2022', '7/28/2021', '12/15/2021', '12/15/2021', '12/15/2021', '6/30/2022', '6/30/2022', '11/10/2021',], 'Daily_PnL': [10,0,-2.25, 30,0,-1.50, 100,0,-3.50, 200,0,-2.50, 300,0,-4.50, 0,-150,-1.50,], 'LTD_PnL': [1000,-150,-102.25, 3000,0,-101.50, 1400,0,-103.50, 2400,0,-102.50, 4000,0,-104.50, 0,-150,-1.50,] } df = pd.DataFrame(data) # 仅保留Interest、Premium类型对应的FlowDate生成基础报表 report = df[(df.FlowType == 'Interest') | (df.FlowType == 'Premium')][['DealNum','FlowDate']].drop_duplicates().reset_index(drop=True) # 按DealNum、FlowDate分组计算Interest类型的Daily_PnL总和,生成MultiIndex Series interest_flowdate_series = df[df.FlowType == 'Interest'].groupby(['DealNum','FlowDate']).sum()['Daily_PnL'] report['Interest-FlowDate-Daily_PnL'] = report[['DealNum','FlowDate']].map(interest_flowdate_series) print(interest_flowdate_series) print(report)
报错原因
原始代码的问题在于:map方法仅支持对单列Series使用,直接对多列DataFrame调用map会触发异常。
实现方案
以下3种可行实现按大数据集下的性能优先级排序:
方案1:merge方法(最优,推荐大数据量使用)
pandas的merge做了高度优化,且扩展性极强,后续需要新增Premium等其他类型的指标时,只要新增对应merge逻辑即可:
# 先计算Interest的分组聚合结果,直接转成DataFrame interest_df = df[df.FlowType == 'Interest'].groupby(['DealNum','FlowDate'], as_index=False)['Daily_PnL'].sum().rename(columns={'Daily_PnL':'Interest-FlowDate-Daily_PnL'}) # 左连接关联到report表,自动按两列匹配 report = report.merge(interest_df, on=['DealNum','FlowDate'], how='left')
方案2:MultiIndex重索引方法
如果已经有现成的MultiIndex Series,可以用重索引的方式直接匹配,性能也优于逐行处理:
report['Interest-FlowDate-Daily_PnL'] = interest_flowdate_series.reindex(pd.MultiIndex.from_frame(report[['DealNum','FlowDate']])).to_numpy()
方案3:元组转换+map方法(仅适合小数据集)
把两列转成元组的单值Series之后再调用map,性能较差,数据量超过10万行不推荐:
report['Interest-FlowDate-Daily_PnL'] = report[['DealNum','FlowDate']].apply(tuple, axis=1).map(interest_flowdate_series)
内容的提问来源于stack exchange,提问作者Arun Batta
相关产品推荐
相关产品推荐

