indcomp is a package for performing indirect treatment comparisons (ITCs).
indcomp currently supports the Matching-Adjusted Indirect Comparison (MAIC) approach, implemented as per NICE's guidance.
View the indcomp documentation.
# PyPI pip install indcomp
- NumPy
- SciPy
- pandas
- matplotlib
For this example, we use simulated data as per NICE's worked example (DSU Technical Support Document - Appendix D).
The objective is to weight individual patient-level data (IPD) for a hypothetical randomised control trial (RCT) comparing treatments A and B, to match on specified characteristics described in aggregated data for a hypothetical RCT comparing treatments A and C. In this example, we weight the AB trial such that the age matches the mean and standard deviation of the AC trial.
This example illustrates a common real-world scenario in which B and C are interventions for a disease, and A is a placebo. Typically, MAIC is used to fairly evaluate IPD for a potential new treatment (B) against that of an existing treatment (C) for which only aggregate data is available (the published results for the AC trial).
The AB trial comprises 500 individual patient records.
from indcomp import MAIC
from indcomp.datasets import load_NICE_DSU18
# load simulated Individual Patient Data (IPD) for trial AB
# load simulated Aggregated Data (AgD) for trial AC
df_AB_IPD, df_AC_AgD = load_NICE_DSU18()
print(f"Number of AB patients: {len(df_AB_IPD)}")
print(df_AB_IPD.sample(5))
> Number of AB patients: 500
> ID age gender trt y
> 113 114 73 Male A 1
> 371 122 45 Female B 1
> 77 78 48 Male A 1
> 441 192 49 Male B 0
> 120 121 68 Male A 1
The AC trial is aggregate data for 300 patients.
# 300 AC patients
print(df_AC_AgD.round(2))
> age.mean age.sd N.male prop.male y.A.sum y.A.bar N.A y.C.sum y.C.bar N.C
> 0 50.27 3.12 68 0.23 125 0.83 150 21 0.14 150
We instantiate and configure a MAIC
instance to weight the AB data to yield the same mean and standard deviation age as the AC trial. These characteristics are quite different for the starting, unweighted data.
# adjust df_AB_IPD['age'] to have same mean as df_AC_AgD['age.mean'] and
# adjust df_AB_IPD['age'] to have same std as df_AC_AgD['age.sd']
maic=MAIC(
df_index=df_AB_IPD,
df_target=df_AC_AgD,
match={
"age.mean": ("mean", "age"),
"age.sd": ("std", "age", "age.mean")
}
)
# compare unweighted populations, before performing matching adjustment
maic.compare_populations()
After calculating the weights, we examine the effective sample size (ESS) and plot the distribution of weights. Our sample size decreases from the original 500 to an effective sample size of 178.56.
# calculate and examine weights and Effective Sample Size
maic.calc_weights()
print(f"Effective Sample Size: {maic.ESS_:.2f}") # original sample size: 500 patients
maic.plot_weights()
> Effective Sample Size: 178.56
After applying the weights, the mean and standard deviation age of the AB trial now matches that of the AC trial.
# compare weighted populations, after performing matching adjustment
maic.compare_populations(weighted=True)