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)
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.
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))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
)
allergyalergi <- 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")
)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)")| 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 |
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")| 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())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.
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)")| 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.
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)")| 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.
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")| 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.
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")| 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.
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")| Subset | n | Alergi Positif | Proporsi Positif |
|---|---|---|---|
| Training | 682 | 317 | 46.48% |
| Testing | 172 | 80 | 46.51% |
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)")| 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.
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\]
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")| 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.
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)")| 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.
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")| 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.
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,
`W²` = W2,
`p-value` = p.value,
Keterangan
)
kable(wald_tabel, caption = "Tabel 12. Hasil Uji Wald (Parsial) Model Final")| Variabel | β | Galat Baku | df | W² | 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.
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")| χ. | 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.
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")| 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.
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())Figure 7.1: Gambar 2. Forest Plot Odds Ratio Model Final (Skala Logaritmik)
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.
| 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.
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.
| 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.
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")| 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.
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"))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.
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")| 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")| 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")| 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.
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: