Disusun oleh:
Tansya Putri Rizkya Zakaria (140610230021)
Demam Berdarah Dengue (DBD) merupakan salah satu penyakit menular yang masih menjadi masalah kesehatan masyarakat di berbagai negara tropis, termasuk Indonesia. Penyakit ini disebabkan oleh virus dengue yang ditularkan melalui gigitan nyamuk Aedes aegypti. Kejadian DBD sendiri dipengaruhi oleh interaksi antara faktor penyebab penyakit (agent), kondisi individu atau host seperti daya tahan tubuh dan kebiasaan masyarakat, serta faktor lingkungan (environment) yang mendukung perkembangbiakan nyamuk, misalnya suhu, kelembaban, dan kondisi sanitasi.
Provinsi Jawa Barat menjadi salah satu wilayah dengan jumlah kasus DBD tertinggi di Indonesia. Berdasarkan data yang berasal dari Badan Pusat Statistik (BPS) Provinsi Jawa Barat, tercatat sekitar 61.423 kasus DBD sepanjang tahun 2024 dengan 339 kasus kematian. Dari total tersebut, Kota Bandung menjadi daerah dengan kasus terbanyak, yaitu 7680 kasus . Angka ini menunjukkan bahwa penularan DBD di Jawa Barat masih cukup tinggi dan menjadi tantangan besar bagi pemerintah daerah maupun masyarakat.
Tingginya kasus DBD ini menandakan bahwa upaya Pemberantasan Sarang Nyamuk (PSN) dan perilaku hidup bersih di masyarakat belum berjalan optimal. Selain itu, faktor perubahan iklim, meningkatnya curah hujan yang menyebabkan genangan air, serta kepadatan penduduk di wilayah perkotaan juga turut memperbesar risiko penularan. Lingkungan yang padat dan sanitasi yang kurang baik menciptakan kondisi ideal bagi nyamuk Aedes aegypti untuk berkembang biak.
Melihat kondisi tersebut, penting untuk melakukan analisis epidemiologi guna mengetahui pola penyebaran kasus DBD, faktor-faktor yang berhubungan dengan peningkatan jumlah kasus, serta wilayah mana yang paling rentan terdampak. Analisis ini diharapkan dapat menjadi dasar dalam menyusun strategi pencegahan dan pengendalian DBD yang lebih efektif, terarah, dan sesuai dengan kondisi di lapangan.
Bagaimana proses terjadinya penyakit Demam Berdarah Dengue (DBD) ditinjau berdasarkan model agent–host–environment di Provinsi Jawa Barat?
Bagaimana ukuran frekuensi penyakit DBD di kabupaten/kota di Provinsi Jawa Barat?
Bagaimana pola distribusi kasus DBD dalam ruang dan waktu di Provinsi Jawa Barat?
Desain studi epidemiologi apa yang paling sesuai untuk menganalisis faktor risiko DBD di Provinsi Jawa Barat?
Wilayah mana yang perlu menjadi prioritas utama dalam upaya pencegahan dan pengendalian DBD di Provinsi Jawa Barat?
Menjelaskan proses terjadinya DBD dengan pendekatan model agent–host–environment.
Menghitung dan menginterpretasikan ukuran frekuensi penyakit DBD di kabupaten/kota di Provinsi Jawa Barat.
Mendeskripsikan dan memvisualisasikan distribusi kasus DBD dalam ruang dan waktu menggunakan data yang tersedia.
Mengidentifikasi desain studi epidemiologi yang paling relevan untuk analisis faktor risiko DBD.
Menentukan wilayah prioritas dalam upaya pencegahan dan pengendalian DBD berdasarkan hasil analisis epidemiologi.
Epidemiologi merupakan salah satu cabang ilmu kesehatan masyarakat yang mempelajari tentang pola, penyebab, dan dampak penyakit dalam suatu populasi. Ilmu ini tidak hanya melihat penyakit dari sisi individu, tetapi juga dari seberapa sering penyakit muncul (frekuensi), bagaimana pola penyebarannya berdasarkan waktu, tempat, dan karakteristik orang (distribusi), serta faktor-faktor yang memengaruhinya (determinan).
Secara etimologis, istilah epidemiologi berasal dari bahasa Yunani, yaitu epi (pada), demos (rakyat/penduduk), dan logos (ilmu). Jadi, secara sederhana epidemiologi dapat diartikan sebagai “ilmu yang mempelajari penyakit pada populasi”. Dalam praktiknya, epidemiologi berperan penting untuk mengidentifikasi penyebab penyakit, memahami mekanisme penularan, serta merancang upaya pencegahan dan pengendalian agar masalah kesehatan dapat ditekan semaksimal mungkin.
Model segitiga epidemiologi menjelaskan bahwa munculnya penyakit terjadi karena interaksi antara tiga komponen utama: agent, host, dan environment. Agent adalah penyebab penyakit, bisa berupa virus, bakteri, parasit, atau zat kimia yang menimbulkan gangguan kesehatan. Kemampuan agent untuk menular, bertahan hidup, dan memicu reaksi pada tubuh manusia menentukan seberapa besar potensi penularannya. Host adalah individu yang bisa terinfeksi, dengan tingkat kerentanan yang dipengaruhi oleh faktor seperti usia, status gizi, daya tahan tubuh, genetik, dan perilaku. Sementara itu, environment atau lingkungan mencakup faktor fisik (iklim, suhu, kelembapan), biologis (keberadaan vektor), dan sosial (kepadatan penduduk, kebersihan, perilaku masyarakat) yang dapat mendukung atau menghambat penularan penyakit.
Ketiga komponen ini saling berinteraksi dan menentukan apakah suatu penyakit bisa muncul atau tidak. Penyakit akan terjadi jika agent yang patogen bertemu dengan host yang rentan dalam lingkungan yang kondusif. Sebaliknya, jika salah satu faktor tidak mendukung, misalnya host memiliki kekebalan kuat atau lingkungan tidak sesuai untuk agent penyakit bisa dicegah.
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.
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.
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.
Desain studi epidemiologi digunakan untuk meneliti hubungan antara paparan (exposure) dan kejadian penyakit (outcome) dalam suatu populasi. Secara umum, desain ini dibagi menjadi dua kelompok besar, yaitu studi observasional, di mana peneliti tidak melakukan intervensi terhadap subjek, dan studi eksperimental, di mana peneliti secara langsung memberikan perlakuan atau intervensi untuk melihat pengaruhnya terhadap outcome.
1. Cross-sectional study atau studi potong lintang
Desain ini mengukur paparan dan penyakit pada waktu yang sama, sehingga memberikan gambaran “snapshot” tentang kondisi populasi pada saat tertentu. Studi ini biasanya digunakan untuk memperkirakan prevalensi penyakit atau faktor risiko, serta untuk mengeksplorasi hubungan awal antara variabel. Kelebihan studi ini adalah prosesnya cepat dan biayanya relatif murah, cocok untuk survei kesehatan masyarakat. Namun, karena tidak dapat menentukan urutan waktu antara paparan dan penyakit, studi ini tidak bisa memastikan hubungan sebab-akibat dan kurang tepat digunakan untuk penyakit langka.
2. Case-control study atau studi kasus-kontrol
Desain yang membandingkan individu yang memiliki penyakit (kasus) dengan individu yang tidak memiliki penyakit (kontrol), kemudian menelusuri apakah mereka pernah terpapar faktor risiko di masa lalu. Studi ini bersifat retrospektif karena dimulai dari outcome menuju exposure. Ukuran asosiasi yang digunakan adalah Odds Ratio (OR). Keunggulan desain ini adalah efisien untuk penyakit yang jarang terjadi dan tidak membutuhkan waktu lama, namun memiliki kelemahan berupa potensi bias recall dan bias seleksi karena bergantung pada ingatan atau catatan masa lalu.
Desain ini menjelaskan di mana sekelompok individu yang terpapar dan tidak terpapar diikuti dalam jangka waktu tertentu untuk melihat apakah mereka mengalami penyakit atau tidak. Studi ini bisa dilakukan secara prospektif (ke depan) maupun retrospektif (menggunakan data historis). Ukuran asosiasi yang umum digunakan adalah Relative Risk (RR). Keunggulan studi kohort adalah dapat menentukan hubungan sebab-akibat dan mengukur beberapa outcome sekaligus. Namun, kekurangannya adalah membutuhkan waktu lama, biaya besar, dan risiko kehilangan partisipan selama periode pengamatan.
4. Randomized Controlled Trial (RCT)
RCT termasuk dalam kategori studi eksperimental di mana peserta
dibagi secara acak menjadi kelompok intervensi dan kelompok kontrol.
Tujuannya untuk menilai efektivitas suatu intervensi atau terapi dengan
meminimalkan bias.
RCT dianggap desain dengan tingkat bukti paling kuat, meski dalam
praktiknya membutuhkan waktu, biaya, dan pengawasan etika yang
ketat.
Penelitian ini menggunakan data sekunder yang diperoleh dari berbagai sumber resmi pemerintah, terutama dari Portal Open Data Provinsi Jawa Barat dan Badan Pusat Statistik (BPS). Data yang digunakan mencakup beberapa variabel yang relevan dengan kasus Demam Berdarah Dengue (DBD) di Provinsi Jawa Barat pada tahun 2024.
library(tidyverse) # termasuk readr, dplyr, ggplot2, dll
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.1.4 ✔ readr 2.1.5
## ✔ forcats 1.0.0 ✔ stringr 1.5.1
## ✔ ggplot2 3.5.2 ✔ tibble 3.2.1
## ✔ lubridate 1.9.4 ✔ tidyr 1.3.1
## ✔ purrr 1.0.2
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
data_dbd <- read_csv("/Users/tansyazakaria/Downloads/Data - Sheet1 (1) copy.csv")
## Rows: 27 Columns: 7
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (1): KABUPATEN/KOTA (nama wilayah)
## dbl (5): Jumlah DBD, Jumlah Penduduk, Jumlah Kejadian Banjir, % Rumah Layak ...
## num (1): % Akses Sanitasi
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
data_dbd
## # A tibble: 27 × 7
## KABUPATEN/KOTA (nama …¹ `Jumlah DBD` `Jumlah Penduduk` Jumlah Kejadian Banj…²
## <chr> <dbl> <dbl> <dbl>
## 1 Kab. Bogor 3404 5682300 29
## 2 Kab. Sukabumi 841 2828020 24
## 3 Kab. Cianjur 1932 2584990 8
## 4 Kab. Bandung 3589 3753120 21
## 5 Garut 3269 2716950 7
## 6 Tasikmalaya 763 1920920 3
## 7 Ciamis 1420 1259230 11
## 8 Kuningan 2165 1213930 3
## 9 Kab. Cirebon 1833 2387960 18
## 10 Majalengka 1275 1352540 5
## # ℹ 17 more rows
## # ℹ abbreviated names: ¹`KABUPATEN/KOTA (nama wilayah)`,
## # ²`Jumlah Kejadian Banjir`
## # ℹ 3 more variables: `% Akses Sanitasi` <dbl>, `% Rumah Layak Huni` <dbl>,
## # `Kasus Meninggal` <dbl>
Penelitian ini menggunakan dua jenis variabel terkait kejadian DBD di Provinsi Jawa Barat. Variabel dependen yang dianalisis adalah jumlah kasus DBD dan jumlah kematian akibat DBD per kabupaten/kota sebagai indikator tingkat kejadian dan keparahan penyakit. Sementara itu, variabel independen terdiri atas beberapa faktor lingkungan dan sosial yang diduga berperan terhadap penyebaran DBD. Variabel tersebut meliputi jumlah penduduk, jumlah kejadian banjir, persentase akses sanitasi layak, dan persentase rumah layak huni. Jumlah penduduk digunakan sebagai dasar perhitungan prevalensi serta mencerminkan tingkat kepadatan populasi yang dapat meningkatkan risiko penularan penyakit berbasis vektor. Jumlah kejadian banjir digunakan sebagai indikator adanya genangan air yang berpotensi menjadi tempat berkembang biaknya nyamuk Aedes aegypti. Persentase akses sanitasi layak menggambarkan kondisi kebersihan lingkungan masyarakat, sedangkan persentase rumah layak huni mencerminkan kondisi fisik tempat tinggal yang dapat berpengaruh terhadap risiko penularan penyakit.
Penelitian ini menerapkan analisis deskriptif epidemiologi untuk menggambarkan pola penyebaran dan faktor yang berhubungan dengan kejadian DBD di Provinsi Jawa Barat berdasarkan aspek waktu, tempat, dan populasi. Ukuran frekuensi yang digunakan meliputi prevalensi dan case fatality rate (CFR) guna menilai tingkat kejadian serta keparahan penyakit di setiap wilayah. Selain itu, dilihat juga korelasi antar variabel untuk melihat seberapa kuat hubungan antar variabel. Pendekatan ini dilengkapi dengan analisis statistik deskriptif serta visualisasi grafis dan spasial untuk memperjelas distribusi data antar kabupaten/kota.
Penelitian ini diawali dengan pengumpulan data sekunder dari Open Data Jabar dan BPS, mencakup jumlah kasus dan kematian akibat DBD, jumlah penduduk, serta variabel lingkungan seperti kejadian banjir, sanitasi, dan kondisi rumah di setiap kabupaten/kota di Jawa Barat. Data kemudian diolah melalui proses pembersihan, penyelarasan nama wilayah, dan pembuatan variabel turunan agar siap dianalisis. Analisis statistika deskriptif dilakukan untuk mengetahui karakteristik dan penyebaran kasus DBD secara umum. Selanjutnya dihitung ukuran frekuensi, yaitu prevalensi dan case fatality rate (CFR), guna menggambarkan tingkat dan keparahan penyakit di setiap wilayah. Tahap akhir berupa visualisasi hasil melalui grafik dan peta distribusi guna melihat pola spasial serta menginterpretasikan wilayah prioritas dan faktor risiko yang terlibat dalam penyebaran DBD di Jawa Barat.
Demam Berdarah Dengue (DBD) merupakan penyakit infeksi yang disebabkan oleh virus dengue dan ditularkan melalui gigitan nyamuk Aedes aegypti. Penularan penyakit ini dapat dijelaskan menggunakan model agent–host–environment, di mana ketiga faktor tersebut saling berinteraksi dan berperan dalam terjadinya penyakit.
Agent (Agen penyebab) Agen penyebab DBD adalah virus dengue yang terdiri dari empat serotipe (DEN-1 hingga DEN-4) dari famili Flaviviridae. Virus ini ditularkan melalui gigitan nyamuk Aedes aegypti, yang berkembang biak di air bersih dan genangan yang tidak tertutup.
Host (Pejamu) Host atau pejamu utama penyakit ini adalah manusia. Risiko tertinggi terdapat pada anak-anak dan masyarakat yang tinggal di wilayah padat penduduk. Faktor seperti kekebalan tubuh yang rendah, kebiasaan menyimpan air terbuka, dan kurangnya kesadaran menjaga kebersihan lingkungan turut meningkatkan risiko tertular.
Environment (Lingkungan) Faktor lingkungan berperan penting dalam penyebaran DBD. Lingkungan dengan sanitasi buruk, curah hujan tinggi, genangan air, dan kepadatan penduduk yang tinggi menjadi tempat ideal bagi perkembangbiakan nyamuk. Kurangnya kegiatan pencegahan, seperti Pemberantasan Sarang Nyamuk (PSN), juga memperparah penyebaran penyakit.
Ketiga faktor tersebut saling berhubungan dalam siklus penularan DBD. Upaya pengendalian penyakit perlu dilakukan secara terpadu melalui perbaikan kondisi lingkungan, pengendalian vektor nyamuk, serta peningkatan kesadaran masyarakat terhadap kebersihan dan pencegahan penyakit.
Diagram di atas menjelaskan bagaimana berbagai faktor saling berperan dalam meningkatkan kasus Demam Berdarah Dengue (DBD). Berdasarkan model agent–host–environment, penyebaran DBD terjadi karena interaksi antara virus dengue (agent), manusia (host), dan lingkungan (environment).
Faktor lingkungan seperti kepadatan penduduk, curah hujan tinggi, suhu panas, banjir, dan sanitasi buruk menciptakan kondisi ideal bagi nyamuk Aedes untuk berkembang biak. Genangan air akibat hujan atau banjir menjadi tempat bertelur, sementara suhu tinggi mempercepat siklus hidup nyamuk dan meningkatkan aktivitas menggigit.
Peningkatan populasi nyamuk kemudian memperbesar risiko penularan virus dengue ke manusia, terutama di wilayah padat dan dengan sanitasi yang tidak memadai. Dengan demikian, pengendalian DBD perlu memperhatikan tidak hanya aspek medis, tetapi juga faktor lingkungan dan perilaku masyarakat dalam menjaga kebersihan lingkungan.
summary(data_dbd)
## KABUPATEN/KOTA (nama wilayah) Jumlah DBD Jumlah Penduduk
## Length:27 Min. : 400 Min. : 209790
## Class :character 1st Qu.: 989 1st Qu.:1064345
## Mode :character Median :1902 Median :1884190
## Mean :2275 Mean :1864637
## 3rd Qu.:3274 3rd Qu.:2569685
## Max. :7680 Max. :5682300
## Jumlah Kejadian Banjir % Akses Sanitasi % Rumah Layak Huni Kasus Meninggal
## Min. : 0.000 Min. :4588 Min. :32.83 Min. : 0.00
## 1st Qu.: 5.000 1st Qu.:5960 1st Qu.:43.49 1st Qu.: 5.50
## Median : 7.000 Median :7732 Median :61.91 Median :11.00
## Mean : 9.667 Mean :7570 Mean :58.72 Mean :12.56
## 3rd Qu.:11.000 3rd Qu.:8972 3rd Qu.:72.64 3rd Qu.:17.00
## Max. :29.000 Max. :9908 Max. :85.78 Max. :38.00
Dari hasil analisis deskriptif, diketahui bahwa penelitian ini melibatkan 27 kabupaten/kota di Provinsi Jawa Barat. Jumlah kasus DBD di masing-masing daerah ternyata beda-beda cukup jauh, ada yang hanya sekitar 400 kasus, tapi ada juga yang sampai lebih dari 7.600 kasus. Kalau dilihat rata-ratanya, sekitar 2.275 kasus, jadi kebanyakan daerah itu ada di level menengah, meskipun beberapa wilayah punya lonjakan kasus yang cukup tinggi.
Jumlah penduduk per daerah juga beragam banget, mulai dari sekitar 200 ribu penduduk sampai lebih dari 5 juta jiwa. Ini nunjukin kalau kepadatan penduduk antarwilayah di Jawa Barat memang nggak sama. Untuk kejadian banjir, median-nya sekitar 7 kejadian per tahun, dengan kisaran 0–29 kejadian. Artinya, ada daerah yang sama sekali nggak kena banjir, tapi ada juga yang sering banget terdampak.
Jika dilihat dari indikator lingkungan, rata-rata akses sanitasi layak berada di angka 77%, sedangkan rumah layak huni rata-rata sekitar 73%. Meski angkanya termasuk cukup baik, masih ada daerah yang angkanya rendah dibanding yang lain. Sementara itu, jumlah kematian akibat DBD juga bervariasi, dengan median 11 kasus dan bisa mencapai hingga 38 kasus di satu wilayah.
Secara keseluruhan, hasil deskriptif ini menunjukkan adanya ketimpangan antarwilayah, baik dari sisi jumlah kasus maupun kondisi lingkungan. Perbedaan ini mengarah pada kemungkinan bahwa kondisi sosial ekonomi dan faktor lingkungan punya peran dalam tingginya kasus DBD. Makanya, temuan awal ini bisa jadi bahan awal untuk analisis lebih lanjut mengenai faktor risiko yang paling berpengaruh di tiap daerah.
Prevalensi_persen <- data_dbd %>%
mutate(
prevalensi_persen = (`Jumlah DBD` / `Jumlah Penduduk`) * 100) %>%
select(
`KABUPATEN/KOTA (nama wilayah)`,
`Jumlah DBD`,
`Jumlah Penduduk`,
prevalensi_persen)
Prevalensi_persen
## # A tibble: 27 × 4
## KABUPATEN/KOTA (nama wilay…¹ `Jumlah DBD` `Jumlah Penduduk` prevalensi_persen
## <chr> <dbl> <dbl> <dbl>
## 1 Kab. Bogor 3404 5682300 0.0599
## 2 Kab. Sukabumi 841 2828020 0.0297
## 3 Kab. Cianjur 1932 2584990 0.0747
## 4 Kab. Bandung 3589 3753120 0.0956
## 5 Garut 3269 2716950 0.120
## 6 Tasikmalaya 763 1920920 0.0397
## 7 Ciamis 1420 1259230 0.113
## 8 Kuningan 2165 1213930 0.178
## 9 Kab. Cirebon 1833 2387960 0.0768
## 10 Majalengka 1275 1352540 0.0943
## # ℹ 17 more rows
## # ℹ abbreviated name: ¹`KABUPATEN/KOTA (nama wilayah)`
Hasil perhitungan menunjukkan bahwa Kota Sukabumi memiliki prevalensi DBD tertinggi di Jawa Barat, yaitu sekitar 0,45% atau 446 kasus per 100.000 penduduk. Angka ini menunjukkan bahwa dari setiap 100.000 penduduk, terdapat sekitar 446 orang yang terinfeksi DBD selama tahun 2024. Tingginya prevalensi ini kemungkinan dipengaruhi oleh kepadatan permukiman, frekuensi kejadian banjir yang cukup tinggi, persentase rumah layak huni yang masih rendah, serta akses sanitasi yang belum optimal, yang semuanya mendukung perkembangbiakan nyamuk Aedes aegypti sebagai vektor DBD. Selanjutnya, Kota Bandung (0,30%) dan Kota Bogor (0,29%) juga menunjukkan prevalensi tinggi, sejalan dengan karakteristik wilayah perkotaan padat penduduk. Kota Tasikmalaya dan Kota Depok berada di posisi menengah dengan nilai sekitar 0,23%, sedangkan Pangandaran, Bandung Barat, Sumedang, Kota Banjar, dan Kuningan menempati urutan berikutnya dengan prevalensi di bawah 0,21%. Rendahnya angka ini dapat dikaitkan dengan tingkat akses sanitasi yang lebih baik, persentase rumah layak huni yang tinggi, dan kejadian banjir yang relatif jarang, sehingga risiko penularan lebih kecil.
Secara umum, kota dengan kepadatan penduduk tinggi cenderung memiliki angka prevalensi DBD lebih besar dibandingkan daerah perdesaan. Hal ini menegaskan pentingnya pengendalian berbasis lingkungan dan upaya preventif untuk menekan penularan DBD di wilayah perkotaan.
# Case Fatality Rate (CFR)
CFR <- data_dbd %>%
mutate(
CFR = `Kasus Meninggal` / `Jumlah DBD`
) %>%
select(
`KABUPATEN/KOTA (nama wilayah)`,
`Jumlah DBD`,
`Kasus Meninggal`,
CFR
)
CFR
## # A tibble: 27 × 4
## `KABUPATEN/KOTA (nama wilayah)` `Jumlah DBD` `Kasus Meninggal` CFR
## <chr> <dbl> <dbl> <dbl>
## 1 Kab. Bogor 3404 23 0.00676
## 2 Kab. Sukabumi 841 4 0.00476
## 3 Kab. Cianjur 1932 11 0.00569
## 4 Kab. Bandung 3589 38 0.0106
## 5 Garut 3269 14 0.00428
## 6 Tasikmalaya 763 8 0.0105
## 7 Ciamis 1420 11 0.00775
## 8 Kuningan 2165 5 0.00231
## 9 Kab. Cirebon 1833 6 0.00327
## 10 Majalengka 1275 11 0.00863
## # ℹ 17 more rows
Berdasarkan grafik Case Fatality Rate (CFR) DBD di Jawa Barat, terlihat bahwa beberapa wilayah memiliki tingkat kematian yang lebih tinggi dibandingkan daerah lainnya. Kota Cimahi menempati posisi tertinggi, diikuti oleh Kabupaten Subang, Purwakarta, dan Kota Banjar. Angka CFR yang lebih tinggi di wilayah-wilayah tersebut menunjukkan bahwa proporsi pasien DBD yang meninggal relatif lebih besar dibandingkan daerah lain di Jawa Barat.
Sementara itu, daerah seperti Pangandaran, Kuningan, Kota Depok, dan Kota Tasikmalaya menunjukkan CFR yang lebih rendah. Hal ini mengindikasikan bahwa meskipun kasus DBD mungkin terjadi, kemampuan dalam penanganan pasien atau kecepatan deteksi dan respon kesehatan di wilayah tersebut relatif lebih baik sehingga kematian dapat ditekan.
Peta jumlah kasus DBD menunjukkan gradasi warna yang semakin gelap pada beberapa wilayah tertentu, menandakan konsentrasi kasus yang tinggi. Kota Bandung terlihat sebagai wilayah dengan warna paling gelap, sesuai dengan jumlah kasus tertinggi yaitu 7.680 kasus. Selain itu, Kota Depok (5.040 kasus), Kab. Bandung Barat (3.754 kasus), Kab. Bogor (3.404 kasus), dan Kab. Bandung (3.589 kasus) juga berada pada kategori gelap, mencerminkan tingginya beban penyakit di pusat pertumbuhan penduduk dan wilayah urban.
Sebaliknya, warna lebih terang tampak dominan pada daerah seperti Kota Banjar (400 kasus), Pangandaran (890 kasus), dan Kab. Sukabumi (841 kasus). Wilayah-wilayah ini memiliki karakteristik jumlah penduduk yang relatif lebih kecil, sehingga jumlah kasus absolut lebih rendah. Secara keseluruhan, pola warna pada peta ini menunjukkan bahwa wilayah metropolitan dan padat penduduk cenderung memiliki kasus DBD lebih banyak dibandingkan wilayah dengan densitas penduduk rendah.
Pada peta prevalensi, intensitas warna gelap terlihat pada wilayah-wilayah dengan proporsi kasus terhadap jumlah penduduk lebih tinggi. Kota Sukabumi (445,94 per 100.000 penduduk), Kota Bandung (303,77 per 100.000), dan Kab. Sumedang (193,99 per 100.000) tampak menjadi klaster dengan tingkat risiko tinggi. Warna yang lebih gelap pada peta menunjukkan bahwa meskipun beberapa wilayah tidak memiliki jumlah kasus tertinggi secara absolut, proporsi penduduk yang terdampak sangat besar.
Di sisi lain, wilayah seperti Kab. Indramayu (27,58 per 100.000) dan Kab. Sukabumi (29,73 per 100.000) tampak lebih terang, menandakan prevalensi rendah. Kondisi ini menunjukkan bahwa distribusi beban penyakit tidak hanya dipengaruhi jumlah kasus, tetapi juga ukuran populasi. Artinya, wilayah lebih kecil dengan kasus cukup besar dapat menghasilkan prevalensi yang lebih tinggi dibandingkan kota besar.
Peta CFR menunjukkan intensitas warna gelap pada wilayah dengan tingkat fatalitas lebih tinggi. Daerah seperti Kota Cimahi (1.41%), Kab. Subang (1.29%), dan Kota Banjar (1.25%) tampak gelap, menandakan proporsi kematian yang lebih tinggi dibandingkan jumlah kasus. Ini dapat mencerminkan keterlambatan deteksi kasus, akses fasilitas kesehatan yang terbatas, atau kurangnya kesadaran masyarakat terkait tanda bahaya DBD.
Sebaliknya, wilayah seperti Pangandaran (0%), Kab. Sumedang (0.30%), dan Kota Tasikmalaya (0.28%) tampak lebih terang, menunjukkan bahwa meskipun ada transmisi aktif, penanganan klinis lebih efektif sehingga angka kematian lebih rendah. Pola ini menegaskan bahwa tingginya kasus tidak selalu berbanding lurus dengan fatalitas, kualitas respon kesehatan sangat berperan.
Peta jumlah penduduk menunjukkan konsentrasi tinggi di wilayah perkotaan seperti Bandung dan Bekasi, yang berpotensi meningkatkan risiko penyebaran DBD karena interaksi antarpenduduk lebih sering. Pola serupa terlihat pada peta kasus meninggal, di mana daerah dengan populasi padat cenderung memiliki angka kematian lebih tinggi.
Wilayah utara dan barat tampak lebih sering mengalami banjir, yang bisa memicu peningkatan populasi nyamuk penular DBD akibat banyaknya genangan air. Sementara itu, akses sanitasi layak cenderung lebih baik di wilayah tengah dan utara, tapi masih rendah di bagian selatan. Kondisi rumah layak huni juga menunjukkan ketimpangan serupa yaitu wilayah selatan relatif tertinggal dibanding bagian lain provinsi.
Secara keseluruhan, daerah dengan penduduk padat, frekuensi banjir tinggi, serta akses sanitasi dan perumahan yang kurang baik cenderung memiliki risiko DBD yang lebih besar. Hal ini menegaskan bahwa faktor sosial dan lingkungan punya peran penting dalam penyebaran DBD di Jawa Barat.
Berdasarkan hasil analisis korelasi antarvariabel, ditemukan beberapa hubungan menarik yang menggambarkan faktor-faktor yang berpotensi memengaruhi penyebaran DBD di Jawa Barat. Variabel Jumlah DBD menunjukkan korelasi positif cukup kuat dengan Kasus Meninggal (r = 0,72), yang berarti semakin tinggi jumlah kasus DBD di suatu wilayah, semakin besar pula kemungkinan terjadinya kematian akibat penyakit ini. Hal ini wajar karena meningkatnya jumlah kasus umumnya diikuti oleh meningkatnya beban sistem kesehatan daerah.
Selain itu, Jumlah Penduduk juga memiliki korelasi positif sedang dengan Jumlah DBD (r = 0,46). Hal ini berarti wilayah dengan penduduk padat cenderung mengalami lebih banyak kasus DBD, kemungkinan karena tingginya interaksi antarindividu serta kondisi permukiman yang padat dan berpotensi menjadi tempat berkembang biaknya nyamuk Aedes aegypti. Sementara itu, Jumlah Kejadian Banjir menunjukkan korelasi positif lemah terhadap kasus DBD (r = 0,22), yang dapat diartikan bahwa banjir mungkin berkontribusi terhadap peningkatan kasus melalui terbentuknya genangan air yang ideal bagi perkembangbiakan nyamuk.
Di sisi lain, persentase Akses Sanitasi (r = -0,17) dan Rumah Layak Huni (r = -0,34) menunjukkan hubungan negatif terhadap jumlah kasus DBD. Artinya, daerah dengan akses sanitasi yang lebih baik dan rumah yang layak huni cenderung memiliki tingkat DBD yang lebih rendah. Kondisi ini menegaskan pentingnya faktor lingkungan dan infrastruktur dasar dalam upaya pengendalian penyakit menular berbasis vektor. Selain itu, Akses Sanitasi dan Rumah Layak Huni memiliki korelasi positif tinggi (r = 0,84), yang menunjukkan bahwa wilayah dengan sanitasi memadai biasanya juga memiliki kualitas tempat tinggal yang lebih baik. Secara keseluruhan, hasil ini menggambarkan bahwa faktor kepadatan penduduk, kondisi lingkungan, dan infrastruktur sanitasi berperan penting dalam menentukan tingkat kerentanan suatu wilayah terhadap penyebaran DBD di Jawa Barat.
Berdasarkan hasil visualisasi heatmap, terlihat bahwa penyebaran kasus DBD di Provinsi Jawa Barat tidak merata antarwilayah. Warna yang semakin pekat menunjukkan jumlah kasus yang lebih tinggi, di mana Kota Bandung, Kabupaten Bandung, dan Kota Bekasi tampak menjadi wilayah dengan intensitas kasus paling besar sepanjang tahun 2020–2024. Pola ini memperlihatkan bahwa wilayah perkotaan dan padat penduduk cenderung lebih rentan terhadap penyebaran DBD dibandingkan daerah dengan kepadatan lebih rendah. Hal ini bisa dipengaruhi oleh faktor seperti banyaknya tempat penampungan air, perilaku masyarakat dalam menjaga kebersihan lingkungan, serta kondisi permukiman yang berpotensi menjadi habitat nyamuk Aedes aegypti.
Sementara itu, daerah seperti Kota Banjar, Kabupaten Tasikmalaya, dan Kabupaten Pangandaran menunjukkan warna yang lebih terang, menandakan kasus DBD relatif rendah. Kondisi ini kemungkinan disebabkan oleh faktor lingkungan yang lebih rural, tingkat urbanisasi yang rendah, serta kepadatan penduduk yang tidak setinggi di daerah perkotaan.
Ada fluktuasi tren kasus DBD dari tahun 2020–2024, menurut hasil visualisasi line chart untuk sepuluh kabupaten/kota dengan jumlah kasus tertinggi. Jumlah kasus di sebagian besar daerah turun pada tahun 2023, tetapi meningkat pada tahun 2024. Misalnya, peningkatan terbesar terlihat di Kota Bandung, yang akan mencapai lebih dari 7.000 kasus pada tahun 2024. Kabupaten Bandung, Kota Bekasi, dan Kota Depok juga mengalami peningkatan serupa.
Tren ini menunjukkan efek musiman dan lingkungan, seperti curah hujan yang tinggi pada tahun tertentu yang meningkatkan jumlah genangan air dan mempercepat perkembangbiakan nyamuk. Peningkatan mobilitas masyarakat setelah pandemi juga dapat meningkatkan kemungkinan penularan antarwilayah. Secara umum, tren waktu ini menunjukkan bahwa DBD memiliki pola yang berubah-ubah tetapi tampaknya telah meningkat dalam beberapa tahun terakhir. Akibatnya, pengawasan rutin dan pencegahan berbasis musim perlu diperkuat.
| Variabel | Moran’s l | P-value Asymptotic | P-value MC | Geary’s C | P-value C |
|---|---|---|---|---|---|
| Jumlah Kasus DBD | 0.1993 | 0.0371 | 0.054 | 0.6586 | 0.0368 |
| Prevalensi DBD | -0.2235 | 0.9106 | 0.929 | 1.0406 | 0.5874 |
Hasil analisis autokorelasi spasial menunjukkan bahwa pola penyebaran kasus DBD cenderung tidak sepenuhnya acak. Ada kemiripan kondisi antarwilayah yang berdekatan karena nilai Moran I yang positif dan signifikan dan nilai Geary C yang di bawah 1. Ini menunjukkan bahwa daerah dengan kasus tinggi biasanya berdekatan dengan daerah dengan kasus rendah. Meskipun polanya tidak terlalu kuat, jelas bahwa kelompok kasus muncul di beberapa daerah.
Sebaliknya, pola tersebut tidak terlihat pada prevalensi DBD; nilai Moran I dan Geary C tidak signifikan, jadi tidak ada kecenderungan wilayah dengan prevalensi tinggi mengelompok atau berdekatan satu sama lain. Persebarannya lebih acak, dan tiap wilayah tampak tidak terpengaruh secara spasial oleh wilayah tetangganya. Jumlah kasus secara keseluruhan menunjukkan pola pengelompokan, sedangkan prevalensi tidak menunjukkan pola ruang yang jelas.
Peta LISA untuk prevalensi DBD menunjukkan bahwa seluruh wilayah di Jawa Barat berada dalam kategori non signifikan. Kondisi ini mengindikasikan bahwa nilai prevalensi di suatu wilayah tidak memiliki hubungan spasial yang kuat dengan wilayah di sekitarnya. Tidak terlihat adanya kecenderungan wilayah dengan prevalensi tinggi terkelompok pada area tertentu, begitu pula untuk wilayah dengan prevalensi rendah.
Secara keseluruhan, hasil ini menggambarkan bahwa persebaran prevalensi DBD bersifat acak dan tidak membentuk pola pengelompokan tertentu. Setiap wilayah tampak memiliki karakteristik prevalensinya masing-masing tanpa menunjukkan adanya pengaruh spasial yang berarti.
Hasil perhitungan menunjukkan bahwa Kota Bandung memiliki skor tertinggi dengan sekitar 68,27, diikuti oleh Kabupaten Bandung dengan skor 62,6, dan Kota Depok dengan skor 61,7. Wilayah-wilayah ini dianggap prioritas tinggi karena tingginya kepadatan penduduk, aktivitas perkotaan yang padat, dan risiko banjir dan tempat berkembang biaknya nyamuk.
Namun, daerah yang termasuk dalam kategori prioritas sedang hingga rendah, seperti Kabupaten Ciamis dan Kabupaten Sumedang, menerima skor yang lebih rendah di bawah 50 yang menunjukkan bahwa risiko penyebaran DBD lebih terkendali. Namun, ini tidak berarti bahwa wilayah tersebut tidak terancam. Upaya promotif dan preventif harus terus dilakukan untuk mencegah peningkatan kasus di masa depan.
Secara keseluruhan, hasil analisis ini menunjukkan bahwa faktor lingkungan dan kepadatan penduduk terus menjadi penentu utama risiko DBD di Jawa Barat. Oleh karena itu, intervensi pengendalian sebaiknya difokuskan pada wilayah dengan prioritas tinggi melalui peningkatan sanitasi lingkungan, perbaikan kualitas hunian, dan pelatihan masyarakat tentang cara memberantas sarang nyamuk secara berkelanjutan.
Penelitian ini menggunakan desain cross-sectional, di mana seluruh data dikumpulkan pada satu periode waktu yang sama. Desain ini dipilih karena sesuai untuk menggambarkan kondisi wilayah terkait kejadian DBD berdasarkan data yang tersedia pada tingkat kabupaten/kota. Melalui desain ini, peneliti dapat melihat pola distribusi dan faktor yang mungkin berkaitan dengan kejadian DBD pada waktu tertentu. Meskipun desain cross-sectional tidak dapat digunakan untuk menarik kesimpulan sebab-akibat, pendekatan ini tetap efektif untuk memberikan gambaran awal mengenai situasi kesehatan masyarakat di suatu wilayah.
Variabel utama yang digunakan terdiri dari jumlah kasus DBD dan jumlah kematian akibat DBD sebagai outcome. Kedua variabel ini nantinya dapat dihitung menjadi angka insidensi maupun Case Fatality Rate (CFR) untuk melihat besar risiko dan tingkat keparahan penyakit. Sementara itu, variabel yang diduga berhubungan dengan kejadian DBD antara lain jumlah kejadian banjir, persentase akses sanitasi layak, persentase rumah layak huni, dan jumlah penduduk sebagai faktor yang menggambarkan kepadatan dan kondisi lingkungan secara umum.
Populasi penelitian mencakup 27 kabupaten/kota di Provinsi Jawa Barat, di mana setiap wilayah menjadi satu unit analisis. Karena semua wilayah yang ada dimasukkan dalam penelitian, metode yang digunakan adalah total sampling, sehingga gambaran yang dihasilkan mewakili keseluruhan provinsi tanpa harus melakukan generalisasi dari sampel kecil. Semua data diperoleh dari sumber resmi pemerintah pada periode yang sama agar hasilnya tetap konsisten.
Seluruh data bersumber dari Open Data Jabar, Badan Pusat Statistik (BPS), dan Dinas Kesehatan Provinsi Jawa Barat dalam periode waktu yang sama (tahun 2024), agar hasil analisis tetap konsisten dan dapat dibandingkan antarwilayah.
Potensi bias utama dalam penelitian ini adalah bias ekologis, yaitu ketika hubungan yang ditemukan pada level kabupaten/kota tidak selalu berlaku pada tingkat individu. Misalnya, wilayah dengan sanitasi buruk dan angka DBD tinggi belum tentu berarti semua individu di wilayah tersebut memiliki risiko tinggi yang sama.
Selain itu, terdapat kemungkinan bias informasi, seperti perbedaan kualitas dan ketepatan pelaporan kasus antarwilayah atau adanya kasus yang tidak tercatat (underreporting). Beberapa variabel pengganggu (confounding) seperti curah hujan, suhu udara, dan perilaku masyarakat dalam menjaga kebersihan lingkungan juga belum sepenuhnya tercakup dalam analisis ini, sehingga dapat memengaruhi hasil secara tidak langsung.
Penyebaran Demam Berdarah Dengue (DBD) di Provinsi Jawa Barat tahun 2024 merupakan hasil interaksi antara virus Dengue sebagai agent, manusia sebagai host yang rentan, dan lingkungan yang mendukung seperti banjir, kepadatan penduduk, sanitasi yang kurang baik, dan rumah yang kurang layak. Berdasarkan ukuran frekuensi, Kota Sukabumi memiliki prevalensi tertinggi, sedangkan Kota Cimahi dan Kabupaten Subang menunjukkan Case Fatality Rate (CFR) tertinggi, menandakan variasi tingkat kejadian dan keparahan penyakit antarwilayah. Analisis spasial menunjukkan pola persebaran kasus yang acak tanpa klaster signifikan, sehingga perbedaan angka DBD lebih dipengaruhi oleh kondisi sosial dan perilaku masyarakat dibanding faktor geografis. Desain studi yang digunakan adalah cross-sectional, sesuai untuk menggambarkan hubungan antarvariabel lingkungan dan kejadian DBD pada level kabupaten/kota. Berdasarkan hasil analisis, wilayah prioritas penanggulangan meliputi Kota Sukabumi, Kota Bandung, Kota Bogor, Kota Cimahi, dan Kabupaten Subang, yang memiliki beban kasus dan fatalitas tinggi.
Upaya pengendalian DBD perlu difokuskan pada wilayah dengan angka kasus dan fatalitas tinggi melalui peningkatan kegiatan Pemberantasan Sarang Nyamuk (PSN), edukasi masyarakat, serta penguatan layanan kesehatan untuk deteksi dini dan penanganan cepat kasus berat. Perbaikan akses sanitasi dan kualitas hunian juga perlu menjadi prioritas di daerah berisiko, disertai integrasi data banjir dan iklim dalam sistem peringatan dini DBD. Selain itu, penelitian lanjutan dengan desain kohort atau time-series penting dilakukan untuk memahami pola musiman dan faktor risiko individu yang memengaruhi penularan DBD di Jawa Barat. Sinergi antara pemerintah daerah, tenaga kesehatan, dan masyarakat sangat dibutuhkan untuk menjaga kebersihan lingkungan serta memantau perkembangan kasus secara berkelanjutan.
Arsyad, R. M., Nabuasa, E., & Ndoen, E. M. (2020). Hubungan antara Perilaku Sanitasi Lingkungan dengan Kejadian Demam Berdarah Dengue (DBD) di Wilayah Kerja Puskesmas Tarus. Media Kesehatan Masyarakat, 2(2), 15–23. https://doi.org/10.35508/mkm.v2i2.2498.
Haryanti, R. S., & Sari, D. (2022). Hubungan faktor host, agent, dan environment dengan kejadian Demam Berdarah Dengue di Kota Semarang. Jurnal Kesehatan Masyarakat, 10(2), 101-108.
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.
Komalasari, V., & Nurhidayah, N. (2023). Faktor Pengaruh Kesehatan Lingkungan terhadap Kejadian Demam Berdarah Dangue (DBD) di Wilayah Endemis: Systematic Literature Review. Health Information : Jurnal Penelitian, 15(3). Retrieved from https://myjurnal.poltekkes-kdi.ac.id/index.php/hijp/article/view/1327/1296
Lestari, P. A., Fajar, N. A., Windusari, Y., Novrikasari, & Sunarsih, E. (2023). Faktor Pengaruh Kesehatan Lingkungan terhadap Kejadian Demam Berdarah Dangue (DBD) di Wilayah Endemis: Systematic Literature Review. Health Information : Jurnal Penelitian, 15(3), e1327. https://myjurnal.poltekkes-kdi.ac.id/index.php/hijp/article/view/1327/1296
Wulandari, R. A., dkk. (2024). Analysis of Climate and Environmental Risk Factors on Dengue Hemorrhagic Fever (DHF) in Bogor District. Journal of Public Health and Community Health Development, 7(3). https://scholarhub.ui.ac.id/kesmas/vol18/iss3/9/
library(tidyverse) # termasuk readr, dplyr, ggplot2, dll
data_dbd <- read_csv("/Users/tansyazakaria/Downloads/Data - Sheet1 (1) copy.csv")
## Rows: 27 Columns: 7
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (1): KABUPATEN/KOTA (nama wilayah)
## dbl (5): Jumlah DBD, Jumlah Penduduk, Jumlah Kejadian Banjir, % Rumah Layak ...
## num (1): % Akses Sanitasi
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
data_dbd
## # A tibble: 27 × 7
## KABUPATEN/KOTA (nama …¹ `Jumlah DBD` `Jumlah Penduduk` Jumlah Kejadian Banj…²
## <chr> <dbl> <dbl> <dbl>
## 1 Kab. Bogor 3404 5682300 29
## 2 Kab. Sukabumi 841 2828020 24
## 3 Kab. Cianjur 1932 2584990 8
## 4 Kab. Bandung 3589 3753120 21
## 5 Garut 3269 2716950 7
## 6 Tasikmalaya 763 1920920 3
## 7 Ciamis 1420 1259230 11
## 8 Kuningan 2165 1213930 3
## 9 Kab. Cirebon 1833 2387960 18
## 10 Majalengka 1275 1352540 5
## # ℹ 17 more rows
## # ℹ abbreviated names: ¹`KABUPATEN/KOTA (nama wilayah)`,
## # ²`Jumlah Kejadian Banjir`
## # ℹ 3 more variables: `% Akses Sanitasi` <dbl>, `% Rumah Layak Huni` <dbl>,
## # `Kasus Meninggal` <dbl>
names(data_dbd) <- c("KABUPATEN/KOTA (nama wilayah)", "Jumlah DBD", "Jumlah Penduduk",
"Jumlah Kejadian Banjir", "% Akses Sanitasi",
"% Rumah Layak Huni", "Kasus Meninggal")
# Prevalensi
Prevalensi_persen <- data_dbd %>%
mutate(
prevalensi_persen = (`Jumlah DBD` / `Jumlah Penduduk`) * 100) %>%
select(
`KABUPATEN/KOTA (nama wilayah)`,
`Jumlah DBD`,
`Jumlah Penduduk`,
prevalensi_persen)
Prevalensi_persen
## # A tibble: 27 × 4
## KABUPATEN/KOTA (nama wilay…¹ `Jumlah DBD` `Jumlah Penduduk` prevalensi_persen
## <chr> <dbl> <dbl> <dbl>
## 1 Kab. Bogor 3404 5682300 0.0599
## 2 Kab. Sukabumi 841 2828020 0.0297
## 3 Kab. Cianjur 1932 2584990 0.0747
## 4 Kab. Bandung 3589 3753120 0.0956
## 5 Garut 3269 2716950 0.120
## 6 Tasikmalaya 763 1920920 0.0397
## 7 Ciamis 1420 1259230 0.113
## 8 Kuningan 2165 1213930 0.178
## 9 Kab. Cirebon 1833 2387960 0.0768
## 10 Majalengka 1275 1352540 0.0943
## # ℹ 17 more rows
## # ℹ abbreviated name: ¹`KABUPATEN/KOTA (nama wilayah)`
# Plot Prevalensi
ggplot(Prevalensi_persen, aes(x = reorder(`KABUPATEN/KOTA (nama wilayah)`, prevalensi_persen),
y = prevalensi_persen, fill = `KABUPATEN/KOTA (nama wilayah)`)) +
geom_bar(stat = "identity", show.legend = FALSE) +
coord_flip() +
labs(
title = "Prevalensi DBD di Jawa Barat",
x = "Kabupaten/Kota",
y = "Prevalensi"
) +
theme_minimal(base_size = 13)
# Case Fatality Rate (CFR)
CFR <- data_dbd %>%
mutate(
CFR = `Kasus Meninggal` / `Jumlah DBD`
) %>%
select(
`KABUPATEN/KOTA (nama wilayah)`,
`Jumlah DBD`,
`Kasus Meninggal`,
CFR
)
CFR
## # A tibble: 27 × 4
## `KABUPATEN/KOTA (nama wilayah)` `Jumlah DBD` `Kasus Meninggal` CFR
## <chr> <dbl> <dbl> <dbl>
## 1 Kab. Bogor 3404 23 0.00676
## 2 Kab. Sukabumi 841 4 0.00476
## 3 Kab. Cianjur 1932 11 0.00569
## 4 Kab. Bandung 3589 38 0.0106
## 5 Garut 3269 14 0.00428
## 6 Tasikmalaya 763 8 0.0105
## 7 Ciamis 1420 11 0.00775
## 8 Kuningan 2165 5 0.00231
## 9 Kab. Cirebon 1833 6 0.00327
## 10 Majalengka 1275 11 0.00863
## # ℹ 17 more rows
# Plot CFR
ggplot(CFR, aes(x = reorder(`KABUPATEN/KOTA (nama wilayah)`, CFR),
y = CFR, fill = `KABUPATEN/KOTA (nama wilayah)`)) +
geom_bar(stat = "identity", show.legend = FALSE) +
coord_flip() +
labs(
title = "Case Fatality Rate (CFR) DBD di Jawa Barat",
x = "Kabupaten/Kota",
y = "CFR"
) +
theme_minimal(base_size = 13)
# 1. Setup & Load Data -------------------------------------------------------
suppressPackageStartupMessages({
library(dplyr)
library(readr)
library(stringr)
library(leaflet)
library(sf)
library(geodata)
library(htmltools)
library(DT)
library(knitr)
library(ggplot2)
})
# Function untuk parsing angka
safe_num <- function(x) {
if (is.numeric(x)) return(as.numeric(x))
x2 <- as.character(x)
x2 <- str_replace_all(x2, "\\s+", "")
x2 <- str_replace_all(x2, "\\.", "") # thousands dot
x2 <- str_replace_all(x2, ",", ".") # comma decimal
x2 <- str_replace_all(x2, "[^0-9eE+\\-\\.]", "")
suppressWarnings(as.numeric(x2))
}
# Parsing numerik
data_dbd <- data_dbd %>%
mutate(
across(`KABUPATEN/KOTA (nama wilayah)`, str_trim),
`Jumlah DBD` = safe_num(`Jumlah DBD`),
`Jumlah Penduduk` = safe_num(`Jumlah Penduduk`),
`Kasus Meninggal` = safe_num(`Kasus Meninggal`),
`Jumlah Kejadian Banjir` = safe_num(`Jumlah Kejadian Banjir`),
`% Akses Sanitasi` = safe_num(`% Akses Sanitasi`),
`% Rumah Layak Huni` = safe_num(`% Rumah Layak Huni`)
)
# 2. Load & Prepare Shapefile ------------------------------------------------
cat("Downloading shapefile...\n")
## Downloading shapefile...
indo <- gadm(country = "IDN", level = 2, path = tempdir())
# Filter Jawa Barat
jabar <- indo[indo$NAME_1 == "Jawa Barat" | indo$NAME_1 == "West Java", ]
# Convert ke sf object
shp <- st_as_sf(jabar)
# Perbaiki nama yang salah di shapefile GADM
shp$NAME_2 <- ifelse(tolower(shp$NAME_2) == "cimahi", "Kota Cimahi", shp$NAME_2)
shp$NAME_2 <- ifelse(tolower(shp$NAME_2) == "depok", "Kota Depok", shp$NAME_2)
shp$NAME_2 <- ifelse(tolower(shp$NAME_2) == "banjar", "Kota Banjar", shp$NAME_2)
# Distribusikan Waduk Cirata ke kabupaten
waduk_idx <- grep("waduk|cirata", tolower(shp$NAME_2))
if(length(waduk_idx) > 0) {
for(idx in waduk_idx) {
waduk_geom <- shp[idx, ]
centroid <- st_centroid(st_geometry(waduk_geom))
coords <- st_coordinates(centroid)
if(coords[2] > -6.7) {
shp$NAME_2[idx] <- "Purwakarta"
} else if(coords[1] > 107.5) {
shp$NAME_2[idx] <- "Bandung Barat"
} else {
shp$NAME_2[idx] <- "Cianjur"
}
}
}
cat("Shapefile Jawa Barat berhasil dimuat:", nrow(shp), "wilayah\n\n")
## Shapefile Jawa Barat berhasil dimuat: 27 wilayah
# Cleaning nama wilayah untuk matching
data_dbd$Wilayah_Clean <- tolower(trimws(data_dbd$`KABUPATEN/KOTA (nama wilayah)`))
data_dbd$Wilayah_Clean <- gsub("^kab\\.?\\s*", "", data_dbd$Wilayah_Clean)
data_dbd$Wilayah_Clean <- gsub("^kabupaten\\s+", "", data_dbd$Wilayah_Clean)
data_dbd$Wilayah_Clean <- gsub("\\s+", " ", data_dbd$Wilayah_Clean)
data_dbd$Wilayah_Clean <- trimws(data_dbd$Wilayah_Clean)
shp$Wilayah_Clean <- tolower(trimws(shp$NAME_2))
shp$Wilayah_Clean <- gsub("^kabupaten\\s+", "", shp$Wilayah_Clean)
shp$Wilayah_Clean <- gsub("\\s+", " ", shp$Wilayah_Clean)
shp$Wilayah_Clean <- trimws(shp$Wilayah_Clean)
# --- Hitung Prevalensi & CFR langsung di data utama ---
data_dbd <- data_dbd %>%
mutate(
Prevalensi_persen = (`Jumlah DBD` / `Jumlah Penduduk`) * 100,
CFR = (`Kasus Meninggal` / `Jumlah DBD`) * 100 # dikali 100 supaya jadi persen
)
# Merge shapefile dengan data
merged <- merge(shp, data_dbd, by = "Wilayah_Clean", all.x = TRUE)
merged_df <- st_transform(merged, crs = 4326)
# Buat peta dengan ggplot2
map_kasus <- ggplot(data = merged_df) +
# Polygon dengan warna sesuai jumlah kasus
geom_sf(aes(fill = `Jumlah DBD`), color = "white", size = 0.8) +
# Gradient warna vibrant seperti gambar pertama
scale_fill_gradientn(
colors = c("#FFF44F", "#FFD700", "#FFA500", "#FF6B35", "#E63946",
"#D62828", "#B8336A", "#9B2C8B", "#7209B7", "#3A0CA3", "#240046"),
na.value = "#E0E0E0",
name = "Jumlah Kasus\nDBD",
labels = function(x) format(x, big.mark = ".", scientific = FALSE),
guide = guide_colorbar(
barwidth = 1.5,
barheight = 12,
title.position = "top",
title.hjust = 0.5
)
) +
# Judul dan tema
labs(
title = "Peta Sebaran Kasus DBD - Jawa Barat",
subtitle = "Distribusi Jumlah Kasus Demam Berdarah Dengue per Kabupaten/Kota") +
theme_minimal() +
theme(
plot.title = element_text(size = 16, face = "bold", hjust = 0.5),
plot.subtitle = element_text(size = 11, hjust = 0.5, color = "gray40"),
plot.caption = element_text(size = 8, color = "gray50", hjust = 0),
legend.position = "right",
legend.title = element_text(size = 11, face = "bold"),
legend.text = element_text(size = 9),
panel.grid = element_blank(),
axis.text = element_blank(),
axis.ticks = element_blank(),
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f5f5f5", color = NA),
plot.margin = margin(10, 10, 10, 10)
) +
coord_sf(datum = NA) # Hilangkan garis koordinat
# Print peta
print(map_kasus)
## Warning in prettyNum(.Internal(format(x, trim, digits, nsmall, width, 3L, :
## 'big.mark' and 'decimal.mark' are both '.', which could be confusing
# PETA PREVALENSI
map_prev <- ggplot(data = merged_df) +
geom_sf(aes(fill = Prevalensi_persen), color = "white", size = 0.8) +
scale_fill_gradientn(
colors = c("#FEF9E7", "#FCF3CF", "#FCE181", "#F9E79F", "#F7DC6F",
"#F4D03F", "#F1C40F", "#D4AC0D", "#B7950B", "#9A7D0A", "#7D6608"),
na.value = "#E0E0E0",
name = "Prevalensi",
labels = function(x) format(round(x, 2), big.mark = ".", scientific = FALSE)
) +
labs(
title = "Peta Prevalensi DBD - Jawa Barat",
subtitle = "Tingkat Kejadian DBD",
) +
theme_minimal() +
theme(
plot.title = element_text(size = 16, face = "bold", hjust = 0.5),
plot.subtitle = element_text(size = 11, hjust = 0.5, color = "gray40"),
plot.caption = element_text(size = 8, color = "gray50", hjust = 0),
legend.position = "right",
panel.grid = element_blank(),
axis.text = element_blank(),
axis.ticks = element_blank(),
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f5f5f5", color = NA)
) +
coord_sf(datum = NA)
print(map_prev)
## Warning in prettyNum(.Internal(format(x, trim, digits, nsmall, width, 3L, :
## 'big.mark' and 'decimal.mark' are both '.', which could be confusing
# PETA CASE FATALITY RATE (CFR)
map_cfr <- ggplot(data = merged_df) +
geom_sf(aes(fill = CFR), color = "white", size = 0.8) +
scale_fill_gradientn(
colors = c("#EBF5FB", "#D6EAF8", "#AED6F1", "#85C1E2", "#5DADE2",
"#3498DB", "#2E86C1", "#2874A6", "#21618C", "#1B4F72", "#154360"),
na.value = "#E0E0E0",
name = "Case Fatality\nRate (%)",
labels = function(x) format(round(x, 2), big.mark = ".", scientific = FALSE)
) +
labs(
title = "Peta Case Fatality Rate (CFR) - Jawa Barat",
subtitle = "Persentase Kematian dari Total Kasus DBD",
) +
theme_minimal() +
theme(
plot.title = element_text(size = 16, face = "bold", hjust = 0.5),
plot.subtitle = element_text(size = 11, hjust = 0.5, color = "gray40"),
plot.caption = element_text(size = 8, color = "gray50", hjust = 0),
legend.position = "right",
panel.grid = element_blank(),
axis.text = element_blank(),
axis.ticks = element_blank(),
plot.background = element_rect(fill = "white", color = NA),
panel.background = element_rect(fill = "#f5f5f5", color = NA)
) +
coord_sf(datum = NA)
print(map_cfr)
## Warning in prettyNum(.Internal(format(x, trim, digits, nsmall, width, 3L, :
## 'big.mark' and 'decimal.mark' are both '.', which could be confusing
# =============================
# 1. Library
# =============================
library(ggplot2)
library(dplyr)
library(patchwork)
##
## Attaching package: 'patchwork'
## The following object is masked from 'package:terra':
##
## area
# =============================
# 2. Asumsi: Data sudah digabung ke merged_df (hasil merge shapefile + data_dbd)
# =============================
# Pastikan merged_df sudah ada dan memiliki kolom berikut:
# `Jumlah Penduduk`, `Kasus Meninggal`, `Jumlah Kejadian Banjir`,
# `% Akses Sanitasi`, `% Rumah Layak Huni`, geometry (sf)
# =============================
# 3. Peta Jumlah Penduduk
# =============================
map_penduduk <- ggplot(data = merged_df) +
geom_sf(aes(fill = `Jumlah Penduduk`), color = "white", size = 0.8) +
scale_fill_gradientn(
colors = c("#D5F4E6", "#A9DFBF", "#7DCEA0", "#52BE80", "#27AE60",
"#229954", "#1E8449", "#196F3D", "#145A32", "#0B5345"),
na.value = "#E0E0E0",
name = "Jumlah\nPenduduk",
labels = function(x) format(x, big.mark = ".", scientific = FALSE)
) +
labs(
title = "Jumlah Penduduk",
subtitle = "Distribusi jumlah penduduk per kabupaten/kota"
) +
theme_minimal() +
theme(
plot.title = element_text(face = "bold", size = 12, hjust = 0.5),
plot.subtitle = element_text(size = 10, hjust = 0.5, color = "gray40"),
legend.position = "right",
panel.grid = element_blank(),
axis.text = element_blank(),
axis.ticks = element_blank()
) +
coord_sf(datum = NA)
# =============================
# 4. Peta Kasus Meninggal
# =============================
map_meninggal <- ggplot(data = merged_df) +
geom_sf(aes(fill = `Kasus Meninggal`), color = "white", size = 0.8) +
scale_fill_gradientn(
colors = c("#FADBD8", "#F5B7B1", "#F1948A", "#EC7063", "#E74C3C",
"#CB4335", "#B03A2E", "#943126", "#78281F", "#641E16"),
na.value = "#E0E0E0",
name = "Kasus\nMeninggal",
labels = function(x) format(x, big.mark = ".", scientific = FALSE)
) +
labs(
title = "Kasus Meninggal DBD",
subtitle = "Jumlah kematian akibat DBD per kabupaten/kota"
) +
theme_minimal() +
theme(
plot.title = element_text(face = "bold", size = 12, hjust = 0.5),
plot.subtitle = element_text(size = 10, hjust = 0.5, color = "gray40"),
legend.position = "right",
panel.grid = element_blank(),
axis.text = element_blank(),
axis.ticks = element_blank()
) +
coord_sf(datum = NA)
# =============================
# 5. Peta Jumlah Kejadian Banjir
# =============================
map_banjir <- ggplot(data = merged_df) +
geom_sf(aes(fill = `Jumlah Kejadian Banjir`), color = "white", size = 0.8) +
scale_fill_gradientn(
colors = c("#EBF5FB", "#D6EAF8", "#AED6F1", "#85C1E2", "#5DADE2",
"#3498DB", "#2E86C1", "#2874A6", "#21618C", "#1B4F72"),
na.value = "#E0E0E0",
name = "Kejadian\nBanjir",
labels = function(x) format(x, big.mark = ".", scientific = FALSE)
) +
labs(
title = "Jumlah Kejadian Banjir",
subtitle = "Frekuensi banjir per kabupaten/kota"
) +
theme_minimal() +
theme(
plot.title = element_text(face = "bold", size = 12, hjust = 0.5),
plot.subtitle = element_text(size = 10, hjust = 0.5, color = "gray40"),
legend.position = "right",
panel.grid = element_blank(),
axis.text = element_blank(),
axis.ticks = element_blank()
) +
coord_sf(datum = NA)
# =============================
# 6. Peta Akses Sanitasi
# =============================
map_sanit <- ggplot(data = merged_df) +
geom_sf(aes(fill = `% Akses Sanitasi`), color = "white", size = 0.8) +
scale_fill_gradientn(
colors = c("#FEF9E7", "#FCF3CF", "#F9E79F", "#F7DC6F", "#F4D03F",
"#F1C40F", "#D4AC0D", "#B7950B", "#9A7D0A", "#7D6608"),
na.value = "#E0E0E0",
name = "Akses\nSanitasi (%)",
labels = function(x) format(round(x, 2), big.mark = ".", scientific = FALSE)
) +
labs(
title = "Akses Sanitasi Layak",
subtitle = "Persentase rumah tangga dengan akses sanitasi layak"
) +
theme_minimal() +
theme(
plot.title = element_text(face = "bold", size = 12, hjust = 0.5),
plot.subtitle = element_text(size = 10, hjust = 0.5, color = "gray40"),
legend.position = "right",
panel.grid = element_blank(),
axis.text = element_blank(),
axis.ticks = element_blank()
) +
coord_sf(datum = NA)
# =============================
# 7. Peta Rumah Layak Huni
# =============================
map_rumah <- ggplot(data = merged_df) +
geom_sf(aes(fill = `% Rumah Layak Huni`), color = "white", size = 0.8) +
scale_fill_gradientn(
colors = c("#FADBD8", "#F5B7B1", "#F1948A", "#FCDC94", "#F9E79F",
"#F7DC6F", "#A9DFBF", "#7DCEA0", "#52BE80", "#27AE60"),
na.value = "#E0E0E0",
name = "Rumah Layak\nHuni (%)",
labels = function(x) format(round(x, 2), big.mark = ".", scientific = FALSE)
) +
labs(
title = "Rumah Layak Huni",
subtitle = "Persentase rumah dengan kondisi layak huni"
) +
theme_minimal() +
theme(
plot.title = element_text(face = "bold", size = 12, hjust = 0.5),
plot.subtitle = element_text(size = 10, hjust = 0.5, color = "gray40"),
legend.position = "right",
panel.grid = element_blank(),
axis.text = element_blank(),
axis.ticks = element_blank()
) +
coord_sf(datum = NA)
combined_maps <- (
(map_penduduk + map_meninggal) /
(map_banjir + map_sanit + map_rumah)
) +
plot_annotation(
theme = theme(
plot.title = element_text(size = 10, face = "bold", hjust = 0.5),
plot.subtitle = element_text(size = 5, hjust = 0.5, color = "gray40"),
plot.caption = element_text(size = 3, hjust = 0.5, color = "gray50"),
plot.background = element_rect(fill = "white", color = NA)
)
)
combined_maps
## Warning in prettyNum(.Internal(format(x, trim, digits, nsmall, width, 3L, :
## 'big.mark' and 'decimal.mark' are both '.', which could be confusing
## Warning in prettyNum(.Internal(format(x, trim, digits, nsmall, width, 3L, :
## 'big.mark' and 'decimal.mark' are both '.', which could be confusing
## Warning in prettyNum(.Internal(format(x, trim, digits, nsmall, width, 3L, :
## 'big.mark' and 'decimal.mark' are both '.', which could be confusing
## Warning in prettyNum(.Internal(format(x, trim, digits, nsmall, width, 3L, :
## 'big.mark' and 'decimal.mark' are both '.', which could be confusing
## Warning in prettyNum(.Internal(format(x, trim, digits, nsmall, width, 3L, :
## 'big.mark' and 'decimal.mark' are both '.', which could be confusing
# --- Import library ---
library(corrplot)
## corrplot 0.95 loaded
# --- Pilih variabel numerik untuk korelasi ---
cor_vars <- c("Jumlah DBD", "Jumlah Penduduk", "Jumlah Kejadian Banjir", "% Akses Sanitasi",
"% Rumah Layak Huni", "Kasus Meninggal")
cor_df <- data_dbd %>%
select(all_of(cor_vars)) %>%
mutate(across(everything(), as.numeric))
# --- Hitung matriks korelasi ---
cor_matrix <- cor(cor_df, use = "pairwise.complete.obs")
# --- Plot korelasi ---
corrplot(cor_matrix,
method = "color",
type = "upper",
addCoef.col = "black",
number.cex = 1,
tl.col = "black",
tl.srt = 45,
tl.cex = 1.3,
col = colorRampPalette(c("#2166ac", "white", "#b2182b"))(300))
# === Library yang dibutuhkan ===
library(ggplot2)
library(readr)
library(dplyr)
# === Import data ===
dbd_tahunan <- read_csv("/Users/tansyazakaria/Downloads/Data DBD tahunan.csv")
## Rows: 27 Columns: 6
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (1): wilayah
## dbl (5): 2020, 2021, 2022, 2023, 2024
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# Cek struktur awal
glimpse(dbd_tahunan)
## Rows: 27
## Columns: 6
## $ wilayah <chr> "KABUPATEN BOGOR", "KABUPATEN SUKABUMI", "KABUPATEN CIANJUR", …
## $ `2020` <dbl> 1296, 561, 684, 2303, 651, 179, 1457, 362, 880, 1153, 707, 214…
## $ `2021` <dbl> 2220, 272, 430, 2026, 1011, 48, 470, 544, 819, 447, 1264, 188,…
## $ `2022` <dbl> 1953, 383, 819, 4191, 904, 319, 702, 1474, 1815, 641, 2186, 37…
## $ `2023` <dbl> 1881, 113, 829, 1005, 730, 88, 232, 630, 728, 253, 1308, 218, …
## $ `2024` <dbl> 3404, 841, 1932, 3589, 3269, 763, 1420, 2165, 1833, 1275, 2303…
# === Ubah dari wide ke long format ===
dbd_long <- dbd_tahunan %>%
pivot_longer(cols = c(`2020`, `2021`, `2022`, `2023`, `2024`),
names_to = "Tahun",
values_to = "Kasus_DBD")
dbd_long$Tahun <- as.numeric(dbd_long$Tahun)
# === Ambil 10 wilayah dengan total kasus tertinggi ===
top10 <- dbd_long %>%
group_by(wilayah) %>%
summarise(total_kasus = sum(Kasus_DBD, na.rm = TRUE)) %>%
arrange(desc(total_kasus)) %>%
slice_head(n = 10)
# === Filter dataset untuk 10 wilayah tersebut ===
dbd_top10 <- dbd_long %>%
filter(wilayah %in% top10$wilayah)
# === Line chart Top 10 ===
ggplot(dbd_top10, aes(x = Tahun, y = Kasus_DBD, 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 DBD (Top 10 Kabupaten/Kota) Jawa Barat 2020–2024",
x = "Tahun",
y = "Jumlah Kasus DBD",
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.
ggplot(dbd_long, aes(x = Tahun, y = reorder(wilayah, Kasus_DBD), fill = Kasus_DBD)) +
geom_tile(color = "white") +
scale_fill_gradient(low = "lightyellow", high = "red") +
theme_minimal(base_size = 13) +
labs(
title = "Heatmap Distribusi Kasus DBD di Jawa Barat (2020–2024)",
x = "Tahun",
y = "Kabupaten/Kota",
fill = "Kasus DBD"
) +
theme(
axis.text.y = element_text(size = 9),
plot.title = element_text(hjust = 0.5, face = "bold")
)
# ANALISIS AUTOKORELASI SPASIAL: MORAN'S I DAN LISA MAP
# LIBRARY
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')`
##
## Attaching package: 'spData'
## The following object is masked _by_ '.GlobalEnv':
##
## coords
library(sf)
library(ggplot2)
library(dplyr)
# DATA
# Gunakan merged_df yang sudah berisi shapefile + data DBD
# 1. Hapus data yang kosong
DBD_Jabar <- merged_df %>%
filter(!is.na(Prevalensi_persen) & !is.na(`Jumlah DBD`))
# Bersihkan nama wilayah biar konsisten
DBD_Jabar$NAME_2 <- trimws(toupper(DBD_Jabar$NAME_2))
# Gabungkan kalau ada wilayah dengan nama ganda (misal CIANJUR muncul 2x)
DBD_Jabar <- DBD_Jabar %>%
group_by(NAME_2) %>%
summarise(across(where(is.numeric), mean, na.rm = TRUE))
## Warning: There was 1 warning in `summarise()`.
## ℹ In argument: `across(where(is.numeric), mean, na.rm = TRUE)`.
## ℹ In group 1: `NAME_2 = "BANDUNG"`.
## Caused by warning:
## ! The `...` argument of `across()` is deprecated as of dplyr 1.1.0.
## Supply arguments directly to `.fns` through an anonymous function instead.
##
## # Previously
## across(a:b, mean, na.rm = TRUE)
##
## # Now
## across(a:b, \(x) mean(x, na.rm = TRUE))
# Set rownames pakai nama wilayah (sekarang sudah unik)
row.names(DBD_Jabar) <- DBD_Jabar$NAME_2
## Warning: Setting row names on a tibble is deprecated.
# SPATIAL WEIGHTS MATRIX
row.names(DBD_Jabar) <- DBD_Jabar$NAME_2
## Warning: Setting row names on a tibble is deprecated.
DBD_Jabar$NAME_2[duplicated(DBD_Jabar$NAME_2)]
## character(0)
W <- poly2nb(DBD_Jabar, queen = TRUE)
if(any(card(W) == 0)) {
coords <- st_coordinates(st_centroid(st_geometry(DBD_Jabar)))
W <- knn2nb(knearneigh(coords, k = 1))
}
WL <- nb2listw(W, style = "W", zero.policy = TRUE)
# GLOBAL MORAN'S I
# Jumlah Kasus DBD
kasus_dbd <- as.numeric(DBD_Jabar$`Jumlah DBD`)
Global_Moran_Kasus <- moran.test(kasus_dbd, WL, zero.policy = TRUE)
Moran_MC_Kasus <- moran.mc(kasus_dbd, WL, nsim = 999, zero.policy = TRUE)
# Prevalensi DBD
prev_dbd <- as.numeric(DBD_Jabar$Prevalensi_persen)
Global_Moran_Prev <- moran.test(prev_dbd, WL, zero.policy = TRUE)
Moran_MC_Prev <- moran.mc(prev_dbd, WL, nsim = 999, zero.policy = TRUE)
# Tabel ringkasan hasil Moran’s I
hasil_moran <- data.frame(
Variabel = c("Jumlah Kasus DBD", "Prevalensi DBD (%)"),
Moran_I = c(
round(Global_Moran_Kasus$estimate[1], 4),
round(Global_Moran_Prev$estimate[1], 4)
),
P_value_Asymptotic = c(
round(Global_Moran_Kasus$p.value, 4),
round(Global_Moran_Prev$p.value, 4)
),
P_value_MC = c(
round(Moran_MC_Kasus$p.value, 4),
round(Moran_MC_Prev$p.value, 4)
)
)
print(hasil_moran)
## Variabel Moran_I P_value_Asymptotic P_value_MC
## 1 Jumlah Kasus DBD 0.1993 0.0371 0.052
## 2 Prevalensi DBD (%) -0.2235 0.9106 0.917
# GEARY'S C (GLOBAL) UNTUK KASUS & PREVALENSI
# Kasus DBD
geary_kasus <- geary.test(
kasus_dbd, WL, zero.policy = TRUE
)
print(geary_kasus)
##
## Geary C test under randomisation
##
## data: kasus_dbd
## weights: WL
##
## Geary C statistic standard deviate = 1.7894, p-value = 0.03678
## alternative hypothesis: Expectation greater than statistic
## sample estimates:
## Geary C statistic Expectation Variance
## 0.65864052 1.00000000 0.03639414
# Prevalensi
geary_prev <- geary.test(prev_dbd, WL, zero.policy = TRUE
)
print(geary_prev)
##
## Geary C test under randomisation
##
## data: prev_dbd
## weights: WL
##
## Geary C statistic standard deviate = -0.22086, p-value = 0.5874
## alternative hypothesis: Expectation greater than statistic
## sample estimates:
## Geary C statistic Expectation Variance
## 1.04056625 1.00000000 0.03373475
# LOCAL MORAN’S I (LISA)
Local_Moran <- localmoran(prev_dbd, WL, zero.policy = TRUE)
mean_prev <- mean(prev_dbd, na.rm = TRUE)
Jabar_LISA <- DBD_Jabar %>%
mutate(
Local_I = Local_Moran[, 1],
E_Ii = Local_Moran[, 2],
Var_Ii = Local_Moran[, 3],
Z_Ii = Local_Moran[, 4],
P_value = Local_Moran[, 5],
Quadrant = case_when(
P_value >= 0.05 ~ "Not Significant",
Prevalensi_persen >= mean_prev & Local_I > 0 ~ "High-High",
Prevalensi_persen < mean_prev & Local_I > 0 ~ "Low-Low",
Prevalensi_persen >= mean_prev & Local_I < 0 ~ "High-Low",
Prevalensi_persen < mean_prev & Local_I < 0 ~ "Low-High",
TRUE ~ "Not Significant"
)
)
# VISUALISASI: LISA MAP
ggplot() +
geom_sf(data = Jabar_LISA, aes(fill = Quadrant), color = "white", size = 0.3) +
scale_fill_manual(
values = c(
"High-High" = "#d7191c", # Merah
"Low-Low" = "#2c7bb6", # Biru tua
"High-Low" = "#fdae61", # Oranye
"Low-High" = "#abd9e9", # Biru muda
"Not Significant" = "#ffffbf" # Kuning
),
name = "Cluster Type"
) +
labs(
title = "LISA Map Prevalensi DBD Jawa Barat",
subtitle = paste0("Mean Prevalensi: ", round(mean_prev, 2), "%")
) +
theme_minimal() +
theme(
plot.title = element_text(face = "bold", size = 14, hjust = 0.5),
plot.subtitle = element_text(size = 11, hjust = 0.5),
legend.position = "right"
)
data_dbd <- data_dbd %>%
mutate(
Prevalensi_persen = (`Jumlah DBD` / `Jumlah Penduduk`) * 100,
CFR_persen = (`Kasus Meninggal` / `Jumlah DBD`) * 100
)
# --------------------------
# 4. Fungsi normalisasi 0–100
# --------------------------
norm_0_100 <- function(x){
x_num <- as.numeric(unlist(x))
mx <- max(x_num, na.rm = TRUE)
if(is.na(mx) || mx == 0) return(rep(0,length(x_num)))
(x_num / mx) * 100
}
# --------------------------
# 5. Bobot indikator (kamu bisa ubah sesuai kebijakan analisis)
# --------------------------
w_prev <- 0.25
w_cfr <- 0.20
w_cases <- 0.20
w_banjir <- 0.15
w_sanit <- 0.10
w_rumah <- 0.10
# --------------------------
# 6. Hitung skor komposit risiko wilayah
# --------------------------
data_dbd <- data_dbd %>%
mutate(
# variabel dengan arah risiko terbalik
raw_sanit = 100 - `% Akses Sanitasi`,
raw_rumah = 100 - `% Rumah Layak Huni`,
Skor_Prev = norm_0_100(Prevalensi_persen),
Skor_CFR = norm_0_100(CFR_persen),
Skor_Kasus = norm_0_100(`Jumlah DBD`),
Skor_Banjir = norm_0_100(`Jumlah Kejadian Banjir`),
Skor_Sanitasi = norm_0_100(raw_sanit),
Skor_Rumah = norm_0_100(raw_rumah),
Skor_Total = (Skor_Prev * w_prev) +
(Skor_CFR * w_cfr) +
(Skor_Kasus * w_cases) +
(Skor_Banjir * w_banjir) +
(Skor_Sanitasi * w_sanit) +
(Skor_Rumah * w_rumah)
)
# --------------------------
# 7. Ranking & kategori prioritas
# --------------------------
q <- quantile(data_dbd$Skor_Total, probs = c(0.25, 0.5, 0.75), na.rm = TRUE)
data_dbd <- data_dbd %>%
mutate(
Kategori = case_when(
Skor_Total >= q[3] ~ "PRIORITAS TINGGI",
Skor_Total >= q[2] ~ "PRIORITAS SEDANG",
Skor_Total >= q[1] ~ "PRIORITAS RENDAH",
TRUE ~ "PRIORITAS MINIMAL"
),
Rank = rank(-Skor_Total, ties.method = "min")
) %>%
arrange(Rank)
# --------------------------
# 8. Plot Top 15 Wilayah Prioritas
# --------------------------
library(ggplot2)
library(dplyr)
data_dbd_top <- data_dbd %>%
slice_max(order_by = Skor_Total, n = 15) # ambil 15 tertinggi
ggplot(data_dbd_top, aes(
x = reorder(`KABUPATEN/KOTA (nama wilayah)`, Skor_Total),
y = Skor_Total,
fill = Kategori
)) +
geom_col() +
coord_flip() +
geom_text(aes(label = round(Skor_Total, 1)), hjust = -0.2) +
scale_fill_manual(values = c(
"PRIORITAS TINGGI" = "#dc2626",
"PRIORITAS SEDANG" = "#f59e0b",
"PRIORITAS RENDAH" = "#10b981",
"PRIORITAS MINIMAL" = "#9ca3af"
)) +
theme_minimal() +
labs(
title = "Top 15 Wilayah Prioritas Penanganan DBD di Jawa Barat",
subtitle = "Skor komposit berbasis Prevalensi (20%), CFR (15%), Kasus (25%), Banjir (15%), Sanitasi (15%), Rumah Layak Huni (10%)",
x = "Wilayah",
y = "Skor Total"
) +
theme(
plot.title = element_text(face = "bold"),
axis.text.y = element_text(size = 17),
axis.title = element_text(face = "bold"),
legend.title = element_blank()
)
https://drive.google.com/drive/folders/10drHq5D6ksRT1ZpR5EA-xfX46n3OBr72?usp=sharing
$# app.R - Dashboard DBD Lengkap dengan Autokorelasi Spasial & Wilayah Prioritas suppressPackageStartupMessages({ library(shiny) library(shinydashboard) library(DT) library(dplyr) library(ggplot2) library(readr) library(readxl) library(stringr) library(scales) library(bslib) library(tidyr) library(leaflet) library(sf) library(geodata) library(plotly) library(corrplot) library(spdep) # untuk analisis spasial })
DATA_FILE <- “data/Data - Sheet1 (1).csv” DATA_TREN_FILE <- “data/Data DBD tahunan.csv”
safe_num <- function(x) { if (is.numeric(x)) return(as.numeric(x)) x2 <- as.character(x) x2 <- str_replace_all(x2, “\s+”, ““) x2 <- str_replace_all(x2,”\.”, ““) x2 <- str_replace_all(x2,”,“,”.”) x2 <- str_replace_all(x2, “[^0-9eE+\\-\\.]”, ““) suppressWarnings(as.numeric(x2)) }
find_column <- function(cols, keywords) { cols_l <- tolower(cols) for (kw in keywords) { hits <- which(str_detect(cols_l, fixed(tolower(kw)))) if (length(hits) > 0) return(cols[hits[1]]) } for (kw in keywords) { hits <- which(str_detect(cols_l, kw)) if (length(hits) > 0) return(cols[hits[1]]) } NA_character_ }
keywords_map <- list( wilayah = c(“wilayah”,“kabupaten”,“kota”,“region”,“nama”,“area”,“district”), cases = c(“jumlah_dbd”,“jumlah dbd”,“dbd”,“kasus”,“cases”,“jumlah_kasus”,“jumlah kasus”), pop = c(“jumlah_penduduk”,“penduduk”,“populasi”,“population”,“jumlahpenduduk”), death = c(“kasus_meninggal”,“meninggal”,“death”,“mati”), banjir = c(“banjir”,“kejadian_banjir”,“jumlah_banjir”), sanit = c(“sanit”,“sanitasi”,“akses_sanitasi”,“persen_sanitasi”), rumah = c(“rumah”,“layak”,“rumah_layak”,“persen_rumah”,“rumah_layak_huni”,“persen_rumah_layak_huni”) )
load_and_process_data <- function(filepath) { ext <- tolower(tools::file_ext(filepath)) df <- tryCatch({ if (ext %in% c(“xls”,“xlsx”)) { read_excel(filepath) } else { read_csv(filepath, show_col_types = FALSE) } }, error = function(e) { stop(paste(“Gagal membaca file:”, e$message)) })
names(df) <- str_trim(names(df)) cols <- names(df)
col_wil <- find_column(cols, keywords_map\(wilayah) col_cases <- find_column(cols, keywords_map\)cases) col_pop <- find_column(cols, keywords_map\(pop) col_death <- find_column(cols, keywords_map\)death) col_banjir <- find_column(cols, keywords_map\(banjir) col_sanit <- find_column(cols, keywords_map\)sanit) col_rumah <- find_column(cols, keywords_map$rumah)
df2 <- df %>% mutate(across(everything(), ~ if(is.character(.)) str_trim(.) else .))
df2\(Wilayah <- if (!is.na(col_wil)) as.character(df2[[col_wil]]) else paste0("R", seq_len(nrow(df2))) df2\)Jumlah_DBD <- if (!is.na(col_cases)) safe_num(df2[[col_cases]]) else NA_real_ df2\(Jumlah_Penduduk <- if (!is.na(col_pop)) safe_num(df2[[col_pop]]) else NA_real_ df2\)Kasus_Meninggal <- if (!is.na(col_death)) safe_num(df2[[col_death]]) else NA_real_ df2\(Jumlah_Kejadian_Banjir <- if (!is.na(col_banjir)) safe_num(df2[[col_banjir]]) else NA_real_ df2\)Persen_Akses_Sanitasi <- if (!is.na(col_sanit)) safe_num(df2[[col_sanit]]) else NA_real_ df2$Persen_Rumah_Layak_Huni <- if (!is.na(col_rumah)) safe_num(df2[[col_rumah]]) else NA_real_
df2 <- df2 %>% mutate( Prevalensi_persen = ifelse(!is.na(Jumlah_DBD) & !is.na(Jumlah_Penduduk) & Jumlah_Penduduk>0, round((Jumlah_DBD / Jumlah_Penduduk) * 100, 3), NA_real_), CaseFatality_pct = ifelse(!is.na(Kasus_Meninggal) & !is.na(Jumlah_DBD) & Jumlah_DBD>0, round((Kasus_Meninggal / Jumlah_DBD) * 100, 4), NA_real_) )
return(df2) }
data_tren <- tryCatch({ ext <- tolower(tools::file_ext(DATA_TREN_FILE)) if (ext %in% c(“xls”, “xlsx”)) { read_excel(DATA_TREN_FILE) } else { read_csv(DATA_TREN_FILE, show_col_types = FALSE) } }, error = function(e) { NULL })
if(!is.null(data_tren)) { names(data_tren)[1] <- “Wilayah” data_tren\(Wilayah <- str_trim(data_tren\)Wilayah) year_cols <- names(data_tren)[names(data_tren) != “Wilayah”] for(col in year_cols) { data_tren[[col]] <- safe_num(data_tren[[col]]) } }
theme_bs <- bs_theme( bootswatch = “minty”, bg = “#FAFAFA”, fg = “#0f3d3e”, primary = “#2C7873” )
ui <- dashboardPage( skin = “black”, dashboardHeader(title = span(“Dashboard Analisis DBD”, style = “font-weight:700;”)),
dashboardSidebar( width = 300, sidebarMenu( id = “tabs”, menuItem(“Data”, tabName = “data”, icon = icon(“table”)), menuItem(“Statistika Deskriptif”, tabName = “statdes”, icon = icon(“chart-bar”)), menuItem(“Korelasi Variabel”, tabName = “korelasi”, icon = icon(“project-diagram”)), menuItem(“Ukuran Frekuensi”, tabName = “freq”, icon = icon(“percent”)), menuItem(“Interpretasi Epidemiologi”, tabName = “interp”, icon = icon(“stethoscope”)), menuItem(“Peta Visualisasi”, tabName = “peta”, icon = icon(“globe”)), menuItem(“Autokorelasi Spasial”, tabName = “spatial”, icon = icon(“connectdevelop”)), menuItem(“Heatmap”, tabName = “heatmap”, icon = icon(“fire”)), menuItem(“Tren Waktu”, tabName = “tren”, icon = icon(“line-chart”)), menuItem(“Wilayah Prioritas”, tabName = “prioritas”, icon = icon(“exclamation-triangle”)), menuItem(“Tentang Dashboard”, tabName = “about”, icon = icon(“info-circle”)) ) ),
dashboardBody( theme = theme_bs, tags\(head(tags\)style(HTML(” body, .content-wrapper, .right-side { background-color: #FAFAFA; } .box { border-radius: 10px; border: 1px solid #ECECEC; box-shadow: 0 2px 6px rgba(0,0,0,0.04); } .box .box-title { color: #115e59; font-weight: 700; } .value-box { border-radius: 8px; } .interpretation-box { background: #f0f9ff; border-left: 4px solid #2C7873; padding: 15px; margin: 10px 0; border-radius: 5px; } .highlight-value { color: #dc2626; font-weight: bold; font-size: 1.1em; } .priority-high { background: #fee2e2; border-left: 4px solid #dc2626; } .priority-medium { background: #fef3c7; border-left: 4px solid #f59e0b; } .priority-low { background: #d1fae5; border-left: 4px solid #10b981; } “))),
tabItems(
# TAB DATA
tabItem(tabName = "data",
fluidRow(
valueBoxOutput("vb_total_wilayah", width = 3),
valueBoxOutput("vb_total_penduduk", width = 3),
valueBoxOutput("vb_total_kasus", width = 3),
valueBoxOutput("vb_total_meninggal", width = 3),
valueBoxOutput("vb_banjir", width = 3),
valueBoxOutput("vb_sanitasi", width = 3),
valueBoxOutput("vb_rumah_layak", width = 3),
),
fluidRow(
box(title = "Data Lengkap", status = "info", solidHeader = TRUE, width = 12,
DTOutput("table_all")
)
)
),
# TAB STATISTIKA DESKRIPTIF
tabItem(tabName = "statdes",
fluidRow(
box(title = "Statistika Deskriptif", status = "primary", solidHeader = TRUE, width = 12,
DTOutput("statdesc_tbl")
)
),
fluidRow(
box(title = "Pengaturan Visualisasi", status = "success", solidHeader = TRUE, width = 4,
uiOutput("select_var_ui"),
radioButtons("viz_type", "Jenis grafik", choices = c("Histogram","Boxplot")),
numericInput("hist_bins", "Bins (histogram):", value = 10, min = 3)
),
box(title = "Plot Interaktif", status = "info", solidHeader = TRUE, width = 8,
plotlyOutput("var_plot", height = "420px")
)
)
),
# TAB KORELASI
tabItem(tabName = "korelasi",
fluidRow(
box(title = "Matrix Korelasi Variabel Epidemiologi DBD", status = "primary",
solidHeader = TRUE, width = 12,
plotOutput("correlation_plot", height = "600px")
)
),
fluidRow(
box(title = "Tabel Nilai Korelasi", status = "info", solidHeader = TRUE, width = 12,
DTOutput("correlation_table")
)
),
fluidRow(
box(title = "Interpretasi Korelasi", status = "warning", solidHeader = TRUE, width = 12,
htmlOutput("correlation_interpretation")
)
)
),
# TAB UKURAN FREKUENSI
tabItem(tabName = "freq",
fluidRow(
box(title = "Tabel Ukuran Frekuensi", status = "info", solidHeader = TRUE, width = 12,
DTOutput("freq_table")
)
),
fluidRow(
box(title = "Top 10 Prevalensi", status = "success", solidHeader = TRUE, width = 12,
DTOutput("top10_prev")
)
)
),
# TAB INTERPRETASI EPIDEMIOLOGI
tabItem(tabName = "interp",
fluidRow(
box(title = "Grafik Perbandingan", status = "info", solidHeader = TRUE, width = 12,
selectInput("epi_var_plot", "Pilih Ukuran:",
choices = c("Prevalensi (%)" = "Prevalensi_persen",
"Case Fatality Rate (%)" = "CaseFatality_pct")),
plotOutput("epi_comparison_plot", height = "500px")
)
),
fluidRow(
box(title = "Interpretasi Epidemiologi", status = "warning", solidHeader = TRUE, width = 12,
htmlOutput("epi_interpretation")
)
),
fluidRow(
box(title = "Rekomendasi Intervensi", status = "success", solidHeader = TRUE, width = 12,
htmlOutput("epi_recommendations")
)
)
),
# TAB PETA
tabItem(tabName = "peta",
fluidRow(
box(title = "Pengaturan Peta", width = 4, status = "primary", solidHeader = TRUE,
uiOutput("map_var_ui"),
selectInput("color_low", "Warna rendah:",
choices = c("yellow", "lightgreen", "lightblue", "white"), selected = "yellow"),
selectInput("color_high", "Warna tinggi:",
choices = c("darkgreen", "red", "blue", "purple"), selected = "red"),
actionButton("btn_load_map", "Muat Peta", icon = icon("map"), class = "btn btn-success"),
tags$hr(),
htmlOutput("map_info")
),
box(title = "Peta Jawa Barat", width = 8, status = "info", solidHeader = TRUE,
leafletOutput("map_leaflet", height = "600px")
)
),
fluidRow(
box(title = "Interpretasi Pola Spasial", width = 12, status = "warning", solidHeader = TRUE,
htmlOutput("spatial_interpretation")
)
)
),
# TAB AUTOKORELASI SPASIAL
tabItem(tabName = "spatial",
fluidRow(
box(title = "📊 Hasil Analisis Moran's I (Global)",
status = "primary", solidHeader = TRUE, width = 12,
DTOutput("moran_table")
)
),
fluidRow(
box(title = "🗺️ LISA Map (Local Indicators of Spatial Association)",
status = "info", solidHeader = TRUE, width = 12,
plotOutput("lisa_map", height = "600px")
)
),
fluidRow(
box(title = "📋 Tabel Hasil LISA per Wilayah",
status = "success", solidHeader = TRUE, width = 12,
DTOutput("lisa_table")
)
),
fluidRow(
box(title = "💡 Interpretasi LISA Clusters",
status = "warning", solidHeader = TRUE, width = 12,
htmlOutput("lisa_interpretation")
)
)
),
# TAB HEATMAP BARU
tabItem(tabName = "heatmap",
fluidRow(
box(title = "🔥 Heatmap Distribusi Kasus DBD", status = "danger", solidHeader = TRUE, width = 12,
htmlOutput("heatmap_info"))
),
fluidRow(
box(title = "⚙️ Pengaturan Heatmap", status = "primary", solidHeader = TRUE, width = 4,
selectInput("heatmap_color_low", "Warna rendah:",
choices = c("Kuning Muda" = "lightyellow",
"Putih" = "white",
"Hijau Muda" = "lightgreen",
"Biru Muda" = "lightblue"),
selected = "lightyellow"),
selectInput("heatmap_color_high", "Warna tinggi:",
choices = c("Merah" = "red",
"Merah Tua" = "darkred",
"Oranye" = "orange",
"Ungu" = "purple"),
selected = "red"),
checkboxInput("heatmap_show_values", "Tampilkan angka di tile", value = FALSE),
numericInput("heatmap_top_n", "Tampilkan top N wilayah:",
value = 27, min = 5, max = 50, step = 1),
actionButton("btn_update_heatmap", "🔄 Update Heatmap", class = "btn btn-success")
),
box(title = "🔥 Heatmap Kasus DBD Jawa Barat", status = "danger", solidHeader = TRUE, width = 8,
plotOutput("heatmap_plot", height = "700px"))
),
fluidRow(
box(title = "📊 Interpretasi Heatmap", status = "warning", solidHeader = TRUE, width = 12,
htmlOutput("heatmap_interpretation"))
)
),
# TAB TREN WAKTU
tabItem(tabName = "tren",
fluidRow(
box(title = "Analisis Tren", width = 12, status = "primary", solidHeader = TRUE,
htmlOutput("tren_info")
)
),
fluidRow(
box(title = "Pilih Wilayah", width = 4, status = "info", solidHeader = TRUE,
uiOutput("tren_wilayah_ui"),
checkboxInput("tren_show_avg", "Tampilkan rata-rata provinsi", value = TRUE),
checkboxInput("tren_show_trend", "Tampilkan garis tren", value = TRUE)
),
box(title = "Grafik Tren", width = 8, status = "success", solidHeader = TRUE,
plotOutput("tren_plot", height = "450px")
)
),
fluidRow(
box(title = "Tabel Data Tren", width = 12, status = "info", solidHeader = TRUE,
DTOutput("tren_table")
)
),
fluidRow(
box(title = "Interpretasi Tren", width = 12, status = "warning", solidHeader = TRUE,
htmlOutput("tren_interpretation")
)
)
),
# TAB WILAYAH PRIORITAS
tabItem(tabName = "prioritas",
fluidRow(
box(title = "Prioritas Berdasarkan Skor Komposit", status = "danger",
solidHeader = TRUE, width = 12,
htmlOutput("prioritas_summary")
)
),
fluidRow(
box(title = "Tabel Wilayah Prioritas", status = "primary", solidHeader = TRUE, width = 12,
DTOutput("prioritas_table")
)
)
),
# TAB TENTANG DASHBOARD (tambahkan ini)
tabItem(tabName = "about",
fluidRow(
box(title = "📊 Tentang Dashboard Analisis DBD",
status = "primary", solidHeader = TRUE, width = 12,
HTML("
<div style='font-size:15px; line-height:1.8;'>
<h3 style='color:#0f3d3e; margin-top:0;'>Selamat Datang di Dashboard Analisis DBD Jawa Barat</h3>
<p>Dashboard interaktif ini dirancang untuk membantu analisis epidemiologi
Demam Berdarah Dengue (DBD) di Jawa Barat dengan pendekatan data-driven dan visualisasi spasial.</p>
<hr style='border-top: 2px solid #2C7873; margin:20px 0;'>
<h4 style='color:#2C7873;'>🎯 Tujuan Dashboard</h4>
<ul style='margin-left:20px;'>
<li>Memvisualisasikan distribusi spasial kasus DBD di Jawa Barat</li>
<li>Menganalisis faktor risiko lingkungan (banjir, sanitasi, perumahan)</li>
<li>Mengidentifikasi wilayah prioritas intervensi</li>
<li>Mendeteksi cluster spasial dengan metode LISA (Local Indicators of Spatial Association)</li>
<li>Memantau tren temporal kasus DBD</li>
</ul>
<hr style='border-top: 1px solid #ddd; margin:20px 0;'>
<h4 style='color:#2C7873;'>📌 Fitur Utama</h4>
<div style='display:grid; grid-template-columns: repeat(2, 1fr); gap:15px; margin-top:15px;'>
<div style='background:#e0f2fe; padding:15px; border-radius:8px; border-left:4px solid #0284c7;'>
<strong>📊 Statistika Deskriptif</strong><br>
<small>Ringkasan statistik (mean, median, SD, skewness, kurtosis) untuk semua variabel numerik</small>
</div>
<div style='background:#fef3c7; padding:15px; border-radius:8px; border-left:4px solid #f59e0b;'>
<strong>🔗 Analisis Korelasi</strong><br>
<small>Heatmap korelasi Pearson antar variabel dengan interpretasi epidemiologi</small>
</div>
<div style='background:#fee2e2; padding:15px; border-radius:8px; border-left:4px solid #dc2626;'>
<strong>📈 Ukuran Frekuensi</strong><br>
<small>Prevalensi (per 100 & per 100.000 penduduk), Case Fatality Rate (CFR)</small>
</div>
<div style='background:#d1fae5; padding:15px; border-radius:8px; border-left:4px solid #10b981;'>
<strong>🗺 Peta Choropleth</strong><br>
<small>Visualisasi spasial interaktif dengan Leaflet (zoom, hover info)</small>
</div>
<div style='background:#e9d5ff; padding:15px; border-radius:8px; border-left:4px solid #7c3aed;'>
<strong>🌐 Autokorelasi Spasial</strong><br>
<small>Moran's I Global, Geary's C, dan LISA Map untuk deteksi cluster</small>
</div>
<div style='background:#fed7aa; padding:15px; border-radius:8px; border-left:4px solid #ea580c;'>
<strong>🔥 Heatmap Temporal</strong><br>
<small>Pola musiman kasus DBD per wilayah dari waktu ke waktu</small>
</div>
<div style='background:#fecaca; padding:15px; border-radius:8px; border-left:4px solid #b91c1c;'>
<strong>🎯 Prioritas Wilayah</strong><br>
<small>Ranking wilayah berdasarkan skor komposit (prevalensi + CFR + faktor risiko)</small>
</div>
<div style='background:#dbeafe; padding:15px; border-radius:8px; border-left:4px solid #1d4ed8;'>
<strong>📉 Analisis Tren</strong><br>
<small>Regresi linear temporal untuk deteksi tren naik/turun</small>
</div>
</div>
<hr style='border-top: 1px solid #ddd; margin:20px 0;'>
<h4 style='color:#2C7873;'>🔬 Metodologi</h4>
<table style='width:100%; border-collapse:collapse; margin-top:10px;'>
<tr style='background:#f3f4f6;'>
<th style='padding:10px; text-align:left; border:1px solid #ddd;'>Metode</th>
<th style='padding:10px; text-align:left; border:1px solid #ddd;'>Kegunaan</th>
</tr>
<tr>
<td style='padding:10px; border:1px solid #ddd;'><strong>Moran's I</strong></td>
<td style='padding:10px; border:1px solid #ddd;'>Mendeteksi autokorelasi spasial global (clustering vs dispersed)</td>
</tr>
<tr style='background:#f9fafb;'>
<td style='padding:10px; border:1px solid #ddd;'><strong>LISA (Local Moran's I)</strong></td>
<td style='padding:10px; border:1px solid #ddd;'>Identifikasi hotspot lokal (High-High, Low-Low, outlier)</td>
</tr>
<tr>
<td style='padding:10px; border:1px solid #ddd;'><strong>Geary's C</strong></td>
<td style='padding:10px; border:1px solid #ddd;'>Validasi autokorelasi dengan pendekatan alternatif</td>
</tr>
<tr style='background:#f9fafb;'>
<td style='padding:10px; border:1px solid #ddd;'><strong>Skor Prioritas Komposit</strong></td>
<td style='padding:10px; border:1px solid #ddd;'>Ranking wilayah (bobot: Prevalensi 25%, CFR 20%, Kasus 20%, Banjir 15%, Sanitasi 10%, Rumah 10%)</td>
</tr>
</table>
<hr style='border-top: 1px solid #ddd; margin:20px 0;'>
<hr style='border-top: 1px solid #ddd; margin:20px 0;'>
<hr style='border-top: 2px solid #2C7873; margin:20px 0;'>
</div>
<hr style='border-top: 1px solid #ddd; margin:20px 0;'>
<div style='text-align:center; margin-top:30px; padding:20px; background:#e0f2fe; border-radius:8px;'>
<p style='font-size:14px; color:#334155;'>
<strong>Nama Mahasiswa:</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>Instansi:</strong> Universitas Padjadjaran
</p>
</div>
</div>
")
)
)
)
)
) )
server <- function(input, output, session) {
std_df <- reactiveVal(NULL) jabar_shp <- reactiveVal(NULL) tren_df <- reactiveVal(NULL)
observe({ tryCatch({ if (file.exists(DATA_FILE)) { df <- load_and_process_data(DATA_FILE) std_df(df) showNotification(“Data berhasil dimuat!”, type = “message”) } }, error = function(e) { showNotification(paste(“Error:”, e$message), type = “error”) }) })
observe({ if(exists(“data_tren”) && !is.null(data_tren)) { tren_df(data_tren) } })
# Value boxes output\(vb_total_wilayah <- renderValueBox({ df <- std_df() if (is.null(df)) return(valueBox("—", "Jumlah Wilayah", icon = icon("map-marker-alt"), color = "olive")) valueBox(length(unique(df\)Wilayah)), “Jumlah Wilayah”, icon = icon(“map-marker-alt”), color = “olive”) })
output\(vb_total_penduduk <- renderValueBox({ df <- std_df() if (is.null(df)) return(valueBox("—", "Total Penduduk", icon = icon("users"), color = "teal")) valueBox(comma(sum(df\)Jumlah_Penduduk, na.rm = TRUE)), “Total Penduduk”, icon = icon(“users”), color = “teal”) })
output\(vb_total_kasus <- renderValueBox({ df <- std_df() if (is.null(df)) return(valueBox("—", "Total Kasus", icon = icon("virus"), color = "maroon")) valueBox(comma(sum(df\)Jumlah_DBD, na.rm = TRUE)), “Total Kasus DBD”, icon = icon(“virus”), color = “maroon”) })
output\(vb_total_meninggal <- renderValueBox({ df <- std_df() if (is.null(df)) return(valueBox("—", "Total Meninggal", icon = icon("heartbeat"), color = "red")) valueBox(comma(sum(df\)Kasus_Meninggal, na.rm = TRUE)), “Total Meninggal”, icon = icon(“heartbeat”), color = “red”) })
# Data table output$table_all <- renderDT({ df <- std_df() if (is.null(df)) return(datatable(data.frame(Message = “Silakan upload data terlebih dahulu”), options = list(dom = ‘t’))) std_cols <- c(“Wilayah”,“Tahun”,“Jumlah_DBD”,“Jumlah_Penduduk”,“Kasus_Meninggal”, “Jumlah_Kejadian_Banjir”,“Persen_Akses_Sanitasi”,“Persen_Rumah_Layak_Huni”) show_cols <- intersect(names(df), std_cols) datatable(df %>% select(all_of(show_cols)), options = list(pageLength = 10, scrollX = TRUE)) })
output$statdesc_tbl <- renderDT({ df <- std_df() if (is.null(df)) return(datatable(data.frame(Message=“Memuat data…”), options = list(dom=‘t’)))
num_df <- df %>% select(where(is.numeric))
freq_cols <- c("Prevalensi_persen", "CaseFatality_pct")
num_df <- num_df[, !(names(num_df) %in% freq_cols), drop = FALSE]
clean_names <- names(num_df)
clean_names <- gsub("%", "Persen_", clean_names, fixed = TRUE)
clean_names <- gsub("\\s+", "_", clean_names)
clean_names <- gsub("[^[:alnum:]_]", "", clean_names)
clean_names <- gsub("_+", "", clean_names)
clean_names <- tolower(clean_names)
names(num_df) <- clean_names
num_df <- num_df[, !duplicated(names(num_df)), drop = FALSE]
if (ncol(num_df) == 0) return(datatable(data.frame(Message="Tidak ada variabel numerik"), options = list(dom='t')))
get_mode <- function(x) {
x2 <- x[!is.na(x)]
if (length(x2) == 0) return(NA_real_)
ux <- unique(x2)
tab <- tabulate(match(x2, ux))
ux[which.max(tab)]
}
skewness <- function(x) {
x <- x[!is.na(x)]
n <- length(x)
if (n < 3) return(NA_real_)
m <- mean(x)
s <- sd(x)
if (s == 0) return(0)
sum((x - m)^3) / (n * s^3)
}
kurtosis <- function(x) {
x <- x[!is.na(x)]
n <- length(x)
if (n < 4) return(NA_real_)
m <- mean(x)
s <- sd(x)
if (s == 0) return(0)
sum((x - m)^4) / (n * s^4) - 3
}
stat <- tibble(
Variabel = names(num_df),
N = sapply(num_df, function(x) sum(!is.na(x))),
Mean = sapply(num_df, function(x) round(mean(x, na.rm = TRUE), 3)),
Median = sapply(num_df, function(x) round(median(x, na.rm = TRUE), 3)),
Mode = sapply(num_df, function(x) {
m <- get_mode(x)
if (is.na(m)) NA else round(m, 3)
}),
SD = sapply(num_df, function(x) round(sd(x, na.rm = TRUE), 3)),
Variance = sapply(num_df, function(x) round(var(x, na.rm = TRUE), 3)),
Min = sapply(num_df, function(x) if (all(is.na(x))) NA else round(min(x, na.rm = TRUE), 3)),
Q1 = sapply(num_df, function(x) if (all(is.na(x))) NA else round(quantile(x, probs = 0.25, na.rm = TRUE, type = 7), 3)),
Q3 = sapply(num_df, function(x) if (all(is.na(x))) NA else round(quantile(x, probs = 0.75, na.rm = TRUE, type = 7), 3)),
Max = sapply(num_df, function(x) if (all(is.na(x))) NA else round(max(x, na.rm = TRUE), 3)),
IQR = sapply(num_df, function(x) {
if (all(is.na(x))) return(NA_real_)
q <- quantile(x, probs = c(0.25, 0.75), na.rm = TRUE, type = 7)
round(as.numeric(q[2] - q[1]), 3)
}),
Range = sapply(num_df, function(x) {
if (all(is.na(x))) return(NA_real_)
round(max(x, na.rm = TRUE) - min(x, na.rm = TRUE), 3)
}),
CV = sapply(num_df, function(x) {
m <- mean(x, na.rm = TRUE)
s <- sd(x, na.rm = TRUE)
if (is.na(m) || m == 0) return(NA_real_)
round(s / m, 3)
}),
Skewness = sapply(num_df, function(x) round(skewness(x), 3)),
Kurtosis = sapply(num_df, function(x) round(kurtosis(x), 3)),
NA_Count = sapply(num_df, function(x) sum(is.na(x))),
Pct_NA = sapply(num_df, function(x) round(mean(is.na(x)) * 100, 2))
)
stat$Variabel <- sapply(stat$Variabel, function(nm) {
nm2 <- gsub("_", " ", nm)
s <- strsplit(nm2, " ")[[1]]
paste(toupper(substring(s,1,1)), substring(s,2), sep = "", collapse = " ")
})
datatable(stat, options = list(pageLength = 15, scrollX = TRUE))
})
# Variable selector output\(select_var_ui <- renderUI({ df <- std_df() if (is.null(df)) return(tags\)div(“Memuat data…”)) num_vars <- names(df %>% select(where(is.numeric))) freq_cols <- c(“Prevalensi_persen”, “CaseFatality_pct”) num_vars <- setdiff(num_vars, freq_cols) num_vars <- num_vars[!duplicated(num_vars)] if (length(num_vars) == 0) return(tags$div(“Tidak ada variabel numerik”)) selectInput(“var_to_plot”, “Variabel numerik:”, choices = num_vars, selected = num_vars[1]) })
# Plot INTERAKTIF dengan HOVER INFO output\(var_plot <- renderPlotly({ df <- std_df(); req(df) var <- input\)var_to_plot req(var)
if (input$viz_type == "Histogram") {
p <- ggplot(df, aes(x = .data[[var]])) +
geom_histogram(fill = "#8fd3c7", color = "white", bins = max(3, input$hist_bins), na.rm = TRUE) +
theme_minimal(base_size = 13) +
labs(x = var, y = "Frekuensi", title = paste("Histogram -", var))
ggplotly(p)
} else {
# Boxplot dengan informasi wilayah
p <- ggplot(df, aes(y = .data[[var]], text = Wilayah)) +
geom_boxplot(fill = "#cfeee6", color = "#0f3d3e", na.rm = TRUE, outlier.shape = NA) +
geom_jitter(aes(x = 0, text = paste0("Wilayah: ", Wilayah, "<br>Nilai: ", round(.data[[var]], 2))),
width = 0.2, alpha = 0.6, size = 2, color = "#dc2626") +
theme_minimal(base_size = 13) +
labs(y = var, title = paste("Boxplot -", var))
ggplotly(p, tooltip = "text")
}
})
# ================================================================== # KORELASI - PERBAIKAN LENGKAP # ==================================================================
# KORELASI PLOT output$correlation_plot <- renderPlot({ df <- std_df(); req(df)
cor_vars <- c("Jumlah_DBD", "Jumlah_Penduduk", "Jumlah_Kejadian_Banjir",
"Persen_Akses_Sanitasi", "Persen_Rumah_Layak_Huni", "Kasus_Meninggal")
available_vars <- intersect(cor_vars, names(df))
if(length(available_vars) < 2) {
plot.new()
text(0.5, 0.5, "Tidak cukup variabel untuk analisis korelasi", cex = 1.2)
return()
}
# Filter dan cek tipe data
cor_df <- df %>% select(all_of(available_vars))
# Pastikan semua kolom numerik
cor_df <- cor_df %>% mutate(across(everything(), as.numeric))
# Remove rows with all NA
cor_df <- cor_df[rowSums(is.na(cor_df)) < ncol(cor_df), ]
if(nrow(cor_df) < 3) {
plot.new()
text(0.5, 0.5, "Tidak cukup data (minimal 3 observasi)", cex = 1.2)
return()
}
# Hitung korelasi dengan pairwise complete
cor_matrix <- cor(cor_df, use = "pairwise.complete.obs")
# Ganti nama kolom untuk display
display_names <- c(
"Jumlah_DBD" = "Jumlah DBD",
"Jumlah_Penduduk" = "Penduduk",
"Jumlah_Kejadian_Banjir" = "Banjir",
"Persen_Akses_Sanitasi" = "Sanitasi",
"Persen_Rumah_Layak_Huni" = "Rumah Layak",
"Kasus_Meninggal" = "Meninggal"
)
rownames(cor_matrix) <- display_names[rownames(cor_matrix)]
colnames(cor_matrix) <- display_names[colnames(cor_matrix)]
corrplot(cor_matrix,
method = "color",
type = "upper",
addCoef.col = "black",
number.cex = 0.8,
tl.col = "black",
tl.srt = 45,
tl.cex = 0.9,
col = colorRampPalette(c("#2166ac", "white", "#b2182b"))(200),
title = "Matrix Korelasi Variabel Epidemiologi DBD",
mar = c(0, 0, 2, 0))
}, res = 96)
# KORELASI TABLE output$correlation_table <- renderDT({ df <- std_df(); req(df)
cor_vars <- c("Jumlah_DBD", "Jumlah_Penduduk", "Jumlah_Kejadian_Banjir",
"Persen_Akses_Sanitasi", "Persen_Rumah_Layak_Huni", "Kasus_Meninggal")
available_vars <- intersect(cor_vars, names(df))
if(length(available_vars) < 2) {
return(datatable(data.frame(Message = "Tidak cukup variabel untuk analisis korelasi"),
options = list(dom = 't')))
}
cor_df <- df %>%
select(all_of(available_vars)) %>%
mutate(across(everything(), as.numeric))
cor_df <- cor_df[rowSums(is.na(cor_df)) < ncol(cor_df), ]
if(nrow(cor_df) < 3) {
return(datatable(data.frame(Message = "Tidak cukup data"), options = list(dom = 't')))
}
cor_matrix <- cor(cor_df, use = "pairwise.complete.obs")
cor_long <- as.data.frame(as.table(cor_matrix))
names(cor_long) <- c("Variabel_1", "Variabel_2", "Korelasi")
cor_long <- cor_long %>%
filter(Variabel_1 != Variabel_2) %>%
mutate(Korelasi = round(Korelasi, 3)) %>%
arrange(desc(abs(Korelasi)))
cor_long <- cor_long[!duplicated(t(apply(cor_long[,1:2], 1, sort))), ]
datatable(cor_long, options = list(pageLength = 15, scrollX = TRUE), rownames = FALSE) %>%
formatStyle('Korelasi',
backgroundColor = styleInterval(
cuts = c(-0.7, -0.4, -0.2, 0.2, 0.4, 0.7),
values = c('#2166ac', '#67a9cf', '#d1e5f0', '#f7f7f7',
'#fddbc7', '#ef8a62', '#b2182b')
))
})
# KORELASI INTERPRETATION - PERBAIKAN output$correlation_interpretation <- renderUI({ df <- std_df(); req(df)
cor_vars <- c("Jumlah_DBD", "Jumlah_Penduduk", "Jumlah_Kejadian_Banjir",
"Persen_Akses_Sanitasi", "Persen_Rumah_Layak_Huni", "Kasus_Meninggal")
available_vars <- intersect(cor_vars, names(df))
if(length(available_vars) < 2) {
return(HTML("<div class='interpretation-box'><p>Tidak cukup variabel untuk analisis korelasi. Minimal 2 variabel numerik diperlukan.</p></div>"))
}
# Prepare data
cor_df <- df %>%
select(all_of(available_vars)) %>%
mutate(across(everything(), as.numeric))
cor_df <- cor_df[rowSums(is.na(cor_df)) < ncol(cor_df), ]
if(nrow(cor_df) < 3) {
return(HTML("<div class='interpretation-box'><p>Tidak cukup data untuk analisis korelasi. Minimal 3 observasi lengkap diperlukan.</p></div>"))
}
# Calculate correlation
cor_matrix <- cor(cor_df, use = "pairwise.complete.obs")
# Calculate p-values DENGAN ERROR HANDLING
n_vars <- ncol(cor_matrix)
p_matrix <- matrix(1, n_vars, n_vars)
rownames(p_matrix) <- colnames(p_matrix) <- colnames(cor_matrix)
for(i in 1:(n_vars-1)) {
for(j in (i+1):n_vars) {
tryCatch({
# Get complete cases for this pair
pair_data <- cor_df[, c(i, j)]
pair_complete <- pair_data[complete.cases(pair_data), ]
if(nrow(pair_complete) >= 3) {
test <- cor.test(pair_complete[[1]], pair_complete[[2]], method = "pearson")
p_matrix[i,j] <- p_matrix[j,i] <- test$p.value
}
}, error = function(e) {
# Jika error, biarkan p-value = 1 (not significant)
})
}
}
# Fungsi klasifikasi
classify_strength <- function(r) {
abs_r <- abs(r)
if(abs_r >= 0.8) return("<strong style='color:#dc2626;'>Sangat Kuat</strong>")
if(abs_r >= 0.6) return("<strong style='color:#f59e0b;'>Kuat</strong>")
if(abs_r >= 0.4) return("<strong style='color:#3b82f6;'>Sedang</strong>")
if(abs_r >= 0.2) return("<strong style='color:#6b7280;'>Lemah</strong>")
return("<strong style='color:#9ca3af;'>Sangat Lemah</strong>")
}
# Find strongest correlation
cor_matrix_no_diag <- cor_matrix
diag(cor_matrix_no_diag) <- NA
max_cor <- max(abs(cor_matrix_no_diag), na.rm = TRUE)
max_pos <- which(abs(cor_matrix_no_diag) == max_cor, arr.ind = TRUE)[1,]
var1 <- rownames(cor_matrix)[max_pos[1]]
var2 <- colnames(cor_matrix)[max_pos[2]]
cor_value <- cor_matrix[max_pos[1], max_pos[2]]
# Build interpretations
interpretations <- ""
# Helper function untuk interpretasi
add_interpretation <- function(var1_name, var2_name) {
if(!(var1_name %in% available_vars && var2_name %in% available_vars)) return("")
r <- cor_matrix[var1_name, var2_name]
if(is.na(r)) return("")
# Interpretasi teks berdasarkan pasangan variabel
interp_text <- ""
if(var1_name == "Jumlah_DBD" && var2_name == "Kasus_Meninggal") {
interp_text <- if(r > 0.7) {
"Korelasi sangat kuat dan positif. Semakin banyak kasus DBD, semakin banyak kematian. Ini <b>normal secara epidemiologi</b>, namun besarnya korelasi juga menunjukkan <b>CFR yang konsisten</b> antar wilayah."
} else if(r > 0.4) {
"Korelasi sedang-kuat. Jumlah kematian meningkat seiring kasus, namun tidak proporsional sempurna. Menunjukkan <b>variasi kualitas penanganan medis</b> antar wilayah."
} else {
"Korelasi lemah. Menunjukkan <b>CFR sangat bervariasi</b> antar wilayah - ada wilayah dengan kasus banyak tapi kematian rendah (penanganan baik) dan sebaliknya."
}
} else if(var1_name == "Jumlah_DBD" && var2_name == "Jumlah_Penduduk") {
interp_text <- if(r > 0.6) {
"Korelasi kuat positif. Wilayah berpenduduk banyak cenderung punya <b>kasus DBD lebih banyak</b>. Ini wajar karena lebih banyak penduduk = lebih banyak host potensial. Namun, untuk analisis risiko, <b>gunakan PREVALENSI</b> (bukan jumlah absolut)."
} else if(r > 0.3) {
"Korelasi sedang. Jumlah penduduk bukan satu-satunya penentu kasus DBD. Faktor lain seperti <b>kepadatan, sanitasi, dan kontrol vektor</b> juga berperan penting."
} else {
"Korelasi lemah. Jumlah kasus DBD <b>tidak ditentukan oleh jumlah penduduk</b>. Ini bagus - menunjukkan faktor lingkungan dan intervensi lebih dominan daripada ukuran populasi."
}
} else if(var1_name == "Jumlah_DBD" && var2_name == "Jumlah_Kejadian_Banjir") {
interp_text <- if(r > 0.4) {
"Korelasi positif yang cukup kuat. Wilayah dengan banjir lebih sering cenderung punya <b>kasus DBD lebih tinggi</b>. Banjir menciptakan <b>genangan air</b> yang ideal untuk breeding site nyamuk Aedes aegypti. <b>Rekomendasi:</b> intensifkan PSN paska-banjir!"
} else if(r > 0.2) {
"Korelasi lemah-sedang. Banjir berkontribusi pada peningkatan DBD, tapi <b>bukan faktor dominan</b>. Faktor lain seperti perilaku masyarakat dan sanitasi lebih berpengaruh."
} else if(r < -0.2) {
"Korelasi negatif. <b>Tidak biasa!</b> Kemungkinan: (1) data banjir tidak sinkron dengan data DBD (lag time), atau (2) banjir besar justru 'membersihkan' breeding sites."
} else {
"Tidak ada korelasi signifikan. Banjir <b>tidak berpengaruh langsung</b> pada kasus DBD di wilayah ini, atau ada lag time antara banjir dan outbreak DBD."
}
} else if(var1_name == "Jumlah_DBD" && var2_name == "Persen_Akses_Sanitasi") {
interp_text <- if(r < -0.4) {
"Korelasi negatif yang kuat. Semakin baik akses sanitasi, <b>semakin rendah kasus DBD</b>. Ini masuk akal - sanitasi baik berarti <b>pengelolaan air bersih yang baik</b>, sehingga mengurangi penampungan air yang jadi breeding site. <b>Rekomendasi:</b> tingkatkan akses sanitasi sebagai strategi preventif!"
} else if(r < -0.2) {
"Korelasi negatif lemah-sedang. Sanitasi membantu mengurangi DBD, tapi <b>efeknya terbatas</b>. Perlu dikombinasi dengan strategi lain seperti PSN dan fogging."
} else if(r > 0.2) {
"Korelasi positif. <b>Paradoks!</b> Kemungkinan: wilayah urban dengan sanitasi baik justru punya mobilitas tinggi dan kepadatan tinggi yang meningkatkan transmisi DBD. Atau, ada <b>confounding variable</b>."
} else {
"Tidak ada korelasi signifikan. Akses sanitasi <b>tidak berpengaruh langsung</b> pada kasus DBD, mungkin karena sanitasi lebih terkait infrastruktur limbah, bukan pengelolaan air bersih yang breeding site nyamuk."
}
} else if(var1_name == "Jumlah_DBD" && var2_name == "Persen_Rumah_Layak_Huni") {
interp_text <- if(r < -0.4) {
"Korelasi negatif yang kuat. Semakin banyak rumah layak huni, <b>semakin rendah kasus DBD</b>. Rumah layak huni biasanya punya <b>ventilasi baik, tidak ada genangan, dan pengelolaan sampah teratur</b> - semua mengurangi breeding site nyamuk. <b>Intervensi housing improvement efektif!</b>"
} else if(r < -0.2) {
"Korelasi negatif lemah-sedang. Kualitas rumah berpengaruh pada DBD, tapi <b>tidak dominan</b>. Faktor perilaku penghuni (PSN) lebih penting."
} else if(r > 0.2) {
"Korelasi positif. <b>Tidak biasa!</b> Kemungkinan: (1) Rumah layak huni di urban area justru punya <b>penampungan air bersih banyak</b> (bak mandi, tandon), atau (2) wilayah wealthier punya mobilitas tinggi."
} else {
"Tidak ada korelasi signifikan. Kualitas fisik rumah <b>tidak menentukan kasus DBD</b> - perilaku penghuni dan pengelolaan lingkungan lebih penting."
}
} else if(var1_name == "Persen_Akses_Sanitasi" && var2_name == "Persen_Rumah_Layak_Huni") {
interp_text <- if(r > 0.7) {
"Korelasi sangat kuat positif. Ini <b>sangat logis</b> - kedua indikator mengukur <b>tingkat pembangunan infrastruktur</b> wilayah. Wilayah maju cenderung punya sanitasi baik DAN rumah layak huni. Ini menunjukkan <b>determinan sosial kesehatan yang saling terkait</b>."
} else if(r > 0.4) {
"Korelasi sedang-kuat. Sanitasi dan housing berkembang bersamaan, tapi <b>tidak sempurna</b>. Ada wilayah dengan rumah baik tapi sanitasi kurang (atau sebaliknya)."
} else {
"Korelasi lemah. Pembangunan sanitasi dan housing <b>tidak sinkron</b> di wilayah ini. Program perbaikan perlu <b>terintegrasi</b>."
}
} else {
# Default interpretation
interp_text <- paste0("Korelasi ", if(r > 0) "positif" else "negatif", " dengan kekuatan ", tolower(classify_strength(r)), ".")
}
paste0(
"<li><strong>", var1_name, " ↔ ", var2_name, ":</strong> r = <span class='highlight-value'>",
round(r, 3), "</span> (", classify_strength(r), ")<br>",
"<em>Interpretasi:</em> ", interp_text,
"</li><br>"
)
}
# Generate interpretations for key pairs
if("Jumlah_DBD" %in% available_vars && "Kasus_Meninggal" %in% available_vars) {
interpretations <- paste0(interpretations, add_interpretation("Jumlah_DBD", "Kasus_Meninggal"))
}
if("Jumlah_DBD" %in% available_vars && "Jumlah_Penduduk" %in% available_vars) {
interpretations <- paste0(interpretations, add_interpretation("Jumlah_DBD", "Jumlah_Penduduk"))
}
if("Jumlah_DBD" %in% available_vars && "Jumlah_Kejadian_Banjir" %in% available_vars) {
interpretations <- paste0(interpretations, add_interpretation("Jumlah_DBD", "Jumlah_Kejadian_Banjir"))
}
if("Jumlah_DBD" %in% available_vars && "Persen_Akses_Sanitasi" %in% available_vars) {
interpretations <- paste0(interpretations, add_interpretation("Jumlah_DBD", "Persen_Akses_Sanitasi"))
}
if("Jumlah_DBD" %in% available_vars && "Persen_Rumah_Layak_Huni" %in% available_vars) {
interpretations <- paste0(interpretations, add_interpretation("Jumlah_DBD", "Persen_Rumah_Layak_Huni"))
}
if("Persen_Akses_Sanitasi" %in% available_vars && "Persen_Rumah_Layak_Huni" %in% available_vars) {
interpretations <- paste0(interpretations, add_interpretation("Persen_Akses_Sanitasi", "Persen_Rumah_Layak_Huni"))
}
if(interpretations == "") {
interpretations <- "<li>Tidak ada pasangan variabel yang dapat diinterpretasi.</li>"
}
HTML(paste0(
"<div class='interpretation-box'>",
"<h4 style='margin-top:0;'>🔍 Korelasi Tertinggi</h4>",
"<p><strong>", var1, "</strong> dengan <strong>", var2, "</strong></p>",
"<p>Nilai: <span class='highlight-value'>", round(cor_value, 3), "</span> (", classify_strength(cor_value), ")</p>",
"</div>",
"<div class='interpretation-box' style='background:#e0f2fe; border-left-color:#0284c7;'>",
"<h4 style='margin-top:0; color:#075985;'>📊 Interpretasi Korelasi Antar Variabel</h4>",
"<p style='font-size:13px; color:#334155;'><em>Korelasi menunjukkan hubungan statistik, bukan kausalitas. Nilai berkisar -1 (korelasi negatif sempurna) hingga +1 (korelasi positif sempurna).</em></p>",
"<ul style='line-height:1.8; margin-top:15px;'>",
interpretations,
"</ul>",
"<p style='background:#fef9c3; padding:10px; border-radius:5px; margin-top:15px;'>",
"<strong>⚠️ Catatan Penting:</strong> Korelasi ≠ Kausalitas. Diperlukan analisis multivariat (regresi) untuk membuktikan hubungan sebab-akibat.",
"</p>",
"</div>"
))
})
# INTERPRETASI EPIDEMIOLOGI - PERBAIKAN PREVALENSI output$epi_interpretation <- renderUI({ df <- std_df() if (is.null(df)) return(HTML(“Memuat…”))
total_kasus <- sum(df$Jumlah_DBD, na.rm = TRUE)
total_penduduk <- sum(df$Jumlah_Penduduk, na.rm = TRUE)
total_meninggal <- sum(df$Kasus_Meninggal, na.rm = TRUE)
# Prevalensi dalam persen (per 100 penduduk)
prev_agregat_persen <- (total_kasus / total_penduduk) * 100
# Prevalensi per 100.000 penduduk
prev_per_100k <- (total_kasus / total_penduduk) * 100000
cfr_agregat <- (total_meninggal / total_kasus) * 100
# Status berdasarkan standar epidemiologi
prev_status <- if(prev_per_100k > 55) {
"<span style='color:#dc2626; font-weight:bold;'>SANGAT TINGGI (Endemis Tinggi)</span>"
} else if(prev_per_100k > 20) {
"<span style='color:#f59e0b; font-weight:bold;'>TINGGI (Endemis)</span>"
} else if(prev_per_100k > 5) {
"<span style='color:#3b82f6; font-weight:bold;'>SEDANG (Sporadis)</span>"
} else {
"<span style='color:#10b981; font-weight:bold;'>RENDAH</span>"
}
cfr_status <- if(cfr_agregat > 2) {
"<span style='color:#dc2626; font-weight:bold;'>TINGGI (>2% - Perlu Perhatian Serius!)</span>"
} else if(cfr_agregat > 1) {
"<span style='color:#f59e0b; font-weight:bold;'>SEDANG (1-2%)</span>"
} else {
"<span style='color:#10b981; font-weight:bold;'>RENDAH (<1% - Baik)</span>"
}
# Analisis wilayah tertinggi
top_prev <- df %>% filter(!is.na(Prevalensi_persen)) %>%
arrange(desc(Prevalensi_persen)) %>% slice(1)
top_cfr <- df %>% filter(!is.na(CaseFatality_pct)) %>%
arrange(desc(CaseFatality_pct)) %>% slice(1)
# Interpretasi Prevalensi
prev_interpretation <- ""
if(nrow(top_prev) > 0) {
prev_per_1000 <- top_prev$Prevalensi_persen * 10
prev_interpretation <- paste0(
"<div class='interpretation-box' style='background:#fee2e2; border-left-color:#dc2626;'>",
"<h5 style='color:#991b1b;'>🔴 Wilayah Prevalensi Tertinggi: ", top_prev$Wilayah, "</h5>",
"<p><strong>Prevalensi:</strong> <span class='highlight-value'>",
round(top_prev$Prevalensi_persen, 3), "%</span></p>",
"<p><strong>Artinya:</strong></p>",
"<ul style='margin-top:5px;'>",
"<li>Dari setiap <b>1.000 penduduk</b>, sekitar <b>",
round(prev_per_1000, 1), " orang</b> terinfeksi</li>",
"<li>Dari setiap <b>100.000 penduduk</b>, sekitar <b>",
round(top_prev$Prevalensi_persen * 1000, 0), " orang</b> terinfeksi</li>",
"</ul>",
"<p style='background:#fef2f2; padding:10px; margin-top:10px; border-radius:5px;'>",
"<strong>⚠️ Implikasi:</strong> ",
if(top_prev$Prevalensi_persen > 0.1) {
"Ini menunjukkan <b>transmisi sangat tinggi</b>! Diperlukan <b>intervensi darurat</b>: fogging massal, PSN intensif, dan surveilans ketat."
} else if(top_prev$Prevalensi_persen > 0.05) {
"Tingkat transmisi <b>cukup tinggi</b>. Perlu <b>peningkatan kontrol vektor</b> dan edukasi masyarakat."
} else {
"Tingkat transmisi <b>terkendali</b>, namun tetap perlu <b>monitoring rutin</b>."
},
"</p></div>"
)
}
# Interpretasi CFR
cfr_interpretation <- ""
if(nrow(top_cfr) > 0 && !is.na(top_cfr$CaseFatality_pct)) {
jumlah_kasus <- top_cfr$Jumlah_DBD
jumlah_mati <- top_cfr$Kasus_Meninggal
cfr_interpretation <- paste0(
"<div class='interpretation-box' style='background:#fef3c7; border-left-color:#f59e0b;'>",
"<h5 style='color:#92400e;'>⚠️ Wilayah CFR Tertinggi: ", top_cfr$Wilayah, "</h5>",
"<p><strong>Case Fatality Rate:</strong> <span class='highlight-value'>",
round(top_cfr$CaseFatality_pct, 2), "%</span></p>",
"<p><strong>Data:</strong> ", jumlah_mati, " meninggal dari ", jumlah_kasus, " kasus DBD</p>",
"<p><strong>Artinya:</strong></p>",
"<ul style='margin-top:5px;'>",
"<li>Dari setiap <b>1.000 pasien DBD</b>, sekitar <b>",
round(top_cfr$CaseFatality_pct * 10, 0), " orang meninggal</b></li>",
"</ul>",
"<p style='background:#fffbeb; padding:10px; margin-top:10px; border-radius:5px;'>",
"<strong>⚠️ Implikasi:</strong> ",
if(top_cfr$CaseFatality_pct > 2) {
"CFR >2% menunjukkan <b>kualitas penanganan medis kurang optimal</b>! Diperlukan: <b>(1)</b> Peningkatan kapasitas rumah sakit, <b>(2)</b> Pelatihan tenaga medis untuk deteksi dini DHF/DSS, <b>(3)</b> Ketersediaan cairan infus dan monitoring ketat."
} else if(top_cfr$CaseFatality_pct > 1) {
"CFR 1-2% masih perlu <b>perbaikan sistem kesehatan</b>. Fokus pada <b>deteksi dini</b> dan <b>rujukan tepat waktu</b>."
} else {
"CFR <1% menunjukkan <b>penanganan medis yang baik</b>. Pertahankan kualitas layanan kesehatan."
},
"</p></div>"
)
}
HTML(paste0(
"<div class='interpretation-box'>",
"<h4 style='margin-top:0;'>📊 Ringkasan Epidemiologi DBD Jawa Barat</h4>",
"<table style='width:100%; font-size:14px;'>",
"<tr><td><strong>Total Kasus DBD:</strong></td><td><b>", comma(total_kasus), "</b> kasus</td></tr>",
"<tr><td><strong>Total Penduduk:</strong></td><td><b>", comma(total_penduduk), "</b> jiwa</td></tr>",
"<tr><td><strong>Total Meninggal:</strong></td><td><b>", comma(total_meninggal), "</b> jiwa</td></tr>",
"</table>",
"</div>",
"<div class='interpretation-box' style='background:#e0f2fe; border-left-color:#0284c7;'>",
"<h5 style='color:#075985;'>📈 Prevalensi Agregat Jawa Barat</h5>",
"<p><strong>Prevalensi:</strong> <span class='highlight-value'>", round(prev_agregat_persen, 4), "%</span> atau ",
"<span class='highlight-value'>", round(prev_per_100k, 2), "</span> per 100.000 penduduk</p>",
"<p><strong>Status:</strong> ", prev_status, "</p>",
"<p><strong>Standar WHO:</strong> Incidence Rate >55/100.000 = Hiperendemis, 20-55 = Endemis, <20 = Sporadis</p>",
"</div>",
"<div class='interpretation-box' style='background:#fef2f2; border-left-color:#dc2626;'>",
"<h5 style='color:#991b1b;'>⚕️ Case Fatality Rate (CFR) Agregat</h5>",
"<p><strong>CFR:</strong> <span class='highlight-value'>", round(cfr_agregat, 2), "%</span></p>",
"<p><strong>Status:</strong> ", cfr_status, "</p>",
"<p><strong>Target WHO:</strong> CFR harus <1% (menunjukkan sistem kesehatan yang baik)</p>",
"</div>",
prev_interpretation,
cfr_interpretation
))
})
# —————————– # PRIORITAS (fix dengan BOBOT BARU) # —————————– library(dplyr) library(DT) library(ggplot2) library(scales)
# safe normalizer 0-100 norm_0_100 <- function(x) { x_num <- as.numeric(x) if (all(is.na(x_num))) return(rep(0, length(x_num))) mx <- max(x_num, na.rm = TRUE) if (is.na(mx) || mx == 0) return(ifelse(is.na(x_num), 0, 0)) (x_num / mx) * 100 }
# ✅ BOBOT BARU sesuai permintaan w_prev <- 0.25 # Prevalensi DBD: 25% w_cfr <- 0.20 # Case Fatality Rate: 20% w_cases <- 0.20 # Jumlah Kasus: 20% w_banjir <- 0.15 # Kejadian Banjir: 15% w_sanit <- 0.10 # Akses Sanitasi: 10% w_rumah <- 0.10 # Rumah Layak Huni: 10% # Total = 100%
# helper: cari kolom nama wilayah yang tersedia get_wilayah_col <- function(df) { candidates <- c(“Wilayah”,“wilayah”,“NAME_2”,“name_2”,“KABUPATEN/KOTA (nama wilayah)”,“KABUPATEN_KOTA (nama wilayah)”,“KABUPATEN”,“KOTA”) found <- intersect(candidates, names(df)) if (length(found) == 0) return(NA_character_) found[1] }
# helper: cari kolom target berdasarkan beberapa pattern get_col_like <- function(df, patterns) { nms <- names(df) norm <- function(x) tolower(gsub(“[^[:alnum:]]”, ““, x)) nms_norm <- norm(nms) for(p in patterns) { p_norm <- norm(p) i <- which(nms_norm == p_norm) if (length(i)>0) return(nms[i[1]]) } # substring match for(p in patterns) { p_norm <- norm(p) i2 <- which(grepl(p_norm, nms_norm, fixed = TRUE)) if (length(i2)>0) return(nms[i2[1]]) } NA_character_ }
# ———- prioritas_table ———- output$prioritas_table <- renderDT({ df <- std_df() req(df)
# standar nama wilayah
name_col <- get_wilayah_col(df)
if (!is.na(name_col)) df <- df %>% mutate(Wilayah = as.character(.data[[name_col]])) else df <- df %>% mutate(Wilayah = paste0("row_", row_number()))
# ✅ Temukan kolom banjir, sanitasi, rumah
col_banjir <- get_col_like(df, c("Jumlah_Kejadian_Banjir", "Kejadian Banjir", "Banjir", "jumlah_banjir"))
col_sanit <- get_col_like(df, c("% Akses Sanitasi", "Persen_Akses_Sanitasi", "Akses Sanitasi", "akses_sanitasi"))
col_rumah <- get_col_like(df, c("% Rumah Layak Huni", "Persen_Rumah_Layak_Huni", "Rumah Layak Huni", "rumah_layak"))
# buat kolom raw (aman)
df2 <- df %>%
mutate(
Prevalensi_persen = if ("Prevalensi_persen" %in% names(.)) as.numeric(Prevalensi_persen) else if ("prevalensi_persen" %in% names(.)) as.numeric(prevalensi_persen) else NA_real_,
CaseFatality_pct = if ("CaseFatality_pct" %in% names(.)) as.numeric(CaseFatality_pct) else if ("casefatality_pct" %in% names(.)) as.numeric(casefatality_pct) else NA_real_,
Jumlah_DBD = if ("Jumlah_DBD" %in% names(.)) as.numeric(Jumlah_DBD) else NA_real_,
raw_banjir = if (!is.na(col_banjir)) as.numeric(.data[[col_banjir]]) else NA_real_,
raw_sanit = if (!is.na(col_sanit)) as.numeric(.data[[col_sanit]]) else NA_real_,
raw_rumah = if (!is.na(col_rumah)) as.numeric(.data[[col_rumah]]) else NA_real_
) %>%
mutate(
# Banjir tinggi = risiko tinggi (tidak perlu diinvert)
raw_banjir = ifelse(is.finite(raw_banjir), raw_banjir, NA_real_),
# Sanitasi rendah = risiko tinggi (invert)
raw_sanit = ifelse(is.finite(raw_sanit), 100 - raw_sanit, NA_real_),
# Rumah tidak layak = risiko tinggi (invert)
raw_rumah = ifelse(is.finite(raw_rumah), 100 - raw_rumah, NA_real_)
)
# ✅ normalisasi setiap komponen dengan BOBOT BARU
df2 <- df2 %>%
mutate(
Skor_Prev = norm_0_100(Prevalensi_persen),
Skor_CFR = norm_0_100(CaseFatality_pct),
Skor_Kasus = norm_0_100(Jumlah_DBD),
Skor_Banjir = norm_0_100(raw_banjir),
Skor_Sanitasi = norm_0_100(raw_sanit),
Skor_Rumah = norm_0_100(raw_rumah)
) %>%
mutate(
Skor_Total = (Skor_Prev * w_prev) +
(Skor_CFR * w_cfr) +
(Skor_Kasus * w_cases) +
(Skor_Banjir * w_banjir) +
(Skor_Sanitasi * w_sanit) +
(Skor_Rumah * w_rumah)
)
# kuartil untuk kategori
q <- quantile(df2$Skor_Total, probs = c(0.25,0.5,0.75), na.rm = TRUE)
df2 <- df2 %>%
mutate(
Kategori = case_when(
!is.finite(Skor_Total) ~ "⚪ PRIORITAS MINIMAL",
Skor_Total >= q[3] ~ "🔴 PRIORITAS TINGGI",
Skor_Total >= q[2] ~ "🟡 PRIORITAS SEDANG",
Skor_Total >= q[1] ~ "🟢 PRIORITAS RENDAH",
TRUE ~ "⚪ PRIORITAS MINIMAL"
),
Rank = ifelse(is.finite(Skor_Total), rank(-Skor_Total, ties.method = "min"), NA_integer_)
)
# ✅ Output table dengan kolom banjir
out_tbl <- df2 %>%
select(Rank, Wilayah, Jumlah_DBD, Prevalensi_persen, CaseFatality_pct,
Kejadian_Banjir = raw_banjir,
Akses_Sanitasi = raw_sanit,
Rumah_Layak = raw_rumah,
Skor_Prev, Skor_CFR, Skor_Kasus, Skor_Banjir, Skor_Sanitasi, Skor_Rumah,
Skor_Total, Kategori) %>%
arrange(Rank)
dat <- datatable(out_tbl, options = list(pageLength = 15, scrollX = TRUE), rownames = FALSE) %>%
formatRound(columns = c("Skor_Prev","Skor_CFR","Skor_Kasus","Skor_Banjir","Skor_Sanitasi","Skor_Rumah","Skor_Total"), digits = 1) %>%
formatRound(columns = c("Prevalensi_persen","CaseFatality_pct"), digits = 2) %>%
formatStyle('Kategori',
backgroundColor = styleEqual(c("🔴 PRIORITAS TINGGI","🟡 PRIORITAS SEDANG","🟢 PRIORITAS RENDAH","⚪ PRIORITAS MINIMAL"),
c("#fee2e2","#fef3c7","#d1fae5","#f3f4f6"))) %>%
formatStyle('Skor_Total',
background = styleColorBar(range(out_tbl$Skor_Total, na.rm = TRUE), '#ef4444'),
backgroundSize = '100% 80%',
backgroundRepeat = 'no-repeat',
backgroundPosition = 'center')
dat
})
# ———- prioritas_plot ———- output$prioritas_plot <- renderPlot({ df <- std_df() req(df)
name_col <- get_wilayah_col(df)
if (!is.na(name_col)) df <- df %>% mutate(Wilayah = as.character(.data[[name_col]])) else df <- df %>% mutate(Wilayah = paste0("row_", row_number()))
col_banjir <- get_col_like(df, c("Jumlah_Kejadian_Banjir", "Kejadian Banjir", "Banjir"))
col_sanit <- get_col_like(df, c("% Akses Sanitasi", "Persen_Akses_Sanitasi", "Akses Sanitasi"))
col_rumah <- get_col_like(df, c("% Rumah Layak Huni", "Persen_Rumah_Layak_Huni", "Rumah Layak Huni"))
df2 <- df %>%
mutate(
Prevalensi_persen = if ("Prevalensi_persen" %in% names(.)) as.numeric(Prevalensi_persen) else NA_real_,
CaseFatality_pct = if ("CaseFatality_pct" %in% names(.)) as.numeric(CaseFatality_pct) else NA_real_,
Jumlah_DBD = if ("Jumlah_DBD" %in% names(.)) as.numeric(Jumlah_DBD) else NA_real_,
raw_banjir = if (!is.na(col_banjir)) as.numeric(.data[[col_banjir]]) else NA_real_,
raw_sanit = if (!is.na(col_sanit)) as.numeric(.data[[col_sanit]]) else NA_real_,
raw_rumah = if (!is.na(col_rumah)) as.numeric(.data[[col_rumah]]) else NA_real_
) %>%
mutate(
raw_banjir = ifelse(is.finite(raw_banjir), raw_banjir, NA_real_),
raw_sanit = ifelse(is.finite(raw_sanit), 100 - raw_sanit, NA_real_),
raw_rumah = ifelse(is.finite(raw_rumah), 100 - raw_rumah, NA_real_),
Skor_Prev = norm_0_100(Prevalensi_persen),
Skor_CFR = norm_0_100(CaseFatality_pct),
Skor_Kasus = norm_0_100(Jumlah_DBD),
Skor_Banjir = norm_0_100(raw_banjir),
Skor_Sanitasi = norm_0_100(raw_sanit),
Skor_Rumah = norm_0_100(raw_rumah),
Skor_Total = (Skor_Prev * w_prev) +
(Skor_CFR * w_cfr) +
(Skor_Kasus * w_cases) +
(Skor_Banjir * w_banjir) +
(Skor_Sanitasi * w_sanit) +
(Skor_Rumah * w_rumah)
) %>%
arrange(desc(Skor_Total)) %>%
slice_head(n = 15)
if (nrow(df2) == 0) {
plot.new(); text(0.5,0.5,"Tidak ada data", cex=1.2); return()
}
df2$Color_Cat <- cut(df2$Skor_Total, breaks = c(-Inf,33,66,Inf), labels = c("Rendah","Sedang","Tinggi"))
ggplot(df2, aes(x = reorder(Wilayah, Skor_Total), y = Skor_Total, fill = Color_Cat)) +
geom_col(alpha = 0.85) +
geom_text(aes(label = round(Skor_Total,1)), hjust = -0.1, size = 3.5, fontface = "bold") +
coord_flip() +
scale_fill_manual(values = c("Rendah"="#10b981","Sedang"="#f59e0b","Tinggi"="#dc2626"),
name = "Tingkat Prioritas") +
scale_y_continuous(limits = c(0, max(df2$Skor_Total, na.rm = TRUE)*1.12), expand = c(0,0)) +
theme_minimal(base_size = 13) +
labs(title = "Top 15 Wilayah Prioritas Intervensi DBD",
subtitle = "Skor Komposit (0-100): Prev 25% + CFR 20% + Kasus 20% + Banjir 15% + Sanit 10% + Rumah 10%",
x = NULL, y = "Skor Prioritas (0-100)") +
theme(
plot.title = element_text(face = "bold", size = 16),
plot.subtitle = element_text(size = 11, color = "gray30"),
legend.position = "bottom",
panel.grid.major.y = element_blank()
)
}, res = 96)
# ———- prioritas_summary ———- output$prioritas_summary <- renderUI({ df <- std_df(); req(df)
col_banjir <- get_col_like(df, c("Jumlah_Kejadian_Banjir","Kejadian Banjir","Banjir"))
col_sanit <- get_col_like(df, c("% Akses Sanitasi","Persen_Akses_Sanitasi","Akses Sanitasi"))
col_rumah <- get_col_like(df, c("% Rumah Layak Huni","Persen_Rumah_Layak_Huni","Rumah Layak Huni"))
df_score <- df %>%
mutate(
Prevalensi_persen = if ("Prevalensi_persen" %in% names(.)) as.numeric(Prevalensi_persen) else NA_real_,
CaseFatality_pct = if ("CaseFatality_pct" %in% names(.)) as.numeric(CaseFatality_pct) else NA_real_,
Jumlah_DBD = if ("Jumlah_DBD" %in% names(.)) as.numeric(Jumlah_DBD) else NA_real_,
raw_banjir = if (!is.na(col_banjir)) as.numeric(.data[[col_banjir]]) else NA_real_,
raw_sanit = if (!is.na(col_sanit)) as.numeric(.data[[col_sanit]]) else NA_real_,
raw_rumah = if (!is.na(col_rumah)) as.numeric(.data[[col_rumah]]) else NA_real_
) %>%
mutate(
raw_banjir = ifelse(is.finite(raw_banjir), raw_banjir, NA_real_),
raw_sanit = ifelse(is.finite(raw_sanit), 100 - raw_sanit, NA_real_),
raw_rumah = ifelse(is.finite(raw_rumah), 100 - raw_rumah, NA_real_),
Skor_Prev = norm_0_100(Prevalensi_persen),
Skor_CFR = norm_0_100(CaseFatality_pct),
Skor_Kasus = norm_0_100(Jumlah_DBD),
Skor_Banjir = norm_0_100(raw_banjir),
Skor_Sanitasi = norm_0_100(raw_sanit),
Skor_Rumah = norm_0_100(raw_rumah),
Skor_Total = (Skor_Prev * w_prev) +
(Skor_CFR * w_cfr) +
(Skor_Kasus * w_cases) +
(Skor_Banjir * w_banjir) +
(Skor_Sanitasi * w_sanit) +
(Skor_Rumah * w_rumah)
)
q75 <- quantile(df_score$Skor_Total, 0.75, na.rm = TRUE)
q50 <- quantile(df_score$Skor_Total, 0.50, na.rm = TRUE)
high <- sum(df_score$Skor_Total >= q75, na.rm = TRUE)
medium <- sum(df_score$Skor_Total >= q50 & df_score$Skor_Total < q75, na.rm = TRUE)
low <- sum(df_score$Skor_Total < q50, na.rm = TRUE)
HTML(paste0(
"<div class='interpretation-box priority-high'>",
"<h4 style='margin-top:0;'>🎯 Ringkasan Wilayah Prioritas</h4>",
"<table style='width:100%; font-size:15px;'>",
"<tr><td>🔴 <strong>PRIORITAS TINGGI:</strong></td><td style='text-align:right;'><b>", high, " wilayah</b> (perlu intervensi segera)</td></tr>",
"<tr><td>🟡 <strong>PRIORITAS SEDANG:</strong></td><td style='text-align:right;'><b>", medium, " wilayah</b> (perlu monitoring ketat)</td></tr>",
"<tr><td>🟢 <strong>PRIORITAS RENDAH:</strong></td><td style='text-align:right;'><b>", low, " wilayah</b> (maintain current efforts)</td></tr>",
"</table>",
"</div>",
"<div class='interpretation-box' style='background:#fef9c3; border-left-color:#f59e0b;'>",
"<h5 style='color:#92400e; margin-top:0;'>📐 Metodologi Perhitungan Skor (REVISI)</h5>",
"<p><strong>Skor Prioritas (0-100)</strong> dihitung dari 6 indikator dengan bobot:</p>",
"<ul style='margin-top:10px;'>",
"<li><strong>Prevalensi DBD (25%):</strong> Tingkat serangan penyakit di populasi</li>",
"<li><strong>Case Fatality Rate (20%):</strong> Kualitas penanganan medis</li>",
"<li><strong>Jumlah Kasus Absolut (20%):</strong> Beban penyakit total</li>",
"<li><strong>Kejadian Banjir (15%):</strong> Faktor lingkungan breeding site nyamuk</li>",
"<li><strong>Akses Sanitasi Buruk (10%):</strong> Infrastruktur lingkungan (dibalik)</li>",
"<li><strong>Rumah Tidak Layak (10%):</strong> Kualitas perumahan (dibalik)</li>",
"</ul>",
"<p style='margin-top:10px;'><em>Total bobot: 100%. Setiap indikator dinormalisasi 0-100, lalu dijumlahkan dengan bobot.</em></p>",
"</div>"
))
})
# ======================================== # AUTOKORELASI SPASIAL - PERBAIKAN # ========================================
# Helper: Prepare spatial data (DIPERBAIKI) prepare_spatial_data <- reactive({ df <- std_df() shp <- jabar_shp() req(df, shp)
tryCatch({
# Cleaning nama wilayah (LEBIH FLEKSIBEL)
df$Wilayah_Clean <- tolower(trimws(as.character(df$Wilayah)))
df$Wilayah_Clean <- gsub("^(kab\\.?\\s*|kabupaten\\s+)", "", df$Wilayah_Clean)
df$Wilayah_Clean <- gsub("\\s+", " ", df$Wilayah_Clean)
shp$Wilayah_Clean <- tolower(trimws(as.character(shp$NAME_2)))
shp$Wilayah_Clean <- gsub("^kabupaten\\s+", "", shp$Wilayah_Clean)
shp$Wilayah_Clean <- gsub("\\s+", " ", shp$Wilayah_Clean)
# Merge
merged <- merge(shp, df, by = "Wilayah_Clean", all.x = TRUE)
# Filter HANYA data lengkap untuk Prevalensi & Jumlah DBD
merged_complete <- merged %>%
filter(!is.na(Prevalensi_persen) & !is.na(Jumlah_DBD) &
is.finite(Prevalensi_persen) & is.finite(Jumlah_DBD))
if(nrow(merged_complete) < 3) {
showNotification("Data tidak cukup untuk analisis spasial (minimal 3 wilayah)", type = "error")
return(NULL)
}
# JANGAN aggregate dulu - cek duplicate
duplicates <- merged_complete$NAME_2[duplicated(merged_complete$NAME_2)]
if(length(duplicates) > 0) {
showNotification(paste("Warning: Duplicate wilayah detected:", paste(duplicates, collapse=", ")), type="warning")
# Aggregate by NAME_2 (JIKA ADA DUPLICATE)
merged_complete <- merged_complete %>%
group_by(NAME_2) %>%
summarise(
Prevalensi_persen = mean(Prevalensi_persen, na.rm = TRUE),
Jumlah_DBD = sum(Jumlah_DBD, na.rm = TRUE),
across(where(is.numeric), ~mean(., na.rm = TRUE)),
geometry = st_union(geometry)
) %>%
st_as_sf()
}
# Set rownames untuk spatial analysis
row.names(merged_complete) <- merged_complete$NAME_2
showNotification(paste("Data spasial siap:", nrow(merged_complete), "wilayah"), type = "message")
return(merged_complete)
}, error = function(e) {
showNotification(paste("Error prepare spatial data:", e$message), type = "error")
return(NULL)
})
})
# ======================================== # MORAN’S I GLOBAL (DIPERBAIKI) # ======================================== output$moran_table <- renderDT({ DBD_Jabar <- prepare_spatial_data() req(DBD_Jabar)
tryCatch({
# DEBUGGING: Print jumlah wilayah
n_wilayah <- nrow(DBD_Jabar)
# Build spatial weights (SAMA SEPERTI R BIASA)
W <- poly2nb(DBD_Jabar, queen = TRUE)
# Handle isolated regions (PENTING!)
isolated <- which(card(W) == 0)
if(length(isolated) > 0) {
showNotification(paste("Isolated regions detected:", length(isolated)), type = "warning")
coords <- st_coordinates(st_centroid(st_geometry(DBD_Jabar)))
W <- knn2nb(knearneigh(coords, k = 1))
}
WL <- nb2listw(W, style = "W", zero.policy = TRUE)
# ===== MORAN'S I UNTUK JUMLAH KASUS =====
kasus_dbd <- as.numeric(DBD_Jabar$Jumlah_DBD)
# Remove NA/Inf
if(any(is.na(kasus_dbd) | !is.finite(kasus_dbd))) {
showNotification("Warning: NA/Inf values in Jumlah_DBD", type = "warning")
return(datatable(data.frame(Error = "Data Jumlah_DBD mengandung NA/Inf")))
}
Global_Moran_Kasus <- moran.test(kasus_dbd, WL, zero.policy = TRUE)
Moran_MC_Kasus <- moran.mc(kasus_dbd, WL, nsim = 999, zero.policy = TRUE)
# Geary's C untuk Kasus
Geary_Kasus <- geary.test(kasus_dbd, WL, zero.policy = TRUE)
# ===== MORAN'S I UNTUK PREVALENSI =====
prev_dbd <- as.numeric(DBD_Jabar$Prevalensi_persen)
if(any(is.na(prev_dbd) | !is.finite(prev_dbd))) {
showNotification("Warning: NA/Inf values in Prevalensi", type = "warning")
return(datatable(data.frame(Error = "Data Prevalensi mengandung NA/Inf")))
}
Global_Moran_Prev <- moran.test(prev_dbd, WL, zero.policy = TRUE)
Moran_MC_Prev <- moran.mc(prev_dbd, WL, nsim = 999, zero.policy = TRUE)
# Geary's C untuk Prevalensi
Geary_Prev <- geary.test(prev_dbd, WL, zero.policy = TRUE)
# ===== TABEL HASIL (SESUAI FORMAT R BIASA) =====
hasil_moran <- data.frame(
Variabel = c("Jumlah Kasus DBD", "Prevalensi DBD (%)"),
`Moran's I` = c(
round(Global_Moran_Kasus$estimate[1], 4),
round(Global_Moran_Prev$estimate[1], 4)
),
`p-value I (Asimtotik)` = c(
round(Global_Moran_Kasus$p.value, 4),
round(Global_Moran_Prev$p.value, 4)
),
`p-value I (MC)` = c(
round(Moran_MC_Kasus$p.value, 4),
round(Moran_MC_Prev$p.value, 4)
),
`Geary's C` = c(
round(Geary_Kasus$estimate[1], 4),
round(Geary_Prev$estimate[1], 4)
),
`p-value C` = c(
round(Geary_Kasus$p.value, 4),
round(Geary_Prev$p.value, 4)
),
`Interpretasi Singkat` = c(
ifelse(Moran_MC_Kasus$p.value < 0.05,
paste0("Ada autokorelasi spasial ",
ifelse(Global_Moran_Kasus$estimate[1] > 0, "positif", "negatif"),
" (cluster kasus ",
ifelse(Global_Moran_Kasus$estimate[1] > 0, "tinggi", "berbeda"),
" cenderung berdekatan)"),
"Tidak ada autokorelasi spasial signifikan (pola acak)"),
ifelse(Moran_MC_Prev$p.value < 0.05,
paste0("Ada autokorelasi spasial ",
ifelse(Global_Moran_Prev$estimate[1] > 0, "positif", "negatif"),
" (cluster prevalensi ",
ifelse(Global_Moran_Prev$estimate[1] > 0, "tinggi", "berbeda"),
" cenderung berdekatan)"),
"Tidak ada autokorelasi spasial signifikan (pola acak)")
),
check.names = FALSE
)
# Info tambahan
output$debug_spatial_info <- renderUI({
HTML(paste0(
"<p style='font-size:12px; color:#6b7280;'>",
"<strong>Info:</strong> Analisis menggunakan ", n_wilayah, " wilayah | ",
"Isolated regions: ", length(isolated), " | ",
"Mean Prevalensi: ", round(mean(prev_dbd), 4), "% | ",
"Mean Kasus: ", round(mean(kasus_dbd), 2),
"</p>"
))
})
datatable(hasil_moran,
options = list(dom = 't', pageLength = 5, scrollX = TRUE),
rownames = FALSE) %>%
formatStyle('Interpretasi Singkat',
fontSize = '12px')
}, error = function(e) {
datatable(data.frame(Error = paste("Gagal analisis Moran's I:", e$message)))
})
})
# ======================================== # LISA MAP # ======================================== output$lisa_map <- renderPlot({ DBD_Jabar <- prepare_spatial_data() req(DBD_Jabar)
tryCatch({
# Build spatial weights
W <- poly2nb(DBD_Jabar, queen = TRUE)
if(any(card(W) == 0)) {
coords <- st_coordinates(st_centroid(st_geometry(DBD_Jabar)))
W <- knn2nb(knearneigh(coords, k = 1))
}
WL <- nb2listw(W, style = "W", zero.policy = TRUE)
# Local Moran's I
prev_dbd <- as.numeric(DBD_Jabar$Prevalensi_persen)
Local_Moran <- localmoran(prev_dbd, WL, zero.policy = TRUE)
mean_prev <- mean(prev_dbd, na.rm = TRUE)
# Classify LISA clusters
Jabar_LISA <- DBD_Jabar %>%
mutate(
Local_I = Local_Moran[, 1],
P_value = Local_Moran[, 5],
Quadrant = case_when(
P_value >= 0.05 ~ "Not Significant",
Prevalensi_persen >= mean_prev & Local_I > 0 ~ "High-High",
Prevalensi_persen < mean_prev & Local_I > 0 ~ "Low-Low",
Prevalensi_persen >= mean_prev & Local_I < 0 ~ "High-Low",
Prevalensi_persen < mean_prev & Local_I < 0 ~ "Low-High",
TRUE ~ "Not Significant"
)
)
# Plot
ggplot() +
geom_sf(data = Jabar_LISA, aes(fill = Quadrant), color = "white", size = 0.3) +
scale_fill_manual(
values = c(
"High-High" = "#d7191c",
"Low-Low" = "#2c7bb6",
"High-Low" = "#fdae61",
"Low-High" = "#abd9e9",
"Not Significant" = "#ffffbf"
),
name = "Cluster Type"
) +
labs(
title = "LISA Map Prevalensi DBD Jawa Barat",
subtitle = paste0("Mean Prevalensi: ", round(mean_prev, 3), "%")
) +
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),
legend.position = "right"
)
}, error = function(e) {
plot.new()
text(0.5, 0.5, paste("Error:", e$message), cex = 1.2, col = "red")
})
}, res = 96)
# ======================================== # TABEL LISA # ======================================== output$lisa_table <- renderDT({ DBD_Jabar <- prepare_spatial_data() req(DBD_Jabar)
tryCatch({
W <- poly2nb(DBD_Jabar, queen = TRUE)
if(any(card(W) == 0)) {
coords <- st_coordinates(st_centroid(st_geometry(DBD_Jabar)))
W <- knn2nb(knearneigh(coords, k = 1))
}
WL <- nb2listw(W, style = "W", zero.policy = TRUE)
prev_dbd <- as.numeric(DBD_Jabar$Prevalensi_persen)
Local_Moran <- localmoran(prev_dbd, WL, zero.policy = TRUE)
mean_prev <- mean(prev_dbd, na.rm = TRUE)
lisa_df <- data.frame(
Wilayah = DBD_Jabar$NAME_2,
Prevalensi = round(DBD_Jabar$Prevalensi_persen, 3),
Local_I = round(Local_Moran[, 1], 4),
Z_score = round(Local_Moran[, 4], 3),
P_value = round(Local_Moran[, 5], 4),
Cluster = case_when(
Local_Moran[, 5] >= 0.05 ~ "Not Significant",
DBD_Jabar$Prevalensi_persen >= mean_prev & Local_Moran[, 1] > 0 ~ "🔴 High-High",
DBD_Jabar$Prevalensi_persen < mean_prev & Local_Moran[, 1] > 0 ~ "🔵 Low-Low",
DBD_Jabar$Prevalensi_persen >= mean_prev & Local_Moran[, 1] < 0 ~ "🟠 High-Low",
DBD_Jabar$Prevalensi_persen < mean_prev & Local_Moran[, 1] < 0 ~ "🟣 Low-High",
TRUE ~ "Not Significant"
)
) %>%
arrange(desc(abs(Local_I)))
datatable(lisa_df,
options = list(pageLength = 15, scrollX = TRUE),
rownames = FALSE) %>%
formatStyle('Cluster',
backgroundColor = styleEqual(
c("🔴 High-High", "🔵 Low-Low", "🟠 High-Low", "🟣 Low-High", "Not Significant"),
c("#fee2e2", "#dbeafe", "#fed7aa", "#e9d5ff", "#fef3c7")
))
}, error = function(e) {
datatable(data.frame(Error = paste("Gagal membuat tabel LISA:", e$message)))
})
})
# ======================================== # INTERPRETASI LISA # ======================================== output$lisa_interpretation <- renderUI({ DBD_Jabar <- prepare_spatial_data() req(DBD_Jabar)
tryCatch({
W <- poly2nb(DBD_Jabar, queen = TRUE)
if(any(card(W) == 0)) {
coords <- st_coordinates(st_centroid(st_geometry(DBD_Jabar)))
W <- knn2nb(knearneigh(coords, k = 1))
}
WL <- nb2listw(W, style = "W", zero.policy = TRUE)
prev_dbd <- as.numeric(DBD_Jabar$Prevalensi_persen)
Local_Moran <- localmoran(prev_dbd, WL, zero.policy = TRUE)
mean_prev <- mean(prev_dbd, na.rm = TRUE)
# Count clusters
sig_mask <- Local_Moran[, 5] < 0.05
hh <- sum(DBD_Jabar$Prevalensi_persen >= mean_prev & Local_Moran[, 1] > 0 & sig_mask)
ll <- sum(DBD_Jabar$Prevalensi_persen < mean_prev & Local_Moran[, 1] > 0 & sig_mask)
hl <- sum(DBD_Jabar$Prevalensi_persen >= mean_prev & Local_Moran[, 1] < 0 & sig_mask)
lh <- sum(DBD_Jabar$Prevalensi_persen < mean_prev & Local_Moran[, 1] < 0 & sig_mask)
HTML(paste0(
"<div class='interpretation-box' style='background:#fee2e2; border-left-color:#dc2626;'>",
"<h4 style='color:#991b1b;'>🗺️ Ringkasan LISA Clusters:</h4>",
"<ul style='font-size:14px;'>",
"<li><strong style='color:#dc2626;'>🔴 High-High (Hotspot):</strong> ", hh, " wilayah - PRIORITAS UTAMA untuk intervensi terkoordinasi!</li>",
"<li><strong style='color:#2563eb;'>🔵 Low-Low (Coldspot):</strong> ", ll, " wilayah - Area aman, pertahankan strategi</li>",
"<li><strong style='color:#f59e0b;'>🟠 High-Low (Outlier):</strong> ", hl, " wilayah - Cek faktor lokal spesifik</li>",
"<li><strong style='color:#7c3aed;'>🟣 Low-High (Outlier):</strong> ", lh, " wilayah - Risiko spillover dari tetangga!</li>",
"</ul>",
"</div>",
"<div class='interpretation-box' style='background:#e0f2fe; border-left-color:#0284c7;'>",
"<h5 style='color:#075985;'>💡 Rekomendasi:</h5>",
"<p>",
if(hh > 0) {
paste0("Terdapat <b>", hh, " wilayah hotspot (High-High)</b>. Lakukan <b>fogging simultan</b>, koordinasi PSN lintas wilayah, dan surveilans cross-border!")
} else {
"Tidak ada hotspot signifikan. Fokus pada intervensi lokal per wilayah."
},
"</p>",
"</div>"
))
}, error = function(e) {
HTML(paste0("<p style='color:red;'>Error: ", e$message, "</p>"))
})
})
# KORELASI INTERPRETATION - PERBAIKAN output$correlation_interpretation <- renderUI({ df <- std_df(); req(df)
cor_vars <- c("Jumlah_DBD", "Jumlah_Penduduk", "Jumlah_Kejadian_Banjir",
"Persen_Akses_Sanitasi", "Persen_Rumah_Layak_Huni", "Kasus_Meninggal")
available_vars <- intersect(cor_vars, names(df))
if(length(available_vars) < 2) {
return(HTML("<div class='interpretation-box'><p>Tidak cukup variabel untuk analisis korelasi. Minimal 2 variabel numerik diperlukan.</p></div>"))
}
# Prepare data
cor_df <- df %>%
select(all_of(available_vars)) %>%
mutate(across(everything(), as.numeric))
cor_df <- cor_df[rowSums(is.na(cor_df)) < ncol(cor_df), ]
if(nrow(cor_df) < 3) {
return(HTML("<div class='interpretation-box'><p>Tidak cukup data untuk analisis korelasi. Minimal 3 observasi lengkap diperlukan.</p></div>"))
}
# Calculate correlation
cor_matrix <- cor(cor_df, use = "pairwise.complete.obs")
# Calculate p-values DENGAN ERROR HANDLING
n_vars <- ncol(cor_matrix)
p_matrix <- matrix(1, n_vars, n_vars)
rownames(p_matrix) <- colnames(p_matrix) <- colnames(cor_matrix)
for(i in 1:(n_vars-1)) {
for(j in (i+1):n_vars) {
tryCatch({
# Get complete cases for this pair
pair_data <- cor_df[, c(i, j)]
pair_complete <- pair_data[complete.cases(pair_data), ]
if(nrow(pair_complete) >= 3) {
test <- cor.test(pair_complete[[1]], pair_complete[[2]], method = "pearson")
p_matrix[i,j] <- p_matrix[j,i] <- test$p.value
}
}, error = function(e) {
# Jika error, biarkan p-value = 1 (not significant)
})
}
}
# Fungsi klasifikasi
classify_strength <- function(r) {
abs_r <- abs(r)
if(abs_r >= 0.8) return("<strong style='color:#dc2626;'>Sangat Kuat</strong>")
if(abs_r >= 0.6) return("<strong style='color:#f59e0b;'>Kuat</strong>")
if(abs_r >= 0.4) return("<strong style='color:#3b82f6;'>Sedang</strong>")
if(abs_r >= 0.2) return("<strong style='color:#6b7280;'>Lemah</strong>")
return("<strong style='color:#9ca3af;'>Sangat Lemah</strong>")
}
# Find strongest correlation
cor_matrix_no_diag <- cor_matrix
diag(cor_matrix_no_diag) <- NA
max_cor <- max(abs(cor_matrix_no_diag), na.rm = TRUE)
max_pos <- which(abs(cor_matrix_no_diag) == max_cor, arr.ind = TRUE)[1,]
var1 <- rownames(cor_matrix)[max_pos[1]]
var2 <- colnames(cor_matrix)[max_pos[2]]
cor_value <- cor_matrix[max_pos[1], max_pos[2]]
# Build interpretations
interpretations <- ""
# Helper function untuk interpretasi
add_interpretation <- function(var1_name, var2_name) {
if(!(var1_name %in% available_vars && var2_name %in% available_vars)) return("")
r <- cor_matrix[var1_name, var2_name]
if(is.na(r)) return("")
# Interpretasi teks berdasarkan pasangan variabel
interp_text <- ""
if(var1_name == "Jumlah_DBD" && var2_name == "Kasus_Meninggal") {
interp_text <- if(r > 0.7) {
"Korelasi sangat kuat dan positif. Semakin banyak kasus DBD, semakin banyak kematian. Ini <b>normal secara epidemiologi</b>, namun besarnya korelasi juga menunjukkan <b>CFR yang konsisten</b> antar wilayah."
} else if(r > 0.4) {
"Korelasi sedang-kuat. Jumlah kematian meningkat seiring kasus, namun tidak proporsional sempurna. Menunjukkan <b>variasi kualitas penanganan medis</b> antar wilayah."
} else {
"Korelasi lemah. Menunjukkan <b>CFR sangat bervariasi</b> antar wilayah - ada wilayah dengan kasus banyak tapi kematian rendah (penanganan baik) dan sebaliknya."
}
} else if(var1_name == "Jumlah_DBD" && var2_name == "Jumlah_Penduduk") {
interp_text <- if(r > 0.6) {
"Korelasi kuat positif. Wilayah berpenduduk banyak cenderung punya <b>kasus DBD lebih banyak</b>. Ini wajar karena lebih banyak penduduk = lebih banyak host potensial. Namun, untuk analisis risiko, <b>gunakan PREVALENSI</b> (bukan jumlah absolut)."
} else if(r > 0.3) {
"Korelasi sedang. Jumlah penduduk bukan satu-satunya penentu kasus DBD. Faktor lain seperti <b>kepadatan, sanitasi, dan kontrol vektor</b> juga berperan penting."
} else {
"Korelasi lemah. Jumlah kasus DBD <b>tidak ditentukan oleh jumlah penduduk</b>. Ini bagus - menunjukkan faktor lingkungan dan intervensi lebih dominan daripada ukuran populasi."
}
} else if(var1_name == "Jumlah_DBD" && var2_name == "Jumlah_Kejadian_Banjir") {
interp_text <- if(r > 0.4) {
"Korelasi positif yang cukup kuat. Wilayah dengan banjir lebih sering cenderung punya <b>kasus DBD lebih tinggi</b>. Banjir menciptakan <b>genangan air</b> yang ideal untuk breeding site nyamuk Aedes aegypti. <b>Rekomendasi:</b> intensifkan PSN paska-banjir!"
} else if(r > 0.2) {
"Korelasi lemah-sedang. Banjir berkontribusi pada peningkatan DBD, tapi <b>bukan faktor dominan</b>. Faktor lain seperti perilaku masyarakat dan sanitasi lebih berpengaruh."
} else if(r < -0.2) {
"Korelasi negatif. <b>Tidak biasa!</b> Kemungkinan: (1) data banjir tidak sinkron dengan data DBD (lag time), atau (2) banjir besar justru 'membersihkan' breeding sites."
} else {
"Tidak ada korelasi signifikan. Banjir <b>tidak berpengaruh langsung</b> pada kasus DBD di wilayah ini, atau ada lag time antara banjir dan outbreak DBD."
}
} else if(var1_name == "Jumlah_DBD" && var2_name == "Persen_Akses_Sanitasi") {
interp_text <- if(r < -0.4) {
"Korelasi negatif yang kuat. Semakin baik akses sanitasi, <b>semakin rendah kasus DBD</b>. Ini masuk akal - sanitasi baik berarti <b>pengelolaan air bersih yang baik</b>, sehingga mengurangi penampungan air yang jadi breeding site. <b>Rekomendasi:</b> tingkatkan akses sanitasi sebagai strategi preventif!"
} else if(r < -0.2) {
"Korelasi negatif lemah-sedang. Sanitasi membantu mengurangi DBD, tapi <b>efeknya terbatas</b>. Perlu dikombinasi dengan strategi lain seperti PSN dan fogging."
} else if(r > 0.2) {
"Korelasi positif. <b>Paradoks!</b> Kemungkinan: wilayah urban dengan sanitasi baik justru punya mobilitas tinggi dan kepadatan tinggi yang meningkatkan transmisi DBD. Atau, ada <b>confounding variable</b>."
} else {
"Tidak ada korelasi signifikan. Akses sanitasi <b>tidak berpengaruh langsung</b> pada kasus DBD, mungkin karena sanitasi lebih terkait infrastruktur limbah, bukan pengelolaan air bersih yang breeding site nyamuk."
}
} else if(var1_name == "Jumlah_DBD" && var2_name == "Persen_Rumah_Layak_Huni") {
interp_text <- if(r < -0.4) {
"Korelasi negatif yang kuat. Semakin banyak rumah layak huni, <b>semakin rendah kasus DBD</b>. Rumah layak huni biasanya punya <b>ventilasi baik, tidak ada genangan, dan pengelolaan sampah teratur</b> - semua mengurangi breeding site nyamuk. <b>Intervensi housing improvement efektif!</b>"
} else if(r < -0.2) {
"Korelasi negatif lemah-sedang. Kualitas rumah berpengaruh pada DBD, tapi <b>tidak dominan</b>. Faktor perilaku penghuni (PSN) lebih penting."
} else if(r > 0.2) {
"Korelasi positif. <b>Tidak biasa!</b> Kemungkinan: (1) Rumah layak huni di urban area justru punya <b>penampungan air bersih banyak</b> (bak mandi, tandon), atau (2) wilayah wealthier punya mobilitas tinggi."
} else {
"Tidak ada korelasi signifikan. Kualitas fisik rumah <b>tidak menentukan kasus DBD</b> - perilaku penghuni dan pengelolaan lingkungan lebih penting."
}
} else if(var1_name == "Persen_Akses_Sanitasi" && var2_name == "Persen_Rumah_Layak_Huni") {
interp_text <- if(r > 0.7) {
"Korelasi sangat kuat positif. Ini <b>sangat logis</b> - kedua indikator mengukur <b>tingkat pembangunan infrastruktur</b> wilayah. Wilayah maju cenderung punya sanitasi baik DAN rumah layak huni. Ini menunjukkan <b>determinan sosial kesehatan yang saling terkait</b>."
} else if(r > 0.4) {
"Korelasi sedang-kuat. Sanitasi dan housing berkembang bersamaan, tapi <b>tidak sempurna</b>. Ada wilayah dengan rumah baik tapi sanitasi kurang (atau sebaliknya)."
} else {
"Korelasi lemah. Pembangunan sanitasi dan housing <b>tidak sinkron</b> di wilayah ini. Program perbaikan perlu <b>terintegrasi</b>."
}
} else {
# Default interpretation
interp_text <- paste0("Korelasi ", if(r > 0) "positif" else "negatif", " dengan kekuatan ", tolower(classify_strength(r)), ".")
}
paste0(
"<li><strong>", var1_name, " ↔ ", var2_name, ":</strong> r = <span class='highlight-value'>",
round(r, 3), "</span> (", classify_strength(r), ")<br>",
"<em>Interpretasi:</em> ", interp_text,
"</li><br>"
)
}
# Generate interpretations for key pairs
if("Jumlah_DBD" %in% available_vars && "Kasus_Meninggal" %in% available_vars) {
interpretations <- paste0(interpretations, add_interpretation("Jumlah_DBD", "Kasus_Meninggal"))
}
if("Jumlah_DBD" %in% available_vars && "Jumlah_Penduduk" %in% available_vars) {
interpretations <- paste0(interpretations, add_interpretation("Jumlah_DBD", "Jumlah_Penduduk"))
}
if("Jumlah_DBD" %in% available_vars && "Jumlah_Kejadian_Banjir" %in% available_vars) {
interpretations <- paste0(interpretations, add_interpretation("Jumlah_DBD", "Jumlah_Kejadian_Banjir"))
}
if("Jumlah_DBD" %in% available_vars && "Persen_Akses_Sanitasi" %in% available_vars) {
interpretations <- paste0(interpretations, add_interpretation("Jumlah_DBD", "Persen_Akses_Sanitasi"))
}
if("Jumlah_DBD" %in% available_vars && "Persen_Rumah_Layak_Huni" %in% available_vars) {
interpretations <- paste0(interpretations, add_interpretation("Jumlah_DBD", "Persen_Rumah_Layak_Huni"))
}
if("Persen_Akses_Sanitasi" %in% available_vars && "Persen_Rumah_Layak_Huni" %in% available_vars) {
interpretations <- paste0(interpretations, add_interpretation("Persen_Akses_Sanitasi", "Persen_Rumah_Layak_Huni"))
}
if(interpretations == "") {
interpretations <- "<li>Tidak ada pasangan variabel yang dapat diinterpretasi.</li>"
}
HTML(paste0(
"<div class='interpretation-box'>",
"<h4 style='margin-top:0;'>🔍 Korelasi Tertinggi</h4>",
"<p><strong>", var1, "</strong> dengan <strong>", var2, "</strong></p>",
"<p>Nilai: <span class='highlight-value'>", round(cor_value, 3), "</span> (", classify_strength(cor_value), ")</p>",
"</div>",
"<div class='interpretation-box' style='background:#e0f2fe; border-left-color:#0284c7;'>",
"<h4 style='margin-top:0; color:#075985;'>📊 Interpretasi Korelasi Antar Variabel</h4>",
"<p style='font-size:13px; color:#334155;'><em>Korelasi menunjukkan hubungan statistik, bukan kausalitas. Nilai berkisar -1 (korelasi negatif sempurna) hingga +1 (korelasi positif sempurna).</em></p>",
"<ul style='line-height:1.8; margin-top:15px;'>",
interpretations,
"</ul>",
"<p style='background:#fef9c3; padding:10px; border-radius:5px; margin-top:15px;'>",
"<strong>⚠️ Catatan Penting:</strong> Korelasi ≠ Kausalitas. Diperlukan analisis multivariat (regresi) untuk membuktikan hubungan sebab-akibat.",
"</p>",
"</div>"
))
})
# UKURAN FREKUENSI output$freq_table <- renderDT({ df <- std_df() if (is.null(df)) return(datatable(data.frame(Message = “Memuat…”))) fd <- df %>% select(Wilayah, Jumlah_DBD, Jumlah_Penduduk, Kasus_Meninggal, Prevalensi_persen, CaseFatality_pct) %>% arrange(desc(Prevalensi_persen)) datatable(fd, options = list(pageLength = 15, scrollX = TRUE)) })
output$top10_prev <- renderDT({ df <- std_df() if (is.null(df)) return(datatable(data.frame(Message=“Memuat…”))) top10 <- df %>% filter(!is.na(Prevalensi_persen)) %>% arrange(desc(Prevalensi_persen)) %>% slice_head(n = 10) datatable(top10 %>% select(Wilayah, Prevalensi_persen), options = list(dom = ‘t’, pageLength = 10), rownames = FALSE) })
# GRAFIK EPIDEMIOLOGI output\(epi_comparison_plot <- renderPlot({ df <- std_df(); req(df, input\)epi_var_plot)
var_name <- input$epi_var_plot
plot_data <- df %>%
filter(!is.na(.data[[var_name]])) %>%
arrange(desc(.data[[var_name]])) %>%
slice_head(n = 15)
ggplot(plot_data, aes(x = reorder(Wilayah, .data[[var_name]]), y = .data[[var_name]])) +
geom_col(fill = "#2C7873", alpha = 0.8) +
geom_text(aes(label = round(.data[[var_name]], 2)), hjust = -0.2, size = 3.5) +
coord_flip() +
theme_minimal(base_size = 13) +
labs(title = paste("Top 15 Wilayah -", var_name), x = "Wilayah", y = var_name) +
theme(panel.grid.major.y = element_blank())
}, res = 96)
output$epi_recommendations <- renderUI({ df <- std_df() if (is.null(df)) return(HTML(“Memuat…”))
top_prev <- df %>% filter(!is.na(Prevalensi_persen)) %>%
arrange(desc(Prevalensi_persen)) %>% slice_head(n = 3)
top_cfr <- df %>% filter(!is.na(CaseFatality_pct) & CaseFatality_pct > 1) %>%
arrange(desc(CaseFatality_pct)) %>% slice_head(n = 3)
recommendations <- "<ul style='font-size:14px; line-height:1.8;'>"
if(nrow(top_prev) > 0) {
recommendations <- paste0(recommendations,
"<li><strong>Prioritas Vektor Control:</strong> Fokus di ",
paste(top_prev$Wilayah, collapse = ", "), "</li>"
)
}
if(nrow(top_cfr) > 0) {
recommendations <- paste0(recommendations,
"<li><strong>Peningkatan Layanan Kesehatan:</strong> Perkuat kapasitas di ",
paste(top_cfr$Wilayah, collapse = ", "), "</li>"
)
}
recommendations <- paste0(recommendations,
"<li><strong>Surveilans Aktif:</strong> Monitoring harian</li>",
"<li><strong>Edukasi Masyarakat:</strong> Kampanye 3M Plus</li>",
"</ul>"
)
HTML(recommendations)
})
# PETA output$map_var_ui <- renderUI({ df <- std_df() req(df) num_vars <- names(df)[sapply(df, is.numeric)] priority_vars <- c(“Prevalensi_persen”, “Jumlah_DBD”, “Jumlah_Penduduk”, “CaseFatality_pct”, “Persen_Akses_Sanitasi”, “Persen_Rumah_Layak_Huni”) available_priority <- intersect(priority_vars, num_vars) other_vars <- setdiff(num_vars, priority_vars) ordered_vars <- c(available_priority, other_vars)
if (length(ordered_vars) == 0) {
return(tags$div("Tidak ada variabel numerik untuk divisualisasikan."))
}
selectInput("map_var", "Variabel:", choices = ordered_vars, selected = ordered_vars[1])
})
observeEvent(input$btn_load_map, { withProgress(message = ‘Memuat peta Jawa Barat…’, value = 0, { tryCatch({ incProgress(0.3, detail = “Mengunduh data geografis…”)
# Gunakan geodata::gadm() sebagai pengganti getData()
indo <- gadm(country = "IDN", level = 2, path = tempdir())
incProgress(0.5, detail = "Memfilter Jawa Barat…")
jabar <- indo[indo$NAME_1 == "Jawa Barat" | indo$NAME_1 == "West Java", ]
if (length(jabar) == 0) {
showNotification("Data Jawa Barat tidak ditemukan!", type = "error")
return()
}
incProgress(0.7, detail = "Memproses shapefile…")
shp <- st_as_sf(jabar)
shp$Wilayah_Original <- shp$NAME_2
shp$Wilayah <- tolower(trimws(as.character(shp$NAME_2)))
shp$Wilayah <- gsub("\\s+", " ", shp$Wilayah)
# Perbaiki nama yang salah di shapefile GADM
shp$NAME_2 <- ifelse(tolower(shp$NAME_2) == "cimahi", "Kota Cimahi", shp$NAME_2)
shp$NAME_2 <- ifelse(tolower(shp$NAME_2) == "depok", "Kota Depok", shp$NAME_2)
shp$NAME_2 <- ifelse(tolower(shp$NAME_2) == "banjar", "Kota Banjar", shp$NAME_2)
# Waduk Cirata: distribusikan ke 3 kabupaten berdasarkan lokasi geografis
# Ambil centroid waduk untuk menentukan kabupaten terdekat
waduk_idx <- grep("waduk|cirata", tolower(shp$NAME_2))
if(length(waduk_idx) > 0) {
for(idx in waduk_idx) {
waduk_geom <- shp[idx, ]
centroid <- st_centroid(st_geometry(waduk_geom))
# Cek kabupaten mana yang paling dekat (simplified logic)
# Purwakarta: utara (lat lebih tinggi, lng tengah)
# Cianjur: selatan (lat lebih rendah)
# Bandung Barat: timur (lng lebih tinggi)
coords <- st_coordinates(centroid)
if(coords[2] > -6.7) { # latitude tinggi = utara
shp$NAME_2[idx] <- "Purwakarta"
} else if(coords[1] > 107.5) { # longitude tinggi = timur
shp$NAME_2[idx] <- "Bandung Barat"
} else { # selatan
shp$NAME_2[idx] <- "Cianjur"
}
}
}
shp$Wilayah_Original <- shp$NAME_2
incProgress(1, detail = "Selesai!")
jabar_shp(shp)
showNotification("Peta berhasil dimuat! Pilih variabel untuk visualisasi.", type = "message")
}, error = function(e) {
showNotification(paste("Error memuat peta:", e$message), type = "error")
jabar_shp(NULL)
})
})
})
output\(map_leaflet <- renderLeaflet({ req(input\)map_var) df <- std_df() shp <- jabar_shp() req(df, shp)
# Cleaning wilayah untuk matching
df$Wilayah_Clean <- tolower(trimws(as.character(df$Wilayah)))
df$Wilayah_Clean <- gsub("^kab\\.?\\s*", "", df$Wilayah_Clean) # hapus "kab" di awal
df$Wilayah_Clean <- gsub("^kabupaten\\s+", "", df$Wilayah_Clean) # hapus "kabupaten" di awal
df$Wilayah_Clean <- gsub("\\s+", " ", df$Wilayah_Clean)
df$Wilayah_Clean <- trimws(df$Wilayah_Clean)
# Cleaning shapefile - JANGAN hapus "kota" karena shapefile pakai format "Kota Bandung"
shp$Wilayah_Clean <- tolower(trimws(as.character(shp$NAME_2)))
shp$Wilayah_Clean <- gsub("^kabupaten\\s+", "", shp$Wilayah_Clean)
shp$Wilayah_Clean <- gsub("\\s+", " ", shp$Wilayah_Clean)
shp$Wilayah_Clean <- trimws(shp$Wilayah_Clean)
merged <- merge(shp, df, by = "Wilayah_Clean", all.x = TRUE)
if (!(input$map_var %in% names(merged))) {
leaflet() %>%
addTiles() %>%
setView(lng = 107.9, lat = -6.9, zoom = 9) %>%
fitBounds(lng1 = 106.3, lat1 = -7.8, lng2 = 108.8, lat2 = -6.0) %>%
addPopups(lng = 107.6191, lat = -7.0051,
popup = paste("Variabel", input$map_var, "tidak ditemukan di data."))
} else {
vals <- merged[[input$map_var]]
valid_vals <- vals[!is.na(vals)]
n_valid <- length(valid_vals)
n_total <- length(vals)
n_missing <- n_total - n_valid
if (n_valid == 0) {
output$map_info <- renderUI({
HTML("<p style='color:red;'><strong>Peringatan:</strong> Tidak ada data valid untuk variabel ini.</p>")
})
leaflet() %>%
addTiles() %>%
setView(lng = 107.9, lat = -6.9, zoom = 9) %>%
fitBounds(lng1 = 106.3, lat1 = -7.8, lng2 = 108.8, lat2 = -6.0)
} else {
output$map_info <- renderUI({
HTML(paste0(
"<h5>Statistik Peta:</h5>",
"<ul style='font-size:13px;'>",
"<li><strong>Wilayah dengan data:</strong> ", n_valid, " dari ", n_total, "</li>",
"<li><strong>Min:</strong> ", round(min(valid_vals, na.rm=TRUE), 2), "</li>",
"<li><strong>Maks:</strong> ", round(max(valid_vals, na.rm=TRUE), 2), "</li>",
"<li><strong>Rata-rata:</strong> ", round(mean(valid_vals, na.rm=TRUE), 2), "</li>",
"</ul>"
))
})
pal <- colorNumeric(
palette = colorRampPalette(c(input$color_low, input$color_high))(10),
domain = merged[[input$map_var]],
na.color = "#cccccc"
)
# Buat formula untuk leaflet
fill_formula <- as.formula(paste0("~pal(", input$map_var, ")"))
legend_formula <- as.formula(paste0("~", input$map_var))
leaflet(merged) %>%
addProviderTiles("CartoDB.Positron") %>%
setView(lng = 107.9, lat = -6.9, zoom = 9) %>%
fitBounds(lng1 = 106.3, lat1 = -7.8, lng2 = 108.8, lat2 = -6.0) %>%
addPolygons(
fillColor = fill_formula,
color = "grey40",
weight = 1.5,
opacity = 1,
fillOpacity = 0.75,
highlightOptions = highlightOptions(
weight = 3,
color = "#333",
fillOpacity = 0.9,
bringToFront = TRUE
),
label = ~{
var_val <- get(input$map_var)
lapply(paste0(
"<strong>", ifelse(grepl("^kota", tolower(NAME_2)),
NAME_2,
paste0("Kabupaten ", NAME_2)), "</strong><br>",
input$map_var, ": ",
ifelse(is.na(var_val),
"<span style='color:red;'>Tidak ada data</span>",
round(var_val, 2))
), htmltools::HTML)
}
) %>%
addLegend(
pal = pal,
values = legend_formula,
title = input$map_var,
position = "bottomright",
na.label = "Tidak ada data"
)
}
}
})
output\(spatial_interpretation <- renderUI({ req(input\)map_var) df <- std_df() shp <- jabar_shp() req(df, shp)
df$Wilayah_Clean <- tolower(trimws(gsub("^(kab\\.?|kabupaten)\\s+", "", as.character(df$Wilayah))))
df$Wilayah_Clean <- gsub("\\s+", " ", trimws(df$Wilayah_Clean))
shp$Wilayah_Clean <- tolower(trimws(gsub("^kabupaten\\s+", "", as.character(shp$NAME_2))))
shp$Wilayah_Clean <- gsub("\\s+", " ", trimws(shp$Wilayah_Clean))
merged <- merge(shp, df, by = "Wilayah_Clean", all.x = TRUE)
var_name <- input$map_var
vals <- merged[[var_name]]
valid_vals <- vals[!is.na(vals)]
if (length(valid_vals) == 0) {
return(HTML("<p>Tidak dapat memberikan interpretasi karena tidak ada data valid.</p>"))
}
q <- quantile(valid_vals, probs = c(0.25, 0.5, 0.75), na.rm = TRUE)
high_threshold <- q[3]
low_threshold <- q[1]
high_regions <- merged[!is.na(vals) & vals >= high_threshold, ]
low_regions <- merged[!is.na(vals) & vals <= low_threshold, ]
high_names <- if(nrow(high_regions) > 0) {
names_formatted <- sapply(head(high_regions$NAME_2, 5), function(nm) {
if(grepl("^kota", tolower(nm))) return(nm)
else return(paste0("Kabupaten ", nm))
})
paste(names_formatted, collapse = ", ")
} else "Tidak ada"
low_names <- if(nrow(low_regions) > 0) {
names_formatted <- sapply(head(low_regions$NAME_2, 5), function(nm) {
if(grepl("^kota", tolower(nm))) return(nm)
else return(paste0("Kabupaten ", nm))
})
paste(names_formatted, collapse = ", ")
} else "Tidak ada"
cv <- sd(valid_vals) / mean(valid_vals) * 100
cluster_pattern <- if(cv > 50) {
"Terdapat <strong>variasi tinggi</strong> antar wilayah, menunjukkan kemungkinan <strong>cluster/pengelompokan</strong> penyakit di wilayah tertentu."
} else if(cv > 25) {
"Terdapat <strong>variasi sedang</strong> antar wilayah."
} else {
"Distribusi relatif <strong>merata</strong> antar wilayah."
}
interpretation <- paste0(
"<h4>Interpretasi Pola Spasial: ", var_name, "</h4>",
"<div style='font-size:14px; line-height:1.8;'>",
"<p><strong>Pola Distribusi:</strong><br>", cluster_pattern, "</p>",
"<p><strong>Wilayah dengan nilai TINGGI</strong> (Q3 = ", round(high_threshold, 2), ") meliputi:<br>",
"<em>", high_names, "</em></p>",
"<p><strong>Wilayah dengan nilai RENDAH</strong> (Q1 = ", round(low_threshold, 2), ") meliputi:<br>",
"<em>", low_names, "</em></p>",
"<p><strong>Rekomendasi:</strong><br>",
if(var_name == "Prevalensi_per100k" || var_name == "Jumlah_DBD") {
"Wilayah dengan prevalensi tinggi memerlukan intervensi prioritas seperti fogging, pemberantasan sarang nyamuk, dan edukasi masyarakat."
} else if(var_name == "Persen_Akses_Sanitasi") {
"Wilayah dengan akses sanitasi rendah perlu program perbaikan sanitasi lingkungan untuk mengurangi risiko DBD."
} else if(var_name == "CaseFatality_pct") {
"Wilayah dengan case fatality rate tinggi memerlukan peningkatan kualitas layanan kesehatan dan deteksi dini."
} else {
"Fokuskan intervensi kesehatan pada wilayah dengan nilai ekstrem (tinggi atau rendah)."
},
"</p>",
"</div>"
)
HTML(interpretation)
})
observe({ if (!exists(“DATA_FILE”)) { showModal(modalDialog( title = “Instruksi singkat”, HTML(“Data belum dimuat. Pastikan sudah menjalankan script utama untuk load data terlebih dahulu.”), easyClose = TRUE, footer = NULL )) } })
# ✅ HEATMAP - PLOT output$heatmap_plot <- renderPlot({ df_tren <- tren_df() req(df_tren)
# Transform data ke format long
dbd_long <- df_tren %>%
pivot_longer(cols = -Wilayah, names_to = "Tahun", values_to = "Kasus_DBD") %>%
mutate(
Tahun = as.numeric(Tahun),
wilayah = Wilayah
)
# Filter top N wilayah berdasarkan total kasus
top_wilayah <- dbd_long %>%
group_by(wilayah) %>%
summarise(Total = sum(Kasus_DBD, na.rm = TRUE)) %>%
arrange(desc(Total)) %>%
slice_head(n = input$heatmap_top_n) %>%
pull(wilayah)
dbd_long_filtered <- dbd_long %>%
filter(wilayah %in% top_wilayah)
# Base plot
p <- ggplot(dbd_long_filtered, aes(x = Tahun, y = reorder(wilayah, Kasus_DBD), fill = Kasus_DBD)) +
geom_tile(color = "white", size = 0.5) +
scale_fill_gradient(
low = input$heatmap_color_low,
high = input$heatmap_color_high,
na.value = "gray90"
) +
theme_minimal(base_size = 13) +
labs(
title = "Heatmap Distribusi Kasus DBD di Jawa Barat",
x = "Tahun",
y = "Kabupaten/Kota",
fill = "Kasus DBD"
) +
theme(
axis.text.y = element_text(size = 9),
axis.text.x = element_text(size = 11),
plot.title = element_text(hjust = 0.5, face = "bold", size = 16),
legend.position = "right"
)
# Tambahkan angka jika diminta
if(input$heatmap_show_values) {
p <- p + geom_text(aes(label = ifelse(is.na(Kasus_DBD), "", comma(Kasus_DBD))),
size = 2.5, color = "black")
}
p
}, res = 96)
# ✅ HEATMAP - INTERPRETASI output$heatmap_interpretation <- renderUI({ df_tren <- tren_df() req(df_tren)
dbd_long <- df_tren %>%
pivot_longer(cols = -Wilayah, names_to = "Tahun", values_to = "Kasus_DBD") %>%
mutate(Tahun = as.numeric(Tahun))
# Cari wilayah dengan kasus tertinggi per tahun
top_per_year <- dbd_long %>%
group_by(Tahun) %>%
slice_max(Kasus_DBD, n = 1) %>%
ungroup()
# Cari wilayah dengan total kasus tertinggi
top_overall <- dbd_long %>%
group_by(Wilayah) %>%
summarise(Total = sum(Kasus_DBD, na.rm = TRUE)) %>%
arrange(desc(Total)) %>%
slice_head(n = 5)
# Analisis tren
trend_analysis <- dbd_long %>%
group_by(Tahun) %>%
summarise(Total_Tahun = sum(Kasus_DBD, na.rm = TRUE)) %>%
arrange(Tahun)
max_year <- trend_analysis %>% slice_max(Total_Tahun, n = 1)
min_year <- trend_analysis %>% slice_min(Total_Tahun, n = 1)
HTML(paste0(
"<div class='interpretation-box' style='background:#fef2f2; border-left-color:#dc2626;'>",
"<h4 style='color:#991b1b;'>🔥 Hotspot & Tren Kasus DBD</h4>",
"<h5 style='margin-top:15px;'>📍 Wilayah dengan Total Kasus Tertinggi:</h5>",
"<ol>",
paste(sapply(1:nrow(top_overall), function(i) {
paste0("<li><strong>", top_overall$Wilayah[i], "</strong>: ",
comma(top_overall$Total[i]), " kasus</li>")
}), collapse = ""),
"</ol>",
"<h5 style='margin-top:15px;'>📅 Puncak Kasus per Tahun:</h5>",
"<ul>",
paste(sapply(1:nrow(top_per_year), function(i) {
paste0("<li><strong>", top_per_year$Tahun[i], ":</strong> ",
top_per_year$Wilayah[i], " (", comma(top_per_year$Kasus_DBD[i]), " kasus)</li>")
}), collapse = ""),
"</ul>",
"<h5 style='margin-top:15px;'>📊 Tren Temporal:</h5>",
"<ul>",
"<li><strong>Tahun dengan kasus tertinggi:</strong> ", max_year$Tahun,
" (", comma(max_year$Total_Tahun), " kasus total)</li>",
"<li><strong>Tahun dengan kasus terendah:</strong> ", min_year$Tahun,
" (", comma(min_year$Total_Tahun), " kasus total)</li>",
"</ul>",
"<div style='background:#fef9c3; padding:10px; border-radius:5px; margin-top:15px;'>",
"<strong>💡 Rekomendasi:</strong>",
"<ul style='margin-top:5px;'>",
"<li>Wilayah dengan warna <strong style='color:", input$heatmap_color_high,
";'>merah gelap</strong> memerlukan intervensi prioritas</li>",
"<li>Pola clustering temporal menunjukkan outbreak musiman</li>",
"<li>Fokuskan surveilans aktif di wilayah hotspot persisten</li>",
"</ul>",
"</div>",
"</div>"
))
})
# TREN WAKTU output$tren_info <- renderUI({ df <- tren_df() if (is.null(df)) { return(HTML(“Data tren waktu belum dimuat.
“)) }
year_cols <- names(df)[names(df) != "Wilayah"]
HTML(paste0(
"<p style='font-size:14px;'>",
"<strong>Data tren tersedia untuk:</strong><br>",
"• Jumlah wilayah: ", nrow(df), "<br>",
"• Periode: ", paste(year_cols, collapse = ", "),
"</p>"
))
})
output\(tren_wilayah_ui <- renderUI({ df <- tren_df() if (is.null(df)) return(tags\)div(“Data tren belum dimuat”))
wilayah_list <- sort(unique(df$Wilayah))
selectInput("tren_wilayah",
"Pilih Wilayah (bisa pilih beberapa):",
choices = wilayah_list,
selected = wilayah_list[1:min(3, length(wilayah_list))],
multiple = TRUE)
})
output\(tren_plot <- renderPlot({ df <- tren_df() req(df, input\)tren_wilayah)
plot_data <- df %>%
filter(Wilayah %in% input$tren_wilayah) %>%
pivot_longer(cols = -Wilayah, names_to = "Tahun", values_to = "Jumlah_Kasus") %>%
mutate(Tahun = as.numeric(Tahun))
if(input$tren_show_avg) {
avg_data <- df %>%
select(-Wilayah) %>%
summarise(across(everything(), ~mean(., na.rm = TRUE))) %>%
mutate(Wilayah = "RATA-RATA JABAR") %>%
pivot_longer(cols = -Wilayah, names_to = "Tahun", values_to = "Jumlah_Kasus") %>%
mutate(Tahun = as.numeric(Tahun))
plot_data <- bind_rows(plot_data, avg_data)
}
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 = comma) +
labs(
title = "Tren Kasus DBD Antar Tahun",
x = "Tahun",
y = "Jumlah Kasus DBD",
color = "Wilayah"
) +
theme_minimal(base_size = 13) +
theme(
plot.title = element_text(face = "bold", size = 16),
legend.position = "right"
)
if(input$tren_show_trend) {
p <- p + geom_smooth(method = "lm", se = FALSE, linetype = "dashed", alpha = 0.5)
}
p
}, res = 96)
output$tren_table <- renderDT({ df <- tren_df() if (is.null(df)) return(datatable(data.frame(Message = “Data tren belum dimuat”)))
req(input$tren_wilayah)
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)
) %>%
ungroup()
datatable(table_data, options = list(pageLength = 10, scrollX = TRUE))
})
output\(tren_interpretation <- renderUI({ df <- tren_df() req(df, input\)tren_wilayah)
interpretations <- lapply(input$tren_wilayah, function(wil) {
wil_data <- df %>%
filter(Wilayah == wil) %>%
select(-Wilayah) %>%
pivot_longer(everything(), names_to = "Tahun", values_to = "Kasus") %>%
mutate(Tahun = as.numeric(Tahun))
if(nrow(wil_data) >= 2) {
model <- lm(Kasus ~ Tahun, data = wil_data)
slope <- coef(model)[2]
trend_status <- if(slope > 50) {
"<span style='color:red;'><b>MENINGKAT SIGNIFIKAN</b></span>"
} else if(slope > 0) {
"<span style='color:orange;'><b>Meningkat</b></span>"
} else if(slope > -50) {
"<span style='color:blue;'><b>Menurun</b></span>"
} else {
"<span style='color:green;'><b>MENURUN SIGNIFIKAN</b></span>"
}
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)
paste0(
"<li><b>", wil, ":</b> Tren ", trend_status, " (",
ifelse(pct_change > 0, "+", ""), pct_change, "% perubahan)</li>"
)
} else {
paste0("<li><b>", wil, ":</b> Data tidak cukup</li>")
}
})
HTML(paste0(
"<div style='font-size:14px;'>",
"<h4>📈 Analisis Tren:</h4>",
"<ul>", paste(interpretations, collapse = ""), "</ul>",
"</div>"
))
}) }
shinyApp(ui, server)$