如何不使用for循环,基于员工起止时间DataFrame切片客户交易DataFrame?
无需循环的高效时间区间筛选方案
当然有更高效的实现方式!我们可以利用Pandas的向量化特性和专门的区间索引工具,彻底摆脱Python循环带来的性能瓶颈,尤其适合处理大规模数据集。
核心思路
把所有工作时间区间整合成一个区间索引,然后直接检查客户交易时间是否落在任意一个区间内——全程都是Pandas内部优化的向量化操作,比循环快得多。
具体实现代码
import pandas as pd from datetime import datetime # 你的示例数据 customer_df = pd.DataFrame({'customer': ['A','B','C','A','D','E','K','A','D','F','P','J'], 'location': ['NY','TX','NY','UT','MA','NV','NY','TX','NY','UT','MA','NV']}, index = [datetime(2020,5,1,9), datetime(2020,5,1,11), datetime(2020,5,1,12), datetime(2020,5,1,18), datetime(2020,5,2,5), datetime(2020,5,2,10), datetime(2020,5,2,19), datetime(2020,5,3,2), datetime(2020,5,3,10), datetime(2020,5,3,18), datetime(2020,5,4,20), datetime(2020,5,4,22)]) start_time_df = pd.DataFrame({'start_time':[datetime(2020,5,1,8), datetime(2020,5,2,8), datetime(2020,5,3,5)]}) end_time_df = pd.DataFrame({'end_time':[datetime(2020,5,1,17), datetime(2020,5,2,17), datetime(2020,5,3,20)]}) # 1. 合并起止时间,构建工作区间表 work_shifts = pd.DataFrame({ 'start': start_time_df['start_time'], 'end': end_time_df['end_time'] }) # 2. 创建IntervalIndex,设置closed='both'确保包含区间两端的时间 shift_intervals = pd.IntervalIndex.from_arrays(work_shifts['start'], work_shifts['end'], closed='both') # 3. 筛选出交易时间落在任意工作区间内的记录 filtered_customer_df = customer_df[customer_df.index.isin(shift_intervals)] print(filtered_customer_df)
输出结果
customer location 2020-05-01 09:00:00 A NY 2020-05-01 11:00:00 B TX 2020-05-01 12:00:00 C NY 2020-05-02 10:00:00 E NV 2020-05-03 10:00:00 D NY 2020-05-03 18:00:00 F UT
为什么这个方法更好?
- 性能碾压循环:Pandas的向量化操作是用C实现的,处理十万甚至百万级数据时,速度会比Python循环快几十上百倍
- 代码更简洁:几行代码就完成了循环的逻辑,可读性更强
- 扩展性强:如果后续要增加更多工作区间,只需要更新
start_time_df和end_time_df即可,无需修改核心逻辑
进阶:关联交易对应的工作时段
如果你还需要知道每条交易属于哪个具体的工作时段,可以用merge_asof来实现,同时保留关联关系:
# 重置索引,把交易时间转为列方便关联 customer_with_time = customer_df.reset_index().rename(columns={'index': 'transaction_time'}) work_shifts_with_id = work_shifts.reset_index().rename(columns={'index': 'shift_id'}) # 用merge_asof匹配最近的不晚于交易时间的工作时段 merged = pd.merge_asof( customer_with_time.sort_values('transaction_time'), work_shifts_with_id.sort_values('start'), left_on='transaction_time', right_on='start' ) # 筛选交易时间不超过对应时段结束时间的记录 final_df = merged[merged['transaction_time'] <= merged['end']].set_index('transaction_time') print(final_df[['customer', 'location', 'shift_id']])
这个版本会额外输出每条交易对应的工作时段ID,适合需要做更细粒度分析的场景。
内容的提问来源于stack exchange,提问作者Eiffelbear
相关产品推荐
相关产品推荐

