1 Introducción

Contexto: La deserción laboral representa un problema enorme para las organizaciones actuales, es por eso que mediante el presente informe con información de dataset IBM HR Analytics se identificaran patrones demográficos y laborales que influyen en la decisión de los empleados de abandonar la empresa, utilizando la estadistica como herramienta teorica.

Objetivo General: Analizar los factores que inciden en la deserción laboral mediante técnicas de probabilidad e inferencia estadística.

#Librerias a utilizar

library(tidyverse)
library(naniar)
library(ggplot2)
library(dplyr)
library(knitr)
library(kableExtra)
library(tibble)
#carga del dataset
df <- read_csv("input/WA_Fn-UseC_-HR-Employee-Attrition.csv")

2 Tareas

2.1 Tarea 1 — Exploración inicial

a) Reporte de dimesiones

glimpse(df)
## Rows: 1,470
## Columns: 35
## $ Age                      <dbl> 41, 49, 37, 33, 27, 32, 59, 30, 38, 36, 35, 2…
## $ Attrition                <chr> "Yes", "No", "Yes", "No", "No", "No", "No", "…
## $ BusinessTravel           <chr> "Travel_Rarely", "Travel_Frequently", "Travel…
## $ DailyRate                <dbl> 1102, 279, 1373, 1392, 591, 1005, 1324, 1358,…
## $ Department               <chr> "Sales", "Research & Development", "Research …
## $ DistanceFromHome         <dbl> 1, 8, 2, 3, 2, 2, 3, 24, 23, 27, 16, 15, 26, …
## $ Education                <dbl> 2, 1, 2, 4, 1, 2, 3, 1, 3, 3, 3, 2, 1, 2, 3, …
## $ EducationField           <chr> "Life Sciences", "Life Sciences", "Other", "L…
## $ EmployeeCount            <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …
## $ EmployeeNumber           <dbl> 1, 2, 4, 5, 7, 8, 10, 11, 12, 13, 14, 15, 16,…
## $ EnvironmentSatisfaction  <dbl> 2, 3, 4, 4, 1, 4, 3, 4, 4, 3, 1, 4, 1, 2, 3, …
## $ Gender                   <chr> "Female", "Male", "Male", "Female", "Male", "…
## $ HourlyRate               <dbl> 94, 61, 92, 56, 40, 79, 81, 67, 44, 94, 84, 4…
## $ JobInvolvement           <dbl> 3, 2, 2, 3, 3, 3, 4, 3, 2, 3, 4, 2, 3, 3, 2, …
## $ JobLevel                 <dbl> 2, 2, 1, 1, 1, 1, 1, 1, 3, 2, 1, 2, 1, 1, 1, …
## $ JobRole                  <chr> "Sales Executive", "Research Scientist", "Lab…
## $ JobSatisfaction          <dbl> 4, 2, 3, 3, 2, 4, 1, 3, 3, 3, 2, 3, 3, 4, 3, …
## $ MaritalStatus            <chr> "Single", "Married", "Single", "Married", "Ma…
## $ MonthlyIncome            <dbl> 5993, 5130, 2090, 2909, 3468, 3068, 2670, 269…
## $ MonthlyRate              <dbl> 19479, 24907, 2396, 23159, 16632, 11864, 9964…
## $ NumCompaniesWorked       <dbl> 8, 1, 6, 1, 9, 0, 4, 1, 0, 6, 0, 0, 1, 0, 5, …
## $ Over18                   <chr> "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", …
## $ OverTime                 <chr> "Yes", "No", "Yes", "Yes", "No", "No", "Yes",…
## $ PercentSalaryHike        <dbl> 11, 23, 15, 11, 12, 13, 20, 22, 21, 13, 13, 1…
## $ PerformanceRating        <dbl> 3, 4, 3, 3, 3, 3, 4, 4, 4, 3, 3, 3, 3, 3, 3, …
## $ RelationshipSatisfaction <dbl> 1, 4, 2, 3, 4, 3, 1, 2, 2, 2, 3, 4, 4, 3, 2, …
## $ StandardHours            <dbl> 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 8…
## $ StockOptionLevel         <dbl> 0, 1, 0, 0, 1, 0, 3, 1, 0, 2, 1, 0, 1, 1, 0, …
## $ TotalWorkingYears        <dbl> 8, 10, 7, 8, 6, 8, 12, 1, 10, 17, 6, 10, 5, 3…
## $ TrainingTimesLastYear    <dbl> 0, 3, 3, 3, 3, 2, 3, 2, 2, 3, 5, 3, 1, 2, 4, …
## $ WorkLifeBalance          <dbl> 1, 3, 3, 3, 3, 2, 2, 3, 3, 2, 3, 3, 2, 3, 3, …
## $ YearsAtCompany           <dbl> 6, 10, 0, 8, 2, 7, 1, 1, 9, 7, 5, 9, 5, 2, 4,…
## $ YearsInCurrentRole       <dbl> 4, 7, 0, 7, 2, 7, 0, 0, 7, 7, 4, 5, 2, 2, 2, …
## $ YearsSinceLastPromotion  <dbl> 0, 1, 0, 3, 2, 3, 0, 0, 1, 7, 0, 0, 4, 1, 0, …
## $ YearsWithCurrManager     <dbl> 5, 7, 0, 0, 2, 6, 0, 0, 8, 7, 3, 8, 3, 2, 3, …
cat("El dataset contiene", nrow(df), "filas y", ncol(df), "columnas.")
## El dataset contiene 1470 filas y 35 columnas.
table(sapply(df, class))
## 
## character   numeric 
##         9        26
df_tra <- df %>%
  mutate(
    Attrition = as.factor(Attrition),
    Department = as.factor(Department),
    OverTime = as.factor(OverTime),
    BusinessTravel = as.factor(BusinessTravel),
    EducationField = as.factor(EducationField),
    JobRole = as.factor(JobRole),
    MaritalStatus = as.factor(MaritalStatus),
    Gender = as.factor(Gender)
  )

Dimensiones: El dataset contiene, 35 variables con un total de 1470 registros los cuales se distribuye en 9 variables character de las cuales 8 se transforman en factor como buena practica, las restantes 26 son valores númericos.

b) Frecuencias absolutas y relativas de Attrition.

tabla_base <- table(df_tra$Attrition)

distribucion <- data.frame(
  Absoluta = as.vector(tabla_base),
  Relativa = as.vector(prop.table(tabla_base)),
  Porcentaje = paste0(round(as.vector(prop.table(tabla_base)) * 100, 2), "%"),
  row.names = names(tabla_base)
)

df_resumen <- df_tra %>%
  count(Attrition) %>%
  mutate(porcentaje = n / sum(n),
         etiqueta = paste0("n=", n, "\n(", round(porcentaje*100, 1), "%)"))

distribucion %>%
  kable(
    caption = "Tabla 1: Distribución de Frecuencias de Attrition",
    col.names = c("Frec. Absoluta (n)", "Frec. Relativa", "Porcentaje (%)"),
    align = "ccc"
  ) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"), 
    full_width = F, 
    position = "center"
  )
Tabla 1: Distribución de Frecuencias de Attrition
Frec. Absoluta (n) Frec. Relativa Porcentaje (%)
No 1233 0.8387755 83.88%
Yes 237 0.1612245 16.12%
ggplot(df_resumen, aes(x = "Dataset", y = porcentaje, fill = Attrition)) +
  geom_bar(stat = "identity", width = 0.4) +
 
  geom_text(aes(label = etiqueta), 
            position = position_stack(vjust = 0.5), 
            color = "white", fontface = "bold", size = 5) +
  scale_y_continuous(labels = scales::percent) +
  theme_minimal() +
  labs(title = "Distribución de Attrition",
       subtitle = "Frecuencias Absolutas (n) y Relativas (%)",
       x = "", 
       y = "Proporción del Total") +
  scale_fill_manual(values = c("No" = "#2E86AB", "Yes" = "#D90368"))

c) Frecuencias absolutas y relativas de Attrition. RTA // La distribución de la variable Attrition se encuentra fuertemente desbalanceada. Como se evidencia en los resultados anteriores, el 83.88% de los empleados permanecen en la compañía, mientras que solo el 16.12% corresponden a casos de deserción.

Para un proyecto de clasificación, este desbalanceo representa un desafío crítico ya que un un modelo básico podría alcanzar una alta precisión (accuracy) simplemente prediciendo siempre la clase mayoritaria (“No”), ignorando la clase de interés (“Yes”).

2.2 Tarea 2 —Tabla de contingencia y probabilidades: Attrition vs. OverTime

a) Tabla de contingencia entre OverTime VS Attrition

tabla_limpia <- table(df_tra$OverTime, df_tra$Attrition)
tabla_limpia <- addmargins(tabla_limpia)
tabla_limpia_df <- as.data.frame.matrix(tabla_limpia)

rownames(tabla_limpia_df) <- c("No realiza horas extra", "Realiza horas extra", "Total")
colnames(tabla_limpia_df) <- c("Attrition = No", "Attrition = Yes", "Total")

tabla_limpia_df %>%
  rownames_to_column(var = "Condición") %>%
  kable(
    caption = "Tabla 2: Contingencia entre Horas Extra y Deserción",
    col.names = c("Condición", "ATT = No", "ATT =  Yes", "Total"),
    align = c("l", "c", "c", "c"),
    booktabs = TRUE
  ) %>%
  add_header_above(c(" " = 1, "Estado de Attrition" = 2, " " = 1), bold = TRUE) %>%
  column_spec(1, width = "1%em") %>%
  column_spec(2:4, width = "7em") %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"),
    full_width = FALSE,
    position = "center"
  )
Tabla 2: Contingencia entre Horas Extra y Deserción
Estado de Attrition
Condición ATT = No ATT = Yes Total
No realiza horas extra 944 110 1054
Realiza horas extra 289 127 416
Total 1233 237 1470

b) Tabla de tabla de probabilidades conjuntas

tabla_conj_raw <- prop.table(table(df_tra$OverTime, df_tra$Attrition))
tabla_conj_marg <- addmargins(tabla_conj_raw)
tabla_conj_marg_df <- as.data.frame.matrix(tabla_conj_marg)

rownames(tabla_conj_marg_df) <- c("OT = No", "OT = Yes", "Marginal col.")
colnames(tabla_conj_marg_df) <- c("ATT = No", "ATT = Yes", "Marginal Fila")

tabla_conj_marg_df %>%
  rownames_to_column(var = "Estado") %>%
  kable(
    caption = "Tabla 3: Probabilidades Conjuntas y Marginales (Distribución Relativa)",
    digits = 4,
    align = c("l", "c", "c", "c"),
    booktabs = TRUE
  ) %>%
  column_spec(1, width = "12em") %>%
  column_spec(2:4, width = "10em") %>%
  kable_styling(
    bootstrap_options = c("striped", "bordered", "hover", "condensed"),
    full_width = FALSE,
    position = "center"
  )
Tabla 3: Probabilidades Conjuntas y Marginales (Distribución Relativa)
Estado ATT = No ATT = Yes Marginal Fila
OT = No 0.6422 0.0748 0.717
OT = Yes 0.1966 0.0864 0.283
Marginal col.  0.8388 0.1612 1.000
  • El 71.7% de los empleados no realiza horas extra, mientras que el 28.3% sí las realiza.
  • El 83.88% de los empleados permanece en la organización, mientras que el 16.12% deserta.

c) probabilidades marginales

Probabilidad de deserción según las horas extra

tabla_fila <- prop.table(table(df_tra$OverTime, df_tra$Attrition), margin = 1)
tabla_fila_df <- as.data.frame.matrix(tabla_fila)
tabla_fila_df$`Total fila` <- rowSums(tabla_fila_df)

rownames(tabla_fila_df) <- c("OT = No", "OT = Yes")
colnames(tabla_fila_df)[1:2] <- c("ATT = No", "ATT = Yes")

tabla_fila_df %>%
  rownames_to_column("Estado") %>%
  kable(
    caption = "Tabla 4: Probabilidades condicionales por fila",
    digits = 4,
    align = c("l","c","c","c")
  ) %>%
  column_spec(1, width = "12em") %>%
  column_spec(2:3, width = "10em") %>%
  kable_styling(
    bootstrap_options = c("striped","hover","condensed"),
    full_width = FALSE,
    position = "center"
  )
Tabla 4: Probabilidades condicionales por fila
Estado ATT = No ATT = Yes Total fila
OT = No 0.8956 0.1044 1
OT = Yes 0.6947 0.3053 1

Para empleados que no realizan horas extra:

  • Probabilidad de permanecer: 89.56%

  • Probabilidad de desertar: 10.44%

Para empleados que sí realizan horas extra:

  • Probabilidad de permanecer: 69.47%

  • Probabilidad de desertar: 30.53%

Conclusión La probabilidad de deserción es considerablemente mayor entre los empleados que realizan horas extra.

Proporción de empleados con horas extra dentro de cada grupo de deserción

tabla_conj_raw <- prop.table(table(df_tra$OverTime, df_tra$Attrition), margin = 2)
tabla_conj_df <- as.data.frame.matrix(tabla_conj_raw)
tabla_conj_df["Totales", ] <- colSums(tabla_conj_df)

rownames(tabla_conj_df)[1:2] <- c("OT = No", "OT = Yes")
colnames(tabla_conj_df) <- c("ATT = No", "ATT = Yes")

tabla_conj_df %>%
  rownames_to_column(var = "Estado") %>%
  kable(
    caption = "Tabla 5: Probabilidades condicionales por columna",
    digits = 4,
    align = c("l", "c", "c"),
    booktabs = TRUE
  ) %>%
  column_spec(1, width = "12em") %>%
  column_spec(2:3, width = "10em") %>%
  kable_styling(
    bootstrap_options = c("striped", "bordered", "hover", "condensed"),
    full_width = FALSE,
    position = "center"
  )
Tabla 5: Probabilidades condicionales por columna
Estado ATT = No ATT = Yes
OT = No 0.7656 0.4641
OT = Yes 0.2344 0.5359
Totales 1.0000 1.0000

Para empleados que no desertan:

  • 76.56% no realiza horas extra

  • 23.44% sí realiza horas extra

Para empleados que sí desertan:

  • 46.41% no realiza horas extra

  • 53.59% sí realiza horas extra

Conclusión Más de la mitad de los empleados que desertan realizan horas extra.

d) Calcule las probabilidades condicionales P(Att=Yes | OT=Yes) y P(Att=Yes | OT=No) usando la definición. Compárelas con P(Att=Yes) y concluya: ¿las horas extra influyen en la deserción?

  • P(ATT = Yes) El 16.12% deserta.
  • P(ATT = Yes | OT = No) El 10.44% deserta dado que no hace horas extra.
  • P(ATT = Yes | OT = Yes) El 30.53%% deserta dado que sí hace horas extra.

Esto indica que las horas extra influyen significativamente en la deserción laboral, ya que la probabilidad de que un colaborador deserte al hacer horas extras es considerablemente mayor, esta deducido gracias a la información de la tabla 4.

e) Evalúe si Attrition y OverTime son independientes verificando si P(A ∩ B) = P(A) · P(B) para alguna combinación.

Se debe tomar un caso puntual para cada conjunto A y B:

  • A: ATT = Yes
  • B: OT = Yes

P(A∩B)=0,0864 <- Esto según la Tabla 3.

Tomando la misma Tabla 3 se observa: P(A) = 0,1612 <- Según nuestra probabilidad marginal de ATT = Yes en la tabla 3. P(B) = 0,283 <- Según nuestra probabilidad marginal de OT = Yes en la tabla 3.

P(A)⋅P(B) 0.1612 × 0.283 = 0.0456

0.0864 != 0.0456

Conclusión Dado que estas variables no cumplen la condición de P(A ∩ B) = P(A) · P(B) se consideran variables dependientes, demostrando así que entre las horas extras y la deserción existe una relación.

2.3 Tarea 3 —Segunda tabla: Department vs. Attrition

a) Construya la tabla de contingencia entre Department y Attrition con frecuencias absolutas y marginales.

tabla_dept <- table(df_tra$Department, df_tra$Attrition)
tabla_dept_marg <- addmargins(tabla_dept)
tabla_dept_df <- as.data.frame.matrix(tabla_dept_marg)

colnames(tabla_dept_df) <- c("ATT = No", "ATT = Yes", "Total")
tabla_dept_df %>%
  rownames_to_column(var = "Department") %>%
  kable(
    caption = "Tabla 6: Contingencia entre Department y Attrition (Frecuencias absolutas)",
    align = c("l","c","c","c"),
    booktabs = TRUE
  ) %>%
  add_header_above(c(" " = 1, "Estado de Attrition" = 2, " " = 1), bold = TRUE) %>%
  column_spec(1, width = "14em") %>%
  column_spec(2:4, width = "7em") %>%
  kable_styling(
    bootstrap_options = c("striped","hover","condensed"),
    full_width = FALSE,
    position = "center"
  )
Tabla 6: Contingencia entre Department y Attrition (Frecuencias absolutas)
Estado de Attrition
Department ATT = No ATT = Yes Total
Human Resources 51 12 63
Research & Development 828 133 961
Sales 354 92 446
Sum 1233 237 1470

b) Tablas de probabilidades conjuntas, marginales y condicionales

tabla_conj_raw_dep <- prop.table(table(df_tra$Department, df_tra$Attrition))
tabla_conj_marg_dep <- addmargins(tabla_conj_raw_dep)
tabla_conj_marg_df_dep <- as.data.frame.matrix(tabla_conj_marg_dep)


colnames(tabla_conj_marg_df_dep) <- c("ATT = No", "ATT = Yes", "Marginal Fila")

tabla_conj_marg_df_dep %>%
  rownames_to_column(var = "Departamento") %>%
  kable(
    caption = "Tabla 7: Probabilidades Conjuntas y Marginales por departamento (Distribución Relativa)",
    digits = 4,
    align = c("l", "c", "c", "c"),
    booktabs = TRUE
  ) %>%
  column_spec(1, width = "12em") %>%
  column_spec(2:4, width = "10em") %>%
  kable_styling(
    bootstrap_options = c("striped", "bordered", "hover", "condensed"),
    full_width = FALSE,
    position = "center"
  )
Tabla 7: Probabilidades Conjuntas y Marginales por departamento (Distribución Relativa)
Departamento ATT = No ATT = Yes Marginal Fila
Human Resources 0.0347 0.0082 0.0429
Research & Development 0.5633 0.0905 0.6537
Sales 0.2408 0.0626 0.3034
Sum 0.8388 0.1612 1.0000

Probabilidad de deserción según departamento

tabla_fila_dep <- prop.table(table(df_tra$Department, df_tra$Attrition), margin = 1)
tabla_fila_def_df <- as.data.frame.matrix(tabla_fila_dep)
tabla_fila_def_df$`Total fila` <- rowSums(tabla_fila_def_df)


colnames(tabla_fila_df)[1:2] <- c("ATT = No", "ATT = Yes")

tabla_fila_def_df %>%
  rownames_to_column("Departamento") %>%
  kable(
    caption = "Tabla 8: Probabilidades condicionales por deaprtamento",
    digits = 4,
    align = c("l","c","c","c")
  ) %>%
  column_spec(1, width = "12em") %>%
  column_spec(2:3, width = "10em") %>%
  kable_styling(
    bootstrap_options = c("striped","hover","condensed"),
    full_width = FALSE,
    position = "center"
  )
Tabla 8: Probabilidades condicionales por deaprtamento
Departamento No Yes Total fila
Human Resources 0.8095 0.1905 1
Research & Development 0.8616 0.1384 1
Sales 0.7937 0.2063 1

Para empleados de Human Resources:

  • Probabilidad de desertar: 19,05%

Para empleados de Research & Development:

  • Probabilidad de desertar: 13,84%

Para empleados de Sales:

  • Probabilidad de desertar: 20,63%

Proporción de empleados por departamento dentro de cada grupo de deserción

tabla_conj_dep <- prop.table(table(df_tra$Department, df_tra$Attrition), margin = 2)
tabla_conj_df_dep <- as.data.frame.matrix(tabla_conj_dep)
tabla_conj_df_dep["Totales", ] <- colSums(tabla_conj_df_dep)

colnames(tabla_conj_df_dep) <- c("ATT = No", "ATT = Yes")

tabla_conj_df_dep %>%
  rownames_to_column(var = "Departamento") %>%
  kable(
    caption = "Tabla 5: Probabilidades condicionales por columna",
    digits = 4,
    align = c("l", "c", "c"),
    booktabs = TRUE
  ) %>%
  column_spec(1, width = "12em") %>%
  column_spec(2:3, width = "10em") %>%
  kable_styling(
    bootstrap_options = c("striped", "bordered", "hover", "condensed"),
    full_width = FALSE,
    position = "center"
  )
Tabla 5: Probabilidades condicionales por columna
Departamento ATT = No ATT = Yes
Human Resources 0.0414 0.0506
Research & Development 0.6715 0.5612
Sales 0.2871 0.3882
Totales 1.0000 1.0000

Para empleados que sí desertan:

  • 5,06% es de Human Resources

  • 56,12% es de Research & Development

  • 38,82% es de Sales