Introducción
Esta entrada continúa la exploración y análisis de la base de datos de la ENES-PISAC. El objetivo es similar al de la primera entrada, desarrollar y documentar estrategias para explorar y analizar esta base de datos con software libre. Como muchas otras encuestas la mayoría de las variables registradas en la ENES son categóricas, nominales u ordinales. En este caso me centro en algunos pares de problemas-soluciones para el manejo, exploración y modelado de variables ordinales.
Recodificación de variables categóricas ordinales
Los objetos de las clases factor
y ordered
se utilizan en R
para manejar variables categóricas nominales y ordinales respectivamente. Se trata de objetos cuyo tratamiento es complicado por la suma de atributos que los componen (niveles, etiquetas, orden) y son una fuente de dolores de cabeza para usuarios principiantes y avanzados. Las funciones de la librería forcats::
, incluída en tidyverse
reducen algunos de estos problemas. Aquí presentamos una estrategia basada en la creación de una lista relacional de valores viejos y nuevos. Moviendo la definición de las relaciones de recodificación a una estructura de datos generamos un código más simple y fácil de mantener.1
Cálculo de coeficientes de correlación para variables ordinales
Nada impide que analicemos la relación entre dos variables ordinales usando tablas de contingencia, sin embargo al hacerlo desperdiciamos un nivel adicional de información: el orden. Las buenas prácticas impiden tajantemente, en cambio, que analicemos esa misma relación calculando el coeficiente de correlación producto-momento entre los códigos numéricos de las variables. Como alternativa se utiliza el coeficiente de correlación policórica.
Visualización de datos ordinales
Una vez más, nada impide que utilicemos gráficos de barras apiladas de proporciones o gráficos de mosaico para analizar variables ordinales. Sin embargo podemos aprovechar el orden de la variable y presentar esas proporciones de manera acumulada.
Ajuste de modelos con \(y\) ordinal
Algunas preguntas pueden responderse con 2 variables y en ese caso nada mejor que gráficos o estadísticos simples. Cuando el número de variables aumenta debemos recurrir a herramientas más sofisticadas como los modelos lineales. Ubicando una variable dependiente ordinal en el modelo simplifica mucho la interpretación de los coeficientes en comparación a un modelo logit multinomial para \(y\) nominal.
library(haven)
library(tidyverse)
library(coefplot)
hogares <- read_sav("../../static/docs/datos/ENES_Hogares_version_final.sav")
#base de personas
personas <- read_sav("../../static/docs/datos/ENES_Personas_version_final.sav")
unida <- left_join(personas, hogares, by = c("nocues", "nhog")) %>%
rename(fcalib_hog = f_calib3.y,
fcalib_per = f_calib3.x)
diccionario_hogares <- tibble(nombre = names(hogares),
etiqueta = map_chr(hogares, attr, "label"))
diccionario_personas <- tibble(nombre = names(personas),
etiqueta = map_chr(personas, attr, "label"))
diccionario <- bind_rows(diccionario_hogares, diccionario_personas)
renombrar <- function(x) {
rename_all(x, ~diccionario$etiqueta[match(., diccionario$nombre)])
}
Recodificar variables ordinales
En la entrada anterior nos preguntamos si el nivel educativo de las personas podría ser una variable confusora de la relación entre el conocimiento de idioma extranjero y la región en la que vive una persona. Al final de esta entrada ajustaremos un modelo lineal ordinal para buscar una respuesta a esa pregunta. Los modelos lineales, especialmente los máxima verosimilitud como el que ajustaremos más adelante, tienen problemas cuando los predictores categóricos tienen muchos niveles o categorías. Esto es un problema técnico con muchas aristas, incluyendo problemas de sobreajuste, errores excesivamente amplios cuando para predictores categóricos con poca información y dificultad en la convergencia en modelos de máxima verosimilitud cuando no hay información.2
Por este u otros motivos recodificar variables categóricas reduciendo el número de categorías es una operación frecuente y combiene desarrollar un método y un flujo de trabajo para hacerlo de manera efectiva. Antes de enfocarnos en los pasos técnicos vale una reflexión sobre las implicaciones metodológicas de este proceso.
Uno de los rituales de paso de un aprendíz de brujo/modelador lineal es tomar cualquier hipótesis aplicable a unos datos y manipular esos datos hasta que todas las pendientes y términos de error queden como nos gustan.3 Más allá de algunas trampillas en el camino se aprende que cualquier decisión sobre los datos –especialmente las decisiones sobre recodificación- tiene mucho impacto en los resultados del análisis y que un análisis honesto requiere fundamentar y documentar las decisiones que tomamos sobre los datos.4 Fundamentar, en este contexto, significa ponerlo en relación con una teoría y con la práctica establecida –el estado del arte- del campo de investigación. Documentar significa que el proceso técnico que se aplicó esté explicitado sea reproducible por nosotros o por otras personas. Incluir el código que nos nos lleva de los datos crudos a los datos finales es la mejor práctica: el código que hace el proceso documenta al mismo tiempo al proceso.
Esto es lo complicado, lo simple es crear una lista de vectores nombrados que se evalúa como argumentos a ser evaluados por una función.
Niveles educativos por región (datos originales)
personas %>%
select(edad = v108, sexo = v109, region, t_aglo, v118a, nivel_ed, f_calib3) %>%
filter(edad >= 30) %>%
mutate_at(vars(-edad, -f_calib3), as_factor) %>%
mutate(region = str_remove(region, " \\(([^\\)]+)\\)"),
region = fct_recode(region, "GBA" = "Gran Buenos Aires")) %>%
count(region, nivel_ed, wt = f_calib3) %>%
group_by(region) %>%
mutate(prop = n / sum (n),
prop = scales::percent(prop)) %>%
select(-n) %>%
ungroup() %>%
spread(region, prop, fill = 0) %>%
knitr::kable(caption = "Porcentaje de nivel educativo por región para personas de 30 o más años")
nivel_ed | Centro | Cuyo | GBA | NEA | NOA | Pampeana | Patagonia |
---|---|---|---|---|---|---|---|
Sin instrucción (incluye nunca asistió o sólo asistió a sala de 5) | 1.6% | 2.4% | 2.3% | 3.6% | 3.1% | 2.1% | 2.7% |
Primaria/EGB incompleto | 11.5% | 12.0% | 7.7% | 21.5% | 14.0% | 8.1% | 12.5% |
Primaria/EGB completo | 24.8% | 26.2% | 23.3% | 22.8% | 20.4% | 27.2% | 18.9% |
Secundario/Polimodal incompleto | 12.3% | 13.7% | 11.4% | 12.5% | 14.9% | 12.8% | 16.8% |
Secundario/Polimodal completo | 23.5% | 20.6% | 25.5% | 17.5% | 22.3% | 23.6% | 23.6% |
Terciario incompleto | 2.5% | 2.6% | 3.2% | 2.2% | 3.5% | 1.7% | 3.5% |
Terciario completo | 8.7% | 7.2% | 9.5% | 7.5% | 9.3% | 9.7% | 10.4% |
Universitario incompleto | 5.7% | 5.7% | 5.3% | 5.1% | 5.8% | 5.0% | 3.8% |
Universitario completo | 9.1% | 9.4% | 11.7% | 7.2% | 6.7% | 9.4% | 7.5% |
Educación especial | 0.3% | 0.3% | 0.2% | 0.1% | 0.1% | 0.3% | 0.2% |
NS/NR | 0 | 0 | 0 | 0 | 0 | 0.0% | 0 |
La categoría “NS/NR” es residual, su frecuencia es prácticamente 0 en todos los casos. La categoría “Educación especial” es una categoría diferente de nivel educativo diferente a las demás y no podrías considerarla un nivel más alto que Universitario Completo, como implícitamente está en la base. Para ajustar un modelo ordinal eliminamos ambas categorías.
Más allá de estas cuestiones de tipo más técnico, el patrón no es completamente claro. La región “GBA” tiene el porcentaje más alto de educación universitaria completa, pero un porcentaje relativamente bajo de universitaria incompleta. Patagonia la más alta de Terciario completa, pero niveles de población Universitaria completa similares a los del NEA. Parte del problema tiene que ver con el gran número de categorías educativas y con la presencia de grados incompletos. Si bien son adecuados en la escala ordinal y contar con más información es mejor que contar con menos información en este caso complican la comprensión. Tomaré una decisión importante: recodificar los niveles educativos compactando las categorías incompletas a la inmediatamente anterior. Este decisión se funda en la noción de que el logro del título tiene implicaciones sociológicas que ván más allá de la educación que se obtuvo en el proceso y que opera como un capital educativo objetivado.5
Recodificación de las variables Nivel educativo e Idioma extranjero
Para recodificar la variable nivel_ed
utilizaremos la función fct_collapse
, de la librería forcats::
. Esta función tiene como primer argumento un objeto de la clase factor
(en este caso la columna nivel_ed
del data frame) y luego, separados por comas, una definición de los nuevas categorías y un vector definido con c()
de las categorías originales que se colapsarán a la nueva. Es posible definir estas relaciones en la llamada a la función, pero el código se hace complicado. Es mejor práctica especificar esta definición en una lista y luego “abrirla” como código a evaluar en la llamada a la función. Para eso utilizaremos el operador !!!
6 en la llamada a fct_collapse
.
A esa lista la llamaremos reco_niveled
y tiene una estructura simple: cada elemento de la lista es un vector de la clase character
con un nombre dentro de la lista. El nombre del vector será la nueva categoría y los elementos del vector las viejas categorías que la compondrán. Es fundamental observar las convenciones sintácticas de R
y ubicar correctamente las comas y las comillas. Es buena práctica insertar saltos de línea en cada definición e identar el código para facilitar la legibilidad.
reco_niveled <- list(
ninguno = c("Sin instrucción (incluye nunca asistió o sólo asistió a sala de 5)",
"Primaria/EGB incompleto"),
primaria = c("Primaria/EGB completo", "Secundario/Polimodal incompleto"),
secundaria = c("Secundario/Polimodal completo", "Terciario incompleto"),
terciario = c("Terciario completo", "Universitario incompleto"),
universitario = "Universitario completo",
otro = c("Educación especial", "NS/NR"))
reco_niveled
## $ninguno
## [1] "Sin instrucción (incluye nunca asistió o sólo asistió a sala de 5)"
## [2] "Primaria/EGB incompleto"
##
## $primaria
## [1] "Primaria/EGB completo" "Secundario/Polimodal incompleto"
##
## $secundaria
## [1] "Secundario/Polimodal completo" "Terciario incompleto"
##
## $terciario
## [1] "Terciario completo" "Universitario incompleto"
##
## $universitario
## [1] "Universitario completo"
##
## $otro
## [1] "Educación especial" "NS/NR"
Una de las ventajas de definir a las relaciones entre categorías como una estructura de datos en la sesión de R
es que la podemos manejar como cualquier otro objeto. Por ejemplo, crear una tabla bien formateada que relaciona categorías nuevas y viejas. Como la tabla se genera a partir de la misma estructura de datos no es necesario actualizar la tabla si hacemos alguna modificación a la definición de recodificación. Así es R
, lo simple es tedioso y lo tedioso simple.
reco_niveled %>%
map_df(~`length<-`(.x, 2)) %>% #Porque los largos son desiguales y falla la conversión a df
gather("Código nuevo", "Códigos originales") %>%
drop_na() %>% #Porque antes los alargué a 2
knitr::kable(caption = "Recodificación de la variable Nivel Educativo")
Código nuevo | Códigos originales |
---|---|
ninguno | Sin instrucción (incluye nunca asistió o sólo asistió a sala de 5) |
ninguno | Primaria/EGB incompleto |
primaria | Primaria/EGB completo |
primaria | Secundario/Polimodal incompleto |
secundaria | Secundario/Polimodal completo |
secundaria | Terciario incompleto |
terciario | Terciario completo |
terciario | Universitario incompleto |
universitario | Universitario completo |
otro | Educación especial |
otro | NS/NR |
Niveles educativos por región (recodificado)
personas %>%
select(v118a, region, f_calib3, t_aglo, v108, v109, nivel_ed, aglo) %>%
filter(v108 >= 30) %>%
# En algún momento tendré que tomar una decisión más responsable sobre el manejo de missing.
drop_na %>%
mutate_at(vars(-v108, - f_calib3), as_factor) %>%
# Aplicar la lista de recodificaciones: lista complicada, código simple
mutate(nivel_ed = fct_collapse(nivel_ed, !!!reco_niveled)) %>%
#Eliminar el texto entre paréntesis de las etiquetas de región
mutate(region = fct_relabel(region, str_remove, " \\(([^\\)]+)\\)")) %>%
filter(nivel_ed != "otro",
v109 != "Otro") %>%
mutate(v118a = ordered(v118a)) %>%
droplevels() -> activo2
Visualización de datos ordinales
Con los datos recodificados es necesario encontrar una forma de explorarlos o comunicarlos. La más simples es una tabla.
# Computamos las frecuencias
activo2 %>%
select(region, nivel_ed, f_calib3) %>%
count(nivel_ed, region, wt = f_calib3) %>%
group_by(region) %>%
mutate(prop = n / sum(n)) -> region_por_niveled
region_por_niveled %>%
select(-n) %>%
mutate(prop = scales::percent(prop)) %>%
spread(nivel_ed, prop) %>%
knitr::kable(caption = "Proporción de población de 30 o más años por nivel educativo por región")
region | ninguno | primaria | secundaria | terciario | universitario |
---|---|---|---|---|---|
Gran Buenos Aires | 10.1% | 34.7% | 28.7% | 14.9% | 11.7% |
Cuyo | 14.5% | 40.0% | 23.3% | 12.9% | 9.4% |
Pampeana | 10.2% | 40.2% | 25.4% | 14.8% | 9.4% |
Centro | 13.2% | 37.2% | 26.1% | 14.4% | 9.1% |
NEA | 25.2% | 35.3% | 19.7% | 12.6% | 7.2% |
NOA | 17.1% | 35.3% | 25.8% | 15.0% | 6.7% |
Patagonia | 15.2% | 35.8% | 27.2% | 14.3% | 7.5% |
Una forma convencional de presentar datos de una tabla cruzada es un gráfico de barras apiladas, como los que utilizamos en la entrada anterior. Pero al tratarse de datos ordinales podríamos aprovechar el orden de las categorías de nivel educativo en el gráfico y, en lugar de barras apiladas, utilizar líneas que unan a las proporciones acumuladas. Esto simplifica la interpretación del gráfico y la comparación. Todas las líneas convergen en el mismo punto: el 100% de la población. Sin embargo las pendientes son diferentes: Gran Buenos Aires está debajo de todas las demás en las categorías educativas más bajas. NEA es el caso opuesto.
region_por_niveled %>%
mutate(acum = cumsum(prop)) %>%
ggplot(aes(x = nivel_ed, y= acum, color = region, group = region)) +
geom_point() +
geom_line() +
theme_minimal() +
theme(legend.position = c(0.8, 0.4)) +
scale_y_continuous(labels = scales::percent) +
expand_limits(y = 0) +
labs(color = NULL,
y = "Acumulado",
x = "Nivel Educativo",
title = "Proporción de población por nivel educativo por regiones")
Correlación policórica entre nivel educativo y conocimiento de idioma
Las variables Nivel Educativo y Conocimiento de idioma extranjero están medidas en un nivel ordinal. En este caso podemos explorar la relación entre una y otra usando el coeficiente de correlación policórica. Cuando usamos este coeficiente estamos asumiendo implícitamente que a la variables ordinales medidas les subyace una variable latente continua y que la registramos como ordinal por problemas de medición. Las categorías de variable ordinal medida funcionan como puntos de corte en esta variable subyacente continua.
activo2 %>%
select(nivel_ed, v118a) %>%
mutate_all(as.ordered) %>%
polycor::hetcor()
##
## Two-Step Estimates
##
## Correlations/Type of Correlation:
## nivel_ed v118a
## nivel_ed 1 Polychoric
## v118a 0.5401 1
##
## Standard Errors:
## nivel_ed v118a
## 0.008233
## Levels: 0.008233
##
## n = 13872
##
## P-values for Tests of Bivariate Normality:
## nivel_ed v118a
## 1.346e-59
## Levels: 1.346e-59
El coeficiente es bastante alto (0.54). Podemos afirmar que hay una correlación positiva entre el nivel educativo y el conocimiento de idioma extranjero.
Modelo lineal
Confusoras
En el análisis bivariado podemos rechazar -informalmente- la hipótesis de independencia estadística entre los tres pares de variables. Hay una relación entre el idioma extranjero y la región en la que se vive, pero a su vez esta variable está relacionada con el nivel educativo alcanzado. Como el nivel educativo no se distribuye de manera homogénea en las regiones cabe la posibilidad de que sea esa distribución desigual del nivel educativo la que nos haga ver, quizás erróneamente, que hay una relación entre idioma extranjero y región, cuando en realidad la relación se da entre nivel educativo e idioma extranjero.
Sería fantástico poder cuantificar la relación entre región y conocimiento de idioma extranjero en unas condiciones ideales en las que el nivel educativo está fijo. Sería fantástico, pero no lo es. Es una posibilidad muy real, es exactamente lo que podemos hacer con un modelo lineal multivariado.
Modelos logit ordinales
El Modelo 1 (en el script mordi1
) es un modelo de razones de probabilidad proporcionales con función de enlace canónica, mejor conocido como logit ordinal. La variable dependiente será el nivel educativo medido ordinalmente con las categorías y el orden definidos en la Tabla 2. Aunque la variable define categorías el modelo se ajusta con respecto a una variable latente continua7.
Las variables independientes o predictores serán la región (categoría de referencia: Gran Buenos) y nivel educativo (categoría de referencia: Ninguna o Primaria incompleta).
Tanto los coeficientes de cada categoría de los predictores como la propia variable dependiente se calculan como logaritmos de razón de probabilidad, un tipo de magnitud bastante complicado de entender.8 Para mantener las cosas simples analizaremos tres características de los coeficientes del modelo:
El signo. Coeficientes con signo positivo indican que el predictor9 está asociado a un incremento del conocimiento de un idioma extranjero y coeficientes de signo negativo a menor nivel educativo. Esto nos ahorro el problema de interpretar al modelo como lo haríamos con un logit multinomial propiamente dicho, como un sistema de diferencias con respecto a una categoría de referencia.
La magnitud. Coeficientes más grandes indican mayores diferencias.
Relacionado con lo anterior, la cercanía de un coeficiente a 0 proporcional a su error estándar. Si el coeficiente es pequeño o el error es muy grande no podríamos rechazar la hipótesis de que el coeficiente poblacional o paramétrico es cero. Consideraremos entonces que ese coeficiente es efectivamente 0 y que no hay asociación entre el predictor y el resultado, es decir, que hay independencia.
Esto parece ser innecesariamente complicado y tal vez lo sea.10 La ventaja es que esos coeficientes se calculan en un nivel fijo de las demás variables. Si toda la asociación entre conocimiento de idioma extranjero y región se explicara por las diferencias educativas entre regiones podríamos esperar que los coeficientes de región se redujeran o su error se incrementara, de tal modo que los interpretaríamos como si fueran 0. En este caso aceptaríamos la hipótesis del nivel educativo como confusora.
Modelos 1 y 2
El Modelo 1 incluye solamente a la región como predictor. La exploración de los datos nos lleva a pensar que, comparadas con GBA, todas las demás regiones están asociadas con un menor nivel de conocimiento de idioma extranjero. Podemos esperar pendiente negativas en todos los casos.
El Modelo 2 incluye, además de la región, al nivel educativo. Podemos esperar que haya una coeficientes positivos y crecientes en magnitud a medida que sube la categoría de nivel educativo. La pregunta es ¿qué pasará con los coeficientes de región? ¿seguirán siendo negativos y diferentes a 0? Si este fuera el caso podríamos afirmar que la región tiene una asociación con el nivel de conocimiento de idioma extranjero que es independiente de la asociación entre región y nivel educativo.
MASS::polr(v118a~region,
data = activo2,
Hess = TRUE) -> mordi1
MASS::polr(v118a~nivel_ed+region,
data = activo2,
Hess = TRUE) -> mordi2
términos <- unique(names(c(mordi1$coefficients, mordi2$coefficients)))
multiplot(mordi1, mordi2, innerCI = 2, names = c("1 Región", "2 Región +\n Nivel\nEducativo"), coefficients = términos) +
theme_minimal() +
labs(x = "Coeficiente estimado + error",
y = "Predictor",
title = "Comparación de modelos sin y con nivel educativo",
caption = "Intervalo de confianza a 2 desviaciones estándar",
colour = "Modelo") +
theme(legend.position = c(0.85, 0.3),
legend.background = element_rect(colour = "white")) +
scale_colour_viridis_d(direction = -1)

Figure 1: Modelos de posibilidades proporcionales de conocimiento de idioma extranjero
La incorporación de la variable educación modera las pendientes de las regiones, sin embargo todas mantienen el signo y en ningún caso sería razonable tratarlas como si efectivamente fueran 0. Hay algo más que las diferencias educativas entre regiones que está asociado al nivel de conocimiento de idiomas extranjeros.
Modelo 3
En este último modelo agregamos un tercer predictor: el tamaño del aglomerado. Esto nos permite, en alguno de los casos, fijar también a esa variable. La lógica es similar al paso que dimos entre el modelo 1 y el 2, solo que en este caso buscamos controlar por las diferencias en el tamaño de los aglomerados
Exploración del modelo 3
MASS::polr(v118a~region+t_aglo+nivel_ed+v108+v109,
data = activo2,
Hess = TRUE) -> mordi3
coefplot(mordi3,
innerCI = 2,
coefficients = names(mordi3$coefficients)) +
theme_minimal() +
labs(x = "Coeficiente estimado + error",
y = "Predictor",
title = "Modelo 3",
caption = "Intervalo de confianza a 2 desviaciones estándar") +
scale_colour_manual(values = "black")
Con el ajuste por el tamaño del aglomerado la región pampena deja de tener una diferencia significativa con Gran Buenos Aires. En el caso de NEA el resultado es similar, aunque tiene una pendiente negativa importante el error de estimación es suficientemente grande como para que consisderemos que bien podría ser 0. El sexo y la edad no hay nada destacable para reportar: el conocimiento de idioma extranjero es linealmente independiente de estas variables. El tamaño del aglomerado sí es importante y a mayor tamaño, mayor nivel de idiomas extranjeros.
Y observamos la 9 Regla de la filosofía de UNIX: Fold knowledge into data so program logic can be stupid and robust.↩
Esto es un problema técnico bastante complicado que por ahora dejaremos de lado afirmando que nos gustan las tablas con muchos quesos y los modelos con pocos parámetros. Una versión mucho más profunda de este principio se detalla en el libro Regression Modeling Strategies de Frank Harrel.↩
Es muy buena idea hacerlo: se aprende mucho sobre las tripas de un modelo linea y sobre la enorme influéncia que tienen nuestras decisiones sobre los datos en los resultados del análisis. De este modo se desarrolla el olfato para detectar estas manipulaciones en los trabajos que leemos. Lo que está mal es publicar esos resultados.↩
Esto no implica que utilizar los datos “como vienen” nos deje a salvo de reflexionar sobre las decisiones que se tomaron sobre los datos. En ese caso en lugar de nuestras decisiones estamos asumiendo las decisiones que tomaron quienes diseñaron y aplicaron el instrumento que produjo los datos.↩
Se aceptan discrepancias.↩
Que se lee bang bang bang según su autor.↩
Similar al caso de la correlación policórica↩
Dicho de otro modo, no simple expresar cuanto cambia \(y\) por cada cambio en las unidades de \(X\).↩
O categoría de la variable independiente.↩
Definitivamente lo es, un modelo log-lineal sería suficiente para la hipótesis según la cuál el nivel educativo es una confusora de la dependencia entre región y conocimiento de idioma. Sin embargo estos modelos tampoco son simples en su interpretación y no ofrecen coeficientes que cuantifiquen la intensidad de la asociación entre un predictor y un resultado.↩