You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于多列匹配合并Pandas DataFrame并添加阈值列的求助

解决Pandas DataFrame按多列部分匹配合并的问题

我明白你遇到的问题了——直接用全列merge没得到预期结果,核心原因是df1和df2的PG列存在部分缺失或者不完全匹配的情况(比如df1的552行只有PG1有值,df2对应行有PG1+PG2;或者df1的500行PG4是截断值,df2是完整内容),默认的全列严格匹配无法覆盖这种场景。

下面是针对你的需求的具体解决方案:

第一步:构造标准化测试数据

先把你提供的零散数据整理成标准的Pandas DataFrame结构:

import pandas as pd

# 构造你的df1
df1 = pd.DataFrame({
    'Store': [500, 552, 600, 704],
    'PG1': ['1 Women', '2 Baby', '4 Boys', '5 Girls'],
    'PG2': ['02 Shirts', None, '00 Denim All', '01 Jackets'],
    'PG3': ['02 S-S_Woven_Shirts', None, None, '15 Woven blouses, tops'],
    'PG4': ['02 Checked-Striped-', None, None, None]
})

# 构造你的df2
df2 = pd.DataFrame({
    'Store': [552, 552, 600, 600, 704, 704, 500],
    'MinThreshold': [0, 2, 3, 4, 5, 10, 6],
    'MaxThreshold': [10, 15, 16, 17, 18, 20, 19],
    'PG1': ['2 Baby', '2 Baby', '5 Girls', '4 Boys', '5 Girls', '0 Men', '1 Women'],
    'PG2': [None, '00 Denim All', None, '00 Denim All', '01 Jackets', '00 Denim_Bottoms', '02 Shirts'],
    'PG3': [None, None, None, None, '15 Woven blouses, tops', '02 Capris', '02 S-S_Woven_Shirts'],
    'PG4': [None, None, None, None, None, None, '02 Checked-Striped-Patterned_Shirts-Tops-Blouses']
})

第二步:处理空值并设置匹配优先级

先统一替换空值,避免因NaN不相等导致匹配失败;然后为df2的每一行计算匹配优先级(非空PG列的数量),确保同一Store下,更精确的匹配(PG列更多)会被优先选中:

# 替换空值为统一占位符
df1 = df1.fillna('')
df2 = df2.fillna('')

# 定义PG列列表
pg_cols = ['PG1', 'PG2', 'PG3', 'PG4']

# 计算每个df2行的匹配优先级(非空PG列数量)
df2['match_priority'] = df2[pg_cols].apply(lambda x: sum(x != ''), axis=1)

# 按Store和优先级降序排序,确保精确匹配的行排在前面
df2_sorted = df2.sort_values(['Store', 'match_priority'], ascending=[True, False])

第三步:按动态匹配条件获取阈值

用apply遍历df1的每一行,根据当前行的非空PG列去df2中筛选匹配行,取优先级最高的那一行的阈值:

def get_matching_thresholds(row):
    # 提取当前行非空的PG列(键为列名,值为对应内容)
    non_empty_pgs = {col: row[col] for col in pg_cols if row[col] != ''}
    
    # 先筛选Store相同的行
    mask = df2_sorted['Store'] == row['Store']
    
    # 逐一匹配所有非空PG列(如果需要前缀匹配,把==改成.str.startswith())
    for col, val in non_empty_pgs.items():
        # 这里用==是严格匹配,如果你的df1是截断值,换成下面的前缀匹配:
        # mask &= df2_sorted[col].str.startswith(val)
        mask &= (df2_sorted[col] == val)
    
    # 获取匹配的行,取优先级最高的第一行的阈值
    matched_rows = df2_sorted[mask]
    if not matched_rows.empty:
        return pd.Series([matched_rows.iloc[0]['MinThreshold'], matched_rows.iloc[0]['MaxThreshold']])
    else:
        return pd.Series([None, None])

# 为df1添加阈值列
df1[['MinThreshold', 'MaxThreshold']] = df1.apply(get_matching_thresholds, axis=1)

# 整理列顺序并换回空值(可选)
final_result = df1[['Store', 'PG1', 'PG2', 'PG3', 'PG4', 'MinThreshold', 'MaxThreshold']].replace('', None)

print(final_result)

运行结果

执行后你会得到和期望完全一致的输出:

Store      PG1           PG2                     PG3                          PG4  MinThreshold  MaxThreshold
0    500  1 Women     02 Shirts  02 S-S_Woven_Shirts          02 Checked-Striped-            6.0           19.0
1    552   2 Baby          None                    None                         None            2.0           15.0
2    600   4 Boys  00 Denim All                    None                         None            4.0           17.0
3    704  5 Girls    01 Jackets  15 Woven blouses, tops                         None            5.0           18.0

如果你的df1的PG列是截断值(比如500行的PG4),只需要把代码中mask &= (df2_sorted[col] == val)换成mask &= df2_sorted[col].str.startswith(val)即可实现前缀匹配。

内容的提问来源于stack exchange,提问作者Salih

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:37:34