Planteamiento del problema

A través de un conjunto de datos (TABLON_ENTRENAMIENTO.csv) se debe entrenar un modelo que realice la predicción de la variable TARGET teniendo en cuenta los inputs dados por las variables (VAR1, VAR2, VAR3, VAR4).

Se debe realizar el test del modelo utilizando los datos del fichero TABLON_VALIDACION.csv. Para ello, elija la métrica que mejor se adapte a la naturaleza de los datos. Pista: la variable TARGET es cuantitativa continua.

Este reto se debe resolver en RStudio y presentar los resultados a través de un documento (html o pdf) creado con RMarkdown.

Debe indagar sobre diferentes naturalezas de modelos predictivos y mostrar cuál de ellos es el que mejor resultado arroja para el caso bajo estudio tras realizar la comparación entre las métricas obtenidas.

Librerías

Cargamos las librerías necesarias:

library(moments)
library(gplots)
library(ggplot2)
library(DescTools)
library(dplyr)
library(corrplot)
library(car)
library(vcd)
library(cluster)
library(ggdendro)
library(factoextra)
library(cli)
library(GGally)
library(rpart)
library(rpart.plot)
library(randomForest)
library(caret)
library(mltools)
library(e1071)
library(ROCR)
library(scales)
library(MLmetrics)

Carga y limpieza de datos

train.target <- read.csv("TABLON_ENTRENAMIENTO.csv", header = TRUE, sep = ";")
test.target <- read.csv("TABLON_VALIDACION.csv", header = TRUE, sep = ";")
head(train.target,n=6)
head(test.target,n=6)
str(train.target)
## 'data.frame':    3150 obs. of  5 variables:
##  $ TARGET: num  0.128 0.138 0.167 0.277 0.376 ...
##  $ VAR1  : int  1 2 3 4 5 6 7 8 9 10 ...
##  $ VAR2  : num  0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 ...
##  $ VAR3  : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ VAR4  : int  300 300 300 300 300 300 300 300 300 300 ...
summary(train.target)
##      TARGET            VAR1           VAR2             VAR3            VAR4    
##  Min.   :0.1277   Min.   : 1.0   Min.   : 0.500   Min.   :1.000   Min.   :300  
##  1st Qu.:2.2131   1st Qu.:13.0   1st Qu.: 0.750   1st Qu.:1.000   1st Qu.:300  
##  Median :3.5503   Median :25.5   Median : 2.500   Median :3.000   Median :400  
##  Mean   :3.0593   Mean   :25.5   Mean   : 3.893   Mean   :3.333   Mean   :400  
##  3rd Qu.:4.1773   3rd Qu.:38.0   3rd Qu.: 7.500   3rd Qu.:6.000   3rd Qu.:500  
##  Max.   :4.6191   Max.   :50.0   Max.   :10.000   Max.   :6.000   Max.   :500
str(test.target)
## 'data.frame':    2250 obs. of  5 variables:
##  $ TARGET: num  0.153 0.164 0.183 0.291 0.402 ...
##  $ VAR1  : int  1 2 3 4 5 6 7 8 9 10 ...
##  $ VAR2  : num  0.8 0.8 0.8 0.8 0.8 0.8 0.8 0.8 0.8 0.8 ...
##  $ VAR3  : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ VAR4  : int  300 300 300 300 300 300 300 300 300 300 ...
summary(test.target)
##      TARGET            VAR1           VAR2           VAR3            VAR4    
##  Min.   :0.1486   Min.   : 1.0   Min.   :0.80   Min.   :1.000   Min.   :300  
##  1st Qu.:2.3024   1st Qu.:13.0   1st Qu.:2.00   1st Qu.:1.000   1st Qu.:300  
##  Median :3.7150   Median :25.5   Median :4.00   Median :3.000   Median :400  
##  Mean   :3.1526   Mean   :25.5   Mean   :4.16   Mean   :3.333   Mean   :400  
##  3rd Qu.:4.2959   3rd Qu.:38.0   3rd Qu.:6.00   3rd Qu.:6.000   3rd Qu.:500  
##  Max.   :4.6055   Max.   :50.0   Max.   :8.00   Max.   :6.000   Max.   :500
sum(complete.cases(train.target)) #no tenemos valores nulos en el set de entrenamiento
## [1] 3150
sum(complete.cases(test.target)) #no tenemos valores nuelos en el set de prueba
## [1] 2250

Transformación de datos

Dados los rangos entre los que se encuentran nuestras variables y hacer más eficiente el uso de ciertos modelos de ML pare regresión, es necesario estandarizar la escala de nuestros valores.

#Escalaremos las variables entre [0,1] en nuestro set de entrenamiento y prueba

train.target$VAR1 <- rescale(train.target$VAR1)
train.target$VAR2 <- rescale(train.target$VAR2)
train.target$VAR3 <- rescale(train.target$VAR3)
train.target$VAR4 <- rescale(train.target$VAR4)
train.target$TARGET <- rescale(train.target$TARGET)

test.target$VAR1 <- rescale(test.target$VAR1)
test.target$VAR2 <- rescale(test.target$VAR2)
test.target$VAR3 <- rescale(test.target$VAR3)
test.target$VAR4 <- rescale(test.target$VAR4)
test.target$TARGET <- rescale(test.target$TARGET)

Correlación

Previo al entrenamiento de modelos, revisemos la relación que existe entre las variables predictoras y la variable a predecir.

full <- bind_rows(train.target,test.target)
pairs(~TARGET + VAR1 + VAR2 + VAR3 +VAR4, data= full, gap=0.4, cex.labels=1.5)

Se observa una correlación positiva entre la variable TARGET y VAR1, con el resto de variables por los valores que puede tomar es difícil establecer una relación.

Observemos la matriz de covarianzas:

matriz <- cor(full)
matriz
##             TARGET          VAR1         VAR2          VAR3        VAR4
## TARGET  1.00000000  9.083607e-01 1.281319e-01 -2.437229e-02 -0.03365784
## VAR1    0.90836072  1.000000e+00 2.703670e-20 -6.635590e-21  0.00000000
## VAR2    0.12813185  2.703670e-20 1.000000e+00  2.034745e-19  0.00000000
## VAR3   -0.02437229 -6.635590e-21 2.034745e-19  1.000000e+00  0.00000000
## VAR4   -0.03365784  0.000000e+00 0.000000e+00  0.000000e+00  1.00000000

Dado que los valores en relación a TARGET con las variables VAR2/3/4 son muy cercanos a cero, podemos concluir que no existe una relación entre ellas, indicando independencia entre sí.

Modelos de regresión

Nos enfrentamos con un problema de regresión, para el cual evaluaremos los siguientes 3 modelos:

  1. Regresión Lineal
  2. KNN (para regresión)
  3. Random Forest (para regresión)
train.target.x <- train.target[,2:5]
head(train.target.x)
train.target.y <- train.target$TARGET
head(train.target.y,n=5)
## [1] 0.000000000 0.002281702 0.008660137 0.033168841 0.055198576
test.target.x <- test.target[,2:5]
head(test.target.x)
test.target.y <- test.target$TARGET
head(test.target.y,n=5)
## [1] 0.001074291 0.003502215 0.007688314 0.031891941 0.056934044

KNN

set.seed(123)
ctrl <- trainControl(method = "repeatedcv", repeats = 3)
#Ejecutamos en modelo
KNN.model.reg <- train(y= train.target.y, x= train.target.x, method = "knn", trControl = ctrl)
KNN.model.reg #presentamos resumen del modelo
## k-Nearest Neighbors 
## 
## 3150 samples
##    4 predictor
## 
## No pre-processing
## Resampling: Cross-Validated (10 fold, repeated 3 times) 
## Summary of sample sizes: 2835, 2834, 2835, 2836, 2835, 2835, ... 
## Resampling results across tuning parameters:
## 
##   k  RMSE        Rsquared   MAE       
##   5  0.01965327  0.9962844  0.01428469
##   7  0.02657084  0.9937080  0.01715075
##   9  0.03748034  0.9876412  0.02243624
## 
## RMSE was used to select the optimal model using the smallest value.
## The final value used for the model was k = 5.
plot(KNN.model.reg)

De acuerdo a este resultado 5 es el número óptimo para el parámetro k.

Predicción sobre train

predict.train <- predict(KNN.model.reg, train.target.x)
head(predict.train,n=10)
##  [1] 0.005364582 0.006682387 0.015655048 0.032337768 0.059865548 0.093799484
##  [7] 0.134835053 0.182530708 0.232414775 0.284796995

Evaluación sobre train

#Calculamos el R2
R2(train.target.y,predict.train)
## [1] 0.9982207
#Ahora probemos sobre test
KNN.model.reg.test <- train(y= test.target.y, x= test.target.x, method = "knn", trControl = ctrl)
KNN.model.reg.test #presentamos resumen del modelo
## k-Nearest Neighbors 
## 
## 2250 samples
##    4 predictor
## 
## No pre-processing
## Resampling: Cross-Validated (10 fold, repeated 3 times) 
## Summary of sample sizes: 2026, 2024, 2026, 2026, 2025, 2024, ... 
## Resampling results across tuning parameters:
## 
##   k  RMSE        Rsquared   MAE       
##   5  0.01957159  0.9969145  0.01327093
##   7  0.03051527  0.9935138  0.01840020
##   9  0.04453787  0.9868923  0.02490235
## 
## RMSE was used to select the optimal model using the smallest value.
## The final value used for the model was k = 5.
plot(KNN.model.reg.test)

De acuerdo a este resultado 5 es el número óptimo para el parámetro k.

Predicción sobre test

predict.test <- predict(KNN.model.reg.test, test.target.x)
head(predict.test,n=10)
##  [1] 0.02021816 0.02021816 0.02021816 0.03776706 0.06142574 0.09630958
##  [7] 0.13829389 0.18702935 0.24073631 0.29730252

Evaluación sobre test

#calculamos R2
R2(test.target.y,predict.test)
## [1] 0.9990997

El R2 también es bastante alto al aplicar en el set de prueba, con lo cual descartamos estemos teniendo un overfitting.

#Calculemos la métrica de MSE
MSE.KNN <- sqrt(mean((test.target.y-predict.test )**2))
MSE.KNN
## [1] 0.01000418
print(sprintf("Mean of squared errors (MSE): %.3f", MSE.KNN))
## [1] "Mean of squared errors (MSE): 0.010"

Al ser cercana a 0, resulta ser un buen indicador para el modelo.

Regresión Lineal

Ahora intentemos resolver el problema con un modelo de regresión lineal.

model <- lm(TARGET ~ ., data = train.target)
summary(model)
## 
## Call:
## lm(formula = TARGET ~ ., data = train.target)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -0.29211 -0.10991  0.02023  0.10661  0.22270 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  0.158559   0.006005  26.405  < 2e-16 ***
## VAR1         0.947571   0.007377 128.455  < 2e-16 ***
## VAR2         0.121574   0.005985  20.312  < 2e-16 ***
## VAR3        -0.021884   0.005286  -4.140 3.57e-05 ***
## VAR4        -0.025658   0.005321  -4.822 1.49e-06 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.1219 on 3145 degrees of freedom
## Multiple R-squared:  0.8435, Adjusted R-squared:  0.8433 
## F-statistic:  4238 on 4 and 3145 DF,  p-value: < 2.2e-16
plot(model)

Predicción

#hagamos la predicción sobre el set de prueba
predict.test.lm <- predict(model, test.target)
head(predict.test.lm,n=10)
##         1         2         3         4         5         6         7         8 
## 0.1585594 0.1778976 0.1972358 0.2165739 0.2359121 0.2552503 0.2745885 0.2939266 
##         9        10 
## 0.3132648 0.3326030

Evaluación

#calculemos el R2
R2(test.target.y,predict.test.lm)
## [1] 0.8423778

El R2 también es bastante alto al aplicar en el set de prueba, con lo cual descartamos estemos teniendo un overfitting.

#Calculemos la métrica de MSE
MSE.LM <- sqrt(mean((test.target.y-predict.test.lm )**2))
MSE.LM
## [1] 0.125488
print(sprintf("Mean of squared errors (MSE): %.3f", MSE.LM))
## [1] "Mean of squared errors (MSE): 0.125"

La métrica al ser cercana a cero es buena.

No obstante, es necesario validemos los supuestos para este modelo.

Supuestos del modelo

El R2 resulta bueno, sin embargo, es necesario validemos si cumple los supuestos para este modelo.

  1. La distribución de los residuos es normal
  2. Homocedasticidad, varianza constante.
  3. Independencia de las variables explicativas
#Distribución de residuos
residuos = rstandard(model)
ks.test(residuos,"pnorm")
## 
##  Asymptotic one-sample Kolmogorov-Smirnov test
## 
## data:  residuos
## D = 0.094072, p-value < 2.2e-16
## alternative hypothesis: two-sided
shapiro.test(residuos)
## 
##  Shapiro-Wilk normality test
## 
## data:  residuos
## W = 0.95004, p-value < 2.2e-16

H0: la variable se distribuya como una normal
H1: la variable no se distribuya como una normal

El p-value corresponde a 2.2e-16 el cual es menor a .05, por lo tanto, se rechaza H0, es decir, los residuos NO se distribuyen como una normal

qqnorm(residuos)
qqline(residuos)

Gráficamente también se observa que los residuos no siguen una distribución, con lo cual, el primer supuesto no se cumple.

  1. Homocedasticidad
varianza<- bartlett.test(train.target)
varianza
## 
##  Bartlett test of homogeneity of variances
## 
## data:  train.target
## Bartlett's K-squared = 591.33, df = 4, p-value < 2.2e-16

H0: varianza(i)=varianza(j)=cte H1: son diferentes, no es constante

El p-value corresponde a 2.2e-16 el cual es menor a .05, por lo tanto, se rechaza H0, es decir, la varianza NO es constante, con lo cual tampoco se cumple este supuesto.

Hasta este punto ya podríamos decir que pese a que el modelo arroja una R2 significativa, no resulta ser un buen modelo predictor.

Random Forest

Finalmente entrenemos el modelo a través de un Randon Forest.

model.rf<-randomForest(x=train.target,
               y=train.target$TARGET,
               importance=TRUE
               )
model.rf
## 
## Call:
##  randomForest(x = train.target, y = train.target$TARGET, importance = TRUE) 
##                Type of random forest: regression
##                      Number of trees: 500
## No. of variables tried at each split: 1
## 
##           Mean of squared residuals: 0.003168046
##                     % Var explained: 96.66
#Graficamos el modelo

plot(model.rf)

Lo que nos dice la gráfica es que a partir de cerca de ntrees= 70 el modelo ya no muestra una mejoría significativa.

El porentaje de variabilidad que explica es 96.66, la cual es buena, solo faltaría validar que no se trata de overffiting. Para ello predecimos sobre el set de prueba y calculamos R2 y MSE.

Predicción

predict.test.rf <- predict(model.rf, test.target)
head(predict.test.rf,n=10)
##         1         2         3         4         5         6         7         8 
## 0.1383424 0.1384214 0.1392956 0.1449234 0.1562603 0.1779716 0.2011958 0.2220415 
##         9        10 
## 0.3057056 0.3379231

Evaluación

R2(test.target.y,predict.test.rf)
## [1] 0.9896118

El R2 obtenido también es alto con lo cual podemos decir que no existe overfitting y resulta ser un buen método predictor.

#Calculemos la métrica de MSE
RF.KNN <- sqrt(mean((test.target.y-predict.test.rf )**2))
RF.KNN
## [1] 0.05877679
print(sprintf("Mean of squared errors (MSE): %.3f", RF.KNN))
## [1] "Mean of squared errors (MSE): 0.059"

Conclusiones

Comparemos las métricas obtenidas

Modelo / Métrica MSE R2
Regresión Lineal 0.125 0.8435
Random Forest 0.057 0.9896118
KNN 0.010 0.9990997

De acuerdo a las métricas obtenidos podemos decir que tanto KNN como Random Forest resultan ser buenos modelos predictores, sin embargo, KNN resulta ser el mejor modelo.

Para este problema la Regresión Lineal NO resulta ser viable dado que no cumple los supuestos que requiere el modelo.