Sumber Data: University Student Allergy Dataset — Mendeley Data (Reza & Paul, 2026)
Metode: Regresi Logistik Biner dengan Seleksi Variabel Stepwise Bidirectional (AIC)
Dataset: 854 observasi mahasiswa | Variabel respons: Status Alergi (Positif/Negatif)


1 Pendahuluan

Penyakit alergi merupakan salah satu masalah kesehatan global dengan prevalensi yang terus meningkat, terutama pada kelompok usia muda dan produktif. Urbanisasi, perubahan gaya hidup, polusi lingkungan, serta paparan alergen domestik menjadi faktor yang berkontribusi terhadap peningkatan kejadian alergi dalam beberapa dekade terakhir. Mahasiswa termasuk kelompok yang rentan karena mengalami perubahan pola hidup, tekanan akademik, pola tidur yang tidak teratur, serta perubahan lingkungan tempat tinggal selama masa perkuliahan.

Kejadian alergi bersifat multifaktorial karena dipengaruhi oleh interaksi faktor genetik, lingkungan, dan perilaku individu. Oleh karena itu, diperlukan metode statistik yang mampu mengevaluasi pengaruh berbagai faktor risiko secara simultan terhadap status alergi. Regresi logistik biner dipilih karena sesuai untuk memodelkan variabel respons dikotomis dan mampu menghasilkan interpretasi dalam bentuk odds ratio untuk mengukur besar risiko masing-masing faktor.

Penelitian mengenai faktor risiko alergi pada populasi mahasiswa masih relatif terbatas, khususnya yang menggunakan pendekatan pemodelan statistik inferensial berbasis regresi logistik biner. Oleh sebab itu, penelitian ini dilakukan untuk mengidentifikasi faktor-faktor yang berhubungan dengan kejadian alergi pada mahasiswa serta membangun model regresi logistik biner yang mampu menjelaskan hubungan antara faktor risiko dan status alergi mahasiswa.

2 Persiapan: Package dan Data

2.1 Instalasi & Load Package

required_packages <- c("dplyr", "ggplot2", "broom", "knitr",
                        "car", "readr", "scales", "ResourceSelection")

missing_packages <- required_packages[
  !vapply(required_packages, requireNamespace, logical(1), quietly = TRUE)
]
if (length(missing_packages) > 0) install.packages(missing_packages)

invisible(lapply(required_packages, library, character.only = TRUE))

2.2 Load dan Transformasi Data

Data dimuat dari file CSV berdelimiter titik-koma (;). Seluruh variabel kemudian ditransformasi: variabel kategorik diubah menjadi factor berlabel Bahasa Indonesia, dan kategori referensi ditetapkan secara eksplisit untuk keperluan interpretasi model.

# ---- Ganti path sesuai lokasi file di komputer Anda ----
allergy <- read_delim(
  file.choose(),
  delim = ";",
  show_col_types = FALSE
)
allergy
alergi <- allergy %>%
  mutate(
    # Outcome biner
    status_alergi_num = ifelse(target_class == "positive", 1L, 0L),
    status_alergi     = ifelse(target_class == "positive", "Alergi", "Tidak Alergi"),

    # Variabel numerik
    usia        = age,
    berat_badan = weight_kg,

    # Variabel kategorik → factor berlabel Indonesia
    jenis_kelamin     = factor(gender,
                               levels = c("Female", "Male"),
                               labels = c("Perempuan", "Laki-laki")),
    kepemilikan_hewan = factor(pet_home,
                               levels = c("No", "Yes"),
                               labels = c("Tidak", "Ya")),
    tempat_tinggal    = factor(residence,
                               levels = c("Urban", "Suburban",
                                          "Rural village", "Industrial area"),
                               labels = c("Perkotaan", "Pinggiran Kota",
                                          "Perdesaan", "Kawasan Industri")),
    lingkungan_jamur  = factor(mold_env,
                               levels = c("No", "Yes"),
                               labels = c("Tidak", "Ya")),
    riwayat_keluarga  = factor(family_allergy,
                               levels = c("No Allergy", "Allergic Rhinitis", "Asthma",
                                          "Atopic Dermatitis", "Drug Allergy", "Food Allergy"),
                               labels = c("Tidak Ada Alergi", "Rinitis Alergi", "Asma",
                                          "Dermatitis Atopik", "Alergi Obat", "Alergi Makanan")),
    perokok_aktif     = factor(smoke_now,
                               levels = c("No", "Yes"),
                               labels = c("Tidak", "Ya")),
    paparan_asap      = factor(secondhand_smoke,
                               levels = c("No", "Yes"),
                               labels = c("Tidak", "Ya"))
  ) %>%
  mutate(
    jenis_kelamin     = relevel(jenis_kelamin,     ref = "Perempuan"),
    kepemilikan_hewan = relevel(kepemilikan_hewan, ref = "Tidak"),
    tempat_tinggal    = relevel(tempat_tinggal,    ref = "Perkotaan"),
    lingkungan_jamur  = relevel(lingkungan_jamur,  ref = "Tidak"),
    riwayat_keluarga  = relevel(riwayat_keluarga,  ref = "Tidak Ada Alergi"),
    perokok_aktif     = relevel(perokok_aktif,     ref = "Tidak"),
    paparan_asap      = relevel(paparan_asap,      ref = "Tidak")
  )

3 Statistika Deskriptif

3.1 Contoh Data

alergi %>%
  transmute(
    Jenis_Kelamin      = jenis_kelamin,
    Usia               = usia,
    Berat_Badan        = berat_badan,
    Kepemilikan_Hewan  = kepemilikan_hewan,
    Tempat_Tinggal     = tempat_tinggal,
    Lingkungan_Jamur   = lingkungan_jamur,
    Riwayat_Keluarga   = riwayat_keluarga,
    Perokok_Aktif      = perokok_aktif,
    Paparan_Asap_Pasif = paparan_asap,
    Status_Alergi      = status_alergi
  ) %>%
  head(8) %>%
  kable(caption = "Tabel 1. Contoh Data Setelah Transformasi (8 Observasi Pertama)")
Table 3.1: Tabel 1. Contoh Data Setelah Transformasi (8 Observasi Pertama)
Jenis_Kelamin Usia Berat_Badan Kepemilikan_Hewan Tempat_Tinggal Lingkungan_Jamur Riwayat_Keluarga Perokok_Aktif Paparan_Asap_Pasif Status_Alergi
Laki-laki 24 62 Ya Pinggiran Kota Tidak Asma Tidak Ya Alergi
Laki-laki 24 57 Ya Perdesaan Ya Tidak Ada Alergi Tidak Tidak Tidak Alergi
Laki-laki 22 86 Tidak Pinggiran Kota Ya Alergi Makanan Ya Tidak Alergi
Laki-laki 25 63 Ya Perdesaan Ya Rinitis Alergi Ya Ya Alergi
Laki-laki 23 63 Tidak Perkotaan Tidak Tidak Ada Alergi Tidak Tidak Alergi
Perempuan 23 65 Tidak Perkotaan Tidak Alergi Obat Tidak Tidak Alergi
Laki-laki 21 100 Tidak Perdesaan Tidak Asma Tidak Tidak Alergi
Laki-laki 25 64 Ya Perkotaan Tidak Tidak Ada Alergi Tidak Tidak Tidak Alergi

3.2 Distribusi Variabel Respons

outcome_tab <- alergi %>%
  count(status_alergi) %>%
  mutate(Persentase = round(n / sum(n) * 100, 2))

kable(outcome_tab,
      col.names = c("Status Alergi", "Frekuensi", "Persentase (%)"),
      caption   = "Tabel 2. Distribusi Status Alergi")
Table 3.2: Tabel 2. Distribusi Status Alergi
Status Alergi Frekuensi Persentase (%)
Alergi 397 46.49
Tidak Alergi 457 53.51
ggplot(alergi, aes(x = status_alergi, fill = status_alergi)) +
  geom_bar(width = 0.6) +
  geom_text(
    stat = "count",
    aes(label = paste0(after_stat(count),
                       "\n(", round(after_stat(count) / nrow(alergi) * 100, 1), "%)")),
    vjust = -0.3, lineheight = 0.9, fontface = "bold", size = 4
  ) +
  scale_fill_manual(values = c("Alergi" = "#e74c3c", "Tidak Alergi" = "#3498db")) +
  scale_y_continuous(expand = expansion(mult = c(0, 0.15))) +
  labs(title = "Distribusi Status Alergi Responden",
       x = "Status Alergi", y = "Jumlah Mahasiswa") +
  theme_minimal(base_size = 13) +
  theme(legend.position  = "none",
        plot.title       = element_text(face = "bold", hjust = 0.5),
        panel.grid.major.x = element_blank())
Gambar 1. Distribusi Status Alergi Responden

Figure 3.1: Gambar 1. Distribusi Status Alergi Responden

Interpretasi: Dari 854 responden, 397 (46,49%) berstatus alergi positif dan 457 (53,51%) berstatus alergi negatif. Proporsi yang relatif seimbang mengindikasikan tidak adanya ketidakseimbangan kelas ekstrem, sehingga analisis regresi logistik dapat dilakukan tanpa prosedur penyeimbangan tambahan.

3.3 Statistik Deskriptif Variabel Numerik

tabel_desc <- data.frame(
  Variabel = c("Usia (tahun)", "Berat Badan (kg)"),
  Minimum  = c(min(alergi$usia), min(alergi$berat_badan)),
  Q1       = c(quantile(alergi$usia, .25), quantile(alergi$berat_badan, .25)),
  Median   = c(median(alergi$usia), median(alergi$berat_badan)),
  Mean     = c(round(mean(alergi$usia), 2), round(mean(alergi$berat_badan), 2)),
  Q3       = c(quantile(alergi$usia, .75), quantile(alergi$berat_badan, .75)),
  Maksimum = c(max(alergi$usia), max(alergi$berat_badan)),
  SD       = c(round(sd(alergi$usia), 2), round(sd(alergi$berat_badan), 2))
)

kable(tabel_desc, align = "c",
      caption = " Statistik Deskriptif Variabel Numerik (n = 854)")
Table 3.3: Statistik Deskriptif Variabel Numerik (n = 854)
Variabel Minimum Q1 Median Mean Q3 Maksimum SD
Usia (tahun) 18 20 22 21.67 23 50 1.88
Berat Badan (kg) 37 57 65 66.36 75 130 13.44

Interpretasi:
Variabel usia memiliki rata-rata sekitar 21–22 tahun dengan sebaran yang relatif homogen, menunjukkan bahwa mayoritas responden berada pada rentang usia mahasiswa aktif. Variabel berat badan memiliki variasi yang lebih besar dibandingkan usia, yang ditunjukkan oleh nilai simpangan baku yang lebih tinggi. Hal ini mengindikasikan adanya heterogenitas karakteristik fisik antarresponden.

3.4 Distribusi Variabel Kategorik

desc_cat <- bind_rows(
  alergi %>% count(jenis_kelamin)     %>% mutate(Variabel = "Jenis Kelamin",           Kategori = as.character(jenis_kelamin))     %>% select(Variabel, Kategori, Frekuensi = n),
  alergi %>% count(tempat_tinggal)    %>% mutate(Variabel = "Tempat Tinggal",           Kategori = as.character(tempat_tinggal))    %>% select(Variabel, Kategori, Frekuensi = n),
  alergi %>% count(kepemilikan_hewan) %>% mutate(Variabel = "Kepemilikan Hewan",        Kategori = as.character(kepemilikan_hewan)) %>% select(Variabel, Kategori, Frekuensi = n),
  alergi %>% count(lingkungan_jamur)  %>% mutate(Variabel = "Lingkungan Berjamur",      Kategori = as.character(lingkungan_jamur))  %>% select(Variabel, Kategori, Frekuensi = n),
  alergi %>% count(riwayat_keluarga)  %>% mutate(Variabel = "Riwayat Alergi Keluarga", Kategori = as.character(riwayat_keluarga))  %>% select(Variabel, Kategori, Frekuensi = n),
  alergi %>% count(perokok_aktif)     %>% mutate(Variabel = "Perokok Aktif",            Kategori = as.character(perokok_aktif))     %>% select(Variabel, Kategori, Frekuensi = n),
  alergi %>% count(paparan_asap)      %>% mutate(Variabel = "Paparan Asap Rokok Pasif", Kategori = as.character(paparan_asap))      %>% select(Variabel, Kategori, Frekuensi = n)
) %>%
  mutate(Persentase = paste0(round(Frekuensi / nrow(alergi) * 100, 2), "%"))

kable(desc_cat, caption = "Distribusi Variabel Prediktor Kategorik (n = 854)")
Table 3.4: Distribusi Variabel Prediktor Kategorik (n = 854)
Variabel Kategori Frekuensi Persentase
Jenis Kelamin Perempuan 185 21.66%
Jenis Kelamin Laki-laki 669 78.34%
Tempat Tinggal Perkotaan 358 41.92%
Tempat Tinggal Pinggiran Kota 156 18.27%
Tempat Tinggal Perdesaan 207 24.24%
Tempat Tinggal Kawasan Industri 133 15.57%
Kepemilikan Hewan Tidak 673 78.81%
Kepemilikan Hewan Ya 181 21.19%
Lingkungan Berjamur Tidak 548 64.17%
Lingkungan Berjamur Ya 306 35.83%
Riwayat Alergi Keluarga Tidak Ada Alergi 352 41.22%
Riwayat Alergi Keluarga Rinitis Alergi 167 19.56%
Riwayat Alergi Keluarga Asma 127 14.87%
Riwayat Alergi Keluarga Dermatitis Atopik 40 4.68%
Riwayat Alergi Keluarga Alergi Obat 18 2.11%
Riwayat Alergi Keluarga Alergi Makanan 150 17.56%
Perokok Aktif Tidak 752 88.06%
Perokok Aktif Ya 102 11.94%
Paparan Asap Rokok Pasif Tidak 679 79.51%
Paparan Asap Rokok Pasif Ya 175 20.49%

Interpretasi:
Sebagian besar responden berjenis kelamin laki-laki dan tidak memiliki hewan peliharaan. Mayoritas mahasiswa juga tidak terpapar lingkungan berjamur maupun asap rokok pasif. Pada variabel riwayat alergi keluarga, sebagian responden memiliki riwayat kondisi atopik tertentu, yang mengindikasikan adanya potensi faktor genetik dalam kejadian alergi mahasiswa.


4 Pemeriksaan Asumsi

4.1 Uji Linearitas — Box–Tidwell

Uji Box–Tidwell memeriksa apakah hubungan antara variabel kontinu (usia dan berat badan) dengan log-odds bersifat linear, dengan menyertakan suku interaksi \(X \cdot \ln(X)\) ke dalam model. Asumsi terpenuhi jika koefisien suku interaksi tidak signifikan (\(p > 0{,}05\)).

\[\text{logit}[\pi(x)] = \beta_0 + \beta_1 X + \beta_2 (X \cdot \ln X)\]

alergi_bt <- alergi %>%
  mutate(
    usia_log        = usia        * log(usia        + 1),
    berat_badan_log = berat_badan * log(berat_badan + 1)
  )

fit_bt <- glm(
  status_alergi_num ~
    jenis_kelamin + usia + usia_log +
    berat_badan + berat_badan_log +
    kepemilikan_hewan + lingkungan_jamur + tempat_tinggal +
    riwayat_keluarga + perokok_aktif + paparan_asap,
  data = alergi_bt, family = binomial
)

bt_hasil <- broom::tidy(fit_bt) %>%
  filter(grepl("log", term)) %>%
  select(Prediktor = term, Estimasi = estimate,
         Galat_Baku = std.error, z_value = statistic, p_value = p.value) %>%
  mutate(
    across(where(is.numeric), ~ round(.x, 4)),
    Kesimpulan = ifelse(p_value > 0.05, "✅ Terpenuhi", "❌ Tidak Terpenuhi")
  )

kable(bt_hasil,
      caption = "Tabel 5. Hasil Uji Linearitas Box–Tidwell untuk Variabel Kontinu")
Table 4.1: Tabel 5. Hasil Uji Linearitas Box–Tidwell untuk Variabel Kontinu
Prediktor Estimasi Galat_Baku z_value p_value Kesimpulan
usia_log 0.1898 0.2556 0.7424 0.4578 ✅ Terpenuhi
berat_badan_log -0.0075 0.0399 -0.1888 0.8503 ✅ Terpenuhi

Kesimpulan: Koefisien suku interaksi untuk usia (\(p = 0{,}458\)) dan berat badan (\(p = 0{,}850\)) tidak signifikan secara statistik. Asumsi linearitas pada skala logit terpenuhi untuk kedua variabel kontinu — tidak diperlukan transformasi tambahan.

4.2 Uji Multikolinearitas — GVIF

Multikolinearitas diperiksa menggunakan Generalized Variance Inflation Factor (GVIF), perluasan VIF untuk variabel kategorik politomus. Kriteria: \(\text{GVIF}^{1/(2 \cdot \text{df})} < 2\).

fit_vif_penuh <- glm(
  status_alergi_num ~
    jenis_kelamin + usia + berat_badan +
    kepemilikan_hewan + tempat_tinggal + lingkungan_jamur +
    riwayat_keluarga + perokok_aktif + paparan_asap,
  data = alergi, family = binomial(link = "logit")
)

vif_raw  <- vif(fit_vif_penuh)
vif_df   <- as.data.frame(vif_raw)

vif_tabel <- data.frame(
  Prediktor = rownames(vif_df),
  GVIF      = round(vif_df[, 1], 4),
  df        = vif_df[, 2],
  GVIF_adj  = round(vif_df[, 3], 4)
) %>%
  mutate(Kesimpulan = ifelse(GVIF_adj < 2, "✅ Aman", "⚠️ Bermasalah"))

kable(vif_tabel,
      col.names = c("Prediktor", "GVIF", "df",
                    "GVIF¹⁄⁽²·ᵈᶠ⁾", "Kesimpulan"),
      caption   = "Tabel 6. Hasil Uji Multikolinearitas GVIF Model Penuh")
Table 4.2: Tabel 6. Hasil Uji Multikolinearitas GVIF Model Penuh
Prediktor GVIF df GVIF¹⁄⁽²·ᵈᶠ⁾ Kesimpulan
jenis_kelamin 1.2657 1 1.1250 ✅ Aman
usia 1.0265 1 1.0132 ✅ Aman
berat_badan 1.2014 1 1.0961 ✅ Aman
kepemilikan_hewan 1.0194 1 1.0097 ✅ Aman
tempat_tinggal 1.0782 3 1.0126 ✅ Aman
lingkungan_jamur 1.0557 1 1.0275 ✅ Aman
riwayat_keluarga 1.0997 5 1.0095 ✅ Aman
perokok_aktif 1.1040 1 1.0507 ✅ Aman
paparan_asap 1.0676 1 1.0333 ✅ Aman

Kesimpulan: Seluruh nilai \(\text{GVIF}^{1/(2 \cdot \text{df})}\) jauh di bawah ambang batas 2,0 (nilai tertinggi: 1,1250 pada jenis kelamin). Tidak terdapat masalah multikolinearitas — estimasi koefisien dapat dianggap stabil dan dapat diandalkan.


5 Pemodelan Regresi Logistik Biner

5.1 Pembagian Data Training dan Testing

Data dibagi menjadi 80% data training dan 20% data testing menggunakan stratified random sampling berdasarkan variabel respons, agar proporsi kelas tetap seimbang pada kedua subset.

set.seed(123)

stratified_split <- function(y, prop = 0.8) {
  idx_by_class <- split(seq_along(y), y)
  train_idx    <- lapply(idx_by_class,
                         function(idx) sample(idx, size = floor(length(idx) * prop)))
  unlist(train_idx, use.names = FALSE)
}

train_id      <- stratified_split(alergi$status_alergi_num, prop = 0.8)
data_training <- alergi[ train_id, ]
data_testing  <- alergi[-train_id, ]

split_info <- data.frame(
  Subset            = c("Training", "Testing"),
  n                 = c(nrow(data_training), nrow(data_testing)),
  Alergi_Positif    = c(sum(data_training$status_alergi_num),
                        sum(data_testing$status_alergi_num)),
  Proporsi_Positif  = c(
    paste0(round(mean(data_training$status_alergi_num) * 100, 2), "%"),
    paste0(round(mean(data_testing$status_alergi_num)  * 100, 2), "%")
  )
)

kable(split_info,
      col.names = c("Subset", "n", "Alergi Positif", "Proporsi Positif"),
      caption   = "Tabel 7. Ringkasan Pembagian Data Training dan Testing")
Table 5.1: Tabel 7. Ringkasan Pembagian Data Training dan Testing
Subset n Alergi Positif Proporsi Positif
Training 682 317 46.48%
Testing 172 80 46.51%

5.2 Model Penuh

Model penuh dibangun dengan memasukkan seluruh 9 variabel kandidat ke dalam model regresi logistik biner menggunakan data training.

\[\text{logit}[\pi(\mathbf{x})] = \beta_0 + \beta_1 x_1 + \beta_2 x_2 + \cdots + \beta_p x_p\]

model_penuh <- glm(
  status_alergi_num ~
    jenis_kelamin + usia + berat_badan +
    kepemilikan_hewan + tempat_tinggal + lingkungan_jamur +
    riwayat_keluarga + perokok_aktif + paparan_asap,
  data   = data_training,
  family = binomial(link = "logit")
)

penuh_tidy <- broom::tidy(model_penuh) %>%
  mutate(
    across(c(estimate, std.error, statistic), ~ round(.x, 4)),
    p.value    = signif(p.value, 3),
    Keterangan = ifelse(p.value < 0.05, "* Signifikan", "Tidak Signifikan")
  )

kable(penuh_tidy,
      col.names = c("Variabel", "β", "Galat Baku", "z", "p-value", "Keterangan"),
      caption   = "Tabel 8. Estimasi Parameter Model Penuh (n training = 682)")
Table 5.2: Tabel 8. Estimasi Parameter Model Penuh (n training = 682)
Variabel β Galat Baku z p-value Keterangan
(Intercept) -0.6139 1.0251 -0.5989 5.49e-01 Tidak Signifikan
jenis_kelaminLaki-laki -0.4158 0.2252 -1.8464 6.48e-02 Tidak Signifikan
usia 0.0024 0.0427 0.0557 9.56e-01 Tidak Signifikan
berat_badan -0.0070 0.0067 -1.0434 2.97e-01 Tidak Signifikan
kepemilikan_hewanYa 0.3095 0.2056 1.5058 1.32e-01 Tidak Signifikan
tempat_tinggalPinggiran Kota 0.2206 0.2334 0.9453 3.45e-01 Tidak Signifikan
tempat_tinggalPerdesaan 0.4999 0.2114 2.3651 1.80e-02 * Signifikan
tempat_tinggalKawasan Industri 0.7637 0.2491 3.0662 2.17e-03 * Signifikan
lingkungan_jamurYa 0.1402 0.1754 0.7996 4.24e-01 Tidak Signifikan
riwayat_keluargaRinitis Alergi 1.1863 0.2298 5.1631 2.00e-07 * Signifikan
riwayat_keluargaAsma 1.0571 0.2504 4.2223 2.42e-05 * Signifikan
riwayat_keluargaDermatitis Atopik 1.3036 0.3977 3.2781 1.05e-03 * Signifikan
riwayat_keluargaAlergi Obat 0.2584 0.6114 0.4226 6.73e-01 Tidak Signifikan
riwayat_keluargaAlergi Makanan 1.0910 0.2324 4.6948 2.70e-06 * Signifikan
perokok_aktifYa 0.6918 0.2771 2.4969 1.25e-02 * Signifikan
paparan_asapYa 0.3126 0.2140 1.4606 1.44e-01 Tidak Signifikan

Interpretasi:
Pada model penuh, beberapa variabel menunjukkan hubungan signifikan terhadap kejadian alergi mahasiswa. Variabel tempat tinggal di kawasan industri, riwayat alergi keluarga, dan merokok aktif memiliki nilai p-value kurang dari 0,05 sehingga berasosiasi secara signifikan dengan status alergi. Sementara itu, variabel usia, berat badan, dan paparan jamur lingkungan belum menunjukkan pengaruh signifikan pada model penuh.

5.3 Seleksi Model — Stepwise Bidirectional AIC

Seleksi variabel dilakukan dengan metode stepwise bidirectional berbasis AIC. Algoritma mempertimbangkan penambahan (forward) dan penghapusan (backward) variabel secara bergantian hingga tidak ada lagi penurunan AIC.

\[\text{AIC} = -2\ln(L) + 2k\]

model_final <- step(model_penuh, direction = "both", trace = 1)
stepwise_tab <- data.frame(
  Langkah              = c("Awal", "1", "2", "3", "Final"),
  `Variabel Dieliminasi` = c("Model Penuh (9 variabel)",
                              "Eliminasi: Usia",
                              "Eliminasi: Paparan Jamur Lingkungan",
                              "Eliminasi: Berat Badan",
                              "Tidak ada eliminasi lebih lanjut"),
  AIC = c(885.35, 883.35, 881.99, 881.02, 881.02),
  ΔAIC = c("—", "↓ 2,00", "↓ 1,36", "↓ 0,97", "✓")
)

kable(stepwise_tab,
      col.names = c("Langkah", "Variabel Dieliminasi/Ditambah", "AIC", "ΔAIC"),
      caption   = "Tabel 9. Ringkasan Proses Seleksi Stepwise Bidirectional AIC")
Table 5.3: Tabel 9. Ringkasan Proses Seleksi Stepwise Bidirectional AIC
Langkah Variabel Dieliminasi/Ditambah AIC ΔAIC
Awal Model Penuh (9 variabel) 885.35
1 Eliminasi: Usia 883.35 ↓ 2,00
2 Eliminasi: Paparan Jamur Lingkungan 881.99 ↓ 1,36
3 Eliminasi: Berat Badan 881.02 ↓ 0,97
Final Tidak ada eliminasi lebih lanjut 881.02

Interpretasi:
Proses seleksi stepwise menghasilkan eliminasi beberapa variabel yang kurang informatif terhadap model. Penurunan nilai AIC pada setiap tahap menunjukkan bahwa model menjadi lebih efisien tanpa kehilangan kemampuan menjelaskan variasi data. Model final dipilih karena memiliki nilai AIC terkecil sehingga dianggap paling optimal.

5.4 Model Final

Model final memuat 6 variabel prediktor: jenis kelamin, kepemilikan hewan peliharaan, jenis tempat tinggal, riwayat alergi keluarga, merokok aktif, dan paparan asap rokok pasif.

final_tidy <- broom::tidy(model_final) %>%
  mutate(
    across(c(estimate, std.error, statistic), ~ round(.x, 4)),
    p.value    = signif(p.value, 3),
    Keterangan = ifelse(p.value < 0.05, "* Signifikan", "Tidak Signifikan")
  )

kable(final_tidy,
      col.names = c("Variabel", "β", "Galat Baku", "z", "p-value", "Keterangan"),
      caption   = "Tabel 10. Estimasi Parameter Model Final (n training = 682)")
Table 5.4: Tabel 10. Estimasi Parameter Model Final (n training = 682)
Variabel β Galat Baku z p-value Keterangan
(Intercept) -0.9431 0.2333 -4.0429 5.28e-05 * Signifikan
jenis_kelaminLaki-laki -0.5034 0.2078 -2.4222 1.54e-02 * Signifikan
kepemilikan_hewanYa 0.3247 0.2043 1.5890 1.12e-01 Tidak Signifikan
tempat_tinggalPinggiran Kota 0.2385 0.2327 1.0250 3.05e-01 Tidak Signifikan
tempat_tinggalPerdesaan 0.5375 0.2090 2.5710 1.01e-02 * Signifikan
tempat_tinggalKawasan Industri 0.7868 0.2478 3.1745 1.50e-03 * Signifikan
riwayat_keluargaRinitis Alergi 1.2224 0.2262 5.4033 1.00e-07 * Signifikan
riwayat_keluargaAsma 1.0683 0.2482 4.3046 1.67e-05 * Signifikan
riwayat_keluargaDermatitis Atopik 1.3288 0.3961 3.3544 7.95e-04 * Signifikan
riwayat_keluargaAlergi Obat 0.2454 0.6084 0.4033 6.87e-01 Tidak Signifikan
riwayat_keluargaAlergi Makanan 1.1148 0.2309 4.8283 1.40e-06 * Signifikan
perokok_aktifYa 0.6995 0.2740 2.5529 1.07e-02 * Signifikan
paparan_asapYa 0.3173 0.2132 1.4882 1.37e-01 Tidak Signifikan

Fungsi logit model final:

\[\hat{g}(\mathbf{x}) = -0{,}9431 - 0{,}5034 \cdot I_{\text{laki-laki}} + 0{,}3247 \cdot I_{\text{hewan}} + 0{,}7868 \cdot I_{\text{industri}} + 0{,}5375 \cdot I_{\text{perdesaan}}\] \[+ 0{,}2385 \cdot I_{\text{pinggiran}} + 1{,}2224 \cdot I_{\text{rinitis}} + 1{,}0683 \cdot I_{\text{asma}} + 1{,}3288 \cdot I_{\text{dermatitis}} + 0{,}2454 \cdot I_{\text{alergi obat}}\] \[+ 1{,}1148 \cdot I_{\text{alergi makanan}} + 0{,}6995 \cdot I_{\text{merokok}} + 0{,}3173 \cdot I_{\text{asap pasif}}\]

Interpretasi:
Model final menunjukkan bahwa faktor lingkungan, riwayat alergi keluarga, dan perilaku merokok berkontribusi terhadap peningkatan peluang kejadian alergi mahasiswa. Koefisien bernilai positif meningkatkan log-odds kejadian alergi, sedangkan koefisien negatif menunjukkan efek protektif. Riwayat dermatitis atopik keluarga memiliki pengaruh paling dominan dalam model.


6 Pengujian Signifikansi dan Kecocokan Model

6.1 Uji G² — Likelihood Ratio Test (Uji Simultan)

Hipotesis:
\(H_0: \beta_1 = \beta_2 = \cdots = \beta_{12} = 0\) vs \(H_1\): minimal satu \(\beta_i \neq 0\)

\[G^2 = -2\ln\left(\frac{L_0}{L_1}\right) = -2[\ln(L_0) - \ln(L_1)]\]

ll_null <- logLik(glm(status_alergi_num ~ 1,
                      data = data_training, family = binomial))
ll_final <- logLik(model_final)

G2_val        <- -2 * (as.numeric(ll_null) - as.numeric(ll_final))
df_G2         <- length(coef(model_final)) - 1
chi2_kritis   <- qchisq(0.95, df = df_G2)
pval_G2       <- pchisq(G2_val, df = df_G2, lower.tail = FALSE)

uji_G2 <- data.frame(
  G2           = round(G2_val, 3),
  df           = df_G2,
  `χ² Tabel`   = round(chi2_kritis, 3),
  `p-value`    = signif(pval_G2, 3),
  Keputusan    = "Tolak H₀"
)

kable(uji_G2, caption = "Tabel 11. Hasil Uji G² (Likelihood Ratio Test) Model Final")
Table 6.1: Tabel 11. Hasil Uji G² (Likelihood Ratio Test) Model Final
G2 df χ..Tabel p.value Keputusan
87.057 12 21.026 0 Tolak H₀

Kesimpulan: \(G^2 = 87{,}057\) dengan \(\text{df} = 12\) jauh melampaui nilai kritis \(\chi^2_{(12;\,0{,}05)} = 21{,}026\) (\(p < 0{,}001\)). \(H_0\) ditolak — model secara keseluruhan signifikan dan mampu menjelaskan variasi status alergi mahasiswa.

6.2 Uji Wald (Uji Parsial)

Uji Wald mengevaluasi signifikansi setiap koefisien secara parsial.

\[W^2 = \left(\frac{\hat{\beta}_j}{SE(\hat{\beta}_j)}\right)^2 \sim \chi^2_{(1)}\]

\(H_0\) ditolak jika \(W^2 > \chi^2_{(1;\,0{,}05)} = 3{,}84\) atau \(p < 0{,}05\).

wald_tabel <- broom::tidy(model_final) %>%
  filter(term != "(Intercept)") %>%
  mutate(
    df          = 1,
    W2          = round((estimate / std.error)^2, 3),
    p.value     = signif(p.value, 3),
    Keterangan  = ifelse(W2 > qchisq(0.95, 1), "✅ Signifikan", "Tidak Signifikan")
  ) %>%
transmute(
    Variabel   = term,
    β          = round(estimate, 4),
    `Galat Baku` = round(std.error, 4),
    df,
    ``       = W2,       
    `p-value`  = p.value,
    Keterangan
  )

kable(wald_tabel, caption = "Tabel 12. Hasil Uji Wald (Parsial) Model Final")
Table 6.2: Tabel 12. Hasil Uji Wald (Parsial) Model Final
Variabel β Galat Baku df p-value Keterangan
jenis_kelaminLaki-laki -0.5034 0.2078 1 5.867 1.54e-02 ✅ Signifikan
kepemilikan_hewanYa 0.3247 0.2043 1 2.525 1.12e-01 Tidak Signifikan
tempat_tinggalPinggiran Kota 0.2385 0.2327 1 1.051 3.05e-01 Tidak Signifikan
tempat_tinggalPerdesaan 0.5375 0.2090 1 6.610 1.01e-02 ✅ Signifikan
tempat_tinggalKawasan Industri 0.7868 0.2478 1 10.078 1.50e-03 ✅ Signifikan
riwayat_keluargaRinitis Alergi 1.2224 0.2262 1 29.196 1.00e-07 ✅ Signifikan
riwayat_keluargaAsma 1.0683 0.2482 1 18.529 1.67e-05 ✅ Signifikan
riwayat_keluargaDermatitis Atopik 1.3288 0.3961 1 11.252 7.95e-04 ✅ Signifikan
riwayat_keluargaAlergi Obat 0.2454 0.6084 1 0.163 6.87e-01 Tidak Signifikan
riwayat_keluargaAlergi Makanan 1.1148 0.2309 1 23.312 1.40e-06 ✅ Signifikan
perokok_aktifYa 0.6995 0.2740 1 6.517 1.07e-02 ✅ Signifikan
paparan_asapYa 0.3173 0.2132 1 2.215 1.37e-01 Tidak Signifikan

Interpretasi:
Hasil uji Wald menunjukkan bahwa beberapa variabel memiliki pengaruh signifikan secara parsial terhadap kejadian alergi mahasiswa. Variabel dengan p-value kurang dari 0,05 dinyatakan signifikan secara statistik, sedangkan variabel lain belum memiliki bukti pengaruh yang cukup kuat dalam model.

6.3 Uji Hosmer–Lemeshow (Goodness-of-Fit)

Uji Hosmer–Lemeshow mengevaluasi kalibrasi model dengan membandingkan frekuensi yang teramati dan diprediksi pada 10 kelompok.
\(H_0\): Model fit dengan data (tidak ada perbedaan signifikan antara nilai teramati dan diprediksi).

p_training_final <- predict(model_final, newdata = data_training, type = "response")
p_testing_final  <- predict(model_final, newdata = data_testing,  type = "response")

hl <- hoslem.test(data_training$status_alergi_num, p_training_final, g = 10)

hl_tabel <- data.frame(
  `χ²`       = round(hl$statistic, 4),
  df          = hl$parameter,
  `χ² Tabel` = round(qchisq(0.95, hl$parameter), 3),
  `p-value`  = round(hl$p.value, 3),
  Keputusan   = "Gagal Tolak H₀ — Model Fit"
)

kable(hl_tabel, caption = "Tabel 13. Hasil Uji Hosmer–Lemeshow Model Final")
Table 6.3: Tabel 13. Hasil Uji Hosmer–Lemeshow Model Final
χ. df χ..Tabel p.value Keputusan
X-squared 2.9821 8 15.507 0.935 Gagal Tolak H₀ — Model Fit

Kesimpulan: \(\chi^2 = 2{,}982\), \(p = 0{,}936 > 0{,}05\). \(H_0\) gagal ditolak — model memiliki kalibrasi yang baik; tidak terdapat perbedaan signifikan antara nilai yang teramati dan diprediksi.


7 Odds Ratio dan Interpretasi Faktor Risiko

Odds ratio (OR) dihitung sebagai \(\text{OR} = e^{\hat{\beta}_j}\) dengan interval kepercayaan 95% Wald: \(\exp(\hat{\beta}_j \pm 1{,}96 \cdot SE(\hat{\beta}_j))\).

or_tabel <- broom::tidy(model_final) %>%
  filter(term != "(Intercept)") %>%
  mutate(
    OR       = round(exp(estimate), 4),
    CI_Bawah = round(exp(estimate - 1.96 * std.error), 4),
    CI_Atas  = round(exp(estimate + 1.96 * std.error), 4),
    `p-value`   = signif(p.value, 3),
    Keterangan  = ifelse(p.value < 0.05, "✅ Signifikan", "Tidak Signifikan")
  ) %>%
  transmute(
    Variabel   = term,
    OR,
    `95% CI`   = paste0(CI_Bawah, " – ", CI_Atas),
    `p-value`,
    Keterangan
  )

kable(or_tabel, caption = "Tabel 14. Odds Ratio dan Interval Kepercayaan 95% Model Final")
Table 7.1: Tabel 14. Odds Ratio dan Interval Kepercayaan 95% Model Final
Variabel OR 95% CI p-value Keterangan
jenis_kelaminLaki-laki 0.6045 0.4022 – 0.9084 1.54e-02 ✅ Signifikan
kepemilikan_hewanYa 1.3836 0.927 – 2.0651 1.12e-01 Tidak Signifikan
tempat_tinggalPinggiran Kota 1.2693 0.8045 – 2.0027 3.05e-01 Tidak Signifikan
tempat_tinggalPerdesaan 1.7117 1.1363 – 2.5785 1.01e-02 ✅ Signifikan
tempat_tinggalKawasan Industri 2.1963 1.3512 – 3.57 1.50e-03 ✅ Signifikan
riwayat_keluargaRinitis Alergi 3.3954 2.1793 – 5.2902 1.00e-07 ✅ Signifikan
riwayat_keluargaAsma 2.9104 1.7894 – 4.7338 1.67e-05 ✅ Signifikan
riwayat_keluargaDermatitis Atopik 3.7765 1.7374 – 8.209 7.95e-04 ✅ Signifikan
riwayat_keluargaAlergi Obat 1.2781 0.3879 – 4.2113 6.87e-01 Tidak Signifikan
riwayat_keluargaAlergi Makanan 3.0490 1.9392 – 4.7939 1.40e-06 ✅ Signifikan
perokok_aktifYa 2.0128 1.1764 – 3.444 1.07e-02 ✅ Signifikan
paparan_asapYa 1.3734 0.9043 – 2.0857 1.37e-01 Tidak Signifikan

Interpretasi:
Nilai odds ratio menunjukkan besar pengaruh masing-masing faktor risiko terhadap peluang kejadian alergi mahasiswa. Variabel dengan OR lebih besar dari 1 meningkatkan risiko alergi, sedangkan OR kurang dari 1 menunjukkan efek protektif. Faktor dengan OR terbesar merupakan faktor yang paling dominan dalam meningkatkan peluang kejadian alergi.

7.1 Forest Plot Odds Ratio

or_plot <- broom::tidy(model_final) %>%
  filter(term != "(Intercept)") %>%
  mutate(
    OR       = exp(estimate),
    CI_bawah = exp(estimate - 1.96 * std.error),
    CI_atas  = exp(estimate + 1.96 * std.error),
    Warna    = ifelse(p.value < 0.05,
                      "Signifikan (p < 0,05)", "Tidak Signifikan")
  )

ggplot(or_plot, aes(x = OR, y = reorder(term, OR), color = Warna)) +
  geom_point(size = 3.5) +
  geom_errorbarh(aes(xmin = CI_bawah, xmax = CI_atas), height = 0.3, linewidth = 0.8) +
  geom_vline(xintercept = 1, linetype = "dashed", color = "red", linewidth = 0.8) +
  scale_x_log10(breaks = c(0.5, 1, 2, 3, 5)) +
  scale_color_manual(values = c("Signifikan (p < 0,05)" = "#2980b9",
                                 "Tidak Signifikan"      = "gray60")) +
  labs(
    title    = "Odds Ratio dan Interval Kepercayaan 95%",
    subtitle = "Model Final — Seleksi Stepwise AIC",
    x        = "Odds Ratio (Skala Logaritmik)",
    y        = "Variabel",
    color    = ""
  ) +
  theme_minimal(base_size = 12) +
  theme(legend.position = "bottom",
        plot.title      = element_text(face = "bold"),
        panel.grid.minor = element_blank())
Gambar 2. Forest Plot Odds Ratio Model Final (Skala Logaritmik)

Figure 7.1: Gambar 2. Forest Plot Odds Ratio Model Final (Skala Logaritmik)

7.2 Ringkasan Interpretasi Faktor Risiko

7.2.1 Faktor Demografis

Jenis kelamin laki-laki menunjukkan efek protektif yang signifikan (OR = 0,605; 95% CI: 0,402–0,908; \(p = 0{,}015\)). Mahasiswa laki-laki memiliki odds alergi 39,55% lebih rendah dibandingkan perempuan, setelah mengontrol variabel lain. Temuan ini konsisten dengan literatur mengenai respons imun adaptif perempuan yang lebih kuat — termasuk produksi IgE yang lebih tinggi dan modulasi respons Th2 oleh estrogen.

7.2.2 Faktor Lingkungan

Tempat Tinggal OR 95% CI p-value Interpretasi
Kawasan Industri 2,196 1,351–3,570 0,001 ↑ risiko signifikan
Perdesaan 1,712 1,136–2,579 0,010 ↑ risiko signifikan
Pinggiran Kota 1,269 0,805–2,003 0,305 Tidak signifikan

Tinggal di kawasan industri meningkatkan odds alergi sebesar 2,196 kali dibandingkan perkotaan — diduga terkait paparan polutan udara (PM2.5, SO₂, VOC) yang merusak integritas epitel saluran napas.

7.2.3 Faktor Gaya Hidup

Merokok aktif secara signifikan meningkatkan risiko alergi (OR = 2,013; 95% CI: 1,176–3,444; \(p = 0{,}011\)). Mahasiswa perokok aktif memiliki odds alergi dua kali lebih tinggi dibandingkan bukan perokok.

7.2.4 Riwayat Alergi Keluarga (Faktor Risiko Dominan)

Table 7.2: Tabel 15. Odds Ratio Riwayat Alergi Keluarga
Kondisi.Atopik.Keluarga OR X95..CI p.value Status
Dermatitis Atopik 3.777 1,737–8,209 < 0,001 ✅ Signifikan
Rinitis Alergi 3.395 2,179–5,290 < 0,001 ✅ Signifikan
Alergi Makanan 3.049 1,939–4,794 < 0,001 ✅ Signifikan
Asma 2.910 1,789–4,734 < 0,001 ✅ Signifikan
Alergi Obat 1.278 0,388–4,211 0,687 Tidak Signifikan

Riwayat alergi keluarga merupakan faktor risiko paling dominan. Riwayat dermatitis atopik keluarga memiliki OR tertinggi (3,777) — hampir 4 kali lipat risiko alergi dibandingkan tanpa riwayat. Urutan dominansi: predisposisi genetik → faktor lingkungan → gaya hidup.


8 Evaluasi Kinerja Model

8.1 Pseudo-R² dan AIC

ll_n  <- logLik(glm(data_training$status_alergi_num ~ 1, family = binomial))
ll_m  <- logLik(model_final)
n     <- nrow(data_training)

eval_tabel <- data.frame(
  Metrik = c("Jumlah Variabel Prediktor", "AIC", "Deviasi (G²)",
             "McFadden Pseudo-R²", "Cox & Snell R²", "Nagelkerke R²", "RMSE"),
  `Model Final` = c(
    6,
    round(AIC(model_final), 3),
    round(-2 * (as.numeric(ll_n) - as.numeric(ll_m)), 3),
    round(1 - as.numeric(ll_m) / as.numeric(ll_n), 3),
    round(1 - exp((2/n) * (as.numeric(ll_n) - as.numeric(ll_m))), 3),
    round((1 - exp((2/n) * (as.numeric(ll_n) - as.numeric(ll_m)))) /
            (1 - exp((2/n) * as.numeric(ll_n))), 3),
    round(sqrt(mean((data_training$status_alergi_num - p_training_final)^2)), 4)
  )
)

kable(eval_tabel, caption = "Tabel 16. Evaluasi Kinerja Model Final")
Table 8.1: Tabel 16. Evaluasi Kinerja Model Final
Metrik Model.Final
Jumlah Variabel Prediktor 6.0000
AIC 881.0150
Deviasi (G²) 87.0570
McFadden Pseudo-R² 0.0920
Cox & Snell R² 0.1200
Nagelkerke R² 0.1600
RMSE 0.4671

Interpretasi:
Model final memiliki nilai AIC yang lebih rendah dibandingkan model penuh, menunjukkan bahwa model lebih efisien dalam menjelaskan data. Nilai pseudo-R² menunjukkan kemampuan penjelasan variasi outcome yang cukup baik untuk studi epidemiologi observasional berbasis survei.

8.2 Kurva ROC dan Nilai AUC

roc_train <- titik_roc(data_training$status_alergi_num, p_training_final) %>% mutate(Data = "Training")
roc_test  <- titik_roc(data_testing$status_alergi_num,  p_testing_final)  %>% mutate(Data = "Testing")

auc_train <- nilai_auc(roc_train)
auc_test  <- nilai_auc(roc_test)

ggplot(bind_rows(roc_train, roc_test),
       aes(x = fpr, y = sensitivitas, color = Data)) +
  geom_path(linewidth = 1.2) +
  geom_abline(intercept = 0, slope = 1, linetype = "dashed", color = "gray50") +
  annotate("text", x = 0.75, y = 0.30,
           label = paste0("AUC Training = ", round(auc_train, 3),
                          "\nAUC Testing  = ", round(auc_test, 3)),
           hjust = 0, size = 4, color = "#2c3e50", fontface = "italic") +
  coord_equal() +
  scale_color_manual(values = c("Training" = "#e74c3c", "Testing" = "#2980b9")) +
  labs(
    title    = "Kurva ROC — Model Final",
    subtitle = paste0("AUC Training = ", round(auc_train, 3),
                      " | AUC Testing = ", round(auc_test, 3)),
    x        = "False Positive Rate (1 − Spesifisitas)",
    y        = "Sensitivitas (True Positive Rate)",
    color    = "Data"
  ) +
  theme_minimal(base_size = 12) +
  theme(plot.title = element_text(face = "bold"))
Gambar 3. Kurva ROC Model Final — Data Training dan Testing

Figure 8.1: Gambar 3. Kurva ROC Model Final — Data Training dan Testing

AUC Testing (0,728) > AUC Training (0,702) — mengindikasikan model tidak mengalami overfitting dan memiliki kemampuan generalisasi yang baik. Nilai AUC 0,7–0,8 dianggap memadai untuk studi eksplorasi faktor risiko berbasis survei.

8.3 Confusion Matrix dan Metrik Klasifikasi

Threshold optimal ditentukan menggunakan Youden Index yang memaksimalkan jumlah sensitivitas dan spesifisitas secara simultan.

# Threshold optimal via Youden Index
optimal <- roc_train %>%
  filter(is.finite(ambang)) %>%
  arrange(desc(youden), desc(sensitivitas)) %>%
  slice(1)
threshold <- optimal$ambang[1]

cat("Threshold Optimal (Youden Index):", round(threshold, 3), "\n")
Threshold Optimal (Youden Index): 0.485 
# Confusion matrix — Training
pred_train <- as.integer(p_training_final >= threshold)
cm_train   <- table(
  `Aktual`  = ifelse(data_training$status_alergi_num == 1, "Alergi", "Tidak Alergi"),
  `Prediksi` = ifelse(pred_train == 1, "Alergi", "Tidak Alergi")
)
kable(addmargins(cm_train), caption = "Tabel 17. Confusion Matrix — Data Training")
Table 8.2: Tabel 17. Confusion Matrix — Data Training
Alergi Tidak Alergi Sum
Alergi 205 112 317
Tidak Alergi 122 243 365
Sum 327 355 682
# Confusion matrix — Testing
pred_test <- as.integer(p_testing_final >= threshold)
cm_test   <- table(
  `Aktual`   = ifelse(data_testing$status_alergi_num == 1, "Alergi", "Tidak Alergi"),
  `Prediksi` = ifelse(pred_test == 1, "Alergi", "Tidak Alergi")
)
kable(addmargins(cm_test), caption = "Tabel 18. Confusion Matrix — Data Testing")
Table 8.2: Tabel 18. Confusion Matrix — Data Testing
Alergi Tidak Alergi Sum
Alergi 52 28 80
Tidak Alergi 29 63 92
Sum 81 91 172
m_train <- metrik_klasifikasi(data_training$status_alergi_num, p_training_final, threshold)
m_test  <- metrik_klasifikasi(data_testing$status_alergi_num,  p_testing_final,  threshold)

perbandingan <- data.frame(
  Metrik        = c("Akurasi", "Tingkat Kesalahan", "Sensitivitas",
                    "Spesifisitas", "Presisi", "Skor F1", "Akurasi Seimbang"),
  Training      = round(c(m_train$Akurasi, m_train$Tingkat_Kesalahan,
                           m_train$Sensitivitas, m_train$Spesifisitas,
                           m_train$Presisi, m_train$Skor_F1,
                           m_train$Akurasi_Seimbang), 4),
  Testing       = round(c(m_test$Akurasi, m_test$Tingkat_Kesalahan,
                           m_test$Sensitivitas, m_test$Spesifisitas,
                           m_test$Presisi, m_test$Skor_F1,
                           m_test$Akurasi_Seimbang), 4)
)

kable(perbandingan,
      col.names = c("Metrik", "Data Training", "Data Testing"),
      caption   = "Tabel 19. Perbandingan Metrik Klasifikasi — Training vs Testing")
Table 8.3: Tabel 19. Perbandingan Metrik Klasifikasi — Training vs Testing
Metrik Data Training Data Testing
Akurasi 0.6569 0.6686
Tingkat Kesalahan 0.3431 0.3314
Sensitivitas 0.6467 0.6500
Spesifisitas 0.6658 0.6848
Presisi 0.6269 0.6420
Skor F1 0.6366 0.6460
Akurasi Seimbang 0.6562 0.6674

Seluruh metrik pada data testing konsisten sedikit lebih baik dari data training (Akurasi: 0,669 vs 0,657; F1-Score: 0,646 vs 0,637) — mengonfirmasi tidak adanya overfitting.


9 Kesimpulan

Penelitian ini menerapkan regresi logistik biner dengan prosedur pemodelan komprehensif menggunakan University Student Allergy Dataset (\(n = 854\)). Model final yang terbentuk melalui seleksi stepwise bidirectional berbasis AIC memuat 6 variabel prediktor: jenis kelamin, kepemilikan hewan peliharaan, jenis tempat tinggal, riwayat alergi keluarga, merokok aktif, dan paparan asap rokok pasif.

Temuan utama:

  1. Riwayat alergi keluarga merupakan determinan terkuat status alergi mahasiswa (OR: 2,910–3,777 untuk berbagai kondisi atopik)
  2. Tinggal di kawasan industri merupakan faktor lingkungan dengan pengaruh terkuat (OR = 2,196; \(p = 0{,}001\))
  3. Merokok aktif merupakan faktor gaya hidup yang dapat dimodifikasi dengan pengaruh signifikan (OR = 2,013; \(p = 0{,}011\))
  4. Jenis kelamin laki-laki menunjukkan efek protektif signifikan (OR = 0,605; \(p = 0{,}015\))
  5. Model memiliki kalibrasi yang baik (Hosmer–Lemeshow \(p = 0{,}936\)) dan kemampuan diskriminasi memadai (AUC testing = 0,728) tanpa indikasi overfitting

10 Referensi

  1. Soegiarto, G. et al. (2019). The prevalence of allergic diseases in school children of metropolitan city in Indonesia shows a similar pattern to that of developed countries. Asia Pac. Allergy, 9, e17.
  2. Nurhaliza, I.; Imanto, M. (2023). Risk Factors of Allergic Rhinitis in Children. Med. Prof. J. Lampung, 13, 8–13.
  3. Fakhry; Prasasti, C.I. (2023). The relationship between family smoking habits and the incidence of allergic rhinitis. Media Gizi Kesmas, 12, 187–192.
  4. Sihotang, W.Y. et al. (2021). Prevalensi dan faktor risiko sangkaan rinitis alergi pada mahasiswa Fakultas Kedokteran Universitas Prima Indonesia. J. Prima Med. Sains, 3, 47–52.
  5. Hosmer, D.W.; Lemeshow, S.; Sturdivant, R.X. (2013). Applied Logistic Regression, 3rd ed. Wiley: Hoboken, NJ.
  6. Reza, M.M.; Paul, B.K. (2026). University Student Allergy Dataset. Mendeley Data. https://data.mendeley.com/datasets/5yrzvgjryt/1
  7. Fox, J.; Monette, G. (1992). Generalized collinearity diagnostics. J. Am. Stat. Assoc., 87, 178–183.
  8. Hastie, T.; Tibshirani, R.; Friedman, J. (2009). The Elements of Statistical Learning, 2nd ed. Springer.

Analisis Kategorik Data — Universitas Padjadjaran
Dataset: University Student Allergy Dataset (Mendeley Data, 2026)