Pandas如何删除被更大时间区间完全覆盖的区间记录
解决方案
overlaps() 方法仅能判断两个区间是否存在交集,无法区分「当前区间覆盖其他区间」和「当前区间被其他区间覆盖」两种场景,直接基于重叠结果过滤会误删覆盖范围最大的区间。我们可以通过区间端点的大小比较,直接判断区间是否被完全覆盖。
基础实现(适合小数据集)
核心判断规则:一个区间需要被删除,当且仅当存在另一个不同的区间,其开始时间早于等于当前区间开始时间,且结束时间晚于等于当前区间结束时间。
import pandas as pd import numpy as np # 构造示例DataFrame arrays = [ [pd.Timestamp(2022,1,1), pd.Timestamp(2022,2,1), pd.Timestamp(2022,1,1), pd.Timestamp(2022,4,1)], [pd.Timestamp(2022,1,31), pd.Timestamp(2022,2,28), pd.Timestamp(2022,3,30), pd.Timestamp(2022,4,30)] ] idx = pd.MultiIndex.from_arrays(arrays) df = pd.DataFrame(np.zeros(4), index=idx) # 提取全量区间的起止时间 all_start = df.index.get_level_values(0).values all_end = df.index.get_level_values(1).values # 逐行判断是否被其他区间覆盖 covered_flag = [] for i in range(len(df)): s, e = all_start[i], all_end[i] # 统计满足「起点<=当前起点、终点>=当前终点」的区间数量,排除自身后数量>0则说明被覆盖 cover_count = ((all_start <= s) & (all_end >= e)).sum() covered_flag.append(cover_count > 1) # 过滤被覆盖的行 res = df[~np.array(covered_flag)]
运行后输出结果符合预期:
0 2022-01-01 2022-03-30 0.0 2022-04-01 2022-04-30 0.0
性能优化版本(适合十万级以上大数据集)
基础实现的时间复杂度为O(n²),数据量大时运行效率低,可以通过排序后单次遍历将复杂度降到O(nlogn):
- 先对所有区间按开始时间升序、结束时间降序排序
- 维护遍历过程中遇到的最大结束时间,排序后当前区间的开始时间一定大于等于之前所有区间的开始时间,只要当前区间结束时间小于等于历史最大结束时间,就说明当前区间被之前的某个区间完全覆盖
# 按索引排序:第一层开始时间升序,第二层结束时间降序 sorted_df = df.sort_index(ascending=[True, False]) sorted_end = sorted_df.index.get_level_values(1).values keep_mask = [] current_max_end = None for e in sorted_end: if current_max_end is None or e > current_max_end: keep_mask.append(True) current_max_end = e else: keep_mask.append(False) res = sorted_df[keep_mask]
注:新版Pandas中已弃用
pd.datetime,统一使用pd.Timestamp生成时间对象。
内容的提问来源于stack exchange,提问作者W. Walter
相关产品推荐
相关产品推荐

