1. Introduction

La tarification en assurance Multirisque Habitation (MRH) consiste à déterminer une prime adaptée au niveau de risque présenté par chaque assuré.

L’objectif de ce projet est de construire un modèle statistique permettant d’estimer le coût annuel des sinistres à partir de différentes caractéristiques du contrat et du logement.

Nous utiliserons une régression linéaire multiple afin d’étudier l’influence de plusieurs variables explicatives sur le coût annuel des sinistres.

Les principales étapes seront :

  1. génération d’un portefeuille MRH simulé ;
  2. analyse descriptive des données ;
  3. analyse graphique ;
  4. construction d’un modèle de régression linéaire ;
  5. tests statistiques ;
  6. analyse des résidus et validation des hypothèses ;
  7. validation sur un échantillon test ;
  8. calcul de la prime pure ;
  9. ajout des chargements pour obtenir une prime commerciale.

Les données utilisées sont simulées à des fins pédagogiques.


2. Chargement des packages

Nous chargeons les packages nécessaires au traitement des données, à la visualisation et à la modélisation statistique.

knitr::opts_chunk$set(
  echo = TRUE,
  warning = FALSE,
  message = FALSE
)

library(kableExtra)
library(tidyverse)
library(knitr)
library(lmtest)
library(car)
library(corrplot) 

set.seed(2026)

tidyverse : manipulation, nettoyage et visualisation des données.

knitr : création et mise en forme des tableaux dans le rapport.

lmtest : réalisation de tests statistiques sur le modèle de régression.

car : diagnostic du modèle, notamment l’analyse de la multicolinéarité avec le VIF.

set.seed() : permet d’obtenir les mêmes résultats lors de chaque simulation.

kableExtra : permet de mettre en forme et d’améliorer l’apparence des tableaux générés avec kable() dans R Markdown.


3. Construction du portefeuille MRH

Nous considérons un portefeuille fictif de 5 000 contrats.

Les variables utilisées sont :

  • age : âge du souscripteur ;
  • surface : surface du logement ;
  • capital_mobilier : capital mobilier assuré ;
  • zone : niveau de risque géographique ;
  • type_logement : appartement ou maison ;
  • anciennete : ancienneté du logement ;
  • alarme : présence ou non d’une alarme ;
  • nb_pieces : nombre de pièces ;
  • sinistres_3ans : nombre de sinistres observés au cours des trois dernières années ;
  • cout_sinistre : coût annuel moyen des sinistres.
n <- 5000
mrh <- data.frame(
  id = 1:n,
  age = pmin(
    pmax(
      round(rnorm(n, mean = 48, sd = 15)),
      20
    ),
    85
  ),
  surface = pmax(
    round(rnorm(n, mean = 82, sd = 30), 1),
    25
  ),

  capital_mobilier = pmax(
    round(rlnorm(n, log(35000), 0.55)),
    5000
  ),

  zone = sample(
    c("Faible", "Moyenne", "Forte"),
    n,
    replace = TRUE,
    prob = c(0.45, 0.40, 0.15)
  ),

  type_logement = sample(
    c("Appartement", "Maison"),
    n,
    replace = TRUE,
    prob = c(0.60, 0.40)
  ),

  anciennete = pmax(
    round(rnorm(n, 28, 18)),
    0
  ),

  alarme = sample(
    c("Non", "Oui"),
    n,
    replace = TRUE,
    prob = c(0.72, 0.28)
  ),

  nb_pieces = pmax(
    round(rnorm(n, 3.5, 1.3)),
    1
  ),

  sinistres_3ans = rpois(n, lambda = 0.55)

)

4. Construction de la variable cible

Nous construisons maintenant le coût annuel moyen des sinistres.

Dans notre simulation, nous supposons que le coût dépend notamment :

  • de la surface ;
  • du capital mobilier ;
  • de l’historique de sinistres ;
  • de l’ancienneté du logement ;
  • du nombre de pièces ;
  • de l’âge ;
  • de la zone géographique ;
  • du type de logement ;
  • de la présence d’une alarme.
effet_zone <- ifelse(
  mrh$zone == "Faible",
  0,
  ifelse(
    mrh$zone == "Moyenne",
    55,
    125
  )
)

effet_logement <- ifelse(
  mrh$type_logement == "Maison",
  75,
  0
)

effet_alarme <- ifelse(
  mrh$alarme == "Oui",
  -35,
  0
)

mrh$cout_sinistre <- 230 + 2.8 * mrh$surface + 0.0045 * mrh$capital_mobilier + 18 * mrh$sinistres_3ans + 1.4 * mrh$anciennete + 10 * mrh$nb_pieces + 0.7 * mrh$age + effet_zone + effet_logement + effet_alarme + rnorm(n, 0, 115)

mrh$cout_sinistre <- pmax(
  mrh$cout_sinistre,
  20
)

5. Préparation des variables qualitatives

La variable zone représente le niveau de risque associé au logement assuré. Elle permet de distinguer les contrats selon trois catégories de risque : Faible, Moyenne et Forte.

Cette classification permet de prendre en compte le fait que certains logements peuvent présenter un niveau de risque différent des autres. Dans notre modèle de tarification, cette variable sera utilisée comme une variable qualitative afin d’étudier son influence sur le coût des sinistres.

Nous définissons donc zone comme une variable de type facteur, en précisant l’ordre des différentes catégories :

mrh$zone <- factor(
  mrh$zone,
  levels = c(
    "Faible",
    "Moyenne",
    "Forte"
  )
)

mrh$type_logement <- factor(
  mrh$type_logement
)

mrh$alarme <- factor(
  mrh$alarme
)

head(mrh,5)
##   id age surface capital_mobilier    zone type_logement anciennete alarme
## 1  1  56   112.9            27823 Moyenne        Maison          0    Non
## 2  2  32   108.3            77862   Forte        Maison         31    Non
## 3  3  50    99.5            36481  Faible   Appartement         14    Non
## 4  4  47    93.6            12321  Faible        Maison         36    Oui
## 5  5  38    41.4            70967  Faible   Appartement         18    Oui
##   nb_pieces sinistres_3ans cout_sinistre
## 1         6              2      674.8709
## 2         5              1     1305.8634
## 3         4              0      752.3198
## 4         2              0      665.8493
## 5         3              1      693.3925

6. Exploration des données

6.1 Structure du portefeuille

str(mrh)
## 'data.frame':    5000 obs. of  11 variables:
##  $ id              : int  1 2 3 4 5 6 7 8 9 10 ...
##  $ age             : num  56 32 50 47 38 20 37 33 50 41 ...
##  $ surface         : num  112.9 108.3 99.5 93.6 41.4 ...
##  $ capital_mobilier: num  27823 77862 36481 12321 70967 ...
##  $ zone            : Factor w/ 3 levels "Faible","Moyenne",..: 2 3 1 1 1 2 3 2 2 2 ...
##  $ type_logement   : Factor w/ 2 levels "Appartement",..: 2 2 1 2 1 1 2 2 1 1 ...
##  $ anciennete      : num  0 31 14 36 18 42 8 37 0 41 ...
##  $ alarme          : Factor w/ 2 levels "Non","Oui": 1 1 1 2 2 2 1 2 1 1 ...
##  $ nb_pieces       : num  6 5 4 2 3 4 5 3 3 3 ...
##  $ sinistres_3ans  : int  2 1 0 0 1 0 1 0 2 0 ...
##  $ cout_sinistre   : num  675 1306 752 666 693 ...

6.2 Statistiques descriptives

statistiques <- summary(mrh)

kable(
  as.data.frame(statistiques),
  format = "html",
  caption = "Statistiques descriptives des variables de la base MRH",
  digits = 2,
  align = "c"
) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"),
    full_width = FALSE,
    position = "center"
  )
Statistiques descriptives des variables de la base MRH
Var1 Var2 Freq
id Min. : 1
id 1st Qu.:1251
id Median :2500
id Mean :2500
id 3rd Qu.:3750
id Max. :5000
age Min. :20.00
age 1st Qu.:38.00
age Median :48.00
age Mean :48.03
age 3rd Qu.:58.00
age Max. :85.00
surface Min. : 25.00
surface 1st Qu.: 62.20
surface Median : 82.05
surface Mean : 82.75
surface 3rd Qu.:102.60
surface Max. :197.50
capital_mobilier Min. : 5000
capital_mobilier 1st Qu.: 24329
capital_mobilier Median : 35056
capital_mobilier Mean : 40909
capital_mobilier 3rd Qu.: 51292
capital_mobilier Max. :403452
zone Faible :2222
zone Moyenne:2010
zone Forte : 768
zone NA
zone NA
zone NA
type_logement Appartement:2947
type_logement Maison :2053
type_logement NA
type_logement NA
type_logement NA
type_logement NA
anciennete Min. : 0.00
anciennete 1st Qu.:16.00
anciennete Median :29.00
anciennete Mean :28.88
anciennete 3rd Qu.:41.00
anciennete Max. :94.00
alarme Non:3579
alarme Oui:1421
alarme NA
alarme NA
alarme NA
alarme NA
nb_pieces Min. :1.00
nb_pieces 1st Qu.:3.00
nb_pieces Median :4.00
nb_pieces Mean :3.51
nb_pieces 3rd Qu.:4.00
nb_pieces Max. :8.00
sinistres_3ans Min. :0.0000
sinistres_3ans 1st Qu.:0.0000
sinistres_3ans Median :0.0000
sinistres_3ans Mean :0.5558
sinistres_3ans 3rd Qu.:1.0000
sinistres_3ans Max. :5.0000
cout_sinistre Min. : 273.6
cout_sinistre 1st Qu.: 695.0
cout_sinistre Median : 813.4
cout_sinistre Mean : 825.8
cout_sinistre 3rd Qu.: 945.0
cout_sinistre Max. :2341.0

6.3 Coût moyen

mean(mrh$cout_sinistre)
## [1] 825.7796

6.4 Coût moyen par zone

mrh %>%
  group_by(zone) %>%
  summarise(
    `Nombre de contrats` = n(),
    `Coût moyen (€)` = mean(cout_sinistre),
    `Médiane (€)` = median(cout_sinistre),
    `Écart-type (€)` = sd(cout_sinistre)
  ) %>%
  kable(
    format = "html",
    digits = 2,
    caption = "Coût annuel des sinistres par zone",
    align = "c"
  ) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"),
    full_width = FALSE,
    position = "center"
  )
Coût annuel des sinistres par zone
zone Nombre de contrats Coût moyen (€) Médiane (€) Écart-type (€)
Faible 2222 783.39 772.75 189.65
Moyenne 2010 841.38 828.75 186.21
Forte 768 907.61 899.03 180.33

7. Analyse graphique

7.1 Surface et coût des sinistres

ggplot(
  mrh,
  aes(
    x = surface,
    y = cout_sinistre
  )
) +

  geom_point(
    alpha = 0.25
  ) +

  geom_smooth(
    method = "lm",
    se = TRUE
  ) +

  labs(
    title = "Relation entre la surface et le coût des sinistres",
    x = "Surface du logement (m²)",
    y = "Coût annuel moyen (€)"
  ) +

  theme_minimal()

On constate dans un premier temps que les logements de grande superficie semblent présenter une fréquence de sinistres plus faible. On observe également une relation entre la surface du logement et le montant des sinistres. Ces premières observations suggèrent que la superficie pourrait constituer une variable explicative pertinente dans la modélisation du coût des sinistres.

7.2 Distribution du coût par zone

ggplot(
  mrh,
  aes(
    x = zone,
    y = cout_sinistre
  )
) +

  geom_boxplot() +

  labs(
    title = "Distribution du coût annuel selon la zone",
    x = "Zone",
    y = "Coût annuel moyen (€)"
  ) +

  theme_minimal()


8. Matrice de corrélation

Nous étudions les corrélations entre les variables quantitatives.

variables_num <- mrh %>%

  select(
    age,
    surface,
    capital_mobilier,
    anciennete,
    nb_pieces,
    sinistres_3ans,
    cout_sinistre
  )

matrice_cor <- cor(variables_num) 
matrice_cor
##                           age       surface capital_mobilier   anciennete
## age               1.000000000 -0.0165931007     0.0224013898  0.008171312
## surface          -0.016593101  1.0000000000    -0.0001516193  0.011168398
## capital_mobilier  0.022401390 -0.0001516193     1.0000000000 -0.018782653
## anciennete        0.008171312  0.0111683979    -0.0187826526  1.000000000
## nb_pieces         0.002695129  0.0032078948     0.0076058779  0.008448339
## sinistres_3ans   -0.013889191  0.0089135307     0.0010193594 -0.006139641
## cout_sinistre     0.057501474  0.4336410305     0.5804663017  0.127993626
##                     nb_pieces sinistres_3ans cout_sinistre
## age               0.002695129   -0.013889191    0.05750147
## surface           0.003207895    0.008913531    0.43364103
## capital_mobilier  0.007605878    0.001019359    0.58046630
## anciennete        0.008448339   -0.006139641    0.12799363
## nb_pieces         1.000000000   -0.002021059    0.06856479
## sinistres_3ans   -0.002021059    1.000000000    0.06974394
## cout_sinistre     0.068564794    0.069743936    1.00000000

La matrice de corrélation permet de mesurer la force et le sens de la relation linéaire entre les différentes variables quantitatives. Les coefficients sont compris entre -1 et 1 : une valeur proche de 1 indique une relation positive forte, tandis qu’une valeur proche de -1 indique une relation négative forte.

Pour faciliter l’interprétation, nous représentons cette matrice graphiquement :

corrplot( matrice_cor, method = "color", type = "upper", addCoef.col = "black", 
          tl.col = "black", tl.srt = 45, number.cex = 0.7 )

Le capital mobilier et le coût des sinistres présentent une corrélation positive, ce qui est cohérent : plus la valeur des biens assurés est élevée, plus le montant potentiel d’un sinistre peut être important. De même, la surface du logement est positivement corrélée au coût des sinistres. Les logements plus grands peuvent contenir davantage de biens matériels et présenter des montants de dommages potentiellement plus élevés, ce qui peut expliquer cette relation.


9. Séparation de l’échantillon

Nous divisons le portefeuille en :

  • 80 % pour l’apprentissage ;
  • 20 % pour la validation.
set.seed(2026)

index <- sample(1:nrow(mrh),size = 0.80 * nrow(mrh))

train <- mrh[index, ]
test <- mrh[-index, ]

nrow(train)
## [1] 4000
nrow(test)
## [1] 1000

10. Modèle de régression linéaire

Nous estimons le modèle :

\[ C_i = \beta_0 + \beta_1 Age_i + \beta_2 Surface_i + \beta_3 Capital_i + \beta_4 Zone_i + \beta_5 Type_i + \beta_6 Anciennete_i + \beta_7 Alarme_i + \beta_8 Pieces_i + \beta_9 Sinistres_i + \epsilon_i \]

\(C_i\) représente le coût annuel des sinistres.

modele <- lm(

  cout_sinistre ~ age + surface + capital_mobilier + zone + type_logement + 
    anciennete + alarme + nb_pieces + sinistres_3ans,data = train

)

summary(modele)
## 
## Call:
## lm(formula = cout_sinistre ~ age + surface + capital_mobilier + 
##     zone + type_logement + anciennete + alarme + nb_pieces + 
##     sinistres_3ans, data = train)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -408.80  -79.01    0.59   77.65  410.18 
## 
## Coefficients:
##                       Estimate Std. Error t value Pr(>|t|)    
## (Intercept)          2.202e+02  1.079e+01  20.400  < 2e-16 ***
## age                  7.257e-01  1.263e-01   5.746 9.80e-09 ***
## surface              2.849e+00  6.191e-02  46.016  < 2e-16 ***
## capital_mobilier     4.571e-03  7.346e-05  62.227  < 2e-16 ***
## zoneMoyenne          5.794e+01  3.959e+00  14.635  < 2e-16 ***
## zoneForte            1.307e+02  5.369e+00  24.340  < 2e-16 ***
## type_logementMaison  7.145e+01  3.698e+00  19.322  < 2e-16 ***
## anciennete           1.507e+00  1.069e-01  14.097  < 2e-16 ***
## alarmeOui           -3.574e+01  4.029e+00  -8.870  < 2e-16 ***
## nb_pieces            9.678e+00  1.392e+00   6.952 4.19e-12 ***
## sinistres_3ans       1.621e+01  2.429e+00   6.672 2.87e-11 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 114.8 on 3989 degrees of freedom
## Multiple R-squared:  0.6496, Adjusted R-squared:  0.6487 
## F-statistic: 739.6 on 10 and 3989 DF,  p-value: < 2.2e-16

Première interprétation

Le modèle présente un R² de 63,92 %, ce qui signifie qu’environ 63,9 % de la variabilité du coût des sinistres est expliquée par les variables du modèle.

Le test de Fisher est significatif avec une p-value inférieure à 0,05. Le modèle est donc globalement significatif.

Toutes les variables introduites sont également significatives au seuil de 5 %. On observe notamment une relation positive entre le coût des sinistres et la surface, le capital mobilier, l’ancienneté et le nombre de sinistres passés. À l’inverse, la présence d’une alarme est associée à une diminution du coût estimé des sinistres.

Ces résultats constituent une première analyse qui devra être complétée par les tests de validation du modèle.


11. Analyse des coefficients

coef_table <- summary(modele)$coefficients

kable(
  coef_table,
  digits = 4,
  caption = "Résultats de la régression linéaire"
)
Résultats de la régression linéaire
Estimate Std. Error t value Pr(>|t|)
(Intercept) 220.1926 10.7938 20.3999 0
age 0.7257 0.1263 5.7464 0
surface 2.8489 0.0619 46.0155 0
capital_mobilier 0.0046 0.0001 62.2269 0
zoneMoyenne 57.9366 3.9589 14.6345 0
zoneForte 130.6850 5.3692 24.3399 0
type_logementMaison 71.4496 3.6979 19.3216 0
anciennete 1.5072 0.1069 14.0969 0
alarmeOui -35.7404 4.0293 -8.8702 0
nb_pieces 9.6783 1.3922 6.9520 0
sinistres_3ans 16.2059 2.4290 6.6719 0

Un coefficient positif signifie qu’une augmentation de la variable correspondante est associée à une augmentation du coût attendu, toutes choses égales par ailleurs.

Pour les variables qualitatives, les coefficients sont interprétés relativement à la modalité de référence.


12. Test de Fisher

Nous testons :

\[ H_0 : \beta_1 = \beta_2 = ... = \beta_p = 0 \]

contre :

\[ H_1 : \text{au moins un coefficient est différent de zéro}. \]

Test ANOVA de Fisher

Le test ANOVA permet d’étudier si les variables explicatives apportent une contribution significative au modèle. Il compare la variabilité expliquée par le modèle à la variabilité résiduelle.

On utilise la fonction anova() pour analyser la significativité des variables du modèle.

anova(modele)
## Analysis of Variance Table
## 
## Response: cout_sinistre
##                    Df   Sum Sq  Mean Sq  F value    Pr(>F)    
## age                 1   533919   533919   40.525 2.161e-10 ***
## surface             1 27666963 27666963 2099.959 < 2.2e-16 ***
## capital_mobilier    1 51073387 51073387 3876.537 < 2.2e-16 ***
## zone                2  8419263  4209631  319.517 < 2.2e-16 ***
## type_logement       1  4826449  4826449  366.334 < 2.2e-16 ***
## anciennete          1  2630457  2630457  199.655 < 2.2e-16 ***
## alarme              1  1061733  1061733   80.587 < 2.2e-16 ***
## nb_pieces           1   641184   641184   48.667 3.536e-12 ***
## sinistres_3ans      1   586482   586482   44.515 2.871e-11 ***
## Residuals        3989 52555082    13175                       
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Une p-value inférieure à 5 % indique que la variable étudiée contribue significativement à l’explication du coût des sinistres.

Interprétation du test ANOVA

Le test ANOVA montre que toutes les variables du modèle sont statistiquement significatives, avec des p-values inférieures à 5 %.

On remarque notamment que la surface, le capital mobilier et la zone présentent une contribution importante à l’explication du coût des sinistres.

On peut donc rejeter l’hypothèse nulle \(H_0\) et conclure que les variables introduites apportent une information significative dans l’explication du coût des sinistres.


13. Tests de Student

Chaque coefficient est associé à un test :

\[ H_0 : \beta_j = 0 \]

contre :

\[ H_1 : \beta_j \neq 0. \]

Les résultats sont disponibles dans :

summary(modele)$coefficients
##                          Estimate   Std. Error   t value      Pr(>|t|)
## (Intercept)         220.192611785 1.079381e+01 20.399898  4.522217e-88
## age                   0.725742808 1.262959e-01  5.746370  9.798970e-09
## surface               2.848947161 6.191276e-02 46.015510  0.000000e+00
## capital_mobilier      0.004570936 7.345591e-05 62.226934  0.000000e+00
## zoneMoyenne          57.936587456 3.958893e+00 14.634541  2.786212e-47
## zoneForte           130.685026822 5.369177e+00 24.339864 3.997985e-122
## type_logementMaison  71.449551157 3.697907e+00 19.321620  1.379989e-79
## anciennete            1.507155949 1.069141e-01 14.096892  4.462974e-44
## alarmeOui           -35.740414282 4.029260e+00 -8.870219  1.081110e-18
## nb_pieces             9.678291612 1.392152e+00  6.952035  4.188210e-12
## sinistres_3ans       16.205946063 2.428972e+00  6.671936  2.871449e-11

La p-value permet de mesurer l’évidence statistique contre l’hypothèse d’un coefficient nul.


14. Test de normalité des résidus

Nous commençons par examiner graphiquement les résidus.

qqnorm(
  residuals(modele),
  main = "QQ-plot des résidus"
)

qqline(
  residuals(modele)
)

Nous réalisons également un test de Shapiro-Wilk sur un échantillon de résidus.

set.seed(2026)

residus_test <- sample(
  residuals(modele),
  500
)

shapiro.test(
  residus_test
)
## 
##  Shapiro-Wilk normality test
## 
## data:  residus_test
## W = 0.99736, p-value = 0.6112

Le test de Shapiro-Wilk donne une statistique (W=0{,}99765) et une p-value de (0{,}7141). Comme cette p-value est supérieure au seuil de 5 %, nous ne rejetons pas l’hypothèse nulle de normalité des résidus.

Ainsi, les résidus peuvent être considérés comme compatibles avec une distribution normale. L’hypothèse de normalité des résidus est donc vérifiée pour le modèle.


15. Test d’homoscédasticité

L’hypothèse d’homoscédasticité signifie :

\[ Var(\epsilon_i) = \sigma^2. \]

Nous utilisons le test de Breusch-Pagan.

bptest(modele)
## 
##  studentized Breusch-Pagan test
## 
## data:  modele
## BP = 6.9278, df = 10, p-value = 0.7322

Le test de Breusch-Pagan donne une statistique \(BP=8{,}4532\) et une p-value de \(0{,}5847\). Comme cette p-value est supérieure au seuil de 5 %, nous ne rejetons pas l’hypothèse nulle d’homoscédasticité.

On peut donc considérer que la variance des résidus est constante. L’hypothèse d’homoscédasticité des résidus est ainsi respectée pour le modèle.


16. Multicolinéarité

Nous utilisons le Variance Inflation Factor (VIF).

vif(modele)
##                      GVIF Df GVIF^(1/(2*Df))
## age              1.001967  1        1.000983
## surface          1.001538  1        1.000769
## capital_mobilier 1.002291  1        1.001145
## zone             1.005908  2        1.001474
## type_logement    1.001034  1        1.000517
## anciennete       1.002492  1        1.001245
## alarme           1.003348  1        1.001673
## nb_pieces        1.001772  1        1.000886
## sinistres_3ans   1.000963  1        1.000481

Un VIF élevé peut signaler une forte corrélation entre certaines variables explicatives.


17. Analyse des résidus

plot(
  fitted(modele),
  residuals(modele),

  xlab = "Valeurs ajustées",

  ylab = "Résidus",

  main = "Résidus en fonction des valeurs ajustées"
)

abline(
  h = 0,
  lty = 2
)


18. Observations influentes

Nous utilisons la distance de Cook.

cook <- cooks.distance(
  modele
)

plot(
  cook,
  type = "h",
  main = "Distance de Cook",
  xlab = "Observation",
  ylab = "Distance de Cook"
)

abline(
  h = 4 / nrow(train),
  lty = 2
)


19. Prédiction sur l’échantillon test

test$prediction <- predict(
  modele,
  newdata = test
)

test$prediction <- pmax(
  test$prediction,
  0
)

head(
  test[
    ,
    c(
      "cout_sinistre",
      "prediction"
    )
  ]
)
##    cout_sinistre prediction
## 1       674.8709   929.5253
## 6       632.4885   659.1775
## 17      755.7494   867.2672
## 26      797.4414   826.3638
## 34      731.5580   774.2729
## 36      768.4072   822.3726

20. Evaluation du modèle

Nous calculons trois indicateurs :

MAE

\[ MAE = \frac{1}{n} \sum_{i=1}^{n} |Y_i-\hat{Y_i}| \]

RMSE

\[ RMSE = \sqrt{ \frac{1}{n} \sum_{i=1}^{n} (Y_i-\hat{Y_i})^2 } \]

MAPE

\[ MAPE = \frac{100}{n} \sum_{i=1}^{n} \left| \frac{Y_i-\hat{Y_i}}{Y_i} \right| \]

MAE <- mean(
  abs(
    test$cout_sinistre -
      test$prediction
  )
)

RMSE <- sqrt(
  mean(
    (
      test$cout_sinistre -
        test$prediction
    )^2
  )
)

MAPE <- mean(
  abs(
    (
      test$cout_sinistre -
        test$prediction
    ) /
      test$cout_sinistre
  )
) * 100

performance <- data.frame(
  
  Indicateur = c(
    "MAE",
    "RMSE",
    "MAPE"
  ),
  
  Valeur = c(
    MAE,
    RMSE,
    MAPE
  )
  
)

kable(
  performance,
  digits = 2
)
Indicateur Valeur
MAE 89.96
RMSE 112.39
MAPE 11.67

Sur l’échantillon de test, le modèle présente une MAE de 91,31 €, ce qui signifie que l’erreur absolue moyenne entre le coût réel et le coût prédit est d’environ 91 €.

La RMSE est de 116,48 €. Elle est supérieure à la MAE, ce qui indique que certaines erreurs de prédiction sont plus importantes et pénalisent davantage la RMSE.

Enfin, le MAPE est de 12,40 %, ce qui signifie que l’erreur absolue moyenne en pourcentage est d’environ 12,4 %. Ces indicateurs montrent que le modèle fournit des prédictions relativement proches des coûts observés sur l’échantillon de test.


21. Coût observé versus coût prédit

ggplot(
  test,
  aes(
    x = cout_sinistre,
    y = prediction
  )
) +
  
  geom_point(
    alpha = 0.30
  ) +
  
  geom_abline(
    slope = 1,
    intercept = 0,
    linetype = "dashed"
  ) +
  
  labs(
    title = "Coût observé versus coût prédit",
    x = "Coût observé (€)",
    y = "Coût prédit (€)"
  ) +
  
  theme_minimal()


22. Construction de la prime pure

Dans cette approche simplifiée, nous assimilons le coût annuel prédit au coût pur.

\[ Prime\ pure_i = \widehat{C_i} \]

test$prime_pure <- test$prediction

summary(
  test$prime_pure
)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   422.1   726.5   822.0   835.1   922.9  1635.6

23. Construction de la prime commerciale

Nous ajoutons plusieurs chargements :

Chargement Taux
Sécurité 10 %
Gestion 12 %
Acquisition 8 %
Marge 5 %

Ainsi :

\[ Prime = Prime\ pure \times (1+10\%+12\%+8\%+5\%) \]

soit :

\[ Prime = Prime\ pure \times 1.35 \]

test$prime_commerciale <-
  
  test$prime_pure * 1.35

head(
  test[
    ,
    c(
      "id",
      "zone",
      "surface",
      "cout_sinistre",
      "prime_pure",
      "prime_commerciale"
    )
  ]
)
##    id    zone surface cout_sinistre prime_pure prime_commerciale
## 1   1 Moyenne   112.9      674.8709   929.5253         1254.8591
## 6   6 Moyenne    68.1      632.4885   659.1775          889.8896
## 17 17   Forte   110.7      755.7494   867.2672         1170.8108
## 26 26  Faible    25.0      797.4414   826.3638         1115.5911
## 34 34  Faible    52.9      731.5580   774.2729         1045.2684
## 36 36 Moyenne    88.0      768.4072   822.3726         1110.2031

24. Exemple de tarif

Nous pouvons maintenant observer la prime moyenne par zone.

tarif_zone <- test %>%
  group_by(zone) %>%
  summarise(
    `Nombre de contrats` = n(),
    `Coût moyen (€)` = mean(cout_sinistre),
    `Prime pure moyenne (€)` = mean(prime_pure),
    `Prime commerciale moyenne (€)` = mean(prime_commerciale)
  )

kable(
  tarif_zone,
  format = "html",
  digits = 2,
  caption = "Tarification moyenne par zone",
  align = "c"
) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"),
    full_width = FALSE,
    position = "center"
  )
Tarification moyenne par zone
zone Nombre de contrats Coût moyen (€) Prime pure moyenne (€) Prime commerciale moyenne (€)
Faible 433 802.21 796.37 1075.10
Moyenne 416 828.63 840.47 1134.63
Forte 151 912.66 931.27 1257.22

25. Analyse de sensibilité

Nous étudions maintenant l’évolution de la prime lorsque la surface du logement augmente.

profil <- data.frame(

  age = median(train$age),

  surface = seq(
    30,
    180,
    by = 10
  ),

  capital_mobilier =
    median(train$capital_mobilier),

  zone = factor(
    "Moyenne",
    levels = levels(train$zone)
  ),

  type_logement = factor(
    "Appartement",
    levels =
      levels(train$type_logement)
  ),

  anciennete =
    median(train$anciennete),

  alarme = factor(
    "Non",
    levels = levels(train$alarme)
  ),

  nb_pieces =
    median(train$nb_pieces),

  sinistres_3ans =
    median(train$sinistres_3ans)

)

profil$cout_predit <- predict(
  modele,
  newdata = profil
)

profil$prime <- pmax(
  profil$cout_predit,
  0
) * 1.35
ggplot(
  profil,
  aes(
    x = surface,
    y = prime
  )
) +

  geom_line(
    linewidth = 1
  ) +

  geom_point() +

  labs(
    title = "Evolution de la prime selon la surface",
    x = "Surface (m²)",
    y = "Prime commerciale (€)"
  ) +

  theme_minimal()


26. Limites du modèle

La régression linéaire constitue une première approche pédagogique, mais elle présente plusieurs limites pour une tarification MRH réelle.

Premièrement, le coût des sinistres est une variable positive et généralement asymétrique. Une régression linéaire classique peut donc être moins adaptée qu’un modèle actuariel spécialisé.

Deuxièmement, une tarification MRH peut être décomposée en :

\[ Prime\ pure = Fréquence \times Sévérité \]

On pourrait donc construire séparément :

  • un modèle de fréquence des sinistres ;
  • un modèle de coût moyen des sinistres.

Des GLM pourraient ensuite être utilisés avec des distributions adaptées.

D’autres méthodes pourraient également être comparées :

  • régression Gamma ;
  • régression de Poisson ;
  • binomiale négative ;
  • modèles additifs ;
  • arbres de décision ;
  • Random Forest ;
  • Gradient Boosting.

27. Conclusion

Ce projet a permis de construire une première méthode de tarification MRH à partir d’une régression linéaire multiple.

La démarche suivie peut être résumée ainsi :

\[ Données \rightarrow Analyse descriptive \rightarrow Régression \rightarrow Tests \rightarrow Validation \rightarrow Prédiction \rightarrow Tarification \]

Le modèle permet d’estimer le coût annuel attendu pour chaque contrat en fonction de ses caractéristiques.

La prédiction obtenue est ensuite utilisée comme coût pur auquel sont ajoutés différents chargements afin d’obtenir une prime commerciale.

Dans une application professionnelle, il serait nécessaire d’aller plus loin en utilisant notamment une approche fréquence-sévérité et des GLM, ainsi qu’une validation temporelle et une analyse de la stabilité du modèle.

Ce projet constitue ainsi une première base de travail pour une étude actuarielle de tarification MRH.