| Title: | Bagging and Regression Trees for Individual Claims Reserving |
| Version: | 0.2.0 |
| Description: | Claim-level (individual) loss reserving in non-life insurance, and the means to check claim-level reserves against the payments that followed. A data contract turns a table of claims and a table of payment transactions into a claim-by-development-year panel at a valuation date; a recursion engine completes the payment history of every reported claim one development year ahead, with plugged-in means or simulated payments, for regression trees, their bootstrap aggregation (bagging) and user-supplied learners; payments for incurred-but-not-reported claims are added from chain-ladder claim counts and the average predicted payment per development year; and back-tests score a prediction and a reference method such as the chain ladder against realised payments by calendar year and component. The default estimator is the method of Janoušek and Pešta (2025) <doi:10.1007/s00362-025-01715-9>, one regression tree per development year with bagging for replicate predictions, whose spread describes the bootstrap resampling variation of the predictions of (by default unpruned) trees, and an out-of-bag diagnostic of the one-year regressions; for totals beyond next year simulated instead of plugged-in completion is recommended. Ships a reproducible synthetic individual-claims dataset generated with the individual claims simulator of Wang and Wüthrich, built on 'SynthETIC'. |
| License: | GPL (≥ 3) |
| URL: | https://github.com/HeirOfIsildur/baggingReserve |
| BugReports: | https://github.com/HeirOfIsildur/baggingReserve/issues |
| Depends: | R (≥ 4.1) |
| Imports: | codetools, graphics, parallel, rpart, rngtools, stats, utils, withr |
| Suggests: | ChainLadder, knitr, R.rsp, testthat (≥ 3.0.0) |
| Config/testthat/edition: | 3 |
| VignetteBuilder: | knitr, R.rsp |
| Encoding: | UTF-8 |
| LazyData: | true |
| LazyDataCompression: | xz |
| Config/roxygen2/version: | 8.1.0 |
| NeedsCompilation: | no |
| Packaged: | 2026-10-27 15:51:21 UTC; honza |
| Author: | Jan Janoušek |
| Maintainer: | Jan Janoušek <janousek@karlin.mff.cuni.cz> |
| Repository: | CRAN |
| Date/Publication: | 2026-11-12 15:10:08 UTC |
baggingReserve: Bagging and Regression Trees for Individual Claims Reserving
Description
Software for claim-level (claim-by-claim) loss reserving in non-life insurance and for checking claim-level reserves against the payments that followed. It has three parts:
Details
a data contract:
claims_panel()turns a table of claims and a table of payment transactions, with user-specified column names, into a claim\timesdevelopment-year panel at a valuation datet. It enforces the valuation date on both tables, distinguishes observed zeros from future cells, keeps recoveries and claims without payments, and refuses input it cannot interpret;a recursion engine, shared by the single-tree fit (
reserve_tree()), the bagged ensemble (bag_reserve()) and user-supplied learners (argumentlearner,two_part_learner()), that completes the payment history of every reported claim one development year ahead, either by plugging in predicted means (completion = "mean") or by simulating the payments (completion = "draw"), with random-number streams that make bagged results independent of the number of worker processes;ibnr_reserve()adds a component for the claims incurred but not reported (IBNR) from chain-ladder projections of the reporting counts;-
back-testing:
backtest()scores a prediction, and a reference method such aschain_ladder(), against realised payments by calendar year and separately for the reported and the IBNR claims; valuing a claims history at an earlier date turns it into such a test.
The default estimator, a documented baseline that any learner can be measured against, is the
method of Janoušek and Pešta (2025), the source article. For every
development year l = 0, \dots, L-1 one regression tree predicts the payment of the next
development year, X_{l+1}, from the claim's static features, its reporting delay and its
payment history X_0, \dots, X_l, using the claims reported by development year l
whose payment in development year l + 1 is observed at the valuation date. In the
package's arguments, printed output and tables, the development-year index l of a tree
is called its period. The lower triangle of outstanding payments of the reported claims
(closed claims included; the status is not a feature of the source article's specification) is
filled recursively, feeding earlier predictions into later features. Bagging replaces each tree
by a bootstrap ensemble: a replicate completes all histories with one bootstrap tree per
development year, the point estimate is the bagged mean of the completed replicates, and the
replicate quantiles describe the variation of the bootstrap-tree predictions under the
resampling (they are neither prediction intervals for the payments nor confidence intervals for
the bagged mean). An out-of-bag score per development year is a diagnostic of the one-year
regressions. The IBNR component is the projected number of late-reported claims times the
average predicted payment per development year (Theorem 1 of the source article).
The default completion = "mean" reproduces the source article. Plugging in means is biased
for the payments after next year when the occurrence of a payment depends on the payment
history, so for totals beyond next year we recommend completion = "draw" (see
bag_reserve()). On the portfolios studied in the JSS manuscript the standard chain ladder is
more accurate than the default trees on most of them; the package is meant for finding out, with
backtest(), whether claim-level information helps on a user's own data.
The package ships a synthetic individual-claims portfolio generated with the simulator used in the source article (synthetic_claims, synthetic_payments; a fresh portfolio with its own seed and twelve accident years, not the source article's data) together with the generated future (synthetic_claims_full, synthetic_payments_full) for back-testing.
Author(s)
Maintainer: Jan Janoušek janousek@karlin.mff.cuni.cz (ORCID)
Authors:
Jan Janoušek janousek@karlin.mff.cuni.cz (ORCID)
Michal Pešta pesta@karlin.mff.cuni.cz (ORCID)
References
Janoušek J, Pešta M (2025). Bagging and regression trees in individual claims reserving. Statistical Papers, 66(4), 89. doi:10.1007/s00362-025-01715-9
Breiman L (1996). Bagging predictors. Machine Learning, 24(2), 123–140. doi:10.1007/BF00058655
Wüthrich MV (2018). Machine learning in individual claims reserving. Scandinavian Actuarial Journal, 2018(6), 465–480. doi:10.1080/03461238.2018.1428681
Wang M, Wüthrich MV (2022). Individual claims generator for claims reserving studies: data simulation.R. SSRN 4127073. doi:10.2139/ssrn.4127073
See Also
Useful links:
Report bugs at https://github.com/HeirOfIsildur/baggingReserve/issues
Data frames of predictions and back-tests
Description
as.data.frame() on a reserve_prediction gives one row per future cell (accident
year, development year, calendar year) with the mean over the replicates of the
reported-claim payments and, when attached, of the IBNR component and the total. On a
reserve_backtest it stacks the per-component tables of backtest() with a
component column.
Usage
## S3 method for class 'reserve_prediction'
as.data.frame(x, row.names = NULL, optional = FALSE, ...)
## S3 method for class 'reserve_backtest'
as.data.frame(x, row.names = NULL, optional = FALSE, ...)
Arguments
x |
A reserve_prediction or a |
row.names, optional |
Unused, for compatibility with the generic. |
... |
Unused. |
Value
A data frame.
Examples
data("synthetic_claims", "synthetic_payments", package = "baggingReserve")
cl <- synthetic_claims[synthetic_claims$Type == "3", ]
pay <- synthetic_payments[synthetic_payments$Id %in% cl$Id, ]
panel <- claims_panel(cl, pay, t = 12, L = 11, features = c("Age", "AccWeekday"))
res <- ibnr_reserve(predict(reserve_tree(panel, seed = 1)))
head(as.data.frame(res))
Run-off triangles of a claims panel or a reserve prediction
Description
as_triangle() aggregates a claims_panel into an observed payment or reporting-count
triangle, or extracts a completed payment triangle from a reserve_prediction. For a
panel, what = "paid" gives the accident-year by development-year triangle of
incremental (or cumulative) payments and what = "counts" the accident-year by
reporting-delay triangle of numbers of reported claims; cells that are not observable
at the valuation date (acc_year + l > t) are NA and observed cells without claims or
payments are 0. For a prediction, the future cells are filled with the predicted
payments of one replicate or of the mean over replicates: the observed cells are kept
for "reported" and "total" and are zero for "ibnr", and no cell is NA.
claim_counts() returns the incremental count triangle of a panel.
Usage
## S3 method for class 'chain_ladder'
as_triangle(x, type = c("incremental", "cumulative"), ...)
## S3 method for class 'reserve_prediction'
as_triangle(
x,
component = c("reported", "ibnr", "total"),
replicate = "mean",
...
)
as_triangle(x, ...)
## S3 method for class 'claims_panel'
as_triangle(x, what = c("paid", "counts"), cumulative = FALSE, ...)
## S3 method for class 'reserve_triangle'
print(x, digits = NULL, ...)
claim_counts(x)
Arguments
x |
A claims_panel (for |
type |
For a chain_ladder object: the completed |
... |
Passed on by the generic to its methods and by |
component |
|
replicate |
Replicate to display, or |
what |
|
cumulative |
Logical (default |
digits |
Round the cells before printing; |
Value
A numeric matrix of class "reserve_triangle" with the accident years as row
names and the development years (or reporting delays) 0, ..., L as column names,
carrying the attributes "t", "what" and "cumulative". It is an ordinary matrix in
every computation; only its print method hides the attributes behind a heading.
Examples
data("synthetic_claims", "synthetic_payments", package = "baggingReserve")
panel <- claims_panel(synthetic_claims, synthetic_payments, t = 12, L = 11,
features = c("Type", "Age", "AccWeekday"))
round(as_triangle(panel) / 1e6, 2)
claim_counts(panel)
# a completed triangle from a prediction on a small subset: all claims of one type
cl <- synthetic_claims[synthetic_claims$Type == "3", ]
pay <- synthetic_payments[synthetic_payments$Id %in% cl$Id, ]
small <- claims_panel(cl, pay, t = 12, L = 11, features = c("Age", "AccWeekday"))
res <- ibnr_reserve(predict(reserve_tree(small, seed = 1)), claim_counts(small))
round(as_triangle(res, "total") / 1e6, 2)
Back-test a reserve prediction against realised payments
Description
Compares the predicted future payments with the payments that actually followed,
cell by cell, from a table of claims and a table of payments that extend beyond the
valuation date (for the shipped data: synthetic_claims_full and
synthetic_payments_full). The truth is split into the component of the claims that
were reported by the valuation date (those in the prediction) and the claims reported
later (IBNR), and restricted to development years 0, ..., L; payments beyond L are
reported separately as the excluded tail. The prediction's replicate range is shown
next to the truth; it describes the variability recorded in the prediction (resampling
variation of bagged trees, simulated payments of one fitted model, or both; see
summary.reserve_prediction()), so "truth inside the range" is a descriptive statement, not a
coverage probability.
Usage
backtest(
pred,
claims,
payments,
id = c(claims = "Id", payments = "Id"),
acc_year = "AccYear",
event_year = "EventYear",
paid = "Paid",
probs = c(0.025, 0.975),
rep_year = "RepYear",
type = 7L,
reference = NULL,
reference_component = c("total", "reported"),
observed_to = NULL
)
Arguments
pred |
A reserve_prediction (with or without an IBNR component). |
claims, payments |
Claims and payments tables with the columns named below. |
id, acc_year, event_year, paid |
Column names, as in |
probs |
Two increasing probabilities in |
rep_year |
Name of the reporting-year column of |
type |
Quantile algorithm for the replicate range, as in |
reference |
Optional prediction of another method, scored against the same truth (see
the section on the reference): a chain_ladder object fitted on the paid triangle of the
prediction's panel, or a named numeric vector of payments by future calendar year, with an
optional |
reference_component |
The population the |
observed_to |
Last fully observed calendar year of |
Details
The truth tables must describe the same portfolio as the prediction, including the
claims reported after the valuation date: the IBNR component of the truth consists of the
claims in claims that are absent from the prediction, so selecting the truth by the
identifiers of the reported claims removes the IBNR claims from it, and an unrelated claim
in claims would be counted as IBNR. Select the population by the same criterion as the
panel (all claims of a type, say), not by identifiers. When claims carries the reporting
year (rep_year), the function rejects claims absent from the prediction that were
reported by the valuation date; it cannot detect IBNR claims that are missing from the
truth tables altogether, so the completeness of the truth population remains the caller's
responsibility. The claims of the prediction must carry in claims the accident years and,
when the reporting year is known, the reporting years they were predicted with (the reporting
delay is a covariate of the fit); a contradiction is an error. (A prediction saved before the
reporting delays were stored in it, claim_rep_delays, is only checked for a reporting year
by t.) Claims whose accident year is not among the prediction's accident years
(for example accident years after the valuation date), and their payments, are not scored;
their numbers and amount are reported in not_scored. Claims that the panel set aside with
claims_panel(beyond_L = "drop") (reported by t with a reporting delay above L, recorded in
the prediction as beyond_L_ids) belong to the portfolio but not to the prediction: they are
not counted as missing from it, and their payments after t, which all fall beyond development
year L, are part of the excluded tail, also when their accident year lies outside the
prediction's accident years (the panel's default range is determined after setting them aside);
not_scored records their number and the number and amount of those payments, and does not count
them among the claims outside the accident years.
Chronology. The truth is held to the data contract of claims_panel(): when claims
carries the reporting year, a claim reported before its accident year, and a payment dated
before the reporting year of its claim (which the model defines as zero, and which the
constructor refuses), are errors naming an example; move such a payment to the reporting
year, or correct the reporting year, as for the constructor. A claim of the prediction's
accident years without a reporting year is treated, as by the constructor, as not reported by
t: its payments after t count as IBNR truth, with a warning. Without a reporting-year column
(see rep_year) none of these checks, nor the population checks above, can be made.
Column names. The defaults of id, acc_year, event_year, paid and rep_year are the
column names of the input tables of the panel the prediction was made from (stored in the
prediction as mapping), so that a portfolio with another layout is back-tested with the
names given once to claims_panel(); a name passed here overrides it. The reporting year
follows the rule of claims_panel(): when the panel was built from a reporting-delay column
(rep_delay) and claims has that column, the reporting year is the accident year plus the
delay, whether or not claims also has a reporting-year column, which must then agree with it
wherever both are known; otherwise it is the reporting-year column. A rep_year passed
explicitly is used instead. For a prediction without a mapping (made by an earlier
version) the defaults are the names shown in the usage.
Follow-up. Every truth cell up to observed_to starts at zero and receives the payments
dated in it, so a calendar year without any payment counts as a realised zero. By default
(observed_to = NULL) the payments are taken to cover every calendar year of the prediction;
if the extract ends earlier, pass the last fully observed calendar year as observed_to:
the cells after it are unobserved (NA in truth_cells), their rows have NA truth, error
and range check, and the total rows (truth, prediction, range and reference alike) sum the
calendar years up to observed_to only (total_years of the result). The function does not
infer the end of the follow-up from the data, because a fully observed year can have no
payment; it warns when observed_to is NULL and the payments hold no transaction in the
last calendar years of the prediction. Payments dated after observed_to are not scored.
Reference. A reference is scored against the truth of the population it predicts:
reference_component = "total" (the default) for a prediction of all claims, such as a
chain ladder on the panel's paid triangle, which needs a prediction with an IBNR component
(attach it with ibnr_reserve()); "reported" for a named vector predicting the payments of
the reported claims only. A chain_ladder reference always predicts all claims, and it must
have been fitted on the paid triangle of the prediction's panel: the same accident years,
development years 0, ..., L, valuation date and observed payments (a chain ladder of
another triangle can be passed as the named vector calendar_year_payments() of it). A
named vector must hold every future calendar year of the prediction and no other year, plus
optionally a total, which must equal the sum over those years; the total row of the
reference is that sum (over the observed years only when observed_to cuts the follow-up).
IBNR claim counts. When pred has an IBNR component and claims carries the reporting
year, the result holds counts, the projected and the realised numbers of IBNR claims by
accident year: projected, the chain-ladder count projection stored by ibnr_reserve()
(ibnr_details$counts_projected) summed over the cells after the valuation date; realised,
the claims of claims absent from the prediction and reported after t with a reporting
delay of at most L (and by observed_to, when given). The count error is a part of the
IBNR error that the pooled mean payment does not explain.
Value
An object of class "reserve_backtest": a list with tables, per component a data
frame with one row per future calendar year plus total (truth, predicted mean,
error = predicted - truth, rel_error = error / truth, NA where the truth is zero; where
the realised amount is negative, a calendar year of net recoveries, rel_error has the sign
opposite to that of error, so an overprediction shows a negative relative error and error
gives the direction; reference_rel_error is defined alike; then
the replicate range lower, upper and whether the truth lies inside it,
inside_range; the range columns are NA for a prediction with a single column, such as a
single-tree fit, whose range is degenerate); the cell-level truth matrices (truth_cells);
the excluded tail (tail); counts (the IBNR claim counts described above, or NULL);
total_years, the calendar years summed in the total rows; observed_to;
reference_component (NULL without a reference); not_scored, the claims and the
payments after t outside the prediction's accident years, the payments after
observed_to and the claims set aside by beyond_L = "drop" with their payments after t
(n_claims_beyond_L, n_payments_beyond_L, amount_beyond_L); the column names used (columns); and the settings.
Examples
data("synthetic_claims", "synthetic_payments", "synthetic_claims_full",
"synthetic_payments_full", package = "baggingReserve")
# a small subset for a quick example: the claims of one type
cl <- synthetic_claims[synthetic_claims$Type == "3", ]
pay <- synthetic_payments[synthetic_payments$Id %in% cl$Id, ]
panel <- claims_panel(cl, pay, t = 12, L = 11, features = c("Age", "AccWeekday"))
res <- ibnr_reserve(predict(reserve_tree(panel, seed = 1)), claim_counts(panel))
# the truth of the same portfolio: all claims of that type, including those reported
# after the valuation date (selecting by the reported claims' identifiers would drop them)
cl_full <- synthetic_claims_full[synthetic_claims_full$Type == "3", ]
pay_full <- synthetic_payments_full[synthetic_payments_full$Id %in% cl_full$Id, ]
backtest(res, cl_full, pay_full, reference = chain_ladder(as_triangle(panel)))
# an extract that ends three years after the valuation date
short <- pay_full[pay_full$EventYear <= 15, ]
backtest(res, cl_full, short, observed_to = 15)$tables$total
Fit bootstrap regression trees and complete reserve replicates
Description
Bootstrap aggregating of the trees of reserve_tree(). For every replicate
b = 1, ..., B and every development year l (its period), a bootstrap sample of the
training claims of that development year is drawn (with replacement, duplicates kept)
and a regression tree is grown on it (by default unpruned, cp = 0, no
cross-validation). Replicate b then completes the payment histories of all reported
claims with its L trees, one per development year, recursively one development year
ahead. The B completed replicates are the output: the point estimate is the bagged
mean of the completed replicate predictions, and their spread describes the variation
under this resampling of the predictions of one set of bootstrap trees (unpruned by
default, unlike the pruned trees of reserve_tree()). It is not a predictive distribution
of the future payments: the numbers of unreported claims are projected once and held
fixed, and with the default completion = "mean" no payment noise is simulated. With
completion = "draw" each replicate is completed once by payments simulated from its own
trees, so its spread adds process variability to this resampling variation. It is still not a
predictive distribution: the estimation part is the variation of one set of (unpruned)
bootstrap trees, a noisier estimator than the bagged mean that is reported; the bootstrap
samples are drawn independently for each development year, not once per claim, so they do
not reproduce how the errors of successive trees are linked through the same claims; and the
IBNR counts stay fixed. The prediction records which case applies (variability), and its
print(), summary(), plot() and backtest() output says so.
Usage
bag_reserve(
panel,
B = 100L,
seed = NULL,
ncores = 1L,
control = rpart::rpart.control(cp = 0, xval = 0),
prune = c("none", "min_xerror"),
oob = TRUE,
keep_trees = FALSE,
batch_size = NULL,
backend = c("auto", "fork", "psock", "serial"),
claim_level = TRUE,
learner = NULL,
completion = c("mean", "draw")
)
## S3 method for class 'bag_reserve'
summary(object, ...)
Arguments
panel |
A claims_panel with |
B |
Number of bootstrap replicates (a positive integer; default 100). |
seed |
Integer seed of the stream allocation. |
ncores |
Number of worker processes (forked on Unix, a socket cluster elsewhere), a
positive integer; default 1, which runs serially. Values above the number of cores detected
are reduced to it, with a message; when |
control |
|
prune |
|
oob |
Compute the out-of-bag diagnostics (default |
keep_trees |
Keep the |
batch_size |
Replicates processed per worker task before OOB votes are returned;
|
backend |
|
claim_level |
Return the claim-level reserve: the mean over the replicates of the
completed claim-by-development-year payment matrices (default |
learner |
|
completion |
|
object |
A |
... |
For |
Details
Completion. The default completion = "mean" is the source article's method, kept so
that the package reproduces it. For totals beyond next year we recommend
completion = "draw": plugging in means is biased for the later years when the occurrence of
a payment depends on the payment history (see the argument completion).
The default B = 100 suffices for the bagged mean: its Monte Carlo standard error is the
replicate standard deviation divided by sqrt(B), small against the errors back-tests
measure. Extreme replicate quantiles and complete out-of-bag coverage need more; the JSS
manuscript uses B = 1000.
A factor level absent from the training claims of a development year gives a warning
(see predict.reserve_tree()). A rare level can also be missing from the bootstrap
sample of one replicate while present in the training claims; that replicate's tree
then treats it as missing without a warning, which is part of the resampling variation.
The out-of-bag (OOB) score of development year l uses, for each training claim, the
trees of the replicates in which it was not sampled (its out-of-bag predictions, or
"votes"), predicts its observed X_{l+1} from its observed history, and compares:
OOB_error = mean((y - OOB)^2) over the covered claims and
OOB_score = 1 - sum_covered (y - OOB)^2 / sum_all (y - mean(y))^2, the denominator
running over all training claims of the development year (Janoušek and
Pešta 2025). It is a diagnostic of the one-year regressions given observed
histories, not a validation of the recursive forecast.
Random numbers come from "L'Ecuyer-CMRG" streams allocated to (replicate, development
year) tasks, so the completed replicates do not depend on ncores, on the batching or on
the scheduling of the workers under the same software; the out-of-bag sums are
accumulated batch by batch and can differ in their last floating-point digits. The
caller's generator kind and state are restored on exit (after the one draw that makes the
seed when seed = NULL). Replicates are streamed: each worker fits
the trees of a replicate, records the OOB votes, aggregates the predicted future cells
and discards the trees unless keep_trees = TRUE.
Value
An object of class c("bag_reserve", "reserve_fit"): a list with the cached
reserve_prediction of the fitted portfolio (prediction), the OOB table (oob,
NULL when oob = FALSE), the trees and bootstrap indices (trees, inbag; NULL
unless keep_trees = TRUE; trees[[b]][[l + 1]] is the tree of replicate b for
development year l), B, L, t, n_train, features, feature_levels, the
control, prune, keep_trees, ncores, backend, elapsed_secs, the RNG
information (rng), the package versions (versions) and the call. Five replicates,
as in the example, illustrate the interface; they are far too few for stable quantiles.
See Also
predict.bag_reserve(), oob_score(), ibnr_reserve()
Examples
data("synthetic_claims", "synthetic_payments", package = "baggingReserve")
# a small subset for a quick example: the claims of one type
cl <- synthetic_claims[synthetic_claims$Type == "3", ]
pay <- synthetic_payments[synthetic_payments$Id %in% cl$Id, ]
panel <- claims_panel(cl, pay, t = 12, L = 11, features = c("Age", "AccWeekday"))
bag <- bag_reserve(panel, B = 5, seed = 2001)
bag
oob_score(bag)
calendar_year_payments(predict(bag), "reported")
# simulated completion, recommended for totals beyond next year
bag_draw <- bag_reserve(panel, B = 5, seed = 2001, completion = "draw")
calendar_year_payments(predict(bag_draw), "reported")[c(1, 11), ]
# another learner for the one-year regressions, here a linear model
lm_learner <- list(fit = function(formula, data) lm(formula, data = data),
predict = function(model, newdata) predict(model, newdata = newdata))
bag_lm <- bag_reserve(panel, B = 5, seed = 2001, learner = lm_learner)
calendar_year_payments(predict(bag_lm), "reported")[c(1, 11), ]
Payments by future calendar year
Description
Sums the predicted future cells by calendar year (acc_year + dev), i.e. along the
diagonals of the run-off triangle: the first row is next year's payments, the last row
the total of all future payments through development year L.
Usage
calendar_year_payments(x, ...)
## S3 method for class 'reserve_prediction'
calendar_year_payments(x, component = c("total", "reported", "ibnr"), ...)
## Default S3 method:
calendar_year_payments(x, ...)
Arguments
x |
A reserve_prediction (or a chain_ladder object, see
|
... |
Not used; an argument given here is an error (for a chain_ladder object, which has a single component, any argument is). |
component |
Component to aggregate; the default |
Value
A matrix with one row per future calendar year (row names are the years) plus a
total row, one column per replicate (the column names of the prediction), and the
attribute "component" holding the component actually aggregated.
Examples
data("synthetic_claims", "synthetic_payments", package = "baggingReserve")
cl <- synthetic_claims[synthetic_claims$Type == "3", ]
pay <- synthetic_payments[synthetic_payments$Id %in% cl$Id, ]
panel <- claims_panel(cl, pay, t = 12, L = 11, features = c("Age", "AccWeekday"))
res <- ibnr_reserve(predict(reserve_tree(panel, seed = 1)), claim_counts(panel))
cbind(reported = calendar_year_payments(res, "reported")[, 1],
ibnr = calendar_year_payments(res, "ibnr")[, 1],
total = calendar_year_payments(res, "total")[, 1])
Future payments of a chain-ladder projection by calendar year
Description
calendar_year_payments() on a chain_ladder object sums the projected incremental cells
after the valuation date by calendar year (accident year plus development year).
Usage
## S3 method for class 'chain_ladder'
calendar_year_payments(x, ...)
Arguments
x |
A chain_ladder object whose columns are development years starting at 0 and whose row names are the accident years. |
... |
Not used; any argument given here is an error. |
Value
Named vector of projected payments per future calendar year and a total.
Examples
data("synthetic_claims", "synthetic_payments", package = "baggingReserve")
cl <- synthetic_claims[synthetic_claims$Type == "3", ]
pay <- synthetic_payments[synthetic_payments$Id %in% cl$Id, ]
panel <- claims_panel(cl, pay, t = 12, L = 11)
calendar_year_payments(chain_ladder(as_triangle(panel)))
Chain-ladder projection of a run-off triangle
Description
Completes a run-off triangle (accident years in rows, development years in columns,
NA for the cells after the valuation date) with development factors estimated from
the observed cells. Two projections are available:
"chain_ladder"the standard chain ladder: factors are ratios of successive column sums of the cumulative triangle over the accident years observed in both columns, and each row is projected from its latest observed cumulative value. Point predictions only, no tail, no uncertainty measure.
"incremental_ratio"the projection used for the baselines of Janoušek and Pešta (2025), the source article: ratios of successive column sums of the incremental triangle, applied multiplicatively to the incremental cells. Provided to apply the source article's convention, not as a reproduction of its results; a factor with a zero denominator is
NaN(zero numerator) orInf(positive numerator), as in the source article's code, and the projected cells that are not finite are listed inundefined.
Usage
chain_ladder(
triangle,
input = NULL,
projection = c("chain_ladder", "incremental_ratio")
)
Arguments
triangle |
Numeric matrix with |
input |
Whether |
projection |
|
Value
An object of class "chain_ladder": a list with factors (one per
development year 1, ..., L, with the number of rows used), the completed
incremental and cumulative triangles, latest, ultimate and outstanding
per accident year, outstanding_total, the observation mask and the method.
Examples
data("synthetic_claims", "synthetic_payments", package = "baggingReserve")
panel <- claims_panel(synthetic_claims, synthetic_payments, t = 12, L = 11,
features = c("Type", "Age", "AccWeekday"))
cl <- chain_ladder(as_triangle(panel))
cl
round(cl$outstanding)
Deprecated: use calendar_year_payments()
Description
chain_ladder_calendar() was exported in version 0.1.1 and is deprecated; it returns
calendar_year_payments() of the chain_ladder object, with a warning.
Usage
chain_ladder_calendar(x)
Arguments
x |
A chain_ladder object. |
Value
As calendar_year_payments.chain_ladder().
Reserves of the individual reported claims
Description
Sums the completed payments of each reported claim over its development years after the
valuation date t (cells with acc_year + dev > t), up to development year L. For a
bagged prediction these are the bagged means; they add up to the mean reported-claim
total of calendar_year_payments().
Usage
claim_reserves(x)
Arguments
x |
A reserve_prediction that holds a claim-level matrix (a single-tree prediction,
or a bagged one with |
Details
The per-claim values are consistent with the aggregate prediction, but they are not a reliable allocation of the reserve between the claims, in particular between open and closed claims. Following the source article, every reported claim is completed, including claims already closed, and the trees see static features and the payment history only, not the settlement status: on the shipped portfolio a third of the predicted reported-claim payments falls on claims closed at the valuation date, and the open claims are underpredicted by about 30% (supplementary material of the JSS manuscript, Section S5). Use the values to aggregate over groups of claims with care, and not as case reserves; allocating a reserve to the open claims needs a model with a settlement layer, such as hirem.
Value
A data frame with one row per reported claim: id, acc_year and reserve.
Examples
data("synthetic_claims", "synthetic_payments", package = "baggingReserve")
cl <- synthetic_claims[synthetic_claims$Type == "3", ]
pay <- synthetic_payments[synthetic_payments$Id %in% cl$Id, ]
panel <- claims_panel(cl, pay, t = 12, L = 11, features = c("Age", "AccWeekday"))
cr <- claim_reserves(predict(reserve_tree(panel, seed = 1)))
summary(cr$reserve)
Build a claim-by-development panel from individual claims and payments
Description
Turns two long tables, one row per claim and one row per payment, into the
observation-masked panel on which the reserving method works: one row per claim
reported by the valuation date t, static features, the reporting delay, and the
incremental payments in development years 0, ..., L. Cells that lie after the
valuation date are NA; observed cells without payments are 0.
Usage
claims_panel(
claims,
payments,
t,
L,
id = c(claims = "Id", payments = "Id"),
acc_year = "AccYear",
rep_year = "RepYear",
rep_delay = NULL,
event_year = "EventYear",
paid = "Paid",
features = character(),
accident_years = NULL,
na_features = c("fail", "surrogate"),
future_payments = c("error", "drop"),
beyond_L = c("error", "drop")
)
## S3 method for class 'claims_panel'
summary(object, ...)
## S3 method for class 'claims_panel'
as.data.frame(x, ...)
Arguments
claims |
Data frame with one row per claim. |
payments |
Data frame with one row per payment (several payments of one claim in the same year are summed). |
t |
Valuation (reserving) date, an integer year on the same grid as the accident
and event years. Claims reported after |
L |
Maximal development year; payments have columns |
id |
Name of the claim identifier column, either one name used in both tables
or |
acc_year |
Name of the accident-year column in |
rep_year |
Name of the reporting-year column in |
rep_delay |
Optional name of a reporting-delay column ( |
event_year |
Name of the payment-year column in |
paid |
Name of the amount column in |
features |
Character vector of static feature columns of |
accident_years |
Optional integer vector of accident years defining the rows of
the aggregate triangles. The default runs from the earliest to the latest accident
year with a reported claim, gaps included. Accident years after the latest one are
not added, because a portfolio in run-off has none; when that latest year is before |
na_features |
|
future_payments |
|
beyond_L |
|
object |
A |
... |
For |
x |
A |
Details
The constructor enforces the valuation date on both tables: claims are kept only if
reported by t (reporting year <= t), payments only if made by t. Claims reported after
t are dropped, and so are claims whose accident year is after t (they cannot be reported
by t); both are counted in n_unreported_dropped. A claim without a reporting year (or
delay) is treated as not reported by t and dropped, with a warning, since a missing date is
more often a data defect than a late report. It preserves
recoveries (negative payments), keeps reported claims without any payment, and
refuses input it cannot interpret: duplicated or missing identifiers, payments on
claims absent from claims, payments before the accident or reporting year,
reporting delays or observed payments beyond L unless beyond_L = "drop" is given, and
payments after t unless future_payments = "drop" is given explicitly (the dropped numbers
and amounts are recorded in the diagnostics). Payments dated before the recorded reporting year (advance
payments, or reporting dates corrected after a first payment) are refused: move such a
payment to the reporting year, or correct the reporting year, before calling the
constructor.
Value
An object of class "claims_panel": a list with elements
- claims
data frame with
id,acc_year,rep_delayand the feature columns- payments
numeric matrix, one row per claim, columns
X0, ..., XL;NAaftert- observed
logical matrix of the same shape,
TRUEwhereacc_year + l <= t- t, L, accident_years, I
valuation date, horizon, accident years, their number
- features, feature_levels
the feature names and the factor levels
- mapping, diagnostics, call
column mapping (carried into the predictions, where
backtest()uses it as its default column names), construction counts, the call- beyond_L_ids
identifiers of the reported claims set aside by
beyond_L = "drop"(empty otherwise)
See Also
as_triangle(), claim_counts(), reserve_tree(), bag_reserve()
Examples
claims <- data.frame(Id = c("a", "b", "c"), AccYear = c(1, 1, 2), RepYear = c(1, 2, 2),
Type = c("x", "y", "x"))
payments <- data.frame(Id = c("a", "a", "a", "b"), EventYear = c(1, 1, 2, 2),
Paid = c(10, 5, -2, 7))
panel <- claims_panel(claims, payments, t = 3, L = 2, features = "Type")
panel
panel$payments
as_triangle(panel)
Payments of the claims incurred but not reported
Description
Adds the IBNR component to a reserve_prediction, following Theorem 1 and
Section 3.6 of Janoušek and Pešta (2025): the numbers of claims still to be
reported are projected from the reporting-count triangle with a chain-ladder-type
method and held fixed across replicates; the payment of such a claim in development
year l is estimated by the pooled average payment of the reported claims in
development year l,
\hat\mu_l = \frac{\sum_i \hat R_{i,l}}{\sum_i \sum_{d \le l,\; i + d \le t} N_{i,d}},
where \hat R_{i,l} are the observed plus predicted payments of the reported
claims (a replicate of the prediction) and N_{i,d} the observed numbers of
claims of accident year i reported with delay d. The IBNR payment in cell
(i, l) is the projected number of claims of accident year i reported
after the valuation date with delay \le l, times \hat\mu_l.
Usage
ibnr_reserve(
pred,
counts = NULL,
projection = c("chain_ladder", "incremental_ratio"),
round = FALSE
)
Arguments
pred |
A reserve_prediction (from |
counts |
The incremental reporting-count triangle of the panel the prediction was
made for. |
projection |
Count projection, |
round |
Round the projected numbers of claims to integers (default |
Details
The estimator assumes that the pooled payment per development year of the reported claims applies to the claims reported late; a payment pattern that depends on the reporting delay or a business mix that differs between early and late reporters invalidates it (see the JSS manuscript, Section 2.4).
An accident year without any reported claim at the valuation date has an all-zero row in
the count triangle, and the chain ladder projects no late-reported claims from it: such
years get no IBNR payments, with a warning. The function has no argument for an external
count projection; when this matters, estimate the IBNR payments of such years outside the
package (for example, as in the formula above, externally projected numbers of late
claims with delay <= l times the pooled mean payments ibnr_details$mu), or leave those
accident years out of the panel (argument accident_years of claims_panel()).
In the last development years \hat\mu_l rests on little information: only the oldest
accident years are observed there, and the other accident years contribute the predictions of
the late trees, which are trained on those few accident years. A few recoveries can then make
\hat\mu_l negative in every replicate (on the claims of type 2 of the shipped portfolio,
for example, \hat\mu_L is negative, and so is the IBNR payment of calendar year 23);
inspect ibnr_details$mu, and ibnr_details$divisor, the reported claims behind it, before
relying on the late cells.
The projected numbers of claims are the same in every replicate, so the spread of the IBNR
component over the replicates comes from the pooled mean payments \hat\mu_l only; it
contains no uncertainty of the count projection.
With projection = "incremental_ratio" and round = TRUE the count projection uses
the source article's incremental-ratio factors and rounding convention: undefined
(NA/NaN) projected counts become zero, an infinite projected count is an error, and
the completed count matrix is rounded once. This applies the source article's
computational conventions; it does not reproduce its numerical results on the shipped
portfolio. The default is the standard chain ladder on cumulative counts without
rounding.
Value
pred with the components ibnr (matrix, future cells by replicates) and
ibnr_details added: the projected count triangle (counts_projected), the
projected numbers of late-reported claims per cell (counts_future_cumulative), the
development factors, the divisor (observed reported-claim counts per development
year), mu (replicates by development years), and the projection settings.
Examples
data("synthetic_claims", "synthetic_payments", package = "baggingReserve")
# a small subset for a quick example: the claims of one type
cl <- synthetic_claims[synthetic_claims$Type == "3", ]
pay <- synthetic_payments[synthetic_payments$Id %in% cl$Id, ]
panel <- claims_panel(cl, pay, t = 12, L = 11, features = c("Age", "AccWeekday"))
tree <- reserve_tree(panel, seed = 1)
res <- ibnr_reserve(predict(tree), claim_counts(panel))
calendar_year_payments(res, "total")
Out-of-bag diagnostics of a bagged fit
Description
Extracts the out-of-bag errors, scores and coverage per development year from a bagged
fit. For development year l, every training claim that was left out of the bootstrap
sample of at least one replicate (a covered claim) receives the average prediction of
those replicates' trees, computed from its observed history. oob_error is the mean
squared error over the covered claims; oob_score is one minus the sum of squared
errors over the covered claims divided by the total sum of squares of the response over
all training claims of the development year (the definition of
Janoušek and Pešta 2025). With B replicates a claim is
covered with probability 1 - (1 - (1 - 1/n)^n)^B, which is also the expected coverage:
about 0.99 at B = 10, so that coverage is essentially complete for B in the hundreds but
not for very small B; the observed coverage = n_scored / n_train reports it, and the
score is NA when no claim is covered or the response has zero variance. Because its
numerator runs over the covered claims only, the score is biased upwards when the
coverage is below one (small B); oob_score_covered is the ordinary coefficient of
determination over the covered claims, which does not have that bias. A negative score
means that the out-of-bag predictions are worse than the mean of the response, which is
common for unpruned bootstrap trees (cp = 0) on noisy late development years;
a larger minbucket or a positive cp in control smooths them. The score is a
diagnostic of the one-year regressions, not a validation of the recursive multi-year
forecast or of the IBNR component.
Usage
oob_score(object)
Arguments
object |
A bag_reserve fit created with |
Value
A data frame with one row per development year and the columns period (the
development-year index), n_train, n_scored (claims with at least one out-of-bag
vote), coverage, mean_votes (mean number of votes among the covered claims),
oob_error, oob_score, response_var and oob_score_covered.
Examples
data("synthetic_claims", "synthetic_payments", package = "baggingReserve")
cl <- synthetic_claims[synthetic_claims$Type == "3", ]
pay <- synthetic_payments[synthetic_payments$Id %in% cl$Id, ]
panel <- claims_panel(cl, pay, t = 12, L = 11, features = c("Age", "AccWeekday"))
bag <- bag_reserve(panel, B = 5, seed = 2026)
oob_score(bag)[, c("period", "n_train", "coverage", "oob_score")]
Plot the out-of-bag scores of a bagged fit
Description
Plot the out-of-bag scores of a bagged fit
Usage
## S3 method for class 'bag_reserve'
plot(x, ...)
Arguments
x |
A bag_reserve fit. |
... |
Passed to |
Value
x, invisibly.
Examples
data("synthetic_claims", "synthetic_payments", package = "baggingReserve")
cl <- synthetic_claims[synthetic_claims$Type == "3", ]
pay <- synthetic_payments[synthetic_payments$Id %in% cl$Id, ]
panel <- claims_panel(cl, pay, t = 12, L = 11, features = c("Age", "AccWeekday"))
plot(bag_reserve(panel, B = 5, seed = 2026))
Plot a reserve prediction
Description
Base-graphics displays of a reserve_prediction:
"calendar"predicted payments per future calendar year: the bagged mean (or the single-tree prediction) as a line, the replicate quantile band shaded when
B > 1, and the realised payments as points whentruthis given."next_year","ultimate"the replicate predictions of next year's payments, or of the total through development year
L: a histogram with a rug forB >= 20, individual points otherwise; vertical lines mark the bagged mean (solid black), the replicate quantiles (dashed grey), thetruth(dot-dashed blue) and anyreferencevalues (dotted green, e.g. a chain-ladder estimate).
Bands and quantiles are empirical quantiles over the columns of the prediction and are
labelled by what these are: the bootstrap replicates of a bagged fit (resampling variation,
with simulated payments as well for completion = "draw") or the simulated completions of a
single-tree fit (process variability given the one fitted model); see the variability of
predict.reserve_tree(). They are neither prediction intervals nor confidence intervals for
the mean.
Usage
## S3 method for class 'reserve_prediction'
plot(
x,
what = c("calendar", "next_year", "ultimate"),
component = c("total", "reported", "ibnr"),
probs = c(0.025, 0.975),
truth = NULL,
reference = NULL,
main = NULL,
...,
type = 7L
)
Arguments
x |
|
what |
Which display: |
component |
|
probs |
Two increasing probabilities in |
truth |
Optional realised payments (default |
reference |
Optional named numeric vector of reference estimates drawn as dotted
vertical lines ( |
main |
Title; |
... |
Further graphical arguments passed to the underlying plotting function
( |
type |
Quantile algorithm for the band and the quantile lines, as in
|
Value
x, invisibly.
Examples
data("synthetic_claims", "synthetic_payments", "synthetic_claims_full",
"synthetic_payments_full", package = "baggingReserve")
cl <- synthetic_claims[synthetic_claims$Type == "3", ]
pay <- synthetic_payments[synthetic_payments$Id %in% cl$Id, ]
panel <- claims_panel(cl, pay, t = 12, L = 11, features = c("Age", "AccWeekday"))
bag <- bag_reserve(panel, B = 20, seed = 2026)
res <- ibnr_reserve(predict(bag), claim_counts(panel))
cl_full <- synthetic_claims_full[synthetic_claims_full$Type == "3", ]
bt <- backtest(res, cl_full, synthetic_payments_full[synthetic_payments_full$Id %in% cl_full$Id, ])
plot(res, "calendar", truth = setNames(bt$tables$total$truth, bt$tables$total$calendar))
plot(res, "next_year", truth = bt$tables$total$truth[1])
plot(bag)
Plot the tree of one development year of a fit
Description
Plot the tree of one development year of a fit
Usage
## S3 method for class 'reserve_tree'
plot(x, period = 0L, ...)
Arguments
x |
A reserve_tree fit. |
period |
Development year |
... |
Passed to |
Value
x, invisibly.
Examples
data("synthetic_claims", "synthetic_payments", package = "baggingReserve")
cl <- synthetic_claims[synthetic_claims$Type == "3", ]
pay <- synthetic_payments[synthetic_payments$Id %in% cl$Id, ]
panel <- claims_panel(cl, pay, t = 12, L = 11, features = c("Age", "AccWeekday"))
plot(reserve_tree(panel, seed = 1), period = 0)
Predictions of a bagged fit
Description
Returns the completed replicate predictions, or their bagged mean, for the fitted
portfolio or for compatible new data. Without newdata, the reserve_prediction
cached at fitting time is returned: one column per bootstrap replicate
(type = "replicates") or their mean (type = "mean", which averages the completed
replicates, never the trees development year by development year). With newdata, the
stored trees complete another claims_panel; this needs keep_trees = TRUE and
completion = "mean" at fitting time (a fit with completion = "draw" does not keep the leaf
responses its simulation draws from, so it predicts only the fitted portfolio).
Usage
## S3 method for class 'bag_reserve'
predict(object, newdata = NULL, type = c("replicates", "mean"), ...)
Arguments
object |
A bag_reserve fit. |
newdata |
|
type |
|
... |
Only |
Details
The completion (plugged-in means or simulated payments) of a bagged fit is chosen when fitting,
with bag_reserve(completion = ), not here: completion is accepted only when it restates the
fit's own choice, and any other argument (R, seed, a misspelt type) is an error, so that
no call silently returns another estimator than the one asked for.
Value
A reserve_prediction as documented in predict.reserve_tree(), with B
columns of predicted reported-claim payments and, unless the fit was made with
claim_level = FALSE, the claim-level matrix averaged over the replicates
(claim_level; its future cells sum to the bagged mean of the reported component). With type = "mean" the object holds one column named
"mean", B = 1 and the estimator label "bagging (mean of replicates)"; the
replicate distribution is then no longer available.
Examples
data("synthetic_claims", "synthetic_payments", package = "baggingReserve")
cl <- synthetic_claims[synthetic_claims$Type == "3", ]
pay <- synthetic_payments[synthetic_payments$Id %in% cl$Id, ]
panel <- claims_panel(cl, pay, t = 12, L = 11, features = c("Age", "AccWeekday"))
bag <- bag_reserve(panel, B = 5, seed = 2026, keep_trees = TRUE)
pred <- predict(bag) # five columns, one per replicate
pred_mean <- predict(bag, type = "mean") # one column, the bagged mean
calendar_year_payments(pred)[c(1, nrow(calendar_year_payments(pred))), ]
# the same trees applied to another panel of the same layout (the claims of type 4)
cl4 <- synthetic_claims[synthetic_claims$Type == "4", ]
panel4 <- claims_panel(cl4, synthetic_payments[synthetic_payments$Id %in% cl4$Id, ],
t = 12, L = 11, features = c("Age", "AccWeekday"))
predict(bag, newdata = panel4)
Predicted future payments of the reported claims
Description
predict() on a reserve_tree completes the payment histories of all claims
reported by the valuation date, recursively one development year ahead: the tree for
development year l predicts X_{l+1} from the features, the reporting delay and
X_0, ..., X_l, where the cells after the valuation date among X_0, ..., X_l are
the predictions of the earlier trees. Following the source article, every reported
claim is completed, including claims recorded as closed; the claim status is not a
covariate unless it is named in features. Do not name a status recorded at the valuation
date as a feature without care: the panel holds it as a static feature, but in the earlier
development years from which the trees learn it was not yet known (a claim closed at t
was open in the years in which it paid), so the trees learn from information that the
claims did not have at the time (see the JSS manuscript on open and closed claims).
Usage
## S3 method for class 'reserve_tree'
predict(
object,
newdata = NULL,
type = c("replicates", "mean"),
completion = c("mean", "draw"),
R = 100L,
seed = NULL,
...
)
Arguments
object |
A fitted reserve_tree. |
newdata |
|
type |
|
completion |
|
R |
Number of simulated completions for |
seed |
Seed for the simulated completions, set with R's default generator kinds as in
|
... |
Not used; any argument given here (a misspelt one, say) is an error. |
Details
The result is a reserve_prediction: the observed incremental triangle, the
coordinates of the cells after the valuation date, and one column per replicate with
the predicted reported-claim payments in those cells. A single-tree fit gives one
prediction column; bag_reserve() gives B.
Value
An object of class "reserve_prediction", a list with
- observed
incremental payments by accident year and development year,
NAafter the valuation date- cells
data frame of the future cells:
acc_year,dev,calendar(acc_year + dev),index(position in the triangle, column-major)- reported
matrix, one row per future cell and one column per replicate, of predicted payments of the reported claims
- ibnr, ibnr_details
NULLuntilibnr_reserve()adds the IBNR component and its details (projected counts, factors, pooled mean payments)- counts
the incremental reporting-count triangle of the panel (
claim_counts()), used byibnr_reserve()by default- claim_level
the completed claim-level payment matrix, one row per reported claim (columns
X0, ..., XL): for a single-tree fit its completion, for a bagged fit the mean of the completed replicates; observed cells keep their observed values. Its future cells add up to the reported-claim prediction, but they are not a reliable allocation of that reserve between the claims: every reported claim is completed, open or closed, and the trees do not know the settlement status, so on the shipped portfolio a third of the reserve falls on claims already closed while the open claims are underpredicted (supplementary material of the JSS manuscript, Section S5). Seeclaim_reserves(); for case-level allocation a model with a settlement layer, such as hirem, is needed.- claim_ids, replicate_ids
identifiers of the completed claims and of the columns
- claim_acc_years, claim_rep_delays
the accident year and reporting delay of every completed claim, which
backtest()checks against its truth tables- mapping, beyond_L_ids
the column names of the panel's input tables, which
backtest()uses as its defaults, and the claims the panel set aside withbeyond_L = "drop"- t, L, accident_years, B, estimator, call
metadata;
estimatoris"tree","tree (R simulated completions)","bagging"or"bagging (simulated completion)", with" (mean of replicates)"(bagging) or"tree (mean of R simulated completions)"aftertype = "mean"- completion, variability
the completion mode (
"mean"or"draw") and the sources of variability among the columns: none for a single-tree fit withcompletion = "mean";"simulated payments"for theRsimulated completions of one fitted model (process variability given the fitted model, no estimation variability);"bootstrap resampling"for the replicates ofbag_reserve(); both for a bagged fit withcompletion = "draw", whose replicatebis one completion simulated from theb-th set of bootstrap trees (its estimation part is the variation of one set of unpruned bootstrap trees, with the samples drawn per development year, not that of the bagged mean). Theprint(),summary(),plot()andbacktest()methods describe the columns accordingly. In no case do the columns contain the uncertainty of the IBNR claim counts or of the model itself.- provenance
the fitting settings, the fitting random-number information (
rng), the package versions, thecompletionand, for simulated completion,Rand the random-number information of the simulation (completion_rng: itsseed, or withseed = NULLthe generator state the draws started from)
See Also
as_triangle(), calendar_year_payments(), summary.reserve_prediction(),
plot.reserve_prediction(), backtest()
Examples
data("synthetic_claims", "synthetic_payments", package = "baggingReserve")
# fit on the claims of one type, then complete another portfolio with the same layout:
# the same features in the same order; factor levels must be known to the fit
cl <- synthetic_claims[synthetic_claims$Type == "3", ]
pay <- synthetic_payments[synthetic_payments$Id %in% cl$Id, ]
panel <- claims_panel(cl, pay, t = 12, L = 11, features = c("Age", "AccWeekday"))
fit <- reserve_tree(panel, seed = 1)
cl2 <- synthetic_claims[synthetic_claims$Type == "4" & synthetic_claims$AccWeekday != "Sun", ]
pay2 <- synthetic_payments[synthetic_payments$Id %in% cl2$Id, ]
other <- claims_panel(cl2, pay2, t = 12, L = 11, features = c("Age", "AccWeekday"))
levels(other$claims$AccWeekday) # a subset of the fit's levels is re-levelled
pred2 <- predict(fit, newdata = other)
calendar_year_payments(pred2)[c(1, 10), ]
Fit one regression tree per development year
Description
Fits a single-tree reserving model: for every development year l = 0, ..., L - 1
(called period in the package's arguments and output), a regression tree
(rpart, ANOVA method) that predicts the payment of the next development year,
X_{l+1}, of a reported claim from its static features, its reporting delay and its
payments X_0, ..., X_l. The tree for development year l is trained on the claims
reported by development year l whose payment in development year l + 1 is already
observed at the valuation date (reporting delay <= l and acc_year + l + 1 <= t);
every fitted development year needs at least two such claims. Each tree is grown with
the given control (default cp = 0 with ten-fold cross-validation) and, by default,
pruned at the smallest cross-validated error (the simplest tree on ties), the pruning rule of
Janoušek and Pešta (2025), the source article (deliberately not
rpart's customary one-standard-error rule, which prunes more and would turn more of the
late, mostly-zero development years into single-leaf trees).
Usage
reserve_tree(
panel,
control = rpart::rpart.control(cp = 0, xval = 10),
prune = c("min_xerror", "none"),
seed = NULL,
learner = NULL
)
## S3 method for class 'reserve_tree'
summary(object, ...)
Arguments
panel |
A claims_panel with |
control |
An |
prune |
|
seed |
Optional integer seed for the cross-validation folds, which are random. It is
set with |
learner |
|
object |
A |
... |
Not used; an argument given here is an error. |
Details
predict() completes the payment histories of all reported claims recursively one
development year ahead and returns a reserve_prediction with a single prediction
column; see predict.reserve_tree().
Value
An object of class c("reserve_tree", "reserve_fit"): a list with the
fitted trees (fits, one per development year, fits[[l + 1]] for development year
l), the chosen complexity parameters (cp_chosen, NA where no pruning was
applied), training sizes (n_train), prune, features, feature_levels, L,
t, the panel, the control, the RNG information (rng), the package versions
(versions) and the call. summary() returns the same information with a table of
leaves and depths per development year. With a user-supplied learner, fits holds the
learner's models, whatever fit() returned; print() and summary() do not inspect them
and report the training sizes per development year (the tree columns and the control
apply to the default learner only).
See Also
bag_reserve() for the bagged version, predict.reserve_tree().
Examples
data("synthetic_claims", "synthetic_payments", package = "baggingReserve")
# a small subset for a quick example: the claims of one type
cl <- synthetic_claims[synthetic_claims$Type == "3", ]
pay <- synthetic_payments[synthetic_payments$Id %in% cl$Id, ]
panel <- claims_panel(cl, pay, t = 12, L = 11, features = c("Age", "AccWeekday"))
tree <- reserve_tree(panel, seed = 1)
tree
pred <- predict(tree)
calendar_year_payments(pred)
Summarise a reserve prediction over its replicates
Description
Summarises the future payments by component and calendar year across the retained
replicate predictions: for every available component (reported-claim payments, IBNR
component, total) and every future calendar year plus the total through development
year L, the mean, standard deviation and empirical quantiles across the replicates.
What the quantiles describe depends on the prediction, whose variability records it (see
predict.reserve_tree()): for the replicates of bag_reserve(), each made by the trees of one
bootstrap sample (unpruned by default, unlike the pruned trees of reserve_tree()), the
resampling variation of the bagged trees; for the simulated completions of a single-tree fit
(predict(fit, completion = "draw")), the process variability of payments simulated from that
one fitted model, without estimation variability; for a bagged fit with
completion = "draw", the resampling variation of one set of bootstrap trees (samples drawn
per development year) together with one simulated completion from each. They are in no case
prediction intervals for the future payments
(the IBNR claim counts are held fixed and the model is taken as given) nor confidence
intervals for the mean. The printed summary states which case applies.
Usage
## S3 method for class 'reserve_prediction'
summary(
object,
probs = c(0.025, 0.25, 0.5, 0.75, 0.975, 0.995),
type = 7L,
...
)
Arguments
object |
|
probs |
Probabilities of the empirical quantiles (numeric in |
type |
Quantile type passed to |
... |
Not used; an argument given here (e.g. a misspelt |
Value
An object of class "summary.reserve_prediction": a list with tables, a
named list of one matrix per component (rows: future calendar years and total;
columns: mean, sd, min, the quantiles, max; sd is NA and only mean is
printed when the prediction has a single column), plus B, estimator, completion,
variability, probs, type, t and L.
Examples
data("synthetic_claims", "synthetic_payments", package = "baggingReserve")
cl <- synthetic_claims[synthetic_claims$Type == "3", ]
pay <- synthetic_payments[synthetic_payments$Id %in% cl$Id, ]
panel <- claims_panel(cl, pay, t = 12, L = 11, features = c("Age", "AccWeekday"))
res <- ibnr_reserve(predict(bag_reserve(panel, B = 5, seed = 2026)), claim_counts(panel))
summary(res, probs = c(0.1, 0.5, 0.9))$tables$total
Synthetic individual claims observed at the reserving date
Description
A synthetic individual claims portfolio generated with the Wang and
Wüthrich (2022) individual claims generator (built on SynthETIC)
on a yearly grid with seed 2026: six claim types, twelve accident years, reporting
delays of up to eight years. It is a fresh portfolio from the same simulator family as the
one analysed by Janoušek and Pešta (2025), not that article's
data (which used another seed and ten accident years). synthetic_claims
holds what the insurer sees at the reserving date t = 12: one row per
reported claim (closed or RBNS), with the settlement information censored at
t = 12. The claims that are still IBNR at that date are absent here and
present only in synthetic_claims_full. Only quantities observable at t = 12
are kept: the generated ultimate claim amount and total number of payments are in
synthetic_claims_full alone, so that nothing unobservable can leak into a fit.
Usage
synthetic_claims
Format
A data frame with one row per reported claim and the columns
- Id
claim identifier, integer, consistent across the four datasets
- Type
claim type, factor with levels
1–6- Age
age of the insured at the accident, years
- AccDate
accident time, continuous, in years since the start of year 1
- AccYear
accident year
i,1–12- AccWeekday
weekday of the accident, factor
- RepDate
reporting time, continuous, in years
- RepYear
reporting year
i + d- RepDelDays
reporting delay in the generator's day count (see above; not used)
- RepDel
reporting delay in years,
d =RepYear - AccYear,0–8- SetYear
settlement year;
NAif the claim is open (RBNS) att = 12- SetDel
settlement delay in years,
SetYear - RepYear;NAif open- Status
factor,
ClosedorRBNSatt = 12- CumPaid
cumulative amount paid up to
t = 12
Details
A word on the time grid. The generator was written for a monthly grid; run with
time_unit = 1 its yearly indices AccYear, RepYear, SetYear
and EventYear are the development grid the method uses, and the reporting delay
RepDel = RepYear - AccYear runs from 0 to 8 years. The generator's calendar
conversion, however, treats one time unit as a month, so RepDelDays is the reporting
delay in days divided by twelve and the three-year cap the generator
places on it does not bind. Both columns are kept as generated; the method only uses
the yearly grid.
Regenerate with data-raw/make_synthetic_claims.R; data-raw/GENERATION.txt records the
generator call and the software versions used.
Source
Wang M, Wüthrich MV (2022). Individual claims generator for claims reserving studies: data simulation.R. SSRN 4127073. doi:10.2139/ssrn.4127073. Code: https://github.com/actuarial-data-science/PackageIndividualClaimsSimulator (GPL-3; formerly JSchelldorfer/IndividualClaimsSimulator, the location the vendored copy was taken from).
See Also
synthetic_payments, synthetic_claims_full, synthetic_payments_full
Other synthetic data:
synthetic_claims_full,
synthetic_payments,
synthetic_payments_full
Examples
data("synthetic_claims", package = "baggingReserve")
str(synthetic_claims)
table(synthetic_claims$AccYear)
Synthetic individual claims: the generated truth
Description
Every generated claim, including those that are IBNR at t = 12, with the
uncensored settlement information. Used to back-test predictions of the
outstanding liabilities. IBNR claims at t = 12 are those whose Id is not
in synthetic_claims.
Usage
synthetic_claims_full
Format
A data frame with one row per generated claim and the columns
- Id, Type, Age, AccDate, AccYear, AccWeekday, RepDate, RepYear, RepDelDays, RepDel
as in synthetic_claims; here the reporting information is uncensored, so IBNR claims have
RepYear > 12- SetYear
settlement year, uncensored
- SetDel
settlement delay in years,
SetYear - RepYear, uncensored- Ultimate
ultimate claim amount as generated (not observable in practice)
- PayCount
total number of payments as generated (not observable in practice)
- ReopenInd
1if the claim was reopened after its first settlement
Source
Wang M, Wüthrich MV (2022). Individual claims generator for claims reserving studies: data simulation.R. SSRN 4127073. doi:10.2139/ssrn.4127073. Code: https://github.com/actuarial-data-science/PackageIndividualClaimsSimulator (GPL-3; formerly JSchelldorfer/IndividualClaimsSimulator, the location the vendored copy was taken from).
See Also
Other synthetic data:
synthetic_claims,
synthetic_payments,
synthetic_payments_full
Examples
data("synthetic_claims", "synthetic_claims_full", package = "baggingReserve")
# claims reported after the valuation date t = 12 (the IBNR claims)
sum(!synthetic_claims_full$Id %in% synthetic_claims$Id)
Synthetic claim payments observed at the reserving date
Description
One row per payment event up to and including year 12 on the claims in
synthetic_claims. Events are yearly-gridded; several events of one claim can fall
into the same year, negative amounts (recoveries) occur, and a few events carry no
payment. Summing Paid per claim and year gives the incremental payments the method
works with.
Usage
synthetic_payments
Format
A data frame with the columns
- Id
claim identifier, matches synthetic_claims
- EventId
running number of the event within the claim
- EventYear
calendar year of the event,
i + d + k- Paid
amount paid (negative for recoveries, zero for non-payment events)
- PayInd
1for a payment,0for a recovery or a zero-amount event, as generated- OpenInd
1if the claim is still open after the event, as generated
Source
Wang M, Wüthrich MV (2022). Individual claims generator for claims reserving studies: data simulation.R. SSRN 4127073. doi:10.2139/ssrn.4127073. Code: https://github.com/actuarial-data-science/PackageIndividualClaimsSimulator (GPL-3; formerly JSchelldorfer/IndividualClaimsSimulator, the location the vendored copy was taken from).
See Also
Other synthetic data:
synthetic_claims,
synthetic_claims_full,
synthetic_payments_full
Examples
data("synthetic_payments", package = "baggingReserve")
head(synthetic_payments)
tapply(synthetic_payments$Paid, synthetic_payments$EventYear, sum)
Synthetic claim payments: the generated truth
Description
Every generated payment, past and future, for the claims in
synthetic_claims_full. Same columns as synthetic_payments. One generated event far
beyond the simulated horizon carried no amount and is dropped (the count is recorded in
data-raw/GENERATION.txt).
Usage
synthetic_payments_full
Format
A data frame with the columns of synthetic_payments.
Source
Wang M, Wüthrich MV (2022). Individual claims generator for claims reserving studies: data simulation.R. SSRN 4127073. doi:10.2139/ssrn.4127073. Code: https://github.com/actuarial-data-science/PackageIndividualClaimsSimulator (GPL-3; formerly JSchelldorfer/IndividualClaimsSimulator, the location the vendored copy was taken from).
See Also
Other synthetic data:
synthetic_claims,
synthetic_claims_full,
synthetic_payments
Examples
data("synthetic_payments_full", package = "baggingReserve")
# realised payments after the valuation date t = 12, by calendar year
fut <- synthetic_payments_full[synthetic_payments_full$EventYear > 12, ]
tapply(fut$Paid, fut$EventYear, sum)
A two-part learner: whether a claim pays, and how much
Description
Builds a learner for reserve_tree() and bag_reserve() from two models: one for the
probability that a claim makes a non-zero payment in the development year, one for the
amount of a non-zero payment. Most payments of the later development years are exactly
zero; a single smooth model then predicts a small positive amount for every claim, and the
recursive completion carries these forward. Separating occurrence from size, and simulating
the occurrence (predict(fit, completion = "draw")), avoids this.
Usage
two_part_learner(occurrence, size, min_nonzero = 20L)
Arguments
occurrence |
A list with The outputs of both models are checked separately, before they are combined, against the
learner contract of |
size |
A list with |
min_nonzero |
When fewer non-zero payments are available in a development year, the size model is replaced by their mean (default 20). |
Details
The prediction is p * s, the probability of a payment times the predicted amount; the
simulation, used by completion = "draw", draws the payment indicator and returns s when
it is 1, else 0. Only the occurrence is then random: the amount of a simulated payment is the
size model's predicted mean, so the simulated completions contain no variation of the payment
size given its predictors, unless size has a simulate(model, newdata) function, which is
then used for the amount of every simulated payment.
Value
A list with functions fit, predict and simulate, to pass as learner.
Examples
data("synthetic_claims", "synthetic_payments", package = "baggingReserve")
cl <- synthetic_claims[synthetic_claims$Type == "3", ]
pay <- synthetic_payments[synthetic_payments$Id %in% cl$Id, ]
panel <- claims_panel(cl, pay, t = 12, L = 11, features = c("Age", "AccWeekday"))
logistic <- list(
fit = function(formula, data) stats::glm(formula, data = data, family = stats::binomial()),
predict = function(model, newdata) stats::predict(model, newdata, type = "response"))
linear <- list(
fit = function(formula, data) stats::lm(formula, data = data),
predict = function(model, newdata) stats::predict(model, newdata))
fit <- reserve_tree(panel, learner = two_part_learner(logistic, linear))
calendar_year_payments(predict(fit))["total", ]
calendar_year_payments(predict(fit, completion = "draw", R = 20, seed = 1))["total", ]