Este documento busca comprender y prever los factores que influyen en la rotación de empleados entre distintos cargos. Esto se hará mediante los 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.
Desde la gerencia se 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.
Según Marroquín y Cruz (2020), la rotación de personal en las empresas colombianas está influenciada por diversos factores, como la insatisfacción personal (35.3%), selección incorrecta (35.3%), baja remuneración (35.3%), condiciones laborales (41.2%), motivación (41.2%), bajas por motivos personales (47.1%), entre otros.
Cadena, N., Posada, N., Arana, M., & Torres, S. (2022) trataron el tema de las renuncias voluntarias en empresas de produccion y encontraron que los empleados tienden a buscar mejores oportunidades, tambien la existencia de bajas remuneraciones, calidad de vida y el horario laboral son las causas y consecuencias de estas empresas en la ciudad de Bogotá.
Tambien Poveda Madrid, E. C., Díaz Álvarez, M. I., & Puentes Vargas, M. P. (2025) definieron un grupo etario (23 - 30 años) e identificaron los problemas que provocan la rotacion a este grupo, como los malos procesos de reclutamiento, condiciones laborales (horarios, ubicacion fisica, tipo de contrato, instalaciones), ambiente laboral, reconocimentos, entre otros son los mayores causantes de la desercion laboral
Y para finalizar Herrity, J. (2025) del portal Indeed, nos indica que 9 causas de la rotacion de personal, como overwork (trabajo excesivo), estilo de gestion inconsistente, falta de reconocimiento, pocas oportunidades de crecimiento, bajos salarios y bajos aumentos, entre otros.
Teniendo en cuenta la informacion recogida de la literatura, decidimos trabajar el modelo bajo las siguentes variables:
Rotación: Variable dicotomica que indica si hay o no rotacion de personal
Satisfación laboral: Medida que indica cuan satisfecho esta el empleado en la empresa
Hipótesis: Se espera un signo positivo, que a mayor satisfacion, menor es la rotación de los empleados.
Equilibrio trabajo - vida: Nos indica el nivel de satisfacion de los empleados entre su vida personal y la laboral.
Hipótesis: Se espera un signo positivo, a mayor equilibrio entre trabajo y vida, menor será la rotación de los empleados.
Horas_Extra: Cantidad de horas hechas despues de la jornada laboral.
Hipótesis: Se espera un signo negativo, si la persona realiza horas extras aumentará la rotación.
Ingreso_Mensual: Salario percibido por el empleado.
Hipótesis: Se espera un signo negativo, a mayor ingreso menor rotación.
Distancia a casa: Kilometros que hay desde la casa del empleado hasta el sitio de trabajo.
Hipótesis: Se espera un signo positivo, entre mayor sea la distancia entre la casa y el trabajo, aumentará la rotación.
Edad: Edad del empleado.
Hipótesis: Se espera un signo negativo, entre mayor edad menor sera la rotacion (la poblacion joven presentará mayor rotación.)
A continuacion realizaremos un analisis univariado y bivariado de las variables que hemos seleccionado.
plot_ly(df1, labels = ~Rotación, type = "pie",
marker = list(colors = c("purple", "gold")),
textinfo = "label+percent",
insidetextorientation = "radial") %>%
layout(title = "Distribución de la Rotación")
Este grafico nos indica que la empresa cuenta relativamente con una alta tasa de retencion de personal (83.9%), sin embargo existe un grupo de empleados (16.1%) que rota, lo que puede significar un problema asociados a los costos de reclutacion, entrenamiento y la perdida de la memoria y conocimiento institucional.
plot_ly(df1,
labels = ~Satisfación_Laboral,
type = "pie",
marker = list(colors = c("purple", "green", "gold", "blue")),
textinfo = "label+percent",
title = "Distribución de Satisfacción Laboral")
Este grafico nos indica los 4 niveles de la satisfacion laboral:
# Equilibrio trabajo - vida
plot_ly(df1, labels = ~Equilibrio_Trabajo_Vida, type = "pie",
marker = list(colors = c("purple", "green", "gold", "blue")),
textinfo = "label+percent") %>%
layout(title = "Distribución del Equilibrio trabajo - vida")
Podemos observar que el 71,1% de los empleados perciben un buen equilibrio entre el trabajo y la vida personal, que contribuye a una alta permanencia de los empleados. Por otro lado, el 28.8% de los empleados perciben un regular o mal balance entre la vida personal y el trabajo, este grupo tiende a tener mas estres y desmotivación lo que los hace mas propensos a rotar.
plot_ly(df1, labels = ~Horas_Extra, type = "pie",
marker = list(colors = c("purple", "gold")),
textinfo = "label+percent") %>%
layout(title = "Distribución de las Horas Extras")
Observamos que el 71.7% de los empleados no trabaja horas extras, lo que se relaciona con el equilibrio entre la vida personal y el trabajo, esto evita el sobrecarga general, estres y la rotacion. Mientras que un parte de los empleados 28.3, trabaja horas extras que puede ser por la alta demanda de trabajo o la falta de personal lo que hace que algunos trabajadores se les aumenta la carga y no tenga una equilibrio trabajo y vida personal lo que genera un incremento de la rotación.
summary(df1$Ingreso_Mensual)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 1009 2911 4919 6503 8379 19999
plot_ly(df1, x = ~Ingreso_Mensual,
type = "histogram", nbinsx = 30,
marker = list(color = 'purple')) %>%
layout(title = "Distribución del Ingreso Mensual",
xaxis = list(title = "Ingreso Mensual"),
yaxis = list(title = "Frecuencia"))
Podemos observar que hay una concentacion entre salarios medio - bajos, donde el 50% de los empleados gana menos de 4.919, pero el promedio de ingresos de la empresa es de 6.503, nos indica la presencia de salarios bastante altos.
summary(df1$Distancia_Casa)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 1.000 2.000 7.000 9.193 14.000 29.000
# Distancia a casa
plot_ly(df1, x = ~Distancia_Casa,
type = "histogram", nbinsx = 20,
marker = list(color = 'gold')) %>%
layout(title = "Distribución de la Antigüedad",
xaxis = list(title = "Distancia a casa (km"),
yaxis = list(title = "Frecuencia"))
Aqui podemos identificar que la mayoria de empleados vivien relativamente cerca del lugar del trabajo (1 a 5 km), este grupo de empleados tiende a tener una menor probabilidad de rotar, mientras que el extremo contrario distancias mayores a 15 km tiende a generar mayor rotacion debido a los costos y tiempos de transporte que afectan el equilibrio entre trabajo y vida personal.
summary(df1$Edad)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 18.00 30.00 36.00 36.92 43.00 60.00
plot_ly(df1, x = ~Edad,
type = "histogram", nbinsx = 20,
marker = list(color = 'green')) %>%
layout(title = "Distribución de la Edad",
xaxis = list(title = "Edad"),
yaxis = list(title = "Frecuencia"))
Podemos presenciar que hay una fuerza joven adulta, donde gran parte de sus empleados tienen entre 30 y 40 años y para este grupo etario existe una menor rotacion a diferencia de las personas mas jovenes 18 a 28 y de 50 a 64 que tienden a tener una rotacion mucho mas alta.
Ahora realizaremos una mira de las variables explicativas, contra la variable a explicar
### 1. Satisfacción Laboral vs Rotación
df_prop1 <- df1 %>%
count(Satisfación_Laboral, y) %>%
group_by(Satisfación_Laboral) %>%
mutate(prop = n / sum(n),
label = scales::percent(prop, accuracy = 1))
g1 <- ggplot(df_prop1, aes(x = factor(Satisfación_Laboral),
y = prop, fill = factor(y))) +
geom_bar(stat = "identity", position = "fill") +
geom_text(aes(label = label),
position = position_fill(vjust = 0.5),
color = "white", size = 4) +
scale_y_continuous(labels = scales::percent) +
labs(title = "Rotación según Satisfacción Laboral",
x = "Nivel de Satisfacción",
y = "Proporción",
fill = "Rotación (1=Sí, 0=No)")
g1
El análisis muestra que la satisfacción laboral tiene una relación directa con la permanencia de los empleados en la organización. A medida que aumenta el nivel de satisfacción, la proporción de rotación disminuye. Observamos que en los niveles más bajos de satisfacción (1 y 2), se presenta una rotación del 23% y 16% respectivamente, en los niveles más altos de satisfacción (3 y 4) se reducen los valores de rotación al 17% y 11% respectivamente. Esto muestra la evidencia que los empleados más satisfechos tienen menor probabilidad de abandonar la empresa.
df_prop2 <- df1 %>%
count(Equilibrio_Trabajo_Vida, y) %>%
group_by(Equilibrio_Trabajo_Vida) %>%
mutate(prop = n / sum(n),
label = scales::percent(prop, accuracy = 1))
g2 <- ggplot(df_prop2, aes(x = factor(Equilibrio_Trabajo_Vida),
y = prop, fill = factor(y))) +
geom_bar(stat = "identity", position = "fill") +
geom_text(aes(label = label),
position = position_fill(vjust = 0.5),
color = "white", size = 4) +
scale_y_continuous(labels = scales::percent) +
labs(title = "Rotación según Equilibrio entre Trabajo y Vida",
x = "Nivel de Equilibrio",
y = "Proporción",
fill = "Rotación (1=Sí, 0=No)")
g2
Este gráfico muestra la relación entre el equilibrio entre trabajo y vida personal y la rotación de empleados. Se observa que cuando los trabajadores perciben un bajo equilibrio (nivel 1), la rotación es más alta (31%), lo que sustenta que entre mas complejo sea para el empleado balancear su vida, mas alta es la probabilidad de que los empleados abandonen la empresa. Por otro lado, a medida que mejora el equilibrio (niveles 2, 3 y 4), la rotación disminuye a un rango entre el 18%, 17% y 14% respectivamente.
### 3. Horas Extra vs Rotación
df_prop3 <- df1 %>%
count(Horas_Extra, y) %>%
group_by(Horas_Extra) %>%
mutate(prop = n / sum(n),
label = scales::percent(prop, accuracy = 1))
g3 <- ggplot(df_prop3, aes(x = factor(Horas_Extra),
y = prop, fill = factor(y))) +
geom_bar(stat = "identity", position = "fill") +
geom_text(aes(label = label),
position = position_fill(vjust = 0.5),
color = "white", size = 4) +
scale_y_continuous(labels = scales::percent) +
labs(title = "Rotación según Horas Extra",
x = "Horas Extra",
y = "Proporción",
fill = "Rotación (1=Sí, 0=No)")
g3
Este gráfico muestra cómo las horas extra encuentran una correspondencia con la rotación, los empleados que realizan horas extras el 31% abandona la empresa, mientras que el grupo que no hacen horas extra, solo un 10% rota. Esto nos hace deducir que el trabajo extra genera desgaste, afectando el equilibrio, por enden aumentando la probabilidad de rotación.
A medida que el ingreso mensual aumenta, la rotación de empleados disminuye de forma clara y progresiva. Esto sugiere que el salario es un factor determinante de la permanencia, y que los empleados con menores ingresos son los más propensos a dejar la organización.
ggplot(df1, aes(x = Rango_Distancia, fill = factor(y))) +
geom_bar(position = "fill") +
scale_y_continuous(labels = percent) +
labs(title = "Rotación según Distancia a Casa",
x = "Intervalos de Distancia",
y = "Proporción",
fill = "Rotación (1=Sí, 0=No)") +
geom_text(stat = "count",
aes(label = scales::percent(..count../tapply(..count..,..x..,sum)[..x..])),
position = position_fill(vjust = 0.8))
Existe una relación positiva entre la distancia a casa y la rotación laboral, los empleados que viven más lejos de la empresa presentan una mayor probabilidad de renunciar. Esto sugiere que la ubicación geográfica y el tiempo de desplazamiento podrían ser factores importantes a considerar en las estrategias de retención.
ggplot(df1, aes(x = Rango_Edad, fill = factor(y))) +
geom_bar(position = "fill") +
scale_y_continuous(labels = percent) +
labs(title = "Rotación según Edad",
x = "Intervalos de Edad",
y = "Proporción",
fill = "Rotación (1=Sí, 0=No)") +
geom_text(stat = "count",
aes(label = scales::percent(..count../tapply(..count..,..x..,sum)[..x..])),
position = position_fill(vjust = 0.8))
La rotación está fuertemente concentrada en los empleados más jóvenes, quienes son más propensos a dejar la empresa. En cambio, los trabajadores de mediana edad (39-50 años) muestran la mayor estabilidad. Esto sugiere que las estrategias de retención deberían enfocarse especialmente en los empleados jóvenes.
# Separo las variables numericas del dataset
num_vars <- df1[, c("Ingreso_Mensual", "Distancia_Casa", "Edad")]
# Matriz de correlación
cor_matrix <- cor(num_vars, use = "complete.obs", method = "pearson")
print(cor_matrix)
## Ingreso_Mensual Distancia_Casa Edad
## Ingreso_Mensual 1.00000000 -0.017014445 0.497753202
## Distancia_Casa -0.01701444 1.000000000 -0.001559109
## Edad 0.49775320 -0.001559109 1.000000000
corrplot(cor_matrix, method = "color", type = "upper",
tl.col = "black", tl.srt = 45, addCoef.col = "black")
Aqui podemos notar que la unica asociasion relevante es la edad y el ingreso, y es que los empleados mas viejos tienden a tener mayores salarios debido a su experiencia.
# Correlacion variables categoricas
# Chi-cuadrado entre Satisfacción y Equilibrio
chisq.test(table(df1$Satisfación_Laboral, df1$Equilibrio_Trabajo_Vida))
##
## Pearson's Chi-squared test
##
## data: table(df1$Satisfación_Laboral, df1$Equilibrio_Trabajo_Vida)
## X-squared = 6.5765, df = 9, p-value = 0.6811
# Chi-cuadrado entre Satisfacción y Horas Extra
chisq.test(table(df1$Satisfación_Laboral, df1$Horas_Extra))
##
## Pearson's Chi-squared test
##
## data: table(df1$Satisfación_Laboral, df1$Horas_Extra)
## X-squared = 3.6881, df = 3, p-value = 0.2972
# Chi-cuadrado entre Equilibrio y Horas Extra
chisq.test(table(df1$Equilibrio_Trabajo_Vida, df1$Horas_Extra))
##
## Pearson's Chi-squared test
##
## data: table(df1$Equilibrio_Trabajo_Vida, df1$Horas_Extra)
## X-squared = 2.3835, df = 3, p-value = 0.4967
Podemos observar que ninguna de las variables categoricas presenta relacionan entre si, ya que todos sus coeficientes p, son mayores al nivel de 0.05 (5%).
Para responder la pregunta de cuales son los factores que mas influyen en la rotacion, se realiza un modelo logit (logistico) para determinar si las hipotesis son correctas y el modelo puedo predecir la rotación.
modelo <- glm(Rotación ~ Satisfación_Laboral + Equilibrio_Trabajo_Vida + Horas_Extra +
Ingreso_Mensual + Distancia_Casa + Edad,
data = train_down_partial, family = binomial)
summary(modelo)
##
## Call:
## glm(formula = Rotación ~ Satisfación_Laboral + Equilibrio_Trabajo_Vida +
## Horas_Extra + Ingreso_Mensual + Distancia_Casa + Edad, family = binomial,
## data = train_down_partial)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 1.637e+00 6.560e-01 2.495 0.012594 *
## Satisfación_Laboral2 -7.181e-01 3.281e-01 -2.188 0.028650 *
## Satisfación_Laboral3 -4.034e-01 2.910e-01 -1.386 0.165635
## Satisfación_Laboral4 -1.031e+00 3.015e-01 -3.420 0.000627 ***
## Equilibrio_Trabajo_Vida2 -2.117e-01 4.665e-01 -0.454 0.650038
## Equilibrio_Trabajo_Vida3 -7.955e-01 4.355e-01 -1.827 0.067747 .
## Equilibrio_Trabajo_Vida4 -1.880e-01 5.229e-01 -0.360 0.719152
## Horas_Extra 1.221e+00 2.221e-01 5.496 3.88e-08 ***
## Ingreso_Mensual -1.016e-04 3.169e-05 -3.206 0.001347 **
## Distancia_Casa 2.843e-02 1.286e-02 2.211 0.027060 *
## Edad -3.643e-02 1.366e-02 -2.667 0.007658 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 606.93 on 465 degrees of freedom
## Residual deviance: 516.91 on 455 degrees of freedom
## AIC: 538.91
##
## Number of Fisher Scoring iterations: 4
El factor de inflación de varianza, nos indica en el modelo logit realizado, si existe multicolinealidad en el modelo, esto se da cuando los valores de VIF estan por encima de 10, y hacemos referencia que cuando esta entre 5 y 10 el modelo presenta una multicolinealidad moderada y por debajo, significa que las variables del modelo no presentan relaciones entre si.
vif(modelo)
## GVIF Df GVIF^(1/(2*Df))
## Satisfación_Laboral 1.054592 3 1.008898
## Equilibrio_Trabajo_Vida 1.058099 3 1.009457
## Horas_Extra 1.037625 1 1.018639
## Ingreso_Mensual 1.272611 1 1.128101
## Distancia_Casa 1.026734 1 1.013279
## Edad 1.300465 1 1.140379
Esto indica que no existe multicolinealidad significativa en el modelo.
Una vez tenemos claro que el modelo esta especificado de manera correcta, con el dataset de test, realizamos las predicciones y calculamos la matriz de confusion.
# Predecir probabilidades (por defecto glm devuelve log-odds, usamos type="response")
pred_probs <- predict(modelo, newdata = test, type = "response")
pred_class <- ifelse(pred_probs >= 0.5, 1, 0)
confusionMatrix(as.factor(pred_class), as.factor(test$Rotación), positive = "1")
## Confusion Matrix and Statistics
##
## Reference
## Prediction 0 1
## 0 312 31
## 1 57 40
##
## Accuracy : 0.8
## 95% CI : (0.7595, 0.8364)
## No Information Rate : 0.8386
## P-Value [Acc > NIR] : 0.986583
##
## Kappa : 0.3562
##
## Mcnemar's Test P-Value : 0.007699
##
## Sensitivity : 0.56338
## Specificity : 0.84553
## Pos Pred Value : 0.41237
## Neg Pred Value : 0.90962
## Prevalence : 0.16136
## Detection Rate : 0.09091
## Detection Prevalence : 0.22045
## Balanced Accuracy : 0.70445
##
## 'Positive' Class : 1
##
La matriz de confusion nos indica si los datos fueron correctamente clasificados o no, a continuacion describiremos lo que nos muestra la matriz.
312 casos estan clasificados correctamente en la clase 0 (no rotan)
57 casos son falsos positivos, osea, clase 0 (no rotan) fueron clasificados como 1 (si rotan)
31 casos son falsos negativos, osea, clase 1 (si rotan) fueron clasificados como 0 (no rotan)
40 casos estan clasificados de manera correcta en la clase 1 (si rotan)
Métricas clave
Accuracy = 0.80 (80%)
El modelo acierta 8 de cada 10 casos.
Kappa = 0.3562
Esto indica que el modelo es justo y es mejor con respecto a adivinar
Sensibilidad (Recall) = 0.563
El modelo es capaz de detectar el 56% de los positivos (clase 1, si rotan).
Especificidad = 0.846
Detecta bien a la clase 0 (negativos).
El modelo es capaz de detectar el 84% de los negativos (clase 0, no rotan).
Precisión (PPV) = 0.412
Esto nos indica que solo el 41% de los casos que el modelo predice como verdaderos, son realmente verdaderos.
Balanced Accuracy = 0.704
Promedia sensibilidad y especificidad → da una medida más justa en contextos desbalanceados.
roc_obj <- roc(test$Rotación, pred_probs)
## Setting levels: control = 0, case = 1
## Setting direction: controls < cases
auc(roc_obj) # Área bajo la curva
## Area under the curve: 0.787
Con una area bajo la curva de 0.787 el modelo tiene un buen poder de discriminacion.
plot(roc_obj, col = "blue", lwd = 2, main = "Curva ROC - Logistic Regression")
Aqui podemos observar la grafica de la curva de ROC, con un corte predeterminado en 0.5
Es una medida que permite identificar correctamente los casos positivos (empleados que no rotan), entre mas cercano a 1, el modelo tiene una muy buena precision.
F1_Score(y_true = as.factor(test$Rotación),
y_pred = as.factor(pred_class),
positive = "1")
## [1] 0.4761905
# Calcular la curva Precision-Recall
pr <- pr.curve(scores.class0 = pred_probs[test$Rotación == 1],
scores.class1 = pred_probs[test$Rotación == 0],
curve = TRUE)
# Mostrar el valor del AUC bajo la curva PR
print(paste("AUC (PR curve):", round(pr$auc.integral, 4)))
## [1] "AUC (PR curve): 0.4354"
# Graficar la curva PR
plot(pr, main = "Curva Precision-Recall", col = "darkred", lwd = 2)
Marroquín, A. Y., & Cruz, F. N. (2020). Hechos que provocan la rotación de personal en las empresas colombianas a partir del año 2020.
Cadena, N., Posada, N., Arana, M., & Torres, S. (2022). Causas y consecuencias de la renuncia voluntaria en las empresas de producción en Bogotá.
Poveda Madrid, E. C., Díaz Álvarez, M. I., & Puentes Vargas, M. P. (2025). Estrategias de retención de empleados entre 23 y 30 años debido a la alta rotación laboral en Bogotá para estas edades
Herrity, J. (2025, marzo 26). 9 causes of high employee turnover and how to prevent it. Indeed Career Guide. https://www.indeed.com/career-advice/career-development/high-employee-turnover