Importation et préparation des données¶

In [ ]:
#changer le dossier par défaut
import os
os.chdir("C:/Users/ricco/Desktop/demo")

#charger les données
import pandas
df = pandas.read_excel("wave5300.xlsx")
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 5300 entries, 0 to 5299
Data columns (total 23 columns):
 #   Column         Non-Null Count  Dtype  
---  ------         --------------  -----  
 0   CLASSE         5300 non-null   object 
 1   V1             5300 non-null   float64
 2   V2             5300 non-null   float64
 3   V3             5300 non-null   float64
 4   V4             5300 non-null   float64
 5   V5             5300 non-null   float64
 6   V6             5300 non-null   float64
 7   V7             5300 non-null   float64
 8   V8             5300 non-null   float64
 9   V9             5300 non-null   float64
 10  V10            5300 non-null   float64
 11  V11            5300 non-null   float64
 12  V12            5300 non-null   float64
 13  V13            5300 non-null   float64
 14  V14            5300 non-null   float64
 15  V15            5300 non-null   float64
 16  V16            5300 non-null   float64
 17  V17            5300 non-null   float64
 18  V18            5300 non-null   float64
 19  V19            5300 non-null   float64
 20  V20            5300 non-null   float64
 21  V21            5300 non-null   float64
 22  SAMPLE_STATUS  5300 non-null   object 
dtypes: float64(21), object(2)
memory usage: 952.5+ KB
In [ ]:
#valeurs de SAMPLE_STATUS
#protocole expérimental décrit dans le livre de Breiman et al., 1984
#pages 49 et 50
df.SAMPLE_STATUS.value_counts()
Out[ ]:
test     5000
train     300
Name: SAMPLE_STATUS, dtype: int64
In [ ]:
#partition learning-test basé sur la colonne SAMPLE_STATUS
dfTrain = df.loc[df.SAMPLE_STATUS == 'train'].drop(columns='SAMPLE_STATUS')
dfTrain.head()
Out[ ]:
CLASSE V1 V2 V3 V4 V5 V6 V7 V8 V9 ... V12 V13 V14 V15 V16 V17 V18 V19 V20 V21
0 A 0.75 0.05 2.60 2.28 2.07 3.42 3.66 2.65 2.30 ... 1.69 0.82 1.59 1.02 0.58 -0.37 2.76 0.92 0.89 -1.13
1 A 1.85 -0.66 0.70 3.69 1.50 2.42 2.57 4.62 2.07 ... 3.12 2.01 1.43 2.00 3.06 1.39 2.79 0.34 0.54 0.64
2 A -1.37 -0.45 -1.35 2.12 -0.15 1.36 0.27 -0.26 -1.19 ... 1.41 2.65 3.10 4.49 3.96 3.65 2.26 1.81 1.04 -0.41
3 A -0.29 -0.28 -1.24 1.65 0.94 1.75 4.58 0.29 3.33 ... 2.29 2.32 1.82 0.71 2.71 2.61 2.43 0.58 1.66 -0.28
4 A -1.16 1.11 1.39 3.09 1.83 4.76 3.01 3.93 2.57 ... 1.59 1.21 3.39 1.64 2.84 -0.03 2.11 2.11 0.37 1.02

5 rows × 22 columns

In [ ]:
#de même pour 'test'
dfTest = df.loc[df.SAMPLE_STATUS == 'test'].drop(columns='SAMPLE_STATUS')
dfTest.shape
Out[ ]:
(5000, 22)

Construction de l'arbre de décision¶

Paramètres standards - Sans post-élagage¶

In [ ]:
#préparation des données (bis)
#partition de train en growing et pruning sets
#'pruning set' = on parle aussi de 'validation set' ou 'tunig set' dans certains logiciels
from sklearn.model_selection import train_test_split
dfGrow, dfPrune = train_test_split(dfTrain,test_size=100,random_state=0,stratify=dfTrain.CLASSE)

#distribution dans growing set
dfGrow.CLASSE.value_counts(normalize=True)
Out[ ]:
A    0.365
B    0.340
C    0.295
Name: CLASSE, dtype: float64
In [ ]:
#et pour le prune
dfPrune.CLASSE.value_counts(normalize=True)
Out[ ]:
A    0.37
B    0.34
C    0.29
Name: CLASSE, dtype: float64
In [ ]:
#import de la classe arbre de décision
from sklearn.tree import DecisionTreeClassifier

#instanciation avec les paramètres par défaut
#ccp_alpha = 0 notamment (pas de post-élagage)
arbre = DecisionTreeClassifier(random_state=0)

#apprentissage
arbre.fit(dfGrow[dfGrow.columns[1:]],dfGrow.CLASSE)

#complexité - nombre de feuilles
arbre.get_n_leaves()
Out[ ]:
30
In [ ]:
#affichage
import matplotlib.pyplot as plt
from sklearn.tree import plot_tree
plt.figure(figsize=(5,5))
plot_tree(arbre,feature_names=dfGrow.columns[1:],filled=True)
plt.show()
In [ ]:
#taux d'erreur en resubstitution (sur les 200 growing set)
#on a un arbre "pur"
#dans le sens où toutes les règles sont sans contre-exemples
1-arbre.score(dfGrow[dfGrow.columns[1:]],dfGrow.CLASSE)
Out[ ]:
0.0
In [ ]:
#performances en test - taux d'erreur
#vrai reflet des performances en généralisation
err_default = 1-arbre.score(dfTest[dfTest.columns[1:]],dfTest.CLASSE)
print(err_default)
0.3096
In [ ]:
#intervalle de confiance à 95%
from statsmodels.stats.proportion import proportion_confint
proportion_confint(count=err_default*dfTest.shape[0],nobs=dfTest.shape[0],alpha=0.05)
Out[ ]:
(0.2967851373368864, 0.3224148626631136)

Mécanisme de post-élagage¶

In [ ]:
#chemin de post-élagage en fonction de ccp_alpha
path =arbre.cost_complexity_pruning_path(dfGrow[dfGrow.columns[1:]],dfGrow.CLASSE)
print(path)
{'ccp_alphas': array([0.        , 0.00484848, 0.00666667, 0.0075    , 0.008     ,
       0.00857143, 0.00875   , 0.00875   , 0.00942068, 0.00944444,
       0.00944444, 0.00981481, 0.0125    , 0.01375926, 0.01520531,
       0.016     , 0.01727124, 0.01795322, 0.0200119 , 0.02482051,
       0.02851282, 0.02942205, 0.03877273, 0.06455013, 0.09808853,
       0.13281383]), 'impurities': array([0.        , 0.00969697, 0.01636364, 0.02386364, 0.03186364,
       0.04043506, 0.04918506, 0.05793506, 0.06735574, 0.07680019,
       0.09568908, 0.10550389, 0.11800389, 0.14552241, 0.17593304,
       0.19193304, 0.20920428, 0.2271575 , 0.2471694 , 0.27198991,
       0.30050273, 0.32992478, 0.36869751, 0.43324763, 0.53133617,
       0.66415   ])}
In [ ]:
#construire arbre pour chaque valeur de ccp_alphas
#receuillir le nombre de feuilles, taux d'erreur en growing, en pruning
Hazos = {'modele':[],'nb_feuilles':[],'err_grow':[],'err_prune':[]}

#go...
for ccp in path['ccp_alphas']:
    #instanciation et entraînement
    cedre = DecisionTreeClassifier(ccp_alpha=ccp,random_state=0)
    cedre.fit(dfGrow[dfGrow.columns[1:]],dfGrow.CLASSE)
    #récupération des infos
    Hazos['modele'].append(cedre)
    Hazos['nb_feuilles'].append(cedre.get_n_leaves())
    Hazos['err_grow'].append(1-cedre.score(dfGrow[dfGrow.columns[1:]],dfGrow.CLASSE))
    #utilisation du 'pruning set' pour la première fois
    Hazos['err_prune'].append(1-cedre.score(dfPrune[dfPrune.columns[1:]],dfPrune.CLASSE))

#nombre d'arbres qui ont été construits
print(len(Hazos['modele']))
26
In [ ]:
#graphique à la Tanagra
#mettant en relation le nombre de feuilles
#avec les taux d'erreur en Growing et Pruning
plt.plot(Hazos['nb_feuilles'],Hazos['err_grow'],c='red',marker='.',label='Growing Set')
plt.plot(Hazos['nb_feuilles'],Hazos['err_prune'],c='green',marker='.',label='Pruning Set')
plt.title("Taux d'erreur en fonction du nombre de feuilles")
plt.xlabel('Nombre de feuilles')
plt.ylabel("Taux d'erreur")
plt.legend()
plt.show()
In [ ]:
#indice de l'arbre le plus performant en pruning
import numpy
idMin = numpy.argmin(Hazos['err_prune'])
print(idMin)
13
In [ ]:
#valeur du ccp_alpha corresp.
path['ccp_alphas'][idMin]
Out[ ]:
0.01375925925925927
In [ ]:
#son nombre de feuilles
Hazos['nb_feuilles'][idMin]
Out[ ]:
14
In [ ]:
#vérifions même info à partir de l'arbre "optimal"
arbre_optim = Hazos['modele'][idMin]
arbre_optim.get_n_leaves()
Out[ ]:
14
In [ ]:
#afficher l'arbre
plt.figure(figsize=(5,5))
plot_tree(arbre_optim,feature_names=dfGrow.columns[1:],filled=True)
plt.show()
In [ ]:
#performance en test
1-arbre_optim.score(dfTest[dfTest.columns[1:]],dfTest.CLASSE)
Out[ ]:
0.2942