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

在两个DataFrame中匹配多值并提取目标列(Python/Pandas)

解决DataFrame多字段匹配失败的方案

一、先统一关键匹配字段的格式

匹配失败的核心原因是phone/email/poc_lastname的格式或内容不统一,先做标准化清洗:

1. 处理Phone字段

df2的phone是科学计数法格式,df1是纯数字字符串/整数,统一转成无格式的字符串:

import pandas as pd

# 处理df1的phone:转字符串,替换NaN为空值
df1['phone'] = df1['phone'].astype(str).replace('nan', '')
# 处理df2的phone:先转整数去除科学计数法,再转字符串
df2['phone'] = df2['phone'].apply(lambda x: str(int(x)) if pd.notna(x) else '')

2. 处理Email字段

邮箱大小写不敏感,可能存在前后空格,统一标准化:

# 转小写、去空格、替换NaN为空值
df1['email'] = df1['email'].str.lower().str.strip().fillna('')
df2['email'] = df2['email'].str.lower().str.strip().fillna('')

3. 处理poc_lastname字段

姓名存在大小写、空格差异,统一转小写并去空格:

df1['poc_lastname'] = df1['poc_lastname'].str.lower().str.strip().fillna('')
df2['poc_lastname'] = df2['poc_lastname'].str.lower().str.strip().fillna('')

二、多字段精准匹配

清洗完成后,用三个字段作为匹配键执行merge,提取目标列:

# 以poc_lastname/phone/email为键合并,保留df2的所有行,匹配df1的affiliate_code
merged_df = pd.merge(
    df2,
    df1[['affiliate_code', 'poc_lastname', 'phone', 'email']],
    on=['poc_lastname', 'phone', 'email'],
    how='left'
)

# 提取需要的列,过滤掉未匹配到的行(可选)
result = merged_df[['affiliate_code', 'poc_lastname', 'phone', 'email']].dropna(subset=['affiliate_code'])

三、模糊匹配处理拼写差异

如果仍有匹配不上的行(比如姓名拼写错误:Sweat vs Sweet,邮箱前缀差异),可以用模糊匹配补充:

1. 姓名模糊匹配(依赖fuzzywuzzy库)

先安装依赖:pip install fuzzywuzzy python-Levenshtein

from fuzzywuzzy import process

# 给df2匹配相似度达标的姓氏
def match_lastname(lastname, df1_lastnames, threshold=80):
    match = process.extractOne(lastname, df1_lastnames)
    return match[0] if match[1] >= threshold else None

df2['matched_lastname'] = df2['poc_lastname'].apply(lambda x: match_lastname(x, df1['poc_lastname'].unique()))

# 用匹配后的姓氏+phone+email合并
merged_fuzzy = pd.merge(
    df2,
    df1[['affiliate_code', 'poc_lastname', 'phone', 'email']],
    left_on=['matched_lastname', 'phone', 'email'],
    right_on=['poc_lastname', 'phone', 'email'],
    how='left'
)

2. 邮箱模糊匹配

拆分邮箱前缀和域名,针对前缀做模糊匹配:

from fuzzywuzzy import fuzz

# 拆分邮箱前缀和域名
def split_email(email):
    if '@' in email:
        return email.split('@', 1)
    return '', ''

df1[['email_prefix', 'email_domain']] = df1['email'].apply(split_email).apply(pd.Series)
df2[['email_prefix', 'email_domain']] = df2['email'].apply(split_email).apply(pd.Series)

# 匹配同域名下前缀相似度达标的记录
def find_affiliate(row, df1):
    candidates = df1[df1['email_domain'] == row['email_domain']]
    if len(candidates) == 0:
        return None
    match_scores = candidates.apply(lambda x: fuzz.ratio(x['email_prefix'], row['email_prefix']), axis=1)
    best_idx = match_scores.idxmax()
    return candidates.loc[best_idx, 'affiliate_code'] if match_scores.max() >= 85 else None

df2['affiliate_code'] = df2.apply(find_affiliate, df1=df1, axis=1)

四、验证匹配结果

查看未匹配的行,针对性调整规则:

# 输出未匹配的记录,分析原因
unmatched = merged_df[merged_df['affiliate_code'].isna()]
print(unmatched)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 07:25:20