Kata Pengantar
Puji syukur saya panjatkan ke hadirat Tuhan Yang Maha Esa, karena atas rahmat dan karunia-Nya buku Econometric Models in Practice ini dapat tersusun. Buku ini disusun sebagai panduan praktis bagi mahasiswa, peneliti, dan praktisi yang ingin memahami serta menerapkan model ekonometrika modern dalam analisis data nyata. Pembahasan dimulai dari model dasar seperti Ordinary Least Squares (OLS), kemudian berlanjut ke model yang lebih kompleks seperti Instrumental Variables (IV), Fixed Effects Panel Model, Difference-in-Differences (DiD), Synthetic Control, hingga Vector Autoregression (VAR).
Pendekatan yang digunakan dalam buku ini menekankan pada keseimbangan antara konsep teoritis dan penerapan empiris. Setiap model tidak hanya dijelaskan secara matematis, tetapi juga disertai contoh implementasi menggunakan perangkat lunak statistik yang umum digunakan. Harapannya, pembaca tidak hanya memahami rumus, tetapi juga mampu menghubungkan model dengan konteks ekonomi dan kebijakan publik yang relevan.
Saya menyadari bahwa kesempurnaan adalah proses yang berkelanjutan. Oleh karena itu, kritik dan saran yang membangun sangat saya harapkan untuk penyempurnaan edisi berikutnya. Semoga buku ini dapat menjadi referensi yang bermanfaat dalam memperkuat literasi ekonometrika di kalangan akademisi dan praktisi di Indonesia.
Bandung, I Gede Nyoman Mindra Jaya, Ph.D
Model dasar untuk relasi linier:
\[ y_i = \beta_0 + \beta_1 x_{1i} + \beta_2 x_{2i} + \varepsilon_i. \]
Konsep Model Regresi Linier dan Nonlinier
Regresi memetakan hubungan variabel dependen \(y\) dengan satu/lebih prediktor \(x\). Intuisi sederhananya: kita mencari garis/hiperbidang yang meringkas pola rerata \(y\) terhadap \(x\).
Model klasik: \[ y_i = \beta_0 + \beta_1 x_{1i} + \cdots + \beta_k x_{ki} + \varepsilon_i,\quad E(\varepsilon_i\mid X)=0,\ \text{Var}(\varepsilon_i\mid X)=\sigma^2. \] “Linier” terhadap parameter berarti \(\beta\) hanya muncul pangkat satu. Bentuk fungsi terhadap \(x\) boleh fleksibel asalkan tetap linier pada \(\beta\): misalnya polinomial \(y=\beta_0+\beta_1 x+\beta_2 x^2+e\) tetap linier pada parameter.
Contoh fitur nonlinier tapi linier-dalam-parameter (basis):
Bentuk umum: \(y_i = f(x_i,\,\beta) + \varepsilon_i\) dengan \(f\) nonlinier pada \(\beta\) (mis. eksponensial, Michaelis–Menten). Estimasi biasanya via Nonlinear Least Squares atau MLE khusus.
set.seed(1)
x <- seq(0, 4, length.out = 200)
# fungsi "benar" (tanpa noise)
f_lin <- 2 + 1.5*x
f_non <- 2 + exp(0.8*x)
# data observasi (dengan noise)
y_lin <- f_lin + rnorm(length(x), 0, 0.5)
y_non <- f_non + rnorm(length(x), 0, 0.5)
plot(x, y_lin, pch = 16, cex = 0.5, col = "grey50",
ylab = "y", main = "Relasi Linier vs Nonlinier")
points(x, y_non, pch = 1, cex = 0.5, col = "grey50")
lines(x, f_lin, lwd = 2) # garis mulus linier
lines(x, f_non, lwd = 2, lty = 2) # garis mulus eksponensial
legend("topleft",
c("Data linier (noisy)", "Data nonlinier (noisy)", "Linier (teoretis)", "Eksponensial (teoretis)"),
pch = c(16, 1, NA, NA), lty = c(NA, NA, 1, 2), lwd = c(NA, NA, 2, 2),
pt.cex = 0.8, bty = "n")
Prinsip kunci: jika hubungan tidak linier terhadap \(x\), sering kali rekayasa fitur (polinomial, log, spline) cukup tanpa pindah ke regresi nonlinier murni.
OLS memilih \(\hat\beta\) yang meminimalkan jumlah kuadrat residu: \[ \hat\beta = \arg\min_\beta (y-X\beta)’(y-X\beta) \Rightarrow \hat\beta=(X’X)^{-1}X’y. \] Di bawah asumsi klasik (Gauss–Markov: linearitas, eksogenitas, ragam konstan, tak ada multikolinearitas sempurna), \(\hat\beta\) adalah BLUE (Best Linear Unbiased Estimator).
Asumsikan bahwa kesalahan berdistribusi normal:
\[
\varepsilon \sim N(0, \sigma^2 I).
\]
Likelihood function:
\[ L(\beta, \sigma^2) \propto (\sigma^2)^{-n/2} \exp\Big\{ -\tfrac{1}{2\sigma^2}(y - X\beta)'(y - X\beta) \Big\}. \]
Memaksimalkan log-likelihood menghasilkan
\[
\hat{\beta}_{\text{MLE}} = \hat{\beta}_{\text{OLS}}.
\]
Artinya, di bawah asumsi error normal, estimasi OLS juga merupakan
estimasi MLE;
perbedaan hanya muncul pada estimasi varians error
\(\sigma^2\), yaitu pembagi \(n\) vs \(n - k -
1\).
Catatan praktik:
Saat terjadi heteroskedastisitas atau autokorelasi, \(\hat{\beta}_{\text{OLS}}\) tetap tak
bias,
namun standar error tidak valid. Untuk mengoreksinya,
gunakan robust standard error
(seperti Heteroskedasticity-Consistent / HC, atau Newey–West)
atau gunakan pendekatan GLS/WLS.
Hipotesis: \(H_0: \beta_j = b_0\). Statistik: \[ t = \frac{\hat\beta_j - b_0}{SE(\hat\beta_j)} \sim t_{(n-k-1)}. \] P-value kecil \(\Rightarrow\) tolak \(H_0\). Interval kepercayaan \((1-\alpha)\%\): \(\hat\beta_j \pm t_{\alpha/2}\cdot SE\).
Hipotesis linear: \(H_0: R\beta=r\) dengan \(q\) restriksi. Statistik F dari model terbatas vs penuh menilai signifikansi gabungan.
\(H_0: \beta_1=\cdots=\beta_k=0\). Statistik F pada tabel
summary(lm) — menilai apakah prediktor bersama-sama
menjelaskan variasi \(y\).
Asumsi klasik memastikan inferensi sah: standar error, p-value, dan CI akurat.
Remedi umum bila asumsi gagal: transformasi variabel (log/Box‑Cox), tambah fitur (polinomial/interaksi), robust SE (HC0–HC3), Newey–West untuk autokorelasi, atau model alternatif (GLS, ARIMA errors).
Kita buat data dengan dua prediktor, salah satunya sedikit kolinear dengan yang lain, plus heteroskedastisitas ringan agar diagnosa bermakna.
n <- 250
X1 <- rnorm(n, 50, 10)
X2_raw <- rnorm(n, 30, 5)
X2 <- 0.6*scale(X1) + 0.8*scale(X2_raw) # sengaja kolinear sedang
sigma <- 2 + 0.02*(X1 - mean(X1))^2 # heteroskedastisitas lemah
eps <- rnorm(n, 0, sigma)
Y <- 10 + 0.5*X1 - 0.3*X2 + eps
dat <- tibble(Y, X1, X2, sigma)
pairs(~ Y + X1 + X2, data = dat, main = "Scatterplot Matrix")
fit <- lm(Y ~ X1 + X2, data = dat)
summary(fit)
##
## Call:
## lm(formula = Y ~ X1 + X2, data = dat)
##
## Residuals:
## Min 1Q Median 3Q Max
## -21.4946 -2.3506 0.0966 2.2356 17.7011
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 13.81273 1.80905 7.635 4.92e-13 ***
## X1 0.42005 0.03614 11.623 < 2e-16 ***
## X2 -0.32006 0.37585 -0.852 0.395
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 4.735 on 247 degrees of freedom
## Multiple R-squared: 0.4538, Adjusted R-squared: 0.4494
## F-statistic: 102.6 on 2 and 247 DF, p-value: < 2.2e-16
confint(fit) # interval kepercayaan koefisien
## 2.5 % 97.5 %
## (Intercept) 10.2495975 17.3758554
## X1 0.3488746 0.4912338
## X2 -1.0603371 0.4202134
Interpretasi cepat.
Std. Error dan
Pr(>|t|) untuk signifikansi. - R-squared
mengukur proporsi variasi \(Y\) yang dijelaskan.newdf <- tibble(X1 = c(40, 60))
newdf$X2 <- matrix(c(25, 35), ncol = 1) # bikin nmatrix.1 juga
predict(fit, newdf, interval = "confidence")
## fit lwr upr
## 1 22.61335 3.669021 41.55767
## 2 27.81381 2.368647 53.25898
predict(fit, newdf, interval = "prediction")
## fit lwr upr
## 1 22.61335 1.497742 43.72895
## 2 27.81381 0.713300 54.91432
Catatan. Interval confidence untuk rerata bersifat lebih sempit daripada prediction untuk observasi baru.
# Uji t sudah ada di summary; contoh uji F gabungan:
# H0: beta_X1 = beta_X2 = 0
fit0 <- lm(Y ~ 1, data = dat)
anova(fit0, fit) # perbandingan model terbatas vs penuh (uji F)
## Analysis of Variance Table
##
## Model 1: Y ~ 1
## Model 2: Y ~ X1 + X2
## Res.Df RSS Df Sum of Sq F Pr(>F)
## 1 249 10139.1
## 2 247 5538.1 2 4601.1 102.6 < 2.2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Plot residual vs fitted semestinya acak; residualPlots
memeriksa nonlinieritas.
par(mfrow=c(1,2))
plot(fit, which = 1) # Residuals vs Fitted
car::residualPlots(fit) # Component + Residual (CERES) plots
## Test stat Pr(>|Test stat|)
## X1 1.0486 0.2954
## X2 -0.8251 0.4101
## Tukey test 1.1241 0.2610
par(mfrow=c(1,1))
Gunakan QQ-plot dan uji formal (Shapiro & Anderson–Darling).
plot(fit, which = 2) # QQ-plot
shapiro.test(residuals(fit)) # sensitif untuk n besar
##
## Shapiro-Wilk normality test
##
## data: residuals(fit)
## W = 0.94741, p-value = 7.73e-08
nortest::ad.test(residuals(fit))
##
## Anderson-Darling normality test
##
## data: residuals(fit)
## A = 2.9442, p-value = 2.043e-07
Breusch–Pagan (bentuk dasar & White-style).
lmtest::bptest(fit) # BP test (fitted)
##
## studentized Breusch-Pagan test
##
## data: fit
## BP = 10.255, df = 2, p-value = 0.005932
lmtest::bptest(fit, ~ fitted(fit) + I(fitted(fit)^2)) # White-type
##
## studentized Breusch-Pagan test
##
## data: fit
## BP = 58.132, df = 2, p-value = 2.381e-13
plot(fit, which = 3) # Scale-Location plot
Robust SE (HC) bila heteroskedastik:
coeftest(fit, vcov = vcovHC(fit, type = "HC3"))
##
## t test of coefficients:
##
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 13.812726 2.370668 5.8265 1.758e-08 ***
## X1 0.420054 0.049091 8.5567 1.246e-15 ***
## X2 -0.320062 0.406506 -0.7873 0.4318
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Durbin–Watson dan Breusch–Godfrey.
lmtest::dwtest(fit)
##
## Durbin-Watson test
##
## data: fit
## DW = 1.953, p-value = 0.3571
## alternative hypothesis: true autocorrelation is greater than 0
lmtest::bgtest(fit, order = 2) # uji autokorelasi orde 2
##
## Breusch-Godfrey test for serial correlation of order up to 2
##
## data: fit
## LM test = 0.77854, df = 2, p-value = 0.6776
Untuk deret waktu, pertimbangkan menambah lag residual atau gunakan Newey–West SE:
coeftest(fit, vcov = NeweyWest(fit, lag = 2, prewhite = FALSE))
VIF dan condition number.
car::vif(fit)
## X1 X2
## 1.663999 1.663999
X <- model.matrix(fit)
kappa(qr.R(qr(X))) # condition number (>>30 indikasi masalah)
## [1] 273.1844
Remedi: buang/rekayasa fitur, regularisasi (Ridge/Lasso), atau kumpulkan lebih banyak data.
Cook’s distance, leverage, dan pengaruh.
par(mfrow=c(1,2))
plot(fit, which = 4) # Cook's distance
plot(fit, which = 5) # Residuals vs Leverage
par(mfrow=c(1,1))
influence.measures(fit)$infmat[1:5,]
## dfb.1_ dfb.X1 dfb.X2 dffit cov.r cook.d
## 1 0.051030617 -0.065363990 -0.002496740 -0.11851524 1.000909 4.670010e-03
## 2 -0.085733056 0.095141837 -0.040630593 0.10988430 1.025760 4.031613e-03
## 3 0.009082744 -0.008283275 0.009692429 0.01149691 1.030027 4.423737e-05
## 4 0.013489488 -0.019628988 0.044407632 -0.05785212 1.019275 1.118744e-03
## 5 0.010630488 -0.007819191 0.005232231 0.01929349 1.016156 1.245448e-04
## hat
## 1 0.00853456
## 2 0.02019325
## 3 0.01735334
## 4 0.01065226
## 5 0.00478831
Dengan asumsi normalitas error, OLS = MLE untuk \(\beta\). Kita verifikasi dengan optimisasi numerik sederhana.
negloglik <- function(par, y, X){
beta <- par[1:ncol(X)]
logs2 <- par[length(par)]
s2 <- exp(logs2) # pastikan positif
e <- y - X %*% beta
n <- length(y)
0.5*n*log(2*pi) + 0.5*n*log(s2) + 0.5*sum(e^2)/s2
}
X <- model.matrix(fit)
y <- dat$Y
init <- c(coef(fit), log(var(residuals(fit))))
opt <- optim(init, negloglik, y=y, X=X, method="BFGS", hessian=TRUE)
beta_mle <- opt$par[1:ncol(X)]
cbind(OLS=coef(fit), MLE=beta_mle)
## OLS MLE
## (Intercept) 13.8127264 13.8127264
## X1 0.4200542 0.4200542
## X2 -0.3200619 -0.3200619
Hasil \(\beta\) dari MLE praktis sama dengan OLS (perbedaan numerik kecil).
fit <- lm(Y ~ X1 + X2, data = dat)
list(
summary = summary(fit),
confint = confint(fit),
t_overallF = anova(lm(Y ~ 1, dat), fit),
bp = bptest(fit),
bp_white = bptest(fit, ~ fitted(fit) + I(fitted(fit)^2)),
dw = dwtest(fit),
bg = bgtest(fit, order = 2),
vif = car::vif(fit),
robust = coeftest(fit, vcov = vcovHC(fit, type = "HC3"))
)
Penutup. OLS tetap rajanya untuk model linier—cepat, transparan, dan kuat—asal rumah tangga asumsi rapi. Saat dunia nyata berisik, naikkan perisai robust SE dan akal sehat spesifikasi.
n <- 200
x1 <- rnorm(n)
x2 <- runif(n, -1, 1)
y <- 3 + 2*x1 - 1.5*x2 + rnorm(n, sd = 0.6)
ols_model <- lm(y ~ x1 + x2)
summary(ols_model)
##
## Call:
## lm(formula = y ~ x1 + x2)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1.78896 -0.34550 0.04367 0.38220 1.33299
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 2.99707 0.04253 70.47 <2e-16 ***
## x1 1.99354 0.04074 48.94 <2e-16 ***
## x2 -1.53342 0.07308 -20.98 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.6001 on 197 degrees of freedom
## Multiple R-squared: 0.9344, Adjusted R-squared: 0.9338
## F-statistic: 1404 on 2 and 197 DF, p-value: < 2.2e-16
Catatan interpretasi. Koefisien menyatakan perubahan rata-rata pada \(y\) akibat perubahan satu unit pada kovariat terkait, ceteris paribus.
Kita mulai dari model linier sederhana:
\[ Y_i = \beta_0 + \beta_1 X_i + \epsilon_i \]
dengan
Estimasi biasa dilakukan dengan Ordinary Least Squares (OLS).
Agar OLS menghasilkan estimator tak-bias (unbiased) dan
konsisten, kita butuh asumsi kunci yang disebut
eksogenitas:
\[ \mathbb{E}[\epsilon_i \mid X_i] = 0 \quad \text{atau ekuivalen} \quad \text{Cov}(X_i, \epsilon_i)=0. \]
Artinya:
Inilah kondisi ideal yang diajarkan di buku teks ekonometrika intro: dunia patuh, data jinak, dosen bahagia.
Endogeneity terjadi ketika asumsi eksogenitas dilanggar, yaitu:
\[ \text{Cov}(X_i, \epsilon_i) \neq 0. \]
Secara verbal:
Variabel penjelas \(X_i\) “tercemar” oleh sesuatu yang juga ada di error \(\epsilon_i\), sehingga \(X_i\) sebagian mengandung sinyal yang bukan murni sebab \(Y_i\), tapi justru “reaksi bersama” dari mekanisme lain.
Akibatnya, OLS tidak lagi bisa diperlakukan sebagai estimator hubungan kausal \(X \rightarrow Y\).
Kita menyebut \(X_i\) sebagai endogenous regressor (regresor endogen).
(a) Omitted Variable Bias (Variabel Terlewat) Ada variabel penting yang memengaruhi \(Y_i\) tetapi tidak masuk model, dan variabel itu juga berkorelasi dengan \(X_i\).
Contoh:
Secara skematik:
\[ \text{Kemampuan} \rightarrow \text{Pendidikan} \rightarrow \text{Gaji} \] dan \[ \text{Kemampuan} \rightarrow \text{Gaji (langsung)}. \]
Kemampuan ada di error. Masalah.
(b) Simultanitas / Reverse Causality \(X_i\) memengaruhi \(Y_i\), tetapi \(Y_i\) juga memengaruhi \(X_i\). Hubungan dua arah.
Contoh klasik penawaran–permintaan:
Ini juga muncul di:
Measurement Error (Kesalahan Pengukuran) Variabel \(X_i\) dicatat dengan noise.
Misal kita ingin “jam belajar”, tapi data berasal dari self-report siswa yang cenderung melebihkan demi terlihat rajin. Distorsi ini masuk ke error dan membuat OLS bias.
Dynamic Panel Bias (Nickell Bias) Dalam model panel dinamis:
\[ Y_{it} = \alpha Y_{i,t-1} + \beta X_{it} + \mu_i + \varepsilon_{it}, \]
lag dependen \(Y_{i,t-1}\) berkorelasi dengan efek tetap individu \(\mu_i\), sehingga endogen.
Estimator OLS untuk \(\beta_1\) jadi bias:
\[ \hat{\beta}_1^{OLS} \xrightarrow{E} \beta_1 + \frac{\text{Cov}(X_i,\epsilon_i)}{\text{Var}(X_i)}. \]
Bahkan dengan n → ∞, estimator tetap tidak mendekati nilai benar.
Uji-t dan p-value tetap bisa terlihat “signifikan” padahal model salah spesifikasi. Ini ilusi presisi.
Masukkan variabel yang relevan agar sumber korelasi berkurang.
Gunakan variabel instrumen \(Z\) yang relevan dan eksogen:
Tahap 1: \[ X_i = \pi_0 + \pi_1 Z_i + v_i \] Tahap 2: \[ Y_i = \beta_0 + \beta_1 \hat{X}_i + e_i \]
Hilangkan efek individu konstan \(\mu_i\) dengan transformasi within.
Gunakan perubahan sebelum–sesudah antar kelompok untuk mengidentifikasi efek kausal.
Gunakan lag terdalam sebagai instrumen internal untuk model panel dinamis.
Endogeneity bukan hanya masalah teknis, tapi juga persoalan filsafat kausalitas: setiap solusi menuntut asumsi baru yang harus dijustifikasi secara ilmiah.
Instrumental Variable (IV) Regression digunakan ketika variabel penjelas utama endogen, yaitu berkorelasi dengan error (\(\text{Cov}(x,u)\neq 0\)). Endogenitas muncul karena omitted variables, measurement error, atau simultaneity (keterbalikan kausal).
Bayangkan kita ingin mengestimasi pengaruh pendidikan \(X\) terhadap pendapatan \(Y\). Masalahnya, pendidikan sering berkorelasi dengan hal-hal yang tak teramati, seperti kemampuan, motivasi, atau lingkungan keluarga \(u\). Model sederhananya:
\[ Y_i = \beta_0 + \beta_1 X_i + \epsilon_i. \]
Idealnya, \(\mathrm{Cov}(X_i, \epsilon_i) = 0\). Namun jika ternyata \(\mathrm{Cov}(X_i, \epsilon_i) \neq 0\), maka asumsi OLS rusak. Akibatnya:
Sumber endogenitas dapat berasal dari:
Untuk itulah muncul Instrumental Variable (IV) — alat bantu untuk “menyaring” pengaruh murni \(X\) yang benar-benar eksogen.
Apa itu variabel instrumen?
Variabel instrumen adalah “solusi kreatif” untuk masalah ini: kita cari variabel \(Z\) yang:
Jadi inti: \(Z\) membantu kita mendapatkan “variasi murni” dalam \(X\) yang bebas dari bias karena \(u\). Dengan variasi ini kita bisa mengestimasi \(\beta\) dengan lebih kredibel
Contoh sederhana
Misalnya kita ingin tahu: apakah merokok \((X)\) menyebabkan kesehatan menurun \((Y)\)?
Tapi kita tahu: banyak variabel tersembunyi seperti “stress”,
“kepribadian”, “kondisi sosio-ekonomi” yang bisa mempengaruhi baik
merokok maupun kesehatan → masalah endogenitas.
Kita bisa memilih instrumen misalnya: tarif cukai rokok di suatu
wilayah \((Z)\). Alasan:
- Tarif cukai mempengaruhi kemungkinan seseorang merokok (relevan).
:contentReferenceoaicite:1
- Kita anggap tarif cukai tidak langsung mempengaruhi kesehatan
kecuali melalui merokok (validitas eksklusinya) — ini asumsi
besar!
- Jika asumsi ini kira-kira benar, maka kita bisa menggunakan tarif
cukai sebagai instrumen untuk merokok dan mengestimasi efek merokok
terhadap kesehatan. :contentReferenceoaicite:2
(Disclaimer: dalam praktik, seringkali asumsi eksklusinya diperdebatkan — karena tarif bisa pula mempengaruhi kesehatan melalui jalur lain, misalnya melalui pengeluaran rumah tangga, stress fiskal, dsb.)
Catatan
Mengapa ini penting? Karena dalam banyak
penelitian, kita ingin tahu “apa yang terjadi jika kita
mengubah \(X\)?” — itu
pertanyaan sebab-akibat.
Tapi banyak gap: orang memilih sendiri, ada faktor tersembunyi, penyakit
bisa mempengaruhi pilihan, dsb. Instrumen adalah seperti “eksternal
shove” ke \(X\) yang kita harap cukup
acak untuk memberi kredibilitas sebab-akibatnya.
Bayangkan: kamu ingin tahu apakah tidur lebih lama bikin nilai kuliah
kamu naik, tapi tentu banyak faktor (motivasi, kesehatan,
murid-lebih-bling). Jika suatu universitas mengubah kebijakan jam tidur
mahasiswa secara acak karena pembangunan kampus — itu bisa jadi
instrumen. (Ya, agak absurd tapi you get the idea.)
Catatan:
Ringkasnya: OLS menjawab korelasi, IV berupaya mendekati kausalitas ketika ada endogenitas.
Catatan Instrumen \(z\) diperlukan untuk mengisolasi variasi \(x\) yang eksogen terhadap error:
- Relevansi: \(\text{Cov}(z, x) \neq 0\) — instrumen harus kuat memprediksi \(x\).
- Validitas (Eksogenitas): \(\text{Cov}(z, u) = 0\) — instrumen tidak memengaruhi \(y\) kecuali melalui \(x\).
Aplikasi umum: pengaruh pendidikan terhadap upah (instrumen: jarak ke kampus), permintaan–penawaran (harga endogen; instrumen: shock biaya), kebijakan publik (intensitas program, kuota, lotere), kesehatan (ketersediaan fasilitas).
Intuisi: IV “meminjam” variasi \(x\) yang datang dari \(z\) (yang bersih dari noise endogen), sehingga estimasi efek kausal \(x \to y\) menjadi konsisten.
Sebuah variabel \(Z\) disebut instrumen yang valid jika memenuhi dua syarat pokok:
Dengan kata lain, \(Z\) memengaruhi \(Y\) hanya melalui \(X\), bukan lewat jalur lain. Secara intuitif: instrumen harus “mendorong” \(X\), tetapi tidak “menyentuh” \(Y\) secara langsung.
Model dengan variabel endogen disebut structural model. Misalnya:
\[ Y = \beta_0 + \beta_1 X + u, \]
dengan \(X\) endogen. Karena \(X\) berkorelasi dengan \(u\), OLS tidak dapat dipakai secara valid. Solusi umum adalah Two-Stage Least Squares (2SLS) menggunakan instrumen \(Z\).
Tahap 1 (First Stage): bangun reduced form untuk \(X\) \[ X = \pi_0 + \pi_1 Z + v \quad \Rightarrow \quad \hat{X} = \hat{\pi}_0 + \hat{\pi}_1 Z. \]
Tahap 2 (Second Stage): gunakan \(\hat{X}\) pada persamaan struktural \[ Y = \beta_0 + \beta_1 \hat{X} + e. \]
Secara aljabar, 2SLS/IV dapat ditulis sebagai \[ \hat{\beta}_{IV} = (X'P_Z X)^{-1} X' P_Z Y, \quad \text{dengan } P_Z = Z (Z'Z)^{-1} Z'. \]
Interpretasi: \(P_Z\) mem-proyeksi \(X\) ke ruang yang dijelaskan oleh \(Z\) (bagian yang eksogen), lalu efeknya diukur pada \(Y\).
Catatan praktik: kekuatan instrumen penting untuk diperiksa (mis. first-stage F-statistic > 10 sebagai aturan praktis).
Instrumen bekerja seperti filter kausalitas: memisahkan sinyal murni \(X\) dari gangguan \(u\). Tanpa instrumen, kita mudah terjebak pada interpretasi korelasional belaka. Dengan instrumen, kita punya pegangan untuk mengatakan “bagian dari \(X\) yang didorong oleh \(Z\) benar-benar memengaruhi \(Y\).”
IV bukan tongkat sihir: ia membantu ketika instrumen valid dan kuat. Diagnosis (relevansi dan eksogenitas) adalah bagian dari sains, bukan sekadar ritual.
Model struktural sederhana (satu regressor endogen):
\[ \begin{aligned} \text{(Struktural)}\quad & y = \beta_0 + \beta_1 x + w'\gamma + u, \qquad \text{dengan } \mathrm{Cov}(x,u)\neq 0, \\\\ \text{(First stage)}\quad & x = \pi_0 + \pi_1 z + w'\delta + v, \qquad \text{dengan } \mathrm{Cov}(z,u)=0,~\mathrm{Cov}(z,v)\neq 0, \end{aligned} \]
di mana \(w\) adalah kovariat eksogen (kontrol).
Identifikasi:
- Just-identified: jumlah instrumen eksklusif (di luar
\(w\)) = jumlah regressor
endogen.
- Overidentified: instrumen lebih banyak dari endogen
(memungkinkan uji validitas — Sargan/Hansen).
Target estimand: pada model linear dengan kesetaraan struktural, IV menaksir ATE (average treatment effect) jika seluruh asumsi terpenuhi. Pada kasus biner dengan kepatuhan heterogen dan monotonicity, interpretasi sering kali LATE (local average treatment effect).
Secara aljabar: \[ \hat\beta^{IV} = (X'P_Z X)^{-1} X'P_Z y, \qquad P_Z = Z\,(Z'Z)^{-1}Z'. \] Di sini \(X\) memuat kolom endogen dan eksogen, \(Z\) memuat semua instrumen (instrumen eksklusif + eksogen). 1SLS menghitung proyeksi \(P_Z y\) dan \(P_Z X\) lalu OLS pada data terproyeksi. Pada kasus just-identified, 1SLS identik dengan 2SLS.
Tahap-1 (first stage): regresikan setiap regressor
endogen pada seluruh instrumen \(Z\)
dan eksogen \(w\); simpan nilai
terprediksi \(\hat x\).
Tahap-2: regresikan \(y\) pada \(\hat
x\) dan \(w\) dengan OLS.
Secara numerik, 2SLS \(=\) 1SLS;
perbedaan hanya pada cara implementasi.
Diagnostik penting:
- Uji kelemahan instrumen: F-statistik tahap-1
(rule-of-thumb \(F>10\)).
- Uji overidentifikasi: Sargan atau Hansen J (jika
overidentified).
- Uji endogenitas: Wu–Hausman (apakah OLS vs IV berbeda
signifikan).
- SE robust: gunakan HC
(heteroskedasticity-consistent) karena IV sensitif terhadap
heteroskedastisitas.
Kita simulasikan endogenitas: \(x\) mengandung komponen error struktural \(u\) (melalui \(v\)). Instrumen \(z\) memengaruhi \(x\) tapi tidak \(y\) secara langsung.
n <- 1000
# Kovariat eksogen (kontrol)
w1 <- rnorm(n)
w2 <- rbinom(n, 1, 0.4)
# Instrumen eksklusif yang relevan dan valid
z <- 0.5*rnorm(n) + 0.5*w1 + rnorm(n, sd = 0.5)
# Error struktural dan korelasi endogenitas
u <- rnorm(n)
v <- 0.6*u + rnorm(n) # membuat x berkorelasi dg u via v
# First stage: x dipengaruhi instrumen z, kontrol w, dan v yang kor. dg u
x <- 0.8 + 0.9*z + 0.5*w1 - 0.4*w2 + v
# Structural: y dipengaruhi x, kontrol w, dan u
y <- 1.5 + 1.2*x + 0.7*w1 - 0.5*w2 + u
dat <- tibble(y, x, z, w1, w2 = factor(w2))
ols_fit <- lm(y ~ x + w1 + w2, data = dat)
summary(ols_fit)
##
## Call:
## lm(formula = y ~ x + w1 + w2, data = dat)
##
## Residuals:
## Min 1Q Median 3Q Max
## -3.0455 -0.6060 -0.0229 0.6353 3.2190
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.24126 0.04258 29.150 < 2e-16 ***
## x 1.49459 0.02220 67.313 < 2e-16 ***
## w1 0.43364 0.03477 12.470 < 2e-16 ***
## w21 -0.29325 0.05980 -4.904 1.1e-06 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.92 on 996 degrees of freedom
## Multiple R-squared: 0.9005, Adjusted R-squared: 0.9002
## F-statistic: 3005 on 3 and 996 DF, p-value: < 2.2e-16
coeftest(ols_fit, vcov = vcovHC(ols_fit, type = "HC3")) # SE robust
##
## t test of coefficients:
##
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.241256 0.040811 30.4151 < 2.2e-16 ***
## x 1.494595 0.021420 69.7742 < 2.2e-16 ***
## w1 0.433642 0.034569 12.5443 < 2.2e-16 ***
## w21 -0.293248 0.059351 -4.9409 9.118e-07 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Karena \(x\) endogen, koefisien OLS untuk \(x\) cenderung bias. Bandingkan nanti dengan IV.
AER::ivregSpesifikasi formula: y ~ x + w1 + w2 | z + w1 + w2. Di
kanan tanda | taruh semua instrumen
(instrumen eksklusif + eksogen yang selalu valid).
iv_2sls <- AER::ivreg(y ~ x + w1 + w2 | z + w1 + w2, data = dat)
summary(iv_2sls, diagnostics = TRUE) # tampilkan uji endogenitas, weak IV, overid (jika ada)
##
## Call:
## AER::ivreg(formula = y ~ x + w1 + w2 | z + w1 + w2, data = dat)
##
## Residuals:
## Min 1Q Median 3Q Max
## -3.42894 -0.71128 -0.02568 0.76344 3.88219
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.60376 0.06523 24.587 < 2e-16 ***
## x 1.04099 0.05773 18.032 < 2e-16 ***
## w1 0.85163 0.06286 13.547 < 2e-16 ***
## w21 -0.52806 0.07603 -6.946 6.79e-12 ***
##
## Diagnostic tests:
## df1 df2 statistic p-value
## Weak instruments 1 996 264.6 <2e-16 ***
## Wu-Hausman 1 995 124.6 <2e-16 ***
## Sargan 0 NA NA NA
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.096 on 996 degrees of freedom
## Multiple R-Squared: 0.8588, Adjusted R-squared: 0.8584
## Wald test: 1162 on 3 and 996 DF, p-value: < 2.2e-16
coeftest(iv_2sls, vcov = vcovHC(iv_2sls, type = "HC3"))
##
## t test of coefficients:
##
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.603762 0.065725 24.4012 < 2.2e-16 ***
## x 1.040992 0.060797 17.1225 < 2.2e-16 ***
## w1 0.851628 0.064668 13.1693 < 2.2e-16 ***
## w21 -0.528062 0.075412 -7.0024 4.622e-12 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Membaca diagnostics
(summary(..., diagnostics=TRUE)):
- Weak instruments: lihat “Weak instruments” /
“First-stage F”.
- Wu–Hausman: menguji endogenitas \(x\).
- Sargan (jika ada >1 instrumen eksklusif): menguji
validitas gabungan instrumen.
Konstruksi \(Z\) dan \(X\) lalu hitung \(\hat\beta^{IV}=(X'P_ZX)^{-1}X'P_Z y\).
# Desain matriks: X (konstanta, x endogen, kontrol), Z (konstanta, instrumen, kontrol)
X <- model.matrix(~ x + w1 + w2, data = dat) # termasuk intercept
Z <- model.matrix(~ z + w1 + w2, data = dat)
PZ <- Z %*% solve(t(Z) %*% Z) %*% t(Z)
beta_iv <- solve(t(X) %*% PZ %*% X) %*% (t(X) %*% PZ %*% dat$y)
colnames(beta_iv) <- "coef_IV_1SLS"
beta_iv
## coef_IV_1SLS
## (Intercept) 1.6037624
## x 1.0409924
## w1 0.8516277
## w21 -0.5280620
Untuk SE pada 1SLS manual, gunakan sandwich formula IV (tidak seremeh OLS). Praktisnya, gunakan
ivreg/ivfixedyang sudah mengimplementasikan varian robust.
Tahap-1: prediksi \(\hat x\) dari \((z, w)\). Tahap-2: regresikan \(y\) pada \(\hat x\) dan \(w\) dengan OLS.
# Tahap-1
fs_fit <- lm(x ~ z + w1 + w2, data = dat)
summary(fs_fit) # cek F-stat (kekuatan instrumen)
##
## Call:
## lm(formula = x ~ z + w1 + w2, data = dat)
##
## Residuals:
## Min 1Q Median 3Q Max
## -3.5840 -0.8050 0.0564 0.7821 3.2299
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 0.79908 0.04910 16.274 < 2e-16 ***
## z 0.85511 0.05257 16.267 < 2e-16 ***
## w1 0.50354 0.04396 11.455 < 2e-16 ***
## w21 -0.47685 0.07448 -6.402 2.36e-10 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.167 on 996 degrees of freedom
## Multiple R-squared: 0.4963, Adjusted R-squared: 0.4948
## F-statistic: 327.2 on 3 and 996 DF, p-value: < 2.2e-16
xhat <- fs_fit$fitted.values
# Tahap-2
tsls_fit <- lm(y ~ xhat + w1 + w2, data = dat)
summary(tsls_fit)
##
## Call:
## lm(formula = y ~ xhat + w1 + w2, data = dat)
##
## Residuals:
## Min 1Q Median 3Q Max
## -5.8724 -1.4524 0.0327 1.4399 6.5000
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.6038 0.1235 12.987 < 2e-16 ***
## xhat 1.0410 0.1093 9.525 < 2e-16 ***
## w1 0.8516 0.1190 7.156 1.61e-12 ***
## w21 -0.5281 0.1439 -3.669 0.000257 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 2.075 on 996 degrees of freedom
## Multiple R-squared: 0.4941, Adjusted R-squared: 0.4925
## F-statistic: 324.2 on 3 and 996 DF, p-value: < 2.2e-16
# Catatan: SE di tahap-2 OLS biasa tidak tepat untuk 2SLS.
# Solusi praktis: pakai ivreg (sudah menghitung SE 2SLS yang benar/robust).
IV fokus pada efek kausal rata-rata dari \(x\) terhadap \(y\) (di bawah asumsi). Prediksi out-of-sample bisa dilakukan, tetapi ingat bahwa IV menekankan identifikasi kausal, bukan akurasi prediksi semata.
cbind(
OLS = coef(ols_fit)["x"],
IV_2SLS = coef(iv_2sls)["x"]
)
## OLS IV_2SLS
## x 1.494595 1.040992
Bandingkan nilai koefisien
x: OLS vs IV. OLS bias menuju arah korelasi \(x\)–\(u\). IV mendekati nilai benar (1.2) karena variasi dari instrumen bebas dari \(u\).
AER::ivreg memudahkan
implementasi dan menyediakan diagnostics.Diperlukan saat variabel penjelas endogen (berkorelasi dengan error). Gunakan instrumen \(z\) yang relevan dan valid.
Model struktural:
\[ y_i = \beta_0 + \beta_1 x_i + \epsilon_i, \quad x_i \text{ endogen}. \]
n <- 500
u <- rnorm(n)
z <- rnorm(n) # instrumen
x <- 0.9*z + 0.5*u + rnorm(n) # endogen (terkait u)
y <- 2 + 1.5*x + u
iv_model <- AER::ivreg(y ~ x | z)
summary(iv_model, diagnostics = TRUE) # tampilkan diagnosa instrumen
##
## Call:
## AER::ivreg(formula = y ~ x | z)
##
## Residuals:
## Min 1Q Median 3Q Max
## -3.30779 -0.67530 0.04916 0.73319 2.60398
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 2.00524 0.04580 43.78 <2e-16 ***
## x 1.45045 0.05816 24.94 <2e-16 ***
##
## Diagnostic tests:
## df1 df2 statistic p-value
## Weak instruments 1 498 223.11 < 2e-16 ***
## Wu-Hausman 1 497 50.77 3.67e-12 ***
## Sargan 0 NA NA NA
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.022 on 498 degrees of freedom
## Multiple R-Squared: 0.8508, Adjusted R-squared: 0.8505
## Wald test: 622 on 1 and 498 DF, p-value: < 2.2e-16
Ringkas. IV mereduksi bias kausal saat ada endogenitas; cek kekuatan instrumen (uji F first-stage) dan validitas (overidentification bila (>)1 instrumen).
Variabel dummy (juga disebut indicator atau binary variables) adalah variabel bernilai 0/1 yang kita gunakan untuk memasukkan informasi kategorikal ke dalam model regresi linier. Dengan dummy, kita bisa memodelkan:
Tujuan dokumen ini: memberi panduan praktis—lengkap dengan
data simulasi, kode R, dan
visualisasi—untuk semua situasi umum pemodelan dengan
variabel dummy. Kita akan memakai sintaks lm() agar
interpretasi tetap jernih. Untuk visualisasi kita andalkan
ggplot2.
Kenapa perlu dummy? Karena dunia nyata jarang 100% kontinu. Kebijakan, jenis kelamin, perlakuan eksperimen, cabang toko, semester, provinsi—semuanya kategori. Tanpa dummy, model tidak bisa membedakan kelompok dan akan “meratakan” efeknya jadi satu garis saja. Dengan dummy, kita memberi hak suara kepada tiap kelompok.
Catatan gaya: kita menjaga nada ringkas dan lugas, sesekali menyelipkan humor hemat—secukupnya untuk membuat konsep lengket di kepala tanpa membuatnya lengket di keyboard.
Kita mulai dengan memuat paket dan membuat satu taman bermain
data yang kaya: ada satu kovariat kontinu (x),
beberapa faktor (misal kelompok 3 level), perlakuan biner
(treat), dan satu faktor tambahan (region 4
level). Sinyal dan noise diatur agar interpretasi koefisien bisa diuji
lewat grafik.
library(tidyverse)
library(broom)
library(modelr)
library(scales)
library(kableExtra)
set.seed(123)
n pengamatan per kelompok agar sampling cukup.x sebagai kovariat kontinu (misal suhu, usia,
pengeluaran iklan).kelompok berlevel "A", "B",
"C" untuk contoh multi-kategori.treat sebagai dummy 0/1 (misal sebelum/sesudah
kebijakan, kontrol/perlakuan).region empat level untuk contoh banyak dummy (dan
jebakan dummy variable trap).Kita membuat true model yang intersepnya berbeda per
kelompok, tetapi kemiringannya sama untuk
x.
n <- 300
dat_vi <- tibble(
kelompok = factor(rep(c("A","B","C"), each = n/3)),
x = rnorm(n, mean = 10, sd = 3)
) %>%
mutate(
# Intersep dasar
beta0 = 5,
# Tambahan intersep per kelompok
delta = case_when(
kelompok == "A" ~ 0,
kelompok == "B" ~ 3,
kelompok == "C" ~ -2
),
beta1 = 1.2, # kemiringan sama untuk semua kelompok
eps = rnorm(n, 0, 2),
y = beta0 + delta + beta1 * x + eps
)
dat_vi %>% head() %>% knitr::kable() %>% kable_styling(full_width = FALSE)
| kelompok | x | beta0 | delta | beta1 | eps | y |
|---|---|---|---|---|---|---|
| A | 8.318573 | 5 | 0 | 1.2 | -1.4304844 | 13.55180 |
| A | 9.309468 | 5 | 0 | 1.2 | -1.5053779 | 14.66598 |
| A | 14.676125 | 5 | 0 | 1.2 | -1.8770774 | 20.73427 |
| A | 10.211525 | 5 | 0 | 1.2 | -2.1050266 | 15.14880 |
| A | 10.387863 | 5 | 0 | 1.2 | -0.8743191 | 16.59112 |
| A | 15.145195 | 5 | 0 | 1.2 | 0.6623583 | 23.83659 |
Sekarang kemiringan terhadap x bergantung pada
kelompok. Intersep sama, tetapi slope berbeda.
n <- 300
dat_vs <- tibble(
kelompok = factor(rep(c("A","B","C"), each = n/3)),
x = rnorm(n, mean = 10, sd = 3)
) %>%
mutate(
beta0 = 8,
# Slope berbeda per kelompok
beta1 = case_when(
kelompok == "A" ~ 0.8,
kelompok == "B" ~ 1.5,
kelompok == "C" ~ 0.3
),
eps = rnorm(n, 0, 2),
y = beta0 + beta1 * x + eps
)
dat_vs %>% head() %>% knitr::kable() %>% kable_styling(full_width = FALSE)
| kelompok | x | beta0 | beta1 | eps | y |
|---|---|---|---|---|---|
| A | 13.222037 | 8 | 0.8 | -2.0282283 | 16.54940 |
| A | 9.917959 | 8 | 0.8 | -1.5826278 | 14.35174 |
| A | 9.900009 | 8 | 0.8 | 0.5991874 | 16.51919 |
| A | 5.451797 | 8 | 0.8 | 3.2781038 | 15.63954 |
| A | 12.371156 | 8 | 0.8 | 2.1692340 | 20.06616 |
| A | 9.367797 | 8 | 0.8 | -1.2491349 | 14.24510 |
Di sini ada treat (0/1) dan kelompok
(A/B/C). Efek treat tidak sama di setiap
kelompok—inilah interaksi dummy–dummy.
n <- 600
dat_dd <- tibble(
kelompok = factor(rep(c("A","B","C"), each = n/3)),
treat = rbinom(n, 1, 0.5),
x = rnorm(n, 10, 3) # ikutkan 1 kovariat kontinu agar realistis
) %>%
mutate(
beta0 = 6,
beta1 = 1.0, # slope x (sama)
# Efek treat baseline (pada kelompok A)
tau_A = 2.0,
# Modifikasi efek treat pada B & C
tau_mod = case_when(
kelompok == "A" ~ 0.0,
kelompok == "B" ~ 1.5, # treat lebih kuat di B
kelompok == "C" ~ -1.0 # treat lebih lemah di C
),
eps = rnorm(n, 0, 2),
y = beta0 + beta1 * x + (tau_A + tau_mod) * treat + eps
)
dat_dd %>% head() %>% knitr::kable() %>% kable_styling(full_width = FALSE)
| kelompok | treat | x | beta0 | beta1 | tau_A | tau_mod | eps | y |
|---|---|---|---|---|---|---|---|---|
| A | 1 | 7.537040 | 6 | 1 | 2 | 0 | -0.4002940 | 15.13675 |
| A | 1 | 9.078228 | 6 | 1 | 2 | 0 | 0.7756405 | 17.85387 |
| A | 0 | 7.293706 | 6 | 1 | 2 | 0 | 1.5878367 | 14.88154 |
| A | 1 | 11.881206 | 6 | 1 | 2 | 0 | -0.2810279 | 19.60018 |
| A | 1 | 13.361065 | 6 | 1 | 2 | 0 | 0.9116104 | 22.27268 |
| A | 0 | 16.381641 | 6 | 1 | 2 | 0 | -2.2911458 | 20.09049 |
Kita buat contoh dengan faktor region berlevel empat.
Kita akan lihat bagaimana R otomatis membuat k-1 dummy
dan kenapa jangan memasukkan semua dummy
plus intercept (itu menciptakan kolinearitas
sempurna).
n <- 400
dat_region <- tibble(
region = factor(sample(paste0("R",1:4), n, replace = TRUE)),
x = rnorm(n, 9, 2.5)
) %>%
mutate(
beta0 = 7,
# Intersep berbeda per region, baseline R1 = 0
delta = case_when(
region == "R1" ~ 0,
region == "R2" ~ 2.0,
region == "R3" ~ -1.5,
region == "R4" ~ 0.5
),
beta1 = 1.1,
eps = rnorm(n, 0, 1.8),
y = beta0 + delta + beta1 * x + eps
)
dat_region %>%
count(region) %>%
knitr::kable(col.names = c("Region","n")) %>%
kable_styling(full_width = FALSE)
| Region | n |
|---|---|
| R1 | 106 |
| R2 | 93 |
| R3 | 98 |
| R4 | 103 |
Model umum: \[
y_i = \beta_0 + \beta_1 x_i + \gamma_1 D_{B,i} + \gamma_2 D_{C,i} +
\varepsilon_i,
\] dengan baseline A (jadi tidak ada \(D_A\)). Koefisien \(\gamma_1\) dan \(\gamma_2\) adalah pergeseran
intersep relatif terhadap A. Slope \(\beta_1\) sama untuk semua kelompok.
m_vi <- lm(y ~ x + kelompok, data = dat_vi)
summary(m_vi)
##
## Call:
## lm(formula = y ~ x + kelompok, data = dat_vi)
##
## Residuals:
## Min 1Q Median 3Q Max
## -5.5530 -1.2101 0.0667 1.3058 5.2295
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 5.31350 0.46156 11.512 < 2e-16 ***
## x 1.16242 0.04059 28.637 < 2e-16 ***
## kelompokB 3.26183 0.28106 11.605 < 2e-16 ***
## kelompokC -2.00877 0.28005 -7.173 5.91e-12 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.98 on 296 degrees of freedom
## Multiple R-squared: 0.7845, Adjusted R-squared: 0.7823
## F-statistic: 359.1 on 3 and 296 DF, p-value: < 2.2e-16
(Intercept): intersep untuk baseline
(A).kelompokB: selisih intersep antara B dan
A.kelompokC: selisih intersep antara C dan
A.x: kemiringan yang sama untuk semua
kelompok.Bandingkan dengan nilai simulasi: kita set delta_B=+3
dan delta_C=-2—lihat apakah koefisien mendekati ini.
dat_vi %>%
ggplot(aes(x, y, color = kelompok)) +
geom_point(alpha = 0.35) +
geom_smooth(method = "lm", se = FALSE) +
labs(title = "Varying Intercept: Garis Regresi Paralel",
subtitle = "Intersep berbeda per kelompok, slope sama",
x = "x (kovariat kontinu)", y = "y (respon)") +
theme_minimal()
Gunakan modelr::add_predictions untuk menggambar garis
prediksi terpisah—tetap sejajar.
grid_vi <- dat_vi %>%
group_by(kelompok) %>%
summarize(x = seq(min(x), max(x), length.out = 50), .groups = "drop")
pred_vi <- grid_vi %>%
add_predictions(m_vi)
pred_vi %>% head() %>% knitr::kable() %>% kable_styling(full_width = FALSE)
| kelompok | x | pred |
|---|---|---|
| A | 3.072493 | 8.885044 |
| A | 3.347789 | 9.205055 |
| A | 3.623085 | 9.525066 |
| A | 3.898382 | 9.845076 |
| A | 4.173678 | 10.165087 |
| A | 4.448974 | 10.485098 |
pred_vi %>%
ggplot(aes(x, pred, color = kelompok)) +
geom_line(size = 1.2) +
labs(title = "Garis Prediksi per Kelompok (Intersep Berbeda)",
x = "x", y = "Prediksi y") +
theme_minimal()
Model umum: \[ y_i = \beta_0 + \beta_1 x_i + \gamma_1 D_{B,i} + \gamma_2 D_{C,i} + \delta_1 (x_i \cdot D_{B,i}) + \delta_2 (x_i \cdot D_{C,i}) + \varepsilon_i. \]
x:kelompokB mengukur selisih slope B
vs A.x:kelompokC mengukur selisih slope C
vs A.kelompok tanpa interaksi—dan memang kita sertakan (sehingga
model lengkap).m_vs <- lm(y ~ x * kelompok, data = dat_vs)
summary(m_vs)
##
## Call:
## lm(formula = y ~ x * kelompok, data = dat_vs)
##
## Residuals:
## Min 1Q Median 3Q Max
## -4.8955 -1.4285 -0.0354 1.0557 6.3866
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 8.09095 0.66795 12.113 < 2e-16 ***
## x 0.78646 0.06658 11.812 < 2e-16 ***
## kelompokB -0.88729 0.98845 -0.898 0.370
## kelompokC -0.03020 0.96732 -0.031 0.975
## x:kelompokB 0.81397 0.09501 8.567 6.09e-16 ***
## x:kelompokC -0.49610 0.09311 -5.328 1.98e-07 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 2.044 on 294 degrees of freedom
## Multiple R-squared: 0.9015, Adjusted R-squared: 0.8998
## F-statistic: 537.9 on 5 and 294 DF, p-value: < 2.2e-16
(Intercept): intersep baseline (A) saat
x=0. Jika x jauh dari nol, pertimbangkan
mean-centering pada x agar intersep lebih
bermakna.x: slope untuk baseline (A).kelompokB, kelompokC: perbedaan intersep
pada x=0 antara B/C dan A.x:kelompokB, x:kelompokC:
perbedaan slope B/C vs A.dat_vs %>%
ggplot(aes(x, y, color = kelompok)) +
geom_point(alpha = 0.35) +
geom_smooth(method = "lm", se = FALSE) +
labs(title = "Varying Slope: Garis dengan Kemiringan Berbeda",
subtitle = "Interaksi x * kelompok mengizinkan slope berbeda",
x = "x", y = "y") +
theme_minimal()
dat_vs_c <- dat_vs %>% mutate(x_c = x - mean(x))
m_vs_c <- lm(y ~ x_c * kelompok, data = dat_vs_c)
broom::tidy(m_vs_c) %>% knitr::kable(digits = 3) %>% kable_styling(full_width = FALSE)
| term | estimate | std.error | statistic | p.value |
|---|---|---|---|---|
| (Intercept) | 15.995 | 0.207 | 77.256 | 0 |
| x_c | 0.786 | 0.067 | 11.812 | 0 |
| kelompokB | 7.293 | 0.291 | 25.021 | 0 |
| kelompokC | -5.016 | 0.291 | -17.219 | 0 |
| x_c:kelompokB | 0.814 | 0.095 | 8.567 | 0 |
| x_c:kelompokC | -0.496 | 0.093 | -5.328 | 0 |
Dengan x_c, (Intercept) sekarang
merepresentasikan rata-rata y pada x
rata-rata untuk baseline. Koefisien interaksi tidak berubah
interpretasinya (tetap selisih slope).
Model: \[ y = \beta_0 + \beta_1 x + \tau \cdot \text{treat} + \gamma_B D_B + \gamma_C D_C + \eta_B (\text{treat}\cdot D_B) + \eta_C (\text{treat}\cdot D_C) + \varepsilon. \]
treat adalah dummy 0/1, baseline efeknya berlaku pada
kelompok A.treat:kelompokB adalah tambahan efek treat
khusus di B (di atas baseline A).treat:kelompokC tambahan efek treat
khusus di C.m_dd <- lm(y ~ x + treat * kelompok, data = dat_dd)
summary(m_dd)
##
## Call:
## lm(formula = y ~ x + treat * kelompok, data = dat_dd)
##
## Residuals:
## Min 1Q Median 3Q Max
## -5.5744 -1.2409 0.0103 1.3311 5.8518
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 5.54592 0.33554 16.528 < 2e-16 ***
## x 1.00886 0.02634 38.301 < 2e-16 ***
## treat 2.25936 0.27511 8.213 1.36e-15 ***
## kelompokB 0.55336 0.27145 2.039 0.04194 *
## kelompokC 0.36787 0.27502 1.338 0.18153
## treat:kelompokB 0.92275 0.38884 2.373 0.01796 *
## treat:kelompokC -1.28031 0.38872 -3.294 0.00105 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.943 on 593 degrees of freedom
## Multiple R-squared: 0.7419, Adjusted R-squared: 0.7392
## F-statistic: 284 on 6 and 593 DF, p-value: < 2.2e-16
library(dplyr)
library(broom)
library(knitr)
library(kableExtra)
coefs <- broom::tidy(m_dd) %>%
dplyr::select(term, estimate)
tau_A <- coefs %>% filter(term == "treat") %>% pull(estimate)
tau_B <- tau_A + (coefs %>% filter(term == "treat:kelompokB") %>% pull(estimate))
tau_C <- tau_A + (coefs %>% filter(term == "treat:kelompokC") %>% pull(estimate))
tibble(
Kelompok = c("A", "B", "C"),
Efek_Treat = c(tau_A, tau_B, tau_C)
) %>%
knitr::kable(digits = 3) %>%
kableExtra::kable_styling(full_width = FALSE)
| Kelompok | Efek_Treat |
|---|---|
| A | 2.259 |
| B | 3.182 |
| C | 0.979 |
dat_dd %>%
group_by(kelompok, treat) %>%
summarize(mean_y = mean(y), .groups = "drop") %>%
mutate(treat = factor(treat, levels = c(0,1), labels = c("Kontrol","Treat"))) %>%
ggplot(aes(treat, mean_y, group = kelompok, linetype = kelompok)) +
geom_point(size = 3) +
geom_line() +
labs(title = "Efek Treat Berbeda per Kelompok",
subtitle = "Garis menunjukkan pergeseran mean saat treat=1 vs 0",
x = "Status", y = "Rata-rata y") +
theme_minimal()
Jika region punya 4 level, R akan membangun otomatis 3
dummy (k-1) dengan baseline R1. Lihat
model.matrix() untuk memahami konstruksinya.
MM <- model.matrix(~ region, data = dat_region)
head(MM) %>% knitr::kable() %>% kable_styling(full_width = FALSE)
| (Intercept) | regionR2 | regionR3 | regionR4 |
|---|---|---|---|
| 1 | 1 | 0 | 0 |
| 1 | 0 | 0 | 1 |
| 1 | 0 | 0 | 0 |
| 1 | 0 | 0 | 0 |
| 1 | 0 | 0 | 0 |
| 1 | 0 | 0 | 0 |
m_region <- lm(y ~ x + region, data = dat_region)
summary(m_region)
##
## Call:
## lm(formula = y ~ x + region, data = dat_region)
##
## Residuals:
## Min 1Q Median 3Q Max
## -5.6656 -1.0873 0.1228 1.2255 4.4737
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 7.65097 0.35811 21.365 < 2e-16 ***
## x 1.06411 0.03446 30.879 < 2e-16 ***
## regionR2 1.46181 0.25310 5.776 1.55e-08 ***
## regionR3 -1.78943 0.24857 -7.199 3.08e-12 ***
## regionR4 0.16767 0.24540 0.683 0.495
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.773 on 395 degrees of freedom
## Multiple R-squared: 0.7264, Adjusted R-squared: 0.7236
## F-statistic: 262.2 on 4 and 395 DF, p-value: < 2.2e-16
Interpretasi: - (Intercept): intersep untuk
R1 (baseline). -
regionR2,regionR3,regionR4:
perbedaan intersep relatif R1. - x: slope yang
sama untuk semua region (karena belum ada interaksi dengan
region).
dat_region %>%
ggplot(aes(x, y, color = region)) +
geom_point(alpha = 0.35) +
geom_smooth(method = "lm", se = FALSE) +
labs(title = "Banyak Dummy (Region): Intersep Berbeda",
x = "x", y = "y") +
theme_minimal()
Jika dua faktor masing-masing memiliki \(k\) dan \(m\) level, interaksi akan menghasilkan \((k-1)\times(m-1)\) koefisien tambahan (di atas main effects), karena semua koefisien didefinisikan relatif terhadap baseline ganda.
Contoh: kelompok (A/B/C) × region (R1–R4)
memerlukan banyak koefisien. Gunakan ini hanya bila
perlu, atau ketika ada hipotesis teoritis yang meyakinkan
mengenai heterogenitas efek.
dat_big <- expand_grid(
kelompok = factor(c("A","B","C")),
region = factor(paste0("R",1:4))
) %>%
mutate(
# simulasi intersep unik per kombinasi (sekadar contoh)
mu = c(5,6,7,8, 4,6,6.5,7.5, 5.5,5,6.7,7.2)
)
# Gabungkan dengan x acak dan noise
set.seed(42)
dat_big <- dat_big %>%
group_by(kelompok, region) %>%
do({
tibble(
x = rnorm(80, 10, 2.5),
y = .$mu + 1.1 * x + rnorm(80,0,2)
)
}) %>% ungroup()
m_big <- lm(y ~ x + kelompok * region, data = dat_big)
broom::glance(m_big) %>% knitr::kable() %>% kable_styling(full_width = FALSE)
| r.squared | adj.r.squared | sigma | statistic | p.value | df | logLik | AIC | BIC | deviance | df.residual | nobs |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0.7223359 | 0.7188174 | 1.965931 | 205.2996 | 0 | 12 | -2004.564 | 4037.128 | 4105.265 | 3660.047 | 947 | 960 |
dat_big %>%
group_by(kelompok, region) %>%
summarize(mean_y = mean(y), .groups = "drop") %>%
ggplot(aes(region, mean_y, fill = kelompok)) +
geom_col(position = position_dodge(width = 0.7)) +
labs(title = "Rata-rata y per Kombinasi Kelompok × Region",
x = "Region", y = "Mean y") +
theme_minimal()
x memudahkan interpretasi intersep.sandwich::vcovHC + lmtest::coeftest untuk SE
robust.library(lmtest)
library(sandwich)
coeftest(m_vi, vcov = vcovHC(m_vi, type = "HC3"))
##
## t test of coefficients:
##
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 5.313504 0.475758 11.168 < 2.2e-16 ***
## x 1.162424 0.039543 29.396 < 2.2e-16 ***
## kelompokB 3.261833 0.293718 11.105 < 2.2e-16 ***
## kelompokC -2.008766 0.282171 -7.119 8.263e-12 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Intercept: mean untuk baseline saat \(x=0\).x: slope umum.D_B: selisih mean (intersep) B vs A.D_C: selisih mean (intersep) C vs A.treattreat +
treat:kelompokBtreat +
treat:kelompokCMisalkan treat=1 sesudah kebijakan. Interaksi
treat * kelompok memeriksa apakah dampak kebijakan berbeda
antar kelompok. Cocok untuk heterogeneous treatment effects
(HTE).
kelompok = kanal penjualan; x = anggaran
iklan. Interaksi x * kanal memberitahu kanal mana yang
lebih responsif terhadap iklan (slope lebih curam).
region sebagai provinsi; x = polutan;
kelompok = kebijakan emisi (ya/tidak). Interaksi menangkap
apakah hubungan polutan–outcome tergantung status kebijakan.
Regresi linier standar mensyaratkan asumsi residual “sehat”. Kita cek cepat pada salah satu model.
par(mfrow = c(1,2))
plot(m_vi, which = 1) # residual vs fitted
plot(m_vi, which = 2) # QQ plot
par(mfrow = c(1,1))
Variabel dummy itu seperti saklar: kecil, tapi mengubah aliran arus
interpretasi. Dengan intersep berbeda, kita akui tiap kelompok punya
“titik awal” berbeda. Dengan slope berbeda, kita izinkan kelompok
merespons perubahan x secara unik. Dengan interaksi, kita
menangkap dialektika antar variabel—kapan efek ini tergantung pada
yang itu.
Jika Anda mengajar, plot–plot di atas adalah alat retoris yang sangat kuat: tidak sekadar berkata “koefisien beda”, tetapi menunjukkan garis yang benar‑benar beda. Dan bila suatu saat ada yang bilang “dummy itu cuma 0/1”, Anda sudah tahu balasannya: betul—namun 0/1 yang bisa menceritakan 1001 cerita.
Data panel adalah dua dimensi kebahagiaan statistik: kita mengamati banyak unit (individu, perusahaan, provinsi) berulang kali pada banyak periode waktu. Keuntungan utama:
Dokumen ini menyajikan panduan komprehensif untuk tiga model klasik regresi panel:
Kita juga membahas perbedaan format data wide vs long, proses transformasi, pilihan model, uji-uji penting (LM Breusch–Pagan, F-test FE, Hausman), serta praktik terbaik (SE robust, klaster, diagnostik dependensi serial dan ketergantungan silang). Semua disertai simulasi data, kode R, dan visualisasi.
library(tidyverse)
library(plm) # jagoan panel
library(lmtest) # uji diagnostik
library(sandwich) # robust vcov
library(broom) # ringkasan tabel
library(knitr)
library(kableExtra)
library(scales)
set.seed(123)
Sebelum membicarakan model, pastikan format data jelas.
Format long memiliki kolom pengenal unit (mis.
id), pengenal waktu (mis. year), lalu
kolom-kolom variabel:
id | year | y | x1 | x2 | z ...
1 | 2018 | . | . | . | .
1 | 2019 | . | . | . | .
...
2 | 2018 | . | . | . | .
Sebagian besar fungsi panel (plm, fixest,
lfe) menyukai format ini.
Format wide menaruh setiap periode sebagai kolom berbeda:
id | y_2018 | y_2019 | y_2020 | x1_2018 | x1_2019 | ...
1 | . | . | . | . | . | ...
2 | . | . | . | . | . | ...
Format ini enak untuk melihat snapshot antar-periode sekaligus, tetapi perlu diubah ke long untuk dianalisis dengan mudah.
# Contoh mini untuk transformasi
wide_df <- tibble(
id = 1:3,
y_2018 = c(10, 12, 9),
y_2019 = c(11, 15, 10),
x_2018 = c(3, 4, 3.5),
x_2019 = c(3.3, 4.2, 3.7)
)
# Wide -> Long
long_df <- wide_df %>%
pivot_longer(cols = -id, names_to = "var", values_to = "value") %>%
separate(var, into = c("name", "year"), sep = "_") %>%
pivot_wider(names_from = name, values_from = value) %>%
mutate(year = as.integer(year)) %>%
arrange(id, year)
long_df %>% kable() %>% kable_styling(full_width = FALSE)
| id | year | y | x |
|---|---|---|---|
| 1 | 2018 | 10 | 3.0 |
| 1 | 2019 | 11 | 3.3 |
| 2 | 2018 | 12 | 4.0 |
| 2 | 2019 | 15 | 4.2 |
| 3 | 2018 | 9 | 3.5 |
| 3 | 2019 | 10 | 3.7 |
Ingat: long adalah format kerja utama untuk model panel.
Kita bangun data yang “masuk akal” untuk mendemonstrasikan pooled, FE, dan RE.
y (kinerja), x1 (belanja
R&D), x2 (leverage), z (shock makro
tahunan).alpha_i
(waktu-tetap) dan tren waktu via
lambda_t.library(dplyr)
library(tidyr)
library(kableExtra)
set.seed(123)
N <- 60 # unit
Tt <- 6 # time periods
years <- 2018:(2018 + Tt - 1)
panel <- expand.grid(id = 1:N, year = years) %>%
arrange(id, year)
# Komponen tak teramati: efek tetap unit dan efek tetap waktu
alpha_i <- rnorm(N, sd = 1.2) # heterogenitas per unit (tetap)
lambda_t <- seq(-0.5, 0.5, length.out = Tt) # tren waktu linear halus
# (Opsional) buat kamus tahun -> lambda
year_map <- tibble(
year = years,
lambda = lambda_t
)
# Kovariat observabel + gabungkan komponen
# Gunakan match / left_join agar setiap baris mendapatkan scalar 'alpha' dan 'lambda'
panel <- panel %>%
mutate(
x1 = rlnorm(n(), meanlog = log(5), sdlog = 0.3), # eksponensial-positif
x2 = rnorm(n(), mean = 0, sd = 1), # leverage ~ N(0,1)
z = rep(rnorm(Tt, 0, 0.5), each = N) # shock makro tahunan (sama untuk semua unit pada t)
) %>%
# alpha_i per unit (scalar per baris)
mutate(alpha = alpha_i[id]) %>%
# lambda_t per tahun (scalar per baris) — pilih salah satu:
# 1) via match:
mutate(lambda = lambda_t[match(year, years)])
# 2) atau pakai join (komentari baris match di atas, lalu pakai ini):
# left_join(year_map, by = "year")
# (setelah join, 'lambda' sudah ada sebagai kolom)
# True DGP (data generating process)
beta0 <- 1.5
beta1 <- 0.6 # x1
beta2 <- -0.4 # x2
beta3 <- 0.8 # z
# Error & outcome
panel <- panel %>%
mutate(
u = rnorm(n(), 0, 0.8),
y = beta0 + beta1*x1 + beta2*x2 + beta3*z + alpha + lambda + u
)
# Tampilkan cuplikan
panel %>%
head() %>%
knitr::kable() %>%
kable_styling(full_width = FALSE)
| id | year | x1 | x2 | z | alpha | lambda | u | y |
|---|---|---|---|---|---|---|---|---|
| 1 | 2018 | 5.603155 | 1.7795029 | 0.4067002 | -0.6725708 | -0.5 | 0.6379044 | 3.940785 |
| 1 | 2019 | 4.300541 | 0.2864244 | 0.4067002 | -0.6725708 | -0.3 | -1.5665641 | 1.751980 |
| 1 | 2020 | 4.524358 | 0.1263159 | 0.4067002 | -0.6725708 | -0.1 | -1.5090601 | 2.207818 |
| 1 | 2021 | 3.683507 | 1.2722668 | 0.4067002 | -0.6725708 | 0.1 | -0.5230239 | 2.430963 |
| 1 | 2022 | 3.625168 | -0.7184662 | 0.4067002 | -0.6725708 | 0.3 | 0.3155159 | 4.230792 |
| 1 | 2023 | 5.476666 | -0.4503386 | 0.4067002 | -0.6725708 | 0.5 | -0.7308528 | 4.388071 |
Secara desain, alpha_i berkorelasi
positif dengan x1 (karena firm yang
“bagus” cenderung belanja R&D lebih besar). Untuk memperjelas
endogenitas halus ini, kita tambahkan korelasi ringan:
# Memperkenalkan korelasi ringan antara alpha_i dan x1:
panel <- panel %>% group_by(id) %>%
mutate(x1 = x1 + 0.2*alpha[1]) %>%
ungroup()
# Rehitung y agar konsisten
panel <- panel %>% mutate(y = beta0 + beta1*x1 + beta2*x2 + beta3*z + alpha + lambda + u)
ggplot(panel, aes(year, y, group = id)) +
geom_line(alpha = 0.2) +
stat_summary(aes(group = 1), fun = mean, geom = "line", size = 1.2) +
labs(title = "Rata-Rata dan Trajektori Unit (Tipis)",
subtitle = "Garis tebal = rata-rata agregat tahunan",
x = "Tahun", y = "y") +
theme_minimal()
plm menyukai objek pdata.frame dengan
indeks (id, year).
pdata <- pdata.frame(panel, index = c("id","year"))
Anggap data panel cuma data cross-section besar. Abaikan efek tetap/acak.
Model: \[ y_{it} = \beta_0 + \beta_1 x1_{it} + \beta_2 x2_{it} + \beta_3 z_t + \varepsilon_{it} \]
pooled <- plm(y ~ x1 + x2 + z, data = pdata, model = "pooling")
summary(pooled)
## Pooling Model
##
## Call:
## plm(formula = y ~ x1 + x2 + z, data = pdata, model = "pooling")
##
## Balanced Panel: n = 60, T = 6, N = 360
##
## Residuals:
## Min. 1st Qu. Median 3rd Qu. Max.
## -3.312435 -0.958148 0.012376 0.901359 4.337458
##
## Coefficients:
## Estimate Std. Error t-value Pr(>|t|)
## (Intercept) 1.076380 0.244307 4.4058 1.396e-05 ***
## x1 0.702449 0.044590 15.7535 < 2.2e-16 ***
## x2 -0.364761 0.072186 -5.0531 6.963e-07 ***
## z 0.769942 0.164609 4.6774 4.132e-06 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Total Sum of Squares: 1223.3
## Residual Sum of Squares: 666.95
## R-Squared: 0.45481
## Adj. R-Squared: 0.45021
## F-statistic: 98.9934 on 3 and 356 DF, p-value: < 2.22e-16
Masalah potensial:
Solusi minimal: gunakan SE robust berklaster
(klaster pada id).
coeftest(pooled, vcov = vcovHC(pooled, type = "HC1", cluster = "group"))
##
## t test of coefficients:
##
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.076380 0.252806 4.2577 2.645e-05 ***
## x1 0.702449 0.043189 16.2647 < 2.2e-16 ***
## x2 -0.364761 0.062895 -5.7995 1.469e-08 ***
## z 0.769942 0.322614 2.3866 0.01753 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Dokumen ini menjelaskan hubungan panel data dengan endogeneity, serta mengapa model efek tetap (fixed effects, FE) kerap dianggap solusi—walau hanya parsial—untuk isu endogeneity. Pembahasan mencakup intuisi, formulasi matematis, equivalence dua pendekatan FE (within vs LSDV), keterbatasan FE, dan demonstrasi simulasi kecil di R.
Ringkasan Eksekutif
Apa itu Endogeneity? Secara umum, endogeneity adalah kondisi ketika variabel penjelas \(X\) berkorelasi dengan error term \(\varepsilon\) dalam model regresi. Sumber utama:
Konsekuensinya: estimasi OLS menjadi bias dan tidak konsisten.
Di mana Panel Data Membantu? Panel data menggabungkan dimensi individu (atau entitas) dan waktu. Dengan mengamati individu yang sama berulang kali, kita bisa mengontrol faktor tak teramati yang tidak berubah dalam waktu (misal: bakat manajerial, budaya organisasi, lokasi tetap). Inilah sumber endogeneity yang paling sering mengganggu analisis cross-section murni.
3. Model Fixed Effects: Formulasi dan Intuisi Misalkan model dasar: \[ Y_{it} = \alpha_i + \beta X_{it} + \varepsilon_{it}, \quad i=1,\dots,N;\; t=1,\dots,T. \]
Asumsi kunci FE: setelah mengondisikan pada \(\alpha_i\), tidak ada korelasi antara \(X_{it}\) dan \(\varepsilon_{it}\). Korelasi antara \(\alpha_i\) dan \(X_{it}\) diperbolehkan.
Within (Demeaning) Transformation Ambil rata-rata per individu: \[ \bar{Y}_i = \alpha_i + \beta \bar{X}_i + \bar{\varepsilon}_i. \]
Kurangkan dari persamaan asli: \[ (Y_{it} - \bar{Y}_{i}) = \beta (X_{it} - \bar{X}_{i}) + (\varepsilon_{it} - \bar{\varepsilon}_{i}) \]
Karena \(\alpha_i\) bersifat konstan untuk setiap individu \(i\) dan tidak berubah sepanjang waktu, maka ketika setiap variabel dikurangi dengan rata-ratanya (\(\bar{Y}_{i}, \bar{X}_{i}\)), komponen \(\alpha_i\) akan tereliminasi secara sempurna dari persamaan. Dengan demikian, variasi yang tersisa hanya mencerminkan perubahan dalam individu (within variation), bukan perbedaan antarindividu.
Inilah maksud frasa “FE menghilangkan \(\alpha_i\)”.
LSDV (Least-Squares Dummy Variables) Alternatif ekuivalen: masukkan dummy untuk tiap individu (kecuali satu untuk menghindari dummy trap): \[ Y_{it} = \beta X_{it} + \sum_{i=1}^{N-1} \alpha_i D_i + \varepsilon_{it}. \] Di sini, \(\alpha_i\) dimasukkan secara eksplisit. Secara teori, estimasi \(\beta\) dari LSDV ekuivalen dengan hasil within transformation.
Inti klarifikasi: FE mengakui \(\alpha_i\) (efek individu tetap), namun menghapusnya secara aljabar (via demeaning) saat menaksir \(\beta\).
Kapan FE Menyelesaikan Endogeneity? FE efektif untuk OVB yang time-invariant. Contoh: - “Budaya perusahaan” yang tak terukur tetapi relatif tetap selama periode studi. - Perbedaan lokasi geografis tetap. - Karakteristik bawaan individu (mis. kemampuan kognitif dasar).
Dengan FE, sumber bias ini tereliminasi dari persamaan sehingga \(\beta\) tidak lagi tercemar oleh korelasi \(\alpha_i\)–\(X_{it}\).
Kapan FE Tidak Cukup? FE tidak menyelesaikan: - Simultaneity (reverse causality): jika \(Y\) juga memengaruhi \(X\) pada waktu yang sama, maka \(X_{it}\) tetap berkorelasi dengan galat (meski \(\alpha_i\) sudah “dibersihkan”). - Time-varying omitted variables: ada faktor tak teramati berubah sepanjang waktu yang memengaruhi \(X\) dan \(Y\) sekaligus (mis. kebijakan baru). - Measurement error di \(X\): kesalahan ukur tetap menimbulkan bias.
Solusi pelengkap: Instrumental Variables (IV), Difference-in-Differences (DiD), dan Dynamic Panel GMM (mis. Arellano–Bond).
Demonstrasi Simulasi (R) Simulasi ini menunjukkan:
1) ketika \(\alpha_i\) berkorelasi dengan \(X_{it}\), OLS
pooled menjadi bias;
2) FE memulihkan estimasi \(\beta\) mendekati nilai sebenarnya.
set.seed(123)
library(dplyr)
library(tidyr)
library(ggplot2)
library(fixest) # untuk feols (FE) dan ols
library(broom)
N <- 200 # jumlah individu
Tt <- 6 # jumlah periode
beta_true <- 0.8
# Efek individu yang tak teramati (alpha_i), mis. "bakat" perusahaan
alpha_i <- rnorm(N, mean = 0, sd = 1)
# Variabel pengganggu yang mendorong korelasi antara X dan alpha (time-invariant)
z_i <- scale(alpha_i + rnorm(N, sd = 0.3))[,1]
# Bangun panel
dat <- expand.grid(i = 1:N, t = 1:Tt) %>% arrange(i, t)
# X dipengaruhi oleh z_i (yang berkorelasi dengan alpha_i), plus shock waktu
dat <- dat %>%
mutate(alpha = alpha_i[i],
z = z_i[i],
nu = rnorm(n(), sd = 0.6),
X = 0.7*z + 0.4*runif(n(), -1, 1) + nu)
# Hasil Y
dat <- dat %>%
mutate(epsilon = rnorm(n(), sd = 1),
Y = alpha + beta_true*X + epsilon)
head(dat)
## i t alpha z nu X epsilon Y
## 1 1 1 -0.5604756 0.09690107 -0.04413361 -0.06727743 2.2819670 1.6676694
## 2 1 2 -0.5604756 0.09690107 -0.70119085 -0.59945388 -0.4636830 -1.5037218
## 3 1 3 -0.5604756 0.09690107 -0.38084896 -0.11761574 -0.3263536 -0.9809218
## 4 1 4 -0.5604756 0.09690107 -0.01730493 -0.08280823 0.8824932 0.2557710
## 5 1 5 -0.5604756 0.09690107 0.40241758 0.33018052 1.2812861 0.9849549
## 6 1 6 -0.5604756 0.09690107 -0.99032793 -0.98350274 -0.6586819 -2.0059597
Estimasi OLS Pooled vs FE
# OLS pooled (abaikan struktur panel)
m_ols <- feols(Y ~ X, data = dat, notes = FALSE)
# FE: kontrol efek tetap individu i
m_fe <- feols(Y ~ X | i, data = dat, notes = FALSE)
# Ringkasan koefisien
tbl <- bind_rows(tidy(m_ols) %>% mutate(model = "Pooled OLS"),
tidy(m_fe) %>% mutate(model = "Fixed Effects (i)")) %>%
filter(term == "X") %>%
mutate(beta_true = beta_true)
tbl
## # A tibble: 2 × 7
## term estimate std.error statistic p.value model beta_true
## <chr> <dbl> <dbl> <dbl> <dbl> <chr> <dbl>
## 1 X 1.48 0.0374 39.7 1.39e-220 Pooled OLS 0.8
## 2 X 0.719 0.0488 14.7 1.62e- 44 Fixed Effects (i) 0.8
Visualisasi Bias
ggplot(tbl, aes(x = model, y = estimate)) +
geom_point(size = 3) +
geom_errorbar(aes(ymin = estimate - 1.96*std.error,
ymax = estimate + 1.96*std.error),
width = 0.1) +
geom_hline(yintercept = beta_true, linetype = "dashed") +
labs(title = "Perbandingan Estimasi Koefisien X",
subtitle = "Garis putus-putus = nilai sebenarnya (beta_true = 0.8)",
x = NULL, y = "Estimasi koefisien") +
theme_minimal(base_size = 12)
Dalam simulasi ini, Pooled OLS cenderung menyimpang dari \(\beta\) yang benar karena \(X\) berkorelasi dengan \(\alpha_i\). FE mengurangi bias tersebut dengan “menghapus” \(\alpha_i\) melalui transformasi within.
Within vs LSDV: Bukti Ekuivalensi Secara teori,
estimasi \(\hat\beta\) dari within estimator dan LSDV
identik. Perbedaan ada pada implementasi komputasional (efisiensi ketika
\(N\) besar). Paket modern (mis. fixest, lfe,
plm) mengestimasi FE tanpa benar-benar membuat ribuan
dummy, melainkan menggunakan algoritme matriks yang menerapkan
transformasi within secara efisien.
Keterbatasan Praktis dan Saran
Y ~ X | i + t.feols(..., iv=...)) atau dynamic panel
GMM.Kalimat Kunci
Lampiran: Contoh Sintaks Alternatif Beberapa
alternatif paket/sintaks yang umum: -
plm::plm(Y ~ X, data = dat, index = c("i","t"), model = "within")
- lfe::felm(Y ~ X | i) -
fixest::feols(Y ~ X | i + t) (tambahkan efek waktu
bersama)
Catatan: untuk IV-FE di
fixest, gunakan formula instrumen, contoh:
feols(Y ~ 1 | i, iv = ~ X ~ Z, data = dat)di manaZadalah instrumen yang valid.
Model Fixed Effect (FE) digunakan dalam data panel untuk mengontrol heterogenitas tak-teramati (unobserved heterogeneity) yang bersifat konstan dalam waktu tetapi berbeda antarunit (misalnya antar individu, perusahaan, atau daerah).
Model umum one-way fixed effect (unit-specific) adalah:
\[ (Y_{it} - \bar{Y}_{i}) = \beta (X_{it} - \bar{X}_{i}) + (\varepsilon_{it} - \bar{\varepsilon}_{i}) \]
dengan:
Tujuan utama model FE adalah menghilangkan korelasi antara \(\alpha_i\) dan variabel penjelas \(x1_{it}, x2_{it}\), sehingga estimasi koefisien \(\beta\) menjadi tidak bias.
Pendekatan LSDV (Least Squares Dummy Variable) memasukkan efek tetap \(\alpha_i\) secara eksplisit ke dalam model dengan menambahkan dummy variable untuk setiap unit individu.
Modelnya menjadi:
\[ y_{it} = \beta_1 x1_{it} + \beta_2 x2_{it} + \beta_3 z_t + \sum_{i=1}^{N-1} \alpha_i D_i + \varepsilon_{it} \]
dengan:
Secara praktis, model ini diestimasi menggunakan OLS, namun dengan sejumlah besar dummy.
Estimator OLS dapat ditulis sebagai:
\[ \hat{\beta}_{LSDV} = (X'D_M X)^{-1} X'D_M y \]
di mana \(D_M\) adalah matriks yang memasukkan dummy untuk setiap unit individu.
Kelebihan LSDV: - Interpretasi mudah (efek tetap tampak eksplisit dalam hasil regresi). - Cocok untuk data panel dengan \(N\) kecil dan \(T\) besar.
Kelemahan LSDV: - Tidak efisien bila \(N\) besar (karena menambah banyak dummy). - Tidak cocok untuk data panel besar (misalnya ribuan individu).
Contoh sintaks di R menggunakan fixest:
library(fixest)
model_lsdv <- feols(y ~ x1 + x2 + z + factor(id), data = data_panel)
summary(model_lsdv)
Pendekatan within transformation atau demeaning menghilangkan efek individu \(\alpha_i\) secara matematis tanpa menambahkan dummy.
Langkah-langkahnya:
(a) Ambil rata-rata per individu \[ \bar{y_i} = \frac{1}{T_i}\sum_{t=1}^{T_i} y_{it}, \quad \bar{x1_i} = \frac{1}{T_i}\sum_{t=1}^{T_i} x1_{it}, \quad \bar{x2_i} = \frac{1}{T_i}\sum_{t=1}^{T_i} x2_{it}, \quad \bar{z_i} = \frac{1}{T_i}\sum_{t=1}^{T_i} z_t \]
(b) Kurangkan setiap nilai aktual dengan rata-ratanya \[ (y_{it} - \bar{y_i}) = \beta_1 (x1_{it} - \bar{x1_i}) + \beta_2 (x2_{it} - \bar{x2_i}) + \beta_3 (z_t - \bar{z_i}) + (\varepsilon_{it} - \bar{\varepsilon_i}) \]
Karena \(\alpha_i\) konstan untuk setiap \(i\): \[ \alpha_i - \bar{\alpha_i} = 0 \]
maka efek individu terhapus dari persamaan.
Regresi dilakukan terhadap variabel yang sudah “dimean-kan”
(demeaned).
Estimator FE (within estimator) diberikan oleh:
\[ \hat{\beta}_{FE} = (X^{*\top} X^*)^{-1} X^{*\top} y^* \]
dengan:
Interpretasi:
Koefisien \(\hat{\beta}_{FE}\)
menggambarkan efek rata-rata dari perubahan \(x1_{it}\) atau \(x2_{it}\) dalam individu yang sama
dari waktu ke waktu, bukan antarindividu.
Perbandingan LSDV dan Within Transformation
| Aspek | LSDV | Within (Demeaning) |
|---|---|---|
| Efek individu \(\alpha_i\) | Dimodelkan eksplisit (dummy) | Dihilangkan secara matematis |
| Implementasi | Tambah dummy untuk setiap individu | Kurangkan rata-rata per individu |
| Efisiensi | Buruk jika N besar | Efisien untuk N besar |
| Interpretasi | Intersep spesifik terlihat | Intersep tidak terlihat langsung |
| Hasil \(\hat{\beta}\) | Identik | Identik |
| Variabel time-invariant | Bisa diestimasi | Tidak bisa diestimasi |
Contoh Implementasi di R
Contoh 1
Berikut contoh data simulasi sederhana dengan dua variabel independen \(x1, x2\), satu efek waktu \(z_t\), dan efek individu \(\alpha_i\).
set.seed(123)
library(dplyr)
library(plm)
# Simulasi data panel
N <- 100 # jumlah unit
Tt <- 5 # jumlah waktu
alpha_i <- rnorm(N, 0, 2) # efek tetap individu
beta <- c(0.5, -0.3, 0.2)
dat <- expand.grid(i = 1:N, t = 1:Tt) %>%
mutate(x1 = runif(N*Tt, 10, 20),
x2 = rnorm(N*Tt, 5, 1.5),
z = rnorm(Tt)[t],
u = rnorm(N*Tt, 0, 1),
alpha = alpha_i[i],
y = beta[1]*x1 + beta[2]*x2 + beta[3]*z + alpha + u)
# Konversi ke data panel
pdata <- pdata.frame(dat, index = c("i", "t"))
# Estimasi dengan LSDV
lsdv_model <- lm(y ~ x1 + x2 + z + factor(i), data = dat)
# Estimasi dengan within transformation (plm)
fe_model <- plm(y ~ x1 + x2 + z, data = pdata, model = "within")
summary(lsdv_model)$coefficients[1:5,]
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -1.5854085 0.52978028 -2.9925775 2.939124e-03
## x1 0.4984952 0.01652002 30.1752188 8.389282e-105
## x2 -0.3024865 0.03204598 -9.4391402 3.245824e-19
## z 0.1939942 0.03737305 5.1907505 3.350568e-07
## factor(i)2 0.4646629 0.61199709 0.7592568 4.481496e-01
summary(fe_model)
## Oneway (individual) effect Within Model
##
## Call:
## plm(formula = y ~ x1 + x2 + z, data = pdata, model = "within")
##
## Balanced Panel: n = 100, T = 5, N = 500
##
## Residuals:
## Min. 1st Qu. Median 3rd Qu. Max.
## -2.6693563 -0.5574777 0.0071086 0.5214703 2.6382967
##
## Coefficients:
## Estimate Std. Error t-value Pr(>|t|)
## x1 0.498495 0.016520 30.1752 < 2.2e-16 ***
## x2 -0.302486 0.032046 -9.4391 < 2.2e-16 ***
## z 0.193994 0.037373 5.1908 3.351e-07 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Total Sum of Squares: 1298.9
## Residual Sum of Squares: 369.63
## R-Squared: 0.71542
## Adj. R-Squared: 0.64231
## F-statistic: 332.686 on 3 and 397 DF, p-value: < 2.22e-16
Kedua metode menghasilkan estimasi \(\hat{\beta}\) yang identik, meskipun pendekatannya berbeda.
Interpretasi Hasil Jika \(\hat{\beta}_1 = 0.5\), artinya setiap kenaikan satu unit pada \(x1_{it}\) dalam individu yang sama (misalnya peningkatan investasi perusahaan dari tahun ke tahun) dikaitkan dengan kenaikan 0.5 unit pada \(y_{it}\), setelah mengendalikan efek tetap individu dan variabel lain.
Kesimpulan
Contoh 2
fe <- plm(y ~ x1 + x2 + z, data = pdata, model = "within", effect = "individual")
summary(fe)
## Oneway (individual) effect Within Model
##
## Call:
## plm(formula = y ~ x1 + x2 + z, data = pdata, effect = "individual",
## model = "within")
##
## Balanced Panel: n = 100, T = 5, N = 500
##
## Residuals:
## Min. 1st Qu. Median 3rd Qu. Max.
## -2.6693563 -0.5574777 0.0071086 0.5214703 2.6382967
##
## Coefficients:
## Estimate Std. Error t-value Pr(>|t|)
## x1 0.498495 0.016520 30.1752 < 2.2e-16 ***
## x2 -0.302486 0.032046 -9.4391 < 2.2e-16 ***
## z 0.193994 0.037373 5.1908 3.351e-07 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Total Sum of Squares: 1298.9
## Residual Sum of Squares: 369.63
## R-Squared: 0.71542
## Adj. R-Squared: 0.64231
## F-statistic: 332.686 on 3 and 397 DF, p-value: < 2.22e-16
coeftest(fe, vcov = vcovHC(fe, type = "HC1", cluster = "group"))
##
## t test of coefficients:
##
## Estimate Std. Error t value Pr(>|t|)
## x1 0.498495 0.015928 31.2968 < 2.2e-16 ***
## x2 -0.302486 0.028804 -10.5016 < 2.2e-16 ***
## z 0.193994 0.035221 5.5079 6.539e-08 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Dokumen ini merangkum spesifikasi Fixed Effects (FE) dua arah—unit dan waktu—beserta langkah estimasi dan implementasi R yang ringkas. Notasi galat menggunakan \(\varepsilon\) (bukan \(u\)).
Intuisi: selain heterogenitas tak-teramati antar-unit (\(\alpha_i\)), sering ada shock agregat setiap periode (\(\lambda_t\)). FE dua arah menyerap keduanya sehingga identifikasi koefisien slope \(\beta\) lebih bersih.
Model panel FE dua arah (unit + waktu): \[ Y_{it} \;=\; \alpha_i \;+\; \lambda_t \;+\; X_{it}^{\top}\beta \;+\; \varepsilon_{it}, \qquad i=1,\dots,N,\; t=1,\dots,T. \]
Komponen:
Normalisasi identifikasi (pilih salah satu) : \[ \sum_{i=1}^N \alpha_i = 0, \quad \sum_{t=1}^T \lambda_t = 0 \quad \text{atau} \quad \alpha_1 = 0, \; \lambda_1 = 0 . \]
Tiga pendekatan umum yang saling ekivalen untuk memperoleh \(\hat{\beta}\).
Definisikan rata-rata menurut unit, waktu, dan keseluruhan: \[ \bar{Y}_{i\cdot}=\frac{1}{T}\sum_t Y_{it},\quad \bar{Y}_{\cdot t}=\frac{1}{N}\sum_i Y_{it},\quad \bar{Y}=\frac{1}{NT}\sum_{i,t} Y_{it}. \]
Transformasi double-demeaning: \[ \tilde{Y}_{it} = Y_{it} - \bar{Y}_{i\cdot} - \bar{Y}_{\cdot t} + \bar{Y}, \qquad \tilde{X}_{it} = X_{it} - \bar{X}_{i\cdot} - \bar{X}_{\cdot t} + \bar{X}. \]
Setelah transformasi, efek tetap lenyap: \[ \tilde{Y}_{it} = \tilde{X}_{it}^{\top} \beta + \tilde{\varepsilon}_{it}. \]
Estimator OLS pada data bertanda “tilde”: \[ \hat{\beta}_{\text{FE,2W}} = (\tilde{X}^{\top}\tilde{X})^{-1} \tilde{X}^{\top}\tilde{y}. \]
Catatan proyeksi (opsional) : menggunakan operator pusat \(M_1\) (unit) dan \(M_2\) (waktu), double-demeaning setara dengan proyeksi Kronecker \(M_2 \otimes M_1\).
Regresi OLS dengan dummy untuk semua unit dan semua waktu (kecuali satu per kelompok untuk menghindari dummy trap) : \[ Y_{it} = \gamma + \sum_{i=2}^{N} \delta_i \, \mathbb{1}\{i\} + \sum_{t=2}^{T} \tau_t \, \mathbb{1}\{t\} + X_{it}^{\top}\beta + \varepsilon_{it}. \]
Koefisien \(\beta\) identik dengan within; \(\delta_i\) dan \(\tau_t\) merepresentasikan \(\alpha_i\) dan \(\lambda_t\) relatif kategori acuan.
Regress-out \(\alpha_i\) dan \(\lambda_t\) dari \(Y\) dan setiap kolom \(X\), lalu OLS pada residualnya. Hasil \(\hat{\beta}\) sama dengan (A) dan (B).
Panel jarang bersifat i.i.d. Gunakan SE yang tepat: - Cluster by unit: robust terhadap heteroskedastisitas dan autokorelasi dalam unit. - Driscoll–Kraay: robust terhadap dependensi lintas-seksi (cocok saat \(T\) sedang-besar). - Two-way cluster: unit dan waktu sekaligus bila tersedia.
Residual terestimasi: \[ \hat{\varepsilon}_{it} = Y_{it} - \hat{\alpha}_i - \hat{\lambda}_t - X_{it}^{\top}\hat{\beta}. \]
Berikut dua jalur populer: fixest (efisien, fleksibel)
dan plm (klasik untuk panel).
Dengan fixest
# install.packages("fixest") # jika belum terpasang
library(fixest)
# Contoh data: gunakan data 'Grunfeld' dari paket 'plm'
# install.packages("plm")
library(plm)
data("Grunfeld", package = "plm")
# Model FE dua arah: efek tetap perusahaan (firm) dan tahun (year)
# Cluster-robust SE by firm (bisa juga 'twoway' dengan fixest >= 0.11)
m_fe2_fixest <- feols(inv ~ value + capital | firm + year, data = Grunfeld, cluster = "firm")
summary(m_fe2_fixest)
## OLS estimation, Dep. Var.: inv
## Observations: 200
## Fixed-effects: firm: 10, year: 20
## Standard-errors: Clustered (firm)
## Estimate Std. Error t value Pr(>|t|)
## value 0.117716 0.010824 10.87502 1.7727e-06 ***
## capital 0.357916 0.047848 7.48021 3.7703e-05 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## RMSE: 47.5 Adj. R2: 0.943118
## Within R2: 0.720145
Beberapa opsi SE lain (jika tersedia versinya) :
# Driscoll-Kraay (melalui vcovDC dari fixest)
summary(m_fe2_fixest, vcov = "DK")
# Two-way cluster (unit & waktu) jika diperlukan/tersedia
summary(m_fe2_fixest, cluster = c("firm","year"))
Dengan plm
# install.packages("plm") # jika belum
library(plm)
# Spesifikasi FE dua arah (within transform)
m_fe2_plm <- plm(inv ~ value + capital, data = Grunfeld,
index = c("firm","year"),
model = "within", effect = "twoways")
summary(m_fe2_plm)
## Twoways effects Within Model
##
## Call:
## plm(formula = inv ~ value + capital, data = Grunfeld, effect = "twoways",
## model = "within", index = c("firm", "year"))
##
## Balanced Panel: n = 10, T = 20, N = 200
##
## Residuals:
## Min. 1st Qu. Median 3rd Qu. Max.
## -162.6094 -19.4710 -1.2669 19.1277 211.8420
##
## Coefficients:
## Estimate Std. Error t-value Pr(>|t|)
## value 0.117716 0.013751 8.5604 6.653e-15 ***
## capital 0.357916 0.022719 15.7540 < 2.2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Total Sum of Squares: 1615600
## Residual Sum of Squares: 452150
## R-Squared: 0.72015
## Adj. R-Squared: 0.67047
## F-statistic: 217.442 on 2 and 169 DF, p-value: < 2.22e-16
Standar error robust dengan plm:
# install.packages("sandwich"); install.packages("lmtest")
library(sandwich); library(lmtest)
# Cluster by firm
coeftest(m_fe2_plm, vcov = vcovHC(m_fe2_plm, method = "arellano", type = "HC1", cluster = "group"))
##
## t test of coefficients:
##
## Estimate Std. Error t value Pr(>|t|)
## value 0.117716 0.009761 12.0599 < 2.2e-16 ***
## capital 0.357916 0.043147 8.2952 3.277e-14 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# Driscoll-Kraay
coeftest(m_fe2_plm, vcov = vcovSCC(m_fe2_plm, type = "HC1", maxlag = 2)) # maxlag pilih sesuai kebutuhan
##
## t test of coefficients:
##
## Estimate Std. Error t value Pr(>|t|)
## value 0.117716 0.020539 5.7313 4.481e-08 ***
## capital 0.357916 0.056092 6.3809 1.629e-09 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Spesifikasi model: \[ Y_{it} = \alpha_i + \lambda_t + X_{it}^{\top}\beta + \varepsilon_{it}. \]
Transformasi double-demeaning: \[ \tilde{Y}_{it} = Y_{it}-\bar{Y}_{i\cdot}-\bar{Y}_{\cdot t}+\bar{Y},\quad \tilde{X}_{it} = X_{it}-\bar{X}_{i\cdot}-\bar{X}_{\cdot t}+\bar{X}. \]
Estimator within dua arah: \[ \hat{\beta}_{\text{FE,2W}} = (\tilde{X}^{\top}\tilde{X})^{-1}\tilde{X}^{\top}\tilde{y}. \]
Pulihkan \(\hat{\alpha}_i\) dan \(\hat{\lambda}_t\) sesuai normalisasi, lalu evaluasi residual \(\hat{\varepsilon}_{it}\).
Laporkan SE yang sesuai (clustered/Driscoll–Kraay/two-way cluster) dan uji relevan.
FWL menyatakan: jika Anda meregresikan \(Y\) dan setiap kolom \(X\) pada semua dummy unit dan waktu, lalu mengambil residualnya (\(\tilde{y}, \tilde{X}\)), maka OLS pada \(\tilde{y} \sim \tilde{X}\) menghasilkan \(\hat{\beta}\) yang sama dengan LSDV dan within.
Secara aljabar, dengan matriks proyeksi \(M\) yang menyingkirkan ruang yang direntang dummy unit dan waktu: \[ \hat{\beta} = (X^{\top} M X)^{-1} X^{\top} M y. \]
fe_tw <- plm(y ~ x1 + x2 + z, data = pdata, model = "within", effect = "twoways")
summary(fe_tw)
## Twoways effects Within Model
##
## Call:
## plm(formula = y ~ x1 + x2 + z, data = pdata, effect = "twoways",
## model = "within")
##
## Balanced Panel: n = 100, T = 5, N = 500
##
## Residuals:
## Min. 1st Qu. Median 3rd Qu. Max.
## -2.6024620 -0.5481059 -0.0048051 0.5006134 2.7048882
##
## Coefficients:
## Estimate Std. Error t-value Pr(>|t|)
## x1 0.499424 0.016551 30.1748 < 2.2e-16 ***
## x2 -0.300839 0.032236 -9.3325 < 2.2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Total Sum of Squares: 1274
## Residual Sum of Squares: 367.55
## R-Squared: 0.71149
## Adj. R-Squared: 0.63461
## F-statistic: 485.828 on 2 and 394 DF, p-value: < 2.22e-16
coeftest(fe_tw, vcov = vcovHC(fe_tw, type = "HC1", cluster = "group"))
##
## t test of coefficients:
##
## Estimate Std. Error t value Pr(>|t|)
## x1 0.499424 0.015766 31.677 < 2.2e-16 ***
## x2 -0.300839 0.029185 -10.308 < 2.2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Alternatif manual:
lm(y ~ x1 + x2 + z + factor(id) + factor(year), data = panel), tapi FE viaplmlebih efisien.
# One-way FE vs pooled
pFtest(fe, pooled)
##
## F test for individual effects
##
## data: y ~ x1 + x2 + z
## F = -7.7887, df1 = -41, df2 = 397, p-value = NA
## alternative hypothesis: significant effects
# Two-ways FE vs pooled (tak langsung, bisa bandingkan dengan model factor manual)
lm_pooled <- lm(y ~ x1 + x2 + z, data = panel)
lm_twoway <- lm(y ~ x1 + x2 + z + factor(id) + factor(year), data = panel)
anova(lm_pooled, lm_twoway) # indikasi kasar
## Analysis of Variance Table
##
## Model 1: y ~ x1 + x2 + z
## Model 2: y ~ x1 + x2 + z + factor(id) + factor(year)
## Res.Df RSS Df Sum of Sq F Pr(>F)
## 1 356 666.95
## 2 293 211.05 63 455.9 10.046 < 2.2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
RE memandang heterogenitas unit sebagai komponen acak: \(\alpha_i \sim \mathcal{N}(0,\sigma_{\alpha}^2)\) dan tidak berkorelasi dengan kovariat \(x_{it}\).
Model: \[ y_{it} = \beta_0 + \beta_1 x1_{it} + \beta_2 x2_{it} + \beta_3 z_t + \alpha_i + u_{it} \] dengan asumsi \(\text{Cov}(\alpha_i, x_{it}) = 0\).
re <- plm(y ~ x1 + x2 + z, data = pdata, model = "random", random.method = "swar")
summary(re)
## Oneway (individual) effect Random Effect Model
## (Swamy-Arora's transformation)
##
## Call:
## plm(formula = y ~ x1 + x2 + z, data = pdata, model = "random",
## random.method = "swar")
##
## Balanced Panel: n = 100, T = 5, N = 500
##
## Effects:
## var std.dev share
## idiosyncratic 0.9311 0.9649 0.214
## individual 3.4204 1.8494 0.786
## theta: 0.7728
##
## Residuals:
## Min. 1st Qu. Median 3rd Qu. Max.
## -2.593311 -0.615791 -0.027389 0.660892 3.189176
##
## Coefficients:
## Estimate Std. Error z-value Pr(>|z|)
## (Intercept) 0.253760 0.343940 0.7378 0.4606
## x1 0.497239 0.016392 30.3343 < 2.2e-16 ***
## x2 -0.303237 0.031803 -9.5350 < 2.2e-16 ***
## z 0.193897 0.037318 5.1958 2.038e-07 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Total Sum of Squares: 1397.6
## Residual Sum of Squares: 460.45
## R-Squared: 0.67055
## Adj. R-Squared: 0.66855
## Chisq: 1009.52 on 3 DF, p-value: < 2.22e-16
coeftest(re, vcov = vcovHC(re, type = "HC1", cluster = "group"))
##
## t test of coefficients:
##
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 0.253760 0.310321 0.8177 0.4139
## x1 0.497239 0.015743 31.5856 < 2.2e-16 ***
## x2 -0.303237 0.029119 -10.4137 < 2.2e-16 ***
## z 0.193897 0.035276 5.4966 6.203e-08 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Uji apakah varians komponen acak \(\sigma_\alpha^2\) non-zero (artinya RE beralasan).
plmtest(pooled, type = "bp")
##
## Lagrange Multiplier Test - (Breusch-Pagan)
##
## data: y ~ x1 + x2 + z
## chisq = 249.51, df = 1, p-value < 2.2e-16
## alternative hypothesis: significant effects
Kunci keputusan: bila \(\alpha_i\) berkorelasi dengan \(x_{it}\), FE konsisten tetapi RE tidak. Gunakan uji Hausman.
phtest(fe, re)
##
## Hausman Test
##
## data: y ~ x1 + x2 + z
## chisq = 0.43892, df = 3, p-value = 0.9321
## alternative hypothesis: one model is inconsistent
Dalam simulasi kita,
x1sengaja dibuat agak berkorelasi denganalpha_i; ekspektasi: Hausman signifikan → pilih FE.
Model FE standar mengasumsikan slope sama untuk semua unit. Jika Anda
ingin slope berbeda (mis. x1 berefek lebih kuat di sektor
A), gunakan interaksi x1 * sector dan tetap sertakan efek
tetap unit/waktu bila relevan.
# Misal kita definisikan 3 sektor berdasarkan id
panel <- panel %>% mutate(sector = factor(ifelse(id <= 20, "A", ifelse(id <= 40, "B", "C"))))
pdata <- pdata.frame(panel, index = c("id","year"))
fe_int <- plm(y ~ x1*sector + x2 + z, data = pdata, model = "within", effect = "twoways")
summary(fe_int)
## Twoways effects Within Model
##
## Call:
## plm(formula = y ~ x1 * sector + x2 + z, data = pdata, effect = "twoways",
## model = "within")
##
## Balanced Panel: n = 60, T = 6, N = 360
##
## Residuals:
## Min. 1st Qu. Median 3rd Qu. Max.
## -2.244464 -0.527023 -0.027684 0.461679 2.103522
##
## Coefficients:
## Estimate Std. Error t-value Pr(>|t|)
## x1 0.619239 0.050011 12.3821 < 2.2e-16 ***
## x2 -0.368322 0.049216 -7.4837 8.622e-13 ***
## x1:sectorB -0.032285 0.072973 -0.4424 0.6585
## x1:sectorC -0.034998 0.074177 -0.4718 0.6374
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Total Sum of Squares: 553.52
## Residual Sum of Squares: 210.84
## R-Squared: 0.61909
## Adj. R-Squared: 0.53008
## F-statistic: 118.239 on 4 and 291 DF, p-value: < 2.22e-16
coeftest(fe_int, vcov = vcovHC(fe_int, type = "HC1", cluster = "group"))
##
## t test of coefficients:
##
## Estimate Std. Error t value Pr(>|t|)
## x1 0.619239 0.049172 12.5934 < 2.2e-16 ***
## x2 -0.368322 0.044998 -8.1852 8.592e-15 ***
## x1:sectorB -0.032285 0.065465 -0.4932 0.6223
## x1:sectorC -0.034998 0.063755 -0.5489 0.5835
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Hati-hati ledakan parameter kalau level faktor banyak. Gunakan interaksi jika ada hipotesis teoritis yang jelas.
Pisahkan variasi within (dalam unit) dan between
(antar unit) untuk x1.
wb <- panel %>%
group_by(id) %>%
mutate(x1_between = mean(x1), x1_within = x1 - x1_between) %>%
ungroup()
# Hubungan antar-unit (between): rata-rata y vs rata-rata x1 per unit
between_df <- wb %>% group_by(id) %>% summarize(y_m = mean(y), x1_m = mean(x1))
p1 <- ggplot(between_df, aes(x1_m, y_m)) +
geom_point() + geom_smooth(method = "lm", se = FALSE) +
labs(title = "Between Variation", x = "mean(x1_i)", y = "mean(y_i)") +
theme_minimal()
# Hubungan dalam-unit (within): deviasi dari mean unit
p2 <- ggplot(wb, aes(x1_within, y - ave(y, id, FUN=mean), color = factor(id))) +
geom_point(alpha = 0.2) +
geom_smooth(method = "lm", se = FALSE) +
guides(color = "none") +
labs(title = "Within Variation",
x = "x1 - mean(x1_i)", y = "y - mean(y_i)") +
theme_minimal()
p1; p2
Panel sering menghadapi heteroskedastisitas, autokorelasi, dan ketergantungan silang.
Gunakan SE robust berklaster pada id
(sudah dicontohkan). Bisa juga Driscoll–Kraay (ketahanan
terhadap dependensi silang) pada T moderat-besar.
# SE Driscoll-Kraay (butuh T cukup besar biasanya)
coeftest(fe_tw, vcov = vcovSCC(fe_tw, type = "HC1", maxlag = 2))
##
## t test of coefficients:
##
## Estimate Std. Error t value Pr(>|t|)
## x1 0.4994243 0.0128222 38.950 < 2.2e-16 ***
## x2 -0.3008392 0.0087336 -34.446 < 2.2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
pbgtest(fe) # uji BG untuk model FE one-way
##
## Breusch-Godfrey/Wooldridge test for serial correlation in panel models
##
## data: y ~ x1 + x2 + z
## chisq = 91.826, df = 5, p-value < 2.2e-16
## alternative hypothesis: serial correlation in idiosyncratic errors
pcdtest(fe_tw, test = "cd") # Pesaran CD test
##
## Pesaran CD test for cross-sectional dependence in panels
##
## data: y ~ x1 + x2 + z
## z = -1.1274, p-value = 0.2596
## alternative hypothesis: cross-sectional dependence
Jika ada ketergantungan silang kuat (mis. shock nasional), pertimbangkan model dua-arah FE (sudah), faktor tak-teramati bersama (CMG), atau standar error Driscoll–Kraay.
x1 log-normal) atau gunakan log
jika interpretasi elastisitas relevan.plm; dokumentasikan alasan
kehilangan observasi.id, year) dan pastikan merge/join
variabel waktu-spesifik benar (hindari look-ahead bias).Untuk memperjelas ekivalensi FE ⇔ dummy penuh:
lm_manual <- lm(y ~ x1 + x2 + z + factor(id), data = panel) # one-way FE manual
summary(lm_manual)$coef[1:8,] %>% kable(digits=3) %>% kable_styling(full_width = FALSE)
| Estimate | Std. Error | t value | Pr(>|t|) | |
|---|---|---|---|---|
| (Intercept) | 1.069 | 0.308 | 3.468 | 0.001 |
| x1 | 0.596 | 0.032 | 18.355 | 0.000 |
| x2 | -0.391 | 0.053 | -7.418 | 0.000 |
| z | -1.141 | 0.758 | -1.506 | 0.133 |
| factor(id)2 | 1.204 | 0.534 | 2.254 | 0.025 |
| factor(id)3 | 3.457 | 0.534 | 6.473 | 0.000 |
| factor(id)4 | 1.678 | 0.534 | 3.140 | 0.002 |
| factor(id)5 | 1.024 | 0.536 | 1.908 | 0.057 |
Koefisien
factor(id)adalah perbedaan intersep unit relatif baseline. Within estimator akan memberikan \(\hat{\beta}\) yang sama untuk kovariat.
factor(year) atau
effect="twoways" (sudah dicontohkan).y_{it-1}
→ butuh Arellano–Bond/GMM (plm::pgmm).library(plm)
# pastikan pdata sudah: pdata <- pdata.frame(panel, index = c("id","year"))
# 1) Hanya y_lag sebagai regressor dinamis; instrumen GMM = lag(y, 2) saja
dyn_min <- pgmm(
formula = y ~ lag(y, 1) | lag(y, 2),
data = pdata,
effect = "individual",
model = "onestep", # 1-step lebih stabil saat matriks hampir singular
transformation = "d", # first-difference Arellano-Bond
collapse = TRUE # kurangi proliferasi instrumen
)
summary(dyn_min, robust = TRUE) # ringkasan + vcov robust
## Oneway (individual) effect One-step model Difference GMM
##
## Call:
## pgmm(formula = y ~ lag(y, 1) | lag(y, 2), data = pdata, effect = "individual",
## model = "onestep", collapse = TRUE, transformation = "d")
##
## Balanced Panel: n = 60, T = 6, N = 360
##
## Number of Observations Used: 240
## Residuals:
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## -5.3838 -0.7915 0.4694 0.4386 1.8238 5.7426
##
## Coefficients:
## Estimate Std. Error z-value Pr(>|z|)
## lag(y, 1) -0.99908 0.53013 -1.8846 0.05949 .
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Sargan test: chisq(0) = 2.179288e-31 (p-value = < 2.22e-16)
## Autocorrelation test (1): normal = 0.1212099 (p-value = 0.90352)
## Autocorrelation test (2): normal = -2.030757 (p-value = 0.04228)
## Wald test for coefficients: chisq(1) = 3.551666 (p-value = 0.059486)
mtest(dyn_min, order = 1) # AR(1) harus signifikan
##
## Arellano-Bond autocorrelation test of degree 1
##
## data: y ~ lag(y, 1) | lag(y, 2)
## normal = 0.3248, p-value = 0.7453
## alternative hypothesis: autocorrelation present
mtest(dyn_min, order = 2) # AR(2) harus TIDAK signifikan
##
## Arellano-Bond autocorrelation test of degree 2
##
## data: y ~ lag(y, 1) | lag(y, 2)
## normal = NaN, p-value = NA
## alternative hypothesis: autocorrelation present
sargan(dyn_min) # Hansen/Sargan idealnya tidak signifikan
##
## Sargan test
##
## data: y ~ lag(y, 1) | lag(y, 2)
## chisq = 2.1793e-31, df = 0, p-value < 2.2e-16
## alternative hypothesis: overidentifying restrictions not valid
Dokumen ini mestinya menjadi toolbox ajar/riset. Anda bisa langsung knit untuk kelas, menyalin tabel ke slide, dan menyesuaikan simulasi ke konteks lokal (misalnya kabupaten–kota dan tahun kebijakan). Jika analisis makin “spicy”, jangan ragu menambahkan two-way FE dan tes Driscoll–Kraay—bumbu pedas yang aman untuk inferensi.
Jawablah dua soal aplikasi berikut secara singkat dan jelas. Gunakan notasi matematis atau interpretasi ekonometrika bila diperlukan.
Seorang peneliti ingin menganalisis pengaruh jam belajar terhadap nilai ujian mahasiswa. Ia mengumpulkan data dari 100 mahasiswa dan memperkirakan model berikut:
\[ \text{Score}_i = \beta_0 + \beta_1 \text{StudyHours}_i + \varepsilon_i \]
Hasil estimasi menunjukkan bahwa \(\hat{\beta}_1 = 2.5\) dan signifikan pada tingkat 5%.
Jelaskan interpretasi dari koefisien \(\hat{\beta}_1 = 2.5\) dalam konteks penelitian ini. (25 poin)
Jika variabel kemampuan awal mahasiswa tidak dimasukkan ke dalam model, apa potensi masalah yang muncul? Jelaskan secara singkat. (25 poin)
Seorang ekonom ingin meneliti pengaruh belanja pemerintah daerah terhadap tingkat kemiskinan di 10 provinsi selama 5 tahun. Model yang digunakan:
\[ \text{Poverty}_{it} = \alpha_i + \beta_1 \text{GovExp}_{it} + \varepsilon_{it} \]
Hasil model Fixed Effects menunjukkan bahwa \(\hat{\beta}_1 = -0.8\) dan signifikan pada tingkat 1%.
Jelaskan makna dari nilai \(\hat{\beta}_1 = -0.8\) dalam konteks penelitian ini. (25 poin)
Mengapa model Fixed Effects lebih sesuai dibanding pooled OLS dalam kasus ini? Berikan alasan singkat. (25 poin)
Kuis dikumpulan pada link gform berikut (10 November 2025 Pukul 16:00 WIB): https://forms.gle/QEwdZz6smK9eW3Jb8
Model Difference-in-Differences (DiD) adalah teknik ekonometrika untuk mengukur efek kausal dari suatu kebijakan, intervensi, atau peristiwa terhadap variabel hasil (outcome), dengan memanfaatkan data sebelum vs sesudah pada kelompok perlakuan (treatment) dan kelompok kontrol (control).
Di dokumen ini, kita membedah DiD dari intuisi → formulasi → asumsi → implementasi R (termasuk event study sederhana), plus diagnostics dan uji placebo ringkas.
Bayangkan pemerintah memberi pelatihan keterampilan kepada sebagian provinsi (treatment), sementara provinsi lain tidak (control). Kita ingin tahu: apakah program ini meningkatkan pendapatan rata-rata?
Masalah: pendapatan bisa naik karena banyak hal (pertumbuhan ekonomi umum, inflasi, tren waktu). Jika kita hanya sebelum–sesudah pada treatment, efek bisa terkontaminasi tren umum.
Trik DiD: bandingkan perubahan di treatment dengan perubahan di kontrol:
\[ \text{Efek kebijakan} = \big(\Delta Y_{\text{treat}}\big) - \big(\Delta Y_{\text{control}}\big) \]
Kelompok kontrol bertindak sebagai counterfactual—dunia alternatif tanpa kebijakan.
Misalkan untuk unit \(i\) dan waktu \(t\):
Model two-by-two dapat dituliskan sebagai:
\[ Y_{it} = \alpha + \beta D_i + \gamma Post_t + \delta (D_i \times Post_t) + \varepsilon_{it}. \]
Koefisien \(\delta\) merepresentasikan efek kausal kebijakan, yaitu selisih perubahan rata-rata outcome antara kelompok treatment dan control:
\[ \text{Efek kebijakan} = \big(\Delta Y_{\text{treat}}\big) - \big(\Delta Y_{\text{control}}\big) \]
Interpretasi
| Sebelum (0) | Sesudah (1) | Perubahan | |
|---|---|---|---|
| Kontrol (0) | \(\bar{Y}_{C,0}\) | \(\bar{Y}_{C,1}\) | \(\bar{Y}_{C,1} - \bar{Y}_{C,0}\) |
| Treatment (1) | \(\bar{Y}_{T,0}\) | \(\bar{Y}_{T,1}\) | \(\bar{Y}_{T,1} - \bar{Y}_{T,0}\) |
Efek DiD:
\[ \text{DiD} = (\bar{Y}_{T,1} - \bar{Y}_{T,0}) - (\bar{Y}_{C,1} - \bar{Y}_{C,0}) \]
Tanpa kebijakan, tren outcome di treatment dan kontrol sejajar (paralel):
\[ E[Y_{T,1} - Y_{T,0} \mid \text{no treatment}] = E[Y_{C,1} - Y_{C,0}] \]
Bila tren dasar tidak paralel, \(\hat{\delta}\) berpotensi
bias.
Karenanya, visualisasi dan uji lead (placebo) berguna sebagai
diagnostics.
Untuk data panel (banyak unit × banyak waktu), lazim digunakan Two-Way Fixed Effects (TWFE):
\[ Y_{it} = \alpha_i + \lambda_t + \delta (D_i \times Post_t) + \varepsilon_{it} \]
dengan:
Kita mulai dari skenario klasik 2×2: satu gelombang kebijakan, dua periode (0=sebelum, 1=sesudah), dua grup (kontrol vs treatment).
pkgs <- c("tidyverse", "fixest", "sandwich", "lmtest", "broom")
need <- pkgs[!pkgs %in% rownames(installed.packages())]
if (length(need)) install.packages(need, repos = "https://cloud.r-project.org")
invisible(lapply(pkgs, library, character.only = TRUE))
set.seed(123)
N <- 200 # total unit
Tt <- 2 # dua periode: 0=pre, 1=post
id <- rep(1:N, each = Tt)
t <- rep(0:1, times = N)
# separuh jadi treatment:
D_i <- rep(rbinom(N, 1, 0.5), each = Tt)
Post_t <- t
# Komponen efek tetap & tren umum
alpha_i <- rnorm(N, 0, 1) # unit heterogeneity (random intercept) - akan di-absorb oleh FE
alpha_i <- rep(alpha_i, each = Tt)
time_shock <- ifelse(t==1, 1.0, 0.0) # ada kenaikan umum di periode post
# Efek perlakuan sebenarnya (ground truth)
delta_true <- 2.0
# Outcome dihasilkan dengan struktur DiD + noise
y <- 1.0 + 0.3*D_i + 0.8*Post_t + delta_true*(D_i*Post_t) + alpha_i + time_shock + rnorm(N*Tt, 0, 1)
did_2x2 <- tibble(id, t, D_i, Post_t, y) %>%
mutate(group = ifelse(D_i==1, "Treatment", "Control"),
period = ifelse(Post_t==1, "Post", "Pre"))
did_2x2 %>% head()
## # A tibble: 6 × 7
## id t D_i Post_t y group period
## <int> <int> <int> <int> <dbl> <chr> <chr>
## 1 1 0 0 0 -0.426 Control Pre
## 2 1 1 0 1 1.34 Control Post
## 3 2 0 1 0 0.618 Treatment Pre
## 4 2 1 1 1 4.30 Treatment Post
## 5 3 0 0 0 0.316 Control Pre
## 6 3 1 0 1 2.88 Control Post
avg <- did_2x2 %>%
group_by(group, period) %>%
summarise(mean_y = mean(y), .groups = "drop")
ggplot(avg, aes(x = period, y = mean_y, group = group)) +
geom_line(size = 1.1) + geom_point(size = 2) +
labs(x = NULL, y = "Rata-rata Y", color = NULL) +
theme_minimal(base_size = 12)
Rata-rata outcome per grup dan periode.
Intepretasi
Sumbu dan Makna Garis
Apa yang Terlihat dari Grafik
Kedua garis naik dari Pre ke Post, tetapi garis treatment naik lebih curam. Artinya:
Secara matematis:
\[ \text{DiD} = (\bar{Y}_{T,\,post} - \bar{Y}_{T,\,pre}) - (\bar{Y}_{C,\,post} - \bar{Y}_{C,\,pre}). \]
Garis kontrol berfungsi sebagai counterfactual — apa yang akan terjadi pada kelompok treatment jika tidak ada kebijakan.
Cara Membaca Secara Intuitif
Ilustrasi Konseptual
Bayangkan:
- Semua provinsi naik pendapatannya sekitar +2 karena
pertumbuhan ekonomi nasional.
- Provinsi yang mendapat pelatihan (treatment) naik
+7.
Perbedaan tambahan +5 itulah efek DiD:
kenaikan ekstra karena kebijakan.
Contoh Reproducible (R)
Contoh kecil berikut mensimulasikan data 2×2 lalu menampilkan grafik mean Pre–Post untuk treatment vs control, lengkap dengan garis counterfactual (putus‑putus) yang menunjukkan “seandainya tidak ada kebijakan”.
set.seed(123)
library(dplyr)
library(ggplot2)
# N unit, 2 periode
n <- 200
dat <- expand.grid(id = 1:n, time = 0:1) %>% as_tibble()
# 50% unit sebagai treatment
dat <- dat %>%
mutate(treat = as.integer(id <= n/2))
# DGP (gunakan nrow(dat) agar aman)
dat <- dat %>%
mutate(
Y = 1.0 + 0.2*treat + 1.0*time + 0.8*(treat*time) + rnorm(nrow(dat), sd = 0.25)
)
# Plot
mean_trend <- dat %>%
group_by(treat, time) %>%
summarise(meanY = mean(Y), .groups = "drop")
ggplot(mean_trend, aes(x = factor(time, labels = c("Pre","Post")),
y = meanY, color = factor(treat))) +
geom_line(size = 1.2) +
geom_point(size = 3) +
labs(x = "Waktu", y = "Rata-rata Y", color = "Kelompok",
title = "Visualisasi Intuisi Difference-in-Differences") +
scale_color_discrete(labels = c("Kontrol","Treatment")) +
theme_minimal()
Kesimpulan Visual
Grafik menunjukkan bahwa kebijakan meningkatkan outcome kelompok treatment lebih besar daripada kontrol. Setelah mengeluarkan efek waktu umum, selisih tambahan itulah efek DiD (\(\Delta \text{DiD}\)).
m_ols <- lm(y ~ D_i + Post_t + D_i:Post_t, data = did_2x2)
summary(m_ols)$coefficients
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.1081474 0.1376832 8.0485281 9.875243e-15
## D_i 0.1920121 0.1977016 0.9712217 3.320307e-01
## Post_t 1.6339070 0.1947135 8.3913387 8.594261e-16
## D_i:Post_t 2.0130034 0.2795923 7.1997812 3.054983e-12
Koefisien interaksi D_i:Post_t adalah \(\hat{\delta}\) (estimasi efek DiD). Untuk
standard errors yang robust & clustered (per
id), gunakan sandwich + lmtest:
# Cluster-robust SE by id
vcov_cl <- sandwich::vcovCL(m_ols, cluster = ~ id, type = "HC1")
coeftest(m_ols, vcov = vcov_cl)
##
## t test of coefficients:
##
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.10815 0.14832 7.4713 5.127e-13 ***
## D_i 0.19201 0.19574 0.9809 0.3272
## Post_t 1.63391 0.15350 10.6447 < 2.2e-16 ***
## D_i:Post_t 2.01300 0.21058 9.5591 < 2.2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# TWFE via fixest::feols
m_twfe <- fixest::feols(y ~ D_i:Post_t | id + t, data = did_2x2, cluster = ~ id)
summary(m_twfe)
## OLS estimation, Dep. Var.: y
## Observations: 400
## Fixed-effects: id: 200, t: 2
## Standard-errors: Clustered (id)
## Estimate Std. Error t value Pr(>|t|)
## D_i:Post_t 2.013 0.210319 9.57118 < 2.2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## RMSE: 0.741589 Adj. R2: 0.739123
## Within R2: 0.315115
\(\hat{\delta}\) (koefisien pada
D_i:Post_t) adalah efek perlakuan setelah mengontrol
unit FE dan time FE, dengan
clustered SE pada level unit.
Pada 2×2, event study terbatas. Namun kita bisa mensimulasikan multi-periode dan memeriksa lead (efek sebelum kebijakan) sebagai placebo. Jika lead signifikan, indikasi pelanggaran tren paralel.
set.seed(42)
N <- 300
Tt <- 6 # lebih banyak periode
id <- rep(1:N, each = Tt)
t <- rep(1:Tt, times = N)
# separuh unit menerima treatment mulai periode 4 (statis, tidak bertahap)
D_i <- rep(rbinom(N, 1, 0.5), each = Tt)
Post_t <- as.integer(t >= 4)
# FE + tren umum
alpha_i <- rep(rnorm(N, 0, 1), each = Tt)
lambda_t <- rep(seq(0, 1.0, length.out = Tt), times = N) # tren umum linear ringan
delta_true <- 1.5
y <- 2 + 0.2*D_i + 0.6*Post_t + delta_true*(D_i*Post_t) + alpha_i + lambda_t + rnorm(N*Tt, 0, 1)
panel <- tibble(id, t, D_i, Post_t, y)
Definisikan event time untuk unit treatment: \(\tau = t - t\_0\) dengan \(t\_0=4\). Untuk kontrol, kita bisa set
NA (tidak pernah treat). Lalu kita bentuk variabel
lead/lag sebagai faktor dan estimasi model dengan
time FE & unit FE, mengabaikan
periode referensi (misal \(\tau=-1\)).
# --- PARAMETER EVENT TIME ---
t0 <- 4
# 1) Event time utk SEMUA unit (kontrol juga dihitung relatif t0),
# agar tidak ada NA yang bikin level hilang.
panel <- panel %>%
dplyr::mutate(
event_time_all = t - t0
)
# 2) Bucket lead/lag yang sama utk semua unit
panel <- panel %>%
dplyr::mutate(
event_bucket = dplyr::case_when(
event_time_all <= -3L ~ "lead_3ormore",
event_time_all == -2L ~ "lead_2",
event_time_all == -1L ~ "lead_1", # baseline yang DI-DROP sbg ref
event_time_all == 0L ~ "lag_0",
event_time_all == 1L ~ "lag_1",
event_time_all == 2L ~ "lag_2",
event_time_all >= 3L ~ "lag_3ormore"
),
event_bucket = factor(
event_bucket,
levels = c("lead_3ormore","lead_2","lead_1",
"lag_0","lag_1","lag_2","lag_3ormore")
)
)
# 3) Pastikan level referensi ADA pada treated; jika tidak, fallback otomatis
avail_ref <- levels(droplevels(panel$event_bucket[panel$D_i == 1L]))
ref_level <- if ("lead_1" %in% avail_ref) "lead_1" else
if ("lead_2" %in% avail_ref) "lead_2" else
if ("lead_3ormore" %in% avail_ref) "lead_3ormore" else
stop("Tidak ada level 'lead_x' pada unit treated. Periksa t0 & konstruksi data.")
# 4) ESTIMASI EVENT-STUDY (pakai SEMUA unit; FE: id + t; cluster: id)
m_es <- fixest::feols(
y ~ i(event_bucket, D_i, ref = ref_level) | id + t,
data = panel,
cluster = ~ id
)
# --- EKSTRAK KOEFISIEN: cari term yang dimulai "event_bucket::" ---
est <- broom::tidy(m_es) %>%
dplyr::filter(stringr::str_detect(term, "^event_bucket::")) %>%
dplyr::select(term, estimate, std.error, p.value)
est
## # A tibble: 5 × 4
## term estimate std.error p.value
## <chr> <dbl> <dbl> <dbl>
## 1 event_bucket::lead_3ormore:D_i -0.208 0.165 2.08e- 1
## 2 event_bucket::lead_2:D_i -0.0311 0.164 8.50e- 1
## 3 event_bucket::lag_0:D_i 1.34 0.163 6.77e-15
## 4 event_bucket::lag_1:D_i 1.56 0.169 4.22e-18
## 5 event_bucket::lag_2:D_i 1.38 0.162 7.95e-16
Interpretasi: koefisien pada leads (mis.
lead_3ormore, lead_2) seharusnya tidak berbeda
signifikan dari nol (tidak ada efek sebelum treatment).
Koefisien pada lags (lag_0, lag_1,
dst.) menunjukkan dinamika efek setelah kebijakan.
# Map nama term -> indeks waktu (-3, -2, 0, 1, 2, 3)
map_time <- c(
"lead_3ormore" = -3,
"lead_2" = -2,
# "lead_1" adalah REFERENCE -> tidak muncul sebagai koefisien
"lag_0" = 0,
"lag_1" = 1,
"lag_2" = 2,
"lag_3ormore" = 3
)
# Ambil nama bucket dari "event_bucket::<bucket>:D_i"
est_plot <- est %>%
dplyr::mutate(
bucket = term |>
stringr::str_remove("^event_bucket::") |>
stringr::str_remove(":D_i$"),
time = dplyr::case_when(
bucket == "lead_3ormore" ~ -3,
bucket == "lead_2" ~ -2,
# "lead_1" adalah baseline (ref), jadi TIDAK muncul di koefisien
bucket == "lag_0" ~ 0,
bucket == "lag_1" ~ 1,
bucket == "lag_2" ~ 2,
bucket == "lag_3ormore" ~ 3,
TRUE ~ NA_real_
),
ci_lo = estimate - 1.96*std.error,
ci_hi = estimate + 1.96*std.error
) %>%
dplyr::arrange(time)
# Cek cepat: kalau semua NA, berarti label-nya beda—lihat names(coef(m_es))
if (all(is.na(est_plot$time))) {
message("Label tidak terpetakan. Cek names(coef(m_es)) untuk pola aktual:")
print(names(coef(m_es)))
}
ggplot2::ggplot(est_plot, ggplot2::aes(x = time, y = estimate)) +
ggplot2::geom_hline(yintercept = 0, linetype = 2) +
ggplot2::geom_point(size = 2) +
ggplot2::geom_errorbar(ggplot2::aes(ymin = ci_lo, ymax = ci_hi), width = 0.1) +
ggplot2::scale_x_continuous(breaks = -3:3) +
ggplot2::labs(
x = "Event time (t - t0)",
y = paste0("ATT (ref = ", ref_level, ")"),
title = "Event-study (TWFE; SE dikelompokkan @ id)"
) +
ggplot2::theme_minimal(base_size = 12)
Event-study: koefisien lead/lag relatif terhadap periode referensi (lead_1).
Di sini contoh staggered adoption minimalis memakai
fixest::sunab() (Sun–Abraham).
set.seed(99)
N <- 400
Tt <- 8
id <- rep(1:N, each = Tt)
t <- rep(1:Tt, times = N)
# Unit dibagi ke beberapa gelombang treatment (g): 3,5,7; sebagian tak pernah treated (g=Inf)
groups <- sample(c(3,5,7,Inf), N, replace = TRUE, prob = c(.3,.3,.2,.2))
g <- rep(groups, each = Tt)
treated <- as.integer(t >= g) # akan 0 bila g=Inf
alpha_i <- rep(rnorm(N, 0, 1), each = Tt)
lambda_t <- rep(sin(2*pi*(1:Tt)/Tt), times = N) # shock waktu non-linear
tau <- 1.2 # ATT rata-rata
y <- 1 + 0.5*treated + tau*treated + alpha_i + lambda_t + rnorm(N*Tt, 0, 1)
dat <- tibble(id, t, y, g, treated)
# Sun-Abraham 'sunab' with fixest
m_sa <- feols(y ~ sunab(g, t) | id + t, data = dat, cluster = ~ id)
iplot(m_sa, ref.line = 0) # plot event-time ATT
Difference-in-Differences (DiD) mengukur efek kausal kebijakan dengan membandingkan perubahan outcome pada kelompok treatment terhadap perubahan pada kelompok control dari sebelum ke sesudah kebijakan. Intuisi kasarnya:
\[ \text{Efek DiD} = (\Delta Y_{\text{treat}}) - (\Delta Y_{\text{control}}). \]
Asumsi kunci: parallel trends — tanpa kebijakan, tren outcome kedua kelompok akan bergerak sejajar.
set.seed(123)
library(dplyr)
library(ggplot2)
# N unit, 2 periode (0 = sebelum, 1 = sesudah)
n <- 200
dat <- expand.grid(id = 1:n, time = 0:1) %>% as_tibble()
# 50% unit sebagai treatment
dat <- dat %>%
mutate(treat = as.integer(id <= n/2))
# DGP:
# baseline 10, beda baseline treatment 2, efek waktu umum 3,
# efek kebijakan (DiD) = 5 (hanya untuk treatment pada periode sesudah)
dat <- dat %>%
mutate(
Y = 10 + 2*treat + 3*time + 5*(treat*time) + rnorm(nrow(.), sd = 3)
# ^^^^^^^^^^ panjang noise tepat = jumlah baris data
)
# Rata-rata per grup-waktu untuk plot
mean_trend <- dat %>%
group_by(treat, time) %>%
summarise(meanY = mean(Y), .groups = "drop")
ggplot(mean_trend, aes(x = time, y = meanY, color = factor(treat))) +
geom_line(size = 1.2) +
geom_point(size = 3) +
scale_x_continuous(breaks = c(0,1), labels = c("Sebelum","Sesudah")) +
scale_color_discrete(labels = c("Kontrol","Treatment")) +
labs(x = "Waktu", y = "Rata-rata Y", color = "Kelompok",
title = "Visualisasi Intuisi Difference-in-Differences") +
theme_minimal()
# (Opsional) Sanity check: regresi DiD klasik
did_lm <- lm(Y ~ treat + time + treat:time, data = dat)
summary(did_lm)$coefficients
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 9.677360 0.2904595 33.317413 6.311389e-117
## treat 2.593858 0.4107718 6.314597 7.278946e-10
## time 3.213972 0.4107718 7.824227 4.696510e-14
## treat:time 4.876206 0.5809190 8.393951 8.433604e-16
# Koefisien interaksi (treat:time) ≈ 5 (sesuai DGP)
Model dasar DiD:
\[ Y_{it} = \alpha + \beta\,\text{treat}_i + \gamma\,\text{post}_t + \delta(\text{treat}_i \times \text{post}_t) + \varepsilon_{it}. \]
Di simulasi ini, \(\text{post}_t = \text{time}\). Koefisien interaksi (\(\delta\)) adalah efek DiD.
m_did <- lm(Y ~ treat + time + treat:time, data = dat)
broom::tidy(m_did)
## # A tibble: 4 × 5
## term estimate std.error statistic p.value
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 (Intercept) 9.68 0.290 33.3 6.31e-117
## 2 treat 2.59 0.411 6.31 7.28e- 10
## 3 time 3.21 0.411 7.82 4.70e- 14
## 4 treat:time 4.88 0.581 8.39 8.43e- 16
Interpretasi cepat: koefisien treat:time adalah estimasi
efek kebijakan (DiD).
Pada praktiknya, error sering berkorelasi dalam unit/kelompok.
Gunakan clustered robust SE (misalnya dikelompokkan
pada id). Jika paket sandwich dan
lmtest tersedia, hasil dengan SE berkelompok akan
ditampilkan.
if (requireNamespace("sandwich", quietly = TRUE) && requireNamespace("lmtest", quietly = TRUE)) {
vcov_cl <- sandwich::vcovCL(m_did, cluster = dat$id, type = "HC1")
lmtest::coeftest(m_did, vcov = vcov_cl)
} else {
message("Lewati SE berkelompok: paket 'sandwich' dan/atau 'lmtest' tidak tersedia.")
}
##
## t test of coefficients:
##
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 9.67736 0.29046 33.3173 < 2.2e-16 ***
## treat 2.59386 0.39943 6.4939 2.511e-10 ***
## time 3.21397 0.41687 7.7097 1.029e-13 ***
## treat:time 4.87621 0.59210 8.2355 2.631e-15 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Untuk data panel (unit berulang antar waktu), model two-way fixed effects (TWFE) menambahkan efek tetap unit dan waktu:
\[ Y_{it} = \alpha_i + \lambda_t + \delta(\text{treat}_i \times \text{post}_t) + \varepsilon_{it}. \]
Implementasi sederhana (demeaning manual) untuk memberi gambaran ide dasarnya:
# Buat faktor unit dan waktu
dat <- dat %>% mutate(id_f = factor(id), t_f = factor(time))
# Estimasi TWFE via regresi dengan dummy unit & waktu (tanpa intercept duplikat)
m_twfe <- lm(Y ~ treat:time + id_f + t_f, data = dat)
broom::tidy(m_twfe) %>%
filter(term == "treat:time") # sorot koefisien interaksi utama
## # A tibble: 1 × 5
## term estimate std.error statistic p.value
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 treat:time 4.88 0.591 8.25 2.24e-14
Catatan: Untuk aplikasi serius, pertimbangkan paket khusus seperti fixest atau lfe untuk efisiennya estimasi FE skala besar serta ragam opsi SE.
Dengan hanya dua periode, pemeriksaan bersifat kualitatif melalui plot rata-rata per grup-waktu. Untuk banyak periode, gunakan event study agar bisa melihat dinamika pra- dan pasca-kebijakan.
ggplot(mean_trend, aes(x = time, y = meanY, group = factor(treat), linetype = factor(treat))) +
geom_line(size = 1.1) + geom_point(size = 3) +
scale_x_continuous(breaks = c(0,1), labels = c("Sebelum","Sesudah")) +
labs(title = "Pemeriksaan Kualitatif Parallel Trends",
x = "Waktu", y = "Rata-rata Outcome", linetype = "Kelompok") +
theme_bw()
Rumus DiD empat sel:
\[ (\bar{Y}_{T,1}-\bar{Y}_{T,0})-(\bar{Y}_{C,1}-\bar{Y}_{C,0}). \]
cells <- dat %>%
group_by(treat, time) %>%
summarise(meanY = mean(Y), .groups = "drop") %>%
pivot_wider(names_from = time, values_from = meanY, names_prefix = "t")
cells
## # A tibble: 2 × 3
## treat t0 t1
## <int> <dbl> <dbl>
## 1 0 9.68 12.9
## 2 1 12.3 20.4
did_four_cells <- (cells %>% filter(treat == 1) %>% pull(t1) -
cells %>% filter(treat == 1) %>% pull(t0)) -
(cells %>% filter(treat == 0) %>% pull(t1) -
cells %>% filter(treat == 0) %>% pull(t0))
did_four_cells
## [1] 4.876206
Mengevaluasi efek kebijakan/perlakuan sebelum–sesudah pada kelompok perlakuan vs kontrol. Asumsi kunci: parallel trends.
\[ Y_{it} = \alpha + \delta D_i + \gamma T_t + \beta(D_i \times T_t) + \varepsilon_{it}. \]
set.seed(42)
n_g <- 100
group <- rep(c(0,1), each = n_g) # 0: kontrol, 1: perlakuan
time <- rep(rep(c(0,1), each = n_g/2), 2) # 0: sebelum, 1: sesudah
tau <- 2 # efek perlakuan sejati
y <- 5 + 1*group + 1*time + tau*(group*time) + rnorm(2*n_g, 0, 1)
did_df <- tibble(y, group, time)
did_model <- fixest::feols(y ~ group*time, data = did_df)
summary(did_model)
## OLS estimation, Dep. Var.: y
## Observations: 200
## Standard-errors: IID
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 4.964328 0.138292 35.89743 < 2.2e-16 ***
## group 0.884421 0.195574 4.52217 1.0585e-05 ***
## time 1.136373 0.195574 5.81044 2.4874e-08 ***
## group:time 1.991162 0.276584 7.19912 1.2635e-11 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## RMSE: 0.968044 Adj. R2: 0.703117
Interpretasi. Koefisien interaksi
group:time adalah taksiran efek perlakuan rata-rata.
Synthetic Control Method (SCM) adalah metode kuasi-eksperimen untuk mengevaluasi dampak kausal kebijakan/peristiwa pada satu unit yang terpapar (treated unit) ketika hanya tersedia beberapa unit pembanding. Inti gagasannya: membangun “kontrol sintetis” sebagai kombinasi cembung dari unit-unit donor (kontrol) sehingga jejak (trajectory) pra-intervensi dari treated tercocokkan sebaik mungkin.
Secara informal, Synthetic Control Method (SCM) bertujuan memperkirakan counterfactual \(Y_{1t}(0)\)—yakni hasil yang mungkin terjadi pada unit treated jika tidak ada intervensi atau kebijakan. Estimatornya didefinisikan sebagai:
\[ \hat{Y}^{\text{SC}}_{1t}(0) = \sum_{j=2}^{J+1} w_j \, Y_{jt}, \]
dengan syarat \(w_j \ge 0\) dan
\(\sum_{j=2}^{J+1} w_j = 1\).
Bobot \(w_j\) dipilih sedemikian rupa
sehingga kombinasi linier dari unit kontrol (\(j = 2, \dots, J+1\)) menghasilkan
hasil dan predictors pra-intervensi yang sedekat
mungkin dengan milik unit treated.
SCM meminimalkan jarak pra-intervensi antara treated dan kombinasi sintetis terhadap sekumpulan predictors (rata-rata pra, lag outcome, kovariat). Matriks \(V\) (bobot untuk predictors) dan vektor \(w\) (bobot donor) dipilih melalui prosedur dua tingkat (nested optimization).
This R Markdown is adapted from Danilo Freire’s “A Simple SCM Tutorial” (2018). Tujuannya: file yang siap knit tanpa error umum seperti
unit.variable not found as numeric variable in foo.
# Jalankan baris-baris ini jika paket belum terpasang (hapus komentar #)
# install.packages("Synth", repos = "https://cloud.r-project.org")
# install.packages("gsynth", repos = "https://cloud.r-project.org")
library(Synth)
library(gsynth)
# Reproducibility
set.seed(1)
# Knitr options (opsional)
knitr::opts_chunk$set(message = FALSE, warning = FALSE, dpi = 120)
Kita buat data untuk 10 state dan 30 tahun. State
A menerima perlakuan mulai tahun 15. Kita
pastikan kolom pengenal unit numeric agar tidak muncul
error
unit.variable not found as numeric variable in foo.
year <- rep(1:30, 10)
state <- rep(LETTERS[1:10], each = 30)
X1 <- round(rnorm(300, mean = 2, sd = 1), 2)
X2 <- round(rbinom(300, 1, 0.5) + rnorm(300), 2)
Y <- round(1 + 2*X1 + rnorm(300), 2)
df <- data.frame(Y = Y, X1 = X1, X2 = X2, state = state, year = year,
stringsAsFactors = FALSE)
# Pastikan tipe data yang sesuai
df$Y <- as.numeric(df$Y)
df$X1 <- as.numeric(df$X1)
df$X2 <- as.numeric(df$X2)
df$year <- as.numeric(df$year)
df$state <- as.character(df$state)
# Kode numerik untuk unit (WAJIB numeric untuk Synth::dataprep)
df$state.num <- as.numeric(factor(df$state, levels = LETTERS[1:10]))
# Perlakuan untuk State A mulai tahun 15
df$T <- ifelse(df$state == "A" & df$year >= 15, 1, 0)
# Tambahkan efek treatment sebesar 20 pada outcome setelah tahun 15 untuk A
df$Y <- ifelse(df$state == "A" & df$year >= 15, df$Y + 20, df$Y)
# Cek struktur
str(df)
## 'data.frame': 300 obs. of 7 variables:
## $ Y : num 2.29 4.51 2.07 8.87 4.37 1.32 8 7.49 6.98 3.72 ...
## $ X1 : num 1.37 2.18 1.16 3.6 2.33 1.18 2.49 2.74 2.58 1.69 ...
## $ X2 : num 1.96 0.4 -0.75 -0.56 -0.45 1.06 0.51 -2.1 0 0.54 ...
## $ state : chr "A" "A" "A" "A" ...
## $ year : num 1 2 3 4 5 6 7 8 9 10 ...
## $ state.num: num 1 1 1 1 1 1 1 1 1 1 ...
## $ T : num 0 0 0 0 0 0 0 0 0 0 ...
head(df)
## Y X1 X2 state year state.num T
## 1 2.29 1.37 1.96 A 1 1 0
## 2 4.51 2.18 0.40 A 2 1 0
## 3 2.07 1.16 -0.75 A 3 1 0
## 4 8.87 3.60 -0.56 A 4 1 0
## 5 4.37 2.33 -0.45 A 5 1 0
## 6 1.32 1.18 1.06 A 6 1 0
Kita siapkan objek dataprep dan jalankan
synth. Perhatikan bahwa: - unit.variable
harus numeric. - treatment.identifier
di-set ke 1 (karena A dikodekan
menjadi 1). - controls.identifier adalah 2 hingga 10.
dataprep.out <- Synth::dataprep(
foo = df,
predictors = c("X1", "X2"),
dependent = "Y",
unit.variable = "state.num",
time.variable = "year",
unit.names.variable = "state",
treatment.identifier = 1,
controls.identifier = 2:10,
time.predictors.prior = 1:14,
time.optimize.ssr = 1:14,
time.plot = 1:30
)
synth.out <- Synth::synth(dataprep.out)
##
## X1, X0, Z1, Z0 all come directly from dataprep object.
##
##
## ****************
## searching for synthetic control unit
##
##
## ****************
## ****************
## ****************
##
## MSPE (LOSS V): 9.831789
##
## solution.v:
## 0.3888387 0.6111613
##
## solution.w:
## 0.1115941 0.1832781 0.1027237 0.312091 0.06096758 0.03509706 0.05893735 0.05746256 0.07784853
# Tabel ringkas
synth.tables <- Synth::synth.tab(
dataprep.res = dataprep.out,
synth.res = synth.out
)
synth.tables
## $tab.pred
## Treated Synthetic Sample Mean
## X1 2.028 2.028 2.017
## X2 0.513 0.513 0.394
##
## $tab.v
## v.weights
## X1 0.389
## X2 0.611
##
## $tab.w
## w.weights unit.names unit.numbers
## 2 0.112 B 2
## 3 0.183 C 3
## 4 0.103 D 4
## 5 0.312 E 5
## 6 0.061 F 6
## 7 0.035 G 7
## 8 0.059 H 8
## 9 0.057 I 9
## 10 0.078 J 10
##
## $tab.loss
## Loss W Loss V
## [1,] 9.761706e-12 9.831789
path.plot(
synth.res = synth.out,
dataprep.res = dataprep.out,
Ylab = "Y",
Xlab = "Year",
Legend = c("State A", "Synthetic State A"),
Legend.position = "topleft"
)
abline(v = 15, lty = 2)
gaps.plot(
synth.res = synth.out,
dataprep.res = dataprep.out,
Ylab = "Gap",
Xlab = "Year",
Ylim = c(-30, 30),
Main = ""
)
abline(v = 15, lty = 2)
gsynth memungkinkan multiple treated units dan
komponen faktor laten (iterative fixed effects). Kita
gunakan two-way fixed effects dengan cross-validation
untuk memilih jumlah faktor r. Agar knitting mulus di
berbagai lingkungan, kita set parallel = FALSE dan
mengurangi nboots.
gs.out <- gsynth(
formula = Y ~ T + X1 + X2,
data = df,
index = c("state", "year"),
force = "two-way",
CV = TRUE,
r = c(0, 5),
se = TRUE,
inference = "parametric",
nboots = 200, # kurangi untuk mempercepat knit
parallel = FALSE # TRUE di mesin lokal jika ingin lebih cepat
)
## Cross-validating ...
## r = 0; sigma2 = 1.13533; IC = 0.95632; PC = 0.96713; MSPE = 1.65502
## r = 1; sigma2 = 0.96885; IC = 1.54420; PC = 4.30644; MSPE = 1.33375
## r = 2; sigma2 = 0.81855; IC = 2.08062; PC = 6.58556; MSPE = 1.27341*
## r = 3; sigma2 = 0.71670; IC = 2.61125; PC = 8.35187; MSPE = 1.79319
## r = 4; sigma2 = 0.62823; IC = 3.10156; PC = 9.59221; MSPE = 2.02301
## r = 5; sigma2 = 0.55497; IC = 3.55814; PC = 10.48406; MSPE = 2.79596
##
## r* = 2
##
## Simulating errors .....Bootstrapping ...
## ..
gs.out
## Call:
## gsynth.formula(formula = Y ~ T + X1 + X2, data = df, index = c("state",
## "year"), force = "two-way", r = c(0, 5), CV = TRUE, se = TRUE,
## nboots = 200, inference = "parametric", parallel = FALSE)
##
## Average Treatment Effect on the Treated:
## Estimate S.E. CI.lower CI.upper p.value
## ATT.avg 20.45 0.7141 19.06 21.85 0
##
## ~ by Period (including Pre-treatment Periods):
## ATT S.E. CI.lower CI.upper p.value n.Treated
## -13 -0.43323 1.0923 -2.5741 1.7076 6.916e-01 0
## -12 -0.78276 0.9471 -2.6390 1.0735 4.085e-01 0
## -11 -0.19199 0.9309 -2.0165 1.6325 8.366e-01 0
## -10 -0.98289 1.0057 -2.9540 0.9882 3.284e-01 0
## -9 -0.94504 0.8148 -2.5419 0.6519 2.461e-01 0
## -8 -0.23970 0.9051 -2.0136 1.5342 7.911e-01 0
## -7 1.74468 1.1957 -0.5988 4.0882 1.445e-01 0
## -6 1.73998 1.2365 -0.6836 4.1636 1.594e-01 0
## -5 0.63689 0.9037 -1.1343 2.4081 4.810e-01 0
## -4 -0.03246 0.8872 -1.7713 1.7063 9.708e-01 0
## -3 -0.25405 0.8082 -1.8380 1.3299 7.533e-01 0
## -2 0.10273 0.8376 -1.5390 1.7445 9.024e-01 0
## -1 -0.23391 0.5835 -1.3776 0.9097 6.885e-01 0
## 0 -0.12827 0.9169 -1.9253 1.6688 8.887e-01 0
## 1 21.19473 1.7697 17.7263 24.6632 0.000e+00 1
## 2 22.84214 1.2183 20.4544 25.2299 0.000e+00 1
## 3 20.36902 1.2826 17.8552 22.8829 0.000e+00 1
## 4 22.33201 1.9629 18.4848 26.1793 0.000e+00 1
## 5 19.68346 2.4703 14.8417 24.5252 1.554e-15 1
## 6 18.08863 0.8339 16.4541 19.7231 0.000e+00 1
## 7 19.75869 1.3372 17.1378 22.3796 0.000e+00 1
## 8 18.92529 1.9750 15.0543 22.7963 0.000e+00 1
## 9 21.74294 0.9219 19.9360 23.5499 0.000e+00 1
## 10 21.64782 0.9279 19.8291 23.4665 0.000e+00 1
## 11 19.39832 1.3055 16.8396 21.9570 0.000e+00 1
## 12 19.31785 0.8282 17.6946 20.9411 0.000e+00 1
## 13 22.28342 1.1193 20.0895 24.4773 0.000e+00 1
## 14 20.38876 0.9184 18.5888 22.1888 0.000e+00 1
## 15 18.92550 1.5937 15.8019 22.0491 0.000e+00 1
## 16 20.37687 1.6095 17.2223 23.5314 0.000e+00 1
##
## Coefficients for the Covariates:
## beta S.E. CI.lower CI.upper p.value
## X1 1.93082 0.06295 1.80745 2.0542 0.0000
## X2 0.03809 0.04610 -0.05227 0.1284 0.4087
# Plot utama
plot(gs.out)
# Counterfactual (trajektori aktual vs counterfactual untuk unit treated)
plot(gs.out, type = "counterfactual")
# Estimasi untuk semua kontrol (raw = "all")
plot(gs.out, type = "counterfactual", raw = "all")
unit.variable not found as numeric variable in foo
Pastikan kolom yang dipakai sebagai unit.variable
numeric (contoh: state.num). Jika masih
gagal, cek apakah nama kolom sesuai (ejaan tepat).
X1,X0,Z1,Z0 were individually input
(pesan informasi)
Itu normal saat synth() dipanggil menggunakan objek
dataprep.
Plot tidak muncul saat knit
Pastikan chunk tidak echo=FALSE dan tidak
fig.show='hide'.
Vector Autoregression (VAR) adalah model untuk beberapa variabel time‑series yang saling mempengaruhi secara dinamis. Setiap variabel dijelaskan oleh lag dirinya dan lag variabel lain. Intuisinya: ekonomi itu ngobrol—bukan monolog—jadi modelnya juga multi-way conversations.
Secara ringkas, VAR cocok ketika:
Untuk vektor \(y_t\in\mathbb{R}^K\) dan lag orde \(p\), model VAR(p): \[ y_t = c + A_1 y_{t-1} + A_2 y_{t-2} + \cdots + A_p y_{t-p} + u_t,\quad u_t\sim \text{i.i.d. }(0,\,\Sigma_u), \] dengan: - \(c\) vektor intersep \((K\times 1)\), - \(A_j\) matriks koefisien \((K\times K)\), - \(u_t\) inovasi white noise kovarian \(\Sigma_u\).
Bentuk persamaan per variabel (misal \(K=2\)): \[ \begin{aligned} y_{1t} &= c_1 + \sum_{j=1}^p a^{(11)}j\,y{1,t-j} + \sum_{j=1}^p a^{(12)}j\,y{2,t-j} + u_{1t},\\ y_{2t} &= c_2 + \sum_{j=1}^p a^{(21)}j\,y{1,t-j} + \sum_{j=1}^p a^{(22)}j\,y{2,t-j} + u_{2t}. \end{aligned} \]
Bila data punya unit root (non‑stationary) dan tidak kointegrasi, gunakan differences. Bila terkointegrasi, gunakan VECM (VAR terbatas) dan, bila perlu, transform ke SVAR/IRF struktural.
Pilih \(p\) via kriteria informasi: AIC, BIC (SC), HQ. Secara umum: - AIC cenderung memilih model lebih besar (baik untuk forecast), - BIC/SC lebih hemat parameter (baik untuk penjelasan).
Variabel \(x\) Granger-causes \(y\) bila lag dari \(x\) meningkatkan kemampuan memprediksi \(y\) di luar lag \(y\) sendiri (uji Wald pada blok koefisien lag \(x\) pada persamaan \(y\)).
vars)Kita gunakan data Canada bawaan paket
vars (indikator makro Kanada: e (employment),
prod (productivity), rw (real wages),
U (unemployment)). Agar ringkas dan stasioner, kita pakai
perubahan log (growth rates) untuk tiga seri pertama,
dan diferensiasi U.
library(vars)
library(tidyverse)
library(urca) # unit root tests
data("Canada", package = "vars") # ts quarterly
# Transformasi: growth rate (diff log) untuk e, prod, rw; dan diff untuk U
can_df <- as_tibble(Canada) |>
mutate(
le = log(e),
lprod= log(prod),
lrw = log(rw)
) |>
mutate(
d_le = c(NA, diff(le)),
d_lprod = c(NA, diff(lprod)),
d_lrw = c(NA, diff(lrw)),
d_U = c(NA, diff(U))
) |>
drop_na(d_le, d_lprod, d_lrw, d_U)
glimpse(can_df)
## Rows: 83
## Columns: 11
## $ e <dbl> 929.8040, 930.3184, 931.4277, 932.6620, 933.5509, 933.5315, 93…
## $ prod <dbl> 404.6398, 403.8149, 404.2158, 405.0467, 404.4167, 402.8191, 40…
## $ rw <dbl> 388.1358, 390.5401, 393.9638, 396.7647, 400.0217, 400.7515, 40…
## $ U <dbl> 7.70, 7.47, 7.27, 7.37, 7.13, 7.40, 8.33, 8.83, 10.43, 12.20, …
## $ le <dbl> 6.834974, 6.835527, 6.836719, 6.838043, 6.838996, 6.838975, 6.…
## $ lprod <dbl> 6.002997, 6.000957, 6.001949, 6.004002, 6.002446, 5.998488, 5.…
## $ lrw <dbl> 5.961355, 5.967531, 5.976259, 5.983343, 5.991519, 5.993342, 6.…
## $ d_le <dbl> 2.080985e-04, 5.530851e-04, 1.191677e-03, 1.324312e-03, 9.5266…
## $ d_lprod <dbl> -1.794141e-03, -2.040810e-03, 9.922648e-04, 2.053575e-03, -1.5…
## $ d_lrw <dbl> 0.0051652516, 0.0061755129, 0.0087283848, 0.0070843162, 0.0081…
## $ d_U <dbl> 0.17, -0.23, -0.20, 0.10, -0.24, 0.27, 0.93, 0.50, 1.60, 1.77,…
adf_le <- ur.df(can_df$d_le, type = "drift", selectlags = "AIC")
adf_lprod <- ur.df(can_df$d_lprod, type = "drift", selectlags = "AIC")
adf_lrw <- ur.df(can_df$d_lrw, type = "drift", selectlags = "AIC")
adf_U <- ur.df(can_df$d_U, type = "drift", selectlags = "AIC")
summary(adf_le)
##
## ###############################################
## # Augmented Dickey-Fuller Test Unit Root Test #
## ###############################################
##
## Test regression drift
##
##
## Call:
## lm(formula = z.diff ~ z.lag.1 + 1 + z.diff.lag)
##
## Residuals:
## Min 1Q Median 3Q Max
## -9.716e-04 -2.668e-04 -2.083e-05 2.900e-04 7.841e-04
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.491e-04 5.622e-05 2.651 0.00971 **
## z.lag.1 -3.595e-01 7.927e-02 -4.536 2.05e-05 ***
## z.diff.lag 3.241e-01 1.076e-01 3.011 0.00350 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 0.0004141 on 78 degrees of freedom
## Multiple R-squared: 0.2251, Adjusted R-squared: 0.2052
## F-statistic: 11.33 on 2 and 78 DF, p-value: 4.789e-05
##
##
## Value of test-statistic is: -4.536 10.2891
##
## Critical values for test statistics:
## 1pct 5pct 10pct
## tau2 -3.51 -2.89 -2.58
## phi1 6.70 4.71 3.86
Dari praktik umum, growth/difference biasanya stasioner. Kita lanjutkan dengan VAR pada \((d\_le, d\_lprod, d\_lrw, d\_U)\).
library(dplyr)
Y <- as.matrix(dplyr::select(can_df, d_le, d_lprod, d_lrw, d_U))
lag_sel <- VARselect(Y, lag.max = 8, type = "const")
lag_sel$selection # Rekomendasi AIC/SC/HQ
## AIC(n) HQ(n) SC(n) FPE(n)
## 2 1 1 2
lag_sel$criteria # Semua nilai kriteria
## 1 2 3 4 5
## AIC(n) -4.416680e+01 -4.419681e+01 -4.395284e+01 -4.397229e+01 -4.373554e+01
## HQ(n) -4.392005e+01 -4.375264e+01 -4.331127e+01 -4.313330e+01 -4.269915e+01
## SC(n) -4.354881e+01 -4.308441e+01 -4.234605e+01 -4.187110e+01 -4.113995e+01
## FPE(n) 6.590898e-20 6.420800e-20 8.273024e-20 8.260333e-20 1.077925e-19
## 6 7 8
## AIC(n) -4.349295e+01 -4.328747e+01 -4.326637e+01
## HQ(n) -4.225915e+01 -4.185626e+01 -4.163775e+01
## SC(n) -4.040297e+01 -3.970309e+01 -3.918759e+01
## FPE(n) 1.436324e-19 1.880081e-19 2.097188e-19
p_opt <- lag_sel$selection[1] # ambil satu (misal AIC)
var_mod <- VAR(Y, p = p_opt, type = "const")
summary(var_mod)
##
## VAR Estimation Results:
## =========================
## Endogenous variables: d_le, d_lprod, d_lrw, d_U
## Deterministic variables: const
## Sample size: 81
## Log Likelihood: 1346.623
## Roots of the characteristic polynomial:
## 0.667 0.667 0.5478 0.5478 0.4377 0.4377 0.3887 0.3448
## Call:
## VAR(y = Y, p = p_opt, type = "const")
##
##
## Estimation results for equation d_le:
## =====================================
## d_le = d_le.l1 + d_lprod.l1 + d_lrw.l1 + d_U.l1 + d_le.l2 + d_lprod.l2 + d_lrw.l2 + d_U.l2 + const
##
## Estimate Std. Error t value Pr(>|t|)
## d_le.l1 9.357e-01 1.522e-01 6.149 3.91e-08 ***
## d_lprod.l1 7.723e-02 2.740e-02 2.818 0.00623 **
## d_lrw.l1 -1.305e-02 2.136e-02 -0.611 0.54327
## d_U.l1 9.867e-05 2.065e-04 0.478 0.63419
## d_le.l2 -3.723e-01 1.641e-01 -2.269 0.02629 *
## d_lprod.l2 9.035e-03 2.809e-02 0.322 0.74864
## d_lrw.l2 -2.026e-02 2.083e-02 -0.973 0.33393
## d_U.l2 -6.821e-05 2.156e-04 -0.316 0.75265
## const 2.260e-04 9.671e-05 2.337 0.02223 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
##
## Residual standard error: 0.0003926 on 72 degrees of freedom
## Multiple R-Squared: 0.6497, Adjusted R-squared: 0.6108
## F-statistic: 16.69 on 8 and 72 DF, p-value: 1.041e-13
##
##
## Estimation results for equation d_lprod:
## ========================================
## d_lprod = d_le.l1 + d_lprod.l1 + d_lrw.l1 + d_U.l1 + d_le.l2 + d_lprod.l2 + d_lrw.l2 + d_U.l2 + const
##
## Estimate Std. Error t value Pr(>|t|)
## d_le.l1 -0.4012469 0.6212914 -0.646 0.52044
## d_lprod.l1 0.2124687 0.1118881 1.899 0.06158 .
## d_lrw.l1 0.0544832 0.0872323 0.625 0.53422
## d_U.l1 -0.0022976 0.0008431 -2.725 0.00806 **
## d_le.l2 -1.2057650 0.6700213 -1.800 0.07611 .
## d_lprod.l2 -0.0466083 0.1146841 -0.406 0.68565
## d_lrw.l2 -0.1537768 0.0850367 -1.808 0.07473 .
## d_U.l2 -0.0002858 0.0008803 -0.325 0.74640
## const 0.0012080 0.0003949 3.059 0.00312 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
##
## Residual standard error: 0.001603 on 72 degrees of freedom
## Multiple R-Squared: 0.2571, Adjusted R-squared: 0.1746
## F-statistic: 3.115 on 8 and 72 DF, p-value: 0.004458
##
##
## Estimation results for equation d_lrw:
## ======================================
## d_lrw = d_le.l1 + d_lprod.l1 + d_lrw.l1 + d_U.l1 + d_le.l2 + d_lprod.l2 + d_lrw.l2 + d_U.l2 + const
##
## Estimate Std. Error t value Pr(>|t|)
## d_le.l1 -0.0696036 0.7978308 -0.087 0.9307
## d_lprod.l1 -0.1914160 0.1436810 -1.332 0.1870
## d_lrw.l1 0.2577981 0.1120192 2.301 0.0243 *
## d_U.l1 0.0011986 0.0010827 1.107 0.2719
## d_le.l2 1.1734908 0.8604072 1.364 0.1769
## d_lprod.l2 -0.3747133 0.1472714 -2.544 0.0131 *
## d_lrw.l2 0.1619206 0.1091998 1.483 0.1425
## d_U.l2 -0.0002132 0.0011305 -0.189 0.8510
## const 0.0010521 0.0005071 2.075 0.0416 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
##
## Residual standard error: 0.002058 on 72 degrees of freedom
## Multiple R-Squared: 0.3933, Adjusted R-squared: 0.3259
## F-statistic: 5.835 on 8 and 72 DF, p-value: 9.696e-06
##
##
## Estimation results for equation d_U:
## ====================================
## d_U = d_le.l1 + d_lprod.l1 + d_lrw.l1 + d_U.l1 + d_le.l2 + d_lprod.l2 + d_lrw.l2 + d_U.l2 + const
##
## Estimate Std. Error t value Pr(>|t|)
## d_le.l1 -562.56339 112.51059 -5.000 3.9e-06 ***
## d_lprod.l1 -61.92467 20.26199 -3.056 0.00314 **
## d_lrw.l1 17.16277 15.79702 1.086 0.28090
## d_U.l1 -0.16069 0.15268 -1.053 0.29608
## d_le.l2 14.94288 121.33516 0.123 0.90233
## d_lprod.l2 -6.39511 20.76831 -0.308 0.75903
## d_lrw.l2 47.22018 15.39942 3.066 0.00305 **
## d_U.l2 -0.25913 0.15942 -1.625 0.10844
## const 0.08461 0.07151 1.183 0.24063
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
##
## Residual standard error: 0.2903 on 72 degrees of freedom
## Multiple R-Squared: 0.6063, Adjusted R-squared: 0.5626
## F-statistic: 13.86 on 8 and 72 DF, p-value: 5.711e-12
##
##
##
## Covariance matrix of residuals:
## d_le d_lprod d_lrw d_U
## d_le 1.541e-07 -4.322e-08 -3.850e-09 -7.773e-05
## d_lprod -4.322e-08 2.569e-06 3.696e-07 -1.687e-06
## d_lrw -3.850e-09 3.696e-07 4.237e-06 1.179e-04
## d_U -7.773e-05 -1.687e-06 1.179e-04 8.426e-02
##
## Correlation matrix of residuals:
## d_le d_lprod d_lrw d_U
## d_le 1.000000 -0.068693 -0.004765 -0.682158
## d_lprod -0.068693 1.000000 0.112010 -0.003627
## d_lrw -0.004765 0.112010 1.000000 0.197328
## d_U -0.682158 -0.003627 0.197328 1.000000
# Serial correlation (Portmanteau)
serial.test(var_mod, lags.pt = 12, type = "PT.asymptotic")
##
## Portmanteau Test (asymptotic)
##
## data: Residuals of VAR object var_mod
## Chi-squared = 125.43, df = 160, p-value = 0.98
# Normalitas residual (Jarque–Bera)
normality.test(var_mod)
## $JB
##
## JB-Test (multivariate)
##
## data: Residuals of VAR object var_mod
## Chi-squared = 11.508, df = 8, p-value = 0.1745
##
##
## $Skewness
##
## Skewness only (multivariate)
##
## data: Residuals of VAR object var_mod
## Chi-squared = 8.4598, df = 4, p-value = 0.07611
##
##
## $Kurtosis
##
## Kurtosis only (multivariate)
##
## data: Residuals of VAR object var_mod
## Chi-squared = 3.048, df = 4, p-value = 0.5498
# ARCH LM test
arch.test(var_mod, lags.multi = 6)
##
## ARCH (multivariate)
##
## data: Residuals of VAR object var_mod
## Chi-squared = 611.88, df = 600, p-value = 0.3595
# Stabilitas (akar di luar unit circle)
mod_roots <- roots(var_mod); mod_roots
## [1] 0.6670496 0.6670496 0.5478293 0.5478293 0.4376757 0.4376757 0.3886854
## [8] 0.3448158
plot(mod_roots, xlab="Real", ylab="Imaginary", main="Roots of the Companion Matrix (Unit Circle)")
abline(h=0, v=0, col="gray70")
Contoh: apakah d_lprod “Granger‑menyebabkan”
d_le?
causality(var_mod, cause = "d_lprod")
## $Granger
##
## Granger causality H0: d_lprod do not Granger-cause d_le d_lrw d_U
##
## data: VAR object var_mod
## F-Test = 3.4444, df1 = 6, df2 = 288, p-value = 0.002662
##
##
## $Instant
##
## H0: No instantaneous causality between: d_lprod and d_le d_lrw d_U
##
## data: VAR object var_mod
## Chi-squared = 2.1809, df = 3, p-value = 0.5357
Orde variabel memengaruhi hasil (Cholesky). Kita gunakan urutan (d_le, d_lprod, d_lrw, d_U).
irf_obj <- irf(var_mod, impulse = "d_lprod", response = c("d_le","d_lrw","d_U"),
n.ahead = 12, boot = TRUE, ci = 0.95, runs = 500)
plot(irf_obj)
fevd_obj <- fevd(var_mod, n.ahead = 12)
plot(fevd_obj)
fc <- predict(var_mod, n.ahead = 8, ci = 0.95)
# Plot untuk satu seri contoh
plot(fc, names = "d_le")
svars/vars::SVAR.VAR memodelkan interaksi dinamis multi‑variabel secara fleksibel. Estimasi mudah (OLS per persamaan), evaluasi pakai uji serial correlation/normalitas/ARCH, stabilitas via akar. Kegunaan utama: IRF, FEVD, peramalan. Saat ada kointegrasi, beralih ke VECM dan/atau SVAR untuk interpretasi struktural.
| Model | Fokus Analisis | Asumsi Kunci | Kegunaan Utama |
|---|---|---|---|
| OLS | Hubungan linier rata-rata | Eksogenitas, homoskedastisitas | Cross‑section & baseline |
| IV | Kausalitas dgn endogenitas | Instrumen relevan & valid | Causal inference |
| FE (Panel) | Kontrol heterogenitas tetap | Efek tetap konstan per unit | Panel data |
| DiD | Evaluasi kebijakan | Parallel trends | Eksperimen alami |
| Synthetic Control | Unit sintetik pembanding | Good pre‑treatment fit | Studi kebijakan per-unit |
| VAR | Dinamika multivariat | (Kira‑)stasioneritas | Makro/keuangan/DERET waktu |
Untuk komputasi dapat dicek di link: https://project25.shinyapps.io/ekonometrika/
Wooldridge, J. M. (2010). Econometric Analysis of Cross Section and Panel Data (2nd ed.). MIT Press.
Angrist, J. D., & Pischke, J.-S. (2009). Mostly Harmless Econometrics: An Empiricist’s Companion. Princeton University Press.
Abadie, A., Diamond, A., & Hainmueller, J. (2010). Synthetic control methods for comparative case studies: Estimating the effect of California’s tobacco control program. Journal of the American Statistical Association, 105(490), 493–505. https://doi.org/10.1198/jasa.2009.ap08746
Abadie, A. (2021). Using Synthetic Controls: Methods and Practice. Cambridge University Press.
Ben-Michael, E., Feller, A., & Rothstein, J. (2021). The augmented synthetic control method. Journal of the American Statistical Association, 116(536), 1789–1803. https://doi.org/10.1080/01621459.2021.1929245
Imai, K., & Kim, I. S. (2021). On the use of two-way fixed effects models for causal inference with heterogeneous treatment effects. American Journal of Political Science, 65(3), 849–866. https://doi.org/10.1111/ajps.12674
Callaway, B., & Sant’Anna, P. H. C. (2021). Difference-in-differences with multiple time periods. Journal of Econometrics, 225(2), 200–230. https://doi.org/10.1016/j.jeconom.2020.12.001
Sun, L., & Abraham, S. (2021). Estimating dynamic treatment effects in event studies with heterogeneous treatment effects. Journal of Econometrics, 225(2), 175–199. https://doi.org/10.1016/j.jeconom.2020.09.006
Lütkepohl, H. (2005). New Introduction to Multiple Time Series Analysis. Springer.