Ecosistemas Acuáticos y Salud Pública: La calidad del agua es fundamental para mantener ecosistemas acuáticos saludables y asegurar agua potable para comunidades humanas. Los parámetros medidos en este modelo son indicadores clave de:
Salud de Ecosistemas Acuáticos:
Ríos, lagos y humedales dependen de condiciones químicas específicas Biodiversidad acuática - muchas especies son sensibles a cambios en pH y oxígeno Cadenas tróficas - alteraciones afectan desde fitoplancton hasta peces depredadores
Procesos Ecológicos Clave:
Fotosíntesis de plantas acuáticas depende de luz (turbidez) y nutrientes Descomposición de materia orgánica requiere oxígeno disuelto Ciclos biogeoquímicos de nitrógeno y fósforo 1. CARGAR PAQUETES BÁSICOS
# Configurar el mirror
options(repos = c(CRAN = "https://cloud.r-project.org/"))
install.packages(c("dplyr", "ggplot2", "caret", "rpart", "randomForest"))
##
## The downloaded binary packages are in
## /var/folders/mm/xmr4j29j06n9d0qql4m4lb6w0000gn/T//RtmpPrCeHH/downloaded_packages
library(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
library(ggplot2)
## Warning: package 'ggplot2' was built under R version 4.5.2
library(caret)
## Loading required package: lattice
library(rpart)
library(randomForest)
## randomForest 4.7-1.2
## Type rfNews() to see new features/changes/bug fixes.
##
## Attaching package: 'randomForest'
## The following object is masked from 'package:ggplot2':
##
## margin
## The following object is masked from 'package:dplyr':
##
## combine
2. CARGAR DATOS DE CALIDAD DEL AGUA
Dataset: Water Quality from Kaggle
https://www.kaggle.com/datasets/mssmartypants/water-quality
n_muestras <- 1000
datos_agua <- data.frame(
pH = runif(n_muestras, 6.0, 8.5),
oxigeno_disuelto = runif(n_muestras, 2.0, 11.0),
conductividad = runif(n_muestras, 100, 2000),
turbidez = runif(n_muestras, 0.1, 10.0),
nitrogeno = runif(n_muestras, 0.1, 5.0),
fosforo = runif(n_muestras, 0.01, 1.0),
temperatura = runif(n_muestras, 10, 30)
)
# calidad_del_agua (1=Buena, 0=Mala)
datos_agua$calidad_del_agua <- ifelse(
datos_agua$pH >= 6.5 & datos_agua$pH <= 8.5 &
datos_agua$oxigeno_disuelto >= 5.0 &
datos_agua$turbidez <= 5.0 &
datos_agua$nitrogeno <= 1.0,
1, # Buena calidad
0 # Mala calidad
)
print(head(datos_agua))
## pH oxigeno_disuelto conductividad turbidez nitrogeno fosforo
## 1 6.941600 2.379984 1488.2733 3.5546478 4.5166453 0.9565078
## 2 6.938480 6.060072 1206.7365 8.0103803 1.9925914 0.4600865
## 3 8.061172 2.763636 129.6487 0.5497789 0.6361089 0.3729493
## 4 7.375001 4.427414 219.8605 9.5627489 4.0958543 0.2375430
## 5 7.811424 4.640093 680.2755 6.0373154 0.8762612 0.2891274
## 6 6.324573 6.175772 1323.0785 7.2766010 1.4102349 0.7983283
## temperatura calidad_del_agua
## 1 19.01991 0
## 2 24.64242 0
## 3 25.07290 0
## 4 29.90812 0
## 5 15.64407 0
## 6 17.22659 0
cat("Muestra de", n_muestras, "registros\n")
## Muestra de 1000 registros
cat("Calidad buena:", sum(datos_agua$calidad_del_agua), "muestras\n")
## Calidad buena: 57 muestras
cat("Calidad mala:", sum(datos_agua$calidad_del_agua == 0), "muestras\n")
## Calidad mala: 943 muestras
3. ANÁLISIS EXPLORATORIO SIMPLE
cat("\n=== ANÁLISIS EXPLORATORIO ===\n")
##
## === ANÁLISIS EXPLORATORIO ===
# Histogramas de las variables
par(mfrow = c(2, 4))
for (i in 1:7) {
hist(datos_agua[[i]], main = names(datos_agua)[i],
xlab = "", col = "#8EE5EE")
}
# Boxplot por calidad del agua
par(mfrow = c(2, 4))
for (i in 1:7) {
boxplot(datos_agua[[i]] ~ datos_agua$calidad_del_agua,
main = names(datos_agua)[i],
names = c("Mala", "Buena"),
col = c("#CD3278", "#BCEE68"))
}
# Correlación con calidad del agua
correlaciones <- cor(datos_agua[,1:7], datos_agua$calidad_del_agua)
cat("\nCorrelación con calidad del agua:\n")
##
## Correlación con calidad del agua:
print(round(correlaciones, 3))
## [,1]
## pH 0.122
## oxigeno_disuelto 0.145
## conductividad -0.046
## turbidez -0.180
## nitrogeno -0.332
## fosforo -0.023
## temperatura -0.033
pH (6.0-8.5) - Acidez/Alcalinidad: • Óptimo ecológico: 6.5-8.5 • <6.5: Acidificación - dissolve metales tóxicos, daña branquias de peces • >8.5: Estrés metabólico - afecta enzimas y procesos celulares • Especies sensibles: Truchas, cangrejos de río, anfibios
Oxígeno Disuelto (2.0-11.0 mg/L) - Vida Aeróbica: • >5 mg/L: Saludable para peces y macroinvertebrados • 2-5 mg/L: Estrés para especies sensibles • <2 mg/L: Condiciones hipóxicas - solo organismos tolerantes • Factores que afectan: Temperatura, actividad fotosintética, descomposición
Turbidez (0.1-10.0 NTU) - Transparencia del Agua: • <5 NTU: Agua clara - buena penetración de luz para fotosíntesis • >5 NTU: Reduce fotosíntesis, cubre habitats bentónicos • Fuentes: Sedimentos, algas, materia orgánica en suspensión • Impacto: Afecta visión de peces depredadores, sedimentación
Nitrógeno (0.1-5.0 mg/L) - Nutriente Esencial: • <1 mg/L: Niveles naturales en aguas no contaminadas • >1 mg/L: Posible contaminación por fertilizantes, aguas residuales • Eutrofización: Crecimiento excesivo de algas, blooms algales • Formas tóxicas: Amonio no ionizado, nitritos
Fósforo (0.01-1.0 mg/L) - Nutriente Limitante: • Principal limitante de crecimiento algal en agua dulce • Fuentes: Detergentes, fertilizantes, desechos orgánicos • >0.03 mg/L: Puede desencadenar eutrofización
Conductividad (100-2000 μS/cm) - Sales Disueltas: • Indicador de contaminación por sales • Aumenta con: Escorrentía agrícola, aguas residuales, minería • Afecta: Osmorregulación de organismos acuáticos
Temperatura (10-30°C) - Metabolismo: • Controla tasas metabólicas de organismos ectotérmicos • Aumento: Disminuye oxígeno disuelto, acelera metabolismo • Especies termófilas vs. criófilas
4. MODELO DE ÁRBOL DE DECISIÓN (SIMPLE)
# Dividir datos en entrenamiento (70%) y prueba (30%)
set.seed(456)
indices_entrenamiento <- createDataPartition(
datos_agua$calidad_del_agua,
p = 0.7,
list = FALSE
)
datos_entrenamiento <- datos_agua[indices_entrenamiento, ]
datos_prueba <- datos_agua[-indices_entrenamiento, ]
cat("Datos de entrenamiento:", nrow(datos_entrenamiento), "\n")
## Datos de entrenamiento: 700
cat("Datos de prueba:", nrow(datos_prueba), "\n")
## Datos de prueba: 300
# Entrenar árbol de decisión simple
modelo_arbol <- rpart(
calidad_del_agua ~ .,
data = datos_entrenamiento,
method = "class",
control = rpart.control(maxdepth = 3) # Limitamos la profundidad para simplicidad
)
5. EVALUAR EL MODELO
# Predecir en datos de prueba
predicciones <- predict(modelo_arbol, datos_prueba, type = "class")
# Matriz de confusión
matriz_confusion <- table(Prediccion = predicciones,
Real = datos_prueba$calidad_del_agua)
cat("Matriz de confusión:\n")
## Matriz de confusión:
print(matriz_confusion)
## Real
## Prediccion 0 1
## 0 280 2
## 1 3 15
# Calcular precisión
precision <- sum(diag(matriz_confusion)) / sum(matriz_confusion)
cat("Precisión del modelo:", round(precision, 3), "\n")
## Precisión del modelo: 0.983
# Métricas adicionales
sensibilidad <- matriz_confusion[2,2] / sum(matriz_confusion[,2])
especificidad <- matriz_confusion[1,1] / sum(matriz_confusion[,1])
cat("Sensibilidad:", round(sensibilidad, 3), "\n")
## Sensibilidad: 0.882
cat("Especificidad:", round(especificidad, 3), "\n")
## Especificidad: 0.989
6. VISUALIZAR EL ÁRBOL DE DECISIÓN
# Plot del árbol
par(mfrow = c(1, 1))
plot(modelo_arbol, margin = 0.1)
text(modelo_arbol, use.n = TRUE, cex = 0.8)
title("Árbol de Decisión - Calidad del Agua")
7. REGLAS DEL MODELO (INTERPRETACIÓN)
# Mostrar reglas simples del árbol
print(modelo_arbol)
## n= 700
##
## node), split, n, loss, yval, (yprob)
## * denotes terminal node
##
## 1) root 700 40 0 (0.94285714 0.05714286)
## 2) nitrogeno>=0.9919251 558 0 0 (1.00000000 0.00000000) *
## 3) nitrogeno< 0.9919251 142 40 0 (0.71830986 0.28169014)
## 6) turbidez>=4.779728 66 0 0 (1.00000000 0.00000000) *
## 7) turbidez< 4.779728 76 36 1 (0.47368421 0.52631579)
## 14) oxigeno_disuelto< 4.990528 29 0 0 (1.00000000 0.00000000) *
## 15) oxigeno_disuelto>=4.990528 47 7 1 (0.14893617 0.85106383) *
Interpretación biológica:
El modelo identifica las variables más importantes para predecir calidad del agua Las reglas ayudan a entender los umbrales críticos para la salud del ecosistema 8. MODELO ALTERNATIVO: RANDOM FOREST
# Entrenar Random Forest simple
modelo_rf <- randomForest(
as.factor(calidad_del_agua) ~ .,
data = datos_entrenamiento,
ntree = 50, # Pocos árboles para ser rápido
importance = TRUE
)
# Importancia de variables
importancia <- importance(modelo_rf)
cat("Importancia de variables (Random Forest):\n")
## Importancia de variables (Random Forest):
print(round(importancia, 3))
## 0 1 MeanDecreaseAccuracy MeanDecreaseGini
## pH 3.800 5.086 5.888 7.607
## oxigeno_disuelto 9.160 8.773 10.248 14.485
## conductividad 0.743 -1.410 -0.330 4.042
## turbidez 7.831 11.439 11.126 16.850
## nitrogeno 11.878 14.875 15.221 29.046
## fosforo -1.963 1.082 -0.749 3.420
## temperatura 1.054 -1.319 -0.289 3.886
NITRÓGENO (23.37 - 32.20) - MÁS IMPORTANTE • Razón biológica: Principal indicador de contaminación por: - Fertilizantes agrícolas - Aguas residuales - Escorrentía urbana • Impacto ecológico: Eutrofización, blooms algales
TURBIDEZ (14.56 - 17.59) - SEGUNDA EN IMPORTANCIA • Razón biológica: Afecta directamente: - Penetración de luz para fotosíntesis - Visibilidad para depredadores - Sedimentación en habitats bentónicos
OXÍGENO DISUELTO (10.68 - 11.77) - TERCERA IMPORTANCIA • Razón biológica: Factor limitante para: - Respiración de peces e invertebrados - Procesos de descomposición - Ciclos biogeoquímicos
9. PREDICCIÓN EN NUEVOS DATOS
# Crear nuevos datos de ejemplo
nuevas_muestras <- data.frame(
pH = c(7.2, 6.1, 8.0),
oxigeno_disuelto = c(8.5, 3.2, 6.0),
conductividad = c(500, 1500, 800),
turbidez = c(2.1, 8.5, 3.0),
nitrogeno = c(0.5, 2.1, 0.8),
fosforo = c(0.1, 0.5, 0.2),
temperatura = c(20, 25, 18)
)
# Predecir calidad
predicciones_nuevas <- predict(modelo_arbol, nuevas_muestras, type = "class")
resultados <- ifelse(predicciones_nuevas == 1, "BUENA", "MALA")
cat("Predicciones para nuevas muestras:\n")
## Predicciones para nuevas muestras:
for (i in 1:nrow(nuevas_muestras)) {
cat("Muestra", i, ":", resultados[i], "calidad\n")
}
## Muestra 1 : BUENA calidad
## Muestra 2 : MALA calidad
## Muestra 3 : BUENA calidad
10. ANÁLISIS BIOLÓGICO
cat("Variables clave para calidad del agua:\n")
## Variables clave para calidad del agua:
cat("1. pH (6.5-8.5): Rango óptimo para vida acuática\n")
## 1. pH (6.5-8.5): Rango óptimo para vida acuática
cat("2. Oxígeno disuelto (>5 mg/L): Essential para peces\n")
## 2. Oxígeno disuelto (>5 mg/L): Essential para peces
cat("3. Turbidez (<5 NTU): Afecta penetración de luz\n")
## 3. Turbidez (<5 NTU): Afecta penetración de luz
cat("4. Nitrógeno (<1 mg/L): Evita eutrofización\n")
## 4. Nitrógeno (<1 mg/L): Evita eutrofización
cat("5. Fósforo: Nutriente limitante para algas\n")
## 5. Fósforo: Nutriente limitante para algas
cat("6. Temperatura: Affecta metabolismo de organismos\n")
## 6. Temperatura: Affecta metabolismo de organismos
11. GRÁFICO FINAL
ggplot(datos_agua, aes(x = oxigeno_disuelto, y = pH, color = as.factor(calidad_del_agua))) +
geom_point(alpha = 0.6, size = 2) +
scale_color_manual(values = c("#FFA54A", "#36648B"),
labels = c("Mala calidad", "Buena calidad"),
name = "Calidad del agua") +
labs(title = "Calidad del Agua vs Oxígeno Disuelto y pH",
subtitle = "Puntos azules: Buena calidad | Puntos rojos: Mala calidad",
x = "Oxígeno Disuelto (mg/L)",
y = "pH") +
theme_minimal()
ggsave("grafico_calidad_agua.png", width = 10, height = 6)
Patrón General Observado: • Los puntos AZULES (buena calidad) se concentran en: - Oxígeno disuelto > 5 mg/L - pH entre 6.5 y 8.5
• Los puntos ROJOS (mala calidad) predominan en: - Oxígeno disuelto < 5 mg/L - pH extremos (<6.5 o >8.5)
Zona Óptima (Cuadrante Superior-Derecho):
• Condiciones: Alto oxígeno (>7 mg/L) + pH neutro (7.0-8.0) •
Significado ecológico: - Máxima diversidad de macroinvertebrados -
Condiciones ideales para reproducción de peces - Alta actividad
fotosintética
Zona de Estrés por Bajo Oxígeno (Lado
Izquierdo):
• Característica: Oxígeno < 5 mg/L, cualquier pH • Impacto biológico:
- Estrés respiratorio en peces - Dominio de organismos anaeróbicos -
Producción de metano y sulfuro de hidrógeno - “Zonas muertas” en
ecosistemas
Zona de Estrés por pH (Extremos Verticales):
• pH < 6.5 (Acidificación): - Liberación de aluminio tóxico - Daño a
branquias de peces - Reducción de biodiversidad
• pH > 8.5 (Alcalinización): - Conversión de amonio a amoníaco tóxico - Precipitación de nutrientes esenciales - Estrés osmorregulatorio
Relación entre Variables:
• Sinergia negativo: Bajo oxígeno + pH extremo = Condiciones críticas •
Compensación: Aguas con pH subóptimo pero alto oxígeno pueden mantener
alguna vida • Puntos atípicos: Algunos puntos rojos en zona “óptima” -
sugieren contaminación por tóxicos no medidos
Limitaciones y Consideraciones: • Variables no incluidas: Metales pesados, pesticidas, patógenos • Variabilidad temporal: No captura fluctuaciones diurnas/estacionales • Contexto ecológico: Umbrales pueden variar por tipo de ecosistema