簡體   English   中英

如何按年份創建時間序列數據的訓練/測試拆分?

[英]How to create a train/test split of time-series data by year?

我想交叉驗證我的時間序列數據並按時間戳的年份進行拆分。

這是 pandas dataframe 中的以下數據:

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() function ,您似乎無法按年指定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]

感謝觀看!

更新的回應

每年具有任意點數的數據的通用方法。

首先,根據示例,一些數據包含幾年的數據,每個數據的點數不同。 這與原始答案的方法相似。

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傳遞到新版本的TimeSeriesSplit class 中,這將根據數據中的年數產生n_split = n_years - 1 TimeSeriesSplit class 為您提供了執行此操作的靈活性,但您需要先從時間戳索引中提取年份。 結果看起來不像你提議的那樣,但我相信結果是你想要的。

首先是一些虛擬數據:

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()

然后是具有正確拆分數( n_years - 1 )的TimeSeriesSplit實例:

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 class 並自定義初始化或添加一些新方法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM