基于另一数据框的类别与范围填充Pandas列
按轨道类别+位置区间匹配添加Speed列的解决方案
以下提供两种实用的实现方法,先给出示例数据方便你对应自己的场景调整:
import pandas as pd # 示例DF1:待补充Speed的主表 DF1 = pd.DataFrame({ 'TrackType': ['A', 'A', 'B', 'B'], 'LocationFrom': [10, 30, 5, 25], 'LocationTo': [20, 40, 15, 35] }) # 示例DF2:包含轨道类别、位置区间、对应限速的参考表 DF2 = pd.DataFrame({ 'TrackType': ['A', 'A', 'B', 'B'], 'StartLoc': [0, 25, 0, 20], 'EndLoc': [25, 50, 20, 40], 'Speed': [60, 80, 50, 70] })
方法一:分组后使用merge_asof
利用merge_asof的范围匹配能力,先按轨道类别分组,再在组内完成区间匹配,确保类别优先匹配:
# 先对参考表按轨道类别+起始位置排序(merge_asof要求右表按匹配键排序) DF2_sorted = DF2.sort_values(['TrackType', 'StartLoc']) # 定义分组处理逻辑 def match_speed_by_group(group): track_type = group.name # 筛选当前轨道类别的参考数据 df2_sub = DF2_sorted[DF2_sorted['TrackType'] == track_type] # 对当前组的起始位置排序(merge_asof要求左表按匹配键排序) group_sorted = group.sort_values('LocationFrom') # 执行merge_asof匹配,按轨道类别关联,按LocationFrom匹配StartLoc的最近区间 merged = pd.merge_asof( group_sorted, df2_sub, left_on='LocationFrom', right_on='StartLoc', direction='backward', # 取小于等于LocationFrom的最大StartLoc对应的区间 by='TrackType' # 强制先按轨道类别分组匹配 ) # 额外校验:确保LocationFrom落在参考区间内(避免匹配到不相关的区间) merged = merged[merged['LocationFrom'] <= merged['EndLoc']] # 恢复原表的索引顺序 return merged.sort_index() # 按轨道类别分组处理,合并结果 result = DF1.groupby('TrackType', group_keys=False).apply(match_speed_by_group) # 整理输出列 result = result[['TrackType', 'LocationFrom', 'LocationTo', 'Speed']]
方法二:IntervalIndex + 分组映射
为每个轨道类别构建位置区间索引,直接匹配主表的位置值:
# 为每个轨道类别创建区间-限速的映射字典 speed_interval_maps = {} for track_type, df2_group in DF2.groupby('TrackType'): # 创建全闭区间(可根据实际需求调整为'left'/'right'/'neither') intervals = pd.IntervalIndex.from_arrays(df2_group['StartLoc'], df2_group['EndLoc'], closed='both') speed_interval_maps[track_type] = pd.Series(df2_group['Speed'].values, index=intervals) # 定义单行匹配函数 def get_matched_speed(row): speed_series = speed_interval_maps[row['TrackType']] # 查找LocationFrom所在的区间对应的限速 match_idx = speed_series.index.get_indexer([row['LocationFrom']])[0] # 若未匹配到返回None(可根据需求改为默认值) return speed_series.iloc[match_idx] if match_idx != -1 else None # 为DF1添加Speed列 DF1['Speed'] = DF1.apply(get_matched_speed, axis=1)
注意事项
- 若需要同时校验
LocationFrom和LocationTo都落在DF2的区间内,可在代码中添加额外判断(比如merged = merged[(merged['LocationFrom'] >= merged['StartLoc']) & (merged['LocationTo'] <= merged['EndLoc'])]) merge_asof的direction参数可根据需求调整:backward取最近的左区间,forward取最近的右区间,nearest取最近的任意区间- IntervalIndex的
closed参数要和你的区间定义一致,避免匹配错误
内容的提问来源于stack exchange,提问作者Xanthan Gum
相关产品推荐
相关产品推荐

