lifelines 0.30.0__tar.gz → 0.30.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {lifelines-0.30.0/lifelines.egg-info → lifelines-0.30.1}/PKG-INFO +14 -3
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/fitters/__init__.py +8 -4
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/fitters/aalen_additive_fitter.py +4 -4
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/fitters/aalen_johansen_fitter.py +19 -13
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/fitters/cox_time_varying_fitter.py +5 -3
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/fitters/coxph_fitter.py +8 -4
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/plotting.py +5 -2
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/statistics.py +2 -3
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/tests/test_estimation.py +21 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/tests/test_npmle.py +1 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/tests/test_plotting.py +17 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/tests/test_statistics.py +18 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/tests/utils/test_utils.py +40 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/utils/__init__.py +51 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/version.py +1 -1
- {lifelines-0.30.0 → lifelines-0.30.1/lifelines.egg-info}/PKG-INFO +14 -3
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines.egg-info/requires.txt +1 -1
- {lifelines-0.30.0 → lifelines-0.30.1}/reqs/base-requirements.txt +1 -1
- {lifelines-0.30.0 → lifelines-0.30.1}/LICENSE +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/MANIFEST.in +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/README.md +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/__init__.py +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/calibration.py +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/datasets/CuZn-LeftCensoredDataset.csv +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/datasets/__init__.py +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/datasets/anderson.csv +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/datasets/c_botulinum_lag_phase.csv +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/datasets/canadian_senators.csv +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/datasets/dd.csv +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/datasets/dfcv_dataset.py +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/datasets/divorce.dat +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/datasets/g3.csv +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/datasets/gbsg2.csv +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/datasets/gehan.dat +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/datasets/holly_molly_polly.tsv +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/datasets/interval_diabetes.csv +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/datasets/kidney_transplant.csv +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/datasets/larynx.csv +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/datasets/lung.csv +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/datasets/lymph_node.csv +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/datasets/lymphoma.csv +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/datasets/merrell1955.csv +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/datasets/mice.csv +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/datasets/multicenter_aids_cohort.tsv +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/datasets/nh4.csv +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/datasets/panel_test.csv +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/datasets/psychiatric_patients.csv +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/datasets/recur.csv +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/datasets/regression.csv +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/datasets/rossi.csv +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/datasets/stanford_heart.csv +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/datasets/static_test.csv +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/datasets/waltons_dataset.csv +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/exceptions.py +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/fitters/breslow_fleming_harrington_fitter.py +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/fitters/crc_spline_fitter.py +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/fitters/exponential_fitter.py +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/fitters/generalized_gamma_fitter.py +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/fitters/generalized_gamma_regression_fitter.py +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/fitters/kaplan_meier_fitter.py +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/fitters/log_logistic_aft_fitter.py +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/fitters/log_logistic_fitter.py +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/fitters/log_normal_aft_fitter.py +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/fitters/log_normal_fitter.py +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/fitters/mixins.py +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/fitters/mixture_cure_fitter.py +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/fitters/nelson_aalen_fitter.py +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/fitters/npmle.py +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/fitters/piecewise_exponential_fitter.py +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/fitters/piecewise_exponential_regression_fitter.py +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/fitters/spline_fitter.py +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/fitters/weibull_aft_fitter.py +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/fitters/weibull_fitter.py +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/generate_datasets.py +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/tests/__init__.py +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/tests/test_generate_datasets.py +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/tests/utils/test_btree.py +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/tests/utils/test_concordance.py +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/utils/btree.py +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/utils/concordance.py +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/utils/lowess.py +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/utils/printer.py +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines/utils/safe_exp.py +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines.egg-info/SOURCES.txt +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines.egg-info/dependency_links.txt +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/lifelines.egg-info/top_level.txt +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/reqs/dev-requirements.txt +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/reqs/docs-requirements.txt +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/setup.cfg +0 -0
- {lifelines-0.30.0 → lifelines-0.30.1}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: lifelines
|
|
3
|
-
Version: 0.30.
|
|
3
|
+
Version: 0.30.1
|
|
4
4
|
Summary: Survival analysis in Python, including Kaplan Meier, Nelson Aalen and regression
|
|
5
5
|
Home-page: https://github.com/CamDavidsonPilon/lifelines
|
|
6
6
|
Author: Cameron Davidson-Pilon
|
|
@@ -18,11 +18,22 @@ Description-Content-Type: text/markdown
|
|
|
18
18
|
License-File: LICENSE
|
|
19
19
|
Requires-Dist: numpy>=1.14.0
|
|
20
20
|
Requires-Dist: scipy>=1.7.0
|
|
21
|
-
Requires-Dist: pandas
|
|
21
|
+
Requires-Dist: pandas<3.0,>=2.1
|
|
22
22
|
Requires-Dist: matplotlib>=3.0
|
|
23
23
|
Requires-Dist: autograd>=1.5
|
|
24
24
|
Requires-Dist: autograd-gamma>=0.3
|
|
25
25
|
Requires-Dist: formulaic>=0.2.2
|
|
26
|
+
Dynamic: author
|
|
27
|
+
Dynamic: author-email
|
|
28
|
+
Dynamic: classifier
|
|
29
|
+
Dynamic: description
|
|
30
|
+
Dynamic: description-content-type
|
|
31
|
+
Dynamic: home-page
|
|
32
|
+
Dynamic: license
|
|
33
|
+
Dynamic: license-file
|
|
34
|
+
Dynamic: requires-dist
|
|
35
|
+
Dynamic: requires-python
|
|
36
|
+
Dynamic: summary
|
|
26
37
|
|
|
27
38
|

|
|
28
39
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
from functools import partial, wraps
|
|
4
4
|
from inspect import getfullargspec
|
|
5
|
-
from datetime import datetime
|
|
5
|
+
from datetime import datetime, UTC
|
|
6
6
|
from textwrap import dedent
|
|
7
7
|
import typing as t
|
|
8
8
|
import collections
|
|
@@ -1229,11 +1229,15 @@ class ParametricUnivariateFitter(UnivariateFitter):
|
|
|
1229
1229
|
# use numerical solver to find the value p = e^{-H(t)}. I think I could use `root` in scipy
|
|
1230
1230
|
# instead of the scalar version. TODO
|
|
1231
1231
|
def _find_root(_p):
|
|
1232
|
-
|
|
1233
|
-
|
|
1232
|
+
survival_at_t = lambda t: float(self.survival_function_at_times(t).values[0])
|
|
1233
|
+
hazard_at_t = lambda t: float(self.hazard_at_times(t).values[0])
|
|
1234
|
+
f = lambda t: _p - survival_at_t(t)
|
|
1235
|
+
fprime = lambda t: survival_at_t(t) * hazard_at_t(t)
|
|
1234
1236
|
return root_scalar(f, bracket=(1e-10, 2 * self.timeline[-1]), fprime=fprime, x0=1.0).root
|
|
1235
1237
|
|
|
1236
1238
|
try:
|
|
1239
|
+
if np.isscalar(p):
|
|
1240
|
+
return float(_find_root(p))
|
|
1237
1241
|
find_root = np.vectorize(_find_root, otypes=[float])
|
|
1238
1242
|
return find_root(p)
|
|
1239
1243
|
except ValueError:
|
|
@@ -1776,7 +1780,7 @@ class ParametricRegressionFitter(RegressionFitter):
|
|
|
1776
1780
|
fit_options: Optional[dict] = None,
|
|
1777
1781
|
) -> ParametricRegressionFitter:
|
|
1778
1782
|
|
|
1779
|
-
self._time_fit_was_called = datetime.
|
|
1783
|
+
self._time_fit_was_called = datetime.now(UTC).strftime("%Y-%m-%d %H:%M:%S") + " UTC"
|
|
1780
1784
|
self._n_examples = df.shape[0]
|
|
1781
1785
|
self.weights_col = weights_col
|
|
1782
1786
|
self.entry_col = entry_col
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
import warnings
|
|
3
|
-
from datetime import datetime
|
|
3
|
+
from datetime import datetime, UTC
|
|
4
4
|
import time
|
|
5
5
|
|
|
6
6
|
import numpy as np
|
|
@@ -153,7 +153,7 @@ class AalenAdditiveFitter(RegressionFitter):
|
|
|
153
153
|
aaf.print_summary()
|
|
154
154
|
|
|
155
155
|
"""
|
|
156
|
-
self._time_fit_was_called = datetime.
|
|
156
|
+
self._time_fit_was_called = datetime.now(UTC).strftime("%Y-%m-%d %H:%M:%S") + " UTC"
|
|
157
157
|
|
|
158
158
|
df = df.copy()
|
|
159
159
|
|
|
@@ -244,7 +244,7 @@ class AalenAdditiveFitter(RegressionFitter):
|
|
|
244
244
|
|
|
245
245
|
hazards_[i, :] = v
|
|
246
246
|
|
|
247
|
-
variance_hazards_[i, :] = (V
|
|
247
|
+
variance_hazards_[i, :] = (V**2).sum(1)
|
|
248
248
|
|
|
249
249
|
X[exits, :] = 0
|
|
250
250
|
|
|
@@ -500,7 +500,7 @@ It's important to know that the naive variance estimates of the coefficients are
|
|
|
500
500
|
# normally (weights * X).dot(Y) / X.dot(weights * X), but we have a slightly different form here.
|
|
501
501
|
beta = X.dot(Y) / X.dot(weights * X)
|
|
502
502
|
errors = Y.values - np.outer(X, beta)
|
|
503
|
-
var = (errors
|
|
503
|
+
var = (errors**2).sum(0) / (Y.shape[0] - 2) / X.dot(weights * X)
|
|
504
504
|
return beta, np.sqrt(var)
|
|
505
505
|
|
|
506
506
|
weights = survival_table_from_events(self.durations, self.event_observed).loc[self._index, "at_risk"].values
|
|
@@ -5,7 +5,7 @@ import pandas as pd
|
|
|
5
5
|
import warnings
|
|
6
6
|
|
|
7
7
|
from lifelines.fitters import NonParametricUnivariateFitter
|
|
8
|
-
from lifelines.utils import _preprocess_inputs, inv_normal_cdf, CensoringType, coalesce
|
|
8
|
+
from lifelines.utils import _preprocess_inputs, inv_normal_cdf, CensoringType, coalesce, LinearAccumulator, QuadraticAccumulator
|
|
9
9
|
from lifelines import KaplanMeierFitter
|
|
10
10
|
from lifelines.plotting import _plot_estimate
|
|
11
11
|
|
|
@@ -219,22 +219,28 @@ class AalenJohansenFitter(NonParametricUnivariateFitter):
|
|
|
219
219
|
ci_labels = ["%s_upper_%g" % (self._label, ci), "%s_lower_%g" % (self._label, ci)]
|
|
220
220
|
assert len(ci_labels) == 2, "ci_labels should be a length 2 array."
|
|
221
221
|
|
|
222
|
-
#
|
|
222
|
+
# Use prefix sum algorithm to reduce time complexity to O(n), by cumulatively updating terms.
|
|
223
|
+
first_term_function = QuadraticAccumulator()
|
|
224
|
+
second_term = 0
|
|
225
|
+
third_term_function = LinearAccumulator()
|
|
223
226
|
all_vars = []
|
|
224
227
|
for _, r in df.iterrows():
|
|
225
|
-
sf = df.loc[df.index <= r.name].copy()
|
|
226
228
|
F_t = float(r["Ft"])
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
*
|
|
234
|
-
|
|
229
|
+
|
|
230
|
+
first_term_coefficient = r["observed"] / r["at_risk"] / (r["at_risk"] - r["observed"])
|
|
231
|
+
first_term_function.add_quadratic_term(a = first_term_coefficient, b = F_t)
|
|
232
|
+
|
|
233
|
+
second_term += (
|
|
234
|
+
(r["lagS"] ** 2)
|
|
235
|
+
* r[self.label_cmprisk]
|
|
236
|
+
* (r["at_risk"] - r[self.label_cmprisk])
|
|
237
|
+
/ (r["at_risk"] ** 3)
|
|
235
238
|
)
|
|
236
|
-
|
|
237
|
-
|
|
239
|
+
|
|
240
|
+
third_term_coefficient = r["lagS"] * r[self.label_cmprisk] / (r["at_risk"] ** 2)
|
|
241
|
+
third_term_function.add_linear_term(a = third_term_coefficient, b = F_t)
|
|
242
|
+
|
|
243
|
+
variance = first_term_function.evaluate(F_t) + second_term - 2 * third_term_function.evaluate(F_t)
|
|
238
244
|
all_vars.append(variance)
|
|
239
245
|
df["variance"] = all_vars
|
|
240
246
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
from datetime import datetime
|
|
4
|
+
from datetime import datetime, UTC
|
|
5
5
|
import warnings
|
|
6
6
|
import time
|
|
7
7
|
from typing import Optional
|
|
@@ -172,7 +172,7 @@ class CoxTimeVaryingFitter(SemiParametricRegressionFitter, ProportionalHazardMix
|
|
|
172
172
|
self.stop_col = stop_col
|
|
173
173
|
self.start_col = start_col
|
|
174
174
|
self.formula = formula
|
|
175
|
-
self._time_fit_was_called = datetime.
|
|
175
|
+
self._time_fit_was_called = datetime.now(UTC).strftime("%Y-%m-%d %H:%M:%S") + " UTC"
|
|
176
176
|
|
|
177
177
|
df = df.copy()
|
|
178
178
|
|
|
@@ -801,7 +801,9 @@ See https://stats.stackexchange.com/questions/11109/how-to-deal-with-perfect-sep
|
|
|
801
801
|
hazards = self.predict_partial_hazard(tv_data).values
|
|
802
802
|
|
|
803
803
|
unique_death_times = np.unique(stop[events.values])
|
|
804
|
-
baseline_hazard_ = pd.DataFrame(
|
|
804
|
+
baseline_hazard_ = pd.DataFrame(
|
|
805
|
+
np.zeros_like(unique_death_times).astype(float), index=unique_death_times, columns=["baseline hazard"]
|
|
806
|
+
)
|
|
805
807
|
|
|
806
808
|
for t in unique_death_times:
|
|
807
809
|
ix = (start.values < t) & (t <= stop.values)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
from typing import Callable, Iterator, List, Optional, Tuple, Union, Any, Iterable
|
|
4
4
|
from textwrap import dedent, fill
|
|
5
|
-
from datetime import datetime
|
|
5
|
+
from datetime import datetime, UTC
|
|
6
6
|
import warnings
|
|
7
7
|
import time
|
|
8
8
|
|
|
@@ -1214,7 +1214,7 @@ class SemiParametricPHFitter(ProportionalHazardMixin, SemiParametricRegressionFi
|
|
|
1214
1214
|
cph.predict_median(df)
|
|
1215
1215
|
|
|
1216
1216
|
"""
|
|
1217
|
-
self._time_fit_was_called = datetime.
|
|
1217
|
+
self._time_fit_was_called = datetime.now(UTC).strftime("%Y-%m-%d %H:%M:%S") + " UTC"
|
|
1218
1218
|
self.duration_col = duration_col
|
|
1219
1219
|
self.event_col = event_col
|
|
1220
1220
|
self.robust = robust
|
|
@@ -2693,9 +2693,13 @@ See https://stats.stackexchange.com/q/11109/11867 for more.\n",
|
|
|
2693
2693
|
if self.strata:
|
|
2694
2694
|
df = df.set_index(self.strata)
|
|
2695
2695
|
|
|
2696
|
-
df = df.sort_values([self.duration_col, self.event_col])
|
|
2696
|
+
df = df.sort_values([col for col in [self.duration_col, self.event_col] if (col is not None)])
|
|
2697
2697
|
T = df.pop(self.duration_col).astype(float)
|
|
2698
|
-
E =
|
|
2698
|
+
E = (
|
|
2699
|
+
df.pop(self.event_col)
|
|
2700
|
+
if (self.event_col is not None)
|
|
2701
|
+
else pd.Series(np.ones(len(df.index)), index=df.index, name="E")
|
|
2702
|
+
).astype(bool)
|
|
2699
2703
|
W = df.pop(self.weights_col) if self.weights_col else pd.Series(np.ones_like(E), index=T.index)
|
|
2700
2704
|
entries = df.pop(self.entry_col) if self.entry_col else None
|
|
2701
2705
|
|
|
@@ -540,7 +540,10 @@ def add_at_risk_counts(
|
|
|
540
540
|
event_table_slice.loc[:tick, ["at_risk", "censored", "observed"]]
|
|
541
541
|
.agg(
|
|
542
542
|
{
|
|
543
|
-
|
|
543
|
+
# `Series.tail(1).values` is a 1D array of length 1. In NumPy>=2.4,
|
|
544
|
+
# `int(np.array([1]))` raises `TypeError: only 0-dimensional arrays can be converted to Python scalars`.
|
|
545
|
+
# Extract a Python scalar for compatibility.
|
|
546
|
+
"at_risk": lambda x: x.tail(1).values.item(),
|
|
544
547
|
"censored": "sum",
|
|
545
548
|
"observed": "sum",
|
|
546
549
|
}
|
|
@@ -554,7 +557,7 @@ def add_at_risk_counts(
|
|
|
554
557
|
)
|
|
555
558
|
.fillna(0)
|
|
556
559
|
)
|
|
557
|
-
counts.extend([int(c) for c in event_table_slice.loc[rows_to_show]])
|
|
560
|
+
counts.extend([int(np.asarray(c).item()) for c in event_table_slice.loc[rows_to_show]])
|
|
558
561
|
else:
|
|
559
562
|
counts.extend([0 for _ in range(n_rows)])
|
|
560
563
|
if n_rows > 1:
|
|
@@ -114,9 +114,9 @@ class StatisticalResult:
|
|
|
114
114
|
self._print_specific_style(style, decimals=decimals, **kwargs)
|
|
115
115
|
else:
|
|
116
116
|
try:
|
|
117
|
-
from IPython.display import display
|
|
117
|
+
from IPython.display import HTML, display
|
|
118
118
|
|
|
119
|
-
display(self)
|
|
119
|
+
display(HTML(self.to_html(decimals=decimals, **kwargs)))
|
|
120
120
|
except ImportError:
|
|
121
121
|
self._ascii_print(decimals=decimals, **kwargs)
|
|
122
122
|
|
|
@@ -175,7 +175,6 @@ class StatisticalResult:
|
|
|
175
175
|
def to_ascii(self, decimals=2, **kwargs):
|
|
176
176
|
extra_kwargs = dict(list(self._kwargs.items()) + list(kwargs.items()))
|
|
177
177
|
meta_data = self._stringify_meta_data(extra_kwargs)
|
|
178
|
-
|
|
179
178
|
df = self.summary
|
|
180
179
|
|
|
181
180
|
s = "<lifelines.StatisticalResult: {0}>".format(self.test_name)
|
|
@@ -482,6 +482,7 @@ class TestUnivariateFitters:
|
|
|
482
482
|
assert not isinstance(fitter.predict(1), Iterable)
|
|
483
483
|
assert isinstance(fitter.predict([1, 2]), Iterable)
|
|
484
484
|
|
|
485
|
+
@flaky
|
|
485
486
|
def test_cumulative_density_ci_is_ordered_correctly(self, positive_sample_lifetimes, univariate_fitters):
|
|
486
487
|
T = positive_sample_lifetimes[0]
|
|
487
488
|
for f in univariate_fitters:
|
|
@@ -558,6 +559,7 @@ class TestUnivariateFitters:
|
|
|
558
559
|
fitter.fit(positive_sample_lifetimes[0], ci_labels=expected)
|
|
559
560
|
npt.assert_array_equal(fitter.confidence_interval_.columns, expected)
|
|
560
561
|
|
|
562
|
+
@flaky
|
|
561
563
|
def test_ci_is_not_all_nan(self, positive_sample_lifetimes, univariate_fitters):
|
|
562
564
|
for f in univariate_fitters:
|
|
563
565
|
fitter = f()
|
|
@@ -2119,6 +2121,25 @@ class TestRegressionFitters:
|
|
|
2119
2121
|
formula = "%s" % subset[-1]
|
|
2120
2122
|
fitter.fit(df[subset], duration_col="t", formula=formula)
|
|
2121
2123
|
|
|
2124
|
+
def test_categorical_prediction(self):
|
|
2125
|
+
df = pd.DataFrame.from_dict(
|
|
2126
|
+
{
|
|
2127
|
+
"t": [1.0, 5.0, 3.0, 4.0, 6.0, 1.0],
|
|
2128
|
+
"categoryb_": pd.Series(["a", "a", "b", "b", "c", "c"], dtype="category"),
|
|
2129
|
+
}
|
|
2130
|
+
)
|
|
2131
|
+
|
|
2132
|
+
df_to_predict = pd.DataFrame.from_dict(
|
|
2133
|
+
{
|
|
2134
|
+
"categoryb_": pd.Series(["a", "b", "c"], dtype="category"),
|
|
2135
|
+
}
|
|
2136
|
+
)
|
|
2137
|
+
|
|
2138
|
+
for fitter in [CoxPHFitter(), WeibullAFTFitter()]:
|
|
2139
|
+
formula = "categoryb_"
|
|
2140
|
+
fitter.fit(df, duration_col="t", formula=formula)
|
|
2141
|
+
fitter.predict_survival_function(df_to_predict)
|
|
2142
|
+
|
|
2122
2143
|
@pytest.mark.xfail
|
|
2123
2144
|
def test_regression_model_has_concordance_index_(self, regression_models, rossi):
|
|
2124
2145
|
|
|
@@ -44,6 +44,23 @@ def waltons():
|
|
|
44
44
|
return load_waltons()[["T", "E"]].iloc[:50]
|
|
45
45
|
|
|
46
46
|
|
|
47
|
+
def test_add_at_risk_counts_is_numpy_scalar_compatible():
|
|
48
|
+
# Regression test for https://github.com/CamDavidsonPilon/lifelines/issues/1671:
|
|
49
|
+
# `add_at_risk_counts` previously produced 1D NumPy arrays (length 1) for "At risk" counts,
|
|
50
|
+
# which breaks `int(c)` on NumPy>=2.4 (`TypeError: only 0-dimensional arrays can be converted to Python scalars`).
|
|
51
|
+
matplotlib = pytest.importorskip("matplotlib")
|
|
52
|
+
matplotlib.use("Agg", force=True)
|
|
53
|
+
from matplotlib import pyplot as plt
|
|
54
|
+
|
|
55
|
+
kmf = KaplanMeierFitter().fit(
|
|
56
|
+
np.random.exponential(10, size=(100,)),
|
|
57
|
+
np.random.binomial(1, 0.8, size=(100,)),
|
|
58
|
+
)
|
|
59
|
+
ax = kmf.plot_survival_function()
|
|
60
|
+
add_at_risk_counts(kmf, ax=ax)
|
|
61
|
+
plt.close(ax.figure)
|
|
62
|
+
|
|
63
|
+
|
|
47
64
|
@pytest.mark.skipif("DISPLAY" not in os.environ, reason="requires display")
|
|
48
65
|
class TestPlotting:
|
|
49
66
|
@pytest.fixture
|
|
@@ -561,3 +561,21 @@ def test_survival_difference_at_fixed_point_in_time_test_interval_censoring():
|
|
|
561
561
|
wf2 = WeibullFitter().fit_interval_censoring(2 * T, 2 * T)
|
|
562
562
|
result = stats.survival_difference_at_fixed_point_in_time_test(T.mean(), wf1, wf2)
|
|
563
563
|
assert result.p_value < 0.05
|
|
564
|
+
|
|
565
|
+
|
|
566
|
+
def test_statistical_result_has_correct_decimal():
|
|
567
|
+
import re
|
|
568
|
+
from lifelines.datasets import load_rossi
|
|
569
|
+
|
|
570
|
+
rossi = load_rossi()
|
|
571
|
+
|
|
572
|
+
results = stats.logrank_test(
|
|
573
|
+
durations_A=rossi.loc[rossi["fin"] == 0, "week"],
|
|
574
|
+
durations_B=rossi.loc[rossi["fin"] == 1, "week"],
|
|
575
|
+
event_observed_A=rossi.loc[rossi["fin"] == 0, "arrest"],
|
|
576
|
+
event_observed_B=rossi.loc[rossi["fin"] == 1, "arrest"],
|
|
577
|
+
)
|
|
578
|
+
|
|
579
|
+
output = results.to_ascii(decimals=10, test=1)
|
|
580
|
+
numbers = re.findall(r"\d+\.\d{10}\b", output)
|
|
581
|
+
assert len(numbers) >= 3
|
|
@@ -1016,3 +1016,43 @@ def test_safe_exp():
|
|
|
1016
1016
|
assert grad(safe_exp)(4.0) == np.exp(4.0)
|
|
1017
1017
|
assert grad(safe_exp)(MAX) == np.exp(MAX)
|
|
1018
1018
|
assert grad(safe_exp)(MAX + 1) == np.exp(MAX)
|
|
1019
|
+
|
|
1020
|
+
class TestAccumulators:
|
|
1021
|
+
EPSILON = 1e-10
|
|
1022
|
+
|
|
1023
|
+
@staticmethod
|
|
1024
|
+
def _check_equality(a: float, b: float) -> None:
|
|
1025
|
+
assert abs(a - b) < TestAccumulators.EPSILON
|
|
1026
|
+
|
|
1027
|
+
def test_linear_accumulator(self):
|
|
1028
|
+
function = utils.LinearAccumulator()
|
|
1029
|
+
TestAccumulators._check_equality(float(0), function.evaluate(0))
|
|
1030
|
+
TestAccumulators._check_equality(float(0), function.evaluate(1))
|
|
1031
|
+
function.add_linear_term(0, 1)
|
|
1032
|
+
TestAccumulators._check_equality(float(0), function.evaluate(0))
|
|
1033
|
+
TestAccumulators._check_equality(float(0), function.evaluate(1))
|
|
1034
|
+
function.add_linear_term(1, -1)
|
|
1035
|
+
TestAccumulators._check_equality(float(1), function.evaluate(0))
|
|
1036
|
+
TestAccumulators._check_equality(float(2), function.evaluate(1))
|
|
1037
|
+
function.add_linear_term(-1, 2)
|
|
1038
|
+
TestAccumulators._check_equality(float(3), function.evaluate(0))
|
|
1039
|
+
TestAccumulators._check_equality(float(3), function.evaluate(1))
|
|
1040
|
+
|
|
1041
|
+
def test_quadratic_accumulator(self):
|
|
1042
|
+
function = utils.QuadraticAccumulator()
|
|
1043
|
+
TestAccumulators._check_equality(float(0), function.evaluate(0))
|
|
1044
|
+
TestAccumulators._check_equality(float(0), function.evaluate(1))
|
|
1045
|
+
TestAccumulators._check_equality(float(0), function.evaluate(2))
|
|
1046
|
+
function.add_quadratic_term(0, 1)
|
|
1047
|
+
TestAccumulators._check_equality(float(0), function.evaluate(0))
|
|
1048
|
+
TestAccumulators._check_equality(float(0), function.evaluate(1))
|
|
1049
|
+
TestAccumulators._check_equality(float(0), function.evaluate(2))
|
|
1050
|
+
function.add_quadratic_term(1, 2)
|
|
1051
|
+
TestAccumulators._check_equality(float(4), function.evaluate(0))
|
|
1052
|
+
TestAccumulators._check_equality(float(1), function.evaluate(1))
|
|
1053
|
+
TestAccumulators._check_equality(float(0), function.evaluate(2))
|
|
1054
|
+
function.add_quadratic_term(-1, -1)
|
|
1055
|
+
TestAccumulators._check_equality(float(3), function.evaluate(0))
|
|
1056
|
+
TestAccumulators._check_equality(float(-3), function.evaluate(1))
|
|
1057
|
+
TestAccumulators._check_equality(float(-9), function.evaluate(2))
|
|
1058
|
+
|
|
@@ -1955,3 +1955,54 @@ class CovariateParameterMappings:
|
|
|
1955
1955
|
design_info = formulaic.ModelSpec.from_spec(formulaic.Formula(formula).get_model_matrix(df))
|
|
1956
1956
|
|
|
1957
1957
|
return design_info
|
|
1958
|
+
|
|
1959
|
+
class LinearAccumulator:
|
|
1960
|
+
"""
|
|
1961
|
+
This class represents a linear function F(x), which can be updated iteratively.
|
|
1962
|
+
"""
|
|
1963
|
+
def __init__(self):
|
|
1964
|
+
"""
|
|
1965
|
+
Initializes F(x) as 0.
|
|
1966
|
+
"""
|
|
1967
|
+
self.const_term: float = 0.0
|
|
1968
|
+
self.linear_term: float = 0.0
|
|
1969
|
+
|
|
1970
|
+
def add_linear_term(self, a: float, b: float) -> None:
|
|
1971
|
+
"""
|
|
1972
|
+
Adds a * (x - b) to F(x).
|
|
1973
|
+
"""
|
|
1974
|
+
self.const_term -= a * b
|
|
1975
|
+
self.linear_term += a
|
|
1976
|
+
|
|
1977
|
+
def evaluate(self, x: float) -> float:
|
|
1978
|
+
"""
|
|
1979
|
+
Evaluates F(x) at the given value of x.
|
|
1980
|
+
"""
|
|
1981
|
+
return self.const_term + self.linear_term * x
|
|
1982
|
+
|
|
1983
|
+
class QuadraticAccumulator:
|
|
1984
|
+
"""
|
|
1985
|
+
This class represents a quadratic function F(x), which can be updated iteratively.
|
|
1986
|
+
"""
|
|
1987
|
+
def __init__(self):
|
|
1988
|
+
"""
|
|
1989
|
+
Initializes F(x) as 0.
|
|
1990
|
+
"""
|
|
1991
|
+
self.const_term: float = 0.0
|
|
1992
|
+
self.linear_term: float = 0.0
|
|
1993
|
+
self.quadratic_term: float = 0.0
|
|
1994
|
+
|
|
1995
|
+
def add_quadratic_term(self, a: float, b: float) -> None:
|
|
1996
|
+
"""
|
|
1997
|
+
Adds a * (x - b)^2 to F(x).
|
|
1998
|
+
"""
|
|
1999
|
+
# a * (x - b)^2 = a * x^2 + (- 2 * a * b) * x + a * b^2
|
|
2000
|
+
self.const_term += a * (b ** 2)
|
|
2001
|
+
self.linear_term -= 2 * a * b
|
|
2002
|
+
self.quadratic_term += a
|
|
2003
|
+
|
|
2004
|
+
def evaluate(self, x: float) -> float:
|
|
2005
|
+
"""
|
|
2006
|
+
Evaluates F(x) at the given value of x.
|
|
2007
|
+
"""
|
|
2008
|
+
return self.const_term + self.linear_term * x + self.quadratic_term * (x ** 2)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: lifelines
|
|
3
|
-
Version: 0.30.
|
|
3
|
+
Version: 0.30.1
|
|
4
4
|
Summary: Survival analysis in Python, including Kaplan Meier, Nelson Aalen and regression
|
|
5
5
|
Home-page: https://github.com/CamDavidsonPilon/lifelines
|
|
6
6
|
Author: Cameron Davidson-Pilon
|
|
@@ -18,11 +18,22 @@ Description-Content-Type: text/markdown
|
|
|
18
18
|
License-File: LICENSE
|
|
19
19
|
Requires-Dist: numpy>=1.14.0
|
|
20
20
|
Requires-Dist: scipy>=1.7.0
|
|
21
|
-
Requires-Dist: pandas
|
|
21
|
+
Requires-Dist: pandas<3.0,>=2.1
|
|
22
22
|
Requires-Dist: matplotlib>=3.0
|
|
23
23
|
Requires-Dist: autograd>=1.5
|
|
24
24
|
Requires-Dist: autograd-gamma>=0.3
|
|
25
25
|
Requires-Dist: formulaic>=0.2.2
|
|
26
|
+
Dynamic: author
|
|
27
|
+
Dynamic: author-email
|
|
28
|
+
Dynamic: classifier
|
|
29
|
+
Dynamic: description
|
|
30
|
+
Dynamic: description-content-type
|
|
31
|
+
Dynamic: home-page
|
|
32
|
+
Dynamic: license
|
|
33
|
+
Dynamic: license-file
|
|
34
|
+
Dynamic: requires-dist
|
|
35
|
+
Dynamic: requires-python
|
|
36
|
+
Dynamic: summary
|
|
26
37
|
|
|
27
38
|

|
|
28
39
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lifelines-0.30.0 → lifelines-0.30.1}/lifelines/fitters/breslow_fleming_harrington_fitter.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lifelines-0.30.0 → lifelines-0.30.1}/lifelines/fitters/generalized_gamma_regression_fitter.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lifelines-0.30.0 → lifelines-0.30.1}/lifelines/fitters/piecewise_exponential_regression_fitter.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|