如何基于Pandas高效筛选接近00/06/12/18点的时间索引行数据
高效构建可缓存的时间窗口索引方案
针对你的百万行级有序时间序列DataFrame,我们可以利用二分查找和预计算时间窗口索引的方式,构建可重复使用的缓存索引,实现快速查询目标时间窗口内的数据。
核心思路
- 确保时间列是UTC格式的
datetime类型,避免时区问题。 - 生成覆盖整个数据时间跨度的所有目标时间点(00:00z、06:00z、12:00z、18:00z)。
- 对每个目标时间点,计算前后15分钟的时间窗口,利用DataFrame已排序的特性,用
searchsorted快速定位窗口内数据的起始和结束索引(O(log n)复杂度)。 - 缓存这些索引范围,后续查询直接通过索引切片获取数据,无需重复计算。
完整实现代码
import numpy as np import pandas as pd np.random.seed(1) # 生成示例数据(模拟非规则间隔可以打乱部分时间点) rows = 50000 data = np.random.rand(rows) tidx = pd.date_range('2010-01-01', periods=rows, freq='T') # 随机删除10%的行,模拟非规则间隔 drop_idx = np.random.choice(tidx.size, int(rows*0.1), replace=False) tidx = tidx.delete(drop_idx) data = np.delete(data, drop_idx) data_frame = pd.DataFrame({"time": tidx, "value": data}) # 确保时间列是UTC datetime类型 data_frame['time'] = pd.to_datetime(data_frame['time'], utc=True) # ---------------------- 构建缓存索引 ---------------------- def build_time_window_index(df, target_hours=[0,6,12,18], window_minutes=15): # 获取数据的时间范围 min_time = df['time'].min() max_time = df['time'].max() # 生成第一个目标时间点(向上取整到最近的target_hours中的时间) first_target = min_time.floor('D') while first_target.hour not in target_hours: first_target += pd.Timedelta(hours=1) # 如果第一个目标时间比min_time早,就加6小时(因为target_hours间隔6小时) if first_target < min_time: first_target += pd.Timedelta(hours=6) # 生成所有目标时间点(间隔6小时,覆盖到max_time之后的最近目标点) last_target = max_time.ceil('D') while last_target.hour not in target_hours: last_target += pd.Timedelta(hours=1) target_times = pd.date_range(start=first_target, end=last_target, freq='6H', utc=True) # 预计算每个目标时间的窗口索引范围 index_cache = [] time_values = df['time'].values # 提取numpy datetime数组,加速searchsorted window = pd.Timedelta(minutes=window_minutes) for target in target_times: window_start = target - window window_end = target + window # 用searchsorted找窗口的起始和结束索引 start_idx = df['time'].searchsorted(window_start, side='left') end_idx = df['time'].searchsorted(window_end, side='right') if start_idx < end_idx: index_cache.append((start_idx, end_idx)) else: index_cache.append(None) # 无数据的窗口存None return index_cache, target_times # 构建缓存索引 index_cache, target_times = build_time_window_index(data_frame) # ---------------------- 使用缓存索引查询 ---------------------- def get_window_data(df, index_cache, idx): if idx < 0 or idx >= len(index_cache): return pd.DataFrame() # 索引越界返回空DataFrame idx_range = index_cache[idx] if idx_range is None: return pd.DataFrame() start, end = idx_range return df.iloc[start:end].copy() # 测试查询 # index[0]:2010-01-01 00:00前后15分钟的数据 print("index[0]的数据:") print(get_window_data(data_frame, index_cache, 0)) # index[1]:2010-01-01 06:00前后15分钟的数据 print("\nindex[1]的数据:") print(get_window_data(data_frame, index_cache, 1))
关键细节说明
searchsorted的高效性:因为DataFrame的时间列是已排序的,searchsorted通过二分查找定位索引,时间复杂度为O(log n),百万级数据也能瞬间完成。- 缓存复用:
index_cache存储的是每个窗口的起始/结束索引,后续查询直接用iloc切片,无需重复计算时间窗口和索引,性能拉满。 - 处理非规则间隔:不管原始数据是规则还是非规则时间间隔,只要时间列是有序的,该方案都能正常工作。
- 空窗口处理:如果某个目标时间窗口内没有数据,缓存中会存
None,查询时返回空DataFrame,符合需求。
性能优化点
- 提取
df['time'].values为numpy数组,能进一步加速searchsorted的执行速度。 - 如果需要频繁查询,可将
index_cache和target_times保存到本地(比如用pickle),下次直接加载使用,无需重新计算。
内容的提问来源于stack exchange,提问作者user2186862
相关产品推荐
相关产品推荐

