Interpolasi Spasial: Data Jura (Logam Berat Tanah)
Data yang digunakan
Dataset jura berisi hasil pengukuran konsentrasi logam
berat pada tanah lapisan atas di wilayah Jura, Swiss, dalam satuan
mg/kg. Dataset ini mencakup beberapa logam (Cd, Co, Cr, Cu, Ni, Pb, dan
Zn), dan variabel yang diinterpolasi dalam analisis ini adalah
konsentrasi seng (Zn). Dataset terdiri dari
jura.pred (259 titik), jura.val (100 titik),
dan jura.grid (grid lokasi prediksi). Karena evaluasi
dilakukan dengan K-fold cross validation, jura.pred dan
jura.val digabung menjadi 359 titik. Koordinat
Xloc dan Yloc berada dalam satuan kilometer
pada sistem koordinat lokal. Konsentrasi Zn ditransformasi dengan
logaritma natural, log(Zn), sehingga semua nilai prediksi, peta, dan
RMSE pada analisis ini berada dalam skala log.
## Warning: package 'gstat' was built under R version 4.5.3
## Warning: package 'sf' was built under R version 4.5.3
## Linking to GEOS 3.14.1, GDAL 3.12.1, PROJ 9.7.1; sf_use_s2() is TRUE
## Warning: package 'terra' was built under R version 4.5.3
## terra 1.9.34
## Warning: package 'tmap' was built under R version 4.5.3
## Warning: package 'viridis' was built under R version 4.5.3
## Loading required package: viridisLite
## Warning: package 'viridisLite' was built under R version 4.5.2
## Warning: package 'dismo' was built under R version 4.5.3
## Loading required package: raster
## Warning: package 'raster' was built under R version 4.5.3
## Loading required package: sp
## Warning: package 'sp' was built under R version 4.5.3
## Warning: package 'dplyr' was built under R version 4.5.2
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:raster':
##
## intersect, select, union
## The following objects are masked from 'package:terra':
##
## intersect, union
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
# Fungsi untuk membantu tampilan peta
tmap_v4 <- utils::packageVersion("tmap") >= "4.0.0"
lay_poly <- function(a) if (tmap_v4) tm_polygons(fill_alpha = a) else tm_polygons(alpha = a)
lay_dots <- function(v) if (tmap_v4) tm_dots(fill = v, fill.scale = tm_scale_continuous(values = "viridis")) else tm_dots(v, palette = "viridis")
lay_fill <- function(v, a) if (tmap_v4) tm_fill(fill = v, fill.scale = tm_scale_continuous(values = "viridis"), fill_alpha = a) else tm_fill(col = v, palette = "viridis", alpha = a)
lay_raster <- function(a) if (tmap_v4) tm_raster(col.scale = tm_scale_continuous(values = "viridis"), col_alpha = a) else tm_raster(alpha = a, palette = "viridis")
lay_judul <- function(t) if (tmap_v4) tm_title(t) else tm_layout(title = t)data(jura) # memuat jura.pred, jura.val, jura.grid
# Pilih variabel yang diinterpolasi
VAR <- "Zn"
LOG_TRANS <- TRUE # transformasi log
dat <- rbind(jura.pred, jura.val)
d <- st_as_sf(dat, coords = c("Xloc", "Yloc"), crs = NA, remove = FALSE)
# Menambahkan kolom variabel yang diinterpolasi ke dalam dataset
d$vble <- if (LOG_TRANS) log(dat[[VAR]]) else dat[[VAR]]
nrow(d)## [1] 359
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 3.219 4.000 4.298 4.254 4.500 5.560
# Batas wilayah
r0 <- rast(xmin = min(jura.grid$Xloc) - 0.025, xmax = max(jura.grid$Xloc) + 0.025,
ymin = min(jura.grid$Yloc) - 0.025, ymax = max(jura.grid$Yloc) + 0.025,
resolution = 0.05)
r0[cellFromXY(r0, as.matrix(jura.grid[, c("Xloc", "Yloc")]))] <- 1
map <- as.polygons(r0, dissolve = TRUE) |> fillHoles() |>
st_as_sf() |> st_set_crs(NA)
# cek apakah semua titik pengamatan berada di dalam batas (TRUE)
sum(lengths(st_intersects(d, map)) > 0) == nrow(d)## [1] TRUE
## ℹ tmap modes "plot" - "view"
## ℹ toggle with `tmap::ttm()`
Untuk membuat prediksi, kita buat grid raster yang mencakup seluruh area dan menggunakan titik pusat grid sebagai lokasi prediksi.
# Membuat grid raster 100x100 sel untuk menutupi wilayah Jura
grid <- rast(map, nrows = 100, ncols = 100)
# Mengambil koordinat titik pusat dari setiap sel dalam grid
xy <- xyFromCell(grid, 1:ncell(grid))
# Membuat objek sf untuk lokasi prediksi
coop <- st_as_sf(as.data.frame(xy), coords = c("x", "y"), crs = st_crs(map))
# Menyaring lokasi prediksi yang berada dalam batas wilayah
coop <- st_filter(coop, map)
qtm(coop)Metode Deterministik
Closest Observation
resp <- coop
resp$pred <- d$vble[st_nearest_feature(coop, d)]
pred <- rasterize(vect(resp), grid, field = "pred", fun = "mean")
c1 <- tm_shape(pred) + lay_raster(0.6) + lay_judul("Closest Observation")
c1
Peta Closest Observation menampilkan permukaan berbentuk petak-petak
poligon (Voronoi) dengan perubahan nilai yang tiba-tiba di batas
antarpetak. Rentang prediksinya paling lebar di antara metode lain (log
Zn sekitar 3,2 sampai 5,6) karena nilai titik pengamatan disalin apa
adanya tanpa perataan, sehingga nilai ekstrem tetap muncul. Karena satu
titik dianggap mewakili seluruh petaknya, metode ini paling sensitif
terhadap variasi lokal.
Invers Distance Weight
# Menerapkan IDW menggunakan semua lokasi titik
res <- gstat(formula = vble ~ 1, locations = d, nmax = nrow(d),
set = list(idp = 1))
# Mendapatkan prediksi untuk lokasi yang belum teramati
resp <- predict(res, coop, debug.level = 0)
resp$x <- st_coordinates(resp)[, 1]
resp$y <- st_coordinates(resp)[, 2]
resp$pred <- resp$var1.pred
pred <- rasterize(vect(resp), grid, field = "pred", fun = "mean")
c2 <- tm_shape(pred) + lay_raster(0.6) + lay_judul("IDW")
c2
Peta IDW menunjukkan kadar log(Zn) didominasi nilai sedang (sekitar 4,2
sampai 4,4), dengan dua area bernilai rendah di bagian tengah-atas dan
kiri-bawah serta beberapa bintik bernilai tinggi yang tersebar. Pola
bintik melingkar di sekitar titik pengamatan merupakan ciri khas IDW,
dan rentang prediksinya lebih sempit daripada data asli karena nilai
ekstrem terhalus oleh perataan jarak.
Nearest Neighbors
res <- gstat(formula = vble ~ 1, locations = d, nmax = 5,
set = list(idp = 0))
resp <- predict(res, coop, debug.level = 0)
resp$x <- st_coordinates(resp)[, 1]
resp$y <- st_coordinates(resp)[, 2]
resp$pred <- resp$var1.pred
pred <- rasterize(vect(resp), grid, field = "pred", fun = "mean")
c3 <- tm_shape(pred) + lay_raster(0.6) + lay_judul("Nearest Neighbors")
c3
Metode Nearest Neighbors menghasilkan pola interpolasi berupa zona-zona
dengan nilai konsentrasi Zn yang relatif serupa. Wilayah dengan
konsentrasi relatif tinggi ditunjukkan oleh warna kuning, sedangkan
konsentrasi relatif rendah ditunjukkan oleh warna ungu. Prediksi
diperoleh dari rata-rata lima titik pengamatan terdekat dengan bobot
yang sama, sehingga pola spasial yang terbentuk dipengaruhi oleh nilai
dan lokasi titik-titik pengamatan di sekitarnya.
Pendekatan Ensembel
Ensembel di bagian ini menggabungkan tiga metode (Closest Observation, IDW, Nearest Neighbors) dengan bobot sama.
# Closest observation (Voronoi)
p1 <- d$vble[st_nearest_feature(coop, d)]
# IDW
gs <- gstat(formula = vble ~ 1, locations = d, nmax = nrow(d),
set = list(idp = 1))
p2 <- predict(gs, coop, debug.level = 0)$var1.pred
# Nearest neighbors
nn <- gstat(formula = vble ~ 1, locations = d, nmax = 5,
set = list(idp = 0))
p3 <- predict(nn, coop, debug.level = 0)$var1.pred
# Ensemble (equal weights)
weights <- c(1/3, 1/3, 1/3)
p4 <- p1 * weights[1] + p2 * weights[2] + p3 * weights[3]
resp <- data.frame(x = st_coordinates(coop)[, 1],
y = st_coordinates(coop)[, 2], pred = p4)
resp <- st_as_sf(resp, coords = c("x", "y"), crs = st_crs(map))
pred <- rasterize(vect(resp), grid, field = "pred", fun = "mean")
c4 <- tm_shape(pred) + lay_raster(0.6) + lay_judul("Ensembel (CLosest Observation + IDW + NN)")
c4## [plot mode] fit legend/component: Some legend items or map compoments do not
## fit well, and are therefore rescaled.
## ℹ Set the tmap option `component.autoscale = FALSE` to disable rescaling.
Peta Ensemble menunjukkan persebaran prediksi konsentrasi Zn yang
merupakan hasil penggabungan tiga metode interpolasi dengan bobot yang
sama. Wilayah dengan konsentrasi Zn relatif tinggi ditunjukkan oleh
warna kuning, terutama di bagian selatan dan beberapa area tengah,
sedangkan konsentrasi relatif rendah ditunjukkan oleh warna ungu,
terutama di bagian tengah-utara dan barat daya. Sementara itu, sebagian
besar wilayah didominasi warna hijau hingga biru kehijauan yang
menunjukkan konsentrasi Zn pada tingkat menengah. Pola yang dihasilkan
memperlihatkan variasi spasial yang merupakan kombinasi karakteristik
ketiga metode, sehingga prediksi tidak hanya bergantung pada titik
pengamatan terdekat, tetapi juga mempertimbangkan pembobotan berdasarkan
jarak.
## [plot mode] fit legend/component: Some legend items or map compoments do not
## fit well, and are therefore rescaled.
## ℹ Set the tmap option `component.autoscale = FALSE` to disable rescaling.
## [plot mode] fit legend/component: Some legend items or map compoments do not
## fit well, and are therefore rescaled.
## ℹ Set the tmap option `component.autoscale = FALSE` to disable rescaling.
## [plot mode] fit legend/component: Some legend items or map compoments do not
## fit well, and are therefore rescaled.
## ℹ Set the tmap option `component.autoscale = FALSE` to disable rescaling.
Evaluasi Model
set.seed(123)
# Fungsi untuk menghitung RMSE
RMSE <- function(observed, predicted) {
sqrt(mean((observed - predicted)^2))
}
# Pembagian data untuk K-fold
kf <- kfold(nrow(d), k = 5)
# Vectors to store the RMSE values obtained with each method
rmse1 <- rep(NA, 5) # Closest observation
rmse2 <- rep(NA, 5) # IDW
rmse3 <- rep(NA, 5) # Nearest neighbors
rmse4 <- rep(NA, 5) # Ensemble
for (k in 1:5) {
# Split data into test and train
test <- d[kf == k, ]
train <- d[kf != k, ]
# Closest observation (nilai titik terdekat = prediksi Voronoi)
p1 <- train$vble[st_nearest_feature(test, train)]
rmse1[k] <- RMSE(test$vble, p1)
# IDW
gs <- gstat(formula = vble ~ 1, locations = train,
nmax = nrow(train), set = list(idp = 1))
p2 <- predict(gs, test, debug.level = 0)$var1.pred
rmse2[k] <- RMSE(test$vble, p2)
# Nearest neighbors
nn <- gstat(formula = vble ~ 1, locations = train,
nmax = 5, set = list(idp = 0))
p3 <- predict(nn, test, debug.level = 0)$var1.pred
rmse3[k] <- RMSE(test$vble, p3)
# Ensemble (weights are inverse RMSE so lower RMSE higher weight)
w <- 1 / c(rmse1[k], rmse2[k], rmse3[k])
weights <- w / sum(w)
p4 <- p1 * weights[1] + p2 * weights[2] + p3 * weights[3]
rmse4[k] <- RMSE(test$vble, p4)
}
# RMSE obtained for each of the 5 splits
data.frame(closest.obs = rmse1, IDW = rmse2,
nearest.neigh = rmse3, ensemble = rmse4)## closest.obs IDW nearest.neigh ensemble
## 1 0.4343861 0.3303143 0.3646094 0.3433597
## 2 0.3469489 0.3561124 0.3206350 0.3104163
## 3 0.4219734 0.3468850 0.3368395 0.3383548
## 4 0.3367056 0.3125789 0.2895793 0.2775792
## 5 0.3130178 0.2761392 0.2829812 0.2511632
# Average RMSE over the 5 splits
data.frame(closest.obs = mean(rmse1), IDW = mean(rmse2),
nearest.neigh = mean(rmse3), ensemble = mean(rmse4))## closest.obs IDW nearest.neigh ensemble
## 1 0.3706064 0.324406 0.3189289 0.3041746
Metode Stokastik
Kriging
## Warning: package 'mapview' was built under R version 4.5.3
# Peta interaktif memakai koordinat lintang-bujur bawaan jura
d_ll <- st_as_sf(dat, coords = c("long", "lat"), crs = 4326)
d_ll$vble <- d$vble
mapview(d_ll, zcol = "vble", map.types = "CartoDB.Voyager")## 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)
# Model variogram awal
MODEL_VGM <- "Sph" # model yang di-fit; coba juga "Exp" lalu bandingkan RMSE
plateau <- mean(tail(v$gamma, 6))
vinitial <- vgm(psill = max(plateau - min(v$gamma), 0.1 * plateau), model = MODEL_VGM,
range = max(v$dist) / 3, nugget = min(v$gamma))
vinitial## model psill range
## 1 Nug 0.05061674 0.0000000
## 2 Sph 0.11025864 0.7308067
# Menyesuaikan (fit) variogram dengan model MODEL_VGM
fv <- fit.variogram(object = v, model = vinitial)
fv## model psill range
## 1 Nug 0.03101508 0.0000000
## 2 Sph 0.12051941 0.6076852
## Warning: package 'automap' was built under R version 4.5.3
## Warning: package 'ggplot2' was built under R version 4.5.3
library(viridis)
# Membuat objek gstat dengan model variogram hasil fitting (fv)
k <- gstat(formula = vble ~ 1, data = d, model = fv)
# Melakukan prediksi pada grid
kpred <- predict(k, coop)## [using ordinary kriging]
# Visualisasi prediksi
ggplot() + geom_sf(data = kpred, aes(color = var1.pred)) +
geom_sf(data = d) +
scale_color_viridis(name = ifelse(LOG_TRANS, paste0("log(", VAR, ")"), VAR)) +
theme_bw()# Visualisasi prediksi varians
ggplot() + geom_sf(data = kpred, aes(color = var1.var)) +
geom_sf(data = d) +
scale_color_viridis(name = "variance") + theme_bw()# Peta raster kriging, dibandingkan dengan metode lain
resp <- kpred
resp$pred <- resp$var1.pred
pred <- rasterize(vect(resp), grid, field = "pred", fun = "mean")
c5 <- tm_shape(pred) + lay_raster(0.6) + lay_judul("Kriging (OK)")
tmap_arrange(c1, c2, c3, c4, c5, ncol = 3)## [plot mode] fit legend/component: Some legend items or map compoments do not
## fit well, and are therefore rescaled.
## ℹ Set the tmap option `component.autoscale = FALSE` to disable rescaling.
## [plot mode] fit legend/component: Some legend items or map compoments do not
## fit well, and are therefore rescaled.
## ℹ Set the tmap option `component.autoscale = FALSE` to disable rescaling.
## [plot mode] fit legend/component: Some legend items or map compoments do not
## fit well, and are therefore rescaled.
## ℹ Set the tmap option `component.autoscale = FALSE` to disable rescaling.
Evaluasi Kriging dan Perbandingan Semua Metode
rmse5 <- rep(NA, 5) # Ordinary kriging
for (k in 1:5) {
test <- d[kf == k, ]
train <- d[kf != k, ]
vt <- variogram(vble ~ 1, train)
plt <- mean(tail(vt$gamma, 6))
init <- function(m) vgm(psill = max(plt - min(vt$gamma), 0.1 * plt), model = m,
range = max(vt$dist) / 3, nugget = min(vt$gamma))
fvt <- tryCatch(fit.variogram(vt, init(MODEL_VGM)), error = function(e) NULL)
if (is.null(fvt) || isTRUE(attr(fvt, "singular"))) fvt <- fit.variogram(vt, init("Exp"))
kr <- gstat(formula = vble ~ 1, data = train, model = fvt)
p5 <- predict(kr, test, debug.level = 0)$var1.pred
rmse5[k] <- RMSE(test$vble, p5)
}
# RMSE per fold, semua metode
data.frame(closest.obs = rmse1, IDW = rmse2, nearest.neigh = rmse3,
ensemble = rmse4, kriging = rmse5)## closest.obs IDW nearest.neigh ensemble kriging
## 1 0.4343861 0.3303143 0.3646094 0.3433597 0.3640644
## 2 0.3469489 0.3561124 0.3206350 0.3104163 0.2969862
## 3 0.4219734 0.3468850 0.3368395 0.3383548 0.3192235
## 4 0.3367056 0.3125789 0.2895793 0.2775792 0.2889410
## 5 0.3130178 0.2761392 0.2829812 0.2511632 0.2447274
# Rata-rata RMSE, semua metode
data.frame(closest.obs = mean(rmse1), IDW = mean(rmse2),
nearest.neigh = mean(rmse3), ensemble = mean(rmse4),
kriging = mean(rmse5))## closest.obs IDW nearest.neigh ensemble kriging
## 1 0.3706064 0.324406 0.3189289 0.3041746 0.3027885
Rata-rata RMSE terkecil dimiliki kriging (0.303), diikuti ensembel (0.304), Nearest Neighbors (0.319), IDW (0.324), dan Closest Observation (0.371). Kriging dan ensembel nyaris setara, sedangkan Closest Observation paling besar galatnya.
Kesimpulan
Model variogram log(Zn) memiliki nugget sebesar 0.031, sill sebesar 0.152, dan range sebesar 0.61 km. Rasio nugget terhadap sill yang mencapai sekitar 20% mengindikasikan adanya ketergantungan spasial yang relatif kuat. Berdasarkan hasil evaluasi menggunakan 5-fold cross validation, metode Ordinary Kriging menunjukkan performa interpolasi terbaik dengan rata-rata RMSE sebesar 0.303, hampir setara dengan ensembel(0.304), lebih rendah dibandingkan Closest Observation (0.371), IDW (0.324), dan Nearest Neighbors (0.319). Hasil ini menunjukkan bahwa pemodelan ketergantungan spasial melalui variogram dapat menghasilkan prediksi konsentrasi Zn yang lebih akurat. Selain itu, Ordinary Kriging juga menyediakan informasi ketidakpastian melalui peta varians kriging sehingga dapat digunakan untuk menilai tingkat keandalan hasil interpolasi pada setiap lokasi.