基于时间区间条件高效实现将df2的Info列数据匹配到df1的方法
基于时间区间条件高效实现将df2的Info列数据匹配到df1的方法
嗨,针对你提出的这个时间区间匹配需求——把df2里的Info列数据匹配到df1,要求df1的Time落在df2的Start_Time和End_Time之间,我来给你分享几个高效的实现方法,尤其是适合大数据量的场景:
首先先明确你提供的原始数据:
df1
| Time | ID | Place_Holder |
|---|---|---|
| 13:10:00 | #1 | g |
| 13:10:00 | #2 | g |
| 14:10:00 | #1 | g |
| 14:10:00 | #2 | g |
| 15:50:00 | #1 | g |
df2
| Start_Time | End_Time | Info |
|---|---|---|
| 13:00:00 | 13:55:00 | abc |
| 14:00:00 | 14:30:00 | xyz |
| 15:00:00 | 15:20:00 | tuv |
方法一:使用merge_asof(推荐,高效适配大数据)
merge_asof是pandas专门为有序键的合并场景设计的工具,非常适合这种时间区间匹配,性能比循环或逐行判断好太多,时间复杂度为O(n log n)(主要来自排序步骤)。
步骤1:转换时间列为datetime类型
首先得把所有时间字符串转成datetime格式,这样才能正确进行区间比较:
import pandas as pd # 转换df1的Time列 df1['Time'] = pd.to_datetime(df1['Time'], format='%H:%M:%S') # 转换df2的Start_Time和End_Time列 df2['Start_Time'] = pd.to_datetime(df2['Start_Time'], format='%H:%M:%S') df2['End_Time'] = pd.to_datetime(df2['End_Time'], format='%H:%M:%S')
步骤2:对df2按Start_Time排序
merge_asof要求右表(这里是df2)必须按匹配键(Start_Time)有序:
df2_sorted = df2.sort_values('Start_Time')
步骤3:合并并过滤区间
先通过merge_asof匹配到Start_Time <= Time的最近行,再过滤出Time <= End_Time的结果,确保Time落在完整区间内:
# 先对df1按Time排序(merge_asof要求左表也有序) df1_sorted = df1.sort_values('Time') # 执行合并,匹配最近的Start_Time不晚于Time的行 merged = pd.merge_asof(df1_sorted, df2_sorted, left_on='Time', right_on='Start_Time') # 过滤出Time在[Start_Time, End_Time]区间内的记录 merged = merged[merged['Time'] <= merged['End_Time']] # 如果需要保留df1中所有原始行(包括没匹配到区间的,Info显示NaN),可以这样处理: merged_full = pd.merge_asof(df1_sorted, df2_sorted, left_on='Time', right_on='Start_Time', direction='backward') merged_full['Info'] = merged_full.apply( lambda row: row['Info'] if row['Time'] <= row['End_Time'] else pd.NA, axis=1 ) # 恢复df1原来的行顺序(如果需要) merged_full = merged_full.sort_index()
方法二:区间索引匹配(适合小数据量)
如果你的数据量不大,也可以用区间索引的方式实现,代码更直观,但性能不如merge_asof:
# 为df2创建时间区间索引 df2['Time_Interval'] = pd.IntervalIndex.from_arrays( df2['Start_Time'], df2['End_Time'], closed='both' ) # 逐行匹配区间 df1['Info'] = df1['Time'].apply( lambda t: df2[df2['Time_Interval'].contains(t)]['Info'].values[0] if any(df2['Time_Interval'].contains(t)) else pd.NA )
预期输出
处理后你会得到类似这样的结果(以保留所有df1行的情况为例):
| Time | ID | Place_Holder | Info |
|---|---|---|---|
| 2023-01-01 13:10:00 | #1 | g | abc |
| 2023-01-01 13:10:00 | #2 | g | abc |
| 2023-01-01 14:10:00 | #1 | g | xyz |
| 2023-01-01 14:10:00 | #2 | g | xyz |
| 2023-01-01 15:50:00 | #1 | g |
(注:日期部分是pandas默认补充的,不影响时间区间的判断,你可以用.dt.time提取时间部分)
备注:内容来源于stack exchange,提问作者Hennie Scholtz
相关产品推荐
相关产品推荐

