From 8bddc353eadc8f62f615034eb00897dcd32b9bca Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Thu, 7 May 2026 13:23:58 -0700 Subject: [PATCH 01/15] Expanded apriori docstring --- chainladder/methods/bornferg.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/chainladder/methods/bornferg.py b/chainladder/methods/bornferg.py index 07c606cd..dbbe1412 100644 --- a/chainladder/methods/bornferg.py +++ b/chainladder/methods/bornferg.py @@ -10,9 +10,14 @@ class BornhuetterFerguson(Benktander): Parameters ---------- apriori: float, optional (default=1.0) - Multiplier for the sample_weight used in the Bornhuetter Ferguson - method. If sample_weight is already an apriori measure of ultimate, - then use 1.0 + Multiplier for the `sample_weight` used in the Bornhuetter Ferguson + method. If `sample_weight` is already an apriori measure of ultimate, + then use 1.0. + The recommended pratice is to seperate the model parameter assumption + and data apart. + For example, if the apriori s 80% of premium, it is recommended to set + the aprior as 0.8 and leave the premium data in `sample_weight` argument + unmodified. apriori_sigma: float, optional (default=0.0) Standard deviation of the apriori. When used in conjunction with the bootstrap model, the model samples aprioris from a lognormal distribution From 022d140b94e17afdf4dfbf153a10984061be75c4 Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Thu, 7 May 2026 13:32:53 -0700 Subject: [PATCH 02/15] Improved docstring and added examples --- chainladder/methods/bornferg.py | 69 +++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/chainladder/methods/bornferg.py b/chainladder/methods/bornferg.py index dbbe1412..056062f7 100644 --- a/chainladder/methods/bornferg.py +++ b/chainladder/methods/bornferg.py @@ -40,14 +40,10 @@ class BornhuetterFerguson(Benktander): Examples -------- Bornhuetter-Ferguson requires an apriori expected ultimate per origin, - supplied through ``sample_weight``. ``sample_weight`` must be a - chainladder Triangle aligned with ``X``, not a scalar; passing - ``sample_weight=14000`` would raise ``AttributeError`` because the model - accesses ``.shape``. + supplied through ``sample_weight``. A common idiom for building a flat per-origin apriori is to take any - same-shape Triangle, zero it out, and add the desired value. Below uses - the chainladder ultimate as the shape donor. + same-shape Triangle, zero it out, and add the desired value. Here is an example. .. testsetup:: @@ -55,40 +51,53 @@ class BornhuetterFerguson(Benktander): .. testcode:: - tr = cl.load_sample('ukmotor') - cl_ult = cl.Chainladder().fit(tr).ultimate_ - apriori = cl_ult * 0 + float(cl_ult.sum()) / 7 - print(apriori) + raa = cl.load_sample('raa') + premium = raa.latest_diagonal*0 + 40000 #zero out and add 40,000 to each origin + + cl.BornhuetterFerguson(apriori=0.7).fit( + X=raa, + sample_weight=premium + ).ibnr_ .. testoutput:: - 2261 - 2007 14903.967562 - 2008 14903.967562 - 2009 14903.967562 - 2010 14903.967562 - 2011 14903.967562 - 2012 14903.967562 - 2013 14903.967562 + 2261 + 1981 NaN + 1982 256 + 1983 718 + 1984 1,596 + 1985 2,659 + 1986 5,239 + 1987 8,574 + 1988 12,715 + 1989 18,585 + 1990 24,861 - Fit with that apriori. The BF ultimates pull the immature origins toward - the apriori while leaving mature origins close to chainladder. + One might be tempted to set never set the aprior and modify the sample_weight directly, and they will result in the same answer, but this is not the recommended practice. It not only add confusion, but it alos mixes the model parameter assumption and data together. .. testcode:: - model = cl.BornhuetterFerguson(apriori=1.0).fit(tr, sample_weight=apriori) - print(model.ultimate_) + raa = cl.load_sample('raa') + premium = raa.latest_diagonal*0 + 40000*0.7 #premium is modified by 70% + + cl.BornhuetterFerguson().fit( + X=raa, + sample_weight=premium + ).ibnr_ .. testoutput:: - 2261 - 2007 12690.000000 - 2008 13145.318280 - 2009 14095.125641 - 2010 13412.748068 - 2011 14150.549749 - 2012 15999.244850 - 2013 16658.824705 + 2261 + 1981 NaN + 1982 256 + 1983 718 + 1984 1,596 + 1985 2,659 + 1986 5,239 + 1987 8,574 + 1988 12,715 + 1989 18,585 + 1990 24,861 """ def __init__(self, apriori=1.0, apriori_sigma=0.0, random_state=None): From 625def84eafa8b9dc6542375b4c084eb2c146150 Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Thu, 7 May 2026 13:46:03 -0700 Subject: [PATCH 03/15] added code for doc tests --- docs/library/contributing.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/library/contributing.md b/docs/library/contributing.md index da85d68d..400ac82d 100644 --- a/docs/library/contributing.md +++ b/docs/library/contributing.md @@ -116,10 +116,17 @@ Contributions to documentation are especially helpful for new users. - Follow established naming conventions - Include new unit tests with reasonable coverage -All PRs should be run locally before submission: +All PRs should be run locally before submission. +For codebase tests, run: + +```bash +pytest +``` + +For documentation changes, rebuild the docs locally with: ```bash -pytest chainladder +uv run jb build docs ``` Large or unfocused PRs may delay merging. Each PR should address a single issue or feature to maintain clarity and quality. From fb80133f1654cb737d8b6fac1fbcf7d5b3fc457e Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Thu, 7 May 2026 13:53:26 -0700 Subject: [PATCH 04/15] Added a new checkbox to remind PR to run tests --- .github/pull_request_template.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 423378a6..94149f1c 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -6,3 +6,5 @@ ## Additional Context for Reviewers + +- [ ] I passed tests locally for both code (`pytest`) and documentation changes (`uv run jb build docs`) From 49daeed312974ea776314ec4743d438f7191df8e Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Thu, 7 May 2026 14:06:54 -0700 Subject: [PATCH 05/15] better command and surpressed some warnings --- .github/pull_request_template.md | 2 +- docs/library/contributing.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 94149f1c..f359f621 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -7,4 +7,4 @@ ## Additional Context for Reviewers -- [ ] I passed tests locally for both code (`pytest`) and documentation changes (`uv run jb build docs`) +- [ ] I passed tests locally for both code (`pytest`) and documentation changes (`uv run jb build docs --builder=custom --custom-builder=doctest`) diff --git a/docs/library/contributing.md b/docs/library/contributing.md index 400ac82d..caab7967 100644 --- a/docs/library/contributing.md +++ b/docs/library/contributing.md @@ -126,7 +126,7 @@ pytest For documentation changes, rebuild the docs locally with: ```bash -uv run jb build docs +uv run jb build docs --builder=custom --custom-builder=doctest ``` Large or unfocused PRs may delay merging. Each PR should address a single issue or feature to maintain clarity and quality. From d8b6f02a773074f05412d71a77950195aaf6047e Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Thu, 7 May 2026 14:07:14 -0700 Subject: [PATCH 06/15] Added example, removed reference to chainladder --- chainladder/methods/bornferg.py | 64 ++++++++++++++++----------------- 1 file changed, 30 insertions(+), 34 deletions(-) diff --git a/chainladder/methods/bornferg.py b/chainladder/methods/bornferg.py index 056062f7..695a6f49 100644 --- a/chainladder/methods/bornferg.py +++ b/chainladder/methods/bornferg.py @@ -51,53 +51,49 @@ class BornhuetterFerguson(Benktander): .. testcode:: - raa = cl.load_sample('raa') - premium = raa.latest_diagonal*0 + 40000 #zero out and add 40,000 to each origin + raa = cl.load_sample("raa") + premium = raa.latest_diagonal * 0 + 40_000 # zero out and add 40,000 to each origin - cl.BornhuetterFerguson(apriori=0.7).fit( - X=raa, - sample_weight=premium - ).ibnr_ + ibnr = cl.BornhuetterFerguson(apriori=0.7).fit(X=raa, sample_weight=premium).ibnr_ + print(ibnr) .. testoutput:: - 2261 - 1981 NaN - 1982 256 - 1983 718 - 1984 1,596 - 1985 2,659 - 1986 5,239 - 1987 8,574 - 1988 12,715 - 1989 18,585 - 1990 24,861 + 2261 + 1981 NaN + 1982 255.707763 + 1983 717.772687 + 1984 1596.061515 + 1985 2658.738155 + 1986 5239.441491 + 1987 8574.335344 + 1988 12714.889984 + 1989 18585.219714 + 1990 24861.068855 One might be tempted to set never set the aprior and modify the sample_weight directly, and they will result in the same answer, but this is not the recommended practice. It not only add confusion, but it alos mixes the model parameter assumption and data together. .. testcode:: - raa = cl.load_sample('raa') - premium = raa.latest_diagonal*0 + 40000*0.7 #premium is modified by 70% + raa = cl.load_sample("raa") + premium = raa.latest_diagonal * 0 + 40_000 * 0.7 # premium is modified by 70% - cl.BornhuetterFerguson().fit( - X=raa, - sample_weight=premium - ).ibnr_ + ibnr = cl.BornhuetterFerguson().fit(X=raa, sample_weight=premium).ibnr_ + print(ibnr) .. testoutput:: - 2261 - 1981 NaN - 1982 256 - 1983 718 - 1984 1,596 - 1985 2,659 - 1986 5,239 - 1987 8,574 - 1988 12,715 - 1989 18,585 - 1990 24,861 + 2261 + 1981 NaN + 1982 255.707763 + 1983 717.772687 + 1984 1596.061515 + 1985 2658.738155 + 1986 5239.441491 + 1987 8574.335344 + 1988 12714.889984 + 1989 18585.219714 + 1990 24861.068855 """ def __init__(self, apriori=1.0, apriori_sigma=0.0, random_state=None): From 39b25f7196f97b99e60fe0827bc7c7787e9842c6 Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Thu, 7 May 2026 14:30:40 -0700 Subject: [PATCH 07/15] doctstrings with examples --- chainladder/methods/benktander.py | 95 ++++++++++++++++++++----------- 1 file changed, 63 insertions(+), 32 deletions(-) diff --git a/chainladder/methods/benktander.py b/chainladder/methods/benktander.py index 2a7d42f8..6be777ec 100644 --- a/chainladder/methods/benktander.py +++ b/chainladder/methods/benktander.py @@ -16,6 +16,7 @@ class Benktander(MethodBase): then use 1.0 n_iters: int, optional (default=1) Number of iterations to use in the Benktander model. + When n_iters=0, the result is equivalent to the BornhuetterFerguson method. apriori_sigma: float, optional (default=0.0) Standard deviation of the apriori. When used in conjunction with the bootstrap model, the model samples aprioris from a lognormal distribution @@ -49,53 +50,83 @@ class Benktander(MethodBase): .. testcode:: - tr = cl.load_sample('ukmotor') - apriori = cl.Chainladder().fit(tr).ultimate_ * 0 + 14000 + xyz = cl.load_sample("xyz") - With ``n_iters=1`` Benktander reproduces Bornhuetter-Ferguson exactly. + ibnr = cl.Benktander().fit(X=xyz["Paid"], sample_weight=xyz["Premium"].latest_diagonal).ibnr_ + print(ibnr) + + .. testoutput:: + + 2261 + 1998 NaN + 1999 115.472127 + 2000 914.033812 + 2001 2432.394513 + 2002 6037.026677 + 2003 13928.934651 + 2004 33925.451475 + 2005 69724.761575 + 2006 73410.593920 + 2007 52977.560411 + 2008 45873.769490 + + When `n_iters=1`, the model is exactly the same as the BornhuetterFerguson model. .. testcode:: - print( - cl.Benktander(apriori=1.0, n_iters=1).fit( - tr, sample_weight=apriori - ).ultimate_ + xyz = cl.load_sample("xyz") + + bk_ibnr = ( + cl.Benktander(n_iters=1) + .fit(X=xyz["Paid"], sample_weight=xyz["Premium"].latest_diagonal) + .ibnr_ ) + bf_ibnr = ( + cl.BornhuetterFerguson() + .fit(X=xyz["Paid"], sample_weight=xyz["Premium"].latest_diagonal) + .ibnr_ + ) + print(bk_ibnr - bf_ibnr) .. testoutput:: - 2261 - 2007 12690.000000 - 2008 13121.098503 - 2009 14028.278620 - 2010 13272.048822 - 2011 13911.968891 - 2012 15614.145287 - 2013 16029.501746 - - Increasing ``n_iters`` pulls the immature origins toward the chainladder - estimate. The 2013 origin shows this most: ``16029`` at ``n_iters=1``, - rising to ``19110`` at ``n_iters=4`` and approaching the chainladder - ultimate of ``20680``. + 2261 + 1998 NaN + 1999 NaN + 2000 NaN + 2001 NaN + 2002 NaN + 2003 NaN + 2004 NaN + 2005 NaN + 2006 NaN + 2007 NaN + 2008 NaN + + When `n_iters>>1`, the model converges to the traditional Chainladder model. .. testcode:: - print( - cl.Benktander(apriori=1.0, n_iters=4).fit( - tr, sample_weight=apriori - ).ultimate_ - ) + xyz = cl.load_sample("xyz") + + bk_ibnr = cl.Benktander(n_iters=1000).fit(X=xyz["Paid"], sample_weight=xyz["Premium"].latest_diagonal).ibnr_ + cl_ibnr = cl.Chainladder().fit(xyz["Paid"]).ibnr_ + print(bk_ibnr - cl_ibnr) .. testoutput:: 2261 - 2007 12690.000000 - 2008 13096.902490 - 2009 14030.535854 - 2010 13138.365841 - 2011 13880.984774 - 2012 16719.527550 - 2013 19110.806503 + 1998 NaN + 1999 NaN + 2000 NaN + 2001 1.455192e-11 + 2002 -7.275958e-12 + 2003 7.275958e-12 + 2004 1.455192e-11 + 2005 -1.455192e-11 + 2006 2.910383e-11 + 2007 -5.820766e-11 + 2008 -7.275958e-11 """ def __init__(self, apriori=1.0, n_iters=1, apriori_sigma=0, random_state=None): From 35de8fcf57122658044f7f6380075207dae17382 Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Thu, 7 May 2026 14:31:38 -0700 Subject: [PATCH 08/15] Added covergence to the n_iters parameter. --- chainladder/methods/benktander.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chainladder/methods/benktander.py b/chainladder/methods/benktander.py index 6be777ec..a0550f54 100644 --- a/chainladder/methods/benktander.py +++ b/chainladder/methods/benktander.py @@ -16,7 +16,8 @@ class Benktander(MethodBase): then use 1.0 n_iters: int, optional (default=1) Number of iterations to use in the Benktander model. - When n_iters=0, the result is equivalent to the BornhuetterFerguson method. + When n_iters=1, the result is equivalent to the BornhuetterFerguson method. + When n_iters>>1, the result converges to the traditional Chainladder model. apriori_sigma: float, optional (default=0.0) Standard deviation of the apriori. When used in conjunction with the bootstrap model, the model samples aprioris from a lognormal distribution From 66ce8a58ecffc526d151d220da3e69347ba17f8e Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Thu, 7 May 2026 15:02:37 -0700 Subject: [PATCH 09/15] docstring improvement and examples --- chainladder/methods/capecod.py | 123 +++++++++++++++++++++++---------- 1 file changed, 86 insertions(+), 37 deletions(-) diff --git a/chainladder/methods/capecod.py b/chainladder/methods/capecod.py index c6959ffc..0d132d82 100644 --- a/chainladder/methods/capecod.py +++ b/chainladder/methods/capecod.py @@ -12,28 +12,34 @@ class CapeCod(Benktander): Parameters ---------- - trend: float (default=0.0) - The cape cod trend assumption. Any Trend transformer on X will + trend: float, optional (default=0.0) + The cape cod trend assumption. Any Trend transformer on X will override this argument. - decay: float (defaut=1.0) - The cape cod decay assumption + decay: float, optional (default=1.0) + The cape cod decay assumption. This parameter is required by the + Generalized Cape Cod Method, as discussed in [Using Best Practices to + Determine a Best Reserve Estimate](https://www.casact.org/sites/default/files/database/forum_98fforum_struhuss.pdf) + by Struzzieri and Hussian. As the `decay` factor approaches 1 + (the default value), the result approaches the traditional Cape Cod + method. As the `decay` factor approaches 0, the result approaches + the `Chainladder` method. n_iters: int, optional (default=1) Number of iterations to use in the Benktander model. + groupby: str or list, optional (default=None) + An option to group levels of the triangle index together for the + purposes of deriving the apriori measures. If omitted, each level of + the triangle index will receive its own apriori computation. apriori_sigma: float, optional (default=0.0) Standard deviation of the apriori. When used in conjunction with the bootstrap model, the model samples aprioris from a lognormal distribution using this argument as a standard deviation. random_state: int, RandomState instance or None, optional (default=None) - Seed for sampling from the apriori distribution. This is ignored when + Seed for sampling from the apriori distribution. This is ignored when using as a deterministic method. If int, random_state is the seed used by the random number generator; If RandomState instance, random_state is the random number generator; If None, the random number generator is the RandomState instance used by np.random. - groupby: - An option to group levels of the triangle index together for the - purposes of deriving the apriori measures. If omitted, each level of - the triangle index will receive its own apriori computation. Attributes @@ -61,8 +67,27 @@ class CapeCod(Benktander): .. testcode:: - tr = cl.load_sample('ukmotor') - exposure = cl.Chainladder().fit(tr).ultimate_ * 0 + 20000 + xyz = cl.load_sample("xyz") + + ibnr = ( + cl.CapeCod().fit(X=xyz["Paid"], sample_weight=xyz["Premium"].latest_diagonal).ibnr_ + ) + print(ibnr) + + .. testoutput:: + + 2261 + 1998 NaN + 1999 88.211299 + 2000 698.247374 + 2001 1858.151261 + 2002 4611.796594 + 2003 10640.571396 + 2004 25916.281295 + 2005 53264.037933 + 2006 56079.713590 + 2007 40470.540502 + 2008 35043.822927 With default ``decay=1`` and ``trend=0``, every origin receives the same apriori loss ratio: the exposure-weighted mean loss ratio across all @@ -70,19 +95,27 @@ class CapeCod(Benktander): .. testcode:: - model = cl.CapeCod().fit(tr, sample_weight=exposure) - print(model.apriori_) + apriori = ( + cl.CapeCod() + .fit(X=xyz["Paid"], sample_weight=xyz["Premium"].latest_diagonal) + .apriori_ + ) + print(apriori) .. testoutput:: 2261 - 2007 0.706225 - 2008 0.706225 - 2009 0.706225 - 2010 0.706225 - 2011 0.706225 - 2012 0.706225 - 2013 0.706225 + 1998 0.763919 + 1999 0.763919 + 2000 0.763919 + 2001 0.763919 + 2002 0.763919 + 2003 0.763919 + 2004 0.763919 + 2005 0.763919 + 2006 0.763919 + 2007 0.763919 + 2008 0.763919 Setting ``decay`` below 1 down-weights distant origins when computing each origin's apriori, so each origin receives its own loss-ratio @@ -90,36 +123,52 @@ class CapeCod(Benktander): .. testcode:: - print(cl.CapeCod(decay=0.5).fit(tr, sample_weight=exposure).apriori_) + apriori = cl.CapeCod(decay=0.5).fit( + X=xyz["Paid"], sample_weight=xyz["Premium"].latest_diagonal + ).apriori_ + print(apriori) .. testoutput:: 2261 - 2007 0.653584 - 2008 0.666113 - 2009 0.683132 - 2010 0.689123 - 2011 0.717497 - 2012 0.776364 - 2013 0.836006 + 1998 0.797751 + 1999 0.799990 + 2000 0.804890 + 2001 0.793706 + 2002 0.777420 + 2003 0.748556 + 2004 0.740594 + 2005 0.687204 + 2006 0.705757 + 2007 0.784466 + 2008 0.830368 Setting ``trend`` projects the loss ratio forward over the experience - period. With ``decay=1``, all origins share the trended apriori. + period. .. testcode:: - print(cl.CapeCod(trend=0.05).fit(tr, sample_weight=exposure).apriori_) + apriori = ( + cl.CapeCod(decay=0.5, trend=0.03) + .fit(X=xyz["Paid"], sample_weight=xyz["Premium"].latest_diagonal) + .apriori_ + ) + print(apriori) .. testoutput:: 2261 - 2007 0.836096 - 2008 0.836096 - 2009 0.836096 - 2010 0.836096 - 2011 0.836096 - 2012 0.836096 - 2013 0.836096 + 1998 1.027105 + 1999 1.014959 + 2000 1.001927 + 2001 0.966406 + 2002 0.924906 + 2003 0.869767 + 2004 0.841331 + 2005 0.765515 + 2006 0.773005 + 2007 0.847345 + 2008 0.890327 """ def __init__( From e0682671fdb3191d6fc8e5f7dcbc5595670c0823 Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Thu, 7 May 2026 15:09:35 -0700 Subject: [PATCH 10/15] Added expectedloss method --- docs/library/api.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/library/api.md b/docs/library/api.md index fd8af1da..0e412b07 100644 --- a/docs/library/api.md +++ b/docs/library/api.md @@ -101,6 +101,7 @@ Classes Chainladder MackChainladder BornhuetterFerguson + ExpectedLoss Benktander CapeCod From 39d33b1d26e828ca4056678c8553c6a8fe8ed4f0 Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Thu, 7 May 2026 15:13:03 -0700 Subject: [PATCH 11/15] Improved docstrings and examples --- chainladder/methods/expectedloss.py | 60 +++++++++++++++++++ .../generated/chainladder.ExpectedLoss.rst | 6 ++ 2 files changed, 66 insertions(+) create mode 100644 docs/library/generated/chainladder.ExpectedLoss.rst diff --git a/chainladder/methods/expectedloss.py b/chainladder/methods/expectedloss.py index ea09cc99..012b0aac 100644 --- a/chainladder/methods/expectedloss.py +++ b/chainladder/methods/expectedloss.py @@ -31,6 +31,66 @@ class ExpectedLoss(Benktander): The ultimate losses per the method ibnr_: Triangle The IBNR per the method + + Examples + -------- + ExpectedLoss is the deterministic Expected Loss IBNR model. It is a special case of the Benktander model where the apriori is set to 1.0. + + .. testsetup:: + + import chainladder as cl + + .. testcode:: + + ibnr = ( + cl.ExpectedLoss() + .fit(X=xyz["Paid"], sample_weight=xyz["Premium"].latest_diagonal) + .ibnr_ + ) + print(ibnr) + + .. testoutput:: + + 2261 + 1998 4178.0 + 1999 6683.0 + 2000 8218.0 + 2001 11481.0 + 2002 16746.0 + 2003 29855.0 + 2004 46511.0 + 2005 98125.0 + 2006 84759.0 + 2007 50573.0 + 2008 44388.0 + + We can specify the apriori as a percentage of the premium. + + .. testcode:: + + xyz = cl.load_sample("xyz") + + ibnr = ( + cl.ExpectedLoss(apriori=0.9) + .fit(X=xyz["Paid"], sample_weight=xyz["Premium"].latest_diagonal) + .ibnr_ + ) + print(ibnr) + + .. testoutput:: + + 2261 + 1998 2178.0 + 1999 3533.0 + 2000 3718.0 + 2001 6481.0 + 2002 10627.7 + 2003 22937.5 + 2004 36578.8 + 2005 84309.9 + 2006 74001.2 + 2007 44329.2 + 2008 39608.3 """ def __init__(self, apriori=1.0, apriori_sigma=0.0, random_state=None): diff --git a/docs/library/generated/chainladder.ExpectedLoss.rst b/docs/library/generated/chainladder.ExpectedLoss.rst new file mode 100644 index 00000000..222db062 --- /dev/null +++ b/docs/library/generated/chainladder.ExpectedLoss.rst @@ -0,0 +1,6 @@ +chainladder.ExpectedLoss +======================== + +.. currentmodule:: chainladder + +.. autoclass:: ExpectedLoss \ No newline at end of file From 0d15ebc488d39ac35a8f83f482429d748cb812b1 Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Thu, 7 May 2026 20:42:17 -0700 Subject: [PATCH 12/15] added load_sample --- chainladder/methods/expectedloss.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/chainladder/methods/expectedloss.py b/chainladder/methods/expectedloss.py index 012b0aac..7110b314 100644 --- a/chainladder/methods/expectedloss.py +++ b/chainladder/methods/expectedloss.py @@ -42,6 +42,8 @@ class ExpectedLoss(Benktander): .. testcode:: + xyz = cl.load_sample("xyz") + ibnr = ( cl.ExpectedLoss() .fit(X=xyz["Paid"], sample_weight=xyz["Premium"].latest_diagonal) From ef6d24f8f740f7dcc195d99ca2254466ac0f8489 Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Fri, 8 May 2026 09:20:42 -0700 Subject: [PATCH 13/15] Improved docstring --- chainladder/methods/expectedloss.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chainladder/methods/expectedloss.py b/chainladder/methods/expectedloss.py index 7110b314..0cca886b 100644 --- a/chainladder/methods/expectedloss.py +++ b/chainladder/methods/expectedloss.py @@ -5,7 +5,8 @@ class ExpectedLoss(Benktander): - """The deterministic Expected Loss IBNR model + """The deterministic Expected Loss IBNR model, it ignores all data in the + triangle, and only uses the apriori to calculate the ultimate losses. Parameters ---------- @@ -34,7 +35,6 @@ class ExpectedLoss(Benktander): Examples -------- - ExpectedLoss is the deterministic Expected Loss IBNR model. It is a special case of the Benktander model where the apriori is set to 1.0. .. testsetup:: From b1277603560b01806fed5776c480160d85680b17 Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Fri, 8 May 2026 09:23:03 -0700 Subject: [PATCH 14/15] Clarification --- chainladder/methods/expectedloss.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chainladder/methods/expectedloss.py b/chainladder/methods/expectedloss.py index 0cca886b..44444a2e 100644 --- a/chainladder/methods/expectedloss.py +++ b/chainladder/methods/expectedloss.py @@ -6,7 +6,8 @@ class ExpectedLoss(Benktander): """The deterministic Expected Loss IBNR model, it ignores all data in the - triangle, and only uses the apriori to calculate the ultimate losses. + triangle, and only uses the sample_weight modified by the apriori to + calculate the ultimate losses. Parameters ---------- From 9c3393173e719c0fe5aa9c61daa5cea7878a36f0 Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Fri, 8 May 2026 12:45:12 -0700 Subject: [PATCH 15/15] Updated the example in fit --- chainladder/methods/benktander.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/chainladder/methods/benktander.py b/chainladder/methods/benktander.py index a0550f54..6681d5b8 100644 --- a/chainladder/methods/benktander.py +++ b/chainladder/methods/benktander.py @@ -164,13 +164,30 @@ def fit(self, X, y=None, sample_weight=None): .. testcode:: - tr = cl.load_sample('ukmotor') - apriori = cl.Chainladder().fit(tr).ultimate_ * 0 + 14000 - print(cl.Benktander(apriori=1.0, n_iters=2).fit(tr, sample_weight=apriori)) + xyz = cl.load_sample("xyz") + + ultimate = ( + cl.Benktander(apriori=1, n_iters=2) + .fit(X=xyz["Paid"], sample_weight=xyz["Premium"].latest_diagonal) + .ultimate_ + ) + print(ultimate) .. testoutput:: - Benktander(n_iters=2) + 2261 + 1998 15822.000000 + 1999 24908.397003 + 2000 37547.676656 + 2001 40511.198946 + 2002 49417.354765 + 2003 50042.095135 + 2004 82437.601111 + 2005 95417.171135 + 2006 88485.508416 + 2007 66882.788227 + 2008 50708.755370 + """ if sample_weight is None: raise ValueError("sample_weight is required.")