library(tidyverse)
library(caret)
library(scales)
library(class)
library(kknn)
library(rpart)
library(rpart.plot)
library(C50)
library(e1071)
library(naivebayes)En esta trabajo estaremos trabajando con el método de clasificación supervisada con el fin de identificar qué factores mantienen a los empleados en la empresa y cuales los impulsa a irse. Estaremos utilizando la base de datos dada “Employee IBM” donde la variable de respuesta es el nivel de satisfacción del empleado (escala de 1 al 4). Para cumplir nuestro objetivo estaremos aplicando tres metodologías de clasificación supervisada: K-vecinos más cercanos , Arboles de decisiones , y Clasificación Bayesiano
Comenzamos cargando la base de datos Employee-IBM.csv, la cual contiene información de 1,470 empleados de IBM con 16 variables que incluyen edad, salario, años en la empresa, balance vida-trabajo, entre otras. La variable que queremos predecir es Satisfaction, que originalmente tenía cuatro niveles (1, 2, 3, 4). También queremos tener una visualización de la distribución por clases a través de gráficas de barras para tener cierto contexto de los datos.
## [1] 1470 16
##
## 1 2 3 4
## 276 303 459 432
##
## 1 2 3 4
## 0.19 0.21 0.31 0.29
## Visualización de la distribución de las clases
barplot(table(datos$Satisfaction),
main = "Distribución del Nivel de Satisfacción",
xlab = "Nivel de Satisfacción",
ylab = "Frecuencia",
col = c("tomato","steelblue","seagreen","goldenrod"),
border = "white")## [1] 0
Con los datos preparados, el siguiente paso fue crear las versiones del dataset necesarias para cada método y dividirlos en conjunto de entrenamiento y prueba.
Creamos dos versiones: datos_factor, donde convertimos Satisfaction y Gender a variables de tipo factor ya que son las únicas variables categóricas reales del dataset, y datos_norm, donde normalizamos todas las variables numéricas usando la función rescale() del paquete scales. Esta normalización es necesaria exclusivamente para KNN, ya que ese algoritmo mide distancias entre observaciones — si una variable tiene valores en miles como MonthlyIncome y otra entre 0 y 1, la variable grande dominaría la distancia injustamente.
# Solo Gender y Satisfaction se convierten a factor
datos_factor <- datos %>%
mutate(
Satisfaction = as.factor(Satisfaction),
Gender = as.factor(Gender)
)
# Versión normalizada (para KNN)
# Solo excluimos Satisfaction y Gender — todo lo demás es numérico y se normaliza
datos_norm <- datos %>%
select(-Satisfaction, -Gender) %>%
mutate(across(everything(), rescale))
# Dividir en entrenamiento y prueba
folds <- createFolds(datos_factor$Satisfaction, k = 6)
entrenamiento <- datos_factor[-folds[[6]], ]
prueba <- datos_factor[folds[[6]], ]
entrenamiento_norm <- datos_norm[-folds[[6]], ]
prueba_norm <- datos_norm[folds[[6]], ]
entrenamiento_labels <- datos_factor$Satisfaction[-folds[[6]]]
prueba_labels <- datos_factor$Satisfaction[folds[[6]]]
# Verificar dimensiones y balance
dim(entrenamiento)[1]## [1] 1226
## [1] 244
##
## 1 2 3 4
## 0.19 0.21 0.31 0.29
##
## 1 2 3 4
## 0.19 0.20 0.31 0.30
Por ultimo, para dividir los datos usamos createFolds() con k=5, lo que garantiza que tanto el entrenamiento como la prueba mantengan la misma proporción de clases (39/61). El resultado fue 1,176 observaciones para entrenamiento y 294 para prueba.
Para cumplir nuestro objetivo estaremos aplicando tres metodologías de clasificación supervisada: K-vecinos más cercanos , Arboles de decisiones , y Clasificación Bayesiano
KNN: Primero usamos train.kknn() para encontrar automáticamente el mejor valor de k, que resultó ser k=29.
modelo_knn <- train.kknn(Satisfaction ~ .,
data = cbind(entrenamiento_norm,
Satisfaction = entrenamiento_labels),
kmax = 30)
modelo_knn##
## Call:
## train.kknn(formula = Satisfaction ~ ., data = cbind(entrenamiento_norm, Satisfaction = entrenamiento_labels), kmax = 30)
##
## Type of response variable: nominal
## Minimal misclassification: 0.7185971
## Best kernel: optimal
## Best k: 30
Luego aplicamos knn() con ese valor sobre los datos de prueba normalizados. La matriz de confusión mostró un accuracy de 54.8% con un Kappa de -0.0237, por debajo del baseline de 27%.
pred_knn <- knn(train = entrenamiento_norm,
test = prueba_norm,
cl = entrenamiento_labels,
k = modelo_knn$best.parameters$k)
confusionMatrix(pred_knn, prueba_labels)## Confusion Matrix and Statistics
##
## Reference
## Prediction 1 2 3 4
## 1 1 4 9 7
## 2 3 4 6 6
## 3 17 29 35 31
## 4 25 13 26 28
##
## Overall Statistics
##
## Accuracy : 0.2787
## 95% CI : (0.2234, 0.3395)
## No Information Rate : 0.3115
## P-Value [Acc > NIR] : 0.8807
##
## Kappa : -0.0108
##
## Mcnemar's Test P-Value : 2.694e-05
##
## Statistics by Class:
##
## Class: 1 Class: 2 Class: 3 Class: 4
## Sensitivity 0.021739 0.08000 0.4605 0.3889
## Specificity 0.898990 0.92268 0.5417 0.6279
## Pos Pred Value 0.047619 0.21053 0.3125 0.3043
## Neg Pred Value 0.798206 0.79556 0.6894 0.7105
## Prevalence 0.188525 0.20492 0.3115 0.2951
## Detection Rate 0.004098 0.01639 0.1434 0.1148
## Detection Prevalence 0.086066 0.07787 0.4590 0.3770
## Balanced Accuracy 0.460365 0.50134 0.5011 0.5084
Usamos rpart() para construir el árbol con los datos de entrenamiento y rpart.plot() para visualizarlo.
Luego, al evaluar sobre los datos de prueba con predict(), obtuvimos un accuracy de 30% con Kappa de -0.0061. El árbol prácticamente predijo siempre “satisfecho” — la clase mayoritaria
pred_cart <- predict(modelo_cart, prueba, type = "class")
confusionMatrix(pred_cart, prueba$Satisfaction)## Confusion Matrix and Statistics
##
## Reference
## Prediction 1 2 3 4
## 1 0 0 0 0
## 2 0 0 0 0
## 3 34 35 61 47
## 4 12 15 15 25
##
## Overall Statistics
##
## Accuracy : 0.3525
## 95% CI : (0.2926, 0.416)
## No Information Rate : 0.3115
## P-Value [Acc > NIR] : 0.0955
##
## Kappa : 0.0656
##
## Mcnemar's Test P-Value : NA
##
## Statistics by Class:
##
## Class: 1 Class: 2 Class: 3 Class: 4
## Sensitivity 0.0000 0.0000 0.8026 0.3472
## Specificity 1.0000 1.0000 0.3095 0.7558
## Pos Pred Value NaN NaN 0.3446 0.3731
## Neg Pred Value 0.8115 0.7951 0.7761 0.7345
## Prevalence 0.1885 0.2049 0.3115 0.2951
## Detection Rate 0.0000 0.0000 0.2500 0.1025
## Detection Prevalence 0.0000 0.0000 0.7254 0.2746
## Balanced Accuracy 0.5000 0.5000 0.5561 0.5515
Usamos naiveBayes() del paquete e1071. Este algoritmo calcula la probabilidad de cada clase dado el valor de las variables predictoras, asumiendo independencia entre ellas.
modelo_bayes <- naiveBayes(Satisfaction ~ ., data = entrenamiento)
pred_bayes <- predict(modelo_bayes, prueba)Al evaluar sobre prueba, obtuvo un accuracy de 27% con Kappa de -0.023. Los tres modelos obtuvieron resultados por debajo o iguales al baseline, con Kappa negativo o cercano a cero — señal de que ninguno está aprendiendo patrones reales.
## Confusion Matrix and Statistics
##
## Reference
## Prediction 1 2 3 4
## 1 2 5 6 7
## 2 6 7 9 4
## 3 25 28 40 45
## 4 13 10 21 16
##
## Overall Statistics
##
## Accuracy : 0.2664
## 95% CI : (0.212, 0.3265)
## No Information Rate : 0.3115
## P-Value [Acc > NIR] : 0.9457
##
## Kappa : -0.0275
##
## Mcnemar's Test P-Value : 5.17e-06
##
## Statistics by Class:
##
## Class: 1 Class: 2 Class: 3 Class: 4
## Sensitivity 0.043478 0.14000 0.5263 0.22222
## Specificity 0.909091 0.90206 0.4167 0.74419
## Pos Pred Value 0.100000 0.26923 0.2899 0.26667
## Neg Pred Value 0.803571 0.80275 0.6604 0.69565
## Prevalence 0.188525 0.20492 0.3115 0.29508
## Detection Rate 0.008197 0.02869 0.1639 0.06557
## Detection Prevalence 0.081967 0.10656 0.5656 0.24590
## Balanced Accuracy 0.476285 0.52103 0.4715 0.48320
Para confirmar que los resultados del Paso 3 no fueron un accidente de una sola partición, aplicamos validación cruzada de 10 folds usando trainControl() de caret. Este método divide los datos en 10 partes, entrena el modelo 10 veces usando 9 partes y prueba con la restante, rotando el fold de prueba en cada iteración. Al final promedia los resultados de las 10 iteraciones
##
## Call:
## summary.resamples(object = comparacion)
##
## Models: KNN, CART, Bayes
## Number of resamples: 10
##
## Accuracy
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## KNN 0.2195122 0.2621951 0.2827869 0.2871518 0.3156737 0.3495935 0
## CART 0.2049180 0.2698920 0.3036323 0.2944530 0.3156737 0.3852459 0
## Bayes 0.2580645 0.2815970 0.3142077 0.3100233 0.3401140 0.3524590 0
##
## Kappa
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## KNN -0.08489526 -0.02827551 -0.0003486521 0.007260273 0.04641069 0.09956076
## CART -0.12651119 -0.02627169 0.0106517217 0.004456947 0.02582983 0.14454001
## Bayes -0.04430611 -0.01530018 0.0292539604 0.023059476 0.06487337 0.07549161
## NA's
## KNN 0
## CART 0
## Bayes 0
Los resultados fueron consistentes con el Paso 3 — los tres modelos obtuvieron accuracy promedio entre 29.4% y 30.9%, equivalente al baseline de 31.3%, con Kappa promedio cercano a cero en todos los casos. Usamos resamples() y dotplot() para comparar los tres modelos visualmente, donde se observa que los intervalos de confianza de los tres se solapan completamente, indicando que no hay diferencia estadísticamente significativa entre ellos. Estos resultados, consistentes a través de 10 folds y tres metodologías diferentes, nos llevan a concluir que las variables disponibles en este dataset no tienen suficiente poder predictivo para clasificar el nivel de satisfacción laboral. Esto es un hallazgo válido en sí mismo — la satisfacción del empleado parece depender de factores cualitativos como cultura organizacional o relación con el supervisor, los cuales no están capturados en estos datos.