Árboles de Decisión

Author

Rubén Cool

¿Qué es Machine Learning?

La creación de un modelo de machine learning implica la recopilación y preparación de datos, la selección de un algoritmo adecuado, el entrenamiento del modelo con los datos preparados y la evaluación de su rendimiento mediante pruebas e iteraciones.

Datos de entrenamiento

El proceso de machine learning comienza con la recopilación y el procesamiento de los datos de entrenamiento. Los datos de mala calidad suelen denominarse basura que entra y basura que sale, por lo que un modelo de machine learning es tan bueno como los datos que se utilizan para entrenarlo. Si bien la preparación y el procesamiento de los datos a veces son un proceso rutinario, podría decirse que es la etapa más crítica para hacer que todo el modelo funcione según lo previsto o perjudicar su rendimiento.

Hay varios tipos diferentes de datos que se utilizan para entrenar un modelo de ML. En primer lugar, es importante conocer la diferencia entre los datos etiquetados y los no etiquetados.

Los datos etiquetados son un conjunto de datos en el que cada instancia o ejemplo va acompañado de una etiqueta o variable de destino que representa el resultado o la clasificación que se espera. Por lo general, estas etiquetas las proporcionan expertos humanos o se obtienen mediante un proceso confiable.

Ejemplo: En una tarea de clasificación de imágenes, los datos etiquetados consistirían en imágenes junto con sus etiquetas de clase correspondientes (por ejemplo, gato, perro, automóvil).

Los datos sin etiquetar son un conjunto de datos en el que las instancias o los ejemplos no tienen etiquetas ni variables de destino asociadas. Los datos se componen únicamente de características de entrada, sin ningún resultado ni una clasificación correspondiente.

Ejemplo: una recopilación de imágenes sin etiquetas ni anotaciones.

Los principales tipos de datos que se utilizan en el entrenamiento son los datos estructurados y no estructurados. Existen varios subtipos, que se pueden ver al expandir las siguientes categorías.

Los datos estructurados se refieren a los datos que están organizados y tienen un formato predefinido, normalmente en forma de tablas o bases de datos con filas y columnas. Este tipo de datos es adecuado para los algoritmos tradicionales de machine learning que requieren características y etiquetas bien definidas. A continuación, se detallan los tipos de datos estructurados.

Datos tabulares: Esto incluye los datos almacenados en hojas de cálculo, bases de datos o archivos CSV, con filas que representan instancias y columnas que representan características o atributos.

Datos de series temporales: Este tipo de datos consiste en secuencias de valores que se miden en puntos sucesivos en el tiempo, como los precios de las acciones, las lecturas de los sensores o los datos meteorológicos.

Los datos no estructurados son aquellos que carecen de una estructura o formato predefinidos, como texto, imágenes, audio y video. Este tipo de datos requiere técnicas de machine learning más avanzadas para extraer patrones e información significativos.

Datos de texto: Esto incluye documentos, artículos, publicaciones en redes sociales y otros datos textuales.

Datos de imagen: Esto incluye imágenes digitales, fotografías y fotogramas de video.

Proceso de Machine Learning

Los datos de entrenamiento compilados se cargan en algoritmos de machine learning. El proceso de ML se divide tradicionalmente en tres categorías amplias: el aprendizaje supervisado, el aprendizaje no supervisado y el aprendizaje por refuerzo.

En el aprendizaje supervisado, los algoritmos se entrenan con datos etiquetados. El objetivo es aprender una función de mapeo que pueda predecir el resultado de nuevos datos de entrada no vistos.

El aprendizaje no supervisado se refiere a los algoritmos que aprenden a partir de datos no etiquetados. El objetivo es descubrir patrones, estructuras o relaciones inherentes dentro de los datos de entrada.

En el aprendizaje por refuerzo, la máquina solo recibe una puntuación de rendimiento como guía; en el aprendizaje semisupervisado, solo se etiqueta una parte de los datos de entrenamiento. La retroalimentación se hace en forma de recompensas o penalizaciones por las acciones de la máquina y esta aprende de estos comentarios para mejorar las decisiones que toma con el paso del tiempo.

Conjunto de entrenamiento y conjunto de test

El esquema muestra cómo se divide un conjunto de datos (Dataset) en Machine Learning para entrenar y evaluar los modelos correctamente. Se divide en tres enfoques principales:

  • Holdout Method (Método de validación simple): El dataset completo se divide en dos partes. Un bloque grande se usa para Training (entrenar el modelo) y un bloque más pequeño se reserva para Testing (probar su rendimiento con datos que no ha visto).

  • Cross Validation (Validación cruzada): El conjunto de entrenamiento se subdivide en múltiples bloques o particiones iguales. El modelo se entrena y valida varias veces turnando estos bloques para asegurar que la evaluación sea robusta y no dependa de un solo corte de datos.

  • Training, Validation, Testing (Tres particiones): Cuando se dispone de suficientes datos (Data Permitting), el dataset se divide en tres partes:

    1. Training: Para ajustar los parámetros del modelo.

    2. Validation: Para ajustar hiperparámetros y tomar decisiones sobre la arquitectura durante el desarrollo.

    3. Testing: Como evaluación final y completamente independiente para medir la eficacia real del modelo.

Sesgo (bias) y varianza

Sobreajuste u “overfitting”

Explicar el conjunto de datos de entrenamiento, en lugar de encontrar patrones que generalizan. El modelo se ajusta bien al conjunto de datos de entrenamiento pero falla en el conjunto de datos de test.

Validación cruzada

  • El proceso básico:

    1. El conjunto de datos total (Data) se divide en k partes iguales o “pliegues” (folds), en este caso 5 (Fold 1 al Fold 5).

    2. El modelo se entrena y se prueba k veces. En cada iteración, se usa un bloque diferente como conjunto de prueba (Test) y los k−1 bloques restantes como conjunto de entrenamiento (Training). Por ejemplo, en el Fold 1 la primera sección es para prueba y las cuatro siguientes para entrenar; en el Fold 2 la sección de prueba se desplaza al segundo bloque, y así sucesivamente.

  • Cálculo del rendimiento: Los resultados de las métricas de evaluación de cada uno de los 5 modelos se combinan calculando su promedio (Average), lo que arroja una medida final de desempeño mucho más sólida y confiable (Final Measure of Performance).

  • Ventajas:

    • Permite que cada muestra o registro de datos se utilice tanto para entrenar como para probar en diferentes momentos, aprovechando al máximo la información disponible.

    • Es el método estándar para estimar qué tan bien generaliza un modelo ante datos nuevos o no vistos.

¿Qué son los árboles de decisión?

  • El enfoque classification and regression tree (CART) fue desarrollado por Breiman et al. (1984).

  • Son un tipo de algoritmos de aprendizaje supervisado (i.e., existe una variable objetivo predefinida).

  • Principalmente usados en problemas de clasificación.

  • Las variables de entrada y salida pueden ser categóricas o continuas.

  • Divide el espacio de predictores (variables independientes) en regiones distintas y no sobrepuestas.

Selecciona varaibles explicativas (predictivas) que son mas discriminantes para la variable respuesta. Construye una regla de decisión que permite asignar un nuevo individuo a una clase de la variable de interés.

Ventajas

  • A diferencia de modelos estadísticos más restrictivos (como la regresión lineal), los árboles no requieren que transformes las variables continuas en discretas ni que codifiques obligatoriamente las categóricas mediante trucos complejos. El algoritmo maneja de forma natural variables numéricas, ordinales y nominales simultáneamente.

  • Su estructura en forma de diagrama de flujo hace que la lógica del modelo sea completamente transparente. Esto facilita enormemente la interpretación de los resultados y permite explicárselos a personas sin formación técnica.

  • Durante su construcción, el algoritmo evalúa todas las variables predictoras disponibles y selecciona por sí mismo cuáles son las más informativas para dividir los datos en cada nodo. Las variables que no aportan valor predictivo simplemente se ignoran o se colocan en niveles inferiores, lo que ayuda a filtrar el ruido sin necesidad de que hagas un análisis de correlación o selección manual previo.

Desventajas

  • Los árboles de decisión se construyen mediante algoritmos voraces (greedy) que seleccionan el mejor corte local en cada nodo de manera secuencial. Si cambias, añades o eliminas unas pocas observaciones en el conjunto de entrenamiento, el corte inicial puede cambiar por completo, provocando un efecto cascada que altera drásticamente toda la estructura del árbol y sus reglas de predicción.

  • Encontrar el árbol perfecto (el que minimice el error global con la estructura más simple) es un problema computacionalmente intratable (NP-completo) debido a la enorme cantidad de combinaciones posibles de variables y puntos de corte. Por esta razón, los algoritmos se basan en optimizaciones locales paso a paso que no garantizan llegar al mejor árbol global, requiriendo métodos posteriores como la poda (pruning).

  • A medida que un árbol se vuelve más profundo, los datos se van fragmentando en subconjuntos cada vez más reducidos en cada bifurcación. Si el dataset total es pequeño, los nodos terminales (hojas) terminarán con muy pocas observaciones, lo que genera estimaciones estadísticas poco confiables, alta varianza y una propensión severa al sobreajuste (overfitting).

Ejemplo 1.

El departamento de analítica de una empresa de comercio electrónico desea diseñar una campaña de marketing personalizada para optimizar el uso de cupones de descuento. Para lograrlo, el equipo de ciencia de datos ha extraído una muestra de clientes con el fin de estudiar qué perfiles son más propensos a utilizarlos.

Se cuenta con un conjunto de datos sintético que registra tres variables por cada cliente:

  • Edad: Variable cuantitativa continua que representa la edad del cliente.

  • Ingresos: Variable cuantitativa continua que indica los ingresos anuales del cliente.

  • Usa Descuento: Variable cualitativa binaria con etiquetas "Sí" o "No", que indica si el cliente históricamente aprovecha los descuentos.

¿Qué variable es la más significativa para la mejor división de la población?

head(datos_clientes)
# A tibble: 6 × 4
     id  edad ingresos descuento
  <int> <dbl>    <dbl> <chr>    
1     1    54    44512 No       
2     2    34    26381 Sí       
3     3    44    59006 Sí       
4     4    46    41716 No       
5     5    44    39386 No       
6     6    39    30141 No       
ggplot(datos_clientes, aes(x = edad, y = ingresos, color = descuento)) +
  geom_point(size = 3, alpha = 0.8) +
  labs(
    title = "Distribución de Clientes por Edad e Ingresos",
    subtitle = "Conjunto de datos sintético",
    x = "Edad",
    y = "Ingresos",
    color = "¿Usa Descuento?"
  ) +
  theme_minimal() +
  scale_color_manual(values = c("Sí" = "#2b5c8f", "No" = "#e07a5f"))

  • ¿Qué es un nodo puro?

    Un nodo es completamente puro cuando todos los elementos que contiene pertenecen a una sola categoría (clase). Por ejemplo, si en una de las cajas finales del árbol (hojas) todos los clientes son "Sí", ese nodo tiene una pureza del 100% porque no hay mezcla.

  • ¿Qué es un nodo impuro?

    Un nodo es impuro cuando hay una mezcla de diferentes clases. Por ejemplo, el nodo raíz (el inicio del árbol) tiene una mezcla de clientes que usan descuento y otros que no. Esa es una alta impureza.

  • ¿Cómo funciona el algoritmo?

    El algoritmo de particionamiento recursivo (greedy) evalúa todas las posibles variables (como Edad o Ingresos) y todos los puntos de corte posibles. Su objetivo en cada paso es encontrar el umbral de corte que reduzca al máximo la impureza, logrando que los grupos resultantes (los nodos hijos) queden lo más “limpios” o separados posibles entre clases.

   No
Sí  0
No  1
rpart.plot(
  modelo_arbol, 
  digits = -4,             # Positivo para mostrar decimales correctos (ej. 3 decimales)
  type = 2,
  extra = 104,            # Muestra la clase predicha, n y las probabilidades de cada clase
  box.palette = "RdYlGn", 
  fallen.leaves = TRUE, 
  branch.lty = 3,        
  main = "Modelo de Árbol de Decisión: Predicción de Descuento"
)

tally(~ descuento, format = "proportion", data = datos_clientes)
descuento
       Sí        No 
0.3133333 0.6866667 

1. Nodo Raíz

  • Contenido: Muestra que el total de la base de datos está compuesto por un 31.33% de clientes que sí usan descuento y un 68.67% que no, lo cual coincide exactamente con la proporción global calculada con tally().

  • Primer corte: El algoritmo evalúa la variable Ingresos con un umbral de 51,962.

2. Rama Izquierda (Clientes con Ingresos \ge 51,962)

  • Representa: El 26.00% de los clientes totales del dataset. En este grupo, la probabilidad inicial de usar descuento es alta (79.49%).

  • Segundo corte: Evalúa la Edad con un umbral de 34 años (edad >= 34).

    • Hojas terminales:

      • Si el cliente tiene 34 años o más (20.67% de los datos), el árbol predice Sí con una probabilidad del 100%.

      • Si el cliente es menor de 34 años (5.33% de los datos), el árbol predice No con una probabilidad del 100%.

3. Rama Derecha (Clientes con Ingresos < 51,962)

  • Representa: El 74.00% de los clientes restantes. Aquí la mayoría no usa descuento (85.59% de probabilidad de “No”).

  • Siguientes cortes: El algoritmo realiza divisiones adicionales evaluando la edad (edad < 35) y un nuevo umbral de ingresos (ingresos < 39510).

    • Hojas terminales (abajo a la derecha):

      • Un subgrupo con ingresos menores a 39,510 y ciertas condiciones de edad se clasifica como Sí (8.67% del total).

      • Los demás grupos se clasifican como No, abarcando el 12.67% y el 52.67% de los datos respectivamente.

¿Cómo funciona Árboles de decisión?

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

Como en cualquier modelo supervisado, el primer paso es asegurar que los datos estén en condiciones adecuadas:

  • Cargar los datos: importa el conjunto de datos y asegúrate de identificar correctamente la variable respuesta y las variables predictoras.

  • Explorar los datos: analiza la distribución de las clases, la presencia de valores atípicos, datos faltantes o categorías con muy pocas observaciones.

  • Codificar variables categóricas: aunque algunos algoritmos pueden manejar variables categóricas directamente, es buena práctica revisar la codificación.

En esta etapa también puedes realizar análisis exploratorio para entender qué variables podrían tener mayor relación con la variable respuesta.

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.

Una división adecuada permite evaluar la capacidad predictiva real del árbol, evitando un sobreajuste (overfitting). La división del conjunto de datos en entrenamiento y prueba puede realizarse fácilmente en R utilizando la función createFolds() del paquete caret, que permite crear particiones aleatorias y balanceadas de los datos.

Paso 3: Construcción del árbol de decisión:

En un árbol de decisión se encuentran los siguientes componentes:

  • Nodos: son las variables de entrada.

  • Ramas: representan los posibles valores de las variables de entrada.

  • Hojas: son los posibles valores de la variable de salida.

Como primer elemento de un árbol de decisión tenemos el nodo raíz que va a representar la variable de mayor relevancia en el proceso de clasificación.

Método CART

De manera general, lo que hace este algoritmo es encontrar la variable independiente que mejor separa nuestros datos en grupos. Esta mejor separación es expresada con una regla. A cada regla corresponde un nodo.

Una vez hecho esto, los datos son separados (particionados) en grupos a partir de la regla obtenida. Después, para cada uno de los grupos resultantes, repite el mismo proceso. Se busca la variable que mejor separa los datos en grupos, se obtiene una regla, y se separan los datos. Hacemos esto de manera recursiva hasta que nos es imposible obtener una mejor separación. Cuando esto ocurre, el algoritmo se detiene. Cuando un grupo no puede ser partido mejor, se le llama nodo terminal u hoja.

Una característica muy importante en este algoritmo es que una vez que alguna variable ha sido elegida para separar los datos, ya no es usada de nuevo en los grupos que ha creado. Se buscan variables distintas que mejoren la separación de los datos.

En R, se encuentra programado en la librería rpart() . También se encuentra programado la visualización del árbol, en la librería rpart.plot() .

Paso 4: Validar la estabilidad del modelo

Para validar el modelo se usa Validación Cruzada (K-fold Cross-validation) Ahora, realiza una validación cruzada K-fold para comprobar la estabilidad del modelo. La forma más usual que se hace es:

  • Divide tu conjunto de entrenamiento en k grupos.

  • Entrena el modelo en K−1 grupos y prueba en el restante.

  • Repite el proceso hasta que cada grupo haya sido usado para prueba exactamente una vez.

Calcula el rendimiento promedio en términos de las métricas anteriores.

Paso 5: Interpretación de los resultados finales:

Finalmente, se analizan tanto el rendimiento como la estructura del árbol:

  • Matriz de confusión promedio obtenida por la validación cruzada.

  • Indicadores de desempeño promedio: exactitud, sensibilidad, especificidad, etc.

  • Interpretación visual del árbol.

Ejemplo 2

Para estos datos la exploración no es necesaria, pero cuando se tengan datos reales es muy importante realizarla.

head(iris)
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1          5.1         3.5          1.4         0.2  setosa
2          4.9         3.0          1.4         0.2  setosa
3          4.7         3.2          1.3         0.2  setosa
4          4.6         3.1          1.5         0.2  setosa
5          5.0         3.6          1.4         0.2  setosa
6          5.4         3.9          1.7         0.4  setosa

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, 4 para entrenamiento y 1 para prueba.

set.seed(2025)
library(caret)
Warning: package 'caret' was built under R version 4.6.2

Adjuntando el paquete: 'caret'
The following object is masked from 'package:mosaic':

    dotPlot
The following object is masked from 'package:purrr':

    lift
folds         <- createFolds(iris$Species, k = 5)
entrenamiento <- iris[-folds[[5]],]
prueba        <- iris[folds[[5]],]
  • set.seed(2025): Fija una “semilla” para el generador de números pseudoaleatorios. Como la selección de los datos para cada grupo es aleatoria, fijar un número (en este caso 2025) garantiza que cada vez que corras el código obtengas exactamente la misma partición de datos, asegurando la reproducibilidad de tu trabajo.

  • library(caret): Carga el paquete caret (Classification And Regression Training), que es una de las herramientas más utilizadas en R para aprendizaje automático y que contiene la función para crear particiones.

  • folds <- createFolds(iris$Species, k = 5): Divide los índices de las filas del conjunto de datos iris en 5 grupos (folds) iguales o de tamaño similar.

    • El parámetro estratificado (iris$Species): Al pasarle la columna de especies, la función se asegura de que cada uno de los 5 grupos mantenga la misma proporción de clases (especies de flores) que el dataset original, evitando que un grupo se quede sin alguna categoría.
  • entrenamiento <- iris[-folds[[5]], ]: Crea el conjunto de datos de entrenamiento. Utiliza el operador negativo [-] para excluir todos los índices que pertenecen al quinto grupo (folds[[5]]). Por lo tanto, toma los datos de los otros 4 grupos restantes (el 80% de los datos) para entrenar el modelo.

  • prueba <- iris[folds[[5]], ]: Crea el conjunto de datos de prueba (o validación). Selecciona únicamente los índices del quinto grupo (folds[[5]]) (el 20% restante de los datos) para evaluar qué tan bien generaliza el modelo con datos que no ha visto durante el entrenamiento.

Construimos el modelo ajustado con los datos de entrenamiento. Para este ejemplo utilizaremos el árbol CART.

library(rpart)
modelo1 <- rpart(Species ~ ., data = entrenamiento, method = "class")
modelo1
n= 120 

node), split, n, loss, yval, (yprob)
      * denotes terminal node

1) root 120 80 setosa (0.33333333 0.33333333 0.33333333)  
  2) Petal.Length< 2.45 40  0 setosa (1.00000000 0.00000000 0.00000000) *
  3) Petal.Length>=2.45 80 40 versicolor (0.00000000 0.50000000 0.50000000)  
    6) Petal.Width< 1.75 44  4 versicolor (0.00000000 0.90909091 0.09090909) *
    7) Petal.Width>=1.75 36  0 virginica (0.00000000 0.00000000 1.00000000) *

Validar la estabilidad del modelo

No se aplico en este ejemplo, pero se podría hacer la validación haciendo 5 replicas del proceso, tomando en cada iteración como conjunto de entrenamiento cada una de las particiones creadas.

Interpretación de los resultados finales

Podemos observar en la tabla, en las filas la clasificación real y en las columnas la predicción. La tasa de aciertos del modelo respecto a las observaciones de entrenamiento sería:

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

pred<- predict(modelo1,entrenamiento[,-5],type="class")
tt<- table(pred,entrenamiento[,5])
tt
            
pred         setosa versicolor virginica
  setosa         40          0         0
  versicolor      0         40         4
  virginica       0          0        36
TA <- sum(diag(tt))/sum(tt) 
paste0("Tasa de aciertos con los datos de entrenamiento: ", round(TA,4)*100, "%", sep="")
[1] "Tasa de aciertos con los datos de entrenamiento: 96.67%"

Podemos hablar de una precisión de aproximadamente el 97%. Ahora, para analizar la calidad del modelo, veamos que tan buena es la predicción para el conjunto de prueba.

Pred    <- predict(modelo1, prueba[,-5],type="class")
table   <- table(prueba[,5],Pred)
table
            Pred
             setosa versicolor virginica
  setosa         10          0         0
  versicolor      0          9         1
  virginica       0          1         9
TAP <- sum(diag(table))/sum(table) 
paste0("Tasa de aciertos con los datos de prueba: ", round(TAP,4)*100, "%", sep="")
[1] "Tasa de aciertos con los datos de prueba: 93.33%"

Encontramos que el modelo logra predecir aproximadamente un 93% los datos de prueba.

rpart.plot(
  modelo1, 
  digits = -4,             # Positivo para mostrar decimales correctos (ej. 3 decimales)
  type = 2,
  extra = 104,            # Muestra la clase predicha, n y las probabilidades de cada clase
  box.palette = "RdYlGn", 
  fallen.leaves = TRUE, 
  branch.lty = 3,        
  main = "Modelo de Árbol de Decisión: Predicción de Descuento"
)

iris[folds[[5]],]
    Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
7            4.6         3.4          1.4         0.3     setosa
8            5.0         3.4          1.5         0.2     setosa
12           4.8         3.4          1.6         0.2     setosa
21           5.4         3.4          1.7         0.2     setosa
25           4.8         3.4          1.9         0.2     setosa
30           4.7         3.2          1.6         0.2     setosa
31           4.8         3.1          1.6         0.2     setosa
36           5.0         3.2          1.2         0.2     setosa
44           5.0         3.5          1.6         0.6     setosa
46           4.8         3.0          1.4         0.3     setosa
57           6.3         3.3          4.7         1.6 versicolor
58           4.9         2.4          3.3         1.0 versicolor
62           5.9         3.0          4.2         1.5 versicolor
70           5.6         2.5          3.9         1.1 versicolor
71           5.9         3.2          4.8         1.8 versicolor
81           5.5         2.4          3.8         1.1 versicolor
83           5.8         2.7          3.9         1.2 versicolor
84           6.0         2.7          5.1         1.6 versicolor
94           5.0         2.3          3.3         1.0 versicolor
98           6.2         2.9          4.3         1.3 versicolor
104          6.3         2.9          5.6         1.8  virginica
105          6.5         3.0          5.8         2.2  virginica
120          6.0         2.2          5.0         1.5  virginica
122          5.6         2.8          4.9         2.0  virginica
124          6.3         2.7          4.9         1.8  virginica
131          7.4         2.8          6.1         1.9  virginica
133          6.4         2.8          5.6         2.2  virginica
138          6.4         3.1          5.5         1.8  virginica
143          5.8         2.7          5.1         1.9  virginica
146          6.7         3.0          5.2         2.3  virginica

Ejemplo 2

Análisis exploratorio

summary(titanic_train)
  PassengerId       Survived          Pclass             Name    
 Min.   :  1.0   Min.   :0.0000   Min.   :1.000   Length   :891  
 1st Qu.:223.5   1st Qu.:0.0000   1st Qu.:2.000   N.unique :891  
 Median :446.0   Median :0.0000   Median :3.000   N.blank  :  0  
 Mean   :446.0   Mean   :0.3838   Mean   :2.309   Min.nchar: 12  
 3rd Qu.:668.5   3rd Qu.:1.0000   3rd Qu.:3.000   Max.nchar: 82  
 Max.   :891.0   Max.   :1.0000   Max.   :3.000                  
                                                                 
        Sex           Age            SibSp           Parch       
 Length   :891   Min.   : 0.42   Min.   :0.000   Min.   :0.0000  
 N.unique :  2   1st Qu.:20.12   1st Qu.:0.000   1st Qu.:0.0000  
 N.blank  :  0   Median :28.00   Median :0.000   Median :0.0000  
 Min.nchar:  4   Mean   :29.70   Mean   :0.523   Mean   :0.3816  
 Max.nchar:  6   3rd Qu.:38.00   3rd Qu.:1.000   3rd Qu.:0.0000  
                 Max.   :80.00   Max.   :8.000   Max.   :6.0000  
                 NAs    :177                                     
       Ticket         Fare              Cabin          Embarked  
 Length   :891   Min.   :  0.00   Length   :891   Length   :891  
 N.unique :681   1st Qu.:  7.91   N.unique :148   N.unique :  4  
 N.blank  :  0   Median : 14.45   N.blank  :687   N.blank  :  2  
 Min.nchar:  3   Mean   : 32.20   Min.nchar:  0   Min.nchar:  0  
 Max.nchar: 18   3rd Qu.: 31.00   Max.nchar: 15   Max.nchar:  1  
                 Max.   :512.33                                  
                                                                 
Titanic <- titanic_train[,c("Pclass","Age","Sex","Survived")]

Titanic$Survived <- as.factor(ifelse(Titanic$Survived==0, "Murio", "Sobrevive"))
Titanic$Pclass <- as.factor(Titanic$Pclass)
Titanic$Sex <-  as.factor(Titanic$Sex)
str(Titanic)
'data.frame':   891 obs. of  4 variables:
 $ Pclass  : Factor w/ 3 levels "1","2","3": 3 1 3 1 3 3 1 3 3 2 ...
 $ Age     : num  22 38 26 35 35 NA 54 2 27 14 ...
 $ Sex     : Factor w/ 2 levels "female","male": 2 1 1 1 2 2 2 2 1 1 ...
 $ Survived: Factor w/ 2 levels "Murio","Sobrevive": 1 2 2 2 1 1 1 1 2 2 ...
sum(is.na(Titanic))
[1] 177
sapply(Titanic, function(x) sum(is.na(x)))
  Pclass      Age      Sex Survived 
       0      177        0        0 
TitanicData <- na.omit(Titanic)

Dividir los datos en conjunto de entrenamiento y prueba

library(caret)
set.seed(2025)

folds <- createFolds(TitanicData$Survived, k = 5)
Titanic <- TitanicData[-folds[[5]],]
pruebatitanic  <- TitanicData[folds[[5]],]

Construimos el modelo ajustado con los datos de entrenamiento. Para este ejemplo utilizaremos el árbol CART.

library(rpart)
modelotitanic <- rpart(Survived ~ ., data = Titanic, method = "class")
modelotitanic
n= 571 

node), split, n, loss, yval, (yprob)
      * denotes terminal node

 1) root 571 232 Murio (0.59369527 0.40630473)  
   2) Sex=male 361  72 Murio (0.80055402 0.19944598)  
     4) Age>=5.5 340  58 Murio (0.82941176 0.17058824) *
     5) Age< 5.5 21   7 Sobrevive (0.33333333 0.66666667)  
      10) Pclass=3 11   4 Murio (0.63636364 0.36363636) *
      11) Pclass=1,2 10   0 Sobrevive (0.00000000 1.00000000) *
   3) Sex=female 210  50 Sobrevive (0.23809524 0.76190476)  
     6) Pclass=3 81  38 Murio (0.53086420 0.46913580)  
      12) Age>=38.5 12   1 Murio (0.91666667 0.08333333) *
      13) Age< 38.5 69  32 Sobrevive (0.46376812 0.53623188)  
        26) Age>=5.5 57  28 Murio (0.50877193 0.49122807)  
          52) Age< 12 7   0 Murio (1.00000000 0.00000000) *
          53) Age>=12 50  22 Sobrevive (0.44000000 0.56000000) *
        27) Age< 5.5 12   3 Sobrevive (0.25000000 0.75000000) *
     7) Pclass=1,2 129   7 Sobrevive (0.05426357 0.94573643) *

Validar la estabilidad del modelo

No se aplico en este ejemplo, pero se podría hacer la validación haciendo 5 replicas del proceso, tomando en cada iteración como conjunto de entrenamiento cada una de las particiones creadas.

Interpretación de los resultados finales

Podemos observar en la tabla, en las filas la clasificación real y en las columnas la predicción. La tasa de aciertos del modelo respecto a las observaciones de entrenamiento sería:

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

pred<- predict(modelotitanic,Titanic[,-4],type="class") 
tt<- table(pred,Titanic[,4]) 
tt 
           
pred        Murio Sobrevive
  Murio       307        63
  Sobrevive    32       169
TA <- sum(diag(tt))/sum(tt)  
paste0("Tasa de aciertos con los datos de entrenamiento: ", round(TA,4)*100, "%", sep="")
[1] "Tasa de aciertos con los datos de entrenamiento: 83.36%"

Podemos hablar de una precisión de aproximadamente el 83.36%. Ahora, para analizar la calidad del modelo, veamos que tan buena es la predicción para el conjunto de prueba.

Pred    <- predict(modelotitanic, pruebatitanic[,-4],type="class")
table   <- table(pruebatitanic[,4],Pred)
table
           Pred
            Murio Sobrevive
  Murio        72        13
  Sobrevive    21        37
TAP <- sum(diag(table))/sum(table) 
paste0("Tasa de aciertos con los datos de prueba: ", round(TAP,4)*100, "%", sep="")
[1] "Tasa de aciertos con los datos de prueba: 76.22%"

Encontramos que el modelo logra predecir aproximadamente un 76% los datos de prueba.

rpart.plot(
  modelotitanic, 
  digits = -4,             # Positivo para mostrar decimales correctos (ej. 3 decimales)
  type = 2,
  extra = 104,            # Muestra la clase predicha, n y las probabilidades de cada clase
  box.palette = "RdYlGn", 
  fallen.leaves = TRUE, 
  branch.lty = 3,        
  main = "Modelo de Árbol de Decisión: Predicción de Descuento"
)

pruebatitanic
    Pclass   Age    Sex  Survived
3        3 26.00 female Sobrevive
23       3 15.00 female Sobrevive
25       3  8.00 female     Murio
38       3 21.00   male     Murio
42       2 27.00 female     Murio
52       3 21.00   male     Murio
61       3 22.00   male     Murio
68       3 19.00   male     Murio
73       2 21.00   male     Murio
76       3 25.00   male     Murio
82       3 29.00   male Sobrevive
84       1 28.00   male     Murio
107      3 21.00 female Sobrevive
112      3 14.50 female     Murio
114      3 20.00 female     Murio
120      3  2.00 female     Murio
124      2 32.50 female Sobrevive
125      1 54.00   male     Murio
132      3 20.00   male     Murio
135      2 25.00   male     Murio
137      1 19.00 female Sobrevive
142      3 22.00 female Sobrevive
147      3 27.00   male Sobrevive
149      2 36.50   male     Murio
150      2 42.00   male     Murio
154      3 40.50   male     Murio
156      1 51.00   male     Murio
163      3 26.00   male     Murio
166      3  9.00   male Sobrevive
171      1 61.00   male     Murio
190      3 36.00   male     Murio
198      3 42.00   male     Murio
208      3 26.00   male Sobrevive
209      3 16.00 female Sobrevive
218      2 42.00   male     Murio
220      2 30.00   male     Murio
222      2 27.00   male     Murio
223      3 51.00   male     Murio
227      2 19.00   male Sobrevive
229      2 18.00   male     Murio
235      2 24.00   male     Murio
238      2  8.00 female Sobrevive
239      2 19.00   male     Murio
243      2 29.00   male     Murio
244      3 22.00   male     Murio
250      2 54.00   male     Murio
253      1 62.00   male     Murio
254      3 30.00   male     Murio
260      2 50.00 female Sobrevive
262      3  3.00   male Sobrevive
276      1 63.00 female Sobrevive
284      3 19.00   male Sobrevive
286      3 33.00   male     Murio
289      2 42.00   male Sobrevive
294      3 24.00 female     Murio
295      3 24.00   male     Murio
311      1 24.00 female Sobrevive
328      2 36.00 female Sobrevive
334      3 16.00   male     Murio
339      3 45.00   male Sobrevive
340      1 45.00   male     Murio
344      2 25.00   male     Murio
366      3 30.00   male     Murio
370      1 24.00 female Sobrevive
371      1 25.00   male Sobrevive
379      3 20.00   male     Murio
381      1 42.00 female Sobrevive
400      2 28.00 female Sobrevive
401      3 39.00   male Sobrevive
404      3 28.00   male     Murio
405      3 20.00 female     Murio
406      2 34.00   male     Murio
423      3 29.00   male     Murio
439      1 64.00   male     Murio
454      1 49.00   male Sobrevive
456      3 29.00   male Sobrevive
461      1 48.00   male Sobrevive
462      3 34.00   male     Murio
464      2 48.00   male     Murio
468      1 56.00   male     Murio
470      3  0.75 female Sobrevive
502      3 21.00 female     Murio
509      3 28.00   male     Murio
515      3 24.00   male     Murio
517      2 34.00 female Sobrevive
519      2 36.00 female Sobrevive
522      3 22.00   male     Murio
540      1 22.00 female Sobrevive
541      1 36.00 female Sobrevive
547      2 19.00 female Sobrevive
549      3 33.00   male     Murio
559      1 39.00 female Sobrevive
563      2 28.00   male     Murio
567      3 19.00   male     Murio
568      3 29.00 female     Murio
572      1 53.00 female Sobrevive
576      3 19.00   male     Murio
581      2 25.00 female Sobrevive
582      1 39.00 female Sobrevive
605      1 35.00   male Sobrevive
606      3 36.00   male     Murio
609      2 22.00 female Sobrevive
610      1 40.00 female Sobrevive
627      2 57.00   male     Murio
632      3 51.00   male     Murio
636      2 28.00 female Sobrevive
655      3 18.00 female     Murio
667      2 25.00   male     Murio
674      2 31.00   male Sobrevive
682      1 27.00   male Sobrevive
701      1 18.00 female Sobrevive
708      1 42.00   male Sobrevive
716      3 19.00   male     Murio
734      2 23.00   male     Murio
735      2 23.00   male     Murio
742      1 36.00   male     Murio
746      1 70.00   male     Murio
752      3  6.00   male Sobrevive
760      1 33.00 female Sobrevive
763      3 20.00   male Sobrevive
768      3 30.50 female     Murio
770      3 32.00   male     Murio
773      2 57.00 female     Murio
775      2 54.00 female Sobrevive
776      3 18.00   male     Murio
778      3  5.00 female Sobrevive
785      3 25.00   male     Murio
796      2 39.00   male     Murio
797      1 49.00 female Sobrevive
817      3 23.00 female     Murio
820      3 10.00   male     Murio
831      3 15.00 female Sobrevive
834      3 23.00   male     Murio
842      2 16.00   male     Murio
846      3 42.00   male     Murio
851      3  4.00   male     Murio
858      1 51.00   male Sobrevive
866      2 42.00 female Sobrevive
871      3 26.00   male     Murio
876      3 15.00 female Sobrevive
880      1 56.00 female Sobrevive
883      3 22.00 female     Murio
891      3 32.00   male     Murio