Pertemuan 9: Interpolasi Spasial
Interpolasi Spasial
Interpolasi spasial merupakan metode yang digunakan untuk memperkirakan nilai peubah spasial pada lokasi yang tidak diketahui berdasarkan data yang tersedia pada lokasi tetangga yang diketahui (Cressie, 1993). Interpolasi ini digunakan dalam berbagai bidang seperti geografi, meteorologi, pertanian, dan lingkungan untuk menghasilkan peta atau permukaan kontinu dari data yang awalnya hanya tersedia pada titik-titik diskrit.
Konsep Dasar
Interpolasi spasial mengasumsikan bahwa nilai variabel yang diukur (misalnya, suhu, ketinggian, kelembaban tanah, dll.) di suatu lokasi akan serupa dengan nilai di lokasi-lokasi tetangga. Dengan kata lain, ada korelasi spasial, di mana lokasi yang lebih dekat cenderung memiliki nilai yang lebih mirip dibandingkan lokasi yang lebih jauh.
Data yang digunakan
Dalam bagian ini, seluruh metode interpolasi menggunakan data
meuse dari paket sp (Pebesma & Bivand),
yang berisi 155 titik pengamatan konsentrasi logam berat pada tanah
lapisan atas (topsoil) di dataran banjir Sungai Meuse, Belanda. Variabel
yang diinterpolasi adalah konsentrasi seng (zinc, ppm).
Karena sebaran zinc menjulur ke kanan, digunakan
transformasi log(zinc) pada seluruh metode, sama seperti
pada bagian Kriging, sehingga RMSE antarmetode dapat dibandingkan secara
adil. Lokasi prediksi adalah meuse.grid (grid 40 m x 40 m,
3.103 titik).
Langkah-langkah:
- Memuat data
meusedanmeuse.grid, lalu mengubahnya menjadi objeksf(CRS 28992, RD New). - Membuat variabel
vble=log(zinc).
# Memuat pustaka yang diperlukan
library(sp)
library(sf)
library(gstat)
library(dismo)
library(ggplot2)
library(viridis)
library(knitr)
data(meuse)
data(meuse.grid)
# Data pengamatan (lokasi sampel)
d <- st_as_sf(meuse, coords = c("x", "y"), crs = 28992)
d$vble <- log(d$zinc)
# Lokasi prediksi (grid meuse)
coop <- st_as_sf(meuse.grid, coords = c("x", "y"), crs = 28992)
# Batas wilayah studi (gabungan sel grid 40 m), dipakai untuk plot Voronoi
map <- st_union(st_buffer(coop, dist = 20, endCapStyle = "SQUARE"))
ggplot() +
geom_sf(data = map, fill = "grey90", color = NA) +
geom_sf(data = d, aes(color = vble), size = 1.8) +
scale_color_viridis_c(name = "log(zinc)") + theme_bw()Lokasi prediksi (coop) adalah titik pusat sel
meuse.grid:
Fungsi bantu prediksi
Agar urutan prediksi selalu sama dengan urutan lokasi target (penting
untuk ensembel dan cross validation), setiap metode dibungkus dalam satu
fungsi dengan masukan data latih (train) dan lokasi target
(newdata).
RMSE <- function(observed, predicted) {
sqrt(mean((observed - predicted)^2))
}
# Closest observation: nilai titik terdekat (setara dengan sel Voronoi)
pred_closest <- function(train, newdata) {
idx <- st_nearest_feature(newdata, train)
train$vble[idx]
}
# IDW dengan semua titik latih, idp = 1
pred_idw <- function(train, newdata, idp = 1) {
g <- gstat(formula = vble ~ 1, locations = train,
nmax = nrow(train), set = list(idp = idp))
predict(g, newdata, debug.level = 0)$var1.pred
}
# Nearest neighbors: rata-rata k tetangga terdekat (idp = 0 -> bobot sama)
pred_nn <- function(train, newdata, k = 5) {
g <- gstat(formula = vble ~ 1, locations = train,
nmax = k, set = list(idp = 0))
predict(g, newdata, debug.level = 0)$var1.pred
}
# Ordinary kriging: variogram di-fit ulang pada data latih
pred_ok <- function(train, newdata) {
vg <- variogram(vble ~ 1, train)
fv <- suppressWarnings(
fit.variogram(vg, vgm(psill = 0.5, model = "Sph", range = 900, nugget = 0.1))
)
g <- gstat(formula = vble ~ 1, data = train, model = fv)
predict(g, newdata, debug.level = 0)$var1.pred
}Metode Deteministik
Closest Observation
Metode ini menggunakan diagram Voronoi untuk membagi ruang menjadi poligon yang masing-masing berisi satu titik penghasil. Setiap titik dalam poligon lebih dekat ke titik penghasil daripada ke titik lainnya. Prediksi harga properti dilakukan berdasarkan nilai titik terdekat.
Langkah-langkah:
- Membuat diagram Voronoi berdasarkan data titik.
- Menentukan prediksi pada titik-titik yang terletak dalam setiap
poligon Voronoi (setara dengan mengambil nilai titik pengamatan
terdekat,
st_nearest_feature()).
# Membuat diagram Voronoi dari lokasi pengamatan
vgeom <- st_collection_extract(st_voronoi(st_union(st_geometry(d))), "POLYGON")
vor <- st_sf(geometry = vgeom)
vor <- st_join(vor, d["vble"])
vor <- suppressWarnings(st_intersection(vor, map))
ggplot() +
geom_sf(data = vor, aes(fill = vble), alpha = 0.7, color = "white", linewidth = 0.1) +
geom_sf(data = d, size = 0.5) +
scale_fill_viridis_c(name = "log(zinc)") + theme_bw()Di sini, kita dapat melihat bagaimana setiap poligon dalam diagram Voronoi mewakili wilayah prediksi yang dipengaruhi oleh titik terdekat.
Invers Distance Weight
Metode IDW (Inverse Distance Weighting) menggunakan bobot berdasarkan jarak antara lokasi yang belum teramati dan lokasi-lokasi yang sudah teramati. Semakin dekat jarak, semakin besar bobot yang diberikan pada nilai lokasi yang teramati.
Langkah-langkah:
- Menerapkan IDW dengan menggunakan fungsi
gstat(). - Menggunakan
predict()untuk mendapatkan prediksi pada lokasi yang tidak teramati.
Nearest Neighbors
Metode Nearest Neighbors menghitung rata-rata harga dari k titik terdekat. Setiap tetangga dianggap memiliki bobot yang sama.
Langkah-langkah:
- Menggunakan fungsi
gstat()untuk menerapkan nearest neighbors dengan parameternmaxyang menentukan jumlah titik tetangga. - Menggunakan fungsi
predict()untuk memprediksi di lokasi yang tidak teramati.
Pendekatan Ensembel
Pendekatan Ensembel menggabungkan hasil prediksi dari berbagai metode interpolasi (seperti Closest Observation, IDW, dan Nearest Neighbors) dengan memberikan bobot pada setiap metode. Bobot ini bisa berdasarkan kinerja masing-masing metode.
Langkah-langkah:
- Menghitung prediksi dengan masing-masing metode.
- Menghitung prediksi ensembel dengan menggabungkan hasil prediksi setiap metode menggunakan bobot tertentu.
# Ensemble (bobot sama)
weights <- c(1/3, 1/3, 1/3)
p4 <- p1 * weights[1] + p2 * weights[2] + p3 * weights[3]Peta prediksi keempat metode:
buat <- function(p, nama) {
x <- coop
x$pred <- p
x$metode <- nama
x[, c("metode", "pred")]
}
hasil <- rbind(
buat(p1, "Closest observation"),
buat(p2, "IDW"),
buat(p3, "Nearest neighbors"),
buat(p4, "Ensemble (bobot sama)")
)
hasil$metode <- factor(hasil$metode, levels = unique(hasil$metode))
ggplot(hasil) +
geom_sf(aes(color = pred), size = 0.5) +
facet_wrap(~ metode, ncol = 2) +
scale_color_viridis_c(name = "log(zinc)") + theme_bw()Metode Stokastik
Kriging
Kriging adalah metode interpolasi geostatistik yang memperhitungkan autokorelasi spasial untuk membuat prediksi. Ordinary Kriging (OK) adalah salah satu varian Kriging yang paling umum digunakan, di mana nilai yang akan diprediksi adalah variabel z yang tidak diketahui, yang dianggap sebagai variabel acak.
Variogram: Dalam Ordinary Kriging, penting untuk membangun variogram. Variogram adalah grafik yang menggambarkan bagaimana variabilitas antar titik pengamatan berubah seiring dengan jarak antar titik tersebut. Kriging menggunakan informasi variogram ini untuk menghitung covariance antar titik.
Model Semivariogram: Setelah variogram dihitung, model semivariogram (seperti spherical, exponential, atau Gaussian) di-fit ke data untuk menangkap hubungan spasial di antara titik-titik pengamatan.
Ordinary Kriging mengasumsikan bahwa nilai rata-rata populasi tidak diketahui, tetapi konstan di seluruh wilayah.
# Memuat paket yang dibutuhkan
library(sp)
library(gstat)
library(sf)
library(mapview)
# Memuat dataset meuse dan meuse.grid yang sudah tersedia di R
data(meuse)
data(meuse.grid)
# Mengonversi dataset meuse menjadi objek 'sf' (simple feature) dengan koordinat 'x' dan 'y', dan CRS 28992 (proyeksi UTM)
meuse <- st_as_sf(meuse, coords = c("x", "y"), crs = 28992)
meuse.grid <- st_as_sf(meuse.grid, coords = c("x", "y"), crs = 28992)
# Menampilkan peta interaktif
mapview(meuse, zcol = "zinc", map.types = "CartoDB.Voyager")Sumber : kodu.ut.ee (Lesson 6 : Spatial Interpolation - Geospatial Analysis with Python and R
Keterangan :
Nugget: Nilai semivarian ketika jarak antarobservasi mendekati nol, yang merepresentasikan variasi mikrospasial dan kesalahan pengukuran.
Sill: Nilai maksimum atau plateau semivarian yang merepresentasikan total varians spasial.
Range: Jarak ketika semivarian mencapai sill, yang menunjukkan batas jangkauan ketergantungan spasial.
Secara umum:
Semivarian rendah pada jarak dekat → indikasi bahwa lokasi berdekatan memiliki nilai yang mirip.
Semivarian meningkat seiring jarak → indikasi adanya struktur ketergantungan spasial.
Semivarian mencapai sill → variasi spasial relatif stabil setelah jarak tertentu.
## short long
## 1 Nug Nug (nugget)
## 2 Exp Exp (exponential)
## 3 Sph Sph (spherical)
## 4 Gau Gau (gaussian)
## 5 Exc Exclass (Exponential class/stable)
## 6 Mat Mat (Matern)
## 7 Ste Mat (Matern, M. Stein's parameterization)
## 8 Cir Cir (circular)
## 9 Lin Lin (linear)
## 10 Bes Bes (bessel)
## 11 Pen Pen (pentaspherical)
## 12 Per Per (periodic)
## 13 Wav Wav (wave)
## 14 Hol Hol (hole)
## 15 Log Log (logarithmic)
## 16 Pow Pow (power)
## 17 Spl Spl (spline)
## 18 Leg Leg (Legendre)
## 19 Err Err (Measurement error)
## 20 Int Int (Intercept)
# Menentukan model variogram awal dengan parameter psill, model, range, dan nugget
vinitial <- vgm(psill = 0.5, model = "Sph", range = 900, nugget = 0.1)
# Memvisualisasikan variogram dengan model awal (vinitial) dan batas cutoff 1000
plot(v, vinitial, cutoff = 1000, cex = 1.5)# Menyesuaikan variogram dengan model "Sph" menggunakan data v dan parameter
fv<-fit.variogram(object=v,model= vgm(psill= 0.5,model= "Sph",range= 900,nugget= 0.1))
fv## model psill range
## 1 Nug 0.05066017 0.0000
## 2 Sph 0.59060556 897.0044
library(automap)
# Autofit variogram untuk data log-zinc
afvg <- autofitVariogram(log(zinc) ~ 1, meuse)
# Plot hasil
plot(afvg)library(ggplot2)
library(viridis)
# Membuat objek gstat dengan model variogram hasil fitting (fv) untuk data zinc
k <- gstat(formula = log(zinc) ~ 1, data = meuse, model = fv)
# Melakukan prediksi variabel pada grid menggunakan model gstat yang sudah dibuat
kpred <- predict(k, meuse.grid)## [using ordinary kriging]
# Visualisasi prediksi log(zinc) dengan warna berdasarkan var1.pred
ggplot() + geom_sf(data = kpred, aes(color = var1.pred)) +
geom_sf(data = meuse) +
scale_color_viridis(name = "log(zinc)") + theme_bw()# Visualisasi prediksi varians dengan warna berdasarkan var1.var
ggplot() + geom_sf(data = kpred, aes(color = var1.var)) +
geom_sf(data = meuse) +
scale_color_viridis(name = "variance") + theme_bw()Evaluasi Model (Cross Validation)
Untuk menilai kinerja setiap metode, dilakukan K-fold cross-validation (K = 5) dan dihitung RMSE (Root Mean Square Error). Cross-validation digunakan untuk memastikan bahwa model tidak hanya cocok dengan data latih tetapi juga dapat memprediksi data baru dengan baik. Metode yang dibandingkan:
- Closest observation
- IDW (idp = 1)
- Nearest neighbors (k = 5)
- Ensembel bobot sama (rata-rata Closest, IDW, NN)
- Ensembel bobot 1/RMSE (Closest, IDW, NN)
- Ordinary Kriging (variogram Sph di-fit ulang pada setiap data latih)
Langkah-langkah:
- Membagi data menjadi K bagian dengan
dismo::kfold(). - Pada setiap fold, seluruh model (termasuk variogram Kriging) dilatih hanya pada data latih, lalu memprediksi data uji.
- Bobot ensembel 1/RMSE dihitung dari cross validation di dalam data latih (inner CV), bukan dari data uji, supaya tidak terjadi kebocoran data (data leakage).
- RMSE dihitung pada skala
log(zinc)(skala model) dan pada skala asli (ppm, setelahexp()).
set.seed(123)
K <- 5
# Bobot ensembel = 1/RMSE hasil CV internal pada data latih
bobot_invrmse <- function(train, k_inner = 3) {
fi <- kfold(nrow(train), k = k_inner)
e <- matrix(NA, k_inner, 3)
for (j in 1:k_inner) {
tr <- train[fi != j, ]
te <- train[fi == j, ]
e[j, ] <- c(RMSE(te$vble, pred_closest(tr, te)),
RMSE(te$vble, pred_idw(tr, te)),
RMSE(te$vble, pred_nn(tr, te)))
}
w <- 1 / colMeans(e)
w / sum(w)
}
# Pembagian data untuk K-fold
kf <- kfold(nrow(d), k = K)
metode <- c("Closest observation", "IDW", "Nearest neighbors",
"Ensemble (bobot sama)", "Ensemble (bobot 1/RMSE)", "Kriging (OK)")
rmse_log <- matrix(NA, K, length(metode),
dimnames = list(paste("Fold", 1:K), metode))
rmse_zinc <- rmse_log
for (f in 1:K) {
test <- d[kf == f, ]
train <- d[kf != f, ]
q1 <- pred_closest(train, test)
q2 <- pred_idw(train, test)
q3 <- pred_nn(train, test)
q4 <- (q1 + q2 + q3) / 3
w <- bobot_invrmse(train)
q5 <- w[1] * q1 + w[2] * q2 + w[3] * q3
q6 <- pred_ok(train, test)
P <- cbind(q1, q2, q3, q4, q5, q6)
rmse_log[f, ] <- apply(P, 2, function(p) RMSE(test$vble, p))
rmse_zinc[f, ] <- apply(P, 2, function(p) RMSE(exp(test$vble), exp(p)))
}RMSE pada skala log(zinc) untuk setiap fold dan
rata-ratanya:
tab_log <- rbind(rmse_log, "Rata-rata" = colMeans(rmse_log))
kable(round(tab_log, 4), caption = "RMSE 5-fold CV, skala log(zinc)")| Closest observation | IDW | Nearest neighbors | Ensemble (bobot sama) | Ensemble (bobot 1/RMSE) | Kriging (OK) | |
|---|---|---|---|---|---|---|
| Fold 1 | 0.6327 | 0.5496 | 0.4958 | 0.4707 | 0.4706 | 0.4281 |
| Fold 2 | 0.5987 | 0.5828 | 0.4943 | 0.4733 | 0.4723 | 0.4183 |
| Fold 3 | 0.6328 | 0.7843 | 0.6516 | 0.6379 | 0.6356 | 0.5055 |
| Fold 4 | 0.4874 | 0.6333 | 0.5316 | 0.4915 | 0.4912 | 0.4198 |
| Fold 5 | 0.5593 | 0.6293 | 0.4791 | 0.4820 | 0.4780 | 0.3840 |
| Rata-rata | 0.5822 | 0.6358 | 0.5305 | 0.5111 | 0.5095 | 0.4312 |
RMSE pada skala asli (ppm):
tab_zinc <- rbind(rmse_zinc, "Rata-rata" = colMeans(rmse_zinc))
kable(round(tab_zinc, 2), caption = "RMSE 5-fold CV, skala zinc (ppm, back-transform exp)")| Closest observation | IDW | Nearest neighbors | Ensemble (bobot sama) | Ensemble (bobot 1/RMSE) | Kriging (OK) | |
|---|---|---|---|---|---|---|
| Fold 1 | 309.90 | 274.47 | 251.30 | 239.41 | 238.85 | 193.36 |
| Fold 2 | 385.80 | 302.53 | 278.28 | 261.52 | 261.74 | 223.30 |
| Fold 3 | 323.50 | 466.47 | 389.75 | 382.66 | 380.84 | 321.35 |
| Fold 4 | 237.64 | 364.47 | 280.71 | 285.78 | 283.87 | 206.72 |
| Fold 5 | 299.40 | 344.74 | 311.78 | 300.49 | 300.08 | 246.75 |
| Rata-rata | 311.25 | 350.54 | 302.36 | 293.97 | 293.08 | 238.30 |
rata <- data.frame(metode = factor(metode, levels = metode),
rmse = colMeans(rmse_log))
ggplot(rata, aes(x = metode, y = rmse, fill = metode)) +
geom_col(show.legend = FALSE) +
geom_text(aes(label = round(rmse, 3)), vjust = -0.4) +
scale_fill_viridis_d() +
labs(x = NULL, y = "Rata-rata RMSE (log(zinc))") +
theme_bw() + theme(axis.text.x = element_text(angle = 25, hjust = 1))Kesimpulan
Berdasarkan rata-rata RMSE 5-fold cross validation, metode dengan galat terkecil adalah Kriging (OK) (RMSE = 0.4312 pada skala log(zinc)), sedangkan metode dengan galat terbesar adalah IDW (RMSE = 0.6358).
Catatan Tambahan
- Data. Seluruh metode (Closest observation, IDW, NN,
Ensembel, Kriging) kini memakai data
meusesesuai ketentuan tugas. Dataproperties(Athena) tidak lagi dipakai. - Variabel respons sama. Semua metode memakai
log(zinc). Pada versi sebelumnya Kriging memakailog(zinc), sedangkan metode lain memakai variabel berbeda sehingga RMSE tidak sebanding. - Kriging masuk perbandingan RMSE. Variogram di-fit ulang pada data latih di setiap fold (bukan memakai variogram dari seluruh data), agar tidak ada kebocoran informasi dari data uji.
- Ensembel tanpa kebocoran data. Pada versi
sebelumnya bobot
1/RMSEdihitung dari RMSE pada data uji yang sama dengan yang dievaluasi sehingga RMSE ensembel terlalu optimistis. Di sini bobot dihitung dari inner CV pada data latih. Ensembel bobot sama juga disertakan sebagai pembanding. - Urutan prediksi Voronoi. Pada versi sebelumnya
st_intersection(v, coop)$vblemengembalikan baris berurutan menurut poligon Voronoi, bukan menurut urutancoop/test, sehingga dapat tidak sejajar denganp2,p3, dan nilai observasi saat dihitung RMSE atau digabung dalam ensembel. Di sinist_nearest_feature()dipakai (hasil identik dengan sel Voronoi) sehingga urutan selalu sejajar. Diagram Voronoi tetap ditampilkan untuk visualisasi. - Grid prediksi. Untuk
meuse, lokasi prediksi memakaimeuse.gridlangsung, sehingga tidak perlu membuat grid raster 100x100 sendiri. Peta memakaiggplot2agar konsisten dengan bagian Kriging dan tidak bergantung pada versitmap(argumenpalettesudah usang ditmapv4). - Pesan R. Teks seperti
[inverse distance weighted interpolation]dan[using ordinary kriging]adalah pesan keluaran R, bukan kode. Di sini disembunyikan dengandebug.level = 0danmessage = FALSE. - Nearest Neighbors. Metode ini memakai
gstatdenganidp = 0(bobot jarak dinolkan, semua tetangga berbobot sama) dannmax = 5, sehingga hasilnya rata-rata 5 tetangga terdekat. Pesan “inverse distance weighted” pada keluaran R muncul karena fungsi yang dipakai sama dengan IDW. - Pembagian fold.
set.seed(123)dipakai agar hasil dapat direproduksi. RMSE bergantung pada pembagian fold, sehingga angka dapat sedikit berbeda jika seed diubah. - Back-transform. RMSE skala ppm dihitung dengan
exp()sederhana (tanpa koreksi bias log-normal), sehingga dipakai hanya untuk gambaran besaran galat. Perbandingan utama memakai RMSE skalalog(zinc). - Penulisan. Judul “Metode Deteministik” pada dokumen asli sebaiknya “Deterministik” (dibiarkan agar teks asli tidak berubah).
Sumber Data dan Referensi
- Data
meuse(paketsp): https://search.r-project.org/CRAN/refmans/sp/html/meuse.html - Paket
spdi CRAN: https://cran.r-project.org/package=sp - Tutorial
gstatdengan data meuse (Pebesma): https://search.r-project.org/CRAN/packages/gstat/vignettes/gstat.pdf - Cressie, N. (1993). Statistics for Spatial Data. Wiley.