Problema: Rotación de cargo

En una organización, se busca comprender y prever los factores que influyen en la rotación de empleados entre distintos cargos. La empresa ha recopilado datos históricos sobre el empleo de sus trabajadores, incluyendo variables como la antigüedad en el cargo actual, el nivel de satisfacción laboral, el salario actual, edad y otros factores relevantes. La gerencia planea desarrollar un modelo de regresión logística que permita estimar la probabilidad de que un empleado cambie de cargo en el próximo período y determinar cuales factores indicen en mayor proporción a estos cambios.

Con esta información, la empresa podrá tomar medidas proactivas para retener a su talento clave, identificar áreas de mejora en la gestión de recursos humanos y fomentar un ambiente laboral más estable y tranquilo. La predicción de la probabilidad de rotación de empleados ayudará a la empresa a tomar decisiones estratégicas informadas y a mantener un equipo de trabajo comprometido y satisfecho en sus roles actuales.

A continuación se describen los pasos que la gerencia ha propuesto para el análisis:

  1. Selección de variables

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

  2. Análisis univariado

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

  3. Análisis bivariado

    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. Estimación del modelo

    Realiza la estimación de un modelo de regresión logístico en el cual la variable respuesta es rotacion (\(y=1\) es si rotación, \(y=0\) es no rotación) y las covariables las 6 seleccionadas en el punto 1. Interprete los coeficientes del modelo y la significancia de los parámetros.

  5. Evaluación

    Evaluar el poder predictivo del modelo con base en la curva ROC y el AUC.

  6. Predicciones

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

  7. Conclusiones

    En las conclusiones adicione una discución sobre cuál sería la estrategia para disminuir la rotación en la empresa (con base en las variables que resultaron significativas en el punto 3).

Solución

Se procede a cargar los datos necesarios para el desarrollo de la actividad

#install.packages("devtools") # solo la primera vez
#devtools::install_github("dgonxalex80/paqueteMODELOS", force =TRUE)
library(paqueteMODELOS)
## Loading required package: boot
## Loading required package: broom
## Loading required package: GGally
## Loading required package: ggplot2
## Registered S3 method overwritten by 'GGally':
##   method from   
##   +.gg   ggplot2
## Loading required package: gridExtra
## Loading required package: knitr
## Loading required package: summarytools
data("rotacion")

Exploración de los datos

Antes de abordar la solución de los puntos solicitados, se llevará a cabo un proceso de exploración para conocer los datos.

Se crea un dataframe llamado df

df <- data.frame(rotacion)
head(df)
##   Rotación Edad Viaje.de.Negocios Departamento Distancia_Casa Educación
## 1       Si   41         Raramente       Ventas              1         2
## 2       No   49    Frecuentemente          IyD              8         1
## 3       Si   37         Raramente          IyD              2         2
## 4       No   33    Frecuentemente          IyD              3         4
## 5       No   27         Raramente          IyD              2         1
## 6       No   32    Frecuentemente          IyD              2         2
##   Campo_Educación Satisfacción_Ambiental Genero                   Cargo
## 1        Ciencias                      2      F        Ejecutivo_Ventas
## 2        Ciencias                      3      M Investigador_Cientifico
## 3            Otra                      4      M     Tecnico_Laboratorio
## 4        Ciencias                      4      F Investigador_Cientifico
## 5           Salud                      1      M     Tecnico_Laboratorio
## 6        Ciencias                      4      M     Tecnico_Laboratorio
##   Satisfación_Laboral Estado_Civil Ingreso_Mensual Trabajos_Anteriores
## 1                   4      Soltero            5993                   8
## 2                   2       Casado            5130                   1
## 3                   3      Soltero            2090                   6
## 4                   3       Casado            2909                   1
## 5                   2       Casado            3468                   9
## 6                   4      Soltero            3068                   0
##   Horas_Extra Porcentaje_aumento_salarial Rendimiento_Laboral Años_Experiencia
## 1          Si                          11                   3                8
## 2          No                          23                   4               10
## 3          Si                          15                   3                7
## 4          Si                          11                   3                8
## 5          No                          12                   3                6
## 6          No                          13                   3                8
##   Capacitaciones Equilibrio_Trabajo_Vida Antigüedad Antigüedad_Cargo
## 1              0                       1          6                4
## 2              3                       3         10                7
## 3              3                       3          0                0
## 4              3                       3          8                7
## 5              3                       3          2                2
## 6              2                       2          7                7
##   Años_ultima_promoción Años_acargo_con_mismo_jefe
## 1                     0                          5
## 2                     1                          7
## 3                     0                          0
## 4                     3                          0
## 5                     2                          2
## 6                     3                          6

¿Cuál es el número de registros y cuál es la cantidad de atributos?

cat("Cantidad de Registros: ", nrow(df), "\n")
## Cantidad de Registros:  1470
cat("Cantidad de atributos: ", ncol(df), "\n")
## Cantidad de atributos:  24

Tipo de dato de los atributos

str(df)
## 'data.frame':    1470 obs. of  24 variables:
##  $ Rotación                   : chr  "Si" "No" "Si" "No" ...
##  $ Edad                       : num  41 49 37 33 27 32 59 30 38 36 ...
##  $ Viaje.de.Negocios          : chr  "Raramente" "Frecuentemente" "Raramente" "Frecuentemente" ...
##  $ Departamento               : chr  "Ventas" "IyD" "IyD" "IyD" ...
##  $ Distancia_Casa             : num  1 8 2 3 2 2 3 24 23 27 ...
##  $ Educación                  : num  2 1 2 4 1 2 3 1 3 3 ...
##  $ Campo_Educación            : chr  "Ciencias" "Ciencias" "Otra" "Ciencias" ...
##  $ Satisfacción_Ambiental     : num  2 3 4 4 1 4 3 4 4 3 ...
##  $ Genero                     : chr  "F" "M" "M" "F" ...
##  $ Cargo                      : chr  "Ejecutivo_Ventas" "Investigador_Cientifico" "Tecnico_Laboratorio" "Investigador_Cientifico" ...
##  $ Satisfación_Laboral        : num  4 2 3 3 2 4 1 3 3 3 ...
##  $ Estado_Civil               : chr  "Soltero" "Casado" "Soltero" "Casado" ...
##  $ Ingreso_Mensual            : num  5993 5130 2090 2909 3468 ...
##  $ Trabajos_Anteriores        : num  8 1 6 1 9 0 4 1 0 6 ...
##  $ Horas_Extra                : chr  "Si" "No" "Si" "Si" ...
##  $ Porcentaje_aumento_salarial: num  11 23 15 11 12 13 20 22 21 13 ...
##  $ Rendimiento_Laboral        : num  3 4 3 3 3 3 4 4 4 3 ...
##  $ Años_Experiencia           : num  8 10 7 8 6 8 12 1 10 17 ...
##  $ Capacitaciones             : num  0 3 3 3 3 2 3 2 2 3 ...
##  $ Equilibrio_Trabajo_Vida    : num  1 3 3 3 3 2 2 3 3 2 ...
##  $ Antigüedad                 : num  6 10 0 8 2 7 1 1 9 7 ...
##  $ Antigüedad_Cargo           : num  4 7 0 7 2 7 0 0 7 7 ...
##  $ Años_ultima_promoción      : num  0 1 0 3 2 3 0 0 1 7 ...
##  $ Años_acargo_con_mismo_jefe : num  5 7 0 0 2 6 0 0 8 7 ...

Valores únicos de las variables categóricas

columnas_categoricas <- sapply(df, is.character)
valores_unicos_categoricos <- lapply(df[, columnas_categoricas], unique)

print(valores_unicos_categoricos)
## $Rotación
## [1] "Si" "No"
## 
## $Viaje.de.Negocios
## [1] "Raramente"      "Frecuentemente" "No_Viaja"      
## 
## $Departamento
## [1] "Ventas" "IyD"    "RH"    
## 
## $Campo_Educación
## [1] "Ciencias"    "Otra"        "Salud"       "Mercadeo"    "Tecnicos"   
## [6] "Humanidades"
## 
## $Genero
## [1] "F" "M"
## 
## $Cargo
## [1] "Ejecutivo_Ventas"        "Investigador_Cientifico"
## [3] "Tecnico_Laboratorio"     "Director_Manofactura"   
## [5] "Representante_Salud"     "Gerente"                
## [7] "Representante_Ventas"    "Director_Investigación" 
## [9] "Recursos_Humanos"       
## 
## $Estado_Civil
## [1] "Soltero"    "Casado"     "Divorciado"
## 
## $Horas_Extra
## [1] "Si" "No"

A continuación, se presentan los valores ausentes en el conjunto de datos

valores_faltantes <- colSums(is.na(df))
cat("Valores faltantes por columna:\n\n")
## Valores faltantes por columna:
cat(paste(names(valores_faltantes), ": ", valores_faltantes, sep = ""), sep = "\n")
## Rotación: 0
## Edad: 0
## Viaje.de.Negocios: 0
## Departamento: 0
## Distancia_Casa: 0
## Educación: 0
## Campo_Educación: 0
## Satisfacción_Ambiental: 0
## Genero: 0
## Cargo: 0
## Satisfación_Laboral: 0
## Estado_Civil: 0
## Ingreso_Mensual: 0
## Trabajos_Anteriores: 0
## Horas_Extra: 0
## Porcentaje_aumento_salarial: 0
## Rendimiento_Laboral: 0
## Años_Experiencia: 0
## Capacitaciones: 0
## Equilibrio_Trabajo_Vida: 0
## Antigüedad: 0
## Antigüedad_Cargo: 0
## Años_ultima_promoción: 0
## Años_acargo_con_mismo_jefe: 0

Nota: Como se observa en el resultado previo, no se detectan valores faltantes en ninguno de los atributos del dataframe.

Se verifica la ausencia de registros duplicados.

indices_duplicados <- duplicated(df) | duplicated(df, fromLast = TRUE)
cantidad_registros_duplicados <- sum(indices_duplicados)
print(paste0("La cantidad de registros duplicados es: ", cantidad_registros_duplicados))
## [1] "La cantidad de registros duplicados es: 0"

Generando el diagrama de caja y bigotes para todos los atributos numéricos

# Obtener las columnas que son numéricas
columnas_numericas <- sapply(df, is.numeric)

# Iterar sobre las columnas numéricas y generar un diagrama de cajas y bigotes para cada una
for (nombre_variable in names(df[columnas_numericas])) {
  # Generar el diagrama de cajas y bigotes para la variable actual
  boxplot(df[[nombre_variable]], main = paste("Diagrama de Cajas y Bigotes para", nombre_variable))
}

Resumen medidas estadísticas básicas

summary(df[, sapply(df, is.numeric)])
##       Edad       Distancia_Casa     Educación     Satisfacción_Ambiental
##  Min.   :18.00   Min.   : 1.000   Min.   :1.000   Min.   :1.000         
##  1st Qu.:30.00   1st Qu.: 2.000   1st Qu.:2.000   1st Qu.:2.000         
##  Median :36.00   Median : 7.000   Median :3.000   Median :3.000         
##  Mean   :36.92   Mean   : 9.193   Mean   :2.913   Mean   :2.722         
##  3rd Qu.:43.00   3rd Qu.:14.000   3rd Qu.:4.000   3rd Qu.:4.000         
##  Max.   :60.00   Max.   :29.000   Max.   :5.000   Max.   :4.000         
##  Satisfación_Laboral Ingreso_Mensual Trabajos_Anteriores
##  Min.   :1.000       Min.   : 1009   Min.   :0.000      
##  1st Qu.:2.000       1st Qu.: 2911   1st Qu.:1.000      
##  Median :3.000       Median : 4919   Median :2.000      
##  Mean   :2.729       Mean   : 6503   Mean   :2.693      
##  3rd Qu.:4.000       3rd Qu.: 8379   3rd Qu.:4.000      
##  Max.   :4.000       Max.   :19999   Max.   :9.000      
##  Porcentaje_aumento_salarial Rendimiento_Laboral Años_Experiencia
##  Min.   :11.00               Min.   :3.000       Min.   : 0.00   
##  1st Qu.:12.00               1st Qu.:3.000       1st Qu.: 6.00   
##  Median :14.00               Median :3.000       Median :10.00   
##  Mean   :15.21               Mean   :3.154       Mean   :11.28   
##  3rd Qu.:18.00               3rd Qu.:3.000       3rd Qu.:15.00   
##  Max.   :25.00               Max.   :4.000       Max.   :40.00   
##  Capacitaciones  Equilibrio_Trabajo_Vida   Antigüedad     Antigüedad_Cargo
##  Min.   :0.000   Min.   :1.000           Min.   : 0.000   Min.   : 0.000  
##  1st Qu.:2.000   1st Qu.:2.000           1st Qu.: 3.000   1st Qu.: 2.000  
##  Median :3.000   Median :3.000           Median : 5.000   Median : 3.000  
##  Mean   :2.799   Mean   :2.761           Mean   : 7.008   Mean   : 4.229  
##  3rd Qu.:3.000   3rd Qu.:3.000           3rd Qu.: 9.000   3rd Qu.: 7.000  
##  Max.   :6.000   Max.   :4.000           Max.   :40.000   Max.   :18.000  
##  Años_ultima_promoción Años_acargo_con_mismo_jefe
##  Min.   : 0.000        Min.   : 0.000            
##  1st Qu.: 0.000        1st Qu.: 2.000            
##  Median : 1.000        Median : 3.000            
##  Mean   : 2.188        Mean   : 4.123            
##  3rd Qu.: 3.000        3rd Qu.: 7.000            
##  Max.   :15.000        Max.   :17.000

Selección de variables

Variables categóricas:

  • Viaje de negocios: Se espera que aquellos empleados que viajan frecuentemente por negocios tengan una mayor probabilidad de rotación en comparación con aquellos que viajan raramente. Esto se debe a que los viajes frecuentes podrían aumentar el estrés y la fatiga, lo que a su vez podría disminuir la satisfacción laboral y aumentar la propensión a dejar la empresa.

  • Horas extra: Se espera que los empleados que trabajan horas extra tengan una mayor probabilidad de rotación en comparación con aquellos que no lo hacen. Esto se debe a que las horas extra pueden generar un desgaste adicional en los empleados, afectando su equilibrio entre el trabajo y la vida personal, lo que a su vez puede llevar a una menor satisfacción laboral y, finalmente, a una mayor probabilidad de rotación.

  • Estado civil: Se espera que existan diferencias en la probabilidad de rotación entre los diferentes estados civiles de los empleados. Por ejemplo, podría hipotetizarse que los empleados solteros tienen una mayor probabilidad de rotación debido a razones relacionadas con la búsqueda de nuevas oportunidades laborales, la movilidad geográfica o la falta de compromisos familiares en comparación con los empleados casados o divorciados.

Variables cuantitativas:

  • Satisfacción laboral: Se espera que exista una relación inversa entre la satisfacción laboral y la probabilidad de rotación. Es decir, a medida que la satisfacción laboral de un empleado disminuye, aumenta su probabilidad de rotación. Esto podría explicarse por el hecho de que los empleados insatisfechos son más propensos a buscar oportunidades laborales en otras organizaciones.

  • Equilibrio trabajo-vida: Se espera que exista una relación inversa entre el equilibrio entre el trabajo y la vida personal y la probabilidad de rotación. Es decir, a medida que el equilibrio entre el trabajo y la vida personal de un empleado se ve afectado negativamente, aumenta su probabilidad de rotación. Esto podría estar relacionado con el agotamiento o la falta de tiempo para actividades personales fuera del trabajo.

  • Edad: Se podría hipotetizar que los empleados más jóvenes tienen una probabilidad más alta de rotación debido a una mayor predisposición hacia la exploración de nuevas oportunidades laborales, mientras que los empleados mayores pueden tener una probabilidad más baja de rotación debido a una mayor estabilidad laboral y compromiso con la organización.

A continuación, se crea un nuevo dataframe con las variables seleccionadas

df_ <- df[, c(
  "Rotación", 
  "Viaje.de.Negocios", 
  "Horas_Extra", 
  "Estado_Civil", 
  "Satisfación_Laboral",
  "Equilibrio_Trabajo_Vida",
  "Edad"
)]

head(df_)
##   Rotación Viaje.de.Negocios Horas_Extra Estado_Civil Satisfación_Laboral
## 1       Si         Raramente          Si      Soltero                   4
## 2       No    Frecuentemente          No       Casado                   2
## 3       Si         Raramente          Si      Soltero                   3
## 4       No    Frecuentemente          Si       Casado                   3
## 5       No         Raramente          No       Casado                   2
## 6       No    Frecuentemente          No      Soltero                   4
##   Equilibrio_Trabajo_Vida Edad
## 1                       1   41
## 2                       3   49
## 3                       3   37
## 4                       3   33
## 5                       3   27
## 6                       2   32

Análisis univariado

En la sección “Exploración de datos”, se ha completado un análisis univariado de todas las variables del conjunto de datos.

A continuación, se exhibirá la distribución de las variables categóricas.

# Iterar a través de cada variable del nuevo dataframe y generar un gráfico de barras si es categórica
for (nombre_variable in names(df_)) {
  if (is.character(df_[[nombre_variable]]) || is.factor(df_[[nombre_variable]])) {
    # Crear el gráfico de barras
    p <- ggplot(df_, aes(x = .data[[nombre_variable]])) +
      geom_bar() +
      labs(title = paste("Gráfico de Barras para", nombre_variable))
    
    # Imprimir el gráfico
    print(p)
  }
}

A continuación, se exhibirá la distribución de las variables numéricas.

# Instalar y cargar el paquete ggplot2 si no está instalado
if (!requireNamespace("ggplot2", quietly = TRUE)) {
  install.packages("ggplot2")
}
library(ggplot2)

# Iterar a través de cada variable del nuevo dataframe y generar un histograma si es numérica
for (nombre_variable in names(df_)) {
  if (is.numeric(df_[[nombre_variable]])) {
    # Crear el histograma
    p <- ggplot(df_, aes(x = .data[[nombre_variable]])) +
      geom_histogram(binwidth = 1, fill = "skyblue", color = "black") +
      labs(title = paste("Histograma de Distribución para", nombre_variable),
           x = nombre_variable, y = "Frecuencia")
    
    # Imprimir el histograma
    print(p)
  }
}

Análisis bivariado

Se empleará la prueba de Chi-cuadrado para examinar la asociación entre cada variable categórica en ‘df_’ y la variable de respuesta Rotación. Esto permitirá determinar si existe una relación significativa entre cada variable categórica y la rotación.

#Lista de variables categóricas en df_
variables_categoricas <- c("Viaje.de.Negocios", "Horas_Extra", "Estado_Civil")

# Iterar a través de cada variable categórica en df_ y realizar la prueba de Chi-cuadrado
for(variable in variables_categoricas){
  # Crear una tabla de contingencia entre la variable categórica y la variable de respuesta Rotación
  tabla_contingencia <- table(df_$Rotación, df_[[variable]])
  
  #Realizar la prueba de Chi-cuadrado
  chi_sq <- chisq.test(tabla_contingencia)
  
  #Imprimir los resultados
  cat("Variable:", variable, "\n")
  print(chi_sq)
  cat("\n")
}
## Variable: Viaje.de.Negocios 
## 
##  Pearson's Chi-squared test
## 
## data:  tabla_contingencia
## X-squared = 24.182, df = 2, p-value = 5.609e-06
## 
## 
## Variable: Horas_Extra 
## 
##  Pearson's Chi-squared test with Yates' continuity correction
## 
## data:  tabla_contingencia
## X-squared = 87.564, df = 1, p-value < 2.2e-16
## 
## 
## Variable: Estado_Civil 
## 
##  Pearson's Chi-squared test
## 
## data:  tabla_contingencia
## X-squared = 46.164, df = 2, p-value = 9.456e-11

Los resultados de la prueba de Chi-cuadrado indican que las variables ‘Viaje.de.Negocios’, ‘Horas_Extra’ y ‘Estado_Civil’ están significativamente asociadas con la variable de respuesta ‘Rotación’. Esto sugiere que todas estas variables pueden influir en la probabilidad de que un empleado decida rotar o permanecer en la empresa. Es decir, el viaje de negocios, las horas extra y el estado civil pueden desempeñar roles importantes en la toma de decisiones de rotación de los empleados, lo que podría tener implicaciones significativas para la gestión de recursos humanos y la retención de empleados en la organización.

Se utilizará el análisis de varianza (ANOVA) para examinar la asociación entre cada variable numérica en ‘df_’ y la variable de respuesta Rotación. Esta técnica nos permitirá determinar si existe una relación significativa entre cada una de estas variables y la rotación en el conjunto de datos.

library(stats)

# Ajustar un modelo ANOVA para cada variable numérica en el conjunto de datos
modelo_anova_satisfaccion <- aov(Satisfación_Laboral ~ Rotación, data = df_)
modelo_anova_equilibrio <- aov(Equilibrio_Trabajo_Vida ~ Rotación, data = df_)
modelo_anova_edad <- aov(Edad ~ Rotación, data = df_)

#Obtener resúmenes de los modelos ANOVA
summary(modelo_anova_satisfaccion)
##               Df Sum Sq Mean Sq F value   Pr(>F)    
## Rotación       1   19.1  19.133   15.89 7.04e-05 ***
## Residuals   1468 1767.6   1.204                     
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
summary(modelo_anova_equilibrio)
##               Df Sum Sq Mean Sq F value Pr(>F)  
## Rotación       1    3.0  2.9974   6.026 0.0142 *
## Residuals   1468  730.2  0.4974                 
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
summary(modelo_anova_edad)
##               Df Sum Sq Mean Sq F value   Pr(>F)    
## Rotación       1   3109  3108.6   38.19 8.31e-10 ***
## Residuals   1468 119502    81.4                     
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Al analizar los resultados de las pruebas ANOVA en las variables numéricas, se destacan los siguientes hallazgos:

  • Para la variable ‘Satisfacción Laboral’: El valor de p es altamente significativo (p < 0.001), indicando diferencias significativas en la media de satisfacción laboral entre los grupos de rotación y no rotación. Esto sugiere que la satisfacción laboral puede influir en la decisión de rotación.

  • Respecto a la variable ‘Equilibrio Trabajo-Vida’: El valor de p también es significativo (p = 0.0142), lo que señala diferencias significativas en la media del equilibrio entre trabajo y vida personal entre los grupos de rotación y no rotación. Esto sugiere que el equilibrio entre el trabajo y la vida personal puede influir en la decisión de rotación.

  • En cuanto a la variable ‘Edad’: El valor de p es significativo (p < 0.001), lo que indica diferencias significativas en la media de edad entre los grupos de rotación y no rotación. Esto sugiere que la edad puede influir en la decisión de rotación.

En resumen, la satisfacción laboral, el equilibrio trabajo-vida y la edad parecen ser factores que influyen en la rotación de empleados.

A continuación, se muestra la matriz de correlación entre las variables numéricas

# Calcular la matriz de correlación
correlation_matrix <- round(cor(df_[, sapply(df_, is.numeric)]), 2)

# Visualizar la matriz de correlación con los números
print(correlation_matrix)
##                         Satisfación_Laboral Equilibrio_Trabajo_Vida  Edad
## Satisfación_Laboral                    1.00                   -0.02  0.00
## Equilibrio_Trabajo_Vida               -0.02                    1.00 -0.02
## Edad                                   0.00                   -0.02  1.00

Al examinar la matriz de correlación, se evidencia que las variables numéricas no muestran correlaciones significativas entre sí, lo que indica la ausencia de problemas de multicolinealidad.

Estimación del modelo

Para llevar a cabo la estimación del modelo, es necesario realizar la codificación adecuada de las variables categóricas. Es importante tener en cuenta que las cuatro variables categóricas son nominales, ya que no presentan ningún orden en sus valores. Por lo tanto, se aplicará la técnica de codificación one-hot en cada una de ellas.

#Mostrar los valores de las cuatro variables categóricas
columnas_categoricas <- c("Rotación", "Viaje.de.Negocios", "Horas_Extra", "Estado_Civil")
valores_unicos_categoricos <- lapply(df_[, columnas_categoricas], unique)

print(valores_unicos_categoricos)
## $Rotación
## [1] "Si" "No"
## 
## $Viaje.de.Negocios
## [1] "Raramente"      "Frecuentemente" "No_Viaja"      
## 
## $Horas_Extra
## [1] "Si" "No"
## 
## $Estado_Civil
## [1] "Soltero"    "Casado"     "Divorciado"

Codificación One-Hot

# Importar la librería necesaria 
library(dplyr)
## Warning: package 'dplyr' was built under R version 4.3.2
## 
## Attaching package: 'dplyr'
## The following object is masked from 'package:gridExtra':
## 
##     combine
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
# Crear un nuevo dataframe para almacenar los resultados de la codificación one-hot
df_encoded <- df_

# Obtener las variables categóricas
variables_categoricas <- c("Rotación", "Viaje.de.Negocios", "Horas_Extra", "Estado_Civil")

# Aplicar la codificación one-hot a cada variable categórica
for (var in variables_categoricas) {
  encoded_cols <- model.matrix(~ 0 + as.factor(df_[[var]]))
  colnames(encoded_cols) <- paste(var, colnames(encoded_cols), sep = "_")
  df_encoded <- cbind(df_encoded, encoded_cols)
}

# Eliminar las variables originales categóricas del nuevo dataframe
df_encoded <- df_encoded %>%
  select(-one_of(variables_categoricas))

# Mostrar las primeras filas del nuevo dataframe codificado
str(df_encoded)
## 'data.frame':    1470 obs. of  13 variables:
##  $ Satisfación_Laboral                                  : num  4 2 3 3 2 4 1 3 3 3 ...
##  $ Equilibrio_Trabajo_Vida                              : num  1 3 3 3 3 2 2 3 3 2 ...
##  $ Edad                                                 : num  41 49 37 33 27 32 59 30 38 36 ...
##  $ Rotación_as.factor(df_[[var]])No                     : num  0 1 0 1 1 1 1 1 1 1 ...
##  $ Rotación_as.factor(df_[[var]])Si                     : num  1 0 1 0 0 0 0 0 0 0 ...
##  $ Viaje.de.Negocios_as.factor(df_[[var]])Frecuentemente: num  0 1 0 1 0 1 0 0 1 0 ...
##  $ Viaje.de.Negocios_as.factor(df_[[var]])No_Viaja      : num  0 0 0 0 0 0 0 0 0 0 ...
##  $ Viaje.de.Negocios_as.factor(df_[[var]])Raramente     : num  1 0 1 0 1 0 1 1 0 1 ...
##  $ Horas_Extra_as.factor(df_[[var]])No                  : num  0 1 0 0 1 1 0 1 1 1 ...
##  $ Horas_Extra_as.factor(df_[[var]])Si                  : num  1 0 1 1 0 0 1 0 0 0 ...
##  $ Estado_Civil_as.factor(df_[[var]])Casado             : num  0 1 0 1 1 0 1 0 0 1 ...
##  $ Estado_Civil_as.factor(df_[[var]])Divorciado         : num  0 0 0 0 0 0 0 1 0 0 ...
##  $ Estado_Civil_as.factor(df_[[var]])Soltero            : num  1 0 1 0 0 1 0 0 1 0 ...

Se observa que las columnas creadas tienen nombres poco descriptivos. Por lo tanto, se realizará la tarea de renombrarlas. Además, se eliminarán algunas columnas para evitar la duplicación de información.

# Renombrar variables
names(df_encoded)[names(df_encoded) == "Rotación_as.factor(df_[[var]])No"] <- "Rotacion_No"
names(df_encoded)[names(df_encoded) == "Rotación_as.factor(df_[[var]])Si"] <- "Rotacion_Si"
names(df_encoded)[names(df_encoded) == "Viaje.de.Negocios_as.factor(df_[[var]])Frecuentemente"] <- "Viaje_Frecuente"
names(df_encoded)[names(df_encoded) == "Viaje.de.Negocios_as.factor(df_[[var]])No_Viaja"] <- "No_Viaja"
names(df_encoded)[names(df_encoded) == "Viaje.de.Negocios_as.factor(df_[[var]])Raramente"] <- "Viaje_Raramente"
names(df_encoded)[names(df_encoded) == "Horas_Extra_as.factor(df_[[var]])No"] <- "Horas_Extra_No"
names(df_encoded)[names(df_encoded) == "Horas_Extra_as.factor(df_[[var]])Si"] <- "Horas_Extra_Si"
names(df_encoded)[names(df_encoded) == "Estado_Civil_as.factor(df_[[var]])Casado"] <- "Estado_Civil_Casado"
names(df_encoded)[names(df_encoded) == "Estado_Civil_as.factor(df_[[var]])Divorciado"] <- "Estado_Civil_Divorciado"
names(df_encoded)[names(df_encoded) == "Estado_Civil_as.factor(df_[[var]])Soltero"] <- "Estado_Civil_Soltero"

str(df_encoded)
## 'data.frame':    1470 obs. of  13 variables:
##  $ Satisfación_Laboral    : num  4 2 3 3 2 4 1 3 3 3 ...
##  $ Equilibrio_Trabajo_Vida: num  1 3 3 3 3 2 2 3 3 2 ...
##  $ Edad                   : num  41 49 37 33 27 32 59 30 38 36 ...
##  $ Rotacion_No            : num  0 1 0 1 1 1 1 1 1 1 ...
##  $ Rotacion_Si            : num  1 0 1 0 0 0 0 0 0 0 ...
##  $ Viaje_Frecuente        : num  0 1 0 1 0 1 0 0 1 0 ...
##  $ No_Viaja               : num  0 0 0 0 0 0 0 0 0 0 ...
##  $ Viaje_Raramente        : num  1 0 1 0 1 0 1 1 0 1 ...
##  $ Horas_Extra_No         : num  0 1 0 0 1 1 0 1 1 1 ...
##  $ Horas_Extra_Si         : num  1 0 1 1 0 0 1 0 0 0 ...
##  $ Estado_Civil_Casado    : num  0 1 0 1 1 0 1 0 0 1 ...
##  $ Estado_Civil_Divorciado: num  0 0 0 0 0 0 0 1 0 0 ...
##  $ Estado_Civil_Soltero   : num  1 0 1 0 0 1 0 0 1 0 ...
library(dplyr)

# Eliminar las columnas especificadas
df_encoded <- df_encoded %>%
  select(-c(Rotacion_No, No_Viaja, Horas_Extra_No, Estado_Civil_Divorciado))

# Mostrar la estructura actualizada del dataframe
str(df_encoded)
## 'data.frame':    1470 obs. of  9 variables:
##  $ Satisfación_Laboral    : num  4 2 3 3 2 4 1 3 3 3 ...
##  $ Equilibrio_Trabajo_Vida: num  1 3 3 3 3 2 2 3 3 2 ...
##  $ Edad                   : num  41 49 37 33 27 32 59 30 38 36 ...
##  $ Rotacion_Si            : num  1 0 1 0 0 0 0 0 0 0 ...
##  $ Viaje_Frecuente        : num  0 1 0 1 0 1 0 0 1 0 ...
##  $ Viaje_Raramente        : num  1 0 1 0 1 0 1 1 0 1 ...
##  $ Horas_Extra_Si         : num  1 0 1 1 0 0 1 0 0 0 ...
##  $ Estado_Civil_Casado    : num  0 1 0 1 1 0 1 0 0 1 ...
##  $ Estado_Civil_Soltero   : num  1 0 1 0 0 1 0 0 1 0 ...

Se eliminan las columnas Rotacion_No, No_Viaja, Horas_Extra_No y Estado_Civil_Divorciado debido a que contenían información duplicada o redundante. Por ejemplo:

  • La columna Rotacion_No se elimina porque ya se contaba con la columna Rotacion_Si, por lo que conservar ambas no era necesario. Una columna es suficiente para representar si la rotación ocurrió o no.

  • La columna No_Viaja se elimina porque ya se tenían las columnas Viaje_Frecuente y Viaje_Raramente. La combinación de estas dos columnas puede representar si alguien viaja o no sin necesidad de tener una columna adicional para indicar que no viaja.

Esta explicación se aplica de manera similar a las demás columnas mencionadas.

Para continuar, se crea el conjunto de entrenamiento y prueba

# Cargar las librerías necesarias
library(caret)
## Loading required package: lattice
## 
## Attaching package: 'lattice'
## The following object is masked from 'package:boot':
## 
##     melanoma
library(pROC)
## Type 'citation("pROC")' for a citation.
## 
## Attaching package: 'pROC'
## The following objects are masked from 'package:stats':
## 
##     cov, smooth, var
library(dplyr)
library(glmnet)
## Loading required package: Matrix
## Loaded glmnet 4.1-8
#Se definen las variables independientes
X <- subset(df_encoded, select = -c(Rotacion_Si))

#Se define la variable dependiente
y <- df_encoded$Rotacion_Si

Se divide los datos en conjunto de entrenamiento y conjunto de prueba (70% train, 30% test)

set.seed(123)

train_index <- createDataPartition(y, p = 0.7, list = FALSE)
X_train <- X[train_index, ]
X_test <- X[-train_index, ]
y_train <- y[train_index]
y_test <- y[-train_index]

Se escala los datos

preprocess_params <- preProcess(X_train, method = c("center", "scale"))
X_train_scaled <- predict(preprocess_params, X_train)
X_test_scaled <- predict(preprocess_params, X_test)

Se crea y se entrena el modelo de regresión logística

logistic_model <- glm(
  y_train ~ ., 
  data = as.data.frame(X_train_scaled), 
  family = "binomial"
)

El resumen del modelo se muestra a continuación

summary(logistic_model)
## 
## Call:
## glm(formula = y_train ~ ., family = "binomial", data = as.data.frame(X_train_scaled))
## 
## Coefficients:
##                         Estimate Std. Error z value Pr(>|z|)    
## (Intercept)             -2.11336    0.11720 -18.032  < 2e-16 ***
## Satisfación_Laboral     -0.37806    0.09254  -4.086 4.40e-05 ***
## Equilibrio_Trabajo_Vida -0.22515    0.09130  -2.466  0.01367 *  
## Edad                    -0.63388    0.10727  -5.909 3.44e-09 ***
## Viaje_Frecuente          0.49319    0.16470   2.994  0.00275 ** 
## Viaje_Raramente          0.23077    0.17832   1.294  0.19562    
## Horas_Extra_Si           0.62043    0.08647   7.175 7.24e-13 ***
## Estado_Civil_Casado      0.25502    0.14361   1.776  0.07578 .  
## Estado_Civil_Soltero     0.63427    0.13263   4.782 1.73e-06 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 896.03  on 1028  degrees of freedom
## Residual deviance: 733.35  on 1020  degrees of freedom
## AIC: 751.35
## 
## Number of Fisher Scoring iterations: 5

Interpretación de los coeficientes del modelo y la significancia de los parámetros:

Intercepto (Intercept): El intercepto es -2.11336. Esto indica el logaritmo de la odds de que la rotación ocurra cuando todas las demás variables predictoras son cero. Es importante tener en cuenta que en muchos casos, las variables predictoras no se pueden hacer cero, por lo que la interpretación directa del intercepto puede no ser relevante en este contexto específico.

Satisfacción Laboral (Satisfación_Laboral): Un incremento de una unidad en la satisfacción laboral se asocia con un decremento de aproximadamente 0.37806 en el logaritmo de la odds de rotación. Este coeficiente es significativo (p < 0.001), lo que sugiere que la satisfacción laboral tiene un efecto significativo en la probabilidad de rotación.

Equilibrio Trabajo-Vida: Un incremento de una unidad en el equilibrio trabajo-vida se asocia con un decremento de aproximadamente 0.22515 en el logaritmo de la odds de rotación. Este coeficiente también es significativo (p = 0.0142), lo que sugiere que el equilibrio entre el trabajo y la vida personal tiene un efecto significativo en la probabilidad de rotación.

Edad (Edad): Un incremento de una unidad en la edad se asocia con un decremento de aproximadamente 0.63388 en el logaritmo de la odds de rotación. Este coeficiente es altamente significativo (p < 0.001), lo que sugiere que la edad tiene un efecto significativo en la probabilidad de rotación.

Viaje Frecuente (Viaje_Frecuente): La presencia de viajes frecuentes se asocia con un incremento de aproximadamente 0.49319 en el logaritmo de la odds de rotación. Este coeficiente es significativo (p = 0.00275), lo que sugiere que los viajes frecuentes tienen un efecto significativo en la probabilidad de rotación.

Viaje Raramente (Viaje_Raramente): La presencia de viajes raros se asocia con un incremento de aproximadamente 0.23077 en el logaritmo de la odds de rotación. Sin embargo, este coeficiente no es significativo (p = 0.19562) a un nivel de significancia del 0.05, lo que sugiere que los viajes raros pueden no tener un efecto significativo en la probabilidad de rotación.

Horas Extra (Horas_Extra_Si): La presencia de horas extra se asocia con un incremento de aproximadamente 0.62043 en el logaritmo de la odds de rotación. Este coeficiente es altamente significativo (p < 0.001), lo que sugiere que las horas extra tienen un efecto significativo en la probabilidad de rotación.

Estado Civil Casado (Estado_Civil_Casado): La pertenencia al estado civil casado se asocia con un incremento de aproximadamente 0.25502 en el logaritmo de la odds de rotación. Sin embargo, este coeficiente no es significativo (p = 0.07578) a un nivel de significancia del 0.05, lo que sugiere que el estado civil casado puede no tener un efecto significativo en la probabilidad de rotación.

Estado Civil Soltero (Estado_Civil_Soltero): La pertenencia al estado civil soltero se asocia con un incremento de aproximadamente 0.63427 en el logaritmo de la odds de rotación. Este coeficiente es altamente significativo (p < 0.001), lo que sugiere que el estado civil soltero tiene un efecto significativo en la probabilidad de rotación.

Evaluación

Se evalua el poder predictivo del modelo con base en la curva ROC y el AUC.

library(pROC)

# Calcular las probabilidades predichas
prob_pred <- predict(logistic_model, newdata = as.data.frame(X_test_scaled), type = "response")

# Calcular el AUC
roc_curve <- roc(y_test, prob_pred)
## Setting levels: control = 0, case = 1
## Setting direction: controls < cases
auc <- auc(roc_curve)

# Graficar la curva ROC
plot(roc_curve, main = "Curva ROC", col = "blue")
abline(a = 0, b = 1, lty = 2, col = "red")
legend("bottomright", legend = paste("AUC =", round(auc, 2)), col = "blue", lty = 1, cex = 0.8)

Obtener un AUC de 0.73 indica que el modelo tiene un poder predictivo moderado, lo que sugiere una capacidad de clasificación mejor que la aleatoria.

Predicciones

Se realiza la predicción de la probabilidad de que un indivudio (hipotético) rote estableciendo un humbral de 0.5

# Datos del individuo hipotético (sustituye los valores por los del individuo real)
nuevo_individuo <- data.frame(
  Satisfación_Laboral = 3,
  Equilibrio_Trabajo_Vida = 2, 
  Edad = 35, 
  Viaje_Frecuente = 1, 
  Viaje_Raramente = 0, 
  Horas_Extra_Si = 0, 
  Estado_Civil_Casado = 1, 
  Estado_Civil_Soltero = 0
)

#Escalar los datos del individuo hipotético
nuevo_individuo_scaled <- predict(preprocess_params, nuevo_individuo)

# Predecir la probabilidad de rotación para el individuo hipotético
prob_rotacion <- predict(
  logistic_model, 
  newdata = as.data.frame(nuevo_individuo_scaled), 
  type = "response"
)

# Definir un umbral 
umbral <- 0.5

# Decidir si se debe intervenir o no
if (prob_rotacion > umbral) {
  mensaje <- "Intervenir: El empleado está en riesgo de rotación."
} else {
  mensaje <- "No intervenir: El empleado no está en riesgo de rotación."
}

print(paste("Probabilidad de rotación:", round(prob_rotacion, 2)))
## [1] "Probabilidad de rotación: 0.15"
print(mensaje)
## [1] "No intervenir: El empleado no está en riesgo de rotación."

Conclusiones

Considerando las variables seleccionadas, se identifican las siguientes variables significativas:

  1. Satisfacción Laboral: Se observa una asociación negativa significativa entre la satisfacción laboral y la probabilidad de rotación. Esto implica que a menor satisfacción laboral, mayor es la probabilidad de que un empleado renuncie.

  2. Equilibrio Trabajo-Vida: También se encuentra una asociación negativa significativa entre el equilibrio trabajo-vida y la rotación. Esto sugiere que un mejor equilibrio entre el trabajo y la vida personal puede reducir la probabilidad de rotación.

  3. Edad: La edad exhibe una asociación negativa significativa con la rotación, indicando que los empleados más jóvenes tienen una mayor probabilidad de rotación.

  4. Viaje Frecuente: Los empleados que viajan con frecuencia tienen una mayor probabilidad de rotación en comparación con aquellos que no lo hacen.

  5. Horas Extra: Se encontró que los empleados que trabajan horas extra tienen una mayor probabilidad de rotación en comparación con aquellos que no lo hacen.

  6. Estado Civil: Los empleados solteros muestran una asociación significativa con la rotación en comparación con los casados.

Para abordar la rotación de empleados, se pueden considerar las siguientes estrategias:

  • Mejorar la satisfacción laboral: Identificar y abordar las áreas de insatisfacción de los empleados, ofreciendo oportunidades de desarrollo profesional, reconocimiento por el desempeño y fomentando un ambiente laboral positivo.

  • Promover el equilibrio trabajo-vida: Ofrecer flexibilidad en los horarios de trabajo, opciones de trabajo remoto y programas de bienestar para mejorar el equilibrio entre la vida personal y profesional de los empleados.

  • Brindar apoyo a los empleados jóvenes: Proporcionar oportunidades de crecimiento profesional, mentoría y desarrollo de habilidades específicas para los empleados más jóvenes.

  • Reducir la necesidad de viajar frecuentemente: Implementar tecnologías y herramientas para la colaboración remota, disminuyendo así la necesidad de viajes frecuentes.

  • Gestionar las horas extra: Monitorear y regular las horas extra para prevenir la sobrecarga laboral y el agotamiento.

  • Considerar políticas para empleados solteros: Adaptar las políticas y programas de la empresa para satisfacer las necesidades específicas de los empleados solteros.