## ============================================================
## UAS ANALISIS RUNTUN WAKTU TERAPAN — STUDI KASUS 3
## ============================================================
library(urca)
library(tseries)
## Registered S3 method overwritten by 'quantmod':
##   method            from
##   as.zoo.data.frame zoo
library(lmtest)
## Loading required package: zoo
## 
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
## 
##     as.Date, as.Date.numeric
df <- read.csv("data_pdb.csv")
df <- df[order(df$Tahun, df$Q), ]
n  <- nrow(df); n                                   # 62 triwulan (2011Q1-2026Q2)
## [1] 62
lnC <- log(df$Konsumsi_RT)
lnY <- log(df$PDB)
C_ts <- ts(cbind(lnC = lnC, lnY = lnY), start = c(df$Tahun[1], df$Q[1]), frequency = 4)

# Dummy musiman (Q1 = referensi), pasca-COVID, dan COVID
Q2 <- as.numeric(df$Q == 2); Q3 <- as.numeric(df$Q == 3); Q4 <- as.numeric(df$Q == 4)
post <- as.numeric(df$Tahun > 2020 | (df$Tahun == 2020 & df$Q >= 2))   # mulai 2020Q2
pQ2 <- post * Q2; pQ3 <- post * Q3; pQ4 <- post * Q4                  # pola musiman pasca-COVID
covid_i <- as.numeric(df$Triwulan == "2020Q2")                         # puncak kontraksi
plot(C_ts, plot.type = "single", col = c("red", "navy"), lwd = 2,
     ylab = "Logaritma natural", xlab = "",
     main = "Ln Konsumsi Rumah Tangga dan Ln PDB (harga konstan 2010)")
legend("topleft", c("ln Konsumsi RT", "ln PDB"), col = c("red", "navy"), lwd = 2, bty = "n")

porsi <- 100 * df$Konsumsi_RT / df$PDB
summary(df[, c("Konsumsi_RT", "PDB")])
##   Konsumsi_RT           PDB         
##  Min.   : 964262   Min.   :1748731  
##  1st Qu.:1187064   1st Qu.:2173000  
##  Median :1417987   Median :2614517  
##  Mean   :1391180   Mean   :2593884  
##  3rd Qu.:1549032   3rd Qu.:2952280  
##  Max.   :1887683   Max.   :3576209
round(c(porsi_rata2 = mean(porsi), porsi_min = min(porsi), porsi_max = max(porsi)), 2)
## porsi_rata2   porsi_min   porsi_max 
##       53.76       51.87       55.23
# Porsi konsumsi per triwulan, sebelum dan sesudah COVID (pola musiman berubah)
round(rbind(pra_covid   = tapply(porsi[post == 0], df$Q[post == 0], mean),
            pasca_covid = tapply(porsi[post == 1], df$Q[post == 1], mean)), 2)
##                 1     2     3     4
## pra_covid   54.99 53.66 53.77 54.84
## pasca_covid 53.53 53.03 52.18 52.86
round(c(pra_covid = mean(porsi[post == 0]), pasca_covid = mean(porsi[post == 1])), 2)
##   pra_covid pasca_covid 
##       54.33       52.91
# ADF dengan dummy musiman, lag 0-4 dipilih BIC pada sampel yang sama;
# p-value MacKinnon (urca::punitroot).
adf_musiman <- function(x, q, trend = TRUE, maxlag = 4) {
  dx <- diff(x); N <- length(x)
  idx <- (maxlag + 2):N                                 # periode t yang dipakai
  best <- NULL
  for (p in 0:maxlag) {
    X <- data.frame(xlag = x[idx - 1])
    if (p > 0) for (j in 1:p) X[[paste0("dl", j)]] <- dx[idx - 1 - j]
    if (trend) X$tren <- idx
    X$S2 <- as.numeric(q[idx] == 2); X$S3 <- as.numeric(q[idx] == 3); X$S4 <- as.numeric(q[idx] == 4)
    m <- lm(dx[idx - 1] ~ ., data = X)
    if (is.null(best) || BIC(m) < best$bic)
      best <- list(bic = BIC(m), tau = coef(summary(m))["xlag", "t value"], p = p)
  }
  pv <- punitroot(best$tau, N = length(idx), trend = if (trend) "ct" else "c", statistic = "t")
  c(tau = round(best$tau, 3), lag = best$p, p_value = round(pv, 4))
}
q  <- df$Q
tabel_akar_unit <- rbind(
  `ln C (level, + tren)`   = adf_musiman(lnC, q, trend = TRUE),
  `ln Y (level, + tren)`   = adf_musiman(lnY, q, trend = TRUE),
  `Δ ln C (selisih)`       = adf_musiman(diff(lnC), q[-1], trend = FALSE),
  `Δ ln Y (selisih)`       = adf_musiman(diff(lnY), q[-1], trend = FALSE))
tabel_akar_unit
##                         tau lag p_value
## ln C (level, + tren) -2.409   0  0.3710
## ln Y (level, + tren) -2.439   0  0.3566
## Δ ln C (selisih)     -9.047   0  0.0000
## Δ ln Y (selisih)     -8.535   0  0.0000
# Pembanding: Phillips-Perron (non-parametrik, tidak perlu memilih lag)
round(c(PP_lnC = pp.test(lnC)$p.value, PP_dlnC = pp.test(diff(lnC))$p.value,
        PP_lnY = pp.test(lnY)$p.value, PP_dlnY = pp.test(diff(lnY))$p.value), 4)
## Warning in pp.test(diff(lnC)): p-value smaller than printed p-value
## Warning in pp.test(diff(lnY)): p-value smaller than printed p-value
##  PP_lnC PP_dlnC  PP_lnY PP_dlnY 
##  0.5114  0.0100  0.0439  0.0100
dl <- data.frame(lnC, lnY, Q2, Q3, Q4, post)

# Nilai kritis Engle-Granger untuk ADF residual (MacKinnon, 2 variabel, dengan konstanta)
cv_eg <- function(Tn) {
  b <- rbind(`1%` = c(-3.89644, -10.9519, -22.527),
             `5%` = c(-3.33613,  -6.1101,  -6.823),
             `10%`= c(-3.04445,  -4.2412,  -2.720))
  round(b[, 1] + b[, 2] / Tn + b[, 3] / Tn^2, 3)
}
cv <- cv_eg(n); cv
##     1%     5%    10% 
## -4.079 -3.436 -3.114
# Trial and error persamaan jangka panjang
spes_lr <- list(
  LR1 = lnC ~ lnY,
  LR2 = lnC ~ lnY + Q2 + Q3 + Q4,
  LR3 = lnC ~ lnY + Q2 + Q3 + Q4 + post)
hasil_lr <- do.call(rbind, lapply(names(spes_lr), function(nm) {
  m <- lm(spes_lr[[nm]], data = dl); u <- residuals(m)
  tau <- ur.df(u, type = "none", lags = 4, selectlags = "BIC")@teststat[1]
  data.frame(Model = nm, beta_lnY = round(coef(m)["lnY"], 4),
             R2 = round(summary(m)$r.squared, 4), BIC = round(BIC(m), 2),
             tau_ADF_resid = round(tau, 3), CV_5pct = cv["5%"],
             Tolak_H0 = tau < cv["5%"])
}))
rownames(hasil_lr) <- NULL
hasil_lr
##   Model beta_lnY     R2     BIC tau_ADF_resid CV_5pct Tolak_H0
## 1   LR1   0.9361 0.9952 -356.07        -1.979  -3.436    FALSE
## 2   LR2   0.9392 0.9976 -387.25        -1.867  -3.436    FALSE
## 3   LR3   0.9792 0.9987 -423.12        -2.639  -3.436    FALSE
# Tolak H0 (residual stasioner) -> terkointegrasi.

pilih_lr <- "LR3"   # BIC terkecil: dummy musiman + pergeseran level pasca-COVID
model_lr <- lm(spes_lr[[pilih_lr]], data = dl)
summary(model_lr)
## 
## Call:
## lm(formula = spes_lr[[pilih_lr]], data = dl)
## 
## Residuals:
##       Min        1Q    Median        3Q       Max 
## -0.012996 -0.004953  0.001177  0.004012  0.017219 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept) -0.295068   0.104716  -2.818  0.00667 ** 
## lnY          0.979241   0.007165 136.663  < 2e-16 ***
## Q2          -0.017503   0.002355  -7.431 6.72e-10 ***
## Q3          -0.022966   0.002400  -9.567 2.22e-13 ***
## Q4          -0.006173   0.002395  -2.578  0.01261 *  
## post        -0.019767   0.002775  -7.123 2.17e-09 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.006649 on 56 degrees of freedom
## Multiple R-squared:  0.9987, Adjusted R-squared:  0.9986 
## F-statistic:  8939 on 5 and 56 DF,  p-value: < 2.2e-16
beta_lr <- coef(model_lr)["lnY"]
ect <- residuals(model_lr)                          # ECT_t = ln C - ln C keseimbangan
round(100 * (exp(coef(model_lr)["post"]) - 1), 2)   # % pergeseran level konsumsi pasca-COVID
##  post 
## -1.96
ect_ts <- ts(100 * ect, start = c(df$Tahun[1], df$Q[1]), frequency = 4)
plot(ect_ts, type = "l", lwd = 2, col = "navy", xlab = "",
     ylab = "% di atas/bawah keseimbangan",
     main = "Error correction term: deviasi konsumsi dari keseimbangan jangka panjang")
abline(h = 0, lty = 2, col = "grey40")

# Sampel disamakan untuk semua model: mulai 2012Q2 (karena ada lag musiman t-4)
dC <- diff(lnC); dYv <- diff(lnY)            # dC[k] = lnC[k+1] - lnC[k]
t_idx <- 6:n
ecm_df <- data.frame(
  Triwulan = df$Triwulan[t_idx],
  dlnC   = dC[t_idx - 1],  dlnY   = dYv[t_idx - 1],
  dlnC_1 = dC[t_idx - 2],  dlnY_1 = dYv[t_idx - 2],
  dlnC_4 = dC[t_idx - 5],  dlnY_4 = dYv[t_idx - 5],
  ect1   = ect[t_idx - 1],                    # ECT periode t-1
  Q2 = Q2[t_idx], Q3 = Q3[t_idx], Q4 = Q4[t_idx],
  pQ2 = pQ2[t_idx], pQ3 = pQ3[t_idx], pQ4 = pQ4[t_idx],
  covid_i = covid_i[t_idx])
nrow(ecm_df)
## [1] 57
spes_sr <- list(
  S1 = dlnC ~ dlnY + ect1,
  S2 = dlnC ~ dlnY + ect1 + Q2 + Q3 + Q4,
  S3 = dlnC ~ dlnY + ect1 + Q2 + Q3 + Q4 + pQ2 + pQ3 + pQ4,
  S4 = dlnC ~ dlnY + ect1 + Q2 + Q3 + Q4 + pQ2 + pQ3 + pQ4 + dlnC_4 + dlnY_4,
  S5 = dlnC ~ dlnY + ect1 + Q2 + Q3 + Q4 + pQ2 + pQ3 + pQ4 + dlnC_4 + dlnY_4 + covid_i,
  S6 = dlnC ~ dlnY + ect1 + Q2 + Q3 + Q4 + pQ2 + pQ3 + pQ4 + dlnC_1 + dlnY_1 + dlnC_4 + dlnY_4 + covid_i)
ket_sr <- c(S1 = "dasar", S2 = "+ musiman", S3 = "+ musiman pasca-COVID",
            S4 = "S3 + lag musiman (t-4)", S5 = "S4 + dummy COVID 2020Q2",
            S6 = "S5 + lag 1")

hasil_sr <- do.call(rbind, lapply(names(spes_sr), function(nm) {
  m  <- lm(spes_sr[[nm]], data = ecm_df)
  ct <- summary(m)$coefficients
  data.frame(Model = nm, Spesifikasi = ket_sr[nm],
             lambda = round(ct["ect1", 1], 4), t_lambda = round(ct["ect1", 3], 2),
             p_lambda = round(ct["ect1", 4], 4),
             gamma_dlnY = round(ct["dlnY", 1], 4),
             adjR2 = round(summary(m)$adj.r.squared, 4),
             BIC = round(BIC(m), 2),
             BG4_p = round(bgtest(m, order = 4)$p.value, 4),
             BP_p  = round(bptest(m)$p.value, 4),
             JB_p  = round(jarque.bera.test(residuals(m))$p.value, 4))
}))
rownames(hasil_sr) <- NULL
hasil_sr$Layak <- with(hasil_sr, lambda < 0 & p_lambda < 0.05 & BG4_p > 0.05 & BP_p > 0.05 & JB_p > 0.05)
hasil_sr
##   Model             Spesifikasi  lambda t_lambda p_lambda gamma_dlnY  adjR2
## 1    S1                   dasar -0.8859    -4.00    2e-04     0.5457 0.6432
## 2    S2               + musiman -0.6819    -5.25    0e+00     1.0127 0.8812
## 3    S3   + musiman pasca-COVID -0.4747    -5.50    0e+00     1.1707 0.9645
## 4    S4  S3 + lag musiman (t-4) -0.3085    -4.83    0e+00     1.1103 0.9837
## 5    S5 S4 + dummy COVID 2020Q2 -0.3242    -5.13    0e+00     1.1954 0.9843
## 6    S6              S5 + lag 1 -0.4049    -4.98    0e+00     1.1767 0.9846
##       BIC  BG4_p   BP_p   JB_p Layak
## 1 -342.55 0.0000 0.1359 0.0000 FALSE
## 2 -396.36 0.0000 0.0035 0.3721 FALSE
## 3 -456.60 0.0054 0.0120 0.0000 FALSE
## 4 -495.19 0.0606 0.0896 0.0005 FALSE
## 5 -494.74 0.1008 0.1023 0.1012  TRUE
## 6 -490.16 0.5402 0.0929 0.1068  TRUE
# Layak: lambda negatif & signifikan, lolos uji autokorelasi (BG lag 4),
# heteroskedastisitas (BP), dan normalitas (JB). Dipilih: layak dengan BIC terkecil.
kand_sr <- hasil_sr[hasil_sr$Layak, ]
pilih_sr <- kand_sr$Model[which.min(kand_sr$BIC)]
pilih_sr
## [1] "S5"
ecm_model <- lm(spes_sr[[pilih_sr]], data = ecm_df)
summary(ecm_model)
## 
## Call:
## lm(formula = spes_sr[[pilih_sr]], data = ecm_df)
## 
## Residuals:
##        Min         1Q     Median         3Q        Max 
## -0.0045820 -0.0011578  0.0001019  0.0009233  0.0065905 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  0.0052211  0.0008054   6.483 5.98e-08 ***
## dlnY         1.1954490  0.0568770  21.018  < 2e-16 ***
## ect1        -0.3241555  0.0632456  -5.125 6.05e-06 ***
## Q2          -0.0226658  0.0035830  -6.326 1.02e-07 ***
## Q3          -0.0107636  0.0024242  -4.440 5.78e-05 ***
## Q4           0.0068673  0.0016594   4.138 0.000151 ***
## pQ2          0.0075936  0.0015383   4.937 1.14e-05 ***
## pQ3         -0.0043811  0.0017535  -2.498 0.016195 *  
## pQ4         -0.0084894  0.0019060  -4.454 5.52e-05 ***
## dlnC_4       0.5281887  0.0665873   7.932 4.33e-10 ***
## dlnY_4      -0.5818918  0.0785679  -7.406 2.56e-09 ***
## covid_i      0.0091706  0.0053575   1.712 0.093830 .  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.002239 on 45 degrees of freedom
## Multiple R-squared:  0.9874, Adjusted R-squared:  0.9843 
## F-statistic: 320.9 on 11 and 45 DF,  p-value: < 2.2e-16
bgtest(ecm_model, order = 4)                 # autokorelasi
## 
##  Breusch-Godfrey test for serial correlation of order up to 4
## 
## data:  ecm_model
## LM test = 7.7595, df = 4, p-value = 0.1008
bptest(ecm_model)                            # heteroskedastisitas
## 
##  studentized Breusch-Pagan test
## 
## data:  ecm_model
## BP = 17.193, df = 11, p-value = 0.1023
jarque.bera.test(residuals(ecm_model))       # normalitas
## 
##  Jarque Bera Test
## 
## data:  residuals(ecm_model)
## X-squared = 4.5811, df = 2, p-value = 0.1012
fit_ts <- ts(cbind(Aktual = ecm_df$dlnC, Fitted = fitted(ecm_model)) * 100,
             start = c(2012, 2), frequency = 4)
plot(fit_ts, plot.type = "single", col = c("black", "red"), lty = c(1, 2), lwd = 2,
     ylab = "% per triwulan", xlab = "", main = "Pertumbuhan konsumsi (q-to-q): aktual vs fitted")
legend("topleft", c("Aktual", "Fitted"), col = c("black", "red"), lty = 1:2, lwd = 2, bty = "n")

lambda  <- coef(ecm_model)["ect1"]
gamma0  <- coef(ecm_model)["dlnY"]
porsi_C <- mean(df$Konsumsi_RT / df$PDB)
ringkas <- c(
  elastisitas_jangka_panjang  = unname(beta_lr),
  elastisitas_jangka_pendek   = unname(gamma0),
  lambda_ECT                  = unname(lambda),
  persen_koreksi_per_triwulan = unname(-100 * lambda),
  half_life_triwulan          = unname(log(0.5) / log(1 + lambda)),
  MPC_jangka_panjang          = unname(beta_lr * porsi_C))
round(ringkas, 4)
##  elastisitas_jangka_panjang   elastisitas_jangka_pendek 
##                      0.9792                      1.1954 
##                  lambda_ECT persen_koreksi_per_triwulan 
##                     -0.3242                     32.4155 
##          half_life_triwulan          MPC_jangka_panjang 
##                      1.7692                      0.5264
# ECT pada periode penting (% deviasi dari keseimbangan)
penting <- c("2019Q4", "2020Q1", "2020Q2", "2020Q3", "2020Q4", "2021Q1", "2021Q2",
             "2021Q3", "2021Q4", "2022Q4", tail(df$Triwulan, 1))
round(setNames(100 * ect[match(penting, df$Triwulan)], penting), 2)
## 2019Q4 2020Q1 2020Q2 2020Q3 2020Q4 2021Q1 2021Q2 2021Q3 2021Q4 2022Q4 2026Q2 
##   0.48   0.24   1.41   1.72   0.93   0.65   0.50  -0.66  -0.39  -0.77   0.66