--- title: "Getting Started" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Getting Started} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` ## Introduction `insurancerating` provides building blocks for common actuarial pricing tasks in R. This vignette presents one possible workflow. It is an illustration rather than a prescribed end-to-end pricing methodology. The sequence is chosen to show how several package components can be combined. Depending on the portfolio, available data, modelling objective and applicable requirements, activities may be omitted, added, repeated or performed in a different order. The example is not intended to represent a canonical industry workflow or the internal process of a particular organisation. A GLM-based pricing exercise commonly combines several tasks: 1. understand observed portfolio experience and rating factors 2. construct modelling variables 3. estimate frequency, severity and technical risk 4. translate technical risk into a reviewable tariff structure 5. interpret, validate and refine the resulting model The work is often iterative. Exploratory results may lead to different variable definitions, large-loss treatment may affect the severity model, and tariff or implementation constraints may require the statistical models to be revisited. The package functions can be combined in different orders depending on the portfolio and modelling objective. The example illustrates how to: - analyse risk factors with `factor_analysis()` - estimate pricing models with `glm()` - interpret coefficients with `rating_table()` - assess model behaviour and stability - apply a small actuarial refinement The focus is on the transition from observed portfolio experience to fitted technical risk and a tariff structure that can be reviewed actuarially. ## Portfolio and data The examples consistently use `MTPL`, a motor portfolio with: - number of claims (`nclaims`), - exposure (`exposure`), - total claim cost per portfolio record (`amount`), - several rating factors ```{r, warning = FALSE, message = FALSE} library(insurancerating) library(dplyr) head(MTPL) ``` ## Understanding rating factors ### Factor analysis A pricing analysis commonly starts with a descriptive review of the portfolio. Before fitting a model, it is useful to assess: - how experience differs across factor levels - whether observed differences are supported by sufficient exposure and claim volume - whether the observed pattern is plausible This is done with `factor_analysis()`. ### Basic factor analysis We start by analysing a single risk factor. ```{r} fa <- factor_analysis( MTPL, risk_factors = "zip", claim_count = "nclaims", exposure = "exposure", claim_amount = "amount" ) fa ``` The output provides commonly used portfolio metrics such as: - frequency = claims / exposure - average severity = loss / claims - risk premium = loss / exposure Loss ratio and average premium can also be calculated when an earned-premium column is supplied. `MTPL` does not contain that quantity, so they are not used in this example. ### Visualising factor behaviour ```{r} autoplot(fa, metrics = c("exposure", "frequency", "risk_premium")) ``` This provides a direct view of: - the distribution of exposure - the variation in claim frequency - the variation in risk premium These are descriptive, univariate results. They show the observed experience and volume by level, but do not control for correlations with other risk factors. ## Handling continuous variables ### Why continuous variables are treated separately Continuous variables can be modelled directly. In many traditional insurance tariffs, they are instead translated into a limited number of segments to improve stability, interpretation and implementation. This example uses the following sequence: 1. analysed as continuous variables 2. translated into tariff segments 3. used in a GLM as categorical rating factors Grouping makes the resulting tariff effect discrete and directly implementable, but introduces a segmentation choice that should be reviewed. ### Analysing the shape with a GAM ```{r} age_freq <- risk_factor_gam( data = MTPL, risk_factor = "age_policyholder", claim_count = "nclaims", exposure = "exposure" ) autoplot(age_freq, show_observations = TRUE) ``` This step is used to inspect: - non-linear patterns - local volatility - areas with low exposure - plausible breakpoints for tariff segments ### Deriving tariff segments ```{r} age_segments <- derive_tariff_segments(age_freq) autoplot(age_segments) summary(age_segments) ``` This derives candidate segment boundaries from the fitted continuous effect. Exposure and claim volume have already informed the GAM estimate. They are reported by `summary()` for review, but are not applied again when the fitted curve is divided into intervals. The evolutionary tree approximates the GAM curve rather than the individual claim outcomes. Each distinct risk-factor value on the fitted curve has equal influence on this approximation; exposure is not applied as a second weight. The resulting segments should be reviewed against exposure, observed experience, stability and practical tariff requirements before they are used in a model. ### Adding tariff segments to the data ```{r} dat <- MTPL |> add_tariff_segments(age_segments, name = "age_cat") |> mutate(across(where(is.character), as.factor)) |> mutate(across(where(is.factor), ~ set_reference_level(., exposure))) ``` `set_reference_level()` sets the reference level to the level with the highest exposure. This changes the coefficient parameterisation, not the fitted values. A high-exposure level is often a useful baseline because its relativity is supported by a substantial part of the portfolio. ## Estimating frequency and severity ### Why GLMs are used Generalized linear models are widely used in insurance pricing. They can: - accommodate non-normal response distributions - produce interpretable multiplicative effects - be translated into tariff relativities A common decomposition is: - frequency --> Poisson GLM - severity --> Gamma GLM ### Frequency model ```{r} mod_freq <- glm( nclaims ~ age_cat, offset = log(exposure), family = poisson(), data = dat ) ``` The response is the observed claim count for each policy-period. Because `log(exposure)` is included as an offset, `predict(mod_freq, type = "response")` returns the expected number of claims for that record's exposure. Dividing that prediction by exposure gives claim frequency per exposure-year. ### Severity model ```{r} severity_data <- dat |> filter(nclaims > 0, amount > 0) |> mutate(average_claim_amount = amount / nclaims) mod_sev <- glm( average_claim_amount ~ age_cat, weights = nclaims, family = Gamma(link = "log"), data = severity_data ) ``` `amount` is the total claim cost recorded for a portfolio row. Dividing by `nclaims` gives average claim severity. Claim count is then used as the model weight because a row containing several claims represents more severity observations than a row containing one claim. Frequency and severity are modelled separately because they describe different parts of the loss process. ## Constructing technical risk premium ```{r} premium_df <- dat |> add_prediction( mod_freq, mod_sev, predictions = c("expected_claim_count", "expected_average_severity") ) |> mutate( claim_frequency = expected_claim_count / exposure, expected_loss = expected_claim_count * expected_average_severity, risk_premium = claim_frequency * expected_average_severity ) premium_df |> select( exposure, expected_claim_count, claim_frequency, expected_average_severity, expected_loss, risk_premium ) |> head() ``` The units are now explicit: - `expected_claim_count` is the expected number of claims for the observed policy exposure; - `claim_frequency` is the expected number of claims per exposure-year; - `expected_average_severity` is the expected amount per claim; - `expected_loss` is the expected loss for the policy-period; - `risk_premium` is expected loss per exposure-year. The risk premium is a technical expected loss cost. It does not include expense loadings, profit margins, taxes or other components of a commercial premium. ## From technical risk to tariff ### Fitting a tariff representation ```{r} burn_unrestricted <- glm( risk_premium ~ age_cat + zip, weights = exposure, family = Gamma(link = "log"), data = premium_df ) ``` This second GLM is not statistically required after fitting frequency and severity. Here it is used as a tariff-construction step: it approximates the combined technical risk with a compact multiplicative structure based on `age_cat` and `zip`. Exposure weights give more influence to tariff cells that represent a larger part of the portfolio. The response is a fitted technical target rather than an observed claim outcome. The model is therefore used to obtain an interpretable and implementable tariff representation, not as a replacement for the underlying frequency and severity analyses. ## Interpreting model effects and observed experience ### Rating table ```{r} rt <- rating_table(burn_unrestricted) |> add_portfolio_experience( data = premium_df, claim_count = "nclaims", exposure = "exposure", claim_amount = "amount", metric = "risk_premium" ) rt ``` `rating_table()` expresses fitted model effects as tariff relativities for the original factor levels, including the reference level. The attached portfolio experience makes it possible to compare those modelled relativities with the observed risk-premium pattern. Exposure remains important when judging whether an apparent difference is sufficiently credible. ### Visualising coefficients ```{r} autoplot(rt, metric = "risk_premium") ``` This plot can be used to assess: - the relative size of coefficients - the structure across levels - the exposure behind each level - whether additional refinement may be needed At this stage, the relevant questions are: - are coefficients sufficiently stable? - do they follow the expected pattern? - are some levels driven by limited exposure? ## Validating the model Validation combines statistical diagnostics with actuarial review. No single measure determines whether a tariff model is suitable. Relevant questions include dispersion, residual structure, out-of-sample stability, exposure by level, plausible factor shapes and agreement with observed experience. ### Overdispersion ```{r} check_overdispersion(mod_freq) ``` For a Poisson frequency model, a dispersion ratio materially above one can indicate remaining heterogeneity or an unsuitable variance assumption. The result should be considered together with residual and factor-level checks. ### Model performance ```{r} model_performance(mod_freq) ``` This reports AIC, BIC and response-scale RMSE for the expected claim-count model. These measures are most meaningful when models use the same response, records, weights and offsets. ### Bootstrap performance ```{r} bp <- bootstrap_performance( mod_freq, dat, n_resamples = 50, sample_fraction = 0.8, sampling = "bootstrap", show_progress = FALSE ) autoplot(bp) ``` This refits the model on repeated bootstrap samples and evaluates RMSE on out-of-bag records. The resulting distribution describes sensitivity to the sampled portfolio records; it is not a prediction interval for future claims. A single fit statistic is not sufficient. In pricing practice, the numerical results should be reviewed together with exposure, observed experience, residual diagnostics and stability of effects over time. The dedicated model validation vignette covers these checks in more detail. ## Refining the tariff At this point, the example has produced: - portfolio-level insight - fitted pricing models - interpretable factor relativities - basic performance diagnostics Depending on the intended tariff, a further refinement step may be useful before implementation. Typical reasons include: - irregular coefficient patterns - monotonicity requirements - externally imposed restrictions - expert-driven adjustments The following small example fixes one ZIP relativity as an explicit actuarial assumption. Other ZIP levels retain their current relativities, and the intercept-only refit recalibrates the overall level without re-estimating the prescribed tariff effects. ```{r} zip_restriction <- data.frame( zip = "3", relativity = 1.05 ) tariff_refinement <- prepare_refinement( burn_unrestricted, data = premium_df ) |> add_restriction(zip_restriction) burn_refined <- refit(tariff_refinement, intercept_only = TRUE) rating_table(burn_refined) ``` The value `1.05` is illustrative; a production restriction requires actuarial support and governance. Smoothing, shrinkage, rebasing and more extensive restrictions are described in [Refinement building blocks](refinement-workflow.html). ## Summary A possible sequence in `insurancerating` is: ```{r, eval = FALSE} factor_analysis() # analyse portfolio behaviour risk_factor_gam() # analyse continuous variables derive_tariff_segments() # derive tariff segments glm() # estimate frequency and severity add_prediction() # construct technical risk premium glm() # represent risk in a tariff structure rating_table() # interpret tariff relativities bootstrap_performance() # assess stability prepare_refinement() # prepare an actuarial refinement refit() # fit the refined tariff model ``` The sequence distinguishes observed experience, fitted model effects, candidate tariff segmentation and model diagnostics. The final modelling choices remain dependent on the portfolio and pricing objective. ## Where to go next - [Pricing workflow and package building blocks](pricing-workflow-building-blocks.html) maps the wider package to the actuarial tasks it supports, including data preparation, large-loss treatment, modelling and tariff construction. - [Refinement building blocks](refinement-workflow.html) covers smoothing, restrictions, shrinkage, rebasing and audit of tariff changes. - [Model validation](model-validation.html) develops the diagnostic and out-of-sample checks used only briefly here. - [Large Portfolios](large-portfolios.html) shows how to reduce policy-period data to model points before fitting a pricing GLM.