statspai.survival¶
survival ¶
Survival and duration analysis models.
CausalSurvivalForestResult ¶
Bases: GRFFamilyForest
A fitted causal survival forest.
Attributes:
| Name | Type | Description |
|---|---|---|
ate |
float
|
Doubly-robust average effect (RMST difference or survival-
probability difference at |
ate_rmst |
float
|
Same as |
se, ci, pvalue |
(float, tuple, float)
|
|
cate |
ndarray
|
Out-of-bag |
cate_variance |
ndarray or None
|
|
horizon |
float
|
|
target |
str
|
|
n_obs, n_trees |
int
|
|
detail |
dict
|
|
Examples:
>>> import numpy as np, statspai as sp
>>> rng = np.random.default_rng(0)
>>> X = rng.normal(size=(600, 2)); W = rng.binomial(1, 0.5, 600)
>>> T = rng.exponential(1 / np.exp(0.5 * X[:, 0] - 0.5 * W))
>>> C = rng.exponential(3, 600)
>>> fit = sp.causal_survival_forest(time=np.minimum(T, C),
... event=(T <= C).astype(int), treat=W,
... covariates=X, horizon=1.0,
... n_estimators=200)
>>> fit.horizon
1.0
CumIncResult
dataclass
¶
Bases: ResultProtocolMixin
Cumulative incidence functions for competing risks.
Attributes:
| Name | Type | Description |
|---|---|---|
cif_table |
DataFrame
|
Long table with columns |
causes |
list
|
The competing-cause labels (excluding the |
gray_test |
dict or None
|
|
alpha |
float
|
Significance level used for the confidence bands. |
Examples:
>>> import statspai as sp
>>> import numpy as np, pandas as pd
>>> rng = np.random.default_rng(0)
>>> n = 200
>>> t1 = rng.exponential(scale=1.0, size=n)
>>> t2 = rng.exponential(scale=1.5, size=n)
>>> cens = rng.exponential(scale=2.0, size=n)
>>> time = np.minimum(np.minimum(t1, t2), cens)
>>> status = np.where((t1 <= t2) & (t1 <= cens), 1,
... np.where((t2 < t1) & (t2 <= cens), 2, 0))
>>> df = pd.DataFrame({"time": time, "status": status})
>>> ci = sp.cuminc(df, duration="time", event="status")
>>> isinstance(ci, sp.CumIncResult)
True
>>> ci.causes
[1, 2]
>>> at1 = ci.cif_at(1.0, cause=1) # CIF for cause 1 at time t=1
>>> bool((at1["cif"] >= 0).all())
True
FineGrayResult
dataclass
¶
Bases: ResultProtocolMixin
Fine-Gray proportional subdistribution hazards model result.
Attributes:
| Name | Type | Description |
|---|---|---|
params |
ndarray
|
Estimated coefficients (log subdistribution hazard ratios). |
bse |
ndarray
|
Standard errors (Fine-Gray sandwich by default; see |
covariates |
list of str
|
Covariate names aligned with |
cause |
int
|
The cause of interest whose subdistribution was modelled. |
n_obs, n_events |
int
|
Sample size and number of cause-of-interest events. |
vcov |
ndarray
|
Covariance matrix behind |
vce |
str
|
|
Examples:
>>> import statspai as sp
>>> import numpy as np, pandas as pd
>>> rng = np.random.default_rng(0)
>>> n = 300
>>> x = rng.normal(size=n)
>>> t1 = rng.exponential(scale=np.exp(-0.5 * x))
>>> t2 = rng.exponential(scale=1.5)
>>> cens = rng.exponential(scale=2.0)
>>> time = np.minimum(np.minimum(t1, t2), cens)
>>> status = np.where((t1 <= t2) & (t1 <= cens), 1,
... np.where((t2 < t1) & (t2 <= cens), 2, 0))
>>> df = pd.DataFrame({"time": time, "status": status, "x": x})
>>> res = sp.finegray(df, duration="time", event="status", x=["x"])
>>> isinstance(res, sp.FineGrayResult)
True
>>> res.covariates
['x']
>>> res.shr.shape # one subdistribution hazard ratio per covariate
(1,)
FrailtyResult
dataclass
¶
Bases: ResultProtocolMixin
lr_theta0
property
¶
Likelihood-ratio test of θ = 0: (chibar2, p).
The null sits on the boundary, so the p-value is half a chi2(1)
tail (Stata's chibar2(01)).
CoxResult ¶
Bases: EconometricResults
Result from Cox proportional hazards estimation.
Extends EconometricResults with survival-specific methods:
.plot(), .ph_test(), .baseline_hazard(), .concordance.
Examples:
>>> import statspai as sp
>>> import numpy as np, pandas as pd
>>> rng = np.random.default_rng(0)
>>> n = 120
>>> x = rng.normal(size=n)
>>> time = rng.exponential(scale=np.exp(-0.5 * x))
>>> event = (rng.random(n) < 0.8).astype(int)
>>> df = pd.DataFrame({"time": time, "status": event, "x": x})
>>> res = sp.cox(data=df, duration="time", event="status", x=["x"])
>>> type(res).__name__
'CoxResult'
>>> list(res.params.index)
['x']
>>> bool(isinstance(float(res.concordance), float))
True
>>> list(res.baseline_hazard().columns)
['time', 'baseline_cumhaz', 'baseline_survival']
>>> isinstance(res.summary(), str)
True
baseline_hazard ¶
Baseline cumulative hazard (Breslow estimator).
Returns:
| Type | Description |
|---|---|
DataFrame
|
Columns |
ph_test ¶
Test the proportional hazards assumption (Grambsch and Therneau).
For each covariate the model is embedded in one whose coefficient
varies with time, beta_j(t) = beta_j + theta_j * g(t), and
theta_j = 0 is tested by a score test at the fitted model; the
GLOBAL row tests all the theta_j jointly. No larger model
is fitted. This is the test of survival::cox.zph in R (version
3.0 and later), and the statistics agree with it under either tie
rule and with strata.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
transform
|
(km, rank, identity, log)
|
The function of time |
'km'
|
method
|
(score, approx)
|
|
'score'
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
One row per covariate and a final |
Notes
Weighted fits (weights=) and counting-process fits (start=)
get the score test of cox.zph for such fits: the case weights
enter the score and the information as they enter the partial
likelihood, the information is the model-based one whatever
variance the fit reports, and g ignores the weights ('km'
is the unweighted Kaplan-Meier of the (start, stop] data,
'rank' ranks the stop times of all rows). Fits with
design= are refused.
The two methods differ most when the covariate distribution of the risk set changes over time, which is when the average information matrix is a poor stand-in for each event time's own.
Before 1.39 this method returned n * rho^2 with rho the
Spearman correlation between time and the unscaled residuals. That
number is not the Grambsch-Therneau statistic and was several times
too small in places.
Examples:
>>> import numpy as np, pandas as pd
>>> import statspai as sp
>>> rng = np.random.default_rng(0)
>>> n = 400
>>> x = rng.binomial(1, 0.5, n)
>>> t = rng.exponential(1.0, n) ** np.where(x == 1, 2.0, 1.0)
>>> df = pd.DataFrame({"t": t, "d": 1, "x": x})
>>> fit = sp.cox(data=df, duration="t", event="d", x=["x"])
>>> bool(fit.ph_test().loc[0, "p_value"] < 0.01)
True
References
[@grambsch1994proportional]
plot ¶
Plot survival-related curves.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
kind
|
str
|
|
'survival'
|
ax
|
Axes
|
|
None
|
**kwargs
|
Any
|
|
{}
|
KMResult ¶
Bases: ResultProtocolMixin
Kaplan-Meier survival analysis result.
Attributes:
| Name | Type | Description |
|---|---|---|
survival_table |
DataFrame
|
Life table with |
median_survival |
float or dict
|
Median survival time (per group if groups present). |
Examples:
>>> import statspai as sp
>>> import numpy as np, pandas as pd
>>> rng = np.random.default_rng(0)
>>> n = 120
>>> df = pd.DataFrame({
... "time": rng.exponential(10, n).round(2),
... "status": (rng.random(n) < 0.7).astype(int),
... })
>>> km = sp.kaplan_meier(data=df, duration="time", event="status",
... conf_type="log-log")
>>> type(km).__name__
'KMResult'
>>> "survival" in km.survival_table.columns
True
>>> bool(0.0 <= km.survival_table["survival"].iloc[-1] <= 1.0)
True
>>> isinstance(km.summary(), str)
True
median_survival
property
¶
Median survival time (scalar or dict by group).
The first time at which S(t) <= 0.5, as Stata stci reports
it. When S(t) equals 0.5 exactly on a step, R survfit
reports the midpoint between that time and the next event time
instead (deaths at 1, 2, 3, 4: Stata 2, R 2.5).
plot ¶
Plot Kaplan-Meier survival curves with confidence bands.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ax
|
Axes
|
|
None
|
causal_survival_forest ¶
causal_survival_forest(data: Optional[DataFrame] = None, time: Any = None, event: Any = None, treat: Any = None, covariates: Any = None, horizon: Optional[float] = None, *, target: str = 'RMST', failure_times: Any = None, clusters: Any = None, weights: Any = None, equalize_cluster_weights: bool = False, W_hat: Any = None, n_estimators: int = 2000, min_samples_leaf: int = 5, max_samples: float = 0.5, mtry: Optional[int] = None, honest: bool = True, honesty_fraction: float = 0.5, honesty_prune_leaves: bool = True, split_alpha: float = 0.05, imbalance_penalty: float = 0.0, stabilize_splits: bool = True, ci_group_size: int = 2, max_depth: Optional[int] = None, propensity_bounds: Optional[Sequence[float]] = None, random_state: Optional[int] = 42, n_jobs: int = 1, alpha: float = 0.05) -> CausalSurvivalForestResult
Causal survival forest: heterogeneous effects on censored survival.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
DataFrame
|
Input data; when omitted, the other inputs are arrays. |
None
|
time
|
str or array - like
|
Observed time |
None
|
event
|
str or array - like
|
1 = event observed, 0 = censored. |
None
|
treat
|
str or array - like
|
Binary treatment. |
None
|
covariates
|
str, list of str or 2-D array
|
Covariates |
None
|
horizon
|
float
|
|
None
|
target
|
('RMST', 'survival_probability')
|
|
"RMST"
|
failure_times
|
array - like
|
Grid for the survival nuisance forests (default: observed times). |
None
|
clusters
|
Any
|
As in :func: |
None
|
weights
|
Any
|
As in :func: |
None
|
equalize_cluster_weights
|
Any
|
As in :func: |
None
|
W_hat
|
array - like
|
Precomputed propensities; default out-of-bag regression forest. |
None
|
n_estimators
|
int
|
|
2000
|
min_samples_leaf
|
int
|
|
5
|
max_samples
|
float
|
|
0.5
|
mtry
|
float
|
|
0.5
|
honest
|
float
|
|
0.5
|
honesty_fraction
|
float
|
|
0.5
|
honesty_prune_leaves
|
float
|
|
0.5
|
imbalance_penalty
|
float
|
Forest options (grf names). |
0.0
|
stabilize_splits
|
float
|
Forest options (grf names). |
0.0
|
ci_group_size
|
float
|
Forest options (grf names). |
0.0
|
max_depth
|
float
|
Forest options (grf names). |
0.0
|
split_alpha
|
float
|
grf |
0.05
|
propensity_bounds
|
(float, float)
|
Clip |
None
|
random_state
|
int
|
|
42
|
n_jobs
|
int
|
|
1
|
alpha
|
float
|
Significance level of the reported interval. |
0.05
|
Returns:
| Type | Description |
|---|---|
CausalSurvivalForestResult
|
|
Examples:
>>> import numpy as np, pandas as pd
>>> import statspai as sp
>>> rng = np.random.default_rng(0)
>>> n = 600; x = rng.normal(size=(n, 2)); w = rng.binomial(1, 0.5, n)
>>> T = rng.exponential(1 / np.exp(0.5 * x[:, 0] - 0.5 * w))
>>> C = rng.exponential(3, n)
>>> df = pd.DataFrame({"t": np.minimum(T, C), "d": (T <= C).astype(int),
... "w": w, "x1": x[:, 0], "x2": x[:, 1]})
>>> csf = sp.causal_survival_forest(df, time="t", event="d", treat="w",
... covariates=["x1", "x2"], horizon=1.0,
... n_estimators=200)
>>> csf.target
'RMST'
References
[@cui2023estimating], [@athey2019generalized]
cuminc ¶
cuminc(data: DataFrame, duration: str, event: str, group: Optional[str] = None, alpha: float = 0.05, variance: str = 'delta', conf_type: str = 'linear', rho: float = 0.0) -> CumIncResult
Cumulative incidence functions for competing risks (Aalen-Johansen).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
DataFrame
|
Input data. |
required |
duration
|
str
|
Column name for the follow-up time. |
required |
event
|
str
|
Column name for the event indicator. |
required |
group
|
str
|
Column name for a grouping variable. When supplied, CIFs are estimated per group and Gray's K-sample test is reported per cause. |
None
|
alpha
|
float
|
Significance level for the confidence bands. |
0.05
|
variance
|
('delta', 'gray')
|
Pointwise variance of the CIF. |
"delta"
|
conf_type
|
('linear', 'log-log')
|
|
"linear"
|
rho
|
float
|
Power of the |
0
|
Returns:
| Type | Description |
|---|---|
CumIncResult
|
With |
Notes
The cumulative incidence for a single cause is not 1 - KM applied to
that cause; treating competing events as censoring over-states the risk.
The Aalen-Johansen estimator weights each cause-specific increment by the
overall (all-cause) survival probability, so the CIFs of all causes plus
the overall survival sum to one at every time.
Examples:
>>> import statspai as sp
>>> import numpy as np, pandas as pd
>>> rng = np.random.default_rng(0)
>>> n = 200
>>> arm = rng.integers(0, 2, n)
>>> t1 = rng.exponential(scale=1.0 + 0.5 * arm, size=n) # cause 1
>>> t2 = rng.exponential(scale=1.5, size=n) # cause 2
>>> cens = rng.exponential(scale=2.0, size=n)
>>> time = np.minimum(np.minimum(t1, t2), cens)
>>> status = np.where((t1 <= t2) & (t1 <= cens), 1,
... np.where((t2 < t1) & (t2 <= cens), 2, 0))
>>> df = pd.DataFrame({"time": time, "status": status, "arm": arm})
>>> ci = sp.cuminc(df, duration="time", event="status", group="arm")
>>> isinstance(ci, sp.CumIncResult)
True
>>> ci.causes
[1, 2]
>>> ci.summary()
>>> ci.plot(cause=1)
finegray ¶
finegray(data: DataFrame, duration: str, event: str, x: Sequence[str], cause: int = 1, alpha: float = 0.05, max_iter: int = 50, tol: float = 1e-10, vce: str = 'robust', small_sample: bool = False, weights: Optional[str] = None) -> FineGrayResult
Fine & Gray (1999) proportional subdistribution hazards model.
Models the effect of covariates on the cumulative incidence of cause
through its subdistribution hazard, so coefficients exponentiate to
subdistribution hazard ratios that map monotonically to the CIF
(unlike cause-specific Cox coefficients).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
DataFrame
|
Input data. |
required |
duration
|
str
|
Follow-up-time column. |
required |
event
|
str
|
Event indicator: |
required |
x
|
sequence of str
|
Covariate column names. |
required |
cause
|
int
|
Cause of interest (default |
1
|
alpha
|
float
|
Significance level for confidence intervals. |
0.05
|
max_iter
|
(int, float)
|
Newton-Raphson controls ( |
50
|
tol
|
(int, float)
|
Newton-Raphson controls ( |
50
|
vce
|
('robust', 'model')
|
|
"robust"
|
small_sample
|
bool
|
Multiply the robust variance by |
False
|
weights
|
str
|
Column of sampling weights (strictly positive), Stata |
None
|
Returns:
| Type | Description |
|---|---|
FineGrayResult
|
With |
Notes
Subjects who fail from a competing cause are retained in the risk set with
time-decaying inverse-probability-of-censoring weights
w_i(t) = Ĝ(t-) / Ĝ(T_i-) (Ĝ = KM estimate of the censoring survival,
evaluated at left limits). The weighted partial likelihood is maximised
by Newton-Raphson with the Breslow tie approximation. Coefficients,
both variances and the log pseudo-likelihood match R cmprsk::crr;
with small_sample=True the standard errors match Stata stcrreg.
Examples:
>>> import statspai as sp
>>> import numpy as np, pandas as pd
>>> rng = np.random.default_rng(0)
>>> n = 300
>>> x = rng.normal(size=n)
>>> t1 = rng.exponential(scale=np.exp(-0.5 * x)) # cause of interest
>>> t2 = rng.exponential(scale=1.5, size=n) # competing cause
>>> cens = rng.exponential(scale=2.0, size=n)
>>> time = np.minimum(np.minimum(t1, t2), cens)
>>> status = np.where((t1 <= t2) & (t1 <= cens), 1,
... np.where((t2 < t1) & (t2 <= cens), 2, 0))
>>> df = pd.DataFrame({"time": time, "status": status, "x": x})
>>> res = sp.finegray(
... df, duration="time", event="status", x=["x"], cause=1
... )
>>> res.cause
1
>>> res.tidy()["term"].tolist()
['x']
>>> bool(res.shr[0] > 0) # subdistribution hazard ratio
True
References
fine1999proportional
cox_frailty ¶
cox_frailty(formula: str, data: DataFrame, cluster: str, alpha: float = 0.05, maxiter: int = 50, tol: float = 1e-10, theta: Optional[float] = None, ties: str = 'efron') -> FrailtyResult
Cox proportional hazards with shared gamma frailty.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
formula
|
str
|
|
required |
data
|
DataFrame
|
|
required |
cluster
|
str
|
Column identifying clusters (e.g. hospital, site). |
required |
alpha
|
float
|
Significance level (kept for API symmetry; intervals are built by the result's accessors). |
0.05
|
maxiter
|
(int, float)
|
Newton-Raphson controls for the penalised fit at each θ. |
50
|
tol
|
(int, float)
|
Newton-Raphson controls for the penalised fit at each θ. |
50
|
theta
|
float
|
Fix the frailty variance instead of estimating it (R
|
None
|
ties
|
('efron', 'breslow')
|
Tie handling in the partial likelihood (R's default is Efron,
Stata's |
"efron"
|
Returns:
| Type | Description |
|---|---|
FrailtyResult
|
|
Notes
θ is the maximiser of the integrated likelihood. At a fixed θ the
coefficients, standard errors and log-frailties are those of Stata
stcox, shared(id) and of R coxph(... + frailty(id,
theta=, sparse=FALSE)). R's default sparse=TRUE approximates the
frailty block of the information by its diagonal, which moves the
standard errors in the fourth digit, and its default method="em"
stops the θ search on a 1e-5 relative change in the likelihood, so its
reported θ is not the exact maximiser.
Examples:
>>> import numpy as np
>>> import pandas as pd
>>> import statspai as sp
>>> rng = np.random.default_rng(0)
>>> cluster = np.repeat(np.arange(20), 15)
>>> n = cluster.size
>>> frailty = rng.gamma(2.0, 0.5, 20)[cluster]
>>> x = rng.normal(size=n)
>>> rate = frailty * np.exp(0.7 * x)
>>> T = rng.exponential(1.0 / rate)
>>> C = rng.exponential(2.0, n)
>>> df = pd.DataFrame({"time": np.minimum(T, C),
... "event": (T <= C).astype(int),
... "x": x, "hospital": cluster})
>>> res = sp.cox_frailty("time + event ~ x", df, cluster="hospital")
>>> res.var_names
['x']
>>> bool(res.theta >= 0) # gamma frailty variance
True
References
[@therneau2000modeling]
cox ¶
cox(formula: Optional[str] = None, data: DataFrame = None, duration: Optional[str] = None, event: Optional[str] = None, x: Optional[List[str]] = None, ties: str = 'efron', strata: Optional[str] = None, robust: str = 'nonrobust', cluster: Optional[str] = None, hazard_ratio: bool = True, alpha: float = 0.05, weights: Optional[str] = None, start: Optional[str] = None, ssc: Optional[str] = None, weights_as: Optional[str] = None, design: Optional[Any] = None) -> CoxResult
Cox Proportional Hazards model via partial likelihood.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
formula
|
str
|
Formula of the form |
None
|
data
|
DataFrame
|
Input data. |
None
|
duration
|
str
|
Column name for follow-up time (overrides formula LHS). |
None
|
event
|
str
|
Column name for event indicator (1 = event, 0 = censored). |
None
|
x
|
list of str
|
Covariate column names (overrides formula RHS). |
None
|
ties
|
str
|
Tie-handling method: |
``'efron'``
|
strata
|
str
|
Column name for stratification variable. |
None
|
robust
|
str
|
|
``'nonrobust'``
|
cluster
|
str
|
Column name for cluster-robust SE. |
None
|
hazard_ratio
|
bool
|
If True, report hazard ratios in the summary alongside coefficients. |
True
|
alpha
|
float
|
Significance level for confidence intervals. |
0.05
|
weights
|
str
|
Column of non-negative case weights (inverse-probability-of-
treatment weights, survey weights, frequency counts). Weighted
partial likelihood as in R |
None
|
start
|
str
|
Column with the start of each row's interval: counting-process data
|
None
|
ssc
|
(r, stata)
|
Small-sample factor of the robust and clustered variances. |
'r'
|
weights_as
|
(frequency, sampling)
|
How to read |
'frequency'
|
design
|
SurveyDesign
|
A complex survey design from :func: |
None
|
Returns:
| Type | Description |
|---|---|
CoxResult
|
Result object extending |
Notes
With start= or weights= the concordance is computed over the
risk sets of the fit (pairs in which one row fails while the other is
at risk, weighted by the product of their weights), as R
concordance() does for counting-process and weighted fits; Harrell's
pairwise C of right-censored data is the special case without either.
Examples:
>>> import statspai as sp
>>> import numpy as np, pandas as pd
>>> rng = np.random.default_rng(0)
>>> n = 150
>>> age = rng.normal(50, 10, n)
>>> treatment = rng.integers(0, 2, n).astype(float)
>>> time = rng.exponential(
... scale=np.exp(-0.03 * (age - 50) - 0.5 * treatment))
>>> status = (rng.random(n) < 0.8).astype(int)
>>> df = pd.DataFrame({"time": time, "status": status,
... "age": age, "treatment": treatment})
>>> res = sp.cox(formula="time ~ age + treatment", data=df,
... event="status")
>>> bool(list(res.params.index) == ["age", "treatment"])
True
>>> bool(isinstance(res.summary(), str))
True
kaplan_meier ¶
kaplan_meier(data: DataFrame, duration: str, event: str, group: Optional[str] = None, alpha: float = 0.05, conf_type: Optional[str] = 'log-log', weights: Optional[str] = None, start: Optional[str] = None, cluster: Optional[str] = None, se_method: Optional[str] = None, design: Optional[Any] = None) -> KMResult
Kaplan-Meier non-parametric survival function estimator.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
DataFrame
|
Input data. |
required |
duration
|
str
|
Column name for duration / follow-up time. |
required |
event
|
str
|
Column name for event indicator (1 = event, 0 = censored). |
required |
group
|
str
|
Column name for group variable (stratification). |
None
|
alpha
|
float
|
Significance level for confidence intervals (Greenwood formula). |
0.05
|
conf_type
|
(log - log, log, plain)
|
Scale on which the interval is symmetric. .. versionchanged:: 1.40.0
The default is |
"log-log"
|
weights
|
str
|
Column of non-negative case weights (inverse-probability-of-
treatment weights give the IPTW-adjusted curve of each |
None
|
start
|
str
|
Column with each row's entry time: counting-process data |
None
|
cluster
|
str
|
Column whose levels are independent units (subject id with several rows); the robust variance sums the influence within each. |
None
|
se_method
|
(greenwood, ij, xie_liu)
|
|
'greenwood'
|
design
|
SurveyDesign
|
A complex survey design from :func: |
None
|
Returns:
| Type | Description |
|---|---|
KMResult
|
Object with |
Examples:
>>> import statspai as sp
>>> import numpy as np, pandas as pd
>>> rng = np.random.default_rng(0)
>>> n = 150
>>> df = pd.DataFrame({
... "time": rng.exponential(10, n).round(2),
... "status": (rng.random(n) < 0.7).astype(int),
... })
>>> km = sp.kaplan_meier(data=df, duration="time", event="status",
... conf_type="log-log")
>>> bool("survival" in km.survival_table.columns)
True
>>> bool(isinstance(km.median_survival, float))
True
logrank_test ¶
Log-rank test for equality of survival distributions across groups.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
DataFrame
|
|
required |
duration
|
str
|
Column names. |
required |
event
|
str
|
Column names. |
required |
group
|
str
|
Column names. |
required |
Returns:
| Type | Description |
|---|---|
dict
|
Keys: |
Examples:
>>> import statspai as sp
>>> import numpy as np, pandas as pd
>>> rng = np.random.default_rng(0)
>>> n = 150
>>> treatment = rng.integers(0, 2, n)
>>> time = rng.exponential(scale=np.exp(-0.5 * treatment))
>>> status = (rng.random(n) < 0.8).astype(int)
>>> df = pd.DataFrame({"time": time, "status": status,
... "treatment": treatment})
>>> res = sp.logrank_test(data=df, duration="time", event="status",
... group="treatment")
>>> bool("p_value" in res and "test_statistic" in res)
True
References
mantel1959statistical
survreg ¶
survreg(formula: Optional[str] = None, data: DataFrame = None, duration: Optional[str] = None, event: Optional[str] = None, x: Optional[List[str]] = None, dist: str = 'weibull', robust: str = 'nonrobust', cluster: Optional[str] = None, alpha: float = 0.05, frailty: Optional[str] = None) -> EconometricResults
Parametric survival model (AFT parameterization).
The data may also come first: sp.survreg(df, duration=..., event=...,
x=[...]) and sp.survreg(df, "t ~ x1 + x2", event=...).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
formula
|
str
|
Formula |
None
|
data
|
DataFrame
|
|
None
|
duration
|
str
|
Follow-up time column (or formula LHS). |
None
|
event
|
str
|
Event indicator column. |
None
|
x
|
list of str
|
Covariate columns (or formula RHS). |
None
|
dist
|
str
|
Distribution: |
``'weibull'``
|
robust
|
str
|
|
``'nonrobust'``
|
cluster
|
str
|
Cluster column; implies the cluster sandwich. |
None
|
alpha
|
float
|
|
0.05
|
frailty
|
(None, gamma)
|
Unobserved heterogeneity. |
None
|
Returns:
| Type | Description |
|---|---|
EconometricResults
|
Fitted parametric survival model. Parameters include covariates
and |
Examples:
>>> import statspai as sp
>>> import numpy as np, pandas as pd
>>> rng = np.random.default_rng(0)
>>> n = 150
>>> age = rng.normal(50, 10, n)
>>> treatment = rng.integers(0, 2, n).astype(float)
>>> time = rng.exponential(
... scale=np.exp(0.02 * (age - 50) + 0.5 * treatment))
>>> status = (rng.random(n) < 0.8).astype(int)
>>> df = pd.DataFrame({"time": time, "status": status,
... "age": age, "treatment": treatment})
>>> res = sp.survreg("time ~ age + treatment", data=df,
... event="status", dist="weibull")
>>> bool("log(sigma)" in list(res.params.index))
True