Skip to main content

Semiparametric Causal Inference for Right-Censored Outcomes with Many Weak Invalid Instruments

Project description

MAWII-SURV

Semiparametric causal inference for right-censored outcomes with many weak or invalid instruments, powered by the GEL-NOW framework.

PyPI Python License: MIT

MAWII-Surv (MAny Weak and Invalid Instruments for Survival outcomes) implements GEL-NOW: Generalized Empirical Likelihood with Non-Orthogonal and Weak moments. It extends classical GEL to non-orthogonal nuisance settings and allows many weak or invalid IVs under right-censoring.


Table of Contents


Introduction to MAWII-Surv

MAWII-Surv (MAny Weak and Invalid Instruments for Survival outcomes) is a Python package for semiparametric causal inference with right-censored outcomes in the presence of many weak or invalid instruments. The package implements the novel GEL-NOW (Generalized Empirical Likelihood with Non-Orthogonal and Weak moments, or GEL 2.0) framework, which extends classical generalized empirical likelihood to settings where nuisance functions enter non-orthogonally and where instruments may be weak or invalid.

Key features include:

  • Heteroscedasticity-based identification under an accelerated failure time (AFT) model, enabling causal effect estimation even with invalid instruments.

  • Flexible nuisance estimation using modern machine learning methods, including deep neural networks, to capture complex nonlinear structures.

  • Robust inference that explicitly accounts for additional variance from non-orthogonal nuisances, ensuring valid confidence intervals.

  • Diagnostics such as a censoring-adjusted over-identification test to assess instrument validity.

  • Applications to biobank-scale data, with built-in support for analyzing time-to-event outcomes such as disease onset.

With simulation tools, diagnostic functions, and real-data examples, MAWII-Surv provides a user-friendly platform for researchers in statistics, econometrics, epidemiology, and genetics to conduct reliable causal inference from censored survival data

Why MAWII-Surv?

  • Survival + Endogeneity: G-estimation for treatment effects with right-censoring and unmeasured confounding.
  • Many Weak/Invalid IVs: Robust to weak instruments and horizontal pleiotropy.
  • GEL 2.0 (GEL-NOW): Empirical Likelihood (EL), Exponential Tilting (ET), and Continuous Updating (CUE) with theory for non-orthogonal nuisances.
  • Modern ML Nuisances: Deep neural nets (PyTorch), Random Forests, XGBoost, plus classical linear models.
  • Diagnostics: Over-identification test adapted to censoring; standard errors account for censoring-induced variance inflation.

Features

  • Uncensor‐data (mawii_noncensor)
  • Right‐censoring (mawii_censor)
  • Multiple model backends:
    • Neural networks
    • Linear regression
    • Random forests
    • XGBoost
  • Choice of Generalized Empirical Likelihood (GEL) functions:
    • Empirical Tilting (ET)
    • Empirical Likelihood (EL)
    • Continuous Updating Estimator (CUE)

Installation

Install from PyPI:

pip install mawiisurv

Dependencies

Make sure you have the following installed (the minimal compatible versions shown):

  • numpy>=1.19
  • torch>=1.8
  • scipy>=1.5
  • scikit-learn>=0.24
  • xgboost>=1.3
  • numba>=0.53

If you plan to use GPU, please install a CUDA-compatible PyTorch from the official download page before installing this package.


Quick start

A runnable demo is provided below. It simulates both non-censored and right-censored data, fits the DNN + ET specification, and prints the point estimate, standard error, and the over-identification test statistic.

# demo

#  pip install mawiisurv
import numpy as np
import mawiisurv
import torch
import copy
# function input
'''
Main input variables:
    X: (n,p) array, covariates
    Z: (n,m) array, instrumental variables
    A: (n,) array, treatment
    Y: (n,) array, outcome
    censor_delta: (n,) array, censor indicator, 1 for uncensor and 0 for censor
    h: window for local KM estimation, and default setting is 1
    model_types: ['neural_network','linear_regression','random_forest','xgboost'], 4 choices of comparsion models, and default setting is ['neural_network']
    rho_function_names: ['ET','EL','CUE'], GEL functions, and default setting is ['EL']

Other DNN setting: 
    hidden_layers=[50, 50],
    learning_rate=0.0005,
    weight_decay=0.0001,
    batch_size=256,
    dropout_rate=0,
    patience=5,
    epochs=1000,
    validation_split=0.05,
    shuffle=False,
    device='cpu'
'''

# two main functions:
'''
'mawii_noncensor' for complete data
'mawii_censor' for right censor data
'''

# function output
'''
function will return a list of result, including 
    'beta' : estimation of treatment effect
    'se' : estimated standard error
    'test' : overidentification test statistics
'''

# complete data simulation
n = 10000
m = 20
p = 1
beta_0 = 0.4
device = 'cuda:0' if torch.cuda.is_available() else 'cpu'


def generate_data(n=10000, m=10, p=5, beta_0=0.4, h_2=0.2,eta_A=1,censor_rate=0.3, case='case1'):
    random_coefs = np.random.normal(loc=0, scale=1, size=1000)
    
    X = np.random.uniform(-2,2,size=(n,p))
    Z = np.random.uniform(-2,2,size=(n,m))

    gamma = np.sqrt(h_2 / (1.5*m)) * random_coefs[0 : m] 
    delta = np.sqrt(h_2 / (1.5*m)) * random_coefs[m : 2*m]

    epsilon_A = np.random.normal(0, 0.4*(1-h_2), size=n)
    epsilon_Y = np.random.normal(0, 0.4*(1-h_2), size=n)
    

    alpha_case1 = np.zeros(m)
    alpha_case2 = np.sqrt(h_2 / (1.5*m)) * (random_coefs[2*m : 3*m])/2
    alpha_case3 = gamma / 2
    Z_new = copy.deepcopy(Z)
    Z_new = np.cos(Z*2)

    
    if case == "case1":
        tmp = np.random.multinomial(1, [1, 0, 0], size=m)
    elif case == "case2":
        tmp = np.random.multinomial(1, [0.6, 0.2, 0.2], size=m)
    elif case == "case3":
        tmp = np.random.multinomial(1, [0.1, 0.9, 0], size=m)
    elif case == "case4":
        tmp = np.random.multinomial(1, [0.1, 0, 0.9], size=m)
    else:
        raise ValueError("Unknown case provided.")
    alpha = tmp[:, 0] * alpha_case1 + tmp[:, 1] * alpha_case2 + tmp[:, 2] * alpha_case3

    U = np.random.normal(0, 0.6*(1-h_2), size=n)
    A = Z_new @ gamma + U + (1 + Z @ delta)*epsilon_A
    T = beta_0*A  + Z_new @ alpha -U  + epsilon_Y 

    rr = 0
    
    while True:
        C = np.random.uniform( -1+rr,5+rr, size=n)
        censor_delta = np.where(T <= C, 1, 0) 
        if np.mean(1-censor_delta) >=censor_rate+0.03:
            rr = rr + 0.1
        elif np.mean(1-censor_delta)<=censor_rate-0.03:
            rr = rr - 0.1
        else:
             break
            
    Y = np.minimum(T,C)
 
    return X, Z, A, Y, T, censor_delta

X, Z, A, Y, T, censor_delta= generate_data(n=10000, m=20, p=1, beta_0=0.4, h_2=0.2, eta_A=1, censor_rate=0.4, case='case1')


# uncensored data simulation

result_noncensor = mawiisurv.mawii_noncensor(X, Z, A, T, 
                          model_types=['neural_network','linear_regression','random_forest','xgboost'],
                          rho_function_names=['ET','EL','CUE'],
                          device = device)

print(f"DNN+ET BETA: {result_noncensor['neural_network']['ET']['beta']:.3f}")
print(f"DNN+ET SE: {result_noncensor['neural_network']['ET']['se']:.3f}")
print(f"DNN+ET over-identification test: {result_noncensor['neural_network']['ET']['test']:.3f}")


# right-censor data simulation

result_censor = mawiisurv.mawii_censor(X, Z, A, Y, censor_delta, h = 4,
                                          model_types=['neural_network'],
                                          rho_function_names=['ET'],
                                          device = device)

print(f"DNN+ET BETA: {result_censor['neural_network']['ET']['beta']:.3f}")
print(f"DNN+ET SE: {result_censor['neural_network']['ET']['se']:.3f}")
print(f"DNN+ET over-identification test: {result_censor['neural_network']['ET']['test']:.3f}")
    

API

mawii_noncensor(
    X, Z, A, Y,
    model_types=['neural_network'],
    rho_function_names=['ET'],
    hidden_layers=[50, 50],
    learning_rate=0.0005,
    weight_decay=0.0001,
    batch_size=256,
    dropout_rate=0,
    patience=5,
    epochs=100,
    validation_split=0.05,
    shuffle=False,
    device='cpu',
) -> dict
mawii_censor(
    X, Z, A, Y, censor_delta, h=1,
    model_types=['neural_network'],
    rho_function_names=['ET'],
    hidden_layers=[50, 50],
    learning_rate=0.0005,
    weight_decay=0.0001,
    batch_size=256,
    dropout_rate=0,
    patience=5,
    epochs=100,
    validation_split=0.05,
    shuffle=False,
    device='cpu',
) -> dict

Arguments

  • X: array of shape n by p, baseline covariates

  • Z: array of shape n by m, instrumental variables

  • A: array of shape n, treatment

  • Y: array of shape n, outcome

  • censor_delta: array of shape n, 1 uncensored and 0 censored, only for mawii_censor

  • h: scalar, window for local Kaplan–Meier in censoring adjustment

  • model_types: list of model backends, choose from neural_network, linear_regression, random_forest, xgboost

  • rho_function_names: list of GEL score types, choose from ET, EL, CUE

  • device: cpu or cuda device string such as cuda:0


Return values:

{
  'neural_network': {
    'ET': {
      'beta': float,    # point estimate
      'se': float,      # standard error
      'test': float     # overidentification test statistic
    },
    'EL': {...},
    'CUE': {...}
  },
  'linear_regression': {...},
  ...
}

Notes

  • We generally recommend ET or EL over CUE under weak identification.

  • Deep NNs tend to be most robust for complex nonlinear nuisances; RF/XGB are strong baselines in moderate dimensions.

  • Under censoring, standard errors include an extra variance component due to estimating the censoring distribution.

Citation:

If you use MAWII-Surv, please cite:

Bu Q., Su W., Zhao X., Liu Z. (2025).
Semiparametric Causal Inference for Right-Censored Outcomes with Many Weak Invalid Instruments.
(manuscript)

BibTeX:

@misc{mawiisurv2025,
  title   = {Semiparametric Causal Inference for Right-Censored Outcomes with Many Weak Invalid Instruments},
  author  = {Bu, Qiushi and Su, Wen and Zhao, Xingqiu and Liu, Zhonghua},
  year    = {2025},
  note    = {Python package: MAWII-Surv},
  howpublished = {\url{https://pypi.org/project/mawiisurv/}}
}

Contributing

Contributions are welcome!
Please use Issues for bug reports and pull requests for code contributions.

License

MIT License — see LICENSE for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mawiisurv-0.5.0.tar.gz (6.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mawiisurv-0.5.0-py3-none-any.whl (6.2 kB view details)

Uploaded Python 3

File details

Details for the file mawiisurv-0.5.0.tar.gz.

File metadata

  • Download URL: mawiisurv-0.5.0.tar.gz
  • Upload date:
  • Size: 6.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.7

File hashes

Hashes for mawiisurv-0.5.0.tar.gz
Algorithm Hash digest
SHA256 c6aa6a52cb0ae74099931b11c719939fe3cb5707218fe39bcc77c672e81834a8
MD5 f3641e2ab72973dec39dc6afd24f37ca
BLAKE2b-256 613f179f5aa39c670fe29f9c482b7d01166dc367f7894870aa8bbf9701134894

See more details on using hashes here.

File details

Details for the file mawiisurv-0.5.0-py3-none-any.whl.

File metadata

  • Download URL: mawiisurv-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 6.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.7

File hashes

Hashes for mawiisurv-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2c8c43ccddff71a5458a8fd11b02cb725bc8f56d2805eeb3e4f50be8f3825c8f
MD5 bcd04f0b8cc81e871f36398c2ca91152
BLAKE2b-256 39cc37dee5f5580d0c7e7be9282384bb3e752a021d8761ba2b51659eccb1b6e9

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page