Balanceo de Datos

EXPLORACIÓN:

# =============================================================================
# TRATAMIENTO DE DATOS NO BALANCEADOS
# =============================================================================

#FUNCIONES DE GRAFICO
#grafica <- function(x1,y1){
#  ggplot(x1,aes(x=factor(y1),fill=factor(y1),na.rm=TRUE))+geom_bar(position="dodge")+
#    geom_text(aes(label=..count..),stat='count',position=position_dodge(1))
#}

# DATOS -------------------------------------------------------------------

# Cargar la base de datos
desafilado <- read.csv("C:/Users/HUAWEI/Google Drive/UNI data/CICLO 9/PEBIBA/evelyn gutierres/4_Balanceo/DatosDesafiliado.csv", header = T, stringsAsFactors =TRUE)

# Estructura de los datos
str(desafilado)
## 'data.frame':    4500 obs. of  6 variables:
##  $ Plan_internacional      : Factor w/ 2 levels "no","yes": 1 1 1 1 1 1 1 1 1 1 ...
##  $ Minutos_dia             : num  203 264 102 229 125 ...
##  $ Minutos_internacionales : num  9 7.5 9.4 7.4 10.2 15.2 13.2 8.3 10.8 11.3 ...
##  $ Reclamos                : int  3 2 3 3 2 2 1 1 3 2 ...
##  $ Llamadas_internacionales: int  3 4 6 6 7 5 4 2 4 5 ...
##  $ Desafiliado             : Factor w/ 2 levels "no","yes": 1 2 1 1 1 1 1 1 1 1 ...
# Plan Internacio
table(desafilado$Plan_internacional)
## 
##   no  yes 
## 4170  330
# Muinutos Dia
hist(desafilado$Minutos_dia)

# Minutos internacionales
hist(desafilado$Minutos_internacionales)

# Reclamos 
hist(desafilado$Reclamos)

# Llamadas Internacionacionales
hist(desafilado$Llamadas_internacionales)

# Desafilado
grafico(desafilado,desafilado$Desafiliado)

# Visualizamos algunos datos
head(desafilado)
##   Plan_internacional Minutos_dia Minutos_internacionales Reclamos
## 1                 no       202.9                     9.0        3
## 2                 no       264.5                     7.5        2
## 3                 no       101.7                     9.4        3
## 4                 no       229.2                     7.4        3
## 5                 no       125.0                    10.2        2
## 6                 no       188.5                    15.2        2
##   Llamadas_internacionales Desafiliado
## 1                        3          no
## 2                        4         yes
## 3                        6          no
## 4                        6          no
## 5                        7          no
## 6                        5          no
# 2
# ProporciĂłn por clase
table(desafilado$Desafiliado)
## 
##   no  yes 
## 4293  207
prop.table(table(desafilado$Desafiliado))
## 
##    no   yes 
## 0.954 0.046
# La data solo contiene aprox. 4.6% de casos positivos (clientes en default)

# ANÁLISIS EXPLORATORIO
barplot(prop.table(table(desafilado$Desafiliado)),
        col = rainbow(2),
        ylim = c(0, 1),
        main = "DistribuciĂłn de Clases")

MODELAMIENTO

PREGUNTA 3

# SIN BALANCEAR LOS DATOS -------------------------------------------------
# Trabajar con datos no balanceados, en la mayorĂ­a de los casos, nos darĂ­a 
# un modelo de predicciĂłn que siempre devuelve la clase mayoritaria. 
# El clasificador estarĂ­a sesgado.

split <- 0.8 # Porcentaje de datos al conjunto de entrenamiento
trainIndex <- createDataPartition(desafilado$Desafiliado, p = split, list = FALSE)

desafilado_train <- desafilado[trainIndex,]
desafilado_test <- desafilado[-trainIndex,]

PREGUNTA 4 - ENTRANAMIENTO DEL MODELO

desafilado_nbal <- glm(Desafiliado ~., data = desafilado_train, family = binomial(link = "logit"))
summary(desafilado_nbal)
## 
## Call:
## glm(formula = Desafiliado ~ ., family = binomial(link = "logit"), 
##     data = desafilado_train)
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -1.4247  -0.3049  -0.2120  -0.1496   3.2244  
## 
## Coefficients:
##                           Estimate Std. Error z value Pr(>|z|)    
## (Intercept)              -7.602223   0.568137 -13.381  < 2e-16 ***
## Plan_internacionalyes     1.850721   0.199442   9.279  < 2e-16 ***
## Minutos_dia               0.016100   0.001794   8.974  < 2e-16 ***
## Minutos_internacionales   0.049114   0.031330   1.568    0.117    
## Reclamos                  0.394161   0.058567   6.730 1.69e-11 ***
## Llamadas_internacionales -0.016676   0.035886  -0.465    0.642    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 1345.8  on 3600  degrees of freedom
## Residual deviance: 1136.8  on 3595  degrees of freedom
## AIC: 1148.8
## 
## Number of Fisher Scoring iterations: 6
# PredicciĂłn de probabilidades sobre el test
desafilado_nbal_pred <- predict(desafilado_nbal, newdata = desafilado_test, type = "response")
head(desafilado_nbal_pred)
##           1          12          13          20          25          28 
## 0.059470310 0.009069126 0.091832195 0.022798393 0.032647446 0.043326849
# Nivel de la desafilado en test
desafilado_nbal_pred_clase <- factor(ifelse(desafilado_nbal_pred > 0.5, 1, 0))
levels(desafilado_nbal_pred_clase) <- c("No","Si")

# Cambiar los niveles pues estån en inglés
levels(desafilado_test$Desafiliado) <- c("No","Si")

# EvaluaciĂłn del modelo sin balancear
# Matriz de confusiĂłn en base al test
table(Predicho = desafilado_nbal_pred_clase, Real = desafilado_test$Desafiliado)
##         Real
## Predicho  No  Si
##       No 858  41
##       Si   0   0
# Matriz ConfusiĂłn sobre el test
# table(desafilado_test$Desafiliado, desafilado_nbal_pred > 0.5)


confusionMatrix(desafilado_nbal_pred_clase, desafilado_test$Desafiliado, positive = "Si",
                mode = "everything")
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  No  Si
##         No 858  41
##         Si   0   0
##                                           
##                Accuracy : 0.9544          
##                  95% CI : (0.9386, 0.9671)
##     No Information Rate : 0.9544          
##     P-Value [Acc > NIR] : 0.5414          
##                                           
##                   Kappa : 0               
##                                           
##  Mcnemar's Test P-Value : 4.185e-10       
##                                           
##             Sensitivity : 0.00000         
##             Specificity : 1.00000         
##          Pos Pred Value :     NaN         
##          Neg Pred Value : 0.95439         
##               Precision :      NA         
##                  Recall : 0.00000         
##                      F1 :      NA         
##              Prevalence : 0.04561         
##          Detection Rate : 0.00000         
##    Detection Prevalence : 0.00000         
##       Balanced Accuracy : 0.50000         
##                                           
##        'Positive' Class : Si              
## 
# Accuracy
confusionMatrix(desafilado_nbal_pred_clase, desafilado_test$Desafiliado, positive = "Si")$overall[1]
##  Accuracy 
## 0.9543938
# Error de clasificaciĂłn

ce(actual = desafilado_test$Desafiliado, predicted = desafilado_nbal_pred_clase)
## [1] 0.04560623
# AUC
auc(actual = ifelse(desafilado_test$Desafiliado == "Si", 1, 0),
    predicted = desafilado_nbal_pred)
## [1] 0.831116
# Se cambia a numérico porque solo acepta este tipo de dato

# Curva ROC
ROCRpred <- prediction(desafilado_nbal_pred, desafilado_test$Desafiliado)
# Primer argumento: Predicciones con el modelo
# Segundo argumento: Valores reales de los datos
ROCRperf <- performance(ROCRpred, "tpr", "fpr")
# EjeX: fpr (False Positive Rate)
# EjeY: tpr (True Positive Rate) 

# GrĂĄfico de la curva ROC
plot(ROCRperf)

plot(ROCRperf, colorize = TRUE)
plot(ROCRperf, colorize = TRUE, print.cutoffs.at = seq(0, 1, by = 0.1), text.adj = c(-0.2,1.7))

BALANCEO

# 1. OVERSAMPLING ---------------------------------------------------------

prop.table(table(desafilado_train$Desafiliado))
## 
##         no        yes 
## 0.95390169 0.04609831
# Cantidad de observaciones en desafilado_train que son Desafiliado = no
table(desafilado_train$Desafiliado)[1]
##   no 
## 3435
desafilado_bal_over <- ovun.sample(Desafiliado ~ ., data = desafilado_train, method = "over", 
                                   N = table(desafilado_train$Desafiliado)[1]*2)$data

table(desafilado_bal_over$Desafiliado)
## 
##   no  yes 
## 3435 3435
head(desafilado_bal_over)
##   Plan_internacional Minutos_dia Minutos_internacionales Reclamos
## 1                 no       101.7                     9.4        3
## 2                 no       229.2                     7.4        3
## 3                 no       125.0                    10.2        2
## 4                 no       188.5                    15.2        2
## 5                 no       221.8                    13.2        1
## 6                 no       153.7                     8.3        1
##   Llamadas_internacionales Desafiliado
## 1                        6          no
## 2                        6          no
## 3                        7          no
## 4                        5          no
## 5                        4          no
## 6                        2          no
# 2. UNDERSAMPLING --------------------------------------------------------

# Cantidad de observaciones en desafilado_train que son Desafiliado = yes
table(desafilado_train$Desafiliado)[2]
## yes 
## 166
desafilado_bal_under <- ovun.sample(Desafiliado ~ ., data = desafilado_train, method = "under", N = table(desafilado_train$Desafiliado)[2]*2)$data

table(desafilado_bal_under$Desafiliado)
## 
##  no yes 
## 166 166
# 3. AMBOS UNDERSAMPLING Y OVERSAMPLING -----------------------------------

# Cantidad de observaciones en el conjunto de entrenamiento
dim(desafilado_train)[1]
## [1] 3601
desafilado_bal_ambos <- ovun.sample(Desafiliado ~ ., data = desafilado_train, 
                                    method = "both", 
                                    p= 0.5, 
                                    N = dim(desafilado_train)[1], 
                                    seed = 1)$data
# p es la prob. de la clase positiva en la nueva muestra generada

table(desafilado_bal_ambos$Desafiliado)
## 
##   no  yes 
## 1853 1748
prop.table(table(desafilado_bal_ambos$Desafiliado))
## 
##        no       yes 
## 0.5145793 0.4854207
# Verificar clase mayoritaria

desafiladoYes <- desafilado_bal_ambos %>% filter(Desafiliado == 'Yes')
head(desafiladoYes)
## [1] Plan_internacional       Minutos_dia              Minutos_internacionales 
## [4] Reclamos                 Llamadas_internacionales Desafiliado             
## <0 rows> (or 0-length row.names)
# Contar valores Ășnicos por columna
sapply(desafiladoYes, function(x) length(unique(x)))
##       Plan_internacional              Minutos_dia  Minutos_internacionales 
##                        0                        0                        0 
##                 Reclamos Llamadas_internacionales              Desafiliado 
##                        0                        0                        0
desafiladoYes %>% distinct()
## [1] Plan_internacional       Minutos_dia              Minutos_internacionales 
## [4] Reclamos                 Llamadas_internacionales Desafiliado             
## <0 rows> (or 0-length row.names)
# 4. ROSE -----------------------------------------------------------------

desafilado_bal_rose <- ROSE(Desafiliado ~ ., data = desafilado_train, seed = 3)$data

table(desafilado_bal_rose$Desafiliado)
## 
##   no  yes 
## 1740 1861
prop.table(table(desafilado_bal_rose$Desafiliado))
## 
##        no       yes 
## 0.4831991 0.5168009
# 5. SMOTE ----------------------------------------------------------------

set.seed(2019) # Para tener resultados reproducibles

desafilado_bal_smote <- DMwR::SMOTE(Desafiliado ~ ., data = desafilado_train, perc.over = 200, k = 5, perc.under = 200)

table(desafilado_bal_smote$Desafiliado)
## 
##  no yes 
## 664 498
prop.table(table(desafilado_bal_smote$Desafiliado))
## 
##        no       yes 
## 0.5714286 0.4285714
# Modelo directo con datos balanceados (randomForest, )

modelo <- SMOTE(Desafiliado ~ ., data = desafilado_train, perc.over = 200, k = 5, perc.under = 200, learner = "randomForest")

# Creamos un SMOTE test dataset en nuestro conjunto test.
smote_test <- SMOTE(Desafiliado ~ ., data = desafilado_test, perc.over = 200, k = 5, perc.under = 200)
prop.table(table(smote_test$Desafiliado))
## 
##        No        Si 
## 0.5714286 0.4285714
# Insertar aleatoriedad
# indices aleatorios
split <- sample(1:nrow(smote_test), nrow(smote_test))
# dataset aleatoria
smote_test <- smote_test[split,]

# Predecimos la clase de la variable luego de realizar un randomForest
p <- predict(modelo, smote_test)
levels(p) <- c("No", "Si")
caret::confusionMatrix(smote_test$Desafiliado, p, positive = "Si", mode="everything")
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  No  Si
##         No 145  19
##         Si  41  82
##                                           
##                Accuracy : 0.7909          
##                  95% CI : (0.7393, 0.8365)
##     No Information Rate : 0.6481          
##     P-Value [Acc > NIR] : 9.427e-08       
##                                           
##                   Kappa : 0.5634          
##                                           
##  Mcnemar's Test P-Value : 0.006706        
##                                           
##             Sensitivity : 0.8119          
##             Specificity : 0.7796          
##          Pos Pred Value : 0.6667          
##          Neg Pred Value : 0.8841          
##               Precision : 0.6667          
##                  Recall : 0.8119          
##                      F1 : 0.7321          
##              Prevalence : 0.3519          
##          Detection Rate : 0.2857          
##    Detection Prevalence : 0.4286          
##       Balanced Accuracy : 0.7957          
##                                           
##        'Positive' Class : Si              
## 
# randomForest sin balancear los datos con SMOTE
bosque <- randomForest(Desafiliado ~ ., data = desafilado_train)
bosque_pred <- predict(bosque, desafilado_test, type = "response")
levels(bosque_pred) <- c("No", "Si")
confusionMatrix(bosque_pred, desafilado_test$Desafiliado, positive = "Si", mode="everything")
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  No  Si
##         No 847  26
##         Si  11  15
##                                           
##                Accuracy : 0.9588          
##                  95% CI : (0.9437, 0.9709)
##     No Information Rate : 0.9544          
##     P-Value [Acc > NIR] : 0.29367         
##                                           
##                   Kappa : 0.4275          
##                                           
##  Mcnemar's Test P-Value : 0.02136         
##                                           
##             Sensitivity : 0.36585         
##             Specificity : 0.98718         
##          Pos Pred Value : 0.57692         
##          Neg Pred Value : 0.97022         
##               Precision : 0.57692         
##                  Recall : 0.36585         
##                      F1 : 0.44776         
##              Prevalence : 0.04561         
##          Detection Rate : 0.01669         
##    Detection Prevalence : 0.02892         
##       Balanced Accuracy : 0.67652         
##                                           
##        'Positive' Class : Si              
## 
# Notar la diferencia en la exactitud de los valores predichos (Accuracy)

Pregunta 7

  • La mejor tecnica por sus resultados es el SMOTE con una precision 88% y ademas la sensividad y specificad