💡 OBJETIVOS:
Comprender los fundamentos de la clasificación no supervisada y entender en qué se diferencia de la supervisada.
Aplicar diferentes algoritmos de clasificación (k-means, ISODATA, Random Forest etc.) con R para clasificar los píxeles en grupos o clases espectrales.
Comparar diferentes resultados variando parámetros como el número de clases para llegar a su número adecuado.
Evaluar y visualizar los resultados, representándolos espacialmente.
Interpretar las clases resultantes en función de la imagen original y su contexto.
Generar productos y exportar resultados en forma de ficheros ráster, cartografía temática o tablas con estadísticas básicas de los grupos clasificados (áreas por clase, histogramas de frecuencia, etc.).
💡 MATERIALES PARA LA ACTIVIDAD:
Los materiales para el desarrollo de esta actividad son los siguientes.
► Se utilizará la misma imagen utilizada en la actividad previa (clasificación manual).
La clasificación no supervisada es la técnica de clasificación en la que el usuario no proporciona información previa. Las imágenes se clasifican en función de sus características espectrales, de modo que los píxeles que tienen propiedades espectrales similares se agrupan en la misma clase o categoría. Es decir, éstas se crean únicamente en función de la información numérica de los datos (es decir, los valores de píxeles para cada una de las bandas o índices).
Es un tipo de clasificación automatizada, si bien el usuario tiene control sobre ciertos aspectos del proceso de clasificación. Esto incluye el número de clases, el número máximo de iteraciones (que es la cantidad de veces que se ejecuta el algoritmo de clasificación) y el porcentaje de umbral de cambio, que especifica cuándo finalizar el procedimiento de clasificación.
Una vez clasificados los datos, el usuario debe interpretar, etiquetar y codificar con colores las clases en consecuencia.
Rápida y fácil de ejecutar.
No requiere trabajo de campo ni conocimiento previo del área (si bien cierto conocimiento es favorable). Por ejemplo, pueden ser útiles al estudiar zonas poco conocidas o con pocos datos.
Al crearse las clases a partir de la información espectral, por lo que no son tan subjetivas como la interpretación visual manual. El algorítmo agrupa automáticamente los píxeles según su similitud espectral.
Ofrecen una mayor consistencia y reproducibilidad (los mismos resultados) siempre que se usen los mismos parámetros iniciales. Este aspecto consituye una ventaja en estudios comparativos o en el análisis de series temporales. La clasificación manual puede variar entre analistas o incluso en distintos momentos.
Pueden detectar estructuras complejas o clases no evidentes, que el analista puede no haber considerado o percibido.
Constituyen métodos eficientes en el manejo de grandes volúmenes de datos, por ejemplo las imágenes multiespectrales con diferentes bandas. Ajustar umbrales manualmente en múltiples dimensiones es muy difícil.
Las clases espectrales no siempre corresponden a clases reales.
El usuario también tiene que dedicar tiempo a denominar, interpretar y asociar las categorías obtenidas a coberturas reales.
Las propiedades espectrales de las clases también pueden cambiar con el tiempo, por lo que no siempre se puede utilizar la misma información de clase al pasar de una imagen a otra.
Gran incertidumbre si hay muchas cubiertas con firmas similares.
Casos ideales de uso
Exploración preliminar del paisaje.
Estudios en zonas desconocidas.
Cuando no se cuenta con datos de referencia.
La clasificación no supervisada utiliza algoritmos de agrupamiento. El propósito de estos algoritmos es minimizar la variabilidad dentro de cada categoría y maximizar la separación entre categorías. Es labor del usuario interpretar y etiquetar las categorías, convirtiendo las clases espectrales en clases de información. Por lo tanto, requiere un esfuerzo suplementario en la identificación de las categorías y su adscripción a clases de información.
K-means es el método más utilizado para realizar una clasificación no supervisada, al que se añadirán en este apartado otros métodos de uso cada vez más frecuentes, como CLARA o Random forest.
| Algoritmo | Tipo | Idea básica | Ventajas | Limitaciones | Uso típico en teledetección |
|---|---|---|---|---|---|
| K-means | Particional | Agrupa píxeles en k clusters minimizando la distancia a la media | Rápido, fácil de implementar | Hay que definir k, sensible a inicialización | Clasificación preliminar de coberturas |
| ISODATA (Iterative Self-Organizing Data Analysis Technique) | Particional dinámico | Similar a K-means pero permite dividir y fusionar clusters | Más flexible que K-means | Más complejo, requiere más parámetros | Análisis exploratorio en imágenes multiespectrales |
| Clustering jerárquico | Jerárquico | Forma una jerarquía de grupos (árbol o dendrograma) | No requiere fijar k inicialmente | Costoso computacionalmente | Análisis detallado en áreas pequeñas |
| DBSCAN (Density-Based Spatial Clustering) | Basado en densidad | Agrupa puntos densos y detecta ruido | Detecta formas arbitrarias, identifica outliers | Difícil ajustar parámetros | Detección de anomalías o cambios |
| Mean Shift | Basado en densidad | Encuentra máximos de densidad en el espacio de datos | No necesita número de clusters | Computacionalmente costoso | Segmentación de imágenes |
| Self-Organizing Maps (SOM) | Redes neuronales | Proyecta datos en un mapa de menor dimensión preservando relaciones | Visualización útil, capta patrones complejos | Interpretación más compleja | Análisis de firmas espectrales |
Para realizar una clasificación no supervisada de datos ráster en R, se requieren los siguientes paquetes.
library(terra) # Manejo de datos en formato ráster.
## terra 1.8.93
library(tidyverse)
## Warning: package 'tidyverse' was built under R version 4.3.2
## Warning: package 'readr' was built under R version 4.3.2
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.2.0 ✔ readr 2.1.5
## ✔ forcats 1.0.1 ✔ stringr 1.6.0
## ✔ ggplot2 4.0.2 ✔ tibble 3.3.1
## ✔ lubridate 1.9.5 ✔ tidyr 1.3.2
## ✔ purrr 1.2.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ tidyr::extract() masks terra::extract()
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(ggplot2) # Visualización de datos ráster
library(RStoolbox) # Clasificación y visualización de imágenes siguiendo el método K-means
## This is version 1.0.2.2 of RStoolbox
library(cluster) # Clasificación según el según el método CLARA.
library(randomForest) # Clasificación siguiendo el método RandomForest.
## Warning: package 'randomForest' was built under R version 4.3.3
## randomForest 4.7-1.2
## Type rfNews() to see new features/changes/bug fixes.
##
## Attaching package: 'randomForest'
##
## The following object is masked from 'package:dplyr':
##
## combine
##
## The following object is masked from 'package:ggplot2':
##
## margin
Los datos que usaremos en este tema se encuentran en la carpeta
/datos/no_supervisada/ que se accede mediante la siguiente
ruta, que se comprueba
A continuación se importará el fichero ráster sobre la que se realizará una clasificación no supervisada
imagen <- rast("D:/G174_2026/LABORATORIO_7_Clasificacion_imagenes_PIXEL/datos/no_supervisada/imagen.tif")
imagen
## class : SpatRaster
## size : 1245, 1497, 7 (nrow, ncol, nlyr)
## resolution : 30, 30 (x, y)
## extent : 594090, 639000, 4190190, 4227540 (xmin, xmax, ymin, ymax)
## coord. ref. : WGS 84 / UTM zone 10N (EPSG:32610)
## source : imagen.tif
## names : coast~rosol, blue, green, red, NIR, SWIR1, ...
## min values : 0.09641791, 0.0748399, 0.04259216, 0.02084067, 0.0008457669, -0.007872183, ...
## max values : 0.73462820, 0.7177562, 0.69246972, 0.78617686, 1.0124315023, 1.043204546, ...
plot(imagen)
Esa imagen contiene una escena de Landsat 8 con 7 bandas.
A continuación será representada en forma de imagen en color verdadero
ggRGB(imagen, r = 4, g= 3, b= 2, stretch ="lin") +
ggtitle("Imagen original") + # Título
labs(x ="Longitud (m)", y ="Latitud (m)") + # Etiquetas de los ejes
theme(plot.title = element_text(hjust =0.5, # Alineación del título
size =20), # Tamaño del título
axis.title = element_text(size =15)) # Tamaño de las etiquetas de los ejes
Las imágenes de satélite deben convertirse en una matriz de datos
antes de aplicar cualquiera de los algoritmos disponibles. Este
dataframe se crea con la función terra:as.data.frame(),
teniendo la precaución de eliminar los valores ausentes (No data) con el
argumento .
df <- as.data.frame(imagen, # Nombre del ráster multibanda.
na.rm=TRUE) # Elimina valores ausentes
Se va a eliminar la primera banda (coastal aerosol).
df <- df %>%
select(-1)
También es conveniente escalar datos, es decir, que todos los valores oscilen entre -1 y +1. Es fundamental que los datos estén escalados , ya que este algoritmo se basa en distancias euclidianas y las variables con rangos grandes podrían dominar el modelo.
df_escalado <- df %>%
scale() %>%
as.data.frame()
Por último, para evitar problemas de memoria, seleccionamos aleatoriamente el 10 % de todos los píxeles.
set.seed(123)
df_muestra <- df_escalado %>%
slice_sample(prop = 0.1)
Es el método más utilizado para la clasificación no supervisada de ráster. El número de grupos (\(k\)) debe ser predefinido por el usuario. El algorítmo agrupa los píxeles dentro de cada cateogría de acuerdo a su proximidad al centroide (punto medio de cada categoría o clase en el “space feature”) más cercano. Una vez asignados todos los píxeles a cada uno de los grupos, se recalculan de nuevo los centroides del grupo. Este procedimiento se itera hasta que no se encuentre una diferencia significativa en los centroides o se alcance el número predefinido de iteraciones. De esta forma, este algoritmo optimiza la posición de los centroides. El inconveniente de este algorítmo es su sensibilidad a los valores atípicos.
clusterLa agrupación de k-means en R se puede realizar utilizando la función
cluster::kmeans(), que se acompaña de los siguientes
argumentos:
El número de grupos se define en el argumento
centers.
El número máximo de iteraciones permitidas se menciona en el
argumento iter.max.
El algoritmo a utilizar se menciona en el argumento
algorithm. El algoritmo de Lloyd es el más
utilizado.
set.seed(999) # Semillas aleatorias
cluster_kmeans <- kmeans(df_muestra, # Bandas a clasificar
centers = 5, # Número de grupos o clusters
nstart = 10, # Número de puntos de partida.
iter.max = 500, # número de iteraciones
algorithm = 'Lloyd') # Algoritmo
str(cluster_kmeans)
## List of 9
## $ cluster : Named int [1:186376] 2 5 1 3 2 1 5 5 5 2 ...
## ..- attr(*, "names")= chr [1:186376] "1237518" "134058" "1172598" "685285" ...
## $ centers : num [1:5, 1:6] 0.51 -0.379 -0.228 1.5 -1.016 ...
## ..- attr(*, "dimnames")=List of 2
## .. ..$ : chr [1:5] "1" "2" "3" "4" ...
## .. ..$ : chr [1:6] "blue" "green" "red" "NIR" ...
## $ totss : num 1119709
## $ withinss : num [1:5] 69012 48202 28390 78219 54248
## $ tot.withinss: num 278071
## $ betweenss : num 841639
## $ size : int [1:5] 53798 42493 23076 24469 42540
## $ iter : int 52
## $ ifault : NULL
## - attr(*, "class")= chr "kmeans"
La salida de la función kmeans proporciona una gran
cantidad de información:
# cluster_kmeans$cluster # Número de casos
cluster_kmeans$centers # Aparecen los valores de los centroides
cluster_kmeans$totss # Total sum of squares
cluster_kmeans$withinss # Within-cluster sum of squares
cluster_kmeans$totalwithins # Total within-cluster sum of squares
cluster_kmeans$betweenss # Between-cluster sum of squares
cluster_kmeans$size # Número de píxeles en cada cluster
cluster_kmeans$iter # Número de iteraciones
cluster_kmeans$ifault # Indicador de un posible fallo del algorítmo
Lo más relevante es lo siguiente:
Tamaño de cada grupo, clase o categoría.
Valor medio de las variables (bandas) que conforman cada grupo.
El vector de agrupación, es decir la identidad de grupo de cada celda o píxel clasificado, dentro del grupo suma de cuadrados por grupo.
withinss (Within-cluster sum of squares). Mide la variabilidad interna de cada cluster. Es la suma de las distancias cuadradas de los puntos al centroide de su cluster. Interpretación:
Bajo = bueno → los puntos están muy agrupados (clusters compactos).
Alto = malo → los clusters están dispersos.
totalwithinss. Es la suma total de la variabilidad interna de todos los clusters (básicamente totalwithinss=∑withinss de todos los clusters). Cuanto menor, mejor es la compactación global. Se usa mucho para comparar distintos valores de k.
betweenss (Between-cluster sum of squares). Mide la separación entre clusters. Representa cuánto varían los centroides entre sí respecto al centro global. Interpretación: Alto = bueno → clusters bien separados. Bajo = malo → clusters poco diferenciados.
Los últimos 3 términos están concetados \[Total SS = Within SS + Between SS\]
Donde:
\[Total SS\] = variabilidad total de los datos \[Within SS\] = variabilidad dentro de clusters \[Between SS\] = variabilidad entre clusters
¿Cómo saber si el el número de grupos es el idóneo?
Ratio de separación. Es una métrica muy usada: betweenss / total SS . Un valor cercano a 1 → excelente (clusters bien separados); un valor más bajo supone mala separación.
Método del “codo” (Elbow method). Se analiza totalwithinss para distintos valores de k. Se busca un punto donde la reducción deja de ser significativa. Si no hay “codo” claro → K-means puede no ser adecuado.
En resumen, una buena solución tiene:
withinss bajo (compactación)
betweenss alto (separación)
| Parámetro | Qué mide | Ideal |
|---|---|---|
| withinss | Compactación interna | Bajo |
| totalwithinss | Compactación global | Bajo |
| betweenss | Separación entre clusters | Alto |
Este algorítmo presenta limitaciones importantes, ya que asume clusters esféricos, no detecta bien estructuras complejas y es sensible tanto a la escala de los valores como a la presencia de “outliers” (casos extremos)
Extender la clasificación a todo el dataframe escalado
df_kmeans_completo <- kmeans(df_escalado, # dataframe escalado (con todos los píxeles)
centers = cluster_kmeans$centers) # Se utilizan los centroides
Se traslada cada pixel clasificado en el dataframe al ráster
raster_kmeans <- imagen[[1]]
values(raster_kmeans) <- df_kmeans_completo$cluster
raster_kmeans
## class : SpatRaster
## size : 1245, 1497, 1 (nrow, ncol, nlyr)
## resolution : 30, 30 (x, y)
## extent : 594090, 639000, 4190190, 4227540 (xmin, xmax, ymin, ymax)
## coord. ref. : WGS 84 / UTM zone 10N (EPSG:32610)
## source(s) : memory
## varname : imagen
## name : coastal aerosol
## min value : 1
## max value : 5
Una vez obtenida la clasificación, se puede intentar una interpretación basíca. Para ello, los píxeles clasificados (con una transparencia del 0.3, my alta) se superponen a la imagen en color verdadero
plotRGB(imagen, r=4, g=3, b=2, axes=TRUE, stretch="lin")
plot(raster_kmeans, col = rainbow(5),
main = 'Visualización preliminar', add=TRUE, alpha = 0.4, legend = TRUE)
Otro opción es representar una única categorías sobre la que se pueden tener dudas. El proceso consiste en crear una máscara binaria donde solo la categoría 1 conserve su valor y el resto se convierta en NA (transparente), para luego dibujarla sobre el RGB.
Crear una capa donde solo los píxeles de categoría 1 existen
solo_cat1 <- ifel(raster_kmeans == 1, 1, NA)
Para visualizarlo, primero dibujamos la imagen RGB en color natural y luego superponemos la categoría seleccionada con un color llamativo (por ejemplo, rojo).
plotRGB(imagen, r=4, g=3, b=2, axes=TRUE, stretch="lin") # Dibuja la imagen RGB
plot(solo_cat1, col = "red", add = TRUE, legend = FALSE) # Superponer la categoría 1
Una vez realizada una interpretación preliminar, hay que identificar
los grupos o clusters (categorías espectrales) en forma de coberturas
reales del territorio (clases de información). Dado que el objeto ráster
raster_kmeans es un ráster cuantitativo, se debe
transformar los números en variables categóricas, convirtiéndolos en
factor (variable categórica), Una vez conozcamos la relación con las
clases de información, se dará nombre a estas categorías
raster_kmeans <- as.factor(raster_kmeans)
Si ya supiéramos a qué clases de información corresponden las clases espectrales, se deberían asignar etiquetas, por ejemplo
levels(raster_kmeans) <- data.frame(ID = 1:5,
clase = c("Suelo desnudo","Regadío","Agua", "Matorral", "Secano"))
Una vez creado este nuevo ráster con los resultados de la clasificación, se puede representar gráficamente, asignando etiquetas de color y clase según la interpretación visual (modo ‘classes’)
mis_colores <- c("Suelo desnudo" = "red",
"Regadío" = "darkgreen",
"Agua" = "blue",
"Matorral" = "tan",
"Secano" = "lightgreen")
# Verifica los niveles del raster como factor
levels(raster_kmeans)
## [[1]]
## ID clase
## 1 1 Suelo desnudo
## 2 2 Regadío
## 3 3 Agua
## 4 4 Matorral
## 5 5 Secano
Finalmente, representaríamos gráficamente el mapa con ggplot2
ggR(raster_kmeans, geom_raster = TRUE, stretch = "lin", forceCat = TRUE) +
ggtitle("Clasificación no supervisada. Método K-means") + # Título
labs(x ="Longitud (m)", y ="Latitud (m)") + # Etiquetas de los ejes
theme(plot.title = element_text(hjust =0.5, # Alineación del título
size =20), # Tamaño del título
axis.title = element_text(size =10), # Tamaño de las etiquetas de los ejes
legend.key.size =unit(1,"cm"), # Tamaño de la leyenda
legend.title = element_text(size = 20), # Tamaño del título de la leyenda
legend.text = element_text (size = 10)) + # Tamaño del texto de la leyenda
# Dibuja los colores según un gradiente
scale_fill_manual(values = mis_colores,
name = "Clase") +
theme_minimal()
Si todo está correcto, el ráster con las categorías obtenidas se
graba como fichero *.tif
writeRaster(raster_kmeans, "D:/G174_2026/LABORATORIO_7_Clasificacion_imagenes_PIXEL/datos/no_supervisada/imagen_cluster_kmeans.tif", overwrite = TRUE)
RStoolboxUna posible alternativa es realizar directamente la clasificación con
K-means sobre el objeto ráster mediante el argumento
algorithm = "Lloyd"en la función
RStoolbox::unsuperClass() del paquete
RStoolbox.
kmeans_rstoolbox <- unsuperClass(imagen,
nSamples = 10000,
nClasses = 5,
nStarts = 200,
algorithm ="Lloyd")
kmeans_rstoolbox$model
kmeans_rstoolbox$map
📝 ACTIVIDAD DE EVALUACIÓN 1:
► Comprueba si los resultados de la clasificación utilizando ambos algoritmos son similares
CLARA es la abreviatura de Clustering Large Applications, un método de agrupamiento propuesto por Kaufman y Rousseeuw (1990). A diferencia de K-means, es más robusto frente a valores atípicos, ya que utiliza medoides en lugar de centroides. A diferencia de estos últimos, el medoide de una clase corresponde a una observación real del conjunto de datos cuya disimilitud total respecto al resto de los puntos del clúster es mínima; es decir, representa el punto más central del grupo.
El procedimiento para calcular los medoides consiste en extraer múltiples muestras del conjunto de datos original, sobre las cuales se aplica el algoritmo PAM (Partitioning Around Medoids) para encontrar el número de medoides especificado por el usuario (equivalente al número de grupos o clústeres). Este algoritmo comienza con un conjunto inicial de medoides seleccionados aleatoriamente y los reemplaza iterativamente con el objetivo de minimizar el coste de intercambio.
Una vez obtenidos los medoides óptimos, cada observación se asigna al medoide más cercano y se calcula la disimilitud correspondiente. Este proceso de muestreo y agrupamiento se repite varias veces, y finalmente se selecciona como resultado el agrupamiento que presenta la menor disimilitud global.
CLARA se aplica usando la función cluster:clara
disponible en el paquete cluster. Esta función se
acompaña de los siguientes argumentos:
Una matrix creada a partir de los datos raster.
k es el número de conglomerados (clases).
sampleses el número de muestras extraidas del
conjunto inicial de datos.
metric es la unidad de medida que sirve para la
separación entre puntos.
cluster_clara <- clara(df_muestra,
k = 5,
metric = "euclidean")
cluster_clara
## Call: clara(x = df_muestra, k = 5, metric = "euclidean")
## Medoids:
## blue green red NIR SWIR1 SWIR2
## 626731 -0.44339608 -0.4679639 -0.6297139 -0.3520198 -0.04224666 0.1763737
## 696807 -1.20425681 -1.0992289 -1.0210409 0.5852803 -0.76658613 -1.1039912
## 62988 0.63334835 0.8050984 0.9391629 0.2945950 0.70766293 0.6413934
## 873048 0.06221418 -0.3499968 -0.5567613 -1.8031165 -1.92199894 -1.7425306
## 1855403 1.13309154 1.5817158 1.9593092 0.9701422 1.63384855 1.1685252
## Objective function: 1.095004
## Clustering vector: Named int [1:186376] 1 2 3 4 1 3 2 2 2 1 2 3 1 1 3 5 4 5 ...
## - attr(*, "names")= chr [1:186376] "1237518" "134058" "1172598" "685285" "1274894" "1413785" "1697371" ...
## Cluster sizes: 55579 36429 51868 21899 20601
## Best sample:
## [1] 1668835 812110 1067226 109947 1199368 946613 1206283 1512265 1131586
## [10] 1849201 1383146 1832605 830056 256836 696807 526862 479003 1220839
## [19] 382215 1238142 996009 407359 1668102 1305796 1780429 873048 1629544
## [28] 1058231 626731 270388 205419 365294 21814 521069 1492551 933172
## [37] 122966 1223498 443919 1855403 914489 444293 1525222 62988 1856997
## [46] 437575 528966 577143 368884 68137
##
## Available components:
## [1] "sample" "medoids" "i.med" "clustering" "objective"
## [6] "clusinfo" "diss" "call" "silinfo" "data"
La salida proporcionada por la función incluye también una gran cantidad de información, entre los que cabe destacar:
la función objetivo para el agrupamiento final de todo el conjunto de datos,
el vector de agrupamiento que representa la identidad del grupo de cada píxel clasificado,
el tamaño de cada grupo,
la mejor muestra, es decir, el número de píxel o celda de la mejor muestra utilizada por CLARA algoritmo para la partición final y componentes disponibles.
cluster_clara$sample # Número de los píxeles que corresponden a la mejor muestra usada para la partición final
## [1] "1668835" "812110" "1067226" "109947" "1199368" "946613" "1206283"
## [8] "1512265" "1131586" "1849201" "1383146" "1832605" "830056" "256836"
## [15] "696807" "526862" "479003" "1220839" "382215" "1238142" "996009"
## [22] "407359" "1668102" "1305796" "1780429" "873048" "1629544" "1058231"
## [29] "626731" "270388" "205419" "365294" "21814" "521069" "1492551"
## [36] "933172" "122966" "1223498" "443919" "1855403" "914489" "444293"
## [43] "1525222" "62988" "1856997" "437575" "528966" "577143" "368884"
## [50] "68137"
cluster_clara$medoids # Medoids de los grupos
## blue green red NIR SWIR1 SWIR2
## 626731 -0.44339608 -0.4679639 -0.6297139 -0.3520198 -0.04224666 0.1763737
## 696807 -1.20425681 -1.0992289 -1.0210409 0.5852803 -0.76658613 -1.1039912
## 62988 0.63334835 0.8050984 0.9391629 0.2945950 0.70766293 0.6413934
## 873048 0.06221418 -0.3499968 -0.5567613 -1.8031165 -1.92199894 -1.7425306
## 1855403 1.13309154 1.5817158 1.9593092 0.9701422 1.63384855 1.1685252
cluster_clara$i.med # Índices de los medoids
## [1] 119088 66931 157972 114102 149483
cluster_clara$objective # Funcion objetiva para la clasificación final de todo el conjunto de datos
## [1] 1.095004
cluster_clara$clusinfo # Información de cada cluster
## size max_diss av_diss isolation
## [1,] 55579 2.793793 1.1137020 1.367622
## [2,] 36429 3.818205 1.0809110 1.869094
## [3,] 51868 5.042190 1.0946160 2.700714
## [4,] 21899 3.604881 0.9754548 1.163699
## [5,] 20601 26.642860 1.1975371 14.270537
El componente objective es el más importante, pues mide
cuán compactos son los clústeres. Si comparamos varias soluciones con
diferente número de grupos, aquella solución con un valor más bajo
corresponde a la mejor clasificación. Permite comparar entre distintas
ejecuciones o distintos k
El componente clusinfo proporciona información
sobre:
El número de observaciones en cada grupo (su tamaño).
La disimilitud máxima y su promedio entre las observaciones en cada grupo y su respectivo medoide, “max_diss” y “av_diss”’ respectivamente.
La ratio entre la máxima disimilitud entre las observaciones del grupo y el medoide del grupo con respecto a la mínima disimilitud entre el medoide del grupo y el medoide de cualquier otro grupo (“isolation”). Un valor pequeño de este último indicador indica que el clúster está bien separado de los otros clústeres. La matriz de disimilitud se puede obtener del componente ‘diss’ como se muestra a continuación.
cluster_clara$diss
La lista con información acerca del “silouette width” para la mejor
muestra es proporcionada por el argumento silinfo.
cluster_clara$silinfo
Extender la clasificación a todo el dataframe escalado
df_clara_completo <- clara(df_escalado, # dataframe escalado (con todos los píxeles)
k = 5)
El resultado del procedimiento CLARA también es un dataframe, por lo que deberás ser convertido en un raster, siguiendo el mismo procedimiento mencionado en K-means.
raster_clara <- imagen[[1]]
values(raster_clara) <- df_clara_completo$clustering
raster_clara
## class : SpatRaster
## size : 1245, 1497, 1 (nrow, ncol, nlyr)
## resolution : 30, 30 (x, y)
## extent : 594090, 639000, 4190190, 4227540 (xmin, xmax, ymin, ymax)
## coord. ref. : WGS 84 / UTM zone 10N (EPSG:32610)
## source(s) : memory
## varname : imagen
## name : coastal aerosol
## min value : 1
## max value : 5
📝 ACTIVIDAD DE EVALUACIÓN 2:
► Muestra la distribución espacial de categorías obtenidas al aplicar el algoritmo CLARA, superpuesta a una imagen en color verdadero
► Representa superpuesta a una imagen en color natura la categoría 3.
► Transforma ese objeto ráster en un ráster categórico
► Identifica qué tipo de uso del territorio corresponde a cada una de las categorías espectrales y asigna las correspondientes etiquetas.
► Asigna a las etiquetas unos colores que se corresponda (aproximadamente) con el tipo de uso del territorio.
► Representa con la función ggr() el mapa temático
correspondiente a la clasificación realizada con el algorimo CLARA.
Si todo está correcto, el ráster con las categorías obtenidas se
graba como fichero *.tif
writeRaster(raster_clara, "D:/G174_2026/LABORATORIO_7_Clasificacion_imagenes_PIXEL/datos/no_supervisada/imagen_cluster_clara.tif", overwrite = TRUE)
El algoritmo Random Forest es una técnica de aprendizaje automático utilizada tanto para clasificación como para regresión. Este método construye múltiples árboles de decisión a partir de subconjuntos del conjunto de datos de entrenamiento, combinando sus resultados para mejorar la precisión y reducir el sobreajuste.
Normalmente, este algoritmo se emplea en procedimientos de clasificación supervisada. No obstante, también puede utilizarse en enfoques no supervisados de forma indirecta, por ejemplo, en combinación con algoritmos como K-means. En este caso, K-means genera agrupamientos iniciales a partir de los datos, y estas etiquetas se utilizan posteriormente para entrenar un modelo Random Forest. Una vez entrenado, el modelo puede predecir la distribución de clases en la imagen de satélite original.
El algorítmo necesita una gran cantidad de RAM, a diferencia de lo que ocurrió con K-means y CLARA. Por ello, se debe utilizar habitualmente una muestra reducida. Primero, se tomarán 1000 muestras aleatorias (sin reemplazo) del fichero ráster original (ìmagenpara obtener los valores de las bandas
raster_muestra_rf <- spatSample(imagen,
size = 1000,
method = "random",
as.points = TRUE)
A continuación, se extraer los valores de las bandas en esos puntos y se convierte en dataframe
df_muestra_rf <- terra::extract(imagen, raster_muestra_rf)
Para que los resultados sean comparables a los de K-means y CLARA, eliminamos las columnas 1 (ID) y 2 (coastal aerosol).
df_muestra_rf <- df_muestra_rf[, -c(1,2)]
Finalmente, comprobamos el éxito de todo el procedimiento
names(df_muestra_rf)
## [1] "blue" "green" "red" "NIR" "SWIR1" "SWIR2"
A continuación se aplica el algorítmo Random Forest a dicha muestra
para calcular los valores de proximidad. Este valor se basa
en la frecuencia con la que los pares de puntos se encuentran en los
mismos nodos terminales, y se asigna un nombre de variable. Este
procedimiento se aplicar a la anterior matriz utilizando la función
randomForest del paquete randomForest.
set.seed(123)
rf_provisional <- randomForest(df_muestra_rf,
ntree = 500,
proximity = TRUE,
oob.prox = TRUE)
Aplicado a los datos, randomForest genera una matriz de
proximidad basada en cómo los puntos caen juntos en los árboles, que
responde a rf_provisional$proximity
Primero se debería aplicar el algoritmo randomForest a la muestra sin ningún entrenamiento previo. Obtenemos una lista
rf_provisional <- randomForest(df_muestra_rf)
names(rf_provisional)
## [1] "call" "type" "predicted" "err.rate"
## [5] "confusion" "votes" "oob.times" "classes"
## [9] "importance" "importanceSD" "localImportance" "proximity"
## [13] "ntree" "mtry" "forest" "y"
## [17] "test" "inbag"
Posteriormente, de esa lista se extrae un elemento, denominado
proximity y se convierte en un vector.
rf_proximity <- rf_provisional$proximity # Asigna una variable a las medidas de la matriz de proximidad
Es sobre estos valores de proximidad sobre los que se aplica el
algorítmo K-means para calcular el número de grupos (en este caso 5).
Estos grupos se utilizan posteriormente para entrenar el algorítmo
Random Forest, siendo el número de árbles a construir definido por el
argumento ntree.
cluster_kmn_rf <- kmeans(rf_proximity,
centers =5,
iter.max =10000)
rf_train <- randomForest(df_muestra_rf,
as.factor(cluster_kmn_rf$cluster),
ntree =2000)
rf_train # Atributos del nuevo objeto
##
## Call:
## randomForest(x = df_muestra_rf, y = as.factor(cluster_kmn_rf$cluster), ntree = 2000)
## Type of random forest: classification
## Number of trees: 2000
## No. of variables tried at each split: 2
##
## OOB estimate of error rate: 3.9%
## Confusion matrix:
## 1 2 3 4 5 class.error
## 1 101 0 1 0 0 0.009803922
## 2 0 178 0 1 8 0.048128342
## 3 2 0 166 0 6 0.045977011
## 4 0 5 0 164 0 0.029585799
## 5 1 9 6 0 352 0.043478261
La matriz de confusión y la estimación OOB (Out-of-Bag) de la tasa de
error indican que el modelo desarrollado por randomForest tiene una
buena precisión. Cuando esto ocurre, se puede usar el modelo para la
predicción de clases en todo el ráster usando la función
predict.
raster_rf <- predict(imagen,
rf_train)
raster_rf # Atributos
## class : SpatRaster
## size : 1245, 1497, 1 (nrow, ncol, nlyr)
## resolution : 30, 30 (x, y)
## extent : 594090, 639000, 4190190, 4227540 (xmin, xmax, ymin, ymax)
## coord. ref. : WGS 84 / UTM zone 10N (EPSG:32610)
## source(s) : memory
## categories : class
## name : class
## min value : 1
## max value : 5
Al igual que en el caso anterior, la interpretación básica consistirá en superponer los píxeles clasificados a la imagen en color verdadero
plotRGB(imagen, r=4, g=3, b=2, axes=TRUE, stretch="lin")
plot(raster_rf, col = rainbow(5),
main = 'Visualización preliminar', add=TRUE, alpha = 0.4, legend = TRUE)
A continuación, se representa gráficamente el raster clasificado según el método randomForest, al igual que se realizó con kmeans y CLARA.
ggR(raster_rf, geom_raster = TRUE, stretch = "lin") +
ggtitle("Clasificación no supervisada según el método Random Forest") + # Título
labs(x ="Longitud (m)", y ="Latitud (m)") + # Etiquetas de los ejes
theme(plot.title = element_text(hjust =0.5, # Alineación del título
size =20), # Tamaño del título
axis.title = element_text(size =10), # Tamaño de las etiquetas de los ejes
legend.key.size =unit(1,"cm"), # size of the legend
legend.title = element_text(size = 20), # size of Legend tittle
legend.text = element_text (size = 10)) + # size of legend text
# Dibuja los colores según un gradiente
scale_fill_manual(name = "Class",
values = c("black", "forestgreen", "burlywood", "red","yellow","blue"),
labels = c("Suelos salinos", "Vegetación", "Cultivos", "Edificaciones","Agua", "Suelos arenosos"))
Si todo está correcto, el ráster con las categorías obtenidas se
graba como fichero *.tif
writeRaster(raster_rf, "D:/G174_2026/LABORATORIO_7_Clasificacion_imagenes_PIXEL/datos/no_supervisada/imagen_cluster_rf.tif", overwrite = TRUE)
El Fuzzy C-Means (FCM) es un algoritmo de agrupamiento (clustering) que pertenece a la denominada “lógica difusa”. A diferencia de los métodos tradicionales “duros” (hard clustering), donde cada punto pertenece a un solo grupo, en FCM los datos pueden pertenecer a varios grupos simultáneamente con distintos grados de pertenencia.
Sus principales características son las siguientes:
Pertenencia difusa: cada observación tiene un valor de pertenencia (entre 0 y 1) para cada clúster. Un píxel en una imagen satelital, por ejemplo, podría ser 70% “bosque” y 30% “suelo desnudo”.
El grado de pertenencia viene dado por el Parámetro de Difusión (m).
Proceso Iterativo: el algoritmo actualiza los centros de los clústeres y los grados de pertenencia en cada paso hasta que los centros dejan de moverse significativamente.
Manejo de la ambigüedad: Es ideal para datos que no tienen fronteras claras, como áreas de transición en vegetación (ecotonos) o imágenes médicas donde los tejidos se mezclan.
Mayor detalle: al obtener una matriz de probabilidad (membership), tienes mucha más información que una simple etiqueta. Puedes saber qué tan “seguro” está el modelo de su clasificación.
Robustez: suele ser menos sensible a ruidos o valores atípicos que el K-means tradicional, ya que estos puntos pueden repartir su pertenencia entre varios grupos en lugar de distorsionar un solo centro.
** Limitaciones
Al igual que K-means, todavía necesitas definir de antemano el número de centros (centers=5).
Costo Computacional: Es más lento que K-means porque tiene que calcular y actualizar una matriz de membresía completa en cada iteración.
Sensibilidad a la escala: Al basarse en distancias euclidianas, si no se escala las variables antes de procesarlas, las variables con números más grandes dominarán el agrupamiento.
Mínimos Locales: puede quedarse “atrapado” en una solución que no es la mejor globalmente, por lo que a veces es necesario ejecutarlo varias veces con semillas diferentes.
library(e1071)
## Warning: package 'e1071' was built under R version 4.3.3
##
## Attaching package: 'e1071'
## The following object is masked from 'package:ggplot2':
##
## element
## The following object is masked from 'package:terra':
##
## interpolate
El argumento m = 2 es el parámetro (exponencial) que
controla el grado de separación (difuminado o fuzziness) de las
categorías, es decir, determina cuan “borrosas” son las fronteras entre
grupos. Con un valor m = 1 el algoritmo se comporta casi
como un K-means normal (pertenencia rígida), mientras que a partir de m
> 1 aumenta el grado de solapamiento. El valor 2 es el estándar por
defecto en la literatura científica y suele funcionar bien para la
mayoría de los casos.
fuzzy_inicial <- cmeans(df_muestra,
centers=5, # Número de grupo a automática partiendo de una semilla aleatoria.
m=2)
Generación de la clasificación definitiva a partir de los centros obtenidos previamente.
fuzzy_completo <- cmeans(df_escalado,
centers= fuzzy_inicial$centers,
m=2)
Creación del fichero ráster con los resultados
raster_fuzzy <- imagen[[1]]
values(raster_fuzzy) <- fuzzy_completo$cluster
raster_fuzzy
## class : SpatRaster
## size : 1245, 1497, 1 (nrow, ncol, nlyr)
## resolution : 30, 30 (x, y)
## extent : 594090, 639000, 4190190, 4227540 (xmin, xmax, ymin, ymax)
## coord. ref. : WGS 84 / UTM zone 10N (EPSG:32610)
## source(s) : memory
## varname : imagen
## name : coastal aerosol
## min value : 1
## max value : 5
Al igual que en el caso anterior, la interpretación básica consistirá en superponer los píxeles clasificados a la imagen en color verdadero
plotRGB(imagen, r=4, g=3, b=2, axes=TRUE, stretch="lin")
plot(raster_fuzzy, col = rainbow(5),
main = 'Visualización preliminar', add=TRUE, alpha = 0.4, legend = TRUE)
Se puede representar también la probabilidad de pertenencia a un determinado cluster. Esto es muy útil para identificar zonas de transición o áreas donde la clasificación es ambigua.
matriz_pertenencia <- fuzzy_completo$membership
prob_cluster1 <- imagen[[1]]
values(prob_cluster1) <- fuzzy_completo$membership[, 1]
plot(prob_cluster1)
writeRaster(raster_fuzzy, "D:/G174_2026/LABORATORIO_7_Clasificacion_imagenes_PIXEL/datos/no_supervisada/imagen_cluster_fuzzy.tif", overwrite = TRUE)
📝 ACTIVIDAD DE EVALUACIÓN 3:
Descarga el fichero landsat_calgary.zip.
► Realiza una clasificación no supervisada de la imagen utilizando para ello al menos dos algorítmos.
► Identifica qué tipo de supeficies corresponde a cada categoría extraida por los algorítmos. La imagen corresponde a la ciudad canadiense de Calgary. Puedes ayudarte de Google Earth para tener una información más detallada.
► Elabora sendos mapas temáticos con ambas clasificaciones.