En una organización, se busca comprender y prever los factores que influyen en la rotación de empleados entre distintos cargos. La empresa ha recopilado datos históricos sobre el empleo de sus trabajadores, incluyendo variables como la antigüedad en el cargo actual, el nivel de satisfacción laboral, el salario actual, edad y otros factores relevantes. La gerencia planea desarrollar un modelo de regresión logística que permita estimar la probabilidad de que un empleado cambie de cargo en el próximo período y determinar cuales factores indicen en mayor proporción a estos cambios.
Con esta información, la empresa podrá tomar medidas proactivas para retener a su talento clave, identificar áreas de mejora en la gestión de recursos humanos y fomentar un ambiente laboral más estable y tranquilo. La predicción de la probabilidad de rotación de empleados ayudará a la empresa a tomar decisiones estratégicas informadas y a mantener un equipo de trabajo comprometido y satisfecho en sus roles actuales.
A continuación, se describen los pasos realizados para el análisis:
Seleccionaremos 3 variables cuantitativas y 3 categóricas que, a primera vista, consideramos que pueden influir en la decisión de un empleado de rotar. Para cada una, plantearemos una hipótesis sobre su relación con la variable rotacion.
Nota sobre la Calidad y Preparación de los
Datos: Se realizó una validación inicial de la calidad del
conjunto de datos rotacion. Se confirmó que el conjunto de
datos no contiene ninguna fila duplicada, ni valores faltantes. En el
dataset original, variables como educacion,
satisfaccion_laboral o rendimiento_laboral
estaban representadas con números (ej: 1, 2, 3, 4, 5). Si se dejan así,
el modelo las tratará incorrectamente como variables numéricas
continuas, asumiendo, por ejemplo, que la “distancia” entre un nivel
educativo y el siguiente es siempre la misma. Para corregir esto, se
realizará la conversión a factores ordenados (convirtiendo cada
variables en un dato tipo factor).
Ingreso_Mensual: Un ingreso mensual bajo puede ser un
factor de insatisfacción que motive a los empleados a buscar
oportunidades mejor remuneradas. Se espera una relación negativa; a
mayor ingreso mensual, menor será la probabilidad de rotación.Edad: Los empleados de mayor edad tienden a valorar más
la estabilidad laboral y pueden tener más responsabilidades personales,
lo que los hace menos propensos a cambiar de trabajo. Se espera una
relación negativa; a mayor edad, menor será la probabilidad de
rotación.Antigüedad: La permanencia en la empresa puede generar
un sentido de lealtad y comodidad. Además, los empleados con más
antigüedad pueden tener beneficios acumulados que no desean perder. Se
espera una relación negativa; a más años en la compañía, menor será la
probabilidad de rotación.Horas_Extra: Trabajar horas extra de forma constante
puede llevar al agotamiento y a un desequilibrio entre la vida laboral y
personal, aumentando el deseo de buscar un empleo con mejor balance. Los
empleados que trabajan horas extra tienen una mayor probabilidad de
rotar que los que no lo hacen.Viaje de Negocios: Viajar frecuentemente por trabajo
puede ser estresante y afectar la vida familiar y social del empleado,
lo que podría incentivar la búsqueda de un rol con menos
desplazamientos. A mayor frecuencia de viajes de negocios, mayor será la
probabilidad de rotación.Estado_Civil: Las personas solteras pueden tener mayor
flexibilidad y menos ataduras geográficas o financieras, lo que les
facilitaría cambiar de empleo en comparación con personas casadas o con
familia. Los empleados solteros tienen una mayor probabilidad de rotar
que los casados.# Creamos el dataframe con las variables de interés a partir del dataset limpio.
datos_seleccionados <- rotacion_corregida %>%
select(Rotación, Ingreso_Mensual, Edad, Antigüedad,
Horas_Extra, `Viaje de Negocios`, Estado_Civil)
head(datos_seleccionados)Ahora que tenemos nuestro conjunto de datos limpio y seleccionado, analizaremos cada variable de forma individual para entender sus características y distribución.
# Gráfico para la variable Rotación
p_rot <- ggplot(datos_seleccionados, aes(x = Rotación, fill = Rotación, text = paste("Conteo:", ..count..))) +
geom_bar() +
labs(title = "Distribución de la Rotación de Empleados", x = "", y = "Cantidad") +
theme_minimal()
ggplotly(p_rot, tooltip = "text")Observamos un desbalance notable en nuestra variable
objetivo. Aproximadamente el 16% de los empleados en la
muestra han rotado (Sí), mientras que el
84% no lo ha hecho (No). Es necesario
aplicar técnicas de balanceo de clases, de lo contrario el modelo
tenderá a predecir mejor la clase mayoritaria.
# --- Análisis de Variables Cuantitativas ---
summary(datos_seleccionados[, c("Ingreso_Mensual", "Edad", "Antigüedad")]) Ingreso_Mensual Edad Antigüedad
Min. : 1009 Min. :18.00 Min. : 0.000
1st Qu.: 2911 1st Qu.:30.00 1st Qu.: 3.000
Median : 4919 Median :36.00 Median : 5.000
Mean : 6503 Mean :36.92 Mean : 7.008
3rd Qu.: 8379 3rd Qu.:43.00 3rd Qu.: 9.000
Max. :19999 Max. :60.00 Max. :40.000
# Histograma interactivo del Ingreso Mensual
p_ingreso <- ggplot(datos_seleccionados, aes(x = Ingreso_Mensual)) +
geom_histogram(fill = "skyblue", color = "black", bins = 30) +
labs(title = "Distribución del Ingreso Mensual")
ggplotly(p_ingreso)# Histograma interactivo de la Edad
p_edad <- ggplot(datos_seleccionados, aes(x = Edad)) +
geom_histogram(fill = "lightgreen", color = "black", bins = 20) +
labs(title = "Distribución de la Edad")
ggplotly(p_edad)# Histograma interactivo de la Edad
p_antigüedad <- ggplot(datos_seleccionados, aes(x = Antigüedad)) +
geom_histogram(fill = "pink", color = "black", bins = 20) +
labs(title = "Distribución de la Antigüedad")
ggplotly(p_antigüedad)# --- Análisis de Variables Categóricas ---
# Grafico para Horas Extras
p_civil <- ggplot(datos_seleccionados, aes(x = Horas_Extra, fill = Horas_Extra)) +
geom_bar() +
labs(title = "Distribución por Horas Extras")
ggplotly(p_civil)En este paso, analizaremos la relación entre cada una de nuestras
variables predictoras y la variable respuesta rotacion. Primero,
codificaremos la variable rotacion a un formato numérico
(0/1).
# Convertimos Rotación a una variable numérica (0 = No, 1 = Si)
datos_seleccionados <- datos_seleccionados %>%
mutate(rotacion_num = ifelse(Rotación == "Si", 1, 0))# --- Relación: Variables Cuantitativas vs. Rotación ---
# Boxplot interactivo Ingreso Mensual vs. Rotación
p_box_ingreso <- ggplot(datos_seleccionados, aes(x = Rotación, y = Ingreso_Mensual, fill = Rotación)) +
geom_boxplot() +
labs(title = "Ingreso Mensual según Rotación") +
theme_minimal()
ggplotly(p_box_ingreso)El gráfico confirma nuestra hipótesis, la mediana del ingreso mensual
de los empleados que rotan es más baja que la de los empleados que se
quedan. Esto sugiere una fuerte relación negativa con la rotación. Por
lo tanto, en el modelo de regresión, esperaríamos que el coeficiente
estimado para Ingreso_Mensual tuviera un signo negativo
(-).
p_box_edad <- ggplot(datos_seleccionados, aes(x = Rotación, y = Edad, fill = Rotación)) +
geom_boxplot() +
labs(title = "Edad según Rotación") +
theme_minimal()
ggplotly(p_box_edad)Los empleados que rotan tienden a ser más jóvenes. La mediana de edad
del grupo “Sí” es menor y la distribución está más concentrada en
valores bajos. Esto indica una relación negativa. Se espera que el
coeficiente para Edad en el modelo tenga un signo negativo
(-).
p_box_antiguedad <- ggplot(datos_seleccionados, aes(x = Rotación, y = Antigüedad, fill = Rotación)) +
geom_boxplot() +
labs(title = "Antigüedad en la empresa según Rotación") +
theme_minimal()
ggplotly(p_box_antiguedad)La diferencia es muy marcada; las personas que rotan tienen, en
general, muchos menos años en la compañía. Esta es otra clara relación
negativa, por lo que esperamos un signo negativo (-) para el coeficiente
de Antigüedad.
p_bar_he <- ggplot(datos_seleccionados, aes(x = Horas_Extra, fill = Rotación)) +
geom_bar(position = "fill") +
labs(title = "Proporción de Rotación según Horas Extra", y = "Proporción") +
theme_minimal()
ggplotly(p_bar_he)Este es uno de los predictores más potentes visualmente. La tasa de
rotación se dispara entre los empleados que trabajan horas extra.
Mientras que solo un pequeño porcentaje de quienes no hacen horas extra
rotan, esta cifra aumenta a más del 30% para quienes sí las hacen. La
hipótesis se confirma. Esto indica una fuerte relación positiva. En el
modelo, el coeficiente para la categoría “Sí” de
Horas_Extra debería tener un signo positivo
(+), indicando que aumenta la probabilidad de rotar en
comparación con la categoría de referencia (“No”).
p_bar_viajes <- ggplot(datos_seleccionados, aes(x = `Viaje de Negocios`, fill = Rotación)) +
geom_bar(position = "fill") +
labs(title = "Proporción de Rotación según Frecuencia de Viajes", y = "Proporción") +
theme_minimal()
ggplotly(p_bar_viajes)La tasa de rotación es notablemente más alta para quienes viajan “Frecuentemente”. Esto sugiere que la intensidad de los viajes es un factor de desgaste. Esperaríamos que el coeficiente para la categoría “Frecuentemente” sea positivo (+) y de mayor magnitud que el de “Raramente”, ambos en comparación con la categoría base (“No_Viaja”).
p_bar_civil <- ggplot(datos_seleccionados, aes(x = Estado_Civil, fill = Rotación)) +
geom_bar(position = "fill") +
labs(title = "Proporción de Rotación según Estado Civil", y = "Proporción") +
theme_minimal()
ggplotly(p_bar_civil)Los empleados “Soltero” muestran la tasa de rotación más alta, casi el doble que la de los empleados “Casado/a”. Esto apoya la hipótesis de que pueden tener mayor flexibilidad para cambiar de trabajo. El coeficiente para “Soltero” debería tener un signo positivo (+) en comparación con la categoría de referencia (“Casado”).
Como vimos en el análisis univariado, solo el 16% de nuestros datos corresponde a empleados que rotaron (“Sí”), mientras que el 84% corresponde a los que no lo hicieron (“No”).
Un modelo de machine learning entrenado con datos desbalanceados se vuelve “perezoso”. Aprende que puede lograr una precisión muy alta (84%) simplemente prediciendo siempre la clase mayoritaria (“No”). En la práctica, este modelo sería inútil, ya que nuestro objetivo es precisamente identificar a la minoría: los empleados en riesgo de irse. El modelo fallaría en detectar a casi todos los empleados que sí rotan, que es el grupo que más nos interesa.
Existen varias estrategias, pero nos centraremos en la más efectiva y comúnmente utilizada: SMOTE (Synthetic Minority Over-sampling Technique).
Para implementar SMOTE correctamente, debemos seguir dos reglas de oro:
# Hacemos la división de datos
set.seed(123) # Para reproducibilidad
division_datos <- initial_split(datos_seleccionados, prop = 0.70, strata = Rotación)
# Creamos los dataframes de entrenamiento y prueba
datos_entrenamiento <- training(division_datos)
datos_prueba <- testing(division_datos)
# Verificamos las proporciones en cada conjunto
cat("Proporción en datos de entrenamiento:\n")Proporción en datos de entrenamiento:
No Si
0.8394942 0.1605058
Proporción en datos de prueba:
No Si
0.8371041 0.1628959
Como se puede observar, el set de entrenamiento conserva el desbalance original: aproximadamente el 83.9% de los empleados no rotan, mientras que solo el 16.1% sí lo hace. Este es el problema que abordaremos con SMOTE.
Para ello, convertimos todas las variables categóricas en dummies
(requisito necesario para aplicar SMOTE) y posteriormente aplicamos
SMOTE sobre la variable respuesta Rotación con el fin de
balancearla. El objetivo es que la clase minoritaria (“Sí”) alcance el
80% del tamaño de la clase mayoritaria.
receta_smote <- recipe(Rotación ~ ., data = datos_entrenamiento) %>%
step_dummy(all_nominal_predictors()) %>%
step_smote(Rotación, over_ratio = 0.8)
receta_preparada_final <- prep(receta_smote)
datos_entrenamiento_balanceados <- bake(receta_preparada_final, new_data = NULL)
cat("\nProporción en datos de entrenamiento DESPUÉS de SMOTE:\n")
Proporción en datos de entrenamiento DESPUÉS de SMOTE:
No Si
0.5556986 0.4443014
Como se puede ver en la salida, el nuevo set de entrenamiento
datos_entrenamiento_balanceados está ahora mucho más
equilibrado, con la clase “Sí” representando cerca del 45% del total.
Ahora sí, procedemos a estimar el modelo.
Una vez balanceados los datos, hemos construido un modelo de regresión logística. Su propósito es identificar y medir los factores de riesgo y de retención más influyentes en la organización.
modelo_logistico_final <- glm(Rotación ~ . - rotacion_num,
data = datos_entrenamiento_balanceados,
family = binomial(link = "logit"))
summary(modelo_logistico_final)
Call:
glm(formula = Rotación ~ . - rotacion_num, family = binomial(link = "logit"),
data = datos_entrenamiento_balanceados)
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) 1.103e+00 3.116e-01 3.539 0.000401 ***
Ingreso_Mensual -5.358e-05 1.953e-05 -2.743 0.006088 **
Edad -2.597e-02 8.329e-03 -3.118 0.001823 **
Antigüedad -4.740e-02 1.407e-02 -3.370 0.000753 ***
Horas_Extra_Si 1.549e+00 1.334e-01 11.612 < 2e-16 ***
`Viaje de Negocios_No_Viaja` -2.143e+00 2.860e-01 -7.492 6.77e-14 ***
`Viaje de Negocios_Raramente` -9.787e-01 1.517e-01 -6.452 1.10e-10 ***
Estado_Civil_Divorciado 1.367e-02 1.702e-01 0.080 0.935987
Estado_Civil_Soltero 1.079e+00 1.452e-01 7.432 1.07e-13 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
(Dispersion parameter for binomial family taken to be 1)
Null deviance: 2133.6 on 1552 degrees of freedom
Residual deviance: 1712.9 on 1544 degrees of freedom
AIC: 1730.9
Number of Fisher Scoring iterations: 4
El summary del modelo nos proporciona información clave
sobre los coeficientes, su significancia estadística y el ajuste general
del modelo.
Estimate)Esta columna indica la dirección del efecto de cada variable sobre la rotación.
Horas_Extra_Si (+1.549): Confirma que
trabajar horas extra incrementa el riesgo de rotación.Estado_Civil_Soltero (+1.079):
Confirma que ser soltero eleva el riesgo de rotación en comparación con
estar casado.Ingreso_Mensual (-0.00005358),
Edad (-0.02597), Antigüedad
(-0.04740): Confirman que un mayor ingreso, mayor edad
y más antigüedad reducen el riesgo de rotación.Viaje_de_Negocios_No_Viaja (-2.143) y
Viaje_de_Negocios_Raramente
(-0.9787):Pr(>|z|) o
p-valor)La significancia nos indica si una variable tiene una influencia real sobre la rotación, una vez que consideramos el efecto de las demás variables en el modelo. Usando un nivel de significancia estándar (p < 0.05), observamos que:
Variables Altamente Significativas:
Son los predictores con un impacto real y confiable. Todos los
siguientes resultaron altamente significativos (con ** o
***):
Ingreso_MensualEdadAntigüedadHoras_ExtraViaje_de_Negocios (todos los niveles)Estado_Civil (específicamente ser “Soltero/a”)Variable No Significativa:
* Estado_Civil_Divorciado: No existe evidencia estadística
para afirmar que ser divorciado tenga un efecto diferente a estar casado
en la rotación.
Los coeficientes del modelo (Estimate) están en una
escala logarítmica (log-odds), que es difícil de interpretar
directamente. Para facilitar la comprensión, los convertimos a
Odds Ratios (OR), que nos dicen cómo cambia la “chance”
(probabilidad a favor / probabilidad en contra) de rotar.
(Intercept) Ingreso_Mensual
3.0127801 0.9999464
Edad Antigüedad
0.9743682 0.9537078
Horas_Extra_Si `Viaje de Negocios_No_Viaja`
4.7086627 0.1173398
`Viaje de Negocios_Raramente` Estado_Civil_Divorciado
0.3758176 1.0137612
Estado_Civil_Soltero
2.9430361
1 - 0.117).1 - 0.954).1 - 0.974).1 - 0.9999464^1000).En esta sección, evaluaremos el poder predictivo del modelo utilizando la Curva ROC y el Área Bajo la Curva (AUC). El objetivo es medir qué tan bien el modelo puede distinguir entre un empleado que rotará y uno que no.
Para realizar la evaluación, primero debemos aplicar a nuestros
datos_prueba las mismas transformaciones que se aplicaron a
los datos de entrenamiento. La receta_preparada_final que
ya creamos se encarga de este proceso.
# 1. Aplicamos la receta ya preparada para transformar los datos de prueba.
datos_prueba_horneados <- bake(receta_preparada_final, new_data = datos_prueba)
# 2. Generamos las predicciones de probabilidad sobre el conjunto de prueba.
# 'type = "response"' nos da la probabilidad de que un empleado rote.
predicciones_prob <- predict(modelo_logistico_final,
newdata = datos_prueba_horneados,
type = "response")
# 3. Calculamos la curva ROC y el valor AUC.
# Comparamos la Rotación real (del set de prueba preparado) con las probabilidades predichas.
roc_obj <- roc(datos_prueba_horneados$Rotación,
predicciones_prob,
levels = c("No", "Si"))
auc_valor <- auc(roc_obj)
# 4. Imprimimos el resultado y graficamos la curva.
cat(paste("El valor del Área Bajo la Curva (AUC) en los datos de prueba es:", round(auc_valor, 4)))El valor del Área Bajo la Curva (AUC) en los datos de prueba es: 0.7472
plot(roc_obj, main="Curva ROC - Desempeño del Modelo en Datos de Prueba", print.auc=TRUE, col = "#2c7be5", lwd = 2)
El modelo obtiene un valor AUC de 0.7472 en el conjunto
de datos de prueba. Este es un resultado bueno, lo que
indica que el modelo posee una capacidad considerable, es decir, si se
toma al azar un empleado que efectivamente rotará y otro que no, existe
un 74.7% de probabilidad de que el modelo asigne una
puntuación de riesgo más alta al empleado que sí rotará.
Con base en el modelo de regresión logística entrenado, se evaluará un caso hipotético que representa un perfil promedio de la organización:
empleado_nuevo <- tibble(
Ingreso_Mensual = 4250,
Edad = 35,
Antigüedad = 5,
Horas_Extra_Si = 0, # No hace horas extra
`Viaje de Negocios_No_Viaja` = 0,
`Viaje de Negocios_Raramente`= 1, # viaja raramente
Estado_Civil_Divorciado = 0,
Estado_Civil_Soltero = 1, # es soltero
rotacion_num = NA_real_
)
prob_rotacion <- predict(modelo_logistico_final,
newdata = empleado_nuevo,
type = "response")
umbral_optimo <- coords(roc_obj, "best", ret = "threshold", drop = TRUE)
decision <- ifelse(prob_rotacion >= umbral_optimo,
"Se recomienda intervenir para retener al empleado.",
"No es prioritario intervenir.")
cat("Probabilidad estimada de rotación:", round(prob_rotacion, 3), "\n")Probabilidad estimada de rotación: 0.458
Umbral óptimo definido: 0.589
Decisión: No es prioritario intervenir.
El modelo estimó una probabilidad de rotación del 45.8%, valor inferior al umbral óptimo de 58.9%, definido a partir de la curva ROC. Con base en este resultado, la decisión final fue clasificarlo como “No es prioritario intervenir”, sin embargo, sugiere mantener atención preventiva.
Al observar los factores que explican la clasificación, se identifican algunos elementos protectores. La edad de 35 años y la antigüedad de 5 años suelen estar asociadas a menor propensión a cambiar de empleo, al igual que el hecho de no realizar horas extra, lo que podría reflejar un equilibrio saludable entre trabajo y vida personal. Sin embargo, también existen elementos que pueden generar cierta vulnerabilidad. Ser soltero y viajar raramente por motivos laborales son características que, en algunos segmentos de la base de datos, aparecen relacionadas con mayor movilidad laboral. Adicionalmente, un ingreso mensual en rango medio puede dejar espacio a que el empleado considere atractivas ofertas más competitivas.
Este análisis ha permitido construir y validar un modelo de regresión logística con un buen poder predictivo (AUC ≈ 0.75), capaz de identificar los factores clave que impulsan la rotación de empleados en la organización. Los resultados demuestran que la rotación no es un evento aleatorio, sino una consecuencia de condiciones laborales y perfiles de empleados específicos y medibles.
El modelo reveló varios factores determinantes, de los cuales destacan los siguientes por su alto impacto:
Sobrecarga Laboral (Horas_Extra):
Es, con diferencia, el principal factor de riesgo. Un
empleado que trabaja horas extra tiene sus chances de rotar
multiplicadas por un factor superior a 4, convirtiéndolo en la “bandera
roja” más importante.
Desgaste por Viajes
(Viaje de Negocios): Viajar frecuentemente es el
segundo factor de riesgo más importante, triplicando las
chances de que un empleado se vaya en comparación con uno que
no viaja.
Factores de Retención (Ingreso_Mensual y
Antigüedad): Un mayor salario y más años en la
compañía actúan como las anclas de retención más
fuertes. Cada año de antigüedad y cada aumento salarial reducen
significativamente la probabilidad de que un empleado busque nuevas
oportunidades.
Perfil Demográfico (Estado_Civil):
Los empleados solteros muestran una propensión a la rotación 2.5
veces mayor que los empleados casados, lo que indica una mayor
movilidad en este segmento.
Con base en la evidencia estadística, se recomienda implementar una estrategia de retención proactiva centrada en las siguientes tres áreas prioritarias:
Dado que es el factor de mayor impacto, las primeras acciones deben centrarse aquí.
Para contrarrestar el desgaste asociado a los viajes frecuentes se proponen dos líneas de acción:
Con el fin de potenciar los factores de retención más efectivos, se recomienda: