# Read the dataset from a CSV file
# Sample dataset creation
set.seed(123)
data <- data.frame(
Policy_ID = 1:1000,
Customer_Age = sample(20:70, 1000, replace = TRUE),
Annual_Premium = round(runif(1000, 500, 3000), 2),
Claim_Frequency = rpois(1000, lambda = 1),
Claim_Severity = round(rlnorm(1000, meanlog = 7, sdlog = 0.5), 2),
Competitor_Price = round(runif(1000, 400, 3200), 2),
Churn = sample(0:1, 1000, replace = TRUE, prob = c(0.8, 0.2))
)
write.csv(data, "insurance_pricing.csv", row.names = FALSE)
head(data,5)
## Policy_ID Customer_Age Annual_Premium Claim_Frequency Claim_Severity
## 1 1 50 1277.92 3 1289.50
## 2 2 34 696.23 0 1193.53
## 3 3 70 1304.36 1 2217.35
## 4 4 33 2062.26 0 1817.03
## 5 5 22 1600.60 3 1246.00
## Competitor_Price Churn
## 1 650.31 0
## 2 2853.44 0
## 3 807.67 0
## 4 2178.77 0
## 5 1649.47 0
# Load dataset
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.1.4 ✔ readr 2.1.5
## ✔ forcats 1.0.0 ✔ stringr 1.5.1
## ✔ ggplot2 3.5.1 ✔ tibble 3.2.1
## ✔ lubridate 1.9.4 ✔ tidyr 1.3.1
## ✔ purrr 1.0.4
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
data <- read.csv("insurance_pricing.csv")
# Explore dataset
str(data)
## 'data.frame': 1000 obs. of 7 variables:
## $ Policy_ID : int 1 2 3 4 5 6 7 8 9 10 ...
## $ Customer_Age : int 50 34 70 33 22 61 69 62 56 33 ...
## $ Annual_Premium : num 1278 696 1304 2062 1601 ...
## $ Claim_Frequency : int 3 0 1 0 3 2 2 1 2 0 ...
## $ Claim_Severity : num 1290 1194 2217 1817 1246 ...
## $ Competitor_Price: num 650 2853 808 2179 1649 ...
## $ Churn : int 0 0 0 0 0 0 0 0 0 0 ...
summary(data)
## Policy_ID Customer_Age Annual_Premium Claim_Frequency
## Min. : 1.0 Min. :20.00 Min. : 502.9 Min. :0.000
## 1st Qu.: 250.8 1st Qu.:32.00 1st Qu.:1110.9 1st Qu.:0.000
## Median : 500.5 Median :44.50 Median :1748.8 Median :1.000
## Mean : 500.5 Mean :44.77 Mean :1747.7 Mean :1.001
## 3rd Qu.: 750.2 3rd Qu.:57.00 3rd Qu.:2365.4 3rd Qu.:2.000
## Max. :1000.0 Max. :70.00 Max. :2998.8 Max. :6.000
## Claim_Severity Competitor_Price Churn
## Min. : 162.0 Min. : 404.6 Min. :0.000
## 1st Qu.: 778.5 1st Qu.:1154.8 1st Qu.:0.000
## Median :1098.6 Median :1782.4 Median :0.000
## Mean :1232.3 Mean :1811.6 Mean :0.191
## 3rd Qu.:1513.6 3rd Qu.:2501.5 3rd Qu.:0.000
## Max. :7524.2 Max. :3199.1 Max. :1.000
ggplot(data, aes(x = Annual_Premium)) +
geom_histogram(bins = 30, fill = "skyblue", color = "black") +
labs(title = "Distribution of Annual Premiums")

# Create risk score
data_original<-data
data <- data %>%
mutate(Risk_Score = Claim_Frequency * Claim_Severity / Annual_Premium)
# Categorize customers by age group
data <- data %>%
mutate(Age_Group = case_when(
Customer_Age < 30 ~ "Young",
Customer_Age >= 30 & Customer_Age < 60 ~ "Middle-aged",
TRUE ~ "Senior"
))
# Explore engineered features
summary(data$Risk_Score)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.0000 0.0000 0.5053 0.8479 1.1717 13.1114
# Fit a linear regression model
model <- lm(Annual_Premium ~ Claim_Frequency + Claim_Severity + Age_Group, data = data)
summary(model)
##
## Call:
## lm(formula = Annual_Premium ~ Claim_Frequency + Claim_Severity +
## Age_Group, data = data)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1311.76 -629.29 3.46 622.82 1324.94
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.720e+03 5.727e+01 30.029 <2e-16 ***
## Claim_Frequency 4.168e+01 2.361e+01 1.766 0.0778 .
## Claim_Severity 1.193e-03 3.432e-02 0.035 0.9723
## Age_GroupSenior -4.696e+01 5.907e+01 -0.795 0.4269
## Age_GroupYoung -2.871e+01 5.902e+01 -0.486 0.6268
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 726.3 on 995 degrees of freedom
## Multiple R-squared: 0.003776, Adjusted R-squared: -0.0002286
## F-statistic: 0.9429 on 4 and 995 DF, p-value: 0.4383
# Predict premiums
data$Predicted_Premium <- predict(model, data)
# Calculate RMSE
library(Metrics)
rmse(data$Annual_Premium, data$Predicted_Premium)
## [1] 724.5253
# Install optimization package
if (!require("nloptr")) install.packages("nloptr")
## Loading required package: nloptr
library(nloptr)
# Define objective function
objective <- function(price) {
revenue <- sum(price * (1 - data$Churn))
cost <- sum(data$Claim_Frequency * data$Claim_Severity)
return(-1 * (revenue - cost))
}
# Optimization
opt_result <- nloptr(
x0 = rep(mean(data$Annual_Premium), nrow(data)),
eval_f = objective,
lb = rep(500, nrow(data)),
ub = rep(3000, nrow(data)),
opts = list("algorithm" = "NLOPT_LN_COBYLA", "xtol_rel" = 1e-4)
)
opt_result
##
## Call:
##
## nloptr(x0 = rep(mean(data$Annual_Premium), nrow(data)), eval_f = objective,
## lb = rep(500, nrow(data)), ub = rep(3000, nrow(data)), opts = list(algorithm = "NLOPT_LN_COBYLA",
## xtol_rel = 1e-04))
##
##
## Minimization using NLopt version 2.8.0
##
## NLopt solver status: 5 ( NLOPT_MAXEVAL_REACHED: Optimization stopped because
## maxeval (above) was reached. )
##
## Number of Iterations....: 100
## Termination conditions: xtol_rel: 1e-04
## Number of inequality constraints: 0
## Number of equality constraints: 0
## Current value of objective function: -274800.86509
## Current value of controls: 2372.653 2372.653 2372.653 2372.653 2372.653 2372.653 2372.653 2372.653
## 2372.653 2372.653 2372.653 2372.653 1747.653 2372.653 2372.653 2372.653
## 2372.653 2372.653 2372.653 2372.653 2372.653 2372.653 2372.653 2372.653
## 2372.653 2372.653 2372.653 2372.653 2372.653 2372.653 2372.653 2372.653
## 2372.653 2372.653 1747.653 2372.653 2372.653 2372.653 2372.653 2372.653
## 2372.653 2372.653 2372.653 2372.653 2372.653 2372.653 2372.653 2372.653
## 2372.653 2372.653 2372.653 1747.653 2372.653 2372.653 2372.653 2372.653
## 2372.653 2372.653 2372.653 2372.653 1747.653 2372.653 2372.653 2372.653
## 2372.653 2372.653 2372.653 2372.653 2372.653 2372.653 2372.653 2372.653
## 2372.653 1747.653 2372.653 2372.653 2372.653 2372.653 2372.653 2372.653
## 2372.653 2372.653 2372.653 2372.653 2372.653 2372.653 2372.653 2372.653
## 2372.653 2372.653 2372.653 2372.653 2372.653 2372.653 2372.653 2372.653
## 2372.653 2372.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
## 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653 1747.653
library(shiny)
library(ggplot2)
ui <- fluidPage(
titlePanel("Pricing Optimization Dashboard"),
sidebarLayout(
sidebarPanel(
sliderInput("age", "Customer Age:", min = 20, max = 70, value = 40),
numericInput("claims", "Claim Frequency:", value = 1, min = 0),
numericInput("severity", "Claim Severity:", value = 5000, min = 0)
),
mainPanel(
plotOutput("premiumPlot")
)
)
)
server <- function(input, output) {
output$premiumPlot <- renderPlot({
predicted <- predict(model, data.frame(
Claim_Frequency = input$claims,
Claim_Severity = input$severity,
Age_Group = ifelse(input$age < 30, "Young",
ifelse(input$age < 60, "Middle-aged", "Senior"))
))
ggplot(data, aes(x = Predicted_Premium)) +
geom_histogram(fill = "blue", color = "black", bins = 30) +
geom_vline(xintercept = predicted, color = "red", linetype = "dashed") +
labs(title = "Predicted Premium", x = "Premium", y = "Frequency")
})
}
shinyApp(ui, server)
Shiny applications not supported in static R Markdown documents