logo

Introducción

El clasificador Bayesiano también conocido como Naive Bayes es uno de los clasificadores más utilizados por su simplicidad y rapidez. Se trata de una técnica de clasificación y predicción supervisada que construye modelos que predicen la probabilidad de posibles resultados, con base en el Teorema de Bayes.

Se basa en la suposición de que todos las variables son independientes dada la clase; esto es, cada variable \(x_i\) es condicionalmente independiente de los demás atributos dada la clase. Es decir, asume que la presencia o ausencia de una característica particular no está relacionada con la presencia o ausencia de cualquier otra característica, dada la clase. Por ejemplo, una fruta puede ser considerada como una manzana si es roja, redonda y de alrededor de 7 cm de diámetro. El clasificador Bayesiano considera que cada una de estas características contribuye de manera independiente a la probabilidad de que esta fruta sea una manzana, independientemente de la presencia o ausencia de las otras características.

Una ventaja de este clasificador es que solo se requiere una pequeña cantidad de datos de entrenamiento para estimar los parámetros (las medias y las varianzas de las variables) necesarias para la clasificación. Como se asumen que las variables son independientes, solo es necesario determinar las varianzas de las variables de cada clase y no toda la matriz de covarianza.

El aprendizaje se puede ver como el proceso de encontrar la clase más probable, dado un conjunto de entrenamiento y un conocimiento a priori sobre la probabilidad de cada clase.

¿Cómo funciona el Clasificador Bayesiano?

Paso 1. Preparación inicial y limpieza de los datos:

Antes de entrenar el modelo, es importante:

  • Revisar que no haya valores faltantes.

  • Codificar variables categóricas si es necesario.

  • Escoger las variables predictoras relevantes.

+Analizar la distribución de las clases (balance o desbalance).

Este paso garantiza que el algoritmo pueda trabajar correctamente con los datos y que las probabilidades calculadas sean representativas.

Paso 2: Dividir los datos en conjunto de entrenamiento y prueba:

Divide el conjunto total en dos subconjuntos de forma aleatoria:

  • Un conjunto de entrenamiento (aproximadamente 70-80%), que se usa para ajustar el modelo.

  • Un conjunto de prueba (20-30%) que no se usará en la construcción inicial del modelo y servirá para evaluar el desempeño del mismo.

Paso 3: Aplicar el Clasificador Bayesiano (NBC):

Suponiendo que las variables son independientes dada la clase y con ayuda del teorema de Bayes, se realizan los siguientes pasos para aplicar el clasificador:

  1. Calcula las probabilidades a priori: estima la frecuencia de cada clase en los datos de entrenamiento: \[ P(C_k) = \dfrac{\text{némero de observaciones de la clase } C_k}{\text{total de observaciones}} \]
  2. Calcula las probabilidades condicionales: para cada variable y clase, calcula \(P(x_j|C_k)\)
  • Si la variable es categórica, se usa la proporción dentro de la clase.
  • Si es numérica, se suele asumir una distribución normal y se estima la media y desviación estándar para cada clase.

Como el denominador \(P(x)\) es común a todas las clases, se omite en la comparación.

  1. Calcula las probabilidades posteriores: la probabilidad posterior de cada clase es proporcional al producto entre las probabilidad a priori y las probabilidades condicionales. \[ P(C_k|{\bf x}) \propto P(C_k) \prod_{j=1}^n P(x_j|C_k)\]

  2. Predicciones: el modelo asigna a cada observación la clase que tenga la mayor probabilidad posterior \(P(C_k|{\bf x})\)

Paso 4: Validar la estabilidad del modelo

Una vez entrenado, es importante validar si el modelo es consistente y robusto. Algunas estrategias comunes son:

  • Validación cruzada (k-fold cross-validation) para ver cómo varía el desempeño al cambiar los subconjuntos de entrenamiento/prueba.

  • Revisar si hay sobreajuste (overfitting). Muy buen desempeño en el conjunto de entrenamiento, pero malo en prueba.

  • Verificar qué tanto influye cada variable en la clasificación.

Paso 5: Interpretación de los resultados finales:

Finalmente, es importante aprender a analizar e interpretar:

  • La matriz de confusión: para ver cuántas observaciones fueron correctamente o incorrectamente clasificadas.

  • La exactitud (accuracy) y otras métricas como precisión, sensibilidad y especificidad, dependiendo del problema.

  • La probabilidad posterior de pertenecer a cada clase, útil en contextos donde queremos interpretar el grado de certeza del modelo.

Ejemplo de motivación

Supongamos que tenemos un conjunto de datos sobre las horas de estudio y número de ausencias en una clase y queremos predecir si el estudiante aprueba o reprueba.

En la figura podemos observar datos simulados de esta situación. Queremos predecir la clase de una nueva observación (🔵) que representa un nuevo estudiante.

library(ggplot2)

set.seed(2025)
n <- 20
data <- data.frame(
  horas_estudio = c(rnorm(n, 5, 1.5), rnorm(n, 3, 1.2)),
  ausencias = c(rnorm(n, 2, 1), rnorm(n, 6, 1)),
  clase = factor(c(rep("Aprueba", n), rep("Reprueba", n)))
)

nuevo <- data.frame(horas_estudio = 5, ausencias = 4)

ggplot(data, aes(x =ausencias , y = horas_estudio, color = clase)) +
  geom_point(size = 3) +
  scale_color_manual(values = c("Aprueba" = "green4", "Reprueba" = "red")) +
  geom_point(data = nuevo, aes(x = ausencias, y = horas_estudio),
             color = "blue", shape = 19, size = 3) +
  labs(x = "Número de ausencias", y = "Horas de estudio") +
  theme_minimal()

¿Cómo actúa el clasificador Bayesiano?

El Clasificador Bayesiano estima la probabilidad de que este punto azul pertenezca a cada clase, en función de:

Ejemplos en R

Ejemplo 1:

Para este ejemplo, tenemos dos variables sintéticas: hora y finde. Queremos predecir la variable lugar donde se encuentra la persona.

hora  <- c(8,14,24,8,14,24,8,14,24,8,14,24,8,14,24,8,14,24,24,24)
finde <- c(1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,1,1)
lugar <- c("casa","restaurante","casa","trabajo","trabajo","casa","trabajo",
           "trabajo","casa","casa","restaurante","casa","trabajo",
           "trabajo","casa","casa","restaurante","casa","cine","cine")

data1 <- data.frame(hora,finde,lugar)

Solución paso a paso:

Paso 1

Preparación inicial y limpieza de los datos:

Veamos como se distribuyen los registros según las variables.

tabla1 <-table(data1$hora,data1$lugar)
tabla1
/ casa cine restaurante trabajo
8 3 0 0 3
14 0 0 3 3
24 6 2 0 0
tabla2 <-table(data1$finde,data1$lugar)
tabla2
/ casa cine restaurante trabajo
0 3 0 0 6
1 6 2 3 0

Paso 2

Dividir los datos en conjunto de entrenamiento y prueba

Para este ejemplo, todos nuestros datos serán el conjunto de entrenamiento y supondremos nuevos datos para el conjunto de prueba. Por ejemplo, supongamos las siguientes dos nuevas observaciones.

nuevos <- data.frame(hora=24,finde=0)

Paso 3

Aplicar el Clasificador Bayesiano

En R, usaremos se tiene la librería e1071() , que implementa Naive Bayes fácilmente.

Los modelos de pronostico Bayesiano, y en particular el clasificador Bayesiano pierden información cuando las variables son numéricas continuos, y vamos a ver la prueba en este ejemplo. Crearemos un modelo con la variable hora tal cual, y después haremos el mismo modelo con la variable hora convertida en factor.

library(e1071)

modelo1 <- naiveBayes(lugar ~ ., data = data1)
modelo1
## 
## Naive Bayes Classifier for Discrete Predictors
## 
## Call:
## naiveBayes.default(x = X, y = Y, laplace = laplace)
## 
## A-priori probabilities:
## Y
##        casa        cine restaurante     trabajo 
##        0.45        0.10        0.15        0.30 
## 
## Conditional probabilities:
##              hora
## Y                 [,1]     [,2]
##   casa        18.66667 8.000000
##   cine        24.00000 0.000000
##   restaurante 14.00000 0.000000
##   trabajo     11.00000 3.286335
## 
##              finde
## Y                  [,1] [,2]
##   casa        0.6666667  0.5
##   cine        1.0000000  0.0
##   restaurante 1.0000000  0.0
##   trabajo     0.0000000  0.0

Podemos calcular la predicción para los datos de entrenamiento y la tasa de aciertos.

pred <- predict(modelo1,data1[,-3],type="class")
tt   <- table(pred,data1[,3])
tt
pred/ casa cine restaurante trabajo
casa 0 0 0 0
cine 6 2 0 0
restaurante 3 0 3 0
trabajo 0 0 0 6
TA   <- (sum(diag(tt)))/sum(tt) # tasa de aciertos
paste("Tasa de aciertos: ",round(TA,4)*100, "%",sep="")
## [1] "Tasa de aciertos: 55%"

La predicción que obtenemos con el modelo es baja. Este problema es habitual cuando usamos datos continuos, que generan distribuciones de probabilidad continuas. Revisemos los datos.

str(data1)
## 'data.frame':    20 obs. of  3 variables:
##  $ hora : num  8 14 24 8 14 24 8 14 24 8 ...
##  $ finde: num  1 1 1 0 0 0 0 0 0 1 ...
##  $ lugar: chr  "casa" "restaurante" "casa" "trabajo" ...

Vamos a convertir las variables en factores para que funcione correctamente el modelo:

data1 <- data1 %>%
  mutate(across(c(hora,hora,finde), as.factor))
str(data1)
## 'data.frame':    20 obs. of  3 variables:
##  $ hora : Factor w/ 3 levels "8","14","24": 1 2 3 1 2 3 1 2 3 1 ...
##  $ finde: Factor w/ 2 levels "0","1": 2 2 2 1 1 1 1 1 1 2 ...
##  $ lugar: chr  "casa" "restaurante" "casa" "trabajo" ...

Calculamos de nuevo, el modelo y su predicción.

modelo1a <- naiveBayes(lugar ~ ., data = data1)
modelo1a
## 
## Naive Bayes Classifier for Discrete Predictors
## 
## Call:
## naiveBayes.default(x = X, y = Y, laplace = laplace)
## 
## A-priori probabilities:
## Y
##        casa        cine restaurante     trabajo 
##        0.45        0.10        0.15        0.30 
## 
## Conditional probabilities:
##              hora
## Y                     8        14        24
##   casa        0.3333333 0.0000000 0.6666667
##   cine        0.0000000 0.0000000 1.0000000
##   restaurante 0.0000000 1.0000000 0.0000000
##   trabajo     0.5000000 0.5000000 0.0000000
## 
##              finde
## Y                     0         1
##   casa        0.3333333 0.6666667
##   cine        0.0000000 1.0000000
##   restaurante 0.0000000 1.0000000
##   trabajo     1.0000000 0.0000000
preda     <- predict(modelo1a,data1[,-3],type="class")
tta       <- table(preda,data1[,3])
tta
preda/ casa cine restaurante trabajo
casa 9 2 0 0
cine 0 0 0 0
restaurante 0 0 3 0
trabajo 0 0 0 6
TAa       <- (sum(diag(tta)))/sum(tta) # tasa de aciertos
paste("Tasa de aciertos: ",round(TAa,4)*100, "%",sep="")
## [1] "Tasa de aciertos: 90%"

Notamos que ahora lo hace mejor. Ahora, podemos predecir la categoría que tomaría el dato nuevo.

predict(modelo1a,nuevos)
## [1] casa
## Levels: casa cine restaurante trabajo

Paso 4

Validar la estabilidad del modelo

Al ser un ejemplo sencillo no se realizó este paso.

Paso 5

Interpretación de los resultados finales

  • De la predicción se puede concluir que una persona a las 24 horas, entre semana estará en su casa.
  • Respecto a la matriz de confusión, podemos notar que el lugar cine es en el que se equivoca el modelo.

Ejemplo 2:

Para este ejemplo, usaremos los datos de supervivientes del Titanic que vienen en R.

Solución paso a paso:

Paso 1

Preparación inicial y limpieza de los datos:

data2 <- as.data.frame(Titanic)
dim(data2)
## [1] 32  5
head(data2)
Class Sex Age Survived Freq
1st Male Child No 0
2nd Male Child No 0
3rd Male Child No 35
Crew Male Child No 0
1st Female Child No 0
2nd Female Child No 0

Esta base de datos, debe acomodarse, pues hay muchas columnas donde la frecuencias es cero y podríamos separar individualmente.

library(tidyr)
library(dplyr)

data2_long <- data2 %>% uncount(weights = Freq)
dim(data2_long)
## [1] 2201    4
head(data2_long)
Class Sex Age Survived
3rd Male Child No
3rd Male Child No
3rd Male Child No
3rd Male Child No
3rd Male Child No
3rd Male Child No

Ahora, data2_long tiene una fila por pasajero individual, lista para ser usada con modelos de clasificación.

Paso 2

Dividir los datos en conjunto de entrenamiento y prueba

Mediante un muestreo aleatorio, separamos el conjunto de entrenamiento y en conjunto de prueba. Supongamos en 5 grupos (folds=5), 4 para entrenamiento y 1 para prueba.

set.seed(2025)
library(caret)
folds         <- createFolds(data2_long$Survived, k =5)
entrenamiento <- data2_long[-folds[[5]],]
prueba        <- data2_long[folds[[5]],]

Por otra parte, guardamos las etiquetas del diagnóstico de todas las observaciones en dos vectores por separado.

# Etiquetas
entrenamiento_labels <- data2_long$Survived[-folds[[5]]]
prueba_labels        <- data2_long$Survived[folds[[5]]]

Paso 3

Aplicar el Clasificador Bayesiano

modelo2 <- naiveBayes(Survived ~ ., data2_long)
modelo2
## 
## Naive Bayes Classifier for Discrete Predictors
## 
## Call:
## naiveBayes.default(x = X, y = Y, laplace = laplace)
## 
## A-priori probabilities:
## Y
##       No      Yes 
## 0.676965 0.323035 
## 
## Conditional probabilities:
##      Class
## Y            1st        2nd        3rd       Crew
##   No  0.08187919 0.11208054 0.35436242 0.45167785
##   Yes 0.28551336 0.16596343 0.25035162 0.29817159
## 
##      Sex
## Y           Male     Female
##   No  0.91543624 0.08456376
##   Yes 0.51617440 0.48382560
## 
##      Age
## Y          Child      Adult
##   No  0.03489933 0.96510067
##   Yes 0.08016878 0.91983122

Calculamos la predicción del modelo.

pred2 <- predict(modelo2,prueba[,-4])
table(pred2,prueba_labels)
pred2/prueba_labels No Yes
No 280 69
Yes 18 73
confusionMatrix(pred2,prueba_labels)
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  No Yes
##        No  280  69
##        Yes  18  73
##                                           
##                Accuracy : 0.8023          
##                  95% CI : (0.7619, 0.8385)
##     No Information Rate : 0.6773          
##     P-Value [Acc > NIR] : 3.402e-09       
##                                           
##                   Kappa : 0.5008          
##                                           
##  Mcnemar's Test P-Value : 8.296e-08       
##                                           
##             Sensitivity : 0.9396          
##             Specificity : 0.5141          
##          Pos Pred Value : 0.8023          
##          Neg Pred Value : 0.8022          
##              Prevalence : 0.6773          
##          Detection Rate : 0.6364          
##    Detection Prevalence : 0.7932          
##       Balanced Accuracy : 0.7268          
##                                           
##        'Positive' Class : No              
## 

Paso 4

Validar la estabilidad del modelo

Apliquemos validación cruzada para validar la estabilidad del modelo. La idea es analizar que la tasa de aciertos conseguida no dependa de la partición utilizada.

set.seed(2025)
train_control <- trainControl(method="cv",number=20,savePredictions = TRUE)

NBC_cv <- train(Survived ~ ., data=cbind(prueba, diagnosis=prueba_labels), 
                method = "naive_bayes", trControl = train_control)

# Resultados de validación cruzada
NBC_cv
## Naive Bayes 
## 
## 440 samples
##   4 predictor
##   2 classes: 'No', 'Yes' 
## 
## No pre-processing
## Resampling: Cross-Validated (20 fold) 
## Summary of sample sizes: 418, 418, 418, 418, 418, 418, ... 
## Resampling results across tuning parameters:
## 
##   usekernel  Accuracy   Kappa    
##   FALSE      0.9954545  0.9902655
##    TRUE      1.0000000  1.0000000
## 
## Tuning parameter 'laplace' was held constant at a value of 0
## Tuning
##  parameter 'adjust' was held constant at a value of 1
## Accuracy was used to select the optimal model using the largest value.
## The final values used for the model were laplace = 0, usekernel = TRUE
##  and adjust = 1.
# Matriz de confusión usando predicciones guardadas por caret
confusionMatrix(NBC_cv$pred$pred, NBC_cv$pred$obs)
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  No Yes
##        No  594   0
##        Yes   2 284
##                                           
##                Accuracy : 0.9977          
##                  95% CI : (0.9918, 0.9997)
##     No Information Rate : 0.6773          
##     P-Value [Acc > NIR] : <2e-16          
##                                           
##                   Kappa : 0.9948          
##                                           
##  Mcnemar's Test P-Value : 0.4795          
##                                           
##             Sensitivity : 0.9966          
##             Specificity : 1.0000          
##          Pos Pred Value : 1.0000          
##          Neg Pred Value : 0.9930          
##              Prevalence : 0.6773          
##          Detection Rate : 0.6750          
##    Detection Prevalence : 0.6750          
##       Balanced Accuracy : 0.9983          
##                                           
##        'Positive' Class : No              
## 

Paso 5

Interpretación de los resultados finales

Al aplicar validación cruzada con 20 particiones, el modelo fue entrenado y evaluado múltiples veces con diferentes subconjuntos del conjunto de datos. Esto permite obtener una estimación más robusta del desempeño general del modelo.

Veamos algunos puntos clave que se deben analizar:

  • Matriz de confusión: nos permite observar en promedio cuántas observaciones fueron clasificadas correctamente y cuántas erróneamente. Notamos que se equivocó en los sobrevivientes.

  • Exactitud (Accuracy): el valor reportado representa el promedio de aciertos que tuvo el clasificador a lo largo de los 20 folds. Una exactitud de 99.97% indica buen desempeño.

  • Índice Kappa: compara la precisión del modelo con la que se esperaría por azar. Sus valores típicamente se interpretan así:

    • \(> 0.80\): Excelente.
    • \(0.60 - 0.80\): Bueno.
    • \(0.40 - 0.60\): Moderado.
    • \(< 0.40\): Débil.

Nota importante: Si el valor de Kappa es bajo pero la exactitud es alta, puede indicar que el modelo está sesgado hacia la clase mas grande.

Ejercicio de clase

Clasificación del rendimiento académico de estudiantes de matemáticas

En este ejercicio utilizaremos datos reales recopilados de estudiantes de educación secundaria en dos escuelas de Portugal. El objetivo es predecir si un estudiante aprobará o no el curso de matemáticas, utilizando información relacionada con:

  • Información académica.
  • Información familiar.
  • Información personal.
  • Información social.

La base de datos la puedes descargar desde Moodle. Para mayor información de las variables pueden consultar: Student Mat Analysis