El primer paso es cargar la base de datos, como la data es corta se puede copiar y pegar directamente usando la libreria paste table que nos permite realizar el copiado y ponerlo en diferentes objetos de R.

data <- data.frame(
          CM = c(2.66,2.89,3.28,2.92,4,2.86,2.76,2.87,
                 3.03,3.92,2.63,3.32,3.57,3.26,3.53,2.74,2.75,2.83,
                 3.12,3.16,2.06,3.62,2.89,3.51,3.54,2.83,3.39,2.67,3.65,
                 4,3.1,2.39),
          NP = c(20L,22L,24L,12L,21L,17L,17L,21L,25L,
                 29L,20L,23L,23L,25L,26L,19L,25L,19L,23L,25L,22L,
                 28L,14L,26L,24L,27L,17L,24L,21L,23L,21L,19L),
         PSI = c(0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,0L,
                 0L,0L,0L,0L,0L,0L,0L,1L,1L,1L,1L,1L,1L,1L,1L,1L,
                 1L,1L,1L,1L,1L),
      MEJORA = c(0L,0L,0L,0L,1L,0L,0L,0L,0L,1L,0L,
                 0L,0L,1L,0L,0L,0L,0L,0L,1L,0L,1L,0L,0L,1L,1L,1L,
                 0L,1L,1L,0L,1L)
)

Ahora realizaremos un analisis de las variables que tenemos una forma es usar str() o summary()

# Funcion str()
str(data)
'data.frame':   32 obs. of  4 variables:
 $ CM    : num  2.66 2.89 3.28 2.92 4 2.86 2.76 2.87 3.03 3.92 ...
 $ NP    : int  20 22 24 12 21 17 17 21 25 29 ...
 $ PSI   : int  0 0 0 0 0 0 0 0 0 0 ...
 $ MEJORA: int  0 0 0 0 1 0 0 0 0 1 ...
# Funcion summary()
summary(data)
       CM              NP             PSI             MEJORA      
 Min.   :2.060   Min.   :12.00   Min.   :0.0000   Min.   :0.0000  
 1st Qu.:2.812   1st Qu.:19.75   1st Qu.:0.0000   1st Qu.:0.0000  
 Median :3.065   Median :22.50   Median :0.0000   Median :0.0000  
 Mean   :3.117   Mean   :21.94   Mean   :0.4375   Mean   :0.3438  
 3rd Qu.:3.515   3rd Qu.:25.00   3rd Qu.:1.0000   3rd Qu.:1.0000  
 Max.   :4.000   Max.   :29.00   Max.   :1.0000   Max.   :1.0000  

Como se puede ver todas las variables son de tipo numericas pero debemos de transformar en categorica tanto a psi y a mejora.

library(tidyverse)

data$PSI <- data$PSI %>% factor()
data$MEJORA <- data$MEJORA %>%  factor()

Con esta transformacion hemos puesto a estas variables en tipo fatcor que es necesario para el analisis del modelo.

EDA

Observamos si existe algunos valores ausentes o mising values

library(DataExplorer)

plot_missing(
  data    = data, 
  title   = "Porcentaje de valores ausentes",
  ggtheme = theme_bw(),
  theme_config = list(legend.position = "none")
)

Como se puede observar no existe ningun valor ausente por lo que no es necesario relaizar algun tipo de imputacion o de eliminacion de estos.

Ahora observamos como se relaciona las 2 variables nuemricas respecto a la variable indenpendiente

library(cowplot)

library(cowplot) 


data %>% 
  ggplot(aes(x =CM , y =NP , color =as.factor(MEJORA)))+
  geom_point(size=2.5)+
  ggpubr::color_palette("jco")+theme_minimal()-> pmain

xdens <- axis_canvas(pmain, axis = "x")+
  geom_density(data = data, aes(x = CM, fill = as.factor(MEJORA)),
               alpha = 0.7, size = 0.2)+
  ggpubr::fill_palette("jco")

ydens <- axis_canvas(pmain, axis = "y", coord_flip = TRUE)+
  geom_density(data = data, aes(x = NP, fill = as.factor(MEJORA)),
               alpha = 0.7, size = 0.2)+
  coord_flip()+
  ggpubr::fill_palette("jco")
p1 <- insert_xaxis_grob(pmain, xdens, grid::unit(.2, "null"), position = "top")
p2<- insert_yaxis_grob(p1, ydens, grid::unit(.2, "null"), position = "right")
ggdraw(p2)

Otra forma de hacer estos tipos de graficos es de la siguiente manera

library(GGally) 

data %>% select(CM,NP) %>% ggpairs(., legend = 1, 
                                        aes(color = factor(data$MEJORA, levels = 0:1, labels = c("V-Block", "Straight")),
                                            alpha=0.5)) + theme_minimal()+
  theme(legend.position = "bottom") + 
  labs(color = "Engine Type")

Como se puede observar las 2 variables presentan una correlacion positiva pero baja ya que su valor ronda en 0.38 aproximadamente.

Ahora toca ver el comportamiento de la variable categorica respecto la variable de interes

data %>% select(PSI,MEJORA) %>% 
  na.omit() %>% ggplot(aes(x = PSI, y = ..count.., fill = as.factor(MEJORA))) +
  geom_bar() +
  scale_fill_manual(values = c("gray50", "orangered2")) +
  labs(title = " ") +
  theme_bw() +
  theme(legend.position = "bottom")   

Ahora vemos la variable mejora como esta proporcionada

data %>% select(MEJORA) %>% group_by(MEJORA) %>% summarise(cantidad=n()) %>% as.data.frame() %>% 
  ggplot(aes(MEJORA,cantidad))+geom_col()+theme_minimal()

como se puede obsrvar la clase 0 es la dominate ante la clase 1. Esto puede generar problemas al momento de generar nuevas predicciones

Modelamiento

Primero haremos un modelo de regresión lineal simple

modelo1 <- lm(as.numeric(MEJORA) ~.,data=data)
summary(modelo1)

Call:
lm(formula = as.numeric(MEJORA) ~ ., data = data)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.78153 -0.27731  0.00531  0.21089  0.81145 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)   
(Intercept) -0.49802    0.52389  -0.951  0.34993   
CM           0.46385    0.16196   2.864  0.00784 **
NP           0.01050    0.01948   0.539  0.59436   
PSI1         0.37855    0.13917   2.720  0.01109 * 
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.3881 on 28 degrees of freedom
Multiple R-squared:  0.4159,    Adjusted R-squared:  0.3533 
F-statistic: 6.646 on 3 and 28 DF,  p-value: 0.001571

Como se puede observar solo va variable np es no significativa

predichos <- modelo1$fitted.values

data %>% ggplot(aes(1:32,MEJORA))+geom_point(mapping=aes(color=MEJORA))+
  geom_line(mapping = aes(x=1:32,y=predichos))+theme_minimal()

como se puede observar existe valores que sobre pasan a cero estos es uno de las tipicas fallas al aplicar un modelo de mco para estos tipos de regresion

summary(predichos)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
 0.9318  1.0502  1.3257  1.3438  1.5855  1.9773 
pred1 <- ifelse(predichos>0.5,1,0)

library(caret)

table(data$MEJORA,pred1)
   pred1
     1
  0 21
  1 11

Como se puede observar solo una clase ha sido bien clasificada por lo que el modelo regresion lineal no sirve para este ejemplo.

modelo2 <-  glm(MEJORA ~.,data=data,family=binomial())
summary(modelo2)

Call:
glm(formula = MEJORA ~ ., family = binomial(), data = data)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-1.9551  -0.6453  -0.2570   0.5888   2.0966  

Coefficients:
             Estimate Std. Error z value Pr(>|z|)   
(Intercept) -13.02135    4.93127  -2.641  0.00828 **
CM            2.82611    1.26293   2.238  0.02524 * 
NP            0.09516    0.14155   0.672  0.50143   
PSI1          2.37869    1.06456   2.234  0.02545 * 
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 41.183  on 31  degrees of freedom
Residual deviance: 25.779  on 28  degrees of freedom
AIC: 33.779

Number of Fisher Scoring iterations: 5

En este modelo se aplico el logit en donde se ve que al igual que el modelo de regresion lieneal solo la variable np es no significativa.

pred2 <- predict(modelo2,data)
pred2 <- ifelse(pred2>0.5,1,0)
table(data$MEJORA,pred2) %>% confusionMatrix()
Confusion Matrix and Statistics

   pred2
     0  1
  0 20  1
  1  4  7
                                          
               Accuracy : 0.8438          
                 95% CI : (0.6721, 0.9472)
    No Information Rate : 0.75            
    P-Value [Acc > NIR] : 0.1530          
                                          
                  Kappa : 0.6296          
                                          
 Mcnemar's Test P-Value : 0.3711          
                                          
            Sensitivity : 0.8333          
            Specificity : 0.8750          
         Pos Pred Value : 0.9524          
         Neg Pred Value : 0.6364          
             Prevalence : 0.7500          
         Detection Rate : 0.6250          
   Detection Prevalence : 0.6562          
      Balanced Accuracy : 0.8542          
                                          
       'Positive' Class : 0               
                                          

Modelo probit

modelo3 <- glm(MEJORA ~. ,
               data = data , family = binomial(link = "probit"))
summary(modelo3)

Call:
glm(formula = MEJORA ~ ., family = binomial(link = "probit"), 
    data = data)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-1.9392  -0.6508  -0.2229   0.5934   2.0451  

Coefficients:
            Estimate Std. Error z value Pr(>|z|)   
(Intercept) -7.45231    2.57152  -2.898  0.00376 **
CM           1.62581    0.68973   2.357  0.01841 * 
NP           0.05173    0.08119   0.637  0.52406   
PSI1         1.42633    0.58695   2.430  0.01510 * 
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 41.183  on 31  degrees of freedom
Residual deviance: 25.638  on 28  degrees of freedom
AIC: 33.638

Number of Fisher Scoring iterations: 6
pred3 <- predict(modelo3,data)
pred3 <- ifelse(pred3>0.5,1,0)

table(data$MEJORA,pred3) %>% confusionMatrix()
Confusion Matrix and Statistics

   pred3
     0  1
  0 20  1
  1  7  4
                                         
               Accuracy : 0.75           
                 95% CI : (0.566, 0.8854)
    No Information Rate : 0.8438         
    P-Value [Acc > NIR] : 0.9483         
                                         
                  Kappa : 0.3632         
                                         
 Mcnemar's Test P-Value : 0.0771         
                                         
            Sensitivity : 0.7407         
            Specificity : 0.8000         
         Pos Pred Value : 0.9524         
         Neg Pred Value : 0.3636         
             Prevalence : 0.8438         
         Detection Rate : 0.6250         
   Detection Prevalence : 0.6562         
      Balanced Accuracy : 0.7704         
                                         
       'Positive' Class : 0              
                                         

Dalex

Lo primero es crear los moelos pero mediate el paquete caret

probit <- train(MEJORA ~ ., data = data,
                         method = "glm",
                         metric = "Accuracy",
                         family = binomial(link = "probit"))

logit <- train(MEJORA ~ ., data = data,
               method = "glm",
               metric = "Accuracy",
               family = "binomial")

Ahora lo siguiente es realizar la creación de los objetos para realizar los graficos con dalex

library(DALEX)
explainer_logit <- DALEX::explain(logit,
                                data = data[,-4],
                                y = data$MEJORA, 
                                label = "Logistic regression")
Preparation of a new explainer is initiated
  -> model label       :  Logistic regression 
  -> data              :  32  rows  3  cols 
  -> target variable   :  32  values 
  -> predict function  :  yhat.train  will be used (  default  )
  -> predicted values  :  No value for predict function target column. (  default  )
  -> model_info        :  package caret , ver. 6.0.86 , task classification (  default  ) 
  -> model_info        :  Model info detected classification task but 'y' is a factor .  (  WARNING  )
  -> model_info        :  By deafult classification tasks supports only numercical 'y' parameter. 
  -> model_info        :  Consider changing to numerical vector with 0 and 1 values.
  -> model_info        :  Otherwise I will not be able to calculate residuals or loss function.
  -> predicted values  :  numerical, min =  0.02447037 , mean =  0.34375 , max =  0.9453403  
  -> residual function :  difference between y and yhat (  default  )
  -> residuals         :  numerical, min =  NA , mean =  NA , max =  NA  
  A new explainer has been created!  
explainer_probit <- DALEX::explain(probit,
                                  data = data[,-4],
                                  y = data$MEJORA, 
                                  label = "Probit regression")
Preparation of a new explainer is initiated
  -> model label       :  Probit regression 
  -> data              :  32  rows  3  cols 
  -> target variable   :  32  values 
  -> predict function  :  yhat.train  will be used (  default  )
  -> predicted values  :  No value for predict function target column. (  default  )
  -> model_info        :  package caret , ver. 6.0.86 , task classification (  default  ) 
  -> model_info        :  Model info detected classification task but 'y' is a factor .  (  WARNING  )
  -> model_info        :  By deafult classification tasks supports only numercical 'y' parameter. 
  -> model_info        :  Consider changing to numerical vector with 0 and 1 values.
  -> model_info        :  Otherwise I will not be able to calculate residuals or loss function.
  -> predicted values  :  numerical, min =  0.01610246 , mean =  0.3427204 , max =  0.9522449  
  -> residual function :  difference between y and yhat (  default  )
  -> residuals         :  numerical, min =  NA , mean =  NA , max =  NA  
  A new explainer has been created!  
model_profile_glm1 <- model_profile(explainer_logit, type = "partial", 
                                    variables = c("CM","NP"))
plot(model_profile_glm1)

model_profile_probit <- model_profile(explainer_probit, type = "partial", 
                                    variables = c("CM","NP"))
plot(model_profile_probit)

single_customer <- data[5,]
explainer_logit %>% predict_parts(new_observation = single_customer) %>% plot()

explainer_probit %>% predict_parts(new_observation = single_customer) %>% plot()