Introducción

Este informe presenta un modelo de regresión multivariable aplicado al análisis de deslizamientos.
Se integran variables geográficas, climáticas y demográficas para estimar el tamaño del deslizamiento y evaluar escenarios predictivos.

1. CARGA DE LIBRERÍAS Y DATOS

# ============================================================
# 1. INSTALACIÓN Y CARGA DE LIBRERÍAS
# ============================================================

paquetes <- c("dplyr", "httr", "jsonlite",
              "scatterplot3d", "knitr")

for (p in paquetes) {
  if (!require(p, character.only = TRUE)) {
    install.packages(p)
    library(p, character.only = TRUE)
  }
}
## Loading required package: dplyr
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
## Loading required package: httr
## Loading required package: jsonlite
## Loading required package: scatterplot3d
## Loading required package: knitr
cat("Librerías cargadas correctamente.\n")
## Librerías cargadas correctamente.
# ============================================================
# 2. CARGA DEL DATASET
# ============================================================

file_name <- "regresiones.csv"

if (!file.exists(file_name)) {
  stop("ERROR: El archivo 'regresiones.csv' no está en el directorio.")
}

datos <- read.csv(file_name,
                  sep = ",",
                  stringsAsFactors = FALSE)

names(datos) <- tolower(names(datos))

cat("Dataset cargado correctamente.\n")
## Dataset cargado correctamente.

2. LIMPIEZA Y VALIDACIÓN

# ============================================================
# 2. LIMPIEZA Y VALIDACIÓN
# ============================================================

datos$latitude  <- as.numeric(datos$latitude)
datos$longitude <- as.numeric(datos$longitude)
datos$admin_division_population <-
  as.numeric(datos$admin_division_population)

datos$event_date <- as.Date(datos$event_date,
                            format = "%Y-%m-%d")

datos <- datos %>%
  filter(!is.na(latitude),
         !is.na(longitude),
         !is.na(event_date),
         latitude >= -90 & latitude <= 90,
         longitude >= -180 & longitude <= 180)

cat("Datos limpiados correctamente.\n")
## Datos limpiados correctamente.

3. FUNCIÓN DE DESCARGA CLIMÁTICA

# ============================================================
# 3. FUNCIÓN PARA DESCARGA CLIMÁTICA
# ============================================================

fetch_weather <- function(lat, lon, date_str) {
  
  if (is.na(lat) || is.na(lon) || is.na(date_str)) {
    return(c(NA, NA))
  }
  
  date <- as.character(date_str)
  
  url <- paste0(
    "https://archive-api.open-meteo.com/v1/archive?",
    "latitude=", lat,
    "&longitude=", lon,
    "&start_date=", date,
    "&end_date=", date,
    "&daily=precipitation_sum,relative_humidity_2m_max"
  )
  
  tryCatch({
    
    res <- GET(url, timeout(10))
    
    if (status_code(res) != 200) {
      return(c(NA, NA))
    }
    
    cont <- fromJSON(content(res, "text", encoding = "UTF-8"))
    
    if (is.null(cont$daily)) {
      return(c(NA, NA))
    }
    
    c(cont$daily$precipitation_sum[1],
      cont$daily$relative_humidity_2m_max[1])
    
  }, error = function(e) {
    c(NA, NA)
  })
}

4. INTEGRACIÓN DE DATOS CLIMÁTICOS

# ============================================================
# 4. DESCARGA DE CLIMA (100 REGISTROS PARA ESTABILIDAD)
# ============================================================

df_demo <- head(datos, 100)

clima_list <- vector("list", nrow(df_demo))

for (i in seq_len(nrow(df_demo))) {
  
  if (i %% 10 == 0) {
    cat("Descargando clima:", i, "/", nrow(df_demo), "\n")
  }
  
  clima_list[[i]] <- fetch_weather(
    df_demo$latitude[i],
    df_demo$longitude[i],
    df_demo$event_date[i]
  )
}
## Descargando clima: 10 / 100 
## Descargando clima: 20 / 100 
## Descargando clima: 30 / 100 
## Descargando clima: 40 / 100 
## Descargando clima: 50 / 100 
## Descargando clima: 60 / 100 
## Descargando clima: 70 / 100 
## Descargando clima: 80 / 100 
## Descargando clima: 90 / 100 
## Descargando clima: 100 / 100
clima_df <- as.data.frame(do.call(rbind, clima_list))
colnames(clima_df) <- c("precip_real", "hum_real")

df_demo <- cbind(df_demo, clima_df)

df_demo <- df_demo %>%
  filter(!is.na(precip_real),
         !is.na(hum_real))

cat("Datos climáticos integrados.\n")
## Datos climáticos integrados.

5. GENERACIÓN DE VARIABLES

# ============================================================
# 5. GENERACIÓN DE VARIABLES SINTÉTICAS
# ============================================================

set.seed(42)

n <- nrow(df_demo)

noise <- rnorm(n, 0, 18)

pop <- df_demo$admin_division_population
pop[is.na(pop)] <- median(pop, na.rm = TRUE)

if(max(pop) == min(pop)){
  pop_norm <- rep(0.5, length(pop))
} else {
  pop_norm <- (pop - min(pop)) /
    (max(pop) - min(pop))
}

df_demo$pop_norm <- pop_norm

df_demo$size_linear <-
  2.4 * df_demo$precip_real +
  125 + noise

cat("Variables sintéticas creadas.\n")
## Variables sintéticas creadas.

6. REGRESIÓN MÚLTIPLE

# ============================================================
# 6. REGRESIÓN MÚLTIPLE
# ============================================================

modelo_multi <- lm(size_linear ~
                     precip_real +
                     hum_real +
                     pop_norm,
                   data = df_demo)

cat("\nResumen del modelo:\n")
## 
## Resumen del modelo:
print(summary(modelo_multi))
## 
## Call:
## lm(formula = size_linear ~ precip_real + hum_real + pop_norm, 
##     data = df_demo)
## 
## Residuals:
##    Min     1Q Median     3Q    Max 
## -51.79 -12.49   1.26  12.63  41.25 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept) 159.9548    20.1705   7.930 3.99e-12 ***
## precip_real   2.3069     0.2740   8.420 3.64e-13 ***
## hum_real     -0.3730     0.2238  -1.667   0.0988 .  
## pop_norm      8.6444    18.5337   0.466   0.6420    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 18.67 on 96 degrees of freedom
## Multiple R-squared:  0.4284, Adjusted R-squared:  0.4105 
## F-statistic: 23.98 on 3 and 96 DF,  p-value: 1.147e-11

7. TABLA RESUMEN

# ============================================================
# 7. TABLA RESUMEN
# ============================================================

tabla_intervalos <- data.frame(
  Min_Precip = min(df_demo$precip_real),
  Max_Precip = max(df_demo$precip_real),
  Min_Hum    = min(df_demo$hum_real),
  Max_Hum    = max(df_demo$hum_real),
  Min_Size   = min(df_demo$size_linear),
  Max_Size   = max(df_demo$size_linear),
  N          = nrow(df_demo)
)

kable(tabla_intervalos,
      format = "markdown",
      caption = "Resumen estadístico del modelo")
Resumen estadístico del modelo
Min_Precip Max_Precip Min_Hum Max_Hum Min_Size Max_Size N
0 44.7 60 100 81.54426 226.7605 100

8. VISUALIZACIÓN 3D DEL MODELO

# Verificar que las columnas existan
if(!all(c("precip_real","hum_real","size_linear") %in% names(df_demo))){
  stop("Las variables climáticas no existen. Ejecuta correctamente la sección 5.")
}

# Filtrar datos válidos
df_plot <- df_demo %>%
  filter(!is.na(precip_real),
         !is.na(hum_real),
         !is.na(size_linear))

# Verificar que haya datos
if(nrow(df_plot) < 5){
  stop("No hay suficientes datos válidos para graficar.")
}

# Modelo 2D solo para visualización
modelo_2d <- lm(size_linear ~ precip_real + hum_real,
                data = df_plot)

x1 <- df_plot$precip_real
x2 <- df_plot$hum_real
y_obs <- df_plot$size_linear

library(scatterplot3d)

8.1 Plano rotado – Ángulo 320°

grafico3d <- scatterplot3d(
  x1, x2, y_obs,
  main = "Plano de regresión:  Tamaño correlacionado con la Precipitación y Humedad",
  xlab = "Precipitación (mm)",
  ylab = "Humedad (%)",
  zlab = "Tamaño del deslizamiento",
  angle = 320,
  color = "darkblue",
  pch = 16
)

grafico3d$plane3d(modelo_2d,
                  draw_polygon = TRUE,
                  polygon_args = list(col = rgb(0.2,0.6,0.8,0.4)))

8.2 Plano rotado – Ángulo 250°

grafico3d <- scatterplot3d(
  x1, x2, y_obs,
  main = "Plano de regresión: Tamaño correlacionado con la Precipitación y Humedad",
  xlab = "Precipitación (mm)",
  ylab = "Humedad (%)",
  zlab = "Tamaño del deslizamiento",
  angle = 250,
  color = "darkblue",
  pch = 16
)

grafico3d$plane3d(modelo_2d,
                  draw_polygon = TRUE,
                  polygon_args = list(col = rgb(0.2,0.6,0.8,0.4)))

8.3 Plano rotado – Ángulo 199°

grafico3d <- scatterplot3d(
  x1, x2, y_obs,
  main = "Plano de regresión:  Tamaño correlacionado con la Precipitación y Humedad",
  xlab = "Precipitación (mm)",
  ylab = "Humedad (%)",
  zlab = "Tamaño del deslizamiento",
  angle = 199,
  color = "darkblue",
  pch = 16
)

grafico3d$plane3d(modelo_2d,
                  draw_polygon = TRUE,
                  polygon_args = list(col = rgb(0.2,0.6,0.8,0.4)))

# 9. ESCENARIO PREDICTIVO

# ============================================================
# 9. CÁLCULO DE ESCENARIO PREDICTIVO (CORREGIDO)
# ============================================================

# Cambiamos "modelo_plot" por "modelo_multi" que es el que definiste en la sección 7
if(!exists("modelo_multi")){
  stop("El modelo no existe. Ejecuta primero la sección de regresión.")
}

# ================================
# Ecuación del modelo
# ================================

# Usamos modelo_multi que contiene las 3 variables (precip, hum, pop)
coeficientes <- coef(modelo_multi)

b0 <- coeficientes[1]                # Intercepto
b1 <- coeficientes["precip_real"]
b2 <- coeficientes["hum_real"]
b3 <- coeficientes["pop_norm"]

cat("\nEcuación del modelo estimado:\n")
## 
## Ecuación del modelo estimado:
cat(paste("Size_linear =", 
          round(b0,4), 
          "+", round(b1,4), "* Precipitación", 
          "+", round(b2,4), "* Humedad", 
          "+", round(b3,4), "* Población_normalizada\n\n"))
## Size_linear = 159.9548 + 2.3069 * Precipitación + -0.373 * Humedad + 8.6444 * Población_normalizada
# ================================
# Pregunta aplicada
# ================================

cat("¿Cuál será el tamaño estimado del deslizamiento si:\n")
## ¿Cuál será el tamaño estimado del deslizamiento si:
cat("Precipitación = 75 mm\n")
## Precipitación = 75 mm
cat("Humedad = 85 %\n")
## Humedad = 85 %
cat("Población normalizada = 0.7 ?\n\n")
## Población normalizada = 0.7 ?
x1_new <- 75
x2_new <- 85
x3_new <- 0.7

# Cálculo manual basado en la ecuación
y_new <- b0 + (b1 * x1_new) + (b2 * x2_new) + (b3 * x3_new)

cat(paste("Respuesta: Tamaño estimado =", 
          round(y_new,4), "\n"))
## Respuesta: Tamaño estimado = 307.3192

10. INTERPRETACIÓN DEL RESULTADO

# ============================================================
# 10. INTERPRETACIÓN DEL TAMAÑO EN PALABRAS
# ============================================================

interpretar_tamano <- function(valor){
  
  if (is.na(valor)) return("Error: Valor no calculado.")
  
  if (valor < 180) {
    return("Deslizamiento pequeño: impacto localizado y bajo riesgo estructural.")
    
  } else if (valor >= 180 && valor < 260) {
    return("Deslizamiento moderado: posible afectación de infraestructura menor.")
    
  } else if (valor >= 260 && valor < 340) {
    return("Deslizamiento grande: alta probabilidad de daños significativos.")
    
  } else {
    return("Deslizamiento severo o crítico: riesgo elevado para infraestructura y población.")
  }
}

# Aplicar interpretación usando y_new
categoria <- interpretar_tamano(y_new)
cat("\nInterpretación del tamaño estimado:\n")
## 
## Interpretación del tamaño estimado:
cat(paste("Respuesta: Tamaño estimado =", 
          round(y_new,4), "\n"))
## Respuesta: Tamaño estimado = 307.3192
cat(categoria, "\n")
## Deslizamiento grande: alta probabilidad de daños significativos.

11. CONCLUSIÓN

El modelo de regresión múltiple permite estimar el tamaño del deslizamiento en función de variables climáticas y demográficas.
Los resultados muestran coherencia estadística y permiten realizar predicciones aplicadas bajo distintos escenarios ambientales.