diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index e6e36f4..cbe2fdc 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -53,18 +53,6 @@ A current-state audit of the whole `recurrent/` package (parametric intensity, renewal, regression, nonparametric, competing risks). Findings are grouped by theme; items already resolved are marked **[done]**. -**B. Dead code / stubs / docs** -- `ParametricRecurrenceRegressionModel` (`parametric_regression.py`) — was a - `# TODO` stub, never imported/exported and a duplicate of - `ParametricRecurrenceModel`; recurrence-regression results are served by - `ProportionalIntensityModel`. **[done]** (file removed) -- `_validate_memory` duplicated verbatim in `renewal/ara.py` and - `renewal/ari.py`. -- 16 copy-pasted docstrings read "Parameters of the Duane model" inside - `CrowAMSAA`/`CoxLewis`. -- `has_left_censoing` typo (`nhpp_fitter.py`, NHPP regression file). -- Doubled `to_xrd()` call in `nonparametric/mcf.py:106-107`. - **C. Simplification** - The multi-start MLE fit scaffolding is copy-pasted across the four repair fitters (`GeneralizedRenewal`, `GeneralizedOneRenewal`, `ARA`, `ARI`): the diff --git a/surpyval/recurrent/parametric/crow_amsaa.py b/surpyval/recurrent/parametric/crow_amsaa.py index e374812..5b49a20 100644 --- a/surpyval/recurrent/parametric/crow_amsaa.py +++ b/surpyval/recurrent/parametric/crow_amsaa.py @@ -61,7 +61,7 @@ def cif(self, x, *params): x : float The value at which CIF is evaluated. params : tuple - Parameters of the Duane model. + Parameters of the Crow-AMSAA model. Returns @@ -85,7 +85,7 @@ def iif(self, x, *params): x : float The value at which IIF is evaluated. params : tuple - Parameters of the Duane model. + Parameters of the Crow-AMSAA model. Returns ------- @@ -108,7 +108,7 @@ def log_iif(self, x, *params): x : float The value at which log(IIF) is evaluated. params : tuple - Parameters of the Duane model. + Parameters of the Crow-AMSAA model. Returns ------- @@ -131,7 +131,7 @@ def inv_cif(self, N, *params): N : float The number of events expected to have occured. params : tuple - Parameters of the Duane model. + Parameters of the Crow-AMSAA model. Returns ------- diff --git a/surpyval/recurrent/parametric/nhpp_fitter.py b/surpyval/recurrent/parametric/nhpp_fitter.py index 1417d96..275fb01 100644 --- a/surpyval/recurrent/parametric/nhpp_fitter.py +++ b/surpyval/recurrent/parametric/nhpp_fitter.py @@ -16,7 +16,7 @@ def create_negll_func(self, data): has_interval_censoring = True if 2 in c else False has_observed = True if 0 in c else False - has_left_censoing = True if -1 in c else False + has_left_censoring = True if -1 in c else False has_right_censoring = True if 1 in c else False x_l = x if x.ndim == 1 else x[:, 0] @@ -41,8 +41,8 @@ def create_negll_func(self, data): x_prev_l[c == 1] if has_right_censoring else np.array([]) ) - x_left = x_l[c == -1] if has_left_censoing else np.array([]) - n_left = n[c == -1] if has_left_censoing else np.array([]) + x_left = x_l[c == -1] if has_left_censoring else np.array([]) + n_left = n[c == -1] if has_left_censoring else np.array([]) x_i_l = x_l[c == 2] if has_interval_censoring else np.array([]) x_i_r = x_r[c == 2] if has_interval_censoring else np.array([]) diff --git a/surpyval/recurrent/regression/nhpp_proportional_intensity.py b/surpyval/recurrent/regression/nhpp_proportional_intensity.py index 46d46ba..b68d4c1 100644 --- a/surpyval/recurrent/regression/nhpp_proportional_intensity.py +++ b/surpyval/recurrent/regression/nhpp_proportional_intensity.py @@ -80,7 +80,7 @@ def create_negll_func(self, data, dist): has_interval_censoring = True if 2 in c else False has_observed = True if 0 in c else False - has_left_censoing = True if -1 in c else False + has_left_censoring = True if -1 in c else False has_right_censoring = True if 1 in c else False x_l = x if x.ndim == 1 else x[:, 0] @@ -112,9 +112,11 @@ def create_negll_func(self, data, dist): ) # Untangle the left censored data - x_left = x_l[c == -1] if has_left_censoing else np.array([]) - n_left = n[c == -1] if has_left_censoing else np.array([]) - Z_left = Z[c == -1] if has_left_censoing else np.zeros((1, Z.shape[1])) + x_left = x_l[c == -1] if has_left_censoring else np.array([]) + n_left = n[c == -1] if has_left_censoring else np.array([]) + Z_left = ( + Z[c == -1] if has_left_censoring else np.zeros((1, Z.shape[1])) + ) # Untangle the interval censored data x_i_l = x_l[c == 2] if has_interval_censoring else np.array([]) diff --git a/surpyval/recurrent/renewal/ara.py b/surpyval/recurrent/renewal/ara.py index 4184ff4..5b26344 100644 --- a/surpyval/recurrent/renewal/ara.py +++ b/surpyval/recurrent/renewal/ara.py @@ -8,6 +8,7 @@ from surpyval.utils.recurrent_utils import ( handle_xicn, reject_left_truncation, + validate_memory, validate_renewal_censoring, ) @@ -81,15 +82,6 @@ class ARA: >>> model = ARA.fit(x, i, c=c, m=2) """ - @staticmethod - def _validate_memory(m): - if m == np.inf: - return - if not (isinstance(m, (int, np.integer)) and m >= 1): - raise ValueError( - "m must be a positive integer or numpy.inf; got {!r}".format(m) - ) - @staticmethod def _build_sampler(model): rho = model.rho @@ -179,7 +171,7 @@ def fit_from_recurrent_data(self, data, dist=Weibull, m=1, init=None): RenewalModel A fitted renewal model. """ - self._validate_memory(m) + validate_memory(m) validate_renewal_censoring(data.c, type(self).__name__) reject_left_truncation(data, type(self).__name__) @@ -293,6 +285,6 @@ def fit_from_parameters(self, params, rho, m=1, dist=Weibull): ARA An ARA object built from the supplied parameters. """ - self._validate_memory(m) + validate_memory(m) model = dist.from_params(params) return self._make_model(model, rho, m) diff --git a/surpyval/recurrent/renewal/ari.py b/surpyval/recurrent/renewal/ari.py index 0fe59a1..abd80ff 100644 --- a/surpyval/recurrent/renewal/ari.py +++ b/surpyval/recurrent/renewal/ari.py @@ -7,6 +7,7 @@ from surpyval.utils.recurrent_utils import ( handle_xicn, reject_left_truncation, + validate_memory, validate_renewal_censoring, ) @@ -69,15 +70,6 @@ class ARI: >>> model = ARI.fit(x, i, m=1, dist=CrowAMSAA) """ - @staticmethod - def _validate_memory(m): - if m == np.inf: - return - if not (isinstance(m, (int, np.integer)) and m >= 1): - raise ValueError( - "m must be a positive integer or numpy.inf; got {!r}".format(m) - ) - @staticmethod def _build_sampler(model): dist = model.model.dist @@ -185,7 +177,7 @@ def fit_from_recurrent_data(self, data, dist=CrowAMSAA, m=1, init=None): ARI A fitted ARI object. """ - self._validate_memory(m) + validate_memory(m) validate_renewal_censoring(data.c, type(self).__name__) reject_left_truncation(data, type(self).__name__) @@ -294,5 +286,5 @@ def fit_from_parameters(self, dist_params, rho, m=1, dist=CrowAMSAA): ARI An ARI object built from the supplied parameters. """ - self._validate_memory(m) + validate_memory(m) return self._make_model(dist, dist_params, rho, m) diff --git a/surpyval/utils/recurrent_utils.py b/surpyval/utils/recurrent_utils.py index 82a6c00..7861f6e 100644 --- a/surpyval/utils/recurrent_utils.py +++ b/surpyval/utils/recurrent_utils.py @@ -54,6 +54,21 @@ def reject_unsupported_nonparametric( ) +def validate_memory(m: object) -> None: + """ + The Arithmetic Reduction of Age/Intensity models (``ARA``/``ARI``) are + parameterised by an integer memory ``m`` (how many prior failures the + repair acts on), with ``m = numpy.inf`` recovering the infinite-memory + limit. Reject anything that is neither a positive integer nor ``inf``. + """ + if m == np.inf: + return + if not (isinstance(m, (int, np.integer)) and m >= 1): + raise ValueError( + "m must be a positive integer or numpy.inf; got {!r}".format(m) + ) + + def validate_renewal_censoring(c: npt.ArrayLike, model_name: str) -> None: """ The renewal models only define likelihood contributions for exact events