La forêt aléatoire

add2.png

\bullet Présentation:

La forêt aléatoire, qui a été élaborée en 2001 par Léo Breiman et Adèle Cutler, permet de discriminer une variable réponse Y continue, binaire ou polychotomique (K \geq 2 classes) à partir d’une matrice de P variables explicatives \mathbf{X} = (X ^1, \cdots, X ^P) continues et/ou qualitatives.

La forêt aléatoire consiste en la construction d’une multitude d’arbres de décision. La prédiction/classification se fait alors en fonction d’un système de vote majoritaire au sein de ces différents arbres.

Ce processus est à l’aune de l’objectif que recherche la forêt aléatoire. En effet, ce qui a donné naissance à ce formibable outil, c’est que les arbres de décision peuvent se montrer instables selon la taille de l’échantillon. Le grand principe de la forêt aléatoire est alors de chercher à tirer profit de cette instabilité en les agrégeant entre eux.

La forêt aléatoire appartient à la famille des outils nécessitant leur consolidation par méthodes d’apprentissage. Soit la construction du modèle depuis le jeu d’apprentissage et sa validation sur un jeu test. Par ailleurs, elle nécessite plusieurs paramètres dont ceux liés à la construction des arbres de décision, le nombre d’arbres à construire et le nombre de variables à tirer au sort pour chaque arbre à construire.

\bullet La forêt aléatoire:

Hypothèses préliminaires: \mathbf{X} variables explicatives continues et/ou qualitatives. Y variable réponse continue, binaire ou polychotomique à K \geq 2 classes.

Le principe:

La méthode d’approche est différente et plus coûteuse que celle des arbres décisionnels. Elle consiste en l’utilisation du hasard pour améliorer les performances d’algorithmes de faibles qualités de classification. Nous rajoutons ainsi une part d’aléatoire au cours de la construction des différents arbres qui seront alors agrégés ensembles pour former une forêt.

La forêt aléatoire se construit en concevant un arbre sur un sous-échantillon tiré aléatoirement (ou échantillon « out-of-bag »). Ensuite, pour chacun des arbres à construire, un sous-ensemble de q \leq P variables explicatives est sélectionné aléatoire et servira à leur élaboration respective.

L’objectif de cette approche est de rendre les arbres construits plus indépendants entre eux ce qui offre de meilleures performances lors de l’agrégation en forêt. L’Approche possède l’avantage d’être très fructueuse en grande dimension et d’être simple à mettre en oeuvre. L’utilisation de la forêt aléatoire permet également de s’affranchir de toute phase d’élagage et de tout problème lié à la multicolinéarité des variables.

L’Algorithme pour Y binaire ou polychotomique:

Pour a = 1, \ldots, nba:

___ Tirer un échantillon aléatoire avec remise de (Y,\mathbf{X}),

___ Estimer un arbre avec randomisation des variables.

En fin d’algorithme, nous possédons nba arbres que nous moyennons et la classification d’un individu est prise par vote majoritaire.

Contrairement aux arbres de décision qui procèdent par validation croisée, la forêt aléatoire estime l’erreur de généralisation lors de la construction des arbres.

En effet, pendant la conception de chaque arbre a \in [1, nba], environ \frac{1}{3} des observations (appelé « Out-of-Bag ») ne sont pas tirées lors de l’échantillonnage et ne serviront donc pas à la construction de l’arbre mais comme échantillon test interne pour chaque arbre. Les prédictions « Out-of-Bag » sont ensuites agrégées et le taux d’erreur correspondant estimé est calculé pour la forêt entière.

De part leur méthode de construction, les forêts aléatoires offrent des modèles de classification particulièrement robustes et fiables.

Paramétrage:

Finalement le paramètre le plus important à fixer est celui du nombre d’arbres à générer. Il semblerait que l’ordre le plus stable dans le cas d’échantillons de petites tailles soit de 8000 à 15000. L’objectif lié à ce paramètre est de chercher à faire converger l’erreur générée par le modèle en construisant un nombre suffisant d’arbres puisque la règle décisionnelle se base sur un système de vote.

A noter qu’en général un choix optimal pour q est de l’ordre de q = \sqrt{P}.

\bullet Annexe théorique:

Nous présentons ici la démonstration du calcul de complexité dans le cas d’un arbre infixe.

Le pseudo-code classique d’un arbre est:

Parcours ( arbre binaire A de racine A_0)
——- Si A_0 distinct de NIL \Rightarrow temps constant T(0)=c pour un sous-arbre vide
——- alors
——- Parcours (arbre de racine fils_gauche A_0) \Rightarrow temps T( k )
——- Afficher clé A_0 \Rightarrow temps constant d
——- Parcours (arbre de racine fils_droit A_0) \Rightarrow temps T(n-k-1)
——- FinSi

Le calcul de complexité associé à cet algorithme est:

– Étape initiale:

T(0) = (c + d) \times 0 + 1

– Étape suivante:

T(n) = T(k) + T(n - k - 1) + d

= [(c + d) k + c] + [(c + d) (n - k - 1) + c] + d]

= (c + d) n + c - (c + d) + c + d

= (c + d) n + c

\bullet Exemple:

Soit la base de données suivante,

add

Nous allons modéliser le jeu complet. Nous prenons pour paramètres:

– une forêt de 5 arbres,

– 5 observations minimum requises pour concevoir un nœud,

– 3 observations minimum requises pour concevoir une feuille,

– Le sous-ensemble de variables dans lequel nous tirons aléatoirement sera de taille 2.

Les deux variables tirées aléatoirement pour la construction des différents arbres sont présentées ci-dessous:

– Premier arbre: X ^2 et X ^4,

– Second arbre: X ^2 et X ^3,

– Troisième arbre: X ^3 et X ^4,

– Quatrième arbre: X ^1 et X ^3,

– Cinquième arbre: X ^1 et X ^5.

En appliquant le raisonnement pour toutes les observations, nous obtenons la matrice de confusion associée:

\begin{tabular}{|l|c|c|} \hline & Predit Y1 & Predit Y2 \\ \hline Y1 & 10 & 0 \\ \hline Y2 & 0 & 10 \\ \hline \end{tabular}

Si nous projetons l’individu i de caractéristiques:

(X_i ^1 = 4.0135, X_i ^2 = 9.1338, X_i ^3 = 7.0677, X_i ^4 = 4.1493, X_i ^5 = 9.3804)

, dans la forêt que nous venons de construire nous avons:

– Dans l’arbre N°1, l’individu sera voté Y = 1.

– Dans l’arbre N°2, l’individu sera voté Y = 2.

– Dans l’arbre N°3, l’individu sera voté Y = 1.

– Dans l’arbre N°4, l’individu sera voté Y = 1.

– Dans l’arbre N°5, l’individu sera voté Y = 1.

Nous avons donc quatre votes Y = 1 contre un vote Y = 2, nous classons cet individu chez les Y_1 par conséquent.

\bullet Application informatique:

Procédure SAS:

%macro decision_noeud(DATA=,Y=,n_noeud=);

   /* Macro-programme pour la détermination du cutoff optimal (variable + seuil/modalité) optimisant la classification selon l’indice de pureté de Gini.
       Les paramètres: – DATA, indique la base de données à traiter,
                       – Y, la variable réponse binaire ou multiclasse,
                       – n_noeud, le nombre minimal d’observations que doit contenir les feuilles terminales
       Sortie: la table Resultat_best indiquant la variable et son seuil/modalité optimisant l’indice de pureté de Gini
       NB: Certains arbres plantent mais du coup ne seront pas pris en compte dans les votes pour la prédiction finale. En attendant une nouvelle version se bug persiste. */

    /* Options nécessaires pour éviter de saturer le log et mettre en évidence les erreurs */
options nonotes spool;

    /* Sortie de l’effectif total en cours */
proc sql noprint;
select count(*) into: n_encours from &DATA.;
quit;

    /* Ajustement de l’intervalle d’observations à prendre en compte en fonction du paramètre n_noeud */
%let n_encoursp = %eval(&n_encours. – &n_noeud. + 1);

    /* Sortie du nombre de variables à gérer */
proc contents data = &DATA. (drop =  &Y.) out = biblio noprint;
run;

    /* Mise sous macro-variable des noms et du nombre de variables à gérer */
proc sql noprint;
select distinct(name) into: list_var separated by  »  » from biblio;
select count(*) into: nb_var from biblio;
quit;

    /* Sortie des informations sur la variable réponse Y */
proc freq data = &DATA. noprint;
tables &Y. / out = Y;
run;

data Y;
set Y;
copy = compress(« ‘ »||&Y.|| »‘n »);
run;

    /* Sortie de la liste des classes de la variable réponse Y */
proc sql noprint;
select distinct(copy) into: list_class separated by « , » from Y;
select distinct(&Y.) into: list_class2 separated by  »  » from Y;
select count(distinct(&Y.)) into: nb_class from Y;
quit;

    /* Initialisation de la table des résultats finaux */
data Resultats_best;
length var $50. cutoffQ $50.;
cutoffC = .;
run;

    /* Pour chaque variable explicative */
%do p = 1 %to &nb_var.;

        /* Récupération du format de la variable pour ajuster le traitement */
data _null_;
set biblio;
if name = « %scan(&list_var.,&p.) »;
call symput(« Type »,Type);
run;

     /* Initialisation de la table des résultats associés à la variable en cours et venant compléter la table des résultats finaux */
data Resultats_encours;
length var $50. cutoffQ $50.;
cutoffC = .;
run;

        /* Si la variable est continue */
%if &Type. = 1 %then %do;

proc sort data = &DATA.;
by %scan(&list_var.,&p.);
run;

            /* Chaque observation équivaut alors à un cutoff sous condition de respecter le paramètre n_noeud */
%do i = &n_noeud. %to &n_encoursp.;

           /* Fixation du cutoff <, >= */
data encours;
set &DATA.;
length cutoff $2.;
cutoff = « <« ;
if _n_ >= &i. then cutoff = « >= »;
run;

            /* Répartition des classes de Y en fonction du cutoff temporaire */
proc freq data = encours noprint;
tables &Y.*cutoff / out = Gini;
run;

                /* Pour les valeurs inférieures au cutoff temporaire */
data inf;
set Gini;
if cutoff = « <« ;
run;

           /* Détermination de l’effectif */
proc sql noprint;
select sum(count) into: card_inf from inf;
quit;

                /* Calcul de l’indice de Gini pour les termes inférieurs */
data inf;
set inf;
count_sqr = (count/&card_inf.)**2;
run;

proc sql noprint;
select sum(count_sqr) into: somme_sqr from inf;
quit;

%let som1 = %sysevalf((1 – &somme_sqr.)*&card_inf./&n_encours.);

            /* Pour les valeurs supérieures au cutoff temporaire */
data sup;
set Gini;
if cutoff = « >= »;
run;

           /* Détermination de l’effectif */
proc sql noprint;
select sum(count) into: card_sup from sup;
quit;

   /* Calcul de l’indice de Gini pour les termes supérieurs */
data sup;
set sup;
count_sqr = (count/&card_sup.)**2;
run;

proc sql noprint;
select sum(count_sqr) into: somme_sqr from sup;
quit;

%let som2 = %sysevalf((1 – &somme_sqr.)*&card_sup./&n_encours.);

    /* Calcul de l’indice de pureté de Gini */
%let Gini = %sysevalf(&som1. + &som2.);

    /* Récupératin du cutoff */
data _null_;
set &DATA.;
if _n_ = &i.;
call symput(« Cutoff »,%scan(&list_var.,&p.));
run;

           /* Préparation des résultats d’intérêt à insérer */
data add;
Var = « %scan(&list_var.,&p.) »;
CutoffC = &Cutoff.;
Gini = &Gini.;
type = 1;
run;

 /* Sortie de la répartition par classe en fonction du cutoff temporaire */
proc freq data = encours noprint;
tables &Y. / out = Ventilation;
where cutoff = « >= »;
run;

proc transpose data = Ventilation out = Ventilation;
id &Y.;
var count;
run;

 /* Fusion des différentes résultats */
data add;
merge add ventilation;
run;

    /* Insertion des nouveaux résultats au tableau de résultats globaux */
data Resultats_encours;
set Resultats_encours add;
run;

%end;

%end;

        /* Si la variable est qualitative */
%if &Type. = 2 %then %do;

       /* Sortie de la liste des modalités pour la variable en cours */
proc freq data = &DATA. noprint;
tables %scan(&list_var.,&p.) / out = list_modas;
run;

proc sql noprint;
select count(*) into: nb_modas from list_modas;
select distinct(%scan(&list_var.,&p.)) into: list_modas separated by  »  » from list_modas;
quit;

    /* Pour chaque modalité */
%do m = 1 %to &nb_modas.;

    /* Regroupement modalité en cours vs le reste du monde */
data encours;
set &DATA.;
length cutoff $2.;
cutoff = « * »;
if %scan(&list_var.,&p.) = « %scan(&list_modas.,&m.) » then cutoff = « ** »;
run;

 /* Répartition de la classe de Y en fonction de la modalité considérée */
proc freq data = encours noprint;
tables &Y.*cutoff / out = Gini;
run;

data inf;
set Gini;
if cutoff = « * »;
run;

     /* Récupération des effectifs pour le reste du monde */
proc sql noprint;
select sum(count) into: card_inf from inf;
quit;

                /* Calcul de l’indice de Gini pour les termes considérés */
data inf;
set inf;
count_sqr = (count/&card_inf.)**2;
run;

proc sql noprint;
select sum(count_sqr) into: somme_sqr from inf;
quit;

%let som1 = %sysevalf((1 – &somme_sqr.)*&card_inf./&n_encours.);

 /* Récupération des effectifs pour la modalité considérée */
data sup;
set Gini;
if cutoff = « ** »;
run;

         /* Récupération des effectifs pour la modalité considérée */
proc sql noprint;
select sum(count) into: card_sup from sup;
quit;

       /* Calcul de l’indice de Gini pour les termes considérés */
data sup;
set sup;
count_sqr = (count/&card_sup.)**2;
run;

proc sql noprint;
select sum(count_sqr) into: somme_sqr from sup;
quit;

%let som2 = %sysevalf((1 – &somme_sqr.)*&card_sup./&n_encours.);

 /* Calcul de l’indice de pureté de Gini */
%let Gini = %sysevalf(&som1. + &som2.);

     /* Préparation des résultats d’intérêt à insérer */
data add;
Var = « %scan(&list_var.,&p.) »;
CutoffQ = « %scan(&list_modas.,&m.) »;
Gini = &Gini.;
type = 2;
run;

                /* Sortie de la répartition par classe en fonction du cutoff temporaire */
proc freq data = encours noprint;
tables &Y. / out = Ventilation;
where cutoff = « ** »;
run;

proc transpose data = Ventilation out = Ventilation;
id &Y.;
var count;
run;

         /* Fusion des différentes résultats */
data add;
merge add ventilation;
run;

 /* Insertion des nouveaux résultats au tableau de résultats globaux */
data Resultats_encours;
set Resultats_encours add;
run;

%end;

%end;

     /* Recherche de la valeur minimale de Gini pour déterminer le cutoff optimal chez la variable en cours */
data Resultats_encours;
set Resultats_encours;
if Gini ne .;
run;

proc sql noprint;
create table Resultats_encours2 as select min(Gini) as min_gini, Var, cutoffC, cutoffQ, Gini, &list_class., type from Resultats_encours;
quit;

data Resultats_encours;
set Resultats_encours2;
if Gini = min_gini;
run;

data Resultats_best;
set Resultats_best Resultats_encours;
run;

%end;

    /* Recherche de la valeur minimale de Gini pour déterminer le cutoff optimal */
data Resultats_best;
set Resultats_best;
n_tot = sum(&list_class.);
if n_tot >= &n_noeud.;
run;

proc sql noprint;
create table Resultats_best2 as select min(Gini) as min_gini, Var, cutoffC, cutoffQ, Gini, &list_class., n_tot, type from Resultats_best;
quit;

data Resultats_best;
set Resultats_best2;
if Gini = min_gini;
run;

    /* Nous retenons le premier de la liste */
data Resultats_best;
set Resultats_best;
if _n_ = 1;
run;

    /* Suppression des tables temporaires et inutiles */
proc datasets lib = work nolist;
delete add Y Ventilation Resultats_encours Resultats_encours2 encours gini Resultats_best2 biblio list_gini list_modas sup inf;
run;

    /* Reset des options d’affichage de la log */
options notes nospool;

%mend;

%macro DT(DATA=,Y=,n_noeud=);

 /* Le macro-programme suivant permet la construction de l’arbre de décision CART et se base sur la macro decision_noeud (application de l’indice de pureté de Gini).
       Les paramètres: – DATA, la base de données à traiter,
                       – Y, la variable réponse binaire ou multiclasse,
                       – n_noeud, le nombre d’observations à considérer pour pouvoir construire un noeud, une feuille.
       Sortie: la table parcours recensant les différentes étapes de construction des feuilles et la table DATA mise à jour avec les prédictions. */

    /* Pour l’itération d’origine nous lançons l’algorithme afin de déterminer la racine de l’arbre */
%decision_noeud(DATA=&DATA.,Y=&Y.,n_noeud=&n_noeud.);

    /* Options nécessaires pour éviter de saturer le log et mettre en évidence les erreurs */
options nonotes spool;

    /* Sortie des effectifs totaux */
data _null_;
set Resultats_best;
call symput(« n_tot »,n_tot);
run;

    /* Edition du cutoff optimal pour la table de synthèse des différents parcours menant aux feuilles terminales */
data parcours;
set Resultats_best;
if Type = 1 then do;
cond_G = catx(‘ ‘,Var, »>= »,CutoffC);
cond_D = catx(‘ ‘,Var, »<« ,CutoffC);
end;
if Type = 2 then do;
cond_G = cat(compress(Var), » = ‘ »,compress(CutoffQ), »‘ »);
cond_D = cat(compress(Var), » ne ‘ »,compress(CutoffQ), »‘ »);
end;
drop Gini min_gini;
run;

proc transpose data = parcours out = parcours;
var cond_G cond_D;
run;

data new_parcours;
set parcours;
rename COL1 = cond;
run;

    /* Si l’effectif total est supérieur à la condition d’arrêt n_noeud alors nous pouvons continuer l’algorithme */
%if &n_tot. >= &n_noeud. %then %do;

%do a = 1 %to 1;

data parcours;
set new_parcours;
if _name_ ne «  »;
run;

    /* Sortie du nombre de noeud à parcourir */
proc sql noprint;
select count(*) into: nb_noeud from parcours;
quit;

data new_parcours;
run;

         /* Pour chaque noeud */
%do n = 1 %to &nb_noeud.;

     /* Sortie du parcours en cours */
data _null_;
set parcours;
if _n_ = &n.;
call symput(« critere »,cond);
run;

     /* Réduction de la base via le parcours considéré */
data traitement;
set &DATA.;
if &critere.;
run;

    /* Sortie de l’effectif restant */
proc sql noprint;
select count(*) into: todo from traitement;
quit;

 /* Si la condition est respectée sur l’effectif n_noeud alors nous lançons la procédure pour créer une nouvelle feuille à la profondeur suivante */
%if &todo. >= %eval(2*&n_noeud.) %then %do;

    /* Détermination du cutoff optimal */
%decision_noeud(DATA=traitement,Y=&Y.,n_noeud=&n_noeud.);
options nonotes spool;

/* Pour eviter le bug lors de la manipulation des variables qualitatives */
%let controlQ = 0;

data _null_;
set Resultats_best;
if Type = 2 then do;
call symput(« controlQ »,n_tot);
end;
run;

         /* Edition du cutoff optimal pour insertion dans la table des parcours */
data add_parcours;
set Resultats_best;
if Type = 1 then do;
cond_G = catx(‘ ‘, »&critere. », »& »,Var, »>= »,CutoffC);
cond_D = catx(‘ ‘, »&critere. », »& »,Var, »<« ,CutoffC);
end;
%if &controlQ. ne &todo. %then %do;
if Type = 2 then do;
cond_G = cat(catt(« &critere. »), » & « ,compress(Var), » = ‘ »,compress(CutoffQ), »‘ »);
cond_D = cat(catt(« &critere. »), » & « ,compress(Var), » ne ‘ »,compress(CutoffQ), »‘ »);
end;
%end;
drop Gini min_gini;
run;

proc transpose data = add_parcours out = add_parcours;
var cond_G cond_D;
run;

data add_parcours;
set add_parcours;
rename COL1 = cond;
run;

       /* Si le cutoff est le même que celui d’origine, bug en cas de variable qualitative, alors on zap l’étape */
%if &controlQ. = &todo. %then %do;

data add_parcours;
cond = « &critere. »;
_name_ = « Stop!   « ;
run;

%end;

data new_parcours;
set new_parcours add_parcours;
run;

%end;

       /* Si le noeud en cours ne respecte pas le critère, alors nous le fermons */
%if &todo. < %eval(2*&n_noeud.)  %then %do;

data add_parcours;
cond = « &critere. »;
_name_ = « Stop!   « ;
run;

data new_parcours;
set new_parcours add_parcours;
run;

%end;

%end;

proc sql noprint;
select count(*) into: stop_or_not from new_parcours where _name_ = « Stop!   « ;
quit;

         /* Tant que tous les noeuds ne sont pas fermés, on relance l’itération pour continuer à creuser l’arbre */
%if &stop_or_not. < &nb_noeud. %then %do;
%let a = 0;
%end;

%end;

%end;

data parcours;
set new_parcours;
if cond ne «  »;
run;

    /* Nous sortons le nombre de feuilles/parcours de l’arbre terminé */
proc sql noprint;
select count(*) into: nb_feuille from parcours;
quit;

    /* Pour chaque parcours */
%do c = 1 %to &nb_feuille.;

data _null_;
set parcours;
if _n_ = &c.;
call symput(« critere »,cond);
run;

data pred;
set &DATA.;
if &critere.;
run;

        /* Sortie de la classe dominante pour le noeud considéré afin de déterminer la prédiction */
proc freq data = pred noprint;
tables &Y. / out = pred;
run;

proc sql noprint;
select max(count) into: class_pred from pred;
quit;

data pred;
set pred;
if count = &class_pred.;
run;

data pred;
set pred;
if _n_ = 1;
call symput(« class_pred »,&Y.);
run;

        /* Nous appliquons la prédiction déterminée à la table de données */
data &DATA.;
set &DATA.;
if &critere. then prediction = &class_pred.;
run;

 /* Nous renseignons dans la foulée le vote majoritaire du parcours considéré */
data parcours;
set parcours;
if _n_ = &c. then prediction = &class_pred.;
run;

%end;

    /* Sortie des prédictions pour les indicateurs de performance */
proc freq data = &DATA.;
tables &Y. * prediction;
run;

 /* Suppression des tables temporaires et inutiles */
proc datasets lib = work nolist;
delete Resultats_best new_parcours add_parcours traitement pred;
run;

   /* Reset des options d’affichage de la log */
options notes nospool;

%mend;

%macro RF(DATA = , Y = , n_noeud = , nv_select = , nb_arbre =);

   /* Macro-programme pour le lancement de la forêt aléatoire et faisant intervenir les macro-programmes decision_noeud et DT.
       Paramètres: – DATA, la base de données à considérer,
                   – Y, la variable réponse binaire ou polychotomique à préciser,
                   – n_noeud, le nombre d’observations minimum pour construire un noeud,
                   – nv_select, le nombre de variables à tirer au sort pour chaque arbres,
                   – nb_arbre, le nombre d’arbre à construire.
       Sortie: La table DATA mise à jour avec les prédictions de la forêt construite.
       NB: Certains arbres plantent mais du coup ne seront pas pris en compte dans les votes pour la prédiction finale. En attendant une nouvelle version ce bug persiste. */

    /* Options nécessaires pour éviter de saturer le log et mettre en évidence les erreurs */
options nonotes spool;

    /* Sortie de l’effectif total */
proc sql noprint;
select count(*) into: npop from &DATA.;
quit;

 /* Calcul de l’effectif out of bag pour les différents arbres à créer */
%let n_outofbag = %eval(2*&npop./3);

    /* Sortie de la liste des variables explicatives pour tirage au sort */
proc contents data = &DATA. (drop = &Y.) out = var_tirage noprint;
run;

    /* Nombre de variables à tirer au sort */
proc sql noprint;
select count(*) into: nb_vars from var_tirage;
quit;

    /* Table temporaire contenant les différentes prédictions des arbres à venir */
data pred_vote;
set &DATA.;
run;

    /* Pour nb_arbre */
%do na = 1 %to &nb_arbre.;

%put Arbre N° &na.;

data learn;
set &DATA.;
a = _n_;
run;

     /* Tirage de l’échantillon out of bag */
data learn (drop = i);
do i = 1 to &n_outofbag.;
select = ceil(ranuni(0)*n);
set learn point = select nobs = n;
position = select;
output;
end;
stop;
run;

        /* Selection des variables aléatoires pour le nombre d’arbres */
%let nb_vars_rest = &nb_vars.;

%do ta = 1 %to &nv_select.;

%if &ta. = 1 %then %do;
data var_tirage_rest;
set var_tirage;
run;
%end;

data _null_;
tirage = ceil(ranuni(0)*&nb_vars_rest.);
call symput(« tirage »,tirage);
run;

data _null_;
set var_tirage_rest;
if _n_ = &tirage.;
call symput(« select_var_add »,name);
run;

data var_tirage_rest;
set var_tirage_rest;
if name ne « &select_var_add. »;
run;

proc sql noprint;
select count(*) into: nb_vars_rest from var_tirage_rest;
quit;

%if &ta. = 1 %then %do;
%let select_var = &select_var_add.;
%end;

%if &ta. > 1 %then %do;
%let select_var = &select_var. &select_var_add.;
%end;

%end;

        /* Création de l’échantillon d’apprentissage */
data learn;
set learn;
keep &Y. &select_var.;
run;

     /* Création de l’arbre sur l’échantillon out of bag déterminé */
options notes nospool;
%DT(DATA = learn, Y = &Y., n_noeud = &n_noeud.);
options nonotes spool;

     /* Nous sortons le nombre de feuilles/parcours de l’arbre terminé */
proc sql noprint;
select count(*) into: nb_feuille from parcours;
quit;

data vote_add;
set &DATA.;
run;

        /* Pour chaque parcours */
%do c = 1 %to &nb_feuille.;

data _null_;
set parcours;
if _n_ = &c.;
call symput(« critere »,cond);
run;

data pred;
set learn;
if &critere.;
run;

            /* Sortie de la classe dominante pour le noeud considéré afin de déterminer la prédiction */
proc freq data = pred noprint;
tables &Y. / out = pred;
run;

proc sql noprint;
select max(count) into: class_pred from pred;
quit;

data pred;
set pred;
if count = &class_pred.;
run;

data pred;
set pred;
if _n_ = 1;
call symput(« class_pred »,&Y.);
run;

     /* Nous appliquons la prédiction déterminée à la table de données */
data vote_add;
set vote_add;
if &critere. then prediction = « &class_pred. »;
run;

%end;

/* Prédiction pour les observations sur chaque arbre */
data vote_add;
set vote_add;
prediction&na. = prediction;
keep prediction&na.;
run;

data pred_vote;
merge pred_vote vote_add;
run;

%end;

    /* Récupération de la liste des classes de la variable réponse Y */
proc freq data = &DATA. noprint;
table &Y. / out = list_class;
run;

proc sql noprint;
select count(*) into: nb_class from list_class;
select distinct(&Y.) into: list_class separated by  »  » from list_class;
quit;

    /* Choix du vote majoritaire */
%do i = 1 %to &npop.;

data vote_i;
set pred_vote;
if _n_ = &i.;
keep prediction1 — prediction&nb_arbre.;
run;

proc transpose data = vote_i out = vote_i;
var _all_;
run;

proc freq data = vote_i noprint;
tables COL1 / out =  vote_i;
run;

proc sql noprint;
select max(count) into: class_pred from vote_i;
quit;

data vote_i;
set vote_i;
if count = &class_pred.;
call symput(« class_pred »,COL1);
run;

data &DATA.;
set &DATA.;
if _n_ = &i. then Prediction = « &class_pred. »;
run;

%end;

/* Matrice de confusion */
proc freq data = &DATA.;
tables &Y. * Prediction;
run;

    /* Suppression des tables temporaires et inutiles */
proc datasets lib = work nolist;
delete var_tirage pred_vote var_tirage_rest parcours vote_add list_class foret vote_i learn;
run;

    /* Reset des options d’affichage de la log */
options notes nospool;

%mend;

Package et fonction R: http://www.inside-r.org/packages/cran/randomForest/docs/randomForest

\bullet Bibliographie:

– Random forests de Leo Breiman

– Arbres de classifications et Forêts aléatoires de S. Gadat

– Apprendre méthodologique pour l’intégration de données de neuro-imagerie et de génétique de C. Labrune

– Apprentissage statistique de A. Aussin

– Data mining et statistique décisionnelle. L’intelligence des données de Stéphane Tufféry