import%20marimo%0A%0A__generated_with%20%3D%20%220.25.0%22%0Aapp%20%3D%20marimo.App(width%3D%22medium%22)%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20coeftable%20as%20ct%0A%20%20%20%20import%20marimo%20as%20mo%0A%20%20%20%20import%20numpy%20as%20np%0A%20%20%20%20import%20pandas%20as%20pd%0A%0A%20%20%20%20from%20increment%20import%20Analysis%2C%20Covariate%2C%20estimate_cate%2C%20targeting_rule%2C%20validate_cate%0A%20%20%20%20from%20increment.frame%20import%20from_unit_summary%0A%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20Analysis%2C%0A%20%20%20%20%20%20%20%20Covariate%2C%0A%20%20%20%20%20%20%20%20ct%2C%0A%20%20%20%20%20%20%20%20estimate_cate%2C%0A%20%20%20%20%20%20%20%20from_unit_summary%2C%0A%20%20%20%20%20%20%20%20mo%2C%0A%20%20%20%20%20%20%20%20np%2C%0A%20%20%20%20%20%20%20%20pd%2C%0A%20%20%20%20%20%20%20%20targeting_rule%2C%0A%20%20%20%20%20%20%20%20validate_cate%2C%0A%20%20%20%20)%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%20Heterogeneous%20treatment%20effects%0A%0A%20%20%20%20The%20average%20effect%20asks%20**did%20it%20work%3F**%20HTE%20asks%20**who%20benefits%20more%3F**%0A%0A%20%20%20%201.%20Confirm%20the%20overall%20effect.%0A%20%20%20%202.%20Estimate%20how%20effects%20vary%20with%20pre-treatment%20covariates.%0A%20%20%20%203.%20Validate%20the%20ranking%20on%20held-out%20users.%0A%20%20%20%204.%20Build%20a%20targeting%20rule%20only%20if%20validation%20passes.%0A%0A%20%20%20%20A%20model%20always%20finds%20a%20%E2%80%9Cbest%E2%80%9D%20group.%20Held-out%20validation%20tells%20us%20whether%20that%0A%20%20%20%20ranking%20is%20real.%20Because%20this%20notebook%20uses%20simulated%20data%2C%20every%20estimate%20can%0A%20%20%20%20be%20compared%20with%20the%20true%20per-user%20effect%2C%20%60tau_true%60.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(np%2C%20pd)%3A%0A%20%20%20%20PLATFORMS%20%3D%20(%22ios%22%2C%20%22android%22%2C%20%22web%22)%0A%20%20%20%20REGIONS%20%3D%20(%22na%22%2C%20%22emea%22%2C%20%22apac%22%2C%20%22latam%22)%0A%0A%20%20%20%20%23%20The%20known%20truth%3A%20tau(x)%20%3D%20BASE_TAU%20%2B%20SPEND_SLOPE%20*%20z(spend)%20%2B%20IOS_BONUS%20*%201%5Bios%5D%2C%0A%20%20%20%20%23%20in%20absolute%20revenue%20per%20user%20-%20the%20scale%20%60estimate_cate%60%20reports%20on.%0A%20%20%20%20BASE_TAU%20%3D%200.15%0A%20%20%20%20SPEND_SLOPE%20%3D%200.10%0A%20%20%20%20IOS_BONUS%20%3D%200.08%0A%20%20%20%20NOISE_SD%20%3D%200.40%0A%0A%20%20%20%20def%20simulate(n%2C%20seed%2C%20*%2C%20heterogeneous)%3A%0A%20%20%20%20%20%20%20%20%22%22%22One%20row%20per%20user%2C%20with%20the%20per-unit%20truth%20carried%20alongside.%22%22%22%0A%20%20%20%20%20%20%20%20rng%20%3D%20np.random.default_rng(seed)%0A%0A%20%20%20%20%20%20%20%20%23%20Strictly%20pre-exposure%20covariates%3A%20measured%20before%20assignment%2C%20so%0A%20%20%20%20%20%20%20%20%23%20nothing%20here%20can%20be%20touched%20by%20the%20treatment.%0A%20%20%20%20%20%20%20%20spend%20%3D%20rng.lognormal(3.0%2C%200.35%2C%20n)%20%20%23%20dollars%20in%20the%2030%20days%20before%0A%20%20%20%20%20%20%20%20tenure%20%3D%20rng.gamma(2.0%2C%203.0%2C%20n)%20%20%23%20months%20on%20the%20platform%0A%20%20%20%20%20%20%20%20platform%20%3D%20rng.choice(PLATFORMS%2C%20size%3Dn%2C%20p%3D(0.35%2C%200.45%2C%200.20))%0A%20%20%20%20%20%20%20%20sessions%20%3D%20rng.poisson(8.0%2C%20n).astype(float)%0A%20%20%20%20%20%20%20%20referrals%20%3D%20rng.poisson(0.6%2C%20n).astype(float)%0A%20%20%20%20%20%20%20%20region%20%3D%20rng.choice(REGIONS%2C%20size%3Dn%2C%20p%3D(0.40%2C%200.30%2C%200.20%2C%200.10))%0A%20%20%20%20%20%20%20%20z_spend%20%3D%20(spend%20-%20spend.mean())%20%2F%20spend.std()%0A%0A%20%20%20%20%20%20%20%20tau%20%3D%20np.full(n%2C%20BASE_TAU)%0A%20%20%20%20%20%20%20%20if%20heterogeneous%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20tau%20%3D%20tau%20%2B%20SPEND_SLOPE%20*%20z_spend%20%2B%20IOS_BONUS%20*%20(platform%20%3D%3D%20%22ios%22)%0A%0A%20%20%20%20%20%20%20%20variant%20%3D%20np.where(rng.random(n)%20%3C%200.5%2C%20%22treatment%22%2C%20%22control%22)%0A%20%20%20%20%20%20%20%20treated%20%3D%20(variant%20%3D%3D%20%22treatment%22).astype(float)%0A%0A%20%20%20%20%20%20%20%20%23%20Prognostic%2C%20not%20moderating%3A%20these%20four%20move%20revenue%20in%20BOTH%20arms%20and%0A%20%20%20%20%20%20%20%20%23%20leave%20tau%20alone%2C%20which%20is%20exactly%20what%20%60adjust%3D%60%20covariates%20are%20for.%0A%20%20%20%20%20%20%20%20%23%20The%20%60min(tenure%2C%206)%60%20kink%20is%20why%20tenure%20is%20declared%20with%20knots%20below.%0A%20%20%20%20%20%20%20%20baseline%20%3D%20(%0A%20%20%20%20%20%20%20%20%20%20%20%201.0%0A%20%20%20%20%20%20%20%20%20%20%20%20%2B%200.30%20*%20z_spend%0A%20%20%20%20%20%20%20%20%20%20%20%20%2B%200.12%20*%20np.minimum(tenure%2C%206.0)%0A%20%20%20%20%20%20%20%20%20%20%20%20%2B%200.10%20*%20(platform%20%3D%3D%20%22web%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%2B%200.02%20*%20sessions%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20revenue%20%3D%20baseline%20%2B%20treated%20*%20tau%20%2B%20rng.normal(0.0%2C%20NOISE_SD%2C%20n)%0A%0A%20%20%20%20%20%20%20%20return%20pd.DataFrame(%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22user_id%22%3A%20%5Bf%22u%7Bi%3A05d%7D%22%20for%20i%20in%20range(n)%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22variant%22%3A%20variant%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22revenue%22%3A%20revenue%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22spend%22%3A%20spend%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22tenure%22%3A%20tenure%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22platform%22%3A%20platform%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22sessions%22%3A%20sessions%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22referrals%22%3A%20referrals%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22region%22%3A%20region%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Never%20fed%20to%20any%20estimator%20-%20the%20answer%20key%2C%20on%20screen.%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22tau_true%22%3A%20tau%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20return%20BASE_TAU%2C%20IOS_BONUS%2C%20SPEND_SLOPE%2C%20simulate%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(simulate)%3A%0A%20%20%20%20hetero_df%20%3D%20simulate(20_000%2C%2020260812%2C%20heterogeneous%3DTrue)%0A%20%20%20%20hetero_df.head(8).round(3)%0A%20%20%20%20return%20(hetero_df%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(BASE_TAU%2C%20IOS_BONUS%2C%20SPEND_SLOPE%2C%20hetero_df%2C%20mo)%3A%0A%20%20%20%20_tau%20%3D%20hetero_df%5B%22tau_true%22%5D%0A%20%20%20%20_shares%20%3D%20hetero_df%5B%22variant%22%5D.value_counts()%0A%20%20%20%20mo.md(f%22%22%22%0A%20%20%20%20%23%23%20Simulated%20cohorts%0A%0A%20%20%20%20**Cohort%20A%3A**%20%7Blen(hetero_df)%3A%2C%7D%20users%20(%7B_shares%5B%22control%22%5D%3A%2C%7D%20control%2C%0A%20%20%20%20%7B_shares%5B%22treatment%22%5D%3A%2C%7D%20treatment).%0A%0A%20%20%20%20%60tau(x)%20%3D%20%7BBASE_TAU%3A.2f%7D%20%2B%20%7BSPEND_SLOPE%3A.2f%7D%20%C3%97%20z(spend)%20%2B%20%7BIOS_BONUS%3A.2f%7D%20%C3%97%201%5Bios%5D%60%0A%0A%20%20%20%20True%20effects%20average%20**%7B_tau.mean()%3A%2B.4f%7D**%20with%20standard%20deviation%0A%20%20%20%20**%7B_tau.std()%3A.4f%7D**.%20Spend%20and%20iOS%20drive%20heterogeneity%3B%20the%20other%20covariates%0A%20%20%20%20improve%20precision%20only.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%201.%20Confirm%20the%20average%20effect%0A%0A%20%20%20%20Start%20with%20the%20ordinary%20experiment%20readout.%20HTE%20is%20a%20follow-up%2C%20not%20a%20substitute%0A%20%20%20%20for%20the%20headline%20effect.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(Analysis%2C%20hetero_df)%3A%0A%20%20%20%20headline%20%3D%20Analysis.from_unit_summary(%0A%20%20%20%20%20%20%20%20hetero_df%2C%0A%20%20%20%20%20%20%20%20unit%3D%22user_id%22%2C%0A%20%20%20%20%20%20%20%20group%3D%22variant%22%2C%0A%20%20%20%20%20%20%20%20control%3D%22control%22%2C%0A%20%20%20%20%20%20%20%20metrics%3D%7B%22revenue%22%3A%20%22mean%22%7D%2C%0A%20%20%20%20).run()%0A%20%20%20%20return%20(headline%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(headline%2C%20hetero_df%2C%20mo)%3A%0A%0A%20%20%20%20_control_mean%20%3D%20hetero_df.loc%5Bhetero_df%5B%22variant%22%5D%20%3D%3D%20%22control%22%2C%20%22revenue%22%5D.mean()%0A%20%20%20%20_truth_relative%20%3D%20hetero_df%5B%22tau_true%22%5D.mean()%20%2F%20_control_mean%0A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%22%5Cn%22.join(%0A%20%20%20%20%20%20%20%20%20%20%20%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20*(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22-%20**Estimated%20lift%3A**%20%7Br.lift.value%3A%2B.2%25%7D%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22%5B%7Br.lift.lb%3A%2B.2%25%7D%2C%20%7Br.lift.ub%3A%2B.2%25%7D%5D%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20for%20r%20in%20headline%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22-%20**True%20lift%3A**%20%7B_truth_relative%3A%2B.2%25%7D%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%202.%20Estimate%20how%20effects%20vary%0A%0A%20%20%20%20-%20Every%20%60interact%3D%60%20covariate%20also%20enters%20as%20a%20main%20effect%2C%20so%20it%20both%20adjusts%0A%20%20%20%20%20%20for%20that%20covariate%20and%20allows%20the%20treatment%20effect%20to%20vary%20with%20it.%0A%20%20%20%20-%20%60adjust%3D%60%20only%20controls.%20Use%20it%20when%20you%20deliberately%20assume%20the%0A%20%20%20%20%20%20covariate%20can%20explain%20variance%20but%20not%20be%20an%20effect%20modifier.%0A%0A%20%20%20%20Real%20users%20rarely%20know%20that%20distinction%20in%20advance.%20Tenure%20is%20a%20plausible%20effect%0A%20%20%20%20modifier%2C%20so%20this%20example%20includes%20it%20in%20%60interact%3D%60%20rather%20than%20using%20simulated%0A%20%20%20%20knowledge%20to%20hide%20it%20in%20%60adjust%3D%60.%20The%20fit%20can%20then%20show%20whether%20the%20data%0A%20%20%20%20supports%20tenure%20HTE%2C%20and%20held-out%20validation%20checks%20whether%20the%20resulting%20ranking%0A%20%20%20%20generalizes.%0A%0A%20%20%20%20HTE%20results%20use%20the%20absolute%20outcome%20scale%3A%20revenue%20per%20user.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(Covariate%2C%20estimate_cate%2C%20from_unit_summary%2C%20hetero_df)%3A%0A%20%20%20%20INTERACT%20%3D%20%5B%0A%20%20%20%20%20%20%20%20%22spend%22%2C%0A%20%20%20%20%20%20%20%20Covariate(name%3D%22tenure%22%2C%20knots%3D4)%2C%0A%20%20%20%20%20%20%20%20Covariate(name%3D%22platform%22%2C%20kind%3D%22categorical%22)%2C%0A%20%20%20%20%5D%0A%20%20%20%20ADJUST%20%3D%20%5B%5D%0A%0A%20%20%20%20hetero_src%20%3D%20from_unit_summary(%0A%20%20%20%20%20%20%20%20hetero_df%2C%0A%20%20%20%20%20%20%20%20unit%3D%22user_id%22%2C%0A%20%20%20%20%20%20%20%20group%3D%22variant%22%2C%0A%20%20%20%20%20%20%20%20control%3D%22control%22%2C%0A%20%20%20%20%20%20%20%20metrics%3D%7B%22revenue%22%3A%20%22mean%22%7D%2C%0A%20%20%20%20)%0A%20%20%20%20fit%20%3D%20estimate_cate(%0A%20%20%20%20%20%20%20%20hetero_src%2C%0A%20%20%20%20%20%20%20%20%22revenue%22%2C%0A%20%20%20%20%20%20%20%20control%3D%22control%22%2C%0A%20%20%20%20%20%20%20%20interact%3DINTERACT%2C%0A%20%20%20%20%20%20%20%20adjust%3DADJUST%2C%0A%20%20%20%20)%0A%20%20%20%20return%20ADJUST%2C%20INTERACT%2C%20fit%2C%20hetero_src%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(IOS_BONUS%2C%20SPEND_SLOPE%2C%20ct%2C%20fit%2C%20hetero_df%2C%20mo%2C%20pd)%3A%0A%20%20%20%20_truth_by_column%20%3D%20%7B%0A%20%20%20%20%20%20%20%20%22d%3Aspend%22%3A%20SPEND_SLOPE%2C%0A%20%20%20%20%20%20%20%20%22d%3Aplatform%3Dios%22%3A%20IOS_BONUS%2C%0A%20%20%20%20%20%20%20%20%22d%3Aplatform%3Dweb%22%3A%200.0%2C%0A%20%20%20%20%7D%0A%20%20%20%20_interaction_frame%20%3D%20pd.DataFrame(%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22interaction%22%3A%20%5Be.name%20for%20e%20in%20fit.interactions%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22estimate%22%3A%20%5Be.coef%20for%20e%20in%20fit.interactions%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22lb%22%3A%20%5Be.lb%20for%20e%20in%20fit.interactions%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22ub%22%3A%20%5Be.ub%20for%20e%20in%20fit.interactions%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22truth%22%3A%20%5B_truth_by_column.get(e.name%2C%200.0)%20for%20e%20in%20fit.interactions%5D%2C%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20)%0A%20%20%20%20_interaction_table%20%3D%20(%0A%20%20%20%20%20%20%20%20ct.CoefTable(_interaction_frame%2C%20rows%3D%22interaction%22)%0A%20%20%20%20%20%20%20%20.estimate(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Estimate%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22estimate%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20ci%3D(%22lb%22%2C%20%22ub%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20fmt%3Dct.Number(decimals%3D4%2C%20signed%3DTrue)%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20.forest(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Estimate%20plot%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20of%3D%22Estimate%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20annotations%3D(ct.Rule(%22truth%22%2C%20axis%3D%22x%22%2C%20color%3D%22red%22)%2C)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20width%3D240%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20.header(%22Estimated%20Interaction%20effects%22%2C%20%22Ground-truth%20in%20red%22)%0A%20%20%20%20)%0A%0A%20%20%20%20mo.vstack(%0A%20%20%20%20%20%20%20%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%5Cn%22.join(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22**Average%20effect%3A**%20%7Bfit.ate%3A%2B.4f%7D%20%5B%7Bfit.lb%3A%2B.4f%7D%2C%20%7Bfit.ub%3A%2B.4f%7D%5D%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22(truth%20%7Bhetero_df%5B'tau_true'%5D.mean()%3A%2B.4f%7D)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22Covariate%20adjustment%20reduced%20the%20standard%20error%20from%20%7Bfit.se_unadjusted%3A.5f%7D%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22to%20%7Bfit.se%3A.5f%7D%20(**%7Bfit.se_reduction%3A.1%25%7D**).%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22**Joint%20heterogeneity%20test%3A**%20%CF%87%C2%B2%3D%7Bfit.heterogeneity.statistic%3A.1f%7D%2C%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22df%3D%7Bfit.heterogeneity.df%7D%2C%20p%3D%7Bfit.heterogeneity.p_value%3A.2g%7D%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22**Pruned%20columns%3A**%20%60%7Bfit.pruned%20or%20'()'%7D%60%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20_interaction_table%2C%0A%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(BASE_TAU%2C%20IOS_BONUS%2C%20SPEND_SLOPE%2C%20ct%2C%20fit%2C%20hetero_df%2C%20mo%2C%20np%2C%20pd)%3A%0A%20%20%20%20_p10%2C%20_p90%20%3D%20(float(np.quantile(hetero_df%5B%22spend%22%5D%2C%20q))%20for%20q%20in%20(0.10%2C%200.90))%0A%20%20%20%20_tenure%20%3D%20float(hetero_df%5B%22tenure%22%5D.median())%0A%20%20%20%20_high%20%3D%20%7B%22spend%22%3A%20_p90%2C%20%22tenure%22%3A%20_tenure%2C%20%22platform%22%3A%20%22ios%22%7D%0A%20%20%20%20_low%20%3D%20%7B%22spend%22%3A%20_p10%2C%20%22tenure%22%3A%20_tenure%2C%20%22platform%22%3A%20%22android%22%7D%0A%0A%20%20%20%20_z%20%3D%20(np.array(%5B_p10%2C%20_p90%5D)%20-%20hetero_df%5B%22spend%22%5D.mean())%20%2F%20hetero_df%5B%22spend%22%5D.std()%0A%20%20%20%20_truth_high%20%3D%20BASE_TAU%20%2B%20SPEND_SLOPE%20*%20_z%5B1%5D%20%2B%20IOS_BONUS%0A%20%20%20%20_truth_low%20%3D%20BASE_TAU%20%2B%20SPEND_SLOPE%20*%20_z%5B0%5D%0A%0A%20%20%20%20_high_est%20%3D%20fit.cate(_high)%0A%20%20%20%20_low_est%20%3D%20fit.cate(_low)%0A%20%20%20%20_gap%20%3D%20fit.contrast(_high%2C%20_low)%0A%20%20%20%20_compare_frame%20%3D%20pd.DataFrame(%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22user%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22iOS%2C%2090th-pct%20spend%20(%24%7B_p90%3A%2C.0f%7D)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22Android%2C%2010th-pct%20spend%20(%24%7B_p10%3A%2C.0f%7D)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22estimate%22%3A%20%5B_high_est.value%2C%20_low_est.value%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22lb%22%3A%20%5B_high_est.lb%2C%20_low_est.lb%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22ub%22%3A%20%5B_high_est.ub%2C%20_low_est.ub%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22truth%22%3A%20%5B_truth_high%2C%20_truth_low%5D%2C%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20)%0A%20%20%20%20_compare_table%20%3D%20(%0A%20%20%20%20%20%20%20%20ct.CoefTable(_compare_frame%2C%20rows%3D%22user%22)%0A%20%20%20%20%20%20%20%20.estimate(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Estimate%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22estimate%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20ci%3D(%22lb%22%2C%20%22ub%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20fmt%3Dct.Number(decimals%3D4%2C%20signed%3DTrue)%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20.forest(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Estimate%20plot%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20of%3D%22Estimate%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20annotations%3D(ct.Rule(%22truth%22%2C%20axis%3D%22x%22%2C%20color%3D%22red%22)%2C)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20width%3D240%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20symmetric%3DTrue%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20.header(%22Compare%20two%20users%22%2C%20%22True%20conditional%20effects%20in%20red%22)%0A%20%20%20%20)%0A%0A%20%20%20%20mo.vstack(%0A%20%20%20%20%20%20%20%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22Holding%20tenure%20fixed%20at%20its%20median%20isolates%20the%20spend%20and%20platform%20contrast.%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22%5Cn%5Cn**Direct%20contrast%3A**%20%7B_gap.value%3A%2B.4f%7D%20%5B%7B_gap.lb%3A%2B.4f%7D%2C%20%7B_gap.ub%3A%2B.4f%7D%5D%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22(truth%20%7B_truth_high%20-%20_truth_low%3A%2B.4f%7D)%22%0A%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20_compare_table%2C%0A%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%203.%20Validate%20on%20held-out%20users%0A%0A%20%20%20%20%60validate_cate%60%20fits%20on%20one%20half%20and%20reports%20effects%20from%20the%20other.%20The%20gate%0A%20%20%20%20passes%20when%20the%20held-out%20ranking%20has%20significant%20**AUTOC**%20(Area%20Under%20the%20Targeting-Operator%20Characteristic%20curve).%0A%20%20%20%20Until%20then%2C%20report%20only%20the%20average%20effect.%0A%0A%0A%20%20%20%20AUTOC%2C%20measures%20whether%20the%20model%E2%80%99s%20held-out%20ranking%20is%20useful%3A%0A%0A%20%20%20%201.%20Score%20users%20by%20predicted%20treatment%20effect.%0A%20%20%20%202.%20Sort%20users%20from%20lowest%20to%20highest%20predicted%20effect.%0A%20%20%20%203.%20Compare%20observed%20treatment%20effects%20as%20you%20move%20through%20that%20ranking.%0A%20%20%20%204.%20Integrate%20that%20curve%20into%20one%20statistic.%0A%0A%20%20%20%20**Interpretation**%3A%0A%0A%20%20%20%20-%20AUTOC%20%3E%200%3A%20higher-ranked%20users%20tend%20to%20have%20larger%20treatment%20effects.%0A%20%20%20%20-%20AUTOC%20%E2%89%88%200%3A%20ranking%20is%20no%20better%20than%20random.%0A%20%20%20%20-%20AUTOC%20%3C%200%3A%20ranking%20is%20backwards.%0A%20%20%20%20-%20Its%20one-sided%20p-value%20tests%20whether%20the%20ranking%20carries%20real%20signal.%0A%20%20%20%20-%20The%20validation%20gate%20passes%20when%20autoc.p_value%20%3C%20alpha.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(ADJUST%2C%20INTERACT%2C%20hetero_src%2C%20validate_cate)%3A%0A%20%20%20%20validation%20%3D%20validate_cate(%0A%20%20%20%20%20%20%20%20hetero_src%2C%0A%20%20%20%20%20%20%20%20%22revenue%22%2C%0A%20%20%20%20%20%20%20%20control%3D%22control%22%2C%0A%20%20%20%20%20%20%20%20interact%3DINTERACT%2C%0A%20%20%20%20%20%20%20%20adjust%3DADJUST%2C%0A%20%20%20%20%20%20%20%20n_groups%3D5%2C%0A%20%20%20%20)%0A%20%20%20%20return%20(validation%2C)%0A%0A%0A%40app.cell%0Adef%20_(validation)%3A%0A%20%20%20%20%23%20looking%20at%20an%20example%20output%0A%20%20%20%20validation.groups%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20def%20display_number(value%2C%20spec%3D%22%2B.4f%22)%3A%0A%20%20%20%20%20%20%20%20return%20%22unavailable%22%20if%20value%20is%20None%20else%20format(value%2C%20spec)%0A%0A%20%20%20%20def%20display_estimate(estimate)%3A%0A%20%20%20%20%20%20%20%20if%20estimate%20is%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20%22unavailable%22%0A%20%20%20%20%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22%7Bdisplay_number(estimate.value)%7D%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22%5B%7Bdisplay_number(estimate.lb)%7D%2C%20%7Bdisplay_number(estimate.ub)%7D%5D%22%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20return%20display_estimate%2C%20display_number%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(display_estimate%2C%20display_number%2C%20mo%2C%20validation)%3A%0A%20%20%20%20mo.md(f%22%22%22%0A%20%20%20%20-%20**Train%20%2F%20holdout%3A**%20%7Bvalidation.n_train%3A%2C%7D%20%2F%20%7Bvalidation.n_holdout%3A%2C%7D%20users%0A%20%20%20%20-%20**Gate%3A**%20%7B%22PASSED%22%20if%20validation.passed%20else%20%22FAILED%22%7D%20(%CE%B1%3D%7Bvalidation.alpha%7D)%0A%20%20%20%20-%20**AUTOC%3A**%20%7Bdisplay_number(validation.autoc.estimate)%7D%20(p%3D%7Bdisplay_number(validation.autoc.p_value%2C%20%22.2g%22)%7D)%0A%20%20%20%20-%20**Qini%3A**%20%7Bdisplay_number(validation.qini.estimate)%7D%20(p%3D%7Bdisplay_number(validation.qini.p_value%2C%20%22.2g%22)%7D)%0A%20%20%20%20-%20**Holdout%20ATE%3A**%20%7Bdisplay_estimate(validation.holdout_ate)%7D%0A%20%20%20%20-%20**Rank%20uncertainty%3A**%20%7Bvalidation.autoc.unavailable_reason%20or%20%22available%22%7D%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%20GATES%20on%20held-out%20users%0A%0A%20%20%20%20GATES%20(Group%20Average%20Treatment%20Effects%20on%20the%20Selected)%20evaluates%20whether%20the%0A%20%20%20%20model%E2%80%99s%20treatment-effect%20ranking%20generalizes%20beyond%20the%20data%20used%20to%20fit%20it.%0A%0A%20%20%20%20We%20fit%20the%20model%20on%20a%20training%20sample%2C%20use%20it%20to%20predict%20treatment%20effects%20for%0A%20%20%20%20untouched%20holdout%20users%2C%20sort%20those%20users%20by%20predicted%20effect%2C%20and%20divide%20them%20into%0A%20%20%20%20K%3D5%20equally%20sized%20score%20groups.%20Within%20each%20group%2C%20we%20estimate%20the%20treatment%20effect%0A%20%20%20%20using%20only%20the%20held-out%20outcomes.%0A%0A%20%20%20%20The%20group%20estimates%20will%20not%20match%20the%20predictions%20exactly%3B%20they%20are%20noisy%20estimates%0A%20%20%20%20with%20their%20own%20confidence%20intervals.%20The%20important%20pattern%20is%20that%20the%20measured%0A%20%20%20%20effect%20increases%20steadily%20from%20the%20lowest-%20to%20the%20highest-ranked%20group.%20That%0A%20%20%20%20monotone%20relationship%20indicates%20that%20the%20model%E2%80%99s%20ranking%20is%20informative%20out%20of%0A%20%20%20%20sample%2C%20rather%20than%20merely%20fitting%20noise%20in%20the%20training%20data.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(ct%2C%20display_number%2C%20pd)%3A%0A%20%20%20%20def%20gates_table(validation%2C%20truth%2C%20title)%3A%0A%20%20%20%20%20%20%20%20%22%22%22Held-out%20effect%20per%20predicted-effect%20group%2C%20with%20its%20interval.%22%22%22%0A%20%20%20%20%20%20%20%20groups%20%3D%20list(validation.groups)%0A%20%20%20%20%20%20%20%20group_labels%20%3D%20%5Bstr(group.group)%20for%20group%20in%20groups%5D%0A%20%20%20%20%20%20%20%20group_labels%5B0%5D%20%2B%3D%20%22%20(lowest)%22%0A%20%20%20%20%20%20%20%20group_labels%5B-1%5D%20%2B%3D%20%22%20(highest)%22%0A%0A%20%20%20%20%20%20%20%20frame%20%3D%20pd.DataFrame(%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22group%22%3A%20group_labels%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22n_display%22%3A%20%5Bf%22%7Bgroup.n%3A%2C%7D%22%20for%20group%20in%20groups%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22effect%22%3A%20%5Bgroup.effect%20for%20group%20in%20groups%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22lb%22%3A%20%5Bgroup.lb%20for%20group%20in%20groups%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22ub%22%3A%20%5Bgroup.ub%20for%20group%20in%20groups%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22mean_score_display%22%3A%20%5Bdisplay_number(group.mean_score)%20for%20group%20in%20groups%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22uncertainty%22%3A%20%5Bgroup.unavailable_reason%20or%20%22available%22%20for%20group%20in%20groups%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20ct.CoefTable(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20frame%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20rows%3D%22group%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20direction%3D%22neutral%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20title%3Dtitle%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20subtitle%3Df%22Dashed%20line%3A%20true%20average%20effect%20(%7Btruth%3A%2B.4f%7D)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20sort_rows%3DFalse%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20.estimate(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22Held-out%20effect%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22effect%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20ci%3D(%22lb%22%2C%20%22ub%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20fmt%3Dct.Number(decimals%3D4%2C%20signed%3DTrue)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20.forest(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22Effect%20plot%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20of%3D%22Held-out%20effect%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20ref%3Dtruth%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20width%3D220%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20.passthrough(%22Holdout%20n%22%2C%20%22n_display%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20.passthrough(%22Mean%20predicted%20effect%22%2C%20%22mean_score_display%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20.passthrough(%22Uncertainty%22%2C%20%22uncertainty%22)%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20return%20(gates_table%2C)%0A%0A%0A%40app.cell%0Adef%20_(gates_table%2C%20hetero_df%2C%20validation)%3A%0A%20%20%20%20gates_table(%0A%20%20%20%20%20%20%20%20validation%2C%0A%20%20%20%20%20%20%20%20float(hetero_df%5B%22tau_true%22%5D.mean())%2C%0A%20%20%20%20%20%20%20%20%22Cohort%20A%3A%20GATES%20holdout%20ranking%22%2C%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%20CLAN%20profile%20table%0A%0A%20%20%20%20The%20CLAN%20profile%20is%20a%20descriptive%20explanation%20of%20who%20the%20model%20ranked%20as%20most%20versus%0A%20%20%20%20least%20affected%20-%20it%20tries%20to%20ask%20of%20GATES%2C%20%22who%20is%20in%20each%20group%3F%22%0A%0A%20%20%20%20For%20each%20covariate%2C%20it%20compares%20the%20mean%20in%20the%20most%20affected%20group%20(group%205)%20against%20the%20mean%20in%20the%20least%20affected%20group%20(group%201).%0A%0A%0A%0A%20%20%20%20-%20Most%20affected%3A%20Group%205%2C%20the%20highest%20predicted-effect%20quintile.%0A%20%20%20%20-%20Least%20affected%3A%20Group%201%2C%20the%20lowest%20predicted-effect%20quintile.%0A%20%20%20%20-%20Difference%3A%20Most%20affected%20minus%20least%20affected.%0A%20%20%20%20-%2095%25%20interval%3A%20Uncertainty%20around%20that%20covariate%20difference.%0A%0A%20%20%20%20It%20does%20not%20estimate%20another%20treatment%20effect.%20It%20profiles%20the%20covariates%20associated%0A%20%20%20%20with%20the%20model%E2%80%99s%20ranking%2C%20using%20held-out%20users.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(validation)%3A%0A%20%20%20%20%23%20looking%20at%20example%20output%0A%20%20%20%20validation.clan%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(ct%2C%20display_number%2C%20hetero_df%2C%20pd%2C%20validation)%3A%0A%20%20%20%20_groups%20%3D%20validation.groups%0A%20%20%20%20_pairs%20%3D%20list(zip(_groups%2C%20_groups%5B1%3A%5D%2C%20strict%3DFalse))%0A%20%20%20%20_monotone%20%3D%20all(%0A%20%20%20%20%20%20%20%20a.effect%20is%20not%20None%20and%20b.effect%20is%20not%20None%20and%20a.effect%20%3C%3D%20b.effect%20for%20a%2C%20b%20in%20_pairs%0A%20%20%20%20)%0A%0A%20%20%20%20%23%20Normalize%20only%20the%20visualization%3A%20keep%20the%20raw%20CLAN%20contrasts%20readable%0A%20%20%20%20%23%20while%20putting%20mixed%20covariates%20on%20a%20common%2C%20dimensionless%20forest%20scale.%0A%20%20%20%20def%20_profile_values(name)%3A%0A%20%20%20%20%20%20%20%20if%20%22%3D%22%20in%20name%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20column%2C%20level%20%3D%20name.split(%22%3D%22%2C%201)%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20(hetero_df%5Bcolumn%5D.astype(str)%20%3D%3D%20level).astype(float)%0A%20%20%20%20%20%20%20%20return%20hetero_df%5Bname%5D.astype(float)%0A%0A%20%20%20%20_clan_frame%20%3D%20pd.DataFrame(%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22covariate%22%3A%20%5Br.covariate%20for%20r%20in%20validation.clan%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22uncertainty%22%3A%20%5Br.unavailable_reason%20or%20%22available%22%20for%20r%20in%20validation.clan%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22difference_display%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20display_number(r.diff%2C%20%22%2B.1%25%22%20if%20%22%3D%22%20in%20r.covariate%20else%20%22%2B.3f%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20for%20r%20in%20validation.clan%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22standardized%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20r.diff%20%2F%20float(_profile_values(r.covariate).std(ddof%3D1))%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20r.diff%20is%20not%20None%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20else%20None%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20for%20r%20in%20validation.clan%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22standardized_lb%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20r.lb%20%2F%20float(_profile_values(r.covariate).std(ddof%3D1))%20if%20r.lb%20is%20not%20None%20else%20None%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20for%20r%20in%20validation.clan%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22standardized_ub%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20r.ub%20%2F%20float(_profile_values(r.covariate).std(ddof%3D1))%20if%20r.ub%20is%20not%20None%20else%20None%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20for%20r%20in%20validation.clan%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22most_display%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20display_number(r.mean_most%2C%20%22.1%25%22%20if%20%22%3D%22%20in%20r.covariate%20else%20%22.3f%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20for%20r%20in%20validation.clan%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22least_display%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20display_number(r.mean_least%2C%20%22.1%25%22%20if%20%22%3D%22%20in%20r.covariate%20else%20%22.3f%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20for%20r%20in%20validation.clan%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20)%0A%20%20%20%20_clan_table%20%3D%20(%0A%20%20%20%20%20%20%20%20ct.CoefTable(_clan_frame%2C%20rows%3D%22covariate%22)%0A%20%20%20%20%20%20%20%20.estimate(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Difference%20(SD)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22standardized%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20ci%3D(%22standardized_lb%22%2C%20%22standardized_ub%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20fmt%3Dct.Number(decimals%3D2%2C%20signed%3DTrue)%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20.forest(%22Standardized%20difference%22%2C%20of%3D%22Difference%20(SD)%22%2C%20ref%3D0.0%2C%20width%3D220%2C%20symmetric%3DTrue)%0A%20%20%20%20%20%20%20%20.passthrough(%22Raw%20difference%22%2C%20%22difference_display%22)%0A%20%20%20%20%20%20%20%20.passthrough(%22Most%20affected%22%2C%20%22most_display%22)%0A%20%20%20%20%20%20%20%20.passthrough(%22Least%20affected%22%2C%20%22least_display%22)%0A%20%20%20%20%20%20%20%20.passthrough(%22Uncertainty%22%2C%20%22uncertainty%22)%0A%20%20%20%20%20%20%20%20.header(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22CLAN%20profile%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Forest%20plot%20uses%20covariate%20differences%20in%20pooled%20SD%20units%3B%20raw%20values%20remain%20in%20the%20table%22%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20)%0A%0A%20%20%20%20_clan_table%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%204.%20Check%20the%20false-discovery%20trap%0A%0A%20%20%20%20We%20simulated%20another%20example%20to%20see%20what%20happens%20when%20there%20is%20no%20heterogeneity%20-%20do%20we%20report%20false%20positives%3F%0A%0A%20%20%20%20Cohort%20B%20has%20a%20constant%20true%20effect%3A%20no%20heterogeneity%20exists.%20A%20wide%20model%20on%20a%0A%20%20%20%20smaller%20cohort%20still%20produces%20an%20in-sample%20%E2%80%9Ctop%E2%80%9D%20group.%20Held-out%0A%20%20%20%20validation%20should%20reject%20that%20story%2C%20however.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(Covariate%2C%20np)%3A%0A%20%20%20%20CANDIDATES%20%3D%20%5B%0A%20%20%20%20%20%20%20%20Covariate(name%3D%22spend%22%2C%20knots%3D4)%2C%0A%20%20%20%20%20%20%20%20Covariate(name%3D%22tenure%22%2C%20knots%3D4)%2C%0A%20%20%20%20%20%20%20%20Covariate(name%3D%22platform%22%2C%20kind%3D%22categorical%22)%2C%0A%20%20%20%20%20%20%20%20Covariate(name%3D%22sessions%22%2C%20knots%3D2)%2C%0A%20%20%20%20%20%20%20%20Covariate(name%3D%22referrals%22)%2C%0A%20%20%20%20%20%20%20%20Covariate(name%3D%22region%22%2C%20kind%3D%22categorical%22)%2C%0A%20%20%20%20%5D%0A%20%20%20%20COVARIATE_COLUMNS%20%3D%20(%22spend%22%2C%20%22tenure%22%2C%20%22platform%22%2C%20%22sessions%22%2C%20%22referrals%22%2C%20%22region%22)%0A%0A%20%20%20%20def%20top_group_in_sample(df%2C%20cate_result%2C%20n_groups%3D5)%3A%0A%20%20%20%20%20%20%20%20%22%22%22The%20naive%20number%3A%20rank%20the%20fitted%20units%20by%20their%20OWN%20predicted%0A%20%20%20%20%20%20%20%20effect%2C%20then%20read%20the%20difference%20in%20means%20inside%20the%20top%20group.%0A%0A%20%20%20%20%20%20%20%20Same%20quantile%20binning%20%60validate_cate%60%20uses%20-%20the%20only%20difference%20is%0A%20%20%20%20%20%20%20%20that%20these%20units%20chose%20the%20ranking%20with%20the%20very%20outcomes%20being%0A%20%20%20%20%20%20%20%20averaged%20back.%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20score%20%3D%20cate_result.score(%0A%20%20%20%20%20%20%20%20%20%20%20%20%7Bc%3A%20df%5Bc%5D.to_numpy()%20for%20c%20in%20COVARIATE_COLUMNS%7D%2C%20deploy_grain%3D%22unit%22%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20edges%20%3D%20np.quantile(score%2C%20np.arange(1%2C%20n_groups)%20%2F%20n_groups)%0A%20%20%20%20%20%20%20%20top%20%3D%20np.searchsorted(edges%2C%20score%2C%20side%3D%22left%22)%20%3D%3D%20n_groups%20-%201%0A%20%20%20%20%20%20%20%20y%20%3D%20df%5B%22revenue%22%5D.to_numpy()%0A%20%20%20%20%20%20%20%20treated%20%3D%20df%5B%22variant%22%5D.to_numpy()%20%3D%3D%20%22treatment%22%0A%20%20%20%20%20%20%20%20return%20float(y%5Btop%20%26%20treated%5D.mean()%20-%20y%5Btop%20%26%20~treated%5D.mean())%0A%0A%20%20%20%20return%20CANDIDATES%2C%20COVARIATE_COLUMNS%2C%20top_group_in_sample%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20CANDIDATES%2C%0A%20%20%20%20estimate_cate%2C%0A%20%20%20%20from_unit_summary%2C%0A%20%20%20%20simulate%2C%0A%20%20%20%20top_group_in_sample%2C%0A%20%20%20%20validate_cate%2C%0A)%3A%0A%20%20%20%20null_df%20%3D%20simulate(1_200%2C%2020260813%2C%20heterogeneous%3DFalse)%0A%20%20%20%20null_src%20%3D%20from_unit_summary(%0A%20%20%20%20%20%20%20%20null_df%2C%0A%20%20%20%20%20%20%20%20unit%3D%22user_id%22%2C%0A%20%20%20%20%20%20%20%20group%3D%22variant%22%2C%0A%20%20%20%20%20%20%20%20control%3D%22control%22%2C%0A%20%20%20%20%20%20%20%20metrics%3D%7B%22revenue%22%3A%20%22mean%22%7D%2C%0A%20%20%20%20)%0A%0A%20%20%20%20%23%20The%20naive%20path%3A%20one%20fit%20on%20everything%2C%20then%20read%20its%20own%20top%20quintile.%0A%20%20%20%20null_fit%20%3D%20estimate_cate(null_src%2C%20%22revenue%22%2C%20control%3D%22control%22%2C%20interact%3DCANDIDATES)%0A%20%20%20%20null_in_sample%20%3D%20top_group_in_sample(null_df%2C%20null_fit)%0A%0A%20%20%20%20%23%20The%20honest%20path%3A%20fit%20on%20half%2C%20re-estimate%20on%20the%20other%20half.%0A%20%20%20%20null_validation%20%3D%20validate_cate(%0A%20%20%20%20%20%20%20%20null_src%2C%20%22revenue%22%2C%20control%3D%22control%22%2C%20interact%3DCANDIDATES%2C%20n_groups%3D5%0A%20%20%20%20)%0A%20%20%20%20return%20null_fit%2C%20null_in_sample%2C%20null_src%2C%20null_validation%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(%0A%20%20%20%20BASE_TAU%2C%0A%20%20%20%20display_estimate%2C%0A%20%20%20%20display_number%2C%0A%20%20%20%20mo%2C%0A%20%20%20%20null_fit%2C%0A%20%20%20%20null_in_sample%2C%0A%20%20%20%20null_validation%2C%0A)%3A%0A%20%20%20%20_honest_top%20%3D%20null_validation.groups%5B-1%5D%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%22%5Cn%22.join(%0A%20%20%20%20%20%20%20%20%20%20%20%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22Every%20user's%20true%20effect%20is%20**%7BBASE_TAU%3A%2B.4f%7D**%3B%20the%20model%20fits%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22%7Blen(null_fit.interactions)%7D%20interaction%20columns%20on%201%2C200%20users.%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%7C%20estimate%20%7C%20value%20%7C%20distance%20from%20truth%20%7C%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%7C%20--%20%7C%20--%20%7C%20--%20%7C%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22%7C%20In-sample%20top%20quintile%20%7C%20%7Bnull_in_sample%3A%2B.4f%7D%20%7C%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22**%7Bnull_in_sample%20-%20BASE_TAU%3A%2B.4f%7D**%20%7C%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22%7C%20Held-out%20top%20group%20%7C%20%7Bdisplay_number(_honest_top.effect)%7D%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22%5B%7Bdisplay_number(_honest_top.lb)%7D%2C%20%7Bdisplay_number(_honest_top.ub)%7D%5D%20%7C%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22%7Bdisplay_number(_honest_top.effect%20-%20BASE_TAU%20if%20_honest_top.effect%20is%20not%20None%20else%20None)%7D%20%7C%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22%7C%20Held-out%20ATE%20%7C%20%7Bdisplay_estimate(null_validation.holdout_ate)%7D%20%7C%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22%7Bdisplay_number(null_validation.holdout_ate.value%20-%20BASE_TAU%20if%20null_validation.holdout_ate%20is%20not%20None%20else%20None)%7D%20%7C%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22-%20Joint-test%20p-value%3A%20%7Bnull_fit.heterogeneity.p_value%3A.2f%7D%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22-%20Validation%20gate%3A%20**%7B'PASSED'%20if%20null_validation.passed%20else%20'FAILED'%7D**%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20f%22(AUTOC%20p%3D%7Bdisplay_number(null_validation.autoc.p_value%2C%20'.2f')%7D)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(BASE_TAU%2C%20gates_table%2C%20null_validation)%3A%0A%20%20%20%20gates_table(%0A%20%20%20%20%20%20%20%20null_validation%2C%0A%20%20%20%20%20%20%20%20BASE_TAU%2C%0A%20%20%20%20%20%20%20%20%22Cohort%20B%20-%20no%20heterogeneity%20at%20all%22%2C%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Held-out%20effects%20are%20flat%20and%20non-monotone%2C%20with%20every%20interval%20covering%20the%0A%20%20%20%20truth.%20In-sample%20ranking%20creates%20a%20staircase%20from%20noise.%20Next%2C%20repeat%20the%20same%0A%20%20%20%20comparison%20across%2040%20null%20cohorts.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20CANDIDATES%2C%0A%20%20%20%20estimate_cate%2C%0A%20%20%20%20from_unit_summary%2C%0A%20%20%20%20np%2C%0A%20%20%20%20simulate%2C%0A%20%20%20%20top_group_in_sample%2C%0A%20%20%20%20validate_cate%2C%0A)%3A%0A%20%20%20%20_in_sample%2C%20_honest%2C%20_fired%20%3D%20%5B%5D%2C%20%5B%5D%2C%200%0A%20%20%20%20for%20_seed%20in%20range(800_000%2C%20800_040)%3A%0A%20%20%20%20%20%20%20%20df_i%20%3D%20simulate(1_200%2C%20_seed%2C%20heterogeneous%3DFalse)%0A%20%20%20%20%20%20%20%20analysis_i%20%3D%20from_unit_summary(%0A%20%20%20%20%20%20%20%20%20%20%20%20df_i%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20unit%3D%22user_id%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20group%3D%22variant%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20control%3D%22control%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20metrics%3D%7B%22revenue%22%3A%20%22mean%22%7D%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20_in_sample.append(%0A%20%20%20%20%20%20%20%20%20%20%20%20top_group_in_sample(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20df_i%2C%20estimate_cate(analysis_i%2C%20%22revenue%22%2C%20control%3D%22control%22%2C%20interact%3DCANDIDATES)%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20_run%20%3D%20validate_cate(analysis_i%2C%20%22revenue%22%2C%20control%3D%22control%22%2C%20interact%3DCANDIDATES)%0A%20%20%20%20%20%20%20%20_honest.append(_run.groups%5B-1%5D.effect)%0A%20%20%20%20%20%20%20%20_fired%20%2B%3D%20_run.passed%0A%0A%20%20%20%20replications%20%3D%20%7B%0A%20%20%20%20%20%20%20%20%22reps%22%3A%20len(_in_sample)%2C%0A%20%20%20%20%20%20%20%20%22in_sample_mean%22%3A%20float(np.mean(_in_sample))%2C%0A%20%20%20%20%20%20%20%20%22in_sample_mc_se%22%3A%20float(np.std(_in_sample%2C%20ddof%3D1)%20%2F%20np.sqrt(len(_in_sample)))%2C%0A%20%20%20%20%20%20%20%20%22honest_mean%22%3A%20float(np.mean(_honest))%20if%20all(x%20is%20not%20None%20for%20x%20in%20_honest)%20else%20None%2C%0A%20%20%20%20%20%20%20%20%22honest_mc_se%22%3A%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20float(np.std(_honest%2C%20ddof%3D1)%20%2F%20np.sqrt(len(_honest)))%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20all(x%20is%20not%20None%20for%20x%20in%20_honest)%0A%20%20%20%20%20%20%20%20%20%20%20%20else%20None%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%22gate_fired%22%3A%20_fired%2C%0A%20%20%20%20%7D%0A%20%20%20%20return%20(replications%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(BASE_TAU%2C%20display_number%2C%20mo%2C%20replications)%3A%0A%20%20%20%20_r%20%3D%20replications%0A%20%20%20%20_gap%20%3D%20(%0A%20%20%20%20%20%20%20%20f%22%7B(_r%5B'honest_mean'%5D%20-%20BASE_TAU)%20%2F%20_r%5B'honest_mc_se'%5D%3A%2B.1f%7D%22%0A%20%20%20%20%20%20%20%20if%20_r%5B%22honest_mean%22%5D%20is%20not%20None%20and%20_r%5B%22honest_mc_se%22%5D%0A%20%20%20%20%20%20%20%20else%20None%0A%20%20%20%20)%0A%20%20%20%20_comparison%20%3D%20(%0A%20%20%20%20%20%20%20%20f%22%7B_gap%7D%20Monte%20Carlo%20SEs%20%7B'low'%20if%20_gap.startswith('-')%20else%20'high'%7D%20of%20truth%22%0A%20%20%20%20%20%20%20%20if%20_gap%20is%20not%20None%0A%20%20%20%20%20%20%20%20else%20%22uncertainty%20unavailable%22%0A%20%20%20%20)%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20f%22%22%22%0A%20%20%20%20Across%20**%7B_r%5B%22reps%22%5D%7D%20null%20cohorts**%3A%0A%0A%20%20%20%20-%20**In-sample%20top%20group%3A**%20%7B_r%5B%22in_sample_mean%22%5D%3A%2B.4f%7D%0A%20%20%20%20%20%20(%7B_r%5B%22in_sample_mean%22%5D%20%2F%20BASE_TAU%3A.1f%7D%C3%97%20truth)%0A%20%20%20%20-%20**Held-out%20top%20group%3A**%20%7Bdisplay_number(_r%5B%22honest_mean%22%5D)%7D%0A%20%20%20%20%20%20(%7B_comparison%7D)%0A%20%20%20%20-%20**Gate%20passes%3A**%20%7B_r%5B%22gate_fired%22%5D%7D%20%2F%20%7B_r%5B%22reps%22%5D%7D%20cohorts%0A%0A%20%20%20%20%3E%20A%20CATE%20model%20always%20finds%20a%20winner.%20Held-out%20re-estimation%20makes%20the%20ranking%20honest.%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%205.%20Build%20a%20targeting%20rule%0A%0A%20%20%20%20We%20can%20create%20a%20targeting%20rule%20off%20of%20a%20CATE%20model%20-%20this%20can%20be%20used%20to%20decide%20**%22who%20should%20the%20treatment%20be%20applied%20to%3F%22**%0A%0A%20%20%20%20**fraction**%20here%20decides%20the%20share%20of%20units%20the%20rule%20would%20treat.%20Typically%20it%20should%20be%20pre-registered%20to%20avoid%20bias%3B%20looking%20at%20the%20group%20table%20and%20selecting%20the%20cut%20based%20off%20of%20it%20can%20bias%20the%20policy%20value%20upwards%20of%2060-120%25.%20Cross%20validation%20procedures%20can%20also%20be%20used%20to%20select%20the%20optimal%20targeting%20fraction%2C%20though%20not%20shown%20here.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(ADJUST%2C%20INTERACT%2C%20hetero_src%2C%20targeting_rule)%3A%0A%20%20%20%20rule%20%3D%20targeting_rule(%0A%20%20%20%20%20%20%20%20hetero_src%2C%0A%20%20%20%20%20%20%20%20%22revenue%22%2C%0A%20%20%20%20%20%20%20%20control%3D%22control%22%2C%0A%20%20%20%20%20%20%20%20interact%3DINTERACT%2C%0A%20%20%20%20%20%20%20%20adjust%3DADJUST%2C%0A%20%20%20%20%20%20%20%20fraction%3D0.40%2C%0A%20%20%20%20)%0A%20%20%20%20return%20(rule%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(COVARIATE_COLUMNS%2C%20hetero_df%2C%20mo%2C%20rule)%3A%0A%20%20%20%20_selected%20%3D%20rule.predict(%7Bc%3A%20hetero_df%5Bc%5D.to_numpy()%20for%20c%20in%20COVARIATE_COLUMNS%7D)%0A%20%20%20%20_rule_truth%20%3D%20float(hetero_df.loc%5B_selected%2C%20%22tau_true%22%5D.mean())%0A%20%20%20%20_best_possible%20%3D%20float(%0A%20%20%20%20%20%20%20%20hetero_df.loc%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20hetero_df%5B%22tau_true%22%5D%20%3E%3D%20hetero_df%5B%22tau_true%22%5D.quantile(1.0%20-%20rule.fraction)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22tau_true%22%2C%0A%20%20%20%20%20%20%20%20%5D.mean()%0A%20%20%20%20)%0A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20f%22%22%22%0A%20%20%20%20**Recommendation%3A**%20%60%7Brule.recommendation%7D%60.%20Treat%20the%20top%20%7Brule.fraction%3A.0%25%7D%0A%20%20%20%20at%20scores%20%E2%89%A5%20**%7Brule.threshold%3A%2B.4f%7D**.%0A%0A%20%20%20%20-%20**Policy%20value%3A**%20%7Brule.policy_value.value%3A%2B.4f%7D%0A%20%20%20%20%20%20(full-sample%20rule%20truth%20%7B_rule_truth%3A%2B.4f%7D%3B%20oracle%20%7B_best_possible%3A%2B.4f%7D)%0A%20%20%20%20-%20**Uplift%20over%20treating%20everyone%3A**%20%7Brule.uplift_vs_average.value%3A%2B.4f%7D%0A%20%20%20%20-%20**Validation%20passed%3A**%20%60%7Brule.validation.passed%7D%60%0A%0A%20%20%20%20Both%20policy%20numbers%20are%20point%20estimates%20with%20no%20interval%2C%20and%20that%20is%0A%20%20%20%20deliberate%3A%20the%20same%20holdout%20both%20selects%20the%20rule%20(via%20the%20significance%0A%20%20%20%20gate)%20and%20reports%20its%20value%2C%20so%20any%20nominal%20interval%20here%20would%20be%0A%20%20%20%20optimistically%20biased%20by%20the%20selection.%20Treat%20them%20as%20descriptive%20of%20the%0A%20%20%20%20chosen%20rule%2C%20not%20as%20confirmatory%20evidence%20about%20its%20magnitude.%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(CANDIDATES%2C%20null_src%2C%20targeting_rule)%3A%0A%20%20%20%20null_rule%20%3D%20targeting_rule(%0A%20%20%20%20%20%20%20%20null_src%2C%0A%20%20%20%20%20%20%20%20%22revenue%22%2C%0A%20%20%20%20%20%20%20%20control%3D%22control%22%2C%0A%20%20%20%20%20%20%20%20interact%3DCANDIDATES%2C%0A%20%20%20%20%20%20%20%20fraction%3D0.40%2C%0A%20%20%20%20)%0A%20%20%20%20return%20(null_rule%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(display_number%2C%20mo%2C%20null_rule)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20f%22%22%22%0A%20%20%20%20%23%23%23%20The%20same%20rule%20on%20Cohort%20B%0A%0A%20%20%20%20-%20**Recommendation%3A**%20%60%7Bnull_rule.recommendation%7D%60%0A%20%20%20%20-%20%60threshold%3D%7Bnull_rule.threshold%7D%60%0A%20%20%20%20-%20%60policy_value%3D%7Bnull_rule.policy_value%7D%60%0A%20%20%20%20-%20%60uplift_vs_average%3D%7Bnull_rule.uplift_vs_average%7D%60%0A%0A%20%20%20%20A%20failed%20gate%20returns%20no%20targeting%20numbers%3B%20the%20attached%20validation%20explains%20why%0A%20%20%20%20(AUTOC%20p%3D%7Bdisplay_number(null_rule.validation.autoc.p_value%2C%20%22.2f%22)%7D).%0A%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(fit%2C%20mo)%3A%0A%20%20%20%20_spend%20%3D%20next(e%20for%20e%20in%20fit.interactions%20if%20e.name%20%3D%3D%20%22d%3Aspend%22)%0A%20%20%20%20_mde%20%3D%202.80%20*%20_spend.se%0A%20%20%20%20_rows%20%3D%20%5Bf%22%7C%20%7Bint(20_000%20*%20k)%3A%2C%7D%20%7C%20%7B_mde%20%2F%20k**0.5%3A%2B.4f%7D%20%7C%22%20for%20k%20in%20(1%2C%204%2C%2016)%5D%0A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%22%5Cn%22.join(%0A%20%20%20%20%20%20%20%20%20%20%20%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%23%23%206.%20Practical%20constraints%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%7C%20users%20%7C%20smallest%20detectable%20interaction%20%7C%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%7C%20--%20%7C%20--%20%7C%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20*_rows%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22-%20Halving%20the%20detectable%20interaction%20requires%20**4%C3%97**%20the%20users.%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22-%20Every%20%60interact%3D%60%20and%20%60adjust%3D%60%20covariate%20must%20be%20measured%20before%20assignment.%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22-%20Use%20this%20order%3A%20average%20effect%20%E2%86%92%20%60estimate_cate%60%20%E2%86%92%20%60validate_cate%60%20%E2%86%92%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%60targeting_rule%60.%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22-%20If%20validation%20fails%2C%20report%20the%20average%20effect%20and%20stop.%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
43193e938933dc2f4ad25ddf276e80dc