多DataFrame按ID/TOD加权求和优化及取最大结果方案咨询
问题描述
我有多个包含ID、TIME_OF_DAY(简称TOD)和数值列的DataFrame,需要为每个DataFrame的数值列赋予对应权重后,按ID与TOD维度做加权求和,示例如下:
权重0.2的DataFrame:
ID TOD M 0 10 morning 1 1 13 afternoon 3 2 32 evening 2 3 10 evening 2权重0.4的DataFrame:
ID TOD W 0 10 morning 1 1 13 morning 3 2 32 afternoon 2 3 10 evening 3加权求和结果:
ID TOD weighed_sum_mw 0 10 morning (0.2*1 + 0.4*1) 1 10 evening (0.2*2 + 0.4*3) 2 13 morning (0.4*3) 3 13 afternoon (0.2*3) 4 32 evening (0.2*2) 5 32 afternoon (0.4*2)
目前用多次合并的方法可行,但内存消耗极高,想找无需合并的实现方案;另外最终只需要保留每个ID对应加权和最大的TOD行,若加权和相同,优先级为Afternoon>Evening>Morning。当前处理4个各约1000万行的DataFrame,后续可能增加数量,现有代码如下:
merged_oc= pd.merge(dfs[0], dfs[3], on=['ID', 'TIME_OF_DAY'], suffixes=('_O', '_C'), how='outer') merged_s = pd.merge(dfs[1], dfs[2], on=['ID', 'TIME_OF_DAY'], suffixes=('_W', 'M'), how='outer') # merge and weighted sum of O and C merged_oc['COUNTS_O_weighted_02']= merged_oc['COUNTS_O'].fillna(0).multiply(0.2) merged_oc['COUNTS_C_weighted_04'] = merged_oc['COUNTS_C'].fillna(0).multiply(0.4) merged_oc['COUNTS'] = merged_oc['COUNTS_O_weighted_02'] + merged_oc['COUNTS_C_weighted_04'] result_oc = merged_oc[['ID', 'TIME_OF_DAY', 'COUNTS', 'COUNTS_O_weighted_02', 'COUNTS_C_weighted_04']] merged_s['COUNTS_W_weighted_04'] = merged_s['COUNTS_W'].fillna(0).multiply(0.4) merged_s['COUNTS_M_weighted_04'] = merged_s['COUNTS_M'].fillna(0).multiply(0.4) merged_s['COUNTS'] = merged_s['COUNTS_W_weighted_04'] + merged_s['COUNTS_M_weighted_04'] result_s = merged_s[['ID', 'TIME_OF_DAY', 'COUNTS', 'COUNTS_W_weighted_04', 'COUNTS_M_weighted_04']] merged_final = pd.merge(result_oc, result_s, on=['ID', 'TIME_OF_DAY'], suffixes=('_OC', '_S'), how='outer') merged_final['COUNTS_OC']= merged_final['COUNTS_OC'].fillna(0) merged_final['COUNTS_S'] = merged_final['COUNTS_S'].fillna(0) merged_final['WEIGHTED_SUM'] = merged_final['COUNTS_OC'] + merged_final['COUNTS_SESSION'] merged_final = merged_final[['ID', 'TIME_OF_DAY', 'WEIGHTED_SUM', 'COUNTS_O_weighted_02', 'COUNTS_C_weighted_04', 'COUNTS_W_weighted_04', 'COUNTS_M_weighted_04']].fillna(0)
解决方案
1. 低内存加权求和方案(避免多次合并)
核心思路是先对单个DataFrame做加权计算,再纵向合并所有处理后的结果,最后按ID和TIME_OF_DAY聚合求和,避免横向合并产生大量空值,大幅降低内存占用:
假设你有一个权重与DataFrame的映射列表,按实际情况调整权重和数值列名:
import pandas as pd # 定义每个DataFrame对应的权重和数值列名 df_weight_map = [ {'df': dfs[0], 'weight': 0.2, 'value_col': 'COUNTS_O'}, {'df': dfs[1], 'weight': 0.4, 'value_col': 'COUNTS_W'}, {'df': dfs[2], 'weight': 0.4, 'value_col': 'COUNTS_M'}, {'df': dfs[3], 'weight': 0.4, 'value_col': 'COUNTS_C'}, ] processed_dfs = [] for item in df_weight_map: # 复制必要列,避免修改原DataFrame temp_df = item['df'][['ID', 'TIME_OF_DAY', item['value_col']]].copy() # 计算加权值,空值填0 temp_df['weighted_value'] = temp_df[item['value_col']].fillna(0) * item['weight'] # 重命名原数值列,保留加权明细 temp_df.rename(columns={item['value_col']: f"{item['value_col']}_weighted_{str(item['weight']).replace('.', '')}"}, inplace=True) processed_dfs.append(temp_df) # 纵向合并所有处理后的DataFrame combined_df = pd.concat(processed_dfs, ignore_index=True) # 按ID和TIME_OF_DAY聚合,求和加权值及各明细项 final_sum_df = combined_df.groupby(['ID', 'TIME_OF_DAY'], as_index=False).sum() # 提取总加权和 final_sum_df['WEIGHTED_SUM'] = final_sum_df['weighted_value']
2. 筛选每个ID的最优TOD行
按规则筛选每个ID的最优行:优先按WEIGHTED_SUM降序,加权和相同时按TOD优先级排序:
# 定义TOD优先级映射,数值越小优先级越高 tod_priority = {'afternoon': 0, 'evening': 1, 'morning': 2} # 添加优先级列 final_sum_df['tod_rank'] = final_sum_df['TIME_OF_DAY'].map(tod_priority) # 排序:先按ID升序,再按WEIGHTED_SUM降序,最后按tod_rank升序 final_sum_df.sort_values(by=['ID', 'WEIGHTED_SUM', 'tod_rank'], ascending=[True, False, True], inplace=True) # 每个ID只保留第一行(即最优行) result = final_sum_df.groupby('ID').first().reset_index() # 按需移除辅助列 result.drop('tod_rank', axis=1, inplace=True)
方案优势
- 内存效率高:纵向合并+分组求和的方式,避免了横向合并产生的大量空值,内存占用仅为原合并方案的几分之一
- 扩展性强:后续新增DataFrame时,只需在
df_weight_map中添加对应配置即可,无需修改核心逻辑 - 计算速度快:分组求和的运算效率远高于多次合并操作
内容的提问来源于stack exchange,提问作者greenteam
相关产品推荐
相关产品推荐

