Este informe analiza la predicción de temperaturas en una región de aguacates utilizando tres métodos de interpolación espacial: Voronoi, IDW (Inverse Distance Weighting) y un modelo de ensamble. Se realiza una validación cruzada de 5 particiones para evaluar la precisión de las predicciones, midiendo el RMSE en cada partición. Los resultados obtenidos se comparan para identificar el modelo más efectivo, y se visualizan mediante mapas y gráficos, mostrando cómo cada técnica captura la variabilidad espacial de la temperatura en el área de estudio.
En esta sección se realiza la configuración inicial necesaria para el
análisis de datos espaciales. Esto incluye la carga de las librerías
requeridas y la importación de un conjunto de datos que contiene
información de temperaturas asociadas a coordenadas geográficas
específicas. Se filtran los datos para una fecha particular
(01/10/2020) y se seleccionan las columnas relevantes:
latitud, longitud y temperatura. Este paso es esencial para preparar los
datos para los análisis espaciales e interpolaciones posteriores.
library(readxl)
library(geoR)
library(ggplot2)
library(sf)
library(sp)
library(gstat)
library(FNN)
library(terra)
library(dismo)
library(tmap)
library(dplyr)
ruta_excel <- "C:/Users/vicod/Documents/Maestria/analisis de datos espaciales y geograficos/Unidad 3/Datos_Completos_Aguacate.xlsx"
datos_aguacate <- read_excel(ruta_excel)
datos_aguacate <- datos_aguacate %>%
filter(grepl("01/10/2020", FORMATTED_DATE_TIME))
datos_aguacate <- datos_aguacate %>%
select(Latitude, Longitude, Temperature)Este código explora los datos y los transforma en un formato
espacial. Se genera un resumen estadístico, un gráfico de dispersión de
las coordenadas, y se convierte el conjunto de datos en un objeto
espacial (sf) con un sistema métrico UTM para facilitar el
análisis. Además, se visualizan las temperaturas en un mapa temático y
se crea un marco de datos con coordenadas reproyectadas para análisis
posteriores.
## Latitude Longitude Temperature
## Min. :2.392 Min. :-76.71 Min. :22.20
## 1st Qu.:2.393 1st Qu.:-76.71 1st Qu.:24.50
## Median :2.393 Median :-76.71 Median :25.80
## Mean :2.393 Mean :-76.71 Mean :25.83
## 3rd Qu.:2.393 3rd Qu.:-76.71 3rd Qu.:27.18
## Max. :2.394 Max. :-76.71 Max. :29.70
plot(
datos_aguacate$Longitude,
datos_aguacate$Latitude,
col = "blue",
pch = 20,
xlab = "Longitude",
ylab = "Latitude",
)datos_sf <- st_as_sf(
datos_aguacate,
coords = c("Longitude", "Latitude"),
crs = 4326 #wgs84
)
datos_sf <- st_transform(datos_sf, crs = 32614)
coords_metros <- st_coordinates(datos_sf)
ggplot(datos_sf) +
geom_sf(aes(color = Temperature), size = 0.9) + # Color según temperatura
scale_color_gradient(low = "blue", high = "orange") + # Gradiente de color
theme_bw() + # Tema limpio para el mapa
labs(
title = "Mapa de temperaturas",
color = "Temperatura"
)En esta sección, los datos de temperatura, con coordenadas reproyectadas, se convierten en un objeto de tipo geoR para realizar análisis geoestadísticos. Se examina la distribución espacial mediante un gráfico de dispersión y se calcula el semivariograma empírico, que cuantifica la dependencia espacial de la variable en función de la distancia entre puntos. Para refinar el análisis, se utiliza un semivariograma agrupado (binned), segmentando las distancias en intervalos definidos. Además, se genera un intervalo de confianza mediante simulaciones de Monte Carlo, lo que permite determinar si la estructura espacial observada es estadísticamente significativa. Los resultados indican la existencia de correlación espacial en los datos, ya que los puntos del semivariograma empírico se encuentran fuera del intervalo generado por las simulaciones, confirmando una relación espacial en la variable analizada.
datos_aguacate_metros <- data.frame(
x = coords_metros[, 1],
y = coords_metros[, 2],
Temperature = datos_aguacate$Temperature
)
datos_g <- as.geodata(datos_aguacate_metros, coords.col = 1:2, data.col = 3)
plot(datos_g)## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 2.046 48.562 76.796 81.797 109.955 234.895
## variog: computing omnidirectional variogram
## variog: computing omnidirectional variogram
## variog.env: generating 99 simulations by permutating data values
## variog.env: computing the empirical variogram for the 99 simulations
## variog.env: computing the envelops
Se ajustan tres modelos teóricos (exponencial, gaussiano y esférico) al semivariograma empírico mediante mínimos cuadrados ordinarios. Los modelos se visualizan en el gráfico del semivariograma con curvas de diferentes colores: rojo para el modelo exponencial, azul para el gaussiano y verde para el esférico. Al evaluar los resultados mediante la suma de cuadrados de los errores, el modelo exponencial (rojo) es el que mejor se ajusta a los datos, ya que presenta la menor suma de cuadrados, lo que lo convierte en el modelo seleccionado para representar la estructura espacial de los datos.
plot(variograma)
ini.vals = expand.grid(seq(2,4,l=0.3),seq(20,80,l =3))
m_mco_exp = variofit(variograma, ini = ini.vals, cov.model ="exp")## variofit: covariance model used is exponential
## variofit: weights used: npairs
## variofit: minimisation function used: optim
## variofit: searching for best initial value ... selected values:
## sigmasq phi tausq kappa
## initial.value "2" "20" "0" "0.5"
## status "est" "est" "est" "fix"
## loss value: 166355.481792341
## variofit: covariance model used is gaussian
## variofit: weights used: npairs
## variofit: minimisation function used: optim
## variofit: searching for best initial value ... selected values:
## sigmasq phi tausq kappa
## initial.value "2" "20" "0" "0.5"
## status "est" "est" "est" "fix"
## loss value: 148966.844805868
## variofit: covariance model used is spherical
## variofit: weights used: npairs
## variofit: minimisation function used: optim
## variofit: searching for best initial value ... selected values:
## sigmasq phi tausq kappa
## initial.value "2" "20" "0" "0.5"
## status "est" "est" "est" "fix"
## loss value: 130279.742164586
## value
## 2309.283
## value
## 3656.729
## value
## 3089.164
## $pmethod
## [1] "WLS (weighted least squares)"
##
## $cov.model
## [1] "exponential"
##
## $spatial.component
## sigmasq phi
## 1.958089 35.419481
##
## $spatial.component.extra
## kappa
## 0.5
##
## $nugget.component
## tausq
## 1.478306
##
## $fix.nugget
## [1] FALSE
##
## $fix.kappa
## [1] TRUE
##
## $practicalRange
## [1] 106.1073
##
## $sum.of.squares
## value
## 2309.283
##
## $estimated.pars
## tausq sigmasq phi
## 1.478306 1.958089 35.419481
##
## $weights
## [1] "npairs"
##
## $call
## variofit(vario = variograma, ini.cov.pars = ini.vals, cov.model = "exp")
##
## attr(,"class")
## [1] "summary.variomodel"
Se crea una grilla de predicción de 100x100 puntos en el área de estudio, basada en el rango de las coordenadas de las muestras. Luego, se ajusta el modelo exponencial al semivariograma y se aplica el método de Kriging para predecir los valores de temperatura en cada punto de la grilla. El resultado se visualiza en un mapa que muestra la distribución espacial de la temperatura interpolada, con contornos que indican áreas de igual valor.
x_range <- range(coords_metros[, 1])
y_range <- range(coords_metros[, 2])
datos_grid <- expand.grid(
Este = seq(x_range[1], x_range[2], length.out = 100),
Norte = seq(y_range[1], y_range[2], length.out = 100)
)
m_exp <- variofit(variograma, ini = ini.vals, cov.model = "exp")## variofit: covariance model used is exponential
## variofit: weights used: npairs
## variofit: minimisation function used: optim
## variofit: searching for best initial value ... selected values:
## sigmasq phi tausq kappa
## initial.value "2" "20" "0" "0.5"
## status "est" "est" "est" "fix"
## loss value: 166355.481792341
kriging_result <- krige.conv(
datos_g,
loc = datos_grid,
krige = krige.control(
trend.d = "cte",
trend.l = "cte",
cov.model = "exponential",
cov.pars = m_exp$cov.pars,
nugget = m_exp$nugget
)
)## krige.conv: model with constant mean
## krige.conv: Kriging performed using global neighbourhood
image(
kriging_result,
main = "Prediccin por Kriging",
xlab = "Est",
ylab = "Nort",
col = terrain.colors(100)
)
contour(
kriging_result, add = TRUE, col = "black"
)En este fragmento de código, se crea un objeto espacial
vect a partir de las coordenadas de los datos de
temperatura. Luego, se define el área de estudio y se generan las celdas
de Voronoi utilizando las coordenadas como puntos de referencia. Las
celdas de Voronoi dividen el espacio en regiones, donde cada celda
corresponde a la zona más cercana a un punto de muestreo. A
continuación, se extraen las temperaturas correspondientes a cada celda
y se visualizan, con las celdas de Voronoi coloreadas según la
temperatura. Además, se superponen los puntos originales sobre el mapa
para mejor referencia visual.
d <- data.frame(
x = datos_aguacate_metros$x,
y = datos_aguacate_metros$y,
temperature = datos_aguacate_metros$Temperature
)
d_vect <- terra::vect(d, c("x", "y"))
bnd <- terra::ext(min(d$x), max(d$x), min(d$y), max(d$y))
v <- terra::voronoi(d_vect, bnd = bnd)
v$temperature <- terra::extract(v, d_vect)$temperature
plot(v, col = terrain.colors(100)[as.numeric(cut(v$temperature, breaks = 100))])
points(d_vect, cex = 0.5, col = "black") En este fragmento de código, se realiza una interpolación espacial
utilizando el método de IDW (Invers Distance
Weighting). Primero, se crea un modelo IDW usando el paquete
gstat, con la temperatura como variable dependiente y las
coordenadas como variables explicativas. Luego, se genera una cuadrícula
de predicción y se aplica el modelo IDW para predecir la temperatura en
esos puntos. Los resultados se convierten en un SpatRaster
para visualización, y se utiliza tmap para mostrar la
predicción en un mapa, donde las temperaturas estimadas se visualizan
con una paleta de colores.
idw_model <- gstat(
formula = temperature ~ 1,
locations = ~ x + y,
data = d,
nmax = 6,
set = list(idp = 3)
)
x_range <- seq(min(d$x), max(d$x), length.out = 100)
y_range <- seq(min(d$y), max(d$y), length.out = 100)
grid <- expand.grid(x = x_range, y = y_range)
coordinates(grid) <- ~ x + y
gridded(grid) <- TRUE
pred_idw <- predict(idw_model, newdata = grid)## [inverse distance weighted interpolation]
resp <- as.data.frame(pred_idw)
resp$x <- coordinates(grid)[, 1]
resp$y <- coordinates(grid)[, 2]
resp$pred <- resp$var1.pred
pred_raster <- rast(resp, type = "xyz", crs = st_crs(datos_sf)$proj4string)
terra::nlyr(pred_raster)## [1] 3
pred_raster_single <- pred_raster[["pred"]]
tm_shape(pred_raster_single) +
tm_raster(palette = "viridis", alpha = 0.6, title = "Prediccion (IDW)") +
tm_layout(main.title = "Interpolacion por IDW", legend.outside = TRUE)idp = 0)En este fragmento de código, se utiliza el método de interpolación
IDW (Invers Distance Weighting) con el parámetro
idp = 0, lo que indica que la predicción de la temperatura
en cada punto se realiza utilizando solo los vecinos más
cercanos, sin ponderación por la distancia. Se define un modelo
IDW con nmax = 4, limitando el número de vecinos a 4. A
continuación, se realiza la predicción sobre una cuadrícula de puntos
generada previamente, y los resultados se visualizan en un mapa usando
tmap, donde las temperaturas estimadas se representan
mediante una paleta de colores.
vecinos <- gstat(
formula = temperature ~ 1,
locations = ~ x + y,
data = d,
nmax = 4,
set = list(idp = 0)
)
pred_vecinos <- predict(vecinos, newdata = grid)## [inverse distance weighted interpolation]
resp <- as.data.frame(pred_vecinos)
resp$x <- coordinates(grid)[, 1]
resp$y <- coordinates(grid)[, 2]
resp$pred <- resp$var1.pred
pred_raster <- rast(resp, type = "xyz", crs = st_crs(datos_sf)$proj4string)
terra::nlyr(pred_raster)## [1] 3
pred_raster_single <- pred_raster[["var1.pred"]]
tm_shape(pred_raster_single) +
tm_raster(palette = "viridis", alpha = 0.6, title = "Prediccin (vecinos)") +
tm_layout(main.title = "Interpolacin por vecinos", legend.outside = TRUE)En este código se realiza una validación cruzada
5-fold para comparar el rendimiento de diferentes métodos de
interpolación (Voronoi, IDW con idp = 3, IDW con
idp = 0, y un ensamble de estos). En cada iteración, se
calcula el RMSE para evaluar la precisión de las
predicciones, comparando las temperaturas estimadas con las observadas.
Además, para el ensamble, se combinan las predicciones de los tres
métodos, ponderadas por su RMSE, para obtener una estimación final. Esto
permite identificar el modelo que ofrece la mejor predicción de la
temperatura.
RMSE <- function(observed, predicted) {
sqrt(mean((observed - predicted)^2))
}
set.seed(123)
kf <- dismo::kfold(nrow(d), k = 5)
rmse_voronoi <- rep(NA, 5)
rmse_idw_1 <- rep(NA, 5)
rmse_idw_0 <- rep(NA, 5)
rmse_ensemble <- rep(NA, 5)
for (k in 1:5) {
test <- d[kf == k, ]
train <- d[kf != k, ]
train_vect <- terra::vect(train, c("x", "y"))
v <- terra::voronoi(train_vect, bnd = terra::ext(min(d$x), max(d$x), min(d$y), max(d$y)))
v$temperature <- terra::extract(v, terra::vect(test, c("x", "y")))
p1 <- v$temperature
rmse_voronoi[k] <- RMSE(test$temperature, p1)
idw_model_1 <- gstat(
formula = temperature ~ 1,
locations = ~ x + y,
data = train,
nmax = 6,
set = list(idp = 3)
)
p2 <- predict(idw_model_1, newdata = test)$var1.pred
rmse_idw_1[k] <- RMSE(test$temperature, p2)
idw_model_0 <- gstat(
formula = temperature ~ 1,
locations = ~ x + y,
data = train,
nmax = 6,
set = list(idp = 0)
)
p3 <- predict(idw_model_0, newdata = test)$var1.pred
rmse_idw_0[k] <- RMSE(test$temperature, p3)
# Ensamble
w <- 1 / c(rmse_voronoi[k], rmse_idw_1[k], rmse_idw_0[k])
weights <- w / sum(w)
p4 <- p1 * weights[1] + p2 * weights[2] + p3 * weights[3]
rmse_ensemble[k] <- RMSE(test$temperature, p4)
}En esta parte del código, se calcula el promedio del
RMSE sobre las 5 particiones de la validación cruzada para cada
uno de los métodos de interpolación (Voronoi, IDW con
idp = 1, IDW con idp = 0, y el modelo de
ensamble). Estos valores proporcionan una evaluación general del
desempeño de cada método. Los resultados se presentan en un data
frame, mostrando la media del RMSE para cada técnica y
permitiendo comparar su efectividad en la predicción de las
temperaturas.
# Resultados del RMSE obtenidos para cada uno de los 5 splits
resultados_rmse <- data.frame(
Voronoi = rmse_voronoi,
IDW_idp3 = rmse_idw_1,
IDW_idp0 = rmse_idw_0,
Ensamble = rmse_ensemble
)
# Promedio de RMSE sobre las 5 particiones
promedio_rmse <- data.frame(
Voronoi = mean(rmse_voronoi),
IDW_idp3 = mean(rmse_idw_1),
IDW_idp0 = mean(rmse_idw_0),
Ensamble = mean(rmse_ensemble)
)
# Mostrar los resultados
print(resultados_rmse)## Voronoi IDW_idp3 IDW_idp0 Ensamble
## 1 41.19481 1.091162 1.136826 1.316229
## 2 41.52005 1.069569 1.127216 1.202038
## 3 40.48358 1.126595 1.115893 1.143574
## 4 41.24370 1.101768 1.056408 1.143588
## 5 41.27278 1.259519 1.304061 1.415685
## Voronoi IDW_idp3 IDW_idp0 Ensamble
## 1 41.14298 1.129723 1.148081 1.244223
Los resultados del análisis de RMSE sobre las 5 particiones de la validación cruzada muestran cómo se desempeñan los distintos métodos de interpolación. Los valores promedio de RMSE para cada técnica son los siguientes:
De estos resultados, podemos concluir que el método Voronoi presenta un RMSE significativamente más alto, lo que indica que su desempeño en la predicción de temperaturas es inferior en comparación con los métodos IDW y el modelo Ensamble.
Entre los métodos IDW, el que utiliza idp = 3 (distancia ponderada inversamente) tiene un rendimiento ligeramente mejor (RMSE = 1.13) que el que utiliza idp = 0 (vecinos más cercanos), aunque la diferencia es pequeña.
El modelo de ensamble, que combina los tres métodos, presenta un RMSE de 1.24, lo que sugiere que no mejora significativamente la precisión sobre los modelos individuales en este caso.
En resumen, los métodos IDW parecen ser más precisos que el Voronoi, y el uso del Ensamble no proporciona una mejora clara en comparación con la selección de un solo modelo como el IDW con idp = 3, esto puede deberse al bajo desempeño del modelo voronoi que afecta significativamente el ensamble.