From d498a5e63be33d4c6c3731bd661004cb911df53a Mon Sep 17 00:00:00 2001 From: "henrydingliu@gmail.com" Date: Thu, 10 Sep 2026 23:20:26 +0000 Subject: [PATCH 01/13] guarding for missing X or y in OLS_fit --- chainladder/utils/tests/test_wtd_reg.py | 32 ++++++++++++++++++++++++ chainladder/utils/weighted_regression.py | 2 ++ 2 files changed, 34 insertions(+) create mode 100644 chainladder/utils/tests/test_wtd_reg.py diff --git a/chainladder/utils/tests/test_wtd_reg.py b/chainladder/utils/tests/test_wtd_reg.py new file mode 100644 index 00000000..dabf5cf5 --- /dev/null +++ b/chainladder/utils/tests/test_wtd_reg.py @@ -0,0 +1,32 @@ +from __future__ import annotations + +import numpy as np + +from chainladder.utils.weighted_regression import WeightedRegression + +class TestOLS: + """Test the OLS calculations""" + + def test_missing_data(self) -> None: + """Check that having nan in X and/or y still results in the right OLS coefficients.""" + X_full = np.array([[[[1.], [2.], [3.], [4.], [5.]]]]) + y_full = np.array([[[[1.], [2.], [3.], [4.], [5.]]]]) + X_missing = np.array([[[[1.], [np.nan], [3.], [4.], [5.]]]]) + y_missing = np.array([[[[1.], [2.], [np.nan], [4.], [5.]]]]) + w = np.array([[[[1.], [1.], [1.], [1.], [1.]]]]) + assert np.all( + WeightedRegression().fit(X_full, y_full, w, "regression").slope_.flatten() + == [1.] + ) + assert np.all( + WeightedRegression().fit(X_full, y_missing, w, "regression").slope_.flatten() + == [1.] + ) + assert np.all( + WeightedRegression().fit(X_missing, y_full, w, "regression").slope_.flatten() + == [1.] + ) + assert np.all( + WeightedRegression().fit(X_missing, y_missing, w, "regression").slope_.flatten() + == [1.] + ) diff --git a/chainladder/utils/weighted_regression.py b/chainladder/utils/weighted_regression.py index 6973e337..85d098ab 100644 --- a/chainladder/utils/weighted_regression.py +++ b/chainladder/utils/weighted_regression.py @@ -191,6 +191,8 @@ def _fit_ols(self): from chainladder.utils.utility_functions import num_to_nan w, x, y, axis = self.w.copy(), self.x.copy(), self.y.copy(), self.axis + w[np.isnan(x)] = 0 + w[np.isnan(y)] = 0 xp = self.xp if xp != sp: x[w == 0] = xp.nan From 770e6881ea8bd4cf7eddca06f3b09e61c37697aa Mon Sep 17 00:00:00 2001 From: "henrydingliu@gmail.com" Date: Fri, 11 Sep 2026 00:21:13 +0000 Subject: [PATCH 02/13] adding test for irregular full triangle --- chainladder/utils/tests/test_tri_w.py | 32 +++++++++++++++++++++++ chainladder/utils/tests/test_utilities.py | 12 +-------- 2 files changed, 33 insertions(+), 11 deletions(-) create mode 100644 chainladder/utils/tests/test_tri_w.py diff --git a/chainladder/utils/tests/test_tri_w.py b/chainladder/utils/tests/test_tri_w.py new file mode 100644 index 00000000..365723a6 --- /dev/null +++ b/chainladder/utils/tests/test_tri_w.py @@ -0,0 +1,32 @@ +from __future__ import annotations + +import chainladder as cl + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from chainladder import Triangle + + +class TestFullTri: + """Test weight generation on full triangles""" + + def test_triangleweight_full_triangle(self, raa: Triangle) -> None: + """ + Testing new path that allows weights on full triangles + """ + ult = cl.Chainladder().fit(raa) + tw = cl.TriangleWeight(n_periods=4).fit(raa) + tw_full = cl.TriangleWeight(n_periods=4).fit(ult.full_triangle_) + assert tw.w_.iloc[:, :, :, 0] == tw_full.w_.iloc[:, :, :, 0] + + + def test_triangleweight_full_irregular_triangle(self) -> None: + """ + Testing unequal grains + """ + prism = cl.load_sample("prism_oydq")["Paid"] + ult = cl.Chainladder().fit(prism) + tw = cl.TriangleWeight(n_periods=4).fit(prism) + tw_full = cl.TriangleWeight(n_periods=4).fit(ult.full_triangle_) + assert tw.w_.iloc[:, :, :, 0] == tw_full.w_.iloc[:, :, :, 0] \ No newline at end of file diff --git a/chainladder/utils/tests/test_utilities.py b/chainladder/utils/tests/test_utilities.py index 34171be9..a35e1084 100644 --- a/chainladder/utils/tests/test_utilities.py +++ b/chainladder/utils/tests/test_utilities.py @@ -1295,14 +1295,4 @@ def test_triangleweight_drop_valuation_all(raa: Triangle) -> None: "1989", "1990", ] - ).fit(raa) - - -def test_triangleweight_full_triangle(raa: Triangle) -> None: - """ - Testing new path that allows weights on full triangles - """ - ult = cl.Chainladder().fit(raa) - tw = cl.TriangleWeight(n_periods=4).fit(raa) - tw_full = cl.TriangleWeight(n_periods=4).fit(ult.full_triangle_) - assert tw.w_.iloc[:, :, :, 0] == tw_full.w_.iloc[:, :, :, 0] + ).fit(raa) \ No newline at end of file From 1156a13f0550c23ac76e7cbb18b27cbc7313065e Mon Sep 17 00:00:00 2001 From: "henrydingliu@gmail.com" Date: Fri, 11 Sep 2026 00:27:40 +0000 Subject: [PATCH 03/13] ruff fixes --- chainladder/utils/tests/test_tri_w.py | 3 +- chainladder/utils/tests/test_utilities.py | 2 +- chainladder/utils/tests/test_wtd_reg.py | 35 ++++++++++++++--------- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/chainladder/utils/tests/test_tri_w.py b/chainladder/utils/tests/test_tri_w.py index 365723a6..8f075b41 100644 --- a/chainladder/utils/tests/test_tri_w.py +++ b/chainladder/utils/tests/test_tri_w.py @@ -20,7 +20,6 @@ def test_triangleweight_full_triangle(self, raa: Triangle) -> None: tw_full = cl.TriangleWeight(n_periods=4).fit(ult.full_triangle_) assert tw.w_.iloc[:, :, :, 0] == tw_full.w_.iloc[:, :, :, 0] - def test_triangleweight_full_irregular_triangle(self) -> None: """ Testing unequal grains @@ -29,4 +28,4 @@ def test_triangleweight_full_irregular_triangle(self) -> None: ult = cl.Chainladder().fit(prism) tw = cl.TriangleWeight(n_periods=4).fit(prism) tw_full = cl.TriangleWeight(n_periods=4).fit(ult.full_triangle_) - assert tw.w_.iloc[:, :, :, 0] == tw_full.w_.iloc[:, :, :, 0] \ No newline at end of file + assert tw.w_.iloc[:, :, :, 0] == tw_full.w_.iloc[:, :, :, 0] diff --git a/chainladder/utils/tests/test_utilities.py b/chainladder/utils/tests/test_utilities.py index a35e1084..fe9410b5 100644 --- a/chainladder/utils/tests/test_utilities.py +++ b/chainladder/utils/tests/test_utilities.py @@ -1295,4 +1295,4 @@ def test_triangleweight_drop_valuation_all(raa: Triangle) -> None: "1989", "1990", ] - ).fit(raa) \ No newline at end of file + ).fit(raa) diff --git a/chainladder/utils/tests/test_wtd_reg.py b/chainladder/utils/tests/test_wtd_reg.py index dabf5cf5..d5502db1 100644 --- a/chainladder/utils/tests/test_wtd_reg.py +++ b/chainladder/utils/tests/test_wtd_reg.py @@ -4,29 +4,38 @@ from chainladder.utils.weighted_regression import WeightedRegression + class TestOLS: """Test the OLS calculations""" def test_missing_data(self) -> None: """Check that having nan in X and/or y still results in the right OLS coefficients.""" - X_full = np.array([[[[1.], [2.], [3.], [4.], [5.]]]]) - y_full = np.array([[[[1.], [2.], [3.], [4.], [5.]]]]) - X_missing = np.array([[[[1.], [np.nan], [3.], [4.], [5.]]]]) - y_missing = np.array([[[[1.], [2.], [np.nan], [4.], [5.]]]]) - w = np.array([[[[1.], [1.], [1.], [1.], [1.]]]]) + X_full = np.array([[[[1.0], [2.0], [3.0], [4.0], [5.0]]]]) + y_full = np.array([[[[1.0], [2.0], [3.0], [4.0], [5.0]]]]) + X_missing = np.array([[[[1.0], [np.nan], [3.0], [4.0], [5.0]]]]) + y_missing = np.array([[[[1.0], [2.0], [np.nan], [4.0], [5.0]]]]) + w = np.array([[[[1.0], [1.0], [1.0], [1.0], [1.0]]]]) assert np.all( - WeightedRegression().fit(X_full, y_full, w, "regression").slope_.flatten() - == [1.] + WeightedRegression() + .fit(X_full, y_full, w, "regression") + .slope_.flatten() + == [1.0] ) assert np.all( - WeightedRegression().fit(X_full, y_missing, w, "regression").slope_.flatten() - == [1.] + WeightedRegression() + .fit(X_full, y_missing, w, "regression") + .slope_.flatten() + == [1.0] ) assert np.all( - WeightedRegression().fit(X_missing, y_full, w, "regression").slope_.flatten() - == [1.] + WeightedRegression() + .fit(X_missing, y_full, w, "regression") + .slope_.flatten() + == [1.0] ) assert np.all( - WeightedRegression().fit(X_missing, y_missing, w, "regression").slope_.flatten() - == [1.] + WeightedRegression() + .fit(X_missing, y_missing, w, "regression") + .slope_.flatten() + == [1.0] ) From 1c545221524e4c052fc60515a9eda0bba52027fd Mon Sep 17 00:00:00 2001 From: "henrydingliu@gmail.com" Date: Fri, 11 Sep 2026 00:29:21 +0000 Subject: [PATCH 04/13] ruff fix --- chainladder/utils/tests/test_wtd_reg.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/chainladder/utils/tests/test_wtd_reg.py b/chainladder/utils/tests/test_wtd_reg.py index d5502db1..0a04ccf3 100644 --- a/chainladder/utils/tests/test_wtd_reg.py +++ b/chainladder/utils/tests/test_wtd_reg.py @@ -16,9 +16,7 @@ def test_missing_data(self) -> None: y_missing = np.array([[[[1.0], [2.0], [np.nan], [4.0], [5.0]]]]) w = np.array([[[[1.0], [1.0], [1.0], [1.0], [1.0]]]]) assert np.all( - WeightedRegression() - .fit(X_full, y_full, w, "regression") - .slope_.flatten() + WeightedRegression().fit(X_full, y_full, w, "regression").slope_.flatten() == [1.0] ) assert np.all( From 57f3dfb90af5b0f85fcb22f10d55d53b2ba44475 Mon Sep 17 00:00:00 2001 From: "henrydingliu@gmail.com" Date: Fri, 11 Sep 2026 05:18:27 +0000 Subject: [PATCH 05/13] adding sparse path --- chainladder/utils/tests/test_wtd_reg.py | 58 +++++++++++++----------- chainladder/utils/weighted_regression.py | 13 ++++-- 2 files changed, 41 insertions(+), 30 deletions(-) diff --git a/chainladder/utils/tests/test_wtd_reg.py b/chainladder/utils/tests/test_wtd_reg.py index 0a04ccf3..1f414798 100644 --- a/chainladder/utils/tests/test_wtd_reg.py +++ b/chainladder/utils/tests/test_wtd_reg.py @@ -1,7 +1,7 @@ from __future__ import annotations import numpy as np - +from chainladder.utils.sparse import sp from chainladder.utils.weighted_regression import WeightedRegression @@ -10,30 +10,34 @@ class TestOLS: def test_missing_data(self) -> None: """Check that having nan in X and/or y still results in the right OLS coefficients.""" - X_full = np.array([[[[1.0], [2.0], [3.0], [4.0], [5.0]]]]) - y_full = np.array([[[[1.0], [2.0], [3.0], [4.0], [5.0]]]]) - X_missing = np.array([[[[1.0], [np.nan], [3.0], [4.0], [5.0]]]]) - y_missing = np.array([[[[1.0], [2.0], [np.nan], [4.0], [5.0]]]]) - w = np.array([[[[1.0], [1.0], [1.0], [1.0], [1.0]]]]) - assert np.all( - WeightedRegression().fit(X_full, y_full, w, "regression").slope_.flatten() - == [1.0] - ) - assert np.all( - WeightedRegression() - .fit(X_full, y_missing, w, "regression") - .slope_.flatten() - == [1.0] - ) - assert np.all( - WeightedRegression() - .fit(X_missing, y_full, w, "regression") - .slope_.flatten() - == [1.0] - ) - assert np.all( - WeightedRegression() - .fit(X_missing, y_missing, w, "regression") - .slope_.flatten() - == [1.0] + data = [ + { + "module": np, + "X": [ + np.array([[[[1.0], [2.0], [3.0], [4.0], [5.0]]]]), + np.array([[[[1.0], [np.nan], [3.0], [4.0], [5.0]]]]), + ], + "y": [ + np.array([[[[1.0], [2.0], [3.0], [4.0], [5.0]]]]), + np.array([[[[1.0], [2.0], [np.nan], [4.0], [5.0]]]]), + ], + "w": np.array([[[[1.0], [1.0], [1.0], [1.0], [1.0]]]]), + "slope": np.array([[[[1.0]]]]), + }, + ] + data.append( + { + "module": sp, + "X": [sp.COO.from_numpy(i, fill_value=np.nan) for i in data[0]["X"]], + "y": [sp.COO.from_numpy(i, fill_value=np.nan) for i in data[0]["y"]], + "w": sp.COO.from_numpy(data[0]["w"]), + "slope": sp.COO.from_numpy(data[0]["slope"]), + } ) + for i in data: + for x in i["X"]: + for y in i["y"]: + assert i["module"].all( + WeightedRegression(xp=i["module"]).fit(x, y, i["w"], "regression").slope_ + == i["slope"] + ) diff --git a/chainladder/utils/weighted_regression.py b/chainladder/utils/weighted_regression.py index 85d098ab..08ea85e2 100644 --- a/chainladder/utils/weighted_regression.py +++ b/chainladder/utils/weighted_regression.py @@ -191,18 +191,25 @@ def _fit_ols(self): from chainladder.utils.utility_functions import num_to_nan w, x, y, axis = self.w.copy(), self.x.copy(), self.y.copy(), self.axis - w[np.isnan(x)] = 0 - w[np.isnan(y)] = 0 xp = self.xp if xp != sp: x[w == 0] = xp.nan y[w == 0] = xp.nan + w[np.isnan(x)] = 0 + w[np.isnan(y)] = 0 else: - w2 = w.copy() + x2, y2, w2 = x.copy(), y.copy(), w.copy() w2 = sp.COO( data=w2.data, coords=w2.coords, fill_value=sp.nan, shape=w2.shape ) x, y = x * w2, y * w2 + x2 = sp.COO( + data=1.0, coords=x2.coords, fill_value=sp.nan, shape=x2.shape + ) + y2 = sp.COO( + data=1.0, coords=y2.coords, fill_value=sp.nan, shape=y2.shape + ) + w = w * x2 * y2 with warnings.catch_warnings(): warnings.simplefilter("ignore", category=RuntimeWarning) From 53ce19444ed34990680ee3116115ebcb5cf7e155 Mon Sep 17 00:00:00 2001 From: "henrydingliu@gmail.com" Date: Fri, 11 Sep 2026 05:44:56 +0000 Subject: [PATCH 06/13] update sparse dependency --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1c2a57af..6b1404da 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,7 +32,7 @@ keywords = ["actuarial", "reserving", "insurance", "chainladder", "IBNR"] dependencies = [ "pandas >=2.3.3, !=3.0.4", "scikit-learn>1.4.2", - "sparse>=0.9", + "sparse>=0.18", "numpy>=2.0", "matplotlib", # Required for TriangleDisplay.heatmap() "dill", From 27648e9221d849bbda3a079e013005c1716dc1a2 Mon Sep 17 00:00:00 2001 From: "henrydingliu@gmail.com" Date: Fri, 11 Sep 2026 06:04:05 +0000 Subject: [PATCH 07/13] remove python 3.10 --- pyproject.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 6b1404da..2be3f829 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,7 @@ maintainers = [ description = "Chainladder Package - P&C Loss Reserving package" readme = "README.rst" license = {text = "MPL-2.0"} -requires-python = ">=3.10" +requires-python = ">=3.11" classifiers = [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", @@ -22,7 +22,6 @@ classifiers = [ "License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)", "Operating System :: OS Independent", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", From 4c292f6291fb82c7f1b53d7c3be6e5ec02ef4ad5 Mon Sep 17 00:00:00 2001 From: henrydingliu <106109320+henrydingliu@users.noreply.github.com> Date: Thu, 10 Sep 2026 23:12:38 -0700 Subject: [PATCH 08/13] Update pytest.yml --- .github/workflows/pytest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index f089ef0e..319bfea5 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -20,7 +20,7 @@ jobs: fail-fast: false matrix: os: ['ubuntu-latest'] - python-version: ['3.10', '3.11', '3.12', '3.13', '3.14'] + python-version: ['3.11', '3.12', '3.13', '3.14'] steps: - uses: actions/checkout@v6 - name: Install uv From 134047d88a5f3e8681ad65ff7f52f8f23c5d9aa0 Mon Sep 17 00:00:00 2001 From: henrydingliu <106109320+henrydingliu@users.noreply.github.com> Date: Thu, 10 Sep 2026 23:12:55 -0700 Subject: [PATCH 09/13] Update pytest_upstream_nightly.yml --- .github/workflows/pytest_upstream_nightly.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pytest_upstream_nightly.yml b/.github/workflows/pytest_upstream_nightly.yml index 359ad0bf..0b13c1c6 100644 --- a/.github/workflows/pytest_upstream_nightly.yml +++ b/.github/workflows/pytest_upstream_nightly.yml @@ -12,7 +12,7 @@ jobs: fail-fast: false matrix: os: ['ubuntu-latest'] - python-version: ['3.10', '3.11', '3.12', '3.13', '3.14'] + python-version: ['3.11', '3.12', '3.13', '3.14'] steps: - uses: actions/checkout@v6 - name: Install uv @@ -42,4 +42,4 @@ jobs: - name: Install dependencies run: uv sync --extra test - name: Run tests - run: uv run --with "pandas>=3,<4" pytest chainladder -m "not r" \ No newline at end of file + run: uv run --with "pandas>=3,<4" pytest chainladder -m "not r" From dd1f3aec55860351820178118ffb44629e7e76c2 Mon Sep 17 00:00:00 2001 From: henrydingliu <106109320+henrydingliu@users.noreply.github.com> Date: Thu, 10 Sep 2026 23:14:40 -0700 Subject: [PATCH 10/13] Update test_wtd_reg.py --- chainladder/utils/tests/test_wtd_reg.py | 32 ++++++++++++------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/chainladder/utils/tests/test_wtd_reg.py b/chainladder/utils/tests/test_wtd_reg.py index 1f414798..7426e15c 100644 --- a/chainladder/utils/tests/test_wtd_reg.py +++ b/chainladder/utils/tests/test_wtd_reg.py @@ -10,21 +10,19 @@ class TestOLS: def test_missing_data(self) -> None: """Check that having nan in X and/or y still results in the right OLS coefficients.""" - data = [ - { - "module": np, - "X": [ - np.array([[[[1.0], [2.0], [3.0], [4.0], [5.0]]]]), - np.array([[[[1.0], [np.nan], [3.0], [4.0], [5.0]]]]), - ], - "y": [ - np.array([[[[1.0], [2.0], [3.0], [4.0], [5.0]]]]), - np.array([[[[1.0], [2.0], [np.nan], [4.0], [5.0]]]]), - ], - "w": np.array([[[[1.0], [1.0], [1.0], [1.0], [1.0]]]]), - "slope": np.array([[[[1.0]]]]), - }, - ] + data = [{ + "module": np, + "X": [ + np.array([[[[1.0], [2.0], [3.0], [4.0], [5.0]]]]), + np.array([[[[1.0], [np.nan], [3.0], [4.0], [5.0]]]]), + ], + "y": [ + np.array([[[[1.0], [2.0], [3.0], [4.0], [5.0]]]]), + np.array([[[[1.0], [2.0], [np.nan], [4.0], [5.0]]]]), + ], + "w": np.array([[[[1.0], [1.0], [1.0], [1.0], [1.0]]]]), + "slope": np.array([[[[1.0]]]]), + }] data.append( { "module": sp, @@ -38,6 +36,8 @@ def test_missing_data(self) -> None: for x in i["X"]: for y in i["y"]: assert i["module"].all( - WeightedRegression(xp=i["module"]).fit(x, y, i["w"], "regression").slope_ + WeightedRegression(xp=i["module"]) + .fit(x, y, i["w"], "regression") + .slope_ == i["slope"] ) From 1a5135806e7327a14fd2b31d6c91b0c7a127433d Mon Sep 17 00:00:00 2001 From: henrydingliu <106109320+henrydingliu@users.noreply.github.com> Date: Thu, 10 Sep 2026 23:15:19 -0700 Subject: [PATCH 11/13] Update weighted_regression.py --- chainladder/utils/weighted_regression.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/chainladder/utils/weighted_regression.py b/chainladder/utils/weighted_regression.py index 08ea85e2..05296c64 100644 --- a/chainladder/utils/weighted_regression.py +++ b/chainladder/utils/weighted_regression.py @@ -203,12 +203,8 @@ def _fit_ols(self): data=w2.data, coords=w2.coords, fill_value=sp.nan, shape=w2.shape ) x, y = x * w2, y * w2 - x2 = sp.COO( - data=1.0, coords=x2.coords, fill_value=sp.nan, shape=x2.shape - ) - y2 = sp.COO( - data=1.0, coords=y2.coords, fill_value=sp.nan, shape=y2.shape - ) + x2 = sp.COO(data=1.0, coords=x2.coords, fill_value=sp.nan, shape=x2.shape) + y2 = sp.COO(data=1.0, coords=y2.coords, fill_value=sp.nan, shape=y2.shape) w = w * x2 * y2 with warnings.catch_warnings(): From 4e1c30430c6c32a8136ca7e9419fe2f7bb0036f5 Mon Sep 17 00:00:00 2001 From: henrydingliu <106109320+henrydingliu@users.noreply.github.com> Date: Thu, 10 Sep 2026 23:18:35 -0700 Subject: [PATCH 12/13] Update test_wtd_reg.py --- chainladder/utils/tests/test_wtd_reg.py | 32 ++++++++++++------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/chainladder/utils/tests/test_wtd_reg.py b/chainladder/utils/tests/test_wtd_reg.py index 7426e15c..e1272ef4 100644 --- a/chainladder/utils/tests/test_wtd_reg.py +++ b/chainladder/utils/tests/test_wtd_reg.py @@ -10,28 +10,28 @@ class TestOLS: def test_missing_data(self) -> None: """Check that having nan in X and/or y still results in the right OLS coefficients.""" - data = [{ - "module": np, - "X": [ - np.array([[[[1.0], [2.0], [3.0], [4.0], [5.0]]]]), - np.array([[[[1.0], [np.nan], [3.0], [4.0], [5.0]]]]), - ], - "y": [ - np.array([[[[1.0], [2.0], [3.0], [4.0], [5.0]]]]), - np.array([[[[1.0], [2.0], [np.nan], [4.0], [5.0]]]]), - ], - "w": np.array([[[[1.0], [1.0], [1.0], [1.0], [1.0]]]]), - "slope": np.array([[[[1.0]]]]), - }] - data.append( + data = [ { + "module": np, + "X": [ + np.array([[[[1.0], [2.0], [3.0], [4.0], [5.0]]]]), + np.array([[[[1.0], [np.nan], [3.0], [4.0], [5.0]]]]), + ], + "y": [ + np.array([[[[1.0], [2.0], [3.0], [4.0], [5.0]]]]), + np.array([[[[1.0], [2.0], [np.nan], [4.0], [5.0]]]]), + ], + "w": np.array([[[[1.0], [1.0], [1.0], [1.0], [1.0]]]]), + "slope": np.array([[[[1.0]]]]), + } + ] + data.append({ "module": sp, "X": [sp.COO.from_numpy(i, fill_value=np.nan) for i in data[0]["X"]], "y": [sp.COO.from_numpy(i, fill_value=np.nan) for i in data[0]["y"]], "w": sp.COO.from_numpy(data[0]["w"]), "slope": sp.COO.from_numpy(data[0]["slope"]), - } - ) + }) for i in data: for x in i["X"]: for y in i["y"]: From ebc24f3e934cd9f2fd870b8c37e24a2cfa5b60e0 Mon Sep 17 00:00:00 2001 From: henrydingliu <106109320+henrydingliu@users.noreply.github.com> Date: Thu, 10 Sep 2026 23:19:53 -0700 Subject: [PATCH 13/13] Update test_wtd_reg.py --- chainladder/utils/tests/test_wtd_reg.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/chainladder/utils/tests/test_wtd_reg.py b/chainladder/utils/tests/test_wtd_reg.py index e1272ef4..18b981e2 100644 --- a/chainladder/utils/tests/test_wtd_reg.py +++ b/chainladder/utils/tests/test_wtd_reg.py @@ -26,11 +26,11 @@ def test_missing_data(self) -> None: } ] data.append({ - "module": sp, - "X": [sp.COO.from_numpy(i, fill_value=np.nan) for i in data[0]["X"]], - "y": [sp.COO.from_numpy(i, fill_value=np.nan) for i in data[0]["y"]], - "w": sp.COO.from_numpy(data[0]["w"]), - "slope": sp.COO.from_numpy(data[0]["slope"]), + "module": sp, + "X": [sp.COO.from_numpy(i, fill_value=np.nan) for i in data[0]["X"]], + "y": [sp.COO.from_numpy(i, fill_value=np.nan) for i in data[0]["y"]], + "w": sp.COO.from_numpy(data[0]["w"]), + "slope": sp.COO.from_numpy(data[0]["slope"]), }) for i in data: for x in i["X"]: