Para esta tarea se ha utilizado un dataset correspondiente a información escolar de una muestra de alumnos. El objetivo principal va a ser predecir la nota G3 de los alumnos. Dicho dataset está disponible en: https://archive.ics.uci.edu/ml/machine-learning-databases/00320/student.zip
#Fijamos directorio de trabajo
setwd("C:/Users/Gonzalo/Documents/datos/tarea3")
rm(list=ls())
ls()
fileURL <- "http://archive.ics.uci.edu/ml/machine-learning-databases/00320/student.zip"
download.file(fileURL,"student.zip", mode="wb")
unzip("student.zip")
#Almacenamos la fecha de la descarga
fechaDescarga <- date()
fechaDescarga
## [1] "Sat Nov 25 19:06:52 2017"
#Librerías necesarias#
library(knitr)
###CARGA DE LOS DATOS Y ANÁLISIS DESCRIPTIVO###
#Lectura de los datos#
setwd("C:/Users/Gonzalo/Documents/datos/tarea3")
mat <- file("student-mat.csv","r")
studentMat <- read.csv2(mat)
#Realizamos un primer anális descriptivo de las variable con summary#
summary(studentMat)
## school sex age address famsize Pstatus Medu
## GP:349 F:208 Min. :15.0 R: 88 GT3:281 A: 41 Min. :0.000
## MS: 46 M:187 1st Qu.:16.0 U:307 LE3:114 T:354 1st Qu.:2.000
## Median :17.0 Median :3.000
## Mean :16.7 Mean :2.749
## 3rd Qu.:18.0 3rd Qu.:4.000
## Max. :22.0 Max. :4.000
## Fedu Mjob Fjob reason
## Min. :0.000 at_home : 59 at_home : 20 course :145
## 1st Qu.:2.000 health : 34 health : 18 home :109
## Median :2.000 other :141 other :217 other : 36
## Mean :2.522 services:103 services:111 reputation:105
## 3rd Qu.:3.000 teacher : 58 teacher : 29
## Max. :4.000
## guardian traveltime studytime failures schoolsup
## father: 90 Min. :1.000 Min. :1.000 Min. :0.0000 no :344
## mother:273 1st Qu.:1.000 1st Qu.:1.000 1st Qu.:0.0000 yes: 51
## other : 32 Median :1.000 Median :2.000 Median :0.0000
## Mean :1.448 Mean :2.035 Mean :0.3342
## 3rd Qu.:2.000 3rd Qu.:2.000 3rd Qu.:0.0000
## Max. :4.000 Max. :4.000 Max. :3.0000
## famsup paid activities nursery higher internet romantic
## no :153 no :214 no :194 no : 81 no : 20 no : 66 no :263
## yes:242 yes:181 yes:201 yes:314 yes:375 yes:329 yes:132
##
##
##
##
## famrel freetime goout Dalc
## Min. :1.000 Min. :1.000 Min. :1.000 Min. :1.000
## 1st Qu.:4.000 1st Qu.:3.000 1st Qu.:2.000 1st Qu.:1.000
## Median :4.000 Median :3.000 Median :3.000 Median :1.000
## Mean :3.944 Mean :3.235 Mean :3.109 Mean :1.481
## 3rd Qu.:5.000 3rd Qu.:4.000 3rd Qu.:4.000 3rd Qu.:2.000
## Max. :5.000 Max. :5.000 Max. :5.000 Max. :5.000
## Walc health absences G1
## Min. :1.000 Min. :1.000 Min. : 0.000 Min. : 3.00
## 1st Qu.:1.000 1st Qu.:3.000 1st Qu.: 0.000 1st Qu.: 8.00
## Median :2.000 Median :4.000 Median : 4.000 Median :11.00
## Mean :2.291 Mean :3.554 Mean : 5.709 Mean :10.91
## 3rd Qu.:3.000 3rd Qu.:5.000 3rd Qu.: 8.000 3rd Qu.:13.00
## Max. :5.000 Max. :5.000 Max. :75.000 Max. :19.00
## G2 G3
## Min. : 0.00 Min. : 0.00
## 1st Qu.: 9.00 1st Qu.: 8.00
## Median :11.00 Median :11.00
## Mean :10.71 Mean :10.42
## 3rd Qu.:13.00 3rd Qu.:14.00
## Max. :19.00 Max. :20.00
## Cambiamos nombres de las variables
#Modificamos los nombres de las variables para que vayan en minúsculas#
names(studentMat) <- tolower(names(studentMat))
attach(studentMat)
#Eliminamos school, mjob, fjob, reason y guardian que son nominales para ver la matriz de correlaciones#
student<-studentMat[,-c(1,9,10,11,12)]
#Convertimos variables binarias no/yes a 0/1 y todas a numericas
student$schoolsup <- as.numeric(studentMat$schoolsup) - 1
student$famsup <- as.numeric(studentMat$famsup) - 1
student$paid <- as.numeric(studentMat$paid) - 1
student$activities <- as.numeric(studentMat$activities) - 1
student$nursery <- as.numeric(studentMat$nursery) - 1
student$higher <- as.numeric(studentMat$higher) - 1
student$internet <- as.numeric(studentMat$internet) - 1
student$romantic <- as.numeric(studentMat$romantic) - 1
#Matriz de correlación solo aplicable a variables numericas#
student<-sapply(student,as.numeric)
matCor <- cor(student)
#Nos quedamos con las correlaciones respecto a G3 (variable relativa a la nota final)
matCor <- as.matrix(matCor[,28])
colnames(matCor)<-"G3"
matCor
## G3
## sex 0.10345565
## age -0.16157944
## address 0.10575606
## famsize 0.08140711
## pstatus -0.05800898
## medu 0.21714750
## fedu 0.15245694
## traveltime -0.11714205
## studytime 0.09781969
## failures -0.36041494
## schoolsup -0.08278821
## famsup -0.03915715
## paid 0.10199624
## activities 0.01609970
## nursery 0.05156790
## higher 0.18246462
## internet 0.09848337
## romantic -0.12996995
## famrel 0.05136343
## freetime 0.01130724
## goout -0.13279147
## dalc -0.05466004
## walc -0.05193932
## health -0.06133460
## absences 0.03424732
## g1 0.80146793
## g2 0.90486799
## g3 1.00000000
#Podemos ver que las relaciones son bastante leves#
#G1 y G2 no nos interesan como correladas con G3 (la nota final es resultado de las dos notas anteriores)#
#Aun así, los alumnos con más failures tienen una nota más baja.
#Aquellos que quieren ir a la universidad tienen una nota más alta (higher)
#Buscamos dicha relación graficamente#
student2<-data.frame(student)
if(! "gridExtra" %in% installed.packages()) install.packages("gridExtra", depend = TRUE)
library(gridExtra)
library(ggplot2)
miGgplot1 <- qplot(failures, g3, data = student2)
miGgplot1
miGgplot2 <- boxplot(g3~higher, data = student2)
Vamos a realizar un clustering o agrupamiento de nuestros alumnos en aprobados/suspensos en función del resto de variables. Dado que nuestras variables son de diferentes tipos utilizamos la distancia de Gower para nuestro cluster jerárquico
##Librerias usadas##
if(! "cluster" %in% installed.packages()) install.packages("cluster", depend = TRUE)
library(cluster)
if(! "dplyr" %in% installed.packages()) install.packages("dplyr", depend = TRUE)
library(dplyr)
library(Hmisc)
#Nos quedamos las variables más influyentes en G3#
student3<-student2[,c(6,7,10,16,18,21,28)]
#Creamos una variable binaria que diga 1 si el alumno prueba y 0 si no#
notag3 <-cut2(student3$g3, c(10,20))
levels(notag3) <- c(0,1)
student4<-cbind(student3,notag3)
#Utilizamos la distancia de Gower,útil con diferentes tipos de variables (binarias, categóricas,...)#
student.mod <- student4 %>% select(-c(g3,notag3))
idsamp <- sample(1:dim(student.mod)[1], 360)
student.samp <- student.mod[idsamp, ]
head(student.samp)
## medu fedu failures higher romantic goout
## 146 1 1 0 1 0 2
## 26 2 2 2 1 0 2
## 370 4 4 0 1 1 2
## 19 3 2 3 1 0 5
## 264 3 3 0 1 0 3
## 41 2 2 1 1 1 3
D<-daisy(student.samp,"gower")
H.fit <- hclust(D, method="ward.D")
plot(H.fit, hang = -1, cex=0.8, labels=student4$notag3[idsamp])
groups <- cutree(H.fit, k=2)
rect.hclust(H.fit, k=2, border="red")
##Solo se explica algo más del 50%. El modelo no parece muy adecuado##
clusplot(student.samp, groups, color=TRUE, shade=TRUE,
labels=2, lines=0, main= 'Customer segments')
En primer lugar vamos a realizar una regresión logística que intente predecir si un alumno aprueba o suspende. En este modelo no se incluyen variables como G1,G2 o G3 ya que no sería util si ya incluimos sus notas previas. Tampoco se incluyen otras como Pstatus, trabajo de los padres, reason o guardian.
#Eliminamos algunas variables que no queremos incluir en el estudio por ser poco útiles#
studentMat<-cbind(studentMat,notag3)
studentMat<-studentMat[,-c(6,9,10,11,12,31,32,33)]
#Realizamos la muestra de test y de training#
set.seed(12345)
train.sample <- sample(1:395,size=(350),replace=F)
train.student <- studentMat[train.sample,]
test.student <- studentMat[-train.sample,]
dim(train.student)
## [1] 350 26
#Realizamos el modelo de regresión logística#
logistic.reg.model <- glm(notag3 ~.,family=binomial,data=train.student)
Sin embargo, este modelo de regresión tiene muchas variables. En estos casos, lo adecuado es buscar un equilibrio de tal forma que podamos explicar lo máximo posible con el menor número de variables. Por ello, realizamos stepwize regression.
logistic.reg.model<-step(logistic.reg.model)
Parece que failures y goout son las variables más significativas a la hora de aprobar o suspender.
summary(logistic.reg.model)
##
## Call:
## glm(formula = notag3 ~ age + failures + schoolsup + famsup +
## goout + walc + absences, family = binomial, data = train.student)
##
## Deviance Residuals:
## Min 1Q Median 3Q Max
## -2.2130 -1.0221 0.6129 0.8160 2.1581
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 5.91198 1.89330 3.123 0.00179 **
## age -0.20883 0.10961 -1.905 0.05675 .
## failures -0.90823 0.18470 -4.917 8.78e-07 ***
## schoolsupyes -0.71355 0.37199 -1.918 0.05508 .
## famsupyes -0.49386 0.26984 -1.830 0.06722 .
## goout -0.39783 0.12773 -3.115 0.00184 **
## walc 0.19438 0.11184 1.738 0.08221 .
## absences -0.02393 0.01450 -1.650 0.09892 .
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 443.21 on 349 degrees of freedom
## Residual deviance: 383.11 on 342 degrees of freedom
## AIC: 399.11
##
## Number of Fisher Scoring iterations: 4
Tratamos ahora de realizar un arbol de decisión para nuestros datos que, de igual manera, nos permita predecir si un alumno suspende o aprueba la asignatura.
#Librerias usadas#
if(! "rpart" %in% installed.packages()) install.packages("rpart", depend = TRUE)
library(rpart)
library(rpart.plot)
library(RColorBrewer)
library(rattle)
## Rattle: A free graphical interface for data science with R.
## Versión 5.1.0 Copyright (c) 2006-2017 Togaware Pty Ltd.
## Escriba 'rattle()' para agitar, sacudir y rotar sus datos.
#Arbol de decision#
mytree <- rpart(notag3 ~ ., data = train.student, method = "class")
#Representamos el arbol#
fancyRpartPlot(mytree)
#Podemos ver que hay mayores posibilidades de aprobar al dedicar más tiempo de estudio, faltar menos a clase o cuando la educación de la madre es superior#
Utilizamos Curvas ROC, el área bajo la curva y la precisión para determinar la calidad de nuestro modelo y cuál es mejor.
#Librerias usadas#
if(! "ROCR" %in% installed.packages()) install.packages("ROCR", depend = TRUE)
library(ROCR)
#Aplicamos el modelo de regresión al conjunto de test#
test.nota.output1 <- predict(logistic.reg.model,newdata=test.student,type='response')
head(test.nota.output1)
## 10 23 36 41 52 56
## 0.8891681 0.9375093 0.8891681 0.4417075 0.7753486 0.7275851
#Obtenemos un vector con el porcentaje de que la nota sea OK
#Si mayor que 0.5 -> OK. Si no, suspenso.
test.nota.output1 <- ifelse(test.nota.output1 > 0.5,1,0)
head(test.nota.output1)
## 10 23 36 41 52 56
## 1 1 1 0 1 1
clasif.error1 <- mean(test.nota.output1 != test.student$notag3)
clasif.error1
## [1] 0.3111111
##Vemos la buena precision (73%) que tiene nuestro modelo##
precision1 <- 1 - clasif.error1
precision1
## [1] 0.6888889
##Calculamos la curva ROC##
pr1 <- prediction(test.nota.output1, test.student$notag3)
prf1 <- performance(pr1, measure = "tpr", x.measure = "fpr")
plot(prf1)
auc1 <- performance(pr1, measure = "auc")
auc1 <- auc1@y.values[[1]]
auc1
## [1] 0.6
##Obtenemos un valor de 0.65. Bastante aceptable para nuestro modelo##
#Calculamos su error#
test.nota.output2 <- predict(mytree, newdata = test.student,type="vector")
test.nota.output2 <- ifelse(test.nota.output2 > 0.5,1,0)
head(test.nota.output2)
## 10 23 36 41 52 56
## 1 1 1 1 1 1
clasif.error2 <- mean(test.nota.output2 != test.student$notag3)
clasif.error2
## [1] 0.3333333
##Vemos la precision (66%) que tiene nuestro modelo##
precision2 <- 1 - clasif.error2
precision2
## [1] 0.6666667
pr2 <- prediction(test.nota.output2, test.student$notag3)
prf2 <- performance(pr2, measure = "tpr", x.measure = "fpr")
plot(prf2)
auc2 <- performance(pr2, measure = "auc")
auc2<- auc2@y.values[[1]]
auc2
## [1] 0.5
##Obtenemos un area bajo la curva de 0'5##
Parece con todo ello , que el modelo óptimo sería el de la regresión ya que tiene mayor precisión (0.68 frente a 0.66), y un área bajo la curva superior (0.6 frente a 0.5)