ABSTRAK
Demam Berdarah Dengue (DBD) masih menjadi masalah kesehatan utama di Indonesia, terutama di Provinsi Jawa Barat yang mencatat jumlah kasus tertinggi pada tahun 2024. Penelitian ini bertujuan menganalisis distribusi kasus DBD serta hubungannya dengan faktor lingkungan seperti banjir, sanitasi, dan kelayakan rumah. Metode yang digunakan ialah studi deskriptif cross-sectional dengan data sekunder dari sumber resmi. Hasil analisis diharapkan dapat menjadi dasar dalam upaya pencegahan dan pengendalian DBD di wilayah berisiko tinggi.
**Untuk memenuhi tugas mata kuliah Epidemiologi**
Dosen Pengampu:
Dr. I Gede Nyoman Mindra Jaya, M.Si.,
Ph.D.Â
Disusun oleh:
Farah Nadira (140610230015)
Tansya Putri Rizkya Zakaria (140610230021)
Annisa Fitriani Fadhilah (140610230025)
PROGRAM STUDI STATISTIKA
FAKULTAS MATEMATIKA DAN ILMU PENGETAHUAN ALAM
UNIVERSITAS PADJADJARAN
2025
Demam Berdarah Dengue (DBD) merupakan salah satu penyakit menular yang hingga kini masih menjadi permasalahan kesehatan masyarakat di Indonesia. Penyakit ini disebabkan oleh infeksi virus dengue yang ditularkan melalui gigitan nyamuk Aedes aegypti dan Aedes albopictus. Indonesia sebagai negara tropis memiliki kondisi iklim, suhu, serta kelembapan yang mendukung perkembangan vektor nyamuk tersebut.
Menurut laporan Kementerian Kesehatan Republik Indonesia, pada tahun 2024 tercatat lebih dari 100 ribu kasus DBD secara nasional. Provinsi Jawa Barat menjadi daerah dengan jumlah kasus tertinggi di Indonesia, disusul oleh Jawa Timur dan Jawa Tengah. Berdasarkan data Dinas Kesehatan Jawa Barat, terdapat lebih dari 60 ribu kasus DBD dengan ratusan kematian yang dilaporkan sepanjang tahun 2024. Angka ini menunjukkan bahwa DBD masih menjadi ancaman serius bagi penduduk di provinsi dengan populasi terbesar di Indonesia tersebut.
Kepadatan penduduk yang tinggi di wilayah perkotaan seperti Bandung, Bekasi, dan Bogor, ditambah dengan kondisi lingkungan yang bervariasi antara daerah urban dan rural, menjadikan Jawa Barat memiliki risiko tinggi terhadap penyebaran penyakit ini. Faktor lingkungan seperti adanya genangan air pascabanjir, akses sanitasi yang belum merata, serta rumah yang belum seluruhnya layak huni turut memperparah kondisi tersebut.
Selain faktor lingkungan fisik, perilaku masyarakat dalam pengelolaan air dan kebersihan lingkungan juga berpengaruh terhadap peningkatan populasi vektor nyamuk. Dalam konteks epidemiologi, penting untuk memahami bagaimana interaksi antara host (manusia), agent (virus dengue), dan environment (lingkungan) berkontribusi terhadap penyebaran penyakit ini.
Oleh karena itu, diperlukan analisis epidemiologi untuk menggambarkan distribusi kasus DBD di Provinsi Jawa Barat, menghitung ukuran frekuensi penyakit (prevalensi dan case fatality rate), serta menilai hubungan sederhana antara kondisi lingkungan dengan kejadian DBD. Hasil analisis ini diharapkan dapat memberikan gambaran wilayah yang perlu diprioritaskan dalam program pencegahan dan pengendalian DBD, sehingga dapat menekan angka kejadian dan kematian akibat penyakit tersebut.
Tujuan dari analisis ini adalah:
Mendeskripsikan pola dan distribusi kasus DBD di Provinsi Jawa Barat tahun 2024.
Menghitung ukuran frekuensi penyakit, khususnya prevalensi dan case fatality rate (CFR).
Menjelaskan secara deskriptif keterkaitan antara faktor lingkungan (banjir, sanitasi, dan kondisi rumah) dengan besarnya kasus DBD di wilayah Jawa Barat.
Memberikan interpretasi epidemiologis dan rekomendasi kebijakan untuk mendukung upaya pengendalian dan pencegahan DBD.
Penelitian ini memiliki beberapa batasan sebagai berikut:
Analisis hanya difokuskan pada penyakit Demam Berdarah Dengue (DBD) di Provinsi Jawa Barat tahun 2024, sehingga hasilnya tidak dapat digeneralisasikan untuk provinsi lain atau tahun yang berbeda.
Unit analisis yang digunakan adalah kabupaten/kota, bukan individu, sehingga hubungan antara faktor lingkungan dan kejadian DBD hanya bersifat agregat (populasi).
Penelitian ini menggunakan data sekunder dari sumber resmi seperti Kementerian Kesehatan dan Badan Pusat Statistik (BPS), sehingga kualitas hasil analisis sangat bergantung pada akurasi pelaporan data tersebut.
Analisis yang dilakukan bersifat deskriptif dan cross-sectional, sehingga tidak dimaksudkan untuk menilai hubungan kausalitas atau perubahan temporal antar variabel.
Bagi mahasiswa: sebagai latihan penerapan konsep epidemiologi pada data riil serta penguatan pemahaman tentang distribusi penyakit berbasis wilayah.
Bagi instansi kesehatan daerah: sebagai informasi awal untuk mengidentifikasi daerah berisiko tinggi serta membantu dalam perencanaan program pengendalian DBD di Jawa Barat.
Bagi masyarakat: sebagai edukasi pentingnya menjaga kebersihan lingkungan dan memperhatikan sanitasi rumah tangga agar tidak menjadi tempat perkembangbiakan nyamuk penyebab DBD.
Epidemiologi merupakan ilmu yang mempelajari distribusi serta determinan masalah kesehatan pada populasi, dan bagaimana hasilnya digunakan untuk mengendalikan masalah tersebut. Melalui pendekatan epidemiologi, suatu penyakit dapat diidentifikasi berdasarkan siapa yang terinfeksi, di mana kejadiannya terjadi, serta kapan peningkatan kasus berlangsung. Dengan demikian, epidemiologi berperan penting dalam perencanaan dan evaluasi program kesehatan masyarakat.
Demam Berdarah Dengue (DBD) adalah penyakit menular yang disebabkan oleh virus dengue dari genus Flavivirus dan ditularkan melalui gigitan nyamuk Aedes aegypti maupun Aedes albopictus. Penularan penyakit ini umumnya meningkat pada musim hujan atau di daerah yang memiliki banyak genangan air. Selain faktor iklim, kepadatan penduduk, dan perilaku masyarakat dalam menjaga kebersihan lingkungan juga turut berperan dalam penyebaran penyakit DBD. Infeksi virus ini menimbulkan gejala klinis berupa demam tinggi mendadak, nyeri otot, ruam kulit, hingga pada kasus berat dapat menyebabkan perdarahan dan syok.
Dalam kajian epidemiologi, timbulnya suatu penyakit dipengaruhi oleh
interaksi antara tiga komponen utama yaitu agent,
host, dan environment.
Ketiga faktor ini saling berinteraksi membentuk hubungan sebab-akibat yang menggambarkan proses penularan penyakit. Hubungan tersebut dapat digambarkan melalui segitiga epidemiologi berikut.
$$
Gambar 1. Epidemiologic Triad pada Kasus DBD
$$
Gambaran di atas menunjukkan bahwa pengendalian penyakit DBD tidak hanya bergantung pada pengobatan penderita, tetapi juga pada upaya memutus rantai penularan melalui perbaikan lingkungan dan perubahan perilaku masyarakat.
Ukuran frekuensi digunakan untuk menggambarkan besarnya masalah kesehatan dalam suatu populasi. Dua ukuran yang digunakan dalam analisis ini adalah prevalensi dan case fatality rate (CFR).
a. Prevalensi
Prevalensi menunjukkan proporsi individu dalam populasi yang menderita
penyakit pada periode tertentu. Rumusnya dapat dituliskan sebagai
berikut:
\[ \text{Prevalensi} = \frac{\text{Jumlah Kasus DBD}}{\text{Jumlah Penduduk}} \times 100.000 \]
Ukuran ini memberikan gambaran beban penyakit pada populasi tertentu, tanpa memperhitungkan kapan seseorang mulai terinfeksi.
b. Case Fatality Rate (CFR)
CFR menunjukkan persentase kasus yang meninggal akibat penyakit dari
seluruh kasus yang tercatat:
\[ \text{CFR} = \frac{\text{Jumlah Kematian Akibat DBD}}{\text{Jumlah Kasus DBD}} \times 100\% \]
Nilai CFR digunakan untuk menilai tingkat keparahan penyakit dan efektivitas penanganan kasus.
Ukuran lain seperti insidensi, attack rate, serta ukuran asosiasi (risk ratio, odds ratio, atau attributable risk) tidak digunakan dalam analisis ini karena data yang tersedia bersifat agregat tahunan, bukan data individu atau longitudinal.
Penelitian ini menggunakan desain potong lintang (cross-sectional study), yaitu pengamatan yang dilakukan pada satu waktu tertentu untuk menggambarkan situasi penyakit dan faktor lingkungan secara bersamaan. Desain ini sesuai untuk analisis deskriptif karena mampu memberikan gambaran distribusi penyakit tanpa menelusuri hubungan sebab-akibat.
Sumber data yang digunakan berasal dari Open Data Jabar dan Badan Pusat Statistik (BPS) tahun 2024, yang mencakup informasi mengenai jumlah kasus DBD, jumlah penduduk, kejadian banjir, persentase akses sanitasi layak, dan persentase rumah tangga dengan rumah layak huni di setiap kabupaten/kota di Provinsi Jawa Barat.
Desain ini memiliki keunggulan dalam efisiensi waktu dan kemudahan analisis, namun juga memiliki keterbatasan yaitu tidak dapat memastikan hubungan kausalitas antarvariabel dan rentan terhadap bias agregasi data.
Autokorelasi spasial adalah konsep dalam geografi dan statistika spasial yang mengacu pada hubungan antara nilai suatu variabel di lokasi geografis tertentu dengan nilai variabel yang sama di lokasi terdekatnya (nearby locations). Dalam konteks epidemiologi, autokorelasi spasial terjadi ketika kasus penyakit (misalnya DBD) cenderung mengelompok (cluster). Artinya, wilayah yang memiliki kasus DBD tinggi cenderung berdekatan dengan wilayah yang juga memiliki kasus tinggi, dan sebaliknya.
Autokorelasi spasial merupakan hal yang penting dalam analisis penyakit menular seperti DBD karena:
Penularan: Penyakit menular memiliki pola penularan yang dipengaruhi oleh jarak, mobilitas penduduk, dan interaksi antarwilayah.
Faktor Risiko Bersama: Wilayah yang berdekatan sering kali memiliki faktor lingkungan dan sosial yang homogen (misalnya, iklim, topografi, pola sanitasi, atau kepadatan penduduk yang serupa), yang kemudian memengaruhi tingkat kasus DBD.
Untuk mengukur autokorelasi spasial, umumnya digunakan dua pendekatan utama:
Global Spatial Autocorrelation: Mengukur tingkat pengelompokan (clustering) secara keseluruhan di seluruh wilayah studi. Indikator yang paling sering digunakan adalah Moran’s I Global.
Local Spatial Autocorrelation: Mengidentifikasi lokasi-lokasi spesifik (disebut hot spots dan cold spots) yang berkontribusi terhadap pola pengelompokan. Indikator yang sering digunakan adalah Local Indicators of Spatial Association (LISA).
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.
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:
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.
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, Ukuran frekuensi digunakan untuk melihat seberapa besar penyebaran DBD pada tiap wilayah dengan menghitung prevalensi, dan case fatality rate (CFR). Kedua ukuran ini membantu menggambarkan tingkat kejadian dan keparahan penyakit di populasi. Pada analisis ini tdaik dilakukan perhitungan ukuran asosiasi dikarenakan data yang tersedia bersifat agregat tahunan, bukan data individu atau longitudinal. Selain itu, digunakan statistika deskriptif untuk memperjelas distribusi dan karakteristik data, serta visualisasi peta dan grafik untuk memperlihatkan pola spasial antarwilayah. Penelitian ini disusun dengan desain cross-sectional secara simulatif, di mana unit analisisnya adalah kabupaten/kota di Provinsi Jawa Barat.
Analisis yang dilakukan ini juga mencakup aspek spasial dengan menyajikan visualisasi data dalam bentuk peta. Peta ini bertujuan untuk memperlihatkan pola geografis (clustering) kasus DBD dan variabel lingkungan antar kabupaten/kota. Meskipun analisis ini tidak mencakup uji statistik formal seperti Moran’s I, visualisasi spasial berfungsi sebagai langkah awal untuk mengidentifikasi potensi adanya autokorelasi spasial—yaitu kecenderungan wilayah berisiko tinggi berada berdekatan—yang dapat menjadi dasar untuk studi analitik lebih lanjut.
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, serta case fatality rate (CFR), yang menunjukkan tingkat keparahan penyakit berdasarkan perbandingan jumlah kematian dan total kasus.
Tahap akhir adalah visualisasi dan interpretasi hasil. Data yang telah dianalisis disajikan dalam bentuk grafik dan peta distribusi (visualisasi spasial) guna menggambarkan pola geografis dan kecenderungan pengelompokan kasus DBD antarwilayah (potensi autokorelasi spasial). Dari hasil tersebut, dilakukan interpretasi untuk memahami kecenderungan, wilayah yang dikategorikan sebagai hot spot kasus tinggi, serta faktor risiko lingkungan yang mungkin berperan dalam penyebaran DBD di Jawa Barat.
Data 2024 menunjukkan bahwa tiap kabupaten/kota di Jawa Barat melaporkan jumlah kasus yang berbeda. Perbedaan ini dipengaruhi oleh banyak faktor, di antaranya kepadatan penduduk, kondisi lingkungan, dan upaya pencegahan yang dilakukan di daerah tersebut.
Tabel Ringkasan Hasil
# Menampilkan ringkasan statistik deskriptif
summary_data <- data_epidem |>
select(Jumlah_DBD, Kasus_Meninggal, Prevalensi_per100k, CFR) |>
summarise(
Rata_rata_Kasus = mean(Jumlah_DBD, na.rm = TRUE),
Median_Kasus = median(Jumlah_DBD, na.rm = TRUE),
Min_Kasus = min(Jumlah_DBD, na.rm = TRUE),
Max_Kasus = max(Jumlah_DBD, na.rm = TRUE),
Rata_rata_Prevalensi = mean(Prevalensi_per100k, na.rm = TRUE),
Rata_rata_CFR = mean(CFR, na.rm = TRUE)
)
knitr::kable(
t(summary_data),
caption = "Tabel 4.1: Ringkasan Statistik Deskriptif Kasus DBD dan Ukuran Frekuensi",
col.names = c("Nilai"),
format.args = list(big.mark = ".", decimal.mark = ",")
)
| Nilai | |
|---|---|
| Rata_rata_Kasus | 2.274,9259259 |
| Median_Kasus | 1.902,0000000 |
| Min_Kasus | 400,0000000 |
| Max_Kasus | 7.680,0000000 |
| Rata_rata_Prevalensi | 151,3275350 |
| Rata_rata_CFR | 0,6144157 |
data_epidem |>
ggplot(aes(x = reorder(Wilayah, Jumlah_DBD), y = Jumlah_DBD)) +
geom_col(fill = "#2C7873") +
geom_text(aes(label = comma(Jumlah_DBD)), hjust = 1.1, color = "white", size = 3) +
coord_flip() +
labs(
title = "Gambar 4.1: Distribusi Jumlah Kasus DBD per Kabupaten/Kota",
x = "Kabupaten/Kota",
y = "Jumlah Kasus"
) +
scale_y_continuous(labels = comma) +
theme_minimal()
Pembahasan: Grafik tersebut menunjukkan wilayah dengan kasus tertinggi berada di bagian atas. Wilayah tersebut dapat dijadikan sasaran utama intervensi karena berkontribusi besar terhadap total kasus provinsi.
A. Prevalensi Kasus
data_epidem |>
ggplot(aes(x = reorder(Wilayah, Prevalensi_per100k), y = Prevalensi_per100k)) +
geom_col(fill = "#1E88E5") +
geom_text(aes(label = round(Prevalensi_per100k, 1)), hjust = 1.1, color = "white", size = 3) +
coord_flip() +
labs(
title = "Gambar 4.2: Prevalensi DBD per 100.000 Penduduk",
x = "Kabupaten/Kota",
y = "Prevalensi per 100.000"
) +
theme_minimal()
Prevalensi membantu melihat beban penyakit dengan mempertimbangkan jumlah penduduk. Ada wilayah yang kasusnya tidak terlalu tinggi, tetapi prevalensinya tinggi karena jumlah penduduk lebih sedikit.
B. Case Fatality Rate (CFR)
data_epidem |>
ggplot(aes(x = reorder(Wilayah, CFR), y = CFR)) +
geom_col(fill = "#FF4500") +
geom_text(aes(label = paste0(round(CFR, 2), "%")), hjust = 1.1, color = "white", size = 3) +
coord_flip() +
labs(
title = "Gambar 4.3: Case Fatality Rate (CFR) DBD (%)",
x = "Kabupaten/Kota",
y = "CFR (%)"
) +
theme_minimal()
data_epidem |>
ggplot(aes(x = Jumlah_Kejadian_Banjir, y = Jumlah_DBD)) +
geom_point(color = "#FF7043", size = 3) +
geom_smooth(method = "lm", se = FALSE, linetype = "dashed") +
labs(
title = "Gambar 4.4: Hubungan Banjir dan Kasus DBD",
x = "Jumlah Kejadian Banjir",
y = "Jumlah Kasus DBD"
) +
scale_y_continuous(labels = comma) +
theme_minimal()
Hasil sebaran menunjukkan bahwa wilayah dengan kejadian banjir lebih banyak cenderung memiliki kasus DBD lebih tinggi. Hal ini sesuai teori bahwa genangan air pascabanjir dapat menjadi tempat berkembang biaknya nyamuk.
Analisis tren kasus DBD Provinsi Jawa Barat dari tahun 2020 hingga 2024 memberikan gambaran umum tentang dinamika penyakit dalam beberapa tahun terakhir.
data_tren_total |>
ggplot(aes(x = Tahun, y = Total_Kasus_Jabar)) +
geom_line(color = "#DC3545", size = 1.2) +
geom_point(color = "#DC3545", size = 3) +
geom_text(aes(label = comma(Total_Kasus_Jabar)), vjust = -1.5, size = 3.5) +
scale_y_continuous(labels = comma) +
scale_x_continuous(breaks = unique(data_tren_total$Tahun)) +
labs(
title = "Gambar 4.5: Tren Total Kasus DBD Provinsi Jawa Barat (2020-2024)",
x = "Tahun",
y = "Total Kasus DBD"
) +
theme_minimal()
Analisis autokorelasi spasial global digunakan untuk mengetahui apakah persebaran kasus DBD memiliki pola mengelompok, acak, atau menyebar. Uji yang digunakan adalah Moran’s I, yang menghitung hubungan antara nilai suatu wilayah dengan nilai wilayah-wilayah yang berdekatan.
Nilai Moran’s I berada pada rentang:
I > 0 → pola mengelompok (clustered)
I = 0 → pola acak (random)
I < 0 → pola menyebar (dispersed)
Dalam konteks kasus DBD, nilai Moran’s I yang positif menunjukkan bahwa wilayah dengan kasus tinggi cenderung berdekatan dengan wilayah tinggi lainnya—mengindikasikan adanya klaster spasial.
# ---- Opsi C: Hotspot berdasarkan Z-score ----
library(sf); library(dplyr); library(ggplot2)
jabar <- st_read("shapefile_jabar/kabkota_jabar.shp")
## Reading layer `kabkota_jabar' from data source
## `C:\Users\Lenovo\Downloads\shapefile_jabar\kabkota_jabar.shp'
## using driver `ESRI Shapefile'
## Simple feature collection with 27 features and 13 fields
## Geometry type: MULTIPOLYGON
## Dimension: XY
## Bounding box: xmin: 106.3705 ymin: -7.823398 xmax: 108.8338 ymax: -5.91377
## Geodetic CRS: WGS 84
jabar$NAME_2_clean <- tolower(jabar$NAME_2)
data_epidem$Wilayah_clean <- tolower(data_epidem$Wilayah)
jabar_merged <- left_join(jabar, data_epidem, by = c("NAME_2_clean" = "Wilayah_clean"))
jabar_clean <- filter(jabar_merged, !is.na(Prevalensi_per100k))
# z-score
mu <- mean(jabar_clean$Prevalensi_per100k, na.rm = TRUE)
sdv <- sd(jabar_clean$Prevalensi_per100k, na.rm = TRUE)
jabar_clean$zprev <- (jabar_clean$Prevalensi_per100k - mu) / sdv
jabar_clean$hotspot <- cut(jabar_clean$zprev,
breaks = c(-Inf, -1.96, 1.96, Inf),
labels = c("Cold", "Neutral", "Hot"))
ggplot(jabar_clean) +
geom_sf(aes(fill = hotspot), color = "white") +
scale_fill_manual(values = c("Cold" = "#1E88E5", "Neutral" = "grey80", "Hot" = "#E53935")) +
labs(title = "Hotspot sederhana berdasarkan z-score (Prevalensi)") +
theme_minimal()
Interpretasi:
Nilai Moran’s I yang positif dan signifikan (p < 0.05) menunjukkan bahwa kasus DBD di Jawa Barat memiliki pola spasial mengelompok (clustered). Artinya, wilayah dengan jumlah kasus tinggi cenderung berdekatan dengan wilayah yang juga memiliki kasus tinggi. Temuan ini mengindikasikan adanya faktor lingkungan dan mobilitas antarwilayah yang berpengaruh secara spasial.
Setelah melihat adanya pola mengelompok secara global, analisis LISA (Local Indicators of Spatial Association) digunakan untuk mengidentifikasi:
High–High → klaster kasus tinggi dikelilingi wilayah tinggi
Low–Low → klaster kasus rendah
High–Low → outlier tinggi di antara wilayah rendah
Low–High → outlier rendah di antara wilayah tinggi
library(spdep)
library(spatialreg)
# Menggunakan data jabar_clean yang sudah tidak ada NA
nb <- poly2nb(jabar_clean)
# Jika ada polygon tanpa tetangga → tambahkan 1 tetangga terdekat
if (any(card(nb) == 0)) {
nb <- knn2nb(knearneigh(st_coordinates(st_centroid(jabar_clean)), k = 1))
}
lw <- nb2listw(nb, style = "W")
# HITUNG LOCAL MORAN
lisa <- localmoran(jabar_clean$Prevalensi_per100k, lw)
# Masukkan hasil ke dalam data
jabar_clean$Ii <- lisa[,1]
jabar_clean$Ei <- lisa[,2]
jabar_clean$VarIi <- lisa[,3]
jabar_clean$Zi <- lisa[,4]
jabar_clean$Pi <- lisa[,5]
# KATEGORI CLUSTER
jabar_clean$cluster <- dplyr::case_when(
jabar_clean$Zi > 1.96 ~ "High-High",
jabar_clean$Zi < -1.96 ~ "Low-Low",
TRUE ~ "Not Significant"
)
# VISUALISASI LISA
library(ggplot2)
ggplot(jabar_clean) +
geom_sf(aes(fill = cluster), color = "white", size = 0.3) +
scale_fill_manual(
values = c(
"High-High" = "red",
"Low-Low" = "blue",
"Not Significant" = "grey80"
)
) +
labs(
title = "Peta Klaster LISA (Local Moran’s I)",
fill = "Cluster"
) +
theme_minimal()
Interpretasi:
Peta LISA menunjukkan adanya klaster High–High pada wilayah Bandung, Bekasi, dan Depok, yang berarti wilayah ini memiliki kasus DBD tinggi dan dikelilingi oleh wilayah dengan kasus tinggi. Wilayah dengan kategori Low–Low muncul di daerah seperti Pangandaran dan Sukabumi. Klaster High–Low atau Low–High mengindikasikan wilayah yang perilaku spasialnya berbeda dari tetangganya—biasanya merupakan kandidat outlier spasial. Temuan ini membantu pemerintah dalam menetapkan prioritas intervensi seperti fogging massal, edukasi 3M, dan pemantauan jentik secara lebih terarah.
Berdasarkan hasil analisis epidemiologi DBD di Jawa Barat, diperoleh bahwa jumlah kasus bervariasi antarwilayah dengan nilai prevalensi dan DFR (Death Fatality Rate) yang menunjukkan adanya perbedaan tingkat risiko di setiap kabupaten/kota. Pola tren kasus dari waktu ke waktu memperlihatkan fluktuasi yang dipengaruhi oleh musim, dengan beberapa periode mengalami peningkatan signifikan. Selain itu, hasil deskriptif menunjukkan bahwa beberapa wilayah secara konsisten memiliki nilai prevalensi dan DFR yang lebih tinggi dibandingkan wilayah lain, sehingga menandakan adanya beban penyakit yang tidak merata.
Analisis lanjutan memperkuat temuan bahwa karakteristik wilayah—seperti kepadatan penduduk dan kondisi lingkungan—berpotensi berkontribusi terhadap variasi risiko DBD. Informasi ini membantu mengidentifikasi wilayah prioritas yang memerlukan intervensi lebih intensif. Secara keseluruhan, gabungan analisis prevalensi, DFR, tren, dan indikator lainnya memberikan gambaran komprehensif mengenai situasi DBD di Jawa Barat, yang penting untuk perencanaan program pengendalian penyakit.
Diperlukan penguatan upaya pencegahan dan pengendalian DBD pada wilayah dengan prevalensi dan DFR tinggi, terutama dengan meningkatkan surveilans, pemberantasan sarang nyamuk, serta edukasi masyarakat. Pemerintah daerah juga diharapkan melakukan pemantauan rutin terhadap perubahan tren kasus dan meningkatkan kualitas serta kelengkapan data epidemiologi agar analisis di masa mendatang dapat lebih akurat.
Tampilan kode lengkap dari file app.R yang digunakan untuk membuat dashboard interaktif.
## # app.R - Dashboard DBD Lengkap dengan Autokorelasi Spasial & Wilayah Prioritas
## suppressPackageStartupMessages({
## library(rsconnect)
## 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
## })
##
## # ----------------------------
## # SETTING: Nama file Anda (DIMUAT OTOMATIS DARI FOLDER 'data')
## # ----------------------------
## ### DIUBAH: Menggunakan path statis, bukan file.choose()
## DATA_FILE <- "data/Data Epidem.xlsx"
## DATA_TREN_FILE <- "data/Data DBD tahunan.xlsx"
## SHP_FILE <- "data/jabar_shapefile.rds" # File dari Langkah 2
##
## # ----------------------------
## # Helper functions (Tidak berubah)
## # ----------------------------
## 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)
## }
##
## # ----------------------------
## # DITAMBAHKAN: Muat data saat aplikasi start
## # ----------------------------
##
## # 1. Muat Data Utama
## data_utama_loaded <- tryCatch({
## if (!file.exists(DATA_FILE)) {
## stop("File data utama tidak ditemukan di folder 'data'. Pastikan 'Data Epidem.xlsx' ada.")
## }
## load_and_process_data(DATA_FILE)
## }, error = function(e) {
## message("Gagal memuat data utama: ", e$message)
## NULL
## })
##
## # 2. Muat Data Tren
## data_tren_loaded <- tryCatch({
## if (!file.exists(DATA_TREN_FILE)) {
## stop("File data tren tidak ditemukan. Pastikan 'Data DBD tahunan.xlsx' ada.")
## }
## ext <- tolower(tools::file_ext(DATA_TREN_FILE))
## df_tren_raw <- if (ext %in% c("xls", "xlsx")) {
## read_excel(DATA_TREN_FILE)
## } else {
## read_csv(DATA_TREN_FILE, show_col_types = FALSE)
## }
##
## # Proses data tren
## names(df_tren_raw)[1] <- "Wilayah"
## df_tren_raw$Wilayah <- str_trim(df_tren_raw$Wilayah)
## year_cols <- names(df_tren_raw)[names(df_tren_raw) != "Wilayah"]
## for(col in year_cols) {
## df_tren_raw[[col]] <- safe_num(df_tren_raw[[col]])
## }
## df_tren_raw # return
##
## }, error = function(e) {
## message("Gagal memuat data tren: ", e$message)
## NULL
## })
##
## # 3. Muat Data Peta (Shapefile)
## jabar_shp_loaded <- tryCatch({
## if (!file.exists(SHP_FILE)) {
## stop("File Peta 'jabar_shapefile.rds' tidak ditemukan. Jalankan script 'prepare_shapefile.R' dari Langkah 2.")
## }
## shp <- readRDS(SHP_FILE)
##
## # Lakukan pra-pemrosesan nama di sini SATU KALI
## 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)
##
## # ... (Logika Waduk Cirata Anda bisa ditambahkan di sini jika masih relevan) ...
##
## # Buat kunci matching (Wilayah_Clean) di sini
## 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)
##
## shp # return
## }, error = function(e) {
## message("Gagal memuat shapefile: ", e$message)
## NULL
## })
##
##
## # ----------------------------
## # UI (Hampir tidak berubah, hanya tombol peta dihapus)
## # ----------------------------
## theme_bs <- bs_theme(
## bootswatch = "minty",
## bg = "#FAFAFA",
## fg = "#0f3d3e",
## primary = "#2C7873"
## )
##
## ui <- dashboardPage(
## skin = "black",
## dashboardHeader(title = span("Dashboard Analisis DBD Lengkap", 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("Tren Waktu", tabName = "tren", icon = icon("line-chart")),
## menuItem("Wilayah Prioritas", tabName = "prioritas", icon = icon("exclamation-triangle"))
## )
## ),
##
## dashboardBody(
## theme = theme_bs,
## tags$head(tags$style(HTML("
## /* ... CSS Anda tidak berubah ... */
## 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 Statdes, Korelasi, Freq, Interp (TIDAK BERUBAH) ...
## 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")
## )
## )
## ),
## 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")
## )
## )
## ),
## 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")
## )
## )
## ),
## 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"),
##
## ### DIHAPUS: Tombol "Muat Peta" tidak perlu lagi
## # 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 Spatial, Tren, Prioritas (TIDAK BERUBAH) ...
## tabItem(tabName = "spatial",
## fluidRow(
## box(title = "Analisis Autokorelasi Spasial (Moran's I)", status = "primary",
## solidHeader = TRUE, width = 12,
## htmlOutput("moran_info"))
## ),
## fluidRow(
## box(title = "Local Moran's I (LISA)", status = "info", solidHeader = TRUE, width = 12,
## plotOutput("lisa_map", height = "600px")
## )
## ),
## fluidRow(
## box(title = "Interpretasi Autokorelasi Spasial", status = "warning",
## solidHeader = TRUE, width = 12,
## htmlOutput("spatial_autocorr_interpretation")
## )
## )
## ),
## 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")
## )
## )
## ),
## 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")
## )
## ),
## fluidRow(
## box(title = "Visualisasi Skor Prioritas", status = "info", solidHeader = TRUE, width = 12,
## plotOutput("prioritas_plot", height = "600px")
## )
## ),
## fluidRow(
## box(title = "Rekomendasi Aksi Per Wilayah", status = "success", solidHeader = TRUE, width = 12,
## htmlOutput("prioritas_recommendations")
## )
## )
## )
## )
## )
## )
##
## # ----------------------------
## # SERVER
## # ----------------------------
## server <- function(input, output, session) {
##
## std_df <- reactiveVal(NULL)
## jabar_shp <- reactiveVal(NULL)
## tren_df <- reactiveVal(NULL)
##
## ### DIUBAH: Mengisi reactiveVal dari data yang sudah di-load di awal
##
## # 1. Isi data utama
## if (!is.null(data_utama_loaded)) {
## std_df(data_utama_loaded)
## } else {
## showNotification("KRITIS: Gagal memuat data utama. Cek 'data/Data Epidem.xlsx'", type="error", duration = NULL)
## }
##
## # 2. Isi data tren
## if (!is.null(data_tren_loaded)) {
## tren_df(data_tren_loaded)
## } else {
## showNotification("PERINGATAN: Gagal memuat data tren. Tab Tren tidak akan bekerja.", type="warning", duration = 10)
## }
##
## # 3. Isi data peta
## if (!is.null(jabar_shp_loaded)) {
## jabar_shp(jabar_shp_loaded)
## } else {
## showNotification("KRITIS: Gagal memuat data peta. Tab Peta & Spasial tidak akan bekerja.", type="error", duration = NULL)
## }
##
## ### DIHAPUS: 'observe' untuk memuat data (karena sudah dimuat di atas)
##
## # Value boxes (Tidak berubah)
## 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 (Tidak berubah)
## output$table_all <- renderDT({
## df <- std_df()
## if (is.null(df)) return(datatable(data.frame(Message = "Gagal memuat data. Periksa file."), 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 (Tidak berubah)
## 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 (Tidak berubah)
## 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")
## }
## })
##
## # ... Sisa KODE KORELASI (Tidak berubah) ...
## 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()
## }
##
## cor_df <- df %>% select(all_of(available_vars))
## cor_df <- cor_df %>% mutate(across(everything(), as.numeric))
## 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()
## }
##
## cor_matrix <- cor(cor_df, use = "pairwise.complete.obs")
##
## 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)
##
## 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')
## ))
## })
##
## 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>"))
## }
##
## 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>"))
## }
##
## cor_matrix <- cor(cor_df, use = "pairwise.complete.obs")
##
## 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({
## 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) {
## })
## }
## }
##
## 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>")
## }
##
## 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]]
##
## interpretations <- ""
##
## 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("")
##
## 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 {
## 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>"
## )
## }
##
## 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>"
## ))
## })
##
## # ... Sisa KODE INTERPRETASI EPI (Tidak berubah) ...
## output$epi_interpretation <- renderUI({
## df <- std_df()
## if (is.null(df)) return(HTML("<i>Memuat...</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)
##
## prev_agregat_persen <- (total_kasus / total_penduduk) * 100
## prev_per_100k <- (total_kasus / total_penduduk) * 100000
##
## cfr_agregat <- (total_meninggal / total_kasus) * 100
##
## 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>"
## }
##
## 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)
##
## 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>100 penduduk</b>, sekitar <b>",
## round(top_prev$Prevalensi_persen, 2), " orang</b> terinfeksi DBD</li>",
## "<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>"
## )
## }
##
## 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>100 pasien DBD</b>, sekitar <b>",
## round(top_cfr$CaseFatality_pct, 1), " orang meninggal</b></li>",
## "<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
## ))
## })
##
## # ... Sisa KODE PRIORITAS (Tidak berubah) ...
## 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
## }
##
## 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%
##
## 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]
## }
##
## 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]])
## }
## 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_
## }
##
## output$prioritas_table <- renderDT({
## 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", "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"))
##
## 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(
## 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_)
## )
##
## 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)
## )
##
## 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) ~ "Data Tidak Lengkap",
## 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_)
## ) %>%
## # Koreksi kategori lagi untuk nama yang lebih konsisten
## mutate(
## Kategori = case_when(
## !is.finite(Skor_Total) ~ "??? Data Tidak Lengkap",
## Skor_Total >= q[3] ~ " PRIO TINGGI",
## Skor_Total >= q[2] ~ "PRIO SEDANG",
## Skor_Total >= q[1] ~ "PRIO RENDAH",
## TRUE ~ "PRIO MINIMAL"
## )
## )
##
##
## out_tbl <- df2 %>%
## select(Rank, Wilayah, Jumlah_DBD, Prevalensi_persen, CaseFatality_pct,
## Kejadian_Banjir = raw_banjir,
## Akses_Sanitasi_Buruk = raw_sanit,
## Rumah_Tidak_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(" PRIO TINGGI","PRIO SEDANG","PRIO RENDAH","PRIO MINIMAL", "??? Data Tidak Lengkap"),
## c("#fee2e2","#fef3c7","#d1fae5","#f3f4f6", "#e5e7eb"))) %>%
## formatStyle('Skor_Total',
## background = styleColorBar(range(out_tbl$Skor_Total, na.rm = TRUE), '#ef4444'),
## backgroundSize = '100% 80%',
## backgroundRepeat = 'no-repeat',
## backgroundPosition = 'center')
## dat
## })
##
## 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)
## ) %>%
## filter(is.finite(Skor_Total)) %>% # Hanya plot yang ada skor
## arrange(desc(Skor_Total)) %>%
## slice_head(n = 15)
##
## if (nrow(df2) == 0) {
## plot.new(); text(0.5,0.5,"Tidak ada data skor", cex=1.2); return()
## }
##
## q <- quantile(df2$Skor_Total, probs = c(0.33, 0.66), na.rm = TRUE)
## df2$Color_Cat <- cut(df2$Skor_Total,
## breaks = c(-Inf, q[1], q[2], 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)
##
## 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)
## ) %>%
## filter(is.finite(Skor_Total)) # Hanya hitung yang lengkap
##
## 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/MINIMAL:</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, 100 - nilai)</li>",
## "<li><strong>Rumah Tidak Layak (10%):</strong> Kualitas perumahan (dibalik, 100 - nilai)</li>",
## "</ul>",
## "<p style='margin-top:10px;'><em>Total bobot: 100%. Setiap indikator dinormalisasi 0-100, lalu dijumlahkan dengan bobot.</em></p>",
## "</div>"
## ))
## })
##
## # ... Sisa KODE FREKUENSI & EPI (Tidak berubah) ...
## 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)
## })
##
## 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("<i>Memuat...</i>"))
##
## 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 (Tidak berubah, tapi observeEvent dihapus)
## 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])
## })
##
## ### DIHAPUS: observeEvent(input$btn_load_map, ...)
## # Peta akan dimuat oleh renderLeaflet secara otomatis karena jabar_shp() sudah diisi
##
## output$map_leaflet <- renderLeaflet({
## req(input$map_var)
## df <- std_df()
## shp <- jabar_shp() # Ini sekarang data yang sudah di-load
## req(df, shp)
##
## # Cleaning wilayah DATA (shp sudah di-clean saat load)
## 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)
##
## # shp$Wilayah_Clean sudah dibuat saat load
##
## 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"
## )
##
## 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"
## )
## }
## }
## })
##
## # ... Sisa KODE INTERPRETASI SPASIAL (Tidak berubah) ...
## 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 sudah ada
## 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_persen" || 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)
## })
##
## ### DIHAPUS: observe() untuk modal dialog
##
## # ... Sisa KODE AUTOKORELASI (Tidak berubah) ...
## output$moran_info <- renderUI({
## df <- std_df()
## shp <- jabar_shp()
##
## if(is.null(shp)) {
## return(HTML("<p style='color:red;'>Data peta (shapefile) tidak berhasil dimuat. Analisis spasial tidak bisa dilanjutkan.</p>"))
## }
##
## req(df, shp)
##
## tryCatch({
## df$Wilayah_Clean <- tolower(trimws(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)
##
## # shp$Wilayah_Clean sudah ada
##
## merged <- merge(shp, df, by = "Wilayah_Clean", all.x = TRUE)
## merged_complete <- merged[!is.na(merged$Prevalensi_persen), ]
##
## if(nrow(merged_complete) < 3) {
## return(HTML("<p>Tidak cukup data untuk analisis (minimal 3 wilayah).</p>"))
## }
##
## nb_queen <- poly2nb(merged_complete, queen = TRUE)
##
## no_neighbor <- which(card(nb_queen) == 0)
## if(length(no_neighbor) > 0) {
## cat("Ditemukan", length(no_neighbor), "wilayah tanpa tetangga, menggunakan k-nearest neighbor\n")
## coords <- st_coordinates(st_centroid(st_geometry(merged_complete)))
## nb_queen <- knn2nb(knearneigh(coords, k = 1))
## }
##
## lw_queen <- nb2listw(nb_queen, style = "W", zero.policy = TRUE)
##
## moran_asym <- moran.test(merged_complete$Prevalensi_persen, lw_queen, zero.policy = TRUE)
##
## set.seed(77)
## moran_mc <- moran.mc(merged_complete$Prevalensi_persen, lw_queen, nsim = 999, zero.policy = TRUE)
##
## I_asym <- as.numeric(moran_asym$estimate[["Moran I statistic"]])
## EI <- as.numeric(moran_asym$estimate[["Expectation"]])
## Z_score <- as.numeric(moran_asym$statistic)
## p_asym <- moran_asym$p.value
## p_perm <- moran_mc$p.value
##
## if(I_asym > 0.3 && p_asym < 0.05) {
## interpretation <- "<span style='color:#dc2626;'><b>KLUSTERING POSITIF KUAT</b></span>"
## meaning <- "Terdapat <b>pola klustering spasial yang signifikan</b>. Wilayah dengan prevalensi DBD tinggi cenderung bertetangga dengan wilayah prevalensi tinggi lainnya. Ini menunjukkan <b>penyebaran DBD antar wilayah</b> yang memerlukan <b>intervensi regional terkoordinasi</b>!"
## } else if(I_asym > 0 && p_asym < 0.1) {
## interpretation <- "<span style='color:#f59e0b;'><b>Klustering Positif Lemah</b></span>"
## meaning <- "Ada <b>sedikit pola klustering spasial</b>, namun tidak signifikan kuat. Penyebaran DBD masih menunjukkan pola geografis yang perlu diperhatikan dalam perencanaan intervensi."
## } else if(I_asym > 0) {
## interpretation <- "<span style='color:#3b82f6;'><b>Klustering Positif (Tidak Signifikan)</b></span>"
## meaning <- "Ada kecenderungan klustering lemah, namun <b>tidak signifikan secara statistik</b>. Pola penyebaran DBD cenderung acak."
## } else {
## interpretation <- "<span style='color:#6b7280;'><b>Tidak Ada Klustering / Pola Acak</b></span>"
## meaning <- "Tidak ada bukti klustering spasial. Pola penyebaran DBD cenderung <b>acak dan tidak dipengaruhi oleh kedekatan geografis</b>."
## }
##
## HTML(paste0(
## "<h4>Hasil Analisis Moran's I Global</h4>",
## "<div class='interpretation-box'>",
## "<table style='width:100%; border-collapse:collapse;'>",
## "<tr><td style='padding:8px; border:1px solid #ddd;'><strong>Moran's I (Asimtotik):</strong></td>",
## "<td style='padding:8px; border:1px solid #ddd;'><span class='highlight-value'>", round(I_asym, 4), "</span></td></tr>",
## "<tr><td style='padding:8px; border:1px solid #ddd;'><strong>Expected I:</strong></td>",
## "<td style='padding:8px; border:1px solid #ddd;'>", round(EI, 4), "</td></tr>",
## "<tr><td style='padding:8px; border:1px solid #ddd;'><strong>Z-Score:</strong></td>",
## "<td style='padding:8px; border:1px solid #ddd;'>", round(Z_score, 3), "</td></tr>",
## "<tr><td style='padding:8px; border:1px solid #ddd;'><strong>P-value (Asimtotik):</strong></td>",
## "<td style='padding:8px; border:1px solid #ddd;'>", format.pval(p_asym, digits = 4), "</td></tr>",
## "<tr><td style='padding:8px; border:1px solid #ddd;'><strong>P-value (Permutasi):</strong></td>",
## "<td style='padding:8px; border:1px solid #ddd;'>", format.pval(p_perm, digits = 4), "</td></tr>",
## "</table>",
## "</div>",
## "<div class='interpretation-box'>",
## "<h5>Interpretasi:</h5>",
## "<p>", interpretation, "</p>",
## "<p><strong>Artinya:</strong> ", meaning, "</p>",
## "</div>",
## "<div style='background:#f0f9ff; padding:10px; margin-top:10px; border-radius:5px;'>",
## "<p style='font-size:13px; margin:0;'><strong>Catatan Metodologi:</strong></p>",
## "<ul style='font-size:12px; margin:5px 0;'>",
## "<li>Menggunakan <b>Queen Contiguity</b> untuk definisi tetangga</li>",
## "<li>Wilayah tanpa tetangga ditangani dengan <b>k-nearest neighbor (k=1)</b></li>",
## "<li>Validasi dengan <b>Monte Carlo permutation (999 simulasi)</b></li>",
## "</ul>",
## "</div>"
## ))
## }, error = function(e) {
## HTML(paste0("<p style='color:red;'>Error dalam analisis: ", e$message, "</p>"))
## })
## })
##
## output$lisa_map <- renderPlot({
## df <- std_df()
## shp <- jabar_shp()
## req(df, shp)
##
## tryCatch({
## df$Wilayah_Clean <- tolower(trimws(gsub("^(kab\\.?|kabupaten)\\s+", "", df$Wilayah)))
## # shp$Wilayah_Clean sudah ada
## merged <- merge(shp, df, by = "Wilayah_Clean", all.x = TRUE)
##
## merged_complete <- merged[!is.na(merged$Prevalensi_persen), ]
##
## if(nrow(merged_complete) < 3) {
## plot.new()
## text(0.5, 0.5, "Tidak cukup data", cex = 1.5)
## return()
## }
##
## nb <- poly2nb(merged_complete, queen = TRUE)
## lw <- nb2listw(nb, style = "W", zero.policy = TRUE)
##
## local_moran <- localmoran(merged_complete$Prevalensi_persen, lw, zero.policy = TRUE)
##
## merged_complete$lisa_cluster <- NA
## merged_complete$lisa_cluster[local_moran[,5] < 0.05] <- "Signifikan"
## merged_complete$lisa_cluster[local_moran[,5] >= 0.05] <- "Tidak Signifikan"
##
## plot(st_geometry(merged_complete), col = "lightgray", border = "white",
## main = "Local Moran's I (LISA) - Prevalensi DBD")
##
## sig_idx <- which(local_moran[,5] < 0.05)
## if(length(sig_idx) > 0) {
## plot(st_geometry(merged_complete[sig_idx,]), col = "#dc2626", add = TRUE)
## }
##
## legend("bottomright",
## legend = c("Signifikan (p<0.05)", "Tidak Signifikan"),
## fill = c("#dc2626", "lightgray"),
## title = "Kluster Lokal")
## }, error = function(e) {
## plot.new()
## text(0.5, 0.5, paste("Error:", e$message), cex = 1)
## })
## }, res = 96)
##
## output$spatial_autocorr_interpretation <- renderUI({
## HTML(paste0(
## "<h4>Panduan Interpretasi Autokorelasi Spasial</h4>",
## "<div class='interpretation-box'>",
## "<h5>Moran's I Global</h5>",
## "<ul>",
## "<li><strong>Nilai positif tinggi (>0.3):</strong> Pola klustering - wilayah serupa bertetangga</li>",
## "<li><strong>Nilai mendekati 0:</strong> Pola acak - tidak ada klustering</li>",
## "<li><strong>Nilai negatif:</strong> Pola tersebar - wilayah berbeda bertetangga</li>",
## "</ul>",
## "</div>",
## "<div class='interpretation-box'>",
## "<h5>LISA (Local Moran's I)</h5>",
## "<p>Menunjukkan <b>wilayah mana yang menjadi hotspot</b> (kluster dengan nilai tinggi yang signifikan).</p>",
## "<p><strong>Wilayah merah:</strong> Kluster lokal yang signifikan (p<0.05) - memerlukan <b>perhatian khusus!</b></p>",
## "</div>"
## ))
## })
##
##
## # ... Sisa KODE TREN (Tidak berubah) ...
## output$tren_info <- renderUI({
## df <- tren_df()
## if (is.null(df)) {
## return(HTML("<p style='color:red;'>Data tren waktu ('Data DBD tahunan.xlsx') tidak berhasil dimuat.</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>"
## ))
## })
##
## 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(is.na(slope)) {
## "<span style='color:grey;'><b>DATA TIDAK CUKUP</b></span>"
## } else 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)