如何在Python中拆分含多日交易记录的时间序列用于机器学习交叉验证?
按日期分组的时间序列交叉验证实现
因为你的数据是按天聚合多条记录,直接用TimeSeriesSplit会把同一天的记录拆到训练和验证集里,这不符合时间序列的核心逻辑——训练集必须完全早于验证集。所以解决的核心是按日期维度拆分,再映射回原数据集的对应记录。
方法1:手动实现日期拆分
先提取所有唯一日期并按时间排序,再按需求划分训练/验证的日期区间,最后筛选对应的数据。
示例代码:
import pandas as pd # 假设你的数据集是df,包含'date'列和其他特征、标签 df = pd.read_csv('your_transaction_data.csv') df['date'] = pd.to_datetime(df['date']) # 提取并排序唯一日期 unique_dates = df['date'].dt.date.unique() unique_dates.sort() # 定义交叉验证折数,比如5折 n_splits = 5 split_size = len(unique_dates) // n_splits # 生成每一组训练/验证的日期区间 for i in range(n_splits): # 训练集日期:从起始到第(i+1)*split_size天 train_dates = unique_dates[: (i+1)*split_size] # 验证集日期:下一个split_size天,最后一折取剩余所有日期 val_dates = unique_dates[(i+1)*split_size : (i+2)*split_size] if (i+2)*split_size <= len(unique_dates) else unique_dates[(i+1)*split_size:] # 映射回原数据集 train_df = df[df['date'].dt.date.isin(train_dates)] val_df = df[df['date'].dt.date.isin(val_dates)] # 这里插入你的模型训练逻辑 print(f"第{i+1}折:训练集日期范围{train_dates[0]}至{train_dates[-1]},验证集日期范围{val_dates[0]}至{val_dates[-1]}") print(f"训练集样本数:{len(train_df)},验证集样本数:{len(val_df)}")
方法2:自定义sklearn兼容的交叉验证器
如果要和sklearn的cross_val_score等API配合使用,可以继承BaseCrossValidator实现自定义拆分逻辑,让代码更贴合sklearn生态:
from sklearn.model_selection import BaseCrossValidator import pandas as pd class DateBasedTimeSeriesSplit(BaseCrossValidator): def __init__(self, n_splits=5): self.n_splits = n_splits def get_n_splits(self, X=None, y=None, groups=None): return self.n_splits def split(self, X, y=None, groups=None): # 假设X是包含'date'列的DataFrame unique_dates = X['date'].dt.date.unique() unique_dates.sort() split_size = len(unique_dates) // self.n_splits for i in range(self.n_splits): val_start_idx = (i+1)*split_size val_end_idx = val_start_idx + split_size if (val_start_idx + split_size) <= len(unique_dates) else len(unique_dates) # 训练集取所有早于验证集的日期 train_dates = unique_dates[:val_start_idx] val_dates = unique_dates[val_start_idx:val_end_idx] # 获取训练和验证集的行索引 train_indices = X[X['date'].dt.date.isin(train_dates)].index val_indices = X[X['date'].dt.date.isin(val_dates)].index yield train_indices, val_indices # 使用示例 df = pd.read_csv('your_transaction_data.csv') df['date'] = pd.to_datetime(df['date']) X = df.drop('target', axis=1) y = df['target'] cv = DateBasedTimeSeriesSplit(n_splits=5) for train_idx, val_idx in cv.split(X, y): X_train, X_val = X.iloc[train_idx], X.iloc[val_idx] y_train, y_val = y.iloc[train_idx], y.iloc[val_idx] # 模型训练逻辑 print(f"训练集样本数:{len(X_train)},验证集样本数:{len(X_val)}")
注意事项
- 必须确保
date列是datetime格式,否则无法正确按日期筛选 - 如果需要滚动验证(训练集每次累加之前所有日期,验证集固定窗口),只需调整
train_dates为unique_dates[:val_start_idx]即可 - 最后一折的验证集如果日期数量不足split_size,直接取剩余所有日期即可
内容的提问来源于stack exchange,提问作者andyolivers
相关产品推荐
相关产品推荐

