Dataset: Johnson & Johnson Quarterly Earnings (1960–1980) Στόχος: Πρόβλεψη τριμηνιαίων κερδών για τα επόμενα 3 χρόνια
Αρχικά, φορτώνουμε και ελέγχουμε τα δεδομένα μας.
# Φόρτωση δεδομένων
data("JohnsonJohnson")
jj <- JohnsonJohnson
## 📊 ΒΑΣΙΚΑ ΧΑΡΑΚΤΗΡΙΣΤΙΚΑ ΧΡΟΝΟΣΕΙΡΑΣ
## • Τύπος Αντικειμένου: ts
## • Έναρξη Χρονοσειράς: Έτος 1960 - Τρίμηνο 1
## • Λήξη Χρονοσειράς: Έτος 1980 - Τρίμηνο 4
## • Συχνότητα Δεδομένων: 4 (Τρίμηνα ανά έτος)
## • Σύνολο Παρατηρήσεων: 84 τρίμηνα (21 χρόνια)
## (Πρώτες 12 Παρατηρήσεις):
## Qtr1 Qtr2 Qtr3 Qtr4
## 1960 0.71 0.63 0.85 0.44
## 1961 0.61 0.69 0.92 0.55
## 1962 0.72 0.77 0.92 0.60
Προβάλλουμε τα δεδομένα για να αποκτήσουμε μια αρχική εικόνα της συμπεριφοράς τους.
# Κύριο time series plot
autoplot(JohnsonJohnson) +
ggtitle("Quarterly Revenue Johnson & Johnson") +
xlab("Έτος") +
ylab("Έσοδα") +
theme_minimal()
#εχουμε τάση και εποχικότητα το Q3 είναι το μεγαλύτερο πάντα σε εσοδα και το q4 το χειρότερο
Ποιο τρίμηνο της χρονιάς είναι συστηματικά το ισχυρότερο; Διερευνούμε ποιο τρίμηνο της χρονιάς είναι συστηματικά το ισχυρότερο χρησιμοποιώντας εποχιακά γραφήματα.
# Seasonal plot: κάθε έτος ως χωριστή γραμμή
ggseasonplot(JohnsonJohnson, year.labels = TRUE) +
ggtitle("Seasonal plot — Johnson & Johnson")
# Subseries plot: ένα mini-plot ανά μήνα/τρίμηνο
ggsubseriesplot(JohnsonJohnson) +
ggtitle("Subseries plot — μεταβολή ανά Τρίμηνο (Quarter)")
Παρατηρούμε πως το Q2 ειναι ανεβασμένο ωστοσο το Q3 φαινεται τα τελευταια χρονια να ειναι το καλύτερο για κάθε χρόνο
Teκμηρίωσε γιατί θεωρείς ότι είναι multiplicative ή additive
Παρατηρουμε οτι δεν ειναι σταθερη η αυξηση,Έχουμε multipicative μοντελο επειδη μεγαλωνει αποτομα το πλάτος των κυμάτων και η εποχικότητα επηρεάζεται απο την τάση, Οσο αυξανεται το trend αυξανεται και το πλατος του κυματος. Δεν ειναι σταθερή η αυξηση εποχιακά. Δηλαδη ενα Q3 που ειναι το καλύτερο quarter κάθε χρονο δεν αυξάνεται σταθερά, και αυτο φαίνεται αισθητά στα τελευται 2-3 χρόνια Λογο του trend, εκτοξεύεται.
Σχολίασε: τι δείχνει το trend component; ευθύγραμμο ή καμπυλωτό;
# Επειδή είδαμε ότι το πλάτος μεγαλώνει → multiplicative
dec_mult <- decompose(JohnsonJohnson, type = "multiplicative")
autoplot(dec_mult) +
ggtitle("Multiplicative Decomposition — J&J")
Το trend δεν αυξανεται γραμμικά.
Εφάρμοσε log() στη σειρά και κάνε autoplot() δίπλα-δίπλα με την αρχική (grid.arrange + ncol=2). Τι αλλάζει μετά το λογάριθμο;
data_log <- log(JohnsonJohnson)
stl_fit <- stl(data_log, s.window = "periodic")
autoplot(stl_fit) +
ggtitle("STL Decomposition (σε log scale)")
Εδώ παρατηρούμε αφού μετατρέψαμε την χρονοσειρά σε additive με την χρήση του log(), η τάση αυξάνεται γραμμικα αντι για καμπυλωτά Εφαρμόζουμε λογάριθμο (log) στη σειρά για να σταθεροποιήσουμε τη διακύμανση και έπειτα ελέγχουμε τα ACF/PACF γραφήματα. Το STL δουλεύει μόνο σε additive σειρές, οπότε η log μετατροπή είναι απαραίτητη.
# Δύο plots δίπλα-δίπλα (log transformed)
p1 <- ggAcf(data_log) + ggtitle("ACF (Log Scale)")
p2 <- ggPacf(data_log) + ggtitle("PACF (Log Scale)")
grid.arrange(p1, p2, ncol = 2)
Απο το PACF plot παρατηρουμε πως το πρώτο lag ειναι στατιστικά σημαντικό αρα μπορουμε να προβλέψουμε με βάση την αμέσως προηγούμενη περίοδο. Το 2ο,3ο κτλπ lags δεν είναι στατιστικά τόσο σημαντικά. #Για αυτο το ACF plot φθίνει γεωμετρικά. σημαινει οτι η κάθε περίδοος εξαρταται απο τη προηγούμενη περίοδο σε μεγάλο βαθμό.
Συγκριτικά, ας δούμε τα ACF/PACF στην αρχική σειρά (χωρίς λογάριθμο):
# Multiplied (Αρχικά Δεδομένα)
p1 <- ggAcf(JohnsonJohnson) + ggtitle("ACF (Original)")
p2 <- ggPacf(JohnsonJohnson) + ggtitle("PACF (Original)")
grid.arrange(p1, p2, ncol = 2)
εδω πέρα το PACF diagram μας λεει οτι εχει αξία και το 2ο lag (2 περιόδους πίσω απο την τωρινή περίοδο).
Ας κανουμε την σειρά στάσιμη εδω πέρα..
air_diff <- diff(diff(log(JohnsonJohnson)), lag = 4)
autoplot(air_diff) +
ggtitle("Μετά από log + 1η διαφόριση + εποχιακή διαφόριση")
Η τάση είναι εκθετική, καθως δεν αυξάνεται γραμμικα και το επιβεβαιωσαμε απο το decomposition που κάναμε.
Το Q3 ειναι το πιο profitable κάθε χρόνο. Η εταιρεία μάλλον κάνει τις περισσότερες πωλήσεις εκείνη τη περίοδο, με αποτέλεσμα η αξία της μετοχής να ανεβαίνει.
Χωρίζουμε χρονολογικά τα δεδομέναμας για την εκπαίδευση (Train) και την αξιολόγηση (Test) των μοντέλων.
# Train: 1960 Q1 — 1978 Q4 (76 παρατηρήσεις)
# Test: 1979 Q1 — 1980 Q4 ( 8 παρατηρήσεις = 2 χρόνια)
train <- window(JohnsonJohnson, end = c(1978, 4))
test <- window(JohnsonJohnson, start = c(1979, 1))
length(train)
## [1] 76
length(test)
## [1] 8
Εφάρμοσε adf.test() στη σειρά Εάν δεν είναι στάσιμη, εφάρμοσε log + diff + diff(lag=4) και ξαναδοκίμασε Προσοχή: εδώ η εποχική περίοδος είναι 4 (τρίμηνα), όχι 12!
adf.test(air_diff)
##
## Augmented Dickey-Fuller Test
##
## data: air_diff
## Dickey-Fuller = -6.8701, Lag order = 4, p-value = 0.01
## alternative hypothesis: stationary
H σειρά ειναι στάσιμη. Το καταλαβαίνουμε απο το p-value < 0.05
Φτιάξε ggAcf() και ggPacf() της διαφορισμένης σειράς δίπλα-δίπλα Σχολίασε: ποια lags δείχνουν σημαντικές τιμές;
p1 <- ggAcf(air_diff) + ggtitle("ACF")
p2 <- ggPacf(air_diff) + ggtitle("PACF")
grid.arrange(p1, p2, ncol = 2)
Αν παρατηρήσουμε το PACF plot είναι στατιστικά σημαντικά το Lag 1 και Lag 4. Αυτό σημαίνει: Lag 1 -> Η ακριβώς προηγούμενη περίοδος (ένα τρίμηνο πριν είναι στατιστικά σημαντική) Lag 4 -> 4 περιόδους πριν δηλαδή ένα χρόνο πριν το ίδιο τρίμηνο είναι στατιστικά σημαντικό, άρα εποχικότητα;
(α) Seasonal Naïve → snaive(train, h = 8) (β) Holt-Winters → hw(train, h = 8, seasonal = “???”) (γ) ARIMA → auto.arima(train, lambda = 0); forecast(…, h = 8) Εμφάνισε τα summary() των τριών μοντέλων
Πρόβλεψη: κάθε μήνας = ίδιος μήνας πέρυσι
fit_snaive <- snaive(train, h = 8)
autoplot(fit_snaive) +
ggtitle("Seasonal Naïve Forecast") +
autolayer(test, series = "Πραγματικά", color = "red")
Τρεις παράμετροι: α (επίπεδο), β (τάση), γ (εποχικότητα)
fit_hw <- hw(train, h = 8, seasonal = "multiplicative")
summary(fit_hw)
##
## Forecast method: Holt-Winters' multiplicative method
##
## Model Information:
## Holt-Winters' multiplicative method
##
## Call:
## hw(y = train, h = 8, seasonal = "multiplicative")
##
## Smoothing parameters:
## alpha = 0.197
## beta = 0.1082
## gamma = 1e-04
##
## Initial states:
## l = 0.5902
## b = 0.0072
## s = 0.8267 1.0468 1.0827 1.0439
##
## sigma: 0.1337
##
## AIC AICc BIC
## 174.6172 177.3444 195.5938
##
## Error measures:
## ME RMSE MAE MPE MAPE MASE
## Training set 0.0420402 0.3595992 0.2602222 0.5285725 9.289055 0.4395027
## ACF1
## Training set 0.1282904
##
## Forecasts:
## Point Forecast Lo 80 Hi 80 Lo 95 Hi 95
## 1979 Q1 12.83495 10.636472 15.03343 9.472669 16.19723
## 1979 Q2 13.76326 11.303396 16.22312 10.001224 17.52529
## 1979 Q3 13.74580 11.122117 16.36949 9.733221 17.75839
## 1979 Q4 11.19950 8.872544 13.52645 7.640730 14.75826
## 1980 Q1 14.57307 11.235132 17.91102 9.468132 19.67802
## 1980 Q2 15.56606 11.610776 19.52135 9.516974 21.61515
## 1980 Q3 15.48923 11.116368 19.86209 8.801514 22.17695
## 1980 Q4 12.57631 8.637829 16.51479 6.552923 18.59970
autoplot(fit_hw) +
ggtitle("Holt-Winters Forecast") +
autolayer(test, series = "Πραγματικά", color = "red")
Η auto.arima δοκιμάζει αυτόματα συνδυασμούς (p,d,q)(P,D,Q) και επιλέγει αυτόν με το χαμηλότερο AIC
fit_arima <- auto.arima(train, lambda = 0) # lambda=0 → log transformation
summary(fit_arima)
## Series: train
## ARIMA(2,0,0)(1,1,0)[4] with drift
## Box Cox transformation: lambda= 0
##
## Coefficients:
## ar1 ar2 sar1 drift
## 0.3131 0.2432 -0.2970 0.0386
## s.e. 0.1244 0.1300 0.1285 0.0046
##
## sigma^2 = 0.008377: log likelihood = 71.78
## AIC=-133.56 AICc=-132.65 BIC=-122.18
##
## Training set error measures:
## ME RMSE MAE MPE MAPE MASE
## Training set 0.001523642 0.3596867 0.2419193 -0.16328 6.770657 0.4085899
## ACF1
## Training set -0.1186641
fc_arima <- forecast(fit_arima, h = 8)
autoplot(fc_arima) +
ggtitle("ARIMA Forecast") +
autolayer(test, series = "Πραγματικά", color = "red")
Εφάρμοσε checkresiduals() στο καλύτερό σου μοντέλο. Είναι τα residuals white noise; (Ljung-Box p-value > 0.05;)
checkresiduals(fit_arima)
##
## Ljung-Box test
##
## data: Residuals from ARIMA(2,0,0)(1,1,0)[4] with drift
## Q* = 3.7985, df = 5, p-value = 0.5788
##
## Model df: 3. Total lags used: 8
autoplot(JohnsonJohnson) +
autolayer(test, series = "Πραγματικά",PI = FALSE) +
autolayer(fit_snaive$mean, series = "Seasonal Naïve",PI = FALSE) +
autolayer(fit_hw$mean, series = "Holt-Winters",PI = FALSE) +
autolayer(fc_arima$mean, series = "ARIMA")
Παρατηρουμε πως το arima ειναι το πιo ακριβης μοντελο μας και μετα ερχεται το holt winters.
Εφάρμοσε accuracy() σε κάθε μοντέλο με το test set Συγκέντρωσε τα RMSE, MAE, MAPE σε ένα πίνακα Ποιο μοντέλο νικά;
accuracy(fit_snaive, test)
## ME RMSE MAE MPE MAPE MASE
## Training set 0.5884722 0.8214427 0.5920833 14.27463 14.77554 1.000000
## Test set 2.5425000 2.7765401 2.5425000 17.89911 17.89911 4.294159
## ACF1 Theil's U
## Training set 0.6642405 NA
## Test set -0.2050122 0.7697759
accuracy(fit_hw, test)
## ME RMSE MAE MPE MAPE MASE
## Training set 0.04204020 0.3595992 0.2602222 0.5285725 9.289055 0.4395027
## Test set 0.07397684 1.0865532 1.0427583 -0.4202121 7.763851 1.7611681
## ACF1 Theil's U
## Training set 0.1282904 NA
## Test set -0.8122651 0.290191
accuracy(fc_arima, test)
## ME RMSE MAE MPE MAPE MASE
## Training set 0.001523642 0.3596867 0.2419193 -0.163280 6.770657 0.4085899
## Test set -0.142883118 0.8136629 0.7225012 -1.524645 5.419212 1.2202695
## ACF1 Theil's U
## Training set -0.1186641 NA
## Test set -0.6150056 0.1989989
To arima ειναι το μοντελο με το μικροτερο σφάλμα
Επανεκπαίδευσε το νικητήριο μοντέλο σε όλα τα διαθέσιμα δεδομένα (jj) Κάνε forecast(h = 12) για 12 τρίμηνα = 3 χρόνια μπροστά Οπτικοποίησε με prediction intervals διακριτά
Τώρα που ξέρουμε το νικητή, εκπαιδεύουμε σε όλα τα δεδομένα
final_model <- auto.arima(JohnsonJohnson, lambda = 0)
final_forecast <- forecast(final_model, h = 12) # 3 χρόνια μπροστά
autoplot(final_forecast) +
ggtitle("Πρόβλεψη 1981-1983") +
ylab("Έσοδα") +
theme_minimal()
Υπολόγισε το μέσο ετήσιο growth rate των προβλεπόμενων ετών Συγκρίνετέ το με το ιστορικό growth rate — είναι ρεαλιστικό;
#Φτιαξαμε ενα dataframe για να υπολογισουμε το μεσο ετησιο growth rate.
yearly_growth_df <- data.frame(
year = numeric(),
full_year_total = numeric(),
growth_rate = numeric()
)
#This function calculates the yearly
calculate_growth_rate <- function(start_year,df){
prev_year_total <- NULL
for (i in 1:21){
current_year <- start_year + i
year <- window(JohnsonJohnson,start=c(current_year,1),end=c(current_year,4))
current_year_total <- sum(year)
if (!is.null(prev_year_total)){
growth <- ((current_year_total - prev_year_total) / prev_year_total) * 100
growth <- round(growth, 2)
}
else {
growth <- NA # The first year (1960) has no 1959 data to compare to
}
new_row <- data.frame(
year=current_year,
full_year_total = current_year_total,
growth_rate = growth
)
# Append the new row to our temporary data frame
df <- rbind(df, new_row)
# Update the previous year total for the next loop iteration
prev_year_total <- current_year_total
}
return(df)
}
# 3. Run the function and assign the result back to your variable
yearly_growth_df <- calculate_growth_rate(1959, yearly_growth_df)
# 4. View dataset
#View(yearly_growth_df)
head(yearly_growth_df, 25)
## year full_year_total growth_rate
## 1 1960 2.63 NA
## 2 1961 2.77 5.32
## 3 1962 3.01 8.66
## 4 1963 3.40 12.96
## 5 1964 4.16 22.35
## 6 1965 5.16 24.04
## 7 1966 6.06 17.44
## 8 1967 6.81 12.38
## 9 1968 8.19 20.26
## 10 1969 9.54 16.48
## 11 1970 13.50 41.51
## 12 1971 16.29 20.67
## 13 1972 19.35 18.78
## 14 1973 23.31 20.47
## 15 1974 25.20 8.11
## 16 1975 28.62 13.57
## 17 1976 31.77 11.01
## 18 1977 38.07 19.83
## 19 1978 45.00 18.20
## 20 1979 51.84 15.20
## 21 1980 58.50 12.85
#Now let preview the predicted quarter values.
# 1. Convert the ts object into a matrix so kable handles the years as row names
forecast_matrix <- as.matrix(t(matrix(final_forecast$mean, nrow=4,
dimnames=list(c("Q1", "Q2", "Q3", "Q4"), 1981:1983))))
# 2. Render the beautiful table
kable(t(forecast_matrix),
digits = 2,
caption = "Προβλέψεις Εσόδων Johnson & Johnson (1981-1983)")
| 1981 | 1982 | 1983 | |
|---|---|---|---|
| Q1 | 18.52 | 21.60 | 25.13 |
| Q2 | 17.06 | 19.83 | 23.10 |
| Q3 | 18.89 | 21.89 | 25.53 |
| Q4 | 13.47 | 15.69 | 18.27 |
τωρα θα συγκρινουμε το growth rate των ετων που προβλεψαμε για να δουμε αν ειναι ρεαλιστικό. * 1981 -> 11.23% * 1982 -> 16.64% * 1983 -> 16.64%
Ο seasonal naive έχει:
Mean Error: 2.5425000 RMSE: 2.7765401 MAPE: 17.89911
το ARIMA model έχει: Mean Error: -0.142883118 RMSE: 0.8136629 MAPE: 5.419212
auto.arima();
Ερμηνεύστε τα (p,d,q)(P,D,Q)₌.Κέρδισε το -> ARIMA(2,0,0)(1,1,0)[4]
Το (2,0,0) ειναι η βραθυπρόθεσμη μνήμη χωρίς εποχικότητα.
΄\(p = 2\) (Autoregressive - AR): δηλαδή 2 παρελθοντικά lags. d παράμετρος (Differencing) -> ο αριθμός διαφόρησεων που κάναμε ΄\(q = 0\) (Moving Average - MA)΄ : Δεν υπάρχουν παρελθοντικά σφάλματα (past shocks). Το σημερινό τρίμηνο δεν επηρεάζεται από το τυχαίο “σφάλμα” ή τις εκπλήξεις του προηγούμενου τριμήνου.
Το (1,1,0) μας εξηγεί την μακρυπρόθεσμη επίδραση με βάση τα προηγούμενα χρόνια
p = 1 (δηλαδή η περσινή ίδια περίοδος είναι στατιστικά σημαντική) d = 1 (κάναμε εποχική διαφόριση) q = 0
το drift υποδηλώνει οτι υπάρχει μια ανοδική τάση μακροπρόθεσμα
| Τρίμηνο | Lower | Upper | Lower | Upper |
|---|---|---|---|---|
| 1981 Q1 | 16.53479 | 20.73297 | 15.57364 | 22.01253 |
| 1981 Q2 | 15.17457 | 19.18062 | 14.26218 | 20.40766 |
| 1981 Q3 | 16.68561 | 21.37892 | 15.62610 | 22.82850 |
| 1981 Q4 | 11.87766 | 15.26528 | 11.11443 | 16.31355 |
| 1982 Q1 | 18.40188 | 25.34630 | 16.90664 | 27.58795 |
| 1982 Q2 | 16.84548 | 23.35265 | 15.45032 | 25.46140 |
| 1982 Q3 | 18.51659 | 25.88334 | 16.94573 | 28.28271 |
| 1982 Q4 | 13.25288 | 18.56606 | 12.12155 | 20.29887 |
| 1983 Q1 | 20.58554 | 30.67519 | 18.52304 | 34.09080 |
| 1983 Q2 | 18.87243 | 28.28574 | 16.95557 | 31.48350 |
| 1983 Q3 | 20.77142 | 31.37172 | 18.62382 | 34.98935 |
| 1983 Q4 | 14.84986 | 22.47328 | 13.30742 | 25.07811 |
Θα προτείνουμε long θέση για την μετοχή καθώς υπάρχει ανοδική αύξηση και τα επόμενα χρόνια προβλέπεται αύξηση 11-16% ανα έτος.