Introducción

El objetivo de este trabajo es aplicar el método de clustering no jerárquico K-Medianas (PAM — Partitioning Around Medoids) a un conjunto de indicadores macroeconómicos obtenidos directamente de la API del Fondo Monetario Internacional (FMI) mediante el paquete imf.data.

Se utilizan tres variables del World Economic Outlook (WEO), todas expresadas en porcentajes y en su versión de proyecciones 2027-2031:

  • Crecimiento del PIB real (NGDP_RPCH).
  • Inflación, IPC promedio (PCPIPCH).
  • Tasa de desempleo (LUR).

Con ellas se agrupan 18 países según su perfil macroeconómico proyectado. Es importante precisar que el resultado describe similitudes esperadas según el FMI; no constituye un pronóstico de cómo evolucionarán los grupos.

A diferencia del clustering jerárquico (p. ej. Ward), en el clustering no jerárquico el número de grupos (k) se define antes de correr el algoritmo, y cada observación se asigna al grupo cuyo representante le resulte más parecido. Dentro de los métodos particionales existen dos variantes principales:

  • K-Medias (K-Means): el representante es un centroide, un promedio artificial que puede no coincidir con ningún país real. Es sensible a valores atípicos porque la media se distorsiona con outliers.
  • K-Medianas / PAM: el representante es un medoide, la observación real más central del grupo. Al elegir un representante existente en lugar de promediar, es mucho más robusto ante valores atípicos.

En la sección 8 se agrega el método jerárquico aglomerativo de Ward, aplicado a cuatro indicadores del Banco Mundial (paquete wbstats) para los mismos 18 países, de modo que ambos enfoques (particional y jerárquico) puedan contrastarse.

1. Clasificación Lógica Económica

1.1 ¿En qué consiste el método?

PAM es un algoritmo de clustering particional (no jerárquico): parte de un número fijo k de grupos definido de antemano y asigna cada observación a uno de ellos, sin construir árbol ni dendrograma. El procedimiento es:

  1. Se seleccionan k observaciones reales como medoides iniciales (fase BUILD).
  2. Cada observación restante se asigna al medoide más cercano (distancia Manhattan).
  3. Para cada grupo se evalúa si intercambiar el medoide por otra observación reduce la suma total de disimilitudes (fase SWAP).
  4. Se repiten los pasos 2 y 3 hasta que ningún intercambio mejore la solución (convergencia).

Al ser particional, no es jerárquico: no hay una secuencia de fusiones ni dendrograma, y una observación puede cambiar de grupo entre iteraciones (en los métodos jerárquicos, una fusión es irrevocable).

1.2 ¿Por qué PAM es idóneo para los datos del FMI?

Los indicadores del FMI (crecimiento, inflación, desempleo) son series volátiles y suelen incluir valores atípicos genuinos: economías con inflación muy alta o con desempleo estructural elevado que se apartan mucho del resto sin que sea un error de medición.

  • K-Means usa la media como representante. Un solo país extremo puede “arrastrar” el centroide y distorsionar la asignación de los demás.
  • PAM usa un medoide real. Su posición no se ve arrastrada por un valor extremo y el resultado es más interpretable: “este grupo se parece al perfil de tal país”, en lugar de un promedio artificial.

Además, al estar las tres variables en porcentajes, son comparables e interpretables directamente. Aun así se aplica scale(), porque tienen dispersiones muy distintas (la inflación puede variar mucho más que el desempleo) y sin estandarizar dominaría el cálculo de distancias.

2. Explicación Matemática (intuitiva)

PAM minimiza la suma total de disimilitudes entre cada observación y el medoide de su grupo:

\[\text{TD} = \sum_{i=1}^{k} \sum_{x \in C_i} d(x, m_i)\]

donde \(C_i\) es el grupo \(i\), \(m_i\) su medoide (observación real) y \(d(x, m_i)\) la distancia entre \(x\) y su medoide.

Distancia Manhattan: PAM suele emplear la distancia de bloques en lugar de la euclídea:

\[d(x_i, x_j) = \sum_{l=1}^{p} |x_{il} - x_{jl}|\]

En lugar de elevar las diferencias al cuadrado (lo que penaliza fuertemente las grandes), suma sus valores absolutos, por lo que un valor extremo en una variable no domina la distancia total. Aquí \(p = 3\): PIB, inflación y desempleo.

En términos económicos: en cada iteración el algoritmo se pregunta “si en vez de este país uso otro país real como representante del grupo, ¿se reduce la distancia total de los países del grupo hacia su representante?”. Si la respuesta es sí, cambia el medoide. Converge cuando ningún cambio mejora la cohesión del grupo.

3. Ventajas y Desventajas Comparativas

Método Tipo Ventajas frente a PAM Desventajas frente a PAM
Ward (Grupo 1) Jerárquico aglomerativo No requiere fijar k de antemano; dendrograma muy interpretable Una asignación no puede revertirse; más costoso con muestras grandes
DBSCAN (Grupo 2) No jerárquico, basado en densidad Detecta ruido/outliers como categoría propia; no requiere fijar k Sensible a Épsilon y MinPts; falla si los grupos tienen densidades muy distintas
K-Means (Grupo 4) No jerárquico, particional Más rápido y escalable a muestras muy grandes Usa centroides (promedios): muy sensible a outliers; los centros pueden no ser países reales
PAM / K-Medianas (Grupo 3) No jerárquico, particional Robusto ante outliers; el representante es un país real e interpretable Más costoso computacionalmente que K-Means; requiere fijar k

Síntesis: PAM combina la lógica de un método particional con una robustez ante valores atípicos que ni K-Means ni Ward ofrecen, lo que lo hace adecuado para indicadores del FMI, donde la volatilidad y los casos extremos son frecuentes.

4. Selección del Número Óptimo de Clústeres

Entre las métricas generales (Método del Codo, Coeficiente de Silueta, Dendrograma, Gráfico de K-Vecinos), la que corresponde a PAM es el Coeficiente de Silueta:

  • El método del codo se basa en la suma de cuadrados intra-grupo, medida pensada para centroides (K-Means).
  • El dendrograma es propio de los métodos jerárquicos (Ward).
  • El gráfico de k-vecinos es la herramienta de DBSCAN para elegir Épsilon.
  • El coeficiente de silueta usa directamente las disimilitudes entre observaciones (compatible con Manhattan) y es la métrica estándar para particiones basadas en medoides.

Para cada observación \(i\):

\[s(i) = \frac{b(i) - a(i)}{\max\{a(i), b(i)\}}\]

donde \(a(i)\) es la distancia promedio de \(i\) a los demás miembros de su clúster (cohesión) y \(b(i)\) la distancia promedio al clúster vecino más cercano (separación). Valores cercanos a 1 indican buena asignación; cercanos a 0 o negativos, que la observación podría pertenecer a otro grupo. Se elige el k con mayor silueta promedio.

5. Ejemplo Práctico en R

5.1 Selección de países

Se seleccionaron 18 países que combinan economías latinoamericanas con economías avanzadas, para que el algoritmo tenga suficiente variabilidad y se pueda observar el efecto de posibles outliers.

library(dplyr)
library(tibble)
library(kableExtra)

paises_iso3 <- c(
  "SLV", "GTM", "HND", "NIC", "CRI", "PAN",   # Centroamérica
  "MEX", "COL", "PER", "CHL", "ARG", "BRA",   # Resto de Latinoamérica
  "USA", "CAN",                                # Norteamérica
  "ESP", "DEU", "FRA", "ITA"                   # Economías avanzadas (referencia)
)

nombres_paises <- c(
  "El Salvador", "Guatemala", "Honduras", "Nicaragua", "Costa Rica", "Panamá",
  "México", "Colombia", "Perú", "Chile", "Argentina", "Brasil",
  "Estados Unidos", "Canadá",
  "España", "Alemania", "Francia", "Italia"
)

Tabla_paises <- tibble(ISO3 = paises_iso3, Pais = nombres_paises)

Tabla_paises %>%
  kable(caption = "Países incluidos en el análisis de clustering") %>%
  kable_classic(html_font = "Times New Roman", font_size = 14) %>%
  add_footnote(label = "Elaboración propia", notation = "symbol") %>%
  kable_styling(full_width = TRUE)
Países incluidos en el análisis de clustering
ISO3 Pais
SLV El Salvador
GTM Guatemala
HND Honduras
NIC Nicaragua
CRI Costa Rica
PAN Panamá
MEX México
COL Colombia
PER Perú
CHL Chile
ARG Argentina
BRA Brasil
USA Estados Unidos
CAN Canadá
ESP España
DEU Alemania
FRA Francia
ITA Italia
* Elaboración propia

5.2 Carga de librerías

library(imf.data)
library(tidyr)
library(ggplot2)
library(cluster)      # PAM y silueta
library(factoextra)   # visualización de clusters y silueta

5.3 Estructura del dataset WEO e indicadores

Estructura_WEO <- list_dimensions("WEO", agency_id = "IMF.RES")

Estructura_WEO %>%
  kable(caption = "Estructura del dataset WEO en la API del FMI") %>%
  kable_classic(html_font = "Times New Roman", font_size = 14) %>%
  add_footnote(label = "Tomado de la API del FMI", notation = "symbol") %>%
  kable_styling()
Estructura del dataset WEO en la API del FMI
position id type name codelist_agency codelist_id codelist_version
0 COUNTRY Dimension Country IMF.RES CL_WEO_COUNTRY 1.0+.0
1 INDICATOR Dimension Indicator IMF.RES CL_WEO_INDICATOR 2.0+.0
2 FREQUENCY Dimension FREQUENCY NA NA NA
3 TIME_PERIOD TimeDimension TIME_PERIOD NA NA NA
* Tomado de la API del FMI
Indicadores_WEO <- list_dimension_values("WEO", "INDICATOR", agency_id = "IMF.RES")

Indicadores_WEO %>%
  filter(if_any(everything(), ~ .x %in% c("NGDP_RPCH", "PCPIPCH", "LUR"))) %>%
  kable(caption = "Indicadores del WEO utilizados") %>%
  kable_classic(html_font = "Times New Roman", font_size = 14) %>%
  kable_styling()
Indicadores del WEO utilizados
code name description
LUR Unemployment rate The percentage of the labor force that is unemployed and actively seeking employment. It is calculated as the number of unemployed persons divided by the total labor force. A measure that expresses the frequency of occurrence of an event or phenomenon in relation to another quantity.
NGDP_RPCH Gross domestic product (GDP), Constant prices, Percent change Gross domestic product reflects the total income earned through the production of goods and services in an economic territory during an accounting period. It can be measured in three different ways: using either the expenditure approach, the income approach, or the production approach. Values that have had the effect of price changes removed, expressed as time series in constant prices (fixed base) or chain-linked volume terms, or as non-time series in previous year’s prices This measures the relative change in a value over time, expressed as a percentage. It is calculated as the difference between the new value and the old value, divided by the old value, and multiplied by 100. 1 A ratio expressed as a fraction of 100.
PCPIPCH All Items, Consumer price index (CPI), Period average, percent change All items in the context of the consumer price index indicates that the index includes prices for all items included in the consumer price index basket. This includes prices related to food, beverages, clothing, housing, health, transport, communications, recreation, education, restaurants and hotels and other miscellanoues goods and services. The Consumer Price Index (CPI) is an index of the prices of consumption goods and services, as compared to a certain reference period. This calculates the change relative to the average value over the entire period, not just the starting point. This is often used when the data fluctuates significantly within the period, and the average provides a more representative value for comparison. A ratio expressed as a fraction of 100.

5.4 Variable 1: Crecimiento del PIB real (WEO)

Se descargan las tres variables del WEO en el mismo formato: un indicador, los 18 países, frecuencia anual, y se conserva el período de proyección 2027-2031 (ventana común a las tres series). Luego se calcula el promedio 2027-2031 por país, que resume su perfil proyectado.

Producto Interno Bruto a precios constantes, cambio porcentual (NGDP_RPCH).

PIB_WEO <- get_data(
  "WEO",
  agency_id = "IMF.RES",
  filters = list(
    INDICATOR = "NGDP_RPCH",
    COUNTRY = paises_iso3,
    FREQUENCY = "A"
  )
)

# Se filtran las proyecciones 2027-2031 y se calcula el promedio por país
PIB_prom <- PIB_WEO %>%
  mutate(TIME_PERIOD = as.numeric(TIME_PERIOD),
         OBS_VALUE = as.numeric(OBS_VALUE)) %>%
  filter(TIME_PERIOD >= 2027 & TIME_PERIOD <= 2031) %>%
  group_by(COUNTRY) %>%
  summarise(Crecimiento_PIB = mean(OBS_VALUE, na.rm = TRUE), .groups = "drop") %>%
  mutate(Crecimiento_PIB = ifelse(is.nan(Crecimiento_PIB), NA_real_, Crecimiento_PIB))

PIB_prom %>%
  mutate(Crecimiento_PIB = round(Crecimiento_PIB, 2)) %>%
  kable(caption = "PIB (crecimiento real, %): promedio proyectado 2027-2031") %>%
  kable_classic(html_font = "Times New Roman", font_size = 14) %>%
  add_footnote(label = "Tomado de la API del FMI (WEO)", notation = "symbol") %>%
  kable_styling()
PIB (crecimiento real, %): promedio proyectado 2027-2031
COUNTRY Crecimiento_PIB
ARG 3.50
BRA 2.36
CAN 1.74
CHL 2.35
COL 2.68
CRI 3.51
DEU 0.92
ESP 1.75
FRA 1.08
GTM 3.91
HND 3.78
ITA 0.70
MEX 2.07
NIC 3.58
PAN 4.58
PER 2.80
SLV 3.00
USA 1.92
* Tomado de la API del FMI (WEO)

5.5 Variable 2: Inflación (WEO)

Variación porcentual del Índice de Precios al Consumidor, promedio del período (PCPIPCH).

Inflacion_WEO <- get_data(
  "WEO",
  agency_id = "IMF.RES",
  filters = list(
    INDICATOR = "PCPIPCH",
    COUNTRY = paises_iso3,
    FREQUENCY = "A"
  )
)

# Se filtran las proyecciones 2027-2031 y se calcula el promedio por país
Inflacion_prom <- Inflacion_WEO %>%
  mutate(TIME_PERIOD = as.numeric(TIME_PERIOD),
         OBS_VALUE = as.numeric(OBS_VALUE)) %>%
  filter(TIME_PERIOD >= 2027 & TIME_PERIOD <= 2031) %>%
  group_by(COUNTRY) %>%
  summarise(Inflacion = mean(OBS_VALUE, na.rm = TRUE), .groups = "drop") %>%
  mutate(Inflacion = ifelse(is.nan(Inflacion), NA_real_, Inflacion))

Inflacion_prom %>%
  mutate(Inflacion = round(Inflacion, 2)) %>%
  kable(caption = "Inflación (IPC, %): promedio proyectado 2027-2031") %>%
  kable_classic(html_font = "Times New Roman", font_size = 14) %>%
  add_footnote(label = "Tomado de la API del FMI (WEO)", notation = "symbol") %>%
  kable_styling()
Inflación (IPC, %): promedio proyectado 2027-2031
COUNTRY Inflacion
ARG 9.54
BRA 3.07
CAN 2.02
CHL 3.07
COL 3.51
CRI 2.57
DEU 2.14
ESP 2.15
FRA 1.83
GTM 3.91
HND 4.10
ITA 2.13
MEX 3.08
NIC 3.00
PAN 2.00
PER 1.94
SLV 1.84
USA 2.18
* Tomado de la API del FMI (WEO)

5.6 Variable 3: Tasa de desempleo (WEO)

Tasa de desempleo como porcentaje de la fuerza laboral (LUR).

Desempleo_WEO <- get_data(
  "WEO",
  agency_id = "IMF.RES",
  filters = list(
    INDICATOR = "LUR",
    COUNTRY = paises_iso3,
    FREQUENCY = "A"
  )
)

# Se filtran las proyecciones 2027-2031 y se calcula el promedio por país
Desempleo_prom <- Desempleo_WEO %>%
  mutate(TIME_PERIOD = as.numeric(TIME_PERIOD),
         OBS_VALUE = as.numeric(OBS_VALUE)) %>%
  filter(TIME_PERIOD >= 2027 & TIME_PERIOD <= 2031) %>%
  group_by(COUNTRY) %>%
  summarise(Desempleo = mean(OBS_VALUE, na.rm = TRUE), .groups = "drop") %>%
  mutate(Desempleo = ifelse(is.nan(Desempleo), NA_real_, Desempleo))

Desempleo_prom %>%
  mutate(Desempleo = round(Desempleo, 2)) %>%
  kable(caption = "Desempleo (tasa, %): promedio proyectado 2027-2031") %>%
  kable_classic(html_font = "Times New Roman", font_size = 14) %>%
  add_footnote(label = "Tomado de la API del FMI (WEO)", notation = "symbol") %>%
  kable_styling()
Desempleo (tasa, %): promedio proyectado 2027-2031
COUNTRY Desempleo
ARG 6.58
BRA 7.39
CAN 6.14
CHL 7.42
COL 10.00
CRI 7.74
DEU 3.07
ESP 9.91
FRA 7.57
HND 6.56
ITA 6.02
MEX 2.78
NIC 2.76
PAN 7.76
PER 6.46
SLV 4.64
USA 4.00
* Tomado de la API del FMI (WEO)

5.7 Chequeo de los datos descargados

# Cada país debe aparecer una sola vez por variable
stopifnot(!anyDuplicated(PIB_prom$COUNTRY),
          !anyDuplicated(Inflacion_prom$COUNTRY),
          !anyDuplicated(Desempleo_prom$COUNTRY))

# Países sin dato proyectado en cada variable (se imputarán con la media)
faltantes <- tibble(
  Variable = c("Crecimiento_PIB", "Inflacion", "Desempleo"),
  `Países sin dato` = c(
    paste(setdiff(paises_iso3, PIB_prom$COUNTRY[!is.na(PIB_prom$Crecimiento_PIB)]), collapse = ", "),
    paste(setdiff(paises_iso3, Inflacion_prom$COUNTRY[!is.na(Inflacion_prom$Inflacion)]), collapse = ", "),
    paste(setdiff(paises_iso3, Desempleo_prom$COUNTRY[!is.na(Desempleo_prom$Desempleo)]), collapse = ", ")
  )
)

faltantes %>%
  kable(caption = "Países sin dato proyectado por variable") %>%
  kable_classic(html_font = "Times New Roman", font_size = 14) %>%
  kable_styling()
Países sin dato proyectado por variable
Variable Países sin dato
Crecimiento_PIB
Inflacion
Desempleo GTM

5.8 Construcción de la matriz de clustering

Siguiendo las recomendaciones técnicas de la tarea: los datos faltantes (NA) se sustituyen por la media de la variable y luego las tres variables se estandarizan con scale().

Datos_cluster <- Tabla_paises %>%
  left_join(PIB_prom,       by = c("ISO3" = "COUNTRY")) %>%
  left_join(Inflacion_prom, by = c("ISO3" = "COUNTRY")) %>%
  left_join(Desempleo_prom, by = c("ISO3" = "COUNTRY")) %>%
  select(Pais, Crecimiento_PIB, Inflacion, Desempleo)

# Tratamiento de datos faltantes: sustitución por la media de cada variable
Datos_cluster <- Datos_cluster %>%
  mutate(across(c(Crecimiento_PIB, Inflacion, Desempleo),
                ~ ifelse(is.na(.x), mean(.x, na.rm = TRUE), .x)))

# Un solo registro por país (evita el error de row.names duplicados)
stopifnot(!anyDuplicated(Datos_cluster$Pais))

Datos_cluster %>%
  mutate(across(where(is.numeric), ~ round(.x, 2))) %>%
  kable(caption = "Matriz de variables para clustering: promedio proyectado 2027-2031 (%)") %>%
  kable_classic(html_font = "Times New Roman", font_size = 14) %>%
  add_footnote(label = "Elaboración propia con datos del WEO (FMI)", notation = "symbol") %>%
  kable_styling()
Matriz de variables para clustering: promedio proyectado 2027-2031 (%)
Pais Crecimiento_PIB Inflacion Desempleo
El Salvador 3.00 1.84 4.64
Guatemala 3.91 3.91 6.28
Honduras 3.78 4.10 6.56
Nicaragua 3.58 3.00 2.76
Costa Rica 3.51 2.57 7.74
Panamá 4.58 2.00 7.76
México 2.07 3.08 2.78
Colombia 2.68 3.51 10.00
Perú 2.80 1.94 6.46
Chile 2.35 3.07 7.42
Argentina 3.50 9.54 6.58
Brasil 2.36 3.07 7.39
Estados Unidos 1.92 2.18 4.00
Canadá 1.74 2.02 6.14
España 1.75 2.15 9.91
Alemania 0.92 2.14 3.07
Francia 1.08 1.83 7.57
Italia 0.70 2.13 6.02
* Elaboración propia con datos del WEO (FMI)
# El clustering se basa en distancias: se estandarizan las variables con scale()
Datos_matriz <- Datos_cluster %>%
  select(Pais, Crecimiento_PIB, Inflacion, Desempleo) %>%
  column_to_rownames("Pais")

Datos_escalados <- scale(Datos_matriz)

# Comprobación: media ≈ 0 y desviación estándar = 1 en cada variable
round(colMeans(Datos_escalados), 6)
## Crecimiento_PIB       Inflacion       Desempleo 
##               0               0               0
apply(Datos_escalados, 2, sd)
## Crecimiento_PIB       Inflacion       Desempleo 
##               1               1               1

5.9 Número óptimo de clusters (coeficiente de silueta)

Tal como se justificó en la sección 4, el método correcto para PAM es el coeficiente de silueta. Se calcula la silueta promedio para k = 2, …, 8 y se elige el k con el valor máximo.

k_valores <- 2:8

silueta_k <- sapply(k_valores, function(k) {
  pam(Datos_escalados, k = k, metric = "manhattan")$silinfo$avg.width
})

Tabla_silueta <- tibble(k = k_valores, Silueta_promedio = silueta_k)

k_optimo <- Tabla_silueta$k[which.max(Tabla_silueta$Silueta_promedio)]

ggplot(Tabla_silueta, aes(x = k, y = Silueta_promedio)) +
  geom_line(color = "steelblue", linewidth = 1) +
  geom_point(size = 3, color = "steelblue") +
  geom_vline(xintercept = k_optimo, linetype = "dashed", color = "red") +
  scale_x_continuous(breaks = k_valores) +
  labs(title = "Coeficiente de silueta - número óptimo de clusters para PAM",
       subtitle = paste("k óptimo =", k_optimo),
       x = "Número de clusters (k)", y = "Silueta promedio") +
  theme_minimal()

“El análisis del coeficiente de silueta promedio para el algoritmo K-Medianas (PAM) muestra que la estructura de agrupación alcanza su máxima separación y cohesión en \(k = 8\) clusters (con un valor promedio de silueta de 0.36). Por lo tanto, k = 8 es el número óptimo de grupos para segmentar a los 18 países analizados según su perfil macroeconómico proyectado.”

5.10 K-Medianas / PAM

modelo_pam <- pam(Datos_escalados, k = k_optimo, metric = "manhattan")

Datos_cluster$Cluster_PAM <- as.factor(modelo_pam$clustering)

# Países que actúan como medoides (representantes reales de cada grupo)
Medoides <- rownames(modelo_pam$medoids)
Medoides
## [1] "Perú"      "Honduras"  "México"    "Panamá"    "España"    "Chile"    
## [7] "Argentina" "Italia"

El grupo 1 se representa con Perú: es el país más típico de ese grupo, el que mejor resume a los demás

# Con 3 variables, fviz_cluster proyecta los datos sobre los dos primeros
# componentes principales para poder graficar los grupos.
fviz_cluster(modelo_pam,
             data = Datos_escalados,
             ellipse.type = "convex",
             repel = TRUE,
             ggtheme = theme_minimal()) +
  labs(title = "Clustering PAM (K-Medianas) - Perfil macroeconómico proyectado 2027-2031",
       subtitle = paste("Medoides:", paste(Medoides, collapse = ", ")))

# Gráfico de silueta: qué tan bien encaja cada país en su clúster
fviz_silhouette(modelo_pam) +
  labs(title = "Gráfico de silueta - calidad de la partición PAM")
##   cluster size ave.sil.width
## 1       1    2          0.36
## 2       2    2          0.83
## 3       3    3          0.20
## 4       4    2          0.29
## 5       5    2          0.16
## 6       6    2          0.99
## 7       7    1          0.00
## 8       8    4          0.15

Este gráfico responde una pregunta: ¿cada país quedó bien ubicado en su grupo? Cada barra es un país. Si la barra es alta, el país encaja muy bien con sus compañeros. Si es baja, está en la frontera entre dos grupos. Si es negativa, probablemente estaría mejor en otro grupo

Perfil de cada clúster

Perfil <- Datos_cluster %>%
  group_by(Cluster_PAM) %>%
  summarise(N_paises = n(),
            across(c(Crecimiento_PIB, Inflacion, Desempleo), ~ round(median(.x), 2)),
            Paises = paste(Pais, collapse = ", "),
            .groups = "drop")

Perfil %>%
  kable(caption = "Perfil de cada clúster (mediana de cada variable, %)") %>%
  kable_classic(html_font = "Times New Roman", font_size = 14) %>%
  kable_styling()
Perfil de cada clúster (mediana de cada variable, %)
Cluster_PAM N_paises Crecimiento_PIB Inflacion Desempleo Paises
1 2 2.90 1.89 5.55 El Salvador, Perú
2 2 3.84 4.00 6.42 Guatemala, Honduras
3 3 2.07 3.00 2.78 Nicaragua, México, Estados Unidos
4 2 4.05 2.28 7.75 Costa Rica, Panamá
5 2 2.22 2.83 9.96 Colombia, España
6 2 2.36 3.07 7.41 Chile, Brasil
7 1 3.50 9.54 6.58 Argentina
8 4 1.00 2.07 6.08 Canadá, Alemania, Francia, Italia
Datos_cluster %>%
  pivot_longer(c(Crecimiento_PIB, Inflacion, Desempleo),
               names_to = "Variable", values_to = "Valor") %>%
  ggplot(aes(x = Cluster_PAM, y = Valor, fill = Cluster_PAM)) +
  geom_boxplot(alpha = 0.6, outlier.shape = 21) +
  facet_wrap(~ Variable, scales = "free_y") +
  labs(title = "Distribución de cada variable por clúster (proyección 2027-2031, %)",
       x = "Clúster", y = "%") +
  theme_minimal() +
  theme(legend.position = "none")

Este gráfico nos muestra cómo se comporta cada variable dentro de cada grupo. La línea gruesa es el valor típico y la caja muestra qué tan parecidos son los países entre sí. Se ve claro que el grupo 7, Argentina, se separa por su inflación, que el grupo 5 tiene el desempleo más alto y que el grupo 8 crece menos. Confirma que PAM formó los grupos según diferencias reales en las variables. Las cajas de los grupos pequeños son casi líneas porque tienen solo uno o dos países.

Crecimiento: el grupo 4 (Costa Rica, Panamá) es el más alto, cerca de 4%, y el grupo 8 (economías avanzadas) el más bajo, cerca de 1%. Desempleo: el grupo 5 (Colombia, España) es el más alto, casi 10%, y el grupo 3 (Nicaragua, México, EE. UU.) el más bajo, cerca de 3%. Inflación: el grupo 7 (Argentina) se separa del resto, con más de 9%. Los demás quedan entre 2% y 4%.

5.11 Medoides en la escala original

Los medoides seleccionados por PAM, expresados de vuelta en la escala original (%) en lugar de valores estandarizados:

media_x <- attr(Datos_escalados, "scaled:center")
sd_x    <- attr(Datos_escalados, "scaled:scale")

medoides_orig <- sweep(sweep(modelo_pam$medoids, 2, sd_x, "*"), 2, media_x, "+")

Representantes_PAM <- as.data.frame(round(medoides_orig, 2)) %>%
  rownames_to_column("Medoide (país real)")

Representantes_PAM %>%
  kable(caption = "PAM: medoides (países reales), en % de la escala original") %>%
  kable_classic(html_font = "Times New Roman", font_size = 14) %>%
  kable_styling()
PAM: medoides (países reales), en % de la escala original
Medoide (país real) Crecimiento_PIB Inflacion Desempleo
Perú 2.80 1.94 6.46
Honduras 3.78 4.10 6.56
México 2.07 3.08 2.78
Panamá 4.58 2.00 7.76
España 1.75 2.15 9.91
Chile 2.35 3.07 7.42
Argentina 3.50 9.54 6.58
Italia 0.70 2.13 6.02

Los ocho medoides, expresados en su escala original, muestran perfiles macroeconómicos claramente distintos: desde economías de alto crecimiento y baja inflación como Panamá, hasta casos extremos como Argentina por su inflación cercana al 10%, España por su desempleo cercano al 10% e Italia por su crecimiento casi nulo.

5.12 Sensibilidad: ¿qué pasa sin el país más extremo?

Se identifica el país con el valor estandarizado más extremo en cualquiera de las tres variables y se repite el análisis sin él, para ver si la partición cambia.

pais_extremo <- rownames(Datos_escalados)[which.max(apply(abs(Datos_escalados), 1, max))]

Datos_sin <- Datos_matriz[rownames(Datos_matriz) != pais_extremo, , drop = FALSE]
Datos_sin_esc <- scale(Datos_sin)

silueta_sin <- sapply(2:8, function(k) {
  pam(Datos_sin_esc, k = k, metric = "manhattan")$silinfo$avg.width
})
k_sin <- (2:8)[which.max(silueta_sin)]

pam_sin <- pam(Datos_sin_esc, k = k_sin, metric = "manhattan")

cat("País excluido:", pais_extremo, "| k óptimo sin ese país:", k_sin,
    "| Medoides:", paste(rownames(pam_sin$medoids), collapse = ", "))
## País excluido: Argentina | k óptimo sin ese país: 5 | Medoides: Canadá, Guatemala, México, Costa Rica, Brasil
Resultado_sin <- Datos_sin %>%
  rownames_to_column("Pais") %>%
  mutate(across(where(is.numeric), ~ round(.x, 2)),
         Cluster = as.factor(pam_sin$clustering)) %>%
  arrange(Cluster)

Resultado_sin %>%
  kable(caption = paste("PAM sin", pais_extremo)) %>%
  kable_classic(html_font = "Times New Roman", font_size = 14) %>%
  kable_styling()
PAM sin Argentina
Pais Crecimiento_PIB Inflacion Desempleo Cluster
El Salvador 3.00 1.84 4.64 1
Perú 2.80 1.94 6.46 1
Estados Unidos 1.92 2.18 4.00 1
Canadá 1.74 2.02 6.14 1
España 1.75 2.15 9.91 1
Alemania 0.92 2.14 3.07 1
Francia 1.08 1.83 7.57 1
Italia 0.70 2.13 6.02 1
Guatemala 3.91 3.91 6.28 2
Honduras 3.78 4.10 6.56 2
Nicaragua 3.58 3.00 2.76 3
México 2.07 3.08 2.78 3
Costa Rica 3.51 2.57 7.74 4
Panamá 4.58 2.00 7.76 4
Colombia 2.68 3.51 10.00 5
Chile 2.35 3.07 7.42 5
Brasil 2.36 3.07 7.39 5

5.13 Limitaciones por tratarse de proyecciones

  • Las proyecciones tienden a ser más suaves que los datos observados y rara vez anticipan crisis, lo que puede reducir la presencia de outliers.
  • Se usó el promedio 2027-2031 como resumen; usar un solo año podría arrojar agrupaciones distintas.

6. Interpretación

for (g in sort(unique(Datos_cluster$Cluster_PAM))) {
  m <- Datos_cluster %>% filter(Cluster_PAM == g)
  cat(sprintf(
    "- **Clúster %s** (medoide: **%s**): %d país(es) — %s. Medianas: PIB %.1f%%, inflación %.1f%%, desempleo %.1f%%.\n",
    g, Medoides[as.integer(g)], nrow(m), paste(m$Pais, collapse = ", "),
    median(m$Crecimiento_PIB), median(m$Inflacion), median(m$Desempleo)))
}
  • Clúster 1 (medoide: Perú): 2 país(es) — El Salvador, Perú. Medianas: PIB 2.9%, inflación 1.9%, desempleo 5.6%.
  • Clúster 2 (medoide: Honduras): 2 país(es) — Guatemala, Honduras. Medianas: PIB 3.8%, inflación 4.0%, desempleo 6.4%.
  • Clúster 3 (medoide: México): 3 país(es) — Nicaragua, México, Estados Unidos. Medianas: PIB 2.1%, inflación 3.0%, desempleo 2.8%.
  • Clúster 4 (medoide: Panamá): 2 país(es) — Costa Rica, Panamá. Medianas: PIB 4.0%, inflación 2.3%, desempleo 7.8%.
  • Clúster 5 (medoide: España): 2 país(es) — Colombia, España. Medianas: PIB 2.2%, inflación 2.8%, desempleo 10.0%.
  • Clúster 6 (medoide: Chile): 2 país(es) — Chile, Brasil. Medianas: PIB 2.4%, inflación 3.1%, desempleo 7.4%.
  • Clúster 7 (medoide: Argentina): 1 país(es) — Argentina. Medianas: PIB 3.5%, inflación 9.5%, desempleo 6.6%.
  • Clúster 8 (medoide: Italia): 4 país(es) — Canadá, Alemania, Francia, Italia. Medianas: PIB 1.0%, inflación 2.1%, desempleo 6.1%.
cat(sprintf("\n- **Silueta promedio** de la partición PAM: **%.3f** (cercana a 1 = grupos bien definidos; cercana a 0 = grupos poco diferenciados).\n",
            modelo_pam$silinfo$avg.width))
  • Silueta promedio de la partición PAM: 0.358 (cercana a 1 = grupos bien definidos; cercana a 0 = grupos poco diferenciados).
cat(sprintf("- **País más extremo** en las variables estandarizadas: %s.\n", pais_extremo))
  • País más extremo en las variables estandarizadas: Argentina.

Con \(k = 8\) (óptimo según la silueta) se observan ocho perfiles, varios de ellos con solo 1-2 países, lo cual es esperable con una muestra de 18 observaciones:

  • Clúster 1 (medoide: Perú) — El Salvador y Perú: crecimiento moderado (~2.9%), la inflación más baja del grupo (~1.9%) y desempleo moderado (~5.6%). Es el perfil de “estabilidad de precios” dentro de la muestra.
  • Clúster 2 (medoide: Honduras) — Guatemala y Honduras: crecimiento relativamente alto (~3.8%) pero también la inflación más alta entre los países centroamericanos “normales” (~4.0%), con desempleo medio (~6.4%).
  • Clúster 3 (medoide: México) — Nicaragua, México y Estados Unidos: el rasgo que los une es el desempleo muy bajo (~2.8%), pese a ser economías muy distintas en tamaño; el algoritmo los agrupa por ese único rasgo compartido, no por similitud estructural completa.
  • Clúster 4 (medoide: Panamá) — Costa Rica y Panamá: el mayor crecimiento proyectado (~4.0%) combinado con el desempleo más alto de Centroamérica (~7.8%) — un perfil de “crecimiento sin absorción de empleo”.
  • Clúster 5 (medoide: España) — Colombia y España: desempleo estructural muy alto (~10%), el más elevado de toda la muestra, con crecimiento e inflación moderados.
  • Clúster 6 (medoide: Chile) — Chile y Brasil: perfil intermedio en las tres variables, sin rasgos extremos.
  • Clúster 7 (medoide: Argentina) — Argentina sola: aislada por su inflación proyectada de 9.5%, muy por encima de cualquier otro país de la muestra. Es el clúster que confirma empíricamente la justificación de la sección 1.2: PAM no distorsiona a los demás grupos por este valor extremo, simplemente lo aísla en su propio clúster.
  • Clúster 8 (medoide: Italia) — Canadá, Alemania, Francia e Italia: el grupo de economías avanzadas de bajo crecimiento (~1.0%), con inflación y desempleo moderados — el perfil típico de economía madura en el WEO.

¿Dónde queda El Salvador? Se agrupa con Perú (Clúster 1): ambos comparten el rasgo de tener la inflación proyectada más baja de la muestra, más que un parecido en crecimiento o desempleo.

Silueta promedio de 0.358: según la escala habitual de Kaufman y Rousseeuw, esto indica una estructura moderada/débil — los clústeres son identificables, pero no están fuertemente separados. Esto es consistente con que \(k=8\) sobre 18 países produce grupos muy pequeños (varios de 1-2 miembros), lo que infla artificialmente la silueta de esos pares muy parecidos sin que necesariamente exista una separación económica profunda entre todos los ocho grupos.

¿Por qué el medoide es un buen representante? El caso de Argentina lo demuestra directamente: su inflación de 9.5% es casi el triple que la del país más cercano. Un centroide (promedio artificial) se habría desplazado hacia ese valor extremo, distorsionando la posición de todo el grupo. Con PAM, Argentina simplemente es su propio medoide — un país real, no un promedio inventado — por lo que ningún otro clúster absorbe esa distorsión.

Sensibilidad al quitar el país más extremo (sección 5.12): al excluir a Argentina, el \(k\) óptimo según la silueta baja de 8 a 5, con medoides Canadá, Guatemala, México, Costa Rica y Brasil. Esto confirma que buena parte de la fragmentación en 8 grupos pequeños estaba ligada a la presencia de ese outlier: al quitarlo, la estructura “natural” de la muestra es más compacta (5 grupos), reforzando la idea de que Argentina no solo es un caso aislado dentro de la partición con \(k=8\), sino que también afecta cuántos grupos parecen “óptimos” en primer lugar.

7. Conclusiones

La aplicación de PAM a los datos del WEO (FMI) confirma, con evidencia empírica y no solo teórica, las ventajas que se argumentaron en la sección 1.2:

  1. Robustez ante outliers, comprobada: Argentina —con una inflación proyectada de 9.5%, muy por encima del resto— quedó aislada en su propio clúster sin deformar a los demás grupos, pero el análisis de sensibilidad (5.12) mostró que su sola presencia alteraba el número “óptimo” de clústeres de 5 a 8. Esto ilustra un matiz importante: PAM es robusto en cómo asigna las observaciones (el medoide no se deforma), pero un outlier fuerte puede seguir afectando cuántos clústeres sugiere el coeficiente de silueta. Vale la pena que el analista revise ambos escenarios (con y sin el caso extremo) antes de fijar un \(k\) definitivo.

  2. El número de clústeres elegido (k = 8) es estadísticamente óptimo, pero económicamente sobredimensionado para 18 países: produce grupos de 1 a 4 países y una silueta promedio moderada (0.358). La versión sin Argentina, con \(k=5\), probablemente describe mejor la estructura económica real de la región (centroamericanos de mayor crecimiento, sudamericanos de mayor inflación/desempleo, economías avanzadas de bajo crecimiento), y sería la partición recomendada para presentar en la exposición si se busca una interpretación económica más clara y comunicable.

  3. Consistencia con la intuición económica: el resultado sí tiene sentido económico — las economías avanzadas (Canadá, Alemania, Francia, Italia) se agrupan por su bajo crecimiento estructural; Costa Rica y Panamá comparten el patrón de “crecer sin generar suficiente empleo”; y España y Colombia comparten desempleo estructural elevado pese a estar en continentes distintos, lo que confirma que el clustering captura similitud macroeconómica, no geográfica.

Advertencia necesaria: estos resultados describen perfiles proyectados por el FMI (WEO, promedio 2027-2031), no datos observados ni un pronóstico de qué países convergerán económicamente. Los grupos deben leerse como “estos países comparten un perfil macroeconómico esperado según el FMI a esa fecha”, sujeto a revisión en cada nueva edición del WEO.

8. Clustering Jerárquico Aglomerativo (Método de Ward) - API del Banco Mundial

8.1 ¿En qué consiste el método de Ward?

Ward es un método jerárquico aglomerativo: parte con cada país como su propio grupo y, en cada paso, fusiona los dos grupos cuya unión aumenta menos la varianza dentro del grupo (ESS). El resultado es un dendrograma: los países más parecidos se unen abajo y los más distintos arriba. A diferencia de PAM, el número de grupos k no se fija de antemano: se decide cortando el árbol donde aparece un salto grande de altura, y una fusión es irrevocable. Como trabaja con promedios y varianzas, es más sensible a valores atípicos que PAM. Encaja con los datos del Banco Mundial porque son variables continuas que describen una gradación de desarrollo.

8.2 Datos del Banco Mundial (2018)

Se usan cuatro indicadores de la API del Banco Mundial (paquete wbstats) para los mismos 18 países, en un único año (2018, sin el efecto del COVID):

  • Emisiones de CO2 per cápita (EN.GHG.CO2.PC.CE.AR5).
  • Esperanza de vida al nacer (SP.DYN.LE00.IN).
  • Gasto en educación, % del PIB (SE.XPD.TOTL.GD.ZS).
  • Crecimiento del PIB, % anual (NY.GDP.MKTP.KD.ZG).
library(wbstats)

ind_bm <- c(
  CO2_percapita   = "EN.GHG.CO2.PC.CE.AR5",
  Esperanza_vida  = "SP.DYN.LE00.IN",
  Gasto_educacion = "SE.XPD.TOTL.GD.ZS",
  Crecimiento_PIB = "NY.GDP.MKTP.KD.ZG"
)
vars_bm <- names(ind_bm)

BM_datos <- wb_data(
  indicator = ind_bm,
  country = paises_iso3,
  start_date = 2018,
  end_date = 2018,
  return_wide = TRUE
)

8.3 Limpieza y estandarización

Si a algún país le falta un indicador, se sustituye por la media de esa variable (permitido por la guía de la tarea). Como las variables tienen unidades distintas (toneladas, años, % del PIB), scale() es obligatorio.

Datos_bm <- BM_datos %>%
  left_join(Tabla_paises, by = c("iso3c" = "ISO3")) %>%
  select(Pais, all_of(vars_bm)) %>%
  mutate(across(all_of(vars_bm), ~ ifelse(is.na(.x), mean(.x, na.rm = TRUE), .x)))

Datos_bm_esc <- scale(column_to_rownames(Datos_bm, "Pais"))

8.4 Método de Ward y dendrograma

hc_ward <- hclust(dist(Datos_bm_esc), method = "ward.D2")

# k: mayor salto de altura entre fusiones, buscando entre 3 y 6 grupos
alturas <- sort(hc_ward$height, decreasing = TRUE)
saltos  <- alturas[-length(alturas)] - alturas[-1]
k_candidatos <- 3:7
k_ward <- k_candidatos[which.max(saltos[k_candidatos - 1])]

plot(hc_ward, hang = -1, cex = 0.8,
     main = "Dendrograma - Método de Ward, Banco Mundial 2018", xlab = "", sub = "")
rect.hclust(hc_ward, k = k_ward, border = 2:(k_ward + 1))

El corte sugiere k = 4 grupos: los países que se unen a menor altura son los más parecidos, y el salto grande antes del corte indica que ahí se estarían juntando grupos muy distintos.

8.5 Países en cada clúster

grupos_ward <- cutree(hc_ward, k = k_ward)
split(names(grupos_ward), grupos_ward)
## $`1`
## [1] "Argentina" "Nicaragua"
## 
## $`2`
## [1] "Brasil"     "Chile"      "Costa Rica" "Honduras"  
## 
## $`3`
## [1] "Canadá"         "Alemania"       "España"         "Francia"       
## [5] "Italia"         "Estados Unidos"
## 
## $`4`
## [1] "Colombia"    "Guatemala"   "México"      "Panamá"      "Perú"       
## [6] "El Salvador"

Recursos y documentación

Paquete de R (CRAN): imf.data — https://cran.r-project.org/web/packages/imf.data/imf.data.pdf

Paquete de R (CRAN): wbstats (Banco Mundial) — https://cran.r-project.org/web/packages/wbstats/wbstats.pdf

Landa Baella, M. y Villagómez Véliz, D. Desarrollo de análisis clúster en R. Estadística Computacional (ejemplo del método de Ward).