casact / chainladder-python

Actuarial reserving in Python
https://chainladder-python.readthedocs.io/en/latest/
Mozilla Public License 2.0
191 stars 71 forks source link

How to correctly apply drop_valuation to quarterly triangles? #457

Open contenrico opened 1 year ago

contenrico commented 1 year ago

Discussed in https://github.com/casact/chainladder-python/discussions/455

Originally posted by **contenrico** September 7, 2023 Hello, I'm working with a quarterly triangle and I'm trying to exclude the latest diagonal (valuation date 2022-12). This is the code I'm running: `cl.Development(drop_valuation='2022').fit_transform(incurred).link_ratio` However, the excluded link ratios are the following: ![image](https://github.com/casact/chainladder-python/assets/137546866/681a2528-fda7-485e-bd3e-8b1000bea142) This is quite odd, as I would expect the following points to be excluded: (2022, 3), (2022, 6), (2022, 9) (2021, 15), (2021, 18), (2021, 21) (2020, 27), (2020, 30), (2020, 33) etc. Is there a way to use drop_valuation that achieves this? Thanks!
henrydingliu commented 1 year ago

can you create a reproducible example using the sample data available in the package?

contenrico commented 1 year ago

Sure:

triangle = cl.load_sample('quarterly') incurred = triangle['incurred'] cl.Development(drop_valuation='2005').fit_transform(incurred).link_ratio

kennethshsu commented 11 months ago

Hi @contenrico, did you ever resolve your issue?

My understanding is that you are just trying to drop the latest diagonal of your triangle, is that correct? There's no need to use cl.Development() for this. You can do this:

import chainladder as cl
triangle = cl.load_sample('quarterly')
incurred = triangle['incurred']
incurred[incurred.valuation < '2006-1-1']

However, if you need the link ratios, excluding information from the latest diagonal, you can then do this:

cl.Development().fit(incurred[incurred.valuation < '2006-1-1']).ldf_

Does this help?

contenrico commented 11 months ago

Hi @kennethshsu,

I was indeed trying to drop the latest diagonal from my triangle, but for the specific purpose of calculating link ratios - so I think I do need to use cl.Development().

Your second code snippet does exactly that, so I think that works! I guess the only improvement I need is to make that '2006-1-1' dynamic based on the valuation date, so I can always exclude the latest diagonal (or latest two, three, etc.).

Thank you!

kennethshsu commented 11 months ago

If you need to access 2006-1-1 as a TimeStamp variable, you can call .valuation_date on your Triangle object. Continuing the example:

incurred.valuation_date

Then, you can calculate the ldfs or cdfs as usual:

cl.Development().fit(incurred[incurred.valuation < incurred.valuation_date]).cdf_

With this, you'll be able to manipulate incurred.valuation_date, for example, excluding the latest 6 months, with

from dateutil.relativedelta import relativedelta
date_x6months = incurred.valuation_date - relativedelta(months=6)
cl.Development().fit(incurred[incurred.valuation < date_x6months]).cdf_

Now, with all that said, I don't know why drop_valuation in cl.Development isn't working as expected. I can try to look into this.

kennethshsu commented 6 months ago

Tests:


# Annual development work 
raa = cl.load_sample("raa")
assert cl.Development(drop_valuation='1981-12-31').fit_transform(raa).cdf_ != cl.Development(drop_valuation='1982-12-31').fit_transform(raa).cdf_
assert cl.Development(drop_valuation='1982-12-31').fit_transform(raa).cdf_ != cl.Development(drop_valuation='1983-12-31').fit_transform(raa).cdf_
assert cl.Development(drop_valuation='1981-12-31').fit_transform(raa).cdf_ != cl.Development(drop_valuation='1983-12-31').fit_transform(raa).cdf_

# but non-annual don't
assert cl.Development().fit_transform(incurred).cdf_ != cl.Development(drop_valuation='1995-03-31').fit_transform(incurred).cdf_
assert cl.Development().fit_transform(incurred).cdf_ != cl.Development(drop_valuation='1995-06-30').fit_transform(incurred).cdf_
assert cl.Development(drop_valuation='1995-03-31').fit_transform(incurred).cdf_ != cl.Development(drop_valuation='1995-06-30').fit_transform(incurred).cdf_ #this fails now
assert cl.Development(drop_valuation='1995-06-30').fit_transform(incurred).cdf_ != cl.Development(drop_valuation='1995-09-30').fit_transform(incurred).cdf_ #this fails now