lifelines 0.28.0__tar.gz → 0.29.0__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.28.0/lifelines.egg-info → lifelines-0.29.0}/PKG-INFO +5 -5
- {lifelines-0.28.0 → lifelines-0.29.0}/README.md +2 -2
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/datasets/__init__.py +1 -1
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/fitters/__init__.py +6 -6
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/fitters/aalen_additive_fitter.py +2 -2
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/fitters/cox_time_varying_fitter.py +1 -1
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/fitters/coxph_fitter.py +6 -4
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/fitters/mixins.py +3 -3
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/fitters/nelson_aalen_fitter.py +1 -1
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/fitters/npmle.py +1 -1
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/fitters/piecewise_exponential_regression_fitter.py +1 -1
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/generate_datasets.py +6 -6
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/tests/test_estimation.py +10 -10
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/tests/test_plotting.py +2 -2
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/tests/utils/test_utils.py +1 -1
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/utils/__init__.py +8 -8
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/version.py +1 -1
- {lifelines-0.28.0 → lifelines-0.29.0/lifelines.egg-info}/PKG-INFO +5 -5
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines.egg-info/requires.txt +2 -2
- {lifelines-0.28.0 → lifelines-0.29.0}/reqs/base-requirements.txt +2 -2
- {lifelines-0.28.0 → lifelines-0.29.0}/reqs/dev-requirements.txt +1 -1
- {lifelines-0.28.0 → lifelines-0.29.0}/LICENSE +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/MANIFEST.in +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/__init__.py +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/calibration.py +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/datasets/CuZn-LeftCensoredDataset.csv +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/datasets/anderson.csv +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/datasets/c_botulinum_lag_phase.csv +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/datasets/canadian_senators.csv +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/datasets/dd.csv +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/datasets/dfcv_dataset.py +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/datasets/divorce.dat +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/datasets/g3.csv +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/datasets/gbsg2.csv +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/datasets/gehan.dat +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/datasets/holly_molly_polly.tsv +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/datasets/interval_diabetes.csv +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/datasets/kidney_transplant.csv +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/datasets/larynx.csv +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/datasets/lung.csv +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/datasets/lymph_node.csv +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/datasets/lymphoma.csv +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/datasets/merrell1955.csv +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/datasets/mice.csv +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/datasets/multicenter_aids_cohort.tsv +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/datasets/nh4.csv +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/datasets/panel_test.csv +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/datasets/psychiatric_patients.csv +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/datasets/recur.csv +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/datasets/regression.csv +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/datasets/rossi.csv +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/datasets/stanford_heart.csv +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/datasets/static_test.csv +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/datasets/waltons_dataset.csv +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/exceptions.py +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/fitters/aalen_johansen_fitter.py +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/fitters/breslow_fleming_harrington_fitter.py +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/fitters/crc_spline_fitter.py +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/fitters/exponential_fitter.py +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/fitters/generalized_gamma_fitter.py +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/fitters/generalized_gamma_regression_fitter.py +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/fitters/kaplan_meier_fitter.py +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/fitters/log_logistic_aft_fitter.py +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/fitters/log_logistic_fitter.py +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/fitters/log_normal_aft_fitter.py +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/fitters/log_normal_fitter.py +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/fitters/mixture_cure_fitter.py +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/fitters/piecewise_exponential_fitter.py +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/fitters/spline_fitter.py +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/fitters/weibull_aft_fitter.py +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/fitters/weibull_fitter.py +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/plotting.py +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/statistics.py +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/tests/__init__.py +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/tests/test_generate_datasets.py +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/tests/test_npmle.py +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/tests/test_statistics.py +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/tests/utils/test_btree.py +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/tests/utils/test_concordance.py +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/utils/btree.py +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/utils/concordance.py +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/utils/lowess.py +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/utils/printer.py +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines/utils/safe_exp.py +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines.egg-info/SOURCES.txt +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines.egg-info/dependency_links.txt +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/lifelines.egg-info/top_level.txt +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/reqs/docs-requirements.txt +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/setup.cfg +0 -0
- {lifelines-0.28.0 → lifelines-0.29.0}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: lifelines
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.29.0
|
|
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
|
|
@@ -17,8 +17,8 @@ Requires-Python: >=3.9
|
|
|
17
17
|
Description-Content-Type: text/markdown
|
|
18
18
|
License-File: LICENSE
|
|
19
19
|
Requires-Dist: numpy<2.0,>=1.14.0
|
|
20
|
-
Requires-Dist: scipy>=1.
|
|
21
|
-
Requires-Dist: pandas>=
|
|
20
|
+
Requires-Dist: scipy>=1.7.0
|
|
21
|
+
Requires-Dist: pandas>=2.1
|
|
22
22
|
Requires-Dist: matplotlib>=3.0
|
|
23
23
|
Requires-Dist: autograd>=1.5
|
|
24
24
|
Requires-Dist: autograd-gamma>=0.3
|
|
@@ -50,8 +50,8 @@ If you are new to survival analysis, wondering why it is useful, or are interest
|
|
|
50
50
|
|
|
51
51
|
## Contact
|
|
52
52
|
- Start a conversation in our [Discussions room](https://github.com/CamDavidsonPilon/lifelines/discussions).
|
|
53
|
-
- Some users have posted common questions at [stats.stackexchange.com](https://stats.stackexchange.com/search?tab=votes&q=%22lifelines%22%20is%3aquestion)
|
|
54
|
-
-
|
|
53
|
+
- Some users have posted common questions at [stats.stackexchange.com](https://stats.stackexchange.com/search?tab=votes&q=%22lifelines%22%20is%3aquestion).
|
|
54
|
+
- Creating an issue in the [Github repository](https://github.com/camdavidsonpilon/lifelines).
|
|
55
55
|
|
|
56
56
|
## Development
|
|
57
57
|
|
|
@@ -24,8 +24,8 @@ If you are new to survival analysis, wondering why it is useful, or are interest
|
|
|
24
24
|
|
|
25
25
|
## Contact
|
|
26
26
|
- Start a conversation in our [Discussions room](https://github.com/CamDavidsonPilon/lifelines/discussions).
|
|
27
|
-
- Some users have posted common questions at [stats.stackexchange.com](https://stats.stackexchange.com/search?tab=votes&q=%22lifelines%22%20is%3aquestion)
|
|
28
|
-
-
|
|
27
|
+
- Some users have posted common questions at [stats.stackexchange.com](https://stats.stackexchange.com/search?tab=votes&q=%22lifelines%22%20is%3aquestion).
|
|
28
|
+
- Creating an issue in the [Github repository](https://github.com/camdavidsonpilon/lifelines).
|
|
29
29
|
|
|
30
30
|
## Development
|
|
31
31
|
|
|
@@ -18,7 +18,7 @@ from autograd.misc import flatten
|
|
|
18
18
|
import autograd.numpy as anp
|
|
19
19
|
|
|
20
20
|
from scipy.optimize import minimize, root_scalar
|
|
21
|
-
from scipy.integrate import
|
|
21
|
+
from scipy.integrate import trapezoid
|
|
22
22
|
from scipy import stats
|
|
23
23
|
|
|
24
24
|
import pandas as pd
|
|
@@ -573,7 +573,7 @@ class ParametricUnivariateFitter(UnivariateFitter):
|
|
|
573
573
|
|
|
574
574
|
# convergence successful.
|
|
575
575
|
# I still need to check for ~np.isnan(minimizing_results.x).any() since minimize will happily
|
|
576
|
-
# return nans even when criteria is
|
|
576
|
+
# return nans even when criteria is satisfied.
|
|
577
577
|
if minimizing_results and minimizing_results.success and ~np.isnan(minimizing_results.x).any():
|
|
578
578
|
sol = utils._to_1d_array(minimizing_results.x)
|
|
579
579
|
# pylint: disable=no-value-for-parameter
|
|
@@ -876,7 +876,7 @@ class ParametricUnivariateFitter(UnivariateFitter):
|
|
|
876
876
|
length n, the end of the period the subject experienced the event in. If the value is equal to the corresponding value in lower_bound, then
|
|
877
877
|
the individual's event was observed (not censored).
|
|
878
878
|
event_observed: numpy array or pd.Series, optional
|
|
879
|
-
length n, if left optional, infer from ``lower_bound`` and ``
|
|
879
|
+
length n, if left optional, infer from ``lower_bound`` and ``upper_bound`` (if lower_bound==upper_bound then event observed, if lower_bound < upper_bound, then event censored)
|
|
880
880
|
timeline: list, optional
|
|
881
881
|
return the estimate at the values in timeline (positively increasing)
|
|
882
882
|
label: string, optional
|
|
@@ -1925,7 +1925,7 @@ class ParametricRegressionFitter(RegressionFitter):
|
|
|
1925
1925
|
def _fit_model(
|
|
1926
1926
|
self, likelihood, Ts, Xs, E, weights, entries, fit_options, show_progress=False, user_supplied_initial_point=None
|
|
1927
1927
|
):
|
|
1928
|
-
|
|
1928
|
+
initial_points_as_arrays, unflatten_array_to_dict = self._prepare_initial_points(
|
|
1929
1929
|
user_supplied_initial_point, Ts, E, entries, weights, Xs
|
|
1930
1930
|
)
|
|
1931
1931
|
|
|
@@ -1939,7 +1939,7 @@ class ParametricRegressionFitter(RegressionFitter):
|
|
|
1939
1939
|
|
|
1940
1940
|
minimum_ll = np.inf
|
|
1941
1941
|
minimum_results = None
|
|
1942
|
-
for _initial_point in
|
|
1942
|
+
for _initial_point in initial_points_as_arrays:
|
|
1943
1943
|
|
|
1944
1944
|
if _initial_point.shape[0] != Xs.columns.size:
|
|
1945
1945
|
raise ValueError("initial_point is not the correct shape.")
|
|
@@ -2507,7 +2507,7 @@ class ParametricRegressionFitter(RegressionFitter):
|
|
|
2507
2507
|
warnings.warn("""Approximating the expected value using trapezoid rule.\n""", exceptions.ApproximationWarning)
|
|
2508
2508
|
subjects = utils._get_index(X)
|
|
2509
2509
|
v = self.predict_survival_function(X, conditional_after=conditional_after)[subjects]
|
|
2510
|
-
return pd.Series(
|
|
2510
|
+
return pd.Series(trapezoid(v.values.T, v.index), index=subjects).squeeze()
|
|
2511
2511
|
|
|
2512
2512
|
@property
|
|
2513
2513
|
def median_survival_time_(self):
|
|
@@ -6,7 +6,7 @@ import time
|
|
|
6
6
|
import numpy as np
|
|
7
7
|
import pandas as pd
|
|
8
8
|
from numpy.linalg import LinAlgError
|
|
9
|
-
from scipy.integrate import
|
|
9
|
+
from scipy.integrate import trapezoid
|
|
10
10
|
|
|
11
11
|
from lifelines.fitters import RegressionFitter
|
|
12
12
|
from lifelines.utils.printer import Printer
|
|
@@ -396,7 +396,7 @@ It's important to know that the naive variance estimates of the coefficients are
|
|
|
396
396
|
"""
|
|
397
397
|
index = _get_index(X)
|
|
398
398
|
t = self._index
|
|
399
|
-
return pd.Series(
|
|
399
|
+
return pd.Series(trapezoid(self.predict_survival_function(X)[index].values.T, t), index=index)
|
|
400
400
|
|
|
401
401
|
def _compute_confidence_intervals(self):
|
|
402
402
|
ci = 100 * (1 - self.alpha)
|
|
@@ -801,7 +801,7 @@ 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(np.zeros_like(unique_death_times), index=unique_death_times, columns=["baseline hazard"])
|
|
804
|
+
baseline_hazard_ = pd.DataFrame(np.zeros_like(unique_death_times).astype(float), index=unique_death_times, columns=["baseline hazard"])
|
|
805
805
|
|
|
806
806
|
for t in unique_death_times:
|
|
807
807
|
ix = (start.values < t) & (t <= stop.values)
|
|
@@ -9,7 +9,7 @@ import time
|
|
|
9
9
|
from numpy import dot, einsum, log, exp, zeros, arange, multiply, ndarray
|
|
10
10
|
import numpy as np
|
|
11
11
|
from scipy.linalg import solve as spsolve, LinAlgError, norm, inv
|
|
12
|
-
from scipy.integrate import
|
|
12
|
+
from scipy.integrate import trapezoid
|
|
13
13
|
from scipy import stats
|
|
14
14
|
from pandas import DataFrame, Series, Index
|
|
15
15
|
import pandas as pd
|
|
@@ -2514,7 +2514,7 @@ See https://stats.stackexchange.com/q/11109/11867 for more.\n",
|
|
|
2514
2514
|
"""
|
|
2515
2515
|
subjects = utils._get_index(X)
|
|
2516
2516
|
v = self.predict_survival_function(X, conditional_after=conditional_after)[subjects]
|
|
2517
|
-
return pd.Series(
|
|
2517
|
+
return pd.Series(trapezoid(v.values.T, v.index), index=subjects)
|
|
2518
2518
|
|
|
2519
2519
|
def _compute_baseline_hazard(self, partial_hazards: DataFrame, name: Any) -> pd.DataFrame:
|
|
2520
2520
|
# https://stats.stackexchange.com/questions/46532/cox-baseline-hazard
|
|
@@ -3223,7 +3223,7 @@ class ParametricPiecewiseBaselinePHFitter(ParametricCoxModelFitter, Proportional
|
|
|
3223
3223
|
|
|
3224
3224
|
for stratum, stratified_X in df.groupby(self.strata):
|
|
3225
3225
|
log_lambdas_ = anp.array(
|
|
3226
|
-
[0] + [self.params_[self._strata_labeler(stratum, i)][0] for i in range(2, self.n_breakpoints + 2)]
|
|
3226
|
+
[0] + [self.params_.loc[self._strata_labeler(stratum, i)].iloc[0] for i in range(2, self.n_breakpoints + 2)]
|
|
3227
3227
|
)
|
|
3228
3228
|
lambdas_ = np.exp(log_lambdas_)
|
|
3229
3229
|
|
|
@@ -3237,7 +3237,9 @@ class ParametricPiecewiseBaselinePHFitter(ParametricCoxModelFitter, Proportional
|
|
|
3237
3237
|
return cumulative_hazard
|
|
3238
3238
|
|
|
3239
3239
|
else:
|
|
3240
|
-
log_lambdas_ = np.array(
|
|
3240
|
+
log_lambdas_ = np.array(
|
|
3241
|
+
[0] + [self.params_.loc[param].iloc[0] for param in self._fitted_parameter_names if param != "beta_"]
|
|
3242
|
+
)
|
|
3241
3243
|
lambdas_ = np.exp(log_lambdas_)
|
|
3242
3244
|
|
|
3243
3245
|
Xs = self.regressors.transform_df(df)
|
|
@@ -110,7 +110,7 @@ class ProportionalHazardMixin:
|
|
|
110
110
|
axes = []
|
|
111
111
|
|
|
112
112
|
for variable in self.params_.index.intersection(columns or self.params_.index):
|
|
113
|
-
|
|
113
|
+
minimum_observed_p_value = test_results.summary.loc[variable, "p"].min()
|
|
114
114
|
|
|
115
115
|
# plot is done (regardless of test result) whenever `show_plots = True`
|
|
116
116
|
if show_plots:
|
|
@@ -154,7 +154,7 @@ class ProportionalHazardMixin:
|
|
|
154
154
|
plt.tight_layout()
|
|
155
155
|
plt.subplots_adjust(top=0.90)
|
|
156
156
|
|
|
157
|
-
if np.round(
|
|
157
|
+
if np.round(minimum_observed_p_value, 2) > p_value_threshold:
|
|
158
158
|
continue
|
|
159
159
|
|
|
160
160
|
counter += 1
|
|
@@ -182,7 +182,7 @@ class ProportionalHazardMixin:
|
|
|
182
182
|
print()
|
|
183
183
|
print(
|
|
184
184
|
"%d. Variable '%s' failed the non-proportional test: p-value is %s."
|
|
185
|
-
% (counter, variable, format_p_value(4)(
|
|
185
|
+
% (counter, variable, format_p_value(4)(minimum_observed_p_value)),
|
|
186
186
|
end="\n\n",
|
|
187
187
|
)
|
|
188
188
|
|
|
@@ -291,7 +291,7 @@ def reconstruct_survival_function(
|
|
|
291
291
|
|
|
292
292
|
# First backfill at events between known observations
|
|
293
293
|
# Second fill all events _outside_ known obs with running_sum
|
|
294
|
-
return full_dataframe.combine_first(df).bfill().fillna(running_sum).clip(lower=0.0)
|
|
294
|
+
return full_dataframe.combine_first(df).astype(float).bfill().fillna(running_sum).clip(lower=0.0)
|
|
295
295
|
|
|
296
296
|
|
|
297
297
|
def npmle_compute_confidence_intervals(left, right, mle_, alpha=0.05, samples=1000):
|
{lifelines-0.28.0 → lifelines-0.29.0}/lifelines/fitters/piecewise_exponential_regression_fitter.py
RENAMED
|
@@ -66,7 +66,7 @@ class PiecewiseExponentialRegressionFitter(ParametricRegressionFitter):
|
|
|
66
66
|
coef_penalty = 0
|
|
67
67
|
if self.penalizer > 0:
|
|
68
68
|
for i in range(params_stacked.shape[1]):
|
|
69
|
-
if not self._cols_to_not_penalize[i]:
|
|
69
|
+
if not self._cols_to_not_penalize.iloc[i]:
|
|
70
70
|
coef_penalty = coef_penalty + (params_stacked[:, i]).var()
|
|
71
71
|
|
|
72
72
|
return neg_ll + self.penalizer * coef_penalty
|
|
@@ -5,7 +5,7 @@ import pandas as pd
|
|
|
5
5
|
|
|
6
6
|
from scipy import stats
|
|
7
7
|
from scipy.optimize import newton
|
|
8
|
-
from scipy.integrate import
|
|
8
|
+
from scipy.integrate import cumulative_trapezoid
|
|
9
9
|
|
|
10
10
|
random = np.random
|
|
11
11
|
|
|
@@ -172,7 +172,7 @@ def constant_coefficients(d, timelines, constant=True, independent=0):
|
|
|
172
172
|
timelines: the observational times
|
|
173
173
|
constant: True for constant coefficients
|
|
174
174
|
independent: the number of coffients to set to 0 (covariate is ind of survival), or
|
|
175
|
-
a list of covariates to make
|
|
175
|
+
a list of covariates to make independent.
|
|
176
176
|
|
|
177
177
|
returns a matrix (t,d+1) of coefficients
|
|
178
178
|
"""
|
|
@@ -187,7 +187,7 @@ def time_varying_coefficients(d, timelines, constant=False, independent=0, randg
|
|
|
187
187
|
timelines: the observational times
|
|
188
188
|
constant: True for constant coefficients
|
|
189
189
|
independent: the number of coffients to set to 0 (covariate is ind of survival), or
|
|
190
|
-
a list of covariates to make
|
|
190
|
+
a list of covariates to make independent.
|
|
191
191
|
randgen: how scalar coefficients (betas) are sampled.
|
|
192
192
|
|
|
193
193
|
returns a matrix (t,d+1) of coefficients
|
|
@@ -221,7 +221,7 @@ def generate_hazard_rates(n, d, timelines, constant=False, independent=0, n_bina
|
|
|
221
221
|
n: the number of instances
|
|
222
222
|
d: the number of covariates
|
|
223
223
|
lifelines: the observational times
|
|
224
|
-
constant: make the
|
|
224
|
+
constant: make the coefficients constant (not time dependent)
|
|
225
225
|
n_binary: the number of binary covariates
|
|
226
226
|
model: from ["aalen", "cox"]
|
|
227
227
|
|
|
@@ -253,7 +253,7 @@ def generate_random_lifetimes(hazard_rates, timelines, size=1, censor=None):
|
|
|
253
253
|
timelines: (t,) the observation times
|
|
254
254
|
size: the number to return, per hardard rate
|
|
255
255
|
censor: If True, adds uniform censoring between timelines.max() and 0
|
|
256
|
-
If a
|
|
256
|
+
If a positive number, censors all events above that value.
|
|
257
257
|
If (n,) np.array >=0 , censor elementwise.
|
|
258
258
|
|
|
259
259
|
|
|
@@ -308,7 +308,7 @@ def cumulative_integral(fx, x):
|
|
|
308
308
|
fx: (n,d) numpy array, what you want to integral of
|
|
309
309
|
x: (n,) numpy array, location to integrate over.
|
|
310
310
|
"""
|
|
311
|
-
return
|
|
311
|
+
return cumulative_trapezoid(fx.T, x, initial=0).T
|
|
312
312
|
|
|
313
313
|
|
|
314
314
|
def construct_survival_curves(hazard_rates, timelines):
|
|
@@ -2008,7 +2008,7 @@ class TestRegressionFitters:
|
|
|
2008
2008
|
def test_fit_will_accept_object_dtype_as_event_col(self, regression_models_sans_strata_model, rossi):
|
|
2009
2009
|
# issue #638
|
|
2010
2010
|
rossi["arrest"] = rossi["arrest"].astype(object)
|
|
2011
|
-
rossi["arrest"]
|
|
2011
|
+
rossi.loc[0, "arrest"] = None
|
|
2012
2012
|
|
|
2013
2013
|
assert rossi["arrest"].dtype == object
|
|
2014
2014
|
rossi = rossi.dropna()
|
|
@@ -2953,7 +2953,7 @@ class TestCoxPHFitter_SemiParametric:
|
|
|
2953
2953
|
assert_frame_equal(cph.baseline_cumulative_hazard_, cph_scaled.baseline_cumulative_hazard_)
|
|
2954
2954
|
|
|
2955
2955
|
|
|
2956
|
-
class
|
|
2956
|
+
class TestCoxPHFitterPieces:
|
|
2957
2957
|
@pytest.fixture
|
|
2958
2958
|
def cph(self):
|
|
2959
2959
|
return CoxPHFitter(baseline_estimation_method="piecewise", breakpoints=[25])
|
|
@@ -3108,7 +3108,7 @@ class TestCoxPHFitter:
|
|
|
3108
3108
|
cph.fit(df, "T", formula="C(cat)")
|
|
3109
3109
|
assert cph.summary.shape[0] == 2
|
|
3110
3110
|
|
|
3111
|
-
def
|
|
3111
|
+
def test_trivial_entry_col(self, rossi):
|
|
3112
3112
|
cph_without_entry_summary = CoxPHFitter().fit(rossi, "week", "arrest").summary
|
|
3113
3113
|
cphs_without_entry_summary = (
|
|
3114
3114
|
CoxPHFitter(baseline_estimation_method="spline", n_baseline_knots=2).fit(rossi, "week", "arrest").summary
|
|
@@ -3125,7 +3125,7 @@ class TestCoxPHFitter:
|
|
|
3125
3125
|
assert_frame_equal(cph_without_entry_summary, cph_with_entry_summary)
|
|
3126
3126
|
assert_frame_equal(cphs_without_entry_summary, cphs_with_entry_summary)
|
|
3127
3127
|
|
|
3128
|
-
def
|
|
3128
|
+
def test_trivial_entry_col_with_strata(self, rossi):
|
|
3129
3129
|
cph_without_entry_summary = CoxPHFitter().fit(rossi, "week", "arrest", strata=["fin"]).summary
|
|
3130
3130
|
|
|
3131
3131
|
rossi["entry"] = 0
|
|
@@ -3466,7 +3466,7 @@ class TestCoxPHFitter:
|
|
|
3466
3466
|
cph.fit(rossi, "week", "arrest")
|
|
3467
3467
|
cph.check_assumptions(rossi)
|
|
3468
3468
|
|
|
3469
|
-
def
|
|
3469
|
+
def test_check_assumptions_throws_if_raise_on_fail_enabled(self, cph, rossi):
|
|
3470
3470
|
cph.fit(rossi, "week", "arrest")
|
|
3471
3471
|
with pytest.raises(ProportionalHazardAssumptionError):
|
|
3472
3472
|
cph.check_assumptions(rossi, p_value_threshold=0.05, raise_on_fail=True)
|
|
@@ -4169,7 +4169,7 @@ log-likelihood ratio test = 33.27 on 7 df
|
|
|
4169
4169
|
expected = pd.Series({"var": 0.643})
|
|
4170
4170
|
assert_series_equal(cph.summary["se(coef)"], expected, atol=0.01, check_names=False)
|
|
4171
4171
|
|
|
4172
|
-
def
|
|
4172
|
+
def test_robust_errors_with_less_trivial_weights_is_the_same_as_R(self, regression_dataset):
|
|
4173
4173
|
"""
|
|
4174
4174
|
df <- data.frame(
|
|
4175
4175
|
"var1" = c(0.209325, 0.693919, 0.443804, 0.065636, 0.386294),
|
|
@@ -4343,7 +4343,7 @@ log-likelihood ratio test = 33.27 on 7 df
|
|
|
4343
4343
|
expected = 0.05
|
|
4344
4344
|
assert abs(cph.log_likelihood_ratio_test().test_statistic - expected) < 0.01
|
|
4345
4345
|
|
|
4346
|
-
def
|
|
4346
|
+
def test_trivial_float_weights_with_no_ties_is_the_same_as_R(self, regression_dataset):
|
|
4347
4347
|
"""
|
|
4348
4348
|
df <- data.frame(
|
|
4349
4349
|
"var1" = c(0.209325, 0.693919, 0.443804, 0.065636, 0.386294),
|
|
@@ -4374,7 +4374,7 @@ log-likelihood ratio test = 33.27 on 7 df
|
|
|
4374
4374
|
expected_ll = -1.142397
|
|
4375
4375
|
assert abs(cph.log_likelihood_ - expected_ll) < 0.001
|
|
4376
4376
|
|
|
4377
|
-
def
|
|
4377
|
+
def test_less_trivial_float_weights_with_no_ties_is_the_same_as_R(self, regression_dataset):
|
|
4378
4378
|
"""
|
|
4379
4379
|
df <- data.frame(
|
|
4380
4380
|
"var1" = c(0.209325, 0.693919, 0.443804, 0.065636, 0.386294),
|
|
@@ -4402,7 +4402,7 @@ log-likelihood ratio test = 33.27 on 7 df
|
|
|
4402
4402
|
expected = pd.Series({"var1": 6.690, "var2": 1.614})
|
|
4403
4403
|
assert_series_equal(cph.summary["se(coef)"], expected, atol=0.01, check_names=False)
|
|
4404
4404
|
|
|
4405
|
-
def
|
|
4405
|
+
def test_non_trivial_float_weights_with_no_ties_is_the_same_as_R(self, regression_dataset):
|
|
4406
4406
|
"""
|
|
4407
4407
|
df <- read.csv('.../lifelines/datasets/regression.csv')
|
|
4408
4408
|
coxph(formula=Surv(T, E) ~ var1 + var2, data=df, weights=var3)
|
|
@@ -5588,7 +5588,7 @@ log-likelihood ratio test = 15.11 on 4 df
|
|
|
5588
5588
|
ctv.fit(heart, id_col="id", event_col="event", strata=["transplant"], initial_point=0.1 * np.ones(3))
|
|
5589
5589
|
npt.assert_allclose(ctv.log_likelihood_ratio_test().test_statistic, 15.68, atol=0.01)
|
|
5590
5590
|
|
|
5591
|
-
def
|
|
5591
|
+
def test_fitter_is_okay_with_trivial_df(self, ctv):
|
|
5592
5592
|
# after all the necessary columns are removed, does this fitter still work with a trivial df?
|
|
5593
5593
|
df = pd.DataFrame.from_records(
|
|
5594
5594
|
[
|
|
@@ -360,12 +360,12 @@ class TestPlotting:
|
|
|
360
360
|
self.plt.show(block=block)
|
|
361
361
|
return
|
|
362
362
|
|
|
363
|
-
def
|
|
363
|
+
def test_naf_plot_cumulative_hazard_bandwidth_1(self, block):
|
|
364
364
|
data1 = np.random.exponential(5, size=(2000, 1)) ** 2
|
|
365
365
|
naf = NelsonAalenFitter()
|
|
366
366
|
naf.fit(data1)
|
|
367
367
|
naf.plot_hazard(bandwidth=5.0, iloc=slice(0, 1700))
|
|
368
|
-
self.plt.title("
|
|
368
|
+
self.plt.title("test_naf_plot_cumulative_hazard_bandwidth_1")
|
|
369
369
|
self.plt.show(block=block)
|
|
370
370
|
return
|
|
371
371
|
|
|
@@ -347,7 +347,7 @@ def test_survival_table_from_events_at_risk_column():
|
|
|
347
347
|
1.0,
|
|
348
348
|
]
|
|
349
349
|
df = utils.survival_table_from_events(df["T"], df["E"])
|
|
350
|
-
assert list(df["at_risk"][1:]) == expected # skip the first event as that is the birth time, 0.
|
|
350
|
+
assert list(df["at_risk"].loc[1:]) == expected # skip the first event as that is the birth time, 0.
|
|
351
351
|
|
|
352
352
|
|
|
353
353
|
def test_survival_table_to_events_casts_to_float():
|
|
@@ -11,7 +11,7 @@ import warnings
|
|
|
11
11
|
from numpy import ndarray
|
|
12
12
|
import numpy as np
|
|
13
13
|
|
|
14
|
-
from scipy.integrate import quad,
|
|
14
|
+
from scipy.integrate import quad, trapezoid
|
|
15
15
|
from scipy.linalg import solve
|
|
16
16
|
from scipy import stats
|
|
17
17
|
|
|
@@ -266,7 +266,7 @@ def _expected_value_of_survival_up_to_t(model_or_survival_function, t: float = n
|
|
|
266
266
|
)
|
|
267
267
|
sf = model_or_survival_function.loc[:t]
|
|
268
268
|
sf = pd.concat((sf, pd.DataFrame([1], index=[0], columns=sf.columns))).sort_index()
|
|
269
|
-
return
|
|
269
|
+
return trapezoid(y=sf.values[:, 0], x=sf.index)
|
|
270
270
|
elif isinstance(model_or_survival_function, lifelines.fitters.UnivariateFitter):
|
|
271
271
|
# lifelines model
|
|
272
272
|
model = model_or_survival_function
|
|
@@ -313,7 +313,7 @@ def _expected_value_of_survival_squared_up_to_t(
|
|
|
313
313
|
sf = model_or_survival_function.loc[:t]
|
|
314
314
|
sf = pd.concat((sf, pd.DataFrame([1], index=[0], columns=sf.columns))).sort_index()
|
|
315
315
|
sf_tau = sf * sf.index.values[:, None]
|
|
316
|
-
return 2 *
|
|
316
|
+
return 2 * trapezoid(y=sf_tau.values[:, 0], x=sf_tau.index)
|
|
317
317
|
elif isinstance(model_or_survival_function, lifelines.fitters.UnivariateFitter):
|
|
318
318
|
# lifelines model
|
|
319
319
|
model = model_or_survival_function
|
|
@@ -556,7 +556,7 @@ def _group_event_table_by_intervals(event_table, intervals) -> pd.DataFrame:
|
|
|
556
556
|
|
|
557
557
|
intervals = np.arange(0, event_max + bin_width, bin_width)
|
|
558
558
|
|
|
559
|
-
event_table = event_table.groupby(pd.cut(event_table["event_at"], intervals, include_lowest=True)).agg(
|
|
559
|
+
event_table = event_table.groupby(pd.cut(event_table["event_at"], intervals, include_lowest=True), observed=False).agg(
|
|
560
560
|
{"removed": ["sum"], "observed": ["sum"], "censored": ["sum"], "at_risk": ["max"]}
|
|
561
561
|
)
|
|
562
562
|
# convert columns from multiindex
|
|
@@ -648,7 +648,7 @@ def datetimes_to_durations(
|
|
|
648
648
|
the units of time to use. See Pandas 'freq'. Default 'D' for days.
|
|
649
649
|
dayfirst: bool, optional (default=False)
|
|
650
650
|
see Pandas `to_datetime`
|
|
651
|
-
na_values : list, optional
|
|
651
|
+
na_values : list[str], optional
|
|
652
652
|
list of values to recognize as NA/NaN. Ex: ['', 'NaT']
|
|
653
653
|
format:
|
|
654
654
|
see Pandas `to_datetime`
|
|
@@ -679,7 +679,7 @@ def datetimes_to_durations(
|
|
|
679
679
|
start_times = pd.Series(start_times).copy()
|
|
680
680
|
end_times = pd.Series(end_times).copy()
|
|
681
681
|
|
|
682
|
-
C = ~(pd.isnull(end_times).values | end_times.isin(na_values or [""]))
|
|
682
|
+
C = ~(pd.isnull(end_times).values | end_times.astype(str).isin(na_values or [""]))
|
|
683
683
|
end_times[~C] = fill_date_
|
|
684
684
|
start_times_ = pd.to_datetime(start_times, dayfirst=dayfirst, format=format)
|
|
685
685
|
end_times_ = pd.to_datetime(end_times, dayfirst=dayfirst, errors="coerce", format=format)
|
|
@@ -1464,7 +1464,7 @@ def add_covariate_to_timeline(
|
|
|
1464
1464
|
cv = cv.sort_values([id_col, duration_col])
|
|
1465
1465
|
cvs = cv.pipe(remove_redundant_rows).pipe(transform_cv_to_long_format).groupby(id_col, sort=True)
|
|
1466
1466
|
|
|
1467
|
-
long_form_df = long_form_df.groupby(id_col, group_keys=False, sort=True).apply(expand, cvs=cvs)
|
|
1467
|
+
long_form_df = long_form_df.groupby(id_col, group_keys=False, sort=True)[long_form_df.columns].apply(expand, cvs=cvs)
|
|
1468
1468
|
return long_form_df.reset_index(drop=True)
|
|
1469
1469
|
|
|
1470
1470
|
|
|
@@ -1506,7 +1506,7 @@ def covariates_from_event_matrix(df, id_col) -> pd.DataFrame:
|
|
|
1506
1506
|
"""
|
|
1507
1507
|
df = df.set_index(id_col)
|
|
1508
1508
|
df = df.fillna(np.inf)
|
|
1509
|
-
df = df.stack(
|
|
1509
|
+
df = df.stack(future_stack=True).reset_index()
|
|
1510
1510
|
df.columns = [id_col, "event", "duration"]
|
|
1511
1511
|
df["_counter"] = 1
|
|
1512
1512
|
return (
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: lifelines
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.29.0
|
|
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
|
|
@@ -17,8 +17,8 @@ Requires-Python: >=3.9
|
|
|
17
17
|
Description-Content-Type: text/markdown
|
|
18
18
|
License-File: LICENSE
|
|
19
19
|
Requires-Dist: numpy<2.0,>=1.14.0
|
|
20
|
-
Requires-Dist: scipy>=1.
|
|
21
|
-
Requires-Dist: pandas>=
|
|
20
|
+
Requires-Dist: scipy>=1.7.0
|
|
21
|
+
Requires-Dist: pandas>=2.1
|
|
22
22
|
Requires-Dist: matplotlib>=3.0
|
|
23
23
|
Requires-Dist: autograd>=1.5
|
|
24
24
|
Requires-Dist: autograd-gamma>=0.3
|
|
@@ -50,8 +50,8 @@ If you are new to survival analysis, wondering why it is useful, or are interest
|
|
|
50
50
|
|
|
51
51
|
## Contact
|
|
52
52
|
- Start a conversation in our [Discussions room](https://github.com/CamDavidsonPilon/lifelines/discussions).
|
|
53
|
-
- Some users have posted common questions at [stats.stackexchange.com](https://stats.stackexchange.com/search?tab=votes&q=%22lifelines%22%20is%3aquestion)
|
|
54
|
-
-
|
|
53
|
+
- Some users have posted common questions at [stats.stackexchange.com](https://stats.stackexchange.com/search?tab=votes&q=%22lifelines%22%20is%3aquestion).
|
|
54
|
+
- Creating an issue in the [Github repository](https://github.com/camdavidsonpilon/lifelines).
|
|
55
55
|
|
|
56
56
|
## Development
|
|
57
57
|
|
|
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.28.0 → lifelines-0.29.0}/lifelines/fitters/breslow_fleming_harrington_fitter.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lifelines-0.28.0 → lifelines-0.29.0}/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
|
|
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
|