Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simple and and vol-weighted averages for "non-square" triangules #36

Open
actuaryactually opened this issue May 17, 2017 · 8 comments
Open

Comments

@actuaryactually
Copy link

actuaryactually commented May 17, 2017

If I want a volume all, or simple all set of link ratios a call to ata(paid.tri) works fine (where paid.tri = cumulative paid development triangle).

If I want to create a volume-weighted fit on the last five years worth of LDF factors I have found that I can utilise the weights argument as follows: chainladder(paid.tri,weights=wgt.vol.5), where I supply an input triangle of weights.

For "annual-annual" triangles, a five-year weighted average can be achieved using this method provided that the user supplies a weights triangle where the latest 6 diagonals are set = 1 and the balance are NAs. However, when I try the same on a annual/quarterly input triangle (i.e. non square) it fails and returns the following...

"Error in checkTriangle(Triangle) :
Number of origin periods, 10, is less than the number of development periods, 39."

By annual/quarterly I mean annual origin cohorts but tracking development progress quarter on quarter, which is common for Lloyd's and reinsurance companies. In my case I had data running out to 9.75 development years, and had 10 origin years. The error message therefore makes sense but essentially indicates that the chainladder algorithm requires a square-input matrix.

I'm wondering if there is a smart way to overcome this? Personally i know that a lot of people would find it easier to adopt Chainladder if they could easily derive simple and volume-weighted averages using a call to a pre-built UDF, e.g. chainladder(paid.tri, no.diag=5, TypeOfAverage="Vol")

I've found that a n-year simple average can be achieved using the following and I think this would be a good addition to your help file even if you don't recast the "chainladder"function.

######################################
require(zoo)

#quick UDF to get rid of NAs:
link_ratio_simple_n_yrs<-function(x,no.diag){
mean(tail(na.trim(x,sides="both"),no.diag))
}

#derive link ratios using build in UDF:
paid.link.ratios<-ata(paid.tri)

#get simple average as:
apply(paid.link.ratios,2,link_ratio_simple_n_yrs,no.diag=5)

######################################

However I can't tell (and this is annoying) how to achieve the same type of thing for volume weighted averages? Any thoughts? or a pain in a a$$?

I suspect my work around for simple weighted average will fail if there are inf values in the triangle. The following page may provide a better alternative to na.trim above: https://artax.karlin.mff.cuni.cz/r-help/library/IDPmisc/html/NaRV.omit.html

@chiefmurph
Copy link
Contributor

Wow what a great find! Great idea!!

@chiefmurph
Copy link
Contributor

@mages:

The error has nothing to do with @actuaryactually 's weights. It is purposely generated in line 127 of ChainLadder.R because qpaid has n>m. Try

ChainLadder:::checkTriangle(qpaid))

Is that condition of a Triangle still required?

@actuaryactually:

I agree that the ability of ChainLadder to replicate popular deterministic calculations might improve its accessibility to a wider audience of actuaries. But a 5-year weighted average is simply the weighted average you would get by ignoring all other data. IMO, that is better accomplished within the data processing step, if only because it makes the fact of the ignored data more transparent. IMO, it would be much more informative for "reserving software" to measure the value of data being ignored. I look forward to the day when someone writes that code.

Your point about how to treat "not Regular" values is non-trivial and requires a bit of thought regarding the conditions that must exist for your package's set of "non Regular" values to occur in a link ratio triangle (I assume those are the values to which you refer because "inf values in the triangle" cannot occur in the loss triangle itself). As you work through the chainladder algorithm's underlying model of the development process, you may find the desire to modify the model for each irregular value individually rather than for the entire group collectively.

In conclusion, I hope that by fixing the qpaid-related bug in checkTriangle we can spread the word about @actuaryactually 's idea for ChainLadder to produce n-year average link ratios.

@actuaryactually
Copy link
Author

actuaryactually commented May 30, 2017 via email

@chiefmurph
Copy link
Contributor

x <- as.LongTriangle(GenIns)
x$cy <- with(x, as.numeric(as.character(dev))+as.numeric(as.character(origin)) - 1)

Here's the crucial data processing step

y <- subset(x, cy >= 5)

5 year simple and weighted averages

ata(as.triangle(y))

This could be generalized.

@chiefmurph
Copy link
Contributor

AFAICT the error message is generated at line 127 of Chainladder.R. My hypothesis is that the "n<=m" condition is required by only one Chainladder package model/function. Otherwise, the ata function would need to work around its use of the 'checkTriangle function. In any case, once that restriction is dealt with, @actuaryactually's suggested algorithm can be more thoroughly explored. I have had the best of intentions to comment out line 127 and check/build the package, but have not found the time. (Besides, I already have one side branch I've been working on extensively and must polish my git skills before starting another.) OTOH, @actuaryactually , if you would like to give this a shot ... .

@actuaryactually
Copy link
Author

actuaryactually commented Jun 21, 2017 via email

@mages
Copy link
Owner

mages commented Nov 1, 2018

Hi David,

Sorry for the long radio silence from my side.

If you would like to base your chain-ladder factors on the last 5 diagonals, ie calendar years, you do the following:

## Only use the last 5 diagonals, i.e. the last 5 calendar years
(weights <- ifelse(row(RAA) + col(RAA) <= 6 | row(RAA) + col(RAA) > 11, 0, 1))
MackChainLadder(GenIns, weights=weights, est.sigma = "Mack")

The argument alpha in MackChainLadder allows you to decide if you want to use volume weighted (alpha=1), simple averages (alpha=0), or a classic linear regression through the origin (alpha=2). Of course alpha can be a vector as well.

In the development version of ChainLadder 0.2.8 we have updated and hopefully also clarified the documentation of the weights argument in chainladder and MackChainLadder and added examples like the above.

I hope this helps.

@trinostics
Copy link
Collaborator

@mages I am coming around to your way of limiting the number of years in the average. However I have a few comments:

  1. Your example should run the Mack Method on RAA, not GenIns, although they are the same shape.
  2. When I use your 'weights' matrix, I get infinite S.E.'s. By not zeroing out the "future cells", the infinite S.E.'s go away:
    (weights <- ifelse(row(RAA) + col(RAA) <= 6 , 0, 1))
    I don't know why "future cell" weights must be unity.
  3. That formula actually gives the four-year average because the 1's correspond to the beginning value in the regressions.

cl <- chainladder(RAA, weights=weights)
sapply(cl$Models, coef)
x x x x x x x x x
3.479860 1.912592 1.266065 1.157990 1.099869 1.041935 1.033264 1.016936 1.009217
The first factor "manually calculated":
sum(RAA[6:9,2])/sum(RAA[6:9, 1])
[1] 3.47986

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants