CONSTRUCTION D’UN CLASSIFIEUR BAYESIEN NAIF EN R
Fouille de données avec R Léa Lê Dinh : Esiee Paris 2022-2023
Le classificateur bayésien naïf est un algorithme d’apprentissage très utilisé en Machine Learning. Il est basé sur le théorème probabiliste de Bayes. Le classifieur bayésien naïf suppose que l’existence d’une variable explicative pour une classe, est indépendante de l’existence d’autres variables explicatives.
Tout d’abord, nous installons les librairies dont nous aurons besoin.
rm(list = ls()) # initialisation
library(kableExtra) # chargement du paquet
library('e1071')
library(caret)
## Le chargement a nécessité le package : ggplot2
## Le chargement a nécessité le package : lattice
Puis, nous créons un Dataframe df_1 et entrons les valeurs dont nous avons besoin.
var_1 <- c('<30', '30-50', '30-50', '30-50', '>50')
var_2 <- c('<20', '<20', '<20', '>20', '>20')
var_3 <- c('Oui', 'Non', 'Oui', 'Non', 'Non')
var_4 <- c('Non', 'Oui', 'Oui', 'Non', 'Oui')
df_1 <- data.frame('Salaire' = var_1,
'Impots' = var_2,
'Etudiant' = var_3,
'Controle' = var_4)
Nous l’affichons :
df_1 %>% kbl(digits=3) %>%
kable_styling(bootstrap_options = "striped", full_width = F, position = "center", latex_options = 'stripped') %>%
scroll_box( height = "250px")
| Salaire | Impots | Etudiant | Controle |
|---|---|---|---|
| <30 | <20 | Oui | Non |
| 30-50 | <20 | Non | Oui |
| 30-50 | <20 | Oui | Oui |
| 30-50 | >20 | Non | Non |
| >50 | >20 | Non | Oui |
Nous scindons le Dataframe en 2 Dataframes. Le premier, appelé X, contient les 3 premières colonnes de df_1 et le second, appelé Y, contient la dernière colonne de df_1. X correspond aux variables prédictives et Y contient la variable à prédire.
#Dataframe df_2 créé en isolant la 4e colonne de df_1
Y <- as.vector(df_1[,c(4)])
data.frame(Y=Y) %>% kbl(digits=3) %>%
kable_styling(bootstrap_options = "striped", full_width = F, position = "center", latex_options = 'stripped') %>%
scroll_box( height = "250px")
| Y |
|---|
| Non |
| Oui |
| Oui |
| Non |
| Oui |
is.vector(Y)
## [1] TRUE
#Suppression de la dernière colonne du dataframe df_1
X <- df_1[,-4]
X %>% kbl(digits=3) %>%
kable_styling(bootstrap_options = "striped", full_width = F, position = "center", latex_options = 'stripped') %>%
scroll_box( height = "250px")
| Salaire | Impots | Etudiant |
|---|---|---|
| <30 | <20 | Oui |
| 30-50 | <20 | Non |
| 30-50 | <20 | Oui |
| 30-50 | >20 | Non |
| >50 | >20 | Non |
# data.frame(Y=Y) %>% kbl(digits=3) %>%
# kable_styling(bootstrap_options = "striped", full_width = F, position = "center", latex_options = 'stripped') %>%
# scroll_box( height = "250px")
Nous créons des tableaux de contingence entre chaque variable explicative et la variable à prédire.
Nous obtenons donc 3 tableaux de contingence (Salaire/Controle, Impots/Controle, Etudiant/Controle).
Nous stockons les tableaux dans une liste que nous appelons tabcont.
tabcont <- lapply(X,function(x) {y = table(Y,x)})
tabcont
## $Salaire
## x
## Y <30 >50 30-50
## Non 1 0 1
## Oui 0 1 2
##
## $Impots
## x
## Y <20 >20
## Non 1 1
## Oui 2 1
##
## $Etudiant
## x
## Y Non Oui
## Non 1 1
## Oui 2 1
Nous modifions les tableaux de contingence précédemment créés.
Nous obtenons la liste des probabilités conditionnelles que nous appelons prop_cond.
#m = vaut par défaut 1
m = 1
#k = nombre de facteur de la variable à prédire
k = 2
#nk est notre tableau
prob_cond <- lapply(X, function(x){
tabcont1 <- table(Y,x)
tabcont2 <- tabcont1 + m
tabcont3 <- rowSums(tabcont1)+k*m
tabcont4 <- tabcont2/tabcont3
return(tabcont4)
})
print(prob_cond)
## $Salaire
## x
## Y <30 >50 30-50
## Non 0.50 0.25 0.50
## Oui 0.20 0.40 0.60
##
## $Impots
## x
## Y <20 >20
## Non 0.5 0.5
## Oui 0.6 0.4
##
## $Etudiant
## x
## Y Non Oui
## Non 0.5 0.5
## Oui 0.6 0.4
Nous cherchons à déterminer, pour chaque individu test, quelles sont les probabilités d’appartenance aux groupes (contrôle = Oui - contrôle = Non) en connaissant les valeurs des variables prédictives (contenue dans le dataframe X).
La probabilité d’appartenance à priori correspond à la probabilité qu’un individu ait un contrôle ou non. Les résultats sont stockés dans un vecteur priori.
#Permet de compter le nombre d'individus dans chaque catégorie (oui/non)
#prob.table
nb_cont <- table(df_1$Controle)
print(nb_cont)
##
## Non Oui
## 2 3
#On transforme en probabilités
priori <- nb_cont/sum(nb_cont)
print(priori)
##
## Non Oui
## 0.4 0.6
priori_labels <- c("Non", "Oui")
print(priori_labels)
## [1] "Non" "Oui"
Nous créons un nouveau dataframe data_test :
var_1 <- c('30-50', '>50', '<30')
var_2 <- c('<20', '<20', '<20')
var_3 <- c('Oui', 'Non', 'Oui')
var_4 <- c('Oui', 'Oui', 'Non')
data_test <- data.frame('Salaire' = var_1,
'Impots' = var_2,
'Etudiant' = var_3,
'Pred' = var_4)
data_test %>% kbl(digits=3) %>%
kable_styling(bootstrap_options = "striped", full_width = F, position = "center", latex_options = 'stripped') %>%
scroll_box( height = "250px")
| Salaire | Impots | Etudiant | Pred |
|---|---|---|---|
| 30-50 | <20 | Oui | Oui |
| >50 | <20 | Non | Oui |
| <30 | <20 | Oui | Non |
Il faut écrire 3 boucles “for” l’une dans l’autre afin de sélectionner un individu, puis extraire les probabilités a priori, puis extraire les probabilités conditionnelles.
# On initialise lnV comme étant un dataframe vide
lnV <- data.frame()
# Première boucle : on sélectionne l'individu entre 1 et nombre total d'individu de data_test, soit le nombre de lignes de data_test
for(i in 1 : nrow(data_test)) {
logS <- 0
#Deuxième boucle : on extrait la probabilité priori
for(j in 1 : length(priori)){
#Troisième boucle : pour k entre 1 et 3 (longueur de la liste des tableaux de contingence prob_cond)
for(k in 1 : length(prob_cond)){
p <- prob_cond[[k]][j,data_test[i,k]]
logS <- logS + log(p)
}
if(j==1){
# Ajout la valeur de logS + log(priori[1]) à la colonne "Oui" du dataframe lnV
lnV[i,"Oui"] <- logS + log(priori[j])
}
else{
# Ajout la valeur de logS + log(priori[2]) à la colonne "Non" du dataframe lnV
lnV[i,"Non"] <- logS + log(priori[j])
}
logS<-0
}
}
print(lnV)
## Oui Non
## 1 -2.995732 -2.448768
## 2 -3.688879 -2.448768
## 3 -2.995732 -3.547380
Dans cette partie, nous utilisons les scripts précédemment écrits afin de créeer deux fonctions. Ces deux fonctions nous permettent de réutiliser le code indépendamment du nombre de variables contenues dans la base de données et du nombre de catégories (labels).
La première fonction Naive_Bayes nous permet d’obtenir : - Les tableaux de contingence avec les probabilités conditionnelles corrigées - Les probabilités d’appartenance à priori
Ces résultats seront contenus dans une liste “out”.
Naive_Bayes <- function(X,Y,m,k){
prob_cond <- lapply(X, function(x){
tabcont1 <- table(Y,x)
tabcont2 <- tabcont1 + m
tabcont3 <- rowSums(tabcont1)+k*m
tabcont4 <- tabcont2/tabcont3
return(tabcont4)
})
priori <- nb_cont/sum(nb_cont)
out <- list("prob_cond" = prob_cond, "priori" = priori)
}
Nous utilisons cette fonction sur notre dataframe.
naive_data <- Naive_Bayes(X,Y,m=1,2)
print(naive_data)
## $prob_cond
## $prob_cond$Salaire
## x
## Y <30 >50 30-50
## Non 0.50 0.25 0.50
## Oui 0.20 0.40 0.60
##
## $prob_cond$Impots
## x
## Y <20 >20
## Non 0.5 0.5
## Oui 0.6 0.4
##
## $prob_cond$Etudiant
## x
## Y Non Oui
## Non 0.5 0.5
## Oui 0.6 0.4
##
##
## $priori
##
## Non Oui
## 0.4 0.6
Nous allons utiliser les fonctions précédemment écrites sur un jeu de données contenant 4000 instances.
load('data_test.Rda')
X <- data_test[,-12]
Y <- data_test$event
naive_datatest <- Naive_Bayes(X,Y,m=1,2)
print(naive_datatest)
## $prob_cond
## $prob_cond$age
## x
## Y level_1 level_2 level_3 level_4
## vv 0.2788365 0.2562688 0.3099298 0.1559679
## dcd 0.1353234 0.2034826 0.3502488 0.3119403
##
## $prob_cond$cpk
## x
## Y Normal Eleve
## vv 0.6549649 0.3450351
## dcd 0.6462687 0.3537313
##
## $prob_cond$height
## x
## Y level_1 level_2 level_3 level_4
## vv 0.1213641 0.3204614 0.4242728 0.1349047
## dcd 0.1199005 0.3328358 0.3915423 0.1567164
##
## $prob_cond$frac
## x
## Y level_1 level_2 level_3 level_4
## vv 0.30391174 0.41825476 0.22718154 0.05165496
## dcd 0.17810945 0.38308458 0.32835821 0.11144279
##
## $prob_cond$hbp
## x
## Y level_1
## vv 0.9994985
## dcd 0.9995025
##
## $prob_cond$ap_lo
## x
## Y level_1 level_2 level_3
## vv 0.9974924774 0.0025075226 0.0005015045
## dcd 0.9731343284 0.0258706468 0.0014925373
##
## $prob_cond$creat
## x
## Y Faible Fort Moyen
## vv 0.83400201 0.05215647 0.11434303
## dcd 0.65323383 0.18656716 0.16069652
##
## $prob_cond$Na
## x
## Y Faible Fort Moyen
## vv 0.87562688 0.05917753 0.06569709
## dcd 0.81194030 0.10149254 0.08706468
##
## $prob_cond$smoke
## x
## Y Non Oui
## vv 0.90672016 0.09327984
## dcd 0.91343284 0.08656716
##
## $prob_cond$alco
## x
## Y Non Oui
## vv 0.94684052 0.05315948
## dcd 0.94577114 0.05422886
##
## $prob_cond$active
## x
## Y Non Oui
## vv 0.1895687 0.8104313
## dcd 0.2238806 0.7761194
##
##
## $priori
##
## Non Oui
## 0.4 0.6
Nous comparons les résultats avec la fonction naiveBayes qui existe sur R.
naiveR <- naiveBayes(event ~ ., data = data_test)
print(naiveR)
##
## Naive Bayes Classifier for Discrete Predictors
##
## Call:
## naiveBayes.default(x = X, y = Y, laplace = laplace)
##
## A-priori probabilities:
## Y
## vv dcd
## 0.498 0.502
##
## Conditional probabilities:
## age
## Y level_1 level_2 level_3 level_4
## vv 0.2786145 0.2560241 0.3097390 0.1556225
## dcd 0.1349602 0.2031873 0.3500996 0.3117530
##
## cpk
## Y Normal Eleve
## vv 0.6551205 0.3448795
## dcd 0.6464143 0.3535857
##
## height
## Y level_1 level_2 level_3 level_4
## vv 0.1209839 0.3202811 0.4241968 0.1345382
## dcd 0.1195219 0.3326693 0.3914343 0.1563745
##
## frac
## Y level_1 level_2 level_3 level_4
## vv 0.30371486 0.41817269 0.22690763 0.05120482
## dcd 0.17778884 0.38296813 0.32818725 0.11105578
##
## hbp
## Y level_1
## vv 1
## dcd 1
##
## ap_lo
## Y level_1 level_2 level_3
## vv 0.9979919679 0.0020080321 0.0000000000
## dcd 0.9736055777 0.0253984064 0.0009960159
##
## creat
## Y Faible Fort Moyen
## vv 0.83433735 0.05170683 0.11395582
## dcd 0.65338645 0.18625498 0.16035857
##
## Na
## Y Faible Fort Moyen
## vv 0.87600402 0.05873494 0.06526104
## dcd 0.81225100 0.10109562 0.08665339
##
## smoke
## Y Non Oui
## vv 0.90712851 0.09287149
## dcd 0.91384462 0.08615538
##
## alco
## Y Non Oui
## vv 0.94728916 0.05271084
## dcd 0.94621514 0.05378486
##
## active
## Y Non Oui
## vv 0.1892570 0.8107430
## dcd 0.2236056 0.7763944
Nous pouvons voir que les résultats sont les mêmes qu’avec la fonction que nous avons écrite.