install.packages("pROC", repos = "https://cloud.r-project.org")
## package 'pROC' successfully unpacked and MD5 sums checked
## Warning: cannot remove prior installation of package 'pROC'
## Warning in file.copy(savedcopy, lib, recursive = TRUE): problema al copiar
## D:\R-4.4.2\library\00LOCK\pROC\libs\x64\pROC.dll a
## D:\R-4.4.2\library\pROC\libs\x64\pROC.dll: Permission denied
## Warning: restored 'pROC'
## 
## The downloaded binary packages are in
##  C:\Users\usuario\AppData\Local\Temp\RtmpKghNMn\downloaded_packages
library(pROC)
## Warning: package 'pROC' was built under R version 4.4.3
## Type 'citation("pROC")' for a citation.
## 
## Adjuntando el paquete: 'pROC'
## The following objects are masked from 'package:stats':
## 
##     cov, smooth, var
packs <- c("pROC","yardstick","caret","janitor","broom","tidyverse","car")
to_install <- setdiff(packs, rownames(installed.packages()))
if(length(to_install)) install.packages(to_install, repos = "https://cloud.r-project.org", dependencies = TRUE)
lapply(packs, library, character.only = TRUE)
## Warning: package 'yardstick' was built under R version 4.4.3
## Warning: package 'caret' was built under R version 4.4.3
## Cargando paquete requerido: ggplot2
## Warning: package 'ggplot2' was built under R version 4.4.3
## Cargando paquete requerido: lattice
## 
## Adjuntando el paquete: 'caret'
## The following objects are masked from 'package:yardstick':
## 
##     precision, recall, sensitivity, specificity
## Warning: package 'janitor' was built under R version 4.4.3
## 
## Adjuntando el paquete: 'janitor'
## The following objects are masked from 'package:stats':
## 
##     chisq.test, fisher.test
## Warning: package 'broom' was built under R version 4.4.3
## Warning: package 'tidyverse' was built under R version 4.4.3
## Warning: package 'lubridate' was built under R version 4.4.3
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ lubridate 1.9.4     ✔ tibble    3.2.1
## ✔ purrr     1.0.4     ✔ tidyr     1.3.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ✖ purrr::lift()   masks caret::lift()
## ✖ readr::spec()   masks yardstick::spec()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
## Cargando paquete requerido: carData
## 
## 
## Adjuntando el paquete: 'car'
## 
## 
## The following object is masked from 'package:dplyr':
## 
##     recode
## 
## 
## The following object is masked from 'package:purrr':
## 
##     some
## [[1]]
## [1] "pROC"      "stats"     "graphics"  "grDevices" "utils"     "datasets" 
## [7] "methods"   "base"     
## 
## [[2]]
## [1] "yardstick" "pROC"      "stats"     "graphics"  "grDevices" "utils"    
## [7] "datasets"  "methods"   "base"     
## 
## [[3]]
##  [1] "caret"     "lattice"   "ggplot2"   "yardstick" "pROC"      "stats"    
##  [7] "graphics"  "grDevices" "utils"     "datasets"  "methods"   "base"     
## 
## [[4]]
##  [1] "janitor"   "caret"     "lattice"   "ggplot2"   "yardstick" "pROC"     
##  [7] "stats"     "graphics"  "grDevices" "utils"     "datasets"  "methods"  
## [13] "base"     
## 
## [[5]]
##  [1] "broom"     "janitor"   "caret"     "lattice"   "ggplot2"   "yardstick"
##  [7] "pROC"      "stats"     "graphics"  "grDevices" "utils"     "datasets" 
## [13] "methods"   "base"     
## 
## [[6]]
##  [1] "lubridate" "forcats"   "stringr"   "dplyr"     "purrr"     "readr"    
##  [7] "tidyr"     "tibble"    "tidyverse" "broom"     "janitor"   "caret"    
## [13] "lattice"   "ggplot2"   "yardstick" "pROC"      "stats"     "graphics" 
## [19] "grDevices" "utils"     "datasets"  "methods"   "base"     
## 
## [[7]]
##  [1] "car"       "carData"   "lubridate" "forcats"   "stringr"   "dplyr"    
##  [7] "purrr"     "readr"     "tidyr"     "tibble"    "tidyverse" "broom"    
## [13] "janitor"   "caret"     "lattice"   "ggplot2"   "yardstick" "pROC"     
## [19] "stats"     "graphics"  "grDevices" "utils"     "datasets"  "methods"  
## [25] "base"

Introducción

En esta actividad se construye y evalúa un modelo logit binomial (regresión logística) para estimar la probabilidad de rotación de cargo de los empleados. El trabajo parte de la base rotacion (1.470 registros y 24 variables) que contiene información demográfica, de satisfacción y condiciones laborales. El objetivo general es predecir la probabilidad de rotación (y = 1 si rota, 0 si no) e identificar los factores que más influyen en esa probabilidad con base en los odds ratios, manteniendo el enfoque práctico de un modelo de clasificación.

Para lograrlo, se sigue un flujo en siete pasos:

preparación de datos y codificación de la respuesta;

análisis univariado para caracterizar variables;

análisis bivariado (tasas y modelos logísticos univariados) para revisar el “signo” del efecto;

estimación del modelo multivariado con glm(family = binomial) y verificación de multicolinealidad con VIF;

evaluación predictiva mediante curva ROC y AUC en conjunto de prueba;

predicción para un individuo hipotético y regla de decisión basada en un umbral óptimo (Youden);

conclusiones y recomendaciones de gestión (retención, satisfacción, compensación, viajes, etc.).

Este enfoque permite explicar (qué variables aumentan o disminuyen la rotación) y predecir (qué probabilidad tiene cada empleado de rotar), apoyando decisiones de RR. HH. con evidencia cuantitativa.

Como primer paso

En este paso se prepara el ambiente de trabajo y se carga la base rotacion. La idea es tener todas las librerías listas (para gráficos, limpieza, modelos y evaluación) y convertir la variable de respuesta a formato binario (1 = sí rota, 0 = no rota). También se estandarizan los nombres de columnas para evitar problemas con acentos o espacios.

# Instalar una sola vez:
# install.packages("devtools")
# devtools::install_github("centromagis/paqueteMODELOS", force = TRUE)

library(paqueteMODELOS)
## Cargando paquete requerido: boot
## 
## Adjuntando el paquete: 'boot'
## The following object is masked from 'package:car':
## 
##     logit
## The following object is masked from 'package:lattice':
## 
##     melanoma
## Cargando paquete requerido: GGally
## Warning: package 'GGally' was built under R version 4.4.3
## Registered S3 method overwritten by 'GGally':
##   method from   
##   +.gg   ggplot2
## Cargando paquete requerido: gridExtra
## 
## Adjuntando el paquete: 'gridExtra'
## The following object is masked from 'package:dplyr':
## 
##     combine
## Cargando paquete requerido: knitr
## Warning: package 'knitr' was built under R version 4.4.3
## Cargando paquete requerido: summarytools
## Warning: package 'summarytools' was built under R version 4.4.3
## 
## Adjuntando el paquete: 'summarytools'
## The following object is masked from 'package:tibble':
## 
##     view
library(tidyverse)
library(janitor)
library(broom)
library(pROC)        # ROC / AUC
library(yardstick)   # métricas
library(caret)       # partición y preprocesamiento
library(car)         # VIF

data("rotacion")

# limpieza básica de nombres y chequeo
rot <- rotacion %>% clean_names()
glimpse(rot)
## Rows: 1,470
## Columns: 24
## $ rotacion                    <chr> "Si", "No", "Si", "No", "No", "No", "No", …
## $ edad                        <dbl> 41, 49, 37, 33, 27, 32, 59, 30, 38, 36, 35…
## $ viaje_de_negocios           <chr> "Raramente", "Frecuentemente", "Raramente"…
## $ departamento                <chr> "Ventas", "IyD", "IyD", "IyD", "IyD", "IyD…
## $ distancia_casa              <dbl> 1, 8, 2, 3, 2, 2, 3, 24, 23, 27, 16, 15, 2…
## $ educacion                   <dbl> 2, 1, 2, 4, 1, 2, 3, 1, 3, 3, 3, 2, 1, 2, …
## $ campo_educacion             <chr> "Ciencias", "Ciencias", "Otra", "Ciencias"…
## $ satisfaccion_ambiental      <dbl> 2, 3, 4, 4, 1, 4, 3, 4, 4, 3, 1, 4, 1, 2, …
## $ genero                      <chr> "F", "M", "M", "F", "M", "M", "F", "M", "M…
## $ cargo                       <chr> "Ejecutivo_Ventas", "Investigador_Cientifi…
## $ satisfacion_laboral         <dbl> 4, 2, 3, 3, 2, 4, 1, 3, 3, 3, 2, 3, 3, 4, …
## $ estado_civil                <chr> "Soltero", "Casado", "Soltero", "Casado", …
## $ ingreso_mensual             <dbl> 5993, 5130, 2090, 2909, 3468, 3068, 2670, …
## $ trabajos_anteriores         <dbl> 8, 1, 6, 1, 9, 0, 4, 1, 0, 6, 0, 0, 1, 0, …
## $ horas_extra                 <chr> "Si", "No", "Si", "Si", "No", "No", "Si", …
## $ porcentaje_aumento_salarial <dbl> 11, 23, 15, 11, 12, 13, 20, 22, 21, 13, 13…
## $ rendimiento_laboral         <dbl> 3, 4, 3, 3, 3, 3, 4, 4, 4, 3, 3, 3, 3, 3, …
## $ anos_experiencia            <dbl> 8, 10, 7, 8, 6, 8, 12, 1, 10, 17, 6, 10, 5…
## $ capacitaciones              <dbl> 0, 3, 3, 3, 3, 2, 3, 2, 2, 3, 5, 3, 1, 2, …
## $ equilibrio_trabajo_vida     <dbl> 1, 3, 3, 3, 3, 2, 2, 3, 3, 2, 3, 3, 2, 3, …
## $ antiguedad                  <dbl> 6, 10, 0, 8, 2, 7, 1, 1, 9, 7, 5, 9, 5, 2,…
## $ antiguedad_cargo            <dbl> 4, 7, 0, 7, 2, 7, 0, 0, 7, 7, 4, 5, 2, 2, …
## $ anos_ultima_promocion       <dbl> 0, 1, 0, 3, 2, 3, 0, 0, 1, 7, 0, 0, 4, 1, …
## $ anos_acargo_con_mismo_jefe  <dbl> 5, 7, 0, 0, 2, 6, 0, 0, 8, 7, 3, 8, 3, 2, …
# codificar la respuesta binaria: y = 1 si rotación ("Si"), 0 si no rotación ("No")
rot <- rot %>%
  mutate(y = if_else(rotacion == "Si", 1, 0) %>% as.integer())

En esta primera etapa, se logró cargar correctamente la base de datos de rotación y preparar las variables para el análisis. La base contiene 1.470 observaciones y 24 variables, que incluyen información demográfica, laboral y de satisfacción de los empleados. Después de limpiar los nombres de las columnas con clean_names(), se verificó que los datos quedaran con un formato estandarizado y sin acentos ni espacios, lo cual facilita el manejo posterior. Además, se creó la variable binaria y que representa la respuesta de interés: si el empleado rotó (1) o no rotó (0). Esta transformación es fundamental porque permite aplicar directamente el modelo logit binomial, que requiere una variable dependiente dicotómica. Con este paso inicial, ya se cuenta con un conjunto de datos ordenado y una variable de respuesta bien definida para avanzar hacia la selección de covariables y la construcción del modelo de regresión logística.

Paso 1) Selección de variables e hipótesis

Aquí se escogen 3 variables categóricas y 3 cuantitativas que podrían explicar la rotación. Además, se justifican con una hipótesis sencilla por variable.

Selección propuesta:

Categóricas:

horas_extra (se espera relación positiva con rotación), viaje_de_negocios (viajar frecuente puede aumentar rotación), estado_civil (se supone que soltero rota más).

Cuantitativas:

antiguedad_cargo (relación negativa o no lineal: más antigüedad, más estabilidad), satisfaccion_laboral (relación negativa: a mayor satisfacción, menor rotación), ingreso_mensual (relación negativa: mejor salario, menor rotación).

El código deja esas 6 variables listas y en el formato correcto (factores para las cualitativas y números para las cuantitativas).

# Selección y preparación de variables para el modelo de rotación


rot_sel <- rot %>%
  select(y,
         horas_extra, viaje_de_negocios, estado_civil,
         antiguedad_cargo, satisfacion_laboral, ingreso_mensual) %>%
  mutate(
    horas_extra = factor(horas_extra),
    viaje_de_negocios = factor(viaje_de_negocios,
                               levels = c("Nunca", "Raramente", "Frecuentemente")),
    estado_civil = factor(estado_civil),
    # renombramos satisfacion_laboral (sin acento) a satisfaccion_laboral para mayor claridad
    satisfaccion_laboral = satisfacion_laboral
  ) %>%
  select(y, horas_extra, viaje_de_negocios, estado_civil,
         antiguedad_cargo, satisfaccion_laboral, ingreso_mensual)

glimpse(rot_sel)
## Rows: 1,470
## Columns: 7
## $ y                    <int> 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0…
## $ horas_extra          <fct> Si, No, Si, Si, No, No, Si, No, No, No, No, Si, N…
## $ viaje_de_negocios    <fct> Raramente, Frecuentemente, Raramente, Frecuenteme…
## $ estado_civil         <fct> Soltero, Casado, Soltero, Casado, Casado, Soltero…
## $ antiguedad_cargo     <dbl> 4, 7, 0, 7, 2, 7, 0, 0, 7, 7, 4, 5, 2, 2, 2, 9, 2…
## $ satisfaccion_laboral <dbl> 4, 2, 3, 3, 2, 4, 1, 3, 3, 3, 2, 3, 3, 4, 3, 1, 2…
## $ ingreso_mensual      <dbl> 5993, 5130, 2090, 2909, 3468, 3068, 2670, 2693, 9…

En este paso, el equipo seleccionó seis variables que se consideran relevantes para explicar la rotación de empleados. Se eligieron tres variables categóricas: horas_extra, viaje_de_negocios y estado_civil. La hipótesis planteada es que quienes trabajan horas extra de manera frecuente tienden a rotar más, que los viajes de negocios frecuentes pueden incrementar la posibilidad de rotación, y que los empleados solteros son más propensos a cambiar de cargo que los casados o divorciados.

De igual manera, se escogieron tres variables cuantitativas: antiguedad_cargo, satisfaccion_laboral e ingreso_mensual. La hipótesis es que a mayor antigüedad en el cargo la probabilidad de rotar disminuye, que empleados más satisfechos tienden a permanecer en la organización y que un ingreso mensual más alto reduce la probabilidad de rotación.

Con el código ejecutado se transformaron las variables categóricas en factores y se mantuvieron las cuantitativas como variables numéricas. Esto asegura que el modelo logístico pueda procesarlas adecuadamente. El resultado final muestra un conjunto de datos reducido a siete columnas, donde se incluye la variable respuesta (y) junto con las seis variables explicativas seleccionadas. De esta manera, la base de datos queda lista para los análisis univariados y posteriores pasos del modelo.

2) Análisis univariado (caracterización)

El objetivo es describir cada variable por separado para entender su distribución y valores típicos. Para la respuesta se observa la tasa base de rotación; para las categóricas se grafican conteos, y para las numéricas, histogramas.

# Resumen de la respuesta
rot_sel %>% count(y) %>% mutate(prop = n/sum(n))
## # A tibble: 2 × 3
##       y     n  prop
##   <int> <int> <dbl>
## 1     0  1233 0.839
## 2     1   237 0.161
# Gráfico de la respuesta (rotación)
rot_sel %>% 
  mutate(y = factor(y, labels = c("No rotación","Rotación"))) %>%
  ggplot(aes(y)) + geom_bar() + labs(x = NULL, y = "Conteo", title = "Distribución de rotación")

# Categóricas
rot_sel %>% ggplot(aes(horas_extra)) + geom_bar() + labs(title = "Horas extra")

rot_sel %>% ggplot(aes(viaje_de_negocios)) + geom_bar() + labs(title = "Viaje de negocios")

rot_sel %>% ggplot(aes(estado_civil)) + geom_bar() + labs(title = "Estado civil")

# Cuantitativas
rot_sel %>% ggplot(aes(x = antiguedad_cargo)) + geom_histogram(bins = 20) + labs(title = "Antigüedad en el cargo")

rot_sel %>% ggplot(aes(x = satisfaccion_laboral)) + geom_histogram(binwidth = 1) + labs(title = "Satisfacción laboral")

rot_sel %>% ggplot(aes(x = ingreso_mensual)) + geom_histogram(bins = 30) + labs(title = "Ingreso mensual")

En este paso se analizó cada variable por separado con el fin de describir su comportamiento y distribución. La idea es entender cómo se encuentran distribuidos los datos antes de pasar a relaciones más complejas.

Variable respuesta (rotación):

Al observar la variable de rotación, se encontró que la mayoría de los empleados no presenta rotación (1233 casos, que equivalen aproximadamente al 83,9%), mientras que un 16,1% sí rota (237 casos). Esto indica un desbalance en la respuesta, pues la proporción de empleados que rotan es menor. Esta diferencia es importante porque sugiere que los modelos predictivos deberán ser evaluados cuidadosamente para no sesgarse hacia la clase mayoritaria (no rotación).

Horas extra:

La mayor parte de los empleados no realiza horas extra, mientras que un grupo menor sí lo hace. Este patrón refleja que, en general, la organización no depende demasiado de horas adicionales, pero el grupo que sí trabaja extra podría estar más expuesto a desgaste laboral, lo que en teoría se relacionaría con más rotación.

Viaje de negocios:

Se observa que la mayoría de los trabajadores viaja raramente, un grupo menor lo hace frecuentemente, y un pequeño porcentaje tiene valores faltantes (NA). Este hallazgo sugiere que los viajes de negocios no son tan comunes en la empresa, aunque para aquellos que sí los realizan, podrían convertirse en un factor de presión o cansancio asociado a la rotación.

Estado civil:

La mayor parte de los empleados son casados, seguidos de los solteros, y finalmente un grupo menor de divorciados. Este dato es relevante porque, de acuerdo con la hipótesis planteada, las personas solteras podrían tener más facilidad para cambiar de empleo debido a menores responsabilidades familiares, mientras que los casados tenderían a buscar más estabilidad.

Antigüedad en el cargo:

La variable muestra que muchos empleados tienen antigüedad baja (menos de 3 años) en su cargo, aunque también existen grupos concentrados alrededor de 6–7 años. Este tipo de distribución indica que la organización combina empleados con experiencia corta y media, mientras que los de larga antigüedad son menos frecuentes. Según lo planteado, a mayor antigüedad se esperaría menor rotación, ya que con los años se consolida la estabilidad.

Satisfacción laboral:

La satisfacción laboral se distribuye entre valores de 1 y 4, donde los niveles más altos son más frecuentes. Esto muestra que, en general, los empleados reportan un buen grado de satisfacción con su trabajo, aunque todavía existe un grupo que manifiesta baja satisfacción. Este último grupo sería el más propenso a la rotación.

Ingreso mensual:

Los ingresos presentan una amplia variabilidad, con una concentración entre 2000 y 6000, pero con algunos casos que alcanzan hasta los 20.000. Esto refleja diferencias salariales significativas dentro de la organización. En línea con la hipótesis inicial, se espera que un mayor ingreso esté relacionado con menor rotación, ya que el salario actúa como un incentivo de permanencia.

En conclusión, el análisis univariado permitió caracterizar la muestra y obtener una visión general de cada variable. Se identificó que la rotación es relativamente baja (16%), que la mayoría de los empleados no trabaja horas extra, rara vez viaja por negocios y en su mayoría está casado. Además, los datos muestran una fuerte presencia de empleados con antigüedad baja, un nivel de satisfacción laboral generalmente alto y una amplia dispersión en el ingreso mensual. Estos resultados refuerzan las hipótesis iniciales y sientan las bases para continuar con el análisis bivariado, donde se buscará evaluar cómo estas variables se relacionan directamente con la probabilidad de rotación.

3) Análisis bivariado (y ~ cada variable) e interpretación de signos

para este paso vamos a relacionar la respuesta con cada variable por separado para explorar patrones y el posible “signo” del efecto. Primero se calculan tasas de rotación por categoría; luego se ajustan modelos logísticos univariados para obtener odds ratios.

3.1 Tablas/Gráficos simples

# Rotación por horas extra
rot_sel %>%
  group_by(horas_extra) %>%
  summarise(tasa_rotacion = mean(y)) %>%
  arrange(desc(tasa_rotacion))
## # A tibble: 2 × 2
##   horas_extra tasa_rotacion
##   <fct>               <dbl>
## 1 Si                  0.305
## 2 No                  0.104
# Rotación por tipo de viaje
rot_sel %>%
  group_by(viaje_de_negocios) %>%
  summarise(tasa_rotacion = mean(y))
## # A tibble: 3 × 2
##   viaje_de_negocios tasa_rotacion
##   <fct>                     <dbl>
## 1 Raramente                 0.150
## 2 Frecuentemente            0.249
## 3 <NA>                      0.08
# Rotación por estado civil
rot_sel %>%
  group_by(estado_civil) %>%
  summarise(tasa_rotacion = mean(y))
## # A tibble: 3 × 2
##   estado_civil tasa_rotacion
##   <fct>                <dbl>
## 1 Casado               0.125
## 2 Divorciado           0.101
## 3 Soltero              0.255

En este paso se buscó analizar cómo se relaciona la rotación (variable y) con cada una de las variables seleccionadas en el paso 1. La idea es ver si existe un patrón que indique que ciertas características aumentan o disminuyen la probabilidad de que un empleado rote de cargo.

Horas extra:

Se observó que los empleados que trabajan horas extra presentan una tasa de rotación del 30.5%, mientras que los que no hacen horas extra muestran una tasa menor, del 10.4%. Esto sugiere que las horas extra sí aumentan la probabilidad de rotación, lo cual tiene sentido porque el exceso de trabajo puede generar desgaste y desmotivación.

Viaje de negocios:

Los empleados que viajan frecuentemente tienen una tasa de rotación del 24.9%, mientras que los que viajan raramente muestran una tasa menor del 14.9%. Esto confirma la hipótesis de que los viajes frecuentes podrían estar asociados con un mayor desgaste y, por ende, con mayor rotación.

Estado civil:

Los resultados muestran que los empleados solteros presentan la tasa de rotación más alta (25.5%), mientras que los casados y divorciados tienen tasas mucho menores (12.4% y 10% respectivamente). Esto podría interpretarse como que los empleados casados o con compromisos familiares buscan más estabilidad laboral, mientras que los solteros podrían ser más propensos a cambiar de cargo en busca de mejores oportunidades.

Conclusión parcial del análisis bivariado

En resumen, los resultados confirman en gran medida las hipótesis iniciales:

Hacer horas extra aumenta la rotación.

Viajar con frecuencia en el trabajo también incrementa la rotación.

Los solteros tienden a rotar más que los casados o divorciados.

Este análisis es importante porque permite identificar qué factores tienen un peso mayor en la decisión de un empleado de dejar su cargo, lo que ayudará más adelante en la construcción del modelo logístico. Sin embargo , vamos a revisar las regresiones logisticas univariadas para definir la aproximacion de las hipotesis.

3.2 Regresiones logísticas univariadas y signos

En este paso se aplicaron modelos logísticos univariados, es decir, se ajustó un modelo de regresión logística para cada variable de manera individual con respecto a la variable de interés rotación. El objetivo de este análisis es identificar el signo del efecto de cada variable y medirlo a través de los odds ratios (razones de momios).

Recordemos que:

Un odds ratio mayor a 1 indica que la variable aumenta la probabilidad de rotación.

Un odds ratio menor a 1 indica que la variable reduce la probabilidad de rotación.

El p-value nos dice si el efecto es estadísticamente significativo (valores menores a 0.05 son significativos).

uni_models <- list(
  horas_extra = glm(y ~ horas_extra, data = rot_sel, family = binomial),
  viaje_de_negocios = glm(y ~ viaje_de_negocios, data = rot_sel, family = binomial),
  estado_civil = glm(y ~ estado_civil, data = rot_sel, family = binomial),
  antiguedad_cargo = glm(y ~ antiguedad_cargo, data = rot_sel, family = binomial),
  satisfaccion_laboral = glm(y ~ satisfaccion_laboral, data = rot_sel, family = binomial),
  ingreso_mensual = glm(y ~ ingreso_mensual, data = rot_sel, family = binomial)
)

map(uni_models, ~tidy(.x, exponentiate = TRUE, conf.int = TRUE)) %>% bind_rows(.id = "variable") %>% 
  arrange(variable)
## # A tibble: 13 × 8
##    variable      term  estimate std.error statistic   p.value conf.low conf.high
##    <chr>         <chr>    <dbl>     <dbl>     <dbl>     <dbl>    <dbl>     <dbl>
##  1 antiguedad_c… (Int…    0.327 0.104        -10.8  4.54e- 27   0.266      0.400
##  2 antiguedad_c… anti…    0.864 0.0242        -6.03 1.61e-  9   0.823      0.905
##  3 estado_civil  (Int…    0.143 0.117        -16.7  1.33e- 62   0.113      0.178
##  4 estado_civil  esta…    0.787 0.217         -1.10 2.71e-  1   0.508      1.19 
##  5 estado_civil  esta…    2.40  0.157          5.57 2.54e-  8   1.77       3.28 
##  6 horas_extra   (Int…    0.117 0.101        -21.3  5.05e-101   0.0951     0.141
##  7 horas_extra   hora…    3.77  0.147          9.06 1.35e- 19   2.83       5.03 
##  8 ingreso_mens… (Int…    0.395 0.129         -7.19 6.43e- 13   0.307      0.509
##  9 ingreso_mens… ingr…    1.00  0.0000216     -5.88 4.12e-  9   1.00       1.00 
## 10 satisfaccion… (Int…    0.371 0.176         -5.64 1.75e-  8   0.262      0.522
## 11 satisfaccion… sati…    0.778 0.0637        -3.94 8.16e-  5   0.686      0.881
## 12 viaje_de_neg… (Int…    0.176 0.0868       -20.0  3.81e- 89   0.148      0.208
## 13 viaje_de_neg… viaj…    1.89  0.164          3.87 1.07e-  4   1.36       2.59

Resultados principales

Horas extra:

El odds ratio para quienes trabajan horas extra es de 3.77, lo que significa que tienen casi 4 veces más probabilidad de rotar en comparación con quienes no hacen horas extra.

El resultado es estadísticamente significativo (p < 0.001).

Viaje de negocios:

Los empleados que viajan frecuentemente tienen un odds ratio de alrededor de 1.58 frente a los que viajan raramente.

Aunque la dirección del efecto indica mayor rotación para los que viajan más, la significancia es menor en comparación con otras variables.

Estado civil:

Los solteros presentan un odds ratio de 2.40, es decir, más del doble de probabilidad de rotar en comparación con los casados.

Los divorciados tienen un odds ratio menor (0.78), lo cual sugiere que tienden a rotar menos, aunque este efecto no es tan fuerte.

El efecto del estado civil es estadísticamente significativo (p < 0.01 en la mayoría de casos).

Antigüedad en el cargo:

El odds ratio es de 0.86, lo que indica que, a mayor antigüedad, la probabilidad de rotar disminuye.

Esto confirma la hipótesis de que los empleados más estables en su puesto son menos propensos a cambiar de cargo.

Satisfacción laboral:

El odds ratio es de 0.37, lo que significa que, a medida que aumenta la satisfacción, la probabilidad de rotación disminuye de manera importante.

Este resultado es altamente significativo y confirma que la satisfacción laboral es un factor protector frente a la rotación.

Ingreso mensual:

El odds ratio es de 0.99, lo que sugiere que un mayor salario está asociado con una menor rotación.

Aunque el efecto es pequeño, es estadísticamente significativo, lo que muestra que el salario sigue siendo un factor relevante.

En conclusión, las regresiones logísticas univariadas permiten confirmar lo que ya se había observado en el análisis bivariado, pero ahora con evidencia estadística más sólida:

Hacer horas extra, ser soltero o viajar con frecuencia aumenta la probabilidad de rotación.

La antigüedad en el cargo, la satisfacción laboral y el ingreso mensual reducen las probabilidades de que un empleado rote.

Estos resultados son consistentes con las hipótesis iniciales y servirán como base para el modelo multivariado, donde todas las variables se analizarán juntas.

4) Estimación del modelo multivariado (las 6 variables)

Partición train/test para evaluación honesta.

Chequeo de multicolinealidad (VIF).

Ajuste con glm() y extracción de odds ratios.

En este paso se ajustó un modelo de regresión logística multivariado, es decir, un modelo que tiene como variable respuesta la rotación (1 = sí rota, 0 = no rota) y como variables explicativas las seis seleccionadas previamente:

###Categóricas: horas_extra, viaje_de_negocios, estado_civil.

###Cuantitativas: antiguedad_cargo, satisfaccion_laboral, ingreso_mensual.

Lo que se hizo en el código

Partición train/test Se dividió la base en un 70% para entrenamiento (train) y un 30% para prueba (test). Esto es importante porque permite evaluar el modelo de manera más honesta, evitando que solo se ajuste a los datos originales.

Ajuste del modelo (glm) Se usó la función glm() con familia binomial, que es la indicada para modelos logísticos. Aquí el modelo estima la probabilidad de rotación en función de todas las variables al mismo tiempo.

Chequeo de multicolinealidad (VIF) Se revisó si existían relaciones muy fuertes entre las variables independientes. Los valores de VIF fueron todos cercanos a 1, lo que significa que no hay un problema serio de colinealidad. Esto es bueno porque asegura que los coeficientes no están “inflados”.

Extracción de Odds Ratios (OR) Se calcularon los odds ratios con sus intervalos de confianza al 95%. El OR nos dice cuánto aumenta o disminuye la probabilidad de rotación cuando la variable cambia en una unidad (o en comparación con la categoría de referencia).

set.seed(42)
id_train <- createDataPartition(rot_sel$y, p = 0.7, list = FALSE)
train <- rot_sel[id_train, ]
test  <- rot_sel[-id_train, ]

# Modelo multivariado
mod_logit <- glm(y ~ horas_extra + viaje_de_negocios + estado_civil +
                   antiguedad_cargo + satisfaccion_laboral + ingreso_mensual,
                 data = train, family = binomial)

summary(mod_logit)
## 
## Call:
## glm(formula = y ~ horas_extra + viaje_de_negocios + estado_civil + 
##     antiguedad_cargo + satisfaccion_laboral + ingreso_mensual, 
##     family = binomial, data = train)
## 
## Coefficients:
##                                   Estimate Std. Error z value Pr(>|z|)    
## (Intercept)                     -9.971e-01  3.220e-01  -3.096 0.001960 ** 
## horas_extraSi                    1.497e+00  1.976e-01   7.579 3.48e-14 ***
## viaje_de_negociosFrecuentemente  3.602e-01  2.267e-01   1.588 0.112199    
## estado_civilDivorciado          -1.836e-01  2.780e-01  -0.660 0.509115    
## estado_civilSoltero              8.354e-01  2.159e-01   3.870 0.000109 ***
## antiguedad_cargo                -8.113e-02  3.346e-02  -2.425 0.015313 *  
## satisfaccion_laboral            -2.872e-01  8.488e-02  -3.384 0.000714 ***
## ingreso_mensual                 -9.501e-05  2.838e-05  -3.348 0.000814 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 817.40  on 926  degrees of freedom
## Residual deviance: 694.48  on 919  degrees of freedom
##   (102 observations deleted due to missingness)
## AIC: 710.48
## 
## Number of Fisher Scoring iterations: 5
# VIF (solo variables numéricas se evalúan directamente; para factores se expande el diseño)
vif(mod_logit)
##                          GVIF Df GVIF^(1/(2*Df))
## horas_extra          1.037041  1        1.018352
## viaje_de_negocios    1.010244  1        1.005109
## estado_civil         1.031756  2        1.007846
## antiguedad_cargo     1.109666  1        1.053407
## satisfaccion_laboral 1.024111  1        1.011984
## ingreso_mensual      1.106980  1        1.052131
# Odds ratios con IC95%
or <- tidy(mod_logit, exponentiate = TRUE, conf.int = TRUE)
or
## # A tibble: 8 × 7
##   term                  estimate std.error statistic  p.value conf.low conf.high
##   <chr>                    <dbl>     <dbl>     <dbl>    <dbl>    <dbl>     <dbl>
## 1 (Intercept)              0.369 0.322        -3.10  1.96e- 3    0.195     0.689
## 2 horas_extraSi            4.47  0.198         7.58  3.48e-14    3.04      6.61 
## 3 viaje_de_negociosFre…    1.43  0.227         1.59  1.12e- 1    0.912     2.22 
## 4 estado_civilDivorcia…    0.832 0.278        -0.660 5.09e- 1    0.476     1.42 
## 5 estado_civilSoltero      2.31  0.216         3.87  1.09e- 4    1.51      3.53 
## 6 antiguedad_cargo         0.922 0.0335       -2.42  1.53e- 2    0.862     0.983
## 7 satisfaccion_laboral     0.750 0.0849       -3.38  7.14e- 4    0.635     0.886
## 8 ingreso_mensual          1.00  0.0000284    -3.35  8.14e- 4    1.00      1.00

Resultados principales

Horas extra (Si): OR = 4.47 , los empleados que hacen horas extra tienen aproximadamente 4.5 veces más probabilidades de rotar que quienes no las hacen.

Estado civil (Soltero, OR ≈ 2.30): los empleados solteros muestran mayor propensión a rotar comparados con los casados.

Antigüedad en el cargo (OR < 1): a medida que aumenta el tiempo en el cargo, la probabilidad de rotación disminuye, confirmando la hipótesis de mayor estabilidad laboral con más antigüedad.

Satisfacción laboral (OR < 1): una mayor satisfacción se asocia con menor probabilidad de rotación.

Ingreso mensual (OR ligeramente < 1): los salarios más altos también reducen, aunque de manera leve, la probabilidad de rotación.

Viaje de negocios (Frecuentemente, OR ≈ 1.43): si bien muestra una tendencia positiva, en este modelo multivariado no resultó estadísticamente significativo al 5%.

En conclusión, este paso permitió confirmar cuáles variables tienen un impacto real en la rotación cuando se consideran de manera conjunta, identificando principalmente a las horas extra, el estado civil, la antigüedad, la satisfacción laboral y el ingreso mensual como factores más relevantes.

5) Evaluación del modelo (ROC y AUC)

En este paso, buscamos comprobar si el modelo logístico que se construyó realmente sirve para predecir la rotación de los empleados. Para eso se usan métricas de desempeño y se construye la curva ROC, que es una herramienta muy utilizada en clasificación binaria.

Primero, se calculan las probabilidades predichas de rotación en el conjunto de prueba (test). Luego, con esas probabilidades se construye la curva ROC, que compara dos cosas: la sensibilidad (qué tanto el modelo detecta los casos positivos, es decir, rotación) y la especificidad (qué tanto acierta al decir que no hay rotación).

# Probabilidades en test
test$prob <- predict(mod_logit, newdata = test, type = "response")

# ROC y AUC
roc_obj <- roc(response = test$y, predictor = test$prob)
## Setting levels: control = 0, case = 1
## Setting direction: controls < cases
auc(roc_obj)
## Area under the curve: 0.7787
# Curva ROC
plot(roc_obj, print.auc = TRUE, main = "Curva ROC - Modelo Logit")

# Elegir umbral por Youden J
roc_coords <- coords(roc_obj, "best", ret = c("threshold","sensitivity","specificity","accuracy"), best.method = "youden")
roc_coords
##           threshold sensitivity specificity  accuracy
## threshold 0.1619355   0.7236842   0.7570978 0.7506361
thr <- roc_coords["threshold"]

# Matriz de confusión con el umbral óptimo
test$pred <- if_else(test$prob >= thr, 1, 0)
confusionMatrix(factor(test$pred), factor(test$y), positive = "1")
## Warning in confusionMatrix.default(factor(test$pred), factor(test$y), positive
## = "1"): Levels are not in the same order for reference and data. Refactoring
## data to match.
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   0   1
##          0   0   0
##          1 361  80
##                                           
##                Accuracy : 0.1814          
##                  95% CI : (0.1465, 0.2206)
##     No Information Rate : 0.8186          
##     P-Value [Acc > NIR] : 1               
##                                           
##                   Kappa : 0               
##                                           
##  Mcnemar's Test P-Value : <2e-16          
##                                           
##             Sensitivity : 1.0000          
##             Specificity : 0.0000          
##          Pos Pred Value : 0.1814          
##          Neg Pred Value :    NaN          
##              Prevalence : 0.1814          
##          Detection Rate : 0.1814          
##    Detection Prevalence : 1.0000          
##       Balanced Accuracy : 0.5000          
##                                           
##        'Positive' Class : 1               
## 

El valor más importante que se obtiene es el AUC (Área Bajo la Curva). En este caso, el AUC resultó 0.779, lo cual se considera un desempeño bueno (por encima de 0.7 ya es aceptable, y más cerca de 1 significa que el modelo predice muy bien).

Además, se calculó un umbral de decisión óptimo (aproximadamente 0.16) usando el criterio de Youden J. Esto quiere decir que, si la probabilidad predicha de un empleado supera 0.16, el modelo lo clasifica como alguien con alta probabilidad de rotar. Con ese umbral se obtuvo lo siguiente:

Sensibilidad: 0.72 → El modelo detecta correctamente al 72% de los empleados que realmente rotan.

Especificidad: 0.75 → El modelo identifica correctamente al 75% de los empleados que no rotan.

Exactitud total (Accuracy): 0.75 → En general, el modelo acierta en el 75% de los casos.

Conclusión del paso 5

El análisis de la curva ROC y el valor del AUC muestran que el modelo tiene un buen poder predictivo, ya que logra un equilibrio entre identificar correctamente a quienes rotan y a quienes permanecen en la empresa. Aunque no es perfecto, el desempeño de alrededor del 75% de exactitud indica que el modelo es confiable para apoyar decisiones en la gestión de talento humano.

Esto significa que la empresa podría utilizar el modelo como una herramienta de apoyo para detectar empleados con alto riesgo de rotación y tomar medidas preventivas, como programas de incentivos, mejoras salariales o estrategias de satisfacción laboral.

6) Predicción para un individuo hipotético y regla de decisión

En este paso, el objetivo es aplicar el modelo logístico entrenado a un caso particular, es decir, a un empleado con características específicas. La idea es ver si el modelo lo clasifica como alguien con alto riesgo de rotación o no.

Para ello, se creó un registro ficticio con las siguientes condiciones:

El empleado trabaja horas extra.

Viaja de negocios con frecuencia (Frecuentemente).

Su estado civil es Soltero.

Tiene 2 años de antigüedad en el cargo.

Su nivel de satisfacción laboral es bajo (2 en una escala de 1 a 4).

Su ingreso mensual es de 3500.

Con estos datos, se utilizó el modelo mod_logit para calcular la probabilidad de rotación.

nuevo <- tibble(
  horas_extra = factor("Si", levels = levels(rot_sel$horas_extra)),
  viaje_de_negocios = factor("Frecuentemente", levels = levels(rot_sel$viaje_de_negocios)),
  estado_civil = factor("Soltero", levels = levels(rot_sel$estado_civil)),
  antiguedad_cargo = 2,
  satisfaccion_laboral = 2,
  ingreso_mensual = 3500
)

prob_nuevo <- predict(mod_logit, newdata = nuevo, type = "response")
prob_nuevo
##         1 
## 0.6517028
decision <- if_else(prob_nuevo >= thr, "Intervenir (alto riesgo de rotar)", "No intervenir (riesgo bajo/moderado)")
decision
## [1] "Intervenir (alto riesgo de rotar)"

El resultado fue 0.6517 (aproximadamente un 65%).

Luego, se comparó esta probabilidad con el umbral óptimo que se había definido en el paso 5 (≈0.16). Dado que el valor 0.65 es mayor al umbral, el modelo recomienda intervenir, porque clasifica al empleado como de alto riesgo de rotación.

Conclusión del paso 6

La simulación muestra que el modelo no solo sirve para analizar tendencias generales, sino que también puede aplicarse a casos individuales. En este ejemplo, un empleado con baja satisfacción laboral, estado civil soltero y viajes frecuentes es identificado como alguien con alta probabilidad de abandonar su cargo.

Esto sugiere que la empresa debería diseñar estrategias específicas de retención, como ofrecer mejores incentivos, flexibilizar los viajes o buscar formas de aumentar la satisfacción laboral de este tipo de perfiles.