시계열 데이터 분석
- 시계열 예측(Times series forecasting)
- 시계열 데이터: 시간의 흐름에 따른 데이터
- 시간 순서대로 수집하거나 정렬한 순차적인(sequential) 데이터를 활용하여 미래 시점의 상태를 예측
- eg. 일별 COVID-19 확진자 수 데이터를 통해 앞으로의 확진자 수를 예측 - 적용: 방역 정책을 수립
- 시계열 예측의 특징
- 완벽한 예측 불가능
- 다변량(multivariate) data: 변수가 여러개
- e.g. 의료 데이터에서 환자의 여러 생리학적 변수(혈압, 심박수, 혈당 농도 등)가 함께 측정되는 경우
- e.g. 여러 지표(온도, 습도, 바람 속도 등)가 한 지역에서 동시에 관측되는 경우가 다변량 데이터의 예
- 데이터 set 구성 변형은 어려움
- 데이터가 정상성(stationarity)을 띨 때 시계열 예측 정확도가 높음
- 시간 종속성(Time dependence)
- 시간 순서대로 배열 = 데이터 포인트가 시간적으로 서로 종속적인 관계를 지님
- 과거의 데이터가 미래를 예측
- 불규칙성(Irregularity)
- 데이터에 노이즈로 작용하는 예측할 수 있는 변동성이 있음
- 외부 event 등 예측 불가능한 변화에 의해 발생
- 계절성(Seasonality)
- 일정 기간을 주기로 반복되는 패턴
- e.g. 겨울-난방수요, 여름-냉방수요
- 추세(Trend)
- 데이터에서 장기간에 걸쳐 나타나는 상승 또는 하락 경향
- e.g. 인구 수
- 주기성(Cyclic)
- 고정된 기간이 아닌 불규칙적인 간격으로 발생
- e.g. 경제 호황/불황기
- 정상성(Stationarity)
- 통계적 속성(평균, 분산, 공분산 등)이 시간에 따라 일정하게 유지되는 성질 = 상승/하락 등 특정 추세가 없는 것
- 시간에 따라 평균(mean)과 분산(variance)이 일정하고, 시간 지연에 따른 자기 공분산(auto-covariance)이 시점에 의존하지 않는 성질
- 즉 시계열 데이터가 일정한 패턴을 가지고 시간이 지나도 변하지 않는다는 것을 의미
- 시계열 분석의 대부분이 데이터의 정상성을 가정하에 수행
= 정상성이 없는 데이터는 변환 후에 분석해야 정확도가 높음 (?) - 정상성을 갖추지 못했다면 변환 필요 (로그 변환 등)
실습) 시계열 데이터 Tesla 주가 데이터
- yfinance는 Yahoo Finance(주식 및 금융 데이터를 쉽게 가져올 수 있게 해주는 파이썬 라이브러리)에서 Tesla 주가 받기
import yfinance as yfimport pandas as pdimport matplotlib.pyplot as plttsla = yf.download('TSLA', start='2018-01-01', end='2024-06-26')tsla - 시각화
df_tsla = pd.DataFrame(tsla['Close'])df_tsla.plot(figsize=(12.2, 6.4)) - ADF(Augmented Dickey-Fuller) 검정
print('ADF test with TSLA time-series')ADF_result = adfuller(df_tsla.values)#ADF 통계량print('ADF Stats: %f' % ADF_result[0])#p-값print('p-value: %f' % ADF_result[1])#임계값print('Critical values:' )for key, value in ADF_result[4].items():print('\t%s: %.4f' % (key, value))
- yfinance는 Yahoo Finance에서 데이터를 가져오기 위해 만들어진 파이썬 라이브러리
- 이 라이브러리는 Yahoo Finance API를 사용하여 주식 가격, 재무 보고서 등 다양한 금융 데이터를 쉽게 가져올 수 있게 해줌 (진짜 신기!!)
실습) Multivariate.ipynb
시계열 데이터 분석기법 (가볍게 이런게 있다하고 넘어가기)
- ARIMA(Auto Regulation Integrated Moving Average)
- 시계열 데이터의 특성을 설명하고 예측하는 데 도움을 주는 통계적 모델
실습) 광주광역시 기온 데이터를 이용한 기온예측 모델링
- 모델링 목적
- 30일치의 기온을 입력값으로 사용하여 다음날 31일째 기온을 예측하는 딥러닝 모델을 만들기
- 설명변수(x): 1~30일 동안의 기온
- 목표변수(y): 31일의 기온
- 모델 종류: 회귀분류
- 딥러닝 모델 조건: 출력노드 1개(31일 기온의 예측값)/ 입력노드: 30개(30일 간의 기온값)
- 데이터 불러오기
import pandas as pdimport numpy as npimport matplotlibimport matplotlib.pyplot as pltimport seaborn as sns
# 데이터 불러오기df = pd.read_csv('/content/temp_mean_gwangju.csv', encoding = 'utf-8', index_col=0) #index_col=0 첫번째열(0번 열)을 인덱스로 지정df.head() - 데이터 탐색
- EDA-Dataframe 탐색
# Dataframe의 첫번째 행(row) 파악df.iloc[0]# 열이름 확인df.columnsdf.info()help(df.info())# 기초통계량 확인df.describe()# count: 비어있지않은 data의 개수# 25/ 50/ 75%: 사분위 값 - 사분위값
- EDA-Dataframe 탐색
- 데이터 전처리
- 결측치 확인
df.isnull()df.isnull().values.sum() #null의 개수# 결측치 시각화plt.figure(figsize=(15,5))sns.heatmap(df.isnull(), cbar=False,);# cbar 컬러 바(color bar) 표시 여부# shift-tab 단축키를 이용해서 함수의 독스트링 확인plt.show()#결측치를 흰색으로, 결측치가 아닌 값을 검은색- 결측치 채우기
# 원본 data를 copy해서 결측치 채우기df_fillna = df.copy()# ffil=forward fill, 앞의 값(전날)을 가져와서 채우라# 온도의 경우 전일과 온도 유사할 것df_fillna.fillna(method='ffill').tail(3) - Data transpose(행열 변환): 특정 변수에 대해 분석하기 위함
df.values.shape# 행열 변환df.T.values.shape - 차원 변경
# 2차원 -> 1차원으로 만들기# 월 별로 2차원으로 array가 되어있으니 1차원으로 변경 (x: 30일치 값 넣고 y: 31일 값 나오고 해야하니)# 이미지(해상도: e.g. 2560 * 1440: 가로/세로의 pixel 수)도 2차원 값# 결측치 제거temp_include_nan = df.T.values.flatten()temp_include_nan.shapetemp = temp_include_nan[~np.isnan(temp_include_nan)]# temp_include_nan 배열에서 결측치가 아닌 값을 선택해서 temp 변수에 할당하는 코드# np.isnan(temp_include_nan): temp_include_nan 배열에서 각 원소가 결측치인지 아닌지 판별하는 조건# ~: not의미# ~np.isnan(temp_include_nan): 결측치가 아니면 True, 맞으면 False로 반환# temp_include_nan[~np.isnan(temp_include_nan)]: temp_include_nan 에서 결측치가 아닌(True) 값으로 채우는 새로운 temp를 생성
- 결측치 채우기
- 결측치 확인
- 모델링
import tensorflowfrom tensorflow.keras.models import Sequentialfrom tensorflow.keras.layers import Dense# input: 30일steps = 30
# 예측기간: 15일로 설정forecast_terms = 15#temp: 원본 시계열 데이터 (일반적으로 Pandas DataFrame이나 Numpy 배열)#steps: 각 입력 샘플이 포함할 시계열 데이터의 길이 (슬라이딩 윈도우의 크기)#forecast_terms: 예측하려는 미래 시계열 데이터의 길이# 학습 데이터 생성x_train = []for i in range(len(temp) - forecast_terms - steps):x_train.append(temp[i:i+steps])# for 반복문을 통해 temp에서 학습 데이터의 입력 데이터인 연속된 30일치 데이터를 추출해서 x_train 리스트에 추가 # len(temp): temp의 길이, temp 리스트에 포함된 데이터의 개수를 의미# forecast_terms: 예측 기간으로 설정한 값# steps: 입력기간으로 설정한 값x_train = np.array(x_train)y_train = temp[steps:len(temp)-forecast_terms]# temp 리스트에서 예측기간 이후(step 이후)부터 예측기간 전(len(temp)-forecase_terms)까지의 데이터를 y_train으로 설정
# 테스트 데이터 생성x_test = []for i in range(forecast_terms):x_test.append(temp[-(steps+forecast_terms)+i:-forecast_terms+i])
x_test = np.array(x_test)y_test = temp[-forecast_terms:]
print("x_train:\n", x_train)print("\ny_train:", y_train)print("\nx_test:\n", x_test)print("\ny_test:", y_test)
input_node = 30
output_node = 1model_simple = Sequential()model_simple.add(Dense(output_node, input_shape=(input_node, )))# Dense=fully connected network=모든 노드가 연결된 것model_simple.compile(loss="mse", optimizer = 'adam')model_simple.summary()model_simple_hist = model_simple.fit(x_train,y_train,epochs=10,batch_size=100,validation_split=0.2) # hist=histogram - 단층 신경망 설
input_node = 30
output_node = 1model_simple = Sequential()model_simple.add(Dense(output_node, input_shape=(input_node, )))# Dense=fully connected network=모든 노드가 연결된 것model_simple.compile(loss="mse", optimizer = 'adam')model_simple.summary()model_simple_hist = model_simple.fit(x_train,y_train,epochs=10,batch_size=100,validation_split=0.2) # hist=histogram - 성능평가를 위한 시각화
plt.plot(model_simple_hist.history["loss"], label="train_loss") # 손실함수 값plt.plot(model_simple_hist.history["val_loss"], label="val_loss")# 검증손실함수# 모델이 학습에 사용되지 않은 검증 데이터에 대해 예측한 값과 실제값 사이의 차이# 학습과정 중 모델의 성능 평가를 위해 사용plt.xlabel("epoch")plt.ylabel("loss")plt.legend(); - 정확도 분석
model_simple.predict(x_test)pred_simple = model_simple.predict(x_test).flatten()acc = np.sum(np.abs(y_test - pred_simple) <= 0.5)/len(y_test)acc_train = np.sum(np.abs(y_train - model_simple.predict(x_train).flatten()) <= 0.5)/len(y_train)
print("단층 신경망 테스트 데이터 정확도: ", np.round(acc, 2),"%")print("단층 신경망 훈련 데이터 정확도: ", np.round(acc_train, 2),"%")
# 초기 가중치 랜덤으로 정확도가 돌릴때마다 다름 -> random seed를 정하든지, 계속 돌리든지하면 같아짐 - 정확도 시각화
plt.figure(figsize=(15,5))plt.plot(y_test, c="C0", marker="o", label="real")plt.plot(pred_simple, c="C1", marker="s", label="predict")plt.xticks(range(15), labels=list(range(17,32)))plt.legend();plt.figure(figsize=(15,5))plt.plot(y_test, c="C0", marker="o", label="real")
#정답 범위plt.fill_between(range(15), y_test-0.5, y_test+0.5, color="C0", alpha=0.3)
plt.plot(pred_simple, c="C1", marker="s", label="predict")plt.xticks(range(15), labels=list(range(17,32)))plt.legend();