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.
library(paqueteMODELOS)
## Loading required package: boot
## Loading required package: broom
## Loading required package: GGally
## Loading required package: ggplot2
## Registered S3 method overwritten by 'GGally':
## method from
## +.gg ggplot2
## Loading required package: gridExtra
## Loading required package: knitr
## Loading required package: summarytools
library(dplyr)
##
## Attaching package: 'dplyr'
## The following object is masked from 'package:gridExtra':
##
## combine
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
data("rotacion")
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, …
head(rotacion)
## # A tibble: 6 × 24
## Rotación Edad `Viaje de Negocios` Departamento Distancia_Casa Educación
## <chr> <dbl> <chr> <chr> <dbl> <dbl>
## 1 Si 41 Raramente Ventas 1 2
## 2 No 49 Frecuentemente IyD 8 1
## 3 Si 37 Raramente IyD 2 2
## 4 No 33 Frecuentemente IyD 3 4
## 5 No 27 Raramente IyD 2 1
## 6 No 32 Frecuentemente IyD 2 2
## # ℹ 18 more variables: Campo_Educación <chr>, Satisfacción_Ambiental <dbl>,
## # Genero <chr>, Cargo <chr>, Satisfación_Laboral <dbl>, Estado_Civil <chr>,
## # Ingreso_Mensual <dbl>, Trabajos_Anteriores <dbl>, Horas_Extra <chr>,
## # Porcentaje_aumento_salarial <dbl>, Rendimiento_Laboral <dbl>,
## # Años_Experiencia <dbl>, Capacitaciones <dbl>,
## # Equilibrio_Trabajo_Vida <dbl>, Antigüedad <dbl>, Antigüedad_Cargo <dbl>,
## # Años_ultima_promoción <dbl>, Años_acargo_con_mismo_jefe <dbl>
Seleccione 3 variables categóricas (distintas de rotación) y 3 variables cuantitativas, que se consideren estén relacionadas con la rotación.
Edad: La edad se asocia con la rotación. Los empleados jóvenes exploran su carrera, buscando nuevas oportunidades. Los mayores buscan estabilidad o se resisten al cambio por la cercanía a la jubilación.
Ingreso mensual: Los empleados con baja remuneración buscan mejores opciones. Perciben mayor remuneración por responsabilidades similares en otras empresas.
Antigüedad en el trabajo: La antigüedad se relaciona inversamente con la rotación. Los empleados con mayor tiempo acumulan beneficios, conocimiento institucional y lealtad a la empresa.
Horas extra: El trabajo extra aumenta la rotación por desgaste y descuido de la vida personal.
Estado civil: Los empleados casados valoran la estabilidad y seguridad laboral, lo que reduce la rotación.
Viaje de negocios: Los viajes frecuentes aumentan el estrés y la insatisfacción con el equilibrio entre la vida laboral y personal, lo que eleva la rotación.
Realiza un análisis univariado (caracterización) de la información contenida en la base de datos rotacion.
A continuación se muestran las estadísticas de las variables que componen el dataset.
summary(rotacion)
## Rotación Edad Viaje de Negocios Departamento
## Length:1470 Min. :18.00 Length:1470 Length:1470
## Class :character 1st Qu.:30.00 Class :character Class :character
## Mode :character Median :36.00 Mode :character Mode :character
## Mean :36.92
## 3rd Qu.:43.00
## Max. :60.00
## Distancia_Casa Educación Campo_Educación Satisfacción_Ambiental
## Min. : 1.000 Min. :1.000 Length:1470 Min. :1.000
## 1st Qu.: 2.000 1st Qu.:2.000 Class :character 1st Qu.:2.000
## Median : 7.000 Median :3.000 Mode :character Median :3.000
## Mean : 9.193 Mean :2.913 Mean :2.722
## 3rd Qu.:14.000 3rd Qu.:4.000 3rd Qu.:4.000
## Max. :29.000 Max. :5.000 Max. :4.000
## Genero Cargo Satisfación_Laboral Estado_Civil
## Length:1470 Length:1470 Min. :1.000 Length:1470
## Class :character Class :character 1st Qu.:2.000 Class :character
## Mode :character Mode :character Median :3.000 Mode :character
## Mean :2.729
## 3rd Qu.:4.000
## Max. :4.000
## Ingreso_Mensual Trabajos_Anteriores Horas_Extra
## Min. : 1009 Min. :0.000 Length:1470
## 1st Qu.: 2911 1st Qu.:1.000 Class :character
## Median : 4919 Median :2.000 Mode :character
## Mean : 6503 Mean :2.693
## 3rd Qu.: 8379 3rd Qu.:4.000
## Max. :19999 Max. :9.000
## Porcentaje_aumento_salarial Rendimiento_Laboral Años_Experiencia
## Min. :11.00 Min. :3.000 Min. : 0.00
## 1st Qu.:12.00 1st Qu.:3.000 1st Qu.: 6.00
## Median :14.00 Median :3.000 Median :10.00
## Mean :15.21 Mean :3.154 Mean :11.28
## 3rd Qu.:18.00 3rd Qu.:3.000 3rd Qu.:15.00
## Max. :25.00 Max. :4.000 Max. :40.00
## Capacitaciones Equilibrio_Trabajo_Vida Antigüedad Antigüedad_Cargo
## Min. :0.000 Min. :1.000 Min. : 0.000 Min. : 0.000
## 1st Qu.:2.000 1st Qu.:2.000 1st Qu.: 3.000 1st Qu.: 2.000
## Median :3.000 Median :3.000 Median : 5.000 Median : 3.000
## Mean :2.799 Mean :2.761 Mean : 7.008 Mean : 4.229
## 3rd Qu.:3.000 3rd Qu.:3.000 3rd Qu.: 9.000 3rd Qu.: 7.000
## Max. :6.000 Max. :4.000 Max. :40.000 Max. :18.000
## Años_ultima_promoción Años_acargo_con_mismo_jefe
## Min. : 0.000 Min. : 0.000
## 1st Qu.: 0.000 1st Qu.: 2.000
## Median : 1.000 Median : 3.000
## Mean : 2.188 Mean : 4.123
## 3rd Qu.: 3.000 3rd Qu.: 7.000
## Max. :15.000 Max. :17.000
Ahora, validaremos si hay datos faltantes en el dataset.
## /\ /\
## { `---' }
## { O O }
## ==> V <== No need for mice. This data set is completely observed.
## \ \|/ /
## `-----'
Teniendo en cuenta que no se encontraron datos faltantes en el dataset
rotacion, se analizan una por una las variables elegidas en el punto
anterior, iniciando con histogramas y cajas y bigotes para las variables
cualitativas.
par(mfrow = c(1, 3))
hist(rotacion$Edad, main = "Histograma de Edad", xlab = "Edad", col = "#EEDFCC")
hist(rotacion$Ingreso_Mensual, main = "Histograma de Ingreso Mensual", xlab = "Ingreso mensual", col ="#7FFFD4")
hist(rotacion$Antigüedad, main = "Histograma de Antigüedad", xlab = "Antigüedad", col ="#CDB7B5")
La variable edad tiene un valor mínimo de 18 años, hasta los 60 años con un promedio de 36,9 y una mediana de 26 años. No cuenta con una distribución normal, se identifica un pequeño sesgo a la derecha.
El Ingreso mensual varía entre 1.009 y 19.999 con media de 6503 y mediana de 4919. No se observa una distribución normal.
La variable antiguedad va desde 0 hasta 40 años,la antigüedad promedio es de 5 años y la mediana de 5 años. El 25% tiene menos de 3 años de antiguedad y otro 25% tiene entre 9 y 40 años. Esta variable tiene valores extremos superiores.
par(mfrow = c(1, 3))
barplot(table(rotacion$Horas_Extra), main = "Gráfico barras Horas_Extra", xlab = "Horas_Extra", col = "#98F5FF")
barplot(table(rotacion$Estado_Civil), main = "Gráfico barras Estado_Civil", xlab = "Estado_Civil", col = "#EE6A50")
barplot(table(rotacion$`Viaje de Negocios`), main = "Gráfico barras Viaje_Negocios", xlab = "Viaje_Negocios", col = "#BCEE68")
El 71% de los empleados viaja raramente (n=1043). El 19% viaja frecuentemente (n=277) y el 10% restante no viaja.
## Viaje_de_Negocios Total Porcentaje
## 1 Frecuentemente 277 18.84%
## 2 No_Viaja 150 10.20%
## 3 Raramente 1043 70.95%
El 45.7% de los empleados son casados (n=673), el 32% es soltero (n= 470) y el 22% restante es divorciado.
## Viaje_de_Negocios Total Porcentaje
## 1 Casado 673 45.78%
## 2 Divorciado 327 22.24%
## 3 Soltero 470 31.97%
El 71,7% de los empleados no trabajan horas extra (n= 1054), el 28.5% restante sí.
## Viaje_de_Negocios Total Porcentaje
## 1 Casado 673 45.78%
## 2 Divorciado 327 22.24%
## 3 Soltero 470 31.97%
Realiza un análisis de bivariado en donde la variable respuesta sea rotacion codificada de la siguiente manera (y=1 es si rotación, y=0 es no rotación). Con base en estos resultados identifique cuales son las variables determinantes de la rotación e interpretar el signo del coeficiente estimado. Compare estos resultados con la hipotesis planteada en el punto 2.
library(ggplot2)
ggplot(rotacion, aes(x = Rotación, y = Edad, fill= Rotación)) +
geom_boxplot() +
labs(title = "Distribución de Edad según Rotación",
x = "Rotación",
y = "Edad") +
scale_fill_manual(values = c("Si" = "#40E0D0", "No" = "#006A89"))
Se presenta una menor rotación en edades por encima de los 30, aunque
hay casos atípicos en los que se presenta rotación por encima de los 50
años.
ggplot(rotacion, aes(x = Rotación, y = Ingreso_Mensual, fill = Rotación)) +
geom_boxplot() +
labs(title = "Distribución de Ingreso Mensual según Rotación",
x = "Rotación",
y = "Ingreso Mensual") +
scale_fill_manual(values = c("Si" = "#40E0D0", "No" = "#006A89"))
Al observar la gráfica, se identifica que las personas con rotación,
tienen un ingreso mensual menor que aquellos que no rotan.
ggplot(rotacion, aes(x = Rotación, y = Antigüedad, fill = Rotación)) +
geom_boxplot() +
labs(title = "Distribución de Antigüedad según Rotación",
x = "Rotación",
y = "Antigüedad") +
scale_fill_manual(values = c("Si" = "#40E0D0", "No" = "#006A89"))
Igual que con las variables anteriores, se observan diferencias en
Antigüedad entre los trabajadores que rotan y los que no rotan,
identificando rotación en el grupo con menor Antigüedad.
ggplot(data = rotacion, aes(x = Rotación, fill = `Horas_Extra`)) +
geom_bar(position = "dodge") +
labs(title = "Rotación y Horas Extra",
x = "Rotación",
y = "Frecuencia",
fill = "Horas Extra")
En el grupo que no tiene rotación, hay mayor frecuencia de trabajadores
que no realizan horas extra. En el grupo que sí tiene rotación, hay más
trabajadores que sí realizaron horas extra.
ggplot(data = rotacion, aes(x = Rotación, fill = `Estado_Civil`)) +
geom_bar(position = "dodge") +
labs(title = "Rotación y Estado Civil",
x = "Rotación",
y = "Frecuencia",
fill = "Estado Civil")
En el grupo de no rotación, la mayor frecuencia de empleados se
encuentra en el grupo de casados y en segundo lugar de solteros. En el
grupo que sí presenta rotación, hay una mayor frecuencia de empleados
solteros y en segundo lugar los casados. están invertidos en relación
con el grupo de no rotación. El grupo de divorciados es el tercero en
ambos grupos.
ggplot(data = rotacion, aes(x = Rotación, fill = `Viaje de Negocios`)) +
geom_bar(position = "dodge") +
labs(title = "Rotación y Viaje de negocios",
x = "Rotación",
y = "Frecuencia",
fill = "Viaje de Negocios")
Al observar la gráfica, se identifica un comportamiento similar de viaje
de negocios en el grupo que presenta rotación y el grupo que no rota. La
mayoría de empleados en cada grupo raramente viaja, seguido del grupo
que viaja frecuentemente y por último el grupo que no viaja.
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.
Inicialmente nos quedamos con las variables seleccionadas y analizadas en los puntos anteriores:
df <- rotacion[c("Edad", "Ingreso_Mensual", "Antigüedad",
"Estado_Civil", "Horas_Extra",
"Viaje de Negocios", "Rotación")]
head(df)
## # A tibble: 6 × 7
## Edad Ingreso_Mensual Antigüedad Estado_Civil Horas_Extra `Viaje de Negocios`
## <dbl> <dbl> <dbl> <chr> <chr> <chr>
## 1 41 5993 6 Soltero Si Raramente
## 2 49 5130 10 Casado No Frecuentemente
## 3 37 2090 0 Soltero Si Raramente
## 4 33 2909 8 Casado Si Frecuentemente
## 5 27 3468 2 Casado No Raramente
## 6 32 3068 7 Soltero No Frecuentemente
## # ℹ 1 more variable: Rotación <chr>
Se recodifican las variables categóricas Estado civil, Horas Extra y Viaje de negocios. Dado que no tienen un orden jerárquico, se recodifican Estado Civil y Viaje de negocios como variables dummies:
# Recodificar "Estado_Civil" a variables dummy
estado_civil_dummies <- model.matrix(~ Estado_Civil - 1, data = df)
# Unir las nuevas variables dummy al dataframe original (asegúrate de no incluir la variable original para evitar multicolinealidad)
df <- cbind(df, estado_civil_dummies)
# Recodificar "Horas extra"
df$Horas_Extra <- ifelse(df$Horas_Extra == "Si", 1, 0)
# Recodificar "Viaje de Negocios" a variables dummy
Viaje_dummies <- model.matrix(~ `Viaje de Negocios` - 1, data = df)
# Unir las nuevas variables dummy al dataframe original
df <- cbind(df, Viaje_dummies)
# Eliminar las variables categóricas originales
df <- df[, !(names(df) %in% c("Estado_Civil", "Viaje de Negocios"))]
Revisamos cómo queda ahora el dataset:
head(df)
## Edad Ingreso_Mensual Antigüedad Horas_Extra Rotación Estado_CivilCasado
## 1 41 5993 6 1 Si 0
## 2 49 5130 10 0 No 1
## 3 37 2090 0 1 Si 0
## 4 33 2909 8 1 No 1
## 5 27 3468 2 0 No 1
## 6 32 3068 7 0 No 0
## Estado_CivilDivorciado Estado_CivilSoltero `Viaje de Negocios`Frecuentemente
## 1 0 1 0
## 2 0 0 1
## 3 0 1 0
## 4 0 0 1
## 5 0 0 0
## 6 0 1 1
## `Viaje de Negocios`No_Viaja `Viaje de Negocios`Raramente
## 1 0 1
## 2 0 0
## 3 0 1
## 4 0 0
## 5 0 1
## 6 0 0
Ahora se realiza la división de los datos en test y train, para el posterior entrenamiento del modelo.
library(caret)
## Loading required package: lattice
##
## Attaching package: 'lattice'
## The following object is masked from 'package:boot':
##
## melanoma
set.seed(123)
particion <- createDataPartition(y = df$Rotación, p = 0.7, list = FALSE) # list = FALSE hace que la función devuelva un vector de índices en lugar de una lista
# Crear los conjuntos de datos de entrenamiento y prueba:
train <- df[particion, ]
test <- df[-particion, ]
Se valida que los resultados en las categorías de respuesta hayan quedado asignadas en los diferentes conjuntos de datos:
table(df$Rotación) # Revisar en el conjunto de datos completo
##
## No Si
## 1233 237
Conjunto train
table(train$Rotación)
##
## No Si
## 864 166
Conjunto test
table(test$Rotación)
##
## No Si
## 369 71
trainControl <- trainControl(method = "cv", number = 10) # Se incluye validación cruzada y 10 iteraciones
# Entrenar el modelo de regresión logística
modelo_logistico <- train(Rotación ~ ., data = train,
method = "glm",
family = "binomial",
trControl = trainControl)
# Mostrar el resultado del entrenamiento
print(modelo_logistico)
## Generalized Linear Model
##
## 1030 samples
## 10 predictor
## 2 classes: 'No', 'Si'
##
## No pre-processing
## Resampling: Cross-Validated (10 fold)
## Summary of sample sizes: 927, 928, 928, 927, 927, 927, ...
## Resampling results:
##
## Accuracy Kappa
## 0.8494994 0.2177855
El modelo predijo correctamente la categoría de “Rotación” en aproximadamente el 84.9% de los casos a través de la validación cruzada.
El coeficiente Kappa es una medida que compara la exactitud observada con la exactitud que se esperaría por azar, ajustando por el desequilibrio en la distribución de las clases. Un valor de Kappa de aproximadamente 0.21 sugiere una concordancia moderada.
Evaluar el poder predictivo del modelo con base en la curva ROC y el AUC.
Usando el modelo final para predecir las probabilidades en el conjunto de datos de prueba:
probabilidades_predichas <- predict(modelo_logistico, newdata = test, type = "prob")
# Cargar el paquete pROC
library(pROC)
## Type 'citation("pROC")' for a citation.
##
## Attaching package: 'pROC'
## The following objects are masked from 'package:stats':
##
## cov, smooth, var
# Calcular la curva ROC usando las probabilidades de la clase de interés y las etiquetas verdaderas
roc_resultado <- roc(test$Rotación, probabilidades_predichas[, "Si"])
## Setting levels: control = No, case = Si
## Setting direction: controls < cases
# Calcular el AUC
auc_resultado <- auc(roc_resultado)
print(auc_resultado)
## Area under the curve: 0.7563
Gráficamos el área bajo la curva
plot(roc_resultado, main = "Curva ROC", col = "#1f77b4")
abline(a = 0, b = 1, lty = 2, col = "red")
Un AUC de 0.75 significa que hay aproximadamente un 75.63% de probabilidad de que el modelo clasifique correctamente un par aleatorio de observaciones. El área comprendida entre la curva ROC y la diagonal del cuadrado (AUC = 0.75), indica un buen ajuste del modelo de predicción. Esto significa que el modelo es capaz de identificar correctamente a los empleados que tienen mayor probabilidad de rotar, lo que puede ser útil para la empresa a la hora de tomar decisiones sobre la gestión del talento humano.
La rotación de empleados representa un desafío para las empresas, impactando en la productividad, el clima laboral y los costos. Este estudio busca identificar variables que influyen en la rotación y proponer estrategias para reducirla.
Se analizó una base de datos con información de empleados, incluyendo variables como edad, ingreso, antigüedad, horas extra, viajes de negocios y estado civil. Se realizó un análisis estadístico para determinar la relación entre estas variables y la rotación.
El modelo predictivo tiene alta sensibilidad pero baja especificidad, lo que indica que es bueno para detectar empleados que no rotan, pero no tanto para aquellos que sí rotan. La edad y el ingreso no mostraron una relación significativa con la rotación. Trabajar horas extra y realizar viajes de negocios frecuentes se asocian con una mayor probabilidad de rotación. Mayor antigüedad, estado civil casado o divorciado (comparado con solteros) y no realizar viajes de negocios (comparado con viajar raramente) se asocian con menor rotación.