本文介绍如何使用 Pandas 对数据按 Pass_ID 字符串字段分组,并高效筛选出全部记录均落在指定时间区间(如 10:00–13:00)内的组,或至少有一条记录落在该区间内的组,涵盖 between_time、groupby.filter 与时间索引协同使用的最佳实践。
本文介绍如何使用 pandas 对数据按 `pass_id` 字符串字段分组,并高效筛选出**全部记录均落在指定时间区间(如 10:00–13:00)内**的组,或**至少有一条记录落在该区间内**的组,涵盖 `between_time`、`groupby.filter` 与时间索引协同使用的最佳实践。
在时间序列分析中,常需按业务标识(如 Pass_ID)分组后,进一步按“日内的时段”进行逻辑筛选——例如仅保留全天活动都发生在上午 10 点至下午 1 点之间的通行记录。Pandas 提供了灵活且高效的方式实现这一目标,核心在于合理利用 between_time 方法与 groupby().filter() 的组合。
✅ 场景一:获取「至少有一条记录」落在指定时段的 Pass_ID
这是最常用的需求:只要某 Pass_ID 下任意一条记录的时间落在 10:00–13:00 内,就将其纳入结果。推荐做法是临时将 Date_Time 设为索引,再调用 between_time 进行全局时间过滤,最后提取唯一 Pass_ID:
# 确保 Date_Time 是 datetime 类型(若尚未转换)
df["Date_Time"] = pd.to_datetime(df["Date_Time"])
# 提取所有在 10:00–13:00 区间内出现过的 Pass_ID(去重)
pass_ids_in_range = (
df.set_index("Date_Time")
.between_time("10:00", "13:00")["Pass_ID"]
.unique()
)
print(pass_ids_in_range) # 输出: ['B']
⚠️ 注意:between_time 仅作用于 DatetimeIndex,因此必须先用 set_index("Date_Time");它默认包含边界(闭区间),且对时分秒精度完全兼容(如 12:12:50.50 会被正确识别)。
✅ 场景二:获取「全部记录」均落在指定时段的 Pass_ID
若需严格筛选——即某个 Pass_ID 的所有时间戳都必须位于 10:00–13:00 内(如用于校验合规性),则应使用 groupby().filter() 配合自定义逻辑:
filtered_groups = (
df.groupby("Pass_ID")
.filter(
lambda group: len(
group.set_index("Date_Time").between_time("10:00", "13:00")
) == len(group)
)
)
pass_ids_full_range = filtered_groups["Pass_ID"].unique()
print(pass_ids_full_range) # 输出: ['B'](因 B 组全部记录均在此区间)
该逻辑等价于:“对每个 Pass_ID 分组,检查其 between_time 返回的行数是否等于原组总行数”。若相等,说明该组无一例外地满足时间约束。
? 补充技巧与注意事项
- 避免重复设索引:若 Date_Time 已是索引,可直接调用 .between_time(),无需 set_index;
- 跨日处理安全:between_time 按“当日时分”匹配,不考虑日期部分,因此 2023-03-30 12:00 和 2023-03-31 12:00 均会被捕获;
- 性能提示:对大数据集,优先使用 set_index + between_time(底层优化),而非逐行 dt.hour.between() 判断;
- 扩展用法:若需多时段(如早高峰 07:00–09:00 或晚高峰 17:00–19:00),只需多次调用 between_time 并 concat 或 isin 即可。
综上,between_time 与 groupby.filter 的组合,兼顾简洁性与语义清晰度,是 Pandas 时间范围分组筛选的推荐范式。掌握二者协同逻辑,可快速应对各类基于“日周期时段”的业务分组过滤需求。











