使用Pandas进行Timestamp过滤:筛选员工可执行的潜在班次
解决DataFrame班次与可用时间的冲突筛选问题
你的需求是移除df1中所有无法完全落在员工可用时段(df2)内的潜在班次,也就是保留那些班次的开始时间晚于等于某个可用时段的开始,且结束时间早于等于该可用时段结束的行。
先分析你之前尝试的问题:df1[df1.starts_at[0] >= df2.starts_at]这个写法有几个明显问题:
- 只拿了df1中第一个班次的开始时间来比较,没有遍历所有班次;
- 仅比较了开始时间,没校验结束时间是否在可用时段内;
- df1和df2长度不同,这样的布尔序列无法正确筛选df1的行。
下面给出两种可行的解决方案:
方法1:通过交叉合并+筛选实现
这种方法先建立两个DataFrame的笛卡尔积,然后筛选出符合条件的班次,最后去重得到结果:
import pandas as pd from pandas import Timestamp # 构造你的数据 n1 = {'starts_at': {0: Timestamp('2019-06-02 09:00:00'), 1: Timestamp('2019-06-02 17:00:00'), 2: Timestamp('2019-06-02 14:00:00'), 3: Timestamp('2019-06-03 09:30:00')}, 'ends_at': {0: Timestamp('2019-06-02 17:00:00'), 1: Timestamp('2019-06-02 22:30:00'), 2: Timestamp('2019-06-02 22:30:00'), 3: Timestamp('2019-06-03 13:00:00')}} n2 = {'starts_at': {0: Timestamp('2019-06-01 14:00:00'), 1: Timestamp('2019-06-01 14:41:32.464000'), 2: Timestamp('2019-06-01 15:00:00'), 3: Timestamp('2019-06-02 10:00:00'), 4: Timestamp('2019-06-02 14:00:00'), 5: Timestamp('2019-06-02 17:00:00'), 6: Timestamp('2019-06-02 17:30:00'), 7: Timestamp('2019-06-03 17:00:00')}, 'ends_at': {0: Timestamp('2019-06-01 22:30:00'), 1: Timestamp('2019-06-01 22:32:44.862000'), 2: Timestamp('2019-06-01 22:30:00'), 3: Timestamp('2019-06-02 18:30:00'), 4: Timestamp('2019-06-03 00:00:00'), 5: Timestamp('2019-06-03 00:00:00'), 6: Timestamp('2019-06-02 21:00:00'), 7: Timestamp('2019-06-03 23:00:00')}} df1 = pd.DataFrame(n1) df2 = pd.DataFrame(n2) # 给df1添加唯一标识,方便后续筛选 df1['shift_id'] = df1.index # 交叉合并两个表,得到所有班次与可用时段的组合 merged = df1.merge(df2, how='cross') # 筛选出完全包含在可用时段内的班次 valid_mask = (merged['starts_at_x'] >= merged['starts_at_y']) & (merged['ends_at_x'] <= merged['ends_at_y']) valid_shifts = merged[valid_mask]['shift_id'].unique() # 得到最终有效班次 result = df1[df1['shift_id'].isin(valid_shifts)].drop('shift_id', axis=1) print(result)
方法2:使用apply逐行校验
这种方法更直观,对df1的每一行检查是否存在df2中的可用时段能完全包含它:
import pandas as pd from pandas import Timestamp # 构造数据(同上) n1 = {'starts_at': {0: Timestamp('2019-06-02 09:00:00'), 1: Timestamp('2019-06-02 17:00:00'), 2: Timestamp('2019-06-02 14:00:00'), 3: Timestamp('2019-06-03 09:30:00')}, 'ends_at': {0: Timestamp('2019-06-02 17:00:00'), 1: Timestamp('2019-06-02 22:30:00'), 2: Timestamp('2019-06-02 22:30:00'), 3: Timestamp('2019-06-03 13:00:00')}} n2 = {'starts_at': {0: Timestamp('2019-06-01 14:00:00'), 1: Timestamp('2019-06-01 14:41:32.464000'), 2: Timestamp('2019-06-01 15:00:00'), 3: Timestamp('2019-06-02 10:00:00'), 4: Timestamp('2019-06-02 14:00:00'), 5: Timestamp('2019-06-02 17:00:00'), 6: Timestamp('2019-06-02 17:30:00'), 7: Timestamp('2019-06-03 17:00:00')}, 'ends_at': {0: Timestamp('2019-06-01 22:30:00'), 1: Timestamp('2019-06-01 22:32:44.862000'), 2: Timestamp('2019-06-01 22:30:00'), 3: Timestamp('2019-06-02 18:30:00'), 4: Timestamp('2019-06-03 00:00:00'), 5: Timestamp('2019-06-03 00:00:00'), 6: Timestamp('2019-06-02 21:00:00'), 7: Timestamp('2019-06-03 23:00:00')}} df1 = pd.DataFrame(n1) df2 = pd.DataFrame(n2) # 定义校验函数:检查当前班次是否被某个可用时段完全包含 def is_valid_shift(row): return ((df2['starts_at'] <= row['starts_at']) & (df2['ends_at'] >= row['ends_at'])).any() # 筛选有效班次 result = df1[df1.apply(is_valid_shift, axis=1)] print(result)
结果说明
两种方法都会得到相同的有效班次:
starts_at ends_at 1 2019-06-02 17:00:00 2019-06-02 22:30:00 2 2019-06-02 14:00:00 2019-06-02 22:30:00
这符合你的需求:移除了索引0(上午班次早于可用时间)和索引3(次日上午班次早于当日可用时段开始时间)的冲突班次。
内容的提问来源于stack exchange,提问作者glouis
相关产品推荐
相关产品推荐

