En el contexto organizacional, comprender los factores que influyen en la rotación de personal es clave para fortalecer la retención del talento humano. A partir de un conjunto de datos históricos, esta actividad tiene como objetivo desarrollar un modelo de regresión logística que permita predecir la probabilidad de que un empleado rote y, a su vez, identificar las variables que más inciden en dicha decisión. El análisis permitirá proponer estrategias basadas en evidencia que ayuden a reducir la rotación, mejorar las condiciones laborales y tomar decisiones informadas en la gestión de recursos humanos. A continuación, se presentan los pasos seguidos para este análisis.
A continuación, se plantean las hipótesis para cada variable independiente que podría estar asociada con la probabilidad de rotación del personal (rot_bin, donde 1 = Sí hay rotación y 0 = No hay rotación).
Sí / No)Frecuentemente, Raramente, Nunca)M / F)Ventas, RH, IyD)rotacion$Rotación = as.factor(rotacion$Rotación)
rotacion$`Viaje de Negocios` = as.factor(rotacion$`Viaje de Negocios`)
rotacion$Horas_Extra = as.factor(rotacion$Horas_Extra)
rotacion$Departamento = as.factor(rotacion$Departamento)
rotacion$Genero = as.factor(rotacion$Genero)
rotacion$Estado_Civil = as.factor(rotacion$Estado_Civil)
rotacion$Equilibrio_Trabajo_Vida = as.factor(rotacion$Equilibrio_Trabajo_Vida)
Antes que nada es necesario convertir nuestras variables a factor, esto se hace necesario al momonetos de aplicar funciones como GLM.
tabla_rotacion = table(rotacion$Rotación)
porcentajes_rotacion = prop.table(tabla_rotacion) * 100
print(tabla_rotacion)
##
## No Si
## 1233 237
print(round(porcentajes_rotacion, 2))
##
## No Si
## 83.88 16.12
De acuerdo a la frecuencia de los resultados, la variable “No” tiene el 84% de los datos totales, por tal motivo mas adlenate tal ves tendriamos problemas de especificidad en cuanto a que este desbalanceo al hacer evakuación cruzada puede tener efectos en los indicadores.
g1 = ggplot(rotacion, aes(x = Edad, fill = `Rotación`)) +
geom_histogram(bins = 30) +
scale_fill_brewer(palette = "Set2") +
theme_bw() +
labs(title = "Distribución de Edad", x = "Edad", y = "Frecuencia")
g1
intervalos = seq(floor(min(rotacion$Edad, na.rm = TRUE)),
ceiling(max(rotacion$Edad, na.rm = TRUE)),
by = 5) # Amplitud
df_edad_intervalo = rotacion %>%
mutate(EdadIntervalo = cut(Edad, breaks = intervalos, right = FALSE, include.lowest = TRUE)) %>%
group_by(EdadIntervalo) %>%
summarise(Frecuencia = n())
print(df_edad_intervalo)
## # A tibble: 9 × 2
## EdadIntervalo Frecuencia
## <fct> <int>
## 1 [18,23) 57
## 2 [23,28) 153
## 3 [28,33) 306
## 4 [33,38) 332
## 5 [38,43) 243
## 6 [43,48) 163
## 7 [48,53) 110
## 8 [53,58] 91
## 9 <NA> 15
En relación al cuadro anterior sobre la frecuencia agrupada de la edad, se infiere que la mayoría de los individuos se encuentran en los grupos [28,33) y [33,38), con 306 y 332 personas respectivamente. Asi mismo se puede observar como disminuye progresivamente después de los 38 años, es decir cada vez es menor la representación de personas mayores en el conjunto de datos.
g2 = ggplot(rotacion, aes(x = Años_Experiencia, fill = `Rotación`)) +
geom_histogram(bins = 30) +
scale_fill_brewer(palette = "Set2") +
theme_bw() +
labs(title = "Distribución de Años de Experiencia", x = "Años de Experiencia", y = "Frecuencia")
g2
promedio_experiencia = rotacion %>%
group_by(`Rotación`) %>%
summarise(Promedio_Anios_Experiencia = mean(Años_Experiencia, na.rm = TRUE))
print(promedio_experiencia)
## # A tibble: 2 × 2
## Rotación Promedio_Anios_Experiencia
## <fct> <dbl>
## 1 No 11.9
## 2 Si 8.24
Los empleados que no han rotado tienen un promedio de 11.86 años de experiencia, mientras que los que sí han rotado tienen un promedio de 8.24 años de experiencia. Por lo anterior, empleados con mayor experiencia tienden a mantenerse en la organización, mientras que los empleados con menos experiencia tienen mayor probabilidad de rotar.
g3 = ggplot(rotacion, aes(x = Satisfación_Laboral, fill = `Rotación`)) +
geom_histogram(bins = 30) +
scale_fill_brewer(palette = "Set2") +
theme_bw() +
labs(title = "Distribución de Satisfación Laboral", x = "Satisfación Laboral", y = "Frecuencia")
g3
tabla_satisfaccion_rotacion = table(rotacion$Satisfación_Laboral, rotacion$`Rotación`)
print(tabla_satisfaccion_rotacion)
##
## No Si
## 1 223 66
## 2 234 46
## 3 369 73
## 4 407 52
df_tabla_satisfaccion = as.data.frame(tabla_satisfaccion_rotacion)
colnames(df_tabla_satisfaccion) = c("Satisfacion_Laboral", "Rotacion", "Frecuencia")
print(df_tabla_satisfaccion)
## Satisfacion_Laboral Rotacion Frecuencia
## 1 1 No 223
## 2 2 No 234
## 3 3 No 369
## 4 4 No 407
## 5 1 Si 66
## 6 2 Si 46
## 7 3 Si 73
## 8 4 Si 52
La mayor parte de los empleados están satisfechos (369) o muy satisfechos (407) con su trabajo. Sin embargo, hay una cantidad significativa de empleados insatisfechos (234) y muy insatisfechos (223), lo que podría representar un riesgo para la empresa. Los empleados que no han rotado tienen una mayor frecuencia en los niveles de satisfacción y muy satisfacción (369 y 407, respectivamente). En contraste, la rotación es más común en empleados muy insatisfechos (66 de 289, 22.8%) e insatisfechos (46 de 280, 16.4%).
g4 = ggplot(rotacion, aes(x = Horas_Extra, fill = `Rotación`)) +
geom_bar() +
scale_fill_brewer(palette = "Set2") +
theme_bw() +
labs(title = "Frecuencia de Horas Extra", x = "Horas Extra", y = "Frecuencia")
g4
tabla_horas_extra = table(rotacion$Horas_Extra, rotacion$`Rotación`)
print(tabla_horas_extra)
##
## No Si
## No 944 110
## Si 289 127
df_tabla_horas_extra = as.data.frame(tabla_horas_extra)
colnames(df_tabla_horas_extra) = c("Horas_Extra", "Rotacion", "Frecuencia")
print(df_tabla_horas_extra)
## Horas_Extra Rotacion Frecuencia
## 1 No No 944
## 2 Si No 289
## 3 No Si 110
## 4 Si Si 127
Tasa de rotación sin horas extra:
\[
\frac{110}{1054} = 10.4\%
\]
Tasa de rotación con horas extra:
\[
\frac{127}{416} = 30.5\%
\]
La tasa de rotación para los empleados que trabajan horas extra (30.5%) es significativamente más alta que la de aquellos que no trabajan horas extra (10.4%). Esto apoya la hipótesis de que trabajar más horas extra está asociado con una mayor probabilidad de rotación.
g5 = ggplot(rotacion, aes(x = `Viaje de Negocios`, fill = `Rotación`)) +
geom_bar() +
scale_fill_brewer(palette = "Set2") +
theme_bw() +
labs(title = "Frecuencia de Viaje de Negocios", x = "Viaje de Negocios", y = "Frecuencia")
g5
tabla_viaje_negocios = table(rotacion$`Viaje de Negocios`, rotacion$`Rotación`)
print(tabla_viaje_negocios)
##
## No Si
## Frecuentemente 208 69
## No_Viaja 138 12
## Raramente 887 156
df_tabla_viaje_negocios = as.data.frame(tabla_viaje_negocios)
colnames(df_tabla_viaje_negocios) = c("Viaje_de_Negocios", "Rotacion", "Frecuencia")
print(df_tabla_viaje_negocios)
## Viaje_de_Negocios Rotacion Frecuencia
## 1 Frecuentemente No 208
## 2 No_Viaja No 138
## 3 Raramente No 887
## 4 Frecuentemente Si 69
## 5 No_Viaja Si 12
## 6 Raramente Si 156
Tasa de rotación para quienes viajan frecuentemente:
\[
\frac{69}{277} = 24.9\%
\]
Tasa de rotación para quienes no viajan:
\[
\frac{12}{150} = 8.0\%
\]
Tasa de rotación para quienes viajan raramente:
\[
\frac{156}{1043} = 15.0\%
\]
Los empleados que viajan con frecuencia tienen la mayor tasa de rotación (24.9%).
Los empleados que no viajan tienen la menor tasa de rotación (8.0%).
Los empleados que viajan raramente tienen una tasa de rotación intermedia (15.0%).
g6 = ggplot(rotacion, aes(x = Genero, fill = `Rotación`)) +
geom_bar() +
scale_fill_brewer(palette = "Set2") +
theme_bw() +
labs(title = "Frecuencia de Género", x = "Género", y = "Frecuencia")
g6
tabla_genero=table(rotacion$Genero, rotacion$`Rotación`)
print(tabla_genero)
##
## No Si
## F 501 87
## M 732 150
df_tabla_genero=as.data.frame(tabla_genero)
colnames(df_tabla_genero)=c("Genero", "Rotacion", "Frecuencia")
print(df_tabla_genero)
## Genero Rotacion Frecuencia
## 1 F No 501
## 2 M No 732
## 3 F Si 87
## 4 M Si 150
La tasa de rotación es ligeramente mayor en los hombres (17.0%) que en las mujeres (14.8%). Aunque hay más hombres en la muestra total, la diferencia en la tasa de rotación no es muy grande, lo que sugiere que el género no es un factor determinante de la rotación.
g7 = ggplot(rotacion, aes(x = Departamento, fill = `Rotación`)) +
geom_bar() +
scale_fill_brewer(palette = "Set2") +
theme_bw() +
labs(title = "Frecuencia de Departamento", x = "Departamento", y = "Frecuencia")
g7
tabla_departamento=table(rotacion$Departamento, rotacion$`Rotación`)
print(tabla_departamento)
##
## No Si
## IyD 828 133
## RH 51 12
## Ventas 354 92
df_tabla_departamento=as.data.frame(tabla_departamento)
colnames(df_tabla_departamento)=c("Departamento", "Rotacion", "Frecuencia")
print(df_tabla_departamento)
## Departamento Rotacion Frecuencia
## 1 IyD No 828
## 2 RH No 51
## 3 Ventas No 354
## 4 IyD Si 133
## 5 RH Si 12
## 6 Ventas Si 92
Ventas tiene la mayor tasa de rotación (20.6%), es decir que los empleados en este departamento cambian de trabajo con mayor frecuencia. Por otro lado Recursos Humanos (RH) también tiene una alta tasa de rotación (19.0%), aunque hay menos empleados en este grupo. Finalmnete el departamento de Investigación y Desarrollo (IyD) tiene la menor tasa de rotación (13.8%), indicando mayor estabilidad en este departamento.
cat_vars = names(rotacion)[sapply(rotacion, function(x) is.factor(x) || is.character(x))]
for (var in cat_vars) {
cat("\nFrecuencia para la variable:", var, "\n")
tabla=table(rotacion[[var]])
df_tabla=as.data.frame(tabla)
colnames(df_tabla)=c(var, "Frecuencia")
print(df_tabla)
}
##
## Frecuencia para la variable: Rotación
## Rotación Frecuencia
## 1 No 1233
## 2 Si 237
##
## Frecuencia para la variable: Viaje de Negocios
## Viaje de Negocios Frecuencia
## 1 Frecuentemente 277
## 2 No_Viaja 150
## 3 Raramente 1043
##
## Frecuencia para la variable: Departamento
## Departamento Frecuencia
## 1 IyD 961
## 2 RH 63
## 3 Ventas 446
##
## Frecuencia para la variable: Campo_Educación
## Campo_Educación Frecuencia
## 1 Ciencias 606
## 2 Humanidades 27
## 3 Mercadeo 159
## 4 Otra 82
## 5 Salud 464
## 6 Tecnicos 132
##
## Frecuencia para la variable: Genero
## Genero Frecuencia
## 1 F 588
## 2 M 882
##
## Frecuencia para la variable: Cargo
## Cargo Frecuencia
## 1 Director_Investigación 80
## 2 Director_Manofactura 145
## 3 Ejecutivo_Ventas 326
## 4 Gerente 102
## 5 Investigador_Cientifico 292
## 6 Recursos_Humanos 52
## 7 Representante_Salud 131
## 8 Representante_Ventas 83
## 9 Tecnico_Laboratorio 259
##
## Frecuencia para la variable: Estado_Civil
## Estado_Civil Frecuencia
## 1 Casado 673
## 2 Divorciado 327
## 3 Soltero 470
##
## Frecuencia para la variable: Horas_Extra
## Horas_Extra Frecuencia
## 1 No 1054
## 2 Si 416
##
## Frecuencia para la variable: Equilibrio_Trabajo_Vida
## Equilibrio_Trabajo_Vida Frecuencia
## 1 1 80
## 2 2 344
## 3 3 893
## 4 4 153
g8 = ggplot(rotacion, aes(x = Equilibrio_Trabajo_Vida, fill = `Rotación`)) +
geom_bar() +
scale_fill_brewer(palette = "Set2") +
theme_bw() +
labs(title = "Frecuencia de Equilibrio_Trabajo_Vida", x = "Equilibrio_Trabajo_Vida", y = "Frecuencia")
g8
tabla_equilibrio=table(rotacion$Equilibrio_Trabajo_Vida, rotacion$`Rotación`)
print(tabla_equilibrio)
##
## No Si
## 1 55 25
## 2 286 58
## 3 766 127
## 4 126 27
df_tabla_equilibrio=as.data.frame(tabla_equilibrio)
colnames(df_tabla_equilibrio)=c("Equilibrio_Trabajo_Vida", "Rotacion", "Frecuencia")
print(df_tabla_equilibrio)
## Equilibrio_Trabajo_Vida Rotacion Frecuencia
## 1 1 No 55
## 2 2 No 286
## 3 3 No 766
## 4 4 No 126
## 5 1 Si 25
## 6 2 Si 58
## 7 3 Si 127
## 8 4 Si 27
La mayor proporción de rotación se da en el nivel 1 (Muy bajo equilibrio), lo que respalda la hipótesis de que una mala conciliación entre trabajo y vida personal aumenta la probabilidad de rotación. En general, a medida que mejora el equilibrio (de bajo a medio), la tasa de rotación disminuye. Sin embargo, el nivel 4 (Alto equilibrio) muestra un pequeño incremento respecto al nivel 3, lo cual podría requerir un análisis más profundo (por ejemplo, revisando el tamaño de la muestra o posibles factores de confusión).
Aplicamos Chi - Cuadrado para evaluar si hay relación significativa entre una variable categórica y la rotación.
tabla_horas_extra=table(rotacion$Horas_Extra, rotacion$`Rotación`)
chi_horas=chisq.test(tabla_horas_extra)
print(chi_horas)
##
## Pearson's Chi-squared test with Yates' continuity correction
##
## data: tabla_horas_extra
## X-squared = 87.564, df = 1, p-value < 2.2e-16
De acuerdo a lo anterior sí hay una relación significativa entre hacer horas extra y la rotación.
tabla_viaje=table(rotacion$`Viaje de Negocios`, rotacion$`Rotación`)
chi_viaje=chisq.test(tabla_viaje)
print(chi_viaje)
##
## Pearson's Chi-squared test
##
## data: tabla_viaje
## X-squared = 24.182, df = 2, p-value = 5.609e-06
Existe una relación estadísticamente significativa entre hacer viajes de negocios y la rotación.
tabla_departamento=table(rotacion$Departamento, rotacion$`Rotación`)
chi_departamento=chisq.test(tabla_departamento)
print(chi_departamento)
##
## Pearson's Chi-squared test
##
## data: tabla_departamento
## X-squared = 10.796, df = 2, p-value = 0.004526
Existe una relación significativa entre el departamento al que pertenece un empleado y su probabilidad de rotación.
tabla_Genero=table(rotacion$Genero, rotacion$`Rotación`)
chi_Genero=chisq.test(tabla_Genero)
print(chi_Genero)
##
## Pearson's Chi-squared test with Yates' continuity correction
##
## data: tabla_Genero
## X-squared = 1.117, df = 1, p-value = 0.2906
No hay evidencia estadística suficiente para decir que el género esté relacionado con la rotación en tu muestra.
rotacion$rot_bin = ifelse(rotacion$`Rotación` == "Si", 1, 0)
modelo_edad = glm(rot_bin ~ Edad, data = rotacion, family = binomial)
summary(modelo_edad)
##
## Call:
## glm(formula = rot_bin ~ Edad, family = binomial, data = rotacion)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 0.20637 0.30597 0.674 0.5
## Edad -0.05225 0.00870 -6.006 1.9e-09 ***
## ---
## 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: 1259.1 on 1468 degrees of freedom
## AIC: 1263.1
##
## Number of Fisher Scoring iterations: 4
De acuerdo al modelo anterior se tiene que cada año adicional de edad reduce la probabilidad de rotación en aproximadamente un 5.1% [exp(-0.0522)]= 94.9% y (1-94.9=0.051) Es decir que los empleados más jóvenes tienden a rotar más que los de mayor edad.
Hipótesis: A mayor edad, menor probabilidad de rotación - la hipótesis se confirma.
modelo_experiencia=glm(rot_bin ~ Años_Experiencia, data = rotacion, family = binomial)
summary(modelo_experiencia)
##
## Call:
## glm(formula = rot_bin ~ Años_Experiencia, family = binomial,
## data = rotacion)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -0.88306 0.12744 -6.929 4.23e-12 ***
## Años_Experiencia -0.07773 0.01217 -6.387 1.69e-10 ***
## ---
## 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: 1248.1 on 1468 degrees of freedom
## AIC: 1252.1
##
## Number of Fisher Scoring iterations: 5
Para los años de experiencia tenemos que a medida que aumentan los años de experiencia, disminuye la probabilidad de que un empleado se vaya. En terminos numericos se podria mencionar que por cada año adicional de experiencia, la probabilidad de rotación disminuye en aproximadamente un 7.5% dado que [exp(-0.07773)]=0.925 y (1 - 0.925= 0.075).
Hipótesis: Los empleados con más experiencia tienden a rotar menos - La hipótesis se confirma.
modelo_sat=glm(rot_bin ~ Satisfación_Laboral, data = rotacion, family = binomial)
summary(modelo_sat)
##
## Call:
## glm(formula = rot_bin ~ Satisfación_Laboral, family = binomial,
## data = rotacion)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -0.9903 0.1757 -5.635 1.75e-08 ***
## Satisfación_Laboral -0.2510 0.0637 -3.940 8.16e-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: 1283.1 on 1468 degrees of freedom
## AIC: 1287.1
##
## Number of Fisher Scoring iterations: 4
En relación a lo anterior tenemos que el coeficiente de Satisfacción Laboral es -0.2510, lo que indica que a mayor satisfacción laboral, menor probabilidad de rotación. Es decir que por cada unidad adicional de satisfacción laboral, la probabilidad de rotación disminuye un 22.2% dado que [exp(-0.2510)] = 0.778 y (1 - 0.778= 0.222).
Hipótesis: A mayor satisfacción laboral, menor probabilidad de rotación - La hipótesis se confirma.
modelo_viaje=glm(rot_bin ~ `Viaje de Negocios`, data = rotacion, family = binomial)
summary(modelo_viaje)
##
## Call:
## glm(formula = rot_bin ~ `Viaje de Negocios`, family = binomial,
## data = rotacion)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -1.1034 0.1389 -7.943 1.98e-15 ***
## `Viaje de Negocios`No_Viaja -1.3389 0.3315 -4.039 5.36e-05 ***
## `Viaje de Negocios`Raramente -0.6346 0.1638 -3.873 0.000107 ***
## ---
## 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: 1274.8 on 1467 degrees of freedom
## AIC: 1280.8
##
## Number of Fisher Scoring iterations: 5
Ahora vamos a evaluar si la frecuencia de viajes de negocios afecta la probabilidad de que un empleado se vaya. Por lo anterior, se tiene que los empleados que no viajan tienen el [exp(-1.3389)] = 26.2% y lo que viajan raramente tienen un [exp(-0.6346)] =53% del riesgo.
Hipótesis: Los empleados que hacen horas extra tienen mayor probabilidad de rotación - La hipótesis se confirma.
modelo_horas=glm(rot_bin ~ Horas_Extra, data = rotacion, family = binomial)
summary(modelo_horas)
##
## Call:
## glm(formula = rot_bin ~ Horas_Extra, family = binomial, data = rotacion)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -2.1496 0.1007 -21.338 <2e-16 ***
## Horas_ExtraSi 1.3274 0.1466 9.056 <2e-16 ***
## ---
## 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: 1217.2 on 1468 degrees of freedom
## AIC: 1221.2
##
## Number of Fisher Scoring iterations: 4
Los empleados que hacen horas extra tienen 3.77 [exp(1.3274)]= 3.77 veces más probabilidades de irse de la empresa que los que no hacen horas extra. Hacer horas extra está fuertemente asociado con una mayor rotación y el AIC = 1221.2 mejora considerable comparado con modelos anteriores.
La frecuencia alta de viajes de negocios incrementa la rotación - La hipótesis se confirma, viajar más está asociado con mayor rotación.
modelo_genero=glm(rot_bin ~ Genero, data = rotacion, family = binomial)
summary(modelo_genero)
##
## Call:
## glm(formula = rot_bin ~ Genero, family = binomial, data = rotacion)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -1.7507 0.1161 -15.073 <2e-16 ***
## GeneroM 0.1656 0.1467 1.128 0.259
## ---
## 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: 1297.3 on 1468 degrees of freedom
## AIC: 1301.3
##
## Number of Fisher Scoring iterations: 4
Aunque el coeficiente para hombres (M) es positivo (indica una rotación un poco mayor que las mujeres -1.7507), no es estadísticamente significativo. Hombres tienen un 18% [exp(0.1656)] = 1.18 más de probabilidades de rotar que mujeres pero como el p-valor es 0.259, ese efecto no es confiable estadísticamente. esto ya era evidente en la prueba de Chi-cuadrado. El género no tiene un impacto significativo en la rotación.
Hipótesis: Se espera una diferencia significativa en la rotación según el género - la hipótesis NO se confirma.
modelo_departamento=glm(rot_bin ~ Departamento, data = rotacion, family = binomial)
summary(modelo_departamento)
##
## Call:
## glm(formula = rot_bin ~ Departamento, family = binomial, data = rotacion)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -1.82866 0.09342 -19.576 < 2e-16 ***
## DepartamentoRH 0.38175 0.33417 1.142 0.25330
## DepartamentoVentas 0.48116 0.14974 3.213 0.00131 **
## ---
## 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: 1288.1 on 1467 degrees of freedom
## AIC: 1294.1
##
## Number of Fisher Scoring iterations: 4
Si bien el departamento de Recursios Humanos tiene más rotación que IyD, esta no es significativa. Sin embargo, Ventas tiene mayor rotación y es estadísticamente significativa. Los empleados del departamento de Ventas tienen aproximadamente 1.6 [exp(0.48116)] = 1.6 veces más probabilidades de rotar que los de Investigación y Desarrollo (IyD). y Por otro lado para RH aunque el efecto es parecido Ccon 1.4 veces, no es significativo, posiblemente por el bajo número de empleados en RH y para investigación y desarrollo con 0.16 veces.
Hipótesis: Los empleados del departamento de ventas rotan más que los de otros departamentos - La hipótesis se confirma, ventas tiene mayor probabilidad de rotación.
modelo_Equilibrio_Trabajo_Vida=glm(rot_bin ~ Equilibrio_Trabajo_Vida, data = rotacion, family = binomial)
summary(modelo_Equilibrio_Trabajo_Vida)
##
## Call:
## glm(formula = rot_bin ~ Equilibrio_Trabajo_Vida, family = binomial,
## data = rotacion)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -0.7885 0.2412 -3.269 0.001080 **
## Equilibrio_Trabajo_Vida2 -0.8071 0.2809 -2.873 0.004066 **
## Equilibrio_Trabajo_Vida3 -1.0085 0.2595 -3.886 0.000102 ***
## Equilibrio_Trabajo_Vida4 -0.7520 0.3212 -2.341 0.019215 *
## ---
## 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: 1284.5 on 1466 degrees of freedom
## AIC: 1292.5
##
## Number of Fisher Scoring iterations: 4
A medida que mejora el equilibrio trabajo-vida, la probabilidad de rotación disminuye. Por cada punto adicional en equilibrio trabajo-vida la probabilidad de rotación disminuye en aproximadamente un 21.3%, dado que [exp(-0.23956)] = 0.787 y (1-0.787=0.213).
# Calcular la proporción global de rot_bin
prop_global=mean(rotacion$rot_bin)
cat("Proporción global de rot_bin:", prop_global, "\n")
## Proporción global de rot_bin: 0.1612245
Bajo lo anterior se tiene que revisar la salida del sigueinte data.frame buscando las semillas que generen proporciones cercanas a 0.1612 en ambos subconjuntos Train_Prop y Test_Prop.
seed_candidates = 100:110
resultados = data.frame(Seed = seed_candidates,
Train_Prop = NA,
Test_Prop = NA,
Diff_Train = NA,
Diff_Test = NA)
n = nrow(rotacion)
size_train = round(0.7 * n)
for (s in seed_candidates) {
set.seed(s)
train_idx = sample(seq_len(n), size = size_train)
train_set=rotacion[train_idx, ]
test_set =rotacion[-train_idx, ]
prop_train=mean(train_set$rot_bin)
prop_test =mean(test_set$rot_bin)
resultados$Train_Prop[resultados$Seed == s]=prop_train
resultados$Test_Prop[resultados$Seed == s] =prop_test
resultados$Diff_Train[resultados$Seed == s]=abs(prop_train - prop_global)
resultados$Diff_Test[resultados$Seed == s] =abs(prop_test - prop_global)
}
print(resultados)
## Seed Train_Prop Test_Prop Diff_Train Diff_Test
## 1 100 0.1525753 0.1814059 0.0086491740 0.020181406
## 2 101 0.1525753 0.1814059 0.0086491740 0.020181406
## 3 102 0.1661808 0.1496599 0.0049562682 0.011564626
## 4 103 0.1574344 0.1700680 0.0037900875 0.008843537
## 5 104 0.1681244 0.1451247 0.0068999028 0.016099773
## 6 105 0.1545190 0.1768707 0.0067055394 0.015646259
## 7 106 0.1574344 0.1700680 0.0037900875 0.008843537
## 8 107 0.1632653 0.1564626 0.0020408163 0.004761905
## 9 108 0.1603499 0.1632653 0.0008746356 0.002040816
## 10 109 0.1593780 0.1655329 0.0018464529 0.004308390
## 11 110 0.1525753 0.1814059 0.0086491740 0.020181406
Por lo naterior tenemos que el Diff_Train con menor valor es igual a 0.0008746356 y su Test_Prop y Train_Prop son equivalentes o proxys al 16%, por lo anterior el seed adecuado es el 108.
set.seed(108)
n=nrow(rotacion)
train_idx=sample(seq_len(n), size = round(0.7 * n)) ## Partición en los datos 70% sea un set para entrenar el modelo y 30%
train_set=rotacion[train_idx, ]## Dividir el dataset en entrenamiento y prueba
test_set =rotacion[-train_idx, ]
cat("Observaciones en entrenamiento:", nrow(train_set), "\n")
## Observaciones en entrenamiento: 1029
cat("Observaciones en prueba:", nrow(test_set), "\n")
## Observaciones en prueba: 441
Acerca de la función GLM esta tiene una gran utilidad, pues no es estrictamente necesario realizar one hot encoding de forma manual en cuanto a que glm() manejan internamente las variables categóricas (si están correctamente definidas como factores, tal como se hizo arriba) y crean las variables dummy necesarias para el análisis.
modelo_entrenamiento=glm(rot_bin ~ Edad + Años_Experiencia + Satisfación_Laboral + # Estimacion del modelo logístico con set de entrenamiento.
`Viaje de Negocios` + Horas_Extra + Genero + Departamento,
data = train_set, family = binomial)
summary(modelo_entrenamiento)
##
## Call:
## glm(formula = rot_bin ~ Edad + Años_Experiencia + Satisfación_Laboral +
## `Viaje de Negocios` + Horas_Extra + Genero + Departamento,
## family = binomial, data = train_set)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 0.50315 0.52031 0.967 0.333533
## Edad -0.03179 0.01442 -2.204 0.027500 *
## Años_Experiencia -0.05055 0.01880 -2.689 0.007159 **
## Satisfación_Laboral -0.36459 0.08147 -4.475 7.63e-06 ***
## `Viaje de Negocios`No_Viaja -1.17334 0.40919 -2.867 0.004138 **
## `Viaje de Negocios`Raramente -0.61835 0.21470 -2.880 0.003975 **
## Horas_ExtraSi 1.47586 0.18809 7.847 4.27e-15 ***
## GeneroM 0.26852 0.19241 1.396 0.162840
## DepartamentoRH 0.20159 0.45835 0.440 0.660078
## DepartamentoVentas 0.68021 0.19327 3.519 0.000432 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 906.03 on 1028 degrees of freedom
## Residual deviance: 768.53 on 1019 degrees of freedom
## AIC: 788.53
##
## Number of Fisher Scoring iterations: 5
El modelo logístico multivariable permitió identificar varios factores significativamente asociados con la rotación de personal. En primer lugar, la edad mostró un coeficiente negativo (-0.03179, p = 0.0275), lo que indica que a mayor edad, menor es la probabilidad de rotación. De forma similar, los años de experiencia también presentaron una relación negativa (-0.05055, p = 0.0071), lo que implica que los empleados con más tiempo en su trayectoria laboral tienden a permanecer más en la empresa. La satisfacción laboral fue otro factor determinante, con un coeficiente de -0.36459 (p < 0.001), lo cual evidencia que a mayor satisfacción, menor es la probabilidad de que el empleado abandone la organización.
En cuanto a los viajes de negocios, se encontró que quienes no viajan tienen una probabilidad significativamente menor de rotación (-1.17334, p = 0.0041), al igual que aquellos que viajan raramente (-0.61835, p = 0.0039), en comparación con los que viajan con frecuencia. Por otro lado, hacer horas extra se asoció fuertemente con un aumento en la probabilidad de rotación (coeficiente de 1.47586, p < 0.001), lo que sugiere que la sobrecarga laboral puede ser un factor crítico de salida. Por otro lado, pertenecer al departamento de ventas incrementa significativamente la rotación (0.68021, p = 0.0004), en contraste con el departamento de Investigación y Desarrollo (IyD), que fue la categoría de referencia.
Por otro lado, la variable Genero con 0.16 y Departamento (RH) con 0.66 no son significativas. Fnalmente, el modelo presenta una null deviance de 906.03, lo que representa el nivel de error del modelo sin incluir predictores, es decir, asumiendo que todos los empleados tienen la misma probabilidad de rotación. Al incorporar las variables explicativas, la residual deviance se reduce a 768.53, lo cual indica una mejora significativa en la capacidad del modelo para explicar la variabilidad de los datos. Por otro lado, el AIC (Akaike Information Criterion) obtenido fue de 788.53, un valor relativamente bajo que respalda la eficiencia del modelo en términos de ajuste y complejidad. Finalmente, el hecho de que el modelo haya requerido solo 5 iteraciones para converger indica que el proceso de estimación fue estable y no presentó problemas numéricos.
| AUC | Interpretación |
|---|---|
| 0.90 – 1.00 | Excelente discriminación |
| 0.80 – 0.90 | Muy buena |
| 0.70 – 0.80 | Buena |
| 0.60 – 0.70 | Aceptable pero débil |
| 0.50 – 0.60 | Pobre |
| 0.50 | Sin capacidad predictiva |
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
## Obtener las probabilidades predichas en el set de prueba usando el modelo ajustado (modelo_entrenamiento)
pred_prob=predict(modelo_entrenamiento, newdata = test_set, type = "response")
roc_obj=roc(test_set$rot_bin, pred_prob)
## Setting levels: control = 0, case = 1
## Setting direction: controls < cases
auc_value=auc(roc_obj)
auc_value
## Area under the curve: 0.7792
El modelo tiene un AUC de 0.7792, lo que indica que su capacidad para distinguir entre empleados que se van y los que se quedan es aceptable. En términos prácticos, si un empleado que se fue (1) y uno que se quedó (0) al azar, hay un 77.9% de probabilidad de que el modelo asigne una mayor probabilidad de salida al que realmente se fue.
plot(roc_obj, col = "blue", main = "Curva ROC en el set de prueba")
legend("bottomright", legend = paste("AUC =", round(auc_value, 2)), col = "blue", lwd = 2)
A continuación se divide en 10 partes y se entrena en 9 y se evalúa en la 1 restante. Este proceso se repite 10 veces, usando diferentes partes de prueba.
library(caret)
## Warning: package 'caret' was built under R version 4.4.3
## Cargando paquete requerido: lattice
## Warning: package 'lattice' was built under R version 4.4.2
##
## Adjuntando el paquete: 'lattice'
## The following object is masked from 'package:boot':
##
## melanoma
control=trainControl(method = "cv", number = 10)
modelo_cv=train(rot_bin ~ Edad + Años_Experiencia + Satisfación_Laboral +
`Viaje de Negocios` + Horas_Extra + Genero + Departamento,
data = rotacion,
method = "glm",
family = "binomial",
trControl = control)
## Warning in train.default(x, y, weights = w, ...): You are trying to do
## regression and your outcome only has two possible values Are you trying to do
## classification? If so, use a 2 level factor as your outcome column.
print(modelo_cv)
## Generalized Linear Model
##
## 1470 samples
## 7 predictor
##
## No pre-processing
## Resampling: Cross-Validated (10 fold)
## Summary of sample sizes: 1323, 1323, 1323, 1323, 1323, 1323, ...
## Resampling results:
##
## RMSE Rsquared MAE
## 0.3377575 0.1627191 0.2296142
Bajo lo anterior se tiene una advertencia que aunque solo hay dos valores posibles en rot_bin (1 y 0), como es de tipo numérico, caret asume que estás haciendo regresión. Por lo anterior se necesita convertir rot_bin a factor con dos niveles antes de entrenar el modelo.
rotacion$rot_bin = as.factor(rotacion$rot_bin) ## ojo aqui es para convertir
control=trainControl(method = "cv", number = 10)
modelo_cv=train(rot_bin ~ Edad + Años_Experiencia + Satisfación_Laboral +
`Viaje de Negocios` + Horas_Extra + Genero + Departamento,
data = rotacion,
method = "glm",
family = "binomial",
trControl = control)
print(modelo_cv)
## Generalized Linear Model
##
## 1470 samples
## 7 predictor
## 2 classes: '0', '1'
##
## No pre-processing
## Resampling: Cross-Validated (10 fold)
## Summary of sample sizes: 1323, 1322, 1324, 1322, 1323, 1323, ...
## Resampling results:
##
## Accuracy Kappa
## 0.8489537 0.1847549
El modelo predice correctamente el 84.89% de los casos en promedio durante la validación cruzada esto es muy bueno en términos generales. Sin emabrgo, el Kappa = 0.1847 es bajo, lo que probablemente se debe a que la clase está desbalanceada (solo 18% son rotación).
Anterioemente ya se anunciaba que poidiamos tener un problema por la frecuencia de los resultados, la variable “No” tiene el 84% de los datos totales. Asi pues surge la necesidad de rebalancear el conjunto de datos. Por lo anterior, procedemos a mejorar el desempeño sobre la clase minoritaria (rotación = “Sí”).
Por lo anterior procedamos a rebalancear con ROSE. Esta funcion de acuerdo con () Funciones para abordar problemas de clasificación binaria en presencia de clases desequilibradas. Se generan muestras sintéticas balanceadas según ROSE (Menardi y Torelli, 2013). También se proporcionan funciones que implementan soluciones más tradicionales para el desequilibrio de clases, así como diferentes métricas para evaluar la precisión del aprendiz. Estas se estiman mediante métodos de retención, bootstrap o validación cruzada.
#library(ROSE)
#rotacion$rot_bin = factor(ifelse(rotacion$rot_bin == 1, "Sí", "No"))
#datos_balanceados = ROSE(rot_bin ~ Edad + Años_Experiencia + Satisfación_Laboral +
# `Viaje de Negocios` + Horas_Extra + Genero + Departamento,
# data = rotacion, seed = 108)$data
Lo anterior nos dice que tenemo espacios, se procede a renombrar las columnas todas las columnas de la forma que remplaza el espacio por _.
names(rotacion) = make.names(names(rotacion))
names(rotacion)
## [1] "Rotación" "Edad"
## [3] "Viaje.de.Negocios" "Departamento"
## [5] "Distancia_Casa" "Educación"
## [7] "Campo_Educación" "Satisfacción_Ambiental"
## [9] "Genero" "Cargo"
## [11] "Satisfación_Laboral" "Estado_Civil"
## [13] "Ingreso_Mensual" "Trabajos_Anteriores"
## [15] "Horas_Extra" "Porcentaje_aumento_salarial"
## [17] "Rendimiento_Laboral" "Años_Experiencia"
## [19] "Capacitaciones" "Equilibrio_Trabajo_Vida"
## [21] "Antigüedad" "Antigüedad_Cargo"
## [23] "Años_ultima_promoción" "Años_acargo_con_mismo_jefe"
## [25] "rot_bin"
names(test_set) = make.names(names(test_set))
names(test_set)
## [1] "Rotación" "Edad"
## [3] "Viaje.de.Negocios" "Departamento"
## [5] "Distancia_Casa" "Educación"
## [7] "Campo_Educación" "Satisfacción_Ambiental"
## [9] "Genero" "Cargo"
## [11] "Satisfación_Laboral" "Estado_Civil"
## [13] "Ingreso_Mensual" "Trabajos_Anteriores"
## [15] "Horas_Extra" "Porcentaje_aumento_salarial"
## [17] "Rendimiento_Laboral" "Años_Experiencia"
## [19] "Capacitaciones" "Equilibrio_Trabajo_Vida"
## [21] "Antigüedad" "Antigüedad_Cargo"
## [23] "Años_ultima_promoción" "Años_acargo_con_mismo_jefe"
## [25] "rot_bin"
Como tenemos un desbalamceo en las clases utilizamos el ajuste por pesos haciendo que las observaciones con rotación = “Sí” se les da más peso para que el modelo no las ignore por ser minoría.
# Calcular pesos en función del desbalance
n_pos = sum(rotacion$rot_bin == 1)
n_neg = sum(rotacion$rot_bin == 0)
ratio = n_neg / n_pos
rotacion$pesos = ifelse(rotacion$rot_bin == 1, ratio, 1)
names(rotacion) = make.names(names(rotacion))
names(rotacion)
## [1] "Rotación" "Edad"
## [3] "Viaje.de.Negocios" "Departamento"
## [5] "Distancia_Casa" "Educación"
## [7] "Campo_Educación" "Satisfacción_Ambiental"
## [9] "Genero" "Cargo"
## [11] "Satisfación_Laboral" "Estado_Civil"
## [13] "Ingreso_Mensual" "Trabajos_Anteriores"
## [15] "Horas_Extra" "Porcentaje_aumento_salarial"
## [17] "Rendimiento_Laboral" "Años_Experiencia"
## [19] "Capacitaciones" "Equilibrio_Trabajo_Vida"
## [21] "Antigüedad" "Antigüedad_Cargo"
## [23] "Años_ultima_promoción" "Años_acargo_con_mismo_jefe"
## [25] "rot_bin" "pesos"
names(test_set) = make.names(names(test_set))
names(test_set)
## [1] "Rotación" "Edad"
## [3] "Viaje.de.Negocios" "Departamento"
## [5] "Distancia_Casa" "Educación"
## [7] "Campo_Educación" "Satisfacción_Ambiental"
## [9] "Genero" "Cargo"
## [11] "Satisfación_Laboral" "Estado_Civil"
## [13] "Ingreso_Mensual" "Trabajos_Anteriores"
## [15] "Horas_Extra" "Porcentaje_aumento_salarial"
## [17] "Rendimiento_Laboral" "Años_Experiencia"
## [19] "Capacitaciones" "Equilibrio_Trabajo_Vida"
## [21] "Antigüedad" "Antigüedad_Cargo"
## [23] "Años_ultima_promoción" "Años_acargo_con_mismo_jefe"
## [25] "rot_bin"
library(caret)
miSummary = function(data, lev = NULL, model = NULL) {
require(caret)
cm = confusionMatrix(data = data$pred, reference = data$obs, positive = "Sí")
out = c(
Accuracy = cm$overall["Accuracy"],
Kappa = cm$overall["Kappa"],
Sens = cm$byClass["Sensitivity"],
Spec = cm$byClass["Specificity"],
ROC = tryCatch({
roc_obj = pROC::roc(response = data$obs, predictor = data$Sí)
as.numeric(pROC::auc(roc_obj))
}, error = function(e) NA)
)
return(out)
}
rotacion$rot_bin = factor(ifelse(rotacion$Rotación == "Si", "Sí", "No"))
control_pesos = trainControl(method = "cv", number = 10, classProbs = TRUE, summaryFunction = miSummary)
modelo_pesos = train(
rot_bin ~ Edad + Años_Experiencia + Satisfación_Laboral +
Viaje.de.Negocios + Horas_Extra + Genero + Departamento,
data = rotacion,
method = "glm",
family = "binomial",
trControl = control_pesos,
metric = "ROC", # Más útil en desbalance que Accuracy
weights = rotacion$pesos
)
## Setting levels: control = No, case = Sí
## Setting direction: controls > cases
## Warning in eval(family$initialize): non-integer #successes in a binomial glm!
## Setting levels: control = No, case = Sí
## Setting direction: controls < cases
## Warning in eval(family$initialize): non-integer #successes in a binomial glm!
## Setting levels: control = No, case = Sí
## Setting direction: controls < cases
## Warning in eval(family$initialize): non-integer #successes in a binomial glm!
## Setting levels: control = No, case = Sí
## Setting direction: controls < cases
## Warning in eval(family$initialize): non-integer #successes in a binomial glm!
## Setting levels: control = No, case = Sí
## Setting direction: controls < cases
## Warning in eval(family$initialize): non-integer #successes in a binomial glm!
## Setting levels: control = No, case = Sí
## Setting direction: controls < cases
## Warning in eval(family$initialize): non-integer #successes in a binomial glm!
## Setting levels: control = No, case = Sí
## Setting direction: controls < cases
## Warning in eval(family$initialize): non-integer #successes in a binomial glm!
## Setting levels: control = No, case = Sí
## Setting direction: controls < cases
## Warning in eval(family$initialize): non-integer #successes in a binomial glm!
## Setting levels: control = No, case = Sí
## Setting direction: controls < cases
## Warning in eval(family$initialize): non-integer #successes in a binomial glm!
## Setting levels: control = No, case = Sí
## Setting direction: controls < cases
## Warning in eval(family$initialize): non-integer #successes in a binomial glm!
## Setting levels: control = No, case = Sí
## Setting direction: controls < cases
## Warning in eval(family$initialize): non-integer #successes in a binomial glm!
print(modelo_pesos)
## Generalized Linear Model
##
## 1470 samples
## 7 predictor
## 2 classes: 'No', 'Sí'
##
## No pre-processing
## Resampling: Cross-Validated (10 fold)
## Summary of sample sizes: 1323, 1323, 1323, 1324, 1323, 1323, ...
## Resampling results:
##
## Accuracy.Accuracy Kappa.Kappa Sens.Sensitivity Spec.Specificity ROC
## 0.7019383 0.258333 0.6748188 0.7071532 0.7572663
El modelo sin corrección, es decir sin correcion por desbalanceo parece muy preciso 84.9%, pero esto se debe en gran parte al desbalance de la variable objetivo (rotación ≈ 16%). Es decir, predice bien la clase “No” (mayoría), pero ignora la minoritaria. El modelo con pesos sacrifica algo de Accuracy global 70.2% pero mejora en Kappa pasando de 0.18 a 0.26 y ofrece un AUC = 0.757, lo que refleja mejor capacidad de distinguir ambas clases - perdiendo 3pp.
Por lo anterior procedamos a rebalancear con ROSE. Esta funcion abordar problemas de clasificación binaria en presencia de clases desequilibradas. Se generan muestras sintéticas balanceadas según ROSE (Menardi y Torelli, 2013). También se proporcionan funciones que implementan soluciones más tradicionales para el desequilibrio de clases, así como diferentes métricas para evaluar la precisión del aprendiz. Estas se estiman mediante métodos de retención, bootstrap o validación cruzada.
datos_rose = ROSE(
rot_bin ~ Edad + Años_Experiencia + Satisfación_Laboral + Viaje.de.Negocios + Horas_Extra + Genero + Departamento,
data = rotacion,
seed = 108
)$data
modelo_rose = train(
rot_bin ~ Edad + Años_Experiencia + Satisfación_Laboral +
Viaje.de.Negocios + Horas_Extra + Genero + Departamento,
data = datos_rose,
method = "glm",
family = "binomial",
trControl = control_pesos,
metric = "Accuracy"
)
## Setting levels: control = No, case = Sí
## Setting direction: controls < cases
## Warning in train.default(x, y, weights = w, ...): The metric "Accuracy" was not
## in the result set. Accuracy.Accuracy will be used instead.
## Setting levels: control = No, case = Sí
## Setting direction: controls < cases
## Setting levels: control = No, case = Sí
## Setting direction: controls < cases
## Setting levels: control = No, case = Sí
## Setting direction: controls < cases
## Setting levels: control = No, case = Sí
## Setting direction: controls < cases
## Setting levels: control = No, case = Sí
## Setting direction: controls < cases
## Setting levels: control = No, case = Sí
## Setting direction: controls < cases
## Setting levels: control = No, case = Sí
## Setting direction: controls < cases
## Setting levels: control = No, case = Sí
## Setting direction: controls < cases
## Setting levels: control = No, case = Sí
## Setting direction: controls < cases
## Setting levels: control = No, case = Sí
## Setting direction: controls < cases
print(modelo_rose)
## Generalized Linear Model
##
## 1470 samples
## 7 predictor
## 2 classes: 'No', 'Sí'
##
## No pre-processing
## Resampling: Cross-Validated (10 fold)
## Summary of sample sizes: 1323, 1323, 1323, 1323, 1323, 1323, ...
## Resampling results:
##
## Accuracy.Accuracy Kappa.Kappa Sens.Sensitivity Spec.Specificity ROC
## 0.6843537 0.3684912 0.6616438 0.7067568 0.7571085
El modelo clasifica segun accuracy correctamente el 68.4% de los casos. El Kappa Kappa es mucho mejor que sin pesos 0.18 y que con pesos 0.26 y la SensibilidadnIndica mayor acuerdo real entre predicción y realidad. Finalmente el AUC tiene Buen desempeño global del modelo (valor aceptable para clasificación).
# Crear el empleado hipotético como un data.frame
nuevo_empleado = data.frame(
Edad = 30,
Años_Experiencia = 5,
Satisfación_Laboral = 2,
Viaje.de.Negocios = factor("Frecuentemente", levels = levels(rotacion$Viaje.de.Negocios)),
Horas_Extra = factor("Si", levels = levels(rotacion$Horas_Extra)),
Genero = factor("M", levels = levels(rotacion$Genero)),
Departamento = factor("Ventas", levels = levels(rotacion$Departamento))
)
# Obtener probabilidad predicha de rotación
prob_rotacion = predict(modelo_pesos, newdata = nuevo_empleado, type = "prob")
print(prob_rotacion)
## No Sí
## 1 0.07538594 0.9246141
umbral = 0.5# Definir umbral
if (prob_rotacion$Sí > umbral) {
mensaje = " Se recomienda intervenir a este empleado (alta probabilidad de rotación)."
} else {
mensaje = "No es necesario intervenir (baja probabilidad de rotación)."
}
cat("Probabilidad de rotación:", round(prob_rotacion$Sí, 4), "\n", mensaje)
## Probabilidad de rotación: 0.9246
## Se recomienda intervenir a este empleado (alta probabilidad de rotación).
Los resultados del análisis bivariado y multivariado permitieron identificar las principales variables asociadas con la rotación del personal. Entre las variables cuantitativas, se encontró que a mayor edad y mayor número de años de experiencia, la probabilidad de rotación disminuye significativamente. Así mismo, una mayor satisfacción laboral se asocia con una menor intención de abandonar la empresa. Por otro lado, desde las variables categóricas, se observó que los empleados que realizan horas extra y aquellos que viajan frecuentemente por motivos laborales presentan una mayor propensión a rotar. Además, el personal del departamento de ventas muestra una tasa de rotación más elevada en comparación con otras áreas de la organización.
En función de estos hallazgos, se propone una estrategia integral para reducir la rotación en la empresa. Primero, es fundamental mejorar la satisfacción laboral a través de programas de bienestar, reconocimiento, formación y oportunidades de crecimiento interno. Segundo, se deben gestionar adecuadamente las horas extra mediante una mejor distribución de tareas y promoción del equilibrio entre vida personal y laboral. Tercero, se recomienda revisar las condiciones asociadas a los viajes de negocios, ofreciendo incentivos, compensaciones o mayor flexibilidad para quienes desempeñan estas funciones con frecuencia. Finalmente, se sugiere intervenir de manera específica en el departamento de ventas, fortaleciendo el acompañamiento, la motivación y los planes de carrera del personal que allí labora.