Skip to content

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_t given :math:\bar{A}_{t-1} and V (no L).
  • Denominator: conditional density/probability of :math:A_t given :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 [trim_per_period, 1 - trim_per_period] before taking the cumulative product. A common value is 0.01. Set to 0 to disable per-period trimming (the default).

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