From 23b39764cc750b2b2630b7edd09a6910358a6d56 Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Tue, 14 Jul 2026 14:22:45 -0700 Subject: [PATCH 01/28] Release prep --- chainladder/__init__.py | 6 +-- docs/library/releases.md | 100 +++++++++++++++++++++++++++++++++++++++ pyproject.toml | 2 +- 3 files changed, 104 insertions(+), 4 deletions(-) diff --git a/chainladder/__init__.py b/chainladder/__init__.py index d14caa1c1..4c3cdc2d5 100644 --- a/chainladder/__init__.py +++ b/chainladder/__init__.py @@ -197,7 +197,7 @@ def get_option( """ Get the option value for the specified option. - .. deprecated:: 0.9.3 + .. deprecated:: 0.10.0 The ``option`` parameter is deprecated; use ``pat`` instead. Parameters @@ -226,7 +226,7 @@ def set_option( """ Set the option value for the specified option. - .. deprecated:: 0.9.3 + .. deprecated:: 0.10.0 The ``option`` parameter is deprecated; use ``pat`` instead. Parameters @@ -283,7 +283,7 @@ def reset_option( Restores the default value for the specified option. Restores default values for all options if pat is None. - .. deprecated:: 0.9.3 + .. deprecated:: 0.10.0 The ``option`` parameter is deprecated; use ``pat`` instead. Parameters diff --git a/docs/library/releases.md b/docs/library/releases.md index e2be91e9a..7b5718a3c 100644 --- a/docs/library/releases.md +++ b/docs/library/releases.md @@ -1,5 +1,105 @@ # {octicon}`megaphone` Releases & Changelog +## Version 0.10 + +### Version 0.10.0 + +Release Date: TBD + +**Highlights** +* **Pandas 3 and Python 3.14 support** — datetime precision now follows the installed pandas default, string-dtype comparisons, removal of `DataFrame.groupby(..., axis=1)`, and cleanup of dead pandas 1.x code ([#807](https://github.com/casact/chainladder-python/pull/807), [#816](https://github.com/casact/chainladder-python/pull/816), [#817](https://github.com/casact/chainladder-python/pull/817), [#818](https://github.com/casact/chainladder-python/pull/818), [#819](https://github.com/casact/chainladder-python/pull/819), [#820](https://github.com/casact/chainladder-python/pull/820)). +* **`numpy>=2.0` is now the minimum supported version** ([#899](https://github.com/casact/chainladder-python/pull/899)); `numba` is no longer a direct dependency ([#898](https://github.com/casact/chainladder-python/pull/898)). +* **Backend deprecations** — the `dask` array backend ([#937](https://github.com/casact/chainladder-python/pull/937)) and dask parallel-compute (bag) paths ([#1008](https://github.com/casact/chainladder-python/pull/1008)), and the `cupy` array backend ([#881](https://github.com/casact/chainladder-python/pull/881)), are deprecated. The `option` parameter of `cl.options.get_option`/`set_option`/`reset_option` is deprecated in favor of `pat`. +* **New public `TriangleWeight` utility** for `latest_n` and drop-based weighting outside the `Development` estimator ([#941](https://github.com/casact/chainladder-python/pull/941)). +* **New `DisposalRate` adjustment method** ([#1022](https://github.com/casact/chainladder-python/pull/1022)). +* **`MackChainladder` multi-triangle (4D) support** ([#873](https://github.com/casact/chainladder-python/pull/873)) plus reconciliation to the Mack papers ([#1096](https://github.com/casact/chainladder-python/pull/1096)); `BootstrapODPSample` multi-index support ([#917](https://github.com/casact/chainladder-python/pull/917)). +* **New `Development` averaging** — geometric averaging type ([#811](https://github.com/casact/chainladder-python/pull/811)) — and a new `Triangle.xs` method ([#763](https://github.com/casact/chainladder-python/pull/763)). +* **New `Triangle.empty` attribute** for pandas consistency ([#901](https://github.com/casact/chainladder-python/pull/901)) and a new `cl.options.describe_option` helper ([#877](https://github.com/casact/chainladder-python/pull/877)). +* **New sample datasets** — `clrd2025`, additional Friedland exhibits, and a centralized sample-dataset manifest surfaced through `list_samples` ([#771](https://github.com/casact/chainladder-python/pull/771), [#787](https://github.com/casact/chainladder-python/pull/787), [#837](https://github.com/casact/chainladder-python/pull/837), [#886](https://github.com/casact/chainladder-python/pull/886), [#1116](https://github.com/casact/chainladder-python/pull/1116)). +* **Type annotations and tooling** — extensive pandas annotations, an explicit public API surface, a pyright type-completeness workflow, and a new `ARCHITECTURE.md` ([#912](https://github.com/casact/chainladder-python/pull/912), [#1035](https://github.com/casact/chainladder-python/pull/1035), [#1042](https://github.com/casact/chainladder-python/pull/1042), [#1065](https://github.com/casact/chainladder-python/pull/1065), [#1070](https://github.com/casact/chainladder-python/pull/1070)). +* **Docs and tests** — large expansion of doctest examples and new unit-test suites across slicing, dunders/arithmetic, pandas compatibility, base, and IO. + +**What's Changed** + +*A curated summary is below. For the complete, unedited list of every merged pull request (including internal integration-branch and CI churn), see the Full Changelog link at the bottom.* + +**Enhancements** +* Add geometric averaging type to the `Development` estimator by @kennethshsu in [#811](https://github.com/casact/chainladder-python/pull/811) +* Add `TriangleWeight` utility class as a public API by @henrydingliu in [#941](https://github.com/casact/chainladder-python/pull/941) +* Add `DisposalRate` method by @henrydingliu in [#1022](https://github.com/casact/chainladder-python/pull/1022) +* Add multi-triangle (4D) support to `MackChainladder` by @henrydingliu in [#873](https://github.com/casact/chainladder-python/pull/873), with reconciliation to the Mack papers in [#1096](https://github.com/casact/chainladder-python/pull/1096) +* Add multi-index support to `BootstrapODPSample` by @henrydingliu in [#917](https://github.com/casact/chainladder-python/pull/917) +* Add `Triangle.xs` by @henrydingliu in [#763](https://github.com/casact/chainladder-python/pull/763) +* Add `empty` attribute to `Triangle` for pandas consistency by @priyam0k in [#901](https://github.com/casact/chainladder-python/pull/901) +* Implement `cl.options.describe_option` by @genedan in [#877](https://github.com/casact/chainladder-python/pull/877) +* Allow `DevelopmentConstant` to be callable on columns by @henrydingliu in [#796](https://github.com/casact/chainladder-python/pull/796) +* Return a Python `bool` from `is_ultimate` by @salexanian in [#793](https://github.com/casact/chainladder-python/pull/793) + +**Pandas 3 / Python 3.14 support** +* Replace datetime constants with pandas default datetime precision by @genedan in [#816](https://github.com/casact/chainladder-python/pull/816) +* Replace object-type comparisons with string comparisons for string dtypes by @genedan in [#819](https://github.com/casact/chainladder-python/pull/819) +* Replace `DataFrame.groupby(..., axis=1)` with a double transpose by @genedan in [#818](https://github.com/casact/chainladder-python/pull/818) +* Wrap raw HTML in `StringIO` by @genedan in [#817](https://github.com/casact/chainladder-python/pull/817) +* pandas 3 / Python 3.14 support by @genedan in [#820](https://github.com/casact/chainladder-python/pull/820) +* Clean up dead pandas 1.x code by @genedan in [#807](https://github.com/casact/chainladder-python/pull/807) + +**API Changes & Deprecations** +* Deprecate the `dask` array backend by @priyam0k in [#937](https://github.com/casact/chainladder-python/pull/937) and the dask parallel-compute (bag) paths in [#1008](https://github.com/casact/chainladder-python/pull/1008) +* Deprecate the `cupy` array backend by @priyam0k in [#881](https://github.com/casact/chainladder-python/pull/881) +* Make API exports explicit by @priyam0k in [#912](https://github.com/casact/chainladder-python/pull/912) +* Remove deprecated `broadcast_axis` by @kennethshsu in [#991](https://github.com/casact/chainladder-python/pull/991) +* Set minimum numpy version to 2.0 by @SaguaroDev in [#899](https://github.com/casact/chainladder-python/pull/899) +* Remove numba as a direct dependency by @SaguaroDev in [#898](https://github.com/casact/chainladder-python/pull/898) + +**Bug Fixes** +* Fix `sigma_` formula by @henrydingliu in [#965](https://github.com/casact/chainladder-python/pull/965) +* Fix `Triangle.full_expectation_` by @henrydingliu in [#884](https://github.com/casact/chainladder-python/pull/884) +* `sparse` `floor()` returns a copy instead of mutating its input by @SaguaroDev in [#894](https://github.com/casact/chainladder-python/pull/894) +* Fix `DevelopmentConstant` tail bug by @kennethshsu in [#923](https://github.com/casact/chainladder-python/pull/923) +* Fix `drop_high` tie-breaking logic by @etw002 in [#1009](https://github.com/casact/chainladder-python/pull/1009) +* Make `quarterly.csv` development dates ISO 8601-compatible by @genedan in [#930](https://github.com/casact/chainladder-python/pull/930) +* Various Mack fixes by @henrydingliu in [#1097](https://github.com/casact/chainladder-python/pull/1097) +* Various Friedland data fixes by @henrydingliu in [#927](https://github.com/casact/chainladder-python/pull/927), [#967](https://github.com/casact/chainladder-python/pull/967) + +**Sample Data** +* Add the `clrd2025` dataset and centralize sample metadata in a manifest surfaced via `list_samples` by @SaguaroDev in [#771](https://github.com/casact/chainladder-python/pull/771), [#886](https://github.com/casact/chainladder-python/pull/886) +* Add Friedland Exhibit III samples and earned premium by @salexanian in [#787](https://github.com/casact/chainladder-python/pull/787) +* Add Friedland Chapter 6–7 exhibits by @henrydingliu in [#837](https://github.com/casact/chainladder-python/pull/837) +* Add the merged Friedland USPP sample dataset by @salexanian in [#1116](https://github.com/casact/chainladder-python/pull/1116) +* Add Friedland auto BI, GL insurer, GL self-insurer, and US industry auto premium datasets by @kennethshsu in [#870](https://github.com/casact/chainladder-python/pull/870), [#905](https://github.com/casact/chainladder-python/pull/905), [#921](https://github.com/casact/chainladder-python/pull/921), [#904](https://github.com/casact/chainladder-python/pull/904) + +**Documentation** +* Add doctest examples across the development, adjustment, tail, ClarkLDF, workflow, and utility estimators by @EKtheSage in [#799](https://github.com/casact/chainladder-python/pull/799), [#800](https://github.com/casact/chainladder-python/pull/800), [#801](https://github.com/casact/chainladder-python/pull/801), [#802](https://github.com/casact/chainladder-python/pull/802), [#803](https://github.com/casact/chainladder-python/pull/803), [#804](https://github.com/casact/chainladder-python/pull/804) +* Add doctest examples for `BootstrapODPSample` and the correlation classes by @priyam0k in [#836](https://github.com/casact/chainladder-python/pull/836), [#844](https://github.com/casact/chainladder-python/pull/844) +* Improve `Development` and `DevelopmentConstant` docstrings and examples by @kennethshsu in [#834](https://github.com/casact/chainladder-python/pull/834), [#993](https://github.com/casact/chainladder-python/pull/993) +* Clarify the `Triangle` constructor docstring and document `array_backend`/`pattern` by @SaguaroDev in [#812](https://github.com/casact/chainladder-python/pull/812), [#813](https://github.com/casact/chainladder-python/pull/813) +* Document creating triangles with preexisting ultimates by @EKtheSage in [#968](https://github.com/casact/chainladder-python/pull/968) +* Add Semiannual (S) grain documentation by @SaguaroDev in [#814](https://github.com/casact/chainladder-python/pull/814) +* Add `ARCHITECTURE.md` by @genedan in [#1070](https://github.com/casact/chainladder-python/pull/1070) +* Various docstring fixes by @henrydingliu in [#1020](https://github.com/casact/chainladder-python/pull/1020) + +**Typing & Refactoring** +* Add pandas type annotations by @genedan in [#1035](https://github.com/casact/chainladder-python/pull/1035), [#1065](https://github.com/casact/chainladder-python/pull/1065) +* Add a pyright type-completeness workflow by @genedan in [#1042](https://github.com/casact/chainladder-python/pull/1042) +* Refactor `cl.__init__.py` and remove dead Python 3.8 code by @genedan in [#857](https://github.com/casact/chainladder-python/pull/857), [#867](https://github.com/casact/chainladder-python/pull/867) +* Refactor `swapaxes` to object by @henrydingliu in [#1084](https://github.com/casact/chainladder-python/pull/1084) +* Enhance `utils.model_diagnostics` by @henrydingliu in [#953](https://github.com/casact/chainladder-python/pull/953), [#954](https://github.com/casact/chainladder-python/pull/954), [#959](https://github.com/casact/chainladder-python/pull/959), [#955](https://github.com/casact/chainladder-python/pull/955), [#982](https://github.com/casact/chainladder-python/pull/982) + +**Testing & CI** +* Add tests for utilities, slicing, dunders/arithmetic, pandas, base, IO, `TriangleDisplay`, and common behavior by @genedan in [#831](https://github.com/casact/chainladder-python/pull/831), [#946](https://github.com/casact/chainladder-python/pull/946), [#950](https://github.com/casact/chainladder-python/pull/950), [#948](https://github.com/casact/chainladder-python/pull/948), [#1038](https://github.com/casact/chainladder-python/pull/1038), [#1046](https://github.com/casact/chainladder-python/pull/1046), [#1002](https://github.com/casact/chainladder-python/pull/1002), [#936](https://github.com/casact/chainladder-python/pull/936), [#1083](https://github.com/casact/chainladder-python/pull/1083), [#1033](https://github.com/casact/chainladder-python/pull/1033), [#825](https://github.com/casact/chainladder-python/pull/825), [#1082](https://github.com/casact/chainladder-python/pull/1082) +* Replace stale xfails with `pytest.raises` by @EKtheSage in [#977](https://github.com/casact/chainladder-python/pull/977), [#987](https://github.com/casact/chainladder-python/pull/987) +* Add coverage and various doctest fixes by @henrydingliu in [#1105](https://github.com/casact/chainladder-python/pull/1105), [#1099](https://github.com/casact/chainladder-python/pull/1099), [#1101](https://github.com/casact/chainladder-python/pull/1101) + +**Dependencies** +* Bump idna, tornado, bleach, soupsieve, and mistune by @dependabot[bot] in [#822](https://github.com/casact/chainladder-python/pull/822), [#962](https://github.com/casact/chainladder-python/pull/962), [#1001](https://github.com/casact/chainladder-python/pull/1001), [#1007](https://github.com/casact/chainladder-python/pull/1007), [#1107](https://github.com/casact/chainladder-python/pull/1107), [#1108](https://github.com/casact/chainladder-python/pull/1108) +* Pin to avoid pandas 3.0.4 and set a maximum sparse version by @henrydingliu in [#1072](https://github.com/casact/chainladder-python/pull/1072), [#957](https://github.com/casact/chainladder-python/pull/957) + +**New Contributors** +* @salexanian made their first contribution in [#793](https://github.com/casact/chainladder-python/pull/793) +* @etw002 made their first contribution in [#1009](https://github.com/casact/chainladder-python/pull/1009) + +**Full Changelog**: https://github.com/casact/chainladder-python/compare/v0.9.2...v0.10.0 + ## Version 0.9 ### Version 0.9.2 diff --git a/pyproject.toml b/pyproject.toml index 4a64ae847..dc10a1ba6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "chainladder" -version = "0.9.2" +version = "0.10.0" authors = [ {name = "John Bogaardt", email = "jbogaardt@gmail.com"}, ] From 1865e79d71f027f856717c8abecbe3b995925aeb Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Tue, 14 Jul 2026 14:33:28 -0700 Subject: [PATCH 02/28] Updated release notes --- docs/library/releases.md | 64 ++++++++++++---------------------------- 1 file changed, 19 insertions(+), 45 deletions(-) diff --git a/docs/library/releases.md b/docs/library/releases.md index 7b5718a3c..5fc4f4469 100644 --- a/docs/library/releases.md +++ b/docs/library/releases.md @@ -6,24 +6,12 @@ Release Date: TBD -**Highlights** -* **Pandas 3 and Python 3.14 support** — datetime precision now follows the installed pandas default, string-dtype comparisons, removal of `DataFrame.groupby(..., axis=1)`, and cleanup of dead pandas 1.x code ([#807](https://github.com/casact/chainladder-python/pull/807), [#816](https://github.com/casact/chainladder-python/pull/816), [#817](https://github.com/casact/chainladder-python/pull/817), [#818](https://github.com/casact/chainladder-python/pull/818), [#819](https://github.com/casact/chainladder-python/pull/819), [#820](https://github.com/casact/chainladder-python/pull/820)). -* **`numpy>=2.0` is now the minimum supported version** ([#899](https://github.com/casact/chainladder-python/pull/899)); `numba` is no longer a direct dependency ([#898](https://github.com/casact/chainladder-python/pull/898)). -* **Backend deprecations** — the `dask` array backend ([#937](https://github.com/casact/chainladder-python/pull/937)) and dask parallel-compute (bag) paths ([#1008](https://github.com/casact/chainladder-python/pull/1008)), and the `cupy` array backend ([#881](https://github.com/casact/chainladder-python/pull/881)), are deprecated. The `option` parameter of `cl.options.get_option`/`set_option`/`reset_option` is deprecated in favor of `pat`. -* **New public `TriangleWeight` utility** for `latest_n` and drop-based weighting outside the `Development` estimator ([#941](https://github.com/casact/chainladder-python/pull/941)). -* **New `DisposalRate` adjustment method** ([#1022](https://github.com/casact/chainladder-python/pull/1022)). -* **`MackChainladder` multi-triangle (4D) support** ([#873](https://github.com/casact/chainladder-python/pull/873)) plus reconciliation to the Mack papers ([#1096](https://github.com/casact/chainladder-python/pull/1096)); `BootstrapODPSample` multi-index support ([#917](https://github.com/casact/chainladder-python/pull/917)). -* **New `Development` averaging** — geometric averaging type ([#811](https://github.com/casact/chainladder-python/pull/811)) — and a new `Triangle.xs` method ([#763](https://github.com/casact/chainladder-python/pull/763)). -* **New `Triangle.empty` attribute** for pandas consistency ([#901](https://github.com/casact/chainladder-python/pull/901)) and a new `cl.options.describe_option` helper ([#877](https://github.com/casact/chainladder-python/pull/877)). -* **New sample datasets** — `clrd2025`, additional Friedland exhibits, and a centralized sample-dataset manifest surfaced through `list_samples` ([#771](https://github.com/casact/chainladder-python/pull/771), [#787](https://github.com/casact/chainladder-python/pull/787), [#837](https://github.com/casact/chainladder-python/pull/837), [#886](https://github.com/casact/chainladder-python/pull/886), [#1116](https://github.com/casact/chainladder-python/pull/1116)). -* **Type annotations and tooling** — extensive pandas annotations, an explicit public API surface, a pyright type-completeness workflow, and a new `ARCHITECTURE.md` ([#912](https://github.com/casact/chainladder-python/pull/912), [#1035](https://github.com/casact/chainladder-python/pull/1035), [#1042](https://github.com/casact/chainladder-python/pull/1042), [#1065](https://github.com/casact/chainladder-python/pull/1065), [#1070](https://github.com/casact/chainladder-python/pull/1070)). -* **Docs and tests** — large expansion of doctest examples and new unit-test suites across slicing, dunders/arithmetic, pandas compatibility, base, and IO. - **What's Changed** -*A curated summary is below. For the complete, unedited list of every merged pull request (including internal integration-branch and CI churn), see the Full Changelog link at the bottom.* +*Changes are grouped into the standard categories below. See the Full Changelog link for the complete, unedited list of merged pull requests.* **Enhancements** +* Add support for pandas 3 and Python 3.14 by @genedan in [#807](https://github.com/casact/chainladder-python/pull/807), [#816](https://github.com/casact/chainladder-python/pull/816), [#817](https://github.com/casact/chainladder-python/pull/817), [#818](https://github.com/casact/chainladder-python/pull/818), [#819](https://github.com/casact/chainladder-python/pull/819), [#820](https://github.com/casact/chainladder-python/pull/820) * Add geometric averaging type to the `Development` estimator by @kennethshsu in [#811](https://github.com/casact/chainladder-python/pull/811) * Add `TriangleWeight` utility class as a public API by @henrydingliu in [#941](https://github.com/casact/chainladder-python/pull/941) * Add `DisposalRate` method by @henrydingliu in [#1022](https://github.com/casact/chainladder-python/pull/1022) @@ -34,22 +22,14 @@ Release Date: TBD * Implement `cl.options.describe_option` by @genedan in [#877](https://github.com/casact/chainladder-python/pull/877) * Allow `DevelopmentConstant` to be callable on columns by @henrydingliu in [#796](https://github.com/casact/chainladder-python/pull/796) * Return a Python `bool` from `is_ultimate` by @salexanian in [#793](https://github.com/casact/chainladder-python/pull/793) - -**Pandas 3 / Python 3.14 support** -* Replace datetime constants with pandas default datetime precision by @genedan in [#816](https://github.com/casact/chainladder-python/pull/816) -* Replace object-type comparisons with string comparisons for string dtypes by @genedan in [#819](https://github.com/casact/chainladder-python/pull/819) -* Replace `DataFrame.groupby(..., axis=1)` with a double transpose by @genedan in [#818](https://github.com/casact/chainladder-python/pull/818) -* Wrap raw HTML in `StringIO` by @genedan in [#817](https://github.com/casact/chainladder-python/pull/817) -* pandas 3 / Python 3.14 support by @genedan in [#820](https://github.com/casact/chainladder-python/pull/820) -* Clean up dead pandas 1.x code by @genedan in [#807](https://github.com/casact/chainladder-python/pull/807) - -**API Changes & Deprecations** -* Deprecate the `dask` array backend by @priyam0k in [#937](https://github.com/casact/chainladder-python/pull/937) and the dask parallel-compute (bag) paths in [#1008](https://github.com/casact/chainladder-python/pull/1008) -* Deprecate the `cupy` array backend by @priyam0k in [#881](https://github.com/casact/chainladder-python/pull/881) -* Make API exports explicit by @priyam0k in [#912](https://github.com/casact/chainladder-python/pull/912) -* Remove deprecated `broadcast_axis` by @kennethshsu in [#991](https://github.com/casact/chainladder-python/pull/991) -* Set minimum numpy version to 2.0 by @SaguaroDev in [#899](https://github.com/casact/chainladder-python/pull/899) -* Remove numba as a direct dependency by @SaguaroDev in [#898](https://github.com/casact/chainladder-python/pull/898) +* Add the `clrd2025` dataset and centralize sample metadata in a manifest surfaced via `list_samples` by @SaguaroDev in [#771](https://github.com/casact/chainladder-python/pull/771), [#886](https://github.com/casact/chainladder-python/pull/886) +* Add Friedland Exhibit III samples and earned premium by @salexanian in [#787](https://github.com/casact/chainladder-python/pull/787) +* Add Friedland Chapter 6–7 exhibits by @henrydingliu in [#837](https://github.com/casact/chainladder-python/pull/837) +* Add the merged Friedland USPP sample dataset by @salexanian in [#1116](https://github.com/casact/chainladder-python/pull/1116) +* Add Friedland auto BI, GL insurer, GL self-insurer, and US industry auto premium datasets by @kennethshsu in [#870](https://github.com/casact/chainladder-python/pull/870), [#905](https://github.com/casact/chainladder-python/pull/905), [#921](https://github.com/casact/chainladder-python/pull/921), [#904](https://github.com/casact/chainladder-python/pull/904) +* Add tests for utilities, slicing, dunders/arithmetic, pandas, base, IO, `TriangleDisplay`, and common behavior by @genedan in [#831](https://github.com/casact/chainladder-python/pull/831), [#946](https://github.com/casact/chainladder-python/pull/946), [#950](https://github.com/casact/chainladder-python/pull/950), [#948](https://github.com/casact/chainladder-python/pull/948), [#1038](https://github.com/casact/chainladder-python/pull/1038), [#1046](https://github.com/casact/chainladder-python/pull/1046), [#1002](https://github.com/casact/chainladder-python/pull/1002), [#936](https://github.com/casact/chainladder-python/pull/936), [#1083](https://github.com/casact/chainladder-python/pull/1083), [#1033](https://github.com/casact/chainladder-python/pull/1033), [#825](https://github.com/casact/chainladder-python/pull/825), [#1082](https://github.com/casact/chainladder-python/pull/1082) +* Replace stale xfails with `pytest.raises` by @EKtheSage in [#977](https://github.com/casact/chainladder-python/pull/977), [#987](https://github.com/casact/chainladder-python/pull/987) +* Add coverage and various doctest fixes by @henrydingliu in [#1105](https://github.com/casact/chainladder-python/pull/1105), [#1099](https://github.com/casact/chainladder-python/pull/1099), [#1101](https://github.com/casact/chainladder-python/pull/1101) **Bug Fixes** * Fix `sigma_` formula by @henrydingliu in [#965](https://github.com/casact/chainladder-python/pull/965) @@ -61,14 +41,12 @@ Release Date: TBD * Various Mack fixes by @henrydingliu in [#1097](https://github.com/casact/chainladder-python/pull/1097) * Various Friedland data fixes by @henrydingliu in [#927](https://github.com/casact/chainladder-python/pull/927), [#967](https://github.com/casact/chainladder-python/pull/967) -**Sample Data** -* Add the `clrd2025` dataset and centralize sample metadata in a manifest surfaced via `list_samples` by @SaguaroDev in [#771](https://github.com/casact/chainladder-python/pull/771), [#886](https://github.com/casact/chainladder-python/pull/886) -* Add Friedland Exhibit III samples and earned premium by @salexanian in [#787](https://github.com/casact/chainladder-python/pull/787) -* Add Friedland Chapter 6–7 exhibits by @henrydingliu in [#837](https://github.com/casact/chainladder-python/pull/837) -* Add the merged Friedland USPP sample dataset by @salexanian in [#1116](https://github.com/casact/chainladder-python/pull/1116) -* Add Friedland auto BI, GL insurer, GL self-insurer, and US industry auto premium datasets by @kennethshsu in [#870](https://github.com/casact/chainladder-python/pull/870), [#905](https://github.com/casact/chainladder-python/pull/905), [#921](https://github.com/casact/chainladder-python/pull/921), [#904](https://github.com/casact/chainladder-python/pull/904) +**Deprecations & Removals** +* Deprecate the `dask` array backend by @priyam0k in [#937](https://github.com/casact/chainladder-python/pull/937) and the dask parallel-compute (bag) paths in [#1008](https://github.com/casact/chainladder-python/pull/1008) +* Deprecate the `cupy` array backend by @priyam0k in [#881](https://github.com/casact/chainladder-python/pull/881) +* Remove deprecated `broadcast_axis` by @kennethshsu in [#991](https://github.com/casact/chainladder-python/pull/991) -**Documentation** +**Maintenance** * Add doctest examples across the development, adjustment, tail, ClarkLDF, workflow, and utility estimators by @EKtheSage in [#799](https://github.com/casact/chainladder-python/pull/799), [#800](https://github.com/casact/chainladder-python/pull/800), [#801](https://github.com/casact/chainladder-python/pull/801), [#802](https://github.com/casact/chainladder-python/pull/802), [#803](https://github.com/casact/chainladder-python/pull/803), [#804](https://github.com/casact/chainladder-python/pull/804) * Add doctest examples for `BootstrapODPSample` and the correlation classes by @priyam0k in [#836](https://github.com/casact/chainladder-python/pull/836), [#844](https://github.com/casact/chainladder-python/pull/844) * Improve `Development` and `DevelopmentConstant` docstrings and examples by @kennethshsu in [#834](https://github.com/casact/chainladder-python/pull/834), [#993](https://github.com/casact/chainladder-python/pull/993) @@ -77,22 +55,18 @@ Release Date: TBD * Add Semiannual (S) grain documentation by @SaguaroDev in [#814](https://github.com/casact/chainladder-python/pull/814) * Add `ARCHITECTURE.md` by @genedan in [#1070](https://github.com/casact/chainladder-python/pull/1070) * Various docstring fixes by @henrydingliu in [#1020](https://github.com/casact/chainladder-python/pull/1020) - -**Typing & Refactoring** * Add pandas type annotations by @genedan in [#1035](https://github.com/casact/chainladder-python/pull/1035), [#1065](https://github.com/casact/chainladder-python/pull/1065) * Add a pyright type-completeness workflow by @genedan in [#1042](https://github.com/casact/chainladder-python/pull/1042) +* Make API exports explicit by @priyam0k in [#912](https://github.com/casact/chainladder-python/pull/912) * Refactor `cl.__init__.py` and remove dead Python 3.8 code by @genedan in [#857](https://github.com/casact/chainladder-python/pull/857), [#867](https://github.com/casact/chainladder-python/pull/867) * Refactor `swapaxes` to object by @henrydingliu in [#1084](https://github.com/casact/chainladder-python/pull/1084) * Enhance `utils.model_diagnostics` by @henrydingliu in [#953](https://github.com/casact/chainladder-python/pull/953), [#954](https://github.com/casact/chainladder-python/pull/954), [#959](https://github.com/casact/chainladder-python/pull/959), [#955](https://github.com/casact/chainladder-python/pull/955), [#982](https://github.com/casact/chainladder-python/pull/982) -**Testing & CI** -* Add tests for utilities, slicing, dunders/arithmetic, pandas, base, IO, `TriangleDisplay`, and common behavior by @genedan in [#831](https://github.com/casact/chainladder-python/pull/831), [#946](https://github.com/casact/chainladder-python/pull/946), [#950](https://github.com/casact/chainladder-python/pull/950), [#948](https://github.com/casact/chainladder-python/pull/948), [#1038](https://github.com/casact/chainladder-python/pull/1038), [#1046](https://github.com/casact/chainladder-python/pull/1046), [#1002](https://github.com/casact/chainladder-python/pull/1002), [#936](https://github.com/casact/chainladder-python/pull/936), [#1083](https://github.com/casact/chainladder-python/pull/1083), [#1033](https://github.com/casact/chainladder-python/pull/1033), [#825](https://github.com/casact/chainladder-python/pull/825), [#1082](https://github.com/casact/chainladder-python/pull/1082) -* Replace stale xfails with `pytest.raises` by @EKtheSage in [#977](https://github.com/casact/chainladder-python/pull/977), [#987](https://github.com/casact/chainladder-python/pull/987) -* Add coverage and various doctest fixes by @henrydingliu in [#1105](https://github.com/casact/chainladder-python/pull/1105), [#1099](https://github.com/casact/chainladder-python/pull/1099), [#1101](https://github.com/casact/chainladder-python/pull/1101) - **Dependencies** -* Bump idna, tornado, bleach, soupsieve, and mistune by @dependabot[bot] in [#822](https://github.com/casact/chainladder-python/pull/822), [#962](https://github.com/casact/chainladder-python/pull/962), [#1001](https://github.com/casact/chainladder-python/pull/1001), [#1007](https://github.com/casact/chainladder-python/pull/1007), [#1107](https://github.com/casact/chainladder-python/pull/1107), [#1108](https://github.com/casact/chainladder-python/pull/1108) +* Set minimum numpy version to 2.0 by @SaguaroDev in [#899](https://github.com/casact/chainladder-python/pull/899) +* Remove numba as a direct dependency by @SaguaroDev in [#898](https://github.com/casact/chainladder-python/pull/898) * Pin to avoid pandas 3.0.4 and set a maximum sparse version by @henrydingliu in [#1072](https://github.com/casact/chainladder-python/pull/1072), [#957](https://github.com/casact/chainladder-python/pull/957) +* Bump idna, tornado, bleach, soupsieve, and mistune by @dependabot[bot] in [#822](https://github.com/casact/chainladder-python/pull/822), [#962](https://github.com/casact/chainladder-python/pull/962), [#1001](https://github.com/casact/chainladder-python/pull/1001), [#1007](https://github.com/casact/chainladder-python/pull/1007), [#1107](https://github.com/casact/chainladder-python/pull/1107), [#1108](https://github.com/casact/chainladder-python/pull/1108) **New Contributors** * @salexanian made their first contribution in [#793](https://github.com/casact/chainladder-python/pull/793) From e18580884579c992b250ef51d5dc27d14fde6efc Mon Sep 17 00:00:00 2001 From: priyam0k <87162535+priyam0k@users.noreply.github.com> Date: Thu, 16 Jul 2026 04:23:27 +0530 Subject: [PATCH 03/28] feat(core): add index/columns/origin/development alternatives to Triangle.drop() Route the keyword alternatives to their axis, mirroring pandas. Refs #1060. --- chainladder/core/pandas.py | 58 ++++++++++++++++++++----- chainladder/core/tests/test_triangle.py | 31 +++++++++++++ chainladder/core/typing.py | 2 +- 3 files changed, 79 insertions(+), 12 deletions(-) diff --git a/chainladder/core/pandas.py b/chainladder/core/pandas.py index 35a83b1b1..8c525d5cc 100644 --- a/chainladder/core/pandas.py +++ b/chainladder/core/pandas.py @@ -601,33 +601,69 @@ def drop( self, labels: str | int | list | None = None, axis: Literal["index", "columns", "origin", "development"] | int = 1, + index: str | int | list | None = None, + columns: str | int | list | None = None, + origin: str | int | list | None = None, + development: str | int | list | None = None, ) -> Triangle: """Drop specified labels from rows or columns. - Remove rows or columns by specifying label names and corresponding axis, - or by specifying directly index or column names. + Remove labels by specifying label names and corresponding axis, or by + specifying directly ``index``, ``columns``, ``origin``, or + ``development`` names. Parameters ----------- labels: str | int | list | None - Index or column labels to drop. + Index or column labels to drop. A single label or list-like. - axis: {0 or ‘index’, 1 or ‘columns’}, default 1 - Whether to drop labels from the index (0 or ‘index’) - or columns (1 or ‘columns’). + axis: {0 or ‘index’, 1 or ‘columns’, 2 or 'origin', 3 or 'development'}, default 1 + The axis to drop ``labels`` from. + + index: str | int | list | None + Alternative to ``axis=0``. Equivalent to ``labels, axis=0``. + + columns: str | int | list | None + Alternative to ``axis=1``. Equivalent to ``labels, axis=1``. + + origin: str | int | list | None + Alternative to ``axis=2``. Equivalent to ``labels, axis=2``. + + development: str | int | list | None + Alternative to ``axis=3``. Equivalent to ``labels, axis=3``. Returns ------- Triangle """ - axis = self._get_axis(axis) - labels = [labels] if type(labels) is str else list(labels) - if axis == 1: - return self[[item for item in self.columns if item not in labels]] + alternatives = {0: index, 1: columns, 2: origin, 3: development} + if any(value is not None for value in alternatives.values()): + if labels is not None: + raise ValueError( + "Cannot specify both 'labels' and any of 'index', " + "'columns', 'origin', or 'development'." + ) + to_drop = { + ax: value for ax, value in alternatives.items() if value is not None + } else: - raise NotImplementedError("Triangle.drop() only implemented for column axis.") + to_drop = {self._get_axis(axis): labels} + result = self + for ax, ax_labels in to_drop.items(): + ax_labels = ( + [ax_labels] if type(ax_labels) is str else list(ax_labels) + ) + if ax == 1: + result = result[ + [item for item in result.columns if item not in ax_labels] + ] + else: + raise NotImplementedError( + "Triangle.drop() only implemented for column axis." + ) + return result @property def T(self) -> DataFrame: # noqa: N802 diff --git a/chainladder/core/tests/test_triangle.py b/chainladder/core/tests/test_triangle.py index 1a0857e79..16c7a2dba 100644 --- a/chainladder/core/tests/test_triangle.py +++ b/chainladder/core/tests/test_triangle.py @@ -338,6 +338,37 @@ def test_drop_invalid_axis_raises(clrd): clrd.drop(labels="CumPaidLoss", axis="bogus") +def test_drop_columns_alternative(clrd): + """columns= should be equivalent to labels=..., axis=1.""" + result = clrd.drop(columns="CumPaidLoss") + assert "CumPaidLoss" not in result.columns + assert result == clrd.drop(labels="CumPaidLoss", axis=1) + + +def test_drop_columns_alternative_list(clrd): + """columns= should accept a list of labels.""" + result = clrd.drop(columns=["CumPaidLoss", "IncurLoss"]) + assert "CumPaidLoss" not in result.columns + assert "IncurLoss" not in result.columns + assert result == clrd.drop(labels=["CumPaidLoss", "IncurLoss"], axis=1) + + +def test_drop_labels_and_alternative_raises(clrd): + """Specifying labels together with an alternative should raise ValueError.""" + with pytest.raises(ValueError): + clrd.drop(labels="CumPaidLoss", columns="IncurLoss") + + +def test_drop_index_origin_development_alternatives_raise(clrd): + """index/origin/development alternatives route to unimplemented axes.""" + with pytest.raises(NotImplementedError): + clrd.drop(index="commauto") + with pytest.raises(NotImplementedError): + clrd.drop(origin="1995") + with pytest.raises(NotImplementedError): + clrd.drop(development="12") + + def test_exposure_tri(): x = cl.load_sample("auto") x = x[x.development == 12] diff --git a/chainladder/core/typing.py b/chainladder/core/typing.py index 5f39b3332..d5231f716 100644 --- a/chainladder/core/typing.py +++ b/chainladder/core/typing.py @@ -97,7 +97,7 @@ def __len__(self) -> int: ... def get_array_module(self, arr: ArrayLike | None = None) -> ModuleType: ... def copy(self) -> Triangle: ... def set_backend(self, backend: str, inplace: bool = False, **kwargs) -> Triangle: ... - def drop(self, labels: str | int | list | None = None, axis: Literal["index", "columns", "origin", "development"] | int = 1) -> Triangle: ... + def drop(self, labels: str | int | list | None = None, axis: Literal["index", "columns", "origin", "development"] | int = 1, index: str | int | list | None = None, columns: str | int | list | None = None, origin: str | int | list | None = None, development: str | int | list | None = None) -> Triangle: ... def val_to_dev(self) -> Triangle: ... def _repr_format(self, origin_as_datetime: bool = False) -> pd.DataFrame: ... def _slice(self, key: pd.Series | np.ndarray, axis: Literal['ddims', 'odims']) -> Triangle: ... From a1e721b5123716416cfb0342e68b17f8a61bd5ac Mon Sep 17 00:00:00 2001 From: priyam0k <87162535+priyam0k@users.noreply.github.com> Date: Wed, 22 Jul 2026 11:45:17 +0530 Subject: [PATCH 04/28] docs(core): add drop() examples and wrap TriangleProtocol.drop signature Address review on #1131: format the long drop() signature one arg per line and add testable Examples to the docstring. --- chainladder/core/pandas.py | 29 +++++++++++++++++++++++++++++ chainladder/core/typing.py | 10 +++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/chainladder/core/pandas.py b/chainladder/core/pandas.py index 8c525d5cc..c1e571ad1 100644 --- a/chainladder/core/pandas.py +++ b/chainladder/core/pandas.py @@ -637,6 +637,35 @@ def drop( ------- Triangle + Examples + -------- + + Drop a single column with the ``labels``/``axis`` form or the + ``columns`` alternative; the two are equivalent. + + .. testsetup:: + + import chainladder as cl + + .. testcode:: + + tri = cl.load_sample('clrd') + print(tri.drop(columns='CumPaidLoss').columns.tolist()) + + .. testoutput:: + + ['IncurLoss', 'BulkLoss', 'EarnedPremDIR', 'EarnedPremCeded', 'EarnedPremNet'] + + A list of labels can be dropped from an axis as well. + + .. testcode:: + + print(tri.drop(columns=['CumPaidLoss', 'IncurLoss']).columns.tolist()) + + .. testoutput:: + + ['BulkLoss', 'EarnedPremDIR', 'EarnedPremCeded', 'EarnedPremNet'] + """ alternatives = {0: index, 1: columns, 2: origin, 3: development} if any(value is not None for value in alternatives.values()): diff --git a/chainladder/core/typing.py b/chainladder/core/typing.py index d5231f716..ae0405193 100644 --- a/chainladder/core/typing.py +++ b/chainladder/core/typing.py @@ -97,7 +97,15 @@ def __len__(self) -> int: ... def get_array_module(self, arr: ArrayLike | None = None) -> ModuleType: ... def copy(self) -> Triangle: ... def set_backend(self, backend: str, inplace: bool = False, **kwargs) -> Triangle: ... - def drop(self, labels: str | int | list | None = None, axis: Literal["index", "columns", "origin", "development"] | int = 1, index: str | int | list | None = None, columns: str | int | list | None = None, origin: str | int | list | None = None, development: str | int | list | None = None) -> Triangle: ... + def drop( + self, + labels: str | int | list | None = None, + axis: Literal["index", "columns", "origin", "development"] | int = 1, + index: str | int | list | None = None, + columns: str | int | list | None = None, + origin: str | int | list | None = None, + development: str | int | list | None = None, + ) -> Triangle: ... def val_to_dev(self) -> Triangle: ... def _repr_format(self, origin_as_datetime: bool = False) -> pd.DataFrame: ... def _slice(self, key: pd.Series | np.ndarray, axis: Literal['ddims', 'odims']) -> Triangle: ... From 2a7c50ef556265cabc784130532ce4846dfdb665 Mon Sep 17 00:00:00 2001 From: priyam0k <87162535+priyam0k@users.noreply.github.com> Date: Wed, 22 Jul 2026 11:54:03 +0530 Subject: [PATCH 05/28] fix(core): handle integer labels in Triangle.drop() Normalize any scalar (str or int) into a list so a bare int routes to its axis instead of raising TypeError. Addresses Bugbot review on #1131. --- chainladder/core/pandas.py | 4 +++- chainladder/core/tests/test_triangle.py | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/chainladder/core/pandas.py b/chainladder/core/pandas.py index c1e571ad1..704ecc7b3 100644 --- a/chainladder/core/pandas.py +++ b/chainladder/core/pandas.py @@ -682,7 +682,9 @@ def drop( result = self for ax, ax_labels in to_drop.items(): ax_labels = ( - [ax_labels] if type(ax_labels) is str else list(ax_labels) + list(ax_labels) + if isinstance(ax_labels, (list, tuple)) + else [ax_labels] ) if ax == 1: result = result[ diff --git a/chainladder/core/tests/test_triangle.py b/chainladder/core/tests/test_triangle.py index 16c7a2dba..81f04cd1d 100644 --- a/chainladder/core/tests/test_triangle.py +++ b/chainladder/core/tests/test_triangle.py @@ -369,6 +369,12 @@ def test_drop_index_origin_development_alternatives_raise(clrd): clrd.drop(development="12") +def test_drop_integer_label_routes_to_axis(clrd): + """A bare int label should route to its axis, not raise TypeError.""" + with pytest.raises(NotImplementedError): + clrd.drop(development=12) + + def test_exposure_tri(): x = cl.load_sample("auto") x = x[x.development == 12] From 815783001dd511e4985933e978c3a37f7d636cbc Mon Sep 17 00:00:00 2001 From: priyam0k <87162535+priyam0k@users.noreply.github.com> Date: Wed, 22 Jul 2026 12:07:32 +0530 Subject: [PATCH 06/28] fix(core): preserve list-like handling in Triangle.drop() Use np.isscalar so pd.Index/ndarray labels pass through list() while scalars (str/int) are wrapped, and a bare drop() still raises. Addresses Bugbot review on #1131. --- chainladder/core/pandas.py | 4 +--- chainladder/core/tests/test_triangle.py | 9 +++++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/chainladder/core/pandas.py b/chainladder/core/pandas.py index 704ecc7b3..20c05be5d 100644 --- a/chainladder/core/pandas.py +++ b/chainladder/core/pandas.py @@ -682,9 +682,7 @@ def drop( result = self for ax, ax_labels in to_drop.items(): ax_labels = ( - list(ax_labels) - if isinstance(ax_labels, (list, tuple)) - else [ax_labels] + [ax_labels] if np.isscalar(ax_labels) else list(ax_labels) ) if ax == 1: result = result[ diff --git a/chainladder/core/tests/test_triangle.py b/chainladder/core/tests/test_triangle.py index 81f04cd1d..2a7ceb5f5 100644 --- a/chainladder/core/tests/test_triangle.py +++ b/chainladder/core/tests/test_triangle.py @@ -375,6 +375,15 @@ def test_drop_integer_label_routes_to_axis(clrd): clrd.drop(development=12) +def test_drop_columns_alternative_index_like(clrd): + """columns= should accept list-likes such as pd.Index and ndarray.""" + labels = clrd.columns[:2] + result = clrd.drop(columns=labels) + assert all(label not in result.columns for label in labels) + assert result == clrd.drop(columns=list(labels)) + assert result == clrd.drop(columns=labels.values) + + def test_exposure_tri(): x = cl.load_sample("auto") x = x[x.development == 12] From b213147c4ad7463771400a9f5c1e5a40a3b8a98b Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Wed, 22 Jul 2026 10:02:13 -0700 Subject: [PATCH 07/28] Sync from main, added a few more updates --- docs/library/releases.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/library/releases.md b/docs/library/releases.md index 5fc4f4469..8e31818e6 100644 --- a/docs/library/releases.md +++ b/docs/library/releases.md @@ -27,6 +27,8 @@ Release Date: TBD * Add Friedland Chapter 6–7 exhibits by @henrydingliu in [#837](https://github.com/casact/chainladder-python/pull/837) * Add the merged Friedland USPP sample dataset by @salexanian in [#1116](https://github.com/casact/chainladder-python/pull/1116) * Add Friedland auto BI, GL insurer, GL self-insurer, and US industry auto premium datasets by @kennethshsu in [#870](https://github.com/casact/chainladder-python/pull/870), [#905](https://github.com/casact/chainladder-python/pull/905), [#921](https://github.com/casact/chainladder-python/pull/921), [#904](https://github.com/casact/chainladder-python/pull/904) +* Add Friedland Chapter 9 (Bornhuetter-Ferguson) documentation by @priyam0k in [#1126](https://github.com/casact/chainladder-python/pull/1126) +* Add `pct_reported_` and `pct_unreported_` properties by @priyam0k in [#1126](https://github.com/casact/chainladder-python/pull/1126) * Add tests for utilities, slicing, dunders/arithmetic, pandas, base, IO, `TriangleDisplay`, and common behavior by @genedan in [#831](https://github.com/casact/chainladder-python/pull/831), [#946](https://github.com/casact/chainladder-python/pull/946), [#950](https://github.com/casact/chainladder-python/pull/950), [#948](https://github.com/casact/chainladder-python/pull/948), [#1038](https://github.com/casact/chainladder-python/pull/1038), [#1046](https://github.com/casact/chainladder-python/pull/1046), [#1002](https://github.com/casact/chainladder-python/pull/1002), [#936](https://github.com/casact/chainladder-python/pull/936), [#1083](https://github.com/casact/chainladder-python/pull/1083), [#1033](https://github.com/casact/chainladder-python/pull/1033), [#825](https://github.com/casact/chainladder-python/pull/825), [#1082](https://github.com/casact/chainladder-python/pull/1082) * Replace stale xfails with `pytest.raises` by @EKtheSage in [#977](https://github.com/casact/chainladder-python/pull/977), [#987](https://github.com/casact/chainladder-python/pull/987) * Add coverage and various doctest fixes by @henrydingliu in [#1105](https://github.com/casact/chainladder-python/pull/1105), [#1099](https://github.com/casact/chainladder-python/pull/1099), [#1101](https://github.com/casact/chainladder-python/pull/1101) @@ -40,6 +42,7 @@ Release Date: TBD * Make `quarterly.csv` development dates ISO 8601-compatible by @genedan in [#930](https://github.com/casact/chainladder-python/pull/930) * Various Mack fixes by @henrydingliu in [#1097](https://github.com/casact/chainladder-python/pull/1097) * Various Friedland data fixes by @henrydingliu in [#927](https://github.com/casact/chainladder-python/pull/927), [#967](https://github.com/casact/chainladder-python/pull/967) +* Accept string axis in `Triangle.drop()` by @priyam0k in [#1123](https://github.com/casact/chainladder-python/pull/1123) **Deprecations & Removals** * Deprecate the `dask` array backend by @priyam0k in [#937](https://github.com/casact/chainladder-python/pull/937) and the dask parallel-compute (bag) paths in [#1008](https://github.com/casact/chainladder-python/pull/1008) @@ -66,7 +69,7 @@ Release Date: TBD * Set minimum numpy version to 2.0 by @SaguaroDev in [#899](https://github.com/casact/chainladder-python/pull/899) * Remove numba as a direct dependency by @SaguaroDev in [#898](https://github.com/casact/chainladder-python/pull/898) * Pin to avoid pandas 3.0.4 and set a maximum sparse version by @henrydingliu in [#1072](https://github.com/casact/chainladder-python/pull/1072), [#957](https://github.com/casact/chainladder-python/pull/957) -* Bump idna, tornado, bleach, soupsieve, and mistune by @dependabot[bot] in [#822](https://github.com/casact/chainladder-python/pull/822), [#962](https://github.com/casact/chainladder-python/pull/962), [#1001](https://github.com/casact/chainladder-python/pull/1001), [#1007](https://github.com/casact/chainladder-python/pull/1007), [#1107](https://github.com/casact/chainladder-python/pull/1107), [#1108](https://github.com/casact/chainladder-python/pull/1108) +* Bump idna, tornado, bleach, soupsieve, mistune, and pillow by @dependabot[bot] in [#822](https://github.com/casact/chainladder-python/pull/822), [#962](https://github.com/casact/chainladder-python/pull/962), [#1001](https://github.com/casact/chainladder-python/pull/1001), [#1007](https://github.com/casact/chainladder-python/pull/1007), [#1107](https://github.com/casact/chainladder-python/pull/1107), [#1108](https://github.com/casact/chainladder-python/pull/1108), [#1141](https://github.com/casact/chainladder-python/pull/1141) **New Contributors** * @salexanian made their first contribution in [#793](https://github.com/casact/chainladder-python/pull/793) From 65f70d05f21742774de8df3b40edb3a165380a2b Mon Sep 17 00:00:00 2001 From: priyam0k <87162535+priyam0k@users.noreply.github.com> Date: Thu, 23 Jul 2026 22:21:09 +0530 Subject: [PATCH 08/28] docs(core): show original columns in drop() example Print the triangle's original columns before the drop, per review on #1131. --- chainladder/core/pandas.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/chainladder/core/pandas.py b/chainladder/core/pandas.py index 20c05be5d..d0f98eeb9 100644 --- a/chainladder/core/pandas.py +++ b/chainladder/core/pandas.py @@ -650,10 +650,12 @@ def drop( .. testcode:: tri = cl.load_sample('clrd') + print(tri.columns.tolist()) print(tri.drop(columns='CumPaidLoss').columns.tolist()) .. testoutput:: + ['IncurLoss', 'CumPaidLoss', 'BulkLoss', 'EarnedPremDIR', 'EarnedPremCeded', 'EarnedPremNet'] ['IncurLoss', 'BulkLoss', 'EarnedPremDIR', 'EarnedPremCeded', 'EarnedPremNet'] A list of labels can be dropped from an axis as well. From e4c0ff609531bfa872ac01730ec1a1e41d8d4397 Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Thu, 23 Jul 2026 11:15:48 -0700 Subject: [PATCH 09/28] Added release date --- docs/library/releases.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/library/releases.md b/docs/library/releases.md index 8e31818e6..02487b523 100644 --- a/docs/library/releases.md +++ b/docs/library/releases.md @@ -4,7 +4,7 @@ ### Version 0.10.0 -Release Date: TBD +Release Date: July 29, 2026 **What's Changed** From 2289b7892c2fa3cefa54606a2f450a4d70b93c78 Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Thu, 23 Jul 2026 16:45:47 -0700 Subject: [PATCH 10/28] Bump uv --- uv.lock | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/uv.lock b/uv.lock index c46939580..d91751045 100644 --- a/uv.lock +++ b/uv.lock @@ -189,7 +189,7 @@ wheels = [ [[package]] name = "chainladder" -version = "0.9.2" +version = "0.10.0" source = { editable = "." } dependencies = [ { name = "dill" }, @@ -924,6 +924,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7d/ed/6bfa4109fcb23a58819600392564fea69cdc6551ffd5e69ccf1d52a40cbc/greenlet-3.2.4-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8c68325b0d0acf8d91dde4e6f930967dd52a5302cd4062932a6b2e7c2969f47c", size = 271061, upload-time = "2025-08-07T13:17:15.373Z" }, { url = "https://files.pythonhosted.org/packages/2a/fc/102ec1a2fc015b3a7652abab7acf3541d58c04d3d17a8d3d6a44adae1eb1/greenlet-3.2.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:94385f101946790ae13da500603491f04a76b6e4c059dab271b3ce2e283b2590", size = 629475, upload-time = "2025-08-07T13:42:54.009Z" }, { url = "https://files.pythonhosted.org/packages/c5/26/80383131d55a4ac0fb08d71660fd77e7660b9db6bdb4e8884f46d9f2cc04/greenlet-3.2.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f10fd42b5ee276335863712fa3da6608e93f70629c631bf77145021600abc23c", size = 640802, upload-time = "2025-08-07T13:45:25.52Z" }, + { url = "https://files.pythonhosted.org/packages/9f/7c/e7833dbcd8f376f3326bd728c845d31dcde4c84268d3921afcae77d90d08/greenlet-3.2.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c8c9e331e58180d0d83c5b7999255721b725913ff6bc6cf39fa2a45841a4fd4b", size = 636703, upload-time = "2025-08-07T13:53:12.622Z" }, { url = "https://files.pythonhosted.org/packages/e9/49/547b93b7c0428ede7b3f309bc965986874759f7d89e4e04aeddbc9699acb/greenlet-3.2.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:58b97143c9cc7b86fc458f215bd0932f1757ce649e05b640fea2e79b54cedb31", size = 635417, upload-time = "2025-08-07T13:18:25.189Z" }, { url = "https://files.pythonhosted.org/packages/7f/91/ae2eb6b7979e2f9b035a9f612cf70f1bf54aad4e1d125129bef1eae96f19/greenlet-3.2.4-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c2ca18a03a8cfb5b25bc1cbe20f3d9a4c80d8c3b13ba3df49ac3961af0b1018d", size = 584358, upload-time = "2025-08-07T13:18:23.708Z" }, { url = "https://files.pythonhosted.org/packages/f7/85/433de0c9c0252b22b16d413c9407e6cb3b41df7389afc366ca204dbc1393/greenlet-3.2.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9fe0a28a7b952a21e2c062cd5756d34354117796c6d9215a87f55e38d15402c5", size = 1113550, upload-time = "2025-08-07T13:42:37.467Z" }, @@ -934,6 +935,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a4/de/f28ced0a67749cac23fecb02b694f6473f47686dff6afaa211d186e2ef9c/greenlet-3.2.4-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:96378df1de302bc38e99c3a9aa311967b7dc80ced1dcc6f171e99842987882a2", size = 272305, upload-time = "2025-08-07T13:15:41.288Z" }, { url = "https://files.pythonhosted.org/packages/09/16/2c3792cba130000bf2a31c5272999113f4764fd9d874fb257ff588ac779a/greenlet-3.2.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1ee8fae0519a337f2329cb78bd7a8e128ec0f881073d43f023c7b8d4831d5246", size = 632472, upload-time = "2025-08-07T13:42:55.044Z" }, { url = "https://files.pythonhosted.org/packages/ae/8f/95d48d7e3d433e6dae5b1682e4292242a53f22df82e6d3dda81b1701a960/greenlet-3.2.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:94abf90142c2a18151632371140b3dba4dee031633fe614cb592dbb6c9e17bc3", size = 644646, upload-time = "2025-08-07T13:45:26.523Z" }, + { url = "https://files.pythonhosted.org/packages/d5/5e/405965351aef8c76b8ef7ad370e5da58d57ef6068df197548b015464001a/greenlet-3.2.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:4d1378601b85e2e5171b99be8d2dc85f594c79967599328f95c1dc1a40f1c633", size = 640519, upload-time = "2025-08-07T13:53:13.928Z" }, { url = "https://files.pythonhosted.org/packages/25/5d/382753b52006ce0218297ec1b628e048c4e64b155379331f25a7316eb749/greenlet-3.2.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0db5594dce18db94f7d1650d7489909b57afde4c580806b8d9203b6e79cdc079", size = 639707, upload-time = "2025-08-07T13:18:27.146Z" }, { url = "https://files.pythonhosted.org/packages/1f/8e/abdd3f14d735b2929290a018ecf133c901be4874b858dd1c604b9319f064/greenlet-3.2.4-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2523e5246274f54fdadbce8494458a2ebdcdbc7b802318466ac5606d3cded1f8", size = 587684, upload-time = "2025-08-07T13:18:25.164Z" }, { url = "https://files.pythonhosted.org/packages/5d/65/deb2a69c3e5996439b0176f6651e0052542bb6c8f8ec2e3fba97c9768805/greenlet-3.2.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1987de92fec508535687fb807a5cea1560f6196285a4cde35c100b8cd632cc52", size = 1116647, upload-time = "2025-08-07T13:42:38.655Z" }, @@ -944,6 +946,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/44/69/9b804adb5fd0671f367781560eb5eb586c4d495277c93bde4307b9e28068/greenlet-3.2.4-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3b67ca49f54cede0186854a008109d6ee71f66bd57bb36abd6d0a0267b540cdd", size = 274079, upload-time = "2025-08-07T13:15:45.033Z" }, { url = "https://files.pythonhosted.org/packages/46/e9/d2a80c99f19a153eff70bc451ab78615583b8dac0754cfb942223d2c1a0d/greenlet-3.2.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ddf9164e7a5b08e9d22511526865780a576f19ddd00d62f8a665949327fde8bb", size = 640997, upload-time = "2025-08-07T13:42:56.234Z" }, { url = "https://files.pythonhosted.org/packages/3b/16/035dcfcc48715ccd345f3a93183267167cdd162ad123cd93067d86f27ce4/greenlet-3.2.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f28588772bb5fb869a8eb331374ec06f24a83a9c25bfa1f38b6993afe9c1e968", size = 655185, upload-time = "2025-08-07T13:45:27.624Z" }, + { url = "https://files.pythonhosted.org/packages/31/da/0386695eef69ffae1ad726881571dfe28b41970173947e7c558d9998de0f/greenlet-3.2.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:5c9320971821a7cb77cfab8d956fa8e39cd07ca44b6070db358ceb7f8797c8c9", size = 649926, upload-time = "2025-08-07T13:53:15.251Z" }, { url = "https://files.pythonhosted.org/packages/68/88/69bf19fd4dc19981928ceacbc5fd4bb6bc2215d53199e367832e98d1d8fe/greenlet-3.2.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c60a6d84229b271d44b70fb6e5fa23781abb5d742af7b808ae3f6efd7c9c60f6", size = 651839, upload-time = "2025-08-07T13:18:30.281Z" }, { url = "https://files.pythonhosted.org/packages/19/0d/6660d55f7373b2ff8152401a83e02084956da23ae58cddbfb0b330978fe9/greenlet-3.2.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b3812d8d0c9579967815af437d96623f45c0f2ae5f04e366de62a12d83a8fb0", size = 607586, upload-time = "2025-08-07T13:18:28.544Z" }, { url = "https://files.pythonhosted.org/packages/8e/1a/c953fdedd22d81ee4629afbb38d2f9d71e37d23caace44775a3a969147d4/greenlet-3.2.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:abbf57b5a870d30c4675928c37278493044d7c14378350b3aa5d484fa65575f0", size = 1123281, upload-time = "2025-08-07T13:42:39.858Z" }, @@ -954,6 +957,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/49/e8/58c7f85958bda41dafea50497cbd59738c5c43dbbea5ee83d651234398f4/greenlet-3.2.4-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:1a921e542453fe531144e91e1feedf12e07351b1cf6c9e8a3325ea600a715a31", size = 272814, upload-time = "2025-08-07T13:15:50.011Z" }, { url = "https://files.pythonhosted.org/packages/62/dd/b9f59862e9e257a16e4e610480cfffd29e3fae018a68c2332090b53aac3d/greenlet-3.2.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd3c8e693bff0fff6ba55f140bf390fa92c994083f838fece0f63be121334945", size = 641073, upload-time = "2025-08-07T13:42:57.23Z" }, { url = "https://files.pythonhosted.org/packages/f7/0b/bc13f787394920b23073ca3b6c4a7a21396301ed75a655bcb47196b50e6e/greenlet-3.2.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:710638eb93b1fa52823aa91bf75326f9ecdfd5e0466f00789246a5280f4ba0fc", size = 655191, upload-time = "2025-08-07T13:45:29.752Z" }, + { url = "https://files.pythonhosted.org/packages/f2/d6/6adde57d1345a8d0f14d31e4ab9c23cfe8e2cd39c3baf7674b4b0338d266/greenlet-3.2.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c5111ccdc9c88f423426df3fd1811bfc40ed66264d35aa373420a34377efc98a", size = 649516, upload-time = "2025-08-07T13:53:16.314Z" }, { url = "https://files.pythonhosted.org/packages/7f/3b/3a3328a788d4a473889a2d403199932be55b1b0060f4ddd96ee7cdfcad10/greenlet-3.2.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d76383238584e9711e20ebe14db6c88ddcedc1829a9ad31a584389463b5aa504", size = 652169, upload-time = "2025-08-07T13:18:32.861Z" }, { url = "https://files.pythonhosted.org/packages/ee/43/3cecdc0349359e1a527cbf2e3e28e5f8f06d3343aaf82ca13437a9aa290f/greenlet-3.2.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23768528f2911bcd7e475210822ffb5254ed10d71f4028387e5a99b4c6699671", size = 610497, upload-time = "2025-08-07T13:18:31.636Z" }, { url = "https://files.pythonhosted.org/packages/b8/19/06b6cf5d604e2c382a6f31cafafd6f33d5dea706f4db7bdab184bad2b21d/greenlet-3.2.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:00fadb3fedccc447f517ee0d3fd8fe49eae949e1cd0f6a611818f4f6fb7dc83b", size = 1121662, upload-time = "2025-08-07T13:42:41.117Z" }, @@ -964,6 +968,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/22/5c/85273fd7cc388285632b0498dbbab97596e04b154933dfe0f3e68156c68c/greenlet-3.2.4-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:49a30d5fda2507ae77be16479bdb62a660fa51b1eb4928b524975b3bde77b3c0", size = 273586, upload-time = "2025-08-07T13:16:08.004Z" }, { url = "https://files.pythonhosted.org/packages/d1/75/10aeeaa3da9332c2e761e4c50d4c3556c21113ee3f0afa2cf5769946f7a3/greenlet-3.2.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:299fd615cd8fc86267b47597123e3f43ad79c9d8a22bebdce535e53550763e2f", size = 686346, upload-time = "2025-08-07T13:42:59.944Z" }, { url = "https://files.pythonhosted.org/packages/c0/aa/687d6b12ffb505a4447567d1f3abea23bd20e73a5bed63871178e0831b7a/greenlet-3.2.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:c17b6b34111ea72fc5a4e4beec9711d2226285f0386ea83477cbb97c30a3f3a5", size = 699218, upload-time = "2025-08-07T13:45:30.969Z" }, + { url = "https://files.pythonhosted.org/packages/dc/8b/29aae55436521f1d6f8ff4e12fb676f3400de7fcf27fccd1d4d17fd8fecd/greenlet-3.2.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b4a1870c51720687af7fa3e7cda6d08d801dae660f75a76f3845b642b4da6ee1", size = 694659, upload-time = "2025-08-07T13:53:17.759Z" }, { url = "https://files.pythonhosted.org/packages/92/2e/ea25914b1ebfde93b6fc4ff46d6864564fba59024e928bdc7de475affc25/greenlet-3.2.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:061dc4cf2c34852b052a8620d40f36324554bc192be474b9e9770e8c042fd735", size = 695355, upload-time = "2025-08-07T13:18:34.517Z" }, { url = "https://files.pythonhosted.org/packages/72/60/fc56c62046ec17f6b0d3060564562c64c862948c9d4bc8aa807cf5bd74f4/greenlet-3.2.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44358b9bf66c8576a9f57a590d5f5d6e72fa4228b763d0e43fee6d3b06d3a337", size = 657512, upload-time = "2025-08-07T13:18:33.969Z" }, { url = "https://files.pythonhosted.org/packages/23/6e/74407aed965a4ab6ddd93a7ded3180b730d281c77b765788419484cdfeef/greenlet-3.2.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2917bdf657f5859fbf3386b12d68ede4cf1f04c90c3a6bc1f013dd68a22e2269", size = 1612508, upload-time = "2025-11-04T12:42:23.427Z" }, From 69a84b669c9a5ee773130df8f6ffeca12ee02997 Mon Sep 17 00:00:00 2001 From: Howard Friman Date: Fri, 24 Jul 2026 16:01:13 +0100 Subject: [PATCH 11/28] Apply lognormal fix for apriori draws and add tests --- chainladder/methods/benktander.py | 6 +++- chainladder/methods/tests/test_benktander.py | 35 +++++++++++++++++++- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/chainladder/methods/benktander.py b/chainladder/methods/benktander.py index bcf84390a..044e48a7a 100644 --- a/chainladder/methods/benktander.py +++ b/chainladder/methods/benktander.py @@ -254,7 +254,11 @@ def _get_benktander_aprioris(self, X, sample_weight): xp = X.get_array_module() if self.apriori_sigma != 0: random_state = xp.random.RandomState(self.random_state) - apriori = random_state.normal(self.apriori, self.apriori_sigma, X.shape[0]) + # Draw from lognormal with E[apriori] = self.apriori and SD = self.apriori_sigma. + cov = self.apriori_sigma / self.apriori + sigma_log = np.sqrt(np.log1p(cov ** 2)) + mu_log = np.log(self.apriori) - 0.5 * sigma_log ** 2 + apriori = random_state.lognormal(mu_log, sigma_log, X.shape[0]) apriori = apriori.reshape(X.shape[0], -1)[..., None, None] apriori = sample_weight * apriori apriori.kdims = X.kdims diff --git a/chainladder/methods/tests/test_benktander.py b/chainladder/methods/tests/test_benktander.py index 0b320a2aa..d7a8fed5c 100644 --- a/chainladder/methods/tests/test_benktander.py +++ b/chainladder/methods/tests/test_benktander.py @@ -75,4 +75,37 @@ def test_odd_shaped_triangle(): atr = tr.grain("OYDQ") ult1 = cl.Benktander(apriori = 1,n_iters=10000).fit(cl.Development(average="volume").fit_transform(atr),sample_weight = atr.latest_diagonal).ultimate_.sum() ult2 = cl.Benktander(apriori = 1,n_iters=10000).fit(cl.Development(average="volume").fit_transform(tr),sample_weight = tr.latest_diagonal).ultimate_.grain("OYDQ").sum() - assert abs(ult1 - ult2) < 1e-5 \ No newline at end of file + assert abs(ult1 - ult2) < 1e-5 + + +def test_bf_apriori_sigma_is_lognormal(): + """apriori_sigma must draw a strictly-positive lognormal apriori (issue #1143).""" + tri = cl.load_sample("genins") + boot = cl.BootstrapODPSample(n_sims=50000, random_state=42).fit_transform(tri) + w = boot.latest_diagonal.copy() + w.values = np.ones_like(w.values) # sample_weight=1 -> expectation_ == raw multiplier + + sigma = 0.8 + bf = cl.BornhuetterFerguson( + apriori=1.0, apriori_sigma=sigma, random_state=7 + ).fit(boot, sample_weight=w) + + mult = np.nanmean(bf.expectation_.values[:, 0, :, 0], axis=1) + mult = mult[np.isfinite(mult)] + + assert (mult > 0).all() # strictly positive -> lognormal, not normal + assert abs(mult.mean() - 1.0) < 0.02 # mean preserved (E = apriori) + assert abs(mult.std() - sigma) < 0.03 # SD preserved (== apriori_sigma) + + +def test_capecod_apriori_sigma_is_positive(): + """CapeCod scatters aprioris through the same sampler; must stay positive (#1143).""" + tri = cl.load_sample("genins") + boot = cl.BootstrapODPSample(n_sims=10000, random_state=42).fit_transform(tri) + premium = boot.latest_diagonal * 0 + 8e6 + + cc = cl.CapeCod(apriori_sigma=0.8, random_state=7).fit(boot, sample_weight=premium) + + vals = cc.expectation_.values + vals = vals[np.isfinite(vals)] + assert (vals > 0).all() # ~10% would be negative under the old normal \ No newline at end of file From 935c567f2d25ad705668af155992ffca355a7422 Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Fri, 24 Jul 2026 12:12:59 -0700 Subject: [PATCH 12/28] Added sphinx-book-theme dependency --- pyproject.toml | 1 + uv.lock | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 4a64ae847..6316c8d2a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,6 +63,7 @@ docs = [ "nbsphinx", "numpydoc", "jupyter-book<2.0", # Pin to v1.x - v2 doesn't support autodoc/autosummary yet + "sphinx-book-theme>=1.1.4", "sphinx-togglebutton", "ipython", "parso>=0.8", diff --git a/uv.lock b/uv.lock index c46939580..4ef87880b 100644 --- a/uv.lock +++ b/uv.lock @@ -221,6 +221,7 @@ all = [ { name = "pytest-cov" }, { name = "pytest-xdist" }, { name = "sphinx" }, + { name = "sphinx-book-theme" }, { name = "sphinx-rtd-theme" }, { name = "sphinx-togglebutton" }, { name = "statsmodels" }, @@ -245,6 +246,7 @@ docs = [ { name = "parso" }, { name = "polars" }, { name = "sphinx" }, + { name = "sphinx-book-theme" }, { name = "sphinx-rtd-theme" }, { name = "sphinx-togglebutton" }, { name = "statsmodels" }, @@ -299,6 +301,7 @@ requires-dist = [ { name = "scikit-learn", specifier = ">1.4.2" }, { name = "sparse", specifier = ">=0.9" }, { name = "sphinx", marker = "extra == 'docs'" }, + { name = "sphinx-book-theme", marker = "extra == 'docs'", specifier = ">=1.1.4,<1.2" }, { name = "sphinx-rtd-theme", marker = "extra == 'docs'" }, { name = "sphinx-togglebutton", marker = "extra == 'docs'" }, { name = "statsmodels", marker = "extra == 'docs'", specifier = ">=0.14.6" }, @@ -924,6 +927,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/7d/ed/6bfa4109fcb23a58819600392564fea69cdc6551ffd5e69ccf1d52a40cbc/greenlet-3.2.4-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8c68325b0d0acf8d91dde4e6f930967dd52a5302cd4062932a6b2e7c2969f47c", size = 271061, upload-time = "2025-08-07T13:17:15.373Z" }, { url = "https://files.pythonhosted.org/packages/2a/fc/102ec1a2fc015b3a7652abab7acf3541d58c04d3d17a8d3d6a44adae1eb1/greenlet-3.2.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:94385f101946790ae13da500603491f04a76b6e4c059dab271b3ce2e283b2590", size = 629475, upload-time = "2025-08-07T13:42:54.009Z" }, { url = "https://files.pythonhosted.org/packages/c5/26/80383131d55a4ac0fb08d71660fd77e7660b9db6bdb4e8884f46d9f2cc04/greenlet-3.2.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f10fd42b5ee276335863712fa3da6608e93f70629c631bf77145021600abc23c", size = 640802, upload-time = "2025-08-07T13:45:25.52Z" }, + { url = "https://files.pythonhosted.org/packages/9f/7c/e7833dbcd8f376f3326bd728c845d31dcde4c84268d3921afcae77d90d08/greenlet-3.2.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c8c9e331e58180d0d83c5b7999255721b725913ff6bc6cf39fa2a45841a4fd4b", size = 636703, upload-time = "2025-08-07T13:53:12.622Z" }, { url = "https://files.pythonhosted.org/packages/e9/49/547b93b7c0428ede7b3f309bc965986874759f7d89e4e04aeddbc9699acb/greenlet-3.2.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:58b97143c9cc7b86fc458f215bd0932f1757ce649e05b640fea2e79b54cedb31", size = 635417, upload-time = "2025-08-07T13:18:25.189Z" }, { url = "https://files.pythonhosted.org/packages/7f/91/ae2eb6b7979e2f9b035a9f612cf70f1bf54aad4e1d125129bef1eae96f19/greenlet-3.2.4-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c2ca18a03a8cfb5b25bc1cbe20f3d9a4c80d8c3b13ba3df49ac3961af0b1018d", size = 584358, upload-time = "2025-08-07T13:18:23.708Z" }, { url = "https://files.pythonhosted.org/packages/f7/85/433de0c9c0252b22b16d413c9407e6cb3b41df7389afc366ca204dbc1393/greenlet-3.2.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9fe0a28a7b952a21e2c062cd5756d34354117796c6d9215a87f55e38d15402c5", size = 1113550, upload-time = "2025-08-07T13:42:37.467Z" }, @@ -934,6 +938,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a4/de/f28ced0a67749cac23fecb02b694f6473f47686dff6afaa211d186e2ef9c/greenlet-3.2.4-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:96378df1de302bc38e99c3a9aa311967b7dc80ced1dcc6f171e99842987882a2", size = 272305, upload-time = "2025-08-07T13:15:41.288Z" }, { url = "https://files.pythonhosted.org/packages/09/16/2c3792cba130000bf2a31c5272999113f4764fd9d874fb257ff588ac779a/greenlet-3.2.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1ee8fae0519a337f2329cb78bd7a8e128ec0f881073d43f023c7b8d4831d5246", size = 632472, upload-time = "2025-08-07T13:42:55.044Z" }, { url = "https://files.pythonhosted.org/packages/ae/8f/95d48d7e3d433e6dae5b1682e4292242a53f22df82e6d3dda81b1701a960/greenlet-3.2.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:94abf90142c2a18151632371140b3dba4dee031633fe614cb592dbb6c9e17bc3", size = 644646, upload-time = "2025-08-07T13:45:26.523Z" }, + { url = "https://files.pythonhosted.org/packages/d5/5e/405965351aef8c76b8ef7ad370e5da58d57ef6068df197548b015464001a/greenlet-3.2.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:4d1378601b85e2e5171b99be8d2dc85f594c79967599328f95c1dc1a40f1c633", size = 640519, upload-time = "2025-08-07T13:53:13.928Z" }, { url = "https://files.pythonhosted.org/packages/25/5d/382753b52006ce0218297ec1b628e048c4e64b155379331f25a7316eb749/greenlet-3.2.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0db5594dce18db94f7d1650d7489909b57afde4c580806b8d9203b6e79cdc079", size = 639707, upload-time = "2025-08-07T13:18:27.146Z" }, { url = "https://files.pythonhosted.org/packages/1f/8e/abdd3f14d735b2929290a018ecf133c901be4874b858dd1c604b9319f064/greenlet-3.2.4-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2523e5246274f54fdadbce8494458a2ebdcdbc7b802318466ac5606d3cded1f8", size = 587684, upload-time = "2025-08-07T13:18:25.164Z" }, { url = "https://files.pythonhosted.org/packages/5d/65/deb2a69c3e5996439b0176f6651e0052542bb6c8f8ec2e3fba97c9768805/greenlet-3.2.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1987de92fec508535687fb807a5cea1560f6196285a4cde35c100b8cd632cc52", size = 1116647, upload-time = "2025-08-07T13:42:38.655Z" }, @@ -944,6 +949,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/44/69/9b804adb5fd0671f367781560eb5eb586c4d495277c93bde4307b9e28068/greenlet-3.2.4-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3b67ca49f54cede0186854a008109d6ee71f66bd57bb36abd6d0a0267b540cdd", size = 274079, upload-time = "2025-08-07T13:15:45.033Z" }, { url = "https://files.pythonhosted.org/packages/46/e9/d2a80c99f19a153eff70bc451ab78615583b8dac0754cfb942223d2c1a0d/greenlet-3.2.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ddf9164e7a5b08e9d22511526865780a576f19ddd00d62f8a665949327fde8bb", size = 640997, upload-time = "2025-08-07T13:42:56.234Z" }, { url = "https://files.pythonhosted.org/packages/3b/16/035dcfcc48715ccd345f3a93183267167cdd162ad123cd93067d86f27ce4/greenlet-3.2.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f28588772bb5fb869a8eb331374ec06f24a83a9c25bfa1f38b6993afe9c1e968", size = 655185, upload-time = "2025-08-07T13:45:27.624Z" }, + { url = "https://files.pythonhosted.org/packages/31/da/0386695eef69ffae1ad726881571dfe28b41970173947e7c558d9998de0f/greenlet-3.2.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:5c9320971821a7cb77cfab8d956fa8e39cd07ca44b6070db358ceb7f8797c8c9", size = 649926, upload-time = "2025-08-07T13:53:15.251Z" }, { url = "https://files.pythonhosted.org/packages/68/88/69bf19fd4dc19981928ceacbc5fd4bb6bc2215d53199e367832e98d1d8fe/greenlet-3.2.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c60a6d84229b271d44b70fb6e5fa23781abb5d742af7b808ae3f6efd7c9c60f6", size = 651839, upload-time = "2025-08-07T13:18:30.281Z" }, { url = "https://files.pythonhosted.org/packages/19/0d/6660d55f7373b2ff8152401a83e02084956da23ae58cddbfb0b330978fe9/greenlet-3.2.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b3812d8d0c9579967815af437d96623f45c0f2ae5f04e366de62a12d83a8fb0", size = 607586, upload-time = "2025-08-07T13:18:28.544Z" }, { url = "https://files.pythonhosted.org/packages/8e/1a/c953fdedd22d81ee4629afbb38d2f9d71e37d23caace44775a3a969147d4/greenlet-3.2.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:abbf57b5a870d30c4675928c37278493044d7c14378350b3aa5d484fa65575f0", size = 1123281, upload-time = "2025-08-07T13:42:39.858Z" }, @@ -954,6 +960,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/49/e8/58c7f85958bda41dafea50497cbd59738c5c43dbbea5ee83d651234398f4/greenlet-3.2.4-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:1a921e542453fe531144e91e1feedf12e07351b1cf6c9e8a3325ea600a715a31", size = 272814, upload-time = "2025-08-07T13:15:50.011Z" }, { url = "https://files.pythonhosted.org/packages/62/dd/b9f59862e9e257a16e4e610480cfffd29e3fae018a68c2332090b53aac3d/greenlet-3.2.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd3c8e693bff0fff6ba55f140bf390fa92c994083f838fece0f63be121334945", size = 641073, upload-time = "2025-08-07T13:42:57.23Z" }, { url = "https://files.pythonhosted.org/packages/f7/0b/bc13f787394920b23073ca3b6c4a7a21396301ed75a655bcb47196b50e6e/greenlet-3.2.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:710638eb93b1fa52823aa91bf75326f9ecdfd5e0466f00789246a5280f4ba0fc", size = 655191, upload-time = "2025-08-07T13:45:29.752Z" }, + { url = "https://files.pythonhosted.org/packages/f2/d6/6adde57d1345a8d0f14d31e4ab9c23cfe8e2cd39c3baf7674b4b0338d266/greenlet-3.2.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c5111ccdc9c88f423426df3fd1811bfc40ed66264d35aa373420a34377efc98a", size = 649516, upload-time = "2025-08-07T13:53:16.314Z" }, { url = "https://files.pythonhosted.org/packages/7f/3b/3a3328a788d4a473889a2d403199932be55b1b0060f4ddd96ee7cdfcad10/greenlet-3.2.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d76383238584e9711e20ebe14db6c88ddcedc1829a9ad31a584389463b5aa504", size = 652169, upload-time = "2025-08-07T13:18:32.861Z" }, { url = "https://files.pythonhosted.org/packages/ee/43/3cecdc0349359e1a527cbf2e3e28e5f8f06d3343aaf82ca13437a9aa290f/greenlet-3.2.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23768528f2911bcd7e475210822ffb5254ed10d71f4028387e5a99b4c6699671", size = 610497, upload-time = "2025-08-07T13:18:31.636Z" }, { url = "https://files.pythonhosted.org/packages/b8/19/06b6cf5d604e2c382a6f31cafafd6f33d5dea706f4db7bdab184bad2b21d/greenlet-3.2.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:00fadb3fedccc447f517ee0d3fd8fe49eae949e1cd0f6a611818f4f6fb7dc83b", size = 1121662, upload-time = "2025-08-07T13:42:41.117Z" }, @@ -964,6 +971,7 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/22/5c/85273fd7cc388285632b0498dbbab97596e04b154933dfe0f3e68156c68c/greenlet-3.2.4-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:49a30d5fda2507ae77be16479bdb62a660fa51b1eb4928b524975b3bde77b3c0", size = 273586, upload-time = "2025-08-07T13:16:08.004Z" }, { url = "https://files.pythonhosted.org/packages/d1/75/10aeeaa3da9332c2e761e4c50d4c3556c21113ee3f0afa2cf5769946f7a3/greenlet-3.2.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:299fd615cd8fc86267b47597123e3f43ad79c9d8a22bebdce535e53550763e2f", size = 686346, upload-time = "2025-08-07T13:42:59.944Z" }, { url = "https://files.pythonhosted.org/packages/c0/aa/687d6b12ffb505a4447567d1f3abea23bd20e73a5bed63871178e0831b7a/greenlet-3.2.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:c17b6b34111ea72fc5a4e4beec9711d2226285f0386ea83477cbb97c30a3f3a5", size = 699218, upload-time = "2025-08-07T13:45:30.969Z" }, + { url = "https://files.pythonhosted.org/packages/dc/8b/29aae55436521f1d6f8ff4e12fb676f3400de7fcf27fccd1d4d17fd8fecd/greenlet-3.2.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b4a1870c51720687af7fa3e7cda6d08d801dae660f75a76f3845b642b4da6ee1", size = 694659, upload-time = "2025-08-07T13:53:17.759Z" }, { url = "https://files.pythonhosted.org/packages/92/2e/ea25914b1ebfde93b6fc4ff46d6864564fba59024e928bdc7de475affc25/greenlet-3.2.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:061dc4cf2c34852b052a8620d40f36324554bc192be474b9e9770e8c042fd735", size = 695355, upload-time = "2025-08-07T13:18:34.517Z" }, { url = "https://files.pythonhosted.org/packages/72/60/fc56c62046ec17f6b0d3060564562c64c862948c9d4bc8aa807cf5bd74f4/greenlet-3.2.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44358b9bf66c8576a9f57a590d5f5d6e72fa4228b763d0e43fee6d3b06d3a337", size = 657512, upload-time = "2025-08-07T13:18:33.969Z" }, { url = "https://files.pythonhosted.org/packages/23/6e/74407aed965a4ab6ddd93a7ded3180b730d281c77b765788419484cdfeef/greenlet-3.2.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2917bdf657f5859fbf3386b12d68ede4cf1f04c90c3a6bc1f013dd68a22e2269", size = 1612508, upload-time = "2025-11-04T12:42:23.427Z" }, From 83838099206d7d69d88441efca2db9c1b7d01143 Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Fri, 24 Jul 2026 12:18:01 -0700 Subject: [PATCH 13/28] Added contributors full names --- docs/library/releases.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/library/releases.md b/docs/library/releases.md index 02487b523..a893baa3c 100644 --- a/docs/library/releases.md +++ b/docs/library/releases.md @@ -71,9 +71,11 @@ Release Date: July 29, 2026 * Pin to avoid pandas 3.0.4 and set a maximum sparse version by @henrydingliu in [#1072](https://github.com/casact/chainladder-python/pull/1072), [#957](https://github.com/casact/chainladder-python/pull/957) * Bump idna, tornado, bleach, soupsieve, mistune, and pillow by @dependabot[bot] in [#822](https://github.com/casact/chainladder-python/pull/822), [#962](https://github.com/casact/chainladder-python/pull/962), [#1001](https://github.com/casact/chainladder-python/pull/1001), [#1007](https://github.com/casact/chainladder-python/pull/1007), [#1107](https://github.com/casact/chainladder-python/pull/1107), [#1108](https://github.com/casact/chainladder-python/pull/1108), [#1141](https://github.com/casact/chainladder-python/pull/1141) -**New Contributors** -* @salexanian made their first contribution in [#793](https://github.com/casact/chainladder-python/pull/793) -* @etw002 made their first contribution in [#1009](https://github.com/casact/chainladder-python/pull/1009) +**Contributors** + +Welcome Shahen Alexanian (@salexanian) and Ethan Wang (@etw002) for making their first contributions! 🎉 + +Incorporated 520 commits from 8 collaborators, including returning contributors Gene Dan (@genedan), Kenneth Hsu (@kennethshsu), Henry Liu (@henrydingliu), Ethan Kang (@EKtheSage), Priyam Alok (@priyam0k), and Nick Kinney (@SaguaroDev). **Full Changelog**: https://github.com/casact/chainladder-python/compare/v0.9.2...v0.10.0 From 2f7a7029b787c196b4faeb657fae485c7cf0ecec Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Fri, 24 Jul 2026 12:20:53 -0700 Subject: [PATCH 14/28] Added contributor fullnames and missing tickets --- docs/library/releases.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/library/releases.md b/docs/library/releases.md index a893baa3c..912bb35b2 100644 --- a/docs/library/releases.md +++ b/docs/library/releases.md @@ -21,6 +21,7 @@ Release Date: July 29, 2026 * Add `empty` attribute to `Triangle` for pandas consistency by @priyam0k in [#901](https://github.com/casact/chainladder-python/pull/901) * Implement `cl.options.describe_option` by @genedan in [#877](https://github.com/casact/chainladder-python/pull/877) * Allow `DevelopmentConstant` to be callable on columns by @henrydingliu in [#796](https://github.com/casact/chainladder-python/pull/796) +* Allow different policy lengths in `ParallelogramOLF` by @kennethshsu in [#772](https://github.com/casact/chainladder-python/pull/772) * Return a Python `bool` from `is_ultimate` by @salexanian in [#793](https://github.com/casact/chainladder-python/pull/793) * Add the `clrd2025` dataset and centralize sample metadata in a manifest surfaced via `list_samples` by @SaguaroDev in [#771](https://github.com/casact/chainladder-python/pull/771), [#886](https://github.com/casact/chainladder-python/pull/886) * Add Friedland Exhibit III samples and earned premium by @salexanian in [#787](https://github.com/casact/chainladder-python/pull/787) @@ -54,6 +55,7 @@ Release Date: July 29, 2026 * Add doctest examples for `BootstrapODPSample` and the correlation classes by @priyam0k in [#836](https://github.com/casact/chainladder-python/pull/836), [#844](https://github.com/casact/chainladder-python/pull/844) * Improve `Development` and `DevelopmentConstant` docstrings and examples by @kennethshsu in [#834](https://github.com/casact/chainladder-python/pull/834), [#993](https://github.com/casact/chainladder-python/pull/993) * Clarify the `Triangle` constructor docstring and document `array_backend`/`pattern` by @SaguaroDev in [#812](https://github.com/casact/chainladder-python/pull/812), [#813](https://github.com/casact/chainladder-python/pull/813) +* Render API methods inline via custom autosummary templates by @priyam0k and @henrydingliu in [#885](https://github.com/casact/chainladder-python/pull/885), [#893](https://github.com/casact/chainladder-python/pull/893), [#900](https://github.com/casact/chainladder-python/pull/900) * Document creating triangles with preexisting ultimates by @EKtheSage in [#968](https://github.com/casact/chainladder-python/pull/968) * Add Semiannual (S) grain documentation by @SaguaroDev in [#814](https://github.com/casact/chainladder-python/pull/814) * Add `ARCHITECTURE.md` by @genedan in [#1070](https://github.com/casact/chainladder-python/pull/1070) @@ -75,7 +77,7 @@ Release Date: July 29, 2026 Welcome Shahen Alexanian (@salexanian) and Ethan Wang (@etw002) for making their first contributions! 🎉 -Incorporated 520 commits from 8 collaborators, including returning contributors Gene Dan (@genedan), Kenneth Hsu (@kennethshsu), Henry Liu (@henrydingliu), Ethan Kang (@EKtheSage), Priyam Alok (@priyam0k), and Nick Kinney (@SaguaroDev). +Incorporated 523 commits from 8 collaborators, including returning contributors Gene Dan (@genedan), Kenneth Hsu (@kennethshsu), Henry Liu (@henrydingliu), Ethan Kang (@EKtheSage), Priyam Alok (@priyam0k), and Nick Kinney (@SaguaroDev). **Full Changelog**: https://github.com/casact/chainladder-python/compare/v0.9.2...v0.10.0 From d539991a9d5babf18298dd440a88342dd751b596 Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Fri, 24 Jul 2026 14:04:53 -0700 Subject: [PATCH 15/28] Tighten sphinx-book-theme version --- pyproject.toml | 2 +- uv.lock | 79 +++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 66 insertions(+), 15 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 6316c8d2a..4a825471d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,7 +63,7 @@ docs = [ "nbsphinx", "numpydoc", "jupyter-book<2.0", # Pin to v1.x - v2 doesn't support autodoc/autosummary yet - "sphinx-book-theme>=1.1.4", + "sphinx-book-theme>=1.2,<1.3; python_version >= '3.11'", "sphinx-togglebutton", "ipython", "parso>=0.8", diff --git a/uv.lock b/uv.lock index 4ef87880b..55f17144c 100644 --- a/uv.lock +++ b/uv.lock @@ -221,7 +221,8 @@ all = [ { name = "pytest-cov" }, { name = "pytest-xdist" }, { name = "sphinx" }, - { name = "sphinx-book-theme" }, + { name = "sphinx-book-theme", version = "1.1.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "sphinx-book-theme", version = "1.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "sphinx-rtd-theme" }, { name = "sphinx-togglebutton" }, { name = "statsmodels" }, @@ -246,7 +247,8 @@ docs = [ { name = "parso" }, { name = "polars" }, { name = "sphinx" }, - { name = "sphinx-book-theme" }, + { name = "sphinx-book-theme", version = "1.1.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "sphinx-book-theme", version = "1.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "sphinx-rtd-theme" }, { name = "sphinx-togglebutton" }, { name = "statsmodels" }, @@ -301,7 +303,8 @@ requires-dist = [ { name = "scikit-learn", specifier = ">1.4.2" }, { name = "sparse", specifier = ">=0.9" }, { name = "sphinx", marker = "extra == 'docs'" }, - { name = "sphinx-book-theme", marker = "extra == 'docs'", specifier = ">=1.1.4,<1.2" }, + { name = "sphinx-book-theme", marker = "python_full_version >= '3.11' and extra == 'docs'", specifier = ">=1.2,<1.3" }, + { name = "sphinx-book-theme", marker = "python_full_version < '3.11' and extra == 'docs'", specifier = ">=1.1.4,<1.2" }, { name = "sphinx-rtd-theme", marker = "extra == 'docs'" }, { name = "sphinx-togglebutton", marker = "extra == 'docs'" }, { name = "statsmodels", marker = "extra == 'docs'", specifier = ">=0.14.6" }, @@ -1180,7 +1183,8 @@ dependencies = [ { name = "myst-parser" }, { name = "pyyaml" }, { name = "sphinx" }, - { name = "sphinx-book-theme" }, + { name = "sphinx-book-theme", version = "1.1.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "sphinx-book-theme", version = "1.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, { name = "sphinx-comments" }, { name = "sphinx-copybutton" }, { name = "sphinx-design" }, @@ -2447,21 +2451,47 @@ wheels = [ name = "pydata-sphinx-theme" version = "0.15.4" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] dependencies = [ - { name = "accessible-pygments" }, - { name = "babel" }, - { name = "beautifulsoup4" }, - { name = "docutils" }, - { name = "packaging" }, - { name = "pygments" }, - { name = "sphinx" }, - { name = "typing-extensions" }, + { name = "accessible-pygments", marker = "python_full_version < '3.11'" }, + { name = "babel", marker = "python_full_version < '3.11'" }, + { name = "beautifulsoup4", marker = "python_full_version < '3.11'" }, + { name = "docutils", marker = "python_full_version < '3.11'" }, + { name = "packaging", marker = "python_full_version < '3.11'" }, + { name = "pygments", marker = "python_full_version < '3.11'" }, + { name = "sphinx", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/67/ea/3ab478cccacc2e8ef69892c42c44ae547bae089f356c4b47caf61730958d/pydata_sphinx_theme-0.15.4.tar.gz", hash = "sha256:7762ec0ac59df3acecf49fd2f889e1b4565dbce8b88b2e29ee06fdd90645a06d", size = 2400673, upload-time = "2024-06-25T19:28:45.041Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/e7/d3/c622950d87a2ffd1654208733b5bd1c5645930014abed8f4c0d74863988b/pydata_sphinx_theme-0.15.4-py3-none-any.whl", hash = "sha256:2136ad0e9500d0949f96167e63f3e298620040aea8f9c74621959eda5d4cf8e6", size = 4640157, upload-time = "2024-06-25T19:28:42.383Z" }, ] +[[package]] +name = "pydata-sphinx-theme" +version = "0.16.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14'", + "python_full_version >= '3.12' and python_full_version < '3.14'", + "python_full_version == '3.11.*'", +] +dependencies = [ + { name = "accessible-pygments", marker = "python_full_version >= '3.11'" }, + { name = "babel", marker = "python_full_version >= '3.11'" }, + { name = "beautifulsoup4", marker = "python_full_version >= '3.11'" }, + { name = "docutils", marker = "python_full_version >= '3.11'" }, + { name = "pygments", marker = "python_full_version >= '3.11'" }, + { name = "sphinx", marker = "python_full_version >= '3.11'" }, + { name = "typing-extensions", marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/00/20/bb50f9de3a6de69e6abd6b087b52fa2418a0418b19597601605f855ad044/pydata_sphinx_theme-0.16.1.tar.gz", hash = "sha256:a08b7f0b7f70387219dc659bff0893a7554d5eb39b59d3b8ef37b8401b7642d7", size = 2412693, upload-time = "2024-12-17T10:53:39.537Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e2/0d/8ba33fa83a7dcde13eb3c1c2a0c1cc29950a048bfed6d9b0d8b6bd710b4c/pydata_sphinx_theme-0.16.1-py3-none-any.whl", hash = "sha256:225331e8ac4b32682c18fcac5a57a6f717c4e632cea5dd0e247b55155faeccde", size = 6723264, upload-time = "2024-12-17T10:53:35.645Z" }, +] + [[package]] name = "pygments" version = "2.20.0" @@ -3145,15 +3175,36 @@ wheels = [ name = "sphinx-book-theme" version = "1.1.4" source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] dependencies = [ - { name = "pydata-sphinx-theme" }, - { name = "sphinx" }, + { name = "pydata-sphinx-theme", version = "0.15.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "sphinx", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/45/19/d002ed96bdc7738c15847c730e1e88282d738263deac705d5713b4d8fa94/sphinx_book_theme-1.1.4.tar.gz", hash = "sha256:73efe28af871d0a89bd05856d300e61edce0d5b2fbb7984e84454be0fedfe9ed", size = 439188, upload-time = "2025-02-20T16:32:32.581Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/51/9e/c41d68be04eef5b6202b468e0f90faf0c469f3a03353f2a218fd78279710/sphinx_book_theme-1.1.4-py3-none-any.whl", hash = "sha256:843b3f5c8684640f4a2d01abd298beb66452d1b2394cd9ef5be5ebd5640ea0e1", size = 433952, upload-time = "2025-02-20T16:32:31.009Z" }, ] +[[package]] +name = "sphinx-book-theme" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14'", + "python_full_version >= '3.12' and python_full_version < '3.14'", + "python_full_version == '3.11.*'", +] +dependencies = [ + { name = "pydata-sphinx-theme", version = "0.16.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "sphinx", marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/f7/154786f3cfb7692cd7acc24b6dfe4dcd1146b66f376b17df9e47125555e9/sphinx_book_theme-1.2.0.tar.gz", hash = "sha256:4a7ebfc7da4395309ac942ddfc38fbec5c5254c3be22195e99ad12586fbda9e3", size = 443962, upload-time = "2026-03-09T23:20:30.442Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/bf/6f506a37c7f8ecc4576caf9486e303c7af249f6d70447bb51dde9d78cb99/sphinx_book_theme-1.2.0-py3-none-any.whl", hash = "sha256:709605d308e1991c5ef0cf19c481dbe9084b62852e317fafab74382a0ee7ccfa", size = 455936, upload-time = "2026-03-09T23:20:28.788Z" }, +] + [[package]] name = "sphinx-comments" version = "0.0.3" From 8b4e131d7cb98752589851d81523af2f41fcd215 Mon Sep 17 00:00:00 2001 From: Kenneth Hsu Date: Fri, 24 Jul 2026 14:32:49 -0700 Subject: [PATCH 16/28] bug bot fix --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 4a825471d..9ab928f5b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,6 +63,7 @@ docs = [ "nbsphinx", "numpydoc", "jupyter-book<2.0", # Pin to v1.x - v2 doesn't support autodoc/autosummary yet + "sphinx-book-theme>=1.1.4,<1.2; python_version < '3.11'", "sphinx-book-theme>=1.2,<1.3; python_version >= '3.11'", "sphinx-togglebutton", "ipython", From fcdfd1d4c86a8699bce0c25fed3385c4d6012113 Mon Sep 17 00:00:00 2001 From: Anonymous <38025250+salexanian@users.noreply.github.com> Date: Tue, 14 Jul 2026 15:09:37 -0400 Subject: [PATCH 17/28] Fix documentation build warnings and cross-references --- chainladder/core/triangle.py | 4 ++-- chainladder/workflow/gridsearch.py | 7 ++----- chainladder/workflow/voting.py | 2 +- docs/_config.yml | 5 +++++ docs/conf.py | 10 ++-------- docs/getting_started/install.md | 2 +- docs/library/glossary.md | 18 ++++++++++++------ docs/user_guide/development.ipynb | 16 ++++++++-------- docs/user_guide/index.md | 29 +++++++++++++++++------------ 9 files changed, 50 insertions(+), 43 deletions(-) diff --git a/chainladder/core/triangle.py b/chainladder/core/triangle.py index 82e41f65b..88e3df641 100644 --- a/chainladder/core/triangle.py +++ b/chainladder/core/triangle.py @@ -109,7 +109,7 @@ class Triangle(TriangleBase): link_ratio, age_to_age: Triangle Displays age-to-age ratios for the triangle. disposal_rate_tri: Triangle - Displays actual disposal rates by origin and development; must have ultimate_ + Displays actual disposal rates by origin and development; must have ``ultimate_`` valuation_date : date The latest valuation date of the data loc: Triangle @@ -1233,7 +1233,7 @@ def age_to_age(self): @property def disposal_rate_tri(self) -> Triangle: """ - Displays disposal rates for the triangle. Must have ultimate_ + Displays disposal rates for the triangle. Must have ``ultimate_`` Returns ------- diff --git a/chainladder/workflow/gridsearch.py b/chainladder/workflow/gridsearch.py index 91ce64861..cd55c7f18 100644 --- a/chainladder/workflow/gridsearch.py +++ b/chainladder/workflow/gridsearch.py @@ -43,10 +43,7 @@ class GridSearch(BaseEstimator): step, which will always raise the error. Default is 'raise' but from version 0.22 it will change to np.nan. n_jobs: int, default=None - The number of jobs to use for the computation. This will only provide - speedup for n_targets > 1 and sufficient large problems. - ``None`` means 1 unless in a :obj:`joblib.parallel_backend` context. - ``-1`` means using all processors. See :term:`Glossary ` + The number of jobs to use for the computation. See :term:`Glossary ` for more details. Attributes @@ -164,7 +161,7 @@ class Pipeline(PipelineSL, EstimatorIO): A step's estimator may be replaced entirely by setting the parameter with its name to another estimator, or a transformer removed by setting to None. - Read more in the :ref:`User Guide `. + Read more in the :ref:`User Guide `. Parameters ---------- diff --git a/chainladder/workflow/voting.py b/chainladder/workflow/voting.py index afafac635..bd0d8f870 100644 --- a/chainladder/workflow/voting.py +++ b/chainladder/workflow/voting.py @@ -175,7 +175,7 @@ class VotingChainladder(_BaseChainladderVoting, MethodBase): individual predictions based on a matrix of weights to form a final prediction. - Read more in the :ref:`User Guide `. + Read more in the :ref:`User Guide `. .. versionadded:: 0.8.0 diff --git a/docs/_config.yml b/docs/_config.yml index 4f9714815..42871edf7 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -37,6 +37,7 @@ sphinx: - sphinx.ext.autosummary - sphinx.ext.doctest - sphinx.ext.linkcode + - sphinx.ext.intersphinx - numpydoc - sphinx.ext.mathjax local_extensions: @@ -46,6 +47,10 @@ sphinx: templates_path: ['_templates'] numpydoc_show_class_members: False bibtex_reference_style: author_year + intersphinx_mapping: + sklearn: + - https://scikit-learn.org/stable/ + - null parse: myst_enable_extensions: diff --git a/docs/conf.py b/docs/conf.py index 071676888..407440506 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,10 +1,3 @@ -import sys -from pathlib import Path -_DOCS_EXT_DIR = Path(__file__).resolve().parent / '_ext' -_p = str(_DOCS_EXT_DIR) -if _p not in sys.path: - sys.path.insert(0, _p) - ############################################################################### # Auto-generated by `jupyter-book config` # If you wish to continue using _config.yml, make edits to that file and @@ -27,6 +20,7 @@ html_theme = 'sphinx_book_theme' html_theme_options = {'search_bar_text': 'Search this book...', 'launch_buttons': {'notebook_interface': 'classic', 'binderhub_url': '', 'jupyterhub_url': '', 'thebe': False, 'colab_url': '', 'deepnote_url': ''}, 'path_to_docs': 'docs', 'repository_url': 'https://github.com/casact/chainladder-python', 'repository_branch': 'master', 'extra_footer': '', 'home_page_in_toc': True, 'announcement': '', 'analytics': {'google_analytics_id': '', 'plausible_analytics_domain': '', 'plausible_analytics_url': 'https://plausible.io/js/script.js'}, 'use_repository_button': True, 'use_edit_page_button': False, 'use_issues_button': True} html_title = 'Chainladder - Python' +intersphinx_mapping = {'sklearn': ['https://scikit-learn.org/stable/', None]} latex_engine = 'pdflatex' myst_enable_extensions = ['html_admonition', 'colon_fence', 'dollarmath', 'linkify', 'substitution'] myst_url_schemes = ['mailto', 'http', 'https'] @@ -38,9 +32,9 @@ nb_execution_timeout = 30 nb_output_stderr = 'show' numfig = True +numpydoc_show_class_members = False pygments_style = 'sphinx' suppress_warnings = ['myst.domains'] templates_path = ['_templates'] -numpydoc_show_class_members = False use_jupyterbook_latex = True use_multitoc_numbering = True diff --git a/docs/getting_started/install.md b/docs/getting_started/install.md index b1374a2db..69819786c 100644 --- a/docs/getting_started/install.md +++ b/docs/getting_started/install.md @@ -24,5 +24,5 @@ install directly from `Github`: ## Developer Installation -If you're interested in contributing, please refer to [Contributing](contributing) +If you're interested in contributing, please refer to [Contributing](../library/contributing) for information on the developer environment. diff --git a/docs/library/glossary.md b/docs/library/glossary.md index 522af3108..cd9d42217 100644 --- a/docs/library/glossary.md +++ b/docs/library/glossary.md @@ -1,10 +1,10 @@ # Glossary This glossary hopes to definitively represent the tacit and explicit -conventions applied in `chanladder` and its API. +conventions applied in `chainladder` and its API. ## General Terms - +```{glossary} backend - The storage of the numerical representation of a [Triangle]{.title-ref}. It can be 'numpy' for a dense CPU bound @@ -17,6 +17,11 @@ estimator hyperparameter - An initial parameter of an estimator that can be set before the estimator is fit. +n_jobs + - The number of parallel jobs used by an estimator. A value of + `None` uses a single processor unless overridden by a joblib + parallel backend. A value of `-1` uses all available processors. + predictor - An estimator that has the `predict` method. All IBNR estimators of `chainladder` are predictors @@ -25,9 +30,9 @@ transformer - An estimator that has the `transform` method. The transform method returns instances of a Triangle. All estimators other than IBNR estimators are transformers. - +``` ## Class API - +```{glossary} Triangle - The core data structure of the `chainladder` package. It emulates `pandas`'s functionality. @@ -50,9 +55,9 @@ Workflow Adjustments - Estimators that allow for the adjustment of the values of a Triangle. - +``` ## Triangle Concepts - +```{glossary} axis - Represents one of the four dimensions of a [Triangle]{.title-ref} instance. The four axes are `index`, `columns`, `origin` and @@ -76,3 +81,4 @@ development valuation - An implicit axis representing the valuation period of each of the cells of a `Triangle`. +``` \ No newline at end of file diff --git a/docs/user_guide/development.ipynb b/docs/user_guide/development.ipynb index 2ef2aa88f..0471ee037 100644 --- a/docs/user_guide/development.ipynb +++ b/docs/user_guide/development.ipynb @@ -5706,13 +5706,13 @@ "adjust for case reserve adequacy. However it is different in two distinct ways.\n", "\n", " 1. The `BerquistSherman` method is a direct adjustment to the data whereas\n", - " the [MunichAdjustment](modules/generated/chainladder.MunichAdjustment) keeps the `Triangle` intact and adjusts the development\n", + " the [MunichAdjustment](../library/generated/chainladder.MunichAdjustment) keeps the `Triangle` intact and adjusts the development\n", " patterns.\n", - " 2. The [MunichAdjustment](modules/generated/chainladder.MunichAdjustment) is built in the context of a stochastic framework.\n", + " 2. The [MunichAdjustment](../library/generated/chainladder.MunichAdjustment) is built in the context of a stochastic framework.\n", "\n", "### Residuals\n", "\n", - "The [MunichAdjustment](modules/generated/chainladder.MunichAdjustment) uses the correlation between the residuals of the\n", + "The [MunichAdjustment](../library/generated/chainladder.MunichAdjustment) uses the correlation between the residuals of the\n", "univariate (basic) model and the (P/I) model. These correlations spin off a\n", "property ``lambda_`` which is represented by the line through the origin of\n", "the correlation plots.\n", @@ -5727,8 +5727,8 @@ "\n", ":::{grid-item-card}\n", ":columns: 4\n", - ":link: ../gallery/plot_munich_resid\n", - ":link-type: doc\n", + ":link: ../gallery/plot_munich_resid\n", + ":link-type: doc\n", "**[MunichAdjustment Residuals]**\n", "```{image} ../images/plot_munich_resid.png\n", "---\n", @@ -5741,8 +5741,8 @@ ":::\n", ":::{grid-item-card}\n", ":columns: 4\n", - ":link: ../gallery/plot_munich\n", - ":link-type: doc\n", + ":link: ../gallery/plot_munich\n", + ":link-type: doc\n", "**[MunichAdjustment Basics]**\n", "```{image} ../images/plot_munich.png\n", "---\n", @@ -6086,7 +6086,7 @@ "in this is also patterns for the carried case reserves based on the prior lag\n", "carried case reserve.\n", "\n", - "Like the [MunichAdjustment](modules/generated/chainladder.MunichAdjustment) and `BerquistSherman`, this estimator\n", + "Like the [MunichAdjustment](../library/generated/chainladder.MunichAdjustment) and [BerquistSherman](../library/generated/chainladder.BerquistSherman), this estimator\n", "is useful when you want to incorporate information about case reserves into paid\n", "ultimates.\n", "\n", diff --git a/docs/user_guide/index.md b/docs/user_guide/index.md index 66ec196c7..6c6db7615 100644 --- a/docs/user_guide/index.md +++ b/docs/user_guide/index.md @@ -8,7 +8,7 @@ Here are a few examples that we have came up with, hopefully the code is simple ````{grid-item-card} :columns: 6 -**[Triangles](triangle)** +**{doc}`Triangles `** ^^^ Data object to manage and manipulate reserving data @@ -18,13 +18,13 @@ Data object to manage and manipulate reserving data ```{image} ../images/plot_triangle_from_pandas.png ``` +++ -**Classes**: **[Triangle](triangle)**, ... +**Classes**: **{py:class}`chainladder.Triangle`**, ... ```` ````{grid-item-card} :columns: 6 -**[Development](development)** +**{doc}`Development `** ^^^ Tooling to generate loss development patterns @@ -34,14 +34,15 @@ Tooling to generate loss development patterns ``` +++ -**Algorithms**: [Development](development:development), [ClarkLDF](development:clarkldf), … +**Algorithms**: {py:class}`chainladder.Development`, +{py:class}`chainladder.ClarkLDF`, … ```` ````{grid-item-card} :columns: 6 -**[Tail Estimation](tails)** +**{doc}`Tail Estimation `** ^^^ Extrapolate development patterns beyond the known data. @@ -51,13 +52,14 @@ Extrapolate development patterns beyond the known data. ``` +++ -**Algorithms**: [TailCurve](tails:tailcurve), [TailConstant](tails:tailconstant), … +**Algorithms**: {py:class}`chainladder.TailCurve`, +{py:class}`chainladder.TailConstant`, … ```` ````{grid-item-card} :columns: 6 -**[IBNR Models](methods)** +**{doc}`IBNR Models `** ^^^ Generate IBNR estimates and associated statistics @@ -69,13 +71,14 @@ Generate IBNR estimates and associated statistics ``` +++ -**Algorithms**: [Chainladder](methods:chainladder), [CapeCod](methods:capecod), … +**Algorithms**: {py:class}`chainladder.Chainladder`, +{py:class}`chainladder.CapeCod`, … ```` ````{grid-item-card} :columns: 6 -**[Adjustments](adjustments)** +**{doc}`Adjustments `** ^^^ Common actuarial data adjustments @@ -87,13 +90,14 @@ Common actuarial data adjustments ``` +++ -**Classes**: [BootstrapODPSample](adjustments:bootstrapodpsample), [Trend](adjustments:trend), … +**Classes**: {py:class}`chainladder.BootstrapODPSample`, +{py:class}`chainladder.Trend`, … ```` ````{grid-item-card} :columns: 6 -**[Workflow](workflow)** +**{doc}`Workflow `** ^^^ Workflow tools for complex analyses @@ -104,7 +108,8 @@ Workflow tools for complex analyses ``` +++ -**Utilities**: [Pipeline](workflow:pipeline), [VotingChainladder](workflow:votingchainladder), … +**Utilities**: {py:class}`chainladder.Pipeline`, +{py:class}`chainladder.VotingChainladder`, … ```` ````` From 6816c743f7717543b2557268ea3c5720a2cec066 Mon Sep 17 00:00:00 2001 From: Anonymous <38025250+salexanian@users.noreply.github.com> Date: Wed, 15 Jul 2026 06:46:06 -0400 Subject: [PATCH 18/28] Restore local Sphinx extension path --- docs/conf.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index 407440506..addaeed64 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,3 +1,10 @@ +import sys +from pathlib import Path +_DOCS_EXT_DIR = Path(__file__).resolve().parent / '_ext' +_p = str(_DOCS_EXT_DIR) +if _p not in sys.path: + sys.path.insert(0, _p) + ############################################################################### # Auto-generated by `jupyter-book config` # If you wish to continue using _config.yml, make edits to that file and From bb8c1e9ddc7f973bebb938040e7a32e2f18fe375 Mon Sep 17 00:00:00 2001 From: Anonymous <38025250+salexanian@users.noreply.github.com> Date: Sun, 26 Jul 2026 12:02:59 -0400 Subject: [PATCH 19/28] Fix Development reference in user guide --- docs/user_guide/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user_guide/index.md b/docs/user_guide/index.md index 6c6db7615..7787abb0a 100644 --- a/docs/user_guide/index.md +++ b/docs/user_guide/index.md @@ -34,7 +34,7 @@ Tooling to generate loss development patterns ``` +++ -**Algorithms**: {py:class}`chainladder.Development`, +**Algorithms**: {ref}`Development `, {py:class}`chainladder.ClarkLDF`, … ```` From bb186ec65df2a00c0a3abff632ea998214ea5602 Mon Sep 17 00:00:00 2001 From: Anonymous <38025250+salexanian@users.noreply.github.com> Date: Sun, 26 Jul 2026 13:19:26 -0400 Subject: [PATCH 20/28] Restore user guide section links. --- docs/user_guide/index.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/user_guide/index.md b/docs/user_guide/index.md index 7787abb0a..fa2e47f70 100644 --- a/docs/user_guide/index.md +++ b/docs/user_guide/index.md @@ -35,7 +35,7 @@ Tooling to generate loss development patterns +++ **Algorithms**: {ref}`Development `, -{py:class}`chainladder.ClarkLDF`, … +{ref}`ClarkLDF `, … ```` @@ -52,8 +52,8 @@ Extrapolate development patterns beyond the known data. ``` +++ -**Algorithms**: {py:class}`chainladder.TailCurve`, -{py:class}`chainladder.TailConstant`, … +**Algorithms**: {ref}`TailCurve `, +{ref}`TailConstant `, … ```` ````{grid-item-card} @@ -71,8 +71,8 @@ Generate IBNR estimates and associated statistics ``` +++ -**Algorithms**: {py:class}`chainladder.Chainladder`, -{py:class}`chainladder.CapeCod`, … +**Algorithms**: {ref}`Chainladder `, +{ref}`CapeCod `, … ```` ````{grid-item-card} @@ -90,8 +90,8 @@ Common actuarial data adjustments ``` +++ -**Classes**: {py:class}`chainladder.BootstrapODPSample`, -{py:class}`chainladder.Trend`, … +**Classes**: {ref}`BootstrapODPSample `, +{ref}`Trend `, … ```` ````{grid-item-card} @@ -108,8 +108,8 @@ Workflow tools for complex analyses ``` +++ -**Utilities**: {py:class}`chainladder.Pipeline`, -{py:class}`chainladder.VotingChainladder`, … +**Utilities**: {ref}`Pipeline `, +{ref}`VotingChainladder `, … ```` ````` From 559bbbdabf9d2d052203317d851cda813b03ea6c Mon Sep 17 00:00:00 2001 From: priyam0k <87162535+priyam0k@users.noreply.github.com> Date: Sat, 25 Jul 2026 13:58:08 +0530 Subject: [PATCH 21/28] Fix Triangle display crash when stored in a DataFrame cell (GH #142) A Triangle is a 4-D container but pandas' is_sequence() misclassified it as a 1-D sequence because the legacy __getitem__ iteration protocol plus __len__ made iter() succeed. Formatting a DataFrame that holds a Triangle in a cell then iterated the Triangle and raised, crashing both the text and HTML reprs. Declare Triangle non-iterable via __iter__ = None so pandas falls back to str(triangle) and renders the summary. Add regression tests. --- chainladder/core/dunders.py | 10 ++++++ chainladder/core/tests/test_display.py | 44 ++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/chainladder/core/dunders.py b/chainladder/core/dunders.py index 2bfe4e6c5..0fe9ac87b 100644 --- a/chainladder/core/dunders.py +++ b/chainladder/core/dunders.py @@ -290,6 +290,16 @@ def __rsub__(self, other): def __len__(self): return self.shape[0] + # A Triangle is a 4-D container, not a 1-D sequence. Without this, Python + # falls back to the legacy iteration protocol (repeatedly calling + # __getitem__(0), __getitem__(1), ...), which treats the integer as a + # column label and raises. That also makes libraries such as pandas + # misclassify a Triangle as a sequence (see is_sequence) and attempt to + # iterate it when formatting a Triangle stored in a DataFrame cell, + # crashing the display. Declaring the type non-iterable makes pandas fall + # back to str(triangle) and render the summary instead (GH #142). + __iter__ = None + def __neg__(self): obj = self.copy() obj.values = -obj.values diff --git a/chainladder/core/tests/test_display.py b/chainladder/core/tests/test_display.py index b8ebc225a..f54db90a7 100644 --- a/chainladder/core/tests/test_display.py +++ b/chainladder/core/tests/test_display.py @@ -8,6 +8,7 @@ import sys +from collections.abc import Iterable from chainladder.core.display import TriangleDisplay from lxml import etree, html as lxml_html from unittest import mock @@ -259,6 +260,49 @@ def test_repr_format_semi_annual(prism: Triangle) -> None: assert any("H1" in str(i) or "H2" in str(i) for i in df.index) +def test_triangle_not_iterable(raa: Triangle) -> None: + """ + A Triangle is a 4-D container, not a 1-D sequence, so it must not be + iterable (GH #142). This prevents pandas from misclassifying it as a + sequence and iterating it while formatting a DataFrame cell. + + Parameters + ---------- + raa: Triangle + The raa sample data set. + + Returns + ------- + None + + """ + assert not isinstance(raa, Iterable) + with pytest.raises(TypeError): + iter(raa) + + +def test_triangle_in_dataframe_cell_display(clrd: Triangle) -> None: + """ + Storing a Triangle in a DataFrame cell and displaying the DataFrame must + not crash (GH #142). + + Parameters + ---------- + clrd: Triangle + The clrd sample data set. + + Returns + ------- + None + + """ + df = pd.DataFrame(data=[["clrd", clrd]], columns=["name", "cl_triangle"]) + # Both the text and HTML representations previously raised because pandas + # iterated the Triangle stored in the cell. + assert isinstance(repr(df), str) + assert isinstance(df._repr_html_(), str) + + def test_heatmap_multi_raises(clrd: Triangle) -> None: """ Heatmap only works on a single-dimension triangle. Raise a ValueError if multidimensional. From 5fe779baabcd31e6f34df9d4c4f8ffa2430859ba Mon Sep 17 00:00:00 2001 From: priyam0k <87162535+priyam0k@users.noreply.github.com> Date: Sun, 26 Jul 2026 00:00:09 +0530 Subject: [PATCH 22/28] Remove dead backend-reset code in _prep_columns (#1045) --- chainladder/core/dunders.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/chainladder/core/dunders.py b/chainladder/core/dunders.py index 0fe9ac87b..fc899a8d9 100644 --- a/chainladder/core/dunders.py +++ b/chainladder/core/dunders.py @@ -110,8 +110,6 @@ def _prep_index(self, x, y): raise ValueError('Index broadcasting is ambiguous between ' + str(x_labels) + ' and ' + str(y_labels)) def _prep_columns(self, x, y): - x_backend, y_backend = x.array_backend, y.array_backend - if len(x.columns) == 1 and len(y.columns) > 1: x.vdims = y.vdims elif len(y.columns) == 1 and len(x.columns) > 1: @@ -140,13 +138,7 @@ def _prep_columns(self, x, y): # Ensure both triangles have the same column order x = x[new_x_cols] y = y[new_x_cols] - - # Reset backends only if they've changed - if x.array_backend != x_backend: - x = x.set_backend(x_backend, inplace=True) - if y.array_backend != y_backend: - y = y.set_backend(y_backend, inplace=True) - + return x, y def _prep_origin_development(self, obj, other): From cef932cd5cd59d4dc4dd34f0e46b18210f7ee74c Mon Sep 17 00:00:00 2001 From: Gene Dan Date: Tue, 28 Jul 2026 21:10:57 -0500 Subject: [PATCH 23/28] FEAT: Explicitly forbid in-place assignment on sparse array. --- chainladder/utils/sparse.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/chainladder/utils/sparse.py b/chainladder/utils/sparse.py index e3dee3063..50bd337a2 100644 --- a/chainladder/utils/sparse.py +++ b/chainladder/utils/sparse.py @@ -6,8 +6,18 @@ from sparse import COO as COO from sparse import elemwise +def _setitem_not_supported(self, key, value) -> None: # noqa + raise TypeError( + """ + In-place item assignment (e.g. `triangle.values[...] = value`) is not + supported on the sparse backend. Use numpy backend for in-place assignment. + """ + ) + + sp.isnan = np.isnan COO.nan = np.array([1.0, np.nan])[-1] +COO.__setitem__ = _setitem_not_supported setattr(sp, 'testing', np.testing) sp.sqrt = np.sqrt sp.log = np.log From 23e48ab89a9c54cc50e4a67493c9faafe1a23b6b Mon Sep 17 00:00:00 2001 From: Gene Dan Date: Tue, 28 Jul 2026 21:11:22 -0500 Subject: [PATCH 24/28] FIX: Fix bug in column assignment with a sparse backend. --- chainladder/core/slice.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/chainladder/core/slice.py b/chainladder/core/slice.py index 96a477995..4ef69c0f0 100644 --- a/chainladder/core/slice.py +++ b/chainladder/core/slice.py @@ -541,20 +541,24 @@ def __setitem__( i = np.where(self.vdims == key)[0][0] # Case sparse backend. if self.array_backend == "sparse": - # Cast value to sparse backend. - value = cast("Triangle", value) - after = cast("COO", value.values) + # Unwrap a Triangle-valued assignment to its raw array. A raw + # COO array can also be passed directly, mirroring the numpy + # branch below. + if isinstance(value, TriangleSlicer): + value = cast("Triangle", value) + after = cast("COO", value.values) + else: + after = cast("COO", value) - # Drop existing data where key matches, reassign coordinates. - before = self.drop(key).values - before = cast("COO", before) - bc = before.coords[1, :] - before.coords[1] = np.where(bc >= i, bc + 1, bc,) + # Filter out existing data at the target column directly from + # self.values' own coordinates. + before = cast("COO", self.values) + keep = before.coords[1, :] != i # Append assigned data and new coordinates. after.coords[1] = i - coords = np.concatenate((before.coords, after.coords), axis=1) - data = np.concatenate((before.data, after.data)) + coords = np.concatenate((before.coords[:, keep], after.coords), axis=1) + data = np.concatenate((before.data[keep], after.data)) # Create new sparse matrix with updated coords and data, assign to backend array. self.values = xp.COO( @@ -577,7 +581,7 @@ def __setitem__( value = self.iloc[:, 0] * 0 + value try: self.values = xp.concatenate((self.values, value.values), axis=1) - except (ValueError, AttributeError): + except (ValueError, AttributeError, AssertionError): # For misaligned triangle support. conc = (self.values, (self.iloc[:, 0] * 0 + cast("Triangle", value)).values) self.values = xp.concatenate(conc, axis=1) From 3837ec7de3a67f440a7c414d04e8101eedacd98d Mon Sep 17 00:00:00 2001 From: Gene Dan Date: Tue, 28 Jul 2026 21:11:47 -0500 Subject: [PATCH 25/28] FEAT: Enable Triangle __array__ for sparse backend. --- chainladder/core/base.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/chainladder/core/base.py b/chainladder/core/base.py index 248245771..402d2256d 100644 --- a/chainladder/core/base.py +++ b/chainladder/core/base.py @@ -608,6 +608,8 @@ def subtriangles(self): return [k for k, v in vars(self).items() if isinstance(v, TriangleBase)] def __array__(self): + if self.array_backend == "sparse": + return self.values.todense() return self.values def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): From 5cb7d16bdf161213ed3d499495beb83c0ceedfca Mon Sep 17 00:00:00 2001 From: Gene Dan Date: Tue, 28 Jul 2026 21:11:55 -0500 Subject: [PATCH 26/28] TEST: Fix tests. --- chainladder/core/tests/test_slicing.py | 27 ++++++++++++++++++------- chainladder/core/tests/test_triangle.py | 22 +++++++++++++++++--- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/chainladder/core/tests/test_slicing.py b/chainladder/core/tests/test_slicing.py index c4c2f5efb..aa3caa810 100644 --- a/chainladder/core/tests/test_slicing.py +++ b/chainladder/core/tests/test_slicing.py @@ -116,7 +116,19 @@ def test_loc_ellipsis(clrd): def test_missing_first_lag(raa): x = raa.copy() - x.values[:, :, :, 0] = 0 + + def set_missing(values) -> None: + """ + Sets the missing values for the first lag. + """ + values[:, :, :, 0] = 0 + + if x.array_backend == "sparse": + # Sparse COO arrays don't support in-place item assignment. + with pytest.raises(TypeError, match="sparse backend"): + set_missing(x.values) + return + set_missing(x.values) x = x.sum(0) assert x.link_ratio.shape == (1, 1, 9, 9) @@ -434,7 +446,8 @@ def test_setitem_virtual_column_numpy_backend(raa: Triangle) -> None: None """ tri = raa.copy() - assert tri.array_backend == "numpy" + if tri.array_backend == "sparse": + pytest.skip("Test is specific to the numpy backend.") tri["double"] = lambda x: x["values"] * 2 assert "double" in tri.columns assert tri["double"] == tri["values"] * 2 @@ -454,7 +467,8 @@ def test_setitem_value_backend_conversion(raa: Triangle) -> None: None """ tri = raa.copy() - value = (tri["values"] * 2).set_backend("sparse") + other_backend = "numpy" if tri.array_backend == "sparse" else "sparse" + value = (tri["values"] * 2).set_backend(other_backend) assert tri.array_backend != value.array_backend tri["values"] = value assert tri.array_backend == raa.array_backend @@ -475,7 +489,6 @@ def test_setitem_existing_column_triangle_value(raa: Triangle) -> None: None """ tri = raa.copy() - assert tri.array_backend != "sparse" value = tri["values"] * 2 tri["values"] = value assert tri["values"] == raa["values"] * 2 @@ -483,7 +496,7 @@ def test_setitem_existing_column_triangle_value(raa: Triangle) -> None: def test_setitem_existing_column_array_value(raa: Triangle) -> None: """ - Reassign an existing column to a raw array value on a non-sparse backend. + Reassign an existing column to a raw array value. Parameters ---------- @@ -495,7 +508,6 @@ def test_setitem_existing_column_array_value(raa: Triangle) -> None: None """ tri = raa.copy() - assert tri.array_backend != "sparse" value = (tri["values"] * 3).values assert not isinstance(value, type(tri)) tri["values"] = value @@ -534,7 +546,8 @@ def test_setitem_new_column_misaligned_triangle(raa: Triangle) -> None: tri["misaligned"] = misaligned # Check the shape, new column should be added. assert tri.shape == (1, 2, 10, 10) - new_col = tri["misaligned"] + new_col = tri["misaligned"].set_backend("numpy") + misaligned = misaligned.set_backend("numpy") # Origin periods 1985 and prior should be nan. assert np.isnan(new_col.values[0, 0, :5, :]).all() # Origin periods 1986 and beyond should match. diff --git a/chainladder/core/tests/test_triangle.py b/chainladder/core/tests/test_triangle.py index 2a7ceb5f5..d51adbf9b 100644 --- a/chainladder/core/tests/test_triangle.py +++ b/chainladder/core/tests/test_triangle.py @@ -303,7 +303,19 @@ def test_dropna_latest_diagonal(raa: Triangle) -> None: None """ t = raa.copy() - t.values[:, :, 0, :] = np.nan + + def set_first_origin_nan(values) -> None: + """ + Sets the values for the first origin period to nan. + """ + values[:, :, 0, :] = np.nan + + if t.array_backend == "sparse": + # Sparse COO arrays don't support in-place item assignment. + with pytest.raises(TypeError, match="sparse backend"): + set_first_origin_nan(t.values) + return + set_first_origin_nan(t.values) result = t.latest_diagonal.dropna() assert result.shape == (1, 1, 9, 1) assert result.origin.min().year == 1982 @@ -575,8 +587,12 @@ def test_array_dunder(raa: Triangle) -> None: """ arr = np.asarray(raa) - assert arr is raa.values - np.testing.assert_array_equal(np.array(raa), raa.values) + if raa.array_backend == "numpy": + # No conversion needed, so __array__ returns the same object. + assert arr is raa.values + else: + np.testing.assert_array_equal(arr, raa.values.todense()) + np.testing.assert_array_equal(np.array(raa), raa.set_backend("numpy").values) def test_triangle_from_dataframe_interchange_protocol() -> None: From 996b2f9620f2378e9d894ec1a7c0f41a8e2e3ae8 Mon Sep 17 00:00:00 2001 From: Gene Dan Date: Tue, 28 Jul 2026 21:42:37 -0500 Subject: [PATCH 27/28] FIX: Apply bugbot fix. --- chainladder/core/slice.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/chainladder/core/slice.py b/chainladder/core/slice.py index 4ef69c0f0..061720902 100644 --- a/chainladder/core/slice.py +++ b/chainladder/core/slice.py @@ -556,8 +556,9 @@ def __setitem__( keep = before.coords[1, :] != i # Append assigned data and new coordinates. - after.coords[1] = i - coords = np.concatenate((before.coords[:, keep], after.coords), axis=1) + after_coords = after.coords.copy() + after_coords[1] = i + coords = np.concatenate((before.coords[:, keep], after_coords), axis=1) data = np.concatenate((before.data[keep], after.data)) # Create new sparse matrix with updated coords and data, assign to backend array. From 254ca053082544f5aefc773082bd06f7b69b359d Mon Sep 17 00:00:00 2001 From: Gene Dan Date: Tue, 28 Jul 2026 21:42:57 -0500 Subject: [PATCH 28/28] TST: Add concurrency guard. --- .github/workflows/pytest.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index 392cdb6fb..17b398fe8 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -8,6 +8,10 @@ on: pull_request: +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} + cancel-in-progress: true + jobs: linux: name: (${{ matrix.python-version }}, ${{ matrix.os }})