如何高效将DataFrame按时间间隔拆分为子DataFrame列表(替代循环)
按时间间隔拆分DataFrame的高效实现方法
问题背景
现有如下结构的esu_tos DataFrame:
Time TickType Price Size 2023-08-13 15:10:46.166 1 4487.25 1 2023-08-13 15:10:47.375 1 4487 1 2023-08-13 15:10:54.656 1 4487.25 2 2023-08-13 15:10:57.627 1 4487 1 2023-08-13 15:10:57.628 1 4487 1 2023-08-13 15:10:57.628 1 4487 1 2023-08-13 15:11:00.759 1 4487.25 1 2023-08-13 15:11:00.759 1 4487 3 2023-08-13 15:11:01.415 1 4487 3 2023-08-13 15:11:01.416 1 4487 1
需要按指定时间间隔(如5s/1min等)拆分为子DataFrame列表,替代原有的循环切片逻辑,且最终要得到可单独处理的子数据集,而非单纯的groupby对象。
原循环实现逻辑:
interval = '5s' # 1min / 30s / 10s / 5s st = datetime(2023,8,13,15,10) end = datetime(2023,8,14,14,1) rng = pd.date_range(st, end, freq = interval, inclusive='both') d = [] d_t = [] for i, k in enumerate(rng): try: a = esu_tos[(esu_tos.Time >= k) & (esu_tos.Time < rng[i+1])] except: a = esu_tos[(esu_tos.Time >= k) & (esu_tos.Time <= end)] d.append(a) d_t.append(k)
高效替代方案
方案1:利用pd.cut+groupby生成子DataFrame列表
通过矢量化操作给每条数据标记所属时间区间,再分组提取子数据集,避免循环带来的性能损耗:
import pandas as pd from datetime import datetime # 确保Time列为datetime类型 esu_tos['Time'] = pd.to_datetime(esu_tos['Time']) interval = '5s' st = datetime(2023,8,13,15,10) end = datetime(2023,8,14,14,1) # 生成时间区间边界 bins = pd.date_range(start=st, end=end, freq=interval, inclusive='both') # 给每条数据分配对应的区间标签 esu_tos['interval'] = pd.cut(esu_tos['Time'], bins=bins, include_lowest=True) # 按区间分组,转换为子DataFrame列表 d = [group for _, group in esu_tos.groupby('interval')] # 提取各区间的起始时间 d_t = [interval_label.left for interval_label in esu_tos['interval'].cat.categories]
优势:
- 基于pandas矢量化操作,数据量越大,性能优势越明显
- 自动处理区间边界逻辑,无需手动捕获
IndexError处理最后一个区间
方案2:保留所有区间(含空数据区间)
如果需要和原循环逻辑完全一致,保留所有时间区间(即使区间内无数据,返回空DataFrame),可以补充如下逻辑:
# 生成所有待处理的区间起始时间 all_interval_starts = bins[:-1] # 构建包含所有区间的子DataFrame列表 d = [] d_t = [] for start_time in all_interval_starts: # 计算对应区间的结束时间 end_time = start_time + pd.Timedelta(interval) # 切片筛选数据 sub_df = esu_tos[(esu_tos['Time'] >= start_time) & (esu_tos['Time'] < end_time)] d.append(sub_df) d_t.append(start_time) # 单独处理最后一个区间,确保覆盖到指定end时间 last_sub_df = esu_tos[(esu_tos['Time'] >= all_interval_starts[-1]) & (esu_tos['Time'] <= end)] d[-1] = last_sub_df
这个方案去掉了原循环中的try-except,通过直接计算区间结束时间实现逻辑,代码更清晰可控。
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

