From c78db01ca00a37959b671dedfa801b9e671bdbc8 Mon Sep 17 00:00:00 2001 From: Ryan O'Dea <70209371+ryan-odea@users.noreply.github.com> Date: Tue, 25 Nov 2025 16:48:50 +0100 Subject: [PATCH 1/2] update switch within outcome model --- pySEQTarget/analysis/_outcome_fit.py | 3 + tests/test_coefficients.py | 84 +++++++++++++++------------- 2 files changed, 47 insertions(+), 40 deletions(-) diff --git a/pySEQTarget/analysis/_outcome_fit.py b/pySEQTarget/analysis/_outcome_fit.py index 7ed823f..7dff090 100644 --- a/pySEQTarget/analysis/_outcome_fit.py +++ b/pySEQTarget/analysis/_outcome_fit.py @@ -20,6 +20,9 @@ def _outcome_fit( ) ) + if self.method == "censoring": + df = df.filter(pl.col("switch") != 1) + df_pd = df.to_pandas() df_pd[self.treatment_col] = df_pd[self.treatment_col].astype("category") diff --git a/tests/test_coefficients.py b/tests/test_coefficients.py index 07435c7..5c15643 100644 --- a/tests/test_coefficients.py +++ b/tests/test_coefficients.py @@ -120,14 +120,15 @@ def test_PreE_censoring_coefs(): s.fit() matrix = s.outcome_model[0]["outcome"].summary2().tables[1]["Coef."].to_list() expected = [ - -4.818288687908951, - 0.511665606890965, - 0.062028316788368384, - 0.025489681857269905, - 0.00018215948440046585, - -0.014019017637918164, - 0.001110238926667272, + -4.872373936951975, + 0.48389186624295133, + 0.0477349453301334, + 0.029127276869076173, + 4.784054961154824e-05, + -0.013614654772205668, + 0.0011281734101133744, ] + assert [round(x, 3) for x in matrix] == [round(x, 3) for x in expected] @@ -150,17 +151,18 @@ def test_PostE_censoring_coefs(): s.fit() matrix = s.outcome_model[0]["outcome"].summary2().tables[1]["Coef."].to_list() expected = [ - -7.9113179326280445, - 0.49092190701455873, - 0.08903087485402544, - 0.026160806382879903, - 0.00019078148503570062, - 0.04445697224987294, - 0.0007051968052005897, - 0.004316239095295115, - 0.013762799304812959, - 0.3196331024454665, + -9.172266785106519, + 0.4707554720116625, + 0.08162617232478116, + 0.029021087196430605, + 7.8937226861939e-05, + 0.06700192286925702, + 0.0005834323664644191, + 0.004870212765388434, + 0.013503198983327514, + 0.4466573801510379, ] + print(matrix) assert [round(x, 3) for x in matrix] == [round(x, 3) for x in expected] @@ -188,13 +190,14 @@ def test_PreE_censoring_excused_coefs(): s.fit() matrix = s.outcome_model[0]["outcome"].summary2().tables[1]["Coef."].to_list() expected = [ - -6.175691049418418, - 1.3493634846413598, - 0.1072284696749134, - -0.003977965364113033, - 0.06959432825811135, - -0.00034297574787048573, + -6.460912082691973, + 1.309708035546933, + 0.10853511682679658, + -0.0038913520688693823, + 0.08849129909709463, + -0.000647578869153453, ] + print(matrix) assert [round(x, 3) for x in matrix] == [round(x, 3) for x in expected] @@ -222,17 +225,18 @@ def test_PostE_censoring_excused_coefs(): s.fit() matrix = s.outcome_model[0]["outcome"].summary2().tables[1]["Coef."].to_list() expected = [ - -7.126398786875262, - 0.2632047482928519, - 0.13345454814736696, - 0.03967181206032395, - -0.00033089446793392585, - 0.03763545026332514, - 0.0007588725152627089, - 0.0036793093608788923, - -0.022372677571544992, - 0.2441842617520696, + -6.790198674624166, + 0.2482916756717123, + 0.13513571519227358, + 0.04065051825929703, + -0.000339056860357248, + 0.03249136338844386, + 0.0007666817173135215, + 0.003709563135709279, + -0.013909695276596253, + 0.20531742459641683, ] + print(matrix) assert [round(x, 3) for x in matrix] == [round(x, 3) for x in expected] @@ -358,12 +362,12 @@ def test_weighted_multinomial(): s.fit() matrix = s.outcome_model[0]["outcome"].summary2().tables[1]["Coef."].to_list() expected = [ - -111.35419661939163, - -12.571187230338328, - 9.234157699403015, - -0.6336774763031923, - 0.016754692338530056, - 5.8240772329087225, - -0.08598454090661659, + -109.99715622379995, + -12.536816769546702, + 9.22013733949143, + -0.6129380297017852, + 0.01597877250531723, + 5.743984176710672, + -0.08478678955657822, ] assert [round(x, 3) for x in matrix] == [round(x, 3) for x in expected] From 65c7b35fe9027764ff2f13271424e6b41f26abd1 Mon Sep 17 00:00:00 2001 From: Ryan O'Dea <70209371+ryan-odea@users.noreply.github.com> Date: Tue, 25 Nov 2025 19:54:03 +0100 Subject: [PATCH 2/2] fix denominator subsetting --- docs/conf.py | 1 + pySEQTarget/SEQuential.py | 31 +++++++++++++++++++++------- pySEQTarget/analysis/__init__.py | 3 +-- pySEQTarget/weighting/_weight_fit.py | 2 +- tests/test_coefficients.py | 22 +++++++++----------- 5 files changed, 37 insertions(+), 22 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 0fe672d..a7f536b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -4,6 +4,7 @@ # https://www.sphinx-doc.org/en/master/usage/configuration.html import importlib.metadata + # -- Project information ----------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information import os diff --git a/pySEQTarget/SEQuential.py b/pySEQTarget/SEQuential.py index b068dc9..d13685f 100644 --- a/pySEQTarget/SEQuential.py +++ b/pySEQTarget/SEQuential.py @@ -7,19 +7,36 @@ import numpy as np import polars as pl -from .analysis import (_calculate_hazard, _calculate_survival, _outcome_fit, - _pred_risk, _risk_estimates, _subgroup_fit) +from .analysis import ( + _calculate_hazard, + _calculate_survival, + _outcome_fit, + _pred_risk, + _risk_estimates, + _subgroup_fit, +) from .error import _datachecker, _param_checker from .expansion import _binder, _diagnostics, _dynamic, _random_selection from .helpers import _col_string, _format_time, bootstrap_loop -from .initialization import (_cense_denominator, _cense_numerator, - _denominator, _numerator, _outcome) +from .initialization import ( + _cense_denominator, + _cense_numerator, + _denominator, + _numerator, + _outcome, +) from .plot import _survival_plot from .SEQopts import SEQopts from .SEQoutput import SEQoutput -from .weighting import (_fit_denominator, _fit_LTFU, _fit_numerator, - _weight_bind, _weight_predict, _weight_setup, - _weight_stats) +from .weighting import ( + _fit_denominator, + _fit_LTFU, + _fit_numerator, + _weight_bind, + _weight_predict, + _weight_setup, + _weight_stats, +) class SEQuential: diff --git a/pySEQTarget/analysis/__init__.py b/pySEQTarget/analysis/__init__.py index 6799dfd..e35ceb7 100644 --- a/pySEQTarget/analysis/__init__.py +++ b/pySEQTarget/analysis/__init__.py @@ -3,6 +3,5 @@ from ._risk_estimates import _risk_estimates as _risk_estimates from ._subgroup_fit import _subgroup_fit as _subgroup_fit from ._survival_pred import _calculate_survival as _calculate_survival -from ._survival_pred import \ - _get_outcome_predictions as _get_outcome_predictions +from ._survival_pred import _get_outcome_predictions as _get_outcome_predictions from ._survival_pred import _pred_risk as _pred_risk diff --git a/pySEQTarget/weighting/_weight_fit.py b/pySEQTarget/weighting/_weight_fit.py index 70fc85d..2996764 100644 --- a/pySEQTarget/weighting/_weight_fit.py +++ b/pySEQTarget/weighting/_weight_fit.py @@ -65,7 +65,7 @@ def _fit_denominator(self, WDT): DT_subset = WDT if self.weight_lag_condition: DT_subset = DT_subset[DT_subset["tx_lag"] == level] - if not self.weight_preexpansion and not self.excused: + if not self.weight_preexpansion: DT_subset = DT_subset[DT_subset["followup"] != 0] if self.weight_eligible_colnames[i] is not None: DT_subset = DT_subset[DT_subset[self.weight_eligible_colnames[i]] == 1] diff --git a/tests/test_coefficients.py b/tests/test_coefficients.py index 5c15643..948ba11 100644 --- a/tests/test_coefficients.py +++ b/tests/test_coefficients.py @@ -162,7 +162,6 @@ def test_PostE_censoring_coefs(): 0.013503198983327514, 0.4466573801510379, ] - print(matrix) assert [round(x, 3) for x in matrix] == [round(x, 3) for x in expected] @@ -225,18 +224,17 @@ def test_PostE_censoring_excused_coefs(): s.fit() matrix = s.outcome_model[0]["outcome"].summary2().tables[1]["Coef."].to_list() expected = [ - -6.790198674624166, - 0.2482916756717123, - 0.13513571519227358, - 0.04065051825929703, - -0.000339056860357248, - 0.03249136338844386, - 0.0007666817173135215, - 0.003709563135709279, - -0.013909695276596253, - 0.20531742459641683, + -6.782732929102242, + 0.26371172100905477, + 0.13625528598217598, + 0.040580427030886, + -0.000343018323531494, + 0.031185150775465315, + 0.000784356550754563, + 0.004338417236024277, + -0.013052187516528172, + 0.20402680950820007, ] - print(matrix) assert [round(x, 3) for x in matrix] == [round(x, 3) for x in expected]