1 Introducción

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.

2 Seleccione 3 variables categóricas (distintas de rotación) y 3 variables cuantitativas, que se consideren estén relacionadas con la rotación, formule hipotesis

2.1 Formulación de Hipótesis

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).

2.1.1 Variables Cuantitativas

  • Edad
    Hipótesis: A mayor edad del empleado, menor será la probabilidad de rotación.
    Justificación: Se espera que empleados con mayor edad tengan mayor estabilidad laboral.

  • Años de experiencia
    Hipótesis: A mayor número de años de experiencia, menor será la probabilidad de rotación.
    Justificación: La experiencia se asocia con mayor permanencia en la organización.

  • Satisfacción laboral
    Hipótesis: A mayor nivel de satisfacción laboral, menor será la probabilidad de rotación.
    Justificación: Un empleado más satisfecho es menos propenso a renunciar.

2.1.2 Variables Categóricas

  • Horas extra ( / No)
    Hipótesis: Los empleados que hacen horas extra tienen una mayor probabilidad de rotación.
    Justificación: Las horas extra podrían estar relacionadas con sobrecarga laboral o estrés.

  • Viaje de negocios (Frecuentemente, Raramente, Nunca)
    Hipótesis: Los empleados que viajan frecuentemente tienen mayor probabilidad de rotación.
    Justificación: Los viajes frecuentes pueden afectar el equilibrio entre trabajo y vida personal.

  • Género (M / F)
    Hipótesis: Se espera una diferencia significativa entre hombres y mujeres en la probabilidad de rotación.
    Justificación: Las mujeres tienen más compromiso en el hogar.

  • Departamento (Ventas, RH, IyD)
    Hipótesis: Los empleados del departamento de Ventas tienen mayor probabilidad de rotación.
    Justificación: Ventas suele estar asociado a metas exigentes y presión laboral.

2.1.3 Variables convertidas factor.

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.

2.1.4 ¿Balanceo de los datos?

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.

3 Realiza un análisis univariado (caracterización) de la información contenida en la base de datos rotacion.

3.0.0.1 Distribución de la “Edad”

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

3.0.0.2 Tabla de frecuencia para los intervalos de la edad.

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.

3.0.0.3 Distribución de los años de experiencia.

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

3.0.0.4 Tabla de frecuencia para el Promedio de años de experiencia.

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.

3.0.0.5 Satisfacción laboral

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

3.0.0.6 Tabla de frecuencia para la satisfacción laboral

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%).

3.0.0.7 Horas extra

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

3.0.0.8 Tabla de frecuencia para las horas extras.

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

3.0.0.9 Cálculo de Tasas de Rotación

  • 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.

3.0.0.10 Viaje de negocios

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

3.0.0.11 Tabla de frecuencia para la frecuencia de viajes de negocios.

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

3.0.0.12 Cálculo de Tasas de Rotación

  • 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%).

3.0.0.13 Frecuencia del Genero.

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

3.0.0.14 Tabla de frecuencia para el Genero

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.

3.0.0.15 Departamento

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

3.0.0.16 Tabla de frecuencia para el Departamento.

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.

3.0.0.17 Identificar variables categóricas

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

3.0.0.18 Frecuencia por equilibrio entre el trabajo y la vida

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

3.0.0.19 Tabla de frecuencia para el tarbajo y el equilibrio de la vida.

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).

3.1 Pruebas Chi-Cuadrado

Aplicamos Chi - Cuadrado para evaluar si hay relación significativa entre una variable categórica y la rotación.

3.1.0.1 Prueba de Chi-cuadrado para Horas Extra vs 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.

3.1.0.2 Prueba de Chi-cuadrado para Viaje de Negocios vs 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.

3.1.0.3 Prueba de Chi-cuadrado para Departamento vs 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.

3.1.0.4 Prueba de Chi-cuadrado para Genero vs 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.

4 Realiza un análisis de bivariado en donde la variable respuesta sea rotacion codificada de la siguiente manera (y=1 es si rotación, y=0 es no rotación). Con base en estos resultados identifique cuales son las variables determinantes de la rotación e interpretar el signo del coeficiente estimado. Compare estos resultados con la hipotesis planteada en el punto 2.

4.1 Regresión Logistica

4.1.0.1 Codificar la variable respuesta

rotacion$rot_bin = ifelse(rotacion$`Rotación` == "Si", 1, 0)

4.1.0.2 Modelo Logistico para Edad

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.

4.1.0.3 Modelo Logistico para Años de Experiencia

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.

4.1.0.4 Modelo Logistico para Satisfacción Laboral

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.

4.1.0.5 Modelo Logistico para Viaje de Negocios

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.

4.1.0.6 Modelo Logistico para Horas Extra

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.

4.2 Modelo Logistico para Género

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.

4.2.0.1 Modelo Logistico para Departamento

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.

4.2.0.2 Modelo Logistico para Equilibrio

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).

5 Realice una partición en los datos de forma aleatoria donde 70% sea un set para entrenar el modelo y 30% para prueba. Estime un modelo logístico con la muestra del 70%. Muestre los resultados.

5.0.0.1 Determinar cual semilla para entrenar.

# 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.

5.0.0.2 La semilla con menor diferencia.

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.

5.1 Preparación de datos con la mejor semeilla de reproducibilidad.

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

5.1.1 Estimación del modelo

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.

6 Evaluar el poder predictivo del modelo con base en la curva ROC y el AUC en el set de datos de prueba.*

6.0.1 Análisis de desempeño del modelo logístico.

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)

6.0.2 Validación cruzada

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).

6.1 Corregir el desbalanceo

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"

6.1.1 Arreglar despalanceo por Pesos

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.

6.1.2 Arreglar despalanceo por Oversampling

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).

7 Realiza una predicción la probabilidad de que un individuo (hipotético) rote y defina un corte para decidir si se debe intervenir a este empleado o no (posible estrategia para motivar al empleado).

# 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).

8 Conclusiones y Estrategia para Reducir la 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.