我想对我的时间序列数据进行交叉验证,并按时间戳的年份进行划分。
这是熊猫数据框中的以下数据:
mock_data
timestamp counts
'2015-01-01 03:45:14' 4
.
.
.
'2016-01-01 13:02:14' 12
.
.
.
'2017-01-01 09:56:54' 6
.
.
.
'2018-01-01 13:02:14' 8
.
.
.
'2019-01-01 11:39:40' 24
.
.
.
'2020-01-01 04:02:03' 30
mock_data.dtypes
timestamp object
counts int64
查看scikit-learn的TimeSeriesSplit()
功能,看来您可以逐年指定n_split
部分。有没有其他方法可以创建连续的训练集,从而导致接下来的训练测试拆分?
tscv = newTimeSeriesSplit(n_splits=5, by='year')
>>> print(tscv)
newTimeSeriesSplit(max_train_size=None, n_splits=5, by='year')
>>> for train_index, test_index in tscv.split(mock_data):
... 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: [2015] TEST: [2016]
TRAIN: [2015 2016] TEST: [2017]
TRAIN: [2015 2016 2017] TEST: [2018]
TRAIN: [2015 2016 2017 2018] TEST: [2019]
TRAIN: [2015 2016 2017 2018 2019] TEST: [2020]
感谢您的观看!
答案 0 :(得分:2)
每年用于任意点数数据的通用方法。
首先,根据示例,一些数据具有几年的数据,每个数据中的点数不同。这与原始答案的方法类似。
import numpy as np
import pandas as pd
ts_2015 = pd.date_range('2015-01-01', '2015-12-31', periods=4).to_series()
ts_2016 = pd.date_range('2016-01-01', '2016-12-31', periods=12).to_series()
ts_2017 = pd.date_range('2017-01-01', '2017-12-31', periods=6).to_series()
ts_2018 = pd.date_range('2018-01-01', '2018-12-31', periods=8).to_series()
ts_2019 = pd.date_range('2019-01-01', '2019-12-31', periods=24).to_series()
ts_2020 = pd.date_range('2020-01-01', '2020-12-31', periods=30).to_series()
ts_all = pd.concat([ts_2015, ts_2016, ts_2017, ts_2018, ts_2019, ts_2020])
df = pd.DataFrame({'X': np.random.randint(0, 100, size=ts_all.shape),
'Y': np.random.randint(100, 200, size=ts_all.shape)},
index=ts_all)
df['year'] = df.index.year
df = df.reset_index()
现在,我们创建要迭代的唯一年份的列表以及存储各种拆分数据帧的命令。
year_list = df['year'].unique().tolist()
splits = {'train': [], 'test': []}
for idx, yr in enumerate(year_list[:-1]):
train_yr = year_list[:idx+1]
test_yr = [year_list[idx+1]]
print('TRAIN: ', train_yr, 'TEST: ',test_yr)
splits['train'].append(df.loc[df.year.isin(train_yr), :])
splits['test'].append(df.loc[df.year.isin(test_yr), :])
结果:
TRAIN: [2015] TEST: [2016]
TRAIN: [2015, 2016] TEST: [2017]
TRAIN: [2015, 2016, 2017] TEST: [2018]
TRAIN: [2015, 2016, 2017, 2018] TEST: [2019]
TRAIN: [2015, 2016, 2017, 2018, 2019] TEST: [2020]
分割后的数据帧如下所示:
>>> splits['train'][0]
index X Y year
0 2015-01-01 00:00:00 20 127 2015
1 2015-05-02 08:00:00 25 197 2015
2 2015-08-31 16:00:00 61 185 2015
3 2015-12-31 00:00:00 75 144 2015
有人向我指出,这种方法行不通,因为它假定每年包含相同数量的记录。
您的意图还不清楚,但是我相信您要做的是将带有时间戳索引的 dataframe 传递到{{1} }类,将根据您的数据年数产生TimeSeriesSplit
。 n_split = n_years - 1
类为您提供了执行此操作的灵活性,但是您需要首先从时间戳索引中提取年份。结果看起来不太像您建议的那样,但是我相信结果就是您想要的。
首先是一些伪数据:
TimeSeriesSplit
import numpy as np
import pandas as pd
from sklearn.model_selection import TimeSeriesSplit
ts_index = pd.date_range('2015-01-01','2020-12-31',freq='M')
df = pd.DataFrame({'X': np.random.randint(0, 100, size=ts_index.shape),
'Y': np.random.randint(100, 200, size=ts_index.shape)},
index=ts_index)
可以使用一年了。因为我们必须按行号索引该事物,并且不建议使用TimeSeriesSplit
,所以将索引从时间戳重置为数值:
pd.ix
然后是一个df['year'] = df.index.year
df = df.reset_index()
实例,该实例具有正确的分割数(TimeSeriesSplit
):
n_years - 1
现在我们可以生成索引。不用打印索引,而是打印相应的年份列,只打印唯一的年份:
tscv = TimeSeriesSplit(n_splits=len(df['year'].unique()) - 1)
for train_idx, test_idx in tscv.split(df['year']):
print('TRAIN: ', df.loc[df.index.isin(train_idx), 'year'].unique(),
'TEST: ', df.loc[df.index.isin(test_idx), 'year'].unique())
您当然会以类似的方式访问您的训练/测试集。如果您真的想很好地解决这个问题,则可以扩展TRAIN: [2015] TEST: [2016]
TRAIN: [2015 2016] TEST: [2017]
TRAIN: [2015 2016 2017] TEST: [2018]
TRAIN: [2015 2016 2017 2018] TEST: [2019]
TRAIN: [2015 2016 2017 2018 2019] TEST: [2020]
类,并自定义初始化或添加一些新方法。