Interpolasi Titik pada Data meuse
Data
## 'data.frame': 155 obs. of 3 variables:
## $ x : num 181072 181025 181165 181298 181307 ...
## $ y : num 333611 333558 333537 333484 333330 ...
## $ zinc: num 1022 1141 640 257 269 ...
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 113.0 198.0 326.0 469.7 674.5 1839.0
coordinates(meuse) <- ~x + y
coordinates(meuse.grid) <- ~x + y
gridded(meuse.grid) <- TRUE
bubble(meuse, "zinc", col = "steelblue", main = "Sebaran Zinc (ppm) pada Titik Sampel")Fungsi Metode Interpolasi
Setiap metode dibuat sebagai fungsi dengan input train
(data latih) dan test (lokasi yang diprediksi). Dengan
begitu fungsi yang sama dapat dipakai untuk membuat peta prediksi maupun
untuk cross validation.
Closest Observation
Setiap lokasi diberi nilai dari satu titik pengamatan yang jaraknya paling dekat (setara poligon Voronoi/Thiessen).
Nearest Neighbour (NN)
Nilai prediksi adalah rata-rata dari k = 5 titik
terdekat (bobot sama, sehingga idp = 0).
Inverse Distance Weighting (IDW)
Titik yang lebih dekat diberi bobot lebih besar, dengan bobot \(w_i \propto 1/d_i^{p}\). Di sini digunakan pangkat \(p = 2\).
Kriging
Ordinary Kriging membutuhkan model variogram. Berikut variogram empiris dan model yang difit pada seluruh data.
v_emp <- variogram(zinc ~ 1, meuse)
v_fit <- fit.variogram(
v_emp,
model = vgm(psill = var(meuse$zinc) * 0.7, model = "Sph",
range = 900, nugget = var(meuse$zinc) * 0.3)
)
v_fit## model psill range
## 1 Nug 24799.13 0.0000
## 2 Sph 134743.94 830.9164
Pada cross validation, variogram dihitung ulang di setiap fold hanya dari data latih, agar tidak terjadi kebocoran informasi dari data uji.
pred_krige <- function(train, test) {
v <- variogram(zinc ~ 1, train)
m <- tryCatch(
suppressWarnings(fit.variogram(
v, vgm(psill = var(train$zinc) * 0.7, "Sph", range = 900,
nugget = var(train$zinc) * 0.3))),
error = function(e) NULL
)
if (is.null(m)) m <- v_fit # cadangan bila fitting gagal
krige(zinc ~ 1, locations = train, newdata = test,
model = m, debug.level = 0)$var1.pred
}Ensemble
Ensemble menggabungkan prediksi beberapa metode. Pada tugas ini digunakan tiga metode: NN, IDW, dan Kriging. Terdapat dua versi:
- Ensemble rata-rata: rata-rata sederhana ketiga prediksi.
- Ensemble berbobot: bobot sebanding dengan \(1/\mathrm{RMSE}^2\) tiap metode (bobot dihitung dari hasil cross validation, lihat bagian berikutnya).
Peta Hasil Interpolasi
Seluruh data digunakan sebagai data latih untuk memprediksi
meuse.grid.
grid_pred <- meuse.grid
grid_pred$Closest <- pred_closest(meuse, meuse.grid)
grid_pred$NN <- pred_nn(meuse, meuse.grid)
grid_pred$IDW <- pred_idw(meuse, meuse.grid)
grid_pred$Kriging <- pred_krige(meuse, meuse.grid)
grid_pred$Ensemble <- (grid_pred$NN + grid_pred$IDW + grid_pred$Kriging) / 3
rng <- range(meuse$zinc)
spplot(grid_pred,
zcol = c("Closest", "NN", "IDW", "Kriging", "Ensemble"),
col.regions = hcl.colors(50, "YlOrRd", rev = TRUE),
at = seq(rng[1], rng[2], length.out = 51),
main = "Interpolasi Zinc (ppm) Berdasarkan Berbagai Metode")
* Closest Observation menghasilkan permukaan berblok-blok
(poligon Voronoi) dan tidak mulus. * NN (k = 5) lebih halus,
tetapi masih terlihat berundak. * IDW mulus namun cenderung
membentuk pola “mata sapi” di sekitar titik sampel. * Kriging
menghasilkan permukaan yang halus dan memanfaatkan struktur autokorelasi
spasial. * Ensemble merupakan kompromi dari ketiga metode.
Cross Validation
Digunakan 5-fold cross validation. Titik dibagi acak menjadi 5 kelompok. Secara bergantian satu kelompok menjadi data uji dan empat kelompok lainnya menjadi data latih. Fold yang sama dipakai untuk semua metode agar perbandingannya adil.
\[RMSE = \sqrt{\frac{1}{n}\sum_{i=1}^{n}(\hat{z}_i - z_i)^2}\]
set.seed(123)
n_fold <- 5
folds <- sample(rep(1:n_fold, length.out = nrow(meuse)))
rmse <- function(obs, pred) sqrt(mean((obs - pred)^2))
cv_pred <- data.frame(obs = meuse$zinc,
Closest = NA_real_, NN = NA_real_,
IDW = NA_real_, Kriging = NA_real_)
for (f in 1:n_fold) {
train <- meuse[folds != f, ]
test <- meuse[folds == f, ]
cv_pred$Closest[folds == f] <- pred_closest(train, test)
cv_pred$NN[folds == f] <- pred_nn(train, test)
cv_pred$IDW[folds == f] <- pred_idw(train, test)
cv_pred$Kriging[folds == f] <- pred_krige(train, test)
}
# Ensemble rata-rata (NN, IDW, Kriging)
cv_pred$Ensemble_Rata2 <- rowMeans(cv_pred[, c("NN", "IDW", "Kriging")])
head(cv_pred)## obs Closest NN IDW Kriging Ensemble_Rata2
## 1 1022 1141 558.0 807.7687 884.0847 749.9511
## 2 1141 1022 684.2 754.4514 882.2215 773.6243
## 3 640 1022 636.8 689.1782 728.1113 684.6965
## 4 257 269 636.8 464.1078 496.1365 532.3481
## 5 269 281 237.8 347.7180 255.6693 280.3958
## 6 281 269 253.2 345.0371 278.9348 292.3906
Ensemble berbobot dibentuk dari RMSE ketiga metode penyusunnya:
rmse_base <- sapply(c("NN", "IDW", "Kriging"),
function(m) rmse(cv_pred$obs, cv_pred[[m]]))
w <- (1 / rmse_base^2) / sum(1 / rmse_base^2)
round(w, 3)## NN IDW Kriging
## 0.308 0.286 0.406
Perbandingan RMSE
metode <- c("Closest", "NN", "IDW", "Kriging",
"Ensemble_Rata2", "Ensemble_Berbobot")
hasil <- data.frame(
Metode = metode,
RMSE = sapply(metode, function(m) rmse(cv_pred$obs, cv_pred[[m]])),
MAE = sapply(metode, function(m) mean(abs(cv_pred$obs - cv_pred[[m]]))),
R2 = sapply(metode, function(m) cor(cv_pred$obs, cv_pred[[m]])^2),
row.names = NULL
)
hasil <- hasil[order(hasil$RMSE), ]
hasil$Peringkat <- seq_len(nrow(hasil))
kable(hasil, digits = 2, row.names = FALSE,
caption = "Perbandingan performa metode interpolasi (5-fold CV)")| Metode | RMSE | MAE | R2 | Peringkat |
|---|---|---|---|---|
| Kriging | 233.29 | 161.81 | 0.60 | 1 |
| Ensemble_Berbobot | 248.41 | 174.44 | 0.56 | 2 |
| Ensemble_Rata2 | 251.43 | 177.06 | 0.55 | 3 |
| NN | 267.83 | 182.46 | 0.47 | 4 |
| IDW | 278.16 | 207.16 | 0.49 | 5 |
| Closest | 351.95 | 220.69 | 0.29 | 6 |
hs <- hasil[order(hasil$RMSE, decreasing = TRUE), ]
par(mar = c(4, 10, 3, 1))
bp <- barplot(hs$RMSE, names.arg = hs$Metode, horiz = TRUE, las = 1,
col = ifelse(hs$RMSE == min(hs$RMSE), "darkgreen", "grey70"),
xlab = "RMSE (ppm)", main = "RMSE Cross Validation per Metode",
xlim = c(0, max(hs$RMSE) * 1.15))
text(hs$RMSE, bp, labels = round(hs$RMSE, 1), pos = 4, cex = 0.9)Plot nilai observasi vs prediksi:
par(mfrow = c(2, 3), mar = c(4, 4, 3, 1))
for (m in metode) {
plot(cv_pred$obs, cv_pred[[m]], pch = 16, col = rgb(0, 0, 1, 0.5),
xlab = "Observasi", ylab = "Prediksi",
main = sprintf("%s\nRMSE = %.1f", m, rmse(cv_pred$obs, cv_pred[[m]])))
abline(0, 1, col = "red", lty = 2)
}Kesimpulan
best <- hasil$Metode[1]
worst <- hasil$Metode[nrow(hasil)]
cat(sprintf(
"Berdasarkan 5-fold cross validation, metode dengan **RMSE terkecil** adalah **%s** (RMSE = %.2f ppm), sedangkan metode dengan RMSE terbesar adalah **%s** (RMSE = %.2f ppm).",
best, hasil$RMSE[1], worst, hasil$RMSE[nrow(hasil)]))Berdasarkan 5-fold cross validation, metode dengan RMSE terkecil adalah Kriging (RMSE = 233.29 ppm), sedangkan metode dengan RMSE terbesar adalah Closest (RMSE = 351.95 ppm).