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 labora: 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, ente mayor edad menor sera la rotacion (la poblacion joven presentará mayor rotación.)
df1 <- rotacion[, c("Rotación",
"Satisfación_Laboral",
"Equilibrio_Trabajo_Vida",
"Horas_Extra",
"Ingreso_Mensual",
"Distancia_Casa",
"Edad")]
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")
Obsrvamos que el 71.7% de los empleados no trabaja horas extras, lo que se relaciona cn el equilibrio entre la vida personal y el trabajo, esto evita el sobrecarga general, estres y la rotacion. Mintras 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.
## 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.
## 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.
## 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.
ggplot(df1, aes(x = Rango_Ingreso, fill = factor(y))) +
geom_bar(position = "fill") +
scale_y_continuous(labels = percent) +
labs(title = "Rotación según Ingreso Mensual",
x = "Rango de Ingreso (intervalos en pesos)",
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))
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.
## 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.
El primer modelo se realizara teniendo en cuenta la particion del dataset en training (70) y test (30) y realizando undersampling parcial, para no perder demasiada informacion y evitar que el modelo solo aprenda de la clase mayoritaria
##
## 0 1
## 864 166
Aqui vemos que sin haber aplicado la técnica, la clase mayoritaria “no rotan” (0) tiene 864 datos.
##
## 0 1
## 300 166
Despues de haber ejecutado el undersampling vemos que ahora sigeu existiendo la clase mayoritaria, pero no tiene ese gran desbalance.
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.
## 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.
## Area under the curve: 0.787
Con una area bajo la curva de 0.787 el modelo tiene un buen poder de discriminacion.
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 qu eno rotan), entre mas cercano a 1, el modelo tiene una muy buena precision.
## [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"
En este modelo, no se realiza ninguna particion a los datos y se realiza con el dataset completo, lo que no nos permitiria realizar validacion cruzada, para evaluar el modelo, pero si las otras metricas.
df3 <- rotacion[, c("Rotación",
"Satisfación_Laboral",
"Equilibrio_Trabajo_Vida",
"Horas_Extra",
"Ingreso_Mensual",
"Distancia_Casa",
"Edad")]
# Variable dependiente
df3$Rotación <- ifelse(df1$Rotación == "Si", 1, 0)
# Variables predictoras
df3$Satisfación_Laboral <- as.factor(df3$Satisfación_Laboral)
df3$Equilibrio_Trabajo_Vida <- as.factor(df3$Equilibrio_Trabajo_Vida)
df3$Horas_Extra <- ifelse(df3$Horas_Extra == "Si", 1, 0)
modelo1 <- glm(Rotación ~ Satisfación_Laboral + Equilibrio_Trabajo_Vida + Horas_Extra +
Ingreso_Mensual + Distancia_Casa + Edad,
data = df3, family = binomial)
summary(modelo1)
##
## Call:
## glm(formula = Rotación ~ Satisfación_Laboral + Equilibrio_Trabajo_Vida +
## Horas_Extra + Ingreso_Mensual + Distancia_Casa + Edad, family = binomial,
## data = df3)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 9.673e-01 4.500e-01 2.149 0.03161 *
## Satisfación_Laboral2 -4.429e-01 2.310e-01 -1.917 0.05520 .
## Satisfación_Laboral3 -4.578e-01 2.044e-01 -2.240 0.02507 *
## Satisfación_Laboral4 -1.060e+00 2.204e-01 -4.810 1.51e-06 ***
## Equilibrio_Trabajo_Vida2 -9.075e-01 3.051e-01 -2.974 0.00294 **
## Equilibrio_Trabajo_Vida3 -1.161e+00 2.829e-01 -4.105 4.05e-05 ***
## Equilibrio_Trabajo_Vida4 -7.508e-01 3.497e-01 -2.147 0.03179 *
## Horas_Extra 1.504e+00 1.570e-01 9.577 < 2e-16 ***
## Ingreso_Mensual -9.793e-05 2.511e-05 -3.900 9.63e-05 ***
## Distancia_Casa 2.787e-02 9.200e-03 3.030 0.00245 **
## Edad -4.001e-02 1.016e-02 -3.939 8.18e-05 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 1298.6 on 1469 degrees of freedom
## Residual deviance: 1105.4 on 1459 degrees of freedom
## AIC: 1127.4
##
## Number of Fisher Scoring iterations: 5
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.
## GVIF Df GVIF^(1/(2*Df))
## Satisfación_Laboral 1.032089 3 1.005278
## Equilibrio_Trabajo_Vida 1.026824 3 1.004422
## Horas_Extra 1.042001 1 1.020785
## Ingreso_Mensual 1.233572 1 1.110663
## Distancia_Casa 1.009816 1 1.004896
## Edad 1.242080 1 1.114486
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.
probabilidades <- predict(modelo1, type = "response")
pred_clases <- ifelse(probabilidades > 0.5, 1, 0)
# 2. Convertir a factor con mismos niveles
pred_clases_factor <- factor(pred_clases, levels = c(0, 1))
real_factor <- factor(df3$Rotación, levels = c(0, 1))
# 3. Matriz de confusión
cm <- confusionMatrix(pred_clases_factor, real_factor, positive = "1")
print(cm)
## Confusion Matrix and Statistics
##
## Reference
## Prediction 0 1
## 0 1213 201
## 1 20 36
##
## Accuracy : 0.8497
## 95% CI : (0.8304, 0.8676)
## No Information Rate : 0.8388
## P-Value [Acc > NIR] : 0.1354
##
## Kappa : 0.1962
##
## Mcnemar's Test P-Value : <2e-16
##
## Sensitivity : 0.15190
## Specificity : 0.98378
## Pos Pred Value : 0.64286
## Neg Pred Value : 0.85785
## Prevalence : 0.16122
## Detection Rate : 0.02449
## Detection Prevalence : 0.03810
## Balanced Accuracy : 0.56784
##
## '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.
1213 casos estan clasificados correctamente en la clase 0 (no rotan)
20 casos son falsos positivos, osea, clase 0 (no rotan) fueron clasificados como 1 (si rotan)
201 casos son falsos negativos, osea, clase 1 (si rotan) fueron clasificados como 0 (no rotan)
36 casos estan clasificados de manera correcta en la clase 1 (si rotan)
Métricas clave
Accuracy = 0.8497 (84.97%)
El modelo acierta casi 9 de cada 10 casos.
Kappa = 0.1962
Esto indica que el modelo tiene una baja capacidad de discriminacion.
Sensibilidad (Recall) = 0.1519
El modelo casi no detecta los casos positivos (clase 1, si rotan).
Especificidad = 0.9837
Detecta bien a la clase 0 (negativos).
El modelo es capaz de detectar el 98.37% de los negativos (clase 0, no rotan).
Precisión (PPV) = 0.6428
Esto nos indica que solo el 64.28% de los casos que el modelo predice como verdaderos, son realmente verdaderos.
Balanced Accuracy = 0.5678
Promedia sensibilidad y especificidad → da una medida más justa en contextos desbalanceados y en este contesto el modelo esta desbalanceado, ya que predice de manera clara quien no rota, pero no de manera clara para los que si rotan.
##
## Call:
## roc.default(response = df3$Rotación, predictor = probabilidades)
##
## Data: probabilidades in 1233 controls (df3$Rotación 0) < 237 cases (df3$Rotación 1).
## Area under the curve: 0.7673
Con una area bajo la curva de 0.7673 (76.73%) el modelo tiene un buen poder de discriminacion.
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 qu eno rotan), entre mas cercano a 1, el modelo tiene una muy buena precision.
# Calcular F1 Score
f1 <- F1_Score(y_true = df3$Rotación,
y_pred = pred_clases,
positive = "1")
print(f1)
## [1] 0.2457338
El modelo no logra un equilibrio entre identificar los casos positivos (Recall) y no equivocarse demasiado (Precision).
# Calcular la curva Precision-Recall
pr1 <- pr.curve(scores.class0 = probabilidades[df3$Rotación == 1],
scores.class1 = probabilidades[df3$Rotación == 0],
curve = TRUE)
# Mostrar el valor del AUC bajo la curva PR
print(paste("AUC (PR curve):", round(pr1$auc.integral, 4)))
## [1] "AUC (PR curve): 0.4431"
El modelo tiene buen Accuracy global, debido a que predice bien la clase mayoritaria (no rotación), sain embargo la curva PR muestra que es muy débil en la clase minoritaria (rotación = 1).
El mejor modelo es en el cual se realiza la particion de los datos y esto lo elegimos con el criterio de informacion de Akaike (AIC), que permite seleccionar el mejor modelo al encontrar un equilibrio entre la complejidad del modelo y la bonda de ajuste, ademas se tuvieron en cuenta los criterios Cox & Snell, Nagelkerke y McFadden.
Modelo de partición
## Chi2 90.02204
## Df 10
## Sig. 5.329071e-15
## Cox and Snell Index 0.1756667
## Nagelkerke Index 0.2412576
## McFadden's R2 0.1483232
Modelo sin particion
## Chi2 193.1868
## Df 10
## Sig. 0
## Cox and Snell Index 0.1231502
## Nagelkerke Index 0.2099316
## McFadden's R2 0.1487674
Ambos modelos son significativos (p < 0.001). El Chi cuadrado es mayor en el segundo modelo (193 vs. 89), lo que indica mayor mejora si no hay ningun modelo Sin embargo, el Nagelkerke R² es superior en el primero (0.239 vs. 0.210), lo que muestra mayor poder explicativo. El McFadden R² es muy similar en ambos (0.15), indicando ajuste aceptable. En conclusión, aunque el segundo modelo mejora más respecto al nulo, el primer modelo es ligeramente mejor por su mayor capacidad explicativa.
Los efectos marginales, permiten realizar una interpretacion mas clara del modelo, analizando el impacto de las variables explicativas frente a la variable target, permitiendo conocer, como los pequeños cambios de una variable independiente afectan la variable dependiente.
factor | AME | SE | z | p | lower | upper |
---|---|---|---|---|---|---|
Distancia_Casa | 0.0053 | 0.0024 | 2.2504 | 0.0244 | 0.0007 | 0.0100 |
Edad | -0.0068 | 0.0025 | -2.7417 | 0.0061 | -0.0117 | -0.0019 |
Equilibrio_Trabajo_Vida2 | -0.0427 | 0.0943 | -0.4524 | 0.6510 | -0.2275 | 0.1422 |
Equilibrio_Trabajo_Vida3 | -0.1540 | 0.0870 | -1.7708 | 0.0766 | -0.3245 | 0.0165 |
Equilibrio_Trabajo_Vida4 | -0.0379 | 0.1055 | -0.3594 | 0.7193 | -0.2448 | 0.1689 |
Horas_Extra | 0.2291 | 0.0364 | 6.2980 | 0.0000 | 0.1578 | 0.3004 |
Ingreso_Mensual | 0.0000 | 0.0000 | -3.3189 | 0.0009 | 0.0000 | 0.0000 |
Satisfación_Laboral2 | -0.1408 | 0.0629 | -2.2392 | 0.0251 | -0.2639 | -0.0176 |
Satisfación_Laboral3 | -0.0811 | 0.0583 | -1.3906 | 0.1643 | -0.1954 | 0.0332 |
Satisfación_Laboral4 | -0.1955 | 0.0557 | -3.5125 | 0.0004 | -0.3046 | -0.0864 |
Distancia al trabajo: Por cada kilómetro adicional, incrementa la probabilidad de rotación en 0.37%, aunque no es estadísticamente significativo (p = 0.12).
Edad: Por cada año adicional reduce la probabilidad de rotación en 0.39% puntos porcentuales, aunque no es estadísticamente significativo (p = 0.12).
Equilibrio Trabajo-Vida baja (2): Con respecto a un Equilibrio Trabajo-Vida muy baja (1), el incremento a un Equilibrio Trabajo-Vida baja (2), reduce la probabilidad de rotar en 25.6% (p < 0.05).
Equilibrio Trabajo-Vida alta (3): Con respecto a un Equilibrio Trabajo-Vida muy baja (1), el incremento a un Equilibrio Trabajo-Vida alto (3), reduce la probabilidad de rotar en 33.76% (p < 0.001).
Equilibrio Trabajo-Vida muy alta (4): Con respecto a un Equilibrio Trabajo-Vida muy baja (1), el incremento a un Equilibrio Trabajo-Vida muy alta (4), reduce la probabilidad de rotar en 24.23% (p < 0.05).
Horas Extra: Por cada hora adicional aumenta la probabilidad de rotación en 25.83%, siendo altamente significativo (p < 0.001).
Ingreso Mensual: El incremento de 1 peso al ingreso del empleado, reduce la probabilidad de rotacion, aunque tiene un efecto positivo muy pequeño es estadísticamente significativo (p < 0.01).
Satisfacción Laboral baja (2): Con respecto a la Satisfacción Laboral muy baja (1), el incremento a una Satisfacción Laboral baja (2), reduce la probabilidad de rotación en 12.18%, aunque con significancia marginal (p = 0.059).
Satisfacción Laboral alto (3): Con respecto a la Satisfacción Laboral muy baja (1), el incremento a una Satisfacción Laboral alta (3), reduce la probabilidad en 11.7% (p < 0.05).
Satisfacción Laboral muy alto (4): Con respecto a la Satisfacción Laboral muy baja (1), el incremento a una Satisfacción Laboral muy alta (4), reduce la probabilidad en 17.54% (p < 0.01).
Realizamos la prediccion de rotacion o no, para un empleado con un nivel de satisfaccion laboral bajo (2), equilibrio entre trabajo y vida personal bajo (2). Este trabajador realiza horas extras, tiene un ingreso de 10.000, la distancia del trabajo a la casa son de 10 km y tiene 28 años de edad.
empleado_nuevo <- data.frame(
Satisfación_Laboral = factor(2,
levels = levels(test$Satisfación_Laboral)),
Equilibrio_Trabajo_Vida = factor(2,
levels = levels(test$Equilibrio_Trabajo_Vida)),
Horas_Extra = 1, # Sí hace horas extra
Ingreso_Mensual = 10000, # Ingreso mensual
Distancia_Casa = 10, # Vive a 15 km
Edad = 28 # Tiene 25 años
)
# Probabilidad de rotación
prob_rotacion <- predict(modelo, newdata = empleado_nuevo, type = "response")
print(paste("Probabilidad de rotación:", round(prob_rotacion, 3)))
## [1] "Probabilidad de rotación: 0.544"
clasificacion <- ifelse(prob_rotacion >= 0.5, "En riesgo de rotación", "No en riesgo")
print(paste("Clasificación:",clasificacion))
## [1] "Clasificación: En riesgo de rotación"
Este empleado tiene una probabilidad de rotar
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
https://www.icesi.edu.co/editorial/intro-clasificacion-web/EvLogit.html