Processing math: 100%

Nous utiliserons des données disponibles dans le package ìnsuranceData.

library(insuranceData)
library(VGAM)
data(AutoBi)
AutoBi
ABCDEFGHIJ0123456789
CASENUM
<int>
ATTORNEY
<int>
CLMSEX
<int>
MARITAL
<int>
CLMINSUR
<int>
SEATBELT
<int>
CLMAGE
<int>
LOSS
<dbl>
511NA215034.940
13222112810.892
662122150.330
71111223211.037
9621421300.138
9712121350.309
12011221193.538
13612221344.882
15222221610.874
15521221NA1.351

1340 montants de sinistres auto incluant des dommages corporels, exprimés en milliers de Dollars. L’analyse de la distribution des montants démarre avec des statistiques descriptives, un histogramme et un boxplot

library(tidyverse)
library(ggplot2)
t(AutoBi %>% summarise(
  moyenne = mean(LOSS),
  ecart_type = sd(LOSS),
  minimum = min(LOSS),
  maximum = max(LOSS),
  Q1 = quantile(LOSS, 0.25),
  Q2 = quantile(LOSS, 0.5),
  Q3 = quantile(LOSS, 0.75)
))
##                   [,1]
## moyenne       5.953461
## ecart_type   33.136205
## minimum       0.005000
## maximum    1067.697000
## Q1            0.640000
## Q2            2.331000
## Q3            3.994750
ggplot(data = AutoBi) + geom_histogram(mapping = aes(x= LOSS), binwidth = 10)

ggplot(data = AutoBi) + geom_boxplot(aes(y = LOSS))

Il s’agit d’un cas classique avec une forte occurence de sinsitres de faible intensité et une occurence non négligeable de sinistres de plus forte intensité. Les graphiqueS qunatile-quantile sont très utile pour exhiber la présence de valeurs extrêmes, on compare les quantiles de

xi1nxini=1, i=1,,n et ln(xi/mini=1,,nxi)1nni=1ln(xi/mini=1,,nxi))

data_quantile = data.frame(
   quantile_loss = sapply(seq(0.001,0.999,0.001), function(alpha) quantile(AutoBi$LOSS, alpha)),
   quantile_exp = sapply(seq(0.001,0.999,0.001), function(alpha) qexp(alpha, rate = 1 / mean(AutoBi$LOSS))),
   quantile_log_exp = sapply(seq(0.001,0.999,0.001), function(alpha) qexp(alpha, rate = 1 / mean(log(AutoBi$LOSS / min(AutoBi$LOSS))))),
   quantile_log_loss = sapply(seq(0.001,0.999,0.001), function(alpha) quantile(log(AutoBi$LOSS / min(AutoBi$LOSS)), alpha))
)

ggplot(data = data_quantile, mapping = aes(x  = quantile_loss , y = quantile_exp )) + geom_point()  

ggplot(data = data_quantile, mapping = aes(x  = quantile_log_loss , y = quantile_exp )) + geom_point() 

Calibration des lois classiques

Loi lognormale

Pour calibrer une loi lognormale, Xlognormal(μ,σ) de densité fX(x)=1σx2πexp{(ln(x)μ)22σ2}, x>0. on infère les paramètres d’une loi normale sur les log données.

# Inference des paramètres de la loi lognormale
mu = mean(log(AutoBi$LOSS))
sigma = sd(log(AutoBi$LOSS))

# Quantile-quantile plot pour la loi lognormale
quantile_lnorm = data.frame(
   quantile_loss = sapply(seq(0.001,0.999,0.001), function(alpha) quantile(AutoBi$LOSS, alpha)),
   quantile_lnorm = sapply(seq(0.001,0.999,0.001), function(alpha) qlnorm(alpha, meanlog = mu, sdlog = sigma))
   )
ggplot(data = quantile_lnorm, mapping = aes(x  = quantile_lnorm , y = quantile_loss )) + geom_point()  + geom_abline(intercept = 0, slope = 1)

data.frame('mu'=mu, 'sigma'= sigma)
ABCDEFGHIJ0123456789
mu
<dbl>
sigma
<dbl>
0.55674721.478487

Loi de Weibull

Pour calibrer la loi de Weibull, de densité

fX(x)=(αβ)(xβ)α1exp{(xβ)α}, x>0. on utilise la méthode du maximum de vraisemblance.

# Fonction qui donne l'oppposé de la log vraisemblance
log_like <- function(param){
  -sum(log(dweibull(AutoBi$LOSS, shape = param[1], scale = param[2])))
}
# La fonction nlm qui permet de minimiser la fonction en argument
mle_weib <- nlm(log_like, c(1/5, 5))$estimate

# QQ-plot pour la loi de Weibull
quantile_weibull = data.frame(
   quantile_loss = sapply(seq(0.001,0.999,0.001), function(alpha) quantile(AutoBi$LOSS, alpha)),
   quantile_weib = sapply(seq(0.001,0.999,0.001), function(alpha) qweibull(alpha, shape = mle_weib[1], scale = mle_weib[2]))
   )

ggplot(data = quantile_weibull, mapping = aes(x  = quantile_weib , y = quantile_loss )) + geom_point()  + geom_abline(intercept = 0, slope = 1)

data.frame('alpha' = mle_weib[1], 'beta' = mle_weib[2])
ABCDEFGHIJ0123456789
alpha
<dbl>
beta
<dbl>
0.64928823.597021

Loi Gamma

Pour calibrer la loi gamma XGamma(α,β) de densité

fX(x)=ex/βxα1βαΓ(α), x>0, on peut utiliser la méthode des moments. On résout le système d’équation

{E(X)=αβV(X)=αβ2ˆα=ˆσ2¯X et ˆβ=¯X2ˆσ2,

¯X=1nni=1Xi et ˆσ2=1n1ni=1(Xi¯X)2.

# Inférence des paramètre de la loi gamma
alpha_g <- var(AutoBi$LOSS) / mean(AutoBi$LOSS)
beta_g <-  mean(AutoBi$LOSS)^2 / var(AutoBi$LOSS)

# QQ-plot pour la loi de gamma 
quantile_gamma = data.frame(
   quantile_loss = sapply(seq(0.001,0.999,0.001), function(alpha) quantile(AutoBi$LOSS, alpha)),
   quantile_gamma = sapply(seq(0.001,0.999,0.001), function(alpha) qgamma(alpha, shape = alpha_g, scale = beta_g))
   )

ggplot(data = quantile_gamma, mapping = aes(x  = quantile_gamma , y = quantile_loss )) + geom_point()  + geom_abline(intercept = 0, slope = 1)

data.frame('alpha' = alpha_g, 'beta_g'=beta_g)
ABCDEFGHIJ0123456789
alpha
<dbl>
beta_g
<dbl>
184.43190.03228

Ce résultat plutôt décevant pourrait être mis sur le dos de la méthode d’inférence car utiliser la méthode des moments si la distribution des pertes est à queue lourde n’est pas forcément pertinent (si par exemple les pertes extrêmes suivent une loi de Pareto).

Comparaison des modèles

Visuellement, on peut superposer les densités des lois estimées à l’histogramme des données.

# Histogramme des données et des densités paramétriques
lnorm_pdf <- function(x) dlnorm(x, meanlog = mu, sdlog = sigma)
gamma_pdf <- function(x) dgamma(x, shape = alpha_g , scale = beta_g)
weibull_pdf <- function(x) dweibull(x, shape = mle_weib[1], scale = mle_weib[2])

hist_loss_AutoBi <- ggplot(data = AutoBi %>% filter(LOSS < 25)) + geom_histogram(mapping = aes(x = LOSS, y = ..density..), bg = 'lightgrey', binwidth = 0.5) + xlab('Montants de sinsitres') + ylab('Densité') + stat_function(fun = lnorm_pdf, linetype="solid") +
  stat_function(fun = gamma_pdf, linetype="dotted") +
  stat_function(fun = weibull_pdf, linetype="dashed") +
  theme_bw() +
  theme(axis.title.x = element_text(size = 12),
        axis.title.y = element_text(size = 12),
        axis.text = element_text(size = 12)
        )

hist_loss_AutoBi 

L’histogramme montre bien le manque d’adéquation des modèles par rapport aux données. L’estimations des paramètres est trop influencé par l’occurence de valeurs extrêmes. On peut tout de même calculer le Bayesian Information criterium

BIC=2ln[p(X|θ)]+kln(n),k désigne le nombre de paramètres

data.frame(
   Modele = c('lognormal', 'gamma', 'Weibull'),
   BIC = c(-2 * sum(log(lnorm_pdf(AutoBi$LOSS))) + 2 * log(length( AutoBi$LOSS)),
-2 * sum(log(gamma_pdf(AutoBi$LOSS))) + 2 * log(length( AutoBi$LOSS)),
-2 * sum(log(weibull_pdf(AutoBi$LOSS))) + 2 * log(length( AutoBi$LOSS))
)
)
ABCDEFGHIJ0123456789
Modele
<chr>
BIC
<dbl>
lognormal6356.169
gammaInf
Weibull6602.629

Le modèle lognormale semble le plus adapté, on peut tester statistiquement l’adéquation en utilisant par exemple le test de Cramer-von Mises basé sur le critère suivant

C-vM=(Fn(x)FX(x))2dFX(x)Fn(x)=1nni=1IXix désigne la fonction de répartition empirique et FX(x):=FX(x;θ) est la fonction de répartition du modèle théorique. La statistique de test est estimé via

T=n×C-vMn=112n+ni=1(2i12nˆFX(Xi:n))2,Xi:n, i=1,,n sont les statistiques d’ordre et ˆFX(x):=FX(x;ˆθ). Le test rejette l’hypothèse

(H0): XFX Lorsque T dépasse la valeur critique de la statistique de test correspondant au quantile à 95% de la distribution de T lorsque H0 est vérifiée. Pour calculer la valeur critique, on utilise un algorithme de bootstrap paramétrique.

Pour k allant de 1 à B=10,000,

  1. Simuler un échantillon X(k)FX
  2. Estimer ˆθ(k) sur la base de X(k)
  3. Calcul de la statistique de test T(k)

Si T>quantile(T(k),k=1,,B95%).

X <- AutoBi$LOSS
# Fonction pour calculer la statistique de test
compute_CvM <- function(X){
  n <- length(X)
  mu = mean(log(X))
  sigma = sd(log(X))
  X_sort <- sort(X)
  CvM = 1/ 12 / n + sum((sapply(1:n, function(i) (2*i-1) / 2 / n - plnorm(X_sort[i], meanlog = mu, sdlog = sigma)))^2)
  return(CvM)
  }

B <- 1000

# Estimation des paramètres
n <- length(X)
mu = mean(log(X))
sigma = sd(log(X))
# Calcul de la statistiques 
T <- compute_CvM(X)
T_boot <- {}
for(k in 1:B){
  X_k <- rlnorm(n, meanlog = mu, sdlog = sigma)
  T_boot[k] <- compute_CvM(X_k)
}

data.frame(Test_stat = T, critical_value = quantile(T_boot, 0.95), Reject =  T>quantile(T_boot, 0.95))
ABCDEFGHIJ0123456789
 
 
Test_stat
<dbl>
critical_value
<dbl>
Reject
<lgl>
95%3.0193110.1278803TRUE

Le test rejette clairement l’hypothèse nulle.

Analyse de valeurs extrêmes

Une technique classique consiste à grapher l’estimateur de Hill en faisant varier le seuil pour la loi de Pareto avec

ˆαj=njni=jln(xni:n/xnj:n).

# On trie par ordre croissant les observations 
X_sort <- sort(X)
n <- length(X)


alpha_hat = sapply(1:(n-1), function(j) (n-j) / sum(sapply((j+1):n, function(i) log(X_sort[i] / X_sort[j]))))
plot(rev(alpha_hat))

Le seuil et le paramètre semble être autour de

j <- n - 150
data.frame(seuil = X_sort[j], alpha = (n-j) / sum(sapply((j+1):n, function(i) log(X_sort[i] / X_sort[j]))))
ABCDEFGHIJ0123456789
seuil
<dbl>
alpha
<dbl>
7.3911.081389

L’analyse menée ici est très approximative, il existe bien d’autres outils d’aide au diagnostique en ce qui concerne les valeurs extrêmes.

Calibration du modèle composite

La méthode précédente ne suppose pas une distribution pour le ventre et la queue de distribution. Le choix de telle ou telle loi paramétrique pour le ventre de la distribution va influencer la valeur estimer du seuil et du paramètre de la loi de Pareto sous jacente. Nous allons calibrer un modèle composite Weibull-Pareto de densité

f(x)={rf1(x)F1(θ) pour 0xθ,(1r)f2(x)1F2(θ) pour x>θ. avec

f1(x)=kβ(xβ)k1e(x/β)k, x>0. et

f2(x)={αθαxα+1, xθ,0, x<θ, La continuité et dérivabilité en θ impose que

r=αθ[1exp(k+αk)]αθ+kθexp(k+αk) et β=(kk+α)1/kθ Pour inférer les paramètres k,α et θ, nous utilisons le maximum de vraisemblance

X <- AutoBi$LOSS
n <- length(X)
# Densité du modèle composite
pdf_weibull_pareto <- function(x, alpha, theta, k){
  # The scale parameter of the Weibull and the mixing parameter are fixed to ensure smoothness of the resulting pdf
  lam <- (k / (k + alpha))^(1 / k) * theta
  r <- VGAM::dpareto(theta, scale = theta, shape = alpha) * pweibull(theta, shape = k, scale = lam) /
    (VGAM::dpareto(theta, scale = theta, shape = alpha) * pweibull(theta, shape = k, scale = lam) +
       dweibull(theta, shape = k, scale = lam))

  ifelse(x < theta, r * dweibull(x, shape = k, scale = lam) / pweibull(theta, shape = k, scale = lam),
         (1 - r) * VGAM::dpareto(x, shape = alpha, scale = theta))
}


# Fonction qui donne l'oppposé de la log vraisemblance
log_like <- function(param){
  -sum(log(pdf_weibull_pareto(X, param[1], param[2], param[3])))
}

# La fonction nlm qui permet de minimiser la fonction en argument
mle_weib_pareto <- optim(c(1, 3, 1.2), log_like)$par
#estimate

data.frame(k = mle_weib_pareto[1],
           beta = (mle_weib_pareto[1] / (mle_weib_pareto[3] + mle_weib_pareto[1]))^(1 / mle_weib_pareto[1]) * mle_weib_pareto[2],
           r = mle_weib_pareto[3] / mle_weib_pareto[2] * (1-exp(-(mle_weib_pareto[3] + mle_weib_pareto[1]) / mle_weib_pareto[1])) / (mle_weib_pareto[3] / mle_weib_pareto[2] + mle_weib_pareto[1] / mle_weib_pareto[2] * exp(-(mle_weib_pareto[3] + mle_weib_pareto[1]) / mle_weib_pareto[1])),
           theta = mle_weib_pareto[2],
           alpha = mle_weib_pareto[3],
           BIC = -2 * sum(log(pdf_weibull_pareto(X, mle_weib_pareto[1], mle_weib_pareto[2], mle_weib_pareto[3]))) + 3 * log(n)
           )
ABCDEFGHIJ0123456789
k
<dbl>
beta
<dbl>
r
<dbl>
theta
<dbl>
alpha
<dbl>
BIC
<dbl>
1.2784543.9103580.67731916.0988160.97819446262.386

Nous gagnons quelques points de BIC par rapport aux modèles classiques.

Calibration d’une loi mélange de Erlang

Nous allons tenter d’améliorer l’adéquation du modèle aux données en utilisant la loi Erlang mélange de densité

fX(x)=mi=1πiex/βxzi1βzi(zi1)!.

Il s’agit d’un modèle de mélange avec un variable aléatoire latente Zπ. On simplifie ici en considérat des mélange de Erlang avec de plus en plus de composante tel que zi=i, i=1,,m. On rappelle qu’à partir de valeurs initiale (ou précédente), l’algorithme EM met à jour la valeur des paramètres de la façon suivante

π(1)i=nk=1P(Zi=zi|xk;θ(0))n et β(1)=1nnk=1xkmi=1π(1)izi, avec

P(Z=zi|X;θ(0))=π(0)iex/β(0)xzi1(β(0))zi(zi1)!mi=1π(0)iex/β(0)xzi1(β(0))zi(zi1)!.

# Densité d'un mélange de Erlang
dMErl <- function(x, pi, beta){
  nb_erlang <- length(pi)
  sum(sapply(1:nb_erlang, function(i) pi[i] * dgamma(x, shape = i, scale = beta)))
}

# Fonction qui calcule la loi de proba de Z sachant un point x et la valeur courante des paramètres
p_z <- function(x, pi, beta, nb_erlang){
  sapply(1:nb_erlang, function(i) pi[i] * dgamma(x, shape = i, scale = beta)) / dMErl(x, pi, beta)
}

# Fonction pour inférer les paramètre d'un mélange de Erlang avec un nombre donnée de composantes
infer_MErl<- function(X, pi_0, beta_0, nb_erlang, eps_pi, eps_beta){
  n <- length(X)
  pi <- list()
  beta <- {}
  pi[[1]] <- pi_0
  beta[1] <- beta_0
  pi[[2]] <- apply(sapply(X, function(x) p_z(x, pi[[1]], beta[1], nb_erlang)), 1, sum) / n
  beta[2] <- mean(X) / (pi[[2]] %*% (1:nb_erlang))
  k <- 2
  while( sum(abs(pi[[k]]-pi[[k-1]])) > eps_pi & abs(beta[k] - beta[k-1]) > eps_beta){
    k <- k + 1
    pi[[k]] <- apply(sapply(X, function(x) p_z(x, pi[[k-1]], beta[k-1], nb_erlang)), 1, sum) / n
    beta[k] <- mean(X) / (pi[[k]] %*% (1:nb_erlang))
  }
  return(list('pi' = pi[[k]], 'beta' = beta[k]))
}

# Initialisation des paramètres
beta_0 <- 10 
eps_pi <- 0.05 
eps_beta <- 0.05
# Nombre de 
nb_max_erlang <- 20
params_MErl <- list()
BIC <- {}
for(nb_erlang in 2:nb_max_erlang){
  params_MErl[[nb_erlang - 1]] <- infer_MErl(X, rep(1/nb_erlang, nb_erlang), beta_0 = 10, nb_erlang, 
                                             eps_pi, eps_beta)
  BIC[nb_erlang-1] <- - 2 * sum(log(sapply(X, function(x) dMErl(x, pi = params_MErl[[nb_erlang - 1]]$pi, params_MErl[[nb_erlang - 1]]$beta)))) + length(params_MErl[[nb_erlang - 1]]$pi) * log(n)
}

plot(BIC)

params_MErl[[nb_max_erlang-1]]
## $pi
##  [1] 0.8700524425 0.0823127111 0.0117333549 0.0035322990 0.0022758300
##  [6] 0.0021349978 0.0021850514 0.0021657738 0.0020075964 0.0017432004
## [11] 0.0014494841 0.0011960970 0.0010181275 0.0009171549 0.0008784476
## [16] 0.0008913068 0.0009724391 0.0012253203 0.0021832110 0.0091251547
## 
## $beta
## [1] 3.909805

Le résultat est assez décevant, on constate bien une diminution du BIC au fur et à mesure qu’on ajoute des composantes au mélange.