import%20marimo%0A%0A__generated_with%20%3D%20%220.19.4%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%20return%20(mo%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%20Poverty%20Risk%20Analysis%20in%20Spain%0A%0A%20%20%20%20Machine%20learning%20project%20to%20predict%20poverty%20risk%20in%20Spanish%20households%20using%20data%20from%20the%20Living%20Conditions%20Survey%20(ECV)%202024.%0A%0A%20%20%20%20%2F%2F%2F%20admonition%20%7C%20Context.%0A%0A%20%20%20%20This%20project%20is%20part%20of%20%5BCarlos%20Capote%5D(https%3A%2F%2Fgithub.com%2Felcapo%2F)'s%20capstone%20project%20for%20the%20%5BMachine%20Learning%20Zoomcamp%5D(https%3A%2F%2Fgithub.com%2FDataTalksClub%2Fmachine-learning-zoomcamp).%0A%20%20%20%20%2F%2F%2F%0A%0A%20%20%20%20%23%23%20Project%20Overview%0A%0A%20%20%20%20%23%23%23%20Objective%0A%0A%20%20%20%20Develop%20a%20classification%20model%20to%20predict%20whether%20a%20household%20would%20be%20considered%20to%20be%20at%20risk%20of%20poverty%20using%20demographic%20and%20socioeconomic%20data.%20In%20other%20words%2C%20we%20are%20reverse%20engineering%20the%20poverty%20risk%20calculation%20in%20order%20to%20create%20a%20model%20that%20can%20reproduce%20and%20explain%20the%20calculation.%0A%0A%20%20%20%20%23%23%23%20Dataset%0A%0A%20%20%20%20Living%20Conditions%20Survey%20(Encuesta%20de%20Condiciones%20de%20Vida%20-%20ECV)%202024%20from%20the%20Spanish%20National%20Statistics%20Institute%20(INE).%20The%20ECV%20is%20the%20Spanish%20version%20of%20the%20EU-SILC%20(European%20Union%20Statistics%20on%20Income%20and%20Living%20Conditions)%20survey.%0A%0A%20%20%20%20%23%23%23%23%20Target%20Variable%0A%0A%20%20%20%20*%20**%60vhPobreza%60**%20-%20Binary%20indicator%20of%20poverty%20risk%20(0%3DNo%2C%201%3DYes).%0A%0A%20%20%20%20%23%23%23%23%20Key%20Features%0A%0A%20%20%20%20-%20Household%20income%20and%20composition.%0A%20%20%20%20-%20Individual%20demographics%20(age%2C%20sex%2C%20education).%0A%20%20%20%20-%20Employment%20status%20and%20work%20patterns.%0A%20%20%20%20-%20Health%20indicators.%0A%20%20%20%20-%20Material%20deprivation%20measures.%0A%20%20%20%20-%20Housing%20conditions.%0A%0A%20%20%20%20%23%23%23%23%20Dataset%20Summary%0A%0A%20%20%20%20The%20project%20merges%20these%20files%20using%20a%20**hybrid%20aggregation%20strategy**%20that%20combines%20household%20head%20characteristics%20with%20aggregated%20household%20statistics.%0A%0A%20%20%20%20%7C%20File%20%7C%20Records%20%7C%20Description%20%7C%0A%20%20%20%20%7C------%7C---------%7C-------------%7C%0A%20%20%20%20%7C%20**ECV_Th_2024**%20%7C%2029%2C781%20households%20%7C%20Household-level%20data%20(includes%20target%20variable)%20%7C%0A%20%20%20%20%7C%20**ECV_Tp_2024**%20%7C%2061%2C526%20persons%20%7C%20Person-level%20data%20(demographics%2C%20employment%2C%20income)%20%7C%0A%0A%20%20%20%20To%20execute%20the%20merge%2C%20there%20is%20a%20**merge.py**%20script%20that%20does%20the%20job%20of%20merging%20the%20housholds%20and%20persons%20files%20into%20a%20single%20**data%2Fmerge.csv**%20file%2C%20which%20we'll%20be%20using%20as%20our%20dataset%20in%20this%20notebook%3A%0A%0A%20%20%20%20%60%60%60bash%0A%20%20%20%20uv%20run%20merge.py%0A%20%20%20%20%60%60%60%0A%0A%20%20%20%20Take%20into%20account%20that%2C%20during%20the%20merge%20process%3A%0A%0A%20%20%20%20*%20We%20also%20replaced%20negative%20values%20(which%20represent%20codes%20for%20different%20missing%20values)%20with%20_NaN_.%0A%20%20%20%20*%20We%20applied%20some%20data%20transformations%20in%20order%20to%20keep%20the%20notebook%20clean.%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%20Data%20Exploration%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%20pandas%20as%20pd%0A%20%20%20%20import%20numpy%20as%20np%0A%20%20%20%20import%20matplotlib.pyplot%20as%20plt%0A%20%20%20%20import%20seaborn%20as%20sns%0A%0A%20%20%20%20def%20load_dataset()%3A%0A%20%20%20%20%20%20%20%20return%20pd.read_csv('data%2Fmerged.csv')%0A%0A%20%20%20%20load_dataset().head()%0A%20%20%20%20return%20load_dataset%2C%20pd%2C%20plt%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%20By%20aggregating%20people%20rows%20to%20the%20household%20records%2C%20we%20obtained%20a%20dataset%20with%20the%20same%20number%20of%20rows%20as%20households%20but%20with%20additional%20columns.%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%20Normalization%0A%0A%20%20%20%20As%20the%20default%20column%20names%20are%20quite%20abstract%2C%20we'll%20renaming%20taking%20into%20consideration%20the%20information%20that%20we%20extracted%20into%20the%20**docs%2Fvariables-reference.md**%20file.%20Also%2C%20we'll%20remove%20some%20columns%20that%20we%20won't%20use.%20We'll%20also%20use%20an%20imputer%20to%20handle%20missing%20values%20by%20filling%20them%20with%20their%20corresponding%20medians.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(load_dataset%2C%20pd)%3A%0A%20%20%20%20from%20sklearn.impute%20import%20SimpleImputer%0A%0A%20%20%20%20imputer%20%3D%20SimpleImputer(strategy%3D'median')%0A%0A%20%20%20%20def%20rename_columns(dataset)%3A%0A%20%20%20%20%20%20%20%20return%20dataset.rename(columns%3D%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22HY020%22%3A%20%22total_disposable_income%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22HY023%22%3A%20%22income_before_all_social_transfers%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22head_PB150%22%3A%20%22head_sex%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22head_PB190%22%3A%20%22head_marital_status%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22head_PE021%22%3A%20%22head_education_level%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22head_PL051A%22%3A%20%22head_employment_status%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22head_PL060%22%3A%20%22head_hours_worked_per_week%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22head_PY010N%22%3A%20%22head_net_employee_income%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22head_PH010%22%3A%20%22head_general_health_status%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22vhPobreza%22%3A%20%22poverty_risk%22%2C%0A%20%20%20%20%20%20%20%20%7D).drop(columns%3D%5B%22HY022%22%2C%20%22HB070%22%2C%20%22HB100%22%5D)%0A%0A%20%20%20%20def%20handle_missing_values(dataset)%3A%0A%20%20%20%20%20%20%20%20return%20pd.DataFrame(%0A%20%20%20%20%20%20%20%20%20%20%20%20imputer.fit_transform(dataset)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20columns%3Ddataset.columns%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20def%20normalize_dataset(dataset)%3A%0A%20%20%20%20%20%20%20%20renamed_dataset%20%3D%20rename_columns(dataset)%0A%20%20%20%20%20%20%20%20return%20handle_missing_values(renamed_dataset)%0A%0A%20%20%20%20df%20%3D%20normalize_dataset(load_dataset())%0A%20%20%20%20return%20df%2C%20imputer%0A%0A%0A%40app.cell%0Adef%20_(df)%3A%0A%20%20%20%20df.head()%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(df)%3A%0A%20%20%20%20df.describe()%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%20Target%20Variable%0A%0A%20%20%20%20The%20target%20variable%2C%20%60vhPobreza%60%2C%20which%20indicates%20whether%20the%20people%20in%20the%20given%20household%20lives%20under%20risk%20of%20poverty%20(0%20%3D%20no%2C%201%20%3D%20yes)%20is%20an%20imbalanced%20variable%20with%20about%20a%2017%25%20of%20the%20households%20in%20the%20dataset%20being%20considered%20to%20be%20in%20risk%20of%20poverty.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(df%2C%20plt)%3A%0A%20%20%20%20plt.figure(figsize%3D(12%2C%206))%0A%20%20%20%20df%5B'poverty_risk'%5D.value_counts().plot(kind%3D'bar'%2C%20alpha%3D0.5%2C%20color%3D'steelblue'%2C%20edgecolor%3D'gray')%0A%20%20%20%20plt.title('Poverty%20Risk%20Distribution')%0A%20%20%20%20plt.xlabel('At%20Risk%20of%20Poverty')%0A%20%20%20%20plt.ylabel('Count')%0A%20%20%20%20plt.xticks(%5B0%2C%201%5D%2C%20%5B'No%20(0)'%2C%20'Yes%20(1)'%5D%2C%20rotation%3D0)%0A%20%20%20%20plt.show()%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%20Features%0A%0A%20%20%20%20In%20this%20section%2C%20we'll%20take%20a%20look%20at%20the%20most%20important%20feature%20variables.%0A%0A%20%20%20%20%23%23%23%20Household%20head%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(plt)%3A%0A%20%20%20%20def%20plot_histogram(data%2C%20title%2C%20xlabel%2C%20ylabel)%3A%0A%20%20%20%20%20%20%20%20fig%2C%20ax%20%3D%20plt.subplots(figsize%3D(12%2C%206))%0A%20%20%20%20%20%20%20%20data.plot.hist(bins%3D10%2C%20alpha%3D0.5%2C%20color%3D'steelblue'%2C%20edgecolor%3D'gray'%2C%20ax%3Dax)%0A%20%20%20%20%20%20%20%20ax.set_title(title)%0A%20%20%20%20%20%20%20%20ax.set_xlabel(xlabel)%0A%20%20%20%20%20%20%20%20ax.set_ylabel(ylabel)%0A%20%20%20%20%20%20%20%20return%20fig%0A%0A%20%20%20%20def%20plot_bars(data%2C%20title%2C%20xlabel%2C%20ylabel%2C%20xticklabels)%3A%0A%20%20%20%20%20%20%20%20fig%2C%20ax%20%3D%20plt.subplots(figsize%3D(12%2C%206))%0A%20%20%20%20%20%20%20%20data.value_counts().plot(kind%3D'bar'%2C%20alpha%3D0.5%2C%20color%3D'steelblue'%2C%20edgecolor%3D'gray'%2C%20ax%3Dax)%0A%20%20%20%20%20%20%20%20ax.set_title(title)%0A%20%20%20%20%20%20%20%20ax.set_xlabel(xlabel)%0A%20%20%20%20%20%20%20%20ax.set_ylabel(ylabel)%0A%20%20%20%20%20%20%20%20ax.set_xticklabels(xticklabels%2C%20rotation%3D0)%0A%20%20%20%20%20%20%20%20return%20fig%0A%20%20%20%20return%20plot_bars%2C%20plot_histogram%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(df%2C%20mo%2C%20plot_bars%2C%20plot_histogram)%3A%0A%20%20%20%20def%20plot_head_total_disposable_outcome()%3A%0A%20%20%20%20%20%20%20%20return%20plot_histogram(%0A%20%20%20%20%20%20%20%20%20%20%20%20data%3Ddf%5B'total_disposable_income'%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20title%3D'Total%20disposable%20income'%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20xlabel%3D'Income%20(%E2%82%AC)'%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20ylabel%3D'Count'%2C%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20def%20plot_head_age()%3A%0A%20%20%20%20%20%20%20%20return%20plot_histogram(%0A%20%20%20%20%20%20%20%20%20%20%20%20data%3Ddf%5B'head_age'%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20title%3D'Age%20of%20the%20household%20head'%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20xlabel%3D'Age%20(years)'%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20ylabel%3D'Count'%2C%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20def%20plot_head_sex()%3A%0A%20%20%20%20%20%20%20%20return%20plot_bars(%0A%20%20%20%20%20%20%20%20%20%20%20%20data%3Ddf%5B'head_sex'%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20title%3D'Sex'%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20xlabel%3D'Sex'%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20ylabel%3D'Count'%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20xticklabels%3D%5B'Man'%2C%20'Woman'%5D%2C%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20def%20plot_head_marital_status()%3A%0A%20%20%20%20%20%20%20%20return%20plot_bars(%0A%20%20%20%20%20%20%20%20%20%20%20%20data%3Ddf%5B'head_marital_status'%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20title%3D'Marital%20status'%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20xlabel%3D'Marital%20status'%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20ylabel%3D'Count'%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20xticklabels%3D%5B'Single'%2C%20'Married'%2C%20'Separated'%2C%20'Widowed'%2C%20'Divorced'%5D%2C%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20def%20plot_head_hours_worked_per_week()%3A%0A%20%20%20%20%20%20%20%20return%20plot_histogram(%0A%20%20%20%20%20%20%20%20%20%20%20%20data%3Ddf%5B'head_hours_worked_per_week'%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20title%3D'Hours%20worked%20per%20week'%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20xlabel%3D'Hours%20per%20week'%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20ylabel%3D'Count'%2C%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20def%20plot_head_general_health_status()%3A%0A%20%20%20%20%20%20%20%20return%20plot_bars(%0A%20%20%20%20%20%20%20%20%20%20%20%20data%3Ddf%5B'head_general_health_status'%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20title%3D'Health%20status'%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20xlabel%3D'Health%20status'%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20ylabel%3D'Count'%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20xticklabels%3D%5B'Very%20good'%2C%20'Good'%2C%20'Fair'%2C%20'Bad'%2C%20'Very%20bad'%5D%2C%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20mo.ui.tabs(%7B%0A%20%20%20%20%20%20%20%20%22Total%20disposable%20outcome%22%3A%20plot_head_total_disposable_outcome()%2C%0A%20%20%20%20%20%20%20%20%22Age%22%3A%20plot_head_age()%2C%0A%20%20%20%20%20%20%20%20%22Sex%22%3A%20plot_head_sex()%2C%0A%20%20%20%20%20%20%20%20%22Marital%20status%22%3A%20plot_head_marital_status()%2C%0A%20%20%20%20%20%20%20%20%22Hours%20Worked%20per%20Week%22%3A%20plot_head_hours_worked_per_week()%2C%0A%20%20%20%20%20%20%20%20%22General%20Health%20Status%22%3A%20plot_head_general_health_status()%2C%0A%20%20%20%20%7D)%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%20Household%20members%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(df%2C%20mo%2C%20plot_histogram)%3A%0A%20%20%20%20def%20plot_household_size()%3A%0A%20%20%20%20%20%20%20%20return%20plot_histogram(%0A%20%20%20%20%20%20%20%20%20%20%20%20data%3Ddf%5B'household_size'%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20title%3D'Household%20size'%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20xlabel%3D'People'%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20ylabel%3D'Count'%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20def%20plot_mean_age()%3A%0A%20%20%20%20%20%20%20%20return%20plot_histogram(%0A%20%20%20%20%20%20%20%20%20%20%20%20data%3Ddf%5B'mean_age'%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20title%3D'Mean%20age'%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20xlabel%3D'Age'%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20ylabel%3D'Count'%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20def%20plot_dependency_ratio()%3A%0A%20%20%20%20%20%20%20%20return%20plot_histogram(%0A%20%20%20%20%20%20%20%20%20%20%20%20data%3Ddf%5B'dependency_ratio'%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20title%3D'Dependency%20ratio'%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20xlabel%3D'Ratio'%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20ylabel%3D'Count'%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20def%20plot_income_per_capita()%3A%0A%20%20%20%20%20%20%20%20return%20plot_histogram(%0A%20%20%20%20%20%20%20%20%20%20%20%20data%3Ddf%5B'income_per_capita'%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20title%3D'Income%20per%20capita'%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20xlabel%3D'Income%20(%E2%82%AC)'%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20ylabel%3D'Count'%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20mo.ui.tabs(%7B%0A%20%20%20%20%20%20%20%20%22Household%20size%22%3A%20plot_household_size()%2C%0A%20%20%20%20%20%20%20%20%22Mean%20age%22%3A%20plot_mean_age()%2C%0A%20%20%20%20%20%20%20%20%22Dependency%20ratio%22%3A%20plot_dependency_ratio()%2C%0A%20%20%20%20%20%20%20%20%22Income%20per%20capita%22%3A%20plot_income_per_capita()%2C%0A%20%20%20%20%7D)%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%20Dataset%20Splits%0A%0A%20%20%20%20Now%20that%20we%20have%20the%20dataset%20ready%20and%20we%20also%20have%20some%20knowledge%20about%20what%20it%20contains%2C%20we'll%20create%20our%20dataset%20splits%3A%0A%0A%20%20%20%20*%20**train**%3A%20used%20to%20train%20our%20models%20before%20comparing%20them%0A%20%20%20%20*%20**val**%3A%20used%20to%20validate%20hyperparameters%20while%20still%20choosing%20the%20best%20model%0A%20%20%20%20*%20**test**%3A%20used%20to%20evaluate%20the%20final%20model%0A%20%20%20%20*%20**full**%3A%20used%20to%20train%20the%20production%20model%20(combines%20the%20train%20and%20validation%20splots)%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(df)%3A%0A%20%20%20%20from%20sklearn.model_selection%20import%20train_test_split%0A%0A%20%20%20%20def%20split_dataset(dataset%2C%20random_state%3D1)%3A%0A%20%20%20%20%20%20%20%20full%2C%20test%20%3D%20train_test_split(dataset%2C%20test_size%3D0.2%2C%20random_state%3Drandom_state)%0A%20%20%20%20%20%20%20%20train%2C%20val%20%3D%20train_test_split(full%2C%20test_size%3D0.25%2C%20random_state%3Drandom_state)%0A%0A%20%20%20%20%20%20%20%20return%20train%2C%20full%2C%20val%2C%20test%0A%0A%20%20%20%20df_train%2C%20df_full%2C%20df_val%2C%20df_test%20%3D%20split_dataset(df)%0A%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%22len(df_train)%22%3A%20len(df_train)%2C%0A%20%20%20%20%20%20%20%20%22len(df_full)%22%3A%20len(df_full)%2C%0A%20%20%20%20%20%20%20%20%22len(df_val)%22%3A%20len(df_val)%2C%0A%20%20%20%20%20%20%20%20%22len(df_test)%22%3A%20len(df_test)%2C%0A%20%20%20%20%7D%0A%20%20%20%20return%20df_full%2C%20df_test%2C%20df_train%2C%20df_val%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%20Separating%20Features%20and%20Target%0A%0A%20%20%20%20We'll%20now%20separate%20our%20features%20from%20the%20target%20variable.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(df_full%2C%20df_test%2C%20df_train%2C%20df_val)%3A%0A%20%20%20%20def%20separate_features_and_target(dataset)%3A%0A%20%20%20%20%20%20%20%20target%20%3D%20dataset.poverty_risk.copy()%0A%20%20%20%20%20%20%20%20features%20%3D%20dataset.copy()%0A%20%20%20%20%20%20%20%20del%20features%5B%22poverty_risk%22%5D%0A%0A%20%20%20%20%20%20%20%20return%20features%2C%20target%0A%0A%20%20%20%20X_train%2C%20y_train%20%3D%20separate_features_and_target(df_train)%0A%20%20%20%20X_full%2C%20y_full%20%3D%20separate_features_and_target(df_full)%0A%20%20%20%20X_val%2C%20y_val%20%3D%20separate_features_and_target(df_val)%0A%20%20%20%20X_test%2C%20y_test%20%3D%20separate_features_and_target(df_test)%0A%20%20%20%20return%20X_full%2C%20X_test%2C%20X_train%2C%20X_val%2C%20y_full%2C%20y_test%2C%20y_train%2C%20y_val%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%20Scale%20Features%0A%0A%20%20%20%20As%20some%20models%20operate%20better%20with%20scaled%20features%2C%20we'll%20train%20a%20standard%20scaler.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(X_full%2C%20X_test%2C%20X_train%2C%20X_val)%3A%0A%20%20%20%20from%20sklearn.preprocessing%20import%20StandardScaler%0A%0A%20%20%20%20scaler%20%3D%20StandardScaler()%0A%0A%20%20%20%20def%20scale_features(X_train%2C%20X_full%2C%20X_val%2C%20X_test)%3A%0A%20%20%20%20%20%20%20%20X_train_scaled%20%3D%20scaler.fit_transform(X_train)%0A%20%20%20%20%20%20%20%20X_full_scaled%20%3D%20scaler.transform(X_full)%0A%20%20%20%20%20%20%20%20X_val_scaled%20%3D%20scaler.transform(X_val)%0A%20%20%20%20%20%20%20%20X_test_scaled%20%3D%20scaler.transform(X_test)%0A%20%20%20%20%20%20%20%20return%20X_train_scaled%2C%20X_full_scaled%2C%20X_val_scaled%2C%20X_test_scaled%0A%0A%20%20%20%20X_train_scaled%2C%20X_full_scaled%2C%20X_val_scaled%2C%20X_test_scaled%20%3D%20scale_features(X_train%2C%20X_full%2C%20X_val%2C%20X_test)%0A%20%20%20%20return%20X_test_scaled%2C%20X_train_scaled%2C%20scaler%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%20Modeling%0A%0A%20%20%20%20In%20this%20section%2C%20we'll%20use%20different%20machine%20learning%20classification%20models%20to%20predict%20the%20poverty%20risk.%20We'll%20finish%20the%20section%20by%20evaluating%20them%20and%20comparing%20them.%0A%0A%20%20%20%20%23%23%23%20Logistic%20Regression%0A%0A%20%20%20%20%23%23%23%23%20Training%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(X_train_scaled%2C%20y_train)%3A%0A%20%20%20%20from%20sklearn.linear_model%20import%20LogisticRegression%0A%0A%20%20%20%20def%20train_logistic_regressor(X%2C%20y)%3A%0A%20%20%20%20%20%20%20%20model%20%3D%20LogisticRegression(%0A%20%20%20%20%20%20%20%20%20%20%20%20class_weight%3D'balanced'%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20max_iter%3D500%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20random_state%3D1%2C%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%20%20%20%20model.fit(X%2C%20y)%0A%0A%20%20%20%20%20%20%20%20return%20model%0A%0A%20%20%20%20logistic_regression_model%20%3D%20train_logistic_regressor(X_train_scaled%2C%20y_train)%0A%20%20%20%20return%20(logistic_regression_model%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%23%20Evaluate%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(X_test_scaled%2C%20logistic_regression_model%2C%20y_test)%3A%0A%20%20%20%20from%20sklearn.metrics%20import%20classification_report%2C%20f1_score%2C%20roc_auc_score%0A%0A%20%20%20%20def%20evaluate_classifier(model%2C%20X_test%2C%20y_test)%3A%0A%20%20%20%20%20%20%20%20y_pred%20%3D%20model.predict(X_test)%0A%20%20%20%20%20%20%20%20y_proba%20%3D%20model.predict_proba(X_test)%5B%3A%2C%201%5D%0A%0A%20%20%20%20%20%20%20%20report%20%3D%20classification_report(y_test%2C%20y_pred%2C%20output_dict%3DTrue)%0A%20%20%20%20%20%20%20%20report%5B%22f1%20score%22%5D%20%3D%20f1_score(y_test%2C%20y_pred)%0A%20%20%20%20%20%20%20%20report%5B%22auc-roc%20score%22%5D%20%3D%20roc_auc_score(y_test%2C%20y_proba)%0A%0A%20%20%20%20%20%20%20%20return%20report%0A%0A%20%20%20%20logistic_regression_evaluation%20%3D%20evaluate_classifier(logistic_regression_model%2C%20X_test_scaled%2C%20y_test)%0A%0A%20%20%20%20logistic_regression_evaluation%0A%20%20%20%20return%20evaluate_classifier%2C%20roc_auc_score%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**Precision**%0A%0A%20%20%20%20From%20the%20previous%20output%20we%20see%20that%20this%20model%20gets%20right%20most%20if%20its%20attempts%20to%20predict%20that%20a%20household%20is%20not%20in%20poverty%20risk%2C%20having%20a%2099.23%25%20of%20success%20at%20predicting%20them.%20But%20it%20has%20a%20lot%20less%20precission%20when%20it%20predicts%20households%20are%20in%20poverty%20risk%2C%20having%20a%2075.73%25%20success%20rate%20in%20those%20cases.%0A%0A%20%20%20%20**Recall**%0A%0A%20%20%20%20If%20rather%20than%20focusing%20at%20what%20it%20predicts%2C%20we%20focus%20in%20what's%20real%2C%20we%20see%20that%20the%20model%20correctly%20identified%2093.35%25%20of%20the%20households%20that%20were%20not%20in%20poverty%20risk%20and%20a%2096.76%25%20of%20the%20households%20that%20were%20in%20poverty%20risk.%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%20Feature%20Importances%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(X_train%2C%20logistic_regression_model%2C%20pd%2C%20plt)%3A%0A%20%20%20%20def%20plot_logistic_regression_feature_importance()%3A%0A%20%20%20%20%20%20%20%20logistic_regression_feature_importance%20%3D%20pd.DataFrame(%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20'Feature'%3A%20X_train.columns%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20'Coefficient'%3A%20logistic_regression_model.coef_%5B0%5D%0A%20%20%20%20%20%20%20%20%7D).sort_values('Coefficient'%2C%20key%3Dabs%2C%20ascending%3DTrue)%0A%0A%20%20%20%20%20%20%20%20fig%2C%20ax%20%3D%20plt.subplots(figsize%3D(12%2C%206))%0A%20%20%20%20%20%20%20%20logistic_regression_feature_importance.tail(15).plot(x%3D'Feature'%2C%20y%3D'Coefficient'%2C%20kind%3D'barh'%2C%20ax%3Dax)%0A%20%20%20%20%20%20%20%20ax.set_title('Logistic%20regression%20-%20Feature%20importances')%0A%20%20%20%20%20%20%20%20ax.set_xlabel('Coefficient')%0A%20%20%20%20%20%20%20%20plt.show()%0A%0A%20%20%20%20plot_logistic_regression_feature_importance()%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%20Random%20Forest%20Classifier%0A%0A%20%20%20%20%23%23%23%23%20Train%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(X_train%2C%20y_train)%3A%0A%20%20%20%20from%20sklearn.ensemble%20import%20RandomForestClassifier%0A%0A%20%20%20%20def%20train_random_forest_classifier(X%2C%20y)%3A%0A%20%20%20%20%20%20%20%20model%20%3D%20RandomForestClassifier(%0A%20%20%20%20%20%20%20%20%20%20%20%20n_estimators%3D100%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20class_weight%3D'balanced'%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20random_state%3D1%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20n_jobs%3D-1%2C%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%20%20%20%20model.fit(X%2C%20y)%0A%0A%20%20%20%20%20%20%20%20return%20model%0A%0A%20%20%20%20random_forest_model%20%3D%20train_random_forest_classifier(X_train%2C%20y_train)%0A%20%20%20%20return%20(random_forest_model%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%23%20Evaluate%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(X_test%2C%20evaluate_classifier%2C%20random_forest_model%2C%20y_test)%3A%0A%20%20%20%20random_forest_evaluation%20%3D%20evaluate_classifier(random_forest_model%2C%20X_test%2C%20y_test)%0A%0A%20%20%20%20random_forest_evaluation%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**Precision**%0A%0A%20%20%20%20From%20the%20previous%20output%20we%20see%20that%20this%20model%20also%20gets%20right%20most%20if%20its%20attempts%20to%20predict%20that%20a%20household%20is%20not%20in%20poverty%20risk%2C%20having%20a%2098.20%25%20of%20success%20at%20predicting%20them.%20But%20this%20time%2C%20the%20model%20also%20has%20a%20very%20%20high%20precission%20when%20it%20predicts%20households%20are%20in%20poverty%20risk%2C%20having%20a%2097.37%25%20success%20rate%20in%20those%20cases.%0A%0A%20%20%20%20**Recall**%0A%0A%20%20%20%20If%20rather%20than%20focusing%20at%20what%20it%20predicts%2C%20we%20focus%20in%20what's%20real%2C%20we%20see%20that%20the%20model%20correctly%20identified%2099.47%25%20of%20the%20households%20that%20were%20not%20in%20poverty%20risk%20and%20a%2091.53%25%20of%20the%20households%20that%20were%20in%20poverty%20risk.%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%20Feature%20Importances%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(X_train%2C%20pd%2C%20plt%2C%20random_forest_model)%3A%0A%20%20%20%20def%20plot_random_forest_feature_importance()%3A%0A%20%20%20%20%20%20%20%20random_forest_feature_importance%20%3D%20pd.DataFrame(%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20'Feature'%3A%20X_train.columns%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20'Importance'%3A%20random_forest_model.feature_importances_%0A%20%20%20%20%20%20%20%20%7D).sort_values('Importance'%2C%20ascending%3DTrue)%0A%0A%20%20%20%20%20%20%20%20fig%2C%20ax%20%3D%20plt.subplots(figsize%3D(12%2C%206))%0A%20%20%20%20%20%20%20%20random_forest_feature_importance.tail(15).plot(x%3D'Feature'%2C%20y%3D'Importance'%2C%20kind%3D'barh'%2C%20ax%3Dax)%0A%20%20%20%20%20%20%20%20ax.set_title('Random%20forest%20-%20Feature%20importances')%0A%20%20%20%20%20%20%20%20ax.set_xlabel('Importance')%0A%20%20%20%20%20%20%20%20plt.show()%0A%0A%20%20%20%20plot_random_forest_feature_importance()%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%20XGBoost%0A%0A%20%20%20%20%23%23%23%23%20Train%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(X_train%2C%20y_train)%3A%0A%20%20%20%20from%20xgboost%20import%20XGBClassifier%0A%0A%20%20%20%20def%20train_xgboost_classifier()%3A%0A%20%20%20%20%20%20%20%20class_ratio%20%3D%20(y_train%20%3D%3D%200).sum()%20%2F%20(y_train%20%3D%3D%201).sum()%0A%0A%20%20%20%20%20%20%20%20model%20%3D%20XGBClassifier(%0A%20%20%20%20%20%20%20%20%20%20%20%20scale_pos_weight%3Dclass_ratio%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20random_state%3D1%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20n_jobs%3D-1%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%20%20%20%20model.fit(X_train%2C%20y_train)%0A%0A%20%20%20%20%20%20%20%20return%20model%0A%0A%20%20%20%20xgboost_classifier_model%20%3D%20train_xgboost_classifier()%0A%20%20%20%20return%20XGBClassifier%2C%20xgboost_classifier_model%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%20Evaluate%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(X_test%2C%20evaluate_classifier%2C%20xgboost_classifier_model%2C%20y_test)%3A%0A%20%20%20%20xgboost_classifier_evaluation%20%3D%20evaluate_classifier(xgboost_classifier_model%2C%20X_test%2C%20y_test)%0A%0A%20%20%20%20xgboost_classifier_evaluation%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**Precision**%0A%0A%20%20%20%20From%20the%20previous%20output%20we%20see%20that%20this%20model%20also%20gets%20right%20most%20if%20its%20attempts%20to%20predict%20that%20a%20household%20is%20not%20in%20poverty%20risk%2C%20having%20a%2098.83%25%20of%20success%20at%20predicting%20them.%20But%20this%20time%2C%20the%20model%20also%20has%20a%20very%20%20high%20precission%20when%20it%20predicts%20households%20are%20in%20poverty%20risk%2C%20having%20a%2093.42%25%20success%20rate%20in%20those%20cases.%0A%0A%20%20%20%20**Recall**%0A%0A%20%20%20%20If%20rather%20than%20focusing%20at%20what%20it%20predicts%2C%20we%20focus%20in%20what's%20real%2C%20we%20see%20that%20the%20model%20correctly%20identified%2098.57%25%20of%20the%20households%20that%20were%20not%20in%20poverty%20risk%20and%20a%2094.57%25%20of%20the%20households%20that%20were%20in%20poverty%20risk.%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%20Comparisson%0A%0A%20%20%20%20From%20the%20completed%20evaluations%2C%20we'll%20continue%20with%20the%20**XGBoost**%20model.%20The%20reason%20is%20that%20although%20it%20performed%20more%20or%20less%20like%20the%20random%20forest%2C%20it%20seems%20to%20have%20identified%20better%20the%20households%20in%20poverty%20risk%2C%20which%20seems%20more%20important%20than%20wrongly%20identifying%20a%20household%20as%20being%20at%20risk%20when%20it's%20not.%0A%0A%20%20%20%20%7C%20Metric%20%7C%20Logistic%20regression%20%7C%20Random%20forest%20%7C%20XGBoost%20%7C%0A%20%20%20%20%7C%20---%20%7C%20---%20%7C%20---%20%7C%20---%20%7C%0A%20%20%20%20%7C%20Precision%20(class%201)%20%7C%200.76%20%7C%200.97%20%7C%200.93%20%7C%0A%20%20%20%20%7C%20Recall%20(class%201)%20%7C%200.97%20%7C%200.92%20%7C%200.95%20%7C%0A%20%20%20%20%7C%20F1%20(class%201)%20%7C%200.85%20%7C%200.94%20%7C%200.94%20%7C%0A%20%20%20%20%7C%20False%20negatives%20%7C%20Very%20few%20%7C%20More%20%7C%20Few%20%7C%0A%20%20%20%20%7C%20False%20positives%20%7C%20More%20%7C%20Very%20few%20%7C%20Some%20%7C%0A%20%20%20%20%7C%20Accuracy%20global%20%7C%200.94%20%7C%200.98%20%7C%200.98%20%7C%0A%20%20%20%20%7C%20AUC-ROC%20%7C%200.991%20%7C%200.994%20%7C%200.996%20%7C%0A%20%20%20%20%7C%20Profile%20%7C%20Inclusive%20%7C%20Restrictive%20%7C%20Balanced%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(r%22%22%22%0A%20%20%20%20%23%23%23%20Hyperparameter%20Tuning%0A%0A%20%20%20%20We'll%20now%20search%20for%20better%20XGBoost%20parameters%20by%20training%20several%20models%20with%20a%20distribution%20of%20parameters.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(XGBClassifier%2C%20X_train%2C%20X_val%2C%20roc_auc_score%2C%20y_train%2C%20y_val)%3A%0A%20%20%20%20from%20sklearn.model_selection%20import%20RandomizedSearchCV%0A%0A%20%20%20%20booster_param_distributions%20%3D%20%7B%0A%20%20%20%20%20%20%20%20%22eta%22%3A%20%5B0.1%2C%200.2%2C%200.3%2C%201.0%5D%2C%0A%20%20%20%20%20%20%20%20%22max_depth%22%3A%20%5B5%2C%2025%2C%2050%5D%2C%0A%20%20%20%20%20%20%20%20%22min_child_weight%22%3A%20%5B1%2C%203%2C%205%2C%207%5D%2C%0A%20%20%20%20%7D%0A%0A%20%20%20%20def%20search_best_xgboost(X_train%2C%20y_train%2C%20X_val%2C%20y_val%2C%20param_distributions)%3A%0A%20%20%20%20%20%20%20%20booster%20%3D%20XGBClassifier(%0A%20%20%20%20%20%20%20%20%20%20%20%20tree_method%3D%22hist%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20early_stopping_rounds%3D2%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20eval_metric%3Droc_auc_score%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20random_state%3D1%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20objective%3D%22binary%3Alogistic%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20nthread%3D8%2C%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%20%20%20%20booster_search%20%3D%20RandomizedSearchCV(booster%2C%20param_distributions)%0A%20%20%20%20%20%20%20%20booster_search.fit(X_train%2C%20y_train%2C%20eval_set%3D%5B(X_val%2C%20y_val)%5D%2C%20verbose%3DFalse)%0A%0A%20%20%20%20%20%20%20%20return%20booster_search%0A%0A%20%20%20%20booster_search%20%3D%20search_best_xgboost(X_train%2C%20y_train%2C%20X_val%2C%20y_val%2C%20booster_param_distributions)%0A%20%20%20%20return%20(booster_search%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(booster_search%2C%20plt)%3A%0A%20%20%20%20def%20plot_experiments(search)%3A%0A%20%20%20%20%20%20%20%20fit_times%20%3D%20search.cv_results_%5B%22mean_fit_time%22%5D%0A%20%20%20%20%20%20%20%20scores%20%3D%20search.cv_results_%5B%22mean_test_score%22%5D%0A%0A%20%20%20%20%20%20%20%20fig%2C%20ax_scores%20%3D%20plt.subplots(figsize%3D(12%2C%206))%0A%20%20%20%20%20%20%20%20ax_fit_times%20%3D%20ax_scores.twinx()%0A%0A%20%20%20%20%20%20%20%20colors%20%3D%20%5B%22royalblue%22%20for%20_%20in%20range(len(search.cv_results_))%5D%0A%20%20%20%20%20%20%20%20colors%5Bsearch.best_index_%5D%20%3D%20%22forestgreen%22%0A%0A%20%20%20%20%20%20%20%20ax_scores.bar(x%3Drange(len(scores))%2C%20height%3Dscores%2C%20color%3Dcolors)%0A%20%20%20%20%20%20%20%20ax_scores.set_xlabel(%22Experiment%22)%0A%20%20%20%20%20%20%20%20ax_scores.set_ylabel(%22Mean%20test%20score%22)%0A%0A%20%20%20%20%20%20%20%20ax_fit_times.plot(range(len(scores))%2C%20fit_times%2C%20color%3D%22red%22)%0A%20%20%20%20%20%20%20%20ax_scores.set_xticks(range(len(scores)))%0A%20%20%20%20%20%20%20%20ax_fit_times.set_ylabel(%22Mean%20fit%20time%22)%0A%0A%20%20%20%20%20%20%20%20plt.title(%22Experiment%20analysis%22)%0A%20%20%20%20%20%20%20%20plt.show()%0A%0A%20%20%20%20plot_experiments(booster_search)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(booster_search%2C%20plt)%3A%0A%20%20%20%20def%20plot_xgboost_parameters(search)%3A%0A%20%20%20%20%20%20%20%20_%2C%20axis%20%3D%20plt.subplots(1%2C%203%2C%20figsize%3D(16%2C%203))%0A%0A%20%20%20%20%20%20%20%20colors%20%3D%20%5B%22royalblue%22%20for%20_%20in%20range(len(search.cv_results_))%5D%0A%20%20%20%20%20%20%20%20colors%5Bsearch.best_index_%5D%20%3D%20%22forestgreen%22%0A%0A%20%20%20%20%20%20%20%20eta%20%3D%20%5Bparam%5B%22eta%22%5D%20for%20param%20in%20search.cv_results_%5B%22params%22%5D%5D%0A%20%20%20%20%20%20%20%20ax_eta%20%3D%20axis%5B0%5D%0A%20%20%20%20%20%20%20%20ax_eta.bar(x%3Drange(len(eta))%2C%20height%3Deta%2C%20color%3Dcolors)%0A%20%20%20%20%20%20%20%20ax_eta.set_xticks(range(len(eta)))%0A%20%20%20%20%20%20%20%20ax_eta.set_xlabel(%22Experiment%22)%0A%20%20%20%20%20%20%20%20ax_eta.set_title(%22Eta%22)%0A%0A%20%20%20%20%20%20%20%20max_depth%20%3D%20%5Bparam%5B%22max_depth%22%5D%20if%20param%5B%22max_depth%22%5D%20!%3D%20None%20else%200%20for%20param%20in%20search.cv_results_%5B%22params%22%5D%5D%0A%20%20%20%20%20%20%20%20ax_max_depth%20%3D%20axis%5B1%5D%0A%20%20%20%20%20%20%20%20ax_max_depth.bar(x%3Drange(len(max_depth))%2C%20height%3Dmax_depth%2C%20color%3Dcolors)%0A%20%20%20%20%20%20%20%20ax_max_depth.set_xticks(range(len(max_depth)))%0A%20%20%20%20%20%20%20%20ax_max_depth.set_xlabel(%22Experiment%22)%0A%20%20%20%20%20%20%20%20ax_max_depth.set_title(%22Maximum%20depth%22)%0A%0A%20%20%20%20%20%20%20%20min_child_weight%20%3D%20%5Bparam%5B%22min_child_weight%22%5D%20for%20param%20in%20search.cv_results_%5B%22params%22%5D%5D%0A%20%20%20%20%20%20%20%20ax_min_child_weight%20%3D%20axis%5B2%5D%0A%20%20%20%20%20%20%20%20ax_min_child_weight.bar(x%3Drange(len(min_child_weight))%2C%20height%3Dmin_child_weight%2C%20color%3Dcolors)%0A%20%20%20%20%20%20%20%20ax_min_child_weight.set_xticks(range(len(min_child_weight)))%0A%20%20%20%20%20%20%20%20ax_min_child_weight.set_xlabel(%22Experiment%22)%0A%20%20%20%20%20%20%20%20ax_min_child_weight.set_title(%22Mimimum%20child%20weight%22)%0A%0A%20%20%20%20%20%20%20%20plt.show()%0A%0A%20%20%20%20plot_xgboost_parameters(booster_search)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(booster_search)%3A%0A%20%20%20%20booster_search.best_params_%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%20Final%20Model%0A%0A%20%20%20%20%23%23%23%23%20Train%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(XGBClassifier%2C%20X_full%2C%20y_full)%3A%0A%20%20%20%20def%20train_final_xgboost_classifier(min_child_weight%2C%20max_depth%2C%20eta)%3A%0A%20%20%20%20%20%20%20%20class_ratio%20%3D%20(y_full%20%3D%3D%200).sum()%20%2F%20(y_full%20%3D%3D%201).sum()%0A%0A%20%20%20%20%20%20%20%20model%20%3D%20XGBClassifier(%0A%20%20%20%20%20%20%20%20%20%20%20%20min_child_weight%3Dmin_child_weight%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20max_depth%3Dmax_depth%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20eta%3Deta%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20scale_pos_weight%3Dclass_ratio%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20random_state%3D1%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20n_jobs%3D-1%0A%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%20%20%20%20model.fit(X_full%2C%20y_full)%0A%0A%20%20%20%20%20%20%20%20return%20model%0A%0A%20%20%20%20final_model%20%3D%20train_final_xgboost_classifier(min_child_weight%3D1%2C%20max_depth%3D50%2C%20eta%3D1.0)%0A%20%20%20%20return%20(final_model%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%23%20Evaluate%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(X_test%2C%20evaluate_classifier%2C%20final_model%2C%20y_test)%3A%0A%20%20%20%20final_model_evaluation%20%3D%20evaluate_classifier(final_model%2C%20X_test%2C%20y_test)%0A%0A%20%20%20%20final_model_evaluation%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%20Save%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(final_model%2C%20imputer%2C%20scaler)%3A%0A%20%20%20%20import%20pickle%0A%0A%20%20%20%20def%20save_model()%3A%0A%20%20%20%20%20%20%20%20with%20open('dist%2Fmodel%2Fmodel.pkl'%2C%20%22wb%22)%20as%20f%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20pickle.dump(final_model%2C%20f)%0A%20%20%20%20%20%20%20%20with%20open('dist%2Fmodel%2Fimputer.pkl'%2C%20%22wb%22)%20as%20f%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20pickle.dump(imputer%2C%20f)%0A%20%20%20%20%20%20%20%20with%20open('dist%2Fmodel%2Fscaler.pkl'%2C%20%22wb%22)%20as%20f%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20pickle.dump(scaler%2C%20f)%0A%0A%20%20%20%20save_model()%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%20Voil%C3%A0%20!%20We%20already%20trained%20and%20saved%20our%20model.%20Now%2C%20we'll%20prepare%20a%20container%20so%20where%20it%20can%20be%20easily%20executed.%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%20Dockerization%0A%0A%20%20%20%20The%20containerized%20version%20of%20this%20model%20is%20available%20in%20the%20**dist%2F**%20folder%20next%20to%20this%20notebook.%0A%0A%20%20%20%20%23%23%23%20Installation%0A%0A%20%20%20%20To%20test%20it%20locally%2C%20you%20must%20first%20clone%20the%20repository%3A%0A%0A%20%20%20%20%60%60%60bash%0A%20%20%20%20git%20clone%20https%3A%2F%2Fgithub.com%2Felcapo%2Fml-zoomcamp-notes-and-homework%0A%20%20%20%20cd%20ml-zoomcamp-notes-and-homework%2Fprojects%2Fcapstone%2Fdist%0A%20%20%20%20%60%60%60%0A%0A%20%20%20%20The%20repository%20contains%20other%20projects%20and%20even%20some%20datasets%2C%20so%20the%20download%20may%20take%20a%20few%20minutes.%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%20Local%20Development%0A%0A%20%20%20%20To%20quickly%20test%20the%20service%2C%20you%20don't%20even%20need%20Docker.%20You%20can%20run%20the%20FastAPI%20server%20with%3A%0A%0A%20%20%20%20%60%60%60bash%0A%20%20%20%20uv%20run%20fastapi%20dev%20serve.py%0A%0A%20%20%20%20%23%20or%20in%20a%20specific%20port%20(ex.%208383)%0A%20%20%20%20uv%20run%20fastapi%20dev%20--port%208383%20serve.py%0A%20%20%20%20%60%60%60%0A%0A%20%20%20%20This%20will%20start%20a%20local%20service%20that%20you%20can%20query%20locally%20with%20your%20web%20browser.%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%20Run%20the%20Container%0A%0A%20%20%20%20The%20folder%20also%20provides%20a%20Docker%20image%20that%20runs%20the%20service%20using%20Uvicorn.%20To%20run%20it%3A%0A%0A%20%20%20%20%60%60%60bash%0A%20%20%20%20docker%20compose%20up%20-d%0A%0A%20%20%20%20%23%20or%20in%20a%20specific%20port%20(ex.%208383)%0A%20%20%20%20PORT%3D8383%20docker%20compose%20up%20-d%0A%20%20%20%20%60%60%60%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%20Usage%0A%0A%20%20%20%20Thanks%20to%20FastAPI%2C%20you%20can%20navigate%20to%20the%20**%2Fdocs**%20URL%20of%20the%20server%20to%20see%20the%20documentation.%20The%20URL%2C%20only%20available%20from%20your%20own%20computer%2C%20would%20look%20like%20this%3A%20http%3A%2F%2F127.0.0.1%3A8383%2Fdocs.%0A%0A%20%20%20%20There%20are%203%20endpoints%20available%3A%0A%0A%20%20%20%20*%20**ping**%3A%20helper%20to%20test%20that%20the%20service%20is%20up%20and%20running%0A%20%20%20%20*%20**random_request**%3A%20returns%20a%20random%20household%20record%20(for%20testing%20purposes)%0A%20%20%20%20*%20**predict**%3A%20receives%20a%20household%20record%20and%20returns%20a%20boolean%20indicating%20if%20its%20in%20poverty%20risk%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.image(src%3D%22dist%2Fdocs.png%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
8bb2e1cf5d40779d17bdd764a30e67a1