We are pleased to announce a new demo Shiny application that uses machine learning to predict annual payments on individual insurance claims for 10 years into the future.
This post describes the basics of the model behind the above Shiny app, and walks through the model fitting, prediction, and simulation ideas using a single claim as an example.
Background
Insurance is the business of selling promises (insurance policies) to pay for potential future claims. After insurance policies are sold, it can be many years before a claim is reported to the insurer and many more years before all payments are made on the claim. Insurers carry a liability on their balance sheet to account for future payments on claims on previously sold policies. This liability is known as the Unpaid Claims Reserve, or just the Reserve. Since the Reserve is a liability for uncertain future payments, it’s exact value is not known and must be estimated. Insurers are very interested in estimating their Reserve as accurately as possible.
Traditionally insurers estimate their Reserve by grouping similar claims and exposures together and analyzing historical loss development patterns across the different groups. They apply the historical loss development patterns of the older groups (with adjustments) to the younger groups of claims and exposures to arrive at a Reserve estimate. This method does not accurately predict individual claim behavior, but, in aggregate, it can accurately estimate the expected value of the total Reserve.
Rather than using the traditional grouping methods, we predict payments on individual insurance claims using machine learning in R. Our goal is to model individual claim behavior as accurately as possible. We want our claim predictions to be indistinguishable from actual claims on an individual claim level, both in expected value and variance. If we can achieve this goal, we can come up with expected values and confidence levels for individual claims. We can aggregate the individual claims to determine the expected value and confidence levels for the total Reserve. There are many other insights we can gather from individual claim predictions, but let’s not get ahead of ourselves. Let’s start with an example.
Example - Predict Payments and Status for a Single Claim 1 Year into the Future
We will look at reported Workers’ Compensation claims. We will not predict losses on unreported claims (i.e. for our claim predictions, we already have 1 observation of the claim and we will predict future payments on this existing/reported claim). We will not predict future claims on policies that have not reported a claim to the insurer.
So our simplified model for 1 claims is:
We start with data for a single claim. Our goal is to predict the payments on our claim for 1 year into the future. We do this by feeding the claim to our payment model (which we will train later), and the model predicts the payments. Simple enough right? But we want to predict the status (“Open” or “Closed”) of the claim. We could do something like this:
Now we first predict the status, and then we predict the payments. We are getting closer to our goal now, but we also want to capture the variability in our prediction. We do this by adding a simulation after each model prediction:
The prediction returned by the above status model is the probability that the claim will be open at age 2 (e.g. our model might predict a 25% probability that our claim will be open at age 2. Since there are only 2 possible status states, “Open” or “Closed”, a 25% probability of being open implies a 75% probability the claim is closed at age 2). We use this probability to simulate open and closed observations of the claim. We then feed the observations (with simulated status) to our trained payment model. The payment model predicts an expected payment, and finally we simulate the variation in this expected payment based on the distribution of the payment model’s residuals.
If this sounds confusing, bear with me. I think a coded example will show it is actually pretty strait forward. Let’s get started:
Step 1: Load the data
Each claim has 10 variables. We have 3,296 claims in the training data and we pulled out 1 claim. We will make predictions for this 1 claim once we fit the models.
library(knitr)
library(kableExtra)
library(dplyr)
# This .RDS can be found at https://github.com/Tychobra/claims_ml_blog_post_data
data_training %
kableExtra::scroll_box(width = &quot;100%&quot;) claim_num
mem_num
cause_code
body_part_code
nature_code
class_code
status
paid_total
status_2
paid_incre_2
WC-163290
Member A
Struck Or Injured
Skull
Amputation
7720
C
6893.6947
C
451.3185
WC-923705
Member A
Struck Or Injured
Brain
Amputation
9101
C
60247.1871
C
0.0000
WC-808299
Member A
Fall - On Same Level
Skull
Burn
9101
O
34240.6365
O
2201.1637
WC-536953
Member A
Struck Or Injured
Skull
Contusion
9015
C
599.8090
C
0.0000
WC-146535
Member A
Struck Or Injured
Skull
Contusion
7720
C
265.5697
C
0.0000
WC-523720
Member A
Strain - Lifting
Skull
Concussion
7720
C
85.8200
C
0.0000
The first column (“claim_num”) is the claim identifier and will not be used as a predictor variable in the model training. The 7 predictor variables (columns 2 through 8) represent the claim at age 1. The last 2 columns (“status_2” and “paid_incre_2”) are the variables we will predict. “status_2” is the claim’s status (“O” for Open and “C” for Closed) at age 2. “paid_incre_2” is the incremental payments made on the claim between age 1 and 2.
Step 2: Fit the Status Model
Our status model is a logistic regression, and we train it with the caret package. We use the step AIC method to perform feature selection.
library(caret)
# train the model
tr_control