Provides tools for insurance rate revision workflows around normalized rating factor tables. Functions reshape long-form rating tables into actuary-friendly wide tables, round-trip those tables through Microsoft Excel, apply and review proposed factor changes, rebase and normalize relativities, compare current and proposed rating output, summarize rate impacts, and solve a one-dimensional rate-level adjustment to meet a target aggregate rate change. The package is designed to work naturally with 'ratingtables' while remaining usable with ordinary data frames and arbitrary user-supplied rating code.
raterevision is a companion workflow package for insurance rate revisions. It
focuses on the work around a rating engine rather than replacing the engine:
The package is designed to work naturally with ratingtables, but most
functions operate on ordinary data frames. solve_rate_target() is deliberately
agnostic to the rating implementation: its callback may run a different spec,
perform driver averaging, call custom code, or multiply an entire vector of
territory-specific base rates by a common scalar.
Once available on CRAN, install the released version with:
install.packages("raterevision")
Install the development version from GitHub with:
pak::pkg_install("gs-actuary/raterevision")
library(raterevision)
wide <- rate_tables_wide(
factor_table,
filters = list(state = "IL"),
matrix_terms = "age_gender_interaction"
)
write_rate_workbook(wide, "IL_rate_revision.xlsx")
proposed <- read_rate_workbook("IL_rate_revision.xlsx")
rate_plan_diff(factor_table, proposed, include_unchanged = FALSE)
The workbook contains a very-hidden manifest so the import does not have to guess which sheets are ordinary coverage-wide tables versus two-way interaction matrices. Sparse factor cells are written as true Excel blanks; clearing a factor cell on a coverage-wide sheet removes that coverage-specific key on import. Interactions deeper than two variables are intentionally never rendered as matrices.
proposed <- replace_factor_set(current, new_age_table)
proposed <- update_factors(proposed, sparse_changes)
proposed <- rebase_factor_set(proposed, "driver_age", base_level = "20")
validate_rate_plan(proposed)
replace_factor_set() treats the replacement as a complete table slice;
update_factors() changes only supplied keys and rejects new keys unless
allow_new_levels = TRUE is explicit.
ratingtables::rate_policies() returns indicated_<coverage> columns, which
compare_rating_outputs() detects automatically:
impact <- compare_rating_outputs(
current_output,
proposed_output,
id_cols = "vehicle_id",
keep_cols = c("charter", "territory")
)
summarize_rate_change(impact, by = c("charter", "coverage"))
rate_change_distribution(impact, by = "coverage")
solution <- solve_rate_target(
current_premium = 10e6,
target = 0.06,
rate_function = function(x) {
# x is the one scalar the actuary has chosen to solve.
# This code can be arbitrarily complicated.
rate_proposed_book(base_multiplier = x)
}
)
For separate base rates by territory, the same scalar can multiply all proposed territory bases. If multiple territory bases are allowed to move independently against only one aggregate target, the problem is underdetermined; the package does not invent a solution.
The repository scripts/ directory contains line-by-line runnable walkthroughs:
00_setup_check_install.R01_excel_roundtrip.R02_programmatic_revision.R03_driver_averaging_rate_change.R04_solve_rate_target.R05_full_demo_workflow.R06_without_raterevision_baseline.RThese scripts are intentionally excluded from the built package so they can be
verbose, exploratory, and useful for live demonstrations. The installed package
also includes the vignette vignette("rate-revision-workflow", package = "raterevision").
For a legacy Excel rater that was not created by raterevision, start with a heuristic profile rather than a blank rebuild:
profile <- profile_rate_workbook("legacy_rater.xlsx")
profile
# Review/edit ordinary columns such as include, table_name, extract_range,
# and header_row before extraction.
profile$include[profile$confidence < .35] <- FALSE
legacy_tables <- extract_rate_tables(profile)
legacy_tables
The profiler makes educated structural guesses; it does not pretend arbitrary workbooks are self-describing. Low-confidence candidates stay in the profile for human review. Extracted tables are raw migration inputs and should be normalized/validated before becoming a production ratingtables plan.
After rating the same records through a trusted rater and the candidate implementation:
comparison <- compare_rating_outputs(
trusted_output,
candidate_output,
id_cols = "vehicle_id",
keep_cols = c("territory", "driver_age", "marital_status", "credit")
)
diagnosis <- diagnose_rating_output(comparison)
diagnosis
diagnosis$suspects
diagnosis$interaction_signals
The diagnostic first checks discrepancies by rating-variable level, then uses lightweight background models to rank likely sources of missing or incorrect premiums. The default print method reports conclusions rather than model internals.