ทำนายการขายไข่ด้วย SARIMA
Tags: python, ai
Published: 2024-04-16 13:48:19

หากเรามีข้อมูลที่เก็บซ้ำๆ ต่อเนื่องกันเป็นเวลานาน แล้วต้องการคาดเดาว่าในอนาคตจะมีแนวโน้มเป็นอย่างไร สิ่งที่สามารถทำนายได้คือ SARIMA

## SARIMA คืออะไร
SARIMA นั้นเป็นส่วนขยายมาจาก ARIMA 
- AR - Autoregressive : จำนวนครั้งที่ข้อมูลในช่วงเวลาก่อนหน้าถูกใช้เป็นตัวแปรอิสระในการคำนวณค่าในช่วงเวลาปัจจุบัน
- I  - Integrated     : ทำให้ข้อมูลมีลักษณะคงที่
- MA - Moving Average : จำนวนครั้งที่ค่าเฉลี่ยของข้อมูลในช่วงเวลาก่อนหน้าถูกใช้ในการคำนวณค่าในช่วงเวลาปัจจุบัน

โดยมีการใช้พารามิเตอร์ p, d, และ q เพื่อกำหนดโครงสร้างของโมเดล ARIMA ที่เหมาะสมกับชุดข้อมูลที่กำลังวิเคราะห์

เหมาะสำหรับการจัดการกับข้อมูลชุดเวลาที่มีแนวโน้มหรือลักษณะซ้ำซาก
การทำ ARIMA มักนิยมใช้ในงานทางการเงิน การวิเคราะห์ภาวะเศรษฐกิจ หรือการทำนายภัยพิบัติทางธรรมชาติ ที่มีข้อมูลบันทึกไว้ซ้ำๆ ตลอดก่อนหน้า

ส่วนที่เพิ่มเข้ามา คือ
- S - Seasonal : ช่วงฤดูกาล
มีการใช้พารามิเตอร์ s เพิ่มเข้าไป

นั่นคือการคำนึงถึงช่วงเวลาฤดูกาลเข้าไปใน ARIMA กลายเป็น SARIMA นั่นเอง

## ขั้นตอน
อธิบายคร่าวๆ ถึงการทำ SARIMA

## นำเข้า Libraries
```
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.tsa.statespace.sarimax import SARIMAX
from statsmodels.tsa.stattools import adfuller
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
from sklearn.metrics import mean_absolute_error, mean_squared_error
```


## นำเข้าชุดข้อมูล (Dataset)
โดยชุดข้อมูลที่ใช้คือ Egg Sales of a local shop for 30 years

```
df = pd.read_csv('/content/train_egg_sales.csv', sep=';')
```
เนื่องจากชุดข้อมูลที่นำมาใช้นั้นมีข้อมูลรวมกันอยู่ จำเป็นต้องใช้ `sep=';'`
หากชุดข้อมูลอื่นแยกมาอยู่แล้ว ก็ไม่จำเป็น
```
sales_data=df[['Date','Egg Sales']]
sales_data=pd.DataFrame(sales_data)
sales_data['Date'] = pd.to_datetime(sales_data['Date'], errors='coerce')
df.head()
```
ในขั้นตอนนี้ สามารถดูข้อมูลที่นำเข้ามาเพื่อตรวจสอบความถูกต้องของจำนวนข้อมูล
 ด้วย`df.info()`
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/15jb2mbwvjyrdvjkvnw7.png)


## รวมช่วงเวลา
จากข้อมูลที่เป็นต่อวัน รวมกันให้เป็นต่อเดือน
```
df1 = sales_data.set_index('Date')

monthly_sales = df1.resample('M').mean()
monthly_sales.head()
```

## แสดงกราฟยอดขายแต่ละเดือน
```
plt.figure(figsize=(12, 6))
plt.plot(monthly_sales['Egg Sales'], linewidth=1, c='cyan')
plt.title("Monthly sales")
plt.xlabel("Date")
plt.ylabel("Egg Sales")
plt.show()
```
เมื่อดูผลกราฟแล้ว จะเห็นว่าเส้นมีการขึ้นลงที่รูปแบบซ้ำใกล้เคียงกัน
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5o4tqs25p94z15zdyjsi.png)



## ตรวจสอบความคงที่
```
def check_stationarity(timeseries):
    # Perform the Dickey-Fuller test
    result = adfuller(timeseries, autolag='AIC')
    p_value = result[1]
    print(f'ADF Statistic: {result[0]}')
    print(f'p-value: {p_value}')
    print('Stationary' if p_value < 0.05 else 'Non-Stationary')

check_stationarity(monthly_sales['Egg Sales'])
```
ซึ่งการตรวจสอบที่ออกมานั้นเป็น Non-Stationary ซึ่งแสดงว่าข้อมูลนี้ไม่มีความคงที่

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bra2l2k4kcwla8xxjro8.png)


## หาพารามิเตอร์ของโมเดล
```
# Plot ACF and PACF
plot_acf(monthly_sales)
plot_pacf(monthly_sales)
plt.show()
```
**ACF กับ PACF** คืออะไร
- ACF - Autocorrelation Function 
  วัดความสัมพันธ์ระหว่างจุดข้อมูลและจุดข้อมูลก่อนหน้าโดยมีความล่าช้าต่างกัน
  
- PACF - Partial Autocorrelation Function 
  ความสัมพันธ์ระหว่างจุดข้อมูลและความล่าช้า

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qhhtibzmkemaj1wjn0sm.png)

## สร้าง Model
กำหนดให้ p d q และ P D Q เป็น 1
* ค่าเปลี่ยนแปลงไปตาม ACF และ PACF
กำหนด s ตามจำนวนเดือน
```
# Define SARIMA parameters
p, d, q = 1, 1, 1
P, D, Q, s = 1, 1, 1, 12  # Assuming monthly seasonality
 
# Fit the SARIMA model
model = SARIMAX(monthly_sales, order=(p, d, q), seasonal_order=(P, D, Q, s))
results = model.fit()
model
print(results.summary())
```

## ลองทำนาย
โดยลองทำนายว่า 3 ปีต่อไปจะเป็นแบบไหน โดยใส่ค่า `forecast_periods = 36`
```
# Forecast future values
forecast_periods = 36  # Forecast the next 36 months
forecast = results.get_forecast(steps=forecast_periods)
forecast_mean = forecast.predicted_mean
forecast_ci = forecast.conf_int()
 
# Plot the forecast
plt.figure(figsize=(12, 6))
plt.plot(monthly_sales, label='Observed')
plt.plot(forecast_mean, label='Forecast', color='red')
plt.fill_between(forecast_ci.index, forecast_ci.iloc[:, 0], forecast_ci.iloc[:, 1], color='pink')
plt.title("Sales Forecast")
plt.xlabel("Date")
plt.ylabel("Sales")
plt.legend()
plt.show()
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l7cc9k8gk0aqxjpinpd3.png)



## วัดประสิทธิภาพของโมเดล
- MAE (Mean Absolute Error) คือค่าเฉลี่ยของความต่างระหว่างค่าที่ทำนายและค่าจริง
- MSE (Mean Squared Error) คือค่าเฉลี่ยของความต่างระหว่างค่าที่ทำนายและค่าจริง โดยยกกำลังสองของความต่างแต่ละค่า
```
observed = monthly_sales[-forecast_periods:]
mae = mean_absolute_error(observed, forecast_mean)
mse = mean_squared_error(observed, forecast_mean)
print(f'MAE: {mae}')
print(f'MSE: {mse}')
```

ค่า MAE และ MSE มีค่าค่อนข้างมาก ซึ่งอาจไม่แม่นยำต่อความเป็นจริง 
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0s5d1p86bv09ydt3qq62.png)


## สรุป
การทำนายด้วย SARIMA นั้นแสดงค่าของการขายไข่ที่เพิ่มขึ้นในอีก 3 ปี

แต่อย่างไรก็ตาม ชุดข้อมูลที่ใช้นี้เป็น Non-Stationary หรือไม่คงที่
ทำให้เกิดการคาดเคลื่อนค่อนข้างมาก ซึ่งสามารถหาชุดข้อมูลอื่นที่เมื่อตรวจสอบเป็น Stationary จะให้ความแม่นยำมากกว่า หรือการแปลง Non-Stationary เป็น Stationary จากอ้างอิงได้

## อ้างอิง

- ข้อมูลที่เกี่ยวข้อง

https://www.geeksforgeeks.org/sarima-seasonal-autoregressive-integrated-moving-average/
https://www.kaggle.com/code/shubhammisar/air-passenger-data-forecasting-sarima/notebook
https://www.kaggle.com/code/tanmay111999/air-passenger-forecast-arima-sarima/notebook

- ชุดข้อมูล

https://www.kaggle.com/datasets/kanchana1990/egg-sales-of-a-local-shop-for-30-years/data