ratingtables is a lightweight R package for executing table-driven insurance
rating plans. It takes policy or entity data, normalized rating-factor tables,
and an ordered calculation specification, then returns rated values and,
optionally, a step-by-step calculation trace.
The package is intended for actuaries and technical analysts who want rating logic that is explicit, testable, version-controlled, portable, and open to ordinary programmatic manipulation.
ratingtables is available on CRAN. Install the released version with:
install.packages("ratingtables")
The current development version can be installed directly from GitHub:
install.packages("pak")
pak::pkg_install("gs-actuary/ratingtables")
Alternatively:
install.packages("remotes")
remotes::install_github("gs-actuary/ratingtables")
Load the package and create the included example rating plan:
library(ratingtables)
example <- example_rating_plan()
plan <- example$plan
policies <- example$policies
Rate the policies:
rated <- rate_policies(
rating_data = policies,
plan = plan
)
rated
For audit, reconciliation, or detailed calculation review, rate the same policies while retaining the step-by-step trace:
result <- rate_policies_with_trace(
rating_data = policies,
plan = plan
)
result$rated_data
head(result$term_trace)
Explain the calculation for one policy and coverage:
explain_rating(
rating_result = result,
row_number = 1,
coverage = "BI"
)
The basic workflow is:
Policy or entity data
+
Normalized rating-factor tables
+
Ordered rating specification
|
v
ratingtables
|
+---- Rated values
|
+---- Step-by-step trace
Insurance rating logic is often split across many spreadsheets, workbook tabs, formulas, rating manuals, implementation documents, system tables, and testing artifacts. These formats may be readable to humans, but they are difficult to validate, compare, version, reproduce, and manipulate programmatically.
The same rating algorithm may be represented several times:
Each separate representation creates another opportunity for transcription errors, inconsistent assumptions, outdated factors, and disagreements over which implementation is authoritative.
ratingtables represents rating factors and additives in a normalized,
long-format table. Each row contains one rating value together with the
rate-set metadata, coverage, term name, and variable-level conditions required
to select it.
The calculation order is represented separately in an explicit rating specification. The specification identifies which terms are applied, the order in which they are applied, where their values come from, and how each value changes the running premium or indicated value.
Separating rating data from calculation order allows the same execution engine to support different products, coverages, rate sets, states, scenarios, and proposed-rate revisions without hard-coding a new rater for each one.
Attempts to modernize premium raters through proprietary platforms are often beset by long delays. Projects can stall in procurement, compete unsuccessfully for internal technology resources, or encounter resistance to adopting a new enterprise tool.
Commercial “low-code” rating environments do not necessarily eliminate technical complexity. They may have steep learning curves, impose rigid configuration models, and make simple calculations more difficult than they would be in a flexible programming language. The resulting knowledge is often specific to one vendor rather than transferable to other analytical or engineering work.
These environments also lack the broad ecosystem, documentation, community knowledge, debugging tools, and general-purpose capabilities available in R, Python, and other widely used programming languages.
ratingtables allows an actuarial team to begin modernizing and standardizing
its rating programs immediately:
This standardization remains useful when an organization ultimately plans to implement a proprietary rating platform. A normalized and executable reference rater can help define requirements, validate converted rating logic, test a production implementation, and identify discrepancies between systems.
The package also encourages programmatic retrieval, validation, modification, display, comparison, and transfer of rating plans. This is a deliberate contrast with error-prone spreadsheet workflows based on manual copy-and-paste operations, hidden formulas, duplicated tabs, and repeated reconciliation.
A rating workflow has four main components.
Ordinary data frames contain the records to be rated. These may represent policies, risks, vehicles, drivers, boats, scheduled items, or other rating entities.
policies
Rating factors are stored in a long-format data frame. Each row represents one factor-table value and the conditions under which it applies.
Conceptually, a factor row contains information such as:
rate-set metadata
coverage
term name
factor value
variable 1 / level 1
variable 2 / level 2
...
This format supports both one-way rating factors and multi-variable interactions.
The rating specification defines the order of calculation. Each row describes one rating step, including:
coverage applicability
step number
term name
value source
calculation type
lookup or input information
rounding behavior
The factor table answers:
What value applies?
The rating specification answers:
When and how is that value used?
new_rating_plan() combines the factor table, rating specification,
coverages, custom functions, and supporting configuration into a validated
rating-plan object.
plan <- new_rating_plan(
factor_table = factor_table,
rating_spec = rating_spec,
coverages = c("BI", "PD")
)
The completed plan can then be applied to policy or entity data:
rated <- rate_policies(
rating_data = policies,
plan = plan
)
Use rate_policies_with_trace() when detailed step-level audit output is
required.
Rating steps that cannot be represented directly by the standard calculation types can use ordinary R functions.
A custom function is registered in new_rating_plan() and referenced
from the rating specification using value_source = "custom_function".
See ?custom_rating_functions for the function interface and a
worked example.
ratingtables currently provides support for:
The core execution functions use base R and accept ordinary data frames. The package does not prescribe how rating tables must be stored, edited, displayed, or deployed.
ratingtables separates high-throughput batch rating from detailed trace
generation.
For standard plans using exact factor lookups, input values, and the built-in
multiplicative, additive, continuous, replacement, and rounding operations,
rate_policies() uses an optimized vectorized batch engine. Exact lookup
structures and rating-step metadata are precompiled when a rating_plan is
created, common rating-data representations are cached once per batch, and
exact factor matching uses keyed vector lookups.
Entity workflows also use grouped aggregation and direct keyed joins for common
cases. Use score_entity_rows() when only rated entity values are needed; use
rate_entities() when detailed entity-level trace output is required.
An illustrative benchmark using a staged driver-to-household-to-vehicle rating workflow produced the following results on Windows x86-64 with R 4.6.1:
| Households | Driver rows | Vehicle rows | Elapsed | | ---------: | ----------: | -----------: | -------: | | 1,000 | 1,934 | 1,526 | 0.03 sec | | 6,000 | 11,673 | 9,233 | 0.11 sec | | 50,000 | 97,022 | 76,028 | 1.14 sec | | 200,000 | 388,388 | 303,995 | 4.34 sec |
These timings are illustrative rather than guarantees. Performance depends on hardware, R version, rating-plan structure, portfolio composition, and the operations used.
Detailed trace generation intentionally performs substantially more work because it retains step-level calculation information for every rated record. For large portfolio rerating, rate revision, or scenario analysis, use the non-trace functions unless the trace itself is required.
The reproducible benchmark script is available in
scripts/benchmark_rating_performance.R.
Potential uses include:
Build a reproducible alternative to spreadsheet-based desktop raters.
Apply current or proposed rating plans to historical policy records.
Compare current and proposed rate sets and analyze premium changes.
Use an independent executable rating plan to test a production implementation.
Maintain an explicit reference representation of the intended rating algorithm, even when production rating occurs elsewhere.
Use calculation traces to identify which terms, factors, and intermediate values produced a final premium.
Convert rating logic from manuals, spreadsheets, system extracts, or other artifacts into normalized factor tables and an explicit calculation specification.
The package executes the completed rating plan. Extracting and interpreting rating logic from arbitrary source materials remains a separate workflow that may require actuarial judgment.
Traceability is a central design goal.
Rather than returning only the final rated value, the package can retain one record for each calculation step. A trace can show:
policy or row identifier
coverage
step number
term name
value source
input value
looked-up or calculated value
value before the step
value after the step
factor-table row used
interpolation details, where applicable
This makes the calculation easier to inspect, validate, compare, and explain.
result <- rate_policies_with_trace(
rating_data = policies,
plan = plan
)
trace_to_excel_style(result$term_trace)
The trace can also be reshaped into wide factor columns:
trace_to_wide_factors(result$term_trace)
Some rating values originate below the policy level.
Examples include:
The generic entity workflow is:
Entity records
|
v
Rate entity rows
|
v
Aggregate by parent
|
v
Join aggregated values to parent records
|
v
Execute parent rating plan
For large entity books, score_entity_rows() is the normal batch-scoring path.
rate_entities() additionally constructs detailed trace output.
The principal functions are:
score_entity_rows()
rate_entities()
aggregate_entity_values()
join_entity_values()
The same framework can support different entity types without requiring product-specific functions in the core package.
Run the installed package demo with:
demo("rating_example", package = "ratingtables")
For a more complex rating example showcasing this package's flexibility, please see the vignette.
vignette("rating-plan-walkthrough", package = "ratingtables")
Additional development examples are available in the repository's scripts/
directory. These scripts are retained in the GitHub repository for learning and
demonstration but are excluded from the built package.
The README quick start is intentionally small. The longer examples are intended to demonstrate more realistic rating workflows, interactions, interpolation, entity aggregation, custom calculations, and trace review.
ratingtables is under active development.
The core rating-plan, lookup, rating, entity-aggregation, validation, and trace workflows are functional. The public API may continue to evolve before version 1.0.0 as the package is tested against additional real-world rating structures.
Feedback from actuaries, pricing analysts, implementation teams, and other potential users is welcome, particularly regarding:
Bug reports, reproducible examples, feature requests, documentation improvements, and focused pull requests are welcome.
See the contribution guidelines for project principles and contribution guidelines.
Large design changes should be discussed in a GitHub issue before implementation. The package aims to remain lightweight, transparent, storage-agnostic, and based on generic rating concepts rather than product-specific hard-coded functions.
The following commands are useful when developing the package from a local checkout:
devtools::document()
devtools::test()
devtools::check()
devtools::install()
These commands are for package development. Ordinary users do not need to clone the repository or run the development workflow to install and use the package.
ratingtables is released under the MIT License.
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.