LAPORAN PROYEK UJIAN TENGAH SEMESTER
ANALISIS EPIDEMIOLOGI
DEMAM
BERDARAH DENGUE (DBD) JAWA BARAT
EPIDEMIOLOGI 2025
Disusun Oleh:
Farah Nadira
NPM:
140610230015
Dosen Mata Kuliah:
Dr. I Gede Nyoman Mindra Jaya,
M.Si.
NIP: 19800603 200312 1 002
Demam Berdarah Dengue (DBD) merupakan salah satu penyakit menular di Provinsi Jawa Barat dengan jumlah kasus yang meningkat drastis dari tahun 2023. Penelitian ini bertujuan untuk mendeskripsikan pola spasial kasus DBD tahun 2024 serta menganalisis hubungan faktor lingkungan terhadap peningkatan kejadian DBD. Data yang digunakan meliputi jumlah kasus dan kematian akibat DBD, jumlah penduduk, kejadian banjir, persentase rumah tangga dengan sanitasi layak, dan persentase rumah layak huni pada 27 kabupaten/kota. Analisis dilakukan menggunakan pendekatan deskriptif epidemiologi melalui ukuran frekuensi berupa prevalensi dan Case Fatality Rate (CFR), serta pemetaan spasial untuk melihat pola distribusi antarwilayah. Hasil menunjukkan bahwa faktor lingkungan seperti curah hujan yang memicu banjir, sanitasi yang kurang memadai, kondisi rumah yang tidak layak huni, serta kepadatan penduduk berkontribusi terhadap peningkatan potensi perkembangbiakan nyamuk Aedes dan risiko penularan DBD. Secara spasial, pola distribusi kasus memperlihatkan bahwa wilayah Jawa Barat bagian tengah dan barat memiliki intensitas kasus yang lebih tinggi dibandingkan wilayah timur. Kota Bandung tercatat memiliki jumlah kasus tertinggi, Kota Sukabumi memiliki prevalensi tertinggi, sedangkan Kota Cimahi memiliki CFR tertinggi meskipun jumlah kasusnya tidak termasuk besar. Perbedaan ini menunjukkan bahwa tingginya prevalensi lebih berkaitan dengan faktor lingkungan dan risiko paparan, sedangkan tingginya CFR menggambarkan kapasitas deteksi dini dan layanan kesehatan. Temuan penelitian ini menekankan pentingnya intervensi berbasis lingkungan, perbaikan sanitasi, pengelolaan risiko banjir, serta peningkatan kualitas layanan kesehatan untuk menurunkan angka kejadian dan kematian akibat DBD di Jawa Barat.
Demam Berdarah Dengue (DBD) merupakan penyakit infeksi virus Dengue yang ditularkan melalui gigitan nyamuk Aedes baik Aedes aegypti maupun Aedes albopictus. Indonesia sebagai negara dengan iklim tropis menjadi daerah yang tepat untuk nyamuk Aedes berkembang biak. Selain itu, Indonesia dengan 285,7 juta jiwa yang menempati posisi keempat sebagai negara terpadat di dunia dengan kepadatan penduduk diperkirakan mencapai 158 jiwa per km^2 menjadi salah satu alasan memperkuat tersebarnya penyakit DBD di Indonesia akibat lingkungan yang padat dan mobilitas penduduk yang tinggi.
Jawa Barat sebagai provinsi terpadat kedua di Indonesia pun diketahui menjadi provinsi dengan jumlah kasus DBD tertinggi di Indonesia, berdasarkan data yang diambil dari Open Data Jabar yakni sebanyak 61.423 kasus di tahun 2024, meningkat 217,8% dari tahun 2023 yang hanya berjumlah 19.328 kasus yang tersebar di 27 kabupaten/kota di Provinsi Jawa Barat. Selain jumlah kasus yang tinggi, Jawa Barat juga merupakan provinsi dengan jumlah kematian akibat DBD tertinggi di Indonesia, yakni sebanyak 339 kasus kematian.
Jumlah kasus meningkat dan kematian yang tinggi tersebut menimbulkan kekhawatiran khususnya bagi pemerintah dan fasilitas kesehatan dan menunjukkan bahwa upaya Pemberantasan Sarang Nyamuk (PSN) di masyarakat masih belum optimal. Selain itu, perubahan iklim dan perilaku masyarakat juga diduga memperparah situasi ini. Hal-hal tersebut kemudian menjadi faktor dari urgensinya penelitian Demam Berdarah Dengue (DBD) di Jawa Barat yang kemudian hasilnya dapat digunakan oleh pihak berwenang terkait untuk mengurangi angka kenaikan kasus DBD dan mengetahui kabupaten/kota apa yang memerlukan perhatian khusus dan harus diaudit penanganannya akibat jumlah kematian yang juga tinggi.
Berdasarkan uraian latar belakang tersebut, didapat rumusan masalah dalam penelitian ini antara lain:
Dari rumusan masalah di atas, tujuan penelitian yang akhirnya dijadikan dasar dalam penelitian ini antara lain:
Epidemiologi adalah cabang ilmu kesehatan masyarakat yang mempelajari kejadian penyakit dalam suatu populasi, termasuk seberapa sering penyakit muncul (frekuensi), bagaimana pola penyebarannya berdasarkan waktu, tempat, dan karakteristik orang (distribusi), serta faktor-faktor yang memengaruhinya (determinan). Secara etimologi, istilah ini berasal dari bahasa Yunani epi (pada), demos (penduduk), dan logos (ilmu). Dalam praktiknya, epidemiologi digunakan untuk mengidentifikasi penyebab penyakit, memahami mekanisme penularan, serta merancang upaya pencegahan dan pengendalian agar masalah kesehatan dapat diminimalkan secara efektif di tingkat populasi.
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.
Di dalam studi epidemiologi terdapat dua jenis ukuran, yakni ukuran frekuensi yang digunakan untuk mengetahui frekuensi atau seberapa sering suatu penyakit terjadi dan ukuran asosiasi yang digunakan untuk mengetahui seberapa kuat hubungan faktor risiko dengan kejadian penyakit tersebut.
Baik ukuran frekuensi maupun ukuran asosiasi sangat penting untuk membantu pihak terkait dalam memahami pola penyakit dan menentukan prioritas dari penyelesaian faktor-faktor yang paling erat hubungannya.
Ukuran frekuensi menggambarkan seberapa sering suatu penyakit atau kejadian kesehatan terjadi pada populasi tertentu dalam periode waktu tertentu.
Prevalensi menunjukkan berapa banyak orang yang sakit (baik kasus
baru maupun lama) pada satu waktu tertentu. Biasanya digunakan untuk
mengukur beban penyakit di masyarakat.
…(1)
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.
x 100%
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.
Case Fatality Rate (CFR) menunjukkan persentase penderita yang
meninggal akibat penyakit tersebut. CFR sering digunakan untuk mengukur
tingkat keparahan penyakit, misalnya kalau CFR-nya tinggi berarti
penyakit itu cenderung berbahaya
…(2)
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.
Relative Risk (RR) digunakan pada penelitian kohort untuk
membandingkan risiko penyakit antara kelompok terpapar dan tidak
terpapar.
Odds Ratio (OR) digunakan pada penelitian kasus-kontrol untuk
membandingkan peluang paparan antara kelompok kasus dan kontrol.
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.
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.
Cross-sectional study atau studi potong lintang
Desain ini mengukur paparan dan penyakit pada waktu yang sama, sehingga
memberikan gambaran 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.
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.
Cohort study atau studi kohort
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 kemampuannya menentukan hubungan
sebab-akibat serta mengukur beberapa outcome sekaligus. Namun,
kekurangannya adalah membutuhkan waktu lama, biaya besar, dan risiko
kehilangan partisipan selama periode pengamatan.
Randomized Controlled Trial (RCT)
RCT termasuk dalam kategori studi eksperimental. Pada desain ini,
peserta dibagi secara acak menjadi dua kelompok, yaitu kelompok
intervensi dan kelompok kontrol. Tujuannya adalah menilai efektivitas
suatu intervensi atau terapi dengan meminimalkan bias melalui
randomisasi dan kontrol. RCT dianggap sebagai desain dengan tingkat
bukti paling kuat dalam menentukan hubungan sebab-akibat, meskipun dalam
pelaksanaannya membutuhkan biaya besar, waktu lama, serta pengawasan
etika yang ketat.
Data yang digunakan merupakan data sekunder yang berkaitan dengan Demam Berdarah Dengue (DBD) di Provinsi Jawa Barat dengan 27 kabupaten/kota tahun 2024 yang bersumber dari sumber resmi seperti Badan Pusat Statistik (BPS) dan Open Data Provinsi Jawa Barat.
Penelitian ini menggunakan beberapa variabel yang berkaitan dengan kejadian Demam Berdarah Dengue (DBD) di Provinsi Jawa Barat. Variabel-variabel tersebut dibagi menjadi dua kelompok, yaitu variabel dependen (terikat) dan variabel independen (bebas). Variabel dependen dalam penelitian ini adalah jumlah kasus DBD di setiap kabupaten/kota di Jawa Barat, yang menggambarkan tingkat kejadian penyakit dan menjadi fokus utama analisis. Selain itu, jumlah kasus meninggal akibat DBD juga digunakan sebagai indikator tingkat keparahan.
Sementara itu, variabel independen meliputi beberapa faktor lingkungan dan sosial yang diduga berhubungan dengan penyebaran DBD. Variabel-variabel tersebut antara lain:
Jumlah penduduk di setiap kabupaten/kota, yang digunakan sebagai dasar perhitungan angka prevalensi. Jumlah penduduk sering kali berkaitan dengan peningkatan risiko penularan penyakit menular, termasuk DBD.
Jumlah kejadian bencana banjir, yang mencerminkan kondisi lingkungan yang lembap dan berpotensi menimbulkan genangan air pasca banjir, tempat ideal bagi nyamuk Aedes untuk berkembang biak.
Persentase rumah tangga yang memiliki akses terhadap sanitasi layak, sebagai indikator kebersihan lingkungan dan potensi terbentuknya genangan baru akibat sistem sanitasi yang kurang baik.
Persentase rumah tangga yang menempati rumah layak huni, yang menunjukkan kondisi fisik perumahan masyarakat. Rumah yang tidak layak huni dapat meningkatkan risiko kontak dengan nyamuk pembawa virus DBD.
Hubungan antara variabel-variabel tersebut dianalisis untuk melihat apakah terdapat keterkaitan antara kondisi lingkungan, sosial, dan jumlah kasus DBD di Jawa Barat. Dengan mengetahui variabel yang berpengaruh, diharapkan hasil penelitian ini dapat memberikan gambaran faktor risiko utama yang berkontribusi terhadap tingginya angka DBD di wilayah tersebut.
Kemudian digunakan juga variabel bantu untuk mencari ukuran frekuensi dari penyakit DBD di Provinsi Jawa Barat, adapun variabel tersebut antara lain:
Penelitian ini menggunakan pendekatan analisis deskriptif epidemiologi untuk memahami pola penyebaran dan faktor yang berhubungan dengan kejadian Demam Berdarah Dengue (DBD) di Provinsi Jawa Barat. Analisis deskriptif dipilih karena sesuai untuk menggambarkan karakteristik penyakit berdasarkan waktu, tempat, dan populasi tanpa memanipulasi variabel. Analisis dilakukan melalui ukuran frekuensi untuk melihat seberapa besar penyebaran DBD pada tiap wilayah dengan menghitung prevalensi dan case fatality rate (CFR), ini membantu menggambarkan tingkat kejadian dan keparahan penyakit di populasi. Selain itu, digunakan statistika deskriptif untuk memperjelas distribusi dan karakteristik data, serta visualisasi peta dan grafik untuk memperlihatkan pola spasial antarwilayah.
Dengan metode ini, hasil analisis diharapkan mampu memberikan gambaran yang lebih jelas tentang hubungan antara kondisi lingkungan, sosial, dan tingkat kejadian DBD di Jawa Barat. Untuk menerapkan metode tersebut secara sistematis, penelitian ini mengikuti beberapa tahapan analisis yang dijabarkan pada bagian alur kerja.
Alur kerja penelitian ini dimulai dari tahap pengumpulan data sekunder yang bersumber dari Open Data Jabar dan Badan Pusat Statistik (BPS). Data yang dikumpulkan meliputi jumlah kasus dan kematian akibat DBD, jumlah penduduk, serta beberapa faktor lingkungan seperti kejadian banjir, sanitasi, dan kondisi rumah di tiap kabupaten/kota di Jawa Barat.
Selanjutnya dilakukan tahap pengolahan data, seperti pengecekan kesesuaian format, penyatuan nama wilayah, serta pembuatan variabel turunan agar data siap dianalisis. Setelah data bersih, dilakukan analisis statistika deskriptif untuk memberikan gambaran umum mengenai sebaran data, seperti nilai rata-rata, minimum, maksimum, dan persebaran kasus DBD di tiap kabupaten/kota.
Kemudian dilanjutkan dengan perhitungan ukuran frekuensi untuk melihat pola penyebaran DBD di wilayah Jawa Barat. Ukuran frekuensi yang digunakan meliputi prevalensi, yang menunjukkan proporsi penduduk yang terjangkit DBD; angka insidensi, yang menggambarkan laju munculnya kasus baru; serta case fatality rate (CFR), yang menunjukkan tingkat keparahan penyakit berdasarkan perbandingan jumlah kematian dan total kasus. Tahap berikutnya adalah analisis ukuran asosiasi untuk menilai seberapa besar pengaruh faktor lingkungan dan sosial terhadap peningkatan kasus DBD di tiap wilayah.
Tahap akhir adalah visualisasi dan interpretasi hasil. Data yang telah dianalisis disajikan dalam bentuk grafik dan peta distribusi guna menggambarkan pola spasial antarwilayah. Dari hasil tersebut, dilakukan interpretasi untuk memahami kecenderungan, wilayah dengan kasus tinggi, serta faktor risiko yang mungkin berperan dalam penyebaran DBD di Jawa Barat.
Jumlah Kasus Demam Berdarah Dengue (DBD) di Provinsi Jawa Barat terhitung sangat tinggi dengan Kota Bandung sebagai Kab/Kota dengan jumlah kasus tertinggi di angka sekitar 7,680 kasus, kemudian di bawahnya kota Depok dan Bekasi dengan angka kasus yang cukup jauh yakni Depok sebanyak 5,040 kasus dan Bekasi sebanyak 4,167 kasus.
Dapat dilihat dari boxplot di atas, mayoritas kabupaten/kota berada di kisaran 900 sampai 3200 kasus jumlah DBD yang berarti utamanya sebaran data jumlah kasus DBD di Provinsi Jawa Barat berada pada rentang tersebut. Namun, dapat dilihat terdapat satu outlier dengan jumlah kasus yang sangat tinggi yakni sekitar 7600 kasus yang menandakan adanya satu wilayah dengan beban penanganan DBD jauh lebih besar dibandingkan wilayah lainnya.
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 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. Beberapa faktor yang mempengaruhi kerentanan tubuh host seperti golongan darah, jenis kelamin, status gizi, dan sebagainya juga dapat mempengaruhi terjangkitnya host (manusia) dari penyakit DBD.
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. Lingkungan dengan suhu yang tinggi juga dapat mempercepat perkembangbiakan nyamuk.
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.
Berikut merupakan diagram dari model segitiga epidemiologi (Agent, Host, Environment) yang menjelaskan bagaimana faktor lingkungan, host (manusia), dan agent (virus dengue) saling berinteraksi hingga menyebabkan kasus Demam Berdarah Dengue.
Dari diagram di atas, dapat dilihat bahwa ketiga faktor berperan dan saling berhubungan terhadap terjadinya kasus penyakit DBD. Untuk Environment sendiri terbagi ke dalam beberapa variabel yang memungkinkan, antara lain:
Kelima variabel dari environment tersebut berujung pada peningkatan jumlah nyamuk Aedes. Khusus untuk variabel kepadatan penduduk juga berhubungan langsung ke faktor host, yaitu manusia. Pada kawasan dengan pemukiman yang rapat dan mobilitas masyarakat yang tinggi, nyamuk lebih mudah menemukan inang untuk menggigit dan memperoleh darah, sehingga peluang penularan virus dengue antarindividu meningkat. Peningkatan jumlah nyamuk Aedes juga akan menambah vektor (agent) Dengue virus. Selain itu, tingginya jumlah penduduk juga menyebabkan penyebaran infeksi dapat berlangsung lebih cepat ketika terdapat kasus positif, karena siklus nyamuk-manusia-nyamuk lebih mudah terjadi.
Akibat keterbatasan data yang ada, ukuran frekuensi yang dapat dihitung pada analisis kali ini hanya prevalensi dan Case Fatality Rate (CFR).
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.
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.
Akibat keterbatasan data, tidak ada ukuran asosiasi yang dapat dihitung dari data yang ada.
Berdasarkan peta sebaran jumlah kasus DBD, terdapat daerah berwarna gelap berwarna ungu tua yang merupakah wilayah Kota Bandung dengan jumlah kasus di atas 6000, ini menunjukkan Kota Bandung sebagai wilayah dengan jumlah kasus DBD tertinggi di Provinsi Jawa Barat. Kemudian daerah seperti Kota Bekasi dan Kota Depok berwarna merah-oranye gelap yang berarti jumlah kasus termasuk tinggi dan berada di wilayah metropolitan bagian barat. Sementara wilayah Selatan Jawa Barat tampak berwarna lebih kuning menunjukkan jumlah kasus relatif rendah yakni di bawah 3000, ini sesuai dengan kepadatan penduduk wilayah Selatan Jawa Barat yang juga lebih rendah serta lingkungan yang lebih rural, sehingga potensi kontak antara vektor atau Dengue virus dengan manusia lebih rendah.
Berdasarkan peta sebaran jumlah penduduk, terdapat daerah berwarna hijau sangat gelap yang merupakan wilayah Kota Bekasi dengan jumlah penduduk di atas 5 juta jiwa, ini menunjukkan Kota Bekasi sebagai wilayah dengan jumlah penduduk tertinggi di Provinsi Jawa Barat. Kemudian daerah seperti Kabupaten Bogor dan Kabupaten Bandung tampak berwarna hijau gelap yang berarti jumlah penduduk termasuk tinggi dan berada pada wilayah dengan konsentrasi pemukiman besar. Sementara wilayah Selatan Jawa Barat tampak berwarna lebih terang menunjukkan jumlah penduduk relatif rendah yakni di bawah 2 juta jiwa. Selain itu, terlihat bahwa wilayah Jawa Barat bagian timur juga didominasi warna hijau muda, yang menunjukkan jumlah penduduk relatif rendah, menandakan bahwa kawasan timur Jawa Barat memiliki distribusi penduduk yang lebih sedikit dan tersebar dibandingkan wilayah tengah hingga barat yang merupakan pusat aktivitas dan pemukiman padat.
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. 10Warna 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.
Pada peta kematian DBD, warna lebih gelap menandakan jumlah kematian lebih tinggi. Daerah seperti Kab. Bandung (38 kematian), Kota Bandung (31 kematian), Kab. Bogor (23 kematian), dan Subang (25 kematian) tampak lebih gelap karena mencatat kematian lebih banyak dibanding wilayah lain. Sementara daerah seperti Pangandaran (0 kematian) dan Indramayu (2 kematian) terlihat lebih terang, menunjukkan angka kematian yang sangat rendah.
Beberapa wilayah dengan prevalensi tinggi seperti Kota Sukabumi justru tidak terlalu berwarna gelap (6 kematian) yang mengindikasikan penanganan klinis yang cukup baik. Secara umum, pola ini menunjukkan bahwa angka kasus tinggi tidak selalu berujung pada angka kematian tinggi, sehingga faktor layanan kesehatan dan deteksi dini memegang peran penting dalam menekan fatalitas DBD.
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.
Dilakukan penelitian dengan menggunakan desain cross-sectional (potong lintang) di mana unit analisis adalah kabupaten/kota di Provinsi Jawa Barat pada tahun 2024. Penelitian ini dilakukan untuk mengetahui hubungan antara variabel akses sanitasi, jumlah banjir, dan akses rumah layak huni terhadap jumlah kasus DBD di Provinsi Jawa Barat.
Variabel utama yang digunakan adalah variabel Jumlah Kasus DBD per kabupaten/kota di Provinsi Jawa Barat dengan total 27 kabupaten/kota. Sedangkan variabel lainnya adalah variabel akses sanitasi, jumlah banjir, dan akses rumah layak huni terhadap jumlah kasus DBD di Provinsi Jawa Barat.
Semua data yang digunakan 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.
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.
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.
Faktor lingkungan memiliki peran penting dalam proses terjadinya peningkatan kasus DBD. Suhu yang tinggi mempercepat siklus hidup nyamuk Aedes, sementara curah hujan dan banjir menciptakan banyak genangan air baru yang menjadi tempat ideal bagi nyamuk berkembang biak. Kondisi sanitasi yang buruk turut memperbanyak tempat perindukan nyamuk, terutama di wilayah padat penduduk yang memiliki banyak wadah penampungan air. Ketika curah hujan tinggi, volume genangan dan kelembapan lingkungan meningkat, memperpanjang umur nyamuk dewasa dan jumlah penduduk yang tinggi mempercepat penularan virus.
Untuk pola spasial menunjukkan bahwa wilayah Jawa Barat bagian tengah dan barat memiliki intensitas kasus yang lebih tinggi dibandingkan bagian timur dan selatan. Warna yang lebih muda pada peta distribusi di wilayah timur menandakan rendahnya jumlah kasus, sejalan dengan karakter wilayah yang lebih rural dan kepadatan penduduk yang lebih rendah. Hasil interpretasi spasial juga memperlihatkan bahwa kombinasi antara kepadatan penduduk tinggi dan curah hujan besar berhubungan dengan peningkatan populasi nyamuk Aedes aegypti, sehingga memperbesar potensi penularan DBD. Dengan demikian, wilayah perkotaan dengan kondisi lingkungan padat dan curah hujan tinggi menjadi area dengan risiko paling tinggi terhadap kejadian DBD.
Hasil analisis juga menunjukkan bahwa Kota Bandung memiliki jumlah kasus DBD terbanyak, Kota Sukabumi memiliki prevalensi tertinggi, dan Kota Cimahi mencatat Case Fatality Rate (CFR) tertinggi, meskipun jumlah kasusnya tidak termasuk yang paling tinggi. Hal ini menunjukkan bahwa masih terdapat kekurangan dalam penanganan kasus DBD di Kota Cimahi sehingga perlu difokuskan untuk meningkatkan pengendalian atau penanganan kesehatan atas kasus DBD di Kota Cimahi. Kemudian dibutuhkan proses pencegahan yang baik di Kota Bandung sebagai kabupaten/kota dengan jumlah kasus DBD tertinggi dan Kota Sukabumi sebagai kabupaten/kota dengan angka prevalensi tertinggi.
Untuk menurunkan risiko penularan DBD, pemerintah daerah perlu memperkuat intervensi lingkungan sebagai langkah pencegahan. Upaya ini mencakup perbaikan sanitasi, pengelolaan sampah dan drainase, pengurangan genangan air, serta peningkatan kegiatan PSN 3M Plus terutama di wilayah dengan kepadatan penduduk tinggi. Untuk daerah seperti Kota Bandung dan Kota Sukabumi yang menunjukkan jumlah kasus dan prevalensi tinggi, program pengendalian vektor harus dilakukan secara lebih intensif dan berkelanjutan, termasuk edukasi masyarakat, pemantauan jentik berkala, serta peningkatan partisipasi warga dalam menjaga kebersihan lingkungan.
Sementara itu, daerah dengan CFR tinggi seperti Kota Cimahi memerlukan penguatan aspek layanan kesehatan. Hal ini dapat dilakukan melalui peningkatan kapasitas tenaga kesehatan dalam deteksi dini dan tata laksana kasus DBD, penyediaan fasilitas rawat inap yang memadai pada periode puncak kasus, serta memastikan sistem rujukan berjalan cepat dan efektif.
Liu, Z., Zhang, Q., Li, L., He, J., Guo, J., Wang, Z., Huang, Y., Xi, Z., Yuan, F., Li, Y., & Li, T. (2023). The effect of temperature on dengue virus transmission by Aedes mosquitoes. Frontiers in Cellular and Infection Microbiology, 13. https://doi.org/10.3389/fcimb.2023.1242173.
Yani, A., Batang, J., No, H., 58, B., Sunggal, K., & , S. (2024). The Influence of Environmental Factors on the Development of Dengue Fever. The International Science of Health Journal. https://doi.org/10.59680/ishel.v2i4.1569.
Rahmawati, Y., Jamil, I., & Hidayah, I. (2025). A Spatial Analysis on Heterogenous Determinant of Dengue Fever Cases in Indonesia. Journal of Geovisualization and Spatial Analysis. https://doi.org/10.1007/s41651-024-00212-1.
Sajib, A., Akter, S., Saha, G., & Hossain, Z. (2024). Demographic-environmental effect on dengue outbreaks in 11 countries. PLOS ONE, 19. https://doi.org/10.1371/journal.pone.0305854.
Sutriyawan, A., Martini, M., Sutiningsih, D., Akbar, H., Agushybana, F., Wahyuningsih, N., Nurlaela, S., & Eneojo, A. (2025). Spatial analysis of dengue incidence and linear effects with climate conditions in Bandung City Indonesia in 2021-2023. Journal of Public Health and Development. https://doi.org/10.55131/jphd/2025/230119.
Ramadona, A., Tozan, Y., Lazuardi, L., & Rocklöv, J. (2019). A combination of incidence data and mobility proxies from social media predicts the intra-urban spread of dengue in Yogyakarta, Indonesia. PLoS Neglected Tropical Diseases, 13. https://doi.org/10.1371/journal.pntd.0007298.
Khan, M., Abdalgader, T., Pedersen, M., & Zhang, L. (2025). Interactive effects of climate change and human mobility on dengue transmission. Ecological Modelling. https://doi.org/10.1016/j.ecolmodel.2024.110924.
Araujo, J., Bomfim, R., Filho, C., Cavalcanti, L., Neto, A., Andrade, J., & Furtado, V. (2024). The impact of COVID-19 mobility restrictions on dengue transmission in urban areas. PLOS Neglected Tropical Diseases, 18. https://doi.org/10.1371/journal.pntd.0012644.
Fauzi, I., Nuraini, N., Ayu, R., & Lestari, B. (2022). Temporal trend and spatial clustering of the dengue fever prevalence in West Java, Indonesia. Heliyon, 8. https://doi.org/10.1016/j.heliyon.2022.e10350.
Yanti, Y., Rahardiantoro, S., & Dito, G. (2023). Spatio-temporal Clustering Analysis of Dengue Hemorrhagic Fever Cases in West Java 2016 2021. Indonesian Journal of Statistics and Its Applications. https://doi.org/10.29244/ijsa.v7i1p56-63.
Jaya, I., Ruchjana, B., Abdullah, A., & Sunengsih, N. (2020). Multilevel model of dengue disease transmission in West Java province, Indonesia by means INLA. . https://doi.org/10.28919/cmbn/5134.
Ridhatusalma, G., Diandra, S., Rahma, H., Pangastuti, S., &
Roslan, N. (2024). Multiple Exponential Regression Modeling of Dengue
Haemorrhagic Fever Factors in West Java Province, Indonesia. Indonesian
Journal of Applied Mathematics and Statistics. https://doi.org/10.71385/idjams.v1i1.12.
Link Dashboard https://farahnadira.shinyapps.io/DBDJawaBarat/
Syntax Dashboard
# app.R - Dashboard DBD
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)
})
# Setting file
DATA_FILE <- readxl::read_excel("Data DBD Epidem Kosongan.xlsx")
DATA_TREN_FILE <- readxl::read_excel("Jumlah Kasus DBD 2016-2024.xlsx")
# Helper functions
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","jumlah penduduk"),
death = c("kasus_meninggal","meninggal","death","mati","kasus meninggal"),
banjir = c("banjir","kejadian_banjir","jumlah_banjir","kejadian banjir","jumlah banjir"),
sanit = c("sanit","sanitasi","akses_sanitasi","persen_sanitasi","akses sanitasi","persen sanitasi"),
rumah = c("rumah","layak","rumah_layak","persen_rumah","rumah_layak_huni","persen_rumah_layak_huni","rumah layak","persen rumah"),
tahun = c("tahun","year","periode")
)
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))
names(df) <- str_replace_all(names(df), "\\s+", " ")
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)
col_tahun <- find_column(cols, keywords_map$tahun)
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$Tahun <- if (!is.na(col_tahun)) as.character(df2[[col_tahun]]) else "2024"
std_cols <- c("Wilayah", "Tahun", "Jumlah_DBD", "Jumlah_Penduduk", "Kasus_Meninggal",
"Jumlah_Kejadian_Banjir", "Persen_Akses_Sanitasi", "Persen_Rumah_Layak_Huni")
df2 <- df2[, std_cols]
df2 <- df2 %>%
mutate(
Prevalensi_pct = ifelse(!is.na(Jumlah_DBD) & !is.na(Jumlah_Penduduk) & Jumlah_Penduduk>0,
round((Jumlah_DBD / Jumlah_Penduduk) * 100, 4), 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)
}
# AUTO-LOAD data
data_tren <- tryCatch({
if(file.exists(DATA_TREN_FILE)) {
ext <- tolower(tools::file_ext(DATA_TREN_FILE))
df <- if (ext %in% c("xls", "xlsx")) {
read_excel(DATA_TREN_FILE)
} else {
read_csv(DATA_TREN_FILE, show_col_types = FALSE)
}
names(df)[1] <- "Wilayah"
df$Wilayah <- str_trim(df$Wilayah)
year_cols <- names(df)[names(df) != "Wilayah"]
for(col in year_cols) {
df[[col]] <- safe_num(df[[col]])
}
df
} else {
NULL
}
}, error = function(e) {
NULL
})
initial_data <- tryCatch({
if(file.exists(DATA_FILE)) {
load_and_process_data(DATA_FILE)
} else {
NULL
}
}, error = function(e) {
NULL
})
# ----------------------------
# UI
# ----------------------------
theme_bs <- bs_theme(
bootswatch = "minty",
bg = "#FAFAFA",
fg = "#0f3d3e",
primary = "#2C7873"
)
ui <- dashboardPage(
skin = "black",
dashboardHeader(title = span("Dashboard Analisis DBD Jawa Barat", 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("Ukuran Frekuensi", tabName = "freq", icon = icon("percent")),
menuItem("Interpretasi Epidemiologi", tabName = "interp", icon = icon("stethoscope")),
menuItem("Peta Visualisasi", tabName = "peta", icon = icon("globe")),
menuItem("Tren Waktu", tabName = "tren", icon = icon("line-chart"))
)
),
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; }
"))),
tabItems(
# 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)
),
fluidRow(
box(title = "Data Lengkap (standarisasi)", status = "info", solidHeader = TRUE, width = 12,
DTOutput("table_all")
)
)
),
# STATISTIKA DESKRIPTIF
tabItem(tabName = "statdes",
fluidRow(
box(title = "Pilih Variabel dan Tahun", status = "primary", solidHeader = TRUE, width = 12,
fluidRow(
column(6, uiOutput("select_var_ui")),
column(6, uiOutput("year_filter_statdes"))
)
)
),
fluidRow(
box(title = "Histogram", status = "success", solidHeader = TRUE, width = 6,
plotOutput("histogram_plot", height = "400px")
),
box(title = "Boxplot (Klik untuk info outlier)", status = "info", solidHeader = TRUE, width = 6,
plotlyOutput("boxplot_plot", height = "400px")
)
),
fluidRow(
box(title = "Info Data Terpilih", status = "warning", solidHeader = TRUE, width = 12,
htmlOutput("clicked_info")
)
),
fluidRow(
box(title = "Statistika Deskriptif (variabel = baris)", status = "primary", solidHeader = TRUE, width = 12,
DTOutput("statdesc_tbl")
)
)
),
# UKURAN FREKUENSI
tabItem(tabName = "freq",
fluidRow(
box(title = "Filter Tahun", status = "primary", solidHeader = TRUE, width = 12,
uiOutput("year_filter_freq")
)
),
fluidRow(
box(title = "Ringkasan Ukuran Frekuensi (agregat)", status = "primary", solidHeader = TRUE, width = 12,
htmlOutput("freq_summary")
)
),
fluidRow(
box(title = "Top 10 Kabupaten/Kota - Prevalensi Tertinggi", status = "success", solidHeader = TRUE, width = 6,
plotlyOutput("top10_prevalensi_plot", height = "450px")
),
box(title = "Top 10 Kabupaten/Kota - CFR Tertinggi", status = "danger", solidHeader = TRUE, width = 6,
plotlyOutput("top10_cfr_plot", height = "450px")
)
),
fluidRow(
box(title = "Tabel Ukuran Frekuensi per Wilayah", status = "info", solidHeader = TRUE, width = 12,
DTOutput("freq_table")
)
)
),
# INTERPRETASI
tabItem(tabName = "interp",
fluidRow(
box(title = "Ringkasan Ukuran Epidemiologi", status = "primary", solidHeader = TRUE, width = 12,
htmlOutput("epi_summary")
)
),
fluidRow(
box(title = "Grafik Perbandingan Ukuran Frekuensi Antar Wilayah", status = "info", solidHeader = TRUE, width = 12,
selectInput("epi_var_plot", "Pilih Ukuran:",
choices = c("Jumlah Kasus DBD" = "Jumlah_DBD",
"Prevalensi (%)" = "Prevalensi_pct",
"Case Fatality Rate (%)" = "CaseFatality_pct"),
selected = "Jumlah_DBD"),
plotOutput("epi_comparison_plot", height = "500px")
)
),
fluidRow(
box(title = "Interpretasi Epidemiologi DBD Jawa Barat", status = "warning", solidHeader = TRUE, width = 12,
htmlOutput("epi_interpretation")
)
)
),
# PETA
tabItem(tabName = "peta",
fluidRow(
box(
title = "Peta Distribusi Variabel",
width = 4,
status = "primary",
solidHeader = TRUE,
helpText("Pilih variabel untuk divisualisasikan pada peta Jawa Barat:"),
uiOutput("map_var_ui"),
uiOutput("map_year_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 Distribusi 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")
)
)
),
# TREN WAKTU
tabItem(tabName = "tren",
fluidRow(
box(
title = "Analisis Tren Waktu Kasus DBD",
width = 12,
status = "primary",
solidHeader = TRUE,
htmlOutput("tren_info")
)
),
fluidRow(
box(
title = "Tren Total Kasus DBD Provinsi Jawa Barat",
width = 12,
status = "info",
solidHeader = TRUE,
plotlyOutput("tren_provinsi_plot", height = "400px")
)
),
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 Kasus DBD per Wilayah (Klik titik untuk detail)",
width = 8,
status = "success",
solidHeader = TRUE,
plotlyOutput("tren_plot_interactive", height = "450px")
)
),
fluidRow(
box(
title = "Detail Titik Terpilih",
width = 12,
status = "warning",
solidHeader = TRUE,
htmlOutput("tren_point_detail")
)
),
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")
)
)
)
)
)
)
# ----------------------------
# SERVER
# ----------------------------
server <- function(input, output, session) {
std_df <- reactiveVal(initial_data)
jabar_shp <- reactiveVal(NULL)
tren_df <- reactiveVal(data_tren)
clicked_point <- reactiveVal(NULL)
# 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 = "Data tidak tersedia"), options = list(dom = 't')))
df_display <- df %>% select(-Prevalensi_pct, -CaseFatality_pct)
datatable(df_display, options = list(pageLength = 10, scrollX = TRUE))
})
# Variable selector UNTUK STATDES
output$select_var_ui <- renderUI({
df <- std_df()
df_tren <- tren_df()
if (is.null(df)) return(tags$div("Memuat data..."))
# Ambil variabel numerik dari data utama
num_vars <- names(df %>% select(where(is.numeric)))
freq_cols <- c("Prevalensi_pct", "CaseFatality_pct")
num_vars <- setdiff(num_vars, freq_cols)
var_display_names <- sapply(num_vars, function(x) {
tools::toTitleCase(gsub("_", " ", x))
})
names(num_vars) <- var_display_names
selectInput("var_to_plot", "Pilih Variabel:", choices = num_vars, selected = num_vars[1])
})
# Filter tahun UNTUK STATDES (dinamis berdasarkan variabel)
output$year_filter_statdes <- renderUI({
df <- std_df()
df_tren <- tren_df()
req(input$var_to_plot)
# Jika variabel adalah Jumlah_DBD dan ada data tren
if(input$var_to_plot == "Jumlah_DBD" && !is.null(df_tren)) {
year_cols <- names(df_tren)[names(df_tren) != "Wilayah"]
selectInput("year_statdes", "Pilih Tahun:",
choices = c("Semua Tahun", year_cols),
selected = "Semua Tahun")
} else {
# Variabel lain hanya menampilkan tahun dari data utama
if ("Tahun" %in% names(df)) {
years <- sort(unique(df$Tahun))
selectInput("year_statdes", "Pilih Tahun:", choices = years, selected = years[1])
} else {
tags$p("Tahun: 2024")
}
}
})
# Filter tahun untuk freq
output$year_filter_freq <- renderUI({
df <- std_df()
if (is.null(df)) return(NULL)
if ("Tahun" %in% names(df)) {
years <- sort(unique(df$Tahun))
selectInput("year_freq", "Pilih Tahun:", choices = c("Semua Tahun", years), selected = "Semua Tahun")
} else {
tags$p("Data tidak memiliki kolom tahun")
}
})
# Data terfilter untuk statdes
filtered_data_statdes <- reactive({
df <- std_df()
df_tren <- tren_df()
req(df, input$var_to_plot)
# Jika variabel adalah Jumlah_DBD, ambil dari data tren
if(input$var_to_plot == "Jumlah_DBD" && !is.null(df_tren)) {
if(!is.null(input$year_statdes)) {
if(input$year_statdes == "Semua Tahun") {
# Reshape ke format panjang
df_long <- df_tren %>%
pivot_longer(cols = -Wilayah, names_to = "Tahun", values_to = "Jumlah_DBD") %>%
mutate(Tahun = as.character(Tahun))
return(df_long)
} else {
# Tahun tertentu
selected_year <- input$year_statdes
if(selected_year %in% names(df_tren)) {
df_result <- df_tren %>%
select(Wilayah, all_of(selected_year)) %>%
rename(Jumlah_DBD = all_of(selected_year)) %>%
mutate(Tahun = selected_year)
return(df_result)
}
}
}
}
# Filter normal untuk variabel lain
if ("Tahun" %in% names(df) && !is.null(input$year_statdes)) {
df <- df %>% filter(Tahun == input$year_statdes)
}
df
})
filtered_data_freq <- reactive({
df <- std_df()
req(df)
if ("Tahun" %in% names(df) && !is.null(input$year_freq) && input$year_freq != "Semua Tahun") {
df <- df %>% filter(Tahun == input$year_freq)
}
df
})
# Histogram
output$histogram_plot <- renderPlot({
df <- filtered_data_statdes()
req(df, input$var_to_plot)
var <- input$var_to_plot
if(nrow(df) == 0 || all(is.na(df[[var]]))) {
plot.new()
text(0.5, 0.5, "Data tidak tersedia")
return()
}
df_clean <- df %>% filter(!is.na(.data[[var]]))
if(nrow(df_clean) < 2) {
plot.new()
text(0.5, 0.5, "Data tidak cukup untuk membuat histogram")
return()
}
# Jika Jumlah_DBD dan ada data dari beberapa tahun
if(var == "Jumlah_DBD" && "Tahun" %in% names(df) && length(unique(df$Tahun)) > 1) {
ggplot(df_clean, aes(x = Jumlah_DBD, fill = Tahun)) +
geom_histogram(bins = 30, color = "white", alpha = 0.7, position = "identity") +
theme_minimal(base_size = 13) +
labs(
title = "Histogram Jumlah Kasus DBD",
x = "Jumlah Kasus DBD",
y = "Frekuensi",
fill = "Tahun"
) +
theme(
plot.title = element_text(face = "bold", size = 14),
legend.position = "right"
) +
scale_fill_viridis_d()
} else {
ggplot(df_clean, aes(x = .data[[var]])) +
geom_histogram(bins = 30, fill = "#8fd3c7", color = "white", alpha = 0.7) +
theme_minimal(base_size = 13) +
labs(
title = paste("Histogram -", tools::toTitleCase(gsub("_", " ", var))),
x = tools::toTitleCase(gsub("_", " ", var)),
y = "Frekuensi"
) +
theme(
plot.title = element_text(face = "bold", size = 14)
)
}
}, res = 96)
# Boxplot
output$boxplot_plot <- renderPlotly({
df <- filtered_data_statdes()
req(df, input$var_to_plot)
var <- input$var_to_plot
# Jika Jumlah_DBD dan ada beberapa tahun, buat boxplot per tahun
if(var == "Jumlah_DBD" && "Tahun" %in% names(df) && length(unique(df$Tahun)) > 1) {
plot_ly(df, x = ~Tahun, y = ~Jumlah_DBD, type = "box",
color = ~Tahun,
boxpoints = "outliers",
text = ~paste("Wilayah:", Wilayah, "<br>Tahun:", Tahun, "<br>Nilai:", comma(Jumlah_DBD)),
hoverinfo = "text") %>%
layout(title = "Boxplot Jumlah Kasus DBD per Tahun",
xaxis = list(title = "Tahun"),
yaxis = list(title = "Jumlah Kasus DBD"),
showlegend = FALSE)
} else {
plot_ly(df, y = ~get(var), type = "box",
marker = list(color = "#2C7873"),
line = list(color = "#0f3d3e"),
name = var,
boxpoints = "outliers",
text = ~paste("Wilayah:", Wilayah, "<br>Nilai:", round(get(var), 2)),
hoverinfo = "text") %>%
layout(title = paste("Boxplot -", tools::toTitleCase(gsub("_", " ", var))),
yaxis = list(title = tools::toTitleCase(gsub("_", " ", var))))
}
})
output$clicked_info <- renderUI({
df <- filtered_data_statdes()
req(df, input$var_to_plot)
event <- event_data("plotly_click")
if (is.null(event)) {
return(HTML("<p><i>Klik pada titik outlier di boxplot untuk melihat detail...</i></p>"))
}
var <- input$var_to_plot
clicked_value <- event$y
df$diff <- abs(df[[var]] - clicked_value)
closest <- df %>% filter(!is.na(diff)) %>% arrange(diff) %>% slice(1)
if (nrow(closest) > 0) {
HTML(paste0(
"<div style='font-size:14px; padding:10px; background:#f0f0f0; border-radius:5px;'>",
"<h4 style='margin-top:0;'>Data Point yang Dipilih:</h4>",
"<table style='width:100%;'>",
"<tr><td><b>Wilayah:</b></td><td>", closest$Wilayah, "</td></tr>",
"<tr><td><b>", tools::toTitleCase(gsub("_", " ", var)), ":</b></td><td>", comma(round(closest[[var]], 2)), "</td></tr>",
if("Tahun" %in% names(closest)) paste0("<tr><td><b>Tahun:</b></td><td>", closest$Tahun, "</td></tr>") else "",
"</table>",
"</div>"
))
} else {
HTML("<p>Data tidak ditemukan</p>")
}
})
# Statistika deskriptif
output$statdesc_tbl <- renderDT({
df <- filtered_data_statdes()
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_pct", "CaseFatality_pct")
num_df <- num_df[, !(names(num_df) %in% freq_cols), 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) {
tools::toTitleCase(gsub("_", " ", nm))
})
datatable(stat, options = list(pageLength = 15, scrollX = TRUE))
})
# Frequencies
output$freq_table <- renderDT({
df <- filtered_data_freq()
if (is.null(df)) return(datatable(data.frame(Message = "Memuat data..."), options = list(dom = 't')))
fd <- df %>%
select(Wilayah, Jumlah_DBD, Jumlah_Penduduk, Kasus_Meninggal,
Prevalensi_pct, CaseFatality_pct) %>%
arrange(desc(Prevalensi_pct))
datatable(fd, options = list(pageLength = 15, scrollX = TRUE))
})
output$freq_summary <- renderUI({
df <- filtered_data_freq()
if (is.null(df)) return(HTML("<i>Memuat data...</i>"))
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_agregat <- round((total_kasus / total_penduduk) * 100, 4)
cfr_agregat <- round((total_meninggal / total_kasus) * 100, 2)
if (all(is.na(df$Prevalensi_pct))) {
txt_prev <- "<i>Tidak ada nilai prevalensi tersedia.</i>"
} else {
top <- df %>% filter(!is.na(Prevalensi_pct)) %>% arrange(desc(Prevalensi_pct)) %>% slice(1)
txt_prev <- paste0("<b>Prevalensi tertinggi</b>: ", round(top$Prevalensi_pct,4),
"% <b>", top$Wilayah, "</b>")
}
HTML(paste0(
"<div style='font-size:15px; line-height:2;'>",
"<table style='width:100%;'>",
"<tr><td><b>Prevalensi Agregat:</b></td><td>", prevalensi_agregat, "%</td></tr>",
"<tr><td><b>CFR Agregat:</b></td><td>", cfr_agregat, "%</td></tr>",
"</table>",
"<p style='margin-top:15px;'>", txt_prev, "</p>",
"</div>"
))
})
# Plot top 10 prevalensi
output$top10_prevalensi_plot <- renderPlotly({
df <- filtered_data_freq()
req(df)
top10 <- df %>%
filter(!is.na(Prevalensi_pct)) %>%
arrange(desc(Prevalensi_pct)) %>%
slice_head(n = 10)
plot_ly(top10, x = ~Prevalensi_pct, y = ~reorder(Wilayah, Prevalensi_pct),
type = "bar", orientation = "h",
marker = list(color = "#2C7873"),
text = ~paste("Prevalensi:", round(Prevalensi_pct, 4), "%"),
hoverinfo = "text+y") %>%
layout(title = "Top 10 Prevalensi Tertinggi",
xaxis = list(title = "Prevalensi (%)"),
yaxis = list(title = ""),
margin = list(l = 150))
})
# Plot top 10 CFR
output$top10_cfr_plot <- renderPlotly({
df <- filtered_data_freq()
req(df)
top10 <- df %>%
filter(!is.na(CaseFatality_pct)) %>%
arrange(desc(CaseFatality_pct)) %>%
slice_head(n = 10)
plot_ly(top10, x = ~CaseFatality_pct, y = ~reorder(Wilayah, CaseFatality_pct),
type = "bar", orientation = "h",
marker = list(color = "#d9534f"),
text = ~paste("CFR:", round(CaseFatality_pct, 2), "%"),
hoverinfo = "text+y") %>%
layout(title = "Top 10 CFR Tertinggi",
xaxis = list(title = "Case Fatality Rate (%)"),
yaxis = list(title = ""),
margin = list(l = 150))
})
# INTERPRETASI EPIDEMIOLOGI
output$epi_summary <- renderUI({
df <- std_df()
if (is.null(df)) return(HTML("<i>Memuat data...</i>"))
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_agregat <- (total_kasus / total_penduduk) * 100
cfr_agregat <- (total_meninggal / total_kasus) * 100
top_kasus <- df %>% filter(!is.na(Jumlah_DBD)) %>% arrange(desc(Jumlah_DBD)) %>% slice(1)
top_prev <- df %>% filter(!is.na(Prevalensi_pct)) %>% arrange(desc(Prevalensi_pct)) %>% slice(1)
top_cfr <- df %>% filter(!is.na(CaseFatality_pct)) %>% arrange(desc(CaseFatality_pct)) %>% slice(1)
HTML(paste0(
"<div style='font-size:15px; line-height:2;'>",
"<h4>Ukuran Frekuensi Penyakit DBD - Jawa Barat</h4>",
"<table style='width:100%; border-collapse: collapse;'>",
"<tr style='background:#f0f0f0;'><th style='padding:10px; text-align:left; border:1px solid #ddd;'>Ukuran</th>",
"<th style='padding:10px; text-align:left; border:1px solid #ddd;'>Wilayah Tertinggi</th></tr>",
"<tr><td style='padding:8px; border:1px solid #ddd;'><b>Jumlah Kasus DBD</b></td>",
"<td style='padding:8px; border:1px solid #ddd;'>", top_kasus$Wilayah, " (", comma(top_kasus$Jumlah_DBD), " kasus)</td></tr>",
"<tr style='background:#f9f9f9;'><td style='padding:8px; border:1px solid #ddd;'><b>Prevalensi</b><br><small>(persen)</small></td>",
"<td style='padding:8px; border:1px solid #ddd;'>", top_prev$Wilayah, " (", round(top_prev$Prevalensi_pct, 4), "%)</td></tr>",
"<tr><td style='padding:8px; border:1px solid #ddd;'><b>Case Fatality Rate</b><br><small>(persen)</small></td>",
"<td style='padding:8px; border:1px solid #ddd;'>", top_cfr$Wilayah, " (", round(top_cfr$CaseFatality_pct, 2), "%)</td></tr>",
"</table>",
"</div>"
))
})
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)
var_labels <- c(
"Jumlah_DBD" = "Jumlah Kasus DBD",
"Prevalensi_pct" = "Prevalensi (%)",
"CaseFatality_pct" = "Case Fatality Rate (%)"
)
if(var_name == "Jumlah_DBD") {
plot_data$label_text <- comma(plot_data[[var_name]])
} else {
plot_data$label_text <- as.character(round(plot_data[[var_name]], 2))
}
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 = label_text), hjust = -0.2, size = 3.5) +
coord_flip() +
theme_minimal(base_size = 13) +
labs(
title = paste("Top 15 Wilayah berdasarkan", var_labels[var_name]),
x = "Wilayah",
y = var_labels[var_name]
) +
theme(
plot.title = element_text(face = "bold", size = 15),
panel.grid.major.y = element_blank()
)
}, res = 96)
output$epi_interpretation <- renderUI({
df <- std_df()
if (is.null(df)) return(HTML("<i>Memuat data...</i>"))
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_agregat <- (total_kasus / total_penduduk) * 100
cfr_agregat <- (total_meninggal / total_kasus) * 100
prevalensi_per100k <- round((total_kasus / total_penduduk) * 100000, 0)
cfr_status <- if(cfr_agregat > 1) "TINGGI (>1%)" else if(cfr_agregat > 0.5) "SEDANG (0.5-1%)" else "RENDAH (<0.5%)"
cfr_meaning <- if(cfr_agregat > 1) {
paste0("angka CFR sebesar ", round(cfr_agregat, 2), "% tergolong tinggi, menunjukkan bahwa dari setiap 100 orang yang terkena DBD, sekitar ",
round(cfr_agregat, 0), " orang meninggal dunia. Hal ini mengindikasikan perlunya peningkatan kualitas penanganan dan akses layanan kesehatan untuk penderita DBD.")
} else if(cfr_agregat > 0.5) {
paste0("angka CFR sebesar ", round(cfr_agregat, 2), "% tergolong sedang, menunjukkan bahwa dari setiap 100 orang yang terkena DBD, sekitar ",
round(cfr_agregat, 1), " orang meninggal dunia. Perlu dilakukan pemantauan dan peningkatan kualitas layanan kesehatan.")
} else {
paste0("angka CFR sebesar ", round(cfr_agregat, 2), "% tergolong rendah, menunjukkan bahwa tingkat kematian akibat DBD relatif terkendali. Namun tetap perlu dilakukan pencegahan dan penanganan yang optimal.")
}
HTML(paste0(
"<div style='font-size:14px; line-height:1.9;'>",
"<h4>Interpretasi Epidemiologi</h4>",
"<p><b>Jumlah Kasus DBD:</b> ", comma(total_kasus), " kasus</p>",
"<p><b>Prevalensi DBD:</b> ", round(prevalensi_agregat, 4), "%</p>",
"<p><b>CFR:</b> ", round(cfr_agregat, 2), "% (", cfr_status, ")</p>",
"<hr>",
"<p style='text-align:justify;'><i><b>Interpretasi:</b> Angka prevalensi sebesar ", round(prevalensi_agregat, 4),
"% berarti dari setiap 100.000 penduduk, terdapat sekitar ", prevalensi_per100k,
" orang yang menderita penyakit DBD. Diperlukan upaya pengendalian yang intensif agar mengurangi ",
"penyebaran penyakit DBD di wilayah Jawa Barat. Sedangkan ", cfr_meaning, "</i></p>",
"</div>"
))
})
# PETA VISUALISASI
output$map_var_ui <- renderUI({
df <- std_df()
df_tren <- tren_df()
req(df)
num_vars <- names(df)[sapply(df, is.numeric)]
priority_vars <- c("Prevalensi_pct", "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])
})
output$map_year_ui <- renderUI({
df_tren <- tren_df()
req(input$map_var)
if(input$map_var == "Jumlah_DBD" && !is.null(df_tren)) {
year_cols <- names(df_tren)[names(df_tren) != "Wilayah"]
selectInput("map_year", "Pilih Tahun (untuk Jumlah DBD):",
choices = year_cols,
selected = year_cols[length(year_cols)])
} else {
NULL
}
})
observeEvent(input$btn_load_map, {
withProgress(message = 'Memuat peta Jawa Barat...', value = 0, {
tryCatch({
incProgress(0.3, detail = "Mengunduh data geografis...")
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$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_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"
}
}
}
incProgress(1, detail = "Selesai!")
jabar_shp(shp)
showNotification("Peta berhasil dimuat!", 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()
df_tren <- tren_df()
shp <- jabar_shp()
req(shp)
if(input$map_var == "Jumlah_DBD" && !is.null(df_tren) && !is.null(input$map_year)) {
selected_year <- input$map_year
if(selected_year %in% names(df_tren)) {
df <- df_tren %>%
select(Wilayah, all_of(selected_year)) %>%
rename(Jumlah_DBD = all_of(selected_year))
}
}
req(df)
df$Wilayah_Clean <- tolower(trimws(as.character(df$Wilayah)))
df$Wilayah_Clean <- gsub("^kab\\.?\\s*", "", df$Wilayah_Clean)
df$Wilayah_Clean <- gsub("^kabupaten\\s+", "", df$Wilayah_Clean)
df$Wilayah_Clean <- gsub("\\s+", " ", df$Wilayah_Clean)
df$Wilayah_Clean <- trimws(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)
shp$Wilayah_Clean <- trimws(shp$Wilayah_Clean)
merged <- merge(shp, df, by = "Wilayah_Clean", all.x = TRUE)
if (!(input$map_var %in% names(merged))) {
return(leaflet() %>% addTiles() %>% setView(lng = 107.9, lat = -6.9, zoom = 9))
}
vals <- merged[[input$map_var]]
valid_vals <- vals[!is.na(vals)]
if (length(valid_vals) == 0) {
return(leaflet() %>% addTiles() %>% setView(lng = 107.9, lat = -6.9, zoom = 9))
}
output$map_info <- renderUI({
year_info <- if(input$map_var == "Jumlah_DBD" && !is.null(input$map_year)) {
paste0("<li><strong>Tahun:</strong> ", input$map_year, "</li>")
} else ""
HTML(paste0(
"<h5>Statistik Peta:</h5>",
"<ul style='font-size:13px;'>",
year_info,
"<li><strong>Min:</strong> ", comma(round(min(valid_vals, na.rm=TRUE), 2)), "</li>",
"<li><strong>Maks:</strong> ", comma(round(max(valid_vals, na.rm=TRUE), 2)), "</li>",
"<li><strong>Rata-rata:</strong> ", comma(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"
)
leaflet(merged) %>%
addProviderTiles("CartoDB.Positron") %>%
setView(lng = 107.9, lat = -6.9, zoom = 9) %>%
addPolygons(
fillColor = ~pal(get(input$map_var)),
color = "grey40",
weight = 1.5,
opacity = 1,
fillOpacity = 0.75,
highlightOptions = highlightOptions(
weight = 3,
color = "#333",
fillOpacity = 0.9,
bringToFront = TRUE
),
label = ~lapply(paste0(
"<strong>", NAME_2, "</strong><br>",
input$map_var, ": ",
ifelse(is.na(get(input$map_var)),
"<span style='color:red;'>Tidak ada data</span>",
comma(round(get(input$map_var), 2)))
), htmltools::HTML)
) %>%
addLegend(
pal = pal,
values = ~get(input$map_var),
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.75), na.rm = TRUE)
high_threshold <- q[2]
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) {
paste(head(high_regions$NAME_2, 5), collapse = ", ")
} else "Tidak ada"
low_names <- if(nrow(low_regions) > 0) {
paste(head(low_regions$NAME_2, 5), collapse = ", ")
} else "Tidak ada"
HTML(paste0(
"<h4>Interpretasi Pola Spasial: ", tools::toTitleCase(gsub("_", " ", var_name)), "</h4>",
"<p><strong>Wilayah TINGGI</strong> (Q3 = ", comma(round(high_threshold, 2)), "): ", high_names, "</p>",
"<p><strong>Wilayah RENDAH</strong> (Q1 = ", comma(round(low_threshold, 2)), "): ", low_names, "</p>"
))
})
# TREN WAKTU
output$tren_info <- renderUI({
df <- tren_df()
if (is.null(df)) {
return(HTML("<p style='color:red;'>Data tren waktu belum tersedia.</p>"))
}
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>"
))
})
# Tren total provinsi dengan persentase perubahan
output$tren_provinsi_plot <- renderPlotly({
df <- tren_df()
req(df)
year_cols <- names(df)[names(df) != "Wilayah"]
total_per_year <- data.frame(
Tahun = year_cols,
Total_Kasus = sapply(year_cols, function(y) sum(df[[y]], na.rm = TRUE))
)
total_per_year$Tahun <- as.numeric(total_per_year$Tahun)
total_per_year$Pct_Change <- NA
for(i in 2:nrow(total_per_year)) {
if(total_per_year$Total_Kasus[i-1] > 0) {
total_per_year$Pct_Change[i] <- ((total_per_year$Total_Kasus[i] - total_per_year$Total_Kasus[i-1]) / total_per_year$Total_Kasus[i-1]) * 100
}
}
plot_ly(total_per_year, x = ~Tahun, y = ~Total_Kasus, type = "scatter", mode = "lines+markers",
line = list(color = "#2C7873", width = 3),
marker = list(size = 10, color = "#2C7873"),
text = ~paste("<b>Tahun:", Tahun, "</b>",
"<br>Total Kasus:", comma(Total_Kasus),
"<br>Perubahan dari tahun lalu:",
ifelse(is.na(Pct_Change), "N/A",
paste0(ifelse(Pct_Change > 0, "+", ""),
round(Pct_Change, 1), "%"))),
hoverinfo = "text") %>%
layout(
title = "Tren Total Kasus DBD Provinsi Jawa Barat (2016-2024)",
xaxis = list(title = "Tahun", dtick = 1),
yaxis = list(title = "Total Kasus DBD"),
hovermode = "closest"
)
})
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)
})
# Tren plot interaktif dengan persentase perubahan
output$tren_plot_interactive <- renderPlotly({
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)) %>%
arrange(Wilayah, Tahun)
# Hitung persentase perubahan per wilayah
plot_data <- plot_data %>%
group_by(Wilayah) %>%
mutate(
Pct_Change = ifelse(lag(Jumlah_Kasus) > 0,
((Jumlah_Kasus - lag(Jumlah_Kasus)) / lag(Jumlah_Kasus)) * 100,
NA)
) %>%
ungroup()
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)) %>%
arrange(Tahun) %>%
mutate(
Pct_Change = ifelse(lag(Jumlah_Kasus) > 0,
((Jumlah_Kasus - lag(Jumlah_Kasus)) / lag(Jumlah_Kasus)) * 100,
NA)
)
plot_data <- bind_rows(plot_data, avg_data)
}
p <- plot_ly()
for(wil in unique(plot_data$Wilayah)) {
wil_data <- plot_data %>% filter(Wilayah == wil)
p <- p %>% add_trace(
data = wil_data,
x = ~Tahun,
y = ~Jumlah_Kasus,
type = "scatter",
mode = "lines+markers",
name = wil,
text = ~paste("<b>", Wilayah, "</b>",
"<br>Tahun:", Tahun,
"<br>Jumlah Kasus:", comma(Jumlah_Kasus),
"<br>Perubahan dari tahun lalu:",
ifelse(is.na(Pct_Change), "N/A",
paste0(ifelse(Pct_Change > 0, "+", ""),
round(Pct_Change, 1), "%"))),
hoverinfo = "text",
line = list(width = 2),
marker = list(size = 8)
)
}
p %>% layout(
title = "Tren Kasus DBD Antar Tahun",
xaxis = list(title = "Tahun", dtick = 1),
yaxis = list(title = "Jumlah Kasus DBD"),
hovermode = "closest",
legend = list(orientation = "v", x = 1.05, y = 1)
)
})
# Detail titik yang diklik
output$tren_point_detail <- renderUI({
event <- event_data("plotly_click", source = "tren_plot_interactive")
if(is.null(event)) {
return(HTML("<p><i>Klik pada titik di grafik untuk melihat detail perubahan...</i></p>"))
}
df <- tren_df()
req(df, input$tren_wilayah)
clicked_year <- event$x
clicked_value <- event$y
# Identifikasi wilayah yang diklik
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)
}
# Cari data yang paling dekat dengan klik
plot_data$diff <- abs(plot_data$Jumlah_Kasus - clicked_value) + abs(plot_data$Tahun - clicked_year) * 100
closest <- plot_data %>% arrange(diff) %>% slice(1)
if(nrow(closest) > 0) {
# Ambil data tahun sebelumnya untuk hitung perubahan
prev_data <- plot_data %>%
filter(Wilayah == closest$Wilayah, Tahun == closest$Tahun - 1)
if(nrow(prev_data) > 0 && prev_data$Jumlah_Kasus > 0) {
pct_change <- ((closest$Jumlah_Kasus - prev_data$Jumlah_Kasus) / prev_data$Jumlah_Kasus) * 100
abs_change <- closest$Jumlah_Kasus - prev_data$Jumlah_Kasus
change_text <- if(pct_change > 0) {
paste0("<span style='color:red;'><b>NAIK ", round(pct_change, 1), "%</b></span> (+", comma(abs_change), " kasus)")
} else {
paste0("<span style='color:green;'><b>TURUN ", round(abs(pct_change), 1), "%</b></span> (", comma(abs_change), " kasus)")
}
} else {
change_text <- "<i>Tidak ada data tahun sebelumnya</i>"
}
HTML(paste0(
"<div style='font-size:14px; padding:15px; background:#f9f9f9; border-left:4px solid #2C7873; border-radius:5px;'>",
"<h4 style='margin-top:0;'>Detail: ", closest$Wilayah, " - Tahun ", closest$Tahun, "</h4>",
"<table style='width:100%; line-height:2;'>",
"<tr><td><b>Jumlah Kasus:</b></td><td>", comma(closest$Jumlah_Kasus), " kasus</td></tr>",
"<tr><td><b>Perubahan dari tahun lalu:</b></td><td>", change_text, "</td></tr>",
"</table>",
"</div>"
))
} else {
HTML("<p>Data tidak ditemukan</p>")
}
})
output$tren_table <- renderDT({
df <- tren_df()
if (is.null(df)) return(datatable(data.frame(Message = "Data tren belum dimuat")))
if(!is.null(input$tren_wilayah) && length(input$tren_wilayah) > 0) {
table_data <- df %>%
filter(Wilayah %in% input$tren_wilayah)
} else {
table_data <- df
}
table_data <- table_data %>%
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 = 27, scrollX = TRUE, scrollY = "400px"))
})
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)
rsconnect::deployApp()
AI digunakan untuk membantu membuat syntax dashboard agar tampilannya terlihat lebih menarik, membantu memunculkan ide, sebagai teman diskusi jika ditemukan hal-hal atau pernyataan di jurnal yang kemudian memunculkan banyak pertanyaan baru di otak saya, serta membantu membuat kalimat interpretasi yang efektif dan mudah dimengerti saat membacanya. Adapun untuk interpretasi aslinya merupakan interpretasi yang saya pahami sendiri dan kemudian dijabarkan kalimatnya agar lebih dapat dipahami.