Introduction

Le package yardstick de Rstudio permet d’obtenir facilement l’AUC, l’indice de Youden, le F-score ou encore le coefficient de correlation de Matthews. On va les calculer sur le jeu de donnees qui suit.

# pour la manip de donnees
library("dplyr")

# pour le jeu de donnees
library("otvPlots")

# pour les KPI
library("yardstick")

# pour la courbe ROC
library("ggplot2")

### les donnees

# on prend une des variables comme score predicitf de la cible
# on met en premier la modalite cible "yes", c'est la convention du package yardstick
set.seed(2019)
donnees = bankData %>% mutate(partition = sample(c("train", "test"), size = nrow(.), replace = T, 
                                                 prob = c(0.7, 0.3)),
                              cible = factor(y, levels = c("yes","no"))) %>% 
  rename(score = duration)

dtf_test = donnees %>% filter(partition == "test")

# frequence cible : 0.12
(freq_cible = donnees %>% summarise(freq_cible = mean(cible == "yes")) %>% .$freq_cible)
[1] 0.1169848

AUC et courbe ROC

# qualite du score : auc 0.81 (!)
# si le dernier contact avec le client a dure longtemps, 
# il y a de fortes chances qu'il ouvre un compte a terme
roc_auc_vec(dtf_test$cible, dtf_test$score)

autoplot(roc_curve(dtf_test, cible, score))
[1] 0.8112117

Les calculs des seuils optimaux

On procede de facon classique :

# on exclut les valeurs min et max des seuils pour eviter le declenchement d'erreurs 
# (prevision constante toujours "oui" ou toujours "non")
vect_seuils = seq(min(dtf_test$score), max(dtf_test$score), length.out = 102)[-c(1, 102)]

KPI = vapply(vect_seuils,
             function(seuil) {
               dtf_test = dtf_test %>% 
                 mutate(prev = factor(c("no","yes")[as.integer(score >= seuil) + 1], 
                                      levels = c("yes","no")))
               Youden = j_index(dtf_test, cible, prev)$.estimate
               Fscore = f_meas(dtf_test, cible, prev)$.estimate
               Matthews = mcc(dtf_test, cible, prev)$.estimate
               return(c("Youden" = Youden, "Fscore" = Fscore, "Matthews" = Matthews))},
             numeric(3))

# seuil qui maximise chaque KPI
index_Youden = which.max(KPI["Youden",])
index_Fscore = which.max(KPI["Fscore",])
index_Matthews = which.max(KPI["Matthews",])

seuil_Youden = vect_seuils[index_Youden]
seuil_Fscore = vect_seuils[index_Fscore]
seuil_Matthews = vect_seuils[index_Matthews]

Indice de Youden maximal

On rappelle que l’indice de Youden peut s’interpreter comme le taux de bien classes qui serait calcule sur un echantillon equilibre.

### perf avec le seuil de Youden 
dtf_test = dtf_test %>% 
  mutate(prev = factor(c("no","yes")[as.integer(score >= seuil_Youden) + 1], 
                       levels = c("yes","no")))

# taux erreur sur echantillon equilibre : 28%
1 - bal_accuracy(dtf_test, cible, prev)$.estimate

# specificite : 0.69
spec(dtf_test, cible, prev)$.estimate

# sensibilite : 0.75
sens(dtf_test, cible, prev)$.estimate

# precision : 0.25
# lift 2.16
precision(dtf_test, cible, prev)$.estimate
precision(dtf_test, cible, prev)$.estimate / freq_cible


# matrice de confusion et les autres KPI
#             Truth
# Prediction  yes   no
#         yes 1225 3629
#         no   407 8095
(conf_Youden = conf_mat(dtf_test, cible, prev))
summary(conf_Youden)
[1] 0.2794616
[1] 0.690464
[1] 0.7506127
[1] 0.2523692
[1] 2.157282
          Truth
Prediction  yes   no
       yes 1225 3629
       no   407 8095
# A tibble: 13 x 3
   .metric              .estimator .estimate
   <chr>                <chr>          <dbl>
 1 accuracy             binary         0.698
 2 kap                  binary         0.238
 3 sens                 binary         0.751
 4 spec                 binary         0.690
 5 ppv                  binary         0.252
 6 npv                  binary         0.952
 7 mcc                  binary         0.300
 8 j_index              binary         0.441
 9 bal_accuracy         binary         0.721
10 detection_prevalence binary         0.363
11 precision            binary         0.252
12 recall               binary         0.751
13 f_meas               binary         0.378

F-score maximal

Quand la cible est rare comme ici, le F-score maximal va correspondre en general a un seuil plus eleve. Autrement dit on va detecter moins de cibles potentielles (sensibilite plus faible) mais elles auront plus de chances de faire partie de la population cible (precision plus elevee, c’est-a-dire lift plus eleve).

# matrice de confusion avec le seuil du Fscore : 
# on perd en sensibilite (0.51) mais on gagne en precision (0.40)
# en detectant 2 061 individus a score eleve (versus 4 854 individus avec le seuil de Youden)
dtf_test = dtf_test %>% 
  mutate(prev = factor(c("no","yes")[as.integer(score >= seuil_Fscore) + 1], 
                       levels = c("yes","no")))

#             Truth
# Prediction   yes    no
#         yes   830  1231
#         no    802 10493
(conf_Fscore = conf_mat(dtf_test, cible, prev))
summary(conf_Fscore)
          Truth
Prediction   yes    no
       yes   830  1231
       no    802 10493
# A tibble: 13 x 3
   .metric              .estimator .estimate
   <chr>                <chr>          <dbl>
 1 accuracy             binary         0.848
 2 kap                  binary         0.363
 3 sens                 binary         0.509
 4 spec                 binary         0.895
 5 ppv                  binary         0.403
 6 npv                  binary         0.929
 7 mcc                  binary         0.366
 8 j_index              binary         0.404
 9 bal_accuracy         binary         0.702
10 detection_prevalence binary         0.154
11 precision            binary         0.403
12 recall               binary         0.509
13 f_meas               binary         0.449

Coefficient de correlation de Matthews maximal

Le coefficient de Matthews fait jouer des roles plus symetriques aux differents nombres dans la matrice de confusion, sur notre exemple on voit qu’en dehors des vrais negatifs tres nombreux, les trois autres nombres dans la matrice de confusion ont des ordres de grandeur proches.

# matrice de confusion avec le seuil de Matthews : 
# sur cet exemple on a encore moins d'individus vises (1 490) et un lift qui frole 4
# et la matrice de confusion est davantage equilibree

dtf_test = dtf_test %>% 
  mutate(prev = factor(c("no","yes")[as.integer(score >= seuil_Matthews) + 1], 
                       levels = c("yes","no")))

#             Truth
# Prediction   yes    no
#         yes   695  795
#         no    937 10929
(conf_Matthews = conf_mat(dtf_test, cible, prev))
summary(conf_Matthews)

summary(conf_Matthews) %>% filter(.metric == "precision") %>% .$.estimate / freq_cible
          Truth
Prediction   yes    no
       yes   695   795
       no    937 10929
# A tibble: 13 x 3
   .metric              .estimator .estimate
   <chr>                <chr>          <dbl>
 1 accuracy             binary         0.870
 2 kap                  binary         0.372
 3 sens                 binary         0.426
 4 spec                 binary         0.932
 5 ppv                  binary         0.466
 6 npv                  binary         0.921
 7 mcc                  binary         0.372
 8 j_index              binary         0.358
 9 bal_accuracy         binary         0.679
10 detection_prevalence binary         0.112
11 precision            binary         0.466
12 recall               binary         0.426
13 f_meas               binary         0.445
[1] 3.98721

retour au debut du document