Time series and Stationary test ใช้ Python กับข้อมูล Time series
Time is what we want most, but what we use worst.
เนื้อหาของ Blog ตอนนี้คือกำลังเข้าสู่ IFRS 9 ในส่วนที่เป็น Forward-looking model แล้ว เนื่องจากมาตรฐานระบุไว้ชัดเจนสำหรับการคำนวณว่า ต้องมีการ Incorporate forward-looking information เข้าไปด้วย นอกจากนี้มาตรฐานยังแนะนำเกี่ยวกับ Forward-looking information ไว้ว่า ควรเป็นข้อมูล Macroeconomics หรือตัวแปรทางเศรษฐกิจที่อาจส่งผลต่อ Model
เมื่อพิจารณาทั้งหมดแล้ว… ไม่รู้ว่ายังพูดว่าเรื่องนี้เกี่ยวข้องกับ IFRS 9 Model เต็ม ๆ อยู่ไหม เพราะข้อมูล Macroeconomics มันเป็นข้อมูลที่อยู่ในรูปแบบที่เรียกว่า Time series หรืออาจเป็นเรื่องดีที่เนื้อหาในตอนนี้ อาจนำไปใช้กับโมเดล Time series ในรูปแบบอื่น ไม่ได้จำกัดเฉพาะ IFRS 9 เท่านั้น
Time series
Time series data ภาษาไทยเรียกว่า “ข้อมูลอนุกรมเวลา” เป็นข้อมูลที่เรียงต่อกันตามช่วงเวลาต่าง ๆ ข้อมูลที่คุ้นเคยกันมากที่สุดคงเป็น Macroeconomics อาจเคยได้ยินจากข่าวว่า GDP ไตรมาสนี้เป็นค่าเท่าใด เมื่อเปรียบเทียบจากไตรมาสก่อนหน้า หรืออัตราการว่างที่เพิ่มสูงขึ้นในช่วงเดือนที่ผ่านมา เป็นต้น
แต่ Time series ไม่ได้จำกัดอยู่แค่ข้อมูล Macroeconomics เท่านั้น เพราะข้อมูลใด ๆ ที่นำมาเรียงต่อกันตามเวลาก็สามารถเป็น Time series ได้ เช่น ยอดจองตัวเครื่องบินของสายการบินในปี 2021 หรือยอดขายรถมือสองของปี 2020 เป็นต้น เมื่อนำข้อมูลประเภทนี้มาแสดงเป็นกราฟจะได้เป็นกราฟที่มีแกน X คือเวลา และแกน Y คือค่าของตัวแปรนั้น ๆ
Imputation
สำหรับข้อมูลที่ใช้ใน Blog ตอนนี้คือข้อมูล Macroeconomics ที่ได้มาจาก Bank of Thailand (BOT) ที่มี API ให้บริการสำหรับดึงข้อมูล
อย่างที่เคยยกตัวอย่างไปว่า ตัวแปร Macroeconomics อาจไม่ได้อยู่ในรูปที่เป็นข้อมูลรายเดือนเสมอไป เพราะอย่าง GDP ก็นำเสนอข้อมูลเป็นรายไตรมาส (ทุก 3 เดือน) แต่ในกรณีที่ต้องการโมเดลในระดับ Monthly basis ดังนั้นจึงต้องมีการ Imputation เพื่อให้ข้อมูลมี Basis ตามต้องการก่อน
การ Imputation กับข้อมูลที่อยู่ใน Pandas DataFrame สามารถได้ด้วยคำสั่ง .interpolate()
ซึ่ง method
ที่ใช้เป็น ‘cubic’
ที่มีจาก Cubic Spline เป็นการ Interpolate ระหว่างจุดสองจุดในรูปแบบนึง ซึ่งวิธีการ Interpolate สามารถใช้วิธีอื่นได้เช่นกัน (Linear) เพราะจุดมุ่งหมายเดียวคือการทำให้ข้อมูลมี Basis ตามที่ต้องการ
วิธีการใช้งาน .interpolate()
ยังมีจุดที่ต้องพิจารณาอีกคือ ข้อมูลใน DataFrame ต้องเป็น Numeric หรือ Datetime เท่านั้น ดังนั้นเพื่อไม่ให้เกิดความสับสน เลยแนะนำว่าให้เปลี่ยน Column ที่เป็นแกนเวลาให้เป็น Format เป็น Datetime และ .set_index()
เก็บเอาไว้
เมื่อ Interpolate เสร็จแล้ว ข้อมูลเช่น GDP จากที่เคยมีเป็นรายไตรมาส ก็ถูกเติมให้เต็มเป็นรายเดือนเรียบร้อยแล้ว
Decomposition
ต่อมาเป็นการทำ Time series decomposition คือการแยกองค์ประกอบหลักของ Time series โดยที่ Components ทั้ง 4 แบ่งออกเป็น Level, Trend, Seasonality และ Noise ที่มีความสัมพันธ์กันในลักษณะต่าง ๆ เช่น Additive หรือ Multiplicative
แต่ก่อนอื่นขอเลือกเฉพาะข้อมูลในช่วงที่ต้องการ Model คือช่วงตั้งแต่ Jan-2014 จนถึง Nov-2018 และการ Decomposition ของ Library จาก Statsmodels ไม่สามารถจัดการกับ Missing values ได้ ดังนั้นจึงต้องตรวจสอบ Missing values ให้ดีก่อน
Checking for missing values:
FDI 0
HD 0
GDP 0
RSI 0
CPI 0
Wage 0
UR 0
OP 0
Im 0
Ex 0
PCI 0
CCI 0
API 0
PII 0
MPI 0
PIR 0
จริง ๆ แล้วฟังก์ชั่น seasonal_decompose()
มี Option .plot()
เพื่อให้แสดงผลออกมาได้อย่างง่ายดาย แต่ต้องแลกมาด้วยการปรับกราฟ ที่ค่อนข้างยุ่งยาก เพราะโดน Fix มาหมดทุกอย่างแล้ว ดังนั้นเลยขอเขียนเป็นฟังก์ชั่นแยกออกมา เพื่อให้ Plot ออกมาได้หลายรูปแบบ เช่นการเปลี่ยน Theme ของกราฟ หรือการใช้งานร่วมกับ plt.subplots()
เป็นต้น
การใช้งาน seasonal_decompose()
ไม่มีความซับซ้อน คือฟังก์ชั่นรับค่าเป็น Series ข้อมูล ส่วน model
ให้ Assumption ว่าเป็น ‘additive’
model ก่อน หมายถึงองค์ประกอบของ Time series components ทั้งหมดเป็น Linear ต่อกัน เมื่อแยกองค์ประกอบของ Time series ออกมาจากตัวอย่าง 3 ตัวแปร ผลลัพธ์ที่ได้คือรูปด้านล่าง
หากสังเกตด้วยตา ตัวแปร Household Debt (HD) ที่ Observation ไม่มีความเป็น Stationary อยู่เลย เพราะมี Trend ที่เป็น Upward อยู่ตลอด หรือตัวแปร GDP ที่ Observation แม้ว่าดูเหมือนมี Seasonal เพราะมี Trend ขึ้นและลง แต่เมื่อมองดู Overall แล้ว ยังมี Trend ที่เป็น Upward อยู่ตลอดเวลา และตัวแปร Unemployment Rate (UR) ที่ Observation ดูมีความเป็น Seasonal หรือ Cycle ขึ้นลง แต่ก็ยากที่จะสรุปผลว่าเป็น Stationary หรือไม่ ดังนั้นจึงเป็นเนื้อหาของ Section ถัดไปคือ การทดสอบ Stationary
Transformation
ก่อนถึงการทดสอบ Stationary ของแทรกสั้น ๆ ด้วยการ Transformation ตัวแปร Macroeconomics เพราะในการทดสอบ Stationary ส่วนมากแล้วต้องการแปลงค่าข้อมูลเพื่อกำจัด Trend ต่าง ๆ ให้หมดไป แต่เพื่อลดการทำงานลง จึงขอ Transform ตัวแปรตามรูปแบบที่คิดไว้ทั้งหมดทุกตัวก่อน จากนั้นนำตัวแปรที่ Transform แล้วไปทดสอบ Stationary ทั้งหมดเลยทีเดียว
รูปแบบการ Transform ไม่ได้จำกัด ขึ้นอยู่กับว่าต้องการเปลี่ยน Time series ให้เป็นไปในลักษณะใด
Code ยาว ๆ ที่เขียนไว้ด้านบนคือการ Transform ในรูปแบบ Logarithm, Moving average และ Year-on-Year Change ซึ่งเรียกว่า First transformation คือการคำนวณค่าจากข้อมูลตัวเองด้วยวิธีการต่าง ๆ ต่อไปคือ Second transformation คือ Transform ที่ไม่มีการคำนวณเข้ามาเกี่ยวข้อง แต่เป็นการเปลี่ยนช่วงเวลาแทน
การทำ Transformation ต่าง ๆ แนะนำให้เริ่มจากข้อมูลที่เป็น Imputed data ที่ยังไม่ได้ตัด Period แม้ว่าต้องการโมเดลในช่วงตั้งแต่ Jan-2014 ถึง Nov-2018 แต่การ Transformation ต้องใช้ข้อมูลเยอะกว่านั้นมาก เช่น Moving average 12 เดือนย้อนหลัง และมี Lag อีก 12 เดือน ดังนั้นหมายความว่าต้องใช้ข้อมูล “ก่อนหน้า” ช่วงที่ต้องการโมเดลจริง ๆ ถึง 2 ปี
เมื่อ Transform ทุกอย่างเรียบร้อยแล้ว จึงสามารถตัดข้อมูลตามช่วงเวลาดังกล่าว เพราะถ้าตัดข้อมูลจาก Period ที่ต้องการโมเดลก่อน อาจทำให้เกิดข้อมูลที่เป็น Missing values โดยไม่ได้ตั้งใจ
Stationary
มาถึงหัวข้อ Stationary analysis หรือ Unit root analysis จริง ๆ เคยเขียนเรื่องนี้ไปแล้วรอบนึง ในตอนที่ทำ ARIMA Model แต่รอบนี้ขอลงรายละเอียดมากกว่าเดิม (นิดหน่อย)
Stationary เป็นคุณสมบัติอย่างหนึ่งสำหรับ Time series หมายถึงข้อมูลเป็น Independence กับ Time คือข้อมูลที่ดีต้องปราศจาก Trend หรือ Seasonal effects เพราะทั้งสองอย่างอาจทำให้โมเดลเกิด Overfitting ได้ รูปแบบของ Stationary มีหลายรูปแบบ เช่น
- Trend stationary คือข้อมูลที่ Stationary บน Trend หมายความว่าถ้าต้องการให้ข้อมูลมีความ Stationary ที่ Observe ต้องกำจัด Trend ออก เป็นต้น การกำจัด Trend ออกสามารถทำได้ด้วยการ Transformation ตัวแปร เช่น Year-on-Year Change หรือการหา Different
- Seasonal stationary คือข้อมูลที่ Stationary บน Seasonal เช่นเดียวกันถ้าต้องการให้ข้อมูล Stationary ที่ Observe ต้องกำจัด Seasonal effect ออกไป เช่นการ Moving average เพื่อให้ข้อมูลมีมา Smooth มากขึ้น
Checks for Stationarity
การทดสอบ Stationary สามารถทำได้หลายแบบ เช่นการ Plot graph อย่าง Decomposition หรือใช้ Statistic analysis บางตัวในการทดสอบ ซึ่ง Blog ตอนนี้ขอใช้เป็น Augmented Dickey-Fuller test (ADF Test) หรือที่รู้จักกันว่า Unit root analysis
ใน Python มี Library สำหรับ ADF Test ที่ใช้งานได้ง่าย และให้ผลลัพธ์ออกมาเป็นทั้งค่า ADF Statistic และ P-Value
Library adfuller()
รับค่าเป็น Array ของข้อมูล Time series มี Parameters ที่ต้องสนใจอยู่ทั้งหมด 2 ตัวแปรด้วยกันคือ
regression
ขึ้นกับว่าข้อมูล Input เป็นไปในลักษณะใด เช่นถ้าเป็นข้อมูลที่เป็น Raw data อาจต้องทดสอบทั้ง Constant และ Trend แต่ในที่นี้ข้อมูลที่นำเข้าเป็นข้อมูลที่ผ่านการ Transform มาทั้งหมดแล้ว ดังนั้นจึงตั้ง Assumption ว่าการ Transform จะช่วยกำจัด Trend และ Seasonal ออกทั้งหมด จึงใส่ค่าเป็น‘nc’
autolag
ระบุเป็น‘AIC’
คือการใช้ Lag ใน Regression ที่ให้ค่า AIC น้อยที่สุด
การแปรผลลัพธ์ สามารถอ่านได้จากทั้ง ADF Statistic หรือ P-Value โดยที่ถ้า ADF Statistic มีค่าน้อยกว่าหรือเท่ากับ Critical value ที่เปอร์เซ็นต์ใด ๆ หมายความว่าข้อมูลเป็น Stationary แต่ถ้า ADF มีค่ามากกว่า Critical value หมายความว่าข้อมูลมี Unit root
เช่นเดียวกันกับ P-Value หากใช้ 95% Confidence interval ถ้า P-Value น้อยกว่าหรือเท่ากับ 0.05 หมายความว่าข้อมูลเป็น Stationary ในทางกลับกันถ้า P-Value มากกว่า 0.05 หมายความว่าข้อมูลมี Unit root
Conclusion
เมื่อทดสอบครบทุกตัวแปรแล้ว ลอง Plot เป็นผลลัพธ์มา 3 ตัวแปร เพื่อให้ดูว่าข้อมูลที่ Observe level ที่มีความ Stationary มีลักษณะเป็นอย่างไร
สำหรับเนื้อหาในตอนนี้ก็จบลงเพียงเท่านี้ สามารถนำไปใช้งานกับโมเดลประเภทอื่นได้ ไม่ได้จำกัดเฉพาะใน IFRS 9 เท่านั้น
สำหรับ Colab notebook สามารถอ่านได้ที่ Link ด้านบน