基于重叠时间范围合并DataFrame,解决KeyError报错问题
我明白你遇到的困扰了——当两个DataFrame的时间区间存在重叠时,用常规的区间匹配方法会抛出KeyError: 'indexer does not intersect a unique set of intervals'。这是因为pandas的IntervalIndex.get_loc要求区间必须是不重叠、无交叉的,而你的df_2里有多个重叠的时间区间(比如E2和E3的区间就有重叠部分),单个时间戳可能匹配多个区间,导致get_loc无法返回唯一索引,所以报错。
下面给你一个简单高效的解决方案,完全能实现你想要的结果:
步骤1:确保所有时间列都是datetime类型
首先要把df_1和df_2里的时间列转换成pandas的datetime格式,避免后续匹配时出现类型问题:
import pandas as pd import numpy as np # 导入你提供的示例数据 df_1 = pd.DataFrame({ 'timestamp': ['2019-07-14 05:31:00', '2019-07-14 06:32:00', '2019-07-14 07:02:00'], 'A': [0.020228, 0.057780, 0.076623], 'B': [0.026572, 0.175499, 0.875499] }) df_2 = pd.DataFrame({ 'start': ['2019-07-14 05:30:00', '2019-07-14 06:00:00', '2019-07-14 06:30:01', '2019-07-14 07:30:01'], 'end': ['2019-07-14 06:30:00', '2019-07-14 07:00:00', '2019-07-14 07:30:00', '2019-07-14 08:30:00'], 'event': ['E1', 'E2', 'E3', 'E4'] }) # 转换时间列类型为datetime df_1['timestamp'] = pd.to_datetime(df_1['timestamp']) df_2['start'] = pd.to_datetime(df_2['start']) df_2['end'] = pd.to_datetime(df_2['end'])
步骤2:交叉合并+条件过滤
利用pandas的merge(how='cross')先做全量交叉合并,然后过滤出timestamp落在对应start和end区间内的行,这种方法完全兼容重叠区间的场景:
# 交叉合并两个DataFrame,生成所有可能的组合 cross_merge = df_1.merge(df_2, how='cross') # 过滤出符合时间区间条件的行 result = cross_merge[(cross_merge['timestamp'] >= cross_merge['start']) & (cross_merge['timestamp'] <= cross_merge['end'])] # 调整列的顺序,和你预期的结果一致 result = result[['start', 'end', 'event', 'timestamp', 'A']].reset_index(drop=True)
运行这段代码后,得到的result就是你想要的输出:
start end event timestamp A 0 2019-07-14 05:30:00 2019-07-14 06:30:00 E1 2019-07-14 05:31:00 0.020228 1 2019-07-14 06:00:00 2019-07-14 07:00:00 E2 2019-07-14 06:32:00 0.057780 2 2019-07-14 06:30:01 2019-07-14 07:30:00 E3 2019-07-14 06:32:00 0.057780 3 2019-07-14 06:30:01 2019-07-14 07:30:00 E3 2019-07-14 07:02:00 0.076623
为什么之前的方法报错?
你提到的高赞答案应该是用了IntervalIndex.get_loc方法,这个方法要求所有区间是无重叠、连续且唯一的。当你的df_2存在重叠区间时,一个时间戳会匹配多个区间,get_loc无法返回单一索引,就会抛出那个KeyError。而上面的交叉合并+过滤方法不依赖区间的唯一性,完全适配重叠场景。
如果你的数据量很大,交叉合并可能会占用较多内存,这时可以用更高效的方法:利用IntervalIndex的overlaps方法批量匹配,不过对于大多数日常场景,交叉合并的方法已经足够简单好用了。
内容的提问来源于stack exchange,提问作者giser_yugang

