lifelines 0.27.8__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.
Files changed (93) hide show
  1. {lifelines-0.27.8/lifelines.egg-info → lifelines-0.29.0}/PKG-INFO +11 -6
  2. {lifelines-0.27.8 → lifelines-0.29.0}/README.md +2 -2
  3. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/datasets/__init__.py +1 -1
  4. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/exceptions.py +4 -0
  5. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/fitters/__init__.py +10 -8
  6. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/fitters/aalen_additive_fitter.py +2 -2
  7. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/fitters/breslow_fleming_harrington_fitter.py +9 -1
  8. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/fitters/cox_time_varying_fitter.py +1 -1
  9. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/fitters/coxph_fitter.py +7 -5
  10. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/fitters/generalized_gamma_fitter.py +6 -5
  11. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/fitters/kaplan_meier_fitter.py +9 -3
  12. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/fitters/mixins.py +11 -6
  13. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/fitters/nelson_aalen_fitter.py +3 -3
  14. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/fitters/npmle.py +1 -1
  15. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/fitters/piecewise_exponential_regression_fitter.py +1 -1
  16. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/generate_datasets.py +6 -6
  17. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/tests/test_estimation.py +86 -19
  18. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/tests/test_plotting.py +2 -2
  19. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/tests/utils/test_utils.py +16 -119
  20. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/utils/__init__.py +11 -13
  21. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/version.py +1 -1
  22. {lifelines-0.27.8 → lifelines-0.29.0/lifelines.egg-info}/PKG-INFO +11 -6
  23. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines.egg-info/SOURCES.txt +1 -3
  24. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines.egg-info/requires.txt +2 -2
  25. {lifelines-0.27.8 → lifelines-0.29.0}/reqs/base-requirements.txt +2 -2
  26. {lifelines-0.27.8 → lifelines-0.29.0}/reqs/dev-requirements.txt +1 -1
  27. lifelines-0.29.0/reqs/docs-requirements.txt +7 -0
  28. {lifelines-0.27.8 → lifelines-0.29.0}/setup.py +1 -3
  29. lifelines-0.27.8/lifelines/utils/sklearn_adapter.py +0 -135
  30. lifelines-0.27.8/reqs/docs-requirements.txt +0 -7
  31. lifelines-0.27.8/reqs/travis-requirements.txt +0 -5
  32. {lifelines-0.27.8 → lifelines-0.29.0}/LICENSE +0 -0
  33. {lifelines-0.27.8 → lifelines-0.29.0}/MANIFEST.in +0 -0
  34. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/__init__.py +0 -0
  35. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/calibration.py +0 -0
  36. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/datasets/CuZn-LeftCensoredDataset.csv +0 -0
  37. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/datasets/anderson.csv +0 -0
  38. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/datasets/c_botulinum_lag_phase.csv +0 -0
  39. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/datasets/canadian_senators.csv +0 -0
  40. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/datasets/dd.csv +0 -0
  41. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/datasets/dfcv_dataset.py +0 -0
  42. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/datasets/divorce.dat +0 -0
  43. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/datasets/g3.csv +0 -0
  44. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/datasets/gbsg2.csv +0 -0
  45. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/datasets/gehan.dat +0 -0
  46. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/datasets/holly_molly_polly.tsv +0 -0
  47. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/datasets/interval_diabetes.csv +0 -0
  48. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/datasets/kidney_transplant.csv +0 -0
  49. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/datasets/larynx.csv +0 -0
  50. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/datasets/lung.csv +0 -0
  51. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/datasets/lymph_node.csv +0 -0
  52. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/datasets/lymphoma.csv +0 -0
  53. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/datasets/merrell1955.csv +0 -0
  54. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/datasets/mice.csv +0 -0
  55. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/datasets/multicenter_aids_cohort.tsv +0 -0
  56. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/datasets/nh4.csv +0 -0
  57. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/datasets/panel_test.csv +0 -0
  58. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/datasets/psychiatric_patients.csv +0 -0
  59. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/datasets/recur.csv +0 -0
  60. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/datasets/regression.csv +0 -0
  61. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/datasets/rossi.csv +0 -0
  62. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/datasets/stanford_heart.csv +0 -0
  63. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/datasets/static_test.csv +0 -0
  64. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/datasets/waltons_dataset.csv +0 -0
  65. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/fitters/aalen_johansen_fitter.py +0 -0
  66. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/fitters/crc_spline_fitter.py +0 -0
  67. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/fitters/exponential_fitter.py +0 -0
  68. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/fitters/generalized_gamma_regression_fitter.py +0 -0
  69. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/fitters/log_logistic_aft_fitter.py +0 -0
  70. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/fitters/log_logistic_fitter.py +0 -0
  71. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/fitters/log_normal_aft_fitter.py +0 -0
  72. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/fitters/log_normal_fitter.py +0 -0
  73. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/fitters/mixture_cure_fitter.py +0 -0
  74. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/fitters/piecewise_exponential_fitter.py +0 -0
  75. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/fitters/spline_fitter.py +0 -0
  76. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/fitters/weibull_aft_fitter.py +0 -0
  77. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/fitters/weibull_fitter.py +0 -0
  78. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/plotting.py +0 -0
  79. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/statistics.py +0 -0
  80. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/tests/__init__.py +0 -0
  81. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/tests/test_generate_datasets.py +0 -0
  82. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/tests/test_npmle.py +0 -0
  83. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/tests/test_statistics.py +0 -0
  84. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/tests/utils/test_btree.py +0 -0
  85. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/tests/utils/test_concordance.py +0 -0
  86. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/utils/btree.py +0 -0
  87. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/utils/concordance.py +0 -0
  88. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/utils/lowess.py +0 -0
  89. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/utils/printer.py +0 -0
  90. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines/utils/safe_exp.py +0 -0
  91. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines.egg-info/dependency_links.txt +0 -0
  92. {lifelines-0.27.8 → lifelines-0.29.0}/lifelines.egg-info/top_level.txt +0 -0
  93. {lifelines-0.27.8 → lifelines-0.29.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lifelines
3
- Version: 0.27.8
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
@@ -9,15 +9,20 @@ License: MIT
9
9
  Classifier: Development Status :: 4 - Beta
10
10
  Classifier: License :: OSI Approved :: MIT License
11
11
  Classifier: Programming Language :: Python
12
- Classifier: Programming Language :: Python :: 3.7
13
- Classifier: Programming Language :: Python :: 3.8
14
12
  Classifier: Programming Language :: Python :: 3.9
15
13
  Classifier: Programming Language :: Python :: 3.10
16
14
  Classifier: Programming Language :: Python :: 3.11
17
15
  Classifier: Topic :: Scientific/Engineering
18
- Requires-Python: >=3.7
16
+ Requires-Python: >=3.9
19
17
  Description-Content-Type: text/markdown
20
18
  License-File: LICENSE
19
+ Requires-Dist: numpy<2.0,>=1.14.0
20
+ Requires-Dist: scipy>=1.7.0
21
+ Requires-Dist: pandas>=2.1
22
+ Requires-Dist: matplotlib>=3.0
23
+ Requires-Dist: autograd>=1.5
24
+ Requires-Dist: autograd-gamma>=0.3
25
+ Requires-Dist: formulaic>=0.2.2
21
26
 
22
27
  ![](http://i.imgur.com/EOowdSD.png)
23
28
 
@@ -45,8 +50,8 @@ If you are new to survival analysis, wondering why it is useful, or are interest
45
50
 
46
51
  ## Contact
47
52
  - Start a conversation in our [Discussions room](https://github.com/CamDavidsonPilon/lifelines/discussions).
48
- - Some users have posted common questions at [stats.stackexchange.com](https://stats.stackexchange.com/search?tab=votes&q=%22lifelines%22%20is%3aquestion)
49
- - creating an issue in the [Github repository](https://github.com/camdavidsonpilon/lifelines).
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).
50
55
 
51
56
  ## Development
52
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
- - creating an issue in the [Github repository](https://github.com/camdavidsonpilon/lifelines).
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
 
@@ -54,7 +54,7 @@ def load_multicenter_aids_cohort_study(**kwargs):
54
54
  """
55
55
  Originally in [1]::
56
56
 
57
- Siz: (78, 4)
57
+ Size: (78, 4)
58
58
 
59
59
  AIDSY: date of AIDS diagnosis
60
60
  W: years from AIDS diagnosis to study entry
@@ -5,6 +5,10 @@ class StatError(Exception):
5
5
  pass
6
6
 
7
7
 
8
+ class ProportionalHazardAssumptionError(Exception):
9
+ pass
10
+
11
+
8
12
  class ConvergenceError(ValueError):
9
13
  # inherits from ValueError for backwards compatibility reasons
10
14
  def __init__(self, msg, original_exception=""):
@@ -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 trapz
21
+ from scipy.integrate import trapezoid
22
22
  from scipy import stats
23
23
 
24
24
  import pandas as pd
@@ -550,7 +550,7 @@ class ParametricUnivariateFitter(UnivariateFitter):
550
550
  minimizing_results, previous_results, minimizing_ll = None, None, np.inf
551
551
  for method, option in zip(
552
552
  ["Nelder-Mead", self._scipy_fit_method],
553
- [{"maxiter": 100}, {**{"disp": show_progress}, **self._scipy_fit_options, **fit_options}],
553
+ [{"maxiter": 400}, {**{"disp": show_progress}, **self._scipy_fit_options, **fit_options}],
554
554
  ):
555
555
 
556
556
  initial_value = self._initial_values if previous_results is None else utils._to_1d_array(previous_results.x)
@@ -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 satisified.
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 ``upper_cound`` (if lower_bound==upper_bound then event observed, if lower_bound < upper_bound, then event censored)
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
@@ -1409,7 +1409,7 @@ class ParametricRegressionFitter(RegressionFitter):
1409
1409
  def _survival_function(self, params, T, Xs):
1410
1410
  return anp.clip(anp.exp(-self._cumulative_hazard(params, T, Xs)), 1e-12, 1 - 1e-12)
1411
1411
 
1412
- def _log_likelihood_right_censoring(self, params, Ts, E, W, entries, Xs) -> float:
1412
+ def _log_likelihood_right_censoring(self, params, Ts: tuple, E, W, entries, Xs) -> float:
1413
1413
 
1414
1414
  T = Ts[0]
1415
1415
  non_zero_entries = entries > 0
@@ -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
- inital_points_as_arrays, unflatten_array_to_dict = self._prepare_initial_points(
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 inital_points_as_arrays:
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(trapz(v.values.T, v.index), index=subjects).squeeze()
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):
@@ -3365,6 +3365,8 @@ class ParametericAFTRegressionFitter(ParametricRegressionFitter):
3365
3365
  also display the baseline survival, defined as the survival at the mean of the original dataset.
3366
3366
  times: iterable
3367
3367
  pass in a times to plot
3368
+ y: str
3369
+ one of "survival_function", "hazard", "cumulative_hazard". Default "survival_function"
3368
3370
  kwargs:
3369
3371
  pass in additional plotting commands
3370
3372
 
@@ -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 trapz
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(trapz(self.predict_survival_function(X)[index].values.T, t), index=index)
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)
@@ -72,7 +72,14 @@ class BreslowFlemingHarringtonFitter(NonParametricUnivariateFitter):
72
72
  alpha = coalesce(alpha, self.alpha)
73
73
 
74
74
  naf = NelsonAalenFitter(alpha=alpha)
75
- naf.fit(durations, event_observed=event_observed, timeline=timeline, label=self._label, entry=entry, ci_labels=ci_labels)
75
+ naf.fit(
76
+ durations,
77
+ event_observed=event_observed,
78
+ timeline=timeline,
79
+ label=self._label,
80
+ entry=entry,
81
+ ci_labels=ci_labels,
82
+ )
76
83
  self.durations, self.event_observed, self.timeline, self.entry, self.event_table, self.weights = (
77
84
  naf.durations,
78
85
  naf.event_observed,
@@ -87,6 +94,7 @@ class BreslowFlemingHarringtonFitter(NonParametricUnivariateFitter):
87
94
  self.confidence_interval_ = np.exp(-naf.confidence_interval_)
88
95
  self.confidence_interval_survival_function_ = self.confidence_interval_
89
96
  self.confidence_interval_cumulative_density = 1 - self.confidence_interval_
97
+ self.confidence_interval_cumulative_density[:] = np.fliplr(self.confidence_interval_cumulative_density.values)
90
98
 
91
99
  # estimation methods
92
100
  self._estimation_method = "survival_function_"
@@ -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 trapz
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
@@ -80,7 +80,7 @@ class CoxPHFitter(RegressionFitter, ProportionalHazardMixin):
80
80
  When ``baseline_estimation_method="spline"``, this allows customizing the points in the time axis for the baseline hazard curve.
81
81
  To use evenly-spaced points in time, the ``n_baseline_knots`` parameter can be employed instead.
82
82
 
83
- breakpoints: int
83
+ breakpoints: list, optional
84
84
  Used when ``baseline_estimation_method="piecewise"``. Set the positions of the baseline hazard breakpoints.
85
85
 
86
86
  Examples
@@ -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(trapz(v.values.T, v.index), index=subjects)
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([0] + [self.params_[param][0] for param in self._fitted_parameter_names if param != "beta_"])
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)
@@ -105,6 +105,7 @@ class GeneralizedGammaFitter(KnownModelParametricUnivariateFitter):
105
105
  """
106
106
 
107
107
  _scipy_fit_method = "SLSQP"
108
+ _scipy_fit_options = {"maxiter": 10_000, "maxfev": 10_000}
108
109
  _fitted_parameter_names = ["mu_", "ln_sigma_", "lambda_"]
109
110
  _bounds = [(None, None), (None, None), (None, None)]
110
111
  _compare_to_values = np.array([0.0, 0.0, 1.0])
@@ -117,14 +118,14 @@ class GeneralizedGammaFitter(KnownModelParametricUnivariateFitter):
117
118
  elif CensoringType.is_interval_censoring(self):
118
119
  # this fails if Ts[1] == Ts[0], so we add a some fudge factors.
119
120
  log_data = log(Ts[1] - Ts[0] + 0.1)
120
- return np.array([log_data.mean(), log(log_data.std() + 0.01), 0.1])
121
+ return np.array([log_data.mean() * 1.5, log(log_data.std() + 0.1), 1.0])
121
122
 
122
123
  def _cumulative_hazard(self, params, times):
123
124
  mu_, ln_sigma_, lambda_ = params
124
125
 
125
126
  sigma_ = safe_exp(ln_sigma_)
126
127
  Z = (log(times) - mu_) / sigma_
127
- ilambda_2 = 1 / lambda_ ** 2
128
+ ilambda_2 = 1 / lambda_**2
128
129
  clipped_exp = np.clip(safe_exp(lambda_ * Z) * ilambda_2, 1e-300, 1e20)
129
130
 
130
131
  if lambda_ > 0:
@@ -137,7 +138,7 @@ class GeneralizedGammaFitter(KnownModelParametricUnivariateFitter):
137
138
 
138
139
  def _log_hazard(self, params, times):
139
140
  mu_, ln_sigma_, lambda_ = params
140
- ilambda_2 = 1 / lambda_ ** 2
141
+ ilambda_2 = 1 / lambda_**2
141
142
  Z = (log(times) - mu_) / safe_exp(ln_sigma_)
142
143
  clipped_exp = np.clip(safe_exp(lambda_ * Z) * ilambda_2, 1e-300, 1e20)
143
144
  if lambda_ > 0:
@@ -171,5 +172,5 @@ class GeneralizedGammaFitter(KnownModelParametricUnivariateFitter):
171
172
  sigma_ = exp(self.ln_sigma_)
172
173
 
173
174
  if lambda_ > 0:
174
- return exp(sigma_ * log(gammainccinv(1 / lambda_ ** 2, p) * lambda_ ** 2) / lambda_) * exp(self.mu_)
175
- return exp(sigma_ * log(gammaincinv(1 / lambda_ ** 2, p) * lambda_ ** 2) / lambda_) * exp(self.mu_)
175
+ return exp(sigma_ * log(gammainccinv(1 / lambda_**2, p) * lambda_**2) / lambda_) * exp(self.mu_)
176
+ return exp(sigma_ * log(gammaincinv(1 / lambda_**2, p) * lambda_**2) / lambda_) * exp(self.mu_)
@@ -351,9 +351,14 @@ class KaplanMeierFitter(NonParametricUnivariateFitter):
351
351
  primary_estimate_name = "survival_function_"
352
352
  secondary_estimate_name = "cumulative_density_"
353
353
 
354
- (self.durations, self.event_observed, self.timeline, self.entry, self.event_table, self.weights) = _preprocess_inputs(
355
- durations, event_observed, timeline, entry, weights
356
- )
354
+ (
355
+ self.durations,
356
+ self.event_observed,
357
+ self.timeline,
358
+ self.entry,
359
+ self.event_table,
360
+ self.weights,
361
+ ) = _preprocess_inputs(durations, event_observed, timeline, entry, weights)
357
362
 
358
363
  alpha = alpha if alpha else self.alpha
359
364
  log_estimate, cumulative_sq_ = _additive_estimate(
@@ -386,6 +391,7 @@ class KaplanMeierFitter(NonParametricUnivariateFitter):
386
391
 
387
392
  self.confidence_interval_survival_function_ = self.confidence_interval_
388
393
  self.confidence_interval_cumulative_density_ = 1 - self.confidence_interval_
394
+ self.confidence_interval_cumulative_density_[:] = np.fliplr(self.confidence_interval_cumulative_density_.values)
389
395
  self._median = median_survival_times(self.survival_function_)
390
396
  self._cumulative_sq_ = cumulative_sq_
391
397
 
@@ -4,6 +4,7 @@ from textwrap import dedent, fill
4
4
  from autograd import numpy as anp
5
5
  import numpy as np
6
6
  from pandas import DataFrame, Series
7
+ from lifelines.exceptions import ProportionalHazardAssumptionError
7
8
  from lifelines.statistics import proportional_hazard_test, TimeTransformers
8
9
  from lifelines.utils import format_p_value
9
10
  from lifelines.utils.lowess import lowess
@@ -28,6 +29,7 @@ class ProportionalHazardMixin:
28
29
  p_value_threshold: float = 0.01,
29
30
  plot_n_bootstraps: int = 15,
30
31
  columns: Optional[List[str]] = None,
32
+ raise_on_fail: bool = False,
31
33
  ) -> None:
32
34
  """
33
35
  Use this function to test the proportional hazards assumption. See usage example at
@@ -51,6 +53,8 @@ class ProportionalHazardMixin:
51
53
  the function significantly.
52
54
  columns: list, optional
53
55
  specify a subset of columns to test.
56
+ raise_on_fail: bool, optional
57
+ throw a ``ProportionalHazardAssumptionError`` if the test fails. Default: False.
54
58
 
55
59
  Returns
56
60
  --------
@@ -106,8 +110,8 @@ class ProportionalHazardMixin:
106
110
  axes = []
107
111
 
108
112
  for variable in self.params_.index.intersection(columns or self.params_.index):
109
- minumum_observed_p_value = test_results.summary.loc[variable, "p"].min()
110
-
113
+ minimum_observed_p_value = test_results.summary.loc[variable, "p"].min()
114
+
111
115
  # plot is done (regardless of test result) whenever `show_plots = True`
112
116
  if show_plots:
113
117
  axes.append([])
@@ -150,7 +154,7 @@ class ProportionalHazardMixin:
150
154
  plt.tight_layout()
151
155
  plt.subplots_adjust(top=0.90)
152
156
 
153
- if np.round(minumum_observed_p_value, 2) > p_value_threshold:
157
+ if np.round(minimum_observed_p_value, 2) > p_value_threshold:
154
158
  continue
155
159
 
156
160
  counter += 1
@@ -178,7 +182,7 @@ class ProportionalHazardMixin:
178
182
  print()
179
183
  print(
180
184
  "%d. Variable '%s' failed the non-proportional test: p-value is %s."
181
- % (counter, variable, format_p_value(4)(minumum_observed_p_value)),
185
+ % (counter, variable, format_p_value(4)(minimum_observed_p_value)),
182
186
  end="\n\n",
183
187
  )
184
188
 
@@ -224,9 +228,8 @@ class ProportionalHazardMixin:
224
228
  ),
225
229
  end="\n\n",
226
230
  )
227
- #################
231
+ #################
228
232
 
229
-
230
233
  if advice and counter > 0:
231
234
  print(
232
235
  dedent(
@@ -243,6 +246,8 @@ class ProportionalHazardMixin:
243
246
 
244
247
  if counter == 0:
245
248
  print("Proportional hazard assumption looks okay.")
249
+ elif raise_on_fail:
250
+ raise ProportionalHazardAssumptionError()
246
251
  return axes
247
252
 
248
253
  @property
@@ -183,7 +183,7 @@ class NelsonAalenFitter(UnivariateFitter):
183
183
  )
184
184
 
185
185
  def _variance_f_discrete(self, population, deaths):
186
- return (population - deaths) * deaths / population ** 3
186
+ return (1 - deaths / population) * (deaths / population) * (1.0 / population)
187
187
 
188
188
  def _additive_f_smooth(self, population, deaths):
189
189
  cum_ = np.cumsum(1.0 / np.arange(1, np.max(population) + 1))
@@ -200,7 +200,7 @@ class NelsonAalenFitter(UnivariateFitter):
200
200
  Parameters
201
201
  -----------
202
202
  bandwidth: float
203
- the bandwith used in the Epanechnikov kernel.
203
+ the bandwidth used in the Epanechnikov kernel.
204
204
 
205
205
  Returns
206
206
  -------
@@ -239,7 +239,7 @@ class NelsonAalenFitter(UnivariateFitter):
239
239
  C = var_hazard_.values != 0.0 # only consider the points with jumps
240
240
  std_hazard_ = np.sqrt(
241
241
  1.0
242
- / (bandwidth ** 2)
242
+ / (bandwidth**2)
243
243
  * np.dot(epanechnikov_kernel(timeline[:, None], timeline[C][None, :], bandwidth) ** 2, var_hazard_.values[C])
244
244
  )
245
245
  values = {
@@ -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):
@@ -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 cumtrapz
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 indepent.
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 indepent.
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 coeffients constant (not time dependent)
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 postive number, censors all events above that value.
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 cumtrapz(fx.T, x, initial=0).T
311
+ return cumulative_trapezoid(fx.T, x, initial=0).T
312
312
 
313
313
 
314
314
  def construct_survival_curves(hazard_rates, timelines):