Обновленный ответ
Общий подход для данных с произвольным числом точек в каждом году.
Во-первых, некоторые данные с данными за несколько лет с различным числомочков в каждом, в соответствии с примером.Это похоже на исходный ответ.
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
Исходный ответ
Мне было указано, что этот подходне будет работать, потому что предполагается, что каждый год содержит одинаковое количество записей.
Ваше намерение немного неясно, но я считаю, что вы хотите сделать, это передать фрейм данных с индексом отметки времени в новую версию класса 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]
Вы, конечно, получите доступ к своим тренировочным / тестовым наборам аналогичным образом.Если вы действительно хотите, чтобы это было удобно, вы можете расширить класс TimeSeriesSplit
и либо настроить инициализацию, либо добавить несколько новых методов.