statspai.msm¶
msm ¶
Marginal Structural Models (MSM) for time-varying treatments.
Implements Robins' inverse-probability-of-treatment-weighted (IPTW) estimator for longitudinal data with time-varying confounding.
MarginalStructuralModel ¶
Class wrapper around :func:msm for programmatic access.
Construct with the same keyword arguments as :func:msm (minus
data), then call :meth:fit with the panel to estimate the
stabilized-IPTW marginal structural model.
Examples:
>>> import numpy as np
>>> import pandas as pd
>>> import statspai as sp
>>> from statspai.msm.msm import MarginalStructuralModel
>>> rng = np.random.default_rng(0)
>>> rows = []
>>> for i in range(150):
... a_prev = 0.0
... for t in range(4):
... l = rng.normal() + 0.4 * a_prev
... pr = 1.0 / (1.0 + np.exp(-(0.4 * l - 0.3)))
... a = float(rng.binomial(1, pr))
... rows.append((i, t, a, l, rng.normal()))
... a_prev = a
>>> panel = pd.DataFrame(rows, columns=['pid', 'visit', 'a', 'l', 'age'])
>>> panel['y'] = (panel.groupby('pid')['a'].transform('sum')
... + rng.normal(0, 1, len(panel)))
>>> model = MarginalStructuralModel(
... y='y', treat='a', id='pid', time='visit',
... time_varying=['l'], baseline=['age']).fit(panel)
>>> isinstance(model.summary(), str)
True
References
[@robins2000marginal]
stabilized_weights ¶
stabilized_weights(data: DataFrame, treat: str, id: str, time: str, time_varying: List[str], baseline: Optional[List[str]] = None, treat_type: str = 'auto', trim_per_period: float = 0.0) -> ndarray
Compute stabilized IPTW weights for time-varying treatments.
Returns one weight per row (long-format). For treatment :math:A_t
with history :math:\bar{A}_{t-1}, time-varying confounders
:math:\bar{L}_t (must be pre-treatment), and baseline V:
- Numerator: conditional density/probability of :math:
A_tgiven :math:\bar{A}_{t-1}and V (no L). - Denominator: conditional density/probability of :math:
A_tgiven :math:\bar{A}_{t-1}, :math:\bar{L}_t, and V.
The row weight is the cumulative product of per-period ratios up to and including period t.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
DataFrame
|
Sorted long-format panel (one row per unit-time). |
required |
treat
|
str
|
Treatment column (binary 0/1 or continuous). |
required |
id
|
str
|
Unit and time identifiers. |
required |
time
|
str
|
Unit and time identifiers. |
required |
time_varying
|
list of str
|
Time-varying confounders (already lagged to pre-treatment). |
required |
baseline
|
list of str
|
Baseline covariates. |
None
|
treat_type
|
(auto, binary, continuous)
|
Overrides auto-detection. |
'auto'
|
trim_per_period
|
float
|
If > 0, trim each period's density ratio at the symmetric
quantile |
0.0
|
Returns:
| Type | Description |
|---|---|
ndarray
|
Stabilized weight for each row. |
Examples:
Long-format panel where the time-varying confounder l responds
to past treatment and predicts the current treatment:
>>> import numpy as np
>>> import pandas as pd
>>> import statspai as sp
>>> rng = np.random.default_rng(42)
>>> rows = []
>>> for i in range(100):
... a_prev = 0.0
... for t in range(4):
... l = rng.normal() + 0.4 * a_prev
... pr = 1.0 / (1.0 + np.exp(-(0.4 * l - 0.3)))
... a = float(rng.binomial(1, pr))
... rows.append((i, t, a, l))
... a_prev = a
>>> panel = pd.DataFrame(rows, columns=['id', 't', 'a', 'l'])
>>> sw = sp.stabilized_weights(panel, treat='a', id='id',
... time='t', time_varying=['l'])
>>> sw.shape
(400,)
>>> round(float(sw.mean()), 2)
1.0