Modelo de Regresión LogísticaLa rotación de empleados es uno de los desafío más para las organizaciones, la cual tiene una gran afectación en la productividad y los costos operativos, así tambien como en el desarrollo personal y profesional de sus colaboradores. Mediante el uso de tecnicas de análisis de datos, se propone abordar el uso del modelo de regresión logistica en el conjunto de datos historico relacionados con el empleo con variables como antigüedad, cargo, satisfacción laboral, salario, edad y otros factores relevantes.
Este modelo tiene como objetivo principal estimar la probabilidad de que un empleado cambie de cargo en el próximo período y, además, identificar los factores que más inciden en estos cambios.
La implementación de este modelo de regresión logística no solo permitirá a la organización anticipar posibles rotaciones, sino que también proporcionará información valiosa para la toma de decisiones estratégicas en la gestión de recursos humanos.
Para el desarrollo de este informe, se hará uso de técnicas y herramientas estadísticas para organizar, resumir y presentar los datos recopilados de acuerdo con la guía del curso de modelos estadisticos para la toma de decisiones, en su modulo de regresión logistica. El desarrollo abordará las siguientes etapas:
Análisis explotario, descriptivo y pre-procesamiento de datos.
Modelo de Regresión Lógistica y métricas de evaluación.
Conclusiones y recomendaciones a la organización.
En este apartado, se realizará la carga de la base de datos original, se realizará una primera visualización de los datos obtenidos, además de los paquetes a utilizar para el desarrollo de la actividad.
Consolidado de paquete de librerias a utilizar en el proyecto.
library(paqueteMODELOS)
library(dplyr)
library(kableExtra)
library(knitr)
library(ggplot2)
library(reshape2)
library(GGally)
library(pheatmap)
library(plotly)
library(flextable)
library(caret)
library(pROC)
Podemos apreciar que la base de datos tiene 24 variables y 1470 registros, entre ellas tenemos 2 variables binarias (“Rotación” y “Horas_Extra), 6 variables categóricas (“Viaje de Negocios”, “Departamento”, “Campo_Educación”, “Género”, “Cargo”, “Estado_Civil”), 5 variables que si bien están escritas como numéricas, son en realidad 5 variables cualitativas u ordinales, y por tanto categóricas (“Educación”, “Satisfacción_Ambiental”, “Satisfación_Laboral”, “Rendimiento_Laboral”, “Equilibrio_Trabajo_Vida”), y las restantes 11 son variables numéricas. Revisemos si existen datos faltantes en la base de datos.
#Descarga de la base de datos original
# devtools::install_github("centromagis/paqueteMODELOS", force =TRUE)
library(paqueteMODELOS)
data("rotacion")
#Se crea una copia de seguridad de los datos originales.
rotacion1 = rotacion
Sin embargo, revisando el set de datos cargado y considerando la naturaleza de las variables presentes, se puede identificar que hay algunas variables de tipo numérico que en realidad representan categorías. Por ello, se debería considerar su tratamiento como categóricas las siguientes variables:
Por lo anterior, se procede con la transformación de estas variables numéricas a categóricas, y se visualiza de la siguiente manera:
# Convertimos las variables numéricas a categóricas (factores)
rotacion1$Educación = factor(rotacion1$Educación,
levels = c(1, 2, 3, 4, 5),
labels = c("Primaria", "Secundaria", "Técnico/Tecnólogo", "Pregrado", "Posgrado"))
rotacion1$Satisfacción_Ambiental = factor(rotacion1$Satisfacción_Ambiental,
levels = c(1, 2, 3, 4),
labels = c("Muy insatisfecho", "Insatisfecho", "Satisfecho", "Muy satisfecho"))
rotacion1$Satisfación_Laboral = factor(rotacion1$Satisfación_Laboral,
levels = c(1, 2, 3, 4),
labels = c("Muy insatisfecho", "Insatisfecho", "Satisfecho", "Muy satisfecho"))
rotacion1$Rendimiento_Laboral = factor(rotacion1$Rendimiento_Laboral,
levels = c(1, 2, 3, 4),
labels = c("Bajo", "Medio", "Alto", "Muy alto"))
rotacion1$Equilibrio_Trabajo_Vida <- factor(rotacion1$Equilibrio_Trabajo_Vida,
levels = c(1, 2, 3, 4),
labels = c("Muy bajo", "Bajo", "Medio", "Alto"))
#Validamos la transformación
glimpse(rotacion1)
## 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 <fct> Secundaria, Primaria, Secundaria, Pregrado…
## $ Campo_Educación <chr> "Ciencias", "Ciencias", "Otra", "Ciencias"…
## $ Satisfacción_Ambiental <fct> Insatisfecho, Satisfecho, Muy satisfecho, …
## $ Genero <chr> "F", "M", "M", "F", "M", "M", "F", "M", "M…
## $ Cargo <chr> "Ejecutivo_Ventas", "Investigador_Cientifi…
## $ Satisfación_Laboral <fct> Muy satisfecho, Insatisfecho, Satisfecho, …
## $ 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 <fct> Alto, Muy alto, Alto, Alto, Alto, Alto, Mu…
## $ 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 <fct> Muy bajo, Medio, Medio, Medio, Medio, Bajo…
## $ 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, …
Una vez revisado el set de datos, se confirma la inexistencia de valores nulos a través de la siguiente tabla:
#Conocer los datos faltantes de la nueva BD filtrada
faltantes = colSums(is.na(rotacion1)) %>%
as.data.frame()
colnames(faltantes) = "Datos Faltantes"
kable(faltantes, caption = " ") %>%
kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = FALSE, position = "center")
| Datos Faltantes | |
|---|---|
| Rotación | 0 |
| Edad | 0 |
| Viaje de Negocios | 0 |
| Departamento | 0 |
| Distancia_Casa | 0 |
| Educación | 0 |
| Campo_Educación | 0 |
| Satisfacción_Ambiental | 0 |
| Genero | 0 |
| Cargo | 0 |
| Satisfación_Laboral | 0 |
| Estado_Civil | 0 |
| Ingreso_Mensual | 0 |
| Trabajos_Anteriores | 0 |
| Horas_Extra | 0 |
| Porcentaje_aumento_salarial | 0 |
| Rendimiento_Laboral | 0 |
| Años_Experiencia | 0 |
| Capacitaciones | 0 |
| Equilibrio_Trabajo_Vida | 0 |
| Antigüedad | 0 |
| Antigüedad_Cargo | 0 |
| Años_ultima_promoción | 0 |
| Años_acargo_con_mismo_jefe | 0 |
De igual forma, se valida que en el conjunto de datos, no existan registros duplicados para eliminar.
# Contamos registros duplicados en el dataframe
duplicados = sum(duplicated(rotacion1))
resultado_duplicados = data.frame(
Descripción = "Registros duplicados",
Cantidad = duplicados
)
kable(resultado_duplicados, col.names = c("Descripción", "Cantidad"), format = "html") %>%
kable_styling(position = "center", full_width = F) %>%
column_spec(1:2, width = "5cm")
| Descripción | Cantidad |
|---|---|
| Registros duplicados | 0 |
A continuación, para conocer un poco más la composición de las variables del dataset, se mostrarán y analizarán algunas tablas.
#Ver frecuencia de categorías en la variable "Rotación"
kbl(addmargins(table(rotacion1$Rotación, useNA = "ifany")), caption = "<center><b>Tabla 2. Frecuencia de la variable Rotación</b></center>", col.names=c("Rotación","Frecuencia"))%>%
kable_classic(full_width = F)
| Rotación | Frecuencia |
|---|---|
| No | 1233 |
| Si | 237 |
| Sum | 1470 |
Vemos que “Rotación” es efectivamente binaria, no parece haber ningún problema aquí, aunque a futuro parece oportuno tener en cuenta que hay una mucho mayor presencia de “No” que de “Sí“. Continuemos ahora con la variable “Viaje de Negocios”.
#Ver frecuencia de categorías en la variable "Viaje de Negocios"
kbl(addmargins(table(rotacion1$`Viaje de Negocios` , useNA = "ifany")), caption = "<center><b>Tabla 3. Frecuencia de la variable Viaje de Negocios</b></center>", col.names=c("Cuanto Viaja","Frecuencia"))%>%
kable_classic(full_width = F)
| Cuanto Viaja | Frecuencia |
|---|---|
| Frecuentemente | 277 |
| No_Viaja | 150 |
| Raramente | 1043 |
| Sum | 1470 |
La variable “Viaje de negocios” tampoco parece mostrar ningún inconveniente, continuemos con la variable “Departamento”.
#Ver frecuencia de categorías en la variable "Viaje de Negocios"
kbl(addmargins(table(rotacion1$Departamento , useNA = "ifany")), caption = "<center><b>Tabla 3. Frecuencia de la variable Viaje de Negocios</b></center>", col.names=c("Cuanto Viaja","Frecuencia"))%>%
kable_classic(full_width = F)
| Cuanto Viaja | Frecuencia |
|---|---|
| IyD | 961 |
| RH | 63 |
| Ventas | 446 |
| Sum | 1470 |
Vemos que en la variable departamento hay tres categorías, “IyD”, “RH” y “Ventas”, con las que se registró el personal evaluado, no parece haber ningún problema con esta variable. Continuamos con la variable “Campo_Educación”.
#Ver frecuencia de categorías en la variable "Campo_Educación"
kbl(addmargins(table(rotacion1$Campo_Educación , useNA = "ifany")), caption = "<center><b>Tabla 5. Frecuencia de la variable Campo_Educación</b></center>", col.names=c("Campo","Frecuencia"))%>%
kable_classic(full_width = F)
| Campo | Frecuencia |
|---|---|
| Ciencias | 606 |
| Humanidades | 27 |
| Mercadeo | 159 |
| Otra | 82 |
| Salud | 464 |
| Tecnicos | 132 |
| Sum | 1470 |
Hay 6 campos de educación distintos considerados, no parece haber inconsistencias con los campos. Así que continuamos con la variable “Genero”.
#Ver frecuencia de categorías en la variable "Genero"
kbl(addmargins(table(rotacion1$Genero , useNA = "ifany")), caption = "<center><b>Tabla 6. Frecuencia de la variable Genero</b></center>", col.names=c("Género","Frecuencia"))%>%
kable_classic(full_width = F)
| Género | Frecuencia |
|---|---|
| F | 588 |
| M | 882 |
| Sum | 1470 |
La variable “genero” muestra que hay una mayor proporción de hombres en los registros, no parece haber inconsistencia en los datos. Continuamos con la variable “Cargo”.
#Ver frecuencia de categorías en la variable "Cargo"
kbl(addmargins(table(rotacion1$Cargo , useNA = "ifany")), caption = "<center><b>Tabla 7. Frecuencia de la variable Cargo</b></center>", col.names=c("Cargo","Frecuencia"))%>%
kable_classic(full_width = F)
| Cargo | Frecuencia |
|---|---|
| Director_Investigación | 80 |
| Director_Manofactura | 145 |
| Ejecutivo_Ventas | 326 |
| Gerente | 102 |
| Investigador_Cientifico | 292 |
| Recursos_Humanos | 52 |
| Representante_Salud | 131 |
| Representante_Ventas | 83 |
| Tecnico_Laboratorio | 259 |
| Sum | 1470 |
La variable “Cargo” identifica 9 cargos diferentes, no parece haber inconsistencias en esta variable. Revisamos ahora la variable “Estado_Civil”.
#Ver frecuencia de categorías en la variable "Estado Civil"
kbl(addmargins(table(rotacion1$Estado_Civil , useNA = "ifany")), caption = "<center><b>Tabla 8. Frecuencia de la variable Estado_Civil</b></center>", col.names=c("Estado Civil","Frecuencia"))%>%
kable_classic(full_width = F)
| Estado Civil | Frecuencia |
|---|---|
| Casado | 673 |
| Divorciado | 327 |
| Soltero | 470 |
| Sum | 1470 |
La variable “Estado_Civil” identifica 3 categorías, no parece haber inconsistencias con las categorías encontradas. Continuamos con la variable “Horas_Extra”
#Ver frecuencia de categorías en la variable "Horas_Extra"
kbl(addmargins(table(rotacion1$Horas_Extra , useNA = "ifany")), caption = "<center><b>Tabla 9. Frecuencia de la variable Horas_Extra</b></center>", col.names=c("Horas Extras","Frecuencia"))%>%
kable_classic(full_width = F)
| Horas Extras | Frecuencia |
|---|---|
| No | 1054 |
| Si | 416 |
| Sum | 1470 |
Finalmente, la variable “Horas_Extra” se puede visualizar que es una variable binaria, y no parece presentar ningún problema. Con ello terminamos de revisar las variables categóricas y binarias.
Algunas fuentes de internet y revistas especializadas, mencionan variables clave que en el estado del arte de la temática están fuertemente relacionadas con la rotación de personas. Entre ellas se incluye el salario como principal causa de abandono, el reconocimiento, la oportunidad de carrera, el balance trabajo-vida, el clima laboral, la carga, presión o estrés, la jornada laboral, el tiempo en la empresa, la formación y capacitación y la motivación.
Formulación de Hipótesis:
Variables Numéricas (cuantitativas)
Ingreso Mensual: el salario es una de las variables que, según la documentación, tiene mayor atribución en la decisión de rotación de cargo en las personas. El factor económico pondera en la mayoría de las ocasiones, se puede inferir que si una empresa otorga un mejor beneficio económico (con las mismas funciones), la probabilidad de cambio o rotación es alta. Una persona con un buen salario, tiende a permanecer en su cargo o empresa.
Antigüedad en el Cargo: Los años que un empleado lleve en el cargo, pueden influir en su decisión de rotación. La hipótesis es que, si la persona lleva más años vinculado a la empresa o cargo, su decisión de cambio es menos probable.
Años de Experiencia: LSe podría asumir que entre mayor experiencia laboral, hay un mayor nivel de conocimiento del empleo, del entorno, del sector y/o de la organización, por lo que es menos deseable que dicha persona abandone la organización, por lo que teóricamente se esperaría que entre más años de experiencia menor probabilidad de cambiar de cargo.
Variables Categóricas (cualitativas)
Horas Extras: Las personas que laboran horas extras a menudo representan una mayor carga física, emocional, actitudinal. Podrían descuidar aspectos tanto personales como familiares, por lo que se puede esperar una probabilidad alta de rotación a medida que su carga laboral extra aumente.
Satisfacción laboral: Esta variable puede incluir varias causas que no están netamente mencionadas en el dataset, como la motivación, reconocimiento, balance trabajo-vida, entre otras. Lo que quiere decir que enmarca a otras variables y de ahí su importancia en la elección para el modelo. La hipótesis sugiere que una persona con un alto nivel de satisfacción laboral, tienda a conservar su cargo o permanecer en la empresa.
Cargo: Existen diferentes tipos de cargos en una organización. Aunque todos los cargos demandan una responsabilidad, la hipótesis reposa en que los cargos más operativos y técnicos suelan tener un más alto índice de rotación, en comparación a otros cargos directivos o gerenciales.
La variable ingreso mensual presenta una distribución que va desde los $1,009 hasta los $19,999, lo que quiere decir que tenemos un amplio rango de valores. La media se situa en los $6,502.93, con una mediana de $4,919. Graficamente podemos observar una distribución asimétrica con una cola hacia los ingresos más altos. Un 25% de la población gana menos de $2,911 y un 75% de la población se situa en salarios menos o iguales a $8,380, lo que representa una “normalidad” en esta variable salarios, donde son muy pocas las personas que acceden a grandes ingresos, así como lo muestra la gráfica de cajas, que incluye gran cantidas de outliers.
#Análisis de las variables
summarytools::descr(rotacion1$Ingreso_Mensual)
## Descriptive Statistics
## rotacion1$Ingreso_Mensual
## N: 1470
##
## Ingreso_Mensual
## ----------------- -----------------
## Mean 6502.93
## Std.Dev 4707.96
## Min 1009.00
## Q1 2911.00
## Median 4919.00
## Q3 8380.00
## Max 19999.00
## MAD 3260.24
## IQR 5468.00
## CV 0.72
## Skewness 1.37
## SE.Skewness 0.06
## Kurtosis 0.99
## N.Valid 1470.00
## Pct.Valid 100.00
#Diagrama de Cajas Ingreso
boxplot(rotacion1$Ingreso_Mensual,
main = "Diagrama de Cajas: Ingreso Mensual",
xlab = "Ingreso $ (Millones COP)",
col = "#9abaf9",
border = "#001865",
horizontal = TRUE)
#Histograma Ingreso
histIM = plot_ly(alpha = 0.6)
histIM = histIM %>% add_histogram(x = rotacion1$Ingreso_Mensual, name = "Ingreso mensual", marker = list(color = "#001865", opacity = 0.5, line = list(color = 'black', width = 1)))
histIM = histIM %>% layout(barmode = "overlay", title = "Histograma de ingreso mensual")
histIM
Para la variable antigüedad en el cargo el promedio de los años está en 4,23 años y de acuerdo con el gráfico de cajas el 50% de las personas tienen una antigüedad que está entre 2 y 7 años, se encuentran unos pocos valores atípicos donde el valor máximo de antigüedad de una persona trabajando en la empresa llega a los 18 años y el mínimo tiene menos de 1 año de antigüedad. El mayor número de personas está en el rango de los 2 años seguido por los nuevos que tienen menos de 1 año y ya posteriormente por un tema de madurez profesional están los que tienen 7 años.
#Analisis de la variable
summarytools::descr(rotacion1$Antigüedad_Cargo)
## Descriptive Statistics
## rotacion1$Antigüedad_Cargo
## N: 1470
##
## Antigüedad_Cargo
## ----------------- ------------------
## Mean 4.23
## Std.Dev 3.62
## Min 0.00
## Q1 2.00
## Median 3.00
## Q3 7.00
## Max 18.00
## MAD 4.45
## IQR 5.00
## CV 0.86
## Skewness 0.92
## SE.Skewness 0.06
## Kurtosis 0.47
## N.Valid 1470.00
## Pct.Valid 100.00
#Cajas para antiguedad en el cargo
boxplot(rotacion1$Antigüedad_Cargo,
main = "Diagrama de Cajas: Antiguedad Cargo",
xlab = "Antiguedad",
col = "#9abaf9",
border = "#001865",
horizontal = TRUE)
#histograma ploty
histANT = plot_ly(alpha = 0.6)
histANT = histANT %>% add_histogram(x = rotacion1$Antigüedad_Cargo, name = "Antiguedad", marker = list(color = "#001865", opacity = 0.5, line = list(color = 'black', width = 1)))
histANT = histANT %>% layout(barmode = "overlay", title = "Histograma de Antiguedad en el Cargo")
histANT
La variable años de experiencia presenta un valor promedio de 11,28 años lo que indica que la empresa cuenta con personal calificado de acuerdo con su experiencia para realizar la labor, los datos del diagrama de cajas muestran que el 50% de la experiencia del personal está entre los 6 y 15 años, entre los valores mínimos hay personal que aparece con 0 años lo que corresponde a meses en la empresa, importante resaltar que el valor máximo en años de experiencia es de 40 años.
#Analisis de la variable
summarytools::descr(rotacion1$Años_Experiencia)
## Descriptive Statistics
## rotacion1$Años_Experiencia
## N: 1470
##
## Años_Experiencia
## ----------------- ------------------
## Mean 11.28
## Std.Dev 7.78
## Min 0.00
## Q1 6.00
## Median 10.00
## Q3 15.00
## Max 40.00
## MAD 5.93
## IQR 9.00
## CV 0.69
## Skewness 1.11
## SE.Skewness 0.06
## Kurtosis 0.91
## N.Valid 1470.00
## Pct.Valid 100.00
#Cajas Años de experiencia
boxplot(rotacion1$Años_Experiencia,
main = "Diagrama de Cajas: Años de Experiencia",
xlab = "Años de Experiencia",
col = "#9abaf9",
border = "#001865",
horizontal = TRUE)
#Histograma años de experiencia
histED = plot_ly(alpha = 0.6)
histED = histED %>% add_histogram(x = rotacion1$Años_Experiencia, name = "Años_Experiencia", marker = list(color = "#001865", opacity = 0.5, line = list(color = 'black', width = 1)))
histED = histED %>% layout(barmode = "overlay", title = "Histograma de Años de Experiencia")
histED
Con los siguientes datos podemos visualizar las características primarias del conjunto de datos para las variables numéricas, en la sección del análisis exploratorio se realizaron tablas que describen el comportamiento de las variables categóricas y binarias.
# Crear un dataframe a partir de la información obtenida de la función summary, Parte 1.
resumen1 <- round(as.data.frame(apply(rotacion[c(2, 5, 13:14, 16, 18:19)], 2, summary)), digits = 2)
# Crear una presentación en tabla de la información del dataframe anteriormente creado, Parte 1.
kable_classic(kbl(resumen1, caption = "<center><b>Tabla 10. Resumen estadísitico de las variables numéricas, Parte 1</b></center>"), full_width = F)
| Edad | Distancia_Casa | Ingreso_Mensual | Trabajos_Anteriores | Porcentaje_aumento_salarial | Años_Experiencia | Capacitaciones | |
|---|---|---|---|---|---|---|---|
| Min. | 18.00 | 1.00 | 1009.00 | 0.00 | 11.00 | 0.00 | 0.0 |
| 1st Qu. | 30.00 | 2.00 | 2911.00 | 1.00 | 12.00 | 6.00 | 2.0 |
| Median | 36.00 | 7.00 | 4919.00 | 2.00 | 14.00 | 10.00 | 3.0 |
| Mean | 36.92 | 9.19 | 6502.93 | 2.69 | 15.21 | 11.28 | 2.8 |
| 3rd Qu. | 43.00 | 14.00 | 8379.00 | 4.00 | 18.00 | 15.00 | 3.0 |
| Max. | 60.00 | 29.00 | 19999.00 | 9.00 | 25.00 | 40.00 | 6.0 |
# Crear un dataframe a partir de la información obtenida de la función summary, Parte 2.
resumen2 <- round(as.data.frame(apply(rotacion[c(21:24)], 2, summary)), digits = 2)
# Crear una presentación en tabla de la información del dataframe anteriormente creado, Parte 2.
kable_classic(kbl(resumen2, caption = "<center><b>Tabla 11. Resumen estadísitico de las variables numéricas, Parte 2</b></center>"), full_width = F)
| Antigüedad | Antigüedad_Cargo | Años_ultima_promoción | Años_acargo_con_mismo_jefe | |
|---|---|---|---|---|
| Min. | 0.00 | 0.00 | 0.00 | 0.00 |
| 1st Qu. | 3.00 | 2.00 | 0.00 | 2.00 |
| Median | 5.00 | 3.00 | 1.00 | 3.00 |
| Mean | 7.01 | 4.23 | 2.19 | 4.12 |
| 3rd Qu. | 9.00 | 7.00 | 3.00 | 7.00 |
| Max. | 40.00 | 18.00 | 15.00 | 17.00 |
En este análisis la respuesta es binaria, si realiza o no realiza horas extras, lo que nos da como resultado el histograma es que la mayor parte de las personas de la empresa no realizan horas extras lo que es aproximadamente un 75% de los resultados de la variable.
#Histograma de Horas Extra
histHE = plot_ly(alpha = 0.6)
histHE = histHE %>% add_histogram(x = rotacion1$Horas_Extra, name = "he", marker = list(fill = "#9abaf9", color = "#FFD39B", opacity = 0.5, line = list(color = 'black', width = 1)))
histHE = histHE %>% layout(barmode = "overlay", title = "Histograma de Horas Extra")
histHE
La variable satisfacción laboral tiene resultados positivos en los resultados satisfecho y muy satisfecho ya que estos dos campos explican más del 60% de los resultados de la variable, en este caso Muy satisfecho el 31% y Satisfecho el 30%, importante tener presente que el campo Muy insatisfecho tiene un resultado ligeramente mayor al campo Satisfecho al cerrar con un peso de 19,7 % de Muy insatisfecho y 19% para insatisfecho.
#Histograma de Satisfacción Laboral
histSL = plot_ly(alpha = 0.6)
histSL = histSL %>% add_histogram(x = rotacion1$Satisfación_Laboral, name = "satis", marker = list(color = "grey", opacity = 0.5, line = list(color = 'black', width = 1)))
histSL = histSL %>% layout(barmode = "overlay", title = "Histograma de satisfacción Laboral")
histSL
Para la variable cargo se evidencia que el cargo de mayor presencia en la empresa es el de Ejecutivo de Ventas, seguido por el investigador científico y el técnico de laboratorio, más adelante se podrá hacer una verificación si la cantidad de personas en un cargo puede estar relacionada con la rotación en la empresa.
#Histograma de Cargo
hitgen = plot_ly(alpha = 0.6)
hitgen = hitgen %>% add_histogram(x = rotacion1$Cargo, name = "satis", marker = list(color = "green", opacity = 0.5, line = list(color = 'black', width = 1)))
hitgen = hitgen %>% layout(barmode = "overlay", title = "Histograma de Cargo")
hitgen
Podría resultar útil realizar algunas inferencias sobre la relación que tienen entre ellas, sín aún ahondar en la relación directa con la variables clase.
Un análisis previo de esta relación de variables numéricas nos indica que la mayoría de las variables presentan una relación positivia entre ellas, las cuales llega a ser moderada y en algunos casos alta (más de 0.70 puntos). Incluyendo variables como la antiguedad, antiguedad en el cargo, años con el mismo jefe, años de experiencia y el ingreso mensual.
# Calcular la matriz de correlación
cor_matrix = cor(select_if(rotacion1, is.numeric))
melted_cor_matrix = melt(cor_matrix)
ggplot(data = melted_cor_matrix, aes(Var1, Var2, fill = value)) +
geom_tile(color = "white") +
scale_fill_gradient2(low = "blue", high = "red", mid = "white",
midpoint = 0, limit = c(-1, 1), space = "Lab",
name="Correlación") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, vjust = 1, size = 8, hjust = 1)) +
coord_fixed() +
labs(title = "Heatmap de Correlación", x = "Variables", y = "Variables") +
geom_text(aes(label = round(value, 2)), size = 2.3)
De acuerdo con el diagrama de cajas se ve una diferencia significativa en los ingresos de las personas que rotan y las personas que no rotan, es así como el 50% de los ingresos de las personas que no rotan están en un rango que va desde los 3,210 y los 8,8834 a diferencia de lo que sucede con las personas que si rotan quedando el 50% entre 2,371 y 5,935 lo que es significativamente menor y puede darnos una explicación de los cargos que pueden rotar en la empresa de acuerdo con el salario que este tenga asignado. Existe una diferencia estadísticamente significativa entre los ingresos mensuales de los empleados que rotaron y los que no rotaron, siendo los ingresos mayores para aquellos que no han rotado.
#Boxplot Ingreso
boxplot_rotacion = plot_ly(rotacion1, x = ~Rotación, y = ~Ingreso_Mensual, type = "box", color = ~Rotación)
boxplot_rotacion = boxplot_rotacion %>% layout(title = "Distribución de Ingreso Mensual por Rotación",
xaxis = list(title = "Rotación"),
yaxis = list(title = "Ingreso Mensual"))
boxplot_rotacion
# Prueba t de Student
prueba_t = t.test(Ingreso_Mensual ~ Rotación, data = rotacion1)
prueba_t
##
## Welch Two Sample t-test
##
## data: Ingreso_Mensual by Rotación
## t = 7.4826, df = 412.74, p-value = 4.434e-13
## alternative hypothesis: true difference in means between group No and group Si is not equal to 0
## 95 percent confidence interval:
## 1508.244 2583.050
## sample estimates:
## mean in group No mean in group Si
## 6832.740 4787.093
La relación entre la rotación y la antigüedad en el cargo se ve principalmente en los que en promedio tienen 2 años y en dónde el 50% de esta relación de rotación está explicada entre los 0 y los 4 años, también se explica que a mayor antigüedad hay menor probabilidad de rotación.Existe una diferencia estadísticamente significativa entre los ingresos mensuales de los empleados que rotaron y los que no rotaron, siendo los ingresos mayores para aquellos que no han rotado.
# Boxplot para Antigüedad
boxplot_antiguedad = plot_ly(rotacion1, x = ~Rotación, y = ~Antigüedad_Cargo, type = "box", color = ~Rotación)
boxplot_antiguedad = boxplot_antiguedad %>% layout(title = "Distribución de Antigüedad por Rotación",
xaxis = list(title = "Rotación"),
yaxis = list(title = "Antigüedad"))
boxplot_antiguedad
# Prueba t para Antigüedad
prueba_t_antiguedad = t.test(Antigüedad_Cargo ~ Rotación, data = rotacion1)
prueba_t_antiguedad
##
## Welch Two Sample t-test
##
## data: Antigüedad_Cargo by Rotación
## t = 6.8471, df = 366.57, p-value = 3.187e-11
## alternative hypothesis: true difference in means between group No and group Si is not equal to 0
## 95 percent confidence interval:
## 1.127107 2.035355
## sample estimates:
## mean in group No mean in group Si
## 4.484185 2.902954
El gráfico de cajas entre “Rotación” y “Años_Experiencia” parece sugerir que es más probable que a menor cantidad de años de experiencia, se cambie de empleo, apoyando lo planteado en la hipótesis. Existe una diferencia estadísticamente significativa entre los ingresos mensuales de los empleados que rotaron y los que no rotaron, siendo los ingresos mayores para aquellos que no han rotado.
# Boxplot para Edad
boxplot_edad = plot_ly(rotacion1, x = ~Rotación, y = ~Años_Experiencia, type = "box", color = ~Rotación)
boxplot_edad = boxplot_edad %>% layout(title = "Distribución de Años de Experiencia por Rotación",
xaxis = list(title = "Rotación"),
yaxis = list(title = "Edad"))
boxplot_edad
# Prueba t para Edad
prueba_t_edad = t.test(Años_Experiencia ~ Rotación, data = rotacion1)
prueba_t_edad
##
## Welch Two Sample t-test
##
## data: Años_Experiencia by Rotación
## t = 7.0192, df = 350.88, p-value = 1.16e-11
## alternative hypothesis: true difference in means between group No and group Si is not equal to 0
## 95 percent confidence interval:
## 2.604401 4.632019
## sample estimates:
## mean in group No mean in group Si
## 11.862936 8.244726
Revisando el gráfico entre “Rotación” y “Horas_Extra”, las proporciones entre el sí y el no parecen sugerir que tener horas extras aumenta la probabilidad de cambiar de empleo, apoyando la hipótesis planteada inicialmente.
# Creamos la tabla resumen Horas Extras
tabla = rotacion1
resumen_rotacion_HE = tabla%>%
count(Rotación, Horas_Extra) %>%
group_by(Rotación) %>%
mutate(Percent = n / sum(n) * 100) %>%
ungroup()
# Creamos la gráfica
grafica_rotacion_HE = ggplot(resumen_rotacion_HE, aes(x = Rotación, y = Percent, fill = Horas_Extra)) +
geom_bar(stat = "identity", position = "dodge") +
geom_text(aes(label = paste0(sprintf("%.1f%%", Percent), " (", n, ")")), position = position_dodge(width = 0.9), vjust = -0.5, size = 3) +
labs(title = "Rotación y Horas Extra", x = "Rotación", y = "Porcentaje") +
theme_minimal() +
scale_fill_brewer(palette = "Set3") +
theme(legend.position = "bottom")
# Creamos la flextable
ft_rotacion_HE = flextable(resumen_rotacion_HE) %>%
theme_vanilla() %>%
autofit()
# Mostramos resultados
ft_rotacion_HE
Rotación | Horas_Extra | n | Percent |
|---|---|---|---|
No | No | 944 | 76.56123 |
No | Si | 289 | 23.43877 |
Si | No | 110 | 46.41350 |
Si | Si | 127 | 53.58650 |
grafica_rotacion_HE
En el análisis se puede identificar que existe una menor posibilidad de rotación cuando hay una mayor satisfacción laboral, donde la persona se siente a gusto con la labor que desempeña, y se ve una clara tendencia cuando el personal está muy insatisfecho hay una posibilidad mayor de rotación, se podría revisar especialmente el caso del personal que está satisfecho y que decide rotar.
# Tabla resumen de la variable Satisfacción Laboral
tabla = rotacion1
resumen_rotacion_SL = tabla%>%
count(Rotación, Satisfación_Laboral) %>%
group_by(Rotación) %>%
mutate(Percent = n / sum(n) * 100) %>%
ungroup()
# Creamos la gráfica
grafica_rotacion_SL <- ggplot(resumen_rotacion_SL, aes(x = Rotación, y = Percent, fill = Satisfación_Laboral)) +
geom_bar(stat = "identity", position = "dodge") +
geom_text(aes(label = paste0(sprintf("%.1f%%", Percent), " (", n, ")")), position = position_dodge(width = 0.9), vjust = -0.5, size = 3) +
labs(title = "Rotación y Satisfaccion Laboral", x = "Rotación", y = "Porcentaje") +
theme_minimal() +
scale_fill_brewer(palette = "Set2") +
theme(legend.position = "bottom")
# Creamos la flextable
ft_rotacion_SL = flextable(resumen_rotacion_SL) %>%
theme_vanilla() %>%
autofit()
# Mostramos resultados
ft_rotacion_SL
Rotación | Satisfación_Laboral | n | Percent |
|---|---|---|---|
No | Muy insatisfecho | 223 | 18.08597 |
No | Insatisfecho | 234 | 18.97810 |
No | Satisfecho | 369 | 29.92701 |
No | Muy satisfecho | 407 | 33.00892 |
Si | Muy insatisfecho | 66 | 27.84810 |
Si | Insatisfecho | 46 | 19.40928 |
Si | Satisfecho | 73 | 30.80169 |
Si | Muy satisfecho | 52 | 21.94093 |
grafica_rotacion_SL
Las gráficas muestran que los cargos con más empleados son Ejecutivo de Ventas e Investigador Científico, destacando que el primero tiene la mayor proporción de empleados que no han rotado (21.8%), aunque también presenta una considerable tasa de rotación (19.8%). En contraste, el cargo de Gerente registra la mayor rotación con un 24.1%. Cargos como Recursos Humanos y Representante de Ventas tienen pocos empleados y bajas tasas de rotación, lo que sugiere estabilidad en estas áreas.
# Creamos la tabla resumen variable Cargo
tabla = rotacion1
resumen_rotacion_SL = tabla%>%
count(Rotación, Cargo) %>%
group_by(Rotación) %>%
mutate(Percent = n / sum(n) * 100) %>%
ungroup()
# Creamos la gráfica
grafica_rotacion_SL = ggplot(resumen_rotacion_SL, aes(x = Rotación, y = Percent, fill = Cargo)) +
geom_bar(stat = "identity", position = "dodge") +
geom_text(aes(label = paste0(sprintf("%.1f%%", Percent), " (", n, ")")), position = position_dodge(width = 0.9), vjust = -0.5, size = 3) +
labs(title = "Rotación y Cargo", x = "Rotación", y = "Porcentaje") +
theme_minimal() +
scale_fill_brewer(palette = "Set2") +
theme(legend.position = "bottom")
# Creamos la flextable
ft_rotacion_SL = flextable(resumen_rotacion_SL) %>%
theme_vanilla() %>%
autofit()
# Mostramos resultados
ft_rotacion_SL
Rotación | Cargo | n | Percent |
|---|---|---|---|
No | Director_Investigación | 78 | 6.3260341 |
No | Director_Manofactura | 135 | 10.9489051 |
No | Ejecutivo_Ventas | 269 | 21.8167072 |
No | Gerente | 97 | 7.8669911 |
No | Investigador_Cientifico | 245 | 19.8702352 |
No | Recursos_Humanos | 40 | 3.2441200 |
No | Representante_Salud | 122 | 9.8945661 |
No | Representante_Ventas | 50 | 4.0551500 |
No | Tecnico_Laboratorio | 197 | 15.9772912 |
Si | Director_Investigación | 2 | 0.8438819 |
Si | Director_Manofactura | 10 | 4.2194093 |
Si | Ejecutivo_Ventas | 57 | 24.0506329 |
Si | Gerente | 5 | 2.1097046 |
Si | Investigador_Cientifico | 47 | 19.8312236 |
Si | Recursos_Humanos | 12 | 5.0632911 |
Si | Representante_Salud | 9 | 3.7974684 |
Si | Representante_Ventas | 33 | 13.9240506 |
Si | Tecnico_Laboratorio | 62 | 26.1603376 |
grafica_rotacion_SL
ggplot(rotacion1, aes(x=Cargo, fill=Rotación)) +
geom_bar(position="dodge") +
labs(x="Cargo", y="Conteo", title="Cargo por Estado de Rotación") +
scale_fill_brewer(palette="Pastel2") +
theme_minimal() +
theme(axis.text.x = element_text(angle=45, hjust=1, vjust=1))
A continuación, se realiza el analisis de la tabla del estadistico de pruebas t de las variables numéricas, en relación a la variable clase “rotación”. Se podría indicar que un p-valor bajo (generalmente menor a 0.05) indica que hay suficiente evidencia para rechazar la hipótesis nula, lo que sugiere que hay una diferencia significativa entre las medias de los grupos categorizados por Rotación. Mientras que un p-valor alto (mayor a 0.05) indica que no hay suficiente evidencia para rechazar la hipótesis nula, lo que significa que no se observa una diferencia significativa entre los grupos.
Las variables Edad, Distancia Casa, Ingreso Mensual, Años de Experiencia, Capacitaciones, Antiguedad, Antiguedad en el Cargo y Años a cargo del mismo jefe tienen una asociación significativa con la rotación (p-valor < 0.05), y sugiere una posibilidad de afectación importante en factores a considerar la rotación y abandono de los empleados.
Por su parte, variables como trabajos anteriores, porcentaje de aumento salarial y años desde la última promoción no parecen estar significativamente asociadas con la rotación (p-valor > 0.05) en este conjunto de datos.
# Nos aseguramos de que las variables categóricas y 'Rotación' están en el formato correcto
datos = rotacion1 %>%
mutate(Rotación = as.factor(Rotación),
`Viaje de Negocios` = as.factor(`Viaje de Negocios`),
Departamento = as.factor(Departamento),
Educación = as.factor(Educación),
Campo_Educación = as.factor(Campo_Educación),
Satisfacción_Ambiental = as.factor(Satisfacción_Ambiental),
Genero = as.factor(Genero),
Cargo = as.factor(Cargo),
Satisfación_Laboral = as.factor(Satisfación_Laboral),
Estado_Civil = as.factor(Estado_Civil),
Horas_Extra = as.factor(Horas_Extra),
Rendimiento_Laboral = as.factor(Rendimiento_Laboral),
Equilibrio_Trabajo_Vida = as.factor(Equilibrio_Trabajo_Vida))
# Lista de variables numéricas
variables_numericas = 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")
# Lista de variables categóricas
variables_categoricas = setdiff(names(datos), variables_numericas)
variables_categoricas = setdiff(variables_categoricas, "Rotación") # Excluir 'Rotación' de las categóricas
# Preparamos el dataframe para los resultados de las pruebas t
resultados_t_test = data.frame(Variable = character(),
Estadístico = numeric(),
P_valor = numeric(),
stringsAsFactors = FALSE)
# Análisis bivariado para variables numéricas y almacenamiento de resultados
for(variable in variables_numericas) {
resultado = t.test(as.formula(paste(variable, "~ Rotación")), data = datos)
resultados_t_test <- rbind(resultados_t_test,
data.frame(Variable = variable,
Estadístico = resultado$statistic,
P_valor = resultado$p.value))
}
# Creamos flextable para las variables numéricas
flextable_num = flextable(resultados_t_test) %>%
theme_vanilla() %>%
autofit()
# Mostramos el flextable de las variables numéricas
flextable_num
Variable | Estadístico | P_valor |
|---|---|---|
Edad | 5.8291495 | 0.0000000137124908200 |
Distancia_Casa | -2.8881831 | 0.0041365119715114137 |
Ingreso_Mensual | 7.4826216 | 0.0000000000004433589 |
Trabajos_Anteriores | -1.5746511 | 0.1163340260169765106 |
Porcentaje_aumento_salarial | 0.5042445 | 0.6144300580581378846 |
Años_Experiencia | 7.0191785 | 0.0000000000115981675 |
Capacitaciones | 2.3305223 | 0.0203637921269869300 |
Antigüedad | 5.2825961 | 0.0000002285905175273 |
Antigüedad_Cargo | 6.8470792 | 0.0000000000318739037 |
Años_ultima_promoción | 1.2879266 | 0.1986512827690630201 |
Años_acargo_con_mismo_jefe | 6.6333988 | 0.0000000001185021900 |
La siguiente tabla, mientras la prueba de Chi-cuadrado para las variables categóricas en relación a la variable clase “rotación”. Un p-valor bajo (menor a 0.05) indica que hay una asociación significativa entre la variable categórica y la variable de rotación mientras que un p-valor alto (mayor a 0.05) indica que no hay una asociación significativa.
Variables como viaje de negocios, departamento, satisfacción ambiental, cargo, satisfacción laboral, estado civil, horas extra, y equilibrio trabajo-vida están significativamente asociadas con la rotación. Por otra parte, variables como educación, campo de educación, género y rendimiento laboral no parecen estar significativamente relacionadas con la rotación.
# Preparamos el dataframe para las pruebas Chi-cuadrado
resultados_chi_cuadrado = data.frame(Variable = character(),
Chi_cuadrado = numeric(),
P_valor = numeric(),
stringsAsFactors = FALSE)
# Análisis bivariado para variables categóricas y almacenamiento de resultados
for(variable in variables_categoricas) {
tabla = table(datos[[variable]], datos$Rotación)
resultado = tryCatch({
chisq.test(tabla)
}, warning = function(w) {
return(list(statistic = NA, p.value = NA))
}, error = function(e) {
return(list(statistic = NA, p.value = NA))
})
resultados_chi_cuadrado <- rbind(resultados_chi_cuadrado,
data.frame(Variable = variable,
Chi_cuadrado = resultado$statistic,
P_valor = resultado$p.value))
}
# Creamos flextable para las variables categóricas
flextable_cat = flextable(resultados_chi_cuadrado)%>%
theme_vanilla() %>%
autofit()
# Mostramos el flextable de las variables categóricas
flextable_cat
Variable | Chi_cuadrado | P_valor |
|---|---|---|
Viaje de Negocios | 24.182414 | 0.000005608614476449930737363 |
Departamento | 10.796007 | 0.004525606574479631993845885 |
Educación | 3.073961 | 0.545525337656594833113388177 |
Campo_Educación | ||
Satisfacción_Ambiental | 22.503881 | 0.000051234689062894279171917 |
Genero | 1.116967 | 0.290572449028911661272900346 |
Cargo | 86.190254 | 0.000000000000002752481638051 |
Satisfación_Laboral | 17.505077 | 0.000556300451038755847110728 |
Estado_Civil | 46.163677 | 0.000000000094555110603408226 |
Horas_Extra | 87.564294 | 0.000000000000000000008158424 |
Rendimiento_Laboral | ||
Equilibrio_Trabajo_Vida | 16.325097 | 0.000972569884534883223603519 |
Lo anterior, permite confirmar las hipotesis formuladas anteriormente en la elección de las variables que segun la documentación y la estadistica relacionada con la rotación empresarial, pueden tener mayor relación con la decisión del empleado.
Como parte final de la preparación de los datos, convertimos las variables categóricas a factor para que el modelo de regresión logistica pueda funcionar correctamente. La funcion automáticamente realiza un proceso de subdivisión de los atributos por variable lo que conocemos como variable Dummie o proceso de One Hot Encoding.
# Convertir variables categóricas en factores
rotacion1$Rotación = as.factor(rotacion1$Rotación)
rotacion1$Cargo = as.factor(rotacion1$Cargo)
rotacion1$Horas_Extra = as.factor(rotacion1$Horas_Extra)
Se realiza la partición de los datos en un conjunto de entrenamiento (70%) y uno de prueba (30%) de manera aleatoria.
set.seed(115)
trainIndex = createDataPartition(rotacion1$Rotación, p = 0.7, list = FALSE)
trainData = rotacion1[trainIndex, ]
testData = rotacion1[-trainIndex, ]
Estimación del modelo con las variables elegidas: Ingreso_Mensual, Antigüedad, Edad, Horas_Extra, Satisfacción_Laboral y Cargo.
# Ajuste del modelo logístico
modelo_logistico <- glm(Rotación ~ Ingreso_Mensual + Antigüedad_Cargo + Años_Experiencia + Horas_Extra + Satisfación_Laboral + Cargo,
data = trainData, family = "binomial")
summary(modelo_logistico)
##
## Call:
## glm(formula = Rotación ~ Ingreso_Mensual + Antigüedad_Cargo +
## Años_Experiencia + Horas_Extra + Satisfación_Laboral +
## Cargo, family = "binomial", data = trainData)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -3.932e+00 1.085e+00 -3.624 0.000290 ***
## Ingreso_Mensual 9.188e-05 6.020e-05 1.526 0.126923
## Antigüedad_Cargo -7.887e-02 3.299e-02 -2.391 0.016825 *
## Años_Experiencia -2.707e-02 2.280e-02 -1.188 0.235003
## Horas_ExtraSi 1.455e+00 1.898e-01 7.668 1.74e-14 ***
## Satisfación_LaboralInsatisfecho -3.900e-01 2.857e-01 -1.365 0.172186
## Satisfación_LaboralSatisfecho -2.075e-01 2.527e-01 -0.821 0.411497
## Satisfación_LaboralMuy satisfecho -9.155e-01 2.699e-01 -3.392 0.000695 ***
## CargoDirector_Manofactura 1.211e+00 9.369e-01 1.292 0.196284
## CargoEjecutivo_Ventas 2.249e+00 8.862e-01 2.538 0.011144 *
## CargoGerente 1.665e-01 9.520e-01 0.175 0.861139
## CargoInvestigador_Cientifico 2.258e+00 9.995e-01 2.259 0.023858 *
## CargoRecursos_Humanos 2.616e+00 1.029e+00 2.543 0.010981 *
## CargoRepresentante_Salud 1.316e+00 9.386e-01 1.402 0.160839
## CargoRepresentante_Ventas 3.599e+00 1.030e+00 3.493 0.000477 ***
## CargoTecnico_Laboratorio 2.790e+00 9.941e-01 2.806 0.005013 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 909.69 on 1029 degrees of freedom
## Residual deviance: 769.94 on 1014 degrees of freedom
## AIC: 801.94
##
## Number of Fisher Scoring iterations: 6
Los coeficientes (estimates) representan el logaritmo de las odds ratios (odds logaritmicas) para cada variable. Un valor positivo implica que un aumento en esa variable aumenta la probabilidad de rotación (para variables continuas), y un valor negativo indica que un aumento en esa variable reduce la probabilidad de rotación. El error estandar mide la precisión de los coeficientes estimados. Un error estándar grande sugiere que la estimación es menos precisa. Los valores z y Pr(>|z|) indican si la variable es estadísticamente significativa para predecir la rotación.
Las Horas_Extra, Alta Satisfacción Laboral y el Cargos de Ventas son las variables más significativas, lo que indica que los empleados que trabajan horas extras y tienen un cargo en ventas son más propensos a rotar, mientras que una satisfacción laboral alta en menos propenso a la rotación.
Varios cargos como Ejecutivo de Ventas, Investigador Científico, Recursos Humanos, Representante de Ventas, y Técnico de Laboratorio, están significativamente asociados con una mayor probabilidad de rotación.
Las variables Ingreso Mensual y Años de Experiencia parecen no ser factores significativos en la rotación de empleados para este modelo y conjunto de datos.
Sin embargo para realizar una completa interpretación del modelo, necesitamos convertir los coeficientes a “odds”, también llamados “razones de probabilidad” ó “oportunidad”, así que procedemos con ello.
# Capturar coeficientes del modelo y transformarlos en oportunidad
Oportunidad = summary(modelo_logistico)$coefficients[-c(1), "Estimate"]
Oportunidad = as.data.frame(round(exp(Oportunidad),3))
# Crear una tabla de odds por cada variable
kbl(Oportunidad, caption = "<center><b>Tabla 13. Oportunidad relativa de cada Variable</b></center>", col.names=c("Variable","Oportunidad"))%>%
kable_classic(full_width = F)
| Variable | Oportunidad |
|---|---|
| Ingreso_Mensual | 1.000 |
| Antigüedad_Cargo | 0.924 |
| Años_Experiencia | 0.973 |
| Horas_ExtraSi | 4.286 |
| Satisfación_LaboralInsatisfecho | 0.677 |
| Satisfación_LaboralSatisfecho | 0.813 |
| Satisfación_LaboralMuy satisfecho | 0.400 |
| CargoDirector_Manofactura | 3.356 |
| CargoEjecutivo_Ventas | 9.482 |
| CargoGerente | 1.181 |
| CargoInvestigador_Cientifico | 9.567 |
| CargoRecursos_Humanos | 13.682 |
| CargoRepresentante_Salud | 3.729 |
| CargoRepresentante_Ventas | 36.550 |
| CargoTecnico_Laboratorio | 16.274 |
Ahora si podemos proceder a interpretar las variables usando la oportunidad relativa de cada una de ellas, junto al análisis del p-valor.
“Ingreso Mensual” Su coeficiente es positivo indicando que a más ingreso mayor probabilidad de cambiar de empleo, la oportunidad de rotación es cerca de 1 veces por cada unidad adicional. Sin embargo, en terminos generales, la variable del ingreso no aporta en el calculo de la probabilidad de rotación.
“Años_Experiencia” Su coeficiente es negativo indicando que a más experiencia menor probabilidad de cambiar de empleo, la oportunidad de rotación disminuye en cerca de 0.973 veces por cada año de experiencia adicional.
“Antiguedad en el Cargo”: Su coeficiente es negativo indicando que a mayor antiguedad en el cargo, menor es la probabilidad de rotación, la oportunidad de rotación es de 0.924 veces por cada año de antiguedad.
“Horas_Extra” muestra la evaluación de la categoría “Si”, y basado en su p-valor, se podría decir que es la más representativa del modelo, el análisis bivariado sugería su importancia, y este análisis refleja la situación. Su coeficiente es positivo indicando que la presencia de horas extras aumenta la probabilidad de cambiar de empleo. La oportunidad de rotación de quienes trabajan horas extras aumenta en cerca de 4.286 veces con respecto a quienes no trabajan horas extras.
“Satisfación_Laboral” muestra la evaluación de las categorías de la 2 a la 4, de estas solo la 4 se muestra como relevante, revisando el análisis bivariado las categorías de los extremos, 1 y 4, se mostraban como evidencia de la relación de esta categoría con la rotación. El coeficiente de la categoría 4 es negativo indicando que con el nivel más alto de satisfacción laboral, la probabilidad de cambiar de empleo es menor. La oportunidad de rotación de quienes tienen el mayor nivel de satisfacción laboral disminuye en cerca de 0,6 veces (1 - 0.400) con respecto a quienes tienen el menor nivel de satisfacción laboral.
En la variable “Cargo”, los representantes de ventas y tecnico de laboratorio son las más representativa del modelo, Sus coeficientes son positivo indicando que es mas probable que roten respecto a la categoría base. Todos los cargos evaluados tienen una mayor probailidad de rotación en relación al director de investigación, lllegando al 36.55 (representante de ventas) y 16.274 (tecnico de laboratorio)
Ahora, se realizará un ejercicio de predicción sobre el conjunto de datos utilizando el modelo de regresión logística, con el objetivo de estimar la probabilidad de rotación de los empleados.
# Predecir probabilidades con el modelo ajustado
prediccion1 = predict(modelo_logistico, testData, type = "response")
# Convertir las predicciones en valores binarios (umbral 0.3)
predicciones_binarias = ifelse(prediccion1 > 0.3, "Sí", "No")
# Comparar las predicciones con las observaciones reales
resp = table(testData$Rotación, predicciones_binarias, dnn = c("observaciones", "predicciones"))
resp
## predicciones
## observaciones No Sí
## No 322 47
## Si 37 34
Exactitud (Accuracy): El modelo fue capaz de predecir correctamente una alta proporción de rotación de los casos de los casos (80.90%).
Tasa de error: El modelo tuvo una tasa de error del 19.09%, lo que significa que aproximadamente en 1 de cada 4 casos el modelo cometió un error en la predicción.
Sensibilidad (Recall): El modelo fue capaz de identificar correctamente el 47.88% de los casos en los que realmente hubo rotación. Esto quiere decir que se está perdiendo una parte significativa de los casos de rotación real.
Especificidad: El modelo identificó correctamente el 87.26% de los casos en los que no hubo rotación. Lo cual indica que el modelo es bastante bueno en predecir correctamente cuando un empleado no rotará.
Precisión (Precision): De todas las predicciones positivas realizadas por el modelo, el 41.97% fueron correctas. Este valor podría ser bajo y representar que el modelo está haciendo muchas predicciones de rotación incorrectas.
Valor predictivo negativo: De todas las predicciones de “No rotación” el 89.69% fueron correctas. Este valor alto indica que el modelo es muy bueno prediciendo correctamente los casos en los que no hubo rotación.
F1-Score: Esta media armónica entre la precisión y la sensibilidad, indica un valor de 0.4473 indicando que el modelo tiene un rendimiento moderado en la predicción de la clase “Rotación = Sí”.
# Extraer valores de la matriz de confusión
VP = resp[2, 2] # Verdaderos positivos (Rotación 'Sí' correctamente clasificada)
VN = resp[1, 1] # Verdaderos negativos (Rotación 'No' correctamente clasificada)
FP = resp[1, 2] # Falsos positivos (Rotación 'No' clasificada como 'Sí')
FN = resp[2, 1] # Falsos negativos (Rotación 'Sí' clasificada como 'No')
# Calcular las métricas
exactitud = (VP + VN) / sum(resp) # Exactitud (accuracy)
tasa_error = (FP + FN) / sum(resp) # Tasa de error
sensibilidad = VP / (VP + FN) # Sensibilidad (recall o Tasa de verdaderos positivos)
especificidad = VN / (VN + FP) # Especificidad (Tasa de verdaderos negativos)
precision = VP / (VP + FP) # Precisión
valor_precision_negativo = VN / (VN + FN) # Valor predictivo negativo
f1_score = 2 * ((precision * sensibilidad) / (precision + sensibilidad)) # F1-Score
# Crear un data frame con las métricas
df = data.frame(
Metrica = c("Exactitud", "Tasa de error", "Sensibilidad", "Especificidad",
"Precisión", "Valor predictivo negativo", "F1-Score"),
Valor = c(exactitud, tasa_error, sensibilidad, especificidad, precision,
valor_precision_negativo, f1_score)
)
# Crear la tabla formateada con kable y kableExtra
tabla_formateada = df %>%
kable(format = "html", align = "lrc", col.names = c("Métrica", "Valor")) %>%
kable_styling(bootstrap_options = "striped", full_width = FALSE) %>%
add_header_above(c(" " = 1, "Métricas del Modelo Logístico" = 1)) %>%
column_spec(1, width = "300px") %>% # Ampliar el ancho de la primera columna
column_spec(2, width = "150px") # Ampliar el ancho de la segunda columna
tabla_formateada
| Métrica | Valor |
|---|---|
| Exactitud | 0.8090909 |
| Tasa de error | 0.1909091 |
| Sensibilidad | 0.4788732 |
| Especificidad | 0.8726287 |
| Precisión | 0.4197531 |
| Valor predictivo negativo | 0.8969359 |
| F1-Score | 0.4473684 |
Un AUC de 0.81 indica un buen rendimiento del modelo. En general, significa que hay un 81% de probabilidad de que el modelo clasifique correctamente un par aleatorio de observaciones, donde una pertenece a la clase ‘Sí’ y otra a la clase ‘No’.
# Calcular la curva ROC y AUC
roc_curve = roc(testData$Rotación, as.numeric(prediccion1))
auc_value = auc(roc_curve)
# Graficar la curva ROC
plot(roc_curve, main = paste("Curva ROC - AUC:", round(auc_value, 2)))
Consideraciones generales del ejercicio.
Una de las primeras observaciones es que la base de datos está desbalanceada, por una parte hay un índice de 1233 y 237, para la rotación negativa (“No”) y positiva (“Si”), respectivamente. Aplicar técnicas de balanceo de clases, como sobremuestreo de la clase minoritaria (rotación) o submuestreo de la clase mayoritaria. Quiza para un mejor rendimiento del modelo, se puede sugerir probar otros algoritmos o realizar ajuste de hiperparámetros.
A partir de los resultados obtenidos en las distintas métricas de análisis de datos realizados a través del modelo de regresión logística podemos realizar las siguientes conclusiones y recomendaciones para disminuir la rotación laboral:
Desarrollar estrategias para mitigar o desincentivar la necesidad de horas extras en el personal, revisando aquellos cargos que tienen tendencia a tener una mayor carga laboral y en los casos en los que es necesario el uso de horas extras buscar compensaciones más allá de lo legal y salarial, para mantener la salud y motivación del personal.
Establecer programas periódicos de evaluación de la satisfacción laboral del personal, además de mecanismos de propuestas de sugerencias y reclamos para mejorar el entorno laboral y relacional, de forma que pueda escucharse la voz del personal y generar estrategias concretas para dichas mejoras y permitir que el personal mantenga un buen nivel de motivación para continuar su desarrollo profesional y personal. También puede ser útil generar programas de reconocimiento al personal que destaque por sus contribuciones a la organización, de manera que el personal pueda sentirse respaldado por la organización.
Es importante para los cargos de técnico de laboratorio y el cargo de representante de ventas prestar atención especial ya que son los que tienen mayor probabilidad de rotación, se propone validar que el personal especialmente en estos cargos cuenten con todas las herramientas para desarrollar su trabajo, validar cargas de trabajo, horarios y desplazamientos y finalmente hacer un estudio de mercado que permita garantizar que la compensación que tienen estos cargos es acorde con lo que se ofrece en el mercado.