Freakonometrics

To content | To menu | To search

Wednesday, December 7 2011

ACT2040: incréments négatifs dans les triangles

Considérons le triangle d'incréments de payements suivants,

> PAID
     [,1] [,2] [,3] [,4] [,5] [,6]
[1,] 3209 4372 4411 4428 4435 4456
[2,] 3367 4659 4696 4720 4730   NA
[3,] 3871 5345 5338 5420   NA   NA
[4,] 4239 5917 6020   NA   NA   NA
[5,] 4929 6794   NA   NA   NA   NA
[6,] 5217   NA   NA   NA   NA   NA
> INC=PAID
> INC[,2:6]=PAID[,2:6]-PAID[,1:5]
> INC
     [,1] [,2] [,3] [,4] [,5] [,6]
[1,] 3209 1163   39   17    7   21
[2,] 3367 1292   37   24   10   NA
[3,] 3871 1474   -7   82   NA   NA
[4,] 4239 1678  103   NA   NA   NA
[5,] 4929 1865   NA   NA   NA   NA
[6,] 5217   NA   NA   NA   NA   NA
Comme on peut le voir, sur la troisième ligne, on a un incrément de paiement négatif. A priori, ce n'est pas forcément gênant. En particulier, on peut faire tourner la méthode Chain Ladder,
> lambda=rep(NA,5)
> for(k in 1:5){
+ lambda[k]=(sum(PAID[1:(6-k),k+1])/
+             sum(PAID[1:(6-k),k]))}
> lambda
[1] 1.380933 1.008476 1.008515 1.001858 1.004735
> PROJECTION=PAID
> for(k in 1:5){
+ PROJECTION[((7-k):6),k+1]=
+ PROJECTION[((7-k):6),k]*lambda[k]}
> sum(PROJECTION[,6]-
+ diag(PROJECTION[,6:1]))
[1] 2469.703
et la sortie coïncide avec la fonction R,
> MackChainLadder(PAID)
MackChainLadder(Triangle = PAID)
 
Latest Dev.To.Date Ultimate    IBNR Mack.S.E CV(IBNR)
1  4,456       1.000    4,456     0.0    0.000      NaN
2  4,730       0.995    4,752    22.4    0.146  0.00652
3  5,420       0.993    5,456    35.8    2.405  0.06721
4  6,020       0.985    6,111    91.3   41.679  0.45629
5  6,794       0.977    6,956   161.5   71.620  0.44334
6  5,217       0.707    7,376 2,158.6   95.750  0.04436
 
Totals
Latest:            32,637.00
Dev:                    0.93
Ultimate:          35,106.70
IBNR:               2,469.70
Mack S.E.:            146.62
CV(IBNR):  0.059366227164502
Message d'avis :
In Mack.S.E(CL[["Models"]], FullTriangle, est.sigma = est.sigma,
'loglinear' model to estimate sigma_n doesn't appear appropriate
p-value > 5.
est.sigma will be overwritten to 'Mack'.
Mack's estimation method will be used instead.
On notera le message d'avis, laissant entendre qu'il peut y avoir un petit soucis. En fait, le soucis apparaît clairement si on souhaite faire une régression de Poisson. Car autant les valeurs non-entières ne sont pas trop gênantes, autant les valeurs négatives le sont !
> Y=as.vector(INC)
> D=rep(1:6,each=6)
> A=rep(2001:2006,6)
> base=data.frame(Y,D,A)
> reg=glm(Y~as.factor(D)+as.factor(A),
+     data=base,family=poisson(link="log"))
Erreur dans eval(expr, envir, enclos) :
les valeurs négatives sont interdites pour la famille poisson
Il faut alors trouver une solution si on a des incréments négatifs. Et aucune solution ne sera intellectuellement satisfaisante, car avec une valeur négative, il n'y a aucune légitimité pour continuer à utiliser un modèle Poissonnien. Donc on va bricoler...
  • Prendre de l'argent à droite et à gauche
La première solution consiste à dire que cet incrément n'a pas de raison d'être. On va donc aller chercher de l'argent dans la colonne avant (sur la même ligne, on va supposer qu'il s'agit d'un problème sur les cadences de paiements) ou sur la colonne après. Au lieu d'avoir
[3,] 3871 1474   -7   82   NA   NA
On peut renflouer en prenant à gauche,
[3,] 3871 1467   0    82   NA   NA
ou à droite
[3,] 3871 1474   0    75   NA   NA
En faisant ces opérations, on change seulement localement la courbe de paiements pour la troisième année de survenance. On peut se demander si le fait de prendre à gauche a un impact sur l'estimation du montant de provisions, ou sur l'incertitude associée. Pour ça, on peut utiliser les fonctions suivantes,
library(ChainLadder)
CL=function(T){
sum(
MackChainLadder(T)$FullTriangle[,ncol(T)]
-diag(T[,ncol(T):1]))}
CL.SE=function(T){
MackChainLadder(T)$Total.Mack.S.E}
CumInc=function(T){
m=T
for(i in 2:nrow(T)){m[,i]=apply(T[,1:i],1,sum)}
return(m)}
On peut alors regarder, si on prend à droite ou à gauche ce qui se passe, ou ce qui se passe si on renfloue non pas à 0 mais à une valeur strictement positive (e.g. 1),
VCL=rep(NA,8)
for(j in 1:8){
INCd=INC
INCd[3,2:4]=INC[3,2:4]+c(1-j,+7,j-1-7)
VCL[j]=CL(CumInc(INCd))}
plot(1:8,VCL,type="b",col="blue",xlim=c(0,9),ylim=c(2462,2474))
VCL=rep(NA,10)
for(j in 1:10){
INCd=INC
INCd[3,2:4]=INC[3,2:4]+c(1-j,+9,j-1-9)
VCL[j]=CL(CumInc(INCd))}
lines(0:9,VCL,type="b",pch=0,col="red")
avec la courbe bleu si on renfloue à 0, et rouge si on renfloue à 1, pour l'estimation du montant total de provisions,

VCL=rep(NA,8)
for(j in 1:8){
INCd=INC
INCd[3,2:4]=INC[3,2:4]+c(1-j,+7,j-1-7)
VCL[j]=CL.SE(CumInc(INCd))}
plot(1:8,VCL,type="b",col="blue",xlim=c(0,9),ylim=c(130,145))
VCL=rep(NA,10)
for(j in 1:10){
INCd=INC
INCd[3,2:4]=INC[3,2:4]+c(1-j,+9,j-1-9)
VCL[j]=CL.SE(CumInc(INCd))}
lines(0:9,VCL,type="b",pch=0,col="red")

avec cette fois l'impact des transferts sur l'écart-type. En abscisse, on a (à peu de choses près) le montant enlevé sur la colonne de droite. Bref, les transferts venant de la gauche ou de la droite sont une solution, mais le choix d’où vient l'argent ne sera pas neutre, ni sur l'estimation, ni sur la variance de l'estimation (et l'intervalle de confiance).
  • Jouer à faire des translations...
Une autre piste pourrait être de noter que, dans le modèle linéaire, si on translate nos données (vers le haut), on ne change pas la pente, et la constante est juste augmenté (de la taille de la translation).
> lm(dist~speed,data=cars)
 
Call:
lm(formula = dist ~ speed, data = cars)
 
Coefficients:
(Intercept)        speed
-17.579        3.932
 
> lm((dist+10)~speed,data=cars)
 
Call:
lm(formula = (dist + 10) ~ speed, data = cars)
 
Coefficients:
(Intercept)        speed
-7.579        3.932 
Autrement dit, la prédiction faite par notre modèle n'est pas modifiée par la translation, à condition d'opérer la translation ensuite sur la prédiction. Sur le dessin ci-dessous, on fait pareil, mais avec une régression de Poisson: la courbe noire est sur les données brutes, la courbe bleu est obtenue sur les points translatés vers le haut, et la rouge si on translate la prédiction sur les points translatés,
http://freakonometrics.blog.free.fr/public/perso4/glm-translation.gif
Si la translation est trop importante, la prédiction finale (en rouge) se retrouve davantage éloignée de la vraie valeur (en noir).
Pareillement, on peut translater nos paiements vers le haut dans les triangles, de manière à avoir des paiements positifs. On peut commencer par translater tout le triangle, par exemple en translatant juste assez pour que tous les incréments soient positifs ou nuls,
> k=7
> Y=as.vector(INC)+k
> D=rep(1:6,each=6)
> A=rep(2001:2006,6)
> base=data.frame(Y,D,A)
> reg=glm(Y~as.factor(D)+as.factor(A),
+ data=base,family=poisson(link="log"))
> Yp=predict(reg,type="response",
+ newdata=base)-k
> sum(Yp[is.na(Y)==TRUE])
[1] 2508.620
On peut aussi se demander ce qui se passerait si on décalait davantage vers le haut. On l'a vu sur l'animation, plus la translation est importante, plus la prédiction se décale. Un stratégie pourrait être de faire une estimation pour plusieurs valeurs de translations - de telle sorte que tous les incréments soient positifs ou nuls - et ensuite d'extrapoler pour savoir ce qui se passerait si on ne translatait pas,
> K=7:20
> R=rep(NA,length(K))
> for(i in 1:length(K)){
+ k=K[i]
+ Y=as.vector(INC)+k
+ D=rep(1:6,each=6)
+ A=rep(2001:2006,6)
+ base=data.frame(Y,D,A)
+ reg=glm(Y~as.factor(D)+as.factor(A),
+ data=base,family=poisson(link="log"))
+ Yp=predict(reg,type="response",
+ newdata=base)-k
+ R[i]=sum(Yp[is.na(Y)==TRUE])}
> plot(K,R,xlim=c(0,20),ylim=c(2465,max(R)))
> abline(lm(R~K),col="blue")
> (yp=predict(lm(R~K),newdata=(K=0)))
1
2470.199
> points(0,yp,col="red",pch=19)

On a cette fois une estimation plus proche de celle que nous avions en utilisant directement, sur le triangle brut, la méthode chain ladder. Mais on peut aller un peu plus loin. Car dans le triangle, on a translaté toutes les valeurs, alors que seule une était négative. On pourrait par exemple translater seulement les valeurs de la troisième colonne,
> K=7:20
> R=rep(NA,length(K))
> for(i in 1:length(K)){
+ k=K[i]
+ Y=as.vector(INC)
+ D=rep(1:6,each=6)
+ A=rep(2001:2006,6)
+ Y[D==3]=Y[D==3]+k
+ base=data.frame(Y,D,A)
+ reg=glm(Y~as.factor(D)+as.factor(A),
+ data=base,family=poisson(link="log"))
+ Yp=predict(reg,type="response",
+ newdata=base)
+ Yp[D==3]=Yp[D==3]-k
+ R[i]=sum(Yp[is.na(Y)==TRUE])}
> predict(lm(R~K),newdata=(K=0))
1
2469.703 
Tiens, on retombe exactement sur l'estimateur de la méthode chain ladder... Étonnant, non ?

Une petite précision. Dans la vraie vie, on ne devrait pas avoir d'incréments négatifs dans les triangles de paiements. Maintenant, il faut reconnaître qu'on en voit apparaître lorsque l'on bootstrappe les résidus, et que l'on génère de pseudo-triangles...
Si des praticiens ont des commentaires sur les incréments négatifs, les commentaires sont ouverts (et peu modérés), donc racontez nous comment vous faites, je suis preneur...

Friday, December 2 2011

ACT2040: lire une sortie de régression

Histoire que les choses soient bien claires, prenons 2 minutes pour revenir sur la lecture d'une sortie de régression,

>  reg=glm(nbre~carburant+zone+ageconducteur+
+
offset(log(exposition)), + data=baseFREQ,family=poisson(link="log")) > summary(reg)   Call: glm(formula = nbre ~ carburant + zone + ageconducteur + offset(log(exposition)), family = poisson(link = "log"), data = baseFREQ)   Deviance Residuals: Min 1Q Median 3Q Max -0.5372 -0.3643 -0.2731 -0.1503 4.5994   Coefficients: Estimate Std. Error z value Pr(>|z|) (Intercept) -2.360655 0.105711 -22.331 < 2e-16 *** carburantE -0.283956 0.043117 -6.586 4.53e-11 *** zoneB 0.122891 0.108713 1.130 0.258 zoneC 0.224469 0.088310 2.542 0.011 * zoneD 0.411243 0.086804 4.738 2.16e-06 *** zoneE 0.532070 0.088226 6.031 1.63e-09 *** ageconducteur -0.007031 0.001550 -4.537 5.70e-06 *** --- Signif. codes: 0***0.001**0.01*0.05 ‘.’ 0.1 ‘ ’ 1   (Dispersion parameter for poisson family taken to be 1)   Null deviance: 13659 on 49999 degrees of freedom Residual deviance: 13538 on 49993 degrees of freedom AIC: 17788   Number of Fisher Scoring iterations: 6

L'écriture formelle de ce modèle serait quelque chose de la forme

http://freakonometrics.blog.free.fr/public/perso4/sortie11.gif

http://freakonometrics.blog.free.fr/public/perso4/sortie02.gif correspond au nombre de sinistres observés pour l'assuré , http://freakonometrics.blog.free.fr/public/perso4/sortie03.gif correspond à l'exposition (i.e. au temps pendant lequel l'assuré a été observé dans la base), http://freakonometrics.blog.free.fr/public/perso4/sortie04.gif est la première variable, ici le carburant (qui est une variable qualitative prenant 2 modalités), http://freakonometrics.blog.free.fr/public/perso4/sortie05.gif est la seconde variable, ici la zone géographique (qui est encore qualitative, avec 5 zones), et enfin http://freakonometrics.blog.free.fr/public/perso4/sortie07.gif est la troisième et dernière variable explicative, ici l'âge du conducteur principal (qui est pris ici comme une variable continue). Le modèle s'écrit, si l'on disjoncte les variables qualitatives http://freakonometrics.blog.free.fr/public/perso4/sortie08.gifhttp://freakonometrics.blog.free.fr/public/perso4/sortie12.gif s'écrit

http://freakonometrics.blog.free.fr/public/perso4/sortie10.gif

Il y alors 6 paramètres à estimer. C'est ce qui est renvoyé dans la sortie de la régression,

               Estimate Std. Error z value Pr(>|z|)
(Intercept)   -2.360655   0.105711 -22.331  < 2e-16 ***
carburantE    -0.283956   0.043117  -6.586 4.53e-11 ***
zoneB          0.122891   0.108713   1.130    0.258
zoneC          0.224469   0.088310   2.542    0.011 *
zoneD          0.411243   0.086804   4.738 2.16e-06 ***
zoneE          0.532070   0.088226   6.031 1.63e-09 ***
ageconducteur -0.007031   0.001550  -4.537 5.70e-06 ***
Dans ce tableau, on a pour chaque paramètre, une estimation, par exemple http://freakonometrics.blog.free.fr/public/perso4/sortie20.gif vaut -2.36, alors que http://freakonometrics.blog.free.fr/public/perso4/sortie21.gif vaut 0.2244. On a ensuite une estimation de l'écart-type de ces estimateurs, par exemple http://freakonometrics.blog.free.fr/public/perso4/sortie23.gif vaut 0.086.  On peut alors utiliser un test de significative basé sur une hypothèse de normalité de ces estimateurs. On note que l'âge du conducteur est significatif dans ce modèle. Pour les facteurs, la significativité est jugée par rapport à la modalité de référence, qui est celle qui n'apparaît pas dans la sortie de la régression. Aussi, le carburant "Essence" est significatif par rapport à la modalité de référence qui est le Diesel. En revanche, la zone "B" n'est pas significative par rapport à la zone de référence qui est la zone "A", ce qui suggèrerait de regrouper les zone "A" et "B" (comme discuté dans un ancien billet). Notons que l'on peut d'ailleurs aller un peu plus loin, en utilisant des estimateurs plus robustes de l'écart-type de ces estimateurs (proposés par Cameron & Trivedi (1998)),
> library(sandwich)
> cov.reg<-vcovHC (reg, type="HC0")
> std.err<-sqrt(diag(cov.reg))
> estimation<-cbind(estimate=reg$coefficients, std.err,
+        pvalue=round(2*(1-pnorm(abs(reg$coefficients/std.err))),5),
+        lower=reg$coefficients-1.96*std.err,
+        upper=reg$coefficients+1.96*std.err)
>
> estimation
estimate     std.err  pvalue       lower        upper
(Intercept)   -2.360654805 0.110541249 0.00000 -2.57731565 -2.143993957
carburantE    -0.283955585 0.044492586 0.00000 -0.37116105 -0.196750117
zoneB          0.122890716 0.111122177 0.26877 -0.09490875  0.340690183
zoneC          0.224468748 0.091032422 0.01367  0.04604520  0.402892295
zoneD          0.411242820 0.089449063 0.00000  0.23592266  0.586562984
zoneE          0.532070373 0.091675282 0.00000  0.35238682  0.711753926
ageconducteur -0.007030691 0.001664597 0.00002 -0.01029330 -0.003768081
On retrouve ici des grandeurs du même ordre que tout à l'heure, avec un intervalle de confiance à 95% pour les estimateurs. Si 0 appartient à l'intervalle de confiance, on dira que le paramètre n'est pas significatif. A partir de ces estimations, il est facile de faire une prédiction, par exemple pour un assuré de 40 ans, résidant dans la zone D et conduisant un véhicule diesel, sa espérance annuelle d'accident est de 10.7%
>  predict(reg,newdata=data.frame(carburant="D",
+  zone="D",ageconducteur=40,exposition=1),type="response")
1
0.1074597
>  cbind(c(1,0,0,0,1,0,40),reg$coefficients)
[,1]         [,2]
(Intercept)      1 -2.360654805
carburantE       0 -0.283955585
zoneB            0  0.122890716
zoneC            0  0.224468748
zoneD            1  0.411242820
zoneE            0  0.532070373
ageconducteur   40 -0.007030691
>  exp(t(c(1,0,0,0,1,0,40))%*%reg$coefficients)
[,1]
[1,] 0.1074597
Notons que dans ce cas, la probabilité de ne pas avoir d'accident est
>  lambda=exp(t(c(1,0,0,0,1,0,40))%*%reg$coefficients)
>  dpois(0,lambda)
[1] 0.8981127
>  exp(-lambda)
[,1]
[1,] 0.8981127
Parmi les autres interprétations possibles de ces valeurs, notons qu'à zone et à âge identique, un assuré conduisant un véhicule essence à une fréquence de sinistres 25% plus faible qu'un assuré conduisant un véhicule diesel,
>  exp(reg$coefficients[2])
carburantE
0.7528001
En bas, on a la déviance du modèle. Dans le cas d'une régression de Poisson, on sait que
http://freakonometrics.blog.free.fr/public/perso4/sortie25.gif
soit ici
>  baseFREQ$pred=predict(reg,type="response")
>  Z=log(baseFREQ$pred^baseFREQ$nbre)
>  2*(sum(log(baseFREQ$nbre^baseFREQ$nbre)-baseFREQ$nbre)-
+  sum(Z- baseFREQ$pred))
[1] 13538.09
> deviance(reg)
[1] 13538.09
Mais la déviance n'est pas forcément pertinente pour comparer des modèles ayant des nombres différentes de paramètres, et donc la sortie inclue le critère d'Akaike.

Thursday, December 1 2011

ACT2040: poids, variable offset et régression binomiale négative

  • Variable offset et processus de Poisson
Comme on l'a vu en cours, le modèle fondamental pour modéliser l'arrivée des sinistres est le processus de Poisson (homogène). Rappelons que si http://freakonometrics.blog.free.fr/public/perso4/ooofset-01.gif est un processus de Poisson (désignant le nombre de sinistres survenant entre http://freakonometrics.blog.free.fr/public/perso4/ooofset-03.gif et http://freakonometrics.blog.free.fr/public/perso4/ooofset-02.gif), alors
http://freakonometrics.blog.free.fr/public/perso4/ooofset-04.gif
i.e. http://freakonometrics.blog.free.fr/public/perso4/ooofset-05.gif suit une loi de Poisson de paramètre http://freakonometrics.blog.free.fr/public/perso4/ooofset-06.gif.
Mais en plus les incréments sont indépendants, au sens où http://freakonometrics.blog.free.fr/public/perso4/ooofset-05.gif et http://freakonometrics.blog.free.fr/public/perso4/ooofset-07.gif sont deux variables de Poisson indépendantes.
Une conséquence est que si le nombre de sinistres pour un assuré présent une année suit une loi de Poisson http://freakonometrics.blog.free.fr/public/perso4/ooofset-08.gif, pour un assuré présent pendant une durée http://freakonometrics.blog.free.fr/public/perso4/ooofset-10.gif, le nombre de sinistre suit une loi de Poisson http://freakonometrics.blog.free.fr/public/perso4/ooofset-09.gif.
Si on revient à la régression de Poisson, on suppose que
http://freakonometrics.blog.free.fr/public/perso4/ooofset-11.gif
mais si on rajoute l'exposition http://freakonometrics.blog.free.fr/public/perso4/ooofset-10.gif, alors
http://freakonometrics.blog.free.fr/public/perso4/ooofset-12.gif
Autrement dit la variable http://freakonometrics.blog.free.fr/public/perso4/ooofset-10.gif intervient dans la régression (ou plutôt son logarithme), mais c'est une variable particulière car le coefficient est ici 1 (et n'est pas à estimer). L'exposition est alors une variable en dehors de l'ensemble des autres variables (offset). Pour intégrer une variable offset, le code est tout simplement
> reg1=glm(nombre~ageconducteur+offset(log(exposition)),
+ data=baseFREQ,family=poisson(link="log"))
  • Variable offset et régression binomiale négative
Dans le cas de la régression binomiale négative, de loi
http://freakonometrics.blog.free.fr/public/perso4/ooofset-13.gif
avec http://freakonometrics.blog.free.fr/public/perso4/ooofset-14.gif et http://freakonometrics.blog.free.fr/public/perso4/ooofset-15.gif.
Le modèle de régression construit à partir de cette loi suppose
http://freakonometrics.blog.free.fr/public/perso4/ooofset-16.gif
Compte tenu du parallèle qui existe entre la loi de Poisson et la loi binomiale négative (on rajoute une variable non-observable suivant une loi Gamma, cf. exercice 3 du partiel), on peut rajouter une variable offset en écrivant
http://freakonometrics.blog.free.fr/public/perso4/ooofset-17.gif
Le code R pour faire une régression binomiale négative est tout simplement
> library(MASS)
> reg2=glm.nb(nombre~ageconducteur+offset(log(exposition)),
+ data=baseFREQ)
On peut d'ailleurs comparer les deux prédictions, sur le portefeuille
>  age=seq(18,85)
>  nb1=predict(reg1,newdata=data.frame(ageconducteur=
+  age,exposition=1),type="response")
>  nb2=predict(reg2,newdata=data.frame(ageconducteur=
+  age,exposition=1), type="response")
> X=seq(10,110,by=5)
> V=tapply(baseFREQ$exposition,cut(baseFREQ$ageconducteur,X),sum)
> plot((X[2:length(X)]+X[1:(length(X)-1)])/2,V,axes=
+ FALSE,col="white",xlab="",ylab="")
> axis(1)
> axis(2)
> for(i in 1:(length(X)-1)){
+ polygon(c(X[i],X[i],X[i+1],X[i+1]),
+         c(0,V[i],V[i],0),col="light green",border=NA)
+ }
> par(new=TRUE)
>  plot(age,nb1,type="l",col="red",axes=FALSE,ylab="",xlab="")
> lines(age,nb2,col="blue")
>  reg3=glm(nombre~as.factor(ageconducteur)+
+ offset(log(exposition)), data=baseFREQ,family=
+ poisson(link="log"))
>  nb3=predict(reg3,newdata=data.frame(ageconducteur=
+ age,exposition=1), type="response")
>  points(age,nb3,pch=3)
> axis(4)
avec en vert l'exposition en fonction de l'âge, en rouge l'espérance du nombre de sinistres par an avec un modèle de Poisson (l'échelle est à droite sur le graphique), en bleu le modèle binomial négatif, et les + correspondent aux estimateurs non-paramétriques de la moyenne du nombre annuel d'accidents par âge. On notera que les sorties de régression sont très proches (les deux modèles étant comparables car on a la même fonction lien),
> summary(reg1)
 
Call:
glm(formula = nombre ~ ageconducteur + offset(log(exposition)),
family = poisson(link = "log"), data = baseFREQ)
 
Deviance Residuals:
Min       1Q   Median       3Q      Max
-0.5068  -0.3759  -0.2776  -0.1521   4.6391
 
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept)   -2.135616   0.072877 -29.304  < 2e-16 ***
ageconducteur -0.007913   0.001533  -5.163 2.43e-07 ***
---
Signif. codes:  0***0.001**0.01*0.05 ‘.’ 0.1 ‘ ’ 1
 
(Dispersion parameter for poisson family taken to be 1)
 
Null deviance: 13665  on 49999  degrees of freedom
Residual deviance: 13638  on 49998  degrees of freedom
AIC: 17882
 
Number of Fisher Scoring iterations: 6
 
> summary(reg2)
 
Call:
glm.nb(formula = nombre ~ ageconducteur + offset(log(exposition)),
data = baseFREQ, init.theta = 0.8502027145, link = log)
 
Deviance Residuals:
Min       1Q   Median       3Q      Max
-0.4904  -0.3699  -0.2757  -0.1522   4.0075
 
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept)   -2.118145   0.075283 -28.136  < 2e-16 ***
ageconducteur -0.008138   0.001583  -5.141 2.74e-07 ***
---
Signif. codes:  0***0.001**0.01*0.05 ‘.’ 0.1 ‘ ’ 1
 
(Dispersion parameter for Negative Binomial(0.8502) family taken to be 1)
 
Null deviance: 11727  on 49999  degrees of freedom
Residual deviance: 11700  on 49998  degrees of freedom
AIC: 17828
 
Number of Fisher Scoring iterations: 1
 
 
Theta:  0.850
Std. Err.:  0.148
 
2 x log-likelihood:  -17821.593 
On notera que l'utilisation d'un modèle binomial négatif est légitimé par la valeur du paramètre de dispersion dans le modèle quasi-Poisson
> reg1b=glm(nombre~ageconducteur+offset(log(exposition)),
+ data=baseFREQ,family=quasipoisson(link="log"))
> summary(reg1b)
 
Call:
glm(formula = nombre ~ ageconducteur + offset(log(exposition)),
family = quasipoisson(link = "log"), data = baseFREQ)
 
Deviance Residuals:
Min       1Q   Median       3Q      Max
-0.5068  -0.3759  -0.2776  -0.1521   4.6391
 
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept)   -2.135616   0.090143 -23.691   <2e-16 ***
ageconducteur -0.007913   0.001896  -4.174    3e-05 ***
---
Signif. codes:  0***0.001**0.01*0.05 ‘.’ 0.1 ‘ ’ 1
 
(Dispersion parameter for quasipoisson family taken to be 1.52996)
 
Null deviance: 13665  on 49999  degrees of freedom
Residual deviance: 13638  on 49998  degrees of freedom
AIC: NA
 
Number of Fisher Scoring iterations: 6

  • Poids versus variable offset dans une régression de Poisson
Rappelons que la loi (conditionnelle) dans une régression log-Poisson est
http://freakonometrics.blog.free.fr/public/perso4/ooofset-20.gif
de telle sorte que la log-vraisemblance s'écrit
http://freakonometrics.blog.free.fr/public/perso4/ooofset-21.gif
La fonction que l'on cherche donc à maximiser est alors
Si l'on rajoute des poids, on va chercher à minimiser la fonction
\
Dans le cas où l'on rajoute une variable offset, on suppose que
http://freakonometrics.blog.free.fr/public/perso4/ooofset-25.gif
de telle sorte que
http://freakonometrics.blog.free.fr/public/perso4/ooofset-24.gif
et donc la log-vraisemblance devient
http://freakonometrics.blog.free.fr/public/perso4/ooofset-26.gif
et la fonction que l'on va maximiser est alors
Les deux fonctions sont manifestement différentes, le poids n'intervenant que sur le second terme dans le cas de la variable offset. La régression de Poisson pondérée donne ici
> reg4=glm(nombre~ageconducteur,weight=exposition,
+  data=baseFREQ,family=poisson(link="log"))
Les ordres de grandeur des prédictions ne sont pas valides ici... Néanmoins, il est possible d'utiliser des poids, comme on le voit sur le calcul rapide ci-dessous,
> M=rep(NA,85-18+1)
> for(i in 18:85){
+ M[i-17]=weighted.mean(x=(baseFREQ$nombre/baseFREQ$exposition)
+ [baseFREQ$ageconducteur==i],
+ w=baseFREQ$exposition[baseFREQ$ageconducteur==i])
+ }
> M
[1] 0.25314943 0.20047905 0.20409416 0.23199038 0.17182485
[6] 0.17236472 0.10258972 0.10115476 0.10784129 0.10133346
[11] 0.08609449 0.05025678 0.08433958 0.06400527 0.06116849
[16] 0.07542661 0.09284957 0.06258664 0.08251126 0.07963923
[21] 0.08038180 0.08674745 0.06699507 0.09803112 0.09619517
[26] 0.08866225 0.09627715 0.09710725 0.08207893 0.09162368
[31] 0.07207799 0.08073084 0.09107094 0.09631251 0.06723020
[36] 0.06862383 0.08284661 0.08548334 0.05930442 0.08185568
[41] 0.07097164 0.07058023 0.06625962 0.08141103 0.10569807
[46] 0.04538514 0.05289389 0.09161058 0.04131018 0.05323120
[51] 0.08052976 0.06241604 0.06418933 0.04717143 0.11227675
[56] 0.05325793 0.04484281 0.04115995 0.11487048 0.06827367
[61] 0.07847543 0.08735320 0.07047837 0.05950256 0.10550113
[66] 0.04496403 0.07965526 0.06195787
> nb3
1          2          3          4          5
0.25314943 0.20047905 0.20409416 0.23199038 0.17182485
6          7          8          9         10
0.17236472 0.10258972 0.10115476 0.10784129 0.10133346
11         12         13         14         15
0.08609449 0.05025678 0.08433958 0.06400527 0.06116849
16         17         18         19         20
0.07542661 0.09284957 0.06258664 0.08251126 0.07963923
21         22         23         24         25
0.08038180 0.08674745 0.06699507 0.09803112 0.09619517
26         27         28         29         30
0.08866225 0.09627715 0.09710725 0.08207893 0.09162368
31         32         33         34         35
0.07207799 0.08073084 0.09107094 0.09631251 0.06723020
36         37         38         39         40
0.06862383 0.08284661 0.08548334 0.05930442 0.08185568
41         42         43         44         45
0.07097164 0.07058023 0.06625962 0.08141103 0.10569807
46         47         48         49         50
0.04538514 0.05289389 0.09161058 0.04131018 0.05323120
51         52         53         54         55
0.08052976 0.06241604 0.06418933 0.04717143 0.11227675
56         57         58         59         60
0.05325793 0.04484281 0.04115995 0.11487048 0.06827367
61         62         63         64         65
0.07847543 0.08735320 0.07047837 0.05950256 0.10550113
66         67         68
0.04496403 0.07965526 0.06195787 
L'idée est d'annualiser les nombres de sinistres, en considérant , mais de mettre un poids plus faible si l'assuré n'a pas été observé longtemps, i.e. un poids . Si on regarde maintenant dans la régression, on obtient exactement l'estimation obtenue avec la variable offset,
i.e
Bref, la régression avec une variable offset est une régression pondérée, mais sur le nombre de sinistres annualisé. Numériquement, on a
> reg5=glm((nombre/exposition)~ageconducteur,weight=exposition,
+ data=baseFREQ,family=poisson(link="log"))
> nb5=predict(reg5,newdata=data.frame(ageconducteur=
+ age,exposition=1), type="response")
> nb1
1          2          3          4          5
0.10248395 0.10167620 0.10087482 0.10007975 0.09929095
6          7          8          9         10
0.09850836 0.09773194 0.09696165 0.09619742 0.09543922
11         12         13         14         15
0.09468699 0.09394070 0.09320028 0.09246570 0.09173691
16         17         18         19         20
0.09101387 0.09029652 0.08958483 0.08887874 0.08817823
21         22         23         24         25
0.08748323 0.08679371 0.08610962 0.08543093 0.08475759
26         27         28         29         30
0.08408955 0.08342678 0.08276923 0.08211687 0.08146965
31         32         33         34         35
0.08082752 0.08019046 0.07955842 0.07893136 0.07830925
36         37         38         39         40
0.07769204 0.07707969 0.07647217 0.07586943 0.07527145
41         42         43         44         45
0.07467818 0.07408959 0.07350564 0.07292628 0.07235150
46         47         48         49         50
0.07178124 0.07121548 0.07065418 0.07009730 0.06954482
51         52         53         54         55
0.06899668 0.06845287 0.06791334 0.06737807 0.06684701
56         57         58         59         60
0.06632014 0.06579742 0.06527883 0.06476432 0.06425386
61         62         63         64         65
0.06374743 0.06324499 0.06274651 0.06225196 0.06176131
66         67         68
0.06127452 0.06079157 0.06031243
> nb5
1          2          3          4          5
0.10248395 0.10167620 0.10087482 0.10007975 0.09929095
6          7          8          9         10
0.09850836 0.09773194 0.09696165 0.09619742 0.09543922
11         12         13         14         15
0.09468699 0.09394070 0.09320028 0.09246570 0.09173691
16         17         18         19         20
0.09101387 0.09029652 0.08958483 0.08887874 0.08817823
21         22         23         24         25
0.08748323 0.08679371 0.08610962 0.08543093 0.08475759
26         27         28         29         30
0.08408955 0.08342678 0.08276923 0.08211687 0.08146965
31         32         33         34         35
0.08082752 0.08019046 0.07955842 0.07893136 0.07830925
36         37         38         39         40
0.07769204 0.07707969 0.07647217 0.07586943 0.07527145
41         42         43         44         45
0.07467818 0.07408959 0.07350564 0.07292628 0.07235150
46         47         48         49         50
0.07178124 0.07121548 0.07065418 0.07009730 0.06954482
51         52         53         54         55
0.06899668 0.06845287 0.06791334 0.06737807 0.06684701
56         57         58         59         60
0.06632014 0.06579742 0.06527883 0.06476432 0.06425386
61         62         63         64         65
0.06374743 0.06324499 0.06274651 0.06225196 0.06176131
66         67         68
0.06127452 0.06079157 0.06031243 
La seule difficulté est qu'il faut admettre que l'on puisse avoir des valeurs non-entière dans une régression de Poisson (ce qui est une toute autre histoire....).

Thursday, November 10 2011

ACT2040: sélection de variables versus sélection de modalités

En cours, nous avions évoqué (très rapidement) la sélection automatique de variables. La méthode la plus simple est une méthode stepwise, basé sur un critère de type AIC, ou BIC. Considérons la base suivante,

>  N = base$nbre
>  E = base$exposition
>  X1 = base$carburant
>  X2 = cut(base$agevehicule,c(0,3,10,101),
+ right=FALSE)
>  X3 = cut(base$ageconducteur,c(0,22,45,101),
+ right=FALSE)
>  X4 = as.factor(base$zone)
>  X5 = as.factor(base$puissance)
>  X6 = as.factor(base$region)
>  X7 = as.factor(base$marque)
>  base1=data.frame(N,E,X1,X2,X3,X4,X5,X6,X7)

Une méthode stepwise (backward) donne ici

> reg1=glm(N~X1+X2+X3+X4+X5+X6+X7+offset(log(E)),
+ family="poisson",data=base1)
> step(reg1)
Start:  AIC=20492.67
N ~ X1 + X2 + X3 + X4 + X5 + X6 + X7 + offset(log(E))
 
Df Deviance   AIC
- X5   11    15316 20482
- X3    2    15305 20490
<none>       15304 20493
- X2    2    15314 20499
- X1    1    15319 20506
- X7   10    15343 20511
- X4    5    15398 20576
- X6   14    15569 20729
 
Step:  AIC=20482.35
N ~ X1 + X2 + X3 + X4 + X6 + X7 + offset(log(E))
 
Df Deviance   AIC
- X3    2    15317 20479
<none>       15316 20482
- X2    2    15326 20488
- X1    1    15334 20498
- X7   10    15359 20505
- X4    5    15410 20566
- X6   14    15579 20717
 
Step:  AIC=20479.33
N ~ X1 + X2 + X4 + X6 + X7 + offset(log(E))
 
Df Deviance   AIC
<none>       15317 20479
- X2    2    15327 20485
- X1    1    15334 20495
- X7   10    15360 20502
- X4    5    15410 20563
- X6   14    15620 20754
 
Call:  glm(formula = N ~ X1 + X2 + X4 + X6 + X7 
+
offset(log(E)),
 family = "poisson", data = base1)   Coefficients: (Intercept) X1E X2[3,10) X2[10,101) X4B -1.0588454 -0.1653822 0.0266763 -0.1135451 -0.0004047 X4C X4D X4E X4F X60 0.1497622 0.3748811 0.5052894 0.4292016 -0.3590838 X61 X62 X63 X64 X65 -0.9300641 -1.0278887 -1.1818218 -1.0971797 -0.9459414 X66 X67 X68 X69 X610 -1.3690795 -1.1425678 -1.5309402 -1.3883549 -1.4603624 X611 X612 X613 X72 X73 -1.6763206 -1.3974092 -1.4864404 0.0246113 0.1144990 X74 X75 X76 X710 X711 -0.0932555 0.1635397 -0.1478095 0.2502030 0.1967970 X712 X713 X714 -0.2420215 0.2161411 -0.1963162   Degrees of Freedom: 49999 Total (i.e. Null); 49967 Residual Null Deviance: 15810 Residual Deviance: 15320 AIC: 20480

Autrement dit, on supprime la troisième (âge du conducteur principal, par classes arbitraires) et la cinquième variable (puissance du véhicule) en gardant toutes les autres. Mais ici, si une variable n'a pas été retenue, c'est que globalement, elle n'apportait pas beaucoup d'information. Il serait toutefois possible de garder une information partielle, en gardant éventuellement certaines modalités. L'idée est de disjoncter la base, en créant des variables indicatrices par modalités. La base sera beaucoup plus grosse, et la sélection prendra alors beaucoup plus de temps,

> base2=data.frame(model.matrix( ~ 0+X1+X2+X3+X4+X5+X6+X7,
+ data=base1)) > base2$E=base1$E > base2$N=base1$N > reg2=glm(N~.-E+offset(log(E)),family="poisson", + data=base2) > step(reg2) Start: AIC=20492.67 N ~ (X1D + X1E + X2.3.10. + X2.10.101. + X3.22.45. + X3.45.101. X4B + X4C + X4D + X4E + X4F + X55 + X56 + X57 + X58 + X59 + X510 + X511 + X512 + X513 + X514 + X515 + X60 + X61 + X62 + X63 + X64 + X65 + X66 + X67 + X68 + X69 + X610 + X611 + X612 + X613 + X72 + X73 + X74 + X75 + X76 + X710 + X711 + X712 + X713 + X714 + E) - E + offset(log(E))     Step: AIC=20492.67 N ~ X1D + X2.3.10. + X2.10.101. + X3.22.45. + X3.45.101. + X4B X4C + X4D + X4E + X4F + X55 + X56 + X57 + X58 + X59 + X510 + X511 + X512 + X513 + X514 + X515 + X60 + X61 + X62 + X63 + X64 + X65 + X66 + X67 + X68 + X69 + X610 + X611 + X612 + X613 + X72 + X73 + X74 + X75 + X76 + X710 + X711 + X712 + X713 + X714 + offset(log(E))   Df Deviance AIC - X4B 1 15304 20491 - X58 1 15304 20491 - X511 1 15304 20491 - X2.3.10. 1 15304 20491 - X72 1 15304 20491 - X513 1 15304 20491 - X512 1 15304 20491 - X515 1 15304 20491 - X74 1 15305 20491 - X3.45.101. 1 15305 20491 - X714 1 15305 20491 - X55 1 15305 20492 - X3.22.45. 1 15305 20492 - X711 1 15306 20492 - X76 1 15306 20492 - X59 1 15306 20492 <none> 15304 20493 - X514 1 15306 20493 - X713 1 15306 20493 - X73 1 15307 20493 - X56 1 15307 20493 - X710 1 15307 20494 - X75 1 15308 20494 - X2.10.101. 1 15308 20495 - X57 1 15309 20495 - X4C 1 15310 20496 - X510 1 15310 20496 - X60 1 15312 20498 - X4F 1 15314 20500 - X712 1 15316 20503 - X1D 1 15319 20506 - X4D 1 15337 20524 - X61 1 15345 20532 - X65 1 15350 20536 - X62 1 15352 20538 - X64 1 15359 20545 - X4E 1 15362 20549 - X63 1 15366 20553 - X67 1 15370 20556 - X612 1 15381 20568 - X69 1 15382 20569 - X66 1 15387 20574 - X610 1 15389 20576 - X68 1 15393 20580 - X611 1 15406 20592 - X613 1 15451 20637   Step: AIC=20490.67 N ~ X1D + X2.3.10. + X2.10.101. + X3.22.45. + X3.45.101. + X4C X4D + X4E + X4F + X55 + X56 + X57 + X58 + X59 + X510 + X511 + X512 + X513 + X514 + X515 + X60 + X61 + X62 + X63 + X64 + X65 + X66 + X67 + X68 + X69 + X610 + X611 + X612 + X613 + X72 + X73 + X74 + X75 + X76 + X710 + X711 + X712 + X713 + X714 + offset(log(E))

etc etc... et si on va directement à la fin,

Step:  AIC=20469.18
N ~ X1D + X2.10.101. + X4C + X4D + X4E + X4F + X57 + X510 + X60
X61 + X62 + X63 + X64 + X65 + X66 + X67 + X68 + X69 + X610 +
X611 + X612 + X613 + X73 + X75 + X76 + X710 + X712 + X713 +
offset(log(E))
 
Df Deviance   AIC
<none>             15315 20469
- X76         1    15317 20470
- X713        1    15317 20470
- X73         1    15317 20470
- X57         1    15318 20470
- X75         1    15318 20471
- X710        1    15319 20471
- X510        1    15319 20471
- X4C         1    15322 20474
- X60         1    15322 20475
- X2.10.101.  1    15325 20478
- X4F         1    15325 20478
- X1D         1    15333 20485
- X712        1    15338 20490
- X61         1    15356 20508
- X4D         1    15359 20511
- X62         1    15363 20515
- X65         1    15363 20515
- X64         1    15371 20524
- X63         1    15378 20530
- X67         1    15383 20536
- X4E         1    15390 20543
- X612        1    15394 20547
- X69         1    15396 20548
- X66         1    15400 20553
- X610        1    15403 20555
- X68         1    15407 20559
- X611        1    15419 20572
- X613        1    15467 20619
 
Call:  glm(formula = N ~ X1D + X2.10.101. + X4C + X4D + X4E + X4F
X57 + X510 + X60 + X61 + X62 + X63 + X64 + X65 + X66 + X67 +
X68 + X69 + X610 + X611 + X612 + X613 + X73 + X75 + X76 +
X710 + X712 + X713 + offset(log(E)), family = "poisson",
data = base2)
 
Coefficients:
(Intercept)          X1D   X2.10.101.          X4C          X4D
-1.20880      0.16886     -0.13808      0.14888      0.37539
X4E          X4F          X57         X510          X60
0.50458      0.42768      0.08381      0.18722     -0.36509
X61          X62          X63          X64          X65
-0.93836     -1.03471     -1.18803     -1.10217     -0.95624
X66          X67          X68          X69         X610
-1.37463     -1.15391     -1.54213     -1.40188     -1.47217
X611         X612         X613          X73          X75
-1.68559     -1.40582     -1.49700      0.10874      0.15022
X76         X710         X712         X713
-0.15183      0.21948     -0.27400      0.19565
 
Degrees of Freedom: 49999 Total (i.e. Null);  49971 Residual
Null Deviance:	    15810
Residual Deviance: 15310 	AIC: 20470 

Si la troisième variable (âge du conducteur principal, par classes arbitraires) disparait assez vite, en revanche, une information sur la cinquième (la puissance) est gardée car certaines modalités semblent être informative sur la fréquence d'accidents. En revanche, on notera qui si on fait un arbre, la troisième variable était toujours clairement significative, ce qui peut nous conforter dans l'idée de faire de la sélection de variables sur les modalités.

> library(tree)
> TREE= tree(N~X1+X2+X3+X4+X5+X6+X7+offset(log(E)),split="gini",
+
mincut = 2500,data=base1) > plot(TREE) > text(TREE,cex=.9)

Tuesday, November 8 2011

ACT2040: régression binomiale négative

La régression binomiale négative n'apparait pas dans les familles proposées sous R, mais il suffit d'utiliser un autre package pour la faire (voire plusieurs autres si on veut aller plus loin sur les classes de modèles sur les variables de comptage),

Si on compare avec la régression de Poisson, on voit que les estimations sont très proches,

>  regp = glm(nbre~ageconducteur+offset(exposition),
+  data=base,family=poisson)
>  regnb = glm.nb(nbre~ageconducteur+offset(exposition),
+  data=base)
>  coefficients(regp)
(Intercept) ageconducteur
-3.206981838  -0.006714151
>  coefficients(regnb)
(Intercept) ageconducteur
-3.214933688  -0.006614091 

On peut également comparer les prédictions, voire rajouter une transformation nonlinéaire de l'âge du conducteur (pour noter que changer de loi influence peu l'estimation, car par défaut le lien est un lien logarithmique, comme pour la régression de Poisson).

 regf = glm(nbre~as.factor(ageconducteur)+offset(exposition),
data=base,family=poisson)
age=sort(unique(base$ageconducteur))
Y1=predict(regf,newdata=data.frame(ageconducteur=age,
exposition=1),type="response")
Y2=predict(regp,newdata=data.frame(ageconducteur=age,
exposition=1),type="response")
Y3=predict(regnb,newdata=data.frame(ageconducteur=age,
exposition=1),type="response")
plot(age,Y1)
lines(age,Y2,col="red",lwd=3)
lines(age,Y3,col="blue",lwd=3)
 
 
library(splines)
regps = glm(nbre~bs(ageconducteur,5)+offset(exposition),
data=base,family=poisson)
regnbs = glm.nb(nbre~bs(ageconducteur,5)+offset(exposition),
data=base)
Y2s=predict(regps,newdata=data.frame(ageconducteur=age,
exposition=1),type="response")
Y3s=predict(regnbs,newdata=data.frame(ageconducteur=age,
exposition=1),type="response")
lines(age,Y2s,col="red",lwd=1)
lines(age,Y3s,col="blue",lwd=1)

Wednesday, October 26 2011

ACT2040, régression sur des variables catégorielles (facteurs)

Petit complément par rapport au cours de mardi. On avait évoqué tout d'abord la lecture des sorties lorsque l'on régresse sur des variables catégorielles (des facteurs). Commençons par supprimer la constante de la régression
> reg0=glm(nbre~0+zone,offset=log(exposition),data=base, 
+
family=poisson(link="log"))
> summary(reg0)
 
Call:
glm(formula = nbre ~ 0 + zone, family = poisson(link = "log"),
data = base, offset = log(exposition))
 
Deviance Residuals:
Min 1Q Median 3Q Max
-0.5717 -0.3968 -0.2996 -0.1547 12.6722
 
Coefficients:
Estimate Std. Error z value Pr(>|z|)
zoneB -2.54187 0.06287 -40.43 <2e-16 ***
zoneA -2.54912 0.05285 -48.23 <2e-16 ***
zoneC -2.38525 0.03753 -63.56 <2e-16 ***
zoneD -2.13454 0.03878 -55.05 <2e-16 ***
zoneE -2.00204 0.03965 -50.49 <2e-16 ***
zoneF -2.06932 0.11547 -17.92 <2e-16 ***
---
Signif. codes: 0***0.001**0.01*0.05 ‘.’ 0.1 ‘ ’ 1
 
(Dispersion parameter for poisson family taken to be 1)
 
Null deviance: 50966 on 50000 degrees of freedom
Residual deviance: 15692 on 49994 degrees of freedom
AIC: 20800
 
Number of Fisher Scoring iterations: 6
 
> predict(reg0,newdata=data.frame(
+
zone=c("A","B","C","D","E"),exposition=rep(1,5)))
1 2 3 4 5
-2.549120 -2.541870 -2.385253 -2.134543 -2.002044
On voit que toutes les modalités sont présentes, et toutes sont significatives. Si on régresse sur la constante, il faudra supprimer une modalité pour rendre le modèle identifiable. On peut forcer pour que la modalité de référence soit la seconde,
> base$zone=relevel(base$zone,"B")
> regB=glm(nbre~zone,offset=log(exposition),data=base,
+ family=poisson(link="log"))
> summary(regB)
 
Call:
glm(formula = nbre ~ zone, family = poisson(link = "log"), 
data = base, offset = log(exposition))   Deviance Residuals: Min 1Q Median 3Q Max -0.5717 -0.3968 -0.2996 -0.1547 12.6722   Coefficients: Estimate Std. Error z value Pr(>|z|) (Intercept) -2.54187 0.06287 -40.431 < 2e-16 *** zoneA -0.00725 0.08213 -0.088 0.929661 zoneC 0.15662 0.07322 2.139 0.032432 * zoneD 0.40733 0.07387 5.514 3.50e-08 *** zoneE 0.53983 0.07433 7.263 3.80e-13 *** zoneF 0.47255 0.13148 3.594 0.000325 *** --- Signif. codes: 0***0.001**0.01*0.05 ‘.’ 0.1 ‘ ’ 1   (Dispersion parameter for poisson family taken to be 1)   Null deviance: 15809 on 49999 degrees of freedom Residual deviance: 15692 on 49994 degrees of freedom AIC: 20800   Number of Fisher Scoring iterations: 6   > predict(regB,newdata=data.frame( + zone=c("A","B","C","D","E"),exposition=rep(1,5))) 1 2 3 4 5 -2.549120 -2.541870 -2.385253 -2.134543 -2.002044
On notera que les prédictions ne changent pas. On peut aussi choisir la première comme modalité de référence,
> base$zone=relevel(base$zone,"A")
> reg=glm(nbre~zone,offset=log(exposition),
> data=base,family=poisson(link="log"))
> summary(reg)
 
Call:
glm(formula = nbre ~ zone, family = poisson(link = "log"), 
data = base, offset = log(exposition))   Deviance Residuals: Min 1Q Median 3Q Max -0.5717 -0.3968 -0.2996 -0.1547 12.6722   Coefficients: Estimate Std. Error z value Pr(>|z|) (Intercept) -2.54912 0.05285 -48.232 < 2e-16 *** zoneB 0.00725 0.08213 0.088 0.929661 zoneC 0.16387 0.06482 2.528 0.011471 * zoneD 0.41458 0.06555 6.324 2.54e-10 *** zoneE 0.54708 0.06607 8.280 < 2e-16 *** zoneF 0.47980 0.12699 3.778 0.000158 *** --- Signif. codes: 0***0.001**0.01*0.05 ‘.’ 0.1 ‘ ’ 1   (Dispersion parameter for poisson family taken to be 1)   Null deviance: 15809 on 49999 degrees of freedom Residual deviance: 15692 on 49994 degrees of freedom AIC: 20800   Number of Fisher Scoring iterations: 6
Le fait que la seconde modalité ne soit pas significative se lit par rapport à la modalité de référence (en l’occurrence la première): non significatif signifie alors non significativement différente. Autrement dit, on peut regrouper les modalités en une seule.
> base$zonesimple=base$zone
> base$zonesimple[base$zone%in%c("A","B")]="A"
> reg=glm(nbre~zonesimple,offset=log(exposition),
+ data=base,family=poisson(link="log"))
> summary(reg)
 
Call:
glm(formula = nbre ~ zonesimple, family = poisson(link = "log"),
data = base, offset = log(exposition))
 
Deviance Residuals:
Min       1Q   Median       3Q      Max
-0.5717  -0.3959  -0.2989  -0.1547  12.6722
 
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) -2.54612    0.04046 -62.937  < 2e-16 ***
zonesimpleC  0.16087    0.05518   2.915  0.00355 **
zonesimpleD  0.41158    0.05604   7.345 2.06e-13 ***
zonesimpleE  0.54408    0.05665   9.605  < 2e-16 ***
zonesimpleF  0.47681    0.12235   3.897 9.74e-05 ***
---
Signif. codes:  0***0.001**0.01*0.05 ‘.’ 0.1 ‘ ’ 1
 
(Dispersion parameter for poisson family taken to be 1)
 
Null deviance: 15809  on 49999  degrees of freedom
Residual deviance: 15692  on 49995  degrees of freedom
AIC: 20798
 
Number of Fisher Scoring iterations: 6
On note qu'avec ce regroupement, les autres modalités sont sensiblement différentes. On peut aussi retenir la troisième comme modalité de référence
> base$zonesimple=relevel(base$zonesimple,"C")
> reg=glm(nbre~zonesimple,offset=log(exposition),
+ data=base,family=poisson(link="log"))
> summary(reg)
 
Call:
glm(formula = nbre ~ zonesimple, family = poisson(link = "log"),
data = base, offset = log(exposition))
 
Deviance Residuals:
Min       1Q   Median       3Q      Max
-0.5717  -0.3959  -0.2989  -0.1547  12.6722
 
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) -2.38525    0.03753 -63.557  < 2e-16 ***
zonesimpleA -0.16087    0.05518  -2.915  0.00355 **
zonesimpleD  0.25071    0.05396   4.646 3.39e-06 ***
zonesimpleE  0.38321    0.05460   7.019 2.24e-12 ***
zonesimpleF  0.31593    0.12142   2.602  0.00927 **
---
Signif. codes:  0***0.001**0.01*0.05 ‘.’ 0.1 ‘ ’ 1
 
(Dispersion parameter for poisson family taken to be 1)
 
Null deviance: 15809  on 49999  degrees of freedom
Residual deviance: 15692  on 49995  degrees of freedom
AIC: 20798
 
Number of Fisher Scoring iterations: 6
Comme toutes les modalités semblent significatives, on peut tenter de prendre comme modalité de référence une des dernières (dont les estimations des coefficients donnent des résultats très proches)
> base$zonesimple=relevel(base$zonesimple,"F")
> reg=glm(nbre~zonesimple,offset=log(exposition),
+ data=base,family=poisson(link="log"))
> summary(reg)
 
Call:
glm(formula = nbre ~ zonesimple, family = poisson(link = "log"),
data = base, offset = log(exposition))
 
Deviance Residuals:
Min       1Q   Median       3Q      Max
-0.5717  -0.3959  -0.2989  -0.1547  12.6722
 
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) -2.06932    0.11547 -17.921  < 2e-16 ***
zonesimpleC -0.31593    0.12142  -2.602  0.00927 **
zonesimpleA -0.47681    0.12235  -3.897 9.74e-05 ***
zonesimpleD -0.06522    0.12181  -0.535  0.59232
zonesimpleE  0.06727    0.12209   0.551  0.58161
---
Signif. codes:  0***0.001**0.01*0.05 ‘.’ 0.1 ‘ ’ 1
 
(Dispersion parameter for poisson family taken to be 1)
 
Null deviance: 15809  on 49999  degrees of freedom
Residual deviance: 15692  on 49995  degrees of freedom
AIC: 20798
 
Number of Fisher Scoring iterations: 6
Au vue de cette dernière sortie, on peut tenter de fusionner toutes les dernières classes ensembles
> base$zonesimple[base$zone%in%c("D","E","F")]="F"
> reg=glm(nbre~zonesimple,offset=log(exposition),
+ data=base,family=poisson(link="log"))
> summary(reg)
 
Call:
glm(formula = nbre ~ zonesimple, family = poisson(link = "log"),
data = base, offset = log(exposition))
 
Deviance Residuals:
Min       1Q   Median       3Q      Max
-0.5660  -0.3959  -0.3004  -0.1547  12.5929
 
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) -2.07182    0.02696 -76.853  < 2e-16 ***
zonesimpleC -0.31344    0.04621  -6.783 1.18e-11 ***
zonesimpleA -0.47431    0.04861  -9.757  < 2e-16 ***
---
Signif. codes:  0***0.001**0.01*0.05 ‘.’ 0.1 ‘ ’ 1
 
(Dispersion parameter for poisson family taken to be 1)
 
Null deviance: 15809  on 49999  degrees of freedom
Residual deviance: 15698  on 49997  degrees of freedom
AIC: 20800
 
Number of Fisher Scoring iterations: 6
Bon, formellement, regrouper deux modalités (i.e. décréter que deux variables sont non significatives simultanément) demande un peu plus qu'un test de Student, ou que deux tests de Student.... Si on remonte un peu en arrière, on aurait pu faire un test multiple avant de fusionner les trois modalités (un test de type Fisher, ou une ANOVA)
> base$zonesimple=relevel(base$zonesimple,"F")
> reg=glm(nbre~zonesimple,offset=log(exposition),
+ data=base,family=poisson(link="log"))
> library(car)
> linearHypothesis(reg,c("zonesimpleD=0","zonesimpleE=0"))
Linear hypothesis test
 
Hypothesis:
zonesimpleD = 0
zonesimpleE = 0
 
Model 1: restricted model
Model 2: nbre ~ zonesimple
 
Res.Df Df  Chisq Pr(>Chisq)
1  49997
2  49995  2 5.7073    0.05763 .
---
Signif. codes:  0***0.001**0.01*0.05 ‘.’ 0.1 ‘ ’ 1 
Manifestement, on peut accepter l'hypothèse que ces trois catégories n'en font qu'une. La zone géographique peut alors se découper en trois grandes zones, et pas en six. On notera que cela correspond à ce que propose un arbre de régression
> library(tree)
> arbre=tree(nbre~zone+offset(log(exposition)),
+ data=base,split="gini")
> plot(arbre)
> text(arbre)

Thursday, October 20 2011

ACT2040, régression de Poisson, exposition et méthode des marges

Suite au cours de mardi, j'ai mis en ligne le code tapé durant le cours. A un moment, on regardait le lien entre un modèle log-Poisson sur deux variables qualitatives, et une méthode de biais minimale (en l’occurrence la méthode des marges). Par rapport au premier billet, où on comptait juste le nombre de polices, on a vu mardi qu'il était possible de prendre en compte l'exposition. Commençons par cumuler les expositions par classe, dans un tableau croisé

> N <- base$nbre
> E <- base$exposition
> X1 <- base$carburant
> X2 <- cut(base$agevehicule,c(0,3,10,101),right=FALSE)
> names1 <- levels(X1)
> names2 <- levels(X2)
> (POPULATION=table(X1,X2))
X2
X1 [0,3) [3,10) [10,101)
D 7492 10203 6718
E 6275 9417 9895
 
> EXPOSITION=POPULATION
> for(k in 1:nrow(EXPOSITION)){
+ EXPOSITION[k,]=tapply(E[X1==names1[k]],
+ X2[X1==names1[k]],sum)}
> EXPOSITION
X2
X1 [0,3) [3,10) [10,101)
D 3078.938 5653.109 3787.503
E 2735.014 5398.950 5777.619
 
> SINISTRE=POPULATION
> for(k in 1:nrow(SINISTRE)){
+ SINISTRE[k,]=tapply(N[X1==names1[k]],
+ X2[X1==names1[k]],sum)}
> SINISTRE
X2
X1 [0,3) [3,10) [10,101)
D 348 659 366
E 229 544 551
 
> (FREQUENCE=SINISTRE/EXPOSITION)
X2
X1 [0,3) [3,10) [10,101)
D 0.11302600 0.11657303 0.09663359
E 0.08372899 0.10076032 0.09536801
La méthode de balancement s'écrit ici, en tenant compte de l'exposition (et non plus du nombre de polices)
> L=matrix(NA,100,2);C=matrix(NA,100,3)
> L[1,]=rep(m,2);colnames(L)=names1
> C[1,]=rep(m,3);colnames(C)=names2
> for(j in 2:100){
+ L[j,1]=sum(SINISTRE[1,])/sum(EXPOSITION[1,]*C[j-1,])
+ L[j,2]=sum(SINISTRE[2,])/sum(EXPOSITION[2,]*C[j-1,])
+ C[j,1]=sum(SINISTRE[,1])/sum(EXPOSITION[,1]*L[j,])
+ C[j,2]=sum(SINISTRE[,2])/sum(EXPOSITION[,2]*L[j,])
+ C[j,3]=sum(SINISTRE[,3])/sum(EXPOSITION[,3]*L[j,])
+ }
> PREDICTION2=SINISTRE
> PREDICTION2[1,]=L[100,1]*C[100,]
> PREDICTION2[2,]=L[100,2]*C[100,]
> PREDICTION2
X2
X1 [0,3) [3,10) [10,101)
D 0.10546068 0.11594590 0.10371964
E 0.09224564 0.10141698 0.09072277
On peut vérifier que le principe de balancement est vérifié,
> sum(PREDICTION2[1,]*EXPOSITION[1,])
[1] 1373
 
> sum(SINISTRE[1,])
[1] 1373
 
> sum(PREDICTION2[2,]*EXPOSITION[2,])
[1] 1324
 
> sum(SINISTRE[2,])
[1] 1324
etc (les plus sceptiques continueront les calculs). Comme nous l'avons vu en cours, les prédictions obtenues ici coïncident avec une régression log-Poisson avec le logarithme de l'exposition en variable offset.
> donnees <- data.frame(N,E,X1,X2)
> regpoislog <- glm(N~X1+X2,offset=log(E),data=donnees,
+ family=poisson(link="log"),start=c(1,1,1,1))
> summary(regpoislog)
 
Call:
glm(formula = N ~ X1 + X2, family = poisson(link = "log"),
start = c(1, 1, 1, 1), offset = log(E))
 
Deviance Residuals:
Min 1Q Median 3Q Max
-0.6297 -0.4260 -0.3012 -0.1560 13.0022
 
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) -2.24942 0.04495 -50.041 < 2e-16 ***
X1E -0.13388 0.03878 -3.452 0.000556 ***
X2[3,10) 0.09479 0.05064 1.872 0.061259 .
X2[10,101) -0.01665 0.05339 -0.312 0.755203
---
Signif. codes: 0***0.001**0.01*0.05 ‘.’ 0.1 ‘ ’ 1
 
(Dispersion parameter for poisson family taken to be 1)
 
Null deviance: 15809 on 49999 degrees of freedom
Residual deviance: 15788 on 49996 degrees of freedom
AIC: 20892
 
Number of Fisher Scoring iterations: 9
 
 
> newdonnees <- data.frame(X1=factor(rep(names1,3)),E=rep(1,6),
> X2=factor(rep(names2,each=2)))
> matrix(predict(regpoislog,newdata=newdonnees,
+ type="response"),2,3)
[,1] [,2] [,3]
[1,] 0.10546068 0.1159459 0.10371964
[2,] 0.09224564 0.1014170 0.09072277
Moralité, cette méthode de biais minimal est un cas particulier des modèles GLM. Sinon, sur la programmation en R, il existe une vignette pour le package pscl. Pour ceux qui veulent des compléments sur les GLM, je mets un lien vers le chapitre 15 du livre de John Fox Applied regression analysis and generalized linear models ainsi que vers le livre de Jeff Gills Generalized Linear Models, ou le livre de James K. Lindsey Applying Generalized Linear Models. Je voudrais aussi renvoyer vers les notes de cours de Germán Rodríguez, avec des notes sur les modèles Logit pour les variables binomiales, la régression de Poisson (avec un petit complément sur la notion de overdispersion que nous aborderons mardi prochain).
Crédit: l'illustration utilisée est à nouveau signée Francis Léveillée

Monday, October 3 2011

ACT2040, introduction aux modèles linéaires généralisés

On commencera ce mardi les GLM, après avoir introduit les lois exponentielles (qui ont du être revues en démonstration vendredi dernier). La notation utilisée sera que la loi (densité ou fonction de probabilité) de http://freakonometrics.blog.free.fr/public/perso4/Yi-ltx.gif sera de la forme

http://freakonometrics.blog.free.fr/public/perso4/loi-exponentielle.gif
Pour un complément plus exhaustif, je renvoie au chapitre en ligne.
  • Le modèle linéaire (Gaussien)
Le modèle de base est le modèle Gaussien que l'on avait revu au dernier cours,
> X=c(1,2,3,4)
> Y=c(1,2,5,6)
> base=data.frame(X,Y)
> reg1=lm(Y~1+X,data=base)
> nbase=data.frame(X=seq(0,5,by=.1))
> Y1=predict(reg1,newdata=nbase)
Pour une prédiction (unique), on obtient la prédiction suivante

Le code pour une telle représentation est le suivant
> plot(X,Y,pch=3,cex=1.5,lwd=2,xlab="",ylab="")
> lines(nbase$X,Y1,col="red",lwd=2)
> u=2
> mu=predict(reg1)[2]
> sigma=summary(reg1)$sigma
> y=seq(0,7,.05)
> loi=dnorm(y,mu,sigma)
> segments(u,y,loi+u,y,col="light green")
> lines(loi+u,y)
> abline(v=u,lty=2)
> points(X[2],Y[2],pch=3,cex=1.5,lwd=2)
> points(X[2],predict(reg1)[2],pch=19,col="red")
> arrows(u-.2,qnorm(.05,mu,sigma),
+ u-.2,qnorm(.95,mu,sigma),length=0.1,code=3,col="blue")
On peut multiplier les prédictions, en se basant sur l'hypothèse d'homoscédasticité (la variance sera alors constante)

Mais on peut aller plus loin
  • Le modèle linéaire généralisé
Plusieurs modèles peuvent etre estimés, en changeant la loi de la variable à expliquer, et la fonction lien,
> reg2=glm(Y~1+X,data=base,family=poisson(link="identity"))
> Y2=predict(reg2,newdata=nbase,type="response")
> reg3=glm(Y~1+X,data=base,family=poisson(link="log"))
> Y3=predict(reg3,newdata=nbase,type="response")
> reg4=glm(Y~1+X,data=base,family=gaussian(link="log"))
> Y4=predict(reg4,newdata=nbase,type="response")
> sigma=sqrt(summary(reg4)$dispersion)
Pour le modèle Poissonnien avec un lien identité, on obtient

On obtient ainsi une variance qui augmente avec la prédiction,

Pour une régression de Poisson avec un lien logarithmique,

i.e. pour nos quatre prédictions

On peut comparer avec une prédiction d'un modèle Gaussien avec un lien logarithmique,

i.e. pour les quatre prédictions

Wednesday, November 3 2010

My residuals look weird... aren't they ?

Since I got the same question twice, let us look at it quickly....  Some students show me a graph (from a Poisson regression) which looks like that,

and they asked "isn't it weird ?", i.e."residuals are null or positive... this is not what we should have, right ?". Actually, residuals are always centered in a glm regression (if you keep the intercept). Let us generate a dataset,
> n=500
> set.seed(1)
> X=runif(n)
> E=rnorm(n)*.3
> XB=-5+2*X+E
> lambda=exp(XB)
> Y=rpois(n,lambda)
> reg=glm(Y~X,family=poisson)
> plot(predict(reg),residuals(reg,"pearson"))
If we look carefully, residuals in the lower part, they are strictly negative... And since, on average, we should have zero, then we are very close to 0.
> mean(residuals(reg,"pearson"))
[1] -0.003381202

Actually, the true value is either
> u=seq(-10,2,by=.1)
> lines(u,(0-exp(u))/sqrt(exp(u)),lwd=.5,col="red")
or
> lines(u,(1-exp(u))/sqrt(exp(u)),lwd=.5,col="blue")

So what happens ? Here, the value of the parameter (the expected value of our Poisson distribution) is very small
> mean(Y)
[1] 0.026

A crude estimator for the glm would be to consider a regression only on the intercept. In that case, the error would be either 0-0.026, or 1-0.026, i.e. for 97.5%, we have -0.026 (which is small, close to 0, but negative), while for 2.5% we have 0.974 (which is huge compared to the previous value). Well, on that graph, we plot Pearson's residuals, i..e. we divide the difference by the expected value... so we have those two curves....
Actually, there are only two clouds since the probability to have 1 (when lambda is close to 2.5%) is small, and extremely small to exceed 2...
On the graph below, the average value of Y increases (and then decreases): initially, almost all the points are on the red curve (i.e. we observe 0, and predict something extremely small), then we have more and more points on the blue curve (for more and more individuals we have 1), then we start to see some on the green curve (i.e. 2), etc. Note that areas where we observe clouds remain unchanged (i.e. the colored curves).
> P=seq(-5,-2,by=.05)
> P=c(P,rev(P))
> for(i in 1:length(P)){
+ set.seed(1)
+ XB=P[i]+2*X+E
+ lambda=exp(XB)
+ Y=rpois(n,lambda)
+ reg=glm(Y~X,family=poisson)
+ u=seq(-10,2,by=.1)
+ plot(predict(reg),residuals(reg,"pearson"),ylim=c(-1,5),xlim=c(-6,0))
+ abline(h=0)
+ lines(u,(0-exp(u))/sqrt(exp(u)),lwd=.5,col="red")
+ lines(u,(1-exp(u))/sqrt(exp(u)),lwd=.5,col="blue")
+ lines(u,(2-exp(u))/sqrt(exp(u)),lwd=.5,col="green")
+ rlines(u,(3-exp(u))/sqrt(exp(u)),lwd=.5,col="orange")
+ lines(u,(3-exp(u))/sqrt(exp(u)),lwd=.5,col="orange")
+ lines(u,(4-exp(u))/sqrt(exp(u)),lwd=.5,col="purple")
+ points(predict(reg),residuals(reg,"pearson"))
+ }


So the graph is perfectly normal since we have a discrete distribution (while we predict something continuous).

Tuesday, November 2 2010

Statistique de l'assurance STT6705V, partie 9

Attention, étant donné que le changement d'heure a eu lieu en France samedi dernier, mais qu'il n'aura lieu au Canada que ce samedi, la transmission sera légèrement perturbée.. Le cours durera deux heures et pas trois: de 11:30-13:30 à Montréal (on commence une heure plus tard) et 16:30-18:30 à Rennes (car la salle n'était pas libre plus tôt).
Nous finirons la partie sur le provisionnement (je parlerais des projets) et sinon je renvoie ici pour des notes de cours détaillées sur ce que nous avons vu.
COMPLÉMENT: un paragraphe de dernière minute... je mets des lignes de code, ici. Il s'agit de ligne que nous utiliserons, et que nous commenterons pendant ce cours..

Monday, October 25 2010

Statistique de l'assurance STT6705V, partie 8

Les vidéos du dernier cours sont en ligne ici et (désolé pour le retard). La semaine étant une semaine de relâche à Montréal, il n'y aura pas de cours cette semaine...
Pour reprendre les simulations qui n'ont pas marché au tableau (j'avais oublié restreindre un vecteur de paramètres et j'avais un problème sur le nom d'un paramètre), je mets ci-dessous des lignes de code qui tournent. Pour commencer, on importe le triangle, et on fait une simple régression log-Poisson,
> source("http://perso.univ-rennes1.fr/arthur.charpentier/bases.R")
> an <- 6; ligne = rep(1:an, each=an);
>          colonne = rep(1:an, an)
> passe = (ligne + colonne - 1)<=an;
> n = sum(passe)
> INC=PAID
> INC[,2:6]=PAID[,2:6]-PAID[,1:5]
> Y = as.vector(INC)
> lig = as.factor(colonne)
> col = as.factor(ligne)
> base=data.frame(Y,lig,col,passe)
> reg=glm(Y~lig+col,data=base,  family=poisson("log"))
> YP=predict(reg,newdata=base,  type="response")
 
ensuite, on fait un peut de bootstrap pour générer des pseudo-triangles, et on les utilise pour quantifier l'erreur d'estimation du montant de provision,
> set.seed(1)
> R1=rep(NA,2000)
> R2=rep(NA,2000)
> R3=rep(NA,2000)
> for(s in 1:2000){
+ erreursim=sample(erreur,
+    size=36,replace=TRUE)
+ Ysim=YP+erreursim*sqrt(YP)
+ Ysim[passe==FALSE]=NA
+ basesim=data.frame(Ysim,lig,col)
+ if(min(Ysim[passe==TRUE])>0){
+ reg=glm(Ysim~lig+col,data=basesim,  family=quasipoisson("log"))
+ YPs=predict(reg,newdata=base, type="response")
+ nsim=sum(passe==FALSE)
+ phi=summary(reg)$dispersion
+ MU=YPs[passe==FALSE]
   # il manquait ici la spécification des indices
[passe==FALSE]
+ R1[s]=sum(rpois(nsim,lambda=MU))
+ R2[s]=sum(rnbinom(nsim,mu=MU,  size=MU/(phi*MU-1)))
+ R3[s]=sum(rgamma(nsim,shape=MU/phi,  scale=phi))
+ }}
.On peut alors faire des dessins pour visualiser les trois distributions du montant de provisions (ou plutôt des scénarios générés de paiements futur), avec une loi de Poisson tout d'abord, en rouge,

On a alors deux méthodes qui tentent d'approcher la loi quasi poisson, i.e. une loi Gamma en bleu,

et une loi binomiale négative (qui ne semble pas tourner ici...). A suivre donc...

Tuesday, October 19 2010

Tweedie models and ratemaking


At the end of the report on standard ratemaking (here) there is a a short footnote on Tweedie models, and a full appendix in the Practitioner’s Guide to Generalized Linear Models by Watson Wyatt (here). We will discuss more in claims reserving the use of that family, but it is also possible to use it in ratemaking. The idea is that, instead of modeling independently frequency and individual costs, it can be possible to model the total sum as a compound Poisson. See e.g. Fitting Tweedie’s Compound Poisson Model to Insurance Claims Data: Dispersion Modelling by Gordon Smyth and Bent Jørgensen (here), Pure Premium Regression with the Tweedie Model by Glenn Meyers (here) or Compound Poisson distribution and GLM’s – Tweedie’s distribution by Rob Kaas (here).
In order to illustrate the difference between

  • a Tweedie model for the individual cumulated loss,
  • a Poisson model for frequency and a Gamma model for single claims,
consider the following simulated model:
> set.seed(1)
> n=1000
> X1=runif(n)*2
> X2=rexp(n)
> lambda=exp(0.25+X1/10+X2/10)
> N=rpois(n,lambda)
> basen=data.frame(N,X1,X2)
> moy=exp((1+X1+X2))
> Y=NA
> I=NA
> for(i in 1:n){I=c(I,rep(i,sum(N[i])))
+               Y=c(Y,rgamma(sum(N[i]),scale=moy[i],shape=1))}
> I=I[-1]; Y=Y[-1]
> basec=data.frame(Y,X1=X1[I],X2=X2[I])

> S=rep(0,n)

> for(i in 1:n){S[i]=sum(Y[I==i])}
> bases=data.frame(S,X1,X2)
Here, the first idea can be to run a Poisson regression on counts, and a Gamma regression on increments,
> reg1=glm(N~X1+X2,data=basen,family=poisson(link="log"))
> reg2=glm(Y~X1+X2,data=basec,family=Gamma(link="log"))
> YP=predict(reg2,newdata=basen,type="response")
> NP=predict(reg1,newdata=basen,type="response")
> PP=YP*NP
Then, we can look for a Tweedie model. First, let us define the density (in order to derive the deviance),
> library(tweedie)
> ftw= function(y,p,mu,psi){
+  if(p==2){f = dgamma(y, 1/psi, 1/(psi*mu))} else
+  if(p==1){f = dpois(y/psi, mu/psi)} else
+  {lambda = mu^(2-p)/psi /(2-p)
+  if(y==0){ f = exp(-lambda)} else
+  { alpha = (2-p)/(p-1)
+     beta = 1 / (psi * (p-1) * mu^(p-1))
+     k = max(10, ceiling(lambda + 7*sqrt(lambda)))
+     f = sum(dpois(1:k,lambda) * dgamma(y,alpha*(1:k),beta))
+  }}
+  return(f)
+  }
, Then we can compute the log-likelihood for different values,
> PUISSANCE=seq(1,2,by=.05)
> E1=rep(NA,length(PUISSANCE))
> for(j in 1:length(PUISSANCE)){
+ puissance=PUISSANCE[j]
+ reg3=glm(S~X1+X2,data=bases,family=tweedie(var.power=puissance, link.power=0) )
+ PT=predict(reg3,newdata=basen,type="response")
+ dev=deviance(reg3)
+ mu=fitted.values(reg3)
+ phi=dev/n
+ K=0
+ for(k in 1:n){
+ K=K+log(ftw(S[k],puissance,mu[k],phi))}
+ E1[j]=K
+ }
The most difficult part is to estimate the power of the Tweedie,
> FT=function(puissance){
+ puissance
+ reg3=glm(S~X1+X2,data=bases,family=tweedie(var.power=puissance, link.power=0) )
+ PT=predict(reg3,newdata=basen,type="response")
+ dev=deviance(reg3)
+ mu=fitted.values(reg3)
+ phi=dev/n
+ K=0
+ for(k in 1:n){
+ K=K+log(ftw(S[k],puissance,mu[k],phi))}
+ return(K)
+ }
> optimize(FT,c(1.01,1.99),maximum=TRUE)
$maximum
[1] 1.639127
$objective
        4
-4331.092

which is here a bit more than 1.6. Actuallly, if we generate several datasets, we have the following distribution for the optimal parameter

(which is quite robust). In fact, if we have a Poisson distributionhttp://perso.univ-rennes1.fr/arthur.charpentier/latex/tweeee01.png, and if http://perso.univ-rennes1.fr/arthur.charpentier/latex/tweeee02.png's are Gamma distributed http://perso.univ-rennes1.fr/arthur.charpentier/latex/tweeee03.png, with http://perso.univ-rennes1.fr/arthur.charpentier/latex/tweeee04.png and http://perso.univ-rennes1.fr/arthur.charpentier/latex/tweeee05.png, then http://perso.univ-rennes1.fr/arthur.charpentier/latex/tweeee06.png defined as the compound Poisson variable has aTweedie distribution with power
http://perso.univ-rennes1.fr/arthur.charpentier/latex/tweeee07.png
Here, in our simulation, we have considered http://perso.univ-rennes1.fr/arthur.charpentier/latex/tweeee08.png=1, so the theoretical power value should have been around 1.5. The same programme with http://perso.univ-rennes1.fr/arthur.charpentier/latex/tweeee08.png=0.3 gives
the following distribution for the power value,

which is not far away from the theoretical one (here 1.77). And if we compare predicted values from the Tweedie, and the product between the predicted value from the Poisson regression, and from the Gamma regression, we obtain similar outputs,
So finally, I do not clearly see the interest of this Tweedie model here, especially if we consider that it might take a lot of time to estimate the power parameter (if it runs....). But we'll see that those models can be extremely interesting in claims reserving....

Friday, October 1 2010

Too large datasets for regression ? What about subsampling....

(almost) recently, a classmate working in an insurance company told me he had too large datasets to run simple regressions (GLM, which involves optimization issues), and that they were thinking of a reward for the one who will write the best R-code (at least the fastest). My first idea was to use subsampling techniques, saying that 10 regressions on 100,000 observations can take less time than a regression on 1,000,000 observations. And perhaps provide also better results...

  • Time to run a regression, as a function of the number of observations
Here, I generate a dataset as follows
http://perso.univ-rennes1.fr/arthur.charpentier/latex/largesamp01.png
and we fit
http://perso.univ-rennes1.fr/arthur.charpentier/latex/largesamp02.png
where http://perso.univ-rennes1.fr/arthur.charpentier/latex/largesamp03.png is a spline function (just to make it as general as possible, since in insurance ratemaking, we include continuous variates that do not influence claims frequency linearly in the score). Yes, there might be also useless variables, including one of them which is strongly correlated with one that has an impact in the regression. The code to generate the dataset is simply
> n=10000
> X1=rexp(n)
> X2=sample(c("A","B","C"),size=n,replace=TRUE)
> X3=runif(n)
> Z=rmnorm(n,c(0,0),matrix(c(1,0.8,.8,1),2,2))
> X4=Z[,1]
> X5=Z[,2]
> X6=X1^2
> E=runif(n)
> lambda=.2*X5-4*dbeta(X3,2,5)+X1+
+1*(X2=="A")-2*(X2=="B")-5*(X2=="C")
> Y=rpois(n,exp(lambda))
> base=data.frame(Y,X1,X2,X3,X4,X5,X6,E)
We would like the study the time it takes to run a regression, as a function of the size (i.e. the number of lines http://perso.univ-rennes1.fr/arthur.charpentier/latex/largesamp04.png) of the dataset. 
> system.time( glm(Y~bs(X1)+X2+X3+X4+
+ X5+X6+offset(log(E)),family=poisson,
+ data=base) )
utilisateur     système      écoulé
0.25        0.00        0.25 
Here, the time I look at is the last one. But so far, it was rather simple, but it is not the best model I can get. Let us use a stepwise (backward) variable selection,
> system.time( step(glm(Y~bs(X1)+X2+X3+
+ X4+X5+X6+offset(log(E)),family=poisson,
+ data=base)) )
Start:  AIC=2882.1
Y ~ bs(X1) + X2 + X3 + X4 + X5 + X6 + offset(log(E))
Step:  AIC=2882.1
Y ~ bs(X1) + X2 + X3 + X4 + X5 + offset(log(E))
Df Deviance    AIC
<none>        2236.0 2882.1
- X5      1   2240.1 2884.2
- X4      1   2244.1 2888.2
- X3      1   4783.2 5427.3
- X2      2   5311.4 5953.5
- bs(X1)  3   6273.7 6913.8
utilisateur     système      écoulé
1.82        0.03        1.86 
Finally, from the first regression, we have points in black (based on 200 simulated datasets), and with a stepwise procedure, we have the points in red.

i.e. it might look linear (proportional), but if it was linear, then on a log-log scale, we should have also straigh lines, with slope 1,

Actually, it looks like a convex function.

The interpretation of that convexity might lead to misinterpretation. On the graph below on the left, on a dataset two times bigger than the previous one (black point) will be less than two times longer to run, while on the right, it  will be more than two timess longer,

Convexity can simply be interpreted as "too large datasets take time, and too small too...". Which is a first step: it should be interesting, in some cases, to run several regressions on smaller datasets....
  • Running 100 regressions on 100 lines, or running 1 regression on 10,000 lines ?
Here, we have datasets with http://perso.univ-rennes1.fr/arthur.charpentier/latex/largesamp04.png=200,000 lines. The questions is how long will it take if we subdived into http://perso.univ-rennes1.fr/arthur.charpentier/latex/largesamp05.png subsamples (of equal size), and run http://perso.univ-rennes1.fr/arthur.charpentier/latex/largesamp05.png regressions ?
> nk=trunc(n/k)rep(1:k,each=nk); nt=nk*k
> base=data.frame(Y[1:nt],X1[1:nt],
+ X2[1:nt],X3[1:nt],X4[1:nt],X5[1:nt],
+ X6[1:nt],E[1:nt],classe)
> system.time( for(j in 1:k){
+  glm(Y~bs(X1)+X2+X3+X4+X5+
+ X6+offset(log(E)),family=poisson
+ ,data=base,subset=classe==j) })
utilisateur     système      écoulé
1.31        0.00        1.31
> system.time( for(j in 1:k){
+      step(glm(Y~bs(X1)+X2+X3+
+ X4+X5+X6+offset(log(E)),family=
+ poisson,data=base,subset=classe==j)) })
Start:  AIC=183.97
Y ~ bs(X1) + X2 + X3 + X4 + X5 + X6 + offset(log(E))
[...]
  Df Deviance    AIC
<none>        117.15 213.04
- X2      2   250.15 342.04
- X3      1   251.00 344.89
- X4      1   420.63 514.53
- bs(X1)  3   626.84 716.74
utilisateur     système      écoulé
11.97        0.03       12.31 
On the graph below, we have the time (y-axis, here on a log scale) it took to run http://perso.univ-rennes1.fr/arthur.charpentier/latex/largesamp05.png regression on samples of size http://perso.univ-rennes1.fr/arthur.charpentier/latex/largesamp06.png, as function of http://perso.univ-rennes1.fr/arthur.charpentier/latex/largesamp05.png (x-axis), including the time it took to run the regression on a dataset of size http://perso.univ-rennes1.fr/arthur.charpentier/latex/largesamp04.png which is the concentration of dots on the left (i.e. http://perso.univ-rennes1.fr/arthur.charpentier/latex/largesamp05.png=1), both on the 6 regressors - in black - and with a strepwise procedure - in red. One has to keep in mind that I did not remove the printing option in the stepwise procedure, so it might be difficult to compare the two clouds (black vs. red). Nevertheless, we clearly see that if we run http://perso.univ-rennes1.fr/arthur.charpentier/latex/largesamp05.png regression on samples of size http://perso.univ-rennes1.fr/arthur.charpentier/latex/largesamp06.png, when http://perso.univ-rennes1.fr/arthur.charpentier/latex/largesamp05.png is not too large, i.e. less than 10 or 15, it is not longer than the regression on http://perso.univ-rennes1.fr/arthur.charpentier/latex/largesamp04.png=200,000 lines.

So here we see that running 100 regressions on 2,000 lines is longer than running 1 regression on 200,000 lines... But maybe we are not comparing things that are actually comparable: what if it takes a bit longer, but we strongely improve the quality of our estimators ?
  • What about the quality of the output ?
Here, we consider only one dataset, with http://perso.univ-rennes1.fr/arthur.charpentier/latex/largesamp04.png=100,000 lines (just to make it run a bit faster). And http://perso.univ-rennes1.fr/arthur.charpentier/latex/largesamp05.png=20 subsets. Recall that the generated dataset is from
http://perso.univ-rennes1.fr/arthur.charpentier/latex/largesamp01.png
and we fit
http://perso.univ-rennes1.fr/arthur.charpentier/latex/largesamp02.png
Here, we plot here http://perso.univ-rennes1.fr/arthur.charpentier/latex/largesamp07.png and a confidence interval, defined as
http://perso.univ-rennes1.fr/arthur.charpentier/latex/largesamp08.png
The lightblue segment is the initial estimator, while the blue one is obtained from the stepwise procedure. The grey area represent the estimation on the overall sample, while the http://perso.univ-rennes1.fr/arthur.charpentier/latex/largesamp05.png segments on the right are the http://perso.univ-rennes1.fr/arthur.charpentier/latex/largesamp05.png estimators (each on samples of size http://perso.univ-rennes1.fr/arthur.charpentier/latex/largesamp06.png).

We can see that we have much more volatility on those http://perso.univ-rennes1.fr/arthur.charpentier/latex/largesamp05.png estimators, but the average (horizontal doted lines) are not so bad... The true value (i.e. the one used to generate the dataset is the dotter black horizontal line).
And if we repeat that on 1,000 simulated dataset, we obtaind the following distribution for http://perso.univ-rennes1.fr/arthur.charpentier/latex/largesamp07.png (blue line), so we have an unbiased estimator of our parameter (the verticular line being here the true value), here including a stepwise procedure,

But if we add the the red curve is the average of the http://perso.univ-rennes1.fr/arthur.charpentier/latex/largesamp09.png the previous one being now the clear blue line in the back, we see that taking average of estimators on subsamples is not bad at all, on the contrary,

and for those who think that the stepwise procedure is a mistake, here is what we get without it,

So what we can see is that running 20 regressions can take (a little) more time (from what we've seen earlier) than running only one on the whole dataset.... but it provides better estimates. So the tradeoff is not that simple, and maybe running severall regressions on huge datasets can be a proper alternative.

Tuesday, December 1 2009

Les modèles Tweedie en actuariat (1)

Rappelons que pour les modèles Tweedie, on suppose ici que la fonvtion variance soit de la forme suivante,
Le cas 0 correspond au cas Gaussien, le cas 1 correspond à la loi de Poisson, et le cas 2 à la loi Gamma. L'idée ici est de considérer les cas intermédiaire, et plus précisément entre 1 et 2. La difficulté est ici de trouver le paramètre puissance. On peut utiliser des méthodes de type maximum de vraisemblance. La "densité" une telle loi serait de la forme suivante
> ftweedie = function(y,p,mu,psi){
+ if(p==2){f = dgamma(y, 1/psi, 1/(psi*mu))} else
+ if(p==1){f = dpois(y/psi, mu/psi)} else
+ {lambda = mu^(2-p)/psi /(2-p)
+ if(y==0){ f = exp(-lambda)} else
+ { alpha = (2-p)/(p-1)
+    beta = 1 / (psi * (p-1) * mu^(p-1))
+    k = max(10, ceiling(lambda + 7*sqrt(lambda)))
+    f = sum(dpois(1:k,lambda) * dgamma(y,alpha*(1:k),beta))
+ }}
+ return(f)
+ }
On peut alors lancer une procédure de type maximum de vraisemblance profilée pour trouver la puissance optimale. On peut utiliser cette fonction pas seulement en tarification, mais aussi en provisionnement. Pour aller un peu plus vite, on peut utiliser la library(statmod), mais pour des aspects numériques, il convient de ne pas avoir de valeurs manquantes dans le triangle,
> source("http://perso.univ-rennes1.fr/arthur.charpentier/bases.R")
> library(statmod)
> an <- 6; ligne = rep(1:an, each=an); colonne = rep(1:an, an)
> passe = (ligne + colonne - 1)<=an; n = sum(passe)
> INC=PAID
> INC[,2:6]=PAID[,2:6]-PAID[,1:5]
> Y = as.vector(INC)
> lig = as.factor(ligne)
> col = as.factor(colonne)
> base=data.frame(Y,lig,col,passe)
 > basen=base[base$passe=="TRUE",]
On utilise alors le code suivant (je reviendrais un jour plus longuement sur les procedures d'affichage de sorties),
> pltweedie <- function(puissqnce){
+ regt = glm(Y~lig+col, tweedie(pow,0),subset=(passe==TRUE))
 
+ reserve = sum(predict(regt,newdata=data.frame(lig,col),type="response")[!passe])
 
+ dev = deviance(regt)
+  phi.hat = dev/n
+  mu = fitted.values(regt)
+  hat.logL = 0
+  for (k in 1:length(y)){
 
+    hat.logL <- hat.logL + log(ftweedie(y[k], pow, mu[k], phi.hat)) }
+  cat("Puissance =", round(pow,3), "phi =", round(phi.hat,2),
+  "Reserve (tot) =", round(reserve), "logL =", round(hat.logL,3),"\n")
+  return(hat.logL)}
Sur le triangle dont on dispose, on peut regarde la logvraisemblance profilée,
> for(pow in c(1,1.25,1.5,1.75,2)){pltweedie(pow)}
Puissance = 1 phi = 1.44 Reserve (tot) = 2427 logL = -Inf
Puissance = 1.25 phi = 0.47 Reserve (tot) = 2427 logL = -95.965
Puissance = 1.5 phi = 0.15 Reserve (tot) = 2428 logL = -99.207
Puissance = 1.75 phi = 0.05 Reserve (tot) = 2434 logL = -102.149
Puissance = 2 phi = 0.01 Reserve (tot) = 2444 logL = -104.157
Il y a eu 21 avis (utilisez warnings() pour les visionner)
mais aussi lancer une routine d'optimisation,
>  optimize(pltweedie, c(1.01,1.99),  tol=1e-4,maximum = TRUE)
Puissance = 1.384 phi = 0.26 Reserve (tot) = 2427 logL = -97.707
Puissance = 1.616 phi = 0.09 Reserve (tot) = 2430 logL = -100.644
Puissance = 1.241 phi = 0.48 Reserve (tot) = 2427 logL = -95.855
Puissance = 1.153 phi = 0.72 Reserve (tot) = 2427 logL = -94.751
Puissance = 1.098 phi = 0.92 Reserve (tot) = 2427 logL = -94.096
Puissance = 1.065 phi = 1.07 Reserve (tot) = 2427 logL = -93.704
Puissance = 1.044 phi = 1.18 Reserve (tot) = 2427 logL = -93.474
Puissance = 1.031 phi = 1.25 Reserve (tot) = 2427 logL = -93.403
Puissance = 1.02 phi = 1.31 Reserve (tot) = 2427 logL = -93.303
Puissance = 1.024 phi = 1.29 Reserve (tot) = 2427 logL = -93.375
Puissance = 1.016 phi = 1.33 Reserve (tot) = 2427 logL = -93.107
Puissance = 1.014 phi = 1.35 Reserve (tot) = 2427 logL = -92.862
Puissance = 1.012 phi = 1.36 Reserve (tot) = 2427 logL = -92.641
Puissance = 1.012 phi = 1.36 Reserve (tot) = 2427 logL = -92.476
Puissance = 1.011 phi = 1.37 Reserve (tot) = 2427 logL = -92.362
Puissance = 1.011 phi = 1.37 Reserve (tot) = 2427 logL = -92.288
Puissance = 1.01 phi = 1.37 Reserve (tot) = 2427 logL = -92.241
Puissance = 1.01 phi = 1.37 Reserve (tot) = 2427 logL = -92.211
Puissance = 1.01 phi = 1.37 Reserve (tot) = 2427 logL = -92.192
Puissance = 1.01 phi = 1.37 Reserve (tot) = 2427 logL = -92.181
Puissance = 1.01 phi = 1.37 Reserve (tot) = 2427 logL = -92.173
Puissance = 1.01 phi = 1.37 Reserve (tot) = 2427 logL = -92.173
$maximum
[1] 1.010050

$objective
[1] -92.1735
Autrement dit, ici, la loi de Poisson semble la mieux adaptée...