Version 0.2.12 [2021-01-05]

Version 0.2.11 [2020-02-10]

Version 0.2.10 [2019-05-27]

Thanks to Marco De Virgilis.

Version 0.2.9 [2018-12-06]

Version 0.2.8 [2018-11-10]

Version 0.2.7 [2018-10-16]

Version 0.2.6 [2018-05-29]

Version 0.2.5 [2017-10-19]

Version 0.2.4 [2017-01-01]

Version 0.2.3 [2016-10-20]

Changes

NEWS file

Moved NEWS file into RMarkdown package vignette format.

Triangles may now have non-numeric rownames

Previously it was required that the row and column names of a triangle be convertible to numeric, although that “requirement” did not always cause a problem. For example, the following sets the rownames of GenIns to the beginning Date of the accident year.

x <- GenIns
rownames(x) <- paste0(2001:2010, "-01-01")
x
##             dev
## origin           1    2    3    4    5    6    7    8    9   10
##   2001-01-01 357.8 1125 1735 2218 2746 3320 3466 3606 3834 3901
##   2002-01-01 352.1 1236 2170 3353 3799 4120 4648 4914 5339   NA
##   2003-01-01 290.5 1292 2219 3235 3986 4133 4629 4909   NA   NA
##   2004-01-01 310.6 1419 2195 3757 4030 4382 4588   NA   NA   NA
##   2005-01-01 443.2 1136 2128 2898 3403 3873   NA   NA   NA   NA
##   2006-01-01 396.1 1333 2181 2986 3692   NA   NA   NA   NA   NA
##   2007-01-01 440.8 1288 2420 3483   NA   NA   NA   NA   NA   NA
##   2008-01-01 359.5 1421 2864   NA   NA   NA   NA   NA   NA   NA
##   2009-01-01 376.7 1363   NA   NA   NA   NA   NA   NA   NA   NA
##   2010-01-01 344.0   NA   NA   NA   NA   NA   NA   NA   NA   NA

A plot with the lattice=TRUE option, which previously would blow up, now displays with nice headings.

plot(x, lattice=TRUE)

It can often be useful to have “origin” values that are not necessarily convertible to numeric. For example, suppose you have a table of claim detail at various evaluation dates. Invariably, such a table will have a Date field holding the date of loss. It would be nice to be able to summarize that data by accident year “cuts”. It turns out there’s a builtin function in R that will get you most of the way there. It’s called ‘cut’.

Here we take the GenIns data in long format and generate 50 claims per accident period. We assign each claim a random date within the year. The incurred (or paid) “value” given is a random perturbation of one-fiftieth of GenInsLong$value.

We accumulate the detail into an accident year triangle using ChainLadder’s as.triangle method. The summarized triangle displayed at the end is very similar to GenIns, and has informative row labels.

x <- GenInsLong
# start off y with x's headings
y <- x[0,]
names(y)[1] <- "lossdate"
set.seed(1234)
n = 50 # number of simulated claims per accident perior
for (i in 1:nrow(x)) {
  y <- rbind(y,
             data.frame(
               lossdate = as.Date(
                 as.numeric(as.Date(paste0(x[i, "accyear"]+2000, "-01-01"))) +
                   round(runif(n, 0, 364),0), origin = "1970-01-01"),
               devyear = x[i, "devyear"],
               incurred.claims = rnorm(n, mean = x[i, "incurred claims"] / n,
                                         sd = x[i, "incurred claims"]/(10*n))
             ))
}
# here's the magic cut
y$ay <- cut(y$lossdate, breaks = "years")
# this summarized triangle is very similar to GenIns
as.triangle(y, origin = "ay", dev = "devyear", value = "incurred.claims")
##             devyear
## ay                1       2       3       4       5       6       7       8
##   2001-01-01 349741 1109368 1737850 2265706 2749056 3318464 3469142 3549578
##   2002-01-01 352821 1245621 2132200 3377061 3820987 4148933 4610189 4891852
##   2003-01-01 296548 1275881 2198221 3235844 3944931 4113276 4623159 4900318
##   2004-01-01 313669 1392038 2171462 3774168 4035879 4461897 4661352      NA
##   2005-01-01 443941 1138787 2190873 2905444 3371444 3849587      NA      NA
##   2006-01-01 391527 1324732 2230006 3000719 3742811      NA      NA      NA
##   2007-01-01 446942 1292116 2416001 3404734      NA      NA      NA      NA
##   2008-01-01 349330 1425022 2844242      NA      NA      NA      NA      NA
##   2009-01-01 369893 1368242      NA      NA      NA      NA      NA      NA
##   2010-01-01 346493      NA      NA      NA      NA      NA      NA      NA
##             devyear
## ay                 9      10
##   2001-01-01 3769684 3980606
##   2002-01-01 5311927      NA
##   2003-01-01      NA      NA
##   2004-01-01      NA      NA
##   2005-01-01      NA      NA
##   2006-01-01      NA      NA
##   2007-01-01      NA      NA
##   2008-01-01      NA      NA
##   2009-01-01      NA      NA
##   2010-01-01      NA      NA

The user is encouraged to experiment with other cut’s – e.g., breaks = "quarters" will generate accident quarter triangles.

New as.LongTriangle function

A new function, as.LongTriangle, will convert a triangle from “wide” (matrix) format to “long” (data.frame) format. This differs from ChainLadder’s as.data.frame.triangle method in that the rownames and colnames of Triangle are stored as factors. This feature can be particularly important when plotting a triangle because the order of the “origin” and “dev” values is important.

Additionally, the columns of the resulting data frame may be renamed from the default values (“origin”, “dev”, and “value”) using the “varnames” argument for “origin”/“dev” and the “value.name” argument for “value”.

In the following example, the GenIns triangle in ChainLadder is converted to a data.frame with non-default names:

GenLong <- as.LongTriangle(GenIns, varnames = c("accident year", "development age"),
                           value.name = "Incurred Loss")
head(GenLong)
##   accident year development age Incurred Loss
## 1             1               1         357.8
## 2             2               1         352.1
## 3             3               1         290.5
## 4             4               1         310.6
## 5             5               1         443.2
## 6             6               1         396.1

In the following plot, the last accident year and the last development age are shown last, rather than second as they would have been if displayed alphabetically (ggplot’s default for character data):

library(ggplot2)
ggplot(GenLong, aes(x=`development age`, y = `Incurred Loss`,
                    group = `accident year`, color = `accident year`)) +
  geom_line()

glmReserve “exposure” attribute may now have names

Previously, when an “exposure” attribute was assigned to a triangle for use with glmReserve, it was assumed/expected that the user would supply the values in the same order as the accident years. Then, behind the scenes, glmReserve would use an arithmetic formula to match the exposure with the appropriate accident year using the numeric “origin” values after the triangle had been converted to long format.

glmReserve now allows for “exposure” to have “names” that coincide with the rownames of the triangle, which are used to match to origin in long format. Here is an example, newly found in ?glmReserve.

  GenIns2 <- GenIns
  rownames(GenIns2) <- paste0(2001:2010, "-01-01")
  expos <- (7 + 1:10 * 0.4) * 10
  names(expos) <- rownames(GenIns2)
  attr(GenIns2, "exposure") <- expos
  glmReserve(GenIns2)
##            Latest Dev.To.Date Ultimate  IBNR    S.E     CV
## 2002-01-01   5339     0.98252     5434    95  110.1 1.1589
## 2003-01-01   4909     0.91263     5379   470  216.0 0.4597
## 2004-01-01   4588     0.86599     5298   710  260.9 0.3674
## 2005-01-01   3873     0.79725     4858   985  303.6 0.3082
## 2006-01-01   3692     0.72235     5111  1419  375.0 0.2643
## 2007-01-01   3483     0.61527     5661  2178  495.4 0.2274
## 2008-01-01   2864     0.42221     6784  3920  790.0 0.2015
## 2009-01-01   1363     0.24162     5642  4279 1046.5 0.2446
## 2010-01-01    344     0.06922     4970  4626 1980.1 0.4280
## total       30457     0.61982    49138 18681 2945.7 0.1577

glmReserve adds support for negative binomial GLM

The glmReserve function now supports the negative binomial GLM, a more natural way to model over-dispersion in count data. The model is fitted through the glm.nb function from the MASS package.

To fit the negative binomial GLM to the loss triangle, simply set nb = TRUE in calling the glmReserve function:

(fit6 <- glmReserve(GenIns, nb = TRUE))
##       Latest Dev.To.Date Ultimate  IBNR     S.E     CV
## 2       5339     0.98288     5432    93   39.61 0.4260
## 3       4909     0.91655     5356   447  133.66 0.2990
## 4       4588     0.88197     5202   614  148.48 0.2418
## 5       3873     0.79594     4866   993  211.05 0.2125
## 6       3692     0.71771     5144  1452  290.03 0.1997
## 7       3483     0.61440     5669  2186  433.05 0.1981
## 8       2864     0.43837     6534  3670  772.92 0.2106
## 9       1363     0.24826     5491  4128  967.76 0.2344
## 10       344     0.07076     4862  4518 1380.14 0.3055
## total  30457     0.62724    48557 18100 2232.92 0.1234

New unit tests

New files in the /inst/unittests/ folder can be used for future enhancements

  • runit.Triangles.R for Triangles.R
  • runit.glmReserve.R for glmReserve.R

Contributors of new contributions to those R files are encouraged to utilize those runit scripts for testing, and, of course, add other runit scripts as warrantted.

Clarified warnings issued by MackChainLadder

By default, R’s lm method generates a warning when it detects an “essentially perfect fit”. This can happen when one column of a triangle is identical to the previous column; i.e., when all link ratios in a column are the same. In the example below, the second column is a fixed constant, 1.05, times the first column. ChainLadder previously issued the lm warning below.

x <- matrix(byrow = TRUE, nrow = 4, ncol = 4, 
            dimnames = list(origin = LETTERS[1:4], dev = 1:4),
            data = c(
              100, 105, 106, 106.5,
              200, 210, 211, NA,
              300, 315, NA, NA,
              400, NA, NA, NA)
            )
mcl <- MackChainLadder(x, est.sigma = "Mack")

Warning messages:
1: In summary.lm(x) : essentially perfect fit: summary may be unreliable
2: In summary.lm(x) : essentially perfect fit: summary may be unreliable
3: In summary.lm(x) : essentially perfect fit: summary may be unreliable

which may have raised a concern with the user when none was warranted.

Now ChainLadder issues an “informational warning”:

mcl <- MackChainLadder(x, est.s