1.1 Contexto científico y social del problema

La calidad del agua es fundamental para la salud humana y el equilibrio de los ecosistemas acuáticos.
Concentraciones inadecuadas de oxígeno disuelto, pH fuera de rango, alta turbidez o exceso de sólidos totales pueden afectar organismos acuáticos, limitar el uso del agua para consumo humano, riego o recreación y generar riesgos sanitarios.

El objetivo de este trabajo es clasificar diferentes fuentes de agua en grupos de calidad (alta, moderada, baja) a partir de sus características fisicoquímicas, como herramienta para apoyar la gestión y monitoreo ambiental.

1.2 Técnica de modelado seleccionada

En este caso se utilizará la técnica de clustering K-Means, que es un método de aprendizaje no supervisado.
Esta técnica es adecuada porque:

1.3 Justificación del conjunto de datos

Para ilustrar la aplicación del clustering en calidad de agua, se utiliza un conjunto de datos con rangos realistas basados en parámetros fisicoquímicos típicamente medidos en monitoreos ambientales:

Estos datos permiten representar dos grandes tipos de agua: aguas relativamente limpias y aguas con señales de contaminación, lo cual es adecuado para observar la capacidad del modelo de separar grupos.

#Justificación de los datos En lugar de utilizar una base de datos externa, se optó por construir un conjunto de datos simulados de calidad de agua con rangos realistas de pH, oxígeno disuelto, turbidez, conductividad y sólidos totales. Esta elección se justifica porque permite controlar la estructura del problema (dos grandes tipos de agua y un grupo intermedio), lo cual facilita evaluar de forma clara el desempeño del modelo de clustering K-Means sin que la interpretación se vea afectada por problemas adicionales como datos faltantes, errores de medición o sesgos de muestreo. De este modo, el conjunto de datos está diseñado específicamente para el objetivo del trabajo: ilustrar y comprender la técnica de clustering aplicada a un problema ambiental de manera reproducible y pedagógicamente guiada.

# =============================================================================

# 2. CREACIÓN DE DATOS SIMULADOS

# =============================================================================

set.seed(123)  # Para resultados reproducibles

n_muestras <- 200

agua <- data.frame(

# Parámetros fisicoquímicos (valores realistas)

ph = c(
rnorm(n_muestras/2, 7.2, 0.4),    # Aguas normales
rnorm(n_muestras/2, 5.8, 0.6)     # Aguas contaminadas
),
oxigeno_disuelto = c(
runif(n_muestras/2, 6, 10),       # Buen oxígeno
runif(n_muestras/2, 2, 5)         # Bajo oxígeno
),
turbidez = c(
runif(n_muestras/2, 1, 10),       # Baja turbidez
runif(n_muestras/2, 15, 40)       # Alta turbidez
),
conductividad = c(
runif(n_muestras/2, 100, 400),    # Conductividad normal
runif(n_muestras/2, 500, 1200)    # Alta conductividad
),
solidos_totales = c(
runif(n_muestras/2, 50, 200),     # Bajos sólidos
runif(n_muestras/2, 300, 600)     # Altos sólidos
)
)

# Asegurar que no haya valores negativos

agua$ph <- ifelse(agua$ph < 0, 0.1, agua$ph)
agua$oxigeno_disuelto <- ifelse(agua$oxigeno_disuelto < 0, 0.1, agua$oxigeno_disuelto)
agua$turbidez <- ifelse(agua$turbidez < 0, 0.1, agua$turbidez)

dim(agua)
## [1] 200   5
head(agua)
##         ph oxigeno_disuelto turbidez conductividad solidos_totales
## 1 6.975810         9.944217 3.135067      241.2046        91.04341
## 2 7.107929         6.548270 7.178413      209.7536       139.08004
## 3 7.823483         9.621238 3.032366      136.3816        74.02772
## 4 7.228203         8.305207 3.866451      114.0981       178.01454
## 5 7.251715         7.581795 2.565854      178.8389       177.16087
## 6 7.886026         7.799210 8.212866      390.5924       121.68302

Aquí se generan 200 muestras con dos “tipos” de agua: la mitad con parámetros típicos de agua de buena calidad y la otra mitad con parámetros que sugieren contaminación (bajo oxígeno, alta turbidez, etc.). Esto permite evaluar si el algoritmo de clustering es capaz de recuperar estos grupos de forma no supervisada.

summary(agua)
##        ph        oxigeno_disuelto    turbidez      conductividad   
##  Min.   :4.568   Min.   :2.048    Min.   : 1.181   Min.   : 101.2  
##  1st Qu.:5.668   1st Qu.:3.587    1st Qu.: 5.650   1st Qu.: 256.7  
##  Median :6.703   Median :5.485    Median :12.484   Median : 450.1  
##  Mean   :6.486   Mean   :5.749    Mean   :16.321   Mean   : 549.5  
##  3rd Qu.:7.232   3rd Qu.:7.843    3rd Qu.:27.238   3rd Qu.: 860.5  
##  Max.   :8.075   Max.   :9.986    Max.   :39.873   Max.   :1183.1  
##  solidos_totales 
##  Min.   : 51.97  
##  1st Qu.:120.03  
##  Median :249.82  
##  Mean   :285.07  
##  3rd Qu.:454.50  
##  Max.   :591.73

pH: El agua muestra una tendencia ligeramente ácida a neutra. La mediana (6.70) es cercana al rango neutro (7.0), pero el valor mínimo (4.57) indica la presencia de episodios o puntos de acidez significativa. La media (6.49) es menor que la mediana, lo que confirma que la distribución está sesgada hacia valores más bajos de pH, probablemente por la influencia de estos valores mínimos. Es crucial investigar las causas de esta acidez.

Oxigeno disuelto: Los niveles de oxígeno disuelto son moderados y variables. El valor mínimo (2.05 mg/L) es crítico, ya que por debajo de 5 mg/L se comienza a generar estrés para la vida acuática. El 25% de los datos (1er cuartil) están por debajo de 3.59 mg/L, lo que indica que una parte significativa de las mediciones presenta condiciones hipóxicas. La mediana (5.49 mg/L) se encuentra en el límite para mantener una biota sana.

Turbidez: Existe una alta variabilidad en la turbidez. La diferencia notable entre la mediana (12.48) y la media (16.32), junto con un tercer cuartil alto (27.24), indica que la distribución está sesgada por valores altos de turbidez. Esto significa que, aunque la mitad de las mediciones son relativamente bajas, hay eventos o fuentes que introducen una gran cantidad de sólidos en suspensión (como sedimentos, algas o materia orgánica), elevando el promedio de manera significativa.

Conductividad: La conductividad, indicadora de sales disueltas, también muestra una distribución muy dispersa. La media (549.5 µS/cm) es considerablemente más alta que la mediana (450.1 µS/cm), lo que confirma la presencia de valores extremadamente altos que elevan el promedio. Esto sugiere una posible influencia de descargas puntuales, escorrentía agrícola o intrusiones de agua salina que aumentan drásticamente la concentración de iones en el agua.

Sólidos totales disueltos: El comportamiento de los sólidos totales es coherente con el de la conductividad, ya que son parámetros directamente relacionados. Se observa el mismo patrón de una distribución sesgada hacia la derecha, con valores máximos que duplican la mediana. Esto corrobora la hipótesis de eventos o fuentes que aportan una alta carga de material disuelto al cuerpo de agua.

par(mfrow = c(2, 3))
for(i in 1:ncol(agua)) {
hist(agua[[i]],
main = paste("Distribución de", names(agua)[i]),
xlab = names(agua)[i],
col = "lightblue", border = "white")
}
par(mfrow = c(1, 1))

1.⁠ ⁠pH

Los valores van más o menos entre 5 y 8.

2.⁠ ⁠Oxígeno disuelto

Va desde ~2 hasta ~10 mg/L.

3.⁠ ⁠Turbidez

Casi no hay valores intermedios. Eso indica dos tipos claros de agua: unas bastante claras y otras claramente turbias.

4.⁠ ⁠Conductividad

Hay un bloque de valores alrededor de 100–400 (baja–media) Y otro bloque entre 500–1200 (alta). - La conductividad también separa aguas poco mineralizadas de aguas con muchos iones disueltos, típico de contaminación o aportes de sales.

5.⁠ ⁠Sólidos totales - Igual que conductividad: un grupo cerca de 50–200 y otro entre 300–600. Refuerza la idea de aguas con pocos sólidos vs. aguas con mucha carga de sólidos.

correlaciones <- cor(agua)
round(correlaciones, 2)
##                     ph oxigeno_disuelto turbidez conductividad solidos_totales
## ph                1.00             0.76    -0.75         -0.75           -0.77
## oxigeno_disuelto  0.76             1.00    -0.81         -0.80           -0.84
## turbidez         -0.75            -0.81     1.00          0.80            0.82
## conductividad    -0.75            -0.80     0.80          1.00            0.84
## solidos_totales  -0.77            -0.84     0.82          0.84            1.00
corrplot(correlaciones, method = "color",
title = "Correlación entre parámetros de calidad de agua",
mar = c(0, 0, 2, 0))

El gráfico muestra qué tan relacionados están los parámetros de calidad del agua entre sí. Los valores cercanos a 1 significan fuerte relación positiva y cercanos a -1 fuerte relación negativa. Principales hallazgos Conductividad y sólidos totales están muy fuertemente correlacionados (casi 1). - A mayor cantidad de sólidos en el agua, mayor conductividad eléctrica. Turbidez también se relaciona positivamente con conductividad y sólidos totales. - Agua más turbia suele tener más partículas y mayor conductividad. pH y oxígeno disuelto tienen correlación más baja con los otros parámetros. - Significa que cambian de forma más independiente.

# =============================================================================

# 5. PREPARACIÓN PARA CLUSTERING

# =============================================================================

# Estandarizar los datos (importante para K-Means)

agua_estandar <- scale(agua)
head(agua_estandar)
##             ph oxigeno_disuelto   turbidez conductividad solidos_totales
## [1,] 0.5479529        1.7294949 -1.0872812    -0.9021873      -1.1004971
## [2,] 0.6957003        0.3293820 -0.7538691    -0.9942363      -0.8280322
## [3,] 1.4958962        1.5963342 -1.0957499    -1.2089780      -1.1970105
## [4,] 0.8302017        1.0537486 -1.0269717    -1.2741963      -0.6071947
## [5,] 0.8564946        0.7554936 -1.1342182    -1.0847161      -0.6120367
## [6,] 1.5658369        0.8451313 -0.6685687    -0.4649662      -0.9267085

La estandarización garantiza que todas las variables (medidas en unidades distintas) contribuyan de forma comparable al cálculo de distancias, evitando que variables con mayor escala dominen el resultado del clustering.

# =============================================================================

# 6. DETERMINACIÓN DEL NÚMERO DE CLUSTERS

# =============================================================================

set.seed(123)
wss <- numeric(10)
for (k in 1:10) {
kmeans_model <- kmeans(agua_estandar, centers = k, nstart = 25)
wss[k] <- kmeans_model$tot.withinss
}

plot(1:10, wss, type = "b", pch = 19,
xlab = "Número de clusters (K)",
ylab = "Suma de cuadrados intra-cluster (WSS)",
main = "Método del Codo para elegir K")

abline(v = 3, col = "red", lty = 2)
text(3, mean(wss), "K = 3", pos = 4, col = "red")

- El eje X es el número de clusters (K) que probamos (desde 2 hasta 10). - El eje Y representa un error (la “inercia” o suma de distancias de los puntos a su cluster).

En este caso, el codo más claro está en K=3. Porque después de 3 clusters, agregar más grupos no mejora significativamente el resultado (la línea se vuelve casi recta). Es el punto de equilibrio.

# =============================================================================

# 7. APLICACIÓN DE K-MEANS

# =============================================================================

k_optimo <- 3

set.seed(123)
kmeans_resultado <- kmeans(agua_estandar, centers = k_optimo, nstart = 25)

# Añadir clusters a los datos originales

agua$cluster <- as.factor(kmeans_resultado$cluster)

# Distribución de muestras por cluster

table(agua$cluster)
## 
##   1   2   3 
##  55  45 100
fviz_cluster(kmeans_resultado, data = agua_estandar,
palette = c("#2E9FDF", "#00AFBB", "#E7B800"),
geom = "point",
ellipse.type = "convex",
main = "Clusters de calidad de agua (K-Means)",
ggtheme = theme_minimal())

par(mfrow = c(2, 3))
for(i in 1:(ncol(agua)-1)) {  # Excluir la columna cluster
boxplot(agua[[i]] ~ agua$cluster,
main = paste(names(agua)[i], "por cluster"),
xlab = "Cluster",
ylab = names(agua)[i],
col = c("#2E9FDF", "#00AFBB", "#E7B800"))
}
par(mfrow = c(1, 1))

plot(agua$ph, agua$oxigeno_disuelto,
col = as.numeric(agua$cluster),
pch = 19, cex = 1.2,
xlab = "pH",
ylab = "Oxígeno disuelto (mg/L)",
main = "pH vs oxígeno disuelto por cluster")
legend("topright",
legend = paste("Cluster", 1:k_optimo),
col = 1:k_optimo, pch = 19)

1ra imagen - Se identificaron 3 grupos (clusters) de calidad de agua. - El eje horizontal (Dim1) explica el 83.7% de la variabilidad de los datos, lo que significa que esta dimensión captura la mayor parte de las diferencias entre los grupos. - Los clusters están claramente separados en el gráfico, lo que indica que el modelo K-Means logró agrupar efectivamente las muestras según su calidad. Hay tres niveles distintos de calidad de agua y una sola variable (Dim1) es suficiente para diferenciarlos en gran medida.

2da imagen - Cada cluster tiene un perfil único de calidad de agua, ya que los niveles de los parámetros (representados por la altura de las barras “p/h”) son distintos para cada grupo (1, 2 y 3). - Esto confirma que el algoritmo K-Means logró agrupar las muestras de agua de manera efectiva según sus características físico-químicas.

3ra imagen - Los clusters están bien diferenciados en el espacio de estas dos variables, lo que confirma que son parámetros clave para definir la calidad del agua. - Cada cluster ocupa una zona específica en el gráfico, lo que significa que cada grupo representa un tipo de agua con una combinación característica de pH y oxígeno disuelto.

##Conclusión

El análisis de clustering con K-Means demostró ser una herramienta efectiva para clasificar la calidad del agua en tres grupos distintos con características fisicoquímicas bien diferenciadas:

Hallazgos Principales

  1. Agrupación Exitosa: El algoritmo identificó consistentemente tres perfiles de calidad de agua a partir de los cinco parámetros analizados (pH, oxígeno disuelto, turbidez, conductividad y sólidos totales).

  2. Variables Clave: El pH y el oxígeno disuelto emergieron como parámetros particularmente determinantes para diferenciar los clusters, mostrando una clara separación en el espacio bidimensional.

  3. Patrones Identificados: Cada cluster representa un nivel distinto de calidad:

Implicaciones Prácticas Esta metodología permite: - Monitoreo eficiente: Clasificar rápidamente fuentes de agua sin necesidad de análisis complejos previos - Gestión dirigida: Implementar estrategias específicas según el nivel de calidad identificado - Detección temprana: Identificar patrones que sugieren deterioro en la calidad del agua