import%20marimo%0A%0A__generated_with%20%3D%20%220.23.4%22%0Aapp%20%3D%20marimo.App(%0A%20%20%20%20width%3D%22medium%22%2C%0A%20%20%20%20app_title%3D%22Environmental%20Sensitivity%20Follow-Up%22%2C%0A)%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%20Environmental%20Sensitivity%20Follow-Up%0A%20%20%20%20%23%23%23%20Which%20coffee-producing%20countries%20still%20show%20environmental%20signal%20after%20population%20is%20accounted%20for%3F%0A%0A%20%20%20%20**INEG%202314H%20%E2%80%94%20Statistics%20for%20Industrial%20Engineers%20%7C%20Warren%20Ross**%0A%0A%20%20%20%20---%0A%0A%20%20%20%20The%20first%20workbook%20showed%20that%20%60ln(Population)%60%20is%20a%20major%20predictor%20of%20coffee%20production.%20This%20follow-up%20asks%20a%20more%20targeted%20question%3A%0A%0A%20%20%20%20%3E%20After%20removing%20the%20population%2Fscale%20effect%2C%20which%20countries%20still%20show%20production%20variation%20associated%20with%20temperature%20or%20rainfall%3F%0A%0A%20%20%20%20This%20notebook%20is%20an%20exploratory%20screening%20tool.%20It%20identifies%20candidate%20countries%20for%20deeper%20environmental%20case-study%20analysis%3B%20it%20does%20not%20prove%20climate%20causation.%20Exploratory%20screening%20is%20the%20first%20step%20of%20the%20research%20cycle%20%E2%80%94%20it%20focuses%20the%20investigation%20before%20committing%20to%20formal%20tests.%20All%20H%E2%82%80%2FH%E2%82%81%20conclusions%20with%20teacher-phrasing%20are%20in%20the%20main%20workbook%20(%60coffee_analysis.py%60%20%C2%A72%E2%80%93%C2%A75).%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%20import%20pathlib%0A%20%20%20%20import%20warnings%0A%0A%20%20%20%20import%20marimo%20as%20mo%0A%20%20%20%20import%20matplotlib.pyplot%20as%20plt%0A%20%20%20%20import%20numpy%20as%20np%0A%20%20%20%20import%20pandas%20as%20pd%0A%20%20%20%20import%20scipy.stats%20as%20stats%0A%20%20%20%20import%20seaborn%20as%20sns%0A%20%20%20%20import%20statsmodels.api%20as%20sm%0A%20%20%20%20import%20statsmodels.formula.api%20as%20smf%0A%20%20%20%20from%20statsmodels.stats.multitest%20import%20multipletests%0A%20%20%20%20from%20statsmodels.stats.stattools%20import%20durbin_watson%0A%20%20%20%20from%20statsmodels.stats.anova%20import%20anova_lm%0A%0A%20%20%20%20warnings.filterwarnings(%22ignore%22)%0A%0A%20%20%20%20plt.rcParams.update(%7B%0A%20%20%20%20%20%20%20%20%22figure.dpi%22%3A%20130%2C%0A%20%20%20%20%20%20%20%20%22axes.spines.top%22%3A%20False%2C%0A%20%20%20%20%20%20%20%20%22axes.spines.right%22%3A%20False%2C%0A%20%20%20%20%20%20%20%20%22font.size%22%3A%2010.5%2C%0A%20%20%20%20%20%20%20%20%22axes.titlesize%22%3A%2012%2C%0A%20%20%20%20%20%20%20%20%22axes.titleweight%22%3A%20%22bold%22%2C%0A%20%20%20%20%7D)%0A%0A%20%20%20%20ACCENT%20%3D%20%22%236F4E37%22%0A%20%20%20%20ACCENT2%20%3D%20%22%232D6A4F%22%0A%20%20%20%20ACCENT3%20%3D%20%22%23C46A32%22%0A%20%20%20%20GREY%20%3D%20%22%238A8A8A%22%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20ACCENT%2C%0A%20%20%20%20%20%20%20%20ACCENT2%2C%0A%20%20%20%20%20%20%20%20ACCENT3%2C%0A%20%20%20%20%20%20%20%20GREY%2C%0A%20%20%20%20%20%20%20%20anova_lm%2C%0A%20%20%20%20%20%20%20%20durbin_watson%2C%0A%20%20%20%20%20%20%20%20mo%2C%0A%20%20%20%20%20%20%20%20multipletests%2C%0A%20%20%20%20%20%20%20%20np%2C%0A%20%20%20%20%20%20%20%20pathlib%2C%0A%20%20%20%20%20%20%20%20pd%2C%0A%20%20%20%20%20%20%20%20plt%2C%0A%20%20%20%20%20%20%20%20sm%2C%0A%20%20%20%20%20%20%20%20smf%2C%0A%20%20%20%20%20%20%20%20stats%2C%0A%20%20%20%20)%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%200.%20Data%20Loading%20and%20Derived%20Variables%0A%0A%20%20%20%20This%20notebook%20uses%20the%20same%20analysis-ready%20country-year%20panel%20as%20the%20main%20workbook.%20The%20unit%20of%20observation%20is%20one%20country%20in%20one%20year.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(np%2C%20pathlib%2C%20pd)%3A%0A%20%20%20%20_here%20%3D%20pathlib.Path(__file__).parent%0A%20%20%20%20DATA_PATH%20%3D%20_here.parent%20%2F%20%22data%22%20%2F%20%22coffee_analysis_panel_with_covariates.csv%22%0A%0A%20%20%20%20panel_raw%20%3D%20pd.read_csv(DATA_PATH).rename(columns%3D%7B%22Brent_Avg%22%3A%20%22Oil_Price_Brent_USD%22%7D)%0A%0A%20%20%20%20panel%20%3D%20panel_raw.dropna(subset%3D%5B%0A%20%20%20%20%20%20%20%20%22ISO3%22%2C%0A%20%20%20%20%20%20%20%20%22Country_Name%22%2C%0A%20%20%20%20%20%20%20%20%22Year%22%2C%0A%20%20%20%20%20%20%20%20%22Production_tonnes%22%2C%0A%20%20%20%20%20%20%20%20%22Avg_Temp_C%22%2C%0A%20%20%20%20%20%20%20%20%22Rain_mm%22%2C%0A%20%20%20%20%20%20%20%20%22Population%22%2C%0A%20%20%20%20%20%20%20%20%22Oil_Price_Brent_USD%22%2C%0A%20%20%20%20%5D).copy()%0A%0A%20%20%20%20panel%20%3D%20panel%5B(panel%5B%22Production_tonnes%22%5D%20%3E%200)%20%26%20(panel%5B%22Population%22%5D%20%3E%200)%5D.copy()%0A%0A%20%20%20%20panel%5B%22ln_Production%22%5D%20%3D%20np.log(panel%5B%22Production_tonnes%22%5D)%0A%20%20%20%20panel%5B%22ln_Population%22%5D%20%3D%20np.log(panel%5B%22Population%22%5D)%0A%0A%20%20%20%20if%20%22Export_Value_1000USD%22%20in%20panel.columns%3A%0A%20%20%20%20%20%20%20%20panel%5B%22ln_Export_Value%22%5D%20%3D%20np.where(%0A%20%20%20%20%20%20%20%20%20%20%20%20panel%5B%22Export_Value_1000USD%22%5D%20%3E%200%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20np.log(panel%5B%22Export_Value_1000USD%22%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20np.nan%2C%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20panel%5B%22Rain_mm_country_mean%22%5D%20%3D%20panel.groupby(%22ISO3%22)%5B%22Rain_mm%22%5D.transform(%22mean%22)%0A%20%20%20%20panel%5B%22Rain_mm_within%22%5D%20%3D%20panel%5B%22Rain_mm%22%5D%20-%20panel%5B%22Rain_mm_country_mean%22%5D%0A%0A%20%20%20%20print(%0A%20%20%20%20%20%20%20%20f%22Loaded%20%7Blen(panel)%3A%2C%7D%20complete%20production%20rows%20across%20%22%0A%20%20%20%20%20%20%20%20f%22%7Bpanel%5B'ISO3'%5D.nunique()%7D%20countries%20from%20%7Bpanel%5B'Year'%5D.min()%7D-%7Bpanel%5B'Year'%5D.max()%7D.%22%0A%20%20%20%20)%0A%20%20%20%20return%20(panel%2C)%0A%0A%0A%40app.cell%0Adef%20_(mo%2C%20panel%2C%20pd)%3A%0A%20%20%20%20_summary%20%3D%20pd.DataFrame(%7B%0A%20%20%20%20%20%20%20%20%22Metric%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Rows%20used%20for%20production%20follow-up%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Countries%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Years%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Countries%20with%20%3E%3D%2015%20observations%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Countries%20with%20time-varying%20rainfall%22%2C%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%22Value%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22%7Blen(panel)%3A%2C%7D%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22%7Bpanel%5B'ISO3'%5D.nunique()%3A%2C%7D%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22%7Bpanel%5B'Year'%5D.min()%7D-%7Bpanel%5B'Year'%5D.max()%7D%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22%7B(panel.groupby('ISO3').size()%20%3E%3D%2015).sum()%3A%2C%7D%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22%7B(panel.groupby('ISO3')%5B'Rain_mm'%5D.nunique()%20%3E%3D%205).sum()%3A%2C%7D%22%2C%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%7D)%0A%0A%20%20%20%20mo.md(f%22%22%22%0A%20%20%20%20%23%23%23%20Dataset%20Snapshot%0A%0A%20%20%20%20%7Bmo.as_html(_summary)%7D%0A%0A%20%20%20%20%3E%20**Rainfall%20caution%3A**%20Rainfall%20only%20works%20as%20a%20within-country%20annual%20signal%20when%20%60Rain_mm%60%20changes%20over%20time.%20For%20countries%20with%20one%20repeated%20rainfall%20value%2C%20rainfall%20is%20a%20structural%20climate%20descriptor%2C%20not%20an%20annual%20weather%20variable.%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---%0A%20%20%20%20%23%23%201.%20Screening%20Logic%0A%0A%20%20%20%20The%20follow-up%20uses%20a%20two-step%20idea%3A%0A%0A%20%20%20%201.%20Fit%20a%20population-only%20model%20within%20each%20country%3A%0A%0A%20%20%20%20%20%20%20%24%24%5Cln(Production)%20%3D%20%5Cbeta_0%20%2B%20%5Cbeta_1%5Cln(Population)%20%2B%20%5Cvarepsilon%24%24%0A%0A%20%20%20%202.%20Correlate%20the%20residuals%20from%20that%20model%20with%20environmental%20variables.%0A%0A%20%20%20%20A%20residual%20is%3A%0A%0A%20%20%20%20%24%24e_i%20%3D%20y_i%20-%20%5Chat%7By%7D_i%24%24%0A%0A%20%20%20%20In%20context%2C%20it%20means%3A%20actual%20log%20production%20minus%20the%20log%20production%20expected%20from%20population%20alone.%0A%0A%20%20%20%20%3E%20**Partial%20correlation%20(FWL-complete).**%20The%20reported%20r%20values%20are%20**true%20FWL%20partial%20correlations**%3A%20both%20the%20response%20*and*%20each%20environmental%20predictor%20are%20separately%20regressed%20on%20ln(Population)%2C%20and%20their%20residuals%20are%20then%20correlated.%20This%20removes%20any%20spurious%20correlation%20between%20the%20environmental%20predictor%20and%20the%20response%20that%20runs%20through%20population.%20For%20formal%20H%E2%82%80%2FH%E2%82%81%20inference%20see%20the%20main%20workbook%20%C2%A72%E2%80%93%C2%A75.%20The%20standardized-%CE%B2%20columns%20in%20the%20ranking%20table%20below%20come%20from%20a%20full%20multivariate%20fit%20per%20country%20and%20measure%20a%20related%20but%20distinct%20quantity%20(partial%20regression%20coefficient%20after%20all%20other%20predictors%20are%20controlled).%20The%20two%20effect-size%20columns%20come%20from%20different%20models%20and%20are%20not%20directly%20comparable.%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%23%20Course%20context%3A%20FWL%20vs.%20General%20Factorial%20Analysis%20vs.%20RCBD%20(Section%204)%0A%0A%20%20%20%20The%20FWL%20theorem%20is%20an%20extracurricular%20research%20technique%20used%20here%20to%20accomplish%20something%0A%20%20%20%20similar%20to%20what%20we%20did%20with%20**General%20Factorial%20Analysis**%20and%20**RCBD**%20in%20Section%204%20of%20the%20course.%0A%20%20%20%20Understanding%20the%20parallels%20and%20differences%20grounds%20this%20analysis%20in%20familiar%20territory.%0A%0A%20%20%20%20---%0A%0A%20%20%20%20**What%20each%20approach%20does**%0A%0A%20%20%20%20*FWL%20(this%20notebook)*%20%E2%80%94%20A%20theorem%20about%20regression%3A%20regress%20both%20Y%20and%20a%20focal%20predictor%20X%E2%82%82%0A%20%20%20%20on%20a%20control%20variable%20X%E2%82%81%2C%20keep%20both%20sets%20of%20residuals%2C%20then%20correlate%20them.%20The%20result%20equals%0A%20%20%20%20the%20coefficient%20X%E2%82%82%20would%20get%20in%20a%20full%20MLR%20with%20both%20predictors%20included.%20Here%2C%20ln(Population)%0A%20%20%20%20is%20the%20control%3B%20temperature%20and%20rainfall%20are%20the%20focal%20predictors.%0A%0A%20%20%20%20*General%20Factorial%20(Section%204)*%20%E2%80%94%20An%20experimental%20design%20that%20tests%20all%20combinations%20of%20k%0A%20%20%20%20factors%20at%20controlled%20levels.%20ANOVA%20partitions%20variance%20into%20main%20effects%2C%20interaction%20effects%2C%0A%20%20%20%20and%20error.%20The%20key%20capability%20factorial%20has%20that%20FWL%20lacks%3A%20**detecting%20interactions**%20%E2%80%94%20whether%0A%20%20%20%20the%20effect%20of%20Factor%20A%20depends%20on%20the%20level%20of%20Factor%20B.%0A%0A%20%20%20%20*RCBD%20(Section%204)*%20%E2%80%94%20A%20design%20for%20one%20factor%20of%20interest%20plus%20one%20**nuisance%20factor**%20(the%0A%20%20%20%20block).%20Each%20treatment%20appears%20exactly%20once%20per%20block.%20ANOVA%20partitions%20SS_Total%20into%0A%20%20%20%20SS_Treatments%20%2B%20SS_Blocks%20%2B%20SS_Error%2C%20removing%20block-to-block%20variability%20from%20the%20error%20term%0A%20%20%20%20to%20give%20a%20more%20powerful%20test%20of%20treatments.%0A%0A%20%20%20%20---%0A%0A%20%20%20%20**How%20they%20overlap**%0A%0A%20%20%20%20All%20three%20are%20doing%20the%20same%20thing%20conceptually%3A%20**isolating%20the%20effect%20of%20one%20variable%20by%0A%20%20%20%20accounting%20for%20another.**%20They%20differ%20only%20in%20how%20they%20achieve%20that%20control%3A%0A%0A%20%20%20%20%7C%20%7C%20FWL%20%7C%20RCBD%20%7C%20Factorial%20%7C%0A%20%20%20%20%7C---%7C---%7C---%7C---%7C%0A%20%20%20%20%7C%20How%20control%20is%20achieved%20%7C%20Algebraic%20residualization%20of%20observed%20data%20%7C%20Block%20structure%20absorbs%20nuisance%20SS%20before%20the%20F-test%20%7C%20Randomization%20makes%20factors%20orthogonal%20by%20design%20%7C%0A%20%20%20%20%7C%20Data%20source%20%7C%20Observational%20panel%20%7C%20Designed%20experiment%20%7C%20Designed%20experiment%20%7C%0A%20%20%20%20%7C%20Nuisance%20variable%20role%20%7C%20Partialed%20out%20of%20both%20Y%20and%20X%20%7C%20Absorbed%20into%20SS_Blocks%20%7C%20Balanced%20across%20factor%20levels%20by%20randomization%20%7C%0A%20%20%20%20%7C%20Interaction%20estimated%3F%20%7C%20No%20%7C%20No%20(assumed%20absent%20%E2%80%94%20RCBD%20requires%20%60%2B%60%20not%20%60*%60)%20%7C%20Yes%20%E2%80%94%20core%20purpose%20%7C%0A%0A%20%20%20%20**FWL%20is%20actually%20most%20similar%20to%20RCBD**%2C%20not%20to%20factorial.%20In%20RCBD%2C%20the%20%22block%22%20(country%2C%20in%0A%20%20%20%20this%20analogy)%20is%20a%20nuisance%20factor%20you%20want%20to%20remove%20before%20testing%20treatments.%20The%20RCBD%20model%0A%20%20%20%20is%20additive%20%E2%80%94%20%60aov(obs%20~%20trt%20%2B%20blk)%60%20%E2%80%94%20and%20explicitly%20assumes%20no%20treatment-by-block%20interaction%2C%0A%20%20%20%20just%20as%20FWL%20assumes%20population%20is%20an%20additive%20nuisance%20separable%20from%20the%20environmental%20signal.%0A%20%20%20%20RCBD%20removes%20SS_Blocks%20from%20SS_Error%20mathematically%3B%20FWL%20removes%20the%20population%20effect%20by%0A%20%20%20%20residualization%20%E2%80%94%20two%20routes%20to%20the%20same%20destination.%0A%0A%20%20%20%20---%0A%0A%20%20%20%20**The%20critical%20differences**%0A%0A%20%20%20%20*Causal%20inference%3A*%20Factorial%20and%20RCBD%20designs%20use%20randomized%20assignment%2C%20so%20their%20F-tests%0A%20%20%20%20support%20causal%20claims.%20FWL%20operates%20on%20observational%20data%20%E2%80%94%20the%20partial%20correlation%20is%20a%20cleaner%0A%20%20%20%20measure%20than%20raw%20r%2C%20but%20unmeasured%20confounders%20(altitude%2C%20variety%2C%20trade%20policy)%20cannot%20be%20ruled%0A%20%20%20%20out.%20Statistical%20control%20is%20not%20the%20same%20as%20experimental%20control.%0A%0A%20%20%20%20*Interactions%3A*%20FWL%20is%20not%20a%20design%20tool%20%E2%80%94%20it%20is%20used%20here%20to%20evaluate%20data%20after%20the%20fact%2C%0A%20%20%20%20partializing%20out%20one%20nuisance%20variable%20(population).%20It%20does%20not%20detect%20interactions.%20If%0A%20%20%20%20temperature%20and%20rainfall%20interact%20in%20their%20effect%20on%20production%20(e.g.%2C%20heat%20is%20damaging%20only%20in%0A%20%20%20%20dry%20conditions)%2C%20FWL%20would%20miss%20it%20entirely.%20A%20factorial%20design%20with%20Temperature%20%C3%97%20Rainfall%20%C3%97%0A%20%20%20%20Population%20as%20factors%20would%20catch%20it%3B%20a%20panel%20regression%20with%20an%20interaction%20term%0A%20%20%20%20(%60Temp%20*%20Rain_mm_within%60)%20would%20be%20the%20observational%20equivalent.%0A%0A%20%20%20%20---%0A%0A%20%20%20%20%23%23%23%23%20Mathematical%20equivalence%3A%20FWL%20and%20RCBD%0A%0A%20%20%20%20Both%20reduce%20to%20the%20same%20matrix%20operation.%20Define%20the%20**annihilator%20matrix**%20for%20nuisance%20variable%20X%E2%82%81%3A%0A%0A%20%20%20%20%24%24M_1%20%3D%20I%20-%20X_1(X_1%5E%5Ctop%20X_1)%5E%7B-1%7DX_1%5E%5Ctop%24%24%0A%0A%20%20%20%20M%E2%82%81%20projects%20any%20vector%20onto%20the%20space%20orthogonal%20to%20X%E2%82%81%20%E2%80%94%20it%20is%20the%20operator%20that%20%22removes%20X%E2%82%81.%22%0A%0A%20%20%20%20**FWL**%20gives%20the%20%CE%B2%E2%82%82%20estimate%20as%3A%0A%20%20%20%20%24%24%5Chat%7B%5Cbeta%7D_2%20%3D%20(X_2%5E%5Ctop%20M_1%20X_2)%5E%7B-1%7D%20X_2%5E%5Ctop%20M_1%20Y%24%24%0A%20%20%20%20i.e.%2C%20regress%20%24%5Ctilde%7BY%7D%20%3D%20M_1%20Y%24%20on%20%24%5Ctilde%7BX%7D_2%20%3D%20M_1%20X_2%24%20%E2%80%94%20both%20variables%20residualized%20on%20X%E2%82%81%20first.%0A%0A%20%20%20%20**RCBD%20treatment%20estimate**%20(set%20X%E2%82%81%20%3D%20block%20dummy%20matrix%2C%20X%E2%82%82%20%3D%20treatment%20dummy%20matrix)%20solves%20the%20identical%20equation%3A%0A%20%20%20%20%24%24%5Chat%7B%5Ctau%7D%20%3D%20(X_T%5E%5Ctop%20M_B%20X_T)%5E%7B-1%7D%20X_T%5E%5Ctop%20M_B%20Y%24%24%0A%0A%20%20%20%20%7C%20%7C%20FWL%20(this%20notebook)%20%7C%20RCBD%20%7C%0A%20%20%20%20%7C---%7C---%7C---%7C%0A%20%20%20%20%7C%20M%20%3D%20%7C%20Population-regression%20annihilator%20%7C%20Block-demeaning%20matrix%20%7C%0A%20%20%20%20%7C%20%24%5Ctilde%7BY%7D%24%20%7C%20Production%20residuals%20after%20removing%20ln(Pop)%20%7C%20%24Y_%7Bij%7D%20-%20%5Cbar%7BY%7D_%7B.j%7D%24%20(block-demeaned%20Y)%20%7C%0A%20%20%20%20%7C%20%24%5Ctilde%7BX%7D_2%24%20%7C%20Temp%2FRain%20residuals%20after%20removing%20ln(Pop)%20%7C%20Treatment%20indicators%20%7C%0A%0A%20%20%20%20**The%20one%20real%20difference%3A**%20RCBD's%20experimental%20design%20forces%20%24X_B%20%5Cperp%20X_T%24%2C%20so%0A%20%20%20%20%24M_B%20X_T%20%3D%20X_T%24%20%E2%80%94%20block-demeaning%20doesn't%20change%20treatment%20indicators%20because%20each%0A%20%20%20%20treatment%20appears%20exactly%20once%20per%20block.%20SS_Treatments%20and%20SS_Blocks%20are%20fully%0A%20%20%20%20independent%20contrasts.%0A%0A%20%20%20%20In%20the%20coffee%20data%2C%20ln(Population)%20*correlates*%20with%20temperature%2C%20so%20%24M_1%20X_2%20%5Cneq%20X_2%24%0A%20%20%20%20%E2%80%94%20the%20residualization%20is%20doing%20real%20algebraic%20work%2C%20not%20a%20cosmetic%20adjustment.%0A%0A%20%20%20%20The%20%60anova_lm%60%20incremental%20F-test%20used%20in%20%C2%A71%20is%20structurally%20identical%20to%20RCBD's%0A%20%20%20%20%24F_0%20%3D%20MS_%7B%5Ctext%7BTrt%7D%7D%2FMS_E%24%3A%20both%20measure%20reduction%20in%20SS_Error%20from%20adding%20the%20focal%0A%20%20%20%20terms%20after%20the%20nuisance%20variable%20is%20accounted%20for.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(anova_lm%2C%20durbin_watson%2C%20multipletests%2C%20np%2C%20panel%2C%20pd%2C%20sm%2C%20stats)%3A%0A%20%20%20%20MIN_OBS%20%3D%2015%0A%20%20%20%20MIN_UNIQUE%20%3D%205%0A%0A%20%20%20%20%23%20Heuristic%20screening%20thresholds%20%E2%80%94%20NOT%20formal%20hypothesis%20tests.%0A%20%20%20%20%23%20Used%20to%20assign%20candidate%20tiers%20for%20case-study%20selection%20only.%0A%20%20%20%20TIER_STRONG_R%20%20%20%3D%200.45%20%20%20%23%20%7Cr%7C%20between%20env%20var%20and%20pop-adjusted%20residual%20(partial)%0A%20%20%20%20TIER_STRONG_DR2%20%3D%200.10%20%20%20%23%20env%20vars%20must%20add%20%E2%89%A510pp%20R%C2%B2%20beyond%20population%0A%20%20%20%20TIER_MOD_R%20%20%20%20%20%20%3D%200.35%20%20%20%23%20%7Cr%7C%20threshold%20for%20Moderate%20tier%0A%20%20%20%20TIER_MOD_P%20%20%20%20%20%20%3D%200.10%20%20%20%23%20p-value%20threshold%20for%20Moderate%20tier%20(BH-adjusted)%0A%0A%20%20%20%20def%20_fit_r2_on_common(y%2C%20pop_col%2C%20env_cols%2C%20data)%3A%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20Compute%20R%C2%B2%20for%20population-only%2C%20env-only%2C%20and%20full%20models%20on%20the%20same%0A%20%20%20%20%20%20%20%20complete-case%20subsample%20(avoids%20comparing%20R%C2%B2%20values%20from%20different%20n).%0A%20%20%20%20%20%20%20%20Returns%20(r2_pop%2C%20r2_env%2C%20r2_full)%20or%20(nan%2C%20nan%2C%20nan)%20on%20failure.%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20_all_cols%20%3D%20%5By%5D%20%2B%20%5Bpop_col%5D%20%2B%20env_cols%0A%20%20%20%20%20%20%20%20_d%20%3D%20data%5B_all_cols%5D.dropna()%0A%20%20%20%20%20%20%20%20_n_env%20%3D%20len(env_cols)%0A%20%20%20%20%20%20%20%20if%20len(_d)%20%3C%20max(_n_env%20%2B%205%2C%20MIN_OBS)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20np.nan%2C%20np.nan%2C%20np.nan%0A%20%20%20%20%20%20%20%20_y%20%3D%20_d%5By%5D%0A%20%20%20%20%20%20%20%20%23%20Population-only%0A%20%20%20%20%20%20%20%20_x_pop%20%3D%20sm.add_constant(_d%5B%5Bpop_col%5D%5D%2C%20has_constant%3D%22add%22)%0A%20%20%20%20%20%20%20%20%23%20Env-only%0A%20%20%20%20%20%20%20%20_x_env%20%3D%20sm.add_constant(_d%5Benv_cols%5D%2C%20has_constant%3D%22add%22)%0A%20%20%20%20%20%20%20%20%23%20Full%0A%20%20%20%20%20%20%20%20_x_full%20%3D%20sm.add_constant(_d%5B%5Bpop_col%5D%20%2B%20env_cols%5D%2C%20has_constant%3D%22add%22)%0A%20%20%20%20%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_r2_pop%20%20%3D%20sm.OLS(_y%2C%20_x_pop).fit().rsquared%0A%20%20%20%20%20%20%20%20%20%20%20%20_r2_env%20%20%3D%20sm.OLS(_y%2C%20_x_env).fit().rsquared%20if%20_n_env%20%3E%200%20else%20np.nan%0A%20%20%20%20%20%20%20%20%20%20%20%20_r2_full%20%3D%20sm.OLS(_y%2C%20_x_full).fit().rsquared%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20_r2_pop%2C%20_r2_env%2C%20_r2_full%0A%20%20%20%20%20%20%20%20except%20Exception%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20np.nan%2C%20np.nan%2C%20np.nan%0A%0A%20%20%20%20def%20_standardized_fit(group_df%2C%20predictors)%3A%0A%20%20%20%20%20%20%20%20_cols%20%3D%20%5B%22ln_Production%22%5D%20%2B%20predictors%0A%20%20%20%20%20%20%20%20_d%20%3D%20group_df%5B_cols%5D.dropna().copy()%0A%20%20%20%20%20%20%20%20if%20len(_d)%20%3C%20len(predictors)%20%2B%205%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20None%0A%20%20%20%20%20%20%20%20_std%20%3D%20_d.std(ddof%3D0)%0A%20%20%20%20%20%20%20%20if%20(_std%20%3D%3D%200).any()%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20None%0A%20%20%20%20%20%20%20%20_z%20%3D%20(_d%20-%20_d.mean())%20%2F%20_std%0A%20%20%20%20%20%20%20%20_x%20%3D%20sm.add_constant(_z%5Bpredictors%5D%2C%20has_constant%3D%22add%22)%0A%20%20%20%20%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20sm.OLS(_z%5B%22ln_Production%22%5D%2C%20_x).fit()%0A%20%20%20%20%20%20%20%20except%20Exception%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20None%0A%0A%20%20%20%20country_rows%20%3D%20%5B%5D%0A%20%20%20%20residual_frames%20%3D%20%5B%5D%0A%0A%20%20%20%20for%20_iso3%2C%20_g%20in%20panel.groupby(%22ISO3%22)%3A%0A%20%20%20%20%20%20%20%20_g%20%3D%20_g.sort_values(%22Year%22).copy()%0A%0A%20%20%20%20%20%20%20%20if%20len(_g)%20%3C%20MIN_OBS%20or%20_g%5B%22ln_Population%22%5D.nunique()%20%3C%20MIN_UNIQUE%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20continue%0A%0A%20%20%20%20%20%20%20%20%23%20---%20Step%201%3A%20Fit%20population-only%20model%20and%20extract%20production%20residuals%20---%0A%20%20%20%20%20%20%20%20_pop_x%20%3D%20sm.add_constant(_g%5B%5B%22ln_Population%22%5D%5D%2C%20has_constant%3D%22add%22)%0A%20%20%20%20%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_pop_fit%20%3D%20sm.OLS(_g%5B%22ln_Production%22%5D%2C%20_pop_x).fit()%0A%20%20%20%20%20%20%20%20except%20Exception%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20continue%0A%0A%20%20%20%20%20%20%20%20_g%5B%22prod_resid_after_pop%22%5D%20%3D%20_pop_fit.resid%0A%20%20%20%20%20%20%20%20residual_frames.append(_g%5B%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22ISO3%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Country_Name%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Year%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22ln_Production%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Production_tonnes%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22ln_Population%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Avg_Temp_C%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Rain_mm%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Oil_Price_Brent_USD%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22prod_resid_after_pop%22%2C%0A%20%20%20%20%20%20%20%20%5D%5D)%0A%0A%20%20%20%20%20%20%20%20%23%20Fix%204%20%E2%80%94%20Durbin-Watson%20statistic%20on%20production%20residuals%20(diagnostic%20for%20serial%20autocorrelation).%0A%20%20%20%20%20%20%20%20%23%20DW%20near%202.0%20%3D%20low%20autocorrelation%3B%20DW%20%3C%201.5%20suggests%20positive%20AR(1)%2C%20inflating%20p-values.%0A%20%20%20%20%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_dw_stat%20%3D%20float(durbin_watson(_pop_fit.resid))%0A%20%20%20%20%20%20%20%20except%20Exception%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_dw_stat%20%3D%20np.nan%0A%0A%20%20%20%20%20%20%20%20_temp_ok%20%3D%20_g%5B%22Avg_Temp_C%22%5D.nunique()%20%3E%3D%20MIN_UNIQUE%0A%20%20%20%20%20%20%20%20_rain_ok%20%3D%20_g%5B%22Rain_mm%22%5D.nunique()%20%3E%3D%20MIN_UNIQUE%0A%20%20%20%20%20%20%20%20_env_vars%20%3D%20%5B%5D%0A%20%20%20%20%20%20%20%20if%20_temp_ok%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_env_vars.append(%22Avg_Temp_C%22)%0A%20%20%20%20%20%20%20%20if%20_rain_ok%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_env_vars.append(%22Rain_mm%22)%0A%0A%20%20%20%20%20%20%20%20%23%20---%20Fix%201%3A%20True%20FWL%20partial%20correlations%20---%0A%20%20%20%20%20%20%20%20%23%20Residualize%20BOTH%20the%20response%20AND%20each%20environmental%20predictor%20on%20ln(Population)%2C%0A%20%20%20%20%20%20%20%20%23%20then%20correlate%20the%20two%20sets%20of%20residuals.%20This%20is%20the%20correct%20FWL%20partial%20correlation.%0A%20%20%20%20%20%20%20%20_r_temp%2C%20_p_temp%20%3D%20(np.nan%2C%20np.nan)%0A%20%20%20%20%20%20%20%20_r_rain%2C%20_p_rain%20%3D%20(np.nan%2C%20np.nan)%0A%0A%20%20%20%20%20%20%20%20_prod_resid%20%3D%20_g%5B%22prod_resid_after_pop%22%5D.values%20%20%23%20y%20residuals%20(already%20computed)%0A%0A%20%20%20%20%20%20%20%20if%20_temp_ok%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Regress%20Avg_Temp_C%20on%20ln(Population)%20%E2%86%92%20extract%20temp%20residuals%0A%20%20%20%20%20%20%20%20%20%20%20%20_temp_data%20%3D%20_g%5B%5B%22Avg_Temp_C%22%2C%20%22ln_Population%22%5D%5D.dropna()%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20len(_temp_data)%20%3E%3D%20MIN_OBS%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20_temp_pop_x%20%3D%20sm.add_constant(_temp_data%5B%5B%22ln_Population%22%5D%5D%2C%20has_constant%3D%22add%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20_temp_resid%20%3D%20sm.OLS(_temp_data%5B%22Avg_Temp_C%22%5D%2C%20_temp_pop_x).fit().resid%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20Align%20indices%3A%20use%20the%20intersection%20of%20the%20production%20residual%20index%20and%20temp%20residual%20index%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20_common_idx%20%3D%20_g.index.intersection(_temp_resid.index)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20_r_temp%2C%20_p_temp%20%3D%20stats.pearsonr(%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_prod_resid%5B_g.index.get_indexer(_common_idx)%5D%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_temp_resid.loc%5B_common_idx%5D.values%2C%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%20except%20Exception%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pass%0A%0A%20%20%20%20%20%20%20%20if%20_rain_ok%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Regress%20Rain_mm%20on%20ln(Population)%20%E2%86%92%20extract%20rain%20residuals%0A%20%20%20%20%20%20%20%20%20%20%20%20_rain_data%20%3D%20_g%5B%5B%22Rain_mm%22%2C%20%22ln_Population%22%5D%5D.dropna()%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20len(_rain_data)%20%3E%3D%20MIN_OBS%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20_rain_pop_x%20%3D%20sm.add_constant(_rain_data%5B%5B%22ln_Population%22%5D%5D%2C%20has_constant%3D%22add%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20_rain_resid%20%3D%20sm.OLS(_rain_data%5B%22Rain_mm%22%5D%2C%20_rain_pop_x).fit().resid%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20_common_idx%20%3D%20_g.index.intersection(_rain_resid.index)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20_r_rain%2C%20_p_rain%20%3D%20stats.pearsonr(%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_prod_resid%5B_g.index.get_indexer(_common_idx)%5D%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_rain_resid.loc%5B_common_idx%5D.values%2C%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%20except%20Exception%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pass%0A%0A%20%20%20%20%20%20%20%20%23%20---%20R%C2%B2%20values%20computed%20on%20a%20common%20complete-case%20sample%20(Fix%206.2)%20---%0A%20%20%20%20%20%20%20%20_r2_pop%2C%20_r2_env%2C%20_r2_full%20%3D%20_fit_r2_on_common(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22ln_Production%22%2C%20%22ln_Population%22%2C%20_env_vars%2C%20_g%0A%20%20%20%20%20%20%20%20)%20if%20_env_vars%20else%20(np.nan%2C%20np.nan%2C%20np.nan)%0A%20%20%20%20%20%20%20%20%23%20If%20no%20env_vars%2C%20r2_pop%20from%20the%20individual%20fit%20above%0A%20%20%20%20%20%20%20%20if%20not%20_env_vars%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_r2_pop%20%3D%20_pop_fit.rsquared%0A%20%20%20%20%20%20%20%20_delta_env%20%3D%20_r2_full%20-%20_r2_pop%20if%20(pd.notna(_r2_full)%20and%20pd.notna(_r2_pop))%20else%20np.nan%0A%20%20%20%20%20%20%20%20_delta_pop%20%3D%20_r2_full%20-%20_r2_env%20if%20(pd.notna(_r2_full)%20and%20pd.notna(_r2_env))%20else%20np.nan%0A%0A%20%20%20%20%20%20%20%20%23%20---%20Fix%203%3A%20Incremental%20F-test%20for%20environmental%20variables%20beyond%20population%20---%0A%20%20%20%20%20%20%20%20_p_f_incr%20%3D%20np.nan%0A%20%20%20%20%20%20%20%20if%20_env_vars%20and%20pd.notna(_r2_full)%20and%20pd.notna(_r2_pop)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_all_cols_incr%20%3D%20%5B%22ln_Production%22%2C%20%22ln_Population%22%5D%20%2B%20_env_vars%0A%20%20%20%20%20%20%20%20%20%20%20%20_d_incr%20%3D%20_g%5B_all_cols_incr%5D.dropna()%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20len(_d_incr)%20%3E%3D%20len(_env_vars)%20%2B%20MIN_OBS%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20_y_incr%20%3D%20_d_incr%5B%22ln_Production%22%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20_x_rest%20%3D%20sm.add_constant(_d_incr%5B%5B%22ln_Population%22%5D%5D%2C%20has_constant%3D%22add%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20_x_full%20%3D%20sm.add_constant(_d_incr%5B%5B%22ln_Population%22%5D%20%2B%20_env_vars%5D%2C%20has_constant%3D%22add%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20_fit_rest%20%3D%20sm.OLS(_y_incr%2C%20_x_rest).fit()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20_fit_full%20%3D%20sm.OLS(_y_incr%2C%20_x_full).fit()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20_anova_tbl%20%3D%20anova_lm(_fit_rest%2C%20_fit_full)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20_p_f_incr%20%3D%20float(_anova_tbl%5B%22Pr(%3EF)%22%5D.iloc%5B1%5D)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20except%20Exception%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pass%0A%0A%20%20%20%20%20%20%20%20_std_fit%20%3D%20_standardized_fit(_g%2C%20%5B%22ln_Population%22%5D%20%2B%20_env_vars)%0A%20%20%20%20%20%20%20%20_beta_pop%20%3D%20np.nan%0A%20%20%20%20%20%20%20%20_beta_temp%20%3D%20np.nan%0A%20%20%20%20%20%20%20%20_beta_rain%20%3D%20np.nan%0A%20%20%20%20%20%20%20%20_p_beta_temp%20%3D%20np.nan%0A%20%20%20%20%20%20%20%20_p_beta_rain%20%3D%20np.nan%0A%20%20%20%20%20%20%20%20if%20_std_fit%20is%20not%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_beta_pop%20%3D%20_std_fit.params.get(%22ln_Population%22%2C%20np.nan)%0A%20%20%20%20%20%20%20%20%20%20%20%20_beta_temp%20%3D%20_std_fit.params.get(%22Avg_Temp_C%22%2C%20np.nan)%0A%20%20%20%20%20%20%20%20%20%20%20%20_beta_rain%20%3D%20_std_fit.params.get(%22Rain_mm%22%2C%20np.nan)%0A%20%20%20%20%20%20%20%20%20%20%20%20_p_beta_temp%20%3D%20_std_fit.pvalues.get(%22Avg_Temp_C%22%2C%20np.nan)%0A%20%20%20%20%20%20%20%20%20%20%20%20_p_beta_rain%20%3D%20_std_fit.pvalues.get(%22Rain_mm%22%2C%20np.nan)%0A%0A%20%20%20%20%20%20%20%20_env_abs%20%3D%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20abs(_r_temp)%20if%20pd.notna(_r_temp)%20else%20np.nan%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20abs(_r_rain)%20if%20pd.notna(_r_rain)%20else%20np.nan%2C%0A%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20_max_abs_env_r%20%3D%20np.nanmax(_env_abs)%0A%20%20%20%20%20%20%20%20if%20pd.isna(_r_temp)%20and%20pd.isna(_r_rain)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_best_env%20%3D%20None%0A%20%20%20%20%20%20%20%20%20%20%20%20_best_env_p%20%3D%20np.nan%0A%20%20%20%20%20%20%20%20elif%20pd.isna(_r_rain)%20or%20(pd.notna(_r_temp)%20and%20abs(_r_temp)%20%3E%3D%20abs(_r_rain))%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_best_env%20%3D%20%22Temperature%22%0A%20%20%20%20%20%20%20%20%20%20%20%20_best_env_p%20%3D%20_p_temp%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_best_env%20%3D%20%22Rainfall%22%0A%20%20%20%20%20%20%20%20%20%20%20%20_best_env_p%20%3D%20_p_rain%0A%20%20%20%20%20%20%20%20_env_dominates_r2%20%3D%20pd.notna(_delta_env)%20and%20pd.notna(_delta_pop)%20and%20_delta_env%20%3E%20_delta_pop%0A%20%20%20%20%20%20%20%20_max_env_beta%20%3D%20np.nanmax(%5Babs(_beta_temp)%20if%20pd.notna(_beta_temp)%20else%20np.nan%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%20%20%20%20%20%20%20%20%20%20%20abs(_beta_rain)%20if%20pd.notna(_beta_rain)%20else%20np.nan%5D)%0A%20%20%20%20%20%20%20%20_env_dominates_beta%20%3D%20pd.notna(_max_env_beta)%20and%20pd.notna(_beta_pop)%20and%20_max_env_beta%20%3E%20abs(_beta_pop)%0A%0A%20%20%20%20%20%20%20%20%23%20Tier%20will%20be%20reassigned%20after%20BH%20correction%20(Fix%202)%3B%20store%20raw%20values%20for%20now.%0A%20%20%20%20%20%20%20%20country_rows.append(%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22ISO3%22%3A%20_iso3%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Country%22%3A%20_g%5B%22Country_Name%22%5D.iloc%5B0%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22n%22%3A%20len(_g)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Rain%20unique%22%3A%20int(_g%5B%22Rain_mm%22%5D.nunique())%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Population%20R2%22%3A%20_r2_pop%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Environment-only%20R2%22%3A%20_r2_env%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Full%20R2%22%3A%20_r2_full%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Environment%20added%20R2%22%3A%20_delta_env%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Population%20added%20R2%22%3A%20_delta_pop%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22r%20Temp%20after%20Pop%20(partial)%22%3A%20_r_temp%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22p%20Temp%22%3A%20_p_temp%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22r%20Rain%20after%20Pop%20(partial)%22%3A%20_r_rain%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22p%20Rain%22%3A%20_p_rain%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22p%20F-incr%22%3A%20_p_f_incr%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22DW%20stat%22%3A%20_dw_stat%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Std%20beta%20Pop%22%3A%20_beta_pop%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Std%20beta%20Temp%22%3A%20_beta_temp%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Std%20beta%20Rain%22%3A%20_beta_rain%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22p%20beta%20Temp%22%3A%20_p_beta_temp%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22p%20beta%20Rain%22%3A%20_p_beta_rain%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Max%20abs%20env%20r%22%3A%20_max_abs_env_r%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Best%20environmental%20variable%22%3A%20_best_env%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22_best_env_p_raw%22%3A%20_best_env_p%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22_best_env_label%22%3A%20_best_env%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Env%20added%20R2%20%3E%20Pop%20added%20R2%22%3A%20_env_dominates_r2%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Env%20beta%20%3E%20Pop%20beta%22%3A%20_env_dominates_beta%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Temp%2Frain%20raw%20p%20stored%20separately%20so%20BH%20can%20be%20applied%20below%0A%20%20%20%20%20%20%20%20%20%20%20%20%22_p_temp_raw%22%3A%20_p_temp%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22_p_rain_raw%22%3A%20_p_rain%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22_delta_env%22%3A%20_delta_env%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22_env_dominates_r2%22%3A%20_env_dominates_r2%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22_max_abs_env_r%22%3A%20_max_abs_env_r%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22_p_f_incr%22%3A%20_p_f_incr%2C%0A%20%20%20%20%20%20%20%20%7D)%0A%0A%20%20%20%20%23%20---%20Fix%202%3A%20Benjamini-Hochberg%20FDR%20correction%20across%20all%20country-level%20p-values%20---%0A%20%20%20%20_screen_df%20%3D%20pd.DataFrame(country_rows)%0A%0A%20%20%20%20%23%20Collect%20raw%20p-values%3B%20treat%20NaN%20as%201.0%20for%20correction%20purposes%20(no%20evidence)%0A%20%20%20%20_p_temp_arr%20%3D%20_screen_df%5B%22_p_temp_raw%22%5D.fillna(1.0).values%0A%20%20%20%20_p_rain_arr%20%3D%20_screen_df%5B%22_p_rain_raw%22%5D.fillna(1.0).values%0A%0A%20%20%20%20_%2C%20_p_temp_adj%2C%20_%2C%20_%20%3D%20multipletests(_p_temp_arr%2C%20method%3D%22fdr_bh%22)%0A%20%20%20%20_%2C%20_p_rain_adj%2C%20_%2C%20_%20%3D%20multipletests(_p_rain_arr%2C%20method%3D%22fdr_bh%22)%0A%0A%20%20%20%20%23%20Restore%20NaN%20where%20the%20original%20was%20NaN%20(country%20lacked%20enough%20variation)%0A%20%20%20%20_p_temp_adj%20%3D%20np.where(_screen_df%5B%22_p_temp_raw%22%5D.isna()%2C%20np.nan%2C%20_p_temp_adj)%0A%20%20%20%20_p_rain_adj%20%3D%20np.where(_screen_df%5B%22_p_rain_raw%22%5D.isna()%2C%20np.nan%2C%20_p_rain_adj)%0A%0A%20%20%20%20_screen_df%5B%22p%20Temp%20(BH-adj)%22%5D%20%3D%20_p_temp_adj%0A%20%20%20%20_screen_df%5B%22p%20Rain%20(BH-adj)%22%5D%20%3D%20_p_rain_adj%0A%0A%20%20%20%20%23%20Derive%20BH-adjusted%20best-env%20p%20for%20tier%20classification%0A%20%20%20%20def%20_bh_best_p(row)%3A%0A%20%20%20%20%20%20%20%20_t%20%3D%20row%5B%22p%20Temp%20(BH-adj)%22%5D%20if%20pd.notna(row%5B%22p%20Temp%20(BH-adj)%22%5D)%20else%20np.nan%0A%20%20%20%20%20%20%20%20_r%20%3D%20row%5B%22p%20Rain%20(BH-adj)%22%5D%20if%20pd.notna(row%5B%22p%20Rain%20(BH-adj)%22%5D)%20else%20np.nan%0A%20%20%20%20%20%20%20%20if%20row%5B%22_best_env_label%22%5D%20%3D%3D%20%22Temperature%22%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20_t%0A%20%20%20%20%20%20%20%20elif%20row%5B%22_best_env_label%22%5D%20%3D%3D%20%22Rainfall%22%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20_r%0A%20%20%20%20%20%20%20%20return%20np.nan%0A%0A%20%20%20%20_screen_df%5B%22_best_env_p_adj%22%5D%20%3D%20_screen_df.apply(_bh_best_p%2C%20axis%3D1)%0A%0A%20%20%20%20%23%20---%20Fix%202%20%2B%20Fix%203%3A%20Tier%20assignment%20using%20BH-adjusted%20p-values%20AND%20incremental%20F-test%20---%0A%20%20%20%20def%20_assign_tier(row)%3A%0A%20%20%20%20%20%20%20%20_max_r%20%20%20%3D%20row%5B%22_max_abs_env_r%22%5D%0A%20%20%20%20%20%20%20%20_p_adj%20%20%20%3D%20row%5B%22_best_env_p_adj%22%5D%0A%20%20%20%20%20%20%20%20_dr2%20%20%20%20%20%3D%20row%5B%22_delta_env%22%5D%0A%20%20%20%20%20%20%20%20_p_f%20%20%20%20%20%3D%20row%5B%22_p_f_incr%22%5D%0A%20%20%20%20%20%20%20%20_dom_r2%20%20%3D%20row%5B%22_env_dominates_r2%22%5D%0A%0A%20%20%20%20%20%20%20%20%23%20Strong%3A%20high%20partial%20r%20AND%20corrected%20p%20significant%20AND%20%CE%94R%C2%B2%20%E2%89%A5%20threshold%0A%20%20%20%20%20%20%20%20%23%20AND%20incremental%20F-test%20confirms%20the%20R%C2%B2%20gain%20is%20not%20just%20noise%20(Fix%203)%0A%20%20%20%20%20%20%20%20if%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20pd.notna(_max_r)%20and%20_max_r%20%3E%3D%20TIER_STRONG_R%0A%20%20%20%20%20%20%20%20%20%20%20%20and%20pd.notna(_p_adj)%20and%20_p_adj%20%3C%200.05%0A%20%20%20%20%20%20%20%20%20%20%20%20and%20pd.notna(_dr2)%20and%20_dr2%20%3E%3D%20TIER_STRONG_DR2%0A%20%20%20%20%20%20%20%20%20%20%20%20and%20pd.notna(_p_f)%20and%20_p_f%20%3C%200.05%0A%20%20%20%20%20%20%20%20)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20%22Strong%22%0A%20%20%20%20%20%20%20%20elif%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20(pd.notna(_dr2)%20and%20_dr2%20%3E%3D%20TIER_STRONG_DR2)%0A%20%20%20%20%20%20%20%20%20%20%20%20or%20_dom_r2%0A%20%20%20%20%20%20%20%20%20%20%20%20or%20(pd.notna(_max_r)%20and%20_max_r%20%3E%3D%20TIER_MOD_R%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20and%20pd.notna(_p_adj)%20and%20_p_adj%20%3C%20TIER_MOD_P)%0A%20%20%20%20%20%20%20%20)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20%22Moderate%22%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20%22Weak%20%2F%20inconclusive%22%0A%0A%20%20%20%20_screen_df%5B%22Candidate%20tier%22%5D%20%3D%20_screen_df.apply(_assign_tier%2C%20axis%3D1)%0A%0A%20%20%20%20%23%20Drop%20internal%20helper%20columns%20before%20exposing%0A%20%20%20%20_internal_cols%20%3D%20%5B%0A%20%20%20%20%20%20%20%20%22_p_temp_raw%22%2C%20%22_p_rain_raw%22%2C%20%22_delta_env%22%2C%20%22_env_dominates_r2%22%2C%0A%20%20%20%20%20%20%20%20%22_max_abs_env_r%22%2C%20%22_p_f_incr%22%2C%20%22_best_env_p_raw%22%2C%20%22_best_env_label%22%2C%0A%20%20%20%20%20%20%20%20%22_best_env_p_adj%22%2C%0A%20%20%20%20%5D%0A%20%20%20%20_screen_df%20%3D%20_screen_df.drop(columns%3D%5Bc%20for%20c%20in%20_internal_cols%20if%20c%20in%20_screen_df.columns%5D)%0A%0A%20%20%20%20country_screen%20%3D%20_screen_df.sort_values(%0A%20%20%20%20%20%20%20%20%5B%22Candidate%20tier%22%2C%20%22Environment%20added%20R2%22%2C%20%22Max%20abs%20env%20r%22%5D%2C%0A%20%20%20%20%20%20%20%20ascending%3D%5BTrue%2C%20False%2C%20False%5D%2C%0A%20%20%20%20)%0A%20%20%20%20residual_panel%20%3D%20pd.concat(residual_frames%2C%20ignore_index%3DTrue)%0A%20%20%20%20candidate_screen%20%3D%20country_screen%5B%0A%20%20%20%20%20%20%20%20country_screen%5B%22Candidate%20tier%22%5D.isin(%5B%22Strong%22%2C%20%22Moderate%22%5D)%0A%20%20%20%20%5D.sort_values(%5B%22Candidate%20tier%22%2C%20%22Environment%20added%20R2%22%5D%2C%20ascending%3D%5BTrue%2C%20False%5D)%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20MIN_OBS%2C%0A%20%20%20%20%20%20%20%20TIER_MOD_P%2C%0A%20%20%20%20%20%20%20%20TIER_MOD_R%2C%0A%20%20%20%20%20%20%20%20TIER_STRONG_DR2%2C%0A%20%20%20%20%20%20%20%20TIER_STRONG_R%2C%0A%20%20%20%20%20%20%20%20candidate_screen%2C%0A%20%20%20%20%20%20%20%20country_screen%2C%0A%20%20%20%20%20%20%20%20residual_panel%2C%0A%20%20%20%20)%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%23%23%20Reading%20the%20new%20screening%20columns%0A%0A%20%20%20%20**Benjamini-Hochberg%20(BH)%20FDR%20correction%20%E2%80%94%20Note%2017**%0A%20%20%20%20Running%20~74%20countries%20%C3%97%202%20environmental%20variables%20%E2%89%88%20148%20simultaneous%20Pearson%20tests%20at%20%CE%B1%20%3D%200.05%20means%20about%207%E2%80%938%20false%20positives%20are%20expected%20by%20chance%20even%20when%20no%20real%20signal%20exists.%20The%20Bonferroni%20correction%20fixes%20this%20by%20making%20each%20test%20harder%20to%20pass%20(%CE%B1%2F148%20%E2%89%88%200.00034)%20but%20is%20too%20conservative%20when%20tests%20are%20correlated.%20The%20Benjamini-Hochberg%20procedure%20controls%20the%20*False%20Discovery%20Rate*%20(FDR)%20%E2%80%94%20the%20expected%20proportion%20of%20%22significant%22%20results%20that%20are%20actually%20false%20%E2%80%94%20at%205%25.%20The%20%60p%20Temp%20(BH-adj)%60%20and%20%60p%20Rain%20(BH-adj)%60%20columns%20in%20the%20table%20are%20BH-corrected.%20Tier%20classification%20uses%20these%20adjusted%20values%2C%20not%20raw%20p-values.%0A%0A%20%20%20%20**Incremental%20F-test%20%E2%80%94%20Note%2018**%0A%20%20%20%20The%20Strong%20tier%20requires%20that%20adding%20temperature%20and%20rainfall%20to%20a%20population-only%20model%20produces%20a%20statistically%20significant%20improvement%20in%20fit%2C%20tested%20with%20a%20nested%20F-test%3A%0A%0A%20%20%20%20-%20Restricted%20model%3A%20ln(Production)%20~%20ln(Population)%0A%20%20%20%20-%20Full%20model%3A%20ln(Production)%20~%20ln(Population)%20%2B%20Temp%20%2B%20Rain%0A%0A%20%20%20%20The%20F-statistic%20measures%20the%20reduction%20in%20residual%20sum%20of%20squares%20(RSS)%20relative%20to%20the%20degrees%20of%20freedom%20consumed%20by%20the%20two%20extra%20predictors.%20The%20%60p%20F-incr%60%20column%20shows%20this%20p-value.%20A%20country%20can%20have%20a%20moderate%20partial%20r%20but%20fail%20the%20F-test%20if%20the%20improvement%20is%20not%20consistent%20across%20the%20full%20time%20series%20%E2%80%94%20that%20country%20would%20be%20classified%20as%20Moderate%20or%20Weak%2C%20not%20Strong.%0A%0A%20%20%20%20**Durbin-Watson%20statistic%20%E2%80%94%20Note%2019**%0A%20%20%20%20Coffee%20production%20within%20a%20country%20is%20often%20serially%20correlated%20over%20time%20(drought%2C%20disease%2C%20expansion%20all%20persist%20across%20years).%20The%20%60DW%20stat%60%20column%20measures%20this%20autocorrelation%3A%20values%20near%202.0%20indicate%20no%20autocorrelation%3B%20values%20below%201.5%20suggest%20positive%20AR(1)%2C%20meaning%20consecutive%20residuals%20move%20together.%20**BH%20correction%20cannot%20fix%20this%20problem**%20%E2%80%94%20BH%20adjusts%20for%20the%20number%20of%20tests%2C%20not%20for%20inflated%20test%20statistics%20within%20each%20test.%20A%20country%20showing%20DW%20%3C%201.5%20together%20with%20a%20small%20BH-adjusted%20p-value%20should%20be%20treated%20as%20a%20weaker%20candidate%20than%20the%20p-value%20alone%20suggests.%0A%0A%20%20%20%20**Tier%20thresholds%20%E2%80%94%20what%20the%20numbers%20mean**%0A%20%20%20%20The%20thresholds%20%60TIER_STRONG_R%20%3D%200.45%60%20and%20%60TIER_STRONG_DR2%20%3D%200.10%60%20are%20heuristic%20cutoffs%20chosen%20for%20case-study%20selection%2C%20not%20derived%20from%20a%20statistical%20distribution.%20r%20%E2%89%A5%200.45%20implies%20r%C2%B2%20%E2%89%88%200.20%20%E2%80%94%20the%20environmental%20variable%20accounts%20for%20at%20least%2020%25%20of%20the%20population-adjusted%20production%20variance.%20%CE%94R%C2%B2%20%E2%89%A5%200.10%20means%20environment%20must%20add%20at%20least%2010%20percentage%20points%20of%20explained%20variance%20beyond%20population%20alone.%20These%20numbers%20were%20chosen%20to%20be%20%22large%20enough%20to%20matter%20for%20a%20case%20study%2C%22%20not%20because%20they%20represent%20a%20critical%20value%20at%20any%20%CE%B1%20level.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20MIN_OBS%2C%0A%20%20%20%20TIER_MOD_P%2C%0A%20%20%20%20TIER_MOD_R%2C%0A%20%20%20%20TIER_STRONG_DR2%2C%0A%20%20%20%20TIER_STRONG_R%2C%0A%20%20%20%20country_screen%2C%0A%20%20%20%20mo%2C%0A%20%20%20%20pd%2C%0A)%3A%0A%20%20%20%20def%20_format_screen(df)%3A%0A%20%20%20%20%20%20%20%20_d%20%3D%20df.copy()%0A%20%20%20%20%20%20%20%20_float_cols%20%3D%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Population%20R2%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Environment-only%20R2%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Full%20R2%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Environment%20added%20R2%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Population%20added%20R2%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22r%20Temp%20after%20Pop%20(partial)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22p%20Temp%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22p%20Temp%20(BH-adj)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22r%20Rain%20after%20Pop%20(partial)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22p%20Rain%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22p%20Rain%20(BH-adj)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22p%20F-incr%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22DW%20stat%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Max%20abs%20env%20r%22%2C%0A%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20for%20_c%20in%20_float_cols%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20_c%20in%20_d.columns%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20_d%5B_c%5D%20%3D%20_d%5B_c%5D.map(lambda%20x%3A%20%22%22%20if%20pd.isna(x)%20else%20f%22%7Bx%3A.3f%7D%22)%0A%20%20%20%20%20%20%20%20return%20_d%0A%0A%20%20%20%20_top_cols%20%3D%20%5B%0A%20%20%20%20%20%20%20%20%22Candidate%20tier%22%2C%0A%20%20%20%20%20%20%20%20%22ISO3%22%2C%0A%20%20%20%20%20%20%20%20%22Country%22%2C%0A%20%20%20%20%20%20%20%20%22n%22%2C%0A%20%20%20%20%20%20%20%20%22Rain%20unique%22%2C%0A%20%20%20%20%20%20%20%20%22Population%20R2%22%2C%0A%20%20%20%20%20%20%20%20%22Environment-only%20R2%22%2C%0A%20%20%20%20%20%20%20%20%22Full%20R2%22%2C%0A%20%20%20%20%20%20%20%20%22Environment%20added%20R2%22%2C%0A%20%20%20%20%20%20%20%20%22Population%20added%20R2%22%2C%0A%20%20%20%20%20%20%20%20%22r%20Temp%20after%20Pop%20(partial)%22%2C%0A%20%20%20%20%20%20%20%20%22p%20Temp%22%2C%0A%20%20%20%20%20%20%20%20%22p%20Temp%20(BH-adj)%22%2C%0A%20%20%20%20%20%20%20%20%22r%20Rain%20after%20Pop%20(partial)%22%2C%0A%20%20%20%20%20%20%20%20%22p%20Rain%22%2C%0A%20%20%20%20%20%20%20%20%22p%20Rain%20(BH-adj)%22%2C%0A%20%20%20%20%20%20%20%20%22p%20F-incr%22%2C%0A%20%20%20%20%20%20%20%20%22DW%20stat%22%2C%0A%20%20%20%20%20%20%20%20%22Best%20environmental%20variable%22%2C%0A%20%20%20%20%5D%0A%0A%20%20%20%20_ranked%20%3D%20_format_screen(%0A%20%20%20%20%20%20%20%20country_screen.sort_values(%22Environment%20added%20R2%22%2C%20ascending%3DFalse)%5B_top_cols%5D.head(20)%0A%20%20%20%20)%0A%0A%20%20%20%20_counts%20%3D%20country_screen%5B%22Candidate%20tier%22%5D.value_counts().reindex(%0A%20%20%20%20%20%20%20%20%5B%22Strong%22%2C%20%22Moderate%22%2C%20%22Weak%20%2F%20inconclusive%22%5D%2C%0A%20%20%20%20%20%20%20%20fill_value%3D0%2C%0A%20%20%20%20)%0A%20%20%20%20_n_strong%20%3D%20int(_counts%5B%22Strong%22%5D)%0A%20%20%20%20_n_mod%20%20%20%20%3D%20int(_counts%5B%22Moderate%22%5D)%0A%20%20%20%20_n_weak%20%20%20%3D%20int(_counts%5B%22Weak%20%2F%20inconclusive%22%5D)%0A%20%20%20%20_n_total%20%20%3D%20_n_strong%20%2B%20_n_mod%20%2B%20_n_weak%0A%0A%20%20%20%20%23%20Flag%20countries%20with%20potential%20autocorrelation%20(DW%20%3C%201.5)%0A%20%20%20%20_dw_flagged%20%3D%20country_screen%5Bcountry_screen%5B%22DW%20stat%22%5D.notna()%20%26%20(country_screen%5B%22DW%20stat%22%5D%20%3C%201.5)%5D%5B%22Country%22%5D.tolist()%0A%20%20%20%20_dw_flag_str%20%3D%20%22%2C%20%22.join(_dw_flagged%5B%3A10%5D)%20%2B%20(%22%E2%80%A6%22%20if%20len(_dw_flagged)%20%3E%2010%20else%20%22%22)%0A%0A%20%20%20%20mo.md(f%22%22%22%0A%20%20%20%20%23%23%23%201.1%20Environmental%20Sensitivity%20Ranking%0A%0A%20%20%20%20**Strong%3A%20%7B_n_strong%7D%20%C2%B7%20Moderate%3A%20%7B_n_mod%7D%20%C2%B7%20Weak%20%2F%20inconclusive%3A%20%7B_n_weak%7D**%20countries%20(of%20%7B_n_total%7D%20with%20%E2%89%A5%20%7BMIN_OBS%7D%20obs)%0A%0A%20%20%20%20%3E%20**These%20tiers%20are%20a%20heuristic%20screening%20rule%2C%20not%20a%20formal%20hypothesis%20test.**%20Threshold%20guide%3A%0A%20%20%20%20%3E%20-%20**Strong**%20%3D%20partial%20%7Cr%7C%20%E2%89%A5%20%7BTIER_STRONG_R%7D%2C%20corrected%20p%20(BH-adj)%20%3C%200.05%2C%20%CE%94R%C2%B2%20%E2%89%A5%20%7BTIER_STRONG_DR2%7D%2C%20AND%20incremental%20F-test%20p%20%3C%200.05%0A%20%20%20%20%3E%20-%20**Moderate**%20%3D%20%CE%94R%C2%B2%20%E2%89%A5%20%7BTIER_STRONG_DR2%7D%2C%20or%20env%20R%C2%B2%20%3E%20pop%20R%C2%B2%2C%20or%20partial%20%7Cr%7C%20%E2%89%A5%20%7BTIER_MOD_R%7D%20with%20BH-adjusted%20p%20%3C%20%7BTIER_MOD_P%7D%0A%20%20%20%20%3E%20-%20%60r%20Temp%20after%20Pop%20(partial)%60%20and%20%60r%20Rain%20after%20Pop%20(partial)%60%20are%20**true%20FWL%20partial%20correlations**%3A%20both%20the%20response%20and%20each%20environmental%20predictor%20are%20residualized%20on%20ln(Population)%20before%20computing%20Pearson%20r.%0A%20%20%20%20%3E%20-%20%60p%20Temp%20(BH-adj)%60%20and%20%60p%20Rain%20(BH-adj)%60%20are%20**corrected%20p-values%20(Benjamini-Hochberg%20FDR)**%20across%20all%20~%7B_n_total%20*%202%7D%20simultaneous%20tests.%20The%20Strong%20and%20Moderate%20tier%20significance%20tests%20use%20these%20corrected%20values.%20Raw%20p-values%20are%20also%20shown%20for%20comparison.%0A%20%20%20%20%3E%20-%20%60p%20F-incr%60%20is%20the%20p-value%20from%20an%20incremental%20F-test%20(partial%20F-test)%20asking%20whether%20the%20environmental%20variables%20significantly%20improve%20fit%20beyond%20the%20population-only%20model.%20The%20Strong%20tier%20requires%20this%20p%20%3C%200.05.%0A%20%20%20%20%3E%20-%20**Serial%20autocorrelation%20warning%20(Fix%204)%3A**%20%60DW%20stat%60%20is%20the%20Durbin-Watson%20statistic%20on%20the%20population-model%20residuals.%20DW%20near%202.0%20%3D%20low%20autocorrelation.%20**Countries%20with%20DW%20%3C%201.5%20have%20positive%20AR(1)%20in%20their%20production%20residuals%3B%20their%20raw%20p-values%20are%20overstated%20and%20should%20be%20interpreted%20conservatively%20even%20after%20BH%20correction.**%20Countries%20flagged%3A%20%7B_dw_flag_str%20if%20_dw_flag_str%20else%20%22none%20at%20DW%20%3C%201.5%22%7D.%0A%0A%20%20%20%20The%20table%20below%20ranks%20countries%20by%20how%20much%20environmental%20variables%20add%20after%20population%20has%20already%20been%20used.%0A%0A%20%20%20%20%7Bmo.as_html(_ranked)%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(%22%22%22%0A%20%20%20%20%23%23%23%23%20How%20to%20read%20the%20ranking%20table%20columns%0A%0A%20%20%20%20%7C%20Column%20%7C%20Meaning%20%7C%0A%20%20%20%20%7C---%7C---%7C%0A%20%20%20%20%7C%20%60Population%20R%C2%B2%60%20%7C%20Variance%20in%20ln(Production)%20explained%20by%20ln(Population)%20alone%20%E2%80%94%20the%20%22scale%20effect%22%20%7C%0A%20%20%20%20%7C%20%60Env%20added%20R%C2%B2%60%20%7C%20Additional%20variance%20explained%20when%20temperature%20and%20rainfall%20are%20added%20%7C%0A%20%20%20%20%7C%20%60p%20F-incr%60%20%7C%20Incremental%20F-test%20p-value%20%E2%80%94%20is%20the%20improvement%20in%20fit%20statistically%20significant%3F%20%7C%0A%20%20%20%20%7C%20%60r%20Temp%20after%20Pop%20(partial)%60%20%7C%20True%20FWL%20partial%20correlation%3A%20temperature%20residuals%20vs.%20production%20residuals%20after%20removing%20population%20from%20both%20%7C%0A%20%20%20%20%7C%20%60r%20Rain%20after%20Pop%20(partial)%60%20%7C%20Same%20for%20rainfall%20%7C%0A%20%20%20%20%7C%20%60p%20Temp%20(BH-adj)%60%20%2F%20%60p%20Rain%20(BH-adj)%60%20%7C%20BH-corrected%20p-values%20%E2%80%94%20use%20these%2C%20not%20raw%20p-values%2C%20to%20judge%20significance%20%7C%0A%20%20%20%20%7C%20%60DW%20stat%60%20%7C%20Durbin-Watson%20autocorrelation%20statistic%20%E2%80%94%20values%20%3C%201.5%20warn%20of%20inflated%20p-values%20%7C%0A%20%20%20%20%7C%20%60Tier%60%20%7C%20Heuristic%20classification%3A%20Strong%20%2F%20Moderate%20%2F%20Weak%20%2F%20Population-dominated%20%7C%0A%20%20%20%20%7C%20%60Best%20env%20variable%60%20%7C%20Which%20of%20temperature%20or%20rainfall%20showed%20a%20stronger%20partial%20signal%20%7C%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---%0A%20%20%20%20%23%23%202.%20Population-Dominated%20Countries%0A%0A%20%20%20%20These%20are%20countries%20where%20the%20population-only%20model%20already%20explains%20a%20large%20share%20of%20production%20variation%20and%20environmental%20variables%20add%20comparatively%20little.%20A%20high%20Population%20R%C2%B2%20is%20not%20a%20scientific%20failure%20%E2%80%94%20it%20tells%20you%20which%20question%20this%20country%20best%20answers%20in%20this%20dataset%3A%20the%20production-at-scale%20question%2C%20not%20the%20environmental-sensitivity%20question.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(country_screen%2C%20mo%2C%20pd)%3A%0A%20%20%20%20_pop_dominated%20%3D%20country_screen%5B%0A%20%20%20%20%20%20%20%20(country_screen%5B%22Population%20R2%22%5D%20%3E%3D%200.50)%0A%20%20%20%20%20%20%20%20%26%20(country_screen%5B%22Environment%20added%20R2%22%5D.fillna(0)%20%3C%200.10)%0A%20%20%20%20%5D.sort_values(%22Population%20R2%22%2C%20ascending%3DFalse)%0A%0A%20%20%20%20_display%20%3D%20_pop_dominated%5B%5B%0A%20%20%20%20%20%20%20%20%22ISO3%22%2C%0A%20%20%20%20%20%20%20%20%22Country%22%2C%0A%20%20%20%20%20%20%20%20%22n%22%2C%0A%20%20%20%20%20%20%20%20%22Population%20R2%22%2C%0A%20%20%20%20%20%20%20%20%22Environment%20added%20R2%22%2C%0A%20%20%20%20%20%20%20%20%22Population%20added%20R2%22%2C%0A%20%20%20%20%20%20%20%20%22Max%20abs%20env%20r%22%2C%0A%20%20%20%20%20%20%20%20%22Candidate%20tier%22%2C%0A%20%20%20%20%5D%5D.head(20).copy()%0A%0A%20%20%20%20for%20_c%20in%20%5B%22Population%20R2%22%2C%20%22Environment%20added%20R2%22%2C%20%22Population%20added%20R2%22%2C%20%22Max%20abs%20env%20r%22%5D%3A%0A%20%20%20%20%20%20%20%20_display%5B_c%5D%20%3D%20_display%5B_c%5D.map(lambda%20x%3A%20%22%22%20if%20pd.isna(x)%20else%20f%22%7Bx%3A.3f%7D%22)%0A%0A%20%20%20%20mo.md(f%22%22%22%0A%20%20%20%20%23%23%23%202.1%20Countries%20Mostly%20Explained%20by%20Population%0A%0A%20%20%20%20%7Bmo.as_html(_display)%7D%0A%0A%20%20%20%20%3E%20These%20countries%20are%20not%20%22bad%22%20cases.%20They%20just%20answer%20a%20different%20question%3A%20production%20tracks%20scale%2Fpopulation%20more%20clearly%20than%20year-to-year%20environmental%20variation%20in%20this%20dataset.%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---%0A%20%20%20%20%23%23%203.%20Interactive%20Country%20Explorer%0A%0A%20%20%20%20Pick%20a%20candidate%20country%20to%20inspect%20the%20population-adjusted%20residuals%20directly.%0A%0A%20%20%20%20%3E%20**Local%20only%3A**%20The%20dropdown%20and%20interactive%20plot%20in%20this%20section%20require%20a%20live%20Marimo%0A%20%20%20%20%3E%20kernel.%20They%20will%20not%20function%20when%20viewing%20this%20notebook%20on%20GitHub%20Pages%20%E2%80%94%20you%20will%20see%0A%20%20%20%20%3E%20a%20static%20snapshot%20of%20the%20last%20selected%20country%20only.%20To%20use%20the%20explorer%2C%20run%20the%20notebook%0A%20%20%20%20%3E%20locally%3A%20%60python3%20-m%20marimo%20edit%20marimo%2Fenvironmental_sensitivity_followup.py%60%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(candidate_screen%2C%20country_screen%2C%20mo)%3A%0A%20%20%20%20_source%20%3D%20candidate_screen%20if%20len(candidate_screen)%20%3E%200%20else%20country_screen%0A%20%20%20%20_choices%20%3D%20%5B%0A%20%20%20%20%20%20%20%20f%22%7Brow.Country%7D%20(%7Brow.ISO3%7D)%22%0A%20%20%20%20%20%20%20%20for%20row%20in%20_source.sort_values(%22Environment%20added%20R2%22%2C%20ascending%3DFalse).itertuples()%0A%20%20%20%20%5D%0A%20%20%20%20country_select%20%3D%20mo.ui.dropdown(%0A%20%20%20%20%20%20%20%20options%3D_choices%2C%0A%20%20%20%20%20%20%20%20value%3D_choices%5B0%5D%2C%0A%20%20%20%20%20%20%20%20label%3D%22Country%22%2C%0A%20%20%20%20)%0A%20%20%20%20mo.md(f%22%22%22%0A%20%20%20%20%23%23%23%20Choose%20a%20Country%0A%0A%20%20%20%20%7Bcountry_select%7D%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%20(country_select%2C)%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%23%23%20How%20to%20read%20the%20four-panel%20country%20explorer%0A%0A%20%20%20%20Each%20panel%20answers%20a%20different%20question%20about%20the%20selected%20country%3A%0A%0A%20%20%20%20-%20**Top-left%20%E2%80%94%20Production%20vs.%20Population%20over%20time%3A**%20Are%20log%20production%20and%20log%20population%20co-trending%3F%20If%20they%20move%20in%20parallel%2C%20population%20explains%20most%20of%20the%20variation.%0A%20%20%20%20-%20**Top-right%20%E2%80%94%20Population-adjusted%20residual%20over%20time%3A**%20This%20is%20what%20remains%20after%20removing%20the%20population%20trend.%20A%20residual%20above%20zero%20means%20the%20country%20produced%20*more*%20than%20population%20alone%20predicts%20that%20year%3B%20below%20zero%20means%20less.%0A%20%20%20%20-%20**Bottom-left%20%E2%80%94%20Residual%20vs.%20Temperature%3A**%20Does%20the%20partial%20residual%20correlate%20with%20temperature%3F%20An%20upward%20slope%20suggests%20warmer%20years%20are%20associated%20with%20higher-than-expected%20production%20for%20this%20country.%0A%20%20%20%20-%20**Bottom-right%20%E2%80%94%20Residual%20vs.%20Rainfall%3A**%20Same%20question%20for%20rainfall.%20If%20rainfall%20is%20fixed%20(climatological%20mean)%2C%20this%20panel%20will%20show%20a%20vertical%20cluster%20%E2%80%94%20no%20year-to-year%20signal.%0A%0A%20%20%20%20%3E%20**Correlation%20%E2%89%A0%20causation.**%20A%20pattern%20in%20any%20panel%20is%20a%20candidate%20signal%20for%20investigation%2C%20not%20a%20proven%20mechanism.%20Temperature%20could%20correlate%20with%20unmeasured%20variables%20(investment%20cycles%2C%20varietal%20shifts%2C%20trade%20policy)%20that%20actually%20drive%20production.%20For%20countries%20where%20rainfall%20is%20a%20fixed%20climatological%20mean%2C%20the%20bottom-right%20panel%20carries%20no%20dynamic%20signal%20%E2%80%94%20see%20rainfall%20limitation%20note%20in%20the%20dataset%20snapshot.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20ACCENT%2C%0A%20%20%20%20ACCENT2%2C%0A%20%20%20%20ACCENT3%2C%0A%20%20%20%20GREY%2C%0A%20%20%20%20country_screen%2C%0A%20%20%20%20country_select%2C%0A%20%20%20%20mo%2C%0A%20%20%20%20np%2C%0A%20%20%20%20plt%2C%0A%20%20%20%20residual_panel%2C%0A)%3A%0A%20%20%20%20_selected_iso3%20%3D%20country_select.value.rsplit(%22(%22%2C%201)%5B-1%5D.rstrip(%22)%22)%0A%20%20%20%20_d%20%3D%20residual_panel%5Bresidual_panel%5B%22ISO3%22%5D%20%3D%3D%20_selected_iso3%5D.sort_values(%22Year%22).copy()%0A%20%20%20%20_meta%20%3D%20country_screen%5Bcountry_screen%5B%22ISO3%22%5D%20%3D%3D%20_selected_iso3%5D.iloc%5B0%5D%0A%0A%20%20%20%20fig_country%2C%20axes_country%20%3D%20plt.subplots(2%2C%202%2C%20figsize%3D(13%2C%208))%0A%20%20%20%20fig_country.suptitle(%0A%20%20%20%20%20%20%20%20f%22%7B_meta%5B'Country'%5D%7D%20%E2%80%93%20Population-Adjusted%20Environmental%20Screen%22%2C%0A%20%20%20%20%20%20%20%20fontsize%3D13%2C%0A%20%20%20%20%20%20%20%20fontweight%3D%22bold%22%2C%0A%20%20%20%20)%0A%0A%20%20%20%20ax%20%3D%20axes_country%5B0%2C%200%5D%0A%20%20%20%20ax.plot(_d%5B%22Year%22%5D%2C%20_d%5B%22ln_Production%22%5D%2C%20color%3DACCENT%2C%20marker%3D%22o%22%2C%20linewidth%3D1.7%2C%20label%3D%22ln(Production)%22)%0A%20%20%20%20ax.plot(_d%5B%22Year%22%5D%2C%20_d%5B%22ln_Population%22%5D%2C%20color%3DGREY%2C%20marker%3D%22s%22%2C%20linewidth%3D1.4%2C%20label%3D%22ln(Population)%22)%0A%20%20%20%20ax.set_title(%22Log%20production%20and%20log%20population%22)%0A%20%20%20%20ax.set_xlabel(%22Year%22)%0A%20%20%20%20ax.legend(fontsize%3D8)%0A%0A%20%20%20%20ax%20%3D%20axes_country%5B0%2C%201%5D%0A%20%20%20%20ax.axhline(0%2C%20color%3D%22%23333333%22%2C%20linestyle%3D%22--%22%2C%20linewidth%3D1)%0A%20%20%20%20ax.plot(_d%5B%22Year%22%5D%2C%20_d%5B%22prod_resid_after_pop%22%5D%2C%20color%3DACCENT2%2C%20marker%3D%22o%22%2C%20linewidth%3D1.7)%0A%20%20%20%20ax.set_title(%22Production%20residual%20after%20population%22)%0A%20%20%20%20ax.set_xlabel(%22Year%22)%0A%20%20%20%20ax.set_ylabel(%22Residual%22)%0A%0A%20%20%20%20ax%20%3D%20axes_country%5B1%2C%200%5D%0A%20%20%20%20ax.scatter(_d%5B%22Avg_Temp_C%22%5D%2C%20_d%5B%22prod_resid_after_pop%22%5D%2C%20color%3DACCENT3%2C%20alpha%3D0.85)%0A%20%20%20%20if%20_d%5B%22Avg_Temp_C%22%5D.nunique()%20%3E%3D%202%3A%0A%20%20%20%20%20%20%20%20_coef%20%3D%20np.polyfit(_d%5B%22Avg_Temp_C%22%5D%2C%20_d%5B%22prod_resid_after_pop%22%5D%2C%201)%0A%20%20%20%20%20%20%20%20_x%20%3D%20np.linspace(_d%5B%22Avg_Temp_C%22%5D.min()%2C%20_d%5B%22Avg_Temp_C%22%5D.max()%2C%20100)%0A%20%20%20%20%20%20%20%20ax.plot(_x%2C%20np.polyval(_coef%2C%20_x)%2C%20color%3D%22%23333333%22%2C%20linestyle%3D%22--%22%2C%20linewidth%3D1.2)%0A%20%20%20%20ax.axhline(0%2C%20color%3D%22%23777777%22%2C%20linestyle%3D%22%3A%22%2C%20linewidth%3D1)%0A%20%20%20%20ax.set_title(f%22Residual%20vs%20temperature%20(partial%20r%20%3D%20%7B_meta%5B'r%20Temp%20after%20Pop%20(partial)'%5D%3A.3f%7D)%22)%0A%20%20%20%20ax.set_xlabel(%22Average%20temperature%20(C)%22)%0A%20%20%20%20ax.set_ylabel(%22Residual%22)%0A%0A%20%20%20%20ax%20%3D%20axes_country%5B1%2C%201%5D%0A%20%20%20%20if%20_d%5B%22Rain_mm%22%5D.nunique()%20%3E%3D%205%3A%0A%20%20%20%20%20%20%20%20ax.scatter(_d%5B%22Rain_mm%22%5D%2C%20_d%5B%22prod_resid_after_pop%22%5D%2C%20color%3DACCENT2%2C%20alpha%3D0.85)%0A%20%20%20%20%20%20%20%20_coef%20%3D%20np.polyfit(_d%5B%22Rain_mm%22%5D%2C%20_d%5B%22prod_resid_after_pop%22%5D%2C%201)%0A%20%20%20%20%20%20%20%20_x%20%3D%20np.linspace(_d%5B%22Rain_mm%22%5D.min()%2C%20_d%5B%22Rain_mm%22%5D.max()%2C%20100)%0A%20%20%20%20%20%20%20%20ax.plot(_x%2C%20np.polyval(_coef%2C%20_x)%2C%20color%3D%22%23333333%22%2C%20linestyle%3D%22--%22%2C%20linewidth%3D1.2)%0A%20%20%20%20%20%20%20%20ax.set_title(f%22Residual%20vs%20rainfall%20(partial%20r%20%3D%20%7B_meta%5B'r%20Rain%20after%20Pop%20(partial)'%5D%3A.3f%7D)%22)%0A%20%20%20%20%20%20%20%20ax.set_xlabel(%22Rainfall%20(mm%2Fyr)%22)%0A%20%20%20%20%20%20%20%20ax.set_ylabel(%22Residual%22)%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20ax.axis(%22off%22)%0A%20%20%20%20%20%20%20%20ax.text(%0A%20%20%20%20%20%20%20%20%20%20%20%200.5%2C%0A%20%20%20%20%20%20%20%20%20%20%20%200.5%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Rainfall%20is%20fixed%20or%20nearly%20fixed%5Cnfor%20this%20country%2C%5Cnso%20within-country%20rainfall%20screening%5Cnis%20not%20meaningful.%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20ha%3D%22center%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20va%3D%22center%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20fontsize%3D11%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20color%3D%22%23444444%22%2C%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20plt.tight_layout()%0A%20%20%20%20mo.mpl.interactive(fig_country)%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---%0A%20%20%20%20%23%23%204.%20Candidate%20Cohort%20Regression%0A%0A%20%20%20%20Now%20compare%20the%20full%20panel%20against%20the%20countries%20flagged%20as%20environmental%20sensitivity%20candidates.%0A%0A%20%20%20%20%3E%20**Selection-bias%20note%20(Fix%205)%3A**%20This%20cohort%20was%20identified%20in%20%C2%A71%20based%20on%20strong%20environmental%20signal.%20The%20regression%20below%20will%20tend%20to%20show%20larger%20coefficients%20and%20smaller%20p-values%20for%20these%20countries%20than%20the%20full%20panel%20%E2%80%94%20this%20is%20partly%20by%20construction%2C%20not%20independent%20confirmation.%20The%20cohort%20was%20selected%20from%20the%20same%20data%20being%20re-analyzed%2C%20so%20finding%20that%20environmental%20variables%20are%20more%20significant%20in%20the%20cohort%20is%20a%20tautological%20result.%20The%20full-panel%20comparison%20is%20included%20specifically%20to%20show%20the%20magnitude%20of%20this%20selection%20effect%3B%20it%20does%20not%20validate%20the%20%C2%A71%20screening%20result.%0A%0A%20%20%20%20Rainfall%20is%20decomposed%20into%20a%20between-country%20structural%20mean%20and%20a%20within-country%20annual%20deviation%20%E2%80%94%20matching%20the%20main%20workbook%20%C2%A73%20specification%20(see%20main%20workbook%20Note%2016).%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(candidate_screen%2C%20mo%2C%20panel%2C%20pd%2C%20smf)%3A%0A%20%20%20%20candidate_iso3%20%3D%20candidate_screen%5B%22ISO3%22%5D.tolist()%0A%20%20%20%20%23%20Use%20rainfall%20decomposition%20(between-country%20mean%20%2B%20within-country%20deviation)%20consistent%0A%20%20%20%20%23%20with%20the%20main%20workbook%20%E2%80%94%20raw%20Rain_mm%20conflates%20cross-country%20and%20year-to-year%20signals.%0A%20%20%20%20_formula%20%3D%20(%0A%20%20%20%20%20%20%20%20%22ln_Production%20~%20Avg_Temp_C%20%2B%20Rain_mm_country_mean%20%2B%20Rain_mm_within%22%0A%20%20%20%20%20%20%20%20%22%20%2B%20ln_Population%20%2B%20Oil_Price_Brent_USD%22%0A%20%20%20%20)%0A%0A%20%20%20%20_full_df%20%3D%20panel.dropna(subset%3D%5B%0A%20%20%20%20%20%20%20%20%22ln_Production%22%2C%0A%20%20%20%20%20%20%20%20%22Avg_Temp_C%22%2C%0A%20%20%20%20%20%20%20%20%22Rain_mm_country_mean%22%2C%0A%20%20%20%20%20%20%20%20%22Rain_mm_within%22%2C%0A%20%20%20%20%20%20%20%20%22ln_Population%22%2C%0A%20%20%20%20%20%20%20%20%22Oil_Price_Brent_USD%22%2C%0A%20%20%20%20%5D).copy()%0A%20%20%20%20_cand_df%20%3D%20_full_df%5B_full_df%5B%22ISO3%22%5D.isin(candidate_iso3)%5D.copy()%0A%0A%20%20%20%20_n_full_countries%20%3D%20int(_full_df%5B%22ISO3%22%5D.nunique())%0A%20%20%20%20_n_cand_countries%20%3D%20int(_cand_df%5B%22ISO3%22%5D.nunique())%0A%20%20%20%20_label_full%20%3D%20f%22Full%20panel%20(n%3D%7B_n_full_countries%7D%20countries)%22%0A%20%20%20%20_label_cand%20%3D%20f%22Candidate%20cohort%20(n%3D%7B_n_cand_countries%7D%20countries%2C%20selected%20for%20environmental%20signal)%22%0A%0A%20%20%20%20_rows%20%3D%20%5B%5D%0A%20%20%20%20cohort_models%20%3D%20%5B%5D%0A%0A%20%20%20%20for%20_label%2C%20_df%20in%20%5B(_label_full%2C%20_full_df)%2C%20(_label_cand%2C%20_cand_df)%5D%3A%0A%20%20%20%20%20%20%20%20if%20len(_df)%20%3C%2020%20or%20_df%5B%22ISO3%22%5D.nunique()%20%3C%202%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20continue%0A%20%20%20%20%20%20%20%20_res%20%3D%20smf.ols(_formula%2C%20data%3D_df).fit(cov_type%3D%22HC3%22)%0A%20%20%20%20%20%20%20%20cohort_models.append((_label%2C%20_res))%0A%20%20%20%20%20%20%20%20_rows.append(%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Sample%22%3A%20_label%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22n%22%3A%20int(_res.nobs)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Countries%22%3A%20int(_df%5B%22ISO3%22%5D.nunique())%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22R2%22%3A%20_res.rsquared%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Adjusted%20R2%22%3A%20_res.rsquared_adj%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Temp%20p%22%3A%20_res.pvalues.get(%22Avg_Temp_C%22%2C%20float(%22nan%22))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Rain%20(between)%20p%22%3A%20_res.pvalues.get(%22Rain_mm_country_mean%22%2C%20float(%22nan%22))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Rain%20(within)%20p%22%3A%20_res.pvalues.get(%22Rain_mm_within%22%2C%20float(%22nan%22))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Population%20p%22%3A%20_res.pvalues.get(%22ln_Population%22%2C%20float(%22nan%22))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Oil%20p%22%3A%20_res.pvalues.get(%22Oil_Price_Brent_USD%22%2C%20float(%22nan%22))%2C%0A%20%20%20%20%20%20%20%20%7D)%0A%0A%20%20%20%20cohort_compare%20%3D%20pd.DataFrame(_rows)%0A%20%20%20%20_display%20%3D%20cohort_compare.copy()%0A%20%20%20%20for%20_c%20in%20%5B%22R2%22%2C%20%22Adjusted%20R2%22%2C%20%22Temp%20p%22%2C%20%22Rain%20(between)%20p%22%2C%20%22Rain%20(within)%20p%22%2C%20%22Population%20p%22%2C%20%22Oil%20p%22%5D%3A%0A%20%20%20%20%20%20%20%20if%20_c%20in%20_display.columns%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_display%5B_c%5D%20%3D%20_display%5B_c%5D.map(lambda%20x%3A%20%22%22%20if%20pd.isna(x)%20else%20(%22%3C%200.0001%22%20if%20x%20%3C%200.0001%20else%20f%22%7Bx%3A.4f%7D%22))%0A%0A%20%20%20%20mo.md(f%22%22%22%0A%20%20%20%20%23%23%23%204.1%20Full%20Panel%20vs%20Candidate%20Cohort%0A%0A%20%20%20%20%7Bmo.as_html(_display)%7D%0A%0A%20%20%20%20%3E%20Rainfall%20is%20decomposed%20into%20a%20between-country%20structural%20mean%20(%60Rain_mm_country_mean%60)%20and%20a%20within-country%20annual%20deviation%20(%60Rain_mm_within%60)%2C%20matching%20the%20main%20workbook's%20specification.%0A%20%20%20%20%3E%20**Interpreting%20this%20table%3A**%20The%20candidate%20cohort%20row%20will%20typically%20show%20lower%20p-values%20and%20higher%20R%C2%B2%20for%20environmental%20variables%20by%20construction%20%E2%80%94%20these%20countries%20were%20selected%20in%20%C2%A71%20precisely%20because%20they%20exhibited%20strong%20environmental%20association.%20This%20is%20a%20descriptive%20comparison%20showing%20the%20magnitude%20of%20the%20selection%20effect%2C%20not%20independent%20confirmation%20of%20the%20%C2%A71%20screening%20result.%20See%20the%20selection-bias%20note%20at%20the%20top%20of%20%C2%A74.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%20(cohort_models%2C)%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%23%23%20How%20to%20read%20the%20cohort%20coefficient%20comparison%0A%0A%20%20%20%20The%20plot%20compares%20regression%20coefficients%20between%20the%20full%20panel%20and%20the%20candidate%20cohort%3A%0A%0A%20%20%20%20-%20**Dots%20to%20the%20right%20of%20zero%3A**%20positive%20association%20with%20ln(Production)%20%E2%80%94%20the%20variable%20is%20linked%20to%20higher%20production%0A%20%20%20%20-%20**Whiskers%20crossing%20zero%3A**%20the%20coefficient%20is%20not%20distinguishable%20from%20zero%20at%20%CE%B1%20%E2%89%88%200.05%20(95%25%20CI)%0A%20%20%20%20-%20**Two%20colors%20%2F%20groups%3A**%20full-panel%20estimates%20vs.%20candidate-cohort%20estimates%20%E2%80%94%20if%20cohort%20coefficients%20are%20larger%2C%20the%20screening%20step%20identified%20a%20subgroup%20where%20environmental%20effects%20are%20more%20pronounced%0A%0A%20%20%20%20%3E%20**Selection%20note%3A**%20The%20cohort%20was%20chosen%20in%20%C2%A71%20for%20strong%20environmental%20signal.%20Larger%20coefficients%20here%20are%20partly%20by%20construction%20%E2%80%94%20this%20comparison%20measures%20the%20*size*%20of%20the%20selection%20effect%2C%20not%20independent%20confirmation%20of%20the%20environmental%20signal.%20See%20main%20workbook%20%C2%A73%E2%80%93%C2%A74%20for%20the%20formal%20inference.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(ACCENT%2C%20ACCENT2%2C%20cohort_models%2C%20mo%2C%20pd%2C%20plt)%3A%0A%20%20%20%20_coef_rows%20%3D%20%5B%5D%0A%20%20%20%20for%20_label%2C%20_res%20in%20cohort_models%3A%0A%20%20%20%20%20%20%20%20for%20_term%2C%20_nice%20in%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20(%22Avg_Temp_C%22%2C%20%22Temperature%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20(%22Rain_mm_country_mean%22%2C%20%22Rainfall-between%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20(%22Rain_mm_within%22%2C%20%22Rainfall-within%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20(%22ln_Population%22%2C%20%22ln(Population)%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20(%22Oil_Price_Brent_USD%22%2C%20%22Oil%20price%22)%2C%0A%20%20%20%20%20%20%20%20%5D%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_ci%20%3D%20_res.conf_int().loc%5B_term%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20_coef_rows.append(%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22Sample%22%3A%20_label%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22Term%22%3A%20_nice%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22coef%22%3A%20_res.params%5B_term%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22lo%22%3A%20_ci%5B0%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22hi%22%3A%20_ci%5B1%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22p%22%3A%20_res.pvalues%5B_term%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D)%0A%20%20%20%20_coef_df%20%3D%20pd.DataFrame(_coef_rows)%0A%0A%20%20%20%20fig_coef_compare%2C%20ax_coef_compare%20%3D%20plt.subplots(figsize%3D(10%2C%206))%0A%20%20%20%20_terms%20%3D%20%5B%22Temperature%22%2C%20%22Rainfall-between%22%2C%20%22Rainfall-within%22%2C%20%22ln(Population)%22%2C%20%22Oil%20price%22%5D%0A%20%20%20%20%23%20Assign%20offsets%20and%20colors%20by%20position%20so%20dynamic%20label%20strings%20(which%20include%20country%20counts)%20work%0A%20%20%20%20_unique_samples%20%3D%20list(_coef_df%5B%22Sample%22%5D.unique())%0A%20%20%20%20_offset_list%20%3D%20%5B-0.17%2C%200.17%5D%0A%20%20%20%20_color_list%20%20%3D%20%5BACCENT%2C%20ACCENT2%5D%0A%0A%20%20%20%20for%20_i%2C%20_sample%20in%20enumerate(_unique_samples)%3A%0A%20%20%20%20%20%20%20%20_sub%20%3D%20_coef_df%5B_coef_df%5B%22Sample%22%5D%20%3D%3D%20_sample%5D.set_index(%22Term%22).loc%5B_terms%5D.reset_index()%0A%20%20%20%20%20%20%20%20_y%20%3D%20%5Bv%20%2B%20_offset_list%5B_i%20%25%20len(_offset_list)%5D%20for%20v%20in%20range(len(_terms))%5D%0A%20%20%20%20%20%20%20%20_xerr%20%3D%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20_sub%5B%22coef%22%5D.values%20-%20_sub%5B%22lo%22%5D.values%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20_sub%5B%22hi%22%5D.values%20-%20_sub%5B%22coef%22%5D.values%2C%0A%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20ax_coef_compare.errorbar(%0A%20%20%20%20%20%20%20%20%20%20%20%20_sub%5B%22coef%22%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20_y%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20xerr%3D_xerr%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20fmt%3D%22o%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20color%3D_color_list%5B_i%20%25%20len(_color_list)%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20capsize%3D4%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20label%3D_sample%2C%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20ax_coef_compare.axvline(0%2C%20color%3D%22%23777777%22%2C%20linestyle%3D%22--%22%2C%20linewidth%3D1)%0A%20%20%20%20ax_coef_compare.set_yticks(range(len(_terms)))%0A%20%20%20%20ax_coef_compare.set_yticklabels(_terms)%0A%20%20%20%20ax_coef_compare.set_xlabel(%22Coefficient%20estimate%20with%2095%25%20CI%22)%0A%20%20%20%20ax_coef_compare.set_title(%22Full%20panel%20vs%20candidate%20cohort%20(selected%20for%20environmental%20signal)%22)%0A%20%20%20%20ax_coef_compare.legend(fontsize%3D8)%0A%20%20%20%20plt.tight_layout()%0A%0A%20%20%20%20mo.mpl.interactive(fig_coef_compare)%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---%0A%20%20%20%20%23%23%205.%20Individual%20Country%20Mini-Models%0A%0A%20%20%20%20For%20the%20strongest%20countries%2C%20fit%20a%20small%20country-specific%20model.%20These%20models%20are%20useful%20for%20interpretation%2C%20but%20each%20country%20has%20only%20about%2030%20observations%2C%20so%20treat%20them%20as%20case-study%20evidence.%0A%0A%20%20%20%20%3E%20**Mini-model%20limitations%3A**%0A%20%20%20%20%3E%20-%20**Small%20n%20per%20country%20(~15%E2%80%9330%20rows)%3A**%20OLS%20coefficients%20can%20be%20volatile%20%E2%80%94%20one%20unusual%20year%20can%20shift%20the%20estimate%20substantially%0A%20%20%20%20%3E%20-%20**No%20year%20fixed%20effects%3A**%20global%20time%20trends%20(e.g.%2C%20oil%20price%20rises%201995%E2%80%932008)%20may%20appear%20as%20production%20trends%20within%20a%20country%0A%20%20%20%20%3E%20-%20**Rainfall%20in%20mini-models%3A**%20Within%20a%20single%20country%20there%20is%20no%20between-country%20variation%2C%20so%20raw%20%60Rain_mm%60%20is%20used%20directly%20(no%20decomposition%20needed).%20Where%20rainfall%20has%20too%20few%20unique%20values%2C%20the%20mini-model%20uses%20population%20and%20temperature%20only.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(candidate_screen%2C%20mo%2C%20panel%2C%20pd%2C%20smf)%3A%0A%20%20%20%20_mini_rows%20%3D%20%5B%5D%0A%20%20%20%20_top_iso3%20%3D%20candidate_screen.sort_values(%22Environment%20added%20R2%22%2C%20ascending%3DFalse)%5B%22ISO3%22%5D.head(10).tolist()%0A%0A%20%20%20%20for%20_iso3%20in%20_top_iso3%3A%0A%20%20%20%20%20%20%20%20_g%20%3D%20panel%5Bpanel%5B%22ISO3%22%5D%20%3D%3D%20_iso3%5D.sort_values(%22Year%22).copy()%0A%20%20%20%20%20%20%20%20_rain_ok%20%3D%20_g%5B%22Rain_mm%22%5D.nunique()%20%3E%3D%205%0A%20%20%20%20%20%20%20%20_formula%20%3D%20%22ln_Production%20~%20ln_Population%20%2B%20Avg_Temp_C%22%0A%20%20%20%20%20%20%20%20if%20_rain_ok%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_formula%20%2B%3D%20%22%20%2B%20Rain_mm%22%0A%0A%20%20%20%20%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_res%20%3D%20smf.ols(_formula%2C%20data%3D_g).fit(cov_type%3D%22HC3%22)%0A%20%20%20%20%20%20%20%20except%20Exception%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20continue%0A%0A%20%20%20%20%20%20%20%20_mini_rows.append(%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22ISO3%22%3A%20_iso3%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Country%22%3A%20_g%5B%22Country_Name%22%5D.iloc%5B0%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22n%22%3A%20int(_res.nobs)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Rain%20included%3F%22%3A%20%22Yes%22%20if%20_rain_ok%20else%20%22No%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22R2%22%3A%20_res.rsquared%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Adj%20R2%22%3A%20_res.rsquared_adj%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Temp%20coef%22%3A%20_res.params.get(%22Avg_Temp_C%22%2C%20float(%22nan%22))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Temp%20p%22%3A%20_res.pvalues.get(%22Avg_Temp_C%22%2C%20float(%22nan%22))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Rain%20coef%22%3A%20_res.params.get(%22Rain_mm%22%2C%20float(%22nan%22))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Rain%20p%22%3A%20_res.pvalues.get(%22Rain_mm%22%2C%20float(%22nan%22))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Pop%20coef%22%3A%20_res.params.get(%22ln_Population%22%2C%20float(%22nan%22))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Pop%20p%22%3A%20_res.pvalues.get(%22ln_Population%22%2C%20float(%22nan%22))%2C%0A%20%20%20%20%20%20%20%20%7D)%0A%0A%20%20%20%20mini_model_table%20%3D%20pd.DataFrame(_mini_rows)%0A%20%20%20%20_display%20%3D%20mini_model_table.copy()%0A%20%20%20%20for%20_c%20in%20%5B%22R2%22%2C%20%22Adj%20R2%22%2C%20%22Temp%20coef%22%2C%20%22Temp%20p%22%2C%20%22Rain%20coef%22%2C%20%22Rain%20p%22%2C%20%22Pop%20coef%22%2C%20%22Pop%20p%22%5D%3A%0A%20%20%20%20%20%20%20%20if%20_c%20in%20_display.columns%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_display%5B_c%5D%20%3D%20_display%5B_c%5D.map(lambda%20x%3A%20%22%22%20if%20pd.isna(x)%20else%20(%22%3C%200.0001%22%20if%20%22p%22%20in%20_c%20and%20x%20%3C%200.0001%20else%20f%22%7Bx%3A.4f%7D%22))%0A%0A%20%20%20%20mo.md(f%22%22%22%0A%20%20%20%20%23%23%23%205.1%20Top%20Candidate%20Country%20Models%0A%0A%20%20%20%20%7Bmo.as_html(_display)%7D%0A%0A%20%20%20%20%3E%20Rainfall%20is%20only%20included%20when%20it%20has%20enough%20within-country%20variation.%20Otherwise%2C%20the%20model%20uses%20population%20and%20temperature%20only.%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%23%23%20How%20to%20read%20the%20mini-model%20table%0A%0A%20%20%20%20%7C%20Column%20%7C%20Meaning%20%7C%0A%20%20%20%20%7C---%7C---%7C%0A%20%20%20%20%7C%20%60n%60%20%7C%20Country-year%20observations%20in%20the%20fit%20%7C%0A%20%20%20%20%7C%20%60Rain%20included%3F%60%20%7C%20Whether%20Rain_mm%20had%20sufficient%20year-to-year%20variation%20to%20include%20%7C%0A%20%20%20%20%7C%20%60R%C2%B2%60%20%2F%20%60Adj%20R%C2%B2%60%20%7C%20Explained-variance%20summary%20%E2%80%94%20see%20main%20workbook%20Note%2017%20%7C%0A%20%20%20%20%7C%20%60Temp%20coef%60%20%2F%20%60Rain%20coef%60%20%7C%20OLS%20coefficient%3A%20estimated%20change%20in%20ln(Production)%20per%20one-unit%20increase%20in%20the%20predictor%2C%20holding%20others%20constant%20%7C%0A%20%20%20%20%7C%20%60Temp%20p%60%20%2F%20%60Rain%20p%60%20%7C%20p-value%20for%20H%E2%82%80%3A%20%CE%B2%20%3D%200%3B%20small%20p%20%2B%20large%20coefficient%20%3D%20strongest%20signal%20%7C%0A%0A%20%20%20%20%3E%20A%20small%20Adj%20R%C2%B2%20with%20a%20significant%20predictor%20is%20common%20at%20n%20%E2%89%88%2015%E2%80%9330%20%E2%80%94%20it%20means%20the%20predictor%20is%20detectable%20but%20the%20model%20explains%20only%20part%20of%20the%20country's%20production%20story.%20Cross-reference%20with%20the%20%C2%A71%20ranking%20table%20for%20that%20country's%20DW%20stat%20before%20concluding.%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---%0A%20%20%20%20%23%23%206.%20Interpretation%0A%0A%20%20%20%20This%20follow-up%20changes%20the%20story%20from%20one%20global%20result%20to%20a%20screening%20workflow%3A%0A%0A%20%20%20%20-%20Population%20explains%20broad%20country%20scale.%0A%20%20%20%20-%20Population-adjusted%20residuals%20show%20which%20countries%20produce%20more%20or%20less%20than%20expected%20from%20population%20alone.%0A%20%20%20%20-%20Temperature%20and%20rainfall%20are%20then%20tested%20against%20what%20population%20did%20not%20explain.%0A%20%20%20%20-%20The%20strongest%20countries%20become%20candidates%20for%20deeper%20case-study%20interpretation.%0A%0A%20%20%20%20Recommended%20next%20step%3A%20choose%203-5%20strong%20countries%20from%20the%20ranking%20table%20and%20investigate%20agricultural%20history%2C%20climate%20shocks%2C%20policy%20changes%2C%20coffee%20variety%2C%20and%20data-quality%20notes%20for%20those%20countries.%0A%0A%20%20%20%20%3E%20**Hand-off%20to%20the%20main%20workbook%3A**%20Tier%20assignments%20and%20country%20rankings%20here%20are%20candidate-selection%20signals%2C%20not%20statistical%20conclusions.%20For%20formal%20H%E2%82%80%2FH%E2%82%81%20conclusions%20with%20INEG%20teacher-phrasing%2C%20see%20%60coffee_analysis.py%60%20%C2%A72%E2%80%93%C2%A75.%0A%20%20%20%20%22%22%22)%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
7854612f96012b0ec10fd99a34bc90ea