Curah hujan merupakan salah satu unsur iklim yang berperan penting dalam pengelolaan sumber daya air, sektor pertanian, serta mitigasi bencana hidrometeorologi seperti banjir dan kekeringan. Informasi mengenai pola dan sebaran curah hujan diperlukan untuk memahami variasi kondisi lingkungan antarwilayah dan mendukung pengambilan keputusan yang berkaitan dengan pengelolaan air serta perencanaan wilayah. Di Indonesia, kondisi geografis dan iklim yang beragam menyebabkan curah hujan berbeda antarwilayah. Variasi tersebut antara lain dipengaruhi oleh angin monsun dan kondisi topografi. Sebagai contoh, curah hujan maksimum di sekitar Gunung Merapi secara konsisten terpusat di sisi barat, yang dikaitkan dengan angin monsun barat dan pengangkatan massa udara di lereng barat (Sofia et al., 2018).
Pengukuran curah hujan umumnya dilakukan melalui stasiun pengamatan yang tersebar pada lokasi tertentu. Namun, nilai yang diperoleh dari stasiun tersebut belum dapat memberikan informasi secara langsung mengenai kondisi curah hujan pada seluruh wilayah, khususnya pada lokasi yang tidak memiliki stasiun pengamatan. Oleh karena itu, diperlukan metode interpolasi spasial untuk memperkirakan nilai curah hujan pada lokasi yang tidak terukur berdasarkan informasi dari stasiun-stasiun yang tersedia.
Interpolasi spasial dapat dilakukan menggunakan beberapa metode dengan pendekatan yang berbeda. Inverse Distance Weighted (IDW) memperkirakan nilai pada lokasi tertentu berdasarkan nilai pengamatan di sekitarnya dengan memberikan bobot yang dipengaruhi oleh jarak. Sementara itu, Ordinary Kriging (OK) memanfaatkan struktur ketergantungan spasial yang dimodelkan melalui variogram dengan asumsi rata-rata lokal konstan tetapi tidak diketahui. Adapun Universal Kriging (UK) mempertimbangkan adanya tren spasial dalam proses pendugaan. Perbedaan pendekatan tersebut memungkinkan setiap metode menghasilkan estimasi dengan tingkat ketelitian yang berbeda. Pada data curah hujan bulanan Selandia Baru dari 294 pos hujan, metode geostatistik menghasilkan galat yang lebih kecil dibandingkan IDW pada seluruh bulan, sedangkan penambahan elevasi dan koordinat sebagai informasi tambahan hanya memberikan peningkatan kecil (Caloiero et al., 2021). Hal ini menunjukkan bahwa kinerja metode interpolasi perlu dibandingkan pada data yang dianalisis, bukan diasumsikan.
Penelitian ini menggunakan data curah hujan tahunan stasiun pengamatan di Indonesia dari Southeast Asia Climate Assessment & Dataset (SACA&D) yang dikelola BMKG, bagian dari International Climate Assessment & Dataset (ICA&D) (van den Besselaar et al., 2015). Analisis mencakup pemeriksaan dan pembersihan data, eksplorasi karakteristik curah hujan, visualisasi sebaran stasiun, serta pemetaan hasil interpolasi menggunakan metode IDW, Ordinary Kriging, dan Universal Kriging. Kinerja ketiga metode dibandingkan menggunakan Leave-One-Out Cross-Validation (LOOCV), yaitu prosedur validasi silang yang dilakukan dengan mengeluarkan satu pengamatan secara bergantian dari proses pemodelan, kemudian membandingkan nilai prediksi dengan nilai pengamatan sebenarnya. Melalui analisis tersebut, diharapkan diperoleh gambaran mengenai pola sebaran curah hujan tahunan di Indonesia serta informasi mengenai perbedaan kinerja ketiga metode interpolasi.
Interpolasi curah hujan di Indonesia telah banyak dikaji pada skala lokal dan regional. Sanusi et al. (2024) membandingkan IDW (pangkat 2) dengan Ordinary Kriging berbagai model semivariogram (spherical, gaussian, exponential) pada curah hujan harian maksimum dari 56 pos hujan di Sulawesi Selatan periode 1986–2021. Ordinary Kriging dengan semivariogram gaussian memberikan galat terkecil (RMSE 57,45; MAE 46,49), sedikit lebih baik daripada IDW (RMSE 59,96; MAE 48,10). Universal Kriging dalam penelitian tersebut hanya dibahas pada tinjauan pustaka dan tidak diuji. Tahitu et al. (2026) menerapkan IDW untuk curah hujan tahun 2023 di Kabupaten Langkat berdasarkan 25 titik pengamatan, dengan variasi pangkat jarak 1 sampai 6,8. Pangkat 6,8 memberikan RMSE terkecil (49,99 mm/tahun), dengan evaluasi menggunakan lima titik uji dan tanpa pembanding metode kriging.
Penelitian lain berfokus pada Ordinary Kriging. Bahtiyar et al. (2014) mengestimasi curah hujan bulan Februari di Kota Semarang dengan membandingkan model variogram exponential, gaussian, dan spherical. Rohma (2022) menerapkan Ordinary Kriging dengan teknik jackknife pada curah hujan bulanan Malang Raya tahun 2016 dan menyimpulkan bahwa metode tersebut dapat digunakan baik pada data berdistribusi normal maupun tidak normal. Najwa et al. (2025) mengembangkan SAR-X Kriging, yaitu gabungan model spatial autoregressive dengan variabel eksogen dan Ordinary Kriging, untuk prediksi curah hujan di Pulau Jawa. Galat MAPE SAR-X Kriging (3,82%) hanya sedikit lebih kecil daripada Ordinary Kriging (3,86%).
Dari penelitian-penelitian tersebut terdapat beberapa celah yang akan dibahas
Cakupan wilayah dan jumlah stasiun. Penelitian terdahulu pada umumnya mencakup satu kabupaten, kota, atau provinsi dengan puluhan pos hujan, sedangkan penelitian ini mencakup seluruh Indonesia dengan ribuan stasiun.
Perbandingan tiga metode. Penelitian terdahulu yang ditelaah membandingkan IDW dengan Ordinary Kriging (Sanusi et al., 2024) atau hanya memakai salah satu metode (Tahitu et al., 2026; Bahtiyar et al., 2014; Rohma, 2022). Penelitian ini membandingkan IDW, Ordinary Kriging, dan Universal Kriging secara langsung pada data yang sama.
Skema validasi. Evaluasi pada penelitian terdahulu menggunakan jumlah titik uji yang kecil (Tahitu et al., 2026) atau tidak dijelaskan sebagai validasi silang (Sanusi et al., 2024). Penelitian ini mengevaluasi seluruh stasiun dengan LOOCV sehingga setiap stasiun menjadi titik uji.
Berdasarkan latar belakang tersebut, rumusan masalah dalam penelitian ini adalah sebagai berikut.
Bagaimana karakteristik dan sebaran spasial curah hujan tahunan di Indonesia berdasarkan data stasiun pengamatan?
Bagaimana hasil pemetaan curah hujan tahunan menggunakan metode IDW, Ordinary Kriging, dan Universal Kriging?
Bagaimana perbandingan kinerja ketiga metode interpolasi berdasarkan hasil LOOCV, dan metode manakah yang memberikan kinerja prediksi terbaik?
Penelitian ini bertujuan untuk:
Mendeskripsikan karakteristik data dan sebaran spasial curah hujan tahunan di Indonesia.
Menghasilkan peta estimasi curah hujan tahunan menggunakan metode IDW, Ordinary Kriging, dan Universal Kriging.
Mengevaluasi dan membandingkan kinerja ketiga metode interpolasi menggunakan LOOCV, serta menentukan metode dengan kinerja prediksi terbaik berdasarkan ukuran kesalahan prediksi yang digunakan.
Penelitian ini diharapkan dapat memberikan manfaat berupa:
Manfaat metodologis, yaitu memberikan pemahaman mengenai penerapan dan perbandingan metode interpolasi spasial untuk data curah hujan.
Penelitian ini dibatasi pada data curah hujan tahunan tahun 2024 dari stasiun pengamatan di Indonesia yang memenuhi kriteria kelengkapan data dan validasi koordinat. Metode interpolasi yang dibandingkan adalah IDW, Ordinary Kriging, dan Universal Kriging. Evaluasi kinerja prediksi dilakukan menggunakan LOOCV berdasarkan ukuran kesalahan prediksi yang diterapkan dalam analisis. Hasil penelitian merepresentasikan estimasi berdasarkan data dan model yang digunakan, sehingga interpretasinya perlu mempertimbangkan persebaran stasiun pengamatan serta keterbatasan metode interpolasi.
Penelitian ini merupakan penelitian kuantitatif dengan pendekatan analisis deskriptif dan spasial. Analisis deskriptif digunakan untuk menggambarkan karakteristik data curah hujan tahunan, sedangkan analisis spasial dilakukan untuk memetakan estimasi curah hujan pada lokasi yang tidak memiliki pengamatan langsung. Metode interpolasi yang digunakan meliputi Inverse Distance Weighted (IDW), Ordinary Krigin* (OK), dan Universal Kriging (UK). Hasil ketiga metode dibandingkan menggunakan Leave-One-Out Cross-Validation (LOOCV). Alur analisis secara berurutan adalah persiapan data, kontrol kualitas data, analisis deskriptif, visualisasi spasial, interpolasi, dan evaluasi model (Gambar 2.1).
Gambar 2.1. Alur penelitian
Data yang digunakan adalah data curah hujan tahunan (annual
precipitation) tahun 2024 dari stasiun pengamatan di Indonesia. Data
bersumber dari Southeast Asia Climate Assessment & Dataset
(SACA&D) yang dikelola oleh BMKG dan merupakan bagian dari
International Climate Assessment & Dataset (ICA&D) (van den
Besselaar et al., 2015). Dua jenis berkas dari sumber tersebut
digunakan, yaitu stations.txt sebagai sumber identitas,
koordinat, dan ketinggian stasiun, serta berkas indeks curah hujan
indexRR sebagai sumber nilai curah hujan.
Variabel utama yang dianalisis adalah ANNUAL, yaitu
jumlah curah hujan tahunan. Pada berkas sumber, nilai indeks curah hujan
(RR) dinyatakan dalam satuan 0,01 mm sehingga seluruh nilai
ANNUAL dibagi 100 agar dinyatakan dalam milimeter (mm).
Koordinat lintang (LAT) dan bujur (LON)
digunakan untuk menentukan posisi stasiun. Data difokuskan pada tahun
2024 dan selanjutnya diseleksi berdasarkan ketersediaan nilai curah
hujan serta validitas koordinat.
Pada tahun 2024 tercatat 7.472 stasiun Indonesia dengan data indeks
curah hujan, tetapi 2.864 di antaranya tidak memiliki nilai
ANNUAL. Jumlah stasiun akhir yang digunakan dalam analisis,
setelah pembersihan data dan pemeriksaan koordinat, dilaporkan pada
subbab Kontrol Kualitas Data (Bab 3). Jumlah tersebut merupakan stasiun
yang memenuhi kriteria pengolahan data, bukan jumlah seluruh stasiun
yang tersedia pada sumber data.
Data curah hujan dibaca dari berkas indexRR, kemudian
dipilih berdasarkan identitas stasiun yang berada di wilayah Indonesia.
Data tersebut digabungkan dengan informasi pada berkas
stations.txt menggunakan identitas stasiun
(STAID) sebagai kunci penggabungan. Selanjutnya, data
diseleksi untuk memperoleh pengamatan tahun 2024.
Pembersihan data dilakukan dalam urutan berikut.
Kelengkapan nilai. Stasiun tanpa nilai ANNUAL
dikeluarkan dari data analisis.
Duplikasi identitas. Pengamatan dengan STAID ganda
diperiksa; hanya satu baris per stasiun yang dipertahankan.
Konversi koordinat. Koordinat lintang dan bujur yang semula ditulis dalam format derajat, menit, dan detik dikonversi ke derajat desimal.
Pemeriksaan koordinat. Koordinat diperiksa dengan tiga kriteria: (a) berada di luar kotak batas Indonesia (lintang −11° sampai 6° dan bujur 95° sampai 141°); (b) nilai lintang sama dengan nilai bujur, yang mengindikasikan kesalahan input; dan (c) berjarak lebih dari 50 km dari daratan Indonesia berdasarkan poligon batas negara.
Koreksi tanda lintang. Pemeriksaan menunjukkan adanya stasiun yang tanda lintangnya tertulis utara (+) pada metadata sumber, padahal nama dan bujurnya menunjukkan lokasi di selatan khatulistiwa. Koreksi dilakukan dengan membalik tanda lintang hanya jika memenuhi aturan eksplisit: lintang melebihi batas atas kotak batas dengan kebalikannya masuk rentang Indonesia dan bujur wajar, atau stasiun jatuh di luar daratan Indonesia sedangkan titik dengan tanda lintang dibalik berada dalam 25 km dari daratan. Seluruh stasiun yang dikoreksi didaftarkan agar dapat diverifikasi.
Stasiun yang dikeluarkan. Stasiun yang tetap gagal pemeriksaan koordinat setelah koreksi dikeluarkan dari analisis dan dicatat sebagai temuan kualitas data.
Koordinat kembar. Stasiun dengan koordinat yang sama dalam proyeksi analisis dikurangi menjadi satu titik, karena koordinat kembar membuat matriks kovariansi pada kriging menjadi singular.
Pemeriksaan nilai ekstrem. Nilai ANNUAL yang berada
di luar batas pagar dalam metode rentang antarkuartil (interquartile
range, IQR), yaitu di bawah \(Q_1 -
1{,}5\times\mathrm{IQR}\) atau di atas \(Q_3 + 1{,}5\times\mathrm{IQR}\),
diidentifikasi. Nilai tersebut tidak langsung dibuang karena curah hujan
ekstrem di Indonesia dapat bersifat nyata. Stasiun hanya dikeluarkan
apabila terbukti salah ukur atau salah input berdasarkan informasi
sumber.
Data yang tampak mencurigakan tidak dikoreksi hanya berdasarkan nama stasiun, melainkan berdasarkan aturan di atas dan dilaporkan agar dapat diperiksa terhadap informasi sumber. Data bersih disimpan dan digunakan pada seluruh analisis berikutnya. Rangkaian pemeriksaan koordinat diringkas pada Gambar 2.2.
Gambar 2.2. Alur kontrol kualitas koordinat dan nilai
Analisis deskriptif dilakukan untuk mengetahui karakteristik curah hujan tahunan melalui ukuran statistik berupa rata-rata, median, simpangan baku, koefisien variasi, nilai minimum dan maksimum, kuartil, serta kemencengan (skewness). Normalitas distribusi diuji dengan uji Shapiro-Wilk, dan distribusi data diamati melalui histogram dan boxplot.
Eksplorasi spasial dilakukan dengan memetakan lokasi stasiun
berdasarkan nilai curah hujan, baik dengan skala warna kontinu maupun
kelas kuantil. Untuk melihat adanya tren spasial berskala luas,
dilakukan regresi linear dengan curah hujan tahunan
(ANNUAL, dalam mm) sebagai variabel respons dan koordinat
\(X\) (arah timur-barat, easting) serta
\(Y\) (arah utara-selatan, northing)
sebagai variabel penjelas, yaitu \(\mathrm{ANNUAL}=\beta_0+\beta_1X+\beta_2Y+\varepsilon\).
\(X\) dan \(Y\) adalah koordinat stasiun hasil proyeksi
Lambert Azimuthal Equal Area dalam satuan kilometer, bukan variabel
lingkungan tersendiri, dan keeratan hubungan dengan ketinggian stasiun
dilihat melalui koefisien korelasi pada stasiun dengan ketinggian lebih
dari nol (nilai ketinggian 0 diperlakukan sebagai data hilang karena
banyak stasiun pedalaman yang tercatat 0 m). Informasi tersebut
digunakan untuk memahami cakupan data dan mempertimbangkan keterbatasan
hasil interpolasi.
Seluruh metode interpolasi menggunakan semua stasiun sebagai data masukan (tanpa pembatasan radius pencarian) dan menduga nilai curah hujan pada titik-titik grid prediksi yang dijelaskan pada Subbab 2.5.
Inverse Distance Weighted merupakan metode interpolasi deterministik yang memperkirakan nilai pada suatu lokasi sebagai rata-rata tertimbang nilai pengamatan di sekitarnya, dengan bobot yang menurun terhadap jarak (Caloiero et al., 2021). Stasiun yang lebih dekat memperoleh bobot lebih besar dibandingkan stasiun yang lebih jauh. Estimasi IDW dirumuskan sebagai:
\[ \hat{Z}(s_0)= \frac{\displaystyle\sum_{i=1}^{n}d_{i0}^{-p}Z(s_i)} {\displaystyle\sum_{i=1}^{n}d_{i0}^{-p}} \]
dengan:
\(\hat{Z}(s_0)\) adalah nilai curah hujan yang diestimasi pada lokasi \(s_0\)
\(Z(s_i)\) adalah nilai curah hujan pada stasiun ke-\(i\)
\(d_{i0}\) adalah jarak antara stasiun ke-\(i\) dan lokasi estimasi
\(p\) adalah parameter pangkat jarak yang mengatur besarnya pengaruh jarak terhadap bobot
\(n\) adalah jumlah stasiun pengamatan yang digunakan dalam estimasi
Pangkat jarak \(p\) sering ditetapkan sama dengan 2 (Caloiero et al., 2021; Sanusi et al., 2024), tetapi dapat pula dipilih dengan membandingkan galat prediksi pada beberapa nilai \(p\) (Tahitu et al., 2026). Pada penelitian ini, nilai \(p\) dipilih dari 1 sampai 6 berdasarkan RMSE LOOCV terkecil. IDW digunakan sebagai metode pembanding bagi metode kriging.
Ordinary Kriging merupakan metode interpolasi geostatistik yang memperhitungkan ketergantungan spasial antarobservasi melalui variogram. Metode ini mengasumsikan bahwa nilai rata-rata tidak diketahui tetapi konstan pada wilayah lokal yang dianalisis. Estimasi diperoleh sebagai kombinasi linear berbobot dari nilai pengamatan:
\[\hat{Z}(s_0)=\sum_{i=1}^{n}\lambda_i Z(s_i),\qquad \sum_{i=1}^{n}\lambda_i=1\]
dengan bobot \(\lambda_i\) ditentukan dengan meminimumkan ragam galat prediksi berdasarkan struktur variasi spasial yang dinyatakan oleh semivariogram:
\[\gamma(h)=\tfrac{1}{2}\,E\!\left[\{Z(s)-Z(s+h)\}^2\right]\]
Tahapan penerapan Ordinary Kriging meliputi
penyusunan semivariogram empiris pada selang jarak (lag) selebar 75 km dengan jarak maksimum 1.500 km
pencocokan model semivariogram teoretis dengan metode kuadrat terkecil terboboti
penggunaan model terpilih untuk menghasilkan estimasi dan ragam galat pada lokasi yang tidak teramati.
Tiga model kandidat dibandingkan, yaitu spherical, exponential, dan gaussian, sebagaimana lazim dilakukan pada kajian curah hujan (Bahtiyar et al., 2014; Sanusi et al., 2024). Model dengan jumlah kuadrat galat pencocokan terkecil dipilih. Ketiga model memiliki parameter nugget (\(c_0\)), partial sill (\(c\)), dan range (\(a\)). Sebagai contoh, model exponential dinyatakan sebagai:
\[\gamma(h)=c_0+c\left(1-e^{-h/a}\right),\quad h>0\]
Universal Kriging merupakan metode interpolasi geostatistik yang mempertimbangkan adanya tren spasial pada nilai pengamatan. Berbeda dengan Ordinary Kriging yang mengasumsikan rata-rata lokal konstan, Universal Kriging memungkinkan nilai rata-rata berubah mengikuti fungsi koordinat atau komponen tren lainnya:
\[Z(s)=\mu(s)+\varepsilon(s),\qquad \mu(s)=\beta_0+\beta_1 X+\beta_2 Y\]
dengan \(\varepsilon(s)\) adalah komponen galat acak yang memiliki struktur ketergantungan spasial. Pada penelitian ini, tren dimodelkan sebagai fungsi linear dari koordinat \(X\) dan \(Y\) dalam satuan kilometer. Ketinggian stasiun tidak digunakan sebagai komponen tren karena nilainya tidak tersedia pada seluruh titik grid prediksi dan banyak stasiun memiliki nilai ketinggian 0 yang tidak dapat dipastikan keakuratannya. Struktur variasi spasial pada residu tren dimodelkan melalui semivariogram dengan prosedur pemilihan model yang sama seperti pada Ordinary Kriging. Pemilihan model semivariogram pada Universal Kriging juga telah diterapkan pada kajian di Indonesia (Sofieyanti & Sirodj, 2024).
Koordinat stasiun diproyeksikan ke sistem koordinat planar Lambert Azimuthal Equal Area yang berpusat pada 2° LS dan 118° BT dengan satuan kilometer (Snyder, 1987), sehingga jarak antartitik dapat dihitung secara konsisten di seluruh wilayah Indonesia. Pengolahan data spasial dilakukan menggunakan konsep simple features (Pebesma, 2018).
Prediksi dibuat pada grid beraturan berjarak 25 km. Titik grid hanya
dipertahankan apabila berada di dalam poligon batas daratan Indonesia,
yaitu batas negara dari data Natural Earth skala menengah yang diakses
melalui paket rnaturalearth dan disederhanakan dengan
toleransi 5 km untuk mempercepat perhitungan. Hasil interpolasi
disajikan sebagai peta estimasi curah hujan untuk masing-masing metode.
Untuk metode kriging, disajikan pula peta simpangan baku galat prediksi
sebagai ukuran ketidakpastian; IDW tidak menghasilkan ukuran
tersebut.
Hasil pemetaan perlu diinterpretasikan dengan memperhatikan persebaran stasiun pengamatan. Daerah dengan stasiun yang relatif jarang dapat memiliki tingkat ketidakpastian estimasi yang berbeda dari daerah dengan pengamatan yang lebih rapat.
Evaluasi model dilakukan menggunakan Leave-One-Out Cross-Validation (LOOCV). Pada prosedur ini, satu pengamatan dikeluarkan secara bergantian, kemudian nilainya diprediksi berdasarkan pengamatan lainnya. Nilai prediksi selanjutnya dibandingkan dengan nilai aktual pada stasiun yang dikeluarkan tersebut. Prosedur ini serupa dengan teknik jackknife yang pernah diterapkan pada kriging curah hujan (Rohma, 2022). Seluruh stasiun menjadi titik uji sehingga evaluasi tidak bergantung pada pemilihan sejumlah kecil titik uji (Gambar 2.3).
Gambar 2.3. Prosedur evaluasi LOOCV
Pada IDW, prediksi LOOCV dihitung langsung dengan mengeluarkan stasiun yang dievaluasi dari penjumlahan bobot. Pada OK dan UK dengan seluruh stasiun sebagai tetangga, prediksi LOOCV dihitung secara eksak dengan satu kali inversi matriks kriging berukuran \(n\times n\), tanpa mengulang proses kriging sebanyak \(n\) kali (Dubrule, 1983). Jika \(A\) adalah matriks sistem kriging yang diperluas dengan basis tren (\(\mathbf{K}\) matriks kovariansi antarstasiun dan \(\mathbf{F}\) matriks basis tren) dan \(\mathbf{y}=(Z_1,\dots,Z_n,0,\dots,0)^{\top}\), maka galat LOOCV pada stasiun ke-\(i\) adalah:
\[ e_i=\frac{\left[A^{-1}\mathbf{y}\right]_i}{\left[A^{-1}\right]_{ii}},\qquad A=\begin{pmatrix}\mathbf{K}&\mathbf{F}\\ \mathbf{F}^{\top}&\mathbf{0}\end{pmatrix} \]
dan prediksi LOOCV adalah \(\hat{Z}_{-i}=Z_i-e_i\). Model semivariogram diestimasi satu kali dari seluruh data sehingga LOOCV pada metode kriging dapat sedikit lebih optimistis dibanding situasi sebenarnya, yaitu ketika model semivariogram harus diestimasi tanpa stasiun yang dievaluasi.
Kinerja model dievaluasi menggunakan ukuran kesalahan prediksi, yaitu Root Mean Squared Error (RMSE), Mean Absolute Error (MAE), dan Mean Error (ME), sebagaimana digunakan pada validasi silang interpolasi curah hujan sebelumnya (Caloiero et al., 2021; Gräler et al., 2016). RMSE relatif, yaitu RMSE dibagi rata-rata curah hujan observasi dan dinyatakan dalam persen, digunakan untuk menggambarkan besarnya galat relatif terhadap tingkat curah hujan. Koefisien determinasi (\(R^2\)) dihitung sebagai kuadrat koefisien korelasi Pearson antara nilai aktual dan nilai prediksi LOOCV.
RMSE dirumuskan sebagai:
\[ \mathrm{RMSE}= \sqrt{\frac{1}{n}\sum_{i=1}^{n}(Z_i-\hat{Z}_i)^2} \]
MAE dirumuskan sebagai:
\[ \mathrm{MAE}= \frac{1}{n}\sum_{i=1}^{n}|Z_i-\hat{Z}_i| \]
ME dirumuskan sebagai:
\[ \mathrm{ME}= \frac{1}{n}\sum_{i=1}^{n}(\hat{Z}_i-Z_i) \]
dengan \(Z_i\) sebagai nilai aktual, \(\hat{Z}_i\) sebagai nilai prediksi LOOCV, dan \(n\) sebagai jumlah pengamatan yang dievaluasi. RMSE dan MAE yang lebih kecil menunjukkan kesalahan prediksi yang lebih rendah, sedangkan ME yang mendekati nol menunjukkan bahwa rata-rata kesalahan prediksi tidak condong ke satu arah. Nilai \(R^2\) digunakan sebagai ukuran tambahan untuk melihat kesesuaian nilai prediksi dengan nilai aktual.
Metode dengan nilai RMSE LOOCV terkecil dipilih sebagai metode dengan kinerja prediksi terbaik di antara metode yang dibandingkan. Namun, perbedaan nilai evaluasi tetap perlu dipertimbangkan secara proporsional dan tidak secara otomatis menunjukkan bahwa suatu metode unggul secara signifikan.
Pengolahan data, analisis statistik, pemodelan interpolasi, evaluasi
prediksi, dan visualisasi dilakukan menggunakan perangkat lunak R versi
4.5.2. Paket yang digunakan mencakup dplyr untuk pengolahan
data, ggplot2 untuk visualisasi, sf untuk
pengolahan data spasial, gstat untuk pemodelan
semivariogram dan interpolasi geostatistik (Gräler et al., 2016),
rnaturalearth dan rnaturalearthdata untuk
batas wilayah, serta knitr dan rmarkdown untuk
penyusunan laporan.
if (!file.exists(file_zip)) {
stop("File zip tidak ditemukan. Ubah nilai 'file_zip' pada chunk 'setup'.")
}
folder_data <- tempfile()
dir.create(folder_data)
unzip(file_zip, exdir = folder_data)
# Konversi koordinat DMS ("-06:11:00") ke derajat desimal
dms_to_decimal <- function(x) {
x <- trimws(as.character(x))
vapply(x, function(z) {
if (is.na(z) || z == "") return(NA_real_)
tanda <- if (substr(z, 1, 1) == "-") -1 else 1
bagian <- strsplit(sub("^[+-]", "", z), ":", fixed = TRUE)[[1]]
if (length(bagian) != 3) return(NA_real_)
tanda * (as.numeric(bagian[1]) +
as.numeric(bagian[2]) / 60 +
as.numeric(bagian[3]) / 3600)
}, numeric(1), USE.NAMES = FALSE)
}
# Ambil STAID dari header file indexRR
cari_staid <- function(header) {
baris_id <- grep("STAID:", header, value = TRUE)
if (length(baris_id) == 0) return(NA_integer_)
as.integer(sub(".*STAID:\\s*([0-9]+).*", "\\1", baris_id[1]))
}
file_stasiun <- list.files(
folder_data, pattern = "^stations\\.txt$",
recursive = TRUE, full.names = TRUE, ignore.case = TRUE
)
if (length(file_stasiun) == 0) stop("File stations.txt tidak ditemukan.")
stations <- read.csv(
file_stasiun[1], skip = 15, header = TRUE,
strip.white = TRUE, fill = TRUE
)
names(stations) <- trimws(names(stations))
stations[] <- lapply(stations, function(x) if (is.character(x)) trimws(x) else x)
stations_id <- stations %>%
filter(toupper(COUNTRYNAME) == "INDONESIA") %>%
mutate(STAID = as.integer(STAID))
cat("Jumlah stasiun Indonesia dalam metadata:",
n_distinct(stations_id$STAID), "\n")
## Jumlah stasiun Indonesia dalam metadata: 8890
files_rr <- list.files(
folder_data, pattern = "^indexRR.*\\.txt$",
recursive = TRUE, full.names = TRUE, ignore.case = TRUE
)
cat("Jumlah file curah hujan ditemukan:", length(files_rr), "\n")
## Jumlah file curah hujan ditemukan: 10061
if (length(files_rr) == 0) stop("File indexRR tidak ditemukan.")
baca_hujan <- function(file) {
header <- readLines(file, n = 30, warn = FALSE)
staid <- cari_staid(header)
# Hanya stasiun Indonesia
if (is.na(staid) || !staid %in% stations_id$STAID) return(NULL)
data <- read.table(file, skip = 30, header = FALSE, na.strings = "-999999")
if (ncol(data) != 21) {
warning("Jumlah kolom tidak sesuai: ", basename(file))
return(NULL)
}
names(data) <- c(
"SOUID", "YEAR", "ANNUAL", "ONDJFM", "AMJJAS", "DJF", "MAM", "JJA",
"SON", "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG",
"SEP", "OCT", "NOV", "DEC"
)
data$STAID <- staid
data
}
data_indonesia <- bind_rows(lapply(files_rr, baca_hujan))
if (nrow(data_indonesia) == 0) stop("Tidak ada data curah hujan stasiun Indonesia.")
cat("Jumlah baris data curah hujan:", nrow(data_indonesia), "\n")
## Jumlah baris data curah hujan: 596917
cat("Jumlah stasiun dengan data curah hujan:",
n_distinct(data_indonesia$STAID), "\n")
## Jumlah stasiun dengan data curah hujan: 8890
cat("=== Header stations.txt ===\n")
## === Header stations.txt ===
cat(readLines(file_stasiun[1], n = 15, warn = FALSE), sep = "\n")
## SOUTHEAST ASIA CLIMATE ASSESSMENT & DATASET (SACA&D), file created on 02-12-2025
## THESE DATA CAN BE USED FREELY PROVIDED THAT THE FOLLOWING SOURCE IS ACKNOWLEDGED:
##
## van den Besselaar and Coauthors, 2015.
## International Climate Assessment & Dataset (ICA&D): climate services across borders.
## Bulletin of the American Meteorological Society, January, 16-21,doi:10.1175/BAMS-D-13-00249.1
## Data and metadata available at http://sacad.database.bmkg.go.id/
##
## 01-05 STAID : Station identifier
## 07-46 STATIONNAME : Station Name
## 48-87 COUNTRYNAME : Country Name
## 89-97 LAT : Latitude in degrees:minutes:seconds (+:North, -:South)
## 99-109 LON : Longitude in degrees:minutes:seconds (+:East, -:West)
## 111-115 HGT : Station elevation in meters
file_contoh <- NULL
for (f in files_rr) {
id <- cari_staid(readLines(f, n = 30, warn = FALSE))
if (!is.na(id) && id %in% stations_id$STAID) { file_contoh <- f; break }
}
if (!is.null(file_contoh)) {
cat("\n=== Header salah satu file indexRR (stasiun Indonesia) ===\n")
cat(readLines(file_contoh, n = 30, warn = FALSE), sep = "\n")
}
##
## === Header salah satu file indexRR (stasiun Indonesia) ===
## SOUTHEAST ASIA CLIMATE ASSESSMENT & DATASET (SACA&D), file created on: 02-12-2025
## THESE DATA CAN BE USED FREELY PROVIDED THAT THE FOLLOWING SOURCE IS ACKNOWLEDGED:
##
## van den Besselaar and Coauthors, 2015.
## International Climate Assessment & Dataset (ICA&D): climate services across borders.
## Bulletin of the American Meteorological Society, January, 16-21,doi:10.1175/BAMS-D-13-00249.1
## Data and metadata available at http://sacad.database.bmkg.go.id/
##
## FILE FORMAT (MISSING VALUE CODE = -999999):
##
## THIS FILE HOLDS DATA FOR STATION INDONESIA STASIUN METEOROLOGI CUT BAU MAIMUN SALEH (STAID: 1)
## LATITUDE: dd:mm:ss +05:52:36
## LONGITUDE: ddd:mm:ss +095:20:16
## ELEVATION: m 126
##
## DATA IS FOR INDEX Precipitation sum (RR) with unit 0.01 Precipitation (mm)
##
## 01-06 SOUID: Source identifier
## 08-11 YEAR : YYYY
## 13-20 ANNUAL DATA VALUES
## 21-28 ONDJFM DATA VALUES
## 29-36 AMJJAS DATA VALUES
## 37-44 DJF DATA VALUES
## 45-52 MAM DATA VALUES
## 53-60 JJA DATA VALUES
## 61-68 SON DATA VALUES
## 69-76 JANUARY DATA VALUES
## etc.
## 157-164 DECEMBER DATA VALUES
| Keterangan | Isi |
|---|---|
| Variabel | Curah hujan tahunan (kolom ANNUAL); satuan asli 0,01 mm, dikonversi ke mm (dibagi 100) |
| Tipe data | Titik (stasiun pengamatan) |
| Tahun | 2024 |
| Cakupan | Indonesia |
| Sumber data | Southeast Asia Climate Assessment & Dataset (SACA&D), BMKG, https://sacad.bmkg.go.id/. Diakses: Rabu, 14 Oktober, 2026, 22:06 |
data_indonesia <- data_indonesia %>% left_join(stations_id, by = "STAID")
data_indonesia_2024 <- data_indonesia %>% filter(YEAR == 2024)
cat("Jumlah baris tahun 2024:", nrow(data_indonesia_2024), "\n")
## Jumlah baris tahun 2024: 7472
cat("Jumlah stasiun tahun 2024:", n_distinct(data_indonesia_2024$STAID), "\n")
## Jumlah stasiun tahun 2024: 7472
data_spasial <- data_indonesia_2024 %>%
select(STAID, YEAR, ANNUAL) %>%
left_join(
stations_id %>% select(STAID, STATIONNAME, COUNTRYNAME, LAT, LON, HGT),
by = "STAID"
)
cat("Jumlah baris data spasial:", nrow(data_spasial), "\n")
## Jumlah baris data spasial: 7472
Urutan pemeriksaan:
data hilang dan duplikasi
konversi koordinat
validasi koordinat terhadap wilayah Indonesia
pemeriksaan outlier nilai curah hujan. Hanya titik yang gagal validasi koordinat yang dikeluarkan, karena posisinya salah dan akan merusak interpolasi.
Outlier nilai tidak otomatis dibuang karena curah hujan tinggi di Indonesia bisa nyata.
cat("Jumlah NA setiap kolom:\n")
## Jumlah NA setiap kolom:
print(colSums(is.na(data_spasial)))
## STAID YEAR ANNUAL STATIONNAME COUNTRYNAME LAT
## 0 0 2864 0 0 0
## LON HGT
## 0 0
# Satuan asli indeks RR di berkas SACA&D adalah 0,01 mm -> bagi 100 agar menjadi mm
data_analisis <- data_spasial %>%
filter(!is.na(ANNUAL)) %>%
mutate(ANNUAL = ANNUAL / 100)
cat("\nBaris dengan ANNUAL kosong yang dibuang:",
nrow(data_spasial) - nrow(data_analisis), "\n")
##
## Baris dengan ANNUAL kosong yang dibuang: 2864
duplikasi_stasiun <- data_analisis %>% count(STAID) %>% filter(n > 1)
if (nrow(duplikasi_stasiun) == 0) {
cat("Tidak ada duplikasi STAID.\n")
} else {
cat("Ada STAID ganda; hanya baris pertama tiap stasiun dipertahankan:\n")
print(duplikasi_stasiun)
data_analisis <- data_analisis %>% group_by(STAID) %>% slice(1) %>% ungroup()
}
## Tidak ada duplikasi STAID.
String koordinat asli disimpan di LAT_raw dan
LON_raw agar kesalahan input di metadata bisa
ditelusuri.
data_analisis <- data_analisis %>%
mutate(
LAT_raw = LAT, LON_raw = LON,
LAT = dms_to_decimal(LAT),
LON = dms_to_decimal(LON)
)
cat("Koordinat gagal dikonversi (NA):",
sum(is.na(data_analisis$LAT) | is.na(data_analisis$LON)), "\n")
## Koordinat gagal dikonversi (NA): 0
data_analisis <- data_analisis %>% filter(!is.na(LAT), !is.na(LON))
summary(data_analisis[, c("LAT", "LON")])
## LAT LON
## Min. :-10.862 Min. : -0.6203
## 1st Qu.: -7.626 1st Qu.:106.4649
## Median : -6.523 Median :110.7644
## Mean : -4.570 Mean :110.9432
## 3rd Qu.: -1.976 3rd Qu.:114.2837
## Max. : 8.043 Max. :140.8508
Titik yang jatuh di luar Indonesia dicek dengan tiga cara, dari yang paling sederhana:
Jika koordinat yang benar sudah diverifikasi dari sumber yang bisa
dipertanggungjawabkan (mis. BMKG atau halaman stasiun di situs penyedia
data), isi tabel koreksi_manual di bawah. Titik tersebut
akan diperbaiki, bukan dibuang.
# Isi hanya jika koordinat benar sudah diverifikasi. Contoh format:
# koreksi_manual <- tibble(STAID = 00000L, LAT = <lintang desimal>, LON = <bujur desimal>)
koreksi_manual <- tibble(STAID = integer(), LAT = numeric(), LON = numeric())
if (nrow(koreksi_manual) > 0) {
data_analisis <- data_analisis %>%
left_join(koreksi_manual %>% rename(LAT_k = LAT, LON_k = LON), by = "STAID") %>%
mutate(LAT = coalesce(LAT_k, LAT), LON = coalesce(LON_k, LON)) %>%
select(-LAT_k, -LON_k)
}
Sebagian stasiun di Jawa tercatat bertanda lintang utara (+) di metadata sumber, padahal nama stasiun dan bujurnya menunjukkan lokasi di selatan khatulistiwa (mis. stasiun Yogyakarta tercatat +07:54, seharusnya -07:54). Aturan koreksi: jika lintang melebihi batas atas, kebalikan tandanya masuk rentang Indonesia, dan bujurnya wajar, maka tanda lintang dibalik. Daftar hasil koreksi ditampilkan agar bisa diverifikasi dengan sumber. Aturan ini hanya menangkap kasus yang lintangnya melewati batas atas; salah tanda di lintang yang kecil (mendekati khatulistiwa) hanya bisa dideteksi lewat pemeriksaan jarak ke poligon di bawah.
koreksi_tanda <- data_analisis %>%
filter(
LAT > bbox_lat[2], -LAT >= bbox_lat[1],
LON >= bbox_lon[1], LON <= bbox_lon[2],
!STAID %in% koreksi_manual$STAID
) %>%
transmute(STAID, STATIONNAME, LAT_raw, LON_raw,
LAT_sebelum = LAT, LAT_sesudah = -LAT, LON)
cat("Stasiun dengan tanda lintang dibalik:", nrow(koreksi_tanda), "\n")
## Stasiun dengan tanda lintang dibalik: 11
kable(as.data.frame(koreksi_tanda), digits = 3,
caption = "Stasiun yang tanda lintangnya dikoreksi")
| STAID | STATIONNAME | LAT_raw | LON_raw | LAT_sebelum | LAT_sesudah | LON |
|---|---|---|---|---|---|---|
| 9522 | MANGGIS | +06:46:22 | +106:48:48 | 6.773 | -6.773 | 106.813 |
| 9523 | CIKARANGNGLUWUNG_JPG KULON | +07:15:15 | +106:37:27 | 7.254 | -7.254 | 106.624 |
| 9538 | BONGAS_GEGERBITUNG | +06:59:24 | +107:01:41 | 6.990 | -6.990 | 107.028 |
| 11839 | PACIRAN | +06:52:36 | +112:23:26 | 6.877 | -6.877 | 112.391 |
| 14403 | DISTAN KOTA YOGYAKARTA | +07:54:00 | +110:27:41 | 7.900 | -7.900 | 110.461 |
| 14404 | KEPATIHAN YOGYAKARTA | +07:59:11 | +110:22:37 | 7.986 | -7.986 | 110.377 |
| 14405 | POLBANGTAN YOGYAKARTA | +07:53:30 | +110:36:50 | 7.892 | -7.892 | 110.614 |
| 14406 | STPN YOGYAKARTA | +08:02:34 | +110:30:30 | 8.043 | -8.043 | 110.508 |
| 14407 | KEBUN BENIH TEGALREJO | +07:57:24 | +110:27:06 | 7.957 | -7.957 | 110.452 |
| 14451 | CIUTARA | +06:47:24 | +106:46:48 | 6.790 | -6.790 | 106.780 |
| 14452 | CILETUH | +07:15:00 | +106:31:48 | 7.250 | -7.250 | 106.530 |
data_analisis <- data_analisis %>%
mutate(LAT = ifelse(STAID %in% koreksi_tanda$STAID, -LAT, LAT))
# Poligon batas Indonesia (opsional; perlu paket rnaturalearth + rnaturalearthdata)
indonesia_ll <- tryCatch(
rnaturalearth::ne_countries(scale = "medium", country = "Indonesia",
returnclass = "sf"),
error = function(e) NULL
)
data_analisis <- data_analisis %>%
mutate(
flag_bbox = LAT < bbox_lat[1] | LAT > bbox_lat[2] |
LON < bbox_lon[1] | LON > bbox_lon[2],
flag_lat_sama_lon = abs(LAT - LON) < 1e-8
)
# Poligon diproyeksikan ke km dan DISEDERHANAKAN (toleransi 5 km) satu kali di sini.
# Poligon batas resolusi tinggi (mis. GADM/shp) berisi jutaan titik dan membuat
# proses spasial berjalan sangat lama; 5 km cukup untuk skala nasional.
indonesia_km <- NULL
if (!is.null(indonesia_ll)) {
indonesia_km <- indonesia_ll %>%
st_make_valid() %>%
st_union() %>%
st_transform(crs_km) %>%
st_simplify(preserveTopology = TRUE, dTolerance = 5)
}
if (!is.null(indonesia_km)) {
pts_cek <- st_as_sf(data_analisis, coords = c("LON", "LAT"),
crs = 4326, remove = FALSE) %>%
st_transform(crs_km)
dekat <- st_is_within_distance(
pts_cek, indonesia_km, dist = 50, sparse = FALSE # 50 km (satuan CRS = km)
)[, 1]
data_analisis$flag_poligon <- !dekat
} else {
data_analisis$flag_poligon <- NA
message("Poligon Indonesia tidak tersedia; pemeriksaan jarak ke poligon dilewati.")
}
kable(
data_analisis %>%
summarise(
`Di luar bounding box` = sum(flag_bbox),
`Lintang = bujur` = sum(flag_lat_sama_lon),
`>50 km dari poligon` = sum(flag_poligon, na.rm = TRUE)
),
caption = "Jumlah stasiun per jenis pemeriksaan"
)
| Di luar bounding box | Lintang = bujur | >50 km dari poligon |
|---|---|---|
| 1 | 1 | 8 |
Pemeriksaan bounding box hanya menangkap salah tanda lintang yang jatuh di luar -11 sampai 6. Salah tanda pada lintang kecil (mis. stasiun di Bengkulu tercatat +03:18 padahal seharusnya -03:18) tetap berada di dalam bounding box, tetapi terdeteksi oleh pemeriksaan jarak ke poligon karena jatuh di luar daratan Indonesia (mis. di Semenanjung Malaysia). Stasiun yang gagal pemeriksaan poligon dikoreksi jika lintang dengan tanda dibalik jatuh dalam 25 km dari daratan Indonesia. Hasil koreksi ditampilkan agar bisa diverifikasi; stasiun yang tetap gagal setelah koreksi dikeluarkan dari analisis.
koreksi_tanda2 <- data_analisis[0, ]
if (!is.null(indonesia_km)) {
kandidat <- data_analisis %>%
filter(flag_poligon %in% TRUE, !flag_bbox, !flag_lat_sama_lon)
if (nrow(kandidat) > 0) {
pts_flip <- kandidat %>%
mutate(LAT_flip = -LAT) %>%
st_as_sf(coords = c("LON", "LAT_flip"), crs = 4326, remove = FALSE) %>%
st_transform(crs_km)
flip_ok <- st_is_within_distance(
pts_flip, indonesia_km, dist = 25, sparse = FALSE
)[, 1]
koreksi_tanda2 <- kandidat[flip_ok, ]
}
}
cat("Stasiun dikoreksi (tanda lintang dibalik, hasil di dekat daratan):",
nrow(koreksi_tanda2), "\n")
## Stasiun dikoreksi (tanda lintang dibalik, hasil di dekat daratan): 7
data_analisis <- data_analisis %>%
mutate(
LAT = ifelse(STAID %in% koreksi_tanda2$STAID, -LAT, LAT),
flag_poligon = ifelse(STAID %in% koreksi_tanda2$STAID, FALSE, flag_poligon)
)
kable(
koreksi_tanda2 %>%
transmute(STAID, STATIONNAME, LAT_raw, LON_raw,
LAT_sebelum = LAT, LAT_sesudah = -LAT, LON),
digits = 3,
caption = "Stasiun yang tanda lintangnya dikoreksi berdasarkan pemeriksaan poligon"
)
| STAID | STATIONNAME | LAT_raw | LON_raw | LAT_sebelum | LAT_sesudah | LON |
|---|---|---|---|---|---|---|
| 14015 | KARANG CAYO | +04:16:36 | +102:53:12 | 4.277 | -4.277 | 102.887 |
| 14020 | KERKAP | +03:18:51 | +102:09:59 | 3.314 | -3.314 | 102.166 |
| 14022 | NAPAL JUNGUR | +03:57:36 | +102:30:18 | 3.960 | -3.960 | 102.505 |
| 14023 | SUKARAMI | +04:07:09 | +102:31:24 | 4.119 | -4.119 | 102.523 |
| 14025 | RENA JAYA | +03:07:18 | +101:35:43 | 3.122 | -3.122 | 101.595 |
| 14253 | AIR PADANG | +03:16:12 | +102:30:00 | 3.270 | -3.270 | 102.500 |
| 14254 | PINANG RAYA | +03:11:24 | +101:31:48 | 3.190 | -3.190 | 101.530 |
Daftar stasiun yang masih bermasalah setelah koreksi. Kolom
LAT_raw dan LON_raw menunjukkan teks asli di
metadata, sehingga terlihat apakah masalahnya ada di file sumber atau di
proses konversi.
bermasalah <- data_analisis %>%
filter(flag_bbox | flag_lat_sama_lon | flag_poligon %in% TRUE) %>%
select(STAID, STATIONNAME, LAT_raw, LON_raw, LAT, LON, HGT, ANNUAL,
flag_bbox, flag_lat_sama_lon, flag_poligon)
kable(as.data.frame(bermasalah), digits = 3,
caption = "Stasiun yang gagal salah satu pemeriksaan koordinat")
| STAID | STATIONNAME | LAT_raw | LON_raw | LAT | LON | HGT | ANNUAL | flag_bbox | flag_lat_sama_lon | flag_poligon |
|---|---|---|---|---|---|---|---|---|---|---|
| 12744 | BERIWIT | -00:37:13 | -000:37:13 | -0.62 | -0.62 | 0 | 4106 | TRUE | TRUE | TRUE |
ggplot(data_analisis, aes(LON, LAT)) +
geom_point(color = "steelblue", size = 2) +
geom_point(
data = filter(data_analisis, flag_bbox | flag_lat_sama_lon),
color = "red", size = 3
) +
labs(title = "Pemeriksaan Koordinat Stasiun", x = "Bujur", y = "Lintang") +
theme_minimal()
Titik merah: stasiun yang gagal pemeriksaan bounding box atau lintang = bujur
Metadata mentah untuk stasiun yang gagal bounding box, untuk dicocokkan dengan sumber aslinya:
id_bermasalah <- data_analisis %>%
filter(flag_bbox | flag_lat_sama_lon) %>%
pull(STAID)
kable(
stations %>%
filter(as.integer(STAID) %in% id_bermasalah) %>%
select(STAID, STATIONNAME, COUNTRYNAME, LAT, LON, HGT),
caption = "Metadata asli stasiun yang gagal validasi koordinat"
)
| STAID | STATIONNAME | COUNTRYNAME | LAT | LON | HGT |
|---|---|---|---|---|---|
| 12744 | BERIWIT | INDONESIA | -00:37:13 | -000:37:13 | 0 |
Titik yang gagal bounding box, berciri lintang = bujur, atau masih berjarak lebih dari 50 km dari daratan Indonesia setelah koreksi tanda dikeluarkan dari analisis spasial dan dicatat sebagai temuan kualitas data. Setelah itu, stasiun dengan koordinat kembar dikurangi menjadi satu titik.
data_final <- data_analisis %>%
filter(!(flag_bbox | flag_lat_sama_lon | flag_poligon %in% TRUE)) %>%
mutate(HGT = suppressWarnings(as.numeric(HGT))) %>%
select(STAID, STATIONNAME, LAT, LON, HGT, ANNUAL)
n_awal_ann <- nrow(data_analisis)
n_gagal_koord <- n_awal_ann - nrow(data_final)
# Koordinat kembar (dalam proyeksi km) membuat matriks kriging singular:
# pertahankan satu titik untuk setiap koordinat
xy_cek <- st_coordinates(
st_transform(st_as_sf(data_final, coords = c("LON", "LAT"), crs = 4326), crs_km)
)
dup_xy <- duplicated(round(xy_cek, 3))
n_dup <- sum(dup_xy)
data_final <- data_final[!dup_xy, ]
cat("Stasiun sebelum validasi koordinat :", n_awal_ann, "\n")
## Stasiun sebelum validasi koordinat : 4608
cat("Dikeluarkan (gagal validasi koordinat):", n_gagal_koord, "\n")
## Dikeluarkan (gagal validasi koordinat): 1
cat("Dikeluarkan (koordinat kembar) :", n_dup, "\n")
## Dikeluarkan (koordinat kembar) : 44
cat("Stasiun dipakai untuk analisis :", nrow(data_final), "\n")
## Stasiun dipakai untuk analisis : 4563
Ringkasan hasil kontrol kualitas: dari 4.608 stasiun
yang memiliki nilai ANNUAL, sebanyak 18 stasiun dikoreksi
tanda lintangnya, 1 stasiun dikeluarkan karena koordinat tidak valid,
dan 44 stasiun dikeluarkan karena berkoordinat kembar, sehingga
4.563 stasiun dipakai dalam seluruh analisis
berikutnya.
Q1 <- quantile(data_final$ANNUAL, 0.25)
Q3 <- quantile(data_final$ANNUAL, 0.75)
IQR_value <- IQR(data_final$ANNUAL)
batas_bawah <- Q1 - 1.5 * IQR_value
batas_atas <- Q3 + 1.5 * IQR_value
cat("Batas bawah:", round(batas_bawah, 1), "\n")
## Batas bawah: 27
cat("Batas atas:", round(batas_atas, 1), "\n")
## Batas atas: 4917.6
outlier_hujan <- data_final %>%
filter(ANNUAL < batas_bawah | ANNUAL > batas_atas) %>%
arrange(desc(ANNUAL))
cat("Jumlah outlier nilai:", nrow(outlier_hujan), "\n")
## Jumlah outlier nilai: 127
kable(as.data.frame(outlier_hujan), digits = 2,
caption = "Stasiun dengan nilai ANNUAL di luar batas IQR")
| STAID | STATIONNAME | LAT | LON | HGT | ANNUAL |
|---|---|---|---|---|---|
| 9502 | BATU KARUT | -6.99 | 106.68 | 0 | 10300.5 |
| 9504 | CIKELAT | -7.38 | 106.46 | 61 | 9803.5 |
| 10023 | REMBANG/LOSARI | -7.30 | 109.52 | 17 | 9685.0 |
| 8302 | BEUTONG | 4.26 | 96.42 | 2 | 9389.0 |
| 8201 | SUNGAI MAS | 4.61 | 96.20 | 0 | 7900.0 |
| 13234 | GIMPU | -1.62 | 120.04 | 44 | 7789.0 |
| 14319 | SASAK | 0.01 | 99.70 | 0 | 7784.0 |
| 14417 | TAMPINGAN | -7.10 | 110.29 | 0 | 7193.4 |
| 10688 | LEBAKBARANG | -7.13 | 109.66 | 65 | 7180.0 |
| 14229 | IWAKA | -4.45 | 136.63 | 2 | 7106.0 |
| 12567 | TOHO | 0.43 | 109.27 | 2 | 7096.0 |
| 13052 | BP3K SINONSAYANG | 1.12 | 124.34 | 6 | 6998.2 |
| 14298 | PENGKADAN | 0.49 | 112.35 | 4 | 6850.0 |
| 92 | STASIUN METEOROLOGI MOZEZ KILANGIN | -4.53 | 136.89 | 30 | 6846.7 |
| 14505 | CIAUL | -6.92 | 106.94 | 66 | 6782.0 |
| 10724 | BELIK | -7.19 | 109.33 | 0 | 6737.0 |
| 10729 | BONGAS | -7.19 | 109.49 | 49 | 6598.0 |
| 8723 | PATAMUAN TANDIKAT | 0.50 | 100.25 | 21 | 6594.4 |
| 10728 | SIPEDANG | -7.14 | 109.49 | 47 | 6439.0 |
| 14421 | KETENGER | -7.32 | 109.23 | 0 | 6418.0 |
| 12726 | HANAU | -2.54 | 112.10 | 0 | 6306.0 |
| 10727 | WATUKUMPUL 1 | -7.16 | 109.42 | 55 | 6284.0 |
| 9497 | GUNUNG BUNDER | -6.65 | 106.70 | 0 | 6170.0 |
| 14318 | SUKAMENANTI | 0.10 | 99.81 | 19 | 6127.5 |
| 8370 | HAPESONG | 1.47 | 99.07 | 0 | 6097.0 |
| 8715 | SEI GERINGGING | -0.42 | 100.09 | 23 | 6069.5 |
| 9613 | RANCABALI | -7.16 | 107.36 | 164 | 6065.0 |
| 14321 | LUNANG | -2.00 | 100.98 | 1 | 5973.6 |
| 10135 | TANJUNGSARI | -7.45 | 109.98 | 71 | 5938.0 |
| 10154 | TUK MUDAL | -7.33 | 109.93 | 86 | 5914.0 |
| 9608 | RANCASUNI | -6.94 | 107.74 | 72 | 5901.5 |
| 9257 | BATU ROTO | -3.47 | 102.32 | 48 | 5876.5 |
| 8763 | PECONINA | -1.59 | 101.14 | 94 | 5857.5 |
| 14056 | BP3K TANA LILI | -2.51 | 120.60 | 0 | 5852.0 |
| 12736 | TEWAH | -1.08 | 113.70 | 7 | 5848.4 |
| 10730 | WATUKUMPUL BPP | -7.16 | 109.42 | 0 | 5836.0 |
| 14301 | RUMAH KAY | -3.45 | 128.53 | 0 | 5799.0 |
| 14 | STASIUN METEOROLOGI MINANGKABAU | -0.79 | 100.29 | 6 | 5776.1 |
| 9459 | PAMIJAHAN | -6.62 | 106.64 | 31 | 5760.0 |
| 8721 | KANDANG AMPEK | 0.50 | 100.37 | 39 | 5742.1 |
| 8337 | SIMPANG KIRI | 2.64 | 97.98 | 0 | 5714.5 |
| 11199 | JERUK/GUNUNG RINGGIT | -7.99 | 113.17 | 69 | 5683.8 |
| 8766 | UPTB TALAMAU | 0.23 | 100.97 | 0 | 5673.0 |
| 13456 | SINGKALA / LAMASI TIMUR | -3.35 | 120.16 | 0 | 5653.0 |
| 8724 | TIKU | 0.37 | 100.91 | 1 | 5644.6 |
| 13478 | STAMET MASAMBA | -2.55 | 120.32 | 5 | 5621.0 |
| 12033 | BANJAR IRIGASI | -6.57 | 106.41 | 21 | 5600.5 |
| 57 | STASIUN METEOROLOGI ANDI JEMMA | -2.55 | 120.32 | 43 | 5596.3 |
| 8340 | SULTAN DAULAT | 2.85 | 97.88 | 0 | 5595.0 |
| 14233 | SILAT HULU | 0.25 | 111.99 | 4 | 5585.0 |
| 10807 | PENJALIN | -7.33 | 109.06 | 34 | 5578.0 |
| 9291 | PINANG PELAPIS | -3.00 | 102.16 | 67 | 5569.5 |
| 9133 | KIKIM SELATAN | -3.73 | 103.24 | 0 | 5561.5 |
| 13038 | BP3K TAMAKO | 3.44 | 125.56 | 2 | 5546.1 |
| 14306 | LEKSULA | -3.76 | 126.52 | 0 | 5539.2 |
| 9625 | SINGAJAYA | -7.48 | 107.92 | 59 | 5483.5 |
| 14144 | SEUNAGAN TIMUR | 4.27 | 96.35 | 0 | 5482.5 |
| 14256 | PAGAR JATI | -3.60 | 102.36 | 8 | 5416.0 |
| 10079 | SOMAGEDE | -7.53 | 109.56 | 11 | 5412.0 |
| 8722 | SICAUNG | -0.55 | 100.29 | 11 | 5410.4 |
| 9228 | ULU MANNA | -4.27 | 102.98 | 17 | 5388.5 |
| 9244 | KEMUMU | -3.43 | 102.27 | 38 | 5377.0 |
| 12356 | EBAN | -9.55 | 124.31 | 113 | 5359.0 |
| 12618 | NANGA TEPUAI | 0.38 | 112.29 | 4 | 5341.5 |
| 12616 | NANGA SURUK | 0.50 | 112.64 | 6 | 5340.0 |
| 8732 | BPP BAWAN | -0.19 | 99.96 | 2 | 5334.5 |
| 14424 | SINGASARI UPBAT / BBI | -7.39 | 109.18 | 0 | 5334.0 |
| 10719 | SEMUGIH AFD/KT | -7.14 | 109.25 | 0 | 5324.0 |
| 14411 | WATERPLAN SEMEN PADANG | -0.96 | 100.48 | 0 | 5322.6 |
| 13042 | BP3K KENDAHE | 3.69 | 125.40 | 11 | 5294.6 |
| 8717 | PADANG LARING | 0.33 | 100.09 | 32 | 5292.0 |
| 9538 | BONGAS_GEGERBITUNG | -6.99 | 107.03 | 72 | 5291.0 |
| 10146 | BEDAKAH | -7.35 | 109.97 | 122 | 5288.0 |
| 13314 | PASIR PUTIH | -5.32 | 120.07 | 74 | 5285.0 |
| 10767 | BUMIJAWA | -7.17 | 109.13 | 0 | 5277.0 |
| 13370 | TOMPO LEMO - LEMO ( SDN.MENRONG ) | -4.51 | 119.77 | 55 | 5271.0 |
| 8716 | BPP PILUBANG | -0.54 | 100.10 | 1 | 5235.3 |
| 11991 | PULOSARI | -6.33 | 105.92 | 0 | 5231.0 |
| 10768 | BOJONG | -7.14 | 109.15 | 0 | 5226.0 |
| 8963 | GUNUNG KERINCI | -1.89 | 101.58 | 101 | 5225.7 |
| 9529 | CIGANGSA_SURADE | -7.33 | 106.55 | 14 | 5225.5 |
| 8718 | ULAKAN TAPAKIS | -0.69 | 100.20 | 1 | 5224.0 |
| 13173 | LALUNDU | -1.29 | 119.51 | 0 | 5219.5 |
| 8350 | HUTABALANG | 1.59 | 98.87 | 0 | 5215.7 |
| 9277 | MEDAN JAYA | -2.99 | 101.49 | 2 | 5209.0 |
| 8933 | KUANTAN MUDIK BPP | -0.63 | 101.42 | 7 | 5203.5 |
| 8767 | KINALI | 0.07 | 100.91 | 4 | 5202.5 |
| 9631 | PAPANDAYAN | -7.37 | 107.71 | 0 | 5199.5 |
| 14025 | RENA JAYA | -3.12 | 101.60 | 1000 | 5197.5 |
| 9458 | PERKEB. CIANTEN | -6.72 | 106.60 | 95 | 5196.5 |
| 8346 | MANDUAMAS | 2.22 | 98.25 | 0 | 5185.0 |
| 8720 | LIMAU PURUT | 0.58 | 100.13 | 0 | 5176.4 |
| 10522 | PANGKALAN | -6.96 | 110.68 | 0 | 5156.0 |
| 9285 | RIMBO PENGADANG | -3.34 | 102.44 | 94 | 5140.0 |
| 13284 | LAKATONG / CIKOANG | -5.54 | 119.45 | 1 | 5133.0 |
| 8162 | LABUHAN HAJI BARAT | 3.54 | 97.01 | 0 | 5123.0 |
| 9853 | WANAYASA | -6.68 | 107.55 | 66 | 5122.0 |
| 12613 | JONGKONG | 0.66 | 112.28 | 3 | 5119.0 |
| 10674 | BLADO SP | -7.07 | 109.83 | 53 | 5081.5 |
| 10769 | KEMARON | -7.18 | 109.16 | 0 | 5078.0 |
| 8750 | SIMPATI | -0.02 | 100.17 | 30 | 5052.9 |
| 14459 | CIJAMPANG_PSDA | -7.13 | 107.15 | 64 | 5047.2 |
| 10156 | WANGAN AJI | -7.30 | 109.92 | 97 | 5044.0 |
| 10741 | SEMANGKIR AFD | -7.10 | 109.35 | 0 | 5040.0 |
| 14448 | PERKEBUNAN CIGARU | -7.10 | 106.60 | 73 | 5024.5 |
| 12607 | NANGA DEDAI | -0.05 | 111.57 | 3 | 5022.0 |
| 8758 | LUBUK GADANG SELATAN | 0.55 | 100.25 | 57 | 5001.5 |
| 13261 | BORONG RAPOA | -5.39 | 120.04 | 71 | 4990.0 |
| 9333 | TANJUNG BINGA | -2.60 | 107.64 | 1 | 4985.4 |
| 11988 | BD.BUNUT | -6.27 | 106.11 | 0 | 4983.3 |
| 12030 | BPP CIBEBER | -6.83 | 106.33 | 63 | 4979.5 |
| 8742 | MUARA PAITI | 0.32 | 100.39 | 13 | 4977.0 |
| 10153 | MOJOTENGAH | -7.33 | 109.89 | 0 | 4969.0 |
| 8550 | MUARA SOMA | 0.69 | 99.36 | 0 | 4957.6 |
| 12608 | NANGA MAU | 0.06 | 112.49 | 4 | 4954.0 |
| 12690 | BAAMANG TENGAH | -2.53 | 112.89 | 0 | 4952.6 |
| 9536 | PERBAWATI | -6.51 | 106.57 | 112 | 4950.0 |
| 14142 | KEB.SEUMANYAM DARUL MAKMUR | 3.97 | 96.56 | 3 | 4949.8 |
| 12620 | SEMITAU | 0.50 | 112.00 | 3 | 4948.0 |
| 10132 | KEPIL | -7.52 | 110.00 | 54 | 4939.0 |
| 14255 | MANNA | -4.44 | 102.90 | 4 | 4936.0 |
| 9854 | CIRACAS | -6.65 | 107.59 | 55 | 4935.5 |
| 8746 | LUBUK SIKAPING | 0.14 | 100.17 | 46 | 4934.4 |
| 10044 | PENUSUPAN / PEJAWARAN | -7.25 | 109.80 | 0 | 4926.0 |
| 12622 | LANJAK | 0.04 | 112.23 | 4 | 4926.0 |
| 13297 | EX SMPK BB. MALINO/BPP. TINGGI MONCONG | -5.25 | 119.85 | 102 | 4925.0 |
| 14018 | WARA BARAT | -2.96 | 120.10 | 2 | 4918.0 |
Outlier nilai dipertahankan. Curah hujan Indonesia memang sangat bervariasi menurut topografi dan letak, sehingga nilai ekstrem belum tentu salah. Stasiun hanya dibuang jika terbukti salah ukur atau salah input.
write.csv(data_final, "data_curah_hujan_2024_bersih.csv", row.names = FALSE)
cat("Tersimpan: data_curah_hujan_2024_bersih.csv (", nrow(data_final), "stasiun )\n")
## Tersimpan: data_curah_hujan_2024_bersih.csv ( 4563 stasiun )
x <- data_final$ANNUAL
n_st <- length(x)
rata <- mean(x)
med <- median(x)
sdv <- sd(x)
cv <- 100 * sdv / rata
skew <- mean((x - rata)^3) / (mean((x - rata)^2))^(3 / 2)
statistik_deskriptif <- data.frame(
Statistik = c("Jumlah stasiun", "Rata-rata", "Median", "Simpangan baku",
"Koefisien variasi (%)", "Minimum", "Kuartil 1", "Kuartil 3",
"Maksimum", "Skewness"),
Nilai = c(n_st, rata, med, sdv, cv, min(x),
unname(quantile(x, 0.25)), unname(quantile(x, 0.75)),
max(x), skew)
)
kable(statistik_deskriptif, digits = 2,
caption = "Statistik deskriptif curah hujan tahunan 2024")
| Statistik | Nilai |
|---|---|
| Jumlah stasiun | 4563.00 |
| Rata-rata | 2556.18 |
| Median | 2380.50 |
| Simpangan baku | 1010.77 |
| Koefisien variasi (%) | 39.54 |
| Minimum | 413.00 |
| Kuartil 1 | 1861.00 |
| Kuartil 3 | 3083.65 |
| Maksimum | 10300.50 |
| Skewness | 1.29 |
stasiun_max <- data_final$STATIONNAME[which.max(x)]
stasiun_min <- data_final$STATIONNAME[which.min(x)]
hgt_max <- data_final$HGT[which.max(x)]
hgt_min <- data_final$HGT[which.min(x)]
q1 <- unname(quantile(x, 0.25)); q3 <- unname(quantile(x, 0.75))
if (n_st >= 3 && n_st <= 5000) {
uji_normal <- shapiro.test(x)
print(uji_normal)
}
##
## Shapiro-Wilk normality test
##
## data: x
## W = 0.93068, p-value < 2.2e-16
ggplot(data_final, aes(ANNUAL)) +
geom_histogram(bins = 20, fill = "lightblue", color = "white") +
geom_vline(xintercept = rata, color = "red", linetype = "dashed") +
geom_vline(xintercept = med, color = "darkgreen", linetype = "dashed") +
labs(title = "Distribusi Curah Hujan Tahunan 2024",
x = "Nilai ANNUAL", y = "Frekuensi") +
theme_minimal()
Gambar 3.1. Histogram curah hujan tahunan 2024 (garis putus-putus: rata-rata dan median)
ggplot(data_final, aes(x = "", y = ANNUAL)) +
geom_boxplot(fill = "lightgreen") +
labs(title = "Boxplot Curah Hujan Tahunan 2024", x = NULL, y = "Nilai ANNUAL") +
theme_minimal()
Gambar 3.2. Boxplot curah hujan tahunan 2024
Data yang dianalisis adalah curah hujan tahunan (annual precipitation) tahun 2024 dari 4.563 stasiun pengamatan di Indonesia setelah pembersihan data, koreksi dan validasi koordinat, serta penghapusan 44 titik berkoordinat kembar.
Rata-rata curah hujan tahunan sebesar 2.556,2 mm dengan simpangan baku 1.010,8 mm, atau koefisien variasi 39,5%, yang menunjukkan keragaman antarlokasi yang cukup besar. Rata-rata lebih tinggi daripada median (2.380,5 mm) dan skewness bernilai 1,29, sehingga distribusi menjulur ke kanan: sebagian besar stasiun berada pada rentang curah hujan menengah, sedangkan sejumlah kecil stasiun mencatat nilai yang jauh lebih tinggi. Uji Shapiro-Wilk menolak normalitas (W = 0,931; p < 2,2 × 10-16). Pada ukuran sampel sebesar ini uji tersebut sangat sensitif sehingga hasilnya dibaca bersama histogram dan boxplot.
Berdasarkan batas pagar IQR (27,0 sampai 4.917,6 mm) terdapat 127 stasiun bernilai ekstrem. Stasiun tersebut tidak dibuang karena curah hujan tinggi di Indonesia dapat bersifat nyata, terutama di daerah pegunungan. Nilai tertinggi, 10.300,5 mm, tercatat di stasiun BATU KARUT (ketinggian tercatat 0 m), sedangkan nilai terendah, 413,0 mm, tercatat di stasiun SUMBERWARU/KALOR KORAN (ketinggian tercatat 3 m). Nilai-nilai ekstrem ini perlu diverifikasi terhadap sumber atau stasiun di sekitarnya karena dapat memengaruhi simpangan baku dan skewness; stasiun hanya dikeluarkan jika terbukti salah ukur atau salah input.
Keragaman yang besar antarstasiun menjadi alasan dilakukannya analisis spasial: statistik deskriptif belum menjelaskan bagaimana curah hujan tersebar secara geografis, sehingga analisis dilanjutkan dengan pemetaan dan interpolasi.
pts_ll <- st_as_sf(data_final, coords = c("LON", "LAT"), crs = 4326, remove = FALSE)
pts_km <- st_transform(pts_ll, crs_km)
xy <- st_coordinates(pts_km)
pts_km$X <- xy[, 1]
pts_km$Y <- xy[, 2]
# Koordinat kembar sudah dikeluarkan pada chunk 'keluarkan'; pastikan tidak tersisa
stopifnot(!any(duplicated(round(st_coordinates(pts_km), 3))))
# Batas wilayah: poligon Indonesia jika tersedia, jika tidak convex hull stasiun
if (!is.null(indonesia_km)) {
poly_km <- indonesia_km # sudah disederhanakan pada chunk validasi-koordinat
} else {
# Cadangan: gabungan lingkaran 75 km di sekitar stasiun. Lebih baik daripada
# convex hull yang menutupi seluruh laut di antara pulau. Disarankan memasang
# rnaturalearth + rnaturalearthdata (atau memakai shp) agar batasnya akurat.
message("Poligon Indonesia tidak tersedia; memakai buffer 75 km di sekitar stasiun.")
poly_km <- pts_km %>% st_geometry() %>% st_buffer(75) %>% st_union()
}
n_stasiun <- nrow(pts_km)
cat("Jumlah stasiun untuk visualisasi dan interpolasi:", n_stasiun, "\n")
## Jumlah stasiun untuk visualisasi dan interpolasi: 4563
ggplot() +
geom_sf(data = st_sf(geometry = poly_km), fill = "grey95", color = "grey60",
linewidth = 0.2) +
geom_sf(data = pts_km, aes(color = ANNUAL), size = 2, alpha = 0.9) +
scale_color_viridis_c(name = "ANNUAL") +
coord_sf(datum = st_crs(4326)) +
labs(title = "Sebaran Curah Hujan Tahunan Indonesia 2024") +
theme_minimal()
Gambar 3.3. Peta sebaran curah hujan tahunan stasiun Indonesia 2024
pts_km$kelas <- cut(
pts_km$ANNUAL,
breaks = unique(quantile(pts_km$ANNUAL, probs = seq(0, 1, 0.2))),
include.lowest = TRUE, dig.lab = 5
)
ggplot() +
geom_sf(data = st_sf(geometry = poly_km), fill = "grey95", color = "grey60",
linewidth = 0.2) +
geom_sf(data = pts_km, aes(color = kelas), size = 2) +
scale_color_viridis_d(name = "Kelas ANNUAL\n(kuantil)") +
coord_sf(datum = st_crs(4326)) +
labs(title = "Kelas Curah Hujan Tahunan Stasiun") +
theme_minimal()
Gambar 3.4. Peta sebaran stasiun menurut kelas kuantil curah hujan
model_tren <- lm(ANNUAL ~ X + Y, data = st_drop_geometry(pts_km))
kable(summary(model_tren)$coefficients, digits = 4,
caption = "Regresi tren ANNUAL terhadap koordinat (X, Y dalam km)")
| Estimate | Std. Error | t value | Pr(>|t|) | |
|---|---|---|---|---|
| (Intercept) | 2661.0190 | 24.7957 | 107.3177 | 0.000 |
| X | -0.0388 | 0.0183 | -2.1212 | 0.034 |
| Y | 0.4595 | 0.0363 | 12.6519 | 0.000 |
r2_tren <- summary(model_tren)$r.squared
cat("R-kuadrat model tren:", round(r2_tren, 3), "\n")
## R-kuadrat model tren: 0.041
cor_hgt <- suppressWarnings(
cor(pts_km$ANNUAL, ifelse(pts_km$HGT > 0, pts_km$HGT, NA_real_),
use = "complete.obs")
)
if (length(cor_hgt) == 0 || is.na(cor_hgt)) cor_hgt <- NA_real_
prop_hgt0 <- mean(pts_km$HGT == 0, na.rm = TRUE)
cat("Proporsi stasiun dengan HGT = 0 (dianggap data hilang):", round(prop_hgt0, 3), "\n")
## Proporsi stasiun dengan HGT = 0 (dianggap data hilang): 0.285
t_tren <- summary(model_tren)$coefficients[, "t value"]
cat("Korelasi ANNUAL dengan ketinggian (hanya HGT > 0):", round(cor_hgt, 3), "\n")
## Korelasi ANNUAL dengan ketinggian (hanya HGT > 0): 0.17
Gambar di atas menampilkan lokasi stasiun yang diwarnai berdasarkan curah hujan tahunan. Stasiun sangat rapat di Pulau Jawa dan cukup rapat di Sumatra, sedangkan di Kalimantan bagian utara, Sulawesi bagian timur, Maluku, dan Papua stasiun jauh lebih jarang. Pola persebaran yang tidak merata ini penting karena ketelitian interpolasi pada suatu lokasi bergantung pada ketersediaan pengamatan di sekitarnya: estimasi di Jawa disusun dari banyak informasi lokal, sedangkan estimasi di Papua bertumpu pada sedikit stasiun yang berjauhan.
Secara visual, nilai curah hujan tinggi (hijau–kuning) tampak di sepanjang sisi barat Sumatra serta di beberapa titik di Kalimantan, Sulawesi, dan Papua bagian selatan-tengah, sedangkan nilai rendah (ungu) lebih banyak terlihat di sebagian Jawa bagian timur dan Nusa Tenggara. Pola yang beragam secara regional ini sejalan dengan temuan bahwa Indonesia terbagi atas beberapa wilayah curah hujan dengan pola musiman yang berbeda, yang dipengaruhi monsun dan suhu permukaan laut (Aldrian & Susanto, 2003). Penelitian ini tidak menguji penyebab klimatologis pola tersebut sehingga interpretasi bersifat deskriptif.
Regresi tren memakai ANNUAL (mm) sebagai variabel
respons dan koordinat stasiun X (timur-barat) dan Y
(utara-selatan), hasil proyeksi dalam kilometer, sebagai variabel
penjelas. Model ini menghasilkan R2 = 0,041, artinya
tren berskala luas yang searah lemah dan hanya menjelaskan sekitar 4%
keragaman data. Statistik t koefisien X dan Y
masing-masing -2,12 dan 12,65; koefisien dapat signifikan secara
statistik pada ribuan titik, tetapi kontribusinya terhadap keragaman
tetap kecil. Korelasi curah hujan dengan ketinggian stasiun juga rendah
(r = 0,17), dihitung hanya pada stasiun berketinggian lebih
dari nol karena 28,5% stasiun tercatat berketinggian 0 m dan
diperlakukan sebagai data hilang. Temuan ini penting untuk membaca hasil
Universal Kriging pada bagian evaluasi.
Seluruh perhitungan memakai proyeksi Lambert Azimuthal Equal Area dalam satuan kilometer agar jarak antartitik valid di seluruh Indonesia. Prediksi dibuat pada grid 25 km yang dipotong mengikuti batas wilayah.
grid_pts <- st_make_grid(poly_km, cellsize = ukuran_sel_km, what = "centers")
grid_km <- st_sf(geometry = grid_pts)
grid_km <- grid_km[lengths(st_intersects(grid_km, poly_km)) > 0, ]
g_xy <- st_coordinates(grid_km)
grid_km$X <- g_xy[, 1]
grid_km$Y <- g_xy[, 2]
cat("Jumlah sel grid prediksi:", nrow(grid_km), "\n")
## Jumlah sel grid prediksi: 2963
hitung_metrik <- function(obs, pred) {
c(ME = mean(pred - obs),
MAE = mean(abs(pred - obs)),
RMSE = sqrt(mean((pred - obs)^2)),
R2 = cor(obs, pred)^2)
}
# Pilih model variogram (Sph/Exp/Gau) dengan SSErr terkecil
pilih_model <- function(v) {
kandidat <- c("Sph", "Exp", "Gau")
hasil <- lapply(kandidat, function(m) {
awal <- vgm(psill = max(v$gamma) * 0.8, model = m,
range = max(v$dist) / 3, nugget = min(v$gamma))
suppressWarnings(fit.variogram(v, awal))
})
sse <- sapply(hasil, function(f) attr(f, "SSErr"))
singular <- sapply(hasil, function(f) isTRUE(attr(f, "singular")))
sse[singular] <- Inf
list(model = hasil[[which.min(sse)]],
tabel = data.frame(Model = kandidat, SSErr = sse))
}
ambil_df <- function(sfobj, nama) {
xy <- st_coordinates(sfobj)
data.frame(X = xy[, 1], Y = xy[, 2],
pred = sfobj$var1.pred, var = sfobj$var1.var, metode = nama)
}
IDW menduga nilai di suatu lokasi sebagai rata-rata tertimbang nilai stasiun, dengan bobot \(1/d^p\). Pangkat \(p\) dipilih dari nilai yang memberi RMSE LOOCV terkecil.
D <- as.matrix(dist(st_coordinates(pts_km)))
z <- pts_km$ANNUAL
# Prediksi LOOCV untuk IDW: tiap stasiun diduga dari stasiun lain
idw_loocv_pred <- function(p) {
W <- 1 / D^p
diag(W) <- 0
as.numeric(W %*% z) / rowSums(W)
}
tabel_idw <- do.call(rbind, lapply(1:6, function(p) {
data.frame(idp = p, t(hitung_metrik(z, idw_loocv_pred(p))))
}))
idp_opt <- tabel_idw$idp[which.min(tabel_idw$RMSE)]
kable(tabel_idw, digits = 2, caption = "LOOCV IDW untuk berbagai pangkat jarak (p)")
| idp | ME | MAE | RMSE | R2 |
|---|---|---|---|---|
| 1 | -48.81 | 639.87 | 879.06 | 0.37 |
| 2 | -28.93 | 455.52 | 678.14 | 0.56 |
| 3 | -15.35 | 441.87 | 669.42 | 0.57 |
| 4 | -9.89 | 456.75 | 693.47 | 0.55 |
| 5 | -7.30 | 469.55 | 712.85 | 0.53 |
| 6 | -6.10 | 478.43 | 726.97 | 0.52 |
cat("Pangkat optimal (RMSE terkecil): p =", idp_opt, "\n")
## Pangkat optimal (RMSE terkecil): p = 3
idw_grid <- idw(ANNUAL ~ 1, locations = pts_km, newdata = grid_km,
idp = idp_opt, debug.level = 0)
pred_idw <- idw_loocv_pred(idp_opt)
Kinerja IDW pada berbagai pangkat jarak disajikan pada tabel di atas. RMSE terkecil diperoleh pada p = 3 (669,4 mm), sehingga nilai tersebut dipakai. Pada p = 1, RMSE mencapai 879,1 mm, dan setelah titik optimum RMSE kembali naik hingga 727,0 mm pada p = 6. Pada p rendah, estimasi terlalu dipengaruhi stasiun jauh sehingga terlalu halus; pada p tinggi, estimasi terlalu dikuasai stasiun terdekat sehingga permukaan menjadi bergerigi dan meniru nilai lokal. Karena bobot IDW hanya bergantung pada jarak, IDW cenderung membentuk gugusan nilai di sekitar stasiun yang bernilai ekstrem dan tidak menyediakan ukuran ketidakpastian.
v_emp <- variogram(ANNUAL ~ 1, pts_km, cutoff = cutoff_km, width = lebar_km)
hasil_ok <- pilih_model(v_emp)
vgm_ok <- hasil_ok$model
nama_ok <- as.character(vgm_ok$model[2])
kable(transform(hasil_ok$tabel, SSErr = formatC(SSErr, format = "e", digits = 3)),
caption = "Perbandingan model variogram (OK); dipilih SSErr terkecil")
| Model | SSErr |
|---|---|
| Sph | 1.296e+11 |
| Exp | 7.393e+10 |
| Gau | 2.011e+11 |
kable(as.data.frame(vgm_ok) %>% select(model, psill, range), digits = 2,
caption = paste("Model variogram terpilih untuk OK:", nama_ok))
| model | psill | range |
|---|---|---|
| Nug | 476921.7 | 0.00 |
| Exp | 574449.5 | 196.86 |
ggplot() +
geom_point(data = v_emp, aes(dist, gamma), size = 2) +
geom_line(data = variogramLine(vgm_ok, maxdist = cutoff_km), aes(dist, gamma),
color = "red") +
labs(x = "Jarak (km)", y = "Semivariansi") +
theme_minimal()
Gambar 3.5. Variogram empiris dan model terpilih (Ordinary Kriging)
ok_grid <- krige(ANNUAL ~ 1, pts_km, newdata = grid_km, model = vgm_ok,
debug.level = 0)
Dari tiga model kandidat (spherical, exponential, gaussian), model Exp memiliki SSErr terkecil sehingga dipilih. Nugget sebesar 476.922 mm2 atau 45,4% dari total sill (1.051.371 mm2) menunjukkan bahwa hampir separuh variasi tidak terstruktur pada jarak antarstasiun yang diamati. Hal ini dapat berasal dari variasi lokal curah hujan pada jarak dekat, galat pengukuran, atau kesalahan data. Parameter range sebesar 196,9 km pada model eksponensial berarti semivariansi mencapai sekitar 95% sill pada jarak kira-kira tiga kali nilai tersebut (± 591 km), sehingga ketergantungan spasial masih ada pada skala regional tetapi menurun cepat terhadap jarak. Karena nugget yang besar membatasi ketepatan prediksi di lokasi yang menyimpang dari tetangganya, kriging cenderung menghaluskan variasi lokal.
UK memakai tren orde pertama terhadap koordinat
(ANNUAL ~ X + Y) sebagai komponen deterministik, lalu
memodelkan galat (residu) tren dengan variogram. Ketinggian tidak
dipakai sebagai kovariat karena nilainya tidak tersedia di seluruh grid
prediksi.
v_res <- variogram(ANNUAL ~ X + Y, pts_km, cutoff = cutoff_km, width = lebar_km)
hasil_uk <- pilih_model(v_res)
vgm_uk <- hasil_uk$model
nama_uk <- as.character(vgm_uk$model[2])
kable(transform(hasil_uk$tabel, SSErr = formatC(SSErr, format = "e", digits = 3)),
caption = "Perbandingan model variogram (UK); dipilih SSErr terkecil")
| Model | SSErr |
|---|---|
| Sph | 9.700e+10 |
| Exp | 6.020e+10 |
| Gau | 1.321e+11 |
kable(as.data.frame(vgm_uk) %>% select(model, psill, range), digits = 2,
caption = paste("Model variogram terpilih untuk UK:", nama_uk))
| model | psill | range |
|---|---|---|
| Nug | 447389.5 | 0.00 |
| Exp | 524890.2 | 136.86 |
uk_grid <- krige(ANNUAL ~ X + Y, pts_km, newdata = grid_km, model = vgm_uk,
debug.level = 0)
n_negatif <- sum(uk_grid$var1.pred < 0, na.rm = TRUE)
cat("Sel grid UK dengan prediksi negatif:", n_negatif, "\n")
## Sel grid UK dengan prediksi negatif: 0
ggplot() +
geom_point(data = v_res, aes(dist, gamma), size = 2) +
geom_line(data = variogramLine(vgm_uk, maxdist = cutoff_km), aes(dist, gamma),
color = "red") +
labs(x = "Jarak (km)", y = "Semivariansi") +
theme_minimal()
Gambar 3.6. Variogram residu dan model terpilih (Universal Kriging)
UK memodelkan tren orde pertama terhadap koordinat dan menyusun variogram pada residunya; model terpilih adalah Exp dengan nugget 46,0% dari total sill dan range 136,9 km (pada OK: 45,4% dan 196,9 km). Karena tren koordinat hanya menjelaskan 4,1% keragaman, perubahan struktur residu dibandingkan OK relatif kecil. Jumlah sel grid UK dengan prediksi negatif: 0.
nama_idw <- paste0("IDW (p = ", idp_opt, ")")
nama_okl <- paste0("Ordinary Kriging (", nama_ok, ")")
nama_ukl <- paste0("Universal Kriging (", nama_uk, ")")
df_pred <- bind_rows(
ambil_df(idw_grid, nama_idw),
ambil_df(ok_grid, nama_okl),
ambil_df(uk_grid, nama_ukl)
) %>% mutate(metode = factor(metode, levels = c(nama_idw, nama_okl, nama_ukl)))
ggplot() +
geom_tile(data = df_pred, aes(X, Y, fill = pred),
width = ukuran_sel_km, height = ukuran_sel_km) +
geom_sf(data = st_sf(geometry = poly_km), fill = NA, color = "grey30",
linewidth = 0.2) +
geom_sf(data = pts_km, size = 0.12, color = "black", alpha = 0.3) +
scale_fill_viridis_c(name = "ANNUAL (mm)") +
labs(x = "Bujur", y = "Lintang") +
coord_sf(datum = st_crs(4326)) +
facet_wrap(~metode, ncol = 1) +
theme_minimal()
Gambar 3.7. Peta hasil interpolasi curah hujan tahunan 2024 (titik: stasiun)
Ketiga peta menampilkan pola regional yang sangat mirip: curah hujan relatif tinggi di sisi barat Sumatra dan beberapa wilayah Kalimantan serta Papua bagian selatan-tengah, dan lebih rendah di Jawa bagian timur serta Nusa Tenggara. Perbedaan utama ada pada kehalusan permukaan. IDW memunculkan titik-titik setempat bernilai tinggi di sekitar stasiun tertentu, sedangkan OK dan UK menghasilkan permukaan yang lebih halus karena memakai struktur korelasi spasial. Di daerah dengan stasiun yang sangat jarang, seperti bagian tengah Papua dan Kalimantan bagian utara, estimasi pada ketiga metode lebih banyak ditentukan oleh beberapa stasiun yang jauh sehingga perlu ditafsirkan dengan hati-hati. Perbedaan visual antarmetode tidak dengan sendirinya menunjukkan akurasi; pemilihan metode terbaik didasarkan pada LOOCV.
Peta ketidakpastian kriging (simpangan baku galat prediksi). IDW tidak menghasilkan ukuran ketidakpastian.
df_sd <- df_pred %>%
filter(metode != nama_idw) %>%
mutate(sd = sqrt(pmax(var, 0)))
ggplot() +
geom_tile(data = df_sd, aes(X, Y, fill = sd),
width = ukuran_sel_km, height = ukuran_sel_km) +
geom_sf(data = st_sf(geometry = poly_km), fill = NA, color = "grey30",
linewidth = 0.2) +
geom_sf(data = pts_km, size = 0.12, color = "black", alpha = 0.3) +
scale_fill_viridis_c(name = "SD galat (mm)", option = "magma", direction = -1) +
labs(x = "Bujur", y = "Lintang") +
coord_sf(datum = st_crs(4326)) +
facet_wrap(~metode, ncol = 1) +
theme_minimal()
Gambar 3.8. Simpangan baku galat prediksi kriging
Simpangan baku galat prediksi kriging berkisar antara 692 dan 1.026 mm. Nilai terendah terlihat di wilayah padat stasiun seperti Jawa dan sebagian besar Sumatra, sedangkan nilai tertinggi di wilayah yang stasiunnya paling jarang, yaitu Papua bagian selatan-tengah dan Kalimantan bagian utara. Pola OK dan UK hampir identik. Dua hal perlu dicermati. Pertama, SD galat di wilayah padat pun masih relatif besar karena nugget yang besar, sehingga menambah stasiun di wilayah yang sudah rapat hanya sedikit menurunkan ketidakpastian. Kedua, selisih SD galat antara wilayah padat dan jarang tidak terlalu besar, sehingga peta ini lebih menegaskan daerah mana yang paling sedikit didukung data daripada memberikan batas kesalahan yang presisi. Simpangan baku galat dihitung berdasarkan model variogram yang dianggap benar sehingga tidak menjamin bahwa kesalahan aktual selalu berada dalam rentang tersebut; IDW tidak menghasilkan ukuran ketidakpastian yang sebanding.
Setiap stasiun secara bergantian dikeluarkan, diduga dari stasiun lain, lalu dibandingkan dengan nilai sebenarnya. Variogram dipilih memakai seluruh data sehingga hasil kriging sedikit lebih optimistis dibanding situasi sebenarnya. Catatan ini perlu disebutkan saat membahas hasil.
LOOCV untuk OK dan UK dihitung dengan rumus eksak dari satu kali
inversi matriks kovariansi (galat LOOCV stasiun ke-\(i\) sama dengan elemen ke-\(i\) dari \(A^{-1}y\) dibagi elemen diagonal ke-\(i\) dari \(A^{-1}\), dengan \(A\) sistem kriging yang diperluas dengan
basis tren; Dubrule, 1983), bukan dengan menjalankan kriging ulang untuk
setiap stasiun. Hasilnya sama dengan krige.cv()
berlingkungan global, tetapi selesai dalam hitungan menit untuk ribuan
stasiun, sedangkan krige.cv() dengan 4.000 lebih stasiun
sangat lambat.
# Kovariansi dari model variogram gstat (Nug, Exp, Sph, Gau): C(h) = sill - gamma(h)
kov_dari_vgm <- function(D, vgm_model) {
K <- matrix(0, nrow(D), ncol(D))
for (i in seq_len(nrow(vgm_model))) {
m <- as.character(vgm_model$model[i])
ps <- vgm_model$psill[i]
r <- vgm_model$range[i]
K <- K + switch(m,
Nug = ps * (D == 0),
Exp = ps * exp(-D / r),
Sph = ps * ifelse(D < r, 1 - 1.5 * D / r + 0.5 * (D / r)^3, 0),
Gau = ps * exp(-(D / r)^2),
stop("Model variogram belum didukung: ", m)
)
}
K
}
# LOOCV eksak untuk kriging dengan basis tren F (OK: kolom 1; UK: 1, X, Y)
loocv_kriging <- function(K, F, z) {
diag(K) <- diag(K) * (1 + 1e-8) # jaga agar K definit positif
Kinv <- chol2inv(chol(K))
KF <- Kinv %*% F
Sinv <- solve(crossprod(F, KF))
d <- diag(Kinv) - rowSums((KF %*% Sinv) * KF)
Kz <- as.numeric(Kinv %*% z)
Az <- Kz - as.numeric(KF %*% (Sinv %*% crossprod(F, Kz)))
data.frame(observed = z, var1.pred = z - Az / d)
}
K_ok <- kov_dari_vgm(D, vgm_ok)
cv_ok <- loocv_kriging(K_ok, matrix(1, nrow(D), 1), z)
rm(K_ok)
K_uk <- kov_dari_vgm(D, vgm_uk)
F_uk <- cbind(1, as.numeric(scale(pts_km$X)), as.numeric(scale(pts_km$Y)))
cv_uk <- loocv_kriging(K_uk, F_uk, z)
rm(K_uk)
tabel_cv <- bind_rows(
data.frame(Metode = nama_idw, t(hitung_metrik(z, pred_idw))),
data.frame(Metode = nama_okl,
t(hitung_metrik(cv_ok$observed, cv_ok$var1.pred))),
data.frame(Metode = nama_ukl,
t(hitung_metrik(cv_uk$observed, cv_uk$var1.pred)))
) %>%
mutate(`RMSE relatif (%)` = 100 * RMSE / mean(z)) %>%
arrange(RMSE)
kable(tabel_cv, digits = 3,
caption = "Perbandingan akurasi interpolasi berdasarkan LOOCV (urut RMSE)")
| Metode | ME | MAE | RMSE | R2 | RMSE relatif (%) |
|---|---|---|---|---|---|
| Universal Kriging (Exp) | -1.047 | 441.933 | 654.067 | 0.584 | 25.588 |
| Ordinary Kriging (Exp) | -0.844 | 448.887 | 661.519 | 0.575 | 25.879 |
| IDW (p = 3) | -15.349 | 441.870 | 669.416 | 0.566 | 26.188 |
terbaik <- tabel_cv$Metode[1]
df_cv <- bind_rows(
data.frame(obs = z, pred = pred_idw, metode = nama_idw),
data.frame(obs = cv_ok$observed, pred = cv_ok$var1.pred, metode = nama_okl),
data.frame(obs = cv_uk$observed, pred = cv_uk$var1.pred, metode = nama_ukl)
) %>% mutate(metode = factor(metode, levels = c(nama_idw, nama_okl, nama_ukl)))
ggplot(df_cv, aes(obs, pred)) +
geom_point(alpha = 0.6, color = "steelblue") +
geom_abline(slope = 1, intercept = 0, color = "red", linetype = "dashed") +
facet_wrap(~metode) +
labs(x = "Pengamatan", y = "Prediksi LOOCV") +
theme_minimal()
Gambar 3.9. Nilai pengamatan vs prediksi LOOCV
Berdasarkan RMSE LOOCV, urutan metode dari yang terbaik adalah:
Universal Kriging (Exp) (654,1 mm), Ordinary Kriging (Exp) (661,5 mm),
IDW (p = 3) (669,4 mm). Metode terbaik, Universal Kriging
(Exp), memiliki RMSE 654,1 mm (25,6% dari rata-rata
ANNUAL), MAE 441,9 mm, dan R2
0,584.
Selisih RMSE antara metode terbaik dan terburuk hanya 2,3%, sehingga ketiga metode berkinerja hampir setara dan keunggulan metode terbaik tidak boleh dibesar-besarkan; penelitian ini juga tidak menguji signifikansi statistik selisih tersebut. UK sedikit lebih baik daripada OK (selisih RMSE 1,1%), selaras dengan temuan bahwa tren koordinat lemah (R2 = 0,041): pemodelan tren hanya menambah sedikit informasi sehingga peningkatan akurasinya juga kecil. OK memiliki RMSE lebih kecil daripada IDW tetapi MAE yang sedikit lebih besar (448,9 mm vs 441,9 mm); ini menunjukkan bahwa kriging lebih baik menekan galat besar (RMSE memberi bobot lebih pada galat besar), sedangkan galat tipikal kedua metode setara.
Mean error ketiga metode berkisar -15,35 sampai -0,84 mm, atau tidak lebih dari 0,6% dari rata-rata curah hujan, sehingga tidak ada bias sistematis yang berarti (ME negatif menandakan kecenderungan menaksir sedikit lebih rendah). RMSE relatif 25,6%–26,2% dan R2 0,57–0,58 menunjukkan bahwa sekitar 42% keragaman nilai pengamatan tidak dijelaskan oleh prediksi LOOCV metode terbaik. Besarnya nugget (45% dari sill) sejalan dengan batas kemampuan prediksi ini: variasi lokal yang tidak terstruktur secara spasial tidak dapat ditangkap oleh metode interpolasi mana pun yang hanya memakai informasi dari stasiun sekitar.
Pada diagram pencar di atas, titik yang dekat garis diagonal 1:1 menunjukkan prediksi yang mendekati nilai aktual. Sebaran titik di sekitar garis relatif serupa pada ketiga metode, sesuai dengan selisih RMSE yang kecil. Nilai pengamatan yang tinggi cenderung berada di bawah garis 1:1, artinya nilai tinggi cenderung diprediksi lebih rendah; hal ini lazim pada interpolasi karena prediksi merupakan rata-rata terboboti nilai di sekitarnya sehingga menghaluskan nilai ekstrem. Stasiun bernilai sangat tinggi, termasuk yang perlu diverifikasi (stasiun BATU KARUT), termasuk yang paling sulit diprediksi dan dapat menaikkan RMSE. Dengan demikian, hasil interpolasi lebih tepat dipakai sebagai gambaran pola spasial regional daripada sebagai pengganti pengamatan langsung pada setiap lokasi.
Pertama, persebaran stasiun tidak merata sehingga ketelitian estimasi berbeda antarwilayah, terutama di Indonesia bagian timur. Kedua, tren pada UK hanya memakai koordinat X dan Y; ketinggian tidak dipakai karena tidak tersedia di seluruh titik grid dan 28,5% stasiun bernilai 0 m, serta faktor lain seperti monsun atau topografi tidak dimasukkan. Ketiga, variogram diestimasi dari seluruh data sebelum LOOCV sehingga hasil LOOCV kriging dapat sedikit optimistis dibanding situasi sebenarnya (Dubrule, 1983). Keempat, sebagian nilai ekstrem dan koordinat pada sumber data tidak dapat diverifikasi sepenuhnya; keputusan mempertahankan atau mengeluarkannya mengikuti aturan pada Bab 2. Kelima, penelitian ini hanya memakai satu tahun pengamatan (2024) sehingga hasilnya tidak mewakili kondisi tahun lain.
Pengembangan selanjutnya dapat mencakup kovariat lingkungan (misalnya ketinggian dari data DEM), prosedur validasi yang membentuk ulang model variogram pada setiap iterasi, serta penggunaan data beberapa tahun.
Aldrian, E., & Susanto, R. D. (2003). Identification of three dominant rainfall regions within Indonesia and their relationship to sea surface temperature. International Journal of Climatology, 23(12), 1435–1452. https://doi.org/10.1002/joc.950
Bahtiyar, A. D. R., Hoyyi, A., & Yasin, H. (2014). Ordinary kriging dalam estimasi curah hujan di Kota Semarang. Jurnal Gaussian, 3(2), 151–159. https://ejournal3.undip.ac.id/index.php/gaussian/article/view/5900
Caloiero, T., Pellicone, G., Modica, G., & Guagliardi, I. (2021). Comparative analysis of different spatial interpolation methods applied to monthly rainfall as support for landscape management. Applied Sciences, 11(20), 9566. https://doi.org/10.3390/app11209566
Dubrule, O. (1983). Cross validation of kriging in a unique neighborhood. Mathematical Geology, 15, 687–699. https://doi.org/10.1007/BF01033232
Gräler, B., Pebesma, E., & Heuvelink, G. (2016). Spatio-temporal interpolation using gstat. The R Journal, 8(1), 204–218. https://doi.org/10.32614/RJ-2016-014
Najwa, S., Pratiwi, D. A., Ahdian, M. R., Indriani, A., Mindra, I. G. N., Falah, A. N., & Ruchjana, B. N. (2025). Integrating spatial autoregressive exogenous with ordinary kriging for improved rainfall prediction in Java: Enhancing accuracy with climate variables and spatial autocorrelation. InPrime: Indonesian Journal of Pure and Applied Mathematics, 7(1), 25–43. https://doi.org/10.15408/inprime.v7i1.42070
Pebesma, E. (2018). Simple features for R: Standardized support for spatial vector data. The R Journal, 10(1), 439–446. https://doi.org/10.32614/RJ-2018-009
Rohma, N. N. (2022). Estimation of ordinary kriging method with jackknife technique on rainfall data in Malang Raya. International Journal on Information and Communication Technology (IJoICT), 8(2), 22–39. https://doi.org/10.21108/ijoict.v8i2.678
Sanusi, W., Sidjara, S., Patahuddin, S., & Danial, M. (2024). A comparison of spatial interpolation methods for regionalizing maximum daily rainfall data in South Sulawesi, Indonesia. ITM Web of Conferences, 58, 04003. https://doi.org/10.1051/itmconf/20245804003
Snyder, J. P. (1987). Map projections: A working manual (U.S. Geological Survey Professional Paper 1395). U.S. Geological Survey. https://doi.org/10.3133/pp1395
Sofia, D. A., Sujono, J., & Legono, D. (2018). Analisis variabilitas spasial dan temporal curah hujan di wilayah Gunung Merapi. Jurnal Teknisia, XXIII(1). https://journal.uii.ac.id/teknisia/article/download/10679/8757/25443
Sofieyanti, N., & Sirodj, D. A. N. (2024). Penerapan metode universal kriging untuk mengestimasi laju penurunan muka tanah DKI Jakarta. Jurnal Gaussian, 13(2), 431–442. https://doi.org/10.14710/j.gauss.13.2.431-442
Tahitu, J. C., Titaley, J., & Manurung, T. (2026). Penerapan metode Inverse Distance Weighted (IDW) dalam interpolasi curah hujan (Studi kasus: Kabupaten Langkat). AITI: Jurnal Teknologi Informasi, 23(1), 61–72. https://doi.org/10.24246/aiti.v23i1.61-72
van den Besselaar, E. J. M., et al. (2015). International Climate Assessment & Dataset: Climate services across borders. Bulletin of the American Meteorological Society, 96(1), 16–21. https://doi.org/10.1175/BAMS-D-13-00249.1. Sumber data: SACA&D, http://sacad.database.bmkg.go.id/
sessionInfo()
## R version 4.5.2 (2025-10-31 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 11 x64 (build 26200)
##
## Matrix products: default
## LAPACK version 3.12.1
##
## locale:
## [1] LC_COLLATE=English_United States.utf8
## [2] LC_CTYPE=English_United States.utf8
## [3] LC_MONETARY=English_United States.utf8
## [4] LC_NUMERIC=C
## [5] LC_TIME=English_United States.utf8
##
## time zone: Asia/Jakarta
## tzcode source: internal
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] knitr_1.51 gstat_2.1-6 sf_1.1-0 ggplot2_4.0.2 dplyr_1.2.0
##
## loaded via a namespace (and not attached):
## [1] s2_1.1.9 sass_0.4.10 generics_0.1.4
## [4] class_7.3-23 KernSmooth_2.23-26 lattice_0.22-7
## [7] digest_0.6.39 magrittr_2.0.4 evaluate_1.0.5
## [10] grid_4.5.2 RColorBrewer_1.1-3 rnaturalearth_1.2.0
## [13] fastmap_1.2.0 jsonlite_2.0.0 e1071_1.7-17
## [16] DBI_1.3.0 viridisLite_0.4.3 scales_1.4.0
## [19] jquerylib_0.1.4 abind_1.4-8 cli_3.6.5
## [22] rlang_1.1.7 units_1.0-0 intervals_0.15.5
## [25] withr_3.0.2 cachem_1.1.0 yaml_2.3.12
## [28] FNN_1.1.4.1 otel_0.2.0 parallel_4.5.2
## [31] tools_4.5.2 spacetime_1.3-4 vctrs_0.7.1
## [34] R6_2.6.1 zoo_1.8-15 proxy_0.4-29
## [37] lifecycle_1.0.5 classInt_0.4-11 pkgconfig_2.0.3
## [40] pillar_1.11.1 bslib_0.10.0 gtable_0.3.6
## [43] glue_1.8.0 Rcpp_1.1.1 rnaturalearthdata_1.0.0
## [46] xfun_0.61 tibble_3.3.1 tidyselect_1.2.1
## [49] rstudioapi_0.18.0 farver_2.1.2 htmltools_0.5.9
## [52] labeling_0.4.3 rmarkdown_2.30 xts_0.14.1
## [55] wk_0.9.5 compiler_4.5.2 S7_0.2.1
## [58] stars_0.7-3 sp_2.2-3