데이터분석과 개발

[전처리] 시계열 데이터에서 train, validation, test 나누기 본문

AI(시계열)/데이터 전처리

[전처리] 시계열 데이터에서 train, validation, test 나누기

긍정적인마인드 2023. 1. 4. 22:32

* 공부한 것을 정리한 글이므로 틀린 내용이 있을 수 있습니다.

* 더 좋은 방법 또는 틀린부분이 발견될 시 계속 수정하며 업데이트 할 예정입니다.

 

오늘은 train, validation, test 데이터셋을 나누는 방법에 대해 포스팅 해보려 합니다.

일반적인 분류, 회귀 모델과 달리 시간의 흐름을 지켜줘야 하기 때문에 어떻게 보면 더 어렵기도 하고 더 쉽기도 한 것 같습니다. 

 

darts 데이터셋에 있는 'Daily minimum Temperature' 데이터를 사용하여 train validation test 를 나눠보겠습니다.

 

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from darts.datasets import TemperatureDataset
sns.set_context('talk')

df = TemperatureDataset().load().pd_dataframe()

# 5년치 최저온도 데이터셋
df = df.loc[:'1985']
df

 

1. 날짜 구간별 나누기

 

정확한 날짜 구간이 필요하다면 인덱스에 datetime형식의 날짜를 넣고 df.loc[시작날짜: 끝날짜] 를 쓰면 좋습니다.

이때 여러가지 날짜 구하는 모듈이 있지만 저의 경우 dateutil 의 relativedelta 함수를 자주 사용하고 있네요.

각 구간의 날짜를 직접 계산해줘야 합니다.

 

(상당히 번거롭습니다 더 좋은 방법 아시는 분 댓글 달아주시면 감사하겠습니다)

 

train 3년, validation 1년, test 1년으로 나눠주겠습니다.

굳이 다 계산을 해주는 이유는 처음 시작날짜만 바꾸면 자동으로 3년,1년,1년이 나눠질 수 있게 하기 위함입니다.

from dateutil.relativedelta import relativedelta

## train: 1981년부터 3년
train_start_date = '1981-01-01'
train_end_date = str(pd.to_datetime(train_start_date) 
                     + relativedelta(years=3) 
                     - relativedelta(days=1))[:10]

print("train 기간: ",f"{train_start_date}~{train_end_date}")


## validation: 1984년부터 1년
valid_start_date = str(pd.to_datetime(train_end_date)
                       + relativedelta(days=1))[:10]

valid_end_date = str(pd.to_datetime(train_end_date)
                     + relativedelta(years=1) )[:10]

print("valid 기간: ",f"{valid_start_date}~{valid_end_date}")

## test: 1985년부터 1년
test_start_date = str(pd.to_datetime(valid_end_date) 
                      + relativedelta(days=1))[:10]

test_end_date = str(pd.to_datetime(valid_end_date) 
                    + relativedelta(years=1))[:10]

print("test 기간: ",f"{test_start_date}~{test_end_date}")


# train, validation, test split
df_train = df.loc[train_start_date:train_end_date]

df_validation = df.loc[valid_start_date:valid_end_date]

df_test = df.loc[test_start_date:test_end_date]

 

나눠진 결과를 보면 아래와 같은 기간으로 나눠지게 됩니다.

나눠진 데이터프레임을 보면 잘 나뉘어진 것을 볼 수 있습니다.

 

train, validation, test 로 나눈 결과

 

2. 데이터 개수별 나누기 : np.split()

 

만약 데이터비율 혹은 데이터개수(ex.1년 365일) 로 나눠도 상관없다면 

np.split()  을 추천합니다. 

 

# np.split() 사용법
# 데이터갯수가 200개인 df 를 150, 50개의 데이터프레임으로 나누려 한다면

df1, df2 = np.split(df, [150])

# 데이터갯수가 200개인 df 를 100, 50, 50개의 데이터프레임으로 나누려 한다면

df1, df2, df3 = np.split(df, [100, 150])

 

함수 안에 나눠줄 데이터프레임, 데이터 개수만 넣어주면 바로 나눠줄 수 있습니다.

 

# Train 4년 test data 1년
df_Train, df_test = np.split(df, [len(df)-365])

# train 3년, validation, test 를 각각 1년(365개씩)
df_train, df_validation, df_test = np.split(df, [len(df)-365*2, len(df)-365])

 

np.split()함수가 간단한 대신 1년이 365일이 확실한지 확인해야 한다는 단점이 있습니다. 

1984년의 경우 윤년이기 때문에 1984년 1월1일이 제외된 모습을 보실 수 있습니다.

 

np.split()을 사용한 나누기

 

잘 나뉘어 졌는지 시각화로도 확인해보겠습니다. 

 

fig, axs = plt.subplots(nrows=2, figsize=(10,5), constrained_layout=True)
axs[0].plot(df_train.index, df_train['Daily minimum temperatures'], label='train')
axs[0].plot(df_validation.index, df_validation['Daily minimum temperatures'], label='validation')
axs[0].plot(df_test.index, df_test['Daily minimum temperatures'], label='test')
axs[0].set_title("train validation test split")
axs[0].legend()

axs[1].plot(df_Train.index, df_Train['Daily minimum temperatures'], label='Train')
axs[1].plot(df_test.index, df_test['Daily minimum temperatures'], label='test')
axs[1].legend()

 

 

지금까지 train validation test 를 나누는 방법에 대해 포스팅 해보았습니다.

 

기회가 된다면 시계열 검증방법에 대해서도 공부하고 정리해보겠습니다.

 

감사합니다.