Tomada de: pixabay.com
Tomada de: pixabay.com


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.

Antes de dar inicio con la solución de cada uno de los pasos se carga la base de datos ‘rotacion’ del ‘paqueteMODELOS’, y se realiza una exploración de los atributos.

# Cargar paquetes necesarios
library(paqueteMODELOS)
library(dplyr)   # para usar glimpse()

# Cargar la base de datos incluida en el paquete
data("rotacion", package = "paqueteMODELOS")

# Explorar la base
glimpse(rotacion)
Rows: 1,470
Columns: 24
$ Rotación                    <chr> "Si", "No", "Si", "No", "No", "No", "No", …
$ Edad                        <dbl> 41, 49, 37, 33, 27, 32, 59, 30, 38, 36, 35…
$ `Viaje de Negocios`         <chr> "Raramente", "Frecuentemente", "Raramente"…
$ Departamento                <chr> "Ventas", "IyD", "IyD", "IyD", "IyD", "IyD…
$ Distancia_Casa              <dbl> 1, 8, 2, 3, 2, 2, 3, 24, 23, 27, 16, 15, 2…
$ Educación                   <dbl> 2, 1, 2, 4, 1, 2, 3, 1, 3, 3, 3, 2, 1, 2, …
$ Campo_Educación             <chr> "Ciencias", "Ciencias", "Otra", "Ciencias"…
$ Satisfacción_Ambiental      <dbl> 2, 3, 4, 4, 1, 4, 3, 4, 4, 3, 1, 4, 1, 2, …
$ Genero                      <chr> "F", "M", "M", "F", "M", "M", "F", "M", "M…
$ Cargo                       <chr> "Ejecutivo_Ventas", "Investigador_Cientifi…
$ Satisfación_Laboral         <dbl> 4, 2, 3, 3, 2, 4, 1, 3, 3, 3, 2, 3, 3, 4, …
$ Estado_Civil                <chr> "Soltero", "Casado", "Soltero", "Casado", …
$ Ingreso_Mensual             <dbl> 5993, 5130, 2090, 2909, 3468, 3068, 2670, …
$ Trabajos_Anteriores         <dbl> 8, 1, 6, 1, 9, 0, 4, 1, 0, 6, 0, 0, 1, 0, …
$ Horas_Extra                 <chr> "Si", "No", "Si", "Si", "No", "No", "Si", …
$ Porcentaje_aumento_salarial <dbl> 11, 23, 15, 11, 12, 13, 20, 22, 21, 13, 13…
$ Rendimiento_Laboral         <dbl> 3, 4, 3, 3, 3, 3, 4, 4, 4, 3, 3, 3, 3, 3, …
$ Años_Experiencia            <dbl> 8, 10, 7, 8, 6, 8, 12, 1, 10, 17, 6, 10, 5…
$ Capacitaciones              <dbl> 0, 3, 3, 3, 3, 2, 3, 2, 2, 3, 5, 3, 1, 2, …
$ Equilibrio_Trabajo_Vida     <dbl> 1, 3, 3, 3, 3, 2, 2, 3, 3, 2, 3, 3, 2, 3, …
$ Antigüedad                  <dbl> 6, 10, 0, 8, 2, 7, 1, 1, 9, 7, 5, 9, 5, 2,…
$ Antigüedad_Cargo            <dbl> 4, 7, 0, 7, 2, 7, 0, 0, 7, 7, 4, 5, 2, 2, …
$ Años_ultima_promoción       <dbl> 0, 1, 0, 3, 2, 3, 0, 0, 1, 7, 0, 0, 4, 1, …
$ Años_acargo_con_mismo_jefe  <dbl> 5, 7, 0, 0, 2, 6, 0, 0, 8, 7, 3, 8, 3, 2, …


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.

Con base en el problema de rotación se seleccionan las siguientes variables para el modelo:

Variable Tipo Justificación Hipótesis
Horas Extra Categórica Los empleados que trabajan horas extra suelen presentar más desgaste y mayor intención de rotar. Quienes trabajan horas extra tienen mayor probabilidad de rotación.
Viaje de Negocios Categórica Los viajes frecuentes pueden generar desequilibrio trabajo–vida personal, afectando la permanencia. Quienes viajan frecuentemente tienen mayor probabilidad de rotación.
Estado Civil Categórica Puede influir en la estabilidad laboral (solteros tienden a cambiar más de empleo que casados). Empleados solteros tienen mayor probabilidad de rotación.
Ingreso Mensual Cuantitativa La compensación es un factor crítico; salarios bajos suelen asociarse a mayor rotación. A menor ingreso, mayor probabilidad de rotación.
Antigüedad Cuantitativa Los empleados con poca antigüedad son los que más fácilmente dejan la empresa. A menor antigüedad, mayor probabilidad de rotación.
Edad Cuantitativa Los más jóvenes suelen rotar más, buscando crecimiento o nuevas oportunidades. A menor edad, mayor probabilidad de rotación.


Se crea la nueva base de datos con las variables seleccionadas:

# Crear la nueva base solo con las variables seleccionadas
data_1 <- rotacion %>%
  select(Rotación, Horas_Extra, `Viaje de Negocios`, Estado_Civil,
         Ingreso_Mensual, Antigüedad, Edad)

# Verificar estructura
glimpse(data_1)
Rows: 1,470
Columns: 7
$ Rotación            <chr> "Si", "No", "Si", "No", "No", "No", "No", "No", "N…
$ Horas_Extra         <chr> "Si", "No", "Si", "Si", "No", "No", "Si", "No", "N…
$ `Viaje de Negocios` <chr> "Raramente", "Frecuentemente", "Raramente", "Frecu…
$ Estado_Civil        <chr> "Soltero", "Casado", "Soltero", "Casado", "Casado"…
$ Ingreso_Mensual     <dbl> 5993, 5130, 2090, 2909, 3468, 3068, 2670, 2693, 95…
$ Antigüedad          <dbl> 6, 10, 0, 8, 2, 7, 1, 1, 9, 7, 5, 9, 5, 2, 4, 10, …
$ Edad                <dbl> 41, 49, 37, 33, 27, 32, 59, 30, 38, 36, 35, 29, 31…

2.📊 Análisis univariado

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


2.1. Identificar el tipo de variable

str(data_1)
tibble [1,470 × 7] (S3: tbl_df/tbl/data.frame)
 $ Rotación         : chr [1:1470] "Si" "No" "Si" "No" ...
 $ Horas_Extra      : chr [1:1470] "Si" "No" "Si" "Si" ...
 $ Viaje de Negocios: chr [1:1470] "Raramente" "Frecuentemente" "Raramente" "Frecuentemente" ...
 $ Estado_Civil     : chr [1:1470] "Soltero" "Casado" "Soltero" "Casado" ...
 $ Ingreso_Mensual  : num [1:1470] 5993 5130 2090 2909 3468 ...
 $ Antigüedad       : num [1:1470] 6 10 0 8 2 7 1 1 9 7 ...
 $ Edad             : num [1:1470] 41 49 37 33 27 32 59 30 38 36 ...

En este ítem se verifica el tipo de variable correspondiente a la base de datos (data_1). Se verificó que las variables Rotación, Horas_Extra, Viaje de Negocios y Estado_Civil son de tipo categórico, ya que contienen información en forma de categorías o niveles de respuesta como “Sí/No”, “Raramente/Frecuentemente” o “Soltero/Casado”. Por otro lado, las variables Ingreso_Mensual, Antigüedad y Edad son de tipo cuantitativo, debido a que están conformadas por valores numéricos que permiten realizar cálculos estadísticos, como promedios o medidas de dispersión. De esta manera, se tiene una base con tres variables categóricas y tres cuantitativas que serán objeto del análisis univariado.


2.2. Identificar valores faltantes

colSums(is.na(data_1))
         Rotación       Horas_Extra Viaje de Negocios      Estado_Civil 
                0                 0                 0                 0 
  Ingreso_Mensual        Antigüedad              Edad 
                0                 0                 0 


El resultado me arroja que no hay datos faltantes.


2.3. Identificar escritura de las categorías

# Solo categorías únicas de las variables categóricas
categoricas <- data_1[, sapply(data_1, function(x) is.factor(x) | is.character(x))]
lapply(categoricas, unique)
$Rotación
[1] "Si" "No"

$Horas_Extra
[1] "Si" "No"

$`Viaje de Negocios`
[1] "Raramente"      "Frecuentemente" "No_Viaja"      

$Estado_Civil
[1] "Soltero"    "Casado"     "Divorciado"


Con este resultado verificamos que no hay inconsistencias en la escritura de las categorías.


2.4. Para las variables categóricas


a.Tabla de frecuencia relativa y porcentaje
library(dplyr)
library(ggplot2)

# Seleccionar solo las variables categóricas
categoricas <- data_1 %>% 
  select(where(~is.factor(.) | is.character(.)))

# ---- 1. Tablas de frecuencias (absolutas y relativas) ----
tablas_categoricas <- lapply(names(categoricas), function(var) {
  abs_freq <- table(categoricas[[var]])                 # absoluta
  rel_freq <- prop.table(abs_freq) * 100                # relativa (%)
  data.frame(
    Variable = var,
    Categoria = names(abs_freq),
    Frecuencia = as.vector(abs_freq),
    Porcentaje = round(as.vector(rel_freq), 2)
  )
})

# Unir todas las tablas en un solo data.frame
tablas_categoricas <- do.call(rbind, tablas_categoricas)
print(tablas_categoricas)
            Variable      Categoria Frecuencia Porcentaje
1           Rotación             No       1233      83.88
2           Rotación             Si        237      16.12
3        Horas_Extra             No       1054      71.70
4        Horas_Extra             Si        416      28.30
5  Viaje de Negocios Frecuentemente        277      18.84
6  Viaje de Negocios       No_Viaja        150      10.20
7  Viaje de Negocios      Raramente       1043      70.95
8       Estado_Civil         Casado        673      45.78
9       Estado_Civil     Divorciado        327      22.24
10      Estado_Civil        Soltero        470      31.97

b. Gráficos de barras o de pastel
library(patchwork)  # instalar si no lo tienes: install.packages("patchwork")
# Lista donde guardaremos los gráficos
lista_graficos <- list()
# ---- Graficos de torta ----
for (var in names(categoricas)) {
  df_plot <- as.data.frame(table(categoricas[[var]]))
  colnames(df_plot) <- c("Categoria", "Frecuencia")
  df_plot$Porcentaje <- round(df_plot$Frecuencia / sum(df_plot$Frecuencia) * 100, 2)
  
  p <- ggplot(df_plot, aes(x = "", y = Frecuencia, fill = Categoria)) +
    geom_bar(stat = "identity", width = 1, color = "white") +
    coord_polar("y", start = 0) +
    geom_text(aes(label = paste0(Porcentaje, "%")), 
              position = position_stack(vjust = 0.5), size = 3) +
    labs(title = paste("Gráfico de Torta -", var),
         x = NULL, y = NULL) +
    theme_void() +
    theme(legend.position = "right")
  
  lista_graficos[[var]] <- p
}

# Unir todos los gráficos en un solo plano
wrap_plots(lista_graficos)

Los gráficos representan las cuatro variables categóricas presentes en la base de datos (data_1), indicando en la variable rotación en la categoría (No) el 83,88% y en la categoría (Si) un 16.12%, evidenciando que hay un ligero desbalance entre las clases. Por otro lado, en el atributo Horas_Extras el 71.7% de las personas no realizan horas extras y tan solo el 28,3% si realizan horas extras. Luego, el diagrama de torta para la variable Viaje de Negocios, indica que los empleados de la organización el 70.95% viaja raramente, el 18,84% viajan frecuentemente y tan solo el 10,2% no viajan. Por último, el atributo Estrato civil con un 45,78% los empreados son casados, el 31,97% son solteros y el 22,24% son divorciados.


2.5. Para las variables numéricas


a. Medidas de tendencia central: media, mediana y moda
library(dplyr)
library(purrr)

# Función para calcular la moda
get_mode <- function(x) {
  ux <- unique(x)
  ux[which.max(tabulate(match(x, ux)))]
}

# Seleccionar solo variables numéricas
numericas <- data_1 %>% select(where(is.numeric))

# Calcular medidas por cada variable y combinarlas en tabla
tendencia_central_tabla <- map_dfr(names(numericas), function(var) {
  x <- numericas[[var]]
  tibble(
    Variable = var,
    Media    = mean(x, na.rm = TRUE),
    Mediana  = median(x, na.rm = TRUE),
    Moda     = get_mode(x)
  )
})

# Mostrar tabla
tendencia_central_tabla
# A tibble: 3 × 4
  Variable          Media Mediana  Moda
  <chr>             <dbl>   <dbl> <dbl>
1 Ingreso_Mensual 6503.      4919  2342
2 Antigüedad         7.01       5     5
3 Edad              36.9       36    35

Los resultados muestran que la variable Ingreso_Mensual presenta una distribución asimétrica positiva, ya que la media 6503 es considerablemente mayor que la mediana 4919 y la moda 2342, lo que sugiere la presencia de algunos valores muy altos que elevan el promedio. En cambio, Antigüedad tiene una media 7.01 ligeramente superior a la mediana 5 y coincide con la moda 5, lo que indica una leve asimetría positiva, con la mayoría de los valores concentrados en niveles bajos y pocos casos con muchos años que incrementan el promedio. Finalmente, Edad presenta valores muy similares entre media 36.9, mediana 36 y moda 35, lo cual indica que su distribución es aproximadamente simétrica, y todas las medidas representan de manera adecuada el valor típico de la variable.


b. Medidas de dispersión: desviación estándar, rango, cuartiles.
library(dplyr)
library(purrr)

# Seleccionar solo variables numéricas
numericas <- data_1 %>% select(where(is.numeric))

# Calcular medidas de dispersión por cada variable
dispersion_tabla <- map_dfr(names(numericas), function(var) {
  x <- numericas[[var]]
  tibble(
    Variable         = var,
    Minimo            = min(x, na.rm = TRUE),
    Maximo            = max(x, na.rm = TRUE),
    Rango             = max(x, na.rm = TRUE) - min(x, na.rm = TRUE),
    Varianza          = var(x, na.rm = TRUE),
    Desviacion_Estandar = sd(x, na.rm = TRUE),
    Coef_Variacion    = (sd(x, na.rm = TRUE) / mean(x, na.rm = TRUE)) * 100
  )
})

# Mostrar tabla
dispersion_tabla
# A tibble: 3 × 7
  Variable       Minimo Maximo Rango Varianza Desviacion_Estandar Coef_Variacion
  <chr>           <dbl>  <dbl> <dbl>    <dbl>               <dbl>          <dbl>
1 Ingreso_Mensu…   1009  19999 18990   2.22e7             4708.             72.4
2 Antigüedad          0     40    40   3.75e1                6.13           87.4
3 Edad               18     60    42   8.35e1                9.14           24.7

Los resultados muestran que Ingreso_Mensual tiene un rango muy amplio 1009 a 19999 y una desviación estándar alta 4708 con un coeficiente de variación del 72.4%, lo que indica gran dispersión y heterogeneidad entre los ingresos. Antigüedad también presenta una dispersión considerable rango de 0 a 40, CV de 87.4%, lo que refleja mucha variabilidad en los años de permanencia. En cambio, Edad tiene un rango más moderado 18 a 60, una desviación estándar de 9.14 y un CV de 24.7%, lo que indica que es la variable más homogénea del conjunto.


c. Distribución: histograma, boxplot.
library(ggplot2)
library(dplyr)
library(tidyr)

# Seleccionar solo variables numéricas
numericas <- data_1 %>% select(where(is.numeric))

# Pasar a formato largo
numericas_long <- numericas %>%
  pivot_longer(cols = everything(), names_to = "Variable", values_to = "Valor")

# 1. Histogramas (en una fila de 3 para que el tercero quede centrado si son 3 variables)
ggplot(numericas_long, aes(x = Valor, fill = Variable)) +
  geom_histogram(bins = 30, color = "black", alpha = 0.7) +
  facet_wrap(~Variable, scales = "free", ncol = 3) +
  theme_minimal() +
  labs(title = "Distribución - Histogramas",
       x = "Valor", y = "Frecuencia") +
  theme(
    legend.position = "none",
    plot.title = element_text(hjust = 0.5) # centra el título
  )

Las distribuciones muestran que la antigüedad de los empleados se concentra en los primeros años, con pocos casos de trayectorias largas; la edad presenta un patrón aproximadamente simétrico, concentrado entre los 30 y 40 años; y el ingreso mensual es asimétrico a la derecha, con la mayoría de empleados en rangos bajos y unos pocos con ingresos muy altos.

# 2. Boxplots (cada variable en un plano separado con facet_wrap)
ggplot(numericas_long, aes(x = Variable, y = Valor, fill = Variable)) +
  geom_boxplot(alpha = 0.7) +
  facet_wrap(~Variable, scales = "free", ncol = 3) +
  theme_minimal() +
  labs(title = "Distribución - Boxplots",
       x = "Variable", y = "Valor") +
  theme(
    legend.position = "none",
    plot.title = element_text(hjust = 0.5) # centra el título
  )


Los boxplot confirman lo observado en los histogramas anteriores: la antigüedad se concentra en valores bajos con algunos empleados con muchos años en la empresa (outliers), la edad muestra una distribución centrada en adultos de 30 a 40 años, y el ingreso mensual presenta gran dispersión con pocos casos de salarios muy altos, lo que coincide con la asimetría vista en la distribución.


2.6. Tratamiento de los valores atípicos

Como hay valores atípicos en las variables Antiguedad e Ingreso_Mensual, es necesario aplicar un método que conserve los valores originales, ya que al parecer son datos verdaderos, por lo tanto se aplica el método de escalamiento robusto.

# Seleccionar solo las variables numéricas
num_vars <- data_1[, sapply(data_1, is.numeric)]

# Función de escalamiento robusto
robust_scaling <- function(x) {
  mediana <- median(x, na.rm = TRUE)
  iqr_val <- IQR(x, na.rm = TRUE)
  return((x - mediana) / iqr_val)
}

# Hacer una copia de la base original
data_2 <- data_1

# Aplicar solo a las variables con outliers
data_2$Ingreso_Mensual <- robust_scaling(data_1$Ingreso_Mensual)
data_2$Antigüedad <- robust_scaling(data_1$Antigüedad)

# Verificar primeras filas
head(data_2)
# A tibble: 6 × 7
  Rotación Horas_Extra `Viaje de Negocios` Estado_Civil Ingreso_Mensual
  <chr>    <chr>       <chr>               <chr>                  <dbl>
1 Si       Si          Raramente           Soltero               0.196 
2 No       No          Frecuentemente      Casado                0.0386
3 Si       Si          Raramente           Soltero              -0.517 
4 No       Si          Frecuentemente      Casado               -0.368 
5 No       No          Raramente           Casado               -0.265 
6 No       No          Frecuentemente      Soltero              -0.339 
# ℹ 2 more variables: Antigüedad <dbl>, Edad <dbl>


2.7. Conversión de las variables categóricas a factores

En este paso se convierte las variables categóricas en factores, que es el tipo de dato que R requiere para tratarlas correctamente en los modelos. En particular, la variable Rotación se transforma en un factor binario con valores 0 (No) y 1 (Sí), lo que permitirá utilizarla como variable respuesta en la regresión logística. De igual forma, variables como Horas_Extra, Viaje de Negocios y Estado_Civil se convierten en factores para que sus distintas categorías sean reconocidas adecuadamente durante el análisis. Finalmente, se revisa la estructura de los datos para confirmar que las transformaciones se realizaron de manera correcta.

# Convertir variables categóricas a factor
# Convertir Rotación en factor binario 0 = No, 1 = Si
data_2$Rotación <- factor(ifelse(data_2$Rotación == "Si", 1, 0), levels = c(0,1))
data_2$`Horas_Extra` <- factor(data_2$`Horas_Extra`)
data_2$`Viaje de Negocios` <- factor(data_2$`Viaje de Negocios`)
data_2$Estado_Civil <- factor(data_2$Estado_Civil)

# Revisar estructura
head(data_2)
# A tibble: 6 × 7
  Rotación Horas_Extra `Viaje de Negocios` Estado_Civil Ingreso_Mensual
  <fct>    <fct>       <fct>               <fct>                  <dbl>
1 1        Si          Raramente           Soltero               0.196 
2 0        No          Frecuentemente      Casado                0.0386
3 1        Si          Raramente           Soltero              -0.517 
4 0        Si          Frecuentemente      Casado               -0.368 
5 0        No          Raramente           Casado               -0.265 
6 0        No          Frecuentemente      Soltero              -0.339 
# ℹ 2 more variables: Antigüedad <dbl>, Edad <dbl>

3.📈 Análisis bivariado

Realiza un análisis 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 hipótesis planteada en el punto 2.


3.1. Resumen general del atributo rotacion

table(data_2$Rotación)

   0    1 
1233  237 
prop.table(table(data_2$Rotación))

        0         1 
0.8387755 0.1612245 

En términos de proporciones: aproximadamente el 16% rotó y el 84% permaneció. Es una distribución desbalanceada, pero común en este tipo de problemas. Esto confirma los resultados obtenidos en el análisis exploratorio realizado en el primer paso.


3.2. Análisis bivariado con variables categóricas y Rotación. Tablas y chi-cuadrado / test exacto

# Reemplaza con tus variables categóricas
categoricas <- c("Horas_Extra", "Viaje de Negocios", "Estado_Civil")

for (v in categoricas) {
  cat("\n\n--- Variable:", v, " ---\n")
  
  print(table(data_2[[v]], data_2$`Rotación`, useNA = "ifany"))
  print(prop.table(table(data_2[[v]], data_2$`Rotación`), margin = 1))
  
  tab <- table(data_2[[v]], data_2$`Rotación`)
  if (any(tab < 5)) {
    print(fisher.test(tab))
  } else {
    print(chisq.test(tab))
  }
  
  f <- as.formula(paste0("`Rotación` ~ `", v, "`"))
  mod <- glm(f, data = data_2, family = binomial)
  print(broom::tidy(mod, exponentiate = TRUE, conf.int = TRUE))
}


--- Variable: Horas_Extra  ---
    
       0   1
  No 944 110
  Si 289 127
    
             0         1
  No 0.8956357 0.1043643
  Si 0.6947115 0.3052885

    Pearson's Chi-squared test with Yates' continuity correction

data:  tab
X-squared = 87.564, df = 1, p-value < 2.2e-16

# A tibble: 2 × 7
  term          estimate std.error statistic   p.value conf.low conf.high
  <chr>            <dbl>     <dbl>     <dbl>     <dbl>    <dbl>     <dbl>
1 (Intercept)      0.117     0.101    -21.3  5.05e-101   0.0951     0.141
2 Horas_ExtraSi    3.77      0.147      9.06 1.35e- 19   2.83       5.03 


--- Variable: Viaje de Negocios  ---
                
                   0   1
  Frecuentemente 208  69
  No_Viaja       138  12
  Raramente      887 156
                
                         0         1
  Frecuentemente 0.7509025 0.2490975
  No_Viaja       0.9200000 0.0800000
  Raramente      0.8504314 0.1495686

    Pearson's Chi-squared test

data:  tab
X-squared = 24.182, df = 2, p-value = 5.609e-06

# A tibble: 3 × 7
  term                  estimate std.error statistic  p.value conf.low conf.high
  <chr>                    <dbl>     <dbl>     <dbl>    <dbl>    <dbl>     <dbl>
1 (Intercept)              0.332     0.139     -7.94 1.98e-15    0.251     0.433
2 `Viaje de Negocios`N…    0.262     0.331     -4.04 5.36e- 5    0.131     0.485
3 `Viaje de Negocios`R…    0.530     0.164     -3.87 1.07e- 4    0.386     0.734


--- Variable: Estado_Civil  ---
            
               0   1
  Casado     589  84
  Divorciado 294  33
  Soltero    350 120
            
                     0         1
  Casado     0.8751857 0.1248143
  Divorciado 0.8990826 0.1009174
  Soltero    0.7446809 0.2553191

    Pearson's Chi-squared test

data:  tab
X-squared = 46.164, df = 2, p-value = 9.456e-11

# A tibble: 3 × 7
  term                  estimate std.error statistic  p.value conf.low conf.high
  <chr>                    <dbl>     <dbl>     <dbl>    <dbl>    <dbl>     <dbl>
1 (Intercept)              0.143     0.117    -16.7  1.33e-62    0.113     0.178
2 Estado_CivilDivorcia…    0.787     0.217     -1.10 2.71e- 1    0.508     1.19 
3 Estado_CivilSoltero      2.40      0.157      5.57 2.54e- 8    1.77      3.28 

El análisis bivariado mostró que las variables categóricas seleccionadas están significativamente asociadas con la rotación del personal. En el caso de Horas_Extra, el 30,5% de quienes realizan horas extra rotan frente al 10,4% de quienes no lo hacen, y el modelo logístico indicó que trabajar horas extra aumenta 3,77 veces la probabilidad de rotación (OR = 3.77; IC95%: 2.83–5.03; p < 0.001). Para Viaje de Negocios, el 24,9% de quienes viajan frecuentemente y el 15% de quienes viajan raramente rotan, frente al 8% de quienes no viajan; esta variable también resultó significativa (p = 5.6e-06), con un aumento del riesgo especialmente en quienes viajan frecuentemente. Finalmente, para Estado_Civil, el 25,5% de los solteros rotan frente al 12,4% de los casados y el 10% de los divorciados, y el modelo indicó que los solteros tienen 2,40 veces más probabilidad de rotar que los casados (OR = 2.40; IC95%: 1.77–3.28; p < 0.001), mientras que los divorciados no presentaron diferencias significativas (p = 0.27). Estos resultados confirman que trabajar horas extra, viajar con frecuencia y ser soltero incrementan significativamente la probabilidad de rotación.

Por otro lado, se aplicó la prueba de chi-cuadrado de Pearson para evaluar la asociación entre cada variable categórica y la rotación del personal, encontrándose valores de p < 0.05, lo que indica que dichas variables están significativamente asociadas con la rotación.


3.3. Variables numéricas: comparar medias y prueba t / Wilcoxon

# Reemplaza con tus variables numéricas
numericas <- c("Ingreso_Mensual","Antigüedad", "Edad")

for (v in numericas) {
  cat("\n\n--- Variable:", v, " ---\n")
  
  # resumen por grupo
  print(
    data_2 %>% 
      group_by(`Rotación`) %>% 
      summarise(
        n = n(),
        mean = mean(.data[[v]], na.rm = TRUE),
        sd = sd(.data[[v]], na.rm = TRUE)
      )
  )
}


--- Variable: Ingreso_Mensual  ---
# A tibble: 2 × 4
  Rotación     n    mean    sd
  <fct>    <int>   <dbl> <dbl>
1 0         1233  0.350  0.881
2 1          237 -0.0241 0.666


--- Variable: Antigüedad  ---
# A tibble: 2 × 4
  Rotación     n   mean    sd
  <fct>    <int>  <dbl> <dbl>
1 0         1233 0.395  1.02 
2 1          237 0.0218 0.992


--- Variable: Edad  ---
# A tibble: 2 × 4
  Rotación     n  mean    sd
  <fct>    <int> <dbl> <dbl>
1 0         1233  37.6  8.89
2 1          237  33.6  9.69

Los resultados muestran que existen diferencias notorias entre los empleados que permanecen en la organización y los que rotan: quienes no rotaron presentan en promedio un mayor ingreso mensual (0.350 vs -0.0241), mayor antigüedad (0.395 vs 0.0218) y mayor edad (37.6 vs 33.6 años) en comparación con quienes sí rotaron. Esto sugiere que la rotación tiende a ser más frecuente en empleados jóvenes, con menor antigüedad y menores ingresos, lo que podría indicar que estos factores están asociados con una mayor probabilidad de abandono en la organización.

# test de normalidad sencillo por grupo (Shapiro)
  by_group <- split(data_2[[v]], data_2$Rotación)
  if (all(sapply(by_group, function(x) sum(!is.na(x))) >= 3)) {
    print(lapply(by_group, function(x) shapiro.test(x)))
  }
$`0`

    Shapiro-Wilk normality test

data:  x
W = 0.97821, p-value = 1.103e-12


$`1`

    Shapiro-Wilk normality test

data:  x
W = 0.94571, p-value = 1.005e-07

La prueba de Shapiro-Wilk aplicada a los grupos con y sin rotación muestra que en ambos casos los datos no siguen una distribución normal, ya que se obtuvieron valores de 𝑊=0.97821 con 𝑝< 0.000000000001 para el grupo sin rotación y 𝑊=0.94571 con 𝑝< 0.0000001 para el grupo con rotación, por lo que se rechaza la hipótesis nula de normalidad en ambos grupos.

# test t o Wilcoxon
  if (var.test(data_2[[v]] ~ data_2$Rotación)$p.value > 0.05) {
    print(t.test(data_2[[v]] ~ data_2$Rotación, var.equal = TRUE))
  } else {
    print(t.test(data_2[[v]] ~ data_2$Rotación))
  }

    Two Sample t-test

data:  data_2[[v]] by data_2$Rotación
t = 6.1796, df = 1468, p-value = 8.31e-10
alternative hypothesis: true difference in means between group 0 and group 1 is not equal to 0
95 percent confidence interval:
 2.699188 5.209709
sample estimates:
mean in group 0 mean in group 1 
       37.56204        33.60759 

La prueba t de muestras independientes indica que existe una diferencia estadísticamente significativa en la media de la variable analizada entre los empleados que no presentan rotación (media = 37.56) y los que sí presentan rotación (media = 33.61), con un estadístico t=6.18, gl=1468 y un valor p=8.31×10^−10, lo que permite rechazar la hipótesis nula de igualdad de medias. Además, el intervalo de confianza del 95% para la diferencia de medias (2.70 a 5.21) confirma que, en promedio, quienes no rotan tienen valores significativamente mayores en esta variable que quienes sí rotan.


3.4. Mapa de calor de correlaciones

library(dplyr)
library(reshape2)
library(ggplot2)

# Selección de variables numéricas
num_vars <- data_2 %>% 
  select(Ingreso_Mensual, Antigüedad, Edad)

# Calcular matriz de correlación (Pearson)
corr_matrix <- cor(num_vars, use = "complete.obs", method = "pearson")

# Pasar a formato largo para ggplot
corr_long <- melt(corr_matrix)

# Gráfico tipo heatmap con ggplot2
ggplot(corr_long, aes(Var1, Var2, fill = value)) +
  geom_tile(color = "white") +
  geom_text(aes(label = round(value, 2)), color = "black", size = 5) +
  scale_fill_gradient2(low = "#B2182B", mid = "white", high = "#2166AC",
                       midpoint = 0, limit = c(-1,1), name = "Correlación") +
  theme_minimal(base_size = 14) +
  theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1),
        panel.grid = element_blank(),
        plot.title = element_text(hjust = 0.5, face = "bold")) +  # Centrado del título
  labs(title = "Mapa de calor de correlaciones",
       x = "", y = "")

El mapa de calor de correlaciones muestra que existe una relación positiva moderada entre las variables numéricas analizadas. En particular, la mayor asociación se observa entre Ingreso Mensual y Antigüedad (r ≈ 0.51) y entre Ingreso Mensual y Edad (r ≈ 0.50), lo cual sugiere que a mayor antigüedad y edad, los empleados tienden a tener ingresos más altos. La correlación entre Edad y Antigüedad (r ≈ 0.31) es positiva pero más débil, indicando que aunque están relacionadas, no necesariamente crecen de manera proporcional. Ninguna de las correlaciones es excesivamente alta (todas < 0.8), lo que reduce el riesgo de multicolinealidad severa en el modelo, aunque la relación entre ingreso y antigüedad merece ser monitoreada.


3.5. Regresión logística bivariada: variables numéricas vs. Rotación

library(dplyr)
library(broom)

# seleccionar solo variables numéricas (todas las covariables)
numericas <- data_2 %>% select(where(is.numeric))

# asegurarse de que Rotación sea factor
data_2$Rotación <- factor(data_2$Rotación)

# lista para resultados
resultados <- list()

for (v in names(numericas)) {
  # fórmula con Rotación como respuesta
  f <- as.formula(paste("`Rotación` ~", v))
  
  # modelo binomial
  mod <- glm(f, data = data_2, family = binomial)
  
  # resultados tidy con OR y CI
  resultados[[v]] <- broom::tidy(mod, exponentiate = TRUE, conf.int = TRUE)
}

# combinar resultados en un data frame
resultados_df <- bind_rows(resultados, .id = "Variable")
resultados_df
# A tibble: 6 × 8
  Variable       term  estimate std.error statistic   p.value conf.low conf.high
  <chr>          <chr>    <dbl>     <dbl>     <dbl>     <dbl>    <dbl>     <dbl>
1 Ingreso_Mensu… (Int…    0.211   0.0719    -21.6   1.27e-103    0.183     0.243
2 Ingreso_Mensu… Ingr…    0.499   0.118      -5.88  4.12e-  9    0.392     0.623
3 Antigüedad     (Int…    0.210   0.0718    -21.7   1.73e-104    0.182     0.242
4 Antigüedad     Anti…    0.616   0.0956     -5.07  4.03e-  7    0.507     0.738
5 Edad           (Int…    1.23    0.306       0.674 5.00e-  1    0.676     2.25 
6 Edad           Edad     0.949   0.00870    -6.01  1.90e-  9    0.933     0.965

Los modelos binomiales muestran que todas las covariables numéricas evaluadas son significativas (p < 0.001). Los coeficientes indican que a mayor ingreso mensual (OR ≈ 0.50), mayor antigüedad (OR ≈ 0.62) y mayor edad (OR ≈ 0.95), la probabilidad de rotación disminuye, lo que evidencia un efecto protector de estas variables sobre la rotación.


3.6. Distribución de la Rotación según Variables Categóricas

library(dplyr)
library(plotly)

categoricas <- c("Horas_Extra", "Viaje de Negocios", "Estado_Civil")

plots <- list()

for (i in seq_along(categoricas)) {
  v <- categoricas[i]
  
  df_plot <- data_2 %>%
    count(Rotación, var = .data[[v]]) %>%
    group_by(var) %>%
    mutate(prop = n / sum(n))
  
  p <- plot_ly(
    df_plot,
    x = ~var,
    y = ~prop,
    color = ~Rotación,
    colors = c("0" = "#1f77b4", "1" = "#ff7f0e"),  # colores personalizados
    type = "bar",
    showlegend = (i == 1)   # solo el primer gráfico muestra la leyenda
  ) %>%
    layout(
      barmode = "stack",
      title = paste("Distribución de la Rotación según Variables Categóricas"),
      yaxis = list(title = "Rotación"),
      xaxis = list(title = v)
    )
  
  plots[[v]] <- p
}

subplot(plots, nrows = 1, shareY = TRUE, titleX = TRUE)

La gráfica muestra que la rotación laboral tiende a ser más frecuente en los empleados que realizan horas extra, viajan con mayor frecuencia por negocios y son solteros o divorciados, mientras que aquellos que no hacen horas extra, viajan poco o nada y están casados presentan mayor estabilidad laboral; esto sugiere que tanto las condiciones de trabajo (carga de horas y viajes) como factores personales (estado civil) pueden influir significativamente en la decisión de permanecer o abandonar la organización.


3.7. Distribución de variables laborales numéricas según rotación

library(dplyr)
library(plotly)

# Variables numéricas
numericas <- c("Ingreso_Mensual", "Antigüedad", "Edad")

# Paletas personalizadas por variable
colores_list <- list(
  Ingreso_Mensual = c("0" = "#1f77b4", "1" = "#ff7f0e"),  # azul - naranja
  Antigüedad      = c("0" = "#2ca02c", "1" = "#d62728"),  # verde - rojo
  Edad            = c("0" = "#9467bd", "1" = "#8c564b")   # morado - café
)

plots <- lapply(numericas, function(var) {
  df <- data_2 %>%
    mutate(valor = .data[[var]])
  
  plot_ly(df,
          x = ~Rotación,
          y = ~valor,
          type = "violin",
          color = ~Rotación,
          colors = colores_list[[var]],   # colores distintos por variable
          box = list(visible = TRUE),
          meanline = list(visible = TRUE),
          name = var) %>%
    layout(
      title = paste("Distribución de variables laborales según rotación"),
      yaxis = list(title = var),
      xaxis = list(title = "Rotación")
    )
})

# Mostrar en varias filas (uno por variable)
subplot(plots, nrows = length(numericas), shareX = TRUE)

La gráfica muestra distribuciones de varias variables (Ingreso Mensual, Antigüedad y Edad) segmentadas según la variable de Rotación (0 = no rotó, 1 = rotó), mediante diagramas de violín que combinan densidad y boxplot. Se observa que la distribución de Edad es bastante similar entre quienes rotaron y quienes no, con medianas cercanas a los 33–35 años. Para Ingreso Mensual y Antigüedad, las distribuciones presentan sesgos y valores atípicos, especialmente en quienes rotaron, indicando que hay individuos con ingresos o antigüedades muy distintos al grupo principal. En general, la gráfica sugiere que la Edad podría no diferenciar significativamente la rotación, mientras que Ingreso Mensual y Antigüedad muestran más variabilidad entre los grupos.

Comparación:

La comparación entre los resultados del punto 2 y el punto 3 evidencia cómo las descripciones generales de las variables se matizan cuando se analizan en función de la rotación laboral. En el punto 2 se observa que la mayoría de empleados no rota, no realiza horas extra, viaja raramente y está casada, además de presentar edades concentradas en los 30–40 años, antigüedades bajas y salarios mayoritariamente en rangos bajos. Sin embargo, el punto 3 revela que, dentro de esas distribuciones generales, son precisamente los empleados que se salen de los patrones mayoritarios quienes hacen horas extra, viajan con frecuencia y son solteros o divorciados los que tienden a rotar más. Asimismo, aunque la edad no muestra grandes diferencias entre los grupos, variables como el ingreso y la antigüedad sí exhiben mayor variabilidad y presencia de casos atípicos en quienes rotan. En conjunto, mientras el análisis descriptivo refleja estabilidad y concentración en ciertos perfiles, el análisis relacional muestra que las condiciones laborales exigentes y algunos factores personales marcan diferencias significativas en la propensión a abandonar la organización.


4. ƒ(x) 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.


4.1. Ajuste general del modelo

# Modelo de regresión logística
modelo_logit <- glm(Rotación ~ Horas_Extra + `Viaje de Negocios` + Estado_Civil +
                      Ingreso_Mensual + Antigüedad + Edad, 
                    data = data_2, family = binomial)

# Resumen del modelo
summary(modelo_logit)

Call:
glm(formula = Rotación ~ Horas_Extra + `Viaje de Negocios` + 
    Estado_Civil + Ingreso_Mensual + Antigüedad + Edad, family = binomial, 
    data = data_2)

Coefficients:
                             Estimate Std. Error z value Pr(>|z|)    
(Intercept)                  -0.80514    0.40181  -2.004 0.045096 *  
Horas_ExtraSi                 1.45145    0.15735   9.224  < 2e-16 ***
`Viaje de Negocios`No_Viaja  -1.30292    0.35077  -3.714 0.000204 ***
`Viaje de Negocios`Raramente -0.64548    0.18036  -3.579 0.000345 ***
Estado_CivilDivorciado       -0.27018    0.22858  -1.182 0.237211    
Estado_CivilSoltero           0.81950    0.17060   4.804 1.56e-06 ***
Ingreso_Mensual              -0.39999    0.14481  -2.762 0.005741 ** 
Antigüedad                   -0.24511    0.11098  -2.209 0.027201 *  
Edad                         -0.02834    0.01008  -2.811 0.004934 ** 
---
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: 1092.9  on 1461  degrees of freedom
AIC: 1110.9

Number of Fisher Scoring iterations: 5
# Calcular pseudo-R² de McFadden
null_dev <- modelo_logit$null.deviance
resid_dev <- modelo_logit$deviance

pseudo_r2 <- 1 - (resid_dev / null_dev)
pseudo_r2
[1] 0.1584244


  • Null deviance = 1298.6 vs. Residual deviance = 1092.9: el modelo con covariables explica mejor la variable respuesta que el modelo nulo (sin predictores).

  • AIC = 1110.9: es una medida de bondad de ajuste, útil para comparar con otros modelos.

  • El pseudo-R² de McFadden obtenido (~0.16 o 15,8%) indica que el modelo con las covariables incluidas explica aproximadamente el 16% de la variabilidad en la probabilidad de rotación en comparación con el modelo nulo, es decir, aquel sin predictores. Aunque este valor no es particularmente elevado, se considera aceptable en estudios de carácter social y de comportamiento laboral, donde la complejidad de los fenómenos y la presencia de múltiples factores no observados hacen que los niveles de explicación estadística sean moderados.

4.2. Interpretación de los coeficientes (en términos de Odds Ratio)

# Odds ratios (exp(coeficientes))
exp(coef(modelo_logit))
                 (Intercept)                Horas_ExtraSi 
                   0.4470267                    4.2692798 
 `Viaje de Negocios`No_Viaja `Viaje de Negocios`Raramente 
                   0.2717371                    0.5244106 
      Estado_CivilDivorciado          Estado_CivilSoltero 
                   0.7632431                    2.2693538 
             Ingreso_Mensual                   Antigüedad 
                   0.6703263                    0.7826154 
                        Edad 
                   0.9720611 

Los resultados muestran los odds ratios (OR) del modelo de regresión logística, los cuales permiten interpretar el efecto de cada variable sobre la probabilidad de rotación: los empleados que trabajan horas extra tienen más de cuatro veces mayor probabilidad de rotar (OR = 4.27), mientras que ser soltero también incrementa el riesgo en más del doble (OR = 2.27). En contraste, no viajar (OR = 0.27) o viajar raramente por negocios (OR = 0.52), así como tener mayores ingresos (OR = 0.67), más antigüedad (OR = 0.78) y mayor edad (OR = 0.97), reducen la probabilidad de rotación. Por su parte, estar divorciado no muestra un efecto tan marcado (OR = 0.76), sugiriendo que esta condición no constituye un factor determinante en el modelo.


4.3. Intervalos de confianza (95%)

# Intervalos de confianza de los OR
exp(confint(modelo_logit))
                                 2.5 %    97.5 %
(Intercept)                  0.2036573 0.9854424
Horas_ExtraSi                3.1411473 5.8239478
`Viaje de Negocios`No_Viaja  0.1310969 0.5236812
`Viaje de Negocios`Raramente 0.3691072 0.7492207
Estado_CivilDivorciado       0.4823237 1.1844924
Estado_CivilSoltero          1.6270178 3.1779671
Ingreso_Mensual              0.5001349 0.8832532
Antigüedad                   0.6266317 0.9688696
Edad                         0.9527131 0.9911501

Los empleados que trabajan horas extra tienen entre 3,14 y 5,82 veces más probabilidad de rotar que quienes no lo hacen, confirmando un efecto fuertemente positivo. Ser soltero también incrementa significativamente el riesgo, entre 1,63 y 3,18 veces más respecto a casados. En contraste, no viajar por negocios (OR entre 0,13 y 0,52), viajar raramente (OR entre 0,37 y 0,75), tener mayores ingresos (OR entre 0,50 y 0,88), más antigüedad (OR entre 0,63 y 0,97) y mayor edad (OR entre 0,95 y 0,99) reducen la probabilidad de rotación, al ubicarse sus intervalos de confianza por debajo de 1. Finalmente, el estado civil divorciado (OR entre 0,48 y 1,18) incluye el valor 1 en su intervalo, por lo que no se puede afirmar que tenga un efecto significativo en la rotación.


4.4. Supuestos para el modelo logístico

# -------------------------------
# 1. Librerías necesarias
# -------------------------------
library(gridExtra)       # Para combinar gráficos
library(car)             # Para VIF
library(ResourceSelection) # Test Hosmer-Lemeshow

# -------------------------------
# 2. Multicolinealidad: VIF
# -------------------------------
vif_valores <- vif(modelo_logit)
print(vif_valores)
                        GVIF Df GVIF^(1/(2*Df))
Horas_Extra         1.029203  1        1.014496
`Viaje de Negocios` 1.013192  2        1.003282
Estado_Civil        1.030179  2        1.007461
Ingreso_Mensual     1.405535  1        1.185553
Antigüedad          1.221176  1        1.105068
Edad                1.256140  1        1.120776

El análisis de multicolinealidad mediante VIF muestra que todos los predictores del modelo tienen valores muy bajos, con GVIF^(1/(2*Df)) entre 1.003 y 1.186, lo que indica que no existe colinealidad preocupante entre las variables; por lo tanto, los coeficientes del modelo pueden interpretarse con confianza, ya que cada variable aporta información independiente sobre la rotación del personal.

# -------------------------------
# 3. Linealidad del logit para variables continuas
# -------------------------------
# Calcular logit predicho
data_2$logit <- log(predict(modelo_logit, type = "response") / 
                    (1 - predict(modelo_logit, type = "response")))

# Variables continuas
continuas <- c("Ingreso_Mensual", "Antigüedad", "Edad")

# Crear lista de gráficos
plots <- lapply(continuas, function(var) {
  ggplot(data_2, aes_string(x = var, y = "logit")) +
    geom_point(color = "steelblue") +
    geom_smooth(method = "loess", color = "darkred") +
    ggtitle(paste("Logit vs", var)) +
    theme_minimal()
})

# Combinar todos los gráficos en un solo plano
do.call(grid.arrange, c(plots, ncol = length(continuas)))


Estas gráficas muestran la relación entre el logit de la rotación y las variables continuas del modelo. En los tres casos, la curva suavizada indica una tendencia aproximadamente lineal descendente, lo que sugiere que el supuesto de linealidad del logit para Ingreso_Mensual, Antigüedad y Edad se cumple razonablemente bien.

# -------------------------------
# 4. Diagnóstico de influencia
# -------------------------------

# Crear el objeto influencia
influencia <- influence.measures(modelo_logit)

# Extraer la tabla de medidas de influencia
inf_df <- as.data.frame(influencia$infmat)

# Seleccionar solo las columnas de interés
tabla_influencia <- inf_df[, c("hat", "dffit", "cook.d")]

# Mostrar la tabla
head(tabla_influencia)
          hat       dffit       cook.d
1 0.006845874  0.13284055 1.222691e-03
2 0.004643789 -0.03277026 4.662070e-05
3 0.009574153  0.12576031 9.030361e-04
4 0.011631673 -0.13469838 1.015707e-03
5 0.003179506 -0.03314758 4.874585e-05
6 0.007828556 -0.08739995 3.819590e-04

Los valores de hat, dffit y cook.d muestran que ninguna observación tiene apalancamiento o influencia significativa sobre el modelo, por lo que los puntos de datos no distorsionan ni afectan de manera relevante los coeficientes ni las predicciones.

# -------------------------------
# 5. Bondad de ajuste: Hosmer-Lemeshow
# -------------------------------
hoslem <- hoslem.test(modelo_logit$y, fitted(modelo_logit))
print(hoslem)

    Hosmer and Lemeshow goodness of fit (GOF) test

data:  modelo_logit$y, fitted(modelo_logit)
X-squared = 25.083, df = 8, p-value = 0.001505

El test de Hosmer-Lemeshow evalúa si el modelo logístico se ajusta bien a los datos; un p-valor bajo indica un mal ajuste. En este caso, con X^2=25.083, df=8 y p=0.0015, el p-valor es menor que 0.05, lo que sugiere que el modelo logístico no se ajusta adecuadamente a los datos.


5.🎯 Evaluación

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


5.1. Curva ROC

# Instalar y cargar librerías si no las tienes
# install.packages("pROC")
library(pROC)

# Obtener probabilidades predichas
probabilidades <- predict(modelo_logit, type = "response")

# Calcular curva ROC
roc_obj <- roc(data_2$Rotación, probabilidades)

# Graficar la curva ROC
plot(roc_obj, col = "blue", lwd = 2, main = "Curva ROC - Modelo Logístico")

5.2. AUC

# Calcular el AUC
auc_valor <- auc(roc_obj)
auc_valor
Area under the curve: 0.7704

La curva ROC muestra un buen desempeño del modelo, ya que se aleja claramente de la diagonal aleatoria, lo que indica capacidad para discriminar entre empleados que rotan y los que no. El área bajo la curva (AUC = 0.7704) refleja un poder predictivo aceptable-bueno, lo que significa que en el 77% de los casos el modelo clasifica correctamente al comparar un empleado que rota frente a uno que no.


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

# --- 1. Renombrar columnas para evitar problemas con espacios ---
colnames(data_2) <- gsub(" ", "_", colnames(data_2))

# Verificar
colnames(data_2)
[1] "Rotación"          "Horas_Extra"       "Viaje_de_Negocios"
[4] "Estado_Civil"      "Ingreso_Mensual"   "Antigüedad"       
[7] "Edad"              "logit"            
# --- 2. Ajustar el modelo de regresión logística con los nombres corregidos ---
modelo_logit <- glm(Rotación ~ Horas_Extra + Viaje_de_Negocios + 
                      Estado_Civil + Ingreso_Mensual + Antigüedad + Edad, 
                    data = data_2, family = binomial)

# --- 3. Crear un individuo hipotético ---
nuevo_individuo <- data.frame(
  Horas_Extra = factor("Si", levels = c("No", "Si")),
  Viaje_de_Negocios = factor("Raramente", levels = c("Frecuentemente", "No_Viaja", "Raramente")),
  Estado_Civil = factor("Soltero", levels = c("Casado", "Divorciado", "Soltero")),
  Ingreso_Mensual = 3000,
  Antigüedad = 5,
  Edad = 30
)

# --- 4. Predecir probabilidad de rotación ---
prob_rotacion <- predict(modelo_logit, newdata = nuevo_individuo, type = "response")

# --- 5. Definir un corte (ejemplo: 0.5) ---
corte <- 0.5

# --- 6. Redondear y mostrar en porcentaje ---
prob_porcentaje <- round(prob_rotacion * 100, 4)

# --- 7. Decisión ---
decision <- ifelse(prob_rotacion >= corte, 
                   "Intervenir (alta probabilidad de rotación)", 
                   "No intervenir (baja probabilidad de rotación)")

# --- 8. Imprimir resultados bonitos ---
cat("Probabilidad de rotación estimada:", prob_porcentaje, "%\n")
Probabilidad de rotación estimada: 0 %
cat("Decisión:", decision, "\n")
Decisión: No intervenir (baja probabilidad de rotación) 

El resultado indica que la probabilidad de rotación es prácticamente nula (0 %), por lo que el caso se considera estable y no requiere intervención ni acciones preventivas de retención.


7. ✅ Conclusiones

En las conclusiones adicione una discusió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).

La rotación en la empresa se asocia principalmente con factores laborales y personales: trabajar horas extra, viajar con frecuencia y ser soltero aumentan significativamente la probabilidad de abandono, mientras que menores ingresos, poca antigüedad y menor edad también caracterizan a quienes rotan. Por tanto, una estrategia efectiva para disminuir la rotación debería enfocarse en mejorar la distribución de las horas extra mediante mayor contratación o rotación de turnos, diseñar planes de viajes más equitativos o con incentivos adicionales, implementar programas de bienestar y conciliación para empleados jóvenes y solteros, así como ofrecer esquemas de crecimiento profesional y mejoras salariales que fortalezcan la permanencia del personal en la organización.

Por otro lado, el modelo logístico actual no presenta un buen ajuste a los datos, según el test de Hosmer-Lemeshow, por lo que sus predicciones podrían no ser totalmente confiables. Se recomienda revisar la selección de variables incluidas en el modelo, considerar transformaciones de las variables continuas, evaluar la inclusión de interacciones relevantes y, si es necesario, explorar modelos alternativos que permitan un ajuste más adecuado.