View source: R/brms_freq_sev.R
brms_freq_sev | R Documentation |
This function allows you to create a multivariate Bayesian frequency-severity model in which the frequency parameter is adjusted by the survival function of the severity model at the deductible/excess/attachment point.
This allows for both linear and non-linear functions of the variates for both the frequency and severity model components.
brms_freq_sev( freq_formula = NULL, sev_formula = NULL, freq_family = NULL, sev_family = NULL, freq_data = NULL, sev_data = NULL, prior = NULL, ded_name = "ded", freq_adj_fun = NULL, stanvars = NULL, mle = FALSE, chains = 2, iter = 1000, warmup = 250, refresh = 100, sample_prior = "no", ded_adj_min = 0, backend = "rstan", adapt_delta = 0.8, max_treedepth = 8, control = NULL, seed = sample.int(.Machine$integer.max, 1), save_pars = NULL, ... )
freq_formula |
BRMS Formula; Linear/Non-linear formula for frequency model |
sev_formula |
BRMS Formula; Linear/Non-linear formula for severity model |
freq_family |
Family; Family for frequency model |
sev_family |
Family; Family for severity model |
freq_data |
Data Frame; The data required for the frequency model |
sev_data |
Data Frame; The data required for the severity model |
prior |
BRMS Prior; The set of priors for both the frequency and severity models |
ded_name |
Character; The column name for the deductible/excess/attachment point in the frequency data |
freq_adj_fun |
Character; The Stan function used to adjust the mean frequency parameter. If NULL, the survival function of the severity model at the deductible will be used. |
mle |
Boolean; If TRUE, the optimize function is used to create parameter point estimates via Maximum-Likelihood Estimation |
ded_adj_min |
Numeric; The minimum value the deductible adjustment can be. This can help when some deductibles are very high. |
... |
Additional accepted BRMS fit parameters |
BRMS Fit
#### Simulate Frequency Data #### options(stringsAsFactors = FALSE, mc.cores = parallel::detectCores()) #' Assuming one rating factor: region. set.seed(123456) # Region Names regions = c("EMEA", "USC") # Number of frequency samples freq_n = 5e3 # Defines a function for lambda freq_lambda = exp(c(EMEA = 0.5, USC = 1)) # Generate samples for ground-up frequency data freq_data = data.frame( pol_id = seq(freq_n), ded = runif(freq_n, 1e3, 5e3), lim = runif(freq_n, 50e3, 100e3), region = sample(regions, freq_n, replace = T) ) %>% mutate( freq_lambda = freq_lambda[region], claimcount_fgu = rpois(freq_n, freq_lambda) ) #### Simulate severity Data #### mu_vec = c(EMEA = 8, USC = 9) sigma_vec = exp(c(EMEA = 0, USC = 0.4)) sev_data = data.frame( ded = rep(freq_data$ded, freq_data$claimcount_fgu), lim = rep(freq_data$lim, freq_data$claimcount_fgu), region = rep(freq_data$region, freq_data$claimcount_fgu) ) %>% mutate( loss_uncapped = unlist( lapply( seq(freq_n), function(i){ rlnorm(freq_data$claimcount_fgu[i], mu_vec[freq_data$region[i]], sigma_vec[freq_data$region[i]] ) } ) ) ) %>% mutate( pol_id = rep(seq(freq_n), freq_data$claimcount_fgu) ) %>% filter( loss_uncapped > ded ) %>% mutate( claim_id = row_number(), lim_exceed = as.integer(loss_uncapped >= lim), loss = pmin(loss_uncapped, lim) ) # Frequency data filtered for losses below the deductible freq_data_net = freq_data %>% left_join( sev_data %>% group_by( pol_id ) %>% summarise( claimcount = n() ) %>% ungroup(), by = "pol_id" ) %>% mutate( claimcount = coalesce(claimcount, 0) ) #### Run Model #### mv_model_fit = brms_freq_sev( freq_formula = bf(claimcount ~ 1 + region), sev_formula = bf(loss | trunc(lb = ded) + cens(lim_exceed) ~ 1 + region, sigma ~ 1 + region ), freq_family = poisson(), sev_family = lognormal(), freq_data = freq_data_net, sev_data = sev_data, prior = c(prior(normal(0, 1), class = Intercept, resp = claimcount), prior(normal(0, 1), class = b, resp = claimcount), prior(normal(8, 1), class = Intercept, resp = loss), prior(lognormal(0, 1), class = Intercept, dpar = sigma, resp = loss), prior(normal(0, 1), class = b, dpar = sigma, resp = loss) ), ded_name = "ded", backend = "cmdstanr", chains = 4, iter = 1000, warmup = 250, refresh = 50, adapt_delta = 0.999, max_treedepth = 15 ) #### Results #### model_post_samples = posterior_samples( mv_model_fit ) %>% transmute( s1_emea = b_loss_s1_Intercept, s1_usc = b_loss_s1_Intercept + b_loss_s1_regionUSC, sigma_emea = exp(b_sigma_loss_Intercept), sigma_usc = exp(b_sigma_loss_Intercept + b_sigma_loss_regionUSC), f1_emea = b_claimcount_f1_Intercept, f1_usc = b_claimcount_f1_Intercept + b_claimcount_f1_regionUSC ) model_output = model_post_samples %>% sapply( function(x) c(lower = quantile(x, 0.025), mean = mean(x), upper = quantile(x, 0.975)) ) %>% as.data.frame() %>% bind_rows( data.frame( s1_emea = 8, s1_usc = 9, sigma_emea = exp(0), sigma_usc = exp(0.4), f1_emea = 0.5, f1_usc = 1 ) )
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.