Skip to content

statspai.dose_response

dose_response

Continuous Treatment Effects: Dose-Response Estimation.

Estimates the dose-response function E[Y(t)] for continuous treatments using the generalized propensity score (GPS) approach and kernel-based methods.

References

Hirano, K. & Imbens, G. W. (2004). The Propensity Score with Continuous Treatments. Applied Bayesian Modeling and Causal Inference from Incomplete-Data Perspectives, 226164, 73-84. [@hirano2004propensity]

Kennedy, E. H., Ma, Z., McHugh, M. D., & Small, D. S. (2017). Non-parametric methods for doubly robust estimation of continuous treatment effects. JRSS-B, 79(4), 1229-1245. [@kennedy2017parametric]

DoseResponse

Generalized Propensity Score dose-response estimator.

Parameters:

Name Type Description Default
data DataFrame
required
y str
required
treat str
required
covariates list of str
required
n_dose_points int
20
dose_range tuple
None
treatment_model sklearn estimator
None
outcome_model sklearn estimator
None
n_bootstrap int
200
alpha float
0.05
random_state int
42

Examples:

>>> import numpy as np
>>> import pandas as pd
>>> import statspai as sp
>>> rng = np.random.default_rng(0)
>>> n = 150
>>> age = rng.normal(50, 10, n)
>>> weight = rng.normal(70, 12, n)
>>> dosage = 0.5 * age + 0.3 * weight + rng.normal(0, 5, n)
>>> outcome = 0.8 * dosage + 0.2 * age + rng.normal(0, 3, n)
>>> df = pd.DataFrame({"outcome": outcome, "dosage": dosage,
...                    "age": age, "weight": weight})
>>> est = sp.DoseResponse(df, y="outcome", treat="dosage",
...                       covariates=["age", "weight"],
...                       n_dose_points=6, n_bootstrap=8, random_state=0)
>>> res = est.fit()
>>> isinstance(res, sp.CausalResult)
True
>>> list(res.detail.columns)
['dose', 'response', 'se', 'ci_lower', 'ci_upper', 'marginal_effect']
References

[@hirano2004propensity], [@kennedy2017parametric]

fit

fit() -> CausalResult

Estimate the dose-response function.

VCNetResult dataclass

Bases: ResultProtocolMixin

Dose-response curve returned by :func:vcnet / :func:scigan.

Attributes:

Name Type Description
t_grid ndarray

Treatment grid at which the curve is evaluated.

mu_hat ndarray

Estimated dose-response :math:\hat\mu(t) on t_grid.

se ndarray

Bootstrap pointwise standard errors.

ci_lo, ci_hi ndarray

Pointwise lower / upper confidence band.

coef_matrix ndarray

Varying-coefficient matrix of shape (n_basis, p + 1).

n_obs, n_basis int
detail dict

Examples:

>>> import statspai as sp
>>> import numpy as np
>>> import pandas as pd
>>> df = pd.DataFrame({
...     "y": np.random.randn(200),
...     "dose": np.random.rand(200),
...     "x1": np.random.randn(200),
... })
>>> res = sp.vcnet(df, y="y", treatment="dose",
...                covariates=["x1"])
>>> res.mu_hat[:3]            # dose-response on the t-grid
>>> print(res.summary())     # tidy curve table

dose_response

dose_response(data: DataFrame, y: str, treat: str, covariates: List[str], n_dose_points: int = 20, dose_range: Optional[Tuple[float, float]] = None, treatment_model: Optional[BaseEstimator] = None, outcome_model: Optional[BaseEstimator] = None, n_bootstrap: int = 200, alpha: float = 0.05, random_state: int = 42) -> CausalResult

Estimate the dose-response function for a continuous treatment.

Parameters:

Name Type Description Default
data DataFrame

Input data.

required
y str

Outcome variable.

required
treat str

Continuous treatment variable.

required
covariates list of str

Covariate names.

required
n_dose_points int

Number of dose levels to evaluate.

20
dose_range tuple of (float, float)

(min, max) dose range. If None, uses 5th-95th percentile.

None
treatment_model sklearn estimator

Model for E[T|X]. Default: GBM.

None
outcome_model sklearn estimator

Model for E[Y|T, GPS]. Default: GBM.

None
n_bootstrap int

Bootstrap iterations for confidence bands.

200
alpha float

Significance level.

0.05
random_state int
42

Returns:

Type Description
CausalResult

detail contains the dose-response curve: columns 'dose', 'response', 'se', 'ci_lower', 'ci_upper'. model_info contains 'avg_marginal_effect' (derivative).

Examples:

>>> import statspai as sp, numpy as np, pandas as pd
>>> rng = np.random.default_rng(0)
>>> n = 400
>>> age = rng.normal(40, 10, n)
>>> weight = rng.normal(70, 12, n)
>>> dosage = 0.5 * age + rng.normal(0, 5, n)      # confounded dose
>>> outcome = 0.2 * dosage + 0.1 * weight + rng.normal(0, 1, n)
>>> df = pd.DataFrame({"outcome": outcome, "dosage": dosage,
...                    "age": age, "weight": weight})
>>> result = sp.dose_response(df, y="outcome", treat="dosage",
...                           covariates=["age", "weight"])
>>> result.detail            # dose-response curve

scigan

scigan(data: DataFrame, y: str, treatment: str, covariates: Sequence[str], t_grid: Optional[Sequence[float]] = None, propensity_weights: Optional[ndarray] = None, **kwargs: Any) -> VCNetResult

Adversarial dose-response estimator (Bica et al. 2020).

The reference SCIGAN architecture is a GAN — too heavy to ship as a dependency-free algorithm. This entry point exposes a propensity-weighted VCNet fit that captures the essential "balancing" behaviour of SCIGAN: residual imbalance along the treatment axis is re-weighted using a user-provided propensity_weights (g(T | X)^{-1}). For the full SCIGAN training loop, plug in your own GAN-generated counterfactual samples and pass the re-weighting through propensity_weights.

Examples:

>>> import statspai as sp
>>> import numpy as np
>>> import pandas as pd
>>> df = pd.DataFrame({
...     "y": np.random.randn(200),
...     "dose": np.random.rand(200),
...     "x1": np.random.randn(200),
... })
>>> w = np.ones(len(df))  # inverse-propensity balancing weights
>>> res = sp.scigan(df, y="y", treatment="dose",
...                 covariates=["x1"],
...                 propensity_weights=w)
>>> res.mu_hat[:3]  # balanced dose-response curve