Skip to content
Scenario6 min read

GATE

We call 'GATE' a scenario where in observational study after fitting DML model we analyzing treatment effect heterogeneity between groups of clients

GATE

We call 'GATE' a scenario where in observational study after fitting DML model we analyzing treatment effect heterogeneity between groups of clients

Data

We will use DGP from Causalis. Read more at https://causalis.causalcraft.com/articles/generate_obs_hte_26_rich

Result
user_idydtenure_monthsavg_sessions_weekspend_last_monthage_yearsincome_monthlyprior_purchases_12msupport_tickets_90dpremium_usermobile_userurban_residentreferred_usermm_obstau_linkg0g1cate
010.0000000.028.8146541.077.93676750.2341011926.6983011.02.01.01.01.00.00.0454530.0454530.0890958.1379819.1423951.004414
1280.0996111.025.9133453.053.77774028.1158595104.2715093.00.01.01.00.01.00.0415140.0415140.24667960.45925778.81730718.358049
236.4004821.024.96992910.0134.76432222.9070625267.9382558.03.00.01.01.00.00.0525930.0525930.1629687.7128559.1385771.425723
342.7882380.040.6550895.059.51707431.9704906597.3270183.02.01.01.01.00.00.0362210.0362210.18875525.38651031.1599325.773422
450.0000000.018.5608993.074.37093039.2372484930.0096285.01.01.01.00.00.00.0363430.0363430.17475715.35925018.6002273.240977
Result

Ground truth ATE is 19.409586529660793 Ground truth ATTE is 10.914991423363865

Result

CausalData(df=(100000, 14), treatment='d', outcome='y', confounders=['tenure_months', 'avg_sessions_week', 'spend_last_month', 'age_years', 'income_monthly', 'prior_purchases_12m', 'support_tickets_90d', 'premium_user', 'mobile_user', 'urban_resident', 'referred_user'], user_id='user_id')

Result
treatmentcountmeanstdminp10p25medianp75p90max
00.09505176.087138240.8007130.00.00.08.3954464.859278190.22790021396.007575
11.0494958.506172199.4856250.00.00.00.0000036.958280148.8371935143.642132

Our data has strong treatment class disbalance. Only 5% of sample activated in treatment.

Treatment group has lower mean LTV. It's too early to draw conclusions.

Result

png

We see large right tale

Result

png

Result
treatmentnoutlier_countoutlier_ratelower_boundupper_boundhas_outliersmethodtail
00.095051113000.118884-97.288916162.148194Trueiqrboth
11.049497210.145686-55.43742092.395699Trueiqrboth

We see many outliers. It's common situation for LTV metric. Dropping them will lead to a biased conclusion

Result
confoundersmean_d_0mean_d_1abs_diffsmdks_pvalue
0premium_user0.7518070.5918370.159970-0.3457210.00000
1income_monthly4549.3851903918.058798631.326392-0.2776110.00000
2spend_last_month89.09180167.37538921.716412-0.2683600.00000
3support_tickets_90d0.9845451.2592440.2746990.2539740.00000
4avg_sessions_week5.0477534.2301480.817606-0.2017350.00000
5prior_purchases_12m3.9042203.5136390.390581-0.1893720.00000
6tenure_months28.74010025.5591613.180939-0.1841560.00000
7age_years36.43598434.8090831.626901-0.1441420.00000
8referred_user0.2714860.3071330.0356470.0786710.00001
9urban_resident0.6007930.5688020.031991-0.0649540.00013
10mobile_user0.8745730.8700750.004498-0.0134770.99998

As we see clients are differ on this confounders. We need to controll them to make causal inference

ATTE

ATTE is right estimand here. We will estimate effect on clients that were treated, had first purchase in our product

Result
value
field
estimandATTE
modelIRM
value11.7030 (ci_abs: 7.2449, 16.1611)
value_relative25.0048 (ci_rel: 15.3806, 34.6290)
alpha0.0500
p_value0.0000
is_significantTrue
n_treated4949
n_control95051
treatment_mean58.5062
control_mean76.0871
time2026-04-09

Our estimate is 12.1542 dollars (ci_abs: 7.7933, 16.5152). Mean in treatment group is 58.5062 dollars, so without our product it would be 46.3520 dollars.

GATET

Quantile groups GATET

Result
groupvalueis_significantci_lowerci_uppern_groupn_treatedn_controlshare_treatedstd_errortest_statp_valuemean_phistd_phimean_propensitymin_propensitymax_propensity
0prior_purchases_12m=(-0.001, 2.0]5.815230True0.58817911.042281274841700257840.061852.6669122.1805110.029225.815230442.6534590.0597190.010.823073
1prior_purchases_12m=(2.0, 3.0]8.376332False-1.08393417.83659819182984181980.051304.8267551.7353960.082678.376332670.0494740.0504920.010.583411
2prior_purchases_12m=(3.0, 4.0]3.922010False-5.99196713.83598718236847173890.046455.0582450.7753700.438123.922010683.2880780.0457960.010.519778
3prior_purchases_12m=(4.0, 6.0]21.469848True10.72607732.213618238121014227980.042585.4816163.9167000.0000921.469848851.7447570.0416270.010.463650
4prior_purchases_12m=(6.0, 16.0]36.380628True8.12933764.63191911286404108820.0358014.4141892.5239460.0116036.3806281543.2136910.0337570.010.554571
Result

png

Integer groups GATET

Result
groupvalueis_significantci_lowerci_uppern_groupn_treatedn_controlshare_treatedstd_errortest_statp_valuemean_phistd_phimean_propensitymin_propensitymax_propensity
0mobile_user=0.05.740115False-0.72329712.20352712565643119220.051173.2977201.7406320.081755.740115370.3607160.0485920.010.823073
1mobile_user=1.012.593462True7.56200817.624917874354306831290.049252.5671164.9056860.0000012.593462761.2590470.0481120.010.617166

Formal group comparisons

  • result.summary() answers which subgroup effects are different from 0.
  • result.contrast(...) answers whether one subgroup effect differs from another.
  • result.pairwise_summary(reference=...) compares every subgroup against a chosen reference group.
Result
value
field
estimandGATET_CONTRAST
modelIRM
contrastmobile_user=1.0 - mobile_user=0.0
left_groupmobile_user=1.0
right_groupmobile_user=0.0
value6.8533 (ci_abs: -1.3376, 15.0443)
std_error4.1791
test_stat1.6399
alpha0.0500
alternativetwo-sided
p_value0.1010
is_significantFalse
left_value12.5935
right_value5.7401
n_left87435
n_right12565
time2026-04-09
Result
left_groupright_groupcontrast_labelleft_valueright_valueestimate_diffstd_errortest_statp_valuep_value_adjci_lowerci_upperis_significantis_significant_adjn_leftn_rightalphap_adjust
0mobile_user=1.0mobile_user=0.0mobile_user=1.0 - mobile_user=0.012.5934625.7401156.8533474.1791191.6399020.1010250.101025-1.33757615.044271FalseFalse87435125650.05none

Manual groups GATET

Result
groupvalueis_significantci_lowerci_uppern_groupn_treatedn_controlshare_treatedstd_errortest_statp_valuemean_phistd_phimean_propensitymin_propensitymax_propensity
00=Middle9.255211True2.74368715.766736570502633544170.046153.3222682.7858120.005349.255211794.7036180.0453500.010.823073
10=Senior7.901003False-9.12345924.92546512133504116290.041548.6861100.9096140.363037.901003958.0034350.0395430.010.601238
20=Young16.317476True10.29580722.339144308171812290050.058803.0723365.3110970.0000016.317476543.2480950.0567930.010.617166

Multi-column indicator groups GATET

Yes. groups= for score="GATET" can be a multi-column DataFrame of 0/1 subgroup indicators.

The columns must define a strict partition of the fitted sample:

  • every column must be binary,
  • every row must belong to exactly one subgroup,
  • every subgroup must contain at least one treated observation.

Groups with no controls are still accepted for GATET, but the estimator emits a warning because within-group overlap is degenerate.

Result
groupvalueis_significantci_lowerci_uppern_groupn_treatedn_controlshare_treatedstd_errortest_statp_valuemean_phistd_phimean_propensitymin_propensitymax_propensity
0Middle_mobile=04.351636False-4.22417412.927446751136271490.048204.3754940.9945470.319964.351636379.7772680.0464500.010.823073
1Middle_mobile=110.036847True2.61259417.461100495392271472680.045843.7879542.6496750.0080610.036847844.3950480.0451830.010.604030
2Senior_mobile=05.735265False-15.35601326.826542250011323870.0452010.7610530.5329650.594065.735265537.7258060.0414830.010.439237
3Senior_mobile=18.526907False-12.55237629.606190963339192420.0405910.7549340.7928370.427878.5269071057.2094770.0390400.010.601238
4Young_mobile=08.735219True0.41767717.052762255416823860.065784.2437222.0583860.039558.735219217.0729460.0618500.010.386215
5Young_mobile=117.092305True10.51094423.673665282631644266190.058173.3578995.0901790.0000017.092305568.6451100.0563360.010.617166