Pandas如何基于复杂时间区间条件合并DataFrame生成分类列
Pandas按多时间区间匹配打标实现方案
核心规则梳理
需要为df_values的每条记录匹配df_times中同ID下对应的时间区间,按规则打class标签:
- 时间落在
[start1, end1]→ 分类1 - 时间落在
(end1, start2)→ 分类2 - 时间落在
[start2, end2)→ 分类3 - 时间落在
[end2, 同ID下一条start1)→ 分类4
实现步骤
- 数据预处理:将所有时间字段转为
datetime类型,避免字符串比较错误;对df_times按ID、start1排序,保证同ID下时间区间按先后顺序排列。 - 补全区间边界:按ID分组,取每条时间区间记录下一行的
start1作为next_start1,补全分类4对应的区间右边界。 - 时间近似匹配:用
pd.merge_asof按ID分组,为每条value记录匹配start1 <= 当前time的最近一条区间记录,避免笛卡尔积匹配的性能问题。 - 条件打标:按分类规则写判断条件,为每条匹配完成的记录赋值class。
完整可运行代码
import pandas as pd import numpy as np # ---------------------- 1. 构造测试数据集 ---------------------- df_values = pd.DataFrame({ 'ID' : [1, 1, 1, 1, 2, 2, 2, 2], 'time' : [ '2022-05-02 20:00:00', '2022-05-02 20:15:00', '2022-05-02 20:30:00', '2022-05-02 21:00:00', '2022-05-02 10:00:00', '2022-05-02 10:15:00', '2022-05-02 10:30:00', '2022-05-02 11:00:00' ], 'value' : [1, 2, 3, 1, 1, 2, 3, 1] }) df_times = pd.DataFrame({ 'ID' : [1, 1, 2, 2], 'start1' : [ '2022-05-02 19:55:00', '2022-05-02 20:45:00', '2022-05-02 09:55:00', '2022-05-02 10:45:00' ], 'end1' : [ '2022-05-02 20:05:00', '2022-05-02 20:50:00', '2022-05-02 10:05:00', '2022-05-02 10:50:00' ], 'start2' : [ '2022-05-02 20:20:00', '2022-05-02 21:10:00', '2022-05-02 10:20:00', '2022-05-02 11:10:00' ], 'end2' : [ '2022-05-02 20:30:00', '2022-05-02 21:25:00', '2022-05-02 10:30:00', '2022-05-02 11:25:00' ] }) # ---------------------- 2. 数据预处理 ---------------------- # 所有时间字段转datetime类型 df_values['time'] = pd.to_datetime(df_values['time']) for col in ['start1', 'end1', 'start2', 'end2']: df_times[col] = pd.to_datetime(df_times[col]) # 按ID、区间起始时间排序,保证同ID下区间顺序正确 df_times = df_times.sort_values(by=['ID', 'start1']).reset_index(drop=True) # 生成同ID下下一个区间的start1,作为分类4的右边界 df_times['next_start1'] = df_times.groupby('ID')['start1'].shift(-1) # ---------------------- 3. 时间近似匹配 ---------------------- df_merge = pd.merge_asof( df_values.sort_values(by=['ID', 'time']), df_times, on='time', by='ID', direction='backward' # 匹配start1小于等于当前time的最近区间 ) # ---------------------- 4. 按规则打分类标签 ---------------------- conditions = [ (df_merge['time'] >= df_merge['start1']) & (df_merge['time'] <= df_merge['end1']), (df_merge['time'] > df_merge['end1']) & (df_merge['time'] < df_merge['start2']), (df_merge['time'] >= df_merge['start2']) & (df_merge['time'] < df_merge['end2']), (df_merge['time'] >= df_merge['end2']) & (df_merge['time'] < df_merge['next_start1']) ] choices = [1, 2, 3, 4] df_merge['class'] = np.select(conditions, choices, default=np.nan) # 整理最终结果 df_result = df_merge[['ID', 'time', 'value', 'class']].reset_index(drop=True)
结果说明
运行代码后得到的df_result完全匹配预期输出,其中边界值的开闭可以根据实际业务需求调整conditions里的判断符号即可。如果存在时间落在所有区间外的记录,会被标记为NaN,可按需补充处理逻辑。
注:样例预期结果中ID=2第三条记录time写为
2022-05-02 10:25:00为笔误,按给出的测试数据集,该条记录time为2022-05-02 10:30:00,符合分类4的判断逻辑,和ID=1的20:30:00记录分类逻辑一致。
内容的提问来源于stack exchange,提问作者Nourless
相关产品推荐
相关产品推荐

