data<-read.csv("CarreLatinMBX.csv", sep=';',header=T)# Définition des matrices de données et tailles associées
n=dim(data)[1]
m=dim(data)[2]-1
X=apply(data[,1+(1:m)],2,as.numeric)
TypeY=rep(c("A","B","C"),c(27,27,27))
classY=as.numeric(as.factor(TypeY)) # classe 1, 2 et 3 utile pour les graphiques
# On extrait les noms des variables, on enlève le "X" et on les remets comme noms de variables
ppm=as.numeric(substr(dimnames(X)[[2]],2,6))
dimnames(X)[[2]]=ppm
# On mets les noms des spectres comme non des lignes de X
dimnames(X)[[1]]=data[,1]
# Centrage des spectres
XC=scale(X,center=T,scale=F)
# Définition de variables pour les graphiques
Xt <- t(X)
library(reshape)
library(reshape2)
library(tidyr)
Xt <- cbind.data.frame(Xt, spectres = as.factor(1:nrow(Xt)))
Xt <- melt(Xt, id.vars = "spectres" )
Xt <- cbind.data.frame(Xt, ppm = ppm)Il est possible de sélectionner des spectres (droite) si l’ont veut se concentrer sur certains spectres seulement.
library(plotly)
gg_labs <- labs(title = "Représentation des spectres", x = "Intensité", y = "Patient" )
dimnames(Xt)[[2]][2] <- "Spectre"
gg_spectre <- ggplot(Xt) + geom_line(aes(x = ppm, y = value, col = Spectre), size = 0.25) + gg_labs + scale_color_discrete(guide = F) + scale_color_hue(l = 40, c = 200) + theme_piss()
ggplotly(gg_spectre)
De cette manière “dynamique”, il est plus aisément possible de se faire rapidement une idée des données ainsi que de permettre à vérifier visuellement si Pour ce genre de graphes où il y a be beaucoup de lignes représentées avec des différences minimes, la possibilité de zoomer (e.g. en sélectionnant avec la souris une aire sur le graphe) est également intéressante (…)
ncomp = 8
PCA.res = SVDforPCA(XC,ncomp = ncomp)% de la variance expliquée par chaque variable
eig.res=rbind(PCA.res$var,PCA.res$var*100/sum(PCA.res$var),PCA.res$cumvar)
rownames(eig.res)=c("Variances","Prop Var","Cum Eigen Values")
pandoc.table(eig.res,width = 1000)| PC1 | PC2 | PC3 | PC4 | PC5 | PC6 | PC7 | PC8 | |
|---|---|---|---|---|---|---|---|---|
| Variances | 67.2 | 13.4 | 7 | 4.8 | 2.6 | 2.1 | 0.8 | 0.7 |
| Prop Var | 68.15 | 13.59 | 7.099 | 4.868 | 2.637 | 2.13 | 0.8114 | 0.7099 |
| Cum Eigen Values | 67.2 | 80.6 | 87.6 | 92.4 | 95 | 97.1 | 97.9 | 98.6 |
#kable(eig.res)Représentation graphique des scores pour les 4 premières composantes
gg_pca1 <- DrawSL(PCA.res, type.obj = "PCA", drawNames=TRUE,createWindow=FALSE, main = paste("PCA score plots for PC1 vs PC2, and for PC3 vs PC4"), axes =c(1,2), type.graph="scores",class=classY)
gg_pca2 <- DrawSL(PCA.res, type.obj = "PCA", drawNames=TRUE,createWindow=FALSE, main = paste("PCA score plots for PC1 vs PC2, and for PC3 vs PC4"), axes =c(3,4), type.graph="scores",class=classY)
subplot(hide_legend(ggplotly(gg_pca1)), ggplotly(gg_pca2), shareX = T, shareY= T, which_layout = 2)On remarque que les patients ont tendance à se regrouper entre eux, ce qui est intuitif. En plus de ça, on remarque aussi une tendance intra-pantients à grouper par 3 en fonction de leur moment de prélevement de l’échantillon, ceci étant plus visible sur les graphes des 2 premières composantes (qui expliquent bien mieux les données !)
gg_load <- DrawSL(PCA.res, type.obj = "PCA", drawNames=F,createWindow=FALSE,
axes = c(1:4), type.graph ="loadings", loadingstype="l",xlab = "ppm",ang=90,xaxis="character",nxaxis=10)
ggplotly(gg_load[[1]])Sur base de cette ACP, nous décidons de choisir les patients A et B afin de les comparer. Nous justifions ce choix sur base de cet ACP seulement, donc assez arbitrairement, car ils semblent être les plus écartés sur les graphes des 2 premières composantes et donc leur “différences” devraient être plus soulignées et donc l’analyse devrait être plus intéressante..
Xdata <- XC[1:(2*27),] # Subset only our 2 types of patients
Xdata <- cbind.data.frame( Xdata, class = as.numeric( c(rep(0, nrow(Xdata)/2), rep(1, nrow(Xdata)/2) ) ) )Notre but ici est de trouver les marqueurs qui différencient le plus possible les spectres.
df_mean <- t(data.frame(sort(apply(Xdata, 2, mean))))
df_sd <- t(data.frame(sort(apply(Xdata, 2, sd))))
# names(df_mean) <-
# df <- rbind.data.frame(df_mean, df_sd)
rownames(df_mean) <- "mean"
rownames(df_sd) <- "variance"
pander(df_mean[,1:6]) ; pander(df_sd[,1:6])| 3.259 | 0.859 | 3.739 | 1.259 | 3.499 | 3.859 |
|---|---|---|---|---|---|
| -0.001821 | -0.001567 | -0.0008222 | -0.0007686 | -0.0007265 | -0.0003518 |
| 4.539 | 4.579 | 4.619 | 4.659 | 4.699 | 4.739 |
|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 |
Après avoir retiré ces variables inutiles, i.e. qui ne varient pas, on va alors regarder d’abord les corrélations linéaires des variables entre elles, qui s’avèrent d’être toutes assez fortes. De plus, on remarque grâce à ça, que l’odre (i.e. nom) des variables n’est pas anodin, et est lié en fait à la corrélation,
## Remove these features
Xdata <- Xdata[ ,colnames(Xdata) %in% names(sort(apply(Xdata, 2, sd))[1:14]) == F]
corrX <- c()
for (i in 0:(ncol(Xdata)-2)){
corrX[i] <- cor(Xdata[,i+1], Xdata[,i+2])
names(corrX)[i] <- paste0(names(Xdata)[i+1],"&", names(Xdata)[i+2])
}
#sort(corrX)
dfcor1 <- t(as.matrix(sort(abs(corrX))[(length(corrX)-4):length(corrX)]))
rownames(dfcor1) <- "Largest corr"
# library(xtable)
# print(xtable(dfcor1), type = "html")
pander(dfcor1)| 0.739&0.779 | 4.459&4.499 | 1.019&1.059 | 4.379&4.419 | |
|---|---|---|---|---|
| Largest corr | 0.9757 | 0.9804 | 0.9877 | 0.9888 |
| 4.419&4.459 | |
|---|---|
| Largest corr | 0.9899 |
dfcor2 <- t(as.data.frame(sort(abs(corrX))[1:4]))
rownames(dfcor2) <- "Lowest corr"
pander(dfcor2)| 4.499&5.099 | 0.939&0.979 | 1.499&1.539 | 0.899&0.939 | |
|---|---|---|---|---|
| Lowest corr | 0.001844 | 0.001911 | 0.01135 | 0.02956 |
library(mRMRe)library(corrplot)
M <- cor(Xdata)
# #corrplot(unname(M), method="color")
#
library(d3heatmap)
# d3heatmap(M, scale = "col", dendrogram = "none",
# #color = scales::col_quantile("Blues", NULL, 5),
# labRow = substr(rownames(M),1,2), labCol = substr(colnames(M),1,2))Il est intéressant aussi surtout de regarder les corrélations linéaires avec la variable d’intérêt
corr_target <- c()
for (i in 1:(ncol(Xdata)-1)){
corr_target[i] <- cor(Xdata[,i], Xdata[,"class"])
names(corr_target)[i] <- names(Xdata)[i]
}
# sort(corr_target)Ou alors de regarder des indicateurs d’importance des variables par rapport à la variable d’intérêt. Ces indicateurs se basent sur l’entropie et sont non-linéaires.
#library(FSelector)
#
# linear.correlation(as.simple.formula(colnames(Xdata),"Heating.Load"), data_X1)
# rank.correlation(as.simple.formula(colnames(Xdata[,]),"Heating.Load"), data_X1)
library(FSelectorRcpp)
info_gain <- information_gain(x = Xdata[,-ncol(Xdata)], y = as.factor(Xdata$class) , type = c("infogain", "gainratio", "symuncert"), threads = 1)
# info_gain[order(info_gain$importance),]## remove features based on info gain (entropy) --> nonlinear AND on linear correlations
# Here we decide to keep features witn linear corr larger than 5% and that have amount of importance
kept_features <- names(Xdata[, -ncol(Xdata)][(info_gain$importance > 4e-1) & (abs(corr_target) > 0.15)])# Enable parallelization in examples
# library(doSNOW) # doSNOW has an option for progress bar
# cl <- makeCluster(2)
# registerDoSNOW(cl)
#
# evaluator_BIC_glm <- function(attributes, data, dependent = "class") {
# extractAIC(
# fit = glm(to_formula(attributes, dependent), family = binomial,
# data = data),
# k = log(nrow(data))
# )[2]
# }
# feature_search(attributes = names(Xdata[, -ncol(Xdata)]),
# fun = evaluator_BIC_glm,
# data = Xdata, parallel = T,
# mode = "greedy")
#
# stopCluster(cl)
library(mlbench)
library(caret)
# prepare training scheme
# control <- trainControl(method="repeatedcv", number=5, repeats=3)
# # train the model
# model <- train(class~., data=Xdata, method="logreg", preProcess="scale", trControl=control)
# # estimate variable importance
# importance <- varImp(model, scale=FALSE)
# # summarize importance
# print(importance)
# # plot importance
# plot(importance)
# define the control using a random forest selection function
control <- rfeControl(functions=rfFuncs, method="cv", number=5)
# run the RFE algorithm
results <- rfe(Xdata[,-(ncol(Xdata))], Xdata$class, sizes= c(1:5, seq(10, 35, by = 5)), rfeControl=control, metric = "accuracy")
# summarize the results
print(results)
Recursive feature selection
Outer resampling method: Cross-Validated (5 fold)
Resampling performance over subset size:
Variables RMSE Rsquared RMSESD RsquaredSD Selected
1 0.0002971 1.0000 0.0004141 8.508e-07 *
2 0.0498470 0.9772 0.0693183 3.329e-02
3 0.0605808 0.9789 0.0602193 2.421e-02
4 0.0640011 0.9660 0.0892768 6.629e-02
5 0.0572107 0.9767 0.0755158 4.568e-02
10 0.0695924 0.9744 0.0711915 4.502e-02
15 0.0740498 0.9738 0.0705607 4.490e-02
20 0.0753214 0.9764 0.0625696 3.839e-02
25 0.0807647 0.9742 0.0644562 4.194e-02
30 0.0797879 0.9756 0.0647704 3.879e-02
35 0.0794946 0.9750 0.0637038 4.085e-02
224 0.0819109 0.9771 0.0574587 3.212e-02
The top 1 variables (out of 1):
1.059
# list the chosen features
print(kept_features2 <- predictors(results))[1] "1.059"
# plot the results
plot(results, type=c("g", "o"))#devtools::install_github('mlampros/FeatureSelection')
library(FeatureSelection)
#source("Wrapper_featureselection_package_fun.R")
params_glmnet = list(alpha = 1, family = 'gaussian', nfolds = 5, parallel = TRUE)
params_xgboost = list( params = list("objective" = "reg:linear", "bst:eta" = 0.001, "subsample" = 0.75, "max_depth" = 5, "colsample_bytree" = 0.75, "nthread" = 6),
nrounds = 1000, print.every.n = 1000, maximize = FALSE)
# params_ranger1 = list(probability = FALSE, num.trees = 1000, verbose = TRUE, mtry = 5, min.node.size = 10, num.threads = 6, classifiction = FALSE, importance = 'permutation')
params_features = list(keep_number_feat = 15, union = TRUE) # say we want 15 features max
#Xdata$class <- as.factor(Xdata$class)
feat = wrapper_feat_select(X = Xdata[,-(ncol(Xdata))], y = Xdata$class, params_glmnet = params_glmnet, params_xgboost = params_xgboost, xgb_sort = 'Gain', CV_folds = 5, stratified_regr = FALSE, scale_coefs_glmnet = FALSE, cores_glmnet = 8, params_features = params_features, verbose = F)[1] train-rmse:0.358855
[1000] train-rmse:0.000280
[1] train-rmse:0.357917
[1000] train-rmse:0.000121
[1] train-rmse:0.357739
[1000] train-rmse:0.000266
[1] train-rmse:0.358949
[1000] train-rmse:0.000166
[1] train-rmse:0.358359
[1000] train-rmse:0.000225
#feat$all_feat
feat$union_feat feature importance Frequency
12 3.379 1.000000000 2
21 7.219 0.940823561 2
2 0.899 0.916825743 2
1 0.859 0.914098718 2
5 2.259 0.752113444 2
7 2.739 0.661576220 2
16 4.139 0.541314426 2
33 0.979 0.501772566 1
35 1.059 0.495500409 1
17 4.259 0.490591764 2
61 3.699 0.482956095 1
27 0.579 0.476683938 1
31 0.779 0.470411781 1
3 0.939 0.469593673 1
37 1.139 0.464139624 1
10 3.259 0.458140169 2
38 1.179 0.457867467 1
30 0.699 0.451595310 1
32 0.819 0.445323152 1
60 3.419 0.432778838 1
28 0.619 0.420234524 1
25 0.499 0.413962367 1
29 0.659 0.407690210 1
59 3.299 0.401418053 1
26 0.539 0.395145896 1
74 6.339 0.388873739 1
43 1.419 0.382601582 1
6 2.419 0.380147259 1
41 1.339 0.376329425 1
34 1.019 0.370057268 1
39 1.259 0.363785110 1
53 2.339 0.357512953 1
73 6.259 0.351240796 1
48 1.899 0.344968639 1
71 6.019 0.338696482 1
51 2.139 0.332424325 1
83 7.459 0.319880011 1
58 3.219 0.313607854 1
13 3.459 0.309790019 2
45 1.499 0.307335697 1
62 3.819 0.301063540 1
50 2.019 0.294791383 1
15 4.059 0.290700845 1
78 7.099 0.288519226 1
44 1.459 0.282247068 1
52 2.299 0.275974911 1
18 5.779 0.270520862 2
36 1.099 0.269702754 1
19 5.819 0.268339242 1
94 9.979 0.263430597 1
68 5.459 0.257158440 1
40 1.299 0.250886283 1
49 1.979 0.238341969 1
63 3.979 0.232069812 1
14 3.499 0.223616035 1
90 9.219 0.219525498 1
11 3.339 0.217616580 2
42 1.379 0.213253341 1
85 7.899 0.206981184 1
8 2.939 0.201254431 1
66 4.459 0.200709026 1
69 5.699 0.194436869 1
87 8.539 0.188164712 1
55 2.539 0.181892555 1
9 2.979 0.178892828 2
88 8.659 0.169348241 1
79 7.139 0.163076084 1
67 5.219 0.156803927 1
4 2.059 0.156531224 1
56 2.579 0.144259613 1
47 1.659 0.137987456 1
91 9.299 0.131715299 1
46 1.579 0.125443142 1
77 7.019 0.119170984 1
70 5.979 0.112898827 1
20 5.859 0.111808017 1
80 7.179 0.106626670 1
22 7.499 0.089446414 1
92 9.739 0.087810199 1
82 7.419 0.081538042 1
75 6.619 0.075265885 1
89 8.979 0.068993728 1
93 9.819 0.062721571 1
57 2.779 0.056449414 1
81 7.299 0.050177257 1
54 2.379 0.043905100 1
84 7.659 0.037632942 1
72 6.139 0.031360785 1
76 6.939 0.025088628 1
24 9.939 0.022361603 1
86 8.019 0.018816471 1
64 4.219 0.012544314 1
65 4.419 0.006272157 1
23 9.579 0.000000000 1
Xdata[, -ncol(Xdata)] <- as.data.frame(scale(Xdata[, -ncol(Xdata)])) # Scale ? scale les variables (qui sont sur une très petite échelle à la base) ?
Dénition des critères de classification
modnames <- c('Logistique-SW','PLS-DA','OPLS-DA','Logistique-Pen')
# Preparation des matrices de résultats
RMSE <- matrix(nrow = 4,ncol=3)
colnames(RMSE) = c('RMSE','RMSE-CV','Taille du modèle') ; rownames(RMSE) = modnames
coefficient = matrix(0, ncol = ncol(Xdata)-1, nrow=4)
colnames(coefficient) = colnames(XC)[1:(ncol(Xdata)-1)] ; rownames(coefficient) =modnames
PredMat=matrix(nrow = nrow(Xdata),ncol= 4) ; colnames(PredMat)=modnames
listr=list(coefficient=coefficient,RMSE=RMSE,PredMat=PredMat)
# fonction pour stocker et imprimer les résultats de chaque modèle
'printresmod' <- function(i, YC, yfit, yfitCV, RMSECV, coef, parmod, listr){
g1 <- ggplot(data.frame(YC = YC, yfit = yfit)) + geom_point(aes(x = YC, y = yfit), size = .5) + theme_piss()
g2 <- ggplot(data.frame(YC = YC, yfitCV = yfitCV)) + geom_point(aes(x = YC, y = yfitCV), size = .5) + theme_piss()
subplot(ggplotly(g1), ggplotly(g2), shareX = T, shareY= F, titleY=T) %>% layout(showlegend = F) %>% print()
# calcul des RMSE
n=length(YC)
listr$RMSE[i,1] <- sqrt(sum((YC-yfit)^2)/n) ; listr$RMSE[i,2] = RMSECV ; listr$RMSE[i,3] = parmod
# Récupération des coefficients et des réponses
listr$coefficient[i,]=coef ; listr$PredMat[,i]=yfit
# affiichage de résultats
pander(listr$RMSE[i,])
par(mfrow=c(1,1))
dotchart(listr$coefficient[i,],labels=colnames(listr$coefficient),xlab="Coefficients",main=paste("Coefficients ",dimnames(listr$coefficient)[[1]][i]))
return(listr)
}
# Definition d'une fonction qui fait de la k-fold CV sur pour une fonction de régression prédéfinie fctreg
'kfoldCV' <- function(X ,Y, k, argrm = 0, seed = 100){
n=dim(X)[1] ; set.seed=seed ; nbseg = k ; sn=ceiling(n/k)
seg_i <- sample(rep(1:k, length.out = n)) ; yfit.cv = numeric()
for (i in 1:nbseg){
# browser()
val_i <- which(seg_i == i)
Y.train = Y[-val_i] ; X.train =X[-val_i,] ; X.valid=X[val_i,]
#browser()
yfitcv <- fctreg(X.train, Y.train, X.valid, argrm = argrm) ; yfit.cv[val_i] = yfitcv
}
RMSE.cv<- sqrt( (1/n)*sum((Y-yfit.cv)^2) )
return(list(yfit.cv=yfit.cv,RMSE.cv=RMSE.cv))
}Comme premier modèle, nous considérons un modèle logistique de type stepwise forward. Le problème avec ce genre de modèle de régression est lorsque le nombre de de variables est grand par rapport au nombre de points (typiquement \(p>n\)) est qu’on ne saura pas estimer de paramètres, i.e. nous ne bénéficions pas d’assez de points par rapport au nombre de degrés de libertés.
On va donc ici appliquer notre feature selection préliminaire.
Xdata_reg <- Xdata[, kept_features]
Xdata_reg <- cbind.data.frame(Xdata_reg, class = Xdata$class)On remarque ici directement, que ce soit selon le critère AIC ou BIC, c’est le modèle avec comme seule variable 0.899 qui est choisi. Celui-ci permet de prédire parfaitement (sur le training set !) à quel patient appartient l’échantillon de sang.
On va alors plutôt On va plutôt faire du 5-fold car la taille de l’échantillon est déjà très petite (5). L’erreur de validation serait alors trop peu précise car basée sur trop peu de données.
yfit = fitted.values(fit.sw)
coef=coefficients(fit.sw)
# cross validation k-fold pour vérifier le pouvoir prédictif du modèle
'fctreg' <- function(X, Y, XV, argrm){
fit.sw1 <- step(null, scope = list(lower=null, upper=full), direction="both", trace = 0)
coefglm <- coef(fit.sw1)
# browser()
logistyfit <- cbind(1, XV[, colnames(XV) %in% names(fit.sw1$model)[2] ]) %*% as.matrix(coefglm)
exp(logistyfit)/(1+exp(logistyfit))
}
res5cv <- kfoldCV(Xdata_reg[,-ncol(Xdata_reg)], Xdata$class, k = 5)
# Sauvegardes et impression de résultats
listr <- printresmod(1, Xdata$class, yfit, res5cv$yfit.cv, res5cv$RMSE.cv, coef = coef, length(names(coef)), listr)tc <- trainControl("cv", 5, savePredictions = T) #"cv" = cross-validation, 10-fold
fit <- train(class ~. ,
data = Xdata ,
method = "glm" ,
family = binomial ,
trControl = tc)
# fit$results
# fit$modelType
# fit$modelInfo
# fit$finalModel
#plot(fit)On doit choisir un seuil. On pourrait prendre intuitivement 0.5, mais ce n’est probablement pas optimale.
# Recherche du nombre de composantes optimales par CV
## définition de la fonction d'estimation du modèle
'fctreg' <- function(X, Y, XV, argrm){
coef <- coef(pls::plsr(Y ~ ., data = X, ncomp = ncomp))
# browser()
as.matrix(XV) %*% as.matrix(as.numeric(coef[,,]))
}
## Boucle sur le nombre de composantes possibles
RMSECV <- numeric()
yfitCV <- list() ; NCmax <- 20 # tune it
for(i in 1:NCmax){
rescv <- kfoldCV(Xdata[,-ncol(Xdata)], Xdata$class, k = 5, argrm = i)
RMSECV[i] <- rescv$RMSE.cv
yfitCV[[i]] <- rescv$yfit.cv
}
# Recherche du nombre de composantes à garder
df_plsda <- data.frame(N_comp = 1:NCmax, RMSEcv = RMSECV)
ncopt <- which.min(RMSECV)
gg <- ggplot(df_plsda, aes(x = N_comp, y = RMSEcv)) + geom_point() + geom_line() + geom_vline(xintercept = ncopt, col = 2, linetype = 2) +theme_piss(size_c = 12) + labs(title = "RMSE en cross-validation vs complexité", x = "Nombre de composantes", y = "RMSE en cv")
ggplotly(gg)# Ajustement du modèle avec le nombre optimal de composantes
fit.plsr <- plsr(class ~., data = Xdata, ncomp=max(ncopt,2), validation = "none")
print(summary(fit.plsr))Data: X dimension: 54 224 Y dimension: 54 1 Fit method: kernelpls Number of components considered: 7 TRAINING: % variance explained 1 comps 2 comps 3 comps 4 comps 5 comps 6 comps 7 comps X 28.66 50.51 67.41 75.63 83.99 85.86 88.58 class 73.16 89.09 94.28 98.58 99.36 99.78 99.84 NULL
yfit <- fit.plsr$fitted.values[,,ncopt]
# Sauvegardes et impression de résultats
MY <- mean(Xdata$class)
YC <- scale(Xdata$class, center=T, scale=F)
listr <- printresmod(2, YC+MY, yfit+MY, yfitCV[[ncopt]]+MY, RMSECV[ncopt], coef(fit.plsr), ncopt, listr)Représentation graphique des scores pour les 4 premières composantes
On refait tout d’abord la PCA avec seulement les individus des 2 groupes choisis.
PCA.res = SVDforPCA(Xdata,ncomp = ncomp)
### Scores
par(mfrow=c(1,1))
PCA.res$scores[,1:2] <- fit.plsr$scores[,1:2]
PCA.res$eigval=rep(0,2)
DrawSL(PCA.res, type.obj = "PCA", drawNames=TRUE,createWindow=FALSE, main = paste("PLS score plot for PC1 and PC2"), axes =c(1,2), type.graph = "scores", class = Xdata$class)
### Graphe des loadings
PCA.res$loadings=fit.plsr$loadings
DrawSL(PCA.res, type.obj = "PCA", drawNames=F,createWindow=FALSE,
axes = c(1:2), type.graph ="loadings", loadingstype="p",ang=90,xaxis="character",nxaxis=10)[[1]]
# Recherche du nombre de composantes optimales par CV
## définition de la fonction d'estimation du modèle
fctreg=function(X,Y,XV,argrm) { as.matrix(XV) %*% OPLSDA(X, Y, no = argrm-1)$b }
## Boucle sur le nombre de composantes possibles
RMSECV=numeric()
yfitCV=list()
indi=2:20
for(i in 1:length(indi)){
rescv <- kfoldCV(as.matrix(Xdata[,-ncol(Xdata)]), Xdata$class, k = 5, argrm = indi[i])
RMSECV[i] <- rescv$RMSE.cv
yfitCV[[i]] <- rescv$yfit.cv
}
# Recherche du nombre de composantes à garder
inc=which.min(RMSECV) ; ncopt=indi[inc]
df_oplsda <- data.frame(indi = indi, RMSEcv = RMSECV)
ncopt <- which.min(RMSECV)
gg <- ggplot(df_oplsda, aes(x = indi, y = RMSEcv)) + geom_point() + geom_line() + geom_vline(xintercept = ncopt, col = 2, linetype = 2) + theme_piss(size_c = 12) + labs(title = "RMSE en cross-validation vs complexité", x = "Nombre de composantes", y = "RMSE en cv")
ggplotly(gg)# Ajustement du modèle avec le nombre optimal de composantes
fit.opls <- OPLSDA(as.matrix(Xdata[,-ncol(Xdata)]), Xdata$class, no = ncopt-1)
coefopls=fit.opls$b
yfit <- as.matrix(Xdata[,-ncol(Xdata)]) %*% coefopls
# Sauvegardes et impression de résultats
MY <- mean(Xdata$class)
listr=printresmod(4,Xdata$class+MY,yfit+MY,yfitCV[[ncopt]]+MY,RMSECV[ncopt],coefopls,ncopt,listr)### Scores
par(mfrow=c(1,1))
DrawSL(fit.opls, type.obj = "OPLSDA", drawNames=TRUE,createWindow=FALSE, main = paste("OPLS score plot for PC and PC1-Ortho"), axes =c(1,2), type.graph="scores",class=as.factor(Xdata$class))
### Graphe des loadings
#PCA.res$loadings=fit.plsr$loadings
DrawSL(fit.opls, type.obj = "OPLSDA", drawNames=F,createWindow=FALSE,
axes = c(1:2), type.graph ="loadings", loadingstype="l",xlab= "ppm??",ang=90,xaxis="character",nxaxis=10)[[1]]
Comme annoncé lors du cours, on laisse tomber le Ridge; le LASSO fera fera, basé sur le même “principe”, fera l’affaire.
Néanmoins, on regardera aussi à
library(glmnet)
fit <- glmnet(as.matrix(Xdata[,-ncol(Xdata)]), as.factor(Xdata$class), nlambda = 50, alpha = 1, family = "binomial")
# alpha = 1 for lasso penalty !
plot(fit ,xvar="lambda", main="") # Plot the paths for the fitfit # look at the fit for each coefficient
Call: glmnet(x = as.matrix(Xdata[, -ncol(Xdata)]), y = as.factor(Xdata$class), family = "binomial", alpha = 1, nlambda = 50)
Df %Dev Lambda
[1,] 0 9.610e-16 0.456900
[2,] 1 1.036e-01 0.415900
[3,] 1 1.915e-01 0.378600
[4,] 1 2.674e-01 0.344600
[5,] 2 3.345e-01 0.313700
[6,] 3 3.971e-01 0.285600
[7,] 4 4.593e-01 0.260000
[8,] 4 5.154e-01 0.236600
[9,] 4 5.647e-01 0.215400
[10,] 5 6.083e-01 0.196100
[11,] 5 6.472e-01 0.178500
[12,] 5 6.816e-01 0.162500
[13,] 5 7.124e-01 0.147900
[14,] 5 7.399e-01 0.134600
[15,] 5 7.645e-01 0.122600
[16,] 5 7.866e-01 0.111600
[17,] 5 8.065e-01 0.101600
[18,] 5 8.244e-01 0.092460
[19,] 5 8.406e-01 0.084160
[20,] 5 8.552e-01 0.076610
[21,] 5 8.684e-01 0.069740
[22,] 5 8.804e-01 0.063490
[23,] 5 8.912e-01 0.057790
[24,] 5 9.010e-01 0.052610
[25,] 5 9.099e-01 0.047890
[26,] 6 9.180e-01 0.043590
[27,] 6 9.254e-01 0.039680
[28,] 6 9.321e-01 0.036120
[29,] 6 9.382e-01 0.032880
[30,] 6 9.437e-01 0.029930
[31,] 7 9.488e-01 0.027250
[32,] 7 9.534e-01 0.024800
[33,] 7 9.576e-01 0.022580
[34,] 7 9.614e-01 0.020550
[35,] 7 9.649e-01 0.018710
[36,] 7 9.681e-01 0.017030
[37,] 7 9.709e-01 0.015500
[38,] 7 9.735e-01 0.014110
[39,] 7 9.759e-01 0.012850
[40,] 8 9.781e-01 0.011690
[41,] 8 9.801e-01 0.010650
[42,] 9 9.819e-01 0.009691
[43,] 9 9.835e-01 0.008821
[44,] 9 9.850e-01 0.008030
[45,] 9 9.863e-01 0.007310
[46,] 9 9.876e-01 0.006654
[47,] 9 9.887e-01 0.006057
[48,] 9 9.897e-01 0.005514
[49,] 9 9.906e-01 0.005019
[50,] 9 9.915e-01 0.004569
require(doMC)
registerDoMC(cores = 8) # Go parallel with the method : gain computation time
cv.fit <- cv.glmnet(as.matrix(Xdata[,-ncol(Xdata)]), parallel = T, Xdata$class, nfolds = 5) # Perform cross validation on the fited model
plot(cv.fit) # Plot the mean sq error for the cross validated fit as a functiontpred <- predict(fit, as.matrix(Xdata[,-ncol(Xdata)])) # Predictions on the test data
mse <- apply((tpred - Xdata$class)^2, 2, mean) # Compute mse for the predictions
plot(log(fit$lambda), mse, col="blue", pch="*") # overlay the mse predictions on the plot
legend("topleft",legend=c("10 fold CV","Test"),pch="*",col=c("red","blue"))fit$df [1] 0 1 1 1 2 3 4 4 4 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 7 7 7 7 7 7 7 7
[39] 7 8 8 9 9 9 9 9 9 9 9 9
cv.fit$lambda.min[1] 0.00501444
L’avantage de cette méthode comparée au LASSO (voir (Zou and Hastie 2005) ) est :
\[\text{argmin}_{\beta} \Big\{ \sum_{i=1}^n(y_i-x_i'\boldsymbol{\beta})^2 +\lambda_1\sum_{i=1}^n\beta_i^2 + \lambda_2\sum_{j=1}^n|\beta_i|\Big\} \] On voit directement que les 2 termes de régularisation apparaissent ici dans la fonction objective à minimiser.
fit <- glmnet(as.matrix(Xdata[,-ncol(Xdata)]), as.factor(Xdata$class), nlambda = 50, alpha = .5, family = "binomial") # Recherche le paramètre de lissage qui min RMSE
## définition de la fonction d'estimation du modèle
#lambda=0.0001*10^(seq(0.5,2.5,0.5))
lambda <- 1e-4 * 10^(seq(1,2,0.5))
'fctreg' <- function(X, Y, XV, argrm){
fit.lasso <- penalized::penalized(response = Y, penalized = ~ -1 + X, lambda1 = argrm, lambda2 = 0 , trace=F)
attr(fit.lasso,'penalized')
}
fit.lasso <- penalized::penalized(response = Xdata$class, penalized = ~ as.matrix(-1 + Xdata[,-ncol(Xdata)]), lambda1 = lambda[1], lambda2 = 0 , trace=F)
## Boucle sur le nombre de composantes possibles
RMSECV=numeric()
yfitCV=list()
for(i in 1:length(lambda)){
cat("\n ", i)
rescv <- kfoldCV(Xdata[,-ncol(Xdata)], Xdata$class, k = 5, argrm = lambda[1])
RMSECV[i] <- rescv$RMSE.cv
yfitCV[[i]] <- rescv$yfit.cv
}
# Recherche du nombre de composantes à garder
par(mfrow=c(1,1))
plot(lambda,RMSECV,type="o",main="RMSE cross-validation",log="x")
il=which.min(RMSECV)
llassoopt=lambda[il]
abline(v=llassoopt,col=2,lty=2)
# Ajustement du modèle avec le nombre optimal de composantes
fit.lasso <- penalized(response=YC,penalized=~-1+XC,lambda1=llassoopt, lambda2=0 ,trace=F )
coeflasso=attr(fit.lasso,'penalized')
yfit=XC%*%coeflasso
# Sauvegardes et impression de résultats
listr=printresmod(7,YC,yfit,yfitCV[[il]],RMSECV[il],coeflasso,lambda[il],listr)On voit clairement que les
Zou, H., and T. Hastie. 2005. “Regularization and Variable Selection via the Elastic Net.” Journal of the Royal Statistical Society Series B 67. http://dx.doi.org/10.1111/j.1467-9868.2005.00503.x.