Esta publicación tiene dos objetivos, uno personal y otro general. El objetivo personal es tener en limpio y siempre disponible un script de consulta para el uso del paquete {Rilostat} de la Organización Internacional del Trabajo (OIT) utilizando R. El segundo objetivo, el general, es simplemente compartirlo a todo aquel que guste.
No asumo que mi código sea el más eficiente o esté libre de errores, así que cualquier corrección, sugerencia o comentario házmelo saber por este medio o a través de mi twitter ecodiegoale.
Además, nunca está de más revisar stackoverflow, github, rpubs o preguntarle a ChatGPT.
Aviso: Si algún link no abre con click izquierdo, trata
con el derecho y abrir en otra ventana o pestaña.
En este blog usaré el paquete {Rilostat} de la
Organización Internacional del Trabajo (OIT) de una
manera muy sencilla utilizando principalmente la librería de
tidyverse. No me detendré a explicar mucho sobre la
manipulación de datos con esta librería, de esto tengo varias entradas,
además que puedes hacer uso de herramientas de Inteligencia Artificial
para que te ayude con eso. Lo importante en este blog será el uso del
paquete {Rilostat}, la cual proporciona una interfaz
sencilla para acceder a la base de datos de la OIT, conocida como
ILOSTAT. Esta es el mayor repositorio mundial de estadísticas
del mercado laboral. Cubre todos los países y regiones, así como una
amplia gama de temas relacionados con el trabajo, incluidos empleo,
desempleo, salarios, tiempo de trabajo y productividad laboral, entre
otros. Contiene series temporales que se remontan hasta 1938;
estadísticas laborales anuales, trimestrales y mensuales; estimaciones a
nivel nacional, regional y mundial; e incluso proyecciones de los
principales indicadores del mercado laboral.
En esta entrada no solo mostraré cómo usar {Rilostat} para la descarga y manipulación de estos indicadores para diversos países y años en un solo paso, también mostraré cómo hacerlo con un enfoque tidy, además de usar funciones básicas pero muy importantes como left_join() y otras relacionadas con expresiones regulares (regex).
Además, calcularemos el índice de Kaitz, que se define como la relación entre el salario mínimo y el salario medio. Este índice es relevante porque mide cuánto “muerde” el salario mínimo al salario promedio de la economía. Es una referencia común en análisis laborales, ya que permite evaluar el poder adquisitivo relativo del salario mínimo, su potencial para reducir desigualdades y su impacto en la competitividad y el empleo.
Lo primero será definir tu directorio de trabajo y cargar las librerías necesarias para el análisis.
#setwd("C:/Users/tu carpeta")
rm(list = ls()) #para borrar todos los objetos en la memoria de la consola
options(scipen=999) #para desactivar la notación científica
# install.packages("Rilostat")
# if(!require(devtools)){install.packages('devtools')}
# install_github("ilostat/Rilostat")
library(tidyverse)
library(Rilostat)Conforme avancemos en el blog haré mención de cuando estemos usando las librerías cargadas.
Como mencioné en la introducción, la base de datos de ILOSTAT contiene cientos de indicadores (1,701) para distintos países, regiones y periodos (en algunos casos con series que arrancan desde 1938). Revisar la documentación de {Rilostat} es fundamental —y altamente recomendable— porque ahí verás las distintas maneras de buscar y filtrar indicadores según tu objetivo o pregunta de investigación. Hacerlo te puede ahorrar muchas horas de prueba y error.
Sin importar el método que elijas, siempre vas a necesitar el código del indicador. A partir de ahí, el resto del flujo de trabajo en R es mucho más sencillo.
El paquete {Rilostat} incluye las siguientes funciones:
## [1] "clean_ilostat_cache" "dataexplorer" "distribution_ilostat"
## [4] "get_ilostat" "get_ilostat_dic" "get_ilostat_toc"
## [7] "label_ilostat"
Pero para mantener este post sencillo y enfocado en el objetivo, solo usaremos tres:
get_ilostat() → Descarga directamente los datos de
ILOSTAT usando el código del indicador y, opcionalmente,
filtros por país, periodo, frecuencia, etc. Es la puerta de entrada a
las series que nos interesan.
get_ilostat_dic() → Devuelve los diccionarios
asociados a un indicador, es decir, las etiquetas y descripciones de las
variables, para que no trabajemos solo con códigos crípticos.
get_ilostat_toc() → Obtiene la “tabla de contenidos”
de ILOSTAT: una lista completa de indicadores disponibles, con
sus códigos, nombres, temas y coberturas. Es ideal para buscar qué serie
usar antes de descargarla.
Como dije anteriormente, para conocer qué indicadores tenemos
disponibles en ILOSTAT utilizaremos la función
get_ilostat_toc(), que nos devuelve una tabla con el
table of contents (TOC) de todos los indicadores, incluyendo
información como:
id: el identificador único del indicador (lo usaremos para descargar los datos).
indicator.label: el nombre descriptivo del indicador.
freq y freq.label: frecuencia de los datos (anual, trimestral, mensual).
data.start y data.end: años de inicio y fin de la serie.
subject.label: el tema al que pertenece (empleo, salarios, productividad, etc.).
## Rows: 1,701
## Columns: 15
## $ id <chr> "SDG_0111_SEX_AGE_RT_A", "SDG_0131_SEX_SOC_RT_A", "SD…
## $ indicator <chr> "SDG_0111_SEX_AGE_RT", "SDG_0131_SEX_SOC_RT", "SDG_05…
## $ indicator.label <chr> "SDG indicator 1.1.1 - Working poverty rate (percenta…
## $ freq <chr> "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A"…
## $ freq.label <chr> "Annual", "Annual", "Annual", "Annual", "Annual", "An…
## $ data.start <dbl> 2000, 1996, 2000, 2000, 2000, 2000, 2000, 2000, 2000,…
## $ data.end <dbl> 2024, 2025, 2025, 2025, 2025, 2026, 2025, 2026, 2024,…
## $ last.update <chr> "23/05/2025 07:08:47", "02/06/2025 13:54:30", "08/08/…
## $ n.records <dbl> 38472, 36164, 1703, 4544, 7162, 59513, 43093, 45696, …
## $ collection <chr> "SDG", "SDG", "SDG", "SDG", "SDG", "SDG", "SDG", "SDG…
## $ collection.label <chr> "SDG labour market indicators (ILOSDG)", "SDG labour …
## $ subject <chr> "POV", "SOC", "EMP", "EMP", "LPY", "IFL", "EAR", "UNE…
## $ subject.label <chr> "Working poverty", "Social protection", "Employment",…
## $ database <chr> "ILOSDG", "ILOSDG", "ILOSDG", "ILOSDG", "ILOSDG", "IL…
## $ database.label <chr> "SDG Labour Market Indicators (ILOSDG)", "SDG Labour …
El argumento search de get_ilostat_toc() en
Rilostat no soporta expresiones regulares directamente,
porque internamente solo filtra con grepl(…, ignore.case =
TRUE) sobre la descripción, así que acepta una sola cadena de
búsqueda. Esto será importante y claro más adelante.
En el paso anterior vimos cómo obtener el listado completo de indicadores, pero {Rilostat} también nos permite consultar el table of contents de otros elementos que no son indicadores, como las regiones o países disponibles en la base de datos.
Aquí estamos solicitando a ILOSTAT que nos devuelva la lista de áreas de referencia (ref_area), es decir, todos los países, regiones y agrupaciones geográficas que reconoce la base de datos.
segment = "ref_area" indica que queremos el catálogo
de países/regiones, no el de indicadores.
lang = "es" especifica que la información (nombres y
descripciones) se descargue en español.
El objetivo de hacer esto es trabajar con nombres y códigos oficiales de países tal y como los maneja la OIT, evitando tener que traducir del inglés los nombres o reetiquetar manualmente las etiquetas. Esto es muy útil para prevenir errores al momento de descargar datos: en lugar de escribir manualmente “México” o “Argentina” y arriesgarnos a un error de codificación o traducción, podemos filtrar directamente por el código ISO que nos devuelve este catálogo.
La documentación oficial indica que es posible buscar palabras o expresiones dentro del índice que lista todos los indicadores de ILOSTAT.
Por ejemplo, buscar la palabra “minimum” devolverá todos los indicadores que contengan ese término en alguna parte de su descripción:
## # A tibble: 1 × 15
## id indicator indicator.label freq freq.label data.start data.end
## <chr> <chr> <chr> <chr> <chr> <dbl> <dbl>
## 1 EAR_4MMN_CUR_N… EAR_4MMN… Statutory nomi… A Annual 1980 2025
## # ℹ 8 more variables: last.update <chr>, n.records <dbl>, collection <chr>,
## # collection.label <chr>, subject <chr>, subject.label <chr>, database <chr>,
## # database.label <chr>
En nuestro caso, realizaremos esta búsqueda para identificar los indicadores necesarios para calcular el índice de Kaitz (salario mínimo / salario medio).
Como aclaré con anterioridad, la función
get_ilostat_toc() incluye un argumento search para
buscar términos dentro del catálogo de indicadores, este parámetro no
soporta expresiones regulares (regex) complejas, ya que
internamente solo realiza un filtro simple con grepl()
para una sola cadena, sin posibilidad de combinar varios términos o
patrones avanzados.
Por eso, en lugar de usar directamente search = “minimum|average”, yo opto por descargar el catálogo completo y luego aplicar un filtro personalizado usando stringr::str_detect() con una expresión regular que permite buscar varios términos a la vez:
toc <- get_ilostat_toc() %>%
filter(
str_detect(indicator.label,
regex("minimum|average",
ignore_case = TRUE)
)
)Este enfoque permite buscar múltiples palabras clave en un solo paso (aquí “minimum” o “average”).
Usa toda la potencia de las expresiones regulares para hacer búsquedas más flexibles y precisas.
Evita limitaciones del argumento search que solo acepta una cadena literal.
Así, obtenemos una lista más completa y relevante de indicadores para nuestro análisis, mejorando la eficiencia y precisión en la selección de datos para calcular el índice de Kaitz.
## # A tibble: 10 × 15
## id indicator indicator.label freq freq.label data.start data.end
## <chr> <chr> <chr> <chr> <chr> <dbl> <dbl>
## 1 SDG_0851_SEX_… SDG_0851… SDG indicator … A Annual 2000 2025
## 2 EAR_4HRL_SEX_… EAR_4HRL… Average hourly… A Annual 1969 2025
## 3 EAR_4HRL_SEX_… EAR_4HRL… Average hourly… A Annual 1990 2025
## 4 EAR_4HRL_SEX_… EAR_4HRL… Average hourly… A Annual 1991 2025
## 5 EAR_4HRL_SEX_… EAR_4HRL… Average hourly… A Annual 1990 2025
## 6 EAR_4HRL_SEX_… EAR_4HRL… Average hourly… A Annual 1990 2025
## 7 EAR_4HRL_SEX_… EAR_4HRL… Average hourly… A Annual 1997 2024
## 8 EAR_4HRL_SEX_… EAR_4HRL… Average hourly… A Annual 1990 2025
## 9 EAR_4HRL_SEX_… EAR_4HRL… Average hourly… A Annual 1991 2025
## 10 EAR_4MTH_SEX_… EAR_4MTH… Average monthl… A Annual 1969 2025
## # ℹ 8 more variables: last.update <chr>, n.records <dbl>, collection <chr>,
## # collection.label <chr>, subject <chr>, subject.label <chr>, database <chr>,
## # database.label <chr>
Con base en lo anterior, ahora sabemos que los códigos que necesitamos son:
EAR_4MTH_SEX_AGE_CUR_NB_A: Average monthly earnings of employees by sex and age
EAR_4MMN_CUR_NB_A: Statutory nominal gross monthly minimum wage
Para los datos de salarios promedio mensuales:
## Rows: 109,142
## Columns: 12
## $ ref_area <chr> "AFG", "AFG", "AFG", "AFG", "AFG", "AFG", "AFG", "AFG",…
## $ source <chr> "BA:15715", "BA:15715", "BA:15715", "BA:15715", "BA:157…
## $ indicator <chr> "EAR_4MTH_SEX_AGE_CUR_NB", "EAR_4MTH_SEX_AGE_CUR_NB", "…
## $ sex <chr> "SEX_T", "SEX_T", "SEX_T", "SEX_T", "SEX_T", "SEX_T", "…
## $ classif1 <chr> "AGE_YTHADULT_YGE15", "AGE_YTHADULT_YGE15", "AGE_YTHADU…
## $ classif2 <chr> "CUR_TYPE_LCU", "CUR_TYPE_PPP", "CUR_TYPE_USD", "CUR_TY…
## $ time <chr> "2020", "2020", "2020", "2020", "2020", "2020", "2020",…
## $ obs_value <dbl> 13202.245, 836.263, 171.874, 9808.431, 621.290, 127.691…
## $ obs_status <chr> "B", "B", "B", "B", "B", "B", "B", "B", "B", "B", "B", …
## $ note_classif <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
## $ note_indicator <chr> "T30:110_I11:264", "T30:110_I11:264", "T30:110_I11:264"…
## $ note_source <chr> "R1:3513", "R1:3513", "R1:3513", "R1:3513", "R1:3513", …
Para los datos del salario mínimo legal:
## Rows: 11,102
## Columns: 8
## $ ref_area <chr> "AFG", "AFG", "AFG", "AFG", "AFG", "AFG", "AFG", "AFG",…
## $ source <chr> "FX:3344", "FX:3344", "FX:3344", "FX:3344", "FX:3344", …
## $ indicator <chr> "EAR_4MMN_CUR_NB", "EAR_4MMN_CUR_NB", "EAR_4MMN_CUR_NB"…
## $ classif1 <chr> "CUR_TYPE_LCU", "CUR_TYPE_PPP", "CUR_TYPE_LCU", "CUR_TY…
## $ time <chr> "2024", "2024", "2023", "2023", "2022", "2022", "2021",…
## $ obs_value <dbl> 5500.000, 393.193, 5500.000, 356.716, 5500.000, 326.700…
## $ note_indicator <chr> "I19:6291_T30:110", "I19:6291_T30:110", "I19:6291_T30:1…
## $ note_source <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
Cuando descargamos los datos con get_ilostat(), muchas
de las variables categóricas vienen codificadas con abreviaturas o
códigos poco intuitivos, como classif1 o classif2.
Para interpretar correctamente la información y trabajar con etiquetas claras (por ejemplo, nombres completos de países, grupos de edad o categorías de sexo), es fundamental acceder a los diccionarios oficiales que explican qué significa cada código.
Por eso, en esta etapa descargamos esos diccionarios con la función
get_ilostat_dic(), especificando qué catálogo queremos y el
idioma (en este caso, español).
Con esto, podremos luego unir (funciones de la familia join) los datos con sus respectivas etiquetas, facilitando el análisis y la presentación de resultados de forma comprensible y profesional.
La estructura de nuestros datos sobre salario promedio luce así:
## Rows: 109,142
## Columns: 12
## $ ref_area <chr> "AFG", "AFG", "AFG", "AFG", "AFG", "AFG", "AFG", "AFG",…
## $ source <chr> "BA:15715", "BA:15715", "BA:15715", "BA:15715", "BA:157…
## $ indicator <chr> "EAR_4MTH_SEX_AGE_CUR_NB", "EAR_4MTH_SEX_AGE_CUR_NB", "…
## $ sex <chr> "SEX_T", "SEX_T", "SEX_T", "SEX_T", "SEX_T", "SEX_T", "…
## $ classif1 <chr> "AGE_YTHADULT_YGE15", "AGE_YTHADULT_YGE15", "AGE_YTHADU…
## $ classif2 <chr> "CUR_TYPE_LCU", "CUR_TYPE_PPP", "CUR_TYPE_USD", "CUR_TY…
## $ time <chr> "2020", "2020", "2020", "2020", "2020", "2020", "2020",…
## $ obs_value <dbl> 13202.245, 836.263, 171.874, 9808.431, 621.290, 127.691…
## $ obs_status <chr> "B", "B", "B", "B", "B", "B", "B", "B", "B", "B", "B", …
## $ note_classif <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
## $ note_indicator <chr> "T30:110_I11:264", "T30:110_I11:264", "T30:110_I11:264"…
## $ note_source <chr> "R1:3513", "R1:3513", "R1:3513", "R1:3513", "R1:3513", …
En la tabla de datos sobre salarios promedio, la columna classif1 contiene códigos que representan diferentes grupos de edad, pero esos códigos no son fáciles de interpretar directamente.
## [1] "AGE_YTHADULT_YGE15" "AGE_YTHADULT_Y15-24" "AGE_YTHADULT_YGE25"
## [4] "AGE_AGGREGATE_TOTAL" "AGE_AGGREGATE_Y15-24" "AGE_AGGREGATE_Y25-54"
## [7] "AGE_AGGREGATE_Y55-64" "AGE_AGGREGATE_YGE65"
Para entender mejor y poder presentar resultados claros, hacemos un
left_join() con el diccionario classif1
que descargamos previamente, que contiene la descripción completa de
cada código (por ejemplo, qué rango de edad representa cada uno).
Esto nos permite reemplazar o complementar los códigos crípticos con etiquetas legibles y en español, facilitando la interpretación y el análisis posterior de los datos.
En resumen, el uso de este join es fundamental para traducir los códigos internos de la base a un lenguaje comprensible y útil para nuestro análisis.
En la función left_join(), el argumento
by especifica las columnas que se usarán para hacer la
combinación entre ambos data frames.
La sintaxis general es:
El orden importa porque:
La primera columna indicada (“columna_en_x”) pertenece al data frame principal al que aplicamos el join (el que está antes del %>% o el primer argumento).
La segunda columna (“columna_en_y”) es la que viene del data frame que estamos uniendo o anexando.
Yo lo haré así:
Ambos data frames tienen la columna classif1 con el mismo nombre, entonces indicamos que la columna classif1 en data debe emparejarse con la columna classif1 en el data frame classif1.
## [1] "CUR_TYPE_LCU" "CUR_TYPE_PPP" "CUR_TYPE_USD"
Hacemos lo mismo con classif2 que contiene información sobre las unidades monetarias empleadas.
Ahora, para ver los descriptores podemos usar:
## # A tibble: 24 × 4
## classif1 classif2 classif1.label classif2.label
## <chr> <chr> <chr> <chr>
## 1 AGE_YTHADULT_YGE15 CUR_TYPE_LCU Edad (Jóvenes, adultos): 15+ Divisa: Moned…
## 2 AGE_YTHADULT_YGE15 CUR_TYPE_PPP Edad (Jóvenes, adultos): 15+ Divisa: $ PPA…
## 3 AGE_YTHADULT_YGE15 CUR_TYPE_USD Edad (Jóvenes, adultos): 15+ Divisa: Dólar…
## 4 AGE_YTHADULT_Y15-24 CUR_TYPE_LCU Edad (Jóvenes, adultos): 15-… Divisa: Moned…
## 5 AGE_YTHADULT_Y15-24 CUR_TYPE_PPP Edad (Jóvenes, adultos): 15-… Divisa: $ PPA…
## 6 AGE_YTHADULT_Y15-24 CUR_TYPE_USD Edad (Jóvenes, adultos): 15-… Divisa: Dólar…
## 7 AGE_YTHADULT_YGE25 CUR_TYPE_LCU Edad (Jóvenes, adultos): 25+ Divisa: Moned…
## 8 AGE_YTHADULT_YGE25 CUR_TYPE_PPP Edad (Jóvenes, adultos): 25+ Divisa: $ PPA…
## 9 AGE_YTHADULT_YGE25 CUR_TYPE_USD Edad (Jóvenes, adultos): 25+ Divisa: Dólar…
## 10 AGE_AGGREGATE_TOTAL CUR_TYPE_LCU Edad (Tramos agregados): Tot… Divisa: Moned…
## # ℹ 14 more rows
En este paso filtramos los datos para quedarnos sólo con las observaciones que son relevantes para calcular el índice de Kaitz.
data <- data %>%
filter(classif1 == "AGE_AGGREGATE_TOTAL",
sex == "SEX_T",
classif2 == "CUR_TYPE_LCU") Específicamente:
classif1 == "AGE_AGGREGATE_TOTAL": seleccionamos el
grupo de edad agregado total, para no segmentar por edades y así obtener
el salario promedio general.
sex == "SEX_T": tomamos el total por sexo, es decir,
todos los empleados sin distinción de género, para tener un promedio
global.
classif2 == "CUR_TYPE_LCU": elegimos los datos
expresados en la moneda local (Local Currency Unit), para que las cifras
sean consistentes y comparables dentro del mismo país.
Al aplicar estos filtros, nos aseguramos de trabajar con el salario promedio mensual total, sin segmentaciones que compliquen el cálculo y análisis del índice de Kaitz.
Finalmente, tenemos un data frame algo grande en cuanto al número de variables, no todas nos interesan, por lo que con un poco de manejo tidy hacemos:
Así nos queda un data frame más limpio:
## Rows: 1,570
## Columns: 5
## $ ref_area <chr> "AFG", "AFG", "AGO", "AGO", "ARG", "ARG", "ARG", "ARG",…
## $ time <chr> "2020", "2014", "2021", "2019", "2024", "2023", "2022",…
## $ obs_value <dbl> 13202.245, 8848.569, 90678.796, 82234.658, 592570.714, …
## $ classif1.label <chr> "Edad (Tramos agregados): Total", "Edad (Tramos agregad…
## $ classif2.label <chr> "Divisa: Moneda local", "Divisa: Moneda local", "Divisa…
Aquí no me detendré a explicar, ya lo hicimos en la sección anterior, pero cualquier duda puedes copiar y pegar las siguientes líneas de código y pedirle a ChatGPT te las explique puntualmente.
mw <- mw %>%
filter(classif1 == "CUR_TYPE_LCU") %>%
select(ref_area, time, obs_value) %>%
rename(mw = obs_value) %>%
filter(!is.na(mw))Para conformar el data frame final usamos:
data <- data %>%
left_join(mw) %>%
left_join(toc_region %>%
select(
starts_with(c("ref_area",
"ilo_region")
)) %>%
distinct()
)Después de unir los datos de salario promedio con los del salario
mínimo legal usando el primer left_join(), hacemos un
segundo join para incorporar información geográfica y
regional.
En este segundo left_join(), unimos el data frame con
toc_region, que contiene los códigos y etiquetas oficiales de
países (ref_area) y regiones de la OIT (ilo_region).
Seleccionamos solo las columnas que comienzan con “ref_area” e
“ilo_region” para traer las etiquetas relevantes, y usamos
distinct() para eliminar posibles duplicados.
Estos duplicados ocurren porque en el data frame toc_region
los países y regiones se repiten varias veces debido a que las variables
tienen diferentes frecuencias (mensual, trimestral, anual). Por lo
tanto, sin eliminar esos duplicados, el left_join()
generaría múltiples coincidencias para cada país o región, lo que podría
duplicar innecesariamente las filas en el resultado final.
Al usar distinct() evitamos este problema y garantizamos
que cada país y región aparezca sólo una vez para un join
limpio y seguro.
Esta unión nos permite agregar nombres legibles de países y regiones a nuestro conjunto de datos, facilitando el análisis y la visualización posteriores.
El índice de Kaitz es una medida económica que relaciona el salario mínimo legal con el salario medio del mercado laboral. Se calcula como la proporción entre el salario mínimo y el salario promedio en una economía determinada. Este índice es una herramienta clave para analizar la generosidad y el impacto relativo del salario mínimo.
\[ \text{Índice de Kaitz} = \frac{\text{Salario mínimo legal}}{\text{Salario medio}} \]
En nuestro data frame final tenemos:
## Rows: 1,570
## Columns: 9
## $ ref_area <chr> "AFG", "AFG", "AGO", "AGO", "ARG", "ARG", "ARG", "ARG…
## $ time <chr> "2020", "2014", "2021", "2019", "2024", "2023", "2022…
## $ obs_value <dbl> 13202.245, 8848.569, 90678.796, 82234.658, 592570.714…
## $ classif1.label <chr> "Edad (Tramos agregados): Total", "Edad (Tramos agreg…
## $ classif2.label <chr> "Divisa: Moneda local", "Divisa: Moneda local", "Divi…
## $ mw <dbl> 5500.0, 5000.0, 21454.0, 21454.0, 271571.0, 156000.0,…
## $ ref_area.label <chr> "Afganistán", "Afganistán", "Angola", "Angola", "Arge…
## $ ilo_region <chr> "ILO_GEO_X40", "ILO_GEO_X40", "ILO_GEO_X06", "ILO_GEO…
## $ ilo_region.label <chr> "Asia y el Pacífico", "Asia y el Pacífico", "África",…
Donde obs_value es el salario medio y mw el mínimo legal.
Con esto tenemos los índices de Kaitz para diversos países en diversos años, suponiendo que queremos saber qué tanto “mordía” el salario mínimo en el año 2023 filtramos como sigue:
data2023 <- data %>%
filter(time == "2023") %>%
filter(!is.na(mw)) %>%
group_by(ref_area) %>%
filter(!any(kaitz > 1.2)) %>%
ungroup()Después de filtrar los datos para el año 2023 y eliminar observaciones con salario mínimo faltante, aplico un filtro adicional para excluir aquellos países donde el índice de Kaitz supera 1.2. Esto se hace por razones de visualización y porque en esos casos la relación salario mínimo/salario medio no refleja claramente las dinámicas típicas del mercado laboral, pudiendo deberse a particularidades de cada país.
Finalmente, un gráfico con {ggplot2}:
data2023 %>%
ggplot(aes(x = reorder(ref_area.label, kaitz, sum),
y = kaitz,
fill = ilo_region.label)) +
geom_bar(stat = "identity",
color = "black") +
geom_hline(yintercept = 0.35,
linetype = "dashed",
linewidth = 0.8) +
geom_hline(yintercept = 0.65,
linetype = "dashed",
linewidth = 0.8) +
facet_wrap(~ ilo_region.label, scales = "free_y") +
coord_flip() +
scale_fill_brewer(palette = "Dark2") +
labs(
title = "Índice de Kaitz",
subtitle = "2023",
y = "Salario mínimo/Salario promedio",
caption = "La Organización Internacional del Trabajo (OIT) sugiere que el salario mínimo represente entre el 35% y el 65%\ndel salario medio. Las líneas punteadas en el gráfico marcan este rango.\nOIT (Rilostat, 2025) | @ecodiegoale") +
theme_dark() +
theme(legend.position = "none",
axis.text = element_text(color = "black"),
axis.title.y = element_blank(),
plot.title = element_text(hjust = 0.5,
face = "bold"),
plot.subtitle = element_text(hjust = 0.5),
plot.caption = element_text(hjust = 0.5),
text = element_text(family = "Dubai")
)