AI 개발 공부 공간

AI, 머신러닝, 딥러닝, Python & PyTorch, 실전 프로젝트

딥러닝/딥러닝: 실전 프로젝트 학습

시계열 : 농산물 가격 예측 프로젝트 2 (데이터 분석과 시각화)

qordnswnd123 2025. 1. 30. 17:06

1. 데이터 로드

import pandas as pd
import numpy as np

train = pd.read_csv('train.csv')

2. train 데이터 정보 확인

train.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 2093 entries, 0 to 2092
Data columns (total 17 columns):
 #   Column          Non-Null Count  Dtype  
---  ------          --------------  -----  
 0   date            2093 non-null   object 
 1   배추_거래량(kg)      2093 non-null   float64
 2   배추_가격(원/kg)     2093 non-null   float64
 3   무_거래량(kg)       2093 non-null   float64
 4   무_가격(원/kg)      2093 non-null   float64
 5   깻잎_거래량(kg)      2093 non-null   float64
 6   깻잎_가격(원/kg)     2093 non-null   float64
 7   시금치_거래량(kg)     2093 non-null   float64
 8   시금치_가격(원/kg)    2093 non-null   float64
 9   토마토_거래량(kg)     2093 non-null   float64
 10  토마토_가격(원/kg)    2093 non-null   float64
 11  청상추_거래량(kg)     2093 non-null   float64
 12  청상추_가격(원/kg)    2093 non-null   int64  
 13  캠벨얼리_거래량(kg)    2093 non-null   float64
 14  캠벨얼리_가격(원/kg)   2093 non-null   int64  
 15  샤인마스캇_거래량(kg)   2093 non-null   float64
 16  샤인마스캇_가격(원/kg)  2093 non-null   int64  
dtypes: float64(13), int64(3), object(1)
memory usage: 278.1+ KB

 

※ 결과 해석

총 2093개의 행과 17개의 열로 구성되어 있습니다.

날짜 열 외에 각 농산물 품목의 거래량과 가격정보가 포함되어 있으며 결측값이 없습니다.

대부분의 열은 부동소수점(float64) 형식이며, 3개 품목(청상추, 캠벨얼리, 샤인마스캇)의 가격은 정수(int64)입니다.

 


3. date 컬럼을 datetime 타입으로 변경

train['date'] = pd.to_datetime(train['date'])
display(train['date'].info())
<class 'pandas.core.series.Series'>
RangeIndex: 2093 entries, 0 to 2092
Series name: date
Non-Null Count  Dtype         
--------------  -----         
2093 non-null   datetime64[ns]
dtypes: datetime64[ns](1)
memory usage: 16.5 KB
 

4. 특정 품목 가격에 대한 시계열 그래프 탐색

  • 파이썬 f-string 포맷팅을 이용하여 선택한 품목 item_selected에 대한 시계열 그래프를 그려 보겠습니다.
  • 마지막 100개의 데이터 train_last에 대한 시계열 그래프도 그려 봄으로써 확대된 이미지를 관찰 해 보겠습니다.
import matplotlib.pyplot as plt

item_selected = '배추'

train_last = train.tail(100)

plt.figure(figsize=(12, 4))

plt.subplot(1,2,1)
plt.plot(train['date'], train[f"{item_selected}_가격(원/kg)"])
plt.title(f"{item_selected}_가격(원/kg) 시계열 그래프")
plt.xlabel('날짜')
plt.ylabel(f"{item_selected}_가격(원/kg)")

plt.subplot(1,2,2)
plt.plot(train_last['date'], train_last[f'{item_selected}_가격(원/kg)'])
plt.title(f"{item_selected}_가격(원/kg) 시계열 그래프 : {train_last['date'].min().date()} ~ {train_last['date'].max().date()}")
plt.xlabel('날짜')
plt.ylabel(f"{item_selected}_가격(원/kg)")

plt.tight_layout()
plt.show()

 

※ 결과 해석

그래프를 통해 품목 가격이 마치 노이즈처럼 보이는 패턴을 관찰해 보면, "주기적"으로 "0"의 값을 가지는 것을 확인할 수 있습니다. 이 값이 '가격' 정보이기 때문에, 이상치라고는 볼 수 없는 비정상적인 데이터라고 간주하고, 0 데이터가 존재하는 이유를 파악하는 것이 중요하며, 향후 어떻게 데이터 전처리를 해야 할 것인지 생각해야 합니다.

 


5. 가격 정보에서 0 값을 이전 값으로 대체

이 코드는 다양한 품목의 가격 정보가 담긴 train 데이터프레임에서 0 값이 있는 경우, 그 값을 이전 행의 값으로 대체합니다. 이를 통해 주기적으로 나타나는 0 값들을 제거하여 데이터의 왜곡을 방지합니다.

  • 파이썬 리스트 컴프리헨션(list comprehension)문법을 사용하여 train_prep의 컬럼 이름에서 '가격'이 포함된 모든 컬럼을 추출하여 price_colummns 리스트로 할당해 보겠습니다.
  • 가격 정보에서 0가 포함된 요소를 replace 함수와 method 매개변수를 적절하게 설정하여 '이전' 가격으로 대체합니다.여기서 inplace 매개변수를 True로 설정함으로써 데이터프레임 자체를 수정하도록 하겠습니다.
train_prep = train.copy()

price_columns = [col for col in train_prep.columns if '가격' in col]

for col in price_columns:
    train_prep.replace(0, method = 'ffill', inplace=True)

display(train.head())
display(train_prep.head())

 

※ 코드 해석

replace(0, method='ffill'): 이 함수는 데이터프레임 내의 0 값을 이전 행의 값으로 대체합니다.
0: 대체할 값입니다. method='ffill': 이전 행의 값으로 대체하는 방법을 정의합니다. 'ffill'은 'forward fill'의 약자로, 이전 행의 값으로 채워준다는 의미입니다.

inplace=True: 원본 데이터프레임을 직접 수정하겠다는 의미입니다.

가격 정보를 대상으로 하기 위해 _가격이 포함된 열만 선택하여 작업을 수행하였습니다.

 

※ 결과 해석

원본 train 데이터와 전처리 후의 train_prep 데이터를 비교하면, train_prep에서는 원래의 값이 유지되도록 동일하게 이전 행의 값으로 대체되어 있습니다. 이는 시계열 분석에 있어 모델링에서 데이터의 왜곡을 줄일 수 있습니다. 예를 들어, 가격이 0인 경우를 그대로 두면 평균 가격이 실제보다 낮게 계산될 수 있으므로, 이러한 전처리는 매우 유용합니다.

그러나 첫 번째 행의 경우에는 여전히 0 값이 남아 있습니다. 이는 method='ffill' 옵션이 이전 행의 값을 사용하여 0을 대체하기 때문에, 첫 번째 행에는 이전 행이 없어서 그렇습니다.


6. 각 품목의 가격, 거래량에 대한 시계열 그래프 탐색

※ 분석 관점

트렌드(Trend): 상승 또는 하락하는 경향을 확인하는 이유는 이러한 추세를 무시하고 모델을 만들면, 예측 오차가 클 가능성이 높아지기 때문입니다. 예를 들어, 연도별로 상승하는 주가 추세를 무시하고 단기 변동만 예측한다면 장기적인 투자에서는 손해를 볼 수 있습니다.

계절성(Seasonality): 계절성은 특정 시간 동안 반복되는 패턴을 말합니다. 예를 들어, 여름에는 에어컨 판매량이 증가하는 것처럼, 이러한 계절성을 알고 있으면 보다 정확한 예측이 가능합니다. 또한, 계절성을 제거하지 않으면 트렌드와 혼동될 수 있어 정확한 분석이 어렵습니다.

이상치(Outliers): 이상치는 예측 모델을 왜곡할 수 있기 때문에 반드시 확인해야 합니다. 이상치가 발견되면 왜 그런 현상이 발생했는지 파악하고, 처리 방법을 결정해야 합니다. 예를 들어, 코로나19와 같은 팬데믹 상황이 지속 발생될 시점이면 이를 무시하면 잘못된 예측을 할 가능성이 높아집니다.

노이즈(Noise): 노이즈는 작은 무작위 변동을 의미하며, 이를 제거하면 모델이 일반적인 패턴을 학습하는 데 유리하여 예측 성능이 향상될 수 있습니다.

이상치와 결측치: 특정 값이 비정상적으로 크거나 도메인에서 존재할 수 없는 값이 관찰되면 이는 데이터 수집 과정에서 오류일 수 있으며, 이를 그대로 두면 모델 학습에 부정적인 영향을 미칠 수 있습니다.

이러한 관점을 적용하면 단순히 데이터를 '받아들일' 수도 있을 수 있고, 이상치를 제거할 수도 있으며, 또는 새로운 변수로 추가할 수도 있습니다. 따라서 데이터를 사전에 분석하고 모델링 과정에서 적절한 영향을 미칠 수 있도록 해야 하며, 이를 무시하면 모델 성능에 직접적인 영향을 미칠 수 있습니다.

import matplotlib.pyplot as plt

plt.figure(figsize=(16, 20))

for i, feature in enumerate(train_prep.columns[1:]):
    plt.subplot(8,2,i+1)
    plt.plot(train_prep['date'], train_prep[feature])
    plt.title(feature + '시계열 그래프')
    plt.xlabel('날짜')
    plt.ylabel(feature)

plt.tight_layout()
plt.grid(True)
plt.show()

 


7. 특정 품목의 이동평균(MA)과 지수이동평균(EMA) 계산 및 시각화

선택한 날짜 범위 내에서 품목의 가격 데이터에 대해 7일 이동평균(MA)과 지수이동평균(EMA)을 계산하고 이를 시각화합니다. 이를 통해 가격의 단기적인 트렌드와 변동성을 파악할 수 있습니다.

import matplotlib.dates as mdates

item_selected = '배추'

# x_start, x_end를 날짜 형식으로 설정
start_date = '2020-04-10'
end_date = '2020-07-10'

train_selected = train_prep[(train_prep['date'] >= start_date) & (train_prep['date'] < end_date) ].copy()

# 다양한 윈도우 크기로 MA와 EMA 계산
window_size = 7

train_selected[f'{item_selected}_가격(원/kg)_MA'] = train_selected[f'{item_selected}_가격(원/kg)'].rolling(window=window_size).mean()
train_selected[f'{item_selected}_가격(원/kg)_EMA'] = train_selected[f'{item_selected}_가격(원/kg)'].ewm(span=window_size, adjust=False).mean()

# y축 범위를 각 그래프의 최대, 최소값으로 설정
y_min = min(train_selected[f'{item_selected}_가격(원/kg)'].min(), train_selected[f'{item_selected}_가격(원/kg)_MA'].min(), train_selected[f'{item_selected}_가격(원/kg)_EMA'].min())
y_max = max(train_selected[f'{item_selected}_가격(원/kg)'].max(), train_selected[f'{item_selected}_가격(원/kg)_MA'].max(), train_selected[f'{item_selected}_가격(원/kg)_EMA'].max())

plt.figure(figsize=(12, 4))

# 가격, MA, EMA 그래프 그리기
plt.plot(train_selected['date'], train_selected[f'{item_selected}_가격(원/kg)'], label='Original Price', alpha=0.5)
plt.plot(train_selected['date'],  train_selected[f'{item_selected}_가격(원/kg)_MA'], label=f'MA-{window_size} days')
plt.plot(train_selected['date'],  train_selected[f'{item_selected}_가격(원/kg)_EMA'], label=f'EMA-{window_size} days')

# x축과 y축의 범위를 설정
plt.xlim([train_selected['date'].min(), train_selected['date'].max()])
plt.ylim([y_min, y_max])

# x축을 날짜 형식으로 설정
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
plt.gca().xaxis.set_major_locator(mdates.DayLocator(interval=5))
plt.gcf().autofmt_xdate()

# 그래프의 제목과 레이블을 설정
plt.title(f'Moving Averages and Exponential Moving Averages of {item_selected}')
plt.xlabel('Date')
plt.ylabel('Price')

# 범례를 표시
plt.legend()

# 그래프를 출력
plt.show()

 

※ 결과 해석

이 시각화를 통해 품목 가격에 대한 다음과 같은 통찰을 얻을 수 있습니다:

  • MA와 EMA 차이: MA는 단순한 평균을 취하므로 급격한 가격 변동에 둔감할 수 있습니다. 반면 EMA는 최근 데이터에 더 높은 가중치를 주므로 빠르게 변하는 트렌드를 잡아볼 수 있습니다.
  • 트렌드 분석: MA와 EMA 라인을 관찰하면 가격의 상승 또는 하락 트렌드를 더 정확하게 볼 수 있습니다.
  • 변동성 파악: MA와 EMA 라인이 원래의 가격과 얼마나 떨어져 있는지를 보면, 가격의 변동성을 어느 정도 파악할 수 있습니다. 이러한 정보는 가격 예측 모델을 만드는데 유용하거나, 특정 시점에서의 가격 변동을 이해하는 데 도움이 됩니다.

8. 품목 거래량과 가격에 대한 산점도 그래프 생성

item_selected = '무'
ax = train_prep.plot.scatter(f"{item_selected}_거래량(kg)",f"{item_selected}_가격(원/kg)", s=1)

plt.show()


9. 전체 품목의 거래량과 가격에 대한 산점도 그래프 생성

# 모든 품목 이름 추출
item_names = [col.split('_')[0] for col in train_prep.columns if '_가격(원/kg)' in col]

# 3x3 그리드로 그림을 초기화
fig, axes = plt.subplots(3, 3, figsize=(12, 8))

# axes 배열을 평평하게 만들기 (쉬운 인덱싱을 위해)
axes = axes.flatten()

# 각 품목 이름을 반복하며 scatter plot 그리기
for i, name in enumerate(item_names):
    ax = axes[i]
    volume_col = f"{name}_거래량(kg)"
    price_col = f"{name}_가격(원/kg)"

    # 거래량, 가격 컬럼만을 추출
    data = train_prep[[volume_col, price_col]].copy()

    # 개별 스케일로 scatter plot 생성
    ax.scatter(data[volume_col], data[price_col], s=1)
    ax.set_title(f"{name} 거래량과 가격")
    ax.set_xlabel("거래량 (kg)")
    ax.set_ylabel("가격 (원/kg)")

    # 스케일 자동 조정
    ax.autoscale_view()

plt.tight_layout()
plt.show()

 

※ 결과 해석

이런 방식으로 여러 품목의 거래량과 가격을 한 번에 비교하면, 품목 간의 상관관계나 패턴을 빠르게 파악할 수 있습니다. 예를 들어, 어떤 품목은 거래량이 많을 때 가격이 낮아지는 경향이 있을 수 있고, 다른 품목은 그렇지 않을 수 있습니다. 이러한 정보는 품목별 가격 예측 전략을 세우거나, 품목 간의 상관관계를 이해하는 데 큰 도움이 됩니다. 또한, 이를 통해 어떤 품목이 시장에서 더 민감하게 반응하는지, 어떤 품목은 안정적인지 등의 통찰을 얻을 수 있습니다.

시금치, 토마토, 깻잎 등의 품목에서 거래량이 많아질수록 가격이 떨어지는 경향이 보이는 것 같습니다. 이러한 정보는 해당 품목에 대한 수요와 공급의 관계를 이해하고, 더 정확한 가격 예측 모델을 만드는데 도움이 될 수 있습니다.

 


10. 품목간 가격과 퍼센트 변화의 상관 관계 분석

※ 퍼센트 변화율 pct_change

퍼센트 변화율은 시계열 데이터에서 각 시장값의 상대적인 변화를 표현하는 지표입니다. 주식 가격, 환율, 상품 가격 등 다양한 금융과 경제 데이터에서 널리 사용됩니다. 퍼센트 변화율은 다음과 같은 수식으로 계산됩니다.

퍼센트 변화율 = (현재 값 - 이전 값) / 이전 값 * 100

이러한 퍼센트 변화율은 단순한 가격이나 수치의 절대적인 변화보다는 상대적인 변화를 측정하기 때문에, 시계열 데이터의 추세나 패턴을 이해하는 데 매우 유용할 수 있습니다.

 

활용 추세 분석: 퍼센트 변화를 시간에 따라 그래프로 표현하면, 데이터의 상승 추세나 하락 추세를 더 명확하게 파악할 수 있습니다.

상관 관계 분석: 다수의 시계열 데이터의 퍼센트 변화를 비교하여 상관 관계를 분석할 수 있습니다. 이를 통해 서로 어떤 연관성이 있는지, 예를 들어 한 상품의 가격이 변할 때 다른 상품의 가격은 어떻게 변하는지 등을 알 수 있습니다.

이상치 탐지: 퍼센트 변화율이 급격하게 증가하거나 감소하는 지점을 찾으면, 그 원인을 분석하여 이상치나 특이 패턴을 탐지할 수 있습니다.

전략 수립: 퍼센트 변화율의 패턴이나 상관 관계를 분석하여, 특정한 투자나 상품 판매 전략을 수립할 수 있습니다.

 

퍼센트 변화율은 이런 방식으로 다양하게 활용하면, 특히 시계열 데이터를 분석할 때 중요한 통찰을 제공하고 예측 모델의 성능을 높이는 데 큰 도움이 될 수 있습니다.

import seaborn as sns

# 가격에 대한 컬럼만 선택하여 상관 관계를 계산
price_columns = [col for col in train_prep.columns if '가격' in col]
correlation_matrix = train[price_columns].corr()

# 퍼센트 변화를 계산
train_pct_change = train_prep[price_columns].pct_change(7)

# 상관 관계 행렬 계산
correlation_matrix_pct_change = train_pct_change.corr()

# 상관 관계 행렬을 시각화
plt.figure(figsize=(12, 6))
ax1 = plt.subplot(1,2,1)
sns.heatmap(correlation_matrix, annot=True, cmap='coolwarm', fmt=".2f")
plt.title('품목 가격 간의 상관 관계')

ax2 = plt.subplot(1,2,2)
sns.heatmap(correlation_matrix_pct_change, annot=True, cmap='coolwarm', fmt=".2f")
plt.title('품목 가격의 퍼센트 변화 간의 상관 관계')

plt.tight_layout()
plt.show()

 

※ 결과 해석

"배추와 토마토 가격의 상관 관계가 0.570이 나왔다. 그렇다면 배추와 토마토 가격간에 정말로 높은 연관성이 있는 걸까?'

여기서 배추와 토마토의 일주일간 가격 퍼센트 변화를 보면 -0.030이었다. 이 값은 두 품목 간에 실제적인 연관성이 거의 없음을 나타낸다. 퍼센트 변화 상관 관계는 시장의 전반적인 흐름을 반영한 상태에서 두 품목의 상대적인 변동 패턴이 일치하는지를 측정하는데, 이 값이 낮다는 것은 두 품목의 가격이 독립적으로 움직인다는 것을 의미합니다.

이 분석을 통해 품목들 가격과 퍼센트 변화에 대한 상관 관계를 시각적으로 비교할 수 있습니다. 단순 가격 상관 관계는 시장의 전반적인 흐름에 따라 연관성이 높게 나올 수 있지만, 퍼센트 변화율을 기반으로 한 상관 관계는 이러한 영향을 상당 부분 제거해 품목 간 가격 움직임이 시장 조건 외의 요인으로부터 독립적으로 측정하기 위한 보정된 해석을 가능하게 합니다.

따라서, 퍼센트 변화에 기반한 상관 관계 분석은 품목들 간의 실제 연관성을 더 정확하게 파악할 수 있게 도와줍니다.


11. 품목 가격에 대한 Lag Scatter Plots: 시간적 패턴과 지연값을 통한 분석

※ Lag Plot

Lag Plot은 시계열 데이터의 자기 상관을 시각적으로 나타내는 방법 중 하나입니다. Lag Plot을 그리면 데이터가 랜덤한지, 양의 상관관계나 음의 상관관계를 가지는지 또는 주기성을 가지는지 등을 한 눈에 파악할 수 있습니다.

 

import matplotlib.pyplot as plt
from pandas.plotting import lag_plot

# Lag 값과 타이틀 설정
lags = [1, 7, 14, 28]
titles = ['Lag 1', 'Lag 7', 'Lag 14', 'Lag 28']

# 그래프 그리기 설정
fig, axes = plt.subplots(2, 2, figsize=(8, 6))

# 각 Lag 값에 따른 Scatter Plot 그리기
for ax, lag, title in zip(axes.flatten(), lags, titles):
    lag_plot(train_prep['무_가격(원/kg)'], lag=lag, ax=ax)
    ax.set_title(title)

# 전체 타이틀 및 레이아웃 설정
plt.suptitle('Lag Scatter Plots', fontsize=16)
plt.tight_layout(rect=[0, 0, 1, 0.96])
plt.show()

 

※ 결과 해석

Lag Scatter Plot을 통해 시계열 데이터 내에서의 자기 상관(Autocorrelation)을 시각적으로 살펴볼 수 있습니다. 여기서 자기 상관이란 시계열 데이터에서 한 시점의 값과 이전 시점의 값들 사이의 상관 관계를 나타냅니다.

각각의 lag 값 (1, 7, 14, 28)에 대한 Scatter Plot을 살펴보면, 특정 시계열 데이터 "무_가격(원/kg)"과 이전 시점의 "무_가격(원/kg)" 사이에 어느 정도의 상관관계가 존재함을 알 수 있습니다. 이는 시계열 데이터 내에서 어떤 시간적 패턴 혹은 주기성이 존재함을 시사합니다.

Lag 1에서의 강한 양의 상관관계는 "무"의 가격이 단기적으로는 안정적임을 의미합니다. 하지만 Lag 값이 증가함에 따라 상관 관계가 약해지는 것을 관찰할 수 있습니다. 이는 장기적으로 보았을 때 여러 요인이 무의 가격에 영향을 미칠 가능성이 있음을 시사합니다.

특히, Lag 값이 커짐에 따라 상관관계가 어떻게 변하는지 확인하면, 데이터가 장기적으로 얼마나 안정적인지 혹은 불안정한지에 대한 통찰을 얻을 수 있습니다. 이러한 정보는 모델을 설계하거나 데이터의 변동성을 분석하는 데에 중요한 통찰을 제공할 수 있습니다.


12. 시계열 데이터에서 주와 월별 가격 분포 분석

시계열 데이터에서 선택 품목의 주(Week)와 월(Month)별 가격 분포를 Box Plot으로 시각화합니다. 주기적인 패턴이나 계절적인 영향을 파악하기 위해 주와 월별로 가격의 분포를 분석하는 것이 목적입니다.

import seaborn as sns

# 주(week)와 월(month) 정보를 새로운 컬럼에 저장
train_prep['Week'] = train_prep['date'].dt.isocalendar().week.astype(np.int32)
train_prep['Month'] = train_prep['date'].dt.month

item_selected = '배추'

# 주와 월별 Box Plot 그리기
fig, axes = plt.subplots(2, 1, figsize=(12, 8))
plt.suptitle('Box Plot by Week and Month', fontsize=16)

# 주별 Box Plot
sns.boxplot(x='Week', y=f'{item_selected}_가격(원/kg)', data=train_prep, ax=axes[0])
axes[0].set_title('Box Plot by Week', fontsize=14)
axes[0].set_xlabel('Week', fontsize=12)
axes[0].set_ylabel(f'{item_selected} 가격 (원/kg)', fontsize=12)

# 월별 Box Plot
sns.boxplot(x='Month', y=f'{item_selected}_가격(원/kg)', data=train_prep, ax=axes[1])
axes[1].set_title('Box Plot by Month', fontsize=14)
axes[1].set_xlabel('Month', fontsize=12)
axes[1].set_ylabel(f'{item_selected} 가격 (원/kg)', fontsize=12)

plt.tight_layout()
plt.show()

 

※ 결과 해석

주별 분포: 주별로 배추 가격의 분포가 어떻게 되는지 파악할 수 있습니다. 특정 주에 가격의 편차가 크다면, 그 주에 어떤 외부 요인(예: 날씨, 휴일 등)이 작용했을 가능성을 추측할 수 있습니다.

월별 분포: 월별 분포를 통해 계절적인 영향을 확인할 수 있습니다. 예를 들어, 겨울에 배추 가격이 상승하는 경향이 있다면, 이는 계절적 요인으로 볼 수 있습니다.

가령 배추의 경우 2월과 9월에 이상치가 주로 나타남을 알 수 있고, 9월에 가격의 변동 폭이 가장 크다는 것을 관찰할 수 있습니다.

Box Plot: 상자의 중앙값(median), 사분위수(Q1, Q3), 이상치(outliers) 등을 확인하여 가격의 분포와 변동성을 이해할 수 있습니다.

이 분석을 통해, 시간적 차원에서의 가격 변동 패턴을 더 깊이 이해할 수 있습니다.

 


13. 시계열 데이터의 계절성 분해(Seasonal Decomposition)를 이용한 품목 가격 분석

품목 가격의 시계열 데이터를 '추세(Trend)', '계절성(Seasonal)', 그리고 '잔차(Residual)' 성분으로 분해합니다. 이를 통해 해당 품목 가격의 장단기 패턴과 예측에 활용할 수 있는 feature를 추출할 수 있습니다.

  • 계절성 분해(seasonal decomposition)를 수행합니다. 이때, seasonal_decompose 함수를 사용하고, model='additive'와 period=30을 설정합니다. model='additive' 는 데이터가 추세와 계절성의 합으로 구성되었다는 가정입니다. period=30은 월별 계절성을 나타내며, 이값은 도메인 지식에 따라 조정 가능합니다. 계절성 분해는 시계열 데이터를 원래의 데이터, 추세(trend), 계절성(seasonal), 그리고 잔차(residual) 네 가지 성분으로 분해합니다.
  • 계절성 분해의 결과를 components 리스트에 저장합니다. 이 리스트에는 원래의 시계열 데이터, 추세 성분, 계절성 성분, 잔차 성분이 순서대로 들어있습니다. 각 성분을 시각화합니다. 이때, x축에는 날짜를, y축에는 해당 성분의 값을 사용합니다.
from statsmodels.tsa.seasonal import seasonal_decompose

item_selected = '배추'
start_date_train = '2021-01'

train_selected = train_prep[train_prep['date'] >= start_date_train].copy()

# 배추 가격 추출
train_selected_item = train_selected[['date', f'{item_selected}_가격(원/kg)']].copy()

# 계절성 분해 수행 (연별 계절성 가정) 및 그래프 그리기
plt.figure(figsize=(12, 8))
decomposition = seasonal_decompose(train_selected_item[f'{item_selected}_가격(원/kg)'], model='additive', period=30)

components = [decomposition.observed, decomposition.trend, decomposition.seasonal, decomposition.resid]
titles = ['Original Series', 'Trend Component', 'Seasonal Component', 'Residual Component']

for idx, component in enumerate(components):
    plt.subplot(4, 1, idx + 1)
    plt.plot(train_selected_item['date'], component)  # x축에 날짜 사용, y축에 성분 사용
    plt.title(titles[idx])
    plt.grid(True)

plt.tight_layout()

#for checkcode only
axes = plt.gcf().axes 

plt.show()

 

※결과 해석

추세(Trend) 분석:

추세 성분은 품목 가격의 장기적인 경향성을 규명합니다. 이는 가격 산정, 공급 계획, 그리고 투자 전략 등에 중요한 근거 자료가 될 수 있습니다. 일반적으로 이동 평균이나 지수 평활화 같은 방법을 통해 추세를 정량화할 수 있으며, 이러한 통계적 측정치는 머신 러닝 모델의 설명 변수(feature)로 활용될 수도 있습니다. 이를 통해 모델의 예측 정확도를 향상시킬 수 있습니다.

계절성(Seasonal) 분석:

계절성은 주기적이고 반복적인 변동을 캡처합니다. 이는 수요 예측에 있어 큰 영향을 미칠 수 있습니다. 예를 들어, 시즌별 소비 트렌드가 있으며, 그 정보를 통해 제품을 미리 준비할 수 있습니다. 계절성을 파악하지 못할 경우, 계절성을 고려한 시계열 모델(SARIMA 등)을 구축하거나, 계절적 지표를 머신러닝 모델의 입력 특성으로 추가하여 예측 성능을 높일 수 있습니다.

잔차(Residual) 분석:

잔차는 추세와 계절성을 제거한 후의 랜덤한 변동성을 의미합니다. 이는 데이터에 검증되지 않은 패턴이 존재하는지를 확인하는 데 활용될 수 있습니다. 또한, 잔차의 분포와 특성을 분석하여 모델의 성능을 진단하는 데도 사용됩니다.