¿Los países del mundo se estructuran naturalmente en bloques homogéneos de desarrollo socio-sanitario, o presentan una diversidad tan heterogénea que resulta inviable definir perfiles comunes de bienestar?

Contexto y Preparación de Datos

En esta pestaña se introduce la fundamentación teórica del estudio, la pregunta de investigación sobre las brechas de desarrollo socio-sanitario, la ficha técnica de las 15 variables del Banco Mundial (2019) y la metodología aplicada para la depuración, filtrado de agregados e imputación de datos por grupos de ingreso.

Introducción

Problema de investigación

El panorama internacional del bienestar humano y la salubridad pública está lejos de constituir un conjunto homogéneo de naciones que comparten los mismos niveles de vida, cobertura sanitaria e infraestructura básica. Por el contrario, en el escenario global coexisten realidades profundas y disímiles: desde economías avanzadas con sistemas de salud altamente tecnificados, coberturas universales de servicios y estructuras demográficas envejecidas, hasta naciones en desarrollo que enfrentan elevadas tasas de mortalidad de menores de cinco años y materna, presiones demográficas juveniles y marcadas deficiencias en el acceso a la electricidad o la desnutrición.

Este marcado contraste plantea una pregunta de investigación central que motiva el presente análisis: ¿Los países estudiados se agrupan naturalmente en tipos o perfiles socio-sanitarios distintos, o son todos tan diferentes entre sí que no es posible encontrar patrones comunes?

Esta interrogante delimita el problema central del trabajo: la identificación empírica de perfiles socio-sanitarios e infraestructurales estructuralmente diferenciados a partir de indicadores cuantitativos globales. En este marco, resulta indispensable precisar dos conceptos fundamentales. En primer lugar, cuando se habla de perfiles estructurales, se hace referencia a configuraciones estables de variables de salud, demografía e infraestructura que reflejan la arquitectura social subyacente de cada país, y no simplemente fluctuaciones de corto plazo asociadas a coyunturas específicas. En segundo lugar, el concepto de agrupación natural implica que los segmentos de países no son definidos de forma arbitraria o por conveniencia geográfica, sino que emergen directamente de los datos empíricos a través de técnicas de aprendizaje no supervisado que identifican similitudes y disimilitudes sin imponer categorías preconcebidas.

La relevancia de este problema reside en que las clasificaciones tradicionales de naciones, como la división por bloques geográficos o por el Ingreso Nacional Bruto per cápita propuesto por el Banco Mundial, resultan unidimensionales e insuficientes para capturar la complejidad multidimensional de los sistemas de salud y desarrollo. Frente a ello, el uso combinado del Análisis de Componentes Principales (ACP) y la clusterización jerárquica mediante el Método de Ward permite construir una taxonomía empírica multidimensional que sintetiza simultáneamente la capacidad sanitaria, la transición demográfica, la cobertura de infraestructura y las condiciones socioeconómicas de cada economía.

Contexto

Los datos provienen de los Indicadores del Desarrollo Mundial (World Development Indicators, WDI) del Banco Mundial. El Banco Mundial compila métricas cuantitativas provenientes de sistemas estadísticos nacionales reconocidos, aplicando metodologías comunes de medición que garantizan la comparabilidad de los indicadores entre países con capacidades institucionales heterogéneas. Esta estandarización metodológica es lo que hace del WDI una fuente adecuada para el análisis multivariado comparado, porque reduce las diferencias de definición entre países; no elimina, sin embargo, la heterogeneidad en la calidad de los registros nacionales ni el hecho de que algunos indicadores, como la mortalidad materna o la incidencia de tuberculosis, sean estimaciones modeladas por organismos internacionales, limitaciones que se retoman en las conclusiones.

Desde una perspectiva analítica, la delimitación del conjunto de datos responde a dos criterios metodológicos fundamentales. Por un lado, se selecciona el año 2019 como marco temporal estático con el objetivo de establecer una línea base de desarrollo estable. Estudiar el comportamiento socio-sanitario global en 2019 resulta estratégicamente relevante porque permite evaluar la situación estructural de las naciones inmediatamente antes de las severas distorsiones estadísticas, coyunturales y operativas causadas por la pandemia de COVID-19 a partir de 2020. Por otro lado, la muestra procesada abarca 192 países y economías, tras un riguroso proceso de depuración que excluyó 49 agregados regionales o bloques institucionales (como América Latina y el Caribe o Países de Ingreso Alto) para evitar duplicidades muestrales y garantizar que cada observación represente una unidad política autónoma.

La muestra analizada abarca un espectro amplio de realidades socio-sanitarias que el análisis no asume dividido en un número predeterminado de grupos. En un extremo se esperan economías con sistemas de salud consolidados, elevada esperanza de vida al nacer, acceso prácticamente universal a la electricidad y una proporción importante de población mayor de 65 años; en el opuesto, naciones con altas tasas de fertilidad y natalidad, elevada mortalidad de menores de cinco años y materna, mayor incidencia de tuberculosis y desnutrición y escasa dotación de personal médico; y entre ambos, una gama de situaciones intermedias cuya agrupación, o ausencia de ella, es precisamente lo que debe establecerse con los datos. Esta apertura es deliberada: si se fijara de antemano cuántos perfiles existen, el algoritmo solo confirmaría una clasificación previa en lugar de someterla a contraste, de modo que el número de grupos, su composición y su significado se determinan más adelante mediante criterios de validación interna y no desde el marco conceptual de esta introducción.

La diversidad estructural de la muestra es lo que otorga valor a la aplicación del aprendizaje no supervisado, pues permite que los algoritmos de reducción de dimensionalidad y de agrupación identifiquen gradientes latentes de bienestar sin que estos sean impuestos por preconceptos teóricos ni por clasificaciones institucionales previas.

Justificación y objetivos

La justificación de este estudio radica en su aporte metodológico y descriptivo para la comprensión del desarrollo socio-sanitario comparado. Desde el punto de vista metodológico, el uso articulado del ACP y la clusterización jerárquica con el método de Ward permite superar las limitaciones de los análisis univariados tradicionales, al construir una estructura factorial que sintetiza simultáneamente 15 indicadores socioeconómicos, sanitarios y demográficos. Desde la perspectiva descriptiva, identificar perfiles diferenciados de países permite documentar qué configuraciones de indicadores coexisten en 2019 y comparar esa taxonomía con la clasificación del Banco Mundial por ingreso; el estudio no evalúa intervenciones ni estima efectos de política.

El objetivo principal de este trabajo es aplicar técnicas de aprendizaje no supervisado (específicamente el Análisis de Componentes Principales y la clusterización jerárquica por el Método de Ward) sobre un conjunto de 15 indicadores del Banco Mundial para el año 2019, con el propósito de evaluar la estructura latente de los datos, identificar perfiles socio-sanitarios diferenciados entre los 192 países conservados y determinar qué dimensiones del bienestar articulan sus principales diferencias.

Para alcanzar este objetivo, el documento se organiza en cuatro pestañas analíticas: en esta primera pestaña (Contexto y Preparación de Datos) se presenta el marco de la investigación, la ficha técnica de los indicadores, el tratamiento de agregados y la imputación condicional de datos faltantes por grupo de ingreso; en la segunda pestaña (Análisis Exploratorio y Descriptivo) se examinan la distribución, asimetría, correlaciones y adecuación muestral de los indicadores originales; en la tercera pestaña (Reducción Dimensional y Segmentación) se desarrollan los resultados del ACP y el agrupamiento jerárquico; y finalmente, en la cuarta pestaña (Resultados, Discusión y Cierre), se discuten los hallazgos y se presentan las conclusiones orientadas a responder la pregunta de investigación planteada.

Metodología

La metodología adoptada en este estudio se basa en un enfoque de aprendizaje no supervisado, diseñado para identificar patrones estructurales en un conjunto de naciones sin necesidad de una clasificación o etiquetado previo. A diferencia de los métodos supervisados, en los que el algoritmo se entrena a partir de categorías predefinidas, el aprendizaje no supervisado permite que la propia matriz de datos revele su estructura latente, generando agrupaciones que emergen directamente de las similitudes y disimilitudes observadas entre los países. Este enfoque resulta idóneo para el análisis socio-sanitario internacional, dado que evita imponer clasificaciones geográficas o institucionales preconcebidas que podrían introducir sesgos en la taxonomía resultante.

El proceso metodológico se articuló en las siguientes etapas secuenciales: extracción de la base de datos oficial, selección y justificación teórica de las 15 variables de estudio, depuración de la muestra mediante el filtrado de agregados regionales y la evaluación de suficiencia muestral, imputación condicional de valores faltantes por grupos de ingreso (con una regresión para el INB), transformación logarítmica de las variables de cola derecha según una regla explícita, estandarización de los datos (\(Z\)-scores), reducción de dimensionalidad mediante el Análisis de Componentes Principales (ACP), determinación del número de clusters, formación de grupos de países con perfiles similares mediante la clusterización jerárquica con el Método de Ward y caracterización de cada segmento identificado, siguiendo el enfoque de análisis exploratorio multivariado descrito por Husson et al. (2017) y James et al. (2021). El procesamiento se realizó en R con los paquetes FactoMineR (Lê et al., 2008) y factoextra (Kassambara y Mundt, 2020) para el ACP y su visualización, y ggplot2 (Wickham, 2016) y plotly (Sievert, 2020) para los gráficos estáticos e interactivos. La manipulación de los datos y las tablas se apoyaron en tidyverse (Wickham et al., 2019), kableExtra (Zhu, 2021) y corrplot (Wei y Simko, 2021); las siluetas y los índices de validación del agrupamiento, en el paquete cluster (Maechler et al., 2023); y el informe se generó con knitr (Xie, 2015), todo ello en R (R Core Team, 2024). El ACP se calcula sobre la matriz de correlaciones de las variables estandarizadas; las variables de cola derecha se transforman con log1p según una regla declarada a priori (asimetría mayor que 1,5 y valor absoluto de la asimetría menor que 0,5 tras la transformación; sección 2.4), y la escala original se conserva como análisis de sensibilidad (sección 3.15). El número de componentes y el de clusters se derivan con reglas declaradas a priori (varianza acumulada de al menos 80 % y mayoría de cinco criterios de validación interna con desempate por rango medio; secciones 3.1 y 3.7). Esas reglas dependen de parámetros fijados por los autores (el umbral de 80 %, el rango de k entre 2 y 8 y la composición del panel de criterios), por lo que su sensibilidad se evalúa en la sección 3.15, junto con la influencia de la República Centroafricana, de la mortalidad bruta, del número de componentes retenidos y de la imputación.

Base de datos y procedimiento de extracción

Los datos utilizados en este estudio provienen de la base de datos oficial de los Indicadores del Desarrollo Mundial (World Development Indicators - WDI) del Banco Mundial. Esta base contiene información macroeconómica, social y sanitaria detallada a nivel global. El procesamiento técnico se ejecutó en el entorno estadístico RStudio, verificando la adecuada importación del archivo en formato rectangular, la corrección de los tipos de variables continuas y la alineación estructural de la matriz. En esta etapa inicial se confirmó que la unidad fundamental de análisis corresponde a los países, donde cada fila representa una economía soberana y cada columna un indicador socio-sanitario específico.

Del conjunto exhaustivo de métricas disponibles en el repositorio del Banco Mundial se seleccionaron 15 indicadores que cubren cuatro dimensiones del desarrollo socio-sanitario: salud y supervivencia, dinámica demográfica, capacidad del sistema de salud y condiciones socioeconómicas y de infraestructura. Esta selección multidimensional busca que el espacio de análisis no quede dominado por un solo aspecto del bienestar y proporciona el insumo cuantitativo necesario para la proyección factorial y el agrupamiento no supervisado.

# Verificación de paquetes: si falta alguno, el Knit se detiene con un mensaje claro
paquetes  <- c("tidyverse", "knitr", "kableExtra", "corrplot", "factoextra", "FactoMineR",
               "cluster", "plotly", "ggrepel", "gridExtra", "scales")
faltantes <- paquetes[!vapply(paquetes, requireNamespace, logical(1), quietly = TRUE)]


# Carga de paquetes necesarios (ggplot2 viene incluido en tidyverse)
library(tidyverse)
library(knitr)
library(kableExtra)
library(corrplot)
library(factoextra)
library(FactoMineR)
library(cluster)
library(plotly)
library(ggrepel)
library(gridExtra)
library(NbClust)


# Opciones globales de formateo
options(scipen = 999)

# PARÁMETROS DEL ESTUDIO
ANIO            <- 2019   
# Año de análisis
MAX_FALTANTES   <- 3  

# Máximo de variables sin dato (de 15) para conservar un país (20 %)

UMBRAL_VARIANZA <- 80     

# % mínimo de varianza acumulada para retener componentes (define N_PC en 3.1)
K_MIN           <- 2     
# Rango de búsqueda del número de clusters.
K_MAX           <- 8      

# el k se obtiene con la regla de la sección 3.7 

set.seed(2019)            

UMBRAL_ASIM_LOG  <- 1.5  

UMBRAL_ASIM_POST <- 0.5 

# Umbrales de ingreso del Banco Mundial (INB per cápita, método Atlas, año fiscal 2021

# INB de 2019): bajo <= 1.035 | medio-bajo <= 4.045 | medio-alto <= 12.535 | alto > 12.535
UMBRALES_INGRESO <- c(1035, 4045, 12535)


PAISES_REF <- c("Colombia", "Estados Unidos", "China", "India", "Brasil", "Japón", "Nigeria", "Alemania")

# Paleta de los grupos de ingreso 

pal_ing <- setNames(c(scales::viridis_pal(end = 0.9)(4), "grey55"),
                    c("Ingreso bajo", "Ingreso medio-bajo", "Ingreso medio-alto", "Ingreso alto", "Sin INB reportado"))

Descripción y justificación teórica de las variables

Las cuatro dimensiones reúnen cinco indicadores de salud y supervivencia (esperanza de vida, mortalidad de menores de cinco años, mortalidad materna, incidencia de tuberculosis y desnutrición), cuatro de dinámica demográfica (fertilidad, natalidad, mortalidad bruta y población de 65 años o más), tres de capacidad del sistema de salud (gasto corriente en salud, densidad de médicos y vacunación DPT) y tres de condiciones socioeconómicas y de infraestructura (INB per cápita, población urbana y acceso a la electricidad). La primera tabla metodológica sintetiza, para cada indicador, el indicador WDI original, el tipo de variable cuantitativa continua, su dimensión de pertenencia y su justificación teórica en el modelo, lo que permite comprender el papel conceptual que desempeñará cada uno al capturar la varianza global en el Análisis de Componentes Principales.

justificacion <- data.frame(
  Indicador = c(
        "Access to electricity (% of population)",
        "Birth rate, crude (per 1,000 people)",
        "Current health expenditure (% of GDP)",
        "Life expectancy at birth, total (years)",
        "Fertility rate, total (births per woman)",
        "Death rate, crude (per 1,000 people)",
        "Urban population (% of total population)",
        "Mortality rate, under-5 (per 1,000 live births)",
        "Maternal mortality ratio (per 100,000)",
        "Incidence of tuberculosis (per 100,000)",
        "Physicians (per 1,000 people)",
        "Immunization, DPT (% of children)",
        "Prevalence of undernourishment (%)",
        "GNI per capita, Atlas method (US$)",
        "Population ages 65 and above (%)"
      ),
  Tipo = "Cuantitativa continua",
  Dimension = c("Condiciones socioeconómicas y de infraestructura", "Dinámica demográfica",
                    "Capacidad del sistema de salud", "Salud y supervivencia", "Dinámica demográfica",
                    "Dinámica demográfica", "Condiciones socioeconómicas y de infraestructura",
                    "Salud y supervivencia", "Salud y supervivencia", "Salud y supervivencia",
                    "Capacidad del sistema de salud", "Capacidad del sistema de salud",
                    "Salud y supervivencia", "Condiciones socioeconómicas y de infraestructura",
                    "Dinámica demográfica"),
  Justificacion = c(
    "Diferencia economías preindustriales de las modernas; es un proxy del acceso a servicios básicos y urbanización.",
    "Captura la dinámica poblacional; tasas altas suelen asociarse a etapas tempranas de transición demográfica y menor desarrollo.",
    "Mide el esfuerzo financiero estatal y privado; refleja la prioridad fiscal asignada a la protección de la salud poblacional.",
    "Es el indicador macro por excelencia de la calidad de vida, nutrición y eficacia del sistema sanitario a largo plazo.",
    "Se relaciona fuertemente con el nivel educativo femenino, el acceso a anticonceptivos y la madurez económica del país.",
    "Permite identificar tanto crisis sanitarias estructurales (en países pobres) como el envejecimiento poblacional agudo (en países ricos).",
    "Señala el grado de urbanización, fundamental para entender la concentración de mercados, hospitales e infraestructura.",
    "Un proxy crítico y altamente sensible a la pobreza extrema, la falta de agua potable y deficiencias en la atención primaria.",
    "Evalúa directamente la cobertura y calidad del sistema de salud reproductiva y la infraestructura hospitalaria de urgencia.",
    "Actúa como marcador de condiciones de hacinamiento, pobreza multidimensional y vulnerabilidad inmunológica comunitaria.",
    "Mide la disponibilidad técnica e inversión en capital humano especializado, vital para la capacidad de respuesta sanitaria.",
    "Refleja la penetración, logística y eficacia de las políticas preventivas estatales en las zonas más vulnerables.",
    "Indicador directo de inseguridad alimentaria, exponiendo carencias económicas profundas que impiden el desarrollo físico y cognitivo.",
    "Proporciona la medida estándar de riqueza promedio, esencial para contrastar la capacidad económica frente a los logros sociales.",
    "Señala el envejecimiento estructural de la sociedad, implicando retos fiscales en pensiones y demanda de enfermedades crónicas."
  )
)

justificacion %>%
  kbl(caption = "Variables seleccionadas y justificación teórica en el modelo",
      col.names = c("Indicador (variable WDI)", "Tipo de variable", "Dimensión", "Justificación en el modelo")) %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = TRUE) %>%
  column_spec(1, bold = TRUE, width = "24%") %>%
  column_spec(4, width = "45%")
Variables seleccionadas y justificación teórica en el modelo
Indicador (variable WDI) Tipo de variable Dimensión Justificación en el modelo
Access to electricity (% of population) Cuantitativa continua Condiciones socioeconómicas y de infraestructura Diferencia economías preindustriales de las modernas; es un proxy del acceso a servicios básicos y urbanización.
Birth rate, crude (per 1,000 people) Cuantitativa continua Dinámica demográfica Captura la dinámica poblacional; tasas altas suelen asociarse a etapas tempranas de transición demográfica y menor desarrollo.
Current health expenditure (% of GDP) Cuantitativa continua Capacidad del sistema de salud Mide el esfuerzo financiero estatal y privado; refleja la prioridad fiscal asignada a la protección de la salud poblacional.
Life expectancy at birth, total (years) Cuantitativa continua Salud y supervivencia Es el indicador macro por excelencia de la calidad de vida, nutrición y eficacia del sistema sanitario a largo plazo.
Fertility rate, total (births per woman) Cuantitativa continua Dinámica demográfica Se relaciona fuertemente con el nivel educativo femenino, el acceso a anticonceptivos y la madurez económica del país.
Death rate, crude (per 1,000 people) Cuantitativa continua Dinámica demográfica Permite identificar tanto crisis sanitarias estructurales (en países pobres) como el envejecimiento poblacional agudo (en países ricos).
Urban population (% of total population) Cuantitativa continua Condiciones socioeconómicas y de infraestructura Señala el grado de urbanización, fundamental para entender la concentración de mercados, hospitales e infraestructura.
Mortality rate, under-5 (per 1,000 live births) Cuantitativa continua Salud y supervivencia Un proxy crítico y altamente sensible a la pobreza extrema, la falta de agua potable y deficiencias en la atención primaria.
Maternal mortality ratio (per 100,000) Cuantitativa continua Salud y supervivencia Evalúa directamente la cobertura y calidad del sistema de salud reproductiva y la infraestructura hospitalaria de urgencia.
Incidence of tuberculosis (per 100,000) Cuantitativa continua Salud y supervivencia Actúa como marcador de condiciones de hacinamiento, pobreza multidimensional y vulnerabilidad inmunológica comunitaria.
Physicians (per 1,000 people) Cuantitativa continua Capacidad del sistema de salud Mide la disponibilidad técnica e inversión en capital humano especializado, vital para la capacidad de respuesta sanitaria.
Immunization, DPT (% of children) Cuantitativa continua Capacidad del sistema de salud Refleja la penetración, logística y eficacia de las políticas preventivas estatales en las zonas más vulnerables.
Prevalence of undernourishment (%) Cuantitativa continua Salud y supervivencia Indicador directo de inseguridad alimentaria, exponiendo carencias económicas profundas que impiden el desarrollo físico y cognitivo.
GNI per capita, Atlas method (US$) Cuantitativa continua Condiciones socioeconómicas y de infraestructura Proporciona la medida estándar de riqueza promedio, esencial para contrastar la capacidad económica frente a los logros sociales.
Population ages 65 and above (%) Cuantitativa continua Dinámica demográfica Señala el envejecimiento estructural de la sociedad, implicando retos fiscales en pensiones y demanda de enfermedades crónicas.

La siguiente tabla detalla la correspondencia exacta entre los nombres conceptuales asignados en el script, los códigos alfanuméricos oficiales del WDI, las definiciones operativas estandarizadas por el Banco Mundial y sus respectivas unidades de medida.

# Tabla de caracterización conceptual de las 15 variables
variables_info <- data.frame(
  Variable = c("EspVida", "Fertilidad", "Natalidad", "MortalidadBruta", "PobUrbana", 
               "MortMenores5", "MortMaterna", "IncidenciaTB", "GastoSaludPIB", "MedicosPor1k", 
               "VacunacionDPT", "Desnutricion", "INB_perCapita", "Pob65Mas", "AccesoElectricidad"),
  Codigo_WDI = c("SP.DYN.LE00.IN", "SP.DYN.TFRT.IN", "SP.DYN.CBRT.IN", "SP.DYN.CDRT.IN", "SP.URB.TOTL.IN.ZS",
                 "SH.DYN.MORT", "SH.STA.MMRT", "SH.TBS.INCD", "SH.XPD.CHEX.GD.ZS", "SH.MED.PHYS.ZS",
                 "SH.IMM.IDPT", "SN.ITK.DEFC.ZS", "NY.GNP.PCAP.CD", "SP.POP.65UP.TO.ZS", "EG.ELC.ACCS.ZS"),
  Nombre_Oficial = c("Esperanza de vida al nacer", "Tasa de fertilidad total", "Tasa bruta de natalidad", 
                     "Tasa bruta de mortalidad", "Población urbana", "Tasa de mortalidad de menores de cinco años", 
                     "Tasa de mortalidad materna", "Incidencia de tuberculosis", "Gasto corriente en salud", 
                     "Densidad de médicos", "Cobertura de vacunación DPT", "Prevalencia de desnutrición", 
                     "Ingreso Nacional Bruto per cápita", "Población de 65 años o más", "Acceso a la electricidad"),
  Definicion = c(
    "Años que viviría un recién nacido si los patrones de mortalidad actuales se mantuvieran constantes durante su vida.",
    "Número de hijos que tendría una mujer si viviera toda su etapa fértil con las tasas de fecundidad por edad vigentes.",
    "Nacimientos con vida durante el año por cada 1.000 habitantes a mitad de año.",
    "Defunciones durante el año por cada 1.000 habitantes a mitad de año.",
    "Porcentaje de la población que vive en áreas urbanas, según la definición de cada país.",
    "Probabilidad de morir entre el nacimiento y los 5 años de edad, por cada 1.000 nacidos vivos.",
    "Muertes de mujeres durante el embarazo, el parto o el posparto, por cada 100.000 nacidos vivos (estimación modelada).",
    "Casos nuevos y recaídas de tuberculosis estimados en el año, por cada 100.000 personas.",
    "Gasto corriente en salud (público, privado y externo) como proporción del PIB; excluye inversión de capital.",
    "Médicos (generalistas y especialistas) por cada 1.000 habitantes.",
    "Porcentaje de niños de 12 a 23 meses que recibió la vacuna DPT (difteria, tos ferina y tétanos).",
    "Porcentaje de la población cuyo consumo habitual de alimentos no cubre sus necesidades de energía (subalimentación). El WDI reporta '<2,5' como 2,5.",
    "Ingreso nacional bruto dividido por la población a mitad de año, en dólares corrientes (método Atlas).",
    "Porcentaje de la población total que tiene 65 años o más.",
    "Porcentaje de la población con acceso a electricidad."
  ),
  Unidad_Medida = c("Años", "Nacimientos por mujer", "Por cada 1.000 personas", 
                    "Por cada 1.000 personas", "% de la población total", 
                    "Por cada 1.000 nacidos vivos", "Por cada 100.000 nacidos vivos", "Por cada 100.000 personas", 
                    "% del PIB", "Por cada 1.000 personas", 
                    "% de niños (12-23 meses)", "% de la población", 
                    "USD actuales (Método Atlas)", "% de la población total", "% de la población"),
  Dimension = c("Salud y supervivencia", "Dinámica demográfica", "Dinámica demográfica",
                    "Dinámica demográfica", "Condiciones socioeconómicas y de infraestructura",
                    "Salud y supervivencia", "Salud y supervivencia", "Salud y supervivencia",
                    "Capacidad del sistema de salud", "Capacidad del sistema de salud",
                    "Capacidad del sistema de salud", "Salud y supervivencia",
                    "Condiciones socioeconómicas y de infraestructura", "Dinámica demográfica",
                    "Condiciones socioeconómicas y de infraestructura")
)

variables_info %>%
  kbl(caption = "Ficha Técnica de las 15 Variables Seleccionadas (WDI Banco Mundial - 2019)",
      col.names = c("Código en R", "Código WDI", "Nombre Oficial del Indicador", "Definición", "Unidad de Medida", "Dimensión Analítica")) %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = TRUE)%>%
  column_spec(1, bold = TRUE) %>%
  column_spec(4, width = "35%")
Ficha Técnica de las 15 Variables Seleccionadas (WDI Banco Mundial - 2019)
Código en R Código WDI Nombre Oficial del Indicador Definición Unidad de Medida Dimensión Analítica
EspVida SP.DYN.LE00.IN Esperanza de vida al nacer Años que viviría un recién nacido si los patrones de mortalidad actuales se mantuvieran constantes durante su vida. Años Salud y supervivencia
Fertilidad SP.DYN.TFRT.IN Tasa de fertilidad total Número de hijos que tendría una mujer si viviera toda su etapa fértil con las tasas de fecundidad por edad vigentes. Nacimientos por mujer Dinámica demográfica
Natalidad SP.DYN.CBRT.IN Tasa bruta de natalidad Nacimientos con vida durante el año por cada 1.000 habitantes a mitad de año. Por cada 1.000 personas Dinámica demográfica
MortalidadBruta SP.DYN.CDRT.IN Tasa bruta de mortalidad Defunciones durante el año por cada 1.000 habitantes a mitad de año. Por cada 1.000 personas Dinámica demográfica
PobUrbana SP.URB.TOTL.IN.ZS Población urbana Porcentaje de la población que vive en áreas urbanas, según la definición de cada país. % de la población total Condiciones socioeconómicas y de infraestructura
MortMenores5 SH.DYN.MORT Tasa de mortalidad de menores de cinco años Probabilidad de morir entre el nacimiento y los 5 años de edad, por cada 1.000 nacidos vivos. Por cada 1.000 nacidos vivos Salud y supervivencia
MortMaterna SH.STA.MMRT Tasa de mortalidad materna Muertes de mujeres durante el embarazo, el parto o el posparto, por cada 100.000 nacidos vivos (estimación modelada). Por cada 100.000 nacidos vivos Salud y supervivencia
IncidenciaTB SH.TBS.INCD Incidencia de tuberculosis Casos nuevos y recaídas de tuberculosis estimados en el año, por cada 100.000 personas. Por cada 100.000 personas Salud y supervivencia
GastoSaludPIB SH.XPD.CHEX.GD.ZS Gasto corriente en salud Gasto corriente en salud (público, privado y externo) como proporción del PIB; excluye inversión de capital. % del PIB Capacidad del sistema de salud
MedicosPor1k SH.MED.PHYS.ZS Densidad de médicos Médicos (generalistas y especialistas) por cada 1.000 habitantes. Por cada 1.000 personas Capacidad del sistema de salud
VacunacionDPT SH.IMM.IDPT Cobertura de vacunación DPT Porcentaje de niños de 12 a 23 meses que recibió la vacuna DPT (difteria, tos ferina y tétanos). % de niños (12-23 meses) Capacidad del sistema de salud
Desnutricion SN.ITK.DEFC.ZS Prevalencia de desnutrición Porcentaje de la población cuyo consumo habitual de alimentos no cubre sus necesidades de energía (subalimentación). El WDI reporta ‘<2,5’ como 2,5. % de la población Salud y supervivencia
INB_perCapita NY.GNP.PCAP.CD Ingreso Nacional Bruto per cápita Ingreso nacional bruto dividido por la población a mitad de año, en dólares corrientes (método Atlas). USD actuales (Método Atlas) Condiciones socioeconómicas y de infraestructura
Pob65Mas SP.POP.65UP.TO.ZS Población de 65 años o más Porcentaje de la población total que tiene 65 años o más. % de la población total Dinámica demográfica
AccesoElectricidad EG.ELC.ACCS.ZS Acceso a la electricidad Porcentaje de la población con acceso a electricidad. % de la población Condiciones socioeconómicas y de infraestructura
Carga de datos y filtrado de agregados regionales

La extracción inicial de datos crudos desde el WDI contiene 266 códigos de entidad, de los cuales 265 reportan al menos un valor de los 15 indicadores en 2019. Un paso de depuración indispensable para el rigor econométrico consiste en restringir la muestra a unidades políticas individuales, porque el archivo mezcla países con agregados regionales, grupos de ingreso y bloques institucionales (como Mundo, Unión Europea, América Latina y el Caribe o África Subsahariana). Se identificaron 49 códigos de agregado, de los cuales 48 reportan datos en 2019 y por tanto figuran entre las 265 entidades con información; al excluirlos queda una base de 217 países y economías.

Mantener los agregados distorsionaría los algoritmos de reducción dimensional y de clusterización, porque introduciría observaciones sintéticas que duplican la información de los países que las componen, alteraría la matriz de varianzas-covarianzas y generaría centroides artificiales. La unidad de análisis resultante no se limita estrictamente a estados soberanos, ya que el WDI reporta de forma individual a economías y territorios como Palestina, Hong Kong o Puerto Rico, de los cuales solo el primero supera el filtro de suficiencia de datos descrito a continuación; por ello se habla en adelante de países y economías, entendidos como unidades que el Banco Mundial reporta por separado.

buscar_csv <- function(dir, rec = FALSE) {
  c(list.files(dir, pattern = "^wdi_datos_2019\\.csv$", full.names = TRUE, recursive = rec),
    list.files(dir, pattern = "_Data\\.csv$", full.names = TRUE, recursive = rec))
}
candidatos <- buscar_csv(".")
if (length(candidatos) == 0) {
  dir_zip <- file.path(tempdir(), "wdi_zip")
  for (z in list.files(".", pattern = "\\.zip$", full.names = TRUE)) {
    contenido <- unzip(z, list = TRUE)$Name
    if (any(grepl("_Data\\.csv$|wdi_datos_2019\\.csv$", contenido))) {
      unzip(z, exdir = dir_zip)
      break
    }
  }
  candidatos <- buscar_csv(dir_zip, rec = TRUE)
}
stopifnot("No se encontró el CSV del WDI (ni un .zip que lo contenga) junto al .Rmd" = length(candidatos) > 0)
ruta_final <- candidatos[1]

raw_data <- read_csv(ruta_final, na = c("..", ""), col_types = cols(.default = col_character()))

df_clean <- raw_data %>%
  select(Country = `Country Name`, Code = `Country Code`, Series = `Series Code`,
         Value = all_of(paste0(ANIO, " [YR", ANIO, "]"))) %>%
  mutate(Value = as.numeric(Value)) %>%
  filter(!is.na(Value))

# Los 49 agregados del WDI
agregados <- c("AFE", "AFW", "ARB", "CEB", "CSS", "EAP", "EAR", "EAS", "ECA", "ECS", "EMU", "EUU", "FCV",
               "HIC", "HPC", "IBD", "IBT", "IDA", "IDB", "IDX", "INX", "LAC", "LCN", "LDC", "LIC", "LMC",
               "LMY", "LTE", "MEA", "MIC", "MNA", "NAC", "OED", "OSS", "PRE", "PSS", "PST", "SAS", "SSA",
               "SSF", "SST", "TEA", "TEC", "TLA", "TMN", "TSA", "TSS", "UMC", "WLD")

n_entidades <- n_distinct(df_clean$Code)

df_countries <- df_clean %>%
  filter(!Code %in% agregados) %>%
  pivot_wider(id_cols = c(Code, Country), names_from = Series, values_from = Value)

resumen_filtro <- tibble(
  Concepto = c("Entidades con dato en 2019 (archivo original)",
               "Agregados regionales / bloques excluidos (con dato en 2019)",
               "Países y economías conservados"),
  Cantidad = c(n_entidades, sum(agregados %in% df_clean$Code), nrow(df_countries))
)

resumen_filtro %>%
  kbl(caption = "Filtrado de agregados: solo países") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Filtrado de agregados: solo países
Concepto Cantidad
Entidades con dato en 2019 (archivo original) 265
Agregados regionales / bloques excluidos (con dato en 2019) 48
Países y economías conservados 217

Para asegurar que las visualizaciones y mapas factoriales mantengan un estándar estético adecuado y de fácil interpretación, los nombres de los 217 países y economías se estandarizan a español utilizando un mapeo por código ISO-3, asegurando la correspondencia con las 15 variables conceptuales.

# Nombres de los países en español
nombres_es <- c(
  ABW="Aruba", AFG="Afganistán", AGO="Angola", ALB="Albania", AND="Andorra", ARE="Emiratos Árabes Unidos",
  ARG="Argentina", ARM="Armenia", ASM="Samoa Americana", ATG="Antigua y Barbuda", AUS="Australia", AUT="Austria",
  AZE="Azerbaiyán", BDI="Burundi", BEL="Bélgica", BEN="Benín", BFA="Burkina Faso", BGD="Bangladés", BGR="Bulgaria",
  BHR="Baréin", BHS="Bahamas", BIH="Bosnia y Herzegovina", BLR="Bielorrusia", BLZ="Belice", BMU="Bermudas",
  BOL="Bolivia", BRA="Brasil", BRB="Barbados", BRN="Brunéi", BTN="Bután", BWA="Botsuana",
  CAF="Rep. Centroafricana", CAN="Canadá", CHE="Suiza", CHI="Islas del Canal", CHL="Chile", CHN="China",
  CIV="Costa de Marfil", CMR="Camerún", COD="RD del Congo", COG="Congo", COL="Colombia",
  COM="Comoras", CPV="Cabo Verde", CRI="Costa Rica", CUB="Cuba", CUW="Curazao", CYM="Islas Caimán", CYP="Chipre",
  CZE="Chequia", DEU="Alemania", DJI="Yibuti", DMA="Dominica", DNK="Dinamarca", DOM="Rep. Dominicana",
  DZA="Argelia", ECU="Ecuador", EGY="Egipto", ERI="Eritrea", ESP="España", EST="Estonia", ETH="Etiopía",
  FIN="Finlandia", FJI="Fiyi", FRA="Francia", FRO="Islas Feroe", FSM="Micronesia", GAB="Gabón", GBR="Reino Unido",
  GEO="Georgia", GHA="Ghana", GIB="Gibraltar", GIN="Guinea", GMB="Gambia", GNB="Guinea-Bisáu",
  GNQ="Guinea Ecuatorial", GRC="Grecia", GRD="Granada", GRL="Groenlandia", GTM="Guatemala", GUM="Guam",
  GUY="Guyana", HKG="Hong Kong", HND="Honduras", HRV="Croacia", HTI="Haití", HUN="Hungría", IDN="Indonesia",
  IMN="Isla de Man", IND="India", IRL="Irlanda", IRN="Irán", IRQ="Irak", ISL="Islandia", ISR="Israel",
  ITA="Italia", JAM="Jamaica", JOR="Jordania", JPN="Japón", KAZ="Kazajistán", KEN="Kenia", KGZ="Kirguistán",
  KHM="Camboya", KIR="Kiribati", KNA="San Cristóbal y Nieves", KOR="Corea del Sur", KWT="Kuwait", LAO="Laos",
  LBN="Líbano", LBR="Liberia", LBY="Libia", LCA="Santa Lucía", LIE="Liechtenstein", LKA="Sri Lanka",
  LSO="Lesoto", LTU="Lituania", LUX="Luxemburgo", LVA="Letonia", MAC="Macao", MAF="San Martín (Francia)",
  MAR="Marruecos", MCO="Mónaco", MDA="Moldavia", MDG="Madagascar", MDV="Maldivas", MEX="México",
  MHL="Islas Marshall", MKD="Macedonia del Norte", MLI="Malí", MLT="Malta", MMR="Myanmar", MNE="Montenegro",
  MNG="Mongolia", MNP="Islas Marianas del Norte", MOZ="Mozambique", MRT="Mauritania", MUS="Mauricio",
  MWI="Malaui", MYS="Malasia", NAM="Namibia", NCL="Nueva Caledonia", NER="Níger", NGA="Nigeria", NIC="Nicaragua",
  NLD="Países Bajos", NOR="Noruega", NPL="Nepal", NRU="Nauru", NZL="Nueva Zelanda", OMN="Omán", PAK="Pakistán",
  PAN="Panamá", PER="Perú", PHL="Filipinas", PLW="Palaos", PNG="Papúa Nueva Guinea", POL="Polonia",
  PRI="Puerto Rico", PRK="Corea del Norte", PRT="Portugal", PRY="Paraguay", PSE="Palestina",
  PYF="Polinesia Francesa", QAT="Catar", ROU="Rumanía", RUS="Rusia", RWA="Ruanda", SAU="Arabia Saudita",
  SDN="Sudán", SEN="Senegal", SGP="Singapur", SLB="Islas Salomón", SLE="Sierra Leona", SLV="El Salvador",
  SMR="San Marino", SOM="Somalia", SRB="Serbia", SSD="Sudán del Sur", STP="Santo Tomé y Príncipe",
  SUR="Surinam", SVK="Eslovaquia", SVN="Eslovenia", SWE="Suecia", SWZ="Esuatini", SXM="San Martín (Países Bajos)",
  SYC="Seychelles", SYR="Siria", TCA="Islas Turcas y Caicos", TCD="Chad", TGO="Togo", THA="Tailandia",
  TJK="Tayikistán", TKM="Turkmenistán", TLS="Timor Oriental", TON="Tonga", TTO="Trinidad y Tobago",
  TUN="Túnez", TUR="Turquía", TUV="Tuvalu", TZA="Tanzania", UGA="Uganda", UKR="Ucrania", URY="Uruguay",
  USA="Estados Unidos", UZB="Uzbekistán", VCT="San Vicente y las Granadinas", VEN="Venezuela",
  VGB="Islas Vírgenes Británicas", VIR="Islas Vírgenes de EE. UU.", VNM="Vietnam", VUT="Vanuatu", WSM="Samoa",
  XKX="Kosovo", YEM="Yemen", ZAF="Sudáfrica", ZMB="Zambia", ZWE="Zimbabue"
)


df_countries <- df_countries %>%
  mutate(Country = coalesce(unname(nombres_es[Code]), Country))
df_final <- df_countries %>%
  rename(
    EspVida = `SP.DYN.LE00.IN`,
    Fertilidad = `SP.DYN.TFRT.IN`,
    Natalidad = `SP.DYN.CBRT.IN`,
    MortalidadBruta = `SP.DYN.CDRT.IN`,
    PobUrbana = `SP.URB.TOTL.IN.ZS`,
    MortMenores5 = `SH.DYN.MORT`,
    MortMaterna = `SH.STA.MMRT`,
    IncidenciaTB = `SH.TBS.INCD`,
    GastoSaludPIB = `SH.XPD.CHEX.GD.ZS`,
    MedicosPor1k = `SH.MED.PHYS.ZS`,
    VacunacionDPT = `SH.IMM.IDPT`,
    Desnutricion = `SN.ITK.DEFC.ZS`,
    INB_perCapita = `NY.GNP.PCAP.CD`,
    Pob65Mas = `SP.POP.65UP.TO.ZS`,
    AccesoElectricidad = `EG.ELC.ACCS.ZS`
  )

vars <- variables_info$Variable            
stopifnot(all(vars %in% names(df_final)))  

Depuración por suficiencia muestral y países excluidos

Una decisión de depuración crítica radica en definir el umbral de datos faltantes permitido por país antes de la imputación. En el aprendizaje no supervisado, permitir observaciones con vacíos sustanciales genera el riesgo de que la imputación reemplace la verdadera varianza del país por promedios agregados, provocando que los componentes principales describan artefactos estadísticos de la imputación y no la realidad empírica de la nación.

Para evitar esta distorsión se establece un umbral de tolerancia del 20 % (máximo 3 variables sin dato de las 15 seleccionadas), de modo que ningún país entre al análisis con más de una quinta parte de su perfil construido por imputación. Como resultado, 25 economías fueron excluidas: territorios dependientes y economías pequeñas cuyo sistema estadístico no reporta de manera sistemática los 15 indicadores (como Gibraltar, Islas Feroe, Aruba o Curazao) y casos con cobertura incompleta como Corea del Norte, Kosovo o Hong Kong. La tabla siguiente presenta la relación formal de estos 25 territorios, con su código ISO-3 y la cantidad de variables observadas sobre el total de 15.

df_final <- df_final %>%
  mutate(n_validos = rowSums(!is.na(across(all_of(vars)))))

# Países excluidos por información insuficiente
excluidos <- df_final %>%
  filter(n_validos < length(vars) - MAX_FALTANTES) %>%
  arrange(n_validos, Country) %>%
  select(Country, Code, n_validos)

excluidos %>%
  kbl(caption = paste0("Economías excluidas por tener más de ", MAX_FALTANTES, " variables sin dato (", nrow(excluidos), " en total)"),
      col.names = c("País / Economía", "Código", "Variables con dato (de 15)")) %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = FALSE) %>%
  scroll_box(height = "320px")
Economías excluidas por tener más de 3 variables sin dato (25 en total)
País / Economía Código Variables con dato (de 15)
Gibraltar GIB 7
Islas Vírgenes de EE. UU. VIR 7
Islas del Canal CHI 7
Samoa Americana ASM 7
San Martín (Francia) MAF 7
Groenlandia GRL 8
Guam GUM 8
Isla de Man IMN 8
Islas Feroe FRO 8
Islas Marianas del Norte MNP 8
Kosovo XKX 8
Liechtenstein LIE 8
Aruba ABW 9
Bermudas BMU 9
Curazao CUW 9
Hong Kong HKG 9
Islas Caimán CYM 9
Islas Vírgenes Británicas VGB 9
Macao MAC 9
San Martín (Países Bajos) SXM 9
Corea del Norte PRK 10
Islas Turcas y Caicos TCA 10
Nueva Caledonia NCL 10
Polinesia Francesa PYF 10
Puerto Rico PRI 10
# Base filtrada (antes de imputar)
df_filtered <- df_final %>%
  filter(n_validos >= length(vars) - MAX_FALTANTES) %>%
  select(-n_validos)

# Resumen de faltantes de los países conservados
tabla_faltantes <- tibble(
  Variable = vars,
  Faltantes = colSums(is.na(df_filtered[vars])),
  Porcentaje = round(100 * Faltantes / nrow(df_filtered), 1)
) %>%
  arrange(desc(Faltantes))

tabla_faltantes %>%
  kbl(caption = paste0("Valores faltantes por variable en los ", nrow(df_filtered), " países conservados"),
      col.names = c("Variable", "N.º de faltantes", "% de faltantes")) %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = FALSE)
Valores faltantes por variable en los 192 países conservados
Variable N.º de faltantes % de faltantes
MedicosPor1k 57 29.7
Desnutricion 28 14.6
INB_perCapita 4 2.1
EspVida 0 0.0
Fertilidad 0 0.0
Natalidad 0 0.0
MortalidadBruta 0 0.0
PobUrbana 0 0.0
MortMenores5 0 0.0
MortMaterna 0 0.0
IncidenciaTB 0 0.0
GastoSaludPIB 0 0.0
VacunacionDPT 0 0.0
Pob65Mas 0 0.0
AccesoElectricidad 0 0.0

La estructura de los valores faltantes en los 192 países conservados es asimétrica: se concentra en la densidad de médicos (57 países, 29,7 %), la prevalencia de desnutrición (28 países, 14,6 %) y el INB per cápita (4 países, 2,1 %), mientras que las otras doce variables tienen cobertura completa. Esta concentración importa para el análisis posterior porque dos de las quince variables, y en particular la densidad de médicos, dependen en una proporción considerable de valores imputados, y la tabla anterior cuantifica esa dependencia.

Imputación de valores faltantes por grupo de ingreso

Los valores faltantes no parecen distribuirse al azar entre los países. La densidad de médicos falta en los cuatro grupos de ingreso, pero con una frecuencia que disminuye al aumentar el ingreso: 10 de los 23 países de ingreso bajo, 17 de los 46 de ingreso medio-bajo, 18 de los 60 de ingreso medio-alto y 11 de los 59 de ingreso alto, además de uno de los cuatro países sin INB reportado, lo que es coherente con que los registros de recursos humanos en salud sean menos sistemáticos donde la capacidad estadística es menor. La prevalencia de desnutrición falta en 12 países de ingreso alto, 8 de ingreso medio-alto, 3 de ingreso medio-bajo, 1 de ingreso bajo y los cuatro países sin INB, con predominio de economías pequeñas como Andorra, San Marino, Singapur, Catar o Bahamas; para los demás países desarrollados el WDI no omite el dato sino que lo reporta como el valor tope de 2,5 %, que indica prevalencias inferiores a ese umbral, y 56 de los 192 países aparecen exactamente en ese valor. Estas distribuciones sugieren que la ausencia de dato depende del nivel de ingreso y, por tanto, que no cumple el supuesto de ausencia completamente aleatoria (MCAR).

Imputar con la mediana global generaría un sesgo sistemático, porque a una economía desarrollada sin dato de desnutrición se le asignaría una mediana mundial que incluye a economías de bajos ingresos, mientras que a un país en desarrollo sin dato de médicos se le imputaría una mediana inflada por naciones ricas. Para corregirlo se implementa una imputación condicional basada en la mediana del grupo de ingreso, definido con los umbrales del Banco Mundial para el año fiscal 2021 (Banco Mundial, 2020), construidos con el INB per cápita de 2019 por el método Atlas: ingreso bajo hasta 1.035 dólares, medio-bajo hasta 4.045, medio-alto hasta 12.535 e ingreso alto por encima de ese valor. Los cuatro países sin INB reportado no tienen grupo de ingreso; en ellos el INB se predice con una regresión (descrita tras la tabla de composición) y las otras variables con faltantes reciben la mediana global como respaldo, lo que consolida una matriz final de 192 países y 15 variables completa.

Este procedimiento respeta la estructura macroeconómica de cada país, pero tiene un costo que la literatura sobre datos faltantes documenta (Little y Rubin, 2019) y que debe reconocerse: al asignar a cada vacío la mediana de su grupo de ingreso, los valores imputados quedan alineados con el INB por construcción. En consecuencia, la asociación del ingreso con la densidad de médicos y con la desnutrición puede aparecer más fuerte que con datos completos, y parte de la concordancia entre las agrupaciones empíricas y la clasificación por ingreso que se examina más adelante puede estar inducida por el método de imputación. Un segundo costo es que, al asignar el mismo valor a todos los vacíos de un grupo de ingreso, la imputación por mediana comprime la dispersión intra-grupo: la densidad de médicos y la desnutrición, que son las dos variables más imputadas, tienen menos varianza dentro de cada grupo de lo que tendrían con datos completos, por lo que los grupos de ingreso parecen más homogéneos en ellas de lo que son. Evita el sesgo de nivel de la mediana global, pero no recupera la variabilidad que el dato ausente habría aportado. Por esta razón, la sección 3.15 contrasta la estabilidad de la partición al excluir ambas variables. La tabla final de esta sección muestra la distribución de los 192 países conservados según su categoría de ingreso.

df_filtered <- df_filtered %>%
  mutate(GrupoIngreso = cut(INB_perCapita,
                            breaks = c(-Inf, UMBRALES_INGRESO, Inf),
                            labels = c("Ingreso bajo", "Ingreso medio-bajo", "Ingreso medio-alto", "Ingreso alto")))

# Celdas que se imputan 
celdas_imputadas <- as.data.frame(is.na(df_filtered[vars]))
inb_imputado     <- celdas_imputadas$INB_perCapita

# Función de imputación: mediana del grupo de ingreso
imputar_por_grupo <- function(x, grupo) {
  med_grupo  <- tapply(x, grupo, median, na.rm = TRUE)
  med_imputa <- med_grupo[as.character(grupo)]        
  
# NA si el país no tiene grupo
  med_imputa <- ifelse(is.na(med_imputa), median(x, na.rm = TRUE), med_imputa)
  ifelse(is.na(x), med_imputa, x)
}

vars_sin_inb <- setdiff(vars, "INB_perCapita")
base_final <- df_filtered %>%
  mutate(across(all_of(vars_sin_inb), ~ imputar_por_grupo(.x, GrupoIngreso)))

vars_pred <- vars_sin_inb[colSums(is.na(df_filtered[vars_sin_inb])) == 0]
form_inb  <- as.formula(paste("log1p(INB_perCapita) ~", paste(vars_pred, collapse = " + ")))
mod_inb   <- lm(form_inb, data = as.data.frame(base_final[!inb_imputado, ]))
r2_inb    <- summary(mod_inb)$r.squared
pred_inb  <- expm1(predict(mod_inb, newdata = as.data.frame(base_final[inb_imputado, vars_pred])))
rango_inb <- range(df_filtered$INB_perCapita, na.rm = TRUE)
base_final$INB_perCapita[inb_imputado] <- pmin(pmax(pred_inb, rango_inb[1]), rango_inb[2])
base_final$INB_imputado <- inb_imputado

stopifnot(sum(is.na(base_final[vars])) == 0, !any(duplicated(base_final$Country)))

# Matriz numérica 

data_imputed <- as.data.frame(base_final[vars])
rownames(data_imputed) <- base_final$Country

data_desc <- data_imputed
data_desc$INB_perCapita[inb_imputado] <- NA

inb_paises <- base_final$Country[inb_imputado]
inb_pred_txt <- paste0(inb_paises, " (", f0(base_final$INB_perCapita[inb_imputado]), " dólares)")

base_final %>%
  count(GrupoIngreso, .drop = FALSE) %>%
  mutate(GrupoIngreso = as.character(GrupoIngreso),
         GrupoIngreso = replace_na(GrupoIngreso, "Sin INB reportado"),
         Porcentaje = round(100 * n / sum(n), 1)) %>%
  kbl(caption = paste0("Composición de la muestra final (", nrow(base_final), " países) según grupo de ingreso"),
      col.names = c("Grupo de ingreso (Banco Mundial)", "N.º de países", "%")) %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Composición de la muestra final (192 países) según grupo de ingreso
Grupo de ingreso (Banco Mundial) N.º de países %
Ingreso bajo 23 12.0
Ingreso medio-bajo 46 24.0
Ingreso medio-alto 60 31.2
Ingreso alto 59 30.7
Sin INB reportado 4 2.1

La matriz data_imputed queda completa, con 192 países y 15 variables. El INB de los cuatro países que no lo reportan (Eritrea, Mónaco, Sudán del Sur y Yemen) no se imputó con la mediana global, porque ese valor habría colocado en el mismo punto del eje del ingreso a economías tan distintas como Mónaco y Sudán del Sur, y el ingreso es además la variable con la que se define el grupo usado para imputar las demás. Se predijo con una regresión de log(1 + INB) sobre las 12 variables sin faltantes, ajustada con los 188 países con INB observado (R² = 0,80) y acotada al rango observado; los valores resultantes son Eritrea (1.802 dólares), Mónaco (84.160 dólares), Sudán del Sur (914 dólares) y Yemen (1.268 dólares). Son estimaciones y no datos: sirven solo como entrada del ACP, y todas las medias de INB que se reportan en adelante (estadísticas descriptivas y perfiles de cluster) las excluyen. El INB y los grupos de ingreso siguen siendo la referencia con la que más adelante se contrastan los clusters, de modo que parte de esa concordancia es esperable por construcción, y se la examina también en un escenario sin las dos variables más imputadas.

Análisis Exploratorio y Descriptivo

En esta pestaña se examina el comportamiento individual y conjunto de los 15 indicadores originales antes de cualquier transformación o modelado.

2.1 Tabla resumen de estadísticas descriptivas

Una vez depurada la muestra e imputados los valores faltantes, el primer paso del análisis exploratorio consiste en caracterizar numéricamente cada uno de los 15 indicadores sobre la matriz final de 192 países. Esta primera aproximación descriptiva no debe entenderse como un simple inventario de cifras, sino como la evidencia empírica inicial que permite evaluar la heterogeneidad estructural de la muestra y, con ello, juzgar la plausibilidad de la pregunta de investigación. Para este propósito, el análisis se apoya en las herramientas del análisis descriptivo univariado, como lo son las medidas de tendencia central, de dispersión, de forma y de valores extremos, las cuales permiten sintetizar el comportamiento de cada variable y fundamentar las decisiones de preprocesamiento que preceden al Análisis de Componentes Principales y a la clusterización jerárquica. La tabla a continuación presenta el resumen estadístico de cada indicador, incluyendo la proporción de valores que fue necesario imputar durante la fase de preparación de datos.

summary_stats <- data_desc %>%
  pivot_longer(cols = everything(), names_to = "Variable", values_to = "Valor") %>%
  filter(!is.na(Valor)) %>%           
  
  # el INB imputado 
  group_by(Variable) %>%
  summarise(
    Media = mean(Valor),
    Desv_Estandar = sd(Valor),
    CV = 100 * sd(Valor) / mean(Valor),
    Minimo = min(Valor),
    Q1 = quantile(Valor, 0.25),
    Mediana = median(Valor),
    Moda = { ancho <- tryCatch(bw.SJ(Valor), error = function(e) bw.nrd0(Valor))   
    
           dens  <- density(Valor, bw = ancho); dens$x[which.max(dens$y)] },   
    Q3 = quantile(Valor, 0.75),
    Maximo = max(Valor),
    Asimetria = mean((Valor - mean(Valor))^3) / mean((Valor - mean(Valor))^2)^1.5,
    Atipicos = sum(Valor < Q1 - 1.5 * (Q3 - Q1) | Valor > Q3 + 1.5 * (Q3 - Q1)),
    .groups = "drop"
  ) %>%
  left_join(select(tabla_faltantes, Variable, Porcentaje), by = "Variable") %>%
  mutate(Variable = factor(Variable, levels = vars)) %>%
  arrange(Variable) %>%
  left_join(select(variables_info, Variable, Unidad_Medida), by = "Variable") %>%
  select(Variable, Unidad_Medida, Media, Desv_Estandar, CV, Minimo, Q1, Mediana, Moda, Q3, Maximo, Asimetria, Atipicos, Porcentaje)

summary_stats %>%
  kbl(caption = paste0("Estadísticas Descriptivas de los 15 Indicadores Seleccionados (", ANIO, ", n = ", nrow(data_imputed), " países)"),
      digits = 2,
      col.names = c("Variable", "Unidad", "Media", "Desv. est.", "CV (%)", "Mín.", "Q1", "Mediana", "Moda*", "Q3", "Máx.",
                    "Asimetría", "N.º atípicos", "% imputado")) %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = FALSE) %>%
  footnote(general = "*Variables continuas: la moda se estima como el valor donde la densidad de kernel (ancho de banda de Sheather y Jones, 1991) alcanza su máximo. Estadísticas calculadas sobre la base final (con imputación; el INB excluye a los cuatro países con valor imputado); la última columna indica qué proporción de cada variable fue imputada.",
           general_title = "Nota:", footnote_as_chunk = TRUE) %>%
  scroll_box(width = "100%")
Estadísticas Descriptivas de los 15 Indicadores Seleccionados (2019, n = 192 países)
Variable Unidad Media Desv. est. CV (%) Mín. Q1 Mediana Moda* Q3 Máx. Asimetría N.º atípicos % imputado
EspVida Años 72.25 8.06 11.15 31.53 66.49 72.89 74.63 77.91 86.15 -0.86 1 0.0
Fertilidad Nacimientos por mujer 2.64 1.30 49.33 0.92 1.60 2.19 1.58 3.44 6.56 1.02 4 0.0
Natalidad Por cada 1.000 personas 19.84 10.01 50.48 5.90 11.00 17.47 10.61 28.02 45.14 0.63 0 0.0
MortalidadBruta Por cada 1.000 personas 7.88 3.30 41.89 0.84 6.10 7.41 6.64 9.34 30.25 2.19 7 0.0
PobUrbana % de la población total 59.59 22.63 37.99 13.93 40.59 61.18 66.01 77.46 100.00 -0.14 0 0.0
MortMenores5 Por cada 1.000 nacidos vivos 28.37 32.07 113.04 1.50 6.77 16.40 5.14 42.88 254.70 2.72 7 0.0
MortMaterna Por cada 100.000 nacidos vivos 135.19 211.93 156.77 1.00 12.00 49.50 9.06 170.75 1650.00 3.32 20 0.0
IncidenciaTB Por cada 100.000 personas 112.60 153.04 135.91 0.00 11.75 48.00 9.74 155.75 877.00 2.15 15 0.0
GastoSaludPIB % del PIB 6.52 3.18 48.77 2.13 4.26 5.73 4.54 8.34 22.22 1.62 5 0.0
MedicosPor1k Por cada 1.000 personas 2.06 1.70 82.57 0.03 0.63 2.24 0.14 3.41 8.68 0.92 2 29.7
VacunacionDPT % de niños (12-23 meses) 88.55 12.19 13.77 35.00 85.00 93.00 97.66 97.00 99.00 -1.79 15 0.0
Desnutricion % de la población 8.88 9.48 106.77 2.50 2.50 4.40 2.56 11.72 53.80 1.94 16 14.6
INB_perCapita USD actuales (Método Atlas) 14333.99 18746.38 130.78 250.00 2217.50 5975.00 1859.11 17957.50 84160.00 1.84 25 2.1
Pob65Mas % de la población total 8.95 6.66 74.43 1.17 3.57 6.23 3.30 14.26 36.29 1.05 1 0.0
AccesoElectricidad % de la población 84.77 25.32 29.87 6.80 76.53 99.75 100.01 100.00 100.00 -1.60 17 0.0
Nota: *Variables continuas: la moda se estima como el valor donde la densidad de kernel (ancho de banda de Sheather y Jones, 1991) alcanza su máximo. Estadísticas calculadas sobre la base final (con imputación; el INB excluye a los cuatro países con valor imputado); la última columna indica qué proporción de cada variable fue imputada.

En primer lugar, la tabla documenta una heterogeneidad sustancial y, en varios indicadores, una asimetría positiva marcada. La mortalidad materna (coeficiente de variación de 157 % y asimetría de 3,32), la mortalidad de menores de cinco años (113 % y 2,72), la incidencia de tuberculosis (136 % y 2,15) y el INB per cápita, calculado con los países que lo reportan (131 % y 1,84), tienen medias muy superiores a sus medianas: en la mortalidad materna la media es de 135 frente a una mediana de 50, y en el INB de 14.334 frente a 5.975 dólares. Un grupo reducido de países con valores extremos arrastra el promedio, de modo que la mediana describe mejor al país típico. La desnutrición tiene una asimetría de 1,94, pero su valor mínimo y su moda coinciden con el tope de reporte del WDI (2,5 %): 68 de los 192 países tienen exactamente ese valor, 56 por el tope y el resto por imputación, de modo que la moda de 2,56 % refleja la censura del dato y no un valor modal real.

En segundo lugar, la dispersión relativa varía entre dimensiones. La esperanza de vida (coeficiente de variación de 11 %) y la vacunación DPT (14 %) son de los indicadores más homogéneos, mientras que la mortalidad materna, la incidencia de tuberculosis y el INB superan el 100 %. Las diferencias de escala (la mortalidad materna llega a 1.650 por 100.000 nacidos vivos y el INB a 84.160 dólares, frente a rangos de unas decenas en la esperanza de vida) confirman que el análisis multivariado exige estandarizar los datos, porque sin ello las variables de mayor magnitud dominarían las distancias y las componentes.

En tercer lugar, la columna de atípicos, calculada con la regla de 1,5 veces el rango intercuartílico, señala al INB per cápita (25 países), la mortalidad materna (20), el acceso a la electricidad (17), la desnutrición (16) y la incidencia de tuberculosis (15) como las variables con más casos aislados. Los atípicos no se eliminan, porque forman parte de la heterogeneidad que se estudia, pero tampoco se interpretan por defecto como valores reales: los más extremos se verifican a continuación.

En síntesis, la tabla establece tres hechos que condicionan el resto del análisis: la amplitud y la asimetría de varios indicadores muestran diferencias estructurales considerables; la disparidad de escalas obliga a estandarizar; y la asimetría exige decidir, con una regla explícita, qué variables se transforman antes del ACP (sección 2.4).

El caso más extremo de la muestra es la República Centroafricana, y por eso se contrastan sus valores con el resto de países y con los metadatos del archivo antes de decidir cómo tratarla.

rca <- nombres_es[["CAF"]]
verif_rca <- map_dfr(c("EspVida", "MortalidadBruta", "MortMenores5", "MortMaterna"), function(v) {
  x <- data_imputed[[v]]; names(x) <- rownames(data_imputed)
  otros <- x[names(x) != rca]
  sig <- if (v == "EspVida") min(otros) else max(otros)
  tibble(Variable = v, `Valor RCA` = x[[rca]], `Siguiente valor más extremo` = sig,
         `País siguiente` = names(otros)[which(otros == sig)[1]],
         `Z-score RCA` = (x[[rca]] - mean(x)) / sd(x))
})
verif_rca %>%
  kbl(digits = 2, caption = "República Centroafricana frente al siguiente valor más extremo de la muestra") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
República Centroafricana frente al siguiente valor más extremo de la muestra
Variable Valor RCA Siguiente valor más extremo País siguiente Z-score RCA
EspVida 31.53 52.99 Chad -5.05
MortalidadBruta 30.25 22.36 Mónaco 6.77
MortMenores5 254.70 120.80 Níger 7.06
MortMaterna 1650.00 1091.00 Nigeria 7.15
# Metadatos del WDI
ruta_meta <- list.files(dirname(ruta_final), pattern = "Metadata\\.csv$", full.names = TRUE)
meta_series <- tryCatch({
  lineas <- enc2utf8(readLines(ruta_meta[1], encoding = "latin1", warn = FALSE))
  i0 <- grep("^Code,License Type,Indicator Name", lineas)[1]
  read_csv(I(paste(lineas[i0:length(lineas)], collapse = "\n")), col_types = cols(.default = col_character()),
           show_col_types = FALSE) %>%
    filter(Code %in% c("SP.DYN.LE00.IN", "SP.DYN.CDRT.IN", "SH.DYN.MORT")) %>%
    transmute(Serie = `Indicator Name`, Fuente = substr(gsub("\\s+", " ", Source), 1, 230))
}, error = function(e) NULL)
if (!is.null(meta_series) && nrow(meta_series) > 0) {
  meta_series %>%
    kbl(caption = "Fuente declarada en los metadatos para las series de mortalidad") %>%
    kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
}
Fuente declarada en los metadatos para las series de mortalidad
Serie Fuente
Life expectancy at birth, total (years) World Population Prospects, United Nations (UN), uri: UN Population Division; Statistical databases and publications from national statistical offices, National Statistical Offices (NSOs), note: Derived from male and female life e
Death rate, crude (per 1,000 people) World Population Prospects, United Nations (UN), publisher: UN Population Division; Statistical databases and publications from national statistical offices, National Statistical Offices (NSOs); Demographic Statistics, Eurostat (E
Mortality rate, under-5 (per 1,000 live births) UN Inter-agency Group for Child Mortality Estimation, UN Children’s Fund (UNICEF), uri: www.childmortality.org, publisher: UNICEF, WHO, World Bank, United Nations Population Division; World Health Organization (WHO), uri: www.chil

La República Centroafricana tiene una esperanza de vida de 31,5 años, 21,5 años menos que el país siguiente (Chad, 53,0), y una mortalidad bruta de 30,2 por mil, frente a 22,4 en el siguiente (Mónaco); también encabeza la mortalidad de menores de cinco años (255) y la materna (1.650). Los tres indicadores de mortalidad extrema apuntan en la misma dirección, lo que indica que no se trata de una cifra aislada mal digitada en una sola serie, pero con los datos disponibles no es posible confirmarla ni descartarla con una segunda fuente: los metadatos del archivo indican la fuente de cada serie, no permiten verificar el valor de un país concreto. Por eso la decisión es conservar a la República Centroafricana en el análisis principal, tratarla como un caso influyente y medir su efecto: en la sección 3.15 se reporta la partición con y sin ella, y las conclusiones se leen como contingentes a ese caso.

2.2 Países con valores extremos por indicador

Si bien la tabla anterior nos permitió caracterizar el comportamiento agregado de cada indicador, esa síntesis univariada oculta la identidad concreta de los países que ocupan las posiciones extremas de cada distribución. Este segundo nivel de análisis, que se apoya en las herramientas del análisis descriptivo de valores extremos, identificando máximos y mínimos observados por variable, tiene un propósito analítico preciso dentro del marco del aprendizaje no supervisado: verificar si los países que se ubican en los extremos de cada indicador coinciden sistemáticamente entre dimensiones. Si los mismos países aparecen repetidamente en los extremos de múltiples variables, esto constituye una evidencia preliminar de que existen configuraciones estructurales coherentes, es decir, que la heterogeneidad no es aleatoria sino que se organiza en torno a perfiles, lo cual valida la pertinencia de aplicar posteriormente técnicas de reducción de dimensionalidad y agrupamiento. Por el contrario, si los extremos de cada indicador estuvieran ocupados por países distintos y sin relación entre sí, la idea de que la heterogeneidad se organiza en perfiles perdería sustento empírico antes de cualquier modelación. La tabla a continuación presenta los tres países con mayor y menor valor observado en cada uno de los 15 indicadores, empleando exclusivamente los datos originales sin imputación para preservar la fidelidad de los extremos.

fmt_top <- function(d, v, n = 3) paste0(d$Country[1:n], " (", format(round(d[[v]][1:n], 1), big.mark = ".", decimal.mark = ",", trim = TRUE), ")", collapse = ", ")

extremos <- map_dfr(vars, function(v) {
  d_desc <- df_filtered %>% filter(!is.na(.data[[v]])) %>% arrange(desc(.data[[v]]))
  d_asc  <- df_filtered %>% filter(!is.na(.data[[v]])) %>% arrange(.data[[v]])
  tibble(Variable = v, Mayores = fmt_top(d_desc, v), Menores = fmt_top(d_asc, v))
})

extremos %>%
  kbl(caption = "Los tres países con mayor y menor valor en cada indicador (valores observados)",
      col.names = c("Variable", "Tres valores más altos", "Tres valores más bajos")) %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = TRUE) %>%
  column_spec(1, bold = TRUE)
Los tres países con mayor y menor valor en cada indicador (valores observados)
Variable Tres valores más altos Tres valores más bajos
EspVida Mónaco (86,2), San Marino (85,3), Japón (84,4) Rep. Centroafricana (31,5), Chad (53,0), Nigeria (53,0)
Fertilidad Somalia (6,6), Níger (6,5), Chad (6,4) Corea del Sur (0,9), Andorra (1,0), San Marino (1,1)
Natalidad Somalia (45,1), Chad (44,3), Rep. Centroafricana (44,1) Corea del Sur (5,9), San Marino (6,7), Andorra (7,0)
MortalidadBruta Rep. Centroafricana (30,2), Mónaco (22,4), Bulgaria (16,3) Catar (0,8), Emiratos Árabes Unidos (1,0), Kuwait (1,6)
PobUrbana Baréin (100), Kuwait (100), Mónaco (100) Papúa Nueva Guinea (13,9), Malaui (16,1), Níger (16,8)
MortMenores5 Rep. Centroafricana (254,7), Níger (120,8), Somalia (118,4) San Marino (1,5), Finlandia (2,4), Luxemburgo (2,4)
MortMaterna Rep. Centroafricana (1.650), Nigeria (1.091), Chad (958) Bielorrusia (1), Noruega (2), Polonia (2)
IncidenciaTB Islas Marshall (877), Kiribati (715), Lesoto (654) Antigua y Barbuda (0), Barbados (0), Mónaco (0)
GastoSaludPIB Tuvalu (22,2), Lesoto (21,9), Estados Unidos (16,5) Bangladés (2,1), Venezuela (2,2), Angola (2,2)
MedicosPor1k Cuba (8,7), Mónaco (8,6), Georgia (7,5) Sierra Leona (0), Sudán del Sur (0), Níger (0)
VacunacionDPT Albania (99), Andorra (99), Antigua y Barbuda (99) Papúa Nueva Guinea (35), Rep. Centroafricana (38), Chad (53)
Desnutricion Somalia (53,8), Haití (42,6), Madagascar (39,6) Argelia (2,5), Armenia (2,5), Australia (2,5)
INB_perCapita Suiza (84.160), Noruega (82.500), Luxemburgo (79.280) Burundi (250), RD del Congo (460), Rep. Centroafricana (500)
Pob65Mas Mónaco (36,3), Japón (28,6), Italia (23,1) Catar (1,2), Emiratos Árabes Unidos (1,5), Zambia (1,8)
AccesoElectricidad Albania (100), Andorra (100), Antigua y Barbuda (100) Sudán del Sur (6,8), Chad (8,4), Burundi (9,8)

La tabla muestra, en primer lugar, que los extremos de las variables de supervivencia y de fecundidad se repiten entre países de África subsahariana. La República Centroafricana encabeza la mortalidad de menores de cinco años y la mortalidad materna (1.650 por 100.000) y tiene la esperanza de vida más baja (31,5 años); Chad y Nigeria completan, junto con ella, los tres valores más bajos de esperanza de vida y los tres más altos de mortalidad materna; y Somalia encabeza la fertilidad y la natalidad, con Níger y Chad en la fertilidad y Chad y la República Centroafricana en la natalidad. En el extremo opuesto, la esperanza de vida más alta corresponde a Mónaco, San Marino y Japón, la mortalidad de menores de cinco años más baja a San Marino, Finlandia y Noruega, y la fertilidad y la natalidad más bajas a Corea del Sur, Andorra y San Marino. Esta coincidencia indica que los mismos países ocupan posiciones opuestas en varias dimensiones, pero no basta por sí sola para concluir que existen grupos, porque la tabla solo recoge los tres casos de cada cola.

Otros indicadores no siguen ese patrón. La incidencia de tuberculosis tiene sus tres valores más altos en las Islas Marshall, Kiribati y Lesoto, es decir, en Estados insulares del Pacífico y en un país del África austral, y los más bajos en Antigua y Barbuda, Barbados y Mónaco. El gasto en salud como porcentaje del PIB alcanza sus máximos en Tuvalu, Lesoto y Estados Unidos y sus mínimos en Bangladés, Venezuela y Angola, y la densidad de médicos tiene sus máximos en Cuba, Mónaco y Georgia, de modo que economías de ingreso medio como Cuba y Georgia comparten la cima de la dotación médica con una de ingreso alto. La desnutrición tiene sus valores más altos en Somalia, Haití y Madagascar, pero su cola baja es un empate de 56 países en el tope de reporte de 2,5 %, por lo que Australia, Austria y Armenia aparecen como los tres menores solo por el orden de los datos y no porque tengan una desnutrición menor que los demás países del empate. Estas divergencias indican que las dimensiones del bienestar no están perfectamente sincronizadas entre países.

La mortalidad bruta y la población de 65 años o más muestran extremos de un tipo distinto. La mortalidad bruta es más alta en la República Centroafricana, Mónaco y Bulgaria y más baja en Catar, Emiratos Árabes Unidos y Kuwait, mientras que la población de 65 años o más es mayor en Mónaco (36,3 %), Japón (28,6 %) e Italia (23,1 %) y menor en Catar (1,2 %), Emiratos Árabes Unidos (1,5 %) y Zambia (1,8 %). Una mortalidad bruta elevada aparece así tanto en un país con mortalidad prematura, la República Centroafricana, como en dos de población envejecida, Mónaco y Bulgaria, y la más baja en economías de ingreso alto de población joven, lo que muestra que esta variable no mide lo mismo que los indicadores de supervivencia.

El INB per cápita muestra la brecha más amplia de la muestra: de 250 dólares en Burundi, 460 en la República Democrática del Congo y 500 en la República Centroafricana a 79.280 en Luxemburgo, 82.500 en Noruega y 84.160 en Suiza, una razón superior a 300 entre extremos. La posición en el ingreso no reproduce exactamente la posición sanitaria: de los tres países de mayor INB solo Noruega figura entre los tres de menor mortalidad de menores de cinco años, y los tres de menor INB comparten con los extremos sanitarios solo a la República Centroafricana, que ocupa los lugares inferiores en esperanza de vida y superiores en mortalidad. Esto indica que el nivel de riqueza se asocia con el bienestar sanitario sin agotar su explicación.

En conjunto, la tabla describe extremos coincidentes en las variables de supervivencia y fecundidad y extremos que no coinciden en la tuberculosis, el gasto en salud y la dotación de médicos. Es una evidencia descriptiva de que la heterogeneidad tiene un núcleo común, pero también dimensiones que no lo siguen; no establece cuántos grupos existen ni su composición, cuestión que corresponde a los métodos de la pestaña siguiente.

2.3 Distribución de cada variable (histogramas y densidad)

Una vez identificados los valores extremos y las posiciones de los países en cada indicador, el siguiente paso del análisis exploratorio consiste en examinar la forma completa de las distribuciones. Mientras que la tabla descriptiva resumió cada variable en un conjunto de estadísticos y la tabla de extremos destacó los casos límite, los histogramas con curvas de densidad permiten visualizar la totalidad de la distribución y evaluar propiedades que los estadísticos resumen no capturan con suficiente claridad: la presencia de multimodalidad, la concentración de observaciones en ciertos rangos, la existencia de colas largas y la forma general de la asimetría. Este análisis se apoya en las herramientas del análisis descriptivo de distribuciones univariadas (histogramas y estimación de densidad por kernel), herramientas que constituyen la representación gráfica complementaria a las medidas numéricas ya presentadas. Su relevancia dentro del marco del aprendizaje no supervisado es doble: por un lado, permite evaluar visualmente si conviene transformar variables antes del Análisis de Componentes Principales, y por otro, mostrar si hay concentraciones de valores que merezcan atención posterior, sin que la forma univariada de una variable permita todavía afirmar que existan subgrupos de países. La figura presenta las distribuciones de los 15 indicadores en su escala original.

data_desc %>%
  pivot_longer(cols = everything(), names_to = "Variable", values_to = "Valor") %>%
  mutate(Variable = factor(Variable, levels = vars)) %>%
  ggplot(aes(x = Valor)) +
  geom_histogram(aes(y = after_stat(density)), bins = 20, fill = "#B590D3", color = "white", alpha = 0.85) +
  geom_density(color = "#E58DB8", linewidth = 1) +
  facet_wrap(~ Variable, scales = "free", ncol = 4) +
  theme_minimal() +
  labs(title = "Distribución de los 15 indicadores (escala original)",
       subtitle = "Histograma y densidad estimada; varias variables muestran asimetría positiva (cola derecha)",
       x = "Valor del indicador", y = "Densidad")

Las distribuciones muestran que ninguno de los indicadores es estrictamente simétrico, aunque la forma de la asimetría cambia con el tipo de variable. En los indicadores de carencia, la mayor parte de los países se concentra en valores bajos y una cola derecha larga reúne a pocos países con valores muy altos, como ocurre con la mortalidad materna (asimetría de 3,32), la mortalidad de menores de cinco años (2,72), la incidencia de tuberculosis (2,15) y la desnutrición (1,94). En los indicadores de acceso y cobertura ocurre lo contrario: la esperanza de vida (−0,86), la vacunación DPT (−1,79) y el acceso a la electricidad (−1,60) tienen una cola izquierda, porque casi todos los países están cerca de un techo y pocos se alejan mucho de él.

El INB per cápita (asimetría de 1,84, con los países que lo reportan) tiene una distribución de tipo log-normal, y también la mortalidad materna, la de menores de cinco años y la incidencia de tuberculosis tienen colas derechas que un logaritmo puede comprimir. Sobre esa base, el ACP no se calcula en escala original: se aplica log1p a las variables que cumplan una regla declarada a priori (sección 2.4) y la escala original se conserva como análisis de sensibilidad (sección 3.15). La razón es técnica: con variables estandarizadas, las cargas del ACP son correlaciones y no tienen unidades, y una cola derecha larga hace que unos pocos países determinen el coeficiente de Pearson y, con él, la orientación de los primeros componentes. La transformación es monótona, de modo que no altera el orden de los países en cada indicador.

Las variables demográficas y de capacidad sanitaria muestran formas intermedias. La fertilidad (1,02) y la natalidad (0,63) tienen una asimetría moderada, la densidad de médicos (0,92) y la población de 65 años o más (1,05) una cola derecha más suave, y la población urbana (−0,14) es la más simétrica.

En síntesis, el análisis de formas respalda tres decisiones metodológicas: estandarizar para igualar escalas, aplicar una regla explícita de transformación a las colas derechas largas y conservar a los países extremos porque forman parte de la heterogeneidad que se estudia. Las distribuciones por sí solas no indican si los países se agrupan en perfiles; esa pregunta se aborda con las técnicas no supervisadas de la sección 3.

2.4 Valores atípicos y comparación de escalas (Boxplots estandarizados)

El análisis de distribuciones evidenció que la mayoría de los indicadores presenta asimetrías pronunciadas y colas largas, lo cual sugiere la presencia de observaciones que se alejan del comportamiento típico de la muestra. La identificación sistemática de estos valores atípicos y la comparación simultánea de las escalas de las 15 variables constituyen el último eslabón del análisis exploratorio univariado antes de abordar las relaciones multivariadas. Para este propósito, el estudio recurre al análisis de valores atípicos mediante boxplots sobre datos estandarizados (Z-scores), una herramienta que permite, por un lado, detectar las observaciones que se sitúan fuera del rango intercuartílico en cada indicador y, por otro, comparar visualmente la dispersión relativa de las 15 variables en una escala común. Su relevancia dentro del marco del aprendizaje no supervisado es doble: primero, porque la presencia de outliers influye directamente en el cálculo de la matriz de correlaciones y en la estimación de los componentes principales, de modo que su identificación temprana permite valorar la robustez de los resultados factoriales; segundo, porque la comparación de escalas confirma visualmente la necesidad de estandarizar los datos antes del ACP y proporciona una idea de qué variables concentran la mayor variabilidad estructural, información que será crucial para interpretar las cargas factoriales en la sección de reducción dimensional. La figura a continuación presenta los boxplots estandarizados de los 15 indicadores, con los valores atípicos resaltados.

# Estandarización temporal solo para visualización conjunta de Boxplots
data_scaled_df <- as.data.frame(scale(data_desc)) %>%
  rownames_to_column(var = "Country") %>%
  pivot_longer(cols = -Country, names_to = "Variable", values_to = "Valor_Estandarizado", values_drop_na = TRUE) %>%
  group_by(Variable) %>%
  mutate(Atipico = Valor_Estandarizado < quantile(Valor_Estandarizado, 0.25) - 1.5 * IQR(Valor_Estandarizado) |
                   Valor_Estandarizado > quantile(Valor_Estandarizado, 0.75) + 1.5 * IQR(Valor_Estandarizado)) %>%
  ungroup()

p_box <- ggplot(data_scaled_df, aes(x = reorder(Variable, Valor_Estandarizado, FUN = median), y = Valor_Estandarizado, fill = Variable)) +
  geom_boxplot(alpha = 0.7, outlier.shape = NA) +
  geom_point(data = filter(data_scaled_df, Atipico),
             aes(text = paste0("<b>", Country, "</b><br>", Variable, "<br>Z-score: ", round(Valor_Estandarizado, 2))),
             color = "red", size = 1.6, alpha = 0.8) +
  coord_flip() +
  theme_minimal() +
  theme(legend.position = "none") +
  labs(title = "Distribución y Valores Atípicos (Boxplots de Variables Estandarizadas)",
       subtitle = "Puntos rojos: valores atípicos; pase el cursor para ver el país",
       x = "Indicadores Socio-Sanitarios",
       y = "Escala Estandarizada (Z-score)")

ggplotly(p_box, tooltip = "text")

Los boxplots estandarizados muestran que la estandarización iguala las escalas pero no la dispersión relativa ni la frecuencia de atípicos. Los recuentos coinciden con los de la tabla descriptiva: 25 países atípicos en el INB, 20 en la mortalidad materna, 17 en el acceso a la electricidad, 16 en la desnutrición y 15 en la incidencia de tuberculosis. Las variables demográficas muestran menos casos aislados: la esperanza de vida tiene 1, la población de 65 años o más 1, la fertilidad 4, la mortalidad bruta 7, la natalidad 0 y la población urbana 0, de modo que su dispersión está menos dominada por países individuales.

Un riesgo metodológico de esta configuración es que el ACP opera sobre correlaciones de Pearson, sensibles a los valores extremos. La estandarización previa no lo elimina, porque los países atípicos mantienen puntuaciones Z elevadas, y por eso la forma de las colas se evalúa a continuación con una regla explícita. La regla, fijada antes de ver los resultados del ACP, es la siguiente: una variable es candidata si su asimetría original supera 1,5, y se transforma con log1p (válido con ceros, como en la incidencia de tuberculosis) solo si su asimetría tras la transformación queda por debajo de 0,5 en valor absoluto. Las variables con cola izquierda no se transforman, porque el logaritmo no corrige un efecto techo.

asimetria <- function(x) { x <- x[!is.na(x)]; mean((x - mean(x))^3) / mean((x - mean(x))^2)^1.5 }
n_atip <- function(x) { q <- quantile(x, c(0.25, 0.75)); sum(x < q[1] - 1.5 * diff(q) | x > q[2] + 1.5 * diff(q)) }

tabla_asim <- tibble(Variable = vars,
                     Asim_orig = sapply(vars, function(v) asimetria(data_imputed[[v]])),
                     Asim_log  = sapply(vars, function(v) asimetria(log1p(data_imputed[[v]]))),
                     Atip_orig = sapply(vars, function(v) n_atip(data_imputed[[v]])),
                     Atip_log  = sapply(vars, function(v) n_atip(log1p(data_imputed[[v]])))) %>%
  mutate(Candidata   = Asim_orig > UMBRAL_ASIM_LOG,
         Transformar = Candidata & abs(Asim_log) < UMBRAL_ASIM_POST)

vars_log   <- tabla_asim$Variable[tabla_asim$Transformar]
vars_no_log <- tabla_asim$Variable[tabla_asim$Candidata & !tabla_asim$Transformar]

# Matriz de entrada del ACP: escala original con log1p en las variables seleccionadas por la regla
data_model <- data_imputed %>% mutate(across(all_of(vars_log), log1p))

tabla_asim %>%
  filter(Candidata) %>%
  transmute(Variable, `Asimetría original` = Asim_orig, `Asimetría (log1p)` = Asim_log,
            `Atípicos originales` = Atip_orig, `Atípicos (log1p)` = Atip_log,
            Decisión = ifelse(Transformar, "log1p", "sin transformar")) %>%
  kbl(digits = 2, caption = "Regla de transformación: variables candidatas (asimetría original > 1,5) y decisión") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Regla de transformación: variables candidatas (asimetría original > 1,5) y decisión
Variable Asimetría original Asimetría (log1p) Atípicos originales Atípicos (log1p) Decisión
MortalidadBruta 2.19 -0.68 7 9 sin transformar
MortMenores5 2.72 0.09 7 0 log1p
MortMaterna 3.32 0.01 20 0 log1p
IncidenciaTB 2.15 -0.23 15 0 log1p
GastoSaludPIB 1.62 0.19 5 2 log1p
Desnutricion 1.94 0.80 16 0 sin transformar
INB_perCapita 1.87 0.02 26 0 log1p

Cumplen la regla 5 variables, que entran al ACP con log1p: la mortalidad de menores de cinco años, la mortalidad materna, la incidencia de tuberculosis, el gasto en salud y el INB per cápita. La transformación reduce la asimetría del INB de 1,87 a 0,02, la de la mortalidad materna de 3,32 a 0,01, la de la mortalidad de menores de cinco años de 2,72 a 0,09 y la de la incidencia de tuberculosis de 2,15 a −0,23. Las variables candidatas que no cumplen la segunda condición (la mortalidad bruta y la desnutrición) se dejan en escala original, porque el logaritmo no deja su asimetría por debajo de 0,5. Con esta regla la matriz que alimenta el ACP deja de estar dominada por unos pocos países en cada cola; la escala original, que es la alternativa, se evalúa como escenario de sensibilidad en la sección 3.15.

2.5 Gráficos dinámicos interactivos de exploración

  1. Esperanza de vida frente al INB per cápita. Pase el cursor sobre cada punto para ver el país y sus indicadores (solo se grafican países con INB observado, no imputado).

La primera herramienta de exploración es un diagrama de dispersión interactivo que cruza la esperanza de vida con el ingreso nacional bruto per cápita, expresado en dólares y en escala logarítmica. Esta escala permite comparar en un mismo plano países cuyos ingresos difieren en órdenes de magnitud, algo que en escala lineal dejaría a la mayoría de economías comprimidas contra el eje vertical. Además de las dos variables principales, el gráfico incorpora otras dos dimensiones del fenómeno: el tamaño de cada punto representa la mortalidad de menores de cinco años y su color el porcentaje de población con acceso a electricidad. De este modo, una sola figura reúne cuatro indicadores del conjunto de quince, y permite observar desde el inicio si el desarrollo económico, la salud y la infraestructura básica se mueven de manera conjunta. Solo se grafican los países con INB efectivamente observado, de modo que ninguna posición proviene de datos imputados.

datos_scatter <- df_filtered %>% filter(!is.na(INB_perCapita))

p <- ggplot(datos_scatter,
            aes(x = INB_perCapita, y = EspVida,
                text = paste0("<b>", Country, "</b>",
                              "<br>INB per cápita: US$ ", format(round(INB_perCapita), big.mark = "."),
                              "<br>Esperanza de vida: ", round(EspVida, 1), " años",
                              "<br>Mortalidad <5 años: ", round(MortMenores5, 1), " por 1.000",
                              "<br>Acceso a electricidad: ", round(AccesoElectricidad, 1), " %"))) +
  geom_point(aes(size = MortMenores5, color = AccesoElectricidad), alpha = 0.7) +
  scale_x_log10(labels = scales::dollar) +
  scale_color_viridis_c(option = "magma") +
  theme_minimal() +
  labs(title = "Exploración Socioeconómica y Salud: Esperanza de Vida vs. INB per Cápita",
       x = "INB per Cápita (USD, escala logarítmica)",
       y = "Esperanza de Vida (Años)",
       color = "Acceso Elec. (%)",
       size = "Mort. <5 años")

ggplotly(p, tooltip = "text")

La nube de puntos muestra una relación creciente y marcada entre el ingreso y la esperanza de vida. Los países con ingresos por debajo de los mil dólares se ubican, en su mayoría, entre los 53 y los 66 años, mientras que los de ingresos superiores a los diez mil dólares se concentran entre los 72 y los 85 años. La pendiente es más pronunciada en el tramo de bajos ingresos y se va aplanando hacia la derecha, lo que indica que los primeros incrementos del ingreso se asocian con ganancias de longevidad mucho mayores que las asociadas a los incrementos en las economías ya prósperas. La dispersión vertical tampoco es uniforme. En el tramo comprendido entre los mil y los cinco mil dólares los países se reparten en un rango amplio, desde cerca de 53 hasta más de 72 años, lo que muestra que a niveles de ingreso similares existen resultados de salud claramente distintos. Esa zona intermedia es la que más heterogeneidad concentra, y pone de manifiesto que el ingreso por sí solo no define el bienestar socio-sanitario de un país.

En cambio, a la derecha del gráfico los puntos se agrupan de forma compacta, lo que refleja una mayor homogeneidad entre las economías de ingresos altos. Las dimensiones adicionales refuerzan esta lectura. El color evidencia un gradiente nítido: los puntos oscuros, con acceso a electricidad bajo, se acumulan en la esquina inferior izquierda, y a medida que el ingreso crece los tonos se aclaran hasta llegar a coberturas cercanas al cien por ciento en prácticamente todos los países de ingresos altos. El tamaño de los puntos sigue un patrón equivalente, pues las burbujas más grandes, que corresponden a mayor mortalidad de menores de cinco años, se ubican en la parte baja del gráfico y de la izquierda, y se reducen hasta volverse pequeñas en la zona superior derecha. Destaca además un caso aislado cerca de los 31 años de esperanza de vida, con ingreso cercano a los quinientos dólares, mortalidad de menores de cinco años muy elevada y acceso a electricidad bajo, que se separa de manera evidente del resto de la muestra.

Para la investigación, esta exploración visual cumple la función de mostrar, antes de aplicar las técnicas de aprendizaje no supervisado, que los indicadores socioeconómicos y sanitarios aparecen entrelazados en los datos: las posiciones altas en ingreso coinciden con alta esperanza de vida, baja mortalidad de menores de cinco años y amplia cobertura eléctrica, y lo contrario ocurre en el extremo inferior. También deja ver que la transición entre ambos extremos no es abrupta sino gradual, con una franja intermedia muy diversa. Estas son las características de la muestra que el análisis de componentes principales y la clusterización jerárquica de Ward tendrán que abordar en las secciones siguientes.

  1. Distribución de la esperanza de vida por grupo de ingreso.

La segunda herramienta de exploración es un diagrama de cajas que descompone la esperanza de vida según la clasificación de ingreso del Banco Mundial: ingreso bajo, medio-bajo, medio-alto y alto. Cada caja resume en una sola figura la mediana, el rango intercuartílico y los extremos de la distribución de cada grupo, y sobre ella se superponen los puntos individuales, de modo que se puede ver cada país y no solo el resumen estadístico. Esta clasificación proviene de una fuente externa a los datos y se basa únicamente en el ingreso, de manera que el gráfico permite observar hasta qué punto una categorización construida con una sola variable se corresponde con el comportamiento de la esperanza de vida, uno de los indicadores centrales de la investigación.

p_ing <- base_final %>%
  filter(!is.na(GrupoIngreso)) %>%
  ggplot(aes(x = GrupoIngreso, y = EspVida, fill = GrupoIngreso,
             text = paste0("<b>", Country, "</b><br>Esperanza de vida: ", round(EspVida, 1), " años"))) +
  geom_boxplot(alpha = 0.8, outlier.shape = NA) +
  geom_jitter(width = 0.15, alpha = 0.5, size = 1.4) +
  scale_fill_manual(values = pal_ing) +
  theme_minimal() +
  theme(legend.position = "none") +
  labs(title = "Esperanza de vida según grupo de ingreso del Banco Mundial",
       x = NULL, y = "Esperanza de vida (años)")

ggplotly(p_ing, tooltip = "text")

La figura muestra un escalonamiento claro de la esperanza de vida a medida que se asciende de grupo. La mediana pasa de alrededor de 61 años en los países de ingreso bajo a cerca de 67,7 en los de ingreso medio-bajo, a unos 74 en los de ingreso medio-alto y a aproximadamente 81,5 en los de ingreso alto. Cada salto entre grupos consecutivos equivale a entre seis y siete años y medio de esperanza de vida, y las cajas, que contienen la mitad central de los países de cada grupo, se desplazan hacia arriba de forma ordenada, sin cruzarse entre sí. El grupo de ingreso bajo es además el único que se extiende de manera notable hacia valores muy reducidos, con un país cercano a los 31 años que alarga considerablemente su rango inferior, el mismo caso extremo que ya se había identificado en el diagrama de dispersión. Sin embargo, los grupos no son compartimentos cerrados. Los bigotes y los puntos de grupos contiguos se solapan de manera considerable: los países de ingreso medio-bajo llegan a valores cercanos a 53 años, equivalentes a los más bajos del grupo de ingreso bajo, y algunos de ingreso medio-alto y alto descienden hasta niveles cercanos a 62 y 61 años, comparables a los del grupo de menores ingresos. Esto significa que una parte de los países tiene una esperanza de vida más parecida a la de otro grupo que a la del propio. La dispersión también varía entre grupos.

El de ingreso bajo y el de ingreso medio-bajo presentan cajas amplias, mientras que el de ingreso alto concentra la mayoría de sus países en un tramo estrecho entre los 77 y los 83 años, aunque con una cola inferior que revela la presencia de algunas excepciones. Para la investigación, esta lectura aporta una caracterización de la esperanza de vida que acompaña la clasificación oficial de ingreso, y permite ver que dicha clasificación recoge una tendencia real pero no agota la diferencia entre países, puesto que el solapamiento entre grupos indica que existen países cuyo desempeño sanitario no coincide con su categoría económica. Se trata de un resultado de la exploración descriptiva mediante diagramas de cajas, que deja establecida la distribución de esta variable por grupo y sirve como referencia con la cual contrastar posteriormente las agrupaciones obtenidas a partir de los quince indicadores en conjunto.

  1. Mapa mundial por indicador. Use el menú desplegable para cambiar de variable (los países sin dato observado aparecen en blanco).

La tercera herramienta de exploración es un mapa coroplético interactivo que traslada los indicadores socio-sanitarios al espacio geográfico. Un menú desplegable permite cambiar entre seis variables: esperanza de vida, mortalidad de menores de cinco años, INB per cápita, acceso a electricidad, médicos por mil habitantes y población de 65 años o más. Cada país se colorea según su valor observado en 2019, y los países sin dato aparecen en blanco, por lo que ningún color proviene de valores imputados. Esta vista complementa a las dos anteriores porque deja ver si los niveles de bienestar se distribuyen al azar entre los países o siguen alguna ordenación territorial. La vista inicial del mapa es la de esperanza de vida.

vars_mapa <- c(EspVida = "Esperanza de vida (años)",
               MortMenores5 = "Mortalidad de menores de 5 años (por 1.000)",
               INB_perCapita = "INB per cápita (US$, log10)",
               AccesoElectricidad = "Acceso a electricidad (%)",
               MedicosPor1k = "Médicos por 1.000 habitantes",
               Pob65Mas = "Población de 65 años o más (%)")

geo_df <- df_filtered   # valores observados (sin imputar)

fig_mapa <- plot_geo(geo_df)
for (i in seq_along(vars_mapa)) {
  v  <- names(vars_mapa)[i]
  z  <- if (v == "INB_perCapita") log10(geo_df[[v]]) else geo_df[[v]]
  fig_mapa <- fig_mapa %>%
    add_trace(type = "choropleth", locations = geo_df$Code, z = z,
              text = paste0("<b>", geo_df$Country, "</b><br>", vars_mapa[i], ": ",
                            format(round(geo_df[[v]], 1), big.mark = ".", decimal.mark = ",", trim = TRUE)),
              hoverinfo = "text", colorscale = "Viridis", reversescale = (v %in% c("MortMenores5")),
              marker = list(line = list(color = "white", width = 0.3)),
              colorbar = list(title = vars_mapa[i], len = 0.6),
              visible = (i == 1), name = vars_mapa[i])
}

botones <- lapply(seq_along(vars_mapa), function(i) {
  list(method = "restyle", args = list("visible", as.list(seq_along(vars_mapa) == i)), label = vars_mapa[i])
})

fig_mapa %>%
  layout(title = paste0("Mapa mundial de indicadores socio-sanitarios (", ANIO, ")"),
         updatemenus = list(list(buttons = botones, x = 0.02, y = 1.08, xanchor = "left", yanchor = "top")),
         geo = list(showframe = FALSE, showcoastlines = FALSE, projection = list(type = "natural earth"),
                    showland = TRUE, landcolor = "#e5e5e5"))

El mapa de esperanza de vida muestra una distribución geográfica ordenada. Los valores más altos, cercanos a los 80 años o más, se concentran en Europa occidental, Canadá, Australia y Nueva Zelanda. Los valores intermedios cubren Estados Unidos, Sudamérica, Rusia, China y el sur de Asia, y los más bajos, entre 50 y 65 años, se agrupan en África subsahariana, con un país del centro del continente en el tono más oscuro, cercano a los 30 años. Los países con valores parecidos tienden a estar cerca unos de otros, de modo que la heterogeneidad entre países sigue una estructura territorial reconocible.

En la vista de mortalidad de menores de cinco años la escala de color está invertida, de modo que el amarillo corresponde a los valores bajos y el morado a los más altos. Por eso la mayor parte del mundo aparece en amarillo, con tasas bajas en América, Europa, Rusia, China y Oceanía, y las diferencias se concentran en África y el sur de Asia. África occidental y central muestra tonos verde azulado, que indican tasas de entre 100 y 150 muertes por cada mil nacidos vivos, mientras que el sur de Asia y el este de África se ubican en tonos verdes intermedios. Un país del centro del continente africano alcanza el extremo de la escala, por encima de las 250 muertes por mil, y coincide con el que presentaba la esperanza de vida más baja en el mapa anterior. La mortalidad de menores de cinco años reproduce así, de forma inversa, el contraste territorial ya observado en la esperanza de vida, lo que muestra que ambos indicadores describen una misma dimensión sanitaria en la muestra.

En la vista del INB per cápita, la escala está expresada en logaritmo de base diez, por lo que cada unidad equivale a multiplicar el ingreso por diez: el valor 3 corresponde a mil dólares y el 4,5 a unos treinta mil. Los tonos amarillo verdosos, correspondientes a los ingresos más altos, aparecen en Estados Unidos, Canadá, Europa occidental y septentrional, Australia y los países del Golfo. Rusia, China, México, Brasil y Argentina se ubican en tonos verdes intermedios, cercanos a los diez mil dólares, y los tonos morados más oscuros, por debajo de los mil dólares, se concentran en el centro y el este de África y en parte del sur de Asia. La distribución del ingreso reproduce el mismo ordenamiento territorial de las dos variables sanitarias, aunque en esta vista los países de ingresos medios ocupan más superficie del mapa, lo que muestra una transición más gradual entre extremos que la observada en la mortalidad de menores de cinco años.

El acceso a electricidad presenta el contraste más marcado de las seis vistas. Casi todo el mapa aparece en amarillo, con cobertura cercana al cien por ciento en América, Europa, Asia y Oceanía, de modo que en estas regiones el indicador casi no distingue entre países. La variación se concentra en África subsahariana y en algunos países aislados, como Papúa Nueva Guinea. El centro del continente africano muestra los tonos más oscuros, con coberturas inferiores al 20 por ciento, y buena parte del resto de África se ubica en tonos azules y verde azulados, entre el 40 y el 60 por ciento, con Sudáfrica como excepción en un tono verde claro, cercano al 85 por ciento. Esto indica que esta variable aporta información diferenciadora sobre todo en el extremo inferior de la muestra, y que una gran parte de los países queda en el tope de la escala.

En la vista de médicos por cada mil habitantes aparecen países en gris, que corresponden a los que no tienen dato observado, en especial en África y en algunos territorios del norte, por lo que la lectura se limita a los países con información. Entre ellos, la mayor parte de África, el sur de Asia y buena parte del sudeste asiático ocupan los tonos más oscuros, con menos de dos médicos por mil habitantes. Estados Unidos, Canadá, China y gran parte de Latinoamérica se ubican en tonos azules, con valores cercanos a dos o tres, mientras que Europa, Rusia y Australia presentan tonos verde azulados, cercanos a cuatro. Argentina se destaca en un tono verde claro dentro de Sudamérica, y un país caribeño alcanza el extremo superior de la escala, por encima de ocho médicos por mil habitantes. Esta variable ordena a los países según la disponibilidad de personal de salud, y su patrón coincide en términos generales con el de las variables anteriores, aunque con diferencias dentro de las regiones, como el contraste de Argentina con el resto de Sudamérica.

La vista de población de 65 años o más muestra un patrón que se lee en sentido distinto a los anteriores, porque indica la estructura de edad de la población y no un nivel de carencia. Los porcentajes más altos se encuentran en Japón, que alcanza el extremo superior de la escala con valores cercanos al 30 por ciento, y en Europa, con tonos verdes que corresponden a entre el 18 y el 23 por ciento. Estados Unidos, Canadá y Australia se ubican en tonos verde azulados, Rusia y China en azules, y Latinoamérica en tonos azul oscuro, con valores más bajos. África y el Medio Oriente aparecen en los tonos más oscuros, con proporciones muy reducidas de población mayor. Aun así, el ordenamiento de las regiones coincide con el de la esperanza de vida, de modo que los países con mayor longevidad son también los de mayor proporción de adultos mayores en la muestra. Al leer las seis vistas en conjunto se observa que las mismas regiones concentran los valores extremos en todos los indicadores. África subsahariana agrupa los niveles más bajos de esperanza de vida, ingreso, acceso a electricidad, disponibilidad de médicos y proporción de población mayor, y los más altos de mortalidad de menores de cinco años, mientras que Europa occidental, Norteamérica, Japón y Oceanía ocupan el extremo opuesto. Entre ambos polos se ubican regiones intermedias, como Latinoamérica, Asia y Europa oriental, cuyo orden no es idéntico en todas las variables. Esto muestra que los quince indicadores del estudio no se reparten al azar entre los países sino que adoptan una estructura territorial coherente, aunque con matices propios de cada variable. El mapa deja así documentada, mediante una herramienta exploratoria descriptiva, la distribución geográfica de la heterogeneidad socio-sanitaria, y establece una referencia visual para contrastar más adelante con el mapa de clusters que resulte de la clusterización jerárquica de Ward.

  1. Rankings. Los 10 países con mayor y menor valor en tres indicadores clave.

La cuarta herramienta de exploración son tres diagramas de barras horizontales que ordenan a los países y muestran los diez con mayor valor y los diez con menor valor en tres indicadores clave: esperanza de vida, mortalidad de menores de cinco años e INB per cápita. Los gráficos anteriores describen la tendencia general de la muestra, y estos permiten ver quiénes ocupan los extremos de cada variable y qué tan lejos están del resto. Se trata de una herramienta exploratoria descriptiva, que no agrupa ni reduce dimensiones, pero que identifica los países que definen los límites de la distribución y los que más pesarán en el análisis posterior. A continuación se interpreta cada ranking.

ranking_plot <- function(var, titulo, unidad, n = 10) {
  d <- df_filtered %>% filter(!is.na(.data[[var]])) %>% arrange(desc(.data[[var]]))
  top <- bind_rows(slice_head(d, n = n) %>% mutate(Grupo = "Mayores"),
                   slice_tail(d, n = n) %>% mutate(Grupo = "Menores")) %>%
    mutate(Valor = .data[[var]])
  ggplot(top, aes(x = reorder(Country, Valor), y = Valor, fill = Grupo,
                  text = paste0("<b>", Country, "</b><br>", titulo, ": ", round(Valor, 1), " ", unidad))) +
    geom_col(alpha = 0.9) +
    coord_flip() +
    scale_fill_manual(values = c(Mayores = "#2166AC", Menores = "#B2182B")) +
    theme_minimal() +
    theme(legend.position = "top", legend.title = element_blank()) +
    labs(title = paste0(titulo, ": 10 países con mayor y menor valor"), x = NULL, y = paste0(titulo, " (", unidad, ")"))
}

ggplotly(ranking_plot("EspVida", "Esperanza de vida", "años"), tooltip = "text")

Los diez países con mayor esperanza de vida se mueven en un rango muy estrecho, entre unos 83 años en Islandia y Corea del Sur y unos 86 en Mónaco, e incluyen a San Marino, Japón, Andorra, Suiza, España, Singapur e Italia. Casi todos son economías europeas, junto con Japón, Corea del Sur y Singapur. Los diez países con menor esperanza de vida, en cambio, se ubican entre unos 60 años en Esuatini y apenas por encima de los 31 en la República Centroafricana, y todos pertenecen a África subsahariana. La distancia entre el décimo país de cada lista supera los veinte años, lo que muestra la amplitud real del rango de esta variable en la muestra. Además, el valor de la República Centroafricana está unos veinte años por debajo del de Chad y Nigeria, los dos siguientes, de modo que no se trata de un extremo más dentro de una escala continua sino de un caso claramente separado del resto, lo que confirma lo ya observado en el diagrama de dispersión y en el mapa.

ggplotly(ranking_plot("MortMenores5", "Mortalidad de menores de 5 años", "por 1.000"), tooltip = "text")

La mortalidad de menores de cinco años presenta la asimetría más fuerte de los tres rankings. Los diez países con mayor tasa están encabezados por la República Centroafricana, con alrededor de 255 muertes por cada mil nacidos vivos, más del doble que Níger, Somalia y Nigeria, que le siguen con tasas cercanas a 120 y quienes a su vez van acompañados de Chad, Sierra Leona, Guinea, Sudán del Sur, la República Democrática del Congo y Malí, con valores de entre 90 y 115. Los diez países con menor tasa, entre los que están Islandia, Suecia, Singapur, Japón, Estonia, Noruega, Luxemburgo, Finlandia, Eslovenia y San Marino, tienen valores tan reducidos, de unas pocas muertes por mil, que sus barras apenas se distinguen del cero. Eso indica que los países de menor mortalidad de menores de cinco años son prácticamente indistinguibles entre sí, mientras que en el otro extremo la dispersión es enorme. Aquí también la República Centroafricana se separa del resto de los países, de modo que el mismo país vuelve a ocupar la posición extrema.

ggplotly(ranking_plot("INB_perCapita", "INB per cápita", "US$"), tooltip = "text")

En el INB per cápita los diez países de mayores ingresos van desde unos 56.000 dólares en Suecia hasta cerca de 84.000 en Suiza, e incluyen a Noruega, Luxemburgo, Islandia, Estados Unidos, Catar, Dinamarca, Irlanda y Singapur. Los diez de menor ingreso, entre ellos Níger, Liberia, Malaui, Somalia, Afganistán, la República Centroafricana, Mozambique, Madagascar, la República Democrática del Congo y Burundi, tienen barras casi invisibles en la misma escala, lo que indica ingresos de solo unos pocos cientos de dólares, es decir, más de cien veces menos que los países del extremo superior. Esta brecha es mucho mayor que la observada en la esperanza de vida, donde el rango entre extremos es de menos de tres veces. Esto concuerda con lo observado en el diagrama de dispersión, donde el eje del ingreso requería escala logarítmica para poder comparar a todos los países.

Al comparar los tres rankings se observa que algunos países aparecen en los extremos de varios indicadores al mismo tiempo. La República Centroafricana y Somalia están en el grupo inferior en esperanza de vida e ingreso y en el superior en mortalidad de menores de cinco años, y Nigeria, Chad, Sierra Leona, Guinea, Malí y Sudán del Sur coinciden en los de esperanza de vida y mortalidad de menores de cinco años. En el extremo opuesto, Islandia y Singapur figuran entre los diez primeros en esperanza de vida e ingreso, y también entre los de menor mortalidad de menores de cinco años, mientras que Japón y San Marino coinciden en esperanza de vida y mortalidad de menores de cinco años. La coincidencia no es total, sin embargo: Estados Unidos, Catar e Irlanda están entre los diez de mayor ingreso pero no entre los diez de mayor esperanza de vida, y países como Níger y la República Democrática del Congo aparecen entre los de mayor mortalidad de menores de cinco años y menor ingreso, pero no entre los de menor esperanza de vida. Esto muestra que las posiciones extremas se repiten entre indicadores sin superponerse por completo, y que el ranking de una variable no reproduce exactamente el de otra. Para la investigación, esta herramienta deja documentados los países que delimitan los extremos de la distribución y el tamaño de las brechas entre ellos, y se observa que el conjunto de los quince indicadores, aunque relacionado, no puede resumirse con una sola variable.

2.6 Matriz de correlaciones

Esta sección examina la dependencia lineal entre los quince indicadores tal como entran al ACP, es decir, con las variables de cola derecha transformadas según la regla de la sección 2.4. Se usa el coeficiente de correlación de Pearson, que es la matriz sobre la que opera el análisis de componentes principales, y se lo contrasta con el coeficiente de Spearman y con la escala original. Su propósito es establecer qué indicadores se mueven conjuntamente, en qué sentido y con qué intensidad antes de reducir la dimensión. La matriz ordena las variables por agrupamiento jerárquico para que las más parecidas queden contiguas; la tabla de pares precisa las asociaciones más fuertes; la comparación con Spearman muestra cuánto depende la intensidad del coeficiente de la forma de las distribuciones; y el mapa de calor interactivo permite consultar cualquier par.

cor_matrix <- cor(data_model)   

# matriz que entra al ACP 
corrplot(cor_matrix, method = "color", type = "upper", order = "hclust",
         addCoef.col = "black", number.cex = 0.62,
         col = colorRampPalette(c("#B2182B", "white", "#2166AC"))(200),
         tl.col = "black", tl.srt = 45, diag = FALSE,
         title = "Matriz de Correlación de Pearson entre Indicadores",
         mar = c(0, 0, 2, 0))

La matriz muestra dos bloques de variables que se relacionan entre sí de forma inversa. El primero reúne la incidencia de tuberculosis, la desnutrición, la fertilidad, la natalidad y las mortalidades de menores de cinco años y materna, con correlaciones positivas de entre 0,5 y 1,0. El segundo agrupa la vacunación DPT, la esperanza de vida, el acceso a la electricidad, la densidad de médicos, la población de 65 años o más, la población urbana y el INB per cápita, con correlaciones de entre 0,3 y 0,8. Entre ambos bloques los coeficientes van de −0,9 a −0,4: la esperanza de vida, por ejemplo, se asocia de forma inversa con la mortalidad de menores de cinco años (−0,92), la mortalidad materna (−0,88), la natalidad (−0,85) y la fertilidad (−0,82). Los indicadores de carencia sanitaria y demográfica se mueven en un sentido y los de longevidad, capacidad e infraestructura en el contrario, lo que anticipa que un único eje podrá resumir buena parte de la variación.

Dos indicadores quedan fuera de ese patrón. La mortalidad bruta no supera 0,5 en valor absoluto con ningún otro indicador (la mayor es con la población de 65 años o más, 0,49), y el gasto en salud como porcentaje del PIB no supera 0,4. Esto indica que ambos aportan información distinta de la del eje principal, y que la mortalidad bruta, cuyo valor depende de la estructura de edad además del nivel de salud, será la variable que defina un segundo eje.

Para precisar la intensidad de las asociaciones, la tabla siguiente lista los diez pares con mayor correlación en valor absoluto:

# Los 10 pares de variables con mayor correlación 
pares_cor <- as.data.frame(as.table(cor_matrix)) %>%
  filter(as.integer(Var1) < as.integer(Var2)) %>%
  mutate(Abs = abs(Freq)) %>%
  arrange(desc(Abs)) %>%
  slice_head(n = 10) %>%
  transmute(`Variable 1` = Var1, `Variable 2` = Var2, `Correlación (r)` = round(Freq, 3))

pares_min <- min(abs(pares_cor[["Correlación (r)"]]))
pares_pos <- sum(pares_cor[["Correlación (r)"]] > 0)
frec_vars <- sort(table(c(as.character(pares_cor[["Variable 1"]]), as.character(pares_cor[["Variable 2"]]))), decreasing = TRUE)
n_inb_pares <- sum(c(as.character(pares_cor[["Variable 1"]]), as.character(pares_cor[["Variable 2"]])) == "INB_perCapita")

pares_cor %>%
  kbl(caption = "Pares de indicadores con mayor correlación lineal") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = FALSE)
Pares de indicadores con mayor correlación lineal
Variable 1 Variable 2 Correlación (r)
Fertilidad Natalidad 0.979
EspVida MortMenores5 -0.919
MortMenores5 MortMaterna 0.916
EspVida MortMaterna -0.880
Natalidad MortMenores5 0.873
MortMenores5 INB_perCapita -0.866
EspVida Natalidad -0.853
Natalidad MortMaterna 0.830
EspVida INB_perCapita 0.828
Natalidad INB_perCapita -0.824

Los diez pares superan 0,8 en valor absoluto, 5 con signo positivo y 5 con signo negativo. Solo 6 variables aparecen en toda la tabla, y la más frecuente es la natalidad (5 pares). El INB per cápita, una vez transformado, aparece en 3 de los diez pares, de modo que el ingreso se asocia con la supervivencia y la fecundidad con una intensidad del mismo orden que la que esos indicadores guardan entre sí. Las asociaciones más fuertes se concentran, por tanto, en un núcleo reducido de indicadores de mortalidad, fecundidad, longevidad e ingreso, que es el que el primer componente tenderá a resumir.

La correlación de Spearman mide la asociación monótona sobre los rangos y no depende de la forma de las colas. Su comparación con Pearson en escala original y en la escala del modelo muestra qué parte de la intensidad observada se debe a la asimetría:

cor_spearman <- cor(data_desc, method = "spearman", use = "pairwise.complete.obs")
iu <- upper.tri(cor_matrix)
n_sp_orig   <- sum(abs(cor_spearman[iu]) > abs(cor_original[iu]))
n_sp_modelo <- sum(abs(cor_spearman[iu]) > abs(cor_matrix[iu]))

ref_inb <- c("EspVida", "MortMenores5", "MortMaterna", "Fertilidad", "AccesoElectricidad", "Desnutricion")
tibble(Indicador = ref_inb,
       `Pearson, escala original` = cor_original["INB_perCapita", ref_inb],
       `Pearson, escala del modelo` = cor_matrix["INB_perCapita", ref_inb],
       Spearman = cor_spearman["INB_perCapita", ref_inb]) %>%
  kbl(digits = 2, caption = "Correlación del INB per cápita (países con INB observado) con otros indicadores según la escala y el coeficiente") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = FALSE)
Correlación del INB per cápita (países con INB observado) con otros indicadores según la escala y el coeficiente
Indicador Pearson, escala original Pearson, escala del modelo Spearman
EspVida 0.67 0.83 0.85
MortMenores5 -0.46 -0.87 -0.86
MortMaterna -0.38 -0.81 -0.81
Fertilidad -0.51 -0.78 -0.78
AccesoElectricidad 0.40 0.71 0.82
Desnutricion -0.43 -0.67 -0.82

En escala original, el INB per cápita correlaciona con la esperanza de vida 0,67 y con la mortalidad materna −0,38 según Pearson, mientras que el coeficiente de Spearman es 0,85 y −0,81. La diferencia no refleja una relación de intensidad moderada, sino el efecto de la asimetría: unos pocos países con ingreso muy alto o mortalidad muy alta determinan el coeficiente lineal. Tras log1p, el coeficiente de Pearson pasa a 0,83 y −0,81, cercano al de Spearman. Para el acceso a la electricidad y la desnutrición, que no se transformaron, la brecha persiste (0,71 frente a 0,82, y −0,67 frente a −0,82). El coeficiente de Spearman supera en valor absoluto al de Pearson en 83 de los 105 pares en escala original y en 80 en la escala del modelo. Este contraste es la razón de preferir la matriz transformada como entrada del ACP: la lectura de intensidad moderada que sugiere la escala original es un artefacto de la forma de las distribuciones, no una propiedad de las relaciones entre los indicadores.

La versión interactiva presenta los mismos coeficientes como mapa de calor, con el mismo ordenamiento jerárquico, y muestra la matriz completa y no solo su mitad superior:

ord <- corrplot::corrMatOrder(cor_matrix, order = "hclust")
cor_ord <- cor_matrix[ord, ord]

plot_ly(x = colnames(cor_ord), y = rownames(cor_ord), z = round(cor_ord, 2),
        type = "heatmap", zmin = -1, zmax = 1,
        colors = colorRamp(c("#B2182B", "white", "#2166AC")),
        hovertemplate = "%{y} vs %{x}<br>r = %{z}<extra></extra>") %>%
  layout(title = "Correlaciones de Pearson (ordenadas por agrupamiento jerárquico)",
         yaxis = list(autorange = "reversed"),
         margin = list(l = 130, b = 130))

Al pasar el cursor sobre una celda se lee el par de variables y su coeficiente exacto, que coincide con el de la matriz estática: por ejemplo, el INB per cápita y la población urbana tienen r = 0,70. Los dos bloques descritos aparecen como regiones de un mismo color a ambos lados de la diagonal y en tono opuesto fuera de ella, mientras que las filas de la mortalidad bruta y del gasto en salud se ven pálidas, de acuerdo con su baja correlación con las demás variables.

2.7 Adecuación de los datos para el ACP (KMO y prueba de Bartlett)

Antes de aplicar el análisis de componentes principales se evalúa si los datos tienen la estructura de correlación que esa técnica requiere. Para ello se emplean dos herramientas sobre la matriz de correlaciones de la sección 2.6: la prueba de esfericidad de Bartlett y el índice de Kaiser-Meyer-Olkin (KMO), este último calculado de forma global y por variable. La prueba de Bartlett contrasta la hipótesis de que la matriz de correlaciones es una matriz identidad, es decir, que los indicadores no están correlacionados entre sí. El KMO compara las correlaciones observadas con las correlaciones parciales, que miden la relación de cada par una vez descontada la influencia de las demás; cuanto más se acerca a 1, más de la correlación se explica por factores comunes y no por pares aislados. Su aporte a la pregunta de investigación es decidir si tiene sentido resumir los indicadores con componentes principales.

n_obs <- nrow(data_model); p_var <- ncol(data_model)
R <- cor_matrix

# Prueba de esfericidad de Bartlett 
chi_bart <- -(n_obs - 1 - (2 * p_var + 5) / 6) * log(det(R))
gl_bart  <- p_var * (p_var - 1) / 2
p_bart   <- pchisq(chi_bart, gl_bart, lower.tail = FALSE)

# Índice de Kaiser-Meyer-Olkin (KMO) global y por variable
Rinv <- solve(R)
P    <- -Rinv / sqrt(outer(diag(Rinv), diag(Rinv)))   

# correlaciones parciales
diag(P) <- 0
R0   <- R; diag(R0) <- 0
kmo_global <- sum(R0^2) / (sum(R0^2) + sum(P^2))
kmo_var    <- rowSums(R0^2) / (rowSums(R0^2) + rowSums(P^2))

tibble(
  Prueba = c("KMO global", "Bartlett: chi-cuadrado", "Bartlett: grados de libertad", "Bartlett: valor p"),
  Resultado = c(round(kmo_global, 3), round(chi_bart, 1), gl_bart, ifelse(p_bart < 0.001, "< 0,001", round(p_bart, 4)))
) %>%
  kbl(caption = "Pruebas de adecuación muestral para el ACP") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Pruebas de adecuación muestral para el ACP
Prueba Resultado
KMO global 0.875
Bartlett: chi-cuadrado 3737.4
Bartlett: grados de libertad 105
Bartlett: valor p < 0,001

El KMO global es de 0,875, un valor alto que indica que las correlaciones entre los quince indicadores superan a las correlaciones parciales, de modo que la información compartida entre variables es considerable. La prueba de Bartlett arroja un chi-cuadrado de 3.737 con 105 grados de libertad, que corresponden a los 105 pares posibles de variables, y un valor p inferior a 0,001, por lo que se rechaza que la matriz sea una identidad. Ambos resultados indican que los datos son adecuados para el ACP.

El KMO por variable aplica el mismo criterio a cada indicador y permite ver si alguno comparte poca información con el resto:

tibble(Variable = names(kmo_var), KMO = round(unname(kmo_var), 3)) %>%
  arrange(desc(KMO)) %>%
  kbl(caption = "KMO por variable (valores > 0,5 indican que la variable es adecuada para el ACP)") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = FALSE)
KMO por variable (valores > 0,5 indican que la variable es adecuada para el ACP)
Variable KMO
MedicosPor1k 0.977
MortMaterna 0.966
MortMenores5 0.962
VacunacionDPT 0.955
INB_perCapita 0.947
AccesoElectricidad 0.945
Desnutricion 0.943
IncidenciaTB 0.935
PobUrbana 0.912
Natalidad 0.848
Fertilidad 0.842
GastoSaludPIB 0.829
EspVida 0.782
Pob65Mas 0.775
MortalidadBruta 0.186

14 de los quince indicadores superan el umbral de 0,5. Los valores más altos corresponden a la densidad de médicos, la mortalidad materna y la mortalidad de menores de cinco años (0,977 el mayor), que son los más redundantes con el resto. Quedan por debajo de 0,5 la mortalidad bruta (0,186), lo que indica que comparte poca información con el resto del conjunto. Se conservan en el análisis porque el KMO global sigue siendo alto y porque su aporte es sustantivo (la mortalidad bruta es la variable que define el segundo eje); el efecto de esa decisión se evalúa de forma explícita en la sección 3.15 con un escenario sin mortalidad bruta.

Reducción Dimensional y Segmentación

En esta pestaña se aplican las técnicas no supervisadas del estudio: el análisis de componentes principales, el agrupamiento jerárquico de Ward y la validación por silueta, y se contrasta la partición resultante con la clasificación del Banco Mundial.

3.1 Ejecución del ACP y proporción de varianza explicada

Se aplica el análisis de componentes principales (ACP, paquete FactoMineR) a los quince indicadores estandarizados, en la escala del modelo definida en la sección 2.4, para resumir su variación conjunta en pocas dimensiones y decidir cuántas conservar antes de agrupar. Los autovalores miden cuánta varianza captura cada componente, y su suma acumulada guía la retención. El resultado responde a la primera pregunta guía: cuántas dimensiones independientes se necesitan para describir las diferencias entre los países.

# ACP con datos estandarizados 

pca_res <- PCA(data_model, scale.unit = TRUE, ncp = ncol(data_model), graph = FALSE)

orientar <- function(p, j, v) {
  s <- sign(p$var$cor[v, j])
  p$var$coord[, j] <- s * p$var$coord[, j]; p$var$cor[, j] <- s * p$var$cor[, j]; p$ind$coord[, j] <- s * p$ind$coord[, j]
  p
}
pca_res <- orientar(pca_res, 1, "EspVida")
pca_res <- orientar(pca_res, 2, "MortalidadBruta")

# Autovalores y Porcentaje de Varianza Explicada
eig_table <- as.data.frame(get_eigenvalue(pca_res))
var_exp   <- eig_table$variance.percent    

# Número de componentes retenidos 
N_PC <- which(eig_table$cumulative.variance.percent >= UMBRAL_VARIANZA)[1]

eig_table %>%
  head(6) %>%
  rownames_to_column("Componente") %>%
  mutate(Kaiser = ifelse(eigenvalue >= 1, "Sí (autovalor ≥ 1)", "No")) %>%
  kbl(caption = "Autovalores y Proporción de Varianza Explicada por Componente Principal", digits = 2,
      col.names = c("Componente", "Autovalor", "% de varianza", "% acumulado", "Criterio de Kaiser")) %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Autovalores y Proporción de Varianza Explicada por Componente Principal
Componente Autovalor % de varianza % acumulado Criterio de Kaiser
Dim.1 9.28 61.86 61.86 Sí (autovalor ≥ 1)
Dim.2 1.57 10.50 72.35 Sí (autovalor ≥ 1)
Dim.3 0.79 5.29 77.64 No
Dim.4 0.77 5.11 82.75 No
Dim.5 0.61 4.10 86.84 No
Dim.6 0.47 3.15 89.99 No

El número de componentes no se fija a mano: N_PC se calcula como el menor número cuya varianza acumulada alcanza 80 %, un umbral convencional declarado antes de ver los resultados. Con esa regla se retienen 4 componentes: con 3 el acumulado es de 77,6 %, por debajo del umbral, y con 4 llega a 82,7 %, 2,75 puntos por encima. Solo 2 componentes tienen autovalor mayor o igual que 1 (criterio de Kaiser); los componentes tercero y siguientes se retienen por la regla de varianza y no por su autovalor (0,79 el tercero y 0,77 el último retenido), y entran a Ward con el peso que les da su varianza. Como esa retención es una convención y no un hecho de los datos, la sensibilidad de los clusters al número de componentes se mide en la sección 3.15.

El primer componente recoge 61,9 % de la varianza total, con un autovalor de 9,28, de modo que una sola dimensión resume más de la mitad de lo que varía entre los 192 países en los quince indicadores. El segundo añade 10,5 % (autovalor de 1,57) y entre los dos se alcanza 72,4 %. El tercero y el cuarto aportan 5,3 % y 5,1 %; son dimensiones de menor peso, y su contenido, como se verá en la sección 3.3, es más específico. La estructura es, por tanto, dominada por un eje principal, con un segundo eje claramente separado y dos dimensiones menores.

3.2 Gráfico de sedimentación (Scree Plot)

El gráfico de sedimentación muestra la caída de la varianza explicada componente a componente y permite contrastar el punto de inflexión (codo) con la regla del 80 % aplicada en la sección anterior.

fviz_eig(pca_res, addlabels = TRUE, ylim = c(0, ceiling(var_exp[1] / 10) * 10 + 10),
         barfill = "#B590D3", barcolor = "#B590D3",
         main = "Porcentaje de Varianza Explicada por Componente Principal (%)",
         xlab = "Componente principal", ylab = "% de varianza explicada")

La curva cae de forma abrupta entre el primer componente (autovalor de 9,28) y el segundo (1,57), desciende de manera más moderada hasta el tercero (0,79) y se aplana desde ahí, con diferencias de 0,03 entre el tercero y el cuarto y de 0,15 entre el cuarto y el quinto. El codo se ubica, por tanto, entre el segundo y el tercer componente, y el criterio de Kaiser retiene dos. La retención de 4 componentes por la regla de 80 % es más inclusiva que ambos criterios: añade 2 dimensiones de autovalor inferior a 1, cuyo aporte es modesto pero que permiten no perder estructura, y esa decisión es la que se somete a prueba en la sección 3.15.

3.3 Interpretación de los primeros componentes principales

Las cargas del ACP, que para variables estandarizadas son las correlaciones entre cada indicador y cada componente (no tienen unidades), permiten asignar un contenido a cada dimensión. Esta sección las examina con el círculo de correlación y la tabla de cargas, para responder qué representa cada componente retenido.

fviz_pca_var(pca_res, col.var = "contrib",
             gradient.cols = c("#C9B3E8", "#E58DB8", "#9B59B6"),
             repel = TRUE,
             title = paste0("Círculo de Correlaciones de las Variables (Dim 1: ", round(var_exp[1], 1),
                            "% | Dim 2: ", round(var_exp[2], 1), "%)"))

c13 <- fviz_pca_var(pca_res, axes = c(1, 3), col.var = "contrib", repel = TRUE,
                    gradient.cols = c("#C9B3E8", "#E58DB8", "#9B59B6"),
                    title = paste0("Dim 1 (", round(var_exp[1], 1), "%) vs Dim 3 (", round(var_exp[3], 1), "%)"))
c34 <- fviz_pca_var(pca_res, axes = c(3, 4), col.var = "contrib", repel = TRUE,
                    gradient.cols = c("#C9B3E8", "#E58DB8", "#9B59B6"),
                    title = paste0("Dim 3 (", round(var_exp[3], 1), "%) vs Dim 4 (", round(var_exp[4], 1), "%)"))
gridExtra::grid.arrange(c13, c34, ncol = 2)

cargas <- as.data.frame(pca_res$var$cor[, 1:N_PC])
colnames(cargas) <- paste0("Dim ", 1:N_PC, " (", round(var_exp[1:N_PC], 1), "%)")

cargas_fmt <- cargas
for (j in seq_along(cargas)) {
  v <- cargas[[j]]
  cargas_fmt[[j]] <- cell_spec(sprintf("%.2f", v),
                               color = ifelse(abs(v) >= 0.6, "white", "black"),
                               background = ifelse(abs(v) >= 0.6, ifelse(v > 0, "#2166AC", "#B2182B"), "white"),
                               bold = abs(v) >= 0.6)
}

cargas_fmt %>%
  rownames_to_column("Variable") %>%
  kbl(escape = FALSE, caption = "Correlación entre las variables originales y los componentes principales (cargas)") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = FALSE)
Correlación entre las variables originales y los componentes principales (cargas)
Variable Dim 1 (61.9%) Dim 2 (10.5%) Dim 3 (5.3%) Dim 4 (5.1%)
EspVida 0.92 -0.24 -0.08 0.04
Fertilidad -0.89 0.11 -0.03 0.16
Natalidad -0.93 0.01 0.03 0.15
MortalidadBruta 0.03 0.93 -0.06 -0.26
PobUrbana 0.66 -0.06 -0.34 0.51
MortMenores5 -0.95 0.00 0.05 -0.02
MortMaterna -0.92 0.02 0.05 0.01
IncidenciaTB -0.76 -0.06 0.10 -0.02
GastoSaludPIB 0.43 0.38 0.62 0.51
MedicosPor1k 0.82 0.29 -0.07 0.01
VacunacionDPT 0.63 -0.31 0.43 -0.26
Desnutricion -0.75 0.09 -0.22 0.15
INB_perCapita 0.91 -0.00 -0.14 0.12
Pob65Mas 0.81 0.48 -0.08 -0.12
AccesoElectricidad 0.83 -0.25 0.02 -0.10

La Dim 1 (61,9 %) tiene cargas de al menos 0,6 en valor absoluto en 13 de los quince indicadores. Las mayores corresponden a la mortalidad de menores de cinco años (0,95), la natalidad (0,93), la esperanza de vida (0,92), la mortalidad materna (0,92) y el INB per cápita (0,91). En un sentido se ubican la esperanza de vida, el INB per cápita, el acceso a la electricidad, la densidad de médicos, la población de 65 años o más, la población urbana y la vacunación DPT, y en el contrario la mortalidad de menores de cinco años, la natalidad, la mortalidad materna, la fertilidad, la incidencia de tuberculosis y la desnutrición. El componente separa, por tanto, a los países según el nivel conjunto de supervivencia, transición demográfica, ingreso y servicios básicos, por lo que se lee como un gradiente general de desarrollo socio-sanitario. Que las mortalidades, la fecundidad, la tuberculosis y la desnutrición cargan en sentido opuesto a la esperanza de vida y al ingreso es una covariación entre indicadores, y el componente no identifica cuál de ellos precede a cuál.

La Dim 2 (10,5 %) no se ordena por nivel de desarrollo: sus cargas en la esperanza de vida (0,24) y en el INB per cápita (0,00) son bajas, y está definida sobre todo por la mortalidad bruta (0,93) y, en menor medida, por la población de 65 años o más y el gasto en salud (0,48 y 0,38). Se interpreta como un eje de estructura demográfica y mortalidad general, que coloca del mismo lado a países con mortalidad bruta elevada por envejecimiento y a países con mortalidad elevada por carencias: entre los seis países que más contribuyen a este eje, Rep. Centroafricana, Mónaco y Bulgaria quedan en el lado positivo y Catar, Emiratos Árabes Unidos y Baréin en el negativo.

La Dim 3 (5,3 %) está definida sobre todo por el gasto en salud y la vacunación DPT y la Dim 4 (5,1 %) por el gasto en salud y la población urbana, con contribuciones de 49 % y 34 % de la variable principal. Son componentes de contenido específico (financiamiento sanitario, población urbana y cobertura de vacunación) y de poca varianza, por lo que no se les atribuye una lectura sustantiva propia; su inclusión en la distancia entre países es una consecuencia de la regla de retención y se evalúa en la sección 3.15.

3.4 Contribución de variables y de países a los Componentes Principales

Las contribuciones del ACP expresan qué porcentaje de cada componente aporta cada variable o cada país, y permiten saber si un eje está construido por muchos indicadores o por pocos, y si unos pocos países lo determinan. Esta sección las examina para la lectura de los componentes y para evaluar la influencia de casos individuales sobre ellos. La contribución uniforme de referencia es de 6,7 % por variable y de 0,52 % por país.

cols_dim <- c("#B590D3", "#E58DB8", "#8FC1E3", "#F2B880")

graf_var <- lapply(seq_len(N_PC), function(i) {
  fviz_contrib(pca_res, choice = "var", axes = i, fill = cols_dim[i], color = cols_dim[i],
               title = paste0("Variables: contribución a Dim ", i, " (", round(var_exp[i], 1), "%)")) +
    theme_minimal() + theme(axis.text.x = element_text(angle = 60, hjust = 1)) + labs(x = NULL)
})
do.call(gridExtra::grid.arrange, c(graf_var, ncol = 2))

En la Dim 1, 9 variables superan el aporte uniforme: la mortalidad de menores de cinco años (9,8 %), la natalidad (9,4 %), la esperanza de vida (9,2 %), la mortalidad materna (9,1 %), el INB per cápita (8,9 %), la fertilidad (8,6 %), el acceso a la electricidad (7,4 %), la densidad de médicos (7,3 %) y la población de 65 años o más (7,1 %). La Dim 2 está construida casi por una sola variable, la mortalidad bruta, con 55,5 % de su contribución. Esto confirma que el primer eje es una construcción conjunta de muchos indicadores, y el segundo, un eje casi unidimensional.

La contribución de los diez países que más aportan a cada componente es la siguiente:

graf_ind <- lapply(seq_len(N_PC), function(i) {
  fviz_contrib(pca_res, choice = "ind", axes = i, top = 10, fill = cols_dim[i], color = cols_dim[i],
               title = paste0("Países: contribución a Dim ", i, " (", round(var_exp[i], 1), "%)")) +
    theme_minimal() + theme(axis.text.x = element_text(angle = 60, hjust = 1)) + labs(x = NULL)
})
do.call(gridExtra::grid.arrange, c(graf_ind, ncol = 2))

En los países, la Dim 1 está determinada por Rep. Centroafricana (3,7 %), Chad (2,9 %), Somalia (2,8 %), RD del Congo (2,2 %) y Mónaco (1,9 %), que suman 13,5 % de la contribución del eje. La Dim 2 es la más sensible a casos individuales: Rep. Centroafricana (18,8 %), Mónaco (7,4 %), Catar (3,1 %) y Emiratos Árabes Unidos (2,7 %), y solo Rep. Centroafricana aporta 18,8 % de ese eje, 36 veces el aporte uniforme. Este resultado señala que la segunda dimensión depende en gran medida de un único país con valores extremos de mortalidad, y que la partición posterior debe evaluarse con y sin él (sección 3.15).

3.5 Los países en el plano factorial (antes de clusterizar)

El plano formado por los dos primeros componentes del ACP sitúa a cada país según sus coordenadas factoriales, antes de aplicar cualquier algoritmo de agrupamiento. Esta sección lo colorea con los grupos de ingreso del Banco Mundial para observar si el ordenamiento de los países coincide con esa clasificación externa y si hay grupos visibles a simple vista.

coords <- data.frame(Country = rownames(pca_res$ind$coord),
                     Dim1 = pca_res$ind$coord[, 1],
                     Dim2 = pca_res$ind$coord[, 2]) %>%
  left_join(select(base_final, Country, Code, GrupoIngreso), by = "Country") %>%
  mutate(GrupoIngreso = factor(replace_na(as.character(GrupoIngreso), "Sin INB reportado"),
                               levels = c("Ingreso bajo", "Ingreso medio-bajo", "Ingreso medio-alto", "Ingreso alto", "Sin INB reportado")),
         dist_origen = sqrt(Dim1^2 + Dim2^2))

# Etiquetas: los 15 países más alejados del origen + países de referencia
etiquetados <- unique(c(coords$Country[order(-coords$dist_origen)][1:15], intersect(PAISES_REF, coords$Country)))

ggplot(coords, aes(Dim1, Dim2, color = GrupoIngreso)) +
  geom_hline(yintercept = 0, linetype = "dashed", color = "grey70") +
  geom_vline(xintercept = 0, linetype = "dashed", color = "grey70") +
  geom_point(size = 2.2, alpha = 0.8) +
  geom_text_repel(data = filter(coords, Country %in% etiquetados), aes(label = Country),
                  size = 3.6, color = "black", max.overlaps = Inf, box.padding = 0.4) +
  scale_color_manual(values = pal_ing) +
  theme_minimal() +
  theme(legend.position = "bottom") +
  labs(title = "Países en el plano principal (Dim 1 vs Dim 2) según grupo de ingreso",
       x = paste0("Dim 1 (", round(var_exp[1], 1), "%)"),
       y = paste0("Dim 2 (", round(var_exp[2], 1), "%)"),
       color = "Grupo de ingreso")

A lo largo de la Dim 1 los promedios por grupo se escalonan según el nivel de ingreso: −4,88 en ingreso bajo, −2,19 en medio-bajo, 0,62 en medio-alto y 3,07 en ingreso alto. Los países de ingreso alto se reparten a ambos lados de la Dim 1, con coordenadas de −0,13 a 4,67, y los de ingreso bajo se concentran en el extremo opuesto (−8,12 a −1,44). La Dim 2, en cambio, no ordena por ingreso: sus promedios por grupo van de −0,5 a 0,8. Los cuatro países sin INB reportado (Eritrea, Mónaco, Sudán del Sur y Yemen) se ubican en el plano según el resto de sus indicadores y no según su ingreso.

Entre los países de referencia, Alemania (4,19), Japón (4,66) y Estados Unidos (3,52) quedan en el lado del mayor bienestar y lejos del origen; Colombia (1,23), Brasil (1,08) y China (1,84) se ubican algo por encima del promedio de la muestra, e India (−1,37) y Nigeria (−4,88) por debajo de él. Los rangos de grupos de ingreso adyacentes se solapan, de modo que el ingreso ordena a los países a lo largo del eje pero no los separa en bloques: el plano sugiere un gradiente continuo, y si los países forman grupos es una pregunta que responde el agrupamiento de las secciones siguientes.

3.6 Extracción de Factor Scores y Matriz de Distancias

El agrupamiento jerárquico de Ward opera sobre las coordenadas factoriales (factor scores) de los primeros 4 componentes, y no sobre los indicadores originales. Esta sección extrae esas coordenadas, calcula la distancia euclídea entre los 192 países (18.336 pares) y examina qué países resultan próximos, para comprobar que la métrica refleja la similitud de perfil. Como el primer componente concentra 75 % de la varianza retenida, domina la distancia.

La clasificación regional que se usa para contrastar la vecindad es la de siete regiones del Banco Mundial, definida de forma fija dentro de este documento para que los resultados no dependan de la versión de ningún paquete:

regiones_iso3 <- list(
  "Asia oriental y Pacífico" = c("ASM", "AUS", "BRN", "CHN", "FJI", "FSM", "GUM", "HKG", "IDN", "JPN", "KHM", "KIR", "KOR", "LAO", "MAC", "MHL", "MMR", "MNG", "MNP", "MYS", "NCL", "NRU", "NZL", "PHL", "PLW", "PNG", "PRK", "PYF", "SGP", "SLB", "THA", "TLS", "TON", "TUV", "VNM", "VUT", "WSM"),
  "Europa y Asia central" = c("ALB", "AND", "ARM", "AUT", "AZE", "BEL", "BGR", "BIH", "BLR", "CHE", "CHI", "CYP", "CZE", "DEU", "DNK", "ESP", "EST", "FIN", "FRA", "FRO", "GBR", "GEO", "GIB", "GRC", "GRL", "HRV", "HUN", "IMN", "IRL", "ISL", "ITA", "KAZ", "KGZ", "LIE", "LTU", "LUX", "LVA", "MCO", "MDA", "MKD", "MLT", "MNE", "NLD", "NOR", "POL", "PRT", "ROU", "RUS", "SMR", "SRB", "SVK", "SVN", "SWE", "TJK", "TKM", "TUR", "UKR", "UZB", "XKX"),
  "América Latina y el Caribe" = c("ABW", "ARG", "ATG", "BHS", "BLZ", "BOL", "BRA", "BRB", "CHL", "COL", "CRI", "CUB", "CUW", "CYM", "DMA", "DOM", "ECU", "GRD", "GTM", "GUY", "HND", "HTI", "JAM", "KNA", "LCA", "MAF", "MEX", "NIC", "PAN", "PER", "PRI", "PRY", "SLV", "SUR", "SXM", "TCA", "TTO", "URY", "VCT", "VEN", "VGB", "VIR"),
  "Oriente Medio y Norte de África" = c("ARE", "BHR", "DJI", "DZA", "EGY", "IRN", "IRQ", "ISR", "JOR", "KWT", "LBN", "LBY", "MAR", "OMN", "PSE", "QAT", "SAU", "SYR", "TUN", "YEM"),
  "Norteamérica" = c("BMU", "CAN", "USA"),
  "Asia meridional" = c("AFG", "BGD", "BTN", "IND", "LKA", "MDV", "NPL", "PAK"),
  "África subsahariana" = c("AGO", "BDI", "BEN", "BFA", "BWA", "CAF", "CIV", "CMR", "COD", "COG", "COM", "CPV", "ERI", "ETH", "GAB", "GHA", "GIN", "GMB", "GNB", "GNQ", "KEN", "LBR", "LSO", "MDG", "MLI", "MOZ", "MRT", "MUS", "MWI", "NAM", "NER", "NGA", "RWA", "SDN", "SEN", "SLE", "SOM", "SSD", "STP", "SWZ", "SYC", "TCD", "TGO", "TZA", "UGA", "ZAF", "ZMB", "ZWE"))
region_wb <- unlist(lapply(names(regiones_iso3), function(r) setNames(rep(r, length(regiones_iso3[[r]])), regiones_iso3[[r]])))
stopifnot(!any(duplicated(names(region_wb))), all(base_final$Code %in% names(region_wb)))
base_final$Region <- unname(region_wb[base_final$Code])
# Extracción de factores estimados 

scores_pca <- pca_res$ind$coord[, 1:N_PC]

# Matriz de Distancia Euclídea sobre los scores factoriales

dist_matrix <- dist(scores_pca, method = "euclidean")

# Algoritmo de Ward

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

La tabla siguiente muestra, para los países de referencia, los tres países más cercanos en este espacio y la distancia que los separa:

dm <- as.matrix(dist_matrix)

vecinos <- lapply(intersect(PAISES_REF, rownames(dm)), function(p) {
  d <- sort(dm[p, setdiff(colnames(dm), p)])[1:3]
  tibble(Pais = p, Vecinos = paste0(names(d), " (", sprintf("%.2f", d), ")", collapse = ", "))
}) %>% bind_rows()

vecinos %>%
  kbl(caption = "Tres países más cercanos a cada país de referencia (distancia euclídea sobre los puntajes del ACP)",
      col.names = c("País de referencia", "Países más cercanos (distancia)")) %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Tres países más cercanos a cada país de referencia (distancia euclídea sobre los puntajes del ACP)
País de referencia Países más cercanos (distancia)
Colombia Panamá (0.41), Irán (0.63), Ecuador (0.82)
Estados Unidos Francia (1.01), Canadá (1.10), Bélgica (1.14)
China Albania (0.55), Antigua y Barbuda (1.01), Trinidad y Tobago (1.05)
India Bangladés (0.81), Indonesia (0.86), Bután (0.96)
Brasil Líbano (0.63), Argentina (1.27), Surinam (1.27)
Japón Alemania (0.50), Austria (0.79), Grecia (0.83)
Nigeria Guinea (0.57), Benín (1.06), Madagascar (1.25)
Alemania Japón (0.50), Austria (0.50), Finlandia (0.75)

La distancia mediana entre pares es de 4,01. El par más próximo es Santo Tomé y Príncipe y Botsuana (0,16) y el más lejano, Emiratos Árabes Unidos y Rep. Centroafricana (15,30). Los vecinos más cercanos de Colombia son Panamá (0.41), Irán (0.63), Ecuador (0.82). La proximidad en este espacio sigue el perfil socio-sanitario y solo en parte la geografía: en 41 % de los países, el vecino más cercano pertenece a otra región del Banco Mundial. Esto indica que las distancias del ACP miden similitud de perfil y que los clusters que Ward construya sobre ellas no reproducirán necesariamente las regiones.

3.7 Determinación del Número de Clusters

El número de clusters se obtiene con una regla declarada a priori, aplicada a los cortes del árbol de Ward sobre los 4 componentes retenidos, para k entre 2 y 8. Cinco criterios de validación interna votan cada uno por su k óptimo. La silueta promedio (Rousseeuw, 1987) y el índice de Davies-Bouldin (Davies y Bouldin, 1979) miden cohesión y separación a partir de distancias entre observaciones y centroides, y se maximiza la primera y se minimiza el segundo. El índice de Calinski-Harabasz (Caliński y Harabasz, 1974), el índice KL (Krzanowski y Lai, 1988) y el criterio del codo se construyen, en cambio, sobre una misma curva, la inercia intra-cluster W(k): el primero compara la inercia entre y dentro de grupos, el segundo la variación relativa de W(k) y el tercero la distancia de la curva a la cuerda que une k = 1 con el k máximo. Por ese origen común, estos tres criterios no son independientes entre sí, y una mayoría formada por ellos no constituye evidencia independiente de la estructura.

Cada criterio vota por su k óptimo; se adopta el k con más votos y, si hay empate, el de menor rango medio de los cinco criterios (rango 1 = mejor valor), y luego el menor k. La regla, el rango de k, el umbral de 80 % para los componentes y la composición del panel son decisiones de los autores fijadas antes de ver los resultados; lo que el código deriva es el k que resulta de aplicarlas, y por eso las secciones siguientes evalúan cuánto cambia esa elección si se modifica alguna de esas decisiones.

CRITERIOS_K <- c("Silueta", "Calinski-Harabasz", "Davies-Bouldin", "KL", "Codo")

# Suma de cuadrados de las filas de una matriz respecto de su centroide
ss_total <- function(m) sum(sweep(m, 2, colMeans(m))^2)

decidir_k <- function(mejor, rangos, criterios = colnames(rangos)) {
  ks      <- as.integer(rownames(rangos))
  votos   <- table(factor(mejor[criterios], levels = ks))
  cand    <- ks[as.vector(votos) == max(votos)]
  rango_m <- rowMeans(rangos[, criterios, drop = FALSE])
  list(k = cand[order(rango_m[as.character(cand)], cand)][1],
       votos = votos, rango_medio = rango_m)
}


seleccionar_k <- function(sc, hc = hclust(dist(sc), method = "ward.D2"),
                          k_min = K_MIN, k_max = K_MAX) {
  sc <- as.matrix(sc)
  n  <- nrow(sc); p <- ncol(sc)
  d  <- dist(sc)
  ks <- k_min:k_max

  wss <- vapply(seq_len(k_max + 1), function(k) {
    cl <- if (k == 1) rep(1L, n) else cutree(hc, k)
    sum(vapply(split(seq_len(n), cl), function(i) ss_total(sc[i, , drop = FALSE]), numeric(1)))
  }, numeric(1))
  tss <- wss[1]

  tabla <- map_dfr(ks, function(k) {
    cl  <- cutree(hc, k)
    idx <- split(seq_len(n), cl)
    cen <- t(vapply(idx, function(i) colMeans(sc[i, , drop = FALSE]), numeric(p)))   # centroides
    s   <- vapply(seq_along(idx), function(g)                            # dispersión de cada grupo
             mean(sqrt(rowSums(sweep(sc[idx[[g]], , drop = FALSE], 2, cen[g, ])^2))), numeric(1))
    dc  <- as.matrix(dist(cen))
    db  <- mean(vapply(seq_len(k), function(i) max((s[i] + s[-i]) / dc[i, -i]), numeric(1)))
    dif <- function(j) (j - 1)^(2 / p) * wss[j - 1] - j^(2 / p) * wss[j]
    tibble(k = k,
           Silueta = mean(cluster::silhouette(cl, d)[, 3]),
           `Calinski-Harabasz` = ((tss - wss[k]) / (k - 1)) / (wss[k] / (n - k)),
           `Davies-Bouldin` = db,
           KL = abs(dif(k)) / abs(dif(k + 1)),
           `Inercia explicada (%)` = 100 * (1 - wss[k] / tss))
  })

  kk <- seq_len(k_max)
  x  <- (kk - 1) / (k_max - 1)
  y  <- (wss[kk] - min(wss[kk])) / (max(wss[kk]) - min(wss[kk]))
  tabla <- tabla %>%
    mutate(Codo = ((1 - x) - y)[ks]) %>%
    select(k, all_of(CRITERIOS_K), `Inercia explicada (%)`)
  
  mejor  <- c(Silueta = ks[which.max(tabla$Silueta)],
              `Calinski-Harabasz` = ks[which.max(tabla$`Calinski-Harabasz`)],
              `Davies-Bouldin` = ks[which.min(tabla$`Davies-Bouldin`)],
              KL = ks[which.max(tabla$KL)],
              Codo = ks[which.max(tabla$Codo)])
  rangos <- cbind(Silueta = rank(-tabla$Silueta),
                  `Calinski-Harabasz` = rank(-tabla$`Calinski-Harabasz`),
                  `Davies-Bouldin` = rank(tabla$`Davies-Bouldin`),
                  KL = rank(-tabla$KL),
                  Codo = rank(-tabla$Codo))
  rownames(rangos) <- ks

  c(list(tabla = tabla, mejor = mejor, rangos = rangos, wss = wss, hc = hc),
    decidir_k(mejor, rangos))
}
res_k      <- seleccionar_k(scores_pca, hc = hc_ward)
K_CLUSTERS <- res_k$k          
tabla_k    <- res_k$tabla

tabla_k %>%
  kbl(digits = 3, col.names = c("k", CRITERIOS_K, "Inercia explicada (%)"),
      caption = "Criterios de validación interna por número de clusters (Ward sobre los scores del ACP)") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE) %>%
  row_spec(which(tabla_k$k == K_CLUSTERS), bold = TRUE, background = "#F1E0F7")
Criterios de validación interna por número de clusters (Ward sobre los scores del ACP)
k Silueta Calinski-Harabasz Davies-Bouldin KL Codo Inercia explicada (%)
2 0.363 135.197 0.841 0.734 0.366 41.574
3 0.359 183.133 0.858 4.442 0.522 65.962
4 0.299 171.050 1.145 7.278 0.468 73.187
5 0.300 145.559 0.958 6.513 0.356 75.690
6 0.271 130.777 0.941 0.130 0.239 77.854
7 0.267 121.990 0.990 0.579 0.120 79.824
8 0.247 117.006 1.018 0.718 0.000 81.656
mejores <- tibble(Criterio = factor(names(res_k$mejor), levels = CRITERIOS_K),
                  k_mejor  = as.integer(res_k$mejor))

p_crit <- tabla_k %>%
  select(k, all_of(CRITERIOS_K)) %>%
  pivot_longer(-k, names_to = "Criterio", values_to = "Valor") %>%
  mutate(Criterio = factor(Criterio, levels = CRITERIOS_K)) %>%
  ggplot(aes(k, Valor)) +
  geom_line(color = "#B590D3", linewidth = 0.9) +
  geom_point(color = "#B590D3", size = 2) +
  geom_vline(data = mejores, aes(xintercept = k_mejor), linetype = "dashed", color = "#FC4E07") +
  facet_wrap(~ Criterio, scales = "free_y", ncol = 3) +
  scale_x_continuous(breaks = K_MIN:K_MAX) +
  theme_minimal() +
  labs(title = "Criterios de validación interna",
       subtitle = paste0("Línea punteada: k que recomienda cada criterio | k seleccionado por mayoría: ", K_CLUSTERS),
       x = "Número de clusters (k)", y = NULL)

p_wss <- tibble(k = seq_len(K_MAX), WSS = res_k$wss[seq_len(K_MAX)]) %>%
  ggplot(aes(k, WSS)) +
  geom_line(color = "#B590D3", linewidth = 0.9) +
  geom_point(color = "#B590D3", size = 2) +
  geom_vline(xintercept = K_CLUSTERS, linetype = "dashed", color = "#FC4E07") +
  scale_x_continuous(breaks = seq_len(K_MAX)) +
  theme_minimal() +
  labs(title = "Método del codo", subtitle = "Suma de cuadrados intra-cluster",
       x = "Número de clusters (k)", y = "WSS")

gridExtra::grid.arrange(p_crit, p_wss, ncol = 2, widths = c(2.2, 1))

# Voto de cada criterio
tibble(Criterio = names(res_k$mejor),
       `Regla` = c("máximo", "máximo", "mínimo", "máximo", "máximo"),
       `k que recomienda` = as.integer(res_k$mejor)) %>%
  kbl(caption = "Voto de cada criterio") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Voto de cada criterio
Criterio Regla k que recomienda
Silueta máximo 2
Calinski-Harabasz máximo 3
Davies-Bouldin mínimo 2
KL máximo 4
Codo máximo 3
resumen_k <- tibble(k = K_MIN:K_MAX,
                    Votos = as.integer(res_k$votos),
                    `Rango medio` = round(unname(res_k$rango_medio), 2))

resumen_k %>%
  kbl(caption = "Votos y rango medio por número de clusters") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE) %>%
  row_spec(which(resumen_k$k == K_CLUSTERS), bold = TRUE, background = "#F1E0F7")
Votos y rango medio por número de clusters
k Votos Rango medio
2 2 2.6
3 2 1.8
4 1 3.2
5 0 3.2
6 0 5.0
7 0 5.8
8 0 6.4
sens_excl <- map_dfr(CRITERIOS_K, function(cr) {
  d <- decidir_k(res_k$mejor, res_k$rangos, setdiff(CRITERIOS_K, cr))
  tibble(`Criterio excluido` = cr, `k resultante` = d$k)
})
sens_excl %>%
  kbl(caption = "k seleccionado al excluir un criterio del panel") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
k seleccionado al excluir un criterio del panel
Criterio excluido k resultante
Silueta 3
Calinski-Harabasz 2
Davies-Bouldin 3
KL 3
Codo 2

Los criterios no convergen: Silueta: k = 2, Calinski-Harabasz: k = 3, Davies-Bouldin: k = 2, KL: k = 4 y Codo: k = 3. La regla selecciona k = 3 por desempate de rango medio entre k = 2 y k = 3 (2 votos cada uno); el rango medio es de 1,80 para k = 3, frente a 2,60 para k = 2 y 3,20 para k = 4. La decisión es frágil: al excluir Calinski-Harabasz y Codo el k resultante cambia, y las siluetas de k = 2 y k = 3 son casi iguales (0,363 y 0,359), mientras que la inercia explicada pasa de 41,6 % a 66,0 %. Un diagnóstico adicional, que no es un voto, es el árbol de Ward: el tramo vertical que sostiene el corte en tres grupos (15,54) es mayor que el que sostiene el corte en dos (10,42) y que el que sostiene el corte en cuatro (7,63).

Como verificación con un panel más amplio y de familias distintas, se aplica además el paquete NbClust (Charrad et al., 2014), que reúne más de veinte índices (se excluyen gap, gamma, gplus y tau por su costo), al corte de Ward de la línea base y de la escala original:

panel_nbclust <- function(sc) {
  if (!requireNamespace("NbClust", quietly = TRUE)) return(NULL)
  grDevices::pdf(NULL)
  on.exit(grDevices::dev.off(), add = TRUE)
  nb <- NULL
  invisible(utils::capture.output(
    nb <- try(NbClust::NbClust(data = sc, distance = "euclidean", min.nc = K_MIN, max.nc = K_MAX,
                               method = "ward.D2", index = "all"), silent = TRUE)))
  if (is.null(nb) || inherits(nb, "try-error")) return(NULL)
  v <- nb$Best.nc["Number_clusters", ]
  v <- v[is.finite(v) & v >= K_MIN & v <= K_MAX]
  table(factor(v, levels = K_MIN:K_MAX))
}
sc_orig   <- PCA(data_imputed, scale.unit = TRUE, graph = FALSE, ncp = N_PC)$ind$coord[, 1:N_PC]
nb_base   <- panel_nbclust(scores_pca)
nb_orig   <- panel_nbclust(sc_orig)
describe_nb <- function(v, espec) {
  if (is.null(v)) return(paste0("no se pudo ejecutar NbClust para ", espec))
  paste0("para ", espec, ", k = ", names(v)[which.max(v)], " reúne ", max(v), " de ", sum(v), " votos")
}
txt_nb <- if (is.null(nb_base) && is.null(nb_orig)) {
  "el paquete NbClust no está "
} else {
  paste0(describe_nb(nb_base, "la línea base"), " y ", describe_nb(nb_orig, "la escala original"))
}
nb_confirma <- !is.null(nb_base) && as.integer(names(nb_base)[which.max(nb_base)]) == K_CLUSTERS 

En el panel ampliado, para la línea base, k = 3 reúne 13 de 23 votos y para la escala original, k = 3 reúne 9 de 23 votos. Esa mayoría coincide con el k de la regla de cinco criterios, lo que respalda ese k como partición de trabajo con evidencia de familias distintas, aunque no lo convierte en el número natural de grupos. En lo que sigue, k = 3 es la partición adoptada: un resumen del gradiente que permite describir perfiles, y no un resultado general de la regla.

La sensibilidad al número de componentes retenidos se resume así:

# k seleccionado por la regla al retener entre 2 y 5 componentes principales
tab_npc <- map_dfr(2:5, function(m) {
  r <- seleccionar_k(pca_res$ind$coord[, 1:m, drop = FALSE])
  tibble(`Componentes retenidos` = m,
         `Varianza acumulada (%)` = eig_table$cumulative.variance.percent[m],
         `k seleccionado` = r$k)
})
tab_npc %>%
  kbl(digits = 1, caption = "k seleccionado según el número de componentes retenidos") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
k seleccionado según el número de componentes retenidos
Componentes retenidos Varianza acumulada (%) k seleccionado
2 72.4 2
3 77.6 3
4 82.7 3
5 86.8 3

La regla selecciona k = 2 con dos componentes, 3 con tres, 3 con cuatro y 3 con cinco. El k, por tanto, depende en parte del número de componentes retenidos; la pertenencia de cada país a su cluster, que es una cuestión distinta, se evalúa en la sección 3.15.

3.8 Dendrograma

El dendrograma muestra el orden en que el algoritmo de Ward (Ward, 1963; Murtagh y Legendre, 2014) fusiona a los 192 países a partir de la matriz de distancias entre puntajes factoriales. En cada paso se une el par de grupos cuya fusión incrementa lo mínimo posible la suma de cuadrados intra-cluster, y la altura de cada fusión cuantifica ese incremento: las uniones bajas corresponden a países casi equivalentes en el espacio factorial y las altas a bloques que solo se reúnen a costa de una pérdida considerable de homogeneidad. Los clusters se numeran de menor a mayor posición media en la Dim 1 y se rotulan con etiquetas neutras (perfil bajo, intermedio y alto), que describen esa posición y no afirman un nivel de desarrollo, un constructo que quince indicadores no miden por completo.

cl_raw <- cutree(hc_ward, k = K_CLUSTERS)
orden  <- names(sort(tapply(scores_pca[names(cl_raw), 1], cl_raw, mean)))
cluster_assign <- setNames(match(as.character(cl_raw), orden), names(cl_raw))

nombres_cl <- switch(as.character(K_CLUSTERS),
                     "2" = c("Perfil bajo", "Perfil alto"),
                     "3" = c("Perfil bajo", "Perfil intermedio", "Perfil alto"),
                     paste("Perfil", seq_len(K_CLUSTERS)))
niveles_cl <- paste0("C", seq_len(K_CLUSTERS), " · ", nombres_cl)

pal_cl <- setNames(rep_len(c("#E58DB8", "#A98BD6", "#7DB9D8", "#F2B880", "#8FD0B0", "#C9B3E8"), K_CLUSTERS), niveles_cl)  

data_clustered <- data_desc %>%     
  
  mutate(Cluster = factor(cluster_assign[rownames(data_desc)], levels = seq_len(K_CLUSTERS), labels = niveles_cl))

base_final <- base_final %>%
  mutate(Cluster = data_clustered$Cluster[match(Country, rownames(data_clustered))])

table(data_clustered$Cluster)
## 
##       C1 · Perfil bajo C2 · Perfil intermedio       C3 · Perfil alto 
##                     35                    100                     57
orden_dend <- unique(cluster_assign[hc_ward$labels[hc_ward$order]])

fviz_dend(hc_ward, k = K_CLUSTERS,
          cex = 0.6,
          k_colors = unname(pal_cl[orden_dend]),
          horiz = TRUE,
          rect = FALSE,             
          lwd = 0.7,
          main = "Dendrograma Jerárquico de Países (Método de Ward)")

El corte en 3 grupos produce clusters de 35, 100, 57 países (18,2 %, 52,1 %, 29,7 % de la muestra). La última fusión ocurre a una altura de 44,5 y la penúltima a 34,1; con dos grupos los tamaños son 135, 57, y con tres, 100, 57, 35. Los cortes posteriores subdividen el bloque central o aíslan casos extremos: con cuatro grupos los tamaños son 57, 50, 50, 35 y con cinco, 57, 50, 50, 34, 1, y aparece un cluster de un solo país (Rep. Centroafricana). El árbol muestra, en suma, una estructura jerárquica con un extremo compacto y un bloque grande que se reorganiza con facilidad al cambiar el nivel de corte, lo que anticipa la fragilidad del grupo central que se documenta en la sección 3.15.

3.9 Caracterización y perfilado de los clusters obtenidos

El perfilado por centroides traduce la pertenencia de cada país a los clusters a las unidades originales de los quince indicadores, promediando cada uno dentro de cada cluster (el INB excluye a los cuatro países con valor imputado), y lo complementa con la versión estandarizada en la escala del modelo. Su aporte a la pregunta de investigación es describir qué rasgos distinguen a cada perfil y en qué indicadores la diferencia entre ellos es mayor.

n_por_cluster <- table(data_clustered$Cluster)

# Promedios de las variables originales por cada cluster

profile_table <- data_clustered %>%
  group_by(Cluster) %>%
  summarise(across(everything(), ~ mean(.x, na.rm = TRUE)), .groups = "drop") %>%
  pivot_longer(cols = -Cluster, names_to = "Variable", values_to = "Promedio") %>%
  pivot_wider(names_from = Cluster, values_from = Promedio) %>%
  mutate(Variable = factor(Variable, levels = vars)) %>%
  arrange(Variable) %>%
  left_join(select(variables_info, Variable, Unidad_Medida), by = "Variable") %>%
  select(Variable, Unidad_Medida, all_of(niveles_cl))

profile_table %>%
  kbl(caption = "Perfil Promedio por Cluster de Países Segmentado", digits = 2,
      col.names = c("Variable", "Unidad", paste0(niveles_cl, " (n = ", as.integer(n_por_cluster), ")"))) %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = FALSE)
Perfil Promedio por Cluster de Países Segmentado
Variable Unidad C1 · Perfil bajo (n = 35) C2 · Perfil intermedio (n = 100) C3 · Perfil alto (n = 57)
EspVida Años 60.58 71.95 79.96
Fertilidad Nacimientos por mujer 4.74 2.52 1.57
Natalidad Por cada 1.000 personas 35.28 19.94 10.17
MortalidadBruta Por cada 1.000 personas 9.03 6.40 9.78
PobUrbana % de la población total 39.72 56.83 76.63
MortMenores5 Por cada 1.000 nacidos vivos 80.15 23.55 5.05
MortMaterna Por cada 100.000 nacidos vivos 469.31 88.89 11.25
IncidenciaTB Por cada 100.000 personas 234.94 124.54 16.54
GastoSaludPIB % del PIB 5.42 5.75 8.56
MedicosPor1k Por cada 1.000 personas 0.26 1.66 3.89
VacunacionDPT % de niños (12-23 meses) 73.77 90.34 94.49
Desnutricion % de la población 21.75 7.94 2.64
INB_perCapita USD actuales (Método Atlas) 1252.65 8216.22 32982.32
Pob65Mas % de la población total 2.88 6.08 17.73
AccesoElectricidad % de la población 38.77 92.20 99.97

El cluster 1 (perfil bajo) reúne a 35 países (18,2 % de la muestra), el cluster 2 (perfil intermedio) a 100 (52,1 %) y el cluster 3 (perfil alto) a 57 (29,7 %), de modo que el grupo intermedio es el más numeroso y los dos extremos se reparten el resto en proporciones desiguales.

En salud y supervivencia, la esperanza de vida asciende de 60,6 años en el cluster 1 a 71,9 en el 2 y a 80,0 en el 3, una brecha de 19,4 años entre los extremos; la mortalidad de menores de cinco años desciende de 80,2 a 23,5 y a 5,0 muertes por cada 1.000 nacidos vivos, y la mortalidad materna lo hace de 469,3 a 88,9 y a 11,2 por cada 100.000, unas 16 y 42 veces más en el extremo bajo que en el alto. En los países la mortalidad de menores de cinco años covaría con la esperanza de vida (r = −0,92), pero el análisis solo muestra covariación entre indicadores y no establece que una mortalidad temprana alta produzca la menor longevidad. La incidencia de tuberculosis (234,9, 124,5 y 16,5 por 100.000) y la desnutrición (21,7, 7,9 y 2,6 %) siguen la misma gradación.

En demografía y capacidad, la fertilidad baja de 4,7 a 2,5 y a 1,6 hijos por mujer, la población de 65 años o más sube de 2,9 % a 6,1 % y a 17,7 %, la densidad de médicos de 0,26 a 1,66 y a 3,89 por 1.000 habitantes, y el acceso a la electricidad de 38,8 % a 92,2 % y a 100,0 %. El INB per cápita medio, calculado sin los países con valor imputado, es de 1.253, 8.216 y 32.982 dólares. La mortalidad bruta no es monótona (9,0, 6,4 y 9,8 por mil), porque depende de la estructura de edad además del nivel de salud: es el indicador que define la Dim 2 y no ordena a los clusters como lo hace la Dim 1.

La tabla siguiente indica qué fracción de la brecha entre el cluster 1 y el último se recorre en cada transición:

brecha <- profile_table %>%
  filter(Variable != "MortalidadBruta") %>%
  mutate(`C1 → C2` = 100 * (.data[[niveles_cl[2]]] - .data[[niveles_cl[1]]]) /
                            (.data[[niveles_cl[K_CLUSTERS]]] - .data[[niveles_cl[1]]]),
         `C2 → C3` = 100 - `C1 → C2`) %>%
  select(Variable, `C1 → C2`, `C2 → C3`)

brecha %>%
  kbl(digits = 0,
      caption = "Porcentaje de la brecha entre C1 y C3 que se recorre en cada transición (se omite la mortalidad bruta por no ser monótona)") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = FALSE)
Porcentaje de la brecha entre C1 y C3 que se recorre en cada transición (se omite la mortalidad bruta por no ser monótona)
Variable C1 → C2 C2 → C3
EspVida 59 41
Fertilidad 70 30
Natalidad 61 39
PobUrbana 46 54
MortMenores5 75 25
MortMaterna 83 17
IncidenciaTB 51 49
GastoSaludPIB 11 89
MedicosPor1k 38 62
VacunacionDPT 80 20
Desnutricion 72 28
INB_perCapita 22 78
Pob65Mas 22 78
AccesoElectricidad 87 13

La primera transición recorre más de la mitad de la brecha en 9 de los 14 indicadores; las mayores fracciones corresponden a el acceso a la electricidad, la mortalidad materna y la vacunación DPT (87 %, 83 % y 80 %). En la segunda transición predominan el gasto en salud, la población de 65 años o más y el INB per cápita (89 %, 78 % y 78 % de la brecha). Esto indica que el paso del perfil bajo al intermedio se concentra en los indicadores de supervivencia y servicios básicos, y el paso del intermedio al alto, en los de capacidad y estructura de edad, una descripción de las posiciones de los grupos que no implica una secuencia temporal.

El perfil estandarizado, en el que cada celda es el promedio de los puntajes Z de las variables en la escala del modelo, muestra las mismas diferencias en una escala común:

z_perfil <- as.data.frame(scale(data_model)) %>%
  mutate(Cluster = data_clustered$Cluster) %>%
  group_by(Cluster) %>%
  summarise(across(everything(), mean), .groups = "drop") %>%
  pivot_longer(-Cluster, names_to = "Variable", values_to = "Z")

orden_vars <- names(sort(pca_res$var$cor[, 1]))

ggplot(z_perfil, aes(x = Cluster, y = factor(Variable, levels = orden_vars), fill = Z)) +
  geom_tile(color = "white", linewidth = 0.8) +
  geom_text(aes(label = sprintf("%.2f", Z)), size = 4.2) +
  scale_fill_gradient2(low = "#B2182B", mid = "white", high = "#2166AC", midpoint = 0, name = "Z-score") +
  theme_minimal() +
  theme(panel.grid = element_blank()) +
  labs(title = "Perfil de cada cluster (promedios estandarizados)",
       subtitle = "Azul: por encima del promedio de la muestra | Rojo: por debajo",
       x = NULL, y = NULL)

El rasgo más marcado del cluster 3 es la población de 65 años o más (+1,32), seguido de la densidad de médicos (+1,07), el INB per cápita (+1,03) y la esperanza de vida (+0,96). El cluster 1 se aparta del promedio sobre todo en el acceso a la electricidad (−1,82), la esperanza de vida (−1,45) y el INB per cápita (−1,30), y el cluster 2 queda cerca del promedio de la muestra, con sus mayores desviaciones en la mortalidad bruta (−0,45), la población de 65 años o más (−0,43) y el acceso a la electricidad (+0,29). Los rasgos del cluster 1 son de carencia y los del cluster 3, de longevidad, capacidad y envejecimiento, y el cluster 2, el más cercano al promedio, no tiene un rasgo propio marcado, lo que anticipa que su delimitación sea la más sensible a la especificación.

Dos de los quince indicadores se imputaron con frecuencia: la densidad de médicos está imputada en 46 % de los países del cluster 1, 35 % del 2 y 11 % del 3, y la desnutrición en 11 %, 18 % y 11 %. Por eso la sección 3.15 repite el análisis sin esas dos variables.

3.10 Comparación de Variables Clave por Cluster (Boxplots por Grupo)

Los promedios de la tabla de perfiles ocultan la dispersión dentro de cada grupo y no indican si los clusters se traslapan; los diagramas de caja la recuperan al mostrar, para cada cluster, la mediana, el rango intercuartílico (IQR) y los valores atípicos. La pregunta que responden es en qué indicadores los grupos se distinguen con nitidez y en cuáles sus distribuciones se mezclan.

data_clustered_long <- data_clustered %>%
  rownames_to_column(var = "Country") %>%
  pivot_longer(cols = -c(Country, Cluster), names_to = "Variable", values_to = "Valor")

# Selección de 6 variables representativas 

var_clave <- c("EspVida", "INB_perCapita", "MortMenores5", "MedicosPor1k", "Pob65Mas", "AccesoElectricidad")

data_clustered_long %>%
  filter(Variable %in% var_clave) %>%
  mutate(Variable = factor(Variable, levels = var_clave)) %>%
  ggplot(aes(x = Cluster, y = Valor, fill = Cluster)) +
  geom_boxplot(alpha = 0.8, outlier.shape = 16) +
  facet_wrap(~ Variable, scales = "free_y", ncol = 3) +
  scale_fill_manual(values = pal_cl) +
  theme_minimal() +
  theme(axis.text.x = element_blank(), legend.position = "bottom") +
  labs(title = "Comparación de Variables Clave por Cluster de Países",
       subtitle = paste0("Distribución y diferencias entre los ", K_CLUSTERS, " grupos identificados"),
       x = NULL, y = "Valor de la Variable", fill = "Cluster")

data_clustered_long %>%
  mutate(Variable = factor(Variable, levels = vars)) %>%
  ggplot(aes(x = Cluster, y = Valor, fill = Cluster)) +
  geom_boxplot(alpha = 0.8, outlier.size = 1) +
  facet_wrap(~ Variable, scales = "free_y", ncol = 5) +
  scale_fill_manual(values = pal_cl) +
  theme_minimal() +
  theme(axis.text.x = element_blank(), legend.position = "bottom") +
  labs(title = "Distribución de los 15 indicadores por cluster", x = NULL, y = "Valor del indicador", fill = "Cluster")

En el panel de las seis variables clave, la esperanza de vida tiene medianas de 61,3, 71,7 y 81,5 años en los clusters 1, 2 y 3, y sus rangos intercuartílicos no se traslapan entre clusters contiguos. El INB per cápita, de distribución muy asimétrica, tiene medianas de 940, 4.645 y 27.140 dólares, con rangos que no se traslapan entre clusters contiguos; la mediana es menor que la media en el cluster 3 (32.982), señal de que unas pocas economías de ingreso muy alto elevan el promedio. La mortalidad de menores de cinco años tiene medianas de 68,6, 19,0 y 3,9 por 1.000, con rangos que se traslapan en ambas transiciones, y la población de 65 años o más medianas de 2,8, 5,6 y 18,5 %, con rangos que no se traslapan entre clusters contiguos.

El panel de los quince indicadores muestra que los tres perfiles se distinguen con mayor nitidez en los indicadores de mortalidad, fecundidad y longevidad, con menor nitidez en la mortalidad bruta, el gasto en salud y la vacunación DPT, que son los que menos cargan en la Dim 1 (0,03, 0,43 y 0,63). Esto indica que los clusters se definen sobre todo por el eje principal y que, en los indicadores que dependen de otros factores, cabe esperar más valores compartidos entre perfiles.

3.11 Visualización del mapa factorial de segmentos de países

El mapa factorial proyecta a cada país en el plano de las dos primeras dimensiones, con una envolvente convexa por cluster y un rombo en cada centroide. Su aporte es mostrar la posición relativa de los perfiles sobre los ejes ya interpretados.

# Mapa factorial (Dim 1 vs Dim 2) 

mapa_df <- data.frame(Country = rownames(scores_pca), Dim1 = scores_pca[, 1], Dim2 = scores_pca[, 2]) %>%
  mutate(Cluster = data_clustered$Cluster[match(Country, rownames(data_clustered))],
         dist_origen = sqrt(Dim1^2 + Dim2^2))

hulls <- mapa_df %>% group_by(Cluster) %>% slice(chull(Dim1, Dim2)) %>% ungroup()
centroides <- mapa_df %>% group_by(Cluster) %>% summarise(Dim1 = mean(Dim1), Dim2 = mean(Dim2), .groups = "drop")

etiquetados_cl <- unique(c(mapa_df$Country[order(-mapa_df$dist_origen)][1:15], intersect(PAISES_REF, mapa_df$Country)))

ggplot(mapa_df, aes(Dim1, Dim2, color = Cluster, fill = Cluster)) +
  geom_polygon(data = hulls, alpha = 0.12, linewidth = 0.6) +
  geom_point(size = 2.2, alpha = 0.85) +
  geom_point(data = centroides, shape = 23, size = 5, color = "black") +
  geom_text_repel(data = filter(mapa_df, Country %in% etiquetados_cl), aes(label = Country),
                  size = 3.6, color = "black", max.overlaps = Inf, box.padding = 0.4) +
  scale_color_manual(values = pal_cl) +
  scale_fill_manual(values = pal_cl) +
  theme_minimal() +
  theme(plot.title = element_text(face = "bold", size = 13, hjust = 0.5), legend.position = "bottom") +
  labs(title = "Mapa Factorial de Segmentación de Países en el Plano Principal (Dim 1 vs Dim 2)",
       x = paste0("Dim 1 (", round(var_exp[1], 1), "%)"),
       y = paste0("Dim 2 (", round(var_exp[2], 1), "%)"),
       color = "Cluster", fill = "Cluster")

Las dos primeras dimensiones recogen 72,4 % de la variabilidad total (61,9 % la Dim 1 y 10,5 % la Dim 2). Como la clusterización se calculó con 4 dimensiones, el plano es una vista parcial: dos países separados en las Dim 3 y 4 pueden aparecer próximos en él. A lo largo de la Dim 1 los clusters se ordenan con centroides en −4,7 (cluster 1), −0,3 (cluster 2) y 3,4 (cluster 3); las distancias entre centroides en el plano son de 4,7 entre los clusters 1 y 2, de 4,0 entre el 2 y el 3 y de 8,1 entre el 1 y el 3. El cluster 1 ocupa de −8,1 a −3,0 en la Dim 1, el cluster 2 de −3,8 a 3,1 y el cluster 3 de 1,2 a 5,8. La partición en 3 clusters explica 66,0 % de la inercia de los 4 componentes. La amplitud de cada cluster en el eje principal indica cuál es más heterogéneo internamente, y el solapamiento de los rangos de clusters contiguos, que sus fronteras no son nítidas.

p_int <- ggplot(mapa_df, aes(Dim1, Dim2, color = Cluster,
                             text = paste0("<b>", Country, "</b><br>", Cluster))) +
  geom_point(size = 2, alpha = 0.85) +
  scale_color_manual(values = pal_cl) +
  theme_minimal() +
  labs(title = "Mapa factorial interactivo de países",
       x = paste0("Dim 1 (", round(var_exp[1], 1), "%)"),
       y = paste0("Dim 2 (", round(var_exp[2], 1), "%)"))

ggplotly(p_int, tooltip = "text") %>% layout(legend = list(orientation = "h", y = -0.2))

El mapa interactivo permite pasar el cursor sobre cada país para ver su cluster y su posición, y localizar los casos fronterizos que la sección siguiente cuantifica.

3.12 Calidad del agrupamiento (silueta por cluster)

El perfil de silueta evalúa país por país la cohesión de la partición ya formada. Un valor cercano a +1 indica que el país está mucho más cerca de los miembros de su propio cluster que de los del cluster vecino más próximo; un valor cercano a 0, que se ubica en la frontera entre dos clusters; y un valor negativo, que en promedio está más cerca del cluster vecino que del propio. Permite verificar si los segmentos son compactos y qué países no encajan con claridad en el suyo.

sil <- silhouette(unname(cluster_assign[rownames(scores_pca)]), dist_matrix)

fviz_silhouette(sil, palette = unname(pal_cl), ggtheme = theme_minimal(), print.summary = FALSE) +
  scale_fill_manual(values = unname(pal_cl), labels = niveles_cl) +
  scale_color_manual(values = unname(pal_cl), labels = niveles_cl) +
  labs(title = "Gráfico de silueta por cluster", y = "Ancho de silueta", fill = "Cluster", color = "Cluster") +
  theme(axis.text.x = element_blank(), axis.ticks.x = element_blank())

# Silueta media por cluster
tibble(Cluster = niveles_cl,
       `N.º de países` = as.integer(n_por_cluster),
       `Silueta media` = round(tapply(sil[, 3], sil[, 1], mean), 3)) %>%
  kbl(caption = "Cohesión de cada cluster") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Cohesión de cada cluster
Cluster N.º de países Silueta media
C1 · Perfil bajo 35 0.483
C2 · Perfil intermedio 100 0.209
C3 · Perfil alto 57 0.546
# Extracción de países en la zona de frontera 
fronterizos <- tibble(Country = rownames(scores_pca), Cluster = niveles_cl[sil[, 1]], Silueta = sil[, 3]) %>%
  filter(Silueta < 0) %>% arrange(Silueta)

if (nrow(fronterizos) > 0) {
  fronterizos %>%
    kbl(digits = 3, caption = "Países con silueta negativa (Observaciones en zona de frontera entre clusters)") %>%
    kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = FALSE)
}
Países con silueta negativa (Observaciones en zona de frontera entre clusters)
Country Cluster Silueta
Ruanda C2 · Perfil intermedio -0.380
Sudán C2 · Perfil intermedio -0.270
Ucrania C2 · Perfil intermedio -0.215
Kenia C2 · Perfil intermedio -0.215
Bahamas C2 · Perfil intermedio -0.215
Albania C2 · Perfil intermedio -0.212
Namibia C2 · Perfil intermedio -0.211
Yemen C2 · Perfil intermedio -0.184
Comoras C2 · Perfil intermedio -0.131
Emiratos Árabes Unidos C2 · Perfil intermedio -0.122
Moldavia C2 · Perfil intermedio -0.118
Antigua y Barbuda C2 · Perfil intermedio -0.082
Dominica C2 · Perfil intermedio -0.072
Santa Lucía C2 · Perfil intermedio -0.067
Trinidad y Tobago C2 · Perfil intermedio -0.059
China C2 · Perfil intermedio -0.054
Gabón C2 · Perfil intermedio -0.042
Pakistán C2 · Perfil intermedio -0.037
Catar C2 · Perfil intermedio -0.026
Líbano C2 · Perfil intermedio -0.019
Eritrea C2 · Perfil intermedio -0.013
Kuwait C2 · Perfil intermedio -0.006
Esuatini C2 · Perfil intermedio -0.001

El ancho de silueta medio global es de 0,359, igual al de k = 3 en la tabla de validación (0,359). Por grupos, el cluster 1 alcanza 0,483 con 35 países, el cluster 2 0,209 con 100 y el cluster 3 0,546 con 57. Según la convención de Kaufman y Rousseeuw (1990), valores entre 0,26 y 0,50 indican una estructura débil y por debajo de 0,25 no se reconoce estructura sustancial. Superan 0,50 15 países del cluster 1, 7 del 2 y 41 del 3, y 50 países del cluster 2 tienen un ancho inferior a 0,25. Hay 23 países con silueta negativa, 0, 23 y 0 en los clusters 1, 2 y 3. Ningún cluster alcanza una estructura fuerte: los extremos son los más cohesionados y el grupo intermedio el menos, de modo que lo que muestra la silueta es un gradiente con zonas de mayor y menor densidad, no tres bloques separados por fronteras nítidas.

3.13 Patrones geográficos: mapa mundial de clusters

El mapa traslada la partición al espacio geográfico para observar si los clusters, formados únicamente con indicadores y sin información de ubicación, se ordenan en el territorio, lo que permite contrastar la segmentación con las siete regiones del Banco Mundial definidas en la sección 3.6.

colorscale_cl <- unlist(lapply(seq_len(K_CLUSTERS), function(i) {
  list(list((i - 1) / K_CLUSTERS, unname(pal_cl[i])), list(i / K_CLUSTERS, unname(pal_cl[i])))
}), recursive = FALSE)

datos_mapa <- base_final %>%
  mutate(Cluster_num = as.integer(Cluster),
         hover = paste0("<b>", Country, "</b><br>", Cluster,
                        "<br>Esperanza de vida: ", round(EspVida, 1), " años",
                        "<br>Mortalidad <5 años: ", round(MortMenores5, 1), " por 1.000"))

plot_geo(datos_mapa) %>%
  add_trace(type = "choropleth", locations = ~Code, z = ~Cluster_num, text = ~hover, hoverinfo = "text",
            zmin = 0.5, zmax = K_CLUSTERS + 0.5, colorscale = colorscale_cl,
            marker = list(line = list(color = "white", width = 0.4)),
            colorbar = list(title = "Cluster", tickvals = seq_len(K_CLUSTERS), ticktext = niveles_cl, len = 0.6)) %>%
  layout(title = "Segmentación de países por perfil socio-sanitario (Ward sobre scores del ACP)",
         geo = list(showframe = FALSE, showcoastlines = FALSE, projection = list(type = "natural earth"),
                    showland = TRUE, landcolor = "#e5e5e5"))
region_cluster <- base_final %>%
  count(Region, Cluster) %>%
  pivot_wider(names_from = Cluster, values_from = n, values_fill = 0) %>%
  mutate(Total = rowSums(across(where(is.numeric))))

region_cluster %>%
  kbl(caption = "Número de países por región del Banco Mundial y cluster") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Número de países por región del Banco Mundial y cluster
Region C1 · Perfil bajo C2 · Perfil intermedio C3 · Perfil alto Total
América Latina y el Caribe 1 26 6 33
Asia meridional 1 7 0 8
Asia oriental y Pacífico 1 22 6 29
Europa y Asia central 0 10 42 52
Norteamérica 0 0 2 2
Oriente Medio y Norte de África 0 19 1 20
África subsahariana 32 16 0 48

El cluster 1 (35 países) está formado casi por completo por África subsahariana: 32 de sus integrantes pertenecen a esa región y constituyen 32 de los 48 países subsaharianos de la muestra. Los restantes (Afganistán, Haití y Papúa Nueva Guinea) están fuera de la región, de modo que el cluster no es un simple sinónimo de ubicación geográfica sino un perfil socio-sanitario. Los 16 países subsaharianos que no están en el cluster 1 se ubican en otros clusters (entre ellos Botsuana, Cabo Verde, Comoras, Eritrea, Esuatini y Gabón), lo que muestra que la región es heterogénea.

El cluster 3 (57 países) se concentra en Europa y Asia central (42) y América Latina y el Caribe (6). El cluster 2 (100 países) es el más heterogéneo geográficamente: reúne países de 6 regiones, América Latina y el Caribe (26), Asia oriental y Pacífico (22), Oriente Medio y Norte de África (19), África subsahariana (16), Europa y Asia central (10) y Asia meridional (7). Los seis países del Golfo, con una esperanza de vida media de 81,0 años, quedan en C2 · Perfil intermedio, es decir, en un perfil que el nombre neutro describe solo como una posición en la Dim 1. Su esperanza de vida es alta, pero su población de 65 años o más es de apenas 2,4 % en promedio, de modo que su posición refleja la combinación de los quince indicadores y no solo su ingreso. La geografía condiciona los clusters, pero no los define.

3.14 Clusters frente a grupos de ingreso y listado de países

La tabla de contingencia contrasta los clusters con los cuatro grupos de ingreso del Banco Mundial, una clasificación construida con una sola variable, el INB per cápita. Esa variable es además una de las quince utilizadas en el análisis, el criterio con que se imputaron los faltantes de las demás y la referencia con la que se valida la partición, de modo que parte de la concordancia es esperable por construcción y lo informativo son las desviaciones. El contraste permite determinar qué información aporta la segmentación multivariada que el ingreso no contiene.

cruce <- base_final %>%
  filter(!is.na(GrupoIngreso)) %>%
  count(Cluster, GrupoIngreso)

cruce %>%
  pivot_wider(names_from = GrupoIngreso, values_from = n, values_fill = 0) %>%
  kbl(caption = "Número de países por cluster y grupo de ingreso del Banco Mundial") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Número de países por cluster y grupo de ingreso del Banco Mundial
Cluster Ingreso bajo Ingreso medio-bajo Ingreso medio-alto Ingreso alto
C1 · Perfil bajo 20 13 1 0
C2 · Perfil intermedio 3 33 47 15
C3 · Perfil alto 0 0 12 44
ggplot(cruce, aes(x = Cluster, y = n, fill = GrupoIngreso)) +
  geom_col(position = "fill", color = "white") +
  scale_y_continuous(labels = scales::percent) +
  scale_fill_manual(values = pal_ing) +
  theme_minimal() +
  labs(title = "Composición de cada cluster según grupo de ingreso",
       x = NULL, y = "Proporción de países", fill = "Grupo de ingreso")

La correspondencia es alta en los extremos. De los 23 países de ingreso bajo, 20 están en el cluster 1 (87,0 %) y Ruanda, Sudán y Siria está(n) en otro cluster; de los 59 de ingreso alto, 44 están en el cluster 3 (74,6 %) y 15 en el cluster 2 (Antigua y Barbuda, Bahamas, Baréin, Brunéi, Kuwait, Mauricio, Nauru, Omán, Panamá, Catar, Arabia Saudita, Seychelles, San Cristóbal y Nieves, Trinidad y Tobago y Emiratos Árabes Unidos). El cluster 3 incluye además 12 países de ingreso medio-alto. Esta concordancia no es una validación independiente: se mide de nuevo, sin las dos variables más imputadas, en la sección 3.15. Lo que aporta la segmentación es la ubicación de los países de ingreso alto que quedan en el perfil intermedio, cuyo perfil conjunto de indicadores difiere del de los demás países de ingreso alto, información que el grupo de ingreso por sí solo no contiene.

El listado completo de países por cluster es el siguiente:

base_final %>%
  group_by(Cluster) %>%
  summarise(`N.º` = n(), Países = paste(sort(Country), collapse = ", "), .groups = "drop") %>%
  kbl(caption = "Listado de países por cluster") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = TRUE) %>%
  column_spec(3, width = "75%")
Listado de países por cluster
Cluster N.º Países
C1 · Perfil bajo 35 Afganistán, Angola, Benín, Burkina Faso, Burundi, Camerún, Chad, Congo, Costa de Marfil, Etiopía, Gambia, Guinea, Guinea-Bisáu, Guinea Ecuatorial, Haití, Lesoto, Liberia, Madagascar, Malaui, Malí, Mauritania, Mozambique, Níger, Nigeria, Papúa Nueva Guinea, RD del Congo, Rep. Centroafricana, Sierra Leona, Somalia, Sudán del Sur, Tanzania, Togo, Uganda, Zambia, Zimbabue
C2 · Perfil intermedio 100 Albania, Antigua y Barbuda, Arabia Saudita, Argelia, Azerbaiyán, Bahamas, Bangladés, Baréin, Belice, Bolivia, Botsuana, Brasil, Brunéi, Bután, Cabo Verde, Camboya, Catar, China, Colombia, Comoras, Dominica, Ecuador, Egipto, El Salvador, Emiratos Árabes Unidos, Eritrea, Esuatini, Filipinas, Fiyi, Gabón, Ghana, Granada, Guatemala, Guyana, Honduras, India, Indonesia, Irak, Irán, Islas Marshall, Islas Salomón, Jamaica, Jordania, Kazajistán, Kenia, Kirguistán, Kiribati, Kuwait, Laos, Líbano, Libia, Malasia, Maldivas, Marruecos, Mauricio, México, Micronesia, Moldavia, Mongolia, Myanmar, Namibia, Nauru, Nepal, Nicaragua, Omán, Pakistán, Palestina, Panamá, Paraguay, Perú, Rep. Dominicana, Ruanda, Samoa, San Cristóbal y Nieves, San Vicente y las Granadinas, Santa Lucía, Santo Tomé y Príncipe, Senegal, Seychelles, Siria, Sri Lanka, Sudáfrica, Sudán, Surinam, Tailandia, Tayikistán, Timor Oriental, Tonga, Trinidad y Tobago, Túnez, Turkmenistán, Turquía, Tuvalu, Ucrania, Uzbekistán, Vanuatu, Venezuela, Vietnam, Yemen, Yibuti
C3 · Perfil alto 57 Alemania, Andorra, Argentina, Armenia, Australia, Austria, Barbados, Bélgica, Bielorrusia, Bosnia y Herzegovina, Bulgaria, Canadá, Chequia, Chile, Chipre, Corea del Sur, Costa Rica, Croacia, Cuba, Dinamarca, Eslovaquia, Eslovenia, España, Estados Unidos, Estonia, Finlandia, Francia, Georgia, Grecia, Hungría, Irlanda, Islandia, Israel, Italia, Japón, Letonia, Lituania, Luxemburgo, Macedonia del Norte, Malta, Mónaco, Montenegro, Noruega, Nueva Zelanda, Países Bajos, Palaos, Polonia, Portugal, Reino Unido, Rumanía, Rusia, San Marino, Serbia, Singapur, Suecia, Suiza, Uruguay

3.15 Robustez de la partición

Esta sección evalúa cuánto cambia la partición adoptada al modificar las decisiones del análisis, repitiendo el ACP, el corte de Ward y la numeración de clusters en cada escenario. La comparación con la línea base usa el índice de Rand ajustado (ARI; Hubert y Arabie, 1985), que vale 1 si las dos particiones coinciden y se acerca a 0 si su acuerdo es el esperable por azar, y el número de países que cambian de cluster. Los escenarios son la escala original, la ampliación del logaritmo a más variables, la exclusión de médicos y desnutrición (las dos más imputadas) o de la mortalidad bruta (la de menor KMO), la exclusión de la República Centroafricana y la retención de dos, tres, cinco y seis componentes.

ari <- function(a, b) {
  tab <- table(a, b); n <- sum(tab); f <- function(x) sum(choose(x, 2))
  s_ij <- f(tab); s_a <- f(rowSums(tab)); s_b <- f(colSums(tab)); esp <- s_a * s_b / choose(n, 2)
  (s_ij - esp) / ((s_a + s_b) / 2 - esp)
}
base_cl <- cluster_assign[rownames(data_model)]

asignar <- function(datos, npc = N_PC) {
  p   <- PCA(datos, scale.unit = TRUE, graph = FALSE, ncp = max(npc, 2))
  sc  <- p$ind$coord[, 1:npc, drop = FALSE]
  d   <- dist(sc)
  raw <- cutree(hclust(d, method = "ward.D2"), K_CLUSTERS)
  s1  <- sign(cor(sc[, 1], datos[["EspVida"]]))
  ord <- names(sort(tapply(s1 * sc[, 1], raw, mean)))
  list(p = p, sc = sc, d = d, npc = npc,
       cl = setNames(match(as.character(raw), ord), names(raw)))
}

escenario <- function(res, id) {
  cl  <- res$cl
  b   <- base_cl[names(cl)]
  tss <- sum(scale(res$sc, scale = FALSE)^2)
  wss <- sum(sapply(split(as.data.frame(res$sc), cl), function(x) sum(scale(x, scale = FALSE)^2)))
  tibble(id = id,
         `% Dim 1` = res$p$eig[1, 2],
         `% acumulado` = res$p$eig[res$npc, 3],
         Tamaños = paste(table(cl), collapse = " / "),
         Silueta = mean(silhouette(cl, res$d)[, 3]),
         `Inercia explicada (%)` = 100 * (1 - wss / tss),
         ARI = ari(b, cl),
         `Países que cambian` = sum(cl != b))
}

vars_log_amp  <- union(vars_log, c("Desnutricion", "MortalidadBruta"))
datos_log_amp <- data_imputed %>% mutate(across(all_of(vars_log_amp), log1p))
sin_rca       <- rownames(data_model)[rownames(data_model) != rca]

asig <- list(
  base   = asignar(data_model),
  orig   = asignar(data_imputed),
  logAmp = asignar(datos_log_amp),
  sinMD  = asignar(select(data_model, -MedicosPor1k, -Desnutricion)),
  sinMB  = asignar(select(data_model, -MortalidadBruta)),
  sinRCA = asignar(data_model[sin_rca, ]),
  npc2   = asignar(data_model, 2),
  npc3   = asignar(data_model, 3),
  npc5   = asignar(data_model, 5),
  npc6   = asignar(data_model, 6)
)

etq_esc <- c(base = "Línea base (log1p según la regla)", orig = "Escala original (sin log1p)",
             logAmp = "log1p también en desnutrición y mortalidad bruta",
             sinMD = "Sin médicos ni desnutrición", sinMB = "Sin mortalidad bruta",
             sinRCA = "Sin la República Centroafricana (191 países)",
             npc2 = "2 componentes", npc3 = "3 componentes", npc5 = "5 componentes", npc6 = "6 componentes")

tab_esc <- bind_rows(lapply(names(asig), function(nm) escenario(asig[[nm]], nm))) %>%
  mutate(Escenario = unname(etq_esc[id]), .before = 1)

tab_esc %>%
  select(-id) %>%
  kbl(digits = 3, caption = "Estabilidad de la partición frente a transformaciones, exclusiones de variables o de un caso extremo y número de componentes") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = FALSE)
Estabilidad de la partición frente a transformaciones, exclusiones de variables o de un caso extremo y número de componentes
Escenario % Dim 1 % acumulado Tamaños Silueta Inercia explicada (%) ARI Países que cambian
Línea base (log1p según la regla) 61.856 82.748 35 / 100 / 57 0.359 65.962 1.000 0
Escala original (sin log1p) 55.646 80.420 37 / 55 / 100 0.342 61.625 0.397 47
log1p también en desnutrición y mortalidad bruta 62.745 83.593 31 / 56 / 105 0.330 63.566 0.367 52
Sin médicos ni desnutrición 62.278 85.547 40 / 55 / 97 0.307 61.547 0.354 51
Sin mortalidad bruta 66.266 83.496 34 / 63 / 95 0.383 69.917 0.447 41
Sin la República Centroafricana (191 países) 61.888 82.622 36 / 99 / 56 0.365 67.053 0.882 7
2 componentes 61.856 72.354 43 / 95 / 54 0.479 77.129 0.832 11
3 componentes 61.856 77.641 38 / 101 / 53 0.408 70.475 0.857 9
5 componentes 61.856 86.843 36 / 100 / 56 0.335 62.976 0.775 14
6 componentes 61.856 89.989 35 / 63 / 94 0.320 61.606 0.466 39

La línea base reproduce la partición adoptada (tamaños 35 / 100 / 57, silueta de 0,359 e inercia explicada de 66,0 %). Al usar la escala original, el ARI es 0,40 y 47 países cambian de cluster (tamaños 37 / 55 / 100); al ampliar el logaritmo a la desnutrición y la mortalidad bruta, el ARI es 0,37 y cambian 52 (tamaños 31 / 56 / 105). La transformación logarítmica no es, por tanto, un detalle: modifica la frontera entre los perfiles intermedio y alto. Sin médicos ni desnutrición, el ARI es 0,35 (51 países cambian), y sin mortalidad bruta, 0,45 (41).

La República Centroafricana aporta 18,8 % de la Dim 2 y tiene valores extremos en mortalidad. Al excluirla, el ARI es 0,88 y 7 de los países restantes cambian (tamaños 36 / 99 / 56), de modo que en esta especificación su influencia sobre la partición es moderada; los tamaños de los clusters de la línea base son contingentes a ese caso. Respecto del número de componentes, con dos, tres, cinco y seis el ARI es 0,83, 0,86, 0,78 y 0,47, y cambian 11, 9, 14 y 39 países. La pertenencia depende de cuántos componentes se retengan, aunque el k seleccionado se mantenga (sección 3.7): las dimensiones tercera y cuarta, que la sección 3.3 describe como frágiles, entran a Ward con el peso de su varianza.

El núcleo de países que no cambian de cluster en ninguno de los escenarios alternativos es el siguiente:

estable <- setNames(rep(TRUE, length(base_cl)), names(base_cl))
for (nm in setdiff(names(asig), "base")) {
  cl <- asig[[nm]]$cl
  estable[names(cl)] <- estable[names(cl)] & (cl == base_cl[names(cl)])
}
nucleo <- tibble(Cluster = niveles_cl,
                 `N.º de países` = as.integer(table(base_cl)),
                 `Conservan su cluster en los 9 escenarios` = as.integer(tapply(estable, base_cl, sum))) %>%
  mutate(`%` = round(100 * `Conservan su cluster en los 9 escenarios` / `N.º de países`, 1))

nucleo %>%
  kbl(caption = "Núcleo estable de cada cluster frente a los nueve escenarios alternativos") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Núcleo estable de cada cluster frente a los nueve escenarios alternativos
Cluster N.º de países Conservan su cluster en los 9 escenarios %
C1 · Perfil bajo 35 30 85.7
C2 · Perfil intermedio 100 37 37.0
C3 · Perfil alto 57 51 89.5
# Transición entre la línea base y la escala original
as.data.frame.matrix(table(Base = base_cl[names(asig$orig$cl)], `Escala original` = asig$orig$cl)) %>%
  kbl(caption = "Cambios de cluster entre la línea base (filas) y la escala original (columnas)") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Cambios de cluster entre la línea base (filas) y la escala original (columnas)
1 2 3
34 1 0
3 54 43
0 0 57

Los núcleos son desiguales: conservan su cluster en todos los escenarios 30 de los 35 países del cluster 1 (86 %), 51 de los 57 del cluster 3 (90 %) y solo 37 de los 100 del cluster 2 (37 %). Los extremos tienen, entonces, un núcleo estable, y el grupo intermedio es el que absorbe los cambios de especificación: es la zona en que el gradiente se corta de forma distinta según qué variables y cuántos componentes se usen.

El k que selecciona la regla de la sección 3.7 también puede cambiar con la especificación:

k_esc <- map_dfr(names(asig), function(nm) {
  r <- seleccionar_k(asig[[nm]]$sc)
  tibble(id = nm, Escenario = unname(etq_esc[nm]), `k seleccionado` = r$k, `Votos del k elegido` = as.integer(max(r$votos)))
})
k_esc %>%
  select(-id) %>%
  kbl(caption = "k seleccionado por la regla de cinco criterios en cada escenario") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
k seleccionado por la regla de cinco criterios en cada escenario
Escenario k seleccionado Votos del k elegido
Línea base (log1p según la regla) 3 2
Escala original (sin log1p) 2 2
log1p también en desnutrición y mortalidad bruta 2 5
Sin médicos ni desnutrición 2 5
Sin mortalidad bruta 2 3
Sin la República Centroafricana (191 países) 3 2
2 componentes 2 2
3 componentes 3 2
5 componentes 3 2
6 componentes 2 3

La regla elige un k distinto de 3 en 6 de los 10 escenarios. Esto confirma que k = 3 es la partición adoptada para describir el gradiente y no un resultado que la regla reproduzca en cualquier especificación.

La concordancia con los grupos de ingreso, que es en parte circular, se mide también en el escenario sin médicos ni desnutrición:

cc_md <- conc(asig$sinMD$cl)
tibble(Escenario = c("Línea base", "Sin médicos ni desnutrición"),
       `Ingreso bajo en el cluster 1` = c(cc[["bajo_c1"]], cc_md[["bajo_c1"]]),
       `Total de ingreso bajo` = n_bajo,
       `Ingreso alto en el último cluster` = c(cc[["alto_ck"]], cc_md[["alto_ck"]]),
       `Total de ingreso alto` = n_alto) %>%
  kbl(caption = "Concordancia entre clusters y grupos de ingreso con y sin las dos variables más imputadas") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Concordancia entre clusters y grupos de ingreso con y sin las dos variables más imputadas
Escenario Ingreso bajo en el cluster 1 Total de ingreso bajo Ingreso alto en el último cluster Total de ingreso alto
Línea base 20 23 44 59
Sin médicos ni desnutrición 19 23 58 59

En la línea base, 20 de los 23 países de ingreso bajo caen en el cluster 1 y 44 de los 59 de ingreso alto en el último; sin médicos ni desnutrición, las cifras son 19 y 58. Parte de la concordancia con el Banco Mundial es, por tanto, un resultado de la construcción y del peso del ingreso en el ACP, y debe leerse como coherencia entre dos descripciones del mismo gradiente y no como confirmación independiente.

En conjunto, la partición adoptada es una descripción en 3 perfiles con núcleos estables en los extremos y un centro cuya composición depende de la especificación (transformación, variables incluidas, número de componentes y presencia de un caso extremo).

Resultados, Discusión y Cierre

En esta pestaña se evalúan los hallazgos principales del análisis, respondiendo a las preguntas guía de investigación, caracterizando los patrones observados entre países, presentando las conclusiones y la bibliografía. Todas las cifras de esta pestaña se calculan en el propio documento a partir de los objetos de las secciones anteriores.

4.1 Preguntas guía de la investigación

La primera pregunta guía indaga qué componentes explican la mayor variabilidad y qué representan. El primer componente recoge 61,9 % de la varianza (autovalor de 9,28) y funciona como un gradiente general socio-sanitario: la mortalidad de menores de cinco años (0,95), la natalidad (0,93), la esperanza de vida (0,92) y la mortalidad materna (0,92) tienen las cargas mayores, de modo que la supervivencia, la transición demográfica, el ingreso y los servicios básicos covarían entre los 192 países. El segundo componente, con 10,5 % (autovalor de 1,57), no ordena por nivel de desarrollo sino por estructura demográfica y mortalidad general, y lo define la mortalidad bruta (0,93). Los componentes tercero y cuarto aportan 5,3 % y 5,1 %, y se retienen por la regla de 80 % de varianza acumulada (82,7 % con 4 componentes) y no por su autovalor. Las cargas describen covariación entre indicadores, no relaciones causales.

La segunda pregunta, cuántos grupos existen y qué los define, se abordó con el método de Ward sobre los 4 componentes retenidos y con una regla de cinco criterios de validación interna declarada a priori. La regla seleccionó k = 3 por desempate de rango medio entre k = 2 y k = 3 (2 votos cada uno), con criterios que no convergen y tres de ellos calculados sobre la misma inercia, y su resultado cambia al excluir Calinski-Harabasz y Codo. Por eso k = 3 se presenta como la partición adoptada y no como el número natural de grupos. Los tres perfiles son el cluster 1 (perfil bajo), con 35 países (esperanza de vida de 60,6 años, mortalidad de menores de cinco años de 80,2 por 1.000 e INB medio de 1.253 dólares, sin los países con INB imputado); el cluster 2 (perfil intermedio), con 100 países (71,9 años, 23,5 por 1.000 y 8.216 dólares); y el cluster 3 (perfil alto), con 57 países (80,0 años, 5,0 por 1.000 y 32.982 dólares). Los tamaños dependen de la especificación: según el escenario, el cluster 1 tiene entre 31 y 43 países, el 2 entre 55 y 101 y el 3 entre 53 y 105, y sin la República Centroafricana los tamaños son 36 / 99 / 56.

La pregunta central, si los países forman bloques homogéneos o son demasiado heterogéneos para definir perfiles comunes, tiene una respuesta intermedia: existen perfiles reconocibles, pero se organizan a lo largo de un gradiente más continuo que discreto. Los dos extremos son los más cohesionados, con siluetas medias de 0,483 en el cluster 1 y de 0,546 en el cluster 3, valores que deben leerse con la convención de Kaufman y Rousseeuw (estructura débil entre 0,26 y 0,50 y razonable por encima de 0,50); el grupo intermedio alcanza 0,209, y 23 países, todos ellos del cluster 2, tienen silueta negativa. La estructura se describe mejor como una partición en tres perfiles, con núcleos que conservan su cluster y un centro que depende de la especificación, que como tres bloques separados.

4.2 Patrones observados entre países

La relación entre los clusters y la clasificación del Banco Mundial es estrecha en los extremos: 20 de los 23 países de ingreso bajo están en el cluster 1 y 44 de los 59 de ingreso alto en el cluster 3, aunque, como se mostró en la sección 3.15, esa concordancia depende en parte de que el ingreso es una variable de entrada, un criterio de imputación y la referencia de la comparación. Entre las desviaciones, 15 países de ingreso alto (Antigua y Barbuda, Bahamas, Baréin, Brunéi, Kuwait, Mauricio, Nauru, Omán, Panamá, Catar, Arabia Saudita, Seychelles, San Cristóbal y Nieves, Trinidad y Tobago y Emiratos Árabes Unidos) quedan en el cluster intermedio; los seis países del Golfo quedan en el cluster intermedio, con una población de 65 años o más de solo 2,4 % en promedio frente a 17,7 % en el cluster 3, una combinación de indicadores que el ingreso por sí solo no anticipa.

En la dimensión geográfica, el cluster 1 es casi por completo subsahariano (32 de sus 35 países) y el cluster 3 se concentra en Europa y Asia central y América Latina y el Caribe, mientras que el cluster 2 reúne países de 6 regiones. Estos patrones describen cómo se distribuyen los indicadores entre países en 2019 y no explican por qué.

4.3 Conclusiones

El análisis muestra que los quince indicadores socio-sanitarios de 2019 se resumen, en buena medida, en un eje principal: el primer componente recoge 61,9 % de la varianza y ordena a los 192 países según un gradiente en el que covarían la supervivencia, la fecundidad, el ingreso y los servicios básicos. El segundo eje, definido por la mortalidad bruta, es de otra naturaleza, y la estructura de edad lo condiciona.

La partición en 3 perfiles es la que se adopta para describir ese gradiente, y no un hallazgo firme sobre el número de grupos. La regla de cinco criterios no converge, tres de ellos no son independientes, y el panel ampliado y las exclusiones muestran que k es una decisión analítica. Se la lee, por tanto, como una partición de trabajo, con núcleos que conservan su cluster y un centro que depende de la especificación.

Los núcleos son los extremos: conservan su cluster en todos los escenarios 86 % del cluster 1 y 90 % del cluster 3, frente a 37 % del cluster 2. Los tamaños de los clusters varían entre escenarios, y que 192 países se repartan en 35, 100, 57 es un resultado contingente a la especificación adoptada.

La República Centroafricana es un caso influyente: tiene valores extremos en esperanza de vida, mortalidad bruta, mortalidad de menores de cinco años y mortalidad materna que no se pudieron verificar con una segunda fuente, aporta 18,8 % de la Dim 2, y su exclusión cambia la pertenencia de 7 países (ARI de 0,88). La partición reportada se obtiene con ella, y las conclusiones se leen como contingentes a ese caso.

La transformación de las variables importa. Con la regla de log1p, el ACP deja de estar dominado por las colas de unas pocas variables; en escala original, 47 países cambian de cluster (ARI de 0,40), y la intensidad de las correlaciones del ingreso con los indicadores de salud pasa de 0,67 en escala original a 0,83 en la del modelo. De igual modo, el número de componentes retenidos modifica la pertenencia (ARI de 0,83 con dos componentes y de 0,47 con seis).

La relación con el ingreso es estrecha pero no independiente: el ingreso entra al ACP, se usa para imputar y es la referencia de comparación, y la concordancia se mantiene (19 de los 23 países de ingreso bajo y 58 de los 59 de ingreso alto) cuando se excluyen las dos variables más imputadas, lo que indica que no depende solo de ellas. La información que la segmentación añade al ingreso está en las desviaciones, como los países de ingreso alto en el perfil intermedio.

Los resultados tienen límites claros: son un corte transversal de 2019, no una trayectoria; muestran covariación entre indicadores y no causas; los cuatro países sin INB reportado tienen un valor estimado; y la partición es sensible a las decisiones descritas en la sección 3.15. Una extensión natural sería repetir el análisis con otros años para comprobar si los núcleos se mantienen, algo que este estudio no hace.

4.4 Alcance de los resultados

Los clusters describen cómo se agrupan los países según quince indicadores en 2019, y la comparación con el Banco Mundial muestra en qué se parece y en qué se aparta esa agrupación de la clasificación por ingreso. El estudio no evalúa intervenciones ni estima efectos, y la partición, por su sensibilidad a la especificación, no debe usarse para asignar a un país a un perfil de forma individual.

4.5 Bibliografía

Banco Mundial. (2020, 1 de julio). Nuevas clasificaciones de países por nivel de ingreso para 2020-2021 [Entrada de blog]. World Bank Blogs.

Banco Mundial. (2019). World Development Indicators [Base de datos]. DataBank. https://databank.worldbank.org/source/world-development-indicators

Bartlett, M. S. (1954). A note on the multiplying factors for various χ² approximations. Journal of the Royal Statistical Society, Series B, 16(2), 296-298.

Caliński, T., y Harabasz, J. (1974). A dendrite method for cluster analysis. Communications in Statistics, 3(1), 1-27.

Charrad, M., Ghazzali, N., Boiteau, V., y Niknafs, A. (2014). NbClust: An R package for determining the relevant number of clusters in a data set. Journal of Statistical Software, 61(6), 1-36.

Davies, D. L., y Bouldin, D. W. (1979). A cluster separation measure. IEEE Transactions on Pattern Analysis and Machine Intelligence, PAMI-1(2), 224-227.

Hubert, L., y Arabie, P. (1985). Comparing partitions. Journal of Classification, 2(1), 193-218.

Husson, F., Lê, S., y Pagès, J. (2017). Exploratory multivariate analysis by example using R (2.ª ed.). CRC Press.

James, G., Witten, D., Hastie, T., y Tibshirani, R. (2021). An introduction to statistical learning with applications in R (2.ª ed.). Springer.

Kaiser, H. F. (1974). An index of factorial simplicity. Psychometrika, 39(1), 31-36.

Kassambara, A., y Mundt, F. (2020). factoextra: Extract and visualize the results of multivariate data analyses (R package version 1.0.7).

Kaufman, L., y Rousseeuw, P. J. (1990). Finding groups in data: An introduction to cluster analysis. Wiley.

Krzanowski, W. J., y Lai, Y. T. (1988). A criterion for determining the number of groups in a data set using sum-of-squares clustering. Biometrics, 44(1), 23-34.

Lê, S., Josse, J., y Husson, F. (2008). FactoMineR: An R package for multivariate analysis. Journal of Statistical Software, 25(1), 1-18.

Little, R. J. A., y Rubin, D. B. (2019). Statistical analysis with missing data (3.ª ed.). Wiley.

Maechler, M., Rousseeuw, P., Struyf, A., Hubert, M., y Hornik, K. (2023). cluster: Cluster analysis basics and extensions [Paquete de R].

Murtagh, F., y Legendre, P. (2014). Ward’s hierarchical agglomerative clustering method: Which algorithms implement Ward’s criterion? Journal of Classification, 31(3), 274-295.

R Core Team. (2024). R: A language and environment for statistical computing. R Foundation for Statistical Computing. https://www.R-project.org/

Rousseeuw, P. J. (1987). Silhouettes: A graphical aid to the interpretation and validation of cluster analysis. Journal of Computational and Applied Mathematics, 20, 53-65.

Satopää, V., Albrecht, J., Irwin, D., y Raghavan, B. (2011). Finding a “kneedle” in a haystack: Detecting knee points in system behavior. 31st International Conference on Distributed Computing Systems Workshops, 166-171.

Sheather, S. J., y Jones, M. C. (1991). A reliable data-based bandwidth selection method for kernel density estimation. Journal of the Royal Statistical Society, Series B, 53(3), 683-690.

Sievert, C. (2020). Interactive web-based data visualization with R, plotly, and shiny. CRC Press.

Ward, J. H. (1963). Hierarchical grouping to optimize an objective function. Journal of the American Statistical Association, 58(301), 236-244.

Wei, T., y Simko, V. (2021). R package ‘corrplot’: Visualization of a correlation matrix [Paquete de R].

Wickham, H. (2016). ggplot2: Elegant graphics for data analysis. Springer.

Wickham, H., Averick, M., Bryan, J., Chang, W., McGowan, L. D., François, R., … Yutani, H. (2019). Welcome to the tidyverse. Journal of Open Source Software, 4(43), 1686.

Xie, Y. (2015). Dynamic documents with R and knitr (2.ª ed.). Chapman and Hall/CRC.

Zhu, H. (2021). kableExtra: Construct complex table with ‘kable’ and pipe syntax [Paquete de R].