# Instalar (solo la primera vez) y cargar paquetes
paquetes <- c("readxl", "tidyverse", "FactoMineR", "factoextra", "missMDA",
"corrplot", "plotly", "knitr", "kableExtra", "DT", "naniar",
"countrycode", "gridExtra", "cluster")
nuevos <- paquetes[!paquetes %in% installed.packages()[, "Package"]]
if (length(nuevos) > 0) install.packages(nuevos)
invisible(lapply(paquetes, library, character.only = TRUE))Las condiciones de vida difieren de forma notable entre países. En 2022, la esperanza de vida al nacer era de unos 84 años en Japón y de 54,5 en Chad, y el PIB per cápita de Luxemburgo, medido en paridad de poder adquisitivo, superaba los 140.000 dólares, mientras que el de Burundi se acercaba a 1.100. Sin embargo, el ingreso por sí solo describe de manera incompleta la situación de un país. Economías con niveles de ingreso similares pueden presentar estructuras demográficas, mercados laborales y coberturas de servicios básicos muy diferentes.
Cuando se quiere comparar a los países a partir de muchos indicadores a la vez aparece otra dificultad, pues buena parte de ellos están correlacionados y aportan información redundante. Las técnicas de aprendizaje no supervisado permiten abordar este problema, ya que identifican la estructura presente en los datos sin partir de una clasificación previa (James et al., 2021).
El presente informe tiene como propósito segmentar a los países del mundo según su nivel y tipo de desarrollo a partir de 18 indicadores de la base World Development Indicators del Banco Mundial correspondientes a 2022. Para ello se aplica, en primer lugar, un Análisis de Componentes Principales (ACP) que reduce el conjunto de variables a un número menor de factores no correlacionados. Sobre esos factores se realiza después una clasificación jerárquica con el método de Ward. El análisis busca establecer cuáles son las dimensiones en las que más se diferencian los países, cuántos grupos homogéneos pueden distinguirse y qué patrones regionales o de desarrollo se observan en ellos.
La información proviene de la base World Development Indicators del Banco Mundial, consultada en el portal DataBank (actualización del 8 de octubre de 2026). La consulta se restringió a economías individuales, 217 en total, de modo que quedaron excluidos los agregados regionales, los grupos por nivel de ingreso y los bloques económicos. Algunas de esas economías son territorios dependientes y no países soberanos; como se explica más adelante, casi todos ellos salen de la muestra por falta de información, y la unidad de análisis final corresponde a países.
Se eligió el año 2022 por dos motivos. Por un lado, es posterior a los años de mayor impacto de la pandemia de COVID-19; en 2020 y 2021 el crecimiento, el empleo, el comercio y los precios sufrieron alteraciones que habrían llevado a agrupar a los países por su situación coyuntural más que por sus características estructurales. Por otro, 2022 es el año reciente con mejor cobertura, dado que para 2023 y 2024 todavía falta un número considerable de registros.
Se seleccionaron 18 variables cuantitativas que abarcan distintos aspectos del desarrollo. El propósito de esta selección fue evitar que la segmentación quedara determinada únicamente por el ingreso e incorporar también la estructura productiva, la situación demográfica, el acceso a servicios y la ocupación del territorio.
indicadores <- c(
# --- Dimensión económica ---
pib_pc = "NY.GDP.PCAP.PP.CD", # PIB per cápita, PPA (US$ internacionales corrientes)
inflacion = "FP.CPI.TOTL.ZG", # Inflación, precios al consumidor (% anual)
comercio = "NE.TRD.GNFS.ZS", # Comercio (% del PIB)
agricultura = "NV.AGR.TOTL.ZS", # Agricultura, silvicultura y pesca, valor agregado (% del PIB)
desempleo = "SL.UEM.TOTL.ZS", # Desempleo (% fuerza laboral, estimación OIT)
part_lab_fem = "SL.TLF.ACTI.FE.ZS", # Participación laboral femenina (% mujeres 15-64)
# --- Dimensión demográfica ---
esp_vida = "SP.DYN.LE00.IN", # Esperanza de vida al nacer (años)
mort_menor5 = "SH.DYN.MORT", # Mortalidad menores de 5 años (por 1.000 nacidos vivos)
fecundidad = "SP.DYN.TFRT.IN", # Tasa de fecundidad (hijos por mujer)
fecund_adol = "SP.ADO.TFRT", # Fecundidad adolescente (por 1.000 mujeres 15-19)
crec_pob = "SP.POP.GROW", # Crecimiento de la población (% anual)
pob_65 = "SP.POP.65UP.TO.ZS", # Población de 65 años o más (% del total)
# --- Dimensión salud y servicios básicos ---
gasto_salud = "SH.XPD.CHEX.GD.ZS", # Gasto corriente en salud (% del PIB)
agua = "SH.H2O.BASW.ZS", # Acceso a agua potable básica (% población)
electricidad = "EG.ELC.ACCS.ZS", # Acceso a electricidad (% población)
internet = "IT.NET.USER.ZS", # Usuarios de internet (% población)
# --- Dimensión territorio y medio ambiente ---
pob_urbana = "SP.URB.TOTL.IN.ZS", # Población urbana (% del total)
bosques = "AG.LND.FRST.ZS" # Área forestal (% del territorio)
)
tabla_vars <- tibble(
Dimensión = rep(c("Económica", "Demográfica", "Salud y servicios básicos",
"Territorio y medio ambiente"), c(6, 6, 4, 2)),
Variable = names(indicadores),
Código = unname(indicadores)
)
kable(tabla_vars, caption = "Variables seleccionadas por dimensión") |>
kable_styling(bootstrap_options = c("striped", "hover", "condensed"), full_width = FALSE)| Dimensión | Variable | Código |
|---|---|---|
| Económica | pib_pc | NY.GDP.PCAP.PP.CD |
| Económica | inflacion | FP.CPI.TOTL.ZG |
| Económica | comercio | NE.TRD.GNFS.ZS |
| Económica | agricultura | NV.AGR.TOTL.ZS |
| Económica | desempleo | SL.UEM.TOTL.ZS |
| Económica | part_lab_fem | SL.TLF.ACTI.FE.ZS |
| Demográfica | esp_vida | SP.DYN.LE00.IN |
| Demográfica | mort_menor5 | SH.DYN.MORT |
| Demográfica | fecundidad | SP.DYN.TFRT.IN |
| Demográfica | fecund_adol | SP.ADO.TFRT |
| Demográfica | crec_pob | SP.POP.GROW |
| Demográfica | pob_65 | SP.POP.65UP.TO.ZS |
| Salud y servicios básicos | gasto_salud | SH.XPD.CHEX.GD.ZS |
| Salud y servicios básicos | agua | SH.H2O.BASW.ZS |
| Salud y servicios básicos | electricidad | EG.ELC.ACCS.ZS |
| Salud y servicios básicos | internet | IT.NET.USER.ZS |
| Territorio y medio ambiente | pob_urbana | SP.URB.TOTL.IN.ZS |
| Territorio y medio ambiente | bosques | AG.LND.FRST.ZS |
Dentro del grupo económico, el PIB per cápita en paridad de poder adquisitivo se utiliza como medida comparable del nivel de ingreso. La inflación y el desempleo reflejan la estabilidad macroeconómica, el comercio expresa el grado de apertura y el peso de la agricultura en el PIB permite distinguir las economías basadas en el sector primario de aquellas orientadas a la industria y los servicios. A estas se sumó la tasa de participación laboral femenina.
Las variables demográficas (esperanza de vida, mortalidad de menores de cinco años, fecundidad total y adolescente, crecimiento de la población y proporción de personas mayores de 65 años) sitúan a cada país en una etapa de la transición demográfica. El gasto en salud y el acceso a agua potable, electricidad e internet se incluyeron como medidas de cobertura de servicios básicos y de conectividad. Finalmente, la proporción de población urbana y el área forestal describen la forma en que se ocupa el territorio.
DataBank entrega los datos en formato largo, con una fila por cada
combinación de país e indicador. Los valores faltantes aparecen
codificados como ".." y el archivo termina con algunas
filas de notas. Para el análisis la tabla se reorganizó en formato
ancho, con una fila por país y una columna por variable.
# El archivo .xlsx debe estar en la misma carpeta que este .Rmd
archivo <- list.files(pattern = "^P_Data_Extract.*\\.xlsx$")[1]
if (is.na(archivo)) stop("No se encontró el Excel del Banco Mundial en la carpeta del .Rmd")
datos_largo <- read_excel(archivo, sheet = "Data", na = c("..", "")) |>
filter(!is.na(`Country Code`)) |> # quita las filas de notas
select(pais = `Country Name`, iso3c = `Country Code`,
codigo = `Series Code`, valor = `2022 [YR2022]`) |>
mutate(valor = as.numeric(valor))
# Renombrar códigos WDI con nombres cortos y pasar a formato ancho
datos <- datos_largo |>
mutate(variable = names(indicadores)[match(codigo, indicadores)]) |>
select(-codigo) |>
pivot_wider(names_from = variable, values_from = valor) |>
select(pais, iso3c, all_of(names(indicadores)))
# Continente de cada país (servirá para interpretar los clusters)
datos$continente <- countrycode(datos$iso3c, "iso3c", "continent",
custom_match = c(XKX = "Europe", CHI = "Europe"))
cat("Número de países:", nrow(datos), "\n")## Número de países: 217
## Número de variables: 18
Puesto que el ACP requiere una matriz completa, se revisó la proporción de valores faltantes por variable y por país.
faltantes <- datos |>
summarise(across(all_of(names(indicadores)), ~ mean(is.na(.)) * 100)) |>
pivot_longer(everything(), names_to = "variable", values_to = "pct_na") |>
arrange(desc(pct_na))
kable(faltantes, digits = 1, col.names = c("Variable", "% faltante"),
caption = "Porcentaje de países sin dato por variable (2022)") |>
kable_styling(bootstrap_options = c("striped", "condensed"), full_width = FALSE)| Variable | % faltante |
|---|---|
| inflacion | 17.5 |
| internet | 16.1 |
| comercio | 15.7 |
| desempleo | 14.3 |
| part_lab_fem | 14.3 |
| gasto_salud | 11.5 |
| agricultura | 9.7 |
| mort_menor5 | 9.7 |
| pib_pc | 8.3 |
| agua | 6.5 |
| bosques | 1.8 |
| electricidad | 0.9 |
| crec_pob | 0.5 |
| esp_vida | 0.0 |
| fecundidad | 0.0 |
| fecund_adol | 0.0 |
| pob_65 | 0.0 |
| pob_urbana | 0.0 |
gg_miss_var(datos |> select(all_of(names(indicadores))), show_pct = TRUE) +
labs(title = "Datos faltantes por variable – 2022", y = "% de países sin dato")datos <- datos |>
mutate(n_na = rowSums(is.na(across(all_of(names(indicadores))))))
table(datos$n_na, dnn = "Número de variables faltantes por país")## Número de variables faltantes por país
## 0 1 2 3 4 5 6 7 8 9
## 141 24 11 9 6 2 12 4 4 4
## Países con información completa: 141
Ninguna variable supera el 18 % de valores faltantes, por lo que no fue necesario descartar indicadores. Los vacíos se concentran en un grupo de territorios dependientes y microestados (Gibraltar, Isla de Man, Islas Caimán, Aruba y Groenlandia, entre otros) para los cuales el Banco Mundial no publica buena parte de las series.
Trabajar únicamente con los casos completos habría supuesto perder
países relevantes a los que les falta un solo dato, como Estados Unidos
(agricultura), Argentina (agua potable), Nigeria (comercio) o Pakistán
(internet). Se optó, por tanto, por excluir los países con más de dos
variables faltantes e imputar los valores restantes mediante el
algoritmo iterativo de ACP implementado en imputePCA (Josse
y Husson, 2016). A diferencia de la imputación por la media, este
procedimiento estima cada valor a partir de la estructura de correlación
del conjunto de variables, lo que altera en menor medida las relaciones
que se analizan después.
max_na <- 2 # máximo de variables faltantes permitidas por país (0 = solo casos completos)
datos_f <- datos |> filter(n_na <= max_na)
cat("Países excluidos:", nrow(datos) - nrow(datos_f), "\n")## Países excluidos: 41
## Países en el análisis: 176
## Valores imputados: 46
# Matriz numérica con los nombres de los países como filas
X <- datos_f |>
select(all_of(names(indicadores))) |>
as.data.frame()
rownames(X) <- datos_f$pais
# Imputación por ACP (sobre variables estandarizadas)
if (anyNA(X)) {
ncp_opt <- estim_ncpPCA(X, ncp.max = 5, scale = TRUE)$ncp
X <- imputePCA(X, ncp = ncp_opt, scale = TRUE)$completeObs |> as.data.frame()
}
stopifnot(!anyNA(X))datos |>
filter(n_na > max_na) |>
select(País = pais, `Variables faltantes` = n_na) |>
arrange(desc(`Variables faltantes`)) |>
kable(caption = "Países excluidos por exceso de datos faltantes") |>
kable_styling(bootstrap_options = c("striped", "condensed"), full_width = FALSE) |>
scroll_box(height = "300px")| País | Variables faltantes |
|---|---|
| American Samoa | 9 |
| Gibraltar | 9 |
| Sint Maarten (Dutch part) | 9 |
| St. Martin (French part) | 9 |
| British Virgin Islands | 8 |
| Channel Islands | 8 |
| Isle of Man | 8 |
| Northern Mariana Islands | 8 |
| Aruba | 7 |
| Cayman Islands | 7 |
| Curacao | 7 |
| Kosovo | 7 |
| Bermuda | 6 |
| Eritrea | 6 |
| Faroe Islands | 6 |
| French Polynesia | 6 |
| Greenland | 6 |
| Guam | 6 |
| Korea, Dem. People’s Rep. | 6 |
| Liechtenstein | 6 |
| Monaco | 6 |
| New Caledonia | 6 |
| Turks and Caicos Islands | 6 |
| Tuvalu | 6 |
| Virgin Islands (U.S.) | 5 |
| Yemen, Rep. | 5 |
| Andorra | 4 |
| Grenada | 4 |
| Macao SAR, China | 4 |
| Naoero | 4 |
| South Sudan | 4 |
| St. Kitts and Nevis | 4 |
| Dominica | 3 |
| Hong Kong SAR, China | 3 |
| Marshall Islands | 3 |
| Micronesia, Fed. Sts. | 3 |
| Myanmar | 3 |
| Puerto Rico (US) | 3 |
| Seychelles | 3 |
| Ukraine | 3 |
| Venezuela, RB | 3 |
Sea \(\mathbf{Z}\) la matriz de \(n\) países y \(p = 18\) variables estandarizadas. El ACP construye nuevas variables, denominadas componentes principales, como combinaciones lineales de las originales:
\[F_k = a_{1k} Z_1 + a_{2k} Z_2 + \dots + a_{pk} Z_p\]
Los coeficientes \(\mathbf{a}_k\) corresponden a los vectores propios de la matriz de correlaciones \(\mathbf{R}\), y la varianza de cada componente es el valor propio asociado, \(\lambda_k\). Los componentes son incorrelacionados entre sí y se ordenan de mayor a menor varianza. Como \(\sum_k \lambda_k = p\), la proporción de varianza que recoge el componente \(k\) es \(\lambda_k / p\) (Jolliffe y Cadima, 2016). La interpretación de cada componente se apoya en las cargas, es decir, en las correlaciones entre las variables y el componente, y en la contribución de cada variable a su construcción (Husson et al., 2017).
La clasificación se realiza sobre las coordenadas de los países en los componentes retenidos, mediante un algoritmo jerárquico aglomerativo. En cada etapa se fusionan los dos grupos \(A\) y \(B\) cuya unión produce el menor incremento de la inercia intragrupo (Ward, 1963):
\[\Delta(A, B) = \frac{n_A \, n_B}{n_A + n_B} \, \lVert \bar{\mathbf{x}}_A - \bar{\mathbf{x}}_B \rVert^2\]
donde \(n_A\) y \(n_B\) son los tamaños de los grupos y \(\bar{\mathbf{x}}_A\) y \(\bar{\mathbf{x}}_B\) sus centroides. El procedimiento genera un dendrograma que se corta en el número de grupos que se considere más adecuado.
El análisis se llevó a cabo en R, con los paquetes
FactoMineR (Lê et al., 2008), factoextra
(Kassambara y Mundt, 2020), missMDA, tidyverse
(Wickham et al., 2019) y plotly (Sievert, 2020).
# Paleta y tema comunes para todos los gráficos
col_principal <- "#2a78d6"
col_continente <- c(Africa = "#2a78d6", Americas = "#eb6834", Asia = "#1baf7a",
Europe = "#eda100", Oceania = "#e87ba4")
theme_set(theme_minimal(base_size = 11) +
theme(panel.grid.minor = element_blank(),
plot.title = element_text(face = "bold")))
# Información de apoyo alineada con las filas de X
info <- datos_f |> select(pais, iso3c, continente)asimetria <- function(x) mean((x - mean(x))^3) / sd(x)^3
descriptivas <- X |>
pivot_longer(everything(), names_to = "Variable", values_to = "valor") |>
group_by(Variable) |>
summarise(Media = mean(valor),
`Desv. est.` = sd(valor),
Mín = min(valor),
Q1 = quantile(valor, 0.25),
Mediana = median(valor),
Q3 = quantile(valor, 0.75),
Máx = max(valor),
`CV (%)` = sd(valor) / mean(valor) * 100,
Asimetría = asimetria(valor)) |>
mutate(Variable = factor(Variable, levels = names(indicadores))) |>
arrange(Variable)
datatable(descriptivas, rownames = FALSE,
caption = "Estadísticas descriptivas de las 18 variables (176 países, 2022)",
options = list(pageLength = 18, dom = "t", scrollX = TRUE)) |>
formatRound(columns = 2:10, digits = 2)El PIB per cápita presenta la mayor dispersión entre las variables económicas. Sus valores van desde cerca de 1.100 dólares en Burundi hasta casi 147.000 en Luxemburgo, con un coeficiente de variación superior al 100 %, y la media se ubica claramente por encima de la mediana debido a un número reducido de países de ingreso muy alto.
La inflación es la variable con mayor asimetría. Aunque 2022 fue un año de alzas de precios generalizadas, algunos países atravesaron episodios mucho más severos, entre ellos Líbano (171 %), Sudán (139 %), Zimbabue (105 %), Argentina y Turquía (72 % en ambos casos). Las variables de acceso a electricidad, agua potable e internet muestran, en cambio, asimetría negativa: la mayoría de los países tiene coberturas cercanas al 100 % y los rezagos se concentran en un grupo pequeño, principalmente del África subsahariana. En lo demográfico la heterogeneidad también es alta, ya que la fecundidad oscila entre aproximadamente uno y más de seis hijos por mujer y la población mayor de 65 años, entre menos del 2 % y casi el 30 %.
g_hist <- X |>
pivot_longer(everything(), names_to = "variable", values_to = "valor") |>
mutate(variable = factor(variable, levels = names(indicadores))) |>
ggplot(aes(x = valor)) +
geom_histogram(bins = 25, fill = col_principal, color = "white", linewidth = 0.3) +
facet_wrap(~ variable, scales = "free", ncol = 3) +
labs(title = "Distribución de las variables", x = NULL, y = "Número de países")
ggplotly(g_hist)Las variables se presentan estandarizadas (puntaje z) para poder compararlas en una misma escala. Al ubicar el cursor sobre un punto se muestra el país correspondiente.
g_box <- X |>
scale() |> as.data.frame() |>
mutate(pais = rownames(X)) |>
pivot_longer(-pais, names_to = "variable", values_to = "z") |>
mutate(variable = factor(variable, levels = rev(names(indicadores)))) |>
ggplot(aes(x = variable, y = z)) +
geom_boxplot(fill = "#cde2fb", color = col_principal, outlier.shape = NA, linewidth = 0.4) +
geom_jitter(aes(text = pais), width = 0.15, size = 0.9, alpha = 0.5, color = col_principal) +
coord_flip() +
labs(title = "Boxplots de las variables estandarizadas", x = NULL, y = "Puntaje z")
ggplotly(g_box, tooltip = c("text", "y"))# Los 3 países con el valor más alto y más bajo en cada variable
extremos <- map_dfr(names(indicadores), function(v) {
orden <- order(X[[v]], decreasing = TRUE)
tibble(Variable = v,
`3 más altos` = paste(rownames(X)[head(orden, 3)], collapse = ", "),
`3 más bajos` = paste(rownames(X)[tail(orden, 3)], collapse = ", "))
})
kable(extremos, caption = "Países con los valores extremos en cada variable") |>
kable_styling(bootstrap_options = c("striped", "condensed"), font_size = 12)| Variable | 3 más altos | 3 más bajos |
|---|---|---|
| pib_pc | Luxembourg, Singapore, Ireland | Somalia, Fed. Rep., Central African Republic, Burundi |
| inflacion | Lebanon, Sudan, Zimbabwe | China, Bolivia, Benin |
| comercio | Luxembourg, San Marino, Singapore | United States, Ethiopia, Sudan |
| agricultura | Syrian Arab Republic, Niger, Guinea-Bissau | Luxembourg, Singapore, San Marino |
| desempleo | Eswatini, South Africa, Djibouti | Niger, Cambodia, Qatar |
| part_lab_fem | Madagascar, Solomon Islands, Iceland | Syrian Arab Republic, Iraq, Afghanistan |
| esp_vida | San Marino, Japan, Switzerland | Nigeria, Somalia, Fed. Rep., Central African Republic |
| mort_menor5 | Central African Republic, Somalia, Fed. Rep., Nigeria | Slovenia, Estonia, San Marino |
| fecundidad | Somalia, Fed. Rep., Chad, Niger | Singapore, China, Korea, Rep. |
| fecund_adol | Central African Republic, Mozambique, Equatorial Guinea | Denmark, San Marino, Korea, Rep. |
| crec_pob | Qatar, Kuwait, United Arab Emirates | Albania, Serbia, Moldova |
| pob_65 | Japan, Italy, Portugal | Zambia, United Arab Emirates, Qatar |
| gasto_salud | Afghanistan, United States, Palau | Qatar, Lao PDR, Brunei Darussalam |
| agua | Belgium, Brunei Darussalam, Denmark | Burkina Faso, Central African Republic, Congo, Dem. Rep. |
| electricidad | Albania, Algeria, Antigua and Barbuda | Malawi, Chad, Burundi |
| internet | Bahrain, Saudi Arabia, United Arab Emirates | Chad, Uganda, Burundi |
| pob_urbana | Bahrain, Kuwait, Singapore | Niger, Malawi, Papua New Guinea |
| bosques | Suriname, Gabon, Palau | Libya, Oman, Egypt, Arab Rep. |
El caso más llamativo es el de la República Centroafricana. Los datos publicados para 2022 le asignan una esperanza de vida de 18,8 años y una mortalidad de menores de cinco años de 425 por cada 1.000 nacidos vivos, cifras muy distantes de las del siguiente país en ambas variables (Somalia, con 53,9 años y 143 por 1.000). Valores de esta magnitud no son plausibles frente a los del resto de países y no hay indicios de un evento demográfico de esa escala en el país durante 2022, por lo que es probable que reflejen un problema de estimación en la serie publicada y no una característica real del país. Por esta razón se excluyó de la estimación del modelo. En la sección de resultados se presenta un análisis de sensibilidad que compara el ACP con y sin este país.
Los demás valores extremos se conservaron porque corresponden a características reales de los países. Entre ellos están la inflación de Líbano, Sudán y Zimbabue, el volumen de comercio de Luxemburgo y Singapur (superior al 300 % del PIB, en buena parte por su papel como centros de reexportación), el crecimiento poblacional de los países del Golfo, explicado por la inmigración laboral, y el desempleo de Sudáfrica y Esuatini.
g_disp <- X |>
mutate(pais = rownames(X), continente = info$continente) |>
ggplot(aes(x = pib_pc, y = esp_vida, color = continente, text = pais)) +
geom_point(size = 2.4, alpha = 0.85) +
scale_x_log10(labels = scales::dollar) +
scale_color_manual(values = col_continente) +
labs(title = "PIB per cápita vs. esperanza de vida (2022)",
x = "PIB per cápita PPA (escala logarítmica)", y = "Esperanza de vida (años)",
color = "Continente")
ggplotly(g_disp, tooltip = c("text", "x", "y"))vars_clave <- c("pib_pc", "esp_vida", "fecundidad", "internet", "pob_65", "agricultura")
g_cont <- X |>
mutate(pais = rownames(X), continente = info$continente) |>
select(pais, continente, all_of(vars_clave)) |>
pivot_longer(all_of(vars_clave), names_to = "variable", values_to = "valor") |>
mutate(variable = factor(variable, levels = vars_clave)) |>
ggplot(aes(x = continente, y = valor, fill = continente)) +
geom_boxplot(alpha = 0.8, linewidth = 0.3, outlier.size = 0.8) +
facet_wrap(~ variable, scales = "free_y", ncol = 3) +
scale_fill_manual(values = col_continente) +
labs(title = "Variables clave por continente", x = NULL, y = NULL) +
theme(legend.position = "none", axis.text.x = element_text(angle = 30, hjust = 1))
ggplotly(g_cont)La relación entre el ingreso y la esperanza de vida tiene la forma descrita por Preston (1975): en los países de menor ingreso, pequeños aumentos se asocian con ganancias importantes en años de vida, mientras que en los de ingreso alto la curva tiende a estabilizarse. Los continentes, además, ocupan zonas bastante definidas del gráfico, lo que anticipa la presencia de un eje dominante asociado al desarrollo.
La matriz de correlaciones apunta en la misma dirección. Se observa un bloque amplio de variables con correlaciones entre 0,6 y 0,9 en valor absoluto, conformado por el ingreso, la esperanza de vida, el acceso a servicios, el uso de internet, la urbanización y el envejecimiento, que se relacionan de forma inversa con la fecundidad, la mortalidad infantil y el peso de la agricultura. Esta redundancia es la que justifica el uso del ACP. La inflación, el desempleo y el área forestal, por el contrario, presentan correlaciones bajas con el resto (|r| < 0,3), por lo que es probable que queden representadas en componentes de menor orden.
En la comparación por continente, África registra los valores más bajos de ingreso, esperanza de vida y uso de internet, junto con la mayor fecundidad y el mayor peso de la agricultura. Europa se sitúa en el extremo opuesto y es, a la vez, el continente con la población más envejecida. América y Asia ocupan posiciones intermedias con una variación interna considerable; en Asia, por ejemplo, se encuentran tanto Singapur y Qatar como Afganistán y Yemen.
Además de la exclusión de la República Centroafricana, se aplicó el logaritmo natural a las cuatro variables con asimetría positiva más marcada y escala multiplicativa: PIB per cápita, inflación, comercio y mortalidad de menores de cinco años. El ACP se basa en correlaciones de Pearson, que son sensibles a los valores extremos y suponen relaciones lineales, y la transformación logarítmica atenúa ambos problemas. En el caso del PIB per cápita, por ejemplo, la asimetría pasa de cerca de 1,8 a −0,2. Dado que el logaritmo solo está definido para valores positivos, se verificó que el mínimo de cada una de estas variables fuera mayor que cero. En el caso de la inflación, el valor más bajo de la muestra es 1.4 %, de modo que no hay tasas negativas ni nulas.
El desempleo también presenta asimetría positiva (cercana a 2), pero no se transformó. Algunos países registran tasas muy próximas a cero (la de Qatar es de 0,1 %), y el logaritmo convertiría esos valores en extremos negativos, con lo que el problema se trasladaría al otro lado de la distribución.
vars_log <- c("pib_pc", "inflacion", "comercio", "mort_menor5")
X_acp <- X[rownames(X) != "Central African Republic", ] |>
mutate(across(all_of(vars_log), log))
stopifnot(all(sapply(X[vars_log], min) > 0)) # control: valores estrictamente positivos
stopifnot(all(sapply(X_acp, function(x) all(is.finite(x))))) # control: sin NA, NaN ni infinitos
info_acp <- info |> filter(pais %in% rownames(X_acp))
cat("Países para el ACP:", nrow(X_acp), "\n")## Países para el ACP: 175
tibble(Variable = vars_log,
`Mínimo original` = sapply(X[vars_log], min),
`Asimetría original` = sapply(X[vars_log], asimetria),
`Asimetría con log` = sapply(X_acp[vars_log], asimetria)) |>
kable(digits = 2, caption = "Efecto de la transformación logarítmica") |>
kable_styling(bootstrap_options = c("striped", "condensed"), full_width = FALSE)| Variable | Mínimo original | Asimetría original | Asimetría con log |
|---|---|---|---|
| pib_pc | 1101.24 | 1.77 | -0.22 |
| inflacion | 1.35 | 5.39 | 0.92 |
| comercio | 2.70 | 2.20 | -0.87 |
| mort_menor5 | 1.40 | 5.81 | 0.01 |
La siguiente tabla resume cómo se pasó de las economías descargadas a los países que finalmente entran al modelo.
tibble(Etapa = c("Economías descargadas de DataBank (sin agregados regionales ni grupos de ingreso)",
"Excluidas por tener más de 2 variables faltantes",
"Excluida por valores no plausibles (República Centroafricana)",
"Muestra final para el ACP y la clasificación"),
Cantidad = c(nrow(datos), -(nrow(datos) - nrow(datos_f)), -1, nrow(X_acp))) |>
kable(caption = "Conformación de la muestra") |>
kable_styling(bootstrap_options = c("striped", "condensed"), full_width = FALSE) |>
row_spec(4, bold = TRUE)| Etapa | Cantidad |
|---|---|
| Economías descargadas de DataBank (sin agregados regionales ni grupos de ingreso) | 217 |
| Excluidas por tener más de 2 variables faltantes | -41 |
| Excluida por valores no plausibles (República Centroafricana) | -1 |
| Muestra final para el ACP y la clasificación | 175 |
Los agregados regionales, los grupos por nivel de ingreso y los bloques económicos se excluyeron desde la consulta en DataBank, por lo que el archivo contiene solo economías individuales. Las exclusiones por datos faltantes eliminaron casi todos los territorios dependientes (Puerto Rico, Hong Kong, Macao, Groenlandia, entre otros). La única entidad de la muestra final que no es un Estado miembro de las Naciones Unidas es Cisjordania y Gaza, correspondiente a Palestina, que tiene la condición de Estado observador; se mantuvo porque el Banco Mundial la reporta como una economía independiente con información completa.
El ACP se calculó sobre las variables estandarizadas, es decir, sobre la matriz de correlaciones, debido a que los indicadores están expresados en unidades muy distintas (dólares, porcentajes, años y tasas por cada mil).
R_acp <- cor(X_acp)
corrplot(R_acp, method = "color", type = "upper", order = "hclust",
tl.col = "black", tl.cex = 0.8, addCoef.col = "black",
number.cex = 0.55, col = COL2("RdBu", 200),
title = "Matriz de correlaciones (variables usadas en el ACP)",
mar = c(0, 0, 2, 0))Antes de interpretar los resultados se evaluó la pertinencia del método con dos pruebas. La prueba de esfericidad de Bartlett contrasta la hipótesis de que la matriz de correlaciones es la identidad, es decir, de que las variables no están relacionadas. El índice de Kaiser-Meyer-Olkin (KMO) mide la proporción de la correlación entre variables que es común a todas ellas; valores superiores a 0,7 suelen considerarse buenos y superiores a 0,8, meritorios.
n <- nrow(X_acp); p <- ncol(X_acp)
# Prueba de Bartlett
chi2_bart <- -(n - 1 - (2 * p + 5) / 6) * log(det(R_acp))
gl_bart <- p * (p - 1) / 2
p_bart <- pchisq(chi2_bart, gl_bart, lower.tail = FALSE)
# Índice KMO global
R_inv <- solve(R_acp)
R_parc <- -R_inv / sqrt(outer(diag(R_inv), diag(R_inv))) # correlaciones parciales
diag(R_parc) <- 0; R0 <- R_acp; diag(R0) <- 0
kmo <- sum(R0^2) / (sum(R0^2) + sum(R_parc^2))
tibble(Prueba = c("Bartlett: Chi-cuadrado", "Bartlett: grados de libertad",
"Bartlett: valor p", "KMO global"),
Valor = c(round(chi2_bart, 1), gl_bart, signif(p_bart, 3), round(kmo, 3))) |>
kable(caption = "Pruebas de adecuación de los datos al ACP") |>
kable_styling(bootstrap_options = c("striped", "condensed"), full_width = FALSE)| Prueba | Valor |
|---|---|
| Bartlett: Chi-cuadrado | 3108.200 |
| Bartlett: grados de libertad | 153.000 |
| Bartlett: valor p | 0.000 |
| KMO global | 0.897 |
La prueba de Bartlett rechaza la hipótesis de independencia (valor p prácticamente igual a cero) y el índice KMO alcanza 0.9, por lo que los datos resultan adecuados para el ACP.
res_pca <- PCA(X_acp, scale.unit = TRUE, ncp = 5, graph = FALSE)
# El signo de cada componente es arbitrario. Para que la interpretación sea
# estable, se orienta cada uno según una variable de referencia.
orientar <- function(res, dim, var_ref) {
if (res$var$coord[var_ref, dim] < 0) {
res$var$coord[, dim] <- -res$var$coord[, dim]
res$var$cor[, dim] <- -res$var$cor[, dim]
res$ind$coord[, dim] <- -res$ind$coord[, dim]
res$svd$U[, dim] <- -res$svd$U[, dim]
res$svd$V[, dim] <- -res$svd$V[, dim]
}
res
}
res_pca <- res_pca |>
orientar(1, "pib_pc") |> # Dim 1: + = mayor desarrollo
orientar(2, "part_lab_fem") |> # Dim 2: + = mayor participación laboral femenina
orientar(3, "crec_pob") |> # Dim 3: + = mayor crecimiento poblacional
orientar(4, "desempleo") # Dim 4: + = mayor desempleoPara comprobar que la exclusión de la República Centroafricana no determina los resultados, se estimó el mismo ACP incluyendo a este país.
X_con_rca <- X |> mutate(across(all_of(vars_log), log))
pca_con <- PCA(X_con_rca, scale.unit = TRUE, ncp = 4, graph = FALSE)
sens <- tibble(
Componente = paste0("Dim ", 1:4),
`% varianza sin RCA` = res_pca$eig[1:4, 2],
`% varianza con RCA` = pca_con$eig[1:4, 2],
`Correlación entre cargas` = sapply(1:4, function(d)
abs(cor(res_pca$var$coord[, d], pca_con$var$coord[, d]))))
contrib_rca <- pca_con$ind$contrib["Central African Republic", 1:4]
kable(sens, digits = 3, caption = "ACP con y sin la República Centroafricana (RCA)") |>
kable_styling(bootstrap_options = c("striped", "condensed"), full_width = FALSE)| Componente | % varianza sin RCA | % varianza con RCA | Correlación entre cargas |
|---|---|---|---|
| Dim 1 | 50.798 | 50.354 | 1.000 |
| Dim 2 | 9.729 | 10.011 | 0.995 |
| Dim 3 | 7.825 | 7.796 | 0.998 |
| Dim 4 | 6.312 | 6.390 | 0.987 |
Las proporciones de varianza prácticamente no cambian y las cargas de los cuatro componentes son casi idénticas en ambas versiones (correlaciones de 0.99 o más). La contribución de este país a cualquiera de los componentes no supera el 4.8 %. Esto se debe en buena parte a la transformación logarítmica de la mortalidad infantil, que reduce el peso de su valor extremo. La exclusión, por tanto, responde a la calidad del dato y no altera la estructura del análisis.
eig <- as.data.frame(res_pca$eig)
colnames(eig) <- c("Valor propio", "% de varianza", "% acumulado")
eig$Componente <- paste0("Dim ", seq_len(nrow(eig)))
datatable(eig[, c(4, 1:3)], rownames = FALSE,
caption = "Valores propios y varianza explicada",
options = list(pageLength = 18, dom = "t")) |>
formatRound(columns = 2:4, digits = 2)g_scree <- eig |>
mutate(Componente = factor(Componente, levels = Componente)) |>
ggplot(aes(x = Componente, y = `% de varianza`,
text = paste0(Componente, "<br>Valor propio: ", round(`Valor propio`, 2),
"<br>% varianza: ", round(`% de varianza`, 1),
"<br>% acumulado: ", round(`% acumulado`, 1)))) +
geom_col(fill = col_principal, width = 0.7) +
geom_line(aes(group = 1), color = "#0d366b", linewidth = 0.6) +
geom_point(color = "#0d366b", size = 2) +
geom_hline(yintercept = 100 / p, linetype = "dashed", color = "grey40") +
annotate("text", x = 14, y = 100 / p + 2.5, size = 3.2, color = "grey30",
label = "Criterio de Kaiser (valor propio = 1)") +
labs(title = "Gráfico de sedimentación (scree plot)",
x = NULL, y = "% de varianza explicada") +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
ggplotly(g_scree, tooltip = "text")Se retuvieron cuatro componentes, que en conjunto explican el 74.7 % de la variabilidad total. Son los únicos con valor propio mayor que 1, de acuerdo con el criterio de Kaiser (1960), y su varianza acumulada supera el umbral del 70 % que suele utilizarse como referencia. A partir de la cuarta dimensión la curva del gráfico de sedimentación es prácticamente plana. El primer componente explica por sí solo el 50.8 %.
cargas <- as.data.frame(res_pca$var$coord[, 1:ncp])
contrib <- as.data.frame(res_pca$var$contrib[, 1:ncp])
cargas |>
rownames_to_column("Variable") |>
mutate(across(-Variable, ~ cell_spec(sprintf("%.2f", .x),
bold = abs(.x) >= 0.5,
color = ifelse(abs(.x) >= 0.5,
ifelse(.x > 0, "#1c5cab", "#b3401a"),
"grey50")))) |>
kable(escape = FALSE, align = "lrrrr",
caption = "Cargas (correlación variable–componente). En negrita: |carga| ≥ 0,5") |>
kable_styling(bootstrap_options = c("striped", "condensed"), full_width = FALSE)| Variable | Dim.1 | Dim.2 | Dim.3 | Dim.4 |
|---|---|---|---|---|
| pib_pc | <span style=” font-weight: bold; color: rgba(28, 92, 171, 255) !important;” >0.93</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >-0.01</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >0.17</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >-0.04</span> |
| inflacion | <span style=” color: rgba(127, 127, 127, 255) !important;” >-0.18</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >0.01</span> | <span style=” font-weight: bold; color: rgba(179, 64, 26, 255) !important;” >-0.68</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >-0.34</span> |
| comercio | <span style=” font-weight: bold; color: rgba(28, 92, 171, 255) !important;” >0.51</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >0.01</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >0.22</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >0.44</span> |
| agricultura | <span style=” font-weight: bold; color: rgba(179, 64, 26, 255) !important;” >-0.84</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >0.11</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >-0.07</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >-0.24</span> |
| desempleo | <span style=” color: rgba(127, 127, 127, 255) !important;” >-0.04</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >-0.49</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >-0.41</span> | <span style=” font-weight: bold; color: rgba(28, 92, 171, 255) !important;” >0.62</span> |
| part_lab_fem | <span style=” color: rgba(127, 127, 127, 255) !important;” >0.26</span> | <span style=” font-weight: bold; color: rgba(28, 92, 171, 255) !important;” >0.78</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >0.29</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >-0.00</span> |
| esp_vida | <span style=” font-weight: bold; color: rgba(28, 92, 171, 255) !important;” >0.91</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >-0.04</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >0.09</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >-0.18</span> |
| mort_menor5 | <span style=” font-weight: bold; color: rgba(179, 64, 26, 255) !important;” >-0.93</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >-0.11</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >-0.04</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >0.12</span> |
| fecundidad | <span style=” font-weight: bold; color: rgba(179, 64, 26, 255) !important;” >-0.92</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >-0.01</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >0.09</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >-0.00</span> |
| fecund_adol | <span style=” font-weight: bold; color: rgba(179, 64, 26, 255) !important;” >-0.86</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >0.15</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >0.07</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >0.19</span> |
| crec_pob | <span style=” font-weight: bold; color: rgba(179, 64, 26, 255) !important;” >-0.55</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >-0.33</span> | <span style=” font-weight: bold; color: rgba(28, 92, 171, 255) !important;” >0.60</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >-0.15</span> |
| pob_65 | <span style=” font-weight: bold; color: rgba(28, 92, 171, 255) !important;” >0.80</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >0.39</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >-0.21</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >-0.08</span> |
| gasto_salud | <span style=” color: rgba(127, 127, 127, 255) !important;” >0.39</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >0.26</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >-0.32</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >-0.06</span> |
| agua | <span style=” font-weight: bold; color: rgba(28, 92, 171, 255) !important;” >0.88</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >-0.22</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >-0.06</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >-0.00</span> |
| electricidad | <span style=” font-weight: bold; color: rgba(28, 92, 171, 255) !important;” >0.85</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >-0.23</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >-0.11</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >-0.04</span> |
| internet | <span style=” font-weight: bold; color: rgba(28, 92, 171, 255) !important;” >0.93</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >-0.19</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >0.09</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >0.01</span> |
| pob_urbana | <span style=” font-weight: bold; color: rgba(28, 92, 171, 255) !important;” >0.69</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >-0.22</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >0.23</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >0.01</span> |
| bosques | <span style=” color: rgba(127, 127, 127, 255) !important;” >0.07</span> | <span style=” font-weight: bold; color: rgba(28, 92, 171, 255) !important;” >0.58</span> | <span style=” color: rgba(127, 127, 127, 255) !important;” >-0.06</span> | <span style=” font-weight: bold; color: rgba(28, 92, 171, 255) !important;” >0.52</span> |
La línea punteada indica la contribución que tendría cada variable si todas aportaran lo mismo (100/18 ≈ 5,6 %).
g_contrib <- lapply(1:ncp, function(d)
fviz_contrib(res_pca, choice = "var", axes = d, top = 8,
fill = col_principal, color = col_principal) +
labs(title = paste0("Contribuciones a la Dim ", d)) +
theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 8)))
gridExtra::grid.arrange(grobs = g_contrib, ncol = 2)c12 <- fviz_pca_var(res_pca, axes = c(1, 2), col.var = "contrib", repel = TRUE,
gradient.cols = c("#9ec5f4", "#2a78d6", "#0d366b")) +
labs(title = "Círculo de correlaciones: Dim 1 y 2")
c34 <- fviz_pca_var(res_pca, axes = c(3, 4), col.var = "contrib", repel = TRUE,
gradient.cols = c("#9ec5f4", "#2a78d6", "#0d366b")) +
labs(title = "Círculo de correlaciones: Dim 3 y 4")
gridExtra::grid.arrange(c12, c34, ncol = 2)Es el componente de mayor peso y en él cargan con fuerza la mayoría de las variables. En el extremo positivo se ubican el PIB per cápita, la esperanza de vida, el uso de internet, el acceso a agua y electricidad, la proporción de personas mayores y la urbanización. En el negativo se encuentran la mortalidad de menores de cinco años, la fecundidad total y adolescente, el peso de la agricultura y el crecimiento de la población. Este eje separa a los países de ingreso alto, con poblaciones longevas y urbanas, de las economías agrícolas con poblaciones jóvenes, y puede entenderse como una síntesis de la transición demográfica y económica.
Está definida principalmente por la participación laboral femenina y el área forestal, que cargan en sentido positivo, y por el desempleo, que lo hace en sentido negativo. Los valores más bajos corresponden a Jordania, Irak, Siria, Argelia y Cisjordania y Gaza, países que combinan una participación femenina muy baja, desempleo elevado y territorios áridos. Los más altos se observan en varios países del África subsahariana, algunas islas del Pacífico y Japón. Este componente es independiente del nivel de ingreso y describe un patrón regional. El ACP identifica la asociación entre estas variables, pero no permite establecer sus causas, de modo que las diferencias observadas no deben atribuirse a un factor específico sin un análisis adicional. Debe tenerse en cuenta, además, que la cobertura forestal también carga en la cuarta dimensión, por lo que no se relaciona únicamente con el mercado laboral.
Contrapone el crecimiento de la población a la inflación. En el extremo positivo aparecen los países del Golfo Pérsico (Qatar, Omán, Kuwait, Emiratos Árabes Unidos y Arabia Saudita) y Singapur, con un crecimiento poblacional elevado debido a la inmigración y precios relativamente estables. En el negativo se sitúan los países que enfrentaron crisis inflacionarias en 2022, como Sudán, Afganistán, Líbano, Sri Lanka y Argentina.
Depende sobre todo del desempleo, con aportes menores del área forestal y del comercio. Sus valores más altos corresponden a países del sur y el centro de África con desempleo persistente, como Sudáfrica, Esuatini, Gabón y la República del Congo.
coord_ind <- as.data.frame(res_pca$ind$coord[, 1:ncp]) |>
rownames_to_column("pais") |>
left_join(info_acp, by = "pais")
g_ind <- ggplot(coord_ind, aes(x = Dim.1, y = Dim.2, color = continente, text = pais)) +
geom_hline(yintercept = 0, color = "grey70", linewidth = 0.3) +
geom_vline(xintercept = 0, color = "grey70", linewidth = 0.3) +
geom_point(size = 2.2, alpha = 0.85) +
scale_color_manual(values = col_continente) +
labs(title = "Países en el plano de las dos primeras componentes",
x = paste0("Dim 1: Desarrollo (", round(eig[1, 2], 1), " %)"),
y = paste0("Dim 2: Particip. laboral femenina y bosques (", round(eig[2, 2], 1), " %)"),
color = "Continente")
ggplotly(g_ind, tooltip = "text")fviz_pca_biplot(res_pca, axes = c(1, 2), geom.ind = "point",
col.ind = info_acp$continente, palette = unname(col_continente),
col.var = "grey20", alpha.ind = 0.6, pointsize = 1.5,
repel = TRUE, legend.title = "Continente",
title = "Biplot: países y variables (Dim 1 y 2)")extremos_dim <- map_dfr(1:ncp, function(d) {
orden <- order(res_pca$ind$coord[, d], decreasing = TRUE)
tibble(Componente = paste0("Dim ", d),
`5 países con valor más alto` = paste(rownames(X_acp)[head(orden, 5)], collapse = ", "),
`5 países con valor más bajo` = paste(rownames(X_acp)[rev(tail(orden, 5))], collapse = ", "))
})
kable(extremos_dim, caption = "Países en los extremos de cada componente") |>
kable_styling(bootstrap_options = c("striped", "condensed"), font_size = 12)| Componente | 5 países con valor más alto | 5 países con valor más bajo |
|---|---|---|
| Dim 1 | San Marino, Japan, Luxembourg, Switzerland, Sweden | Chad, Niger, Somalia, Fed. Rep., Congo, Dem. Rep., Burundi |
| Dim 2 | Solomon Islands, Liberia, Japan, Guinea-Bissau, Burundi | West Bank and Gaza, Jordan, Iraq, Djibouti, Algeria |
| Dim 3 | Qatar, Kuwait, Oman, United Arab Emirates, Singapore | Afghanistan, Sudan, Lebanon, Eswatini, Sri Lanka |
| Dim 4 | Gabon, Congo, Rep., Eswatini, Djibouti, St. Vincent and the Grenadines | Sudan, Argentina, Ethiopia, Burundi, Pakistan |
En el plano de las dos primeras dimensiones, el eje horizontal ordena a los países según su nivel de desarrollo. Casi todos los países africanos se ubican a la izquierda y los europeos a la derecha, mientras que los de América y Asia se distribuyen en la zona central. En el eje vertical, los países árabes ocupan la parte inferior, lo que coincide con la interpretación dada a la segunda dimensión.
La clasificación se realizó sobre los cuatro componentes retenidos y
no sobre las 18 variables originales. Si se hubieran utilizado estas
últimas, el nivel de desarrollo habría tenido un peso desproporcionado
en el cálculo de las distancias, puesto que varias variables lo miden de
manera casi equivalente. Los componentes descartados, que suman
alrededor del 25 % de la varianza, recogen sobre todo variación
particular de cada país y aportan poco a la formación de grupos. Se
utilizó la función HCPC() de FactoMineR con
distancia euclidiana.
Las distancias se calcularon sobre las coordenadas de los países sin reescalar los componentes. Como la varianza de cada componente es su valor propio, la primera dimensión tiene más peso en las distancias que las otras tres. Esta es una decisión deliberada y es la forma habitual de aplicar la clasificación sobre los resultados de un ACP (Husson et al., 2017): cada dimensión influye en proporción a la información que resume. Si se estandarizaran los componentes, la cuarta dimensión, que explica cerca del 6 % de la varianza, pesaría lo mismo que la primera, que explica alrededor del 50 %.
# ACP con los 4 componentes retenidos (base de la clusterización)
res_pca4 <- PCA(X_acp, scale.unit = TRUE, ncp = ncp, graph = FALSE) |>
orientar(1, "pib_pc") |> orientar(2, "part_lab_fem") |>
orientar(3, "crec_pob") |> orientar(4, "desempleo")
coord4 <- res_pca4$ind$coord # coordenadas de los países en los 4 factores
# Árbol jerárquico de Ward (solo para elegir el número de clusters)
arbol <- hclust(dist(coord4), method = "ward.D2")La altura de cada unión del dendrograma refleja la pérdida de homogeneidad que se produce al fusionar dos grupos. Un aumento brusco indica que se están uniendo grupos poco parecidos, por lo que conviene cortar el árbol antes de ese punto.
saltos <- tibble(k = 2:11, altura = rev(arbol$height)[1:10])
g_saltos <- ggplot(saltos, aes(x = factor(k), y = altura,
text = paste0("Pasar de ", k, " a ", k - 1,
" clusters<br>Altura: ", round(altura, 1)))) +
geom_col(fill = col_principal, width = 0.7) +
labs(title = "Altura de las últimas uniones del árbol de Ward",
x = "Número de clusters (k)", y = "Altura de la unión (pérdida de homogeneidad)")
ggplotly(g_saltos, tooltip = "text")El coeficiente de silueta (Rousseeuw, 1987) compara, para cada país, la cercanía a su propio grupo con la cercanía al grupo vecino. Toma valores entre −1 y 1, y los valores altos indican grupos mejor separados.
codo <- fviz_nbclust(coord4, hcut, method = "wss", k.max = 8, hc_method = "ward.D2") +
labs(title = "Método del codo", x = "Número de clusters", y = "Suma de cuadrados intra-cluster")
silueta <- fviz_nbclust(coord4, hcut, method = "silhouette", k.max = 8, hc_method = "ward.D2") +
labs(title = "Coeficiente de silueta promedio", x = "Número de clusters", y = "Silueta")
gridExtra::grid.arrange(codo, silueta, ncol = 2)crit_k <- map_dfr(2:8, function(k) {
g <- cutree(arbol, k)
sil <- cluster::silhouette(g, dist(coord4))
W <- sum(sapply(unique(g), function(c)
sum(scale(coord4[g == c, , drop = FALSE], scale = FALSE)^2)))
Tot <- sum(scale(coord4, scale = FALSE)^2)
tibble(k = k,
`Inercia entre / total (%)` = (1 - W / Tot) * 100,
`Silueta promedio` = mean(sil[, 3]),
`Tamaño del grupo más pequeño` = min(table(g)))
})
kable(crit_k, digits = 2, caption = "Criterios para elegir el número de clusters") |>
kable_styling(bootstrap_options = c("striped", "condensed"), full_width = FALSE)| k | Inercia entre / total (%) | Silueta promedio | Tamaño del grupo más pequeño |
|---|---|---|---|
| 2 | 46.13 | 0.46 | 37 |
| 3 | 61.51 | 0.31 | 37 |
| 4 | 67.08 | 0.32 | 13 |
| 5 | 72.46 | 0.31 | 13 |
| 6 | 75.20 | 0.31 | 8 |
| 7 | 77.36 | 0.29 | 7 |
| 8 | 79.27 | 0.29 | 7 |
Se optó por cuatro grupos. Las mayores alturas del dendrograma corresponden a las tres últimas uniones y, con cuatro grupos, la inercia entre grupos representa cerca de dos tercios de la inercia total; a partir de ese punto la ganancia por cada grupo adicional es reducida. La silueta alcanza su máximo con dos grupos, pero esa partición se limita a separar países de ingreso alto y bajo, información que ya ofrece la primera dimensión. Con cuatro grupos la silueta es prácticamente igual que con tres y aparece un conjunto de países con un perfil claramente diferenciado.
Elegir cuatro grupos implica aceptar una separación algo menor a cambio de una segmentación más informativa. Con dos grupos solo se distingue entre países de desarrollo alto y bajo; con cuatro se separan, dentro de los países de ingreso alto, los que tienen poblaciones envejecidas de los que crecen por inmigración, y se aísla un grupo intermedio que en la partición de dos grupos quedaba repartido entre los extremos. Para el objetivo del trabajo, que es caracterizar distintos tipos de desarrollo, esta información adicional justifica la elección.
Un coeficiente de silueta cercano a 0,3 indica una estructura de grupos moderada y no una separación nítida. Por ello, los cuatro grupos deben entenderse como una segmentación exploratoria y no como una clasificación definitiva de los países.
Se fijó consol = FALSE para conservar exactamente la
partición del árbol de Ward, sin la reasignación posterior mediante
k-medias que HCPC() aplica por defecto.
# Ponerle nombre a cada cluster según su perfil. Se ordenan por su promedio en la
# Dim 1 (desarrollo); entre los dos de mayor desarrollo, el de mayor Dim 3
# (crecimiento poblacional) es el de los países de alto ingreso con inmigración.
cl <- res_hcpc$data.clust$clust
med_dim <- aggregate(coord4, by = list(cl = cl), FUN = mean)
med_dim$cl <- as.character(med_dim$cl)
orden_d1 <- med_dim$cl[order(med_dim$Dim.1)]
altos <- orden_d1[3:4]
golfo <- altos[which.max(med_dim$Dim.3[match(altos, med_dim$cl)])]
desar <- setdiff(altos, golfo)
etiquetas <- setNames(
c("1. Bajo desarrollo", "2. Desarrollo medio",
"3. Desarrollados y envejecidos", "4. Alto ingreso con crecimiento acelerado"),
c(orden_d1[1], orden_d1[2], desar, golfo))
grupos <- tibble(pais = rownames(coord4),
cluster = factor(etiquetas[as.character(cl)], levels = sort(etiquetas))) |>
left_join(info_acp, by = "pais")
col_cluster <- setNames(c("#2a78d6", "#eb6834", "#1baf7a", "#eda100"), sort(etiquetas))
grupos |> count(cluster, name = "Número de países") |>
rename(Cluster = cluster) |>
kable(caption = "Tamaño de cada cluster") |>
kable_styling(bootstrap_options = c("striped", "condensed"), full_width = FALSE)| Cluster | Número de países |
|---|---|
|
37 |
|
82 |
|
43 |
|
13 |
Para evaluar la sensibilidad de los grupos frente a la selección del número de componentes, se comparó la partición obtenida con cuatro dimensiones con las que resultan de utilizar tres y cinco componentes, y se calculó además la silueta promedio de cada grupo. Este procedimiento no equivale a una evaluación de estabilidad por remuestreo, ya que no se modifican los países ni los datos. La comparación se hace con el índice de Rand ajustado (ARI), que vale 1 cuando dos particiones coinciden por completo y cerca de 0 cuando coinciden solo por azar.
sil_k <- cluster::silhouette(as.integer(grupos$cluster), dist(coord4))
sil_cl <- tapply(sil_k[, 3], grupos$cluster, mean)
ari <- function(a, b) {
tab <- table(a, b); n <- sum(tab)
s_ij <- sum(choose(tab, 2))
s_a <- sum(choose(rowSums(tab), 2)); s_b <- sum(choose(colSums(tab), 2))
esp <- s_a * s_b / choose(n, 2)
(s_ij - esp) / ((s_a + s_b) / 2 - esp)
}
particion <- function(nc) cutree(hclust(dist(res_pca$ind$coord[, 1:nc]), "ward.D2"), k)
ari_3 <- ari(particion(4), particion(3))
ari_5 <- ari(particion(4), particion(5))
tibble(Grupo = names(sil_cl), `Silueta promedio` = sil_cl) |>
kable(digits = 2, caption = "Silueta promedio por grupo") |>
kable_styling(bootstrap_options = c("striped", "condensed"), full_width = FALSE)| Grupo | Silueta promedio |
|---|---|
|
0.4637714 |
|
0.1315204 |
|
0.5482357 |
|
0.3142855 |
tibble(Comparación = c("4 componentes frente a 3", "4 componentes frente a 5"),
ARI = c(ari_3, ari_5)) |>
kable(digits = 2, caption = "Coincidencia entre particiones según el número de componentes") |>
kable_styling(bootstrap_options = c("striped", "condensed"), full_width = FALSE)| Comparación | ARI |
|---|---|
| 4 componentes frente a 3 | 0.75 |
| 4 componentes frente a 5 | 0.81 |
La concordancia con las particiones basadas en tres y cinco componentes es moderada (ARI de 0.75 y 0.81, respectivamente). La silueta por grupo muestra dónde está la fragilidad: el grupo de desarrollo medio es el menos cohesionado (0.13), lo que es coherente con su heterogeneidad, mientras que los grupos de los extremos tienen siluetas más altas. Los países que cambian de grupo al modificar el número de componentes se encuentran, en su mayoría, en la frontera entre el grupo de desarrollo medio y sus vecinos. Este resultado refuerza la lectura de la segmentación como exploratoria.
mapa <- plot_ly()
for (cl_i in levels(grupos$cluster)) {
sub <- grupos |> filter(cluster == cl_i)
mapa <- mapa |>
add_trace(type = "choropleth", locations = sub$iso3c, z = rep(1, nrow(sub)),
text = paste0(sub$pais, "<br>", cl_i), hoverinfo = "text",
colorscale = list(c(0, col_cluster[[cl_i]]), c(1, col_cluster[[cl_i]])),
showscale = FALSE, marker = list(line = list(color = "white", width = 0.4)),
name = cl_i)
}
mapa |>
layout(title = "Mapa mundial de los clusters de países (2022)",
geo = list(showframe = FALSE, showcoastlines = FALSE,
projection = list(type = "natural earth"),
showcountries = TRUE, countrycolor = "white",
landcolor = "#e8e8e6", showland = TRUE))Azul: bajo desarrollo. Naranja: desarrollo medio. Verde: desarrollados y envejecidos. Amarillo: alto ingreso con crecimiento acelerado. Los países en gris no forman parte del análisis.
coord_cl <- as.data.frame(coord4) |>
rownames_to_column("pais") |>
left_join(grupos |> select(pais, cluster), by = "pais")
plano_clusters <- function(ejex, ejey, titx, tity) {
g <- ggplot(coord_cl, aes(x = .data[[ejex]], y = .data[[ejey]],
color = cluster, text = paste0(pais, "<br>", cluster))) +
geom_hline(yintercept = 0, color = "grey70", linewidth = 0.3) +
geom_vline(xintercept = 0, color = "grey70", linewidth = 0.3) +
geom_point(size = 2.3, alpha = 0.85) +
scale_color_manual(values = col_cluster) +
labs(x = titx, y = tity, color = "Cluster")
ggplotly(g, tooltip = "text")
}
plano_clusters("Dim.1", "Dim.2",
paste0("Dim 1: Desarrollo (", round(eig[1, 2], 1), " %)"),
paste0("Dim 2: Particip. laboral femenina y bosques (", round(eig[2, 2], 1), " %)")) |>
layout(title = "Clusters en el plano Dim 1 – Dim 2")# Colores del dendrograma en el mismo orden en que aparecen los grupos en el árbol
ct <- cutree(arbol, k)
orden_ramas <- unique(ct[arbol$order])
col_ramas <- sapply(orden_ramas, function(i)
col_cluster[[as.character(grupos$cluster[match(names(ct)[ct == i][1], grupos$pais)])]])
fviz_dend(arbol, k = k, k_colors = unname(col_ramas),
cex = 0.35, rect = TRUE, rect_fill = TRUE, rect_border = "grey60",
horiz = TRUE, main = "Dendrograma de Ward sobre los 4 componentes principales",
ylab = "Altura")Los grupos se ordenan principalmente a lo largo del eje de desarrollo. Los dos de mayor ingreso aparecen casi superpuestos en el plano formado por las dimensiones 1 y 2, pero se separan con claridad en el plano de las dimensiones 1 y 3 debido al elevado crecimiento poblacional de uno de ellos.
# Variables en su escala original (sin logaritmos), para interpretar fácilmente
X_orig <- X[rownames(X_acp), ]
perfil <- X_orig |>
rownames_to_column("pais") |>
left_join(grupos |> select(pais, cluster), by = "pais") |>
group_by(cluster) |>
summarise(across(all_of(names(indicadores)), median), n = n()) |>
pivot_longer(-c(cluster, n), names_to = "Variable", values_to = "mediana") |>
select(-n) |>
pivot_wider(names_from = cluster, values_from = mediana) |>
mutate(Variable = factor(Variable, levels = names(indicadores))) |>
arrange(Variable)
perfil |>
mutate(across(-Variable, ~ ifelse(abs(.x) >= 1000,
format(round(.x), big.mark = "."),
format(round(.x, 1), nsmall = 1)))) |>
kable(align = "lrrrr", caption = "Mediana de cada variable por cluster (escala original)") |>
kable_styling(bootstrap_options = c("striped", "condensed"), full_width = FALSE)| Variable |
|
|
|
|
|---|---|---|---|---|
| pib_pc | 3.195 | 14.568 | 51.483 | 71.957 |
| inflacion | 10.3 | 8.1 | 9.1 | 5.0 |
| comercio | 56.1 | 87.8 | 110.2 | 162.2 |
| agricultura | 25.6 | 8.3 | 2.2 | 0.8 |
| desempleo | 3.8 | 7.1 | 5.6 | 3.6 |
| part_lab_fem | 59.8 | 54.8 | 72.2 | 65.2 |
| esp_vida | 63.5 | 72.7 | 80.6 | 81.9 |
| mort_menor5 | 60.0 | 16.9 | 3.9 | 5.6 |
| fecundidad | 4.3 | 2.2 | 1.5 | 1.6 |
| fecund_adol | 90.2 | 36.8 | 6.7 | 5.6 |
| crec_pob | 2.5 | 1.0 | 0.0 | 2.5 |
| pob_65 | 3.1 | 6.3 | 19.9 | 4.1 |
| gasto_salud | 4.2 | 5.8 | 8.9 | 5.3 |
| agua | 67.2 | 95.1 | 99.9 | 100.0 |
| electricidad | 49.0 | 100.0 | 100.0 | 100.0 |
| internet | 28.0 | 74.7 | 89.9 | 97.6 |
| pob_urbana | 35.1 | 61.0 | 77.1 | 94.2 |
| bosques | 22.6 | 33.1 | 36.1 | 3.7 |
Los tonos azules indican que el grupo se encuentra por encima del promedio de los países en la variable correspondiente, y los naranjas, que se encuentra por debajo.
# Perfil estandarizado: cuántas desviaciones estándar está cada cluster
# por encima (+) o por debajo (−) del promedio mundial
perfil_z <- as.data.frame(scale(X_acp)) |>
rownames_to_column("pais") |>
left_join(grupos |> select(pais, cluster), by = "pais") |>
group_by(cluster) |>
summarise(across(all_of(names(indicadores)), mean)) |>
pivot_longer(-cluster, names_to = "variable", values_to = "z") |>
mutate(variable = factor(variable, levels = rev(names(indicadores))))
g_heat <- ggplot(perfil_z, aes(x = cluster, y = variable, fill = z,
text = paste0(cluster, "<br>", variable, ": ", round(z, 2)))) +
geom_tile(color = "white", linewidth = 0.6) +
geom_text(aes(label = sprintf("%.1f", z)), size = 3, color = "grey15") +
scale_fill_gradient2(low = "#d95926", mid = "#f2f2f0", high = "#2a78d6", midpoint = 0,
limits = c(-2.2, 2.2), oob = scales::squish) +
labs(title = "Perfil de los clusters (promedio estandarizado)",
x = NULL, y = NULL, fill = "Desv. est.\nrespecto a\nla media") +
theme(axis.text.x = element_text(angle = 15, hjust = 1))
ggplotly(g_heat, tooltip = "text")El estadístico v-test permite determinar si la media de una variable en un grupo difiere significativamente de la media general. Un valor absoluto mayor que 2 equivale aproximadamente a un nivel de significancia del 5 %, y el signo indica si el grupo se ubica por encima o por debajo del promedio.
# Variables que caracterizan significativamente cada cluster (prueba v-test de HCPC)
desc_cl <- map_dfr(names(res_hcpc$desc.var$quanti), function(c) {
tab <- as.data.frame(res_hcpc$desc.var$quanti[[c]])
tibble(Cluster = etiquetas[c], Variable = rownames(tab),
`v-test` = tab$v.test, `Media cluster` = tab$`Mean in category`,
`Media general` = tab$`Overall mean`)
}) |>
filter(abs(`v-test`) >= 3) |>
arrange(Cluster, desc(abs(`v-test`)))
datatable(desc_cl, rownames = FALSE, filter = "top",
caption = "Variables que más distinguen a cada cluster (|v-test| ≥ 3; en logaritmo: pib_pc, inflacion, comercio, mort_menor5)",
options = list(pageLength = 10)) |>
formatRound(columns = 3:5, digits = 2)La siguiente tabla resume los rasgos que distinguen a cada grupo; el detalle se presenta a continuación.
| Grupo | Rasgos que lo distinguen |
|---|---|
| Bajo desarrollo | Fecundidad y mortalidad infantil altas, economía agrícola, acceso limitado a electricidad e internet |
| Desarrollo medio | Valores cercanos al promedio mundial, desempleo más alto, participación laboral femenina baja, gran heterogeneidad interna |
| Desarrollados y envejecidos | Esperanza de vida superior a 80 años, fecundidad baja, mayor proporción de personas mayores y mayor gasto en salud |
| Alto ingreso con crecimiento acelerado | Ingreso más alto, población joven que crece por inmigración, alta apertura comercial y urbanización casi total |
Lo integran principalmente países del África subsahariana, como Níger, Chad, Malí, Nigeria, Etiopía, la República Democrática del Congo y Mozambique, a los que se suman Afganistán, Haití y algunas islas del Pacífico (Islas Salomón y Papúa Nueva Guinea, entre ellas). El PIB per cápita mediano ronda los 3.000 dólares, la esperanza de vida se aproxima a 63 años y la fecundidad a cuatro hijos por mujer. Menos de la mitad de la población tiene acceso a electricidad y la agricultura representa cerca de una cuarta parte del PIB. Se trata de poblaciones jóvenes, mayoritariamente rurales y de rápido crecimiento, que se encuentran en las primeras etapas de la transición demográfica.
Es el grupo más numeroso y también el más heterogéneo. Reúne a la mayor parte de América Latina, incluida Colombia, a buena parte de Asia (China, India, Indonesia y Vietnam, entre otros), al mundo árabe, a Asia Central, a los países de los Balcanes que no pertenecen a la Unión Europea y a los países de ingreso medio del sur de África. En casi todas las variables sus valores se acercan al promedio mundial: un ingreso mediano cercano a 15.000 dólares, una esperanza de vida de unos 73 años y una cobertura de servicios básicos prácticamente universal, con rezagos en el uso de internet. Sus rasgos más distintivos son la tasa de desempleo, la más alta de los cuatro grupos, y una participación laboral femenina inferior a la de los demás, características que se deben en gran medida a los países árabes y del sur de África que lo conforman.
Comprende a Europa, incluida Rusia, junto con Estados Unidos, Canadá, Japón, Corea del Sur, Australia, Nueva Zelanda, Chile, Uruguay y Cuba. Es el grupo con la población más envejecida, pues cerca de una quinta parte de sus habitantes tiene 65 años o más. La fecundidad mediana es de alrededor de 1,4 hijos por mujer y el crecimiento poblacional es casi nulo. También registra el mayor gasto en salud, la participación laboral femenina más alta y una esperanza de vida superior a 80 años. El principal desafío que enfrentan estos países es el financiamiento de las pensiones y de la atención en salud de una población cada vez mayor.
Es el grupo más pequeño. Incluye a los países del Golfo (Qatar, Emiratos Árabes Unidos, Kuwait, Arabia Saudita, Omán y Baréin), a centros financieros y comerciales como Singapur, Luxemburgo, Irlanda y Malta, y a Israel, Islandia y Maldivas. Presenta el ingreso más alto, con una mediana superior a 70.000 dólares, así como la mayor apertura comercial y una urbanización casi total. Se diferencia del grupo 3 por tener una población joven y en rápido crecimiento, que en estos países obedece más a la inmigración atraída por las economías petroleras y financieras que a la natalidad. La cobertura forestal es muy baja, lo que es esperable en territorios desérticos o insulares de poca extensión.
table(Cluster = grupos$cluster, Continente = grupos$continente) |>
addmargins() |>
kable(caption = "Distribución de los países por cluster y continente") |>
kable_styling(bootstrap_options = c("striped", "condensed"), full_width = FALSE)| Africa | Americas | Asia | Europe | Oceania | Sum | |
|---|---|---|---|---|---|---|
|
32 | 1 | 1 | 0 | 3 | 37 |
|
18 | 25 | 32 | 2 | 5 | 82 |
|
0 | 5 | 3 | 33 | 2 | 43 |
|
0 | 0 | 9 | 4 | 0 | 13 |
| Sum | 50 | 31 | 45 | 39 | 10 | 175 |
latam_desarrollados <- grupos |>
filter(grepl("^3", cluster),
countrycode(iso3c, "iso3c", "region") == "Latin America & Caribbean") |>
pull(pais) |> paste(collapse = ", ")
col_pos <- coord_cl |> filter(pais == "Colombia")
rank_d1 <- rank(-coord4[, "Dim.1"])["Colombia"]Los cuatro componentes retenidos explican el 74.7 % de la variabilidad. El primero, asociado al nivel de desarrollo, explica alrededor de la mitad, lo que indica que las diferencias entre países responden en primer lugar a su grado de desarrollo y que los indicadores de ingreso, salud, servicios, conectividad y estructura demográfica tienden a moverse de manera conjunta. Los tres componentes restantes recogen patrones independientes del ingreso: la participación laboral de las mujeres, especialmente baja en el mundo árabe; la oposición entre crecimiento poblacional e inflación, y el desempleo en el sur de África.
La clasificación produjo cuatro grupos (bajo desarrollo, desarrollo medio, desarrollados y envejecidos, y alto ingreso con crecimiento acelerado), cuyas características se describieron en la sección anterior.
El modelo no incluía ninguna variable de localización y, aun así, los grupos guardan una correspondencia estrecha con la geografía. El África subsahariana conforma casi en su totalidad el grupo 1 y Europa constituye el núcleo del grupo 3. Este resultado sugiere que el desarrollo tiene un componente regional importante, posiblemente ligado a factores históricos, institucionales y de vecindad que no se incluyeron en el análisis.
La correspondencia, sin embargo, no es completa. Algunos países latinoamericanos (Chile, Cuba, Uruguay) se agrupan con los países desarrollados por su esperanza de vida, su baja fecundidad y el envejecimiento de su población, a pesar de tener un ingreso inferior al de la mayoría de ese grupo. Sudáfrica, Botsuana y Gabón se apartan del resto del África subsahariana y quedan en el grupo de desarrollo medio, mientras que Afganistán y Haití, que no pertenecen a ese continente, se clasifican en el de bajo desarrollo.
Los grupos 3 y 4 tienen niveles de ingreso comparables y, pese a ello, el análisis los separa por su estructura demográfica. Este resultado muestra que una clasificación basada solo en el PIB per cápita habría omitido diferencias relevantes. Por último, a lo largo de la primera dimensión los grupos 1, 2 y 3 se ordenan de manera coherente con las etapas de la transición demográfica, desde la alta natalidad y mortalidad hasta el envejecimiento.
Colombia forma parte del grupo de desarrollo medio y ocupa el puesto 84 entre 175 países en la dimensión de desarrollo. Comparte grupo con la mayoría de los países latinoamericanos, con los que tiene en común una cobertura casi universal de servicios básicos y una transición demográfica avanzada, aunque mantiene brechas de ingreso, empleo y conectividad respecto a los países desarrollados.
Para precisar la posición de Colombia se identificaron los cinco países más cercanos en el espacio de los cuatro componentes y se compararon sus indicadores con la mediana de su grupo.
d_col <- sqrt(colSums((t(coord4) - coord4["Colombia", ])^2))
vecinos <- names(sort(d_col))[2:6]
tibble(`País` = vecinos, Distancia = sort(d_col)[2:6],
Grupo = as.character(grupos$cluster[match(vecinos, grupos$pais)])) |>
kable(digits = 2, caption = "Países más parecidos a Colombia (distancia en los 4 componentes)") |>
kable_styling(bootstrap_options = c("striped", "condensed"), full_width = FALSE)| País | Distancia | Grupo |
|---|---|---|
| El Salvador | 0.59 |
|
| Antigua and Barbuda | 0.63 |
|
| Mauritius | 0.73 |
|
| Brazil | 0.73 |
|
| Paraguay | 0.83 |
|
grupo_col <- as.character(col_pos$cluster)
comp_col <- tibble(
Variable = names(indicadores),
Colombia = unlist(X_orig["Colombia", names(indicadores)]),
`Mediana del grupo` = perfil[[grupo_col]][match(names(indicadores), perfil$Variable)]) |>
mutate(`Diferencia (%)` = (Colombia - `Mediana del grupo`) / abs(`Mediana del grupo`) * 100)
kable(comp_col, digits = 1, caption = paste("Colombia frente a la mediana del grupo", grupo_col)) |>
kable_styling(bootstrap_options = c("striped", "condensed"), full_width = FALSE)| Variable | Colombia | Mediana del grupo | Diferencia (%) |
|---|---|---|---|
| pib_pc | 20854.0 | 14567.8 | 43.2 |
| inflacion | 10.2 | 8.1 | 25.6 |
| comercio | 48.3 | 87.8 | -45.0 |
| agricultura | 8.8 | 8.3 | 6.6 |
| desempleo | 10.5 | 7.1 | 49.3 |
| part_lab_fem | 56.3 | 54.8 | 2.7 |
| esp_vida | 76.5 | 72.7 | 5.2 |
| mort_menor5 | 12.3 | 16.9 | -27.2 |
| fecundidad | 1.7 | 2.2 | -25.5 |
| fecund_adol | 60.2 | 36.8 | 63.4 |
| crec_pob | 1.1 | 1.0 | 7.2 |
| pob_65 | 9.0 | 6.3 | 43.3 |
| gasto_salud | 7.6 | 5.8 | 30.7 |
| agua | 96.7 | 95.1 | 1.7 |
| electricidad | 100.0 | 100.0 | 0.0 |
| internet | 72.8 | 74.7 | -2.6 |
| pob_urbana | 78.0 | 61.0 | 27.9 |
| bosques | 53.9 | 33.1 | 62.8 |
Los países más cercanos a Colombia son El Salvador, Antigua and Barbuda, Mauritius, Brazil, Paraguay. La tabla permite identificar en qué indicadores Colombia se ubica por encima o por debajo de la mediana de su grupo. Las variables con mayor carga en la primera dimensión (ingreso, esperanza de vida, mortalidad infantil, fecundidad y acceso a servicios) son las que explican su posición intermedia en el eje de desarrollo.
El análisis permitió identificar patrones de desarrollo reconocibles entre los países. Las 18 variables pudieron resumirse en cuatro dimensiones que conservan cerca de tres cuartas partes de la información original, y la primera de ellas, vinculada al nivel de desarrollo, concentra alrededor de la mitad de la variabilidad. Esto confirma que gran parte de los indicadores económicos, sociales y de acceso a servicios evolucionan de forma conjunta.
La clasificación dio lugar a cuatro grupos con interpretación económica y geográfica. Tres de ellos pueden interpretarse como etapas sucesivas del desarrollo, y el cuarto reúne a economías de ingreso alto cuya población crece principalmente por inmigración. Los grupos coinciden en buena medida con regiones reales del mundo, aun cuando el modelo no contaba con información sobre la ubicación de los países, lo que sugiere que la segmentación recoge diferencias de fondo. Con todo, la silueta cercana a 0,3 y la concordancia moderada frente a otras especificaciones indican que se trata de una segmentación exploratoria, útil para describir asociaciones entre dimensiones del desarrollo más que para clasificar a cada país de forma definitiva.
Los resultados muestran también que el desarrollo es un fenómeno multidimensional. Junto al eje principal aparecieron dimensiones que no dependen del ingreso, como la participación laboral de las mujeres, la estabilidad de precios y el desempleo, y que permiten distinguir entre países con niveles de riqueza similares.
En cuanto al método, la combinación del ACP con la clasificación de Ward resultó apropiada para este conjunto de datos. Las pruebas de Bartlett y KMO confirmaron la pertinencia del ACP, que redujo la redundancia entre variables muy correlacionadas al generar dimensiones incorrelacionadas. Como la clasificación se realizó sobre las coordenadas factoriales sin estandarizarlas, los componentes de mayor varianza tienen más influencia en las distancias del método de Ward, de modo que la segmentación es más sensible a las dimensiones que explican mayor variabilidad, en particular al nivel de desarrollo.
El estudio presenta algunas limitaciones. Al referirse a un único año, describe la situación de los países en un momento determinado y no su evolución. Fue necesario excluir territorios con información incompleta y un país con valores atípicos, así como imputar algunos datos. La imputación se realizó sobre las variables en su escala original, antes de aplicar los logaritmos; imputar sobre la escala logarítmica es una alternativa razonable que podría modificar la asignación de algunos países de frontera. La asignación de algunos países, sobre todo los del grupo de desarrollo medio, cambia cuando se modifica el número de componentes utilizados en la clasificación. Los resultados dependen, además, de las variables seleccionadas; la inclusión de indicadores de educación, desigualdad o calidad institucional podría modificar la composición de los grupos.
Este tipo de análisis tiene diversas aplicaciones en economía y en el diseño de políticas públicas. Los organismos multilaterales, como el Banco Mundial o el Banco Interamericano de Desarrollo, pueden emplearlo para orientar la cooperación hacia grupos de países con necesidades similares, en lugar de basarse exclusivamente en el nivel de ingreso. Un gobierno puede utilizarlo para compararse con los países de su mismo grupo, fijar metas alcanzables e identificar experiencias exitosas; en el caso de Colombia, la referencia serían otros países de desarrollo medio. La segmentación también aporta elementos para adecuar las políticas a la etapa demográfica de cada país: mientras los países del grupo 1 requieren inversión en educación, salud materno-infantil y empleo juvenil, los del grupo 3 deben prepararse para financiar las pensiones y la atención en salud de una población envejecida. Las entidades financieras recurren a clasificaciones similares para evaluar el riesgo país, y su aplicación en distintos años permitiría hacer seguimiento a los Objetivos de Desarrollo Sostenible a partir de los cambios de grupo. La misma metodología puede emplearse a escala nacional, con información del DANE por departamentos o municipios, como apoyo a la focalización de la inversión pública regional.
Arel-Bundock, V., Enevoldsen, N., & Yetman, C. J. (2018). countrycode: An R package to convert country names and country codes. Journal of Open Source Software, 3(28), 848.
Banco Mundial. (2026). World Development Indicators [Base de datos]. DataBank. https://databank.worldbank.org/source/world-development-indicators
Husson, F., Lê, S., & Pagès, J. (2017). Exploratory multivariate analysis by example using R (2.ª ed.). CRC Press.
James, G., Witten, D., Hastie, T., & Tibshirani, R. (2021). An introduction to statistical learning with applications in R (2.ª ed.). Springer.
Jolliffe, I. T., & Cadima, J. (2016). Principal component analysis: A review and recent developments. Philosophical Transactions of the Royal Society A, 374(2065), 20150202.
Josse, J., & Husson, F. (2016). missMDA: A package for handling missing values in multivariate data analysis. Journal of Statistical Software, 70(1), 1–31.
Kaiser, H. F. (1960). The application of electronic computers to factor analysis. Educational and Psychological Measurement, 20(1), 141–151.
Kassambara, A., & Mundt, F. (2020). factoextra: Extract and visualize the results of multivariate data analyses (Versión 1.0.7) [Paquete de R]. https://CRAN.R-project.org/package=factoextra
Lê, S., Josse, J., & Husson, F. (2008). FactoMineR: An R package for multivariate analysis. Journal of Statistical Software, 25(1), 1–18.
Preston, S. H. (1975). The changing relation between mortality and level of economic development. Population Studies, 29(2), 231–248.
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.
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.
Wickham, H., Averick, M., Bryan, J., Chang, W., McGowan, L. D., François, R., Grolemund, G., Hayes, A., Henry, L., Hester, J., Kuhn, M., Pedersen, T. L., Miller, E., Bache, S. M., Müller, K., Ooms, J., Robinson, D., Seidel, D. P., Spinu, V., … Yutani, H. (2019). Welcome to the tidyverse. Journal of Open Source Software, 4(43), 1686.