Pandas如何按分组后销售额和最接近目标值规则合并两个DataFrame
解决方案
你可以通过以下步骤实现批量分组匹配:
步骤1:封装最优组合查找函数
把你现有的单组逻辑封装成可复用函数,同时绑定ID和销售额,方便返回匹配的ID组合:
import itertools import math import pandas as pd def find_best_combination(target_sales, sub_df2): # 绑定ID和销售额,避免组合后找不到对应ID id_sales = list(zip(sub_df2['ID'], sub_df2['Sales'])) best_ids = [] min_diff = math.inf best_sum = 0 # 遍历所有长度的组合 for length in range(1, len(id_sales)+1): for comb in itertools.combinations(id_sales, length): current_sum = sum([x[1] for x in comb]) current_diff = abs(target_sales - current_sum) # 更新最优结果 if current_diff < min_diff: min_diff = current_diff best_ids = [x[0] for x in comb] best_sum = current_sum # 差值为0直接跳出,已经是最优解 if min_diff == 0: return best_ids, best_sum return best_ids, best_sum
步骤2:预处理df2并批量处理df1
先把df2按Name、Date分组存储,再遍历df1每行匹配对应分组计算结果:
# 预处理df2,按Name和Date分组生成查询字典 df2_grouped = df2.groupby(['Name', 'Date']) # 遍历df1每行计算结果 result_rows = [] for _, row in df1.iterrows(): name = row['Name'] date = row['Date'] target = row['Total Sales'] # 查找对应分组,不存在则返回空结果 if (name, date) not in df2_grouped.groups: comb_ids = [] comb_total = 0 else: sub_df2 = df2_grouped.get_group((name, date)) comb_ids, comb_total = find_best_combination(target, sub_df2) # 拼接结果行 result_rows.append({ 'Name': name, 'Date': date, 'Total Sales': target, 'Comb IDs': comb_ids, 'Comb Total': comb_total }) # 转成DataFrame得到最终结果 result_df = pd.DataFrame(result_rows)
示例运行结果
针对你给出的测试数据,最终输出的result_df如下:
| Name | Date | Total Sales | Comb IDs | Comb Total |
|---|---|---|---|---|
| John | 2021-10-01 | 15500 | ['JO1', 'JO2'] | 15000 |
| John | 2021-11-01 | 5500 | ['JO4'] | 5500 |
| Jack | 2021-10-10 | 17600 | ['JA1', 'JA2'] | 17000 |
| Nancy | 2021-10-12 | 20700 | ['NA1','NA2','NA3'] | 20600 |
| Ahmed | 2021-10-30 | 12000 | ['AH1', 'AH2'] | 12000 |
优化提示
如果分组内的数据量较大(超过20条),暴力枚举所有组合会出现性能问题,你可以把组合查找逻辑替换为动态规划实现的子集和问题解法,时间复杂度会大幅降低。
内容的提问来源于stack exchange,提问作者Ibrahim Ayoup
相关产品推荐
相关产品推荐

