Analisis Epidemiologi Malaria di Provinsi Nusa Tenggara Timur: Pendekatan Statistik Spasial dan Pemodelan Prediktif Berbasis Data Kabupaten/Kota

Dosen Pengampu :

Dr. I Gede Nyoman Mindra Jaya, S.Si., M.Si,



Disusun oleh:
Tansya Putri Rizkya Zakaria (140610230021)



PROGRAM STUDI S-1 STATISTIKA

FAKULTAS MATEMATIKA DAN ILMU PENGETAHUAN ALAM

UNIVERSITAS PADJADJARAN

JATINANGOR

2025

Abstrak

Malaria di Provinsi Nusa Tenggara Timur (NTT) masih tersebar tidak merata antarwilayah, sehingga diperlukan pendekatan berbasis data dalam penentuan prioritas intervensi. Penelitian ini bertujuan menganalisis faktor risiko malaria, pola penyebaran spasial dan temporal, serta mengidentifikasi wilayah dengan tingkat kerentanan yang lebih tinggi. Studi ini cocok menggunakan desain cross-sectional dengan data agregat tingkat kabupaten/kota. Metode analisis meliputi statistika deskriptif, pemetaan dan autokorelasi spasial, serta pemodelan binomial negatif yang sesuai untuk data jumlah kasus yang bersifat overdispersi. Hasil analisis menunjukkan bahwa akses terhadap sanitasi layak berhubungan signifikan dengan kejadian malaria, di mana peningkatan akses sanitasi cenderung menurunkan jumlah kasus. Analisis spasial juga mengungkap adanya pola pengelompokan kasus malaria, dengan wilayah Pulau Sumba teridentifikasi sebagai area dengan tingkat kerentanan relatif lebih tinggi. Integrasi hasil pemodelan dan indeks prioritas menghasilkan pemetaan wilayah prioritas penanganan malaria yang lebih terarah. Hasil penelitian ini memberikan dasar berbasis bukti untuk mendukung perencanaan pengendalian malaria yang lebih spesifik dan efisien di Provinsi Nusa Tenggara Timur, khususnya dalam penentuan wilayah prioritas alokasi sumber daya.

Kata kunci: Malaria; Analisis Spasial; Sanitasi; Binomial Negatif; Nusa Tenggara Timur

BAB 1

PENDAHULUAN

1.1 Latar Belakang

Malaria masih menjadi tantangan penting kesehatan masyarakat di Indonesia, khususnya di wilayah timur. Provinsi Nusa Tenggara Timur (NTT) termasuk daerah dengan beban malaria tertinggi secara nasional. Kondisi geografis, iklim tropis kering, mobilitas penduduk, serta keberadaan vektor Anopheles yang aktif hampir sepanjang tahun menyebabkan penularan malaria di wilayah ini tetap bertahan. Data Badan Pusat Statistik (BPS) Provinsi NTT mencatat 8.884 kasus malaria pada tahun 2024. Meskipun beberapa wilayah menunjukkan penurunan kasus, malaria di NTT masih bersifat endemis dengan distribusi yang tidak merata antar kabupaten/kota. Kabupaten Sumba Barat Daya mencatat jumlah kasus tertinggi, sementara beberapa wilayah seperti Manggarai mendekati status eliminasi dengan jumlah kasus yang sangat rendah, menunjukkan adanya perbedaan tingkat kerentanan antar wilayah. Kondisi tersebut menegaskan pentingnya analisis epidemiologi untuk memahami pola spasial malaria di NTT, mengidentifikasi faktor yang berperan terhadap variasi kasus, serta menentukan wilayah berisiko tinggi. Hasil analisis diharapkan dapat menjadi dasar perencanaan strategi pencegahan dan pengendalian malaria yang lebih efektif, tepat sasaran, dan sesuai dengan karakteristik lokal.

1.2 Rumusan Masalah

  1. Faktor-faktor agent, host, dan environment apa saja yang berkontribusi terhadap penularan malaria di NTT?

  2. Bagaimana frekuensi penyakit (prevalensi, insidensi, attack rate) dan hubungan faktor risiko dengan malaria di tingkat kabupaten/kota?

  3. Apa pola spasial dan temporal dari penyebaran malaria, dan adakah wilayah dengan kerentanan lebih tinggi atau cluster penyakit tertentu?

  4. Bagaimana desain studi epidemiologi yang tepat untuk menganalisis hubungan antara faktor risiko dan malaria di NTT?

  5. Model apa yang paling sesuai untuk memetakan dan memprediksi kejadian malaria?

1.3 Tujuan

  1. Mengidentifikasi faktor agent, host, dan environment yang berperan dalam penyebaran malaria di NTT.
  2. Menghitung ukuran frekuensi dan asosiasi epidemiologi untuk menilai risiko malaria di tiap wilayah.
  3. Menyajikan visualisasi distribusi penyakit melalui peta dan analisis pola spasial atau temporal.
  4. Menentukan desain studi epidemiologi yang tepat, termasuk variabel utama, populasi, metode sampling, dan identifikasi potensi bias.
  5. Membangun model yang sesuai untuk pemetaan malaria, disertai evaluasi performa dan interpretasi hasilnya untuk mendukung pengendalian penyakit.

BAB 2

TINJAUAN PUSTAKA

2.1 Konsep Dasar Epidemiologi

Epidemiologi merupakan cabang ilmu kesehatan masyarakat yang mempelajari pola, frekuensi, dan faktor yang memengaruhi terjadinya penyakit dalam suatu populasi. Kajian epidemiologi berfokus pada distribusi penyakit berdasarkan waktu, tempat, dan karakteristik penduduk, serta pada determinan biologis, lingkungan, perilaku, dan sosial yang berperan dalam timbulnya penyakit. Secara etimologis, epidemiologi berasal dari kata epi (pada), demos (rakyat), dan logos (ilmu), yang bermakna ilmu tentang penyakit pada populasi. Dalam penerapannya, epidemiologi menggunakan ukuran seperti angka kejadian dan prevalensi untuk menggambarkan besarnya masalah kesehatan. Pendekatan ini berperan penting dalam mengidentifikasi penyebab dan pola penularan penyakit, serta menjadi dasar perencanaan, pencegahan, pengendalian, dan evaluasi program kesehatan masyarakat.

2.2 Konsep Agen-Host-Environment


Model segitiga epidemiologi menjelaskan bahwa penyakit terjadi akibat interaksi antara agent, host, dan environment. Agent merupakan penyebab penyakit dengan tingkat penularan dan daya tahan tertentu. Host adalah individu yang dapat terinfeksi, dengan kerentanan dipengaruhi oleh usia, status gizi, sistem imun, dan perilaku. Environment mencakup kondisi fisik, biologis, dan sosial yang dapat mendukung atau menghambat penularan penyakit. Penyakit muncul ketika agent bertemu host yang rentan dalam lingkungan yang mendukung. Sebaliknya, pencegahan dapat dilakukan dengan mengendalikan salah satu komponen tersebut, misalnya melalui perbaikan lingkungan atau peningkatan daya tahan host.

2.3 Ukuran Epidemiologi

Dalam epidemiologi, ukuran frekuensi dan ukuran asosiasi digunakan untuk menunjukkan seberapa sering suatu penyakit terjadi dan seberapa kuat hubungan antara faktor risiko dengan kejadian penyakit tersebut. Kedua ukuran ini penting untuk analisis data kesehatan masyarakat karena membantu peneliti memahami pola penyakit dan menentukan prioritas intervensi.

2.3.1 Ukuran Frekuensi

Ukuran frekuensi menggambarkan seberapa sering suatu penyakit atau kejadian kesehatan terjadi pada populasi tertentu dalam periode waktu tertentu.

1. Prevalensi menunjukkan berapa banyak orang yang sakit (baik kasus baru maupun lama) pada satu waktu tertentu. Biasanya digunakan untuk mengukur beban penyakit di masyarakat.

2. Cumulative Incidence (Insidensi Kumulatif) menunjukkan proporsi individu yang menjadi kasus baru selama periode waktu tertentu. Ukuran ini digunakan untuk mengetahui risiko seseorang tertular atau terkena penyakit dalam periode tertentu.

3. Incidence Rate (Angka Insidensi) menunjukkan laju terjadinya kasus baru suatu penyakit dalam periode tertentu di antara populasi yang berisiko. Ukuran ini menggambarkan kecepatan munculnya kasus baru dalam suatu waktu.

4. Case Fatality Rate (CFR) menunjukkan persentase penderita yang meninggal akibat penyakit tersebut. CFR sering digunakan untuk mengukur tingkat keparahan penyakit, misalnya kalau CFR-nya tinggi berarti penyakit itu cenderung berbahaya.

2.3.2 Ukuran Asosiasi

Ukuran asosiasi digunakan untuk menilai hubungan antara paparan (exposure) dan kejadian penyakit (outcome). Tujuannya adalah untuk mengetahui apakah suatu faktor benar-benar berperan sebagai penyebab atau faktor risiko penyakit.

1. Relative Risk (RR) digunakan pada penelitian kohort untuk membandingkan risiko penyakit antara kelompok terpapar dan tidak terpapar.

2. Odds Ratio (OR) digunakan pada penelitian kasus-kontrol untuk membandingkan peluang paparan antara kelompok kasus dan kontrol.

3. Attributable Risk (AR) atau risk difference menunjukkan selisih risiko antara kelompok terpapar dan tidak terpapar, menggambarkan seberapa besar proporsi kejadian penyakit yang bisa dicegah jika paparan dihilangkan.

4. Population Attributable Risk (PAR) menunjukkan seberapa besar proporsi penyakit di populasi yang disebabkan oleh paparan tertentu. Jadi, ukuran ini bantu nunjukin dampak faktor risiko terhadap keseluruhan populasi.

2.4 Desain Studi

Desain studi epidemiologi digunakan untuk mengkaji hubungan antara paparan dan kejadian penyakit dalam populasi. Secara umum, desain ini dibedakan menjadi studi observasional dan studi eksperimental. Studi observasional mencakup cross-sectional, case-control, dan cohort. Studi potong lintang mengukur paparan dan penyakit pada waktu yang sama untuk menggambarkan prevalensi dan hubungan awal, tetapi tidak dapat menilai sebab-akibat. Studi kasus-kontrol membandingkan kelompok sakit dan tidak sakit untuk menelusuri paparan sebelumnya, efisien untuk penyakit langka namun rentan bias. Studi kohort mengikuti kelompok terpapar dan tidak terpapar untuk menilai kejadian penyakit dan hubungan sebab-akibat, tetapi membutuhkan waktu dan biaya besar. Sementara itu, Randomized Controlled Trial (RCT) merupakan studi eksperimental dengan pembagian subjek secara acak, memiliki tingkat bukti paling kuat namun memerlukan sumber daya dan pengawasan etika yang ketat.

2.5 Modeling

Pemodelan epidemiologi digunakan untuk menganalisis hubungan faktor risiko dengan kejadian malaria serta memetakan distribusi penyakit secara spasial. Analisis dapat diawali dengan regresi linier (Ordinary Least Squares/OLS) sebagai model dasar, kemudian dibandingkan dengan model regresi spasial seperti Spatial Autoregressive Model (SAR) dan Spatial Error Model (SEM) apabila terdapat indikasi autokorelasi spasial. Pemilihan model terbaik dilakukan melalui evaluasi kriteria kecocokan model, seperti Akaike Information Criterion (AIC), di mana model dengan nilai AIC terendah dianggap paling efisien dan layak digunakan. Selain itu, karena jumlah kasus malaria merupakan data diskrit yang sering mengalami overdispersi, regresi untuk data count seperti regresi binomial negatif digunakan ketika model linier atau Poisson tidak memenuhi asumsi. Pendekatan ini memastikan bahwa model yang digunakan sesuai dengan karakteristik data dan tujuan analisis epidemiologi.

BAB 3 METODOLOGI

3.1 Sumber Data

Penelitian ini menggunakan data sekunder yang diperoleh dari beberapa sumber resmi pemerintah, yaitu dari Badan Pusat Statistik (BPS) dan PPID Kemendagri. Data yang digunakan mencakup beberapa variabel yang relevan dengan kasus Malaria di Provinsi Nusa Tenggara Timur pada tahun 2024.

3.2 Variabel

Penelitian ini menggunakan jumlah kasus malaria per kabupaten/kota sebagai variabel dependen. Variabel independen meliputi jumlah penduduk, persentase akses sanitasi layak, persentase rumah layak huni, dan kepadatan penduduk. Jumlah penduduk merepresentasikan besaran populasi yang berkaitan dengan potensi penularan, sanitasi layak mencerminkan kondisi lingkungan yang memengaruhi keberadaan vektor, dan rumah layak huni menggambarkan kualitas tempat tinggal yang berperan dalam perlindungan terhadap paparan malaria, dan kepadatan penduduk merepresentasikan berapa banyak orang yang tinggal per \(km^2\)

3.3 Metode Analisis

Penelitian ini menerapkan analisis deskriptif epidemiologi untuk menggambarkan pola kejadian malaria di Provinsi Nusa Tenggara Timur berdasarkan aspek waktu, tempat, dan populasi. Prevalensi digunakan untuk menilai tingkat kejadian di setiap kabupaten/kota, serta analisis korelasi untuk melihat hubungan antarvariabel. Analisis ini dilengkapi dengan statistika deskriptif, visualisasi grafis dan spasial, serta pemodelan.

3.4 Alur Kerja

Penelitian ini menggunakan data sekunder dari BPS yang mencakup kasus malaria, jumlah penduduk, serta variabel lingkungan dan perumahan di kabupaten/kota Provinsi Nusa Tenggara Timur. Analisis meliputi deskripsi kejadian malaria berdasarkan konsep agent–host–environment, perhitungan prevalensi, serta visualisasi grafik dan peta untuk melihat pola spasial dan wilayah berisiko. Penelitian menggunakan desain potong lintang dengan unit analisis kabupaten/kota, dan diakhiri dengan pemodelan statistik untuk mengkaji hubungan faktor demografi dan lingkungan dengan kejadian malaria.

BAB 4 HASIL DAN PEMBAHASAN

4.1 Deskripsi Kasus dan Proses Penyakit

4.1.1 Faktor Agent–Host–Environment

Malaria merupakan penyakit infeksi yang muncul akibat interaksi antara agen penyakit, penjamu (host), dan lingkungan. Penyakit ini disebabkan oleh parasit Plasmodium, terutama Plasmodium falciparum dan Plasmodium vivax, yang ditularkan melalui gigitan nyamuk Anopheles betina. Kerentanan terhadap malaria dipengaruhi oleh faktor host seperti usia, daya tahan tubuh, dan status gizi, di mana anak-anak, ibu hamil, serta individu dengan imunitas rendah memiliki risiko lebih tinggi. Selain itu, perilaku masyarakat, termasuk aktivitas di luar rumah pada malam hari dan tidak menggunakan kelambu saat tidur, turut meningkatkan peluang penularan. Faktor lingkungan juga berperan penting, seperti iklim tropis, keberadaan genangan air sebagai tempat perkembangbiakan nyamuk, serta kondisi pemukiman dan akses layanan kesehatan yang terbatas. Interaksi ketiga faktor tersebut menyebabkan malaria masih bersifat endemis di Provinsi Nusa Tenggara Timur dan memerlukan upaya pengendalian yang terencana dan berkelanjutan.

4.1.2 Diagram Faktor Resiko

Gambar tersebut menggambarkan kerangka konseptual penularan malaria yang dipengaruhi oleh interaksi antara faktor lingkungan, vektor, agen, dan host. Faktor lingkungan, seperti iklim, sanitasi dan ketersediaan air bersih, kondisi permukiman, serta keberadaan habitat nyamuk, berperan dalam menciptakan kondisi yang mendukung perkembangan nyamuk Anopheles. Lingkungan yang tidak mendukung kesehatan akan meningkatkan kepadatan dan umur vektor, sehingga peluang nyamuk membawa dan menularkan parasit Plasmodium menjadi lebih besar. Penularan selanjutnya dipengaruhi oleh faktor host, termasuk perilaku, tingkat imunitas, mobilitas penduduk, dan akses terhadap layanan kesehatan, yang menentukan tingkat paparan dan kemampuan individu dalam mencegah maupun mengobati infeksi. Interaksi antara agen Plasmodium, vektor, dan host inilah yang pada akhirnya menentukan terjadinya kasus malaria di suatu wilayah.

4.2 Statistika Deskriptif

Statistik deskriptif menunjukkan variasi kasus malaria yang sangat lebar antar kabupaten/kota di Provinsi Nusa Tenggara Timur, dengan jumlah kasus antara 2 hingga 5.492. Meskipun median kasus rendah, nilai rata-rata yang lebih tinggi menandakan adanya wilayah dengan beban kasus yang besar. Perbedaan jumlah penduduk, kondisi lingkungan, kualitas perumahan, kepadatan penduduk, dan ketersediaan puskesmas yang tidak merata turut memperlihatkan ketimpangan risiko malaria antarwilayah dan memerlukan analisis lanjutan.

4.3 Ukuran Epidemiologi

4.3.1 Ukuran Frekuensi

4.3.1.1 Prevalensi

Hasil analisis prevalensi menunjukkan adanya ketimpangan kejadian malaria antar kabupaten/kota di Provinsi Nusa Tenggara Timur. Kabupaten Sumba Barat Daya memiliki prevalensi tertinggi yaitu 1,67% yang artinga dari 100 penduduk sekitar 1-2 orang mengalami malaria. Prevalensi Sumba Barat Daya jauh melampaui wilayah lain, diikuti oleh Sumba Timur, Sumba Barat, dan Alor. Sebaliknya, sebagian besar kabupaten/kota lainnya menunjukkan prevalensi yang sangat rendah hingga mendekati nol. Pola ini menegaskan bahwa malaria di NTT tidak tersebar merata, melainkan terkonsentrasi pada wilayah tertentu, khususnya Pulau Sumba, yang diduga berkaitan dengan kondisi lingkungan, kualitas perumahan, dan sanitasi yang masih kurang mendukung pencegahan malaria.

4.4 Visualisasi

4.4.1 Peta Jumlah Kasus Malaria

Peta jumlah kasus malaria memperlihatkan adanya konsentrasi kasus tinggi pada beberapa kabupaten tertentu, sementara sebagian besar wilayah lainnya menunjukkan jumlah kasus yang relatif rendah. Pola ini menandakan adanya perbedaan beban penyakit yang cukup tajam antarwilayah.

4.4.2 Peta Jumlah Penduduk Nusa Tenggara Timur

Peta jumlah penduduk menunjukkan distribusi populasi yang tidak merata di NTT, dengan beberapa kabupaten memiliki penduduk yang jauh lebih besar dibandingkan wilayah lainnya. Kondisi ini menjadi konteks penting dalam interpretasi jumlah kasus malaria, karena wilayah berpenduduk besar cenderung memiliki beban absolut kasus yang lebih tinggi meskipun tingkat risikonya relatif rendah.

4.4.3 Peta Prevalensi

Peta prevalensi malaria memberikan gambaran yang lebih proporsional dengan mempertimbangkan ukuran populasi. Dari peta ini terlihat bahwa wilayah dengan jumlah kasus tinggi tidak selalu memiliki prevalensi tertinggi, dan sebaliknya. Hal ini menegaskan pentingnya penggunaan prevalensi dalam menilai tingkat risiko malaria, terutama pada wilayah dengan jumlah penduduk yang berbeda-beda.

4.4.3 Peta Faktor Lain Malaria di Nusa Tenggara Timur

Selanjutnya, peta sanitasi layak dan peta rumah layak huni menggambarkan adanya ketimpangan kualitas lingkungan permukiman antarwilayah. Wilayah dengan persentase sanitasi layak dan rumah layak huni yang rendah cenderung beririsan dengan daerah berisiko malaria lebih tinggi, sehingga kondisi lingkungan dan kualitas hunian berpotensi berperan dalam mendukung penularan malaria. Sementara itu, kepadatan penduduk tinggi terkonsentrasi di wilayah perkotaan dan tidak bertepatan dengan wilayah risiko malaria utama, sehingga perannya relatif terbatas.

4.5 Matriks Korelasi

Kasus malaria cenderung lebih rendah di wilayah dengan akses sanitasi dan kondisi rumah yang lebih baik, ditunjukkan oleh korelasi negatif yang cukup jelas. Sebaliknya, jumlah penduduk dan kepadatan penduduk memiliki hubungan yang sangat lemah dengan kasus malaria. Sanitasi layak dan rumah layak juga saling berkaitan kuat, menandakan bahwa faktor lingkungan dan kualitas tempat tinggal lebih berpengaruh terhadap kejadian malaria dibandingkan faktor demografi.

4.6 Tren

Tren kasus malaria di sepuluh kabupaten/kota tertinggi di NTT selama 2020–2024 bersifat fluktuatif, dengan puncak pada 2021 terutama di Sumba Barat Daya, Timor Tengah Selatan, dan Sumba Timur. Setelah lonjakan tersebut, kasus menurun drastis pada 2022–2023 dan tetap rendah pada 2024, meski beberapa wilayah menunjukkan peningkatan ringan. Pola ini menekankan pentingnya pemantauan berkelanjutan untuk mencegah lonjakan di masa depan.


4.7 Desain Studi Epidemiologi

4.7.1 Desain Studi

Penelitian ini menggunakan desain cross-sectional, di mana seluruh variabel diamati pada periode yang sama. Desain ini digunakan untuk menganalisis hubungan kejadian malaria dengan faktor lingkungan dan layanan kesehatan pada tingkat kabupaten/kota di Provinsi Nusa Tenggara Timur.

4.7.2 Variabel Penelitian

Variabel dependen adalah jumlah kasus malaria per kabupaten/kota sebagai indikator beban penyakit, yang dapat dinyatakan sebagai angka kejadian untuk menyesuaikan perbedaan ukuran populasi. Variabel independen meliputi jumlah penduduk, persentase sanitasi layak, rumah layak huni, jumlah puskesmas, dan kepadatan penduduk yang merepresentasikan kondisi demografis, lingkungan, dan ketersediaan layanan kesehatan.

4.7.3 Populasi dan Sampling


Populasi penelitian mencakup seluruh kabupaten/kota di Provinsi Nusa Tenggara Timur dengan metode total sampling. Seluruh wilayah dianalisis sebagai unit observasi menggunakan data BPS tahun 2024 untuk memastikan konsistensi dan keterbandingan antarwilayah.

4.7.4 Bias

Penelitian ini berpotensi mengalami ecological fallacy karena menggunakan data agregat kabupaten/kota, sehingga hasil tidak dapat digeneralisasi pada tingkat individu. Desain cross-sectional juga membatasi penarikan kesimpulan kausal. Selain itu, perbedaan kualitas pelaporan kasus antarwilayah serta tidak dimasukkannya seluruh faktor risiko, seperti iklim dan kondisi geografis, berpotensi menimbulkan bias dan confounding, sehingga hasil perlu diinterpretasikan secara hati-hati.

4.8 Autokorelasi Spasial

Variabel Moran I P-Value
Prevalensi 0.222 0.000423
Kasus Malaria 0.115 0.00320
Sanitasi Layak 0.530 0.0000759
Rumah Layak Huni 0.384 0.00256
Jumlah Puskesmas 0.147 0.101
Kepadatan Penduduk -0.0627 0.0702

Hasil uji Global Moran’s I menunjukkan bahwa prevalensi dan jumlah kasus malaria memiliki autokorelasi spasial positif dan signifikan, sedangkan sanitasi layak dan rumah layak huni menunjukkan pola pengelompokan yang lebih kuat. Sebaliknya, jumlah puskesmas dan kepadatan penduduk tidak menunjukkan autokorelasi spasial yang signifikan. Hal ini menunjukkan bahwa malaria dan sebagian faktor risikonya membentuk klaster wilayah, sehingga diperlukan analisis spasial lokal untuk identifikasi yang lebih spesifik.

Hasil Local Moran’s I (LISA) menunjukkan bahwa klaster hotspot prevalensi malaria terkonsentrasi di Pulau Sumba, khususnya Kabupaten Sumba Barat Daya, Sumba Barat, dan Sumba Timur. Kabupaten Sumba Tengah teridentifikasi sebagai klaster Low–High, sedangkan wilayah lain di Nusa Tenggara Timur tidak menunjukkan pola spasial lokal yang signifikan. Pola ini menunjukkan bahwa pengelompokan malaria bersifat terbatas secara geografis dan terkait dengan karakteristik wilayah tertentu.


4.9 Wilayah Prioritas

Plot peringkat wilayah menunjukkan bahwa prioritas penanganan malaria di Provinsi Nusa Tenggara Timur terkonsentrasi di Pulau Sumba, dengan Sumba Barat Daya sebagai wilayah dengan indeks prioritas tertinggi, diikuti oleh Sumba Barat, Sumba Timur, dan Sumba Tengah. Manggarai Timur dan Manggarai masih termasuk kategori prioritas tinggi meskipun memiliki nilai indeks yang lebih rendah, yang menunjukkan adanya potensi risiko berdasarkan faktor lingkungan dan spasial meskipun jumlah kasus aktual relatif rendah. Wilayah lainnya berada pada prioritas sedang hingga rendah, sehingga fokus intervensi utama diarahkan ke wilayah dengan indeks tertinggi, khususnya di Pulau Sumba.

4.10 Modeling

4.10.1 Pemilihan Model dan Uji Diagnostik

Untuk menentukan model terbaik, dilakukan perbandingan antara model regresi linear (OLS) dan model yang mempertimbangkan efek spasial (SAR dan SEM) dengan y (Prevalensi).

OLS SAR SEM
AIC 20.13 22.11 21.36
Log-Likelihood -5.06 -5.05 -4.68

Uji Lagrange Multiplier (LM Test)

Uji Spasial Statistik LM df p-value
LM Spatial Error (RSerr) 0.3593 1 0.5489
LM Spatial Lag (RSlag) 0.0156 1 0.9005

Dalam pemodelan statistik, perbandingan antara OLS dan model spasial (SAR dan SEM) menunjukkan bahwa model nonspasial lebih efisien. Hal ini didukung oleh hasil uji Lagrange Multiplier, di mana uji error spasial (RSerr: p-value = 0,5489) dan uji lag spasial (RSlag: p-value = 0,9005) sama-sama tidak signifikan. Hal ini menunjukkan tidak adanya ketergantungan spasial yang tersisa pada residual, sehingga penggunaan model spasial tidak memberikan peningkatan yang berarti dibandingkan OLS.

4.10.1.1 Uji Overdispersi

Berdasarkan hasil uji diagnostik terhadap distribusi data, diperoleh hasil sebagai berikut:

Nilai
Rata-Rata 403.8182
Varians 1382384
Overdispresi 740.72

Data jumlah kasus malaria mengalami overdispersi, ditunjukkan oleh varians yang jauh lebih besar daripada rata-rata dan parameter dispersi signifikan (p = 0.0385). Oleh karena itu, model Poisson dan OLS tidak sesuai, sehingga digunakan regresi binomial negatif yang lebih tepat untuk data dengan varians berlebih.

4.10.1.2 Uji Multikolinearitas

Sebelum lanjut ke analisis, dilakukan uji multikolinearitas menggunakan nilai Variance Inflation Factor (VIF) untuk menjamin stabilitas model, dengan hasil sebagai berikut :

Variabel VIF
Sanitasi Layak 2.25
Rumah Layak Huni 2.31
Jumlah Puskesmas 1.08
Kepadatan Penduduk 1.15

Seluruh variabel independen memiliki nilai VIF rendah (1.08–2.31), sehingga tidak terdapat multikolinearitas. Dengan terpenuhinya uji diagnostik utama dan tidak adanya ketergantungan spasial pada sisaan, regresi binomial negatif dipilih sebagai model yang paling sesuai untuk mengestimasi pengaruh faktor risiko terhadap kejadian malaria.

4.10.2 Hasil Fitting Model Binomial Negatif

Jumlah penduduk dimasukkan sebagai offset logaritmik agar model mengestimasi laju kejadian malaria. Parameter diestimasi dengan MLE dan dilaporkan sebagai koefisien regresi serta IRR.

Variabel Estimasi (β) Std. Error z-value p-value IRR
Intersep -0.847 2.098 -0.404 0.687 0.43
Sanitasi Layak (%) -0.104 0.037 -2.846 0.004 0.90
Rumah Layak Huni (%) 0.023 0.036 0.629 0.529 1.02
Jumlah Puskesmas 0.039 0.040 0.953 0.341 1.04
Kepadatan Penduduk -0.001 0.001 -0.912 0.362 1.00

Berdasarkan tabel di atas, model matematis prediktif untuk jumlah kasus malaria di Provinsi NTT :

\[ \begin{aligned}Y_i &\sim \text{NegBin}(\mu_i, \theta) \\\log(\mu_i) &= -0.847 - 0.104(\text{Sanitasi}_i) + 0.023(\text{Rumah}_i) + 0.039(\text{Puskesmas}_i) \\&\quad - 0.001(\text{Kepadatan}_i) + \log(\text{Penduduk}_i)\end{aligned} \]

Hasil pemodelan menunjukkan bahwa hanya akses sanitasi layak yang berhubungan signifikan dengan jumlah kasus malaria, dengan koefisien negatif (p = 0,004) dan IRR sebesar 0,90. Hal ini mengindikasikan bahwa setiap peningkatan 1 persen akses sanitasi layak berkaitan dengan penurunan jumlah kasus malaria sekitar 10 persen, dengan asumsi variabel lain konstan. Variabel rumah layak huni, jumlah puskesmas, dan kepadatan penduduk tidak menunjukkan hubungan yang signifikan secara statistik. Meskipun demikian, arah hubungan variabel-variabel tersebut tetap memberikan informasi deskriptif, namun belum cukup kuat untuk disimpulkan sebagai faktor penentu dalam model. Ketidaksignifikanan jumlah puskesmas mengindikasikan bahwa variabel ini lebih merefleksikan kapasitas deteksi dan pelaporan kasus dibandingkan sebagai faktor protektif langsung terhadap kejadian malaria.

4.10.3 Evaluasi Model

Indikator Evaluasi Nilai
Log-Likelihood -129.16
AIC 270.31
Pseudo R-Squared (Cragg-Uhler) 0.4666
Deviance Explained 32.49%
Root Mean Square Error (RMSE) 1083.63
Mean Absolute Error (MAE) 547.49

Model menunjukkan kinerja yang cukup baik. Nilai log-likelihood sebesar −129.16 dan AIC sebesar 270.31 menunjukkan kecocokan model yang memadai. Nilai Pseudo R² (Cragg–Uhler) sebesar 0.47 dan deviance explained sebesar 32.49% menunjukkan bahwa model mampu menjelaskan sebagian variasi kasus malaria antar kabupaten/kota. Nilai RMSE sebesar 1083.63 dan MAE sebesar 547.49 menunjukkan bahwa kesalahan prediksi masih relatif besar, terutama pada wilayah dengan jumlah kasus tinggi. Secara keseluruhan, model dinilai layak digunakan, meskipun memiliki keterbatasan dalam memprediksi nilai ekstrem.

4.10.4 Peta Prediksi Kasus Malaria NTT 2024

Peta prediksi kasus malaria di Provinsi Nusa Tenggara Timur tahun 2024 menunjukkan adanya pengelompokan spasial risiko malaria. Wilayah dengan risiko tertinggi terkonsentrasi di Pulau Sumba (Sumba Barat Daya, Sumba Barat, dan Sumba Tengah) serta sebagian Flores seperti Manggarai Timur, sedangkan risiko terendah terdapat di Timor bagian timur, Flores Timur, dan Kota Kupang. Pola ini menegaskan bahwa wilayah Sumba dan Manggarai Timur perlu menjadi prioritas intervensi pengendalian malaria, khususnya melalui perbaikan sanitasi dan layanan kesehatan.

BAB 5

PENUTUP

5.1 Kesimpulan

Penularan malaria di Provinsi Nusa Tenggara Timur dipengaruhi oleh interaksi faktor agent, host, dan environment, dengan kondisi lingkungan dan akses sanitasi layak sebagai faktor yang berperan penting. Frekuensi penyakit pada tingkat kabupaten/kota menunjukkan variasi antarwilayah, di mana hasil pemodelan mengindikasikan bahwa akses sanitasi layak berhubungan signifikan dengan penurunan jumlah kasus malaria, sementara variabel rumah layak huni, jumlah puskesmas, dan kepadatan penduduk tidak menunjukkan hubungan yang signifikan secara statistik.

Analisis spasial dan temporal mengungkapkan adanya pola pengelompokan kejadian malaria, dengan wilayah Pulau Sumba sebagai area dengan kerentanan lebih tinggi. Desain studi cross-sectional berbasis data agregat serta pemodelan binomial negatif terbukti sesuai untuk memetakan dan memprediksi kejadian malaria dalam konteks data lokal. Integrasi antara hasil pemodelan dan analisis indeks prioritas memberikan landasan berbasis data bagi pembuat kebijakan untuk melakukan intervensi yang lebih spesifik dan terukur, khususnya dalam pengalokasian sumber daya pada wilayah dengan kerentanan tertinggi.

5.2 Saran

Berdasarkan hasil penelitian, disarankan agar Pemerintah Provinsi NTT dan Dinas Kesehatan memfokuskan intervensi pengendalian malaria pada peningkatan akses sanitasi layak, mengingat sanitasi merupakan faktor determinan yang signifikan, melalui integrasi program Sanitasi Total Berbasis Masyarakat (STBM) dengan upaya eliminasi malaria, khususnya di wilayah prioritas tinggi seperti Pulau Sumba. Mengingat pola penularan malaria yang cenderung mengelompok secara spasial, diperlukan koordinasi lintas kabupaten dalam perencanaan dan pelaksanaan intervensi agar penanganan lebih efektif dan berkelanjutan. Untuk penelitian selanjutnya, disarankan penggunaan desain multilevel yang mengombinasikan data individu dan lingkungan guna meminimalkan ecological fallacy, penambahan variabel iklim untuk memperkuat analisis temporal, serta pemetaan habitat perkembangbiakan vektor sebagai validasi lapangan terhadap wilayah hotspot yang teridentifikasi secara spasial.

DAFTAR PUSTAKA

Badan Pusat Statistik Provinsi Nusa Tenggara Timur. (2024). Jumlah penduduk, laju pertumbuhan penduduk, distribusi persentase penduduk, kepadatan penduduk, rasio jenis kelamin penduduk menurut kabupaten/kota di Provinsi Nusa Tenggara Timur, 2024. https://ntt.bps.go.id/id/statistics-table/3/V1ZSbFRUY3lTbFpEYTNsVWNGcDZjek53YkhsNFFUMDkjMw==/jumlah-penduduk--laju-pertumbuhan-penduduk--distribusi-persentase-penduduk--kepadatan-penduduk--rasio-jenis-kelamin-penduduk-menurut-kabupaten-kota-di-provinsi-nusa-tenggara-timur--2025.html?year=2024

Badan Pusat Statistik Kabupaten Malaka. (2024). Jumlah penduduk menurut kabupaten/kota di Provinsi Nusa Tenggara Timur (ribu jiwa), 2021–2025 [Statistik]. https://malakakab.bps.go.id/id/statistics-table/1/MTM5IzE=/jumlah-penduduk-menurut-kabupaten-kota-di-provinsi-nusa-tenggara-timur--ribu-jiwa---2021---2025.html

Badan Pusat Statistik Provinsi Nusa Tenggara Timur. (2024). Jumlah kasus penyakit menurut kabupaten/kota dan jenis penyakit – Provinsi Nusa Tenggara Timur [Statistik]. https://ntt.bps.go.id/id/statistics-table/2/MTQ4NSMy/jumlah-kasus-penyakit-menurut-kabupaten-kota-dan-jenis-penyakit.html

Badan Pusat Statistik Provinsi Nusa Tenggara Timur. (2024). Persentase rumah tangga dengan akses sanitasi layak . https://ntt.bps.go.id/id/statistics-table/2/MTUyNyMy/persentase-rumah-tangga-dengan-akses-sanitasi-layak.html

Badan Pusat Statistik Provinsi Nusa Tenggara Timur. (2024). Persentase rumah tangga yang memiliki akses terhadap hunian layak (rumah layak huni) menurut kabupaten/kota. https://ntt.bps.go.id/id/statistics-table/2/MTc5MSMy/persentase-rumah-tangga-yang-memiliki-akses-terhadap-hunian-layak--rumah-layak-huni--menurut-kabupaten-kota-.html

Elith, J., & Leathwick, J.R. (2024). Environmental factors to malaria incidence: A literature review. https://doaj.org/article/ec75fbd3aac14277a27c76f2d1b4ecbe

Environmental and socio‐economic determinants of malaria transmission: A review. (2025). Environmental risk factors and malaria incidence. https://www.ncbi.nlm.nih.gov/pmc/articles/PMC12512371/

Zhang, W., Liu, Q., Ni, J., et al. (2025). Negative binomial regression analysis of factors influencing the number of distinct mosquito species in Zhejiang Province, China, 2023. Scientific Reports, 15, Article 10433. https://doi.org/10.1038/s41598-025-94288-4

UNICEF Indonesia. (2023). Social determinants influencing access to malaria services. https://www.unicef.org/indonesia/health/reports/social-determinants-influencing-access-malaria-services

Haryono, S.K.M., M.Kes., H., Dr. Agus Kharmayana Rubaya, S.K.M., M.P.H., & Achmad Husein, S.K.M., M.Pd. (2021). Pengantar Epidemiologi. Yogyakarta: Poltekkes Jogja Press. ISBN 978-623-6238-12-7.

World Health Organization. (2023). World malaria report 2023.
https://www.who.int/teams/global-malaria-programme/reports/world-malaria-report-2023

LAMPIRAN

library(DT)
library(ggplot2)
## Warning: package 'ggplot2' was built under R version 4.4.3
library(corrplot)
## corrplot 0.95 loaded
library(readxl)
library(sf)
## Warning: package 'sf' was built under R version 4.4.3
## Linking to GEOS 3.13.0, GDAL 3.8.5, PROJ 9.5.1; sf_use_s2() is TRUE
library(leaflet)
library(RColorBrewer)
library(stringr)
library(spdep)     
## Loading required package: spData
## To access larger datasets in this package, install the spDataLarge
## package with: `install.packages('spDataLarge',
## repos='https://nowosad.github.io/drat/', type='source')`
library(purrr)    
library(spatialreg)   
## Loading required package: Matrix
## 
## Attaching package: 'spatialreg'
## The following objects are masked from 'package:spdep':
## 
##     get.ClusterOption, get.coresOption, get.mcOption,
##     get.VerboseOption, get.ZeroPolicyOption, set.ClusterOption,
##     set.coresOption, set.mcOption, set.VerboseOption,
##     set.ZeroPolicyOption
library(broom)   
library(tidyverse)
## Warning: package 'readr' was built under R version 4.4.3
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.6
## ✔ forcats   1.0.1     ✔ tibble    3.3.0
## ✔ lubridate 1.9.4     ✔ tidyr     1.3.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ tidyr::expand() masks Matrix::expand()
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ✖ tidyr::pack()   masks Matrix::pack()
## ✖ tidyr::unpack() masks Matrix::unpack()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(AER)
## Loading required package: car
## Loading required package: carData
## 
## Attaching package: 'car'
## 
## The following object is masked from 'package:dplyr':
## 
##     recode
## 
## The following object is masked from 'package:purrr':
## 
##     some
## 
## Loading required package: lmtest
## Loading required package: zoo
## 
## Attaching package: 'zoo'
## 
## The following objects are masked from 'package:base':
## 
##     as.Date, as.Date.numeric
## 
## Loading required package: sandwich
## Loading required package: survival
library(lmtest)
library(pscl)
## Classes and Methods for R originally developed in the
## Political Science Computational Laboratory
## Department of Political Science
## Stanford University (2002-2015),
## by and under the direction of Simon Jackman.
## hurdle and zeroinfl functions by Achim Zeileis.
library(performance)
## Warning: package 'performance' was built under R version 4.4.3
library(MASS)
## 
## Attaching package: 'MASS'
## 
## The following object is masked from 'package:dplyr':
## 
##     select
library(dplyr) 
data_malaria <- read_excel("/Users/tansyazakaria/Documents/Data Malaria.xlsx")
data_malaria
## # A tibble: 22 × 7
##    Wilayah  KasusMalaria JumlahPenduduk SanitasiLayak RumahLayak JumlahPuskesmas
##    <chr>           <dbl>          <dbl>         <dbl>      <dbl>           <dbl>
##  1 Sumba B…          568         155013          60.8       27.3              10
##  2 Sumba T…         1151         259261          65.9       33.4              24
##  3 Kupang             17         380212          73.3       42.8              26
##  4 Timor T…          154         481281          70.7       31.3              37
##  5 Timor T…           19         275439          80.8       45.8              28
##  6 Belu               22         235709          85.8       35.0              17
##  7 Alor              787         225020          85.8       58.8              27
##  8 Lembata            25         143345          88.6       64.1              12
##  9 Flores …           69         292523          97.0       70.0              21
## 10 Sikka             391         340327          85.4       37.7              25
## # ℹ 12 more rows
## # ℹ 1 more variable: KepadatanPenduduk <dbl>
summary(data_malaria)
##    Wilayah           KasusMalaria     JumlahPenduduk   SanitasiLayak  
##  Length:22          Min.   :   2.00   Min.   : 92354   Min.   :52.51  
##  Class :character   1st Qu.:  17.25   1st Qu.:169788   1st Qu.:67.38  
##  Mode  :character   Median :  24.00   Median :267350   Median :82.77  
##                     Mean   : 403.82   Mean   :257093   Mean   :77.28  
##                     3rd Qu.: 132.75   3rd Qu.:320622   3rd Qu.:85.82  
##                     Max.   :5492.00   Max.   :481281   Max.   :96.98  
##    RumahLayak    JumlahPuskesmas KepadatanPenduduk
##  Min.   :23.20   Min.   : 6.00   Min.   :  38.0   
##  1st Qu.:35.28   1st Qu.:12.00   1st Qu.: 103.2   
##  Median :44.27   Median :21.50   Median : 125.0   
##  Mean   :46.20   Mean   :19.64   Mean   : 272.6   
##  3rd Qu.:58.45   3rd Qu.:25.75   3rd Qu.: 207.8   
##  Max.   :69.99   Max.   :37.00   Max.   :3030.0
# 1. Baca shapefile Indonesia
shp_indo <- st_read(
  "/Users/tansyazakaria/Downloads/geoBoundaries-IDN-ADM2-all/geoBoundaries-IDN-ADM2.shp"
)
## Reading layer `geoBoundaries-IDN-ADM2' from data source 
##   `/Users/tansyazakaria/Downloads/geoBoundaries-IDN-ADM2-all/geoBoundaries-IDN-ADM2.shp' 
##   using driver `ESRI Shapefile'
## Simple feature collection with 519 features and 5 fields
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: 95.01079 ymin: -11.00762 xmax: 141.0194 ymax: 6.07693
## Geodetic CRS:  WGS 84
# 2. Daftar kabupaten/kota NTT
kab_ntt <- c(
  "Alor", "Belu", "Ende", "Flores Timur", "Kupang", "Lembata",
  "Malaka", "Manggarai", "Manggarai Barat", "Manggarai Timur",
  "Nagekeo", "Ngada", "Rote Ndao", "Sabu Raijua",
  "Sikka", "Sumba Barat", "Sumba Barat Daya", "Sumba Tengah",
  "Sumba Timur", "Timor Tengah Selatan", "Timor Tengah Utara",
  "Kota Kupang"
)

# 3. Filter NTT
shp_ntt <- shp_indo %>%
  mutate(kab_clean = str_to_lower(str_squish(shapeName))) %>%
  filter(kab_clean %in% str_to_lower(kab_ntt))

message("✅ Peta NTT berhasil dimuat!")
## ✅ Peta NTT berhasil dimuat!
message(paste("📍 Jumlah wilayah:", nrow(shp_ntt)))
## 📍 Jumlah wilayah: 22
cat("\n=== NAMA WILAYAH DI SHAPEFILE ===\n")
## 
## === NAMA WILAYAH DI SHAPEFILE ===
print(shp_ntt$shapeName)
##  [1] "Alor"                 "Belu"                 "Ende"                
##  [4] "Flores Timur"         "Kota Kupang"          "Kupang"              
##  [7] "Lembata"              "Malaka"               "Manggarai"           
## [10] "Manggarai Barat"      "Manggarai Timur"      "Nagekeo"             
## [13] "Ngada"                "Rote Ndao"            "Sabu Raijua"         
## [16] "Sikka"                "Sumba Barat"          "Sumba Barat Daya"    
## [19] "Sumba Tengah"         "Sumba Timur"          "Timor Tengah Selatan"
## [22] "Timor Tengah Utara"
data_malaria <- data_malaria %>%
  mutate(kab_clean = str_to_lower(str_squish(Wilayah))) # Ganti 'Wilayah' sesuai nama kolom di Excel kamu

ntt_sf <- shp_ntt %>%
  left_join(data_malaria, by = "kab_clean")
ntt_sf <- ntt_sf %>%
  mutate(Prevalensi_persen = (KasusMalaria / JumlahPenduduk) * 100)

ggplot(
  data = ntt_sf, 
  aes(
    x = reorder(Wilayah, Prevalensi_persen), 
    y = Prevalensi_persen
  )
) +
  geom_bar(stat = "identity", fill = "skyblue") +
  coord_flip() +
  labs(
    title = "Prevalensi Malaria per 100 Penduduk di Provinsi NTT",
    x = "Kabupaten/Kota",
    y = "Prevalensi (%)"
  ) +
  theme_minimal(base_size = 13)

bbox_ntt <- st_bbox(ntt_sf)
map_malaria <- ggplot(ntt_sf) +
  geom_sf(aes(fill = Prevalensi_persen), color = "white", size = 0.6) +
  coord_sf(
    xlim = c(bbox_ntt["xmin"], bbox_ntt["xmax"]),
    ylim = c(bbox_ntt["ymin"], bbox_ntt["ymax"]),
    expand = FALSE
  ) +
  scale_fill_gradientn(
    colors = c("#D2DCB6", "#A1BC98", "#778873",
               "#8BAE66", "#73AF6F", "#628141"),
    na.value = "#E0E0E0",
    name = "Prevalensi"
  ) +
  labs(
    title = "Peta Prevalensi Malaria di Provinsi Nusa Tenggara Timur"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(size = 20, face = "bold", hjust = 0.5),
    plot.subtitle = element_text(size = 11, hjust = 0.5),
    legend.position = "right",
    panel.grid = element_blank(),
    axis.text = element_blank(),
    axis.ticks = element_blank()
  )
map_malaria

map_kasus <- ggplot(ntt_sf) +
  geom_sf(aes(fill = KasusMalaria), color = "white", size = 0.6) +
  coord_sf(
    xlim = c(bbox_ntt["xmin"], bbox_ntt["xmax"]),
    ylim = c(bbox_ntt["ymin"], bbox_ntt["ymax"]),
    expand = FALSE
  ) +
  scale_fill_gradientn(
    colors = c("#C6D8E8",  # biru muda (soft, tidak terlalu terang)
               "#9DBBD6",
               "#6F98BF",
               "#4E7FA8",
               "#35648C",
               "#244A6B"   # biru tua
    ),
    na.value = "#E0E0E0",
    name = "Jumlah Kasus"
  ) +
  labs(
    title = "Peta Jumlah Kasus Malaria di Provinsi Nusa Tenggara Timur" ) +
  theme_minimal() +
  theme(
    plot.title = element_text(size = 20, face = "bold", hjust = 0.5),
    plot.subtitle = element_text(size = 11, hjust = 0.5),
    legend.position = "right",
    panel.grid = element_blank(),
    axis.text = element_blank(),
    axis.ticks = element_blank()
  )
map_kasus

map_penduduk <- ggplot(ntt_sf) +
  geom_sf(aes(fill = JumlahPenduduk), color = "white", size = 0.6) +
  coord_sf(
    xlim = c(bbox_ntt["xmin"], bbox_ntt["xmax"]),
    ylim = c(bbox_ntt["ymin"], bbox_ntt["ymax"]),
    expand = FALSE
  ) +
  scale_fill_gradientn(
    colors = c( "#EFE1C6",
                "#D9C29E",
                "#BFA57A",
                "#9C845A",
                "#7A6440",
                "#4F3F2A"
    ),
    na.value = "#E0E0E0",
    name = "Jumlah Penduduk"
  ) +
  labs(
    title = "Jumlah Penduduk di Provinsi Nusa Tenggara Timur" ) +
  theme_minimal() +
  theme(
    plot.title = element_text(size = 15, face = "bold", hjust = 0.5),
    plot.subtitle = element_text(size = 11, hjust = 0.5),
    legend.position = "right",
    panel.grid = element_blank(),
    axis.text = element_blank(),
    axis.ticks = element_blank()
  )
map_penduduk

map_sanit <- ggplot(ntt_sf) +
  geom_sf(aes(fill = SanitasiLayak), color = "white", size = 0.6) +
  coord_sf(
    xlim = c(bbox_ntt["xmin"], bbox_ntt["xmax"]),
    ylim = c(bbox_ntt["ymin"], bbox_ntt["ymax"]),
    expand = FALSE
  ) +
  scale_fill_gradientn(
    colors = c("#D6CFE3",  # ungu muda (soft)
               "#B9A8CF",
               "#9A86B8",
               "#7B649E",
               "#5E4A82",
               "#43305F"  
    ),
    na.value = "#E0E0E0",
    name = "% Sanitasi Layak"
  ) +
  labs(
    title = "Peta % Sanitasi Layak di Provinsi Nusa Tenggara Timur" ) +
  theme_minimal() +
  theme(
    plot.title = element_text(size = 15, face = "bold", hjust = 0.5),
    plot.subtitle = element_text(size = 11, hjust = 0.5),
    legend.position = "right",
    panel.grid = element_blank(),
    axis.text = element_blank(),
    axis.ticks = element_blank()
  )
map_sanit

map_rumah <- ggplot(ntt_sf) +
  geom_sf(aes(fill = SanitasiLayak), color = "white", size = 0.6) +
  coord_sf(
    xlim = c(bbox_ntt["xmin"], bbox_ntt["xmax"]),
    ylim = c(bbox_ntt["ymin"], bbox_ntt["ymax"]),
    expand = FALSE
  ) +
  scale_fill_gradientn(
    colors = c("#F2C6D4",  # pink muda (soft)
               "#E7A6BC",
               "#D986A3",
               "#C86C8F",
               "#AD4F72",
               "#8F3557"   # pink tua
    ),
    na.value = "#E0E0E0",
    name = "% Rumah Layak Huni"
  ) +
  labs(
    title = "Peta % Rumah Layak Huni di Provinsi Nusa Tenggara Timur" ) +
  theme_minimal() +
  theme(
    plot.title = element_text(size = 15, face = "bold", hjust = 0.5),
    plot.subtitle = element_text(size = 11, hjust = 0.5),
    legend.position = "right",
    panel.grid = element_blank(),
    axis.text = element_blank(),
    axis.ticks = element_blank()
  )
map_rumah

map_kepadatanpenduduk <- ggplot(ntt_sf) +
  geom_sf(aes(fill = KepadatanPenduduk), color = "white", size = 0.6) +
  coord_sf(
    xlim = c(bbox_ntt["xmin"], bbox_ntt["xmax"]),
    ylim = c(bbox_ntt["ymin"], bbox_ntt["ymax"]),
    expand = FALSE
  ) +
  scale_fill_gradientn(
    colors = (colors = c("#FFF9C4", "#FFD54F", "#FB8C00", "#E65100")),
    na.value = "#E0E0E0",
    name = "Kepadatan Penduduk per km^2"
  ) +
  labs(
    title = "Peta Kepadatan Penduduk di Provinsi Nusa Tenggara Timur" ) +
  theme_minimal() +
  theme(
    plot.title = element_text(size = 12, face = "bold", hjust = 0.5),
    plot.subtitle = element_text(size = 11, hjust = 0.5),
    legend.position = "right",
    panel.grid = element_blank(),
    axis.text = element_blank(),
    axis.ticks = element_blank()
  )
map_kepadatanpenduduk

cor_vars <- c("KasusMalaria",
              "JumlahPenduduk",
              "SanitasiLayak",
              "RumahLayak",
              "JumlahPuskesmas",
              "KepadatanPenduduk")

cor_df <- data_malaria %>% 
  dplyr::select(all_of(cor_vars)) %>% 
  mutate(across(everything(), as.numeric))

# Hitung matriks korelasi
library(corrplot)

cor_matrix <- cor(cor_df[, c("KasusMalaria", "JumlahPenduduk", "SanitasiLayak", 
                           "RumahLayak", "KepadatanPenduduk")], 
                  use = "complete.obs")

# Membuat Visualisasi
corrplot(cor_matrix, 
         method = "color", 
         type = "upper",
         addCoef.col = "black",      # Warna koefisien angka
         tl.col = "black",           # Warna label teks
         tl.srt = 45,                # Rotasi label
         col = colorRampPalette(c("#e74c3c", "white", "#3498db"))(200), # Tema warna
         title = "\nMatriks Korelasi Faktor Risiko", 
         mar = c(0,0,2,0))           # Margin agar judul tidak terpotong

malaria_tahunan <- read_xlsx("/Users/tansyazakaria/Documents/Data Tahunan Malaria.xlsx")

# Cek struktur awal
glimpse(malaria_tahunan)
## Rows: 22
## Columns: 6
## $ Wilayah <chr> "Sumba Barat", "Sumba Timur", "Kupang", "Timor Tengah Selatan"…
## $ `2020`  <dbl> 3912, 1636, 138, 86, 16, 24, 218, 3, 11, 73, 5, 0, 6, 1, 2, 12…
## $ `2021`  <dbl> 24433, 32048, 15075, 41942, 27374, 13552, 8989, 11492, 43001, …
## $ `2022`  <dbl> 1903, 5537, 50, 696, 26, 19, 412, 29, 191, 531, 6, 2, 10, 261,…
## $ `2023`  <dbl> 712, 2184, 59, 504, 7, 27, 331, 11, 162, 282, 6, 4, 13, 50, 16…
## $ `2024`  <dbl> 568, 1151, 17, 154, 19, 22, 787, 25, 69, 391, 8, 3, 28, 53, 18…
malaria_long <- malaria_tahunan %>%
  pivot_longer(cols = c(`2020`, `2021`, `2022`, `2023`, `2024`),
               names_to = "Tahun",
               values_to = "Kasus_malaria")

malaria_long$Tahun <- as.numeric(malaria_long$Tahun)

#Ambil 10 wilayah dengan total kasus tertinggi
top10 <- malaria_long %>%
  group_by(Wilayah) %>%
  summarise(total_kasus = sum(Kasus_malaria, na.rm = TRUE)) %>%
  arrange(desc(total_kasus)) %>%
  slice_head(n = 10)

#Filter dataset untuk 10 wilayah tersebut
malaria_top10 <- malaria_long %>%
  filter(Wilayah %in% top10$Wilayah)

ggplot(malaria_top10, aes(x = Tahun, y = Kasus_malaria, group = Wilayah, color = Wilayah)) +
  geom_line(size = 1.2) +
  geom_point(size = 2.8) +
  theme_minimal(base_size = 13) +
  scale_color_brewer(palette = "Paired") +  
  # 🔹 Palet "Paired" kasih warna cerah dan kontras untuk 10 kategori
  labs(
    title = "Tren Kasus malaria (Top 10 Kabupaten/Kota) Nusa Tenggara Timur 2020–2024",
    x = "Tahun",
    y = "Jumlah Kasus malaria",
    color = "Kabupaten/Kota"
  ) +
  theme(
    legend.position = "bottom",   # legend di bawah biar nggak nutup grafik
    legend.title = element_text(face = "bold"),
    legend.text = element_text(size = 9),
    plot.title = element_text(hjust = 0.5, face = "bold")
  )
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

ntt_sf <- ntt_sf %>%
  mutate(
    KasusMalaria = as.numeric(as.character(KasusMalaria)),
    JumlahPenduduk = as.numeric(as.character(JumlahPenduduk)),
    Prevalensi = (KasusMalaria / JumlahPenduduk) * 100
  ) %>%
  mutate(across(any_of(c("SanitasiLayak", "RumahLayak", "JumlahPuskesmas", "KepadatanPenduduk")), 
                ~as.numeric(as.character(.))))
# BOBOT SPASIAL (KNN k=3) 
coords <- st_centroid(ntt_sf) |> st_coordinates()
## Warning: st_centroid assumes attributes are constant over geometries
nb_knn <- knearneigh(coords, k = 3) 
nb_work <- knn2nb(nb_knn)
lw_work <- nb2listw(nb_work, style = "W", zero.policy = TRUE)

# GLOBAL MORAN'S I 
vars <- c("Prevalensi", "KasusMalaria", "SanitasiLayak", "RumahLayak", "JumlahPuskesmas", "KepadatanPenduduk")

moran_results <- purrr::map_dfr(vars, function(v){
  # Cek apakah kolom ada dan bertipe numerik
  if(!is.numeric(ntt_sf[[v]])) {
    stop(paste("Variabel", v, "tidak ditemukan atau bukan angka!"))
  }
  
  mt <- spdep::moran.test(ntt_sf[[v]], lw_work, zero.policy = TRUE)
  tibble::tibble(
    Variabel = v,
    Moran_I  = as.numeric(mt$estimate["Moran I statistic"]),
    P_value  = mt$p.value
  )
})

print("--- Hasil Global Moran's I 2024 ---")
## [1] "--- Hasil Global Moran's I 2024 ---"
print(moran_results)
## # A tibble: 6 × 3
##   Variabel          Moran_I   P_value
##   <chr>               <dbl>     <dbl>
## 1 Prevalensi         0.222  0.000423 
## 2 KasusMalaria       0.115  0.00320  
## 3 SanitasiLayak      0.530  0.0000759
## 4 RumahLayak         0.384  0.00256  
## 5 JumlahPuskesmas    0.147  0.101    
## 6 KepadatanPenduduk -0.0627 0.702
# 4. LOCAL MORAN'S I (LISA) - ANALISIS KLASTER LOKAL
# Standarisasi Z-score (Wajib secara teori untuk menentukan High/Low)
ntt_sf$z_pre <- as.numeric(scale(ntt_sf$Prevalensi))

# Hitung Spatial Lag dan Local Moran
ntt_sf$lag_z <- lag.listw(lw_work, ntt_sf$z_pre, zero.policy = TRUE)
lisa_res     <- localmoran(ntt_sf$Prevalensi, lw_work, zero.policy = TRUE)
ntt_sf$p_lisa <- lisa_res[, 5]

# Klasifikasi Kuadran LISA
ntt_sf <- ntt_sf %>%
  mutate(LISA_cat = case_when(
    p_lisa > 0.05 ~ "Not Significant",
    z_pre >= 0 & lag_z >= 0 ~ "High-High",
    z_pre < 0  & lag_z < 0  ~ "Low-Low",
    z_pre >= 0 & lag_z < 0  ~ "High-Low",
    z_pre < 0  & lag_z >= 0 ~ "Low-High",
    TRUE ~ "Not Significant"
  ))


# 5. VISUALISASI PETA LISA
ggplot(ntt_sf) +
  geom_sf(aes(fill = LISA_cat), color = "white", size = 0.2) +
  scale_fill_manual(values = c(
    "High-High"       = "#d7191c", # Merah (Hotspot)
    "Low-Low"        = "#2c7bb6", # Biru (Coldspot)
    "High-Low"       = "#fdae61", # Oranye
    "Low-High"       = "#abd9e9", # Biru Muda
    "Not Significant" = "grey90"  # Abu-abu
  )) +
  labs(
    title = "Peta Klaster LISA Prevalensi Malaria di NTT",
    fill = "Klaster Spasial"
  ) +
  theme_minimal()

# HITUNG PRIORITAS 
ntt_prioritas <- ntt_sf %>%
  st_drop_geometry() %>% 
  # 1. Pastikan semua kolom yang digunakan adalah numerik
  mutate(across(c(Prevalensi, SanitasiLayak, RumahLayak, JumlahPuskesmas, KepadatanPenduduk), 
                ~as.numeric(as.character(.)))) %>%
  # 2. Perhitungan Z-Score
  mutate(
    z_prevalensi = as.numeric(scale(Prevalensi)),
    z_sanitasi   = -as.numeric(scale(SanitasiLayak)),   
    z_rumah      = -as.numeric(scale(RumahLayak)),      
    z_puskesmas  = -as.numeric(scale(JumlahPuskesmas)), 
    z_kepadatan  = as.numeric(scale(KepadatanPenduduk)),
    
    # 3. Hitung Indeks Prioritas
    indeks_prioritas = (0.50 * z_prevalensi) +
                       (0.30 * z_sanitasi) +
                       (0.10 * z_puskesmas) +
                       (0.05 * z_rumah) +
                       (0.05 * z_kepadatan)
  )


# KATEGORISASI & RANKING
# Menggunakan quantile dari data asli (22 kabupaten)
q_tinggi <- quantile(ntt_prioritas$indeks_prioritas, 0.75)
q_sedang <- quantile(ntt_prioritas$indeks_prioritas, 0.50)

ntt_prioritas <- ntt_prioritas %>%
  mutate(
    kategori_prioritas = case_when(
      indeks_prioritas >= q_tinggi ~ "Prioritas Tinggi",
      indeks_prioritas >= q_sedang ~ "Prioritas Sedang",
      TRUE ~ "Prioritas Rendah"
    ),
    rank_prioritas = rank(-indeks_prioritas, ties.method = "first")
  ) %>%
  arrange(rank_prioritas)


# JOIN KEMBALI KE SF UNTUK PETA
ntt_prioritas_sf <- ntt_sf %>%
  left_join(
    ntt_prioritas %>% 
      dplyr::select(Wilayah, z_prevalensi, z_sanitasi, z_rumah, 
                    z_puskesmas, z_kepadatan, indeks_prioritas, 
                    kategori_prioritas, rank_prioritas),
    by = "Wilayah"
  )

message("✅ Perhitungan selesai untuk ", nrow(ntt_prioritas), " kabupaten/kota.")
## ✅ Perhitungan selesai untuk 22 kabupaten/kota.
print(head(ntt_prioritas %>% dplyr::select(Wilayah, rank_prioritas, kategori_prioritas)))
##            Wilayah rank_prioritas kategori_prioritas
## 1 Sumba Barat Daya              1   Prioritas Tinggi
## 2      Sumba Barat              2   Prioritas Tinggi
## 3      Sumba Timur              3   Prioritas Tinggi
## 4     Sumba Tengah              4   Prioritas Tinggi
## 5  Manggarai Timur              5   Prioritas Tinggi
## 6        Manggarai              6   Prioritas Tinggi
# 1. Tentukan palet warna manual agar sesuai dengan kategori
warna_prioritas <- c(
  "Prioritas Tinggi" = "#d73027", # Merah
  "Prioritas Sedang" = "#fee08b", # Kuning/Oranye
  "Prioritas Rendah" = "#1a9850"  # Hijau
)

# 2. Buat Plot
plot_prioritas <- ggplot(ntt_prioritas, 
                         aes(x = reorder(Wilayah, indeks_prioritas), 
                             y = indeks_prioritas, 
                             fill = kategori_prioritas)) +
  geom_col(width = 0.7) +
  # Tambahkan label angka skor dan ranking di ujung bar
  geom_text(aes(label = paste0("#", rank_prioritas, " (", round(indeks_prioritas, 2), ")")), 
            hjust = -0.1, size = 3.5, fontface = "bold") +
  # Balik koordinat agar nama kabupaten mudah dibaca (Sumbu Y jadi Nama Wilayah)
  coord_flip() +
  # Gunakan warna manual yang sudah didefinisikan
  scale_fill_manual(values = warna_prioritas) +
  # Perluas sumbu Y sedikit agar label teks tidak terpotong
  scale_y_continuous(expand = expansion(mult = c(0, 0.2))) +
  labs(
    title = "Ranking Wilayah Prioritas Penanganan Malaria di NTT",
    x = "Kabupaten/Kota",
    y = "Indeks Prioritas (Semakin Tinggi = Semakin Mendesak)",
    fill = "Kategori Prioritas"
  ) +
  theme_minimal(base_size = 13) +
  theme(
    plot.title = element_text(face = "bold", size = 16),
    legend.position = "bottom",
    panel.grid.minor = element_blank(),
    axis.text.y = element_text(face = "bold")
  )

# Tampilkan plot
print(plot_prioritas)

# 1. ESTIMASI MODEL OLS (NON-SPASIAL)
# Menggunakan Prevalensi sebagai variabel dependen (y)
ols_malaria <- lm(Prevalensi ~ SanitasiLayak + RumahLayak + JumlahPuskesmas + KepadatanPenduduk, 
                  data = ntt_sf)

# 2. ESTIMASI MODEL SPASIAL (SAR & SEM)
# SAR: Spatial Autoregressive Model (Lag Model)
sar_malaria <- lagsarlm(Prevalensi ~ SanitasiLayak + RumahLayak + JumlahPuskesmas + KepadatanPenduduk, 
                        data = ntt_sf, listw = lw_work, zero.policy = TRUE)

# SEM: Spatial Error Model
sem_malaria <- errorsarlm(Prevalensi ~ SanitasiLayak + RumahLayak + JumlahPuskesmas+ KepadatanPenduduk, 
                          data = ntt_sf, listw = lw_work, zero.policy = TRUE)

# 3. PERBANDINGAN MODEL (AIC & LOG-LIKELIHOOD)
model_comp <- data.frame(
  Model = c("OLS", "SAR", "SEM"),
  AIC = c(AIC(ols_malaria), AIC(sar_malaria), AIC(sem_malaria)),
  LogLik = c(logLik(ols_malaria), logLik(sar_malaria), logLik(sem_malaria))
)

print("--- Tabel Perbandingan Model ---")
## [1] "--- Tabel Perbandingan Model ---"
print(model_comp)
##   Model      AIC    LogLik
## 1   OLS 22.13763 -5.068815
## 2   SAR 24.11732 -5.058662
## 3   SEM 23.22044 -4.610218
# 4. UJI DIAGNOSTIK: LAGRANGE MULTIPLIER (LM TEST)
# Untuk menentukan secara formal apakah efek spasial signifikan
lm_tests <- lm.LMtests(ols_malaria, lw_work, test = c("LMerr", "LMlag"))
## Please update scripts to use lm.RStests in place of lm.LMtests
print(summary(lm_tests))
##  Rao's score (a.k.a Lagrange multiplier) diagnostics for spatial
##  dependence
## data:  
## model: lm(formula = Prevalensi ~ SanitasiLayak + RumahLayak +
## JumlahPuskesmas + KepadatanPenduduk, data = ntt_sf)
## test weights: listw
##  
##       statistic parameter p.value
## RSerr  0.359251         1  0.5489
## RSlag  0.015644         1  0.9005
# Eksplorasi Mean vs Varians
mean_kasus <- mean(ntt_sf$KasusMalaria)
var_kasus  <- var(ntt_sf$KasusMalaria)
cat("Rata-rata Kasus:", mean_kasus, "\n")
## Rata-rata Kasus: 403.8182
cat("Varians Kasus   :", var_kasus, "\n")
## Varians Kasus   : 1382384
cat("Rasio Var/Mean :", var_kasus / mean_kasus, "\n") # Jika > 1, indikasi Overdispersi
## Rasio Var/Mean : 3423.283
# Model Poisson (Sebagai Baseline untuk Uji Overdispersi)
pois_model <- glm(KasusMalaria ~ SanitasiLayak + RumahLayak + JumlahPuskesmas + 
                    KepadatanPenduduk + offset(log(JumlahPenduduk)), 
                  family = poisson(link = "log"), data = ntt_sf)

# Formal Dispersion Test
disp_test <- dispersiontest(pois_model, alternative = "greater")
print(disp_test) # Jika p-value < 0.05, wajib pakai binomial negatif
## 
##  Overdispersion test
## 
## data:  pois_model
## z = 1.7681, p-value = 0.03852
## alternative hypothesis: true dispersion is greater than 1
## sample estimates:
## dispersion 
##   740.7223
# PEMODELAN binomial negatif

# Fitting Model Final (Berdasarkan seleksi AIC terkecil sebelumnya)
nb_final <- glm.nb(KasusMalaria ~ SanitasiLayak + RumahLayak + JumlahPuskesmas + KepadatanPenduduk +
                     offset(log(JumlahPenduduk)), data = ntt_sf)

summary(nb_final)
## 
## Call:
## glm.nb(formula = KasusMalaria ~ SanitasiLayak + RumahLayak + 
##     JumlahPuskesmas + KepadatanPenduduk + offset(log(JumlahPenduduk)), 
##     data = ntt_sf, init.theta = 0.4559582298, link = log)
## 
## Coefficients:
##                     Estimate Std. Error z value Pr(>|z|)   
## (Intercept)       -0.8468106  2.0984073  -0.404  0.68654   
## SanitasiLayak     -0.1040975  0.0365796  -2.846  0.00443 **
## RumahLayak         0.0228265  0.0362871   0.629  0.52931   
## JumlahPuskesmas    0.0385442  0.0404515   0.953  0.34067   
## KepadatanPenduduk -0.0005160  0.0005661  -0.912  0.36202   
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for Negative Binomial(0.456) family taken to be 1)
## 
##     Null deviance: 41.243  on 21  degrees of freedom
## Residual deviance: 27.842  on 17  degrees of freedom
## AIC: 270.31
## 
## Number of Fisher Scoring iterations: 1
## 
## 
##               Theta:  0.456 
##           Std. Err.:  0.114 
## 
##  2 x log-likelihood:  -258.314
# Menghitung Incidence Rate Ratio (IRR) & Confidence Interval
irr_val <- exp(coef(nb_final))
ci_val  <- exp(confint.default(nb_final)) # Menggunakan Wald CI
cat("--- Incident Rate Ratio (IRR) ---\n")
## --- Incident Rate Ratio (IRR) ---
print(cbind(IRR = irr_val, ci_val))
##                         IRR      2.5 %     97.5 %
## (Intercept)       0.4287803 0.00701564 26.2061015
## SanitasiLayak     0.9011374 0.83879232  0.9681165
## RumahLayak        1.0230890 0.95285276  1.0985025
## JumlahPuskesmas   1.0392967 0.96007945  1.1250502
## KepadatanPenduduk 0.9994841 0.99837578  1.0005937
# A. Metrik Kelayakan Model
res_pR2 <- pR2(nb_final)
## fitting null model for pseudo-r2
log_lik <- as.numeric(logLik(nb_final))
aic_val <- AIC(nb_final)
r2_cu   <- res_pR2["r2CU"] # Pseudo R-Square Cragg-Uhler
dev_exp <- (nb_final$null.deviance - nb_final$deviance) / nb_final$null.deviance

# B. Metrik Akurasi Prediksi
actual    <- ntt_sf$KasusMalaria
predicted <- predict(nb_final, type = "response")
rmse_val  <- sqrt(mean((actual - predicted)^2))
mae_val   <- mean(abs(actual - predicted))

# C. Likelihood Ratio Test (Poisson vs NB)
lrt_result <- lrtest(pois_model, nb_final)

cat("\n==========================================\n")
## 
## ==========================================
cat("HASIL EVALUASI MODEL FINAL binomial negatif\n")
## HASIL EVALUASI MODEL FINAL binomial negatif
cat("==========================================\n")
## ==========================================
cat("Log-Likelihood     :", log_lik, "\n")
## Log-Likelihood     : -129.1568
cat("AIC               :", aic_val, "\n")
## AIC               : 270.3136
cat("Pseudo R2 (CU)    :", r2_cu, "\n")
## Pseudo R2 (CU)    : 0.4666021
cat("Deviance Explained:", dev_exp * 100, "%\n")
## Deviance Explained: 32.49179 %
cat("RMSE              :", rmse_val, "\n")
## RMSE              : 1083.633
cat("MAE               :", mae_val, "\n")
## MAE               : 547.4946
cat("Dispersion (Theta):", nb_final$theta, "\n")
## Dispersion (Theta): 0.4559582
cat("==========================================\n")
## ==========================================
print(lrt_result)
## Likelihood ratio test
## 
## Model 1: KasusMalaria ~ SanitasiLayak + RumahLayak + JumlahPuskesmas + 
##     KepadatanPenduduk + offset(log(JumlahPenduduk))
## Model 2: KasusMalaria ~ SanitasiLayak + RumahLayak + JumlahPuskesmas + 
##     KepadatanPenduduk + offset(log(JumlahPenduduk))
##   #Df  LogLik Df Chisq Pr(>Chisq)    
## 1   5 -5503.0                        
## 2   6  -129.2  1 10748  < 2.2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
ntt_sf$pred_nb <- predicted

ggplot(ntt_sf) +
  geom_sf(aes(fill = pred_nb), color = "white", size = 0.2) +
  scale_fill_viridis_c(option = "plasma") +
  labs(title = "Peta Prediksi Kasus Malaria NTT 2024",
       subtitle = "Model binomial negatif Regression",
       fill = "Prediksi Kasus") +
  theme_minimal()

SYNTAX DASHBOARD

\[ library(corrplot) library(dplyr) library(readxl) library(sf) library(leaflet) library(RColorBrewer) library(stringr) library(spdep) # untuk analisis spasial library(purrr) # untuk map_dfr library(spatialreg) # untuk model spasial SAR & SEM library(broom) # untuk tidy hasil regresi library(tidyr) library(tidyverse) # ============================================================================= # LOAD DATA EXCEL # ============================================================================= data <- read_excel("Data Malaria.xlsx") # ============================================================================= # PERSIAPAN DATA TREN WAKTU # ============================================================================= # Reactive: Load data tren dari file Excel (sheet terpisah atau file lain) tren_df <- reactive({ # OPSI 1: Jika data tren ada di sheet terpisah di file yang sama tryCatch({ read_excel("Data Tahunan Malaria.xlsx") }, error = function(e) { # OPSI 2: Jika dari file terpisah # read_excel("path/to/tren_waktu.xlsx") # OPSI 3: Jika belum ada, return NULL return(NULL) }) }) # Rename kolom sesuai kebutuhan data <- data %>% rename( Wilayah = Wilayah, KasusMalaria = KasusMalaria, JumlahPenduduk = JumlahPenduduk, SanitasiLayak = SanitasiLayak, RumahLayak = RumahLayak, JumlahPuskesmas = JumlahPuskesmas, KepadatanPenduduk = KepadatanPenduduk # ✅ TAMBAHKAN INI ) %>% mutate( kab_clean = str_to_lower(str_squish(Wilayah)), Prevalensi = (KasusMalaria / JumlahPenduduk) * 100 # ✅ TAMBAHKAN INI ) # ============================================================================= # LOAD PETA NTT DARI SHAPEFILE LOKAL # ============================================================================= message("📥 Loading peta NTT dari shapefile lokal…") # 1. Baca shapefile Indonesia shp_indo <- st_read( "geoBoundaries-IDN-ADM2-all/geoBoundaries-IDN-ADM2.shp" ) # 2. Daftar kabupaten/kota NTT kab_ntt <- c( "Alor", "Belu", "Ende", "Flores Timur", "Kupang", "Lembata", "Malaka", "Manggarai", "Manggarai Barat", "Manggarai Timur", "Nagekeo", "Ngada", "Rote Ndao", "Sabu Raijua", "Sikka", "Sumba Barat", "Sumba Barat Daya", "Sumba Tengah", "Sumba Timur", "Timor Tengah Selatan", "Timor Tengah Utara", "Kota Kupang" ) # 3. Filter NTT shp_ntt <- shp_indo %>% mutate(kab_clean = str_to_lower(str_squish(shapeName))) %>% filter(kab_clean %in% str_to_lower(kab_ntt)) # 4. Cek hasil message("✅ Peta NTT berhasil dimuat!") message(paste("📍 Jumlah wilayah:", nrow(shp_ntt))) cat("\n=== NAMA WILAYAH DI SHAPEFILE ===\n") print(shp_ntt$shapeName) # ============================================================================= # JOIN DATA SPASIAL + ATRIBUT # ============================================================================= ntt_sf <- shp_ntt %>% left_join(data, by = "kab_clean") # ============================================================================= # PERSIAPAN DATA & HITUNG PREVALENSI + KEPADATAN PENDUDUK # ============================================================================= ntt_sf <- ntt_sf %>% mutate( Prevalensi = (KasusMalaria / JumlahPenduduk) * 100) Prevalensi <- data$KasusMalaria / data$JumlahPenduduk * 100 # Cek nama kolom colnames(ntt_sf) # Hitung wilayah yang berhasil di-merge wilayah_terisi <- sum(!is.na(ntt_sf$KasusMalaria)) message(paste("\n✅ Wilayah dengan data:", wilayah_terisi, "dari", nrow(ntt_sf))) if(wilayah_terisi == 0) { warning("⚠️ TIDAK ADA DATA YANG TER-MERGE! Cek nama wilayah di Excel!") } # ============================================================================= # PERSIAPAN DATA & HITUNG PREVALENSI # ============================================================================= ntt_sf <- ntt_sf %>% mutate( Prevalensi = (KasusMalaria / JumlahPenduduk) * 100 # KepadatanPenduduk sudah ada dari join, tidak perlu dihitung ulang ) # ============================================================================= # BOBOT SPASIAL (KNN k=3) # Menjamin pulau-pulau di NTT (Sumba, Flores, Timor) terhubung # ============================================================================= coords <- st_centroid(ntt_sf) %>% st_coordinates() nb_knn <- knearneigh(coords, k = 3) nb_work <- knn2nb(nb_knn) lw_work <- nb2listw(nb_work, style = "W", zero.policy = TRUE) # ============================================================================= # GLOBAL MORAN'S I (UJI UMUM) # ============================================================================= vars <- c("Prevalensi", "KasusMalaria", "SanitasiLayak", "RumahLayak", "JumlahPuskesmas", "KepadatanPenduduk") moran_results <- purrr::map_dfr(vars, function(v){ # Cek apakah kolom ada dan bertipe numerik if(!is.numeric(ntt_sf[[v]])) { warning(paste("Variabel", v, "tidak ditemukan atau bukan angka!")) return(NULL) } mt <- spdep::moran.test(ntt_sf[[v]], lw_work, zero.policy = TRUE) tibble::tibble( Variabel = v, Moran_I = as.numeric(mt$estimate["Moran I statistic"]), P_value = mt$p.value ) }) print("--- Hasil Global Moran's I ---") print(moran_results) # ============================================================================= # LOCAL MORAN'S I (LISA) - ANALISIS KLASTER LOKAL # ============================================================================= # Standarisasi Z-score (Wajib secara teori untuk menentukan High/Low) ntt_sf$z_pre <- as.numeric(scale(ntt_sf$Prevalensi)) # Hitung Spatial Lag dan Local Moran ntt_sf$lag_z <- lag.listw(lw_work, ntt_sf$z_pre, zero.policy = TRUE) lisa_res <- localmoran(ntt_sf$Prevalensi, lw_work, zero.policy = TRUE) ntt_sf$p_lisa <- lisa_res[, 5] # Klasifikasi Kuadran LISA ntt_sf <- ntt_sf %>% mutate(LISA_cat = case_when( p_lisa > 0.05 ~ "Not Significant", z_pre >= 0 & lag_z >= 0 ~ "High-High", z_pre < 0 & lag_z < 0 ~ "Low-Low", z_pre >= 0 & lag_z < 0 ~ "High-Low", z_pre < 0 & lag_z >= 0 ~ "Low-High", TRUE ~ "Not Significant" )) message("\n✅ Analisis LISA selesai!") print(table(ntt_sf$LISA_cat)) # SIMPAN OBJEK INI UNTUK DASHBOARD shp <- ntt_sf # Gunakan ntt_sf sebagai shp untuk konsistensi dengan dashboard # ============================================================================= # ESTIMASI MODEL REGRESI # ============================================================================= # STEP 1: MODEL LINEAR & SPASIAL # -------------------------------- # 1.1 Model OLS (Non-Spasial) ols_malaria <- lm(Prevalensi ~ SanitasiLayak + RumahLayak + JumlahPuskesmas + KepadatanPenduduk, data = ntt_sf) # 1.2 Model Spasial SAR & SEM sar_malaria <- lagsarlm(Prevalensi ~ SanitasiLayak + RumahLayak + JumlahPuskesmas + KepadatanPenduduk, data = ntt_sf, listw = lw_work, zero.policy = TRUE) sem_malaria <- errorsarlm(Prevalensi ~ SanitasiLayak + RumahLayak + JumlahPuskesmas + KepadatanPenduduk, data = ntt_sf, listw = lw_work, zero.policy = TRUE) # 1.3 Perbandingan Model Spasial model_comp <- data.frame( Model = c("OLS", "SAR", "SEM"), AIC = c(AIC(ols_malaria), AIC(sar_malaria), AIC(sem_malaria)), LogLik = c(as.numeric(logLik(ols_malaria)), as.numeric(logLik(sar_malaria)), as.numeric(logLik(sem_malaria))), P_Value_Spasial = c(NA, summary(sar_malaria)$rho[4], summary(sem_malaria)$lambda[4]) ) # STEP 2: UJI ASUMSI # -------------------------------- # 2.1 Uji Multikolinearitas (VIF) vif_results <- car::vif(ols_malaria) vif_df <- data.frame( Variabel = names(vif_results), VIF = as.numeric(vif_results), Status = ifelse(vif_results > 10, "⚠️ Tinggi", ifelse(vif_results > 5, "⚠️ Sedang", "✅ Aman")) ) # 2.2 Uji Dispersi Data mean_kasus <- mean(ntt_sf$KasusMalaria) var_kasus <- var(ntt_sf$KasusMalaria) dispersion_ratio <- var_kasus / mean_kasus dispersion_df <- data.frame( Statistik = c("Mean", "Variance", "Dispersion Ratio", "Status"), Nilai = c(round(mean_kasus, 2), round(var_kasus, 2), round(dispersion_ratio, 2), ifelse(dispersion_ratio > 1.5, "⚠️ Overdispersion", "✅ Normal")) ) # STEP 3: MODEL NEGATIVE BINOMIAL # -------------------------------- library(MASS) # 3.1 Model Negative Binomial DENGAN OFFSET nb_model <- glm.nb(KasusMalaria ~ SanitasiLayak + RumahLayak + JumlahPuskesmas + KepadatanPenduduk + offset(log(JumlahPenduduk)), data = ntt_sf) # 3.2 Hitung IRR (Incident Rate Ratio) coef_nb <- summary(nb_model)$coefficients irr_df <- data.frame( Variabel = rownames(coef_nb)[-1], # Exclude intercept Koefisien = coef_nb[-1, 1], SE = coef_nb[-1, 2], P_value = coef_nb[-1, 4], IRR = exp(coef_nb[-1, 1]), IRR_Lower = exp(coef_nb[-1, 1] - 1.96 * coef_nb[-1, 2]), IRR_Upper = exp(coef_nb[-1, 1] + 1.96 * coef_nb[-1, 2]), Signifikan = ifelse(coef_nb[-1, 4] < 0.05, "✅ Ya", "❌ Tidak") ) # 3.3 Pseudo R-squared & Model Fit pseudo_r2 <- 1 - (nb_model$deviance / nb_model$null.deviance) deviance_explained <- (1 - nb_model$deviance / nb_model$null.deviance) * 100 model_fit <- data.frame( Metrik = c("AIC", "Log-Likelihood", "Pseudo R²", "Theta (Dispersion)", "Deviance Explained (%)"), Nilai = c(round(AIC(nb_model), 2), round(as.numeric(logLik(nb_model)), 2), round(pseudo_r2, 4), round(nb_model$theta, 4), round(deviance_explained, 2)) ) message("\n✅ Semua model berhasil diestimasi!") print("--- VIF Test ---") print(vif_df) print("--- Dispersion Test ---") print(dispersion_df) print("--- IRR Results ---") print(irr_df) # ============================================================================= # PERHITUNGAN PRIORITAS WILAYAH # =============================================================================# ============================================================================= # PERHITUNGAN PRIORITAS WILAYAH (METODE Z-SCORE) # ============================================================================= message("\n✅ Menghitung prioritas wilayah dengan Z-score standardization…") # Hitung prioritas ntt_prioritas <- ntt_sf %>% st_drop_geometry() %>% # Drop geometry untuk perhitungan # Drop NA untuk variabel yang dipakai drop_na(Prevalensi, SanitasiLayak, RumahLayak, JumlahPuskesmas, KepadatanPenduduk) %>% mutate( # Z-score standardization z_prevalensi = as.numeric(scale(Prevalensi)), z_sanitasi = -as.numeric(scale(SanitasiLayak)), # Negatif: sanitasi rendah = risiko tinggi z_rumah = -as.numeric(scale(RumahLayak)), # Negatif: rumah tidak layak = risiko tinggi z_puskesmas = -as.numeric(scale(JumlahPuskesmas)), # Negatif: puskesmas sedikit = risiko tinggi z_kepadatan = as.numeric(scale(KepadatanPenduduk)), # Positif: kepadatan tinggi = risiko tinggi # Indeks prioritas (weighted sum of z-scores) indeks_prioritas = 0.50 * z_prevalensi + 0.30 * z_sanitasi + 0.10 * z_puskesmas + 0.05 * z_rumah + 0.05 * z_kepadatan ) # Hitung quantile untuk kategorisasi q50 <- quantile(ntt_prioritas$indeks_prioritas, 0.50, na.rm = TRUE) q75 <- quantile(ntt_prioritas$indeks_prioritas, 0.75, na.rm = TRUE) # Kategorisasi & Ranking ntt_prioritas <- ntt_prioritas %>% mutate( kategori_prioritas = case_when( indeks_prioritas >= q75 ~ "Prioritas Tinggi", indeks_prioritas >= q50 ~ "Prioritas Sedang", TRUE ~ "Prioritas Rendah" ), rank_prioritas = rank(-indeks_prioritas, ties.method = "first") ) %>% arrange(desc(indeks_prioritas)) # Gabungkan kembali dengan geometri untuk peta # Gabungkan kembali dengan geometri untuk peta ntt_prioritas_sf <- ntt_sf %>% left_join( ntt_prioritas %>% # Tambahkan dplyr:: di depan select agar tidak error dplyr::select(Wilayah, z_prevalensi, z_sanitasi, z_rumah, z_puskesmas, z_kepadatan, indeks_prioritas, kategori_prioritas, rank_prioritas), by = "Wilayah" ) # ============================================================================= # UI # ============================================================================= ui <- dashboardPage( dashboardHeader(title = "Dashboard Malaria NTT"), dashboardSidebar( sidebarMenu( menuItem("Data", tabName = "data", icon = icon("table")), menuItem("Statistika Deskriptif", tabName = "statdes", icon = icon("chart-bar")), menuItem("Matriks Korelasi", tabName = "korelasi", icon = icon("project-diagram")), menuItem("Prevalensi", tabName = "prevalensi", icon = icon("percentage")), menuItem("Peta Visualisasi", tabName = "peta", icon = icon("map")), menuItem("Autokorelasi Spasial", tabName = "autokorelasi", icon = icon("globe")), menuItem("Tren Waktu", tabName = "tren", icon = icon("chart-line")), menuItem("Modeling", tabName = "modeling", icon = icon("brain")), menuItem("Wilayah Prioritas", tabName = "prioritas", icon = icon("exclamation-triangle")), menuItem("Tentang Dashboard", tabName = "about", icon = icon("info-circle")) ) ), dashboardBody( tags$head( tags$style(HTML(" #kasus_tertinggi .small-box h3, #kasus_terendah .small-box h3 { font-size: 18px !important; } .interpretasi-box { background-color: #f9f9f9; border-left: 4px solid #3498db; padding: 15px; margin: 10px 0; border-radius: 4px; } .interpretasi-box h4 { margin-top: 0; color: #2c3e50; font-weight: bold; } .rekomendasi-box { background-color: #fff3cd; border-left: 4px solid #ffc107; padding: 15px; margin: 10px 0; border-radius: 4px; } .rekomendasi-box h4 { margin-top: 0; color: #856404; font-weight: bold; } ")) ), tabItems( # ============================================ # TAB 1: DATA # ============================================ tabItem(tabName = "data", fluidRow( valueBoxOutput("total_kasus", width = 3), valueBoxOutput("total_penduduk", width = 3), valueBoxOutput("kasus_tertinggi", width = 3), valueBoxOutput("kasus_terendah", width = 3) ), fluidRow( box( title = "Tabel Data Malaria", width = 12, status = "primary", solidHeader = TRUE, DTOutput("tabel_data") ) ) ), # ============================================ # TAB 2: STATISTIKA DESKRIPTIF # ============================================ tabItem(tabName = "statdes", fluidRow( box( title = "Statistika Deskriptif", width = 12, status = "primary", solidHeader = TRUE, DTOutput("tabel_statdes") ) ), fluidRow( box( title = "Visualisasi", width = 12, status = "info", solidHeader = TRUE, selectInput("variabel", "Pilih Variabel:", choices = c("KasusMalaria", "JumlahPenduduk", "SanitasiLayak", "RumahLayak", "KepadatanPenduduk")), selectInput("jenis_plot", "Pilih Jenis Plot:", choices = c("Histogram", "Boxplot", "Grafik Batang per Wilayah")), plotOutput("plot_visualisasi", height = 400, click = "plot_click"), hr(), verbatimTextOutput("info_plot") ) ) ), # ============================================ # TAB 3: MATRIKS KORELASI # ============================================ tabItem(tabName = "korelasi", fluidRow( box( title = "Matriks Korelasi", width = 6, status = "primary", solidHeader = TRUE, plotOutput("plot_korelasi", height = 400) ), box( title = "Nilai Korelasi", width = 6, status = "primary", solidHeader = TRUE, DTOutput("tabel_korelasi") ) ), fluidRow( box( title = "Interpretasi Korelasi", width = 12, status = "warning", solidHeader = TRUE, htmlOutput("interpretasi_korelasi") ) ) ), # ============================================ # TAB 4: PREVALENSI # ============================================ tabItem(tabName = "prevalensi", fluidRow( box( title = "Prevalensi Malaria per Wilayah (%)", width = 12, status = "primary", solidHeader = TRUE, plotOutput("plot_prevalensi", height = 500) ) ), fluidRow( box( title = "Interpretasi Prevalensi", width = 12, status = "info", solidHeader = TRUE, htmlOutput("interpretasi_prevalensi") ) ) ), # ============================================ # TAB 5: PETA VISUALISASI # ============================================ tabItem(tabName = "peta", fluidRow( box( title = "Pengaturan Peta", width = 12, status = "primary", solidHeader = TRUE, column(4, selectInput("var_peta", "Pilih Variabel:", choices = c("Kasus Malaria" = "KasusMalaria", "Prevalensi (%)" = "Prevalensi", "Sanitasi Layak (%)" = "SanitasiLayak", "Rumah Layak (%)" = "RumahLayak", "Kepadatan Penduduk" = "KepadatanPenduduk"), selected = "Prevalensi") ), column(4, selectInput("warna_rendah", "Warna Rendah:", choices = c("Hijau" = "green", "Biru" = "blue", "Kuning" = "yellow", "Ungu" = "purple"), selected = "yellow") ), column(4, selectInput("warna_tinggi", "Warna Tinggi:", choices = c("Merah" = "red", "Oranye" = "orange", "Pink" = "pink", "Coklat" = "brown"), selected = "red") ) ) ), fluidRow( box( title = "Peta Tematik NTT", width = 12, status = "info", solidHeader = TRUE, leafletOutput("peta_interaktif", height = 600) ) ), fluidRow( box( title = "Statistik Peta", width = 6, status = "warning", solidHeader = TRUE, verbatimTextOutput("statistik_peta") ), box( title = "Interpretasi Pola Spasial", width = 6, status = "warning", solidHeader = TRUE, htmlOutput("interpretasi_peta") ) ) ), # ============================================ # TAB 6: AUTOKORELASI SPASIAL # ============================================ tabItem(tabName = "autokorelasi", fluidRow( box( title = "Global Moran's I - Pengujian Autokorelasi Spasial", width = 12, status = "primary", solidHeader = TRUE, DTOutput("tabel_moran") ) ), fluidRow( box( title = "Interpretasi Global Moran's I", width = 12, status = "info", solidHeader = TRUE, htmlOutput("interpretasi_moran") ) ), fluidRow( box( title = "Peta LISA - Local Indicators of Spatial Association", width = 12, status = "warning", solidHeader = TRUE, leafletOutput("peta_lisa", height = 600) ) ), fluidRow( box( title = "Interpretasi Peta LISA", width = 12, status = "success", solidHeader = TRUE, htmlOutput("interpretasi_lisa") ) ) ), # TAB TREN WAKTU tabItem(tabName = "tren", # Section 1: Info Data fluidRow( box( title = "📋 Informasi Data Tren", width = 12, status = "primary", solidHeader = TRUE, htmlOutput("tren_info") ) ), # Section 2: Kontrol & Grafik fluidRow( box( title = "⚙️ Pengaturan Visualisasi", width = 4, status = "info", solidHeader = TRUE, uiOutput("tren_wilayah_ui"), hr(), checkboxInput("tren_show_avg", "Tampilkan rata-rata provinsi", value = TRUE), checkboxInput("tren_show_trend", "Tampilkan garis tren (regresi)", value = TRUE) ), box( title = "📈 Grafik Tren Kasus Malaria", width = 8, status = "success", solidHeader = TRUE, plotOutput("tren_plot", height = "450px") ) ), # Section 3: Tabel Data fluidRow( box( title = "📊 Tabel Data Tren & Statistik", width = 12, status = "info", solidHeader = TRUE, DTOutput("tren_table") ) ), # Section 4: Interpretasi fluidRow( box( title = "💡 Interpretasi & Analisis Tren", width = 12, status = "warning", solidHeader = TRUE, htmlOutput("tren_interpretation") ) ) ), # ============================================ # TAB 7: MODELING REGRESI # ============================================ tabItem(tabName = "modeling", # SECTION 1 fluidRow( box( title = "📍 Model Awal - Linear & Spasial", width = 12, status = "primary", solidHeader = TRUE, collapsible = TRUE, h4("Perbandingan Model Linear"), DTOutput("tabel_model_linear"), hr(), htmlOutput("kesimpulan_section1") ) ), # SECTION 2 fluidRow( box( title = "🔬 Diagnostik Model & Karakteristik Data", width = 12, status = "warning", solidHeader = TRUE, collapsible = TRUE, selectInput( "pilih_uji", "Pilih Uji yang Ingin Ditampilkan:", choices = c( "Uji Multikolinearitas (VIF)" = "vif", "Uji Dispersi Data" = "dispersi" ), selected = "vif" ), hr(), uiOutput("output_uji_asumsi") ) ), # SECTION 3 fluidRow( box( title = "⚖️ Keputusan Pemilihan Model", width = 12, status = "danger", solidHeader = TRUE, collapsible = TRUE, htmlOutput("keputusan_model_visual") ) ), # SECTION 3.5 fluidRow( box( title = "📐 Model Matematis Negative Binomial", width = 12, status = "info", solidHeader = TRUE, collapsible = TRUE, collapsed = TRUE, htmlOutput("model_matematis_nb") ) ), # SECTION 4 fluidRow( box( title = "🏆 IRR dan Evaluasi Model", width = 12, status = "success", solidHeader = TRUE, collapsible = TRUE, fluidRow( column(6, h4("📊 Koefisien & Incidence Rate Ratio (IRR)"), DTOutput("tabel_irr") ), column(6, h4("📈 Visualisasi IRR (Forest Plot)"), plotOutput("plot_irr", height = 400) ) ), hr(), fluidRow( column(6, h4("📋 Evaluasi Model"), DTOutput("tabel_model_fit") ), column(6, h4("💡 Interpretasi Variabel Signifikan"), htmlOutput("interpretasi_final") ) ) ) ) ), # ============================================ # TAB 8: WILAYAH PRIORITAS # ============================================ tabItem(tabName = "prioritas", # Section 2: Bar Chart Prioritas fluidRow( box( title = "📊 Ranking Wilayah Berdasarkan Skor Prioritas", width = 12, status = "info", solidHeader = TRUE, plotOutput("plot_prioritas", height = 600) ) ), # Section 3: Peta Prioritas fluidRow( box( title = "🗺️ Peta Wilayah Prioritas", width = 12, status = "warning", solidHeader = TRUE, leafletOutput("peta_prioritas", height = 600) ) ), # Section 4: Tabel Detail Prioritas fluidRow( box( title = "📋 Tabel Detail Skor Prioritas per Wilayah", width = 12, status = "success", solidHeader = TRUE, DTOutput("tabel_prioritas") ) ), # Section 5: Rekomendasi Intervensi fluidRow( box( title = "💡 Rekomendasi Intervensi Berdasarkan Prioritas", width = 12, status = "danger", solidHeader = TRUE, htmlOutput("rekomendasi_prioritas") ) ) ), # ============================================ # TAB: TENTANG DASHBOARD # ============================================ tabItem(tabName = "about", fluidRow( box( title = "📊 Tentang Dashboard Analisis Malaria NTT", status = "primary", solidHeader = TRUE, width = 12, HTML(" <div style='font-size:15px; line-height:1.8;'> <h3 style='color:#e74c3c; margin-top:0;'>Dashboard Analisis Malaria Nusa Tenggara Timur</h3> <p>Dashboard interaktif untuk analisis epidemiologi malaria di 22 kabupaten/kota NTT dengan pendekatan statistik spasial dan pemodelan prediktif.</p> <hr style='border-top: 2px solid #e74c3c; margin:20px 0;'> <h4 style='color:#e74c3c;'>📌 Fitur Dashboard</h4> <ul style='margin-left:20px; line-height:2;'> <li>Statistika Deskriptif & Visualisasi Data</li> <li>Analisis Korelasi & Prevalensi</li> <li>Peta Choropleth Interaktif</li> <li>Autokorelasi Spasial (Global Moran's I & LISA)</li> <li>Analisis Tren Temporal</li> <li>Modeling Negative Binomial Regression</li> <li>Penentuan Wilayah Prioritas (Z-score Method)</li> </ul> <hr style='border-top: 1px solid #ddd; margin:20px 0;'> <div style='text-align:center; margin-top:20px; padding:20px; background:#ffe6e6; border-radius:8px;'> <h4 style='color:#c0392b; margin-top:0;'>📧 Informasi</h4> <p style='font-size:14px; color:#2c3e50; line-height:1.8;'> <strong>Dibuat oleh:</strong> Tansya Putri Rizkya Zakaria<br> <strong>Mata Kuliah:</strong> Epidemiologi<br> <strong>Dosen Pengampu:</strong> Dr. I Gede Nyoman Mindra Jaya, S.Si., M.Si,<br> <strong>Institusi:</strong> Universitas Padjadjaran<br> <strong>Tahun:</strong> 2024/2025 </p> </div> </div> ") ) ) ) ) # <-- TUTUP tabItems ) # <-- TUTUP dashboardBody ) # <-- TUTUP dashboardPage # ============================================================================= # SERVER # ============================================================================= server <- function(input, output, session) { # ============================================ # TAB 1: INFO BOXES # ============================================ output$total_kasus <- renderValueBox({ valueBox( format(sum(data$KasusMalaria), big.mark = ","), "Total Kasus Malaria", icon = icon("viruses"), color = "red" ) }) output$total_penduduk <- renderValueBox({ valueBox( format(sum(data$JumlahPenduduk), big.mark = ","), "Total Penduduk", icon = icon("users"), color = "blue" ) }) output$kasus_tertinggi <- renderValueBox({ idx <- which.max(data$KasusMalaria) valueBox( paste(data$Wilayah[idx], "-", data$KasusMalaria[idx]), "Kasus Tertinggi", icon = icon("arrow-up"), color = "orange" ) }) output$kasus_terendah <- renderValueBox({ idx <- which.min(data$KasusMalaria) valueBox( paste(data$Wilayah[idx], "-", data$KasusMalaria[idx]), "Kasus Terendah", icon = icon("arrow-down"), color = "green" ) }) # ============================================ # TAB 1: TABEL DATA # ============================================ output$tabel_data <- renderDT({ datatable( data %>% dplyr::select(Wilayah, KasusMalaria, JumlahPenduduk, SanitasiLayak, RumahLayak, JumlahPuskesmas, KepadatanPenduduk), # ✅ Pilih kolom yang mau ditampilkan options = list(pageLength = 10, scrollX = TRUE), rownames = FALSE ) }) # ============================================ # TAB 2: STATISTIKA DESKRIPTIF # ============================================ output$tabel_statdes <- renderDT({ vars <- c("KasusMalaria", "JumlahPenduduk", "SanitasiLayak", "RumahLayak", "KepadatanPenduduk") stat_df <- data.frame( Variabel = vars, Mean = sapply(vars, function(v) round(mean(data[[v]], na.rm = TRUE), 2)), Median = sapply(vars, function(v) round(median(data[[v]], na.rm = TRUE), 2)), SD = sapply(vars, function(v) round(sd(data[[v]], na.rm = TRUE), 2)), Min = sapply(vars, function(v) round(min(data[[v]], na.rm = TRUE), 2)), Max = sapply(vars, function(v) round(max(data[[v]], na.rm = TRUE), 2)), Q1 = sapply(vars, function(v) round(quantile(data[[v]], 0.25, na.rm = TRUE), 2)), Q3 = sapply(vars, function(v) round(quantile(data[[v]], 0.75, na.rm = TRUE), 2)) ) datatable(stat_df, options = list(pageLength = 5, scrollX = TRUE), rownames = FALSE) }) # ============================================ # TAB 2: VISUALISASI # ============================================ output$plot_visualisasi <- renderPlot({ req(input$variabel, input$jenis_plot) if (input$jenis_plot == "Histogram") { ggplot(data, aes_string(x = input$variabel)) + geom_histogram(bins = 10, fill = "#3498db", color = "white", alpha = 0.8) + geom_text(stat = "bin", aes(label = after_stat(count), y = after_stat(count)), vjust = -0.5, bins = 10, size = 3.5) + theme_minimal() + labs(title = paste("Histogram", input$variabel), x = input$variabel, y = "Frekuensi") + theme(plot.title = element_text(hjust = 0.5, face = "bold", size = 16)) } else if (input$jenis_plot == "Boxplot") { ggplot(data, aes(x = "", y = .data[[input$variabel]])) + geom_boxplot(fill = "#e74c3c", alpha = 0.7, outlier.colour = "red", outlier.size = 3) + geom_point(alpha = 0.5, size = 3, color = "#2c3e50", position = position_jitter(width = 0.1)) + theme_minimal() + labs(title = paste("Boxplot", input$variabel), y = input$variabel, subtitle = "Klik pada titik untuk melihat informasi wilayah") + theme(plot.title = element_text(hjust = 0.5, face = "bold", size = 16), plot.subtitle = element_text(hjust = 0.5, size = 11, color = "gray40"), axis.text.x = element_blank(), axis.title.x = element_blank()) } else { ggplot(data, aes(x = reorder(Wilayah, -.data[[input$variabel]]), y = .data[[input$variabel]])) + geom_col(fill = "#3498db", alpha = 0.7) + geom_text(aes(label = round(.data[[input$variabel]], 1)), hjust = -0.2, size = 3.5) + coord_flip() + theme_minimal() + labs(title = paste("Grafik Batang", input$variabel, "per Wilayah"), x = "Wilayah", y = input$variabel) + theme(plot.title = element_text(hjust = 0.5, face = "bold", size = 16), axis.text.y = element_text(size = 10)) } }) # ============================================ # TAB 2: INFO PLOT # ============================================ output$info_plot <- renderPrint({ req(input$variabel, input$jenis_plot) if (input$jenis_plot == "Histogram") { cat("=== HISTOGRAM ===\n") cat("Menampilkan distribusi frekuensi dari", input$variabel, "\n\n") cat("Statistik Deskriptif:\n") cat("Mean :", round(mean(data[[input$variabel]], na.rm = TRUE), 2), "\n") cat("Median :", round(median(data[[input$variabel]], na.rm = TRUE), 2), "\n") cat("SD :", round(sd(data[[input$variabel]], na.rm = TRUE), 2), "\n") cat("Min :", round(min(data[[input$variabel]], na.rm = TRUE), 2), "\n") cat("Max :", round(max(data[[input$variabel]], na.rm = TRUE), 2)) } else if (input$jenis_plot == "Boxplot") { if (!is.null(input$plot_click)) { clicked_y <- input$plot_click$y distances <- abs(data[[input$variabel]] - clicked_y) nearest_idx <- which.min(distances) threshold <- (max(data[[input$variabel]], na.rm = TRUE) - min(data[[input$variabel]], na.rm = TRUE)) * 0.05 if (min(distances) < threshold) { cat("=== INFORMASI WILAYAH YANG DIKLIK ===\n\n") cat("Wilayah :", data$Wilayah[nearest_idx], "\n") cat(input$variabel, ":", data[[input$variabel]][nearest_idx], "\n\n") cat("Data Lengkap:\n") cat("Kasus Malaria :", data$KasusMalaria[nearest_idx], "\n") cat("Jumlah Penduduk :", format(data$JumlahPenduduk[nearest_idx], big.mark = ","), "\n") cat("Sanitasi Layak :", data$SanitasiLayak[nearest_idx], "%\n") cat("Rumah Layak :", data$RumahLayak[nearest_idx], "%\n") cat("Kepadatan Penduduk :", round(data$KepadatanPenduduk[nearest_idx], 2), "jiwa/km²\n") } else { cat("=== BOXPLOT ===\n") cat("Klik pada titik data untuk melihat informasi wilayah\n\n") cat("Ringkasan Statistik:\n") cat("Min :", round(min(data[[input$variabel]], na.rm = TRUE), 2), "\n") cat("Q1 :", round(quantile(data[[input$variabel]], 0.25, na.rm = TRUE), 2), "\n") cat("Median :", round(median(data[[input$variabel]], na.rm = TRUE), 2), "\n") cat("Q3 :", round(quantile(data[[input$variabel]], 0.75, na.rm = TRUE), 2), "\n") cat("Max :", round(max(data[[input$variabel]], na.rm = TRUE), 2), "\n") } } else { cat("=== BOXPLOT ===\n") cat("Klik pada titik data untuk melihat informasi wilayah\n\n") cat("Ringkasan Statistik:\n") cat("Min :", round(min(data[[input$variabel]], na.rm = TRUE), 2), "\n") cat("Q1 :", round(quantile(data[[input$variabel]], 0.25, na.rm = TRUE), 2), "\n") cat("Median :", round(median(data[[input$variabel]], na.rm = TRUE), 2), "\n") cat("Q3 :", round(quantile(data[[input$variabel]], 0.75, na.rm = TRUE), 2), "\n") cat("Max :", round(max(data[[input$variabel]], na.rm = TRUE), 2), "\n") } } else { cat("=== GRAFIK BATANG PER WILAYAH ===\n") cat("Menampilkan", input$variabel, "untuk setiap wilayah (diurutkan dari tertinggi ke terendah)\n\n") sorted_data <- data[order(data[[input$variabel]], decreasing = TRUE), ] for(i in 1:nrow(sorted_data)) { cat(sprintf("%-25s : %8.2f\n", sorted_data$Wilayah[i], sorted_data[[input$variabel]][i])) } } }) # ============================================ # TAB 3: MATRIKS KORELASI # ============================================ cor_matrix <- reactive({ cor(data[, c("KasusMalaria", "JumlahPenduduk", "SanitasiLayak", "RumahLayak", "KepadatanPenduduk")], use = "complete.obs") }) output$plot_korelasi <- renderPlot({ corrplot(cor_matrix(), method = "color", type = "upper", addCoef.col = "black", tl.col = "black", tl.srt = 45, col = colorRampPalette(c("#e74c3c", "white", "#3498db"))(200), title = "Matriks Korelasi", mar = c(0,0,1,0)) }) output$tabel_korelasi <- renderDT({ cor_df <- as.data.frame(round(cor_matrix(), 3)) cor_df$Variabel <- rownames(cor_df) cor_df <- cor_df[, c("Variabel", colnames(cor_matrix()))] datatable(cor_df, options = list(pageLength = 5, scrollX = TRUE), rownames = FALSE) }) output$interpretasi_korelasi <- renderUI({ cm <- cor_matrix() interpret_strength <- function(r) { abs_r <- abs(r) if (abs_r >= 0.8) "sangat kuat" else if (abs_r >= 0.6) "kuat" else if (abs_r >= 0.4) "sedang" else if (abs_r >= 0.2) "lemah" else "sangat lemah" } interpret_direction <- function(r) { if (r > 0) "positif (searah)" else "negatif (berlawanan arah)" } kor_penduduk <- cm["KasusMalaria", "JumlahPenduduk"] kor_sanitasi <- cm["KasusMalaria", "SanitasiLayak"] kor_rumah <- cm["KasusMalaria", "RumahLayak"] kor_kepadatan <- cm["KasusMalaria", "KepadatanPenduduk"] interpretasi <- "<ul style='line-height: 1.8;'>" interpretasi <- paste0(interpretasi, "<li><b>Kasus Malaria vs Jumlah Penduduk:</b> Korelasi ", interpret_direction(kor_penduduk), " dengan kekuatan ", interpret_strength(kor_penduduk), " (r = ", round(kor_penduduk, 3), "). ", ifelse(kor_penduduk > 0, "Semakin besar jumlah penduduk, cenderung semakin tinggi kasus malaria.", "Semakin besar jumlah penduduk, cenderung semakin rendah kasus malaria."), "</li>") interpretasi <- paste0(interpretasi, "<li><b>Kasus Malaria vs Kepadatan Penduduk:</b> Korelasi ", interpret_direction(kor_kepadatan), " dengan kekuatan ", interpret_strength(kor_kepadatan), " (r = ", round(kor_kepadatan, 3), "). ", ifelse(kor_kepadatan > 0, "Wilayah dengan kepadatan penduduk tinggi cenderung memiliki kasus malaria lebih tinggi.", "Wilayah dengan kepadatan penduduk tinggi cenderung memiliki kasus malaria lebih rendah."), "</li>") interpretasi <- paste0(interpretasi, "<li><b>Kasus Malaria vs Sanitasi Layak:</b> Korelasi ", interpret_direction(kor_sanitasi), " dengan kekuatan ", interpret_strength(kor_sanitasi), " (r = ", round(kor_sanitasi, 3), "). ", ifelse(kor_sanitasi > 0, "Wilayah dengan sanitasi layak yang lebih tinggi cenderung memiliki kasus malaria lebih tinggi (ini tidak biasa dan perlu investigasi lebih lanjut).", "Wilayah dengan sanitasi layak yang lebih tinggi cenderung memiliki kasus malaria lebih rendah (sesuai dengan teori kesehatan masyarakat)."), "</li>") interpretasi <- paste0(interpretasi, "<li><b>Kasus Malaria vs Rumah Layak:</b> Korelasi ", interpret_direction(kor_rumah), " dengan kekuatan ", interpret_strength(kor_rumah), " (r = ", round(kor_rumah, 3), "). ", ifelse(kor_rumah > 0, "Wilayah dengan rumah layak yang lebih tinggi cenderung memiliki kasus malaria lebih tinggi (ini tidak biasa dan perlu investigasi lebih lanjut).", "Wilayah dengan rumah layak yang lebih tinggi cenderung memiliki kasus malaria lebih rendah (sesuai dengan teori kesehatan masyarakat)."), "</li>") interpretasi <- paste0(interpretasi, "</ul>") HTML(interpretasi) }) # ============================================ # TAB 4: PREVALENSI # ============================================ output$plot_prevalensi <- renderPlot({ ggplot(data, aes(x = reorder(Wilayah, Prevalensi), y = Prevalensi)) + geom_col(aes(fill = Prevalensi), alpha = 0.8) + geom_text(aes(label = paste0(round(Prevalensi, 2), "%")), hjust = -0.1, size = 3.5) + scale_fill_gradient(low = "#2ecc71", high = "#e74c3c", name = "Prevalensi (%)") + coord_flip() + theme_minimal() + labs(title = "Prevalensi Malaria per Wilayah", subtitle = "Persentase kasus malaria terhadap jumlah penduduk", x = "Wilayah", y = "Prevalensi (%)") + theme(plot.title = element_text(hjust = 0.5, face = "bold", size = 18), plot.subtitle = element_text(hjust = 0.5, size = 12, color = "gray40"), axis.text.y = element_text(size = 10), legend.position = "right") }) output$interpretasi_prevalensi <- renderUI({ prev_agregat <- (sum(data$KasusMalaria) / sum(data$JumlahPenduduk)) * 100 total_penduduk <- sum(data$JumlahPenduduk) total_kasus <- sum(data$KasusMalaria) idx_max <- which.max(data$Prevalensi) wilayah_max <- data$Wilayah[idx_max] prev_max <- data$Prevalensi[idx_max] kasus_max <- data$KasusMalaria[idx_max] penduduk_max <- data$JumlahPenduduk[idx_max] idx_min <- which.min(data$Prevalensi) wilayah_min <- data$Wilayah[idx_min] prev_min <- data$Prevalensi[idx_min] kasus_min <- data$KasusMalaria[idx_min] penduduk_min <- data$JumlahPenduduk[idx_min] interpretasi <- paste0( "<div class='interpretasi-box'>", "<h4>📊 Prevalensi Agregat NTT</h4>", "<p>Dari <b>", format(total_penduduk, big.mark = "."), " penduduk</b> di NTT, terdapat <b>", format(total_kasus, big.mark = "."), " kasus malaria</b> atau sekitar <b>", round(prev_agregat, 2), "%</b>. ", "Artinya, dari setiap <b>100 penduduk</b>, sekitar <b>", ceiling(prev_agregat), " orang</b> terkena malaria.", # ✅ GANTI: round() → ceiling() "</p></div>", "<div class='interpretasi-box'>", "<h4>🔴 Prevalensi Tertinggi: ", wilayah_max, "</h4>", "<p>Dari <b>", format(penduduk_max, big.mark = "."), " penduduk</b> di ", wilayah_max, ", terdapat <b>", format(kasus_max, big.mark = "."), " kasus malaria</b> atau <b>", round(prev_max, 2), "%</b>. ", "Artinya, dari setiap <b>100 penduduk</b>, sekitar <b>", ceiling(prev_max), " orang</b> terkena malaria. ", # ✅ GANTI: round() → ceiling() "Wilayah ini merupakan prioritas utama untuk intervensi pengendalian malaria.", "</p></div>", "<div class='interpretasi-box'>", "<h4>🟢 Prevalensi Terendah: ", wilayah_min, "</h4>", "<p>Dari <b>", format(penduduk_min, big.mark = "."), " penduduk</b> di ", wilayah_min, ", terdapat <b>", format(kasus_min, big.mark = "."), " kasus malaria</b> atau <b>", round(prev_min, 2), "%</b>. ", "Artinya, dari setiap <b>100 penduduk</b>, sekitar <b>", ceiling(prev_min), " orang</b> terkena malaria. ", # ✅ GANTI: round() → ceiling() "Wilayah ini menunjukkan keberhasilan program pengendalian malaria dan dapat dijadikan contoh best practice.", "</p></div>" ) HTML(interpretasi) }) # ============================================ # TAB 5: PETA VISUALISASI # ============================================ output$peta_interaktif <- renderLeaflet({ req(input$var_peta) if (is.null(shp) || nrow(shp) == 0) { return( leaflet() %>% addTiles() %>% setView(lng = 120.5, lat = -9.5, zoom = 7) %>% addPopups(lng = 120.5, lat = -9.5, popup = "<b style='color: red;'>PETA NTT GAGAL DIMUAT!</b><br> Cek kembali path shapefile Anda.", options = popupOptions(closeButton = FALSE)) ) } var_label <- switch(input$var_peta, "KasusMalaria" = "Kasus Malaria", "Prevalensi" = "Prevalensi (%)", "SanitasiLayak" = "Sanitasi Layak (%)", "RumahLayak" = "Rumah Layak (%)", "KepadatanPenduduk" = "Kepadatan Penduduk (jiwa/km²)") warna_rendah <- switch(input$warna_rendah, "green" = "#27ae60", "blue" = "#3498db", "yellow" = "#f1c40f", "purple" = "#9b59b6") warna_tinggi <- switch(input$warna_tinggi, "red" = "#e74c3c", "orange" = "#e67e22", "pink" = "#ff6b9d", "brown" = "#8b4513") nilai_var <- shp[[input$var_peta]] pal <- colorNumeric( palette = colorRampPalette(c(warna_rendah, warna_tinggi))(100), domain = nilai_var, na.color = "#cccccc" ) leaflet(shp) %>% addProviderTiles(providers$CartoDB.Positron) %>% addPolygons( fillColor = ~pal(get(input$var_peta)), fillOpacity = 0.8, color = "white", weight = 2, opacity = 1, highlight = highlightOptions( weight = 3, color = "#666", fillOpacity = 0.9, bringToFront = TRUE ), label = ~paste0(shapeName, ": ", ifelse(is.na(get(input$var_peta)), "Data tidak tersedia", round(get(input$var_peta), 2))), popup = ~paste0("<b>", shapeName, "</b><br>", var_label, ": ", ifelse(is.na(get(input$var_peta)), "Data tidak tersedia", paste0(round(get(input$var_peta), 2), ifelse(input$var_peta %in% c("Prevalensi", "SanitasiLayak", "RumahLayak"), "%", ""))), "<br>Kasus Malaria: ", ifelse(is.na(KasusMalaria), "N/A", KasusMalaria), "<br>Jumlah Penduduk: ", ifelse(is.na(JumlahPenduduk), "N/A", format(JumlahPenduduk, big.mark = "."))), labelOptions = labelOptions( style = list("font-weight" = "bold", padding = "3px 8px"), textsize = "13px" ) ) %>% addLegend( position = "bottomright", pal = pal, values = ~get(input$var_peta), title = var_label, opacity = 0.8, labFormat = labelFormat( suffix = ifelse(input$var_peta %in% c("Prevalensi", "SanitasiLayak", "RumahLayak"), "%", "") ) ) %>% setView(lng = 120.5, lat = -9.5, zoom = 8) }) output$statistik_peta <- renderPrint({ req(input$var_peta) nilai <- data[[input$var_peta]] var_label <- switch(input$var_peta, "KasusMalaria" = "Kasus Malaria", "Prevalensi" = "Prevalensi", "SanitasiLayak" = "Sanitasi Layak", "RumahLayak" = "Rumah Layak", "KepadatanPenduduk" = "Kepadatan Penduduk") cat("=== STATISTIK PETA ===\n\n") cat("Variabel:", var_label, "\n") cat("Wilayah dengan data:", nrow(data), "\n\n") cat("Min :", round(min(nilai, na.rm = TRUE), 2), "\n") cat("Q1 :", round(quantile(nilai, 0.25, na.rm = TRUE), 2), "\n") cat("Median :", round(median(nilai, na.rm = TRUE), 2), "\n") cat("Mean :", round(mean(nilai, na.rm = TRUE), 2), "\n") cat("Q3 :", round(quantile(nilai, 0.75, na.rm = TRUE), 2), "\n") cat("Max :", round(max(nilai, na.rm = TRUE), 2), "\n") cat("SD :", round(sd(nilai, na.rm = TRUE), 2), "\n") }) output$interpretasi_peta <- renderUI({ req(input$var_peta) nilai <- data[[input$var_peta]] q3 <- quantile(nilai, 0.75, na.rm = TRUE) q1 <- quantile(nilai, 0.25, na.rm = TRUE) wilayah_tinggi <- data %>% filter(.data[[input$var_peta]] >= q3) %>% arrange(desc(.data[[input$var_peta]])) %>% pull(Wilayah) wilayah_rendah <- data %>% filter(.data[[input$var_peta]] <= q1) %>% arrange(.data[[input$var_peta]]) %>% pull(Wilayah) var_label <- switch(input$var_peta, "KasusMalaria" = "Kasus Malaria", "Prevalensi" = "Prevalensi Malaria", "SanitasiLayak" = "Sanitasi Layak", "RumahLayak" = "Rumah Layak", "KepadatanPenduduk" = "Kepadatan Penduduk") interpretasi <- paste0( "<h5>📍 Pola Distribusi</h5>", "<p style='font-size: 13px;'>Terdapat <b>variasi tinggi</b> antar wilayah, menunjukkan kemungkinan <b>cluster/pengelompokan</b> di wilayah tertentu.</p>", "<h5 style='color: #e74c3c;'>Wilayah TINGGI (Q3 = ", round(q3, 2), ")</h5>", "<p style='font-size: 12px;'><i>", paste(wilayah_tinggi, collapse = ", "), "</i></p>", "<h5 style='color: #27ae60;'>Wilayah RENDAH (Q1 = ", round(q1, 2), ")</h5>", "<p style='font-size: 12px;'><i>", paste(wilayah_rendah, collapse = ", "), "</i></p>" ) HTML(interpretasi) }) output$tabel_moran <- renderDT({ moran_df <- moran_results %>% mutate( Moran_I = round(Moran_I, 4), P_value = round(P_value, 4), Signifikansi = ifelse(P_value < 0.05, "Signifikan ✓", "Tidak Signifikan"), Interpretasi = case_when( Moran_I > 0 & P_value < 0.05 ~ "Klaster Positif (Mengelompok)", Moran_I < 0 & P_value < 0.05 ~ "Klaster Negatif (Tersebar)", TRUE ~ "Acak (Random)" ) ) datatable(moran_df, options = list(pageLength = 10, scrollX = TRUE), rownames = FALSE) }) output$interpretasi_moran <- renderUI({ prev_moran <- moran_results %>% filter(Variabel == "Prevalensi") interpretasi <- paste0( "<div class='interpretasi-box'>", "<h4>📊 Apa itu Global Moran's I?</h4>", "<p>Global Moran's I mengukur apakah ada <b>pola pengelompokan spasial</b> (clustering) ", "pada data antar wilayah. Nilai berkisar antara <b>-1 hingga +1</b>:</p>", "<ul>", "<li><b>Moran's I > 0:</b> Pola mengelompok (wilayah dengan nilai tinggi berdekatan dengan wilayah nilai tinggi)</li>", "<li><b>Moran's I ≈ 0:</b> Pola acak (tidak ada pola spasial)</li>", "<li><b>Moran's I < 0:</b> Pola tersebar (wilayah nilai tinggi berdekatan dengan wilayah nilai rendah)</li>", "</ul></div>", "<div class='interpretasi-box'>", "<h4>🎯 Hasil untuk Prevalensi Malaria</h4>", "<p><b>Moran's I = ", round(prev_moran$Moran_I, 4), "</b> (p-value = ", round(prev_moran$P_value, 4), ")</p>", "<p>", ifelse(prev_moran$P_value < 0.05, paste0("<b>Kesimpulan:</b> Terdapat autokorelasi spasial yang <b>signifikan</b>. ", ifelse(prev_moran$Moran_I > 0, "Prevalensi malaria cenderung <b>mengelompok</b> (wilayah dengan prevalensi tinggi berdekatan dengan wilayah prevalensi tinggi lainnya).", "Prevalensi malaria cenderung <b>tersebar</b> (wilayah dengan prevalensi tinggi berdekatan dengan wilayah prevalensi rendah).")), "<b>Kesimpulan:</b> Tidak ada autokorelasi spasial yang signifikan. Pola prevalensi malaria bersifat <b>acak</b>."), "</p></div>" ) HTML(interpretasi) }) output$peta_lisa <- renderLeaflet({ # Color palette untuk LISA pal_lisa <- colorFactor( palette = c("High-High" = "#d7191c", "Low-Low" = "#2c7bb6", "High-Low" = "#fdae61", "Low-High" = "#abd9e9", "Not Significant" = "#e0e0e0"), domain = shp$LISA_cat ) leaflet(shp) %>% addProviderTiles(providers$CartoDB.Positron) %>% addPolygons( fillColor = ~pal_lisa(LISA_cat), fillOpacity = 0.8, color = "white", weight = 2, opacity = 1, highlight = highlightOptions( weight = 3, color = "#666", fillOpacity = 0.9, bringToFront = TRUE ), label = ~paste0(shapeName, ": ", LISA_cat), popup = ~paste0( "<b>", shapeName, "</b><br>", "Klaster LISA: <b>", LISA_cat, "</b><br>", "Prevalensi: ", round(Prevalensi, 2), "%<br>", "P-value: ", round(p_lisa, 4), "<br>", "Z-score: ", round(z_pre, 2) ), labelOptions = labelOptions( style = list("font-weight" = "bold", padding = "3px 8px"), textsize = "13px" ) ) %>% addLegend( position = "bottomright", pal = pal_lisa, values = ~LISA_cat, title = "Klaster LISA", opacity = 0.8 ) %>% setView(lng = 120.5, lat = -9.5, zoom = 8) }) output$interpretasi_lisa <- renderUI({ # Hitung jumlah per kategori LISA lisa_summary <- shp %>% st_drop_geometry() %>% count(LISA_cat) %>% arrange(desc(n)) # Wilayah High-High (Hotspot) hotspot <- shp %>% st_drop_geometry() %>% filter(LISA_cat == "High-High") %>% arrange(desc(Prevalensi)) %>% pull(shapeName) # Wilayah Low-Low (Coldspot) coldspot <- shp %>% st_drop_geometry() %>% filter(LISA_cat == "Low-Low") %>% arrange(Prevalensi) %>% pull(shapeName) interpretasi <- paste0( "<div class='interpretasi-box'>", "<h4>📍 Apa itu Peta LISA?</h4>", "<p>LISA (Local Indicators of Spatial Association) mengidentifikasi <b>klaster lokal</b> yang signifikan secara statistik:</p>", "<ul>", "<li><b style='color:#d7191c;'>High-High (Hotspot):</b> Wilayah prevalensi tinggi dikelilingi wilayah prevalensi tinggi → <b>Prioritas Utama Intervensi</b></li>", "<li><b style='color:#2c7bb6;'>Low-Low (Coldspot):</b> Wilayah prevalensi rendah dikelilingi wilayah prevalensi rendah → <b>Best Practice</b></li>", "<li><b style='color:#fdae61;'>High-Low (Outlier):</b> Wilayah prevalensi tinggi dikelilingi wilayah rendah → Perlu investigasi khusus</li>", "<li><b style='color:#abd9e9;'>Low-High (Outlier):</b> Wilayah prevalensi rendah dikelilingi wilayah tinggi → Risiko penularan</li>", "</ul></div>", "<div class='rekomendasi-box'>", "<h4>🔥 Hotspot (High-High) - Prioritas Tertinggi</h4>", "<p>", ifelse(length(hotspot) > 0, paste0("<b>", length(hotspot), " wilayah:</b> ", paste(hotspot, collapse = ", "), "<br>", "Wilayah ini dan tetangganya sama-sama memiliki prevalensi tinggi. ", "<b>Perlu intervensi masif dan terkoordinasi antar wilayah.</b>"), "Tidak ada hotspot signifikan terdeteksi."), "</p></div>", "<div class='interpretasi-box'>", "<h4>❄️ Coldspot (Low-Low) - Contoh Keberhasilan</h4>", "<p>", ifelse(length(coldspot) > 0, paste0("<b>", length(coldspot), " wilayah:</b> ", paste(coldspot, collapse = ", "), "<br>", "Wilayah ini dan tetangganya berhasil menekan malaria. ", "<b>Pelajari strategi mereka untuk direplikasi ke wilayah lain.</b>"), "Tidak ada coldspot signifikan terdeteksi."), "</p></div>" ) HTML(interpretasi) }) # ============================================================================= # TAB TREN: INFO DATA # ============================================================================= output$tren_info <- renderUI({ df <- tren_df() # Cek apakah data tersedia if (is.null(df)) { return(HTML(" <div style='background:#f8d7da; padding:20px; border-radius:8px; border-left:4px solid #dc3545;'> <h4 style='color:#721c24; margin-top:0;'>⚠️ Data Tren Waktu Belum Dimuat</h4> <p style='color:#721c24;'> Pastikan file Excel memiliki sheet '<b>TrenWaktu</b>' dengan format:<br> <code>Wilayah | 2018 | 2019 | 2020 | 2021 | …</code> </p> </div> ")) } # Ambil kolom tahun (semua kolom kecuali 'Wilayah') year_cols <- names(df)[names(df) != "Wilayah"] HTML(paste0( "<div style='background:#d4edda; padding:20px; border-radius:8px; border-left:4px solid #28a745;'> <h4 style='color:#155724; margin-top:0;'>✅ Data Tren Waktu Berhasil Dimuat</h4> <p style='font-size:14px; color:#155724;'> <strong>📊 Ringkasan Data:</strong><br> • Jumlah wilayah: <b>", nrow(df), "</b><br> • Periode waktu: <b>", min(year_cols), " - ", max(year_cols), "</b><br> • Total tahun: <b>", length(year_cols), " tahun</b> </p> </div>" )) }) # ============================================================================= # TAB TREN: KONTROL PEMILIHAN WILAYAH # ============================================================================= output$tren_wilayah_ui <- renderUI({ df <- tren_df() # Jika data belum ada if (is.null(df)) { return(tags$div( style = "padding:15px; background:#fff3cd; border-radius:5px;", tags$p( style = "color:#856404; margin:0;", icon("exclamation-triangle"), " Data tren belum dimuat. Silakan muat file terlebih dahulu." ) )) } # Ambil daftar wilayah dan urutkan wilayah_list <- sort(unique(df$Wilayah)) # Pilih 3 wilayah pertama sebagai default (atau semua jika < 3) default_selected <- wilayah_list[1:min(3, length(wilayah_list))] selectInput( "tren_wilayah", "Pilih Wilayah (bisa pilih beberapa):", choices = wilayah_list, selected = default_selected, multiple = TRUE ) }) # ============================================================================= # TAB TREN: GRAFIK TREN WAKTU # ============================================================================= output$tren_plot <- renderPlot({ df <- tren_df() req(df, input$tren_wilayah) # Transform data dari wide ke long format plot_data <- df %>% filter(Wilayah %in% input$tren_wilayah) %>% pivot_longer( cols = -Wilayah, # ✅ Ini sudah benar names_to = "Tahun", values_to = "Jumlah_Kasus" ) %>% mutate(Tahun = as.numeric(Tahun)) # Tambahkan rata-rata provinsi jika dicentang if(input$tren_show_avg) { avg_data <- df %>% dplyr::select(-Wilayah) %>% # ✅ PERBAIKAN: Tambah dplyr:: di depan summarise(across(everything(), ~mean(., na.rm = TRUE))) %>% mutate(Wilayah = "RATA-RATA NTT") %>% pivot_longer( cols = -Wilayah, names_to = "Tahun", values_to = "Jumlah_Kasus" ) %>% mutate(Tahun = as.numeric(Tahun)) plot_data <- bind_rows(plot_data, avg_data) } # Buat plot dasar p <- ggplot(plot_data, aes(x = Tahun, y = Jumlah_Kasus, color = Wilayah, group = Wilayah)) + geom_line(size = 1.2) + geom_point(size = 3) + scale_x_continuous(breaks = unique(plot_data$Tahun)) + scale_y_continuous(labels = scales::comma) + labs( title = "Tren Kasus Malaria Antar Tahun di NTT", subtitle = paste0("Periode: ", min(plot_data$Tahun), " - ", max(plot_data$Tahun)), x = "Tahun", y = "Jumlah Kasus Malaria", color = "Wilayah" ) + theme_minimal(base_size = 13) + theme( plot.title = element_text(face = "bold", size = 16, hjust = 0.5), plot.subtitle = element_text(size = 12, hjust = 0.5, color = "gray40"), legend.position = "right", legend.title = element_text(face = "bold"), panel.grid.minor = element_blank() ) # Tambahkan garis tren (regresi linear) jika dicentang if(input$tren_show_trend) { p <- p + geom_smooth( method = "lm", se = FALSE, linetype = "dashed", alpha = 0.5, size = 0.8 ) } p }, res = 96) # ============================================================================= # TAB TREN: TABEL DATA DENGAN STATISTIK # ============================================================================= output$tren_table <- renderDT({ df <- tren_df() # Jika data belum ada if (is.null(df)) { return(datatable( data.frame(Pesan = "⚠️ Data tren belum dimuat. Silakan muat file terlebih dahulu."), options = list(dom = 't'), rownames = FALSE )) } req(input$tren_wilayah) # Filter data berdasarkan wilayah yang dipilih table_data <- df %>% filter(Wilayah %in% input$tren_wilayah) %>% rowwise() %>% mutate( Rata_rata = round(mean(c_across(-Wilayah), na.rm = TRUE), 1), Total = sum(c_across(-Wilayah), na.rm = TRUE), Min = min(c_across(-Wilayah), na.rm = TRUE), Max = max(c_across(-Wilayah), na.rm = TRUE), Range = Max - Min ) %>% ungroup() %>% # Urutkan berdasarkan total kasus (tertinggi ke terendah) arrange(desc(Total)) # Tampilkan tabel dengan formatting datatable( table_data, options = list( pageLength = 10, scrollX = TRUE, dom = 'Bfrtip', buttons = c('copy', 'csv', 'excel') ), rownames = FALSE, class = 'cell-border stripe' ) %>% formatRound(columns = c('Rata_rata'), digits = 1) %>% formatStyle( 'Total', background = styleColorBar(range(table_data$Total), '#3498db'), backgroundSize = '100% 90%', backgroundRepeat = 'no-repeat', backgroundPosition = 'center' ) }) # ============================================================================= # TAB TREN: INTERPRETASI & ANALISIS TREN # ============================================================================= output$tren_interpretation <- renderUI({ df <- tren_df() req(df, input$tren_wilayah) # Analisis untuk setiap wilayah yang dipilih interpretations <- lapply(input$tren_wilayah, function(wil) { # Ambil data wilayah wil_data <- df %>% filter(Wilayah == wil) %>% dplyr::select(-Wilayah) %>% # ✅ PERBAIKAN: Tambah dplyr:: pivot_longer(everything(), names_to = "Tahun", values_to = "Kasus") %>% mutate(Tahun = as.numeric(Tahun)) # Minimal 2 data point untuk regresi if(nrow(wil_data) >= 2) { # Regresi linear untuk mendeteksi tren model <- lm(Kasus ~ Tahun, data = wil_data) slope <- coef(model)[2] # Klasifikasi tren berdasarkan slope trend_status <- if(slope > 50) { "<span style='color:#dc3545; font-weight:bold;'>📈 MENINGKAT SIGNIFIKAN</span>" } else if(slope > 10) { "<span style='color:#fd7e14; font-weight:bold;'>📈 Meningkat</span>" } else if(slope > -10) { "<span style='color:#6c757d; font-weight:bold;'>➡️ Stabil</span>" } else if(slope > -50) { "<span style='color:#17a2b8; font-weight:bold;'>📉 Menurun</span>" } else { "<span style='color:#28a745; font-weight:bold;'>📉 MENURUN SIGNIFIKAN</span>" } # Hitung perubahan persentase (tahun pertama vs terakhir) first_val <- wil_data$Kasus[1] last_val <- wil_data$Kasus[nrow(wil_data)] pct_change <- round(((last_val - first_val) / first_val) * 100, 1) # Hitung rata-rata kasus per tahun avg_kasus <- round(mean(wil_data$Kasus, na.rm = TRUE), 1) paste0( "<li style='margin-bottom:15px; padding:10px; background:#f8f9fa; border-left:4px solid ", ifelse(slope > 0, "#dc3545", "#28a745"), "; border-radius:4px;'>", "<b style='font-size:15px;'>", wil, ":</b><br>", "• Tren: ", trend_status, "<br>", "• Perubahan: ", ifelse(pct_change > 0, "+", ""), pct_change, "% ", "(dari ", scales::comma(first_val), " → ", scales::comma(last_val), " kasus)<br>", "• Rata-rata: ", scales::comma(avg_kasus), " kasus/tahun<br>", "• Kenaikan/penurunan: ~", round(abs(slope), 1), " kasus/tahun", "</li>" ) } else { paste0( "<li style='margin-bottom:10px;'><b>", wil, ":</b> ", "<span style='color:#dc3545;'>⚠️ Data tidak cukup untuk analisis tren</span></li>" ) } }) HTML(paste0( "<div style='background:#fff; padding:20px; border-radius:8px;'>", "<h4 style='color:#2c3e50; margin-top:0; border-bottom:2px solid #3498db; padding-bottom:10px;'>", "📊 Analisis Tren Otomatis</h4>", "<ul style='list-style:none; padding-left:0;'>", paste(interpretations, collapse = ""), "</ul>", "<div style='margin-top:20px; padding:15px; background:#e7f3ff; border-radius:5px;'>", "<p style='margin:0; font-size:13px; color:#004085;'>", "<b>ℹ️ Catatan:</b> Analisis tren menggunakan regresi linear sederhana. ", "Tren 'signifikan' ditentukan jika perubahan rata-rata >50 kasus/tahun.", "</p></div>", "</div>" )) }) # ============================================ # SECTION 1: MODEL AWAL # ============================================ output$tabel_model_linear <- renderDT({ model_comp_display <- model_comp %>% mutate( AIC = round(AIC, 2), LogLik = round(LogLik, 2), P_Value_Spasial = ifelse(is.na(P_Value_Spasial), "-", round(P_Value_Spasial, 4)), Status = ifelse(Model == "OLS", "✅ Terpilih", "") ) datatable(model_comp_display, options = list(pageLength = 5, scrollX = TRUE, dom = 't'), rownames = FALSE) }) output$kesimpulan_section1 <- renderUI({ best_model <- model_comp$Model[which.min(model_comp$AIC)] HTML(paste0( "<div style='background:#d4edda; padding:20px; border-radius:8px; border-left:6px solid #28a745;'>", "<h4 style='margin-top:0;'>✅ Kesimpulan Section 1</h4>", "<ul style='line-height:1.8;'>", "<li><b>Model Terpilih:</b> ", best_model, " (AIC terkecil = ", round(min(model_comp$AIC), 2), ")</li>", "<li><b>Keputusan:</b> <b>Efek spasial tidak signifikan sehingga model spasial linear tidak diperlukan.</b> ", "Analisis dilanjutkan menggunakan model OLS.</li>", "</ul>", "</div>" )) }) # ============================================ # SECTION 2: UJI ASUMSI (INTERAKTIF) # ============================================ output$output_uji_asumsi <- renderUI({ if(input$pilih_uji == "vif") { # Tampilkan VIF tagList( h4("📊 Hasil Uji Multikolinearitas (VIF)"), renderDT({ datatable(vif_df, options = list(pageLength = 10, scrollX = TRUE, dom = 't'), rownames = FALSE) }), HTML(paste0( "<div style='background:#e7f3ff; padding:15px; margin-top:15px; border-radius:5px;'>", "<p><b>📖 Interpretasi VIF:</b></p>", "<ul>", "<li>VIF < 5: ✅ Tidak ada multikolinearitas</li>", "<li>VIF 5-10: ⚠️ Multikolinearitas sedang</li>", "<li>VIF > 10: ❌ Multikolinearitas tinggi</li>", "</ul>", "<p><b>Hasil:</b> ", ifelse(max(vif_results) > 10, "Ada variabel dengan VIF tinggi", "Semua variabel aman dari multikolinearitas"), "</p></div>" )) ) } else { # Tampilkan Dispersi tagList( h4("📊 Hasil Uji Dispersi Data"), renderDT({ datatable(dispersion_df, options = list(pageLength = 10, scrollX = TRUE, dom = 't'), rownames = FALSE) }), HTML(paste0( "<div style='background:#e7f3ff; padding:15px; margin-top:15px; border-radius:5px;'>", "<p><b>📖 Interpretasi Dispersi:</b></p>", "<ul>", "<li>Ratio ≈ 1: Data sesuai asumsi Poisson</li>", "<li>Ratio > 1.5: <b>Overdispersion</b> (variance >> mean)</li>", "<li>Ratio < 0.5: <b>Underdispersion</b> (variance << mean)</li>", "</ul>", "<p><b>Hasil:</b> Dispersion Ratio = ", round(dispersion_ratio, 2), " → ", ifelse(dispersion_ratio > 1.5, "<b style='color:#dc3545;'>Overdispersion terdeteksi!</b>", "Data sesuai asumsi"), "</p></div>" )), # Visualisasi Distribusi dengan penjelasan kenapa model gagal h4("📈 Visualisasi Distribusi Data Kasus", style = "margin-top:20px;"), renderPlot({ ggplot(ntt_sf, aes(x = KasusMalaria)) + geom_histogram(bins = 10, fill = "#3498db", color = "white", alpha = 0.8) + geom_vline(aes(xintercept = mean(KasusMalaria)), color = "#e74c3c", linetype = "dashed", linewidth = 1) + geom_vline(aes(xintercept = mean(KasusMalaria) + sd(KasusMalaria)), color = "#f39c12", linetype = "dashed", linewidth = 0.8) + geom_vline(aes(xintercept = mean(KasusMalaria) - sd(KasusMalaria)), color = "#f39c12", linetype = "dashed", linewidth = 0.8) + annotate("text", x = mean(ntt_sf$KasusMalaria), y = Inf, label = paste0("Mean = ", round(mean(ntt_sf$KasusMalaria), 1)), vjust = 2, color = "#e74c3c", fontface = "bold") + annotate("rect", xmin = -Inf, xmax = Inf, ymin = -Inf, ymax = Inf, fill = NA, color = NA) + annotate("text", x = Inf, y = Inf, label = "OLS ❌ → count data\nPoisson ❌ → overdispersion\nNB ✅ → menangani overdispersion", hjust = 1.1, vjust = 1.5, size = 4, color = "#2c3e50", fontface = "bold", lineheight = 1.2) + theme_minimal() + labs(title = "Distribusi Kasus Malaria di NTT", subtitle = paste0("Mean = ", round(mean(ntt_sf$KasusMalaria), 2), " | Variance = ", round(var(ntt_sf$KasusMalaria), 2)), x = "Jumlah Kasus", y = "Frekuensi") + theme(plot.title = element_text(hjust = 0.5, face = "bold", size = 14), plot.subtitle = element_text(hjust = 0.5, size = 11)) }, height = 350) ) } }) # ============================================ # SECTION 3: KEPUTUSAN MODEL # ============================================ output$keputusan_model_visual <- renderUI({ HTML(paste0( "<div style='text-align:center; padding:30px;'>", "<div style='display:inline-block; max-width:800px; text-align:left;'>", # Diagram Alur Keputusan "<div style='background:#f8f9fa; padding:25px; border-radius:10px; margin-bottom:20px;'>", "<h3 style='text-align:center; color:#2c3e50; margin-top:0;'>🔍 Alur Keputusan Model</h3>", "<div style='display:flex; justify-content:space-between; align-items:center; margin:20px 0;'>", "<div style='flex:1; background:#3498db; color:white; padding:15px; border-radius:8px; text-align:center;'>", "<b>Model Linear<br>(OLS)</b>", "</div>", "<div style='flex:0; padding:0 15px; font-size:24px;'>→</div>", "<div style='flex:1; background:#e74c3c; color:white; padding:15px; border-radius:8px; text-align:center;'>", "<b>❌ Gagal<br>Overdispersion</b>", "</div>", "</div>", "<div style='display:flex; justify-content:space-between; align-items:center; margin:20px 0;'>", "<div style='flex:1; background:#3498db; color:white; padding:15px; border-radius:8px; text-align:center;'>", "<b>Model Poisson</b>", "</div>", "<div style='flex:0; padding:0 15px; font-size:24px;'>→</div>", "<div style='flex:1; background:#e74c3c; color:white; padding:15px; border-radius:8px; text-align:center;'>", "<b>❌ Gagal<br>Variance > Mean</b>", "</div>", "</div>", "<div style='display:flex; justify-content:space-between; align-items:center; margin:20px 0;'>", "<div style='flex:1; background:#27ae60; color:white; padding:15px; border-radius:8px; text-align:center;'>", "<b>Negative Binomial</b>", "</div>", "<div style='flex:0; padding:0 15px; font-size:24px;'>→</div>", "<div style='flex:1; background:#27ae60; color:white; padding:15px; border-radius:8px; text-align:center;'>", "<b>✅ COCOK!<br>Mengatasi Overdispersion</b>", "</div>", "</div>", "</div>", # Penjelasan "<div style='background:#d4edda; padding:20px; border-radius:8px; border-left:6px solid #28a745;'>", "<h4 style='margin-top:0; color:#155724;'>✅ Kesimpulan Keputusan</h4>", "<p style='font-size:15px; line-height:1.8;'>", "Berdasarkan hasil uji asumsi:", "<ul>", "<li>Model <b>Linear (OLS)</b> tidak cocok karena variabel dependen adalah <b>data cacah (count data)</b></li>", "<li>Model <b>Poisson</b> tidak cocok karena terdapat <b>overdispersion kuat</b> (variance >> mean)</li>", "<li>Model <b>Negative Binomial</b> dipilih karena dapat menangani overdispersion dengan parameter dispersi tambahan (theta)</li>", "</ul>", "<b>Pendekatan berbasis data cacah (count regression) dengan Negative Binomial adalah pilihan terbaik.</b>", "</p>", "</div>", "</div>", "</div>" )) }) # ============================================ # SECTION 4: MODEL AKHIR # ============================================ output$interpretasi_final <- renderUI({ req(irr_df) tryCatch({ sig_vars <- irr_df %>% filter(Signifikan == "✅ Ya") if(nrow(sig_vars) > 0) { interpretasi <- "<div style='background:#d4edda; padding:15px; border-radius:8px;'> <h5 style='margin-top:0; color:#155724;'>✅ Variabel yang Berpengaruh Signifikan:</h5> <p style='font-size:13px; margin-bottom:10px;'><i>Catatan: Model menggunakan offset log(Jumlah Penduduk), sehingga IRR menggambarkan perubahan <b>tingkat kejadian</b> (rate), bukan jumlah kasus absolut.</i></p> <ul>" for(i in 1:nrow(sig_vars)) { var <- sig_vars$Variabel[i] irr <- sig_vars$IRR[i] persen <- abs((irr - 1) * 100) is_percentage <- grepl("Layak|Persentase|Persen|%", var, ignore.case = TRUE) unit_text <- ifelse(is_percentage, "peningkatan 1%", "kenaikan 1 unit") interpretasi <- paste0( interpretasi, "<li style='margin-bottom:10px;'><b>", var, ":</b><br>", "IRR = ", round(irr, 3), " (95% CI: ", round(sig_vars$IRR_Lower[i], 3), " - ", round(sig_vars$IRR_Upper[i], 3), ")<br>", "<i>→ Setiap ", unit_text, " ", var, " <b>", ifelse(irr > 1, "meningkatkan", "menurunkan"), "</b> tingkat kejadian malaria sebesar <b>", round(persen, 1), "%</b></i></li>" ) } interpretasi <- paste0(interpretasi, "</ul></div>") } else { interpretasi <- "<div style='background:#f8d7da; padding:15px; border-radius:8px;'> <p style='color:#721c24;'>❌ Tidak ada variabel yang signifikan pada level α = 0.05</p></div>" } HTML(interpretasi) }, error = function(e) { HTML(paste0( "<div style='background:#f8d7da; padding:15px; border-radius:8px;'>", "<p style='color:#721c24;'>⚠️ Error: ", e$message, "</p>", "</div>" )) }) }) # ============================================ # SECTION 3.5: MODEL MATEMATIS NB # ============================================ # ============================================ # SECTION 3.5: MODEL MATEMATIS NB (DENGAN KOEFISIEN REAL) # ============================================ output$model_matematis_nb <- renderUI({ req(nb_model) tryCatch({ # Ambil koefisien dari model coef_model <- summary(nb_model)$coefficients # Validasi bahwa koefisien ada if(is.null(coef_model) || nrow(coef_model) == 0) { return(h4("⚠️ Koefisien model tidak tersedia")) } beta0 <- coef_model["(Intercept)", "Estimate"] beta1 <- coef_model["SanitasiLayak", "Estimate"] beta2 <- coef_model["RumahLayak", "Estimate"] beta3 <- coef_model["JumlahPuskesmas", "Estimate"] beta4 <- coef_model["KepadatanPenduduk", "Estimate"] # Hitung IRR untuk setiap koefisien irr1 <- exp(beta1) irr2 <- exp(beta2) irr3 <- exp(beta3) irr4 <- exp(beta4) HTML(paste0( "<div style='background:#f8f9fa; padding:25px; border-radius:10px;'>", "<h4 style='color:#2c3e50; margin-top:0;'>📊 Model Matematis Negative Binomial (Dengan Offset)</h4>", # Model dengan Offset "<div style='background:white; padding:20px; margin:15px 0; border-radius:8px; border:2px solid #27ae60;'>", "<h5 style='color:#27ae60; margin-top:0;'>Model Estimasi dengan Offset</h5>", "<p style='font-size:15px; text-align:center; font-family:monospace; background:#fff3cd; padding:20px; border-radius:5px; line-height:2; border:2px dashed #ffc107;'>", "<b>log(Kasus Malaria<sub>i</sub>) = log(Jumlah Penduduk<sub>i</sub>) +</b><br>", "<b>", round(beta0, 4), " ", ifelse(beta1 >= 0, "+", ""), " ", round(beta1, 4), "(Sanitasi Layak) ", ifelse(beta2 >= 0, "+", ""), " ", round(beta2, 4), "(Rumah Layak) ", ifelse(beta3 >= 0, "+", ""), " ", round(beta3, 4), "(Jumlah Puskesmas) ", ifelse(beta4 >= 0, "+", ""), " ", round(beta4, 4), "(Kepadatan Penduduk)</b>", "</p>", "<div style='background:#e7f3ff; padding:15px; margin-top:15px; border-radius:5px;'>", "<p><b>📖 Interpretasi Offset:</b></p>", "<ul>", "<li>Offset <code>log(Jumlah Penduduk)</code> digunakan untuk mengontrol perbedaan ukuran populasi antar wilayah</li>", "<li>Model memprediksi <b>rate/tingkat kejadian</b>, bukan jumlah kasus absolut</li>", "<li>Ini membuat perbandingan antar wilayah lebih adil dan bermakna</li>", "</ul>", "</div>", "</div>", # Tabel Koefisien "<div style='background:white; padding:20px; margin:15px 0; border-radius:8px; border:2px solid #3498db;'>", "<h5 style='color:#3498db; margin-top:0;'>Tabel Koefisien Model</h5>", "<table style='width:100%; border-collapse:collapse;'>", "<tr style='background:#3498db; color:white;'>", "<th style='padding:12px; text-align:left; border:1px solid #ddd;'>Variabel</th>", "<th style='padding:12px; text-align:center; border:1px solid #ddd;'>β (Koefisien)</th>", "<th style='padding:12px; text-align:center; border:1px solid #ddd;'>Std. Error</th>", "<th style='padding:12px; text-align:center; border:1px solid #ddd;'>P-value</th>", "<th style='padding:12px; text-align:center; border:1px solid #ddd;'>Signifikan?</th>", "</tr>", "<tr style='background:#f8f9fa;'>", "<td style='padding:10px; border:1px solid #ddd;'><b>Intercept (β₀)</b></td>", "<td style='padding:10px; text-align:center; border:1px solid #ddd; font-family:monospace;'><b>", round(beta0, 4), "</b></td>", "<td style='padding:10px; text-align:center; border:1px solid #ddd;'>", round(coef_model["(Intercept)", "Std. Error"], 4), "</td>", "<td style='padding:10px; text-align:center; border:1px solid #ddd;'>", round(coef_model["(Intercept)", "Pr(>|z|)"], 4), "</td>", "<td style='padding:10px; text-align:center; border:1px solid #ddd;'>", ifelse(coef_model["(Intercept)", "Pr(>|z|)"] < 0.05, "✅", "❌"), "</td>", "</tr>", "<tr>", "<td style='padding:10px; border:1px solid #ddd;'><b>Sanitasi Layak (β₁)</b></td>", "<td style='padding:10px; text-align:center; border:1px solid #ddd; font-family:monospace;'><b>", round(beta1, 4), "</b></td>", "<td style='padding:10px; text-align:center; border:1px solid #ddd;'>", round(coef_model["SanitasiLayak", "Std. Error"], 4), "</td>", "<td style='padding:10px; text-align:center; border:1px solid #ddd;'>", round(coef_model["SanitasiLayak", "Pr(>|z|)"], 4), "</td>", "<td style='padding:10px; text-align:center; border:1px solid #ddd;'>", ifelse(coef_model["SanitasiLayak", "Pr(>|z|)"] < 0.05, "✅", "❌"), "</td>", "</tr>", "<tr style='background:#f8f9fa;'>", "<td style='padding:10px; border:1px solid #ddd;'><b>Rumah Layak (β₂)</b></td>", "<td style='padding:10px; text-align:center; border:1px solid #ddd; font-family:monospace;'><b>", round(beta2, 4), "</b></td>", "<td style='padding:10px; text-align:center; border:1px solid #ddd;'>", round(coef_model["RumahLayak", "Std. Error"], 4), "</td>", "<td style='padding:10px; text-align:center; border:1px solid #ddd;'>", round(coef_model["RumahLayak", "Pr(>|z|)"], 4), "</td>", "<td style='padding:10px; text-align:center; border:1px solid #ddd;'>", ifelse(coef_model["RumahLayak", "Pr(>|z|)"] < 0.05, "✅", "❌"), "</td>", "</tr>", "<tr>", "<td style='padding:10px; border:1px solid #ddd;'><b>Jumlah Puskesmas (β₃)</b></td>", "<td style='padding:10px; text-align:center; border:1px solid #ddd; font-family:monospace;'><b>", round(beta3, 4), "</b></td>", "<td style='padding:10px; text-align:center; border:1px solid #ddd;'>", round(coef_model["JumlahPuskesmas", "Std. Error"], 4), "</td>", "<td style='padding:10px; text-align:center; border:1px solid #ddd;'>", round(coef_model["JumlahPuskesmas", "Pr(>|z|)"], 4), "</td>", "<td style='padding:10px; text-align:center; border:1px solid #ddd;'>", ifelse(coef_model["JumlahPuskesmas", "Pr(>|z|)"] < 0.05, "✅", "❌"), "</td>", "</tr>", "<tr style='background:#f8f9fa;'>", "<td style='padding:10px; border:1px solid #ddd;'><b>Kepadatan Penduduk (β₄)</b></td>", "<td style='padding:10px; text-align:center; border:1px solid #ddd; font-family:monospace;'><b>", round(beta4, 4), "</b></td>", "<td style='padding:10px; text-align:center; border:1px solid #ddd;'>", round(coef_model["KepadatanPenduduk", "Std. Error"], 4), "</td>", "<td style='padding:10px; text-align:center; border:1px solid #ddd;'>", round(coef_model["KepadatanPenduduk", "Pr(>|z|)"], 4), "</td>", "<td style='padding:10px; text-align:center; border:1px solid #ddd;'>", ifelse(coef_model["KepadatanPenduduk", "Pr(>|z|)"] < 0.05, "✅", "❌"), "</td>", "</tr>", "</table>", "</div>", # Parameter Dispersi "<div style='background:white; padding:20px; margin:15px 0; border-radius:8px; border:2px solid #e74c3c;'>", "<h5 style='color:#e74c3c; margin-top:0;'>Parameter Dispersi</h5>", "<p style='font-size:16px;'>", "<b>Theta (θ) = ", round(nb_model$theta, 4), "</b><br>", "Parameter ini menangani <b>overdispersion</b> pada data dengan:<br>", "• Dispersion ratio data = <b>", round(dispersion_ratio, 2), "</b><br>", "• Semakin kecil θ, semakin besar overdispersion yang ditangani model", "</p>", "</div>", "</div>" )) }, error = function(e) { HTML(paste0( "<div style='background:#f8d7da; padding:20px; border-radius:8px;'>", "<h4 style='color:#721c24;'>⚠️ Error: ", e$message, "</h4>", "</div>" )) }) }) # ============================================ # SECTION 4: OUTPUT IRR & EVALUASI # ============================================ # Tabel IRR # Tabel IRR output$tabel_irr <- renderDT({ req(irr_df) tryCatch({ irr_display <- irr_df %>% mutate( Koefisien = round(Koefisien, 4), SE = round(SE, 4), P_value = round(P_value, 4), IRR = round(IRR, 3), IRR_Lower = round(IRR_Lower, 3), IRR_Upper = round(IRR_Upper, 3), `CI 95%` = paste0("[", IRR_Lower, " - ", IRR_Upper, "]") ) %>% dplyr::select(Variabel, Koefisien, SE, P_value, IRR, `CI 95%`, Signifikan) # ✅ TAMBAH dplyr:: datatable( irr_display, options = list(pageLength = 10, scrollX = TRUE, dom = 't'), rownames = FALSE ) %>% formatStyle( 'Signifikan', target = 'row', backgroundColor = styleEqual(c('✅ Ya', '❌ Tidak'), c('#d4edda', '#f8d7da')) ) }, error = function(e) { datatable( data.frame(Error = paste("Tabel IRR error:", e$message)), options = list(dom = 't'), rownames = FALSE ) }) }) # Plot IRR (Forest Plot) output$plot_irr <- renderPlot({ req(irr_df) tryCatch({ ggplot(irr_df, aes(x = IRR, y = reorder(Variabel, IRR))) + geom_vline(xintercept = 1, linetype = "dashed", color = "red", linewidth = 1) + geom_point(aes(color = Signifikan), size = 4) + geom_errorbarh(aes(xmin = IRR_Lower, xmax = IRR_Upper, color = Signifikan), height = 0.2, linewidth = 1) + scale_color_manual(values = c("✅ Ya" = "#28a745", "❌ Tidak" = "#dc3545")) + labs( title = "Forest Plot - Incidence Rate Ratio (IRR)", subtitle = "IRR > 1: Meningkatkan risiko | IRR < 1: Menurunkan risiko", x = "IRR (95% CI)", y = "Variabel" ) + theme_minimal(base_size = 12) + theme( plot.title = element_text(face = "bold", hjust = 0.5, size = 14), plot.subtitle = element_text(hjust = 0.5, size = 11), legend.position = "bottom" ) }, error = function(e) { plot.new() text(0.5, 0.5, paste("Plot IRR error:", e$message), col = "red", cex = 1.2) }) }) # Tabel Model Fit (Evaluasi Model) output$tabel_model_fit <- renderDT({ req(model_fit) tryCatch({ datatable( model_fit, options = list(pageLength = 10, scrollX = TRUE, dom = 't'), rownames = FALSE ) %>% formatStyle( 'Metrik', fontWeight = 'bold' ) }, error = function(e) { datatable( data.frame(Error = paste("Evaluasi model error:", e$message)), options = list(dom = 't'), rownames = FALSE ) }) }) # ============================================================================= # TAB 8: WILAYAH PRIORITAS - SERVER # ============================================================================= # Plot Prioritas (Bar Chart) # Plot Prioritas (Bar Chart) output$plot_prioritas <- renderPlot({ req(ntt_prioritas) ggplot(ntt_prioritas, aes(x = reorder(Wilayah, indeks_prioritas), y = indeks_prioritas, fill = kategori_prioritas)) + geom_col(width = 0.75) + geom_text(aes(label = paste0("#", rank_prioritas, " (", round(indeks_prioritas, 2), ")")), hjust = -0.1, size = 3.5, fontface = "bold") + coord_flip() + scale_fill_manual( values = c( "Prioritas Tinggi" = "#d73027", "Prioritas Sedang" = "#fee08b", "Prioritas Rendah" = "#1a9850" ) ) + labs( title = "Ranking Wilayah Prioritas Penanganan Malaria", subtitle = "Berdasarkan Z-score: Prevalensi (50%), Sanitasi (30%), Puskesmas (10%), Rumah (5%), Kepadatan (5%)", x = "Kabupaten/Kota", y = "Indeks Prioritas (Z-score)", fill = "Kategori Prioritas" ) + theme_minimal(base_size = 12) + theme( plot.title = element_text(face = "bold", hjust = 0.5, size = 16), plot.subtitle = element_text(hjust = 0.5, size = 11), legend.position = "bottom", axis.text.y = element_text(size = 10) ) }) # Peta Prioritas output$peta_prioritas <- renderLeaflet({ req(ntt_prioritas_sf) # Color palette pal_prioritas <- colorFactor( palette = c("Prioritas Tinggi" = "#d73027", "Prioritas Sedang" = "#fee08b", "Prioritas Rendah" = "#1a9850"), domain = ntt_prioritas_sf$kategori_prioritas ) leaflet(ntt_prioritas_sf) %>% addProviderTiles(providers$CartoDB.Positron) %>% addPolygons( fillColor = ~pal_prioritas(kategori_prioritas), fillOpacity = 0.8, color = "white", weight = 2, opacity = 1, highlight = highlightOptions( weight = 3, color = "#666", fillOpacity = 0.9, bringToFront = TRUE ), label = ~paste0(shapeName, ": ", kategori_prioritas, " (Rank #", rank_prioritas, ")"), popup = ~paste0( "<b>", shapeName, "</b><br>", "Kategori: <b style='color:", ifelse(kategori_prioritas == "Prioritas Tinggi", "#d73027", ifelse(kategori_prioritas == "Prioritas Sedang", "#d4a017", "#1a9850")), ";'>", kategori_prioritas, "</b><br>", "Ranking: <b>#", rank_prioritas, "</b><br>", "Indeks Prioritas: <b>", round(indeks_prioritas, 3), "</b><br>", "<hr style='margin:5px 0;'>", "<b>Komponen Z-score:</b><br>", "• Prevalensi: ", round(z_prevalensi, 2), "<br>", "• Sanitasi: ", round(z_sanitasi, 2), "<br>", "• Rumah: ", round(z_rumah, 2), "<br>", "• Puskesmas: ", round(z_puskesmas, 2), "<br>", "• Kepadatan: ", round(z_kepadatan, 2), "<br>", "<hr style='margin:5px 0;'>", "<b>Data Aktual:</b><br>", "Prevalensi: ", round(Prevalensi, 2), "%<br>", "Sanitasi Layak: ", round(SanitasiLayak, 1), "%<br>", "Rumah Layak: ", round(RumahLayak, 1), "%<br>", "Jumlah Puskesmas: ", JumlahPuskesmas, "<br>", "Kepadatan: ", round(KepadatanPenduduk, 2), " jiwa/km²" ), labelOptions = labelOptions( style = list("font-weight" = "bold", padding = "3px 8px"), textsize = "13px" ) ) %>% addLegend( position = "bottomright", pal = pal_prioritas, values = ~kategori_prioritas, title = "Kategori Prioritas", opacity = 0.8 ) %>% setView(lng = 120.5, lat = -9.5, zoom = 8) }) # Tabel Prioritas # Tabel Prioritas output$tabel_prioritas <- renderDT({ req(ntt_prioritas) tabel <- ntt_prioritas %>% dplyr::select( # ✅ TAMBAH dplyr:: di sini! Ranking = rank_prioritas, Wilayah, `Indeks Prioritas` = indeks_prioritas, Kategori = kategori_prioritas ) %>% mutate( `Indeks Prioritas` = round(`Indeks Prioritas`, 3) ) datatable( tabel, options = list( pageLength = 22, scrollX = TRUE, order = list(list(0, 'asc')) ), rownames = FALSE, caption = htmltools::tags$caption( style = 'caption-side: top; text-align: left; color: #2c3e50; font-size: 14px; padding: 10px;', htmltools::em('Prioritas ditentukan berdasarkan Z-score: Prevalensi (50%), Sanitasi (30%), Puskesmas (10%), Rumah (5%), Kepadatan (5%)') ) ) %>% formatStyle( 'Kategori', backgroundColor = styleEqual( c('Prioritas Tinggi', 'Prioritas Sedang', 'Prioritas Rendah'), c('#ffcccc', '#fff9cc', '#ccffcc') ), fontWeight = 'bold', textAlign = 'center' ) %>% formatStyle( 'Indeks Prioritas', background = styleColorBar(range(tabel$`Indeks Prioritas`), '#3498db'), backgroundSize = '100% 90%', backgroundRepeat = 'no-repeat', backgroundPosition = 'center' ) %>% formatStyle( 'Ranking', fontWeight = 'bold', textAlign = 'center', color = styleInterval( c(5, 10, 15), c('#d73027', '#fc8d59', '#fee08b', '#1a9850') ) ) }) # Rekomendasi Prioritas output$rekomendasi_prioritas <- renderUI({ req(ntt_prioritas) # Wilayah prioritas tinggi tinggi <- ntt_prioritas %>% filter(kategori_prioritas == "Prioritas Tinggi") %>% arrange(rank_prioritas) %>% pull(Wilayah) # Wilayah prioritas sedang sedang <- ntt_prioritas %>% filter(kategori_prioritas == "Prioritas Sedang") %>% arrange(rank_prioritas) %>% pull(Wilayah) HTML(paste0( "<div style='background:#fff3cd; padding:20px; border-radius:8px; border-left:6px solid #ffc107;'>", "<h4 style='color:#856404; margin-top:0;'>🎯 Rekomendasi Strategis</h4>", # Prioritas Tinggi "<div style='background:#ffe6e6; padding:15px; margin:10px 0; border-radius:5px; border-left:4px solid #d73027;'>", "<h5 style='color:#d73027; margin-top:0;'>🔴 PRIORITAS TINGGI (", length(tinggi), " Wilayah)</h5>", "<p><b>Wilayah:</b> ", paste(tinggi, collapse = ", "), "</p>", "<p><b>Strategi Intervensi:</b></p>", "<ul>", "<li><b>Mobilisasi Sumber Daya Intensif:</b> Alokasi anggaran dan SDM kesehatan prioritas</li>", "<li><b>Program Pengendalian Vektor Masif:</b> Distribusi kelambu, IRS (Indoor Residual Spraying)</li>", "<li><b>Peningkatan Surveilans Aktif:</b> Active case detection dan mass blood survey</li>", "<li><b>Perbaikan Infrastruktur Sanitasi:</b> Program STBM (Sanitasi Total Berbasis Masyarakat)</li>", "<li><b>Edukasi Masyarakat Intensif:</b> Kampanye pencegahan dan deteksi dini</li>", "</ul>", "</div>", # Prioritas Sedang "<div style='background:#fffacd; padding:15px; margin:10px 0; border-radius:5px; border-left:4px solid #d4a017;'>", "<h5 style='color:#856404; margin-top:0;'>🟡 PRIORITAS SEDANG (", length(sedang), " Wilayah)</h5>", "<p><b>Wilayah:</b> ", paste(sedang, collapse = ", "), "</p>", "<p><b>Strategi Intervensi:</b></p>", "<ul>", "<li><b>Penguatan Sistem Kesehatan:</b> Peningkatan kapasitas puskesmas</li>", "<li><b>Surveilans Rutin:</b> Passive case detection dan monitoring berkala</li>", "<li><b>Program Preventif:</b> Edukasi dan distribusi kelambu terbatas</li>", "</ul>", "</div>", # Timeline "<div style='background:#e7f3ff; padding:15px; margin:10px 0; border-radius:5px;'>", "<h5 style='color:#004085; margin-top:0;'>📅 Timeline Implementasi</h5>", "<ul>", "<li><b>Fase 1 (0-3 bulan):</b> Intervensi darurat di wilayah prioritas tinggi</li>", "<li><b>Fase 2 (3-12 bulan):</b> Ekspansi ke wilayah prioritas sedang</li>", "<li><b>Fase 3 (12-24 bulan):</b> Monitoring dan evaluasi berkelanjutan</li>", "</ul>", "</div>", "</div>" )) }) } shinyApp(ui = ui, server = server) \]