百万行43列DataFrame:替代iterrows()的反向行对高效删除方案
问题
处理100万行、43列的DataFrame时,现有代码运行极慢。需求是:找出特定列值相同且CA列值互为相反数的行对,删除这类反向行对。
示例说明
假设有DataFrame df:
| Column A | Column B | Column C | Column D |
|---|---|---|---|
| 'Brown' | 'Bottle' | 1234555 | 100 |
| 'yellow' | 'Cup' | 1234555 | 80 |
| 'Red' | 'Bottle' | 1234555 | -100 |
| 'Red' | 'Bottle' | 1234555 | -100 |
| 'Brown' | 'Bottle' | 1234533 | 100 |
若指定列B和列C进行匹配,需删除第1行和第3行(二者列B、列C值相同,列D值互为相反数),最终输出:
| Column A | Column B | Column C | Column D |
|---|---|---|---|
| 'yellow' | 'Cup' | 1234555 | 80 |
| 'Red' | 'Bottle' | 1234555 | -100 |
| 'Brown' | 'Bottle' | 1234533 | 100 |
低效代码(使用iterrows())
df_dupes = data[data.duplicated(subset = criteria_, keep=False)] df_dupes_list = np.array(df_dupes.to_numpy().tolist()) df_1 = df_dupes_list[:,[0,1,7,9,8,23,35]] df_2 = df_1.tolist() for i, row in df_dupes.iterrows(): if row.ConvertedAUD < 0 and [row.BA, row.OA, row.BN, row.DN, row.DT,row.D, -row.CA] in df_2: try: c = np.where((data['BA'] ==row.BA) & (data['OA'] ==row.OA) & (data['BN'] ==row.BN)& (data['DT'] ==row.DT)& (data['DN'] ==row.DN)& (data['D'] ==row.D)& (data['CA'] ==-row.CA))[0][0] data.drop(labels=[i,data.index.values[c]], axis=0, inplace=True) except: pass
高效解决方案
核心思路是用Pandas向量化操作替代逐行循环,避免iterrows()的低效问题,具体步骤如下:
1. 标记匹配组
按指定匹配列(对应代码中的BA, OA, BN, DN, DT, D)分组,同时为每行记录CA列的绝对值,方便后续查找相反数对。
2. 统计每组内正负值数量
对每个匹配组,分别统计CA为正、为负的行数,确定可抵消删除的行对数量。
3. 生成待删除行索引
- 对有可删除对的组,取出CA为正和为负的行索引
- 按两组中更少的行数选取对应数量的行对,标记为待删除项
- 去重避免重复删除同一行
代码实现
import pandas as pd # 定义匹配列和目标CA列 match_cols = ['BA', 'OA', 'BN', 'DN', 'DT', 'D'] ca_col = 'CA' # 1. 生成匹配键+CA绝对值的组合,保留原始索引 data['abs_ca'] = data[ca_col].abs() group_key = match_cols + ['abs_ca'] # 2. 分组统计正负CA的数量 group_stats = data.groupby(group_key).agg( pos_count=pd.NamedAgg(column=ca_col, aggfunc=lambda x: (x > 0).sum()), neg_count=pd.NamedAgg(column=ca_col, aggfunc=lambda x: (x < 0).sum()) ).reset_index() # 3. 计算每组可删除的行对数量 group_stats['delete_pairs'] = group_stats[['pos_count', 'neg_count']].min(axis=1) # 4. 筛选出有可删除对的组 valid_groups = group_stats[group_stats['delete_pairs'] > 0] # 5. 收集待删除的索引 to_delete = [] for _, row in valid_groups.iterrows(): # 构建当前组的筛选条件 mask = (data[match_cols] == row[match_cols]).all(axis=1) & (data['abs_ca'] == row['abs_ca']) group_data = data[mask] # 取出对应数量的正、负CA行索引 pos_indices = group_data[group_data[ca_col] > 0].index[:row['delete_pairs']] neg_indices = group_data[group_data[ca_col] < 0].index[:row['delete_pairs']] to_delete.extend(pos_indices) to_delete.extend(neg_indices) # 6. 去重后删除行,清理临时列 to_delete = list(set(to_delete)) data_cleaned = data.drop(to_delete).drop(columns=['abs_ca'])
效率提升原因
- 全程使用Pandas向量化分组、聚合操作,比逐行迭代快数倍甚至数十倍
- 仅在处理分组统计结果时用了一次循环,循环次数远小于原始数据行数
- 避免了原代码中
np.where全局查找、列表in判断这类高耗时操作
内容的提问来源于stack exchange,提问作者bbaba
相关产品推荐
相关产品推荐

