Librerias necesarias

library(readxl) # Importar datos de excel 
library(skimr) # Resumen estadísticos
library(readr) # Importar datos csv
library(caret) # Dividir en conjunto de entrenamiento y prueba 
library(partykit) # Arboles de decisión 
library(e1071) # Matriz de confusión 
library(ROCR) # Curva ROC
library(rpart) # Arbol de decision Particionamiento recursivo
library(rpart.plot) # Graficos para rpart

1) Analisis Exploratorio De Datos (EDA)

1.1) Descripcion de las variables

Variable Ejemplo Descripción
ID 1 Identificador único de cada registro.
Program.Code HD Código granular que describe el destino y las actividades del viaje (por ejemplo, “HD” para un programa de historia).
FromGrade 8 El grado más bajo en la escuela de un participante en el programa.
ToGrade 8 El grado más alto en la escuela de un participante en el programa.
GroupState IN Abreviatura del estado donde se ubica la escuela de origen. “OTHER” se usa para geografías raras.
IsNonAnnual 1 Indica si el grupo de esta escuela típicamente salta un año entre programas.
Days 3 Número de días que el grupo estuvo en el programa con uno de los instructores.
TransportType A Modo de transporte desde la ubicación de origen al punto de inicio (A = Avión, B = Bus, T = Tren).
SpecialPay NA Indica si el maestro recauda todo el dinero para remitirlo a EduTravel en lugar de hacerlo individualmente.
Price 1174 Precio pagado por participante para asistir al programa.
FRP.Active 72 Número de participantes que compraron seguro de cancelación del viaje.
Income.Level P Indicador del nivel de ingresos de los padres (A = bajo, Q = alto, Z = no clasificado).
EZ.Pay 0.2286 Porcentaje de participantes que se inscriben en un plan de pago automático.
School.Sponsor 0 Indicador binario de si la escuela patrocinó oficialmente el viaje.
Product.Type East Coast Nivel de agrupación del tipo de programa turístico.
New.Existing EXISTING Indica si el grupo ha viajado previamente con EduTravel (“NEW” si no lo ha hecho antes).
FPP 105 Número total de participantes que pagaron completamente para asistir al programa.
Group.Revenue 125735.4 Monto total pagado por todos los participantes del grupo.
Meetings 0 Número de reuniones con los padres antes del viaje.
GradeLow Elementary Tipo de grado más bajo de la escuela.
GradeHigh Elementary Tipo de grado más alto de la escuela.
DepartureMonth January Mes de partida.
Poverty.Code A Código de pobreza del área de origen (A = bajo porcentaje bajo la línea de pobreza, D = alto).
CRM Segment 1 Tipo de segmento de cliente definido en el sistema CRM (números entre 1 y 11).
SchoolType PUBLIC Indica si la escuela es pública o privada.
Meeting.Flag 1 Indica si hubo una reunión con los padres, señalando compromiso con el viaje.
Low.Grade 7 Grado más bajo en la escuela de origen.
High.Grade 8 Grado más alto en la escuela de origen.
School.Enrollment 955 Total de estudiantes inscritos en la escuela.
MajorProgramCode H Agregación del código del programa; la primera letra del código granular del programa.
SingleGrade 1 Indicador de si el viaje incluye estudiantes del mismo grado.
FPP.enrollment 0.06346 Proporción de participantes en el programa respecto al total de la inscripción escolar.
FPP.PAX 0.93650794 Proporción de participantes que pagaron completamente en comparación con el total de pasajeros.
Size L Tamaño de la escuela (S, M, L, S-M, M-L) por quintiles de tamaño.
Retained.in.2020 1 Indicador de éxito (1/0): ¿el grupo volvió al año siguiente?

1.2) Resumen Estadisticos

skim(edutravel)
Data summary
Name edutravel
Number of rows 2389
Number of columns 41
_______________________
Column type frequency:
character 16
numeric 25
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
Program.Code 0 1.00 2 3 0 28 0
GroupState 0 1.00 2 14 0 54 0
TransportType 0 1.00 1 1 0 4 0
SpecialPay 1919 0.20 2 2 0 3 0
Poverty.Code 599 0.75 1 1 0 6 0
Region 0 1.00 5 19 0 6 0
SchoolType 0 1.00 3 21 0 4 0
Low.Grade 68 0.97 1 2 0 12 0
Income.Level 62 0.97 1 2 0 22 0
Product.Type 0 1.00 7 13 0 6 0
New.Existing 0 1.00 3 8 0 2 0
GradeLow 0 1.00 4 10 0 4 0
GradeHigh 0 1.00 4 10 0 4 0
DepartureMonth 0 1.00 3 8 0 6 0
AggregatedCode 0 1.00 1 1 0 4 0
Size 91 0.96 1 3 0 4 0

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
FromGrade 127 0.95 7.27 1.39 3.0 7.00 8.00 8.00 12.00 ▁▂▇▁▁
ToGrade 150 0.94 7.91 1.56 3.0 8.00 8.00 8.00 12.00 ▁▁▇▁▁
IsNonAnnual 0 1.00 0.15 0.36 0.0 0.00 0.00 0.00 1.00 ▇▁▁▁▂
Days 0 1.00 4.58 1.43 1.0 4.00 5.00 5.00 12.00 ▂▇▂▁▁
Price 0 1.00 1615.22 645.10 79.0 1174.00 1700.00 2048.00 4200.00 ▃▆▇▁▁
FRP.Active 0 1.00 16.87 16.94 0.0 6.00 12.00 23.00 257.00 ▇▁▁▁▁
FRP.Cancelled 0 1.00 3.31 3.68 0.0 1.00 2.00 4.00 45.00 ▇▁▁▁▁
FRP.Percentage 0 1.00 0.57 0.23 0.0 0.46 0.60 0.73 1.00 ▂▂▇▇▃
Cancelled.Pax 0 1.00 4.81 4.66 0.0 2.00 4.00 6.00 39.00 ▇▂▁▁▁
Extra.Pax 0 1.00 2.95 2.88 0.0 1.00 2.00 4.00 47.00 ▇▁▁▁▁
CRMSegment 4 1.00 6.92 2.75 1.0 5.00 6.00 10.00 11.00 ▁▇▂▁▇
Meeting.Flag 0 1.00 0.86 0.35 0.0 1.00 1.00 1.00 1.00 ▁▁▁▁▇
High.Grade 68 0.97 8.39 1.75 1.0 8.00 8.00 8.00 12.00 ▁▁▁▇▂
School.Enrollment 91 0.96 648.36 411.73 19.0 360.00 597.00 825.75 3990.00 ▇▂▁▁▁
EZ.Pay 0 1.00 0.21 0.16 0.0 0.10 0.20 0.29 1.75 ▇▂▁▁▁
School.Sponsor 0 1.00 0.11 0.31 0.0 0.00 0.00 0.00 1.00 ▇▁▁▁▁
FPP 0 1.00 31.30 29.13 2.0 12.00 23.00 41.00 286.00 ▇▁▁▁▁
Group.Revenue 0 1.00 1615.22 645.10 79.0 1174.00 1700.00 2048.00 4200.00 ▃▆▇▁▁
Meetings 0 1.00 1.10 0.61 0.0 1.00 1.00 1.00 2.00 ▂▁▇▁▃
FirstMeeting 337 0.86 262.08 79.52 -204.0 208.00 250.00 287.00 749.00 ▁▁▇▂▁
LastMeeting 337 0.86 228.98 53.64 -204.0 196.75 233.00 261.00 749.00 ▁▁▇▁▁
SingleGrade 0 1.00 0.56 0.50 0.0 0.00 1.00 1.00 1.00 ▆▁▁▁▇
FPP.Enrollment 91 0.96 0.07 0.08 0.0 0.02 0.05 0.09 2.05 ▇▁▁▁▁
FPP.Pax 0 1.00 0.90 0.05 0.6 0.88 0.91 0.93 1.00 ▁▁▁▇▆
Retained.in.2020 0 1.00 0.61 0.49 0.0 0.00 1.00 1.00 1.00 ▅▁▁▁▇

1.3) Transformacion de tipo variable

edutravel$Program.Code <- as.factor(edutravel$Program.Code)
edutravel$FromGrade <- as.factor(edutravel$FromGrade)
edutravel$ToGrade <- as.factor(edutravel$ToGrade)
edutravel$GroupState <- as.factor(edutravel$GroupState)
edutravel$IsNonAnnual <- as.factor(edutravel$IsNonAnnual)
edutravel$Days <- as.factor(edutravel$Days)
edutravel$TransportType <- as.factor(edutravel$TransportType)
edutravel$SpecialPay <- as.factor(edutravel$SpecialPay)
edutravel$Poverty.Code <- as.factor(edutravel$Poverty.Code)
edutravel$Region <- as.factor(edutravel$Region )
edutravel$CRMSegment <- as.factor(edutravel$CRMSegment)
edutravel$SchoolType <- as.factor(edutravel$SchoolType)
edutravel$Meeting.Flag <- as.factor(edutravel$Meeting.Flag)
edutravel$Income.Level <- as.factor(edutravel$Income.Level)
edutravel$Product.Type <- as.factor(edutravel$Product.Type)
edutravel$New.Existing <- as.factor(edutravel$New.Existing)
edutravel$GradeLow <- as.factor(edutravel$GradeLow)
edutravel$GradeHigh <- as.factor(edutravel$GradeHigh)
edutravel$DepartureMonth <- as.factor(edutravel$DepartureMonth)
edutravel$Meetings <- as.factor(edutravel$Meetings)
edutravel$AggregatedCode <- as.factor(edutravel$AggregatedCode)
edutravel$SingleGrade <- as.factor(edutravel$SingleGrade)
edutravel$Size <- as.factor(edutravel$Size)
edutravel$Low.Grade <- as.factor(edutravel$Low.Grade)
edutravel$Low.High <- as.factor(edutravel$High.Grade)
edutravel$Retained.in.2020 <- as.factor(edutravel$Retained.in.2020) 

1.4) Manejo de datos faltantes

# definimos funcion 
FillNAs<-function(data_frame){ 
  fill_number<-0 
  fill_factor<-"NA_filled" 
  fill_character<-"NA_filled"
  fill_date<-as.Date("1900-01-01")
  
  
  
  # Make a loop in the columns of the data frame and according to the 
  # data type, fill the respective value and create a surrogate column
  
  for (i in 1 : ncol(data_frame)){
    if (class(data_frame[,i]) %in% c("numeric","integer")) { 
      if (any(is.na(data_frame[,i]))){
        data_frame[,paste0(colnames(data_frame)[i],"_filledNA")]<- as.factor(ifelse(is.na(data_frame[,i]),"1","0"))
        data_frame[is.na(data_frame[,i]),i]<-fill_number
      }
    } else
      if (class(data_frame[,i]) %in% c("factor")) { 
        if (any(is.na(data_frame[,i]))){
          data_frame[,i]<-as.character(data_frame[,i]) 
          data_frame[,paste0(colnames(data_frame)[i],"_filledNA")]<-as.factor(ifelse(is.na(data_frame[,i]),"1","0")) 
          data_frame[is.na(data_frame[,i]),i]<-fill_factor
          data_frame[,i]<-as.factor(data_frame[,i])
          
        }
      } else {
        if (class(data_frame[,i]) %in% c("character")) { 
          if (any(is.na(data_frame[,i]))){
            data_frame[,paste0(colnames(data_frame)[i],"_filledNA")]<- as.factor(ifelse(is.na(data_frame[,i]),"1","0"))
            data_frame[is.na(data_frame[,i]),i]<-afill_character
          }
        } else {
          if (class(data_frame[,i]) %in% c("Date")) {
            if (any(is.na(data_frame[,i]))){ 
              data_frame[,paste0(colnames(data_frame)[i],"_filledNA")]<-as.factor(ifelse(is.na(data_frame[,i]),"1","0")) 
              data_frame[is.na(data_frame[,i]),i]<-fill_date
            }
          }
        }
      }
  }
  return(data_frame)
}



# aplicamos función
edutravelNA <-FillNAs(edutravel)

# combinamos categorías

1.5) Manejo de datos categoricos con pocas observaciones

El propósito de esta función, en un contexto práctico, es simplificar y mejorar la calidad de los análisis de datos categóricos, especialmente en situaciones donde:

Reducción de complejidad en las categorías:

En un dataset con muchas categorías, algunas pueden tener muy pocas observaciones, lo que hace que los análisis sean menos significativos y más complejos de interpretar. Combinar estas categorías poco representadas en una genérica llamada “Others” ayuda a reducir el ruido en los datos y enfocar los análisis en las categorías principales. Facilitar la visualización de datos:

Al crear gráficos (por ejemplo, gráficos de barras, de pastel o tablas resumen), un exceso de categorías hace que la visualización sea desordenada y difícil de leer. Agrupar las categorías con pocos datos bajo “Others” mejora la claridad visual y destaca las categorías más relevantes. Mejorar los modelos estadísticos o de machine learning:

En modelos de regresión, clasificación o cualquier algoritmo que utilice variables categóricas, demasiadas categorías con baja frecuencia pueden llevar a problemas de sobreajuste (overfitting) o resultados inestables. Agrupar las categorías pequeñas en “Others” permite que los modelos se centren en las categorías más significativas, mejorando su rendimiento. Normalización de los datos para reportes:

En informes o reportes donde se presenten resultados agrupados, es común agrupar valores menores en una categoría “Otros” para que los resultados sean más concisos y comprensibles. Análisis exploratorio de datos:

Durante el análisis inicial de un dataset, agrupar categorías poco representadas facilita la identificación de patrones significativos en las categorías principales sin distracciones innecesarias.

### Combining Categories

# Definimos funcion
CombineCategories <-function(data_frame,mincount){ 
  for (i in 1 : ncol(data_frame)){
    a<-data_frame[,i]
    replace <- names(which(table(a) < mincount))
    levels(a)[levels(a) %in%    replace]    <- paste("Others",colnames(data_frame)[i],sep=".")
    
    
    
    data_frame[,i]<-a } 
  return(data_frame) 
}

# aplicamos funcion
# dataframe with a mincount of 10
head(CombineCategories(edutravelNA, 10))
##   Program.Code FromGrade ToGrade GroupState IsNonAnnual Days
## 1           HS         4       4         CA           0    1
## 2           HC         8       8         AZ           0    7
## 3           HD         8       8         FL           0    3
## 4           HN         9      12         VA           1    3
## 5           HD         6       8         FL           0    6
## 6           HC        10      12         LA           0    4
##          TransportType SpecialPay Price FRP.Active FRP.Cancelled FRP.Percentage
## 1                    A  NA_filled   424         25             3          0.424
## 2                    A         CP  2350          9             9          0.409
## 3                    A  NA_filled  1181         17             6          0.708
## 4                    B  NA_filled   376          0             0          0.000
## 5 Others.TransportType  NA_filled   865         40             8          0.494
## 6                    A  NA_filled  2025          9             4          0.900
##   Cancelled.Pax Extra.Pax Poverty.Code              Region CRMSegment
## 1             3         4            B Southern California          4
## 2            11         3            C               Other         10
## 3             6         3            C               Other         10
## 4             1         0    NA_filled               Other          7
## 5             9         8            D               Other         10
## 6             3         1            C               Other          8
##   SchoolType Meeting.Flag        Low.Grade High.Grade School.Enrollment
## 1     PUBLIC            1                K          5               927
## 2     PUBLIC            1                7          8               850
## 3     PUBLIC            1                6          8               955
## 4        CHD            0        NA_filled          0                 0
## 5     PUBLIC            1                6          8               720
## 6     PUBLIC            1 Others.Low.Grade         12               939
##   Income.Level EZ.Pay School.Sponsor Product.Type New.Existing FPP
## 1            Q  0.170              1   CA History     EXISTING  59
## 2            A  0.091              0   East Coast     EXISTING  22
## 3            O  0.042              0   East Coast     EXISTING  24
## 4    NA_filled  0.000              0   East Coast     EXISTING  18
## 5            C  0.383              0   East Coast     EXISTING  81
## 6            I  0.100              0   East Coast          NEW  10
##   Group.Revenue Meetings FirstMeeting LastMeeting   GradeLow  GradeHigh
## 1           424        1          155         155 Elementary Elementary
## 2          2350        2          423         140     Middle     Middle
## 3          1181        1          124         124     Middle     Middle
## 4           376        0            0           0       High       High
## 5           865        1          145         145     Middle     Middle
## 6          2025        1           91          91       High       High
##          DepartureMonth AggregatedCode SingleGrade FPP.Enrollment   FPP.Pax
## 1 Others.DepartureMonth              H           1     0.06364617 0.9365079
## 2 Others.DepartureMonth              H           1     0.02588235 0.8800000
## 3 Others.DepartureMonth              H           1     0.02513089 0.8888889
## 4 Others.DepartureMonth              H           0     0.00000000 1.0000000
## 5 Others.DepartureMonth              H           0     0.11250000 0.9101124
## 6 Others.DepartureMonth              H           0     0.01064963 0.9090909
##        Size Retained.in.2020  Low.High FromGrade_filledNA ToGrade_filledNA
## 1         L                1         5                  0                0
## 2         L                1         8                  0                0
## 3         L                1         8                  0                0
## 4 NA_filled                0 NA_filled                  0                0
## 5       M-L                0         8                  0                0
## 6         L                1        12                  0                0
##   SpecialPay_filledNA Poverty.Code_filledNA CRMSegment_filledNA
## 1                   1                     0                   0
## 2                   0                     0                   0
## 3                   1                     0                   0
## 4                   1                     1                   0
## 5                   1                     0                   0
## 6                   1                     0                   0
##   Low.Grade_filledNA High.Grade_filledNA School.Enrollment_filledNA
## 1                  0                   0                          0
## 2                  0                   0                          0
## 3                  0                   0                          0
## 4                  1                   1                          1
## 5                  0                   0                          0
## 6                  0                   0                          0
##   Income.Level_filledNA FirstMeeting_filledNA LastMeeting_filledNA
## 1                     0                     0                    0
## 2                     0                     0                    0
## 3                     0                     0                    0
## 4                     1                     1                    1
## 5                     0                     0                    0
## 6                     0                     0                    0
##   FPP.Enrollment_filledNA Size_filledNA Low.High_filledNA
## 1                       0             0                 0
## 2                       0             0                 0
## 3                       0             0                 0
## 4                       1             1                 1
## 5                       0             0                 0
## 6                       0             0                 0

1.6) Separamos en conjunto de prueba y entrenamiento

# indicamos semilla para consistencia
set.seed(2021) 

# separamos datos (80% entrenamiento y 20% prueba)
Partition <- createDataPartition(y = edutravelNA$Retained.in.2020, 
                                 p = 0.8, list = FALSE)

# sepramos en objetos
training <- edutravelNA[ Partition,] 
testing <- edutravelNA[ -Partition,]

2) Modelamos arboles de decisión con inferencia condicional “CTREE”

2.1) Estructuramos modelo para toma de decisiones

Cómo leer el gráfico del árbol de decisión generado por CTREE

El gráfico representa un árbol de decisión que muestra cómo las variables predictoras explican la variable respuesta (Retained.in.2020, en este caso, la retención de grupos al siguiente año). Cada componente del árbol tiene un significado específico. A continuación, se explica cómo interpretar sus elementos:

Componentes del gráfico:

  1. Nodos de decisión (óvalos):
    • Los óvalos representan las variables predictoras que el árbol utiliza para dividir los datos.
    • Cada nodo indica:
      • Variable utilizada: Por ejemplo, SingleGrade, IsNonAnnual, New.Existing, etc.
      • Valor de p: Indica la significancia estadística de la división (entre más pequeño, más relevante es la variable para predecir la retención).
    • La rama izquierda representa los valores falsos o negativos (como 0 o “NO”).
    • La rama derecha representa los valores verdaderos o positivos (como 1 o “SÍ”).
  2. Nodos terminales (rectángulos):
    • Estos son los puntos finales del árbol, donde se muestra la proporción de éxito (retención = 1) y fracaso (retención = 0) dentro del grupo correspondiente.
    • Cada nodo terminal incluye:
      • Número de observaciones (n): Cantidad de grupos que caen en esa categoría.
      • Barras de proporción: Representan la fracción de casos en las categorías de la variable respuesta:
        • Barra gris (inferior): Proporción de casos con retención = 0 (no retenidos).
        • Barra negra (superior): Proporción de casos con retención = 1 (retenidos).
  3. Barras de proporción:
    • Cada nodo terminal muestra un gráfico de barras que visualiza las probabilidades para cada valor de la variable respuesta:
      • Mayor barra negra → Más casos de éxito (retención).
      • Mayor barra gris → Más casos de fracaso (no retención).
  4. Divisiones de las ramas:
    • Las ramas conectan los nodos y representan las decisiones basadas en el valor de la variable predictora en el nodo actual.
    • Ejemplo: Si SingleGrade = 1, el árbol sigue por la rama derecha; si SingleGrade = 0, sigue por la rama izquierda.

Ejemplo de lectura del gráfico:

  1. Nodo raíz (SingleGrade):
    • La variable SingleGrade divide los datos en dos grandes ramas:
      • Rama izquierda: Grupos con múltiples grados (SingleGrade = 0).
      • Rama derecha: Grupos con un solo grado (SingleGrade = 1).
  2. Rama izquierda (IsNonAnnual):
    • Dentro de los grupos con múltiples grados, se evalúa si el grupo participa en un programa no anual (IsNonAnnual):
      • Rama izquierda: Grupos anuales (IsNonAnnual = 0).
      • Rama derecha: Grupos no anuales (IsNonAnnual = 1).
  3. Nodo terminal (Nodo 4):
    • Si SingleGrade = 0, IsNonAnnual = 0, y el grupo es EXISTING (New.Existing = EXISTING), los grupos terminan en el Nodo 4.
    • Proporción de éxito:
      • La barra negra representa la proporción de grupos retenidos.
      • La barra gris representa la proporción de grupos no retenidos.
      • Por ejemplo, si n = 261 y la barra negra ocupa el 80%, aproximadamente 209 grupos fueron retenidos.
  4. Rama derecha (New.Existing):
    • Si el grupo tiene un solo grado (SingleGrade = 1) y participa en un programa anual, se evalúa si el grupo es nuevo o existente:
      • Grupos EXISTING: Tienen nodos con probabilidades más altas de retención.
      • Grupos NEW: Tienen divisiones adicionales según otras variables, como FRP.Active.

Pasos para interpretar cualquier nodo:

  1. Identifica la variable en el óvalo:
    • ¿Qué característica del grupo se está evaluando?
    • ¿Qué valores (0 o 1, EXISTING o NEW) siguen hacia cada rama?
  2. Sigue las ramas:
    • Decide por cuál rama seguir en función del valor de la variable.
  3. Lee los nodos terminales:
    • Mira el tamaño del nodo (n) y las barras de proporción para entender la probabilidad de éxito o fracaso en retención.

Ejemplo práctico:

  • Un grupo con:
    • SingleGrade = 0 (múltiples grados).
    • IsNonAnnual = 0 (programa anual).
    • New.Existing = NEW (nuevo grupo).
    • FRP.Active > 14 (muchos seguros comprados).
  • Terminaría en el Nodo 7:
    • Según las barras, tendría una alta probabilidad de retención (mayor proporción de barra negra).

Notas finales:

- El árbol prioriza divisiones que maximicen la diferencia en probabilidades entre los grupos.

  • Las variables cercanas al nodo raíz tienen mayor importancia en la predicción.
# indicmaos modelo
tree.ctree = ctree(training$Retained.in.2020 ~ .-GroupState,data=training)

# graficamos 
# Now let's graph and adjust the font size
plot(tree.ctree, gp = gpar(fontsize = 7)) 

Interpretación del árbol de decisión (CTREE)

El árbol de decisión generado con el paquete CTREE y basado en la variable de respuesta Retained.in.2020 (indicador binario de éxito de retención al año siguiente) analiza cómo las variables predictoras afectan esta retención. A continuación, una interpretación detallada:

Nodo Raíz:

1. SingleGrade (p < 0.001): - El modelo evalúa si el grupo contiene estudiantes de un solo grado (SingleGrade) como primer punto de división.

  • - Si es 1 (grupo de un solo grado), se mueve a la rama derecha.
    • Si es 0 (grupo con múltiples grados), se mueve a la rama izquierda.

Rama Izquierda (Grupos con múltiples grados):

  1. IsNonAnnual (p < 0.001):
    • Aquí, el árbol evalúa si el grupo participa en un programa no anual (IsNonAnnual).
  • - Si es 1 (programa no anual), se mueve a la derecha.

  • - Si es 0 (programa anual), se mueve a la izquierda.

  1. New.Existing (p < 0.001):
    • Para los programas anuales, el árbol evalúa si el grupo es nuevo (NEW) o ya ha participado previamente (EXISTING).

    • Grupos EXISTING:

      • Se pasa al nodo terminal (Nodo 4).
      • Aquí, la probabilidad de retención se calcula directamente, con proporciones altas de éxito.
    • Grupos NEW:

      • Se evalúa FRP.Active (p = 0.015):
        • Si el número de participantes que compraron un seguro es menor o igual a 14, se mueve al Nodo 6 (probabilidad de retención más baja).
        • Si es mayor a 14, se mueve al Nodo 7 (probabilidad de retención más alta).
  2. Nodo Terminal:
    • Los nodos terminales (4, 6, 7, etc.) muestran barras con proporciones de éxito (1) y fracaso (0) en retención.

Rama Derecha (Grupos de un solo grado): 5. IsNonAnnual (p < 0.001): - Evalúa nuevamente si el programa es no anual.

  1. New.Existing (p < 0.001):
    • Análisis similar para grupos de programas no anuales.

    • Grupos EXISTING:

      • Se evalúa FRP.Active (p < 0.001):
        • Si el número es menor o igual a 14, se analiza el código de pobreza (Poverty.Code, p = 0.01).
          • Los niveles altos de pobreza tienden a reducir la retención (Nodo 13).
          • Los niveles bajos aumentan la retención (Nodo 14).
        • Si el número es mayor a 14, se mueve al Nodo 15.
    • Grupos NEW:

      • Similar a los existentes, la retención se evalúa según otros factores, como FRP.Active.

Conclusiones:

  1. Factores principales:

    - SingleGrade y IsNonAnnual son los primeros determinantes de retención.

  2. Impacto del seguro (FRP.Active):

    • Grupos con mayor proporción de participantes que compraron un seguro de cancelación tienen mayores probabilidades de retención.
  3. Pobreza (Poverty.Code):

    • Los grupos de áreas con menor pobreza tienen mejores tasas de retención, especialmente para programas existentes y con seguro activo.
  4. Programas no anuales:

    • Los programas no anuales parecen estar asociados con una retención menor en general cuando no gay single grade.

Sugerencias: Puedes usar esta información para diseñar estrategias focalizadas, como ofrecer incentivos adicionales a grupos nuevos o en áreas de mayor pobreza para mejorar la retención.

2.2) Modelamos arbol de decision para hacer predicciones

Para predecir la probabilidad usamos el modelo de árbol de decisión. Para ellos usamos el conjunto de datos de prueva y adicional, que queremos como outcome la probabilidad.

prob.ctree<-predict(tree.ctree,newdata=testing,type="prob") 
head(prob.ctree)
##             0         1
## 9  0.05991736 0.9400826
## 10 0.05991736 0.9400826
## 11 0.05991736 0.9400826
## 21 0.05991736 0.9400826
## 29 0.28352490 0.7164751
## 30 0.28352490 0.7164751

Con el anterior codigo creamos una tabla que tiene dos categorías:

  • 1: para aquellas observacionales con la probabilidad de de retención
    1. Para aquellas observaciones con la probabilidad de no retención

2.3) Clasificmaos datos de prueba

La anterior tabla de datos tiene 477 observaciones, las cuales clasificaremos según un umbral. Para ello, necesitamos el valor de corte. Generalmente, dicho umbral se calcula en función de una función de costos/utilidad.

Como no tenemos dicha función de costos/utilidad, calculemos el valor de corte como la probabilidad promedio de retención

mean(as.integer(edutravelNA$Retained.in.2020))
## [1] 1.607367

Podemos observar que la media es 1,607 lo que nos debe llamar la atención porque es mayor que 1. Cuando echamos un vistazo a la dependiente notamos que R guarda con los valores 1 y 2 en lugar de 0 y 1.

Así que todo lo que tenemos que hacer es restar 1 a la media,y obtenemos que la probabilidad media de ser retenido es 0.607, que es lo que usaremos como valor de corte

Primero vamos a crear un vector de 477 unos, el tamaño de la base de prueba

classification.ctree<- rep("1",477)
head(classification.ctree)
## [1] "1" "1" "1" "1" "1" "1"

Ahora asignaremos cero para aquellas observaciones con una retención menor a 0.607

classification.ctree[prob.ctree[,2]<0.607]= "0" 

2.4) Evaluacion con matriz de confusion

Primero convertirmeos el modelo a factoe, ya que la función que utilizaremos para crear la Matriz de Confusion requiere que los datos sean factores con el mismo numero de niveles, en nuestro caso dos nivels 0 y 1

classification.ctree<- as.factor(classification.ctree)

Aplicamos función para matriz de confusión

confusionMatrix(classification.ctree,testing$Retained.in.2020, positive = "1") 
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   0   1
##          0 138  37
##          1  49 253
##                                           
##                Accuracy : 0.8197          
##                  95% CI : (0.7822, 0.8532)
##     No Information Rate : 0.608           
##     P-Value [Acc > NIR] : <2e-16          
##                                           
##                   Kappa : 0.6174          
##                                           
##  Mcnemar's Test P-Value : 0.2356          
##                                           
##             Sensitivity : 0.8724          
##             Specificity : 0.7380          
##          Pos Pred Value : 0.8377          
##          Neg Pred Value : 0.7886          
##              Prevalence : 0.6080          
##          Detection Rate : 0.5304          
##    Detection Prevalence : 0.6331          
##       Balanced Accuracy : 0.8052          
##                                           
##        'Positive' Class : 1               
## 

2.5) Analisamos curva ROC

Cálculos de los errores:

Este paso básicamente almacena la información necesaria para evaluar qué tan bien las predicciones del modelo coinciden con los valores reales.

prediction.ctree.ROC    <-  prediction(prob.ctree[,2], testing$Retained.in.2020) 

La función prediction() crea un objeto que contiene las predicciones del modelo y los valores verdaderos. Este objeto es necesario para calcular métricas como la sensibilidad, la especificidad y la curva ROC.

Posteriormente evaluamos el desempeño: performance(): Calcula métricas de rendimiento del modelo.

Argumentos:

- prediction.ctree.ROC: Objeto con las predicciones y valores verdaderos.

- “tpr”: Tasa de verdaderos positivos (True Positive Rate, Sensibilidad). Se usará en el eje y.

- “fpr”: Tasa de falsos positivos (False Positive Rate, 1 - Especificidad). Se usará en el eje x.

ROC.ctree <- performance(prediction.ctree.ROC,"tpr","fpr") 

Graficamos datos

plot(ROC.ctree) 

La curva ROC muestra el equilibrio entre sensibilidad (o TPR) y especificidad (1 - FPR). Los clasificadores cuya curva se acerca más a la esquina superior izquierda indican un mejor rendimiento. Como referencia, se espera que un clasificador aleatorio tenga puntos a lo largo de la diagonal (FPR = TPR). Cuanto más cerca esté la curva ROC de la diagonal de 45 grados, menos preciso será el modelo. Aunque en este momento esta curva pueda no parecer significativa, será muy útil como herramienta de comparación cuando tengamos al menos dos modelos.

2.6) Calculo del área bajo la curva

Interpretación práctica del AUC

Guía para interpretar los valores del AUC:

  • Mayor al 90%: Excelente.
  • Entre 80% y 90%: Muy bueno.
  • Entre 70% y 80%: Bueno.
  • Entre 60% y 70%: Regular.
  • Menor al 60%: De poco valor.

El AUC es especialmente útil para comparar la precisión de modelos diferentes, como determinar si una regresión logística tiene un mejor rendimiento predictivo que un árbol de clasificación.

Calculamos el área bajo la curva usando la función `performance`, especificando que queremos calcular auc (area under the curve)

AUC.temp <- performance(prediction.ctree.ROC,"auc") 

Obtenemos el valor numérico indicando el y valor

Diferencia entre $ y @:

  1. $: Se utiliza para acceder a elementos en listas, dataframes o clases más simples.

  2. @: Se utiliza para acceder a slots en objetos de clases S4, que son más complejas.

AUC.ctree <- as.numeric(AUC.temp@y.values) 
AUC.ctree 
## [1] 0.8530242
print(paste("El área bajo la curva es", AUC.ctree*100,"%"))
## [1] "El área bajo la curva es 85.3024156371012 %"

3) Modelamos arboles de decisión con Particionamiento recursivo “RPART”:

cp (complexity parameter): Controla el grado de complejidad del árbol.

Interpretación: Este parámetro define el cambio mínimo en el criterio de ajuste (por ejemplo, Gini para clasificación) que debe ocurrir para que una división adicional se realice en el árbol.

Un valor más bajo de cp permite que el árbol sea más complejo (es decir, tenga más nodos), mientras que un valor más alto lo restringe y lo hace más simple.

3.1) Definimos un parametro de complejidad inicial

rpart.cp = rpart.control(cp = 0.0005)

3.2) Construimos un arbol incila con un valor de complejidad muy bajo

tree.rpart<-rpart(Retained.in.2020 ~.,data=training,    
                  method="class", control=rpart.cp) 
rpart.plot(tree.rpart)

Podemos observar que el nivel de complejidad del árbol impide la interpretación y fácilmente se podría pensar en que hay un sobre ajuste del modelo. Por tal motivo, debemos encontrar el valor optimo de cp

3.3) Escoger el valor mas optimo del parameto de complejidad (cp)

  • Primero, miramos el error para diferentes valor de cp
# Impriimos valores
printcp(tree.rpart) 
## 
## Classification tree:
## rpart(formula = Retained.in.2020 ~ ., data = training, method = "class", 
##     control = rpart.cp)
## 
## Variables actually used in tree construction:
##  [1] Cancelled.Pax     CRMSegment        DepartureMonth    Extra.Pax        
##  [5] FPP               FPP.Enrollment    FromGrade         FRP.Active       
##  [9] FRP.Percentage    GroupState        Income.Level      IsNonAnnual      
## [13] LastMeeting       Low.Grade         New.Existing      Price            
## [17] Program.Code      Region            School.Enrollment SingleGrade      
## [21] Size             
## 
## Root node error: 751/1912 = 0.39278
## 
## n= 1912 
## 
##            CP nsplit rel error  xerror     xstd
## 1  0.30359521      0   1.00000 1.00000 0.028435
## 2  0.07523302      1   0.69640 0.69640 0.025955
## 3  0.00985353      3   0.54594 0.54061 0.023812
## 4  0.00865513      8   0.49667 0.58855 0.024546
## 5  0.00732357     10   0.47936 0.59254 0.024604
## 6  0.00665779     12   0.46471 0.60053 0.024719
## 7  0.00621394     18   0.42477 0.60586 0.024794
## 8  0.00532623     23   0.39015 0.61252 0.024887
## 9  0.00399467     27   0.36884 0.62716 0.025088
## 10 0.00332889     30   0.35686 0.62850 0.025105
## 11 0.00310697     39   0.32490 0.63116 0.025141
## 12 0.00266312     42   0.31558 0.64048 0.025264
## 13 0.00199734     45   0.30759 0.66045 0.025520
## 14 0.00177541     47   0.30360 0.66312 0.025554
## 15 0.00133156     50   0.29827 0.66312 0.025554
## 16 0.00066578     52   0.29561 0.67643 0.025718
## 17 0.00050000     58   0.29161 0.67244 0.025669
# Graficamos valores
plotcp(tree.rpart)

Podemos observar que con cp=0.027se reduce el error antes de que vuelva a incrementar marginalmente.

  • Segundo, al arbol con sobajeaste, le indicamos su valor optimo de cp

Elementos del gráfico:

Colores:

  • Verde: Los nodos en los que predomina la clase 1.
  • Azul: Los nodos en los que predomina la clase 0.
pruned.tree <-prune(tree.rpart, cp=0.027) 
rpart.plot(pruned.tree)

El gráfico muestra un árbol de decisión optimizado con un parámetro de complejidad (cp=0.027) que clasifica instancias basándose en las variables SingleGrade, IsNonAnnual y New.Existing. La raíz del árbol divide los datos en función de si SingleGrade = 0 o no, donde el 61% de los datos iniciales pertenecen a la clase 1. A medida que se desciende por el árbol, las divisiones adicionales como IsNonAnnual = 1 y New.Existing = NEW permiten afinar las predicciones. Los nodos terminales muestran las probabilidades y proporciones de pertenencia a cada clase, con colores que indican la clase predominante (verde para 1 y azul para 0). Esto refleja cómo las combinaciones de variables afectan la probabilidad de pertenecer a una clase específica.

3.4) Evaluamos el modelo con la matriz de confusion

El siguiente código nos dará como resultado dos columnas. La columna cero es la probabilidad de no retención y la columna uno es la probabilidad de retención

prob.rpart<-predict(
  pruned.tree, # Idicamos el modelo de árbol creado
  newdata=testing, # Indicamos que es con los datos de prueba 
  type="prob" # Indicamos que queremos por tipo de probabilidad 
  )

Ahora, realizamos la calificación del árbol. Para ello creamos un vector de “1” que tenga la misma cantidad de observaciones que el conjunto de datos de prueba (En este caso 477)

classification.rpart<-rep("1",477) 

Posteriormente, asignamos los valores equivalentes a cero con base al umbral que habíamos construido anteriormente. Para ello, tomamos los valores de probabilidad respectivamente asignados, específicamente de la columna dos, que son los que indican “1” (retención)

classification.rpart[prob.rpart[,2]<0.607]="0"  

Para poder categorizar en la matriz de confusión es importante deja la variable en tipo factor:

classification.rpart<- as.factor(classification.rpart)

Ahora si elaboramos la matriz de confusión

confusionMatrix(classification.rpart, # indicamos los valores de probabilidad
                testing$Retained.in.2020, # indicamos los valores del conjunto de prueba
                positive = "1" # indicamos los caso positivos son para aquellos igual a 1
                ) 
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction   0   1
##          0 131  34
##          1  56 256
##                                           
##                Accuracy : 0.8113          
##                  95% CI : (0.7733, 0.8455)
##     No Information Rate : 0.608           
##     P-Value [Acc > NIR] : < 2e-16         
##                                           
##                   Kappa : 0.5957          
##                                           
##  Mcnemar's Test P-Value : 0.02686         
##                                           
##             Sensitivity : 0.8828          
##             Specificity : 0.7005          
##          Pos Pred Value : 0.8205          
##          Neg Pred Value : 0.7939          
##              Prevalence : 0.6080          
##          Detection Rate : 0.5367          
##    Detection Prevalence : 0.6541          
##       Balanced Accuracy : 0.7916          
##                                           
##        'Positive' Class : 1               
## 

3.5) Analizamos curva ROC

En esta parte vamos a graficar la curva ROC del modelo “rpart” y compararemos la curva con el modelo realizado con el modelo “ctree”.

Primero, calculamos el error del modelo “rpart

prediction.rpart.ROC    <-  prediction(prob.rpart[,2],  # Columna de unos
                                   testing$Retained.in.2020) 

Segundo, estimamos los valores de la curva ROC

ROC.rpart <- performance(prediction.rpart.ROC, # errores de la clasificación
                         "tpr", # tasa de verdaderos positivos
                         "fpr" # tasa de falsos positivos
                         ) 
# Graficamos la curva
plot(ROC.rpart)

Tercero, compramos la anterior curva con el modelo “ctree”.

plot(ROC.ctree) 

No hace falta calcular el area bajo la curva, podemos ver que el modelo “ctree” tiene un mejor desempeño

#plot(ROC.ctree,  # Indicamos el modelo
#     add=TRUE, # no borramos el grafico anterior
#     col="red" # Indicamos que el la nueva curva sea roja 
#) 

Cuarto, calculamos el AUC (area under the curve) para poder comparar los modelos.

# Creamos AUC datos
AUC.temp.rpart <- performance(prediction.rpart.ROC,"auc") # Create AUC data 
# Extraemos valores numericos
AUC.rpart <- as.numeric(AUC.temp.rpart@y.values) 
AUC.rpart
## [1] 0.8157662
# Ahora comparemoslo con el AUC de ctree
AUC.ctree
## [1] 0.8530242

Podemos ver que ambos modelos tien un un buen AUC. Sn embaergo, el el modelo “ctree” es ligeramente mejor.