סדרות עיתיות הן סוג אחר של בעיות ממה שטיפלנו בו עד כה. במובן מסוים הן שייכות לבעיות “supervised”, אך בסדרות עיתיות המטרה אינה לחזות ערך \(y\) בודד, אלא לחזות סדרה של ערכים בהתבסס על סדרה שקדמה להם.
בסדרות עיתיות אנחנו מנסים להבין תופעות כגון עונתיות, ומגמות, כדי לנסות לחזות דברים שונים כגון:
ביחידה זו נשתמש בנתונים מנורמלים של חיפושי גוגל, ננתח, וננסה לפרש סדרות עיתיות עבור מונחי חיפוש כגון:
כמו כן נשתמש בנתוני פתיחה וסגירה של מניית Tesla
פרק זה לא ידון רבות בתיאוריה של סדרות עיתיות, אך הקורא המעוניין מופנה לספר הבא, ספר המציג קצת תיאוריה והרבה פרקטיקה בנושא של סדרות עיתיות:
Forecasting: Principles and Practice, OTexts (2018?). J. Hyndman and G. Athanasopoulos, Available online: https://otexts.org/fpp2/, (feched 12/11/2018).
סדרה עיתית היא אוסף של תצפיות בעלי יחס סדר (התצפיות באות אחת אחרי השנה), במרווחי זמן קבועים (לדוגמה פעם בחודש).
חיזוי סדרות עיתיות מעניין אותנו בכמה רמות:
תהליך העבודה עליו דיברנו תופס גם בעבודה עם סדרות עיתיות (יבוא הנתונים, סידור הנתונים, ויז’ואליזציה, מידול, חיזוי, והצגת הממצאים בפני מקבלי החלטות).
הפעולה הראשונה בעבודה עם סדרה עיתית היא קריאת הסדרה והכנסתה לפורמט של סדרה עיתית ts.
library(tidyverse)
zombie_raw <- read_csv("data-files/zombieTimeline.csv", skip = 3,
col_names = c("Month", "search_volume"))
glimpse(zombie_raw)
## Observations: 179
## Variables: 2
## $ Month <chr> "2004-01", "2004-02", "2004-03", "2004-04", "200...
## $ search_volume <int> 6, 6, 8, 6, 6, 6, 6, 6, 7, 10, 7, 7, 6, 7, 7, 7,...
# most of the time the data format will not be so nice, and you will have to consolidate data.
# in this case we already get a year-month data ready. Just need to turn it into date.
zombie_raw <- zombie_raw %>%
mutate(Month = lubridate::ymd(paste0(Month, "-01")))
zombie_ts <- ts(zombie_raw$search_volume, start = 2004, frequency = 12)
# frequency is super important, it will be used later on in our forecast algorithms.
# Frequency can have values:
# 1=Annual
# 4=Quarterly
# 12=Monthly
# 52=Weekly
# 60=Seconds
# 24=Hourly
# 7=Daily
# You get the drift...
# You can see that R prints the data in a nice table:
zombie_ts
## Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
## 2004 6 6 8 6 6 6 6 6 7 10 7 7
## 2005 6 7 7 7 8 10 14 9 9 13 12 11
## 2006 10 11 12 11 9 11 10 11 11 15 9 9
## 2007 9 9 9 12 14 13 13 16 17 20 14 15
## 2008 14 13 15 17 17 16 14 14 15 25 21 21
## 2009 21 21 22 23 25 23 22 27 27 44 28 25
## 2010 21 23 22 23 25 22 22 23 24 41 39 35
## 2011 37 36 35 34 45 34 34 34 37 64 37 34
## 2012 30 32 36 35 69 100 43 36 41 67 47 44
## 2013 34 37 35 35 32 34 35 34 37 65 33 28
## 2014 25 28 29 26 27 28 27 27 30 62 28 25
## 2015 25 26 26 25 25 24 24 26 28 56 30 26
## 2016 23 24 23 26 22 22 21 21 25 40 19 19
## 2017 18 18 17 17 20 18 18 16 18 38 18 16
## 2018 18 17 20 20 19 18 18 23 18 34 20
# Here is the timeplot describing search volume of zombie
ggplot(zombie_raw, aes(x = Month, y = search_volume)) +
geom_line() +
ggtitle("Google search volume for the word Zombie") +
ylab("Google coefficient for search volume") +
scale_x_date(date_breaks = "6 months") +
theme(axis.text.x = element_text(angle = 90))
התרשים הבא מציג את ה“עונות השונות” (כל שנה מהווה עונה). הוא מוצג כגרף ggplot2, אך בעצם מופעל מחבילת forecast שבה נשתמש בהמשך הפרק לויז’ואליזציה וכמו כן לבניית מודלים.
library(forecast)
## Warning: package 'forecast' was built under R version 3.4.4
ggseasonplot(zombie_ts, year.labels = TRUE, year.labels.left = TRUE) +
ylab("Search volume (google trend coefficient)") +
ggtitle("Seasonal plot: the search for Zombies")
# polar view
ggseasonplot(zombie_ts, polar = TRUE) +
ylab("Search volume (google trend coefficient)") +
ggtitle("Seasonal plot: the search for Zombies")
מעיון בתרשימים עולה תמונה של עליה של החיפושים של המונח עד שנת 2011, והחל משנת 2012, עונתיות שנתית (שיא בחיפושים סביב חודש אוקטובר). למה?
כמו כן, יש שיא מקומי בחודש יוני של שנת 2012.
כמו כן, ישנה היחלשות מסוימות בעוצמת החיפושים (הפיקים הולכים וקטנים בשנים האחרונות).
מקובל גם לבחון את הקורלציה בין תצפיות לאורך זמן.
ggAcf(zombie_ts, lag = 48)
התרשים מציג קורלציה גבוהה יחסית בין חודש לחודשים שקדמו לו, וגם בין חודש לבין אותו החודש שנה לפני (12 חודשים אחורה). מעבר לקשר של 12 חודשים אחורה, הקשר הולך ונחלש, וכמעט נעלם לאחר 25 חודשים.
כעת נרצה להשתמש בפקודה שתתאים מודל שיתפוס את העונתיות מחד, ואת המגמה מאידך.
המודלים הנאייביים ביותר הם:
autoplot(zombie_ts) +
autolayer(meanf(zombie_ts, h=24),
series="Mean", PI=FALSE) +
autolayer(naive(zombie_ts, h=24),
series="Naive", PI=FALSE) +
autolayer(snaive(zombie_ts, h=24),
series="Seasonal naive", PI=FALSE) +
ggtitle("Forecasts for zombie searches") +
xlab("Year") + ylab("Zombie searches") +
guides(colour=guide_legend(title="Forecast"))
# With confidence intervals using the seasonal naive model:
autoplot(zombie_ts) +
autolayer(snaive(zombie_ts, h=24),
series="Seasonal naive", PI=TRUE) +
ggtitle("Forecasts for zombie searches - with confidence intervals") +
xlab("Year") + ylab("Zombie searches") +
guides(colour=guide_legend(title="Forecast"))
מעבר למודלים הבסיסיים שהוצגו לעיל, ישנן עוד שתי משפחות של מודלים מתקדמים יותר:
משפחת המודלים מסוג החלקה אקספוננציאלית מבוססים על הרעיון שכל תצפית עתידית מבוססת על ממוצע משוקלל (החלקה) של תצפיות קודמות. ייתכן שממוצע זה מורכב מרכיבים מיוחדים (כגון מגמה, או עונתיות).
\[ \hat{y}_{T+1|T}=\alpha y_T + \alpha(1-\alpha)y_{T-1}+\alpha(1-\alpha)^2y_{T-1}+\ldots \]
הפרמטר \(\alpha\) הוא פרמטר ההחלקה.
פקודת ets מבצעת התאמה של הסדרה העיתית לאחד מ-18 משפחות הכוללות סוגים שונים של מודלים דומים (טרנדים, עונתיות, ושגיאות חיבוריות וכפליות)
zombie_ets <- ets(zombie_ts)
summary(zombie_ets)
## ETS(M,N,M)
##
## Call:
## ets(y = zombie_ts)
##
## Smoothing parameters:
## alpha = 0.9382
## gamma = 3e-04
##
## Initial states:
## l = 7.9029
## s = 0.9539 1.0513 1.6211 0.9665 0.9472 0.9522
## 1.0004 0.9908 0.8864 0.8987 0.8669 0.8647
##
## sigma: 0.1609
##
## AIC AICc BIC
## 1364.966 1367.911 1412.777
##
## Training set error measures:
## ME RMSE MAE MPE MAPE MASE
## Training set 0.09152998 5.943424 2.726626 -0.756665 10.63041 0.4627506
## ACF1
## Training set -0.09240921
autoplot(zombie_ets)
המודל המיטבי הינו מודל בעל שגיאה כפלית, עונתיות כפלית, וללא טרנד. שימו לב שהמודל עשוי להשתנות, כתלות באורך הסדרות (התצפיות) שמזינים לתוכו.
zombie_ets_3yrs <- ets(tail(zombie_ts, 36))
summary(zombie_ets_3yrs)
## ETS(M,N,A)
##
## Call:
## ets(y = tail(zombie_ts, 36))
##
## Smoothing parameters:
## alpha = 0.3661
## gamma = 1e-04
##
## Initial states:
## l = 24.4847
## s = -1.3401 16.7692 -1.3084 -1.0286 -2.6781 -2.1174
## -0.9071 -0.6359 -1.2091 -1.9143 -1.9626 -1.6675
##
## sigma: 0.1159
##
## AIC AICc BIC
## 205.7536 229.7536 229.5064
##
## Training set error measures:
## ME RMSE MAE MPE MAPE MASE
## Training set -0.3272774 1.953499 1.64154 -2.094269 7.86075 0.4863822
## ACF1
## Training set -0.05379723
autoplot(zombie_ets_3yrs)
כדי לספק את המודל, פונקציית ets מבצעת Best Fit בהתבסס על מדד log-liklihood.
באמצעות פונקציית predict, ניתן לספק תחזית.
zombie_prediction <- predict(zombie_ets, h = 36)
autoplot(zombie_prediction)
כדי לבחון את מידת הדיוק של המודלים, מדווחת הפונקציה ets על מספר סוגי שגיאה.
accuracy מתוך חבילת forecast.