scikit-learn中针对纵向/面板数据的交叉验证

3

我有一些纵向/面板数据,采用以下形式(数据输入的代码在问题下方)。 X和y的观测值按时间和国家进行索引(例如,时间1的美国,时间2的美国,时间1的加拿大)。

    time x  y
USA 1    5  10
USA 2    5  12
USA 3    6  13
CAN 1    2  2
CAN 2    2  3
CAN 3    4  5

我正在使用sklearn来预测y。为了得到可重复的示例,我们可以使用线性回归。

为了进行交叉验证,我不能使用test_train_split,因为这样切分可能会把time=3的数据放入X_train中,而将time=2的数据放入y_test中。这是无助于我们的,因为在time=2时,我们尝试预测y时,实际上还没有在time=3上的数据用于训练。

我试图使用TimeSeriesSplit来实现交叉验证,如下图所示:

enter image description here (来源:https://stats.stackexchange.com/questions/14099/using-k-fold-cross-validation-for-time-series-model-selection)

y = df.y
X = df.drop(['y'], 1)
print(y)
print(X)

from sklearn.model_selection import TimeSeriesSplit

X = X.to_numpy()

from sklearn.model_selection import TimeSeriesSplit
tscv = TimeSeriesSplit(n_splits = 2, max_train_size=3)
print(tscv)
for train_index, test_index in tscv.split(X):
    print("TRAIN:", train_index, "TEST:", test_index)
    X_train, X_test = X[train_index], X[test_index]
    y_train, y_test = y[train_index], y[test_index]

我需要的差不多就是这样,但还不完全一样:

TRAIN: [0 1] TEST: [2 3]
TRAIN: [1 2 3] TEST: [4 5]
  • 我如何使用TimeSeriesSplit索引对模型进行交叉验证?

我认为一个困难在于我的数据不是严格的时间序列:它不仅按time索引,还按country索引,因此数据具有纵向/面板的性质。

我的期望输出是:

  1. 一系列的测试和训练索引,允许我执行“前向逐步”交叉验证

例如:

TRAIN: [1] TEST: [2]
TRAIN: [1 2] TEST: [3]
  1. 根据上述索引值,基于time的值,将X_trainx_testy_testy_train拆分成训练集和测试集,或者确保是否需要这样做。

  2. 使用“前向步进”交叉验证方法对任何模型(例如线性回归)进行精度评分。

import numpy as np
import pandas as pd

data = np.array([['country','time','x','y'],
                ['USA',1, 5, 10],
                ['USA',2, 5, 12],
                ['USA',3,6, 13],
                ['CAN',1,2, 2],
                ['CAN',2,2, 3],
                ['CAN',3,4, 5]],                
               )
                
df = pd.DataFrame(data=data[1:,1:],
                  index=data[1:,0],
                  columns=data[0,1:])

df

1
将您的 X DataFrame 转换为 numpy 对象,可以使用 X.to_numpy()。 - sabacherli
@sabacherli 那样解决了错误!非常感谢您。也许我会编辑问题,以便我可以请求关于如何使用您刚刚帮助我获取的时间序列分割索引实现时序交叉验证的帮助,如果可以的话? - Jeremy K.
1个回答

2
< p > TimeSeriesSplit 假设您的数据集按时间索引,这意味着每行属于不同的时间步骤。因此,为什么不将数据进行unstack,使您只有时间作为索引,然后再进行拆分。拆分后,您可以再次stack数据形状以获取训练的底层表格。

data = np.array([['country','time','x','y'],
                ['USA',1, 5, 10],
                ['USA',2, 5, 12],
                ['USA',3,6, 13],
                ['CAN',1,2, 2],
                ['CAN',2,2, 3],
                ['CAN',3,4, 5]],                
               )

df = pd.DataFrame(data=data[1:,1:],
                  index=data[1:,0],
                  columns=data[0,1:])

df1 = df.reset_index().set_index(['time','index']).unstack(-1)
print(df1)

        x       y    
index CAN USA CAN USA
time                 
1       2   5   2  10
2       2   5   3  12
3       4   6   5  13

现在,由于每一行都是按时间索引的,所以您可以轻松地将此数据拆分成组,然后在拆分后重新堆叠以获取您的X_train、X_test等...

from sklearn.model_selection import TimeSeriesSplit
tscv = TimeSeriesSplit(n_splits = 2, max_train_size=3)

X_cols = ['time', 'index', 'x']
y_cols = ['y']

for train_index, test_index in tscv.split(df1):
    print("TRAIN:", train_index, "TEST:", test_index)
    X_train, X_test = df1.iloc[train_index].stack(-1).reset_index()[X_cols].to_numpy(), df1.iloc[test_index].stack(-1).reset_index()[X_cols].to_numpy()
    y_train, y_test = df1.iloc[train_index].stack(-1).reset_index()[y_cols].to_numpy(), df1.iloc[test_index].stack(-1).reset_index()[y_cols].to_numpy()

TRAIN: [0] TEST: [1]
TRAIN: [0 1] TEST: [2]

你可以打印最新文件夹的X_train和y_train来查看正在发生的情况 -
print('For - TRAIN: [0 1] TEST: [2]')
print(" ")
print("X_train:")
print(X_train)
print(" ")
print("X_test:")
print(X_test)
print(" ")
print("y_train:")
print(y_train)
print(" ")
print("y_test:")
print(y_test)
print("X_train:")
print(X_train)
print(" ")
print("X_test:")
print(X_test)
print(" ")
print("y_train:")
print(y_train)
print(" ")
print("y_test:")
print(y_test)

For - TRAIN: [0 1] TEST: [2]

X_train:
[['1' 'CAN' '2']
 ['1' 'USA' '5']
 ['2' 'CAN' '2']
 ['2' 'USA' '5']]
 
X_test:
[['3' 'CAN' '4']
 ['3' 'USA' '6']]
 
y_train:
[['2']
 ['10']
 ['3']
 ['12']]
 
y_test:
[['5']
 ['13']]

现在您可以按时间拆分数据框,并将其扩展回需要进行训练的形状。


2
下一步,一旦我有时间序列拆分,我是否可以像这样将其传递给GridSearchCVGridSearchCV(my_pipeline, param_grid, cv = tscv.split(df1), ...)?这是我目前正在做的事情,我想确认一下这是否有意义。再次感谢您。 - Jeremy K.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接