为何Pandas无法处理超550条记录的DataFrame匹配任务?
解决Pandas对比大数据量Excel文件找唯一记录的问题
我需要从包含约4万条记录的Total.xlsx中,找出仅存在于该文件中的约4千条记录,对比对象是包含约3.6万条记录的Recent.xlsx。我原本使用的双重循环Pandas代码仅能处理最多550条记录,处理完整文件或超过500条记录的文件时无法正常运行,尝试分块处理也无效。
原代码
import pandas as pd file1 = 'Total.xlsx' df1 = pd.read_excel(file1) file2 = 'Recent.xlsx' df2 = pd.read_excel(file2) non_matching_rows = [] for index1, row1 in df1.iterrows(): row_matches = False for index2, row2 in df2.iterrows(): if row1.equals(row2): row_matches = True break if not row_matches: non_matching_rows.append(row1) non_matching_df = pd.DataFrame(non_matching_rows) display(non_matching_df) print(non_matching_df.count())
原代码的问题
这段代码的核心问题是双重逐行循环:
- 时间复杂度为O(n*m),4万条记录乘以3.6万条记录意味着144亿次循环操作,完全超出合理计算时间范围。
iterrows()本身是低效率的遍历方式,无法利用Pandas的向量化运算优势,大数据量下会导致内存占用过高或直接卡死。
高效解决方案
以下两种方法均基于Pandas的向量化操作,能在几秒到几十秒内完成大数据量对比:
方法一:使用merge的匹配标记(推荐)
通过合并两个DataFrame并添加匹配状态标记,直接筛选仅存在于Total.xlsx的记录:
import pandas as pd # 读取两个Excel文件 df_total = pd.read_excel('Total.xlsx') df_recent = pd.read_excel('Recent.xlsx') # 左连接合并,添加_merge列标记匹配状态 merged_df = df_total.merge(df_recent, how='left', indicator=True) # 筛选仅存在于Total的记录,删除标记列 only_in_total = merged_df[merged_df['_merge'] == 'left_only'].drop(columns=['_merge']) # 输出结果 display(only_in_total) print(only_in_total.count())
- 优势:完全基于Pandas的高效向量化合并逻辑,内存占用可控,处理4万+3.6万级别的数据毫无压力。
方法二:利用isin结合行元组集合
将Recent.xlsx的行转换为元组集合,通过集合快速判断Total.xlsx的行是否存在:
import pandas as pd df_total = pd.read_excel('Total.xlsx') df_recent = pd.read_excel('Recent.xlsx') # 将Recent的所有行转换为元组,存入集合(集合的查询是O(1)时间复杂度) recent_row_set = set(df_recent.apply(tuple, axis=1)) # 筛选Total中不在Recent集合里的行 only_in_total = df_total[~df_total.apply(tuple, axis=1).isin(recent_row_set)] display(only_in_total) print(only_in_total.count())
- 注意:如果DataFrame包含非哈希able类型的列(如列表),此方法会报错,此时优先使用方法一。
分块处理的正确姿势(若内存不足)
如果Excel文件过大导致内存不足,可以分块读取Recent.xlsx并逐步筛选:
import pandas as pd df_total = pd.read_excel('Total.xlsx') # 标记所有行初始为未匹配 df_total['is_unique'] = True # 分块读取Recent.xlsx,每次处理1万条 chunk_size = 10000 for chunk in pd.read_excel('Recent.xlsx', chunksize=chunk_size): # 找出当前块中与Total匹配的行 matched = df_total.merge(chunk, how='inner') # 将匹配到的行标记为非唯一 df_total.loc[df_total.index.isin(matched.index), 'is_unique'] = False # 筛选仅存在于Total的行 only_in_total = df_total[df_total['is_unique']].drop(columns=['is_unique']) display(only_in_total) print(only_in_total.count())
内容的提问来源于stack exchange,提问作者Subhash-23
相关产品推荐
相关产品推荐

