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

如何高效比对两个DataFrame中的邮箱列并匹配关联记录?

问题描述

我有两个数据集,各包含邮箱地址和记录ID。需要找出两个数据集中邮箱匹配的记录,提取双方的记录ID、匹配列名及对应值,整合成新数据集输出。目前用嵌套循环实现,但效率极低——当两个数据集各有15万条记录时,预计耗时数千小时。求优化建议。

原实现代码:

import pandas as pd

# Load the first CSV file (contact1_Contacts)
contacts_1_df = pd.read_csv("C:/temp/Contacts1.csv")

# Load the second CSV file (contact2_Contacts)
contacts_2_df = pd.read_csv("C:/temp/contacts2.CSV")


contacts_1_df.dropna(subset=['ContactId'], inplace=True)
contacts_2_df.dropna(subset=['ContactID'], inplace=True)

contacts_1_df.drop(['Full Name', 'Source Ref', 'Last Name', 'First Name', 'Birthdate', 'Mobile Phone', 'Date of Birth'], axis='columns', inplace=True)
contacts_2_df.drop(['Title', 'First Name','Middle Name', 'Surname', 'Maiden Name', 'Known As', 'Gender', 'DOB', 'Deceased?', 'Deceased Date', 'Phone Number', 'Nationality'], axis='columns', inplace=True)

#convert dataframes to lower
contacts_1_df = contacts_1_df.apply(lambda x: x.astype(str).str.lower())
contacts_2_df = contacts_2_df.apply(lambda x: x.astype(str).str.lower())

matches_df = pd.DataFrame(columns = ['Contact1ContactGUID','Contact1ContactEmailType','Contact1ContactEmailValue','Contact2ContactID','Contact2EmailType','Contact2EmailValue'])

contact1_record_count = 0
contact2_record_count = 0


#for each contact1 contact
for contacts_1_index, contact1_row in contacts_1_df[['Preferred Email', 'Company Email', 'Email Address 2', 'Email 3', 'ContactId']].iterrows():
    #variable for columnindex so i can get the column name
    contact1_colindex = 0

    for contact1_email_col_value in contact1_row:

        #increment the column index
        contact1_colindex = contact1_colindex + 1
      
        #dont test for nan values
        if(contact1_email_col_value != 'nan'):

        
            for contact2_index, contact2_row in contacts_2_df[['Email'  ,'Email_1'  ,'Email_2'  ,'Email_3'  ,'Email_4','ContactID']].iterrows():

                #variable to hold the col index so i can get the column name
                contact2_colindex = 0

                for contact2_email in contact2_row:
 
                    #dont test for nan values
                    if(contact2_email != 'nan'):

                        if(contact2_email == contact1_email_col_value):
                            #print('****************MATCH****************')
                            match_row = {'Contact1ContactGUID': contact1_row[4], 'Contact1ContactEmailType': contact1_row.index[contact1_colindex], 'Contact1ContactEmailValue': contact1_email_col_value, 'Contact2ContactID': contact2_row[5], 'Contact2EmailType': contact2_row.index[contact2_colindex], 'Contact2EmailValue': contact2_email }
                            matches_df = pd.concat([matches_df, pd.DataFrame([match_row])], ignore_index=True)

                        contact2_colindex = contact2_colindex + 1


matches_df.to_csv('C:/temp/output.csv', encoding='utf-8', index=False)
优化方案

嵌套循环的时间复杂度是O(nmk*l),完全无法处理十万级数据。改用Pandas的向量化操作和长格式转换,能把运行时间压缩到分钟级。

核心思路

  • 将每个数据集的多列邮箱转成长格式(每行对应一个邮箱+记录ID+邮箱类型),让每个邮箱成为独立行,方便后续合并。
  • 对两个长格式数据集按邮箱值做内连接,直接获取所有匹配结果。
  • 过滤无效的"nan"邮箱值,避免无效匹配。

优化后代码

import pandas as pd

# 加载数据
contacts_1_df = pd.read_csv("C:/temp/Contacts1.csv")
contacts_2_df = pd.read_csv("C:/temp/contacts2.CSV")

# 清理无效记录:仅保留有记录ID的行
contacts_1_df = contacts_1_df.dropna(subset=['ContactId']).copy()
contacts_2_df = contacts_2_df.dropna(subset=['ContactID']).copy()

# 筛选需要的列:记录ID + 所有邮箱列
contact1_email_cols = ['Preferred Email', 'Company Email', 'Email Address 2', 'Email 3']
contact2_email_cols = ['Email', 'Email_1', 'Email_2', 'Email_3', 'Email_4']

contacts_1_df = contacts_1_df[['ContactId'] + contact1_email_cols]
contacts_2_df = contacts_2_df[['ContactID'] + contact2_email_cols]

# 统一转换为小写字符串
contacts_1_df = contacts_1_df.apply(lambda x: x.astype(str).str.lower())
contacts_2_df = contacts_2_df.apply(lambda x: x.astype(str).str.lower())

# --------------------------
# 转换为长格式:每个邮箱单独一行
# --------------------------
# 处理第一个数据集
df1_long = contacts_1_df.melt(
    id_vars=['ContactId'],
    value_vars=contact1_email_cols,
    var_name='Contact1ContactEmailType',
    value_name='Contact1ContactEmailValue'
)
# 过滤空邮箱(nan字符串)
df1_long = df1_long[df1_long['Contact1ContactEmailValue'] != 'nan']

# 处理第二个数据集
df2_long = contacts_2_df.melt(
    id_vars=['ContactID'],
    value_vars=contact2_email_cols,
    var_name='Contact2EmailType',
    value_name='Contact2EmailValue'
)
df2_long = df2_long[df2_long['Contact2EmailValue'] != 'nan']

# --------------------------
# 按邮箱值内连接,得到所有匹配结果
# --------------------------
matches_df = pd.merge(
    df1_long,
    df2_long,
    left_on='Contact1ContactEmailValue',
    right_on='Contact2EmailValue',
    how='inner'
)

# 重命名列以匹配输出需求
matches_df = matches_df.rename(columns={
    'ContactId': 'Contact1ContactGUID',
})

# 调整列顺序为需求格式
output_cols = [
    'Contact1ContactGUID',
    'Contact1ContactEmailType',
    'Contact1ContactEmailValue',
    'ContactID',
    'Contact2EmailType',
    'Contact2EmailValue'
]
matches_df = matches_df[output_cols]

# 输出结果
matches_df.to_csv('C:/temp/output.csv', encoding='utf-8', index=False)

效率提升原因

  • 向量化操作:Pandas的melt和merge都是底层优化的C级实现,比Python循环快几个数量级。
  • 时间复杂度降低:转换长格式是O(n)和O(m),合并操作是O(n+m)(基于哈希表),十万级数据几分钟即可完成。
  • 避免低效操作:原代码每次匹配都用concat追加行,这是极其耗时的操作;新代码直接通过合并生成完整结果集,避免了频繁的内存操作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 21:55:56