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%20marimo%20as%20mo%0A%20%20%20%20from%20rich.pretty%20import%20pprint%0A%0A%20%20%20%20from%20increment%20import%20(%0A%20%20%20%20%20%20%20%20Baseline%2C%0A%20%20%20%20%20%20%20%20InferenceSpec%2C%0A%20%20%20%20%20%20%20%20PowerDesign%2C%0A%20%20%20%20%20%20%20%20achieved_power%2C%0A%20%20%20%20%20%20%20%20minimum_detectable_effect%2C%0A%20%20%20%20%20%20%20%20power_curve%2C%0A%20%20%20%20%20%20%20%20required_sample_size%2C%0A%20%20%20%20)%0A%20%20%20%20from%20increment.decision%20import%20FixedInference%0A%20%20%20%20from%20increment.estimation.arm_contract%20import%20(%0A%20%20%20%20%20%20%20%20AnalysisAxes%2C%0A%20%20%20%20%20%20%20%20ArmPlanningProcedure%2C%0A%20%20%20%20%20%20%20%20FamilyPolicy%2C%0A%20%20%20%20%20%20%20%20MetricCapabilities%2C%0A%20%20%20%20%20%20%20%20PlanningFamilyExpansion%2C%0A%20%20%20%20%20%20%20%20RelativeDecisionPolicy%2C%0A%20%20%20%20)%0A%20%20%20%20from%20increment.semantics.assignment%20import%20ParallelAssignment%0A%20%20%20%20from%20increment.semantics.models%20import%20MethodSpec%0A%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20AnalysisAxes%2C%0A%20%20%20%20%20%20%20%20ArmPlanningProcedure%2C%0A%20%20%20%20%20%20%20%20Baseline%2C%0A%20%20%20%20%20%20%20%20FamilyPolicy%2C%0A%20%20%20%20%20%20%20%20FixedInference%2C%0A%20%20%20%20%20%20%20%20InferenceSpec%2C%0A%20%20%20%20%20%20%20%20MethodSpec%2C%0A%20%20%20%20%20%20%20%20MetricCapabilities%2C%0A%20%20%20%20%20%20%20%20ParallelAssignment%2C%0A%20%20%20%20%20%20%20%20PlanningFamilyExpansion%2C%0A%20%20%20%20%20%20%20%20PowerDesign%2C%0A%20%20%20%20%20%20%20%20RelativeDecisionPolicy%2C%0A%20%20%20%20%20%20%20%20achieved_power%2C%0A%20%20%20%20%20%20%20%20minimum_detectable_effect%2C%0A%20%20%20%20%20%20%20%20mo%2C%0A%20%20%20%20%20%20%20%20power_curve%2C%0A%20%20%20%20%20%20%20%20pprint%2C%0A%20%20%20%20%20%20%20%20required_sample_size%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%20Power%20analysis%0A%0A%20%20%20%20Power%20analysis%20is%20used%20to%20size%20an%20experiment.%20It%20can%20help%20answer%0A%20%20%20%20*%20how%20many%20users%20does%20a%20target%20lift%20need%3F%0A%20%20%20%20*%20how%20much%20power%20does%20a%20fixed%20budget%20buy%3F%0A%20%20%20%20*%20what%20is%20the%20smallest%20effect%20a%20given%20sample%20size%20could%20even%20detect%3F%0A%0A%0A%20%20%20%20We%20additionally%20support%20power%20estimation%20beyond%20just%20the%20simple%20case%2C%20such%20as%20CUPED%20and%20multiple%20variants.%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%20Assumptions%0A%0A%20%20%20%20%60mean%60%20and%20%60var%60%20describe%20the%20*control*%20arm's%20per-unit%20outcome.%20In%20real%20life%0A%20%20%20%20they%20come%20from%20one%20of%20two%20places%3A%0A%20%20%20%20*%20a%20historical%20query%20(%60SELECT%20avg(revenue)%2C%20var_samp(revenue)%20FROM%20...%60%20over%20the%20population%20you%20plan%0A%20%20%20%20to%20expose)%0A%20%20%20%20*%20or%20%60Baseline.from_summary(stats)%60%20where%20%60stats%60%20is%20a%20%60SummaryStats%60%20produced%20by%20a%20prior%20increment%20analysis%20(a%20pilot%20or%20an%20earlier%20readout).%0A%0A%20%20%20%20For%20a%20conversion-style%20metric%2C%20%60Baseline.from_proportion(p)%60%20fills%20in%20the%20Bernoulli%20variance%20%60p%20*%20(1%20-%20p)%60%20for%20you.%0A%0A%20%20%20%20Here%3A%20average%2014-day%20revenue%20per%20exposed%20user.%20Heavy%20right%20tail%2C%20so%20the%20variance%20dwarfs%20the%20mean%20-%20which%20is%20exactly%20why%20sample%20sizes%20get%20large.%20CUPED%20can%20be%20used%20to%20help%20with%20this%20later%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20MEAN_REVENUE%20%3D%2012.40%20%20%23%20dollars%20per%20user%0A%20%20%20%20VAR_REVENUE%20%3D%20430.0%20%20%23%20dollars%5E2%20per%20user%20(sd%20~%20%2420.7)%0A%0A%20%20%20%20TARGET_LIFT%20%3D%200.03%20%20%23%20%2B3%25%20revenue%20per%20user%0A%20%20%20%20BUDGET_N%20%3D%2020_000%0A%20%20%20%20BIGGER_N%20%3D%2080_000%0A%0A%20%20%20%20%23%20%60cuped_rho%60%20is%20the%20assumed%20correlation%20between%20the%20outcome%20and%20a%20pre-period%20covariate%0A%20%20%20%20CUPED_RHO%20%3D%200.5%0A%20%20%20%20N_VARIANTS%20%3D%204%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20BIGGER_N%2C%0A%20%20%20%20%20%20%20%20BUDGET_N%2C%0A%20%20%20%20%20%20%20%20CUPED_RHO%2C%0A%20%20%20%20%20%20%20%20MEAN_REVENUE%2C%0A%20%20%20%20%20%20%20%20N_VARIANTS%2C%0A%20%20%20%20%20%20%20%20TARGET_LIFT%2C%0A%20%20%20%20%20%20%20%20VAR_REVENUE%2C%0A%20%20%20%20)%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20AnalysisAxes%2C%0A%20%20%20%20ArmPlanningProcedure%2C%0A%20%20%20%20Baseline%2C%0A%20%20%20%20FamilyPolicy%2C%0A%20%20%20%20FixedInference%2C%0A%20%20%20%20MEAN_REVENUE%2C%0A%20%20%20%20MethodSpec%2C%0A%20%20%20%20MetricCapabilities%2C%0A%20%20%20%20ParallelAssignment%2C%0A%20%20%20%20PlanningFamilyExpansion%2C%0A%20%20%20%20PowerDesign%2C%0A%20%20%20%20RelativeDecisionPolicy%2C%0A%20%20%20%20VAR_REVENUE%2C%0A)%3A%0A%20%20%20%20baseline%20%3D%20Baseline(mean%3DMEAN_REVENUE%2C%20var%3DVAR_REVENUE)%0A%20%20%20%20design%20%3D%20PowerDesign(power%3D0.80)%0A%20%20%20%20procedure%20%3D%20ArmPlanningProcedure(%0A%20%20%20%20%20%20%20%20assignment%3DParallelAssignment()%2C%0A%20%20%20%20%20%20%20%20analysis%3DAnalysisAxes(%0A%20%20%20%20%20%20%20%20%20%20%20%20identification%3D%22randomized%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20view%3D%22total%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20segmented%3DFalse%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20completed_windows_only%3DTrue%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20population%3D%22assigned%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20variance_adjustment%3D%22none%22%2C%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20dependence%3D%22iid%22%2C%0A%20%20%20%20%20%20%20%20inference%3DFixedInference()%2C%0A%20%20%20%20%20%20%20%20estimand%3D%22mean%22%2C%0A%20%20%20%20%20%20%20%20metric%3DMetricCapabilities(%0A%20%20%20%20%20%20%20%20%20%20%20%20metric_type%3D%22mean%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20value_scale%3D%22relative%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20winsorization%3D%22none%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20outcome_window%3D%22bounded%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20uptake_window%3D%22not_applicable%22%2C%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20decision%3DRelativeDecisionPolicy(%0A%20%20%20%20%20%20%20%20%20%20%20%20alternative%3D%22two-sided%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20null_lift%3D0.0%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20family%3DFamilyPolicy(kind%3D%22none%22%2C%20axes%3D()%2C%20nominal_alpha%3D0.05)%2C%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20family_expansion%3DPlanningFamilyExpansion(family_size%3D1)%2C%0A%20%20%20%20%20%20%20%20%23%20The%20decision%20method%20is%20the%20estimator%20the%20ship%20decision%20will%20use%3B%0A%20%20%20%20%20%20%20%20%23%20sensitivity%20methods%20only%20add%20reporting-only%20comparison%20rows.%0A%20%20%20%20%20%20%20%20decision_method%3DMethodSpec(name%3D%22unadjusted%22)%2C%0A%20%20%20%20%20%20%20%20sensitivity_methods%3D()%2C%0A%20%20%20%20%20%20%20%20prior_present%3DFalse%2C%0A%20%20%20%20)%0A%20%20%20%20return%20baseline%2C%20design%2C%20procedure%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22%0A%20%20%20%20%23%23%201.%20How%20many%20users%20do%20I%20need%20to%20detect%20a%20target%20lift%3F%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(TARGET_LIFT%2C%20baseline%2C%20design%2C%20pprint%2C%20procedure%2C%20required_sample_size)%3A%0A%20%20%20%20rss%20%3D%20required_sample_size(TARGET_LIFT%2C%20baseline%2C%20procedure%2C%20design)%0A%20%20%20%20pprint(rss)%0A%20%20%20%20return%20(rss%2C)%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22%0A%20%20%20%20%23%23%202.%20I%20can%20only%20get%20a%20fixed%20budget%20of%20users%20per%20arm%20-%20what%20power%20is%20that%3F%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20BIGGER_N%2C%0A%20%20%20%20BUDGET_N%2C%0A%20%20%20%20TARGET_LIFT%2C%0A%20%20%20%20achieved_power%2C%0A%20%20%20%20baseline%2C%0A%20%20%20%20design%2C%0A%20%20%20%20procedure%2C%0A)%3A%0A%20%20%20%20ap%20%3D%20achieved_power(BUDGET_N%2C%20TARGET_LIFT%2C%20baseline%2C%20procedure%2C%20design)%0A%20%20%20%20ap_bigger%20%3D%20achieved_power(BIGGER_N%2C%20TARGET_LIFT%2C%20baseline%2C%20procedure%2C%20design)%0A%20%20%20%20return%20ap%2C%20ap_bigger%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(BIGGER_N%2C%20BUDGET_N%2C%20TARGET_LIFT%2C%20ap%2C%20ap_bigger%2C%20mo)%3A%0A%20%20%20%20mo.md(f%22%22%22%0A%20%20%20%20%7C%20%7C%20%7C%0A%20%20%20%20%7C--%7C--%3A%7C%0A%20%20%20%20%7C%20Power%2C%20%7BBUDGET_N%3A%2C%7D%2Farm%20%7C%20%7Bap.power%3A.1%25%7D%20%7C%0A%20%20%20%20%7C%20Power%2C%20%7BBIGGER_N%3A%2C%7D%2Farm%20%7C%20%7Bap_bigger.power%3A.1%25%7D%20(more%20N%20-%3E%20more%20power)%20%7C%0A%0A%20%20%20%20A%20real%20%7BTARGET_LIFT%3A%2B.0%25%7D%20effect%20at%20%7BBUDGET_N%3A%2C%7D%20users%2Farm%20would%20be%20missed%0A%20%20%20%20%7B1%20-%20ap.power%3A.0%25%7D%20of%20the%20time.%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(%22%22%22%0A%20%20%20%20%23%23%203.%20What%20is%20the%20smallest%20lift%20I%20could%20detect%3F%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20BIGGER_N%2C%0A%20%20%20%20BUDGET_N%2C%0A%20%20%20%20baseline%2C%0A%20%20%20%20design%2C%0A%20%20%20%20minimum_detectable_effect%2C%0A%20%20%20%20procedure%2C%0A)%3A%0A%20%20%20%20mde_small%20%3D%20minimum_detectable_effect(BUDGET_N%2C%20baseline%2C%20procedure%2C%20design)%0A%20%20%20%20mde_big%20%3D%20minimum_detectable_effect(BIGGER_N%2C%20baseline%2C%20procedure%2C%20design)%0A%20%20%20%20return%20mde_big%2C%20mde_small%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(BIGGER_N%2C%20BUDGET_N%2C%20MEAN_REVENUE%2C%20mde_big%2C%20mde_small%2C%20mo)%3A%0A%20%20%20%20def%20_mde_row(label%2C%20result)%3A%0A%20%20%20%20%20%20%20%20if%20result.mde_relative%20is%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20f%22%7C%20%7Blabel%7D%20%7C%20not%20available%20(%7Bresult.mde_unavailable_reason%7D)%20%7C%20-%20%7C%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%7C%20%7Blabel%7D%20%7C%20%7Bresult.mde_relative%3A%2B.2%25%7D%20%7C%20%7BMEAN_REVENUE%20*%20result.mde_relative%3A%2B.3f%7D%20%7C%22%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20small_row%20%3D%20_mde_row(f%22%7BBUDGET_N%3A%2C%7D%20users%2Farm%22%2C%20mde_small)%0A%20%20%20%20big_row%20%3D%20_mde_row(f%22%7BBIGGER_N%3A%2C%7D%20users%2Farm%22%2C%20mde_big)%0A%20%20%20%20mo.md(f%22%22%22%0A%20%20%20%20%7C%20Budget%20%7C%20MDE%20(relative)%20%7C%20MDE%20(dollars%2Fuser)%20%7C%0A%20%20%20%20%7C--%7C--%3A%7C--%3A%7C%0A%20%20%20%20%7Bsmall_row%7D%0A%20%20%20%20%7Bbig_row%7D%0A%0A%20%20%20%20The%20MDE%20shrinks%20as%20N%20grows%3A%20bigger%20tests%20resolve%20finer%20effects.%20A%20row%0A%20%20%20%20reads%20%22not%20available%22%20when%20no%20effect%20reaches%20the%20target%20power%20at%20that%0A%20%20%20%20budget.%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%20Extended%20features%0A%0A%20%20%20%20%23%23%20Estimating%20a%20Power%20Curve%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(baseline%2C%20design%2C%20power_curve%2C%20procedure)%3A%0A%20%20%20%20import%20altair%20as%20alt%0A%0A%20%20%20%20sample_sizes%20%3D%20range(5_000%2C%2055_001%2C%205_000)%0A%20%20%20%20effect_sizes%20%3D%20%5B0.01%2C%200.02%2C%200.03%2C%200.05%5D%0A%20%20%20%20alphas%20%3D%20%5B0.01%2C%200.05%2C%200.10%5D%0A%20%20%20%20procedures%20%3D%20%5B%0A%20%20%20%20%20%20%20%20procedure.model_copy(%0A%20%20%20%20%20%20%20%20%20%20%20%20update%3D%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22decision%22%3A%20procedure.decision.model_copy(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20update%3D%7B%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%22family%22%3A%20procedure.decision.family.model_copy(%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%20%20%20%20update%3D%7B%22nominal_alpha%22%3A%20nominal_alpha%7D%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)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%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%7D%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20for%20nominal_alpha%20in%20alphas%0A%20%20%20%20%5D%0A%0A%20%20%20%20curve%20%3D%20power_curve(%0A%20%20%20%20%20%20%20%20n_per_arm%3Dsample_sizes%2C%0A%20%20%20%20%20%20%20%20relative_lift%3Deffect_sizes%2C%0A%20%20%20%20%20%20%20%20baseline%3Dbaseline%2C%0A%20%20%20%20%20%20%20%20procedure%3Dprocedures%2C%0A%20%20%20%20%20%20%20%20design%3Ddesign%2C%0A%20%20%20%20%20%20%20%20units_per_week%3D30000%2C%0A%20%20%20%20)%0A%0A%20%20%20%20curve_df%20%3D%20curve.to_frame(backend%3D%22pandas%22)%0A%0A%20%20%20%20alt.Chart(curve_df).mark_line().encode(%0A%20%20%20%20%20%20%20%20x%3Dalt.X(%22duration_days%3AQ%22%2C%20title%3D%22Duration%20(Days)%22)%2C%0A%20%20%20%20%20%20%20%20y%3Dalt.Y(%22power%3AQ%22%2C%20scale%3Dalt.Scale(domain%3D%5B0%2C%201%5D))%2C%0A%20%20%20%20%20%20%20%20color%3Dalt.Color(%22relative_lift%3AN%22%2C%20title%3D%22Relative%20lift%22)%2C%0A%20%20%20%20%20%20%20%20strokeDash%3Dalt.StrokeDash(%22alpha%3AN%22%2C%20title%3D%22Alpha%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%20Multiple%20variants%20%2B%20Bonferroni%20-%20more%20users%20for%20the%20same%20power%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20N_VARIANTS%2C%0A%20%20%20%20PlanningFamilyExpansion%2C%0A%20%20%20%20TARGET_LIFT%2C%0A%20%20%20%20baseline%2C%0A%20%20%20%20design%2C%0A%20%20%20%20procedure%2C%0A%20%20%20%20required_sample_size%2C%0A%20%20%20%20rss%2C%0A)%3A%0A%20%20%20%20procedure_mc%20%3D%20procedure.model_copy(%0A%20%20%20%20%20%20%20%20update%3D%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22decision%22%3A%20procedure.decision.model_copy(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20update%3D%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22family%22%3A%20procedure.decision.family.model_copy(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20update%3D%7B%22kind%22%3A%20%22bonferroni%22%2C%20%22axes%22%3A%20(%22arm%22%2C)%7D%0A%20%20%20%20%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%7D%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%22family_expansion%22%3A%20PlanningFamilyExpansion(family_size%3DN_VARIANTS)%2C%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20)%0A%20%20%20%20rss_mc%20%3D%20required_sample_size(TARGET_LIFT%2C%20baseline%2C%20procedure_mc%2C%20design)%0A%0A%20%20%20%20extra%20%3D%20rss_mc.n_per_arm%20-%20rss.n_per_arm%0A%20%20%20%20return%20extra%2C%20procedure_mc%2C%20rss_mc%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(N_VARIANTS%2C%20extra%2C%20mo%2C%20procedure%2C%20procedure_mc%2C%20rss%2C%20rss_mc)%3A%0A%20%20%20%20mo.md(f%22%22%22%0A%20%20%20%20%7C%20%7C%20%7C%20%7C%0A%20%20%20%20%7C--%7C--%3A%7C--%3A%7C%0A%20%20%20%20%7C%201%20variant%2C%20no%20correction%20%7C%20%7Brss.n_per_arm%3A%2C%7D%20users%2Farm%20%7C%20alpha%20%7Bprocedure.compiled_alpha%3A.4f%7D%20%7C%0A%20%20%20%20%7C%20%7BN_VARIANTS%7D%20variants%2C%20bonferroni%20%7C%20%7Brss_mc.n_per_arm%3A%2C%7D%20users%2Farm%20%7C%20alpha%20%7Bprocedure_mc.compiled_alpha%3A.4f%7D%20%7C%0A%0A%20%20%20%20%7Bextra%3A%2C%7D%20more%20users%2Farm%2C%20a%20%7Bextra%20%2F%20rss.n_per_arm%3A.1%25%7D%20increase%20-%20and%20that%20is%0A%20%20%20%20per%20arm%2C%20across%20%7BN_VARIANTS%20%2B%201%7D%20arms.%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(%22%22%22%0A%20%20%20%20%23%23%20CUPED%20variance%20reduction%20-%20fewer%20users%20for%20the%20same%20power%0A%0A%20%20%20%20Planning%20with%20a%20nonzero%20%60cuped_rho%60%20requires%20a%20procedure%20that%20declares%0A%20%20%20%20CUPED%3B%20make%20it%20the%20*decision*%20method%2C%20since%20the%20savings%20are%20only%20real%0A%20%20%20%20if%20the%20ship%20decision%20itself%20uses%20the%20adjusted%20estimator.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20Baseline%2C%0A%20%20%20%20CUPED_RHO%2C%0A%20%20%20%20MEAN_REVENUE%2C%0A%20%20%20%20MethodSpec%2C%0A%20%20%20%20TARGET_LIFT%2C%0A%20%20%20%20VAR_REVENUE%2C%0A%20%20%20%20design%2C%0A%20%20%20%20procedure%2C%0A%20%20%20%20required_sample_size%2C%0A%20%20%20%20rss%2C%0A)%3A%0A%20%20%20%20baseline_cuped%20%3D%20Baseline(mean%3DMEAN_REVENUE%2C%20var%3DVAR_REVENUE%2C%20cuped_rho%3DCUPED_RHO)%0A%20%20%20%20procedure_cuped%20%3D%20procedure.model_copy(%0A%20%20%20%20%20%20%20%20update%3D%7B%22decision_method%22%3A%20MethodSpec(name%3D%22cuped%22%2C%20variance_reduction%3D%22cuped%22)%7D%0A%20%20%20%20)%0A%20%20%20%20rss_cuped%20%3D%20required_sample_size(TARGET_LIFT%2C%20baseline_cuped%2C%20procedure_cuped%2C%20design)%0A%0A%20%20%20%20saved%20%3D%20rss.n_per_arm%20-%20rss_cuped.n_per_arm%0A%20%20%20%20return%20procedure_cuped%2C%20rss_cuped%2C%20saved%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(CUPED_RHO%2C%20mo%2C%20rss%2C%20rss_cuped%2C%20saved)%3A%0A%20%20%20%20mo.md(f%22%22%22%0A%20%20%20%20%7C%20%7C%20%7C%0A%20%20%20%20%7C--%7C--%3A%7C%0A%20%20%20%20%7C%20rho%20%3D%200.0%20(no%20adjustment)%20%7C%20%7Brss.n_per_arm%3A%2C%7D%20users%2Farm%20%7C%0A%20%20%20%20%7C%20rho%20%3D%20%7BCUPED_RHO%3A.1f%7D%20(pre-period%20cov.)%20%7C%20%7Brss_cuped.n_per_arm%3A%2C%7D%20users%2Farm%20%7C%0A%20%20%20%20%7C%20Effective%20variance%20%7C%20%7Brss.effective_var%3A%2C.1f%7D%20-%3E%20%7Brss_cuped.effective_var%3A%2C.1f%7D%20%7C%0A%0A%20%20%20%20%7Bsaved%3A%2C%7D%20fewer%20users%2Farm%2C%20a%20%7Bsaved%20%2F%20rss.n_per_arm%3A.1%25%7D%20reduction%20-%20theory%20says%0A%20%20%20%20required%20N%20scales%20by%20%601%20-%20rho%5E2%60%20%3D%20%7B1%20-%20CUPED_RHO**2%3A.2f%7D.%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(%22%22%22%0A%20%20%20%20%23%23%20Non-inferiority%20sizing%20-%20how%20many%20users%20to%20CONFIRM%20no%20loss%0A%0A%20%20%20%20A%20non-inferiority%20design%20asks%20a%20different%20question%20than%20a%20headline%20metric%3A%20not%20%22how%20many%0A%20%20%20%20users%20to%20detect%20a%20lift%2C%22%20but%20%22how%20many%20users%20to%20CONFIRM%20we%20didn't%20lose%20more%0A%20%20%20%20than%20a%20tolerance%2C%20if%20the%20true%20effect%20is%20actually%20zero.%22%20That's%0A%20%20%20%20%60relative_lift%3D0.0%60%20against%20a%20one-sided%20design%20shifted%20to%20a%20nonzero%0A%20%20%20%20%60null_lift%60.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20Baseline%2C%0A%20%20%20%20CUPED_RHO%2C%0A%20%20%20%20MEAN_REVENUE%2C%0A%20%20%20%20RelativeDecisionPolicy%2C%0A%20%20%20%20VAR_REVENUE%2C%0A%20%20%20%20design%2C%0A%20%20%20%20procedure_cuped%2C%0A%20%20%20%20required_sample_size%2C%0A)%3A%0A%20%20%20%20non_inferiority_procedure%20%3D%20procedure_cuped.model_copy(%0A%20%20%20%20%20%20%20%20update%3D%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22decision%22%3A%20RelativeDecisionPolicy(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20alternative%3D%22greater%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20null_lift%3D-0.025%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20family%3Dprocedure_cuped.decision.family%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20)%0A%20%20%20%20non_inferiority_baseline%20%3D%20Baseline(mean%3DMEAN_REVENUE%2C%20var%3DVAR_REVENUE%2C%20cuped_rho%3DCUPED_RHO)%0A%20%20%20%20non_inferiority_plan%20%3D%20required_sample_size(%0A%20%20%20%20%20%20%20%20relative_lift%3D0.0%2C%0A%20%20%20%20%20%20%20%20baseline%3Dnon_inferiority_baseline%2C%0A%20%20%20%20%20%20%20%20procedure%3Dnon_inferiority_procedure%2C%0A%20%20%20%20%20%20%20%20design%3Ddesign%2C%0A%20%20%20%20)%0A%0A%20%20%20%20non_inferiority_mde%20%3D%20non_inferiority_plan.mde_relative%0A%20%20%20%20true_lift_at_boundary%20%3D%20(%0A%20%20%20%20%20%20%20%20None%0A%20%20%20%20%20%20%20%20if%20non_inferiority_mde%20is%20None%0A%20%20%20%20%20%20%20%20else%20(1%20%2B%20non_inferiority_procedure.decision.null_lift)%20*%20(1%20%2B%20non_inferiority_mde)%20-%201%0A%20%20%20%20)%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20non_inferiority_mde%2C%0A%20%20%20%20%20%20%20%20non_inferiority_plan%2C%0A%20%20%20%20%20%20%20%20non_inferiority_procedure%2C%0A%20%20%20%20%20%20%20%20true_lift_at_boundary%2C%0A%20%20%20%20)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(%0A%20%20%20%20mo%2C%0A%20%20%20%20non_inferiority_mde%2C%0A%20%20%20%20non_inferiority_plan%2C%0A%20%20%20%20non_inferiority_procedure%2C%0A%20%20%20%20true_lift_at_boundary%2C%0A)%3A%0A%20%20%20%20margin%20%3D%20-non_inferiority_procedure.decision.null_lift%0A%20%20%20%20if%20non_inferiority_mde%20is%20None%20or%20true_lift_at_boundary%20is%20None%3A%0A%20%20%20%20%20%20%20%20mde_line%20%3D%20f%22%7C%20MDE%20beyond%20the%20margin%20%7C%20not%20available%20(%7Bnon_inferiority_plan.mde_unavailable_reason%7D)%20%7C%22%0A%20%20%20%20%20%20%20%20mde_note%20%3D%20%22No%20effect%20beyond%20the%20margin%20reaches%20the%20target%20power%20at%20this%20N.%22%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20mde_line%20%3D%20f%22%7C%20MDE%20beyond%20the%20margin%20%7C%20%7Bnon_inferiority_mde%3A%2B.2%25%7D%20%7C%22%0A%20%20%20%20%20%20%20%20mde_note%20%3D%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%60mde_relative%60%20here%20is%20the%20distance%20DETECTABLE%20BEYOND%20the%20margin%2C%20not%20an%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22absolute%20effect%20size%3A%20%7Bnon_inferiority_mde%3A%2B.2%25%7D%20beyond%20a%20%7Bmargin%3A.0%25%7D%20margin%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22is%20a%20true%20lift%20of%20about%20%7Btrue_lift_at_boundary%3A%2B.2%25%7D.%22%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20mo.md(f%22%22%22%0A%20%20%20%20%7C%20%7C%20%7C%0A%20%20%20%20%7C--%7C--%3A%7C%0A%20%20%20%20%7C%20Margin%20(tolerated%20loss)%20%7C%20%7Bmargin%3A.0%25%7D%20%7C%0A%20%20%20%20%7C%20Treatment%20arm%20%7C%20%7Bnon_inferiority_plan.n_per_arm%3A%2C%7D%20users%20%7C%0A%20%20%20%20%7C%20Total%20(both%20arms)%20%7C%20%7Bnon_inferiority_plan.n_total%3A%2C%7D%20users%20%7C%0A%20%20%20%20%7C%20Power%20at%20that%20N%20%7C%20%7Bnon_inferiority_plan.power%3A.1%25%7D%20%7C%0A%20%20%20%20%7Bmde_line%7D%0A%0A%20%20%20%20%7Bmde_note%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%20Sequential%20planning%20with%20asymptotic_mean%20inference%0A%0A%20%20%20%20Declaring%20%60InferenceSpec(kind%3D%22asymptotic_mean%22)%60%20on%20the%20procedure%20plans%20the%0A%20%20%20%20boundary%20the%20runtime%20monitors%20with%20under%20that%20same%20declaration%2C%20at%2014%0A%20%20%20%20equally%20spaced%20looks.%20%60kind%3D%22always_valid%22%60%20has%20no%20planning%20construction%3A%0A%20%20%20%20plan%20such%20a%20test%20at%20a%20fixed%20horizon%20and%20monitor%20it%20at%20runtime.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20ArmPlanningProcedure%2C%0A%20%20%20%20InferenceSpec%2C%0A%20%20%20%20TARGET_LIFT%2C%0A%20%20%20%20achieved_power%2C%0A%20%20%20%20baseline%2C%0A%20%20%20%20minimum_detectable_effect%2C%0A%20%20%20%20pprint%2C%0A%20%20%20%20required_sample_size%2C%0A)%3A%0A%20%20%20%20sequential_procedure%20%3D%20ArmPlanningProcedure.standard(%0A%20%20%20%20%20%20%20%20%22mean%22%2C%20inference%3DInferenceSpec(kind%3D%22asymptotic_mean%22)%0A%20%20%20%20)%0A%0A%20%20%20%20sample_size%20%3D%20required_sample_size(%0A%20%20%20%20%20%20%20%20relative_lift%3DTARGET_LIFT%2C%0A%20%20%20%20%20%20%20%20baseline%3Dbaseline%2C%0A%20%20%20%20%20%20%20%20procedure%3Dsequential_procedure%2C%0A%20%20%20%20%20%20%20%20planned_looks%3D14%2C%0A%20%20%20%20)%0A%0A%20%20%20%20power%20%3D%20achieved_power(%0A%20%20%20%20%20%20%20%20n_per_arm%3D25_000%2C%0A%20%20%20%20%20%20%20%20relative_lift%3DTARGET_LIFT%2C%0A%20%20%20%20%20%20%20%20baseline%3Dbaseline%2C%0A%20%20%20%20%20%20%20%20procedure%3Dsequential_procedure%2C%0A%20%20%20%20%20%20%20%20planned_looks%3D14%2C%0A%20%20%20%20)%0A%0A%20%20%20%20mde%20%3D%20minimum_detectable_effect(%0A%20%20%20%20%20%20%20%20n_per_arm%3D25_000%2C%0A%20%20%20%20%20%20%20%20baseline%3Dbaseline%2C%0A%20%20%20%20%20%20%20%20procedure%3Dsequential_procedure%2C%0A%20%20%20%20%20%20%20%20planned_looks%3D14%2C%0A%20%20%20%20)%0A%0A%20%20%20%20pprint(%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22required_sample_size%22%3A%20sample_size%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22achieved_power%22%3A%20power%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22minimum_detectable_effect%22%3A%20mde%2C%0A%20%20%20%20%20%20%20%20%7D%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
9fcd6f507f5b3a16610b23e3dd43bc90