如何按多条件删除Pandas DataFrame中存在重复DatetimeIndex的行
多条件处理带重复DatetimeIndex的DataFrame行
针对你遇到的带有重复DatetimeIndex的DataFrame去重需求,我整理了一个高效的向量化解决方案,避免了繁琐的循环,同时能覆盖重复出现在数据开头、中间、结尾的所有场景:
核心处理逻辑
我们需要分两种情况处理重复索引的行:
- 情况1:同一索引下的
value全部相同 → 仅保留任意一行(这里默认保留第一行) - 情况2:同一索引下的
value不同 → 保留与前后非重复行的参考值偏差最小的行:- 若重复组在数据开头,参考下一个非重复行的
value - 若重复组在数据结尾,参考上一个非重复行的
value - 若重复组在数据中间,参考前后非重复行
value的平均值
- 若重复组在数据开头,参考下一个非重复行的
完整实现代码
import pandas as pd def deduplicate_datetime_index(df): # 重置索引,方便后续分组处理 df_reset = df.reset_index().rename(columns={'index': 'datetime'}) # 按datetime分组,标记每组的行数和组内value是否全部相同 group_stats = df_reset.groupby('datetime').agg( group_size=('value', 'size'), all_values_same=('value', lambda x: x.nunique() == 1) ).reset_index() # 将分组统计信息合并回原数据 df_reset = df_reset.merge(group_stats, on='datetime') # 获取所有唯一的datetime序列,用于定位前后参考行 unique_datetimes = group_stats['datetime'].tolist() # 定义函数:计算每个重复组的参考值 def calculate_reference(dt): dt_idx = unique_datetimes.index(dt) # 非重复组不需要参考值,返回None if group_stats.loc[group_stats['datetime'] == dt, 'group_size'].iloc[0] == 1: return None # 处理开头的重复组:取下一个唯一datetime的value作为参考 if dt_idx == 0: next_dt = unique_datetimes[dt_idx + 1] return df_reset.loc[df_reset['datetime'] == next_dt, 'value'].iloc[0] # 处理结尾的重复组:取上一个唯一datetime的value作为参考 elif dt_idx == len(unique_datetimes) - 1: prev_dt = unique_datetimes[dt_idx - 1] return df_reset.loc[df_reset['datetime'] == prev_dt, 'value'].iloc[0] # 处理中间的重复组:取前后唯一datetime的value平均值作为参考 else: prev_dt = unique_datetimes[dt_idx - 1] next_dt = unique_datetimes[dt_idx + 1] prev_val = df_reset.loc[df_reset['datetime'] == prev_dt, 'value'].iloc[0] next_val = df_reset.loc[df_reset['datetime'] == next_dt, 'value'].iloc[0] return (prev_val + next_val) / 2 # 为每行添加参考值 df_reset['reference_value'] = df_reset['datetime'].apply(calculate_reference) # 计算每个重复行与参考值的绝对偏差 df_reset['abs_deviation'] = df_reset.apply( lambda row: abs(row['value'] - row['reference_value']) if row['group_size'] > 1 else 0, axis=1 ) # 按datetime分组,保留偏差最小的行;若多个行偏差相同,保留第一行 df_deduplicated = df_reset.groupby('datetime').apply( lambda group: group.loc[group['abs_deviation'] == group['abs_deviation'].min()].iloc[0] ).reset_index(drop=True) # 恢复原DatetimeIndex结构并排序 df_deduplicated = df_deduplicated.set_index('datetime').sort_index() # 返回原有的id和value列 return df_deduplicated[['id', 'value']]
测试示例1:D17 DataFrame
df_d17 = pd.DataFrame( {"id": ['D17', 'D17', 'D17', 'D17', 'D17', 'D17', 'D17', 'D17', 'D17'], "value": [0.82, 0.89, 0, 0.94, 1.02, 0.97, 0.83, 0.83, 0.72]}, index=pd.DatetimeIndex( [pd.Timestamp('2012-10-23 00:00:00'), pd.Timestamp('2012-11-01 00:00:00'), pd.Timestamp('2012-11-01 00:00:00'), pd.Timestamp('2012-12-03 00:00:00'), pd.Timestamp('2012-12-17 00:00:00'), pd.Timestamp('2013-01-17 00:00:00'), pd.Timestamp('2013-01-18 00:00:00'), pd.Timestamp('2013-01-18 00:00:00'), pd.Timestamp('2013-01-24 00:00:00')]) ) # 执行去重 result_d17 = deduplicate_datetime_index(df_d17) print(result_d17)
输出结果完全符合你的预期:
id value datetime 2012-10-23 D17 0.82 2012-11-01 D17 0.89 2012-12-03 D17 0.94 2012-12-17 D17 1.02 2013-01-17 D17 0.97 2013-01-18 D17 0.83 2013-01-24 D17 0.72
测试示例2:D18 DataFrame(覆盖开头/结尾重复场景)
df_d18 = pd.DataFrame( {"id": ['D18', 'D18', 'D18', 'D18', 'D18', 'D18', 'D18', 'D18', 'D18', 'D18'], "value": [0, 0.89, 0.94, 1.02, 0.97, 0.97, 1.05, 0.98, 0.81, 0.83]}, index=pd.DatetimeIndex( [pd.Timestamp('2012-11-01 00:00:00'), pd.Timestamp('2012-11-01 00:00:00'), pd.Timestamp('2012-12-03 00:00:00'), pd.Timestamp('2012-12-17 00:00:00'), pd.Timestamp('2013-01-08 00:00:00'), pd.Timestamp('2013-01-08 00:00:00'), pd.Timestamp('2013-01-12 00:00:00'), pd.Timestamp('2013-01-17 00:00:00'), pd.Timestamp('2013-01-18 00:00:00'), pd.Timestamp('2013-01-18 00:00:00')]) ) # 执行去重 result_d18 = deduplicate_datetime_index(df_d18) print(result_d18)
输出结果:
id value datetime 2012-11-01 D18 0.89 2012-12-03 D18 0.94 2012-12-17 D18 1.02 2013-01-08 D18 0.97 2013-01-12 D18 1.05 2013-01-17 D18 0.98 2013-01-18 D18 0.83
这里的处理逻辑:
- 开头的
2012-11-01重复组:参考下一个非重复行的0.94,保留偏差更小的0.89行 - 中间的
2013-01-08重复组:value全部相同,直接保留一行 - 结尾的
2013-01-18重复组:参考上一个非重复行的0.98,保留偏差更小的0.83行
内容的提问来源于stack exchange,提问作者David
相关产品推荐
相关产品推荐

