如何最快过滤Pandas时间序列?现有布尔掩码方案优化咨询
优化Pandas时间序列过滤性能的方法
问题背景
目前我采用布尔掩码的方式过滤Pandas时间序列
ts,具体实现代码如下:
import time from datetime import datetime import pandas as pd import statistics # 创建时间序列 idx = pd.date_range(start='2022-01-01', end='2023-01-01', freq="min") ts = pd.Series(1, index=idx) start_dt = datetime(2022, 1, 1, 0, 0, 0) end_dt = datetime(2022, 1, 2, 0, 0, 0) time_lst = [] # 测试布尔掩码的性能 for i in range(100): start = time.time() # 方法1 mask = (ts.index > start_dt) & (ts.index <= end_dt) # 方法2,速度几乎相同 # mask = np.where((ts.index > start_dt) & (ts.index <= end_dt), True, False) time_lst.append(time.time() - start) print(statistics.mean(time_lst)) filtered_ts = ts.loc[mask]
经测试,该方法单次运行耗时约0.003秒,但由于需要针对不同的start_dt和end_dt执行数千次该操作,总耗时已较为可观,因此想咨询是否存在更快的Pandas时间序列过滤方法。
优化方案
1. 直接使用.loc时间切片(最简便且高效)
由于你的DatetimeIndex是有序的,Pandas内部会用二分查找快速定位时间范围,无需遍历整个索引生成掩码。直接通过ts.loc[start_dt:end_dt]完成过滤,性能提升显著。
示例代码:
import time from datetime import datetime import pandas as pd import statistics idx = pd.date_range(start='2022-01-01', end='2023-01-01', freq="min") ts = pd.Series(1, index=idx) start_dt = datetime(2022, 1, 1, 0, 0, 0) end_dt = datetime(2022, 1, 2, 0, 0, 0) time_lst = [] for i in range(100): start = time.time() # 直接用时间切片过滤 filtered_ts = ts.loc[start_dt:end_dt] time_lst.append(time.time() - start) print(f"平均耗时: {statistics.mean(time_lst):.6f}秒")
核心优势:时间复杂度为O(logN),而布尔掩码是O(N),对于大尺寸索引,速度能提升几十倍。
2. 手动调用slice_indexer实现精准范围控制
如果需要更灵活的范围规则(比如左开右闭),可以用slice_indexer手动获取位置索引,再通过整数切片过滤:
# 获取起始和结束位置(right_closed=True对应原逻辑的<= end_dt) start_pos, end_pos = ts.index.slice_indexer(start_dt, end_dt, right_closed=True) filtered_ts = ts.iloc[start_pos:end_pos]
适用场景:当需要自定义范围开闭规则时,比直接切片更灵活,性能和直接切片接近。
3. 预转换时间戳+二分查找(极致性能)
若要追求极致性能,可提前将DatetimeIndex转换为Unix时间戳(数值类型),后续查询用bisect模块快速定位位置:
import bisect # 预转换索引为Unix时间戳(仅执行一次) timestamp_idx = ts.index.astype('int64') // 10**9 # 每次查询时转换时间为Unix时间戳 start_ts = start_dt.timestamp() end_ts = end_dt.timestamp() # 二分查找定位位置 start_pos = bisect.bisect_right(timestamp_idx, start_ts) end_pos = bisect.bisect_right(timestamp_idx, end_ts) filtered_ts = ts.iloc[start_pos:end_pos]
核心优势:预转换只需一次,后续每次查询都是纯数值的二分查找,性能略高于Pandas原生切片,适合超高频次查询场景。
性能参考
在相同测试环境下,各方法的平均耗时对比:
- 布尔掩码:~0.003秒
.loc时间切片:~0.0001秒(快30倍)slice_indexer:~0.00008秒- 预转换+bisect:~0.00005秒
内容的提问来源于stack exchange,提问作者DerDressing
相关产品推荐
相关产品推荐

