Freakonometrics

To content | To menu | To search

Tag - arbre

Entries feed - Comments feed

Tuesday, December 6 2011

ACT2040: de la robustesse des arbres

Pour répondre à une question rapide, la robustesse des arbres dépend de deux choses: du nombre d'observations (plus il y a d'observations, plus les nœuds seront stables), et de l'hétérogénéité entre les classes (plus les classes sont différentes, plus il sera facile de les distinguer). Pour faire simple, considérons un petit modèle binomial à trois classes (avec une unique variable explicative), par exemple si on simule 1000 valeurs,

m=c(.2,.5,.3)
moyenne=function(x){
return(m[1]*(x<3)+m[2]*((x>=3)&(x<=7))+m[3]*(x>7))
}
MY=Vectorize(moyenne)
set.seed(1)
n=1000
U=runif(n)*10
Y=rbinom(n,size=1,prob=MY(U))
plot(U,Y,ylim=c(-.1,1.1))
library(splines)
base=data.frame(Y,U)
reg=glm(Y~bs(U,10),data=base,family=binomial)
u=seq(0,10,by=.01)
y=predict(reg,newdata=data.frame(U=u),type="response")
lines(u,y,lwd=2,col="blue")
lines(u,MY(u),lwd=2,col="red",type="s")

Si on simule quelques milliers d'échantillons, on peut regarder la distribution de mes nœuds (si le découpage par arbre converge), en gardant - au mieux - que deux nœuds,

set.seed(1)
N=matrix(NA,10000,2)
n=1000
for(s in 1:1000){
U=runif(n)*10
Y=rbinom(n,size=1,prob=MY(U))
T=tree(Y~U)
noeud=T$frame$splits[,1]
noeud=noeud[noeud!=""]
NOEUD=as.numeric(substr(noeud,2,nchar(noeud)))
N[s,]=NOEUD[1:2]
}
vn=as.vector(N)
vn=vn[is.na(vn)==FALSE]
plot(density(vn,bw=.2),col="blue",lwd=2,xlim=c(0,10))
abline(v=c(3,7),col="red")

Rappelons que l'on a ici 1000 observations pour faire notre régression, avec trois classes, dont les valeurs moyennes par classe sont respectivement 20%, 50% et 30%. On note que l'on coupe bien entre les deux premières classes, un peu moins bien entre les deux dernières.

Si on suppose que les classes sont moins différentes, par exemple avec des valeurs moyennes à 30%, 50% et 40%, on obtient visuellement la distribution suivante pour les nœuds,

En l’occurrence, on a beaucoup de mal à distinguer entre les deux dernières classes.

Si on change la taille de notre échantillon, en passant de 1000 à 200, on s'aperçoit (en revenant aux valeurs précédentes des taux par classes) que l'on a beaucoup de mal à estimer correctement les nœuds,

De manière assez surprenante, il va prendre des nœuds proches des bords (proches de 0 et de 10).

Moralité, les classes obtenues par arbre ne sont robustes qu'à condition d'avoir suffisamment d'observations, et d'avoir des classes réellement différentes.

Monday, October 10 2011

ACT2040, régression binomiale et arbres

Mardi, avant de parler de la régression poissonienne, on va revenir un peu sur les modèles binomiaux, et présenter les arbres de régression. Pour la théorie, je peux renvoyer vers des notes de cours sur internet (ici, , ou encore) mais je présenterais les grandes idées au tableau. Pour le code, c'est assez facile
> sinistre=read.table("http://freakonometrics.free.fr/sinistreACT2040.txt",
+ header=TRUE,sep=";")
> sinistres=sinistre[sinistre$garantie=="1RC",]
> contrat=read.table("http://freakonometrics.free.fr/contractACT2040.txt",
+ header=TRUE,sep=";")
> T=table(sinistres$nocontrat)
> T1=as.numeric(names(T))
> T2=as.numeric(T)
> nombre1 = data.frame(nocontrat=T1,nbre=T2)
> I = contrat$nocontrat%in%T1
> T1= contrat$nocontrat[I==FALSE]
> nombre2 = data.frame(nocontrat=T1,nbre=0)
> nombre=rbind(nombre1,nombre2)
> base = merge(contrat,nombre)
> Y = base$nbre>0
> X1 = base$ageconducteur
> library(tree)
> arbre=tree(Y~X1,split="gini")
> plot(arbre)
> text(arbre,cex=.8)

Pour élaguer, il y a plusieurs méthodes, mais le plus simple est de demander de constituer des classes avec suffisamment de monde dedans,
> arbre=tree(Y~X1,split="gini", mincut = 5000)
> plot(arbre)
> text(arbre,cex=.8)

On peut bien sûr rajouter plusieurs variables (qualitatives comme quantitatives),
> age=base$ageconducteur
> car=base$agevehicule
> region=base$zone
> arbre=tree(Y~age+car+region,split="gini", mincut = 2600)
> plot(arbre)
> text(arbre,cex=.8)

Pour mieux comprendre la mise en œuvre pratique, et l'algorithme itératif qui se cache derrière, on peut utiliser le code suivant, calculant la distance du chi-deux, l'entropie, ou le critère de Gini
> u=sort(unique(base$ageconducteur))
> base$Y = base$nbre>0
> gini=entropie=chideux=rep(NA,length(u))
> for(i in 2:(length(u))){
+ I=base$ageconducteur<(u[i]-.5)
+ ft1=sum(base$Y[I==TRUE])/nrow(base)
+ ff1=sum(base$Y[I==FALSE])/nrow(base)
+ ft0=sum(1-base$Y[I==TRUE])/nrow(base)
+ ff0=sum(1-base$Y[I==FALSE])/nrow(base)
+ M=matrix(c(ft0,ff0,ft1,ff1),2,2)
+ ft=ft0+ft1
+ f0=ft0+ff0
+ Mind=matrix(c(ft*f0,f0*(1-ft),(1-f0)*ft,(1-f0)*(1-ft)),2,2)
+ Q=sum(nrow(B)*(M-Mind)^2/Mind)
+ entropie[i] = -((ft1+ft0)*(ft1/(ft1+ft0)*log(ft1/(ft1+ft0))+
+ ft0/(ft1+ft0)*log(ft0/(ft1+ft0))) +
+ (ff1+ff0)*(ff1/(ff1+ff0)*log(ff1/(ff1+ff0))+
+ ff0/(ff1+ff0)*log(ff0/(ff1+ff0)))
+ )
+
+ gini[i] = -((ft1+ft0)*(ft1/(ft1+ft0)*(1-ft1/(ft1+ft0))+
+ ft0/(ft1+ft0)*(1-ft0/(ft1+ft0))) +
+ (ff1+ff0)*(ff1/(ff1+ff0)*(1-ff1/(ff1+ff0))+
+ ff0/(ff1+ff0)*(1-ff0/(ff1+ff0))))
+
+ chideux[i] = Q
+ }
> plot(u-.5,entropie,type="b",pch=19,col="blue",
+ ylab="Entropie ou déviance",xlab="Age du conducteur")
> n=which.min(entropie)
> points(u[n]-.5,entropie[n],pch=19,col="red",cex=1.5)
Pour rappel, les trois critères sont données par les relations suivantes. Pour le critère du chi-deux,
http://perso.univ-rennes1.fr/arthur.charpentier/latex/arbre-comp-01.png
où, classiquement
http://perso.univ-rennes1.fr/arthur.charpentier/latex/arbre-comp-02.png
pour le découpage en deux classes, puis
http://perso.univ-rennes1.fr/arthur.charpentier/latex/arbre-comp-05.png
pour le découpage en trois classes, etc. Pour l'entropie,
http://perso.univ-rennes1.fr/arthur.charpentier/latex/arbre-comp-03.png
avec deux classes, ou, si on a trois classes,
http://perso.univ-rennes1.fr/arthur.charpentier/latex/arbre-comp-06.png
Enfin le critère de Gini,
L'image “http://perso.univ-rennes1.fr/arthur.charpentier/latex/arbre-comp-04.png” ne peut être affichée car elle contient des erreurs.
avec deux classes, ou avec trois,

http://perso.univ-rennes1.fr/arthur.charpentier/latex/arbre-comp-07.png

Sunday, September 19 2010

Statistique de l'assurance STT6705V, partie 3

Mercredi dernier, nous avons enfin pu faire cours en visio avec Rennes (ici et ). J'ai perdu le code que j'avais pu tapé en direct, mais majorité du code peut toutefois se trouver dans les transparents (toujours ici). La semaine prochaine, nous parlerons de surdispersion et de modèles plus poussés que le modèle de Poisson pour modéliser les nombres de sinistres.
Je vais juste mettre quelques compléments sur les arbres, car nous reparlerons de la modélisation des variables binomiales quand nous écrêterons les charges de sinistres. Pour quelques compléments théoriques, je peux renvoyer vers quelques vieux billets (ici par exemple, ou ) mais aussi vers des notes de cours (ici, , ou encore, mais la liste est loin d'être exhaustive). Côté mise en œuvre, ce que j'avais fait dans au tableau (à partir de la partie de code en ligne là) correspond à ce qui suit,
>  Y=base$nbre>0
>  X1=base$ageconducteur
> library(tree)
>  arbre=tree(Y~X1,split="gini")
>  plot(arbre)
>  text(arbre,cex=.8)

Comme on ne voit rien, il va falloir élaguer. Et pour couper les arbres, le plus simple est de demander de constituer des classes avec suffisamment de monde dedans,
le code est alors de la forme suivante,
>  arbre=tree(Y~X1,split="gini", mincut = 5000)
>  plot(arbre)
>  text(arbre,cex=.8)

On peut bien sûr rajouter plusieurs variables, qualitatives comme quantitatives,
>  age=base$ageconducteur
>  car=base$agevehicule
>  region=base$zone
>  arbre=tree(Y~age+car+region,split="gini", mincut = 2600)
>  plot(arbre)
>  text(arbre,cex=.8)

D'ailleurs, on notera qu'en rajoutant le bonus en début d'image, il est très significatif sur la probabilité d'avoir au moins un accident dans l'année, comme je l'avais dit oralement au tableau,
>  age=base$ageconducteur
>  car=base$agevehicule
>  region=base$zone
>  bonus=base$bonus
>  arbre=tree(Y~age+car+region+bonus,split="gini", mincut = 2600)
>  plot(arbre)
>  text(arbre,cex=.8)

(ah oui, le dessin est, comme toujours, de Manu Larcenet, maître de conférence à Vélizy, comme en atteste le document en ligne ici)

Tuesday, April 6 2010

Découper les arbres

Suite à une question sur l'utilisation des arbres en tarification automobile (ici), je vais revenir sur la construction des classes, de manière "plus pratique".

Commençons par l'utilisation du critère du chi-deux,
http://perso.univ-rennes1.fr/arthur.charpentier/latex/arbre-comp-01.png
où, classiquement
http://perso.univ-rennes1.fr/arthur.charpentier/latex/arbre-comp-02.png
On va donc couper la variable âge en deux classes, disons [18,a] (notée A) et [a+1,90] (notée B). On va alors attribuer à a toutes les valeurs possibles et imaginables, entre 18 et 90 (le minimum et le maximum des données observées). On obtient alors les valeurs suivantes

Autrement dit, le découpage qui permet d'avoir des classes les moins indépendantes possibles serait [18-27] et [28-90]. Une fois constituée cette séparation, on va continuer. On va alors chercher à couper un des deux groupes en deux. Autrement dit, on aura trois classes,
  • [18,b] (notée A), [b+1,27] (notée B) et [28,90] (notée C)
  • [18,27] (notée A), [28,b] (notée B) et [b+1,90] (notée C)
On calcule alors la distance du chi-deux entre les deux variables,
http://perso.univ-rennes1.fr/arthur.charpentier/latex/arbre-comp-05.png
et on a les valeurs suivantes,

Autrement dit, cette fois on nous recommande le découpage [18-27], [28-75] et [76-90]. Mais on peut essayer de changer de critère, par exemple on peut calculer l'entropie, avec pour commencer un découpage en deux classes,
http://perso.univ-rennes1.fr/arthur.charpentier/latex/arbre-comp-03.png
soit numériquement,

ou avec trois classes,
http://perso.univ-rennes1.fr/arthur.charpentier/latex/arbre-comp-06.png

Enfin, on peut aussi utiliser le critère de Gini, là aussi pour constituer deux classes,
L'image “http://perso.univ-rennes1.fr/arthur.charpentier/latex/arbre-comp-04.png” ne peut être affichée car elle contient des erreurs.

ou pour séparer en trois classes,

http://perso.univ-rennes1.fr/arthur.charpentier/latex/arbre-comp-07.png

soit, numériquement,

Bref, sur cet exemple, quelle que soit la méthode de découpage retenu, on arrive au même découpage. Ah oui, le code sous R est assez simple (mais loin d'être optimal),

> B=read.table("D:\\base-continuite-classe-logistique.txt")
> u=sort(unique(B$age))
> gini=entropie=chideux=rep(NA,length(u))
> for(i in 2:(length(u))){
+ I=B$age<(u[i]-.5)
+ ft1=sum(B$Y[I==TRUE])/nrow(B)
+ ff1=sum(B$Y[I==FALSE])/nrow(B)
+ ft0=sum(1-B$Y[I==TRUE])/nrow(B)
+ ff0=sum(1-B$Y[I==FALSE])/nrow(B)
+ M=matrix(c(ft0,ff0,ft1,ff1),2,2)
+ ft=ft0+ft1
+ f0=ft0+ff0
+ Mind=matrix(c(ft*f0,f0*(1-ft),(1-f0)*ft,(1-f0)*(1-ft)),2,2)
+ Q=sum(nrow(B)*(M-Mind)^2/Mind)
+  entropie[i] = -((ft1+ft0)*(ft1/(ft1+ft0)*log(ft1/(ft1+ft0))+
+           ft0/(ft1+ft0)*log(ft0/(ft1+ft0))) +
+                  (ff1+ff0)*(ff1/(ff1+ff0)*log(ff1/(ff1+ff0))+
+           ff0/(ff1+ff0)*log(ff0/(ff1+ff0)))
+ )
+
+  gini[i] = -((ft1+ft0)*(ft1/(ft1+ft0)*(1-ft1/(ft1+ft0))+
+           ft0/(ft1+ft0)*(1-ft0/(ft1+ft0))) +
+                  (ff1+ff0)*(ff1/(ff1+ff0)*(1-ff1/(ff1+ff0))+
+           ff0/(ff1+ff0)*(1-ff0/(ff1+ff0))))
+
+ chideux[i] = Q
+ }
> plot(u-.5,entropie,type="b",pch=19,col="blue",
+ ylab="Entropie ou déviance",xlab="Age du conducteur")
> n=which.min(entropie)
> points(u[n]-.5,entropie[n],pch=19,col="red",cex=1.5)