Skip to Content
Course content

179: Insurance Pricing Models in R

Click on the "Edit" button in the top corner of the screen to edit your slide content.

When we talk about "pricing" in insurance, we aren't just picking a number that feels right. We're trying to estimate the expected loss—the amount the company expects to pay out in claims for a specific risk profile. In the industry, this is often called the "Pure Premium."

I've found that the biggest hurdle for engineers moving into actuarial R work is treating insurance claims like standard regression problems. Claims data is messy; it's usually skewed, non-negative, and has a lot of zeros. If you use a standard linear model, you'll end up predicting negative claim costs for low-risk drivers, which is physically impossible and a great way to get yelled at by your underwriting lead.

Setting up our policyholder data

Let's build a simple pricing model for a theoretical auto insurance product. I'll create a small synthetic dataset with three variables: the driver's age, the value of the car, and the actual claim amount paid out over the last year.

# Synthetic insurance data
set.seed(42)
n <- 1000
df <- data.frame(
  age = sample(18:80, n, replace = TRUE),
  car_value = runif(n, 5000, 50000),
  claims = rgamma(n, shape = 0.5, scale = 2000) # Skewed claim distribution
)

# Let's simulate that younger drivers and expensive cars cost more
df$claims <- df$claims * (100 / df$age) * (df$car_value / 20000)

The trap of using standard linear regression

Now, my first instinct—and a common mistake—is to just throw this into a lm() call. It's the fastest way to get a result, but let's see why it fails here.

# The 'wrong' way
basic_model <- lm(claims ~ age + car_value, data = df)
summary(basic_model)

# Let's predict for a 19-year-old with a $60k car
new_driver <- data.frame(age = 19, car_value = 60000)
predict(basic_model, new_driver)

If you run this on a larger or more varied dataset, you'll frequently see the model predict a negative claim amount for very old drivers with cheap cars. It's because lm() assumes a Gaussian (normal) distribution of errors. Insurance claims aren't normal; they're heavily right-skewed. We need a Generalized Linear Model (GLM).

Switching to a Gamma GLM for claim costs

Since claim amounts are always positive and typically follow a Gamma distribution, I'm going to switch to glm(). I'll use a log link function. This ensures that our predictions stay positive, as the model essentially predicts the log of the cost and then exponentiates it back.

# The 'right' way for cost modeling
pricing_model <- glm(claims ~ age + car_value, 
                     family = Gamma(link = "log"), 
                     data = df)

summary(pricing_model)

# Now let's check that same 19-year-old
predict(pricing_model, new_driver, type = "response")

Notice the type = "response" argument. Without it, R would give you the prediction on the scale of the link function (the log scale), which isn't useful for a premium quote. Now we have a value that actually makes sense in a business context.

Calculating the final premium

The value we just predicted is the Pure Premium—the raw cost of the risk. But an insurance company can't survive on pure premiums; they need to cover administrative overhead and make a profit. In the industry, we apply a "loading factor."

# Define our loading factor (e.g., 20% for expenses and profit)
loading_factor <- 1.20

# Calculate final premium
pure_premium <- predict(pricing_model, new_driver, type = "response")
final_premium <- pure_premium * loading_factor

cat("The final quoted premium is: $", round(final_premium, 2))

By using a Gamma GLM, we've moved from a mathematically incorrect model to one that respects the nature of insurance risk. You've effectively built a basic pricing engine.




📋 Practical Task

Build a Home Insurance Pricing Model for Roof Age and Location

You have been tasked with creating a pricing model for a new home insurance product. The primary risk drivers are the age of the roof (in years) and the region (Coastal vs. Inland).

Your goal:

  • Create a dataframe with 500 observations containing roof_age (1 to 30 years), region (a factor with levels "Coastal" and "Inland"), and claim_amount (simulated using a Gamma distribution).
  • Build a GLM using the Gamma(link = "log") family to predict claim_amount based on roof_age and region.
  • Predict the Pure Premium for a home with a 25-year-old roof located in a "Coastal" region.
  • Apply a loading factor of 1.35 (35% markup) to arrive at the final premium.

Print the final premium to the console, rounded to two decimal places.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.