Trabajo Final

Lectura de datos

#install.packages("readxl")
library(readxl)

data <- read_excel("../data/Base Corp Bancaria.XLS")
data
## # A tibble: 150 × 13
##       Id Sector   VENTAS  CREVEN     REOP   RENNET ROTCAT ROTINV CAJAPRO  CAPTRA
##    <dbl> <chr>     <dbl>   <dbl>    <dbl>    <dbl>  <dbl>  <dbl>   <dbl>   <dbl>
##  1     1 Ind      2.01e5  0.0129  0.0408  -1.04e-2  39.4  8.50e1 -1.27e5  1.46e4
##  2     2 Servicio 1.31e6  0.101  -0.255   -2.13e-1  23.1  1.60e1 -1.38e6 -2.00e5
##  3     3 Servicio 4.05e2  0.605   0.309    2.92e-2 209.   0       2.61e2 -1.98e2
##  4     4 Agro     3.05e3 -0.0966 -0.00540  9.55e-4   1.81 4.22e0 -2.58e3 -3.17e1
##  5     5 Servicio 4.57e3 -0.174   0.249    1.41e-1  41.5  0      -6.88e2  5.36e2
##  6     6 Agro     5.13e2  1.70   -0.516   -7.51e-1 187.   9.51e1 -4.10e2 -2.84e2
##  7     7 Servicio 1.90e3  0.227   0.0133   1.24e-2  44.7  3.14e1 -1.58e3  3.76e2
##  8     8 Servicio 1.64e4 -0.225  -0.195    1.04e-2 389.   1.10e3 -9.62e3 -1.35e4
##  9     9 Ind      7.3 e1 -0.109   0.35     3.5 e-1  58.8  0       2.63e1  4.26e1
## 10    10 Ind      2.20e3  0.131   0.285    1.18e-1  96.4  3.40e1 -7.03e2  9.00e2
## # ℹ 140 more rows
## # ℹ 3 more variables: RAZCO <dbl>, AUDEU <dbl>, TipoCliente <chr>

Exploracion de los datos

str(data)
## tibble [150 × 13] (S3: tbl_df/tbl/data.frame)
##  $ Id         : num [1:150] 1 2 3 4 5 6 7 8 9 10 ...
##  $ Sector     : chr [1:150] "Ind" "Servicio" "Servicio" "Agro" ...
##  $ VENTAS     : num [1:150] 200782 1307317 405 3046 4575 ...
##  $ CREVEN     : num [1:150] 0.0129 0.1006 0.6052 -0.0966 -0.1739 ...
##  $ REOP       : num [1:150] 0.0408 -0.2548 0.3095 -0.0054 0.2493 ...
##  $ RENNET     : num [1:150] -0.010388 -0.213042 0.029151 0.000955 0.140959 ...
##  $ ROTCAT     : num [1:150] 39.35 23.08 209.37 1.81 41.55 ...
##  $ ROTINV     : num [1:150] 84.95 16 0 4.22 0 ...
##  $ CAJAPRO    : num [1:150] -127443 -1384286 261 -2585 -688 ...
##  $ CAPTRA     : num [1:150] 14615.1 -199651.9 -198.4 -31.7 535.8 ...
##  $ RAZCO      : num [1:150] 1.25 0.544 0.567 0.711 1.609 ...
##  $ AUDEU      : num [1:150] -0.1571 -0.288 0.0245 -0.1797 -0.0744 ...
##  $ TipoCliente: chr [1:150] "A" "B" "A" "A" ...

Sector es la unica variable categorica, las demas son numericas. La variable a a clasificar es TipoCliente. Hay que eliminar la variable Id.

unique(data$TipoCliente)
## [1] "A" "B" "D" "C"

Hay que clasificar en 4 categorias, A, B, C y D.

sum(is.na(data))
## [1] 0

No hay valores nulos (GOD)

summary(data)
##        Id            Sector              VENTAS              CREVEN        
##  Min.   :  1.00   Length:150         Min.   :     15.7   Min.   :-0.66609  
##  1st Qu.: 38.25   Class :character   1st Qu.:    868.1   1st Qu.:-0.01191  
##  Median : 75.50   Mode  :character   Median :   3059.0   Median : 0.14427  
##  Mean   : 75.50                      Mean   :  25572.7   Mean   : 0.55087  
##  3rd Qu.:112.75                      3rd Qu.:  11665.5   3rd Qu.: 0.38826  
##  Max.   :150.00                      Max.   :1307316.8   Max.   :31.06180  
##       REOP              RENNET              ROTCAT            ROTINV       
##  Min.   :-3.30079   Min.   :-0.751462   Min.   :-349.51   Min.   :   0.00  
##  1st Qu.: 0.02194   1st Qu.: 0.001020   1st Qu.:  24.65   1st Qu.:   0.00  
##  Median : 0.05771   Median : 0.019720   Median :  59.24   Median :  16.59  
##  Mean   : 0.05482   Mean   : 0.007681   Mean   :  72.06   Mean   :  59.17  
##  3rd Qu.: 0.11509   3rd Qu.: 0.043939   3rd Qu.:  86.81   3rd Qu.:  70.24  
##  Max.   : 0.75003   Max.   : 0.418695   Max.   : 640.70   Max.   :1097.86  
##     CAJAPRO               CAPTRA              RAZCO              AUDEU         
##  Min.   :-1384286.3   Min.   :-199651.9   Min.   : 0.09751   Min.   :-0.74305  
##  1st Qu.:   -7274.8   1st Qu.:    -32.6   1st Qu.: 0.94965   1st Qu.:-0.09087  
##  Median :   -1599.1   Median :    103.5   Median : 1.29449   Median : 0.01200  
##  Mean   :  -21025.8   Mean   :   -434.3   Mean   : 1.80923   Mean   : 0.15159  
##  3rd Qu.:    -359.1   3rd Qu.:    835.3   3rd Qu.: 1.85248   3rd Qu.: 0.10196  
##  Max.   :   12063.5   Max.   :  28027.0   Max.   :15.48785   Max.   :10.38674  
##  TipoCliente       
##  Length:150        
##  Class :character  
##  Mode  :character  
##                    
##                    
## 

Vamos a separar los datos entre la variable a clasificar y los datos.

# Separar la variable a clasificar

y <- data$TipoCliente

X <- data[, -which(names(data) %in% c("TipoCliente", "Id"))]

Cuantas observaciones hay por cada categoria?

table(y)
## y
##   A   B   C   D 
## 120  17   8   5

No vamos a obtener resultados muy buenos, hay muy pocas observaciones de C y D.

Vamos a separar la variable a clasificar de las demas, estandarizar con Z-score los datos numericos (eso lo hace automaticamente la regresion logistica, no es necesario hacerlo antes) y a aplicar one hot encoding a las variables categoricas.

Ademas, hay que dividir las variables en train y test.

#install.packages("readxl")
library(readxl)

data <- read_excel("../data/Base Corp Bancaria.XLS")

# Cargar librerías necesarias
library(dplyr)
## 
## Adjuntando el paquete: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(caret)
## Cargando paquete requerido: ggplot2
## Cargando paquete requerido: lattice
library(fastDummies)

# Supongamos que 'data' es el dataframe original
data <- subset(data, select = -Id)  # Eliminar la columna Id

# Definir la variable objetivo (y) antes de transformaciones
y <- data$TipoCliente
X <- subset(data, select = -TipoCliente)  # Variables predictoras

# Separar variables numéricas y categóricas (excluyendo 'TipoCliente')
num_vars <- select_if(X, is.numeric)
cat_vars <- select_if(X, is.character)

# Normalizar las variables numéricas
num_vars <- as.data.frame(scale(num_vars))

# Aplicar One-Hot Encoding a las categóricas
cat_vars <- fastDummies::dummy_cols(cat_vars, remove_first_dummy = TRUE, remove_selected_columns = TRUE)

# Combinar nuevamente las variables transformadas
X <- cbind(num_vars, cat_vars)  # Este será el conjunto de variables predictoras

# Dividir en train y test (85% train, 15% test)
set.seed(123)  # Para reproducibilidad
train_index <- createDataPartition(y, p = 0.85, list = FALSE)
X_train <- X[train_index, ]
X_test <- X[-train_index, ]
y_train <- y[train_index]
y_test <- y[-train_index]

# Mostrar dimensiones finales de los conjuntos
cat("Dimensiones de X_train:", dim(X_train), "\n")
## Dimensiones de X_train: 129 12
cat("Dimensiones de X_test:", dim(X_test), "\n")
## Dimensiones de X_test: 21 12
cat("Dimensiones de y_train:", length(y_train), "\n")
## Dimensiones de y_train: 129
cat("Dimensiones de y_test:", length(y_test), "\n")
## Dimensiones de y_test: 21
X_test
##          VENTAS      CREVEN        REOP      RENNET      ROTCAT      ROTINV
## 1    1.52118475 -0.20178808 -0.04495681 -0.13518510 -0.35155107  0.21670455
## 3   -0.21850399  0.02036500  0.81743552  0.16061815  1.47570343 -0.49724999
## 13  -0.22100053 -0.04233520  0.02626236  0.11889235 -0.03990836  1.18024031
## 22   2.43692028 -0.15578631 -0.11248756  0.11401412 -0.77448593 -0.49724999
## 26  -0.21782792 -0.07924558  0.12067748 -0.36873098 -0.32822279 -0.36293577
## 31   0.10464961 -0.15008378  0.46512416  0.19389123 -0.52460221 -0.42824536
## 37  -0.05298852 -0.20180326  0.10685465  0.21957943 -0.12397072  0.39399198
## 43  -0.16821691 -0.19673486 -0.10027025  0.02003162 -0.61236300 -0.09028207
## 47  -0.21563283 -0.01825601  0.40288525  0.21539207 -0.36901494 -0.49724999
## 69  -0.19165586 -0.17598293  0.95762889  1.75560653 -0.77448593 -0.49724999
## 80  -0.11076972 -0.09069839 -0.12443934 -0.05615936 -0.51029268 -0.49628250
## 84  -0.18293861 -0.11934078  0.37695733  0.44480073  0.75417376  0.73895214
## 85  -0.21666990 -0.08798756  0.15863687  0.26933196 -0.07391952  0.05848637
## 92   0.65515546 -0.14009920  0.08857715 -0.02187741  0.12563567  0.50330267
## 96  -0.21302317 -0.17894206  0.12354204  0.32642722 -0.26877658 -0.49724999
## 109 -0.16416021 -0.14642273  0.06404791  0.38245808 -0.43326987 -0.46341785
## 117 -0.18019333 -0.21154649 -0.03879082  0.13414305 -0.04880179 -0.49724999
## 123 -0.20232944 -0.45645045 -0.08003355 -1.08633603  0.18356567 -0.49724999
## 127 -0.22188836 -0.22199617  0.04730686  0.13827900 -0.77201683 -0.49724999
## 147 -0.21925300 -0.05707611 -0.17828324 -0.03332703 -0.08396380 -0.32449078
## 150  0.02343346 -0.23225551 -0.43390191  0.37499620  2.97576311 -0.43909112
##         CAJAPRO      CAPTRA       RAZCO       AUDEU Sector_Ind Sector_Servicio
## 1   -0.90475292  0.86443924 -0.28031122 -0.32615013          1               0
## 3    0.18097650  0.01355078 -0.62270874 -0.13430638          0               1
## 13   0.17800592  0.02665343 -0.06530083  0.03608758          0               1
## 22  -1.82355056  0.56323615 -0.26157385 -0.37079154          0               1
## 26   0.17623828  0.02707906 -0.06982486 -0.15772498          1               0
## 31   0.01600305 -0.08620079 -0.51456587 -0.16635147          0               1
## 37   0.06484336  0.12920176 -0.27151127 -0.25714361          1               0
## 43   0.13011018  0.07130619  0.31103687  0.38203910          1               0
## 47   0.17502820  0.02891140 -0.22953626 -0.01272092          1               0
## 69   0.17871022  0.05655673 -0.13232651 -0.33380810          0               1
## 80   0.07755478  0.03836374 -0.32429768 -0.16378917          0               1
## 84   0.16469954  0.12284487  0.55004576 -0.36017228          1               0
## 85   0.17712623  0.01736652 -0.62479500 -0.14934250          1               0
## 92  -0.47942128  0.52438599 -0.31259827 -0.09169528          1               0
## 96   0.17417214  0.02435296 -0.42810355 -0.33678666          0               1
## 109  0.13450066  0.01819538 -0.46806310 -0.22298953          0               0
## 117  0.14889725  0.05424305 -0.17069176 -0.25019099          0               1
## 123  0.16132674  0.01882837 -0.46104205  0.03567836          0               1
## 127  0.17876021  0.02512495 -0.02939729  1.74586018          0               1
## 147  0.17772663  0.02975232  0.87713810 -0.35875502          0               1
## 150  0.28132349 -1.74375518 -0.65824577  0.05151516          1               0

MODELO LOGISTICO

#install.packages("caret")
#install.packages("glmnet")
#install.packages("MLmetrics")

library(MLmetrics)
## 
## Adjuntando el paquete: 'MLmetrics'
## The following objects are masked from 'package:caret':
## 
##     MAE, RMSE
## The following object is masked from 'package:base':
## 
##     Recall
library(caret)
library(glmnet)
## Cargando paquete requerido: Matrix
## Loaded glmnet 4.1-8
# Configurar la validación cruzada (K-Fold Cross Validation)
set.seed(123)  # Para reproducibilidad
cv_control <- trainControl(
  method = "cv",  # Validación cruzada
  number = 10,
  classProbs = TRUE,
  summaryFunction = multiClassSummary
)

# Definir la búsqueda de hiperparámetros
grid <- expand.grid(
  alpha = seq(0, 1, by = 0.05),  # Elastic Net: 0 = Ridge, 1 = Lasso, 0.5 = combinación
  lambda = 10^seq(-2, 1, length.out = 100)  # Regularización lambda
)

# Ajustar el modelo con Grid Search
modelo_logistico <- suppressWarnings(train(
  x = X_train,
  y = as.factor(y_train),  
  method = "glmnet",
  trControl = cv_control,
  tuneGrid = grid,
  metric = "Accuracy",
  family = "multinomial",
  preProcess = c("center", "scale")
))

best_lambda <- modelo_logistico$bestTune$lambda
best_alpha <- modelo_logistico$bestTune$alpha

print(paste("Mejor lambda:", best_lambda))
## [1] "Mejor lambda: 0.533669923120631"
print(paste("Mejor alpha:", best_alpha))
## [1] "Mejor alpha: 0"
regresion_logistica <- suppressWarnings(glmnet(
  x = as.matrix(X_train),  
  y = as.factor(y_train),        
  alpha = best_alpha,      
  lambda = best_lambda,    
  family = "multinomial",
  preProcess = c("center", "scale")
))

Obtenemos alpha = 0 y lambda = 0.533669923120631, es una regresion ridge.

Estos son los coeficientes de la matriz Beta:

library(Matrix)

coefs_A <- as.matrix(coef(regresion_logistica)$A)
coefs_B <- as.matrix(coef(regresion_logistica)$B)
coefs_C <- as.matrix(coef(regresion_logistica)$C)
coefs_D <- as.matrix(coef(regresion_logistica)$D)

coef_df <- data.frame(Variable = rownames(coefs_A),
                      Clase_A = coefs_A[,1],
                      Clase_B = coefs_B[,1],
                      Clase_C = coefs_C[,1],
                      Clase_D = coefs_D[,1])

print(coef_df)
##                        Variable      Clase_A      Clase_B      Clase_C
##                                  1.930342684 -0.053235642 -0.755408515
## VENTAS                   VENTAS -0.067984816  0.082587671 -0.006487609
## CREVEN                   CREVEN  0.034879635 -0.023612008 -0.008853504
## REOP                       REOP -0.004219053  0.010455323  0.009809657
## RENNET                   RENNET  0.016676330  0.026110562  0.004747563
## ROTCAT                   ROTCAT -0.071836171  0.007768072  0.065276055
## ROTINV                   ROTINV  0.013625060  0.022543397 -0.014475057
## CAJAPRO                 CAJAPRO  0.056509258 -0.070429016  0.005629910
## CAPTRA                   CAPTRA  0.028458801 -0.034964379  0.003725232
## RAZCO                     RAZCO -0.034314475 -0.011877867  0.079294755
## AUDEU                     AUDEU -0.084873995  0.084772679 -0.005339554
## Sector_Ind           Sector_Ind -0.133443917  0.128947736 -0.051842184
## Sector_Servicio Sector_Servicio  0.102227214 -0.129936783  0.018643451
##                      Clase_D
##                 -1.121698527
## VENTAS          -0.008115246
## CREVEN          -0.002414123
## REOP            -0.016045927
## RENNET          -0.047534454
## ROTCAT          -0.001207956
## ROTINV          -0.021693399
## CAJAPRO          0.008289848
## CAPTRA           0.002780345
## RAZCO           -0.033102414
## AUDEU            0.005440870
## Sector_Ind       0.056338365
## Sector_Servicio  0.009066118

Este es el accuracy y la matriz de confusion:

regresion_logistica <- suppressWarnings(glmnet(
  x = as.matrix(X_train),  
  y = as.factor(y_train),        
  alpha = best_alpha,      
  lambda = best_lambda,    
  family = "multinomial",
  preProcess = c("center", "scale")   
))

# Predecir con el modelo
X_test_matrix <- as.matrix(X_test)  # Convertir a matriz
predicciones <- predict(regresion_logistica, newx = X_test_matrix, type = "response")

# Seleccionar la clase con mayor probabilidad para cada observación
predicciones_clases <- colnames(predicciones)[apply(predicciones, 1, which.max)]

# Convertir predicciones a factor con los niveles de y_test
predicciones_clases <- factor(predicciones_clases, levels = levels(y_test))

# Convertir y_test a factor
y_test <- factor(y_test)

# Convertir predicciones_clases a factor con los mismos niveles de y_test
predicciones_clases <- factor(predicciones_clases, levels = levels(y_test))

# Calcular la matriz de confusión
library(caret)
matriz_confusion <- confusionMatrix(predicciones_clases, y_test)

# Mostrar la matriz de confusión
print(matriz_confusion)
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction A B C
##          A 0 0 0
##          B 0 0 0
##          C 0 0 0
## 
## Overall Statistics
##                                   
##                Accuracy : NaN     
##                  95% CI : (NA, NA)
##     No Information Rate : NA      
##     P-Value [Acc > NIR] : NA      
##                                   
##                   Kappa : NaN     
##                                   
##  Mcnemar's Test P-Value : NA      
## 
## Statistics by Class:
## 
##                      Class: A Class: B Class: C
## Sensitivity                NA       NA       NA
## Specificity                NA       NA       NA
## Pos Pred Value             NA       NA       NA
## Neg Pred Value             NA       NA       NA
## Prevalence                NaN      NaN      NaN
## Detection Rate            NaN      NaN      NaN
## Detection Prevalence      NaN      NaN      NaN
## Balanced Accuracy          NA       NA       NA

Como se dijo al inicio, no se obtienen buenos resultados porque hay muy pocas observaciones de C y de D. El modelo tiene un accuracy de 0.85 en test. Casualmente, en la separacion de Train y Test, todas las D’s se fueron a Train.

MODELO DE Analisis de Discriminante:

# Cargar librerías necesarias
library(MASS)
## 
## Adjuntando el paquete: 'MASS'
## The following object is masked from 'package:dplyr':
## 
##     select
library(caret)

# Asegurar que y es un factor
y <- as.factor(y)

# Configurar la validación cruzada (10-fold CV)
control_cv <- trainControl(method = "cv", number = 10)

# Entrenar el modelo LDA con Cross Validation
modelo_lda_cv <- train(x = X_train, y = y_train, 
                       method = "lda", 
                       trControl = control_cv,
                       preProcess = c("center", "scale"))

# Predecir con el modelo validado
predicciones <- predict(modelo_lda_cv, newdata = X_test)

# Crear la matriz de confusión
matriz_confusion <- table(Predicho = predicciones, Real = y_test)

# Calcular el accuracy final
accuracy <- sum(diag(matriz_confusion)) / sum(matriz_confusion)

# Mostrar resultados finales
cat("\nAccuracy después de Cross Validation:", accuracy, "\n")
## 
## Accuracy después de Cross Validation: 0.7619048
print(matriz_confusion)
##         Real
## Predicho  A  B  C
##        A 16  2  0
##        B  2  0  1
##        C  0  0  0
##        D  0  0  0

Este modelo mas sensillo con peor accuracy en test

MODELO DE ARBOL DE DECISION

para este modelo no hay que estandarizar

# Instalar y cargar paquetes necesarios
if (!requireNamespace("readxl", quietly = TRUE)) install.packages("readxl")
if (!requireNamespace("dplyr", quietly = TRUE)) install.packages("dplyr")
if (!requireNamespace("caret", quietly = TRUE)) install.packages("caret")
if (!requireNamespace("fastDummies", quietly = TRUE)) install.packages("fastDummies")

library(readxl)
library(dplyr)
library(caret)
library(fastDummies)

# Cargar datos
data <- read_excel("../data/Base Corp Bancaria.XLS")

# Eliminar columna 'id' si existe
data <- subset(data, select = -Id)

# Definir la variable objetivo
y <- data$TipoCliente
X <- subset(data, select = -TipoCliente)

# Separar variables numéricas y categóricas
num_vars <- select_if(X, is.numeric)
cat_vars <- select_if(X, is.character)

# Aplicar One-Hot Encoding a las categóricas
cat_vars <- fastDummies::dummy_cols(cat_vars, remove_first_dummy = TRUE, remove_selected_columns = TRUE)

# Combinar nuevamente las variables transformadas
X <- cbind(num_vars, cat_vars)

# Dividir en train y test (85% train, 15% test)
set.seed(123)  # Para reproducibilidad
train_index <- createDataPartition(y, p = 0.85, list = FALSE)

X_train <- X[train_index, , drop = FALSE]
X_test <- X[-train_index, , drop = FALSE]
y_train <- y[train_index]
y_test <- y[-train_index]
# Cargar librerías
library(caret)
library(rpart)
library(rpart.plot)

# Convertimos y_train en factor para clasificación
df <- data.frame(X_train, y = as.factor(y_train))

# Definir control del árbol con minsplit y maxdepth
control_rpart <- rpart.control(minsplit = 1,  # Mínimo de observaciones para dividir un nodo
                               maxdepth = 10, # Profundidad máxima del árbol
                               minbucket = 1) # Mínimo de observaciones en un nodo terminal

# Definir la búsqueda en malla (grid search) para ajustar el hiperparámetro cp
grid <- expand.grid(cp = seq(0.001, 10, by = 0.005))  # Complexity Parameter

# Configurar la validación cruzada (cross-validation)
control_caret <- trainControl(method = "cv", number = 10)  # 10-fold cross-validation

# Entrenar el modelo con búsqueda de hiperparámetros
modelo <- train(y ~ ., data = df, method = "rpart", 
                trControl = control_caret, tuneGrid = grid,
                control = control_rpart)  # Aquí pasamos los otros hiperparámetros

# Realizar predicciones en los datos de prueba
predicciones <- predict(modelo, newdata = X_test, type = "raw")

# Convertir y_test en factor con los mismos niveles que df$y
y_test <- factor(y_test, levels = levels(df$y))
predicciones <- factor(predicciones, levels = levels(df$y))

# Eliminar niveles vacíos
y_test <- droplevels(y_test)
predicciones <- droplevels(predicciones)

# Calcular la matriz de confusión
conf_matrix <- confusionMatrix(predicciones, y_test)
## Warning in levels(reference) != levels(data): longitud de objeto mayor no es
## múltiplo de la longitud de uno menor
## Warning in confusionMatrix.default(predicciones, y_test): Levels are not in the
## same order for reference and data. Refactoring data to match.
# Mostrar la matriz de confusión y el accuracy
print(conf_matrix$table)
##           Reference
## Prediction  A  B  C
##          A 16  2  1
##          B  2  0  0
##          C  0  0  0
accuracy <- conf_matrix$overall["Accuracy"]
cat("Accuracy del modelo:", accuracy, "\n")
## Accuracy del modelo: 0.7619048
# Visualizar el árbol
rpart.plot(modelo$finalModel, type = 4, extra = 101, under = TRUE, 
           box.palette = "RdYlGn", # Rojo → Amarillo → Verde
           main = "Árbol de Decisión Optimizado")

Todo lo manda a A xD


PCA

Como siempre, lo primero es aplicar one hot encoding a las categoricas y luego normalizar todo el dataframe, pero eso lo hace automaticamente R al pedirle el PCA.

library(dplyr)
library(fastDummies)
library(caret)
library(readxl)

# Leer los datos
data_antes_de_PCA <- read_excel("../data/Base Corp Bancaria.XLS")

# Eliminar la columna Id
data_antes_de_PCA <- data_antes_de_PCA[, -which(names(data_antes_de_PCA) %in% c("Id"))]

# Identificar variables categóricas
categorical_vars <- sapply(data_antes_de_PCA, is.factor) | sapply(data_antes_de_PCA, is.character)

# Aplicar One-Hot Encoding a todas las variables categóricas
data_encoded <- dummy_cols(data_antes_de_PCA, select_columns = names(data_antes_de_PCA)[categorical_vars], 
                           remove_first_dummy = TRUE, remove_selected_columns = TRUE)

data_encoded
## # A tibble: 150 × 15
##     VENTAS  CREVEN     REOP   RENNET ROTCAT ROTINV CAJAPRO  CAPTRA RAZCO   AUDEU
##      <dbl>   <dbl>    <dbl>    <dbl>  <dbl>  <dbl>   <dbl>   <dbl> <dbl>   <dbl>
##  1  2.01e5  0.0129  0.0408  -1.04e-2  39.4  8.50e1 -1.27e5  1.46e4 1.25  -0.157 
##  2  1.31e6  0.101  -0.255   -2.13e-1  23.1  1.60e1 -1.38e6 -2.00e5 0.544 -0.288 
##  3  4.05e2  0.605   0.309    2.92e-2 209.   0       2.61e2 -1.98e2 0.567  0.0245
##  4  3.05e3 -0.0966 -0.00540  9.55e-4   1.81 4.22e0 -2.58e3 -3.17e1 0.711 -0.180 
##  5  4.57e3 -0.174   0.249    1.41e-1  41.5  0      -6.88e2  5.36e2 1.61  -0.0744
##  6  5.13e2  1.70   -0.516   -7.51e-1 187.   9.51e1 -4.10e2 -2.84e2 0.617  1.57  
##  7  1.90e3  0.227   0.0133   1.24e-2  44.7  3.14e1 -1.58e3  3.76e2 9.80  -0.341 
##  8  1.64e4 -0.225  -0.195    1.04e-2 389.   1.10e3 -9.62e3 -1.35e4 0.669  0.0583
##  9  7.3 e1 -0.109   0.35     3.5 e-1  58.8  0       2.63e1  4.26e1 2.50  10.4   
## 10  2.20e3  0.131   0.285    1.18e-1  96.4  3.40e1 -7.03e2  9.00e2 1.72   0.0169
## # ℹ 140 more rows
## # ℹ 5 more variables: Sector_Ind <int>, Sector_Servicio <int>,
## #   TipoCliente_B <int>, TipoCliente_C <int>, TipoCliente_D <int>

El tipo de cliente A es cuando B, C y D son 0. Igual que Sector Agro es cuando Industria y Servicio son 0

# Aplicar PCA correctamente
pca_result <- prcomp(data_encoded, center = TRUE, scale. = TRUE)
data_pca <- as.data.frame(pca_result$x)

# Mostrar resumen de la varianza explicada
summary(pca_result)
## Importance of components:
##                           PC1    PC2    PC3     PC4     PC5     PC6     PC7
## Standard deviation     1.7352 1.4066 1.2761 1.15598 1.08695 1.04026 0.99191
## Proportion of Variance 0.2007 0.1319 0.1086 0.08909 0.07876 0.07214 0.06559
## Cumulative Proportion  0.2007 0.3326 0.4412 0.53028 0.60904 0.68118 0.74678
##                            PC8     PC9    PC10   PC11    PC12    PC13    PC14
## Standard deviation     0.96505 0.85432 0.82061 0.7520 0.65794 0.54850 0.40202
## Proportion of Variance 0.06209 0.04866 0.04489 0.0377 0.02886 0.02006 0.01077
## Cumulative Proportion  0.80886 0.85752 0.90241 0.9401 0.96897 0.98903 0.99980
##                          PC15
## Standard deviation     0.0546
## Proportion of Variance 0.0002
## Cumulative Proportion  1.0000

Vamos a trabajar con al menos el 80% de la variabilidad de nuestros datos

# Calcular la proporción acumulada de varianza explicada
var_acumulada <- cumsum((pca_result$sdev^2) / sum(pca_result$sdev^2))

# Encontrar el número mínimo de componentes que explican al menos el 90% de la varianza
num_componentes <- min(which(var_acumulada >= 0.80))

# Seleccionar solo las columnas necesarias en los datos transformados
data_pca <- data_pca[, 1:num_componentes]

# Mostrar cuántos componentes se seleccionaron
cat("Número de componentes seleccionados:", num_componentes, "\n")
## Número de componentes seleccionados: 8

Seleccionamos los 8 primeros componentes principales

Ahora vamos a realizar clustering con k-means para data_encoded y data_pca para ver que resultados obtenemos.

k-means para data_encoded

library(cluster)  # Para el coeficiente de silueta
#install.packages("factoextra")
#install.packages("ggplot2")
library(factoextra)  # Para visualización de clustering
## Welcome! Want to learn more? See two factoextra-related books at https://goo.gl/ve3WBa
library(ggplot2)

# Definir el rango de clusters a probar
k_values <- 2:10
silhouette_scores <- numeric(length(k_values))  # Vector para almacenar coeficiente de sombra

# Aplicar K-Means para cada valor de k y calcular la silueta
for (i in seq_along(k_values)) {
  k <- k_values[i]
  
  # Aplicar K-Means con k clusters
  set.seed(123)  # Para reproducibilidad
  kmeans_result <- kmeans(data_encoded, centers = k, nstart = 25)
  
  # Calcular coeficiente de silueta
  sil <- silhouette(kmeans_result$cluster, dist(data_encoded))
  silhouette_scores[i] <- mean(sil[, 3])  # Tercer columna contiene el coeficiente de silueta promedio
}

# Crear un dataframe con los resultados
df_silhouette <- data.frame(K = k_values, Silhouette = silhouette_scores)

# Graficar el coeficiente de silueta en función del número de clusters
ggplot(df_silhouette, aes(x = K, y = Silhouette)) +
  geom_line(color = "blue") +
  geom_point(size = 3) +
  labs(title = "Coeficiente de Silueta para Diferentes Valores de K",
       x = "Número de Clusters (K)",
       y = "Coeficiente de Silueta") +
  theme_minimal()

El criterio del codo nos diria en este caso que el numero apropiado de clusters esta entre 5 y 7. Escojamos 6.

k-means para data_pca

library(cluster)
library(factoextra)
library(ggplot2)

# Definir el rango de clusters a probar
k_values <- 2:10
silhouette_scores <- numeric(length(k_values))  # Vector para almacenar coeficiente de sombra

# Aplicar K-Means para cada valor de k y calcular la silueta
for (i in seq_along(k_values)) {
  k <- k_values[i]
  
  # Aplicar K-Means con k clusters
  set.seed(123)  # Para reproducibilidad
  kmeans_result <- kmeans(data_pca, centers = k, nstart = 25)
  
  # Calcular coeficiente de silueta
  sil <- silhouette(kmeans_result$cluster, dist(data_pca))
  silhouette_scores[i] <- mean(sil[, 3])  # Tercer columna contiene el coeficiente de silueta promedio
}

# Crear un dataframe con los resultados
df_silhouette <- data.frame(K = k_values, Silhouette = silhouette_scores)

# Graficar el coeficiente de silueta en función del número de clusters
ggplot(df_silhouette, aes(x = K, y = Silhouette)) +
  geom_line(color = "blue") +
  geom_point(size = 3) +
  labs(title = "Coeficiente de Silueta para Diferentes Valores de K en PCA",
       x = "Número de Clusters (K)",
       y = "Coeficiente de Silueta") +
  theme_minimal()

El criterio del codo en este caso nos indica que a lo mas hay 3 clusters.

APLICANDO K-MEANS CON LA CANTIDAD DE CLUSTERS ENCONTRADA

library(dplyr)
# Aplicar K-Means con 6 clusters a data_encoded
set.seed(123)
kmeans_result <- kmeans(data_encoded, centers = 6, nstart = 25)
kmeans_pca_result <- kmeans(data_pca, centers = 3, nstart = 25)

# Crear la nueva columna de clusters
data$Cluster_sin_PCA <- kmeans_result$cluster  # Pegarlo directamente a data_antes_de_PCA
data$Cluster_con_PCA <- kmeans_pca_result$cluster  # Pegarlo directamente a data_antes_de_PCA
data_pca$Cluster <- kmeans_pca_result$cluster  # Pegarlo a data_pca

data
## # A tibble: 150 × 14
##    Sector   VENTAS  CREVEN     REOP   RENNET ROTCAT ROTINV CAJAPRO  CAPTRA RAZCO
##    <chr>     <dbl>   <dbl>    <dbl>    <dbl>  <dbl>  <dbl>   <dbl>   <dbl> <dbl>
##  1 Ind      2.01e5  0.0129  0.0408  -1.04e-2  39.4  8.50e1 -1.27e5  1.46e4 1.25 
##  2 Servicio 1.31e6  0.101  -0.255   -2.13e-1  23.1  1.60e1 -1.38e6 -2.00e5 0.544
##  3 Servicio 4.05e2  0.605   0.309    2.92e-2 209.   0       2.61e2 -1.98e2 0.567
##  4 Agro     3.05e3 -0.0966 -0.00540  9.55e-4   1.81 4.22e0 -2.58e3 -3.17e1 0.711
##  5 Servicio 4.57e3 -0.174   0.249    1.41e-1  41.5  0      -6.88e2  5.36e2 1.61 
##  6 Agro     5.13e2  1.70   -0.516   -7.51e-1 187.   9.51e1 -4.10e2 -2.84e2 0.617
##  7 Servicio 1.90e3  0.227   0.0133   1.24e-2  44.7  3.14e1 -1.58e3  3.76e2 9.80 
##  8 Servicio 1.64e4 -0.225  -0.195    1.04e-2 389.   1.10e3 -9.62e3 -1.35e4 0.669
##  9 Ind      7.3 e1 -0.109   0.35     3.5 e-1  58.8  0       2.63e1  4.26e1 2.50 
## 10 Ind      2.20e3  0.131   0.285    1.18e-1  96.4  3.40e1 -7.03e2  9.00e2 1.72 
## # ℹ 140 more rows
## # ℹ 4 more variables: AUDEU <dbl>, TipoCliente <chr>, Cluster_sin_PCA <int>,
## #   Cluster_con_PCA <int>
# Instalar paquetes si no están instalados
if (!requireNamespace("scatterplot3d", quietly = TRUE)) install.packages("scatterplot3d")
if (!requireNamespace("plotly", quietly = TRUE)) install.packages("plotly")
if (!requireNamespace("ggplot2", quietly = TRUE)) install.packages("ggplot2")

# Cargar librerías
library(scatterplot3d)
library(plotly)
## 
## Adjuntando el paquete: 'plotly'
## The following object is masked from 'package:MASS':
## 
##     select
## The following object is masked from 'package:ggplot2':
## 
##     last_plot
## The following object is masked from 'package:stats':
## 
##     filter
## The following object is masked from 'package:graphics':
## 
##     layout
library(ggplot2)
library(dplyr)
library(tidyr)
## 
## Adjuntando el paquete: 'tidyr'
## The following objects are masked from 'package:Matrix':
## 
##     expand, pack, unpack
# Generar todas las combinaciones posibles de tres componentes principales
componentes <- colnames(data_pca)[1:8]  # Tomar solo PC1 a PC8
combinaciones <- combn(componentes, 3, simplify = FALSE)

# Crear gráficos 3D interactivos para cada combinación
graficos <- lapply(combinaciones, function(combo) {
  plot_ly(
    data = data_pca, 
    x = ~get(combo[1]), 
    y = ~get(combo[2]), 
    z = ~get(combo[3]), 
    color = ~as.factor(Cluster),
    colors = "Dark2",
    type = "scatter3d",
    mode = "markers"
  ) %>%
    layout(
      title = paste("3D PCA: ", combo[1], "-", combo[2], "-", combo[3]),
      scene = list(
        xaxis = list(title = combo[1]),
        yaxis = list(title = combo[2]),
        zaxis = list(title = combo[3])
      )
    )
})
graficos[[55]]

En los dos clusters donde se concentra la mayor variabilidad encontramos que el algoritmo basicamente aparto un dato que es extrano de todos los demas.

# Instalar y cargar librerías necesarias
#install.packages("plotly")  # Si no está instalado
library(plotly)
library(dplyr)
library(tidyr)

# Variables a considerar (excluyendo Sector, TipoCliente y Cluster_con_PCA)
numeric_vars <- c("VENTAS", "CREVEN", "REOP", "RENNET", "ROTCAT", "ROTINV", 
                  "CAJAPRO", "CAPTRA", "RAZCO", "AUDEU")

# Generar todas las combinaciones posibles de 3 variables
combinations <- combn(numeric_vars, 3, simplify = FALSE)

# Lista para almacenar los gráficos
plots <- list()

# Crear gráficos 3D para cada combinación
for (i in seq_along(combinations)) {
  vars <- combinations[[i]]
  
  # Crear gráfico interactivo con plotly
  p <- plot_ly(
    data = data, 
    x = as.formula(paste0("~", vars[1])), 
    y = as.formula(paste0("~", vars[2])), 
    z = as.formula(paste0("~", vars[3])), 
    color = ~as.factor(Cluster_sin_PCA),  # Cluster como factor para colores
    colors = c("red", "blue", "green", "purple", "orange"),  # Ajusta según número de clusters
    type = "scatter3d", 
    mode = "markers",
    marker = list(size = 5, opacity = 0.8)
  ) %>%
    layout(
      title = paste(vars[1], vars[2], vars[3], sep = " - "),
      scene = list(
        xaxis = list(title = vars[1]),
        yaxis = list(title = vars[2]),
        zaxis = list(title = vars[3])
      )
    )
  
  # Guardar en la lista de gráficos
  plots[[paste(vars, collapse = "_")]] <- p
}
#Probar todas las combinaciones, cambiar el numero
plots[[120]]