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%20%20%20%20from%20scipy.special%20import%20expit%0A%0A%20%20%20%20from%20increment%20import%20(%0A%20%20%20%20%20%20%20%20AdjustmentSet%2C%0A%20%20%20%20%20%20%20%20Analysis%2C%0A%20%20%20%20%20%20%20%20IdentificationGate%2C%0A%20%20%20%20%20%20%20%20Method%2C%0A%20%20%20%20%20%20%20%20Observational%2C%0A%20%20%20%20)%0A%20%20%20%20from%20increment.tables%20import%20estimates_to_readout%2C%20readout_table%0A%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20AdjustmentSet%2C%0A%20%20%20%20%20%20%20%20Analysis%2C%0A%20%20%20%20%20%20%20%20IdentificationGate%2C%0A%20%20%20%20%20%20%20%20Method%2C%0A%20%20%20%20%20%20%20%20Observational%2C%0A%20%20%20%20%20%20%20%20ct%2C%0A%20%20%20%20%20%20%20%20estimates_to_readout%2C%0A%20%20%20%20%20%20%20%20expit%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%20readout_table%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%20Observational%20inference%3A%20IPTW%20and%20DML%0A%0A%20%20%20%20Treatment%20was%20chosen%2C%20not%20randomized.%20Here%2C%20%60x1%60%20and%20%60x2%60%20affect%20both%20treatment%0A%20%20%20%20and%20revenue%2C%20so%20raw%20group%20means%20are%20confounded.%0A%0A%20%20%20%20IPTW%20and%20DML%20adjust%20for%20those%20measured%20differences.%20Both%20assume%20the%20adjustment%0A%20%20%20%20set%20contains%20every%20confounder%3B%20overlap%20checks%20can%20diagnose%20poor%20comparisons%20but%0A%20%20%20%20cannot%20prove%20that%20assumption.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(expit%2C%20np%2C%20pd)%3A%0A%20%20%20%20_rng%20%3D%20np.random.default_rng(42)%0A%20%20%20%20_n%20%3D%203000%0A%20%20%20%20_effect%20%3D%200.2%0A%0A%20%20%20%20_x1%20%3D%20_rng.normal(size%3D_n)%0A%20%20%20%20_x2%20%3D%20_rng.normal(size%3D_n)%0A%20%20%20%20_propensity%20%3D%20expit(-0.3%20%2B%201.2%20*%20_x1%20-%200.8%20*%20_x2)%0A%20%20%20%20_d%20%3D%20_rng.binomial(1%2C%20_propensity)%0A%20%20%20%20_y%20%3D%201.0%20%2B%200.9%20*%20_x1%20%2B%200.6%20*%20_x2%20%2B%20_effect%20*%20_d%20%2B%20_rng.normal(scale%3D0.5%2C%20size%3D_n)%0A%0A%20%20%20%20df%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_id%22%3A%20%5Bf%22u%7Bi%7D%22%20for%20i%20in%20range(_n)%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22variant%22%3A%20np.where(_d%20%3D%3D%201%2C%20%22treatment%22%2C%20%22control%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22revenue%22%3A%20_y%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22x1%22%3A%20_x1%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22x2%22%3A%20_x2%2C%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20)%0A%20%20%20%20df.head()%0A%20%20%20%20return%20(df%2C)%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%20Simulate%20confounded%20data%0A%0A%20%20%20%20The%20true%20effect%20is%20**%2B0.2**%2C%20or%20**%2B20%25**%20relative%20to%20the%20untreated%20mean.%0A%20%20%20%20%60x1%60%20and%20%60x2%60%20drive%20both%20treatment%20and%20revenue.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(Analysis%2C%20df)%3A%0A%20%20%20%20naive_results%20%3D%20Analysis.from_unit_summary(%0A%20%20%20%20%20%20%20%20df%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(naive_results%2C)%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%201.%20Naive%20comparison%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(estimates_to_readout%2C%20naive_results%2C%20readout_table)%3A%0A%20%20%20%20readout_table(estimates_to_readout(naive_results)%2C%20title%3D%22Naive%20comparison%20(confounded)%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo%2C%20naive_results)%3A%0A%20%20%20%20_naive_lift%20%3D%20naive_results%5B0%5D.lift.value%0A%0A%20%20%20%20mo.md(f%22%22%22%0A%20%20%20%20The%20naive%20estimate%20is%20**%7B_naive_lift%3A%2B.2%25%7D**%2C%20not%20the%20true%20%2B20%25.%20Treated%20users%0A%20%20%20%20already%20had%20higher%20expected%20revenue%20because%20of%20%60x1%60%20and%20%60x2%60.%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%202.%20IPTW%3A%20reweight%20comparable%20users%0A%0A%20%20%20%20IPTW%20weights%20users%20by%20their%20treatment%20propensity.%20The%20default%20overlap%20gate%0A%20%20%20%20refuses%20extreme%20weights%3B%20%60overlap%3D%22trim%22%60%20instead%20drops%20poorly%20overlapping%20users%0A%20%20%20%20and%20names%20the%20resulting%20overlap%20population%20on%20the%20estimate.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(AdjustmentSet%2C%20Analysis%2C%20IdentificationGate%2C%20Observational%2C%20df)%3A%0A%20%20%20%20iptw_analysis%20%3D%20Analysis.from_unit_summary(%0A%20%20%20%20%20%20%20%20df%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%20metrics%3D%7B%22revenue%22%3A%20%22mean%22%7D%2C%0A%20%20%20%20%20%20%20%20design%3DObservational(%0A%20%20%20%20%20%20%20%20%20%20%20%20control_group%3D%22control%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20adjustment%3DAdjustmentSet(covariates%3D(%22x1%22%2C%20%22x2%22))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20gate%3DIdentificationGate(overlap%3D%22trim%22)%2C%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20)%0A%20%20%20%20iptw_results%20%3D%20iptw_analysis.run()%0A%20%20%20%20return%20iptw_analysis%2C%20iptw_results%0A%0A%0A%40app.cell%0Adef%20_(estimates_to_readout%2C%20iptw_results%2C%20readout_table)%3A%0A%20%20%20%20readout_table(estimates_to_readout(iptw_results)%2C%20title%3D%22IPTW-adjusted%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%203.%20DML%3A%20adjust%20treatment%20and%20outcome%0A%0A%20%20%20%20DML%20cross-fits%20both%20a%20propensity%20model%20and%20an%20outcome%20model%3A%20every%20user's%0A%20%20%20%20prediction%20comes%20from%20folds%20that%20excluded%20that%20user.%20It%20uses%20the%20same%20declared%0A%20%20%20%20adjustment%20set%3B%20only%20the%20method%20changes.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(Method%2C%20iptw_analysis)%3A%0A%20%20%20%20dml_results%20%3D%20iptw_analysis.run(decision_method%3DMethod(name%3D%22dml%22))%0A%20%20%20%20return%20(dml_results%2C)%0A%0A%0A%40app.cell%0Adef%20_(dml_results%2C%20estimates_to_readout%2C%20readout_table)%3A%0A%20%20%20%20readout_table(estimates_to_readout(dml_results)%2C%20title%3D%22DML-adjusted%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20final_comparison(ct%2C%20dml_results%2C%20iptw_results%2C%20mo%2C%20naive_results%2C%20np%2C%20pd)%3A%0A%20%20%20%20_comparison%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%22estimator%22%3A%20%5B%22Naive%22%2C%20%22IPTW%22%2C%20%22DML%22%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22estimate%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20naive_results%5B0%5D.lift.value%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20iptw_results%5B0%5D.lift.value%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20dml_results%5B0%5D.lift.value%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%22lb%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20np.nan%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20iptw_results%5B0%5D.lift.lb%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20dml_results%5B0%5D.lift.lb%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%22ub%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20np.nan%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20iptw_results%5B0%5D.lift.ub%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20dml_results%5B0%5D.lift.ub%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%22truth%22%3A%200.2%2C%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20)%0A%20%20%20%20_comparison_table%20%3D%20(%0A%20%20%20%20%20%20%20%20ct.CoefTable(_comparison%2C%20rows%3D%22estimator%22)%0A%20%20%20%20%20%20%20%20.estimate(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Lift%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.Percent(scale%3D100.0%2C%20decimals%3D2)%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%22Lift%20comparison%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20of%3D%22Lift%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%20with%20truth%22%2C%20%22Known%20truth%3A%20%2B20%25%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%20_comparison_table%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.md(%22%22%22%0A%20%20%20%20%20%20%20%20%20%20%20%20Both%20adjusted%20estimates%20recover%20the%20known%20effect.%20DML%20can%20be%20more%20precise%0A%20%20%20%20%20%20%20%20%20%20%20%20when%20its%20outcome%20model%20explains%20variation%20that%20weighting%20alone%20cannot.%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%22%22)%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_()%3A%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
e3281a308b04135538d26c624c0c8318