#tistory 관련 코드(필요없음)
from IPython.core.display import display, HTML
display(HTML("<style>.container {width:90% !important;}</style>"))
import pandas as pd
pd.set_option('display.max_columns',500) #생략없이 출력 가능
Time series end to end¶
데이터 : https://community.tableau.com/docs/DOC-1236¶
참고 페이지 : https://towardsdatascience.com/an-end-to-end-project-on-time-series-analysis-and-forecasting-with-python-4835e6bf050b¶
시계열 참고¶
funiture 판매 예측¶
import warnings
import itertools # 반복 가능한 데이터 스트림을 처리하는 데 유용한 많은 함수와 제네레이터가 포함
import numpy as np
import matplotlib.pyplot as plt
warnings.filterwarnings("ignore")
plt.style.use('fivethirtyeight')
import pandas as pd
import statsmodels.api as sm #통계분석 기능을 제공하는 파이썬 패키지
import matplotlib
#차트 기본 크기 설정
matplotlib.rcParams['axes.labelsize'] = 14
matplotlib.rcParams['xtick.labelsize'] = 12
matplotlib.rcParams['ytick.labelsize'] = 12
matplotlib.rcParams['text.color'] = 'k'
pd.set_option('display.max_columns',500) #생략없이 출력
df = pd.read_excel("C://Users//82106//Desktop//Sample - Superstore.xls")
df.head()
#Category가 Furniture인것만 사용
furniture = df.loc[df['Category'] == 'Furniture']
furniture.head()
furniture['Order Date'].min(), furniture['Order Date'].max()
데이터 전처리¶
#필요없는 열 제거
cols = ['Row ID', 'Order ID', 'Ship Date', 'Ship Mode', 'Customer ID', 'Customer Name', 'Segment', 'Country', 'City', 'State', 'Postal Code', 'Region', 'Product ID', 'Category', 'Sub-Category', 'Product Name', 'Quantity', 'Discount', 'Profit']
furniture.drop(cols, axis=1, inplace=True)
furniture = furniture.sort_values('Order Date')
furniture.head()
#누락 확인
furniture.isnull().sum()
furniture = furniture.groupby('Order Date')['Sales'].sum().reset_index()
furniture.head()
#index를 Order Date로 한다.
furniture = furniture.set_index('Order Date')
furniture.head()
furniture.index
resampling¶
resample 연산을 쓰면 시간 간격을 재조정하는 리샘플링(resampling)이 가능하다. 이 때 시간 구간이 작아지면 데이터 양이 증가한다고 해서 업-샘플링(up-sampling)이라 하고 시간 구간이 커지면 데이터 양이 감소한다고 해서 다운-샘플링(down-sampling)이라 부른다. https://rfriend.tistory.com/494
#MS는인덱스는 해당 월의 마지막 일자로 표시를 시작일로 변경
y = furniture['Sales'].resample('MS').mean()
#2017년 매출
y['2017':]
Visualizing Furniture Sales Time Series Data¶
y.plot(figsize = (15,6))
plt.show()
연초에는 항상 매출이 낮고 연말에는 높은 계절 패턴이 있음을 확인.
#차트 기본 크기 설정
from pylab import rcParams
rcParams['figure.figsize'] = 18, 8
decomposition = sm.tsa.seasonal_decompose(y, model='additive')
fig = decomposition.plot()
plt.show()
일반 ARIMA모형과 계절 ARIMA모형이 서로 곱해져 있으므로 Multiplicative모형입니다. 합계되어 있으면 Additive모형인데 Multiplicative모형은 Additive모형보다 일반적으로 더(more) general합니다.
Time series forecasting with ARIMA¶
ARIMA라고 알려진 시계열 예측에 가장 많이 사용되는 방법 중 하나를 적용할 것이다. ARIMA는 자기진행적 통합 이동 평균을 의미한다. ARIMA 모델은 표기법 ARIMA(p, d, q)로 표시된다. 세 가지 파라미터: 계절성, 추세, 노이즈
p = d = q = range(0, 2)
pdq = list(itertools.product(p, d, q))
seasonal_pdq = [(x[0], x[1], x[2], 12) for x in list(itertools.product(p, d, q))]
print('Examples of parameter combinations for Seasonal ARIMA...')
print('SARIMAX: {} x {}'.format(pdq[1], seasonal_pdq[1]))
print('SARIMAX: {} x {}'.format(pdq[1], seasonal_pdq[2]))
print('SARIMAX: {} x {}'.format(pdq[2], seasonal_pdq[3]))
print('SARIMAX: {} x {}'.format(pdq[2], seasonal_pdq[4]))
다음은 ARIMA 모델의 파라미터 선택이다. 여기서 목표는 "grid search"를 사용하여 모델에 가장 적합한 성능을 산출하는 최적의 매개변수 집합을 찾는 것이다.
for param in pdq:
for param_seasonal in seasonal_pdq:
try:
mod = sm.tsa.statespace.SARIMAX(y,
order=param,
seasonal_order=param_seasonal,
enforce_stationarity=False,
enforce_invertibility=False)
results = mod.fit()
print('ARIMA{}x{}12 - AIC:{}'.format(param, param_seasonal, results.aic))
except:
continue
위의 출력 중에서 SARIMAX(1, 1, 1)x(1, 1, 0, 12)가 297.78의 최저 AIC 값을 산출했다.
Fitting the ARIMA model¶
mod = sm.tsa.statespace.SARIMAX(y,
order=(1, 1, 1),
seasonal_order=(1, 1, 0, 12),
enforce_stationarity=False,
enforce_invertibility=False)
results = mod.fit()
print(results.summary().tables[1])
results.plot_diagnostics(figsize=(16, 8))
plt.show()
모델 잔차가 정규 분포에 가깝다
Validating forecasts¶
예측의 정확성을 파악하기 위해 예측 판매량을 시계열의 실제 판매량과 비교하고, 2017–01–01에서 종료 시점까지 예측을 설정한다.
pred = results.get_prediction(start=pd.to_datetime('2017-01-01'), dynamic=False)
pred_ci = pred.conf_int() #추정된 계수의 신뢰구간 계산
ax = y['2014':].plot(label='observed')
pred.predicted_mean.plot(ax=ax, label='One-step ahead Forecast', alpha=.7, figsize=(14, 7))
ax.fill_between(pred_ci.index,
pred_ci.iloc[:, 0],
pred_ci.iloc[:, 1], color='k', alpha=.2)
ax.set_xlabel('Date')
ax.set_ylabel('Furniture Sales')
plt.legend()
plt.show()
y_forecasted = pred.predicted_mean
y_truth = y['2017-01-01':]
mse = ((y_forecasted - y_truth) ** 2).mean()
print('The Mean Squared Error of our forecasts is {}'.format(round(mse, 2)))
평균 제곱 오차(MSE)는 추정 값과 추정 값 사이의 평균 제곱 차이를 측정한다. MSE는 추정자의 품질에 대한 척도로서 항상 음성이 아니며, MSE가 작을수록 가장 적합한 선을 찾는 데 더 가까워진다.
print('The Root Mean Squared Error of our forecasts is {}'.format(round(np.sqrt(mse), 2)))
Producing and visualizing forecasts¶
pred_uc = results.get_forecast(steps=100)
pred_ci = pred_uc.conf_int() #추정된 계수의 신뢰구간 계산
ax = y.plot(label='observed', figsize=(14, 7))
pred_uc.predicted_mean.plot(ax=ax, label='Forecast')
ax.fill_between(pred_ci.index,
pred_ci.iloc[:, 0],
pred_ci.iloc[:, 1], color='k', alpha=.25)
ax.set_xlabel('Date')
ax.set_ylabel('Furniture Sales')
plt.legend()
plt.show()
Time Series of Furniture vs. Office Supplies¶
데이터에 따르면 Time Series of Furniture보다 Office Supplies의 매출이 훨씬 더 많았다.
furniture = df.loc[df['Category'] == 'Furniture']
office = df.loc[df['Category'] == 'Office Supplies']
furniture.shape, office.shape
cols = ['Row ID', 'Order ID', 'Ship Date', 'Ship Mode', 'Customer ID', 'Customer Name', 'Segment', 'Country', 'City', 'State', 'Postal Code', 'Region', 'Product ID', 'Category', 'Sub-Category', 'Product Name', 'Quantity', 'Discount', 'Profit']
furniture.drop(cols, axis=1, inplace=True)
office.drop(cols, axis=1, inplace=True)
furniture = furniture.sort_values('Order Date')
office = office.sort_values('Order Date')
furniture = furniture.groupby('Order Date')['Sales'].sum().reset_index()
office = office.groupby('Order Date')['Sales'].sum().reset_index()
furniture = furniture.set_index('Order Date')
office = office.set_index('Order Date')
y_furniture = furniture['Sales'].resample('MS').mean()
y_office = office['Sales'].resample('MS').mean()
furniture = pd.DataFrame({'Order Date':y_furniture.index, 'Sales':y_furniture.values})
office = pd.DataFrame({'Order Date': y_office.index, 'Sales': y_office.values})
furniture.head()
office.head()
store = furniture.merge(office, how='inner', on='Order Date')
store.head()
store.rename(columns={'Sales_x': 'furniture_sales', 'Sales_y': 'office_sales'}, inplace=True)
store.head()
plt.figure(figsize=(20, 8))
plt.plot(store['Order Date'], store['furniture_sales'], 'b-', label = 'furniture')
plt.plot(store['Order Date'], store['office_sales'], 'r-', label = 'office supplies')
plt.xlabel('Date'); plt.ylabel('Sales'); plt.title('Sales of Furniture and Office Supplies')
plt.legend()
funiture와 office supllies 판매가 비슷한 계절 패턴을 공유한다는 것을 관찰할 수 있다. 두 종목 모두 연초다. 사무용품도 여름철이 한산한 것 같다. 게다가, 가구들의 하루 평균 판매량은 사무실 용품의 매월의 매상액보다 높다. 사무용품보다 가구 가격이 훨씬 높기 때문에 이해할 만하다. 때때로 사무용품들의 하루 평균 판매량은 가구보다 높았다. 사무실 용품 판매가 가구 판매량을 넘어선 것은 언제가 처음이었는지 알아보자.
first_date = store.ix[np.min(list(np.where(store['office_sales'] > store['furniture_sales'])[0])), 'Order Date']
print("Office supplies first time produced higher sales than furniture is {}.".format(first_date.date()))
! pip install Prophet
! pip install pystan
from fbprophet import Prophet
furniture.head()
furniture = furniture.rename(columns={'Order Date': 'ds', 'Sales': 'y'})
furniture_model = Prophet(interval_width=0.95)
furniture_model.fit(furniture)
office = office.rename(columns={'Order Date': 'ds', 'Sales': 'y'})
office_model = Prophet(interval_width=0.95)
office_model.fit(office)
furniture_forecast = furniture_model.make_future_dataframe(periods=36, freq='MS')
furniture_forecast = furniture_model.predict(furniture_forecast)
office_forecast = office_model.make_future_dataframe(periods=36, freq='MS')
office_forecast = office_model.predict(office_forecast)
plt.figure(figsize=(18, 6))
furniture_model.plot(furniture_forecast, xlabel = 'Date', ylabel = 'Sales')
plt.title('Furniture Sales');
plt.figure(figsize=(18, 6))
office_model.plot(office_forecast, xlabel = 'Date', ylabel = 'Sales')
plt.title('Office Supplies Sales');
예측 비교¶
furniture_names = ['furniture_%s' % column for column in furniture_forecast.columns]
office_names = ['office_%s' % column for column in office_forecast.columns]
furniture_names
merge_furniture_forecast = furniture_forecast.copy()
merge_office_forecast = office_forecast.copy()
merge_furniture_forecast.columns = furniture_names
merge_office_forecast.columns = office_names
forecast = pd.merge(merge_furniture_forecast, merge_office_forecast, how = 'inner', left_on = 'furniture_ds', right_on = 'office_ds')
forecast = forecast.rename(columns={'furniture_ds': 'Date'}).drop('office_ds', axis=1)
forecast.head()
plt.figure(figsize=(10, 7))
plt.plot(forecast['Date'], forecast['furniture_trend'], 'b-', label = "funiture")
plt.plot(forecast['Date'], forecast['office_trend'], 'r-', label = "office")
plt.legend(loc = 'upper right'); plt.xlabel('Date'); plt.ylabel('Sales')
plt.title('Furniture vs. Office Supplies Sales Trend');
plt.figure(figsize=(10, 7))
plt.plot(forecast['Date'], forecast['furniture_yhat'], 'b-',label = "funiture")
plt.plot(forecast['Date'], forecast['office_yhat'], 'r-',label = "office")
plt.legend(loc = "upper right"); plt.xlabel('Date'); plt.ylabel('Sales')
plt.title('Furniture vs. Office Supplies Estimate');
#furniture
furniture_model.plot_components(furniture_forecast);
#office
office_model.plot_components(office_forecast);
비록 사무용품들의 성장이 약간 더 강해진 것처럼 보이지만, 가구와 사무용품들의 판매는 시간이 지나면서 선형적으로 증가해 왔고 계속해서 증가할 것이라는 것을 보는 것은 좋은 일이다. funiture 최악의 달은 4월, office supply 최악의 달은 2월이다. funiture에 가장 좋은 달은 12월이고, office supply에 가장 좋은 달은 10월이다.
'나는야 데이터사이언티스트 > PYTHON' 카테고리의 다른 글
[Python]Jupyter Notebook 잘 사용하기 (0) | 2020.04.06 |
---|---|
[Python]시계열 데이터 모델링 - 기초버전 (0) | 2020.04.05 |
[Python] 용량이 큰 CSV 파일 빠르게 불러오기 (1) | 2020.03.23 |
[Python]데이터 시각화, 연관성 분석 heat map, pairplot 그리기 (0) | 2020.03.22 |
[Python]데이터 시각화, matplotlib & seaborn - line Plot(선 그래프) (0) | 2020.03.20 |