高效移除Pandas DataFrame中重复的子DataFrame
高效移除Pandas DataFrame中重复的子DataFrame
嘿,针对你遇到的这个要高效移除重复子DataFrame的问题,我这里有个性能拉满的解决方案,比你原来的循环方法快太多了,尤其适合百万级别的大数据量!
先回顾下你的示例数据和需求:
你有这样一个DataFrame:
import pandas as pd df_dupl = pd.DataFrame({ 'EVENT_TIME': ['00:01', '00:01', '00:01', '00:03', '00:03', '00:03', '00:06', '00:06', '00:06', '00:08', '00:08', '00:10', '00:10', '00:11', '00:11', '00:13', '00:13', '00:13'], 'UNIQUE_ID': [123, 123, 123, 125, 125, 125, 123, 123, 123, 127, 127, 123, 123, 123, 123, 123, 123, 123], 'Value1': ['A', 'B', 'A', 'A', 'B', 'A', 'A', 'B', 'A', 'A', 'B', 'A', 'B', 'C', 'B', 'A', 'B', 'A'], 'Value2': [0.3, 0.2, 0.2, 0.1, 1.3, 0.2, 0.3, 0.2, 0.2, 0.1, 1.3, 0.3, 0.2, 0.3, 0.2, 0.3, 0.2, 0.2] })
你的核心需求是:按UNIQUE_ID分组后,只保留和上一个同ID的EVENT_TIME子DataFrame不重复的那些子DataFrame——比如00:06的123子DataFrame和00:01的完全一致,所以要删掉;但00:13的虽然也和00:01一致,但中间隔了00:10、00:11的子DataFrame,所以要保留。
高效解决方案(无NaN场景)
这个方法用Pandas的分组、移位和向量化操作,完全避免了循环,性能提升非常明显:
# 定义需要比较的数值列 value_cols = ['Value1', 'Value2'] # 给每个(UNIQUE_ID, EVENT_TIME)组内的行分配序号,用来对齐前后组的行做比较 enums = df_dupl.groupby(['UNIQUE_ID', 'EVENT_TIME']).cumcount() # 分组键:UNIQUE_ID + 组内序号 groups = ['UNIQUE_ID', enums] # 计算每个(UNIQUE_ID, EVENT_TIME)组的行数大小 sizes = df_dupl.groupby(['UNIQUE_ID', 'EVENT_TIME']).transform('size') # 判断当前子DataFrame是否和上一个同UNIQUE_ID的子DataFrame完全重复 dup = ( # 检查每一行的value_cols是否和上一个同组序号的行的值相等 df_dupl.groupby(groups)[value_cols].shift().eq(df_dupl[value_cols]).all(axis=1) # 确保组内所有行都满足上述条件(即整个子DataFrame完全重复) .groupby(['UNIQUE_ID', 'EVENT_TIME']).transform('all') # 同时当前组的大小要和上一个同UNIQUE_ID的组大小一致 & sizes.groupby(df_dupl['UNIQUE_ID']).diff().eq(0) ) # 过滤掉重复的子DataFrame,保留原数据顺序 df_result = df_dupl[~dup].sort_values(['UNIQUE_ID', 'EVENT_TIME'], kind='stable').reset_index(drop=True)
处理含NaN的场景
如果你的数据里有NaN,并且需要把NaN视为相等的话,只需要修改重复判断的逻辑即可:
value_cols = ['Value1', 'Value2'] enums = df_dupl.groupby(['UNIQUE_ID', 'EVENT_TIME']).cumcount() groups = ['UNIQUE_ID', enums] sizes = df_dupl.groupby(['UNIQUE_ID', 'EVENT_TIME']).transform('size') # 修改判断逻辑,把NaN相等的情况也纳入判断 dup = ( ( df_dupl.groupby(groups)[value_cols].shift().eq(df_dupl[value_cols]) # 新增:前后行都是NaN的情况也视为相等 | (df_dupl.groupby(groups)[value_cols].shift().isna() & df_dupl[value_cols].isna()) ).all(axis=1) .groupby(['UNIQUE_ID', 'EVENT_TIME']).transform('all') & sizes.groupby(df_dupl['UNIQUE_ID']).diff().eq(0) ) df_result = df_dupl[~dup].sort_values(['UNIQUE_ID', 'EVENT_TIME'], kind='stable').reset_index(drop=True)
性能对比
我们针对不同数据量测试了几种方案的耗时,结果如下(单位:毫秒):
| 方法 | 1000行 | 10000行 | 100000行 |
|---|---|---|---|
| 你的原方法 | 556 | 5410 | 未测试 |
| 其他方案 | 696-1240 | 4560-10100 | 未测试 |
| 上述最优方案 | 11.3 | 34.1 | 318 |
可以看到,这个最优方案的性能碾压其他方法,即使处理10万行也只需要300多毫秒,完全能hold住百万级别的数据。
备注:内容来源于stack exchange,提问作者user23503493
相关产品推荐
相关产品推荐

