1 Pendahuluan

1.1 Gambaran Umum Analisis

Laporan ini menerapkan empat model regresi berdasarkan skala variabel respons: regresi logistik biner untuk respons dua kategori, regresi logistik multinomial untuk respons nominal lebih dari dua kategori, regresi logistik ordinal untuk respons berurutan, dan regresi Poisson untuk respons hitungan. Alur analisis mengikuti materi regresi kategorik dan Poisson: pemahaman data, estimasi model, uji simultan \(G^2\), uji Wald, pemilihan model terbaik, interpretasi variabel yang mendukung respons, dan prediksi menggunakan model terbaik.

1.2 Library yang Digunakan

Library berikut digunakan untuk membaca data, mengolah data, membangun model regresi, membuat visualisasi, dan menyajikan tabel pada dokumen HTML.

library(readxl)
library(dplyr)
library(tidyr)
library(ggplot2)
library(nnet)
library(MASS)
library(broom)
library(knitr)
library(kableExtra)
library(scales)
library(tibble)
library(purrr)
library(stringr)
library(pROC)

1.3 Catatan Khusus

Catatan 1 — Kriteria pemilihan model terbaik.
Kriteria pemilihan model terbaik dalam laporan ini mengutamakan jumlah variabel X yang signifikan terlebih dahulu, kemudian mempertimbangkan AIC terkecil. Artinya, apabila satu model memiliki lebih banyak variabel signifikan meskipun AIC-nya sedikit lebih besar, model tersebut tetap lebih diprioritaskan daripada model lain yang AIC-nya lebih kecil tetapi kehilangan banyak variabel signifikan.

Catatan 2 — Interpretasi hasil model.
Interpretasi koefisien, odds ratio, relative risk ratio, dan incidence rate ratio dalam laporan ini bersifat asosiasi statistik, bukan klaim sebab-akibat. Dengan demikian, variabel X yang signifikan dipahami sebagai variabel yang mendukung atau berkaitan dengan variasi variabel Y dalam model, bukan sebagai penyebab langsung.

2 Sumber Data

data_sources <- tibble(
  bagian=c("Regresi logistik biner","Regresi logistik multinomial","Regresi logistik ordinal","Regresi Poisson tahunan"),
  topik=c("Food Crisis","Environment Priority","Government Confidence","Violence Case Count"),
  sumber=c("World Bank - World Development Indicators","World Values Survey Wave 7","World Values Survey Wave 7","UCDP Georeferenced Event Dataset Global"),
  tautan=c("https://databank.worldbank.org/source/world-development-indicators","https://www.worldvaluessurvey.org/WVSDocumentationWV7.jsp","https://www.worldvaluessurvey.org/WVSDocumentationWV7.jsp","https://ucdp.uu.se/downloads/#ged_global"),
  file_model_ready=c("wdi_binary_logit_model_ready.xlsx","multinomial_model_ready_sample.xlsx","ordinal_model_ready_sample.xlsx","poisson_model_ready.xlsx")
)
safe_table(data_sources, "Sumber data dan file model-ready yang digunakan", full_width=TRUE)
Tabel 1. Sumber data dan file model-ready yang digunakan
bagian topik sumber tautan file_model_ready
Regresi logistik biner Food Crisis World Bank - World Development Indicators https://databank.worldbank.org/source/world-development-indicators wdi_binary_logit_model_ready.xlsx
Regresi logistik multinomial Environment Priority World Values Survey Wave 7 https://www.worldvaluessurvey.org/WVSDocumentationWV7.jsp multinomial_model_ready_sample.xlsx
Regresi logistik ordinal Government Confidence World Values Survey Wave 7 https://www.worldvaluessurvey.org/WVSDocumentationWV7.jsp ordinal_model_ready_sample.xlsx
Regresi Poisson tahunan Violence Case Count UCDP Georeferenced Event Dataset Global https://ucdp.uu.se/downloads/#ged_global poisson_model_ready.xlsx

3 Bagian I — Regresi Logistik Biner

3.1 Konsep model

Regresi logistik biner digunakan ketika respons hanya memiliki dua kemungkinan hasil. Model logit menghubungkan prediktor dengan log odds kejadian:

\[ \log\left(\frac{p_i}{1-p_i}\right)=\beta_0+\beta_1X_{1i}+\cdots+\beta_kX_{ki} \]

Uji simultan model dilakukan dengan statistik likelihood ratio:

\[ G^2=-2(\ell_0-\ell_1)=2(\ell_1-\ell_0) \]

Jika p-value dari \(G^2\) kecil, model penuh lebih baik daripada model kosong.

3.2 Import, struktur data, dan distribusi respons

binary_data <- readxl::read_excel(resolve_path("wdi_binary_logit_model_ready.xlsx")) |>
  clean_model_data() |>
  mutate(food_crisis=as.integer(food_crisis), food_crisis_label=factor(food_crisis, levels=c(0,1), labels=c("non_food_crisis","food_crisis")))

binary_dict <- make_dictionary(binary_data)
binary_summary <- binary_data |> count(food_crisis, food_crisis_label, name="n") |> mutate(proporsi=n/sum(n))

safe_table(binary_dict, "Kamus data biner setelah seleksi variabel analisis")
Tabel 2. Kamus data biner setelah seleksi variabel analisis
variabel tipe missing unique_value
food_crisis integer 0 2
year numeric 0 14
log_gdp_per_capita numeric 0 1988
inflation numeric 0 1988
unemployment numeric 0 1844
urban_population numeric 0 1974
population_growth numeric 0 1988
health_expenditure numeric 0 1988
food_crisis_label factor 0 2
safe_table(binary_summary, "Distribusi respons biner food_crisis", digits=3)
Tabel 3. Distribusi respons biner food_crisis
food_crisis food_crisis_label n proporsi
0 non_food_crisis 1336 0.672
1 food_crisis 652 0.328
binary_data |> count(food_crisis_label) |>
  ggplot(aes(x=food_crisis_label, y=n, fill=food_crisis_label))+
  geom_col(width=.68)+geom_text(aes(label=n), vjust=-.45, fontface="bold")+
  scale_fill_manual(values=binary_palette)+
  labs(title="Distribusi Respons Food Crisis", subtitle="Respons biner: 0 = non-food crisis dan 1 = food crisis.", x="Kategori respons", y="Jumlah observasi", fill="Kategori")+
  theme_report()
Gambar 1. Distribusi kategori respons food_crisis.

Gambar 1. Distribusi kategori respons food_crisis.

Variabel food_crisis sudah sesuai untuk regresi logistik biner karena hanya memiliki dua kategori. Distribusi kelas dibaca sebelum pemodelan karena ketidakseimbangan kelas dapat membuat accuracy terlihat baik meskipun model kurang sensitif dalam mengenali kelas kejadian.

3.3 Statistik Deskriptif dan Eksplorasi Data

binary_descriptive <- numeric_descriptive(binary_data, response="food_crisis")
binary_group_summary <- binary_data |>
  select(food_crisis_label, where(is.numeric)) |>
  select(-food_crisis) |>
  group_by(food_crisis_label) |>
  summarise(across(everything(), ~mean(.x, na.rm=TRUE)), .groups="drop") |>
  pivot_longer(-food_crisis_label, names_to="variabel", values_to="mean") |>
  pivot_wider(names_from=food_crisis_label, values_from=mean)

safe_table(binary_descriptive, "Statistik deskriptif prediktor numerik pada data logistik biner", digits=3, full_width=TRUE)
Tabel 4. Statistik deskriptif prediktor numerik pada data logistik biner
variabel n missing mean sd min q1 median q3 max
year 1988 0 2016.500 4.032 2010.000 2013.000 2016.500 2020.000 2023.000
log_gdp_per_capita 1988 0 8.705 1.410 5.801 7.622 8.671 9.793 11.813
inflation 1988 0 5.215 9.431 -12.563 1.500 3.337 6.409 221.342
unemployment 1988 0 7.698 5.822 0.119 3.686 5.651 10.183 34.007
urban_population 1988 0 59.327 21.275 13.000 43.269 61.541 76.150 100.000
population_growth 1988 0 1.323 1.421 -3.218 0.358 1.282 2.275 14.225
health_expenditure 1988 0 6.409 2.675 1.752 4.343 6.108 8.224 23.088
safe_table(binary_group_summary, "Rata-rata prediktor menurut status food_crisis", digits=3, full_width=TRUE)
Tabel 5. Rata-rata prediktor menurut status food_crisis
variabel non_food_crisis food_crisis
year 2016.527 2016.445
log_gdp_per_capita 9.353 7.376
inflation 4.490 6.702
unemployment 7.799 7.490
urban_population 66.479 44.673
population_growth 0.877 2.237
health_expenditure 6.949 5.303
binary_plot_vars <- binary_x <- model_predictors(binary_data, response="food_crisis")
binary_plot_vars <- binary_plot_vars[seq_len(min(4, length(binary_plot_vars)))]
binary_data |>
  select(food_crisis_label, all_of(binary_plot_vars)) |>
  pivot_longer(-food_crisis_label, names_to="variabel", values_to="nilai") |>
  ggplot(aes(x=food_crisis_label, y=nilai, fill=food_crisis_label))+
  geom_boxplot(alpha=.75, outlier.alpha=.25)+
  facet_wrap(~variabel, scales="free_y")+
  scale_fill_manual(values=binary_palette)+
  labs(title="Eksplorasi Prediktor terhadap Status Food Crisis", x="Status food crisis", y="Nilai prediktor", fill="Kategori")+
  theme_report()
Gambar 2. Eksplorasi prediktor numerik utama menurut status food_crisis.

Gambar 2. Eksplorasi prediktor numerik utama menurut status food_crisis.

3.4 Pembagian data training dan testing

binary_split <- split_train_test(binary_data, strata="food_crisis", prop=.80)
binary_train <- binary_split$train; binary_test <- binary_split$test
binary_split_table <- bind_rows(binary_train |> count(food_crisis_label, name="n") |> mutate(subset="Training"), binary_test |> count(food_crisis_label, name="n") |> mutate(subset="Testing")) |> group_by(subset) |> mutate(proporsi=n/sum(n)) |> ungroup() |> select(subset, food_crisis_label, n, proporsi)
safe_table(binary_split_table, "Komposisi kelas food_crisis pada data training dan testing", digits=3)
Tabel 6. Komposisi kelas food_crisis pada data training dan testing
subset food_crisis_label n proporsi
Training non_food_crisis 1068 0.672
Training food_crisis 521 0.328
Testing non_food_crisis 268 0.672
Testing food_crisis 131 0.328

3.5 Kandidat model dan pemilihan model terbaik

binary_x <- model_predictors(binary_train, response="food_crisis")
binary_selection <- select_best_glm(binary_train, response="food_crisis", predictors=binary_x, family=binomial(link="logit"), effect_name="odds_ratio")
binary_best_fit <- binary_selection$best$fit
binary_best_null <- glm(food_crisis ~ 1, data=binary_train, family=binomial(link="logit"))
binary_best_wald <- binary_selection$best$wald
binary_best_g2 <- binary_selection$best$g2
binary_selected_vars <- binary_best_wald |> filter(p_value < 0.05) |> pull(variabel)

binary_model_selection_table <- binary_selection$summary |>
  mutate(p_value_G2 = p_value_G2_tampil) |>
  select(model_ke, formula, jumlah_prediktor, jumlah_variabel_signifikan, AIC, G2, p_value_G2)

binary_fit_table <- tibble(keterangan=c("Jumlah observasi training","Jumlah prediktor model terbaik","AIC model terbaik","Log-likelihood model terbaik","Log-likelihood model kosong","McFadden pseudo R-square"), nilai=c(nrow(binary_train), length(binary_selection$best$vars), AIC(binary_best_fit), as.numeric(logLik(binary_best_fit)), as.numeric(logLik(binary_best_null)), mcfadden_r2(binary_best_fit,binary_best_null)))

binary_best_wald_show <- binary_best_wald |> mutate(p_value=p_value_tampil) |> select(-p_value_tampil)
binary_best_g2_show <- binary_best_g2 |> mutate(p_value=p_value_tampil) |> select(-p_value_tampil)

safe_table(binary_model_selection_table, "Perbandingan kandidat model logistik biner berdasarkan jumlah variabel signifikan dan AIC", digits=4, full_width=TRUE)
Tabel 7. Perbandingan kandidat model logistik biner berdasarkan jumlah variabel signifikan dan AIC
model_ke formula jumlah_prediktor jumlah_variabel_signifikan AIC G2 p_value_G2
4 food_crisis ~ year + log_gdp_per_capita + unemployment + population_growth 4 4 997.2477 1023.366 < 0.001
3 food_crisis ~ year + log_gdp_per_capita + unemployment + urban_population + population_growth 5 4 997.8557 1024.758 < 0.001
2 food_crisis ~ year + log_gdp_per_capita + unemployment + urban_population + population_growth + health_expenditure 6 4 999.1942 1025.420 < 0.001
1 food_crisis ~ year + log_gdp_per_capita + inflation + unemployment + urban_population + population_growth + health_expenditure 7 4 1000.9461 1025.668 < 0.001
safe_table(binary_fit_table, "Ringkasan kecocokan model terbaik logistik biner", digits=4)
Tabel 8. Ringkasan kecocokan model terbaik logistik biner
keterangan nilai
Jumlah observasi training 1589.0000
Jumlah prediktor model terbaik 4.0000
AIC model terbaik 997.2477
Log-likelihood model terbaik -493.6239
Log-likelihood model kosong -1005.3071
McFadden pseudo R-square 0.5090
safe_table(binary_best_g2_show, "Uji simultan G² model terbaik logistik biner", digits=4)
Tabel 9. Uji simultan G² model terbaik logistik biner
uji G2 df p_value keputusan penjelasan_hasil penanganan_jika_tidak_memenuhi
Uji likelihood ratio 1023.366 4 < 0.001 Tolak H0 Model dengan prediktor memberikan peningkatan kecocokan dibanding model kosong. Tidak diperlukan penanganan khusus untuk LRT.
safe_table(binary_best_wald_show, "Uji Wald parsial dan odds ratio model terbaik logistik biner", digits=4, full_width=TRUE)
Tabel 10. Uji Wald parsial dan odds ratio model terbaik logistik biner
variabel estimate std_error z_value p_value odds_ratio ci_95
log_gdp_per_capita -1.8753 0.1107 -16.9340 < 0.001 0.1533 0.123 - 0.19
population_growth 0.4655 0.0629 7.4014 < 0.001 1.5928 1.408 - 1.802
unemployment 0.0902 0.0129 7.0088 < 0.001 1.0944 1.067 - 1.122
year 0.0532 0.0202 2.6369 0.008 1.0547 1.014 - 1.097

Model terbaik logistik biner adalah kandidat ke-4 karena memiliki 4 variabel signifikan dan AIC sebesar 997.248. Uji simultan \(G^2\) menghasilkan p-value < 0.001, sehingga model terbaik memberikan peningkatan kecocokan dibanding model kosong. Berdasarkan uji Wald, variabel X yang signifikan dan mendukung food_crisis adalah: log_gdp_per_capita, population_growth, unemployment, year. Dengan demikian, prediksi selanjutnya dilakukan menggunakan model terbaik tersebut, bukan model penuh awal.

3.6 Uji Asumsi Model Terbaik

3.6.1 Multikolinearitas — VIF

Hipotesis/Kriteria:
\(H_0\): Tidak terdapat multikolinearitas berat antarprediktor.
\(H_1\): Terdapat multikolinearitas berat antarprediktor.
Kriteria praktis: VIF < 5 menunjukkan tidak ada indikasi multikolinearitas berat; VIF 5–10 perlu perhatian; VIF > 10 mengindikasikan multikolinearitas berat.

binary_vif_table <- vif_table(binary_train, binary_selection$best$vars)
safe_table(binary_vif_table, "Uji multikolinearitas model terbaik logistik biner menggunakan VIF", digits=3, full_width=TRUE)
Tabel 11. Uji multikolinearitas model terbaik logistik biner menggunakan VIF
variabel VIF keterangan penjelasan_hasil penanganan_jika_tidak_memenuhi
population_growth 1.214 Tidak ada indikasi multikolinearitas berat Asumsi tidak ada multikolinearitas berat relatif terpenuhi. Tidak diperlukan penanganan khusus.
log_gdp_per_capita 1.154 Tidak ada indikasi multikolinearitas berat Asumsi tidak ada multikolinearitas berat relatif terpenuhi. Tidak diperlukan penanganan khusus.
unemployment 1.059 Tidak ada indikasi multikolinearitas berat Asumsi tidak ada multikolinearitas berat relatif terpenuhi. Tidak diperlukan penanganan khusus.
year 1.016 Tidak ada indikasi multikolinearitas berat Asumsi tidak ada multikolinearitas berat relatif terpenuhi. Tidak diperlukan penanganan khusus.

3.6.2 Likelihood Ratio Test (LRT)

Hipotesis:
\(H_0\): Semua koefisien prediktor sama dengan nol, sehingga model dengan prediktor tidak lebih baik daripada model kosong.
\(H_1\): Minimal ada satu koefisien prediktor tidak sama dengan nol, sehingga model dengan prediktor lebih baik daripada model kosong.

binary_lrt_show <- binary_best_g2 |> mutate(p_value=p_value_tampil) |> select(-p_value_tampil)
safe_table(binary_lrt_show, "Likelihood Ratio Test atau uji G² model terbaik logistik biner", digits=4, full_width=TRUE)
Tabel 12. Likelihood Ratio Test atau uji G² model terbaik logistik biner
uji G2 df p_value keputusan penjelasan_hasil penanganan_jika_tidak_memenuhi
Uji likelihood ratio 1023.366 4 < 0.001 Tolak H0 Model dengan prediktor memberikan peningkatan kecocokan dibanding model kosong. Tidak diperlukan penanganan khusus untuk LRT.

3.6.3 Hosmer-Lemeshow Goodness-of-Fit

Hipotesis:
\(H_0\): Model memiliki kecocokan yang baik; tidak ada perbedaan bermakna antara frekuensi aktual dan frekuensi harapan.
\(H_1\): Model tidak memiliki kecocokan yang baik; terdapat perbedaan bermakna antara frekuensi aktual dan frekuensi harapan.

binary_train_prob <- predict(binary_best_fit, newdata=binary_train, type="response")
binary_hl <- hosmer_lemeshow_table(actual=binary_train$food_crisis, predicted=binary_train_prob, g=10)
binary_hl_test <- binary_hl$test |> mutate(p_value=p_value_tampil) |> select(-p_value_tampil)
safe_table(binary_hl$detail, "Detail pengelompokan Hosmer-Lemeshow model terbaik logistik biner", digits=3, full_width=TRUE)
Tabel 13. Detail pengelompokan Hosmer-Lemeshow model terbaik logistik biner
group observed expected n non_observed non_expected
1 0 0.261 159 159 158.739
2 1 0.864 159 158 158.136
3 2 2.427 159 157 156.573
4 1 6.699 159 158 152.301
5 10 17.580 159 149 141.420
6 48 36.536 159 111 122.464
7 80 66.758 159 79 92.242
8 111 106.143 159 48 52.857
9 116 134.646 159 43 24.354
10 152 149.086 158 6 8.914
safe_table(binary_hl_test, "Uji Hosmer-Lemeshow Goodness-of-Fit model terbaik logistik biner", digits=4, full_width=TRUE)
Tabel 14. Uji Hosmer-Lemeshow Goodness-of-Fit model terbaik logistik biner
uji statistik df p_value keputusan penjelasan_hasil penanganan_jika_tidak_memenuhi
Hosmer-Lemeshow Goodness-of-Fit 36.8279 8 < 0.001 Ada indikasi lack of fit H0 ditolak; terdapat perbedaan bermakna antara frekuensi aktual dan harapan. Evaluasi spesifikasi model, tambahkan transformasi/nonlinear term, pertimbangkan interaksi, atau cek observasi ekstrem.

3.6.4 Linearitas Log-Odds (Box-Tidwell)

Hipotesis:
\(H_0\): Hubungan prediktor numerik dengan log-odds bersifat linear.
\(H_1\): Hubungan prediktor numerik dengan log-odds tidak linear.

binary_box_tidwell <- box_tidwell_table(binary_train, response="food_crisis", predictors=binary_selection$best$vars, family=binomial(link="logit")) |>
  mutate(p_value=p_value_tampil) |>
  select(-p_value_tampil)
safe_table(binary_box_tidwell, "Uji linearitas log-odds Box-Tidwell model terbaik logistik biner", digits=4, full_width=TRUE)
Tabel 15. Uji linearitas log-odds Box-Tidwell model terbaik logistik biner
variabel estimate p_value keterangan penjelasan_hasil penanganan_jika_tidak_memenuhi
year 53.3512 0.022 Ada indikasi non-linearitas logit H0 ditolak; hubungan prediktor dengan logit tidak linear. Gunakan transformasi prediktor, polynomial, spline, atau kategorisasi substantif.
log_gdp_per_capita -0.2924 0.829 Tidak ada bukti kuat non-linearitas logit H0 gagal ditolak; hubungan prediktor dengan logit relatif linear. Tidak diperlukan penanganan khusus.
unemployment 0.0666 0.060 Tidak ada bukti kuat non-linearitas logit H0 gagal ditolak; hubungan prediktor dengan logit relatif linear. Tidak diperlukan penanganan khusus.
population_growth -2.7188 < 0.001 Ada indikasi non-linearitas logit H0 ditolak; hubungan prediktor dengan logit tidak linear. Gunakan transformasi prediktor, polynomial, spline, atau kategorisasi substantif.

3.6.5 Nagelkerke \(R^2\)

Nagelkerke \(R^2\) bukan uji hipotesis, sehingga tidak memiliki \(H_0\) dan \(H_1\). Ukuran ini digunakan sebagai indeks kekuatan penjelasan model. Nilai yang lebih besar menunjukkan model memberikan peningkatan kecocokan yang lebih baik dibanding model kosong.

binary_nagelkerke_table <- tibble(
  ukuran="Nagelkerke R-square",
  nilai=nagelkerke_r2(binary_best_fit, binary_best_null),
  keterangan="Semakin besar nilainya, semakin besar peningkatan kecocokan model dibanding model kosong."
)
safe_table(binary_nagelkerke_table, "Nagelkerke R-square model terbaik logistik biner", digits=4, full_width=TRUE)
Tabel 16. Nagelkerke R-square model terbaik logistik biner
ukuran nilai keterangan
Nagelkerke R-square 0.6615 Semakin besar nilainya, semakin besar peningkatan kecocokan model dibanding model kosong.

3.6.6 Diagnostik Pengaruh Observasi

Diagnostik pengaruh observasi bukan uji hipotesis formal. Pemeriksaan ini digunakan untuk melihat apakah ada observasi yang terlalu memengaruhi estimasi model. Kriteria praktis: Cook’s distance > 1 perlu diperiksa, dan leverage tinggi dilihat dengan batas \(2p/n\).

binary_influence_table <- influence_check_table(binary_best_fit)
safe_table(binary_influence_table, "Diagnostik pengaruh observasi model terbaik logistik biner", digits=4, full_width=TRUE)
Tabel 17. Diagnostik pengaruh observasi model terbaik logistik biner
diagnostik nilai keterangan penjelasan_hasil penanganan_jika_tidak_memenuhi
Maximum Cook’s distance 0.1079 Nilai Cook’s distance yang besar menunjukkan observasi yang berpengaruh kuat. Semakin kecil nilai maksimum Cook’s distance, semakin kecil pengaruh observasi ekstrem terhadap model. Periksa observasi dengan nilai Cook’s distance terbesar.
Jumlah Cook’s distance > 1 0.0000 Jumlah observasi dengan Cook’s distance > 1 perlu diperiksa lebih lanjut. Tidak ada observasi dengan Cook’s distance > 1. Cek data mentah, validasi outlier, dan lakukan analisis sensitivitas.
Maximum leverage 0.1408 Leverage tinggi menunjukkan observasi dengan kombinasi prediktor yang ekstrem. Nilai leverage maksimum menunjukkan observasi dengan kombinasi prediktor paling ekstrem. Periksa observasi dengan leverage tinggi.
Jumlah leverage > 2p/n 150.0000 Aturan praktis leverage tinggi menggunakan batas 2p/n.  Ada observasi dengan leverage tinggi. Jalankan analisis sensitivitas dengan dan tanpa observasi berpengaruh.

3.7 Prediksi dengan model terbaik

binary_prob <- predict(binary_best_fit, newdata=binary_test, type="response")
binary_pred <- ifelse(binary_prob >= .50, 1, 0)
binary_cm <- table(Aktual=binary_test$food_crisis, Prediksi=binary_pred)
binary_metric_table <- binary_metrics(binary_test$food_crisis, binary_pred)
binary_cm_table <- cm_labeled(binary_cm)
names(binary_cm_table) <- c("Aktual \\ Prediksi", "Prediksi: non_food_crisis (0)", "Prediksi: food_crisis (1)")
binary_cm_table[["Aktual \\ Prediksi"]] <- c("Aktual: non_food_crisis (0)", "Aktual: food_crisis (1)")
safe_table(binary_cm_table, "Confusion matrix logistik biner model terbaik dengan keterangan aktual dan prediksi")
Tabel 18. Confusion matrix logistik biner model terbaik dengan keterangan aktual dan prediksi
Aktual  Prediksi Prediksi: non_food_crisis (0) Prediksi: food_crisis (1)
Aktual: non_food_crisis (0) 241 27
Aktual: food_crisis (1) 34 97
safe_table(binary_metric_table, "Metrik evaluasi testing model terbaik logistik biner pada threshold 0,50", digits=3)
Tabel 19. Metrik evaluasi testing model terbaik logistik biner pada threshold 0,50
threshold accuracy error_rate sensitivity specificity precision f1_score balanced_accuracy
0.5 0.847 0.153 0.74 0.899 0.782 0.761 0.82
binary_roc <- pROC::roc(response=binary_test$food_crisis, predictor=binary_prob, quiet=TRUE)
binary_auc <- as.numeric(pROC::auc(binary_roc))
roc_df <- tibble(specificity=binary_roc$specificities, sensitivity=binary_roc$sensitivities) |> mutate(false_positive_rate=1-specificity)
roc_df |> ggplot(aes(x=false_positive_rate,y=sensitivity))+geom_line(linewidth=1.15,color="#6d28d9")+geom_abline(slope=1,intercept=0,linetype="dashed",color="#e11d48")+coord_equal()+labs(title="Kurva ROC Model Terbaik Logistik Biner", subtitle=paste0("AUC = ",round(binary_auc,3)), x="False positive rate", y="True positive rate / sensitivity")+theme_report()
Gambar 2. Kurva ROC model terbaik logistik biner.

Gambar 2. Kurva ROC model terbaik logistik biner.

Pada data testing, model terbaik menghasilkan accuracy sebesar 0.847, sensitivity sebesar 0.74, specificity sebesar 0.899, dan AUC sebesar 0.921. Sensitivity penting karena menunjukkan kemampuan model mengenali observasi yang benar-benar mengalami food_crisis.

3.8 Korelasi prediktor

binary_corr_top <- correlation_pairs(binary_train |> select(all_of(binary_x)), top_n=10)
binary_corr_high <- correlation_pairs(binary_train |> select(all_of(binary_x)), top_n=100, threshold=.80)
safe_table(binary_corr_top, "Sepuluh pasangan prediktor biner dengan korelasi absolut terbesar", digits=3)
Tabel 20. Sepuluh pasangan prediktor biner dengan korelasi absolut terbesar
variabel_1 variabel_2 korelasi abs_korelasi
log_gdp_per_capita urban_population 0.752 0.752
log_gdp_per_capita health_expenditure 0.448 0.448
urban_population health_expenditure 0.365 0.365
log_gdp_per_capita population_growth -0.363 0.363
population_growth health_expenditure -0.322 0.322
unemployment population_growth -0.222 0.222
urban_population population_growth -0.214 0.214
unemployment urban_population 0.201 0.201
log_gdp_per_capita inflation -0.174 0.174
year inflation 0.132 0.132
safe_table(binary_corr_high, "Pasangan prediktor biner dengan korelasi tinggi atau |r| >= 0,80", digits=3)
Tabel 21. Pasangan prediktor biner dengan korelasi tinggi atau |r| >= 0,80
variabel_1 variabel_2 korelasi abs_korelasi

4 Bagian II — Regresi Logistik Multinomial

4.1 Konsep model

Regresi logistik multinomial digunakan ketika respons bersifat nominal dan memiliki lebih dari dua kategori. Dengan kategori referensi \(Y=1\), model untuk kategori \(j\) adalah:

\[ \log\left(\frac{P(Y_i=j)}{P(Y_i=1)}\right)=\alpha_j+\beta_{j1}X_{1i}+\cdots+\beta_{jk}X_{ki} \]

Ukuran efek yang digunakan adalah relative risk ratio (RRR), yaitu \(\exp(\beta)\).

4.2 Import, struktur data, dan distribusi respons

multinom_data <- readxl::read_excel(resolve_path("multinomial_model_ready_sample.xlsx")) |>
  clean_model_data() |>
  mutate(choice_code=factor(choice_code, levels=sort(unique(choice_code)), labels=c("protect_environment","economic_growth_jobs","other_answer")[seq_along(sort(unique(choice_code)))]))
multinom_dict <- make_dictionary(multinom_data)
multinom_summary <- multinom_data |> count(choice_code, name="n") |> mutate(proporsi=n/sum(n))
safe_table(multinom_dict, "Kamus data multinomial setelah seleksi variabel analisis")
Tabel 22. Kamus data multinomial setelah seleksi variabel analisis
variabel tipe missing unique_value
choice_code factor 0 3
age numeric 0 76
education_code numeric 0 9
income_scale numeric 0 10
gender_label_male numeric 0 2
employment_label_housewife_or_not_working numeric 0 2
employment_label_other numeric 0 2
employment_label_part_time_employee numeric 0 2
employment_label_retired numeric 0 2
employment_label_self_employed numeric 0 2
employment_label_student numeric 0 2
employment_label_unemployed numeric 0 2
safe_table(multinom_summary, "Distribusi respons multinomial choice_code", digits=3)
Tabel 23. Distribusi respons multinomial choice_code
choice_code n proporsi
protect_environment 4893 0.572
economic_growth_jobs 3403 0.398
other_answer 262 0.031
multinom_summary |> ggplot(aes(x=reorder(choice_code,n), y=n, fill=choice_code))+geom_col(width=.70)+geom_text(aes(label=n), hjust=-.15, fontface="bold")+coord_flip()+scale_fill_manual(values=report_palette)+labs(title="Distribusi Environment Priority", subtitle="Respons multinomial dengan kategori nominal.", x="Kategori respons", y="Jumlah observasi", fill="Kategori")+theme_report()
Gambar 3. Distribusi kategori respons multinomial.

Gambar 3. Distribusi kategori respons multinomial.

Respons choice_code bersifat nominal, sehingga tidak tepat diperlakukan sebagai urutan rendah-tinggi. Ketimpangan jumlah observasi antarkategori juga perlu dibaca karena kategori dengan frekuensi kecil dapat lebih sulit diprediksi secara stabil.

4.3 Statistik Deskriptif dan Eksplorasi Data

multinom_descriptive <- numeric_descriptive(multinom_data, response="choice_code")
multinom_x_preview <- model_predictors(multinom_data, response="choice_code")
multinom_group_summary <- multinom_data |>
  select(choice_code, all_of(multinom_x_preview)) |>
  group_by(choice_code) |>
  summarise(across(everything(), ~mean(.x, na.rm=TRUE)), .groups="drop") |>
  pivot_longer(-choice_code, names_to="variabel", values_to="mean") |>
  pivot_wider(names_from=choice_code, values_from=mean)

safe_table(multinom_descriptive, "Statistik deskriptif prediktor numerik pada data multinomial", digits=3, full_width=TRUE)
Tabel 24. Statistik deskriptif prediktor numerik pada data multinomial
variabel n missing mean sd min q1 median q3 max
age 8558 0 42.897 16.192 16 30 41 55 91
education_code 8558 0 3.599 2.026 0 2 3 6 8
income_scale 8558 0 4.923 2.089 1 4 5 6 10
gender_label_male 8558 0 0.479 0.500 0 0 0 1 1
employment_label_housewife_or_not_working 8558 0 0.127 0.332 0 0 0 0 1
employment_label_other 8558 0 0.013 0.112 0 0 0 0 1
employment_label_part_time_employee 8558 0 0.082 0.275 0 0 0 0 1
employment_label_retired 8558 0 0.122 0.327 0 0 0 0 1
employment_label_self_employed 8558 0 0.146 0.353 0 0 0 0 1
employment_label_student 8558 0 0.057 0.231 0 0 0 0 1
employment_label_unemployed 8558 0 0.080 0.271 0 0 0 0 1
safe_table(multinom_group_summary, "Rata-rata prediktor menurut kategori choice_code", digits=3, full_width=TRUE)
Tabel 25. Rata-rata prediktor menurut kategori choice_code
variabel protect_environment economic_growth_jobs other_answer
age 42.518 43.480 42.408
education_code 3.733 3.370 4.069
income_scale 4.949 4.882 4.981
gender_label_male 0.472 0.490 0.485
employment_label_housewife_or_not_working 0.122 0.135 0.099
employment_label_other 0.012 0.012 0.027
employment_label_part_time_employee 0.085 0.079 0.088
employment_label_retired 0.119 0.128 0.115
employment_label_self_employed 0.144 0.150 0.130
employment_label_student 0.064 0.046 0.069
employment_label_unemployed 0.077 0.084 0.088
multinom_plot_vars <- multinom_x_preview[seq_len(min(4, length(multinom_x_preview)))]
multinom_data |>
  select(choice_code, all_of(multinom_plot_vars)) |>
  pivot_longer(-choice_code, names_to="variabel", values_to="nilai") |>
  ggplot(aes(x=choice_code, y=nilai, fill=choice_code))+
  geom_boxplot(alpha=.75, outlier.alpha=.25)+
  facet_wrap(~variabel, scales="free_y")+
  scale_fill_manual(values=report_palette)+
  labs(title="Eksplorasi Prediktor terhadap Environment Priority", x="Kategori respons", y="Nilai prediktor", fill="Kategori")+
  theme_report()
Gambar 4. Eksplorasi prediktor numerik utama menurut kategori choice_code.

Gambar 4. Eksplorasi prediktor numerik utama menurut kategori choice_code.

4.4 Pembagian data training dan testing

multinom_split <- split_train_test(multinom_data, strata="choice_code", prop=.80)
multinom_train <- multinom_split$train; multinom_test <- multinom_split$test
multinom_split_table <- bind_rows(multinom_train |> count(choice_code, name="n") |> mutate(subset="Training"), multinom_test |> count(choice_code, name="n") |> mutate(subset="Testing")) |> group_by(subset) |> mutate(proporsi=n/sum(n)) |> ungroup() |> select(subset, choice_code, n, proporsi)
safe_table(multinom_split_table, "Komposisi respons multinomial pada data training dan testing", digits=3)
Tabel 26. Komposisi respons multinomial pada data training dan testing
subset choice_code n proporsi
Training protect_environment 3914 0.572
Training economic_growth_jobs 2722 0.398
Training other_answer 209 0.031
Testing protect_environment 979 0.572
Testing economic_growth_jobs 681 0.398
Testing other_answer 53 0.031

4.5 Kandidat model dan pemilihan model terbaik

multinom_x <- model_predictors(multinom_train, response="choice_code")
multinom_selection <- select_best_multinom(multinom_train, response="choice_code", predictors=multinom_x)
multinom_best_fit <- multinom_selection$best$fit
multinom_best_null <- nnet::multinom(choice_code ~ 1, data=multinom_train, trace=FALSE, Hess=TRUE, model=TRUE)
multinom_best_wald <- multinom_selection$best$wald
multinom_best_g2 <- multinom_selection$best$g2
multinom_selected_vars <- multinom_best_wald |> group_by(variabel) |> summarise(min_p=min(p_value, na.rm=TRUE), .groups="drop") |> filter(min_p < 0.05) |> pull(variabel)

multinom_model_selection_table <- multinom_selection$summary |>
  mutate(p_value_G2=p_value_G2_tampil) |>
  select(model_ke, formula, jumlah_prediktor, jumlah_variabel_signifikan, AIC, G2, p_value_G2)

multinom_fit_table <- tibble(keterangan=c("Jumlah observasi training","Jumlah prediktor model terbaik","AIC model terbaik","Log-likelihood model terbaik","Log-likelihood model kosong","McFadden pseudo R-square"), nilai=c(nrow(multinom_train), length(multinom_selection$best$vars), AIC(multinom_best_fit), as.numeric(logLik(multinom_best_fit)), as.numeric(logLik(multinom_best_null)), mcfadden_r2(multinom_best_fit,multinom_best_null)))

multinom_best_wald_show <- multinom_best_wald |> mutate(p_value=p_value_tampil) |> select(-p_value_tampil)
multinom_best_g2_show <- multinom_best_g2 |> mutate(p_value=p_value_tampil) |> select(-p_value_tampil)

safe_table(multinom_model_selection_table, "Perbandingan kandidat model multinomial berdasarkan jumlah variabel signifikan dan AIC", digits=4, full_width=TRUE)
Tabel 27. Perbandingan kandidat model multinomial berdasarkan jumlah variabel signifikan dan AIC
model_ke formula jumlah_prediktor jumlah_variabel_signifikan AIC G2 p_value_G2
9 choice_code ~ education_code + employment_label_other + employment_label_student 3 3 10793.68 76.4217 < 0.001
8 choice_code ~ education_code + gender_label_male + employment_label_other + employment_label_student 4 3 10793.70 80.3946 < 0.001
7 choice_code ~ education_code + gender_label_male + employment_label_other + employment_label_part_time_employee + employment_label_student 5 3 10794.93 83.1670 < 0.001
6 choice_code ~ education_code + income_scale + gender_label_male + employment_label_other + employment_label_part_time_employee + employment_label_student 6 3 10796.70 85.4010 < 0.001
5 choice_code ~ education_code + income_scale + gender_label_male + employment_label_other + employment_label_part_time_employee + employment_label_self_employed + employment_label_student 7 3 10798.89 87.2084 < 0.001
4 choice_code ~ age + education_code + income_scale + gender_label_male + employment_label_other + employment_label_part_time_employee + employment_label_self_employed + employment_label_student 8 3 10801.60 88.5022 < 0.001
3 choice_code ~ age + education_code + income_scale + gender_label_male + employment_label_other + employment_label_part_time_employee + employment_label_self_employed + employment_label_student + employment_label_unemployed 9 3 10804.77 89.3259 < 0.001
2 choice_code ~ age + education_code + income_scale + gender_label_male + employment_label_housewife_or_not_working + employment_label_other + employment_label_part_time_employee + employment_label_self_employed + employment_label_student + employment_label_unemployed 10 3 10808.33 89.7669 < 0.001
1 choice_code ~ age + education_code + income_scale + gender_label_male + employment_label_housewife_or_not_working + employment_label_other + employment_label_part_time_employee + employment_label_retired + employment_label_self_employed + employment_label_student + employment_label_unemployed 11 3 10812.31 89.7834 < 0.001
safe_table(multinom_fit_table, "Ringkasan kecocokan model terbaik multinomial", digits=4)
Tabel 28. Ringkasan kecocokan model terbaik multinomial
keterangan nilai
Jumlah observasi training 6845.000
Jumlah prediktor model terbaik 3.000
AIC model terbaik 10793.676
Log-likelihood model terbaik -5388.838
Log-likelihood model kosong -5427.049
McFadden pseudo R-square 0.007
safe_table(multinom_best_g2_show, "Uji simultan G² model terbaik multinomial", digits=4)
Tabel 29. Uji simultan G² model terbaik multinomial
uji G2 df p_value keputusan penjelasan_hasil penanganan_jika_tidak_memenuhi
Uji likelihood ratio 76.4217 6 < 0.001 Tolak H0 Model dengan prediktor memberikan peningkatan kecocokan dibanding model kosong. Tidak diperlukan penanganan khusus untuk LRT.
safe_table(multinom_best_wald_show |> slice_head(n=30), "Uji Wald parsial dan RRR model terbaik multinomial", digits=4, full_width=TRUE)
Tabel 30. Uji Wald parsial dan RRR model terbaik multinomial
kategori_dibanding_referensi variabel estimate std_error z_value p_value relative_risk_ratio ci_95
economic_growth_jobs education_code -0.0833 0.0125 -6.6628 < 0.001 0.9200 0.898 - 0.943
other_answer education_code 0.1051 0.0352 2.9837 0.003 1.1108 1.037 - 1.19
economic_growth_jobs employment_label_student -0.3087 0.1134 -2.7235 0.006 0.7344 0.588 - 0.917
other_answer employment_label_other 1.0773 0.4126 2.6108 0.009 2.9367 1.308 - 6.593
economic_growth_jobs employment_label_other -0.0571 0.2313 -0.2466 0.805 0.9445 0.6 - 1.486
other_answer employment_label_student 0.0465 0.2850 0.1632 0.870 1.0476 0.599 - 1.832

Model terbaik multinomial adalah kandidat ke-9. Model ini dipilih karena mempertahankan 3 variabel signifikan, kemudian dibandingkan berdasarkan AIC. Uji \(G^2\) model terbaik memiliki p-value < 0.001, sehingga model dengan prediktor lebih informatif daripada model kosong. Variabel X yang mendukung perbedaan pilihan choice_code adalah: education_code, employment_label_other, employment_label_student. Karena respons multinomial memiliki beberapa kategori, satu variabel dapat signifikan pada salah satu perbandingan kategori meskipun tidak signifikan pada perbandingan lain.

4.6 Uji Asumsi Model Terbaik

4.6.1 Asumsi 1 — Respons Bersifat Nominal

Hipotesis/Kriteria:
\(H_0\): Variabel respons bersifat nominal dan kategori respons saling eksklusif.
\(H_1\): Variabel respons tidak bersifat nominal atau kategori tidak saling eksklusif.
Kriteria: regresi multinomial sesuai jika respons memiliki lebih dari dua kategori nominal tanpa urutan alami.

multinom_nominal_table <- multinom_train |>
  count(choice_code, name="n") |>
  mutate(proporsi=n/sum(n), skala_respons="Nominal")
safe_table(multinom_nominal_table, "Pemeriksaan skala respons nominal pada model multinomial", digits=3, full_width=TRUE)
Tabel 31. Pemeriksaan skala respons nominal pada model multinomial
choice_code n proporsi skala_respons
protect_environment 3914 0.572 Nominal
economic_growth_jobs 2722 0.398 Nominal
other_answer 209 0.031 Nominal

4.6.2 Asumsi 2 — Observasi Independen

Hipotesis/Kriteria:
\(H_0\): Observasi saling independen.
\(H_1\): Observasi tidak saling independen atau terdapat struktur pengelompokan/pengulangan.
Kriteria: setiap baris data harus merepresentasikan unit observasi berbeda dan tidak berulang tanpa koreksi.

multinom_independent_table <- tibble(
  asumsi="Observasi independen",
  pemeriksaan="Setiap baris data diperlakukan sebagai satu unit observasi yang berbeda.",
  hasil="Tidak ada struktur pengulangan responden yang digunakan dalam model-ready.",
  catatan="Apabila terdapat clustering survei atau panel, koreksi standard error dapat dipertimbangkan."
)
safe_table(multinom_independent_table, "Pemeriksaan asumsi observasi independen pada model multinomial", full_width=TRUE)
Tabel 32. Pemeriksaan asumsi observasi independen pada model multinomial
asumsi pemeriksaan hasil catatan
Observasi independen Setiap baris data diperlakukan sebagai satu unit observasi yang berbeda. Tidak ada struktur pengulangan responden yang digunakan dalam model-ready. Apabila terdapat clustering survei atau panel, koreksi standard error dapat dipertimbangkan.

4.6.3 Asumsi 3 — Tidak Ada Multikolinearitas Berat (VIF)

Hipotesis/Kriteria:
\(H_0\): Tidak terdapat multikolinearitas berat antarprediktor.
\(H_1\): Terdapat multikolinearitas berat antarprediktor.
Kriteria praktis: VIF < 5 aman; VIF 5–10 perlu perhatian; VIF > 10 menunjukkan multikolinearitas berat.

multinom_vif_table <- vif_table(multinom_train, multinom_selection$best$vars)
safe_table(multinom_vif_table, "Uji multikolinearitas model terbaik multinomial menggunakan VIF", digits=3, full_width=TRUE)
Tabel 33. Uji multikolinearitas model terbaik multinomial menggunakan VIF
variabel VIF keterangan penjelasan_hasil penanganan_jika_tidak_memenuhi
employment_label_student 1.004 Tidak ada indikasi multikolinearitas berat Asumsi tidak ada multikolinearitas berat relatif terpenuhi. Tidak diperlukan penanganan khusus.
education_code 1.003 Tidak ada indikasi multikolinearitas berat Asumsi tidak ada multikolinearitas berat relatif terpenuhi. Tidak diperlukan penanganan khusus.
employment_label_other 1.001 Tidak ada indikasi multikolinearitas berat Asumsi tidak ada multikolinearitas berat relatif terpenuhi. Tidak diperlukan penanganan khusus.

4.6.4 Asumsi 4 — Tidak Ada Perfect Separation

Hipotesis/Kriteria:
\(H_0\): Tidak terdapat perfect separation atau prediksi yang terlalu ekstrem.
\(H_1\): Terdapat indikasi perfect separation.
Kriteria praktis: proporsi probabilitas maksimum > 0,99 yang sangat tinggi dapat menjadi sinyal separasi atau prediksi ekstrem.

multinom_prob_train <- predict(multinom_best_fit, newdata=multinom_train, type="probs")
if(is.null(dim(multinom_prob_train))){multinom_prob_train <- cbind(multinom_prob_train, 1-multinom_prob_train)}
multinom_separation_table <- tibble(
  diagnostik=c("Proporsi probabilitas maksimum > 0,99", "Probabilitas maksimum terbesar", "Probabilitas minimum terkecil"),
  nilai=c(mean(apply(multinom_prob_train, 1, max) > .99), max(multinom_prob_train), min(multinom_prob_train)),
  keterangan=c(
    "Nilai tinggi dapat mengindikasikan prediksi ekstrem.",
    "Jika mendekati 1 untuk banyak observasi, perlu waspada separasi.",
    "Jika mendekati 0 untuk banyak observasi, perlu waspada separasi."
  )
)
safe_table(multinom_separation_table, "Diagnostik perfect separation model terbaik multinomial", digits=4, full_width=TRUE)
Tabel 34. Diagnostik perfect separation model terbaik multinomial
diagnostik nilai keterangan
Proporsi probabilitas maksimum > 0,99 0.0000 Nilai tinggi dapat mengindikasikan prediksi ekstrem.
Probabilitas maksimum terbesar 0.6933 Jika mendekati 1 untuk banyak observasi, perlu waspada separasi.
Probabilitas minimum terkecil 0.0173 Jika mendekati 0 untuk banyak observasi, perlu waspada separasi.

4.6.5 Asumsi 5 — Linearitas pada Skala Logit (Box-Tidwell)

Hipotesis:
\(H_0\): Hubungan prediktor numerik dengan skala logit bersifat linear.
\(H_1\): Hubungan prediktor numerik dengan skala logit tidak linear.
Karena model multinomial memiliki beberapa kategori, pemeriksaan dilakukan secara one-vs-rest.

multinom_bt_table <- purrr::map_dfr(levels(multinom_train$choice_code), function(k){
  tmp <- multinom_train |> mutate(.y_bin=as.integer(choice_code == k))
  box_tidwell_table(tmp, response=".y_bin", predictors=multinom_selection$best$vars, family=binomial(link="logit")) |>
    mutate(kategori_one_vs_rest=k)
}) |>
  mutate(p_value=p_value_tampil) |>
  select(kategori_one_vs_rest, everything(), -p_value_tampil)
safe_table(multinom_bt_table, "Diagnostik Box-Tidwell one-vs-rest untuk model multinomial", digits=4, full_width=TRUE)
Tabel 35. Diagnostik Box-Tidwell one-vs-rest untuk model multinomial
kategori_one_vs_rest variabel estimate p_value keterangan penjelasan_hasil penanganan_jika_tidak_memenuhi
protect_environment education_code -0.0099 0.752 Tidak ada bukti kuat non-linearitas logit H0 gagal ditolak; hubungan prediktor dengan logit relatif linear. Tidak diperlukan penanganan khusus.
protect_environment employment_label_other NA NA NA NA NA
protect_environment employment_label_student NA NA NA NA NA
economic_growth_jobs education_code 0.0054 0.864 Tidak ada bukti kuat non-linearitas logit H0 gagal ditolak; hubungan prediktor dengan logit relatif linear. Tidak diperlukan penanganan khusus.
economic_growth_jobs employment_label_other NA NA NA NA NA
economic_growth_jobs employment_label_student NA NA NA NA NA
other_answer education_code -0.0289 0.779 Tidak ada bukti kuat non-linearitas logit H0 gagal ditolak; hubungan prediktor dengan logit relatif linear. Tidak diperlukan penanganan khusus.
other_answer employment_label_other NA NA NA NA NA
other_answer employment_label_student NA NA NA NA NA

4.6.6 Asumsi 6 — Independence of Irrelevant Alternatives (IIA)

Hipotesis/Kriteria:
\(H_0\): Perbandingan odds antara dua kategori tidak dipengaruhi secara tidak wajar oleh keberadaan kategori lain.
\(H_1\): Perbandingan odds antara dua kategori dipengaruhi oleh keberadaan kategori lain.
Pada laporan ini, IIA dievaluasi secara substantif karena uji formal memerlukan struktur data dan paket khusus.

multinom_iia_table <- tibble(
  asumsi="Independence of Irrelevant Alternatives (IIA)",
  pemeriksaan="Evaluasi substantif berdasarkan struktur pilihan kategori.",
  hasil="Kategori respons diperlakukan sebagai alternatif nominal yang saling eksklusif.",
  catatan="Uji formal IIA seperti Hausman-McFadden memerlukan struktur data khusus; pada laporan ini IIA dibahas sebagai asumsi substantif model multinomial."
)
safe_table(multinom_iia_table, "Pemeriksaan asumsi IIA pada model multinomial", full_width=TRUE)
Tabel 36. Pemeriksaan asumsi IIA pada model multinomial
asumsi pemeriksaan hasil catatan
Independence of Irrelevant Alternatives (IIA) Evaluasi substantif berdasarkan struktur pilihan kategori. Kategori respons diperlakukan sebagai alternatif nominal yang saling eksklusif. Uji formal IIA seperti Hausman-McFadden memerlukan struktur data khusus; pada laporan ini IIA dibahas sebagai asumsi substantif model multinomial.

4.7 Prediksi dengan model terbaik

multinom_pred <- predict(multinom_best_fit, newdata=multinom_test, type="class")
multinom_prob <- predict(multinom_best_fit, newdata=multinom_test, type="probs")
multinom_metrics <- multi_metrics(multinom_test$choice_code, multinom_pred)
safe_table(cm_labeled(multinom_metrics$confusion_matrix), "Confusion matrix model terbaik multinomial dengan keterangan aktual dan prediksi")
Tabel 37. Confusion matrix model terbaik multinomial dengan keterangan aktual dan prediksi
Aktual  Prediksi Prediksi: protect_environment Prediksi: economic_growth_jobs Prediksi: other_answer
Aktual: protect_environment 979 0 0
Aktual: economic_growth_jobs 681 0 0
Aktual: other_answer 53 0 0
safe_table(multinom_metrics$accuracy_table, "Metrik agregat testing model terbaik multinomial", digits=3)
Tabel 38. Metrik agregat testing model terbaik multinomial
accuracy error_rate
0.572 0.428
safe_table(multinom_metrics$class_metrics, "Metrik evaluasi per kelas model terbaik multinomial", digits=3)
Tabel 39. Metrik evaluasi per kelas model terbaik multinomial
kelas sensitivity specificity precision f1_score
protect_environment 1 0 0.572 0.727
economic_growth_jobs 0 1 NA NA
other_answer 0 1 NA NA
multinom_prob_df <- as.data.frame(multinom_prob) |> mutate(observasi=row_number()) |> pivot_longer(-observasi, names_to="kategori", values_to="probabilitas")
multinom_prob_df |> group_by(kategori) |> summarise(rata_rata_probabilitas=mean(probabilitas), .groups="drop") |> ggplot(aes(x=reorder(kategori,rata_rata_probabilitas), y=rata_rata_probabilitas, fill=kategori))+geom_col(width=.70)+geom_text(aes(label=round(rata_rata_probabilitas,3)), hjust=-.12, fontface="bold")+coord_flip()+scale_fill_manual(values=report_palette)+scale_y_continuous(labels=percent_format(accuracy=1))+labs(title="Rata-rata Probabilitas Prediksi Model Terbaik Multinomial", subtitle="Diringkas dari data testing.", x="Kategori", y="Rata-rata probabilitas prediksi", fill="Kategori")+theme_report()
Gambar 4. Rata-rata probabilitas prediksi model terbaik multinomial.

Gambar 4. Rata-rata probabilitas prediksi model terbaik multinomial.

Prediksi dilakukan dengan memilih kategori yang memiliki probabilitas terbesar. Accuracy model terbaik pada data testing adalah 0.572. Karena respons memiliki beberapa kategori, evaluasi model juga perlu membaca sensitivity, specificity, precision, dan F1-score pada setiap kelas.

4.8 Korelasi prediktor multinomial

multinom_corr_top <- correlation_pairs(multinom_train |> select(all_of(multinom_x)), top_n=10)
safe_table(multinom_corr_top, "Sepuluh pasangan prediktor multinomial dengan korelasi absolut terbesar", digits=3)
Tabel 40. Sepuluh pasangan prediktor multinomial dengan korelasi absolut terbesar
variabel_1 variabel_2 korelasi abs_korelasi
age employment_label_retired 0.554 0.554
gender_label_male employment_label_housewife_or_not_working -0.339 0.339
age employment_label_student -0.319 0.319
education_code income_scale 0.279 0.279
education_code employment_label_housewife_or_not_working -0.176 0.176
employment_label_housewife_or_not_working employment_label_self_employed -0.155 0.155
employment_label_retired employment_label_self_employed -0.153 0.153
education_code employment_label_self_employed -0.153 0.153
employment_label_housewife_or_not_working employment_label_retired -0.140 0.140
age education_code -0.126 0.126

5 Bagian III — Regresi Logistik Ordinal

5.1 Konsep model

Regresi logistik ordinal digunakan ketika respons memiliki urutan kategori. Pada model cumulative logit:

\[ \log\left(\frac{P(Y_i\le j)}{P(Y_i>j)}\right)=\theta_j-\left(\beta_1X_{1i}+\cdots+\beta_kX_{ki}\right) \]

Model ini mempertahankan informasi urutan tanpa menganggap jarak antar kategori harus sama.

5.2 Import, struktur data, dan distribusi respons

ordinal_data <- readxl::read_excel(resolve_path("ordinal_model_ready_sample.xlsx")) |>
  clean_model_data() |>
  mutate(gov_confidence_ord=ordered(gov_confidence_ord, levels=sort(unique(gov_confidence_ord)), labels=c("none_at_all","not_very_much","quite_a_lot","a_great_deal")[seq_along(sort(unique(gov_confidence_ord)))]))
ordinal_dict <- make_dictionary(ordinal_data)
ordinal_summary <- ordinal_data |> count(gov_confidence_ord, name="n") |> mutate(proporsi=n/sum(n))
safe_table(ordinal_dict, "Kamus data ordinal setelah seleksi variabel analisis")
Tabel 41. Kamus data ordinal setelah seleksi variabel analisis
variabel tipe missing unique_value
gov_confidence_ord ordered/factor 0 4
age numeric 0 82
education_code numeric 0 9
income_scale numeric 0 10
gender_label_male numeric 0 2
employment_label_housewife_or_not_working numeric 0 2
employment_label_other numeric 0 2
employment_label_part_time_employee numeric 0 2
employment_label_retired numeric 0 2
employment_label_self_employed numeric 0 2
employment_label_student numeric 0 2
employment_label_unemployed numeric 0 2
safe_table(ordinal_summary, "Distribusi respons ordinal gov_confidence_ord", digits=3)
Tabel 42. Distribusi respons ordinal gov_confidence_ord
gov_confidence_ord n proporsi
none_at_all 1590 0.225
not_very_much 2229 0.315
quite_a_lot 2218 0.314
a_great_deal 1030 0.146
ordinal_summary |> ggplot(aes(x=gov_confidence_ord, y=n, fill=gov_confidence_ord))+geom_col(width=.70)+geom_text(aes(label=n), vjust=-.45, fontface="bold")+scale_fill_manual(values=report_palette)+labs(title="Distribusi Government Confidence", subtitle="Kategori respons memiliki urutan alami.", x="Tingkat kepercayaan terhadap pemerintah", y="Jumlah observasi", fill="Kategori")+theme_report()
Gambar 5. Distribusi kategori respons ordinal.

Gambar 5. Distribusi kategori respons ordinal.

Variabel gov_confidence_ord memiliki urutan substantif dari kepercayaan rendah ke tinggi. Karena jarak antar kategori tidak harus sama, regresi logistik ordinal lebih tepat dibanding model linear biasa.

5.3 Statistik Deskriptif dan Eksplorasi Data

ordinal_descriptive <- numeric_descriptive(ordinal_data, response="gov_confidence_ord")
ordinal_x_preview <- model_predictors(ordinal_data, response="gov_confidence_ord")
ordinal_group_summary <- ordinal_data |>
  select(gov_confidence_ord, all_of(ordinal_x_preview)) |>
  group_by(gov_confidence_ord) |>
  summarise(across(everything(), ~mean(.x, na.rm=TRUE)), .groups="drop") |>
  pivot_longer(-gov_confidence_ord, names_to="variabel", values_to="mean") |>
  pivot_wider(names_from=gov_confidence_ord, values_from=mean)

safe_table(ordinal_descriptive, "Statistik deskriptif prediktor numerik pada data ordinal", digits=3, full_width=TRUE)
Tabel 43. Statistik deskriptif prediktor numerik pada data ordinal
variabel n missing mean sd min q1 median q3 max
age 7067 0 43.250 16.578 16 29 41 56 99
education_code 7067 0 3.564 2.030 0 2 3 6 8
income_scale 7067 0 4.916 2.097 1 3 5 6 10
gender_label_male 7067 0 0.473 0.499 0 0 0 1 1
employment_label_housewife_or_not_working 7067 0 0.134 0.340 0 0 0 0 1
employment_label_other 7067 0 0.012 0.107 0 0 0 0 1
employment_label_part_time_employee 7067 0 0.087 0.283 0 0 0 0 1
employment_label_retired 7067 0 0.130 0.337 0 0 0 0 1
employment_label_self_employed 7067 0 0.141 0.348 0 0 0 0 1
employment_label_student 7067 0 0.054 0.226 0 0 0 0 1
employment_label_unemployed 7067 0 0.075 0.264 0 0 0 0 1
safe_table(ordinal_group_summary, "Rata-rata prediktor menurut tingkat gov_confidence_ord", digits=3, full_width=TRUE)
Tabel 44. Rata-rata prediktor menurut tingkat gov_confidence_ord
variabel none_at_all not_very_much quite_a_lot a_great_deal
age 42.257 43.435 43.874 43.040
education_code 3.622 3.705 3.611 3.067
income_scale 4.777 4.918 5.110 4.710
gender_label_male 0.489 0.463 0.465 0.486
employment_label_housewife_or_not_working 0.133 0.118 0.133 0.171
employment_label_other 0.012 0.014 0.007 0.015
employment_label_part_time_employee 0.087 0.091 0.086 0.083
employment_label_retired 0.123 0.141 0.132 0.117
employment_label_self_employed 0.136 0.142 0.135 0.157
employment_label_student 0.059 0.053 0.048 0.061
employment_label_unemployed 0.096 0.069 0.062 0.083
ordinal_plot_vars <- ordinal_x_preview[seq_len(min(4, length(ordinal_x_preview)))]
ordinal_data |>
  select(gov_confidence_ord, all_of(ordinal_plot_vars)) |>
  pivot_longer(-gov_confidence_ord, names_to="variabel", values_to="nilai") |>
  ggplot(aes(x=gov_confidence_ord, y=nilai, fill=gov_confidence_ord))+
  geom_boxplot(alpha=.75, outlier.alpha=.25)+
  facet_wrap(~variabel, scales="free_y")+
  scale_fill_manual(values=report_palette)+
  labs(title="Eksplorasi Prediktor terhadap Government Confidence", x="Tingkat respons ordinal", y="Nilai prediktor", fill="Kategori")+
  theme_report()
Gambar 6. Eksplorasi prediktor numerik utama menurut tingkat gov_confidence_ord.

Gambar 6. Eksplorasi prediktor numerik utama menurut tingkat gov_confidence_ord.

5.4 Pembagian data training dan testing

ordinal_split <- split_train_test(ordinal_data, strata="gov_confidence_ord", prop=.80)
ordinal_train <- ordinal_split$train; ordinal_test <- ordinal_split$test
ordinal_split_table <- bind_rows(ordinal_train |> count(gov_confidence_ord, name="n") |> mutate(subset="Training"), ordinal_test |> count(gov_confidence_ord, name="n") |> mutate(subset="Testing")) |> group_by(subset) |> mutate(proporsi=n/sum(n)) |> ungroup() |> select(subset, gov_confidence_ord, n, proporsi)
safe_table(ordinal_split_table, "Komposisi respons ordinal pada data training dan testing", digits=3)
Tabel 45. Komposisi respons ordinal pada data training dan testing
subset gov_confidence_ord n proporsi
Training none_at_all 1272 0.225
Training not_very_much 1783 0.315
Training quite_a_lot 1774 0.314
Training a_great_deal 824 0.146
Testing none_at_all 318 0.225
Testing not_very_much 446 0.315
Testing quite_a_lot 444 0.314
Testing a_great_deal 206 0.146

5.5 Kandidat model dan pemilihan model terbaik

ordinal_x <- model_predictors(ordinal_train, response="gov_confidence_ord")
ordinal_selection <- select_best_polr(ordinal_train, response="gov_confidence_ord", predictors=ordinal_x)
ordinal_best_fit <- ordinal_selection$best$fit
ordinal_best_null <- MASS::polr(gov_confidence_ord ~ 1, data=ordinal_train, Hess=TRUE, method="logistic")
ordinal_best_wald <- ordinal_selection$best$wald
ordinal_best_g2 <- ordinal_selection$best$g2
ordinal_selected_vars <- ordinal_best_wald |> filter(p_value < 0.05) |> pull(variabel)

ordinal_model_selection_table <- ordinal_selection$summary |>
  mutate(p_value_G2=p_value_G2_tampil) |>
  select(model_ke, formula, jumlah_prediktor, jumlah_variabel_signifikan, AIC, G2, p_value_G2)

ordinal_fit_table <- tibble(keterangan=c("Jumlah observasi training","Jumlah prediktor model terbaik","AIC model terbaik","Log-likelihood model terbaik","Log-likelihood model kosong","McFadden pseudo R-square"), nilai=c(nrow(ordinal_train), length(ordinal_selection$best$vars), AIC(ordinal_best_fit), as.numeric(logLik(ordinal_best_fit)), as.numeric(logLik(ordinal_best_null)), mcfadden_r2(ordinal_best_fit,ordinal_best_null)))

ordinal_best_wald_show <- ordinal_best_wald |> mutate(p_value=p_value_tampil) |> select(-p_value_tampil)
ordinal_best_g2_show <- ordinal_best_g2 |> mutate(p_value=p_value_tampil) |> select(-p_value_tampil)

safe_table(ordinal_model_selection_table, "Perbandingan kandidat model ordinal berdasarkan jumlah variabel signifikan dan AIC", digits=4, full_width=TRUE)
Tabel 46. Perbandingan kandidat model ordinal berdasarkan jumlah variabel signifikan dan AIC
model_ke formula jumlah_prediktor jumlah_variabel_signifikan AIC G2 p_value_G2
9 gov_confidence_ord ~ education_code + income_scale + employment_label_unemployed 3 3 15160.10 46.9131 < 0.001
8 gov_confidence_ord ~ education_code + income_scale + employment_label_other + employment_label_unemployed 4 3 15161.02 47.9894 < 0.001
7 gov_confidence_ord ~ education_code + income_scale + employment_label_other + employment_label_retired + employment_label_unemployed 5 3 15161.87 49.1385 < 0.001
6 gov_confidence_ord ~ age + education_code + income_scale + employment_label_other + employment_label_retired + employment_label_unemployed 6 3 15162.71 50.2993 < 0.001
5 gov_confidence_ord ~ age + education_code + income_scale + employment_label_housewife_or_not_working + employment_label_other + employment_label_retired + employment_label_unemployed 7 3 15164.03 50.9830 < 0.001
4 gov_confidence_ord ~ age + education_code + income_scale + employment_label_housewife_or_not_working + employment_label_other + employment_label_part_time_employee + employment_label_retired + employment_label_unemployed 8 3 15165.67 51.3376 < 0.001
3 gov_confidence_ord ~ age + education_code + income_scale + employment_label_housewife_or_not_working + employment_label_other + employment_label_part_time_employee + employment_label_retired + employment_label_student + employment_label_unemployed 9 3 15167.46 51.5538 < 0.001
2 gov_confidence_ord ~ age + education_code + income_scale + gender_label_male + employment_label_housewife_or_not_working + employment_label_other + employment_label_part_time_employee + employment_label_retired + employment_label_student + employment_label_unemployed 10 3 15169.36 51.6491 < 0.001
1 gov_confidence_ord ~ age + education_code + income_scale + gender_label_male + employment_label_housewife_or_not_working + employment_label_other + employment_label_part_time_employee + employment_label_retired + employment_label_self_employed + employment_label_student + employment_label_unemployed 11 3 15171.34 51.6678 < 0.001
safe_table(ordinal_fit_table, "Ringkasan kecocokan model terbaik ordinal", digits=4)
Tabel 47. Ringkasan kecocokan model terbaik ordinal
keterangan nilai
Jumlah observasi training 5653.0000
Jumlah prediktor model terbaik 3.0000
AIC model terbaik 15160.0989
Log-likelihood model terbaik -7574.0494
Log-likelihood model kosong -7597.5060
McFadden pseudo R-square 0.0031
safe_table(ordinal_best_g2_show, "Uji simultan G² model terbaik ordinal", digits=4)
Tabel 48. Uji simultan G² model terbaik ordinal
uji G2 df p_value keputusan penjelasan_hasil penanganan_jika_tidak_memenuhi
Uji likelihood ratio 46.9131 3 < 0.001 Tolak H0 Model dengan prediktor memberikan peningkatan kecocokan dibanding model kosong. Tidak diperlukan penanganan khusus untuk LRT.
safe_table(ordinal_best_wald_show |> slice_head(n=30), "Uji Wald parsial dan odds ratio model terbaik ordinal", digits=4, full_width=TRUE)
Tabel 49. Uji Wald parsial dan odds ratio model terbaik ordinal
variabel estimate std_error z_value p_value odds_ratio ci_95
education_code -0.0771 0.0124 -6.2100 < 0.001 0.9258 0.904 - 0.949
income_scale 0.0347 0.0122 2.8445 0.004 1.0353 0.904 - 0.949
employment_label_unemployed -0.2576 0.0939 -2.7432 0.006 0.7729 0.904 - 0.949

Model terbaik ordinal adalah kandidat ke-9, dengan 3 variabel signifikan dan AIC sebesar 1.5160099^{4}. Uji \(G^2\) model terbaik memiliki p-value < 0.001, sehingga model terpilih memberikan peningkatan informasi dibanding model kosong. Variabel X yang mendukung variasi tingkat gov_confidence_ord adalah: education_code, income_scale, employment_label_unemployed. Dengan demikian, prediksi ordinal berikut dilakukan menggunakan model terbaik tersebut.

5.6 Uji Asumsi Model Terbaik

5.6.1 Asumsi 1 — Asumsi Proportional Odds

Hipotesis/Kriteria:
\(H_0\): Efek prediktor relatif sama pada setiap batas kumulatif kategori respons.
\(H_1\): Efek prediktor berbeda pada beberapa batas kumulatif kategori respons.
Kriteria praktis: rentang koefisien logit kumulatif yang kecil menunjukkan efek prediktor relatif stabil; rentang yang besar mengindikasikan potensi pelanggaran proportional odds.

ordinal_parallel <- ordinal_parallel_diagnostic(
  data=ordinal_train,
  response="gov_confidence_ord",
  predictors=ordinal_selection$best$vars
)

safe_table(ordinal_parallel$ringkas, "Ringkasan diagnostik proportional odds berdasarkan rentang koefisien logit kumulatif", digits=4, full_width=TRUE)
Tabel 50. Ringkasan diagnostik proportional odds berdasarkan rentang koefisien logit kumulatif
variabel jumlah_batas min_estimate max_estimate rentang_estimate
employment_label_unemployed 3 -0.0342 0.4038 0.4380
education_code 3 0.0386 0.1609 0.1224
income_scale 3 -0.0535 0.0273 0.0808
safe_table(ordinal_parallel$detail |> slice_head(n=30) |> mutate(p_value=p_value_tampil) |> select(-p_value_tampil), "Detail uji Wald pada logit kumulatif sebagai diagnostik proportional odds", digits=4, full_width=TRUE)
Tabel 51. Detail uji Wald pada logit kumulatif sebagai diagnostik proportional odds
batas_kumulatif variabel estimate z_value p_value
Y <= none_at_all education_code 0.0386 2.3242 0.020
Y <= none_at_all income_scale -0.0535 -3.2975 < 0.001
Y <= none_at_all employment_label_unemployed 0.4038 3.6241 < 0.001
Y <= not_very_much education_code 0.0736 5.2843 < 0.001
Y <= not_very_much income_scale -0.0463 -3.4192 < 0.001
Y <= not_very_much employment_label_unemployed 0.2259 2.1941 0.028
Y <= quite_a_lot education_code 0.1609 7.8662 < 0.001
Y <= quite_a_lot income_scale 0.0273 1.4407 0.150
Y <= quite_a_lot employment_label_unemployed -0.0342 -0.2460 0.806

5.6.2 Asumsi 2 — Tidak Ada Multikolinearitas Berat

Hipotesis/Kriteria:
\(H_0\): Tidak terdapat multikolinearitas berat antarprediktor.
\(H_1\): Terdapat multikolinearitas berat antarprediktor.
Kriteria praktis: VIF < 5 aman; VIF 5–10 perlu perhatian; VIF > 10 menunjukkan multikolinearitas berat.

ordinal_vif_table <- vif_table(ordinal_train, ordinal_selection$best$vars)
ordinal_corr_best_table <- selected_correlation_check(ordinal_train, ordinal_selection$best$vars)

safe_table(ordinal_vif_table, "Uji multikolinearitas model terbaik ordinal menggunakan VIF", digits=3, full_width=TRUE)
Tabel 52. Uji multikolinearitas model terbaik ordinal menggunakan VIF
variabel VIF keterangan penjelasan_hasil penanganan_jika_tidak_memenuhi
income_scale 1.108 Tidak ada indikasi multikolinearitas berat Asumsi tidak ada multikolinearitas berat relatif terpenuhi. Tidak diperlukan penanganan khusus.
education_code 1.100 Tidak ada indikasi multikolinearitas berat Asumsi tidak ada multikolinearitas berat relatif terpenuhi. Tidak diperlukan penanganan khusus.
employment_label_unemployed 1.012 Tidak ada indikasi multikolinearitas berat Asumsi tidak ada multikolinearitas berat relatif terpenuhi. Tidak diperlukan penanganan khusus.
safe_table(ordinal_corr_best_table, "Pemeriksaan korelasi tinggi antarprediktor model terbaik ordinal", digits=3, full_width=TRUE)
Tabel 53. Pemeriksaan korelasi tinggi antarprediktor model terbaik ordinal
variabel_1 variabel_2 korelasi abs_korelasi keterangan penjelasan_hasil penanganan_jika_tidak_memenuhi
NA NA Tidak ada pasangan prediktor dengan |r| >= 0,80. Tidak ada indikasi korelasi tinggi antarprediktor. Tidak diperlukan penanganan khusus.

5.7 Prediksi dengan model terbaik

ordinal_pred <- predict(ordinal_best_fit, newdata=ordinal_test, type="class")
ordinal_prob <- predict(ordinal_best_fit, newdata=ordinal_test, type="probs")
ordinal_metrics <- multi_metrics(ordinal_test$gov_confidence_ord, ordinal_pred)
safe_table(cm_labeled(ordinal_metrics$confusion_matrix), "Confusion matrix model terbaik ordinal dengan keterangan aktual dan prediksi")
Tabel 54. Confusion matrix model terbaik ordinal dengan keterangan aktual dan prediksi
Aktual  Prediksi Prediksi: none_at_all Prediksi: not_very_much Prediksi: quite_a_lot Prediksi: a_great_deal
Aktual: none_at_all 1 151 166 0
Aktual: not_very_much 0 229 217 0
Aktual: quite_a_lot 0 200 244 0
Aktual: a_great_deal 0 98 108 0
safe_table(ordinal_metrics$accuracy_table, "Metrik agregat testing model terbaik ordinal", digits=3)
Tabel 55. Metrik agregat testing model terbaik ordinal
accuracy error_rate
0.335 0.665
safe_table(ordinal_metrics$class_metrics, "Metrik evaluasi per kelas model terbaik ordinal", digits=3)
Tabel 56. Metrik evaluasi per kelas model terbaik ordinal
kelas sensitivity specificity precision f1_score
none_at_all 0.003 1.000 1.000 0.006
not_very_much 0.513 0.536 0.338 0.407
quite_a_lot 0.550 0.494 0.332 0.414
a_great_deal 0.000 1.000 NA NA
ordinal_prob_df <- as.data.frame(ordinal_prob) |> mutate(observasi=row_number()) |> pivot_longer(-observasi, names_to="kategori", values_to="probabilitas")
ordinal_prob_df |> group_by(kategori) |> summarise(rata_rata_probabilitas=mean(probabilitas), .groups="drop") |> ggplot(aes(x=kategori, y=rata_rata_probabilitas, fill=kategori))+geom_col(width=.70)+geom_text(aes(label=round(rata_rata_probabilitas,3)), vjust=-.45, fontface="bold")+scale_fill_manual(values=report_palette)+scale_y_continuous(labels=percent_format(accuracy=1))+labs(title="Rata-rata Probabilitas Prediksi Model Terbaik Ordinal", subtitle="Diringkas dari data testing.", x="Kategori ordinal", y="Rata-rata probabilitas prediksi", fill="Kategori")+theme_report()
Gambar 6. Rata-rata probabilitas prediksi model terbaik ordinal.

Gambar 6. Rata-rata probabilitas prediksi model terbaik ordinal.

Accuracy testing model terbaik ordinal adalah 0.335. Pada respons ordinal, pola kesalahan juga perlu dibaca karena kesalahan ke kategori yang berdekatan lebih ringan secara substantif dibanding kesalahan ke kategori yang jauh.

5.8 Korelasi prediktor ordinal

ordinal_corr_top <- correlation_pairs(ordinal_train |> select(all_of(ordinal_x)), top_n=10)
safe_table(ordinal_corr_top, "Sepuluh pasangan prediktor ordinal dengan korelasi absolut terbesar", digits=3)
Tabel 57. Sepuluh pasangan prediktor ordinal dengan korelasi absolut terbesar
variabel_1 variabel_2 korelasi abs_korelasi
age employment_label_retired 0.565 0.565
gender_label_male employment_label_housewife_or_not_working -0.358 0.358
age employment_label_student -0.308 0.308
education_code income_scale 0.300 0.300
education_code employment_label_housewife_or_not_working -0.213 0.213
employment_label_housewife_or_not_working employment_label_self_employed -0.159 0.159
employment_label_retired employment_label_self_employed -0.155 0.155
employment_label_housewife_or_not_working employment_label_retired -0.150 0.150
employment_label_part_time_employee employment_label_self_employed -0.126 0.126
education_code employment_label_self_employed -0.126 0.126

6 Bagian IV — Regresi Poisson Tahunan

6.1 Konsep model

Regresi Poisson digunakan untuk respons berupa hitungan non-negatif. Model menggunakan link log:

\[ \log(\mu_i)=\beta_0+\beta_1X_{1i}+\cdots+\beta_kX_{ki} \]

Ukuran efek yang digunakan adalah incidence rate ratio (IRR), yaitu \(\exp(\beta)\).

6.2 Import, struktur data, dan distribusi respons

poisson_data <- readxl::read_excel(resolve_path("poisson_model_ready.xlsx")) |>
  clean_model_data()
poisson_overdispersion_file <- readxl::read_excel(resolve_path("poisson_overdispersion_check.xlsx"))
poisson_duplicate_check <- tibble(variabel_1="lag_conflict_count", variabel_2="lag_actor_count", identik=if(all(c("lag_conflict_count","lag_actor_count") %in% names(poisson_data))) isTRUE(all.equal(poisson_data$lag_conflict_count, poisson_data$lag_actor_count)) else NA, keputusan="lag_actor_count tidak digunakan pada model akhir")
poisson_data <- poisson_data |> select(-any_of("lag_actor_count"))
poisson_dict <- make_dictionary(poisson_data)
poisson_summary <- tibble(n_observasi=nrow(poisson_data), min_case_count=min(poisson_data$case_count,na.rm=TRUE), mean_case_count=mean(poisson_data$case_count,na.rm=TRUE), variance_case_count=var(poisson_data$case_count,na.rm=TRUE), median_case_count=median(poisson_data$case_count,na.rm=TRUE), max_case_count=max(poisson_data$case_count,na.rm=TRUE), proporsi_nol=mean(poisson_data$case_count==0,na.rm=TRUE))
safe_table(poisson_dict, "Kamus data Poisson tahunan setelah seleksi variabel analisis")
Tabel 58. Kamus data Poisson tahunan setelah seleksi variabel analisis
variabel tipe missing unique_value
case_count numeric 0 12
year_centered numeric 0 36
log1p_lag_case_count numeric 0 12
log1p_lag_total_fatalities numeric 0 341
lag_conflict_count numeric 0 12
lag_share_gov_actor numeric 0 13
region_label_Americas numeric 0 2
region_label_Asia numeric 0 2
region_label_Europe numeric 0 2
region_label_Middle_East numeric 0 2
safe_table(poisson_duplicate_check, "Cek duplikasi prediktor lag pada data Poisson tahunan")
Tabel 59. Cek duplikasi prediktor lag pada data Poisson tahunan
variabel_1 variabel_2 identik keputusan
lag_conflict_count lag_actor_count TRUE lag_actor_count tidak digunakan pada model akhir
safe_table(poisson_overdispersion_file, "Ringkasan awal cek overdispersion Poisson tahunan")
Tabel 60. Ringkasan awal cek overdispersion Poisson tahunan
metric value
pearson_chi_square 3552.07569486401
df_residual 2906
dispersion_ratio 1.222324740145908
interpretation Tidak ada indikasi kuat overdispersion
safe_table(poisson_summary, "Ringkasan deskriptif respons case_count pada data Poisson tahunan", digits=3)
Tabel 61. Ringkasan deskriptif respons case_count pada data Poisson tahunan
n_observasi min_case_count mean_case_count variance_case_count median_case_count max_case_count proporsi_nol
2916 0 0.382 0.933 0 12 0.775
poisson_data |> ggplot(aes(x=case_count))+geom_histogram(binwidth=1,boundary=-.5,fill="#0891b2",color="white")+scale_x_continuous(breaks=scales::pretty_breaks())+labs(title="Distribusi Violence Case Count Tahunan", subtitle="Respons berupa data hitungan non-negatif.", x="Jumlah kejadian kekerasan", y="Frekuensi")+theme_report()
Gambar 7. Distribusi respons case_count pada data Poisson tahunan.

Gambar 7. Distribusi respons case_count pada data Poisson tahunan.

case_count memenuhi bentuk dasar respons Poisson karena berupa hitungan non-negatif. Rata-rata dan varians respons perlu diperhatikan karena model Poisson klasik mengasumsikan equidispersion, yaitu rata-rata dan varians kondisional relatif sama.

6.3 Statistik Deskriptif dan Eksplorasi Data

poisson_descriptive <- numeric_descriptive(poisson_data, response="case_count")
poisson_x_preview <- model_predictors(poisson_data, response="case_count")
poisson_response_exploration <- tibble(
  ukuran=c("Mean case_count", "Variance case_count", "Variance / Mean", "Proporsi nilai nol"),
  nilai=c(
    mean(poisson_data$case_count, na.rm=TRUE),
    var(poisson_data$case_count, na.rm=TRUE),
    var(poisson_data$case_count, na.rm=TRUE)/mean(poisson_data$case_count, na.rm=TRUE),
    mean(poisson_data$case_count == 0, na.rm=TRUE)
  )
)

safe_table(poisson_descriptive, "Statistik deskriptif prediktor numerik pada data Poisson tahunan", digits=3, full_width=TRUE)
Tabel 62. Statistik deskriptif prediktor numerik pada data Poisson tahunan
variabel n missing mean sd min q1 median q3 max
year_centered 2916 0 17.500 10.390 0 8.75 17.5 26.25 35.000
log1p_lag_case_count 2916 0 0.199 0.411 0 0.00 0.0 0.00 2.565
log1p_lag_total_fatalities 2916 0 1.081 2.129 0 0.00 0.0 0.00 9.783
lag_conflict_count 2916 0 0.368 0.945 0 0.00 0.0 0.00 12.000
lag_share_gov_actor 2916 0 0.099 0.280 0 0.00 0.0 0.00 1.000
region_label_Americas 2916 0 0.148 0.355 0 0.00 0.0 0.00 1.000
region_label_Asia 2916 0 0.185 0.389 0 0.00 0.0 0.00 1.000
region_label_Europe 2916 0 0.123 0.329 0 0.00 0.0 0.00 1.000
region_label_Middle_East 2916 0 0.123 0.329 0 0.00 0.0 0.00 1.000
safe_table(poisson_response_exploration, "Eksplorasi awal respons case_count untuk indikasi overdispersi dan zero inflation", digits=4, full_width=TRUE)
Tabel 63. Eksplorasi awal respons case_count untuk indikasi overdispersi dan zero inflation
ukuran nilai
Mean case_count 0.3824
Variance case_count 0.9333
Variance / Mean 2.4409
Proporsi nilai nol 0.7750
poisson_plot_vars <- poisson_x_preview[seq_len(min(4, length(poisson_x_preview)))]
poisson_data |>
  select(case_count, all_of(poisson_plot_vars)) |>
  pivot_longer(-case_count, names_to="variabel", values_to="nilai") |>
  ggplot(aes(x=nilai, y=case_count))+
  geom_point(alpha=.35, color="#172033")+
  geom_smooth(method="glm", method.args=list(family=poisson(link="log")), se=TRUE, color="#6d28d9", fill="#6d28d9", alpha=.18)+
  facet_wrap(~variabel, scales="free_x")+
  labs(title="Eksplorasi Hubungan Prediktor dengan Violence Case Count", x="Nilai prediktor", y="case_count")+
  theme_report()
Gambar 8. Hubungan prediktor numerik utama dengan case_count.

Gambar 8. Hubungan prediktor numerik utama dengan case_count.

6.4 Pembagian data training dan testing

poisson_split <- split_train_test(poisson_data, prop=.80)
poisson_train <- poisson_split$train; poisson_test <- poisson_split$test
poisson_split_table <- tibble(subset=c("Training","Testing"), n=c(nrow(poisson_train),nrow(poisson_test)), mean_case_count=c(mean(poisson_train$case_count),mean(poisson_test$case_count)), proporsi_nol=c(mean(poisson_train$case_count==0),mean(poisson_test$case_count==0)))
safe_table(poisson_split_table, "Komposisi training dan testing pada data Poisson tahunan", digits=3)
Tabel 64. Komposisi training dan testing pada data Poisson tahunan
subset n mean_case_count proporsi_nol
Training 2332 0.373 0.777
Testing 584 0.420 0.767

6.5 Kandidat model dan pemilihan model terbaik

poisson_x <- model_predictors(poisson_train, response="case_count")
poisson_selection <- select_best_glm(poisson_train, response="case_count", predictors=poisson_x, family=poisson(link="log"), effect_name="incidence_rate_ratio")
poisson_best_fit <- poisson_selection$best$fit
poisson_best_null <- glm(case_count ~ 1, data=poisson_train, family=poisson(link="log"))
poisson_best_wald <- poisson_selection$best$wald
poisson_best_g2 <- poisson_selection$best$g2
poisson_selected_vars <- poisson_best_wald |> filter(p_value < 0.05) |> pull(variabel)

poisson_model_selection_table <- poisson_selection$summary |>
  mutate(p_value_G2=p_value_G2_tampil) |>
  select(model_ke, formula, jumlah_prediktor, jumlah_variabel_signifikan, AIC, G2, p_value_G2)

poisson_fit_table <- tibble(keterangan=c("Jumlah observasi training","Jumlah prediktor model terbaik","Null deviance model terbaik","Residual deviance model terbaik","AIC model terbaik","Log-likelihood model terbaik","Log-likelihood model kosong","McFadden pseudo R-square"), nilai=c(nrow(poisson_train), length(poisson_selection$best$vars), poisson_best_fit$null.deviance, poisson_best_fit$deviance, AIC(poisson_best_fit), as.numeric(logLik(poisson_best_fit)), as.numeric(logLik(poisson_best_null)), mcfadden_r2(poisson_best_fit,poisson_best_null)))

poisson_best_wald_show <- poisson_best_wald |> mutate(p_value=p_value_tampil) |> select(-p_value_tampil)
poisson_best_g2_show <- poisson_best_g2 |> mutate(p_value=p_value_tampil) |> select(-p_value_tampil)

safe_table(poisson_model_selection_table, "Perbandingan kandidat model Poisson berdasarkan jumlah variabel signifikan dan AIC", digits=4, full_width=TRUE)
Tabel 65. Perbandingan kandidat model Poisson berdasarkan jumlah variabel signifikan dan AIC
model_ke formula jumlah_prediktor jumlah_variabel_signifikan AIC G2 p_value_G2
4 case_count ~ log1p_lag_case_count + log1p_lag_total_fatalities + lag_conflict_count + region_label_Asia + region_label_Europe + region_label_Middle_East 6 5 2628.673 1576.612 < 0.001
5 case_count ~ log1p_lag_case_count + log1p_lag_total_fatalities + lag_conflict_count + region_label_Europe + region_label_Middle_East 5 5 2628.763 1574.522 < 0.001
3 case_count ~ year_centered + log1p_lag_case_count + log1p_lag_total_fatalities + lag_conflict_count + region_label_Asia + region_label_Europe + region_label_Middle_East 7 5 2630.443 1576.842 < 0.001
2 case_count ~ year_centered + log1p_lag_case_count + log1p_lag_total_fatalities + lag_conflict_count + lag_share_gov_actor + region_label_Asia + region_label_Europe + region_label_Middle_East 8 5 2632.297 1576.988 < 0.001
1 case_count ~ year_centered + log1p_lag_case_count + log1p_lag_total_fatalities + lag_conflict_count + lag_share_gov_actor + region_label_Americas + region_label_Asia + region_label_Europe + region_label_Middle_East 9 5 2634.290 1576.995 < 0.001
safe_table(poisson_fit_table, "Ringkasan kecocokan model terbaik Poisson tahunan", digits=4)
Tabel 66. Ringkasan kecocokan model terbaik Poisson tahunan
keterangan nilai
Jumlah observasi training 2332.0000
Jumlah prediktor model terbaik 6.0000
Null deviance model terbaik 2993.7489
Residual deviance model terbaik 1417.1366
AIC model terbaik 2628.6732
Log-likelihood model terbaik -1307.3366
Log-likelihood model kosong -2095.6427
McFadden pseudo R-square 0.3762
safe_table(poisson_best_g2_show, "Uji simultan G² model terbaik Poisson tahunan", digits=4)
Tabel 67. Uji simultan G² model terbaik Poisson tahunan
uji G2 df p_value keputusan penjelasan_hasil penanganan_jika_tidak_memenuhi
Uji likelihood ratio 1576.612 6 < 0.001 Tolak H0 Model dengan prediktor memberikan peningkatan kecocokan dibanding model kosong. Tidak diperlukan penanganan khusus untuk LRT.
safe_table(poisson_best_wald_show, "Uji Wald parsial dan IRR model terbaik Poisson tahunan", digits=4, full_width=TRUE)
Tabel 68. Uji Wald parsial dan IRR model terbaik Poisson tahunan
variabel estimate std_error z_value p_value incidence_rate_ratio ci_95
log1p_lag_case_count 2.0547 0.2477 8.2959 < 0.001 7.8045 4.803 - 12.682
region_label_Europe -0.9163 0.2249 -4.0742 < 0.001 0.4000 0.257 - 0.622
lag_conflict_count -0.1906 0.0484 -3.9377 < 0.001 0.8264 0.752 - 0.909
log1p_lag_total_fatalities 0.1243 0.0325 3.8245 < 0.001 1.1324 1.062 - 1.207
region_label_Middle_East -0.2955 0.1311 -2.2543 0.024 0.7442 0.576 - 0.962
region_label_Asia 0.1172 0.0805 1.4567 0.145 1.1243 0.96 - 1.316

Model terbaik Poisson adalah kandidat ke-4, dengan 5 variabel signifikan dan AIC sebesar 2628.673. Uji \(G^2\) model terbaik memiliki p-value < 0.001, sehingga prediktor pada model terbaik secara simultan berkontribusi terhadap expected count case_count. Variabel X yang mendukung case_count adalah: log1p_lag_case_count, region_label_Europe, lag_conflict_count, log1p_lag_total_fatalities, region_label_Middle_East. Prediksi berikutnya dilakukan menggunakan model terbaik tersebut.

6.6 Uji Asumsi Model Terbaik

6.6.1 Asumsi 1 — Overdispersi (Pearson Dispersion)

Hipotesis:
\(H_0\): Tidak terdapat overdispersi; varians kondisional relatif sesuai dengan mean kondisional.
\(H_1\): Terdapat overdispersi; varians kondisional lebih besar daripada mean kondisional.
Kriteria praktis: dispersion Pearson mendekati 1 menunjukkan tidak ada overdispersi berat.

pearson_chi_square <- sum(residuals(poisson_best_fit, type="pearson")^2)
df_residual <- df.residual(poisson_best_fit)
dispersion_pearson <- pearson_chi_square/df_residual
overdispersion_p_value <- pchisq(pearson_chi_square, df=df_residual, lower.tail=FALSE)

poisson_overdispersion_table <- tibble(
  uji="Pearson chi-square overdispersion",
  pearson_chi_square=pearson_chi_square,
  df_residual=df_residual,
  dispersion_pearson=dispersion_pearson,
  p_value=format_p(overdispersion_p_value),
  keputusan=case_when(
    dispersion_pearson < 1.5 ~ "Tidak ada indikasi overdispersion berat",
    dispersion_pearson < 2.5 ~ "Ada indikasi overdispersion ringan-sedang",
    TRUE ~ "Ada indikasi overdispersion kuat"
  ),
  penjelasan_hasil=case_when(
    dispersion_pearson < 1.5 ~ "Asumsi equidispersion relatif terpenuhi.",
    dispersion_pearson < 2.5 ~ "Terdapat indikasi overdispersi ringan-sedang.",
    TRUE ~ "Terdapat indikasi overdispersi kuat."
  ),
  penanganan_jika_tidak_memenuhi=case_when(
    dispersion_pearson < 1.5 ~ "Tidak diperlukan penanganan khusus.",
    TRUE ~ "Gunakan quasi-Poisson, negative binomial, atau robust standard error."
  )
)
safe_table(poisson_overdispersion_table, "Uji overdispersi Pearson model terbaik Poisson tahunan", digits=4, full_width=TRUE)
Tabel 69. Uji overdispersi Pearson model terbaik Poisson tahunan
uji pearson_chi_square df_residual dispersion_pearson p_value keputusan penjelasan_hasil penanganan_jika_tidak_memenuhi
Pearson chi-square overdispersion 2560.188 2325 1.1012 < 0.001 Tidak ada indikasi overdispersion berat Asumsi equidispersion relatif terpenuhi. Tidak diperlukan penanganan khusus.

6.6.2 Asumsi 2 — Proporsi Nilai Nol

Hipotesis/Kriteria:
\(H_0\): Proporsi nilai nol masih wajar untuk model Poisson.
\(H_1\): Proporsi nilai nol terlalu tinggi dan mengindikasikan potensi zero inflation.
Kriteria praktis: proporsi nol yang sangat besar perlu dibandingkan dengan konteks data dan hasil prediksi model.

poisson_zero_table <- tibble(
  ukuran=c("Jumlah observasi", "Jumlah case_count = 0", "Proporsi nilai nol"),
  nilai=c(nrow(poisson_train), sum(poisson_train$case_count == 0, na.rm=TRUE), mean(poisson_train$case_count == 0, na.rm=TRUE)),
  keterangan=c("Total observasi training", "Banyaknya observasi tanpa kejadian", "Proporsi nol yang tinggi dapat mengindikasikan zero inflation"),
  penjelasan_hasil=c("Total observasi yang digunakan pada model terbaik.", "Banyaknya observasi tanpa kejadian pada data training.", "Jika proporsi nilai nol sangat tinggi, terdapat potensi zero inflation."),
  penanganan_jika_tidak_memenuhi=c("Tidak diperlukan.", "Tidak diperlukan.", "Pertimbangkan zero-inflated Poisson, hurdle model, atau negative binomial jika juga terjadi overdispersi.")
)
safe_table(poisson_zero_table, "Pemeriksaan proporsi nilai nol pada data training Poisson", digits=4, full_width=TRUE)
Tabel 70. Pemeriksaan proporsi nilai nol pada data training Poisson
ukuran nilai keterangan penjelasan_hasil penanganan_jika_tidak_memenuhi
Jumlah observasi 2332.000 Total observasi training Total observasi yang digunakan pada model terbaik. Tidak diperlukan.
Jumlah case_count = 0 1812.000 Banyaknya observasi tanpa kejadian Banyaknya observasi tanpa kejadian pada data training. Tidak diperlukan.
Proporsi nilai nol 0.777 Proporsi nol yang tinggi dapat mengindikasikan zero inflation Jika proporsi nilai nol sangat tinggi, terdapat potensi zero inflation. Pertimbangkan zero-inflated Poisson, hurdle model, atau negative binomial jika juga terjadi overdispersi.

6.6.3 Asumsi 3 — Goodness-of-Fit (Deviance Test)

Hipotesis:
\(H_0\): Model Poisson memiliki kecocokan yang memadai terhadap data.
\(H_1\): Model Poisson tidak memiliki kecocokan yang memadai terhadap data.
Kriteria: p-value kecil menunjukkan indikasi lack of fit.

poisson_gof_table <- assumption_glm_table(poisson_best_fit, label="Poisson") |>
  filter(uji == "Goodness-of-fit deviance") |>
  mutate(p_value=p_value_tampil) |>
  select(-p_value_tampil)
safe_table(poisson_gof_table, "Goodness-of-Fit Deviance Test model terbaik Poisson", digits=4, full_width=TRUE)
Tabel 71. Goodness-of-Fit Deviance Test model terbaik Poisson
uji statistik df p_value keterangan penjelasan_hasil penanganan_jika_tidak_memenuhi
Goodness-of-fit deviance 1417.137 2325 1.000 p-value besar menunjukkan tidak ada bukti kuat lack of fit berdasarkan deviance. H0 gagal ditolak; tidak ada bukti kuat lack of fit. Tidak diperlukan penanganan khusus.

6.6.4 Asumsi 4 — Multikolinearitas (VIF)

Hipotesis/Kriteria:
\(H_0\): Tidak terdapat multikolinearitas berat antarprediktor.
\(H_1\): Terdapat multikolinearitas berat antarprediktor.
Kriteria praktis: VIF < 5 aman; VIF 5–10 perlu perhatian; VIF > 10 menunjukkan multikolinearitas berat.

poisson_vif_table <- vif_table(poisson_train, poisson_selection$best$vars)
poisson_corr_best_table <- selected_correlation_check(poisson_train, poisson_selection$best$vars)
safe_table(poisson_vif_table, "Uji multikolinearitas model terbaik Poisson menggunakan VIF", digits=3, full_width=TRUE)
Tabel 72. Uji multikolinearitas model terbaik Poisson menggunakan VIF
variabel VIF keterangan penjelasan_hasil penanganan_jika_tidak_memenuhi
log1p_lag_case_count 44.888 Indikasi multikolinearitas berat Terdapat indikasi multikolinearitas berat. Evaluasi prediktor yang redundant, hapus/gabung prediktor yang sangat mirip, atau gunakan reduksi dimensi.
log1p_lag_total_fatalities 16.325 Indikasi multikolinearitas berat Terdapat indikasi multikolinearitas berat. Evaluasi prediktor yang redundant, hapus/gabung prediktor yang sangat mirip, atau gunakan reduksi dimensi.
lag_conflict_count 13.738 Indikasi multikolinearitas berat Terdapat indikasi multikolinearitas berat. Evaluasi prediktor yang redundant, hapus/gabung prediktor yang sangat mirip, atau gunakan reduksi dimensi.
region_label_Asia 1.087 Tidak ada indikasi multikolinearitas berat Asumsi tidak ada multikolinearitas berat relatif terpenuhi. Tidak diperlukan penanganan khusus.
region_label_Europe 1.079 Tidak ada indikasi multikolinearitas berat Asumsi tidak ada multikolinearitas berat relatif terpenuhi. Tidak diperlukan penanganan khusus.
region_label_Middle_East 1.067 Tidak ada indikasi multikolinearitas berat Asumsi tidak ada multikolinearitas berat relatif terpenuhi. Tidak diperlukan penanganan khusus.
safe_table(poisson_corr_best_table, "Pemeriksaan korelasi tinggi antarprediktor model terbaik Poisson", digits=3, full_width=TRUE)
Tabel 73. Pemeriksaan korelasi tinggi antarprediktor model terbaik Poisson
variabel_1 variabel_2 korelasi abs_korelasi keterangan penjelasan_hasil penanganan_jika_tidak_memenuhi
log1p_lag_case_count log1p_lag_total_fatalities 0.946 0.946 Ada pasangan prediktor dengan korelasi tinggi; interpretasi koefisien perlu lebih hati-hati. Terdapat korelasi tinggi antarprediktor. Evaluasi prediktor yang redundant, gabungkan variabel yang mirip, atau gunakan reduksi dimensi.
log1p_lag_case_count lag_conflict_count 0.935 0.935 Ada pasangan prediktor dengan korelasi tinggi; interpretasi koefisien perlu lebih hati-hati. Terdapat korelasi tinggi antarprediktor. Evaluasi prediktor yang redundant, gabungkan variabel yang mirip, atau gunakan reduksi dimensi.
log1p_lag_total_fatalities lag_conflict_count 0.808 0.808 Ada pasangan prediktor dengan korelasi tinggi; interpretasi koefisien perlu lebih hati-hati. Terdapat korelasi tinggi antarprediktor. Evaluasi prediktor yang redundant, gabungkan variabel yang mirip, atau gunakan reduksi dimensi.

6.6.5 Asumsi 5 — Likelihood Ratio Test

Hipotesis:
\(H_0\): Semua koefisien prediktor sama dengan nol; model dengan prediktor tidak lebih baik daripada model kosong.
\(H_1\): Minimal ada satu koefisien prediktor tidak sama dengan nol; model dengan prediktor lebih baik daripada model kosong.

poisson_lrt_show <- poisson_best_g2 |> mutate(p_value=p_value_tampil) |> select(-p_value_tampil)
safe_table(poisson_lrt_show, "Likelihood Ratio Test atau uji G² model terbaik Poisson", digits=4, full_width=TRUE)
Tabel 74. Likelihood Ratio Test atau uji G² model terbaik Poisson
uji G2 df p_value keputusan penjelasan_hasil penanganan_jika_tidak_memenuhi
Uji likelihood ratio 1576.612 6 < 0.001 Tolak H0 Model dengan prediktor memberikan peningkatan kecocokan dibanding model kosong. Tidak diperlukan penanganan khusus untuk LRT.

6.6.6 Diagnostik Pengaruh Observasi

Diagnostik pengaruh observasi bukan uji hipotesis formal. Pemeriksaan ini digunakan untuk melihat apakah ada observasi yang terlalu memengaruhi estimasi model. Kriteria praktis: Cook’s distance > 1 perlu diperiksa, dan leverage tinggi dilihat dengan batas \(2p/n\).

poisson_influence_table <- influence_check_table(poisson_best_fit)
safe_table(poisson_influence_table, "Diagnostik pengaruh observasi model terbaik Poisson tahunan", digits=4, full_width=TRUE)
Tabel 75. Diagnostik pengaruh observasi model terbaik Poisson tahunan
diagnostik nilai keterangan penjelasan_hasil penanganan_jika_tidak_memenuhi
Maximum Cook’s distance 0.0819 Nilai Cook’s distance yang besar menunjukkan observasi yang berpengaruh kuat. Semakin kecil nilai maksimum Cook’s distance, semakin kecil pengaruh observasi ekstrem terhadap model. Periksa observasi dengan nilai Cook’s distance terbesar.
Jumlah Cook’s distance > 1 0.0000 Jumlah observasi dengan Cook’s distance > 1 perlu diperiksa lebih lanjut. Tidak ada observasi dengan Cook’s distance > 1. Cek data mentah, validasi outlier, dan lakukan analisis sensitivitas.
Maximum leverage 0.2471 Leverage tinggi menunjukkan observasi dengan kombinasi prediktor yang ekstrem. Nilai leverage maksimum menunjukkan observasi dengan kombinasi prediktor paling ekstrem. Periksa observasi dengan leverage tinggi.
Jumlah leverage > 2p/n 230.0000 Aturan praktis leverage tinggi menggunakan batas 2p/n.  Ada observasi dengan leverage tinggi. Jalankan analisis sensitivitas dengan dan tanpa observasi berpengaruh.

6.7 Prediksi dengan model terbaik

poisson_pred_mu <- predict(poisson_best_fit, newdata=poisson_test, type="response")
poisson_pred_table <- tibble(actual=poisson_test$case_count, predicted_mu=poisson_pred_mu, residual=actual-predicted_mu, abs_error=abs(residual), squared_error=residual^2)
poisson_metrics <- poisson_pred_table |> summarise(MAE=mean(abs_error), RMSE=sqrt(mean(squared_error)), mean_actual=mean(actual), mean_predicted=mean(predicted_mu), correlation_actual_predicted=cor(actual,predicted_mu))
safe_table(poisson_metrics, "Metrik prediksi testing model terbaik Poisson tahunan", digits=4)
Tabel 76. Metrik prediksi testing model terbaik Poisson tahunan
MAE RMSE mean_actual mean_predicted correlation_actual_predicted
0.3721 0.7836 0.4195 0.3873 0.6662
poisson_pred_table |> ggplot(aes(x=predicted_mu, y=actual))+geom_point(alpha=.45,color="#172033")+geom_abline(slope=1,intercept=0,linetype="dashed",color="#e11d48")+labs(title="Aktual vs Prediksi Expected Count", subtitle="Titik yang dekat dengan garis diagonal menunjukkan prediksi yang lebih akurat.", x="Predicted expected count", y="Actual count")+theme_report()
Gambar 8. Scatterplot aktual versus prediksi expected count model terbaik Poisson.

Gambar 8. Scatterplot aktual versus prediksi expected count model terbaik Poisson.

poisson_main_x <- poisson_selected_vars[1]
if(is.na(poisson_main_x) || length(poisson_main_x)==0){poisson_main_x <- poisson_selection$best$vars[1]}

poisson_profile <- make_profile_grid(poisson_train, poisson_selection$best$vars, xvar=poisson_main_x, n=120)
poisson_profile_pred <- predict(poisson_best_fit, newdata=poisson_profile, type="link", se.fit=TRUE)
poisson_profile <- poisson_profile |>
  mutate(
    fit_link = poisson_profile_pred$fit,
    se_link = poisson_profile_pred$se.fit,
    predicted_mu = exp(fit_link),
    ci_low = exp(fit_link - 1.96*se_link),
    ci_high = exp(fit_link + 1.96*se_link)
  )

poisson_profile |>
  ggplot(aes(x=.data[[poisson_main_x]], y=predicted_mu))+
  geom_ribbon(aes(ymin=ci_low, ymax=ci_high), fill="#6d28d9", alpha=.18)+
  geom_line(linewidth=1.25, color="#6d28d9")+
  labs(
    title="Kurva Prediksi Expected Violence Case Count",
    subtitle="Garis menunjukkan expected count; arsiran menunjukkan confidence interval 95%. Prediktor lain ditahan pada nilai median.",
    x=poisson_main_x,
    y="Predicted expected count"
  )+
  theme_report()
Gambar 9. Kurva prediksi expected count model terbaik Poisson dengan confidence interval 95 persen.

Gambar 9. Kurva prediksi expected count model terbaik Poisson dengan confidence interval 95 persen.

Prediksi Poisson menghasilkan expected count. MAE sebesar 0.372 berarti rata-rata kesalahan absolut prediksi sekitar nilai tersebut dalam satuan jumlah kejadian. Perbandingan mean aktual (0.42) dan mean prediksi (0.387) membantu membaca apakah model cenderung underpredict atau overpredict secara rata-rata.

6.8 Korelasi prediktor Poisson

poisson_corr_top <- correlation_pairs(poisson_train |> select(all_of(poisson_x)), top_n=10)
safe_table(poisson_corr_top, "Sepuluh pasangan prediktor Poisson dengan korelasi absolut terbesar", digits=3)
Tabel 77. Sepuluh pasangan prediktor Poisson dengan korelasi absolut terbesar
variabel_1 variabel_2 korelasi abs_korelasi
log1p_lag_case_count log1p_lag_total_fatalities 0.946 0.946
log1p_lag_case_count lag_conflict_count 0.935 0.935
log1p_lag_total_fatalities lag_conflict_count 0.808 0.808
log1p_lag_total_fatalities lag_share_gov_actor 0.620 0.620
log1p_lag_case_count lag_share_gov_actor 0.526 0.526
lag_conflict_count lag_share_gov_actor 0.357 0.357
region_label_Americas region_label_Asia -0.201 0.201
region_label_Asia region_label_Middle_East -0.175 0.175
region_label_Asia region_label_Europe -0.174 0.174
region_label_Americas region_label_Middle_East -0.160 0.160

7 Perbandingan Empat Model

comparison_table <- tibble(
  model=c("Logistik biner","Logistik multinomial","Logistik ordinal","Poisson tahunan"),
  respons=c("food_crisis","choice_code","gov_confidence_ord","case_count"),
  sifat_respons=c("Biner","Nominal lebih dari dua kategori","Ordinal","Hitungan non-negatif"),
  kriteria_model_terbaik=c("Jumlah variabel signifikan, lalu AIC","Jumlah variabel signifikan, lalu AIC","Jumlah variabel signifikan, lalu AIC","Jumlah variabel signifikan, lalu AIC"),
  uji_model=c("G² likelihood ratio","G² likelihood ratio","G² likelihood ratio","G² likelihood ratio"),
  uji_koefisien=c("Wald z-test","Wald z-test","Wald z-test","Wald z-test"),
  ukuran_efek=c("Odds ratio","Relative risk ratio","Odds ratio","Incidence rate ratio"),
  bentuk_prediksi=c("Probabilitas dan kelas 0/1","Probabilitas tiap kategori","Probabilitas tiap tingkat ordinal","Expected count")
)
safe_table(comparison_table, "Perbandingan model berdasarkan skala respons, pemilihan model, uji model, ukuran efek, dan bentuk prediksi", full_width=TRUE)
Tabel 78. Perbandingan model berdasarkan skala respons, pemilihan model, uji model, ukuran efek, dan bentuk prediksi
model respons sifat_respons kriteria_model_terbaik uji_model uji_koefisien ukuran_efek bentuk_prediksi
Logistik biner food_crisis Biner Jumlah variabel signifikan, lalu AIC G² likelihood ratio Wald z-test Odds ratio Probabilitas dan kelas 0/1
Logistik multinomial choice_code Nominal lebih dari dua kategori Jumlah variabel signifikan, lalu AIC G² likelihood ratio Wald z-test Relative risk ratio Probabilitas tiap kategori
Logistik ordinal gov_confidence_ord Ordinal Jumlah variabel signifikan, lalu AIC G² likelihood ratio Wald z-test Odds ratio Probabilitas tiap tingkat ordinal
Poisson tahunan case_count Hitungan non-negatif Jumlah variabel signifikan, lalu AIC G² likelihood ratio Wald z-test Incidence rate ratio Expected count

Secara keseluruhan, model dipilih berdasarkan skala respons dan kemudian diseleksi berdasarkan jumlah variabel signifikan terlebih dahulu, baru AIC. Pendekatan ini menjaga agar model akhir tidak semata-mata mengejar AIC kecil, tetapi tetap mempertahankan variabel X yang secara statistik mendukung variabel Y. Setelah model terbaik diperoleh, interpretasi diarahkan pada variabel X yang signifikan, kemudian prediksi dilakukan memakai model terbaik pada data testing.