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.
Los tipos de variable de acuerdo con su naturaleza son los siguientes:
tabla_variables <- data.frame(
Variable = c("Rotación",
"Edad",
"Viaje_de_Negocios",
"Departamento",
"Distancia_Casa","Educación","Campo_Educación",
"Satisfacción_Ambiental","Genero","Cargo",
"Satisfación_Laboral","Estado_Civil","Ingreso_Mensual",
"Trabajos_Anteriores","Horas_Extra",
"Porcentaje_aumento_salarial","Rendimiento_Laboral",
"Años_Experiencia","Capacitaciones",
"Equilibrio_Trabajo_Vida","Antigüedad",
"Antigüedad_Cargo","Años_ultima_promoción",
"Años_acargo_con_mismo_jefe"),
Tipo_estadistico = c("Categórica nominal",
"Numérica discreta",
"Categórica ordinal",
"Categórica nominal",
"Numérica continua",
"Ordinal",
"Categórica nominal",
"Ordinal",
"Categórica nominal",
"Categórica nominal",
"Ordinal",
"Categórica nominal",
"Numérica continua",
"Numérica discreta",
"Binaria",
"Numérica continua",
"Ordinal",
"Numérica continua",
"Numérica discreta",
"Ordinal",
"Numérica continua",
"Numérica continua",
"Numérica discreta",
"Numérica continua"),
Codificacion = c(
"0=No rota, 1=Rota",
"Edad en años",
"Raramente / Frecuentemente / No viaja",
"Área funcional (Ventas, IyD, etc.)",
"Kilómetros desde la casa al trabajo",
"1=Primaria, 2=Secundaria, 3=Técnico, 4=Pregrado, 5=Posgrado",
"Área de formación",
"1=Muy insatisfecho, 2=Insatisfecho, 3=Satisfecho, 4=Muy satisfecho",
"F / M",
"Tipo de cargo",
"1=Muy insatisfecho, 2=Insatisfecho, 3=Satisfecho, 4=Muy satisfecho",
"Soltero / Casado / Divorciado",
"Ingreso mensual",
"Número de trabajos previos",
"0=No, 1=Sí",
"Porcentaje de incremento salarial",
"1=Bajo, 2=Medio, 3=Alto, 4=Muy alto",
"Años de experiencia laboral",
"Número de capacitaciones",
"1=Muy bajo, 2=Bajo, 3=Medio, 4=Alto",
"Años en la empresa",
"Años en el cargo",
"Años desde última promoción",
"Años con el mismo jefe"
)
)
kable(tabla_variables,caption = "Descripción de las variables de la base de datos")
| Variable | Tipo_estadistico | Codificacion |
|---|---|---|
| Rotación | Categórica nominal | 0=No rota, 1=Rota |
| Edad | Numérica discreta | Edad en años |
| Viaje_de_Negocios | Categórica ordinal | Raramente / Frecuentemente / No viaja |
| Departamento | Categórica nominal | Área funcional (Ventas, IyD, etc.) |
| Distancia_Casa | Numérica continua | Kilómetros desde la casa al trabajo |
| Educación | Ordinal | 1=Primaria, 2=Secundaria, 3=Técnico, 4=Pregrado, 5=Posgrado |
| Campo_Educación | Categórica nominal | Área de formación |
| Satisfacción_Ambiental | Ordinal | 1=Muy insatisfecho, 2=Insatisfecho, 3=Satisfecho, 4=Muy satisfecho |
| Genero | Categórica nominal | F / M |
| Cargo | Categórica nominal | Tipo de cargo |
| Satisfación_Laboral | Ordinal | 1=Muy insatisfecho, 2=Insatisfecho, 3=Satisfecho, 4=Muy satisfecho |
| Estado_Civil | Categórica nominal | Soltero / Casado / Divorciado |
| Ingreso_Mensual | Numérica continua | Ingreso mensual |
| Trabajos_Anteriores | Numérica discreta | Número de trabajos previos |
| Horas_Extra | Binaria | 0=No, 1=Sí |
| Porcentaje_aumento_salarial | Numérica continua | Porcentaje de incremento salarial |
| Rendimiento_Laboral | Ordinal | 1=Bajo, 2=Medio, 3=Alto, 4=Muy alto |
| Años_Experiencia | Numérica continua | Años de experiencia laboral |
| Capacitaciones | Numérica discreta | Número de capacitaciones |
| Equilibrio_Trabajo_Vida | Ordinal | 1=Muy bajo, 2=Bajo, 3=Medio, 4=Alto |
| Antigüedad | Numérica continua | Años en la empresa |
| Antigüedad_Cargo | Numérica continua | Años en el cargo |
| Años_ultima_promoción | Numérica discreta | Años desde última promoción |
| Años_acargo_con_mismo_jefe | Numérica continua | Años con el mismo jefe |
Se observa que es necesario recodificar varias variables para poder incluirlas en la modelación.
# Preparación de datos
skimr::skim(rotacion)
| Name | rotacion |
| Number of rows | 1470 |
| Number of columns | 24 |
| _______________________ | |
| Column type frequency: | |
| character | 8 |
| numeric | 16 |
| ________________________ | |
| Group variables | None |
Variable type: character
| skim_variable | n_missing | complete_rate | min | max | empty | n_unique | whitespace |
|---|---|---|---|---|---|---|---|
| Rotación | 0 | 1 | 2 | 2 | 0 | 2 | 0 |
| Viaje de Negocios | 0 | 1 | 8 | 14 | 0 | 3 | 0 |
| Departamento | 0 | 1 | 2 | 6 | 0 | 3 | 0 |
| Campo_Educación | 0 | 1 | 4 | 11 | 0 | 6 | 0 |
| Genero | 0 | 1 | 1 | 1 | 0 | 2 | 0 |
| Cargo | 0 | 1 | 7 | 23 | 0 | 9 | 0 |
| Estado_Civil | 0 | 1 | 6 | 10 | 0 | 3 | 0 |
| Horas_Extra | 0 | 1 | 2 | 2 | 0 | 2 | 0 |
Variable type: numeric
| skim_variable | n_missing | complete_rate | mean | sd | p0 | p25 | p50 | p75 | p100 | hist |
|---|---|---|---|---|---|---|---|---|---|---|
| Edad | 0 | 1 | 36.92 | 9.14 | 18 | 30 | 36 | 43 | 60 | ▂▇▇▃▂ |
| Distancia_Casa | 0 | 1 | 9.19 | 8.11 | 1 | 2 | 7 | 14 | 29 | ▇▅▂▂▂ |
| Educación | 0 | 1 | 2.91 | 1.02 | 1 | 2 | 3 | 4 | 5 | ▂▃▇▆▁ |
| Satisfacción_Ambiental | 0 | 1 | 2.72 | 1.09 | 1 | 2 | 3 | 4 | 4 | ▅▅▁▇▇ |
| Satisfación_Laboral | 0 | 1 | 2.73 | 1.10 | 1 | 2 | 3 | 4 | 4 | ▅▅▁▇▇ |
| Ingreso_Mensual | 0 | 1 | 6502.93 | 4707.96 | 1009 | 2911 | 4919 | 8379 | 19999 | ▇▅▂▁▂ |
| Trabajos_Anteriores | 0 | 1 | 2.69 | 2.50 | 0 | 1 | 2 | 4 | 9 | ▇▃▂▂▁ |
| Porcentaje_aumento_salarial | 0 | 1 | 15.21 | 3.66 | 11 | 12 | 14 | 18 | 25 | ▇▅▃▂▁ |
| Rendimiento_Laboral | 0 | 1 | 3.15 | 0.36 | 3 | 3 | 3 | 3 | 4 | ▇▁▁▁▂ |
| Años_Experiencia | 0 | 1 | 11.28 | 7.78 | 0 | 6 | 10 | 15 | 40 | ▇▇▂▁▁ |
| Capacitaciones | 0 | 1 | 2.80 | 1.29 | 0 | 2 | 3 | 3 | 6 | ▂▇▇▂▃ |
| Equilibrio_Trabajo_Vida | 0 | 1 | 2.76 | 0.71 | 1 | 2 | 3 | 3 | 4 | ▁▃▁▇▂ |
| Antigüedad | 0 | 1 | 7.01 | 6.13 | 0 | 3 | 5 | 9 | 40 | ▇▂▁▁▁ |
| Antigüedad_Cargo | 0 | 1 | 4.23 | 3.62 | 0 | 2 | 3 | 7 | 18 | ▇▃▂▁▁ |
| Años_ultima_promoción | 0 | 1 | 2.19 | 3.22 | 0 | 0 | 1 | 3 | 15 | ▇▁▁▁▁ |
| Años_acargo_con_mismo_jefe | 0 | 1 | 4.12 | 3.57 | 0 | 2 | 3 | 7 | 17 | ▇▂▅▁▁ |
La base de datos contiene 1470 observaciones y 24 variables, incluyendo variables categóricas (como Rotación, Departamento, Horas_Extra) y variables cuantitativas (como Edad, Ingreso_Mensual y Antigüedad).
# RECODIFICACIÓN COMPLETA DE VARIABLES
# Copia de seguridad (opcional pero recomendado)
rotacion2 <- rotacion
# 1. Variable respuesta
rotacion2 <- rotacion2 %>%
mutate(
Rotación = factor(Rotación,
levels = c("No","Si"),
labels = c("No rota","Rota")),
y = ifelse(Rotación == "Rota", 1, 0)
)
# 2. Variables categóricas nominales
rotacion2 <- rotacion2 %>%
mutate(
`Viaje de Negocios` = factor(`Viaje de Negocios`),
Departamento = factor(Departamento),
Campo_Educación = factor(Campo_Educación),
Genero = factor(Genero),
Cargo = factor(Cargo),
Estado_Civil = factor(Estado_Civil),
Horas_Extra = factor(Horas_Extra, levels = c("No","Si"))
)
# 3. Variables ordinales
rotacion2 <- rotacion2 %>%
mutate(
Educación = factor(Educación,
levels = c(1,2,3,4,5),
labels = c("Primaria","Secundaria","Tecnico","Pregrado","Posgrado"),
ordered = TRUE),
Satisfacción_Ambiental = factor(Satisfacción_Ambiental,
levels = c(1,2,3,4),
labels = c("Muy insatisfecho","Insatisfecho","Satisfecho","Muy satisfecho"),
ordered = TRUE),
Satisfación_Laboral = factor(Satisfación_Laboral,
levels = c(1,2,3,4),
labels = c("Muy insatisfecho","Insatisfecho","Satisfecho","Muy satisfecho"),
ordered = TRUE),
Rendimiento_Laboral = factor(Rendimiento_Laboral,
levels = c(1,2,3,4),
labels = c("Bajo","Medio","Alto","Muy alto"),
ordered = TRUE),
Equilibrio_Trabajo_Vida = factor(Equilibrio_Trabajo_Vida,
levels = c(1,2,3,4),
labels = c("Muy bajo","Bajo","Medio","Alto"),
ordered = TRUE)
)
# 4. Variables numéricas
rotacion2 <- rotacion2 %>%
mutate(
Edad = as.numeric(Edad),
Distancia_Casa = as.numeric(Distancia_Casa),
Ingreso_Mensual = as.numeric(Ingreso_Mensual),
Trabajos_Anteriores = as.numeric(Trabajos_Anteriores),
Porcentaje_aumento_salarial = as.numeric(Porcentaje_aumento_salarial),
Años_Experiencia = as.numeric(Años_Experiencia),
Capacitaciones = as.numeric(Capacitaciones),
Antigüedad = as.numeric(Antigüedad),
Antigüedad_Cargo = as.numeric(Antigüedad_Cargo),
Años_ultima_promoción = as.numeric(Años_ultima_promoción),
Años_acargo_con_mismo_jefe = as.numeric(Años_acargo_con_mismo_jefe)
)
La correcta clasificación de las variables permite diferenciar entre variables nominales, ordinales y numéricas, lo cual es fundamental para la especificación del modelo de datos. En particular, las variables ordinales fueron tratadas respetando su estructura jerárquica, evitando su incorrecta transformación en variables nominales, lo que podría generar pérdida de información relevante. Una vez codificadas correctamente se procede a realizar el procesamiento de datos.
# Variables numéricas
vars_num <- c("Edad","Distancia_Casa","Ingreso_Mensual",
"Trabajos_Anteriores","Porcentaje_aumento_salarial",
"Años_Experiencia","Capacitaciones",
"Antigüedad","Antigüedad_Cargo",
"Años_ultima_promoción","Años_acargo_con_mismo_jefe")
# Función para crear histogramas pequeños
crear_hist <- function(var){
ggplot(rotacion2, aes_string(x = var)) +
geom_histogram(bins = 25,
fill = "#66c2a5", # color bonito
color = "white",
alpha = 0.9) +
theme_minimal(base_size = 9) +
labs(title = var,
x = "",
y = "") +
theme(
plot.title = element_text(size = 9, face = "bold"),
axis.text = element_text(size = 7)
)
}
# Crear lista de gráficos
plots <- lapply(vars_num, crear_hist)
# Organizar en grilla (3 columnas)
wrap_plots(plots, ncol = 3)
vars_muy_alta<- c("Ingreso_Mensual")
vars_alta <- c("Antigüedad","Antigüedad_Cargo",
"Años_Experiencia","Distancia_Casa",
"Años_acargo_con_mismo_jefe","Edad","Porcentaje_aumento_salarial")
vars_baja <- c("Trabajos_Anteriores","Capacitaciones","Años_ultima_promoción")
data_baja <- rotacion2 %>%
select(all_of(vars_baja)) %>%
pivot_longer(cols = everything(),
names_to = "variable",
values_to = "valor")
g_baja <- ggplot(data_baja, aes(x = variable, y = valor, fill = variable)) +
geom_violin(alpha = 0.6) +
geom_boxplot(width = 0.15, color = "black", alpha = 0.8, outlier.color = "red") +
theme_minimal(base_size = 10) +
theme(axis.text.x = element_text(angle = 45, hjust = 1),
legend.position = "none") +
labs(title = "Distribución variables numéricas (escala baja)",
x = "", y = "Valor") +
scale_fill_brewer(palette = "Pastel1")
g_baja
data_alta <- rotacion2 %>%
select(all_of(vars_alta)) %>%
pivot_longer(cols = everything(),
names_to = "variable",
values_to = "valor")
g_alta <- ggplot(data_alta, aes(x = variable, y = valor, fill = variable)) +
geom_violin(alpha = 0.6) +
geom_boxplot(width = 0.15, color = "black", alpha = 0.8, outlier.color = "red") +
theme_minimal(base_size = 10) +
theme(axis.text.x = element_text(angle = 45, hjust = 1),
legend.position = "none") +
labs(title = "Distribución variables numéricas (escala alta)",
x = "", y = "Valor") +
scale_fill_brewer(palette = "Pastel1")
g_alta
Las variables de escala alta presentan distribuciones asimétricas con presencia de valores extremos, como por ejemplo, en ingreso y años desde la última promoción.
Se puede identificar datos atípicos con respecto a los años de experiencia de los colaboradores, esto puede estar relacionado con la edad de estos y con la cantidad de empresas en las que han laborado anteriormente. Por otra parte, hay valores atípicos en los ingresos mensuales producto de la variedad de perfiles laborales dentro de la empresa, personas con altos cargos gerenciales técnicos especializados y profesionales con alto número de capacitaciones y responsabilidades.
La edad presenta una distribución concentrada en adultos jóvenes. Esto puede indicar una fuerza laboral relativamente joven, lo cual podría estar asociado con mayor rotación debido a menor estabilidad laboral.
data_muy_alta <- rotacion2 %>%
select(all_of(vars_muy_alta)) %>%
pivot_longer(cols = everything(),
names_to = "variable",
values_to = "valor")
g_muy_alta <- ggplot(data_muy_alta, aes(x = variable, y = valor, fill = variable)) +
geom_violin(alpha = 0.6) +
geom_boxplot(width = 0.15, color = "black", alpha = 0.8, outlier.color = "red") +
theme_minimal(base_size = 10) +
theme(axis.text.x = element_text(angle = 45, hjust = 1),
legend.position = "none") +
labs(title = "Distribución variables numéricas (escala muy alta)",
x = "", y = "Valor") +
scale_fill_brewer(palette = "Pastel1")
g_muy_alta
El ingreso mensual presenta dispersión, lo que sugiere desigualdad salarial dentro de la empresa. Niveles bajos de ingreso podrían estar asociados con mayor oportunidad de rotación. Adicionalmente, se observan datos atípicos superiores, pero se consideran esperados, ya que pueden corresponder a cargos directivos, por lo cual son pocos quienes tienen muy altos ingresos.
Los gráficos de barras permiten observar la distribución de las variables categóricas. Se identifican concentraciones importantes en ciertas categorías, particularmente en variables como departamento y cargo, lo que sugiere una estructura organizacional no homogénea. Asimismo, variables como horas extra y estado civil muestran distribuciones que podrían estar asociadas con la rotación laboral.
Se observa que la mayoría de empleadostrabaja horas extra (71.7%). Una alta frecuencia de esta condición puede indicar sobrecarga laboral, lo cual podría estar relacionado con mayores niveles de rotación.
Se identifica la distribución del estado civil de los empleados. Si predominan los empleados solteros, esto podría influir en una mayor movilidad laboral y, por tanto, en la rotación.
Se observan diferencias importantes entre departamentos, lo que puede sugerir condiciones laborales distintas que influyen en la rotación.
La edad presenta una distribución concentrada en adultos jóvenes. Esto puede indicar una fuerza laboral relativamente joven, lo cual podría estar asociado con mayor rotación debido a menor estabilidad laboral.
De los diagramas anteriores se puede identificar que:
La satisfacción laboral del personal se compone de cuatro niveles, siendo 1 mala satisfacción laboral y 4 una excelente satisfacción laboral. Para este estudio, se puede afirmar que el 64% de los empleados presenta una satisfacción aceptable.
El departamento que más personal agrupa es IyD con un 65% seguido del departamento de ventas con un 31% y finalmente RH con un 4%.
Solo el 29% del personal realiza horas extras.
Solo el 16% del personal presenta rotación.
Es importante comprender que la rotación de personal no se puede asociar en concreto a una variable que algún colaborador presente o no.
Posteriormente, se realiza un estudio con indicadores de tendencia central, posición y varianza para comprender los datos, esta información se ilustra en la sección de anexos.
ggplot(rotacion2, aes(x = Rotación, fill = Rotación)) +
geom_bar(alpha = 0.9) +
# Etiquetas: conteo + porcentaje
geom_text(
stat = "count",
aes(label = paste0(after_stat(count), "\n(",
round(after_stat(count) / sum(after_stat(count)) * 100, 1),
"%)")),
vjust = 0.5,
color = "black",
size = 4
) +
theme_minimal() +
labs(title = "Distribución de la Rotación",
x = "", y = "Frecuencia") +
scale_fill_manual(values = c("#66c2a5", "#fc8d62"))
La variable de rotación presenta desbalance, lo cual es relevante para la estimación del modelo logístico y su capacidad predictiva.
Se observa que la mayoría de los empleados no rotan (83.9%), mientras que una menor proporción sí lo hace (16.1%). Sin embargo, este grupo representa un porcentaje relevante que justifica el análisis, ya que la rotación implica costos importantes para la empresa.
Con el objetivo de identificar la relación entre la variable respuesta Rotación y las variables explicativas seleccionadas, se realizaron pruebas estadísticas bivariadas adecuadas según el tipo de variable.
vars_cat <- c("Viaje de Negocios","Departamento","Campo_Educación",
"Genero","Cargo","Estado_Civil","Horas_Extra")
crear_bivar_cat <- function(var){
# Calcular proporciones correctamente
data_plot <- rotacion2 %>%
group_by(.data[[var]], Rotación) %>%
summarise(n = n(), .groups = "drop") %>%
group_by(.data[[var]]) %>%
mutate(prop = n / sum(n))
ggplot(data_plot,
aes(x = .data[[var]], y = prop, fill = Rotación)) +
geom_bar(stat = "identity", position = "fill", alpha = 0.9) +
# Etiquetas correctas
geom_text(aes(label = paste0(round(prop*100,1), "%")),
position = position_fill(vjust = 0.5),
size = 3) +
theme_minimal(base_size = 9) +
labs(title = var,
x = "",
y = "Proporción") +
scale_fill_manual(values = c("#66c2a5", "#fc8d62")) +
theme(
axis.text.x = element_text(angle = 45, hjust = 1, size = 7),
plot.title = element_text(size = 9, face = "bold")
)
}
plots_cat_biv <- lapply(vars_cat, crear_bivar_cat)
wrap_plots(plots_cat_biv, ncol = 2)
La matriz de correlación permite identificar relaciones lineales entre variables numéricas. Valores cercanos a 1 o -1 indican relaciones fuertes entre variables.
Considerando lo mencionado anteriormente, es pertinente realizar un pequeño test mediante coeficientes de correlación, chi cuadrado para variables categóricas o dicotómicas (Satisfacción laboral, Horas extra y Departamento) y el test de Mann-Whitney como alternativa a la prueba t-student dado que no se sigue una distribución normal.
library(lsr)
library(kableExtra)
datos_filtrado<-rotacion2
test_mw <- wilcox.test(Ingreso_Mensual ~ Rotación, data = datos_filtrado)
p_val_mw <- test_mw$p.value
test_anos <- wilcox.test(Años_Experiencia ~ Rotación, data = datos_filtrado)
p_val_anos <- test_anos$p.value
test_casa <- wilcox.test(Distancia_Casa ~ Rotación, data = datos_filtrado)
p_val_casa <- test_anos$p.value
test_chihoras <- chisq.test(table(datos_filtrado$Horas_Extra, datos_filtrado$Rotación))
p_valHoras_extra <- test_chihoras$p.value
test_chidep <- chisq.test(table(datos_filtrado$Departamento, datos_filtrado$Rotación))
p_valDepartamento <- test_chidep$p.value
test_chisat <- chisq.test(table(datos_filtrado$Satisfación_Laboral, datos_filtrado$Rotación))
p_valSatisfaccion <- test_chisat$p.value
# 1. Cálculos de Coeficientes (Fuerza)
# Para Mann-Whitney/Spearman usamos cor(method="spearman")
r_ingreso <- abs(cor(datos_filtrado$Ingreso_Mensual, as.numeric(as.factor(datos_filtrado$Rotación)), method="spearman"))
r_años <- abs(cor(datos_filtrado$Años_Experiencia, as.numeric(as.factor(datos_filtrado$Rotación)), method="spearman"))
r_casa <- abs(cor(datos_filtrado$Distancia_Casa, as.numeric(as.factor(datos_filtrado$Rotación)), method="spearman"))
# Para Categorías usamos V de Cramer
v_satisfaccion <- lsr::cramersV(table(datos_filtrado$Satisfación_Laboral, datos_filtrado$Rotación))
v_depto <- lsr::cramersV(table(datos_filtrado$Departamento, datos_filtrado$Rotación))
v_horas <- lsr::cramersV(table(datos_filtrado$Horas_Extra, datos_filtrado$Rotación))
# 2. Crear el Dataframe con AMBOS datos
Tabla_enunciado <- data.frame(
Metrica = c("Fuerza (r / V)", "P-Valor"),
Ingreso = c(round(r_ingreso, 3), round(p_val_mw, 4)),
Años = c(round(r_años, 3), round(p_val_anos, 4)),
Distancia = c(round(r_casa, 3), round(p_val_casa, 4)),
Satisfaccion = c(round(v_satisfaccion, 3), round(p_valSatisfaccion, 4)),
Depto = c(round(v_depto, 3), round(p_valDepartamento, 4)),
Horas_Extra = c(round(v_horas, 3), round(p_valHoras_extra, 4))
)
# 3. Formatear la tabla
colnames(Tabla_enunciado) <- c("Indicador", "Ingreso Mensual", "Años Experiencia", "Distancia Casa", "Satisfacción", "Departamento", "Horas Extra")
knitr::kable(Tabla_enunciado,
booktabs = TRUE,
caption = "<center><b>Análisis de Correlaciones</b></center>",
escape = FALSE) %>%
kable_styling(full_width = TRUE, bootstrap_options = c("striped", "hover"))
| Indicador | Ingreso Mensual | Años Experiencia | Distancia Casa | Satisfacción | Departamento | Horas Extra |
|---|---|---|---|---|---|---|
| Fuerza (r / V) | 0.198 | 0.199 | 0.079 | 0.1090 | 0.0860 | 0.244 |
| P-Valor | 0.000 | 0.000 | 0.000 | 0.0006 | 0.0045 | 0.000 |
De la anterior tabla podemos confirmar la relación entre las variables y rotación, empezando por horas extra (0.244), seguido de años de experiencia (0.199), ingreso mensual (0.198), satisfacción (0.109), departamento (0.086) y Distancia de la casa (0.079).
El análisis bivariado evidencia que variables como horas extra, ingreso mensual y antigüedad presentan diferencias significativas entre los grupos de empleados que rotan y los que no. En particular, la rotación se asocia con mayores cargas laborales y menores niveles de ingreso, lo cual es consistente con las hipótesis planteadas inicialmente. Asimismo, se observan diferencias entre departamentos, lo que sugiere heterogeneidad en las condiciones laborales dentro de la organización.
Se observan correlaciones positivas entre variables como antigüedad y experiencia, lo cual es consistente con la dinámica laboral. Asimismo, se identifican diferencias en la distribución de los datos según la rotación, particularmente en variables como ingreso y satisfacción laboral.
La selección de variables se realizó considerando tanto el análisis exploratorio previo como la relevancia teórica de cada variable en la explicación de la rotación laboral. Se optó por incluir un conjunto parsimonioso de predictores que combinan características laborales, organizacionales y de experiencia del empleado.
Hipótesis: Se espera que la realización de horas extra incremente la oportunidad de rotación, debido al desgaste laboral, menor equilibrio trabajo-vida y posibles efectos negativos sobre el bienestar del empleado.
Hipótesis: Se espera que la oportunidad de rotación varíe según el tipo de cargo, particularmente en aquellos roles con mayores niveles de presión comercial, carga operativa o menor estabilidad, como ventas o cargos técnicos, los cuales podrían presentar mayores niveles de rotación en comparación con cargos administrativos o directivos.
Hipótesis: Se espera que un menor nivel de equilibrio entre el trabajo y la vida personal incremente la oportunidad de rotación, ya que afecta negativamente la satisfacción y el bienestar del empleado.
Hipótesis: Se espera que un mayor nivel de distancia entre el hogar y el lugar de trabajo incremente la oportunidad de rotación, debido a mayores costos de desplazamiento y menor bienestar asociado al tiempo de traslado.
Hipótesis: Los empleados con mayor número de trabajos anteriores presentan mayor probabilidad de rotación, ya que pueden reflejar menor estabilidad laboral.
Hipótesis: Se espera que una mayor antigüedad en la empresa reduzca la oportunidad de rotación, dado que los empleados con mayor tiempo tienden a desarrollar mayor estabilidad, compromiso organizacional y costos de salida más altos.
# Definir receta (preprocesamiento)
rotacion<-rotacion2 %>%
select(-y)
# receta con todas las variables
#receta <- recipe(Rotación ~ ., data = rotacion) %>%
# receta con 3 variables categóricas y 3 numéricas
# mis variables elegidas son:
receta <- recipe(
Rotación ~ Horas_Extra + Cargo + Equilibrio_Trabajo_Vida +
Distancia_Casa + Trabajos_Anteriores + Antigüedad,
data = rotacion
) %>%
# Convertir variables categóricas a dummies
step_dummy(all_nominal_predictors()) %>%
# Normalizar variables numéricas
step_normalize(all_numeric_predictors()) %>%
# Eliminar variables con varianza cero (si existen)
step_zv(all_predictors())
# Definir modelo logístico
modelo_log <- logistic_reg() %>%
set_engine("glm")
# Crear workflow
workflow_modelo <- workflow() %>%
add_recipe(receta) %>%
add_model(modelo_log)
# Ajustar modelo
ajuste <- fit(workflow_modelo, data = rotacion)
# Resultados del modelo
# Coeficientes del modelo
resultados <- tidy(ajuste)
print(resultados)
## # A tibble: 16 × 5
## term estimate std.error statistic p.value
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 (Intercept) -2.07 0.0999 -20.8 9.14e-96
## 2 Distancia_Casa 0.249 0.0757 3.28 1.02e- 3
## 3 Trabajos_Anteriores 0.219 0.0767 2.86 4.30e- 3
## 4 Antigüedad -0.223 0.108 -2.07 3.82e- 2
## 5 Horas_Extra_Si 0.682 0.0722 9.43 3.92e-21
## 6 Cargo_Director_Manofactura 0.301 0.238 1.26 2.07e- 1
## 7 Cargo_Ejecutivo_Ventas 0.890 0.308 2.89 3.86e- 3
## 8 Cargo_Gerente 0.207 0.220 0.941 3.47e- 1
## 9 Cargo_Investigador_Cientifico 0.754 0.298 2.53 1.14e- 2
## 10 Cargo_Recursos_Humanos 0.465 0.149 3.12 1.82e- 3
## 11 Cargo_Representante_Salud 0.271 0.231 1.18 2.39e- 1
## 12 Cargo_Representante_Ventas 0.801 0.179 4.48 7.45e- 6
## 13 Cargo_Tecnico_Laboratorio 0.979 0.284 3.45 5.67e- 4
## 14 Equilibrio_Trabajo_Vida_1 -0.187 0.0779 -2.40 1.63e- 2
## 15 Equilibrio_Trabajo_Vida_2 0.254 0.0750 3.39 7.01e- 4
## 16 Equilibrio_Trabajo_Vida_3 -0.00770 0.0862 -0.0893 9.29e- 1
# Importancia de variables (VIP)
# Extraer modelo y graficar importancia
vip(extract_fit_parsnip(ajuste)) +
geom_col(fill = "salmon")
La importancia de variables fue evaluada a partir de la magnitud de los coeficientes estimados en el modelo logístico. Se observa que variables como las horas extra y el cargo presentan mayor influencia en la probabilidad de rotación. No obstante, esta medida debe interpretarse con cautela, ya que no considera el tipo de variable, ni la significancia estadística.
# Construir tabla de OR
tabla_or <- resultados %>%
mutate(
Odds_Ratio = exp(estimate),
OR_inf = exp(estimate - 1.96 * std.error),
OR_sup = exp(estimate + 1.96 * std.error),
p_value = round(p.value, 4)
) %>%
select(term, Odds_Ratio, OR_inf, OR_sup, p_value) %>%
mutate(across(c(Odds_Ratio, OR_inf, OR_sup), round, 2)) %>%
filter(term != "(Intercept)")
# Mostrar tabla bonita
tabla_or %>%
kable(caption = "Odds Ratios del modelo logístico",
col.names = c("Variable", "Odds Ratio", "IC 2.5%", "IC 97.5%", "p-valor")) %>%
kable_styling(full_width = FALSE, position = "center")
| Variable | Odds Ratio | IC 2.5% | IC 97.5% | p-valor |
|---|---|---|---|---|
| Distancia_Casa | 1.28 | 1.11 | 1.49 | 0.0010 |
| Trabajos_Anteriores | 1.24 | 1.07 | 1.45 | 0.0043 |
| Antigüedad | 0.80 | 0.65 | 0.99 | 0.0382 |
| Horas_Extra_Si | 1.98 | 1.72 | 2.28 | 0.0000 |
| Cargo_Director_Manofactura | 1.35 | 0.85 | 2.16 | 0.2071 |
| Cargo_Ejecutivo_Ventas | 2.44 | 1.33 | 4.46 | 0.0039 |
| Cargo_Gerente | 1.23 | 0.80 | 1.89 | 0.3468 |
| Cargo_Investigador_Cientifico | 2.13 | 1.19 | 3.81 | 0.0114 |
| Cargo_Recursos_Humanos | 1.59 | 1.19 | 2.13 | 0.0018 |
| Cargo_Representante_Salud | 1.31 | 0.83 | 2.06 | 0.2393 |
| Cargo_Representante_Ventas | 2.23 | 1.57 | 3.16 | 0.0000 |
| Cargo_Tecnico_Laboratorio | 2.66 | 1.53 | 4.65 | 0.0006 |
| Equilibrio_Trabajo_Vida_1 | 0.83 | 0.71 | 0.97 | 0.0163 |
| Equilibrio_Trabajo_Vida_2 | 1.29 | 1.11 | 1.49 | 0.0007 |
| Equilibrio_Trabajo_Vida_3 | 0.99 | 0.84 | 1.17 | 0.9288 |
El forest plot muestra los odds ratios estimados y sus intervalos de confianza. Los valores superiores a 1 indican un aumento en la probabilidad de rotación, mientras que valores inferiores a 1 indican un efecto protector. Las variables cuyos intervalos de confianza no cruzan el valor 1 son estadísticamente significativas.
library(ggplot2)
# Preparar datos
forest_data <- resultados %>%
mutate(
Odds_Ratio = exp(estimate),
OR_inf = exp(estimate - 1.96 * std.error),
OR_sup = exp(estimate + 1.96 * std.error)
) %>%
filter(term != "(Intercept)")
# Ordenar variables (opcional pero recomendado)
forest_data <- forest_data %>%
arrange(Odds_Ratio) %>%
mutate(term = factor(term, levels = term))
# Forest plot
ggplot(forest_data, aes(x = Odds_Ratio, y = term)) +
geom_errorbarh(aes(xmin = OR_inf, xmax = OR_sup),
height = 0.2, color = "#75c2d5",size=1.1) +
geom_vline(xintercept = 1, linetype = "dashed", color = "gray50",size=1.1) +
geom_vline(xintercept = 2, linetype = "dashed", color = "gray40",size=1.1) +
geom_vline(xintercept = 3, linetype = "dashed", color = "gray30",size=1.1) +
geom_point(color = "#db8e62", size = 3) +
scale_x_log10() +
labs(
title = "Forest Plot - Odds Ratios del modelo",
x = "Odds Ratio (escala logarítmica)",
y = ""
) +
theme_classic(base_size = 11)
La interpretación de los coeficientes del modelo logístico se realiza en términos de odds ratios. Se encuentra que variables como la realización de horas extra duplican aproximadamente las odds de rotación (OR ≈ 1.98). Asimismo, cargos como técnico de laboratorio, ejecutivo de ventas y representante de ventas presentan entre 2.2 y 2.7 veces mayores odds de rotación respecto a la categoría base. Por su parte, la antigüedad en la empresa reduce las odds de rotación en aproximadamente un 20% por cada año adicional (OR ≈ 0.80). En conjunto, estos resultados evidencian que tanto las condiciones laborales como el tipo de cargo desempeñado influyen significativamente en la decisión de rotar.
Para cada una de las variables y características la interpretación sería:
Por cada unidad adicional en distancia al trabajo, la oportunidad de rotar aumenta en 1.28 veces, manteniendo constantes las demás variables.
Por cada unidad adicional en trabajos anteriores, la oportunidad de rotar aumenta en 1.24 veces, manteniendo constantes las demás variables.
Por cada unidad adicional en antigüedad, la oportunidad de rotar se reduce en un 20%, manteniendo constantes las demás variables.
Los empleados que realizan horas extra tienen 1.98 veces la oportunidad de rotar respecto de los que no.
Los empleados con cargo de Ejecutivo de ventas tienen 2.44 veces la oportunidad de rotar respecto a los representantes de ventas.
Los empleados con cargo de Investigador científico tienen 2.13 veces la oportunidad de rotar respecto a los representantes de ventas.
Los empleados con cargo de Recursos humanos tienen 1.59 veces la oportunidad de rotar respecto a los representantes de ventas.
Los empleados con cargo de Representante de ventas tienen 2.23 veces la oportunidad de rotar respecto a los representantes de ventas.
Los empleados con cargo de Técnico de laboratorio tienen 2.66 veces la oportunidad de rotar respecto a los representantes de ventas.
Los empleados con equilibrio trabajo-vida: muy bajo tienen un 17% menos oportunidad de rotar respecto de la categoría alto equilibrio trabajo-vida
Los empleados con equilibrio trabajo-vida: bajo tienen 1.29 veces la oportunidad de rotar respecto de la categoría alto equilibrio trabajo-vida
Algunas características, como ciertos cargos y el nivel medio de equilibrio trabajo-vida, no resultaron estadísticamente significativas, por lo que no existe evidencia suficiente para afirmar que influyan en la rotación. Sus intervalos de confianza incluyen el valor 1, lo que sugiere que los efectos estimados podrían explicarse por variabilidad aleatoria.
Con el fin de evaluar el desempeño del modelo de regresión logística estimado, se analizaron diferentes métricas de clasificación, incluyendo la curva ROC, el área bajo la curva (AUC), la matriz de confusión y la sensibilidad frente a distintos puntos de corte.
La curva ROC (Receiver Operating Characteristic) permite evaluar la capacidad del modelo para discriminar entre empleados que rotan (y = 1) y aquellos que no (y = 0), considerando todos los posibles puntos de corte.
# Generar probabilidades
predicciones <- predict(ajuste, rotacion, type = "prob") %>%
bind_cols(rotacion)
head(predicciones)
## # A tibble: 6 × 26
## `.pred_No rota` .pred_Rota Rotación Edad `Viaje de Negocios` Departamento
## <dbl> <dbl> <fct> <dbl> <fct> <fct>
## 1 0.348 0.652 Rota 41 Raramente Ventas
## 2 0.948 0.0520 No rota 49 Frecuentemente IyD
## 3 0.523 0.477 Rota 37 Raramente IyD
## 4 0.813 0.187 No rota 33 Frecuentemente IyD
## 5 0.805 0.195 No rota 27 Raramente IyD
## 6 0.895 0.105 No rota 32 Frecuentemente IyD
## # ℹ 20 more variables: Distancia_Casa <dbl>, Educación <ord>,
## # Campo_Educación <fct>, Satisfacción_Ambiental <ord>, Genero <fct>,
## # Cargo <fct>, Satisfación_Laboral <ord>, Estado_Civil <fct>,
## # Ingreso_Mensual <dbl>, Trabajos_Anteriores <dbl>, Horas_Extra <fct>,
## # Porcentaje_aumento_salarial <dbl>, Rendimiento_Laboral <ord>,
## # Años_Experiencia <dbl>, Capacitaciones <dbl>,
## # Equilibrio_Trabajo_Vida <ord>, Antigüedad <dbl>, Antigüedad_Cargo <dbl>, …
# Evaluación: ROC y AUC
# Curva ROC
roc_obj <- roc(
response = ifelse(predicciones$Rotación == "Rota", 1, 0),
predictor = predicciones$.pred_Rota
)
# AUC
auc_valor <- auc(roc_obj)
cat("Área bajo la curva (AUC):", round(auc_valor, 3))
## Área bajo la curva (AUC): 0.786
plot(roc_obj, col = "#fc8d62", lwd = 2)
text(0.6, 0.2,
paste("AUC =", round(auc(roc_obj), 3)),
cex = 1.2)
El valor del AUC obtenido indica la capacidad predictiva del modelo:
Un AUC cercano a 0.5 indica que el modelo no tiene capacidad de discriminación.
Un AUC superior a 0.7 indica un modelo con buen poder predictivo.
Un AUC superior a 0.8 representa un modelo muy bueno.
En este caso se obtiene un AUC de 0.786 que indica que el modelo tiene una capacidad discriminativa moderadamente buena. Es decir, el modelo es capaz de separar a los empleados que rotan de los que no rotan con una probabilidad de acierto cercana al 78.6%. Si el AUC fuera cercano a 0.5, significaría que el modelo no discrimina mejor que al azar. Si fuera superior a 0.8, indicaría un muy buen desempeño. Así se considera que tiene un buen ajuste, considerando que solo fueron tenidas en cuenta 6 variables de todas las que se encontraban disponibles en la tabla. Se podría decir que es un modelo parsimonioso.
La matriz de confusión muestra la clasificación real y la clasificación predicha por el modelo. Es útil para calcular métricas como la precisión, la tasa de falsos positivos y la tasa de falsos negativos a partir de la matriz de confusión. La métricas de Sensibilidad y Especificidad miden la capacidad del modelo para clasificar correctamente las observaciones positivas y negativas, respectivamente. Una alta sensibilidad y especificidad indican un buen ajuste.
predicciones_clas <- predict(ajuste, rotacion, type = "class") %>%
bind_cols(rotacion)
library(caret)
conf_matrix <- confusionMatrix(predicciones_clas$.pred_class, rotacion$Rotación)
print(conf_matrix)
## Confusion Matrix and Statistics
##
## Reference
## Prediction No rota Rota
## No rota 1212 201
## Rota 21 36
##
## Accuracy : 0.849
## 95% CI : (0.8296, 0.8669)
## No Information Rate : 0.8388
## P-Value [Acc > NIR] : 0.1517
##
## Kappa : 0.1945
##
## Mcnemar's Test P-Value : <2e-16
##
## Sensitivity : 0.9830
## Specificity : 0.1519
## Pos Pred Value : 0.8577
## Neg Pred Value : 0.6316
## Prevalence : 0.8388
## Detection Rate : 0.8245
## Detection Prevalence : 0.9612
## Balanced Accuracy : 0.5674
##
## 'Positive' Class : No rota
##
### Umbral óptimo
# Obtener mejor umbral (Youden)
coords_roc <- coords(roc_obj, "best", ret = "threshold")
coords_roc
## threshold
## 1 0.1684637
umbral_opt <- coords_roc
predicciones$pred_opt <- ifelse(predicciones$.pred_Rota > umbral_opt,
"Rota", "No rota")
# Convertir a factor
predicciones$pred_opt <- factor(predicciones$pred_opt,
levels = c("No rota","Rota"))
rotacion$Rotación <- factor(rotacion$Rotación,
levels = c("No rota","Rota"))
library(caret)
confusionMatrix(predicciones$pred_opt, rotacion$Rotación)
## Confusion Matrix and Statistics
##
## Reference
## Prediction No rota Rota
## No rota 0 0
## Rota 1233 237
##
## Accuracy : 0.1612
## 95% CI : (0.1428, 0.181)
## No Information Rate : 0.8388
## P-Value [Acc > NIR] : 1
##
## Kappa : 0
##
## Mcnemar's Test P-Value : <2e-16
##
## Sensitivity : 0.0000
## Specificity : 1.0000
## Pos Pred Value : NaN
## Neg Pred Value : 0.1612
## Prevalence : 0.8388
## Detection Rate : 0.0000
## Detection Prevalence : 0.0000
## Balanced Accuracy : 0.5000
##
## 'Positive' Class : No rota
##
summary(predicciones$.pred_Rota)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.004209 0.055356 0.109769 0.161224 0.232343 0.823997
El umbral óptimo basado en el criterio de Youden maximiza simultáneamente la sensibilidad y la especificidad sin considerar el desbalance de la muestra ni los costos asociados a los errores de clasificación. En este caso, dicho criterio conduce a una regla de decisión extrema que clasifica la mayoría de las observaciones como rotación, lo cual resulta poco informativo y operacionalmente inviable.
umbrales <- c(0.2, 0.3, 0.4, 0.5)
evaluar_umbral <- function(u){
pred <- ifelse(predicciones$.pred_Rota > u, "Rota", "No rota")
pred <- factor(pred, levels = c("No rota","Rota"))
cm <- caret::confusionMatrix(pred, rotacion$Rotación)
data.frame(
umbral = u,
accuracy = cm$overall["Accuracy"],
sensitivity = cm$byClass["Sensitivity"],
specificity = cm$byClass["Specificity"]
)
}
do.call(rbind, lapply(umbrales, evaluar_umbral))
## umbral accuracy sensitivity specificity
## Accuracy 0.2 0.7585034 0.7785888 0.6540084
## Accuracy1 0.3 0.8299320 0.9026764 0.4514768
## Accuracy2 0.4 0.8510204 0.9610706 0.2784810
## Accuracy3 0.5 0.8489796 0.9829684 0.1518987
El análisis de diferentes umbrales de clasificación evidencia un trade-off entre la precisión global del modelo y su capacidad para identificar correctamente empleados con alta probabilidad de rotación. Aunque umbrales más altos maximizan la precisión, reducen significativamente la capacidad del modelo para detectar rotación. En este contexto, un umbral de 0.2 resulta más adecuado, ya que permite un mejor equilibrio entre sensibilidad y especificidad, incrementando la capacidad del modelo para identificar empleados en riesgo.
En contextos organizacionales, el costo de no identificar a un empleado con alta probabilidad de rotación (falso negativo) suele ser mayor que el costo de intervenir innecesariamente a un empleado que no rotará (falso positivo). Por esta razón, es preferible utilizar un umbral más bajo que incremente la detección de casos de rotación, incluso a costa de una menor precisión global.
Aunque el criterio estadístico de Youden sugiere un umbral óptimo, este no resulta adecuado en contextos con desbalance de clases y objetivos de predicción orientados a la toma de decisiones. En este estudio, se selecciona un umbral de 0.2 debido a que permite un mejor equilibrio entre la capacidad de detección de rotación y la estabilidad del modelo, generando resultados más útiles desde una perspectiva operativa.
library(scales) # Para escalar
calibration_data <- data.frame(
probabilidad = predicciones$.pred_Rota,
rotacion = ifelse(predicciones$Rotación == "Rota", 1, 0)
)
calibration_plot <- predicciones %>%
mutate(
y = ifelse(Rotación == "Rota", 1, 0),
bin = ntile(.pred_Rota, 10) # 10 grupos
) %>%
group_by(bin) %>%
summarise(
prob_predicha = mean(.pred_Rota),
prob_real = mean(y)
)
ggplot(calibration_plot, aes(x = prob_predicha, y = prob_real)) +
geom_point(size = 3, color = "#fc8d62") +
geom_line(color = "#fc8d62") +
geom_abline(slope = 1, intercept = 0, linetype = "dashed") +
labs(title = "Curva de calibración",
x = "Probabilidad predicha",
y = "Proporción observada") +
theme_minimal()
La curva de calibración permite evaluar la correspondencia entre las probabilidades predichas por el modelo y las frecuencias observadas de rotación. Los resultados sugieren que, aunque el modelo presenta una adecuada capacidad discriminatoria, la calibración de las probabilidades no es perfecta, lo cual indica que las probabilidades estimadas deben interpretarse con cautela en contextos de toma de decisiones.
Dividir los datos en conjuntos de entrenamiento y conjunto de prueba repetidamente (por ejemplo, mediante validación cruzada k-fold) y calcular métricas de bondad de ajuste en cada iteración. Esto proporciona una evaluación más robusta del modelo.
La elección de la métrica de bondad de ajuste depende de tus objetivos específicos y del contexto del problema. En general, es recomendable utilizar múltiples métricas y pruebas para evaluar el rendimiento del modelo desde diferentes perspectivas.
# 4. Validación cruzada (ejemplo rápido con tidymodels)
library(tidymodels)
# Definir validación cruzada (5 pliegues)
set.seed(123)
cv_splits <- vfold_cv(rotacion, v = 5)
# Ajustar modelo con validación cruzada
cv_results <- fit_resamples(
ajuste,
resamples = cv_splits,
metrics = metric_set(roc_auc),
control = control_resamples(save_pred = TRUE)
)
# Resumen de resultados de validación cruzada
collect_metrics(cv_results)
## # A tibble: 1 × 6
## .metric .estimator mean n std_err .config
## <chr> <chr> <dbl> <int> <dbl> <chr>
## 1 roc_auc binary 0.769 5 0.0123 pre0_mod0_post0
library(yardstick)
# Agregar predicciones de CV
cv_pred <- collect_predictions(cv_results)
# Crear variable binaria real
cv_pred <- cv_pred %>%
mutate(
y = ifelse(Rotación == "Rota", 1, 0),
pred_02 = ifelse(.pred_Rota > 0.2, 1, 0)
)
cv_pred %>%
summarise(
accuracy = mean(pred_02 == y),
sensibilidad = sum(pred_02 == 1 & y == 1) / sum(y == 1),
especificidad = sum(pred_02 == 0 & y == 0) / sum(y == 0)
)
## # A tibble: 1 × 3
## accuracy sensibilidad especificidad
## <dbl> <dbl> <dbl>
## 1 0.754 0.620 0.780
La validación cruzada de 5 pliegues muestra un AUC promedio de 0.769, ligeramente inferior al obtenido en la muestra completa (0.786), lo cual indica una buena capacidad de generalización del modelo. La baja variabilidad entre pliegues (error estándar de 0.012) sugiere que el desempeño del modelo es estable y consistente, evidenciando la ausencia de sobreajuste significativo.
Para evaluar la robustez de la regla de decisión, se implementó una validación cruzada con almacenamiento de predicciones. Esto permitió analizar el desempeño del modelo bajo un umbral de clasificación específico (0.2) en datos fuera de muestra, evidenciando su capacidad real para identificar empleados con alta probabilidad de rotación.
La evaluación del modelo mediante validación cruzada y un umbral de clasificación de 0.2 muestra una precisión global del 75.4%. Más importante aún, el modelo logra identificar el 62% de los empleados que rotan, manteniendo una correcta clasificación del 78% de aquellos que no rotan. Estos resultados evidencian un adecuado balance entre sensibilidad y especificidad, lo cual es fundamental en contextos donde la identificación temprana de rotación es prioritaria.
En el presente análisis no se incluyeron criterios tradicionales de bondad de ajuste como la prueba de razón de verosimilitud (Likelihood Ratio Test), la deviance, ni los criterios de información AIC y BIC. Si bien estas métricas son ampliamente utilizadas en modelos de regresión logística, su principal utilidad radica en la comparación entre modelos alternativos o en la evaluación del ajuste desde una perspectiva inferencial. En este caso, dado que la tarea se enfocó en la construcción de un modelo con fines predictivos y no en la comparación de múltiples especificaciones, se priorizaron métricas orientadas al desempeño predictivo, como el AUC, la validación cruzada y las medidas de clasificación asociadas a un umbral de decisión. Estas métricas permiten evaluar de manera más directa la capacidad del modelo para identificar correctamente la rotación de empleados, lo cual resulta más relevante para los objetivos de la actividad.
library(dplyr)
library(knitr)
library(kableExtra)
# Crear tabla base manual de hipótesis
hipotesis <- data.frame(
variable = c("Distancia_Casa",
"Trabajos_Anteriores",
"Antigüedad",
"Horas_Extra_Si",
"Cargo",
"Equilibrio_Trabajo_Vida"),
hipotesis = c(
"Aumenta oportunidad de rotación",
"Aumenta oportunidad de rotación",
"Reduce oportunidad de rotación",
"Aumenta oportunidad de rotación",
"Depende oportunidad de del cargo",
"Reduce oportunidad de rotación"
)
)
# Resultados del modelo (solo variables clave)
resultados_modelo <- resultados %>%
filter(term %in% c("Distancia_Casa",
"Trabajos_Anteriores",
"Antigüedad",
"Horas_Extra_Si")) %>%
mutate(
efecto = ifelse(estimate > 0, "Aumenta rotación", "Reduce rotación"),
significancia = ifelse(p.value < 0.05, "Significativa", "No significativa")
) %>%
select(term, efecto, significancia)
# Unir tablas
tabla_final <- hipotesis %>%
left_join(resultados_modelo, by = c("variable" = "term"))
# Evaluación de cumplimiento
tabla_final <- tabla_final %>%
mutate(
cumple = case_when(
variable == "Cargo" ~ "Parcial",
variable == "Equilibrio_Trabajo_Vida" ~ "Parcial",
hipotesis == efecto ~ "Sí",
TRUE ~ "No"
)
)
# Mostrar tabla
kable(tabla_final, caption = "Contraste de hipótesis vs resultados") %>%
kable_styling(full_width = FALSE)
| variable | hipotesis | efecto | significancia | cumple |
|---|---|---|---|---|
| Distancia_Casa | Aumenta oportunidad de rotación | Aumenta rotación | Significativa | No |
| Trabajos_Anteriores | Aumenta oportunidad de rotación | Aumenta rotación | Significativa | No |
| Antigüedad | Reduce oportunidad de rotación | Reduce rotación | Significativa | No |
| Horas_Extra_Si | Aumenta oportunidad de rotación | Aumenta rotación | Significativa | No |
| Cargo | Depende oportunidad de del cargo | NA | NA | Parcial |
| Equilibrio_Trabajo_Vida | Reduce oportunidad de rotación | NA | NA | Parcial |
# 9. Predicciones
nuevo_empleado <- rotacion[1, ]
nuevo_empleado$Horas_Extra <- "Si"
nuevo_empleado$Cargo <- "Ejecutivo_Ventas"
nuevo_empleado$Equilibrio_Trabajo_Vida <- factor(
levels(rotacion$Equilibrio_Trabajo_Vida)[1], # categoría
levels = levels(rotacion$Equilibrio_Trabajo_Vida),
ordered = TRUE
)
nuevo_empleado$Distancia_Casa <- 27
nuevo_empleado$Trabajos_Anteriores <- 1
nuevo_empleado$Antigüedad <- 2
prediccion <- predict(ajuste, new_data = nuevo_empleado, type = "prob")
prediccion
## # A tibble: 1 × 2
## `.pred_No rota` .pred_Rota
## <dbl> <dbl>
## 1 0.278 0.722
Se realizó la predicción de la probabilidad de rotación para un empleado hipotético con características específicas, incluyendo la realización de horas extra, cargo de ejecutivo de ventas, muy bajo equilibrio entre trabajo y vida personal, distancia al trabajo de 27 km, un trabajo anterior y una antigüedad de 2 años.
El modelo logístico estimó una probabilidad de rotación de 0.722 (72.2%), lo que indica un alto riesgo de que el empleado abandone la organización.
Dado que la probabilidad estimada es superior al punto de corte, se concluye que se recomienda intervenir al empleado.
Como estrategia, se sugiere implementar acciones orientadas a mejorar el equilibrio entre la vida laboral y personal, así como revisar la carga de trabajo y ofrecer incentivos que contribuyan a aumentar la satisfacción y permanencia del empleado en la organización.
Los resultados del modelo logístico evidencian que la rotación laboral está principalmente determinada por factores relacionados con las condiciones laborales y la estructura organizacional. En particular, el trabajo en horas extra incrementa significativamente la probabilidad de rotación, casi duplicando las probabilidades de abandono. Asimismo, variables como la distancia al trabajo y un mayor número de trabajos anteriores aumentan la probabilidad de rotación, mientras que la antigüedad en la empresa actúa como un factor de retención, lo cual es consistente con la literatura sobre estabilidad laboral. En cuanto a los cargos, se observa que posiciones como ejecutivo de ventas, representante de ventas, técnico de laboratorio e investigador científico presentan aumentos importantes en la oportunidad de rotación, siendo estas algunas de las variables con mayor magnitud de efecto dentro del modelo.
De manera contraintuitiva, el nivel más bajo de equilibrio trabajo-vida presenta una menor oportunidad de rotación respecto al nivel alto. Este resultado podría reflejar efectos de selección o restricciones en la movilidad laboral, donde empleados con condiciones menos favorables no necesariamente rotan más debido a limitaciones en sus alternativas de empleo. Por tanto, este resultado debe interpretarse con cautela.
En cuanto a las características que no resultaron estadísticamente significativas, como los cargos de director de manufactura, gerente y representante de salud, así como el nivel medio de equilibrio trabajo-vida, no se encuentra evidencia suficiente para afirmar que estas categorías influyan en la oportunidad de rotación en comparación con sus respectivas categorías base. Aunque algunos coeficientes presentan odds ratios mayores a 1, sus intervalos de confianza incluyen el valor 1 y los p-valores son superiores a los niveles convencionales de significancia, lo que indica que estos efectos podrían deberse al azar. Por lo tanto, no es posible establecer una relación concluyente entre estas variables y la rotación, por lo que su interpretación debe realizarse con cautela.
A partir de los resultados del modelo, se identifican varias líneas de intervención orientadas a disminuir la rotación laboral. En primer lugar, la fuerte asociación entre la realización de horas extra y una mayor oportunidad de rotación sugiere la necesidad de implementar políticas de gestión de carga laboral, como la redistribución de turnos, la contratación adicional de personal o el establecimiento de límites efectivos a las horas extra.
En segundo lugar, el efecto positivo de la distancia al trabajo indica que los costos de desplazamiento son un factor relevante, por lo que la empresa podría considerar esquemas de trabajo flexible, modalidades híbridas o apoyos al transporte.
En tercer lugar, el hecho de que empleados con mayor número de trabajos anteriores presenten mayor rotación sugiere la conveniencia de fortalecer los procesos de selección y retención temprana, así como programas de onboarding que incrementen el compromiso organizacional desde el inicio.
Adicionalmente, la antigüedad reduce la rotación, lo que indica que estrategias de fidelización, planes de carrera y desarrollo profesional pueden ser efectivos para retener talento en el mediano y largo plazo.
En cuanto a los cargos con mayores niveles de rotación (como ventas y roles técnicos), se recomienda diseñar incentivos específicos, esquemas de compensación variables y mejoras en las condiciones laborales adaptadas a las características de cada puesto. Finalmente, aunque el equilibrio trabajo-vida presenta resultados no lineales, su relevancia sugiere que políticas de bienestar laboral deben mantenerse, pero evaluarse con mayor profundidad para identificar segmentos donde estas intervenciones sean más efectivas.
En términos de desempeño predictivo, el modelo presenta una capacidad de discriminación adecuada, con un valor de AUC cercano a 0.79, lo que indica que logra diferenciar razonablemente entre empleados que rotan y los que no. La validación cruzada confirma la estabilidad de este resultado, con métricas consistentes entre muestras, lo que sugiere que el modelo no está sobreajustado. No obstante, el análisis de la matriz de confusión evidencia un trade-off importante entre sensibilidad y especificidad, particularmente al seleccionar el umbral de clasificación. En este sentido, aunque el umbral tradicional de 0.5 ofrece una alta precisión global, resulta poco efectivo para identificar correctamente a los empleados que rotan. Por el contrario, un umbral más bajo, como 0.2, mejora sustancialmente la capacidad del modelo para detectar casos de rotación, lo cual es más relevante desde una perspectiva de gestión preventiva. En conjunto, estos resultados indican que el modelo es útil como herramienta de apoyo a la toma de decisiones, especialmente para priorizar intervenciones en empleados con mayor riesgo de rotación, aunque su uso debe complementarse con criterios organizacionales y análisis adicionales.