如何使用Pandas处理存在重叠的时间范围排序问题
解决方案:处理指令覆盖生成最终时间线快照
针对你描述的指令覆盖问题,我们可以通过时间区间分割+优先级筛选+区间合并的步骤,用Pandas实现最终的连续时间线快照。以下是具体实现步骤和代码:
核心思路
- 预处理数据:将时间字段转换为可比较的datetime类型,确保后续时间操作准确。
- 分割时间区间:提取所有指令的起始/结束时间,生成最小粒度的不重叠时间区间,确保每个区间只被单一优先级的指令覆盖。
- 筛选最高优先级指令:对每个最小区间,找到所有覆盖它的指令,保留编号(Number)最大的后续指令,确定该区间的层级。
- 合并相邻同层级区间:将连续的相同层级区间合并,得到简洁的最终时间线。
代码实现
1. 导入依赖并构造示例数据
import pandas as pd # 构造你描述的示例输入数据 data = [ {"Number": 9166, "timeFrom": "11:55", "timeTo": "11:59", "levelFrom": 0, "levelTo": 18}, {"Number": 9166, "timeFrom": "11:59", "timeTo": "12:24", "levelFrom": 18, "levelTo": 18}, {"Number": 9166, "timeFrom": "12:24", "timeTo": "12:27", "levelFrom": 18, "levelTo": 0}, {"Number": 9167, "timeFrom": "11:59", "timeTo": "12:52", "levelFrom": 18, "levelTo": 18} ] df = pd.DataFrame(data) # 将时间字符串转换为datetime类型(假设为同一天,添加日期保证时间可比较) df["timeFrom"] = pd.to_datetime("2024-01-01 " + df["timeFrom"]) df["timeTo"] = pd.to_datetime("2024-01-01 " + df["timeTo"])
2. 生成最小粒度时间区间
# 收集所有指令的时间点并排序 all_time_points = pd.concat([df["timeFrom"], df["timeTo"]]).unique() all_time_points.sort() # 生成所有不重叠的最小时间区间 time_intervals = [] for i in range(len(all_time_points) - 1): time_intervals.append({ "start_time": all_time_points[i], "end_time": all_time_points[i+1] }) interval_df = pd.DataFrame(time_intervals)
3. 为每个区间匹配最高优先级的层级
def get_final_level(row): # 筛选出覆盖当前区间的所有指令:指令起始时间≤区间起始,指令结束时间≥区间结束 mask = (df["timeFrom"] <= row["start_time"]) & (df["timeTo"] >= row["end_time"]) matching_commands = df[mask] if matching_commands.empty: return None # 无覆盖的区间可根据需求处理,这里直接丢弃 # 取编号最大的后续指令的目标层级 highest_priority_cmd = matching_commands.sort_values("Number", ascending=False).iloc[0] return highest_priority_cmd["levelTo"] # 为每个区间分配最终层级 interval_df["level"] = interval_df.apply(get_final_level, axis=1) # 过滤无覆盖的区间 interval_df = interval_df.dropna(subset=["level"])
4. 合并相邻同层级区间
# 合并连续的相同层级区间 merged_intervals = [] current_interval = interval_df.iloc[0].to_dict() for idx in range(1, len(interval_df)): next_interval = interval_df.iloc[idx].to_dict() # 如果相邻且层级相同,合并结束时间 if next_interval["level"] == current_interval["level"] and next_interval["start_time"] == current_interval["end_time"]: current_interval["end_time"] = next_interval["end_time"] else: merged_intervals.append(current_interval) current_interval = next_interval merged_intervals.append(current_interval) # 生成最终结果DataFrame final_df = pd.DataFrame(merged_intervals)[["start_time", "end_time", "level"]] # 转换回时间字符串格式(可选) final_df["start_time"] = final_df["start_time"].dt.strftime("%H:%M") final_df["end_time"] = final_df["end_time"].dt.strftime("%H:%M") print(final_df)
输出结果
运行代码后,得到的最终时间线如下:
start_time end_time level 0 11:55 11:59 18 1 11:59 12:52 18
完全符合你描述的需求:指令9167覆盖了9166中12:24返回0的部分,最终11:59到12:52保持18层级。
扩展说明
- 优先级逻辑:这里默认
Number越大的指令越晚到达,优先级越高。如果你的优先级规则不同,只需修改排序逻辑即可。 - 初始状态处理:如果需要处理时间线起始前的初始层级,可以在最开始添加一个覆盖起始前区间的默认指令。
- 跨天时间:如果涉及跨天时间,只需确保datetime字段包含完整的日期信息即可,代码无需修改。
内容的提问来源于stack exchange,提问作者Rarooranya
相关产品推荐
相关产品推荐

