From a90f159b5db008831e0ef9ecf5f02d32956a849c Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 16 May 2023 11:30:31 +0100 Subject: [PATCH 001/229] Renamed all instances of SF_12 to SF_12_MCS, and added the SF_12_PCS variable. Also created a PCS module, and tried to modify the outcome visualisation stuff but made some mistakes so still WIP. Handovers work, but cross-validation also still has some issues --- Makefile | 3 + config/default.yaml | 2 +- minos/data_generation/US_format_raw.py | 4 +- minos/longitudnial_visualisation.R | 12 +- minos/minosPipeline/RunPipeline.py | 9 +- minos/modules/alcohol.py | 2 +- minos/modules/housing.py | 2 +- minos/modules/income.py | 2 +- minos/modules/labour.py | 2 +- minos/modules/loneliness.py | 2 +- minos/modules/mental_wellbeing.py | 17 ++- minos/modules/neighbourhood.py | 2 +- minos/modules/nutrition.py | 2 +- minos/modules/physical_wellbeing.py | 130 +++++++++++++++++ minos/modules/replenishment.py | 8 +- minos/modules/replenishment_nowcast.py | 4 +- minos/modules/replenishment_scotland.py | 4 +- minos/modules/tobacco.py | 2 +- minos/outcomes/Makefile | 42 +++--- minos/outcomes/SF12_mean_from_csvs.py | 4 +- minos/outcomes/aggregate_long_stack.py | 2 +- minos/outcomes/aggregate_minos_output.py | 4 +- minos/outcomes/format_KNN_SF12.py | 10 +- minos/outcomes/format_SF12_longitudinal.py | 2 +- minos/outcomes/grand_SF12_geojson.py | 4 +- minos/outcomes/make_lineplot.sh | 8 +- minos/outcomes/minos_SF12_maps.R | 2 +- minos/outcomes/minos_t_tests.R | 20 +-- minos/outcomes/sf12_difference_map.R | 6 +- minos/outcomes/sf12_mean_confusion_matrix.py | 2 +- minos/outcomes/sf12_single_map.R | 2 +- minos/transitions/estimate_transitions.R | 10 +- minos/transitions/model_definitions.txt | 9 +- minos/utils_datain.R | 4 +- minos/utils_outcome_vis.R | 20 +-- minos/validation/cross_validation.Rmd | 57 ++++++-- .../figure-html/unnamed-chunk-10-1.png | Bin 0 -> 75542 bytes .../figure-html/unnamed-chunk-10-2.png | Bin 0 -> 14690 bytes .../figure-html/unnamed-chunk-4-1.png | Bin 0 -> 79849 bytes .../figure-html/unnamed-chunk-4-2.png | Bin 0 -> 18454 bytes .../figure-html/unnamed-chunk-6-1.png | Bin 0 -> 16641 bytes .../figure-html/unnamed-chunk-6-2.png | Bin 0 -> 18381 bytes .../figure-html/unnamed-chunk-6-3.png | Bin 0 -> 18277 bytes .../figure-html/unnamed-chunk-6-4.png | Bin 0 -> 17121 bytes .../figure-html/unnamed-chunk-6-5.png | Bin 0 -> 17340 bytes .../figure-html/unnamed-chunk-6-6.png | Bin 0 -> 17384 bytes .../figure-html/unnamed-chunk-6-7.png | Bin 0 -> 18396 bytes .../figure-html/unnamed-chunk-7-1.png | Bin 0 -> 66218 bytes .../figure-html/unnamed-chunk-7-2.png | Bin 0 -> 16025 bytes .../figure-html/unnamed-chunk-9-1.png | Bin 0 -> 15827 bytes .../figure-html/unnamed-chunk-9-2.png | Bin 0 -> 17681 bytes .../figure-html/unnamed-chunk-9-3.png | Bin 0 -> 18657 bytes .../figure-html/unnamed-chunk-9-4.png | Bin 0 -> 19002 bytes .../figure-html/unnamed-chunk-9-5.png | Bin 0 -> 19051 bytes .../figure-html/unnamed-chunk-9-6.png | Bin 0 -> 19517 bytes .../figure-html/unnamed-chunk-9-7.png | Bin 0 -> 18315 bytes minos/validation/handovers.Rmd | 132 +++++++++++++----- 57 files changed, 394 insertions(+), 155 deletions(-) create mode 100644 minos/modules/physical_wellbeing.py create mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-10-1.png create mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-10-2.png create mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-4-1.png create mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-4-2.png create mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-6-1.png create mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-6-2.png create mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-6-3.png create mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-6-4.png create mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-6-5.png create mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-6-6.png create mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-6-7.png create mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-7-1.png create mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-7-2.png create mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-9-1.png create mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-9-2.png create mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-9-3.png create mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-9-4.png create mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-9-5.png create mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-9-6.png create mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-9-7.png diff --git a/Makefile b/Makefile index a6edc7ed..5f61d38d 100644 --- a/Makefile +++ b/Makefile @@ -124,6 +124,8 @@ clean_all: clean_data clean_out clean_transitions clean_logs clean_data: ### Remove data files generated in the pipeline clean_data: rm -f data/*/*.csv + rm -f data/*/*/*.csv + rm -f data/*/*/*/*.csv clean_out: ### Remove all output files clean_out: @@ -140,6 +142,7 @@ clean_transitions: rm -rf data/transitions/*/*.rds rm -rf data/transitions/*/*.txt rm -rf data/transitions/*/*/*.rds + rm -rf data/transitions/*/*/*/*.rds rm -rf data/transitions/*/*/*.txt rm -rf data/transitions/scotland/*/*.rds diff --git a/config/default.yaml b/config/default.yaml index 2a66556c..63bfa326 100755 --- a/config/default.yaml +++ b/config/default.yaml @@ -26,7 +26,7 @@ replenishing_dir: 'data/replenishing' # Similarly Mortality requires Replenishment but everything else requires Mortality so it goes second. (priority 1) # Finally everything else can go in any order (priority 2). #components : [MWB(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Income(), Housing(), Labour(), Nutrition(), Education(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] -components : [MWB(), Loneliness(), Tobacco(), Neighbourhood(), Housing(), Nutrition(), Income(), Education(), Mortality(), Replenishment()] +components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Neighbourhood(), Housing(), Nutrition(), Income(), Education(), Mortality(), Replenishment()] scale_rates: diff --git a/minos/data_generation/US_format_raw.py b/minos/data_generation/US_format_raw.py index a5c94744..26f071e1 100755 --- a/minos/data_generation/US_format_raw.py +++ b/minos/data_generation/US_format_raw.py @@ -237,8 +237,8 @@ def format_ukhls_columns(year): 'sclonely': 'loneliness', # is lonely. # sclonely only available in waves 9-11. scsf7 may be a good substitute. 'sex': 'sex', # biological sex. - 'sf12mcs_dv': 'SF_12', # SF12 mental component summary - 'sf12pcs_dv': 'SF_12p', # SF12 physical component summary + 'sf12mcs_dv': 'SF_12_MCS', # SF12 mental component summary + 'sf12pcs_dv': 'SF_12_PCS', # SF12 physical component summary 'smoker': 'smoker', # Currently smokes. # TODO waves present roughly matches ncigs. no data for waves 1-5. # for waves 2 and 5 similar variable 'smnow' could be used. diff --git a/minos/longitudnial_visualisation.R b/minos/longitudnial_visualisation.R index 6414df55..a273a15b 100644 --- a/minos/longitudnial_visualisation.R +++ b/minos/longitudnial_visualisation.R @@ -152,15 +152,15 @@ main <- function() { data4$time <- factor(data4$time) # spaghetti and violin plots over time for SF12 - sf12_violin <- violin_plot(data, "SF_12") - sf12_spag <- spaghetti_plot(data, "SF_12") + sf12_violin <- violin_plot(data, "SF_12_MCS") + sf12_spag <- spaghetti_plot(data, "SF_12_MCS") - sf12_violin2 <- violin_plot(data2, "SF_12") - sf12_spag2 <- spaghetti_plot(data2, "SF_12") + sf12_violin2 <- violin_plot(data2, "SF_12_MCS") + sf12_spag2 <- spaghetti_plot(data2, "SF_12_MCS") # joint plots SF12 for two interventions together with colour groupings. - joint_spaghetti_plot(data, data2, "SF_12", "baseline", "energy poverty") - #joint_violin_plot <- joint_violin_plot(data3, data2, "SF_12", "base", 'energy poverty measures') + joint_spaghetti_plot(data, data2, "SF_12_MCS", "baseline", "energy poverty") + #joint_violin_plot <- joint_violin_plot(data3, data2, "SF_12_MCS", "base", 'energy poverty measures') # Stacked barplots for discrete data. #housing_barplot <- percent_barplot(data, "housing_quality", "Baseline") diff --git a/minos/minosPipeline/RunPipeline.py b/minos/minosPipeline/RunPipeline.py index 77b24da7..9a3bfdeb 100755 --- a/minos/minosPipeline/RunPipeline.py +++ b/minos/minosPipeline/RunPipeline.py @@ -22,6 +22,7 @@ from minos.modules.housing import Housing from minos.modules.income import Income from minos.modules.mental_wellbeing import MWB +from minos.modules.physical_wellbeing import SF_12_PCS from minos.modules.labour import Labour from minos.modules.neighbourhood import Neighbourhood from minos.modules.alcohol import Alcohol @@ -61,8 +62,9 @@ def validate_components(config_components, intervention): # Note priority in vivarium modules supercedes this. two # Outcome module goes first (last in sim) components_map = { - # Outcome module. + # Outcome modules. "MWB()": MWB(), + "SF_12_PCS()": SF_12_PCS(), #Intermediary modules "Tobacco()": Tobacco(), "Alcohol()": Alcohol(), @@ -225,9 +227,6 @@ def RunPipeline(config, intervention=None): print('New children', len(pop[pop['parent_id'] != -1])) logging.info(f"New children: {len(pop[pop['parent_id'] != -1])}") - #for component in components: - # component.plot(pop, config) - return simulation @@ -244,4 +243,4 @@ def get_output_data_filename(config, year=0): # Now add year to output file name output_data_filename += f"{config.time.start.year + year}.csv" - return(output_data_filename) \ No newline at end of file + return output_data_filename diff --git a/minos/modules/alcohol.py b/minos/modules/alcohol.py index 7b5646dc..a6ad78e6 100755 --- a/minos/modules/alcohol.py +++ b/minos/modules/alcohol.py @@ -55,7 +55,7 @@ def setup(self, builder): 'ethnicity', 'region', 'hh_income', - 'SF_12', + 'SF_12_MCS', 'education_state', 'labour_state', 'job_sec', diff --git a/minos/modules/housing.py b/minos/modules/housing.py index 78347716..21a261c6 100755 --- a/minos/modules/housing.py +++ b/minos/modules/housing.py @@ -57,7 +57,7 @@ def setup(self, builder): # transition models and any outputs. view_columns = ["sex", "labour_state", - "SF_12", + "SF_12_MCS", "job_sec", "ethnicity", "age", diff --git a/minos/modules/income.py b/minos/modules/income.py index bf60972e..aa4265f7 100755 --- a/minos/modules/income.py +++ b/minos/modules/income.py @@ -63,7 +63,7 @@ def setup(self, builder): 'job_sec', 'labour_state', 'education_state', - 'SF_12', + 'SF_12_MCS', 'housing_quality', 'job_sector'] #view_columns += self.transition_model.rx2('model').names diff --git a/minos/modules/labour.py b/minos/modules/labour.py index 157c433e..e5ac326f 100755 --- a/minos/modules/labour.py +++ b/minos/modules/labour.py @@ -57,7 +57,7 @@ def setup(self, builder): # transition models and any outputs. view_columns = ["sex", "labour_state", - "SF_12", + "SF_12_MCS", "job_sec", "ethnicity", "age", diff --git a/minos/modules/loneliness.py b/minos/modules/loneliness.py index 3bbdbf2a..522985b3 100755 --- a/minos/modules/loneliness.py +++ b/minos/modules/loneliness.py @@ -49,7 +49,7 @@ def setup(self, builder): # transition models and any outputs. view_columns = ["sex", "labour_state", - "SF_12", + "SF_12_MCS", "job_sec", "ethnicity", "education_state", diff --git a/minos/modules/mental_wellbeing.py b/minos/modules/mental_wellbeing.py index 3bd9b6fd..63839646 100755 --- a/minos/modules/mental_wellbeing.py +++ b/minos/modules/mental_wellbeing.py @@ -1,7 +1,6 @@ """ -Module for income in Minos. -Calculation of monthly household income -Possible extension to interaction with employment/education and any spatial/interaction effects. +Module for SF_12_MCS in Minos. +Calculation of next mental wellbeing state """ import pandas as pd @@ -60,7 +59,7 @@ def setup(self, builder): 'labour_state', 'job_sec', 'hh_income', - 'SF_12', + 'SF_12_MCS', 'housing_quality', 'phealth', 'ncigs', @@ -95,13 +94,13 @@ def on_time_step(self, event): ## Predict next income value newWaveMWB = self.calculate_mwb(pop) - newWaveMWB = pd.DataFrame(newWaveMWB, columns=["SF_12"]) + newWaveMWB = pd.DataFrame(newWaveMWB, columns=["SF_12_MCS"]) # Set index type to int (instead of object as previous) newWaveMWB.index = newWaveMWB.index.astype(int) # Draw individuals next states randomly from this distribution. # Update population with new income - self.population_view.update(newWaveMWB['SF_12']) + self.population_view.update(newWaveMWB['SF_12_MCS']) def calculate_mwb(self, pop): """Calculate income transition distribution based on provided people/indices @@ -115,13 +114,13 @@ def calculate_mwb(self, pop): """ # year can only be 2017 as its the only year with data for all vars year = 2017 - transition_model = r_utils.load_transitions(f"SF_12/ols/SF_12_{year}_{year+1}", self.rpy2Modules, path=self.transition_dir) - return r_utils.predict_next_timestep_ols(transition_model, self.rpy2Modules, pop, 'SF_12') + transition_model = r_utils.load_transitions(f"SF_12_MCS/ols/SF_12_MCS_{year}_{year+1}", self.rpy2Modules, path=self.transition_dir) + return r_utils.predict_next_timestep_ols(transition_model, self.rpy2Modules, pop, 'SF_12_MCS') def plot(self, pop, config): file_name = config.output_plots_dir + f"mwb_hist_{self.year}.pdf" f = plt.figure() - histplot(pop, x = "SF_12", stat='density') + histplot(pop, x = "SF_12_MCS", stat='density') plt.savefig(file_name) plt.close('all') diff --git a/minos/modules/neighbourhood.py b/minos/modules/neighbourhood.py index 83d04d3a..cd1056e1 100755 --- a/minos/modules/neighbourhood.py +++ b/minos/modules/neighbourhood.py @@ -50,7 +50,7 @@ def setup(self, builder): 'region', 'hh_income', 'neighbourhood_safety', - 'SF_12', + 'SF_12_MCS', 'labour_state', 'education_state', 'housing_quality', diff --git a/minos/modules/nutrition.py b/minos/modules/nutrition.py index 4c824b9f..4a61e2db 100755 --- a/minos/modules/nutrition.py +++ b/minos/modules/nutrition.py @@ -46,7 +46,7 @@ def setup(self, builder): 'ethnicity', 'region', 'hh_income', - 'SF_12', + 'SF_12_MCS', 'education_state', 'labour_state', 'job_sec', diff --git a/minos/modules/physical_wellbeing.py b/minos/modules/physical_wellbeing.py new file mode 100644 index 00000000..1d4bd2b2 --- /dev/null +++ b/minos/modules/physical_wellbeing.py @@ -0,0 +1,130 @@ +""" +Module for income in Minos. +Calculation of monthly household income +Possible extension to interaction with employment/education and any spatial/interaction effects. +""" + +import pandas as pd +from pathlib import Path +import minos.modules.r_utils as r_utils +from minos.modules.base_module import Base +from seaborn import histplot +import matplotlib.pyplot as plt + + +class SF_12_PCS(Base): + """Physical Well-Being Module""" + # Special methods used by vivarium. + @property + def name(self): + return 'sf_12_pcs' + + def __repr__(self): + return "SF_12_PCS()" + + def setup(self, builder): + """ Initialise the module during simulation.setup(). + + Notes + ----- + - Load in data from pre_setup + - Register any value producers/modifiers for income + - Add required columns to population data frame + - Update other required items such as randomness stream. + + Parameters + ---------- + builder : vivarium.engine.Builder + Vivarium's control object. Stores all simulation metadata and allows modules to use it. + + """ + + # Load in inputs from pre-setup. + self.rpy2Modules = builder.data.load("rpy2_modules") + + # Build vivarium objects for calculating transition probabilities. + # Typically this is registering rate/lookup tables. See vivarium docs/other modules for examples. + #self.transition_coefficients = builder. + + # Assign randomness streams if necessary. + self.random = builder.randomness.get_stream(self.generate_random_crn_key()) + + # Determine which subset of the main population is used in this module. + # columns_created is the columns created by this module. + # view_columns is the columns from the main population used in this module. + # In this case, view_columns are taken straight from the transition model + view_columns = ['pidp', + 'sex', + 'ethnicity', + 'age', + 'education_state', + 'labour_state', + 'job_sec', + 'hh_income', + 'SF_12_PCS', + 'housing_quality', + 'phealth', + 'ncigs', + 'nutrition_quality', + 'neighbourhood_safety', + 'loneliness'] + + self.population_view = builder.population.get_view(columns=view_columns) + + # Population initialiser. When new individuals are added to the microsimulation a constructer is called for each + # module. Declare what constructer is used. usually on_initialize_simulants method is called. Inidividuals are + # created at the start of a model "setup" or after some deterministic (add cohorts) or random (births) event. + builder.population.initializes_simulants(self.on_initialize_simulants) + + # Declare events in the module. At what times do individuals transition states from this module. E.g. when does + # individual graduate in an education module. + builder.event.register_listener("time_step", self.on_time_step, priority=9) + + def on_time_step(self, event): + """Produces new children and updates parent status on time steps. + + Parameters + ---------- + event : vivarium.population.PopulationEvent + The event time_step that called this function. + """ + + self.year = event.time.year + + # Get living people to update their income + pop = self.population_view.get(event.index, query="alive =='alive'") + + ## Predict next income value + newWavePCS = self.calculate_pcs(pop) + newWavePCS = pd.DataFrame(newWavePCS, columns=["SF_12_PCS"]) + # Set index type to int (instead of object as previous) + newWavePCS.index = newWavePCS.index.astype(int) + + # Draw individuals next states randomly from this distribution. + # Update population with new income + self.population_view.update(newWavePCS['SF_12_PCS']) + + def calculate_pcs(self, pop): + """Calculate income transition distribution based on provided people/indices + + Parameters + ---------- + index : pd.Index + Which individuals to calculate transitions for. + Returns + ------- + """ + # year can only be 2017 as its the only year with data for all vars + year = 2017 + transition_model = r_utils.load_transitions(f"SF_12_PCS/ols/SF_12_PCS_{year}_{year+1}", + self.rpy2Modules, + path=self.transition_dir) + return r_utils.predict_next_timestep_ols(transition_model, self.rpy2Modules, pop, 'SF_12_PCS') + + def plot(self, pop, config): + + file_name = config.output_plots_dir + f"pcs_hist_{self.year}.pdf" + f = plt.figure() + histplot(pop, x="SF_12_PCS", stat='density') + plt.savefig(file_name) + plt.close('all') diff --git a/minos/modules/replenishment.py b/minos/modules/replenishment.py index e4e00de8..2b7f0b20 100755 --- a/minos/modules/replenishment.py +++ b/minos/modules/replenishment.py @@ -53,7 +53,7 @@ def setup(self, builder): 'birth_year', 'nobs', 'region', - 'SF_12', + 'SF_12_MCS', 'hh_int_y', 'hh_int_m', 'Date', @@ -70,7 +70,7 @@ def setup(self, builder): 'max_educ', 'yearly_energy', 'job_sector', - 'SF_12p', + 'SF_12_PCS', 'gross_pay_se', 'nutrition_quality', 'job_hours_se', @@ -265,7 +265,7 @@ def setup(self, builder): 'birth_year', 'nobs', 'region', - 'SF_12', + 'SF_12_MCS', 'hh_int_y', 'hh_int_m', 'Date', @@ -282,7 +282,7 @@ def setup(self, builder): 'max_educ', 'yearly_energy', 'job_sector', - 'SF_12p', + 'SF_12_PCS', 'gross_pay_se', 'nutrition_quality', 'job_hours_se', diff --git a/minos/modules/replenishment_nowcast.py b/minos/modules/replenishment_nowcast.py index 6c032c47..ef99172b 100755 --- a/minos/modules/replenishment_nowcast.py +++ b/minos/modules/replenishment_nowcast.py @@ -44,7 +44,7 @@ def setup(self, builder): 'birth_year', 'nobs', 'region', - 'SF_12', + 'SF_12_MCS', 'hh_int_y', 'hh_int_m', 'Date', @@ -61,7 +61,7 @@ def setup(self, builder): 'max_educ', 'yearly_energy', 'job_sector', - 'SF_12p', + 'SF_12_PCS', 'gross_pay_se', 'nutrition_quality', 'job_hours_se', diff --git a/minos/modules/replenishment_scotland.py b/minos/modules/replenishment_scotland.py index a76874f3..c06508ea 100644 --- a/minos/modules/replenishment_scotland.py +++ b/minos/modules/replenishment_scotland.py @@ -54,7 +54,7 @@ def setup(self, builder): 'birth_year', 'nobs', 'region', - 'SF_12', + 'SF_12_MCS', 'hh_int_y', 'hh_int_m', 'Date', @@ -71,7 +71,7 @@ def setup(self, builder): 'max_educ', 'yearly_energy', 'job_sector', - 'SF_12p', + 'SF_12_PCS', 'gross_pay_se', 'nutrition_quality', 'job_hours_se', diff --git a/minos/modules/tobacco.py b/minos/modules/tobacco.py index 3a704468..47e4bd15 100755 --- a/minos/modules/tobacco.py +++ b/minos/modules/tobacco.py @@ -57,7 +57,7 @@ def setup(self, builder): 'ethnicity', 'region', 'hh_income', - 'SF_12', + 'SF_12_MCS', 'education_state', 'labour_state', 'job_sec', diff --git a/minos/outcomes/Makefile b/minos/outcomes/Makefile index 01d0f388..893f30a4 100644 --- a/minos/outcomes/Makefile +++ b/minos/outcomes/Makefile @@ -31,13 +31,19 @@ default_all_lineplots: all_lineplots all_lineplots: aggregate_minos_output_treated aggregate_minos_output_living_wage aggregate_minos_output_all_child_uplift aggregate_minos_output_poverty_child_uplift aggregate_minos_output_energy vulnerable_groups_lineplots: aggregate_combined_vulnerable aggregate_minos_output_all_child_uplift aggregate_minos_output_disabled_child_uplift aggregate_minos_output_minority_child_uplift aggregate_minos_output_three_child_uplift aggregate_minos_output_young_adult_child_uplift aggregate_minos_output_uneducated_child_uplift +#### PCS PLOTS +default_all_lineplots_PCS: AGG_VAR=SF_12_PCS +default_all_lineplots_PCS: default_all_lineplots + +default_all_lineplots_MCS: AGG_VAR=SF_12_MCS +default_all_lineplots_MCS: default_all_lineplots aggregate_minos_output: aggregate_minos_output: DIRECTORIES = baseline,hhIncomeChildUplift,livingWageIntervention,energyDownlift aggregate_minos_output: DIRECTORY_TAGS = "Baseline,£25 All Child Uplift,Living Wage,Energy Downlift" aggregate_minos_output: SUBSET_FUNCTIONS = "who_alive,who_alive,who_alive,who_alive" aggregate_minos_output: - bash minos/outcomes/make_lineplot.sh $(MODE) $(DIRECTORIES) $(DIRECTORY_TAGS) $(DIRECTORY_SUBSETS) "all" + bash minos/outcomes/make_lineplot.sh $(MODE) $(DIRECTORIES) $(DIRECTORY_TAGS) $(DIRECTORY_SUBSETS) "all" $(AGG_VAR) aggregate_minos_output_treated: MODE=default_config @@ -48,32 +54,32 @@ aggregate_minos_output_treated: bash minos/outcomes/make_lineplot.sh $(MODE) $(TREATED_DIRECTORIES) $(TREATED_DIRECTORY_TAGS) $(TREATED_DIRECTORY_SUBSETS) "treated" aggregate_minos_output_living_wage: - bash minos/outcomes/make_lineplot.sh $(MODE) baseline,livingWageIntervention "Baseline,Living Wage Intervention" who_below_living_wage,who_boosted "living_wage_treated" + bash minos/outcomes/make_lineplot.sh $(MODE) baseline,livingWageIntervention "Baseline,Living Wage Intervention" who_below_living_wage,who_boosted "living_wage_treated" $(AGG_VAR) aggregate_minos_output_poverty_child_uplift: - bash minos/outcomes/make_lineplot.sh $(MODE) baseline,hhIncomePovertyLineChildUplift "Baseline,Poverty Line Uplift" who_below_poverty_line_and_kids,who_boosted "poverty_line_child_uplift" + bash minos/outcomes/make_lineplot.sh $(MODE) baseline,hhIncomePovertyLineChildUplift "Baseline,Poverty Line Uplift" who_below_poverty_line_and_kids,who_boosted "poverty_line_child_uplift" $(AGG_VAR) aggregate_minos_output_all_child_uplift: - bash minos/outcomes/make_lineplot.sh $(MODE) baseline,hhIncomeChildUplift "Baseline,All Children Uplift" who_kids,who_kids "all_child_uplift" + bash minos/outcomes/make_lineplot.sh $(MODE) baseline,hhIncomeChildUplift "Baseline,All Children Uplift" who_kids,who_kids "all_child_uplift" $(AGG_VAR) aggregate_minos_output_disabled_child_uplift: - bash minos/outcomes/make_lineplot.sh $(MODE) baseline,hhIncomeChildUplift "Baseline,Disabled Children Uplift" who_disabled,who_disabled "disabled_child_uplift" + bash minos/outcomes/make_lineplot.sh $(MODE) baseline,hhIncomeChildUplift "Baseline,Disabled Children Uplift" who_disabled,who_disabled "disabled_child_uplift" $(AGG_VAR) aggregate_minos_output_minority_child_uplift: - bash minos/outcomes/make_lineplot.sh $(MODE) baseline,hhIncomeChildUplift "Baseline,Minority Children Uplift" who_ethnic_minority,who_ethnic_minority "minority_child_uplift" + bash minos/outcomes/make_lineplot.sh $(MODE) baseline,hhIncomeChildUplift "Baseline,Minority Children Uplift" who_ethnic_minority,who_ethnic_minority "minority_child_uplift" $(AGG_VAR) aggregate_minos_output_three_child_uplift: - bash minos/outcomes/make_lineplot.sh $(MODE) baseline,hhIncomeChildUplift "Baseline,Three Children Uplift" who_three_kids,who_three_kids "three_child_uplift" + bash minos/outcomes/make_lineplot.sh $(MODE) baseline,hhIncomeChildUplift "Baseline,Three Children Uplift" who_three_kids,who_three_kids "three_child_uplift" $(AGG_VAR) # omitted due to lack of sample size aggregate_minos_output_young_mother_child_uplift: - bash minos/outcomes/make_lineplot.sh $(MODE) baseline,hhIncomeChildUplift "Baseline,Young Mother Children Uplift" who_young_mother,who_young_mother "young_mother_child_uplift" + bash minos/outcomes/make_lineplot.sh $(MODE) baseline,hhIncomeChildUplift "Baseline,Young Mother Children Uplift" who_young_mother,who_young_mother "young_mother_child_uplift" $(AGG_VAR) aggregate_minos_output_young_adult_child_uplift: - bash minos/outcomes/make_lineplot.sh $(MODE) baseline,hhIncomeChildUplift "Baseline,Young Single Parent Children Uplift" who_young_adults,who_young_adults "young_adult_child_uplift" + bash minos/outcomes/make_lineplot.sh $(MODE) baseline,hhIncomeChildUplift "Baseline,Young Single Parent Children Uplift" who_young_adults,who_young_adults "young_adult_child_uplift" $(AGG_VAR) aggregate_minos_output_uneducated_child_uplift: - bash minos/outcomes/make_lineplot.sh $(MODE) baseline,hhIncomeChildUplift "Baseline,Low Education Children Uplift" who_no_formal_education,who_no_formal_education "low_education_child_uplift" + bash minos/outcomes/make_lineplot.sh $(MODE) baseline,hhIncomeChildUplift "Baseline,Low Education Children Uplift" who_no_formal_education,who_no_formal_education "low_education_child_uplift" $(AGG_VAR) aggregate_minos_output_scottish: MODE=default_config @@ -81,19 +87,19 @@ aggregate_minos_output_scottish: DIRECTORIES=baseline,hhIncomeChildUplift,living aggregate_minos_output_scottish: DIRECTORY_TAGS="Baseline,£25 All Child Uplift,Living Wage,Energy Downlift" aggregate_minos_output_scottish: SUBSET_FUNCTIONS=who_scottish,who_scottish,who_scottish,who_scottish aggregate_minos_output_scottish: - bash minos/outcomes/make_lineplot.sh $(MODE) $(DIRECTORIES) $(DIRECTORY_TAGS) $(SUBSET_FUNCTIONS) "who_scottish?" + bash minos/outcomes/make_lineplot.sh $(MODE) $(DIRECTORIES) $(DIRECTORY_TAGS) $(SUBSET_FUNCTIONS) "who_scottish?" $(AGG_VAR) aggregate_minos_output_living_wage_scot: - bash minos/outcomes/make_lineplot.sh $(MODE) baseline,livingWageIntervention "Baseline,Living Wage Intervention" who_scottish,who_scottish "living_wage_treated" + bash minos/outcomes/make_lineplot.sh $(MODE) baseline,livingWageIntervention "Baseline,Living Wage Intervention" who_scottish,who_scottish "living_wage_treated" $(AGG_VAR) aggregate_minos_output_all_child_uplift_scot: - bash minos/outcomes/make_lineplot.sh $(MODE) baseline,hhIncomeChildUplift "Baseline,All Children Uplift" who_scottish,who_scottish "all_child_uplift" + bash minos/outcomes/make_lineplot.sh $(MODE) baseline,hhIncomeChildUplift "Baseline,All Children Uplift" who_scottish,who_scottish "all_child_uplift" $(AGG_VAR) aggregate_minos_output_poverty_child_uplift_scot: - bash minos/outcomes/make_lineplot.sh $(MODE) baseline,hhIncomePovertyLineChildUplift "Baseline,Poverty Line Uplift" who_scottish,who_scottish "poverty_line_child_uplift" + bash minos/outcomes/make_lineplot.sh $(MODE) baseline,hhIncomePovertyLineChildUplift "Baseline,Poverty Line Uplift" who_scottish,who_scottish "poverty_line_child_uplift" $(AGG_VAR) aggregate_minos_output_energy_scot: - bash minos/outcomes/make_lineplot.sh $(MODE) baseline,energyDownlift "Baseline,Energy Downlift" who_scottish,who_scottish "energy_downlift" + bash minos/outcomes/make_lineplot.sh $(MODE) baseline,energyDownlift "Baseline,Energy Downlift" who_scottish,who_scottish "energy_downlift" $(AGG_VAR) lineplots_national_scottish_subsample: aggregate_minos_output_living_wage_scot aggregate_minos_output_all_child_uplift_scot aggregate_minos_output_poverty_child_uplift_scot aggregate_minos_output_energy_scot aggregate_minos_output_scottish @@ -102,10 +108,10 @@ aggregate_combined_vulnerable:VULNERABLE_DIRECTORIES=baseline,hhIncomeChildUplif aggregate_combined_vulnerable:VULNERABLE_DIRECTORY_TAGS="Baseline,All Children Uplift,Disabled Children Uplift,Minority Children Uplift,Three Children Uplift,Young Single Parent Children Uplift,Low Education Children Uplift" aggregate_combined_vulnerable:VULNERABLE_DIRECTORY_SUBSETS=who_alive,who_kids,who_disabled,who_ethnic_minority,who_three_kids,who_young_adults,who_no_formal_education aggregate_combined_vulnerable: - bash minos/outcomes/make_lineplot.sh $(MODE) $(VULNERABLE_DIRECTORIES) $(VULNERABLE_DIRECTORY_TAGS) $(VULNERABLE_DIRECTORY_SUBSETS) "all_vulnerable" + bash minos/outcomes/make_lineplot.sh $(MODE) $(VULNERABLE_DIRECTORIES) $(VULNERABLE_DIRECTORY_TAGS) $(VULNERABLE_DIRECTORY_SUBSETS) "all_vulnerable" $(AGG_VAR) aggregate_minos_output_energy: - bash minos/outcomes/make_lineplot.sh $(MODE) baseline,energyDownlift "Baseline,Energy Downlift" who_alive,who_alive "energy_downlift" + bash minos/outcomes/make_lineplot.sh $(MODE) baseline,energyDownlift "Baseline,Energy Downlift" who_alive,who_alive "energy_downlift" $(AGG_VAR) @@ -128,7 +134,7 @@ OUT_POVERTYUP = output/$(DEFAULT_OUTPUT_SUBDIRECTORY)/hhIncomePovertyLineChildUp SPATIAL_DIRECTORY1 = output/$(DEFAULT_OUTPUT_SUBDIRECTORY)/$(INTERVENTION1)# first geojson for comparison in diff plot. SPATIAL_DIRECTORY2 = output/$(DEFAULT_OUTPUT_SUBDIRECTORY)/$(INTERVENTION2)# second geojson for comparison in aggregate_two_and_diff AGG_METHOD = nanmean# what method to aggregate with. -AGG_VARIABLE = SF_12# what variable to aggregate. +AGG_VARIABLE = SF_12_MCS# what variable to aggregate. AGG_YEAR = 2025# what year to map in data. AGG_LOCATION = scotland # or manchester/scotland/sheffield SAVE_FILE1 = $(SPATIAL_DIRECTORY1)/$(AGG_METHOD)_$(AGG_VARIABLE)_$(AGG_YEAR).geojson # data source from aggregation. Need to automate these file paths somehow. diff --git a/minos/outcomes/SF12_mean_from_csvs.py b/minos/outcomes/SF12_mean_from_csvs.py index 2bf22900..ced101da 100755 --- a/minos/outcomes/SF12_mean_from_csvs.py +++ b/minos/outcomes/SF12_mean_from_csvs.py @@ -10,11 +10,11 @@ def get_SF12_mean(file_names, year, source): for file in file_names: #print(file) data = pd.read_csv(file) - mean = np.nanmean(data['SF_12']) + mean = np.nanmean(data['SF_12_MCS']) #print(mean) means.append(mean) - means = pd.DataFrame(means, columns=["SF_12"]) + means = pd.DataFrame(means, columns=["SF_12_MCS"]) print(year, means) out_filename = source + f"means_{year}.csv" means.to_csv(out_filename) diff --git a/minos/outcomes/aggregate_long_stack.py b/minos/outcomes/aggregate_long_stack.py index 30361b6d..42a43e9f 100755 --- a/minos/outcomes/aggregate_long_stack.py +++ b/minos/outcomes/aggregate_long_stack.py @@ -73,7 +73,7 @@ def relative_scaling(df, v, ref): return df -def main(output_dir, source_directories, tags, v="SF_12", method="nanmean", destination = None, ref=None): +def main(output_dir, source_directories, tags, v="SF_12_MCS", method="nanmean", destination = None, ref=None): """ Parameters diff --git a/minos/outcomes/aggregate_minos_output.py b/minos/outcomes/aggregate_minos_output.py index b9437a67..bec68625 100755 --- a/minos/outcomes/aggregate_minos_output.py +++ b/minos/outcomes/aggregate_minos_output.py @@ -87,7 +87,7 @@ def main(source, mode, years, tag, v, method, subset_function_string): tag: list Corresponding name of the MINOS batch source. Usually what intervention was used. Baseline Uplift, etc.. v: str - What variable to aggregate on. Defaults to SF_12 + What variable to aggregate on. Defaults to SF_12_MCS method: func What function to aggregate over. Default np.nanmean. destination: str @@ -115,7 +115,7 @@ def main(source, mode, years, tag, v, method, subset_function_string): help="Subdirectories within source that are aggregated. Usually experiment names baseline childUplift etc.") parser.add_argument("-t", "--tags", required=True, type=str, help="Corresponding name tags for which data is being processed. I.E which intervention Baseline/£20 Uplift etc. Used as label in later plots.") - parser.add_argument("-v", "--variable", required=False, type=str, default='SF_12', + parser.add_argument("-v", "--variable", required=False, type=str, default='SF_12_MCS', help="What variable from Minos is being aggregated. Defaults to SF12.") parser.add_argument("-a", "--aggregate_method", required=False, type=str, default="nanmean", help="What method is used to aggregate population. Defaults to np.nanmean.") diff --git a/minos/outcomes/format_KNN_SF12.py b/minos/outcomes/format_KNN_SF12.py index eb11b9a3..2da2a423 100755 --- a/minos/outcomes/format_KNN_SF12.py +++ b/minos/outcomes/format_KNN_SF12.py @@ -24,18 +24,18 @@ def main(year, params, param_names, source): SF12_data = pd.read_csv("output/livingWage/baseline_vs_livingWage.csv") spatial_data = spatial_data.merge(SF12_data, how='left') - # generate new SF_12 column that is living wage if in cluster 5 and baseline otherwise. - spatial_data["SF_12"] = spatial_data["baseline"] - spatial_data["SF_12"] = spatial_data["lwage"] + # generate new SF_12_MCS column that is living wage if in cluster 5 and baseline otherwise. + spatial_data["SF_12_MCS"] = spatial_data["baseline"] + spatial_data["SF_12_MCS"] = spatial_data["lwage"] # TODO make this clearer as a function. # OMIT ME IF NO INCLUSION OF CLUSTER DIFFERENCE. - #spatial_data.loc[spatial_data['Cluster'] == 5, "SF_12"] = spatial_data.loc[spatial_data['Cluster'] == 5, "lwage"] + #spatial_data.loc[spatial_data['Cluster'] == 5, "SF_12_MCS"] = spatial_data.loc[spatial_data['Cluster'] == 5, "lwage"] spatial_data.drop(labels=['baseline', 'lwage'], axis=1, inplace=True) # group by lsoa and take SF12 mean. - spatial_data = spatial_data.groupby("ZoneID", as_index=False)[["Cluster", "SF_12"]].mean() + spatial_data = spatial_data.groupby("ZoneID", as_index=False)[["Cluster", "SF_12_MCS"]].mean() # save to useful data. sf12_dict = defaultdict(int, zip(spatial_data["ZoneID"], spatial_data["SF_12"])) diff --git a/minos/outcomes/format_SF12_longitudinal.py b/minos/outcomes/format_SF12_longitudinal.py index 6db7c095..b7c5ab73 100755 --- a/minos/outcomes/format_SF12_longitudinal.py +++ b/minos/outcomes/format_SF12_longitudinal.py @@ -134,7 +134,7 @@ def relative_scaling(df, years, v, ref): year_df[v] /= x_bar df.loc[df['year'] == year, v] = year_df[v] return df -def main(sources, years, labels, v="SF_12", destination="plots/", agger=np.nanmean, ref=None): +def main(sources, years, labels, v="SF_12_MCS", destination="plots/", agger=np.nanmean, ref=None): """ Parameters diff --git a/minos/outcomes/grand_SF12_geojson.py b/minos/outcomes/grand_SF12_geojson.py index e453f16c..ae03343b 100755 --- a/minos/outcomes/grand_SF12_geojson.py +++ b/minos/outcomes/grand_SF12_geojson.py @@ -62,7 +62,7 @@ def main(source, spatial_source, year, params, param_names): print(US_data.shape) # subset US data. grab common pidps to prevent NA errors. spatial_data2 = spatial_data.loc[spatial_data["pidp"].isin(US_data["pidp"]),] - US_data2 = US_data.loc[US_data["pidp"].isin(spatial_data2["pidp"]),["pidp", "SF_12", 'hidp', "boost_amount"]] + US_data2 = US_data.loc[US_data["pidp"].isin(spatial_data2["pidp"]),["pidp", "SF_12_MCS", 'hidp', "boost_amount"]] US_data2 = US_data2.groupby("pidp", as_index=False).mean() # left merge US data into spatial data. @@ -75,7 +75,7 @@ def main(source, spatial_source, year, params, param_names): spatial_data3.reset_index(drop=True) # default dict assigns missing values to 0. prevents key errors later for any LSOA missing a value. - spatial_dict = defaultdict(int, zip(spatial_data3["ZoneID"], spatial_data3["SF_12"])) + spatial_dict = defaultdict(int, zip(spatial_data3["ZoneID"], spatial_data3["SF_12_MCS"])) print("merger done.") # load in geojson map data from ONS. diff --git a/minos/outcomes/make_lineplot.sh b/minos/outcomes/make_lineplot.sh index 11712731..af7a6dbe 100644 --- a/minos/outcomes/make_lineplot.sh +++ b/minos/outcomes/make_lineplot.sh @@ -46,10 +46,10 @@ while getopts ":h" option; do done AGG_METHOD="nanmean" -AGG_VAR="SF_12" +#AGG_VAR="SF_12_MCS" # custom baseline for living wage only. -python3 minos/outcomes/aggregate_minos_output.py -m "$1" -d "$2" -t "$3" -a $AGG_METHOD -v $AGG_VAR -f "$4" +python3 minos/outcomes/aggregate_minos_output.py -m "$1" -d "$2" -t "$3" -a $AGG_METHOD -f "$4" -v "$6" # stack aggregated files into one long array. -python3 minos/outcomes/aggregate_long_stack.py -m "$1" -s "$2" -t "$3" -r Baseline -v $AGG_VAR -a $AGG_METHOD +python3 minos/outcomes/aggregate_long_stack.py -m "$1" -s "$2" -t "$3" -r Baseline -a $AGG_METHOD -v "$6" # make line plot. -python3 minos/outcomes/aggregate_lineplot.py -m "$1" -s "$2" -v $AGG_VAR -d "plots" -a $AGG_METHOD -p "$5" \ No newline at end of file +python3 minos/outcomes/aggregate_lineplot.py -m "$1" -s "$2" -d "plots" -a $AGG_METHOD -p "$5" -v "$6" \ No newline at end of file diff --git a/minos/outcomes/minos_SF12_maps.R b/minos/outcomes/minos_SF12_maps.R index 600ff922..0d45c23f 100755 --- a/minos/outcomes/minos_SF12_maps.R +++ b/minos/outcomes/minos_SF12_maps.R @@ -146,7 +146,7 @@ main.single <- function(geojson_file_name, plot_destination_file, mode, v){ lsoa_subset_function <- choose_lsoa_function(mode) data <- load_geojson(geojson_file_name) data <- subset_geojson(data, lsoa_subset_function) - #data[which(data$SF_12 %in% head(sort(data$SF_12), 10)),] # ten worst performing areas by SF12. + #data[which(data$SF_12_MCS %in% head(sort(data$SF_12), 10)),] # ten worst performing areas by SF12. minos_map(data, v, plot_destination_file) } diff --git a/minos/outcomes/minos_t_tests.R b/minos/outcomes/minos_t_tests.R index b90d1c30..9d495ea7 100755 --- a/minos/outcomes/minos_t_tests.R +++ b/minos/outcomes/minos_t_tests.R @@ -44,7 +44,7 @@ minos.split.hist2 <- function(d1, d2, var, labels, cols, title){ yupper <- max(max(hgA$counts), max(hgB$counts)) - plot(hgA, col=cols[1], xlab="SF_12", xlim=c(20,80), ylim = c(0, yupper+0.005), ylab="Density", main=title) + plot(hgA, col=cols[1], xlab="SF_12_MCS", xlim=c(20,80), ylim = c(0, yupper+0.005), ylab="Density", main=title) plot(hgB, col=cols[2], add = T) legend("topleft", labels, fill=cols) } @@ -66,13 +66,13 @@ minos.split.hist3 <- function(d1, d2, d3, var, labels, cols, title){ hgC <- hist(v3, breaks=ax, plot=F) hgC$counts <- hgC$counts/sum(hgC$counts) yupper <- max(max(hgA$counts), max(hgB$counts), max(hgC$counts)) - plot(hgA, col=cols[1], xlab="SF_12", xlim=c(20,80), ylab="Density", ylim = c(0, yupper+0.005), main=title) + plot(hgA, col=cols[1], xlab="SF_12_MCS", xlim=c(20,80), ylab="Density", ylim = c(0, yupper+0.005), main=title) plot(hgB, col=cols[2], add = T) plot(hgC, col=cols[3], add = T) legend("topleft", labels, fill=cols) } -test_title <- "Projected vs Real SF_12 distribution for 2016 UK Population." +test_title <- "Projected vs Real SF_12_MCS distribution for 2016 UK Population." main <- function(year){ real_source <- 'data/final_US/' @@ -83,20 +83,20 @@ main <- function(year){ d1 <- read.csv('data/final_US/2016_US_Cohort.csv') d2 <- read.csv('output/baseline/run_id_0_2016.csv') - minos.t.test(d1, d2, "SF_12") - minos.split.hist2(d1, d2, "SF_12", c("Real", "Minos"), c(c1, c2), "Minos SF12 Prediction VS Real Data.") + minos.t.test(d1, d2, "SF_12_MCS") + minos.split.hist2(d1, d2, "SF_12_MCS", c("Real", "Minos"), c(c1, c2), "Minos SF12 Prediction VS Real Data.") minos_baseline<-get.sf12.data(real_source, 'output/baseline', year)$d2 minos_poverty<-get.sf12.data(real_source, 'output/povertyUplift', year)$d2 minos_all<-get.sf12.data(real_source, 'output/childUplift', year)$d2 - minos.t.test(minos0, minos20, "SF_12") - minos.t.test(minos20, minos100, "SF_12") + minos.t.test(minos0, minos20, "SF_12_MCS") + minos.t.test(minos20, minos100, "SF_12_MCS") - minos.split.hist2(minos0, minos20, "SF_12", c("0", "20"), c(c1, c2), "£0 vs £20 Uplift") - minos.split.hist2(minos0, minos100, "SF_12", c("0", "100"), c(c1, c3), "£0 vs £100 Uplift") - minos.split.hist3(minos0, minos20, minos100, "SF_12", c("0", "20", "100"), c(c1, c2, c3), "All Three Income Strategies") + minos.split.hist2(minos0, minos20, "SF_12_MCS", c("0", "20"), c(c1, c2), "£0 vs £20 Uplift") + minos.split.hist2(minos0, minos100, "SF_12_MCS", c("0", "100"), c(c1, c3), "£0 vs £100 Uplift") + minos.split.hist3(minos0, minos20, minos100, "SF_12_MCS", c("0", "20", "100"), c(c1, c2, c3), "All Three Income Strategies") } main(2016) \ No newline at end of file diff --git a/minos/outcomes/sf12_difference_map.R b/minos/outcomes/sf12_difference_map.R index 39891001..b6d8c107 100755 --- a/minos/outcomes/sf12_difference_map.R +++ b/minos/outcomes/sf12_difference_map.R @@ -78,8 +78,8 @@ main <- function(){ } main() -#main.diff("output/baseline/nanmean_SF_12_2018.geojson", -# "output/povertyUplift/nanmean_SF_12_2018.geojson", +#main.diff("output/baseline/nanmean_SF_12_MCS_2018.geojson", +# "output/povertyUplift/nanmean_SF_12_MCS_2018.geojson", # "output/baseline/sf12_baseline_povertyUplift_diff_map.pdf", # "sheffield", -# "SF_12") \ No newline at end of file +# "SF_12_MCS") \ No newline at end of file diff --git a/minos/outcomes/sf12_mean_confusion_matrix.py b/minos/outcomes/sf12_mean_confusion_matrix.py index 14da4d12..34ad15be 100755 --- a/minos/outcomes/sf12_mean_confusion_matrix.py +++ b/minos/outcomes/sf12_mean_confusion_matrix.py @@ -18,7 +18,7 @@ def get_means_dataframe(params, year): if p[0] not in out.keys(): out[p[0]] = {} try: - out[p[0]][p[1]] = np.nanmean(pd.read_csv(file_name)["SF_12"]) + out[p[0]][p[1]] = np.nanmean(pd.read_csv(file_name)["SF_12_MCS"]) except: out[p[0]][p[1]] = np.nanmean(pd.read_csv(file_name)["0"]) diff --git a/minos/outcomes/sf12_single_map.R b/minos/outcomes/sf12_single_map.R index 8c35dec6..730abd0c 100644 --- a/minos/outcomes/sf12_single_map.R +++ b/minos/outcomes/sf12_single_map.R @@ -48,4 +48,4 @@ main <- function(){ } main() -#main.single("output/baseline/2016.geojson", "output/baseline/scotland_sf12_map.pdf", "scotland", "SF_12") +#main.single("output/baseline/2016.geojson", "output/baseline/scotland_sf12_map.pdf", "scotland", "SF_12_MCS") diff --git a/minos/transitions/estimate_transitions.R b/minos/transitions/estimate_transitions.R index 2f2d95db..26957239 100644 --- a/minos/transitions/estimate_transitions.R +++ b/minos/transitions/estimate_transitions.R @@ -121,8 +121,8 @@ estimate_yearly_zip <- function(data, formula, include_weights = FALSE, depend) if(depend == 'next_ncigs' | depend == 'ncigs') { # first subset just the columns we want - cols <- c('pidp', depend, 'age', 'sex', 'education_state', 'SF_12', 'job_sec', - 'hh_income', 'ethnicity', 'weight') + cols <- c('pidp', depend, 'age', 'sex', 'education_state', 'SF_12_MCS', 'job_sec', + 'hh_income', 'ethnicity', 'weight', 'SF_12_PCS') dat.subset <- data[, cols] # Replace missing values with NA (util func) @@ -236,7 +236,7 @@ run_yearly_models <- function(transitionDir_path, if(dependent == 'ncigs' & year < 2013) { next } #TODO: Maybe copy values from wave 2 onto wave 1? Assuming physical health changes slowly? # SF_12 predictor (physical health score) not available in wave 1 - if(dependent == 'SF_12' & year == 2009) { next } + if(dependent %in% c('SF_12_MCS', 'SF_12_PCS') & year == 2009) { next } print(paste0('Starting estimation for ', dependent, ' in ', year)) @@ -267,10 +267,10 @@ run_yearly_models <- function(transitionDir_path, } #print(formula.string) - ## For the SF_12 model alone, we need to modify the formula on the fly + ## For the SF_12 models (MCS & PCS), we need to modify the formula on the fly # as neighbourhood_safety, loneliness, nutrition_quality and ncigs are # not present every year - if(dependent == 'SF_12') { + if(dependent %in% c('SF_12_MCS', 'SF_12_PCS')) { if(!year %in% c(2011, 2014, 2017, 2020)) { formula.string <- str_remove(formula.string, " \\+ factor\\(neighbourhood_safety\\)") } diff --git a/minos/transitions/model_definitions.txt b/minos/transitions/model_definitions.txt index d8e66139..dc033f9e 100644 --- a/minos/transitions/model_definitions.txt +++ b/minos/transitions/model_definitions.txt @@ -1,8 +1,9 @@ OLS : hh_income ~ hh_income + age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = "1") + relevel(factor(job_sec), ref = '3') + relevel(factor(job_sector), ref = '1') OLS : nutrition_quality ~ age + factor(sex) + relevel(factor(education_state), ref = '3') + relevel(factor(ethnicity), ref = 'WBI') + hh_income + ncigs -OLS : SF_12 ~ age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + hh_income + factor(housing_quality) + phealth + factor(neighbourhood_safety) + ncigs + nutrition_quality + SF_12 -CLM : housing_quality ~ age + factor(sex) + SF_12 + relevel(factor(ethnicity), ref = 'WBI') + hh_income -CLM : loneliness ~ age + factor(sex) + SF_12 + relevel(factor(education_state), ref = '3') + relevel(factor(job_sec), ref = '3') + hh_income + relevel(factor(hh_comp), ref = '3') + relevel(factor(marital_status), ref = 'Partnered') + relevel(factor(ethnicity), ref = 'WBI') +OLS : SF_12_MCS ~ age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + hh_income + factor(housing_quality) + phealth + factor(neighbourhood_safety) + ncigs + nutrition_quality + SF_12_MCS +OLS : SF_12_PCS ~ age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + hh_income + factor(housing_quality) + factor(neighbourhood_safety) + ncigs + nutrition_quality + SF_12_PCS +CLM : housing_quality ~ age + factor(sex) + SF_12_MCS + relevel(factor(ethnicity), ref = 'WBI') + hh_income +CLM : loneliness ~ age + factor(sex) + SF_12_MCS + relevel(factor(education_state), ref = '3') + relevel(factor(job_sec), ref = '3') + hh_income + relevel(factor(hh_comp), ref = '3') + relevel(factor(marital_status), ref = 'Partnered') + relevel(factor(ethnicity), ref = 'WBI') CLM : neighbourhood_safety ~ age + factor(sex) + factor(job_sec) + relevel(factor(ethnicity), ref = 'WBI') + hh_income + factor(housing_quality) + relevel(factor(region), ref = 'South East') NNET : education_state ~ factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') -ZIP : ncigs ~ age + factor(sex) + relevel(factor(education_state), ref = '3') + SF_12 + relevel(factor(job_sec), ref = '3') + hh_income + relevel(factor(ethnicity), ref = 'WBI') | relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(job_sec), ref = '3') + hh_income + SF_12 +ZIP : ncigs ~ age + factor(sex) + relevel(factor(education_state), ref = '3') + SF_12_MCS + relevel(factor(job_sec), ref = '3') + hh_income + relevel(factor(ethnicity), ref = 'WBI') | relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(job_sec), ref = '3') + hh_income + SF_12_MCS diff --git a/minos/utils_datain.R b/minos/utils_datain.R index 2d8877c6..a0b2f211 100644 --- a/minos/utils_datain.R +++ b/minos/utils_datain.R @@ -125,7 +125,7 @@ collapse_multiple_out_to_summary <- function(big.out) { grouped <- big.out %>% group_by(pidp) %>% - summarise(SF_12 = mean(SF_12), + summarise(SF_12_MCS = mean(SF_12_MCS), hh_income = mean(hh_income), housing_quality = median(housing_quality), neighbourhood_safety = median(neighbourhood_safety), @@ -144,7 +144,7 @@ get_summary_out <- function(scenario_out_path, year, var.list) { grouped <- big.out %>% group_by(pidp) %>% - summarise(SF_12 = mean(SF_12), + summarise(SF_12_MCS = mean(SF_12_MCS), hh_income = mean(hh_income), housing_quality = median(housing_quality), neighbourhood_safety = median(neighbourhood_safety), diff --git a/minos/utils_outcome_vis.R b/minos/utils_outcome_vis.R index 2ce9cace..df5044a9 100644 --- a/minos/utils_outcome_vis.R +++ b/minos/utils_outcome_vis.R @@ -28,13 +28,13 @@ SF12_marg_dist_densigram_plot <- function(base, save = FALSE, save.path = '/home/luke/Documents/WORK/MINOS/TEST_PLOTS/') { # get just one year - b.start <- base %>% filter(time == 2020, SF_12 != -8.0) - b.end <- base %>% filter(time == target.year, SF_12 != -8.0) - i.end <- int %>% filter(time == target.year, SF_12 != -8.0) + b.start <- base %>% filter(time == 2020, SF_12_MCS != -8.0) + b.end <- base %>% filter(time == target.year, SF_12_MCS != -8.0) + i.end <- int %>% filter(time == target.year, SF_12_MCS != -8.0) # get just the SF12 columns and rename for later - b.start <- b.start %>% select(pidp, SF_12) %>% rename(initial = SF_12) - b.end <- b.end %>% select(pidp, SF_12) %>% rename(end = SF_12) - i.end <- i.end %>% select(pidp, SF_12) %>% rename(end = SF_12) + b.start <- b.start %>% select(pidp, SF_12_MCS) %>% rename(initial = SF_12_MCS) + b.end <- b.end %>% select(pidp, SF_12_MCS) %>% rename(end = SF_12_MCS) + i.end <- i.end %>% select(pidp, SF_12_MCS) %>% rename(end = SF_12_MCS) # combine before we plot b.end$scen <- 'baseline' @@ -74,11 +74,11 @@ SF12_marg_dist_densigram_plot_oneyear <- function(base, save = FALSE, save.path = '/home/luke/Documents/WORK/MINOS/TEST_PLOTS/') { # get just one year - b.end <- base %>% filter(time == target.year, SF_12 != -8.0) - i.end <- int %>% filter(time == target.year, SF_12 != -8.0) + b.end <- base %>% filter(time == target.year, SF_12_MCS != -8.0) + i.end <- int %>% filter(time == target.year, SF_12_MCS != -8.0) # get just the SF12 columns and rename for later - b.end <- b.end %>% select(pidp, SF_12) %>% rename(baseline = SF_12) - i.end <- i.end %>% select(pidp, SF_12) %>% rename(intervention = SF_12) + b.end <- b.end %>% select(pidp, SF_12_MCS) %>% rename(baseline = SF_12_MCS) + i.end <- i.end %>% select(pidp, SF_12_MCS) %>% rename(intervention = SF_12_MCS) # combine before we plot combined <- merge(b.end, i.end, by = 'pidp') diff --git a/minos/validation/cross_validation.Rmd b/minos/validation/cross_validation.Rmd index e6e23d1b..44234e92 100644 --- a/minos/validation/cross_validation.Rmd +++ b/minos/validation/cross_validation.Rmd @@ -95,23 +95,23 @@ yearly_box_plots(pivoted.df = income.pivoted, subset.min = -5000) ``` -## SF12 +## SF12_MCS ```{r} -sf12.pivoted <- combine_and_pivot_long(df1 = cv, +sf12.m.pivoted <- combine_and_pivot_long(df1 = cv, df1.name = 'simulated', df2 = raw, df2.name = 'raw', - var = 'SF_12') + var = 'SF_12_MCS') -plot_mean_comparison(pivoted.df = sf12.pivoted, - var = 'SF_12', +plot_mean_comparison(pivoted.df = sf12.m.pivoted, + var = 'SF_12_MCS', group.var = 'scenario', save = TRUE, save.path = '/home/luke/Documents/WORK/MINOS/VALIDATION_PLOTS/Cross-Validation/') -compare_boxplot_long(pivoted.df = sf12.pivoted, - var = 'SF_12', +compare_boxplot_long(pivoted.df = sf12.m.pivoted, + var = 'SF_12_MCS', scen1 = 'raw', scen2 = 'simulation') ``` @@ -119,13 +119,48 @@ compare_boxplot_long(pivoted.df = sf12.pivoted, ### T-Tests ```{r} -sf12.t.tests <- two_sample_t_test(cv, 'cv', raw, 'raw', 'hh_income') -sf12.t.tests +sf12.m.t.tests <- two_sample_t_test(cv, 'cv', raw, 'raw', 'SF_12_MCS') +sf12.m.t.tests ``` ```{r} -yearly_box_plots(pivoted.df = sf12.pivoted, - var = 'SF_12', +yearly_box_plots(pivoted.df = sf12.m.pivoted, + var = 'SF_12_MCS', + scen1 = 'raw', + scen2 = 'simulation') +``` + +## SF_12_PCS + +```{r} +sf12.p.pivoted <- combine_and_pivot_long(df1 = cv, + df1.name = 'simulated', + df2 = raw, + df2.name = 'raw', + var = 'SF_12_PCS') + +plot_mean_comparison(pivoted.df = sf12.p.pivoted, + var = 'SF_12_PCS', + group.var = 'scenario', + save = TRUE, + save.path = '/home/luke/Documents/WORK/MINOS/VALIDATION_PLOTS/Cross-Validation/') + +compare_boxplot_long(pivoted.df = sf12.p.pivoted, + var = 'SF_12_PCS', + scen1 = 'raw', + scen2 = 'simulation') +``` + +### T-tests + +```{r} +sf12.p.t.tests <- two_sample_t_test(cv, 'cv', raw, 'raw', 'SF_12_PCS') +sf12.p.t.tests +``` + +```{r} +yearly_box_plots(pivoted.df = sf12.p.pivoted, + var = 'nutrition_quality', scen1 = 'raw', scen2 = 'simulation') ``` diff --git a/minos/validation/cross_validation_files/figure-html/unnamed-chunk-10-1.png b/minos/validation/cross_validation_files/figure-html/unnamed-chunk-10-1.png new file mode 100644 index 0000000000000000000000000000000000000000..276f3ca551221f3a63cf1eeeea3e0fd8650a8dc0 GIT binary patch literal 75542 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 LEG6S_{hR*}KAiPQ literal 0 HcmV?d00001 diff --git a/minos/validation/cross_validation_files/figure-html/unnamed-chunk-10-2.png b/minos/validation/cross_validation_files/figure-html/unnamed-chunk-10-2.png new file mode 100644 index 0000000000000000000000000000000000000000..d86ab1004f988af27d6b463720e0514a6c5d514c GIT binary patch literal 14690 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 literal 0 HcmV?d00001 diff --git a/minos/validation/cross_validation_files/figure-html/unnamed-chunk-6-1.png b/minos/validation/cross_validation_files/figure-html/unnamed-chunk-6-1.png new file mode 100644 index 0000000000000000000000000000000000000000..fa14c20f8f4ec1fcdc593c31f2d869a7d6884be6 GIT binary patch literal 16641 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 literal 0 HcmV?d00001 diff --git a/minos/validation/cross_validation_files/figure-html/unnamed-chunk-6-2.png b/minos/validation/cross_validation_files/figure-html/unnamed-chunk-6-2.png new file mode 100644 index 0000000000000000000000000000000000000000..9b0378afce5a07db50a9217cfde72bda9a778691 GIT binary patch literal 18381 zcmeIabx@S=`!LEbASePNB_JUvDWIg1t4K>oHv-bB64HwbNQ;Ol2&~dAjnpEkbazO1 z=fVQ#-bFv(_|2I)=ggTiXWn`LaNOs9?zrl{`XNYFS(f+$?FAeh9AbI72WmJt_%IwC z+!{h$Fj7~>vJ4(@R3BFMbi7#J8C8JU=vu3fvv%*@Qf!gBrk^&2;Cu(GnUv9YnUvvY88aB^~TadF+ed6S!) zn}>&omzVd}ty{Nm-{#}vv_29t+X=!O085vnwSvfg5d3kvS1qDS##fJ|cDk&)`D=Vw0 zsHm!{K6>;>O-)T*U0p*%LsL^zOG`^zTU$p*M^{%@Pft%@U*EvMz|hdp$jHdp*!c0| z$0jBwrlzK5W@b;GJTW&nx3I9Vw6wIcva+_ewz09XwY9agv$MCicW`iUbaZrba(epo z>9c3gogMM5;>8PhcXtmD4^K}|FE1}|Z*LzTA75WzI2`Wh=lAmE z%U7>ny?*`L-`_tVARsU>@XebyK|w*m!NDORA)%q6VPRq6;o%Vx5pUnVjf{+Z_wHR( zRMh+T@1vumKYaKQ6BG0CK^b{`@&UK0YBKAu%x#fk1ru^5yH-uSrQs z$;rtnDJiL`scC6x>FMbi85xoK0ZD%G4cEN@5#x@si~>y>FJr7nc3Odxw*Od`T2!~ zg~i3irKP3i<>i%?mDSbNwY9bN_4SR7jm^!?t*x!??d_eNo!#Bty}iBt{r!W30~88% zczAepbc9BukB^T}PEJlwPp7^adV@P~-bqgPISvkeAm$$=Vs_IU2Zsqq{=q$s7oS(h z2tHEGPBv{$xU7Zq7;?OX_Ot82^8JXpm}SiBF5iA*8cP2t=#5~I;pvBu=|Rk<_f{I~ zFH`EvnT8QlO6TJQ%UPa|I9fV>k9Qv$bDNtxa!)AZuNz+>dcNH@zPeHojDr*Ua}S)C zPRNdPg&BeK_zfHizXrX46Y#$W{x>}^;*gph>#DjROEpG{ZVBE=WeZ<19LSs~8=~3Q z^L8)xNF8v{4iHup*21ZV!J;IFcqnUgBo*y+ML55Infkg@B|&@HaQ&0fF)jqZHWbe4 zHKM*GwwWN>ZpC3=E>^wxIHRe7pHVC=$Ju4eNVrsEIXDz<#H3WsW&UVUmbiu!8te43 zsAX!fmYPn!w2_M8Ly6%P{ey2wHuDYZ0&2AAr`|HKCdKs_uXG&rOL!x9hUPB1e#*Q1 z&ERS{rzdazEXiW_L9GYnd|v32b35#d%kRR;;fa?z%OYGyo;-QB$z{E~(1w0StmL5E zFGJTrU4pXQk^BT1Gu9)6T++f57@6-%>`4;dSBO%N3s6jKbnp#6X5&YQX5`i<;r?a; z6D}?$FSK1>{L!0Qn9SDgn?Tda-OGI$wKr>N!OJ~M2syxmv=vxbMpsc)iR*cY7q5ye z9`4Zd;gL72aJ6}Kl)pqz(HL7v$=>3 zH@TS9_4g2$%`4Y3#5A;$)}IGAysnasc&B4{+7ZMm>u%y6CeC0A{F~lZ(KJv z@#Ix8-p<2C6=^y;9AjpL{Ha5ZP$6GGGA|r?jl<$mNGtUlKImb8FV|f1hd_B=3b)rd z=m0qD&2hQnygMZ;Tvd@eBgx@UtfeMU11wr8|70RWVz4Re{!Ly7kDXM~1etbCUm!665o1va7OQ#K1eivsZ3=>IKh@D z{b3G6{p%gr0+1~I=Xe5_-A@+Ai|tG9cq(rxhvCu&Rl3cU*mLPQKC6|?xkv}s#G}Bu zNT!N&>j4Z>a|wcvBlUM+W*P1yGHHjy{yyXbMcJcjk_BBTy!Lx(P1u$TOr^&s1_Cp& zGu+Va@Mh?g9AtpPEvDswy0-Ygqpq0)Q9X`cQ?etkaJ(YmUn7@A8d4WIm$%=q`r$~( z!XnL|B6lhAqAeDq1|Am@Ny*$9t<#Ofzm%>PY}t;K@P>=u zI~9qjQ}3odxK}v5_S%t@ehX&2@9y$J#5ZxKL>7e8Pm=UJQnm}Z%2>l{G}5HrRYay`Jy0<~LB45X?LWVB!gknj(w8^XHd&csZwZuQ>r~)cU~#^mpDE zM;g(vYU*B9yacek)aew$u$jg@F|i$&)b4KTKJx$?OA7JFg%Fo6kmX4*Z7Qg(@*a>$ z@Io&HuJ}BoQ7^lmhwIS8jS3G2MA*6uy+DxQ7ob12mD0RNKg_B&Jbs1)=B$zIx{lWY zN&t8!#H6cg|DKm4!9_d<${ zM{Owy|H80Yql?>ng+R{!U&Jt1NjG%#gaKAh!eDh_a1SZiH+LLuR zd^+`4iuTuz?9|)&JJEoXFV4|AphIcz6T78C+;u*Kfa4|wbT)I*>QHxon3W=u> zN7x4@x{Ss5q>er9%X7<&Y+m0fA^1D5!*eo@DjD9Mt2$rtK{%VYvQmqC{>3o-9nedC0Bc3>V{G1LnBJ1=jP+=M;qURZEN~DgA1z_y;-x!Kx%_#l5Y*SR# z@g+qkORrhv^A>b2?W#33o&Y13p>-Y3t{BJ?-XPvi_Koc@L;PuAU z;5(1~Mc0SN%!c;tdaj^g%l1GEyrfpONap2)(mGJ;1|lnfgd5aAn?rXRWPp(KEpK>w zrWqgLaUojejGciX6_EPIW9A^BBJyC!4m%{7S1Zgd2Qim9ybNw)NX6&CzyJjY5~ly%5-U_4O|<`Z`Umk(Z%PNQ)Uv5!K$v1d zOoEZ0hZt%0!@xn!2o{xR*R8LLEN`6Gfr+=too=>;t$gKZpf~zW2976LaKO$cN+&oH z4!+l0QX)GGEZ1m(FTrpZkl)<91Un7JVZZwC)!9ch&tvWbBZjbg8_;rP{%+DvKYT{j z?fXD~Os@mxl;`qygKGm2#yo$6mbd@=Glq`KA}pAzL(V>W=L93Z_#Yz^&iH=S7b9rU zRyh^e_T*n;T0tz5*$^lvs6SoPL>!P^oPXc&EmRLqvlw>|=6jf)P8$!C_AdIKWq10# zHVE!Po(!jYEOTgasnaK{y5ZG|)5z&(CmZM27zKEC&csEi$BXr^HqKDW{YlzmW9AiR z?2Q5-Rny=V{p2icPo%d8wnZ9-;Uv*54CIyy{U|fOX`GiK;D~g1?osFto&fLs`8PQ*W0K?Ol3|3y#tO*Y~7LaoEksE(lIV4k6|MSGN;N ztLyc$_hgL_!p#GvRl{Ca;3Ii?TlWg%6{Qne$&sl#La%iGtYt}7<_AZSb-u+=?lvOw zCvJSQH=z~TpAmjUWFD!IXbE2LjkAs5=L9aJ6h)zW56qlHR}o-kp}U?lmrUjR8|HvrB|J%@_{o){v{ zQ%TN|jHU1QOIWxrsm|QT?kWX~B*LPInT03tKPMe8WetR6K7xfDIr$++KVo;?&Dy2A z!Sj>k+i}PDTtfC|CK9Sh5zohu%&Bv;(<4fb?MqJY4p2hy-)aGmDDupZ!-nm3A;&+k zjseuFv$m_F&5pEbosb=V&e43Y(B}K>e*n$|R38Z{@B6b!g5PVpZ9nXb|4YE!W=vNE(A{FR zD*Lyeo8)i6C`<^tbQw?vdl2La3xWr@-9vk8+;@c82w0!{rTeD zWrtP*uoN(s!U=;K^g`MHIe8`HFDkXryE%Y)Ae)J2`>2jSjAj|p8P zWCB)%W1v)>a!*dGqQV*Sj~H0OKk-d_t{W?z5!#Pvp&TpQ7@mIy?C%9Y{~*E`aww2V zj1*FQ!&vN~jf>YYqLK0#Yp*f41CJA9sri6(T$6+BqC$7F)+)ZE2IXak8SX=lbF2vH zuK{a+=p}QQG2|Ha()fK<;^_wD+)E18#11*%LXQX=p}o-qoDnJZ&#oR-RIUw?ZQLjfH7&7ITmP{ndUXr`sJdhEBSczHQm* z_#<~24`n7KGh&kJ04~89b|}Wj#?bjQ^uky^NyYwp=+0-Dk2I`~djleFAeAI(!-h+H zSEh~#{nqyBQH{)!K#>(`)A<=~ASzJCP9qPo?n|9%MNj=0pK+6PxX7=TNPFJ-XZ!xe zEIBFIgm#O|5b}s-F{Wx<&KG#3*MMK=M`}gnO=nHGK#<8l{yot$c^+?z>uh}gV0JA7 zuB^@5<9+1uZ@`B4cCakMbH{I2TbRB>i~WPW7i>MUT|fHbLakKFQS08yHqGK)KXfqg z(SGBWd%%k(KgGza=ZThw6|K@1iXVCX+m8r(rPmv5VK!*yxg3B-yF_u4GS%_kjEGLOt=4TNP1+Jkj6@(kdA~}!@EX5P> zqggb04G*A$C_R?(xN;3K*?$pQelXe6AztM-nG5z}1$*I3A_j_`tMPlB23qAv$ej< z8?QbxBTO_h;k9{C2Edq#8q#+KkN50U_Sq074`6jXzaipAJw%1;v!&sK9vfxCv_hk^ zoA?}1S{Xcn6|lr@iQq)zjiGOeHQG37<>L-P3%TlyEOud-SeHW_(!J7@Kaf&C?L@rf zW6UV91HfsmKoAIQIYloCPe`=cb+{>br}=`rbxrNmkvxu&eTz#ICQAI0v5!C;ktJh! z%=mIBfmC&w*R0ewRqjc9 zp=){|FGp-c+vpMO?7s2=&MbK*D*DH{mppU3#4{Q+kJ82VPz-n!D9nZMf-TRkKHD!I zgi$BQASX0j3JKXqK@_l7sbYNl?_W;?h8+MM<6M59&Pgg-(OvYS>z z;QTI^t3Dx3p~5@f$TP#Jxb%n%u>qvh!jE)eKhD)F569C;P&Jd`laY5xajze|79ac0 zYNjHwH1=TlYqWJhMM96^aFNOGgtzH@KpF_7oB($)$M=)yX)!}}r}EBbn3$p5a|WBfL@M}qGow;xQWYH@u(&9q%E!5w`* zT@pyd$qyHVNj)H=42&oM9zX^m7KZBG!V?I^2yF;F7d3HQYT!K*szQXeVj%0h1+G3u zCSRBi%q)L7&NbHjO|bJ4yRH=cnkMGw;h@>__G9eA#mWZeo=3;Y&*I7^-rn3{5sIL4 zPJ~K+B{(|(obM)wJP`Yi9yjV%$3RGEbtRe;(U&tRJA03G;odOP#C(>7*6??ra|94t zYv`dtihrrjk1!!)%GLCk>j?3khmFIy!(}z3iwqFrq;5Qcf)^09we=>6hdfj-3q@m_ zn6Bx^*ewpAxp9I0e)@Gt&QrV{#XgFIYhx@J*?x5M#(67Y@x=VS&qC}QQsqE!-oU@= zC|vIJewR3v;AAzYsBi;Bj&I-|d&dvs*4@s(eTTPWVO?7=vlWphUrPOVBXQBBNtXJq z_wVqW-5oQwpAL+Qnk_Mg+wj`k97ZxMhhvDeYZvNJBL4a@dz=2<2|eEFui3;BJR2+? zATJtJ7~H4J{(F?Pgr-yxR#TZL;MJD!hydGOD!Dd!h# zS5sy;Q$DF3`BEN`i8Eo>h|2pGVZfA!QM*KCB7=C_^;DKNrT!2SueVUx_c&+u8lIFq z)FZ=LnKL$vhogt`4+F_mDHP?QB65Ki*Em`y$oT#ev6FE zGaj-)gH1W)VWNwhp~coqYG<4$gGlGUzmBMLHfS797_I^{jh^C`H(0kdp07x-^Ca}| z0kL2M@WrC9BL=e20W+eJ%#xvz4j6`Nx5CFwrnk>cHtS1^F11krJFoE$&a=#7FLsh@ z&7XR5@}@*B(L8Kjjk)3C!n>9Z2Z@qyqmn>XSSi&iLRh65PPk&@K-HpoanUeDfc^+-P09eEXUFnKf^%Ez%AoR;cf003$;-)iO!Q!LTT!( zh}@^x5ckF$t~3v|O)7|Cm@&?jG!NHAzBqL7!9Q-a0!3#;-qos8BjO9_w`;&n!v~&S zh5#hqV68IuJC;)T29JS92;`Jz$nhcvR&pT}%JR_iQs)^KM_(b~oU>`FcYuHsgmGYX zJdb$A1RDtK4vd55aJ@^g#-QB!>x;3arzP1&1_Risv)z+Fl<@UnAWk&DOU-{4EaqBba3M*23|=jVkso z0T}9*=RPP5{$?x*obm+9*`DwWX>Xqqy5Vy1ZBgWN;#Nm^918pFwDJMq132sZn z$edOSeHp2&JO}^0U_;Fk+=m9kkC3;ei+yg|R>_7BPo;Z{%9$VPK4o0H4KhE=nb#p> z(jAo8gAg22U;DhSfA=yfN=ehB4?6|Aq4a3()+JR02X3-$50|I>JSOQ6(8$`)@%dU4 zVfqG$z8B(J-=pZrpQ14g;!_87$TwbWtC$43_6(dSOdr)FAC2^w97*|ij0HI>*5UO; zVw!BmJoZn{=CT^wX4x4|zF0lGg)TuL^48dSz^%PZeuDn3#KNV?>QV#>8C*)RLd*^E zlR51F+Y5jRTsz-phVKcy;<-Zf2XG@FOkorHbq#Rf8uw@c}&cf ztQ&$KyA-AMZr$;Z>WxQ^<=shqZy50aR&3BI+=VRe}8T`m3Gua50QD_^h52pA&uq=cNFP_n+7oOp=}RAn$eczjD6wVbx{tbLIQo?O}s#5S8{muA&I? z^6Cqcl$_mjWR6@eBN2!I+nv5HIzIYDXEgHAV;mcOV{YEj;$)~`E^Zr}Dt~vxyXHV8 zpN(BoiNvEHSv_nd!6zSY_ha(}m=srgpEZrGlryq85>EA1XX#GrQs zS<}N$n^pUHtQaZ&c3Ac*z?aPUaG#a-Tm6=mR|L%>CXbe*8?WKp%iWVZUTPqDGzt_A zuTIZU4nM{k66baJBvg{@T=ap&Qe%^CtrF-uUedd$l017-7Q(YU8a6wHQQdz3>SBT4 zJUv-%>yd2s6$Z&Z5-x`o8f2*Tu0$g6PJQR?@tC^RhmDRJ6AwRQ7amGe<>=nxwM6Q_F z?EKWmz0FUgb?%?REHB=ka!$uA|(eXEMiFFo2L*h25$LiIm3sO5Zl!`>yFQx=5PlQo*L zl!7)MV>?eKI4Da#srbt2PNNvtWJijx<+VNkT)FbM-;I($GCVvAT^~v%%MPF662l$c zg+zPOb1D0~LD4~{r=M#rn`fnAlg=T%yNiQQn6n(R9yK8CL1KN+LPc}cZSmCbj?VBV z-N>s=aZvob;i7jsGGq79vENF4Wcpr^cbAgXcm%KbfV$}BRRK~k!&YO@=(=G1*lPDF zC=#c*ybVarM477?TDaVVz+kWDTK%Lo(+>S`h1YaahI!&HUe)=c42lS=pYjGQPt^Oj zvUZv!dtEac^6?-L;@I83&sQHGC6X)8+X8RX)njm24x~HJwV^?F*N9*seLHBt*r(6L zWvM9skZOOjthd*4Z{QV;E%}bVEz9|H03M#qUU|s|Y9wtYEpee3!~6cnRqyj%}a7Yv{+tb0((lW$=9!zo%{RBhi&iKKFBu8tq(I?Qdn}1 zkIEN%xUYkiraPfAOW~P8$eNzXLB2&^Oc~wdUOb%-@9{)1Rmjco8{mRm-P0{7|BL z=NHrN?0G8?4wDcl#JR~EC4bkov@TeMe5p^cO9{6WJ6IWODp7SD5BvZM<8{9>(Wa>Q zBget&^@S!D6?uLP5BBzQ0jFV&m&v8cX1|C1BbRPd|nZNq2C0%i%6q}WH zx&s@&Ef}?yW|$I^9e!>@7?AMCQYIuw7sj}lc~bcX=G@I(M?tH!6yyr7$L$Y--b=A2xKpgxa90?m0?x!s^%u3&!5Q_Xvrgtjjsz&p4C z*a2HSXr9U1$dOU7{7VaaO-3clQ}uSqYQ-I0EB}DOKQzk!v{H_|qObSd&CuDMWds-M z6$-EYBD1vX{S0+X1#II5(2}vEJ0G?Rg$>JW+fhjBd z3F1?X=a5t7gDvv7K-4yF1AG>u+O^a=SQ^9Zc>Cn^hyI*t__agC)A17WqI1X|XV2PS zs2yxrG*9g|N?r+ZXzND%*#1Ftl#O0H(u+-P)r9kCcn^-l2wEp}#x%Pu69l7YH3jnn zNJ_;SYB~tnKQlx(H*$yac$HLWp!`=SaeQQef_bnKWGRiL_h^*RQXUv)Ao*gyC%f zeHlow8L(YWFq)mtzxo1!v1d`B2A{B2dfvha^_?msp5i-OsTtbD5j;f(cAj_OiT_oVK&_7uLfW#T zieyb`XNRhiLOuW%>(xL-)3zNnUKf``Uz6yD{}QtdIh5#z0BL(x<2|~P$_GshMfkdNs?Iq$&HBH$1MPd2sMqzHn^Wh@V+{0aEK{ZtT_#O#;$`PH@zp+s zc#OzlCT0(NgTh0We>R>iuPKRa`7E1olUbP>G~0QD+~OgS~hT;=H$-si`SY5hqvgA+*i>Bb_Y~I6iKx{Y%=)DO2M{u ztD@WAR6hU38xU5#84@kEs|YN!by#OnXmO!)KGvxOEgWK%X6{nrS!Z6Ol@YI&ZR^L} zF5mGJ%e5Q_V{ji8YzeA9=DTw9GS88H0}1l&^?2uWHIk%lc-icldR!TQO z`Y7!nbBsZ$^ZlTa2ea9#snLU07AJ_IFrEO?SWv8qx1w$Dv8pMXty<%7b>*9n1Q7q8 zoJ>#W!$%`KLz6S}K50t>7zbnby$82qo|ldxDhycrdmEUx5b5Hws5SY#thVR%CI z#t$A&N!O#HRG21`l8Pd!x@OYV-Gr(yH32c1F0NBk9sWnQ$9Xp+8uwpPV&;@5xp+4Y zTNwgB?%Q-;-_m}dw&V*3Qv#RY()C_Y?3C)Uo&C^aE;^qCDIGDS{1)ohw}|bDyDOkR z1UB%C-vy2aqjPL=WX*@@U({sw5iEp%UKua+hZf&HtSXMgIr-C1GMXt)r}e?L54lQ= zzfpD8Q}Vk)$b+wnPZ#$l$v{>>;(~9+YCg^hs7`@GnEa?3gOa5{Vpkkh-Y`Z9owdExPPFu*+HN== zZ~Jns=RM+nE{~%`sDbT)5lLTeo|$6n8~(MVgOf;v9XvS6E(j3b7~{Dehxg4Df+@IB zk1!Fp>ewdnW0Weg=B%AZ#1<%4<%fh9FxLJ{w8JkQ(fVn9b3do^Mwq?RO*>%7)4Fy; zn|{gPxzS>{__1f@x_sj9nhg4Pr!~jMprHY#{)a1)S=kROlun-webztBQs8q>Abl;3 zfG$0iIn=cj?$$k`aD34wWt$?=Yt}&4UE&HXy#8D0l8emYaRF~d#1}#({c-NY`V+~` zhNGlY(qiQ7Bu+ITi${Fq$ty1279lxmYWo*$b+4ARatp#D#HZi#s43YG8DQ+mO*sg6 zP3TVAA}Kzd^0xEK+!s%ed$XL4>8Hxl&%@Qv`dL6bQ^oS4+{~|IAtT?Or18)PJbiaD z=1}LK_o7RQG`8Bdm4BEI8eKrcNeBD!4YO4ZLks)s@Kex1bk_TS0lmG>RQ3 z4d}VE2Aa8kjaKEn(adoMHUs?oH_pJq6UduLcU$RB5$rmx(n<5g_#cHZzynLrgX7Lk zpR6o7QIgU>vWlRhHS+}S5U8~-OJKTU9Di6W`c6t|%^^iEnLU_%*9SOg2a(DfpR za2G8L6quNs^H$k8vcuN_)#;KY&Gu;*Hx8iSrF-#gUb41>sxd&SpGOXoV|`_rLji3k z4dQRfYU z{x~7{f{5qFFRq=&)3PAktBk(%tJG8Vqra%-5KEMi5=J1-?WjfMG29^QtSrwk%#1zF z>^0H?ZYStatrLhoW~`UKVI3FBv$k6)y(+6KAgP(yIBssIw!6Yp$#mNR=9nph0sbuI33-x)9l@C ziScv|RdH?J0OOuo?2)Iy<7=4Zrv+350Ho*Ge0Zbhp!+$u=5f$$7fv<6b9Ra^n)uF9 z=2P$FW;Q$0a~pz~NT680wK17^qEMWoW`dBNR@ZymHD2FsWcumIG!BS6{$Kz0#>x)W zGhpP^lVuAU$u>j*2#P#5bf>EAM!w8nU{QqZ1zv~z*2_^HN9MfNGr&W^1U3FWwLZf$ ziTVD5Zlwc$AdJD@(lj%H$B&+E`qDc`VZ;_R4{!KLw>2$B6ZDz;H{|1Bnz)^U!rL(3 z0rtKDhOh{5o2@=HwEM%56$CYZe{LT~zYO$%5%BNKZpT~xdHp$Cp2qfQiR2H5Cgpea zJ{Y0W%eQR*I?B6RA}qy~&(eLY|2oP#InT8lW2op64#s*6#2BAg_e3qlN4sioa(muQ zHE`fB={t6{Yde93#d^UqTH|grH4>tsp8b1waF8tj&pv!&cZL||2^&~xYw7xLWU{Z@ z9O>V>q8Gr07$C&Ckhc5?w`PlM^v=UH$&ig<6?HpbqI30?_t}~!ApW9e&V`t2 zPdHK#ujuYs7wP|hf8CK%wAJw@kSL$v)MjmRz#$J43bECr2p|KEKucA&`E=XnzA zlc^gh>!mJYN?$G3O34137f%3&`7ez@WO#c36cg2XKS%9nolgfbC*B081?!*wJnF;i zy#m31>6inFXM^fJ#G@drEUrzZpPiZzQ zqLI)FVVA2^ZAB-3ZSFygWr;!py@-{x^RCrm$bt7-h{Jcj>Z0--*8(ApvfXEay?!fO z3U=Z&osgxYF^izprUwajpLEK3PAF|jt@?$fEnvt&)?L{n9%Zjh>0i5J6HJIyaKc5^ zJavJY_r~oJ5;rn5Q8c_h=%roy6#gXN9qq-u%_FpeVUdd0qUPe|kXQa(k~w)>>WQbn z>nt1JXDmlVI>SoU_oYM)juL$hAF;IAmF{{wA`iGS%qzWZiLLg88@(b)bSv!0q*aC~k#)j`lvhW_KI(|w6XC7iFo|CJAk94S z-u9uFx0xYAi_1(gM!s-0lzgr2eY?WVF{UvFc>-ro%_1+}l1t*Dnz$e8KkP*@gpcJ4 zB8Da?B7bINO!$RIwFmKLGu#-X*B`v^!BjoVK&Gk=SX-hZ?)l~cEB&2?r26iJ?yRmj z`(ELay-z#tITj66`)UJ%LNe!h9p0z1e$W!zvuNb+TvfmA#Wq};Hmz7H7t`c0K-(!Z zL9qzu$?OTpy6h`Z%6AeM@0QXQ~hMVB<6>o=4o$Dt@x$a$Yk{QNNs12zM z^&aJ!DT>2h2uTfFO3wuZz#GZ0=rZ@^h4YGY#M4#SPe~`gpAnW8LGWLe>}dj3j0N&x zNrki} zqjuy`&m{blX0IEQl4t}cTKO4syc-hts2B{W~do1^{c0|AGl{_ITbdOBT z+%>&kkQJbFmDAv8vGLmGoMuFrYr~D5j$@|mj7ss0Cv`Wd zQALVdntkL^^Ua?J@ABO@O9~+`@|;Xc-bi1>)`87M}JO{OT7|IIC8E@ID2RqyImLjMBKHaEM+*^ zNUiee@XG~X<0v8d?7ZPO@K|PqALyBzP#~Woa&%i`9iuh&(uk(Mp7&zN2$}QiB!${5 zBdy%)osNf$2;USdLNX>d+_9|a49xk|g!kbwB#H(~B83KljSikE<7iKg^#^=1Fx-@D z2*(c@bHg3go~y0HIcw49I>N-$bkve~L~fikS!QH$QVr6v8&nuB-Pv4sM4r~p2xY!8 z6iJjs^dv8ZSX6Bvd@1k6LSHJUFSa+j9kgFd`u((yG(n>JKDVU2gwyU_UgYtDiO2^m z67QbUsM~yv=SJ!bvU>2}DzSG<86QVmIa|x>C%tB)exJg_V?*P~_8N2`4e_GzEqUH5 zhuX^n7l%H2pxN>(qTVgnAs>$7jss2ei_q%H;Wt-J!@%+Ko0m}| zTHGQH#-wFxoJTw-ffL$tQQX9r8LCim_9NO_ZsX?7+M9i7NLY5IPoR+IGULXJCn?Sv zc2Z0C4;v>a@-A9ymOl0Js1xiVS$w3A>>BqyxxHJ%zrSqIXFV{YmB%@7l5!9t^v~yf z|Kt0<|Fs6lXuUNYJqfMA=ZE)S!M}Eb|E7DTK*0nKrrH z{jQpyzI!%lcWHQ;xsm3`UH#sDv4dRA8wdJd?y?{@LSjVOGg`PdiPv}Ka~(HiypBWc z)!YW%o_f{c>>gXcc-C_xD8AJ$y1e5a*A$)50IP9PSZ+JG+WJ-M`$JPv-6xb?qnA_< z>Q$Dr(7p4L2OFPmn=c)jIoAmrYPhbL<#il#U_g%w8%qdj4D+Ad7|A%2 z??d{m#ru9{(rgW~chA$BwX8hY%o=ce-yS+)3GxG9uce`%`G_F4`=dyF9Zo?0R%K~B zDH_QY3%ty6xlT-@XNXJ4+lR^%61Wu<$Wu5vNYsAZ|~EJl6F> z%Pq3Yka**i_Lg@?9O|Wl4afRl+ySkFXu2i1`1%>&HL_77brTM@`PF?(UU7bSF!dBI zYg#wbebPQAotP&QsLz>bgS|Ea=5jrw59{je=-6U+tK5{tI;-#vsK~u?R+qG{?qlhLEG7X$cC2Nh zZXAiITpc-BlJ72Rm1N)w95h$xy2GP{o*V`3;o}m|ru%1uDw3hG<(!;kowvKIGQ1P6 zjj7EbDPskp7bXrcR%k~v!jK|JQuK2qDlI7<5_W7beh1-bf90q!cDG+YujCry*U@!c zj|YuDYzo@cr|bT6A@EuiDlsRu73Q`!-#3!D@v$gbHcPSR4{azqZ-}RSQEAAlko2~P z&`0G@EW|r-wHxDs4=pE!dF?q2ubk2^tn&-2C>!?mA|Am`udlxIN;#=7#^3Q;%)JRIymT>ZYTPw-@52@A_Jepja8Q|YWT)qMDARz$#c zgdKrYs`|CC+CG-7AHOeOMZWlAG4$ZDxhi=%1YLT68`MU~OBLzaPL5aijX#dcQ05Dh zd5>}EH7ph~+(~}8sO$=L$?I!dTcj$J9-Ll?;falNlmwWY*!pNY2l?R2^zoHO4<37 z;8^Ww$nw$oQy(kvmn^)d+a~NrstbUttzNC*pyPV{OwJ8Qlu(P1?g3)^jHAtA_4*eL3&4e2Sq?Z zdJj!{4K?(VZxZx*-e>Q3@BN)0=f}Cu`Qc^mnYq`jHfvV9@mE!rB`2jLg+L(W@^VsY z5C{<*0wJg)Apj%wWvq+f0it?OLmE6np->nMMnFJ7NJvOTM0DcB31VVm5)zV=Cr^@+ zl9G{;ojP@joSgjh>C+Sx6qJ;dR8&-F&YYp9rlz5xIeYf(xpU`eX=%@&KY!uE1v)x9 zdV2bc7cVj}FfcMQGBGh-x^#(|nVE%!g_V_+jg9T{<;(2s>>L~%oSd9ju3Wi#^(q$^ z7dJOI4-XG7FE1Y--?eMku3x`?FeoS}I5;>YBqTI6^zGZX zVPRqK-n|PC4}bsueMCe=WMpJiRMdwLAEKk9V`5@rV`D#l{1_J(7at#=kdS~xA`=r6 zKYjX?l$4a5oSc%9lA4;DmX?;Do}Q7Bk(rs9m6es9ot=}Dlbf5HmzS5Hpa1#u=YoQQ z!otF$qN3vB;*ye*($dnhva<5>@`{Rz%F4>Bs;cVh>YAFG+S=N>y1Fl4zI^@qwZ6Xo z+qZ8G4GoQrjZIBW-@kwV@#9Bxb8|~e%g>)bTU%S(+S=ON+dDcsIy*bNy1Kf%yL)dAeCMG5)C#R;Srl+T8 zW@ct*XXobT=I7@Z78Vv47nhcn&}j7X^76{c%IfOs+S=Os`ufJk1_p!K+}zyS+S=aU z-r3pN-QC^W+uPsYKR7rzJUl!)I>KVH6Z!g{;0&C!lY8g@fiU>t{y{^h*Nh>MOAvXf zI~vbFqDP1k8oi0wwXrRIH)1k|`&1frzJ{{FkMA^`p>Mn^MT8mb?1q4y~hv5i1#A?$L~8FFfb2#Hx&+7PGt>a0JHViTK?hTg}DKH2)D zMX^Fp-IZ+xVLiy{OURRCZVLMCwX-4oI`RaOO+pN8n>*-wiui8jimR?Uu{s$_(r$2C z8y2+N(4>$p$L4-!C(E zgNKHve&b!F2s=_mQ4!>aAQ}%f(iV388)_AjjG{!+xp%V+@e0UI;Gt(YEAogH$g>tMK15!Dx~Zc!X$<#j;8tpi`YQ^JomhR zPk%S8BKz*XET5Lw8qWoyuWt}0530kP4MGhVd8#|}(K_=LJ!iXMd$#jI--F_sQ?~qS z^}E1cP-v@NhJc{+ZJn6ze6+H;&H|}9)Kjklvoqz2youHQdIx3)gKk#j-g`BX#*b!h zwI0~fo*(YH*#)B;5m((8uY5YM+Zm))tPX?tfDtwpUhP<7Fg$C}E8tM|*3 znPI}r4>r5x4qhyVT&81#Xj66wNCds}^^AR>>`m$|i^*_XP%wwaT`AUk_du^#F-AKE zphg6>#&kmFqZGrrn&k3sdW120v_sRF$X`A!?JZ5WbUw+KYxr;_kxVW;na~B^^vT2 zo}^?UYDI0iC?Ojain#=J3U%5d)z}DWMpj4@WykCAl%Z-}gRm0~V`1JoJ{7#?!|{wA zXX!J9kC@xf?#rjqdcTK!ybGTc&s9sSq~i&)j^)g8>CC>s(Vyv|b)aDTtyU|%%H*JY zzwl<}Et|gXe)9Kwyd}51Ac8=Nbf>AXJaU_p;g&q%ws|V+db7JbC9=Zo+CtcGeFM+| z0N3WnnGU(B@}&KqVg|=8jvrjcWM)&bfxgb^3nkm=DhTOoivQ9S-)87&XHOu9_}H(h z5ZS!%hKeadRBvscExWHwj1fRPT!7FtJ|5BhC3BmxLt=mthA^3wgONf^NH`(qS&)#& zZxAp9GZ^swZw6Qb+gCn%VOfS=K|adDJHKI0uenyZi9U6|gqS4ZyurOd9cFyF}7wb!>gWxPa|;8rl=tEEsx)a6-J?)%h1#?!xUz(_+~T10E} zkR_vA2#6n*>hpselR`B#drbsp$|RhkZfq3~Q>R!vJ*_E7?9M}dWHlREJCy!B%8V*iUWx5yQ|&=U8A?jk=ad~C z`{TorYpk_L!E6E?Fj5K_oih`;>fn~C!Hp-><~3Kp3m zD!M~=RUXjGUjv&zJyG;^3!$pEznEukX=<5S97ae0^&t+0*IT<+pZ7ng4Gg__dn=}$ zHOx}r%NF9IEPVWnC31X=OibML=|qQieA)BNwo3|%}P&aufk$_j58EADxm_39)9Pp1uE z#=Y>!2HM&Y#mwpYkCwPyW97!3Q@672uP7$$uY{Wxsh8#m)c?kawZo1+#qZG|i|d?h zYqcuv-*qHJmdfqDQbIg+3v3eq)o(~VCZfc-CiO2?yjNlIX>pBt zUs6)dSS0xF@g?Ny_fuvOXlP>cQA){Js+NK3Vgh%_WfFk-4FVl=cQ|K`XVG@^)I_PL z)92pLgb=_wm2t`CLSDMC4^)`q;G$NwmZWElVAz($XM}z+YwNA$k^SzQ8`hf<=r6ZI zfv~zi+_fFOKfT5}Hc^#eP0`>9w1ki*tetPbbl|9;uRtv562RjUGEv*RDRoV|)@82} z9fsctN?s}Lt`N5^W)XU>D>$G~&Jn&SJjwh{`^j`~oPG75|C%|>CXus#i)Ek~$!AV1oCM4L#~qe)Nq@6(FwhhDH4MH6`R zJvP0H#ewexL+_K=Ip!6YdwHmWa|GTa>=mLTIIbikN;+7hV@)!6|KPA0CY%=H=`tS$ zG`I}x5X$>wSU}oy9Yq9f^+EXR(mO4`72cxPwxB=8((Sby_m)j`ZG7WjSn6n6OqqLT zX;e<{zS2lMItGL}X+A{JB~(=tjnM@2U42Ul-h*sr!9ap!CoGOy>5g9f+K zsMqZG5RZM>Xc)eo*DC5=Id`2gVT_T)j29>oswu=Z8+p~1A#1J;2N+J|IDaR4d)eQ$ zZCe5Ez>FL(d2)a(9b5brXEC8jj60&Rv*4ylWN4;eLG>oBi^~V&d1YNVaGmL3pgnBm zsK~%-O$tX%9gNQZXRU2>XOisF^}P?~g|+S2%9Hqg4GZIXWw5mF2ZLAlYCq$4t|M|B zITl+Elh~9ogJHP8Mrj~}S8AcI$g%n|y#H|H#`QS4+qNvzlvRiute*F&0MTir?c+=) zIQ4k|j}EID0D^Z?5IeXhdV;|QH>!mjodcuR?{6(kpY``z;KU(^gb7RklzU_am{8Jw z>px z`wR1m2q4R>9oF`~${?f$kcFH z5dy3)39ldlB`_Q%Zy486V;*Pq-L~fjT<@g~`~wN%MhZ7qtaSy4GGXgR_|-(}bQ}}c z024*7^|dxP&%Ma=Z`b!K#_gmE1v^c!0{Lg1sa4G#pEz|a(U|@q%!+>Bw~~sovU)O6 zK$=K~OLm;~P*ZABf243L?i7?EW}YOlUw?yY-bx2|Pdz$n@6g#_TOx@99x7^3i@SJk z&LV1DovHP2cKu#rf-XLw7@3guDvWOF>(<3-#4jSpIFn?iuTvzqfpY71$f!s!JgPc; zCxYcW6Wu5~?r`z}yx&R24S$x)6?^BS$prH>TjFvL6^#eGOZ+A)9s>xb$sD-D@iuEr z!fSwfTVT|_^|C57?vqnoSUg^Hmxt%>@MUELw0_sP-o5qsBRIt89{+*5uQAB(Iwz7f zErNpw&M$<&iyCiSc+k1_0ibBUS>VW-}Q#n3xAQEt|tvy=)B^FX~=quQ1y1{X* z8Nb)~-z*G5_-d8Tsc*-fk$^-Pq2yVQwbu-4f}@r#^Wnq$2KVLRlIexbmMNJ>lJ1d; z49WU`FS0!WJ=_ZZWU)?DQzo<>j`mi5?i3`BO^>NzslXh5{!IL-5_i;J(%`lnI|V~k z%TK)IUiC{V_ZRocix1cHx}?(~mJ`zG`GRz=q8+zH*xc;C@ex_n+3lGlv;72W1N{_b z2NtGL`@xY%vPW9XXwzd0-|s22XF}P*lGRMYX{zR@xIcG2Ptk+2#;GVl)Tf>KF|^fE zW{(?Kr)^V0#}wQxu9NDOi^8aEHt-j04$F{T=h-)fxnN26ycq@8M|; zV|M}0&mkAu;m-L$$%urID;RVvR*0%&c+}Q|wBN#YIVzdB-)&yUF#hm#F_ z?8fb;Sj3)ct@`>deQlSZhhXb-LcA`^!owO~(Ob?6SyRBy6}9ZJt!y3JUq|9lo|GM9 zoaq%8D;#Pn@q^GCrtw-opM(-rkPC15qUP*{j;%*#HqP1;^|2qpo=Y#Hnk)L&WItKs zgaIWpOCwjSkXvi1a=rt9QG7*v6PgkJf%m=eZ>4a!S~O~%Mc?c~NXDU`cg=J)*6QM+QPA<#94vLzsYrK|uS5zSm~rGe#ELi@`?N~dJq9;z1`X!E zeP_YWQ0iMNRZ&ut_UHZ zd+7MO$5;@`!w#cU%eeuoD8~VN0}Ym9oMQ=QLrP1*B{O+hkv~n#oWGm`k;<#}BTBW4 z&`p1YuZFP<{5FLn8b$~189ww$Gb*Z%w^v(8%tUbPd`v*MyO5E#j%9}}P(#Txej@tt zno6(AO4!2zcDrz>5ezm9L9oO=T0A!-i6D=LPE*aHs8pZUdmx!3c4=yWR)Kk#oWC|d z+J!|EceqG65$}JdtwsRh0zdQ8Wq#!J@1H)189%U=8M(`h{O-F8mkhuSu+YyCkY9nt zFu?+>cG%S9s5%ZPRi8@2C9m=$jqz^=Sdo~?8K3BxuPh`W;VfWAGE2iZB;jG?&_C~i z`M+NKPe4CV2qcr3c>%m=aJ!IR`d2(4YX347VD;D9f+yk~Z&{8p1zs@?w3PxXElBtq z(!b*1)Z6SM-OGk-s4njG#Q;D#VDi}uPzFPAc36*DvhXPg;f3?yj-Tr@hr8RAfx!fRC$e))EDyn3Aa=1I zBn8cLRTgJsk0YZ?Ba1oVhfAe(TdKpdaOm^uBGWh&(`Fd@2_Y0p^mcwU9$O;z8o2W~ zu0xps51)vu))C_we&!be4q=+r7dU)Kci?!wmZy{uQ&JMr41(*TGI!5)NRs3PSPm}P zX9u1v0FH?2pI^K1`e4MK6nJgpg)co$?_l4h;9wEt29;_8;QBqBHj!THzBOXyPVxl~ zhdwnF$4&f�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 YxBaF1cod5#0ab{+jEZ!@U6W`38!X-3F8}}l literal 0 HcmV?d00001 diff --git a/minos/validation/cross_validation_files/figure-html/unnamed-chunk-6-6.png b/minos/validation/cross_validation_files/figure-html/unnamed-chunk-6-6.png new file mode 100644 index 0000000000000000000000000000000000000000..2be15fd7a86a2acda8ab4bdad936bafe815b6840 GIT binary patch literal 17384 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 YrPs!68L7d4Q9_`ipedh!^Ufdt4KNWR!vFvP literal 0 HcmV?d00001 diff --git a/minos/validation/cross_validation_files/figure-html/unnamed-chunk-7-1.png b/minos/validation/cross_validation_files/figure-html/unnamed-chunk-7-1.png new file mode 100644 index 0000000000000000000000000000000000000000..c328ef1dc8da38aa350014a7438438de9e805e6d GIT binary patch literal 66218 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 literal 0 HcmV?d00001 diff --git a/minos/validation/cross_validation_files/figure-html/unnamed-chunk-7-2.png b/minos/validation/cross_validation_files/figure-html/unnamed-chunk-7-2.png new file mode 100644 index 0000000000000000000000000000000000000000..ff295d048ccb0f95f793fb8dbde669f29595575f GIT binary patch literal 16025 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 literal 0 HcmV?d00001 diff --git a/minos/validation/cross_validation_files/figure-html/unnamed-chunk-9-2.png b/minos/validation/cross_validation_files/figure-html/unnamed-chunk-9-2.png new file mode 100644 index 0000000000000000000000000000000000000000..7d6b91a2dfdf42ab008efc01744a89e4117ae8f3 GIT binary patch literal 17681 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 literal 0 HcmV?d00001 diff --git a/minos/validation/cross_validation_files/figure-html/unnamed-chunk-9-7.png b/minos/validation/cross_validation_files/figure-html/unnamed-chunk-9-7.png new file mode 100644 index 0000000000000000000000000000000000000000..3bc4111d930f8754d1cf6e8a917bb0569024c344 GIT binary patch literal 18315 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�@o+ z$94L2T%GA=fZ3kcTCQ^A)d(~!on9f>R@zuhc)X$OPG@d%!DV<3j^{22{QVkS!n{;A z_L{2qxta`6!#NwXQm}`z=67~%&<+C$rM6e-BJB#oO0#vga1U?u*WnJNwu@6%rdlnY*R;1X^bH7>a%Tq^ zGSYCFFtVjExG=zrSJJ#%FT*AAq@=umKrX$yPti=&m~f^>03|e85`#=)>e9Ni(HV2A z;y2TDCDT8@u*{PnyF=2&eVHPx_JWm1r84eE+I3LRPBc3W+|yb?+yO1VGLM!_NNOj3)=itf4d+u;VP@m^LYmMf97?Jz3+YSfRTgX*w4+#QpP%L6 zl@{}B?ba1<=G{*)*~~UUVVj2rvG0Ip0!94f{)@*cq`dlDXwlcYo0Y)n<`GAOpM2< zUJm8l+dO^v>+k|vC;q-`(;-wXhF9p*;kpIcs`=R}H8Khfm+5V=B zqh?Yo_YEWB3jYlZRdSW0xM(S--&O6w09xct!Pvh0(%q?%)@U*9dI_nRjyPqEqZo9?<(Zm!pb1_ldAK<(ibRGy5+Fh*f02XI5h!BBB-mW2|~|w|2r8 zV!>C8$nk6Yd@M;REfnL80CS8K+as1XI|f#ntjeDv zR3|-(mdln}5>xm;-L6oP>k;S3$sRqnaa`*`*osv5CnRNS@ z^PJ2s^eV4nm7QCT{RUTU8{>`An!Oe;`Ut4mU(MR}pWtX*$rw`0sqQhdY+mT3V6tsF zWS%W>VIn+oPy6X6dT45m3%9Z+=kcA*6`=zk);?gB5)sc}k!~0BMHt5*!AGcype(ht z_E8|W!$2*Yv9tXtS%?qPWq?~W_3A3$kOC|7CMAt+`~Qn;2pm_5BTs(N_`-l`^#q zS@7RkjzW5@Mr#?m$G*%iDK|W#*8&-9q}xm9?$h)$?zWv8roZ5TZH8G7q;@kEN%KUi z3i&tk?=I!Oz`kON6A0s+J8-pWkr*gRsgIC0^%IV->t$Y1%>!&gRF{$UR}AtsmXrD} zF|AbTx$i3PBkzml62b1cWUDOZF!MHES83lW7-IEjN3TyIShH42huo%B9F$2g{44oC z91%E;MZ@wAJp}aq3|3$p~!ndEg+rdA5-k@oaK9aYewNAMtkhGN?#NB##`ZM>~=VZ`Cze zLjV_cP@b=rB0dbbR=*Ti2$gLG(j|*80=NILTMjbA9kDJg<@A2BUHWEP;Pl`@wAw4o z&pSm*kOa&J_an>gnq%n`Er^2Hgu{ul7~PW4Yv7yPn@I_zic6dUWJd|@N!-t)Y;52+ zVb#hl0~&+B(cE%6B4A@OfNaA2Fj@IW9W1>qN7YHm< zckk3P2?x|c9V*fyv%T7m4B7qw)ZeU4mf^W+diC(Pc_&z9p8BiewJcpe^N7hWev4?> zY7W1$k?@6OO|2u@t$PC-_e3bHe4A6^vqT$&icE)^X$`~)FQzSLudk@*NXL1vdu0i8 z)%t6qcn(VkZsc*S<|?>3&Xy@b`uco-ET`DpDvF7h#2(%p=pV@I+@-5m8KpmT2#${g z)Y(3!E_tWKL(0?tp-&vA)FX_}s3%=+Zv*bzTbp}n!K_EoR7*ea*FCoTP-J%nUd^6g zcNFXJGEKL9ym8>YI>W-HdP13y`Z-#;L8N-tN-B6y&T8Y{j#B^f$ zogTH~fOs&|;DiI0m@xR=${L1_^e3*$@5J1(3EI9`m^R**ij3Rb2&I1u{Vq0r)GQz# zujkGry48&dNh?9r+R7al33Wa%_U{U`9j#d3FPI!y(&%zwI~_`~KX`K|oKm7mkcP-#JB2Iy4Z0Xeul)x{F5Gcv1Jt}_q?Dc;G DhX7Om literal 0 HcmV?d00001 diff --git a/minos/validation/handovers.Rmd b/minos/validation/handovers.Rmd index 907ef02f..94e89ed8 100644 --- a/minos/validation/handovers.Rmd +++ b/minos/validation/handovers.Rmd @@ -25,6 +25,12 @@ Source utils for some functions. source('Minos/minos/utils_datain.R') ``` +```{r, include=FALSE} +start.year <- 2020 + +save.path <- '/home/luke/Documents/WORK/MINOS/VALIDATION_PLOTS/Handovers/' +``` + ## Data ```{r} @@ -36,14 +42,6 @@ out.path <- 'Minos/output/default_config/' base.dat <- read_singular_local_out(out.path, 'baseline') ``` -## Constants - -```{r} -start.year <- 2020 - -save.path <- '/home/luke/Documents/WORK/MINOS/VALIDATION_PLOTS/Handovers/' -``` - # PATHWAYS @@ -120,67 +118,135 @@ base.income.check <- base.dat %>% ``` -## SF12 +## SF_12_MCS ```{r} # first figure out how to plot final_US # start with hh_income -raw.sf12 <- raw.dat %>% - dplyr::select(pidp, time, SF_12) %>% +raw.sf12.m <- raw.dat %>% + dplyr::select(pidp, time, SF_12_MCS) %>% + filter(SF_12_MCS > 0) %>% group_by(time) %>% - summarise(sf12 = mean(SF_12, na.rm = TRUE)) %>% + summarise(sf12.m = mean(SF_12_MCS, na.rm = TRUE)) %>% mutate(source = 'final_US') -base.sf12 <- base.dat %>% - dplyr::select(pidp, time, SF_12) %>% +base.sf12.m <- base.dat %>% + dplyr::select(pidp, time, SF_12_MCS) %>% + filter(SF_12_MCS > 0) %>% group_by(time) %>% - summarise(sf12 = mean(SF_12, na.rm = TRUE)) %>% + summarise(sf12.m = mean(SF_12_MCS, na.rm = TRUE)) %>% mutate(source = 'baseline_output') # merge before plot -sf12 <- rbind(raw.sf12, base.sf12) +sf12.m <- rbind(raw.sf12.m, base.sf12.m) # Now plot -ggplot(data = sf12, mapping = aes(x = time, y = sf12, group = source, colour = source)) + +ggplot(data = sf12.m, mapping = aes(x = time, y = sf12.m, group = source, colour = source)) + geom_line() + geom_vline(xintercept=start.year, linetype='dotted') + - labs(title = 'Mental Wellbeing (SF12)', subtitle = 'Full Sample') + + labs(title = 'Mental Wellbeing (SF12_MCS)', subtitle = 'Full Sample') + xlab('Year') + - ylab('SF12') + ylab('SF12_MCS') ## Try a version where final_US is limited to only those present from wave 1 # onwards because the sample refreshments are messing with the plot -raw.sf12.wave1 <- raw.dat$pidp[raw.dat$time == 2009] +raw.sf12.m.wave1 <- raw.dat$pidp[raw.dat$time == 2009] -raw.sf12.2 <- raw.dat %>% - dplyr::select(pidp, time, SF_12) %>% - filter(pidp %in% raw.sf12.wave1) %>% +raw.sf12.m.2 <- raw.dat %>% + dplyr::select(pidp, time, SF_12_MCS) %>% + filter(SF_12_MCS > 0) %>% + filter(pidp %in% raw.sf12.m.wave1) %>% group_by(time) %>% - summarise(sf12 = mean(SF_12, na.rm = TRUE)) %>% + summarise(sf12.m = mean(SF_12_MCS, na.rm = TRUE)) %>% mutate(source = 'final_US') -base.sf12.2 <- base.dat %>% - dplyr::select(pidp, time, SF_12) %>% - filter(pidp %in% raw.sf12.wave1) %>% +base.sf12.m.2 <- base.dat %>% + dplyr::select(pidp, time, SF_12_MCS) %>% + filter(SF_12_MCS > 0) %>% + filter(pidp %in% raw.sf12.m.wave1) %>% group_by(time) %>% - summarise(sf12 = mean(SF_12, na.rm = TRUE)) %>% + summarise(sf12.m = mean(SF_12_MCS, na.rm = TRUE)) %>% mutate(source = 'baseline_output') -sf12.2 <- rbind(raw.sf12.2, base.sf12.2) +sf12.m.2 <- rbind(raw.sf12.m.2, base.sf12.m.2) # Now plot -ggplot(data = sf12.2, mapping = aes(x = time, y = sf12, group = source, colour = source)) + +ggplot(data = sf12.m.2, mapping = aes(x = time, y = sf12.m, group = source, colour = source)) + geom_line() + geom_vline(xintercept=start.year, linetype='dotted') + - labs(title = 'Mental Wellbeing (SF12 MCS)') + + labs(title = 'Mental Wellbeing (SF12_MCS)') + xlab('Year') + - ylab('SF12') + ylab('SF12_MCS') -ggsave(filename = 'SF12_wav1.png', +ggsave(filename = 'SF12_MCS_wav1.png', plot = last_plot(), path = save.path) ``` +## SF_12_PCS + +```{r} +# first figure out how to plot final_US +# start with hh_income +raw.sf12.p <- raw.dat %>% + dplyr::select(pidp, time, SF_12_PCS) %>% + filter(SF_12_PCS > 0) %>% + group_by(time) %>% + summarise(sf12.p = mean(SF_12_PCS, na.rm = TRUE)) %>% + mutate(source = 'final_US') + +base.sf12.p <- base.dat %>% + dplyr::select(pidp, time, SF_12_PCS) %>% + filter(SF_12_PCS > 0) %>% + group_by(time) %>% + summarise(sf12.p = mean(SF_12_PCS, na.rm = TRUE)) %>% + mutate(source = 'baseline_output') + +# merge before plot +sf12.p <- rbind(raw.sf12.p, base.sf12.p) + +# Now plot +ggplot(data = sf12.p, mapping = aes(x = time, y = sf12.p, group = source, colour = source)) + + geom_line() + + geom_vline(xintercept=start.year, linetype='dotted') + + labs(title = 'Physical Wellbeing (SF12_PCS)', subtitle = 'Full Sample') + + xlab('Year') + + ylab('SF12_PCS') + +## Try a version where final_US is limited to only those present from wave 1 +# onwards because the sample refreshments are messing with the plot +raw.sf12.p.wave1 <- raw.dat$pidp[raw.dat$time == 2009] + +raw.sf12.p.2 <- raw.dat %>% + dplyr::select(pidp, time, SF_12_PCS) %>% + filter(SF_12_PCS > 0) %>% + filter(pidp %in% raw.sf12.p.wave1) %>% + group_by(time) %>% + summarise(sf12.p = mean(SF_12_PCS, na.rm = TRUE)) %>% + mutate(source = 'final_US') + +base.sf12.p.2 <- base.dat %>% + dplyr::select(pidp, time, SF_12_PCS) %>% + filter(SF_12_PCS > 0) %>% + filter(pidp %in% raw.sf12.p.wave1) %>% + group_by(time) %>% + summarise(sf12.p = mean(SF_12_PCS, na.rm = TRUE)) %>% + mutate(source = 'baseline_output') + +sf12.p.2 <- rbind(raw.sf12.p.2, base.sf12.p.2) + +# Now plot +ggplot(data = sf12.p.2, mapping = aes(x = time, y = sf12.p, group = source, colour = source)) + + geom_line() + + geom_vline(xintercept=start.year, linetype='dotted') + + labs(title = 'Physical Wellbeing (SF12_PCS)') + + xlab('Year') + + ylab('SF12_PCS') + +ggsave(filename = 'SF12_PCS_wav1.png', + plot = last_plot(), + path = save.path) +``` ## Housing Quality From 3589510001fb80ebed27aa9969bc679be4948d80 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Wed, 17 May 2023 09:31:59 +0100 Subject: [PATCH 002/229] Embarassing fix here. Forgot to remove missing values from the raw data in handover plots (assumed that this had been done during complete_case but that was only for some variables). This has changed the shape of the SF12 MCS and PCS plots, and shows some problematic behaviour that needs to be monitored. I believe these will be vastly improved with the rate of change model work that is ongoing, but remains to be seen --- minos/validation/handovers.Rmd | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/minos/validation/handovers.Rmd b/minos/validation/handovers.Rmd index 94e89ed8..bca0aae7 100644 --- a/minos/validation/handovers.Rmd +++ b/minos/validation/handovers.Rmd @@ -140,6 +140,8 @@ base.sf12.m <- base.dat %>% # merge before plot sf12.m <- rbind(raw.sf12.m, base.sf12.m) +print(sum(sf12.m$SF_12_MCS < 0)) + # Now plot ggplot(data = sf12.m, mapping = aes(x = time, y = sf12.m, group = source, colour = source)) + geom_line() + @@ -302,12 +304,14 @@ rm(raw.housing, base.housing, housing, housing.norm) ```{r} raw.neighbour <- raw.dat %>% dplyr::select(pidp, time, neighbourhood_safety) %>% + filter(neighbourhood_safety > 0) %>% group_by(time, neighbourhood_safety) %>% count() %>% mutate(source = 'final_US') base.neighbour <- base.dat %>% dplyr::select(pidp, time, neighbourhood_safety) %>% + filter(neighbourhood_safety > 0) %>% group_by(time, neighbourhood_safety) %>% count() %>% mutate(source = 'baseline_output') @@ -352,12 +356,14 @@ ggsave(filename = 'neighbourhood_safety.png', ```{r} raw.loneliness <- raw.dat %>% dplyr::select(pidp, time, loneliness) %>% + filter(loneliness > 0) %>% group_by(time, loneliness) %>% count() %>% mutate(source = 'final_US') base.loneliness <- base.dat %>% dplyr::select(pidp, time, loneliness) %>% + filter(loneliness > 0) %>% group_by(time, loneliness) %>% count() %>% mutate(source = 'baseline_output') @@ -406,6 +412,7 @@ TODO: Drop negative values ONLY from waves with no data i.e. not 7,9,11 # start with hh_income raw.nut <- raw.dat %>% dplyr::select(pidp, time, nutrition_quality) %>% + filter(nutrition_quality >= 0) %>% group_by(time) %>% summarise(nutrition_quality = mean(nutrition_quality, na.rm = TRUE)) %>% mutate(source = 'final_US') @@ -474,6 +481,7 @@ NOTE: At present, physical health is not predicted or transitioned. # start with hh_income raw.phealth <- raw.dat %>% dplyr::select(pidp, time, phealth) %>% + filter(phealth >= 0) %>% filter(time > 2009) %>% group_by(time) %>% summarise(phealth = mean(phealth, na.rm = TRUE)) %>% From 71ee05d8eed4092e4c5422e8ba14c0a730db6fca Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Wed, 17 May 2023 09:58:48 +0100 Subject: [PATCH 003/229] Fixed typo in the cross-validation notebook, and updated the util function combine_and_pivot() to remove all missing values (negative 1-3, 7-9). Also removed some .pngs that were tracked by accident --- minos/validation/cross_validation.Rmd | 2 +- .../figure-html/unnamed-chunk-10-1.png | Bin 75542 -> 0 bytes .../figure-html/unnamed-chunk-10-2.png | Bin 14690 -> 0 bytes .../figure-html/unnamed-chunk-4-1.png | Bin 79849 -> 0 bytes .../figure-html/unnamed-chunk-4-2.png | Bin 18454 -> 0 bytes .../figure-html/unnamed-chunk-6-1.png | Bin 16641 -> 0 bytes .../figure-html/unnamed-chunk-6-2.png | Bin 18381 -> 0 bytes .../figure-html/unnamed-chunk-6-3.png | Bin 18277 -> 0 bytes .../figure-html/unnamed-chunk-6-4.png | Bin 17121 -> 0 bytes .../figure-html/unnamed-chunk-6-5.png | Bin 17340 -> 0 bytes .../figure-html/unnamed-chunk-6-6.png | Bin 17384 -> 0 bytes .../figure-html/unnamed-chunk-6-7.png | Bin 18396 -> 0 bytes .../figure-html/unnamed-chunk-7-1.png | Bin 66218 -> 0 bytes .../figure-html/unnamed-chunk-7-2.png | Bin 16025 -> 0 bytes .../figure-html/unnamed-chunk-9-1.png | Bin 15827 -> 0 bytes .../figure-html/unnamed-chunk-9-2.png | Bin 17681 -> 0 bytes .../figure-html/unnamed-chunk-9-3.png | Bin 18657 -> 0 bytes .../figure-html/unnamed-chunk-9-4.png | Bin 19002 -> 0 bytes .../figure-html/unnamed-chunk-9-5.png | Bin 19051 -> 0 bytes .../figure-html/unnamed-chunk-9-6.png | Bin 19517 -> 0 bytes .../figure-html/unnamed-chunk-9-7.png | Bin 18315 -> 0 bytes minos/validation/utils.r | 4 ++++ 22 files changed, 5 insertions(+), 1 deletion(-) delete mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-10-1.png delete mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-10-2.png delete mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-4-1.png delete mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-4-2.png delete mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-6-1.png delete mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-6-2.png delete mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-6-3.png delete mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-6-4.png delete mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-6-5.png delete mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-6-6.png delete mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-6-7.png delete mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-7-1.png delete mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-7-2.png delete mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-9-1.png delete mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-9-2.png delete mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-9-3.png delete mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-9-4.png delete mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-9-5.png delete mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-9-6.png delete mode 100644 minos/validation/cross_validation_files/figure-html/unnamed-chunk-9-7.png diff --git a/minos/validation/cross_validation.Rmd b/minos/validation/cross_validation.Rmd index 44234e92..c7c88656 100644 --- a/minos/validation/cross_validation.Rmd +++ b/minos/validation/cross_validation.Rmd @@ -160,7 +160,7 @@ sf12.p.t.tests ```{r} yearly_box_plots(pivoted.df = sf12.p.pivoted, - var = 'nutrition_quality', + var = 'SF_12_PCS', scen1 = 'raw', scen2 = 'simulation') ``` diff --git a/minos/validation/cross_validation_files/figure-html/unnamed-chunk-10-1.png b/minos/validation/cross_validation_files/figure-html/unnamed-chunk-10-1.png deleted file mode 100644 index 276f3ca551221f3a63cf1eeeea3e0fd8650a8dc0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 75542 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 LEG6S_{hR*}KAiPQ diff --git a/minos/validation/cross_validation_files/figure-html/unnamed-chunk-10-2.png b/minos/validation/cross_validation_files/figure-html/unnamed-chunk-10-2.png deleted file mode 100644 index d86ab1004f988af27d6b463720e0514a6c5d514c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14690 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 diff --git a/minos/validation/cross_validation_files/figure-html/unnamed-chunk-6-1.png b/minos/validation/cross_validation_files/figure-html/unnamed-chunk-6-1.png deleted file mode 100644 index fa14c20f8f4ec1fcdc593c31f2d869a7d6884be6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16641 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 diff --git a/minos/validation/cross_validation_files/figure-html/unnamed-chunk-6-2.png b/minos/validation/cross_validation_files/figure-html/unnamed-chunk-6-2.png deleted file mode 100644 index 9b0378afce5a07db50a9217cfde72bda9a778691..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18381 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�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 YxBaF1cod5#0ab{+jEZ!@U6W`38!X-3F8}}l diff --git a/minos/validation/cross_validation_files/figure-html/unnamed-chunk-6-6.png b/minos/validation/cross_validation_files/figure-html/unnamed-chunk-6-6.png deleted file mode 100644 index 2be15fd7a86a2acda8ab4bdad936bafe815b6840..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17384 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 YrPs!68L7d4Q9_`ipedh!^Ufdt4KNWR!vFvP diff --git a/minos/validation/cross_validation_files/figure-html/unnamed-chunk-7-1.png b/minos/validation/cross_validation_files/figure-html/unnamed-chunk-7-1.png deleted file mode 100644 index c328ef1dc8da38aa350014a7438438de9e805e6d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 66218 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 diff --git a/minos/validation/cross_validation_files/figure-html/unnamed-chunk-7-2.png b/minos/validation/cross_validation_files/figure-html/unnamed-chunk-7-2.png deleted file mode 100644 index ff295d048ccb0f95f793fb8dbde669f29595575f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16025 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 diff --git a/minos/validation/cross_validation_files/figure-html/unnamed-chunk-9-2.png b/minos/validation/cross_validation_files/figure-html/unnamed-chunk-9-2.png deleted file mode 100644 index 7d6b91a2dfdf42ab008efc01744a89e4117ae8f3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17681 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 diff --git a/minos/validation/cross_validation_files/figure-html/unnamed-chunk-9-7.png b/minos/validation/cross_validation_files/figure-html/unnamed-chunk-9-7.png deleted file mode 100644 index 3bc4111d930f8754d1cf6e8a917bb0569024c344..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18315 zcmeIacT|&G*Dp%w>ZT}7L5d(ns)eFJAc%D70@4Lkn)Kd-2nYy>2#B=MrAzPNmL?z& zdJT~dflxz-Cr_T}=;-L`>gws~J$?FAUtizAz`)SZ(8$Qh z*x1;_#KhFp^x3m#W@cvQ=H?a_7SEqQfAQkQ%a<=LEiJ9AtgNlAZES38ZEat@dSz#4 zXK!!s;Nals=;-9+gwj^=I-w9;o;%w>FMR=+9#|=kMU%!4G6cqI4&70uh;E<4z(9qDgZ{LQ6g@uQQM?^%td-pCfGBPSEDmps) z{rmSZF)<%Le29&W{rK@?TwGjye0)Me!lzH45)%_YfBu}5l$4yDoRX4~nwpxHmIi~t z($mv3GBPqVGqbX?va_>ua&mHWbH9A~l9!j4pPye)P*7M{SX5M0TwGjIQu6id*V59` zva+)B^74v`ipt8$s;a8$>gt-Bn%dghy1Kgh`uc{3hQ`LmrlzLm=H_qTzWw>!&!0bgdU|?$d;9wO;Ba_W!H80;1eM0*+oyXlGEWC|eFAJx?FJXin7`2Jbhzc)1VG zh3;DqyPwjS?7K1(q{~1CvDMiFr2Zc!%3L3L%Z`(<6ce#Yl3R6sH_@tgSx@z~Vamgm z&F2thRSG3zzg=U_;xjfSgS@4Nf7BfK+7=dkc$&pTbBRjH&^PJn!Pf{IDGwiJf*TJZ zvGGG+oMmjbq^vz>oekxt)$J1bBVD|h3Cw_ZQWH)sr-Kou%0zPvmog@9H7+dep_aDA z3BJF9E*?E*ywY5w_xb&JMW}G!jvGORC$aFT@XEnKo99a4&m5 zx%-K87RKh~q{u*}3DCR%Gz-Q@DH>5GEIzM72&!VdnF$0~L2h-T8LA&bdC|OnpT|AZ zTyr{B3CKZ&6HgJh299=OBkC2?UWls4^lszX0)a3cnh9C|7|mN~7wY3yVeqM?ggUap zD5OwAr!TF&7Zj~^7paQhb9vQJLf^B&^o87>U_Pu(JhHZV`2d@7dTW0aL`_dDJeN3P zvkPfK39lso+};}?90i&giG|UT?l#ip{YU$8om5PmzIsJ6#UU)!nF+WZbWhH2HLX zaI_o2n+qV-Iwi_Zhx_?Vu_m_Q@!j8Tchs+2lrG8dAo#riE1~*2Kq@kRbSuh6inprWDokLX`pm-2A#4V1fSl zzsv#&Tg40Uh8`T*1ga zT=F**8|;T=MR7Hl@K*pa?IE?sHxj~dUvi#bgdjO7Vp_*QC_5|R#Tp1Ch6+Stbk^Da z>+jBw(YSc@Wda-rjNsW@C?WI);YETFavp-K>}L(^C7e}c1cXp1ds;zPTIKISn@TtT zt0h9Ago4x>^Rf^S13Az3j4up&2_Vf;3Qya=t?>7f+q1qf$j-lX{{Pr2*HJ7?_g=y6 zbDM2^qS)AA1dKi}U|RmCiD=D}Xego8JnL}&t%#-avWMX53h=v)%Np}-hY(pCcQ?t@ zg`~BwskE9UC3ews9%p=lzt2Y+*|=@AED5YA)b}I>gYAvfNRaY)|Uiz|J;EOoC!hJhQrQZH*u)WPv<22 z#Lmv`9Xik#KOZ5ZR*DFu-)fsD=c3$^&6j_gw-`1w<2Jlz+ybVQ!}e#YOTq8^0Fq@P zviW+FNs|p}Mkk{aTML)tm$oGE+GeFl5g&4rY?6x97Y9y1RsqnqB_fKvz=2<^gWWE+ z*@H!Db9nW1l!CukZg_=UtDK(=Ld!)^Xkn=zb)xtcYGMN5A*ekMUYfeA1AqA1V zAJz~0NQcfvzoMFVA*O|la80mn-N`D;Z!Lu-Rg9EYk!?R9yvVlt##2lTTxgk*%Hh+B zJGP%BI=l`H-c5djb>**`AAC_dwy>d>plCA~_Y-}~;cIE_30M~^-_oO{ zmjUck=gN0OL~84@r<^wxD%F#!NjZA#eAG;=;*HAaUh7O$*FkLGXXm1O1`PODuNmN8 z(Z&*6h2?^&m?6R-RxX&}W%zr>jD+b4Q8#Ms`L87eYAcB3+sC8bGs5wtgQc$5jSU+^iYu}q#4!GftFB1ByQQRHc33cgg;>;?tn4!b98 z>VvWNfWR+3fpl~vjhjU~jZR6u=$hwK8!uK4pI*14A|B!dq}tbNT4$=8W@L0^m}efd z;B_LX2=Z%uj97m<@)e8D7O}ZEIw(l~MTJ;6D}K~!=jZkZgCa|pcs6n^NXM5wj-~j- zVe!GgdgSnB^s6-apgXJM{iPnqEHB}A!$#uTm{WN@6O`&M12G18dc!SQzDjF5Xp?RM zRU2Vn1}F^4EtuWVJUG*5=+CpclBtBV+=n@udm9v(9_+J6mu!CLRs`8)hq{P?zc8xT zv%$XQzsj^5+8dj5N1AVF`_l}U2#@SRq!_ZGd;M~?N$myExyIBQ&vf<@>ag66z5%tL zrRIN8XF*wVDw|#G_2IG#o)aV>65R#gm6f$F&PcuY>hB4UYAS}wBE^1AD|+_=bntxT zWw^Di=Dwh7UDc-lnoSWD8Tmd#Lf!M%@Pjlzam$e*m#TQ-_DL3ePcjuI|2Px>#uSWY z!Qg=@jIa(vuyECI3D0POUNT0tNo(WXqsiqPOb!9<*>=KDbMZ<+NC$tqmTs3xv0FH0 zn=_im4HI+%^nOBfU(B~)YKCD&7|>CpZ-k5y0yQ2dKW6IJ#7};N4K_I}=I=&)zA6hr z;ML9TP1LPE(h7v5u`OQYMiB7T*yGpIX6lpLBt(E_)YEF#cUU6)hmBc{G+9hj@s_~0 z6V#YxVOQ*I=irn7j16{U*CNrt(qy8MMU%Urg&3kiB#xWu)V3_zJh2F>aJU>|9SEFV z1h{sn z6_M{CB$(4CaZx8P%e~R5)!v;dvU9av7e5sm*3zlIo43j_X~m7-FCYmE2WzX7ZM3p= zO8-(?V*sCZdEA-)04HHsSFCmMv?-dF*|VRjln9(Ec-QweExbD`h-S&-G}6_nEXj0( z6R~K`0H`>?c`ajMDW|QSgif)m?nCbG9b1inN-|L>`EGtG4wBEV=3gyoltjOV*0tiz z?AMT1bH23tnSsgawW(UFupTkfpbyDjv)GjZ;;t(eXcSK z@;P}58%c#xG`FBsZ2U{n9*yjC4Ma`mDWjxqrW{7!cM1R4lk~^yD7+{vf==^ApQ}@I z2Z!GUp0COt%5>USF1cAM(4MBDo$$vHA>#MA&7Fc91kRt4cxrqD_Ki0(yxB9_-f6Fr zyClK$T0rJ`$3{7EX{QHr$63{qS5)nsA_3yI&Ab-e(1Qy4`%TKWp12Cr8sb|pKG6jC zfOIK(33+b*sFDCT_h50q;k9THc?be;>RiL*+flfx7uD{z>)Td^P0gvlk7j9Ym>wNs z54@WZIqY3;EG8J6|Jhf4DgUxY!hKdNMRcwYV^KaM05 zaNc7T{VO+zGZUxiyTGM2eyHPz=J>5fEeryZ1Zl*lTx1BO$hS=`A{)2*K|ETR_`SF;Rr z3-@ses63TkLS1|Y(IW%7g#muf0#(l+UyEDPjT89#w_mbZXcy;;Z^G%V7jwMoJM*K| z@UPxg?9S6&^QX<8Z|Zug!BNO_C$<{zYe{3dK=TSb9dZw2x(dEY+9-Q5$gZU zhZcy5euVA>hK!X-P_2DQ?T|D(_v_Bg-F(y@fX6br=Y9a+BmWZuxwM#DAQ&j^bv(=e zO@z?kc5IP97iGb=d;hxI8zzvzr`1ZV=4NKEccs<7`1eRC{m)&-E&7u$(jd7QYWV9L zFn>9SCO>h-Gx9H{mo@rOo;1_Uz%0Z=CFEZ$X)s^VmtO}$>Hh%j@)4r0Ej@G$`}+aU zj=x^~uY6u2`j?6RdC$&a1JB)|^H*GxO!M_*(r<-^CAzswy zWB$VfcZ|{BZ{+`_*Z<1r|KW1~2kHH%TdI?kM#5iv{OiG}M2h2=LM8DKpH$fV6}%)# z01mJKC*-Urj#mzXL{_MG_p7DYMQcFn1q3G=)gYHbphtSQ2)%>Q%{Tq~ZRP4mFW`BH z?_Dy=5?y)SW1IyBJ)+yfa-VUkKp3>Z^G5*hqUKdV35rL5?fg;zlUsec41YayAAXy+ z#|but19SwKH{>#iLJZG;;dkU#H9&_OFtbOH7C@)|08wxdGTP&{$m;54xFZLC>Xx_A z5I_O15;DpY3s2!`hTmp~&B&ikrI|-q*SRsHfFQR@2ikdcATxTz6_B&xUxDEd&xSLD z1gOu3uaR2?o((rsfLwY54Z#mTQ)}W14PcDZb*mcS@84GV&xsXS2Kn9dtDS8}jI#Rv zTq8uLa-VO9L;{8z*EQv2h)2JdyjifOPSPYJbiSKQt`O5#9N-iq#-s=NZZ?4q{Y2+` zdVL-KqA_a(_n5y&K)WqX+8cLrao0fiSCVV=8MCU{$S{J|^}(IlgITs$5@NHgNt%@Q ze^2TRQONHW%gc5f_LG|}D~^_@JK#caoN-j(2MF;bH;Y~>K1y96a#;7@M%lWl+sst0 zB`J&+*akWbfd3}tA~?z5sz8sr03!D@Au`Jc*q1UL9PRMxPO-!s=_CiOS_uJJg^^Ge zz39lqit2tr!#A-N77#_|05OJ3WvTAbj)~AjqsbbyYBaO6+ksvDX5)%(rO>8oodTrg zi!dlf+B_lNLeqmwK*u7)$i zgDPCMogG%@FgZn)9)Hn0bdM0FRZ8Stj?p-oy<^>tiBBAO@z*Xj`mk18gMxutYfyir z(n4l2vmpl}Y2tQCNd_zZUpxXEV&l;l2s4(sX=+7IrN-=cJBRO0DuvP?(1Y3VOw{CW zlTJZY4u|=#@5tKN8RzwVCq!nD;f2i;o=wdC#N@u$_l5Gid}P+6eV2TLOwQDcCtfQc zyQ5r){$R1;OuIGteYv&%gYEfZ*++$I1&psoI8^GeZ_OOx@bd2gU0ELgfJp|q<&A+Fm5wrZ`uaY;r2U&e z956vT2^d+vcg#*-fK}0)t|e}VrQUs3-_lreC69zu`tjdWyF}nuQ z3^GMsz(5=XOe$AOD*^Kp7T>=}R!}{j`fq6gU|}?9RZ5BKJYo$53q($}YIi@6IKLys z2j74h3OB#y4#8ziA>~bP7+Y5@iP~(Z_JdKsfY9w+_G6$Fz4YlQB2iO^0_7H$f$D*l z+9Z}Ik-5gSVz5miS7}1o$ui}{nt{F3_$HIQHHVsFf6LNQm#0x6XkAE=~ z9;)`Q@6w0kH;;h|8Y$SgOptXh#0jxni8;bPt66HP*z@QWxa7S|u3pdc5@JY8nMEmf zw);M^qcr=lB?7Sf*v9B&g)0AUa_oMGk*CoFDyW_5rNd3|(pj7dSkx(;VBD{Fn4M4& zf04?*gvEN68*(10o8Jmtq>f#C3-gvGbbowS_ z8^LBjOEJ(XQ~SuG^VpaQ<~WD-$a`eUTfHr z^Io?ckzKXFrnfjkH_BEwIk?r1n+|Q4fI#W@N`wp=R4h_o!wk?bfoT61h!#(6KE@a5xX ztfEWr`ZaZ{=b@b{x)vMiB^>Cu6J=^bgU7_e4N~iA-aevbqv}-(Tg@N*KbEhjg;s_k z@7P6~L?VP(INcifYp~LL86%zpcJ1O-LU%HTB=g9xyhES;z59TppVV!{Vs3Y$+-jL3q*C84GC`4 zII3E`WZGxTY2PDm?S?A8~2IBMTacr84 zTO1qAP5t^4x@PXKy-#%w@2hv(-d&pxcnf9!<($S&Iyrq7pvzG$Pr1-21mq)I4@sJs zIf!XN`{VC2Y(CUvOiz>t;^=IF^RGSYm)YAPsr`%TjvORVM@haM5dxmrpZa>Y_7#wg z^8I-T2{kz=;bsl^nSN$4Npv~;e~zqhrxjhWDu3-G?+wiRlny7U8x#m7{Glu*C02(; zt6Q25Jt)13$D9BITD_=q=FZO&Io@mVAEGo4V3*T*^C18k82!-9AVmeRf@~o4$$CVa zGmvN|H$t2CO#&UpD9%|l1=Sq~lJY_l*Wfthn=AO>O;OI7aAF!5kH@KiR60Bvx>xmg zDv|G}1_}L*``iF98YMM|0)7SnAOarUUaCRTD*o<_n)}jKw)-yMJ@_|z2IQu?3fz=g z>db1axW1mM4gev$2>Wv|%-)^3p)Ds&NWrG^+unVD1qkT*FqQ89n#w`3crTDa;v0J4 z6UftS!@Fsx8^^E=^YpVh34bsaxLB0N2~GKq&sWdd`YgNug>mtTNFff1Q!MO00LeZ< zS(WD8&Q+75*h1ASfBXj9CiM123?z(c{%T_LQ#Af!!B}LM-%)hliyHpkTiO6DRDajl zI6*&|ll*L-W^OfAM#&~8Uy_PT#Z?H-fW#IXGgFFBaYjc94CvB< zpZMb(gb2D5h{xgoL!A^jQ;nZz%Qp!OLd?mYc%28m1Q&D1Kt)gVSFh^;Yu{%D76+ok zunE6p7ZQImMuO{#RQP?4*cII1OVMM4fgoqQ5j;__WAx+G0+31qpva(c(((zLQ^s(e ziQEcMnQy^Yu9MXTWT9y=VXE_fEx0;wYOQZm(f(ZUDhx6w$5{K|w^ zmWwsd1YmrHCtM%T033W7;jf~^-z9^;8rWI!LcrHI@NEC5>~Q0}>hNC|M1VB^oK4U1 z>dZxZ5P|>>6P_U{cjn3oHv%dF9tLZ#L6u_%RK$vEUj{mK5`aiF?q^?GrqlIbNeqG# z@>~ay=+W2FP37Q+w*eXeG(mO^ktwc%ayingBD+8Upyc=8OI4uyZY9)VtobS%C-?XP zmB5>yqRb~w0|6GLbqRYq^Wu{&2Q2u?jc{SHc2YnaeWT9 zW!wY#|aEKn=Rz(5?5TqqxaM+F>7631Gnfc zFT6G4 z@=LxxsnloC*3(mC9c_zlDNncQuA`kNJFIw5yhU`BW)~-ZXSIy^!^XFSQ|sM5>5Wob z?o_vtw%AmIXPYg6T2f|F6%Eopm=CTS8wjn^xyDUwr`At&WX`m*GCwf(Q|7UD&w2Px zCQDKGs`L!_y$s*1tBZSL!W01A_58lagVh59u~p7WtfjfNeIhURr2lwH@5|`jK|fRd zImy(D$siUF$QjzYH(s-wnLoCzE{M?y?q-JnfI=G z^q{qK9yiT?MCWHMqXDmmA3alLuGt76M}Wm-&J#pt#gFLh(}i`{Nin{tS{m&hLPyE> znKU<6$lEP?15x|A36g*%y_CANSL*6(V_jHvcXH?I@Z$A2(Q*UJf9+FQ$6e`YxYv`p zIz0TUZ&jpd;Zyrklv5zy2b@{se@TNG%fZ!}vHS1VHOcV!K7jCj3pW3^6nN@YUlGP} zJ%yp}0KBfPS%|lN+WCkM1{G+oJ8NmO8SFq^ugu_T&d#nqAQi0D$)$ z$Wk2QS;9>vx$F|B?`qfmbR6Z#TJEJ`@pS98OzTY)*-b9ujNAh9ri(B~fT#_LJl~;8 z3)?IoZKlOt9gJ*?u@IPTzh0(&#A}iWZxH096Mqz~458zXti}@2S+amSz^nR0ymi+_ z$`8{VfvW1o+=JZm`YQ3u^?v?}MMZqO`ZeP9rj8p2F)beBZU-?mxqir>I?YM_t#!mh z%xB)^H4U#&vgFLReqvlu#p)7C)1Rio>_~>!l!C)WWD`sht2NO{&5YE}*;wUhOO=Dx zmncEiis9ny`cs&}R>EEYJlN_-*_L*KZLCc1Y*S>a{6-y>y=^r=Og5tssy ze*)sZ@2~eXCbnaap$&-ZAiI$dYFp0o8N3m#Le-UEieI*Y7Xg19+ZVyE@KQZa6>`b` z)0|#A!z2sYW;p=8AAbAU;vM^wNt3t&zIY$$!e?mh!}hHMfbjp-Im@O$yb7Co%e7 z@NJmS5WSB540~H+?Y&pHz6*s>06w2@HWHhqLX&Xg53863(u$vZ5NI?BAI!s&UT;0m zYW5g_)6zw}NNs*oROxE(et3+v5s8J;o5UiuCRoTfO93Kk>ckpskIfV+0|00fs3A^W z(t>QnxS#OW?`hc%Pd$WOicHvq`p8^d?)p<}0XsfyIiZwt!77lm;2L_mG+#S`9H>1B z00pHU^sQZor+%3wO9`KdD;`J^0aHDJSp18BulR;fvP|$;xxnF%(xWX^G4CTM!U@2Q z)G$;)i@AW0Cf|tw`*?qzRQQZ=a-QqXL^ z6VHl)urV6?{e~Ef4j5zIh?F7Ye=Kh* z!rm)Dct{y$weI2F?5tzwkA)sM+fEXtXPM|Og&=*K=O0Dnyk4Z-4*e0&zt-I#5H)$JQXT(nWZB;wDfRn1Sawf#=i}#HxW2 zs8=c14G({9PN$4A?V9sfMY5dNF7E#k?s0F2UwF&xzzNutQe)0uw5u8tm-JX?61aVV zuW*OD#5Ny1KH(DnEO}=Tvlu`u&}kB-$F?-df-jw4XK}<{6#m|kF1u*bf#|y+_gMxq zXd3Yz2I!v^ znu+S+$4S144SPe;2Wh+kb9~E_VNpDu>47E~s!IUc`+>!{>YQ2 zlLFvr5>51H5qpCx$2PvR_ig-M;%QyKr(r6c?1$G9zq>J?)mB>a049@jwwXPi2BdsF zc88#@6HwV(se#_IkeS<%6vpXC`Cs6Gi+HGXS;GO?Ox;RNQ8`tr1(~e^o=xOm!~nfC z9#*ld=T3bSU!+qkQrU=CloQG!CV+}*ExtUPZe!6XUIW3%uYj6_L-8|zRXLFvT;FEZ z6%6l@e&D2AA7MxJ<^wzIj0&DO+#H4?BY$dJJ)xBqB8I0>?u>0?#+~Xn;)Zhs-NNu| z5T7mK-x;bpitq>XAIjW31!To@YdUy81o}Na=|gA$*U(KhyXv5SdKYVTtOZ%BS_2i^ zzo-Y(xk>s{;ha-bp zo47dJ_ju|W!saPJ!LpKR;5n)!E1g*j#nbGbf0*JLE&Wp5VOkJ7QPe#WCKNY0j^8LD z3K-J@ZyoIWxzRj}d(iPuBWI_*?yCl4q~f;E46^L+y*X;0+_~o(+L{P4q)@*;fNA)L zGp95{2r9qX;0{x)!tAgl_Y=x%=kv?wD@nfM73U1N%s9;RFKiRp{#k~x$lXn0vJ(2I z*>+`l%&=KCIocnMJ7&lFx~l*4j>#$|u5_&H(82GT>0yIbGhloFQiy0*>zB1yf|Z9! zEwK4>tzzKQa(AyQNiH(aV-ypVYwDv~2BuWz6N918PpBHw9=6-QK9pIvgykNxL5fe#!77|hzbl-- zejqjecw(*9C2F0Mc{3AjreS_26}jRx!9rCjS8t?=gAeUTM!Wzs@)p!hz%vfzotETh z2jjEB*3T>z`@?GVt!FZliGF3xxHSkQNfr{bnj0a(%__Y*-RS~{&)mnKB4=N5#fgKO zL51R5%oVMBw|VZItL#cWMdkEhSa4k@bA@!Dj4YQJY~lBoGAmayFthc{Zb8HP8qU`h zH}H}VeG$j{QZFaJ<6=ZP4C72x7jl*@e4jCA?zghz09)HqKaF96SN%4#r>+i}mD(cx z6F0vGBF|3v*GQ9B74DU4Wug~e{WA8%A8_|Un#d8sA^j1qK#KDNM&l}zS!ueeYZ&zH z;<-l5q00H|x1Dsx3D4IxFgr`b9KSHZ?%nO?^O%!Z_zuusbbhZ%txx9>w&rdvrUi?_ z)-G5Bk^~jui}XOPx~9m8>2x;|*XS!F=H@#Nu%dJd>U=i_uz5drpEP5FERP>lDahn4 zkPeMfS+cUhtjrIMLthQp?)3X+I{jw7_x@~5OLUWc6Z(#ZH=gEjBUW^*)a!-T4ZG;q zT5}$FyE5W(S#``TM zHqAqh@TPY{jsrCifpln9m}%kKzh)JA&SSG2ala(vL}u$YZMb76g%#k9fooa8E%#5; z`zLNAEbXq0ecL#y54VK2Nn-4blTn@(BG0FiALt7KA>7vOF2 z=V9)AkYQ%##8Gc;#B^b z5=BeP_|mgQ42#*rUrx%?P^qVrt{Cy>A0P?5<2 z=KYXu4a!NSc;G4iEw2kXqM@z>x+)D9AhfWI^BB8__+JP&` z1$qj4f>QR!Drx$}S7(*f6Mbg{L3X{Sn52-nx?N0iqvkW2*=ttwP$v6&_ifYaC)b2# zl4!m{{iTXcTM8m%61=1aNq_+j;oH^ae6ho}3%tb<@wxI7MXC+y=vk_-m86&9i^O~| zy^;F?GJ|NDXbCo0kGRrmK#WN;qUsk;{Fh77992PV-_)0fB9kWDs|VWy5DUoXR#Jh*#+EoP~GOB%muoK82WKf5Y*-XXm;#b)muhfICtiJa&FPqURX*~Sy zWqc(fX%c^fjuw8FkG`N-{igsWy0L1e)h8Q;5W{LPHWnThWXi1+z0DXWTH%=J1Goy@ zwu#b2EA$H7D*&_PDng8(ZdJQ|h)xq=sRUn8Ucar=ajf5;OD~}XtE(I>30QcoWtyeOQz z-al8xTGL%KUbRI28cHHeZ}@My>Bed91s?c2Jy8{D+419hlbHvmzXaJL#vvCGr$$$ z=it0b|E}Vb>WtTY>_v>g)lCuIfL9fJd`%x{$O>(f^87dC zy*E50Z<}lTx>52 zQ|onID1JPMDSS{DE$Smsi+Lu7Zo*^Q08A@<*HTt4G}Wnd3_HL=y0m)X8ZfeSqt7Z% z@|yPkDj*Af!i(RmJA)Wfun7S%>i;uz^1p*C|4%lA=Zu86$~jbnN%{3f=TMipd-AKn z^3;IgwxQ8fe)A@z|HCEmn;Td+d(QM8cGwTiV$vczs-O=^)p^33t>w}B3KKYmGi)(f zTc-iQ7rvvzxHEw8mW*=`eKfHd-1_j;n7Ps+1-N|{1z;m4Jt&O1{)tWl;7`33Y~UU% zZ8-Oln+4^>1o$}Y^NaDg0~dHCfA8yoV^|?-Zmo+5)igEBk5Boi9bRUNq`nU6kvlTZ ztW*_lIZuavJ>~*YB+YytutEzPTb;?zQL#Db(YtqzH8@=yuB3M^f@wC{R!3gDm4l2mp*VIRSmvc-~j^h@Iu10FavR#dOm&g9IOZNYjKJd7L&_iT`L-JjG)FvvpWyDyO5w@iA*;D2YhyU zzgdL1g7dsaJVFhRhmvX3IN=OsA%=u3keT*V2MUng1y6!trvJw~-q+b5V*$~h6Ze{x z#l+f&_-QMi!2cV+=(U;9WP?7m9MK!LnVK~mFe})aGn0pa0&c)S4}pIV4u5e-;(iK| zVN!P%MUMxGhpvK1UT-82O(>{~{r$*5bX1Ek`D(Bo_}_u;Ux8<^!vY8Nf-D@?O$utm z;z(#h-1ENZTeeoTD*Jitsu)|EJ@OW*tcmVwF2Bp&aIiPPT&JVsI=R({6|s5PvzIhYwPW3Z7FsWln>4&2XG>8iTT?f(Gw0LQ~f=89cRl%BA4 zLWK$`mTYV@f<)vXL@(ZtJW!HV7k?>pa!V;PBe&^btI)PyF}Dr3yfJ``9etpNsI+vp zj(3e|Gn?sBx8?QTc5#vNwQ+p}_J8++s4m-^W$F+mhV#>p6K-_><+st`nLNZb-emfR z)w*g;EnQc%TrMI}M8WHOOvT{WRSJ08o6xYAAP$`@DZC0$D0 z%*013pu~?B0Dr7GX@f3eGvBtU^`1~-h*#dtKSYqP6t{lo8aV9~@Q65HNoR3fmS5B# zStPA2fadB6H+@ySd&M?QF>ToDrMZ*G?w9I2oDDT<0DfL^(qGL8CPMJiw0O+S}-z@f#53jgU@n)Iaj`ZpkJdq6JZWJP1NHaxvl9RHIELxe?6G_a(8)SR*uxid0R` zM{kH{RblneM#db?A(|eNCF^I6Abe(TO<)g3d>|Xs*w?S8u$!xqEx6{Uhkr36K8FFhp5n>�@o+ z$94L2T%GA=fZ3kcTCQ^A)d(~!on9f>R@zuhc)X$OPG@d%!DV<3j^{22{QVkS!n{;A z_L{2qxta`6!#NwXQm}`z=67~%&<+C$rM6e-BJB#oO0#vga1U?u*WnJNwu@6%rdlnY*R;1X^bH7>a%Tq^ zGSYCFFtVjExG=zrSJJ#%FT*AAq@=umKrX$yPti=&m~f^>03|e85`#=)>e9Ni(HV2A z;y2TDCDT8@u*{PnyF=2&eVHPx_JWm1r84eE+I3LRPBc3W+|yb?+yO1VGLM!_NNOj3)=itf4d+u;VP@m^LYmMf97?Jz3+YSfRTgX*w4+#QpP%L6 zl@{}B?ba1<=G{*)*~~UUVVj2rvG0Ip0!94f{)@*cq`dlDXwlcYo0Y)n<`GAOpM2< zUJm8l+dO^v>+k|vC;q-`(;-wXhF9p*;kpIcs`=R}H8Khfm+5V=B zqh?Yo_YEWB3jYlZRdSW0xM(S--&O6w09xct!Pvh0(%q?%)@U*9dI_nRjyPqEqZo9?<(Zm!pb1_ldAK<(ibRGy5+Fh*f02XI5h!BBB-mW2|~|w|2r8 zV!>C8$nk6Yd@M;REfnL80CS8K+as1XI|f#ntjeDv zR3|-(mdln}5>xm;-L6oP>k;S3$sRqnaa`*`*osv5CnRNS@ z^PJ2s^eV4nm7QCT{RUTU8{>`An!Oe;`Ut4mU(MR}pWtX*$rw`0sqQhdY+mT3V6tsF zWS%W>VIn+oPy6X6dT45m3%9Z+=kcA*6`=zk);?gB5)sc}k!~0BMHt5*!AGcype(ht z_E8|W!$2*Yv9tXtS%?qPWq?~W_3A3$kOC|7CMAt+`~Qn;2pm_5BTs(N_`-l`^#q zS@7RkjzW5@Mr#?m$G*%iDK|W#*8&-9q}xm9?$h)$?zWv8roZ5TZH8G7q;@kEN%KUi z3i&tk?=I!Oz`kON6A0s+J8-pWkr*gRsgIC0^%IV->t$Y1%>!&gRF{$UR}AtsmXrD} zF|AbTx$i3PBkzml62b1cWUDOZF!MHES83lW7-IEjN3TyIShH42huo%B9F$2g{44oC z91%E;MZ@wAJp}aq3|3$p~!ndEg+rdA5-k@oaK9aYewNAMtkhGN?#NB##`ZM>~=VZ`Cze zLjV_cP@b=rB0dbbR=*Ti2$gLG(j|*80=NILTMjbA9kDJg<@A2BUHWEP;Pl`@wAw4o z&pSm*kOa&J_an>gnq%n`Er^2Hgu{ul7~PW4Yv7yPn@I_zic6dUWJd|@N!-t)Y;52+ zVb#hl0~&+B(cE%6B4A@OfNaA2Fj@IW9W1>qN7YHm< zckk3P2?x|c9V*fyv%T7m4B7qw)ZeU4mf^W+diC(Pc_&z9p8BiewJcpe^N7hWev4?> zY7W1$k?@6OO|2u@t$PC-_e3bHe4A6^vqT$&icE)^X$`~)FQzSLudk@*NXL1vdu0i8 z)%t6qcn(VkZsc*S<|?>3&Xy@b`uco-ET`DpDvF7h#2(%p=pV@I+@-5m8KpmT2#${g z)Y(3!E_tWKL(0?tp-&vA)FX_}s3%=+Zv*bzTbp}n!K_EoR7*ea*FCoTP-J%nUd^6g zcNFXJGEKL9ym8>YI>W-HdP13y`Z-#;L8N-tN-B6y&T8Y{j#B^f$ zogTH~fOs&|;DiI0m@xR=${L1_^e3*$@5J1(3EI9`m^R**ij3Rb2&I1u{Vq0r)GQz# zujkGry48&dNh?9r+R7al33Wa%_U{U`9j#d3FPI!y(&%zwI~_`~KX`K|oKm7mkcP-#JB2Iy4Z0Xeul)x{F5Gcv1Jt}_q?Dc;G DhX7Om diff --git a/minos/validation/utils.r b/minos/validation/utils.r index 4de4e9c3..af452d6e 100644 --- a/minos/validation/utils.r +++ b/minos/validation/utils.r @@ -7,12 +7,16 @@ # Function to combine two dataframes and pivot_longer for further processing # with ggplot or ttests. Must be either simulation output or raw files combine_and_pivot_long <- function(df1, df1.name, df2, df2.name, var) { + # first set up vector of missing values to remove + miss.vals <- c(-9, -8, -7, -3, -2, -1) # get only the columns we want a rename df1 <- df1 %>% select('pidp', 'time', var) %>% + filter(!var %in% miss.vals) %>% set_names(c('pidp', 'time', df1.name)) df2 <- df2 %>% select('pidp', 'time', var) %>% + filter(!var %in% miss.vals) %>% set_names(c('pidp', 'time', df2.name)) # merge on pidp and time merged <- merge(df1, df2, by = c('pidp', 'time')) From 6536b40bb5ba6e6eab58c2dcb38a7b6b237e796f Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Wed, 17 May 2023 11:08:31 +0100 Subject: [PATCH 004/229] Fixed typo in outcomes/Makefile where AGG_VAR was not supplied as an argument to make_lineplot.sh in the treated multiplot --- minos/outcomes/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/minos/outcomes/Makefile b/minos/outcomes/Makefile index 893f30a4..1fc5aafa 100644 --- a/minos/outcomes/Makefile +++ b/minos/outcomes/Makefile @@ -51,7 +51,7 @@ aggregate_minos_output_treated: TREATED_DIRECTORIES = baseline,hhIncomeChildUpli aggregate_minos_output_treated: TREATED_DIRECTORY_TAGS="Baseline,£25 All Child Uplift,£25 Poverty Line Child Uplift, Living Wage,Energy Downlift" aggregate_minos_output_treated: TREATED_DIRECTORY_SUBSETS = $(ALIVE),$(BOOSTED),$(BOOSTED),$(BOOSTED),$(BOOSTED) aggregate_minos_output_treated: - bash minos/outcomes/make_lineplot.sh $(MODE) $(TREATED_DIRECTORIES) $(TREATED_DIRECTORY_TAGS) $(TREATED_DIRECTORY_SUBSETS) "treated" + bash minos/outcomes/make_lineplot.sh $(MODE) $(TREATED_DIRECTORIES) $(TREATED_DIRECTORY_TAGS) $(TREATED_DIRECTORY_SUBSETS) "treated" $(AGG_VAR) aggregate_minos_output_living_wage: bash minos/outcomes/make_lineplot.sh $(MODE) baseline,livingWageIntervention "Baseline,Living Wage Intervention" who_below_living_wage,who_boosted "living_wage_treated" $(AGG_VAR) From 70245f3e42373a902ae2a287f6d1faa9fdde89f3 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Wed, 17 May 2023 11:09:37 +0100 Subject: [PATCH 005/229] Added a variable for the save.path in cross_validation.Rmd so it only has to be changed in one place --- minos/validation/cross_validation.Rmd | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/minos/validation/cross_validation.Rmd b/minos/validation/cross_validation.Rmd index c7c88656..84dacb99 100644 --- a/minos/validation/cross_validation.Rmd +++ b/minos/validation/cross_validation.Rmd @@ -40,6 +40,8 @@ cv.simul.files <- list.files('Minos/data/final_US/cross_validation/simulation/', pattern = '[0-9]{4}_US_cohort.csv', full.names = TRUE) raw <- do.call(rbind, lapply(cv.simul.files, read.csv)) + +savepath <- '/home/luke/Documents/WORK/MINOS/VALIDATION_PLOTS/Cross-Validation/' ``` # Analysis @@ -69,7 +71,7 @@ plot_mean_comparison(pivoted.df = income.pivoted, var = 'hh_income', group.var = 'scenario', save = TRUE, - save.path = '/home/luke/Documents/WORK/MINOS/VALIDATION_PLOTS/Cross-Validation/') + save.path = savepath) compare_boxplot_long(pivoted.df = income.pivoted, var = 'hh_income', @@ -108,7 +110,7 @@ plot_mean_comparison(pivoted.df = sf12.m.pivoted, var = 'SF_12_MCS', group.var = 'scenario', save = TRUE, - save.path = '/home/luke/Documents/WORK/MINOS/VALIDATION_PLOTS/Cross-Validation/') + save.path = savepath) compare_boxplot_long(pivoted.df = sf12.m.pivoted, var = 'SF_12_MCS', @@ -143,7 +145,7 @@ plot_mean_comparison(pivoted.df = sf12.p.pivoted, var = 'SF_12_PCS', group.var = 'scenario', save = TRUE, - save.path = '/home/luke/Documents/WORK/MINOS/VALIDATION_PLOTS/Cross-Validation/') + save.path = savepath) compare_boxplot_long(pivoted.df = sf12.p.pivoted, var = 'SF_12_PCS', @@ -178,7 +180,7 @@ plot_mean_comparison(pivoted.df = nut.pivoted, var = 'nutrition_quality', group.var = 'scenario', save = TRUE, - save.path = '/home/luke/Documents/WORK/MINOS/VALIDATION_PLOTS/Cross-Validation/') + save.path = savepath) compare_boxplot_long(pivoted.df = nut.pivoted, var = 'nutrition_quality', @@ -206,7 +208,7 @@ plot_mean_comparison(pivoted.df = tobacco.pivoted, var = 'ncigs', group.var = 'scenario', save = TRUE, - save.path = '/home/luke/Documents/WORK/MINOS/VALIDATION_PLOTS/Cross-Validation/') + save.path = savepath) compare_boxplot_long(pivoted.df = tobacco.pivoted, var = 'ncigs', @@ -242,7 +244,7 @@ hous.pivoted <- combine_and_pivot_long(df1 = cv, cv_ordinal_plots(pivoted.df = hous.pivoted, var = 'housing_quality', save = TRUE, - save.path = '/home/luke/Documents/WORK/MINOS/VALIDATION_PLOTS/Cross-Validation/') + save.path = savepath) ``` ## Neighbourhood Safety @@ -257,7 +259,7 @@ nhsafe.pivoted <- combine_and_pivot_long(df1 = cv, cv_ordinal_plots(pivoted.df = nhsafe.pivoted, var = 'neighbourhood_safety', save = TRUE, - save.path = '/home/luke/Documents/WORK/MINOS/VALIDATION_PLOTS/Cross-Validation/') + save.path = savepath) ``` ## Loneliness @@ -272,7 +274,7 @@ lnly.pivoted <- combine_and_pivot_long(df1 = cv, cv_ordinal_plots(pivoted.df = lnly.pivoted, var = 'loneliness', save = TRUE, - save.path = '/home/luke/Documents/WORK/MINOS/VALIDATION_PLOTS/Cross-Validation/') + save.path = savepath) ``` From c427a7b7b22a1a5705884563745a72a20bda10a1 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Wed, 17 May 2023 11:28:04 +0100 Subject: [PATCH 006/229] Forgot to add PCS module to cross_validation.yaml --- config/cross_validation.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/cross_validation.yaml b/config/cross_validation.yaml index 119cee8b..73178765 100644 --- a/config/cross_validation.yaml +++ b/config/cross_validation.yaml @@ -26,7 +26,7 @@ replenishing_dir: 'data/replenishing/cross_validation' # Similarly Mortality requires Replenishment but everything else requires Mortality so it goes second. (priority 1) # Finally everything else can go in any order (priority 2). #components : [MWB(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Income(), Housing(), Labour(), Nutrition(), Education(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] -components : [MWB(), Loneliness(), Tobacco(), Neighbourhood(), Income(), Housing(), Nutrition(), Education(), Mortality(), Replenishment()] +components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Neighbourhood(), Income(), Housing(), Nutrition(), Education(), Mortality(), Replenishment()] scale_rates: From faf7fcbf8d5ce9bfa24be69568028208c754d9d4 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Thu, 6 Jul 2023 16:28:44 +0100 Subject: [PATCH 007/229] Fixed some references to SF_12 -> SF_12_MCS, and fixed the PCS section also --- minos/validation/handovers.Rmd | 53 +++++++++++++++++----------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/minos/validation/handovers.Rmd b/minos/validation/handovers.Rmd index c485a821..915696e5 100644 --- a/minos/validation/handovers.Rmd +++ b/minos/validation/handovers.Rmd @@ -41,12 +41,6 @@ source(here::here('minos', 'utils_validation_vis.R')) source(here::here('minos', 'validation', 'utils.r')) ``` -```{r, include=FALSE} -start.year <- 2020 - -save.path <- '/home/luke/Documents/WORK/MINOS/VALIDATION_PLOTS/Handovers/' -``` - ## Data ```{r} @@ -58,8 +52,6 @@ out.path <- here::here('output', 'default_config/') base.dat <- read_singular_local_out(out.path, 'baseline', drop.dead = TRUE) ``` -<<<<<<< HEAD -======= ## Constants ```{r} @@ -70,8 +62,6 @@ create.if.not.exists(save.path) shall.we.save <- FALSE ``` ->>>>>>> development - # PATHWAYS ## Income @@ -228,12 +218,8 @@ ggplot(data = sf12.m.2, mapping = aes(x = time, y = sf12.m, group = source, colo xlab('Year') + ylab('SF12_MCS') -<<<<<<< HEAD -ggsave(filename = 'SF12_MCS_wav1.png', -======= if (shall.we.save) { - ggsave(filename = 'SF12_wav1.png', ->>>>>>> development + ggsave(filename = 'SF12_MCS_wav1.png', plot = last_plot(), path = save.path) } @@ -241,7 +227,23 @@ if (shall.we.save) { rm(raw.sf12, base.sf12, sf12, raw.sf12.wave1, raw.sf12.2, base.sf12.2, sf12.2) ``` -<<<<<<< HEAD +### Spaghetti + +```{r} +raw.sf12.m <- raw.dat %>% + select(pidp, age, time, SF_12_MCS) +base.sf12.m <- base.dat %>% + select(pidp, age, time, SF_12_MCS) + +sf12.m.spag <- rbind(raw.sf12.m, base.sf12.m) + +spaghetti_plot(sf12.m.spag, 'SF_12_MCS', + save = shall.we.save, + save.path = save.path) + +rm(raw.sf12.m, base.sf12.m, sf12.m.spag) +``` + ## SF_12_PCS ```{r} @@ -305,25 +307,25 @@ ggplot(data = sf12.p.2, mapping = aes(x = time, y = sf12.p, group = source, colo ggsave(filename = 'SF12_PCS_wav1.png', plot = last_plot(), path = save.path) -======= +``` + ### Spaghetti ```{r} -raw.sf12 <- raw.dat %>% - select(pidp, age, time, SF_12) -base.sf12 <- base.dat %>% - select(pidp, age, time, SF_12) +raw.sf12.p <- raw.dat %>% + select(pidp, age, time, SF_12_PCS) +base.sf12.p <- base.dat %>% + select(pidp, age, time, SF_12_PCS) -sf12.spag <- rbind(raw.sf12, base.sf12) +sf12.p.spag <- rbind(raw.sf12.p, base.sf12.p) -spaghetti_plot(sf12.spag, 'SF_12', +spaghetti_plot(sf12.m.spag, 'SF_12_PCS', save = shall.we.save, save.path = save.path) -rm(raw.sf12, base.sf12, sf12.spag) +rm(raw.sf12.p, base.sf12.p, sf12.p.spag) ``` - ## Financial Situation ```{r} @@ -372,7 +374,6 @@ if (shall.we.save) { } rm(raw.financial_situation, base.financial_situation, financial_situation, financial_situation.norm) ->>>>>>> development ``` ## Housing Quality From e6c1583d51d6b335f24e9cb0b14911d5330c9b72 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Thu, 6 Jul 2023 16:29:30 +0100 Subject: [PATCH 008/229] Formatted the housing_tenure variable to replace numbers with strings --- minos/data_generation/US_format_raw.py | 11 +++++++++ persistent_data/JSON/education_gov.json | 23 ++++++++++++++++++- .../JSON/education_ukhls_simple.json | 23 ++++++++++++++++++- persistent_data/JSON/housing_tenure.json | 11 +++++++++ 4 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 persistent_data/JSON/housing_tenure.json diff --git a/minos/data_generation/US_format_raw.py b/minos/data_generation/US_format_raw.py index 014e0811..66a6b715 100755 --- a/minos/data_generation/US_format_raw.py +++ b/minos/data_generation/US_format_raw.py @@ -321,6 +321,16 @@ def format_ukhls_columns(year): def format_council_tax(data): """Format any council tax data for calculation of monthly overheads.""" +def format_housing_tenure(data): + """ + + Returns + ------- + + """ + data["housing_tenure"] = data["housing_tenure"].astype(str).map(labour_ukhls) + return data + def format_ukhls_ethnicity(data): """ Format ethnicity variables. @@ -516,6 +526,7 @@ def format_data(year, data): data = format_ukhls_employment(data) data = format_ukhls_education(data) data = format_ukhls_heating(data) + data = format_housing_tenure(data) data = format_analysis_weight(data) diff --git a/persistent_data/JSON/education_gov.json b/persistent_data/JSON/education_gov.json index 9fc0b4d2..852a0ce9 100755 --- a/persistent_data/JSON/education_gov.json +++ b/persistent_data/JSON/education_gov.json @@ -1 +1,22 @@ -{"96": "0", "16": "1", "13": "2", "14": "2", "15": "2", "7": "3", "8": "3", "9": "3", "10": "3", "11": "3", "12": "3", "5": "5", "3": "5", "4": "6", "2": "6", "1": "7", "6": "7", "-1": "-1", "-2": "-2", "-7": "-7", "-8": "-8", "-9": "-9"} +{"96": "0", +"16": "1", +"13": "2", +"14": "2", +"15": "2", +"7": "3", +"8": "3", +"9": "3", +"10": "3", +"11": "3", +"12": "3", +"5": "5", +"3": "5", +"4": "6", +"2": "6", +"1": "7", +"6": "7", +"-1": "-1", +"-2": "-2", +"-7": "-7", +"-8": "-8", +"-9": "-9"} diff --git a/persistent_data/JSON/education_ukhls_simple.json b/persistent_data/JSON/education_ukhls_simple.json index 04dabb50..b0a8f03a 100755 --- a/persistent_data/JSON/education_ukhls_simple.json +++ b/persistent_data/JSON/education_ukhls_simple.json @@ -1 +1,22 @@ -{"96": "Less than GCSE", "12": "A-Level", "13": "GCSE", "14": "CSE", "15": "GCSE", "16": "GCSE", "7": "A-Level", "8": "A-Level", "9": "A-Level", "10": "A-Level", "11": "A-Level", "2": "Degree", "3": "HE Diploma", "4": "HE Diploma", "5": "Higher Degree", "6": "Degree", "1": "Higher Degree", "-1": "-1", "-2": "-2", "-7": "-7", "-8": "-8", "-9": "-9"} \ No newline at end of file +{"96": "Less than GCSE", +"12": "A-Level", +"13": "GCSE", +"14": "CSE", +"15": "GCSE", +"16": "GCSE", +"7": "A-Level", +"8": "A-Level", +"9": "A-Level", +"10": "A-Level", +"11": "A-Level", +"2": "Degree", +"3": "HE Diploma", +"4": "HE Diploma", +"5": "Higher Degree", +"6": "Degree", +"1": "Higher Degree", +"-1": "-1", +"-2": "-2", +"-7": "-7", +"-8": "-8", +"-9": "-9"} \ No newline at end of file diff --git a/persistent_data/JSON/housing_tenure.json b/persistent_data/JSON/housing_tenure.json new file mode 100644 index 00000000..8f993951 --- /dev/null +++ b/persistent_data/JSON/housing_tenure.json @@ -0,0 +1,11 @@ +{ +"1": "Owned outright", +"2": "Owned with mortgage", +"3": "Local authority rent", +"4": "Housing assoc rented", +"5": "Rented from employer", +"6": "Rented private unfurnished", +"7": "Rented private furnished", +"8": "Other", +"-9": "-9" +} \ No newline at end of file From f80b203fea7367a396c10840025d7e028b50c6f1 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Thu, 6 Jul 2023 16:31:35 +0100 Subject: [PATCH 009/229] Running SF_12_PCS through complete_case --- minos/data_generation/US_complete_case.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/minos/data_generation/US_complete_case.py b/minos/data_generation/US_complete_case.py index 4eb5cbc6..3e0724af 100755 --- a/minos/data_generation/US_complete_case.py +++ b/minos/data_generation/US_complete_case.py @@ -60,8 +60,8 @@ def complete_case_custom_years(data, var, years): data = US_utils.load_multiple_data(file_names) complete_case_vars = ["housing_quality", 'marital_status', 'yearly_energy', "job_sec", - "education_state", 'region', "age", "job_sector", 'SF_12', 'financial_situation', - "housing_tenure"] # many of these + "education_state", 'region', "age", "job_sector", 'SF_12_MCS', 'SF_12_PCS', + 'financial_situation', "housing_tenure"] # many of these # REMOVED: 'job_sector', 'labour_state' data = complete_case_varlist(data, complete_case_vars) From f5f42c7834de2c43f94898c0577e0e68f8913495 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Thu, 6 Jul 2023 16:40:08 +0100 Subject: [PATCH 010/229] Added PCS to a lot of models, as well as some other additional variables that probably should have been in some models (things like urban, financial_situation, loneliness etc.) --- minos/modules/financial_situation.py | 5 +- minos/modules/heating.py | 3 +- minos/modules/housing.py | 5 +- minos/modules/housing_tenure.py | 165 ++++++++++++++++++ minos/modules/income.py | 1 + minos/modules/loneliness.py | 1 + minos/modules/mental_wellbeing.py | 3 +- minos/modules/neighbourhood.py | 3 +- minos/modules/nutrition.py | 3 +- minos/modules/physical_wellbeing.py | 5 +- minos/modules/tobacco.py | 1 + minos/transitions/Makefile | 4 +- .../transitions/model_definitions_default.txt | 25 +-- 13 files changed, 201 insertions(+), 23 deletions(-) create mode 100644 minos/modules/housing_tenure.py diff --git a/minos/modules/financial_situation.py b/minos/modules/financial_situation.py index 41b8473d..b65c26d1 100644 --- a/minos/modules/financial_situation.py +++ b/minos/modules/financial_situation.py @@ -15,7 +15,7 @@ def name(self): return 'financial_situation' def __repr__(self): - return "financialSituation()" + return "FinancialSituation()" def setup(self, builder): """ Initialise the module during simulation.setup(). @@ -57,7 +57,8 @@ def setup(self, builder): 'job_sec', #'labour_state', 'education_state', - 'SF_12', + 'SF_12_MCS', + 'SF_12_PCS', 'housing_quality', 'job_sector', 'hh_income', diff --git a/minos/modules/heating.py b/minos/modules/heating.py index 7e31dc14..ec332080 100644 --- a/minos/modules/heating.py +++ b/minos/modules/heating.py @@ -50,7 +50,8 @@ def setup(self, builder): # transition models and any outputs. view_columns = ["sex", #"labour_state", - "SF_12", + "SF_12_MCS", + 'SF_12_PCS', "job_sec", "ethnicity", "age", diff --git a/minos/modules/housing.py b/minos/modules/housing.py index f33a7432..fea3e4b6 100755 --- a/minos/modules/housing.py +++ b/minos/modules/housing.py @@ -63,7 +63,10 @@ def setup(self, builder): "ethnicity", "age", "housing_quality", - "hh_income"] + "hh_income", + 'housing_tenure', + 'urban', + 'financial_situation'] self.population_view = builder.population.get_view(columns=view_columns) # Population initialiser. When new individuals are added to the microsimulation a constructer is called for each diff --git a/minos/modules/housing_tenure.py b/minos/modules/housing_tenure.py new file mode 100644 index 00000000..b145b7ac --- /dev/null +++ b/minos/modules/housing_tenure.py @@ -0,0 +1,165 @@ +""" +Module for housing tenure in Minos. +Type of housing i.e. social rented, private rented, owned with mortgage, owned outright etc. +Possible future work for moving households and changing household composition (e.g. marrying/births) +""" + +import pandas as pd +from pathlib import Path +from minos.modules import r_utils +from minos.modules.base_module import Base +import matplotlib.pyplot as plt +from seaborn import catplot +import logging +from datetime import datetime as dt + + +class Housing(Base): + + @property + def name(self): + return "housing_tenure" + + def __repr__(self): + return "HousingTenure()" + + # In Daedalus pre_setup was done in the run_pipeline file. This way is tidier and more modular in my opinion. + + def setup(self, builder): + """ Initialise the module during simulation.setup(). + + Notes + ----- + - Load in data from pre_setup + - Register any value producers/modifiers for death rate + - Add required columns to population data frame + - Add listener event to check if people die on each time step. + - Update other required items such as randomness stream. + + Parameter + ---------- + builder : vivarium.engine.Builder + Vivarium's control object. Stores all simulation metadata and allows modules to use it. + + """ + + # Load in inputs from pre-setup. + self.rpy2Modules = builder.data.load("rpy2_modules") + + # Build vivarium objects for calculating transition probabilities. + # Typically this is registering rate/lookup tables. See vivarium docs/other modules for examples. + + # Assign randomness streams if necessary. Only useful if seeding counterfactuals. + self.random = builder.randomness.get_stream(self.generate_random_crn_key()) + + # Determine which subset of the main population is used in this module. + # columns_created is the columns created by this module. + # view_columns is the columns from the main population used in this module. essentially what is needed for + # transition models and any outputs. + view_columns = ["sex", + "ethnicity", + "age", + "hh_income", + 'housing_tenure', + 'urban', + 'financial_situation'] + self.population_view = builder.population.get_view(columns=view_columns) + + # Population initialiser. When new individuals are added to the microsimulation a constructer is called for each + # module. Declare what constructer is used. usually on_initialize_simulants method is called. Inidividuals are + # created at the start of a model "setup" or after some deterministic (add cohorts) or random (births) event. + builder.population.initializes_simulants(self.on_initialize_simulants) + + # Declare events in the module. At what times do individuals transition states from this module. E.g. when does + # individual graduate in an education module. + builder.event.register_listener("time_step", self.on_time_step, priority=5) + + def on_time_step(self, event): + """Produces new children and updates parent status on time steps. + + Parameters + ---------- + event : vivarium.population.PopulationEvent + The event time_step that called this function. + """ + + logging.info("HOUSING TENURE") + + # Construct transition probability distributions. + # Draw individuals next states randomly from this distribution. + # Adjust other variables according to changes in state. E.g. a birth would increase child counter by one. + + pop = self.population_view.get(event.index, query="alive=='alive'") + self.year = event.time.year + + housing_tenure_prob_df = self.calculate_housing_tenure(pop) + + housing_tenure_prob_df["housing_tenure"] = self.random.choice(housing_tenure_prob_df.index, + list(housing_tenure_prob_df.columns), + housing_tenure_prob_df) + + housing_tenure_prob_df.index = housing_tenure_prob_df.index.astype(int) + + # convert numeric prediction into string factors (low, medium, high) + #housing_tenure_factor_dict = {1: 'Low', + # 2: 'Medium', + # 3: 'High'} + #housing_tenure_prob_df.replace({'housing_quality': housing_tenure_factor_dict}, + # inplace=True) + + self.population_view.update(housing_tenure_prob_df["housing_tenure"]) + + def calculate_housing_tenure(self, pop): + """Calculate housing tenure transition distribution based on provided people/indices. + + Parameters + ---------- + pop : pd.DataFrame + The population dataframe. + Returns + ------- + """ + # load transition model based on year. + if self.cross_validation: + # if cross-val, fix year to final year model + year = 2019 + else: + year = min(self.year, 2019) + + transition_model = r_utils.load_transitions(f"housing_tenure/clm/housing_tenure_{year}_{year+1}", + self.rpy2Modules, + path=self.transition_dir) + # returns probability matrix (3xn) of next ordinal state. + prob_df = r_utils.predict_next_timestep_clm(transition_model, + self.rpy2Modules, + pop, + 'housing_tenure') + return prob_df + + # set up list of columns + cols = ['FT Employed', 'PT Employed', 'Job Seeking', 'FT Education', 'Family Care', 'Not Working'] + + # load transition model based on year. + # year = min(self.year, 2018) # TODO just use latest model for now. Needs some kind of reweighting if extrapolating later. + if self.cross_validation: + # if cross-val, fix year to final year model + year = 2019 + else: + year = min(self.year, 2019) + + transition_model = r_utils.load_transitions(f"S7_labour_state/nnet/S7_labour_state_{year}_{year + 1}", + self.rpy2Modules, path=self.transition_dir) + # returns probability matrix (9xn) of next ordinal state. + prob_df = r_utils.predict_nnet(transition_model, self.rpy2Modules, pop, cols) + return prob_df + + def plot(self, pop, config): + + file_name = config.output_plots_dir + f"housing_tenure_barplot_{self.year}.pdf" + densities = pd.DataFrame(pop['housing_tenure'].value_counts(normalize=True)) + densities.columns = ['densities'] + densities['housing_tenure'] = densities.index + f = plt.figure() + cat = catplot(data=densities, y='housing_tenure', x='densities', kind='bar', orient='h') + plt.savefig(file_name) + plt.close() diff --git a/minos/modules/income.py b/minos/modules/income.py index 307014af..fefc0072 100755 --- a/minos/modules/income.py +++ b/minos/modules/income.py @@ -64,6 +64,7 @@ def setup(self, builder): 'S7_labour_state', 'education_state', 'SF_12_MCS', + 'SF_12_PCS', 'housing_quality', 'job_sector', 'hh_income_diff'] diff --git a/minos/modules/loneliness.py b/minos/modules/loneliness.py index 3bf0c787..e6b9b7fa 100755 --- a/minos/modules/loneliness.py +++ b/minos/modules/loneliness.py @@ -51,6 +51,7 @@ def setup(self, builder): view_columns = ["sex", "S7_labour_state", "SF_12_MCS", + "SF_12_PCS", "job_sec", "ethnicity", "education_state", diff --git a/minos/modules/mental_wellbeing.py b/minos/modules/mental_wellbeing.py index cec49c54..f8a5252f 100755 --- a/minos/modules/mental_wellbeing.py +++ b/minos/modules/mental_wellbeing.py @@ -69,7 +69,8 @@ def setup(self, builder): 'nutrition_quality', 'neighbourhood_safety', 'loneliness', - 'SF_12_diff'] + 'SF_12_diff', + 'financial_situation'] self.population_view = builder.population.get_view(columns=view_columns) diff --git a/minos/modules/neighbourhood.py b/minos/modules/neighbourhood.py index ca73c4c1..23f62faf 100755 --- a/minos/modules/neighbourhood.py +++ b/minos/modules/neighbourhood.py @@ -55,7 +55,8 @@ def setup(self, builder): 'S7_labour_state', 'education_state', 'housing_quality', - 'job_sec'] + 'job_sec', + 'urban'] #view_columns += self.transition_model.rx2('model').names self.population_view = builder.population.get_view(columns=view_columns) diff --git a/minos/modules/nutrition.py b/minos/modules/nutrition.py index afcac5bc..c26f13ea 100755 --- a/minos/modules/nutrition.py +++ b/minos/modules/nutrition.py @@ -54,7 +54,8 @@ def setup(self, builder): 'hh_income', 'alcohol_spending', 'ncigs', - 'nutrition_quality'] + 'nutrition_quality', + 'financial_situation'] #view_columns += self.transition_model.rx2('model').names self.population_view = builder.population.get_view(columns=view_columns) diff --git a/minos/modules/physical_wellbeing.py b/minos/modules/physical_wellbeing.py index 1d4bd2b2..ed1219b1 100644 --- a/minos/modules/physical_wellbeing.py +++ b/minos/modules/physical_wellbeing.py @@ -58,7 +58,7 @@ def setup(self, builder): 'ethnicity', 'age', 'education_state', - 'labour_state', + 'S7_labour_state', 'job_sec', 'hh_income', 'SF_12_PCS', @@ -67,7 +67,8 @@ def setup(self, builder): 'ncigs', 'nutrition_quality', 'neighbourhood_safety', - 'loneliness'] + 'loneliness', + 'financial_situation'] self.population_view = builder.population.get_view(columns=view_columns) diff --git a/minos/modules/tobacco.py b/minos/modules/tobacco.py index 15d9b563..293bbddd 100755 --- a/minos/modules/tobacco.py +++ b/minos/modules/tobacco.py @@ -59,6 +59,7 @@ def setup(self, builder): 'region', 'hh_income', 'SF_12_MCS', + 'SF_12_PCS', 'education_state', 'S7_labour_state', 'job_sec', diff --git a/minos/transitions/Makefile b/minos/transitions/Makefile index 0651be06..a59dc5a9 100644 --- a/minos/transitions/Makefile +++ b/minos/transitions/Makefile @@ -11,7 +11,7 @@ #transitions: $(TRANSITION_DATA)/loneliness/clm/loneliness_clm_2018_2019.rds transitions_default: | $(TRANSITION_DATA) -transitions_default: final_data $(TRANSITION_DATA)/SF_12/ols/SF_12_2017_2018.rds +transitions_default: final_data $(TRANSITION_DATA)/SF_12_MCS/ols/SF_12_MCS_2017_2018.rds transitions_SIPHER7: | $(TRANSITION_DATA) transitions_SIPHER7: final_data $(TRANSITION_DATA)/S7_mental_health/clm/S7_mental_health_2019_2020.rds @@ -23,7 +23,7 @@ cv_transitions: $(TRANSITION_DATA)/cross_validation/version5/hh_income/ols/hh_in cv_S7_transitions: | $(TRANSITION_DATA) cv_S7_transitions: $(TRANSITION_DATA)/cross_validation/version5/S7_mental_health/clm/S7_mental_health_2019_2020.rds -$(TRANSITION_DATA)/SF_12/ols/SF_12_2017_2018.rds: $(FINALDATA)/2020_US_cohort.csv $(TRANSITION_SOURCE)/estimate_transitions.R $(TRANSITION_SOURCE)/model_definitions_default.txt +$(TRANSITION_DATA)/SF_12_MCS/ols/SF_12_MCS_2017_2018.rds: $(FINALDATA)/2020_US_cohort.csv $(TRANSITION_SOURCE)/estimate_transitions.R $(TRANSITION_SOURCE)/model_definitions_default.txt $(RSCRIPT) $(SOURCEDIR)/transitions/estimate_transitions.R --default $(TRANSITION_DATA)/S7_mental_health/clm/S7_mental_health_2019_2020.rds: $(FINALDATA)/2020_US_cohort.csv $(TRANSITION_SOURCE)/estimate_transitions.R $(TRANSITION_SOURCE)/model_definitions_S7.txt diff --git a/minos/transitions/model_definitions_default.txt b/minos/transitions/model_definitions_default.txt index c8aabed0..c50221ea 100644 --- a/minos/transitions/model_definitions_default.txt +++ b/minos/transitions/model_definitions_default.txt @@ -1,13 +1,14 @@ -CLM : financial_situation ~ factor(financial_situation) + scale(SF_12) + factor(job_sec)+ scale(hh_income)+ I(scale(hh_income)**2) + factor(marital_status) + hhsize + factor(housing_tenure) -LOGIT : heating ~ factor(heating) + scale(SF_12)+ relevel(factor(ethnicity), ref='WBI') + hh_income + factor(marital_status)+ ncigs+ hhsize + factor(urban) + factor(housing_tenure) + factor(financial_situation) -OLS : hh_income ~ hh_income + age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = "1") + relevel(factor(job_sec), ref = '3') + relevel(factor(job_sector), ref = '1') -OLS_DIFF : hh_income ~ hh_income_diff + age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = "1") + relevel(factor(job_sec), ref = '3') + relevel(factor(job_sector), ref = '1') -OLS : nutrition_quality ~ age + factor(sex) + relevel(factor(education_state), ref = '3') + relevel(factor(ethnicity), ref = 'WBI') + hh_income + ncigs -CLM : housing_quality ~ age + factor(sex) + SF_12_MCS + relevel(factor(ethnicity), ref = 'WBI') + hh_income -CLM : loneliness ~ age + factor(sex) + SF_12_MCS + relevel(factor(education_state), ref = '3') + relevel(factor(job_sec), ref = '3') + hh_income + relevel(factor(hh_comp), ref = '3') + relevel(factor(marital_status), ref = 'Partnered') + relevel(factor(ethnicity), ref = 'WBI') + SF_12_PCS -CLM : neighbourhood_safety ~ age + factor(sex) + factor(job_sec) + relevel(factor(ethnicity), ref = 'WBI') + hh_income + factor(housing_quality) + relevel(factor(region), ref = 'South East') +CLM : financial_situation ~ factor(financial_situation) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(job_sec)+ scale(hh_income)+ I(scale(hh_income)**2) + factor(marital_status) + hhsize + factor(housing_tenure) +LOGIT : heating ~ factor(heating) + scale(SF_12_MCS) + scale(SF_12_PCS) + relevel(factor(ethnicity), ref='WBI') + hh_income + factor(marital_status)+ ncigs + hhsize + factor(urban) + factor(housing_tenure) + factor(financial_situation) +OLS : hh_income ~ hh_income + age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = "1") + relevel(factor(job_sec), ref = '3') + relevel(factor(job_sector), ref = '1') + scale(SF_12_MCS) + scale(SF_12_PCS) +OLS_DIFF : hh_income ~ hh_income_diff + age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = "1") + relevel(factor(job_sec), ref = '3') + relevel(factor(job_sector), ref = '1') + scale(SF_12_MCS) + scale(SF_12_PCS) +OLS : nutrition_quality ~ age + factor(sex) + relevel(factor(education_state), ref = '3') + relevel(factor(ethnicity), ref = 'WBI') + hh_income + ncigs + factor(financial_situation) +CLM : housing_quality ~ age + factor(sex) + scale(SF_12_MCS) + relevel(factor(ethnicity), ref = 'WBI') + hh_income + factor(housing_tenure) + factor(urban) + factor(financial_situation) +NNET : housing_tenure ~ age + factor(sex) + factor(housing_tenure) + relevel(factor(ethnicity), ref = 'WBI') + hh_income + factor(urban) + factor(financial_situation) +CLM : loneliness ~ age + factor(sex) + scale(SF_12_MCS) + relevel(factor(education_state), ref = '3') + relevel(factor(job_sec), ref = '3') + hh_income + relevel(factor(hh_comp), ref = '3') + relevel(factor(marital_status), ref = 'Partnered') + relevel(factor(ethnicity), ref = 'WBI') + scale(SF_12_PCS) +CLM : neighbourhood_safety ~ age + factor(sex) + factor(job_sec) + relevel(factor(ethnicity), ref = 'WBI') + hh_income + factor(housing_quality) + relevel(factor(region), ref = 'South East') + factor(urban) NNET : education_state ~ factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') -ZIP : ncigs ~ age + factor(sex) + relevel(factor(education_state), ref = '3') + SF_12_MCS + relevel(factor(job_sec), ref = '3') + hh_income + relevel(factor(ethnicity), ref = 'WBI') | relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(job_sec), ref = '3') + hh_income + SF_12_MCS + SF_12_PCS -OLS : SF_12_PCS ~ age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + hh_income + factor(housing_quality) + factor(neighbourhood_safety) + ncigs + nutrition_quality + SF_12_PCS -OLS : SF_12_MCS ~ SF_12_MCS + age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + hh_income + factor(housing_quality) + phealth + factor(neighbourhood_safety) + ncigs + nutrition_quality + factor(loneliness) -OLS_DIFF : SF_12_MCS ~ SF_12_MCS_diff + age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + hh_income + factor(housing_quality) + phealth + factor(neighbourhood_safety) + ncigs + nutrition_quality + factor(loneliness) +ZIP : ncigs ~ age + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + relevel(factor(job_sec), ref = '3') + hh_income + relevel(factor(ethnicity), ref = 'WBI') | relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(job_sec), ref = '3') + hh_income + scale(SF_12_MCS) + scale(SF_12_PCS) +OLS : SF_12_PCS ~ scale(SF_12_PCS) + age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + hh_income + factor(housing_quality) + factor(neighbourhood_safety) + ncigs + nutrition_quality + factor(loneliness) + factor(financial_situation) +OLS : SF_12_MCS ~ scale(SF_12_MCS) + age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + hh_income + factor(housing_quality) + phealth + factor(neighbourhood_safety) + ncigs + nutrition_quality + factor(loneliness) + factor(financial_situation) +OLS_DIFF : SF_12_MCS ~ SF_12_MCS_diff + age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + hh_income + factor(housing_quality) + phealth + factor(neighbourhood_safety) + ncigs + nutrition_quality + factor(loneliness) + factor(financial_situation) From 9b4452cb623b0ba444708c086c829be6928f5fec Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Thu, 6 Jul 2023 17:08:03 +0100 Subject: [PATCH 011/229] Fit model for urban and added it to US_complete_case. Fixed formatting of housing_tenure and fixed typo in handover.Rmd --- minos/data_generation/US_complete_case.py | 2 +- minos/data_generation/US_format_raw.py | 4 +++- minos/transitions/model_definitions_default.txt | 1 + minos/validation/handovers.Rmd | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/minos/data_generation/US_complete_case.py b/minos/data_generation/US_complete_case.py index 3e0724af..c1b23a9f 100755 --- a/minos/data_generation/US_complete_case.py +++ b/minos/data_generation/US_complete_case.py @@ -61,7 +61,7 @@ def complete_case_custom_years(data, var, years): complete_case_vars = ["housing_quality", 'marital_status', 'yearly_energy', "job_sec", "education_state", 'region', "age", "job_sector", 'SF_12_MCS', 'SF_12_PCS', - 'financial_situation', "housing_tenure"] # many of these + 'financial_situation', "housing_tenure", 'urban'] # many of these # REMOVED: 'job_sector', 'labour_state' data = complete_case_varlist(data, complete_case_vars) diff --git a/minos/data_generation/US_format_raw.py b/minos/data_generation/US_format_raw.py index 66a6b715..fd0b5271 100755 --- a/minos/data_generation/US_format_raw.py +++ b/minos/data_generation/US_format_raw.py @@ -61,6 +61,8 @@ heating_ukhls = US_utils.load_json(json_source, "heating_ukhls.json") ## Location region_dict = US_utils.load_json(json_source, "region.json") +## Housing Tenure +housing_tenure_dict = US_utils.load_json(json_source, 'housing_tenure.json') def format_sex(data): @@ -328,7 +330,7 @@ def format_housing_tenure(data): ------- """ - data["housing_tenure"] = data["housing_tenure"].astype(str).map(labour_ukhls) + data["housing_tenure"] = data["housing_tenure"].astype(str).map(housing_tenure_dict) return data diff --git a/minos/transitions/model_definitions_default.txt b/minos/transitions/model_definitions_default.txt index c50221ea..be413245 100644 --- a/minos/transitions/model_definitions_default.txt +++ b/minos/transitions/model_definitions_default.txt @@ -1,5 +1,6 @@ CLM : financial_situation ~ factor(financial_situation) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(job_sec)+ scale(hh_income)+ I(scale(hh_income)**2) + factor(marital_status) + hhsize + factor(housing_tenure) LOGIT : heating ~ factor(heating) + scale(SF_12_MCS) + scale(SF_12_PCS) + relevel(factor(ethnicity), ref='WBI') + hh_income + factor(marital_status)+ ncigs + hhsize + factor(urban) + factor(housing_tenure) + factor(financial_situation) +LOGIT : urban ~ age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = "1") OLS : hh_income ~ hh_income + age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = "1") + relevel(factor(job_sec), ref = '3') + relevel(factor(job_sector), ref = '1') + scale(SF_12_MCS) + scale(SF_12_PCS) OLS_DIFF : hh_income ~ hh_income_diff + age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = "1") + relevel(factor(job_sec), ref = '3') + relevel(factor(job_sector), ref = '1') + scale(SF_12_MCS) + scale(SF_12_PCS) OLS : nutrition_quality ~ age + factor(sex) + relevel(factor(education_state), ref = '3') + relevel(factor(ethnicity), ref = 'WBI') + hh_income + ncigs + factor(financial_situation) diff --git a/minos/validation/handovers.Rmd b/minos/validation/handovers.Rmd index 915696e5..a5c09e6f 100644 --- a/minos/validation/handovers.Rmd +++ b/minos/validation/handovers.Rmd @@ -319,7 +319,7 @@ base.sf12.p <- base.dat %>% sf12.p.spag <- rbind(raw.sf12.p, base.sf12.p) -spaghetti_plot(sf12.m.spag, 'SF_12_PCS', +spaghetti_plot(sf12.p.spag, 'SF_12_PCS', save = shall.we.save, save.path = save.path) From eeaee17c0c9c750a86cd1d91401011819a61efd4 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Thu, 6 Jul 2023 17:55:22 +0100 Subject: [PATCH 012/229] Added heating to complete_case vars --- minos/data_generation/US_complete_case.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/minos/data_generation/US_complete_case.py b/minos/data_generation/US_complete_case.py index c1b23a9f..2289aa94 100755 --- a/minos/data_generation/US_complete_case.py +++ b/minos/data_generation/US_complete_case.py @@ -61,7 +61,7 @@ def complete_case_custom_years(data, var, years): complete_case_vars = ["housing_quality", 'marital_status', 'yearly_energy', "job_sec", "education_state", 'region', "age", "job_sector", 'SF_12_MCS', 'SF_12_PCS', - 'financial_situation', "housing_tenure", 'urban'] # many of these + 'financial_situation', "housing_tenure", 'urban', 'heating'] # many of these # REMOVED: 'job_sector', 'labour_state' data = complete_case_varlist(data, complete_case_vars) From 73651b8f74d4982ef3f491ea65f55f6eeff50a3a Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Fri, 7 Jul 2023 11:17:17 +0100 Subject: [PATCH 013/229] Minor formatting fixes for some JSONs, added docstring to a US_format_raw.py function, and fixed heating module trying to convert from float to int where it shouldnt have done --- minos/data_generation/US_format_raw.py | 5 ++++- minos/modules/heating.py | 1 - persistent_data/JSON/heating_ukhls.json | 9 ++++++++- persistent_data/JSON/region.json | 18 +++++++++++++++++- 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/minos/data_generation/US_format_raw.py b/minos/data_generation/US_format_raw.py index fd0b5271..3ad5f761 100755 --- a/minos/data_generation/US_format_raw.py +++ b/minos/data_generation/US_format_raw.py @@ -323,12 +323,15 @@ def format_ukhls_columns(year): def format_council_tax(data): """Format any council tax data for calculation of monthly overheads.""" + def format_housing_tenure(data): """ + Format housing tenure variable Returns ------- - + data : pd.DataFrame + Data with housing_tenure formatted to strings """ data["housing_tenure"] = data["housing_tenure"].astype(str).map(housing_tenure_dict) return data diff --git a/minos/modules/heating.py b/minos/modules/heating.py index ec332080..82fb89ee 100644 --- a/minos/modules/heating.py +++ b/minos/modules/heating.py @@ -98,7 +98,6 @@ def on_time_step(self, event): list(heating_prob_df.columns), heating_prob_df) heating_prob_df.index = pop.index - heating_prob_df['heating'] = heating_prob_df['heating'].astype(int) self.population_view.update(heating_prob_df["heating"]) def calculate_heating(self, pop): diff --git a/persistent_data/JSON/heating_ukhls.json b/persistent_data/JSON/heating_ukhls.json index 5f3a5f99..70e40e63 100755 --- a/persistent_data/JSON/heating_ukhls.json +++ b/persistent_data/JSON/heating_ukhls.json @@ -1 +1,8 @@ -{"1" : "1", "2" : "0", "3" : "-3", "-1" : "-1", "-2" : "-2", "-8" : "-8", "-9" : "-9", "-10" : "-10"} +{"1" : "1", +"2" : "0", +"3" : "-3", +"-1" : "-1", +"-2" : "-2", +"-8" : "-8", +"-9" : "-9", +"-10" : "-10"} diff --git a/persistent_data/JSON/region.json b/persistent_data/JSON/region.json index 79a5e051..a1d612c5 100755 --- a/persistent_data/JSON/region.json +++ b/persistent_data/JSON/region.json @@ -1 +1,17 @@ -{"1": "North East", "2": "North West", "3": "Yorkshire and The Humber", "4": "East Midlands", "5": "West Midlands", "6": "East of England", "7": "London", "8": "South East", "9": "South West", "10": "Wales", "11": "Scotland", "12": "Northern Ireland", "-1": "-1", "-2": "-2", "-7": "-7", "-8": "-8", "-9": "-9"} +{"1": "North East", +"2": "North West", +"3": "Yorkshire and The Humber", +"4": "East Midlands", +"5": "West Midlands", +"6": "East of England", +"7": "London", +"8": "South East", +"9": "South West", +"10": "Wales", +"11": "Scotland", +"12": "Northern Ireland", +"-1": "-1", +"-2": "-2", +"-7": "-7", +"-8": "-8", +"-9": "-9"} From 0ef36d1f1df5c9b66a239194cfd3ad6b23a01f2e Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Fri, 7 Jul 2023 13:30:20 +0100 Subject: [PATCH 014/229] Added housing_tenure module and amended config files --- .../cross_validation_default1.yaml | 2 +- .../cross_validation_default2.yaml | 2 +- .../cross_validation_default3.yaml | 2 +- .../cross_validation_default4.yaml | 2 +- .../cross_validation_default5.yaml | 2 +- config/default.yaml | 2 +- config/scot_default.yaml | 2 +- minos/minosPipeline/RunPipeline.py | 2 + minos/modules/housing_tenure.py | 47 +++++++------------ 9 files changed, 27 insertions(+), 36 deletions(-) diff --git a/config/cross_validation/cross_validation_default1.yaml b/config/cross_validation/cross_validation_default1.yaml index 2772d158..3ca9bbf3 100644 --- a/config/cross_validation/cross_validation_default1.yaml +++ b/config/cross_validation/cross_validation_default1.yaml @@ -30,7 +30,7 @@ cross_validation: TRUE # Similarly Mortality requires Replenishment but everything else requires Mortality so it goes second. (priority 1) # Finally everything else can go in any order (priority 2). #components : [MWB(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Income(), Housing(), Labour(), Nutrition(), Education(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] -components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Neighbourhood(), Heating(), Housing(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] +components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Neighbourhood(), Heating(), Housing(), HousingTenure(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] scale_rates: diff --git a/config/cross_validation/cross_validation_default2.yaml b/config/cross_validation/cross_validation_default2.yaml index 6f07e812..12cbea9c 100644 --- a/config/cross_validation/cross_validation_default2.yaml +++ b/config/cross_validation/cross_validation_default2.yaml @@ -30,7 +30,7 @@ cross_validation: TRUE # Similarly Mortality requires Replenishment but everything else requires Mortality so it goes second. (priority 1) # Finally everything else can go in any order (priority 2). #components : [MWB(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Income(), Housing(), Labour(), Nutrition(), Education(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] -components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Neighbourhood(), Heating(), Housing(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] +components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Neighbourhood(), Heating(), Housing(), HousingTenure(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] scale_rates: diff --git a/config/cross_validation/cross_validation_default3.yaml b/config/cross_validation/cross_validation_default3.yaml index 2610b16b..5a33b1e8 100644 --- a/config/cross_validation/cross_validation_default3.yaml +++ b/config/cross_validation/cross_validation_default3.yaml @@ -30,7 +30,7 @@ cross_validation: TRUE # Similarly Mortality requires Replenishment but everything else requires Mortality so it goes second. (priority 1) # Finally everything else can go in any order (priority 2). #components : [MWB(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Income(), Housing(), Labour(), Nutrition(), Education(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] -components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Neighbourhood(), Heating(), Housing(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] +components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Neighbourhood(), Heating(), Housing(), HousingTenure(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] scale_rates: diff --git a/config/cross_validation/cross_validation_default4.yaml b/config/cross_validation/cross_validation_default4.yaml index d689e820..a846d4aa 100644 --- a/config/cross_validation/cross_validation_default4.yaml +++ b/config/cross_validation/cross_validation_default4.yaml @@ -30,7 +30,7 @@ cross_validation: TRUE # Similarly Mortality requires Replenishment but everything else requires Mortality so it goes second. (priority 1) # Finally everything else can go in any order (priority 2). #components : [MWB(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Income(), Housing(), Labour(), Nutrition(), Education(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] -components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Neighbourhood(), Heating(), Housing(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] +components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Neighbourhood(), Heating(), Housing(), HousingTenure(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] scale_rates: diff --git a/config/cross_validation/cross_validation_default5.yaml b/config/cross_validation/cross_validation_default5.yaml index 769912e3..39fe0aae 100644 --- a/config/cross_validation/cross_validation_default5.yaml +++ b/config/cross_validation/cross_validation_default5.yaml @@ -30,7 +30,7 @@ cross_validation: TRUE # Similarly Mortality requires Replenishment but everything else requires Mortality so it goes second. (priority 1) # Finally everything else can go in any order (priority 2). #components : [MWB(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Income(), Housing(), Labour(), Nutrition(), Education(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] -components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Neighbourhood(), Heating(), Housing(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] +components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Neighbourhood(), Heating(), Housing(), HousingTenure(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] scale_rates: diff --git a/config/default.yaml b/config/default.yaml index 83892e41..4b2e5d80 100755 --- a/config/default.yaml +++ b/config/default.yaml @@ -27,7 +27,7 @@ cross_validation: FALSE # Similarly Mortality requires Replenishment but everything else requires Mortality so it goes second. (priority 1) # Finally everything else can go in any order (priority 2). #components : [MWB(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Income(), Housing(), Labour(), Nutrition(), Education(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] -components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Neighbourhood(), Heating(), Housing(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] +components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Neighbourhood(), Heating(), Housing(), HousingTenure(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] scale_rates: diff --git a/config/scot_default.yaml b/config/scot_default.yaml index 2aa8176e..b6d38127 100755 --- a/config/scot_default.yaml +++ b/config/scot_default.yaml @@ -27,7 +27,7 @@ cross_validation: FALSE # Similarly Mortality requires Replenishment but everything else requires Mortality so it goes second. (priority 1) # Finally everything else can go in any order (priority 2). #components : [MWB(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Income(), Housing(), Labour(), Nutrition(), Education(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] -components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Neighbourhood(), Heating(), Housing(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] +components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Neighbourhood(), Heating(), Housing(), HousingTenure(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] scale_rates: diff --git a/minos/minosPipeline/RunPipeline.py b/minos/minosPipeline/RunPipeline.py index 4585b581..b5b0d699 100755 --- a/minos/minosPipeline/RunPipeline.py +++ b/minos/minosPipeline/RunPipeline.py @@ -38,6 +38,7 @@ from minos.modules.S7EquivalentIncome import S7EquivalentIncome from minos.modules.heating import Heating from minos.modules.financial_situation import financialSituation +from minos.modules.housing_tenure import HousingTenure from minos.modules.intervention import hhIncomeIntervention from minos.modules.intervention import hhIncomeChildUplift @@ -88,6 +89,7 @@ def validate_components(config_components, intervention): "FertilityAgeSpecificRates()": FertilityAgeSpecificRates(), "Mortality()": Mortality(), "Education()": Education(), + "HousingTenure()": HousingTenure() } SIPHER7_components_map = { # SIPHER7 stuff diff --git a/minos/modules/housing_tenure.py b/minos/modules/housing_tenure.py index b145b7ac..d6fbbe0c 100644 --- a/minos/modules/housing_tenure.py +++ b/minos/modules/housing_tenure.py @@ -14,7 +14,7 @@ from datetime import datetime as dt -class Housing(Base): +class HousingTenure(Base): @property def name(self): @@ -95,16 +95,14 @@ def on_time_step(self, event): housing_tenure_prob_df = self.calculate_housing_tenure(pop) housing_tenure_prob_df["housing_tenure"] = self.random.choice(housing_tenure_prob_df.index, - list(housing_tenure_prob_df.columns), - housing_tenure_prob_df) + list(housing_tenure_prob_df.columns), + housing_tenure_prob_df) housing_tenure_prob_df.index = housing_tenure_prob_df.index.astype(int) # convert numeric prediction into string factors (low, medium, high) - #housing_tenure_factor_dict = {1: 'Low', - # 2: 'Medium', - # 3: 'High'} - #housing_tenure_prob_df.replace({'housing_quality': housing_tenure_factor_dict}, + #housing_tenure_factor_dict = {} + #housing_tenure_prob_df.replace({'housing_tenure': housing_tenure_factor_dict}, # inplace=True) self.population_view.update(housing_tenure_prob_df["housing_tenure"]) @@ -126,31 +124,22 @@ def calculate_housing_tenure(self, pop): else: year = min(self.year, 2019) - transition_model = r_utils.load_transitions(f"housing_tenure/clm/housing_tenure_{year}_{year+1}", + # 2019 model doesn't have the 'Rented private furnished' category. Set differently for years before this + if year == 2019: + cols = ['Owned outright', 'Owned with mortgage', 'Local authority rent', 'Housing assoc rented', + 'Rented from employer', 'Rented private unfurnished', 'Other'] + elif year < 2019: + cols = ['Owned outright', 'Owned with mortgage', 'Local authority rent', 'Housing assoc rented', + 'Rented from employer', 'Rented private unfurnished', 'Rented private furnished', 'Other'] + + transition_model = r_utils.load_transitions(f"housing_tenure/nnet/housing_tenure_{year}_{year+1}", self.rpy2Modules, path=self.transition_dir) # returns probability matrix (3xn) of next ordinal state. - prob_df = r_utils.predict_next_timestep_clm(transition_model, - self.rpy2Modules, - pop, - 'housing_tenure') - return prob_df - - # set up list of columns - cols = ['FT Employed', 'PT Employed', 'Job Seeking', 'FT Education', 'Family Care', 'Not Working'] - - # load transition model based on year. - # year = min(self.year, 2018) # TODO just use latest model for now. Needs some kind of reweighting if extrapolating later. - if self.cross_validation: - # if cross-val, fix year to final year model - year = 2019 - else: - year = min(self.year, 2019) - - transition_model = r_utils.load_transitions(f"S7_labour_state/nnet/S7_labour_state_{year}_{year + 1}", - self.rpy2Modules, path=self.transition_dir) - # returns probability matrix (9xn) of next ordinal state. - prob_df = r_utils.predict_nnet(transition_model, self.rpy2Modules, pop, cols) + prob_df = r_utils.predict_nnet(transition_model, + self.rpy2Modules, + pop, + cols) return prob_df def plot(self, pop, config): From ed5e35e6d659e6c0d227fde75bb94366ae2daa0d Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Fri, 7 Jul 2023 13:33:00 +0100 Subject: [PATCH 015/229] Formatting changes to S7Labour module --- minos/modules/S7Labour.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/minos/modules/S7Labour.py b/minos/modules/S7Labour.py index 5eca38be..2bc48818 100644 --- a/minos/modules/S7Labour.py +++ b/minos/modules/S7Labour.py @@ -95,12 +95,13 @@ def on_time_step(self, event): labour_prob_df = self.calculate_labour(pop) - labour_prob_df["S7_labour_state"] = self.random.choice(labour_prob_df.index, list(labour_prob_df.columns), labour_prob_df) + labour_prob_df["S7_labour_state"] = self.random.choice(labour_prob_df.index, + list(labour_prob_df.columns), + labour_prob_df) labour_prob_df.index = labour_prob_df.index.astype(int) self.population_view.update(labour_prob_df["S7_labour_state"]) - def calculate_labour(self, pop): """Calculate labour transition distribution based on provided people/indices. @@ -122,9 +123,14 @@ def calculate_labour(self, pop): else: year = min(self.year, 2019) - transition_model = r_utils.load_transitions(f"S7_labour_state/nnet/S7_labour_state_{year}_{year+1}", self.rpy2Modules, path=self.transition_dir) + transition_model = r_utils.load_transitions(f"S7_labour_state/nnet/S7_labour_state_{year}_{year+1}", + self.rpy2Modules, + path=self.transition_dir) # returns probability matrix (9xn) of next ordinal state. - prob_df = r_utils.predict_nnet(transition_model, self.rpy2Modules, pop, cols) + prob_df = r_utils.predict_nnet(transition_model, + self.rpy2Modules, + pop, + cols) return prob_df def plot(self, pop, config): From c9e5d4e51bd035359a9bdbdcf5c32db12da419f0 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Fri, 7 Jul 2023 14:57:56 +0100 Subject: [PATCH 016/229] Functionalised the handovers plots in utils_validation_vis.R and updated the handovers.Rmd script --- minos/utils_validation_vis.R | 128 ++++++++ minos/validation/handovers.Rmd | 554 +-------------------------------- 2 files changed, 138 insertions(+), 544 deletions(-) diff --git a/minos/utils_validation_vis.R b/minos/utils_validation_vis.R index d2dc788a..7b23402b 100644 --- a/minos/utils_validation_vis.R +++ b/minos/utils_validation_vis.R @@ -40,6 +40,134 @@ validation_prep_ordinal <- function(raw.dat, base.dat, var) { ############ PLOTTING ############ +handover_continuous <- function(raw.dat, base.dat, var, save = FALSE) { + raw.var <- raw.dat %>% + dplyr::select(pidp, time, all_of(var), weight) %>% + filter(time != 2009) %>% + filter(!.data[[var]] %in% miss.values) %>% + drop_na() %>% + group_by(time) %>% + summarise(mean = weighted.mean(x = .data[[var]], + w = weight, + na.rm=TRUE)) %>% + mutate(source = 'final_US') + + base.var <- base.dat %>% + dplyr::select(pidp, time, all_of(var), weight) %>% + filter(time != 2009) %>% + filter(!.data[[var]] %in% miss.values) %>% + drop_na() %>% + group_by(time) %>% + summarise(mean = weighted.mean(x = .data[[var]], + w = weight, + na.rm=TRUE)) %>% + mutate(source = 'baseline_output') + + # merge before plot + merged <- rbind(raw.var, base.var) + + # Now plot + p1 <- ggplot(data = merged, mapping = aes(x = time, y = mean, group = source, colour = source)) + + geom_line() + + geom_vline(xintercept=start.year, linetype='dotted') + + labs(title = var, subtitle = 'Full Sample') + + xlab('Year') + + ylab(var) + + ## Try a version where final_US is limited to only those present from wave 1 + # onwards because the sample refreshments are messing with the plot + raw.wave1 <- raw.dat$pidp[raw.dat$time == 2009] + + raw.var2 <- raw.dat %>% + dplyr::select(pidp, time, all_of(var), weight) %>% + filter(pidp %in% raw.wave1) %>% + filter(time != 2009) %>% + filter(!.data[[var]] %in% miss.values) %>% + drop_na() %>% + group_by(time) %>% + summarise(mean = weighted.mean(x = .data[[var]], + w = weight, + na.rm = TRUE)) %>% + mutate(source = 'final_US') + + base.var2 <- base.dat %>% + dplyr::select(pidp, time, all_of(var), weight) %>% + filter(pidp %in% raw.wave1) %>% + filter(time != 2009) %>% + filter(!.data[[var]] %in% miss.values) %>% + drop_na() %>% + group_by(time) %>% + summarise(mean = weighted.mean(x = .data[[var]], + w = weight, + na.rm = TRUE)) %>% + mutate(source = 'baseline_output') + + merged2 <- rbind(raw.var2, base.var2) + + # Now plot + p2 <- ggplot(data = merged2, mapping = aes(x = time, y = mean, group = source, colour = source)) + + geom_line() + + geom_vline(xintercept=start.year, linetype='dotted') + + labs(title = var, subtitle = 'Wave 1 Sample') + + xlab('Year') + + ylab(var) + + if (save) { + ggsave(filename = paste0(var, '.png'), + plot = p1, + path = save.path) + } + print(p1) + print(p2) +} + +handover_ordinal <- function(raw.dat, base.dat, var, save=FALSE) { + raw.var <- raw.dat %>% + dplyr::select(pidp, time, all_of(var)) %>% + filter(.data[[var]] > 0) %>% + group_by(time, .data[[var]]) %>% + count() %>% + mutate(source = 'final_US') + + base.var <- base.dat %>% + dplyr::select(pidp, time, all_of(var)) %>% + filter(.data[[var]] > 0) %>% + group_by(time, .data[[var]]) %>% + count() %>% + mutate(source = 'baseline_output') + + merged <- rbind(raw.var, base.var) + merged[[var]] <- as.factor(merged[[var]]) + + p1 <- ggplot(data = merged, mapping = aes(x = time, y = n, group = .data[[var]], colour = .data[[var]])) + + geom_line() + + geom_point() + + geom_vline(xintercept=start.year, linetype='dotted') + + labs(title = var, subtitle = 'Counts by Level') + + xlab('Year') + + ylab('Count') + + var.norm <- merged %>% + group_by(time) %>% + mutate(total = sum(n)) %>% + mutate(prct = (n / total)) + + p2 <- ggplot(data = var.norm, mapping = aes(x = time, y = prct, fill=.data[[var]])) + + geom_bar(stat = 'identity') + + geom_vline(xintercept=start.year, linetype='dotted') + + labs(title = var) + + xlab('Year') + + ylab('Proportion') + + if (save) { + ggsave(filename = paste0(var, '.png',), + plot = last_plot(), + path = save.path) + } + print(p1) + print(p2) +} + spaghetti_plot <- function(data, v, save=FALSE, save.path=NULL, filename.tag=NULL) { # spaghetti plot displaying trajectories over time for continuous variable v diff --git a/minos/validation/handovers.Rmd b/minos/validation/handovers.Rmd index a5c09e6f..47797bc4 100644 --- a/minos/validation/handovers.Rmd +++ b/minos/validation/handovers.Rmd @@ -67,75 +67,7 @@ shall.we.save <- FALSE ## Income ```{r} -# first figure out how to plot final_US -# start with hh_income -raw.income <- raw.dat %>% - dplyr::select(pidp, time, hh_income, weight) %>% - group_by(time) %>% - summarise(income = mean(hh_income, na.rm=TRUE)) %>% - mutate(source = 'final_US') - -base.income <- base.dat %>% - dplyr::select(pidp, time, hh_income, weight) %>% - group_by(time) %>% - summarise(income = mean(hh_income)) %>% - mutate(source = 'baseline_output') - -# merge before plot -income <- rbind(raw.income, base.income) - -# Now plot -ggplot(data = income, mapping = aes(x = time, y = income, group = source, colour = source)) + - geom_line() + - geom_vline(xintercept=start.year, linetype='dotted') + - labs(title = 'Household Income', subtitle = 'Full Sample') + - xlab('Year') + - ylab('Income') - -## Try a version where final_US is limited to only those present from wave 1 -# onwards because the sample refreshments are messing with the plot -raw.income.wave1 <- raw.dat$pidp[raw.dat$time == 2009] - -raw.income2 <- raw.dat %>% - dplyr::select(pidp, time, hh_income) %>% - filter(pidp %in% raw.income.wave1) %>% - group_by(time) %>% - summarise(income = mean(hh_income, na.rm = TRUE)) %>% - mutate(source = 'final_US') - -base.income2 <- base.dat %>% - dplyr::select(pidp, time, hh_income) %>% - filter(pidp %in% raw.income.wave1) %>% - group_by(time) %>% - summarise(income = mean(hh_income, na.rm = TRUE)) %>% - mutate(source = 'baseline_output') - -income2 <- rbind(raw.income2, base.income2) - -# Now plot -ggplot(data = income2, mapping = aes(x = time, y = income, group = source, colour = source)) + - geom_line() + - geom_vline(xintercept=start.year, linetype='dotted') + - labs(title = 'Household Income') + - xlab('Year') + - ylab('Income') - -if (shall.we.save) { - ggsave(filename = 'hh_income_wav1.png', - plot = last_plot(), - path = save.path) -} - -raw.income.check <- raw.dat %>% - dplyr::select(pidp, time, hh_income) %>% - filter(pidp %in% raw.income.wave1) - -base.income.check <- base.dat %>% - dplyr::select(pidp, time, hh_income) %>% - filter(pidp %in% raw.income.wave1) - -rm(raw.income, base.income, income, raw.income.wave1, raw.income2, base.income2, - raw.income.check, base.income.check) +handover_continuous(raw.dat, base.dat, var = 'hh_income') ``` ### Spaghetti @@ -159,72 +91,7 @@ rm(raw.inc, base.inc, income.spag) ## SF_12_MCS ```{r} -# first figure out how to plot final_US -# start with hh_income -raw.sf12.m <- raw.dat %>% - dplyr::select(pidp, time, SF_12_MCS) %>% - filter(SF_12_MCS > 0) %>% - group_by(time) %>% - summarise(sf12.m = mean(SF_12_MCS, na.rm = TRUE)) %>% - mutate(source = 'final_US') - -base.sf12.m <- base.dat %>% - dplyr::select(pidp, time, SF_12_MCS) %>% - filter(SF_12_MCS > 0) %>% - group_by(time) %>% - summarise(sf12.m = mean(SF_12_MCS, na.rm = TRUE)) %>% - mutate(source = 'baseline_output') - -# merge before plot -sf12.m <- rbind(raw.sf12.m, base.sf12.m) - -print(sum(sf12.m$SF_12_MCS < 0)) - -# Now plot -ggplot(data = sf12.m, mapping = aes(x = time, y = sf12.m, group = source, colour = source)) + - geom_line() + - geom_vline(xintercept=start.year, linetype='dotted') + - labs(title = 'Mental Wellbeing (SF12_MCS)', subtitle = 'Full Sample') + - xlab('Year') + - ylab('SF12_MCS') - -## Try a version where final_US is limited to only those present from wave 1 -# onwards because the sample refreshments are messing with the plot -raw.sf12.m.wave1 <- raw.dat$pidp[raw.dat$time == 2009] - -raw.sf12.m.2 <- raw.dat %>% - dplyr::select(pidp, time, SF_12_MCS) %>% - filter(SF_12_MCS > 0) %>% - filter(pidp %in% raw.sf12.m.wave1) %>% - group_by(time) %>% - summarise(sf12.m = mean(SF_12_MCS, na.rm = TRUE)) %>% - mutate(source = 'final_US') - -base.sf12.m.2 <- base.dat %>% - dplyr::select(pidp, time, SF_12_MCS) %>% - filter(SF_12_MCS > 0) %>% - filter(pidp %in% raw.sf12.m.wave1) %>% - group_by(time) %>% - summarise(sf12.m = mean(SF_12_MCS, na.rm = TRUE)) %>% - mutate(source = 'baseline_output') - -sf12.m.2 <- rbind(raw.sf12.m.2, base.sf12.m.2) - -# Now plot -ggplot(data = sf12.m.2, mapping = aes(x = time, y = sf12.m, group = source, colour = source)) + - geom_line() + - geom_vline(xintercept=start.year, linetype='dotted') + - labs(title = 'Mental Wellbeing (SF12_MCS)') + - xlab('Year') + - ylab('SF12_MCS') - -if (shall.we.save) { - ggsave(filename = 'SF12_MCS_wav1.png', - plot = last_plot(), - path = save.path) -} - -rm(raw.sf12, base.sf12, sf12, raw.sf12.wave1, raw.sf12.2, base.sf12.2, sf12.2) +handover_continuous(raw.dat, base.dat, var = 'SF_12_MCS') ``` ### Spaghetti @@ -247,66 +114,7 @@ rm(raw.sf12.m, base.sf12.m, sf12.m.spag) ## SF_12_PCS ```{r} -# first figure out how to plot final_US -# start with hh_income -raw.sf12.p <- raw.dat %>% - dplyr::select(pidp, time, SF_12_PCS) %>% - filter(SF_12_PCS > 0) %>% - group_by(time) %>% - summarise(sf12.p = mean(SF_12_PCS, na.rm = TRUE)) %>% - mutate(source = 'final_US') - -base.sf12.p <- base.dat %>% - dplyr::select(pidp, time, SF_12_PCS) %>% - filter(SF_12_PCS > 0) %>% - group_by(time) %>% - summarise(sf12.p = mean(SF_12_PCS, na.rm = TRUE)) %>% - mutate(source = 'baseline_output') - -# merge before plot -sf12.p <- rbind(raw.sf12.p, base.sf12.p) - -# Now plot -ggplot(data = sf12.p, mapping = aes(x = time, y = sf12.p, group = source, colour = source)) + - geom_line() + - geom_vline(xintercept=start.year, linetype='dotted') + - labs(title = 'Physical Wellbeing (SF12_PCS)', subtitle = 'Full Sample') + - xlab('Year') + - ylab('SF12_PCS') - -## Try a version where final_US is limited to only those present from wave 1 -# onwards because the sample refreshments are messing with the plot -raw.sf12.p.wave1 <- raw.dat$pidp[raw.dat$time == 2009] - -raw.sf12.p.2 <- raw.dat %>% - dplyr::select(pidp, time, SF_12_PCS) %>% - filter(SF_12_PCS > 0) %>% - filter(pidp %in% raw.sf12.p.wave1) %>% - group_by(time) %>% - summarise(sf12.p = mean(SF_12_PCS, na.rm = TRUE)) %>% - mutate(source = 'final_US') - -base.sf12.p.2 <- base.dat %>% - dplyr::select(pidp, time, SF_12_PCS) %>% - filter(SF_12_PCS > 0) %>% - filter(pidp %in% raw.sf12.p.wave1) %>% - group_by(time) %>% - summarise(sf12.p = mean(SF_12_PCS, na.rm = TRUE)) %>% - mutate(source = 'baseline_output') - -sf12.p.2 <- rbind(raw.sf12.p.2, base.sf12.p.2) - -# Now plot -ggplot(data = sf12.p.2, mapping = aes(x = time, y = sf12.p, group = source, colour = source)) + - geom_line() + - geom_vline(xintercept=start.year, linetype='dotted') + - labs(title = 'Physical Wellbeing (SF12_PCS)') + - xlab('Year') + - ylab('SF12_PCS') - -ggsave(filename = 'SF12_PCS_wav1.png', - plot = last_plot(), - path = save.path) +handover_continuous(raw.dat, base.dat, var = 'SF_12_PCS') ``` ### Spaghetti @@ -329,99 +137,13 @@ rm(raw.sf12.p, base.sf12.p, sf12.p.spag) ## Financial Situation ```{r} -raw.financial_situation <- raw.dat %>% - dplyr::select(pidp, time, financial_situation) %>% - group_by(time, financial_situation) %>% - count() %>% - mutate(source = 'final_US') - -base.financial_situation <- base.dat %>% - dplyr::select(pidp, time, financial_situation) %>% - group_by(time, financial_situation) %>% - count() %>% - mutate(source = 'baseline_output') - - - -# merge before plot -financial_situation <- rbind(raw.financial_situation, base.financial_situation) -financial_situation$financial_situation <- as.factor(financial_situation$financial_situation) - -# Now plot -ggplot(data = financial_situation, mapping = aes(x = time, y = n, group = financial_situation, colour = financial_situation)) + - geom_line() + - geom_vline(xintercept=start.year, linetype='dotted') + - labs(title = 'Financial Situation', subtitle = 'Counts by Level') + - xlab('Year') + - ylab('Count') - -financial_situation.norm <- financial_situation %>% - group_by(time) %>% - mutate(total = sum(n)) %>% - mutate(prct = (n / total)) - -ggplot(data = financial_situation.norm, mapping = aes(x = time, y = prct, fill=financial_situation)) + - geom_bar(stat = 'identity') + - geom_vline(xintercept=start.year, linetype='dotted') + - labs(title = 'Financial situation') + - xlab('Year') + - ylab('Proportion') - -if (shall.we.save) { - ggsave(filename = 'financial_situation.png', - plot = last_plot(), - path = save.path) -} - -rm(raw.financial_situation, base.financial_situation, financial_situation, financial_situation.norm) +handover_ordinal(raw.dat, base.dat, var = 'financial_situation', save = shall.we.save) ``` ## Housing Quality ```{r} -raw.housing <- raw.dat %>% - dplyr::select(pidp, time, housing_quality) %>% - group_by(time, housing_quality) %>% - count() %>% - mutate(source = 'final_US') - -base.housing <- base.dat %>% - dplyr::select(pidp, time, housing_quality) %>% - group_by(time, housing_quality) %>% - count() %>% - mutate(source = 'baseline_output') - -# merge before plot -housing <- rbind(raw.housing, base.housing) -housing$housing_quality <- as.factor(housing$housing_quality) - -# Now plot -ggplot(data = housing, mapping = aes(x = time, y = n, group = housing_quality, colour = housing_quality)) + - geom_line() + - geom_vline(xintercept=start.year, linetype='dotted') + - labs(title = 'Housing Quality', subtitle = 'Counts by Level') + - xlab('Year') + - ylab('Count') - -housing.norm <- housing %>% - group_by(time) %>% - mutate(total = sum(n)) %>% - mutate(prct = (n / total)) - -ggplot(data = housing.norm, mapping = aes(x = time, y = prct, fill=housing_quality)) + - geom_bar(stat = 'identity') + - geom_vline(xintercept=start.year, linetype='dotted') + - labs(title = 'Housing Quality') + - xlab('Year') + - ylab('Proportion') - -if (shall.we.save) { - ggsave(filename = 'housing_quality.png', - plot = last_plot(), - path = save.path) -} - -rm(raw.housing, base.housing, housing, housing.norm) +handover_ordinal(raw.dat, base.dat, var = 'housing_quality', save = shall.we.save) ``` ### Spaghetti @@ -444,52 +166,7 @@ rm(raw.s, base.s, spag) ## Neighbourhood Safety ```{r} -raw.neighbour <- raw.dat %>% - dplyr::select(pidp, time, neighbourhood_safety) %>% - filter(neighbourhood_safety > 0) %>% - group_by(time, neighbourhood_safety) %>% - count() %>% - mutate(source = 'final_US') - -base.neighbour <- base.dat %>% - dplyr::select(pidp, time, neighbourhood_safety) %>% - filter(neighbourhood_safety > 0) %>% - group_by(time, neighbourhood_safety) %>% - count() %>% - mutate(source = 'baseline_output') - -# merge before plot -neighbour <- rbind(raw.neighbour, base.neighbour) -neighbour$neighbourhood_safety <- as.factor(neighbour$neighbourhood_safety) - -# Now plot -ggplot(data = neighbour, mapping = aes(x = time, y = n, group = neighbourhood_safety, colour = neighbourhood_safety)) + - geom_line() + - geom_vline(xintercept=start.year, linetype='dotted') + - labs(title = 'Neighbourhood Safety', subtitle = 'Counts by level') + - xlab('Year') + - ylab('Count') - -neighbour.norm <- neighbour %>% - group_by(time) %>% - filter(neighbourhood_safety != -9) %>% - mutate(total = sum(n)) %>% - mutate(prct = (n / total)) - -ggplot(data = neighbour.norm, mapping = aes(x = time, y = prct, fill=neighbourhood_safety)) + - geom_bar(stat = 'identity') + - geom_vline(xintercept=start.year, linetype='dotted') + - labs(title = 'Neighbourhood Safety') + - xlab('Year') + - ylab('Proportion') - -if (shall.we.save) { - ggsave(filename = 'neighbourhood_safety.png', - plot = last_plot(), - path = save.path) -} - -#rm(raw.neighbour, base.neighbour, neighbour, neighbour.norm) +handover_ordinal(raw.dat, base.dat, var = 'neighbourhood_safety', save = shall.we.save) ``` ### Spaghetti @@ -512,52 +189,7 @@ rm(raw.s, base.s, spag) ## Loneliness ```{r} -raw.loneliness <- raw.dat %>% - dplyr::select(pidp, time, loneliness) %>% - filter(loneliness > 0) %>% - group_by(time, loneliness) %>% - count() %>% - mutate(source = 'final_US') - -base.loneliness <- base.dat %>% - dplyr::select(pidp, time, loneliness) %>% - filter(loneliness > 0) %>% - group_by(time, loneliness) %>% - count() %>% - mutate(source = 'baseline_output') - -# merge before plot -loneliness <- rbind(raw.loneliness, base.loneliness) -loneliness$loneliness <- as.factor(loneliness$loneliness) - -# Now plot -ggplot(data = loneliness, mapping = aes(x = time, y = n, group = loneliness, colour = loneliness)) + - geom_line() + - geom_vline(xintercept=start.year, linetype='dotted') + - labs(title = 'Loneliness', subtitle = 'Counts by level') + - xlab('Year') + - ylab('Count') - -loneliness.norm <- loneliness %>% - group_by(time) %>% - filter(loneliness != -9) %>% - mutate(total = sum(n)) %>% - mutate(prct = (n / total)) - -ggplot(data = loneliness.norm, mapping = aes(x = time, y = prct, fill=loneliness)) + - geom_bar(stat = 'identity') + - geom_vline(xintercept=start.year, linetype='dotted') + - labs(title = 'Loneliness') + - xlab('Year') + - ylab('Proportion') - -if (shall.we.save) { - ggsave(filename = 'loneliness.png', - plot = last_plot(), - path = save.path) -} - -rm(raw.loneliness, base.loneliness, loneliness, loneliness.norm) +handover_ordinal(raw.dat, base.dat, var = 'loneliness', save = shall.we.save) ``` ### Spaghetti @@ -582,69 +214,7 @@ rm(raw.s, base.s, spag) TODO: Drop negative values ONLY from waves with no data i.e. not 7,9,11 ```{r} -# first figure out how to plot final_US -# start with hh_income -raw.nut <- raw.dat %>% - dplyr::select(pidp, time, nutrition_quality) %>% - filter(nutrition_quality >= 0) %>% - group_by(time) %>% - summarise(nutrition_quality = mean(nutrition_quality, na.rm = TRUE)) %>% - mutate(source = 'final_US') - -base.nut <- base.dat %>% - dplyr::select(pidp, time, nutrition_quality) %>% - group_by(time) %>% - summarise(nutrition_quality = mean(nutrition_quality, na.rm = TRUE)) %>% - mutate(source = 'baseline_output') - -# merge before plot -nutrition_quality <- rbind(raw.nut, base.nut) - -# Now plot -ggplot(data = nutrition_quality, mapping = aes(x = time, y = nutrition_quality, group = source, colour = source)) + - geom_line() + - geom_vline(xintercept=start.year, linetype='dotted') + - labs(title = 'Nutrition Quality', subtitle = 'Full Sample') + - xlab('Year') + - ylab('Nutrition Quality') - -## Try a version where final_US is limited to only those present from wave 1 -# onwards because the sample refreshments are messing with the plot -raw.wave1 <- raw.dat$pidp[raw.dat$time == 2009] - -raw.nut2 <- raw.dat %>% - dplyr::select(pidp, time, nutrition_quality) %>% - filter(pidp %in% raw.wave1) %>% - filter(nutrition_quality >= 0) %>% - group_by(time) %>% - summarise(nutrition_quality = mean(nutrition_quality, na.rm = TRUE)) %>% - mutate(source = 'final_US') - -base.nut2 <- base.dat %>% - dplyr::select(pidp, time, nutrition_quality) %>% - filter(pidp %in% raw.wave1) %>% - group_by(time) %>% - summarise(nutrition_quality = mean(nutrition_quality, na.rm = TRUE)) %>% - mutate(source = 'baseline_output') - -nutrition_quality2 <- rbind(raw.nut2, base.nut2) - -# Now plot -ggplot(data = nutrition_quality2, mapping = aes(x = time, y = nutrition_quality, group = source, colour = source)) + - geom_line() + - geom_vline(xintercept=start.year, linetype='dotted') + - labs(title = 'Nutrition Quality') + - xlab('Year') + - ylab('Nutrition Quality') - -if (shall.we.save) { - ggsave(filename = 'nutrition_quality_wav1.png', - plot = last_plot(), - path = save.path) -} - -rm(raw.nut, base.nut, nutrition_quality, raw.wave1, raw.nut2, base.nut2, - nutrition_quality2) +handover_continuous(raw.dat, base.dat, var = 'nutrition_quality') ``` ### Spaghetti @@ -669,82 +239,7 @@ rm(raw.s, base.s, spag) NOTE: At present, physical health is not predicted or transitioned. ```{r} -# first figure out how to plot final_US -# start with hh_income -raw.phealth <- raw.dat %>% - dplyr::select(pidp, time, phealth) %>% - filter(phealth >= 0) %>% - filter(time > 2009) %>% - group_by(time) %>% - summarise(phealth = mean(phealth, na.rm = TRUE)) %>% - mutate(source = 'final_US') - -base.phealth <- base.dat %>% - dplyr::select(pidp, time, phealth) %>% - filter(time > 2009) %>% - group_by(time) %>% - summarise(phealth = mean(phealth, na.rm = TRUE)) %>% - mutate(source = 'baseline_output') - -# merge before plot -phealth <- rbind(raw.phealth, base.phealth) - -# Now plot -ggplot(data = phealth, mapping = aes(x = time, y = phealth, group = source, colour = source)) + - geom_line() + - geom_vline(xintercept=start.year, linetype='dotted') + - labs(title = 'Physical Health', subtitle = 'Full Sample') + - xlab('Year') + - ylab('Average') - -## Try a version where final_US is limited to only those present from wave 1 -# onwards because the sample refreshments are messing with the plot -raw.wave1 <- raw.dat$pidp[raw.dat$time == 2009] - -raw.phealth2 <- raw.dat %>% - dplyr::select(pidp, time, phealth) %>% - filter(time > 2009) %>% - filter(pidp %in% raw.wave1) %>% - group_by(time) %>% - summarise(phealth = mean(phealth, na.rm = TRUE)) %>% - mutate(source = 'final_US') - -base.phealth2 <- base.dat %>% - dplyr::select(pidp, time, phealth) %>% - filter(time > 2009) %>% - filter(pidp %in% raw.wave1) %>% - group_by(time) %>% - summarise(phealth = mean(phealth, na.rm = TRUE)) %>% - mutate(source = 'baseline_output') - -phealth2 <- rbind(raw.phealth2, base.phealth2) - -# Now plot -ggplot(data = phealth2, mapping = aes(x = time, y = phealth, group = source, colour = source)) + - geom_line() + - geom_vline(xintercept=start.year, linetype='dotted') + - labs(title = 'Physical Health') + - xlab('Year') + - ylab('Average') - -if (shall.we.save) { - ggsave(filename = 'physical_health_wav1.png', - plot = last_plot(), - path = save.path) -} - -#raw.phealth.check <- raw.dat %>% -# select(pidp, time, phealth) %>% -# filter(time > 2009) %>% -# filter(pidp %in% raw.wave1) - -#base.phealth.check <- base.dat %>% -# select(pidp, time, phealth) %>% -# filter(time > 2009) %>% -# filter(pidp %in% raw.wave1) - -rm(raw.phealth, base.phealth, phealth, raw.wave1, raw.phealth2, base.phealth2, phealth2) -#rm(raw.phealth.check, base.phealth.check) +handover_continuous(raw.dat, base.dat, var = 'phealth') ``` ### Spaghetti @@ -767,36 +262,7 @@ rm(raw.s, base.s, spag) ## Education ```{r} -prepped.list <- validation_prep_ordinal(raw.dat, base.dat, 'education_state') - -var <- prepped.list$var -norm <- prepped.list$norm - -tit <- 'Education State' - -# Now plot -ggplot(data = var, mapping = aes(x = time, y = n, group = education_state, colour = education_state)) + - geom_line() + - geom_vline(xintercept=start.year, linetype='dotted') + - labs(title = tit, subtitle = 'Counts by Level') + - xlab('Year') + - ylab('Count') - -ggplot(data = norm, mapping = aes(x = time, y = prct, fill=education_state)) + - geom_bar(stat = 'identity') + - geom_vline(xintercept=start.year, linetype='dotted') + - labs(title = tit) + - xlab('Year') + - ylab('Proportion') - -if (shall.we.save) { - ggsave(filename = 'education_state.png', - plot = last_plot(), - path = save.path) -} - - -rm(prepped.list, var, norm, tit) +handover_ordinal(raw.dat, base.dat, var = 'education_state', save = shall.we.save) ``` From 49d8979b0fee23c4b0e8e2d2ae6666190e075793 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Fri, 7 Jul 2023 15:07:59 +0100 Subject: [PATCH 017/229] Added auditc vars to format_raw --- minos/data_generation/US_format_raw.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/minos/data_generation/US_format_raw.py b/minos/data_generation/US_format_raw.py index 3ad5f761..cda23194 100755 --- a/minos/data_generation/US_format_raw.py +++ b/minos/data_generation/US_format_raw.py @@ -297,6 +297,12 @@ def format_ukhls_columns(year): 'hhsize': 'hhsize', # number of people in household 'tenure_dv': 'housing_tenure', # housing tenure type (owned, rented etc.) 'urban_dv': 'urban', # urban or rural household. + # Alcohol Use Disorder Variables (auditc) + 'auditc1': 'auditc1', # Past 12 months alcohol drink? + 'auditc2': 'auditc2', # Always been non-drinker + 'auditc3': 'auditc3', # Alcohol frequency past 12 months + 'auditc4': 'auditc4', # Drinks on typical day + 'auditc5': 'auditc5', # Six or more drinks frequency } # Some variables change names halfway through UKHLS. From 909fd4e4664913c5d1ab286474c5087fbcb34eae Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 10 Jul 2023 14:35:09 +0100 Subject: [PATCH 018/229] Added the AUDITC score using auditc vars from US. Calculated the 3 item AUDITC score and run through complete case --- minos/data_generation/US_complete_case.py | 4 + .../generate_composite_vars.py | 96 ++++++++++++++++++- 2 files changed, 96 insertions(+), 4 deletions(-) diff --git a/minos/data_generation/US_complete_case.py b/minos/data_generation/US_complete_case.py index 2289aa94..cfbd00fc 100755 --- a/minos/data_generation/US_complete_case.py +++ b/minos/data_generation/US_complete_case.py @@ -89,6 +89,10 @@ def complete_case_custom_years(data, var, years): data['S7_mental_health'] = data['S7_mental_health'].astype(int) data = complete_case_custom_years(data, 'S7_labour_state', years=list(range(2009, 2021, 1))) + # PCS Vars + # AUDITC (alcohol) - present in 2015, 2017, 2019, 2020 + data = complete_case_custom_years(data, 'auditc', years=[2015, 2017, 2019, 2020]) + drop_columns = [#'financial_situation', # these are just SF12 MICE columns for now. see US_format_raw.py 'ghq_depression', 'scsf1', diff --git a/minos/data_generation/generate_composite_vars.py b/minos/data_generation/generate_composite_vars.py index ef7c1069..b2a3457b 100755 --- a/minos/data_generation/generate_composite_vars.py +++ b/minos/data_generation/generate_composite_vars.py @@ -766,6 +766,7 @@ def generate_parents_education(data): # Groupby hid then filter to make sure there is a 16 year old in the house grouped_dat = data.groupby(['hidp'], axis=1).filter(lambda d: (d.age != 16.0), axis=0) # filtered_dat = grouped_dat.filter(lambda d: (d['age'] != 16)) + # TODO: FINISH THIS! return data @@ -775,12 +776,12 @@ def calculate_equivalent_income(data): Parameters ---------- - pop: PopulationView - Population from MINOS to calculate next income for. + data: pd.DataFrame + US data Returns ------- - nextWaveIncome: pd.Series - Vector of new household incomes from OLS prediction. + data : pd.DataFrame + US data with variable for equivalent income """ print('Calculating equivalent income...') # This is a deterministic calculation based on the values from each of the SIPHER7 variables @@ -900,6 +901,92 @@ def calculate_equivalent_income(data): return data +def calculate_auditc_score(data): + """ + Alcohol use disorders can be assessed via the AUDITC score. This score is derived from 3 questions that form part of + the full 10 question AUDIT screening test, where AUDITC specifically focuses on consumption. The 3 questions are: + + 1. How often do you have a drink containing alcohol? + 2. How many units of alcohol do you drink on a typical day when you are drinking? + 3. How often have you had 6 or more units if female, or 8 or more if male, on a single occasion in the last year? + + Each question is ordinal with 5 levels, depending on the 'severity' of the answer. We then score each question from + 0-4, with higher scores meaning higher 'severity'. The total across the 3 questions then creates a score from 0-12, + with 0-4 meaning sensible drinking, 5-7 meaning hazardous drinking, and 8+ meaning harmful drinking. + See following link for information on scoring: + https://www.drinktalkingportal.co.uk/clinical-guidance/alcohol-abuse-screening/alcohol-audit-audit-c + + To calculate this score, we rely on 4 variables in Understanding Society shown at the following link: + https://www.understandingsociety.ac.uk/documentation/mainstage/dataset-documentation?search_api_views_fulltext=auditc + + Question 1 above relies on auditc1 & auditc3, question 2 relies on auditc4, and question 3 uses auditc5. + + NOTE: The final variable used (auditc5) specifically mentions 6 or more drink frequency, rather than 6+/8+ units. + This could be a mistake in the description or the actual question asked being incorrect (not the true AUDITC + question). There's no information about which one it is, so I'm treating it the same as the AUDITC3 question + for our purposes. Added benefit that this is simpler to code without checking for gender also. + + Parameters + ---------- + data: pd.DataFrame + US data + Returns + ------- + data : pd.DataFrame + US data with variable for equivalent income + """ + print('Calculating AUDITC score (alcohol use disorder)...') + + # AUDITC1 - How often do you have a drink containing alcohol + data['temp_auditc1'] = -9 + data['temp_auditc1'][data['auditc1'] == 2] = 0 # no alcoholic drink in past 12 months + data['temp_auditc1'][data['auditc2'] == 1] = 0 # always been non-drinker (maybe not necessary but thorough) + data['temp_auditc1'][data['auditc3'] == 1] = 0 # Never drank in past 12 months (again might be doubling up) + data['temp_auditc1'][data['auditc3'] == 2] = 1 # Drank in past 12 months: monthly or less + data['temp_auditc1'][data['auditc3'] == 3] = 2 # Drank in past 12 months: 2-4 times per month + data['temp_auditc1'][data['auditc3'] == 4] = 3 # Drank in past 12 months: 2-3 times per week + data['temp_auditc1'][data['auditc3'] == 5] = 4 # Drank in past 12 months: 4+ times per week + + # AUDITC2 - How many units drank on typical day + data['temp_auditc2'] = -9 + data['temp_auditc2'][data['auditc1'] == 2] = 0 # no alcoholic drink in past 12 months + data['temp_auditc2'][data['auditc2'] == 1] = 0 # always been non-drinker (maybe not necessary but thorough) + data['temp_auditc2'][data['auditc3'] == 1] = 0 # Never drank in past 12 months (again might be doubling up) + data['temp_auditc2'][data['auditc4'] == 1] = 0 # Drinks on typical day: 1-2 + data['temp_auditc2'][data['auditc4'] == 2] = 1 # Drinks on typical day: 3-4 + data['temp_auditc2'][data['auditc4'] == 3] = 2 # Drinks on typical day: 5-6 + data['temp_auditc2'][data['auditc4'] == 4] = 3 # Drinks on typical day: 7-9 + data['temp_auditc2'][data['auditc4'] == 5] = 4 # Drinks on typical day: 10+ + + # AUDITC3 - Six or more drinks frequency + data['temp_auditc3'] = -9 + data['temp_auditc3'][data['auditc1'] == 2] = 0 # no alcoholic drink in past 12 months + data['temp_auditc3'][data['auditc2'] == 1] = 0 # always been non-drinker (maybe not necessary but thorough) + data['temp_auditc3'][data['auditc3'] == 1] = 0 # Never drank in past 12 months (again might be doubling up) + data['temp_auditc3'][data['auditc5'] == 1] = 0 # Six or more drinks frequency: Never + data['temp_auditc3'][data['auditc5'] == 2] = 1 # Six or more drinks frequency: Less than monthly + data['temp_auditc3'][data['auditc5'] == 3] = 2 # Six or more drinks frequency: Monthly + data['temp_auditc3'][data['auditc5'] == 4] = 3 # Six or more drinks frequency: Weekly + data['temp_auditc3'][data['auditc5'] == 5] = 4 # Six or more drinks frequency: Daily or almost daily + + # Combined score + data['auditc'] = 0 + data['auditc'][data['temp_auditc1'] >= 0] = data['auditc'] + data['temp_auditc1'] + data['auditc'][data['temp_auditc2'] >= 0] = data['auditc'] + data['temp_auditc2'] + data['auditc'][data['temp_auditc3'] >= 0] = data['auditc'] + data['temp_auditc3'] + # if any of the components are missing, set the final value as missing also + data['auditc'][(data['temp_auditc1'] == -9) | + (data['temp_auditc2'] == -9) | + (data['temp_auditc3'] == -9)] = -9 + + #data.drop(labels=['auditc1', 'auditc2', 'auditc3', 'auditc4', 'auditc5', + # 'temp_auditc1', 'temp_auditc2', 'temp_auditc3'], + # axis=1, + # inplace=True) + + return data + + def main(): maxyr = US_utils.get_data_maxyr() # first collect and load the datafiles for every year @@ -923,6 +1010,7 @@ def main(): data = generate_marital_status(data) # marital status data = generate_physical_health_score(data) # physical health score data = calculate_equivalent_income(data) # equivalent income + data = calculate_auditc_score(data) print('Finished composite generation. Saving data...') US_utils.save_multiple_files(data, years, "data/composite_US/", "") From 3e47ef2130d987664246a1e3b4534e625b3068a6 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 10 Jul 2023 16:11:29 +0100 Subject: [PATCH 019/229] Dropped variables used to calculate auditc score --- .../generate_composite_vars.py | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/minos/data_generation/generate_composite_vars.py b/minos/data_generation/generate_composite_vars.py index b2a3457b..b1d5e27c 100755 --- a/minos/data_generation/generate_composite_vars.py +++ b/minos/data_generation/generate_composite_vars.py @@ -970,19 +970,27 @@ def calculate_auditc_score(data): data['temp_auditc3'][data['auditc5'] == 5] = 4 # Six or more drinks frequency: Daily or almost daily # Combined score - data['auditc'] = 0 - data['auditc'][data['temp_auditc1'] >= 0] = data['auditc'] + data['temp_auditc1'] - data['auditc'][data['temp_auditc2'] >= 0] = data['auditc'] + data['temp_auditc2'] - data['auditc'][data['temp_auditc3'] >= 0] = data['auditc'] + data['temp_auditc3'] + data['auditc_score'] = 0 + data['auditc_score'][data['temp_auditc1'] >= 0] += data['temp_auditc1'] + data['auditc_score'][data['temp_auditc2'] >= 0] += data['temp_auditc2'] + data['auditc_score'][data['temp_auditc3'] >= 0] += data['temp_auditc3'] # if any of the components are missing, set the final value as missing also - data['auditc'][(data['temp_auditc1'] == -9) | + data['auditc_score'][(data['temp_auditc1'] == -9) | (data['temp_auditc2'] == -9) | (data['temp_auditc3'] == -9)] = -9 - #data.drop(labels=['auditc1', 'auditc2', 'auditc3', 'auditc4', 'auditc5', - # 'temp_auditc1', 'temp_auditc2', 'temp_auditc3'], - # axis=1, - # inplace=True) + # Ordinal values + data['auditc'] = '-9' + data['auditc'][data['auditc_score'] == 0] = 'Non-drinker' # 0 score is non-drinker + data['auditc'][data['auditc_score'].isin(range(1, 5))] = 'Low Risk' # 1-4 is sensible + data['auditc'][data['auditc_score'].isin(range(5, 8))] = 'Increased Risk' # 5-7 is hazardous + data['auditc'][data['auditc_score'] >= 8] = 'High Risk' # 8-12 is harmful + + data.drop(labels=['auditc1', 'auditc2', 'auditc3', 'auditc4', 'auditc5', + 'temp_auditc1', 'temp_auditc2', 'temp_auditc3', + 'auditc_score'], + axis=1, + inplace=True) return data From e0357b4840893173a7009d9714e8b5add4cd6fa5 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 10 Jul 2023 16:11:47 +0100 Subject: [PATCH 020/229] Added script for testing auditc variable --- minos/testing/auditc.Rmd | 91 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 minos/testing/auditc.Rmd diff --git a/minos/testing/auditc.Rmd b/minos/testing/auditc.Rmd new file mode 100644 index 00000000..7d938c8a --- /dev/null +++ b/minos/testing/auditc.Rmd @@ -0,0 +1,91 @@ +--- +title: "AUDITC Testing" +output: + html_document: + toc: true + toc_float: true + collapsed: false + number_sections: true + df_print: paged + code_folding: hide +--- + +# SETUP + +```{r "setup", include=FALSE} + +require(tidyverse) +require(ggplot2) +require(knitr) +require(here) + +#workingDir <- "/home/luke/Documents/WORK/MINOS/" +workingDir <- normalizePath('../') +knitr::opts_knit$set(root.dir = workingDir) +knitr::opts_chunk$set( + warning = FALSE, + message = FALSE +) +rm(workingDir) +``` + +Source utils for some functions. + +```{r} +source(here::here('minos', 'utils_datain.R')) +source(here::here('minos', 'utils_validation_vis.R')) +source(here::here('minos', 'validation', 'utils.r')) +``` + +## Data + +```{r} +# Read raw datafiles in +comp.files <- list.files(here::here('data', 'composite_US'), pattern='[0-9]{4}_US_cohort.csv', full.names = TRUE) +comp.dat <- do.call(rbind, lapply(comp.files, read.csv)) + +final.files <- list.files(here::here('data', 'final_US'), pattern='[0-9]{4}_US_cohort.csv', full.names = TRUE) +final.dat <- do.call(rbind, lapply(final.files, read.csv)) +``` + +# Analysis + +```{r} +comp <- comp.dat %>% + dplyr::select(pidp, time, auditc) %>% + dplyr::filter(time >= 2015) %>% + dplyr::filter(time != 2016, time != 2018) %>% + group_by(time, auditc) %>% + count() + +comp$auditc <- as.factor(comp$auditc) + +ggplot(comp, aes(x = time, y = n, group = auditc, color = auditc)) + + geom_line() + + labs(title = 'AUDITC - Corrected Data') + + ylab('Counts') +``` + + + +```{r} +final <- final.dat %>% + dplyr::select(pidp, time, auditc) %>% + dplyr::filter(time >= 2015) %>% + dplyr::filter(time != 2016, time != 2018) %>% + group_by(time, auditc) %>% + count() + +final$auditc <- as.factor(final$auditc) + +ggplot(final, aes(x = time, y = n, group = auditc, color = auditc)) + + geom_line() + + labs(title = 'AUDITC - Final Data') + + ylab('Counts') +``` + + +```{r} + +``` + From 559849701b1f90bf03614bc0cbb72ba301509823 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 10 Jul 2023 16:13:55 +0100 Subject: [PATCH 021/229] Estimated a model for auditc, and modified the estimate_transitions.R script to fit this model --- minos/transitions/estimate_transitions.R | 2 ++ minos/transitions/model_definitions_default.txt | 1 + 2 files changed, 3 insertions(+) diff --git a/minos/transitions/estimate_transitions.R b/minos/transitions/estimate_transitions.R index 8a271ffc..bdc5c9d0 100644 --- a/minos/transitions/estimate_transitions.R +++ b/minos/transitions/estimate_transitions.R @@ -322,6 +322,8 @@ run_yearly_models <- function(transitionDir_path, if(grepl('neighbourhood_safety', dependent)){ depend.year <- year + 3 } # set up 3 year horizon # tobacco model only estimated for 2013 onwards if(dependent == 'ncigs' & year < 2013) { next } + # alcohol model (auditc) only in specific years + if(dependent == 'auditc' & !year %in% c(2014, 2016, 2018, 2019)) { next } #TODO: Maybe copy values from wave 2 onto wave 1? Assuming physical health changes slowly? # SF_12 predictor (physical health score) not available in wave 1 if(dependent %in% c('SF_12_MCS', 'SF_12_PCS') & year == 2009) { next } diff --git a/minos/transitions/model_definitions_default.txt b/minos/transitions/model_definitions_default.txt index be413245..359a382c 100644 --- a/minos/transitions/model_definitions_default.txt +++ b/minos/transitions/model_definitions_default.txt @@ -1,3 +1,4 @@ +NNET : auditc ~ age + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + hh_income + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + factor(financial_situation) CLM : financial_situation ~ factor(financial_situation) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(job_sec)+ scale(hh_income)+ I(scale(hh_income)**2) + factor(marital_status) + hhsize + factor(housing_tenure) LOGIT : heating ~ factor(heating) + scale(SF_12_MCS) + scale(SF_12_PCS) + relevel(factor(ethnicity), ref='WBI') + hh_income + factor(marital_status)+ ncigs + hhsize + factor(urban) + factor(housing_tenure) + factor(financial_situation) LOGIT : urban ~ age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = "1") From e2d009ed2c8ba86c3ad3146f63b05708c34f521c Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 10 Jul 2023 16:14:41 +0100 Subject: [PATCH 022/229] Updated alcohol module and added back to the module list in all default config files --- .../cross_validation_default1.yaml | 2 +- .../cross_validation_default2.yaml | 2 +- .../cross_validation_default3.yaml | 2 +- .../cross_validation_default4.yaml | 2 +- .../cross_validation_default5.yaml | 2 +- config/default.yaml | 2 +- minos/modules/alcohol.py | 45 ++++++++++++------- 7 files changed, 34 insertions(+), 23 deletions(-) diff --git a/config/cross_validation/cross_validation_default1.yaml b/config/cross_validation/cross_validation_default1.yaml index 3ca9bbf3..8ca9d7ab 100644 --- a/config/cross_validation/cross_validation_default1.yaml +++ b/config/cross_validation/cross_validation_default1.yaml @@ -30,7 +30,7 @@ cross_validation: TRUE # Similarly Mortality requires Replenishment but everything else requires Mortality so it goes second. (priority 1) # Finally everything else can go in any order (priority 2). #components : [MWB(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Income(), Housing(), Labour(), Nutrition(), Education(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] -components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Neighbourhood(), Heating(), Housing(), HousingTenure(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] +components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Heating(), Housing(), HousingTenure(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] scale_rates: diff --git a/config/cross_validation/cross_validation_default2.yaml b/config/cross_validation/cross_validation_default2.yaml index 12cbea9c..e5872ba0 100644 --- a/config/cross_validation/cross_validation_default2.yaml +++ b/config/cross_validation/cross_validation_default2.yaml @@ -30,7 +30,7 @@ cross_validation: TRUE # Similarly Mortality requires Replenishment but everything else requires Mortality so it goes second. (priority 1) # Finally everything else can go in any order (priority 2). #components : [MWB(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Income(), Housing(), Labour(), Nutrition(), Education(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] -components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Neighbourhood(), Heating(), Housing(), HousingTenure(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] +components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Heating(), Housing(), HousingTenure(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] scale_rates: diff --git a/config/cross_validation/cross_validation_default3.yaml b/config/cross_validation/cross_validation_default3.yaml index 5a33b1e8..31e0843f 100644 --- a/config/cross_validation/cross_validation_default3.yaml +++ b/config/cross_validation/cross_validation_default3.yaml @@ -30,7 +30,7 @@ cross_validation: TRUE # Similarly Mortality requires Replenishment but everything else requires Mortality so it goes second. (priority 1) # Finally everything else can go in any order (priority 2). #components : [MWB(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Income(), Housing(), Labour(), Nutrition(), Education(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] -components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Neighbourhood(), Heating(), Housing(), HousingTenure(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] +components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Heating(), Housing(), HousingTenure(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] scale_rates: diff --git a/config/cross_validation/cross_validation_default4.yaml b/config/cross_validation/cross_validation_default4.yaml index a846d4aa..746d00b6 100644 --- a/config/cross_validation/cross_validation_default4.yaml +++ b/config/cross_validation/cross_validation_default4.yaml @@ -30,7 +30,7 @@ cross_validation: TRUE # Similarly Mortality requires Replenishment but everything else requires Mortality so it goes second. (priority 1) # Finally everything else can go in any order (priority 2). #components : [MWB(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Income(), Housing(), Labour(), Nutrition(), Education(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] -components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Neighbourhood(), Heating(), Housing(), HousingTenure(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] +components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Heating(), Housing(), HousingTenure(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] scale_rates: diff --git a/config/cross_validation/cross_validation_default5.yaml b/config/cross_validation/cross_validation_default5.yaml index 39fe0aae..d81208b8 100644 --- a/config/cross_validation/cross_validation_default5.yaml +++ b/config/cross_validation/cross_validation_default5.yaml @@ -30,7 +30,7 @@ cross_validation: TRUE # Similarly Mortality requires Replenishment but everything else requires Mortality so it goes second. (priority 1) # Finally everything else can go in any order (priority 2). #components : [MWB(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Income(), Housing(), Labour(), Nutrition(), Education(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] -components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Neighbourhood(), Heating(), Housing(), HousingTenure(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] +components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Heating(), Housing(), HousingTenure(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] scale_rates: diff --git a/config/default.yaml b/config/default.yaml index 4b2e5d80..22853a1c 100755 --- a/config/default.yaml +++ b/config/default.yaml @@ -27,7 +27,7 @@ cross_validation: FALSE # Similarly Mortality requires Replenishment but everything else requires Mortality so it goes second. (priority 1) # Finally everything else can go in any order (priority 2). #components : [MWB(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Income(), Housing(), Labour(), Nutrition(), Education(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] -components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Neighbourhood(), Heating(), Housing(), HousingTenure(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] +components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Heating(), Housing(), HousingTenure(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] scale_rates: diff --git a/minos/modules/alcohol.py b/minos/modules/alcohol.py index a6ad78e6..83cbb883 100755 --- a/minos/modules/alcohol.py +++ b/minos/modules/alcohol.py @@ -10,6 +10,7 @@ import matplotlib.pyplot as plt from seaborn import histplot + class Alcohol(Base): # Special methods used by vivarium. @@ -56,11 +57,9 @@ def setup(self, builder): 'region', 'hh_income', 'SF_12_MCS', + 'SF_12_PCS', 'education_state', - 'labour_state', - 'job_sec', - 'hh_income', - 'alcohol_spending'] + 'financial_situation'] #view_columns += self.transition_model.rx2('model').names self.population_view = builder.population.get_view(columns=view_columns) @@ -86,14 +85,16 @@ def on_time_step(self, event): self.year = event.time.year ## Predict next alcohol value - newWaveAlcohol = self.calculate_alcohol(pop) - newWaveAlcohol = pd.DataFrame(newWaveAlcohol, columns=["alcohol_spending"]) + alcohol_prob_df = self.calculate_alcohol(pop) + alcohol_prob_df["auditc"] = self.random.choice(alcohol_prob_df.index, + list(alcohol_prob_df.columns), + alcohol_prob_df) # Set index type to int (instead of object as previous) - newWaveAlcohol.index = newWaveAlcohol.index.astype(int) + alcohol_prob_df.index = alcohol_prob_df.index.astype(int) # Draw individuals next states randomly from this distribution. # Update population with new alcohol - self.population_view.update(newWaveAlcohol['alcohol_spending'].astype(int)) + self.population_view.update(alcohol_prob_df["auditc"]) def calculate_alcohol(self, pop): @@ -106,15 +107,25 @@ def calculate_alcohol(self, pop): Returns ------- """ - # load transition model based on year. - year = min(self.year, 2018) - transition_model = r_utils.load_transitions(f"alcohol/zip/alcohol_zip_{year}_{year + 1}", path=self.transition_dir) - # The calculation relies on the R predict method and the model that has already been specified - nextWaveAlcohol = r_utils.predict_next_timestep_zip(model = transition_model, - current = pop, - dependent = 'alcohol_spending', - rescale_factor = 50) - return nextWaveAlcohol + # load transition model based on year + # For alcohol, selecting the 2018 model as the 2019 model sample has lots more missing for some reason + if self.cross_validation: + # if cross-val, fix year to final year model + year = 2018 + else: + year = min(self.year, 2018) + + cols = ['Non-drinker', 'Low Risk', 'Increased Risk', 'High Risk'] + + transition_model = r_utils.load_transitions(f"auditc/nnet/auditc_{year}_{year + 1}", + self.rpy2Modules, + path=self.transition_dir) + # returns probability matrix (3xn) of next ordinal state. + prob_df = r_utils.predict_nnet(transition_model, + self.rpy2Modules, + pop, + cols) + return prob_df def plot(self, pop, config): From 4a361f3d196c955a0b2d2d67edcbebb63aed8ee2 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 10 Jul 2023 16:15:12 +0100 Subject: [PATCH 023/229] Added a section for alcohol to both handovers and cross_validation_default notebooks --- minos/validation/cross_validation_default.Rmd | 15 +++++++++++++++ minos/validation/handovers.Rmd | 3 ++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/minos/validation/cross_validation_default.Rmd b/minos/validation/cross_validation_default.Rmd index 37d9c3a9..9d73ac5c 100644 --- a/minos/validation/cross_validation_default.Rmd +++ b/minos/validation/cross_validation_default.Rmd @@ -267,6 +267,21 @@ cv_ordinal_plots(pivoted.df = heat.pivoted, rm(heat.pivoted) ``` +# Alcohol + +```{r} +alc.pivoted <- combine_and_pivot_long(df1 = cv, + df1.name = 'simulated', + df2 = raw, + df2.name = 'raw', + var = 'auditc') + +cv_ordinal_plots(pivoted.df = alc.pivoted, + var = 'auditc', + save = FALSE) +rm(alc.pivoted) +``` + # SECONDARY VARS ## Marital Status diff --git a/minos/validation/handovers.Rmd b/minos/validation/handovers.Rmd index 47797bc4..3654f273 100644 --- a/minos/validation/handovers.Rmd +++ b/minos/validation/handovers.Rmd @@ -297,8 +297,9 @@ spaghetti_plot(spag, 'education_state', rm(raw.s, base.s, spag) ``` +## Alcohol (AUDITC) ```{r} - +handover_ordinal(raw.dat, base.dat, var = 'auditc', save = shall.we.save) ``` From 47107b80564ac2cacb9d69a978f9792906dbe64a Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 10 Jul 2023 16:21:22 +0100 Subject: [PATCH 024/229] Forgot to add auditc to both alcohol module and replenishment (oops). Also added code to load the correct RPy2 modules in alcohol module --- minos/modules/alcohol.py | 4 +++- minos/modules/replenishment.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/minos/modules/alcohol.py b/minos/modules/alcohol.py index 83cbb883..5eb77b54 100755 --- a/minos/modules/alcohol.py +++ b/minos/modules/alcohol.py @@ -39,6 +39,7 @@ def setup(self, builder): """ # Load in inputs from pre-setup. + self.rpy2Modules = builder.data.load("rpy2_modules") # Build vivarium objects for calculating transition probabilities. # Typically this is registering rate/lookup tables. See vivarium docs/other modules for examples. @@ -59,7 +60,8 @@ def setup(self, builder): 'SF_12_MCS', 'SF_12_PCS', 'education_state', - 'financial_situation'] + 'financial_situation', + 'auditc'] #view_columns += self.transition_model.rx2('model').names self.population_view = builder.population.get_view(columns=view_columns) diff --git a/minos/modules/replenishment.py b/minos/modules/replenishment.py index 6c135676..3228440b 100755 --- a/minos/modules/replenishment.py +++ b/minos/modules/replenishment.py @@ -94,7 +94,7 @@ def setup(self, builder): 'financial_situation', 'housing_tenure', 'urban', - ] + 'auditc'] # Shorthand methods for readability. self.population_view = builder.population.get_view(view_columns) # view simulants From a81140013f69305383d846f6c13a1fe93b7cd43b Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 10 Jul 2023 16:53:59 +0100 Subject: [PATCH 025/229] Defined factor levels for auditc in estimate_transitions.R --- minos/transitions/estimate_transitions.R | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/minos/transitions/estimate_transitions.R b/minos/transitions/estimate_transitions.R index bdc5c9d0..2051859d 100644 --- a/minos/transitions/estimate_transitions.R +++ b/minos/transitions/estimate_transitions.R @@ -245,6 +245,11 @@ run_yearly_models <- function(transitionDir_path, 'FT Education', 'Family Care', 'Not Working')) + data$auditc <- factor(data$auditc, + levels = c('Non-drinker', + 'Low Risk', + 'Increased Risk', + 'High Risk')) # read file repeat{ From 5e18bec90430e2b1e21ee704afa19dcfe378685c Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 11 Jul 2023 15:19:33 +0100 Subject: [PATCH 026/229] Added alcohol to the physical wellbeing model and module, and updated handovers and cross_validation with more plots --- minos/data_generation/generate_stock_pop.py | 32 ++++++++--- minos/modules/physical_wellbeing.py | 3 +- minos/testing/auditc.Rmd | 21 ++++++- minos/transitions/estimate_transitions.R | 3 + .../transitions/model_definitions_default.txt | 2 +- minos/validation/cross_validation_default.Rmd | 56 ++++++++++++++++--- 6 files changed, 97 insertions(+), 20 deletions(-) diff --git a/minos/data_generation/generate_stock_pop.py b/minos/data_generation/generate_stock_pop.py index 79cb4b83..1f2557e2 100755 --- a/minos/data_generation/generate_stock_pop.py +++ b/minos/data_generation/generate_stock_pop.py @@ -68,7 +68,7 @@ def reweight_stock(data, projections): return reweighted_data -def wave_data_copy(data, var, copy_year, paste_year): +def wave_data_copy(data, var, copy_year, paste_year, var_type): """ Unfortunately due to some of the variables we rely on not being available in all waves, we have to take a copy of some information and paste it onto another year. Due to the current aim (24/02/23) being to include wave 12 of data @@ -118,10 +118,17 @@ def wave_data_copy(data, var, copy_year, paste_year): # drop intermediate columns data_merged.drop(labels=[var_x, var_y], axis=1, inplace=True) - # last step is to impute the still missing with the mean value. Without this we would have to drop all the - # missing values, meaning anybody not in wave 11 would be removed. This is dodgy because we don't know who should be - # missing, but I don't know what else to do - data_merged[var][(data_merged['time'] == paste_year) & (data_merged[var].isna())] = round(data_merged[var][data_merged['time'] == paste_year].mean()) + # last step is to impute the still missing with the median value (code is different for continuous vs ordinal vars). + # Without this we would have to drop all the missing values, meaning anybody not in wave 11 would be removed. + # This is dodgy because we don't know who should actually be missing, but I don't know what else to do + #data_merged[var][(data_merged['time'] == paste_year) & (data_merged[var].isna())] = round(data_merged[var][data_merged['time'] == paste_year].mean()) + if var_type == 'continuous': + data_merged[var][(data_merged['time'] == paste_year) & (data_merged[var].isna())] = \ + data_merged[var][data_merged['time'] == paste_year].median() + elif var_type == 'ordinal': + data_merged[var][(data_merged['time'] == paste_year) & (data_merged[var].isna())] = \ + data_merged[var][data_merged['time'] == paste_year].value_counts().index[0] + return data_merged @@ -143,21 +150,30 @@ def generate_stock(projections, cross_validation): # Will be used in the future for the 16-25 year olds at the beginning of the simulation data['max_educ'] = data['education_state'] + # copy 2015 alcohol data onto 2014 for cross-validation runs + data = wave_data_copy(data, + var='auditc', + copy_year=2015, + paste_year=2014, + var_type='ordinal') # copy 2017 loneliness data onto 2014 for cross-validation runs data = wave_data_copy(data, var='loneliness', copy_year=2017, - paste_year=2014) + paste_year=2014, + var_type='ordinal') # copy wave 11 nutrition_quality onto wave 12 data = wave_data_copy(data, var='nutrition_quality', copy_year=2019, - paste_year=2020) + paste_year=2020, + var_type='continuous') # copy wave 7 nutrition_quality onto wave 6 data = wave_data_copy(data, var='nutrition_quality', copy_year=2015, - paste_year=2014) + paste_year=2014, + var_type='continuous') # Set loneliness and ncigs as int data['loneliness'] = data['loneliness'].astype('int64') diff --git a/minos/modules/physical_wellbeing.py b/minos/modules/physical_wellbeing.py index ed1219b1..93317367 100644 --- a/minos/modules/physical_wellbeing.py +++ b/minos/modules/physical_wellbeing.py @@ -68,7 +68,8 @@ def setup(self, builder): 'nutrition_quality', 'neighbourhood_safety', 'loneliness', - 'financial_situation'] + 'financial_situation', + 'auditc'] self.population_view = builder.population.get_view(columns=view_columns) diff --git a/minos/testing/auditc.Rmd b/minos/testing/auditc.Rmd index 7d938c8a..87b007a7 100644 --- a/minos/testing/auditc.Rmd +++ b/minos/testing/auditc.Rmd @@ -45,7 +45,10 @@ comp.files <- list.files(here::here('data', 'composite_US'), pattern='[0-9]{4}_U comp.dat <- do.call(rbind, lapply(comp.files, read.csv)) final.files <- list.files(here::here('data', 'final_US'), pattern='[0-9]{4}_US_cohort.csv', full.names = TRUE) -final.dat <- do.call(rbind, lapply(final.files, read.csv)) +final.dat <- do.call(rbind, lapply(final.files, read.csv)) + +cv.files <- list.files(here::here('data', 'final_US', 'cross_validation', 'batch1'), pattern='[0-9]{4}_US_cohort.csv', full.names = TRUE) +cv.dat <- do.call(rbind, lapply(final.files, read.csv)) ``` # Analysis @@ -84,6 +87,22 @@ ggplot(final, aes(x = time, y = n, group = auditc, color = auditc)) + ylab('Counts') ``` +```{r} +cv <- cv.dat %>% + dplyr::select(pidp, time, auditc) %>% + dplyr::filter(time >= 2015) %>% + dplyr::filter(time != 2016, time != 2018) %>% + group_by(time, auditc) %>% + count() + +cv$auditc <- as.factor(cv$auditc) + +ggplot(cv, aes(x = time, y = n, group = auditc, color = auditc)) + + geom_line() + + labs(title = 'AUDITC - Cross-Validation Batch 1') + + ylab('Counts') +``` + ```{r} diff --git a/minos/transitions/estimate_transitions.R b/minos/transitions/estimate_transitions.R index 2051859d..8131d62a 100644 --- a/minos/transitions/estimate_transitions.R +++ b/minos/transitions/estimate_transitions.R @@ -379,6 +379,9 @@ run_yearly_models <- function(transitionDir_path, if(year < 2013) { formula.string <- str_remove(formula.string, " \\+ ncigs") } + if(!year %in% c(2015, 2017, 2019, 2020)) { + formula.string <- str_remove(formula.string, " \\+ factor\\(auditc\\)") + } } #print(formula.string) # Now make string into formula diff --git a/minos/transitions/model_definitions_default.txt b/minos/transitions/model_definitions_default.txt index 359a382c..ad4649bd 100644 --- a/minos/transitions/model_definitions_default.txt +++ b/minos/transitions/model_definitions_default.txt @@ -11,6 +11,6 @@ CLM : loneliness ~ age + factor(sex) + scale(SF_12_MCS) + relevel(factor(educati CLM : neighbourhood_safety ~ age + factor(sex) + factor(job_sec) + relevel(factor(ethnicity), ref = 'WBI') + hh_income + factor(housing_quality) + relevel(factor(region), ref = 'South East') + factor(urban) NNET : education_state ~ factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') ZIP : ncigs ~ age + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + relevel(factor(job_sec), ref = '3') + hh_income + relevel(factor(ethnicity), ref = 'WBI') | relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(job_sec), ref = '3') + hh_income + scale(SF_12_MCS) + scale(SF_12_PCS) -OLS : SF_12_PCS ~ scale(SF_12_PCS) + age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + hh_income + factor(housing_quality) + factor(neighbourhood_safety) + ncigs + nutrition_quality + factor(loneliness) + factor(financial_situation) +OLS : SF_12_PCS ~ scale(SF_12_PCS) + age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + hh_income + factor(housing_quality) + ncigs + nutrition_quality + factor(loneliness) + factor(financial_situation) + factor(auditc) OLS : SF_12_MCS ~ scale(SF_12_MCS) + age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + hh_income + factor(housing_quality) + phealth + factor(neighbourhood_safety) + ncigs + nutrition_quality + factor(loneliness) + factor(financial_situation) OLS_DIFF : SF_12_MCS ~ SF_12_MCS_diff + age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + hh_income + factor(housing_quality) + phealth + factor(neighbourhood_safety) + ncigs + nutrition_quality + factor(loneliness) + factor(financial_situation) diff --git a/minos/validation/cross_validation_default.Rmd b/minos/validation/cross_validation_default.Rmd index 9d73ac5c..45a3fee4 100644 --- a/minos/validation/cross_validation_default.Rmd +++ b/minos/validation/cross_validation_default.Rmd @@ -137,39 +137,77 @@ print(inc.20) rm(inc.14, inc.16, inc.18, inc.20) ``` -# SF_12 +# SF_12_MCS ```{r} -cv.mean.plots(cv1, cv2, cv3, cv4, cv5, raw, 'SF_12') -multi_year_boxplots(raw, cv, 'SF_12') -snapshot_OP_plots(raw, cv, 'SF_12', target.years = c(2014, 2016, 2018, 2020)) -q_q_comparison(raw, cv, 'SF_12') +cv.mean.plots(cv1, cv2, cv3, cv4, cv5, raw, 'SF_12_MCS') +multi_year_boxplots(raw, cv, 'SF_12_MCS') +snapshot_OP_plots(raw, cv, 'SF_12_MCS', target.years = c(2014, 2016, 2018, 2020)) +q_q_comparison(raw, cv, 'SF_12_MCS') ``` ```{r} sf12.14 <- marg_dist_densigram_plot_oneyear(observed = raw, predicted = cv, - var = 'SF_12', + var = 'SF_12_MCS', target.year = 2014) print(sf12.14) ``` ```{r} sf12.16 <- marg_dist_densigram_plot_oneyear(observed = raw, predicted = cv, - var = 'SF_12', + var = 'SF_12_MCS', target.year = 2016) print(sf12.16) ``` ```{r} sf12.18 <- marg_dist_densigram_plot_oneyear(observed = raw, predicted = cv, - var = 'SF_12', + var = 'SF_12_MCS', target.year = 2018) print(sf12.18) ``` ```{r} sf12.20 <- marg_dist_densigram_plot_oneyear(observed = raw, predicted = cv, - var = 'SF_12', + var = 'SF_12_MCS', + target.year = 2020) +print(sf12.20) +rm(sf12.14, sf12.16, sf12.18, sf12.20) +``` + +# SF_12_PCS + +```{r} +cv.mean.plots(cv1, cv2, cv3, cv4, cv5, raw, 'SF_12_PCS') +multi_year_boxplots(raw, cv, 'SF_12_PCS') +snapshot_OP_plots(raw, cv, 'SF_12_PCS', target.years = c(2014, 2016, 2018, 2020)) +q_q_comparison(raw, cv, 'SF_12_PCS') +``` +```{r} +sf12.14 <- marg_dist_densigram_plot_oneyear(observed = raw, + predicted = cv, + var = 'SF_12_PCS', + target.year = 2014) +print(sf12.14) +``` +```{r} +sf12.16 <- marg_dist_densigram_plot_oneyear(observed = raw, + predicted = cv, + var = 'SF_12_PCS', + target.year = 2016) +print(sf12.16) +``` +```{r} +sf12.18 <- marg_dist_densigram_plot_oneyear(observed = raw, + predicted = cv, + var = 'SF_12_PCS', + target.year = 2018) +print(sf12.18) +``` +```{r} +sf12.20 <- marg_dist_densigram_plot_oneyear(observed = raw, + predicted = cv, + var = 'SF_12_PCS', target.year = 2020) print(sf12.20) rm(sf12.14, sf12.16, sf12.18, sf12.20) From 5e16cf71fe039d0cdb6e22e17d29c09411b652fa Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 11 Jul 2023 16:44:55 +0100 Subject: [PATCH 027/229] Added exercise variables to US_format_raw --- minos/data_generation/US_format_raw.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/minos/data_generation/US_format_raw.py b/minos/data_generation/US_format_raw.py index cda23194..944d6254 100755 --- a/minos/data_generation/US_format_raw.py +++ b/minos/data_generation/US_format_raw.py @@ -303,6 +303,15 @@ def format_ukhls_columns(year): 'auditc3': 'auditc3', # Alcohol frequency past 12 months 'auditc4': 'auditc4', # Drinks on typical day 'auditc5': 'auditc5', # Six or more drinks frequency + # Exercise/activity variables + 'mday': 'mday', # 7 days moderate activites + 'mdhrs': 'mdhrs', # usual hours moderate activities + 'mdmin': 'mdmin', # usual minute moderate activities + 'mwhrs': 'mwhrs', # weekly hours moderate activities + 'vday': 'vday', # 7 days moderate activites + 'vdhrs': 'vdhrs', # usual hours moderate activities + 'vdmin': 'vdmin', # usual minute moderate activities + 'vwhrs': 'vwhrs', # weekly hours moderate activities } # Some variables change names halfway through UKHLS. From de7f685353860f8198b285e3ea7b152087fe5ba6 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Wed, 12 Jul 2023 10:02:07 +0100 Subject: [PATCH 028/229] Forgot to add one of the exercise variables --- minos/data_generation/US_format_raw.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/minos/data_generation/US_format_raw.py b/minos/data_generation/US_format_raw.py index 944d6254..7c5cee2c 100755 --- a/minos/data_generation/US_format_raw.py +++ b/minos/data_generation/US_format_raw.py @@ -308,10 +308,12 @@ def format_ukhls_columns(year): 'mdhrs': 'mdhrs', # usual hours moderate activities 'mdmin': 'mdmin', # usual minute moderate activities 'mwhrs': 'mwhrs', # weekly hours moderate activities - 'vday': 'vday', # 7 days moderate activites - 'vdhrs': 'vdhrs', # usual hours moderate activities - 'vdmin': 'vdmin', # usual minute moderate activities - 'vwhrs': 'vwhrs', # weekly hours moderate activities + 'mwmin': 'mwmin', # weekly minutes moderate activities + 'vday': 'vday', # 7 days vigorous activites + 'vdhrs': 'vdhrs', # usual hours vigorous activities + 'vdmin': 'vdmin', # usual minute vigorous activities + 'vwhrs': 'vwhrs', # weekly hours vigorous activities + 'vwmin': 'vwmin', # weekly minutes vigorous activities } # Some variables change names halfway through UKHLS. From 0ccb964d36353d1209ab9c166cf0b4df20259edf Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Wed, 12 Jul 2023 14:50:19 +0100 Subject: [PATCH 029/229] Added function to calculate whether someone meets the government criteria for being considered active --- .../generate_composite_vars.py | 70 ++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/minos/data_generation/generate_composite_vars.py b/minos/data_generation/generate_composite_vars.py index b1d5e27c..3d3abf0f 100755 --- a/minos/data_generation/generate_composite_vars.py +++ b/minos/data_generation/generate_composite_vars.py @@ -995,6 +995,73 @@ def calculate_auditc_score(data): return data +def generate_exercise_composite(data): + """ + + + Parameters + ---------- + data + + Returns + ------- + + """ + # First do moderate activity + # this is a combination of information from all moderate activity variables + data['temp_mod_act'] = 0 + # hours per day * 60 (for minutes) where neither is missing + data['temp_mod_act'][(data['mday'] >= 0) & (data['mdhrs'] >= 0)] += data['mday'] * (data['mdhrs'] * 60) + # minutes per day + data['temp_mod_act'][(data['mday'] >= 0) & (data['mdmin'] >= 0)] += data['mday'] * data['mdmin'] + # hours per week (asked separately I think for when hours per day is unknown) + data['temp_mod_act'][data['mwhrs'] >= 0] += data['mwhrs'] + # minutes per week (mins per day unknown) + data['temp_mod_act'][data['mwmin'] >= 0] += data['mwmin'] + # handle missing + data['temp_mod_act'][(data['mday'] < 0) & + (data['mdhrs'] < 0) & + (data['mdmin'] < 0) & + (data['mwhrs'] < 0) & + (data['mwmin'] < 0)] = -9 + + # now vigorous + data['temp_vig_act'] = 0 + # hours per day * 60 (for minutes) where neither is missing + data['temp_vig_act'][(data['vday'] >= 0) & (data['vdhrs'] >= 0)] += data['vday'] * (data['vdhrs'] * 60) + # minutes per day + data['temp_vig_act'][(data['vday'] >= 0) & (data['vdmin'] >= 0)] += data['vday'] * data['vdmin'] + # hours per week (asked separately I think for when hours per day is unknown) + data['temp_vig_act'][data['vwhrs'] >= 0] += data['vwhrs'] + # minutes per week (mins per day unknown) + data['temp_vig_act'][data['vwmin'] >= 0] += data['vwmin'] + # handle missing + data['temp_vig_act'][(data['vday'] < 0) & + (data['vdhrs'] < 0) & + (data['vdmin'] < 0) & + (data['vwhrs'] < 0) & + (data['vwmin'] < 0)] = -9 + + # Now do the calculations for a binary active variable + data['active'] = -9 + # if either more than 150 mins moderate, or 75 mins vigorous, then active == TRUE + data['active'][(data['temp_mod_act'] >= 150) | (data['temp_vig_act'] >= 75)] = 1 + #data['active'][data['temp_vig_act'] >= 75] = 1 + # if both less than 150 mins moderate and less than 75 mins vigorous, then active == FALSE + data['active'][(data['temp_mod_act'] < 150) & (data['temp_vig_act'] < 75)] = 0 + # if both missing, then missing + data['active'][(data['temp_mod_act'] == -9) & (data['temp_vig_act'] == -9)] = -9 + + # drop cols no longer need + data.drop(labels=['temp_mod_act', 'temp_vig_act', + 'mday', 'mdhrs', 'mdmin', 'mwhrs', 'mwmin', + 'vday', 'vdhrs', 'vdmin', 'vwhrs', 'vwmin'], + axis=1, + inplace=True) + + return data + + def main(): maxyr = US_utils.get_data_maxyr() # first collect and load the datafiles for every year @@ -1018,7 +1085,8 @@ def main(): data = generate_marital_status(data) # marital status data = generate_physical_health_score(data) # physical health score data = calculate_equivalent_income(data) # equivalent income - data = calculate_auditc_score(data) + data = calculate_auditc_score(data) # alcohol use disorder for consumption (auditc) + data = generate_exercise_composite(data) # exercise composite print('Finished composite generation. Saving data...') US_utils.save_multiple_files(data, years, "data/composite_US/", "") From 815f1260eb7ab3db403b66ec6b04333c9f90af09 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Wed, 12 Jul 2023 14:51:11 +0100 Subject: [PATCH 030/229] Added model for active in model_definitions_default.txt, and modified estimate_transitions to be able to run this model in the correct years. Also added active to the SF_12_ models --- minos/transitions/estimate_transitions.R | 5 +++++ minos/transitions/model_definitions_default.txt | 7 ++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/minos/transitions/estimate_transitions.R b/minos/transitions/estimate_transitions.R index 8131d62a..cb38886d 100644 --- a/minos/transitions/estimate_transitions.R +++ b/minos/transitions/estimate_transitions.R @@ -329,6 +329,8 @@ run_yearly_models <- function(transitionDir_path, if(dependent == 'ncigs' & year < 2013) { next } # alcohol model (auditc) only in specific years if(dependent == 'auditc' & !year %in% c(2014, 2016, 2018, 2019)) { next } + # active only in specific years + if(dependent == 'active' & !year %in% c(2014, 2016, 2018, 2019)) { next } #TODO: Maybe copy values from wave 2 onto wave 1? Assuming physical health changes slowly? # SF_12 predictor (physical health score) not available in wave 1 if(dependent %in% c('SF_12_MCS', 'SF_12_PCS') & year == 2009) { next } @@ -382,6 +384,9 @@ run_yearly_models <- function(transitionDir_path, if(!year %in% c(2015, 2017, 2019, 2020)) { formula.string <- str_remove(formula.string, " \\+ factor\\(auditc\\)") } + if(!year %in% c(2015, 2017, 2019, 2020)) { + formula.string <- str_remove(formula.string, " \\+ factor\\(active\\)") + } } #print(formula.string) # Now make string into formula diff --git a/minos/transitions/model_definitions_default.txt b/minos/transitions/model_definitions_default.txt index ad4649bd..b71bfe20 100644 --- a/minos/transitions/model_definitions_default.txt +++ b/minos/transitions/model_definitions_default.txt @@ -1,3 +1,4 @@ +LOGIT : active ~ age + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + hh_income + relevel(factor(ethnicity), ref = 'WBI') NNET : auditc ~ age + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + hh_income + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + factor(financial_situation) CLM : financial_situation ~ factor(financial_situation) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(job_sec)+ scale(hh_income)+ I(scale(hh_income)**2) + factor(marital_status) + hhsize + factor(housing_tenure) LOGIT : heating ~ factor(heating) + scale(SF_12_MCS) + scale(SF_12_PCS) + relevel(factor(ethnicity), ref='WBI') + hh_income + factor(marital_status)+ ncigs + hhsize + factor(urban) + factor(housing_tenure) + factor(financial_situation) @@ -11,6 +12,6 @@ CLM : loneliness ~ age + factor(sex) + scale(SF_12_MCS) + relevel(factor(educati CLM : neighbourhood_safety ~ age + factor(sex) + factor(job_sec) + relevel(factor(ethnicity), ref = 'WBI') + hh_income + factor(housing_quality) + relevel(factor(region), ref = 'South East') + factor(urban) NNET : education_state ~ factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') ZIP : ncigs ~ age + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + relevel(factor(job_sec), ref = '3') + hh_income + relevel(factor(ethnicity), ref = 'WBI') | relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(job_sec), ref = '3') + hh_income + scale(SF_12_MCS) + scale(SF_12_PCS) -OLS : SF_12_PCS ~ scale(SF_12_PCS) + age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + hh_income + factor(housing_quality) + ncigs + nutrition_quality + factor(loneliness) + factor(financial_situation) + factor(auditc) -OLS : SF_12_MCS ~ scale(SF_12_MCS) + age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + hh_income + factor(housing_quality) + phealth + factor(neighbourhood_safety) + ncigs + nutrition_quality + factor(loneliness) + factor(financial_situation) -OLS_DIFF : SF_12_MCS ~ SF_12_MCS_diff + age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + hh_income + factor(housing_quality) + phealth + factor(neighbourhood_safety) + ncigs + nutrition_quality + factor(loneliness) + factor(financial_situation) +OLS : SF_12_PCS ~ scale(SF_12_PCS) + age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + hh_income + factor(housing_quality) + ncigs + nutrition_quality + factor(loneliness) + factor(financial_situation) + factor(auditc) + factor(active) +OLS : SF_12_MCS ~ scale(SF_12_MCS) + age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + hh_income + factor(housing_quality) + phealth + factor(neighbourhood_safety) + ncigs + nutrition_quality + factor(loneliness) + factor(financial_situation) + factor(active) +OLS_DIFF : SF_12_MCS ~ SF_12_MCS_diff + age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + hh_income + factor(housing_quality) + phealth + factor(neighbourhood_safety) + ncigs + nutrition_quality + factor(loneliness) + factor(financial_situation) + factor(active) From eeb52dd427c54d0c1feceb1fb14f71ecd1c78c35 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Wed, 12 Jul 2023 14:51:40 +0100 Subject: [PATCH 031/229] Added the physical activity module --- minos/modules/alcohol.py | 1 - minos/modules/physical_activity.py | 112 +++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 minos/modules/physical_activity.py diff --git a/minos/modules/alcohol.py b/minos/modules/alcohol.py index 5eb77b54..12d80c5c 100755 --- a/minos/modules/alcohol.py +++ b/minos/modules/alcohol.py @@ -98,7 +98,6 @@ def on_time_step(self, event): # Update population with new alcohol self.population_view.update(alcohol_prob_df["auditc"]) - def calculate_alcohol(self, pop): """Calculate alcohol transition distribution based on provided people/indices diff --git a/minos/modules/physical_activity.py b/minos/modules/physical_activity.py new file mode 100644 index 00000000..cc84e391 --- /dev/null +++ b/minos/modules/physical_activity.py @@ -0,0 +1,112 @@ +"""Module for estimating change in active variable for subjective ability to heat ones home""" + + +import pandas as pd +from pathlib import Path +from minos.modules import r_utils +from minos.modules.base_module import Base +import logging +from datetime import datetime as dt + + +class PhysicalActivity(Base): + @property + def name(self): + return "physical_activity" + + def __repr__(self): + return "PhysicalActivity()" + + def setup(self, builder): + """ Initialise the module during simulation.setup(). + + Notes + ----- + - Load in data from pre_setup + - Register any value producers/modifiers for death rate + - Add required columns to population data frame + - Add listener event to check if people die on each time step. + - Update other required items such as randomness stream. + + Parameter + ---------- + builder : vivarium.engine.Builder + Vivarium's control object. Stores all simulation metadata and allows modules to use it. + + """ + + # Load in inputs from pre-setup. + self.rpy2_modules = builder.data.load("rpy2_modules") + + # Build vivarium objects for calculating transition probabilities. + # Typically this is registering rate/lookup tables. See vivarium docs/other modules for examples. + + # Assign randomness streams if necessary. Only useful if seeding counterfactuals. + self.random = builder.randomness.get_stream(self.generate_random_crn_key()) + + + # Determine which subset of the main population is used in this module. + # columns_created is the columns created by this module. + # view_columns is the columns from the main population used in this module. essentially what is needed for + # transition models and any outputs. + view_columns = ["sex", + "SF_12_MCS", + 'SF_12_PCS', + "ethnicity", + "age", + "education_state", + "hh_income", + "active"] + self.population_view = builder.population.get_view(columns=view_columns) + + # Population initialiser. When new individuals are added to the microsimulation a constructer is called for each + # module. Declare what constructer is used. usually on_initialize_simulants method is called. Inidividuals are + # created at the start of a model "setup" or after some deterministic (add cohorts) or random (births) event. + builder.population.initializes_simulants(self.on_initialize_simulants) + + # Declare events in the module. At what times do individuals transition states from this module. E.g. when does + # individual graduate in an education module. + builder.event.register_listener("time_step", self.on_time_step, priority=5) + + + def on_time_step(self, event): + """Produces new children and updates parent status on time steps. + + Parameters + ---------- + event : vivarium.population.PopulationEvent + The event time_step that called this function. + """ + # Construct transition probability distributions. + # Draw individuals next states randomly from this distribution. + # Adjust other variables according to changes in state. E.g. a birth would increase child counter by one. + + pop = self.population_view.get(event.index, query="alive=='alive'") + self.year = event.time.year + + active_prob_df = self.calculate_active(pop) + active_prob_df[0.] = 1 - active_prob_df[1.0] + active_prob_df.index = pop.index + active_prob_df["active"] = self.random.choice(active_prob_df.index, + list(active_prob_df.columns), + active_prob_df) + active_prob_df.index = pop.index + self.population_view.update(active_prob_df["active"]) + + def calculate_active(self, pop): + """Calculate active transition distribution based on provided people/indices. + + Parameters + ---------- + pop : pd.DataFrame + The population dataframe. + Returns + ------- + """ + # load transition model based on year. + year = 2019 + transition_model = r_utils.load_transitions(f"active/logit/active_{year}_{year+1}", self.rpy2_modules) + # returns probability matrix (3xn) of next ordinal state. + prob_df = r_utils.predict_next_timestep_logit(transition_model, self.rpy2_modules, pop, 'active') + prob_df.columns = [1.] + return prob_df From d5d5f159ce30517846128437842db64f9054e2fb Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Wed, 12 Jul 2023 14:57:07 +0100 Subject: [PATCH 032/229] Added physical activity module into all the relevant config files --- config/cross_validation/cross_validation_default1.yaml | 2 +- config/cross_validation/cross_validation_default2.yaml | 2 +- config/cross_validation/cross_validation_default3.yaml | 2 +- config/cross_validation/cross_validation_default4.yaml | 2 +- config/cross_validation/cross_validation_default5.yaml | 2 +- config/default.yaml | 2 +- config/scot_default.yaml | 2 +- minos/modules/physical_activity.py | 4 ++-- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/config/cross_validation/cross_validation_default1.yaml b/config/cross_validation/cross_validation_default1.yaml index 8ca9d7ab..ed1fafcb 100644 --- a/config/cross_validation/cross_validation_default1.yaml +++ b/config/cross_validation/cross_validation_default1.yaml @@ -30,7 +30,7 @@ cross_validation: TRUE # Similarly Mortality requires Replenishment but everything else requires Mortality so it goes second. (priority 1) # Finally everything else can go in any order (priority 2). #components : [MWB(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Income(), Housing(), Labour(), Nutrition(), Education(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] -components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Heating(), Housing(), HousingTenure(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] +components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Alcohol(), PhysicalActivity(), Neighbourhood(), Heating(), Housing(), HousingTenure(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] scale_rates: diff --git a/config/cross_validation/cross_validation_default2.yaml b/config/cross_validation/cross_validation_default2.yaml index e5872ba0..ea281a5a 100644 --- a/config/cross_validation/cross_validation_default2.yaml +++ b/config/cross_validation/cross_validation_default2.yaml @@ -30,7 +30,7 @@ cross_validation: TRUE # Similarly Mortality requires Replenishment but everything else requires Mortality so it goes second. (priority 1) # Finally everything else can go in any order (priority 2). #components : [MWB(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Income(), Housing(), Labour(), Nutrition(), Education(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] -components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Heating(), Housing(), HousingTenure(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] +components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Alcohol(), PhysicalActivity(), Neighbourhood(), Heating(), Housing(), HousingTenure(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] scale_rates: diff --git a/config/cross_validation/cross_validation_default3.yaml b/config/cross_validation/cross_validation_default3.yaml index 31e0843f..8e1c11b5 100644 --- a/config/cross_validation/cross_validation_default3.yaml +++ b/config/cross_validation/cross_validation_default3.yaml @@ -30,7 +30,7 @@ cross_validation: TRUE # Similarly Mortality requires Replenishment but everything else requires Mortality so it goes second. (priority 1) # Finally everything else can go in any order (priority 2). #components : [MWB(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Income(), Housing(), Labour(), Nutrition(), Education(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] -components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Heating(), Housing(), HousingTenure(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] +components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Alcohol(), PhysicalActivity(), Neighbourhood(), Heating(), Housing(), HousingTenure(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] scale_rates: diff --git a/config/cross_validation/cross_validation_default4.yaml b/config/cross_validation/cross_validation_default4.yaml index 746d00b6..c4e156a3 100644 --- a/config/cross_validation/cross_validation_default4.yaml +++ b/config/cross_validation/cross_validation_default4.yaml @@ -30,7 +30,7 @@ cross_validation: TRUE # Similarly Mortality requires Replenishment but everything else requires Mortality so it goes second. (priority 1) # Finally everything else can go in any order (priority 2). #components : [MWB(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Income(), Housing(), Labour(), Nutrition(), Education(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] -components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Heating(), Housing(), HousingTenure(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] +components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Alcohol(), PhysicalActivity(), Neighbourhood(), Heating(), Housing(), HousingTenure(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] scale_rates: diff --git a/config/cross_validation/cross_validation_default5.yaml b/config/cross_validation/cross_validation_default5.yaml index d81208b8..0fa7299c 100644 --- a/config/cross_validation/cross_validation_default5.yaml +++ b/config/cross_validation/cross_validation_default5.yaml @@ -30,7 +30,7 @@ cross_validation: TRUE # Similarly Mortality requires Replenishment but everything else requires Mortality so it goes second. (priority 1) # Finally everything else can go in any order (priority 2). #components : [MWB(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Income(), Housing(), Labour(), Nutrition(), Education(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] -components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Heating(), Housing(), HousingTenure(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] +components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Alcohol(), PhysicalActivity(), Neighbourhood(), Heating(), Housing(), HousingTenure(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] scale_rates: diff --git a/config/default.yaml b/config/default.yaml index 22853a1c..fb4b689c 100755 --- a/config/default.yaml +++ b/config/default.yaml @@ -27,7 +27,7 @@ cross_validation: FALSE # Similarly Mortality requires Replenishment but everything else requires Mortality so it goes second. (priority 1) # Finally everything else can go in any order (priority 2). #components : [MWB(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Income(), Housing(), Labour(), Nutrition(), Education(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] -components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Heating(), Housing(), HousingTenure(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] +components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Alcohol(), PhysicalActivity(), Neighbourhood(), Heating(), Housing(), HousingTenure(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] scale_rates: diff --git a/config/scot_default.yaml b/config/scot_default.yaml index b6d38127..d24c88fd 100755 --- a/config/scot_default.yaml +++ b/config/scot_default.yaml @@ -27,7 +27,7 @@ cross_validation: FALSE # Similarly Mortality requires Replenishment but everything else requires Mortality so it goes second. (priority 1) # Finally everything else can go in any order (priority 2). #components : [MWB(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Income(), Housing(), Labour(), Nutrition(), Education(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] -components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Neighbourhood(), Heating(), Housing(), HousingTenure(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] +components : [MWB(), SF_12_PCS(), Loneliness(), Tobacco(), Alcohol(), PhysicalActivity(), Neighbourhood(), Heating(), Housing(), HousingTenure(), Nutrition(), financialSituation(), Income(), Education(), Mortality(), Replenishment()] scale_rates: diff --git a/minos/modules/physical_activity.py b/minos/modules/physical_activity.py index cc84e391..20ea883c 100644 --- a/minos/modules/physical_activity.py +++ b/minos/modules/physical_activity.py @@ -88,8 +88,8 @@ def on_time_step(self, event): active_prob_df[0.] = 1 - active_prob_df[1.0] active_prob_df.index = pop.index active_prob_df["active"] = self.random.choice(active_prob_df.index, - list(active_prob_df.columns), - active_prob_df) + list(active_prob_df.columns), + active_prob_df) active_prob_df.index = pop.index self.population_view.update(active_prob_df["active"]) From 7458eb4b208c62ba871c9882519f96d595a3b3d2 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Wed, 12 Jul 2023 15:44:39 +0100 Subject: [PATCH 033/229] Added physical activity module to RunPipeline, and added active variable to replenishment list. Also added active var to all the modules it needs to be in as a predictor --- minos/minosPipeline/RunPipeline.py | 4 +++- minos/modules/mental_wellbeing.py | 3 ++- minos/modules/physical_activity.py | 4 ++-- minos/modules/physical_wellbeing.py | 3 ++- minos/modules/replenishment.py | 3 ++- minos/modules/tobacco.py | 2 +- 6 files changed, 12 insertions(+), 7 deletions(-) diff --git a/minos/minosPipeline/RunPipeline.py b/minos/minosPipeline/RunPipeline.py index b5b0d699..f61dc050 100755 --- a/minos/minosPipeline/RunPipeline.py +++ b/minos/minosPipeline/RunPipeline.py @@ -39,6 +39,7 @@ from minos.modules.heating import Heating from minos.modules.financial_situation import financialSituation from minos.modules.housing_tenure import HousingTenure +from minos.modules.physical_activity import PhysicalActivity from minos.modules.intervention import hhIncomeIntervention from minos.modules.intervention import hhIncomeChildUplift @@ -89,7 +90,8 @@ def validate_components(config_components, intervention): "FertilityAgeSpecificRates()": FertilityAgeSpecificRates(), "Mortality()": Mortality(), "Education()": Education(), - "HousingTenure()": HousingTenure() + "HousingTenure()": HousingTenure(), + "PhysicalActivity()": PhysicalActivity() } SIPHER7_components_map = { # SIPHER7 stuff diff --git a/minos/modules/mental_wellbeing.py b/minos/modules/mental_wellbeing.py index f8a5252f..0e04304e 100755 --- a/minos/modules/mental_wellbeing.py +++ b/minos/modules/mental_wellbeing.py @@ -70,7 +70,8 @@ def setup(self, builder): 'neighbourhood_safety', 'loneliness', 'SF_12_diff', - 'financial_situation'] + 'financial_situation', + 'active'] self.population_view = builder.population.get_view(columns=view_columns) diff --git a/minos/modules/physical_activity.py b/minos/modules/physical_activity.py index 20ea883c..c0af2767 100644 --- a/minos/modules/physical_activity.py +++ b/minos/modules/physical_activity.py @@ -85,7 +85,7 @@ def on_time_step(self, event): self.year = event.time.year active_prob_df = self.calculate_active(pop) - active_prob_df[0.] = 1 - active_prob_df[1.0] + active_prob_df[0] = 1 - active_prob_df[1] active_prob_df.index = pop.index active_prob_df["active"] = self.random.choice(active_prob_df.index, list(active_prob_df.columns), @@ -108,5 +108,5 @@ def calculate_active(self, pop): transition_model = r_utils.load_transitions(f"active/logit/active_{year}_{year+1}", self.rpy2_modules) # returns probability matrix (3xn) of next ordinal state. prob_df = r_utils.predict_next_timestep_logit(transition_model, self.rpy2_modules, pop, 'active') - prob_df.columns = [1.] + prob_df.columns = [1] return prob_df diff --git a/minos/modules/physical_wellbeing.py b/minos/modules/physical_wellbeing.py index 93317367..58bc0b5b 100644 --- a/minos/modules/physical_wellbeing.py +++ b/minos/modules/physical_wellbeing.py @@ -69,7 +69,8 @@ def setup(self, builder): 'neighbourhood_safety', 'loneliness', 'financial_situation', - 'auditc'] + 'auditc', + 'active'] self.population_view = builder.population.get_view(columns=view_columns) diff --git a/minos/modules/replenishment.py b/minos/modules/replenishment.py index 3228440b..56b6b076 100755 --- a/minos/modules/replenishment.py +++ b/minos/modules/replenishment.py @@ -94,7 +94,8 @@ def setup(self, builder): 'financial_situation', 'housing_tenure', 'urban', - 'auditc'] + 'auditc', + 'active'] # Shorthand methods for readability. self.population_view = builder.population.get_view(view_columns) # view simulants diff --git a/minos/modules/tobacco.py b/minos/modules/tobacco.py index 293bbddd..087c1342 100755 --- a/minos/modules/tobacco.py +++ b/minos/modules/tobacco.py @@ -134,4 +134,4 @@ def plot(self, pop, config): f = plt.figure() histplot(pop, x="ncigs", stat='density') plt.savefig(file_name) - plt.close() \ No newline at end of file + plt.close() From ccafb236be87341a17701546d994bda4a31456f3 Mon Sep 17 00:00:00 2001 From: ld-archer Date: Thu, 13 Jul 2023 10:40:19 +0100 Subject: [PATCH 034/229] Added docstring for generate_physical_activity_binary in generate_composite_variables.py --- .../generate_composite_vars.py | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/minos/data_generation/generate_composite_vars.py b/minos/data_generation/generate_composite_vars.py index 3d3abf0f..b67ca3c9 100755 --- a/minos/data_generation/generate_composite_vars.py +++ b/minos/data_generation/generate_composite_vars.py @@ -928,7 +928,7 @@ def calculate_auditc_score(data): Parameters ---------- - data: pd.DataFrame + data : pd.DataFrame US data Returns ------- @@ -995,15 +995,31 @@ def calculate_auditc_score(data): return data -def generate_exercise_composite(data): +def generate_physical_activity_binary(data): """ + To determine what is a healthy level of physical activity, we are following government guidelines found here: + https://www.gov.uk/government/publications/physical-activity-guidelines-adults-and-older-adults + + This document states that for adults and older adults (over the age of 19), the government recommends at least 150 + minutes of moderate activty, or 75 minutes of vigorous activity in a week. Understanding Society has some good + variables on moderate and vigorous activity, with the caveat that they have only been asked on waves 7,9,11, and 12. + For both moderate and vigorous activity, US includes a number of variables: + - Number of days in past week did activity + - Average hours and minutes per day of activity + - Average hours and minutes per week of activity (if answered don't know to how much per day) + + With this, we can generate a binary variable for whether or not an individual is hitting the government exercise + targets. Parameters ---------- - data - + data : pd.DataFrame + US data Returns + ------- + data : pd.DataFrame + US data with binary 'active' variable ------- """ @@ -1086,7 +1102,7 @@ def main(): data = generate_physical_health_score(data) # physical health score data = calculate_equivalent_income(data) # equivalent income data = calculate_auditc_score(data) # alcohol use disorder for consumption (auditc) - data = generate_exercise_composite(data) # exercise composite + data = generate_physical_activity_binary(data) # physical activity composite print('Finished composite generation. Saving data...') US_utils.save_multiple_files(data, years, "data/composite_US/", "") From 4a1019b2cf4714507c275af1c60d84b28e8bd2c6 Mon Sep 17 00:00:00 2001 From: ld-archer Date: Thu, 13 Jul 2023 15:11:44 +0100 Subject: [PATCH 035/229] Added code to force cross-validation runs to the final year model for physical_activity and heating (force to 2018 for phy act as this is better than 2019 model) --- minos/modules/heating.py | 6 +++++- minos/modules/physical_activity.py | 10 +++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/minos/modules/heating.py b/minos/modules/heating.py index 82fb89ee..88a04e12 100644 --- a/minos/modules/heating.py +++ b/minos/modules/heating.py @@ -111,7 +111,11 @@ def calculate_heating(self, pop): ------- """ # load transition model based on year. - year = 2019 + if self.cross_validation: + # if cross-val, fix year to final year model + year = 2019 + else: + year = min(self.year, 2019) transition_model = r_utils.load_transitions(f"heating/logit/heating_{year}_{year+1}", self.rpy2_modules) # returns probability matrix (3xn) of next ordinal state. prob_df = r_utils.predict_next_timestep_logit(transition_model, self.rpy2_modules, pop, 'heating') diff --git a/minos/modules/physical_activity.py b/minos/modules/physical_activity.py index c0af2767..8fa150bd 100644 --- a/minos/modules/physical_activity.py +++ b/minos/modules/physical_activity.py @@ -85,7 +85,7 @@ def on_time_step(self, event): self.year = event.time.year active_prob_df = self.calculate_active(pop) - active_prob_df[0] = 1 - active_prob_df[1] + active_prob_df[0.] = 1. - active_prob_df[1] active_prob_df.index = pop.index active_prob_df["active"] = self.random.choice(active_prob_df.index, list(active_prob_df.columns), @@ -104,9 +104,13 @@ def calculate_active(self, pop): ------- """ # load transition model based on year. - year = 2019 + if self.cross_validation: + # if cross-val, fix year to 2018. 2019 model has vastly reduced sample for some reason + year = 2018 + else: + year = min(self.year, 2018) transition_model = r_utils.load_transitions(f"active/logit/active_{year}_{year+1}", self.rpy2_modules) # returns probability matrix (3xn) of next ordinal state. prob_df = r_utils.predict_next_timestep_logit(transition_model, self.rpy2_modules, pop, 'active') - prob_df.columns = [1] + prob_df.columns = [1.] return prob_df From 480e2fd20f3222a7d1754fd5f74880cb4e9d139d Mon Sep 17 00:00:00 2001 From: ld-archer Date: Thu, 13 Jul 2023 15:12:24 +0100 Subject: [PATCH 036/229] Run physical activity through complete case --- minos/data_generation/US_complete_case.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/minos/data_generation/US_complete_case.py b/minos/data_generation/US_complete_case.py index cfbd00fc..da8f9ba9 100755 --- a/minos/data_generation/US_complete_case.py +++ b/minos/data_generation/US_complete_case.py @@ -92,6 +92,8 @@ def complete_case_custom_years(data, var, years): # PCS Vars # AUDITC (alcohol) - present in 2015, 2017, 2019, 2020 data = complete_case_custom_years(data, 'auditc', years=[2015, 2017, 2019, 2020]) + # active (physical activity) - present in 2015, 2017, 2019, 2020 + data = complete_case_custom_years(data, 'active', years=[2015, 2017, 2019, 2020]) drop_columns = [#'financial_situation', # these are just SF12 MICE columns for now. see US_format_raw.py 'ghq_depression', From b31a78c5bf78433be54b9cfc2de76d7df4c6e436 Mon Sep 17 00:00:00 2001 From: ld-archer Date: Thu, 13 Jul 2023 15:12:48 +0100 Subject: [PATCH 037/229] Added active var to handovers and cross-validation --- minos/validation/cross_validation_default.Rmd | 15 +++++++++++++++ minos/validation/handovers.Rmd | 6 ++++++ 2 files changed, 21 insertions(+) diff --git a/minos/validation/cross_validation_default.Rmd b/minos/validation/cross_validation_default.Rmd index 45a3fee4..2f8521f8 100644 --- a/minos/validation/cross_validation_default.Rmd +++ b/minos/validation/cross_validation_default.Rmd @@ -320,6 +320,21 @@ cv_ordinal_plots(pivoted.df = alc.pivoted, rm(alc.pivoted) ``` +# Physical Activity + +```{r} +active.pivoted <- combine_and_pivot_long(df1 = cv, + df1.name = 'simulated', + df2 = raw, + df2.name = 'raw', + var = 'active') + +cv_ordinal_plots(pivoted.df = active.pivoted, + var = 'active', + save = FALSE) +rm(active.pivoted) +``` + # SECONDARY VARS ## Marital Status diff --git a/minos/validation/handovers.Rmd b/minos/validation/handovers.Rmd index 3654f273..18130083 100644 --- a/minos/validation/handovers.Rmd +++ b/minos/validation/handovers.Rmd @@ -303,3 +303,9 @@ rm(raw.s, base.s, spag) handover_ordinal(raw.dat, base.dat, var = 'auditc', save = shall.we.save) ``` +## Physical Activity + +```{r} +handover_ordinal(raw.dat, base.dat, var = 'active', save = shall.we.save) +``` + From 416440dbccd904b42f9c73e1b5b99c9bd476ae1e Mon Sep 17 00:00:00 2001 From: ld-archer Date: Thu, 13 Jul 2023 15:13:21 +0100 Subject: [PATCH 038/229] Started a notebook for physical activity documentation --- docsrc/Makefile | 1 + .../notebooks/physical_activity.Rmd | 45 +++++++++++++++++++ docsrc/documentation/notebooks/tobacco.Rmd | 28 ++++++------ 3 files changed, 60 insertions(+), 14 deletions(-) create mode 100644 docsrc/documentation/notebooks/physical_activity.Rmd diff --git a/docsrc/Makefile b/docsrc/Makefile index c8efe694..33069af7 100755 --- a/docsrc/Makefile +++ b/docsrc/Makefile @@ -35,6 +35,7 @@ render_rmd: (cd ..; bash docsrc/compile_rmd_rst.sh "$(NOTEBOOKDIR)loneliness.Rmd" "$(NOTEBOOKOUTPUTDIR)loneliness.rst") (cd ..; bash docsrc/compile_rmd_rst.sh "$(NOTEBOOKDIR)neighbourhood.Rmd" "$(NOTEBOOKOUTPUTDIR)neighbourhood.rst") (cd ..; bash docsrc/compile_rmd_rst.sh "$(NOTEBOOKDIR)tobacco.Rmd" "$(NOTEBOOKOUTPUTDIR)tobacco.rst") + (cd ..; bash docsrc/compile_rmd_rst.sh "$(NOTEBOOKDIR)physical_activity.Rmd" "$(NOTEBOOKOUTPUTDIR)physical_activity.rst") # Knitr outputs figures for the documentation in a folder /figure at the top repo level. # I couldn't figure out how change where to output them so manually move them instead.. diff --git a/docsrc/documentation/notebooks/physical_activity.Rmd b/docsrc/documentation/notebooks/physical_activity.Rmd new file mode 100644 index 00000000..18dc43ac --- /dev/null +++ b/docsrc/documentation/notebooks/physical_activity.Rmd @@ -0,0 +1,45 @@ +--- +title: "Physical Activity" +output: html_document +bibliography: + - "docsrc/refs.bib" # switch bibliography position. depending on where the file is being run. stick to this one when compiiling the website using the makefile + #- "../../refs.bib" # use this one if knitting directly in Rstudio. Easier for debugging. +--- + +```{r setup, include=FALSE} +knitr::opts_knit$set(root.dir= "../minos") +``` + +## Physical Activity + +Whether a person is physically active or not has strong links to both mental and physical wellbeing, as well as other variables included in the MINOS pathways. + +```{r load_data, include=FALSE} +source('docsrc/documentation/notebooks/sphinx_notebook_utils.R') +``` + +### Methods + +What methods are used? Justification due to output data type. explanation of model output. + +```{r housing_barchart, echo=FALSE} +discrete_barplot("data/transitions/active/logit/active_2018_2019.rds", 'next_active') +``` + +### Data + + What variables are included? Why is this output chosen. What explanatory variables are used and why are they chosen + +### Results + + What are the results. Coefficients tables. diagnostic plots. measures of goodness of fit. + + +```{r active_output, echo=FALSE} +options(width=120) +clm_barplot("data/transitions/active/logit/active_2018_2019.rds", "next_active") +clm_output("data/transitions/active/logit/active_2018_2019.rds") +``` + + +### References diff --git a/docsrc/documentation/notebooks/tobacco.Rmd b/docsrc/documentation/notebooks/tobacco.Rmd index 49b3ab4b..0a124af6 100644 --- a/docsrc/documentation/notebooks/tobacco.Rmd +++ b/docsrc/documentation/notebooks/tobacco.Rmd @@ -37,26 +37,26 @@ Two set of variables are needed for the logistic and poisson parts of the ZIP mo Variables that predict how much a person smokes. -age. persons age. generally older people and very young smoke. -SF_12. wellbeing estimates number of cigarettes smoked. -labour_state. whether a person is employed or not. -ethnicity. certain ethnicities more likely to smoke cigarettes. -education_state. highest qualification. -job_sec job quality -hh_income household income -ncigs previous number consumed. +- age. persons age. generally older people and very young smoke. +- SF_12. wellbeing estimates number of cigarettes smoked. +- labour_state. whether a person is employed or not. +- ethnicity. certain ethnicities more likely to smoke cigarettes. +- education_state. highest qualification. +- job_sec job quality +- hh_income household income +- ncigs previous number consumed. Variables that predict whether a person smokes -ethnicity. certain ethnicities more likely to smoke cigarettes. -labour_state. whether a person is employed or not. -age -SF_12. wellbeing estimates number of cigarettes smoked. -ncigs previous number consumed. +- ethnicity. certain ethnicities more likely to smoke cigarettes. +- labour_state. whether a person is employed or not. +- age +- SF_12. wellbeing estimates number of cigarettes smoked. +- ncigs previous number consumed. ### Results -Almost all coefficients significant. Particularly prevous consumption of cigarettes. Good estimation of the number of non-smokers in the population at around 55\%. Counts of smoking are underdispersed and fail to estimate consumption over 20 cigarettes. +Almost all coefficients significant. Particularly previous consumption of cigarettes. Good estimation of the number of non-smokers in the population at around 55\%. Counts of smoking are underdispersed and fail to estimate consumption over 20 cigarettes. ```{r tobacco_output, echo=FALSE} From 5861f804ae6084b26c50d45c6ad41308c1ca217a Mon Sep 17 00:00:00 2001 From: ld-archer Date: Mon, 2 Oct 2023 12:15:54 +0100 Subject: [PATCH 039/229] Material Deprivation proxy variable generated --- minos/data_generation/US_format_raw.py | 9 +++ .../generate_composite_vars.py | 64 +++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/minos/data_generation/US_format_raw.py b/minos/data_generation/US_format_raw.py index 7c5cee2c..27f29ce9 100755 --- a/minos/data_generation/US_format_raw.py +++ b/minos/data_generation/US_format_raw.py @@ -314,6 +314,15 @@ def format_ukhls_columns(year): 'vdmin': 'vdmin', # usual minute vigorous activities 'vwhrs': 'vwhrs', # weekly hours vigorous activities 'vwmin': 'vwmin', # weekly minutes vigorous activities + # Material Deprivation vars + 'matdepa': 'matdepa', # Material Deprivation: Holiday + 'matdepd': 'matdepd', # Material Deprivation: House + 'matdepe': 'matdepe', # Material Deprivation: Contents Insurance + 'matdepf': 'matdepf', # Material Deprivation: Regular Savings + 'matdepg': 'matdepg', # Material Deprivation: Replace worn out furniture + 'matdeph': 'matdeph', # Material Deprivation: Replace or repair major electrical goods + 'matdepi': 'matdepi', # Material Deprivation: Money for self + 'matdepj': 'matdepj', # Material Deprivation: Keep up with bills } # Some variables change names halfway through UKHLS. diff --git a/minos/data_generation/generate_composite_vars.py b/minos/data_generation/generate_composite_vars.py index b67ca3c9..2bb13911 100755 --- a/minos/data_generation/generate_composite_vars.py +++ b/minos/data_generation/generate_composite_vars.py @@ -1078,6 +1078,69 @@ def generate_physical_activity_binary(data): return data +def generate_matdep_proxy(data): + """ + This function will create a single proxy from the information in the material deprivation variables (all named + matdep{letter}). Each variable is a 4 level ordinal with the following levels: + + 1 - I/We have this + 2 - Can't afford it + 3 - Don't need it now + 4 - Does not apply + + A score of 1 indicates a positive response. + A score of 2 indicates a negative response. + A score of 3 or 4 will be treated as a missing value. + + Each score of 1 will increment a material deprivation score variable by 1, and the final sum over all the variables + will be divided by the number of non-missing items to get a score from 0-1. + + Parameters + ---------- + data : pd.DataFrame + US data with the material deprivation component variables. + Returns + ------- + data : pd.DataFrame + US data with the combined matdep proxy created, and component variables removed. + """ + # Create matdepscore variable and populate from the matdep variables + # Matdepscore + 1 if individual indicates they have the specific variable + # Then no increment if individual indicates they cannot afford it + # Finally scores of 3 (Don't need it now) or 4 (Does not apply) will be treated as a missing value + data['matdepscore'] = 0 + data['matdepscore'][data['matdepa'] == 1] += 1 + data['matdepscore'][data['matdepd'] == 1] += 1 + data['matdepscore'][data['matdepe'] == 1] += 1 + data['matdepscore'][data['matdepf'] == 1] += 1 + data['matdepscore'][data['matdepg'] == 1] += 1 + data['matdepscore'][data['matdeph'] == 1] += 1 + data['matdepscore'][data['matdepi'] == 1] += 1 + data['matdepscore'][data['matdepj'] == 1] += 1 + + # counter to increment when values are not missing (negative or 3, 4) + data['counter'] = 0 + data['counter'][data['matdepa'].isin([1, 2])] += 1 + data['counter'][data['matdepd'].isin([1, 2])] += 1 + data['counter'][data['matdepe'].isin([1, 2])] += 1 + data['counter'][data['matdepf'].isin([1, 2])] += 1 + data['counter'][data['matdepg'].isin([1, 2])] += 1 + data['counter'][data['matdeph'].isin([1, 2])] += 1 + data['counter'][data['matdepi'].isin([1, 2])] += 1 + data['counter'][data['matdepj'].isin([1, 2])] += 1 + + # Now use the counter value and matdepscore to generate a score between 0 and 1 + data['matdep'] = data['matdepscore'] / data['counter'] + + # drop cols no longer need + data.drop(labels=['matdepa', 'matdepd', 'matdepe', 'matdepf', 'matdepg', 'matdeph', 'matdepi', 'matdepj', + 'matdepscore', 'counter'], + axis=1, + inplace=True) + + return data + + def main(): maxyr = US_utils.get_data_maxyr() # first collect and load the datafiles for every year @@ -1103,6 +1166,7 @@ def main(): data = calculate_equivalent_income(data) # equivalent income data = calculate_auditc_score(data) # alcohol use disorder for consumption (auditc) data = generate_physical_activity_binary(data) # physical activity composite + data = generate_matdep_proxy(data) # Material Deprivation proxy print('Finished composite generation. Saving data...') US_utils.save_multiple_files(data, years, "data/composite_US/", "") From f071bfa9ee060ec40ca01467cf931e3ecf1fdf27 Mon Sep 17 00:00:00 2001 From: ld-archer Date: Thu, 5 Oct 2023 10:11:42 +0100 Subject: [PATCH 040/229] Got GLMM Beta models working for material deprivation --- config/default.yaml | 2 +- environment.yml | 1 + minos/data_generation/US_complete_case.py | 8 +- .../generate_composite_vars.py | 4 +- minos/data_generation/generate_repl_pop.py | 2 +- minos/minosPipeline/RunPipeline.py | 62 +----- minos/modules/housing.py | 1 + minos/modules/income.py | 5 +- minos/modules/material_deprivation.py | 139 ++++++++++++ minos/modules/mental_wellbeing.py | 7 +- minos/modules/nutrition.py | 6 +- minos/modules/physical_wellbeing.py | 131 +++++++++++ minos/modules/r_utils.py | 57 ++++- minos/modules/replenishment.py | 9 +- minos/transitions/Makefile | 29 ++- .../estimate_longitudinal_transitions.R | 26 ++- .../transitions/model_definitions_default.txt | 1 + .../transitions/transition_model_functions.R | 120 ++++++---- minos/utils_validation_vis.R | 210 +++++++++--------- 19 files changed, 584 insertions(+), 236 deletions(-) create mode 100644 minos/modules/material_deprivation.py diff --git a/config/default.yaml b/config/default.yaml index f8bf38c0..20cc24d3 100755 --- a/config/default.yaml +++ b/config/default.yaml @@ -23,7 +23,7 @@ replenishing_dir: 'data/replenishing' cross_validation: FALSE # Correct ordering of components is automated in RunPipeline, so can be passed in any order here -components : [lmmYJMWB(), SF_12_PCS(), Loneliness(), Tobacco(), Alcohol(), PhysicalActivity(), Neighbourhood(), Heating(), Housing(), HousingTenure(), lmmYJNutrition(), financialSituation(), lmmYJIncome(), Education(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] +components : [lmmYJMWB(), lmmYJPCS(), Loneliness(), Tobacco(), Alcohol(), PhysicalActivity(), Neighbourhood(), MaterialDeprivation(), Heating(), Housing(), HousingTenure(), lmmYJNutrition(), financialSituation(), lmmYJIncome(), Education(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] scale_rates: method: "constant" diff --git a/environment.yml b/environment.yml index a4ca9087..ef4ab2c8 100644 --- a/environment.yml +++ b/environment.yml @@ -28,6 +28,7 @@ dependencies: # Added pip and python at top to avoid conda giving grumpy warning - pandoc - sphinx - myst-parser + - r-glmmTMB - pip: # Same packages as were in "requirements.txt" except minos and vivarium, as both installed during "make install" - rpy2 - pyyaml>=5.4 diff --git a/minos/data_generation/US_complete_case.py b/minos/data_generation/US_complete_case.py index c897f1d9..3026fa48 100755 --- a/minos/data_generation/US_complete_case.py +++ b/minos/data_generation/US_complete_case.py @@ -71,14 +71,8 @@ def cut_outliers(df, lower, upper, var): data = US_utils.load_multiple_data(file_names) complete_case_vars = ["housing_quality", 'marital_status', 'yearly_energy', "job_sec", -<<<<<<< HEAD "education_state", 'region', "age", "job_sector", 'SF_12_MCS', 'SF_12_PCS', - 'financial_situation', "housing_tenure", 'urban', 'heating'] # many of these -======= - "education_state", 'region', "age", "job_sector", 'financial_situation', #'SF_12', - "housing_tenure", - "nkids_ind"] ->>>>>>> development + 'financial_situation', "housing_tenure", 'urban', 'heating', "nkids_ind"] # REMOVED: 'job_sector', 'labour_state' data = complete_case_varlist(data, complete_case_vars) diff --git a/minos/data_generation/generate_composite_vars.py b/minos/data_generation/generate_composite_vars.py index 4d20d5d8..20e2dc3a 100755 --- a/minos/data_generation/generate_composite_vars.py +++ b/minos/data_generation/generate_composite_vars.py @@ -1141,6 +1141,8 @@ def generate_matdep_proxy(data): axis=1, inplace=True) + return data + def calculate_children(data, parity_max=PARITY_MAX_DEFAULT): @@ -1197,7 +1199,7 @@ def calculate_children(data, def generate_difference_variables(data): # creating difference in hh income for lmm difference models. data = data.sort_values(by=['time']) - diff_columns = ["hh_income", "SF_12", "nutrition_quality"] + diff_columns = ["hh_income", "SF_12_MCS", "SF_12_PCS", "nutrition_quality", 'matdep'] diff_column_names = [item + "_diff" for item in diff_columns] data[diff_column_names] = data.groupby(["pidp"])[diff_columns].diff().fillna(0) data['nutrition_quality_diff'] = data['nutrition_quality_diff'].astype(int) diff --git a/minos/data_generation/generate_repl_pop.py b/minos/data_generation/generate_repl_pop.py index c7b82371..0538da7d 100755 --- a/minos/data_generation/generate_repl_pop.py +++ b/minos/data_generation/generate_repl_pop.py @@ -65,7 +65,7 @@ def expand_repl(US_2018): # now update Date variable (just use US_utils function new_repl = US_utils.generate_interview_date_var(new_repl) # adjust pidp to ensure unique values (have checked this and made sure this will never give us a duplicate) - new_repl['pidp'] = new_repl['pidp'] + year + 1000000 + new_repl.index + new_repl['pidp'] = new_repl['pidp'] + year + 1000000 + (3 * new_repl.index) #print(f"There are {len(new_repl)} people in the replenishing population in year {year}.") diff --git a/minos/minosPipeline/RunPipeline.py b/minos/minosPipeline/RunPipeline.py index 33d268fa..86b84b81 100755 --- a/minos/minosPipeline/RunPipeline.py +++ b/minos/minosPipeline/RunPipeline.py @@ -20,7 +20,7 @@ from minos.modules.replenishment_scotland import ReplenishmentScotland from minos.modules.add_new_birth_cohorts import FertilityAgeSpecificRates, nkidsFertilityAgeSpecificRates from minos.modules.housing import Housing -from minos.modules.physical_wellbeing import SF_12_PCS lmmYJPCS +from minos.modules.physical_wellbeing import SF_12_PCS, lmmYJPCS from minos.modules.income import Income, geeIncome, geeYJIncome, lmmDiffIncome, lmmYJIncome from minos.modules.mental_wellbeing import MWB, geeMWB, geeYJMWB, lmmDiffMWB, lmmYJMWB from minos.modules.labour import Labour @@ -30,6 +30,7 @@ from minos.modules.loneliness import Loneliness from minos.modules.education import Education from minos.modules.nutrition import Nutrition, lmmYJNutrition, lmmDiffNutrition +from minos.modules.material_deprivation import MaterialDeprivation from minos.modules.S7Labour import S7Labour from minos.modules.S7Housing import S7Housing @@ -85,6 +86,9 @@ "FertilityAgeSpecificRates()": FertilityAgeSpecificRates(), "Mortality()": Mortality(), "Education()": Education(), + "MaterialDeprivation()": MaterialDeprivation(), + "PhysicalActivity()": PhysicalActivity(), + "HousingTenure()": HousingTenure(), } SIPHER7_components_map = { # SIPHER7 stuff @@ -178,61 +182,6 @@ def validate_components(config_components, intervention): component_list: list List of component module classes. """ -<<<<<<< HEAD - - #components = [eval(x) for x in config.components] # more adapative way but security issues. - # last one in first one off. any module that requires another should be BELOW IT in this order. - # Note priority in vivarium modules supercedes this. two - # Outcome module goes first (last in sim) - components_map = { - # Outcome modules. - "MWB()": MWB(), - "SF_12_PCS()": SF_12_PCS(), - #Intermediary modules - "Tobacco()": Tobacco(), - "Alcohol()": Alcohol(), - "Neighbourhood()": Neighbourhood(), - "Labour()": Labour(), - "Heating()": Heating(), - "Housing()": Housing(), - "Income()": Income(), - "financialSituation()": financialSituation(), - "Loneliness()": Loneliness(), - "Nutrition()": Nutrition(), - "nkidsFertilityAgeSpecificRates()": nkidsFertilityAgeSpecificRates(), - "FertilityAgeSpecificRates()": FertilityAgeSpecificRates(), - "Mortality()": Mortality(), - "Education()": Education(), - "HousingTenure()": HousingTenure(), - "PhysicalActivity()": PhysicalActivity() - } - - SIPHER7_components_map = { # SIPHER7 stuff - "S7Labour()" : S7Labour(), - "S7Housing()" : S7Housing(), - "S7Neighbourhood()": S7Neighbourhood(), - "S7MentalHealth()" : S7MentalHealth(), - "S7PhysicalHealth()": S7PhysicalHealth(), - "S7EquivalentIncome()": S7EquivalentIncome() - } - - intervention_components_map = { #Interventions - "hhIncomeIntervention": hhIncomeIntervention(), - "hhIncomeChildUplift": hhIncomeChildUplift(), - "hhIncomePovertyLineChildUplift": hhIncomePovertyLineChildUplift(), - "livingWageIntervention": livingWageIntervention(), - "energyDownlift": energyDownlift() - } - - replenishment_components_map = { - "Replenishment()": Replenishment(), - "NoReplenishment()": NoReplenishment(), - "ReplenishmentNowcast()": ReplenishmentNowcast(), - "ReplenishmentScotland()": ReplenishmentScotland(), - } - -======= ->>>>>>> development component_list = [] replenishment_component = [] print("Initial components list:", config_components) @@ -301,6 +250,7 @@ def RunPipeline(config, intervention=None): "bestNormalize": importr("bestNormalize"), "VGAM": importr("VGAM"), "lme4": importr("lme4"), + "glmmTMB": importr("glmmTMB"), } simulation._data.write("rpy2_modules", rpy2_modules) diff --git a/minos/modules/housing.py b/minos/modules/housing.py index e0badebe..4e22d80d 100755 --- a/minos/modules/housing.py +++ b/minos/modules/housing.py @@ -59,6 +59,7 @@ def setup(self, builder): view_columns = ["sex", "S7_labour_state", "SF_12_MCS", + "SF_12_PCS", "job_sec", "ethnicity", "age", diff --git a/minos/modules/income.py b/minos/modules/income.py index 20cbcf14..f46af957 100755 --- a/minos/modules/income.py +++ b/minos/modules/income.py @@ -612,8 +612,10 @@ def setup(self, builder): 'time', 'pidp', #'weight', - 'SF_12', + 'SF_12_MCS', + 'SF_12_PCS', 'hh_income_diff', + 'job_sector' ] #columns_created = ['hh_income_diff'] # view_columns += self.transition_model.rx2('model').names @@ -706,6 +708,7 @@ def calculate_income(self, pop): dependent='hh_income_new', yeo_johnson = True, reflect=False, + mod_type='gamma', noise_std= 0.175)#0.45 for yj. 100? for non yj. # get new hh income diffs and update them into history_data. #self.update_history_dataframe(pop, self.year-1) diff --git a/minos/modules/material_deprivation.py b/minos/modules/material_deprivation.py new file mode 100644 index 00000000..0d7612e0 --- /dev/null +++ b/minos/modules/material_deprivation.py @@ -0,0 +1,139 @@ +""" +Module for material deprivation in Minos. +""" + +import pandas as pd +import minos.modules.r_utils as r_utils +from minos.modules.base_module import Base +from seaborn import histplot +import matplotlib.pyplot as plt +import numpy as np +import logging + + +class MaterialDeprivation(Base): + """Mental Well-Being Module""" + # Special methods used by vivarium. + @property + def name(self): + return 'material_deprivation' + + def __repr__(self): + return "MaterialDeprivation()" + + def setup(self, builder): + """ Initialise the module during simulation.setup(). + Notes + ----- + - Load in data from pre_setup + - Register any value producers/modifiers for income + - Add required columns to population data frame + - Update other required items such as randomness stream. + Parameters + ---------- + builder : vivarium.engine.Builder + Vivarium's control object. Stores all simulation metadata and allows modules to use it. + """ + + # Load in inputs from pre-setup. + self.rpy2_modules = builder.data.load("rpy2_modules") + + # Build vivarium objects for calculating transition probabilities. + # Typically this is registering rate/lookup tables. See vivarium docs/other modules for examples. + #self.transition_coefficients = builder. + + # Assign randomness streams if necessary. + self.random = builder.randomness.get_stream(self.generate_random_crn_key()) + + # Determine which subset of the main population is used in this module. + # columns_created is the columns created by this module. + # view_columns is the columns from the main population used in this module. + # In this case, view_columns are taken straight from the transition model + view_columns = ['pidp', + 'sex', + 'ethnicity', + 'age', + 'time', + 'region', + 'education_state', + 'hh_income', + 'SF_12_MCS', + 'SF_12_MCS_diff', + 'SF_12_PCS', + 'SF_12_PCS_diff', + 'marital_status', + 'hhsize', + 'housing_tenure', + 'urban', + 'financial_situation', + 'matdep', + 'matdep_diff', + 'weight'] + + self.population_view = builder.population.get_view(columns=view_columns) + + # Population initialiser. When new individuals are added to the microsimulation a constructer is called for each + # module. Declare what constructer is used. usually on_initialize_simulants method is called. Inidividuals are + # created at the start of a model "setup" or after some deterministic (add cohorts) or random (births) event. + #builder.population.initializes_simulants(self.on_initialize_simulants, creates_columns=columns_created) + + # Declare events in the module. At what times do individuals transition states from this module. E.g. when does + # individual graduate in an education module. + builder.event.register_listener("time_step", self.on_time_step, priority=5) + + #only need to load this once for now. + #self.gee_transition_model = r_utils.load_transitions(f"SF_12/lmm/SF_12_LMM", self.rpy2_modules, path=self.transition_dir) + self.gee_transition_model = r_utils.load_transitions(f"matdep/glmmb/matdep_GLMMB", self.rpy2_modules, path=self.transition_dir) + + def on_time_step(self, event): + """Produces new children and updates parent status on time steps. + Parameters + ---------- + event : vivarium.population.PopulationEvent + The event time_step that called this function. + """ + + self.year = event.time.year + # Get living people to update their income + pop = self.population_view.get(event.index, query="alive =='alive'") + pop = pop.sort_values('pidp') #sorting aligns index to make sure individual gets their correct prediction. + pop["matdep_last"] = pop["matdep"] + + # Predict next mwb value + newWaveMatdep = pd.DataFrame(columns=['matdep']) + newWaveMatdep['matdep'] = self.calculate_mwb(pop) + newWaveMatdep.index = pop.index + #newWaveMatdep["matdep"] -= 1 + + sf12_mean = np.mean(newWaveMatdep["matdep"]) + std_ratio = (11/np.std(newWaveMatdep["matdep"])) + newWaveMatdep["matdep"] *= (11/np.std(newWaveMatdep["matdep"])) + newWaveMatdep["matdep"] -= ((std_ratio-1)*sf12_mean) + newWaveMatdep["matdep"] -= 1.5 + #newWaveMatdep["matdep"] += (50 - np.mean(newWaveMatdep["SF_12_MCS"])) + newWaveMatdep["matdep"] = np.clip(newWaveMatdep["matdep"], 0, 1) # keep within [0, 100] bounds of SF12. + newWaveMatdep["matdep_diff"] = newWaveMatdep["matdep"] - pop["matdep"] + # Update population with new SF12_MCS + #print(np.mean(newWaveMatdep["matdep"])) + #print(np.std(newWaveMatdep["matdep"])) + self.population_view.update(newWaveMatdep[['matdep', "matdep_diff"]]) + + + def calculate_mwb(self, pop): + """Calculate SF_12 transition distribution based on provided people/indices + Parameters + ---------- + index : pd.Index + Which individuals to calculate transitions for. + Returns + ------- + """ + out_data = r_utils.predict_next_timestep_yj_gamma_glmm(self.gee_transition_model, + self.rpy2_modules, + current= pop, + dependent='matdep', + reflect=False, + yeo_johnson= False, + mod_type='beta', + noise_std= 5) # 5 for non yj, 0.35 for yj + return out_data \ No newline at end of file diff --git a/minos/modules/mental_wellbeing.py b/minos/modules/mental_wellbeing.py index a0636179..80900963 100755 --- a/minos/modules/mental_wellbeing.py +++ b/minos/modules/mental_wellbeing.py @@ -484,7 +484,9 @@ def setup(self, builder): 'ncigs', 'nutrition_quality', 'neighbourhood_safety', - 'loneliness'] + 'loneliness', + 'financial_situation', + 'active',] self.population_view = builder.population.get_view(columns=view_columns) @@ -550,7 +552,8 @@ def calculate_mwb(self, pop): dependent='SF_12_MCS', reflect=True, yeo_johnson= True, - noise_std= 0.1)# 5 for non yj, 0.35 for yj + mod_type='gamma', + noise_std= 0.1) # 5 for non yj, 0.35 for yj return out_data diff --git a/minos/modules/nutrition.py b/minos/modules/nutrition.py index feaf1483..9599abe0 100755 --- a/minos/modules/nutrition.py +++ b/minos/modules/nutrition.py @@ -165,7 +165,8 @@ def setup(self, builder): 'sex', 'ethnicity', 'region', - 'SF_12', + 'SF_12_MCS', + 'SF_12_PCS', 'education_state', #'labour_state', 'job_sec', @@ -174,7 +175,8 @@ def setup(self, builder): #'alcohol_spending', 'ncigs', 'nutrition_quality', - 'nutrition_quality_diff'] + 'nutrition_quality_diff', + 'financial_situation'] #view_columns += self.transition_model.rx2('model').names self.population_view = builder.population.get_view(columns=view_columns) diff --git a/minos/modules/physical_wellbeing.py b/minos/modules/physical_wellbeing.py index 58bc0b5b..766df8bc 100644 --- a/minos/modules/physical_wellbeing.py +++ b/minos/modules/physical_wellbeing.py @@ -5,6 +5,7 @@ """ import pandas as pd +import numpy as np from pathlib import Path import minos.modules.r_utils as r_utils from minos.modules.base_module import Base @@ -131,3 +132,133 @@ def plot(self, pop, config): histplot(pop, x="SF_12_PCS", stat='density') plt.savefig(file_name) plt.close('all') + + +class lmmYJPCS(Base): + """Mental Well-Being Module""" + # Special methods used by vivarium. + @property + def name(self): + return 'glmm_pcs' + + def __repr__(self): + return "lmmYJPCS()" + + def setup(self, builder): + """ Initialise the module during simulation.setup(). + Notes + ----- + - Load in data from pre_setup + - Register any value producers/modifiers for income + - Add required columns to population data frame + - Update other required items such as randomness stream. + Parameters + ---------- + builder : vivarium.engine.Builder + Vivarium's control object. Stores all simulation metadata and allows modules to use it. + """ + + # Load in inputs from pre-setup. + self.rpy2_modules = builder.data.load("rpy2_modules") + + # Build vivarium objects for calculating transition probabilities. + # Typically this is registering rate/lookup tables. See vivarium docs/other modules for examples. + #self.transition_coefficients = builder. + + # Assign randomness streams if necessary. + self.random = builder.randomness.get_stream(self.generate_random_crn_key()) + + # Determine which subset of the main population is used in this module. + # columns_created is the columns created by this module. + # view_columns is the columns from the main population used in this module. + # In this case, view_columns are taken straight from the transition model + view_columns = ['pidp', + 'sex', + 'ethnicity', + 'age', + 'time', + #'education_state', + #'labour_state', + #'job_sec', + 'hh_income', + 'SF_12_MCS', + 'SF_12_MCS_diff', + 'SF_12_PCS', + 'SF_12_PCS_diff', + 'housing_quality', + #'phealth', + 'ncigs', + 'nutrition_quality', + 'neighbourhood_safety', + 'loneliness', + 'financial_situation', + 'active', + 'auditc',] + + self.population_view = builder.population.get_view(columns=view_columns) + + # Population initialiser. When new individuals are added to the microsimulation a constructer is called for each + # module. Declare what constructer is used. usually on_initialize_simulants method is called. Inidividuals are + # created at the start of a model "setup" or after some deterministic (add cohorts) or random (births) event. + #builder.population.initializes_simulants(self.on_initialize_simulants, creates_columns=columns_created) + + # Declare events in the module. At what times do individuals transition states from this module. E.g. when does + # individual graduate in an education module. + builder.event.register_listener("time_step", self.on_time_step, priority=9) + + #only need to load this once for now. + #self.gee_transition_model = r_utils.load_transitions(f"SF_12/lmm/SF_12_LMM", self.rpy2_modules, path=self.transition_dir) + self.gee_transition_model = r_utils.load_transitions(f"SF_12_PCS/glmm/SF_12_PCS_GLMM", self.rpy2_modules, path=self.transition_dir) + + def on_time_step(self, event): + """Produces new children and updates parent status on time steps. + Parameters + ---------- + event : vivarium.population.PopulationEvent + The event time_step that called this function. + """ + + self.year = event.time.year + # Get living people to update their income + pop = self.population_view.get(event.index, query="alive =='alive'") + pop = pop.sort_values('pidp') #sorting aligns index to make sure individual gets their correct prediction. + pop["SF_12_PCS_last"] = pop["SF_12_PCS"] + + # Predict next mwb value + newWavePWB = pd.DataFrame(columns=['SF_12_PCS']) + newWavePWB['SF_12_PCS'] = self.calculate_pwb(pop) + newWavePWB.index = pop.index + #newWavePWB["SF_12_PCS"] -= 1 + + sf12_mean = np.mean(newWavePWB["SF_12_PCS"]) + std_ratio = (11/np.std(newWavePWB["SF_12_PCS"])) + newWavePWB["SF_12_PCS"] *= (11/np.std(newWavePWB["SF_12_PCS"])) + newWavePWB["SF_12_PCS"] -= ((std_ratio-1)*sf12_mean) + newWavePWB["SF_12_PCS"] -= 1.5 + #newWavePWB["SF_12_PCS"] += (50 - np.mean(newWavePWB["SF_12_PCS"])) + newWavePWB["SF_12_PCS"] = np.clip(newWavePWB["SF_12_PCS"], 0, 100) # keep within [0, 100] bounds of SF12. + newWavePWB["SF_12_PCS_diff"] = newWavePWB["SF_12_PCS"] - pop["SF_12_PCS"] + # Update population with new SF_12_PCS + #print(np.mean(newWavePWB["SF_12_PCS"])) + #print(np.std(newWavePWB["SF_12_PCS"])) + self.population_view.update(newWavePWB[['SF_12_PCS', "SF_12_PCS_diff"]]) + + + def calculate_pwb(self, pop): + """Calculate SF_12 transition distribution based on provided people/indices + Parameters + ---------- + index : pd.Index + Which individuals to calculate transitions for. + Returns + ------- + """ + out_data = r_utils.predict_next_timestep_yj_gamma_glmm(self.gee_transition_model, + self.rpy2_modules, + current= pop, + dependent='SF_12_PCS', + reflect=True, + yeo_johnson= True, + mod_type='gamma', + noise_std= 0.1) # 5 for non yj, 0.35 for yj + return out_data diff --git a/minos/modules/r_utils.py b/minos/modules/r_utils.py index a648ac26..b58ed6d2 100755 --- a/minos/modules/r_utils.py +++ b/minos/modules/r_utils.py @@ -250,6 +250,46 @@ def predict_next_timestep_zip(model, rpy2Modules, current, dependent): return np.ceil(preds) +def predict_next_timestep_logit(model, rpy2_modules, current, dependent): + """ + This function will take the transition model loaded in load_transitions() and use it to predict the next timestep + for a module. + + Parameters + ---------- + model : R rds object + Fitted model loaded in from .rds file + current : vivarium.framework.population.PopulationView + View including columns that are required for prediction + dependent : str + The independent variable we are trying to predict + + Returns: + ------- + A prediction of the information for next timestep + """ + # import R packages + base = rpy2_modules['base'] + stats = rpy2_modules['stats'] + + # Convert from pandas to R using package converter + with localconverter(ro.default_converter + pandas2ri.converter): + currentRDF = ro.conversion.py2rpy(current) + + # R predict method returns a Vector of predicted values, so need to be bound to original df and converter to Pandas + prediction = stats.predict(model, currentRDF, type='response') + newRPopDF = base.cbind(currentRDF, predicted = prediction) + # Convert back to pandas + with localconverter(ro.default_converter + pandas2ri.converter): + newPandasPopDF = ro.conversion.rpy2py(newRPopDF) + + # Now rename the predicted var (have to drop original column first) + newPandasPopDF[[dependent]] = newPandasPopDF[['predicted']] + newPandasPopDF.drop(labels=['predicted'], axis='columns', inplace=True) + + return newPandasPopDF[[dependent]] + + def predict_next_timestep_gee(model, rpy2_modules, current, dependent, noise_std): """ This function will take the transition model loaded in load_transitions() and use it to predict the next timestep @@ -377,7 +417,7 @@ def predict_next_timestep_yj_gaussian_lmm(model, rpy2_modules, current, dependen return pd.DataFrame(prediction_output, columns=[dependent]) -def predict_next_timestep_yj_gamma_glmm(model, rpy2_modules, current, dependent, reflect, yeo_johnson, noise_std = 1): +def predict_next_timestep_yj_gamma_glmm(model, rpy2_modules, current, dependent, reflect, yeo_johnson, mod_type, noise_std = 1): """ This function will take the transition model loaded in load_transitions() and use it to predict the next timestep for a module. @@ -399,6 +439,7 @@ def predict_next_timestep_yj_gamma_glmm(model, rpy2_modules, current, dependent, stats = rpy2_modules['stats'] bestNormalize = rpy2_modules['bestNormalize'] lme4 = rpy2_modules["lme4"] + glmmTMB = rpy2_modules["glmmTMB"] # Convert from pandas to R using package converter with localconverter(ro.default_converter + pandas2ri.converter): @@ -417,14 +458,20 @@ def predict_next_timestep_yj_gamma_glmm(model, rpy2_modules, current, dependent, # get minimum value to reverse transformatino to strictly positve values. min_value = model.do_slot("min_value") - prediction = lme4.predict_merMod(model, newdata=currentRDF, type='response', allow_new_levels=True) # estimate next income using gamma GEE. - # Inverting transforms to get back to true income values. + if mod_type == 'gamma': + prediction = lme4.predict_merMod(model, newdata=currentRDF, type='response', + allow_new_levels=True) # estimate next income using gamma GEE. + # Inverting transforms to get back to true income values. + prediction = prediction.ro + (min_value.ro - 0.001) # invert shift to strictly positive values. + elif mod_type == 'beta': + prediction = stats.predict(model, newdata=currentRDF, type='response', + allow_new_levels=True) # estimate next income using gamma GEE. + - prediction = prediction.ro + (min_value.ro - 0.001) # invert shift to strictly positive values. if dependent == 'nutrition_quality': prediction = prediction.ro + stats.rnorm(current.shape[0], 0, noise_std) # add gaussian noise. - elif dependent == "SF_12" and noise_std: + elif dependent in ["SF_12_MCS", "SF_12_PCS"] and noise_std: VGAM = rpy2_modules["VGAM"] prediction = prediction.ro + VGAM.rlaplace(current.shape[0], 0, noise_std) # add gaussian noise. #prediction = prediction.ro + stats.rnorm(current.shape[0], 0, noise_std) # add gaussian noise. diff --git a/minos/modules/replenishment.py b/minos/modules/replenishment.py index 3f399172..fcf7b704 100755 --- a/minos/modules/replenishment.py +++ b/minos/modules/replenishment.py @@ -53,7 +53,6 @@ def setup(self, builder): 'birth_year', 'nobs', 'region', - 'SF_12_MCS', 'hh_int_y', 'hh_int_m', 'Date', @@ -71,7 +70,6 @@ def setup(self, builder): 'max_educ', 'yearly_energy', 'job_sector', - 'SF_12_PCS', 'gross_pay_se', 'nutrition_quality', 'job_hours_se', @@ -98,9 +96,14 @@ def setup(self, builder): 'urban', 'auditc', 'active', - 'SF_12_diff', + 'SF_12_MCS', + 'SF_12_MCS_diff', 'hh_income_diff', 'nutrition_quality_diff', + 'SF_12_PCS', + 'SF_12_PCS_diff', + 'matdep', + 'matdep_diff', ] # Shorthand methods for readability. diff --git a/minos/transitions/Makefile b/minos/transitions/Makefile index 4401f655..423e1b30 100644 --- a/minos/transitions/Makefile +++ b/minos/transitions/Makefile @@ -11,36 +11,45 @@ #transitions: $(TRANSITION_DATA)/loneliness/clm/loneliness_clm_2018_2019.rds transitions_default: | $(TRANSITION_DATA) -transitions_default: final_data $(TRANSITION_DATA)/ncigs/zip/ncigs_2019_2020.rds $(TRANSITION_DATA)/SF_12/glmm/SF_12_GLMM.rds +transitions_default: final_data $(TRANSITION_DATA)/ncigs/zip/ncigs_2019_2020.rds $(TRANSITION_DATA)/SF_12_MCS/glmm/SF_12_MCS_GLMM.rds transitions_SIPHER7: | $(TRANSITION_DATA) -transitions_SIPHER7: final_data $(TRANSITION_DATA)/S7_mental_health/clm/S7_mental_health_2019_2020.rds +transitions_SIPHER7: final_data $(TRANSITION_DATA)/S7_mental_health/clm/S7_mental_health_2019_2020.rds $(TRANSITION_DATA)/hh_income/glmm/hh_income_new_GLMM.rds scot_transitions: final_data $(TRANSITION_DATA)/scotland/hh_income/ols/hh_income_2018_2019.rds cv_transitions: $(TRANSITION_DATA)/cross_validation/version5/ncigs/zip/ncigs_2018_2019.rds $(TRANSITION_DATA)/cross_validation/version5/SF_12/glmm/SF_12_GLMM.rds cv_S7_transitions: | $(TRANSITION_DATA) -cv_S7_transitions: $(TRANSITION_DATA)/cross_validation/version5/S7_mental_health/clm/S7_mental_health_2019_2020.rds +cv_S7_transitions: $(TRANSITION_DATA)/cross_validation/version5/S7_mental_health/clm/S7_mental_health_2019_2020.rds $(TRANSITION_DATA)/cross_validation/version5/hh_income/glmm/hh_income_new_GLMM.rds -$(TRANSITION_DATA)/ncigs/zip/ncigs_2019_2020.rds: $(FINALDATA)/2020_US_cohort.csv $(TRANSITION_SOURCE)/estimate_transitions.R $(TRANSITION_SOURCE)/model_definitions_default.txt +$(TRANSITION_DATA)/ncigs/zip/ncigs_2019_2020.rds: $(FINALDATA)/2020_US_cohort.csv $(TRANSITION_SOURCE)/estimate_transitions.R $(TRANSITION_SOURCE)/transition_model_functions.R $(TRANSITION_SOURCE)/model_definitions_default.txt $(RSCRIPT) $(SOURCEDIR)/transitions/estimate_transitions.R --default -$(TRANSITION_DATA)/S7_mental_health/clm/S7_mental_health_2019_2020.rds: $(FINALDATA)/2020_US_cohort.csv $(TRANSITION_SOURCE)/estimate_transitions.R $(TRANSITION_SOURCE)/model_definitions_S7.txt +$(TRANSITION_DATA)/S7_mental_health/clm/S7_mental_health_2019_2020.rds: $(FINALDATA)/2020_US_cohort.csv $(TRANSITION_SOURCE)/estimate_transitions.R $(TRANSITION_SOURCE)/transition_model_functions.R $(TRANSITION_SOURCE)/model_definitions_S7.txt $(RSCRIPT) $(SOURCEDIR)/transitions/estimate_transitions.R --sipher7 -$(TRANSITION_DATA)/scotland/hh_income/ols/hh_income_2018_2019.rds: $(SCOTDATA)/2020_US_cohort.csv $(TRANSITION_SOURCE)/estimate_transitions.R $(TRANSITION_SOURCE)/model_definitions_SCOTLAND.txt +$(TRANSITION_DATA)/scotland/hh_income/ols/hh_income_2018_2019.rds: $(SCOTDATA)/2020_US_cohort.csv $(TRANSITION_SOURCE)/estimate_transitions.R $(TRANSITION_SOURCE)/transition_model_functions.R $(TRANSITION_SOURCE)/model_definitions_SCOTLAND.txt $(RSCRIPT) $(SOURCEDIR)/transitions/estimate_transitions.R --scotland -$(TRANSITION_DATA)/cross_validation/version5/ncigs/zip/ncigs_2018_2019.rds: $(FINALDATA)/cross_validation/batch5/2020_US_cohort.csv $(TRANSITION_SOURCE)/estimate_transitions.R $(TRANSITION_SOURCE)/model_definitions_default.txt +$(TRANSITION_DATA)/cross_validation/version5/ncigs/zip/ncigs_2018_2019.rds: $(FINALDATA)/cross_validation/batch5/2020_US_cohort.csv $(TRANSITION_SOURCE)/estimate_transitions.R $(TRANSITION_SOURCE)/transition_model_functions.R $(TRANSITION_SOURCE)/model_definitions_default.txt $(RSCRIPT) $(SOURCEDIR)/transitions/estimate_transitions.R --cross_validation -$(TRANSITION_DATA)/SF_12/glmm/SF_12_GLMM.rds: $(FINALDATA)/2019_US_cohort.csv $(TRANSITION_SOURCE)/estimate_longitudinal_transitions.R $(TRANSITION_SOURCE)/model_definitions_default.txt - $(RSCRIPT) $(SOURCEDIR)/transitions/estimate_longitudinal_transitions.R +$(TRANSITION_DATA)/cross_validation/version5/S7_mental_health/clm/S7_mental_health_2019_2020.rds: $(FINALDATA)/cross_validation/batch5/2020_US_cohort.csv $(TRANSITION_SOURCE)/estimate_transitions.R $(TRANSITION_SOURCE)/transition_model_functions.R $(TRANSITION_SOURCE)/model_definitions_S7.txt + $(RSCRIPT) $(SOURCEDIR)/transitions/estimate_transitions.R --sipher7 --cross_validation -$(TRANSITION_DATA)/cross_validation/version5/SF_12/glmm/SF_12_GLMM.rds: $(FINALDATA)/cross_validation/batch5/2020_US_cohort.csv $(TRANSITION_SOURCE)/estimate_longitudinal_transitions.R $(TRANSITION_SOURCE)/model_definitions_default.txt +$(TRANSITION_DATA)/SF_12_MCS/glmm/SF_12_MCS_GLMM.rds: $(FINALDATA)/2020_US_cohort.csv $(TRANSITION_SOURCE)/estimate_longitudinal_transitions.R $(TRANSITION_SOURCE)/transition_model_functions.R $(TRANSITION_SOURCE)/model_definitions_default.txt + $(RSCRIPT) $(SOURCEDIR)/transitions/estimate_longitudinal_transitions.R --default + +$(TRANSITION_DATA)/hh_income/glmm/hh_income_new_GLMM.rds: $(FINALDATA)/2020_US_cohort.csv $(TRANSITION_SOURCE)/estimate_longitudinal_transitions.R $(TRANSITION_SOURCE)/transition_model_functions.R $(TRANSITION_SOURCE)/model_definitions_S7.txt + $(RSCRIPT) $(SOURCEDIR)/transitions/estimate_longitudinal_transitions.R --sipher7 + +$(TRANSITION_DATA)/cross_validation/version5/SF_12_MCS/glmm/SF_12_MCS_GLMM.rds: $(FINALDATA)/cross_validation/batch5/2020_US_cohort.csv $(TRANSITION_SOURCE)/estimate_longitudinal_transitions.R $(TRANSITION_SOURCE)/transition_model_functions.R $(TRANSITION_SOURCE)/model_definitions_default.txt $(RSCRIPT) $(SOURCEDIR)/transitions/estimate_longitudinal_transitions.R --cross_validation +$(TRANSITION_DATA)/cross_validation/version5/hh_income/glmm/hh_income_new_GLMM.rds: $(FINALDATA)/cross_validation/batch5/2020_US_cohort.csv $(TRANSITION_SOURCE)/estimate_longitudinal_transitions.R $(TRANSITION_SOURCE)/transition_model_functions.R $(TRANSITION_SOURCE)/model_definitions_S7.txt + $(RSCRIPT) $(SOURCEDIR)/transitions/estimate_longitudinal_transitions.R --sipher7 --cross_validation + $(TRANSITION_DATA): @echo "Creating transition data directory" mkdir -p $(TRANSITION_DATA) diff --git a/minos/transitions/estimate_longitudinal_transitions.R b/minos/transitions/estimate_longitudinal_transitions.R index 338eab60..87665db3 100644 --- a/minos/transitions/estimate_longitudinal_transitions.R +++ b/minos/transitions/estimate_longitudinal_transitions.R @@ -32,7 +32,7 @@ run_longitudinal_models <- function(transitionDir_path, transitionSourceDir_path modDef_path = paste0(transitionSourceDir_path, mod_def_name) modDefs <- file(description = modDef_path, open="r", blocking = TRUE) - valid_longitudnial_model_types <- c("LMM", "LMM_DIFF", "GLMM", "GEE_DIFF","ORDGEE", "CLMM") + valid_longitudnial_model_types <- c("LMM", "LMM_DIFF", "GLMM", "GEE_DIFF","ORDGEE", "CLMM", 'GLMMB') data[which(data$ncigs==-8), 'ncigs'] <- 0 @@ -84,14 +84,14 @@ run_longitudinal_models <- function(transitionDir_path, transitionSourceDir_path # use.weights <- TRUE #} - if (dependent == "SF_12") { + if (dependent %in% c("SF_12_MCS", 'SF_12_PCS')) { do.reflect = TRUE # only SF12 continuous data is reflected to be left skewed. } else { do.reflect=FALSE } - if (dependent == "SF_12" || dependent == "hh_income") { + if (dependent %in% c("SF_12_MCS", 'SF_12_PCS', 'hh_income')) { do.yeo.johnson = T # } else { @@ -125,7 +125,7 @@ run_longitudinal_models <- function(transitionDir_path, transitionSourceDir_path dependent <- paste0(dependent, "_diff") formula.string <- paste0(dependent, " ~ ", independents) form <- as.formula(formula.string) - } + } # if using glmms need to be careful which time the outcome variable is from. # for nutrition quality and SF12 using previous wave information to predict next @@ -134,7 +134,7 @@ run_longitudinal_models <- function(transitionDir_path, transitionSourceDir_path # For SF12 predicting current state given changes in all other information and previous SF12 value. # I.E. using 2020 information and 2019 SF12 to estimate 2020 SF12. # We have SF_12_last in the model formula for 2019 SF12. - if (dependent == "nutrition_quality" || dependent == "hh_income") { + if (dependent == "nutrition_quality" || dependent == "hh_income") { # get leading nutrition/income value and label with _new. data <- data %>% group_by(pidp) %>% @@ -144,8 +144,8 @@ run_longitudinal_models <- function(transitionDir_path, transitionSourceDir_path dependent <- paste0(dependent, "_new") formula.string <- paste0(dependent, " ~ ", independents) form <- as.formula(formula.string) - } - else if (dependent.isin(['SF_12_MCS', 'SF_12_PCS'])) { + } + else if (dependent %in% c('SF_12_MCS', 'SF_12_PCS', 'matdep')) { # get lagged SF12 value and label with _last. data <- data %>% group_by(pidp) %>% @@ -159,7 +159,7 @@ run_longitudinal_models <- function(transitionDir_path, transitionSourceDir_path # formula.string <- paste0(dependent, " ~ ", independents) form <- as.formula(formula.string) - } + } # get only required variables and sort by pidp/time. df <- data[, append(all.vars(form), c("time", 'pidp', 'weight'))] sorted_df <- df[order(df$pidp, df$time),] @@ -167,7 +167,7 @@ run_longitudinal_models <- function(transitionDir_path, transitionSourceDir_path # function call and parameters based on model type. if(tolower(mod.type) == 'glmm') { # - model <- estimate_longitudinal_glmm(data = sorted_df, + model <- estimate_gamma_glmm(data = sorted_df, formula = form, include_weights = use.weights, depend = dependent, @@ -205,6 +205,14 @@ run_longitudinal_models <- function(transitionDir_path, transitionSourceDir_path formula = form, depend = dependent) + } else if (tolower(mod.type) == "glmmb") { + + model <- estimate_beta_glmm(data = sorted_df, + formula = form, + depend = dependent, + reflect = do.reflect, + yeo_johnson = do.yeo.johnson) + } write_coefs <- F diff --git a/minos/transitions/model_definitions_default.txt b/minos/transitions/model_definitions_default.txt index 90904b9f..23485996 100644 --- a/minos/transitions/model_definitions_default.txt +++ b/minos/transitions/model_definitions_default.txt @@ -10,6 +10,7 @@ NNET : housing_tenure ~ age + factor(sex) + factor(housing_tenure) + relevel(fac CLM : loneliness ~ factor(loneliness) + scale(age) + factor(sex) + scale(SF_12_MCS) + scale(SF_12_PCS) + relevel(factor(education_state), ref = '3') + relevel(factor(job_sec), ref = '3') + scale(hh_income) + relevel(factor(hh_comp), ref = '3') + relevel(factor(marital_status), ref = 'Partnered') + relevel(factor(ethnicity), ref = 'WBI') CLM : neighbourhood_safety ~ scale(age) + factor(sex) + factor(job_sec) + relevel(factor(ethnicity), ref = 'WBI') + scale(hh_income) + factor(housing_quality) + relevel(factor(region), ref = 'South East') + factor(urban) NNET : education_state ~ factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') +GLMMB : matdep ~ scale(matdep) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(marital_status) + hhsize + factor(housing_tenure) + factor(urban) + factor(financial_situation) + (1|pidp) ZIP : ncigs ~ ncigs + age + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + relevel(factor(job_sec), ref = '3') + scale(hh_income) + relevel(factor(ethnicity), ref = 'WBI') | I(factor(ncigs>0)) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(job_sec), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) GLMM : SF_12_PCS ~ scale(SF_12_PCS_last) + I(scale(SF_12_PCS_last)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + factor(housing_quality) + scale(hh_income) + factor(neighbourhood_safety) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(financial_situation) + factor(active) + factor(auditc) + (1|pidp) GLMM : SF_12_MCS ~ scale(SF_12_MCS_last) + I(scale(SF_12_MCS_last)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + factor(housing_quality) + scale(hh_income) + factor(neighbourhood_safety) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(financial_situation) + factor(active) + (1|pidp) diff --git a/minos/transitions/transition_model_functions.R b/minos/transitions/transition_model_functions.R index da81586a..6fd07bda 100644 --- a/minos/transitions/transition_model_functions.R +++ b/minos/transitions/transition_model_functions.R @@ -4,6 +4,7 @@ require(nnet) require(pscl) require(bestNormalize) require(lme4) +require(glmmTMB) ################ Model Specific Functions ################ @@ -13,12 +14,12 @@ require(lme4) # information available) estimate_yearly_ols <- function(data, formula, include_weights = FALSE, depend, transform = FALSE) { - + if (transform){ yj <- yeojohnson(data[, c(depend)], standardize=FALSE) data[, c(depend)] <- predict(yj) } - + if(include_weights) { # fit the model including weights (after 2009) model <- lm(formula, @@ -30,7 +31,7 @@ estimate_yearly_ols <- function(data, formula, include_weights = FALSE, depend, data = data) } model[[depend]] <- data[[depend]] - + if (transform){ model$transform <- yj } @@ -38,7 +39,7 @@ estimate_yearly_ols <- function(data, formula, include_weights = FALSE, depend, } estimate_yearly_clm <- function(data, formula, include_weights = FALSE, depend) { - + # Sort out dependent type (factor) data[[depend]] <- as.factor(data[[depend]]) data <- data[, append(all.vars(formula), c("time", 'pidp', 'weight'))] @@ -68,19 +69,19 @@ estimate_yearly_clm <- function(data, formula, include_weights = FALSE, depend) estimate_yearly_logit <- function(data, formula, include_weights = FALSE, depend) { - + # Sort out dependent type (factor) data[[depend]] <- as.factor(data[[depend]]) - + data = replace.missing(data) - + if(include_weights) { model <- glm(formula, family=binomial(link="logit"), weights = weight, data=data) } else { model <- glm(formula, family=binomial(link="logit"), data=data) } # add obs and preds to model object for any later plotting. - # This is mildly stupid.. + # This is mildly stupid.. model[[depend]] <- data[[depend]] model$class_preds <- predict(model) return(model) @@ -88,11 +89,11 @@ estimate_yearly_logit <- function(data, formula, include_weights = FALSE, depend estimate_yearly_nnet <- function(data, formula, include_weights = FALSE, depend) { - + data = replace.missing(data) # Sort out dependent type (factor) data[[depend]] <- as.factor(data[[depend]]) - + if(include_weights) { model <- multinom(formula = formula, data = data, @@ -111,7 +112,7 @@ estimate_yearly_nnet <- function(data, formula, include_weights = FALSE, depend) } estimate_yearly_zip <- function(data, formula, include_weights = FALSE, depend) { - + if(include_weights) { model <- zeroinfl(formula = formula, data = data, @@ -121,16 +122,16 @@ estimate_yearly_zip <- function(data, formula, include_weights = FALSE, depend) } else { model <- zeroinfl(formula = formula, data = data, - dist = 'pois', + dist = 'pois', link='logit') model[[depend]] <- data[[depend]] model$class_preds <- predict(model) } - + #print(summary(model)) #prs<- 1 - (logLik(model)/logLik(zeroinfl(next_ncigs ~ 1, data=dat.subset, dist='negbin', link='logit'))) #print(prs) - + return(model) } @@ -143,12 +144,12 @@ nanmax <- function(x) { ifelse( !all(is.na(x)), max(x, na.rm=T), NA) } nanmin <- function(x) { ifelse( !all(is.na(x)), min(x, na.rm=T), NA) } estimate_longitudinal_lmm <- function(data, formula, include_weights = FALSE, depend, yeo_johnson, reflect) { - + data <- replace.missing(data) #data <- drop_na(data) max_value <- nanmax(data[[depend]]) if (reflect) { - data[, c(depend)] <- max_value - data[, c(depend)] + data[, c(depend)] <- max_value - data[, c(depend)] } if (yeo_johnson) { yj <- yeojohnson(data[,c(depend)]) @@ -156,11 +157,11 @@ estimate_longitudinal_lmm <- function(data, formula, include_weights = FALSE, de } if(include_weights) { - model <- lmer(formula, - weights=weight, + model <- lmer(formula, + weights=weight, data = data) } else { - model <- lmer(formula, + model <- lmer(formula, data = data) } if (yeo_johnson) { @@ -170,7 +171,7 @@ estimate_longitudinal_lmm <- function(data, formula, include_weights = FALSE, de attr(model,"max_value") <- max_value # Works though. } #browser() - #model@transform <- yj + #model@transform <- yj #model@min_value <- min_value #model@max_value <- max_value return(model) @@ -178,7 +179,7 @@ estimate_longitudinal_lmm <- function(data, formula, include_weights = FALSE, de estimate_longitudinal_lmm_diff <- function(data, formula, include_weights = FALSE, depend, reflect, yeo_johnson) { - + data <- replace.missing(data) data <- drop_na(data) if (yeo_johnson){ @@ -187,11 +188,11 @@ estimate_longitudinal_lmm_diff <- function(data, formula, include_weights = FALS } if(include_weights) { - model <- lmer(formula, - weights=weight, + model <- lmer(formula, + weights=weight, data = data) } else { - model <- lmer(formula, + model <- lmer(formula, data = data) } if (yeo_johnson){ @@ -200,21 +201,21 @@ estimate_longitudinal_lmm_diff <- function(data, formula, include_weights = FALS #attr(model,"max_value") <- max_value #attr(model,"min_value") <- min_value #browser() - + #model@transform <- yj # S4 class uses @ rather than $ for assignment. y tho? #model@min_value <- min_value #model@max_value <- max_value return(model) } -estimate_longitudinal_glmm <- function(data, formula, include_weights = FALSE, depend, yeo_johnson, reflect) { - +estimate_gamma_glmm <- function(data, formula, include_weights = FALSE, depend, yeo_johnson, reflect) { + # Sort out dependent type (factor) data <- replace.missing(data) #data <- drop_na(data) if (reflect) { max_value <- nanmax(data[[depend]]) - data[, c(depend)] <- max_value - data[, c(depend)] + data[, c(depend)] <- max_value - data[, c(depend)] } if (yeo_johnson) { @@ -224,21 +225,21 @@ estimate_longitudinal_glmm <- function(data, formula, include_weights = FALSE, d min_value <- nanmin(data[[depend]]) data[[depend]] <- data[[depend]] - min_value + 0.001 - + if(include_weights) { - model <- glmer(formula, + model <- glmer(formula, nAGQ=0, # fast but inaccurate optimiser. nAGQ=1 takes forever.. family=Gamma(link='log'), # gamma family with log link. - weights=weight, + weights=weight, data = data) } else { - model <- glmer(formula, - nAGQ=0, + model <- glmer(formula, + nAGQ=0, family=Gamma(link='log'), data = data) } attr(model,"min_value") <- min_value - + if (yeo_johnson){ attr(model,"transform") <- yj # This is an unstable hack to add attributes to S4 class R objects. } @@ -248,7 +249,7 @@ estimate_longitudinal_glmm <- function(data, formula, include_weights = FALSE, d return(model) } -estimate_longitudinal_mlogit_gee <- function(data, formula, include_weights=FALSE, depend) +estimate_longitudinal_mlogit_gee <- function(data, formula, include_weights=FALSE, depend) { #data[[depend]] <- as.factor(data[[depend]]) data <- replace.missing(data) @@ -273,7 +274,7 @@ estimate_longitudinal_mlogit_gee <- function(data, formula, include_weights=FALS return (model) } -estimate_longitudinal_clmm <- function(data, formula, depend) +estimate_longitudinal_clmm <- function(data, formula, depend) { data <- replace.missing(data) data <- drop_na(data) @@ -281,8 +282,51 @@ estimate_longitudinal_clmm <- function(data, formula, depend) model <- clmm2(formula, random=factor(pidp), link='probit', # logistic link function (can use probit or cloglog as well.) - data = tail(data, 1000), # get seg fault if using too many rows :(. clip to most recent data. + data = tail(data, 1000), # get seg fault if using too many rows :(. clip to most recent data. threshold="flexible", nAGQ=1) # negative int values for nAGQ gives fast but sloppy prediction. (see ?clmm2) return (model) -} \ No newline at end of file +} + +estimate_beta_glmm <- function(data, formula, depend, reflect, yeo_johnson) { + + data <- replace.missing(data) + data <- drop_na(data) + + if (reflect) { + max_value <- nanmax(data[[depend]]) + data[, c(depend)] <- max_value - data[, c(depend)] + } + if (yeo_johnson) + { + yj <- yeojohnson(data[,c(depend)]) + data[, c(depend)] <- predict(yj) + } + + min_value <- nanmin(data[[depend]]) + #data[[depend]] <- data[[depend]] - min_value + 0.001 + #data[[depend]][data[[depend]] == 1] = 0.999 + #data[[depend]][data[[depend]] == 0] = 0.001 + + print(paste0('Some data summary stats for beta GLMM ', depend)) + print(paste0('Min value of dependent variable: ', min_value)) + print(paste0('Max value of dependent variable: ', max(data[[depend]]))) + + model <- glmmTMB(formula, + data = data, + family = beta_family(link = 'logit'), + weights = weight, + na.action = na.omit, + verbose = TRUE) + + attr(model,"min_value") <- min_value + + if (yeo_johnson){ + attr(model,"transform") <- yj # This is an unstable hack to add attributes to S4 class R objects. + } + if (reflect) { + attr(model,"max_value") <- max_value # Works though. + } + + return (model) +} diff --git a/minos/utils_validation_vis.R b/minos/utils_validation_vis.R index 85bee272..e7033308 100644 --- a/minos/utils_validation_vis.R +++ b/minos/utils_validation_vis.R @@ -21,32 +21,32 @@ validation_prep_ordinal <- function(raw.dat, base.dat, var) { group_by(time, .data[[var]]) %>% count() %>% mutate(source = 'US') - + base <- base.dat %>% dplyr::select(pidp, time, all_of(var)) %>% group_by(time, .data[[var]]) %>% count() %>% mutate(source = 'baseline_output') - + combined <- rbind(raw, base) combined[[var]] <- as.factor(combined[[var]]) - + var.norm <- combined %>% group_by(time) %>% filter(.data[[var]] != -9) %>% mutate(total = sum(n)) %>% mutate(prct = (n / total)) - + prepped <- list(var = combined, norm = var.norm) - + return(prepped) } ############ PLOTTING ############ handover_continuous <- function(raw.dat, base.dat, var, save = FALSE) { - raw.var <- raw.dat %>% + raw.var <- raw.dat %>% dplyr::select(pidp, time, all_of(var), weight) %>% filter(time != 2009) %>% filter(!.data[[var]] %in% miss.values) %>% @@ -56,7 +56,7 @@ handover_continuous <- function(raw.dat, base.dat, var, save = FALSE) { w = weight, na.rm=TRUE)) %>% mutate(source = 'final_US') - + base.var <- base.dat %>% dplyr::select(pidp, time, all_of(var), weight) %>% filter(time != 2009) %>% @@ -67,10 +67,10 @@ handover_continuous <- function(raw.dat, base.dat, var, save = FALSE) { w = weight, na.rm=TRUE)) %>% mutate(source = 'baseline_output') - + # merge before plot merged <- rbind(raw.var, base.var) - + # Now plot p1 <- ggplot(data = merged, mapping = aes(x = time, y = mean, group = source, colour = source)) + geom_line() + @@ -78,23 +78,23 @@ handover_continuous <- function(raw.dat, base.dat, var, save = FALSE) { labs(title = var, subtitle = 'Full Sample') + xlab('Year') + ylab(var) - - ## Try a version where final_US is limited to only those present from wave 1 + + ## Try a version where final_US is limited to only those present from wave 1 # onwards because the sample refreshments are messing with the plot raw.wave1 <- raw.dat$pidp[raw.dat$time == 2009] - - raw.var2 <- raw.dat %>% + + raw.var2 <- raw.dat %>% dplyr::select(pidp, time, all_of(var), weight) %>% filter(pidp %in% raw.wave1) %>% filter(time != 2009) %>% filter(!.data[[var]] %in% miss.values) %>% drop_na() %>% group_by(time) %>% - summarise(mean = weighted.mean(x = .data[[var]], + summarise(mean = weighted.mean(x = .data[[var]], w = weight, na.rm = TRUE)) %>% mutate(source = 'final_US') - + base.var2 <- base.dat %>% dplyr::select(pidp, time, all_of(var), weight) %>% filter(pidp %in% raw.wave1) %>% @@ -102,13 +102,13 @@ handover_continuous <- function(raw.dat, base.dat, var, save = FALSE) { filter(!.data[[var]] %in% miss.values) %>% drop_na() %>% group_by(time) %>% - summarise(mean = weighted.mean(x = .data[[var]], + summarise(mean = weighted.mean(x = .data[[var]], w = weight, na.rm = TRUE)) %>% mutate(source = 'baseline_output') - + merged2 <- rbind(raw.var2, base.var2) - + # Now plot p2 <- ggplot(data = merged2, mapping = aes(x = time, y = mean, group = source, colour = source)) + geom_line() + @@ -116,7 +116,7 @@ handover_continuous <- function(raw.dat, base.dat, var, save = FALSE) { labs(title = var, subtitle = 'Wave 1 Sample') + xlab('Year') + ylab(var) - + if (save) { ggsave(filename = paste0(var, '.png'), plot = p1, @@ -127,23 +127,23 @@ handover_continuous <- function(raw.dat, base.dat, var, save = FALSE) { } handover_ordinal <- function(raw.dat, base.dat, var, save=FALSE) { - raw.var <- raw.dat %>% + raw.var <- raw.dat %>% dplyr::select(pidp, time, all_of(var)) %>% filter(.data[[var]] > 0) %>% group_by(time, .data[[var]]) %>% count() %>% mutate(source = 'final_US') - + base.var <- base.dat %>% dplyr::select(pidp, time, all_of(var)) %>% filter(.data[[var]] > 0) %>% group_by(time, .data[[var]]) %>% count() %>% mutate(source = 'baseline_output') - + merged <- rbind(raw.var, base.var) merged[[var]] <- as.factor(merged[[var]]) - + p1 <- ggplot(data = merged, mapping = aes(x = time, y = n, group = .data[[var]], colour = .data[[var]])) + geom_line() + geom_point() + @@ -151,19 +151,19 @@ handover_ordinal <- function(raw.dat, base.dat, var, save=FALSE) { labs(title = var, subtitle = 'Counts by Level') + xlab('Year') + ylab('Count') - + var.norm <- merged %>% group_by(time) %>% mutate(total = sum(n)) %>% mutate(prct = (n / total)) - + p2 <- ggplot(data = var.norm, mapping = aes(x = time, y = prct, fill=.data[[var]])) + geom_bar(stat = 'identity') + geom_vline(xintercept=start.year, linetype='dotted') + labs(title = var) + xlab('Year') + ylab('Proportion') - + if (save) { ggsave(filename = paste0(var, '.png',), plot = last_plot(), @@ -177,29 +177,29 @@ spaghetti_plot <- function(data, v, save=FALSE, save.path=NULL, filename.tag=NUL { # spaghetti plot displaying trajectories over time for continuous variable v # data: list Some dataset to plot. Needs v, time and pidp variables. - # v : some continuous variable to plot. + # v : some continuous variable to plot. # save : whether to save the plot # save.path : path to save plot at, must be defined if save == TRUE - - #TODO convert this to pure ggplot2 as with joint spaghetti plot below. Far more flexible and doesnt need stupid wide format conversion. + + #TODO convert this to pure ggplot2 as with joint spaghetti plot below. Far more flexible and doesnt need stupid wide format conversion. data_plot <- data[, c("time", "pidp", v)] # Remove missing values data_plot <- data_plot %>% filter(!.data[[v]] %in% miss.values) - + # get range of years to figure out if this is handover or not if (min(data_plot$time) < 2020) { handover <- TRUE } - - output_plot <- ggplot(data_plot, aes(x = time, y = !!sym(v), group = pidp)) + - ggplot2::labs(x = "time", y = v) + - ggplot2::theme_classic() + - ggplot2::theme(text = ggplot2::element_text(size = 12)) + + + output_plot <- ggplot(data_plot, aes(x = time, y = !!sym(v), group = pidp)) + + ggplot2::labs(x = "time", y = v) + + ggplot2::theme_classic() + + ggplot2::theme(text = ggplot2::element_text(size = 12)) + ggplot2::geom_line(colour="blue", alpha=0.2) + ggplot2::geom_point() + gghighlight::gghighlight(max(!!sym(v)) > 20) - + if (save) { if(is.null(save.path)) { stop('ERROR: save.path must be defined when saving the plot') @@ -214,7 +214,7 @@ spaghetti_plot <- function(data, v, save=FALSE, save.path=NULL, filename.tag=NUL if (!is.null(filename.tag)) { save.filename <- paste0(filename.tag, '_', save.filename) } - + ggsave(filename = save.filename, plot = output_plot, path = save.path) @@ -226,29 +226,29 @@ spaghetti_highlight_max_plot <- function(data, v, save=FALSE, save.path=NULL, fi { # spaghetti plot displaying trajectories over time for continuous variable v # data: list Some dataset to plot. Needs v, time and pidp variables. - # v : some continuous variable to plot. + # v : some continuous variable to plot. # save : whether to save the plot # save.path : path to save plot at, must be defined if save == TRUE - - #TODO convert this to pure ggplot2 as with joint spaghetti plot below. Far more flexible and doesnt need stupid wide format conversion. + + #TODO convert this to pure ggplot2 as with joint spaghetti plot below. Far more flexible and doesnt need stupid wide format conversion. data_plot <- data[, c("time", "pidp", v)] # Remove missing values data_plot <- data_plot %>% filter(!.data[[v]] %in% miss.values) - + # get range of years to figure out if this is handover or not if (min(data_plot$time) < 2020) { handover <- TRUE } - - output_plot <- ggplot(data_plot, aes(x = time, y = !!sym(v), color=factor(pidp), group = pidp)) + - ggplot2::labs(x = "time", y = v) + - ggplot2::theme_classic() + - ggplot2::theme(text = ggplot2::element_text(size = 12)) + + + output_plot <- ggplot(data_plot, aes(x = time, y = !!sym(v), color=factor(pidp), group = pidp)) + + ggplot2::labs(x = "time", y = v) + + ggplot2::theme_classic() + + ggplot2::theme(text = ggplot2::element_text(size = 12)) + ggplot2::geom_line() + ggplot2::geom_point() + gghighlight::gghighlight(min(!!sym(v)) < 5, (pidp %% 1000) == 13, use_direct_label=FALSE, max_highlight=10) - + if (save) { if(is.null(save.path)) { stop('ERROR: save.path must be defined when saving the plot') @@ -263,7 +263,7 @@ spaghetti_highlight_max_plot <- function(data, v, save=FALSE, save.path=NULL, fi if (!is.null(filename.tag)) { save.filename <- paste0(filename.tag, '_', save.filename) } - + ggsave(filename = save.filename, plot = output_plot, path = save.path) @@ -275,17 +275,17 @@ violin_plot <- function(data, v) { # plot violins (similar to boxplots) over time for continuous variable v # data: list Some dataset to plot. Needs v, time and pidp variables. - # v : some continuous variable to plot. + # v : some continuous variable to plot. data <- data[, c("time", "pidp", v)] data <- data[order(data$pidp, data$time),] data$time <- factor(data$time) data <- data %>% filter(!.data[[v]] %in% miss.values) - + violin_long <- ggplot(data, aes_string(x = 'time', y = v)) + geom_violin() + geom_boxplot(width = 0.1, outlier.colour = "blue") + - theme_classic() + + theme_classic() + scale_y_continuous(limits = quantile(data[, c(v)], c(0.001, 0.999), na.rm =TRUE)) return(violin_long) } @@ -299,7 +299,7 @@ density_ridges <- function(data, v, save=FALSE, save.path=NULL, filename.tag=NUL if (min(data_plot$time) < 2020) { handover <- TRUE } - + data_plot$time <- factor(data_plot$time) data_plot <- data_plot[order(data_plot$time),] output_plot <- ggplot(data_plot, aes(x=!!sym(v), y=time)) + @@ -307,7 +307,7 @@ density_ridges <- function(data, v, save=FALSE, save.path=NULL, filename.tag=NUL #scale_color_viridis_d() + scale_color_cyclical(values=c("#F8766D", "#00BA38","#619CFF")) + scale_linetype_cyclical(values=c(1, 2, 3)) - + if(save) { if(is.null(save.path)) { stop('ERROR: save.path must be defined when saving the plot') @@ -322,15 +322,15 @@ density_ridges <- function(data, v, save=FALSE, save.path=NULL, filename.tag=NUL if (!is.null(filename.tag)) { save.filename <- paste0(filename.tag, '_', save.filename) } - + ggsave(filename = save.filename, plot = output_plot, path = save.path) } return(output_plot) } -# -marg_dist_densigram_plot_oneyear <- function(observed, +# +marg_dist_densigram_plot_oneyear <- function(observed, predicted, var, target.year = 2020, @@ -342,10 +342,10 @@ marg_dist_densigram_plot_oneyear <- function(observed, # get just the SF12 columns and rename for later o.end <- o.end %>% select(pidp, all_of(var)) %>% rename(observed = var) p.end <- p.end %>% select(pidp, all_of(var)) %>% rename(predicted = var) - + # combine before we plot combined <- merge(o.end, p.end, by = 'pidp') - + if (var %in% c('hh_income', 'equivalent_income')) { # do inverse hyperbolic sine transformation for hh_income asinh_trans <- scales::trans_new( @@ -353,7 +353,7 @@ marg_dist_densigram_plot_oneyear <- function(observed, transform = function(x) {asinh(x)}, inverse = function(x) {sinh(x)} ) - + p <- ggplot(data = combined, aes(x = observed, y = predicted)) + geom_point(alpha = 0.6, size=0.1) + #geom_smooth() + @@ -365,7 +365,7 @@ marg_dist_densigram_plot_oneyear <- function(observed, theme(legend.position = c(0.15, 0.9)) + labs(title = paste0(var, ' - ', target.year), subtitle = 'Marginal Distributions - Inverse Hyperbolic Sine transformation') + - coord_fixed() # force equal sized axes for easier interpretation. + coord_fixed() # force equal sized axes for easier interpretation. } else { # no transformation for other vars p <- ggplot(data = combined, aes(x = observed, y = predicted)) + @@ -377,11 +377,11 @@ marg_dist_densigram_plot_oneyear <- function(observed, labs(title = paste0(var, ' - ', target.year), subtitle = 'Marginal Distributions') } - + p1 <- ggMarginal(p, type='densigram', xparams = list(position = 'dodge'), yparams=list(position = 'dodge')) - + plot.name <- paste0('scatter_marg_densigram_', var, '_', target.year, '.png') - + if(save) { ggsave(filename = plot.name, plot = p1, @@ -421,46 +421,46 @@ cv.mean.plots <- function(cv1, cv2, cv3, cv4, cv5, raw, var) { summarise(mean.var = mean(.data[[var]], na.rm = TRUE)) cv5.inc$source <- 'cv5' cv5.inc$mode <- 'cross-validation' - + raw.inc <- raw %>% filter(.data[[var]] != -9) %>% group_by(time) %>% summarise(mean.var = mean(.data[[var]], na.rm = TRUE)) raw.inc$source <- 'raw' raw.inc$mode <- 'raw' - + cv.list <- list(cv1.inc, cv2.inc, cv3.inc, cv4.inc, cv5.inc) cv.inc <- do.call(rbind, cv.list) - + df.list <- list(cv.inc, raw.inc) combined <- do.call(rbind, df.list) - + # First plot all cv runs separately p1 <- ggplot(combined, aes(x = time, y = mean.var, group = source, color = source, linetype = mode)) + geom_line() + labs(title = paste0(var, ': CV vs raw'), subtitle = 'Individual CV runs') + ylab(var) print(p1) - + cv.inc2 <- cv.inc %>% group_by(time) %>% summarise(var = mean(mean.var), min = min(mean.var), max = max(mean.var)) cv.inc2$source <- 'cross-validation' - + raw.inc <- raw.inc %>% select(-mode) %>% mutate(var = mean.var, min = mean.var, max = mean.var) %>% select(-mean.var) - + combined2 <- rbind(cv.inc2, raw.inc) - + p2 <- ggplot(combined2, aes(x = time, y = var, group = source, color = source)) + geom_line() + - geom_ribbon(aes(ymin = min, ymax = max, fill = source), + geom_ribbon(aes(ymin = min, ymax = max, fill = source), alpha = 0.1, linetype = 'dashed') + labs(title = paste0(var, ': CV vs raw'), subtitle = 'Combined CV runs') + @@ -471,24 +471,26 @@ cv.mean.plots <- function(cv1, cv2, cv3, cv4, cv5, raw, var) { snapshot_OP_plots <- function(raw, cv, var, target.years) { cv.snap <- cv %>% select(pidp, time, all_of(var)) %>% + dplyr::filter(!.data[[var]] %in% miss.values) %>% filter(time %in% target.years) %>% rename(predicted = .data[[var]]) - + raw.snap <- raw %>% select(pidp, time, all_of(var)) %>% + dplyr::filter(!.data[[var]] %in% miss.values) %>% filter(time %in% target.years) %>% rename(observed = .data[[var]]) - + snap <- merge(cv.snap, raw.snap, by = c('pidp', 'time')) - + if (var %in% c('hh_income', 'equivalent_income')) { asinh_trans <- scales::trans_new( "inverse_hyperbolic_sine", transform = function(x) {asinh(x)}, inverse = function(x) {sinh(x)} ) - + ggplot(snap, aes(x = observed, y = predicted)) + geom_point() + scale_y_continuous(trans=asinh_trans) + @@ -512,25 +514,27 @@ snapshot_OP_plots <- function(raw, cv, var, target.years) { ## Yearly box plots for multiple years, compared between raw and cv runs multi_year_boxplots <- function(raw, cv, var) { raw.var <- raw %>% - dplyr::select(pidp, time, all_of(var)) + dplyr::select(pidp, time, all_of(var)) %>% + dplyr::filter(!.data[[var]] %in% miss.values) raw.var$source <- 'raw' - + cv.var <- cv %>% - dplyr::select(pidp, time, all_of(var)) + dplyr::select(pidp, time, all_of(var)) %>% + dplyr::filter(!.data[[var]] %in% miss.values) cv.var$source <- 'cross-validation' - + combined <- rbind(raw.var, cv.var) combined$time <- as.factor(combined$time) combined <- drop_na(combined) - combined <- filter(combined, .data[[var]] != -9) - + #combined <- filter(combined, .data[[var]] != -9) + if (var %in% c('hh_income', 'equivalent_income')) { combined <- filter(combined, .data[[var]] < quantile(.data[[var]], 0.99), .data[[var]] > quantile(.data[[var]], 0.01)) } else if (var == 'ncigs') { #combined <- filter(combined, .data[[var]] < quantile(.data[[var]], 0.99)) combined <- filter(combined, .data[[var]] < quantile(.data[[var]], 0.99), !.data[[var]] == 0) } - + ggplot(data = combined, aes(x = time, y = .data[[var]], group = interaction(time, source), color = source)) + geom_boxplot(notch=TRUE) + labs(title = paste0(var, ': Yearly box plots')) @@ -538,20 +542,22 @@ multi_year_boxplots <- function(raw, cv, var) { q_q_comparison <- function(raw, cv, var) { raw.inc <- raw %>% - select(pidp, time, any_of(var)) + select(pidp, time, any_of(var)) %>% + dplyr::filter(!.data[[var]] %in% miss.values) raw.inc$source <- 'raw' cv.inc <- cv %>% - select(pidp, time, any_of(var)) + select(pidp, time, any_of(var)) %>% + dplyr::filter(!.data[[var]] %in% miss.values) cv.inc$source <- 'cross-validation' - + combined <- rbind(raw.inc, cv.inc) combined <- filter(combined, .data[[var]] != -9) - + if (var == 'ncigs') { print('Removing ncigs == 0 values for this plot') combined <- filter(combined, .data[[var]] != 0) } - + ggplot(combined, aes(sample = .data[[var]], group = source, color = source)) + stat_qq() + labs(title = paste0(var, ': Q-Q')) @@ -564,25 +570,27 @@ q_q_comparison <- function(raw, cv, var) { handover_boxplots <- function(raw, baseline, var) { raw.var <- raw %>% - dplyr::select(pidp, time, all_of(var)) + dplyr::select(pidp, time, all_of(var)) %>% + dplyr::filter(!.data[[var]] %in% miss.values) raw.var$source <- 'final_US' - + baseline.var <- baseline %>% - dplyr::select(pidp, time, all_of(var)) + dplyr::select(pidp, time, all_of(var)) %>% + dplyr::filter(!.data[[var]] %in% miss.values) baseline.var$source <- 'baseline_output' - + combined <- rbind(raw.var, baseline.var) combined$time <- as.factor(combined$time) combined <- drop_na(combined) - combined <- filter(combined, .data[[var]] != -9) - + #combined <- filter(combined, .data[[var]] != -9) + if (var %in% c('hh_income', 'equivalent_income')) { combined <- filter(combined, .data[[var]] < quantile(.data[[var]], 0.99), .data[[var]] > quantile(.data[[var]], 0.01)) } else if (var == 'ncigs') { #combined <- filter(combined, .data[[var]] < quantile(.data[[var]], 0.99)) combined <- filter(combined, .data[[var]] < quantile(.data[[var]], 0.99), !.data[[var]] == 0) } - + ggplot(data = combined, aes(x = time, y = .data[[var]], group = interaction(time, source), fill= source)) + geom_boxplot(notch=TRUE) + labs(title = paste0(var, ': Yearly box plots')) @@ -591,21 +599,23 @@ handover_boxplots <- function(raw, baseline, var) { # summarise(summary_var = weighted.mean(x = .data[[var]], w = weight)) %>% handover_lineplots <- function(raw, base, var) { # GENERALISE THIS AND DOCSTRING - raw.means <- raw %>% + raw.means <- raw %>% dplyr::select(pidp, time, var) %>% + dplyr::filter(!.data[[var]] %in% miss.values) %>% group_by(time) %>% summarise(summary_var = mean(.data[[var]], na.rm = TRUE)) %>% mutate(source = 'final_US') - + base.means <- base %>% dplyr::select(pidp, time, var) %>% + dplyr::filter(!.data[[var]] %in% miss.values) %>% group_by(time) %>% summarise(summary_var = mean(!!sym(var))) %>% mutate(source = 'baseline_output') - + # merge before plot combined <- rbind(raw.means, base.means) - + # Now plot ggplot(data = combined, aes(x = time, y = summary_var, group = source, color = source)) + geom_line() + @@ -613,4 +623,4 @@ handover_lineplots <- function(raw, base, var) { labs(title = var, subtitle = 'Full Sample') + xlab('Year') + ylab(var) -} \ No newline at end of file +} From b6d1cbd64ed8d638d3fa3c42707da1b9e6cfbc6f Mon Sep 17 00:00:00 2001 From: ld-archer Date: Thu, 5 Oct 2023 23:50:21 +0100 Subject: [PATCH 041/229] Changed matdep proxy to a 3 level ordinal and fit a CLM model to it. Handovers look good --- .../generate_composite_vars.py | 12 +- minos/modules/material_deprivation.py | 109 ++++++++++++------ minos/transitions/estimate_transitions.R | 4 + .../transitions/model_definitions_default.txt | 2 +- minos/utils_validation_vis.R | 4 +- minos/validation/handovers.Rmd | 107 ++++++++++++++--- 6 files changed, 176 insertions(+), 62 deletions(-) diff --git a/minos/data_generation/generate_composite_vars.py b/minos/data_generation/generate_composite_vars.py index 20e2dc3a..cdc89bbf 100755 --- a/minos/data_generation/generate_composite_vars.py +++ b/minos/data_generation/generate_composite_vars.py @@ -1132,12 +1132,18 @@ def generate_matdep_proxy(data): data['counter'][data['matdepj'].isin([1, 2])] += 1 # Now use the counter value and matdepscore to generate a score between 0 and 1 - data['matdep'] = data['matdepscore'] / data['counter'] - data['matdep'][data['counter'] == 0] = -9 + data['matdepTEMP'] = data['matdepscore'] / data['counter'] + data['matdepTEMP'][data['counter'] == 0] = -9 + + # now convert to 3 level ordinal + data['matdep'] = -9 + data['matdep'][data['matdepTEMP'] == 0] = 1 + data['matdep'][(data['matdepTEMP'] > 0) & (data['matdepTEMP'] < 1)] = 2 + data['matdep'][data['matdepTEMP'] == 1] = 3 # drop cols no longer need data.drop(labels=['matdepa', 'matdepd', 'matdepe', 'matdepf', 'matdepg', 'matdeph', 'matdepi', 'matdepj', - 'matdepscore', 'counter'], + 'matdepscore', 'counter', 'matdepTEMP'], axis=1, inplace=True) diff --git a/minos/modules/material_deprivation.py b/minos/modules/material_deprivation.py index 0d7612e0..bc47edd7 100644 --- a/minos/modules/material_deprivation.py +++ b/minos/modules/material_deprivation.py @@ -67,7 +67,6 @@ def setup(self, builder): 'urban', 'financial_situation', 'matdep', - 'matdep_diff', 'weight'] self.population_view = builder.population.get_view(columns=view_columns) @@ -81,9 +80,13 @@ def setup(self, builder): # individual graduate in an education module. builder.event.register_listener("time_step", self.on_time_step, priority=5) + + #only need to load this once for now. #self.gee_transition_model = r_utils.load_transitions(f"SF_12/lmm/SF_12_LMM", self.rpy2_modules, path=self.transition_dir) - self.gee_transition_model = r_utils.load_transitions(f"matdep/glmmb/matdep_GLMMB", self.rpy2_modules, path=self.transition_dir) + #self.gee_transition_model = r_utils.load_transitions(f"matdep/glmmb/matdep_GLMMB", self.rpy2_modules, path=self.transition_dir) + # self.gee_transition_model = r_utils.load_transitions(f"matdep/clm/matdep_2018-2019", self.rpy2_modules, + # path=self.transition_dir) def on_time_step(self, event): """Produces new children and updates parent status on time steps. @@ -93,33 +96,53 @@ def on_time_step(self, event): The event time_step that called this function. """ + # self.year = event.time.year + # # Get living people to update their income + # pop = self.population_view.get(event.index, query="alive =='alive'") + # pop = pop.sort_values('pidp') #sorting aligns index to make sure individual gets their correct prediction. + # pop["matdep_last"] = pop["matdep"] + # + # # Predict next mwb value + # newWaveMatdep = pd.DataFrame(columns=['matdep']) + # newWaveMatdep['matdep'] = self.calculate_matdep(pop) + # newWaveMatdep.index = pop.index + # #newWaveMatdep["matdep"] -= 1 + # + # sf12_mean = np.mean(newWaveMatdep["matdep"]) + # std_ratio = (11/np.std(newWaveMatdep["matdep"])) + # newWaveMatdep["matdep"] *= (11/np.std(newWaveMatdep["matdep"])) + # newWaveMatdep["matdep"] -= ((std_ratio-1)*sf12_mean) + # newWaveMatdep["matdep"] -= 1.5 + # #newWaveMatdep["matdep"] += (50 - np.mean(newWaveMatdep["SF_12_MCS"])) + # newWaveMatdep["matdep"] = np.clip(newWaveMatdep["matdep"], 0, 1) # keep within [0, 100] bounds of SF12. + # newWaveMatdep["matdep_diff"] = newWaveMatdep["matdep"] - pop["matdep"] + # # Update population with new SF12_MCS + # #print(np.mean(newWaveMatdep["matdep"])) + # #print(np.std(newWaveMatdep["matdep"])) + # self.population_view.update(newWaveMatdep[['matdep', "matdep_diff"]]) + + pop = self.population_view.get(event.index, query="alive=='alive'") self.year = event.time.year - # Get living people to update their income - pop = self.population_view.get(event.index, query="alive =='alive'") - pop = pop.sort_values('pidp') #sorting aligns index to make sure individual gets their correct prediction. - pop["matdep_last"] = pop["matdep"] - - # Predict next mwb value - newWaveMatdep = pd.DataFrame(columns=['matdep']) - newWaveMatdep['matdep'] = self.calculate_mwb(pop) - newWaveMatdep.index = pop.index - #newWaveMatdep["matdep"] -= 1 - - sf12_mean = np.mean(newWaveMatdep["matdep"]) - std_ratio = (11/np.std(newWaveMatdep["matdep"])) - newWaveMatdep["matdep"] *= (11/np.std(newWaveMatdep["matdep"])) - newWaveMatdep["matdep"] -= ((std_ratio-1)*sf12_mean) - newWaveMatdep["matdep"] -= 1.5 - #newWaveMatdep["matdep"] += (50 - np.mean(newWaveMatdep["SF_12_MCS"])) - newWaveMatdep["matdep"] = np.clip(newWaveMatdep["matdep"], 0, 1) # keep within [0, 100] bounds of SF12. - newWaveMatdep["matdep_diff"] = newWaveMatdep["matdep"] - pop["matdep"] - # Update population with new SF12_MCS - #print(np.mean(newWaveMatdep["matdep"])) - #print(np.std(newWaveMatdep["matdep"])) - self.population_view.update(newWaveMatdep[['matdep', "matdep_diff"]]) - - - def calculate_mwb(self, pop): + + matdep_prob_df = self.calculate_matdep(pop) + + matdep_prob_df["matdep"] = self.random.choice(matdep_prob_df.index, + list(matdep_prob_df.columns), + matdep_prob_df) + 1 + + # housing_prob_df.index = pop.index + # + # # convert numeric prediction into string factors (low, medium, high) + # housing_factor_dict = {1: 'Low', + # 2: 'Medium', + # 3: 'High'} + # housing_prob_df.replace({'matdep': housing_factor_dict}, + # inplace=True) + + self.population_view.update(matdep_prob_df["matdep"]) + + + def calculate_matdep(self, pop): """Calculate SF_12 transition distribution based on provided people/indices Parameters ---------- @@ -128,12 +151,24 @@ def calculate_mwb(self, pop): Returns ------- """ - out_data = r_utils.predict_next_timestep_yj_gamma_glmm(self.gee_transition_model, - self.rpy2_modules, - current= pop, - dependent='matdep', - reflect=False, - yeo_johnson= False, - mod_type='beta', - noise_std= 5) # 5 for non yj, 0.35 for yj - return out_data \ No newline at end of file + # out_data = r_utils.predict_next_timestep_yj_gamma_glmm(self.gee_transition_model, + # self.rpy2_modules, + # current= pop, + # dependent='matdep', + # reflect=False, + # yeo_johnson= False, + # mod_type='beta', + # noise_std= 0.1) # 5 for non yj, 0.35 for yj + # return out_data + + # load transition model based on year. + if self.cross_validation: + # if cross-val, fix year to final year model + year = 2019 + else: + year = min(self.year, 2019) + + transition_model = r_utils.load_transitions(f"matdep/clm/matdep_{year}_{year + 1}", self.rpy2_modules, path=self.transition_dir) + # returns probability matrix (3xn) of next ordinal state. + prob_df = r_utils.predict_next_timestep_clm(transition_model, self.rpy2_modules, pop, 'matdep') + return prob_df diff --git a/minos/transitions/estimate_transitions.R b/minos/transitions/estimate_transitions.R index 99ba52d7..b428333f 100644 --- a/minos/transitions/estimate_transitions.R +++ b/minos/transitions/estimate_transitions.R @@ -174,6 +174,7 @@ run_yearly_models <- function(transitionDir_path, if(dependent %in% c('SF_12_MCS', 'SF_12_PCS') & year == 2009) { next } # OLS_DIFF models can only start from wave 2 (no diff in first wave) if(tolower(mod.type) == 'ols_diff' & year == 2009) { next } + if(dependent %in% c('matdep') & year %in% c(2009, 2010, 2012, 2014, 2016, 2018)) { next } print(paste0('Starting estimation for ', dependent, ' in ', year)) @@ -225,6 +226,9 @@ run_yearly_models <- function(transitionDir_path, if(!year %in% c(2015, 2017, 2019, 2020)) { formula.string <- str_remove(formula.string, " \\+ factor\\(active\\)") } + if(!year %in% c(2009, 2010, 2012, 2014, 2016, 2018, 2020)) { + formula.string <- str_remove(formula.string, " \\+ factor\\(matdep\\)") + } } #print(formula.string) # Now make string into formula diff --git a/minos/transitions/model_definitions_default.txt b/minos/transitions/model_definitions_default.txt index 23485996..0d35a8fd 100644 --- a/minos/transitions/model_definitions_default.txt +++ b/minos/transitions/model_definitions_default.txt @@ -10,7 +10,7 @@ NNET : housing_tenure ~ age + factor(sex) + factor(housing_tenure) + relevel(fac CLM : loneliness ~ factor(loneliness) + scale(age) + factor(sex) + scale(SF_12_MCS) + scale(SF_12_PCS) + relevel(factor(education_state), ref = '3') + relevel(factor(job_sec), ref = '3') + scale(hh_income) + relevel(factor(hh_comp), ref = '3') + relevel(factor(marital_status), ref = 'Partnered') + relevel(factor(ethnicity), ref = 'WBI') CLM : neighbourhood_safety ~ scale(age) + factor(sex) + factor(job_sec) + relevel(factor(ethnicity), ref = 'WBI') + scale(hh_income) + factor(housing_quality) + relevel(factor(region), ref = 'South East') + factor(urban) NNET : education_state ~ factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') -GLMMB : matdep ~ scale(matdep) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(marital_status) + hhsize + factor(housing_tenure) + factor(urban) + factor(financial_situation) + (1|pidp) +CLM : matdep ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(marital_status) + hhsize + factor(housing_tenure) + factor(urban) + factor(financial_situation) ZIP : ncigs ~ ncigs + age + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + relevel(factor(job_sec), ref = '3') + scale(hh_income) + relevel(factor(ethnicity), ref = 'WBI') | I(factor(ncigs>0)) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(job_sec), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) GLMM : SF_12_PCS ~ scale(SF_12_PCS_last) + I(scale(SF_12_PCS_last)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + factor(housing_quality) + scale(hh_income) + factor(neighbourhood_safety) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(financial_situation) + factor(active) + factor(auditc) + (1|pidp) GLMM : SF_12_MCS ~ scale(SF_12_MCS_last) + I(scale(SF_12_MCS_last)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + factor(housing_quality) + scale(hh_income) + factor(neighbourhood_safety) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(financial_situation) + factor(active) + (1|pidp) diff --git a/minos/utils_validation_vis.R b/minos/utils_validation_vis.R index e7033308..67ddaa10 100644 --- a/minos/utils_validation_vis.R +++ b/minos/utils_validation_vis.R @@ -129,14 +129,14 @@ handover_continuous <- function(raw.dat, base.dat, var, save = FALSE) { handover_ordinal <- function(raw.dat, base.dat, var, save=FALSE) { raw.var <- raw.dat %>% dplyr::select(pidp, time, all_of(var)) %>% - filter(.data[[var]] > 0) %>% + filter(!.data[[var]] %in% miss.values) %>% group_by(time, .data[[var]]) %>% count() %>% mutate(source = 'final_US') base.var <- base.dat %>% dplyr::select(pidp, time, all_of(var)) %>% - filter(.data[[var]] > 0) %>% + filter(!.data[[var]] %in% miss.values) %>% group_by(time, .data[[var]]) %>% count() %>% mutate(source = 'baseline_output') diff --git a/minos/validation/handovers.Rmd b/minos/validation/handovers.Rmd index f80b9970..e4f826f1 100644 --- a/minos/validation/handovers.Rmd +++ b/minos/validation/handovers.Rmd @@ -250,28 +250,78 @@ TODO: Drop negative values ONLY from waves with no data i.e. not 7,9,11 # nutriton quality line/boxplots. handover_boxplots(raw.dat, base.dat, 'nutrition_quality') handover_lineplots(raw.dat, base.dat, 'nutrition_quality') + +test.pidps <- raw.dat %>% + select(pidp) + +test.base <- base.dat %>% + filter(pidp %in% test.pidps$pidp) + +handover_boxplots(raw.dat, test.base, 'nutrition_quality') +handover_lineplots(raw.dat, test.base, 'nutrition_quality') ``` ### Spaghetti ```{r} -# raw.nut <- raw.dat %>% -# select(pidp, age, time, nutrition_quality) -# base.nut <- base.dat %>% -# select(pidp, age, time, nutrition_quality) -# -# nut.spag <- rbind(raw.nut, base.nut) -# -# spaghetti_plot(nut.spag, 'nutrition_quality', -# save = shall.we.save, -# save.path = save.path) -# -# -# density_ridges(nut.spag, "nutrition_quality", -# save = T, -# save.path = save.path) -# -# rm(raw.nut, base.nut, nut.spag) +raw.nut <- raw.dat %>% + select(pidp, age, time, nutrition_quality) +base.nut <- base.dat %>% + select(pidp, age, time, nutrition_quality) + +nut.spag <- rbind(raw.nut, base.nut) + +spaghetti_plot(nut.spag, 'nutrition_quality', + save = shall.we.save, + save.path = save.path) + + +density_ridges(nut.spag, "nutrition_quality", + save = T, + save.path = save.path) + +rm(raw.nut, base.nut, nut.spag) +``` + +## Tobacco + +TODO: Drop negative values ONLY from waves with no data i.e. not 7,9,11 + +```{r} +# nutriton quality line/boxplots. +handover_boxplots(raw.dat, base.dat, 'ncigs') +handover_lineplots(raw.dat, base.dat, 'ncigs') + +test.pidps <- raw.dat %>% + select(pidp) + +test.base <- base.dat %>% + filter(pidp %in% test.pidps$pidp) + +handover_boxplots(raw.dat, test.base, 'ncigs') +handover_lineplots(raw.dat, test.base, 'ncigs') +``` + +### Spaghetti + +```{r} +raw.nut <- raw.dat %>% + select(pidp, age, time, ncigs) +base.nut <- base.dat %>% + select(pidp, age, time, ncigs) + +nut.spag <- rbind(raw.nut, base.nut) + +spaghetti_plot(nut.spag, 'ncigs', + save = shall.we.save, + save.path = save.path) + + +density_ridges(nut.spag, "ncigs", + save = FALSE, + save.path = save.path) + +rm(raw.nut, base.nut, nut.spag) ``` ## Physical Health @@ -427,7 +477,26 @@ handover_ordinal(raw.dat, base.dat, var = 'active', save = shall.we.save) ## Material Deprivation ```{r} -handover_boxplots(raw.dat, base.dat, 'matdep') -handover_lineplots(raw.dat, base.dat, 'matdep') +#handover_boxplots(raw.dat, base.dat, 'matdep') +#handover_lineplots(raw.dat, base.dat, 'matdep') +handover_ordinal(raw.dat, base.dat, var = 'matdep', save = shall.we.save) ``` +```{r} +# raw.s <- raw.dat %>% +# select(pidp, age, time, matdep) %>% +# filter(!matdep %in% miss.values) +# base.s <- base.dat %>% +# select(pidp, age, time, matdep) %>% +# filter(!matdep %in% miss.values) +# +# spag <- rbind(raw.s, base.s) +# +# spaghetti_plot(spag, 'matdep', +# save = shall.we.save, +# save.path = save.path) +# +# rm(raw.s, base.s, spag) +``` + + From 2193acbc90fd42092655b42cf0df80ec5b66e249 Mon Sep 17 00:00:00 2001 From: ld-archer Date: Fri, 6 Oct 2023 11:56:21 +0100 Subject: [PATCH 042/229] Fixed dependency for CV runs and updated CV vis script with material deprivation composite visualisations --- minos/transitions/Makefile | 2 +- minos/validation/cross_validation_default.Rmd | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/minos/transitions/Makefile b/minos/transitions/Makefile index 423e1b30..0815db30 100644 --- a/minos/transitions/Makefile +++ b/minos/transitions/Makefile @@ -18,7 +18,7 @@ transitions_SIPHER7: final_data $(TRANSITION_DATA)/S7_mental_health/clm/S7_menta scot_transitions: final_data $(TRANSITION_DATA)/scotland/hh_income/ols/hh_income_2018_2019.rds -cv_transitions: $(TRANSITION_DATA)/cross_validation/version5/ncigs/zip/ncigs_2018_2019.rds $(TRANSITION_DATA)/cross_validation/version5/SF_12/glmm/SF_12_GLMM.rds +cv_transitions: $(TRANSITION_DATA)/cross_validation/version5/ncigs/zip/ncigs_2018_2019.rds $(TRANSITION_DATA)/cross_validation/version5/SF_12_MCS/glmm/SF_12_MCS_GLMM.rds cv_S7_transitions: | $(TRANSITION_DATA) cv_S7_transitions: $(TRANSITION_DATA)/cross_validation/version5/S7_mental_health/clm/S7_mental_health_2019_2020.rds $(TRANSITION_DATA)/cross_validation/version5/hh_income/glmm/hh_income_new_GLMM.rds diff --git a/minos/validation/cross_validation_default.Rmd b/minos/validation/cross_validation_default.Rmd index 96b5a57c..91e514bc 100644 --- a/minos/validation/cross_validation_default.Rmd +++ b/minos/validation/cross_validation_default.Rmd @@ -341,6 +341,21 @@ cv_ordinal_plots(pivoted.df = active.pivoted, rm(active.pivoted) ``` +# Material Deprivation + +```{r} +matdep.pivoted <- combine_and_pivot_long(df1 = cv, + df1.name = 'simulated', + df2 = raw, + df2.name = 'raw', + var = 'matdep') + +cv_ordinal_plots(pivoted.df = matdep.pivoted, + var = 'matdep', + save = FALSE) +rm(matdep.pivoted) +``` + # SECONDARY VARS ## Marital Status From 0b091f22bde141bb59a83fbf5d4c3c5e8231ec89 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 9 Oct 2023 23:24:08 +0100 Subject: [PATCH 043/229] First stab at chronic disease module --- config/default.yaml | 2 +- minos/data_generation/US_complete_case.py | 4 + minos/data_generation/US_format_raw.py | 21 +++ minos/data_generation/US_missing_LOCF.py | 6 +- .../generate_composite_vars.py | 89 ++++++++++++- minos/data_generation/generate_repl_pop.py | 2 +- minos/minosPipeline/RunPipeline.py | 11 +- minos/modules/chron_disease.py | 122 ++++++++++++++++++ minos/modules/mental_wellbeing.py | 3 +- minos/modules/physical_wellbeing.py | 9 +- minos/modules/replenishment.py | 1 + minos/transitions/estimate_transitions.R | 50 +++---- .../transitions/model_definitions_default.txt | 5 +- minos/validation/handovers.Rmd | 36 +++++- 14 files changed, 323 insertions(+), 38 deletions(-) create mode 100644 minos/modules/chron_disease.py diff --git a/config/default.yaml b/config/default.yaml index 20cc24d3..8d3e3779 100755 --- a/config/default.yaml +++ b/config/default.yaml @@ -23,7 +23,7 @@ replenishing_dir: 'data/replenishing' cross_validation: FALSE # Correct ordering of components is automated in RunPipeline, so can be passed in any order here -components : [lmmYJMWB(), lmmYJPCS(), Loneliness(), Tobacco(), Alcohol(), PhysicalActivity(), Neighbourhood(), MaterialDeprivation(), Heating(), Housing(), HousingTenure(), lmmYJNutrition(), financialSituation(), lmmYJIncome(), Education(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] +components : [lmmYJMWB(), lmmYJPCS(), Loneliness(), Tobacco(), Alcohol(), PhysicalActivity(), Neighbourhood(), MaterialDeprivation(), ChronicDisease(), Heating(), Housing(), HousingTenure(), lmmYJNutrition(), financialSituation(), lmmYJIncome(), Education(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] scale_rates: method: "constant" diff --git a/minos/data_generation/US_complete_case.py b/minos/data_generation/US_complete_case.py index 3026fa48..57468299 100755 --- a/minos/data_generation/US_complete_case.py +++ b/minos/data_generation/US_complete_case.py @@ -102,6 +102,10 @@ def cut_outliers(df, lower, upper, var): data['S7_mental_health'] = data['S7_mental_health'].astype(int) data = complete_case_custom_years(data, 'S7_labour_state', years=list(range(2009, 2021, 1))) + # PCS complete case vars + data['chron_disease'] = data['chron_disease'].astype(int) + data = complete_case_custom_years(data, 'chron_disease', years=list(range(2011, 2021, 1))) + # PCS Vars # AUDITC (alcohol) - present in 2015, 2017, 2019, 2020 data = complete_case_custom_years(data, 'auditc', years=[2015, 2017, 2019, 2020]) diff --git a/minos/data_generation/US_format_raw.py b/minos/data_generation/US_format_raw.py index 98f75866..11d325f9 100755 --- a/minos/data_generation/US_format_raw.py +++ b/minos/data_generation/US_format_raw.py @@ -332,6 +332,27 @@ def format_ukhls_columns(year): 'matdeph': 'matdeph', # Material Deprivation: Replace or repair major electrical goods 'matdepi': 'matdepi', # Material Deprivation: Money for self 'matdepj': 'matdepj', # Material Deprivation: Keep up with bills + # Chronic Disease Vars + 'hcond1': 'hcond1', # Health Condition 1: Asthma + 'hcond2': 'hcond2', # Health Condition 2: Arthritis + 'hcond3': 'hcond3', # Health Condition 3: Congestive Heart Failure + 'hcond4': 'hcond4', # Health Condition 4: Coronary Heart Failure + 'hcond5': 'hcond5', # Health Condition 5: Angina + 'hcond6': 'hcond6', # Health Condition 6: Heart attack or myocardial infarction + 'hcond7': 'hcond7', # Health Condition 7: Stroke + 'hcond8': 'hcond8', # Health Condition 8: Emphysema + 'hcond10': 'hcond10', # Health Condition 10: Hypothyroidism + 'hcond11': 'hcond11', # Health Condition 11: Chronic Bronchitis + 'hcond12': 'hcond12', # Health Condition 12: Any kind of liver condition + 'hcond13': 'hcond13', # Health Condition 13: Cancer or malignancy + 'hcond14': 'hcond14', # Health Condition 14: Diabetes + 'hcond15': 'hcond15', # Health Condition 15: Epilepsy + 'hcond16': 'hcond16', # Health Condition 16: High blood pressure + 'hcond18': 'hcond18', # Health Condition 18: Other long standing/chronic condition + 'hcond19': 'hcond19', # Health Condition 19: Multiple Sclerosis + 'hcond20': 'hcond20', # Health Condition 20: H.I.V + 'hcond21': 'hcond21', # Health Condition 21: COPD + 'hcond96': 'hcond96', # Health Condition 96: None of these } # Some variables change names halfway through UKHLS. diff --git a/minos/data_generation/US_missing_LOCF.py b/minos/data_generation/US_missing_LOCF.py index 14db118c..23d38124 100755 --- a/minos/data_generation/US_missing_LOCF.py +++ b/minos/data_generation/US_missing_LOCF.py @@ -244,7 +244,11 @@ def main(data, save=False): # note columns can be forward and back filled for immutables like ethnicity. f_columns = ['education_state', 'labour_state_raw', 'job_sec', 'heating', 'ethnicity', 'sex', 'birth_year', 'yearly_gas', 'yearly_electric', 'yearly_gas_electric', 'yearly_oil', 'yearly_other_fuel', 'smoker', - 'nkids_ind_raw'] # 'ncigs', 'ndrinks'] + 'nkids_ind_raw', + 'hcond1', 'hcond2', 'hcond3', 'hcond4', 'hcond5', 'hcond6', 'hcond7', 'hcond8', 'hcond10', + 'hcond11', 'hcond12', 'hcond13', 'hcond14', 'hcond15', 'hcond16', 'hcond18', 'hcond19', 'hcond20', + 'hcond21', # ALL HCOND are health conditions - for the chronic conditions proxy var + ] # 'ncigs', 'ndrinks'] fb_columns = ["sex", "ethnicity", "birth_year"] # or here if they're immutable. mf_columns = ['education_state', 'nkids_ind_raw'] li_columns = ["age"] diff --git a/minos/data_generation/generate_composite_vars.py b/minos/data_generation/generate_composite_vars.py index cdc89bbf..366404cd 100755 --- a/minos/data_generation/generate_composite_vars.py +++ b/minos/data_generation/generate_composite_vars.py @@ -1150,6 +1150,92 @@ def generate_matdep_proxy(data): return data +def generate_chron_disease_proxy(data): + """ + For a first pass at a chronic disease proxy variable, we are going to focus on the distinction between no chronic + disease (CD), 1 CD, and more than 1. We think this will give us a fairly simple ordinal variable that has a strong + correlation with SF_12_PCS. + + NOTE: There is a variable (hcond96) for none of the above conditions. I'm going to ignore this and just rely on + the tally of CD's we create from each individual variable. + + Parameters + ---------- + data : pd.DataFrame + US data with the individual chronic disease variables. + + Returns + ------- + data : pd.DataFrame + US data with the chronic disease proxy, and component variables removed. + """ + + # first start the counter and increment with every non-missing positive response to each chronic condition + data['cd_counter'] = 0 + data['cd_counter'][data['hcond1'] == 1] += 1 # Asthma + data['cd_counter'][data['hcond2'] == 1] += 1 # Arthritis + data['cd_counter'][data['hcond3'] == 1] += 1 # Congestive Heart Failure + data['cd_counter'][data['hcond4'] == 1] += 1 # Coronary Heart Failure + data['cd_counter'][data['hcond5'] == 1] += 1 # Angina + data['cd_counter'][data['hcond6'] == 1] += 1 # Heart attack or myocardial infarction + data['cd_counter'][data['hcond7'] == 1] += 1 # Stroke + data['cd_counter'][data['hcond8'] == 1] += 1 # Emphysema + data['cd_counter'][data['hcond10'] == 1] += 1 # Hypothyroidism + data['cd_counter'][data['hcond11'] == 1] += 1 # Chronic Bronchitis + data['cd_counter'][data['hcond12'] == 1] += 1 # Any kind of liver condition + data['cd_counter'][data['hcond13'] == 1] += 1 # Cancer or malignancy + data['cd_counter'][data['hcond14'] == 1] += 1 # Diabetes + data['cd_counter'][data['hcond15'] == 1] += 1 # Epilepsy + data['cd_counter'][data['hcond16'] == 1] += 1 # High blood pressure + data['cd_counter'][data['hcond18'] == 1] += 1 # Other long standing/chronic condition + data['cd_counter'][data['hcond19'] == 1] += 1 # Multiple Sclerosis + data['cd_counter'][data['hcond20'] == 1] += 1 # H.I.V + data['cd_counter'][data['hcond21'] == 1] += 1 # COPD + + # next we need a missing counter to find cases where all vars are missing + data['cd_miss_counter'] = 0 + data['cd_miss_counter'][data['hcond1'] < 0] += 1 # Asthma + data['cd_miss_counter'][data['hcond2'] < 0] += 1 # Arthritis + data['cd_miss_counter'][data['hcond3'] < 0] += 1 # Congestive Heart Failure + data['cd_miss_counter'][data['hcond4'] < 0] += 1 # Coronary Heart Failure + data['cd_miss_counter'][data['hcond5'] < 0] += 1 # Angina + data['cd_miss_counter'][data['hcond6'] < 0] += 1 # Heart attack or myocardial infarction + data['cd_miss_counter'][data['hcond7'] < 0] += 1 # Stroke + data['cd_miss_counter'][data['hcond8'] < 0] += 1 # Emphysema + data['cd_miss_counter'][data['hcond10'] < 0] += 1 # Hypothyroidism + data['cd_miss_counter'][data['hcond11'] < 0] += 1 # Chronic Bronchitis + data['cd_miss_counter'][data['hcond12'] < 0] += 1 # Any kind of liver condition + data['cd_miss_counter'][data['hcond13'] < 0] += 1 # Cancer or malignancy + data['cd_miss_counter'][data['hcond14'] < 0] += 1 # Diabetes + data['cd_miss_counter'][data['hcond15'] < 0] += 1 # Epilepsy + data['cd_miss_counter'][data['hcond16'] < 0] += 1 # High blood pressure + data['cd_miss_counter'][data['hcond18'] < 0] += 1 # Other long-standing/chronic condition + data['cd_miss_counter'][data['hcond19'] < 0] += 1 # Multiple Sclerosis + data['cd_miss_counter'][data['hcond20'] < 0] += 1 # H.I.V + data['cd_miss_counter'][data['hcond21'] < 0] += 1 # COPD + + # 'hcond96': 'hcond96', # Health Condition 96: None of these + + data['chron_disease'] = -9 + data['chron_disease'][data['cd_counter'] == 0] = 1 # No Chronic disease + data['chron_disease'][data['cd_counter'] == 1] = 2 # 1 Chronic disease + data['chron_disease'][data['cd_counter'] > 1] = 3 # 1+ Chronic disease + + # Now missing - when all (19) CD's are missing then the ordinal variable can be labelled missing + data['chron_disease'][data['cd_miss_counter'] == 19] = -9 # All missing + + # drop cols no longer need + data.drop(labels=['hcond1', 'hcond2', 'hcond3', 'hcond4', 'hcond5', 'hcond6', 'hcond7', 'hcond8', 'hcond10', + 'hcond11', 'hcond12', 'hcond13', 'hcond14', 'hcond15', 'hcond16', 'hcond18', 'hcond19', 'hcond20', + 'hcond21', 'hcond96', + 'cd_counter', 'cd_miss_counter'], + axis=1, + inplace=True) + + return data + + + def calculate_children(data, parity_max=PARITY_MAX_DEFAULT): """ @@ -1238,8 +1324,9 @@ def main(): data = calculate_auditc_score(data) # alcohol use disorder for consumption (auditc) data = generate_physical_activity_binary(data) # physical activity composite data = generate_matdep_proxy(data) # Material Deprivation proxy + data = generate_chron_disease_proxy(data) # Chronic Disease proxy data = calculate_children(data) # total number of biological children - data = generate_difference_variables(data) # difference variables for longitudinal/difference models. + data = generate_difference_variables(data) # difference variables for longitudinal/difference models. print('Finished composite generation. Saving data...') US_utils.save_multiple_files(data, years, "data/composite_US/", "") diff --git a/minos/data_generation/generate_repl_pop.py b/minos/data_generation/generate_repl_pop.py index 0538da7d..c624520f 100755 --- a/minos/data_generation/generate_repl_pop.py +++ b/minos/data_generation/generate_repl_pop.py @@ -65,7 +65,7 @@ def expand_repl(US_2018): # now update Date variable (just use US_utils function new_repl = US_utils.generate_interview_date_var(new_repl) # adjust pidp to ensure unique values (have checked this and made sure this will never give us a duplicate) - new_repl['pidp'] = new_repl['pidp'] + year + 1000000 + (3 * new_repl.index) + new_repl['pidp'] = new_repl['pidp'] + year + 1000000 + (5 * new_repl.index) #print(f"There are {len(new_repl)} people in the replenishing population in year {year}.") diff --git a/minos/minosPipeline/RunPipeline.py b/minos/minosPipeline/RunPipeline.py index 86b84b81..d11d936b 100755 --- a/minos/minosPipeline/RunPipeline.py +++ b/minos/minosPipeline/RunPipeline.py @@ -30,7 +30,13 @@ from minos.modules.loneliness import Loneliness from minos.modules.education import Education from minos.modules.nutrition import Nutrition, lmmYJNutrition, lmmDiffNutrition +from minos.modules.heating import Heating +from minos.modules.financial_situation import financialSituation +from minos.modules.housing_tenure import HousingTenure +from minos.modules.physical_activity import PhysicalActivity from minos.modules.material_deprivation import MaterialDeprivation +from minos.modules.chron_disease import ChronicDisease + from minos.modules.S7Labour import S7Labour from minos.modules.S7Housing import S7Housing @@ -38,10 +44,6 @@ from minos.modules.S7MentalHealth import S7MentalHealth from minos.modules.S7PhysicalHealth import S7PhysicalHealth from minos.modules.S7EquivalentIncome import S7EquivalentIncome -from minos.modules.heating import Heating -from minos.modules.financial_situation import financialSituation -from minos.modules.housing_tenure import HousingTenure -from minos.modules.physical_activity import PhysicalActivity from minos.modules.intervention import hhIncomeIntervention from minos.modules.intervention import hhIncomeChildUplift @@ -89,6 +91,7 @@ "MaterialDeprivation()": MaterialDeprivation(), "PhysicalActivity()": PhysicalActivity(), "HousingTenure()": HousingTenure(), + "ChronicDisease()": ChronicDisease(), } SIPHER7_components_map = { # SIPHER7 stuff diff --git a/minos/modules/chron_disease.py b/minos/modules/chron_disease.py new file mode 100644 index 00000000..f1bb22ce --- /dev/null +++ b/minos/modules/chron_disease.py @@ -0,0 +1,122 @@ +""" +Module for chronic disease in Minos. +""" + +import pandas as pd +import minos.modules.r_utils as r_utils +from minos.modules.base_module import Base +from seaborn import histplot +import matplotlib.pyplot as plt +import numpy as np +import logging + + +class ChronicDisease(Base): + """Chronic Disease Module""" + + # Special methods used by vivarium. + @property + def name(self): + return 'chron_disease' + + def __repr__(self): + return "ChronicDisease()" + + def setup(self, builder): + """ Initialise the module during simulation.setup(). + Notes + ----- + - Load in data from pre_setup + - Register any value producers/modifiers for income + - Add required columns to population data frame + - Update other required items such as randomness stream. + Parameters + ---------- + builder : vivarium.engine.Builder + Vivarium's control object. Stores all simulation metadata and allows modules to use it. + """ + + # Load in inputs from pre-setup. + self.rpy2_modules = builder.data.load("rpy2_modules") + + # Build vivarium objects for calculating transition probabilities. + # Typically this is registering rate/lookup tables. See vivarium docs/other modules for examples. + # self.transition_coefficients = builder. + + # Assign randomness streams if necessary. + self.random = builder.randomness.get_stream(self.generate_random_crn_key()) + + # Determine which subset of the main population is used in this module. + # columns_created is the columns created by this module. + # view_columns is the columns from the main population used in this module. + # In this case, view_columns are taken straight from the transition model + view_columns = ['pidp', + 'sex', + 'ethnicity', + 'age', + 'region', + 'education_state', + 'hh_income', + 'SF_12_MCS', + 'SF_12_PCS', + 'marital_status', + 'loneliness', + 'ncigs', + 'nutrition_quality', + 'active', + 'auditc', + 'matdep', + 'chron_disease'] + + self.population_view = builder.population.get_view(columns=view_columns) + + # Population initialiser. When new individuals are added to the microsimulation a constructer is called for each + # module. Declare what constructer is used. usually on_initialize_simulants method is called. Inidividuals are + # created at the start of a model "setup" or after some deterministic (add cohorts) or random (births) event. + # builder.population.initializes_simulants(self.on_initialize_simulants, creates_columns=columns_created) + + # Declare events in the module. At what times do individuals transition states from this module. E.g. when does + # individual graduate in an education module. + builder.event.register_listener("time_step", self.on_time_step, priority=5) + + def on_time_step(self, event): + """Produces new children and updates parent status on time steps. + Parameters + ---------- + event : vivarium.population.PopulationEvent + The event time_step that called this function. + """ + + pop = self.population_view.get(event.index, query="alive=='alive'") + self.year = event.time.year + + cd_prob_df = self.calculate_chron_disease(pop) + + cd_prob_df["chron_disease"] = self.random.choice(cd_prob_df.index, + list(cd_prob_df.columns), + cd_prob_df) + 1 + + self.population_view.update(cd_prob_df["chron_disease"].astype(float)) + + def calculate_chron_disease(self, pop): + """Calculate chron_disease transition distribution based on provided people/indices + Parameters + ---------- + index : pd.Index + Which individuals to calculate transitions for. + Returns + ------- + """ + + # load transition model based on year. + if self.cross_validation: + # if cross-val, fix year to final year model + year = 2019 + else: + year = min(self.year, 2019) + + transition_model = r_utils.load_transitions(f"chron_disease/clm/chron_disease_{year}_{year + 1}", + self.rpy2_modules, path=self.transition_dir) + # returns probability matrix (3xn) of next ordinal state. + prob_df = r_utils.predict_next_timestep_clm(transition_model, self.rpy2_modules, pop, 'chron_disease') + return prob_df diff --git a/minos/modules/mental_wellbeing.py b/minos/modules/mental_wellbeing.py index 80900963..f3c30edc 100755 --- a/minos/modules/mental_wellbeing.py +++ b/minos/modules/mental_wellbeing.py @@ -486,7 +486,8 @@ def setup(self, builder): 'neighbourhood_safety', 'loneliness', 'financial_situation', - 'active',] + 'active', + 'chron_disease'] self.population_view = builder.population.get_view(columns=view_columns) diff --git a/minos/modules/physical_wellbeing.py b/minos/modules/physical_wellbeing.py index 766df8bc..62e04445 100644 --- a/minos/modules/physical_wellbeing.py +++ b/minos/modules/physical_wellbeing.py @@ -193,7 +193,8 @@ def setup(self, builder): 'loneliness', 'financial_situation', 'active', - 'auditc',] + 'auditc', + 'chron_disease'] self.population_view = builder.population.get_view(columns=view_columns) @@ -255,10 +256,10 @@ def calculate_pwb(self, pop): """ out_data = r_utils.predict_next_timestep_yj_gamma_glmm(self.gee_transition_model, self.rpy2_modules, - current= pop, + current=pop, dependent='SF_12_PCS', reflect=True, - yeo_johnson= True, + yeo_johnson=True, mod_type='gamma', - noise_std= 0.1) # 5 for non yj, 0.35 for yj + noise_std=0.1) # 5 for non yj, 0.35 for yj return out_data diff --git a/minos/modules/replenishment.py b/minos/modules/replenishment.py index fcf7b704..889fab31 100755 --- a/minos/modules/replenishment.py +++ b/minos/modules/replenishment.py @@ -104,6 +104,7 @@ def setup(self, builder): 'SF_12_PCS_diff', 'matdep', 'matdep_diff', + 'chron_disease', ] # Shorthand methods for readability. diff --git a/minos/transitions/estimate_transitions.R b/minos/transitions/estimate_transitions.R index b428333f..53fcc73f 100644 --- a/minos/transitions/estimate_transitions.R +++ b/minos/transitions/estimate_transitions.R @@ -175,6 +175,7 @@ run_yearly_models <- function(transitionDir_path, # OLS_DIFF models can only start from wave 2 (no diff in first wave) if(tolower(mod.type) == 'ols_diff' & year == 2009) { next } if(dependent %in% c('matdep') & year %in% c(2009, 2010, 2012, 2014, 2016, 2018)) { next } + if(dependent %in% c('chron_disease') & year < 2011) { next } print(paste0('Starting estimation for ', dependent, ' in ', year)) @@ -207,28 +208,33 @@ run_yearly_models <- function(transitionDir_path, ## For the SF_12 models (MCS & PCS), we need to modify the formula on the fly # as neighbourhood_safety, loneliness, nutrition_quality and ncigs are # not present every year - if(dependent %in% c('SF_12_MCS', 'SF_12_PCS')) { - if(!year %in% c(2011, 2014, 2017, 2020)) { - formula.string <- str_remove(formula.string, " \\+ factor\\(neighbourhood_safety\\)") - } - if(!year > 2016) { - formula.string <- str_remove(formula.string, " \\+ factor\\(loneliness\\)") - } - if(!year %in% c(2015, 2017, 2019)) { - formula.string <- str_remove(formula.string, " \\+ nutrition_quality") - } - if(year < 2013) { - formula.string <- str_remove(formula.string, " \\+ ncigs") - } - if(!year %in% c(2015, 2017, 2019, 2020)) { - formula.string <- str_remove(formula.string, " \\+ factor\\(auditc\\)") - } - if(!year %in% c(2015, 2017, 2019, 2020)) { - formula.string <- str_remove(formula.string, " \\+ factor\\(active\\)") - } - if(!year %in% c(2009, 2010, 2012, 2014, 2016, 2018, 2020)) { - formula.string <- str_remove(formula.string, " \\+ factor\\(matdep\\)") - } + # CHANGE 9/10/23: We remove these variables because they're not present, + # so we should do this for all models and not just SF_12 + if(!year %in% c(2011, 2014, 2017, 2020)) { + formula.string <- str_remove(formula.string, " \\+ factor\\(neighbourhood_safety\\)") + } + if(!year > 2016) { + formula.string <- str_remove(formula.string, " \\+ factor\\(loneliness\\)") + } + if(!year %in% c(2015, 2017, 2019)) { + formula.string <- str_remove(formula.string, " \\+ nutrition_quality") + formula.string <- str_remove(formula.string, " \\+ scale\\(nutrition_quality\\)") + } + if(year < 2013) { + formula.string <- str_remove(formula.string, " \\+ ncigs") + formula.string <- str_remove(formula.string, " \\+ scale\\(ncigs\\)") + } + if(!year %in% c(2015, 2017, 2019, 2020)) { + formula.string <- str_remove(formula.string, " \\+ factor\\(auditc\\)") + } + if(!year %in% c(2015, 2017, 2019, 2020)) { + formula.string <- str_remove(formula.string, " \\+ factor\\(active\\)") + } + if(!year %in% c(2009, 2010, 2012, 2014, 2016, 2018, 2020)) { + formula.string <- str_remove(formula.string, " \\+ factor\\(matdep\\)") + } + if(year < 2011) { + formula.string <- str_remove(formula.string, " \\+ factor\\(chron_disease\\)") } #print(formula.string) # Now make string into formula diff --git a/minos/transitions/model_definitions_default.txt b/minos/transitions/model_definitions_default.txt index 0d35a8fd..7284a59a 100644 --- a/minos/transitions/model_definitions_default.txt +++ b/minos/transitions/model_definitions_default.txt @@ -11,6 +11,7 @@ CLM : loneliness ~ factor(loneliness) + scale(age) + factor(sex) + scale(SF_12_M CLM : neighbourhood_safety ~ scale(age) + factor(sex) + factor(job_sec) + relevel(factor(ethnicity), ref = 'WBI') + scale(hh_income) + factor(housing_quality) + relevel(factor(region), ref = 'South East') + factor(urban) NNET : education_state ~ factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') CLM : matdep ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(marital_status) + hhsize + factor(housing_tenure) + factor(urban) + factor(financial_situation) +CLM : chron_disease ~ factor(chron_disease) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(marital_status) + factor(loneliness) + scale(ncigs) + scale(nutrition_quality) + factor(active) + factor(auditc) + factor(matdep) ZIP : ncigs ~ ncigs + age + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + relevel(factor(job_sec), ref = '3') + scale(hh_income) + relevel(factor(ethnicity), ref = 'WBI') | I(factor(ncigs>0)) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(job_sec), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) -GLMM : SF_12_PCS ~ scale(SF_12_PCS_last) + I(scale(SF_12_PCS_last)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + factor(housing_quality) + scale(hh_income) + factor(neighbourhood_safety) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(financial_situation) + factor(active) + factor(auditc) + (1|pidp) -GLMM : SF_12_MCS ~ scale(SF_12_MCS_last) + I(scale(SF_12_MCS_last)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + factor(housing_quality) + scale(hh_income) + factor(neighbourhood_safety) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(financial_situation) + factor(active) + (1|pidp) +GLMM : SF_12_PCS ~ scale(SF_12_PCS_last) + I(scale(SF_12_PCS_last)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + factor(housing_quality) + scale(hh_income) + factor(neighbourhood_safety) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(financial_situation) + factor(active) + factor(auditc) + factor(chron_disease) + (1|pidp) +GLMM : SF_12_MCS ~ scale(SF_12_MCS_last) + I(scale(SF_12_MCS_last)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + factor(housing_quality) + scale(hh_income) + factor(neighbourhood_safety) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(financial_situation) + factor(active) + factor(chron_disease) + (1|pidp) diff --git a/minos/validation/handovers.Rmd b/minos/validation/handovers.Rmd index e4f826f1..989f16dc 100644 --- a/minos/validation/handovers.Rmd +++ b/minos/validation/handovers.Rmd @@ -90,7 +90,7 @@ base.inc <- base.dat %>% income.spag <- rbind(raw.inc, base.inc) density_ridges(income.spag, 'hh_income', - save=TRUE, + save=shall.we.save, save.path=save.path) pidp_sample <- sample(unique(income.spag$pidp), size=nrow(raw.inc)/10, replace=FALSE) @@ -499,4 +499,38 @@ handover_ordinal(raw.dat, base.dat, var = 'matdep', save = shall.we.save) # rm(raw.s, base.s, spag) ``` +## Chronic Disease +```{r} +handover_ordinal(raw.dat, base.dat, var = 'chron_disease', save = shall.we.save) +``` + +```{r} +raw.s <- raw.dat %>% + select(pidp, age, time, chron_disease) %>% + filter(!chron_disease %in% miss.values) +base.s <- base.dat %>% + select(pidp, age, time, chron_disease) %>% + filter(!chron_disease %in% miss.values) + +spag <- rbind(raw.s, base.s) + +spaghetti_plot(spag, 'chron_disease', + save = shall.we.save, + save.path = save.path) + +density_ridges(spag, 'chron_disease', + save=shall.we.save, + save.path=save.path) + +pidp_sample <- sample(unique(spag$pidp), size=nrow(raw.s)/10, replace=FALSE) +spag.sample <- spag[which(spag$pidp %in% pidp_sample), ] +spaghetti_plot(spag.sample, 'chron_disease', + save = shall.we.save, + save.path = save.path) +spaghetti_highlight_max_plot(spag.sample, 'chron_disease', + save = shall.we.save, + save.path = save.path) + +rm(raw.s, base.s, spag) +``` From 9a757d16cbc3f173ecffd66ef1e1a2cd8b930e1e Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 10 Oct 2023 10:35:57 +0100 Subject: [PATCH 044/229] Tried to force chron_disease predictions to an absorbing type (i.e. not allow individuals to go down levels). This is not good so looking for other solutions --- minos/modules/chron_disease.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/minos/modules/chron_disease.py b/minos/modules/chron_disease.py index f1bb22ce..c59d5812 100644 --- a/minos/modules/chron_disease.py +++ b/minos/modules/chron_disease.py @@ -96,6 +96,12 @@ def on_time_step(self, event): list(cd_prob_df.columns), cd_prob_df) + 1 + # Chronic disease has to be absorbing, so individuals cannot drop back to lower levels + #pop['previous_chron_disease'] = + cd_prob_df['previous_chron_disease'] = pop['chron_disease'] + cd_prob_df['chron_disease'][cd_prob_df['previous_chron_disease'] > cd_prob_df['chron_disease']] = \ + cd_prob_df['previous_chron_disease'] + self.population_view.update(cd_prob_df["chron_disease"].astype(float)) def calculate_chron_disease(self, pop): From c9cd8e290ad15600ad7c3e9a8990c705d406c54b Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 17 Oct 2023 09:53:40 +0100 Subject: [PATCH 045/229] Ran chron_disease var through complete case --- minos/data_generation/US_complete_case.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/minos/data_generation/US_complete_case.py b/minos/data_generation/US_complete_case.py index 57468299..8c7d0806 100755 --- a/minos/data_generation/US_complete_case.py +++ b/minos/data_generation/US_complete_case.py @@ -72,7 +72,8 @@ def cut_outliers(df, lower, upper, var): complete_case_vars = ["housing_quality", 'marital_status', 'yearly_energy', "job_sec", "education_state", 'region', "age", "job_sector", 'SF_12_MCS', 'SF_12_PCS', - 'financial_situation', "housing_tenure", 'urban', 'heating', "nkids_ind"] + 'financial_situation', "housing_tenure", 'urban', 'heating', "nkids_ind", + 'chron_disease'] # REMOVED: 'job_sector', 'labour_state' data = complete_case_varlist(data, complete_case_vars) @@ -111,6 +112,7 @@ def cut_outliers(df, lower, upper, var): data = complete_case_custom_years(data, 'auditc', years=[2015, 2017, 2019, 2020]) # active (physical activity) - present in 2015, 2017, 2019, 2020 data = complete_case_custom_years(data, 'active', years=[2015, 2017, 2019, 2020]) + # chronic disease in the all years function drop_columns = [#'financial_situation', # these are just SF12 MICE columns for now. see US_format_raw.py 'ghq_depression', From 5b2b208b797c90c6848bfdd3b02309765060cf39 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 17 Oct 2023 11:10:15 +0100 Subject: [PATCH 046/229] Moved hcond variables from forward fill to forward monotonic fill as they should be absorbing states --- minos/data_generation/US_missing_LOCF.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/minos/data_generation/US_missing_LOCF.py b/minos/data_generation/US_missing_LOCF.py index 23d38124..aba612d6 100755 --- a/minos/data_generation/US_missing_LOCF.py +++ b/minos/data_generation/US_missing_LOCF.py @@ -245,12 +245,13 @@ def main(data, save=False): f_columns = ['education_state', 'labour_state_raw', 'job_sec', 'heating', 'ethnicity', 'sex', 'birth_year', 'yearly_gas', 'yearly_electric', 'yearly_gas_electric', 'yearly_oil', 'yearly_other_fuel', 'smoker', 'nkids_ind_raw', - 'hcond1', 'hcond2', 'hcond3', 'hcond4', 'hcond5', 'hcond6', 'hcond7', 'hcond8', 'hcond10', - 'hcond11', 'hcond12', 'hcond13', 'hcond14', 'hcond15', 'hcond16', 'hcond18', 'hcond19', 'hcond20', - 'hcond21', # ALL HCOND are health conditions - for the chronic conditions proxy var ] # 'ncigs', 'ndrinks'] fb_columns = ["sex", "ethnicity", "birth_year"] # or here if they're immutable. - mf_columns = ['education_state', 'nkids_ind_raw'] + mf_columns = ['education_state', 'nkids_ind_raw', + 'hcond1', 'hcond2', 'hcond3', 'hcond4', 'hcond5', 'hcond6', 'hcond7', 'hcond8', 'hcond10', + 'hcond11', 'hcond12', 'hcond13', 'hcond14', 'hcond15', 'hcond16', 'hcond18', 'hcond19', 'hcond20', + 'hcond21' # ALL HCOND are health conditions - for the chronic conditions proxy var + ] # hcond variables are absorbing and therefore cannot go backwards li_columns = ["age"] data = locf(data, f_columns=f_columns, fb_columns=fb_columns, mf_columns=mf_columns) print("After LOCF correction.") @@ -263,6 +264,7 @@ def main(data, save=False): US_utils.save_multiple_files(data, years, 'data/locf_US/', "") return data + if __name__ == "__main__": # Load in data. # Process data by year and pidp. From 0cdcbdc02381e92f0b4b4c8dc6c1f7d35ab5e76c Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Thu, 19 Oct 2023 11:09:25 +0100 Subject: [PATCH 047/229] PCS in a reasonable state, chron_disease seems to be much happier with a CLM model with the data fixes I implemented for ensuring absorbing transitions are handled correclty --- minos/modules/chron_disease.py | 12 ++-- minos/transitions/Makefile | 2 +- .../estimate_longitudinal_transitions.R | 47 +++++++------ .../transitions/model_definitions_default.txt | 2 +- .../transitions/transition_model_functions.R | 67 ++++++++++++++++--- 5 files changed, 89 insertions(+), 41 deletions(-) diff --git a/minos/modules/chron_disease.py b/minos/modules/chron_disease.py index c59d5812..2ed071ac 100644 --- a/minos/modules/chron_disease.py +++ b/minos/modules/chron_disease.py @@ -96,13 +96,13 @@ def on_time_step(self, event): list(cd_prob_df.columns), cd_prob_df) + 1 - # Chronic disease has to be absorbing, so individuals cannot drop back to lower levels - #pop['previous_chron_disease'] = - cd_prob_df['previous_chron_disease'] = pop['chron_disease'] - cd_prob_df['chron_disease'][cd_prob_df['previous_chron_disease'] > cd_prob_df['chron_disease']] = \ - cd_prob_df['previous_chron_disease'] + # # Chronic disease has to be absorbing, so individuals cannot drop back to lower levels + # #pop['previous_chron_disease'] = + # cd_prob_df['previous_chron_disease'] = pop['chron_disease'] + # cd_prob_df['chron_disease'][cd_prob_df['previous_chron_disease'] > cd_prob_df['chron_disease']] = \ + # cd_prob_df['previous_chron_disease'] - self.population_view.update(cd_prob_df["chron_disease"].astype(float)) + self.population_view.update(cd_prob_df["chron_disease"]) def calculate_chron_disease(self, pop): """Calculate chron_disease transition distribution based on provided people/indices diff --git a/minos/transitions/Makefile b/minos/transitions/Makefile index 0815db30..6bbd9dd7 100644 --- a/minos/transitions/Makefile +++ b/minos/transitions/Makefile @@ -23,7 +23,7 @@ cv_transitions: $(TRANSITION_DATA)/cross_validation/version5/ncigs/zip/ncigs_201 cv_S7_transitions: | $(TRANSITION_DATA) cv_S7_transitions: $(TRANSITION_DATA)/cross_validation/version5/S7_mental_health/clm/S7_mental_health_2019_2020.rds $(TRANSITION_DATA)/cross_validation/version5/hh_income/glmm/hh_income_new_GLMM.rds -$(TRANSITION_DATA)/ncigs/zip/ncigs_2019_2020.rds: $(FINALDATA)/2020_US_cohort.csv $(TRANSITION_SOURCE)/estimate_transitions.R $(TRANSITION_SOURCE)/transition_model_functions.R $(TRANSITION_SOURCE)/model_definitions_default.txt +$(TRANSITION_DATA)/ncigs/zip/ncigs_2019_2020.rds: $(FINALDATA)/2020_US_cohort.csv $(TRANSITION_SOURCE)/estimate_transitions.R $(TRANSITION_SOURCE)/model_definitions_default.txt #$(TRANSITION_SOURCE)/transition_model_functions.R $(RSCRIPT) $(SOURCEDIR)/transitions/estimate_transitions.R --default $(TRANSITION_DATA)/S7_mental_health/clm/S7_mental_health_2019_2020.rds: $(FINALDATA)/2020_US_cohort.csv $(TRANSITION_SOURCE)/estimate_transitions.R $(TRANSITION_SOURCE)/transition_model_functions.R $(TRANSITION_SOURCE)/model_definitions_S7.txt diff --git a/minos/transitions/estimate_longitudinal_transitions.R b/minos/transitions/estimate_longitudinal_transitions.R index 87665db3..8b48052a 100644 --- a/minos/transitions/estimate_longitudinal_transitions.R +++ b/minos/transitions/estimate_longitudinal_transitions.R @@ -32,7 +32,7 @@ run_longitudinal_models <- function(transitionDir_path, transitionSourceDir_path modDef_path = paste0(transitionSourceDir_path, mod_def_name) modDefs <- file(description = modDef_path, open="r", blocking = TRUE) - valid_longitudnial_model_types <- c("LMM", "LMM_DIFF", "GLMM", "GEE_DIFF","ORDGEE", "CLMM", 'GLMMB') + valid_longitudnial_model_types <- c("LMM", "LMM_DIFF", "GLMM", "GEE_DIFF","ORDGEE", "CLMM", 'GLMMB', 'MSM') data[which(data$ncigs==-8), 'ncigs'] <- 0 @@ -50,13 +50,11 @@ run_longitudinal_models <- function(transitionDir_path, transitionSourceDir_path next }# skip this iteration if model not in valid types. - # Get dependent and independents split2 <- str_split(split1[2], pattern = " ~ ")[[1]] dependent <- split2[1] independents <- split2[2] - - + ## Yearly model estimation loop # Need to construct dataframes for each year that have independents from time T and dependents from time T+1 if(mode == 'cross_validation') { @@ -74,15 +72,14 @@ run_longitudinal_models <- function(transitionDir_path, transitionSourceDir_path create.if.not.exists(out.path2) print(paste0('Starting for ', dependent, '...')) - - # no weight var in 2009 (wave 1) - use.weights <- FALSE + + #use.weights <- TRUE # TODO strange behaviour using weights for gees/glmms. Needs scaling? disabling for now.. - #if(year == 2009) { - # use.weights <- FALSE - #} else { - # use.weights <- TRUE - #} + if (mod.type %in% c("GLMM", "GEE_DIFF","ORDGEE", 'GLMMB')) { + use.weights <- FALSE + } else { + use.weights <- TRUE + } if (dependent %in% c("SF_12_MCS", 'SF_12_PCS')) { do.reflect = TRUE # only SF12 continuous data is reflected to be left skewed. @@ -123,8 +120,6 @@ run_longitudinal_models <- function(transitionDir_path, transitionSourceDir_path rename_with(.fn = ~paste0(dependent, '_', .), .cols = diff) # add the dependent as prefix to the calculated diff # update model formula with _diff variable. dependent <- paste0(dependent, "_diff") - formula.string <- paste0(dependent, " ~ ", independents) - form <- as.formula(formula.string) } # if using glmms need to be careful which time the outcome variable is from. @@ -142,24 +137,19 @@ run_longitudinal_models <- function(transitionDir_path, transitionSourceDir_path mutate(new = lead(.data[[dependent]], order_by = time)) %>% rename_with(.fn = ~paste0(dependent, '_', .), .cols = new) # add the dependent as prefix to the calculated diff dependent <- paste0(dependent, "_new") - formula.string <- paste0(dependent, " ~ ", independents) - form <- as.formula(formula.string) } - else if (dependent %in% c('SF_12_MCS', 'SF_12_PCS', 'matdep')) { + else if (dependent %in% c('SF_12_MCS', 'SF_12_PCS', 'matdep', 'chron_disease')) { # get lagged SF12 value and label with _last. data <- data %>% group_by(pidp) %>% #mutate(diff = .data[[dependent]] - lag(.data[[dependent]], order_by = time)) %>% mutate(last = lag(.data[[dependent]], order_by = time)) %>% rename_with(.fn = ~paste0(dependent, '_', .), .cols = last) # add the dependent as prefix to the calculated diff - # data <- data %>% - # group_by(pidp) %>% - # mutate(diff = .data[[dependent]] - lag(.data[[dependent]], order_by = time)) %>% - # rename_with(.fn = ~paste0(dependent, '_', .), .cols = diff) - # - formula.string <- paste0(dependent, " ~ ", independents) - form <- as.formula(formula.string) } + + formula.string <- paste0(dependent, " ~ ", independents) + form <- as.formula(formula.string) + # get only required variables and sort by pidp/time. df <- data[, append(all.vars(form), c("time", 'pidp', 'weight'))] sorted_df <- df[order(df$pidp, df$time),] @@ -200,6 +190,7 @@ run_longitudinal_models <- function(transitionDir_path, transitionSourceDir_path depend = dependent) } else if (tolower(mod.type) == "clmm") { + print('Arrived at the model estimation function call.') model <- estimate_longitudinal_clmm(data = sorted_df, formula = form, @@ -213,6 +204,12 @@ run_longitudinal_models <- function(transitionDir_path, transitionSourceDir_path reflect = do.reflect, yeo_johnson = do.yeo.johnson) + } else if (tolower(mod.type) == 'msm') { + + model <- estimate_longitudinal_msm(data = sorted_df, + formula = form, + depend = dependent, + start.year = min(year.range)) } write_coefs <- F @@ -287,6 +284,8 @@ cross_validation <- args$crossval default <- args$default sipher7 <- args$SIPHER7 +default <- TRUE + ## RUNTIME ARGS transSourceDir <- 'minos/transitions/' dataDir <- 'data/final_US/' diff --git a/minos/transitions/model_definitions_default.txt b/minos/transitions/model_definitions_default.txt index 7284a59a..9035a2ff 100644 --- a/minos/transitions/model_definitions_default.txt +++ b/minos/transitions/model_definitions_default.txt @@ -1,3 +1,4 @@ +CLM : chron_disease ~ factor(chron_disease) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(marital_status) + scale(ncigs) LOGIT : active ~ age + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + hh_income + relevel(factor(ethnicity), ref = 'WBI') NNET : auditc ~ age + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + hh_income + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + factor(financial_situation) CLM : financial_situation ~ factor(financial_situation) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(job_sec) + scale(hh_income)+ I(scale(hh_income)**2) + factor(marital_status) + hhsize + factor(housing_tenure) @@ -11,7 +12,6 @@ CLM : loneliness ~ factor(loneliness) + scale(age) + factor(sex) + scale(SF_12_M CLM : neighbourhood_safety ~ scale(age) + factor(sex) + factor(job_sec) + relevel(factor(ethnicity), ref = 'WBI') + scale(hh_income) + factor(housing_quality) + relevel(factor(region), ref = 'South East') + factor(urban) NNET : education_state ~ factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') CLM : matdep ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(marital_status) + hhsize + factor(housing_tenure) + factor(urban) + factor(financial_situation) -CLM : chron_disease ~ factor(chron_disease) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(marital_status) + factor(loneliness) + scale(ncigs) + scale(nutrition_quality) + factor(active) + factor(auditc) + factor(matdep) ZIP : ncigs ~ ncigs + age + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + relevel(factor(job_sec), ref = '3') + scale(hh_income) + relevel(factor(ethnicity), ref = 'WBI') | I(factor(ncigs>0)) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(job_sec), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) GLMM : SF_12_PCS ~ scale(SF_12_PCS_last) + I(scale(SF_12_PCS_last)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + factor(housing_quality) + scale(hh_income) + factor(neighbourhood_safety) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(financial_situation) + factor(active) + factor(auditc) + factor(chron_disease) + (1|pidp) GLMM : SF_12_MCS ~ scale(SF_12_MCS_last) + I(scale(SF_12_MCS_last)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + factor(housing_quality) + scale(hh_income) + factor(neighbourhood_safety) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(financial_situation) + factor(active) + factor(chron_disease) + (1|pidp) diff --git a/minos/transitions/transition_model_functions.R b/minos/transitions/transition_model_functions.R index 6fd07bda..e0ce149b 100644 --- a/minos/transitions/transition_model_functions.R +++ b/minos/transitions/transition_model_functions.R @@ -5,6 +5,7 @@ require(pscl) require(bestNormalize) require(lme4) require(glmmTMB) +require(msm) ################ Model Specific Functions ################ @@ -276,15 +277,23 @@ estimate_longitudinal_mlogit_gee <- function(data, formula, include_weights=FALS estimate_longitudinal_clmm <- function(data, formula, depend) { + + print('In the function call') + data <- replace.missing(data) data <- drop_na(data) data[, c(depend)] <- factor(data[, c(depend)]) + + print('Before fitting the model') + + print(formula) + model <- clmm2(formula, - random=factor(pidp), - link='probit', # logistic link function (can use probit or cloglog as well.) - data = tail(data, 1000), # get seg fault if using too many rows :(. clip to most recent data. - threshold="flexible", - nAGQ=1) # negative int values for nAGQ gives fast but sloppy prediction. (see ?clmm2) + random=factor(pidp), + link='probit', # logistic link function (can use probit or cloglog as well.) + data = tail(data, 1000), # get seg fault if using too many rows :(. clip to most recent data. + threshold="flexible", + nAGQ=1) # negative int values for nAGQ gives fast but sloppy prediction. (see ?clmm2) return (model) } @@ -308,10 +317,6 @@ estimate_beta_glmm <- function(data, formula, depend, reflect, yeo_johnson) { #data[[depend]][data[[depend]] == 1] = 0.999 #data[[depend]][data[[depend]] == 0] = 0.001 - print(paste0('Some data summary stats for beta GLMM ', depend)) - print(paste0('Min value of dependent variable: ', min_value)) - print(paste0('Max value of dependent variable: ', max(data[[depend]]))) - model <- glmmTMB(formula, data = data, family = beta_family(link = 'logit'), @@ -330,3 +335,47 @@ estimate_beta_glmm <- function(data, formula, depend, reflect, yeo_johnson) { return (model) } + +estimate_longitudinal_msm <- function(data, formula, depend, start.year) { + + data <- replace.missing(data) + + # Now set up the variable specific information the model needs such as subject, allowed transition matrix etc. + # Also needs some data wrangling to ensure that subjects have multiple waves of information, and that there + # are no intermittent missing waves as the msm function cannot handle this + # TODO: Impute missing intermittent waves?? + if (depend == 'chron_disease') { + print('Defining allowed transition matrix. Only unidirection transitions allowed.') + allowed.trans.matrix <- rbind( c( 1, 1, 1 ), c( 0, 1, 1 ), c( 0, 0, 1 ) ) + + print('Preparing data for MSM model estimation...') + # sort dataframe by pidp and time so observations within subjects are consecutive in the data + data <- data %>% + select(pidp, time, everything()) %>% # put pidp and time at the front of the df + group_by(pidp) %>% + filter(n() > 1) %>% # filter individuals with only 1 observation in data + group_by(pidp) %>% + complete(time = start.year:2020) %>% # complete the data (insert missing rows for the time period of interest) + filter(!cumprod(is.na(age)) & rev(!cumprod(is.na(rev(age))))) %>% # remove leading and trailing missing rows + group_by(pidp) %>% + mutate(contains.na = if_any(everything(), is.na)) %>% # test for any intermittent missing (i.e. if respondent misses a wave - msm model cannot handle this) + filter(!any(contains.na == TRUE)) %>% + select(pidp, time, -contains.na, everything()) + + data <- data[order(data$pidp, data$time), ] # order everything by pidp and time so individuals time points are in consecutive order (msm needs this) + rownames(data) <- NULL + + subj <- data$pidp + } + + print('Fitting the MSM model...') + model <- msm(formula = formula, + subject = subj, + data = data, + qmatrix = allowed.trans.matrix, + gen.inits = TRUE, + obstype = 1, + na.action = na.omit) + + return(model) +} From df1d7a769cc878c3f7cb47d3cdc5d7612aa1ac6c Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Thu, 19 Oct 2023 11:50:40 +0100 Subject: [PATCH 048/229] Addition of chron_disease to cross validation scripts, and small tweaks to both validation scripts --- minos/validation/cross_validation_default.Rmd | 15 ++++++++ minos/validation/handovers.Rmd | 36 +++++++++---------- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/minos/validation/cross_validation_default.Rmd b/minos/validation/cross_validation_default.Rmd index 91e514bc..ac6743a1 100644 --- a/minos/validation/cross_validation_default.Rmd +++ b/minos/validation/cross_validation_default.Rmd @@ -356,6 +356,21 @@ cv_ordinal_plots(pivoted.df = matdep.pivoted, rm(matdep.pivoted) ``` +# Chronic Disease + +```{r} +chron_disease.pivoted <- combine_and_pivot_long(df1 = cv, + df1.name = 'simulated', + df2 = raw, + df2.name = 'raw', + var = 'chron_disease') + +cv_ordinal_plots(pivoted.df = chron_disease.pivoted, + var = 'chron_disease', + save = FALSE) +rm(chron_disease.pivoted) +``` + # SECONDARY VARS ## Marital Status diff --git a/minos/validation/handovers.Rmd b/minos/validation/handovers.Rmd index 989f16dc..c16c103d 100644 --- a/minos/validation/handovers.Rmd +++ b/minos/validation/handovers.Rmd @@ -155,10 +155,10 @@ base.sf12.p <- base.dat %>% select(pidp, age, time, SF_12_PCS) sf12.p.spag <- rbind(raw.sf12.p, base.sf12.p) -# -# spaghetti_plot(sf12.p.spag, 'SF_12_PCS', -# save = shall.we.save, -# save.path = save.path) + +spaghetti_plot(sf12.p.spag, 'SF_12_PCS', + save = shall.we.save, + save.path = save.path) density_ridges(sf12.p.spag, "SF_12_PCS", save = shall.we.save, @@ -483,20 +483,20 @@ handover_ordinal(raw.dat, base.dat, var = 'matdep', save = shall.we.save) ``` ```{r} -# raw.s <- raw.dat %>% -# select(pidp, age, time, matdep) %>% -# filter(!matdep %in% miss.values) -# base.s <- base.dat %>% -# select(pidp, age, time, matdep) %>% -# filter(!matdep %in% miss.values) -# -# spag <- rbind(raw.s, base.s) -# -# spaghetti_plot(spag, 'matdep', -# save = shall.we.save, -# save.path = save.path) -# -# rm(raw.s, base.s, spag) +raw.s <- raw.dat %>% + select(pidp, age, time, matdep) %>% + filter(!matdep %in% miss.values) +base.s <- base.dat %>% + select(pidp, age, time, matdep) %>% + filter(!matdep %in% miss.values) + +spag <- rbind(raw.s, base.s) + +spaghetti_plot(spag, 'matdep', + save = shall.we.save, + save.path = save.path) + +rm(raw.s, base.s, spag) ``` ## Chronic Disease From 2b6ea89d57c7b1426dc156b0d1c2daadf0cd0100 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Thu, 19 Oct 2023 17:30:59 +0100 Subject: [PATCH 049/229] Changed the target for generating All Child Uplift lineplots back to only visualise the all child uplift scenario, and not both all child and poverty child on the same plot --- minos/outcomes/Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/minos/outcomes/Makefile b/minos/outcomes/Makefile index 6093156b..256ec37f 100644 --- a/minos/outcomes/Makefile +++ b/minos/outcomes/Makefile @@ -59,11 +59,11 @@ aggregate_minos_output_living_wage: aggregate_minos_output_poverty_child_uplift: bash minos/outcomes/make_lineplot.sh $(MODE) baseline,hhIncomePovertyLineChildUplift "Baseline,Poverty Line Uplift" who_below_poverty_line_and_kids,who_boosted "poverty_line_child_uplift" $(AGG_VAR) -#aggregate_minos_output_all_child_uplift: -# bash minos/outcomes/make_lineplot.sh $(MODE) baseline,hhIncomeChildUplift "Baseline,All Children Uplift" who_kids,who_boosted "all_child_uplift" - aggregate_minos_output_all_child_uplift: - bash minos/outcomes/make_lineplot.sh $(MODE) baseline,hhIncomeChildUplift,hhIncomePovertyLineChildUplift "Baseline,All Children Uplift,Poverty Line Child Uplift" who_kids,who_boosted,who_boosted "all_vs_poverty_child_uplift" $(AGG_VAR) + bash minos/outcomes/make_lineplot.sh $(MODE) baseline,hhIncomeChildUplift "Baseline,All Children Uplift" who_kids,who_boosted "all_child_uplift" $(AGG_VAR) + +#aggregate_minos_output_all_child_uplift: +# bash minos/outcomes/make_lineplot.sh $(MODE) baseline,hhIncomeChildUplift,hhIncomePovertyLineChildUplift "Baseline,All Children Uplift,Poverty Line Child Uplift" who_kids,who_boosted,who_boosted "all_vs_poverty_child_uplift" $(AGG_VAR) aggregate_minos_output_disabled_child_uplift: bash minos/outcomes/make_lineplot.sh $(MODE) baseline,hhIncomeChildUplift "Baseline,Disabled Children Uplift" who_disabled,who_disabled "disabled_child_uplift" $(AGG_VAR) From f34b6a5bd6dd0ea58c861844cef3b259826e1d02 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Fri, 20 Oct 2023 09:48:48 +0100 Subject: [PATCH 050/229] Testing something --- minos/outcomes/aggregate_minos_output.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/minos/outcomes/aggregate_minos_output.py b/minos/outcomes/aggregate_minos_output.py index cd4032d7..97973e01 100755 --- a/minos/outcomes/aggregate_minos_output.py +++ b/minos/outcomes/aggregate_minos_output.py @@ -63,13 +63,14 @@ def aggregate_variables_by_year(source, mode, years, tag, v, method, subset_func with Pool() as pool: aggregated_means = pool.starmap(aggregate_csv, zip(files, repeat(v), repeat(method), repeat("who_alive"), repeat(mode))) - elif year == 2020 and tag != "Baseline": - continue # skip processing here. ignoring starting data from interventions. else: with Pool() as pool: aggregated_means = pool.starmap(aggregate_csv, zip(files, repeat(v), repeat(method), repeat(subset_func_string), repeat(mode))) + #elif year == 2020 and tag != "Baseline": + # continue # skip processing here. ignoring starting data from interventions. + new_df = pd.DataFrame(aggregated_means) new_df.columns = [v] new_df['year'] = year From f244b3a400c3d7ae16342b661f30136dec9215fc Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Fri, 20 Oct 2023 15:38:01 +0100 Subject: [PATCH 051/229] Changed alcohol module (auditc var) to a CLM model instead of NNET --- minos/modules/alcohol.py | 33 +++++++++++++++---- .../transitions/model_definitions_default.txt | 6 ++-- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/minos/modules/alcohol.py b/minos/modules/alcohol.py index 12d80c5c..2805f059 100755 --- a/minos/modules/alcohol.py +++ b/minos/modules/alcohol.py @@ -88,12 +88,25 @@ def on_time_step(self, event): ## Predict next alcohol value alcohol_prob_df = self.calculate_alcohol(pop) + # alcohol_prob_df["auditc"] = self.random.choice(alcohol_prob_df.index, + # list(alcohol_prob_df.columns), + # alcohol_prob_df) + alcohol_prob_df["auditc"] = self.random.choice(alcohol_prob_df.index, list(alcohol_prob_df.columns), - alcohol_prob_df) + alcohol_prob_df) + 1 + # Set index type to int (instead of object as previous) alcohol_prob_df.index = alcohol_prob_df.index.astype(int) + # convert numeric prediction into string factors (low, medium, high) + alcohol_factor_dict = {1: 'Non-drinker', + 2: 'Low Risk', + 3: 'Increased Risk', + 4: 'High Risk'} + alcohol_prob_df.replace({'auditc': alcohol_factor_dict}, + inplace=True) + # Draw individuals next states randomly from this distribution. # Update population with new alcohol self.population_view.update(alcohol_prob_df["auditc"]) @@ -118,14 +131,20 @@ def calculate_alcohol(self, pop): cols = ['Non-drinker', 'Low Risk', 'Increased Risk', 'High Risk'] - transition_model = r_utils.load_transitions(f"auditc/nnet/auditc_{year}_{year + 1}", + #transition_model = r_utils.load_transitions(f"auditc/nnet/auditc_{year}_{year + 1}", + # self.rpy2Modules, + # path=self.transition_dir) + transition_model = r_utils.load_transitions(f"auditc/clm/auditc_{year}_{year + 1}", self.rpy2Modules, path=self.transition_dir) # returns probability matrix (3xn) of next ordinal state. - prob_df = r_utils.predict_nnet(transition_model, - self.rpy2Modules, - pop, - cols) + # prob_df = r_utils.predict_nnet(transition_model, + # self.rpy2Modules, + # pop, + # cols) + + prob_df = r_utils.predict_next_timestep_clm(transition_model, self.rpy2Modules, pop, 'auditc') + return prob_df def plot(self, pop, config): @@ -134,4 +153,4 @@ def plot(self, pop, config): f = plt.figure() histplot(pop, x="alcohol_spending", stat='density') plt.savefig(file_name) - plt.close() \ No newline at end of file + plt.close() diff --git a/minos/transitions/model_definitions_default.txt b/minos/transitions/model_definitions_default.txt index 9035a2ff..cd2c63a4 100644 --- a/minos/transitions/model_definitions_default.txt +++ b/minos/transitions/model_definitions_default.txt @@ -1,6 +1,6 @@ CLM : chron_disease ~ factor(chron_disease) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(marital_status) + scale(ncigs) LOGIT : active ~ age + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + hh_income + relevel(factor(ethnicity), ref = 'WBI') -NNET : auditc ~ age + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + hh_income + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + factor(financial_situation) +CLM : auditc ~ age + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + hh_income + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + factor(financial_situation) CLM : financial_situation ~ factor(financial_situation) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(job_sec) + scale(hh_income)+ I(scale(hh_income)**2) + factor(marital_status) + hhsize + factor(housing_tenure) LOGIT : heating ~ factor(heating) + scale(SF_12_MCS) + scale(SF_12_PCS) + relevel(factor(ethnicity), ref='WBI') + hh_income + factor(marital_status) + ncigs + hhsize + factor(urban) + factor(housing_tenure) + factor(financial_situation) LOGIT : urban ~ age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = "1") @@ -13,5 +13,5 @@ CLM : neighbourhood_safety ~ scale(age) + factor(sex) + factor(job_sec) + releve NNET : education_state ~ factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') CLM : matdep ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(marital_status) + hhsize + factor(housing_tenure) + factor(urban) + factor(financial_situation) ZIP : ncigs ~ ncigs + age + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + relevel(factor(job_sec), ref = '3') + scale(hh_income) + relevel(factor(ethnicity), ref = 'WBI') | I(factor(ncigs>0)) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(job_sec), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) -GLMM : SF_12_PCS ~ scale(SF_12_PCS_last) + I(scale(SF_12_PCS_last)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + factor(housing_quality) + scale(hh_income) + factor(neighbourhood_safety) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(financial_situation) + factor(active) + factor(auditc) + factor(chron_disease) + (1|pidp) -GLMM : SF_12_MCS ~ scale(SF_12_MCS_last) + I(scale(SF_12_MCS_last)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + factor(housing_quality) + scale(hh_income) + factor(neighbourhood_safety) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(financial_situation) + factor(active) + factor(chron_disease) + (1|pidp) +GLMM : SF_12_PCS ~ scale(SF_12_PCS_last) + I(scale(SF_12_PCS_last)**2) + scale(SF_12_PCS_diff) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + factor(housing_quality) + scale(hh_income) + factor(neighbourhood_safety) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(financial_situation) + factor(active) + factor(auditc) + factor(chron_disease) + (1|pidp) +GLMM : SF_12_MCS ~ scale(SF_12_MCS_last) + I(scale(SF_12_MCS_last)**2) + scale(SF_12_MCS_diff) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + factor(housing_quality) + scale(hh_income) + factor(neighbourhood_safety) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(financial_situation) + factor(active) + factor(chron_disease) + (1|pidp) From b0a36ca17e9c4aac2b79e34a7e0d0004f3ee664c Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 24 Oct 2023 10:40:47 +0100 Subject: [PATCH 052/229] Fixed interventions by changing the boost reset logic, and changed alcohol back to NNET for testing --- minos/modules/alcohol.py | 44 +++++++++---------- minos/modules/intervention.py | 14 +++++- .../transitions/model_definitions_default.txt | 4 +- 3 files changed, 36 insertions(+), 26 deletions(-) diff --git a/minos/modules/alcohol.py b/minos/modules/alcohol.py index 2805f059..a08af3fa 100755 --- a/minos/modules/alcohol.py +++ b/minos/modules/alcohol.py @@ -88,24 +88,24 @@ def on_time_step(self, event): ## Predict next alcohol value alcohol_prob_df = self.calculate_alcohol(pop) - # alcohol_prob_df["auditc"] = self.random.choice(alcohol_prob_df.index, - # list(alcohol_prob_df.columns), - # alcohol_prob_df) - alcohol_prob_df["auditc"] = self.random.choice(alcohol_prob_df.index, list(alcohol_prob_df.columns), - alcohol_prob_df) + 1 + alcohol_prob_df) + + # alcohol_prob_df["auditc"] = self.random.choice(alcohol_prob_df.index, + # list(alcohol_prob_df.columns), + # alcohol_prob_df) + 1 # Set index type to int (instead of object as previous) alcohol_prob_df.index = alcohol_prob_df.index.astype(int) - # convert numeric prediction into string factors (low, medium, high) - alcohol_factor_dict = {1: 'Non-drinker', - 2: 'Low Risk', - 3: 'Increased Risk', - 4: 'High Risk'} - alcohol_prob_df.replace({'auditc': alcohol_factor_dict}, - inplace=True) + # # convert numeric prediction into string factors (low, medium, high) + # alcohol_factor_dict = {1: 'Non-drinker', + # 2: 'Low Risk', + # 3: 'Increased Risk', + # 4: 'High Risk'} + # alcohol_prob_df.replace({'auditc': alcohol_factor_dict}, + # inplace=True) # Draw individuals next states randomly from this distribution. # Update population with new alcohol @@ -131,17 +131,17 @@ def calculate_alcohol(self, pop): cols = ['Non-drinker', 'Low Risk', 'Increased Risk', 'High Risk'] - #transition_model = r_utils.load_transitions(f"auditc/nnet/auditc_{year}_{year + 1}", - # self.rpy2Modules, - # path=self.transition_dir) - transition_model = r_utils.load_transitions(f"auditc/clm/auditc_{year}_{year + 1}", - self.rpy2Modules, - path=self.transition_dir) + transition_model = r_utils.load_transitions(f"auditc/nnet/auditc_{year}_{year + 1}", + self.rpy2Modules, + path=self.transition_dir) + # transition_model = r_utils.load_transitions(f"auditc/clm/auditc_{year}_{year + 1}", + # self.rpy2Modules, + # path=self.transition_dir) # returns probability matrix (3xn) of next ordinal state. - # prob_df = r_utils.predict_nnet(transition_model, - # self.rpy2Modules, - # pop, - # cols) + prob_df = r_utils.predict_nnet(transition_model, + self.rpy2Modules, + pop, + cols) prob_df = r_utils.predict_next_timestep_clm(transition_model, self.rpy2Modules, pop, 'auditc') diff --git a/minos/modules/intervention.py b/minos/modules/intervention.py index 8f4c4d1f..b1e32d4d 100755 --- a/minos/modules/intervention.py +++ b/minos/modules/intervention.py @@ -184,7 +184,9 @@ def on_time_step(self, event): pop = self.population_view.get(event.index, query="alive =='alive'") # print(np.mean(pop['hh_income'])) # for debugging purposes. # TODO probably a faster way to do this than resetting the whole column. - pop['hh_income'] -= pop['boost_amount'] # reset boost if people move out of bottom decile. + #pop['hh_income'] -= pop['boost_amount'] # reset boost if people move out of bottom decile. + # reset boost amount back to 0 before applying intervention again + pop['boost_amount'] = 0 pop['boost_amount'] = (25 * 30.436875 * pop['nkids'] / 7) # £25 per week * 30.463/7 weeks per average month * nkids. pop['income_boosted'] = (pop['boost_amount'] != 0) pop['hh_income'] += pop['boost_amount'] @@ -258,7 +260,9 @@ def on_time_step(self, event): pop = self.population_view.get(event.index, query="alive =='alive'") # TODO probably a faster way to do this than resetting the whole column. - pop['hh_income'] -= pop['boost_amount'] + #pop['hh_income'] -= pop['boost_amount'] + # reset boost amount back to 0 before applying intervention again + pop['boost_amount'] = 0 # Poverty is defined as having (equivalised) disposable hh income <= 60% of national median. # About £800 as of 2020 + adjustment for inflation. # Subset everyone who is under poverty line. @@ -353,6 +357,8 @@ def on_time_step(self, event): pop = self.population_view.get(event.index, query="alive =='alive' and job_sector == 2") # TODO probably a faster way to do this than resetting the whole column. #pop['hh_income'] -= pop['boost_amount'] + # reset boost amount back to 0 before applying intervention again + pop['boost_amount'] = 0 # Now get who gets uplift (different for London/notLondon) who_uplifted_London = pop['hourly_wage'] > 0 who_uplifted_London *= pop['region'] == 'London' @@ -449,6 +455,8 @@ def on_time_step(self, event): pop = self.population_view.get(event.index, query="alive =='alive'") # TODO probably a faster way to do this than resetting the whole column. #pop['hh_income'] -= pop['boost_amount'] + # reset boost amount back to 0 before applying intervention again + pop['boost_amount'] = 0 # Poverty is defined as having (equivalised) disposable hh income <= 60% of national median. # About £800 as of 2020 + adjustment for inflation. # Subset everyone who is under poverty line. @@ -527,6 +535,8 @@ def on_time_step(self, event): pop = self.population_view.get(event.index, query="alive =='alive'") # TODO probably a faster way to do this than resetting the whole column. #pop['hh_income'] -= pop['boost_amount'] + # reset boost amount back to 0 before applying intervention again + pop['boost_amount'] = 0 # Poverty is defined as having (equivalised) disposable hh income <= 60% of national median. # About £800 as of 2020 + adjustment for inflation. # Subset everyone who is under poverty line. diff --git a/minos/transitions/model_definitions_default.txt b/minos/transitions/model_definitions_default.txt index cd2c63a4..6bd3f847 100644 --- a/minos/transitions/model_definitions_default.txt +++ b/minos/transitions/model_definitions_default.txt @@ -1,6 +1,6 @@ CLM : chron_disease ~ factor(chron_disease) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(marital_status) + scale(ncigs) LOGIT : active ~ age + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + hh_income + relevel(factor(ethnicity), ref = 'WBI') -CLM : auditc ~ age + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + hh_income + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + factor(financial_situation) +NNET : auditc ~ age + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + hh_income + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + factor(financial_situation) CLM : financial_situation ~ factor(financial_situation) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(job_sec) + scale(hh_income)+ I(scale(hh_income)**2) + factor(marital_status) + hhsize + factor(housing_tenure) LOGIT : heating ~ factor(heating) + scale(SF_12_MCS) + scale(SF_12_PCS) + relevel(factor(ethnicity), ref='WBI') + hh_income + factor(marital_status) + ncigs + hhsize + factor(urban) + factor(housing_tenure) + factor(financial_situation) LOGIT : urban ~ age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = "1") @@ -13,5 +13,5 @@ CLM : neighbourhood_safety ~ scale(age) + factor(sex) + factor(job_sec) + releve NNET : education_state ~ factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') CLM : matdep ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(marital_status) + hhsize + factor(housing_tenure) + factor(urban) + factor(financial_situation) ZIP : ncigs ~ ncigs + age + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + relevel(factor(job_sec), ref = '3') + scale(hh_income) + relevel(factor(ethnicity), ref = 'WBI') | I(factor(ncigs>0)) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(job_sec), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) -GLMM : SF_12_PCS ~ scale(SF_12_PCS_last) + I(scale(SF_12_PCS_last)**2) + scale(SF_12_PCS_diff) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + factor(housing_quality) + scale(hh_income) + factor(neighbourhood_safety) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(financial_situation) + factor(active) + factor(auditc) + factor(chron_disease) + (1|pidp) +GLMM : SF_12_PCS ~ scale(SF_12_PCS_last) + I(scale(SF_12_PCS_last)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + factor(housing_quality) + scale(hh_income) + factor(neighbourhood_safety) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(financial_situation) + factor(active) + factor(auditc) + factor(chron_disease) + (1|pidp) GLMM : SF_12_MCS ~ scale(SF_12_MCS_last) + I(scale(SF_12_MCS_last)**2) + scale(SF_12_MCS_diff) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + factor(housing_quality) + scale(hh_income) + factor(neighbourhood_safety) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(financial_situation) + factor(active) + factor(chron_disease) + (1|pidp) From 36f266d9506c43e44b155f8921f55dfe051f5232 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Fri, 27 Oct 2023 15:36:43 +0100 Subject: [PATCH 053/229] Few fixes for the transition model formulae and the alcohol module switch to NNET --- minos/modules/alcohol.py | 2 +- minos/modules/physical_wellbeing.py | 3 ++- minos/modules/r_utils.py | 3 ++- minos/transitions/model_definitions_S7.txt | 4 ++-- minos/transitions/model_definitions_default.txt | 2 +- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/minos/modules/alcohol.py b/minos/modules/alcohol.py index a08af3fa..8781d035 100755 --- a/minos/modules/alcohol.py +++ b/minos/modules/alcohol.py @@ -143,7 +143,7 @@ def calculate_alcohol(self, pop): pop, cols) - prob_df = r_utils.predict_next_timestep_clm(transition_model, self.rpy2Modules, pop, 'auditc') + # prob_df = r_utils.predict_next_timestep_clm(transition_model, self.rpy2Modules, pop, 'auditc') return prob_df diff --git a/minos/modules/physical_wellbeing.py b/minos/modules/physical_wellbeing.py index 62e04445..1366a58e 100644 --- a/minos/modules/physical_wellbeing.py +++ b/minos/modules/physical_wellbeing.py @@ -194,7 +194,8 @@ def setup(self, builder): 'financial_situation', 'active', 'auditc', - 'chron_disease'] + 'chron_disease', + 'matdep'] self.population_view = builder.population.get_view(columns=view_columns) diff --git a/minos/modules/r_utils.py b/minos/modules/r_utils.py index b58ed6d2..8938fc94 100755 --- a/minos/modules/r_utils.py +++ b/minos/modules/r_utils.py @@ -152,7 +152,8 @@ def predict_next_timestep_clm(model, rpy2modules, current, dependent): currentRDF = ro.conversion.py2rpy(current) # need to cast the dependent var to an R FactorVector - if dependent in ['loneliness', 'neighbourhood_safety', 'housing_quality']: + if dependent in ['loneliness', 'neighbourhood_safety', 'housing_quality', 'auditc', 'financial_situation', + 'chron_disease', 'matdep']: dependent_index = list(currentRDF.colnames).index(dependent) dependent_col = FactorVector(currentRDF.rx2(dependent)) currentRDF[dependent_index] = dependent_col diff --git a/minos/transitions/model_definitions_S7.txt b/minos/transitions/model_definitions_S7.txt index 26a29c7e..137a2465 100644 --- a/minos/transitions/model_definitions_S7.txt +++ b/minos/transitions/model_definitions_S7.txt @@ -2,7 +2,7 @@ CLM : loneliness ~ age + factor(sex) + SF_12 + relevel(factor(education_state), NNET : education_state ~ factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') CLM : S7_housing_quality ~ age + factor(sex) + SF_12 + relevel(factor(ethnicity), ref = 'WBI') + hh_income CLM : S7_neighbourhood_safety ~ age + factor(sex) + factor(job_sec) + relevel(factor(ethnicity), ref = 'WBI') + hh_income + factor(housing_quality) + relevel(factor(region), ref = 'South East') -NNET : S7_labour_state ~ age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = "1") + hh_income + S7_physical_health + S7_mental_health +NNET : S7_labour_state ~ age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = "1") + S7_physical_health + S7_mental_health CLM : S7_physical_health ~ age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = "1") + factor(loneliness) + hh_income + S7_physical_health + S7_mental_health CLM : S7_mental_health ~ age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = "1") + factor(loneliness) + hh_income + S7_physical_health + S7_mental_health -GLMM : hh_income ~ scale(hh_income) + scale(hh_income_diff) + scale(age) + I(scale(age)**2) + I(scale(age)**3) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = "1") + relevel(factor(job_sec), ref = '3') + (1|pidp) \ No newline at end of file +GLMM : hh_income ~ scale(hh_income) + scale(hh_income_diff) + scale(age) + I(scale(age)**2) + I(scale(age)**3) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = "1") + relevel(factor(job_sec), ref = '3') + factor(S7_labour_state) + (1|pidp) \ No newline at end of file diff --git a/minos/transitions/model_definitions_default.txt b/minos/transitions/model_definitions_default.txt index 6bd3f847..8c9a1273 100644 --- a/minos/transitions/model_definitions_default.txt +++ b/minos/transitions/model_definitions_default.txt @@ -13,5 +13,5 @@ CLM : neighbourhood_safety ~ scale(age) + factor(sex) + factor(job_sec) + releve NNET : education_state ~ factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') CLM : matdep ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(marital_status) + hhsize + factor(housing_tenure) + factor(urban) + factor(financial_situation) ZIP : ncigs ~ ncigs + age + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + relevel(factor(job_sec), ref = '3') + scale(hh_income) + relevel(factor(ethnicity), ref = 'WBI') | I(factor(ncigs>0)) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(job_sec), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) -GLMM : SF_12_PCS ~ scale(SF_12_PCS_last) + I(scale(SF_12_PCS_last)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + factor(housing_quality) + scale(hh_income) + factor(neighbourhood_safety) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(financial_situation) + factor(active) + factor(auditc) + factor(chron_disease) + (1|pidp) +GLMM : SF_12_PCS ~ scale(SF_12_PCS_last) + I(scale(SF_12_PCS_last)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + factor(housing_quality) + scale(hh_income) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(active) + factor(auditc) + factor(chron_disease) + (1|pidp) GLMM : SF_12_MCS ~ scale(SF_12_MCS_last) + I(scale(SF_12_MCS_last)**2) + scale(SF_12_MCS_diff) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + factor(housing_quality) + scale(hh_income) + factor(neighbourhood_safety) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(financial_situation) + factor(active) + factor(chron_disease) + (1|pidp) From cdd55a5ef37991341b7a65a406021aba5217b4b4 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 30 Oct 2023 16:54:03 +0000 Subject: [PATCH 054/229] Implemented utility score and QALY calculations, using the Energy Crises interventions as a test comparison --- Makefile | 1 + minos/outcomes/Makefile | 34 ++++- minos/outcomes/QALY_calculation.py | 141 ++++++++++++++++++ minos/outcomes/aggregate_minos_output.py | 6 +- .../testing/QALY_comparison_energyCrises.Rmd | 97 ++++++++++++ minos/utils.py | 33 ++++ 6 files changed, 305 insertions(+), 7 deletions(-) create mode 100644 minos/outcomes/QALY_calculation.py create mode 100644 minos/testing/QALY_comparison_energyCrises.Rmd diff --git a/Makefile b/Makefile index 92c9fefb..bd88db62 100644 --- a/Makefile +++ b/Makefile @@ -21,6 +21,7 @@ DATAOUT = $(CURDIR)/output CONFIG = $(CURDIR)/config TRANSITION_DATA = $(DATADIR)/transitions PLOTDIR = $(CURDIR)/plots +TESTING = $(CURDIR)/minos/testing # These paths point to the Python/R site-packages directory in the conda environment SITEPACKAGES = $(shell python3 -c 'from distutils.sysconfig import get_python_lib; print(get_python_lib())') diff --git a/minos/outcomes/Makefile b/minos/outcomes/Makefile index 256ec37f..9c026ae4 100644 --- a/minos/outcomes/Makefile +++ b/minos/outcomes/Makefile @@ -1,11 +1,37 @@ +OUTCOMES=$(ROOT)/minos/outcomes #TODO: Now that the makefile is split and in the same directory as its scripts, can we do relative paths? -test_plot: MODE=default_config -test_plot: ALIVE=who_alive -test_plot: BOOSTED=who_boosted -test_plot: aggregate_minos_output_energy +##################################### +# QALY Calculations +##################################### + +.phony: QALY QALY_baseline QALY_energyDownlift QALY_energyDownliftNoSupport + +QALY_baseline: MODE=default_config +QALY_baseline: INTERVENTION=baseline +QALY_baseline: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_energyDownlift: MODE=default_config +QALY_energyDownlift: INTERVENTION=energyDownlift +QALY_energyDownlift: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_energyDownliftNoSupport: MODE=default_config +QALY_energyDownliftNoSupport: INTERVENTION=energyDownliftNoSupport +QALY_energyDownliftNoSupport: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_comparison_energyCrises: QALY_baseline QALY_energyDownlift QALY_energyDownliftNoSupport + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises.Rmd')" + firefox file://$(TESTING)/QALY_comparison_energyCrises.html + +#test_plot: MODE=default_config +#test_plot: ALIVE=who_alive +#test_plot: BOOSTED=who_boosted +#test_plot: aggregate_minos_output_energy ##################################### # Post-hoc aggregation of multiple MINOS runs on bash terminal. diff --git a/minos/outcomes/QALY_calculation.py b/minos/outcomes/QALY_calculation.py new file mode 100644 index 00000000..08d6e734 --- /dev/null +++ b/minos/outcomes/QALY_calculation.py @@ -0,0 +1,141 @@ +""" +author: Luke Archer +date: 30/10/23 + +This script will calculate the QALYs for each year for a specific intervention (or baseline). First this will just be +at the whole population level, but will be expanded soon to allow for calculations within specified groups. +""" + +import argparse +import pandas as pd +import numpy as np +import os +import yaml +from multiprocessing import Pool +from itertools import repeat +import glob as glob +from aggregate_subset_functions import dynamic_subset_function + +import minos.utils as utils + + +def aggregate_csv(filename): + """ + + Parameters + ---------- + filename + v + agg_method + subset_func_string + mode + + Returns + ------- + + """ + df = pd.read_csv(filename, low_memory=False) + #if subset_func_string: + #df = dynamic_subset_function(df, subset_func_string, mode) + #print(f"For substring chain {subset_func_string} there are {df.shape[0]} eligible individuals in the dataset.") + + pop_size = df['alive'].value_counts()['alive'] + + return [pop_size, np.nanmean(df['SF_12_MCS']), np.nanmean(df['SF_12_PCS'])] + + +def calculate_qaly(df): + """ + QALY calculation comes from Lawrence and Fleishman (2004) - https://pubmed.ncbi.nlm.nih.gov/15090102/ + + In table 4 of the above paper, regression model coefficients were presented which allow the mapping of MCS and PCS + scores onto EQ-5D, from which we can calculate utility scores. + + From the utility scores we can calculate QALYs by multiplying the utility score by the population size (alive). + + Parameters + ---------- + df + + Returns + ------- + + """ + + # Run without any subpopulations to worry about + + # First calculate utility score using values table 4 from Lawrence and Fleishman (2004) + df['utility'] = -1.6984 + \ + (df['SF_12_PCS'] * 0.07927) + \ + (df['SF_12_MCS'] * 0.02859) + \ + ((df['SF_12_PCS'] * df['SF_12_MCS']) * -0.000126) + \ + ((df['SF_12_PCS'] * df['SF_12_PCS']) * -0.00141) + \ + ((df['SF_12_MCS'] * df['SF_12_MCS']) * -0.00014) + \ + ((df['SF_12_PCS'] * df['SF_12_MCS'] * df['SF_12_PCS']) * 0.0000107) + + # Now calculate QALYs by multiplying utility score by pop_size + df['QALYs'] = df['utility'] * df['pop_size'] + + return df + + +def main(mode, intervention): + + # set file directory + file_dir = os.path.join('output/', mode, intervention) + runtime_list = os.listdir(os.path.abspath(file_dir)) + runtime = utils.get_latest_subdirectory(runtime_list) + + batch_source = os.path.join(file_dir, runtime) + # batch_source = os.path.join(source, directory) + # get years from MINOS batch run config yaml. + with open(f"{batch_source}/config_file.yml", "r") as stream: + config = yaml.safe_load(stream) + start_year = config['time']['start']['year'] + end_year = config['time']['end']['year'] + years = np.arange(start_year, end_year) + + combined_output = pd.DataFrame() + # use multiprocessing to read in files and aggregating + for year in years+1: + files = glob.glob(os.path.join(batch_source, f"*{year}.csv")) # grab all files at source with suffix year.csv. + + # 2018 is special case - not simulated yet and therefore doesn't have any of the tags for subset functions + # Therefore we are just going to get everyone alive for now + # TODO: Set this value from the config file so it only happens for the year before simulation (currently 2020) and isn't hardcoded + with Pool() as pool: + aggregated_means = pool.starmap(aggregate_csv, zip(files)) + + new_df = pd.DataFrame(aggregated_means) + new_df.columns = ['pop_size', 'SF_12_MCS', 'SF_12_PCS'] + new_df['year'] = year + new_df['intervention'] = intervention + combined_output = pd.concat([combined_output, new_df]) + print(f'Finished aggregating data for year {year}...') + + print('Finished aggregating data...') + + qaly_df = calculate_qaly(combined_output) + + # finally, save qaly df into output directory + out_name = os.path.join(batch_source, 'qalys.csv') + qaly_df.to_csv(out_name) + + +if __name__ == '__main__': + + parser = argparse.ArgumentParser(description="Arguments for calculation QALYs over different experiments or " + "sub-populations.") + + parser.add_argument("-m", "--mode", required=True, + help="Which experiment are we calculating for? Options currently are default_config, SIPHER7," + "and SIPHER7_glasgow.") + parser.add_argument("-i", "--intervention", required=False, default="baseline", + help="Is this a baseline or intervention run? Which intervention if intervention?") + + args = parser.parse_args() + + mode = args.mode + intervention = args.intervention + + main(mode, intervention) diff --git a/minos/outcomes/aggregate_minos_output.py b/minos/outcomes/aggregate_minos_output.py index 97973e01..ffc5786d 100755 --- a/minos/outcomes/aggregate_minos_output.py +++ b/minos/outcomes/aggregate_minos_output.py @@ -136,9 +136,9 @@ def main(source, mode, years, tag, v, method, subset_function_string): if method == "nanmean": method = np.nanmean else: - #TODO no better way to do this to my knowledge without eval() which shouldn't be used. + # TODO no better way to do this to my knowledge without eval() which shouldn't be used. raise ValueError("Unknown aggregate function specified. Please add specifc function required at 'aggregate_minos_output.py") - #TODO replace this if...else... with a try...except block around the main function below. + # TODO replace this if...else... with a try...except block around the main function below. directories = directories.split(",") @@ -149,7 +149,7 @@ def main(source, mode, years, tag, v, method, subset_function_string): # Handle the datetime folder inside the output. Select most recent run runtime = os.listdir(os.path.abspath(os.path.join('output/', mode, directory))) - #TODO: Replace this block (or encapsulate) in a try except block for proper error handling + # TODO: Replace this block (or encapsulate) in a try except block for proper error handling if len(runtime) > 1: # if more than 1, select most recent datetime runtime = max(runtime, key=lambda d: datetime.strptime(d, "%Y_%m_%d_%H_%M_%S")) diff --git a/minos/testing/QALY_comparison_energyCrises.Rmd b/minos/testing/QALY_comparison_energyCrises.Rmd new file mode 100644 index 00000000..7d909044 --- /dev/null +++ b/minos/testing/QALY_comparison_energyCrises.Rmd @@ -0,0 +1,97 @@ +--- +title: "QALY Comparisons - Energy Crisis Interventions" +output: + html_document: + toc: true + toc_float: true + collapsed: false + number_sections: true + df_print: paged + code_folding: hide +--- + +# SETUP + +```{r "setup", include=FALSE} + +require(tidyverse) +require(ggplot2) +require(knitr) +require(here) +require(MESS) + +#workingDir <- "/home/luke/Documents/WORK/MINOS/" +workingDir <- normalizePath('../') +knitr::opts_knit$set(root.dir = workingDir) +knitr::opts_chunk$set( +) +rm(workingDir) +``` + +```{r} +source(here::here('minos', 'utils_datain.R')) +``` + + +## Data + +```{r} +# read in QALY files for baseline and interventions +out.path <- here::here('output', 'default_config') + +base.path <- get_latest_runtime_subdirectory(here::here(out.path, 'baseline')) +support.path <- get_latest_runtime_subdirectory(here::here(out.path, 'energyDownlift')) +noSupport.path <- get_latest_runtime_subdirectory(here::here(out.path, 'energyDownliftNoSupport')) + +base <- read.csv(paste0(base.path, 'qalys.csv')) +support <- read.csv(paste0(support.path, 'qalys.csv')) +noSupport <- read.csv(paste0(noSupport.path, 'qalys.csv')) +``` + +# QALY comparison + +```{r} +# combine the dataframes +combined <- rbind(base, support, noSupport) + +ggplot(data = combined, aes(x = year, y = QALYs, group = intervention, colour = intervention)) + + geom_line() +``` + +## Area Under the Curve Comparison + +```{r} +#base.auc <- sum(diff(base$time)*rollmean(base$QALYs, 2)) +base.auc <- data.frame(intervention = 'baseline', AUC = auc(x=base$year, y=base$QALYs, type = 'spline')) +support.auc <- data.frame(intervention = 'Support', AUC = auc(x=support$year, y=support$QALYs, type = 'spline')) +noSupport.auc <- data.frame(intervention = 'NoSupport', AUC = auc(x=noSupport$year, y=noSupport$QALYs, type = 'spline')) + +combined.auc <- rbind(base.auc, support.auc, noSupport.auc) +``` + +```{r} +print(combined.auc) + +ggplot(data = combined.auc, aes(x = intervention, y = AUC, group = intervention, fill = intervention)) + + geom_col() +``` + +```{r} +# Another version showing difference from baseline for better comparison +combined.auc2 <- combined.auc %>% + pivot_wider(names_from = 'intervention', + values_from = 'AUC') %>% + mutate(change_baseline = baseline - baseline, + change_Support = Support - baseline, + change_noSupport = NoSupport - baseline) %>% + pivot_longer(cols = change_baseline:change_noSupport, + names_to = 'intervention', + names_prefix = 'change_', + values_to = 'AUC_difference') + +print(combined.auc2) + +ggplot(combined.auc2, aes(x = intervention, y = AUC_difference, group = intervention, fill = intervention)) + + geom_col() +``` + diff --git a/minos/utils.py b/minos/utils.py index 5f5d05d2..64cf2733 100755 --- a/minos/utils.py +++ b/minos/utils.py @@ -40,6 +40,39 @@ def read_config(config_file): return config +def get_latest_subdirectory(runtime_list): + """ + This function will return the latest runtime subdirectory when runtime_list is a list with len > 1. In the case that + runtime_list is a list with only a single element, then that single element will be returned (as this indicates + that there is only a single subdirectory to choose from. + + This function is required for generating outcomes based on the latest run of data, and can be modified if specific + runs are required that are not the latest (although this will probably never be necessary). + + Parameters + ---------- + runtime_list : list + List of runtime subdirectories, which are all datetime values that were allocated the exact time when a run + began. + + Returns + ------- + runtime : string + The latest (or only) runtime string for creating the directory when loading data from a Minos run. + """ + + if len(runtime_list) > 1: + # if more than 1, select most recent datetime + runtime = max(runtime_list, key=lambda d: datetime.strptime(d, "%Y_%m_%d_%H_%M_%S")) + elif len(runtime_list) == 1: + runtime = runtime_list[0] # os.listdir returns a list, we only have 1 element + else: + raise RuntimeError("The output directory supplied contains no subdirectories, and therefore no data to " + "aggregate. Please check the output directory.") + + return runtime + + # TODO Investigate the mock artifact manager. Not sure if this is what we should be using. def base_plugins(): config = {'required': { From 22eff99bd85e6340d6c3863e29a699212bc26cca Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 31 Oct 2023 14:51:22 +0000 Subject: [PATCH 055/229] Added cost effectiveness calculations to QALY comparison energyCrises testing script. Added Make target to run QALY calculation for living wage intervention. Added MESS package to environment.yml --- environment.yml | 1 + minos/outcomes/Makefile | 9 ++ minos/outcomes/QALY_calculation.py | 17 ++- .../testing/QALY_comparison_energyCrises.Rmd | 135 +++++++++++++++++- 4 files changed, 152 insertions(+), 10 deletions(-) diff --git a/environment.yml b/environment.yml index ef4ab2c8..961a6bd7 100644 --- a/environment.yml +++ b/environment.yml @@ -29,6 +29,7 @@ dependencies: # Added pip and python at top to avoid conda giving grumpy warning - sphinx - myst-parser - r-glmmTMB + - r-MESS - pip: # Same packages as were in "requirements.txt" except minos and vivarium, as both installed during "make install" - rpy2 - pyyaml>=5.4 diff --git a/minos/outcomes/Makefile b/minos/outcomes/Makefile index 9c026ae4..a62427a6 100644 --- a/minos/outcomes/Makefile +++ b/minos/outcomes/Makefile @@ -24,10 +24,19 @@ QALY_energyDownliftNoSupport: INTERVENTION=energyDownliftNoSupport QALY_energyDownliftNoSupport: python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) +QALY_livingWage: MODE=default_config +QALY_livingWage: INTERVENTION=livingWageIntervention +QALY_livingWage: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + QALY_comparison_energyCrises: QALY_baseline QALY_energyDownlift QALY_energyDownliftNoSupport $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises.Rmd')" firefox file://$(TESTING)/QALY_comparison_energyCrises.html +QALY_comparison_livingWage: QALY_baseline QALY_livingWage + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" + firefox file://$(TESTING)/QALY_comparison_livingWage.html + #test_plot: MODE=default_config #test_plot: ALIVE=who_alive #test_plot: BOOSTED=who_boosted diff --git a/minos/outcomes/QALY_calculation.py b/minos/outcomes/QALY_calculation.py index 08d6e734..fedfa6ad 100644 --- a/minos/outcomes/QALY_calculation.py +++ b/minos/outcomes/QALY_calculation.py @@ -19,7 +19,7 @@ import minos.utils as utils -def aggregate_csv(filename): +def aggregate_csv(filename, intervention): """ Parameters @@ -41,7 +41,12 @@ def aggregate_csv(filename): pop_size = df['alive'].value_counts()['alive'] - return [pop_size, np.nanmean(df['SF_12_MCS']), np.nanmean(df['SF_12_PCS'])] + if intervention == 'baseline': + total_boost = 0 + else: + total_boost = df['boost_amount'].sum() + + return [pop_size, total_boost, np.nanmean(df['SF_12_MCS']), np.nanmean(df['SF_12_PCS'])] def calculate_qaly(df): @@ -100,14 +105,12 @@ def main(mode, intervention): for year in years+1: files = glob.glob(os.path.join(batch_source, f"*{year}.csv")) # grab all files at source with suffix year.csv. - # 2018 is special case - not simulated yet and therefore doesn't have any of the tags for subset functions - # Therefore we are just going to get everyone alive for now - # TODO: Set this value from the config file so it only happens for the year before simulation (currently 2020) and isn't hardcoded + # aggregate the files using multiprocessing with Pool() as pool: - aggregated_means = pool.starmap(aggregate_csv, zip(files)) + aggregated_means = pool.starmap(aggregate_csv, zip(files, repeat(intervention))) new_df = pd.DataFrame(aggregated_means) - new_df.columns = ['pop_size', 'SF_12_MCS', 'SF_12_PCS'] + new_df.columns = ['pop_size', 'total_boost', 'SF_12_MCS', 'SF_12_PCS'] new_df['year'] = year new_df['intervention'] = intervention combined_output = pd.concat([combined_output, new_df]) diff --git a/minos/testing/QALY_comparison_energyCrises.Rmd b/minos/testing/QALY_comparison_energyCrises.Rmd index 7d909044..7ea85c28 100644 --- a/minos/testing/QALY_comparison_energyCrises.Rmd +++ b/minos/testing/QALY_comparison_energyCrises.Rmd @@ -36,7 +36,7 @@ source(here::here('minos', 'utils_datain.R')) ## Data ```{r} -# read in QALY files for baseline and interventions +# # read in QALY files for baseline and interventions out.path <- here::here('output', 'default_config') base.path <- get_latest_runtime_subdirectory(here::here(out.path, 'baseline')) @@ -55,7 +55,24 @@ noSupport <- read.csv(paste0(noSupport.path, 'qalys.csv')) combined <- rbind(base, support, noSupport) ggplot(data = combined, aes(x = year, y = QALYs, group = intervention, colour = intervention)) + - geom_line() + geom_smooth() +``` + +## Groupby and Mean + +```{r} +base <- base %>% + group_by(year) %>% + summarise(QALYs = mean(QALYs), + boost_amount = mean(total_boost)) +support <- support %>% + group_by(year) %>% + summarise(QALYs = mean(QALYs), + boost_amount = mean(total_boost)) +noSupport <- noSupport %>% + group_by(year) %>% + summarise(QALYs = mean(QALYs), + boost_amount = mean(total_boost)) ``` ## Area Under the Curve Comparison @@ -80,7 +97,7 @@ ggplot(data = combined.auc, aes(x = intervention, y = AUC, group = intervention, # Another version showing difference from baseline for better comparison combined.auc2 <- combined.auc %>% pivot_wider(names_from = 'intervention', - values_from = 'AUC') %>% + values_from = c('AUC')) %>% mutate(change_baseline = baseline - baseline, change_Support = Support - baseline, change_noSupport = NoSupport - baseline) %>% @@ -95,3 +112,115 @@ ggplot(combined.auc2, aes(x = intervention, y = AUC_difference, group = interven geom_col() ``` +# Cost per QALY + +The [UK Government](https://www.gov.uk/government/publications/valuation-of-risks-to-life-and-health-monetary-value-of-a-life-year-voly/a-scoping-study-on-the-valuation-of-risks-to-life-and-health-the-monetary-value-of-a-life-year-voly) has set the value of 1 QALY to be equal to $60,000. This is the number we will use for our calculations. + +```{r} + +combined.cost <- combined %>% + select(-X, -pop_size, -SF_12_MCS, -SF_12_PCS, -utility) %>% + mutate(QALY_value = QALYs * 60000) + +ggplot(combined.cost, aes(x = year, y = QALY_value, group = intervention, colour = intervention)) + + geom_line() + +combined.cost.change <- combined.cost %>% + select(-total_boost, -QALYs) %>% + pivot_wider(names_from = 'intervention', + values_from = 'QALY_value') %>% + mutate(change_Support = energyDownlift - baseline, + change_NoSupport = energyDownliftNoSupport - baseline) %>% + select(-baseline, -energyDownlift, -energyDownliftNoSupport) %>% + pivot_longer(cols = -year, + names_prefix = 'change_', + names_to = 'intervention', + values_to = 'value_change') + +ggplot(combined.cost.change, aes(x = year, y = value_change, group = intervention, colour = intervention)) + + geom_line() + + + + # select(-QALYs) %>% + # pivot_wider(names_from = 'intervention', + # values_from = c('total_boost', 'QALY_value'), + # names_sep = '000') %>% + # mutate(QALY_value_change000energyDownlift = QALY_value000energyDownlift - QALY_value000baseline, + # QALY_value_change000energyDownliftNoSupport = QALY_value000energyDownliftNoSupport - QALY_value000baseline) %>% + # select(-QALY_value000baseline, -QALY_value000energyDownlift, -QALY_value000energyDownliftNoSupport, -total_boost000baseline) %>% + # pivot_longer(cols = total_boost000energyDownlift:QALY_value_change000energyDownliftNoSupport, + # names_sep = '000', + # names_to = c('.value', 'intervention'), + # names_repair = 'unique') #%>% + # pivot_longer(cols = QALY_value_change_Support:QALY_value_change_NoSupport, + # names_prefix = 'QALY_value_change_', + # names_to = 'intervention2', + # values_to = 'QALY_value_change') + +# combined.cost <- combined %>% +# select(-X, -pop_size, -SF_12_MCS, -SF_12_PCS, -utility) %>% +# pivot_wider(names_from = 'intervention', +# values_from = c('mean_boost', 'QALYs')) %>% +# mutate(change_energyDownlift = QALYs_energyDownlift - QALYs_baseline, +# change_energyDownliftNoSupport = QALYs_energyDownliftNoSupport - QALYs_baseline) %>% +# mutate(costPerQALY_energyDownlift = change_energyDownlift / mean_boost_energyDownlift, +# costPerQALY_energyDownliftNoSupport = change_energyDownliftNoSupport / mean_boost_energyDownliftNoSupport) %>% +# pivot_longer(cols = costPerQALY_energyDownlift:costPerQALY_energyDownliftNoSupport, +# names_prefix = 'costPerQALY_', +# names_to = 'intervention', +# values_to = 'costPerQALY') %>% +# select(year, intervention, costPerQALY) + +# options(scipen=999) +# +# ggplot(data = combined.cost, aes(x = year, y = costPerQALY, group = intervention, colour = intervention)) + +# geom_line() +``` + +```{r} +total.combined.cost <- combined.cost %>% + group_by(intervention) %>% + summarise(total_value_change = sum(value_change)) + +ggplot(data = total.combined.cost, aes(x = intervention, y = total_value_change, group = intervention, fill = intervention)) + + geom_col() +``` + +# Incremental Cost-Effectiveness Ratio + +From [NICE guidelines](https://www.nice.org.uk/process/pmg6/chapter/assessing-cost-effectiveness) about the cost-effectiveness of health interventions (7.3), and works with a statistic called the Incremental Cost-Effectiveness Ratio (ICER). The formula for calculating the ICER can be found [here](https://en.wikipedia.org/wiki/Incremental_cost-effectiveness_ratio), but in short, it represents the average incremental cost associated with 1 additional unit of the measurement of effect. In our case, the unit of measurement of effect is a QALY. + +From the NICE guidelines, there is a general consensus that anything below £20,000 is cost effective, and anything above £30,000 is not. Between £20,000 and £30,000 is a grey area, and depends on the field and other factors to decide (i.e. if a rare or important condition is tackled by an intervention then the cost-effective threshold is higher). + + +```{r} +# ICER = (C1 - C0) / (E1 - E0) +# where +# C1 = cost of intervention +# E1 = effect of intervention +# C0 = cost of control group +# E0 = effect of control group + +ICER <- combined.cost %>% + pivot_wider(names_from = 'intervention', + values_from = c('total_boost', 'QALYs', 'QALY_value')) %>% + mutate(ICER_Support = (total_boost_energyDownlift - total_boost_baseline) / (QALYs_energyDownlift - QALYs_baseline), + ICER_NoSupport = (total_boost_energyDownliftNoSupport - total_boost_baseline) / (QALYs_energyDownliftNoSupport - QALYs_baseline)) %>% + select(year, ICER_Support, ICER_NoSupport) %>% + pivot_longer(cols = -year, + names_prefix = 'ICER_', + names_to = 'intervention', + values_to = 'ICER') + +ggplot(ICER, aes(x = year, y = ICER, group = intervention, colour = intervention)) + + geom_line() + +ICER.final <- ICER %>% + group_by(intervention) %>% + summarise(ICER = mean(ICER)) + +ggplot(ICER.final, aes(x = intervention, y = ICER, group = intervention, fill = intervention, colour = intervention)) + + geom_col() +``` + From 875260d5be344fbc2a440b2a8eb7bf03a66447b4 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Wed, 1 Nov 2023 11:27:18 +0000 Subject: [PATCH 056/229] Added run_id to QALY_calculation outputs for plotting confidence. Improved script for comparing energy crises interventions, and added new script for QALY calculations on living wage intervention. Also fixed function to get runtime subdirectory path in utils --- minos/outcomes/QALY_calculation.py | 23 +- .../testing/QALY_comparison_energyCrises.Rmd | 58 ++++- minos/testing/QALY_comparison_livingWage.Rmd | 223 ++++++++++++++++++ minos/utils.py | 2 +- scripts/local_batch.sh | 2 +- 5 files changed, 290 insertions(+), 18 deletions(-) create mode 100644 minos/testing/QALY_comparison_livingWage.Rmd diff --git a/minos/outcomes/QALY_calculation.py b/minos/outcomes/QALY_calculation.py index fedfa6ad..3f95c75c 100644 --- a/minos/outcomes/QALY_calculation.py +++ b/minos/outcomes/QALY_calculation.py @@ -39,14 +39,25 @@ def aggregate_csv(filename, intervention): #df = dynamic_subset_function(df, subset_func_string, mode) #print(f"For substring chain {subset_func_string} there are {df.shape[0]} eligible individuals in the dataset.") + # get the run_id from the filename and attach to the dataset (if batch run) + filename_nopath = filename.split(sep='/')[-1] + if filename_nopath.count('_') == 0: + # If no underscore in filename, this is not a batch run and run_id can be set to 1 + run_id = 1 # 0 would be more pythonic but the batch runs start at 1 so I'm copying that + else: + # If underscores present, take run_id from filename + run_id = filename_nopath.split(sep='_')[0].lstrip('0') + + # record size of not dead population in year pop_size = df['alive'].value_counts()['alive'] + # record total_boost amount for intervention runs, set to 0 for baseline if intervention == 'baseline': total_boost = 0 else: total_boost = df['boost_amount'].sum() - return [pop_size, total_boost, np.nanmean(df['SF_12_MCS']), np.nanmean(df['SF_12_PCS'])] + return [run_id, pop_size, total_boost, np.nanmean(df['SF_12_MCS']), np.nanmean(df['SF_12_PCS'])] def calculate_qaly(df): @@ -110,7 +121,7 @@ def main(mode, intervention): aggregated_means = pool.starmap(aggregate_csv, zip(files, repeat(intervention))) new_df = pd.DataFrame(aggregated_means) - new_df.columns = ['pop_size', 'total_boost', 'SF_12_MCS', 'SF_12_PCS'] + new_df.columns = ['run_id', 'pop_size', 'total_boost', 'SF_12_MCS', 'SF_12_PCS'] new_df['year'] = year new_df['intervention'] = intervention combined_output = pd.concat([combined_output, new_df]) @@ -121,8 +132,14 @@ def main(mode, intervention): qaly_df = calculate_qaly(combined_output) # finally, save qaly df into output directory + # reorder columns first and sort by run_id and year + # reorder first + cols_to_front = ['run_id', 'year'] + qaly_df = qaly_df[cols_to_front + [col for col in qaly_df.columns if col not in cols_to_front]] + # now sort + qaly_df = qaly_df.sort_values(['run_id', 'year'], ascending=True) out_name = os.path.join(batch_source, 'qalys.csv') - qaly_df.to_csv(out_name) + qaly_df.to_csv(out_name, index=False) if __name__ == '__main__': diff --git a/minos/testing/QALY_comparison_energyCrises.Rmd b/minos/testing/QALY_comparison_energyCrises.Rmd index 7ea85c28..de6ad858 100644 --- a/minos/testing/QALY_comparison_energyCrises.Rmd +++ b/minos/testing/QALY_comparison_energyCrises.Rmd @@ -46,6 +46,12 @@ noSupport.path <- get_latest_runtime_subdirectory(here::here(out.path, 'energyDo base <- read.csv(paste0(base.path, 'qalys.csv')) support <- read.csv(paste0(support.path, 'qalys.csv')) noSupport <- read.csv(paste0(noSupport.path, 'qalys.csv')) + +# out.path <- '/home/luke/Documents/WORK/MINOS/PLOTS_FROM_ARC/QALY_TESTING/' +# +# base <- read.csv(paste0(out.path, 'qaly_baseline.csv')) +# support <- read.csv(paste0(out.path, 'qalys_support.csv')) +# noSupport <- read.csv(paste0(out.path, 'qalys_noSupport.csv')) ``` # QALY comparison @@ -123,22 +129,22 @@ combined.cost <- combined %>% mutate(QALY_value = QALYs * 60000) ggplot(combined.cost, aes(x = year, y = QALY_value, group = intervention, colour = intervention)) + - geom_line() + geom_smooth() combined.cost.change <- combined.cost %>% select(-total_boost, -QALYs) %>% pivot_wider(names_from = 'intervention', - values_from = 'QALY_value') %>% - mutate(change_Support = energyDownlift - baseline, - change_NoSupport = energyDownliftNoSupport - baseline) %>% - select(-baseline, -energyDownlift, -energyDownliftNoSupport) %>% - pivot_longer(cols = -year, - names_prefix = 'change_', - names_to = 'intervention', - values_to = 'value_change') + values_from = 'QALY_value') #%>% + # mutate(change_Support = energyDownlift - baseline, + # change_NoSupport = energyDownliftNoSupport - baseline) %>% + # select(-baseline, -energyDownlift, -energyDownliftNoSupport) %>% + # pivot_longer(cols = -year, + # names_prefix = 'change_', + # names_to = 'intervention', + # values_to = 'value_change') ggplot(combined.cost.change, aes(x = year, y = value_change, group = intervention, colour = intervention)) + - geom_line() + geom_smooth() @@ -179,7 +185,11 @@ ggplot(combined.cost.change, aes(x = year, y = value_change, group = interventio ``` ```{r} -total.combined.cost <- combined.cost %>% +combined.cost.mean <- combined.cost %>% + group_by(year, intervention) %>% + summarise(across(everything(), mean)) + +total.combined.cost <- combined.cost.change %>% group_by(intervention) %>% summarise(total_value_change = sum(value_change)) @@ -202,12 +212,12 @@ From the NICE guidelines, there is a general consensus that anything below £20, # C0 = cost of control group # E0 = effect of control group -ICER <- combined.cost %>% +ICER_test2 <- combined.cost.mean %>% pivot_wider(names_from = 'intervention', values_from = c('total_boost', 'QALYs', 'QALY_value')) %>% mutate(ICER_Support = (total_boost_energyDownlift - total_boost_baseline) / (QALYs_energyDownlift - QALYs_baseline), ICER_NoSupport = (total_boost_energyDownliftNoSupport - total_boost_baseline) / (QALYs_energyDownliftNoSupport - QALYs_baseline)) %>% - select(year, ICER_Support, ICER_NoSupport) %>% +select(year, ICER_Support, ICER_NoSupport) %>% pivot_longer(cols = -year, names_prefix = 'ICER_', names_to = 'intervention', @@ -222,5 +232,27 @@ ICER.final <- ICER %>% ggplot(ICER.final, aes(x = intervention, y = ICER, group = intervention, fill = intervention, colour = intervention)) + geom_col() + + +ICER_test3 <- combined.cost.mean %>% + pivot_wider(names_from = 'intervention', + values_from = c('total_boost', 'QALYs', 'QALY_value')) %>% + mutate(C_Support = total_boost_energyDownlift - total_boost_baseline, + E_Support = QALYs_energyDownlift - QALYs_baseline, + C_NoSupport = total_boost_energyDownliftNoSupport - total_boost_baseline, + E_NoSupport = QALYs_energyDownliftNoSupport - QALYs_baseline) %>% + select(year, C_Support, E_Support, C_NoSupport, E_NoSupport) %>% + mutate(ICER_Support = C_Support / E_Support, + ICER_NoSupport = C_NoSupport / E_NoSupport)# %>% + #select(year, ICER_Support, ICER_NoSupport)# %>% +# pivot_longer(cols = -year, +# names_prefix = 'ICER_', +# names_to = 'intervention', +# values_to = 'ICER') + +``` + +```{r} + ``` diff --git a/minos/testing/QALY_comparison_livingWage.Rmd b/minos/testing/QALY_comparison_livingWage.Rmd new file mode 100644 index 00000000..af73477d --- /dev/null +++ b/minos/testing/QALY_comparison_livingWage.Rmd @@ -0,0 +1,223 @@ +--- +title: "QALY Comparison - Living Wage Intervention" +output: + html_document: + toc: true + toc_float: true + collapsed: false + number_sections: true + df_print: paged + code_folding: hide +--- + +# SETUP + +```{r "setup", include=FALSE} + +require(tidyverse) +require(ggplot2) +require(knitr) +require(here) +require(MESS) +require(scales) + +#workingDir <- "/home/luke/Documents/WORK/MINOS/" +workingDir <- normalizePath('../') +knitr::opts_knit$set(root.dir = workingDir) +knitr::opts_chunk$set( + options(scipen=999) +) +rm(workingDir) +``` + +```{r} +source(here::here('minos', 'utils_datain.R')) +``` + + +## Data + +```{r} +# # read in QALY files for baseline and interventions +out.path <- here::here('output', 'default_config') + +base.path <- get_latest_runtime_subdirectory(here::here(out.path, 'baseline')) +wage.path <- get_latest_runtime_subdirectory(here::here(out.path, 'livingWageIntervention')) + +base <- read.csv(paste0(base.path, 'qalys.csv')) +wage <- read.csv(paste0(wage.path, 'qalys.csv')) + +# out.path <- '/home/luke/Documents/WORK/MINOS/PLOTS_FROM_ARC/QALY_TESTING/' +# +# base <- read.csv(paste0(out.path, 'qaly_baseline.csv')) +# support <- read.csv(paste0(out.path, 'qalys_support.csv')) +# noSupport <- read.csv(paste0(out.path, 'qalys_noSupport.csv')) +``` + + +# QALY comparison + +```{r} +# combine the dataframes +combined <- rbind(base, wage) + +ggplot(data = combined, aes(x = year, y = QALYs, group = intervention, colour = intervention)) + + geom_smooth() +``` + +...and SF_12 MCS and PCS for good measure. + +```{r} +ggplot(combined, aes(x = year, y = SF_12_MCS, group = intervention, colour = intervention)) + + geom_smooth() + +ggplot(combined, aes(x = year, y = SF_12_PCS, group = intervention, colour = intervention)) + + geom_smooth() +``` + +## Area Under the Curve Comparison + +```{r} +base.auc <- base %>% + group_by(run_id) %>% + summarise(AUC = auc(x=year, y = QALYs, type = 'spline')) %>% + mutate(intervention = 'baseline') + +wage.auc <- wage %>% + group_by(run_id) %>% + summarise(AUC = auc(x=year, y = QALYs, type = 'spline')) %>% + mutate(intervention = 'LivingWage') + +combined.auc <- rbind(base.auc, wage.auc) + +# #base.auc <- sum(diff(base$time)*rollmean(base$QALYs, 2)) +# base.auc <- data.frame(intervention = 'baseline', AUC = auc(x=base$year, y=base$QALYs, type = 'spline')) +# support.auc <- data.frame(intervention = 'Support', AUC = auc(x=support$year, y=support$QALYs, type = 'spline')) +# noSupport.auc <- data.frame(intervention = 'NoSupport', AUC = auc(x=noSupport$year, y=noSupport$QALYs, type = 'spline')) +# +# combined.auc <- rbind(base.auc, support.auc, noSupport.auc) +``` + +```{r} +print(combined.auc) + +ggplot(data = combined.auc, aes(x = intervention, y = AUC, group = intervention, fill = intervention)) + + stat_summary(fun = 'mean', geom = 'bar') + + geom_errorbar(aes(ymin = min(AUC), ymax = max(AUC), width = 0.4)) + + labs(title = 'Total AUC comparison', subtitle = 'Baseline vs Living Wage Intervention') + + xlab('Intervention') +``` + +```{r} +# Another version showing difference from baseline for better comparison +combined.auc2 <- combined.auc %>% + pivot_wider(names_from = 'intervention', + values_from = c('AUC')) %>% + mutate(change_baseline = baseline - baseline, + change_LivingWage = LivingWage - baseline) %>% + pivot_longer(cols = change_baseline:change_LivingWage, + names_to = 'intervention', + names_prefix = 'change_', + values_to = 'AUC_difference') %>% + filter(intervention != 'baseline') + +print(combined.auc2) + +ggplot(combined.auc2, aes(x = intervention, y = AUC_difference, group = intervention, fill = intervention)) + + stat_summary(fun = 'mean', geom = 'bar') + + geom_errorbar(aes(ymin = min(AUC_difference), ymax = max(AUC_difference), width = 0.4)) + + labs(title = 'Change in AUC', subtitle = 'Baseline vs Living Wage Intervention') + + xlab('Intervention') +``` + +# Cost per QALY + +The [UK Government](https://www.gov.uk/government/publications/valuation-of-risks-to-life-and-health-monetary-value-of-a-life-year-voly/a-scoping-study-on-the-valuation-of-risks-to-life-and-health-the-monetary-value-of-a-life-year-voly) has set the value of 1 QALY to be equal to $60,000. This is the number we will use for our calculations. + +```{r} + +# pivot_longer(cols = change_baseline:change_LivingWage, +# names_to = 'intervention', +# names_prefix = 'change_', +# values_to = 'AUC_difference') %>% +# filter(intervention != 'baseline') + +combined.cost <- combined %>% + select(-pop_size, -SF_12_MCS, -SF_12_PCS, -utility) %>% + mutate(QALY_value = QALYs * 60000) + +ggplot(combined.cost, aes(x = year, y = QALY_value, group = intervention, colour = intervention)) + + geom_smooth() + + scale_y_continuous(label = comma) + +combined.cost.change <- combined.cost %>% + select(-total_boost, -QALYs) %>% + pivot_wider(names_from = 'intervention', + values_from = 'QALY_value') %>% + mutate(change_baseline = baseline - baseline, + change_LivingWage = livingWageIntervention - baseline) %>% + select(-baseline, -livingWageIntervention) %>% + pivot_longer(cols = change_baseline:change_LivingWage, + names_to = 'intervention', + names_prefix = 'change_', + values_to = 'QALY_value_change') %>% + filter(intervention != 'baseline') + +ggplot(combined.cost.change, aes(x = year, y = QALY_value_change, group = intervention, fill = intervention)) + + geom_smooth() + + scale_y_continuous(label = comma) +``` + +```{r} +total.combined.cost <- combined.cost.change %>% + group_by(run_id, intervention) %>% + summarise(TotalChange = sum(QALY_value_change)) + +#print(paste0('Total combined cost == ', format(round(as.numeric(total.combined.cost), 1), nsmall=1, big.mark=","))) + +ggplot(total.combined.cost, aes(x = intervention, y = TotalChange, group = intervention, fill = intervention)) + + stat_summary(fun = 'mean', geom = 'bar') + + geom_errorbar(aes(ymin = min(TotalChange), ymax = max(TotalChange), width = 0.4)) + + labs(title = 'Total Change in QALY Value', subtitle = 'Baseline vs Living Wage Intervention') + + xlab('Intervention') +``` + +# Incremental Cost-Effectiveness Ratio + +From [NICE guidelines](https://www.nice.org.uk/process/pmg6/chapter/assessing-cost-effectiveness) about the cost-effectiveness of health interventions (7.3), and works with a statistic called the Incremental Cost-Effectiveness Ratio (ICER). The formula for calculating the ICER can be found [here](https://en.wikipedia.org/wiki/Incremental_cost-effectiveness_ratio), but in short, it represents the average incremental cost associated with 1 additional unit of the measurement of effect. In our case, the unit of measurement of effect is a QALY. + +From the NICE guidelines, there is a general consensus that anything below £20,000 is cost effective, and anything above £30,000 is not. Between £20,000 and £30,000 is a grey area, and depends on the field and other factors to decide (i.e. if a rare or important condition is tackled by an intervention then the cost-effective threshold is higher).There is also a hard limit of £100,000, where any intervention with an ICER above this value is never acceptable. + + +```{r} +combined.cost.mean <- combined.cost %>% + group_by(run_id, year, intervention) %>% + summarise(across(everything(), mean)) + +# ICER = (C1 - C0) / (E1 - E0) +# where +# C1 = cost of intervention +# E1 = effect of intervention +# C0 = cost of control group +# E0 = effect of control group + +ICER <- combined.cost.mean %>% + pivot_wider(names_from = 'intervention', + values_from = c('total_boost', 'QALYs', 'QALY_value')) %>% + mutate(ICER = (total_boost_livingWageIntervention - total_boost_baseline) / (QALYs_livingWageIntervention - QALYs_baseline)) %>% + select(run_id, year, ICER) %>% + mutate(intervention = 'LivingWage') + +ggplot(ICER, aes(x = year, y = ICER)) + + geom_smooth() + +ICER.final <- ICER %>% + group_by(run_id, intervention) %>% + summarise(ICER = mean(ICER)) + +ggplot(ICER.final, aes(x = intervention, y = ICER, group = intervention, fill = intervention)) + + stat_summary(fun = 'mean', geom = 'bar') + + geom_errorbar(aes(ymin = min(ICER), ymax = max(ICER), width = 0.4)) + + labs(title = 'ICER', subtitle = 'Living Wage Intervention') + + xlab('Intervention') +``` \ No newline at end of file diff --git a/minos/utils.py b/minos/utils.py index 64cf2733..ae0bf721 100755 --- a/minos/utils.py +++ b/minos/utils.py @@ -8,7 +8,7 @@ import os from os.path import dirname as up import pandas as pd -import datetime +from datetime import datetime from itertools import product import scipy from math import sqrt, log, ceil, floor diff --git a/scripts/local_batch.sh b/scripts/local_batch.sh index 2e217d05..6a8027aa 100644 --- a/scripts/local_batch.sh +++ b/scripts/local_batch.sh @@ -1,6 +1,6 @@ NUMRUNS=10 -declare -a intlist=(hhIncomeChildUplift livingWageIntervention) +declare -a intlist=(energyDownlift energyDownliftNoSupport livingWageIntervention) TIME=$(date +%Y_%m_%d_%H_%M_%S) From 389a77b6b53a26a8f89c3862eb7d03013a7c9345 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Wed, 1 Nov 2023 11:45:36 +0000 Subject: [PATCH 057/229] Switched living wage intervention to all pop under threshold instead of just those in public sector --- minos/modules/intervention.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/minos/modules/intervention.py b/minos/modules/intervention.py index b1e32d4d..c3779874 100755 --- a/minos/modules/intervention.py +++ b/minos/modules/intervention.py @@ -354,7 +354,7 @@ def on_time_step(self, event): logging.info( f"\tApplying effects of the living wage intervention in year {event.time.year}...") - pop = self.population_view.get(event.index, query="alive =='alive' and job_sector == 2") + pop = self.population_view.get(event.index, query="alive =='alive'") # TODO probably a faster way to do this than resetting the whole column. #pop['hh_income'] -= pop['boost_amount'] # reset boost amount back to 0 before applying intervention again From af68299b798fbb2515c9d340baf6717f6dc34b26 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Wed, 1 Nov 2023 12:07:12 +0000 Subject: [PATCH 058/229] Updated the scripts for QALY comparison of living wage and energy crisis interventions --- .../testing/QALY_comparison_energyCrises.Rmd | 173 +++++++----------- minos/testing/QALY_comparison_livingWage.Rmd | 16 +- 2 files changed, 65 insertions(+), 124 deletions(-) diff --git a/minos/testing/QALY_comparison_energyCrises.Rmd b/minos/testing/QALY_comparison_energyCrises.Rmd index de6ad858..1a5050fb 100644 --- a/minos/testing/QALY_comparison_energyCrises.Rmd +++ b/minos/testing/QALY_comparison_energyCrises.Rmd @@ -19,11 +19,13 @@ require(ggplot2) require(knitr) require(here) require(MESS) +require(scales) #workingDir <- "/home/luke/Documents/WORK/MINOS/" workingDir <- normalizePath('../') knitr::opts_knit$set(root.dir = workingDir) knitr::opts_chunk$set( + options(scipen = 999) ) rm(workingDir) ``` @@ -64,30 +66,23 @@ ggplot(data = combined, aes(x = year, y = QALYs, group = intervention, colour = geom_smooth() ``` -## Groupby and Mean +## Area Under the Curve Comparison ```{r} -base <- base %>% - group_by(year) %>% - summarise(QALYs = mean(QALYs), - boost_amount = mean(total_boost)) -support <- support %>% - group_by(year) %>% - summarise(QALYs = mean(QALYs), - boost_amount = mean(total_boost)) -noSupport <- noSupport %>% - group_by(year) %>% - summarise(QALYs = mean(QALYs), - boost_amount = mean(total_boost)) -``` +base.auc <- base %>% + group_by(run_id) %>% + summarise(AUC = auc(x=year, y = QALYs, type = 'spline')) %>% + mutate(intervention = 'baseline') -## Area Under the Curve Comparison +support.auc <- support %>% + group_by(run_id) %>% + summarise(AUC = auc(x=year, y = QALYs, type = 'spline')) %>% + mutate(intervention = 'Support') -```{r} -#base.auc <- sum(diff(base$time)*rollmean(base$QALYs, 2)) -base.auc <- data.frame(intervention = 'baseline', AUC = auc(x=base$year, y=base$QALYs, type = 'spline')) -support.auc <- data.frame(intervention = 'Support', AUC = auc(x=support$year, y=support$QALYs, type = 'spline')) -noSupport.auc <- data.frame(intervention = 'NoSupport', AUC = auc(x=noSupport$year, y=noSupport$QALYs, type = 'spline')) +noSupport.auc <- noSupport %>% + group_by(run_id) %>% + summarise(AUC = auc(x=year, y = QALYs, type = 'spline')) %>% + mutate(intervention = 'NoSupport') combined.auc <- rbind(base.auc, support.auc, noSupport.auc) ``` @@ -96,7 +91,10 @@ combined.auc <- rbind(base.auc, support.auc, noSupport.auc) print(combined.auc) ggplot(data = combined.auc, aes(x = intervention, y = AUC, group = intervention, fill = intervention)) + - geom_col() + stat_summary(fun = 'mean', geom = 'bar') + + geom_errorbar(aes(ymin = min(AUC), ymax = max(AUC), width = 0.4)) + + labs(title = 'Total AUC comparison', subtitle = 'Baseline vs Energy Crisis Interventions') + + xlab('Intervention') ``` ```{r} @@ -110,12 +108,16 @@ combined.auc2 <- combined.auc %>% pivot_longer(cols = change_baseline:change_noSupport, names_to = 'intervention', names_prefix = 'change_', - values_to = 'AUC_difference') + values_to = 'AUC_difference') %>% + filter(intervention != 'baseline') print(combined.auc2) ggplot(combined.auc2, aes(x = intervention, y = AUC_difference, group = intervention, fill = intervention)) + - geom_col() + stat_summary(fun = 'mean', geom = 'bar') + + geom_errorbar(aes(ymin = min(AUC_difference), ymax = max(AUC_difference), width = 0.4)) + + labs(title = 'Change in AUC', subtitle = 'Baseline vs Energy Crisis Interventions') + + xlab('Intervention') ``` # Cost per QALY @@ -123,78 +125,43 @@ ggplot(combined.auc2, aes(x = intervention, y = AUC_difference, group = interven The [UK Government](https://www.gov.uk/government/publications/valuation-of-risks-to-life-and-health-monetary-value-of-a-life-year-voly/a-scoping-study-on-the-valuation-of-risks-to-life-and-health-the-monetary-value-of-a-life-year-voly) has set the value of 1 QALY to be equal to $60,000. This is the number we will use for our calculations. ```{r} - combined.cost <- combined %>% - select(-X, -pop_size, -SF_12_MCS, -SF_12_PCS, -utility) %>% + select(-pop_size, -SF_12_MCS, -SF_12_PCS, -utility) %>% mutate(QALY_value = QALYs * 60000) ggplot(combined.cost, aes(x = year, y = QALY_value, group = intervention, colour = intervention)) + - geom_smooth() + geom_smooth() + + scale_y_continuous(label = comma) combined.cost.change <- combined.cost %>% select(-total_boost, -QALYs) %>% pivot_wider(names_from = 'intervention', - values_from = 'QALY_value') #%>% - # mutate(change_Support = energyDownlift - baseline, - # change_NoSupport = energyDownliftNoSupport - baseline) %>% - # select(-baseline, -energyDownlift, -energyDownliftNoSupport) %>% - # pivot_longer(cols = -year, - # names_prefix = 'change_', - # names_to = 'intervention', - # values_to = 'value_change') - -ggplot(combined.cost.change, aes(x = year, y = value_change, group = intervention, colour = intervention)) + - geom_smooth() - - + values_from = 'QALY_value') %>% + mutate(change_baseline = baseline - baseline, + change_Support = energyDownlift - baseline, + change_NoSupport = energyDownliftNoSupport - baseline) %>% + select(-baseline, -energyDownlift, -energyDownliftNoSupport) %>% + pivot_longer(cols = change_baseline:change_NoSupport, + names_to = 'intervention', + names_prefix = 'change_', + values_to = 'QALY_value_change') %>% + filter(intervention != 'baseline') - # select(-QALYs) %>% - # pivot_wider(names_from = 'intervention', - # values_from = c('total_boost', 'QALY_value'), - # names_sep = '000') %>% - # mutate(QALY_value_change000energyDownlift = QALY_value000energyDownlift - QALY_value000baseline, - # QALY_value_change000energyDownliftNoSupport = QALY_value000energyDownliftNoSupport - QALY_value000baseline) %>% - # select(-QALY_value000baseline, -QALY_value000energyDownlift, -QALY_value000energyDownliftNoSupport, -total_boost000baseline) %>% - # pivot_longer(cols = total_boost000energyDownlift:QALY_value_change000energyDownliftNoSupport, - # names_sep = '000', - # names_to = c('.value', 'intervention'), - # names_repair = 'unique') #%>% - # pivot_longer(cols = QALY_value_change_Support:QALY_value_change_NoSupport, - # names_prefix = 'QALY_value_change_', - # names_to = 'intervention2', - # values_to = 'QALY_value_change') - -# combined.cost <- combined %>% -# select(-X, -pop_size, -SF_12_MCS, -SF_12_PCS, -utility) %>% -# pivot_wider(names_from = 'intervention', -# values_from = c('mean_boost', 'QALYs')) %>% -# mutate(change_energyDownlift = QALYs_energyDownlift - QALYs_baseline, -# change_energyDownliftNoSupport = QALYs_energyDownliftNoSupport - QALYs_baseline) %>% -# mutate(costPerQALY_energyDownlift = change_energyDownlift / mean_boost_energyDownlift, -# costPerQALY_energyDownliftNoSupport = change_energyDownliftNoSupport / mean_boost_energyDownliftNoSupport) %>% -# pivot_longer(cols = costPerQALY_energyDownlift:costPerQALY_energyDownliftNoSupport, -# names_prefix = 'costPerQALY_', -# names_to = 'intervention', -# values_to = 'costPerQALY') %>% -# select(year, intervention, costPerQALY) - -# options(scipen=999) -# -# ggplot(data = combined.cost, aes(x = year, y = costPerQALY, group = intervention, colour = intervention)) + -# geom_line() +ggplot(combined.cost.change, aes(x = year, y = QALY_value_change, group = intervention, colour = intervention, fill = intervention)) + + geom_smooth() + + scale_y_continuous(label = comma) ``` ```{r} -combined.cost.mean <- combined.cost %>% - group_by(year, intervention) %>% - summarise(across(everything(), mean)) - total.combined.cost <- combined.cost.change %>% - group_by(intervention) %>% - summarise(total_value_change = sum(value_change)) - -ggplot(data = total.combined.cost, aes(x = intervention, y = total_value_change, group = intervention, fill = intervention)) + - geom_col() + group_by(run_id, intervention) %>% + summarise(TotalChange = sum(QALY_value_change)) + +ggplot(total.combined.cost, aes(x = intervention, y = TotalChange, group = intervention, fill = intervention)) + + stat_summary(fun = 'mean', geom = 'bar') + + geom_errorbar(aes(ymin = min(TotalChange), ymax = max(TotalChange), width = 0.4)) + + labs(title = 'Total Change in QALY Value', subtitle = 'Baseline vs Energy Crisis Interventions') + + xlab('Intervention') ``` # Incremental Cost-Effectiveness Ratio @@ -203,8 +170,11 @@ From [NICE guidelines](https://www.nice.org.uk/process/pmg6/chapter/assessing-co From the NICE guidelines, there is a general consensus that anything below £20,000 is cost effective, and anything above £30,000 is not. Between £20,000 and £30,000 is a grey area, and depends on the field and other factors to decide (i.e. if a rare or important condition is tackled by an intervention then the cost-effective threshold is higher). - ```{r} +combined.cost.mean <- combined.cost %>% + group_by(run_id, year, intervention) %>% + summarise(across(everything(), mean)) + # ICER = (C1 - C0) / (E1 - E0) # where # C1 = cost of intervention @@ -212,44 +182,29 @@ From the NICE guidelines, there is a general consensus that anything below £20, # C0 = cost of control group # E0 = effect of control group -ICER_test2 <- combined.cost.mean %>% +ICER <- combined.cost.mean %>% pivot_wider(names_from = 'intervention', values_from = c('total_boost', 'QALYs', 'QALY_value')) %>% mutate(ICER_Support = (total_boost_energyDownlift - total_boost_baseline) / (QALYs_energyDownlift - QALYs_baseline), ICER_NoSupport = (total_boost_energyDownliftNoSupport - total_boost_baseline) / (QALYs_energyDownliftNoSupport - QALYs_baseline)) %>% -select(year, ICER_Support, ICER_NoSupport) %>% - pivot_longer(cols = -year, + select(run_id, year, ICER_Support, ICER_NoSupport) %>% + pivot_longer(cols = -c(run_id, year), names_prefix = 'ICER_', names_to = 'intervention', values_to = 'ICER') -ggplot(ICER, aes(x = year, y = ICER, group = intervention, colour = intervention)) + - geom_line() +ggplot(ICER, aes(x = year, y = ICER, group = intervention, fill = intervention, colour = intervention)) + + geom_smooth() ICER.final <- ICER %>% - group_by(intervention) %>% + group_by(run_id, intervention) %>% summarise(ICER = mean(ICER)) -ggplot(ICER.final, aes(x = intervention, y = ICER, group = intervention, fill = intervention, colour = intervention)) + - geom_col() - - -ICER_test3 <- combined.cost.mean %>% - pivot_wider(names_from = 'intervention', - values_from = c('total_boost', 'QALYs', 'QALY_value')) %>% - mutate(C_Support = total_boost_energyDownlift - total_boost_baseline, - E_Support = QALYs_energyDownlift - QALYs_baseline, - C_NoSupport = total_boost_energyDownliftNoSupport - total_boost_baseline, - E_NoSupport = QALYs_energyDownliftNoSupport - QALYs_baseline) %>% - select(year, C_Support, E_Support, C_NoSupport, E_NoSupport) %>% - mutate(ICER_Support = C_Support / E_Support, - ICER_NoSupport = C_NoSupport / E_NoSupport)# %>% - #select(year, ICER_Support, ICER_NoSupport)# %>% -# pivot_longer(cols = -year, -# names_prefix = 'ICER_', -# names_to = 'intervention', -# values_to = 'ICER') - +ggplot(ICER.final, aes(x = intervention, y = ICER, group = intervention, fill = intervention)) + + stat_summary(fun = 'mean', geom = 'bar') + + geom_errorbar(aes(ymin = min(ICER), ymax = max(ICER), width = 0.4)) + + labs(title = 'ICER', subtitle = 'Living Wage Intervention') + + xlab('Intervention') ``` ```{r} diff --git a/minos/testing/QALY_comparison_livingWage.Rmd b/minos/testing/QALY_comparison_livingWage.Rmd index af73477d..b9cb24bd 100644 --- a/minos/testing/QALY_comparison_livingWage.Rmd +++ b/minos/testing/QALY_comparison_livingWage.Rmd @@ -89,13 +89,6 @@ wage.auc <- wage %>% mutate(intervention = 'LivingWage') combined.auc <- rbind(base.auc, wage.auc) - -# #base.auc <- sum(diff(base$time)*rollmean(base$QALYs, 2)) -# base.auc <- data.frame(intervention = 'baseline', AUC = auc(x=base$year, y=base$QALYs, type = 'spline')) -# support.auc <- data.frame(intervention = 'Support', AUC = auc(x=support$year, y=support$QALYs, type = 'spline')) -# noSupport.auc <- data.frame(intervention = 'NoSupport', AUC = auc(x=noSupport$year, y=noSupport$QALYs, type = 'spline')) -# -# combined.auc <- rbind(base.auc, support.auc, noSupport.auc) ``` ```{r} @@ -135,13 +128,6 @@ ggplot(combined.auc2, aes(x = intervention, y = AUC_difference, group = interven The [UK Government](https://www.gov.uk/government/publications/valuation-of-risks-to-life-and-health-monetary-value-of-a-life-year-voly/a-scoping-study-on-the-valuation-of-risks-to-life-and-health-the-monetary-value-of-a-life-year-voly) has set the value of 1 QALY to be equal to $60,000. This is the number we will use for our calculations. ```{r} - -# pivot_longer(cols = change_baseline:change_LivingWage, -# names_to = 'intervention', -# names_prefix = 'change_', -# values_to = 'AUC_difference') %>% -# filter(intervention != 'baseline') - combined.cost <- combined %>% select(-pop_size, -SF_12_MCS, -SF_12_PCS, -utility) %>% mutate(QALY_value = QALYs * 60000) @@ -163,7 +149,7 @@ combined.cost.change <- combined.cost %>% values_to = 'QALY_value_change') %>% filter(intervention != 'baseline') -ggplot(combined.cost.change, aes(x = year, y = QALY_value_change, group = intervention, fill = intervention)) + +ggplot(combined.cost.change, aes(x = year, y = QALY_value_change, group = intervention, colour = intervention, fill = intervention)) + geom_smooth() + scale_y_continuous(label = comma) ``` From d5d3cee4c359338e18ee9f8252e52ed5bd79edbb Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Wed, 1 Nov 2023 14:11:36 +0000 Subject: [PATCH 059/229] =?UTF-8?q?Added=20a=20new=20R=20package=20to=20en?= =?UTF-8?q?vironment=20for=20better=20representing=20=C2=A3=20values=20in?= =?UTF-8?q?=20visualisations,=20and=20corrected=20an=20error=20I=20introdu?= =?UTF-8?q?ced=20in=20minos/utils=20when=20changing=20the=20datetime=20imp?= =?UTF-8?q?ort=20to=20from=20datetime=20import=20datetime?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- environment.yml | 1 + minos/utils.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/environment.yml b/environment.yml index 961a6bd7..228a43fc 100644 --- a/environment.yml +++ b/environment.yml @@ -30,6 +30,7 @@ dependencies: # Added pip and python at top to avoid conda giving grumpy warning - myst-parser - r-glmmTMB - r-MESS + - r-scales - pip: # Same packages as were in "requirements.txt" except minos and vivarium, as both installed during "make install" - rpy2 - pyyaml>=5.4 diff --git a/minos/utils.py b/minos/utils.py index ae0bf721..0d67ae9a 100755 --- a/minos/utils.py +++ b/minos/utils.py @@ -121,7 +121,7 @@ def to_years(time: pd.Timedelta) -> float: def get_time(): - return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + return datetime.now().strftime("%Y-%m-%d %H:%M:%S") def make_uniform_pop_data(age_bin_midpoint=False): From d3f830aa1ba9536fd5ab9782396adf9d527e4879 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Wed, 1 Nov 2023 16:23:28 +0000 Subject: [PATCH 060/229] Added standard error bars, see how this works before moving to 95% confidence intervals --- .../testing/QALY_comparison_energyCrises.Rmd | 43 ++++++++++---- minos/testing/QALY_comparison_livingWage.Rmd | 56 ++++++++++++++----- 2 files changed, 73 insertions(+), 26 deletions(-) diff --git a/minos/testing/QALY_comparison_energyCrises.Rmd b/minos/testing/QALY_comparison_energyCrises.Rmd index 1a5050fb..d09b0b68 100644 --- a/minos/testing/QALY_comparison_energyCrises.Rmd +++ b/minos/testing/QALY_comparison_energyCrises.Rmd @@ -90,9 +90,15 @@ combined.auc <- rbind(base.auc, support.auc, noSupport.auc) ```{r} print(combined.auc) -ggplot(data = combined.auc, aes(x = intervention, y = AUC, group = intervention, fill = intervention)) + - stat_summary(fun = 'mean', geom = 'bar') + - geom_errorbar(aes(ymin = min(AUC), ymax = max(AUC), width = 0.4)) + +combined.auc.plot <- combined.auc %>% + group_by(intervention) %>% + summarise(sd = sd(AUC), + se = sd / sqrt(n()), + AUC = mean(AUC)) + +ggplot(data = combined.auc.plot, aes(x = intervention, y = AUC, group = intervention, fill = intervention)) + + geom_col() + + geom_errorbar(aes(ymin = AUC - se, ymax = AUC + se, width = 0.4, group = intervention)) + labs(title = 'Total AUC comparison', subtitle = 'Baseline vs Energy Crisis Interventions') + xlab('Intervention') ``` @@ -113,9 +119,14 @@ combined.auc2 <- combined.auc %>% print(combined.auc2) -ggplot(combined.auc2, aes(x = intervention, y = AUC_difference, group = intervention, fill = intervention)) + - stat_summary(fun = 'mean', geom = 'bar') + - geom_errorbar(aes(ymin = min(AUC_difference), ymax = max(AUC_difference), width = 0.4)) + +combined.auc2.plot <- combined.auc2 %>% + group_by(intervention) %>% + summarise(se = sd(AUC_difference) / sqrt(n()), + AUC_difference = mean(AUC_difference)) + +ggplot(combined.auc2.plot, aes(x = intervention, y = AUC_difference, group = intervention, fill = intervention)) + + geom_col() + + geom_errorbar(aes(ymin = AUC_difference - se, ymax = AUC_difference + se, width = 0.4, group = intervention)) + labs(title = 'Change in AUC', subtitle = 'Baseline vs Energy Crisis Interventions') + xlab('Intervention') ``` @@ -155,11 +166,15 @@ ggplot(combined.cost.change, aes(x = year, y = QALY_value_change, group = interv ```{r} total.combined.cost <- combined.cost.change %>% group_by(run_id, intervention) %>% - summarise(TotalChange = sum(QALY_value_change)) + summarise(TotalChange = sum(QALY_value_change)) %>% + group_by(intervention) %>% + summarise(se = sd(TotalChange) / sqrt(n()), + TotalChange = mean(TotalChange)) ggplot(total.combined.cost, aes(x = intervention, y = TotalChange, group = intervention, fill = intervention)) + - stat_summary(fun = 'mean', geom = 'bar') + - geom_errorbar(aes(ymin = min(TotalChange), ymax = max(TotalChange), width = 0.4)) + + geom_col() + + geom_errorbar(aes(ymin = TotalChange - se, ymax = TotalChange + se, width = 0.4, group = intervention)) + + scale_y_continuous(label = comma) + labs(title = 'Total Change in QALY Value', subtitle = 'Baseline vs Energy Crisis Interventions') + xlab('Intervention') ``` @@ -198,11 +213,15 @@ ggplot(ICER, aes(x = year, y = ICER, group = intervention, fill = intervention, ICER.final <- ICER %>% group_by(run_id, intervention) %>% - summarise(ICER = mean(ICER)) + summarise(ICER = mean(ICER)) %>% + group_by(intervention) %>% + summarise(se = sd(ICER) / sqrt(n()), + ICER = mean(ICER)) ggplot(ICER.final, aes(x = intervention, y = ICER, group = intervention, fill = intervention)) + - stat_summary(fun = 'mean', geom = 'bar') + - geom_errorbar(aes(ymin = min(ICER), ymax = max(ICER), width = 0.4)) + + geom_col() + + geom_errorbar(aes(ymin = ICER - se, ymax = ICER + se, width = 0.4, group = intervention)) + + scale_y_continuous(label = comma) + labs(title = 'ICER', subtitle = 'Living Wage Intervention') + xlab('Intervention') ``` diff --git a/minos/testing/QALY_comparison_livingWage.Rmd b/minos/testing/QALY_comparison_livingWage.Rmd index b9cb24bd..8a0c9ad7 100644 --- a/minos/testing/QALY_comparison_livingWage.Rmd +++ b/minos/testing/QALY_comparison_livingWage.Rmd @@ -94,9 +94,15 @@ combined.auc <- rbind(base.auc, wage.auc) ```{r} print(combined.auc) -ggplot(data = combined.auc, aes(x = intervention, y = AUC, group = intervention, fill = intervention)) + - stat_summary(fun = 'mean', geom = 'bar') + - geom_errorbar(aes(ymin = min(AUC), ymax = max(AUC), width = 0.4)) + +combined.auc.plot <- combined.auc %>% + group_by(intervention) %>% + summarise(sd = sd(AUC), + se = sd / sqrt(n()), + AUC = mean(AUC)) + +ggplot(data = combined.auc.plot, aes(x = intervention, y = AUC, group = intervention, fill = intervention)) + + geom_col() + + geom_errorbar(aes(ymin = AUC - se, ymax = AUC + se, width = 0.4, group = intervention)) + labs(title = 'Total AUC comparison', subtitle = 'Baseline vs Living Wage Intervention') + xlab('Intervention') ``` @@ -116,9 +122,14 @@ combined.auc2 <- combined.auc %>% print(combined.auc2) -ggplot(combined.auc2, aes(x = intervention, y = AUC_difference, group = intervention, fill = intervention)) + - stat_summary(fun = 'mean', geom = 'bar') + - geom_errorbar(aes(ymin = min(AUC_difference), ymax = max(AUC_difference), width = 0.4)) + +combined.auc2.plot <- combined.auc2 %>% + group_by(intervention) %>% + summarise(se = sd(AUC_difference) / sqrt(n()), + AUC_difference = mean(AUC_difference)) + +ggplot(combined.auc2.plot, aes(x = intervention, y = AUC_difference, group = intervention, fill = intervention)) + + geom_col() + + geom_errorbar(aes(ymin = AUC_difference - se, ymax = AUC_difference + se, width = 0.4, group = intervention)) + labs(title = 'Change in AUC', subtitle = 'Baseline vs Living Wage Intervention') + xlab('Intervention') ``` @@ -157,13 +168,17 @@ ggplot(combined.cost.change, aes(x = year, y = QALY_value_change, group = interv ```{r} total.combined.cost <- combined.cost.change %>% group_by(run_id, intervention) %>% - summarise(TotalChange = sum(QALY_value_change)) + summarise(TotalChange = sum(QALY_value_change)) %>% + group_by(intervention) %>% + summarise(se = sd(TotalChange) / sqrt(n()), + TotalChange = mean(TotalChange)) #print(paste0('Total combined cost == ', format(round(as.numeric(total.combined.cost), 1), nsmall=1, big.mark=","))) ggplot(total.combined.cost, aes(x = intervention, y = TotalChange, group = intervention, fill = intervention)) + - stat_summary(fun = 'mean', geom = 'bar') + - geom_errorbar(aes(ymin = min(TotalChange), ymax = max(TotalChange), width = 0.4)) + + geom_col() + + geom_errorbar(aes(ymin = TotalChange - se, ymax = TotalChange + se, width = 0.4, group = intervention)) + + scale_y_continuous(label = comma) + labs(title = 'Total Change in QALY Value', subtitle = 'Baseline vs Living Wage Intervention') + xlab('Intervention') ``` @@ -195,15 +210,28 @@ ICER <- combined.cost.mean %>% mutate(intervention = 'LivingWage') ggplot(ICER, aes(x = year, y = ICER)) + - geom_smooth() + geom_smooth() + + scale_y_continuous(label=comma) ICER.final <- ICER %>% group_by(run_id, intervention) %>% - summarise(ICER = mean(ICER)) + summarise(ICER = mean(ICER)) %>% + group_by(intervention) %>% + summarise(se = sd(ICER) / sqrt(n()), + ICER = mean(ICER)) ggplot(ICER.final, aes(x = intervention, y = ICER, group = intervention, fill = intervention)) + - stat_summary(fun = 'mean', geom = 'bar') + - geom_errorbar(aes(ymin = min(ICER), ymax = max(ICER), width = 0.4)) + + geom_col() + + geom_errorbar(aes(ymin = ICER - se, ymax = ICER + se, width = 0.4, group = intervention)) + + scale_y_continuous(label = comma) + labs(title = 'ICER', subtitle = 'Living Wage Intervention') + xlab('Intervention') -``` \ No newline at end of file +``` + + + + +```{r} + +``` + From 4a77d49fb6dee2d5551869956f4da2d0cfdfaa18 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Wed, 1 Nov 2023 16:26:52 +0000 Subject: [PATCH 061/229] Created a temporary combined target to make QALY comparisons easier on arc --- minos/outcomes/Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/minos/outcomes/Makefile b/minos/outcomes/Makefile index a62427a6..a7ba0ded 100644 --- a/minos/outcomes/Makefile +++ b/minos/outcomes/Makefile @@ -37,6 +37,10 @@ QALY_comparison_livingWage: QALY_baseline QALY_livingWage $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" firefox file://$(TESTING)/QALY_comparison_livingWage.html +QALY_comparisons: QALY_baseline QALY_energyDownlift QALY_energyDownliftNoSupport QALY_livingWage + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises.Rmd')" + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" + #test_plot: MODE=default_config #test_plot: ALIVE=who_alive #test_plot: BOOSTED=who_boosted From 7aa1262037fbdf54fdddca30fd7055296e3857fa Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Wed, 1 Nov 2023 18:58:09 +0000 Subject: [PATCH 062/229] Updated transition models to match latest versions in development --- minos/modules/financial_situation.py | 6 +++- minos/modules/heating.py | 6 +++- minos/modules/housing.py | 7 ++++- minos/modules/housing_tenure.py | 4 ++- minos/modules/loneliness.py | 4 ++- minos/modules/mental_wellbeing.py | 4 ++- minos/modules/neighbourhood.py | 5 +++- minos/modules/nutrition.py | 1 + minos/modules/tobacco.py | 4 ++- minos/transitions/estimate_transitions.R | 22 +++++++------- .../transitions/model_definitions_default.txt | 30 +++++++++---------- 11 files changed, 60 insertions(+), 33 deletions(-) diff --git a/minos/modules/financial_situation.py b/minos/modules/financial_situation.py index b65c26d1..daeba6be 100644 --- a/minos/modules/financial_situation.py +++ b/minos/modules/financial_situation.py @@ -50,6 +50,7 @@ def setup(self, builder): # view_columns is the columns from the main population used in this module. # In this case, view_columns are taken straight from the transition model view_columns = ['pidp', + 'hidp', 'age', 'sex', 'ethnicity', @@ -66,7 +67,10 @@ def setup(self, builder): 'yearly_energy', 'financial_situation', 'marital_status', - 'hhsize' + 'hhsize', + 'loneliness', + 'nutrition_quality', + 'ncigs' ] # view_columns += self.transition_model.rx2('model').names self.population_view = builder.population.get_view(columns=view_columns) diff --git a/minos/modules/heating.py b/minos/modules/heating.py index 88a04e12..4e9e2549 100644 --- a/minos/modules/heating.py +++ b/minos/modules/heating.py @@ -63,7 +63,11 @@ def setup(self, builder): "urban", 'hhsize', "housing_tenure", - "financial_situation"] + "financial_situation", + 'region', + 'education_state', + 'loneliness', + 'nutrition_quality'] self.population_view = builder.population.get_view(columns=view_columns) # Population initialiser. When new individuals are added to the microsimulation a constructer is called for each diff --git a/minos/modules/housing.py b/minos/modules/housing.py index 4e22d80d..b472ac20 100755 --- a/minos/modules/housing.py +++ b/minos/modules/housing.py @@ -69,7 +69,12 @@ def setup(self, builder): 'urban', 'financial_situation', "hh_income_diff", - 'housing_tenure'] + 'housing_tenure', + 'region', + 'education_state', + 'loneliness', + 'nutrition_quality', + 'ncigs'] self.population_view = builder.population.get_view(columns=view_columns) diff --git a/minos/modules/housing_tenure.py b/minos/modules/housing_tenure.py index d6fbbe0c..ce5a8d5a 100644 --- a/minos/modules/housing_tenure.py +++ b/minos/modules/housing_tenure.py @@ -62,7 +62,9 @@ def setup(self, builder): "hh_income", 'housing_tenure', 'urban', - 'financial_situation'] + 'financial_situation', + 'region', + 'education_state'] self.population_view = builder.population.get_view(columns=view_columns) # Population initialiser. When new individuals are added to the microsimulation a constructer is called for each diff --git a/minos/modules/loneliness.py b/minos/modules/loneliness.py index 0033d847..8e98dedb 100755 --- a/minos/modules/loneliness.py +++ b/minos/modules/loneliness.py @@ -61,7 +61,9 @@ def setup(self, builder): "loneliness", "hh_comp", "marital_status", - "ncigs"] + "ncigs", + 'region', + 'nutrition_quality'] self.population_view = builder.population.get_view(columns=view_columns) # Population initialiser. When new individuals are added to the microsimulation a constructer is called for each diff --git a/minos/modules/mental_wellbeing.py b/minos/modules/mental_wellbeing.py index f3c30edc..692c2070 100755 --- a/minos/modules/mental_wellbeing.py +++ b/minos/modules/mental_wellbeing.py @@ -487,7 +487,9 @@ def setup(self, builder): 'loneliness', 'financial_situation', 'active', - 'chron_disease'] + 'chron_disease', + 'region', + 'education_state'] self.population_view = builder.population.get_view(columns=view_columns) diff --git a/minos/modules/neighbourhood.py b/minos/modules/neighbourhood.py index a51a0181..8b21b5ed 100755 --- a/minos/modules/neighbourhood.py +++ b/minos/modules/neighbourhood.py @@ -56,7 +56,10 @@ def setup(self, builder): 'education_state', 'housing_quality', 'job_sec', - 'urban'] + 'urban', + 'loneliness', + 'nutrition_quality', + 'ncigs'] #view_columns += self.transition_model.rx2('model').names self.population_view = builder.population.get_view(columns=view_columns) diff --git a/minos/modules/nutrition.py b/minos/modules/nutrition.py index 9599abe0..d4379382 100755 --- a/minos/modules/nutrition.py +++ b/minos/modules/nutrition.py @@ -160,6 +160,7 @@ def setup(self, builder): # view_columns is the columns from the main population used in this module. # In this case, view_columns are taken straight from the transition model view_columns = ['pidp', + 'hidp', 'time', 'age', 'sex', diff --git a/minos/modules/tobacco.py b/minos/modules/tobacco.py index bf530697..4d6db2e6 100755 --- a/minos/modules/tobacco.py +++ b/minos/modules/tobacco.py @@ -65,7 +65,9 @@ def setup(self, builder): 'S7_labour_state', 'job_sec', 'alcohol_spending', - 'ncigs'] + 'ncigs', + 'housing_quality', + 'loneliness'] #view_columns += self.transition_model.rx2('model').names self.population_view = builder.population.get_view(columns=view_columns) diff --git a/minos/transitions/estimate_transitions.R b/minos/transitions/estimate_transitions.R index 53fcc73f..a008e642 100644 --- a/minos/transitions/estimate_transitions.R +++ b/minos/transitions/estimate_transitions.R @@ -211,30 +211,30 @@ run_yearly_models <- function(transitionDir_path, # CHANGE 9/10/23: We remove these variables because they're not present, # so we should do this for all models and not just SF_12 if(!year %in% c(2011, 2014, 2017, 2020)) { - formula.string <- str_remove(formula.string, " \\+ factor\\(neighbourhood_safety\\)") + formula.string <- str_remove_all(formula.string, " \\+ factor\\(neighbourhood_safety\\)") } if(!year > 2016) { - formula.string <- str_remove(formula.string, " \\+ factor\\(loneliness\\)") + formula.string <- str_remove_all(formula.string, " \\+ factor\\(loneliness\\)") } if(!year %in% c(2015, 2017, 2019)) { - formula.string <- str_remove(formula.string, " \\+ nutrition_quality") - formula.string <- str_remove(formula.string, " \\+ scale\\(nutrition_quality\\)") + formula.string <- str_remove_all(formula.string, " \\+ nutrition_quality") + formula.string <- str_remove_all(formula.string, " \\+ scale\\(nutrition_quality\\)") } if(year < 2013) { - formula.string <- str_remove(formula.string, " \\+ ncigs") - formula.string <- str_remove(formula.string, " \\+ scale\\(ncigs\\)") + formula.string <- str_remove_all(formula.string, " \\+ ncigs") + formula.string <- str_remove_all(formula.string, " \\+ scale\\(ncigs\\)") } if(!year %in% c(2015, 2017, 2019, 2020)) { - formula.string <- str_remove(formula.string, " \\+ factor\\(auditc\\)") + formula.string <- str_remove_all(formula.string, " \\+ factor\\(auditc\\)") } if(!year %in% c(2015, 2017, 2019, 2020)) { - formula.string <- str_remove(formula.string, " \\+ factor\\(active\\)") + formula.string <- str_remove_all(formula.string, " \\+ factor\\(active\\)") } if(!year %in% c(2009, 2010, 2012, 2014, 2016, 2018, 2020)) { - formula.string <- str_remove(formula.string, " \\+ factor\\(matdep\\)") + formula.string <- str_remove_all(formula.string, " \\+ factor\\(matdep\\)") } if(year < 2011) { - formula.string <- str_remove(formula.string, " \\+ factor\\(chron_disease\\)") + formula.string <- str_remove_all(formula.string, " \\+ factor\\(chron_disease\\)") } #print(formula.string) # Now make string into formula @@ -272,6 +272,8 @@ run_yearly_models <- function(transitionDir_path, depend = next.dependent) } else if(tolower(mod.type) == 'zip') { + + print(formula.string) model <- estimate_yearly_zip(data = merged, formula = form, diff --git a/minos/transitions/model_definitions_default.txt b/minos/transitions/model_definitions_default.txt index 8c9a1273..9c0bd002 100644 --- a/minos/transitions/model_definitions_default.txt +++ b/minos/transitions/model_definitions_default.txt @@ -1,17 +1,17 @@ -CLM : chron_disease ~ factor(chron_disease) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(marital_status) + scale(ncigs) +CLM : chron_disease ~ factor(chron_disease) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(marital_status) + scale(ncigs) LOGIT : active ~ age + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + hh_income + relevel(factor(ethnicity), ref = 'WBI') -NNET : auditc ~ age + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + hh_income + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + factor(financial_situation) -CLM : financial_situation ~ factor(financial_situation) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(job_sec) + scale(hh_income)+ I(scale(hh_income)**2) + factor(marital_status) + hhsize + factor(housing_tenure) -LOGIT : heating ~ factor(heating) + scale(SF_12_MCS) + scale(SF_12_PCS) + relevel(factor(ethnicity), ref='WBI') + hh_income + factor(marital_status) + ncigs + hhsize + factor(urban) + factor(housing_tenure) + factor(financial_situation) -LOGIT : urban ~ age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = "1") -GLMM : hh_income ~ scale(hh_income) + scale(hh_income_diff) + scale(age) + I(scale(age)**2) + I(scale(age)**3) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = "1") + relevel(factor(job_sec), ref = '3') + relevel(factor(job_sector), ref = '1') + scale(SF_12_MCS) + scale(SF_12_PCS) + (1|pidp) -LMM : nutrition_quality ~ scale(nutrition_quality) + scale(nutrition_quality_diff) + scale(age) + factor(sex) + relevel(factor(education_state), ref = '3') + relevel(factor(ethnicity), ref = 'WBI') + scale(hh_income) + ncigs + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(financial_situation) + (1|pidp) -CLM : housing_quality ~ factor(housing_quality) + scale(age) + I(scale(age)**2) + factor(sex) + scale(SF_12_MCS) + scale(SF_12_PCS) + relevel(factor(ethnicity), ref = 'WBI') + scale(hh_income) + I(scale(hh_income)**2) + scale(hh_income_diff) + factor(housing_tenure) -NNET : housing_tenure ~ age + factor(sex) + factor(housing_tenure) + relevel(factor(ethnicity), ref = 'WBI') + hh_income + factor(urban) + factor(financial_situation) -CLM : loneliness ~ factor(loneliness) + scale(age) + factor(sex) + scale(SF_12_MCS) + scale(SF_12_PCS) + relevel(factor(education_state), ref = '3') + relevel(factor(job_sec), ref = '3') + scale(hh_income) + relevel(factor(hh_comp), ref = '3') + relevel(factor(marital_status), ref = 'Partnered') + relevel(factor(ethnicity), ref = 'WBI') -CLM : neighbourhood_safety ~ scale(age) + factor(sex) + factor(job_sec) + relevel(factor(ethnicity), ref = 'WBI') + scale(hh_income) + factor(housing_quality) + relevel(factor(region), ref = 'South East') + factor(urban) -NNET : education_state ~ factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') -CLM : matdep ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(marital_status) + hhsize + factor(housing_tenure) + factor(urban) + factor(financial_situation) -ZIP : ncigs ~ ncigs + age + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + relevel(factor(job_sec), ref = '3') + scale(hh_income) + relevel(factor(ethnicity), ref = 'WBI') | I(factor(ncigs>0)) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(job_sec), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) +NNET : auditc ~ age + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + hh_income + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + factor(financial_situation) +CLM : financial_situation ~ factor(financial_situation) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + relevel(factor(job_sec), ref = '3') + scale(hh_income)+ I(scale(hh_income)**2) + factor(marital_status) + factor(housing_tenure) +LOGIT : heating ~ factor(heating) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) + factor(urban) + factor(housing_tenure) + factor(financial_situation) +LOGIT : urban ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + scale(hh_income) +GLMM : hh_income ~ scale(hh_income) + scale(hh_income_diff) + scale(age) + I(scale(age)**2) + I(scale(age)**3) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + relevel(factor(job_sec), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + (1|pidp) +LMM : nutrition_quality ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + scale(hh_income) + (1|pidp) + (1|hidp) + time +CLM : housing_quality ~ scale(age) + I(scale(age)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) + I(scale(hh_income)**2) + scale(hh_income_diff) + factor(housing_tenure) +NNET : housing_tenure ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_tenure) + factor(urban) + factor(financial_situation) + scale(hh_income) +CLM : loneliness ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + relevel(factor(job_sec), ref = '3') + scale(hh_income) + relevel(factor(marital_status), ref = 'Partnered') +CLM : neighbourhood_safety ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) +NNET : education_state ~ factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') +CLM : matdep ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(marital_status) + hhsize + factor(housing_tenure) + factor(urban) + factor(financial_situation) +ZIP : ncigs ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "3") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) | I(factor(ncigs>0)) + relevel(factor(ethnicity), ref = 'WBI') + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + relevel(factor(job_sec), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) GLMM : SF_12_PCS ~ scale(SF_12_PCS_last) + I(scale(SF_12_PCS_last)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + factor(housing_quality) + scale(hh_income) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(active) + factor(auditc) + factor(chron_disease) + (1|pidp) -GLMM : SF_12_MCS ~ scale(SF_12_MCS_last) + I(scale(SF_12_MCS_last)**2) + scale(SF_12_MCS_diff) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + factor(housing_quality) + scale(hh_income) + factor(neighbourhood_safety) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(financial_situation) + factor(active) + factor(chron_disease) + (1|pidp) +GLMM : SF_12_MCS ~ scale(SF_12_MCS_last) + I(scale(SF_12_MCS_last)**2) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + scale(hh_income) + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + I(factor(ncigs>0)) + (1|pidp) From bfb3b34a65889da82876af778c603da35ee87078 Mon Sep 17 00:00:00 2001 From: ld-archer Date: Thu, 2 Nov 2023 13:33:03 +0000 Subject: [PATCH 063/229] Updates to QALY comparison script for living wage --- minos/outcomes/QALY_calculation.py | 23 +++++++++------ minos/testing/QALY_comparison_livingWage.Rmd | 31 ++++++++++++++++++-- 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/minos/outcomes/QALY_calculation.py b/minos/outcomes/QALY_calculation.py index 3f95c75c..82724005 100644 --- a/minos/outcomes/QALY_calculation.py +++ b/minos/outcomes/QALY_calculation.py @@ -24,15 +24,18 @@ def aggregate_csv(filename, intervention): Parameters ---------- - filename - v - agg_method - subset_func_string - mode + filename : basestring + Name of the file to be aggregated for a specific year. + intervention : basestring + Name of intervention, to specify whether to record certain values as 0 (in case of baseline) or calculate them + from data. Returns ------- - + : vector + Vector of information about that specific run for that specific year. This is to be aggregated in the + Multiprocessing pool to generate a dataframe with each row corresponding to a specific run in a specific + intervention. """ df = pd.read_csv(filename, low_memory=False) #if subset_func_string: @@ -51,13 +54,15 @@ def aggregate_csv(filename, intervention): # record size of not dead population in year pop_size = df['alive'].value_counts()['alive'] - # record total_boost amount for intervention runs, set to 0 for baseline + # record boost information for intervention runs, set to 0 for baseline if intervention == 'baseline': + pop_boosted = 0 total_boost = 0 else: + pop_boosted = df['income_boosted'].sum() total_boost = df['boost_amount'].sum() - return [run_id, pop_size, total_boost, np.nanmean(df['SF_12_MCS']), np.nanmean(df['SF_12_PCS'])] + return [run_id, pop_size, pop_boosted, total_boost, np.nanmean(df['SF_12_MCS']), np.nanmean(df['SF_12_PCS'])] def calculate_qaly(df): @@ -121,7 +126,7 @@ def main(mode, intervention): aggregated_means = pool.starmap(aggregate_csv, zip(files, repeat(intervention))) new_df = pd.DataFrame(aggregated_means) - new_df.columns = ['run_id', 'pop_size', 'total_boost', 'SF_12_MCS', 'SF_12_PCS'] + new_df.columns = ['run_id', 'pop_size', 'pop_boosted', 'total_boost', 'SF_12_MCS', 'SF_12_PCS'] new_df['year'] = year new_df['intervention'] = intervention combined_output = pd.concat([combined_output, new_df]) diff --git a/minos/testing/QALY_comparison_livingWage.Rmd b/minos/testing/QALY_comparison_livingWage.Rmd index 8a0c9ad7..e2164b16 100644 --- a/minos/testing/QALY_comparison_livingWage.Rmd +++ b/minos/testing/QALY_comparison_livingWage.Rmd @@ -52,17 +52,42 @@ wage <- read.csv(paste0(wage.path, 'qalys.csv')) # base <- read.csv(paste0(out.path, 'qaly_baseline.csv')) # support <- read.csv(paste0(out.path, 'qalys_support.csv')) # noSupport <- read.csv(paste0(out.path, 'qalys_noSupport.csv')) +# combine the dataframes +combined <- rbind(base, wage) ``` +# Intervention Information -# QALY comparison +Just going to plot some things here like how many people affected by an intervention and what the mean / total boost amounts look like. ```{r} -# combine the dataframes -combined <- rbind(base, wage) +ggplot(filter(combined, intervention != 'baseline'), aes(x = year, y = pop_boosted, group = intervention, colour = intervention)) + + geom_smooth() + +ggplot(filter(combined, intervention != 'baseline'), aes(x = year, y = total_boost, group = intervention, colour = intervention)) + + geom_smooth() +``` + +# QALY comparison + +```{r} ggplot(data = combined, aes(x = year, y = QALYs, group = intervention, colour = intervention)) + geom_smooth() + +# Now change from baseline +combined.QALY.change <- combined %>% + select(run_id, year, intervention, QALYs) %>% + pivot_wider(names_from = 'intervention', + values_from = 'QALYs') %>% + mutate(QALYs = livingWageIntervention - baseline) %>% + select(run_id, year, QALYs) %>% + mutate(intervention = 'LivingWage') + +ggplot(data = combined.QALY.change, aes(x = year, y = QALYs, group = intervention, colour = intervention)) + + geom_hline(yintercept = 0, linetype = 'dashed') + + geom_smooth() + + labs(title = 'QALY change from Baseline') ``` ...and SF_12 MCS and PCS for good measure. From 2fb89abf523b31623a3bc8b18bba3544a70796e7 Mon Sep 17 00:00:00 2001 From: ld-archer Date: Thu, 2 Nov 2023 15:14:11 +0000 Subject: [PATCH 064/229] Seeded mortality to negate the potential effect in QALY calculations --- minos/modules/mortality.py | 4 +++- minos/outcomes/QALY_calculation.py | 15 +++++++++++---- minos/testing/QALY_comparison_livingWage.Rmd | 8 ++++++-- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/minos/modules/mortality.py b/minos/modules/mortality.py index 966ae0e6..4b66f011 100755 --- a/minos/modules/mortality.py +++ b/minos/modules/mortality.py @@ -84,7 +84,9 @@ def setup(self, builder): # Assign mortality a common random number stream (seeded). #self.random = builder.randomness.get_stream(f'mortality_handler') # Assign a random mortality CRN stream (unseeded). - self.random = builder.randomness.get_stream(self.generate_random_crn_key()) + #self.random = builder.randomness.get_stream(self.generate_random_crn_key()) + # generate fixed CRN stream (seeded) + self.random = builder.randomness.get_stream(self.generate_fixed_crn_key()) # Which columns are created by this module in on_initialize_simulants. columns_created = ['cause_of_death', 'years_of_life_lost'] diff --git a/minos/outcomes/QALY_calculation.py b/minos/outcomes/QALY_calculation.py index 82724005..be20e5c9 100644 --- a/minos/outcomes/QALY_calculation.py +++ b/minos/outcomes/QALY_calculation.py @@ -52,7 +52,14 @@ def aggregate_csv(filename, intervention): run_id = filename_nopath.split(sep='_')[0].lstrip('0') # record size of not dead population in year - pop_size = df['alive'].value_counts()['alive'] + # record size of dead population in year + # from both of these pieces of information we can calculate incidence of mortality + total_pop_size = len(df) + alive_pop = df['alive'].value_counts()['alive'] + dead = df['alive'].value_counts()['dead'] + + # to investigate the mortality rate we can look at the ratio of dead to alive and compare across years + alive_ratio = (alive_pop / total_pop_size) * 100 # record boost information for intervention runs, set to 0 for baseline if intervention == 'baseline': @@ -62,7 +69,7 @@ def aggregate_csv(filename, intervention): pop_boosted = df['income_boosted'].sum() total_boost = df['boost_amount'].sum() - return [run_id, pop_size, pop_boosted, total_boost, np.nanmean(df['SF_12_MCS']), np.nanmean(df['SF_12_PCS'])] + return [run_id, alive_pop, pop_boosted, total_boost, alive_ratio, np.nanmean(df['SF_12_MCS']), np.nanmean(df['SF_12_PCS'])] def calculate_qaly(df): @@ -95,7 +102,7 @@ def calculate_qaly(df): ((df['SF_12_PCS'] * df['SF_12_MCS'] * df['SF_12_PCS']) * 0.0000107) # Now calculate QALYs by multiplying utility score by pop_size - df['QALYs'] = df['utility'] * df['pop_size'] + df['QALYs'] = df['utility'] * df['alive_pop'] return df @@ -126,7 +133,7 @@ def main(mode, intervention): aggregated_means = pool.starmap(aggregate_csv, zip(files, repeat(intervention))) new_df = pd.DataFrame(aggregated_means) - new_df.columns = ['run_id', 'pop_size', 'pop_boosted', 'total_boost', 'SF_12_MCS', 'SF_12_PCS'] + new_df.columns = ['run_id', 'alive_pop', 'pop_boosted', 'total_boost', 'alive_ratio', 'SF_12_MCS', 'SF_12_PCS'] new_df['year'] = year new_df['intervention'] = intervention combined_output = pd.concat([combined_output, new_df]) diff --git a/minos/testing/QALY_comparison_livingWage.Rmd b/minos/testing/QALY_comparison_livingWage.Rmd index e2164b16..39d4909c 100644 --- a/minos/testing/QALY_comparison_livingWage.Rmd +++ b/minos/testing/QALY_comparison_livingWage.Rmd @@ -66,6 +66,9 @@ ggplot(filter(combined, intervention != 'baseline'), aes(x = year, y = pop_boost ggplot(filter(combined, intervention != 'baseline'), aes(x = year, y = total_boost, group = intervention, colour = intervention)) + geom_smooth() + +ggplot(combined, aes(x = year, y = alive_ratio, group = intervention, colour = intervention)) + + geom_smooth() ``` @@ -165,7 +168,7 @@ The [UK Government](https://www.gov.uk/government/publications/valuation-of-risk ```{r} combined.cost <- combined %>% - select(-pop_size, -SF_12_MCS, -SF_12_PCS, -utility) %>% + select(-alive_pop, -SF_12_MCS, -SF_12_PCS, -utility) %>% mutate(QALY_value = QALYs * 60000) ggplot(combined.cost, aes(x = year, y = QALY_value, group = intervention, colour = intervention)) + @@ -173,7 +176,7 @@ ggplot(combined.cost, aes(x = year, y = QALY_value, group = intervention, colour scale_y_continuous(label = comma) combined.cost.change <- combined.cost %>% - select(-total_boost, -QALYs) %>% + select(run_id, year, intervention, QALY_value) %>% pivot_wider(names_from = 'intervention', values_from = 'QALY_value') %>% mutate(change_baseline = baseline - baseline, @@ -228,6 +231,7 @@ combined.cost.mean <- combined.cost %>% # E0 = effect of control group ICER <- combined.cost.mean %>% + select(run_id, year, intervention, total_boost, QALYs, QALY_value) %>% pivot_wider(names_from = 'intervention', values_from = c('total_boost', 'QALYs', 'QALY_value')) %>% mutate(ICER = (total_boost_livingWageIntervention - total_boost_baseline) / (QALYs_livingWageIntervention - QALYs_baseline)) %>% From b9ae61fe0ee9f343493f2618187068ebdb55a5aa Mon Sep 17 00:00:00 2001 From: ld-archer Date: Thu, 2 Nov 2023 15:17:47 +0000 Subject: [PATCH 065/229] Added some combined targets to save myself the pain of remembering arc4 run targets --- scripts/Makefile | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/scripts/Makefile b/scripts/Makefile index 3107f60b..b7c9901a 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -77,6 +77,14 @@ all_scot_scenarios: MODE=scotland_mode all_scot_scenarios: RUN_CONFIG=$(CONFIG)/scot_default.yaml all_scot_scenarios: scot_setup baseline intervention_hhIncomeChildUplift intervention_PovertyLineChildUplift intervention_livingWage intervention_energyDownLift +qaly_scenarios: MODE=default_config +qaly_scenarios: RUN_CONFIG=$(CONFIG)/default_noReplenishment.yaml +qaly_scenarios: baseline intervention_energyDownLift intervention_energyDownLiftNoSupport intervention_livingWage + +qaly_scenarios_no_replenishment: MODE=default_no_replenishment +qaly_scenarios_no_replenishment: RUN_CONFIG=$(CONFIG)/default_noReplenishment.yaml +qaly_scenarios_no_replenishment: baseline intervention_energyDownLift intervention_energyDownLiftNoSupport intervention_livingWage + ##################################### ## Running MINOS scenarios on Arc4 ##################################### @@ -117,6 +125,14 @@ arc4_all_scot_scenarios: arc4_baseline arc4_intervention_hhIncomeChildUplift arc arc4_cv_baseline: cv_setup bash scripts/arc_submit.sh -c $(CONFIG)/cross_validation.yaml -o cross_validation +arc4_qaly_scenarios: MODE=default_config +arc4_qaly_scenarios: RUN_CONFIG=$(CONFIG)/default_noReplenishment.yaml +arc4_qaly_scenarios: arc4_baseline arc4_intervention_energyDownLift arc4_intervention_energyDownLiftNoSupport arc4_intervention_livingWage + +arc4_qaly_scenarios_no_replenishment: MODE=default_no_replenishment +arc4_qaly_scenarios_no_replenishment: RUN_CONFIG=$(CONFIG)/default_noReplenishment.yaml +arc4_qaly_scenarios_no_replenishment: arc4_baseline arc4_intervention_energyDownLift arc4_intervention_energyDownLiftNoSupport arc4_intervention_livingWage + ##################################### # Running scenarios on beefy HPC in LIDA. ##################################### From 928092a9baf8228fecc10ba63f659f29857294f3 Mon Sep 17 00:00:00 2001 From: ld-archer Date: Thu, 2 Nov 2023 15:39:08 +0000 Subject: [PATCH 066/229] Forgot to update the config in the new combined target... --- scripts/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/Makefile b/scripts/Makefile index b7c9901a..3f551a2c 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -78,7 +78,7 @@ all_scot_scenarios: RUN_CONFIG=$(CONFIG)/scot_default.yaml all_scot_scenarios: scot_setup baseline intervention_hhIncomeChildUplift intervention_PovertyLineChildUplift intervention_livingWage intervention_energyDownLift qaly_scenarios: MODE=default_config -qaly_scenarios: RUN_CONFIG=$(CONFIG)/default_noReplenishment.yaml +qaly_scenarios: RUN_CONFIG=$(CONFIG)/default_config.yaml qaly_scenarios: baseline intervention_energyDownLift intervention_energyDownLiftNoSupport intervention_livingWage qaly_scenarios_no_replenishment: MODE=default_no_replenishment @@ -126,7 +126,7 @@ arc4_cv_baseline: cv_setup bash scripts/arc_submit.sh -c $(CONFIG)/cross_validation.yaml -o cross_validation arc4_qaly_scenarios: MODE=default_config -arc4_qaly_scenarios: RUN_CONFIG=$(CONFIG)/default_noReplenishment.yaml +arc4_qaly_scenarios: RUN_CONFIG=$(CONFIG)/default_config.yaml arc4_qaly_scenarios: arc4_baseline arc4_intervention_energyDownLift arc4_intervention_energyDownLiftNoSupport arc4_intervention_livingWage arc4_qaly_scenarios_no_replenishment: MODE=default_no_replenishment From 76b9865449f742db10a1766c5f3c5f1f99cb78d9 Mon Sep 17 00:00:00 2001 From: ld-archer Date: Thu, 2 Nov 2023 15:44:50 +0000 Subject: [PATCH 067/229] More mistakes in config name... --- scripts/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/Makefile b/scripts/Makefile index 3f551a2c..8d15c28c 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -78,7 +78,7 @@ all_scot_scenarios: RUN_CONFIG=$(CONFIG)/scot_default.yaml all_scot_scenarios: scot_setup baseline intervention_hhIncomeChildUplift intervention_PovertyLineChildUplift intervention_livingWage intervention_energyDownLift qaly_scenarios: MODE=default_config -qaly_scenarios: RUN_CONFIG=$(CONFIG)/default_config.yaml +qaly_scenarios: RUN_CONFIG=$(CONFIG)/default.yaml qaly_scenarios: baseline intervention_energyDownLift intervention_energyDownLiftNoSupport intervention_livingWage qaly_scenarios_no_replenishment: MODE=default_no_replenishment @@ -126,7 +126,7 @@ arc4_cv_baseline: cv_setup bash scripts/arc_submit.sh -c $(CONFIG)/cross_validation.yaml -o cross_validation arc4_qaly_scenarios: MODE=default_config -arc4_qaly_scenarios: RUN_CONFIG=$(CONFIG)/default_config.yaml +arc4_qaly_scenarios: RUN_CONFIG=$(CONFIG)/default.yaml arc4_qaly_scenarios: arc4_baseline arc4_intervention_energyDownLift arc4_intervention_energyDownLiftNoSupport arc4_intervention_livingWage arc4_qaly_scenarios_no_replenishment: MODE=default_no_replenishment From 201b3ab0b7031a432609614ef9234eabc19b6a56 Mon Sep 17 00:00:00 2001 From: ld-archer Date: Thu, 2 Nov 2023 16:30:08 +0000 Subject: [PATCH 068/229] More updates to QALY comparisons script, and additions to Makefile for no replenishment runs --- minos/modules/replenishment.py | 28 ++++++++++++--- minos/outcomes/Makefile | 36 +++++++++++++++++--- minos/outcomes/QALY_calculation.py | 6 ++-- minos/testing/QALY_comparison_livingWage.Rmd | 32 ++++++++++++++--- 4 files changed, 85 insertions(+), 17 deletions(-) diff --git a/minos/modules/replenishment.py b/minos/modules/replenishment.py index 889fab31..d28386ba 100755 --- a/minos/modules/replenishment.py +++ b/minos/modules/replenishment.py @@ -290,7 +290,6 @@ def setup(self, builder): 'birth_year', 'nobs', 'region', - 'SF_12_MCS', 'hh_int_y', 'hh_int_m', 'Date', @@ -302,12 +301,12 @@ def setup(self, builder): 'smoker', 'loneliness', 'weight', - 'ndrinks', 'nkids', + 'nkids_ind', + 'ndrinks', 'max_educ', 'yearly_energy', 'job_sector', - 'SF_12_PCS', 'gross_pay_se', 'nutrition_quality', 'job_hours_se', @@ -317,14 +316,33 @@ def setup(self, builder): 'jb_inc_per', 'hourly_wage', 'gross_paypm', - 'marital_status', 'phealth', + 'marital_status', 'hh_comp', + #'labour_state', 'S7_labour_state', 'S7_housing_quality', 'S7_neighbourhood_safety', 'S7_physical_health', - 'S7_mental_health',] + 'S7_mental_health', + 'equivalent_income', + 'heating', + 'hhsize', + 'financial_situation', + 'housing_tenure', + 'urban', + 'auditc', + 'active', + 'SF_12_MCS', + 'SF_12_MCS_diff', + 'hh_income_diff', + 'nutrition_quality_diff', + 'SF_12_PCS', + 'SF_12_PCS_diff', + 'matdep', + 'matdep_diff', + 'chron_disease', + ] # Shorthand methods for readability. self.population_view = builder.population.get_view(view_columns) # view simulants diff --git a/minos/outcomes/Makefile b/minos/outcomes/Makefile index a7ba0ded..f19f970b 100644 --- a/minos/outcomes/Makefile +++ b/minos/outcomes/Makefile @@ -41,10 +41,38 @@ QALY_comparisons: QALY_baseline QALY_energyDownlift QALY_energyDownliftNoSupport $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises.Rmd')" $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" -#test_plot: MODE=default_config -#test_plot: ALIVE=who_alive -#test_plot: BOOSTED=who_boosted -#test_plot: aggregate_minos_output_energy +QALY_baseline_norepl: MODE=default_no_replenishment +QALY_baseline_norepl: INTERVENTION=baseline +QALY_baseline_norepl: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_energyDownlift_norepl: MODE=default_no_replenishment +QALY_energyDownlift_norepl: INTERVENTION=energyDownlift +QALY_energyDownlift_norepl: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_energyDownliftNoSupport_norepl: MODE=default_no_replenishment +QALY_energyDownliftNoSupport_norepl: INTERVENTION=energyDownliftNoSupport +QALY_energyDownliftNoSupport_norepl: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_livingWage_norepl: MODE=default_no_replenishment +QALY_livingWage_norepl: INTERVENTION=livingWageIntervention +QALY_livingWage_norepl: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_comparison_energyCrises_norepl: QALY_baseline_norepl QALY_energyDownlift_norepl QALY_energyDownliftNoSupport_norepl + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises.Rmd')" + firefox file://$(TESTING)/QALY_comparison_energyCrises.html + +QALY_comparison_livingWage_norepl: QALY_baseline_norepl QALY_livingWage_norepl + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" + firefox file://$(TESTING)/QALY_comparison_livingWage.html + +QALY_comparisons_norepl: QALY_baseline_norepl QALY_energyDownlift_norepl QALY_energyDownliftNoSupport_norepl QALY_livingWage_norepl + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises.Rmd')" + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" + ##################################### # Post-hoc aggregation of multiple MINOS runs on bash terminal. diff --git a/minos/outcomes/QALY_calculation.py b/minos/outcomes/QALY_calculation.py index be20e5c9..0ece71d6 100644 --- a/minos/outcomes/QALY_calculation.py +++ b/minos/outcomes/QALY_calculation.py @@ -56,7 +56,7 @@ def aggregate_csv(filename, intervention): # from both of these pieces of information we can calculate incidence of mortality total_pop_size = len(df) alive_pop = df['alive'].value_counts()['alive'] - dead = df['alive'].value_counts()['dead'] + dead_pop = df['alive'].value_counts()['dead'] # to investigate the mortality rate we can look at the ratio of dead to alive and compare across years alive_ratio = (alive_pop / total_pop_size) * 100 @@ -69,7 +69,7 @@ def aggregate_csv(filename, intervention): pop_boosted = df['income_boosted'].sum() total_boost = df['boost_amount'].sum() - return [run_id, alive_pop, pop_boosted, total_boost, alive_ratio, np.nanmean(df['SF_12_MCS']), np.nanmean(df['SF_12_PCS'])] + return [run_id, alive_pop, dead_pop, total_pop_size, pop_boosted, total_boost, alive_ratio, np.nanmean(df['SF_12_MCS']), np.nanmean(df['SF_12_PCS'])] def calculate_qaly(df): @@ -133,7 +133,7 @@ def main(mode, intervention): aggregated_means = pool.starmap(aggregate_csv, zip(files, repeat(intervention))) new_df = pd.DataFrame(aggregated_means) - new_df.columns = ['run_id', 'alive_pop', 'pop_boosted', 'total_boost', 'alive_ratio', 'SF_12_MCS', 'SF_12_PCS'] + new_df.columns = ['run_id', 'alive_pop', 'dead_pop', 'total_pop_size', 'pop_boosted', 'total_boost', 'alive_ratio', 'SF_12_MCS', 'SF_12_PCS'] new_df['year'] = year new_df['intervention'] = intervention combined_output = pd.concat([combined_output, new_df]) diff --git a/minos/testing/QALY_comparison_livingWage.Rmd b/minos/testing/QALY_comparison_livingWage.Rmd index 39d4909c..84a61566 100644 --- a/minos/testing/QALY_comparison_livingWage.Rmd +++ b/minos/testing/QALY_comparison_livingWage.Rmd @@ -39,7 +39,8 @@ source(here::here('minos', 'utils_datain.R')) ```{r} # # read in QALY files for baseline and interventions -out.path <- here::here('output', 'default_config') +#out.path <- here::here('output', 'default_config') +out.path <- here::here('output', 'default_no_replenishment') base.path <- get_latest_runtime_subdirectory(here::here(out.path, 'baseline')) wage.path <- get_latest_runtime_subdirectory(here::here(out.path, 'livingWageIntervention')) @@ -62,13 +63,33 @@ Just going to plot some things here like how many people affected by an interven ```{r} ggplot(filter(combined, intervention != 'baseline'), aes(x = year, y = pop_boosted, group = intervention, colour = intervention)) + - geom_smooth() + geom_smooth() + + labs(title = 'Number Individuals Receiving Intervention') ggplot(filter(combined, intervention != 'baseline'), aes(x = year, y = total_boost, group = intervention, colour = intervention)) + - geom_smooth() + geom_smooth() + + labs(title = 'Total Boost amount by year') ggplot(combined, aes(x = year, y = alive_ratio, group = intervention, colour = intervention)) + - geom_smooth() + geom_smooth() + + labs(title = 'Ratio of living to dead in output dataframe') + +## incidence of mortality +deaths <- combined %>% + select(run_id, intervention, year, alive_pop, dead_pop) %>% + group_by(run_id, intervention) %>% + mutate(total_pop = alive_pop + dead_pop, + deaths_this_year = dead_pop - lag(dead_pop)) %>% + group_by(run_id, year, intervention) %>% + summarise(death_incidence = deaths_this_year / total_pop) + +ggplot(deaths, aes(x = year, y = death_incidence, group = intervention, colour = intervention)) + + geom_smooth() + + labs(title = 'Incidence of Mortality Comparison') + + +# pivot_wider(names_from = 'year', +# values_from = 'dead_pop') ``` @@ -76,7 +97,8 @@ ggplot(combined, aes(x = year, y = alive_ratio, group = intervention, colour = i ```{r} ggplot(data = combined, aes(x = year, y = QALYs, group = intervention, colour = intervention)) + - geom_smooth() + geom_smooth() + + labs(title = 'QALYs per year') # Now change from baseline combined.QALY.change <- combined %>% From 8b22f8a3209d6e717d1f7e3ee766622c85c98e65 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 6 Nov 2023 10:21:10 +0000 Subject: [PATCH 069/229] Included some diagnostic histograms in QALY comparison scripts --- .../testing/QALY_comparison_energyCrises.Rmd | 163 +++++++++++++++++- minos/testing/QALY_comparison_livingWage.Rmd | 94 +++++++++- 2 files changed, 249 insertions(+), 8 deletions(-) diff --git a/minos/testing/QALY_comparison_energyCrises.Rmd b/minos/testing/QALY_comparison_energyCrises.Rmd index d09b0b68..9d4d8212 100644 --- a/minos/testing/QALY_comparison_energyCrises.Rmd +++ b/minos/testing/QALY_comparison_energyCrises.Rmd @@ -54,16 +54,139 @@ noSupport <- read.csv(paste0(noSupport.path, 'qalys.csv')) # base <- read.csv(paste0(out.path, 'qaly_baseline.csv')) # support <- read.csv(paste0(out.path, 'qalys_support.csv')) # noSupport <- read.csv(paste0(out.path, 'qalys_noSupport.csv')) + +# combine the dataframes +combined <- rbind(base, support, noSupport) ``` -# QALY comparison +# Intervention Information + +Just going to plot some things here like how many people affected by an intervention and what the mean / total boost amounts look like. ```{r} -# combine the dataframes -combined <- rbind(base, support, noSupport) +ggplot(filter(combined, intervention != 'baseline'), aes(x = year, y = pop_boosted, group = intervention, colour = intervention)) + + geom_smooth() + + labs(title = 'Number Individuals Receiving Intervention') + +ggplot(filter(combined, intervention != 'baseline'), aes(x = year, y = total_boost, group = intervention, colour = intervention)) + + geom_smooth() + + labs(title = 'Total Boost amount by year') + +ggplot(combined, aes(x = year, y = alive_ratio, group = intervention, colour = intervention)) + + geom_smooth() + + labs(title = 'Ratio of living to dead in output dataframe') + +## incidence of mortality +deaths <- combined %>% + select(run_id, intervention, year, alive_pop, dead_pop) %>% + group_by(run_id, intervention) %>% + mutate(total_pop = alive_pop + dead_pop, + deaths_this_year = dead_pop - lag(dead_pop)) %>% + group_by(run_id, year, intervention) %>% + summarise(death_incidence = deaths_this_year / total_pop) + +ggplot(deaths, aes(x = year, y = death_incidence, group = intervention, colour = intervention)) + + geom_smooth() + + labs(title = 'Incidence of Mortality Comparison') + + +pivot_wider(names_from = 'year', + values_from = 'dead_pop') +``` +# QALY comparison + +```{r} ggplot(data = combined, aes(x = year, y = QALYs, group = intervention, colour = intervention)) + - geom_smooth() + geom_smooth() + + labs(title = 'QALYs per year') + +# Now change from baseline +combined.QALY.change <- combined %>% + select(run_id, year, intervention, QALYs) %>% + pivot_wider(names_from = 'intervention', + values_from = 'QALYs') %>% + mutate(QALYs_Support = energyDownlift - baseline, + QALYs_NoSupport = energyDownliftNoSupport - baseline) %>% + select(run_id, year, QALYs_Support, QALYs_NoSupport) %>% + pivot_longer(cols = QALYs_Support:QALYs_NoSupport, + names_prefix = 'QALYs_', + names_to = 'intervention', + values_to = 'QALYs') + +ggplot(data = combined.QALY.change, aes(x = year, y = QALYs, group = intervention, colour = intervention)) + + geom_hline(yintercept = 0, linetype = 'dashed') + + geom_smooth() + + labs(title = 'QALY change from Baseline') +``` + +Can check histogram over each run to see the range of QALYs. + +```{r} +# histogram of qalys +ggplot(combined, aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + + geom_histogram() + + facet_wrap(~year) + +ggplot(filter(combined, year == 2022), aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + + geom_histogram() + + labs(title = 'QALYs by run', subtitle = '2022') + +ggplot(filter(combined, year == 2028), aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + + geom_histogram() + + labs(title = 'QALYs by run', subtitle = '2028') + +ggplot(filter(combined, year == 2034), aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + + geom_histogram() + + labs(title = 'QALYs by run', subtitle = '2034') +``` + +and histograms of change... + +```{r} +# histogram of qalys +ggplot(combined.QALY.change, aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + + geom_histogram() + + facet_wrap(~year) + + labs(title = 'QALY change by run', subtitle = 'All Years') + + xlab('QALY Change') + +ggplot(filter(combined.QALY.change, year == 2022), aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + + geom_histogram() + + labs(title = 'QALY change by run', subtitle = '2022') + + xlab('QALY Change') + +ggplot(filter(combined.QALY.change, year == 2028), aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + + geom_histogram() + + labs(title = 'QALY change by run', subtitle = '2028') + + xlab('QALY Change') + +ggplot(filter(combined.QALY.change, year == 2034), aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + + geom_histogram() + + labs(title = 'QALY change by run', subtitle = '2034') + + xlab('QALY Change') +``` + +...and SF_12 MCS and PCS for good measure. + +```{r} +ggplot(combined, aes(x = year, y = SF_12_MCS, group = intervention, colour = intervention)) + + geom_smooth() + + labs(title = 'SF_12_MCS') + +ggplot(combined, aes(x = SF_12_MCS, group = intervention, colour = intervention, fill = intervention)) + + geom_histogram() + + facet_wrap(~year) + + labs(title = 'SF_12_MCS') + +ggplot(combined, aes(x = year, y = SF_12_PCS, group = intervention, colour = intervention)) + + geom_smooth() + + labs(title = 'SF_12_PCS') + +ggplot(combined, aes(x = SF_12_PCS, group = intervention, colour = intervention, fill = intervention)) + + geom_histogram() + + facet_wrap(~year) + + labs(title = 'SF_12_PCS') ``` ## Area Under the Curve Comparison @@ -129,6 +252,10 @@ ggplot(combined.auc2.plot, aes(x = intervention, y = AUC_difference, group = int geom_errorbar(aes(ymin = AUC_difference - se, ymax = AUC_difference + se, width = 0.4, group = intervention)) + labs(title = 'Change in AUC', subtitle = 'Baseline vs Energy Crisis Interventions') + xlab('Intervention') + +ggplot(combined.auc2, aes(x = AUC_difference, group = intervention, colour = intervention, fill = intervention)) + + geom_histogram() + + labs(title = 'Area Under Curve change by run') ``` # Cost per QALY @@ -161,6 +288,11 @@ combined.cost.change <- combined.cost %>% ggplot(combined.cost.change, aes(x = year, y = QALY_value_change, group = intervention, colour = intervention, fill = intervention)) + geom_smooth() + scale_y_continuous(label = comma) + +ggplot(combined.cost.change, aes(x = QALY_value_change, group = intervention, colour = intervention, fill = intervention)) + + geom_histogram() + + facet_wrap(~year) + + labs(title = 'QALY value change') ``` ```{r} @@ -226,6 +358,29 @@ ggplot(ICER.final, aes(x = intervention, y = ICER, group = intervention, fill = xlab('Intervention') ``` +```{r} +ggplot(ICER, aes(x = ICER, group = intervention, colour = intervention, fill = intervention)) + + geom_histogram() + + facet_wrap(~year) + + labs(title = 'ICER by run', subtitle = 'All Years') + +ggplot(filter(ICER, year == 2022), aes(x = ICER, group = intervention, colour = intervention, fill = intervention)) + + geom_histogram() + + labs(title = 'ICER by run', subtitle = '2022') + +ggplot(filter(ICER, year == 2028), aes(x = ICER, group = intervention, colour = intervention, fill = intervention)) + + geom_histogram() + + labs(title = 'ICER by run', subtitle = '2028') + +ggplot(filter(ICER, year == 2031), aes(x = ICER, group = intervention, colour = intervention, fill = intervention)) + + geom_histogram() + + labs(title = 'ICER by run', subtitle = '2031') + +ggplot(filter(ICER, year == 2034), aes(x = ICER, group = intervention, colour = intervention, fill = intervention)) + + geom_histogram() + + labs(title = 'ICER by run', subtitle = '2034') +``` + ```{r} ``` diff --git a/minos/testing/QALY_comparison_livingWage.Rmd b/minos/testing/QALY_comparison_livingWage.Rmd index 84a61566..dedb5eb9 100644 --- a/minos/testing/QALY_comparison_livingWage.Rmd +++ b/minos/testing/QALY_comparison_livingWage.Rmd @@ -40,7 +40,7 @@ source(here::here('minos', 'utils_datain.R')) ```{r} # # read in QALY files for baseline and interventions #out.path <- here::here('output', 'default_config') -out.path <- here::here('output', 'default_no_replenishment') +out.path <- here::here('output', 'default_config') base.path <- get_latest_runtime_subdirectory(here::here(out.path, 'baseline')) wage.path <- get_latest_runtime_subdirectory(here::here(out.path, 'livingWageIntervention')) @@ -88,8 +88,8 @@ ggplot(deaths, aes(x = year, y = death_incidence, group = intervention, colour = labs(title = 'Incidence of Mortality Comparison') -# pivot_wider(names_from = 'year', -# values_from = 'dead_pop') +pivot_wider(names_from = 'year', + values_from = 'dead_pop') ``` @@ -115,14 +115,65 @@ ggplot(data = combined.QALY.change, aes(x = year, y = QALYs, group = interventio labs(title = 'QALY change from Baseline') ``` +Can check histogram over each run to see the range of QALYs. + +```{r} +# histogram of qalys +ggplot(combined, aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + + geom_histogram() + + facet_wrap(~year) + +ggplot(filter(combined, year == 2022), aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + + geom_histogram() + + labs(title = 'QALYs by run', subtitle = '2022') + +ggplot(filter(combined, year == 2028), aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + + geom_histogram() + + labs(title = 'QALYs by run', subtitle = '2028') + +ggplot(filter(combined, year == 2034), aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + + geom_histogram() + + labs(title = 'QALYs by run', subtitle = '2034') +``` + +and histograms of change... + +```{r} +# histogram of qalys +ggplot(combined.QALY.change, aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + + geom_histogram() + + facet_wrap(~year) + +ggplot(filter(combined.QALY.change, year == 2022), aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + + geom_histogram() + + labs(title = 'QALY change by run', subtitle = '2022') + +ggplot(filter(combined.QALY.change, year == 2028), aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + + geom_histogram() + + labs(title = 'QALY change by run', subtitle = '2028') + +ggplot(filter(combined.QALY.change, year == 2034), aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + + geom_histogram() + + labs(title = 'QALY change by run', subtitle = '2034') +``` + + ...and SF_12 MCS and PCS for good measure. ```{r} ggplot(combined, aes(x = year, y = SF_12_MCS, group = intervention, colour = intervention)) + geom_smooth() +ggplot(combined, aes(x = SF_12_MCS, group = intervention, colour = intervention, fill = intervention)) + + geom_histogram() + + facet_wrap(~year) + ggplot(combined, aes(x = year, y = SF_12_PCS, group = intervention, colour = intervention)) + geom_smooth() + +ggplot(combined, aes(x = SF_12_PCS, group = intervention, colour = intervention, fill = intervention)) + + geom_histogram() + + facet_wrap(~year) ``` ## Area Under the Curve Comparison @@ -182,6 +233,10 @@ ggplot(combined.auc2.plot, aes(x = intervention, y = AUC_difference, group = int geom_errorbar(aes(ymin = AUC_difference - se, ymax = AUC_difference + se, width = 0.4, group = intervention)) + labs(title = 'Change in AUC', subtitle = 'Baseline vs Living Wage Intervention') + xlab('Intervention') + +ggplot(combined.auc2, aes(x = AUC_difference, group = intervention, colour = intervention, fill = intervention)) + + geom_histogram() + + labs(title = 'Area Under Curve change by run') ``` # Cost per QALY @@ -190,7 +245,8 @@ The [UK Government](https://www.gov.uk/government/publications/valuation-of-risk ```{r} combined.cost <- combined %>% - select(-alive_pop, -SF_12_MCS, -SF_12_PCS, -utility) %>% + #select(-alive_pop, -SF_12_MCS, -SF_12_PCS, -utility) %>% + select(run_id, year, pop_size, total_boost, intervention, QALYs) %>% mutate(QALY_value = QALYs * 60000) ggplot(combined.cost, aes(x = year, y = QALY_value, group = intervention, colour = intervention)) + @@ -231,6 +287,11 @@ ggplot(total.combined.cost, aes(x = intervention, y = TotalChange, group = inter scale_y_continuous(label = comma) + labs(title = 'Total Change in QALY Value', subtitle = 'Baseline vs Living Wage Intervention') + xlab('Intervention') + +ggplot(combined.cost.change, aes(x = QALY_value_change, group = intervention, colour = intervention, fill = intervention)) + + geom_histogram() + + facet_wrap(~year) + + labs(title = 'QALY value change') ``` # Incremental Cost-Effectiveness Ratio @@ -277,10 +338,35 @@ ggplot(ICER.final, aes(x = intervention, y = ICER, group = intervention, fill = scale_y_continuous(label = comma) + labs(title = 'ICER', subtitle = 'Living Wage Intervention') + xlab('Intervention') + ``` +```{r} +ggplot(ICER, aes(x = ICER, group = intervention, colour = intervention, fill = intervention)) + + geom_histogram() + + facet_wrap(~year) + + labs(title = 'ICER by run', subtitle = 'All Years') + +ggplot(filter(ICER, year == 2022), aes(x = ICER, group = intervention, colour = intervention, fill = intervention)) + + geom_histogram() + + labs(title = 'ICER by run', subtitle = '2022') + +ggplot(filter(ICER, year == 2028), aes(x = ICER, group = intervention, colour = intervention, fill = intervention)) + + geom_histogram() + + labs(title = 'ICER by run', subtitle = '2028') + +ggplot(filter(ICER, year == 2031), aes(x = ICER, group = intervention, colour = intervention, fill = intervention)) + + geom_histogram() + + labs(title = 'ICER by run', subtitle = '2031') + +ggplot(filter(ICER, year == 2034), aes(x = ICER, group = intervention, colour = intervention, fill = intervention)) + + geom_histogram() + + labs(title = 'ICER by run', subtitle = '2034') +``` + + ```{r} From 8ed90c4c4a0b182bd079149fc5d6de3881d675cb Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 6 Nov 2023 10:50:18 +0000 Subject: [PATCH 070/229] Remove a line that was left by accident --- minos/testing/QALY_comparison_energyCrises.Rmd | 4 ---- minos/testing/QALY_comparison_livingWage.Rmd | 4 ---- 2 files changed, 8 deletions(-) diff --git a/minos/testing/QALY_comparison_energyCrises.Rmd b/minos/testing/QALY_comparison_energyCrises.Rmd index 9d4d8212..a200e1d7 100644 --- a/minos/testing/QALY_comparison_energyCrises.Rmd +++ b/minos/testing/QALY_comparison_energyCrises.Rmd @@ -88,10 +88,6 @@ deaths <- combined %>% ggplot(deaths, aes(x = year, y = death_incidence, group = intervention, colour = intervention)) + geom_smooth() + labs(title = 'Incidence of Mortality Comparison') - - -pivot_wider(names_from = 'year', - values_from = 'dead_pop') ``` # QALY comparison diff --git a/minos/testing/QALY_comparison_livingWage.Rmd b/minos/testing/QALY_comparison_livingWage.Rmd index dedb5eb9..2526117b 100644 --- a/minos/testing/QALY_comparison_livingWage.Rmd +++ b/minos/testing/QALY_comparison_livingWage.Rmd @@ -86,10 +86,6 @@ deaths <- combined %>% ggplot(deaths, aes(x = year, y = death_incidence, group = intervention, colour = intervention)) + geom_smooth() + labs(title = 'Incidence of Mortality Comparison') - - -pivot_wider(names_from = 'year', - values_from = 'dead_pop') ``` From 17f610902444913b6aec08c22ca98907c7e0ff25 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 6 Nov 2023 10:52:13 +0000 Subject: [PATCH 071/229] Fixed another typo --- minos/testing/QALY_comparison_energyCrises.Rmd | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/minos/testing/QALY_comparison_energyCrises.Rmd b/minos/testing/QALY_comparison_energyCrises.Rmd index a200e1d7..e7f1ab6f 100644 --- a/minos/testing/QALY_comparison_energyCrises.Rmd +++ b/minos/testing/QALY_comparison_energyCrises.Rmd @@ -260,7 +260,8 @@ The [UK Government](https://www.gov.uk/government/publications/valuation-of-risk ```{r} combined.cost <- combined %>% - select(-pop_size, -SF_12_MCS, -SF_12_PCS, -utility) %>% + #select(-pop_size, -SF_12_MCS, -SF_12_PCS, -utility) %>% + select(run_id, year, pop_size, total_boost, intervention, QALYs) %>% mutate(QALY_value = QALYs * 60000) ggplot(combined.cost, aes(x = year, y = QALY_value, group = intervention, colour = intervention)) + From f76ff77800b976da7dfbe3ec02debba1396aa451 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 6 Nov 2023 10:55:44 +0000 Subject: [PATCH 072/229] Another one... --- minos/testing/QALY_comparison_energyCrises.Rmd | 2 +- minos/testing/QALY_comparison_livingWage.Rmd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/minos/testing/QALY_comparison_energyCrises.Rmd b/minos/testing/QALY_comparison_energyCrises.Rmd index e7f1ab6f..d8728960 100644 --- a/minos/testing/QALY_comparison_energyCrises.Rmd +++ b/minos/testing/QALY_comparison_energyCrises.Rmd @@ -261,7 +261,7 @@ The [UK Government](https://www.gov.uk/government/publications/valuation-of-risk ```{r} combined.cost <- combined %>% #select(-pop_size, -SF_12_MCS, -SF_12_PCS, -utility) %>% - select(run_id, year, pop_size, total_boost, intervention, QALYs) %>% + select(run_id, year, alive_pop, total_boost, intervention, QALYs) %>% mutate(QALY_value = QALYs * 60000) ggplot(combined.cost, aes(x = year, y = QALY_value, group = intervention, colour = intervention)) + diff --git a/minos/testing/QALY_comparison_livingWage.Rmd b/minos/testing/QALY_comparison_livingWage.Rmd index 2526117b..5e64fa61 100644 --- a/minos/testing/QALY_comparison_livingWage.Rmd +++ b/minos/testing/QALY_comparison_livingWage.Rmd @@ -242,7 +242,7 @@ The [UK Government](https://www.gov.uk/government/publications/valuation-of-risk ```{r} combined.cost <- combined %>% #select(-alive_pop, -SF_12_MCS, -SF_12_PCS, -utility) %>% - select(run_id, year, pop_size, total_boost, intervention, QALYs) %>% + select(run_id, year, alive_pop, total_boost, intervention, QALYs) %>% mutate(QALY_value = QALYs * 60000) ggplot(combined.cost, aes(x = year, y = QALY_value, group = intervention, colour = intervention)) + From 32d0b5e21e90b3ef976979e511de73247b922658 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 6 Nov 2023 14:07:34 +0000 Subject: [PATCH 073/229] New visualisation script for comparing energyDownlift with energyDownliftNoSupport --- minos/outcomes/Makefile | 4 + .../testing/QALY_comparison_energyCrises.Rmd | 1 + .../QALY_comparison_energyCrises_diff.Rmd | 179 ++++++++++++++++++ minos/testing/QALY_comparison_livingWage.Rmd | 33 +--- 4 files changed, 189 insertions(+), 28 deletions(-) create mode 100644 minos/testing/QALY_comparison_energyCrises_diff.Rmd diff --git a/minos/outcomes/Makefile b/minos/outcomes/Makefile index f19f970b..e50e98c8 100644 --- a/minos/outcomes/Makefile +++ b/minos/outcomes/Makefile @@ -33,6 +33,10 @@ QALY_comparison_energyCrises: QALY_baseline QALY_energyDownlift QALY_energyDownl $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises.Rmd')" firefox file://$(TESTING)/QALY_comparison_energyCrises.html +QALY_comparison_energyCrises_diff: QALY_energyDownlift QALY_energyDownliftNoSupport + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises_diff.Rmd')" + firefox file://$(TESTING)/QALY_comparison_energyCrises_diff.html + QALY_comparison_livingWage: QALY_baseline QALY_livingWage $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" firefox file://$(TESTING)/QALY_comparison_livingWage.html diff --git a/minos/testing/QALY_comparison_energyCrises.Rmd b/minos/testing/QALY_comparison_energyCrises.Rmd index d8728960..58f5c22c 100644 --- a/minos/testing/QALY_comparison_energyCrises.Rmd +++ b/minos/testing/QALY_comparison_energyCrises.Rmd @@ -32,6 +32,7 @@ rm(workingDir) ```{r} source(here::here('minos', 'utils_datain.R')) +source(here::here('minos', 'utils_qaly.R')) ``` diff --git a/minos/testing/QALY_comparison_energyCrises_diff.Rmd b/minos/testing/QALY_comparison_energyCrises_diff.Rmd new file mode 100644 index 00000000..a5944efa --- /dev/null +++ b/minos/testing/QALY_comparison_energyCrises_diff.Rmd @@ -0,0 +1,179 @@ +--- +title: "QALY Comparisons - Energy Crisis Interventions - NoSupport Baseline" +output: + html_document: + toc: true + toc_float: true + collapsed: false + number_sections: true + df_print: paged + code_folding: hide +--- + +# SETUP + +```{r "setup", include=FALSE} + +require(tidyverse) +require(ggplot2) +require(knitr) +require(here) +require(MESS) +require(scales) + +#workingDir <- "/home/luke/Documents/WORK/MINOS/" +workingDir <- normalizePath('../') +knitr::opts_knit$set(root.dir = workingDir) +knitr::opts_chunk$set( + options(scipen = 999) +) +rm(workingDir) +``` + +```{r} +source(here::here('minos', 'utils_datain.R')) +source(here::here('minos', 'utils_qaly.R')) +``` + + +## Data + +```{r} +# # read in QALY files for baseline and interventions +out.path <- here::here('output', 'default_config') + +base.path <- get_latest_runtime_subdirectory(here::here(out.path, 'baseline')) +support.path <- get_latest_runtime_subdirectory(here::here(out.path, 'energyDownlift')) +noSupport.path <- get_latest_runtime_subdirectory(here::here(out.path, 'energyDownliftNoSupport')) + +base <- read.csv(paste0(base.path, 'qalys.csv')) +support <- read.csv(paste0(support.path, 'qalys.csv')) +noSupport <- read.csv(paste0(noSupport.path, 'qalys.csv')) + +# out.path <- '/home/luke/Documents/WORK/MINOS/PLOTS_FROM_ARC/QALY_TESTING/' +# +# base <- read.csv(paste0(out.path, 'qaly_baseline.csv')) +# support <- read.csv(paste0(out.path, 'qalys_support.csv')) +# noSupport <- read.csv(paste0(out.path, 'qalys_noSupport.csv')) + +# combine the dataframes +combined <- rbind(support, noSupport) +``` + +# Intervention Information + +Just going to plot some things here like how many people affected by an intervention and what the mean / total boost amounts look like. + +```{r} +ggplot(combined, aes(x = year, y = pop_boosted, group = intervention, colour = intervention)) + + geom_smooth() + + labs(title = 'Number Individuals Receiving Intervention') + +combined.diff <- combined %>% + select(run_id, year, intervention, total_boost) %>% + pivot_wider(names_from = 'intervention', + values_from = 'total_boost') %>% + mutate(diff.boost = energyDownlift - energyDownliftNoSupport) + +ggplot(combined.diff, aes(x = year, y = diff.boost)) + + geom_smooth() + + labs(title = 'Total Boost amount by year') + + scale_y_continuous(label = comma) + +ggplot(combined, aes(x = year, y = alive_ratio, group = intervention, colour = intervention)) + + geom_smooth() + + labs(title = 'Ratio of living to dead in output dataframe') + +## incidence of mortality +deaths <- combined %>% + select(run_id, intervention, year, alive_pop, dead_pop) %>% + group_by(run_id, intervention) %>% + mutate(total_pop = alive_pop + dead_pop, + deaths_this_year = dead_pop - lag(dead_pop)) %>% + group_by(run_id, year, intervention) %>% + summarise(death_incidence = deaths_this_year / total_pop) + +ggplot(deaths, aes(x = year, y = death_incidence, group = intervention, colour = intervention)) + + geom_smooth() + + labs(title = 'Incidence of Mortality Comparison') +``` + +# QALY comparison + +```{r} +ggplot(data = combined, aes(x = year, y = QALYs, group = intervention, colour = intervention)) + + geom_smooth() + + labs(title = 'QALYs per year') + +# Now change from baseline +combined.QALY.change <- combined %>% + select(run_id, year, intervention, QALYs) %>% + pivot_wider(names_from = 'intervention', + values_from = 'QALYs') %>% + mutate(QALYs = energyDownlift - energyDownliftNoSupport) %>% + select(run_id, year, QALYs) + +ggplot(data = combined.QALY.change, aes(x = year, y = QALYs)) + + geom_hline(yintercept = 0, linetype = 'dashed') + + geom_smooth() + + labs(title = 'QALY change', subtitle = 'Support vs No Support') +``` + +## Area Under the Curve Comparison + +```{r} +auc.plots(base = noSupport, + base.name = 'NoSupport', + intervention = support, + int.name = 'Support') +``` + +# Cost per QALY + +The [UK Government](https://www.gov.uk/government/publications/valuation-of-risks-to-life-and-health-monetary-value-of-a-life-year-voly/a-scoping-study-on-the-valuation-of-risks-to-life-and-health-the-monetary-value-of-a-life-year-voly) has set the value of 1 QALY to be equal to $60,000. This is the number we will use for our calculations. + +```{r} +cost.per.qaly(base = noSupport, + base.name = 'energyDownliftNoSupport', + int = support, + int.name = 'energyDownlift', + QALY_value = 60000) # QALY value == £60,000 +``` + +# Incremental Cost-Effectiveness Ratio + +From [NICE guidelines](https://www.nice.org.uk/process/pmg6/chapter/assessing-cost-effectiveness) about the cost-effectiveness of health interventions (7.3), and works with a statistic called the Incremental Cost-Effectiveness Ratio (ICER). The formula for calculating the ICER can be found [here](https://en.wikipedia.org/wiki/Incremental_cost-effectiveness_ratio), but in short, it represents the average incremental cost associated with 1 additional unit of the measurement of effect. In our case, the unit of measurement of effect is a QALY. + +From the NICE guidelines, there is a general consensus that anything below £20,000 is cost effective, and anything above £30,000 is not. Between £20,000 and £30,000 is a grey area, and depends on the field and other factors to decide (i.e. if a rare or important condition is tackled by an intervention then the cost-effective threshold is higher). + +```{r} +ICER(base = noSupport, + base.name = 'energyDownliftNoSupport', + int = support, + int.name = 'energyDownlift', + QALY_value = 60000) +``` + + + + + + + + + + + + +```{r} + +``` + + + + + + + + + diff --git a/minos/testing/QALY_comparison_livingWage.Rmd b/minos/testing/QALY_comparison_livingWage.Rmd index 5e64fa61..f317f1a1 100644 --- a/minos/testing/QALY_comparison_livingWage.Rmd +++ b/minos/testing/QALY_comparison_livingWage.Rmd @@ -32,6 +32,7 @@ rm(workingDir) ```{r} source(here::here('minos', 'utils_datain.R')) +source(here::here('minos', 'utils_qaly.R')) ``` @@ -205,34 +206,10 @@ ggplot(data = combined.auc.plot, aes(x = intervention, y = AUC, group = interven ``` ```{r} -# Another version showing difference from baseline for better comparison -combined.auc2 <- combined.auc %>% - pivot_wider(names_from = 'intervention', - values_from = c('AUC')) %>% - mutate(change_baseline = baseline - baseline, - change_LivingWage = LivingWage - baseline) %>% - pivot_longer(cols = change_baseline:change_LivingWage, - names_to = 'intervention', - names_prefix = 'change_', - values_to = 'AUC_difference') %>% - filter(intervention != 'baseline') - -print(combined.auc2) - -combined.auc2.plot <- combined.auc2 %>% - group_by(intervention) %>% - summarise(se = sd(AUC_difference) / sqrt(n()), - AUC_difference = mean(AUC_difference)) - -ggplot(combined.auc2.plot, aes(x = intervention, y = AUC_difference, group = intervention, fill = intervention)) + - geom_col() + - geom_errorbar(aes(ymin = AUC_difference - se, ymax = AUC_difference + se, width = 0.4, group = intervention)) + - labs(title = 'Change in AUC', subtitle = 'Baseline vs Living Wage Intervention') + - xlab('Intervention') - -ggplot(combined.auc2, aes(x = AUC_difference, group = intervention, colour = intervention, fill = intervention)) + - geom_histogram() + - labs(title = 'Area Under Curve change by run') +auc.plots(base = base, + base.name = 'baseline', + intervention = wage, + int.name = 'LivingWage') ``` # Cost per QALY From d385224a1a3850209d62f0228f5af5271d312646 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 6 Nov 2023 14:12:13 +0000 Subject: [PATCH 074/229] Started a utils script for visualising qaly outputs --- minos/utils_qaly.R | 171 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 minos/utils_qaly.R diff --git a/minos/utils_qaly.R b/minos/utils_qaly.R new file mode 100644 index 00000000..6a23618c --- /dev/null +++ b/minos/utils_qaly.R @@ -0,0 +1,171 @@ +### QALY calculation plots and utility functions + + +# AUC Plots: +# This function will plot the total AUC (which equals the total QALYs in the +# system) for 2 interventions, and the change in AUC between the 2. +auc.plots <- function(base, base.name, intervention, int.name) { + base.auc <- base %>% + group_by(run_id) %>% + summarise(AUC = auc(x=year, y = QALYs, type = 'spline')) %>% + mutate(intervention = base.name) + + int.auc <- intervention %>% + group_by(run_id) %>% + summarise(AUC = auc(x=year, y = QALYs, type = 'spline')) %>% + mutate(intervention = int.name) + + combined.auc <- rbind(base.auc, int.auc) + + print(combined.auc) + + combined.auc.plot <- combined.auc %>% + group_by(intervention) %>% + summarise(sd = sd(AUC), + margin = (qt(0.975, df = n() - 1) * sd) / sqrt(n()), + AUC = mean(AUC)) + + p1 <- ggplot(data = combined.auc.plot, aes(x = intervention, y = AUC, group = intervention, fill = intervention)) + + geom_col() + + geom_errorbar(aes(ymin = AUC - margin, ymax = AUC + margin, width = 0.4, group = intervention)) + + labs(title = 'Total AUC comparison', subtitle = paste0(int.name, ' vs ', base.name)) + + xlab('Intervention') + print(p1) + + # Another version showing difference from baseline for easier comparison + combined.auc2 <- combined.auc %>% + pivot_wider(names_from = 'intervention', + values_from = c('AUC')) %>% + mutate(change_baseline = .data[[base.name]] - .data[[base.name]], + change_intervention = .data[[int.name]] - .data[[base.name]]) %>% + pivot_longer(cols = change_baseline:change_intervention, + names_to = 'intervention', + names_prefix = 'change_', + values_to = 'AUC_difference') %>% + filter(intervention != 'baseline') + + print(combined.auc2) + + combined.auc2.plot <- combined.auc2 %>% + group_by(intervention) %>% + summarise(margin = (qt(0.975, df = n() - 1) * sd(AUC_difference)) / sqrt(n()), + AUC_difference = mean(AUC_difference)) + + p2 <- ggplot(combined.auc2.plot, aes(x = intervention, y = AUC_difference, group = intervention, fill = intervention)) + + geom_col() + + geom_errorbar(aes(ymin = AUC_difference - margin, ymax = AUC_difference + margin, width = 0.4, group = intervention)) + + labs(title = 'Change in AUC', subtitle = paste0(int.name, ' vs ', base.name)) + + xlab('Intervention') + + print(p2) + + print(paste0('Change in AUC == ', combined.auc2.plot$AUC_difference, ' +- ', combined.auc2.plot$margin)) +} + + + +## COST PER QALY +# This function will calculate and plot the cost per QALY over time, as well +# as the total cost change over the length of the simulation. +cost.per.qaly <- function(base, base.name, int, int.name, QALY_value) { + combined <- rbind(base, int) + + combined.cost <- combined %>% + #select(-alive_pop, -SF_12_MCS, -SF_12_PCS, -utility) %>% + select(run_id, year, alive_pop, total_boost, intervention, QALYs) %>% + mutate(QALY_value = QALYs * QALY_value) + + p1 <-ggplot(combined.cost, aes(x = year, y = QALY_value, group = intervention, colour = intervention)) + + geom_smooth() + + scale_y_continuous(label = comma) + + labs(title = 'QALY value comparison', subtitle = paste0(int.name, ' vs ', base.name)) + print(p1) + + combined.cost.change <- combined.cost %>% + select(run_id, year, intervention, QALY_value) %>% + pivot_wider(names_from = 'intervention', + values_from = 'QALY_value') %>% + mutate(change_baseline = .data[[base.name]] - .data[[base.name]], + change_intervention = .data[[int.name]] - .data[[base.name]]) %>% + select(run_id, year, change_baseline, change_intervention) %>% + pivot_longer(cols = change_baseline:change_intervention, + names_to = 'intervention', + names_prefix = 'change_', + values_to = 'QALY_value_change') %>% + filter(intervention != 'baseline') + + p2 <- ggplot(combined.cost.change, aes(x = year, y = QALY_value_change, group = intervention, colour = intervention, fill = intervention)) + + geom_smooth() + + geom_hline(yintercept = 0, linetype = 'dashed') + + scale_y_continuous(label = comma) + + labs(title = 'QALY value change', subtitle = paste0(int.name, ' vs ', base.name)) + print(p2) + + total.combined.cost <- combined.cost.change %>% + group_by(run_id, intervention) %>% + summarise(TotalChange = sum(QALY_value_change)) %>% + group_by(intervention) %>% + summarise(margin = (qt(0.975, df = n() - 1) * sd(TotalChange)) / sqrt(n()), + TotalChange = mean(TotalChange)) + + p3 <- ggplot(total.combined.cost, aes(x = intervention, y = TotalChange, group = intervention, fill = intervention)) + + geom_col() + + geom_errorbar(aes(ymin = TotalChange - margin, ymax = TotalChange + margin, width = 0.4, group = intervention)) + + scale_y_continuous(label = comma) + + labs(title = 'Total Change in QALY Value', subtitle = paste0(int.name, ' vs ', base.name)) + + xlab('Intervention') + print(p3) +} + + +ICER <- function(base, base.name, int, int.name, QALY_value) { + combined <- rbind(base, int) + + combined.cost.mean <- combined %>% + #select(-alive_pop, -SF_12_MCS, -SF_12_PCS, -utility) %>% + select(run_id, year, total_boost, intervention, QALYs) %>% + mutate(QALY_value = QALYs * QALY_value) %>% + group_by(run_id, year, intervention) %>% + summarise(across(everything(), mean)) + + # ICER = (C1 - C0) / (E1 - E0) + # where + # C1 = cost of intervention + # E1 = effect of intervention + # C0 = cost of control group + # E0 = effect of control group + + ICER <- combined.cost.mean %>% + pivot_wider(names_from = 'intervention', + values_from = c('total_boost', 'QALYs', 'QALY_value')) %>% + mutate(ICER = (total_boost_energyDownlift - total_boost_energyDownliftNoSupport) / (QALYs_energyDownlift - QALYs_energyDownliftNoSupport)) %>% + select(run_id, year, ICER) %>% + pivot_longer(cols = -c(run_id, year), + names_prefix = 'ICER_', + names_to = 'intervention', + values_to = 'ICER') + + p1 <- ggplot(ICER, aes(x = year, y = ICER, group = intervention, fill = intervention, colour = intervention)) + + geom_smooth() + + scale_y_continuous(label = comma) + + labs(title = 'ICER', subtitle = paste0(int.name, ' vs ', base.name)) + print(p1) + + ICER.final <- ICER %>% + group_by(run_id, intervention) %>% + summarise(ICER = mean(ICER)) %>% + group_by(intervention) %>% + summarise(margin = (qt(0.975, df=n() - 1) * sd(ICER)) / sqrt(n()), + ICER = mean(ICER)) + + p2 <- ggplot(ICER.final, aes(x = intervention, y = ICER, group = intervention, fill = intervention)) + + geom_col() + + geom_errorbar(aes(ymin = ICER - margin, ymax = ICER + margin, width = 0.4, group = intervention)) + + scale_y_continuous(label = comma) + + labs(title = 'ICER', subtitle = paste0(int.name, ' vs ', base.name)) + + xlab('Intervention') + print(p2) + + print(paste0('ICER == ', ICER.final$ICER, ' +- ', ICER.final$margin)) +} + From e245135f90b7a8785fab977cf22f549951319a39 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 7 Nov 2023 11:44:16 +0000 Subject: [PATCH 075/229] Removed the 1st and 100th percentiles from some outputs --- minos/testing/QALY_comparison_livingWage.Rmd | 210 +++++++----------- minos/transitions/Makefile | 2 +- .../estimate_longitudinal_transitions.R | 2 - minos/transitions/estimate_transitions.R | 2 - minos/utils_qaly.R | 19 +- 5 files changed, 97 insertions(+), 138 deletions(-) diff --git a/minos/testing/QALY_comparison_livingWage.Rmd b/minos/testing/QALY_comparison_livingWage.Rmd index f317f1a1..3b2a3913 100644 --- a/minos/testing/QALY_comparison_livingWage.Rmd +++ b/minos/testing/QALY_comparison_livingWage.Rmd @@ -58,6 +58,34 @@ wage <- read.csv(paste0(wage.path, 'qalys.csv')) combined <- rbind(base, wage) ``` +```{r} +diff <- combined %>% + pivot_longer(cols = -c(run_id, year, intervention, total_pop_size, alive_pop, dead_pop), + names_to = 'measure') %>% + group_by(run_id, year, measure) %>% + summarise(diff = value[intervention == 'livingWageIntervention'] - value[intervention == 'baseline']) %>% + pivot_wider(names_from = 'measure', + values_from = 'diff') + +wage.short <- wage %>% + select(run_id, year, total_pop_size, alive_pop, dead_pop) + +combined.diff <- merge(wage.short, diff, by=c('run_id', 'year')) + + +qalys_3sd <- 3 * sd(combined.diff$QALYs) +combined.culled <- filter(combined.diff, + QALYs < QALYs + qalys_3sd, + QALYs > QALYs - qalys_3sd) + + + # pivot_wider(id_cols = c(run_id, year), + # names_from = 'intervention', + # values_from = -c(run_id, year)) %>% + # mutate(across(ends_with('_livingWageIntervention'), .names = "{.col}_diff") - across(ends_with('_baseline'))) +``` + + # Intervention Information Just going to plot some things here like how many people affected by an intervention and what the mean / total boost amounts look like. @@ -175,36 +203,6 @@ ggplot(combined, aes(x = SF_12_PCS, group = intervention, colour = intervention, ## Area Under the Curve Comparison -```{r} -base.auc <- base %>% - group_by(run_id) %>% - summarise(AUC = auc(x=year, y = QALYs, type = 'spline')) %>% - mutate(intervention = 'baseline') - -wage.auc <- wage %>% - group_by(run_id) %>% - summarise(AUC = auc(x=year, y = QALYs, type = 'spline')) %>% - mutate(intervention = 'LivingWage') - -combined.auc <- rbind(base.auc, wage.auc) -``` - -```{r} -print(combined.auc) - -combined.auc.plot <- combined.auc %>% - group_by(intervention) %>% - summarise(sd = sd(AUC), - se = sd / sqrt(n()), - AUC = mean(AUC)) - -ggplot(data = combined.auc.plot, aes(x = intervention, y = AUC, group = intervention, fill = intervention)) + - geom_col() + - geom_errorbar(aes(ymin = AUC - se, ymax = AUC + se, width = 0.4, group = intervention)) + - labs(title = 'Total AUC comparison', subtitle = 'Baseline vs Living Wage Intervention') + - xlab('Intervention') -``` - ```{r} auc.plots(base = base, base.name = 'baseline', @@ -217,54 +215,34 @@ auc.plots(base = base, The [UK Government](https://www.gov.uk/government/publications/valuation-of-risks-to-life-and-health-monetary-value-of-a-life-year-voly/a-scoping-study-on-the-valuation-of-risks-to-life-and-health-the-monetary-value-of-a-life-year-voly) has set the value of 1 QALY to be equal to $60,000. This is the number we will use for our calculations. ```{r} -combined.cost <- combined %>% - #select(-alive_pop, -SF_12_MCS, -SF_12_PCS, -utility) %>% - select(run_id, year, alive_pop, total_boost, intervention, QALYs) %>% - mutate(QALY_value = QALYs * 60000) - -ggplot(combined.cost, aes(x = year, y = QALY_value, group = intervention, colour = intervention)) + - geom_smooth() + - scale_y_continuous(label = comma) - -combined.cost.change <- combined.cost %>% - select(run_id, year, intervention, QALY_value) %>% - pivot_wider(names_from = 'intervention', - values_from = 'QALY_value') %>% - mutate(change_baseline = baseline - baseline, - change_LivingWage = livingWageIntervention - baseline) %>% - select(-baseline, -livingWageIntervention) %>% - pivot_longer(cols = change_baseline:change_LivingWage, - names_to = 'intervention', - names_prefix = 'change_', - values_to = 'QALY_value_change') %>% - filter(intervention != 'baseline') - -ggplot(combined.cost.change, aes(x = year, y = QALY_value_change, group = intervention, colour = intervention, fill = intervention)) + - geom_smooth() + - scale_y_continuous(label = comma) +cost.per.qaly(base = base, + base.name = 'baseline', + int = wage, + int.name = 'livingWageIntervention', + QALY_value = 60000) # QALY value == £60,000 ``` ```{r} -total.combined.cost <- combined.cost.change %>% - group_by(run_id, intervention) %>% - summarise(TotalChange = sum(QALY_value_change)) %>% - group_by(intervention) %>% - summarise(se = sd(TotalChange) / sqrt(n()), - TotalChange = mean(TotalChange)) - -#print(paste0('Total combined cost == ', format(round(as.numeric(total.combined.cost), 1), nsmall=1, big.mark=","))) - -ggplot(total.combined.cost, aes(x = intervention, y = TotalChange, group = intervention, fill = intervention)) + - geom_col() + - geom_errorbar(aes(ymin = TotalChange - se, ymax = TotalChange + se, width = 0.4, group = intervention)) + - scale_y_continuous(label = comma) + - labs(title = 'Total Change in QALY Value', subtitle = 'Baseline vs Living Wage Intervention') + - xlab('Intervention') - -ggplot(combined.cost.change, aes(x = QALY_value_change, group = intervention, colour = intervention, fill = intervention)) + - geom_histogram() + - facet_wrap(~year) + - labs(title = 'QALY value change') +# total.combined.cost <- combined.cost.change %>% +# group_by(run_id, intervention) %>% +# summarise(TotalChange = sum(QALY_value_change)) %>% +# group_by(intervention) %>% +# summarise(se = sd(TotalChange) / sqrt(n()), +# TotalChange = mean(TotalChange)) +# +# #print(paste0('Total combined cost == ', format(round(as.numeric(total.combined.cost), 1), nsmall=1, big.mark=","))) +# +# ggplot(total.combined.cost, aes(x = intervention, y = TotalChange, group = intervention, fill = intervention)) + +# geom_col() + +# geom_errorbar(aes(ymin = TotalChange - se, ymax = TotalChange + se, width = 0.4, group = intervention)) + +# scale_y_continuous(label = comma) + +# labs(title = 'Total Change in QALY Value', subtitle = 'Baseline vs Living Wage Intervention') + +# xlab('Intervention') +# +# ggplot(combined.cost.change, aes(x = QALY_value_change, group = intervention, colour = intervention, fill = intervention)) + +# geom_histogram() + +# facet_wrap(~year) + +# labs(title = 'QALY value change') ``` # Incremental Cost-Effectiveness Ratio @@ -275,68 +253,36 @@ From the NICE guidelines, there is a general consensus that anything below £20, ```{r} -combined.cost.mean <- combined.cost %>% - group_by(run_id, year, intervention) %>% - summarise(across(everything(), mean)) - -# ICER = (C1 - C0) / (E1 - E0) -# where -# C1 = cost of intervention -# E1 = effect of intervention -# C0 = cost of control group -# E0 = effect of control group - -ICER <- combined.cost.mean %>% - select(run_id, year, intervention, total_boost, QALYs, QALY_value) %>% - pivot_wider(names_from = 'intervention', - values_from = c('total_boost', 'QALYs', 'QALY_value')) %>% - mutate(ICER = (total_boost_livingWageIntervention - total_boost_baseline) / (QALYs_livingWageIntervention - QALYs_baseline)) %>% - select(run_id, year, ICER) %>% - mutate(intervention = 'LivingWage') - -ggplot(ICER, aes(x = year, y = ICER)) + - geom_smooth() + - scale_y_continuous(label=comma) - -ICER.final <- ICER %>% - group_by(run_id, intervention) %>% - summarise(ICER = mean(ICER)) %>% - group_by(intervention) %>% - summarise(se = sd(ICER) / sqrt(n()), - ICER = mean(ICER)) - -ggplot(ICER.final, aes(x = intervention, y = ICER, group = intervention, fill = intervention)) + - geom_col() + - geom_errorbar(aes(ymin = ICER - se, ymax = ICER + se, width = 0.4, group = intervention)) + - scale_y_continuous(label = comma) + - labs(title = 'ICER', subtitle = 'Living Wage Intervention') + - xlab('Intervention') - +ICER(base = base, + base.name = 'baseline', + int = wage, + int.name = 'livingWageIntervention', + QALY_value = 60000) ``` ```{r} -ggplot(ICER, aes(x = ICER, group = intervention, colour = intervention, fill = intervention)) + - geom_histogram() + - facet_wrap(~year) + - labs(title = 'ICER by run', subtitle = 'All Years') - -ggplot(filter(ICER, year == 2022), aes(x = ICER, group = intervention, colour = intervention, fill = intervention)) + - geom_histogram() + - labs(title = 'ICER by run', subtitle = '2022') - -ggplot(filter(ICER, year == 2028), aes(x = ICER, group = intervention, colour = intervention, fill = intervention)) + - geom_histogram() + - labs(title = 'ICER by run', subtitle = '2028') - -ggplot(filter(ICER, year == 2031), aes(x = ICER, group = intervention, colour = intervention, fill = intervention)) + - geom_histogram() + - labs(title = 'ICER by run', subtitle = '2031') - -ggplot(filter(ICER, year == 2034), aes(x = ICER, group = intervention, colour = intervention, fill = intervention)) + - geom_histogram() + - labs(title = 'ICER by run', subtitle = '2034') +# ggplot(ICER, aes(x = ICER, group = intervention, colour = intervention, fill = intervention)) + +# geom_histogram() + +# facet_wrap(~year) + +# labs(title = 'ICER by run', subtitle = 'All Years') +# +# ggplot(filter(ICER, year == 2022), aes(x = ICER, group = intervention, colour = intervention, fill = intervention)) + +# geom_histogram() + +# labs(title = 'ICER by run', subtitle = '2022') +# +# ggplot(filter(ICER, year == 2028), aes(x = ICER, group = intervention, colour = intervention, fill = intervention)) + +# geom_histogram() + +# labs(title = 'ICER by run', subtitle = '2028') +# +# ggplot(filter(ICER, year == 2031), aes(x = ICER, group = intervention, colour = intervention, fill = intervention)) + +# geom_histogram() + +# labs(title = 'ICER by run', subtitle = '2031') +# +# ggplot(filter(ICER, year == 2034), aes(x = ICER, group = intervention, colour = intervention, fill = intervention)) + +# geom_histogram() + +# labs(title = 'ICER by run', subtitle = '2034') ``` diff --git a/minos/transitions/Makefile b/minos/transitions/Makefile index 6bbd9dd7..895f0ca1 100644 --- a/minos/transitions/Makefile +++ b/minos/transitions/Makefile @@ -23,7 +23,7 @@ cv_transitions: $(TRANSITION_DATA)/cross_validation/version5/ncigs/zip/ncigs_201 cv_S7_transitions: | $(TRANSITION_DATA) cv_S7_transitions: $(TRANSITION_DATA)/cross_validation/version5/S7_mental_health/clm/S7_mental_health_2019_2020.rds $(TRANSITION_DATA)/cross_validation/version5/hh_income/glmm/hh_income_new_GLMM.rds -$(TRANSITION_DATA)/ncigs/zip/ncigs_2019_2020.rds: $(FINALDATA)/2020_US_cohort.csv $(TRANSITION_SOURCE)/estimate_transitions.R $(TRANSITION_SOURCE)/model_definitions_default.txt #$(TRANSITION_SOURCE)/transition_model_functions.R +$(TRANSITION_DATA)/ncigs/zip/ncigs_2019_2020.rds: $(FINALDATA)/2020_US_cohort.csv $(TRANSITION_SOURCE)/estimate_transitions.R $(TRANSITION_SOURCE)/model_definitions_default.txt $(TRANSITION_SOURCE)/transition_model_functions.R $(RSCRIPT) $(SOURCEDIR)/transitions/estimate_transitions.R --default $(TRANSITION_DATA)/S7_mental_health/clm/S7_mental_health_2019_2020.rds: $(FINALDATA)/2020_US_cohort.csv $(TRANSITION_SOURCE)/estimate_transitions.R $(TRANSITION_SOURCE)/transition_model_functions.R $(TRANSITION_SOURCE)/model_definitions_S7.txt diff --git a/minos/transitions/estimate_longitudinal_transitions.R b/minos/transitions/estimate_longitudinal_transitions.R index 8b48052a..ae83db4e 100644 --- a/minos/transitions/estimate_longitudinal_transitions.R +++ b/minos/transitions/estimate_longitudinal_transitions.R @@ -284,8 +284,6 @@ cross_validation <- args$crossval default <- args$default sipher7 <- args$SIPHER7 -default <- TRUE - ## RUNTIME ARGS transSourceDir <- 'minos/transitions/' dataDir <- 'data/final_US/' diff --git a/minos/transitions/estimate_transitions.R b/minos/transitions/estimate_transitions.R index a008e642..561840e3 100644 --- a/minos/transitions/estimate_transitions.R +++ b/minos/transitions/estimate_transitions.R @@ -272,8 +272,6 @@ run_yearly_models <- function(transitionDir_path, depend = next.dependent) } else if(tolower(mod.type) == 'zip') { - - print(formula.string) model <- estimate_yearly_zip(data = merged, formula = form, diff --git a/minos/utils_qaly.R b/minos/utils_qaly.R index 6a23618c..527d175a 100644 --- a/minos/utils_qaly.R +++ b/minos/utils_qaly.R @@ -135,16 +135,33 @@ ICER <- function(base, base.name, int, int.name, QALY_value) { # C0 = cost of control group # E0 = effect of control group + # prepare var names + c1 <- paste('total_boost', int.name, sep='_') + c0 <- paste('total_boost', base.name, sep='_') + e1 <- paste('QALYs', int.name, sep='_') + e0 <- paste('QALYs', base.name, sep='_') + ICER <- combined.cost.mean %>% pivot_wider(names_from = 'intervention', values_from = c('total_boost', 'QALYs', 'QALY_value')) %>% - mutate(ICER = (total_boost_energyDownlift - total_boost_energyDownliftNoSupport) / (QALYs_energyDownlift - QALYs_energyDownliftNoSupport)) %>% + mutate(ICER = (.data[[c1]] - .data[[c0]]) / (.data[[e1]] - .data[[e0]])) %>% select(run_id, year, ICER) %>% pivot_longer(cols = -c(run_id, year), names_prefix = 'ICER_', names_to = 'intervention', values_to = 'ICER') + pc1 <- quantile(ICER$ICER, .01) + print(pc1) + print(min(ICER$ICER)) + pc99 <- quantile(ICER$ICER, .99) + print(pc99) + print(max(ICER$ICER)) + + ICER <- filter(ICER, ICER < pc99, ICER > pc1) + print(min(ICER$ICER)) + print(max(ICER$ICER)) + p1 <- ggplot(ICER, aes(x = year, y = ICER, group = intervention, fill = intervention, colour = intervention)) + geom_smooth() + scale_y_continuous(label = comma) + From 1c6ad41d037be79afa458d1eeaf07784770b9b27 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Wed, 8 Nov 2023 10:40:16 +0000 Subject: [PATCH 076/229] Fixing the problem introduced when merging the new ageing module into branch. Also readded some modules that were removed during merge --- config/default.yaml | 2 +- minos/modules/ageing.py | 4 +-- minos/modules/replenishment.py | 48 +++++++++++++++++----------------- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/config/default.yaml b/config/default.yaml index 7bfd9b7a..aa4d78c3 100755 --- a/config/default.yaml +++ b/config/default.yaml @@ -24,7 +24,7 @@ cross_validation: FALSE # Correct ordering of components is automated in RunPipeline, so can be passed in any order here # Correct ordering of components is automated in RunPipeline, so can be passed in any order here -components : [lmmYJMWB(), lmmYJPCS(), Loneliness(), Tobacco(), Alcohol(), PhysicalActivity(), Neighbourhood(), Heating(), Housing(), HousingTenure(), lmmYJNutrition(), financialSituation(), lmmYJIncome(), Education(), Ageing(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] +components : [lmmYJMWB(), lmmYJPCS(), Loneliness(), Tobacco(), Alcohol(), PhysicalActivity(), Neighbourhood(), MaterialDeprivation(), ChronicDisease(), Heating(), Housing(), HousingTenure(), lmmYJNutrition(), financialSituation(), lmmYJIncome(), Education(), Ageing(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] scale_rates: diff --git a/minos/modules/ageing.py b/minos/modules/ageing.py index 293885a8..439d7e82 100644 --- a/minos/modules/ageing.py +++ b/minos/modules/ageing.py @@ -43,8 +43,8 @@ def on_time_step(self, event): population = self.population_view.get(event.index, query="alive == 'alive'") population['age'] += event.step_size / pd.Timedelta(days=365.25) - # add one to current year - population['time'] += int(event.step_size / pd.Timedelta(days=365.25)) + # # add one to current year + # population['time'] += int(event.step_size / pd.Timedelta(days=365.25)) # update new population. logging.info(f"Aged population to year {event.time.year}") diff --git a/minos/modules/replenishment.py b/minos/modules/replenishment.py index faf059eb..64e5cfce 100755 --- a/minos/modules/replenishment.py +++ b/minos/modules/replenishment.py @@ -243,17 +243,17 @@ def on_time_step(self, event): # logging logging.info(f"\tTotal new 16 year olds added to the model: {cohort_size}") - # def update_time(self, event): - # """ Update time variable by the length of the simulation time step in days - # Parameters - # ---------- - # event : builder.event - # some time point at which to run the method. - # """ - # # get alive people and add time in years to their age. - # population = self.population_view.get(event.index, query="alive == 'alive'") - # population['time'] += event.step_size / pd.Timedelta(days=365.25) - # self.population_view.update(population) + def update_time(self, event): + """ Update time variable by the length of the simulation time step in days + Parameters + ---------- + event : builder.event + some time point at which to run the method. + """ + # get alive people and add time in years to their age. + population = self.population_view.get(event.index, query="alive == 'alive'") + population['time'] += event.step_size / pd.Timedelta(days=365.25) + self.population_view.update(population) class NoReplenishment(Base): @@ -441,19 +441,19 @@ def age_simulants(self, event): population['age'] += event.step_size / pd.Timedelta(days=365.25) self.population_view.update(population) - # def update_time(self, event): - # """ - # Update time variable by the length of the simulation time step in days - # - # Parameters - # ---------- - # event : builder.event - # some time point at which to run the method. - # """ - # # get alive people and add time in years to their age. - # population = self.population_view.get(event.index, query="alive == 'alive'") - # population['time'] += int(event.step_size / pd.Timedelta(days=365.25)) - # self.population_view.update(population) + def update_time(self, event): + """ + Update time variable by the length of the simulation time step in days + + Parameters + ---------- + event : builder.event + some time point at which to run the method. + """ + # get alive people and add time in years to their age. + population = self.population_view.get(event.index, query="alive == 'alive'") + population['time'] += int(event.step_size / pd.Timedelta(days=365.25)) + self.population_view.update(population) # Special methods for vivarium. @property From dcceee435120e75c6bc3c8c5d99126f396d0f7eb Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Wed, 8 Nov 2023 15:26:35 +0000 Subject: [PATCH 077/229] Added targets for calculating qalys on glasgow synthetic simulation runs --- minos/outcomes/Makefile | 29 +++++++++++++++++++ .../testing/QALY_comparison_energyCrises.Rmd | 3 +- .../QALY_comparison_energyCrises_diff.Rmd | 2 +- minos/testing/QALY_comparison_livingWage.Rmd | 2 +- scripts/Makefile | 8 ++--- 5 files changed, 37 insertions(+), 7 deletions(-) diff --git a/minos/outcomes/Makefile b/minos/outcomes/Makefile index ad123334..962839ef 100644 --- a/minos/outcomes/Makefile +++ b/minos/outcomes/Makefile @@ -9,6 +9,9 @@ OUTCOMES=$(ROOT)/minos/outcomes .phony: QALY QALY_baseline QALY_energyDownlift QALY_energyDownliftNoSupport +QALY: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + QALY_baseline: MODE=default_config QALY_baseline: INTERVENTION=baseline QALY_baseline: @@ -78,6 +81,32 @@ QALY_comparisons_norepl: QALY_baseline_norepl QALY_energyDownlift_norepl QALY_en $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" +QALY_baseline_glasgow: MODE=glasgow_scaled +QALY_baseline_glasgow: INTERVENTION=baseline +QALY_baseline_glasgow: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_energysupport_glasgow: MODE=glasgow_scaled +QALY_energysupport_glasgow: INTERVENTION=energyDownlift +QALY_energysupport_glasgow: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_energynosupport_glasgow: MODE=glasgow_scaled +QALY_energynosupport_glasgow: INTERVENTION=energyDownliftNoSupport +QALY_energynosupport_glasgow: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_livwage_glasgow: MODE=glasgow_scaled +QALY_livwage_glasgow: INTERVENTION=livingWageIntervention +QALY_livwage_glasgow: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALYs_glasgow: QALY_baseline_glasgow QALY_energysupport_glasgow QALY_energynosupport_glasgow QALY_livwage_glasgow + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises.Rmd')" + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises_diff.Rmd')" + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" + + ##################################### # Post-hoc aggregation of multiple MINOS runs on bash terminal. ##################################### diff --git a/minos/testing/QALY_comparison_energyCrises.Rmd b/minos/testing/QALY_comparison_energyCrises.Rmd index 58f5c22c..46c0c184 100644 --- a/minos/testing/QALY_comparison_energyCrises.Rmd +++ b/minos/testing/QALY_comparison_energyCrises.Rmd @@ -40,7 +40,8 @@ source(here::here('minos', 'utils_qaly.R')) ```{r} # # read in QALY files for baseline and interventions -out.path <- here::here('output', 'default_config') +#out.path <- here::here('output', 'default_config') +out.path <- here::here('output', 'glasgow_scaled') base.path <- get_latest_runtime_subdirectory(here::here(out.path, 'baseline')) support.path <- get_latest_runtime_subdirectory(here::here(out.path, 'energyDownlift')) diff --git a/minos/testing/QALY_comparison_energyCrises_diff.Rmd b/minos/testing/QALY_comparison_energyCrises_diff.Rmd index a5944efa..6a67135c 100644 --- a/minos/testing/QALY_comparison_energyCrises_diff.Rmd +++ b/minos/testing/QALY_comparison_energyCrises_diff.Rmd @@ -40,7 +40,7 @@ source(here::here('minos', 'utils_qaly.R')) ```{r} # # read in QALY files for baseline and interventions -out.path <- here::here('output', 'default_config') +out.path <- here::here('output', 'glasgow_scaled') base.path <- get_latest_runtime_subdirectory(here::here(out.path, 'baseline')) support.path <- get_latest_runtime_subdirectory(here::here(out.path, 'energyDownlift')) diff --git a/minos/testing/QALY_comparison_livingWage.Rmd b/minos/testing/QALY_comparison_livingWage.Rmd index 3b2a3913..9c4e2f93 100644 --- a/minos/testing/QALY_comparison_livingWage.Rmd +++ b/minos/testing/QALY_comparison_livingWage.Rmd @@ -41,7 +41,7 @@ source(here::here('minos', 'utils_qaly.R')) ```{r} # # read in QALY files for baseline and interventions #out.path <- here::here('output', 'default_config') -out.path <- here::here('output', 'default_config') +out.path <- here::here('output', 'glasgow_scaled') base.path <- get_latest_runtime_subdirectory(here::here(out.path, 'baseline')) wage.path <- get_latest_runtime_subdirectory(here::here(out.path, 'livingWageIntervention')) diff --git a/scripts/Makefile b/scripts/Makefile index 38f2aff8..0f593f13 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -148,10 +148,10 @@ arc4_all_scenarios_default: RUN_CONFIG=$(CONFIG)/default.yaml arc4_all_scenarios_default: arc4_all_scenarios ## All SF12 scenarios using glasgow scaled population -arc4_all_scenarios_default: setup_glasgow_scaled -arc4_all_scenarios_default: MODE=SF12_glasgow_scaled -arc4_all_scenarios_default: RUN_CONFIG=$(CONFIG)/glasgow_scaled.yaml -arc4_all_scenarios_default: arc4_all_scenarios +arc4_all_scenarios_default_glasgow: setup_glasgow_scaled +arc4_all_scenarios_default_glasgow: MODE=glasgow_scaled +arc4_all_scenarios_default_glasgow: RUN_CONFIG=$(CONFIG)/glasgow_scaled.yaml +arc4_all_scenarios_default_glasgow: arc4_all_scenarios ## All Scotland scenarios ## DEPRECATED - these have been superseded by the glasgow synthetic population scenarios From 84f863733a1a27aa2c6533bfa598d99d7b5bdc10 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Thu, 9 Nov 2023 11:05:28 +0000 Subject: [PATCH 078/229] Added diagnostic SF12 plots to QALY comparison scripts, and changed pointer to glasgow_scaled for visualising synthetic population runs --- minos/outcomes/Makefile | 2 + .../QALY_comparison_energyCrises_diff.Rmd | 28 ++--- minos/testing/QALY_comparison_livingWage.Rmd | 116 +++++++++--------- minos/utils_qaly.R | 69 +++++++++++ 4 files changed, 140 insertions(+), 75 deletions(-) diff --git a/minos/outcomes/Makefile b/minos/outcomes/Makefile index 962839ef..e795fe59 100644 --- a/minos/outcomes/Makefile +++ b/minos/outcomes/Makefile @@ -47,6 +47,7 @@ QALY_comparison_livingWage: QALY_baseline QALY_livingWage QALY_comparisons: QALY_baseline QALY_energyDownlift QALY_energyDownliftNoSupport QALY_livingWage $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises.Rmd')" $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises_diff.Rmd')" QALY_baseline_norepl: MODE=default_no_replenishment QALY_baseline_norepl: INTERVENTION=baseline @@ -79,6 +80,7 @@ QALY_comparison_livingWage_norepl: QALY_baseline_norepl QALY_livingWage_norepl QALY_comparisons_norepl: QALY_baseline_norepl QALY_energyDownlift_norepl QALY_energyDownliftNoSupport_norepl QALY_livingWage_norepl $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises.Rmd')" $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises_diff.Rmd')" QALY_baseline_glasgow: MODE=glasgow_scaled diff --git a/minos/testing/QALY_comparison_energyCrises_diff.Rmd b/minos/testing/QALY_comparison_energyCrises_diff.Rmd index 6a67135c..b3206854 100644 --- a/minos/testing/QALY_comparison_energyCrises_diff.Rmd +++ b/minos/testing/QALY_comparison_energyCrises_diff.Rmd @@ -101,25 +101,23 @@ ggplot(deaths, aes(x = year, y = death_incidence, group = intervention, colour = # QALY comparison ```{r} -ggplot(data = combined, aes(x = year, y = QALYs, group = intervention, colour = intervention)) + - geom_smooth() + - labs(title = 'QALYs per year') +QALY_comparison(base = noSupport, + base.name = 'energyDownliftNoSupport', + int = support, + int.name = 'energyDownlift') +``` -# Now change from baseline -combined.QALY.change <- combined %>% - select(run_id, year, intervention, QALYs) %>% - pivot_wider(names_from = 'intervention', - values_from = 'QALYs') %>% - mutate(QALYs = energyDownlift - energyDownliftNoSupport) %>% - select(run_id, year, QALYs) +## SF12 Change -ggplot(data = combined.QALY.change, aes(x = year, y = QALYs)) + - geom_hline(yintercept = 0, linetype = 'dashed') + - geom_smooth() + - labs(title = 'QALY change', subtitle = 'Support vs No Support') +```{r} +sf12.plots(base = noSupport, + base.name = 'energyDownliftNoSupport', + int = support, + int.name = 'energyDownlift') ``` -## Area Under the Curve Comparison + +## Area Under the Curve ```{r} auc.plots(base = noSupport, diff --git a/minos/testing/QALY_comparison_livingWage.Rmd b/minos/testing/QALY_comparison_livingWage.Rmd index 9c4e2f93..734e0ae5 100644 --- a/minos/testing/QALY_comparison_livingWage.Rmd +++ b/minos/testing/QALY_comparison_livingWage.Rmd @@ -121,84 +121,80 @@ ggplot(deaths, aes(x = year, y = death_incidence, group = intervention, colour = # QALY comparison ```{r} -ggplot(data = combined, aes(x = year, y = QALYs, group = intervention, colour = intervention)) + - geom_smooth() + - labs(title = 'QALYs per year') - -# Now change from baseline -combined.QALY.change <- combined %>% - select(run_id, year, intervention, QALYs) %>% - pivot_wider(names_from = 'intervention', - values_from = 'QALYs') %>% - mutate(QALYs = livingWageIntervention - baseline) %>% - select(run_id, year, QALYs) %>% - mutate(intervention = 'LivingWage') - -ggplot(data = combined.QALY.change, aes(x = year, y = QALYs, group = intervention, colour = intervention)) + - geom_hline(yintercept = 0, linetype = 'dashed') + - geom_smooth() + - labs(title = 'QALY change from Baseline') +QALY_comparison(base = base, + base.name = 'baseline', + int = wage, + int.name = 'livingWageIntervention') ``` Can check histogram over each run to see the range of QALYs. ```{r} -# histogram of qalys -ggplot(combined, aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + - geom_histogram() + - facet_wrap(~year) - -ggplot(filter(combined, year == 2022), aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + - geom_histogram() + - labs(title = 'QALYs by run', subtitle = '2022') - -ggplot(filter(combined, year == 2028), aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + - geom_histogram() + - labs(title = 'QALYs by run', subtitle = '2028') - -ggplot(filter(combined, year == 2034), aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + - geom_histogram() + - labs(title = 'QALYs by run', subtitle = '2034') +# # histogram of qalys +# ggplot(combined, aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + +# geom_histogram() + +# facet_wrap(~year) +# +# ggplot(filter(combined, year == 2022), aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + +# geom_histogram() + +# labs(title = 'QALYs by run', subtitle = '2022') +# +# ggplot(filter(combined, year == 2028), aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + +# geom_histogram() + +# labs(title = 'QALYs by run', subtitle = '2028') +# +# ggplot(filter(combined, year == 2034), aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + +# geom_histogram() + +# labs(title = 'QALYs by run', subtitle = '2034') ``` and histograms of change... ```{r} -# histogram of qalys -ggplot(combined.QALY.change, aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + - geom_histogram() + - facet_wrap(~year) - -ggplot(filter(combined.QALY.change, year == 2022), aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + - geom_histogram() + - labs(title = 'QALY change by run', subtitle = '2022') - -ggplot(filter(combined.QALY.change, year == 2028), aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + - geom_histogram() + - labs(title = 'QALY change by run', subtitle = '2028') - -ggplot(filter(combined.QALY.change, year == 2034), aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + - geom_histogram() + - labs(title = 'QALY change by run', subtitle = '2034') +# # histogram of qalys +# ggplot(combined.QALY.change, aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + +# geom_histogram() + +# facet_wrap(~year) +# +# ggplot(filter(combined.QALY.change, year == 2022), aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + +# geom_histogram() + +# labs(title = 'QALY change by run', subtitle = '2022') +# +# ggplot(filter(combined.QALY.change, year == 2028), aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + +# geom_histogram() + +# labs(title = 'QALY change by run', subtitle = '2028') +# +# ggplot(filter(combined.QALY.change, year == 2034), aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + +# geom_histogram() + +# labs(title = 'QALY change by run', subtitle = '2034') ``` ...and SF_12 MCS and PCS for good measure. ```{r} -ggplot(combined, aes(x = year, y = SF_12_MCS, group = intervention, colour = intervention)) + - geom_smooth() - -ggplot(combined, aes(x = SF_12_MCS, group = intervention, colour = intervention, fill = intervention)) + - geom_histogram() + - facet_wrap(~year) +# ggplot(combined, aes(x = year, y = SF_12_MCS, group = intervention, colour = intervention)) + +# geom_smooth() +# +# ggplot(combined, aes(x = SF_12_MCS, group = intervention, colour = intervention, fill = intervention)) + +# geom_histogram() + +# facet_wrap(~year) +# +# ggplot(combined, aes(x = year, y = SF_12_PCS, group = intervention, colour = intervention)) + +# geom_smooth() +# +# ggplot(combined, aes(x = SF_12_PCS, group = intervention, colour = intervention, fill = intervention)) + +# geom_histogram() + +# facet_wrap(~year) +``` -ggplot(combined, aes(x = year, y = SF_12_PCS, group = intervention, colour = intervention)) + - geom_smooth() +## SF12 Change -ggplot(combined, aes(x = SF_12_PCS, group = intervention, colour = intervention, fill = intervention)) + - geom_histogram() + - facet_wrap(~year) +```{r} +sf12.plots(base = noSupport, + base.name = 'energyDownliftNoSupport', + int = support, + int.name = 'energyDownlift') ``` ## Area Under the Curve Comparison diff --git a/minos/utils_qaly.R b/minos/utils_qaly.R index 527d175a..d791b080 100644 --- a/minos/utils_qaly.R +++ b/minos/utils_qaly.R @@ -1,6 +1,75 @@ ### QALY calculation plots and utility functions +## QALY comparisons +QALY_comparison <- function(base, base.name, int, int.name) { + + combined <- rbind(base, int) + + p1 <- ggplot(data = combined, aes(x = year, y = QALYs, group = intervention, colour = intervention)) + + geom_smooth() + + labs(title = 'QALYs per year') + print(p1) + + # Now change from baseline + combined.QALY.change <- combined %>% + select(run_id, year, intervention, QALYs) %>% + pivot_wider(names_from = 'intervention', + values_from = 'QALYs') %>% + mutate(QALYs = .data[[int.name]] - .data[[base.name]]) %>% + select(run_id, year, QALYs) + + p2 <- ggplot(data = combined.QALY.change, aes(x = year, y = QALYs)) + + geom_hline(yintercept = 0, linetype = 'dashed') + + geom_smooth() + + labs(title = 'QALY change', subtitle = paste0(int.name, ' vs ', base.name)) + print(p2) +} + + +## SF12 Comparison Plots +# Plot mean comparison and change from baseline for both MCS and PCS. +sf12.plots <- function(base, base.name, int, int.name) { + + combined <- rbind(base, int) + + ## now SF12 plots for comparison + p1 <- ggplot(combined, aes(x = year, y = SF_12_MCS, group = intervention, colour = intervention, fill = intervention)) + + geom_smooth() + p2 <- ggplot(combined, aes(x = year, y = SF_12_PCS, group = intervention, colour = intervention, fill = intervention)) + + geom_smooth() + + # print(p1) + # print(p2) + + combined.SF12 <- combined %>% + select(run_id, year, intervention, SF_12_MCS, SF_12_PCS) %>% + pivot_wider(names_from = 'intervention', + values_from = c('SF_12_MCS', 'SF_12_PCS')) %>% + mutate(MCS_diff = .data[[paste0('SF_12_MCS_', int.name)]] - .data[[paste0('SF_12_MCS_', base.name)]], + PCS_diff = .data[[paste0('SF_12_PCS_', int.name)]] - .data[[paste0('SF_12_PCS_', base.name)]]) %>% + select(run_id, year, MCS_diff, PCS_diff) + + p3 <- ggplot(combined.SF12, aes(x = year, y = MCS_diff)) + + geom_smooth() + + geom_hline(yintercept = 0, linetype = 'dashed') + + labs(title = 'Change in SF_12_MCS', subtitle = paste0(int.name, ' vs ', base.name)) + + xlab('Year') + + ylab('Change in MCS') + + p4 <- ggplot(combined.SF12, aes(x = year, y = PCS_diff)) + + geom_smooth() + + geom_hline(yintercept = 0, linetype = 'dashed') + + labs(title = 'Change in SF_12_PCS', subtitle = paste0(int.name, ' vs ', base.name)) + + xlab('Year') + + ylab('Change in MCS') + + print(p3) + print(p4) +} + + + # AUC Plots: # This function will plot the total AUC (which equals the total QALYs in the # system) for 2 interventions, and the change in AUC between the 2. From 326c98c64d4861cfba42542d401e5ee2be4c5e5c Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Thu, 9 Nov 2023 11:36:29 +0000 Subject: [PATCH 079/229] Fixed mistake in one of the qaly scripts --- minos/testing/QALY_comparison_livingWage.Rmd | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/minos/testing/QALY_comparison_livingWage.Rmd b/minos/testing/QALY_comparison_livingWage.Rmd index 734e0ae5..d154f2a3 100644 --- a/minos/testing/QALY_comparison_livingWage.Rmd +++ b/minos/testing/QALY_comparison_livingWage.Rmd @@ -191,10 +191,10 @@ and histograms of change... ## SF12 Change ```{r} -sf12.plots(base = noSupport, - base.name = 'energyDownliftNoSupport', - int = support, - int.name = 'energyDownlift') +sf12.plots(base = base, + base.name = 'baseline', + int = wage, + int.name = 'livingWageIntervention') ``` ## Area Under the Curve Comparison From 4012a1987686fe6218288017f8615545a5cbaad2 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Thu, 9 Nov 2023 13:23:12 +0000 Subject: [PATCH 080/229] Updated the configs with some new packages --- .../cross_validation_default1.yaml | 2 +- .../cross_validation_default2.yaml | 2 +- .../cross_validation_default3.yaml | 2 +- .../cross_validation_default4.yaml | 2 +- .../cross_validation_default5.yaml | 2 +- config/default_noReplenishment.yaml | 2 +- config/glasgow_scaled.yaml | 4 +-- .../testing/QALY_comparison_energyCrises.Rmd | 28 +++++++-------- .../QALY_comparison_energyCrises_diff.Rmd | 27 ++++++++------- minos/testing/QALY_comparison_livingWage.Rmd | 34 +++++++++++-------- minos/utils_qaly.R | 4 +-- 11 files changed, 56 insertions(+), 53 deletions(-) diff --git a/config/cross_validation/cross_validation_default1.yaml b/config/cross_validation/cross_validation_default1.yaml index 39be066e..cf57d979 100644 --- a/config/cross_validation/cross_validation_default1.yaml +++ b/config/cross_validation/cross_validation_default1.yaml @@ -23,7 +23,7 @@ replenishing_dir: 'data/replenishing/cross_validation' cross_validation: TRUE # Correct ordering of components is automated in RunPipeline, so can be passed in any order here -components : [lmmYJMWB(), lmmYJPCS(), Loneliness(), Tobacco(), Alcohol(), PhysicalActivity(), Neighbourhood(), Heating(), Housing(), HousingTenure(), lmmYJNutrition(), financialSituation(), lmmYJIncome(), Education(), Ageing(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] +components : [lmmYJMWB(), lmmYJPCS(), Loneliness(), Tobacco(), Alcohol(), PhysicalActivity(), Neighbourhood(), MaterialDeprivation(), ChronicDisease(), Heating(), Housing(), HousingTenure(), lmmYJNutrition(), financialSituation(), lmmYJIncome(), Education(), Ageing(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] scale_rates: method: "constant" diff --git a/config/cross_validation/cross_validation_default2.yaml b/config/cross_validation/cross_validation_default2.yaml index 8ef048da..cf4f3b35 100644 --- a/config/cross_validation/cross_validation_default2.yaml +++ b/config/cross_validation/cross_validation_default2.yaml @@ -23,7 +23,7 @@ replenishing_dir: 'data/replenishing/cross_validation' cross_validation: TRUE # Correct ordering of components is automated in RunPipeline, so can be passed in any order here -components : [lmmYJMWB(), lmmYJPCS(), Loneliness(), Tobacco(), Alcohol(), PhysicalActivity(), Neighbourhood(), Heating(), Housing(), HousingTenure(), lmmYJNutrition(), financialSituation(), lmmYJIncome(), Education(), Ageing(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] +components : [lmmYJMWB(), lmmYJPCS(), Loneliness(), Tobacco(), Alcohol(), PhysicalActivity(), Neighbourhood(), MaterialDeprivation(), ChronicDisease(), Heating(), Housing(), HousingTenure(), lmmYJNutrition(), financialSituation(), lmmYJIncome(), Education(), Ageing(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] scale_rates: method: "constant" diff --git a/config/cross_validation/cross_validation_default3.yaml b/config/cross_validation/cross_validation_default3.yaml index 3aa9fe08..e901c893 100644 --- a/config/cross_validation/cross_validation_default3.yaml +++ b/config/cross_validation/cross_validation_default3.yaml @@ -23,7 +23,7 @@ replenishing_dir: 'data/replenishing/cross_validation' cross_validation: TRUE # Correct ordering of components is automated in RunPipeline, so can be passed in any order here -components : [lmmYJMWB(), lmmYJPCS(), Loneliness(), Tobacco(), Alcohol(), PhysicalActivity(), Neighbourhood(), Heating(), Housing(), HousingTenure(), lmmYJNutrition(), financialSituation(), lmmYJIncome(), Education(), Ageing(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] +components : [lmmYJMWB(), lmmYJPCS(), Loneliness(), Tobacco(), Alcohol(), PhysicalActivity(), Neighbourhood(), MaterialDeprivation(), ChronicDisease(), Heating(), Housing(), HousingTenure(), lmmYJNutrition(), financialSituation(), lmmYJIncome(), Education(), Ageing(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] scale_rates: method: "constant" diff --git a/config/cross_validation/cross_validation_default4.yaml b/config/cross_validation/cross_validation_default4.yaml index ee4bd159..209e2ac0 100644 --- a/config/cross_validation/cross_validation_default4.yaml +++ b/config/cross_validation/cross_validation_default4.yaml @@ -24,7 +24,7 @@ cross_validation: TRUE # Correct ordering of components is automated in RunPipeline, so can be passed in any order here # Correct ordering of components is automated in RunPipeline, so can be passed in any order here -components : [lmmYJMWB(), lmmYJPCS(), Loneliness(), Tobacco(), Alcohol(), PhysicalActivity(), Neighbourhood(), Heating(), Housing(), HousingTenure(), lmmYJNutrition(), financialSituation(), lmmYJIncome(), Education(), Ageing(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] +components : [lmmYJMWB(), lmmYJPCS(), Loneliness(), Tobacco(), Alcohol(), PhysicalActivity(), Neighbourhood(), MaterialDeprivation(), ChronicDisease(), Heating(), Housing(), HousingTenure(), lmmYJNutrition(), financialSituation(), lmmYJIncome(), Education(), Ageing(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] scale_rates: method: "constant" diff --git a/config/cross_validation/cross_validation_default5.yaml b/config/cross_validation/cross_validation_default5.yaml index 842c8d3b..c83f36ca 100644 --- a/config/cross_validation/cross_validation_default5.yaml +++ b/config/cross_validation/cross_validation_default5.yaml @@ -24,7 +24,7 @@ cross_validation: TRUE # Correct ordering of components is automated in RunPipeline, so can be passed in any order here # Correct ordering of components is automated in RunPipeline, so can be passed in any order here -components : [lmmYJMWB(), lmmYJPCS(), Loneliness(), Tobacco(), Alcohol(), PhysicalActivity(), Neighbourhood(), Heating(), Housing(), HousingTenure(), lmmYJNutrition(), financialSituation(), lmmYJIncome(), Education(), Ageing(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] +components : [lmmYJMWB(), lmmYJPCS(), Loneliness(), Tobacco(), Alcohol(), PhysicalActivity(), Neighbourhood(), MaterialDeprivation(), ChronicDisease(), Heating(), Housing(), HousingTenure(), lmmYJNutrition(), financialSituation(), lmmYJIncome(), Education(), Ageing(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] scale_rates: method: "constant" diff --git a/config/default_noReplenishment.yaml b/config/default_noReplenishment.yaml index 7bfd9b7a..9d1a00f4 100644 --- a/config/default_noReplenishment.yaml +++ b/config/default_noReplenishment.yaml @@ -24,7 +24,7 @@ cross_validation: FALSE # Correct ordering of components is automated in RunPipeline, so can be passed in any order here # Correct ordering of components is automated in RunPipeline, so can be passed in any order here -components : [lmmYJMWB(), lmmYJPCS(), Loneliness(), Tobacco(), Alcohol(), PhysicalActivity(), Neighbourhood(), Heating(), Housing(), HousingTenure(), lmmYJNutrition(), financialSituation(), lmmYJIncome(), Education(), Ageing(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] +components : [lmmYJMWB(), lmmYJPCS(), Loneliness(), Tobacco(), Alcohol(), PhysicalActivity(), Neighbourhood(), MaterialDeprivation(), ChronicDisease(), Heating(), Housing(), HousingTenure(), lmmYJNutrition(), financialSituation(), lmmYJIncome(), Education(), Ageing(), Mortality(), nkidsFertilityAgeSpecificRates(), NoReplenishment()] scale_rates: diff --git a/config/glasgow_scaled.yaml b/config/glasgow_scaled.yaml index 3eef98c4..5d3599a3 100644 --- a/config/glasgow_scaled.yaml +++ b/config/glasgow_scaled.yaml @@ -29,9 +29,7 @@ cross_validation: FALSE # Finally everything else can go in any order (priority 2). #components : [MWB(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Income(), Housing(), Labour(), Nutrition(), Education(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] #components : [MWB(), Loneliness(), Tobacco(), Neighbourhood(), Housing(), Nutrition(), Income(), Education(), Mortality(), Replenishment()] -components : [lmmYJMWB(), Loneliness(), Tobacco(), Neighbourhood(), Housing(), lmmYJNutrition(), lmmYJIncome(), Education(), - nkidsFertilityAgeSpecificRates(), Ageing(), Mortality(), - Replenishment()] +components : [lmmYJMWB(), lmmYJPCS(), Loneliness(), Tobacco(), Alcohol(), PhysicalActivity(), Neighbourhood(), MaterialDeprivation(), ChronicDisease(), Heating(), Housing(), HousingTenure(), lmmYJNutrition(), financialSituation(), lmmYJIncome(), Education(), Ageing(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] scale_rates: method: "constant" constant: diff --git a/minos/testing/QALY_comparison_energyCrises.Rmd b/minos/testing/QALY_comparison_energyCrises.Rmd index 46c0c184..40d655f6 100644 --- a/minos/testing/QALY_comparison_energyCrises.Rmd +++ b/minos/testing/QALY_comparison_energyCrises.Rmd @@ -40,22 +40,22 @@ source(here::here('minos', 'utils_qaly.R')) ```{r} # # read in QALY files for baseline and interventions -#out.path <- here::here('output', 'default_config') -out.path <- here::here('output', 'glasgow_scaled') - -base.path <- get_latest_runtime_subdirectory(here::here(out.path, 'baseline')) -support.path <- get_latest_runtime_subdirectory(here::here(out.path, 'energyDownlift')) -noSupport.path <- get_latest_runtime_subdirectory(here::here(out.path, 'energyDownliftNoSupport')) +# out.path <- here::here('output', 'default_config') +# #out.path <- here::here('output', 'glasgow_scaled') +# +# base.path <- get_latest_runtime_subdirectory(here::here(out.path, 'baseline')) +# support.path <- get_latest_runtime_subdirectory(here::here(out.path, 'energyDownlift')) +# noSupport.path <- get_latest_runtime_subdirectory(here::here(out.path, 'energyDownliftNoSupport')) +# +# base <- read.csv(paste0(base.path, 'qalys.csv')) +# support <- read.csv(paste0(support.path, 'qalys.csv')) +# noSupport <- read.csv(paste0(noSupport.path, 'qalys.csv')) -base <- read.csv(paste0(base.path, 'qalys.csv')) -support <- read.csv(paste0(support.path, 'qalys.csv')) -noSupport <- read.csv(paste0(noSupport.path, 'qalys.csv')) +out.path <- '/home/luke/Documents/WORK/MINOS/PLOTS_FROM_ARC/QALY_TESTING/' -# out.path <- '/home/luke/Documents/WORK/MINOS/PLOTS_FROM_ARC/QALY_TESTING/' -# -# base <- read.csv(paste0(out.path, 'qaly_baseline.csv')) -# support <- read.csv(paste0(out.path, 'qalys_support.csv')) -# noSupport <- read.csv(paste0(out.path, 'qalys_noSupport.csv')) +base <- read.csv(paste0(out.path, 'qalys_baseline.csv')) +support <- read.csv(paste0(out.path, 'qalys_support.csv')) +noSupport <- read.csv(paste0(out.path, 'qalys_nosupport.csv')) # combine the dataframes combined <- rbind(base, support, noSupport) diff --git a/minos/testing/QALY_comparison_energyCrises_diff.Rmd b/minos/testing/QALY_comparison_energyCrises_diff.Rmd index b3206854..6e4ac338 100644 --- a/minos/testing/QALY_comparison_energyCrises_diff.Rmd +++ b/minos/testing/QALY_comparison_energyCrises_diff.Rmd @@ -40,21 +40,22 @@ source(here::here('minos', 'utils_qaly.R')) ```{r} # # read in QALY files for baseline and interventions -out.path <- here::here('output', 'glasgow_scaled') - -base.path <- get_latest_runtime_subdirectory(here::here(out.path, 'baseline')) -support.path <- get_latest_runtime_subdirectory(here::here(out.path, 'energyDownlift')) -noSupport.path <- get_latest_runtime_subdirectory(here::here(out.path, 'energyDownliftNoSupport')) +# #out.path <- here::here('output', 'glasgow_scaled') +# out.path <- here::here('output', 'default_config') +# +# base.path <- get_latest_runtime_subdirectory(here::here(out.path, 'baseline')) +# support.path <- get_latest_runtime_subdirectory(here::here(out.path, 'energyDownlift')) +# noSupport.path <- get_latest_runtime_subdirectory(here::here(out.path, 'energyDownliftNoSupport')) +# +# base <- read.csv(paste0(base.path, 'qalys.csv')) +# support <- read.csv(paste0(support.path, 'qalys.csv')) +# noSupport <- read.csv(paste0(noSupport.path, 'qalys.csv')) -base <- read.csv(paste0(base.path, 'qalys.csv')) -support <- read.csv(paste0(support.path, 'qalys.csv')) -noSupport <- read.csv(paste0(noSupport.path, 'qalys.csv')) +out.path <- '/home/luke/Documents/WORK/MINOS/PLOTS_FROM_ARC/QALY_TESTING/' -# out.path <- '/home/luke/Documents/WORK/MINOS/PLOTS_FROM_ARC/QALY_TESTING/' -# -# base <- read.csv(paste0(out.path, 'qaly_baseline.csv')) -# support <- read.csv(paste0(out.path, 'qalys_support.csv')) -# noSupport <- read.csv(paste0(out.path, 'qalys_noSupport.csv')) +base <- read.csv(paste0(out.path, 'qalys_baseline.csv')) +support <- read.csv(paste0(out.path, 'qalys_support.csv')) +noSupport <- read.csv(paste0(out.path, 'qalys_nosupport.csv')) # combine the dataframes combined <- rbind(support, noSupport) diff --git a/minos/testing/QALY_comparison_livingWage.Rmd b/minos/testing/QALY_comparison_livingWage.Rmd index d154f2a3..8f853b65 100644 --- a/minos/testing/QALY_comparison_livingWage.Rmd +++ b/minos/testing/QALY_comparison_livingWage.Rmd @@ -40,20 +40,20 @@ source(here::here('minos', 'utils_qaly.R')) ```{r} # # read in QALY files for baseline and interventions -#out.path <- here::here('output', 'default_config') -out.path <- here::here('output', 'glasgow_scaled') +# out.path <- here::here('output', 'default_config') +# #out.path <- here::here('output', 'glasgow_scaled') +# +# base.path <- get_latest_runtime_subdirectory(here::here(out.path, 'baseline')) +# wage.path <- get_latest_runtime_subdirectory(here::here(out.path, 'livingWageIntervention')) +# +# base <- read.csv(paste0(base.path, 'qalys.csv')) +# wage <- read.csv(paste0(wage.path, 'qalys.csv')) -base.path <- get_latest_runtime_subdirectory(here::here(out.path, 'baseline')) -wage.path <- get_latest_runtime_subdirectory(here::here(out.path, 'livingWageIntervention')) +out.path <- '/home/luke/Documents/WORK/MINOS/PLOTS_FROM_ARC/QALY_TESTING/' -base <- read.csv(paste0(base.path, 'qalys.csv')) -wage <- read.csv(paste0(wage.path, 'qalys.csv')) +base <- read.csv(paste0(out.path, 'qalys_baseline.csv')) +wage <- read.csv(paste0(out.path, 'qalys_livwage.csv')) -# out.path <- '/home/luke/Documents/WORK/MINOS/PLOTS_FROM_ARC/QALY_TESTING/' -# -# base <- read.csv(paste0(out.path, 'qaly_baseline.csv')) -# support <- read.csv(paste0(out.path, 'qalys_support.csv')) -# noSupport <- read.csv(paste0(out.path, 'qalys_noSupport.csv')) # combine the dataframes combined <- rbind(base, wage) ``` @@ -91,7 +91,11 @@ combined.culled <- filter(combined.diff, Just going to plot some things here like how many people affected by an intervention and what the mean / total boost amounts look like. ```{r} -ggplot(filter(combined, intervention != 'baseline'), aes(x = year, y = pop_boosted, group = intervention, colour = intervention)) + + +test <- combined %>% + filter(intervention != 'baseline') + +ggplot(test, aes(x = year, y = pop_boosted, group = intervention, colour = intervention)) + geom_smooth() + labs(title = 'Number Individuals Receiving Intervention') @@ -99,9 +103,9 @@ ggplot(filter(combined, intervention != 'baseline'), aes(x = year, y = total_boo geom_smooth() + labs(title = 'Total Boost amount by year') -ggplot(combined, aes(x = year, y = alive_ratio, group = intervention, colour = intervention)) + - geom_smooth() + - labs(title = 'Ratio of living to dead in output dataframe') +# ggplot(combined, aes(x = year, y = alive_ratio, group = intervention, colour = intervention)) + +# geom_smooth() + +# labs(title = 'Ratio of living to dead in output dataframe') ## incidence of mortality deaths <- combined %>% diff --git a/minos/utils_qaly.R b/minos/utils_qaly.R index d791b080..37ead5ea 100644 --- a/minos/utils_qaly.R +++ b/minos/utils_qaly.R @@ -39,8 +39,8 @@ sf12.plots <- function(base, base.name, int, int.name) { p2 <- ggplot(combined, aes(x = year, y = SF_12_PCS, group = intervention, colour = intervention, fill = intervention)) + geom_smooth() - # print(p1) - # print(p2) + print(p1) + print(p2) combined.SF12 <- combined %>% select(run_id, year, intervention, SF_12_MCS, SF_12_PCS) %>% From 35a1567b5f79293f7ecc65cb3524a9df53b16d5a Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Thu, 9 Nov 2023 13:25:22 +0000 Subject: [PATCH 081/229] Added arc4 combined target for running qaly simulations with synthetic glasgow pop --- scripts/Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/Makefile b/scripts/Makefile index 0f593f13..bcaf3114 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -179,6 +179,10 @@ arc4_qaly_scenarios: MODE=default_config arc4_qaly_scenarios: RUN_CONFIG=$(CONFIG)/default.yaml arc4_qaly_scenarios: arc4_baseline arc4_intervention_energyDownLift arc4_intervention_energyDownLiftNoSupport arc4_intervention_livingWage +arc4_qaly_scenarios: MODE=glasgow_scaled +arc4_qaly_scenarios: RUN_CONFIG=$(CONFIG)/glasgow_scaled.yaml +arc4_qaly_scenarios: arc4_baseline arc4_intervention_energyDownLift arc4_intervention_energyDownLiftNoSupport arc4_intervention_livingWage + arc4_qaly_scenarios_no_replenishment: MODE=default_no_replenishment arc4_qaly_scenarios_no_replenishment: RUN_CONFIG=$(CONFIG)/default_noReplenishment.yaml arc4_qaly_scenarios_no_replenishment: arc4_baseline arc4_intervention_energyDownLift arc4_intervention_energyDownLiftNoSupport arc4_intervention_livingWage From a6ef1371644765f98956d0a5c5b5216d1cc81267 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Thu, 9 Nov 2023 13:25:56 +0000 Subject: [PATCH 082/229] Fixed typo... --- scripts/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/Makefile b/scripts/Makefile index bcaf3114..ea924a95 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -179,9 +179,9 @@ arc4_qaly_scenarios: MODE=default_config arc4_qaly_scenarios: RUN_CONFIG=$(CONFIG)/default.yaml arc4_qaly_scenarios: arc4_baseline arc4_intervention_energyDownLift arc4_intervention_energyDownLiftNoSupport arc4_intervention_livingWage -arc4_qaly_scenarios: MODE=glasgow_scaled -arc4_qaly_scenarios: RUN_CONFIG=$(CONFIG)/glasgow_scaled.yaml -arc4_qaly_scenarios: arc4_baseline arc4_intervention_energyDownLift arc4_intervention_energyDownLiftNoSupport arc4_intervention_livingWage +arc4_qaly_scenarios_glasgow: MODE=glasgow_scaled +arc4_qaly_scenarios_glasgow: RUN_CONFIG=$(CONFIG)/glasgow_scaled.yaml +arc4_qaly_scenarios_glasgow: arc4_baseline arc4_intervention_energyDownLift arc4_intervention_energyDownLiftNoSupport arc4_intervention_livingWage arc4_qaly_scenarios_no_replenishment: MODE=default_no_replenishment arc4_qaly_scenarios_no_replenishment: RUN_CONFIG=$(CONFIG)/default_noReplenishment.yaml From 2e3043c6a4a28a0c244c8642fcee792eb92b1f72 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Thu, 9 Nov 2023 15:09:31 +0000 Subject: [PATCH 083/229] Cast type of chron disease variable to int --- minos/data_generation/generate_repl_pop.py | 1 + minos/minosPipeline/RunPipeline.py | 1 + 2 files changed, 2 insertions(+) diff --git a/minos/data_generation/generate_repl_pop.py b/minos/data_generation/generate_repl_pop.py index fde1852f..384b5ef3 100755 --- a/minos/data_generation/generate_repl_pop.py +++ b/minos/data_generation/generate_repl_pop.py @@ -222,6 +222,7 @@ def generate_replenishing(projections, scotland_mode, cross_validation, inflated final_repl['S7_physical_health'] = final_repl['S7_physical_health'].astype(int) final_repl['nutrition_quality_diff'] = final_repl['nutrition_quality_diff'].astype(int) final_repl['neighbourhood_safety'] = final_repl['neighbourhood_safety'].astype(int) + final_repl['chron_disease'] = final_repl['chron_disease'].astype(int) US_utils.check_output_dir(output_dir) final_repl.to_csv(f'{output_dir}/replenishing_pop_2019-2070.csv', index=False) diff --git a/minos/minosPipeline/RunPipeline.py b/minos/minosPipeline/RunPipeline.py index fc142f70..37d0daf3 100755 --- a/minos/minosPipeline/RunPipeline.py +++ b/minos/minosPipeline/RunPipeline.py @@ -234,6 +234,7 @@ def type_check(data): data['S7_physical_health'] = data['S7_physical_health'].astype(int) data['nutrition_quality_diff'] = data['nutrition_quality_diff'].astype(int) data['neighbourhood_safety'] = data['neighbourhood_safety'].astype(int) + data['chron_disease'] = data['chron_disease'].astype(int) return data From c805741e54d0fd19a9bf6aa79e5086756ac71f33 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Thu, 9 Nov 2023 15:19:27 +0000 Subject: [PATCH 084/229] Trying to fix the type problem with chron_disease on arc --- minos/data_generation/generate_repl_pop.py | 2 +- minos/minosPipeline/RunPipeline.py | 2 +- minos/modules/chron_disease.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/minos/data_generation/generate_repl_pop.py b/minos/data_generation/generate_repl_pop.py index 384b5ef3..f39d4f22 100755 --- a/minos/data_generation/generate_repl_pop.py +++ b/minos/data_generation/generate_repl_pop.py @@ -222,7 +222,7 @@ def generate_replenishing(projections, scotland_mode, cross_validation, inflated final_repl['S7_physical_health'] = final_repl['S7_physical_health'].astype(int) final_repl['nutrition_quality_diff'] = final_repl['nutrition_quality_diff'].astype(int) final_repl['neighbourhood_safety'] = final_repl['neighbourhood_safety'].astype(int) - final_repl['chron_disease'] = final_repl['chron_disease'].astype(int) + final_repl['chron_disease'] = final_repl['chron_disease'].astype(float) US_utils.check_output_dir(output_dir) final_repl.to_csv(f'{output_dir}/replenishing_pop_2019-2070.csv', index=False) diff --git a/minos/minosPipeline/RunPipeline.py b/minos/minosPipeline/RunPipeline.py index 37d0daf3..da3cac4f 100755 --- a/minos/minosPipeline/RunPipeline.py +++ b/minos/minosPipeline/RunPipeline.py @@ -234,7 +234,7 @@ def type_check(data): data['S7_physical_health'] = data['S7_physical_health'].astype(int) data['nutrition_quality_diff'] = data['nutrition_quality_diff'].astype(int) data['neighbourhood_safety'] = data['neighbourhood_safety'].astype(int) - data['chron_disease'] = data['chron_disease'].astype(int) + data['chron_disease'] = data['chron_disease'].astype(float) return data diff --git a/minos/modules/chron_disease.py b/minos/modules/chron_disease.py index 2ed071ac..dfa82dff 100644 --- a/minos/modules/chron_disease.py +++ b/minos/modules/chron_disease.py @@ -102,7 +102,7 @@ def on_time_step(self, event): # cd_prob_df['chron_disease'][cd_prob_df['previous_chron_disease'] > cd_prob_df['chron_disease']] = \ # cd_prob_df['previous_chron_disease'] - self.population_view.update(cd_prob_df["chron_disease"]) + self.population_view.update(cd_prob_df["chron_disease"].astype(float)) def calculate_chron_disease(self, pop): """Calculate chron_disease transition distribution based on provided people/indices From c80dbcbd63024940b594b9641ad11c42ef4ab52c Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Thu, 9 Nov 2023 15:27:42 +0000 Subject: [PATCH 085/229] Now fixing type issue for matdep --- minos/data_generation/generate_repl_pop.py | 1 + minos/minosPipeline/RunPipeline.py | 1 + 2 files changed, 2 insertions(+) diff --git a/minos/data_generation/generate_repl_pop.py b/minos/data_generation/generate_repl_pop.py index f39d4f22..49142611 100755 --- a/minos/data_generation/generate_repl_pop.py +++ b/minos/data_generation/generate_repl_pop.py @@ -223,6 +223,7 @@ def generate_replenishing(projections, scotland_mode, cross_validation, inflated final_repl['nutrition_quality_diff'] = final_repl['nutrition_quality_diff'].astype(int) final_repl['neighbourhood_safety'] = final_repl['neighbourhood_safety'].astype(int) final_repl['chron_disease'] = final_repl['chron_disease'].astype(float) + final_repl['matdep'] = final_repl['matdep'].astype(int) US_utils.check_output_dir(output_dir) final_repl.to_csv(f'{output_dir}/replenishing_pop_2019-2070.csv', index=False) diff --git a/minos/minosPipeline/RunPipeline.py b/minos/minosPipeline/RunPipeline.py index da3cac4f..31a02432 100755 --- a/minos/minosPipeline/RunPipeline.py +++ b/minos/minosPipeline/RunPipeline.py @@ -235,6 +235,7 @@ def type_check(data): data['nutrition_quality_diff'] = data['nutrition_quality_diff'].astype(int) data['neighbourhood_safety'] = data['neighbourhood_safety'].astype(int) data['chron_disease'] = data['chron_disease'].astype(float) + data['matdep'] = data['matdep'].astype(int) return data From 4ed97f322fb3b5def61ce81d0225648d1dc9bd8d Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Thu, 9 Nov 2023 15:37:36 +0000 Subject: [PATCH 086/229] Different attempt at fixing type problem with matdep --- minos/data_generation/generate_repl_pop.py | 2 +- minos/minosPipeline/RunPipeline.py | 2 +- minos/modules/material_deprivation.py | 11 +---------- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/minos/data_generation/generate_repl_pop.py b/minos/data_generation/generate_repl_pop.py index 49142611..edd7f0da 100755 --- a/minos/data_generation/generate_repl_pop.py +++ b/minos/data_generation/generate_repl_pop.py @@ -223,7 +223,7 @@ def generate_replenishing(projections, scotland_mode, cross_validation, inflated final_repl['nutrition_quality_diff'] = final_repl['nutrition_quality_diff'].astype(int) final_repl['neighbourhood_safety'] = final_repl['neighbourhood_safety'].astype(int) final_repl['chron_disease'] = final_repl['chron_disease'].astype(float) - final_repl['matdep'] = final_repl['matdep'].astype(int) + final_repl['matdep'] = final_repl['matdep'].astype(float) US_utils.check_output_dir(output_dir) final_repl.to_csv(f'{output_dir}/replenishing_pop_2019-2070.csv', index=False) diff --git a/minos/minosPipeline/RunPipeline.py b/minos/minosPipeline/RunPipeline.py index 31a02432..aa8c7603 100755 --- a/minos/minosPipeline/RunPipeline.py +++ b/minos/minosPipeline/RunPipeline.py @@ -235,7 +235,7 @@ def type_check(data): data['nutrition_quality_diff'] = data['nutrition_quality_diff'].astype(int) data['neighbourhood_safety'] = data['neighbourhood_safety'].astype(int) data['chron_disease'] = data['chron_disease'].astype(float) - data['matdep'] = data['matdep'].astype(int) + data['matdep'] = data['matdep'].astype(float) return data diff --git a/minos/modules/material_deprivation.py b/minos/modules/material_deprivation.py index bc47edd7..0218f259 100644 --- a/minos/modules/material_deprivation.py +++ b/minos/modules/material_deprivation.py @@ -130,16 +130,7 @@ def on_time_step(self, event): list(matdep_prob_df.columns), matdep_prob_df) + 1 - # housing_prob_df.index = pop.index - # - # # convert numeric prediction into string factors (low, medium, high) - # housing_factor_dict = {1: 'Low', - # 2: 'Medium', - # 3: 'High'} - # housing_prob_df.replace({'matdep': housing_factor_dict}, - # inplace=True) - - self.population_view.update(matdep_prob_df["matdep"]) + self.population_view.update(matdep_prob_df["matdep"].astype(float)) def calculate_matdep(self, pop): From 453939c9258985bff1b31dcb7ac6a966b7cb25ea Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Fri, 10 Nov 2023 10:19:17 +0000 Subject: [PATCH 087/229] Point to the right location for qaly outputs, remove some debugging stuff from earlier --- .../testing/QALY_comparison_energyCrises.Rmd | 28 +++++++++---------- .../QALY_comparison_energyCrises_diff.Rmd | 28 +++++++++---------- minos/testing/QALY_comparison_livingWage.Rmd | 22 +++++++-------- 3 files changed, 39 insertions(+), 39 deletions(-) diff --git a/minos/testing/QALY_comparison_energyCrises.Rmd b/minos/testing/QALY_comparison_energyCrises.Rmd index 40d655f6..8ec7dd1f 100644 --- a/minos/testing/QALY_comparison_energyCrises.Rmd +++ b/minos/testing/QALY_comparison_energyCrises.Rmd @@ -40,22 +40,22 @@ source(here::here('minos', 'utils_qaly.R')) ```{r} # # read in QALY files for baseline and interventions -# out.path <- here::here('output', 'default_config') -# #out.path <- here::here('output', 'glasgow_scaled') -# -# base.path <- get_latest_runtime_subdirectory(here::here(out.path, 'baseline')) -# support.path <- get_latest_runtime_subdirectory(here::here(out.path, 'energyDownlift')) -# noSupport.path <- get_latest_runtime_subdirectory(here::here(out.path, 'energyDownliftNoSupport')) -# -# base <- read.csv(paste0(base.path, 'qalys.csv')) -# support <- read.csv(paste0(support.path, 'qalys.csv')) -# noSupport <- read.csv(paste0(noSupport.path, 'qalys.csv')) +out.path <- here::here('output', 'default_config') +#out.path <- here::here('output', 'glasgow_scaled') -out.path <- '/home/luke/Documents/WORK/MINOS/PLOTS_FROM_ARC/QALY_TESTING/' +base.path <- get_latest_runtime_subdirectory(here::here(out.path, 'baseline')) +support.path <- get_latest_runtime_subdirectory(here::here(out.path, 'energyDownlift')) +noSupport.path <- get_latest_runtime_subdirectory(here::here(out.path, 'energyDownliftNoSupport')) -base <- read.csv(paste0(out.path, 'qalys_baseline.csv')) -support <- read.csv(paste0(out.path, 'qalys_support.csv')) -noSupport <- read.csv(paste0(out.path, 'qalys_nosupport.csv')) +base <- read.csv(paste0(base.path, 'qalys.csv')) +support <- read.csv(paste0(support.path, 'qalys.csv')) +noSupport <- read.csv(paste0(noSupport.path, 'qalys.csv')) + +# out.path <- '/home/luke/Documents/WORK/MINOS/PLOTS_FROM_ARC/QALY_TESTING/' +# +# base <- read.csv(paste0(out.path, 'qalys_baseline.csv')) +# support <- read.csv(paste0(out.path, 'qalys_support.csv')) +# noSupport <- read.csv(paste0(out.path, 'qalys_nosupport.csv')) # combine the dataframes combined <- rbind(base, support, noSupport) diff --git a/minos/testing/QALY_comparison_energyCrises_diff.Rmd b/minos/testing/QALY_comparison_energyCrises_diff.Rmd index 6e4ac338..0cac78fd 100644 --- a/minos/testing/QALY_comparison_energyCrises_diff.Rmd +++ b/minos/testing/QALY_comparison_energyCrises_diff.Rmd @@ -40,22 +40,22 @@ source(here::here('minos', 'utils_qaly.R')) ```{r} # # read in QALY files for baseline and interventions -# #out.path <- here::here('output', 'glasgow_scaled') -# out.path <- here::here('output', 'default_config') -# -# base.path <- get_latest_runtime_subdirectory(here::here(out.path, 'baseline')) -# support.path <- get_latest_runtime_subdirectory(here::here(out.path, 'energyDownlift')) -# noSupport.path <- get_latest_runtime_subdirectory(here::here(out.path, 'energyDownliftNoSupport')) -# -# base <- read.csv(paste0(base.path, 'qalys.csv')) -# support <- read.csv(paste0(support.path, 'qalys.csv')) -# noSupport <- read.csv(paste0(noSupport.path, 'qalys.csv')) +#out.path <- here::here('output', 'glasgow_scaled') +out.path <- here::here('output', 'default_config') -out.path <- '/home/luke/Documents/WORK/MINOS/PLOTS_FROM_ARC/QALY_TESTING/' +base.path <- get_latest_runtime_subdirectory(here::here(out.path, 'baseline')) +support.path <- get_latest_runtime_subdirectory(here::here(out.path, 'energyDownlift')) +noSupport.path <- get_latest_runtime_subdirectory(here::here(out.path, 'energyDownliftNoSupport')) -base <- read.csv(paste0(out.path, 'qalys_baseline.csv')) -support <- read.csv(paste0(out.path, 'qalys_support.csv')) -noSupport <- read.csv(paste0(out.path, 'qalys_nosupport.csv')) +base <- read.csv(paste0(base.path, 'qalys.csv')) +support <- read.csv(paste0(support.path, 'qalys.csv')) +noSupport <- read.csv(paste0(noSupport.path, 'qalys.csv')) + +# out.path <- '/home/luke/Documents/WORK/MINOS/PLOTS_FROM_ARC/QALY_TESTING/' +# +# base <- read.csv(paste0(out.path, 'qalys_baseline.csv')) +# support <- read.csv(paste0(out.path, 'qalys_support.csv')) +# noSupport <- read.csv(paste0(out.path, 'qalys_nosupport.csv')) # combine the dataframes combined <- rbind(support, noSupport) diff --git a/minos/testing/QALY_comparison_livingWage.Rmd b/minos/testing/QALY_comparison_livingWage.Rmd index 8f853b65..c3f67ce6 100644 --- a/minos/testing/QALY_comparison_livingWage.Rmd +++ b/minos/testing/QALY_comparison_livingWage.Rmd @@ -40,19 +40,19 @@ source(here::here('minos', 'utils_qaly.R')) ```{r} # # read in QALY files for baseline and interventions -# out.path <- here::here('output', 'default_config') -# #out.path <- here::here('output', 'glasgow_scaled') -# -# base.path <- get_latest_runtime_subdirectory(here::here(out.path, 'baseline')) -# wage.path <- get_latest_runtime_subdirectory(here::here(out.path, 'livingWageIntervention')) -# -# base <- read.csv(paste0(base.path, 'qalys.csv')) -# wage <- read.csv(paste0(wage.path, 'qalys.csv')) +out.path <- here::here('output', 'default_config') +#out.path <- here::here('output', 'glasgow_scaled') -out.path <- '/home/luke/Documents/WORK/MINOS/PLOTS_FROM_ARC/QALY_TESTING/' +base.path <- get_latest_runtime_subdirectory(here::here(out.path, 'baseline')) +wage.path <- get_latest_runtime_subdirectory(here::here(out.path, 'livingWageIntervention')) -base <- read.csv(paste0(out.path, 'qalys_baseline.csv')) -wage <- read.csv(paste0(out.path, 'qalys_livwage.csv')) +base <- read.csv(paste0(base.path, 'qalys.csv')) +wage <- read.csv(paste0(wage.path, 'qalys.csv')) + +# out.path <- '/home/luke/Documents/WORK/MINOS/PLOTS_FROM_ARC/QALY_TESTING/' +# +# base <- read.csv(paste0(out.path, 'qalys_baseline.csv')) +# wage <- read.csv(paste0(out.path, 'qalys_livwage.csv')) # combine the dataframes combined <- rbind(base, wage) From aa2f87d301a6da7771d0985525c360a605a05f85 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Fri, 10 Nov 2023 11:16:53 +0000 Subject: [PATCH 088/229] Switch to visualise glasgow synthetic runs --- minos/testing/QALY_comparison_energyCrises.Rmd | 4 ++-- minos/testing/QALY_comparison_energyCrises_diff.Rmd | 4 ++-- minos/testing/QALY_comparison_livingWage.Rmd | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/minos/testing/QALY_comparison_energyCrises.Rmd b/minos/testing/QALY_comparison_energyCrises.Rmd index 8ec7dd1f..153182f5 100644 --- a/minos/testing/QALY_comparison_energyCrises.Rmd +++ b/minos/testing/QALY_comparison_energyCrises.Rmd @@ -40,8 +40,8 @@ source(here::here('minos', 'utils_qaly.R')) ```{r} # # read in QALY files for baseline and interventions -out.path <- here::here('output', 'default_config') -#out.path <- here::here('output', 'glasgow_scaled') +#out.path <- here::here('output', 'default_config') +out.path <- here::here('output', 'glasgow_scaled') base.path <- get_latest_runtime_subdirectory(here::here(out.path, 'baseline')) support.path <- get_latest_runtime_subdirectory(here::here(out.path, 'energyDownlift')) diff --git a/minos/testing/QALY_comparison_energyCrises_diff.Rmd b/minos/testing/QALY_comparison_energyCrises_diff.Rmd index 0cac78fd..8aaaa65f 100644 --- a/minos/testing/QALY_comparison_energyCrises_diff.Rmd +++ b/minos/testing/QALY_comparison_energyCrises_diff.Rmd @@ -40,8 +40,8 @@ source(here::here('minos', 'utils_qaly.R')) ```{r} # # read in QALY files for baseline and interventions -#out.path <- here::here('output', 'glasgow_scaled') -out.path <- here::here('output', 'default_config') +out.path <- here::here('output', 'glasgow_scaled') +#out.path <- here::here('output', 'default_config') base.path <- get_latest_runtime_subdirectory(here::here(out.path, 'baseline')) support.path <- get_latest_runtime_subdirectory(here::here(out.path, 'energyDownlift')) diff --git a/minos/testing/QALY_comparison_livingWage.Rmd b/minos/testing/QALY_comparison_livingWage.Rmd index c3f67ce6..59bbe653 100644 --- a/minos/testing/QALY_comparison_livingWage.Rmd +++ b/minos/testing/QALY_comparison_livingWage.Rmd @@ -40,8 +40,8 @@ source(here::here('minos', 'utils_qaly.R')) ```{r} # # read in QALY files for baseline and interventions -out.path <- here::here('output', 'default_config') -#out.path <- here::here('output', 'glasgow_scaled') +#out.path <- here::here('output', 'default_config') +out.path <- here::here('output', 'glasgow_scaled') base.path <- get_latest_runtime_subdirectory(here::here(out.path, 'baseline')) wage.path <- get_latest_runtime_subdirectory(here::here(out.path, 'livingWageIntervention')) From e75b54cc42d487d3b2dd26c6c662a3c390fe3878 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 13 Nov 2023 09:52:12 +0000 Subject: [PATCH 089/229] Added new make targets for the uk synthetic QALY calculations, and pointed the notebooks to the SF12_uk_scaled directory (on arc) --- minos/outcomes/Makefile | 32 +++++++++++++++++++ .../testing/QALY_comparison_energyCrises.Rmd | 3 +- .../QALY_comparison_energyCrises_diff.Rmd | 3 +- minos/testing/QALY_comparison_livingWage.Rmd | 3 +- 4 files changed, 38 insertions(+), 3 deletions(-) diff --git a/minos/outcomes/Makefile b/minos/outcomes/Makefile index e795fe59..02b97a12 100644 --- a/minos/outcomes/Makefile +++ b/minos/outcomes/Makefile @@ -12,6 +12,8 @@ OUTCOMES=$(ROOT)/minos/outcomes QALY: python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) +## Default QALYs + QALY_baseline: MODE=default_config QALY_baseline: INTERVENTION=baseline QALY_baseline: @@ -49,6 +51,8 @@ QALY_comparisons: QALY_baseline QALY_energyDownlift QALY_energyDownliftNoSupport $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises_diff.Rmd')" +## No Replenishment QALYs (cohort) + QALY_baseline_norepl: MODE=default_no_replenishment QALY_baseline_norepl: INTERVENTION=baseline QALY_baseline_norepl: @@ -82,6 +86,7 @@ QALY_comparisons_norepl: QALY_baseline_norepl QALY_energyDownlift_norepl QALY_en $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises_diff.Rmd')" +## Glasgow QALYs QALY_baseline_glasgow: MODE=glasgow_scaled QALY_baseline_glasgow: INTERVENTION=baseline @@ -108,6 +113,33 @@ QALYs_glasgow: QALY_baseline_glasgow QALY_energysupport_glasgow QALY_energynosup $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises_diff.Rmd')" $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" +## UK scale QALYs + +QALY_baseline_uk: MODE=uk_scaled +QALY_baseline_uk: INTERVENTION=baseline +QALY_baseline_uk: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_energysupport_uk: MODE=uk_scaled +QALY_energysupport_uk: INTERVENTION=energyDownlift +QALY_energysupport_uk: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_energynosupport_uk: MODE=uk_scaled +QALY_energynosupport_uk: INTERVENTION=energyDownliftNoSupport +QALY_energynosupport_uk: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_livwage_uk: MODE=uk_scaled +QALY_livwage_uk: INTERVENTION=livingWageIntervention +QALY_livwage_uk: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALYs_uk: QALY_baseline_uk QALY_energysupport_uk QALY_energynosupport_uk QALY_livwage_uk + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises.Rmd')" + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises_diff.Rmd')" + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" + ##################################### # Post-hoc aggregation of multiple MINOS runs on bash terminal. diff --git a/minos/testing/QALY_comparison_energyCrises.Rmd b/minos/testing/QALY_comparison_energyCrises.Rmd index 153182f5..20575e7f 100644 --- a/minos/testing/QALY_comparison_energyCrises.Rmd +++ b/minos/testing/QALY_comparison_energyCrises.Rmd @@ -41,7 +41,8 @@ source(here::here('minos', 'utils_qaly.R')) ```{r} # # read in QALY files for baseline and interventions #out.path <- here::here('output', 'default_config') -out.path <- here::here('output', 'glasgow_scaled') +#out.path <- here::here('output', 'glasgow_scaled') +out.path <- here::here('output', 'SF12_uk_scaled') base.path <- get_latest_runtime_subdirectory(here::here(out.path, 'baseline')) support.path <- get_latest_runtime_subdirectory(here::here(out.path, 'energyDownlift')) diff --git a/minos/testing/QALY_comparison_energyCrises_diff.Rmd b/minos/testing/QALY_comparison_energyCrises_diff.Rmd index 8aaaa65f..49b184cd 100644 --- a/minos/testing/QALY_comparison_energyCrises_diff.Rmd +++ b/minos/testing/QALY_comparison_energyCrises_diff.Rmd @@ -40,8 +40,9 @@ source(here::here('minos', 'utils_qaly.R')) ```{r} # # read in QALY files for baseline and interventions -out.path <- here::here('output', 'glasgow_scaled') #out.path <- here::here('output', 'default_config') +#out.path <- here::here('output', 'glasgow_scaled') +out.path <- here::here('output', 'SF12_uk_scaled') base.path <- get_latest_runtime_subdirectory(here::here(out.path, 'baseline')) support.path <- get_latest_runtime_subdirectory(here::here(out.path, 'energyDownlift')) diff --git a/minos/testing/QALY_comparison_livingWage.Rmd b/minos/testing/QALY_comparison_livingWage.Rmd index 59bbe653..0d9beb28 100644 --- a/minos/testing/QALY_comparison_livingWage.Rmd +++ b/minos/testing/QALY_comparison_livingWage.Rmd @@ -41,7 +41,8 @@ source(here::here('minos', 'utils_qaly.R')) ```{r} # # read in QALY files for baseline and interventions #out.path <- here::here('output', 'default_config') -out.path <- here::here('output', 'glasgow_scaled') +#out.path <- here::here('output', 'glasgow_scaled') +out.path <- here::here('output', 'SF12_uk_scaled') base.path <- get_latest_runtime_subdirectory(here::here(out.path, 'baseline')) wage.path <- get_latest_runtime_subdirectory(here::here(out.path, 'livingWageIntervention')) From 1c471231f99f9d08df7d9ca38b54bd05e94046de Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 13 Nov 2023 09:58:13 +0000 Subject: [PATCH 090/229] Fixed the path to the output directory for arc --- minos/outcomes/Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/minos/outcomes/Makefile b/minos/outcomes/Makefile index 02b97a12..1706b80d 100644 --- a/minos/outcomes/Makefile +++ b/minos/outcomes/Makefile @@ -115,22 +115,22 @@ QALYs_glasgow: QALY_baseline_glasgow QALY_energysupport_glasgow QALY_energynosup ## UK scale QALYs -QALY_baseline_uk: MODE=uk_scaled +QALY_baseline_uk: MODE=SF12_uk_scaled QALY_baseline_uk: INTERVENTION=baseline QALY_baseline_uk: python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) -QALY_energysupport_uk: MODE=uk_scaled +QALY_energysupport_uk: MODE=SF12_uk_scaled QALY_energysupport_uk: INTERVENTION=energyDownlift QALY_energysupport_uk: python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) -QALY_energynosupport_uk: MODE=uk_scaled +QALY_energynosupport_uk: MODE=SF12_uk_scaled QALY_energynosupport_uk: INTERVENTION=energyDownliftNoSupport QALY_energynosupport_uk: python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) -QALY_livwage_uk: MODE=uk_scaled +QALY_livwage_uk: MODE=SF12_uk_scaled QALY_livwage_uk: INTERVENTION=livingWageIntervention QALY_livwage_uk: python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) From 1e54b726d30ac5147e902542f28b071f2fb837a8 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 13 Nov 2023 10:09:10 +0000 Subject: [PATCH 091/229] Updated all config files to the latest set of modules --- config/S7_glasgow_scaled.yaml | 4 +--- config/inflated_default.yaml | 2 +- config/uk_scaled.yaml | 4 +--- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/config/S7_glasgow_scaled.yaml b/config/S7_glasgow_scaled.yaml index 3d32b207..1fa9d3c8 100644 --- a/config/S7_glasgow_scaled.yaml +++ b/config/S7_glasgow_scaled.yaml @@ -30,9 +30,7 @@ synthetic: TRUE # Finally everything else can go in any order (priority 2). #components : [MWB(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Income(), Housing(), Labour(), Nutrition(), Education(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] #components : [MWB(), Loneliness(), Tobacco(), Neighbourhood(), Housing(), Nutrition(), Income(), Education(), Mortality(), Replenishment()] -components : [S7EquivalentIncome(), Loneliness(), S7Neighbourhood(), S7Housing(), S7PhysicalHealth(), S7MentalHealth(), S7Labour(), lmmYJIncome(), Education(), - nkidsFertilityAgeSpecificRates(), Ageing(), Mortality(), - Replenishment()] +components : [lmmYJMWB(), lmmYJPCS(), Loneliness(), Tobacco(), Alcohol(), PhysicalActivity(), Neighbourhood(), MaterialDeprivation(), ChronicDisease(), Heating(), Housing(), HousingTenure(), lmmYJNutrition(), financialSituation(), lmmYJIncome(), Education(), Ageing(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] scale_rates: method: "constant" constant: diff --git a/config/inflated_default.yaml b/config/inflated_default.yaml index 926c806e..e11c1226 100644 --- a/config/inflated_default.yaml +++ b/config/inflated_default.yaml @@ -29,7 +29,7 @@ cross_validation: FALSE #components : [MWB(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Income(), Housing(), Labour(), Nutrition(), Education(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] #components : [MWB(), Loneliness(), Tobacco(), Neighbourhood(), Housing(), Nutrition(), Income(), Education(), Mortality(), Replenishment()] # Correct ordering of components is automated in RunPipeline, so can be passed in any order here -components : [lmmYJMWB(), lmmYJPCS(), Loneliness(), Tobacco(), Alcohol(), PhysicalActivity(), Neighbourhood(), Heating(), Housing(), HousingTenure(), lmmYJNutrition(), financialSituation(), lmmYJIncome(), Education(), Ageing(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] +components : [lmmYJMWB(), lmmYJPCS(), Loneliness(), Tobacco(), Alcohol(), PhysicalActivity(), Neighbourhood(), MaterialDeprivation(), ChronicDisease(), Heating(), Housing(), HousingTenure(), lmmYJNutrition(), financialSituation(), lmmYJIncome(), Education(), Ageing(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] scale_rates: method: "constant" diff --git a/config/uk_scaled.yaml b/config/uk_scaled.yaml index 8a0033e2..b8a7e8d9 100644 --- a/config/uk_scaled.yaml +++ b/config/uk_scaled.yaml @@ -30,9 +30,7 @@ synthetic: TRUE # Finally everything else can go in any order (priority 2). #components : [MWB(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Income(), Housing(), Labour(), Nutrition(), Education(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] #components : [MWB(), Loneliness(), Tobacco(), Neighbourhood(), Housing(), Nutrition(), Income(), Education(), Mortality(), Replenishment()] -components : [lmmYJMWB(), Loneliness(), Tobacco(), Neighbourhood(), Housing(), lmmYJNutrition(), lmmYJIncome(), Education(), - nkidsFertilityAgeSpecificRates(), Ageing(), Mortality(), - Replenishment()] +components : [lmmYJMWB(), lmmYJPCS(), Loneliness(), Tobacco(), Alcohol(), PhysicalActivity(), Neighbourhood(), MaterialDeprivation(), ChronicDisease(), Heating(), Housing(), HousingTenure(), lmmYJNutrition(), financialSituation(), lmmYJIncome(), Education(), Ageing(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] scale_rates: method: "constant" constant: From a3b52e71dff5c0651b7846b49257bad0ca48d767 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 13 Nov 2023 11:02:21 +0000 Subject: [PATCH 092/229] Changed the way components are read in to the model from config. Instead of defining the component list in each config file (legacy logic), now we keep the component lists for each experiment in a text file, which is defined in the config files and read in in RunPipeline.py. This means we only have to update 1 file with new modules instead of each individual file --- config/S7_glasgow_scaled.yaml | 2 ++ config/S7_uk_scaled.yaml | 2 ++ config/SF12_components.txt | 20 +++++++++++++++++++ config/SIPHER7.yaml | 4 ++++ config/SIPHER7_components.txt | 13 ++++++++++++ .../cross_validation_S7_1.yaml | 4 ++++ .../cross_validation_S7_2.yaml | 6 +++++- .../cross_validation_S7_3.yaml | 6 +++++- .../cross_validation_S7_4.yaml | 6 +++++- .../cross_validation_S7_5.yaml | 6 +++++- .../cross_validation_default1.yaml | 4 ++++ .../cross_validation_default2.yaml | 4 ++++ .../cross_validation_default3.yaml | 4 ++++ .../cross_validation_default4.yaml | 4 ++++ .../cross_validation_default5.yaml | 4 ++++ config/default.yaml | 4 ++++ config/default_noReplenishment.yaml | 4 ++++ config/glasgow_scaled.yaml | 2 ++ config/inflated_default.yaml | 4 ++++ config/scot_default.yaml | 4 ++++ config/scotland_scaled.yaml | 2 ++ config/uk_scaled.yaml | 2 ++ minos/minosPipeline/RunPipeline.py | 9 ++++++++- 23 files changed, 115 insertions(+), 5 deletions(-) create mode 100644 config/SF12_components.txt create mode 100644 config/SIPHER7_components.txt diff --git a/config/S7_glasgow_scaled.yaml b/config/S7_glasgow_scaled.yaml index 1fa9d3c8..87a76b3c 100644 --- a/config/S7_glasgow_scaled.yaml +++ b/config/S7_glasgow_scaled.yaml @@ -21,6 +21,8 @@ replenishing_dir: 'data/replenishing/glasgow_scaled' cross_validation: FALSE synthetic: TRUE +component_file: 'config/SIPHER7_components.txt' + # REALLY IMPORTANT NOTE FOR THE LOVE OF GOD READ ME. # The order of these listed components is important. They are initialised last one in first one off. # All other components need the replenishment module which loads in real cohort data. diff --git a/config/S7_uk_scaled.yaml b/config/S7_uk_scaled.yaml index ad1b7c30..c6c246bf 100644 --- a/config/S7_uk_scaled.yaml +++ b/config/S7_uk_scaled.yaml @@ -21,6 +21,8 @@ replenishing_dir: 'data/replenishing/uk_scaled' cross_validation: FALSE synthetic: TRUE +component_file: 'config/SIPHER7_components.txt' + # REALLY IMPORTANT NOTE FOR THE LOVE OF GOD READ ME. # The order of these listed components is important. They are initialised last one in first one off. # All other components need the replenishment module which loads in real cohort data. diff --git a/config/SF12_components.txt b/config/SF12_components.txt new file mode 100644 index 00000000..981b788f --- /dev/null +++ b/config/SF12_components.txt @@ -0,0 +1,20 @@ +lmmYJMWB() +lmmYJPCS() +Loneliness() +Tobacco() +Alcohol() +PhysicalActivity() +Neighbourhood() +MaterialDeprivation() +ChronicDisease() +Heating() +Housing() +HousingTenure() +lmmYJNutrition() +financialSituation() +lmmYJIncome() +Education() +Ageing() +Mortality() +nkidsFertilityAgeSpecificRates() +Replenishment() \ No newline at end of file diff --git a/config/SIPHER7.yaml b/config/SIPHER7.yaml index 4ff8ccfd..c9948f2e 100644 --- a/config/SIPHER7.yaml +++ b/config/SIPHER7.yaml @@ -20,7 +20,11 @@ output_data_dir: "output" transition_dir: 'data/transitions' replenishing_dir: 'data/replenishing' + cross_validation: FALSE +synthetic: FALSE + +component_file: 'config/SIPHER7_components.txt' # Correct ordering of components is automated in RunPipeline, so can be passed in any order here components : [S7EquivalentIncome(), Loneliness(), S7Neighbourhood(), S7Housing(), S7PhysicalHealth(), S7MentalHealth(), S7Labour(), lmmYJIncome(), Education(), diff --git a/config/SIPHER7_components.txt b/config/SIPHER7_components.txt new file mode 100644 index 00000000..a5f90b22 --- /dev/null +++ b/config/SIPHER7_components.txt @@ -0,0 +1,13 @@ +S7EquivalentIncome() +Loneliness() +S7Neighbourhood() +S7Housing() +S7PhysicalHealth() +S7MentalHealth() +S7Labour() +lmmYJIncome() +Education() +nkidsFertilityAgeSpecificRates() +Ageing() +Mortality() +Replenishment() \ No newline at end of file diff --git a/config/cross_validation/cross_validation_S7_1.yaml b/config/cross_validation/cross_validation_S7_1.yaml index 03043aad..49ca14f2 100644 --- a/config/cross_validation/cross_validation_S7_1.yaml +++ b/config/cross_validation/cross_validation_S7_1.yaml @@ -20,7 +20,11 @@ output_data_dir: "output" transition_dir: 'data/transitions/cross_validation/version1' replenishing_dir: 'data/replenishing/cross_validation' + cross_validation: TRUE +synthetic: TRUE + +component_file: 'config/SIPHER7_components.txt' # Correct ordering of components is automated in RunPipeline, so can be passed in any order here components : [S7EquivalentIncome(), Loneliness(), S7Neighbourhood(), S7Housing(), S7PhysicalHealth(), S7MentalHealth(), S7Labour(), lmmYJIncome(), Education(), diff --git a/config/cross_validation/cross_validation_S7_2.yaml b/config/cross_validation/cross_validation_S7_2.yaml index 3878f604..47f312c3 100644 --- a/config/cross_validation/cross_validation_S7_2.yaml +++ b/config/cross_validation/cross_validation_S7_2.yaml @@ -18,9 +18,13 @@ input_data_dir: "data/final_US/cross_validation/batch2" persistent_data_dir: "persistent_data" output_data_dir: "output" -transition_dir: 'data/transitions/cross_validation/version2' +transition_dir: 'data/transitions/cross_validation/version1' replenishing_dir: 'data/replenishing/cross_validation' + cross_validation: TRUE +synthetic: TRUE + +component_file: 'config/SIPHER7_components.txt' # Correct ordering of components is automated in RunPipeline, so can be passed in any order here components : [S7EquivalentIncome(), Loneliness(), S7Neighbourhood(), S7Housing(), S7PhysicalHealth(), S7MentalHealth(), S7Labour(), lmmYJIncome(), Education(), diff --git a/config/cross_validation/cross_validation_S7_3.yaml b/config/cross_validation/cross_validation_S7_3.yaml index 68b3fc37..9c23a0a2 100644 --- a/config/cross_validation/cross_validation_S7_3.yaml +++ b/config/cross_validation/cross_validation_S7_3.yaml @@ -18,9 +18,13 @@ input_data_dir: "data/final_US/cross_validation/batch3" persistent_data_dir: "persistent_data" output_data_dir: "output" -transition_dir: 'data/transitions/cross_validation/version3' +transition_dir: 'data/transitions/cross_validation/version1' replenishing_dir: 'data/replenishing/cross_validation' + cross_validation: TRUE +synthetic: TRUE + +component_file: 'config/SIPHER7_components.txt' # Correct ordering of components is automated in RunPipeline, so can be passed in any order here components : [S7EquivalentIncome(), Loneliness(), S7Neighbourhood(), S7Housing(), S7PhysicalHealth(), S7MentalHealth(), S7Labour(), lmmYJIncome(), Education(), diff --git a/config/cross_validation/cross_validation_S7_4.yaml b/config/cross_validation/cross_validation_S7_4.yaml index 91bc531e..acc16af3 100644 --- a/config/cross_validation/cross_validation_S7_4.yaml +++ b/config/cross_validation/cross_validation_S7_4.yaml @@ -18,9 +18,13 @@ input_data_dir: "data/final_US/cross_validation/batch4" persistent_data_dir: "persistent_data" output_data_dir: "output" -transition_dir: 'data/transitions/cross_validation/version4' +transition_dir: 'data/transitions/cross_validation/version1' replenishing_dir: 'data/replenishing/cross_validation' + cross_validation: TRUE +synthetic: TRUE + +component_file: 'config/SIPHER7_components.txt' # Correct ordering of components is automated in RunPipeline, so can be passed in any order here components : [S7EquivalentIncome(), Loneliness(), S7Neighbourhood(), S7Housing(), S7PhysicalHealth(), S7MentalHealth(), S7Labour(), lmmYJIncome(), Education(), diff --git a/config/cross_validation/cross_validation_S7_5.yaml b/config/cross_validation/cross_validation_S7_5.yaml index 18b10aad..7decf51f 100644 --- a/config/cross_validation/cross_validation_S7_5.yaml +++ b/config/cross_validation/cross_validation_S7_5.yaml @@ -18,9 +18,13 @@ input_data_dir: "data/final_US/cross_validation/batch5" persistent_data_dir: "persistent_data" output_data_dir: "output" -transition_dir: 'data/transitions/cross_validation/version5' +transition_dir: 'data/transitions/cross_validation/version1' replenishing_dir: 'data/replenishing/cross_validation' + cross_validation: TRUE +synthetic: TRUE + +component_file: 'config/SIPHER7_components.txt' # Correct ordering of components is automated in RunPipeline, so can be passed in any order here components : [S7EquivalentIncome(), Loneliness(), S7Neighbourhood(), S7Housing(), S7PhysicalHealth(), S7MentalHealth(), S7Labour(), lmmYJIncome(), Education(), diff --git a/config/cross_validation/cross_validation_default1.yaml b/config/cross_validation/cross_validation_default1.yaml index cf57d979..da2ee482 100644 --- a/config/cross_validation/cross_validation_default1.yaml +++ b/config/cross_validation/cross_validation_default1.yaml @@ -20,7 +20,11 @@ output_data_dir: "output" transition_dir: 'data/transitions/cross_validation/version1' replenishing_dir: 'data/replenishing/cross_validation' + cross_validation: TRUE +synthetic: FALSE + +component_file: 'config/SF12_components.txt' # Correct ordering of components is automated in RunPipeline, so can be passed in any order here components : [lmmYJMWB(), lmmYJPCS(), Loneliness(), Tobacco(), Alcohol(), PhysicalActivity(), Neighbourhood(), MaterialDeprivation(), ChronicDisease(), Heating(), Housing(), HousingTenure(), lmmYJNutrition(), financialSituation(), lmmYJIncome(), Education(), Ageing(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] diff --git a/config/cross_validation/cross_validation_default2.yaml b/config/cross_validation/cross_validation_default2.yaml index cf4f3b35..f42a2b0b 100644 --- a/config/cross_validation/cross_validation_default2.yaml +++ b/config/cross_validation/cross_validation_default2.yaml @@ -20,7 +20,11 @@ output_data_dir: "output" transition_dir: 'data/transitions/cross_validation/version2' replenishing_dir: 'data/replenishing/cross_validation' + cross_validation: TRUE +synthetic: FALSE + +component_file: 'config/SF12_components.txt' # Correct ordering of components is automated in RunPipeline, so can be passed in any order here components : [lmmYJMWB(), lmmYJPCS(), Loneliness(), Tobacco(), Alcohol(), PhysicalActivity(), Neighbourhood(), MaterialDeprivation(), ChronicDisease(), Heating(), Housing(), HousingTenure(), lmmYJNutrition(), financialSituation(), lmmYJIncome(), Education(), Ageing(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] diff --git a/config/cross_validation/cross_validation_default3.yaml b/config/cross_validation/cross_validation_default3.yaml index e901c893..a49a9a95 100644 --- a/config/cross_validation/cross_validation_default3.yaml +++ b/config/cross_validation/cross_validation_default3.yaml @@ -20,7 +20,11 @@ output_data_dir: "output" transition_dir: 'data/transitions/cross_validation/version3' replenishing_dir: 'data/replenishing/cross_validation' + cross_validation: TRUE +synthetic: FALSE + +component_file: 'config/SF12_components.txt' # Correct ordering of components is automated in RunPipeline, so can be passed in any order here components : [lmmYJMWB(), lmmYJPCS(), Loneliness(), Tobacco(), Alcohol(), PhysicalActivity(), Neighbourhood(), MaterialDeprivation(), ChronicDisease(), Heating(), Housing(), HousingTenure(), lmmYJNutrition(), financialSituation(), lmmYJIncome(), Education(), Ageing(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] diff --git a/config/cross_validation/cross_validation_default4.yaml b/config/cross_validation/cross_validation_default4.yaml index 209e2ac0..19501426 100644 --- a/config/cross_validation/cross_validation_default4.yaml +++ b/config/cross_validation/cross_validation_default4.yaml @@ -20,7 +20,11 @@ output_data_dir: "output" transition_dir: 'data/transitions/cross_validation/version4' replenishing_dir: 'data/replenishing/cross_validation' + cross_validation: TRUE +synthetic: FALSE + +component_file: 'config/SF12_components.txt' # Correct ordering of components is automated in RunPipeline, so can be passed in any order here # Correct ordering of components is automated in RunPipeline, so can be passed in any order here diff --git a/config/cross_validation/cross_validation_default5.yaml b/config/cross_validation/cross_validation_default5.yaml index c83f36ca..e4de1034 100644 --- a/config/cross_validation/cross_validation_default5.yaml +++ b/config/cross_validation/cross_validation_default5.yaml @@ -20,7 +20,11 @@ output_data_dir: "output" transition_dir: 'data/transitions/cross_validation/version5' replenishing_dir: 'data/replenishing/cross_validation' + cross_validation: TRUE +synthetic: FALSE + +component_file: 'config/SF12_components.txt' # Correct ordering of components is automated in RunPipeline, so can be passed in any order here # Correct ordering of components is automated in RunPipeline, so can be passed in any order here diff --git a/config/default.yaml b/config/default.yaml index aa4d78c3..defa5732 100755 --- a/config/default.yaml +++ b/config/default.yaml @@ -20,7 +20,11 @@ output_data_dir: "output" transition_dir: 'data/transitions' replenishing_dir: 'data/replenishing' + cross_validation: FALSE +synthetic: FALSE + +component_file: 'config/SF12_components.txt' # Correct ordering of components is automated in RunPipeline, so can be passed in any order here # Correct ordering of components is automated in RunPipeline, so can be passed in any order here diff --git a/config/default_noReplenishment.yaml b/config/default_noReplenishment.yaml index 9d1a00f4..c54d668b 100644 --- a/config/default_noReplenishment.yaml +++ b/config/default_noReplenishment.yaml @@ -20,7 +20,11 @@ output_data_dir: "output" transition_dir: 'data/transitions' replenishing_dir: 'data/replenishing' + cross_validation: FALSE +synthetic: FALSE + +component_file: 'config/SF12_components.txt' # Correct ordering of components is automated in RunPipeline, so can be passed in any order here # Correct ordering of components is automated in RunPipeline, so can be passed in any order here diff --git a/config/glasgow_scaled.yaml b/config/glasgow_scaled.yaml index 1fa9d3c8..67ff2644 100644 --- a/config/glasgow_scaled.yaml +++ b/config/glasgow_scaled.yaml @@ -21,6 +21,8 @@ replenishing_dir: 'data/replenishing/glasgow_scaled' cross_validation: FALSE synthetic: TRUE +component_file: 'config/SF12_components.txt' + # REALLY IMPORTANT NOTE FOR THE LOVE OF GOD READ ME. # The order of these listed components is important. They are initialised last one in first one off. # All other components need the replenishment module which loads in real cohort data. diff --git a/config/inflated_default.yaml b/config/inflated_default.yaml index e11c1226..a0ebfb9b 100644 --- a/config/inflated_default.yaml +++ b/config/inflated_default.yaml @@ -17,7 +17,11 @@ output_data_dir: "output" transition_dir: 'data/transitions' replenishing_dir: 'data/replenishing/inflated' + cross_validation: FALSE +synthetic: FALSE + +component_file: 'config/SF12_components.txt' # REALLY IMPORTANT NOTE FOR THE LOVE OF GOD READ ME. # The order of these listed components is important. They are initialised last one in first one off. diff --git a/config/scot_default.yaml b/config/scot_default.yaml index 55664b6b..8c4d0bca 100755 --- a/config/scot_default.yaml +++ b/config/scot_default.yaml @@ -17,7 +17,11 @@ output_data_dir: "output" transition_dir: 'data/transitions/scotland' replenishing_dir: 'data/replenishing/scotland' + cross_validation: FALSE +synthetic: FALSE + +component_file: 'config/SF12_components.txt' # REALLY IMPORTANT NOTE FOR THE LOVE OF GOD READ ME. # The order of these listed components is important. They are initialised last one in first one off. diff --git a/config/scotland_scaled.yaml b/config/scotland_scaled.yaml index c6e5c518..95e1f608 100644 --- a/config/scotland_scaled.yaml +++ b/config/scotland_scaled.yaml @@ -21,6 +21,8 @@ replenishing_dir: 'data/replenishing/scotland_scaled' cross_validation: FALSE synthetic: TRUE +component_file: 'config/SF12_components.txt' + # REALLY IMPORTANT NOTE FOR THE LOVE OF GOD READ ME. # The order of these listed components is important. They are initialised last one in first one off. # All other components need the replenishment module which loads in real cohort data. diff --git a/config/uk_scaled.yaml b/config/uk_scaled.yaml index b8a7e8d9..fee9eb5b 100644 --- a/config/uk_scaled.yaml +++ b/config/uk_scaled.yaml @@ -21,6 +21,8 @@ replenishing_dir: 'data/replenishing/uk_scaled' cross_validation: FALSE synthetic: TRUE +component_file: 'SF12_components' + # REALLY IMPORTANT NOTE FOR THE LOVE OF GOD READ ME. # The order of these listed components is important. They are initialised last one in first one off. # All other components need the replenishment module which loads in real cohort data. diff --git a/minos/minosPipeline/RunPipeline.py b/minos/minosPipeline/RunPipeline.py index aa8c7603..05b219e3 100755 --- a/minos/minosPipeline/RunPipeline.py +++ b/minos/minosPipeline/RunPipeline.py @@ -257,7 +257,14 @@ def RunPipeline(config, intervention=None): # Replenishment always go last. (first in sim) # components = validate_components(config['components'], intervention) - components = validate_and_sort_components(config['components'], intervention) + # read in the components from the correct text file + component_list = [] + with open(config['component_file']) as comp_file: + for line in comp_file: + component_list.append(line.rstrip()) + + + components = validate_and_sort_components(component_list, intervention) # Initiate vivarium simulation object but DO NOT setup yet. simulation = InteractiveContext(components=components, From 18cd987a00af303680559cc4a406fc55f258b81d Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 14 Nov 2023 10:30:07 +0000 Subject: [PATCH 093/229] Updated the way configs get the components for a current run. Now the components are read from a text file, so we only have to modify 1 file instead of several. Also updated some transition models to point to the correct MCS or PCS, and cleaned up some things in modules --- config/SIPHER7.yaml | 2 +- config/SIPHER7_components.txt | 1 + .../cross_validation_S7_1.yaml | 2 +- .../cross_validation_S7_2.yaml | 2 +- .../cross_validation_S7_3.yaml | 2 +- .../cross_validation_S7_4.yaml | 2 +- .../cross_validation_S7_5.yaml | 2 +- minos/data_generation/generate_repl_pop.py | 4 +- minos/minosPipeline/RunPipeline.py | 4 +- minos/modules/S7Housing.py | 1 - minos/modules/S7Neighbourhood.py | 1 - minos/modules/chron_disease.py | 2 +- minos/modules/material_deprivation.py | 2 +- minos/modules/physical_wellbeing.py | 5 --- .../QALY_comparison_energyCrises_diff.Rmd | 44 +++++++++++++++++++ minos/transitions/model_definitions_S7.txt | 13 +++--- .../transitions/model_definitions_default.txt | 4 +- 17 files changed, 66 insertions(+), 27 deletions(-) diff --git a/config/SIPHER7.yaml b/config/SIPHER7.yaml index c9948f2e..34d9011e 100644 --- a/config/SIPHER7.yaml +++ b/config/SIPHER7.yaml @@ -27,7 +27,7 @@ synthetic: FALSE component_file: 'config/SIPHER7_components.txt' # Correct ordering of components is automated in RunPipeline, so can be passed in any order here -components : [S7EquivalentIncome(), Loneliness(), S7Neighbourhood(), S7Housing(), S7PhysicalHealth(), S7MentalHealth(), S7Labour(), lmmYJIncome(), Education(), +components : [S7EquivalentIncome(), Loneliness(), S7Neighbourhood(), S7Housing(), S7PhysicalHealth(), S7MentalHealth(), S7Labour(), JobSec(), lmmYJIncome(), Education(), nkidsFertilityAgeSpecificRates(), Ageing(), Mortality(), Replenishment()] diff --git a/config/SIPHER7_components.txt b/config/SIPHER7_components.txt index a5f90b22..f40b051a 100644 --- a/config/SIPHER7_components.txt +++ b/config/SIPHER7_components.txt @@ -6,6 +6,7 @@ S7PhysicalHealth() S7MentalHealth() S7Labour() lmmYJIncome() +JobSec() Education() nkidsFertilityAgeSpecificRates() Ageing() diff --git a/config/cross_validation/cross_validation_S7_1.yaml b/config/cross_validation/cross_validation_S7_1.yaml index 49ca14f2..0a9875c5 100644 --- a/config/cross_validation/cross_validation_S7_1.yaml +++ b/config/cross_validation/cross_validation_S7_1.yaml @@ -22,7 +22,7 @@ transition_dir: 'data/transitions/cross_validation/version1' replenishing_dir: 'data/replenishing/cross_validation' cross_validation: TRUE -synthetic: TRUE +synthetic: FALSE component_file: 'config/SIPHER7_components.txt' diff --git a/config/cross_validation/cross_validation_S7_2.yaml b/config/cross_validation/cross_validation_S7_2.yaml index 47f312c3..941598a9 100644 --- a/config/cross_validation/cross_validation_S7_2.yaml +++ b/config/cross_validation/cross_validation_S7_2.yaml @@ -22,7 +22,7 @@ transition_dir: 'data/transitions/cross_validation/version1' replenishing_dir: 'data/replenishing/cross_validation' cross_validation: TRUE -synthetic: TRUE +synthetic: FALSE component_file: 'config/SIPHER7_components.txt' diff --git a/config/cross_validation/cross_validation_S7_3.yaml b/config/cross_validation/cross_validation_S7_3.yaml index 9c23a0a2..7c82c9f4 100644 --- a/config/cross_validation/cross_validation_S7_3.yaml +++ b/config/cross_validation/cross_validation_S7_3.yaml @@ -22,7 +22,7 @@ transition_dir: 'data/transitions/cross_validation/version1' replenishing_dir: 'data/replenishing/cross_validation' cross_validation: TRUE -synthetic: TRUE +synthetic: FALSE component_file: 'config/SIPHER7_components.txt' diff --git a/config/cross_validation/cross_validation_S7_4.yaml b/config/cross_validation/cross_validation_S7_4.yaml index acc16af3..39932d3d 100644 --- a/config/cross_validation/cross_validation_S7_4.yaml +++ b/config/cross_validation/cross_validation_S7_4.yaml @@ -22,7 +22,7 @@ transition_dir: 'data/transitions/cross_validation/version1' replenishing_dir: 'data/replenishing/cross_validation' cross_validation: TRUE -synthetic: TRUE +synthetic: FALSE component_file: 'config/SIPHER7_components.txt' diff --git a/config/cross_validation/cross_validation_S7_5.yaml b/config/cross_validation/cross_validation_S7_5.yaml index 7decf51f..b33fb31b 100644 --- a/config/cross_validation/cross_validation_S7_5.yaml +++ b/config/cross_validation/cross_validation_S7_5.yaml @@ -22,7 +22,7 @@ transition_dir: 'data/transitions/cross_validation/version1' replenishing_dir: 'data/replenishing/cross_validation' cross_validation: TRUE -synthetic: TRUE +synthetic: FALSE component_file: 'config/SIPHER7_components.txt' diff --git a/minos/data_generation/generate_repl_pop.py b/minos/data_generation/generate_repl_pop.py index 880a0b53..4ef25057 100755 --- a/minos/data_generation/generate_repl_pop.py +++ b/minos/data_generation/generate_repl_pop.py @@ -229,8 +229,8 @@ def generate_replenishing(projections, scotland_mode, cross_validation, inflated final_repl['S7_physical_health'] = final_repl['S7_physical_health'].astype(int) final_repl['nutrition_quality_diff'] = final_repl['nutrition_quality_diff'].astype(int) final_repl['neighbourhood_safety'] = final_repl['neighbourhood_safety'].astype(int) - final_repl['chron_disease'] = final_repl['chron_disease'].astype(float) - final_repl['matdep'] = final_repl['matdep'].astype(float) + final_repl['chron_disease'] = final_repl['chron_disease'].astype(int) + final_repl['matdep'] = final_repl['matdep'].astype(int) US_utils.check_output_dir(output_dir) final_repl.to_csv(f'{output_dir}/replenishing_pop_2019-2070.csv', index=False) diff --git a/minos/minosPipeline/RunPipeline.py b/minos/minosPipeline/RunPipeline.py index 05b219e3..507bf7c5 100755 --- a/minos/minosPipeline/RunPipeline.py +++ b/minos/minosPipeline/RunPipeline.py @@ -234,8 +234,8 @@ def type_check(data): data['S7_physical_health'] = data['S7_physical_health'].astype(int) data['nutrition_quality_diff'] = data['nutrition_quality_diff'].astype(int) data['neighbourhood_safety'] = data['neighbourhood_safety'].astype(int) - data['chron_disease'] = data['chron_disease'].astype(float) - data['matdep'] = data['matdep'].astype(float) + data['chron_disease'] = data['chron_disease'].astype(int) + data['matdep'] = data['matdep'].astype(int) return data diff --git a/minos/modules/S7Housing.py b/minos/modules/S7Housing.py index 41b9f9ab..91985885 100644 --- a/minos/modules/S7Housing.py +++ b/minos/modules/S7Housing.py @@ -57,7 +57,6 @@ def setup(self, builder): # transition models and any outputs. view_columns = ["sex", "S7_labour_state", - "SF_12", "job_sec", "ethnicity", "age", diff --git a/minos/modules/S7Neighbourhood.py b/minos/modules/S7Neighbourhood.py index c550404e..ba3f41f2 100644 --- a/minos/modules/S7Neighbourhood.py +++ b/minos/modules/S7Neighbourhood.py @@ -51,7 +51,6 @@ def setup(self, builder): 'region', 'hh_income', 'S7_neighbourhood_safety', - 'SF_12', 'S7_labour_state', 'education_state', 'housing_quality', diff --git a/minos/modules/chron_disease.py b/minos/modules/chron_disease.py index dfa82dff..42284bbe 100644 --- a/minos/modules/chron_disease.py +++ b/minos/modules/chron_disease.py @@ -102,7 +102,7 @@ def on_time_step(self, event): # cd_prob_df['chron_disease'][cd_prob_df['previous_chron_disease'] > cd_prob_df['chron_disease']] = \ # cd_prob_df['previous_chron_disease'] - self.population_view.update(cd_prob_df["chron_disease"].astype(float)) + self.population_view.update(cd_prob_df["chron_disease"].astype(int)) def calculate_chron_disease(self, pop): """Calculate chron_disease transition distribution based on provided people/indices diff --git a/minos/modules/material_deprivation.py b/minos/modules/material_deprivation.py index 0218f259..4ec314b5 100644 --- a/minos/modules/material_deprivation.py +++ b/minos/modules/material_deprivation.py @@ -130,7 +130,7 @@ def on_time_step(self, event): list(matdep_prob_df.columns), matdep_prob_df) + 1 - self.population_view.update(matdep_prob_df["matdep"].astype(float)) + self.population_view.update(matdep_prob_df["matdep"].astype(int)) def calculate_matdep(self, pop): diff --git a/minos/modules/physical_wellbeing.py b/minos/modules/physical_wellbeing.py index 1366a58e..e611bfb0 100644 --- a/minos/modules/physical_wellbeing.py +++ b/minos/modules/physical_wellbeing.py @@ -177,16 +177,12 @@ def setup(self, builder): 'ethnicity', 'age', 'time', - #'education_state', - #'labour_state', - #'job_sec', 'hh_income', 'SF_12_MCS', 'SF_12_MCS_diff', 'SF_12_PCS', 'SF_12_PCS_diff', 'housing_quality', - #'phealth', 'ncigs', 'nutrition_quality', 'neighbourhood_safety', @@ -245,7 +241,6 @@ def on_time_step(self, event): #print(np.std(newWavePWB["SF_12_PCS"])) self.population_view.update(newWavePWB[['SF_12_PCS', "SF_12_PCS_diff"]]) - def calculate_pwb(self, pop): """Calculate SF_12 transition distribution based on provided people/indices Parameters diff --git a/minos/testing/QALY_comparison_energyCrises_diff.Rmd b/minos/testing/QALY_comparison_energyCrises_diff.Rmd index 49b184cd..79dcb308 100644 --- a/minos/testing/QALY_comparison_energyCrises_diff.Rmd +++ b/minos/testing/QALY_comparison_energyCrises_diff.Rmd @@ -118,6 +118,50 @@ sf12.plots(base = noSupport, int.name = 'energyDownlift') ``` +```{r} +base <- noSupport +base.name <- 'energyDownliftNoSupport' +int <- support +int.name <- 'energyDownlift' + +combined <- rbind(base, int) + +## now SF12 plots for comparison +p1 <- ggplot(combined, aes(x = year, y = SF_12_MCS, group = intervention, colour = intervention, fill = intervention)) + + geom_smooth() +p2 <- ggplot(combined, aes(x = year, y = SF_12_PCS, group = intervention, colour = intervention, fill = intervention)) + + geom_smooth() + +print(p1) +print(p2) + +combined.SF12 <- combined %>% + select(run_id, year, intervention, SF_12_MCS, SF_12_PCS) %>% + pivot_wider(names_from = 'intervention', + values_from = c('SF_12_MCS', 'SF_12_PCS')) %>% + mutate(MCS_diff = .data[[paste0('SF_12_MCS_', int.name)]] - .data[[paste0('SF_12_MCS_', base.name)]], + PCS_diff = .data[[paste0('SF_12_PCS_', int.name)]] - .data[[paste0('SF_12_PCS_', base.name)]]) %>% + select(run_id, year, MCS_diff, PCS_diff) + +p3 <- ggplot(combined.SF12, aes(x = year, y = MCS_diff)) + + geom_smooth() + + geom_hline(yintercept = 0, linetype = 'dashed') + + labs(title = 'Change in SF_12_MCS', subtitle = paste0(int.name, ' vs ', base.name)) + + xlab('Year') + + ylab('Change in MCS') + +p4 <- ggplot(combined.SF12, aes(x = year, y = PCS_diff)) + + geom_smooth() + + geom_hline(yintercept = 0, linetype = 'dashed') + + labs(title = 'Change in SF_12_PCS', subtitle = paste0(int.name, ' vs ', base.name)) + + xlab('Year') + + ylab('Change in MCS') + +print(p3) +print(p4) + +``` + ## Area Under the Curve diff --git a/minos/transitions/model_definitions_S7.txt b/minos/transitions/model_definitions_S7.txt index 44ad8434..7797a5e5 100644 --- a/minos/transitions/model_definitions_S7.txt +++ b/minos/transitions/model_definitions_S7.txt @@ -1,8 +1,9 @@ -CLM : loneliness ~ age + factor(sex) + SF_12 + relevel(factor(education_state), ref = '3') + relevel(factor(job_sec), ref = '3') + hh_income + relevel(factor(hh_comp), ref = '3') + relevel(factor(marital_status), ref = 'Partnered') + relevel(factor(ethnicity), ref = 'WBI') +CLM : loneliness ~ age + factor(sex) + relevel(factor(education_state), ref = '3') + relevel(factor(job_sec), ref = '3') + scale(hh_income) + relevel(factor(hh_comp), ref = '3') + relevel(factor(marital_status), ref = 'Partnered') + relevel(factor(ethnicity), ref = 'WBI') NNET : education_state ~ factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') -CLM : S7_housing_quality ~ age + factor(sex) + SF_12 + relevel(factor(ethnicity), ref = 'WBI') + hh_income -CLM : S7_neighbourhood_safety ~ age + factor(sex) + factor(job_sec) + relevel(factor(ethnicity), ref = 'WBI') + hh_income + factor(housing_quality) + relevel(factor(region), ref = 'Scotland') -NNET : S7_labour_state ~ age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + hh_income + S7_physical_health + S7_mental_health -CLM : S7_physical_health ~ age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(loneliness) + hh_income + S7_physical_health + S7_mental_health -CLM : S7_mental_health ~ age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(loneliness) + hh_income + S7_physical_health + S7_mental_health +CLM : job_sec ~ factor(job_sec) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = '3') +CLM : S7_housing_quality ~ age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(hh_income) +CLM : S7_neighbourhood_safety ~ age + factor(sex) + factor(job_sec) + relevel(factor(ethnicity), ref = 'WBI') + scale(hh_income) + factor(housing_quality) + relevel(factor(region), ref = 'Scotland') +NNET : S7_labour_state ~ age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + scale(hh_income) + S7_physical_health + S7_mental_health +CLM : S7_physical_health ~ age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(loneliness) + scale(hh_income) + S7_physical_health + S7_mental_health +CLM : S7_mental_health ~ age + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(loneliness) + scale(hh_income) + S7_physical_health + S7_mental_health GLMM : hh_income ~ scale(hh_income) + scale(hh_income_diff) + scale(age) + I(scale(age)**2) + I(scale(age)**3) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + relevel(factor(job_sec), ref = '3') + (1|pidp) diff --git a/minos/transitions/model_definitions_default.txt b/minos/transitions/model_definitions_default.txt index 135dff63..0ece1e43 100644 --- a/minos/transitions/model_definitions_default.txt +++ b/minos/transitions/model_definitions_default.txt @@ -1,6 +1,6 @@ CLM : chron_disease ~ factor(chron_disease) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(marital_status) + scale(ncigs) -LOGIT : active ~ age + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + hh_income + relevel(factor(ethnicity), ref = 'WBI') -NNET : auditc ~ age + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + hh_income + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + factor(financial_situation) +LOGIT : active ~ scale(age) + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + scale(hh_income) + relevel(factor(ethnicity), ref = 'WBI') +NNET : auditc ~ scale(age) + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + scale(hh_income) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + factor(financial_situation) CLM : housing_quality ~ scale(age) + I(scale(age)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) + I(scale(hh_income)**2) + scale(hh_income_diff) + factor(housing_tenure) CLM : loneliness ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + relevel(factor(job_sec), ref = '3') + scale(hh_income) + relevel(factor(marital_status), ref = 'Partnered') CLM : neighbourhood_safety ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) From 87e31a36efd2016a953cdd5d72aa2eb9470b1fee Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 14 Nov 2023 11:51:11 +0000 Subject: [PATCH 094/229] Attempting to fix weird bug where PCS doesnt transition. Think this will fix it. Updated some paths in config/uk_scaled.yaml, updated some functions in minos/utils_qaly.R and some tweaks to the QALY comparison scripts --- config/uk_scaled.yaml | 2 +- minos/outcomes/Makefile | 4 + .../testing/QALY_comparison_energyCrises.Rmd | 4 +- .../QALY_comparison_energyCrises_diff.Rmd | 166 +++++++++++++----- minos/testing/QALY_comparison_livingWage.Rmd | 82 ++++++++- minos/utils_qaly.R | 12 +- scripts/Makefile | 14 +- 7 files changed, 221 insertions(+), 63 deletions(-) diff --git a/config/uk_scaled.yaml b/config/uk_scaled.yaml index fee9eb5b..4c0fdcb8 100644 --- a/config/uk_scaled.yaml +++ b/config/uk_scaled.yaml @@ -21,7 +21,7 @@ replenishing_dir: 'data/replenishing/uk_scaled' cross_validation: FALSE synthetic: TRUE -component_file: 'SF12_components' +component_file: 'config/SF12_components.txt' # REALLY IMPORTANT NOTE FOR THE LOVE OF GOD READ ME. # The order of these listed components is important. They are initialised last one in first one off. diff --git a/minos/outcomes/Makefile b/minos/outcomes/Makefile index 1706b80d..e5d78fd4 100644 --- a/minos/outcomes/Makefile +++ b/minos/outcomes/Makefile @@ -140,6 +140,10 @@ QALYs_uk: QALY_baseline_uk QALY_energysupport_uk QALY_energynosupport_uk QALY_li $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises_diff.Rmd')" $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" +QALY_comparison_livingWage_uk: QALY_baseline_uk QALY_livwage_uk + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" + firefox file://$(TESTING)/QALY_comparison_livingWage.html + ##################################### # Post-hoc aggregation of multiple MINOS runs on bash terminal. diff --git a/minos/testing/QALY_comparison_energyCrises.Rmd b/minos/testing/QALY_comparison_energyCrises.Rmd index 20575e7f..073e3060 100644 --- a/minos/testing/QALY_comparison_energyCrises.Rmd +++ b/minos/testing/QALY_comparison_energyCrises.Rmd @@ -40,9 +40,9 @@ source(here::here('minos', 'utils_qaly.R')) ```{r} # # read in QALY files for baseline and interventions -#out.path <- here::here('output', 'default_config') +out.path <- here::here('output', 'default_config') #out.path <- here::here('output', 'glasgow_scaled') -out.path <- here::here('output', 'SF12_uk_scaled') +#out.path <- here::here('output', 'SF12_uk_scaled') base.path <- get_latest_runtime_subdirectory(here::here(out.path, 'baseline')) support.path <- get_latest_runtime_subdirectory(here::here(out.path, 'energyDownlift')) diff --git a/minos/testing/QALY_comparison_energyCrises_diff.Rmd b/minos/testing/QALY_comparison_energyCrises_diff.Rmd index 79dcb308..12af542e 100644 --- a/minos/testing/QALY_comparison_energyCrises_diff.Rmd +++ b/minos/testing/QALY_comparison_energyCrises_diff.Rmd @@ -40,9 +40,9 @@ source(here::here('minos', 'utils_qaly.R')) ```{r} # # read in QALY files for baseline and interventions -#out.path <- here::here('output', 'default_config') +out.path <- here::here('output', 'default_config') #out.path <- here::here('output', 'glasgow_scaled') -out.path <- here::here('output', 'SF12_uk_scaled') +#out.path <- here::here('output', 'SF12_uk_scaled') base.path <- get_latest_runtime_subdirectory(here::here(out.path, 'baseline')) support.path <- get_latest_runtime_subdirectory(here::here(out.path, 'energyDownlift')) @@ -119,46 +119,46 @@ sf12.plots(base = noSupport, ``` ```{r} -base <- noSupport -base.name <- 'energyDownliftNoSupport' -int <- support -int.name <- 'energyDownlift' - -combined <- rbind(base, int) - -## now SF12 plots for comparison -p1 <- ggplot(combined, aes(x = year, y = SF_12_MCS, group = intervention, colour = intervention, fill = intervention)) + - geom_smooth() -p2 <- ggplot(combined, aes(x = year, y = SF_12_PCS, group = intervention, colour = intervention, fill = intervention)) + - geom_smooth() - -print(p1) -print(p2) - -combined.SF12 <- combined %>% - select(run_id, year, intervention, SF_12_MCS, SF_12_PCS) %>% - pivot_wider(names_from = 'intervention', - values_from = c('SF_12_MCS', 'SF_12_PCS')) %>% - mutate(MCS_diff = .data[[paste0('SF_12_MCS_', int.name)]] - .data[[paste0('SF_12_MCS_', base.name)]], - PCS_diff = .data[[paste0('SF_12_PCS_', int.name)]] - .data[[paste0('SF_12_PCS_', base.name)]]) %>% - select(run_id, year, MCS_diff, PCS_diff) - -p3 <- ggplot(combined.SF12, aes(x = year, y = MCS_diff)) + - geom_smooth() + - geom_hline(yintercept = 0, linetype = 'dashed') + - labs(title = 'Change in SF_12_MCS', subtitle = paste0(int.name, ' vs ', base.name)) + - xlab('Year') + - ylab('Change in MCS') - -p4 <- ggplot(combined.SF12, aes(x = year, y = PCS_diff)) + - geom_smooth() + - geom_hline(yintercept = 0, linetype = 'dashed') + - labs(title = 'Change in SF_12_PCS', subtitle = paste0(int.name, ' vs ', base.name)) + - xlab('Year') + - ylab('Change in MCS') - -print(p3) -print(p4) +# base <- noSupport +# base.name <- 'energyDownliftNoSupport' +# int <- support +# int.name <- 'energyDownlift' +# +# combined <- rbind(base, int) +# +# ## now SF12 plots for comparison +# p1 <- ggplot(combined, aes(x = year, y = SF_12_MCS, group = intervention, colour = intervention, fill = intervention)) + +# geom_smooth() +# p2 <- ggplot(combined, aes(x = year, y = SF_12_PCS, group = intervention, colour = intervention, fill = intervention)) + +# geom_smooth() +# +# print(p1) +# print(p2) +# +# combined.SF12 <- combined %>% +# select(run_id, year, intervention, SF_12_MCS, SF_12_PCS) %>% +# pivot_wider(names_from = 'intervention', +# values_from = c('SF_12_MCS', 'SF_12_PCS')) %>% +# mutate(MCS_diff = .data[[paste0('SF_12_MCS_', int.name)]] - .data[[paste0('SF_12_MCS_', base.name)]], +# PCS_diff = .data[[paste0('SF_12_PCS_', int.name)]] - .data[[paste0('SF_12_PCS_', base.name)]]) %>% +# select(run_id, year, MCS_diff, PCS_diff) +# +# p3 <- ggplot(combined.SF12, aes(x = year, y = MCS_diff)) + +# geom_smooth() + +# geom_hline(yintercept = 0, linetype = 'dashed') + +# labs(title = 'Change in SF_12_MCS', subtitle = paste0(int.name, ' vs ', base.name)) + +# xlab('Year') + +# ylab('Change in MCS') +# +# p4 <- ggplot(combined.SF12, aes(x = year, y = PCS_diff)) + +# geom_smooth() + +# geom_hline(yintercept = 0, linetype = 'dashed') + +# labs(title = 'Change in SF_12_PCS', subtitle = paste0(int.name, ' vs ', base.name)) + +# xlab('Year') + +# ylab('Change in MCS') +# +# print(p3) +# print(p4) ``` @@ -181,7 +181,8 @@ cost.per.qaly(base = noSupport, base.name = 'energyDownliftNoSupport', int = support, int.name = 'energyDownlift', - QALY_value = 60000) # QALY value == £60,000 + QALY_value = 60000, # QALY value == £60,000 + int.label <- 'EPCG') ``` # Incremental Cost-Effectiveness Ratio @@ -195,7 +196,84 @@ ICER(base = noSupport, base.name = 'energyDownliftNoSupport', int = support, int.name = 'energyDownlift', - QALY_value = 60000) + QALY_value = 60000, + int.label = 'EPCG') +``` + + +```{r} +#ICER <- function(base, base.name, int, int.name, QALY_value) { + +base <- noSupport +base.name <- 'energyDownliftNoSupport' +int <- support +int.name <- 'energyDownlift' +QALY_value <- 60000 + + + +combined <- rbind(base, int) + +combined.cost.mean <- combined %>% + #select(-alive_pop, -SF_12_MCS, -SF_12_PCS, -utility) %>% + select(run_id, year, total_boost, intervention, QALYs) %>% + mutate(QALY_value = QALYs * QALY_value) %>% + group_by(run_id, year, intervention) %>% + summarise(across(everything(), mean)) + +# ICER = (C1 - C0) / (E1 - E0) +# where +# C1 = cost of intervention +# E1 = effect of intervention +# C0 = cost of control group +# E0 = effect of control group + +# prepare var names +c1 <- paste('total_boost', int.name, sep='_') +c0 <- paste('total_boost', base.name, sep='_') +e1 <- paste('QALYs', int.name, sep='_') +e0 <- paste('QALYs', base.name, sep='_') + +ICER <- combined.cost.mean %>% + pivot_wider(names_from = 'intervention', + values_from = c('total_boost', 'QALYs', 'QALY_value')) %>% + mutate(ICER = (.data[[c1]] - .data[[c0]]) / (.data[[e1]] - .data[[e0]])) %>% + select(run_id, year, ICER) %>% + mutate(intervention = int.name) + +pc1 <- quantile(ICER$ICER, .01) +print(pc1) +print(min(ICER$ICER)) +pc99 <- quantile(ICER$ICER, .99) +print(pc99) +print(max(ICER$ICER)) + +ICER <- filter(ICER, ICER < pc99, ICER > pc1) +print(min(ICER$ICER)) +print(max(ICER$ICER)) + +p1 <- ggplot(ICER, aes(x = year, y = ICER, group = intervention, fill = intervention, colour = intervention)) + + geom_smooth() + + scale_y_continuous(label = comma) + + labs(title = 'ICER', subtitle = paste0(int.name, ' vs ', base.name)) +print(p1) + +ICER.final <- ICER %>% + group_by(run_id, intervention) %>% + summarise(ICER = mean(ICER)) %>% + group_by(intervention) %>% + summarise(margin = (qt(0.975, df=n() - 1) * sd(ICER)) / sqrt(n()), + ICER = mean(ICER)) + +p2 <- ggplot(ICER.final, aes(x = intervention, y = ICER, group = intervention, fill = intervention)) + + geom_col() + + geom_errorbar(aes(ymin = ICER - margin, ymax = ICER + margin, width = 0.4, group = intervention)) + + scale_y_continuous(label = comma) + + labs(title = 'ICER', subtitle = paste0(int.name, ' vs ', base.name)) + + xlab('Intervention') +print(p2) + +print(paste0('ICER == ', ICER.final$ICER, ' +- ', ICER.final$margin)) ``` diff --git a/minos/testing/QALY_comparison_livingWage.Rmd b/minos/testing/QALY_comparison_livingWage.Rmd index 0d9beb28..0c852343 100644 --- a/minos/testing/QALY_comparison_livingWage.Rmd +++ b/minos/testing/QALY_comparison_livingWage.Rmd @@ -220,9 +220,11 @@ cost.per.qaly(base = base, base.name = 'baseline', int = wage, int.name = 'livingWageIntervention', - QALY_value = 60000) # QALY value == £60,000 + QALY_value = 60000, + int.label <- 'LivingWage') # QALY value == £60,000 ``` + ```{r} # total.combined.cost <- combined.cost.change %>% # group_by(run_id, intervention) %>% @@ -258,7 +260,83 @@ ICER(base = base, base.name = 'baseline', int = wage, int.name = 'livingWageIntervention', - QALY_value = 60000) + QALY_value = 60000, + int.label = 'LivingWage') +``` + +```{r} +#ICER <- function(base, base.name, int, int.name, QALY_value) { + +base <- base +base.name <- 'baseline' +int <- wage +int.name <- 'livingWageIntervention' +QALY_value <- 60000 +int.label <- 'LivingWage' + + +combined <- rbind(base, int) + +combined.cost.mean <- combined %>% + #select(-alive_pop, -SF_12_MCS, -SF_12_PCS, -utility) %>% + select(run_id, year, total_boost, intervention, QALYs) %>% + mutate(QALY_value = QALYs * QALY_value) %>% + group_by(run_id, year, intervention) %>% + summarise(across(everything(), mean)) + +# ICER = (C1 - C0) / (E1 - E0) +# where +# C1 = cost of intervention +# E1 = effect of intervention +# C0 = cost of control group +# E0 = effect of control group + +# prepare var names +c1 <- paste('total_boost', int.name, sep='_') +c0 <- paste('total_boost', base.name, sep='_') +e1 <- paste('QALYs', int.name, sep='_') +e0 <- paste('QALYs', base.name, sep='_') + +ICER <- combined.cost.mean %>% + pivot_wider(names_from = 'intervention', + values_from = c('total_boost', 'QALYs', 'QALY_value')) %>% + mutate(ICER = (.data[[c1]] - .data[[c0]]) / (.data[[e1]] - .data[[e0]])) %>% + select(run_id, year, ICER) %>% + mutate(intervention = int.label) + +pc1 <- quantile(ICER$ICER, .01) +print(pc1) +print(min(ICER$ICER)) +pc99 <- quantile(ICER$ICER, .99) +print(pc99) +print(max(ICER$ICER)) + +ICER <- filter(ICER, ICER < pc99, ICER > pc1) +print(min(ICER$ICER)) +print(max(ICER$ICER)) + +p1 <- ggplot(ICER, aes(x = year, y = ICER, group = intervention, fill = intervention, colour = intervention)) + + geom_smooth() + + scale_y_continuous(label = comma) + + labs(title = 'ICER', subtitle = paste0(int.name, ' vs ', base.name)) +print(p1) + +ICER.final <- ICER %>% + group_by(run_id, intervention) %>% + summarise(ICER = mean(ICER)) %>% + group_by(intervention) %>% + summarise(margin = (qt(0.975, df=n() - 1) * sd(ICER)) / sqrt(n()), + ICER = mean(ICER)) + +p2 <- ggplot(ICER.final, aes(x = intervention, y = ICER, group = intervention, fill = intervention)) + + geom_col() + + geom_errorbar(aes(ymin = ICER - margin, ymax = ICER + margin, width = 0.4, group = intervention)) + + scale_y_continuous(label = comma) + + labs(title = 'ICER', subtitle = paste0(int.name, ' vs ', base.name)) + + xlab('Intervention') +print(p2) + +print(paste0('ICER == ', ICER.final$ICER, ' +- ', ICER.final$margin)) ``` diff --git a/minos/utils_qaly.R b/minos/utils_qaly.R index 37ead5ea..9bcaad98 100644 --- a/minos/utils_qaly.R +++ b/minos/utils_qaly.R @@ -136,7 +136,7 @@ auc.plots <- function(base, base.name, intervention, int.name) { ## COST PER QALY # This function will calculate and plot the cost per QALY over time, as well # as the total cost change over the length of the simulation. -cost.per.qaly <- function(base, base.name, int, int.name, QALY_value) { +cost.per.qaly <- function(base, base.name, int, int.name, QALY_value, int.label) { combined <- rbind(base, int) combined.cost <- combined %>% @@ -161,7 +161,8 @@ cost.per.qaly <- function(base, base.name, int, int.name, QALY_value) { names_to = 'intervention', names_prefix = 'change_', values_to = 'QALY_value_change') %>% - filter(intervention != 'baseline') + filter(intervention != 'baseline') %>% + mutate(intervention = int.label) p2 <- ggplot(combined.cost.change, aes(x = year, y = QALY_value_change, group = intervention, colour = intervention, fill = intervention)) + geom_smooth() + @@ -187,7 +188,7 @@ cost.per.qaly <- function(base, base.name, int, int.name, QALY_value) { } -ICER <- function(base, base.name, int, int.name, QALY_value) { +ICER <- function(base, base.name, int, int.name, QALY_value, int.label) { combined <- rbind(base, int) combined.cost.mean <- combined %>% @@ -215,10 +216,7 @@ ICER <- function(base, base.name, int, int.name, QALY_value) { values_from = c('total_boost', 'QALYs', 'QALY_value')) %>% mutate(ICER = (.data[[c1]] - .data[[c0]]) / (.data[[e1]] - .data[[e0]])) %>% select(run_id, year, ICER) %>% - pivot_longer(cols = -c(run_id, year), - names_prefix = 'ICER_', - names_to = 'intervention', - values_to = 'ICER') + mutate(intervention = int.label) pc1 <- quantile(ICER$ICER, .01) print(pc1) diff --git a/scripts/Makefile b/scripts/Makefile index c6b9105b..493709c5 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -143,25 +143,25 @@ intervention_energyDownLiftNoSupport_scotland: setup_scotland_scaled baseline: ### Baseline run of MINOS, using configuration defined in testConfig.yaml baseline_uk: setup_uk_scaled - $(PYTHON) scripts/run.py -c $(CONFIG)/uk_scaled.yaml -o uk_scaled + $(PYTHON) scripts/run.py -c $(CONFIG)/uk_scaled.yaml -o SF12_uk_scaled intervention_hhIncome_uk: setup_uk_scaled - $(PYTHON) scripts/run.py -c $(CONFIG)/uk_scaled.yaml -o uk_scaled -i 'hhIncomeIntervention' + $(PYTHON) scripts/run.py -c $(CONFIG)/uk_scaled.yaml -o SF12_uk_scaled -i 'hhIncomeIntervention' intervention_hhIncomeChildUplift_uk: setup_uk_scaled - $(PYTHON) scripts/run.py -c $(CONFIG)/uk_scaled.yaml -o uk_scaled -i 'hhIncomeChildUplift' + $(PYTHON) scripts/run.py -c $(CONFIG)/uk_scaled.yaml -o SF12_uk_scaled -i 'hhIncomeChildUplift' intervention_PovertyLineChildUplift_uk: setup_uk_scaled - $(PYTHON) scripts/run.py -c $(CONFIG)/uk_scaled.yaml -o uk_scaled -i 'hhIncomePovertyLineChildUplift' + $(PYTHON) scripts/run.py -c $(CONFIG)/uk_scaled.yaml -o SF12_uk_scaled -i 'hhIncomePovertyLineChildUplift' intervention_livingWage_uk: setup_uk_scaled - $(PYTHON) scripts/run.py -c $(CONFIG)/uk_scaled.yaml -o uk_scaled -i 'livingWageIntervention' + $(PYTHON) scripts/run.py -c $(CONFIG)/uk_scaled.yaml -o SF12_uk_scaled -i 'livingWageIntervention' intervention_energyDownLift_uk: setup_uk_scaled - $(PYTHON) scripts/run.py -c $(CONFIG)/uk_scaled.yaml -o uk_scaled -i 'energyDownlift' + $(PYTHON) scripts/run.py -c $(CONFIG)/uk_scaled.yaml -o SF12_uk_scaled -i 'energyDownlift' intervention_energyDownLiftNoSupport_uk: setup_uk_scaled - $(PYTHON) scripts/run.py -c $(CONFIG)/uk_scaled.yaml -o uk_scaled -i 'energyDownliftNoSupport' + $(PYTHON) scripts/run.py -c $(CONFIG)/uk_scaled.yaml -o SF12_uk_scaled -i 'energyDownliftNoSupport' ##################################### From 3454ed0f531c6ef37b676fb7f14bf56c15e293ac Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 14 Nov 2023 11:57:22 +0000 Subject: [PATCH 095/229] Added make target for running uk qaly scenarios on arc, and updated path in comparison scripts to uk scaled output directory --- minos/testing/QALY_comparison_energyCrises.Rmd | 4 ++-- minos/testing/QALY_comparison_energyCrises_diff.Rmd | 4 ++-- scripts/Makefile | 4 ++++ 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/minos/testing/QALY_comparison_energyCrises.Rmd b/minos/testing/QALY_comparison_energyCrises.Rmd index 073e3060..20575e7f 100644 --- a/minos/testing/QALY_comparison_energyCrises.Rmd +++ b/minos/testing/QALY_comparison_energyCrises.Rmd @@ -40,9 +40,9 @@ source(here::here('minos', 'utils_qaly.R')) ```{r} # # read in QALY files for baseline and interventions -out.path <- here::here('output', 'default_config') +#out.path <- here::here('output', 'default_config') #out.path <- here::here('output', 'glasgow_scaled') -#out.path <- here::here('output', 'SF12_uk_scaled') +out.path <- here::here('output', 'SF12_uk_scaled') base.path <- get_latest_runtime_subdirectory(here::here(out.path, 'baseline')) support.path <- get_latest_runtime_subdirectory(here::here(out.path, 'energyDownlift')) diff --git a/minos/testing/QALY_comparison_energyCrises_diff.Rmd b/minos/testing/QALY_comparison_energyCrises_diff.Rmd index 12af542e..e07da1c4 100644 --- a/minos/testing/QALY_comparison_energyCrises_diff.Rmd +++ b/minos/testing/QALY_comparison_energyCrises_diff.Rmd @@ -40,9 +40,9 @@ source(here::here('minos', 'utils_qaly.R')) ```{r} # # read in QALY files for baseline and interventions -out.path <- here::here('output', 'default_config') +#out.path <- here::here('output', 'default_config') #out.path <- here::here('output', 'glasgow_scaled') -#out.path <- here::here('output', 'SF12_uk_scaled') +out.path <- here::here('output', 'SF12_uk_scaled') base.path <- get_latest_runtime_subdirectory(here::here(out.path, 'baseline')) support.path <- get_latest_runtime_subdirectory(here::here(out.path, 'energyDownlift')) diff --git a/scripts/Makefile b/scripts/Makefile index 493709c5..23f6df01 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -243,6 +243,10 @@ arc4_qaly_scenarios_glasgow: MODE=glasgow_scaled arc4_qaly_scenarios_glasgow: RUN_CONFIG=$(CONFIG)/glasgow_scaled.yaml arc4_qaly_scenarios_glasgow: arc4_baseline arc4_intervention_energyDownLift arc4_intervention_energyDownLiftNoSupport arc4_intervention_livingWage +arc4_qaly_scenarios_glasgow: MODE=SF12_uk_scaled +arc4_qaly_scenarios_glasgow: RUN_CONFIG=$(CONFIG)/uk_scaled.yaml +arc4_qaly_scenarios_glasgow: arc4_baseline arc4_intervention_energyDownLift arc4_intervention_energyDownLiftNoSupport arc4_intervention_livingWage + arc4_qaly_scenarios_no_replenishment: MODE=default_no_replenishment arc4_qaly_scenarios_no_replenishment: RUN_CONFIG=$(CONFIG)/default_noReplenishment.yaml arc4_qaly_scenarios_no_replenishment: arc4_baseline arc4_intervention_energyDownLift arc4_intervention_energyDownLiftNoSupport arc4_intervention_livingWage From 003d6cdd6f97bf0f7c77352c69ff2e38229da382 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 14 Nov 2023 11:58:07 +0000 Subject: [PATCH 096/229] Fixed typo in new make target --- scripts/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/Makefile b/scripts/Makefile index 23f6df01..31d17887 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -243,9 +243,9 @@ arc4_qaly_scenarios_glasgow: MODE=glasgow_scaled arc4_qaly_scenarios_glasgow: RUN_CONFIG=$(CONFIG)/glasgow_scaled.yaml arc4_qaly_scenarios_glasgow: arc4_baseline arc4_intervention_energyDownLift arc4_intervention_energyDownLiftNoSupport arc4_intervention_livingWage -arc4_qaly_scenarios_glasgow: MODE=SF12_uk_scaled -arc4_qaly_scenarios_glasgow: RUN_CONFIG=$(CONFIG)/uk_scaled.yaml -arc4_qaly_scenarios_glasgow: arc4_baseline arc4_intervention_energyDownLift arc4_intervention_energyDownLiftNoSupport arc4_intervention_livingWage +arc4_qaly_scenarios_uk: MODE=SF12_uk_scaled +arc4_qaly_scenarios_uk: RUN_CONFIG=$(CONFIG)/uk_scaled.yaml +arc4_qaly_scenarios_uk: arc4_baseline arc4_intervention_energyDownLift arc4_intervention_energyDownLiftNoSupport arc4_intervention_livingWage arc4_qaly_scenarios_no_replenishment: MODE=default_no_replenishment arc4_qaly_scenarios_no_replenishment: RUN_CONFIG=$(CONFIG)/default_noReplenishment.yaml From acd97050a68cfc8adab9a80d35a27f8240ff7fbf Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 14 Nov 2023 12:07:15 +0000 Subject: [PATCH 097/229] Added setup calls for arc4 qaly scenarios targets --- scripts/Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/Makefile b/scripts/Makefile index 31d17887..6bb45207 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -235,18 +235,22 @@ arc4_all_scenarios_S7_glasgow: arc4_all_scenarios arc4_cv_baseline: cv_setup bash scripts/arc_submit.sh -c $(CONFIG)/cross_validation.yaml -o cross_validation +arc4_qaly_scenarios: setup arc4_qaly_scenarios: MODE=default_config arc4_qaly_scenarios: RUN_CONFIG=$(CONFIG)/default.yaml arc4_qaly_scenarios: arc4_baseline arc4_intervention_energyDownLift arc4_intervention_energyDownLiftNoSupport arc4_intervention_livingWage +arc4_qaly_scenarios_glasgow: setup_glasgow_scaled arc4_qaly_scenarios_glasgow: MODE=glasgow_scaled arc4_qaly_scenarios_glasgow: RUN_CONFIG=$(CONFIG)/glasgow_scaled.yaml arc4_qaly_scenarios_glasgow: arc4_baseline arc4_intervention_energyDownLift arc4_intervention_energyDownLiftNoSupport arc4_intervention_livingWage +arc4_qaly_scenarios_uk: setup_uk_scaled arc4_qaly_scenarios_uk: MODE=SF12_uk_scaled arc4_qaly_scenarios_uk: RUN_CONFIG=$(CONFIG)/uk_scaled.yaml arc4_qaly_scenarios_uk: arc4_baseline arc4_intervention_energyDownLift arc4_intervention_energyDownLiftNoSupport arc4_intervention_livingWage +arc4_qaly_scenarios_no_replenishment: setup arc4_qaly_scenarios_no_replenishment: MODE=default_no_replenishment arc4_qaly_scenarios_no_replenishment: RUN_CONFIG=$(CONFIG)/default_noReplenishment.yaml arc4_qaly_scenarios_no_replenishment: arc4_baseline arc4_intervention_energyDownLift arc4_intervention_energyDownLiftNoSupport arc4_intervention_livingWage From b5d7ba0565cd5a7c4b759f21e59d738d8ea41568 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 14 Nov 2023 12:21:41 +0000 Subject: [PATCH 098/229] Changing dependency for replenishing pops to `transition_default` as the previous pointer to the education model doesnt match any targets (not sure how we got this far? Or why its a problem now?) --- minos/data_generation/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/minos/data_generation/Makefile b/minos/data_generation/Makefile index 441e9863..f40b70db 100644 --- a/minos/data_generation/Makefile +++ b/minos/data_generation/Makefile @@ -133,5 +133,5 @@ $(DATADIR)/replenishing/scotland_scaled/replenishing_pop_2019-2070.csv: $(SCOTLA $(UKSCALEDDATA)/2020_US_cohort.csv: $(FINALDATA)/2020_US_cohort.csv $(DATAGEN)/US_utils.py $(DATAGEN)/US_household_upscaling.py $(PYTHON) $(DATAGEN)/US_household_upscaling.py -r 'uk' -p 1 -$(DATADIR)/replenishing/uk_scaled/replenishing_pop_2019-2070.csv: $(UKSCALEDDATA)/2020_US_cohort.csv $(TRANSITION_DATA)/education_state/nnet/education_state_2018_2019.rds $(DATAGEN)/US_utils.py $(DATAGEN)/generate_repl_pop.py $(PERSISTJSON)/*.json $(MODULES)/r_utils.py +$(DATADIR)/replenishing/uk_scaled/replenishing_pop_2019-2070.csv: $(UKSCALEDDATA)/2020_US_cohort.csv transitions_default $(DATAGEN)/US_utils.py $(DATAGEN)/generate_repl_pop.py $(PERSISTJSON)/*.json $(MODULES)/r_utils.py $(PYTHON) $(DATAGEN)/generate_repl_pop.py --region 'uk' From f9b7eb344e6a99865c78fe64537b4b1c723b757c Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 14 Nov 2023 12:38:08 +0000 Subject: [PATCH 099/229] Deal with duplicate pidps in uk repl pop --- minos/data_generation/generate_repl_pop.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/minos/data_generation/generate_repl_pop.py b/minos/data_generation/generate_repl_pop.py index 4ef25057..49bb3011 100755 --- a/minos/data_generation/generate_repl_pop.py +++ b/minos/data_generation/generate_repl_pop.py @@ -88,7 +88,7 @@ def expand_repl(US_2018, region): # Luke - 20/10/23 # synthetic upscaled glasgow data results in some duplicate pidp's still # only 10 on initial testing so I'm just going to remove these - if region == "glasgow" or region == "scotland": + if region == "glasgow" or region == "scotland" or region == 'uk': expanded_repl.drop_duplicates(subset=['pidp'], inplace=True) From 6523b7f61e25fed678a60ba1acaa64cfa5a287bd Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 14 Nov 2023 14:25:43 +0000 Subject: [PATCH 100/229] Trying a 5% version of the uk population --- minos/data_generation/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/minos/data_generation/Makefile b/minos/data_generation/Makefile index f40b70db..4652455b 100644 --- a/minos/data_generation/Makefile +++ b/minos/data_generation/Makefile @@ -131,7 +131,7 @@ $(DATADIR)/replenishing/scotland_scaled/replenishing_pop_2019-2070.csv: $(SCOTLA # synthetic input for UK with spatial component. $(UKSCALEDDATA)/2020_US_cohort.csv: $(FINALDATA)/2020_US_cohort.csv $(DATAGEN)/US_utils.py $(DATAGEN)/US_household_upscaling.py - $(PYTHON) $(DATAGEN)/US_household_upscaling.py -r 'uk' -p 1 + $(PYTHON) $(DATAGEN)/US_household_upscaling.py -r 'uk' -p 5 $(DATADIR)/replenishing/uk_scaled/replenishing_pop_2019-2070.csv: $(UKSCALEDDATA)/2020_US_cohort.csv transitions_default $(DATAGEN)/US_utils.py $(DATAGEN)/generate_repl_pop.py $(PERSISTJSON)/*.json $(MODULES)/r_utils.py $(PYTHON) $(DATAGEN)/generate_repl_pop.py --region 'uk' From 470479e461011cc90bb027a822a9c2fbe16093d8 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 14 Nov 2023 16:44:49 +0000 Subject: [PATCH 101/229] Added a function to calculate the total cost of the intervention (int - baseline). Also fixed the QALY calculation as there was a small mistake (pointed to MCS instead of PCS in one leg of the calculation) --- minos/outcomes/QALY_calculation.py | 12 +- .../QALY_comparison_energyCrises_diff.Rmd | 79 +------- minos/testing/QALY_comparison_livingWage.Rmd | 186 +----------------- minos/utils_qaly.R | 24 +++ 4 files changed, 42 insertions(+), 259 deletions(-) diff --git a/minos/outcomes/QALY_calculation.py b/minos/outcomes/QALY_calculation.py index 0ece71d6..2ebceb6f 100644 --- a/minos/outcomes/QALY_calculation.py +++ b/minos/outcomes/QALY_calculation.py @@ -94,12 +94,12 @@ def calculate_qaly(df): # First calculate utility score using values table 4 from Lawrence and Fleishman (2004) df['utility'] = -1.6984 + \ - (df['SF_12_PCS'] * 0.07927) + \ - (df['SF_12_MCS'] * 0.02859) + \ - ((df['SF_12_PCS'] * df['SF_12_MCS']) * -0.000126) + \ - ((df['SF_12_PCS'] * df['SF_12_PCS']) * -0.00141) + \ - ((df['SF_12_MCS'] * df['SF_12_MCS']) * -0.00014) + \ - ((df['SF_12_PCS'] * df['SF_12_MCS'] * df['SF_12_PCS']) * 0.0000107) + (df['SF_12_PCS'] * 0.07927) + \ + (df['SF_12_MCS'] * 0.02859) + \ + ((df['SF_12_PCS'] * df['SF_12_MCS']) * -0.000126) + \ + ((df['SF_12_PCS'] * df['SF_12_PCS']) * -0.00141) + \ + ((df['SF_12_MCS'] * df['SF_12_MCS']) * -0.00014) + \ + ((df['SF_12_PCS'] * df['SF_12_PCS'] * df['SF_12_PCS']) * 0.0000107) # Now calculate QALYs by multiplying utility score by pop_size df['QALYs'] = df['utility'] * df['alive_pop'] diff --git a/minos/testing/QALY_comparison_energyCrises_diff.Rmd b/minos/testing/QALY_comparison_energyCrises_diff.Rmd index e07da1c4..2a542b63 100644 --- a/minos/testing/QALY_comparison_energyCrises_diff.Rmd +++ b/minos/testing/QALY_comparison_energyCrises_diff.Rmd @@ -200,80 +200,14 @@ ICER(base = noSupport, int.label = 'EPCG') ``` +# Total Cost of Intervention ```{r} -#ICER <- function(base, base.name, int, int.name, QALY_value) { - -base <- noSupport -base.name <- 'energyDownliftNoSupport' -int <- support -int.name <- 'energyDownlift' -QALY_value <- 60000 - - - -combined <- rbind(base, int) - -combined.cost.mean <- combined %>% - #select(-alive_pop, -SF_12_MCS, -SF_12_PCS, -utility) %>% - select(run_id, year, total_boost, intervention, QALYs) %>% - mutate(QALY_value = QALYs * QALY_value) %>% - group_by(run_id, year, intervention) %>% - summarise(across(everything(), mean)) - -# ICER = (C1 - C0) / (E1 - E0) -# where -# C1 = cost of intervention -# E1 = effect of intervention -# C0 = cost of control group -# E0 = effect of control group - -# prepare var names -c1 <- paste('total_boost', int.name, sep='_') -c0 <- paste('total_boost', base.name, sep='_') -e1 <- paste('QALYs', int.name, sep='_') -e0 <- paste('QALYs', base.name, sep='_') - -ICER <- combined.cost.mean %>% - pivot_wider(names_from = 'intervention', - values_from = c('total_boost', 'QALYs', 'QALY_value')) %>% - mutate(ICER = (.data[[c1]] - .data[[c0]]) / (.data[[e1]] - .data[[e0]])) %>% - select(run_id, year, ICER) %>% - mutate(intervention = int.name) - -pc1 <- quantile(ICER$ICER, .01) -print(pc1) -print(min(ICER$ICER)) -pc99 <- quantile(ICER$ICER, .99) -print(pc99) -print(max(ICER$ICER)) - -ICER <- filter(ICER, ICER < pc99, ICER > pc1) -print(min(ICER$ICER)) -print(max(ICER$ICER)) - -p1 <- ggplot(ICER, aes(x = year, y = ICER, group = intervention, fill = intervention, colour = intervention)) + - geom_smooth() + - scale_y_continuous(label = comma) + - labs(title = 'ICER', subtitle = paste0(int.name, ' vs ', base.name)) -print(p1) - -ICER.final <- ICER %>% - group_by(run_id, intervention) %>% - summarise(ICER = mean(ICER)) %>% - group_by(intervention) %>% - summarise(margin = (qt(0.975, df=n() - 1) * sd(ICER)) / sqrt(n()), - ICER = mean(ICER)) - -p2 <- ggplot(ICER.final, aes(x = intervention, y = ICER, group = intervention, fill = intervention)) + - geom_col() + - geom_errorbar(aes(ymin = ICER - margin, ymax = ICER + margin, width = 0.4, group = intervention)) + - scale_y_continuous(label = comma) + - labs(title = 'ICER', subtitle = paste0(int.name, ' vs ', base.name)) + - xlab('Intervention') -print(p2) - -print(paste0('ICER == ', ICER.final$ICER, ' +- ', ICER.final$margin)) +intervention.cost(base = noSupport, + base.name = 'energyDownliftNoSupport', + int = support, + int.name = 'energyDownlift', + int.label = 'EPCG') ``` @@ -286,7 +220,6 @@ print(paste0('ICER == ', ICER.final$ICER, ' +- ', ICER.final$margin)) - ```{r} ``` diff --git a/minos/testing/QALY_comparison_livingWage.Rmd b/minos/testing/QALY_comparison_livingWage.Rmd index 0c852343..7c8eb10b 100644 --- a/minos/testing/QALY_comparison_livingWage.Rmd +++ b/minos/testing/QALY_comparison_livingWage.Rmd @@ -132,67 +132,6 @@ QALY_comparison(base = base, int.name = 'livingWageIntervention') ``` -Can check histogram over each run to see the range of QALYs. - -```{r} -# # histogram of qalys -# ggplot(combined, aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + -# geom_histogram() + -# facet_wrap(~year) -# -# ggplot(filter(combined, year == 2022), aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + -# geom_histogram() + -# labs(title = 'QALYs by run', subtitle = '2022') -# -# ggplot(filter(combined, year == 2028), aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + -# geom_histogram() + -# labs(title = 'QALYs by run', subtitle = '2028') -# -# ggplot(filter(combined, year == 2034), aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + -# geom_histogram() + -# labs(title = 'QALYs by run', subtitle = '2034') -``` - -and histograms of change... - -```{r} -# # histogram of qalys -# ggplot(combined.QALY.change, aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + -# geom_histogram() + -# facet_wrap(~year) -# -# ggplot(filter(combined.QALY.change, year == 2022), aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + -# geom_histogram() + -# labs(title = 'QALY change by run', subtitle = '2022') -# -# ggplot(filter(combined.QALY.change, year == 2028), aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + -# geom_histogram() + -# labs(title = 'QALY change by run', subtitle = '2028') -# -# ggplot(filter(combined.QALY.change, year == 2034), aes(x = QALYs, group = intervention, colour = intervention, fill = intervention)) + -# geom_histogram() + -# labs(title = 'QALY change by run', subtitle = '2034') -``` - - -...and SF_12 MCS and PCS for good measure. - -```{r} -# ggplot(combined, aes(x = year, y = SF_12_MCS, group = intervention, colour = intervention)) + -# geom_smooth() -# -# ggplot(combined, aes(x = SF_12_MCS, group = intervention, colour = intervention, fill = intervention)) + -# geom_histogram() + -# facet_wrap(~year) -# -# ggplot(combined, aes(x = year, y = SF_12_PCS, group = intervention, colour = intervention)) + -# geom_smooth() -# -# ggplot(combined, aes(x = SF_12_PCS, group = intervention, colour = intervention, fill = intervention)) + -# geom_histogram() + -# facet_wrap(~year) -``` - ## SF12 Change ```{r} @@ -224,30 +163,6 @@ cost.per.qaly(base = base, int.label <- 'LivingWage') # QALY value == £60,000 ``` - -```{r} -# total.combined.cost <- combined.cost.change %>% -# group_by(run_id, intervention) %>% -# summarise(TotalChange = sum(QALY_value_change)) %>% -# group_by(intervention) %>% -# summarise(se = sd(TotalChange) / sqrt(n()), -# TotalChange = mean(TotalChange)) -# -# #print(paste0('Total combined cost == ', format(round(as.numeric(total.combined.cost), 1), nsmall=1, big.mark=","))) -# -# ggplot(total.combined.cost, aes(x = intervention, y = TotalChange, group = intervention, fill = intervention)) + -# geom_col() + -# geom_errorbar(aes(ymin = TotalChange - se, ymax = TotalChange + se, width = 0.4, group = intervention)) + -# scale_y_continuous(label = comma) + -# labs(title = 'Total Change in QALY Value', subtitle = 'Baseline vs Living Wage Intervention') + -# xlab('Intervention') -# -# ggplot(combined.cost.change, aes(x = QALY_value_change, group = intervention, colour = intervention, fill = intervention)) + -# geom_histogram() + -# facet_wrap(~year) + -# labs(title = 'QALY value change') -``` - # Incremental Cost-Effectiveness Ratio From [NICE guidelines](https://www.nice.org.uk/process/pmg6/chapter/assessing-cost-effectiveness) about the cost-effectiveness of health interventions (7.3), and works with a statistic called the Incremental Cost-Effectiveness Ratio (ICER). The formula for calculating the ICER can be found [here](https://en.wikipedia.org/wiki/Incremental_cost-effectiveness_ratio), but in short, it represents the average incremental cost associated with 1 additional unit of the measurement of effect. In our case, the unit of measurement of effect is a QALY. @@ -264,104 +179,15 @@ ICER(base = base, int.label = 'LivingWage') ``` -```{r} -#ICER <- function(base, base.name, int, int.name, QALY_value) { - -base <- base -base.name <- 'baseline' -int <- wage -int.name <- 'livingWageIntervention' -QALY_value <- 60000 -int.label <- 'LivingWage' - - -combined <- rbind(base, int) - -combined.cost.mean <- combined %>% - #select(-alive_pop, -SF_12_MCS, -SF_12_PCS, -utility) %>% - select(run_id, year, total_boost, intervention, QALYs) %>% - mutate(QALY_value = QALYs * QALY_value) %>% - group_by(run_id, year, intervention) %>% - summarise(across(everything(), mean)) - -# ICER = (C1 - C0) / (E1 - E0) -# where -# C1 = cost of intervention -# E1 = effect of intervention -# C0 = cost of control group -# E0 = effect of control group - -# prepare var names -c1 <- paste('total_boost', int.name, sep='_') -c0 <- paste('total_boost', base.name, sep='_') -e1 <- paste('QALYs', int.name, sep='_') -e0 <- paste('QALYs', base.name, sep='_') - -ICER <- combined.cost.mean %>% - pivot_wider(names_from = 'intervention', - values_from = c('total_boost', 'QALYs', 'QALY_value')) %>% - mutate(ICER = (.data[[c1]] - .data[[c0]]) / (.data[[e1]] - .data[[e0]])) %>% - select(run_id, year, ICER) %>% - mutate(intervention = int.label) - -pc1 <- quantile(ICER$ICER, .01) -print(pc1) -print(min(ICER$ICER)) -pc99 <- quantile(ICER$ICER, .99) -print(pc99) -print(max(ICER$ICER)) - -ICER <- filter(ICER, ICER < pc99, ICER > pc1) -print(min(ICER$ICER)) -print(max(ICER$ICER)) - -p1 <- ggplot(ICER, aes(x = year, y = ICER, group = intervention, fill = intervention, colour = intervention)) + - geom_smooth() + - scale_y_continuous(label = comma) + - labs(title = 'ICER', subtitle = paste0(int.name, ' vs ', base.name)) -print(p1) - -ICER.final <- ICER %>% - group_by(run_id, intervention) %>% - summarise(ICER = mean(ICER)) %>% - group_by(intervention) %>% - summarise(margin = (qt(0.975, df=n() - 1) * sd(ICER)) / sqrt(n()), - ICER = mean(ICER)) - -p2 <- ggplot(ICER.final, aes(x = intervention, y = ICER, group = intervention, fill = intervention)) + - geom_col() + - geom_errorbar(aes(ymin = ICER - margin, ymax = ICER + margin, width = 0.4, group = intervention)) + - scale_y_continuous(label = comma) + - labs(title = 'ICER', subtitle = paste0(int.name, ' vs ', base.name)) + - xlab('Intervention') -print(p2) - -print(paste0('ICER == ', ICER.final$ICER, ' +- ', ICER.final$margin)) -``` - +# Total Cost of Intervention ```{r} -# ggplot(ICER, aes(x = ICER, group = intervention, colour = intervention, fill = intervention)) + -# geom_histogram() + -# facet_wrap(~year) + -# labs(title = 'ICER by run', subtitle = 'All Years') -# -# ggplot(filter(ICER, year == 2022), aes(x = ICER, group = intervention, colour = intervention, fill = intervention)) + -# geom_histogram() + -# labs(title = 'ICER by run', subtitle = '2022') -# -# ggplot(filter(ICER, year == 2028), aes(x = ICER, group = intervention, colour = intervention, fill = intervention)) + -# geom_histogram() + -# labs(title = 'ICER by run', subtitle = '2028') -# -# ggplot(filter(ICER, year == 2031), aes(x = ICER, group = intervention, colour = intervention, fill = intervention)) + -# geom_histogram() + -# labs(title = 'ICER by run', subtitle = '2031') -# -# ggplot(filter(ICER, year == 2034), aes(x = ICER, group = intervention, colour = intervention, fill = intervention)) + -# geom_histogram() + -# labs(title = 'ICER by run', subtitle = '2034') +intervention.cost(base = baseline, + base.name = 'baseline', + int = livWage, + int.name = 'livingWageIntervention', + int.label = 'Living Wage') ``` diff --git a/minos/utils_qaly.R b/minos/utils_qaly.R index 9bcaad98..ebdfdd78 100644 --- a/minos/utils_qaly.R +++ b/minos/utils_qaly.R @@ -253,3 +253,27 @@ ICER <- function(base, base.name, int, int.name, QALY_value, int.label) { print(paste0('ICER == ', ICER.final$ICER, ' +- ', ICER.final$margin)) } +intervention.cost <- function(base, base.name, int, int.name, int.label) { + combined <- rbind(base, int) + + total.cost <- combined %>% + select(run_id, year, intervention, total_boost) %>% + group_by(run_id, intervention) %>% + summarise(total_cost = sum(total_boost)) %>% + pivot_wider(names_from = 'intervention', + values_from = 'total_cost') %>% + mutate(cost_difference = .data[[int.name]] - .data[[base.name]]) %>% + select(run_id, cost_difference) %>% + mutate(intervention = int.name) %>% + group_by(intervention) %>% + summarise(margin = (qt(0.975, df=n() - 1) * sd(cost_difference)) / sqrt(n()), + cost_difference = mean(cost_difference)) + + p1 <- ggplot(total.cost, aes(x = intervention, y = cost_difference, group = intervention, fill = intervention, colour = intervention)) + + geom_col() + + geom_errorbar(aes(ymin = cost_difference - margin, ymax = cost_difference + margin, width = 0.4, group = intervention)) + + labs(title = 'Total cost of intervention', subtitle = paste0(int.name, ' vs ', base.name)) + print(p1) + + print(paste("Total cost of the intervention over the full simulation (2020-2035) ==", total.cost$cost_difference, sep = ' ')) +} From 9cb77ff51211e416adbcff13343a761afee13b8d Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Thu, 16 Nov 2023 15:54:41 +0000 Subject: [PATCH 102/229] Fixed some typos in living wage QALY comparison script --- minos/testing/QALY_comparison_livingWage.Rmd | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/minos/testing/QALY_comparison_livingWage.Rmd b/minos/testing/QALY_comparison_livingWage.Rmd index 7c8eb10b..92884d0d 100644 --- a/minos/testing/QALY_comparison_livingWage.Rmd +++ b/minos/testing/QALY_comparison_livingWage.Rmd @@ -183,11 +183,11 @@ ICER(base = base, # Total Cost of Intervention ```{r} -intervention.cost(base = baseline, +intervention.cost(base = base, base.name = 'baseline', - int = livWage, + int = wage, int.name = 'livingWageIntervention', - int.label = 'Living Wage') + int.label = 'LivingWage') ``` From b400b4be071f041c077bbe9adae936508d5fbb11 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 20 Nov 2023 11:51:56 +0000 Subject: [PATCH 103/229] Wrote a generic version of the QALY_comparison.Rmd notebook, where key parameters such as baseline and intervention data are set by arguments to the render call in outcomes/Makefile --- minos/outcomes/Makefile | 150 ++++++++++--- minos/outcomes/QALY_comparison.Rmd | 208 ++++++++++++++++++ .../QALY_comparison_energyCrises_diff.Rmd | 63 ++---- 3 files changed, 345 insertions(+), 76 deletions(-) create mode 100644 minos/outcomes/QALY_comparison.Rmd diff --git a/minos/outcomes/Makefile b/minos/outcomes/Makefile index e5d78fd4..1a332fe3 100644 --- a/minos/outcomes/Makefile +++ b/minos/outcomes/Makefile @@ -34,22 +34,46 @@ QALY_livingWage: INTERVENTION=livingWageIntervention QALY_livingWage: python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) -QALY_comparison_energyCrises: QALY_baseline QALY_energyDownlift QALY_energyDownliftNoSupport - $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises.Rmd')" - firefox file://$(TESTING)/QALY_comparison_energyCrises.html - -QALY_comparison_energyCrises_diff: QALY_energyDownlift QALY_energyDownliftNoSupport - $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises_diff.Rmd')" - firefox file://$(TESTING)/QALY_comparison_energyCrises_diff.html +################## GENERIC QALY SCRIPT ################## +QALY_comparison: + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison.Rmd', params = list( \ + experiment = $(EXPERIMENT), \ + baseline = $(BASELINE), \ + intervention = $(INTERVENTION), \ + intervention_label = $(INTERVENTION_LABEL) \ + ))" + #firefox file://$(TESTING)/QALY_comparison_livingWage.html + +QALY_comparison_energyCrises: QALY_energyDownlift QALY_energyDownliftNoSupport +QALY_comparison_energyCrises: EXPERIMENT=default_config +QALY_comparison_energyCrises: BASELINE=energyDownliftNoSupport +QALY_comparison_energyCrises: INTERVENTION=energyDownlift +QALY_comparison_energyCrises: INTERVENTION_LABEL=EPCG +QALY_comparison_energyCrises: + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison.Rmd', params = list( \ + experiment = $(EXPERIMENT), \ + baseline = $(BASELINE), \ + intervention = $(INTERVENTION), \ + intervention_label = $(INTERVENTION_LABEL) \ + ))" QALY_comparison_livingWage: QALY_baseline QALY_livingWage - $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" - firefox file://$(TESTING)/QALY_comparison_livingWage.html +QALY_comparison_livingWage: EXPERIMENT=default_config +QALY_comparison_livingWage: BASELINE=energyDownliftNoSupport +QALY_comparison_livingWage: INTERVENTION=energyDownlift +QALY_comparison_livingWage: INTERVENTION_LABEL=EPCG +QALY_comparison_livingWage: + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison_livingWage.Rmd', params = list( \ + experiment = $(EXPERIMENT), \ + baseline = $(BASELINE), \ + intervention = $(INTERVENTION), \ + intervention_label = $(INTERVENTION_LABEL) \ + ))" + -QALY_comparisons: QALY_baseline QALY_energyDownlift QALY_energyDownliftNoSupport QALY_livingWage - $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises.Rmd')" - $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" - $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises_diff.Rmd')" +QALY_comparisons: QALY_comparison_livingWage QALY_comparison_energyCrises + +################## ^ GENERIC QALY SCRIPT ^ ################## ## No Replenishment QALYs (cohort) @@ -73,18 +97,34 @@ QALY_livingWage_norepl: INTERVENTION=livingWageIntervention QALY_livingWage_norepl: python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) -QALY_comparison_energyCrises_norepl: QALY_baseline_norepl QALY_energyDownlift_norepl QALY_energyDownliftNoSupport_norepl - $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises.Rmd')" - firefox file://$(TESTING)/QALY_comparison_energyCrises.html +QALY_comparison_energyCrises_norepl: QALY_energyDownlift_norepl QALY_energyDownliftNoSupport_norepl +QALY_comparison_energyCrises_norepl: EXPERIMENT=default_no_replenishment +QALY_comparison_energyCrises_norepl: BASELINE=energyDownliftNoSupport +QALY_comparison_energyCrises_norepl: INTERVENTION=energyDownlift +QALY_comparison_energyCrises_norepl: INTERVENTION_LABEL=EPCG +QALY_comparison_energyCrises_norepl: + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison.Rmd', params = list( \ + experiment = $(EXPERIMENT), \ + baseline = $(BASELINE), \ + intervention = $(INTERVENTION), \ + intervention_label = $(INTERVENTION_LABEL) \ + ))" QALY_comparison_livingWage_norepl: QALY_baseline_norepl QALY_livingWage_norepl - $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" - firefox file://$(TESTING)/QALY_comparison_livingWage.html +QALY_comparison_livingWage_norepl: EXPERIMENT=default_no_replenishment +QALY_comparison_livingWage_norepl: BASELINE=energyDownliftNoSupport +QALY_comparison_livingWage_norepl: INTERVENTION=energyDownlift +QALY_comparison_livingWage_norepl: INTERVENTION_LABEL=EPCG +QALY_comparison_livingWage_norepl: + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison.Rmd', params = list( \ + experiment = $(EXPERIMENT), \ + baseline = $(BASELINE), \ + intervention = $(INTERVENTION), \ + intervention_label = $(INTERVENTION_LABEL) \ + ))" + -QALY_comparisons_norepl: QALY_baseline_norepl QALY_energyDownlift_norepl QALY_energyDownliftNoSupport_norepl QALY_livingWage_norepl - $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises.Rmd')" - $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" - $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises_diff.Rmd')" +QALY_comparisons_norepl: QALY_comparison_livingWage_norepl QALY_comparison_energyCrises_norepl ## Glasgow QALYs @@ -108,10 +148,34 @@ QALY_livwage_glasgow: INTERVENTION=livingWageIntervention QALY_livwage_glasgow: python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) -QALYs_glasgow: QALY_baseline_glasgow QALY_energysupport_glasgow QALY_energynosupport_glasgow QALY_livwage_glasgow - $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises.Rmd')" - $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises_diff.Rmd')" - $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" +QALY_comparison_energyCrises_glasgow: QALY_energysupport_glasgow QALY_energynosupport_glasgow +QALY_comparison_energyCrises_glasgow: EXPERIMENT=glasgow_scaled +QALY_comparison_energyCrises_glasgow: BASELINE=energyDownliftNoSupport +QALY_comparison_energyCrises_glasgow: INTERVENTION=energyDownlift +QALY_comparison_energyCrises_glasgow: INTERVENTION_LABEL=EPCG +QALY_comparison_energyCrises_glasgow: + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison.Rmd', params = list( \ + experiment = $(EXPERIMENT), \ + baseline = $(BASELINE), \ + intervention = $(INTERVENTION), \ + intervention_label = $(INTERVENTION_LABEL) \ + ))" + +QALY_comparison_livingWage_glasgow: QALY_baseline_glasgow QALY_livwage_glasgow +QALY_comparison_livingWage_glasgow: EXPERIMENT=glasgow_scaled +QALY_comparison_livingWage_glasgow: BASELINE=energyDownliftNoSupport +QALY_comparison_livingWage_glasgow: INTERVENTION=energyDownlift +QALY_comparison_livingWage_glasgow: INTERVENTION_LABEL=EPCG +QALY_comparison_livingWage_glasgow: + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison.Rmd', params = list( \ + experiment = $(EXPERIMENT), \ + baseline = $(BASELINE), \ + intervention = $(INTERVENTION), \ + intervention_label = $(INTERVENTION_LABEL) \ + ))" + + +QALY_comparisons_glasgow: QALY_comparison_livingWage_glasgow QALY_comparison_energyCrises_glasgow ## UK scale QALYs @@ -135,14 +199,36 @@ QALY_livwage_uk: INTERVENTION=livingWageIntervention QALY_livwage_uk: python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) -QALYs_uk: QALY_baseline_uk QALY_energysupport_uk QALY_energynosupport_uk QALY_livwage_uk - $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises.Rmd')" - $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises_diff.Rmd')" - $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" +QALY_comparison_energyCrises_uk: QALY_energysupport_uk QALY_energynosupport_uk +QALY_comparison_energyCrises_uk: EXPERIMENT=glasgow_scaled +QALY_comparison_energyCrises_uk: BASELINE=energyDownliftNoSupport +QALY_comparison_energyCrises_uk: INTERVENTION=energyDownlift +QALY_comparison_energyCrises_uk: INTERVENTION_LABEL=EPCG +QALY_comparison_energyCrises_uk: + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison.Rmd', params = list( \ + experiment = $(EXPERIMENT), \ + baseline = $(BASELINE), \ + intervention = $(INTERVENTION), \ + intervention_label = $(INTERVENTION_LABEL) \ + ))" QALY_comparison_livingWage_uk: QALY_baseline_uk QALY_livwage_uk - $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" - firefox file://$(TESTING)/QALY_comparison_livingWage.html +QALY_comparison_livingWage_uk: EXPERIMENT=glasgow_scaled +QALY_comparison_livingWage_uk: BASELINE=energyDownliftNoSupport +QALY_comparison_livingWage_uk: INTERVENTION=energyDownlift +QALY_comparison_livingWage_uk: INTERVENTION_LABEL=EPCG +QALY_comparison_livingWage_uk: + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison.Rmd', params = list( \ + experiment = $(EXPERIMENT), \ + baseline = $(BASELINE), \ + intervention = $(INTERVENTION), \ + intervention_label = $(INTERVENTION_LABEL) \ + ))" + + +QALY_comparisons_uk: QALY_comparison_livingWage_uk QALY_comparison_energyCrises_uk + + ##################################### diff --git a/minos/outcomes/QALY_comparison.Rmd b/minos/outcomes/QALY_comparison.Rmd new file mode 100644 index 00000000..d8358293 --- /dev/null +++ b/minos/outcomes/QALY_comparison.Rmd @@ -0,0 +1,208 @@ +--- +title: "QALY Comparisons - Energy Crisis Interventions - NoSupport Baseline" +output: + html_document: + toc: true + toc_float: true + collapsed: false + number_sections: true + df_print: paged + code_folding: hide +params: + experiment: experiment + baseline: baseline + intervention: intervention + intervention_label: intervention_label +--- + +# SETUP + +```{r "setup", include=FALSE} + +require(tidyverse) +require(ggplot2) +require(knitr) +require(here) +require(MESS) +require(scales) + +#workingDir <- "/home/luke/Documents/WORK/MINOS/" +workingDir <- normalizePath('../') +knitr::opts_knit$set(root.dir = workingDir) +knitr::opts_chunk$set( + options(scipen = 999) +) +rm(workingDir) +``` + +```{r} +source(here::here('minos', 'utils_datain.R')) +source(here::here('minos', 'utils_qaly.R')) +``` + + +## Data + +```{r read_data, include=False} +experiment <- params$experiment +baseline <- params$baseline +intervention <- params$intervention +intervention_label <- params$intervention_label + +out.path <- here::here('output', args$experiment) + +base.path <- get_latest_runtime_subdirectory(here::here(out.path, baseline)) +int.path <- get_latest_runtime_subdirectory(here::here(out.path, intervention)) + +base <- read.csv(paste0(base.path, 'qalys.csv')) +int <- read.csv(paste0(int.path, 'qalys.csv')) + +combined <- rbind(base, int) +``` + + +```{r} +# # read in QALY files for baseline and interventions +#out.path <- here::here('output', 'default_config') +#out.path <- here::here('output', 'glasgow_scaled') +out.path <- here::here('output', 'SF12_uk_scaled') + +base.path <- get_latest_runtime_subdirectory(here::here(out.path, 'baseline')) +support.path <- get_latest_runtime_subdirectory(here::here(out.path, 'energyDownlift')) +noSupport.path <- get_latest_runtime_subdirectory(here::here(out.path, 'energyDownliftNoSupport')) + +base <- read.csv(paste0(base.path, 'qalys.csv')) +support <- read.csv(paste0(support.path, 'qalys.csv')) +noSupport <- read.csv(paste0(noSupport.path, 'qalys.csv')) + +# out.path <- '/home/luke/Documents/WORK/MINOS/PLOTS_FROM_ARC/QALY_TESTING/' +# +# base <- read.csv(paste0(out.path, 'qalys_baseline.csv')) +# support <- read.csv(paste0(out.path, 'qalys_support.csv')) +# noSupport <- read.csv(paste0(out.path, 'qalys_nosupport.csv')) + +# combine the dataframes +combined <- rbind(support, noSupport) +``` + +# Intervention Information + +Just going to plot some things here like how many people affected by an intervention and what the mean / total boost amounts look like. + +```{r} +ggplot(combined, aes(x = year, y = pop_boosted, group = intervention, colour = intervention)) + + geom_smooth() + + labs(title = 'Number Individuals Receiving Intervention') + +combined.diff <- combined %>% + select(run_id, year, intervention, total_boost) %>% + pivot_wider(names_from = 'intervention', + values_from = 'total_boost') %>% + mutate(diff.boost = energyDownlift - energyDownliftNoSupport) + +ggplot(combined.diff, aes(x = year, y = diff.boost)) + + geom_smooth() + + labs(title = 'Total Boost amount by year') + + scale_y_continuous(label = comma) + +ggplot(combined, aes(x = year, y = alive_ratio, group = intervention, colour = intervention)) + + geom_smooth() + + labs(title = 'Ratio of living to dead in output dataframe') + +## incidence of mortality +deaths <- combined %>% + select(run_id, intervention, year, alive_pop, dead_pop) %>% + group_by(run_id, intervention) %>% + mutate(total_pop = alive_pop + dead_pop, + deaths_this_year = dead_pop - lag(dead_pop)) %>% + group_by(run_id, year, intervention) %>% + summarise(death_incidence = deaths_this_year / total_pop) + +ggplot(deaths, aes(x = year, y = death_incidence, group = intervention, colour = intervention)) + + geom_smooth() + + labs(title = 'Incidence of Mortality Comparison') +``` + +# QALY comparison + +```{r} +QALY_comparison(base = base, + base.name = baseline, + int = int, + int.name = intervention) +``` + +## SF12 Change + +```{r} +sf12.plots(base = base, + base.name = baseline, + int = int, + int.name = intervention) +``` + + +## Area Under the Curve + +```{r} +auc.plots(base = base, + base.name = baseline, + int = int, + int.name = intervention) +``` + +# Cost per QALY + +The [UK Government](https://www.gov.uk/government/publications/valuation-of-risks-to-life-and-health-monetary-value-of-a-life-year-voly/a-scoping-study-on-the-valuation-of-risks-to-life-and-health-the-monetary-value-of-a-life-year-voly) has set the value of 1 QALY to be equal to $60,000. This is the number we will use for our calculations. + +```{r} +cost.per.qaly(base = base, + base.name = baseline, + int = int, + int.name = intervention, + QALY_value = 60000, # QALY value == £60,000 + int.label <- intervention_label) +``` + +# Incremental Cost-Effectiveness Ratio + +From [NICE guidelines](https://www.nice.org.uk/process/pmg6/chapter/assessing-cost-effectiveness) about the cost-effectiveness of health interventions (7.3), and works with a statistic called the Incremental Cost-Effectiveness Ratio (ICER). The formula for calculating the ICER can be found [here](https://en.wikipedia.org/wiki/Incremental_cost-effectiveness_ratio), but in short, it represents the average incremental cost associated with 1 additional unit of the measurement of effect. In our case, the unit of measurement of effect is a QALY. + +From the NICE guidelines, there is a general consensus that anything below £20,000 is cost effective, and anything above £30,000 is not. Between £20,000 and £30,000 is a grey area, and depends on the field and other factors to decide (i.e. if a rare or important condition is tackled by an intervention then the cost-effective threshold is higher). + +```{r} +ICER(base = base, + base.name = baseline, + int = int, + int.name = intervention, + QALY_value = 60000, + int.label = intervention_label) +``` + +# Total Cost of Intervention + +```{r} +intervention.cost(base = base, + base.name = baseline, + int = int, + int.name = intervention, + int.label = intervention_label) +``` + + + + + + + + + + + +```{r} + +``` + + + + diff --git a/minos/testing/QALY_comparison_energyCrises_diff.Rmd b/minos/testing/QALY_comparison_energyCrises_diff.Rmd index 2a542b63..2e6ca5f0 100644 --- a/minos/testing/QALY_comparison_energyCrises_diff.Rmd +++ b/minos/testing/QALY_comparison_energyCrises_diff.Rmd @@ -8,6 +8,10 @@ output: number_sections: true df_print: paged code_folding: hide +params: + experiment: experiment + baseline: baseline + intervention: intervention --- # SETUP @@ -38,6 +42,21 @@ source(here::here('minos', 'utils_qaly.R')) ## Data +```{r read_data, include=False} +experiment <- params$experiment +baseline <- params$baseline +intervention <- params$intervention + +out.path <- here::here('output', args$experiment) + +base.path <- get_latest_runtime_subdirectory(here::here(out.path, baseline)) +int.path <- get_latest_runtime_subdirectory(here::here(out.path, intervention)) + +base <- read.csv(paste0(base.path, 'qalys.csv')) +int <- read.csv(paste0(int.path, 'qalys.csv')) +``` + + ```{r} # # read in QALY files for baseline and interventions #out.path <- here::here('output', 'default_config') @@ -118,50 +137,6 @@ sf12.plots(base = noSupport, int.name = 'energyDownlift') ``` -```{r} -# base <- noSupport -# base.name <- 'energyDownliftNoSupport' -# int <- support -# int.name <- 'energyDownlift' -# -# combined <- rbind(base, int) -# -# ## now SF12 plots for comparison -# p1 <- ggplot(combined, aes(x = year, y = SF_12_MCS, group = intervention, colour = intervention, fill = intervention)) + -# geom_smooth() -# p2 <- ggplot(combined, aes(x = year, y = SF_12_PCS, group = intervention, colour = intervention, fill = intervention)) + -# geom_smooth() -# -# print(p1) -# print(p2) -# -# combined.SF12 <- combined %>% -# select(run_id, year, intervention, SF_12_MCS, SF_12_PCS) %>% -# pivot_wider(names_from = 'intervention', -# values_from = c('SF_12_MCS', 'SF_12_PCS')) %>% -# mutate(MCS_diff = .data[[paste0('SF_12_MCS_', int.name)]] - .data[[paste0('SF_12_MCS_', base.name)]], -# PCS_diff = .data[[paste0('SF_12_PCS_', int.name)]] - .data[[paste0('SF_12_PCS_', base.name)]]) %>% -# select(run_id, year, MCS_diff, PCS_diff) -# -# p3 <- ggplot(combined.SF12, aes(x = year, y = MCS_diff)) + -# geom_smooth() + -# geom_hline(yintercept = 0, linetype = 'dashed') + -# labs(title = 'Change in SF_12_MCS', subtitle = paste0(int.name, ' vs ', base.name)) + -# xlab('Year') + -# ylab('Change in MCS') -# -# p4 <- ggplot(combined.SF12, aes(x = year, y = PCS_diff)) + -# geom_smooth() + -# geom_hline(yintercept = 0, linetype = 'dashed') + -# labs(title = 'Change in SF_12_PCS', subtitle = paste0(int.name, ' vs ', base.name)) + -# xlab('Year') + -# ylab('Change in MCS') -# -# print(p3) -# print(p4) - -``` - ## Area Under the Curve From 63f511e99edf83e4bbc07c33c973198d62ef03bb Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 20 Nov 2023 15:07:41 +0000 Subject: [PATCH 104/229] Changing back to individual scripts and make targets for now as couldnt get the params argument to RScript render working from Makefile --- minos/outcomes/Makefile | 143 ++++++----------------------- minos/outcomes/QALY_comparison.Rmd | 12 ++- 2 files changed, 36 insertions(+), 119 deletions(-) diff --git a/minos/outcomes/Makefile b/minos/outcomes/Makefile index 1a332fe3..3bea29ca 100644 --- a/minos/outcomes/Makefile +++ b/minos/outcomes/Makefile @@ -34,41 +34,14 @@ QALY_livingWage: INTERVENTION=livingWageIntervention QALY_livingWage: python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) -################## GENERIC QALY SCRIPT ################## -QALY_comparison: - $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison.Rmd', params = list( \ - experiment = $(EXPERIMENT), \ - baseline = $(BASELINE), \ - intervention = $(INTERVENTION), \ - intervention_label = $(INTERVENTION_LABEL) \ - ))" - #firefox file://$(TESTING)/QALY_comparison_livingWage.html - -QALY_comparison_energyCrises: QALY_energyDownlift QALY_energyDownliftNoSupport -QALY_comparison_energyCrises: EXPERIMENT=default_config -QALY_comparison_energyCrises: BASELINE=energyDownliftNoSupport -QALY_comparison_energyCrises: INTERVENTION=energyDownlift -QALY_comparison_energyCrises: INTERVENTION_LABEL=EPCG -QALY_comparison_energyCrises: - $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison.Rmd', params = list( \ - experiment = $(EXPERIMENT), \ - baseline = $(BASELINE), \ - intervention = $(INTERVENTION), \ - intervention_label = $(INTERVENTION_LABEL) \ - ))" - -QALY_comparison_livingWage: QALY_baseline QALY_livingWage -QALY_comparison_livingWage: EXPERIMENT=default_config -QALY_comparison_livingWage: BASELINE=energyDownliftNoSupport -QALY_comparison_livingWage: INTERVENTION=energyDownlift -QALY_comparison_livingWage: INTERVENTION_LABEL=EPCG -QALY_comparison_livingWage: - $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison_livingWage.Rmd', params = list( \ - experiment = $(EXPERIMENT), \ - baseline = $(BASELINE), \ - intervention = $(INTERVENTION), \ - intervention_label = $(INTERVENTION_LABEL) \ - ))" +QALYs: QALY_baseline QALY_energysupport QALY_energynosupport QALY_livwage + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises.Rmd')" + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises_diff.Rmd')" + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" + +QALY_comparison_livingWage: QALY_baseline QALY_livwage + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" + firefox file://$(TESTING)/QALY_comparison_livingWage.html QALY_comparisons: QALY_comparison_livingWage QALY_comparison_energyCrises @@ -97,34 +70,14 @@ QALY_livingWage_norepl: INTERVENTION=livingWageIntervention QALY_livingWage_norepl: python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) -QALY_comparison_energyCrises_norepl: QALY_energyDownlift_norepl QALY_energyDownliftNoSupport_norepl -QALY_comparison_energyCrises_norepl: EXPERIMENT=default_no_replenishment -QALY_comparison_energyCrises_norepl: BASELINE=energyDownliftNoSupport -QALY_comparison_energyCrises_norepl: INTERVENTION=energyDownlift -QALY_comparison_energyCrises_norepl: INTERVENTION_LABEL=EPCG -QALY_comparison_energyCrises_norepl: - $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison.Rmd', params = list( \ - experiment = $(EXPERIMENT), \ - baseline = $(BASELINE), \ - intervention = $(INTERVENTION), \ - intervention_label = $(INTERVENTION_LABEL) \ - ))" - -QALY_comparison_livingWage_norepl: QALY_baseline_norepl QALY_livingWage_norepl -QALY_comparison_livingWage_norepl: EXPERIMENT=default_no_replenishment -QALY_comparison_livingWage_norepl: BASELINE=energyDownliftNoSupport -QALY_comparison_livingWage_norepl: INTERVENTION=energyDownlift -QALY_comparison_livingWage_norepl: INTERVENTION_LABEL=EPCG -QALY_comparison_livingWage_norepl: - $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison.Rmd', params = list( \ - experiment = $(EXPERIMENT), \ - baseline = $(BASELINE), \ - intervention = $(INTERVENTION), \ - intervention_label = $(INTERVENTION_LABEL) \ - ))" - - -QALY_comparisons_norepl: QALY_comparison_livingWage_norepl QALY_comparison_energyCrises_norepl +QALYs_norepl: QALY_baseline_norepl QALY_energysupport_norepl QALY_energynosupport_norepl QALY_livwage_norepl + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises.Rmd')" + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises_diff.Rmd')" + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" + +QALY_comparison_livingWage_norepl: QALY_baseline_norepl QALY_livwage_norepl + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" + firefox file://$(TESTING)/QALY_comparison_livingWage.html ## Glasgow QALYs @@ -148,34 +101,14 @@ QALY_livwage_glasgow: INTERVENTION=livingWageIntervention QALY_livwage_glasgow: python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) -QALY_comparison_energyCrises_glasgow: QALY_energysupport_glasgow QALY_energynosupport_glasgow -QALY_comparison_energyCrises_glasgow: EXPERIMENT=glasgow_scaled -QALY_comparison_energyCrises_glasgow: BASELINE=energyDownliftNoSupport -QALY_comparison_energyCrises_glasgow: INTERVENTION=energyDownlift -QALY_comparison_energyCrises_glasgow: INTERVENTION_LABEL=EPCG -QALY_comparison_energyCrises_glasgow: - $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison.Rmd', params = list( \ - experiment = $(EXPERIMENT), \ - baseline = $(BASELINE), \ - intervention = $(INTERVENTION), \ - intervention_label = $(INTERVENTION_LABEL) \ - ))" +QALYs_glasgow: QALY_baseline_glasgow QALY_energysupport_glasgow QALY_energynosupport_glasgow QALY_livwage_glasgow + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises.Rmd')" + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises_diff.Rmd')" + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" QALY_comparison_livingWage_glasgow: QALY_baseline_glasgow QALY_livwage_glasgow -QALY_comparison_livingWage_glasgow: EXPERIMENT=glasgow_scaled -QALY_comparison_livingWage_glasgow: BASELINE=energyDownliftNoSupport -QALY_comparison_livingWage_glasgow: INTERVENTION=energyDownlift -QALY_comparison_livingWage_glasgow: INTERVENTION_LABEL=EPCG -QALY_comparison_livingWage_glasgow: - $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison.Rmd', params = list( \ - experiment = $(EXPERIMENT), \ - baseline = $(BASELINE), \ - intervention = $(INTERVENTION), \ - intervention_label = $(INTERVENTION_LABEL) \ - ))" - - -QALY_comparisons_glasgow: QALY_comparison_livingWage_glasgow QALY_comparison_energyCrises_glasgow + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" + firefox file://$(TESTING)/QALY_comparison_livingWage.html ## UK scale QALYs @@ -199,34 +132,14 @@ QALY_livwage_uk: INTERVENTION=livingWageIntervention QALY_livwage_uk: python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) -QALY_comparison_energyCrises_uk: QALY_energysupport_uk QALY_energynosupport_uk -QALY_comparison_energyCrises_uk: EXPERIMENT=glasgow_scaled -QALY_comparison_energyCrises_uk: BASELINE=energyDownliftNoSupport -QALY_comparison_energyCrises_uk: INTERVENTION=energyDownlift -QALY_comparison_energyCrises_uk: INTERVENTION_LABEL=EPCG -QALY_comparison_energyCrises_uk: - $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison.Rmd', params = list( \ - experiment = $(EXPERIMENT), \ - baseline = $(BASELINE), \ - intervention = $(INTERVENTION), \ - intervention_label = $(INTERVENTION_LABEL) \ - ))" +QALYs_uk: QALY_baseline_uk QALY_energysupport_uk QALY_energynosupport_uk QALY_livwage_uk + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises.Rmd')" + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises_diff.Rmd')" + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" QALY_comparison_livingWage_uk: QALY_baseline_uk QALY_livwage_uk -QALY_comparison_livingWage_uk: EXPERIMENT=glasgow_scaled -QALY_comparison_livingWage_uk: BASELINE=energyDownliftNoSupport -QALY_comparison_livingWage_uk: INTERVENTION=energyDownlift -QALY_comparison_livingWage_uk: INTERVENTION_LABEL=EPCG -QALY_comparison_livingWage_uk: - $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison.Rmd', params = list( \ - experiment = $(EXPERIMENT), \ - baseline = $(BASELINE), \ - intervention = $(INTERVENTION), \ - intervention_label = $(INTERVENTION_LABEL) \ - ))" - - -QALY_comparisons_uk: QALY_comparison_livingWage_uk QALY_comparison_energyCrises_uk + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" + firefox file://$(TESTING)/QALY_comparison_livingWage.html diff --git a/minos/outcomes/QALY_comparison.Rmd b/minos/outcomes/QALY_comparison.Rmd index d8358293..8c2bd2c7 100644 --- a/minos/outcomes/QALY_comparison.Rmd +++ b/minos/outcomes/QALY_comparison.Rmd @@ -9,10 +9,14 @@ output: df_print: paged code_folding: hide params: - experiment: experiment - baseline: baseline - intervention: intervention - intervention_label: intervention_label + experiment: + value: experiment + baseline: + value: baseline + intervention: + value: intervention + intervention_label: + value: intervention_label --- # SETUP From 942162bc2abb2dfad05bd36bf9d1709d13d5dfdc Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 20 Nov 2023 15:40:15 +0000 Subject: [PATCH 105/229] Fix issue in QALY_comparison script where something was left in that caused a problem --- minos/outcomes/Makefile | 4 ---- .../QALY_comparison_energyCrises_diff.Rmd | 19 ------------------- 2 files changed, 23 deletions(-) diff --git a/minos/outcomes/Makefile b/minos/outcomes/Makefile index 3bea29ca..2d3133f8 100644 --- a/minos/outcomes/Makefile +++ b/minos/outcomes/Makefile @@ -35,7 +35,6 @@ QALY_livingWage: python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) QALYs: QALY_baseline QALY_energysupport QALY_energynosupport QALY_livwage - $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises.Rmd')" $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises_diff.Rmd')" $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" @@ -71,7 +70,6 @@ QALY_livingWage_norepl: python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) QALYs_norepl: QALY_baseline_norepl QALY_energysupport_norepl QALY_energynosupport_norepl QALY_livwage_norepl - $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises.Rmd')" $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises_diff.Rmd')" $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" @@ -102,7 +100,6 @@ QALY_livwage_glasgow: python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) QALYs_glasgow: QALY_baseline_glasgow QALY_energysupport_glasgow QALY_energynosupport_glasgow QALY_livwage_glasgow - $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises.Rmd')" $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises_diff.Rmd')" $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" @@ -133,7 +130,6 @@ QALY_livwage_uk: python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) QALYs_uk: QALY_baseline_uk QALY_energysupport_uk QALY_energynosupport_uk QALY_livwage_uk - $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises.Rmd')" $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises_diff.Rmd')" $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" diff --git a/minos/testing/QALY_comparison_energyCrises_diff.Rmd b/minos/testing/QALY_comparison_energyCrises_diff.Rmd index 2e6ca5f0..6dfba142 100644 --- a/minos/testing/QALY_comparison_energyCrises_diff.Rmd +++ b/minos/testing/QALY_comparison_energyCrises_diff.Rmd @@ -8,10 +8,6 @@ output: number_sections: true df_print: paged code_folding: hide -params: - experiment: experiment - baseline: baseline - intervention: intervention --- # SETUP @@ -42,21 +38,6 @@ source(here::here('minos', 'utils_qaly.R')) ## Data -```{r read_data, include=False} -experiment <- params$experiment -baseline <- params$baseline -intervention <- params$intervention - -out.path <- here::here('output', args$experiment) - -base.path <- get_latest_runtime_subdirectory(here::here(out.path, baseline)) -int.path <- get_latest_runtime_subdirectory(here::here(out.path, intervention)) - -base <- read.csv(paste0(base.path, 'qalys.csv')) -int <- read.csv(paste0(int.path, 'qalys.csv')) -``` - - ```{r} # # read in QALY files for baseline and interventions #out.path <- here::here('output', 'default_config') From d64c6335fc1fcdd03af9a4690abc8dfcbe2d87e1 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Thu, 23 Nov 2023 17:31:03 +0000 Subject: [PATCH 106/229] Added dependency to household upscaling, and some WIP stuff on the msm model for chron_disease --- minos/data_generation/Makefile | 4 +- .../data_generation/US_household_upscaling.py | 2 +- minos/modules/alcohol.py | 2 - .../estimate_longitudinal_transitions.R | 16 ++++- .../transitions/transition_model_functions.R | 34 +++++++-- minos/utils_qaly.R | 2 +- minos/validation/handovers.Rmd | 69 ++++++++++++++----- 7 files changed, 98 insertions(+), 31 deletions(-) diff --git a/minos/data_generation/Makefile b/minos/data_generation/Makefile index 4652455b..7ba57134 100644 --- a/minos/data_generation/Makefile +++ b/minos/data_generation/Makefile @@ -130,8 +130,8 @@ $(DATADIR)/replenishing/scotland_scaled/replenishing_pop_2019-2070.csv: $(SCOTLA $(PYTHON) $(DATAGEN)/generate_repl_pop.py --region 'scotland' # synthetic input for UK with spatial component. -$(UKSCALEDDATA)/2020_US_cohort.csv: $(FINALDATA)/2020_US_cohort.csv $(DATAGEN)/US_utils.py $(DATAGEN)/US_household_upscaling.py - $(PYTHON) $(DATAGEN)/US_household_upscaling.py -r 'uk' -p 5 +$(UKSCALEDDATA)/2020_US_cohort.csv: $(FINALDATA)/2020_US_cohort.csv $(DATAGEN)/US_utils.py $(DATAGEN)/US_household_upscaling.py $(PERSISTJSON)/HH2011PopEst2020UK_population_UK.csv + $(PYTHON) $(DATAGEN)/US_household_upscaling.py -r 'uk' -p 1 $(DATADIR)/replenishing/uk_scaled/replenishing_pop_2019-2070.csv: $(UKSCALEDDATA)/2020_US_cohort.csv transitions_default $(DATAGEN)/US_utils.py $(DATAGEN)/generate_repl_pop.py $(PERSISTJSON)/*.json $(MODULES)/r_utils.py $(PYTHON) $(DATAGEN)/generate_repl_pop.py --region 'uk' diff --git a/minos/data_generation/US_household_upscaling.py b/minos/data_generation/US_household_upscaling.py index 0c976f65..28b2af9e 100644 --- a/minos/data_generation/US_household_upscaling.py +++ b/minos/data_generation/US_household_upscaling.py @@ -88,7 +88,7 @@ def main(region, percentage = 100, bootstrapping=False, n=100_000): number of boostrapping samples to take. """ # get synthetic data. - synthpop_file_path = "persistent_data/spatial_data/HH2011PopEst2020S_population.csv" + synthpop_file_path = "persistent_data/spatial_data/HH2011PopEst2020UK_population_UK.csv" try: synthpop_data = pd.read_csv(synthpop_file_path) # this is individual population weighted data. except FileNotFoundError as e: diff --git a/minos/modules/alcohol.py b/minos/modules/alcohol.py index 8781d035..3ac35110 100755 --- a/minos/modules/alcohol.py +++ b/minos/modules/alcohol.py @@ -1,7 +1,5 @@ """ Module for alcohol in Minos. -Calculation of monthly household alcohol -Possible extension to interaction with employment/education and any spatial/interaction effects. """ import pandas as pd diff --git a/minos/transitions/estimate_longitudinal_transitions.R b/minos/transitions/estimate_longitudinal_transitions.R index ae83db4e..17894122 100644 --- a/minos/transitions/estimate_longitudinal_transitions.R +++ b/minos/transitions/estimate_longitudinal_transitions.R @@ -19,6 +19,7 @@ require(tidyverse) require(stringr) require(texreg) require(dplyr) +require(survival) ################################### # Main loop for longitudinal models @@ -32,7 +33,7 @@ run_longitudinal_models <- function(transitionDir_path, transitionSourceDir_path modDef_path = paste0(transitionSourceDir_path, mod_def_name) modDefs <- file(description = modDef_path, open="r", blocking = TRUE) - valid_longitudnial_model_types <- c("LMM", "LMM_DIFF", "GLMM", "GEE_DIFF","ORDGEE", "CLMM", 'GLMMB', 'MSM') + valid_longitudnial_model_types <- c("LMM", "LMM_DIFF", "GLMM", "GEE_DIFF","ORDGEE", "CLMM", "SURV", 'GLMMB', 'MSM') data[which(data$ncigs==-8), 'ncigs'] <- 0 @@ -210,6 +211,19 @@ run_longitudinal_models <- function(transitionDir_path, transitionSourceDir_path formula = form, depend = dependent, start.year = min(year.range)) + + } else if (tolower(mod.type) == 'surv') { + + # generate new formula with survival object + form.string2 <- paste0('Surv(time, time + 1, ', dependent, ') ~ ', independents) + formula2 <- as.formula(form.string2) + + sorted_df[[dependent]] <- factor(sorted_df[[dependent]], ordered=TRUE) + + model <- estimate_survival(data = sorted_df, + formula = formula2, + depend = dependent) + } write_coefs <- F diff --git a/minos/transitions/transition_model_functions.R b/minos/transitions/transition_model_functions.R index e0ce149b..f9a98017 100644 --- a/minos/transitions/transition_model_functions.R +++ b/minos/transitions/transition_model_functions.R @@ -6,6 +6,7 @@ require(bestNormalize) require(lme4) require(glmmTMB) require(msm) +require(survival) ################ Model Specific Functions ################ @@ -368,14 +369,33 @@ estimate_longitudinal_msm <- function(data, formula, depend, start.year) { subj <- data$pidp } + print('This is the formula:') + print(formula) + print('Fitting the MSM model...') - model <- msm(formula = formula, - subject = subj, - data = data, - qmatrix = allowed.trans.matrix, - gen.inits = TRUE, - obstype = 1, - na.action = na.omit) + # ms_object <- msm(formula = formula, + # subject = subj, + # data = data, + # qmatrix = allowed.trans.matrix, + # gen.inits = TRUE, + # obstype = 1, + # na.action = na.omit) + + ms_object <- msm(formula = formula, + subject = subj, + data = data) + + ms_model <- msm1(ms_object) + + return(model) +} + +estimate_survival <- function(data, formula, depend) { + + data <- replace.missing(data) + data <- drop_na(data) + + model <- survreg(formula, data = data, dist = 'extreme') return(model) } diff --git a/minos/utils_qaly.R b/minos/utils_qaly.R index ebdfdd78..61fec7c3 100644 --- a/minos/utils_qaly.R +++ b/minos/utils_qaly.R @@ -62,7 +62,7 @@ sf12.plots <- function(base, base.name, int, int.name) { geom_hline(yintercept = 0, linetype = 'dashed') + labs(title = 'Change in SF_12_PCS', subtitle = paste0(int.name, ' vs ', base.name)) + xlab('Year') + - ylab('Change in MCS') + ylab('Change in PCS') print(p3) print(p4) diff --git a/minos/validation/handovers.Rmd b/minos/validation/handovers.Rmd index c16c103d..613954c8 100644 --- a/minos/validation/handovers.Rmd +++ b/minos/validation/handovers.Rmd @@ -264,23 +264,23 @@ handover_lineplots(raw.dat, test.base, 'nutrition_quality') ### Spaghetti ```{r} -raw.nut <- raw.dat %>% - select(pidp, age, time, nutrition_quality) -base.nut <- base.dat %>% - select(pidp, age, time, nutrition_quality) - -nut.spag <- rbind(raw.nut, base.nut) - -spaghetti_plot(nut.spag, 'nutrition_quality', - save = shall.we.save, - save.path = save.path) - - -density_ridges(nut.spag, "nutrition_quality", - save = T, - save.path = save.path) - -rm(raw.nut, base.nut, nut.spag) +# raw.nut <- raw.dat %>% +# select(pidp, age, time, nutrition_quality) +# base.nut <- base.dat %>% +# select(pidp, age, time, nutrition_quality) +# +# nut.spag <- rbind(raw.nut, base.nut) +# +# spaghetti_plot(nut.spag, 'nutrition_quality', +# save = shall.we.save, +# save.path = save.path) +# +# +# density_ridges(nut.spag, "nutrition_quality", +# save = T, +# save.path = save.path) +# +# rm(raw.nut, base.nut, nut.spag) ``` ## Tobacco @@ -466,12 +466,25 @@ cv_ordinal_plots(pivoted.df = pivoted, ```{r} handover_ordinal(raw.dat, base.dat, var = 'auditc', save = shall.we.save) + +raw.2020.alc <- raw.dat %>% + filter(time == 2020) %>% + group_by(auditc) %>% + summarise(n = n()) %>% + mutate(prop = n / sum(n)) + ``` ## Physical Activity ```{r} handover_ordinal(raw.dat, base.dat, var = 'active', save = shall.we.save) + +raw.2020.phys <- raw.dat %>% + filter(time == 2020) %>% + group_by(active) %>% + summarise(n = n()) %>% + mutate(prop = n / sum(n)) ``` ## Material Deprivation @@ -480,6 +493,28 @@ handover_ordinal(raw.dat, base.dat, var = 'active', save = shall.we.save) #handover_boxplots(raw.dat, base.dat, 'matdep') #handover_lineplots(raw.dat, base.dat, 'matdep') handover_ordinal(raw.dat, base.dat, var = 'matdep', save = shall.we.save) + +raw.dat$chron_disease <- as.factor(raw.dat$chron_disease) +raw.2020.cd <- raw.dat %>% + select(time, chron_disease, age) %>% + filter(time == 2020) %>% + mutate(age_group = cut(age, + breaks = c(0,20,30,40,50,60,70,80,90,100,120), + labels = c('16-19', '20-30', '30-40', '40-50', '50-60', '60-70', '70-80', '80-90', '90-100', '100+'))) %>% + group_by(age_group, chron_disease) %>% + summarise(n = n()) %>% + group_by(age_group) %>% + mutate(prop = n / sum(n), + perc = prop * 100) + # mutate(prop = n() / sum(n), + # perc = prop * 100) + +raw.2020.cd + +ggplot(raw.2020.cd, aes(x = age_group, y = perc, group = chron_disease, colour = chron_disease, fill = chron_disease)) + + geom_col(position = 'dodge') + + xlab('Age Group') + + ylab('Proportion') ``` ```{r} From 3a75b8fc48c4d47842e470591934967fffc4de70 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Thu, 23 Nov 2023 17:38:15 +0000 Subject: [PATCH 107/229] Changed a filename --- minos/data_generation/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/minos/data_generation/Makefile b/minos/data_generation/Makefile index 7ba57134..971fbbdf 100644 --- a/minos/data_generation/Makefile +++ b/minos/data_generation/Makefile @@ -130,7 +130,7 @@ $(DATADIR)/replenishing/scotland_scaled/replenishing_pop_2019-2070.csv: $(SCOTLA $(PYTHON) $(DATAGEN)/generate_repl_pop.py --region 'scotland' # synthetic input for UK with spatial component. -$(UKSCALEDDATA)/2020_US_cohort.csv: $(FINALDATA)/2020_US_cohort.csv $(DATAGEN)/US_utils.py $(DATAGEN)/US_household_upscaling.py $(PERSISTJSON)/HH2011PopEst2020UK_population_UK.csv +$(UKSCALEDDATA)/2020_US_cohort.csv: $(FINALDATA)/2020_US_cohort.csv $(DATAGEN)/US_utils.py $(DATAGEN)/US_household_upscaling.py $(PERSISTJSON)/HH2011PopEst2020UK_population.csv $(PYTHON) $(DATAGEN)/US_household_upscaling.py -r 'uk' -p 1 $(DATADIR)/replenishing/uk_scaled/replenishing_pop_2019-2070.csv: $(UKSCALEDDATA)/2020_US_cohort.csv transitions_default $(DATAGEN)/US_utils.py $(DATAGEN)/generate_repl_pop.py $(PERSISTJSON)/*.json $(MODULES)/r_utils.py From 8ca2ab41aefef90f62750ef99f31f436c5035e08 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Thu, 23 Nov 2023 18:36:56 +0000 Subject: [PATCH 108/229] Removed lots of unnecessary columns to reduce data size (ran out of RAM first time I tried the upscaling, I have 64GB...) --- minos/data_generation/Makefile | 2 +- .../data_generation/US_household_upscaling.py | 2 +- minos/data_generation/generate_stock_pop.py | 19 +++++++++ minos/modules/replenishment.py | 42 +++---------------- 4 files changed, 27 insertions(+), 38 deletions(-) diff --git a/minos/data_generation/Makefile b/minos/data_generation/Makefile index 971fbbdf..3b51c33e 100644 --- a/minos/data_generation/Makefile +++ b/minos/data_generation/Makefile @@ -130,7 +130,7 @@ $(DATADIR)/replenishing/scotland_scaled/replenishing_pop_2019-2070.csv: $(SCOTLA $(PYTHON) $(DATAGEN)/generate_repl_pop.py --region 'scotland' # synthetic input for UK with spatial component. -$(UKSCALEDDATA)/2020_US_cohort.csv: $(FINALDATA)/2020_US_cohort.csv $(DATAGEN)/US_utils.py $(DATAGEN)/US_household_upscaling.py $(PERSISTJSON)/HH2011PopEst2020UK_population.csv +$(UKSCALEDDATA)/2020_US_cohort.csv: $(FINALDATA)/2020_US_cohort.csv $(DATAGEN)/US_utils.py $(DATAGEN)/US_household_upscaling.py $(PERSISTDATA)/spatial_data/HH2011PopEst2020UK_population.csv $(PYTHON) $(DATAGEN)/US_household_upscaling.py -r 'uk' -p 1 $(DATADIR)/replenishing/uk_scaled/replenishing_pop_2019-2070.csv: $(UKSCALEDDATA)/2020_US_cohort.csv transitions_default $(DATAGEN)/US_utils.py $(DATAGEN)/generate_repl_pop.py $(PERSISTJSON)/*.json $(MODULES)/r_utils.py diff --git a/minos/data_generation/US_household_upscaling.py b/minos/data_generation/US_household_upscaling.py index 28b2af9e..4c4f29f4 100644 --- a/minos/data_generation/US_household_upscaling.py +++ b/minos/data_generation/US_household_upscaling.py @@ -88,7 +88,7 @@ def main(region, percentage = 100, bootstrapping=False, n=100_000): number of boostrapping samples to take. """ # get synthetic data. - synthpop_file_path = "persistent_data/spatial_data/HH2011PopEst2020UK_population_UK.csv" + synthpop_file_path = "persistent_data/spatial_data/HH2011PopEst2020UK_population.csv" try: synthpop_data = pd.read_csv(synthpop_file_path) # this is individual population weighted data. except FileNotFoundError as e: diff --git a/minos/data_generation/generate_stock_pop.py b/minos/data_generation/generate_stock_pop.py index 1f2557e2..528f1d61 100755 --- a/minos/data_generation/generate_stock_pop.py +++ b/minos/data_generation/generate_stock_pop.py @@ -182,6 +182,25 @@ def generate_stock(projections, cross_validation): data['nutrition_quality'] = data['nutrition_quality'].astype('int64') #data['housing_quality'] = data['housing_quality'].astype('int64') + # drop some columns that are not needed + data.drop(labels=['job_duration_m', + 'job_duration_y', + 'job_industry', + 'job_occupation', + 'alcohol_spending', + 'ndrinks', + 'job_inc', + 'jb_inc_per', + 'depression', + 'birth_month', + 'academic_year', + 'hourly_rate', + 'gross_paypm', + 'gross_pay_se', + 'job_hours_se'], + inplace=True, + axis=1) + US_utils.save_multiple_files(data, years, "data/final_US/", "") # Cross Validation stuff diff --git a/minos/modules/replenishment.py b/minos/modules/replenishment.py index 918ee59d..8711b2f3 100755 --- a/minos/modules/replenishment.py +++ b/minos/modules/replenishment.py @@ -42,44 +42,29 @@ def setup(self, builder): 'entrance_time', 'time', 'exit_time', - 'job_industry', - 'job_occupation', + 'birth_year', + 'hh_int_m', + 'hh_int_y', 'job_sec', - 'job_duration_m', - 'job_duration_y', - 'depression', - 'academic_year', 'hidp', - 'birth_month', - 'birth_year', 'nobs', 'region', - 'hh_int_y', - 'hh_int_m', 'Date', 'housing_quality', 'hh_income', 'neighbourhood_safety', 'ncigs', - 'alcohol_spending', 'smoker', 'loneliness', 'weight', 'nkids', 'nkids_ind', - 'ndrinks', 'max_educ', 'yearly_energy', 'job_sector', - 'gross_pay_se', 'nutrition_quality', - 'job_hours_se', - 'hourly_rate', 'job_hours', - 'job_inc', - 'jb_inc_per', 'hourly_wage', - 'gross_paypm', 'phealth', 'marital_status', 'hh_comp', @@ -278,46 +263,31 @@ def setup(self, builder): 'alive', 'ethnicity', 'entrance_time', + 'birth_year', + 'hh_int_m', + 'hh_int_y', 'time', 'exit_time', - 'job_industry', - 'job_occupation', 'job_sec', - 'job_duration_m', - 'job_duration_y', - 'depression', - 'academic_year', 'hidp', - 'birth_month', - 'birth_year', 'nobs', 'region', - 'hh_int_y', - 'hh_int_m', 'Date', 'housing_quality', 'hh_income', 'neighbourhood_safety', 'ncigs', - 'alcohol_spending', 'smoker', 'loneliness', 'weight', 'nkids', 'nkids_ind', - 'ndrinks', 'max_educ', 'yearly_energy', 'job_sector', - 'gross_pay_se', 'nutrition_quality', - 'job_hours_se', - 'hourly_rate', 'job_hours', - 'job_inc', - 'jb_inc_per', 'hourly_wage', - 'gross_paypm', 'phealth', 'marital_status', 'hh_comp', From 6d6e6abb32d86ed745f5ba88dffa935cf1729799 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Fri, 24 Nov 2023 11:10:45 +0000 Subject: [PATCH 109/229] Fixed problem with US_household_upscaling where SIMD deciles were being added to all upscaled data, which would cause all non-scottish rows to be dropped. Also had to change some of the code in replenishment where simd columns were added to all synthetic data. Now there is a keyword in the config for whether this is a scottish dataset or not. SIMD columns are not required for non-scottish or full UK data --- config/S7_glasgow_scaled.yaml | 1 + config/S7_uk_scaled.yaml | 1 + config/SIPHER7.yaml | 1 + config/cross_validation/cross_validation_S7_1.yaml | 1 + config/cross_validation/cross_validation_S7_2.yaml | 1 + config/cross_validation/cross_validation_S7_3.yaml | 1 + config/cross_validation/cross_validation_S7_4.yaml | 1 + config/cross_validation/cross_validation_S7_5.yaml | 1 + .../cross_validation/cross_validation_default1.yaml | 1 + .../cross_validation/cross_validation_default2.yaml | 1 + .../cross_validation/cross_validation_default3.yaml | 1 + .../cross_validation/cross_validation_default4.yaml | 1 + .../cross_validation/cross_validation_default5.yaml | 1 + config/default.yaml | 1 + config/default_noReplenishment.yaml | 1 + config/glasgow_scaled.yaml | 1 + config/inflated_default.yaml | 1 + config/scot_default.yaml | 1 + config/scotland_scaled.yaml | 1 + config/uk_scaled.yaml | 1 + minos/data_generation/US_household_upscaling.py | 5 +++-- minos/data_generation/US_upscaling.py | 10 +++++++++- minos/modules/replenishment.py | 11 +++++------ 23 files changed, 37 insertions(+), 9 deletions(-) diff --git a/config/S7_glasgow_scaled.yaml b/config/S7_glasgow_scaled.yaml index 87a76b3c..bea0b712 100644 --- a/config/S7_glasgow_scaled.yaml +++ b/config/S7_glasgow_scaled.yaml @@ -20,6 +20,7 @@ replenishing_dir: 'data/replenishing/glasgow_scaled' cross_validation: FALSE synthetic: TRUE +scotland: TRUE component_file: 'config/SIPHER7_components.txt' diff --git a/config/S7_uk_scaled.yaml b/config/S7_uk_scaled.yaml index c6c246bf..5aaf2719 100644 --- a/config/S7_uk_scaled.yaml +++ b/config/S7_uk_scaled.yaml @@ -20,6 +20,7 @@ replenishing_dir: 'data/replenishing/uk_scaled' cross_validation: FALSE synthetic: TRUE +scotland: FALSE component_file: 'config/SIPHER7_components.txt' diff --git a/config/SIPHER7.yaml b/config/SIPHER7.yaml index 34d9011e..8da933cc 100644 --- a/config/SIPHER7.yaml +++ b/config/SIPHER7.yaml @@ -23,6 +23,7 @@ replenishing_dir: 'data/replenishing' cross_validation: FALSE synthetic: FALSE +scotland: FALSE component_file: 'config/SIPHER7_components.txt' diff --git a/config/cross_validation/cross_validation_S7_1.yaml b/config/cross_validation/cross_validation_S7_1.yaml index 0a9875c5..96b8e450 100644 --- a/config/cross_validation/cross_validation_S7_1.yaml +++ b/config/cross_validation/cross_validation_S7_1.yaml @@ -23,6 +23,7 @@ replenishing_dir: 'data/replenishing/cross_validation' cross_validation: TRUE synthetic: FALSE +scotland: FALSE component_file: 'config/SIPHER7_components.txt' diff --git a/config/cross_validation/cross_validation_S7_2.yaml b/config/cross_validation/cross_validation_S7_2.yaml index 941598a9..f8d34ac3 100644 --- a/config/cross_validation/cross_validation_S7_2.yaml +++ b/config/cross_validation/cross_validation_S7_2.yaml @@ -23,6 +23,7 @@ replenishing_dir: 'data/replenishing/cross_validation' cross_validation: TRUE synthetic: FALSE +scotland: FALSE component_file: 'config/SIPHER7_components.txt' diff --git a/config/cross_validation/cross_validation_S7_3.yaml b/config/cross_validation/cross_validation_S7_3.yaml index 7c82c9f4..4ebfa337 100644 --- a/config/cross_validation/cross_validation_S7_3.yaml +++ b/config/cross_validation/cross_validation_S7_3.yaml @@ -23,6 +23,7 @@ replenishing_dir: 'data/replenishing/cross_validation' cross_validation: TRUE synthetic: FALSE +scotland: FALSE component_file: 'config/SIPHER7_components.txt' diff --git a/config/cross_validation/cross_validation_S7_4.yaml b/config/cross_validation/cross_validation_S7_4.yaml index 39932d3d..1c31a720 100644 --- a/config/cross_validation/cross_validation_S7_4.yaml +++ b/config/cross_validation/cross_validation_S7_4.yaml @@ -23,6 +23,7 @@ replenishing_dir: 'data/replenishing/cross_validation' cross_validation: TRUE synthetic: FALSE +scotland: FALSE component_file: 'config/SIPHER7_components.txt' diff --git a/config/cross_validation/cross_validation_S7_5.yaml b/config/cross_validation/cross_validation_S7_5.yaml index b33fb31b..3874bce3 100644 --- a/config/cross_validation/cross_validation_S7_5.yaml +++ b/config/cross_validation/cross_validation_S7_5.yaml @@ -23,6 +23,7 @@ replenishing_dir: 'data/replenishing/cross_validation' cross_validation: TRUE synthetic: FALSE +scotland: FALSE component_file: 'config/SIPHER7_components.txt' diff --git a/config/cross_validation/cross_validation_default1.yaml b/config/cross_validation/cross_validation_default1.yaml index da2ee482..993bf020 100644 --- a/config/cross_validation/cross_validation_default1.yaml +++ b/config/cross_validation/cross_validation_default1.yaml @@ -23,6 +23,7 @@ replenishing_dir: 'data/replenishing/cross_validation' cross_validation: TRUE synthetic: FALSE +scotland: FALSE component_file: 'config/SF12_components.txt' diff --git a/config/cross_validation/cross_validation_default2.yaml b/config/cross_validation/cross_validation_default2.yaml index f42a2b0b..e2ce946a 100644 --- a/config/cross_validation/cross_validation_default2.yaml +++ b/config/cross_validation/cross_validation_default2.yaml @@ -23,6 +23,7 @@ replenishing_dir: 'data/replenishing/cross_validation' cross_validation: TRUE synthetic: FALSE +scotland: FALSE component_file: 'config/SF12_components.txt' diff --git a/config/cross_validation/cross_validation_default3.yaml b/config/cross_validation/cross_validation_default3.yaml index a49a9a95..7f56dcb2 100644 --- a/config/cross_validation/cross_validation_default3.yaml +++ b/config/cross_validation/cross_validation_default3.yaml @@ -23,6 +23,7 @@ replenishing_dir: 'data/replenishing/cross_validation' cross_validation: TRUE synthetic: FALSE +scotland: FALSE component_file: 'config/SF12_components.txt' diff --git a/config/cross_validation/cross_validation_default4.yaml b/config/cross_validation/cross_validation_default4.yaml index 19501426..2021344f 100644 --- a/config/cross_validation/cross_validation_default4.yaml +++ b/config/cross_validation/cross_validation_default4.yaml @@ -23,6 +23,7 @@ replenishing_dir: 'data/replenishing/cross_validation' cross_validation: TRUE synthetic: FALSE +scotland: FALSE component_file: 'config/SF12_components.txt' diff --git a/config/cross_validation/cross_validation_default5.yaml b/config/cross_validation/cross_validation_default5.yaml index e4de1034..bbe6f33a 100644 --- a/config/cross_validation/cross_validation_default5.yaml +++ b/config/cross_validation/cross_validation_default5.yaml @@ -23,6 +23,7 @@ replenishing_dir: 'data/replenishing/cross_validation' cross_validation: TRUE synthetic: FALSE +scotland: FALSE component_file: 'config/SF12_components.txt' diff --git a/config/default.yaml b/config/default.yaml index defa5732..377737a9 100755 --- a/config/default.yaml +++ b/config/default.yaml @@ -23,6 +23,7 @@ replenishing_dir: 'data/replenishing' cross_validation: FALSE synthetic: FALSE +scotland: FALSE component_file: 'config/SF12_components.txt' diff --git a/config/default_noReplenishment.yaml b/config/default_noReplenishment.yaml index c54d668b..497d85a2 100644 --- a/config/default_noReplenishment.yaml +++ b/config/default_noReplenishment.yaml @@ -23,6 +23,7 @@ replenishing_dir: 'data/replenishing' cross_validation: FALSE synthetic: FALSE +scotland: FALSE component_file: 'config/SF12_components.txt' diff --git a/config/glasgow_scaled.yaml b/config/glasgow_scaled.yaml index 67ff2644..65e7be1d 100644 --- a/config/glasgow_scaled.yaml +++ b/config/glasgow_scaled.yaml @@ -20,6 +20,7 @@ replenishing_dir: 'data/replenishing/glasgow_scaled' cross_validation: FALSE synthetic: TRUE +scotland: TRUE component_file: 'config/SF12_components.txt' diff --git a/config/inflated_default.yaml b/config/inflated_default.yaml index a0ebfb9b..53488415 100644 --- a/config/inflated_default.yaml +++ b/config/inflated_default.yaml @@ -20,6 +20,7 @@ replenishing_dir: 'data/replenishing/inflated' cross_validation: FALSE synthetic: FALSE +scotland: FALSE component_file: 'config/SF12_components.txt' diff --git a/config/scot_default.yaml b/config/scot_default.yaml index 8c4d0bca..f69ee289 100755 --- a/config/scot_default.yaml +++ b/config/scot_default.yaml @@ -20,6 +20,7 @@ replenishing_dir: 'data/replenishing/scotland' cross_validation: FALSE synthetic: FALSE +scotland: TRUE component_file: 'config/SF12_components.txt' diff --git a/config/scotland_scaled.yaml b/config/scotland_scaled.yaml index 95e1f608..88410fa0 100644 --- a/config/scotland_scaled.yaml +++ b/config/scotland_scaled.yaml @@ -20,6 +20,7 @@ replenishing_dir: 'data/replenishing/scotland_scaled' cross_validation: FALSE synthetic: TRUE +scotland: TRUE component_file: 'config/SF12_components.txt' diff --git a/config/uk_scaled.yaml b/config/uk_scaled.yaml index 4c0fdcb8..1dceadd3 100644 --- a/config/uk_scaled.yaml +++ b/config/uk_scaled.yaml @@ -20,6 +20,7 @@ replenishing_dir: 'data/replenishing/uk_scaled' cross_validation: FALSE synthetic: TRUE +scotland: FALSE component_file: 'config/SF12_components.txt' diff --git a/minos/data_generation/US_household_upscaling.py b/minos/data_generation/US_household_upscaling.py index 4c4f29f4..dfe2d8bf 100644 --- a/minos/data_generation/US_household_upscaling.py +++ b/minos/data_generation/US_household_upscaling.py @@ -124,8 +124,9 @@ def main(region, percentage = 100, bootstrapping=False, n=100_000): print(f"Taking {percentage}% of sample giving {sampled_data.shape[0]} rows.") # merge with spatial_attributes - # get simd_deciles - sampled_data = merge_with_spatial_attributes(sampled_data, get_spatial_attribute_data(), "ZoneID") + # Get SIMD Deciles for Scottish data + if region in ['scotland', 'glasgow']: + sampled_data = merge_with_spatial_attributes(sampled_data, get_spatial_attribute_data(), "ZoneID") sampled_data['weight'] = 1 # force sample weights to 1. as this data is expanded weights no longer representative # but still updating weights helps with weighted aggregates later. diff --git a/minos/data_generation/US_upscaling.py b/minos/data_generation/US_upscaling.py index b6712237..d253f49f 100644 --- a/minos/data_generation/US_upscaling.py +++ b/minos/data_generation/US_upscaling.py @@ -5,10 +5,12 @@ # take those individuals in glasgow data zones # merge them with MINOS data to get individuals for all of glasgow # take some random percentage as a subset. May not be necessary for a medium size city. + import pandas as pd import numpy as np import US_utils + def subset_zone_ids(data, subset): """ Return some subset of the UK national set of ZoneIDs e.g. glasgow/manchester only. @@ -24,6 +26,7 @@ def subset_zone_ids(data, subset): subsetted_data = data.loc[data["ZoneID"].isin(subset)] return subsetted_data + def merge_with_synthpop_individuals(synthpop, msim_data, merge_column="pidp"): """ Merge US data on synthetic pop individual data. @@ -44,6 +47,7 @@ def merge_with_synthpop_individuals(synthpop, msim_data, merge_column="pidp"): #merged_data[f"new_{merge_column}"] = merged_data[f'new_{merge_column}'] return merged_data + def get_spatial_attribute_data(): try: simd_data = pd.read_csv("persistent_data/spatial_data/scotland_simd_to_data_zones.csv")[ @@ -61,6 +65,7 @@ def get_spatial_attribute_data(): """) return simd_data + def get_knn_cluster_data(): try: @@ -75,9 +80,11 @@ def get_knn_cluster_data(): store in persistent_data/spatial_data/.""") return knn_data + def merge_with_spatial_attributes(synthpop, spatial_data, merge_column): return synthpop.merge(spatial_data, on=merge_column) + def take_synthpop_sample(merged_data, percent, seed=8): """ Take smaller subset of full scale synthetic population @@ -96,6 +103,7 @@ def take_synthpop_sample(merged_data, percent, seed=8): sample_data = merged_data.sample(n=n, replace=False, random_state=seed) return sample_data + def main(): """ 1. Grab individual synthetic spatial population for UK. @@ -149,4 +157,4 @@ def main(): if __name__ == '__main__': - main() \ No newline at end of file + main() diff --git a/minos/modules/replenishment.py b/minos/modules/replenishment.py index 8711b2f3..cb2bad07 100755 --- a/minos/modules/replenishment.py +++ b/minos/modules/replenishment.py @@ -94,12 +94,11 @@ def setup(self, builder): ] if config.synthetic: # only have spatial column and new pidp for synthpop. - view_columns += ["ZoneID", - # "new_pidp", - 'local_simd_deciles', - 'simd_decile', - # 'cluster' - ] + view_columns += ["ZoneID"] + + if config.scotland: # add simd columns for scottish runs + view_columns += ['local_simd_deciles', + 'simd_deciles'] # Shorthand methods for readability. self.population_view = builder.population.get_view(view_columns) # view simulants From 98a76983e1081217130173136adbee6fa66d7d39 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Fri, 24 Nov 2023 16:02:00 +0000 Subject: [PATCH 110/229] Created a new script to handle upscaling to uk individual population size, however this crashes my PC so Im going to have to test it on arc. Forgive me John Hodrien... --- minos/data_generation/Makefile | 25 ++- .../data_generation/US_household_upscaling.py | 39 ++-- minos/data_generation/US_upscaling_test.py | 176 ++++++++++++++++++ minos/testing/QALY_comparison_livingWage.Rmd | 8 + 4 files changed, 226 insertions(+), 22 deletions(-) create mode 100644 minos/data_generation/US_upscaling_test.py diff --git a/minos/data_generation/Makefile b/minos/data_generation/Makefile index 3b51c33e..b5af5196 100644 --- a/minos/data_generation/Makefile +++ b/minos/data_generation/Makefile @@ -10,7 +10,9 @@ synthetic_glasgow_data: data glasgow_scaled_data synthetic_scotland_data: data scotland_scaled_data synthetic_scotland_repl -synthetic_uk_data: data uk_scaled_data synthetic_uk_repl +synthetic_uk_hh_data: data uk_hh_scaled_data synthetic_uk_hh_repl + +synthetic_uk_ind_data: data uk_ind_scaled_data synthetic_uk_ind_repl raw_data: ### Generate starting data in the correct format from raw Understanding Society data raw_data: $(RAWDATA)/2020_US_cohort.csv @@ -61,9 +63,13 @@ scotland_scaled_data : $(SCOTLANDSCALEDDATA)/2020_US_cohort.csv synthetic_scotland_repl: $(DATADIR)/replenishing/scotland_scaled/replenishing_pop_2019-2070.csv -uk_scaled_data: $(UKSCALEDDATA)/2020_US_cohort.csv +uk_hh_scaled_data: $(UKSCALEDDATA)/2020_US_cohort.csv + +synthetic_uk_hh_repl: $(DATADIR)/replenishing/uk_scaled/replenishing_pop_2019-2070.csv + +uk_ind_scaled_data: $(UKSCALEDDATA)/2020_US_cohort.csv -synthetic_uk_repl: $(DATADIR)/replenishing/uk_scaled/replenishing_pop_2019-2070.csv +synthetic_uk_ind_repl: $(DATADIR)/replenishing/uk_scaled/replenishing_pop_2019-2070.csv #sheffield_scaled_data goes here. @@ -129,9 +135,16 @@ $(SCOTLANDSCALEDDATA)/2020_US_cohort.csv: $(FINALDATA)/2020_US_cohort.csv $(DATA $(DATADIR)/replenishing/scotland_scaled/replenishing_pop_2019-2070.csv: $(SCOTLANDSCALEDDATA)/2020_US_cohort.csv $(TRANSITION_DATA)/education_state/nnet/education_state_2018_2019.rds $(DATAGEN)/US_utils.py $(DATAGEN)/generate_repl_pop.py $(PERSISTJSON)/*.json $(MODULES)/r_utils.py $(PYTHON) $(DATAGEN)/generate_repl_pop.py --region 'scotland' -# synthetic input for UK with spatial component. -$(UKSCALEDDATA)/2020_US_cohort.csv: $(FINALDATA)/2020_US_cohort.csv $(DATAGEN)/US_utils.py $(DATAGEN)/US_household_upscaling.py $(PERSISTDATA)/spatial_data/HH2011PopEst2020UK_population.csv +# synthetic input for UK with spatial component - HOUSEHOLDS +$(UKSCALEDDATA)/household/2020_US_cohort.csv: $(FINALDATA)/2020_US_cohort.csv $(DATAGEN)/US_utils.py $(DATAGEN)/US_household_upscaling.py $(PERSISTDATA)/spatial_data/HH2011PopEst2020UK_population.csv $(PYTHON) $(DATAGEN)/US_household_upscaling.py -r 'uk' -p 1 -$(DATADIR)/replenishing/uk_scaled/replenishing_pop_2019-2070.csv: $(UKSCALEDDATA)/2020_US_cohort.csv transitions_default $(DATAGEN)/US_utils.py $(DATAGEN)/generate_repl_pop.py $(PERSISTJSON)/*.json $(MODULES)/r_utils.py +$(DATADIR)/replenishing/uk_scaled/household/replenishing_pop_2019-2070.csv: $(UKSCALEDDATA)/2020_US_cohort.csv transitions_default $(DATAGEN)/US_utils.py $(DATAGEN)/generate_repl_pop.py $(PERSISTJSON)/*.json $(MODULES)/r_utils.py + $(PYTHON) $(DATAGEN)/generate_repl_pop.py --region 'uk' + +# synthetic input for UK with spatial component - INDIVIDUALS +$(UKSCALEDDATA)/household/2020_US_cohort.csv: $(FINALDATA)/2020_US_cohort.csv $(DATAGEN)/US_utils.py $(DATAGEN)/US_household_upscaling.py $(PERSISTDATA)/spatial_data/IndSPUKL_population.csv + $(PYTHON) $(DATAGEN)/US_upscaling_test.py -r 'uk' -p 1 + +$(DATADIR)/replenishing/uk_scaled/household/replenishing_pop_2019-2070.csv: $(UKSCALEDDATA)/2020_US_cohort.csv transitions_default $(DATAGEN)/US_utils.py $(DATAGEN)/generate_repl_pop.py $(PERSISTJSON)/*.json $(MODULES)/r_utils.py $(PYTHON) $(DATAGEN)/generate_repl_pop.py --region 'uk' diff --git a/minos/data_generation/US_household_upscaling.py b/minos/data_generation/US_household_upscaling.py index dfe2d8bf..e544a81c 100644 --- a/minos/data_generation/US_household_upscaling.py +++ b/minos/data_generation/US_household_upscaling.py @@ -14,13 +14,14 @@ """ - import pandas as pd -from minos.data_generation.US_upscaling import subset_zone_ids, take_synthpop_sample, merge_with_spatial_attributes, get_spatial_attribute_data +from minos.data_generation.US_upscaling import subset_zone_ids, take_synthpop_sample, merge_with_spatial_attributes, \ + get_spatial_attribute_data import numpy as np import US_utils import argparse + def merge_with_synthpop_households(synthpop, msim_data, merge_column="hidp"): """ Merge US data on synthetic pop individual data. @@ -28,8 +29,8 @@ def merge_with_synthpop_households(synthpop, msim_data, merge_column="hidp"): ---------- synthpop, msim_data : pd.DataFrame synthetic spatial population and US msim_data to merge. - merge_column : str - Which column to merge upon? usually pidp/hidp + merge_column : str + Which column to merge upon? usually pidp/hidp Returns ------- merged_data : pd.DataFrame @@ -38,9 +39,10 @@ def merge_with_synthpop_households(synthpop, msim_data, merge_column="hidp"): synthpop[f"new_{merge_column}"] = np.arange(synthpop.shape[0]) synthpop[merge_column] = synthpop[merge_column].astype(int) merged_data = synthpop.merge(msim_data, how='left', on=merge_column) - #merged_data[f"new_{merge_column}"] = merged_data[f'new_{merge_column}'] + # merged_data[f"new_{merge_column}"] = merged_data[f'new_{merge_column}'] return merged_data + def get_data_zones(region): """ @@ -54,11 +56,12 @@ def get_data_zones(region): Provides a list of data zones to return. """ - if region == "glasgow":# get glasgow data zones, get Understanding Society data. - data_zones = pd.read_csv("persistent_data/spatial_data/glasgow_data_zones.csv")["lsoa11cd"] # glasgow data zone IDs. + if region == "glasgow": # get glasgow data zones, get Understanding Society data. + data_zones = pd.read_csv("persistent_data/spatial_data/glasgow_data_zones.csv")[ + "lsoa11cd"] # glasgow data zone IDs. elif region == "scotland": data_zones = pd.read_csv("persistent_data/spatial_data/scotland_data_zones.csv")["DZ2011_Code"] - data_zones.columns = ['lsoa11cd'] # standardise column name for zone codes. + data_zones.columns = ['lsoa11cd'] # standardise column name for zone codes. elif region == "manchester": data_zones = pd.read_csv("persistent_data/spatial_data/manchester_lsoas.csv")["lsoa11cd"] elif region == "sheffield": @@ -71,7 +74,8 @@ def get_data_zones(region): return data_zones -def main(region, percentage = 100, bootstrapping=False, n=100_000): + +def main(region, percentage=100, bootstrapping=False, n=100_000): """ 1. Grab individual synthetic spatial population for UK. 2. Take subset of spatial population in a specific subregion. @@ -82,13 +86,17 @@ def main(region, percentage = 100, bootstrapping=False, n=100_000): Parameters ---------- + percentage : int + What percent of the synthetic population to use for subsetting bootstrapping : bool Do bootstrapping on top of synthetic sample to induce uncertainty? n : int number of boostrapping samples to take. """ # get synthetic data. - synthpop_file_path = "persistent_data/spatial_data/HH2011PopEst2020UK_population.csv" + #synthpop_file_path = "persistent_data/spatial_data/HH2011PopEst2020UK_population.csv" + #synthpop_file_path = "persistent_data/spatial_data/HHSPUKL_population.csv" + synthpop_file_path = "persistent_data/spatial_data/IndSPUKL_population.csv" try: synthpop_data = pd.read_csv(synthpop_file_path) # this is individual population weighted data. except FileNotFoundError as e: @@ -96,13 +104,13 @@ def main(region, percentage = 100, bootstrapping=False, n=100_000): print(f"Synthetic population file not found at {synthpop_file_path}. Please ask MINOS maintainers for access.") raise - data_zones = get_data_zones(region) US_data = pd.read_csv("data/final_US/2020_US_cohort.csv") # only expanding on one year of US data for 2020. + # If region is not UK, subset the synthpop data to contain only that region (if UK then keep the whole file) if type(data_zones) == pd.core.series.Series: subsetted_synthpop_data = subset_zone_ids(synthpop_data, data_zones) else: - subsetted_synthpop_data = synthpop_data # no subsetting for full UK population. + subsetted_synthpop_data = synthpop_data # no subsetting for full UK population. # if bootstrapping sample from subsetted synthetic data with replacement. if bootstrapping: @@ -111,7 +119,8 @@ def main(region, percentage = 100, bootstrapping=False, n=100_000): # merge synthetic and US data together. subsetted_synthpop_data['hidp'] = subsetted_synthpop_data['hhid'] merged_data = merge_with_synthpop_households(subsetted_synthpop_data, US_data) - merged_data = merged_data.dropna(axis=0, subset=["time"]) # remove rows that are missing in spatial data and aren't merged properly. + merged_data = merged_data.dropna(axis=0, subset=[ + "time"]) # remove rows that are missing in spatial data and aren't merged properly. print(f"{sum(merged_data['time'].value_counts())} rows out of {merged_data.shape[0]} successfully merged.") # scramble new hidp and pidp. @@ -120,7 +129,7 @@ def main(region, percentage = 100, bootstrapping=False, n=100_000): merged_data['pidp'] = merged_data.index # creating new pidps. # take subset of sample if desired. defaults to 100% for now. - sampled_data = take_synthpop_sample(merged_data, percentage/100) + sampled_data = take_synthpop_sample(merged_data, percentage / 100) print(f"Taking {percentage}% of sample giving {sampled_data.shape[0]} rows.") # merge with spatial_attributes @@ -165,5 +174,3 @@ def main(region, percentage = 100, bootstrapping=False, n=100_000): bootstrap_sample_size = 1 main(region, percentage, do_bootstrapping, bootstrap_sample_size) - - diff --git a/minos/data_generation/US_upscaling_test.py b/minos/data_generation/US_upscaling_test.py new file mode 100644 index 00000000..274693eb --- /dev/null +++ b/minos/data_generation/US_upscaling_test.py @@ -0,0 +1,176 @@ +""" +Previous version of this is upscaling on households and is problematic because its impossible to calculate new household IDs + +Get household IDs +shuffle IDs using hashing mod 10**20 to give 20 digit IDs + +for each new households ID +get everyone with the old household ID +assign them the new household ID +put them into the population + +Probably some fancy way to do this pandas gruoping/merger. +left (right?) merge on hidp might be the simplest way to do this? + +""" + +import pandas as pd +from minos.data_generation.US_upscaling import subset_zone_ids, take_synthpop_sample, merge_with_spatial_attributes, \ + get_spatial_attribute_data +import numpy as np +import US_utils +import argparse + + +def merge_with_synthpop_households(synthpop, msim_data, merge_column="pidp"): + """ Merge US data on synthetic pop individual data. + + Parameters + ---------- + synthpop, msim_data : pd.DataFrame + synthetic spatial population and US msim_data to merge. + merge_column : str + Which column to merge upon? usually pidp/hidp + Returns + ------- + merged_data : pd.DataFrame + Merged synthetic US data with spatial component. + """ + synthpop[f"new_{merge_column}"] = np.arange(synthpop.shape[0]) + synthpop[merge_column] = synthpop[merge_column].astype(int) + merged_data = synthpop.merge(msim_data, how='left', on=merge_column) + # merged_data[f"new_{merge_column}"] = merged_data[f'new_{merge_column}'] + return merged_data + + +def get_data_zones(region): + """ + + Parameters + ---------- + region: str + Region to return subset for + Returns + ------- + data_zones : list + Provides a list of data zones to return. + """ + + if region == "glasgow": # get glasgow data zones, get Understanding Society data. + data_zones = pd.read_csv("persistent_data/spatial_data/glasgow_data_zones.csv")[ + "lsoa11cd"] # glasgow data zone IDs. + elif region == "scotland": + data_zones = pd.read_csv("persistent_data/spatial_data/scotland_data_zones.csv")["DZ2011_Code"] + data_zones.columns = ['lsoa11cd'] # standardise column name for zone codes. + elif region == "manchester": + data_zones = pd.read_csv("persistent_data/spatial_data/manchester_lsoas.csv")["lsoa11cd"] + elif region == "sheffield": + data_zones = pd.read_csv("persistent_data/spatial_data/sheffield_lsoas.csv")["lsoa11cd"] + elif region == "uk": + data_zones = None + else: + print("Error! Invalid region defined for spatial subsetting.") + raise ValueError + + return data_zones + + +def main(region, percentage=100, bootstrapping=False, n=100_000): + """ + 1. Grab individual synthetic spatial population for UK. + 2. Take subset of spatial population in a specific subregion. + Glasgow City region / GMCA/ Sheffield City Region / Scotland + 3. Merge synthetic population on real Understanding Society data to get populated individual rows with a spatial component. + 4. + + Parameters + ---------- + + percentage : int + What percent of the synthetic population to use for subsetting + bootstrapping : bool + Do bootstrapping on top of synthetic sample to induce uncertainty? + n : int + number of boostrapping samples to take. + """ + # get synthetic data. + #synthpop_file_path = "persistent_data/spatial_data/HH2011PopEst2020UK_population.csv" + #synthpop_file_path = "persistent_data/spatial_data/HHSPUKL_population.csv" + synthpop_file_path = "persistent_data/spatial_data/IndSPUKL_population.csv" + try: + synthpop_data = pd.read_csv(synthpop_file_path) # this is individual population weighted data. + except FileNotFoundError as e: + print(e) + print(f"Synthetic population file not found at {synthpop_file_path}. Please ask MINOS maintainers for access.") + raise + + data_zones = get_data_zones(region) + US_data = pd.read_csv("data/final_US/2020_US_cohort.csv") # only expanding on one year of US data for 2020. + # If region is not UK, subset the synthpop data to contain only that region (if UK then keep the whole file) + if type(data_zones) == pd.core.series.Series: + subsetted_synthpop_data = subset_zone_ids(synthpop_data, data_zones) + else: + subsetted_synthpop_data = synthpop_data # no subsetting for full UK population. + + # if bootstrapping sample from subsetted synthetic data with replacement. + if bootstrapping: + subsetted_synthpop_data = subsetted_synthpop_data.sample(n, replace=True) + + # merge synthetic and US data together. + subsetted_synthpop_data['pidp'] = subsetted_synthpop_data['pidp'] + merged_data = merge_with_synthpop_households(subsetted_synthpop_data, US_data) + merged_data = merged_data.dropna(axis=0, subset=[ + "time"]) # remove rows that are missing in spatial data and aren't merged properly. + print(f"{sum(merged_data['time'].value_counts())} rows out of {merged_data.shape[0]} successfully merged.") + + # scramble new hidp and pidp. + merged_data['pidp'] = merged_data['new_pidp'] # replace old pidp. + merged_data.drop(['new_pidp', 'pidp'], axis=1, inplace=True) # removing old hidp columns + #merged_data['pidp'] = merged_data.index # creating new pidps. + + # take subset of sample if desired. defaults to 100% for now. + sampled_data = take_synthpop_sample(merged_data, percentage / 100) + print(f"Taking {percentage}% of sample giving {sampled_data.shape[0]} rows.") + + # merge with spatial_attributes + # Get SIMD Deciles for Scottish data + if region in ['scotland', 'glasgow']: + sampled_data = merge_with_spatial_attributes(sampled_data, get_spatial_attribute_data(), "ZoneID") + + sampled_data['weight'] = 1 # force sample weights to 1. as this data is expanded weights no longer representative + # but still updating weights helps with weighted aggregates later. + + US_utils.check_output_dir(f"data/scaled_{region}_US/ind/") # check save directory exists or create it. + US_utils.save_file(sampled_data, f"data/scaled_{region}_US/ind/", '', 2020) + + +if __name__ == '__main__': + + parser = argparse.ArgumentParser(description="Raw Data formatting from Understanding Society") + parser.add_argument("-r", "--region", required=True, type=str, + help="""The region to subset for the UK synthetic population. + glasgow, scotland, manchester, sheffield, uk only for now.""") + parser.add_argument("-p", "--percentage", required=False, type=int, + help="Percentage of synthetic population to use (e.g. 0-100%).") + parser.add_argument("-b", "--do_bootstrapping", required=False, type=bool, + help="Bootstrapping the synthetic population to incudce uncertainty?") + parser.add_argument("-s", "--bootstrap_sample_size", required=False, type=int, + help="How many bootstrap samples to take. Should only be used with do_bootstrapping above.") + + args = vars(parser.parse_args()) + print(args) + region = args['region'] + if 'percentage' in args.keys(): + percentage = args['percentage'] + else: + percentage = 100 + if 'do_bootstrapping' in args.keys(): + do_bootstrapping = args['do_bootstrapping'] + else: + do_bootstrapping = False + if "bootstrap_sample_size" in args.keys(): + bootstrap_sample_size = args['bootstrap_sample_size'] + else: + bootstrap_sample_size = 1 + + main(region, percentage, do_bootstrapping, bootstrap_sample_size) diff --git a/minos/testing/QALY_comparison_livingWage.Rmd b/minos/testing/QALY_comparison_livingWage.Rmd index 92884d0d..69f1eb6a 100644 --- a/minos/testing/QALY_comparison_livingWage.Rmd +++ b/minos/testing/QALY_comparison_livingWage.Rmd @@ -122,6 +122,14 @@ ggplot(deaths, aes(x = year, y = death_incidence, group = intervention, colour = labs(title = 'Incidence of Mortality Comparison') ``` +# Number people Below Living Wage + +```{r} +# Only need this for the baseline 2020 input data +base.num <- base %>% + filter() +``` + # QALY comparison From 7e631465ad383c1ac8820bf6e7437abbe28bf96e Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Fri, 24 Nov 2023 16:19:36 +0000 Subject: [PATCH 111/229] Fixed some Make targets that were duplicated, and changed some output paths to specify between hh and ind synthetic populations --- Makefile | 9 +++++++-- config/uk_scaled.yaml | 4 ++-- minos/data_generation/Makefile | 8 ++++---- minos/data_generation/US_household_upscaling.py | 4 ++-- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index cebff797..2271eab2 100644 --- a/Makefile +++ b/Makefile @@ -120,9 +120,14 @@ setup_scotland_scaled: install synthetic_glasgow_data transitions_default synthe setup_scotland_scaled_S7: install synthetic_glasgow_data transitions_SIPHER7 synthetic_scotland_repl -setup_uk_scaled: install synthetic_uk_data transitions_default synthetic_uk_repl +setup_uk_hh_scaled: install synthetic_uk_hh_data transitions_default synthetic_uk_hh_repl + +setup_uk_hh_scaled_S7: install synthetic_uk_hh_data transitions_SIPHER7 synthetic_uk_hh_repl + +setup_uk_ind_scaled: install synthetic_uk_ind_data transitions_default synthetic_uk_ind_repl + +setup_uk_ind_scaled_S7: install synthetic_uk_ind_data transitions_SIPHER7 synthetic_uk_ind_repl -setup_uk_scaled_S7: install synthetic_uk_data transitions_SIPHER7 synthetic_uk_repl ##################################### ### ADDITIONAL MAKEFILES diff --git a/config/uk_scaled.yaml b/config/uk_scaled.yaml index 1dceadd3..2bb4e798 100644 --- a/config/uk_scaled.yaml +++ b/config/uk_scaled.yaml @@ -11,12 +11,12 @@ population: mortality_file: 'regional_Mortality2011_LEEDS1_2.csv' fertility_file: 'regional_Fertility2011_LEEDS1_2.csv' -input_data_dir: "data/scaled_uk_US" +input_data_dir: "data/scaled_uk_US/ind" persistent_data_dir: "persistent_data" output_data_dir: "output" transition_dir: 'data/transitions' -replenishing_dir: 'data/replenishing/uk_scaled' +replenishing_dir: 'data/replenishing/uk_scaled/ind' cross_validation: FALSE synthetic: TRUE diff --git a/minos/data_generation/Makefile b/minos/data_generation/Makefile index b5af5196..75dd2ee2 100644 --- a/minos/data_generation/Makefile +++ b/minos/data_generation/Makefile @@ -136,15 +136,15 @@ $(DATADIR)/replenishing/scotland_scaled/replenishing_pop_2019-2070.csv: $(SCOTLA $(PYTHON) $(DATAGEN)/generate_repl_pop.py --region 'scotland' # synthetic input for UK with spatial component - HOUSEHOLDS -$(UKSCALEDDATA)/household/2020_US_cohort.csv: $(FINALDATA)/2020_US_cohort.csv $(DATAGEN)/US_utils.py $(DATAGEN)/US_household_upscaling.py $(PERSISTDATA)/spatial_data/HH2011PopEst2020UK_population.csv +$(UKSCALEDDATA)/hh/2020_US_cohort.csv: $(FINALDATA)/2020_US_cohort.csv $(DATAGEN)/US_utils.py $(DATAGEN)/US_household_upscaling.py $(PERSISTDATA)/spatial_data/HH2011PopEst2020UK_population.csv $(PYTHON) $(DATAGEN)/US_household_upscaling.py -r 'uk' -p 1 -$(DATADIR)/replenishing/uk_scaled/household/replenishing_pop_2019-2070.csv: $(UKSCALEDDATA)/2020_US_cohort.csv transitions_default $(DATAGEN)/US_utils.py $(DATAGEN)/generate_repl_pop.py $(PERSISTJSON)/*.json $(MODULES)/r_utils.py +$(DATADIR)/replenishing/uk_scaled/hh/replenishing_pop_2019-2070.csv: $(UKSCALEDDATA)/hh/2020_US_cohort.csv transitions_default $(DATAGEN)/US_utils.py $(DATAGEN)/generate_repl_pop.py $(PERSISTJSON)/*.json $(MODULES)/r_utils.py $(PYTHON) $(DATAGEN)/generate_repl_pop.py --region 'uk' # synthetic input for UK with spatial component - INDIVIDUALS -$(UKSCALEDDATA)/household/2020_US_cohort.csv: $(FINALDATA)/2020_US_cohort.csv $(DATAGEN)/US_utils.py $(DATAGEN)/US_household_upscaling.py $(PERSISTDATA)/spatial_data/IndSPUKL_population.csv +$(UKSCALEDDATA)/ind/2020_US_cohort.csv: $(FINALDATA)/2020_US_cohort.csv $(DATAGEN)/US_utils.py $(DATAGEN)/US_household_upscaling.py $(PERSISTDATA)/spatial_data/IndSPUKL_population.csv $(PYTHON) $(DATAGEN)/US_upscaling_test.py -r 'uk' -p 1 -$(DATADIR)/replenishing/uk_scaled/household/replenishing_pop_2019-2070.csv: $(UKSCALEDDATA)/2020_US_cohort.csv transitions_default $(DATAGEN)/US_utils.py $(DATAGEN)/generate_repl_pop.py $(PERSISTJSON)/*.json $(MODULES)/r_utils.py +$(DATADIR)/replenishing/uk_scaled/ind/replenishing_pop_2019-2070.csv: $(UKSCALEDDATA)/ind/2020_US_cohort.csv transitions_default $(DATAGEN)/US_utils.py $(DATAGEN)/generate_repl_pop.py $(PERSISTJSON)/*.json $(MODULES)/r_utils.py $(PYTHON) $(DATAGEN)/generate_repl_pop.py --region 'uk' diff --git a/minos/data_generation/US_household_upscaling.py b/minos/data_generation/US_household_upscaling.py index e544a81c..fc1e6538 100644 --- a/minos/data_generation/US_household_upscaling.py +++ b/minos/data_generation/US_household_upscaling.py @@ -140,8 +140,8 @@ def main(region, percentage=100, bootstrapping=False, n=100_000): sampled_data['weight'] = 1 # force sample weights to 1. as this data is expanded weights no longer representative # but still updating weights helps with weighted aggregates later. - US_utils.check_output_dir(f"data/scaled_{region}_US/") # check save directory exists or create it. - US_utils.save_file(sampled_data, f"data/scaled_{region}_US/", '', 2020) + US_utils.check_output_dir(f"data/scaled_{region}_US/hh/") # check save directory exists or create it. + US_utils.save_file(sampled_data, f"data/scaled_{region}_US/hh/", '', 2020) if __name__ == '__main__': From a0f2ca23ffbfa209331f18f854ab29fd1dccb2ff Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Fri, 24 Nov 2023 16:20:34 +0000 Subject: [PATCH 112/229] Fixed dependency of uk synthetic qaly runs --- scripts/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/Makefile b/scripts/Makefile index 6bb45207..98d459b6 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -245,7 +245,7 @@ arc4_qaly_scenarios_glasgow: MODE=glasgow_scaled arc4_qaly_scenarios_glasgow: RUN_CONFIG=$(CONFIG)/glasgow_scaled.yaml arc4_qaly_scenarios_glasgow: arc4_baseline arc4_intervention_energyDownLift arc4_intervention_energyDownLiftNoSupport arc4_intervention_livingWage -arc4_qaly_scenarios_uk: setup_uk_scaled +arc4_qaly_scenarios_uk: setup_uk_ind_scaled arc4_qaly_scenarios_uk: MODE=SF12_uk_scaled arc4_qaly_scenarios_uk: RUN_CONFIG=$(CONFIG)/uk_scaled.yaml arc4_qaly_scenarios_uk: arc4_baseline arc4_intervention_energyDownLift arc4_intervention_energyDownLiftNoSupport arc4_intervention_livingWage From f03aacc0661749ce5e0697072760042033ab3afd Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Fri, 24 Nov 2023 17:39:50 +0000 Subject: [PATCH 113/229] Fixes to some make targets and dependencies --- minos/data_generation/Makefile | 8 ++++---- scripts/Makefile | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/minos/data_generation/Makefile b/minos/data_generation/Makefile index 75dd2ee2..ad265abd 100644 --- a/minos/data_generation/Makefile +++ b/minos/data_generation/Makefile @@ -63,13 +63,13 @@ scotland_scaled_data : $(SCOTLANDSCALEDDATA)/2020_US_cohort.csv synthetic_scotland_repl: $(DATADIR)/replenishing/scotland_scaled/replenishing_pop_2019-2070.csv -uk_hh_scaled_data: $(UKSCALEDDATA)/2020_US_cohort.csv +uk_hh_scaled_data: $(UKSCALEDDATA)/hh/2020_US_cohort.csv -synthetic_uk_hh_repl: $(DATADIR)/replenishing/uk_scaled/replenishing_pop_2019-2070.csv +synthetic_uk_hh_repl: $(DATADIR)/replenishing/uk_scaled/hh/replenishing_pop_2019-2070.csv -uk_ind_scaled_data: $(UKSCALEDDATA)/2020_US_cohort.csv +uk_ind_scaled_data: $(UKSCALEDDATA)/ind/2020_US_cohort.csv -synthetic_uk_ind_repl: $(DATADIR)/replenishing/uk_scaled/replenishing_pop_2019-2070.csv +synthetic_uk_ind_repl: $(DATADIR)/replenishing/uk_scaled/ind/replenishing_pop_2019-2070.csv #sheffield_scaled_data goes here. diff --git a/scripts/Makefile b/scripts/Makefile index 98d459b6..ed121063 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -142,25 +142,25 @@ intervention_energyDownLiftNoSupport_scotland: setup_scotland_scaled ############################################ baseline: ### Baseline run of MINOS, using configuration defined in testConfig.yaml -baseline_uk: setup_uk_scaled +baseline_uk: setup_uk_ind_scaled $(PYTHON) scripts/run.py -c $(CONFIG)/uk_scaled.yaml -o SF12_uk_scaled -intervention_hhIncome_uk: setup_uk_scaled +intervention_hhIncome_uk: setup_uk_hh_scaled $(PYTHON) scripts/run.py -c $(CONFIG)/uk_scaled.yaml -o SF12_uk_scaled -i 'hhIncomeIntervention' -intervention_hhIncomeChildUplift_uk: setup_uk_scaled +intervention_hhIncomeChildUplift_uk: setup_uk_hh_scaled $(PYTHON) scripts/run.py -c $(CONFIG)/uk_scaled.yaml -o SF12_uk_scaled -i 'hhIncomeChildUplift' -intervention_PovertyLineChildUplift_uk: setup_uk_scaled +intervention_PovertyLineChildUplift_uk: setup_uk_hh_scaled $(PYTHON) scripts/run.py -c $(CONFIG)/uk_scaled.yaml -o SF12_uk_scaled -i 'hhIncomePovertyLineChildUplift' -intervention_livingWage_uk: setup_uk_scaled +intervention_livingWage_uk: setup_uk_hh_scaled $(PYTHON) scripts/run.py -c $(CONFIG)/uk_scaled.yaml -o SF12_uk_scaled -i 'livingWageIntervention' -intervention_energyDownLift_uk: setup_uk_scaled +intervention_energyDownLift_uk: setup_uk_hh_scaled $(PYTHON) scripts/run.py -c $(CONFIG)/uk_scaled.yaml -o SF12_uk_scaled -i 'energyDownlift' -intervention_energyDownLiftNoSupport_uk: setup_uk_scaled +intervention_energyDownLiftNoSupport_uk: setup_uk_hh_scaled $(PYTHON) scripts/run.py -c $(CONFIG)/uk_scaled.yaml -o SF12_uk_scaled -i 'energyDownliftNoSupport' From e31d29a5e002cdb37eca26b9cd1c0c155cf764f4 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Sat, 25 Nov 2023 13:37:53 +0000 Subject: [PATCH 114/229] Reduced the RAM impact of upscaling populations by switching the script around a bit. Now the synthpop data is sampled immediately after reading it in, instead of merging with US data and then sampling --- minos/data_generation/Makefile | 2 +- minos/data_generation/US_upscaling_test.py | 18 +++++++++++------- minos/data_generation/generate_repl_pop.py | 4 ++-- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/minos/data_generation/Makefile b/minos/data_generation/Makefile index ad265abd..fe676b51 100644 --- a/minos/data_generation/Makefile +++ b/minos/data_generation/Makefile @@ -143,7 +143,7 @@ $(DATADIR)/replenishing/uk_scaled/hh/replenishing_pop_2019-2070.csv: $(UKSCALEDD $(PYTHON) $(DATAGEN)/generate_repl_pop.py --region 'uk' # synthetic input for UK with spatial component - INDIVIDUALS -$(UKSCALEDDATA)/ind/2020_US_cohort.csv: $(FINALDATA)/2020_US_cohort.csv $(DATAGEN)/US_utils.py $(DATAGEN)/US_household_upscaling.py $(PERSISTDATA)/spatial_data/IndSPUKL_population.csv +$(UKSCALEDDATA)/ind/2020_US_cohort.csv: $(FINALDATA)/2020_US_cohort.csv $(DATAGEN)/US_utils.py $(DATAGEN)/US_upscaling_test.py $(PERSISTDATA)/spatial_data/IndSPUKL_population.csv $(PYTHON) $(DATAGEN)/US_upscaling_test.py -r 'uk' -p 1 $(DATADIR)/replenishing/uk_scaled/ind/replenishing_pop_2019-2070.csv: $(UKSCALEDDATA)/ind/2020_US_cohort.csv transitions_default $(DATAGEN)/US_utils.py $(DATAGEN)/generate_repl_pop.py $(PERSISTJSON)/*.json $(MODULES)/r_utils.py diff --git a/minos/data_generation/US_upscaling_test.py b/minos/data_generation/US_upscaling_test.py index 274693eb..2cfa7c95 100644 --- a/minos/data_generation/US_upscaling_test.py +++ b/minos/data_generation/US_upscaling_test.py @@ -116,6 +116,10 @@ def main(region, percentage=100, bootstrapping=False, n=100_000): if bootstrapping: subsetted_synthpop_data = subsetted_synthpop_data.sample(n, replace=True) + # take subset of sample if desired. defaults to 100% for now. + subsetted_synthpop_data = take_synthpop_sample(subsetted_synthpop_data, percentage / 100) + print(f"Taking {percentage}% of sample giving {subsetted_synthpop_data.shape[0]} rows.") + # merge synthetic and US data together. subsetted_synthpop_data['pidp'] = subsetted_synthpop_data['pidp'] merged_data = merge_with_synthpop_households(subsetted_synthpop_data, US_data) @@ -125,23 +129,23 @@ def main(region, percentage=100, bootstrapping=False, n=100_000): # scramble new hidp and pidp. merged_data['pidp'] = merged_data['new_pidp'] # replace old pidp. - merged_data.drop(['new_pidp', 'pidp'], axis=1, inplace=True) # removing old hidp columns + merged_data.drop(['new_pidp'], axis=1, inplace=True) # removing old hidp columns #merged_data['pidp'] = merged_data.index # creating new pidps. - # take subset of sample if desired. defaults to 100% for now. - sampled_data = take_synthpop_sample(merged_data, percentage / 100) - print(f"Taking {percentage}% of sample giving {sampled_data.shape[0]} rows.") + # # take subset of sample if desired. defaults to 100% for now. + # sampled_data = take_synthpop_sample(merged_data, percentage / 100.0) + # print(f"Taking {percentage}% of sample giving {sampled_data.shape[0]} rows.") # merge with spatial_attributes # Get SIMD Deciles for Scottish data if region in ['scotland', 'glasgow']: - sampled_data = merge_with_spatial_attributes(sampled_data, get_spatial_attribute_data(), "ZoneID") + merged_data = merge_with_spatial_attributes(merged_data, get_spatial_attribute_data(), "ZoneID") - sampled_data['weight'] = 1 # force sample weights to 1. as this data is expanded weights no longer representative + merged_data['weight'] = 1 # force sample weights to 1. as this data is expanded weights no longer representative # but still updating weights helps with weighted aggregates later. US_utils.check_output_dir(f"data/scaled_{region}_US/ind/") # check save directory exists or create it. - US_utils.save_file(sampled_data, f"data/scaled_{region}_US/ind/", '', 2020) + US_utils.save_file(merged_data, f"data/scaled_{region}_US/ind/", '', 2020) if __name__ == '__main__': diff --git a/minos/data_generation/generate_repl_pop.py b/minos/data_generation/generate_repl_pop.py index 49bb3011..0b9b85d9 100755 --- a/minos/data_generation/generate_repl_pop.py +++ b/minos/data_generation/generate_repl_pop.py @@ -206,8 +206,8 @@ def generate_replenishing(projections, scotland_mode, cross_validation, inflated data_source = 'scaled_scotland_US' output_dir = 'data/replenishing/scotland_scaled' elif region == 'uk': - data_source = 'scaled_uk_US' - output_dir = 'data/replenishing/uk_scaled' + data_source = 'scaled_uk_US/ind' + output_dir = 'data/replenishing/ind/uk_scaled' # first collect and load the datafile for 2018 file_name = f"data/{data_source}/2020_US_cohort.csv" From bf746bd8a333efd5fe3a76c0a67f75bb668f15da Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Sat, 25 Nov 2023 15:59:44 +0000 Subject: [PATCH 115/229] Fixed problem with replenishing dir for uk scaled data not set correctly, and added a bash file for collecting and compressing important qaly files on arc for easier downloading --- compress_qaly_files.sh | 35 ++++++++++++++++++++++ minos/data_generation/US_upscaling_test.py | 2 +- minos/data_generation/generate_repl_pop.py | 2 +- scripts/Makefile | 12 ++++---- 4 files changed, 43 insertions(+), 8 deletions(-) create mode 100644 compress_qaly_files.sh diff --git a/compress_qaly_files.sh b/compress_qaly_files.sh new file mode 100644 index 00000000..a87f3a09 --- /dev/null +++ b/compress_qaly_files.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# Path variable +base_path="/home/luke/Documents/WORK/MINOS/Minos/output/" + +# List of directories +directories=("baseline" "energyDownlift" "energyDownliftNoSupport" "livingWageIntervention") + +# Step 1: Ask for user input +read -p "Enter a unique descriptive suffix for the output filename: " suffix + +# Step 2: Create a directory +output_dir="QALY_analysis_$suffix" +mkdir "$output_dir" + +# Step 3 & 4: Loop over directories and copy/ rename qalys.csv +for dir in "${directories[@]}"; do + newest_dir=$(ls -t "${base_path}${dir}/" | head -n 1) + cp "${base_path}${dir}/${newest_dir}/qalys.csv" "$output_dir/qalys_${dir}.csv" +done + +# Step 5 & 6: Loop over directories and copy/ rename run_1_minos.log +for dir in "${directories[@]}"; do + newest_dir=$(ls -t "${base_path}${dir}/" | head -n 1) + cp "${base_path}${dir}/${newest_dir}/run_1_minos.log" "$output_dir/${dir}_1.log" +done + +# Step 7: Copy QALY_comparison_*.html files +cp "${base_path}minos/testing/QALY_comparison_"*.html "$output_dir/" + +# Step 8: Compress the directory +tar -czvf "$output_dir.tar.gz" "$output_dir" + +echo "Script completed successfully!" + diff --git a/minos/data_generation/US_upscaling_test.py b/minos/data_generation/US_upscaling_test.py index 2cfa7c95..c0900f13 100644 --- a/minos/data_generation/US_upscaling_test.py +++ b/minos/data_generation/US_upscaling_test.py @@ -121,7 +121,7 @@ def main(region, percentage=100, bootstrapping=False, n=100_000): print(f"Taking {percentage}% of sample giving {subsetted_synthpop_data.shape[0]} rows.") # merge synthetic and US data together. - subsetted_synthpop_data['pidp'] = subsetted_synthpop_data['pidp'] + #subsetted_synthpop_data['pidp'] = subsetted_synthpop_data['pidp'] merged_data = merge_with_synthpop_households(subsetted_synthpop_data, US_data) merged_data = merged_data.dropna(axis=0, subset=[ "time"]) # remove rows that are missing in spatial data and aren't merged properly. diff --git a/minos/data_generation/generate_repl_pop.py b/minos/data_generation/generate_repl_pop.py index 0b9b85d9..9ae42c5e 100755 --- a/minos/data_generation/generate_repl_pop.py +++ b/minos/data_generation/generate_repl_pop.py @@ -207,7 +207,7 @@ def generate_replenishing(projections, scotland_mode, cross_validation, inflated output_dir = 'data/replenishing/scotland_scaled' elif region == 'uk': data_source = 'scaled_uk_US/ind' - output_dir = 'data/replenishing/ind/uk_scaled' + output_dir = 'data/replenishing/uk_scaled/ind' # first collect and load the datafile for 2018 file_name = f"data/{data_source}/2020_US_cohort.csv" diff --git a/scripts/Makefile b/scripts/Makefile index ed121063..33314d32 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -145,22 +145,22 @@ baseline: ### Baseline run of MINOS, using configuration defined in testConfig.y baseline_uk: setup_uk_ind_scaled $(PYTHON) scripts/run.py -c $(CONFIG)/uk_scaled.yaml -o SF12_uk_scaled -intervention_hhIncome_uk: setup_uk_hh_scaled +intervention_hhIncome_uk: setup_uk_ind_scaled $(PYTHON) scripts/run.py -c $(CONFIG)/uk_scaled.yaml -o SF12_uk_scaled -i 'hhIncomeIntervention' -intervention_hhIncomeChildUplift_uk: setup_uk_hh_scaled +intervention_hhIncomeChildUplift_uk: setup_uk_ind_scaled $(PYTHON) scripts/run.py -c $(CONFIG)/uk_scaled.yaml -o SF12_uk_scaled -i 'hhIncomeChildUplift' -intervention_PovertyLineChildUplift_uk: setup_uk_hh_scaled +intervention_PovertyLineChildUplift_uk: setup_uk_ind_scaled $(PYTHON) scripts/run.py -c $(CONFIG)/uk_scaled.yaml -o SF12_uk_scaled -i 'hhIncomePovertyLineChildUplift' -intervention_livingWage_uk: setup_uk_hh_scaled +intervention_livingWage_uk: setup_uk_ind_scaled $(PYTHON) scripts/run.py -c $(CONFIG)/uk_scaled.yaml -o SF12_uk_scaled -i 'livingWageIntervention' -intervention_energyDownLift_uk: setup_uk_hh_scaled +intervention_energyDownLift_uk: setup_uk_ind_scaled $(PYTHON) scripts/run.py -c $(CONFIG)/uk_scaled.yaml -o SF12_uk_scaled -i 'energyDownlift' -intervention_energyDownLiftNoSupport_uk: setup_uk_hh_scaled +intervention_energyDownLiftNoSupport_uk: setup_uk_ind_scaled $(PYTHON) scripts/run.py -c $(CONFIG)/uk_scaled.yaml -o SF12_uk_scaled -i 'energyDownliftNoSupport' From 8d76b7eb2555eed5cabba9ba61c7495e28c91b68 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Sun, 26 Nov 2023 17:29:20 +0000 Subject: [PATCH 116/229] Cast vars to factor in the handovers_ordinal() function in validation --- minos/utils_validation_vis.R | 3 +++ minos/validation/handovers.Rmd | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/minos/utils_validation_vis.R b/minos/utils_validation_vis.R index 67ddaa10..e7d070aa 100644 --- a/minos/utils_validation_vis.R +++ b/minos/utils_validation_vis.R @@ -140,6 +140,9 @@ handover_ordinal <- function(raw.dat, base.dat, var, save=FALSE) { group_by(time, .data[[var]]) %>% count() %>% mutate(source = 'baseline_output') + + raw.var[[var]] <- as.factor(raw.var[[var]]) + base.var[[var]] <- as.factor(base.var[[var]]) merged <- rbind(raw.var, base.var) merged[[var]] <- as.factor(merged[[var]]) diff --git a/minos/validation/handovers.Rmd b/minos/validation/handovers.Rmd index 613954c8..45c95743 100644 --- a/minos/validation/handovers.Rmd +++ b/minos/validation/handovers.Rmd @@ -540,6 +540,7 @@ rm(raw.s, base.s, spag) handover_ordinal(raw.dat, base.dat, var = 'chron_disease', save = shall.we.save) ``` + ```{r} raw.s <- raw.dat %>% select(pidp, age, time, chron_disease) %>% @@ -550,6 +551,8 @@ base.s <- base.dat %>% spag <- rbind(raw.s, base.s) +spag$chron_disease <- as.numeric(spag$chron_disease) + spaghetti_plot(spag, 'chron_disease', save = shall.we.save, save.path = save.path) @@ -569,3 +572,8 @@ spaghetti_highlight_max_plot(spag.sample, 'chron_disease', rm(raw.s, base.s, spag) ``` + +```{r} + +``` + From 496bfd6291b360126043aebbff6d8bed7c9127b9 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 27 Nov 2023 18:11:58 +0000 Subject: [PATCH 117/229] Modified the intervention cost function to print out the cost in the first year as well as the overall cost of intervention. Also added some missing demographics from the PCS model --- minos/modules/physical_wellbeing.py | 4 ++- minos/outcomes/Makefile | 2 +- minos/testing/QALY_comparison_livingWage.Rmd | 7 ++--- .../transitions/model_definitions_default.txt | 2 +- minos/utils_qaly.R | 29 ++++++++++++++----- 5 files changed, 30 insertions(+), 14 deletions(-) diff --git a/minos/modules/physical_wellbeing.py b/minos/modules/physical_wellbeing.py index e611bfb0..d8e3bafc 100644 --- a/minos/modules/physical_wellbeing.py +++ b/minos/modules/physical_wellbeing.py @@ -191,7 +191,9 @@ def setup(self, builder): 'active', 'auditc', 'chron_disease', - 'matdep'] + 'matdep', + 'region', + 'education_state'] self.population_view = builder.population.get_view(columns=view_columns) diff --git a/minos/outcomes/Makefile b/minos/outcomes/Makefile index 2d3133f8..bab0b991 100644 --- a/minos/outcomes/Makefile +++ b/minos/outcomes/Makefile @@ -38,7 +38,7 @@ QALYs: QALY_baseline QALY_energysupport QALY_energynosupport QALY_livwage $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises_diff.Rmd')" $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" -QALY_comparison_livingWage: QALY_baseline QALY_livwage +QALY_comparison_livingWage: QALY_baseline QALY_livingWage $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" firefox file://$(TESTING)/QALY_comparison_livingWage.html diff --git a/minos/testing/QALY_comparison_livingWage.Rmd b/minos/testing/QALY_comparison_livingWage.Rmd index 69f1eb6a..33a0bafa 100644 --- a/minos/testing/QALY_comparison_livingWage.Rmd +++ b/minos/testing/QALY_comparison_livingWage.Rmd @@ -40,9 +40,9 @@ source(here::here('minos', 'utils_qaly.R')) ```{r} # # read in QALY files for baseline and interventions -#out.path <- here::here('output', 'default_config') +out.path <- here::here('output', 'default_config') #out.path <- here::here('output', 'glasgow_scaled') -out.path <- here::here('output', 'SF12_uk_scaled') +#out.path <- here::here('output', 'SF12_uk_scaled') base.path <- get_latest_runtime_subdirectory(here::here(out.path, 'baseline')) wage.path <- get_latest_runtime_subdirectory(here::here(out.path, 'livingWageIntervention')) @@ -198,9 +198,8 @@ intervention.cost(base = base, int.label = 'LivingWage') ``` - - ```{r} ``` + diff --git a/minos/transitions/model_definitions_default.txt b/minos/transitions/model_definitions_default.txt index 0ece1e43..809668e6 100644 --- a/minos/transitions/model_definitions_default.txt +++ b/minos/transitions/model_definitions_default.txt @@ -12,6 +12,6 @@ LOGIT : urban ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI NNET : housing_tenure ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_tenure) + factor(urban) + factor(financial_situation) + scale(hh_income) GLMM : hh_income ~ scale(hh_income) + scale(hh_income_diff) + scale(age) + I(scale(age)**2) + I(scale(age)**3) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + relevel(factor(job_sec), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + (1|pidp) LMM : nutrition_quality ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + scale(hh_income) + (1|pidp) + (1|hidp) + time -GLMM : SF_12_PCS ~ scale(SF_12_PCS_last) + I(scale(SF_12_PCS_last)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + factor(housing_quality) + scale(hh_income) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(active) + factor(auditc) + factor(chron_disease) + (1|pidp) +GLMM : SF_12_PCS ~ scale(SF_12_PCS_last) + I(scale(SF_12_PCS_last)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + scale(hh_income) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(active) + factor(auditc) + factor(chron_disease) + (1|pidp) GLMM : SF_12_MCS ~ scale(SF_12_MCS_last) + I(scale(SF_12_MCS_last)**2) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + scale(hh_income) + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + I(factor(ncigs>0)) + (1|pidp) ZIP : ncigs ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) | I(factor(ncigs>0)) + relevel(factor(ethnicity), ref = 'WBI') + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + relevel(factor(job_sec), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) diff --git a/minos/utils_qaly.R b/minos/utils_qaly.R index 61fec7c3..b9d2b3c4 100644 --- a/minos/utils_qaly.R +++ b/minos/utils_qaly.R @@ -256,15 +256,29 @@ ICER <- function(base, base.name, int, int.name, QALY_value, int.label) { intervention.cost <- function(base, base.name, int, int.name, int.label) { combined <- rbind(base, int) - total.cost <- combined %>% + total.cost.byYear <- combined %>% select(run_id, year, intervention, total_boost) %>% - group_by(run_id, intervention) %>% - summarise(total_cost = sum(total_boost)) %>% + group_by(run_id, year, intervention) %>% + summarise(total_yearly_cost = sum(total_boost)) %>% pivot_wider(names_from = 'intervention', - values_from = 'total_cost') %>% + values_from = 'total_yearly_cost') %>% mutate(cost_difference = .data[[int.name]] - .data[[base.name]]) %>% - select(run_id, cost_difference) %>% - mutate(intervention = int.name) %>% + select(run_id, year, cost_difference) %>% + mutate(intervention = int.name) + + p2 <- ggplot(total.cost.byYear, aes(x = year, y = cost_difference, group = intervention, fill = intervention, colour = intervention)) + + geom_smooth() + + labs(title = 'Intervention Cost by Year', subtitle = paste0(int.name, ' vs ', base.name)) + print(p2) + + total.cost.2021 <- total.cost.byYear %>% + filter(year == 2021) + + print(paste("Cost of intervention in the first year (2021) ==", total.cost.2021$cost_difference, sep = ' ')) + + total.cost <- total.cost.byYear %>% + group_by(intervention, run_id) %>% + summarise(cost_difference = sum(cost_difference)) %>% group_by(intervention) %>% summarise(margin = (qt(0.975, df=n() - 1) * sd(cost_difference)) / sqrt(n()), cost_difference = mean(cost_difference)) @@ -272,7 +286,8 @@ intervention.cost <- function(base, base.name, int, int.name, int.label) { p1 <- ggplot(total.cost, aes(x = intervention, y = cost_difference, group = intervention, fill = intervention, colour = intervention)) + geom_col() + geom_errorbar(aes(ymin = cost_difference - margin, ymax = cost_difference + margin, width = 0.4, group = intervention)) + - labs(title = 'Total cost of intervention', subtitle = paste0(int.name, ' vs ', base.name)) + labs(title = 'Total cost of intervention', subtitle = paste0(int.name, ' vs ', base.name)) + + scale_y_continuous(label = comma) print(p1) print(paste("Total cost of the intervention over the full simulation (2020-2035) ==", total.cost$cost_difference, sep = ' ')) From 2e9243db42c9ab6c195381bd1dff7f32baed9b79 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Wed, 13 Dec 2023 14:05:01 +0000 Subject: [PATCH 118/229] Small fix for matdep model to not run for 2020 --- minos/transitions/estimate_transitions.R | 4 +++- minos/transitions/model_definitions_default.txt | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/minos/transitions/estimate_transitions.R b/minos/transitions/estimate_transitions.R index 561840e3..aa6ae4b0 100644 --- a/minos/transitions/estimate_transitions.R +++ b/minos/transitions/estimate_transitions.R @@ -174,7 +174,7 @@ run_yearly_models <- function(transitionDir_path, if(dependent %in% c('SF_12_MCS', 'SF_12_PCS') & year == 2009) { next } # OLS_DIFF models can only start from wave 2 (no diff in first wave) if(tolower(mod.type) == 'ols_diff' & year == 2009) { next } - if(dependent %in% c('matdep') & year %in% c(2009, 2010, 2012, 2014, 2016, 2018)) { next } + if(dependent %in% c('matdep') & year %in% c(2009, 2010, 2012, 2014, 2016, 2018, 2020)) { next } if(dependent %in% c('chron_disease') & year < 2011) { next } print(paste0('Starting estimation for ', dependent, ' in ', year)) @@ -381,6 +381,8 @@ mode <- 'default' create.if.not.exists(transitionDir) +#default <- T + # Set different paths for scotland mode, cross-validation etc. if(scotland.mode) { diff --git a/minos/transitions/model_definitions_default.txt b/minos/transitions/model_definitions_default.txt index 809668e6..951b4427 100644 --- a/minos/transitions/model_definitions_default.txt +++ b/minos/transitions/model_definitions_default.txt @@ -1,3 +1,4 @@ +CLM : matdep ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(marital_status) + hhsize + factor(housing_tenure) + factor(urban) + factor(financial_situation) CLM : chron_disease ~ factor(chron_disease) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(marital_status) + scale(ncigs) LOGIT : active ~ scale(age) + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + scale(hh_income) + relevel(factor(ethnicity), ref = 'WBI') NNET : auditc ~ scale(age) + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + scale(hh_income) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + factor(financial_situation) @@ -5,7 +6,6 @@ CLM : housing_quality ~ scale(age) + I(scale(age)**2) + factor(sex) + relevel(fa CLM : loneliness ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + relevel(factor(job_sec), ref = '3') + scale(hh_income) + relevel(factor(marital_status), ref = 'Partnered') CLM : neighbourhood_safety ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) NNET : education_state ~ factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') -CLM : matdep ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(marital_status) + hhsize + factor(housing_tenure) + factor(urban) + factor(financial_situation) CLM : financial_situation ~ factor(financial_situation) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + relevel(factor(job_sec), ref = '3') + scale(hh_income)+ I(scale(hh_income)**2) + factor(marital_status) + factor(housing_tenure) LOGIT : heating ~ factor(heating) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) + factor(urban) + factor(housing_tenure) + factor(financial_situation) LOGIT : urban ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + scale(hh_income) From facfc5bc766e3ee66b5cded75cb573efe8434945 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Fri, 5 Jan 2024 14:16:03 +0000 Subject: [PATCH 119/229] Adjusting values in QALY calculation by weight. Currently SF_12 MCS, PCS, and boost_amount --- minos/outcomes/QALY_calculation.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/minos/outcomes/QALY_calculation.py b/minos/outcomes/QALY_calculation.py index 2ebceb6f..a866d0c0 100644 --- a/minos/outcomes/QALY_calculation.py +++ b/minos/outcomes/QALY_calculation.py @@ -61,6 +61,12 @@ def aggregate_csv(filename, intervention): # to investigate the mortality rate we can look at the ratio of dead to alive and compare across years alive_ratio = (alive_pop / total_pop_size) * 100 + # adjust SF_12 values by sampling weight + df['SF_12_MCS'] = (df['SF_12_MCS'] * ((1 / df['weight']) / df['weight'].sum())) + df['SF_12_PCS'] = (df['SF_12_PCS'] * ((1 / df['weight']) / df['weight'].sum())) + # also adjust boost_amount + df['boost_amount'] = (df['boost_amount'] * ((1 / df['weight']) / df['weight'].sum())) + # record boost information for intervention runs, set to 0 for baseline if intervention == 'baseline': pop_boosted = 0 @@ -101,6 +107,9 @@ def calculate_qaly(df): ((df['SF_12_MCS'] * df['SF_12_MCS']) * -0.00014) + \ ((df['SF_12_PCS'] * df['SF_12_PCS'] * df['SF_12_PCS']) * 0.0000107) + # adjust for sample weight + df['utility'] = df['utility'] * (df['weight'] / df['weight'].sum()) + # Now calculate QALYs by multiplying utility score by pop_size df['QALYs'] = df['utility'] * df['alive_pop'] From 8fd436ad89fccb40f3b9b985604982fb61932add Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Fri, 5 Jan 2024 14:17:52 +0000 Subject: [PATCH 120/229] Drop zero weight records before calculating population means --- minos/outcomes/QALY_calculation.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/minos/outcomes/QALY_calculation.py b/minos/outcomes/QALY_calculation.py index a866d0c0..b80ab1cf 100644 --- a/minos/outcomes/QALY_calculation.py +++ b/minos/outcomes/QALY_calculation.py @@ -61,6 +61,8 @@ def aggregate_csv(filename, intervention): # to investigate the mortality rate we can look at the ratio of dead to alive and compare across years alive_ratio = (alive_pop / total_pop_size) * 100 + # drop zero weight samples + df = df[df['weight'] > 0] # adjust SF_12 values by sampling weight df['SF_12_MCS'] = (df['SF_12_MCS'] * ((1 / df['weight']) / df['weight'].sum())) df['SF_12_PCS'] = (df['SF_12_PCS'] * ((1 / df['weight']) / df['weight'].sum())) From 53506e8a4e07518b73786a52b101e010b2fd3c16 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Fri, 5 Jan 2024 14:22:38 +0000 Subject: [PATCH 121/229] Handle boost_amount reweight separately so it doesnt break baseline calculations --- minos/outcomes/QALY_calculation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/minos/outcomes/QALY_calculation.py b/minos/outcomes/QALY_calculation.py index b80ab1cf..e5688910 100644 --- a/minos/outcomes/QALY_calculation.py +++ b/minos/outcomes/QALY_calculation.py @@ -66,14 +66,14 @@ def aggregate_csv(filename, intervention): # adjust SF_12 values by sampling weight df['SF_12_MCS'] = (df['SF_12_MCS'] * ((1 / df['weight']) / df['weight'].sum())) df['SF_12_PCS'] = (df['SF_12_PCS'] * ((1 / df['weight']) / df['weight'].sum())) - # also adjust boost_amount - df['boost_amount'] = (df['boost_amount'] * ((1 / df['weight']) / df['weight'].sum())) # record boost information for intervention runs, set to 0 for baseline if intervention == 'baseline': pop_boosted = 0 total_boost = 0 else: + # also adjust boost_amount + df['boost_amount'] = (df['boost_amount'] * ((1 / df['weight']) / df['weight'].sum())) pop_boosted = df['income_boosted'].sum() total_boost = df['boost_amount'].sum() From 2b220ea26d5f2a5997d76b9ea0a2b5f8b840bb3b Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Fri, 5 Jan 2024 14:24:33 +0000 Subject: [PATCH 122/229] Remove something I added earlier by mistake --- minos/outcomes/QALY_calculation.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/minos/outcomes/QALY_calculation.py b/minos/outcomes/QALY_calculation.py index e5688910..4c744edd 100644 --- a/minos/outcomes/QALY_calculation.py +++ b/minos/outcomes/QALY_calculation.py @@ -109,9 +109,6 @@ def calculate_qaly(df): ((df['SF_12_MCS'] * df['SF_12_MCS']) * -0.00014) + \ ((df['SF_12_PCS'] * df['SF_12_PCS'] * df['SF_12_PCS']) * 0.0000107) - # adjust for sample weight - df['utility'] = df['utility'] * (df['weight'] / df['weight'].sum()) - # Now calculate QALYs by multiplying utility score by pop_size df['QALYs'] = df['utility'] * df['alive_pop'] From 50576d35259854e2c9615d2d8a5b5937d747a8a0 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 5 Feb 2024 18:14:06 +0000 Subject: [PATCH 123/229] Removed chron_disease and matdep from PCS model --- minos/modules/physical_wellbeing.py | 2 -- minos/transitions/model_definitions_default.txt | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/minos/modules/physical_wellbeing.py b/minos/modules/physical_wellbeing.py index d8e3bafc..9ca25a71 100644 --- a/minos/modules/physical_wellbeing.py +++ b/minos/modules/physical_wellbeing.py @@ -190,8 +190,6 @@ def setup(self, builder): 'financial_situation', 'active', 'auditc', - 'chron_disease', - 'matdep', 'region', 'education_state'] diff --git a/minos/transitions/model_definitions_default.txt b/minos/transitions/model_definitions_default.txt index 70269e4f..e389de79 100644 --- a/minos/transitions/model_definitions_default.txt +++ b/minos/transitions/model_definitions_default.txt @@ -14,6 +14,6 @@ CLM : job_sec ~ factor(job_sec) + scale(age) + factor(sex) + relevel(factor(ethn NNET : housing_tenure ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_tenure) + factor(urban) + factor(financial_situation) + scale(hh_income) GLMM : hh_income ~ scale(hh_income) + scale(hh_income_diff) + scale(age) + I(scale(age)**2) + I(scale(age)**3) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + relevel(factor(job_sec), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + (1|pidp) LMM : nutrition_quality ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + scale(hh_income) + (1|pidp) + (1|hidp) + time -GLMM : SF_12_PCS ~ scale(SF_12_PCS_last) + I(scale(SF_12_PCS_last)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + scale(hh_income) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(active) + factor(auditc) + factor(chron_disease) + (1|pidp) +GLMM : SF_12_PCS ~ scale(SF_12_PCS_last) + I(scale(SF_12_PCS_last)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + scale(hh_income) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(active) + factor(auditc) + (1|pidp) GLMM : SF_12_MCS ~ scale(SF_12_MCS_last) + I(scale(SF_12_MCS_last)**2) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + scale(hh_income) + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + I(factor(ncigs>0)) + (1|pidp) ZIP : ncigs ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) | I(factor(ncigs>0)) + relevel(factor(ethnicity), ref = 'WBI') + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + relevel(factor(job_sec), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) From 911e770b1402e286cf8c2033e4e4cde52b301b79 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 5 Feb 2024 18:14:54 +0000 Subject: [PATCH 124/229] Fixed some merge conflict stuff that was missed at time of merge --- minos/transitions/estimate_transitions.R | 41 ++---------------------- 1 file changed, 3 insertions(+), 38 deletions(-) diff --git a/minos/transitions/estimate_transitions.R b/minos/transitions/estimate_transitions.R index 8ea8f9ea..4256c2b0 100644 --- a/minos/transitions/estimate_transitions.R +++ b/minos/transitions/estimate_transitions.R @@ -58,19 +58,6 @@ run_yearly_models <- function(transitionDir_path, modDefs <- file(description = modDef_path, open="r", blocking = TRUE) ## Set some factor levels because R defaults to using alphabetical ordering -<<<<<<< HEAD - data$housing_quality <- factor(data$housing_quality, - levels = c('Low', - 'Medium', - 'High')) - data$S7_housing_quality <- factor(data$S7_housing_quality, - levels = c('No to all', - 'Yes to some', - 'Yes to all')) - data$S7_neighbourhood_safety <- factor(data$S7_neighbourhood_safety, - levels = c('Often', - 'Some of the time', -======= orig_data$housing_quality <- factor(orig_data$housing_quality, levels = c('Low', 'Medium', @@ -81,8 +68,7 @@ run_yearly_models <- function(transitionDir_path, 'Yes to all')) orig_data$S7_neighbourhood_safety <- factor(orig_data$S7_neighbourhood_safety, levels = c('Often', - 'Some of the time', ->>>>>>> development + 'Some of the time', 'Hardly ever')) orig_data$S7_labour_state <- factor(orig_data$S7_labour_state, levels = c('FT Employed', @@ -91,7 +77,7 @@ run_yearly_models <- function(transitionDir_path, 'FT Education', 'Family Care', 'Not Working')) - data$auditc <- factor(data$auditc, + orig_data$auditc <- factor(orig_data$auditc, levels = c('Non-drinker', 'Low Risk', 'Increased Risk', @@ -156,13 +142,8 @@ run_yearly_models <- function(transitionDir_path, { print(paste0("WARNING. model ", paste0(mod.type, " not valid for yearly models. Skipping.."))) next -<<<<<<< HEAD - }# skip this iteration if model not in valid types. - -======= }# skip this iteration if model not in valid types. - ->>>>>>> development + # reset the formula string for each year formula.string <- formula.string.orig @@ -187,7 +168,6 @@ run_yearly_models <- function(transitionDir_path, if(grepl('neighbourhood_safety', dependent)){ depend.year <- year + 3 } # set up 3 year horizon # tobacco model only estimated for 2013 onwards if(dependent == 'ncigs' & year < 2013) { next } -<<<<<<< HEAD # alcohol model (auditc) only in specific years if(dependent == 'auditc' & !year %in% c(2014, 2016, 2018, 2019)) { next } # active only in specific years @@ -195,10 +175,6 @@ run_yearly_models <- function(transitionDir_path, #TODO: Maybe copy values from wave 2 onto wave 1? Assuming physical health changes slowly? # SF_12 predictor (physical health score) not available in wave 1 if(dependent %in% c('SF_12_MCS', 'SF_12_PCS') & year == 2009) { next } -======= - # Can't fit a time lagged model to SF12 in first wave - if(dependent == 'SF_12' & year == 2009) { next } ->>>>>>> development # OLS_DIFF models can only start from wave 2 (no diff in first wave) if(tolower(mod.type) == 'ols_diff' & year == 2009) { next } if(dependent %in% c('matdep') & year %in% c(2009, 2010, 2012, 2014, 2016, 2018, 2020)) { next } @@ -243,12 +219,7 @@ run_yearly_models <- function(transitionDir_path, if(!year > 2016) { formula.string <- str_remove_all(formula.string, " \\+ factor\\(loneliness\\)") } -<<<<<<< HEAD - if(!year %in% c(2015, 2017, 2019)) { - formula.string <- str_remove_all(formula.string, " \\+ nutrition_quality") -======= if(!year %in% c(2015, 2017, 2019, 2021)) { ->>>>>>> development formula.string <- str_remove_all(formula.string, " \\+ scale\\(nutrition_quality\\)") } if(year < 2013) { @@ -413,12 +384,6 @@ mode <- 'default' create.if.not.exists(transitionDir) -<<<<<<< HEAD -#default <- T - - -======= ->>>>>>> development # Set different paths for scotland mode, cross-validation etc. if(scotland.mode) { print('Estimating transition models in Scotland mode') From 90dacb73ff9579fd5383e6ac384251c04ddf7730 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 5 Feb 2024 18:15:57 +0000 Subject: [PATCH 125/229] Removed variable from replenishing columns list that has been removed upstream --- minos/modules/replenishment.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/minos/modules/replenishment.py b/minos/modules/replenishment.py index 141334a9..dc148208 100755 --- a/minos/modules/replenishment.py +++ b/minos/modules/replenishment.py @@ -63,7 +63,6 @@ def setup(self, builder): 'yearly_energy', 'job_sector', 'nutrition_quality', - 'job_hours_se', 'job_hours', 'hourly_wage', 'phealth', @@ -276,7 +275,6 @@ def setup(self, builder): 'yearly_energy', 'job_sector', 'nutrition_quality', - 'job_hours_se', 'job_hours', 'hourly_wage', 'phealth', From 3ce5288338811db054b97439a71804bd9dc8610f Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 5 Feb 2024 18:16:58 +0000 Subject: [PATCH 126/229] Added capital letter to FinancialSituation module name to work with component priority stuff --- config/SF12_components.txt | 2 +- minos/minosPipeline/RunPipeline.py | 38 +++++++++++++++------------- minos/modules/financial_situation.py | 2 +- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/config/SF12_components.txt b/config/SF12_components.txt index a7f0bbfe..a425c95c 100644 --- a/config/SF12_components.txt +++ b/config/SF12_components.txt @@ -10,7 +10,7 @@ Heating() Housing() HousingTenure() lmmYJNutrition() -financialSituation() +FinancialSituation() S7Labour() HourlyWage() JobSec() diff --git a/minos/minosPipeline/RunPipeline.py b/minos/minosPipeline/RunPipeline.py index 5a6e3da6..faa0e516 100755 --- a/minos/minosPipeline/RunPipeline.py +++ b/minos/minosPipeline/RunPipeline.py @@ -32,7 +32,7 @@ from minos.modules.education import Education from minos.modules.nutrition import Nutrition, lmmYJNutrition, lmmDiffNutrition from minos.modules.heating import Heating -from minos.modules.financial_situation import financialSituation +from minos.modules.financial_situation import FinancialSituation from minos.modules.housing_tenure import HousingTenure from minos.modules.physical_activity import PhysicalActivity from minos.modules.material_deprivation import MaterialDeprivation @@ -73,6 +73,7 @@ "lmmYJPCS()": lmmYJPCS(), "MWB()": MWB(), # Intermediary modules + "Ageing()": Ageing(), "Tobacco()": Tobacco(), "Alcohol()": Alcohol(), "Neighbourhood()": Neighbourhood(), @@ -84,7 +85,7 @@ "lmmDiffIncome()": lmmDiffIncome(), "lmmYJIncome()": lmmYJIncome(), "Income()": Income(), - "financialSituation()": financialSituation(), + "FinancialSituation()": FinancialSituation(), "Loneliness()": Loneliness(), "Nutrition()": Nutrition(), "lmmYJNutrition()": lmmYJNutrition(), @@ -117,8 +118,7 @@ "hhIncomePovertyLineChildUplift": hhIncomePovertyLineChildUplift(), "livingWageIntervention": livingWageIntervention(), "energyDownlift": energyDownlift(), - "energyDownliftNoSupport": energyDownliftNoSupport(), - "Ageing()": Ageing(), + "energyDownliftNoSupport": energyDownliftNoSupport() } replenishment_components_map = { @@ -150,26 +150,24 @@ def get_priorities(): component_priorities = {} component_priorities.update({el: 0 for el in replenishment_components_map}) component_priorities.update({el: 1 for el in ["Mortality()"]}) - component_priorities.update({el: 2 for el in ["FertilityAgeSpecificRates()", - "nkidsFertilityAgeSpecificRates()", - "Ageing"]}) - component_priorities.update({el: 3 for el in ['Income()', + component_priorities.update({el: 2 for el in ["Ageing()"]}) + component_priorities.update({el: 3 for el in ["FertilityAgeSpecificRates()", + "nkidsFertilityAgeSpecificRates()"]}) + component_priorities.update({el: 4 for el in ["Education()"]}) + component_priorities.update({el: 5 for el in ['Income()', 'geeIncome()', 'geeYJIncome()', 'lmmDiffIncome()', - 'lmmYJIncome()', - 'JobSec()', - 'JobHours()', - 'HourlyWage()']}) # Any new income-based components to be added here - component_priorities.update({el: 4 for el in intervention_components_map}) - component_priorities.update({el: 5 for el in ["Education()"]}) + 'lmmYJIncome()']}) # Any new income-based components to be added here + component_priorities.update({el: 6 for el in intervention_components_map}) and_finally = ['MWB()', 'geeMWB()', "geeYJMWB()", "lmmYJMWB()", "lmmDiffMWB()", - 'S7EquivalentIncome()'] + 'S7EquivalentIncome()', + "lmmYJPCS()"] everything_else = [el for el in list(components_map) + list(SIPHER7_components_map) if el not in list(component_priorities) + and_finally] @@ -177,7 +175,7 @@ def get_priorities(): # print("Everything else:\n", everything_else) component_priorities.update({el: 6 for el in everything_else}) - component_priorities.update({el: 7 for el in and_finally}) + component_priorities.update({el: 9 for el in and_finally}) # component_priorities.update({el: 8 for el in metrics_map}) return component_priorities, all_components_map @@ -226,7 +224,13 @@ def RunPipeline(config, intervention=None): A dataframe with the resulting simulation """ # Check modules are valid and convert to modules - components_raw = config['components'] + #components_raw = config['components'] + # read in the components from the correct text file + components_raw = [] + with open(config['component_file']) as comp_file: + for line in comp_file: + components_raw.append(line.rstrip()) + if intervention is not None: components_raw += intervention diff --git a/minos/modules/financial_situation.py b/minos/modules/financial_situation.py index 57e0afbb..cbce7d6b 100644 --- a/minos/modules/financial_situation.py +++ b/minos/modules/financial_situation.py @@ -6,7 +6,7 @@ from datetime import datetime as dt -class financialSituation(Base): +class FinancialSituation(Base): # Special methods used by vivarium. From 9885e7b25e85bf768790543dca9e61cceb8bfd16 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 5 Feb 2024 18:17:26 +0000 Subject: [PATCH 127/229] Added some missing dependencies to environment file --- environment.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/environment.yml b/environment.yml index a3ccd846..ff50aa83 100644 --- a/environment.yml +++ b/environment.yml @@ -1,4 +1,4 @@ -name: minos_condagcc +name: minos_conda channels: - conda-forge - defaults @@ -28,7 +28,7 @@ dependencies: # Added pip and python at top to avoid conda giving grumpy warning - r-ggridges - r-gghighlight - r-randomForest - - r-parellelly + - r-parallelly - r-doParallel - r-caret - pandoc From 981c946399d01fc313eb4189c5a72870ed7b719a Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 5 Feb 2024 18:17:57 +0000 Subject: [PATCH 128/229] Fixed year range of some PCS vars for new 2021 data --- minos/data_generation/US_complete_case.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/minos/data_generation/US_complete_case.py b/minos/data_generation/US_complete_case.py index 3abbf4d5..5380757b 100755 --- a/minos/data_generation/US_complete_case.py +++ b/minos/data_generation/US_complete_case.py @@ -112,9 +112,9 @@ def cut_outliers(df, lower, upper, var): # PCS Vars # AUDITC (alcohol) - present in 2015, 2017, 2019, 2020 - data = complete_case_custom_years(data, 'auditc', years=[2015, 2017, 2019, 2020]) + data = complete_case_custom_years(data, 'auditc', years=[2015, 2017, 2019, 2020, 2021]) # active (physical activity) - present in 2015, 2017, 2019, 2020 - data = complete_case_custom_years(data, 'active', years=[2015, 2017, 2019, 2020]) + data = complete_case_custom_years(data, 'active', years=[2015, 2017, 2019, 2020, 2021]) # chronic disease in the all years function drop_columns = [#'financial_situation', # these are just SF12 MICE columns for now. see US_format_raw.py From f8b66852185b323d49d3e35eb13e44c7dab07bdc Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 5 Feb 2024 18:18:24 +0000 Subject: [PATCH 129/229] Copied heating var information from 2020 to 2021 for kick off year --- minos/data_generation/generate_stock_pop.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/minos/data_generation/generate_stock_pop.py b/minos/data_generation/generate_stock_pop.py index f2f452ca..a5f5f03d 100755 --- a/minos/data_generation/generate_stock_pop.py +++ b/minos/data_generation/generate_stock_pop.py @@ -185,6 +185,11 @@ def generate_stock(projections, cross_validation): copy_year=2014, paste_year=2015, var_type='ordinal') + data = wave_data_copy(data, + var='heating', + copy_year=2020, + paste_year=2021, + var_type='ordinal') # Set loneliness and ncigs as int data['loneliness'] = data['loneliness'].astype('int64') @@ -205,7 +210,6 @@ def generate_stock(projections, cross_validation): 'depression', 'birth_month', 'academic_year', - 'hourly_rate', 'gross_paypm', 'gross_pay_se', 'job_hours_se'], From f56d07ee07dc0c2318a10c826d14e5b94f494b25 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 6 Feb 2024 11:31:03 +0000 Subject: [PATCH 130/229] Ignored lines beginning with # in transition estimation scripts so comments in model_definitions_ files are now properly ignored --- minos/transitions/estimate_longitudinal_transitions.R | 3 ++- minos/transitions/estimate_transitions.R | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/minos/transitions/estimate_longitudinal_transitions.R b/minos/transitions/estimate_longitudinal_transitions.R index adf512e2..22482bf8 100644 --- a/minos/transitions/estimate_longitudinal_transitions.R +++ b/minos/transitions/estimate_longitudinal_transitions.R @@ -50,6 +50,7 @@ run_longitudinal_models <- function(transitionDir_path, transitionSourceDir_path def = readLines(modDefs, n = 1) # Read one line from the connection. if(identical(def, character(0))){break} # If the line is empty, exit. + if(startsWith(def, '#')){next} # If line starts with '#', line is comment and should be ignored # Get model type split1 <- str_split(def, pattern = " : ")[[1]] @@ -98,7 +99,7 @@ run_longitudinal_models <- function(transitionDir_path, transitionSourceDir_path do.reflect=FALSE } - if (dependent %in% c("SF_12_MCS", 'SF_12_PCS', 'hh_income')) { + if (dependent %in% c("SF_12_MCS", 'hh_income')) { # 'SF_12_PCS' do.yeo.johnson = T # } else { do.yeo.johnson = F diff --git a/minos/transitions/estimate_transitions.R b/minos/transitions/estimate_transitions.R index 4256c2b0..3349d3a0 100644 --- a/minos/transitions/estimate_transitions.R +++ b/minos/transitions/estimate_transitions.R @@ -90,6 +90,7 @@ run_yearly_models <- function(transitionDir_path, def = readLines(modDefs, n = 1) # Read one line from the connection. if(identical(def, character(0))){break} # If the line is empty, exit. + if(startsWith(def, '#')){next} # If line starts with '#', line is comment and should be ignored # Get model type split1 <- str_split(def, pattern = " : ")[[1]] From c3d51b3815d4fb9c0a99efc3991e4235094baadb Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 6 Feb 2024 11:31:52 +0000 Subject: [PATCH 131/229] Fixed complete case year range for chron_disease variable --- minos/data_generation/US_complete_case.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/minos/data_generation/US_complete_case.py b/minos/data_generation/US_complete_case.py index 5380757b..936ac385 100755 --- a/minos/data_generation/US_complete_case.py +++ b/minos/data_generation/US_complete_case.py @@ -108,7 +108,7 @@ def cut_outliers(df, lower, upper, var): # PCS complete case vars data['chron_disease'] = data['chron_disease'].astype(int) - data = complete_case_custom_years(data, 'chron_disease', years=list(range(2011, 2021, 1))) + data = complete_case_custom_years(data, 'chron_disease', years=list(range(2011, 2022, 1))) # PCS Vars # AUDITC (alcohol) - present in 2015, 2017, 2019, 2020 From 51e790149b31388a16e44fdc70e70c7823dda629 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 6 Feb 2024 18:34:01 +0000 Subject: [PATCH 132/229] Added Matdep to the PCS model, and testing LMM models for PCS --- config/SF12_components.txt | 2 +- minos/data_generation/US_complete_case.py | 2 + minos/data_generation/generate_stock_pop.py | 10 ++++ minos/modules/physical_wellbeing.py | 51 ++++++++++++------- .../estimate_longitudinal_transitions.R | 4 +- .../transitions/model_definitions_default.txt | 4 +- 6 files changed, 50 insertions(+), 23 deletions(-) diff --git a/config/SF12_components.txt b/config/SF12_components.txt index a425c95c..a64174f1 100644 --- a/config/SF12_components.txt +++ b/config/SF12_components.txt @@ -4,8 +4,8 @@ Loneliness() Tobacco() Alcohol() PhysicalActivity() -Neighbourhood() MaterialDeprivation() +Neighbourhood() Heating() Housing() HousingTenure() diff --git a/minos/data_generation/US_complete_case.py b/minos/data_generation/US_complete_case.py index 936ac385..8191d6f7 100755 --- a/minos/data_generation/US_complete_case.py +++ b/minos/data_generation/US_complete_case.py @@ -116,6 +116,8 @@ def cut_outliers(df, lower, upper, var): # active (physical activity) - present in 2015, 2017, 2019, 2020 data = complete_case_custom_years(data, 'active', years=[2015, 2017, 2019, 2020, 2021]) # chronic disease in the all years function + # matdep (Material Deprivation) - present in 2009, 2010, 2012, 2014, 2016, 2018, 2020 + data = complete_case_custom_years(data, 'matdep', years=[2009, 2010, 2012, 2014, 2016, 2018, 2020]) drop_columns = [#'financial_situation', # these are just SF12 MICE columns for now. see US_format_raw.py 'ghq_depression', diff --git a/minos/data_generation/generate_stock_pop.py b/minos/data_generation/generate_stock_pop.py index a5f5f03d..0daf4ba3 100755 --- a/minos/data_generation/generate_stock_pop.py +++ b/minos/data_generation/generate_stock_pop.py @@ -190,6 +190,16 @@ def generate_stock(projections, cross_validation): copy_year=2020, paste_year=2021, var_type='ordinal') + data = wave_data_copy(data, + var='matdep', + copy_year=2020, + paste_year=2021, + var_type='ordinal') + data = wave_data_copy(data, + var='matdep', + copy_year=2014, + paste_year=2015, + var_type='ordinal') # Set loneliness and ncigs as int data['loneliness'] = data['loneliness'].astype('int64') diff --git a/minos/modules/physical_wellbeing.py b/minos/modules/physical_wellbeing.py index 9ca25a71..03421f08 100644 --- a/minos/modules/physical_wellbeing.py +++ b/minos/modules/physical_wellbeing.py @@ -190,6 +190,8 @@ def setup(self, builder): 'financial_situation', 'active', 'auditc', + 'chron_disease', + 'matdep', 'region', 'education_state'] @@ -202,11 +204,14 @@ def setup(self, builder): # Declare events in the module. At what times do individuals transition states from this module. E.g. when does # individual graduate in an education module. - builder.event.register_listener("time_step", self.on_time_step, priority=9) + #builder.event.register_listener("time_step", self.on_time_step, priority=9) + super().setup(builder) #only need to load this once for now. #self.gee_transition_model = r_utils.load_transitions(f"SF_12/lmm/SF_12_LMM", self.rpy2_modules, path=self.transition_dir) - self.gee_transition_model = r_utils.load_transitions(f"SF_12_PCS/glmm/SF_12_PCS_GLMM", self.rpy2_modules, path=self.transition_dir) + #self.gee_transition_model = r_utils.load_transitions(f"SF_12_PCS/glmm/SF_12_PCS_GLMM", self.rpy2_modules, path=self.transition_dir) + self.gee_transition_model = r_utils.load_transitions(f"SF_12_PCS/lmm/SF_12_PCS_LMM", self.rpy2_modules, + path=self.transition_dir) def on_time_step(self, event): """Produces new children and updates parent status on time steps. @@ -228,14 +233,14 @@ def on_time_step(self, event): newWavePWB.index = pop.index #newWavePWB["SF_12_PCS"] -= 1 - sf12_mean = np.mean(newWavePWB["SF_12_PCS"]) - std_ratio = (11/np.std(newWavePWB["SF_12_PCS"])) - newWavePWB["SF_12_PCS"] *= (11/np.std(newWavePWB["SF_12_PCS"])) - newWavePWB["SF_12_PCS"] -= ((std_ratio-1)*sf12_mean) - newWavePWB["SF_12_PCS"] -= 1.5 - #newWavePWB["SF_12_PCS"] += (50 - np.mean(newWavePWB["SF_12_PCS"])) - newWavePWB["SF_12_PCS"] = np.clip(newWavePWB["SF_12_PCS"], 0, 100) # keep within [0, 100] bounds of SF12. - newWavePWB["SF_12_PCS_diff"] = newWavePWB["SF_12_PCS"] - pop["SF_12_PCS"] + # sf12_mean = np.mean(newWavePWB["SF_12_PCS"]) + # std_ratio = (11/np.std(newWavePWB["SF_12_PCS"])) + # newWavePWB["SF_12_PCS"] *= (11/np.std(newWavePWB["SF_12_PCS"])) + # newWavePWB["SF_12_PCS"] -= ((std_ratio-1)*sf12_mean) + # newWavePWB["SF_12_PCS"] -= 1.5 + # #newWavePWB["SF_12_PCS"] += (50 - np.mean(newWavePWB["SF_12_PCS"])) + # newWavePWB["SF_12_PCS"] = np.clip(newWavePWB["SF_12_PCS"], 0, 100) # keep within [0, 100] bounds of SF12. + # newWavePWB["SF_12_PCS_diff"] = newWavePWB["SF_12_PCS"] - pop["SF_12_PCS"] # Update population with new SF_12_PCS #print(np.mean(newWavePWB["SF_12_PCS"])) #print(np.std(newWavePWB["SF_12_PCS"])) @@ -250,12 +255,20 @@ def calculate_pwb(self, pop): Returns ------- """ - out_data = r_utils.predict_next_timestep_yj_gamma_glmm(self.gee_transition_model, - self.rpy2_modules, - current=pop, - dependent='SF_12_PCS', - reflect=True, - yeo_johnson=True, - mod_type='gamma', - noise_std=0.1) # 5 for non yj, 0.35 for yj - return out_data + nextWavePWB = r_utils.predict_next_timestep_yj_gaussian_lmm(self.gee_transition_model, + self.rpy2Modules, + pop, + dependent='SF_12_PCS', + reflect=True, + yeo_johnson=False, + noise_std=1) # + + # nextWavePWB = r_utils.predict_next_timestep_yj_gamma_glmm(self.gee_transition_model, + # self.rpy2_modules, + # current=pop, + # dependent='SF_12_PCS', + # reflect=True, + # yeo_johnson=True, + # mod_type='gamma', + # noise_std=0.1) # 5 for non yj, 0.35 for yj + return nextWavePWB diff --git a/minos/transitions/estimate_longitudinal_transitions.R b/minos/transitions/estimate_longitudinal_transitions.R index 22482bf8..62647429 100644 --- a/minos/transitions/estimate_longitudinal_transitions.R +++ b/minos/transitions/estimate_longitudinal_transitions.R @@ -99,7 +99,7 @@ run_longitudinal_models <- function(transitionDir_path, transitionSourceDir_path do.reflect=FALSE } - if (dependent %in% c("SF_12_MCS", 'hh_income')) { # 'SF_12_PCS' + if (dependent %in% c("SF_12_MCS", 'SF_12_PCS', 'hh_income')) { do.yeo.johnson = T # } else { do.yeo.johnson = F @@ -334,7 +334,7 @@ sipher7 <- args$SIPHER7 ################################################################### # DELETE ME -#default <- TRUE +default <- TRUE #cross_validation <- TRUE # DELETE ME ################################################################### diff --git a/minos/transitions/model_definitions_default.txt b/minos/transitions/model_definitions_default.txt index e389de79..85575ba0 100644 --- a/minos/transitions/model_definitions_default.txt +++ b/minos/transitions/model_definitions_default.txt @@ -1,4 +1,6 @@ +LMM : SF_12_PCS ~ scale(SF_12_PCS_last) + I(scale(SF_12_PCS_last)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + scale(hh_income) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(active) + factor(auditc) + factor(matdep) + (1|pidp) + (1|hidp) + time RF : hourly_wage ~ time + sex + age + I(age**2) + ethnicity + region + education_state + urban + job_sec +CLM : chron_disease ~ factor(chron_disease) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(marital_status) + scale(ncigs) CLM : matdep ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(marital_status) + hhsize + factor(housing_tenure) + factor(urban) + factor(financial_situation) LOGIT : active ~ scale(age) + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + scale(hh_income) + relevel(factor(ethnicity), ref = 'WBI') NNET : auditc ~ scale(age) + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + scale(hh_income) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + factor(financial_situation) @@ -14,6 +16,6 @@ CLM : job_sec ~ factor(job_sec) + scale(age) + factor(sex) + relevel(factor(ethn NNET : housing_tenure ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_tenure) + factor(urban) + factor(financial_situation) + scale(hh_income) GLMM : hh_income ~ scale(hh_income) + scale(hh_income_diff) + scale(age) + I(scale(age)**2) + I(scale(age)**3) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + relevel(factor(job_sec), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + (1|pidp) LMM : nutrition_quality ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + scale(hh_income) + (1|pidp) + (1|hidp) + time -GLMM : SF_12_PCS ~ scale(SF_12_PCS_last) + I(scale(SF_12_PCS_last)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + scale(hh_income) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(active) + factor(auditc) + (1|pidp) +# GLMM : SF_12_MCS ~ scale(SF_12_MCS_last) + I(scale(SF_12_MCS_last)**2) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + scale(hh_income) + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + I(factor(ncigs>0)) + (1|pidp) ZIP : ncigs ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) | I(factor(ncigs>0)) + relevel(factor(ethnicity), ref = 'WBI') + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + relevel(factor(job_sec), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) From 7f4ec8abe650016651f7863f6ed991f71f3ef4ad Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 6 Feb 2024 18:34:46 +0000 Subject: [PATCH 133/229] Fix for new components txt file in RunPipeline --- minos/minosPipeline/RunPipeline.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/minos/minosPipeline/RunPipeline.py b/minos/minosPipeline/RunPipeline.py index faa0e516..89935110 100755 --- a/minos/minosPipeline/RunPipeline.py +++ b/minos/minosPipeline/RunPipeline.py @@ -345,10 +345,10 @@ def RunPipeline(config, intervention=None): # Print metrics for desired module. # TODO: this can be extended towards a generalised metrics method for each module. - if 'Mortality()' in config.components: + if 'Mortality()' in components: print('dead', len(pop[pop['alive'] == 'dead'])) logging.info(f"Total dead: {len(pop[pop['alive'] == 'dead'])}") - if 'FertilityAgeSpecificRates()' in config.components: + if 'FertilityAgeSpecificRates()' in components: print('New children', len(pop[pop['parent_id'] != -1])) logging.info(f"New children: {len(pop[pop['parent_id'] != -1])}") From 5110aab6efa658ed433b7a1e1e9b5d8dc2b7d213 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 6 Feb 2024 18:35:21 +0000 Subject: [PATCH 134/229] Added new super setup command to chron_disease --- minos/modules/chron_disease.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/minos/modules/chron_disease.py b/minos/modules/chron_disease.py index 42284bbe..5aeb668e 100644 --- a/minos/modules/chron_disease.py +++ b/minos/modules/chron_disease.py @@ -77,7 +77,8 @@ def setup(self, builder): # Declare events in the module. At what times do individuals transition states from this module. E.g. when does # individual graduate in an education module. - builder.event.register_listener("time_step", self.on_time_step, priority=5) + #builder.event.register_listener("time_step", self.on_time_step, priority=5) + super().setup(builder) def on_time_step(self, event): """Produces new children and updates parent status on time steps. From 484d95097826b3779ff183b07b12832e2a9f7da2 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 6 Feb 2024 18:35:51 +0000 Subject: [PATCH 135/229] Added a max_ncigs plot to handovers and small change in tobacco.py --- minos/modules/tobacco.py | 18 +++++++++++++++--- minos/validation/handovers.Rmd | 29 +++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/minos/modules/tobacco.py b/minos/modules/tobacco.py index 4e4946c6..d28b6048 100755 --- a/minos/modules/tobacco.py +++ b/minos/modules/tobacco.py @@ -53,7 +53,8 @@ def setup(self, builder): # columns_created is the columns created by this module. # view_columns is the columns from the main population used in this module. # In this case, view_columns are taken straight from the transition model - view_columns = ["age", + view_columns = ["pidp", + "age", "sex", "ethnicity", "region", @@ -100,10 +101,21 @@ def on_time_step(self, event): newWaveTobacco = pd.DataFrame(self.calculate_tobacco(pop)) newWaveTobacco.columns = ['ncigs'] newWaveTobacco.index = pop.index - newWaveTobacco["ncigs"] = newWaveTobacco["ncigs"].astype(int) # Draw individuals next states randomly from this distribution. # Update population with new tobacco + + # clip values to a reasonable range to nullify extreme projections + # hopefully this step will be nullified when we move to a longitudinal zip model newWaveTobacco["ncigs"] = np.clip(newWaveTobacco['ncigs'], 0, 300) + # Also seem to have another unfortunate problem that only happens in 2030 (maybe after also but fails in 2030) + # Some NA values are included in ncigs prediction, which fails the type casting below. For a quick fix which + # will also hopefully be nullified with the new transition model, we can just force these to 300 + # THIS IS ASSUMING THESE ARE PREDICTED AN INFINITE VALUE THAT IS RECORDED AS NA + newWaveTobacco['ncigs'][newWaveTobacco['ncigs'].isna()] = 0 + + # final step cast to int to maintain data types in population_view + newWaveTobacco["ncigs"] = newWaveTobacco["ncigs"].astype(int) + self.population_view.update(newWaveTobacco["ncigs"]) def calculate_tobacco(self, pop): @@ -127,7 +139,7 @@ def calculate_tobacco(self, pop): transition_model = r_utils.load_transitions(f"ncigs/zip/ncigs_{year}_{year + 1}", self.rpy2Modules, path=self.transition_dir) # The calculation relies on the R predict method and the model that has already been specified nextWaveTobacco = r_utils.predict_next_timestep_zip(model=transition_model, - rpy2Modules= self.rpy2Modules, + rpy2Modules=self.rpy2Modules, current=pop, dependent='ncigs') return nextWaveTobacco diff --git a/minos/validation/handovers.Rmd b/minos/validation/handovers.Rmd index 59816853..6713c616 100644 --- a/minos/validation/handovers.Rmd +++ b/minos/validation/handovers.Rmd @@ -285,6 +285,8 @@ handover_lineplots(raw.dat, test.base, 'nutrition_quality') ## Tobacco +### ncigs mean + TODO: Drop negative values ONLY from waves with no data i.e. not 7,9,11 ```{r} @@ -324,6 +326,33 @@ density_ridges(nut.spag, "ncigs", rm(raw.nut, base.nut, nut.spag) ``` +### ncigs Max + +```{r} +raw.ncigs_max <- raw.dat %>% + select(pidp, time, ncigs) %>% + group_by(time) %>% + summarise(max_ncigs = max(ncigs)) %>% + mutate(source = 'final_US') + +base.ncigs_max <- base.dat %>% + dplyr::select(pidp, time, ncigs) %>% + group_by(time) %>% + summarise(max_ncigs = max(ncigs)) %>% + mutate(source = 'baseline_output') + +max_ncigs <- rbind(raw.ncigs_max, base.ncigs_max) + +# Now plot +ggplot(data = max_ncigs, mapping = aes(x = time, y = max_ncigs, group = source, colour = source)) + + geom_line() + + geom_vline(xintercept=start.year, linetype='dotted') + + labs(title = 'Max ncigs', subtitle = 'Full Sample') + + xlab('Year') + + ylab('Average') +``` + + ## Physical Health NOTE: At present, physical health is not predicted or transitioned. From bdbf85a9653bb8e47f1acd837d7b4f9757db55bd Mon Sep 17 00:00:00 2001 From: ld-archer Date: Wed, 7 Feb 2024 16:53:37 +0000 Subject: [PATCH 136/229] Removed ChronicDisease and MaterialDeprivation from components list, and changed complete_case and generate_stock_pop to not run complete case or wave_data_copy for these vars --- config/SF12_components.txt | 1 - minos/data_generation/US_complete_case.py | 4 ++-- minos/data_generation/generate_stock_pop.py | 26 ++++++++++++++------- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/config/SF12_components.txt b/config/SF12_components.txt index a64174f1..651bd3a9 100644 --- a/config/SF12_components.txt +++ b/config/SF12_components.txt @@ -4,7 +4,6 @@ Loneliness() Tobacco() Alcohol() PhysicalActivity() -MaterialDeprivation() Neighbourhood() Heating() Housing() diff --git a/minos/data_generation/US_complete_case.py b/minos/data_generation/US_complete_case.py index 8191d6f7..d7ae0116 100755 --- a/minos/data_generation/US_complete_case.py +++ b/minos/data_generation/US_complete_case.py @@ -108,7 +108,7 @@ def cut_outliers(df, lower, upper, var): # PCS complete case vars data['chron_disease'] = data['chron_disease'].astype(int) - data = complete_case_custom_years(data, 'chron_disease', years=list(range(2011, 2022, 1))) + # data = complete_case_custom_years(data, 'chron_disease', years=list(range(2011, 2022, 1))) # PCS Vars # AUDITC (alcohol) - present in 2015, 2017, 2019, 2020 @@ -117,7 +117,7 @@ def cut_outliers(df, lower, upper, var): data = complete_case_custom_years(data, 'active', years=[2015, 2017, 2019, 2020, 2021]) # chronic disease in the all years function # matdep (Material Deprivation) - present in 2009, 2010, 2012, 2014, 2016, 2018, 2020 - data = complete_case_custom_years(data, 'matdep', years=[2009, 2010, 2012, 2014, 2016, 2018, 2020]) + # data = complete_case_custom_years(data, 'matdep', years=[2009, 2010, 2012, 2014, 2016, 2018, 2020]) drop_columns = [#'financial_situation', # these are just SF12 MICE columns for now. see US_format_raw.py 'ghq_depression', diff --git a/minos/data_generation/generate_stock_pop.py b/minos/data_generation/generate_stock_pop.py index 0daf4ba3..4be75b2a 100755 --- a/minos/data_generation/generate_stock_pop.py +++ b/minos/data_generation/generate_stock_pop.py @@ -190,15 +190,25 @@ def generate_stock(projections, cross_validation): copy_year=2020, paste_year=2021, var_type='ordinal') + # data = wave_data_copy(data, + # var='matdep', + # copy_year=2020, + # paste_year=2021, + # var_type='ordinal') + # data = wave_data_copy(data, + # var='matdep', + # copy_year=2014, + # paste_year=2015, + # var_type='ordinal') + # data = wave_data_copy(data, + # var='matdep', + # copy_year=2016, + # paste_year=2017, + # var_type='ordinal') data = wave_data_copy(data, - var='matdep', - copy_year=2020, - paste_year=2021, - var_type='ordinal') - data = wave_data_copy(data, - var='matdep', - copy_year=2014, - paste_year=2015, + var='nutrition_quality', + copy_year=2019, + paste_year=2020, var_type='ordinal') # Set loneliness and ncigs as int From 808e037434e2b5d6841426b0754aa85e6bef909e Mon Sep 17 00:00:00 2001 From: ld-archer Date: Wed, 7 Feb 2024 16:54:30 +0000 Subject: [PATCH 137/229] Testing PCS with different model types (currently RandomForest), and added some diagnostic histograms and density plots in handovers for PCS --- minos/modules/physical_wellbeing.py | 42 +++++---- minos/modules/r_utils.py | 9 +- minos/transitions/Makefile | 12 ++- .../transitions/model_definitions_default.txt | 41 ++++---- .../transitions/transition_model_functions.R | 94 +++++++++---------- minos/validation/handovers.Rmd | 43 +++++++++ 6 files changed, 155 insertions(+), 86 deletions(-) diff --git a/minos/modules/physical_wellbeing.py b/minos/modules/physical_wellbeing.py index 03421f08..6cdc9606 100644 --- a/minos/modules/physical_wellbeing.py +++ b/minos/modules/physical_wellbeing.py @@ -173,6 +173,7 @@ def setup(self, builder): # view_columns is the columns from the main population used in this module. # In this case, view_columns are taken straight from the transition model view_columns = ['pidp', + 'hidp', 'sex', 'ethnicity', 'age', @@ -210,7 +211,9 @@ def setup(self, builder): #only need to load this once for now. #self.gee_transition_model = r_utils.load_transitions(f"SF_12/lmm/SF_12_LMM", self.rpy2_modules, path=self.transition_dir) #self.gee_transition_model = r_utils.load_transitions(f"SF_12_PCS/glmm/SF_12_PCS_GLMM", self.rpy2_modules, path=self.transition_dir) - self.gee_transition_model = r_utils.load_transitions(f"SF_12_PCS/lmm/SF_12_PCS_LMM", self.rpy2_modules, + #self.gee_transition_model = r_utils.load_transitions(f"SF_12_PCS/lmm/SF_12_PCS_LMM", self.rpy2_modules, + # path=self.transition_dir) + self.rf_transition_model = r_utils.load_transitions(f"SF_12_PCS/rf/SF_12_PCS_RF", self.rpy2_modules, path=self.transition_dir) def on_time_step(self, event): @@ -233,14 +236,14 @@ def on_time_step(self, event): newWavePWB.index = pop.index #newWavePWB["SF_12_PCS"] -= 1 - # sf12_mean = np.mean(newWavePWB["SF_12_PCS"]) - # std_ratio = (11/np.std(newWavePWB["SF_12_PCS"])) - # newWavePWB["SF_12_PCS"] *= (11/np.std(newWavePWB["SF_12_PCS"])) - # newWavePWB["SF_12_PCS"] -= ((std_ratio-1)*sf12_mean) - # newWavePWB["SF_12_PCS"] -= 1.5 - # #newWavePWB["SF_12_PCS"] += (50 - np.mean(newWavePWB["SF_12_PCS"])) - # newWavePWB["SF_12_PCS"] = np.clip(newWavePWB["SF_12_PCS"], 0, 100) # keep within [0, 100] bounds of SF12. - # newWavePWB["SF_12_PCS_diff"] = newWavePWB["SF_12_PCS"] - pop["SF_12_PCS"] + sf12_mean = np.mean(newWavePWB["SF_12_PCS"]) + std_ratio = (11/np.std(newWavePWB["SF_12_PCS"])) + newWavePWB["SF_12_PCS"] *= (11/np.std(newWavePWB["SF_12_PCS"])) + newWavePWB["SF_12_PCS"] -= ((std_ratio-1)*sf12_mean) + newWavePWB["SF_12_PCS"] -= 1.5 + #newWavePWB["SF_12_PCS"] += (50 - np.mean(newWavePWB["SF_12_PCS"])) + newWavePWB["SF_12_PCS"] = np.clip(newWavePWB["SF_12_PCS"], 0, 100) # keep within [0, 100] bounds of SF12. + newWavePWB["SF_12_PCS_diff"] = newWavePWB["SF_12_PCS"] - pop["SF_12_PCS"] # Update population with new SF_12_PCS #print(np.mean(newWavePWB["SF_12_PCS"])) #print(np.std(newWavePWB["SF_12_PCS"])) @@ -255,13 +258,20 @@ def calculate_pwb(self, pop): Returns ------- """ - nextWavePWB = r_utils.predict_next_timestep_yj_gaussian_lmm(self.gee_transition_model, - self.rpy2Modules, - pop, - dependent='SF_12_PCS', - reflect=True, - yeo_johnson=False, - noise_std=1) # + + nextWavePWB = r_utils.predict_next_rf(self.rf_transition_model, + self.rpy2_modules, + pop, + dependent='SF_12_PCS', + noise_std=1) + + # nextWavePWB = r_utils.predict_next_timestep_yj_gaussian_lmm(self.gee_transition_model, + # self.rpy2_modules, + # pop, + # dependent='SF_12_PCS', + # reflect=True, + # yeo_johnson=False, + # noise_std=1) # # nextWavePWB = r_utils.predict_next_timestep_yj_gamma_glmm(self.gee_transition_model, # self.rpy2_modules, diff --git a/minos/modules/r_utils.py b/minos/modules/r_utils.py index c5c4aa7a..7751c447 100755 --- a/minos/modules/r_utils.py +++ b/minos/modules/r_utils.py @@ -504,7 +504,7 @@ def predict_next_timestep_yj_gamma_glmm(model, rpy2_modules, current, dependent, return pd.DataFrame(prediction_output, columns=[dependent]) -def predict_next_rf(model, rpy2_modules, current, dependent): +def predict_next_rf(model, rpy2_modules, current, dependent, noise_std = 1): # import R packages base = rpy2_modules['base'] @@ -517,6 +517,13 @@ def predict_next_rf(model, rpy2_modules, current, dependent): # R predict method returns a Vector of predicted values, so need to be bound to original df and converter to Pandas prediction = stats.predict(model, newdata=currentRDF) + + # Add noise to predictions + if dependent in ["SF_12_MCS", "SF_12_PCS"] and noise_std: + VGAM = rpy2_modules["VGAM"] + prediction = prediction.ro + VGAM.rlaplace(current.shape[0], 0, noise_std) # add laplace noise. + + newRPopDF = base.cbind(currentRDF, predicted=prediction) # Convert back to pandas with localconverter(ro.default_converter + pandas2ri.converter): diff --git a/minos/transitions/Makefile b/minos/transitions/Makefile index 10821b6d..e5e54e6f 100644 --- a/minos/transitions/Makefile +++ b/minos/transitions/Makefile @@ -19,14 +19,22 @@ $(TRANSITION_DATA): ############# Default ############# transitions_default: | $(TRANSITION_DATA) -transitions_default: final_data $(TRANSITION_DATA)/SF_12_MCS/glmm/SF_12_MCS_GLMM.rds $(TRANSITION_DATA)/ncigs/zip/ncigs_2019_2020.rds +transitions_default: final_data $(TRANSITION_DATA)/SF_12_PCS/rf/SF_12_PCS_RF.rds $(TRANSITION_DATA)/ncigs/zip/ncigs_2019_2020.rds +# $(TRANSITION_DATA)/SF_12_MCS/glmm/SF_12_MCS_GLMM.rds $(TRANSITION_DATA)/ncigs/zip/ncigs_2019_2020.rds: $(FINALDATA)/2021_US_cohort.csv $(TRANSITION_SOURCE)/estimate_transitions.R $(TRANSITION_SOURCE)/model_definitions_default.txt $(TRANSITION_SOURCE)/transition_model_functions.R $(RSCRIPT) $(SOURCEDIR)/transitions/estimate_transitions.R --default -$(TRANSITION_DATA)/SF_12_MCS/glmm/SF_12_MCS_GLMM.rds: $(FINALDATA)/2021_US_cohort.csv $(TRANSITION_SOURCE)/estimate_longitudinal_transitions.R $(TRANSITION_SOURCE)/model_definitions_default.txt $(TRANSITION_SOURCE)/transition_model_functions.R +$(TRANSITION_DATA)/SF_12_PCS/rf/SF_12_PCS_RF.rds: $(FINALDATA)/2021_US_cohort.csv $(TRANSITION_SOURCE)/estimate_longitudinal_transitions.R $(TRANSITION_SOURCE)/model_definitions_default.txt $(TRANSITION_SOURCE)/transition_model_functions.R $(RSCRIPT) $(SOURCEDIR)/transitions/estimate_longitudinal_transitions.R --default + +# $(TRANSITION_DATA)/SF_12_MCS/glmm/SF_12_MCS_GLMM.rds + + + + + ############# SIPHER7 ############# transitions_SIPHER7: | $(TRANSITION_DATA) diff --git a/minos/transitions/model_definitions_default.txt b/minos/transitions/model_definitions_default.txt index 85575ba0..6b9a9885 100644 --- a/minos/transitions/model_definitions_default.txt +++ b/minos/transitions/model_definitions_default.txt @@ -1,21 +1,22 @@ -LMM : SF_12_PCS ~ scale(SF_12_PCS_last) + I(scale(SF_12_PCS_last)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + scale(hh_income) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(active) + factor(auditc) + factor(matdep) + (1|pidp) + (1|hidp) + time -RF : hourly_wage ~ time + sex + age + I(age**2) + ethnicity + region + education_state + urban + job_sec -CLM : chron_disease ~ factor(chron_disease) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(marital_status) + scale(ncigs) -CLM : matdep ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(marital_status) + hhsize + factor(housing_tenure) + factor(urban) + factor(financial_situation) -LOGIT : active ~ scale(age) + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + scale(hh_income) + relevel(factor(ethnicity), ref = 'WBI') -NNET : auditc ~ scale(age) + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + scale(hh_income) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + factor(financial_situation) -CLM : housing_quality ~ scale(age) + I(scale(age)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) + I(scale(hh_income)**2) + scale(hh_income_diff) + factor(housing_tenure) -CLM : loneliness ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + relevel(factor(job_sec), ref = '3') + scale(hh_income) + relevel(factor(marital_status), ref = 'Partnered') -CLM : neighbourhood_safety ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) -NNET : education_state ~ factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') -CLM : financial_situation ~ factor(financial_situation) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + relevel(factor(job_sec), ref = '3') + scale(hh_income)+ I(scale(hh_income)**2) + factor(marital_status) + factor(housing_tenure) -LOGIT : heating ~ factor(heating) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) + factor(urban) + factor(housing_tenure) + factor(financial_situation) -LOGIT : urban ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + scale(hh_income) -NNET : S7_labour_state ~ factor(S7_labour_state) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + factor(region) + relevel(factor(education_state), ref = "1") -CLM : job_sec ~ factor(job_sec) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = '3') -NNET : housing_tenure ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_tenure) + factor(urban) + factor(financial_situation) + scale(hh_income) -GLMM : hh_income ~ scale(hh_income) + scale(hh_income_diff) + scale(age) + I(scale(age)**2) + I(scale(age)**3) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + relevel(factor(job_sec), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + (1|pidp) -LMM : nutrition_quality ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + scale(hh_income) + (1|pidp) + (1|hidp) + time +#LMM : SF_12_PCS ~ scale(SF_12_PCS_last) + I(scale(SF_12_PCS_last)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + scale(hh_income) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(active) + factor(auditc) + (1|pidp) + time +RF : SF_12_PCS ~ SF_12_PCS_last + I(SF_12_PCS_last**2) + sex + ethnicity + age + I(age**2) + region + education_state + housing_quality + hh_income + nutrition_quality + I(ncigs>0) + loneliness + active + auditc + time +#RF : hourly_wage ~ time + sex + age + I(age**2) + ethnicity + region + education_state + urban + job_sec +#CLM : chron_disease ~ factor(chron_disease) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(marital_status) + scale(ncigs) +#CLM : matdep ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(marital_status) + hhsize + factor(housing_tenure) + factor(urban) + factor(financial_situation) +#LOGIT : active ~ scale(age) + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + scale(hh_income) + relevel(factor(ethnicity), ref = 'WBI') +#NNET : auditc ~ scale(age) + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + scale(hh_income) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + factor(financial_situation) +#CLM : housing_quality ~ scale(age) + I(scale(age)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) + I(scale(hh_income)**2) + scale(hh_income_diff) + factor(housing_tenure) +#CLM : loneliness ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + relevel(factor(job_sec), ref = '3') + scale(hh_income) + relevel(factor(marital_status), ref = 'Partnered') +#CLM : neighbourhood_safety ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) +#NNET : education_state ~ factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') +#CLM : financial_situation ~ factor(financial_situation) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + relevel(factor(job_sec), ref = '3') + scale(hh_income)+ I(scale(hh_income)**2) + factor(marital_status) + factor(housing_tenure) +#LOGIT : heating ~ factor(heating) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) + factor(urban) + factor(housing_tenure) + factor(financial_situation) +#LOGIT : urban ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + scale(hh_income) +#NNET : S7_labour_state ~ factor(S7_labour_state) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + factor(region) + relevel(factor(education_state), ref = "1") +#CLM : job_sec ~ factor(job_sec) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = '3') +#NNET : housing_tenure ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_tenure) + factor(urban) + factor(financial_situation) + scale(hh_income) +#GLMM : hh_income ~ scale(hh_income) + scale(hh_income_diff) + scale(age) + I(scale(age)**2) + I(scale(age)**3) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + relevel(factor(job_sec), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + (1|pidp) +#LMM : nutrition_quality ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + scale(hh_income) + (1|pidp) + (1|hidp) + time # -GLMM : SF_12_MCS ~ scale(SF_12_MCS_last) + I(scale(SF_12_MCS_last)**2) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + scale(hh_income) + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + I(factor(ncigs>0)) + (1|pidp) -ZIP : ncigs ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) | I(factor(ncigs>0)) + relevel(factor(ethnicity), ref = 'WBI') + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + relevel(factor(job_sec), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) +#GLMM : SF_12_MCS ~ scale(SF_12_MCS_last) + I(scale(SF_12_MCS_last)**2) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + scale(hh_income) + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + I(factor(ncigs>0)) + (1|pidp) +#ZIP : ncigs ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) | I(factor(ncigs>0)) + relevel(factor(ethnicity), ref = 'WBI') + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + relevel(factor(job_sec), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) diff --git a/minos/transitions/transition_model_functions.R b/minos/transitions/transition_model_functions.R index 8bdfdae5..5df824ee 100644 --- a/minos/transitions/transition_model_functions.R +++ b/minos/transitions/transition_model_functions.R @@ -5,8 +5,8 @@ library(nnet) library(pscl) library(bestNormalize) library(lme4) -library(glmmTMB) -library(msm) +#library(glmmTMB) +#library(msm) library(randomForest) library(caret) library(doParallel) @@ -231,7 +231,7 @@ estimate_gamma_glmm <- function(data, formula, include_weights = FALSE, depend, min_value <- nanmin(data[[depend]]) data[[depend]] <- data[[depend]] - min_value + 0.001 - + if(include_weights) { model <- glmer(formula, nAGQ=0, # fast but inaccurate optimiser. nAGQ=1 takes forever.. @@ -256,42 +256,42 @@ estimate_gamma_glmm <- function(data, formula, include_weights = FALSE, depend, } estimate_longitudinal_glmm_gauss <- function(data, formula, include_weights = FALSE, depend, yeo_johnson, reflect) { - + # Sort out dependent type (factor) data <- replace.missing(data) #data <- drop_na(data) if (reflect) { max_value <- nanmax(data[[depend]]) - data[, c(depend)] <- max_value - data[, c(depend)] + data[, c(depend)] <- max_value - data[, c(depend)] } if (yeo_johnson) { yj <- yeojohnson(data[,c(depend)]) data[, c(depend)] <- predict(yj) } - + min_value <- nanmin(data[[depend]]) data[[depend]] <- data[[depend]] - min_value + 0.001 - + if (depend == 'hourly_wage') { # Histogram of hourly_wage_diff hist(data$hourly_wage_diff, main = "Distribution of hourly_wage_diff", xlab = "hourly_wage_diff") } - + if(include_weights) { - model <- lmer(formula, + model <- lmer(formula, nAGQ=0, # fast but inaccurate optimiser. nAGQ=1 takes forever.. family=gaussian(link='identity'), # Gaussian family with identity link - weights=weight, + weights=weight, data = data) } else { - model <- lmer(formula, - nAGQ=0, + model <- lmer(formula, + nAGQ=0, family=gaussian(link='identity'), data = data) } attr(model,"min_value") <- min_value - + if (yeo_johnson){ attr(model,"transform") <- yj # This is an unstable hack to add attributes to S4 class R objects. } @@ -301,7 +301,7 @@ estimate_longitudinal_glmm_gauss <- function(data, formula, include_weights = FA return(model) } -estimate_longitudinal_mlogit_gee <- function(data, formula, include_weights=FALSE, depend) +estimate_longitudinal_mlogit_gee <- function(data, formula, include_weights=FALSE, depend) { #data[[depend]] <- as.factor(data[[depend]]) data <- replace.missing(data) @@ -328,17 +328,17 @@ estimate_longitudinal_mlogit_gee <- function(data, formula, include_weights=FALS estimate_longitudinal_clmm <- function(data, formula, depend) { - + print('In the function call') - + data <- replace.missing(data) data <- drop_na(data) data[, c(depend)] <- factor(data[, c(depend)]) - + print('Before fitting the model') - + print(formula) - + model <- clmm2(formula, random=factor(pidp), link='probit', # logistic link function (can use probit or cloglog as well.) @@ -388,20 +388,20 @@ estimate_beta_glmm <- function(data, formula, depend, reflect, yeo_johnson) { } estimate_longitudinal_msm <- function(data, formula, depend, start.year) { - + data <- replace.missing(data) - + # Now set up the variable specific information the model needs such as subject, allowed transition matrix etc. - # Also needs some data wrangling to ensure that subjects have multiple waves of information, and that there + # Also needs some data wrangling to ensure that subjects have multiple waves of information, and that there # are no intermittent missing waves as the msm function cannot handle this # TODO: Impute missing intermittent waves?? if (depend == 'chron_disease') { print('Defining allowed transition matrix. Only unidirection transitions allowed.') allowed.trans.matrix <- rbind( c( 1, 1, 1 ), c( 0, 1, 1 ), c( 0, 0, 1 ) ) - + print('Preparing data for MSM model estimation...') # sort dataframe by pidp and time so observations within subjects are consecutive in the data - data <- data %>% + data <- data %>% select(pidp, time, everything()) %>% # put pidp and time at the front of the df group_by(pidp) %>% filter(n() > 1) %>% # filter individuals with only 1 observation in data @@ -412,16 +412,16 @@ estimate_longitudinal_msm <- function(data, formula, depend, start.year) { mutate(contains.na = if_any(everything(), is.na)) %>% # test for any intermittent missing (i.e. if respondent misses a wave - msm model cannot handle this) filter(!any(contains.na == TRUE)) %>% select(pidp, time, -contains.na, everything()) - + data <- data[order(data$pidp, data$time), ] # order everything by pidp and time so individuals time points are in consecutive order (msm needs this) rownames(data) <- NULL - + subj <- data$pidp } - + print('This is the formula:') print(formula) - + print('Fitting the MSM model...') # ms_object <- msm(formula = formula, # subject = subj, @@ -430,34 +430,34 @@ estimate_longitudinal_msm <- function(data, formula, depend, start.year) { # gen.inits = TRUE, # obstype = 1, # na.action = na.omit) - + ms_object <- msm(formula = formula, subject = subj, data = data) - + ms_model <- msm1(ms_object) - + return(model) } estimate_survival <- function(data, formula, depend) { - + data <- replace.missing(data) data <- drop_na(data) - + model <- survreg(formula, data = data, dist = 'extreme') - + return(model) } estimate_RandomForest <- function(data, formula, depend) { - + print('Beginning estimation of the RandomForest model. This can take a while, its probably not frozen...') - - numCores <- availableCores() / 2 - + + numCores <- availableCores() - 1 + registerDoParallel(cores = numCores) - + data <- replace.missing(data) data <- drop_na(data) @@ -465,19 +465,19 @@ estimate_RandomForest <- function(data, formula, depend) { # Train RandomForest with parallel processing fitControl <- trainControl(method = "cv", number = 5, allowParallel = TRUE, verboseIter = TRUE) set.seed(123) - + # Adjusting the model parameters to use fewer trees and limit depth - rfModel <- train(formula, data = data, + rfModel <- train(formula, data = data, method = "rf", trControl = fitControl, - tuneGrid = expand.grid(mtry = 3), + tuneGrid = expand.grid(mtry = 3), ntree = 100) # RF Parameters - + # expand.grid(mtry = ncol(data) / 3) - - - + + + #model <- randomForest(formula, data = data, ntree = 100, do.trace = TRUE) - + return(rfModel) } diff --git a/minos/validation/handovers.Rmd b/minos/validation/handovers.Rmd index 6713c616..06a0c607 100644 --- a/minos/validation/handovers.Rmd +++ b/minos/validation/handovers.Rmd @@ -167,6 +167,49 @@ density_ridges(sf12.p.spag, "SF_12_PCS", rm(raw.sf12.p, base.sf12.p, sf12.p.spag) ``` +### Histograms + +I have a hypothesis that the longer we run through simulation, the more simulants are predicted a negative PCS value which is then clipped to a 0. Histograms over a few snapshots of time should show if this is happening. + +```{r} +pcs.2021 <- base.dat %>% + select(pidp, time, SF_12_PCS) %>% + filter(time == 2021) + +# pcs.2022 <- base.dat %>% +# select(pidp, time, SF_12_PCS) %>% +# filter(time == 2022) + +pcs.2025 <- base.dat %>% + select(pidp, time, SF_12_PCS) %>% + filter(time == 2025) + +pcs.2030 <- base.dat %>% + select(pidp, time, SF_12_PCS) %>% + filter(time == 2030) + +pcs.2036 <- base.dat %>% + select(pidp, time, SF_12_PCS) %>% + filter(time == 2036) + + +merged <- rbind(pcs.2021, pcs.2025, pcs.2030, pcs.2036) +merged$time <- as.factor(merged$time) + +ggplot(merged, aes(x = SF_12_PCS, group = time, colour = time, fill = time)) + + geom_histogram() + + labs(title = 'Distribution of SF_12_PCS') + +ggplot(merged, aes(x = SF_12_PCS, group = time, colour = time, fill = time)) + + geom_freqpoly() + + labs(title = 'Distribution of SF_12_PCS', subtitle = 'Count') + +ggplot(merged, aes(x = SF_12_PCS, y = after_stat(density), group = time, colour = time, fill = time)) + + geom_freqpoly() + + labs(title = 'Distribution of SF_12_PCS', subtitle = 'Density') +``` + + ## Financial Situation ```{r} From fb095776e5a27b55b5926261cbef9e8ece0ac60c Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Fri, 9 Feb 2024 15:04:41 +0000 Subject: [PATCH 138/229] Testing different PCS models, currently on LMM with log transformation of input data --- minos/modules/physical_wellbeing.py | 61 ++++++++++++------- minos/transitions/Makefile | 7 ++- .../estimate_longitudinal_transitions.R | 14 +++-- .../transitions/model_definitions_default.txt | 7 ++- .../transitions/transition_model_functions.R | 50 ++++++++------- 5 files changed, 87 insertions(+), 52 deletions(-) diff --git a/minos/modules/physical_wellbeing.py b/minos/modules/physical_wellbeing.py index 6cdc9606..674990cf 100644 --- a/minos/modules/physical_wellbeing.py +++ b/minos/modules/physical_wellbeing.py @@ -178,6 +178,7 @@ def setup(self, builder): 'ethnicity', 'age', 'time', + 'weight', 'hh_income', 'SF_12_MCS', 'SF_12_MCS_diff', @@ -213,8 +214,12 @@ def setup(self, builder): #self.gee_transition_model = r_utils.load_transitions(f"SF_12_PCS/glmm/SF_12_PCS_GLMM", self.rpy2_modules, path=self.transition_dir) #self.gee_transition_model = r_utils.load_transitions(f"SF_12_PCS/lmm/SF_12_PCS_LMM", self.rpy2_modules, # path=self.transition_dir) - self.rf_transition_model = r_utils.load_transitions(f"SF_12_PCS/rf/SF_12_PCS_RF", self.rpy2_modules, - path=self.transition_dir) + #self.rf_transition_model = r_utils.load_transitions(f"SF_12_PCS/rf/SF_12_PCS_RF", self.rpy2_modules, + # path=self.transition_dir) + #self.glmmb_transition_model = r_utils.load_transitions(f"SF_12_PCS/glmmb/SF_12_PCS_GLMMB", self.rpy2_modules, + # path=self.transition_dir) + self.lmm_transition_model = r_utils.load_transitions(f"SF_12_PCS/lmm/SF_12_PCS_LMM", self.rpy2_modules, + path=self.transition_dir) def on_time_step(self, event): """Produces new children and updates parent status on time steps. @@ -227,22 +232,30 @@ def on_time_step(self, event): self.year = event.time.year # Get living people to update their income pop = self.population_view.get(event.index, query="alive =='alive'") - pop = pop.sort_values('pidp') #sorting aligns index to make sure individual gets their correct prediction. + pop = pop.sort_values('pidp') # sorting aligns index to make sure individual gets their correct prediction. pop["SF_12_PCS_last"] = pop["SF_12_PCS"] + # Calculate min and max values for clipping later + min_PCS = min(pop['SF_12_PCS']) + max_PCS = max(pop['SF_12_PCS']) # Predict next mwb value newWavePWB = pd.DataFrame(columns=['SF_12_PCS']) - newWavePWB['SF_12_PCS'] = self.calculate_pwb(pop) + newWavePWB['SF_12_PCS'] = self.calculate_pwb(pop.copy()) newWavePWB.index = pop.index #newWavePWB["SF_12_PCS"] -= 1 + ### This chunk is to increase variance sf12_mean = np.mean(newWavePWB["SF_12_PCS"]) - std_ratio = (11/np.std(newWavePWB["SF_12_PCS"])) - newWavePWB["SF_12_PCS"] *= (11/np.std(newWavePWB["SF_12_PCS"])) + std_ratio = (9.8/np.std(newWavePWB["SF_12_PCS"])) + newWavePWB["SF_12_PCS"] *= (9.8/np.std(newWavePWB["SF_12_PCS"])) newWavePWB["SF_12_PCS"] -= ((std_ratio-1)*sf12_mean) newWavePWB["SF_12_PCS"] -= 1.5 - #newWavePWB["SF_12_PCS"] += (50 - np.mean(newWavePWB["SF_12_PCS"])) - newWavePWB["SF_12_PCS"] = np.clip(newWavePWB["SF_12_PCS"], 0, 100) # keep within [0, 100] bounds of SF12. + newWavePWB["SF_12_PCS"] += (49.3 - np.mean(newWavePWB["SF_12_PCS"])) + #newWavePWB["SF_12_PCS"] = np.clip(newWavePWB["SF_12_PCS"], 0, 100) # keep within [0, 100] bounds of SF12. + + # Clip to minimum and maximum values seen in current wave + #newWavePWB["SF_12_PCS"] = np.clip(newWavePWB["SF_12_PCS"], min_PCS, max_PCS) + newWavePWB["SF_12_PCS_diff"] = newWavePWB["SF_12_PCS"] - pop["SF_12_PCS"] # Update population with new SF_12_PCS #print(np.mean(newWavePWB["SF_12_PCS"])) @@ -259,19 +272,25 @@ def calculate_pwb(self, pop): ------- """ - nextWavePWB = r_utils.predict_next_rf(self.rf_transition_model, - self.rpy2_modules, - pop, - dependent='SF_12_PCS', - noise_std=1) - - # nextWavePWB = r_utils.predict_next_timestep_yj_gaussian_lmm(self.gee_transition_model, - # self.rpy2_modules, - # pop, - # dependent='SF_12_PCS', - # reflect=True, - # yeo_johnson=False, - # noise_std=1) # + # nextWavePWB = r_utils.predict_next_timestep_beta_glmm(self.glmmb_transition_model, + # self.rpy2_modules, + # pop, + # dependent='SF_12_PCS', + # reflect=True, + # noise_std=0.03) + + # nextWavePWB = r_utils.predict_next_rf(self.rf_transition_model, + # self.rpy2_modules, + # pop, + # dependent='SF_12_PCS', + # noise_std=1) + + nextWavePWB = r_utils.predict_next_timestep_yj_gaussian_lmm(self.lmm_transition_model, + self.rpy2_modules, + pop, + dependent='SF_12_PCS', + log_transform=True, + noise_std=0.03) # # nextWavePWB = r_utils.predict_next_timestep_yj_gamma_glmm(self.gee_transition_model, # self.rpy2_modules, diff --git a/minos/transitions/Makefile b/minos/transitions/Makefile index e5e54e6f..9ddb42f8 100644 --- a/minos/transitions/Makefile +++ b/minos/transitions/Makefile @@ -19,17 +19,18 @@ $(TRANSITION_DATA): ############# Default ############# transitions_default: | $(TRANSITION_DATA) -transitions_default: final_data $(TRANSITION_DATA)/SF_12_PCS/rf/SF_12_PCS_RF.rds $(TRANSITION_DATA)/ncigs/zip/ncigs_2019_2020.rds +transitions_default: final_data $(TRANSITION_DATA)/SF_12_PCS/lmm/SF_12_PCS_LMM.rds $(TRANSITION_DATA)/ncigs/zip/ncigs_2020_2021.rds # $(TRANSITION_DATA)/SF_12_MCS/glmm/SF_12_MCS_GLMM.rds -$(TRANSITION_DATA)/ncigs/zip/ncigs_2019_2020.rds: $(FINALDATA)/2021_US_cohort.csv $(TRANSITION_SOURCE)/estimate_transitions.R $(TRANSITION_SOURCE)/model_definitions_default.txt $(TRANSITION_SOURCE)/transition_model_functions.R +$(TRANSITION_DATA)/ncigs/zip/ncigs_2020_2021.rds: $(FINALDATA)/2021_US_cohort.csv $(TRANSITION_SOURCE)/estimate_transitions.R $(TRANSITION_SOURCE)/model_definitions_default.txt $(TRANSITION_SOURCE)/transition_model_functions.R $(RSCRIPT) $(SOURCEDIR)/transitions/estimate_transitions.R --default -$(TRANSITION_DATA)/SF_12_PCS/rf/SF_12_PCS_RF.rds: $(FINALDATA)/2021_US_cohort.csv $(TRANSITION_SOURCE)/estimate_longitudinal_transitions.R $(TRANSITION_SOURCE)/model_definitions_default.txt $(TRANSITION_SOURCE)/transition_model_functions.R +$(TRANSITION_DATA)/SF_12_PCS/lmm/SF_12_PCS_LMM.rds: $(FINALDATA)/2021_US_cohort.csv $(TRANSITION_SOURCE)/estimate_longitudinal_transitions.R $(TRANSITION_SOURCE)/model_definitions_default.txt $(TRANSITION_SOURCE)/transition_model_functions.R $(RSCRIPT) $(SOURCEDIR)/transitions/estimate_longitudinal_transitions.R --default # $(TRANSITION_DATA)/SF_12_MCS/glmm/SF_12_MCS_GLMM.rds +#$(TRANSITION_DATA)/SF_12_PCS/rf/SF_12_PCS_RF.rds diff --git a/minos/transitions/estimate_longitudinal_transitions.R b/minos/transitions/estimate_longitudinal_transitions.R index 62647429..1e8bf456 100644 --- a/minos/transitions/estimate_longitudinal_transitions.R +++ b/minos/transitions/estimate_longitudinal_transitions.R @@ -92,18 +92,24 @@ run_longitudinal_models <- function(transitionDir_path, transitionSourceDir_path use.weights <- TRUE } - if (dependent %in% c("SF_12_MCS", 'SF_12_PCS')) { + if (dependent %in% c("SF_12_MCS")) { # 'SF_12_PCS' do.reflect = TRUE # only SF12 continuous data is reflected to be left skewed. } else { do.reflect=FALSE } - if (dependent %in% c("SF_12_MCS", 'SF_12_PCS', 'hh_income')) { + if (dependent %in% c("SF_12_MCS", 'hh_income')) { # 'SF_12_PCS' do.yeo.johnson = T # } else { do.yeo.johnson = F } + + if (dependent %in% c("SF_12_PCS")) { + do.log.transform <- T + } else { + do.log.transform <- F + } # experimental ordinal long models. ignore. # if (mod.type == "ORDGEE") { @@ -201,6 +207,7 @@ run_longitudinal_models <- function(transitionDir_path, transitionSourceDir_path include_weights = use.weights, reflect = do.reflect, yeo_johnson = F, + log_transform = do.log.transform, depend = dependent) } else if(tolower(mod.type) == 'lmm_diff') { @@ -231,8 +238,7 @@ run_longitudinal_models <- function(transitionDir_path, transitionSourceDir_path model <- estimate_beta_glmm(data = sorted_df, formula = form, depend = dependent, - reflect = do.reflect, - yeo_johnson = do.yeo.johnson) + reflect = TRUE) } else if (tolower(mod.type) == 'msm') { diff --git a/minos/transitions/model_definitions_default.txt b/minos/transitions/model_definitions_default.txt index 6b9a9885..620d956b 100644 --- a/minos/transitions/model_definitions_default.txt +++ b/minos/transitions/model_definitions_default.txt @@ -1,5 +1,6 @@ -#LMM : SF_12_PCS ~ scale(SF_12_PCS_last) + I(scale(SF_12_PCS_last)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + scale(hh_income) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(active) + factor(auditc) + (1|pidp) + time -RF : SF_12_PCS ~ SF_12_PCS_last + I(SF_12_PCS_last**2) + sex + ethnicity + age + I(age**2) + region + education_state + housing_quality + hh_income + nutrition_quality + I(ncigs>0) + loneliness + active + auditc + time +LMM : SF_12_PCS ~ scale(SF_12_PCS_last) + I(scale(SF_12_PCS_last)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + scale(hh_income) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(active) + factor(auditc) + (1|pidp) + time +#RF : SF_12_PCS ~ SF_12_PCS_last + I(SF_12_PCS_last**2) + sex + ethnicity + age + I(age**2) + region + education_state + housing_quality + hh_income + nutrition_quality + I(ncigs>0) + loneliness + active + auditc + time +#GLMMB : SF_12_PCS ~ scale(SF_12_PCS_last) + I(scale(SF_12_PCS_last)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + scale(hh_income) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(active) + factor(auditc) + (1|pidp) + time #RF : hourly_wage ~ time + sex + age + I(age**2) + ethnicity + region + education_state + urban + job_sec #CLM : chron_disease ~ factor(chron_disease) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(marital_status) + scale(ncigs) #CLM : matdep ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(marital_status) + hhsize + factor(housing_tenure) + factor(urban) + factor(financial_situation) @@ -19,4 +20,4 @@ RF : SF_12_PCS ~ SF_12_PCS_last + I(SF_12_PCS_last**2) + sex + ethnicity + age + #LMM : nutrition_quality ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + scale(hh_income) + (1|pidp) + (1|hidp) + time # #GLMM : SF_12_MCS ~ scale(SF_12_MCS_last) + I(scale(SF_12_MCS_last)**2) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + scale(hh_income) + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + I(factor(ncigs>0)) + (1|pidp) -#ZIP : ncigs ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) | I(factor(ncigs>0)) + relevel(factor(ethnicity), ref = 'WBI') + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + relevel(factor(job_sec), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) +ZIP : ncigs ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) | I(factor(ncigs>0)) + relevel(factor(ethnicity), ref = 'WBI') + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + relevel(factor(job_sec), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) diff --git a/minos/transitions/transition_model_functions.R b/minos/transitions/transition_model_functions.R index 5df824ee..6e44d193 100644 --- a/minos/transitions/transition_model_functions.R +++ b/minos/transitions/transition_model_functions.R @@ -5,7 +5,7 @@ library(nnet) library(pscl) library(bestNormalize) library(lme4) -#library(glmmTMB) +library(glmmTMB) #library(msm) library(randomForest) library(caret) @@ -149,11 +149,13 @@ estimate_yearly_zip <- function(data, formula, include_weights = FALSE, depend) nanmax <- function(x) { ifelse( !all(is.na(x)), max(x, na.rm=T), NA) } nanmin <- function(x) { ifelse( !all(is.na(x)), min(x, na.rm=T), NA) } -estimate_longitudinal_lmm <- function(data, formula, include_weights = FALSE, depend, yeo_johnson, reflect) { +estimate_longitudinal_lmm <- function(data, formula, include_weights = FALSE, depend, yeo_johnson, log_transform, reflect) { data <- replace.missing(data) #data <- drop_na(data) max_value <- nanmax(data[[depend]]) + min_value <- nanmin(data[[depend]]) + if (reflect) { data[, c(depend)] <- max_value - data[, c(depend)] } @@ -161,6 +163,12 @@ estimate_longitudinal_lmm <- function(data, formula, include_weights = FALSE, de yj <- yeojohnson(data[,c(depend)]) data[, c(depend)] <- predict(yj) } + + # LA 8/2/24 + ## Log Normal Transformation for PCS + if (log_transform) { + data[[depend]] <- log(data[[depend]]) + } if(include_weights) { model <- lmer(formula, @@ -176,6 +184,14 @@ estimate_longitudinal_lmm <- function(data, formula, include_weights = FALSE, de if (reflect) { attr(model,"max_value") <- max_value # Works though. } + + ## LA 9/2/24 + # Saving min and max value from input data for clipping in r_utils function + if (depend == 'SF_12_PCS') { + attr(model,"min_value") <- min_value + attr(model,"max_value") <- max_value + } + #browser() #model@transform <- yj #model@min_value <- min_value @@ -348,38 +364,30 @@ estimate_longitudinal_clmm <- function(data, formula, depend) return (model) } -estimate_beta_glmm <- function(data, formula, depend, reflect, yeo_johnson) { +estimate_beta_glmm <- function(data, formula, depend, reflect) { + # Beta family for GLMM is for data in interval 0-1 only. It can model + # fractional data data <- replace.missing(data) - data <- drop_na(data) - + #data <- drop_na(data) + if (reflect) { - max_value <- nanmax(data[[depend]]) + max_value <- nanmax(data[[depend]]) - 0.01 data[, c(depend)] <- max_value - data[, c(depend)] } - if (yeo_johnson) - { - yj <- yeojohnson(data[,c(depend)]) - data[, c(depend)] <- predict(yj) + + # Convert data to range 0-1 + if (depend %in% c('SF_12_MCS', 'SF_12_PCS')) { + data[[depend]] = data[[depend]] / 100 } - min_value <- nanmin(data[[depend]]) - #data[[depend]] <- data[[depend]] - min_value + 0.001 - #data[[depend]][data[[depend]] == 1] = 0.999 - #data[[depend]][data[[depend]] == 0] = 0.001 - model <- glmmTMB(formula, data = data, family = beta_family(link = 'logit'), weights = weight, na.action = na.omit, verbose = TRUE) - - attr(model,"min_value") <- min_value - - if (yeo_johnson){ - attr(model,"transform") <- yj # This is an unstable hack to add attributes to S4 class R objects. - } + if (reflect) { attr(model,"max_value") <- max_value # Works though. } From 8446e3db303e031803238f77c89477f5f5865516 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Fri, 9 Feb 2024 15:06:49 +0000 Subject: [PATCH 139/229] Changed some prediction function arguments in r_utils and updated the function calls in modules. Also commented out most spaghetti plots for handovers speed --- minos/modules/income.py | 1 - minos/modules/mental_wellbeing.py | 1 - minos/modules/nutrition.py | 11 +- minos/modules/r_utils.py | 135 +++++++++++----- minos/validation/handovers.Rmd | 260 +++++++++++++++--------------- 5 files changed, 234 insertions(+), 174 deletions(-) diff --git a/minos/modules/income.py b/minos/modules/income.py index 4b362193..03e53a85 100755 --- a/minos/modules/income.py +++ b/minos/modules/income.py @@ -710,7 +710,6 @@ def calculate_income(self, pop): dependent='hh_income_new', yeo_johnson=True, reflect=False, - mod_type='gamma', noise_std= 0.175)#0.45 for yj. 100? for non yj. # get new hh income diffs and update them into history_data. #self.update_history_dataframe(pop, self.year-1) diff --git a/minos/modules/mental_wellbeing.py b/minos/modules/mental_wellbeing.py index 83569337..70109da5 100755 --- a/minos/modules/mental_wellbeing.py +++ b/minos/modules/mental_wellbeing.py @@ -552,7 +552,6 @@ def calculate_mwb(self, pop): dependent='SF_12_MCS', reflect=True, yeo_johnson= True, - mod_type='gamma', noise_std= 0.1) # 5 for non yj, 0.35 for yj return out_data diff --git a/minos/modules/nutrition.py b/minos/modules/nutrition.py index 383dd597..9d7c8365 100755 --- a/minos/modules/nutrition.py +++ b/minos/modules/nutrition.py @@ -236,12 +236,11 @@ def calculate_nutrition(self, pop): ------- """ nextWaveNutrition = r_utils.predict_next_timestep_yj_gaussian_lmm(self.gee_transition_model, - self.rpy2Modules, - pop, - dependent='nutrition_quality_new', - reflect=False, - yeo_johnson= False, - noise_std=1)# + self.rpy2Modules, + pop, + dependent='nutrition_quality_new', + log_transform=False, + noise_std=1) return nextWaveNutrition diff --git a/minos/modules/r_utils.py b/minos/modules/r_utils.py index 7751c447..18c88566 100755 --- a/minos/modules/r_utils.py +++ b/minos/modules/r_utils.py @@ -7,7 +7,7 @@ import rpy2.robjects as ro from rpy2.robjects import pandas2ri, r from rpy2.robjects.conversion import localconverter -from rpy2.robjects.vectors import FactorVector +from rpy2.robjects.vectors import FactorVector, FloatVector from rpy2.robjects import numpy2ri import pandas as pd import numpy as np @@ -337,7 +337,7 @@ def predict_next_timestep_gee(model, rpy2_modules, current, dependent, noise_std return newPandasPopDF[[dependent]] -def predict_next_timestep_yj_gaussian_lmm(model, rpy2_modules, current, dependent, reflect, yeo_johnson, noise_std = 0): +def predict_next_timestep_yj_gaussian_lmm(model, rpy2_modules, current, dependent, log_transform, noise_std = 0): """ This function will take the transition model loaded in load_transitions() and use it to predict the next timestep for a module. @@ -367,25 +367,18 @@ def predict_next_timestep_yj_gaussian_lmm(model, rpy2_modules, current, dependen with localconverter(ro.default_converter + pandas2ri.converter): currentRDF = ro.conversion.py2rpy(current) - - # Inverse yeojohnson transform. - # Stored in estimate_transitions.R. - # Note inverse=True arg is needed to get back to actual gross income. - if reflect: + if dependent == "SF_12_PCS": max_value = model.do_slot("max_value") + min_value = model.do_slot("min_value") - currentRDF[currentRDF.names.index(dependent)] = max_value.ro - currentRDF.rx2(dependent) - - if yeo_johnson: - # stupid workaround to get attributes from R S4 type objects. Replaces rx2. - yj = model.do_slot("transform") - #yj = model.rx2('transform') # use yj from fitted model for latest year. - currentRDF[currentRDF.names.index(dependent)] = stats.predict(yj, newdata=currentRDF.rx2(dependent)) # apply yj transform + if log_transform: + # log transformation currently only for PCS + currentRDF[currentRDF.names.index(dependent)] = base.log(currentRDF.rx2(dependent)) # explicitly convert to matrix to overcome error in predict_merMod below #currentRDF_matrix = matrix.as_matrix(currentRDF) - ols_data = lme4.predict_merMod(model, currentRDF, type='response', allow_new_levels=True) # estimate next income using OLS. + prediction = lme4.predict_merMod(model, currentRDF, type='response', allow_new_levels=True) # estimate next income using OLS. # if dependent == "SF_12": # ols_data = ols_data.ro + stats.rnorm(n, 0, noise_std) # add gaussian noise. @@ -397,31 +390,40 @@ def predict_next_timestep_yj_gaussian_lmm(model, rpy2_modules, current, dependen # #ols_data = ols_data.ro + stats.rcauchy(n, 0, noise_std) # ols_data = ols_data.ro + stats.rnorm(n, 0, noise_std) # add gaussian noise. - valid_dependents = ['hh_income', 'hh_income_new', 'nutrition_quality_new', 'nutrition_quality', 'nutrition_quality_diff'] + # if dependent == "SF_12_PCS": + # dependent_list = list(prediction.rx2(dependent)) + # print("After noise added:") + # print(min(dependent_list)) + # print(max(dependent_list)) + + if log_transform: + prediction = base.exp(prediction) + + + valid_dependents = ['hh_income', 'hh_income_new', 'nutrition_quality_new', 'nutrition_quality', + 'nutrition_quality_diff', 'SF_12_PCS'] if dependent == "SF_12" and noise_std: - prediction = ols_data.ro + stats.rnorm(current.shape[0], 0, noise_std) # add gaussian noise. + prediction = prediction.ro + stats.rnorm(current.shape[0], 0, noise_std) # add gaussian noise. elif (dependent in valid_dependents) and noise_std: VGAM = rpy2_modules["VGAM"] - prediction = ols_data.ro + VGAM.rlaplace(current.shape[0], 0, noise_std) # add gaussian noise. + prediction = prediction.ro + VGAM.rlaplace(current.shape[0], 0, noise_std) # add gaussian noise. else: - prediction = ols_data # no noise is added. - - if yeo_johnson: - prediction = stats.predict(yj, newdata=prediction, inverse=True) # invert yj transform. - - if reflect: - prediction = max_value.ro - prediction + prediction = prediction # no noise is added. # R predict method returns a Vector of predicted values, so need to be bound to original df and converter to Pandas # Convert back to pandas with localconverter(ro.default_converter + pandas2ri.converter): #ols_data = ro.conversion.rpy2py(ols_data) - prediction_output = ro.conversion.rpy2py(prediction) + prediction = ro.conversion.rpy2py(prediction) - return pd.DataFrame(prediction_output, columns=[dependent]) + if dependent == "SF_12_PCS": + # Final step is to clip the values to min and max seen in input data + prediction = np.clip(prediction, min_value, max_value) + + return pd.DataFrame(prediction, columns=[dependent]) -def predict_next_timestep_yj_gamma_glmm(model, rpy2_modules, current, dependent, reflect, yeo_johnson, mod_type, noise_std = 1): +def predict_next_timestep_yj_gamma_glmm(model, rpy2_modules, current, dependent, reflect, yeo_johnson, noise_std = 1): """ This function will take the transition model loaded in load_transitions() and use it to predict the next timestep for a module. @@ -462,14 +464,10 @@ def predict_next_timestep_yj_gamma_glmm(model, rpy2_modules, current, dependent, # get minimum value to reverse transformatino to strictly positve values. min_value = model.do_slot("min_value") - if mod_type == 'gamma': - prediction = lme4.predict_merMod(model, newdata=currentRDF, type='response', - allow_new_levels=True) # estimate next income using gamma GEE. - # Inverting transforms to get back to true income values. - prediction = prediction.ro + (min_value.ro - 0.001) # invert shift to strictly positive values. - elif mod_type == 'beta': - prediction = stats.predict(model, newdata=currentRDF, type='response', - allow_new_levels=True) # estimate next income using gamma GEE. + prediction = lme4.predict_merMod(model, newdata=currentRDF, type='response', + allow_new_levels=True) # estimate next income using gamma GEE. + # Inverting transforms to get back to true income values. + prediction = prediction.ro + (min_value.ro - 0.001) # invert shift to strictly positive values. @@ -504,6 +502,71 @@ def predict_next_timestep_yj_gamma_glmm(model, rpy2_modules, current, dependent, return pd.DataFrame(prediction_output, columns=[dependent]) +def predict_next_timestep_beta_glmm(model, rpy2_modules, current, dependent, reflect, noise_std = 1): + """ + This function will take the transition model loaded in load_transitions() and use it to predict the next timestep + for a module. + Parameters + ---------- + model : R rds object + Fitted model loaded in from .rds file + current : vivarium.framework.population.PopulationView + View including columns that are required for prediction + dependent : str + The independent variable we are trying to predict + Returns: + ------- + A prediction of the information for next timestep + """ + # import R packages + base = rpy2_modules['base'] + stats = rpy2_modules['stats'] + glmmTMB = rpy2_modules["glmmTMB"] + + # Convert from pandas to R using package converter + with localconverter(ro.default_converter + pandas2ri.converter): + currentRDF = ro.conversion.py2rpy(current) + + # flip left skewed data to right skewed about its maximum. + if reflect: + max_value = model.do_slot("max_value") + max_value = FloatVector(max_value) + currentRDF[currentRDF.names.index(dependent)] = max_value.ro - currentRDF.rx2(dependent) + + # convert PCS to 0-1 range for beta family + currentRDF[currentRDF.names.index(dependent)] = currentRDF.rx2(dependent) / FloatVector([100]).ro + #current[dependent] = current[dependent] / 100 + + prediction = stats.predict(model, + newdata=currentRDF, + type='response', + allow_new_levels=True) # estimate next income using beta GLMM. + + + if dependent in ["SF_12_MCS", "SF_12_PCS"] and noise_std: + VGAM = rpy2_modules["VGAM"] + prediction = prediction.ro + VGAM.rlaplace(current.shape[0], 0, noise_std) # add gaussian noise. + else: + prediction = prediction + + # convert PCS back to 0-100 range for real values + #currentRDF[currentRDF.names.index(dependent)] = currentRDF.rx2(dependent) * FloatVector([100]).ro + + if reflect: + prediction = max_value.ro - prediction + + # R predict method returns a Vector of predicted values, so need to be bound to original df and converter to Pandas + # Convert back to pandas + with localconverter(ro.default_converter + pandas2ri.converter): + prediction_output = ro.conversion.rpy2py(prediction) + + # Convert prediction back to 0-100 scale + prediction = pd.DataFrame(prediction_output, columns=[dependent]) + prediction[dependent] = prediction[dependent] * 100 + + return prediction + + def predict_next_rf(model, rpy2_modules, current, dependent, noise_std = 1): # import R packages diff --git a/minos/validation/handovers.Rmd b/minos/validation/handovers.Rmd index 06a0c607..a4991387 100644 --- a/minos/validation/handovers.Rmd +++ b/minos/validation/handovers.Rmd @@ -82,27 +82,27 @@ handover_lineplots(raw.dat, base.dat, "hh_income") ### Spaghetti ```{r} -raw.inc <- raw.dat %>% - select(pidp, age, time, hh_income) -base.inc <- base.dat %>% - select(pidp, age, time, hh_income) - -income.spag <- rbind(raw.inc, base.inc) - -density_ridges(income.spag, 'hh_income', - save=shall.we.save, - save.path=save.path) - -pidp_sample <- sample(unique(income.spag$pidp), size=nrow(raw.inc)/10, replace=FALSE) -income.spag.sample <- income.spag[which(income.spag$pidp %in% pidp_sample), ] -spaghetti_plot(income.spag.sample, 'hh_income', - save = shall.we.save, - save.path = save.path) -spaghetti_highlight_max_plot(income.spag.sample, 'hh_income', - save = shall.we.save, - save.path = save.path) - -rm(raw.inc, base.inc, income.spag) +# raw.inc <- raw.dat %>% +# select(pidp, age, time, hh_income) +# base.inc <- base.dat %>% +# select(pidp, age, time, hh_income) +# +# income.spag <- rbind(raw.inc, base.inc) +# +# density_ridges(income.spag, 'hh_income', +# save=shall.we.save, +# save.path=save.path) +# +# pidp_sample <- sample(unique(income.spag$pidp), size=nrow(raw.inc)/10, replace=FALSE) +# income.spag.sample <- income.spag[which(income.spag$pidp %in% pidp_sample), ] +# spaghetti_plot(income.spag.sample, 'hh_income', +# save = shall.we.save, +# save.path = save.path) +# spaghetti_highlight_max_plot(income.spag.sample, 'hh_income', +# save = shall.we.save, +# save.path = save.path) +# +# rm(raw.inc, base.inc, income.spag) ``` @@ -350,23 +350,23 @@ handover_lineplots(raw.dat, test.base, 'ncigs') ### Spaghetti ```{r} -raw.nut <- raw.dat %>% - select(pidp, age, time, ncigs) -base.nut <- base.dat %>% - select(pidp, age, time, ncigs) - -nut.spag <- rbind(raw.nut, base.nut) - -spaghetti_plot(nut.spag, 'ncigs', - save = shall.we.save, - save.path = save.path) - - -density_ridges(nut.spag, "ncigs", - save = FALSE, - save.path = save.path) - -rm(raw.nut, base.nut, nut.spag) +# raw.nut <- raw.dat %>% +# select(pidp, age, time, ncigs) +# base.nut <- base.dat %>% +# select(pidp, age, time, ncigs) +# +# nut.spag <- rbind(raw.nut, base.nut) +# +# spaghetti_plot(nut.spag, 'ncigs', +# save = shall.we.save, +# save.path = save.path) +# +# +# density_ridges(nut.spag, "ncigs", +# save = FALSE, +# save.path = save.path) +# +# rm(raw.nut, base.nut, nut.spag) ``` ### ncigs Max @@ -641,20 +641,20 @@ ggplot(raw.2020.cd, aes(x = age_group, y = perc, group = chron_disease, colour = ``` ```{r} -raw.s <- raw.dat %>% - select(pidp, age, time, matdep) %>% - filter(!matdep %in% miss.values) -base.s <- base.dat %>% - select(pidp, age, time, matdep) %>% - filter(!matdep %in% miss.values) - -spag <- rbind(raw.s, base.s) - -spaghetti_plot(spag, 'matdep', - save = shall.we.save, - save.path = save.path) - -rm(raw.s, base.s, spag) +# raw.s <- raw.dat %>% +# select(pidp, age, time, matdep) %>% +# filter(!matdep %in% miss.values) +# base.s <- base.dat %>% +# select(pidp, age, time, matdep) %>% +# filter(!matdep %in% miss.values) +# +# spag <- rbind(raw.s, base.s) +# +# spaghetti_plot(spag, 'matdep', +# save = shall.we.save, +# save.path = save.path) +# +# rm(raw.s, base.s, spag) ``` ## Chronic Disease @@ -665,35 +665,35 @@ handover_ordinal(raw.dat, base.dat, var = 'chron_disease', save = shall.we.save) ```{r} -raw.s <- raw.dat %>% - select(pidp, age, time, chron_disease) %>% - filter(!chron_disease %in% miss.values) -base.s <- base.dat %>% - select(pidp, age, time, chron_disease) %>% - filter(!chron_disease %in% miss.values) - -spag <- rbind(raw.s, base.s) - -spag$chron_disease <- as.numeric(spag$chron_disease) - -spaghetti_plot(spag, 'chron_disease', - save = shall.we.save, - save.path = save.path) - -density_ridges(spag, 'chron_disease', - save=shall.we.save, - save.path=save.path) - -pidp_sample <- sample(unique(spag$pidp), size=nrow(raw.s)/10, replace=FALSE) -spag.sample <- spag[which(spag$pidp %in% pidp_sample), ] -spaghetti_plot(spag.sample, 'chron_disease', - save = shall.we.save, - save.path = save.path) -spaghetti_highlight_max_plot(spag.sample, 'chron_disease', - save = shall.we.save, - save.path = save.path) - -rm(raw.s, base.s, spag) +# raw.s <- raw.dat %>% +# select(pidp, age, time, chron_disease) %>% +# filter(!chron_disease %in% miss.values) +# base.s <- base.dat %>% +# select(pidp, age, time, chron_disease) %>% +# filter(!chron_disease %in% miss.values) +# +# spag <- rbind(raw.s, base.s) +# +# spag$chron_disease <- as.numeric(spag$chron_disease) +# +# spaghetti_plot(spag, 'chron_disease', +# save = shall.we.save, +# save.path = save.path) +# +# density_ridges(spag, 'chron_disease', +# save=shall.we.save, +# save.path=save.path) +# +# pidp_sample <- sample(unique(spag$pidp), size=nrow(raw.s)/10, replace=FALSE) +# spag.sample <- spag[which(spag$pidp %in% pidp_sample), ] +# spaghetti_plot(spag.sample, 'chron_disease', +# save = shall.we.save, +# save.path = save.path) +# spaghetti_highlight_max_plot(spag.sample, 'chron_disease', +# save = shall.we.save, +# save.path = save.path) +# +# rm(raw.s, base.s, spag) ``` ## Hourly Wage @@ -704,58 +704,58 @@ handover_lineplots(raw.dat, base.dat, 'hourly_wage') ``` ```{r} -raw.s <- raw.dat %>% - select(pidp, age, time, hourly_wage) -base.s <- base.dat %>% - select(pidp, age, time, hourly_wage) - -spag <- rbind(raw.s, base.s) - -spaghetti_plot(spag, 'hourly_wage', - save = shall.we.save, - save.path = save.path) - -# now a version limited to 0-100 -spag <- spag %>% - filter(hourly_wage <= 300) -spaghetti_plot(spag, 'hourly_wage', - save = shall.we.save, - save.path = save.path) - -rm(raw.s, base.s, spag) +# raw.s <- raw.dat %>% +# select(pidp, age, time, hourly_wage) +# base.s <- base.dat %>% +# select(pidp, age, time, hourly_wage) +# +# spag <- rbind(raw.s, base.s) +# +# spaghetti_plot(spag, 'hourly_wage', +# save = shall.we.save, +# save.path = save.path) +# +# # now a version limited to 0-100 +# spag <- spag %>% +# filter(hourly_wage <= 300) +# spaghetti_plot(spag, 'hourly_wage', +# save = shall.we.save, +# save.path = save.path) +# +# rm(raw.s, base.s, spag) ``` ```{r} -# some basic statistics about hourly_wage variable -print("Some basic statistics on hourly_wage. These are all for 2020.") -raw.2020 <- raw.dat %>% filter(time == 2020) -print('All living people in 2020') -print(paste0("Number of people alive in 2020: ", nrow(raw.2020))) -# number individuals with an hourly_wage > 0 -print('People not in work') -print(paste0("Number of individuals not in paid work: ", sum(!raw.2020$S7_labour_state %in% c('FT Employed', 'PT Employed')))) -print(paste0("Number of individuals with hourly_wage == 0: ", sum(raw.2020$hourly_wage == 0))) - -# Number in work -print('People in work') -print(paste0("Number of individuals in paid work: ", sum(raw.2020$S7_labour_state %in% c('FT Employed', 'PT Employed')))) -print(paste0("Number of individuals with hourly_wage > 0: ", sum(raw.2020$hourly_wage > 0))) - -# Extreme hourly_wage values -print('Extreme hourly wage values: HIGH') -print(paste0("Number of individuals with hourly_wage > 30: ", sum(raw.2020$hourly_wage > 30))) -print(paste0("Number of individuals with hourly_wage > 50: ", sum(raw.2020$hourly_wage > 50))) -print(paste0("Number of individuals with hourly_wage > 100: ", sum(raw.2020$hourly_wage > 100))) -print(paste0("Number of individuals with hourly_wage > 500: ", sum(raw.2020$hourly_wage > 500))) -print(paste0("Number of individuals with hourly_wage > 1000: ", sum(raw.2020$hourly_wage > 1000))) -print(paste0("Number of individuals with hourly_wage > 2000: ", sum(raw.2020$hourly_wage > 2000))) - -raw.2020.work <- raw.2020 %>% filter(hourly_wage > 0) -print('Extreme hourly wage values: LOW (non-zero)') -print(paste0("Number of individuals with hourly_wage < 20: ", sum(raw.2020.work$hourly_wage < 20))) -print(paste0("Number of individuals with hourly_wage < 10: ", sum(raw.2020.work$hourly_wage < 10))) -print(paste0("Number of individuals with hourly_wage < 5: ", sum(raw.2020.work$hourly_wage < 5))) -print(paste0("Number of individuals with hourly_wage < 1: ", sum(raw.2020.work$hourly_wage < 1))) +# # some basic statistics about hourly_wage variable +# print("Some basic statistics on hourly_wage. These are all for 2020.") +# raw.2020 <- raw.dat %>% filter(time == 2020) +# print('All living people in 2020') +# print(paste0("Number of people alive in 2020: ", nrow(raw.2020))) +# # number individuals with an hourly_wage > 0 +# print('People not in work') +# print(paste0("Number of individuals not in paid work: ", sum(!raw.2020$S7_labour_state %in% c('FT Employed', 'PT Employed')))) +# print(paste0("Number of individuals with hourly_wage == 0: ", sum(raw.2020$hourly_wage == 0))) +# +# # Number in work +# print('People in work') +# print(paste0("Number of individuals in paid work: ", sum(raw.2020$S7_labour_state %in% c('FT Employed', 'PT Employed')))) +# print(paste0("Number of individuals with hourly_wage > 0: ", sum(raw.2020$hourly_wage > 0))) +# +# # Extreme hourly_wage values +# print('Extreme hourly wage values: HIGH') +# print(paste0("Number of individuals with hourly_wage > 30: ", sum(raw.2020$hourly_wage > 30))) +# print(paste0("Number of individuals with hourly_wage > 50: ", sum(raw.2020$hourly_wage > 50))) +# print(paste0("Number of individuals with hourly_wage > 100: ", sum(raw.2020$hourly_wage > 100))) +# print(paste0("Number of individuals with hourly_wage > 500: ", sum(raw.2020$hourly_wage > 500))) +# print(paste0("Number of individuals with hourly_wage > 1000: ", sum(raw.2020$hourly_wage > 1000))) +# print(paste0("Number of individuals with hourly_wage > 2000: ", sum(raw.2020$hourly_wage > 2000))) +# +# raw.2020.work <- raw.2020 %>% filter(hourly_wage > 0) +# print('Extreme hourly wage values: LOW (non-zero)') +# print(paste0("Number of individuals with hourly_wage < 20: ", sum(raw.2020.work$hourly_wage < 20))) +# print(paste0("Number of individuals with hourly_wage < 10: ", sum(raw.2020.work$hourly_wage < 10))) +# print(paste0("Number of individuals with hourly_wage < 5: ", sum(raw.2020.work$hourly_wage < 5))) +# print(paste0("Number of individuals with hourly_wage < 1: ", sum(raw.2020.work$hourly_wage < 1))) ``` ## S7 Labour State From d7931d23da5dc6e4079fa4a68cd27be782b29298 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 12 Feb 2024 09:49:54 +0000 Subject: [PATCH 140/229] Tuning the noise in the physical wellbeing prediction step, and removal of some testing and debugging code --- minos/modules/physical_wellbeing.py | 6 +-- .../estimate_longitudinal_transitions.R | 5 ++- .../transitions/model_definitions_default.txt | 39 +++++++++---------- 3 files changed, 24 insertions(+), 26 deletions(-) diff --git a/minos/modules/physical_wellbeing.py b/minos/modules/physical_wellbeing.py index 674990cf..797bdc19 100644 --- a/minos/modules/physical_wellbeing.py +++ b/minos/modules/physical_wellbeing.py @@ -246,8 +246,8 @@ def on_time_step(self, event): ### This chunk is to increase variance sf12_mean = np.mean(newWavePWB["SF_12_PCS"]) - std_ratio = (9.8/np.std(newWavePWB["SF_12_PCS"])) - newWavePWB["SF_12_PCS"] *= (9.8/np.std(newWavePWB["SF_12_PCS"])) + std_ratio = (10.6/np.std(newWavePWB["SF_12_PCS"])) + newWavePWB["SF_12_PCS"] *= std_ratio newWavePWB["SF_12_PCS"] -= ((std_ratio-1)*sf12_mean) newWavePWB["SF_12_PCS"] -= 1.5 newWavePWB["SF_12_PCS"] += (49.3 - np.mean(newWavePWB["SF_12_PCS"])) @@ -290,7 +290,7 @@ def calculate_pwb(self, pop): pop, dependent='SF_12_PCS', log_transform=True, - noise_std=0.03) # + noise_std=0.025) # # nextWavePWB = r_utils.predict_next_timestep_yj_gamma_glmm(self.gee_transition_model, # self.rpy2_modules, diff --git a/minos/transitions/estimate_longitudinal_transitions.R b/minos/transitions/estimate_longitudinal_transitions.R index 1e8bf456..b806c799 100644 --- a/minos/transitions/estimate_longitudinal_transitions.R +++ b/minos/transitions/estimate_longitudinal_transitions.R @@ -42,7 +42,7 @@ run_longitudinal_models <- function(transitionDir_path, transitionSourceDir_path valid_longitudnial_model_types <- c("LMM", "LMM_DIFF", "GLMM", "GEE_DIFF","ORDGEE", "CLMM", "RF", "SURV", 'GLMMB', 'MSM') - data[which(data$ncigs==-8), 'ncigs'] <- 0 + orig_data[which(orig_data$ncigs==-8), 'ncigs'] <- 0 repeat{ # first thing, take copy of the orig_data to ensure we get the same starting point each time @@ -340,7 +340,7 @@ sipher7 <- args$SIPHER7 ################################################################### # DELETE ME -default <- TRUE +#default <- TRUE #cross_validation <- TRUE # DELETE ME ################################################################### @@ -355,6 +355,7 @@ mode <- 'default' ################################################################################ # REMOVE REMOVE REMOVE REMOVE REMOVE REMOVE REMOVE REMOVE REMOVE REMOVE #default <- T +# cross_validation <- T # REMOVE REMOVE REMOVE REMOVE REMOVE REMOVE REMOVE REMOVE REMOVE REMOVE ################################################################################ diff --git a/minos/transitions/model_definitions_default.txt b/minos/transitions/model_definitions_default.txt index 620d956b..47ba32d2 100644 --- a/minos/transitions/model_definitions_default.txt +++ b/minos/transitions/model_definitions_default.txt @@ -1,23 +1,20 @@ +RF : hourly_wage ~ time + sex + age + I(age**2) + ethnicity + region + education_state + urban + job_sec +CLM : chron_disease ~ factor(chron_disease) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(marital_status) + scale(ncigs) +CLM : matdep ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(marital_status) + hhsize + factor(housing_tenure) + factor(urban) + factor(financial_situation) +LOGIT : active ~ scale(age) + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + scale(hh_income) + relevel(factor(ethnicity), ref = 'WBI') +NNET : auditc ~ scale(age) + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + scale(hh_income) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + factor(financial_situation) +CLM : housing_quality ~ scale(age) + I(scale(age)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) + I(scale(hh_income)**2) + scale(hh_income_diff) + factor(housing_tenure) +CLM : loneliness ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + relevel(factor(job_sec), ref = '3') + scale(hh_income) + relevel(factor(marital_status), ref = 'Partnered') +CLM : neighbourhood_safety ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) +NNET : education_state ~ factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') +CLM : financial_situation ~ factor(financial_situation) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + relevel(factor(job_sec), ref = '3') + scale(hh_income)+ I(scale(hh_income)**2) + factor(marital_status) + factor(housing_tenure) +LOGIT : heating ~ factor(heating) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) + factor(urban) + factor(housing_tenure) + factor(financial_situation) +LOGIT : urban ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + scale(hh_income) +NNET : S7_labour_state ~ factor(S7_labour_state) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + factor(region) + relevel(factor(education_state), ref = "1") +CLM : job_sec ~ factor(job_sec) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = '3') +NNET : housing_tenure ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_tenure) + factor(urban) + factor(financial_situation) + scale(hh_income) +GLMM : hh_income ~ scale(hh_income) + scale(hh_income_diff) + scale(age) + I(scale(age)**2) + I(scale(age)**3) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + relevel(factor(job_sec), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + (1|pidp) +LMM : nutrition_quality ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + scale(hh_income) + (1|pidp) + (1|hidp) + time LMM : SF_12_PCS ~ scale(SF_12_PCS_last) + I(scale(SF_12_PCS_last)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + scale(hh_income) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(active) + factor(auditc) + (1|pidp) + time -#RF : SF_12_PCS ~ SF_12_PCS_last + I(SF_12_PCS_last**2) + sex + ethnicity + age + I(age**2) + region + education_state + housing_quality + hh_income + nutrition_quality + I(ncigs>0) + loneliness + active + auditc + time -#GLMMB : SF_12_PCS ~ scale(SF_12_PCS_last) + I(scale(SF_12_PCS_last)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + scale(hh_income) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(active) + factor(auditc) + (1|pidp) + time -#RF : hourly_wage ~ time + sex + age + I(age**2) + ethnicity + region + education_state + urban + job_sec -#CLM : chron_disease ~ factor(chron_disease) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(marital_status) + scale(ncigs) -#CLM : matdep ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(marital_status) + hhsize + factor(housing_tenure) + factor(urban) + factor(financial_situation) -#LOGIT : active ~ scale(age) + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + scale(hh_income) + relevel(factor(ethnicity), ref = 'WBI') -#NNET : auditc ~ scale(age) + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + scale(hh_income) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + factor(financial_situation) -#CLM : housing_quality ~ scale(age) + I(scale(age)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) + I(scale(hh_income)**2) + scale(hh_income_diff) + factor(housing_tenure) -#CLM : loneliness ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + relevel(factor(job_sec), ref = '3') + scale(hh_income) + relevel(factor(marital_status), ref = 'Partnered') -#CLM : neighbourhood_safety ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) -#NNET : education_state ~ factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') -#CLM : financial_situation ~ factor(financial_situation) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + relevel(factor(job_sec), ref = '3') + scale(hh_income)+ I(scale(hh_income)**2) + factor(marital_status) + factor(housing_tenure) -#LOGIT : heating ~ factor(heating) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) + factor(urban) + factor(housing_tenure) + factor(financial_situation) -#LOGIT : urban ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + scale(hh_income) -#NNET : S7_labour_state ~ factor(S7_labour_state) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + factor(region) + relevel(factor(education_state), ref = "1") -#CLM : job_sec ~ factor(job_sec) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = '3') -#NNET : housing_tenure ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_tenure) + factor(urban) + factor(financial_situation) + scale(hh_income) -#GLMM : hh_income ~ scale(hh_income) + scale(hh_income_diff) + scale(age) + I(scale(age)**2) + I(scale(age)**3) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + relevel(factor(job_sec), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + (1|pidp) -#LMM : nutrition_quality ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + scale(hh_income) + (1|pidp) + (1|hidp) + time -# -#GLMM : SF_12_MCS ~ scale(SF_12_MCS_last) + I(scale(SF_12_MCS_last)**2) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + scale(hh_income) + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + I(factor(ncigs>0)) + (1|pidp) +GLMM : SF_12_MCS ~ scale(SF_12_MCS_last) + I(scale(SF_12_MCS_last)**2) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + scale(hh_income) + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + I(factor(ncigs>0)) + (1|pidp) ZIP : ncigs ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) | I(factor(ncigs>0)) + relevel(factor(ethnicity), ref = 'WBI') + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + relevel(factor(job_sec), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) From ae95c045558df6075d47494a520a9bc1e91e8c79 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 12 Feb 2024 14:16:16 +0000 Subject: [PATCH 141/229] Some fixes for running interventions on synthetic data --- Makefile | 10 +++-- config/uk_scaled.yaml | 7 +-- minos/data_generation/Makefile | 44 ++++++++++--------- .../data_generation/US_household_upscaling.py | 12 ++--- scripts/Makefile | 2 +- 5 files changed, 41 insertions(+), 34 deletions(-) diff --git a/Makefile b/Makefile index 6cf2fd16..5ad4483f 100644 --- a/Makefile +++ b/Makefile @@ -122,13 +122,15 @@ setup_scotland_scaled: install synthetic_glasgow_data transitions_default synthe setup_scotland_scaled_S7: install synthetic_glasgow_data transitions_SIPHER7 synthetic_scotland_repl -setup_uk_hh_scaled: install synthetic_uk_hh_data transitions_default synthetic_uk_hh_repl +setup_uk_scaled: install synthetic_uk_data transitions_default synthetic_uk_repl -setup_uk_hh_scaled_S7: install synthetic_uk_hh_data transitions_SIPHER7 synthetic_uk_hh_repl +#setup_uk_hh_scaled: install synthetic_uk_hh_data transitions_default synthetic_uk_hh_repl -setup_uk_ind_scaled: install synthetic_uk_ind_data transitions_default synthetic_uk_ind_repl +#setup_uk_hh_scaled_S7: install synthetic_uk_hh_data transitions_SIPHER7 synthetic_uk_hh_repl -setup_uk_ind_scaled_S7: install synthetic_uk_ind_data transitions_SIPHER7 synthetic_uk_ind_repl +#setup_uk_ind_scaled: install synthetic_uk_ind_data transitions_default synthetic_uk_ind_repl + +#setup_uk_ind_scaled_S7: install synthetic_uk_ind_data transitions_SIPHER7 synthetic_uk_ind_repl ##################################### diff --git a/config/uk_scaled.yaml b/config/uk_scaled.yaml index ee61a48a..50ce3c00 100644 --- a/config/uk_scaled.yaml +++ b/config/uk_scaled.yaml @@ -11,12 +11,12 @@ population: mortality_file: 'regional_Mortality2011_LEEDS1_2.csv' fertility_file: 'regional_Fertility2011_LEEDS1_2.csv' -input_data_dir: "data/scaled_uk_US/ind" +input_data_dir: "data/scaled_uk_US" persistent_data_dir: "persistent_data" output_data_dir: "output" transition_dir: 'data/transitions' -replenishing_dir: 'data/replenishing/uk_scaled/ind' +replenishing_dir: 'data/replenishing/uk_scaled' cross_validation: FALSE synthetic: TRUE @@ -33,7 +33,8 @@ component_file: 'config/SF12_components.txt' # Finally everything else can go in any order (priority 2). #components : [MWB(), Loneliness(), Tobacco(), Alcohol(), Neighbourhood(), Income(), Housing(), Labour(), Nutrition(), Education(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] #components : [MWB(), Loneliness(), Tobacco(), Neighbourhood(), Housing(), Nutrition(), Income(), Education(), Mortality(), Replenishment()] -components : [lmmYJMWB(), lmmYJPCS(), Loneliness(), Tobacco(), Alcohol(), PhysicalActivity(), Neighbourhood(), MaterialDeprivation(), ChronicDisease(), Heating(), Housing(), HousingTenure(), lmmYJNutrition(), financialSituation(), lmmYJIncome(), Education(), Ageing(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] +#components : [lmmYJMWB(), lmmYJPCS(), Loneliness(), Tobacco(), Alcohol(), PhysicalActivity(), Neighbourhood(), MaterialDeprivation(), ChronicDisease(), Heating(), Housing(), HousingTenure(), lmmYJNutrition(), financialSituation(), lmmYJIncome(), Education(), Ageing(), Mortality(), nkidsFertilityAgeSpecificRates(), Replenishment()] + scale_rates: method: "constant" constant: diff --git a/minos/data_generation/Makefile b/minos/data_generation/Makefile index 28cdc85d..49674ef9 100644 --- a/minos/data_generation/Makefile +++ b/minos/data_generation/Makefile @@ -10,9 +10,11 @@ synthetic_glasgow_data: data glasgow_scaled_data synthetic_scotland_data: data scotland_scaled_data synthetic_scotland_repl -synthetic_uk_hh_data: data uk_hh_scaled_data synthetic_uk_hh_repl +synthetic_uk_data: data uk_scaled_data -synthetic_uk_ind_data: data uk_ind_scaled_data synthetic_uk_ind_repl +synthetic_uk_hh_data: data uk_hh_scaled_data + +synthetic_uk_ind_data: data uk_ind_scaled_data raw_data: ### Generate starting data in the correct format from raw Understanding Society data raw_data: $(RAWDATA)/2021_US_cohort.csv @@ -63,15 +65,17 @@ scotland_scaled_data : $(SCOTLANDSCALEDDATA)/2021_US_cohort.csv synthetic_scotland_repl: $(DATADIR)/replenishing/scotland_scaled/replenishing_pop_2019-2070.csv -uk_hh_scaled_data: $(UKSCALEDDATA)/hh/2020_US_cohort.csv +#uk_hh_scaled_data: $(UKSCALEDDATA)/hh/2020_US_cohort.csv uk_scaled_data: $(UKSCALEDDATA)/2021_US_cohort.csv -synthetic_uk_hh_repl: $(DATADIR)/replenishing/uk_scaled/hh/replenishing_pop_2019-2070.csv - -uk_ind_scaled_data: $(UKSCALEDDATA)/ind/2020_US_cohort.csv +synthetic_uk_repl: $(DATADIR)/replenishing/uk_scaled/replenishing_pop_2015-2070.csv -synthetic_uk_ind_repl: $(DATADIR)/replenishing/uk_scaled/ind/replenishing_pop_2019-2070.csv +#synthetic_uk_hh_repl: $(DATADIR)/replenishing/uk_scaled/hh/replenishing_pop_2019-2070.csv +# +#uk_ind_scaled_data: $(UKSCALEDDATA)/ind/2020_US_cohort.csv +# +#synthetic_uk_ind_repl: $(DATADIR)/replenishing/uk_scaled/ind/replenishing_pop_2019-2070.csv #sheffield_scaled_data goes here. @@ -137,19 +141,19 @@ $(SCOTLANDSCALEDDATA)/2021_US_cohort.csv: $(FINALDATA)/2021_US_cohort.csv $(DATA $(DATADIR)/replenishing/scotland_scaled/replenishing_pop_2015-2070.csv: $(SCOTLANDSCALEDDATA)/2021_US_cohort.csv $(TRANSITION_DATA)/education_state/nnet/education_state_2020_2021.rds $(DATAGEN)/US_utils.py $(DATAGEN)/generate_repl_pop.py $(PERSISTJSON)/*.json $(MODULES)/r_utils.py $(PYTHON) $(DATAGEN)/generate_repl_pop.py --region 'scotland' -# synthetic input for UK with spatial component - HOUSEHOLDS -$(UKSCALEDDATA)/hh/2020_US_cohort.csv: $(FINALDATA)/2020_US_cohort.csv $(DATAGEN)/US_utils.py $(DATAGEN)/US_household_upscaling.py $(PERSISTDATA)/spatial_data/HH2011PopEst2020UK_population.csv - $(PYTHON) $(DATAGEN)/US_household_upscaling.py -r 'uk' -p 1 - -$(DATADIR)/replenishing/uk_scaled/hh/replenishing_pop_2019-2070.csv: $(UKSCALEDDATA)/hh/2020_US_cohort.csv transitions_default $(DATAGEN)/US_utils.py $(DATAGEN)/generate_repl_pop.py $(PERSISTJSON)/*.json $(MODULES)/r_utils.py - $(PYTHON) $(DATAGEN)/generate_repl_pop.py --region 'uk' - -# synthetic input for UK with spatial component - INDIVIDUALS -$(UKSCALEDDATA)/ind/2020_US_cohort.csv: $(FINALDATA)/2020_US_cohort.csv $(DATAGEN)/US_utils.py $(DATAGEN)/US_upscaling_test.py $(PERSISTDATA)/spatial_data/IndSPUKL_population.csv - $(PYTHON) $(DATAGEN)/US_upscaling_test.py -r 'uk' -p 1 - -$(DATADIR)/replenishing/uk_scaled/ind/replenishing_pop_2019-2070.csv: $(UKSCALEDDATA)/ind/2020_US_cohort.csv transitions_default $(DATAGEN)/US_utils.py $(DATAGEN)/generate_repl_pop.py $(PERSISTJSON)/*.json $(MODULES)/r_utils.py - $(PYTHON) $(DATAGEN)/generate_repl_pop.py --region 'uk' +## synthetic input for UK with spatial component - HOUSEHOLDS +#$(UKSCALEDDATA)/hh/2020_US_cohort.csv: $(FINALDATA)/2020_US_cohort.csv $(DATAGEN)/US_utils.py $(DATAGEN)/US_household_upscaling.py $(PERSISTDATA)/spatial_data/HH2011PopEst2020UK_population.csv +# $(PYTHON) $(DATAGEN)/US_household_upscaling.py -r 'uk' -p 1 +# +#$(DATADIR)/replenishing/uk_scaled/hh/replenishing_pop_2019-2070.csv: $(UKSCALEDDATA)/hh/2020_US_cohort.csv transitions_default $(DATAGEN)/US_utils.py $(DATAGEN)/generate_repl_pop.py $(PERSISTJSON)/*.json $(MODULES)/r_utils.py +# $(PYTHON) $(DATAGEN)/generate_repl_pop.py --region 'uk' +# +## synthetic input for UK with spatial component - INDIVIDUALS +#$(UKSCALEDDATA)/ind/2020_US_cohort.csv: $(FINALDATA)/2020_US_cohort.csv $(DATAGEN)/US_utils.py $(DATAGEN)/US_upscaling_test.py $(PERSISTDATA)/spatial_data/IndSPUKL_population.csv +# $(PYTHON) $(DATAGEN)/US_upscaling_test.py -r 'uk' -p 1 +# +#$(DATADIR)/replenishing/uk_scaled/ind/replenishing_pop_2019-2070.csv: $(UKSCALEDDATA)/ind/2020_US_cohort.csv transitions_default $(DATAGEN)/US_utils.py $(DATAGEN)/generate_repl_pop.py $(PERSISTJSON)/*.json $(MODULES)/r_utils.py +# $(PYTHON) $(DATAGEN)/generate_repl_pop.py --region 'uk' # synthetic input for UK with spatial component. $(UKSCALEDDATA)/2021_US_cohort.csv: $(FINALDATA)/2021_US_cohort.csv $(DATAGEN)/US_utils.py $(DATAGEN)/US_household_upscaling.py diff --git a/minos/data_generation/US_household_upscaling.py b/minos/data_generation/US_household_upscaling.py index 521a038b..998f0577 100644 --- a/minos/data_generation/US_household_upscaling.py +++ b/minos/data_generation/US_household_upscaling.py @@ -94,9 +94,9 @@ def main(region, percentage=100, bootstrapping=False, n=100_000): number of boostrapping samples to take. """ # get synthetic data. - #synthpop_file_path = "persistent_data/spatial_data/HH2011PopEst2020UK_population.csv" + synthpop_file_path = "persistent_data/spatial_data/HH2011PopEst2020UK_population.csv" #synthpop_file_path = "persistent_data/spatial_data/HHSPUKL_population.csv" - synthpop_file_path = "persistent_data/spatial_data/IndSPUKL_population.csv" + #synthpop_file_path = "persistent_data/spatial_data/IndSPUKL_population.csv" try: synthpop_data = pd.read_csv(synthpop_file_path) # this is individual population weighted data. except FileNotFoundError as e: @@ -139,10 +139,10 @@ def main(region, percentage=100, bootstrapping=False, n=100_000): sampled_data['weight'] = 1 # force sample weights to 1. as this data is expanded weights no longer representative # but still updating weights helps with weighted aggregates later. - US_utils.check_output_dir(f"data/scaled_{region}_US/hh/") # check save directory exists or create it. - US_utils.save_file(sampled_data, f"data/scaled_{region}_US/hh/", '', 2020) - # US_utils.check_output_dir(f"data/scaled_{region}_US/") # check save directory exists or create it. - # US_utils.save_file(sampled_data, f"data/scaled_{region}_US/", '', 2021) + # US_utils.check_output_dir(f"data/scaled_{region}_US/hh/") # check save directory exists or create it. + # US_utils.save_file(sampled_data, f"data/scaled_{region}_US/hh/", '', 2020) + US_utils.check_output_dir(f"data/scaled_{region}_US/") # check save directory exists or create it. + US_utils.save_file(sampled_data, f"data/scaled_{region}_US/", '', 2021) if __name__ == '__main__': diff --git a/scripts/Makefile b/scripts/Makefile index 33314d32..276af800 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -245,7 +245,7 @@ arc4_qaly_scenarios_glasgow: MODE=glasgow_scaled arc4_qaly_scenarios_glasgow: RUN_CONFIG=$(CONFIG)/glasgow_scaled.yaml arc4_qaly_scenarios_glasgow: arc4_baseline arc4_intervention_energyDownLift arc4_intervention_energyDownLiftNoSupport arc4_intervention_livingWage -arc4_qaly_scenarios_uk: setup_uk_ind_scaled +arc4_qaly_scenarios_uk: setup_uk_scaled arc4_qaly_scenarios_uk: MODE=SF12_uk_scaled arc4_qaly_scenarios_uk: RUN_CONFIG=$(CONFIG)/uk_scaled.yaml arc4_qaly_scenarios_uk: arc4_baseline arc4_intervention_energyDownLift arc4_intervention_energyDownLiftNoSupport arc4_intervention_livingWage From bff549e908bf86b5a2126f3469dd1598bb7f9f22 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 12 Feb 2024 14:57:59 +0000 Subject: [PATCH 142/229] Fixed path problem in synthetic uk replenishing pop generation --- minos/data_generation/generate_repl_pop.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/minos/data_generation/generate_repl_pop.py b/minos/data_generation/generate_repl_pop.py index 8f7946a7..0ef4d501 100755 --- a/minos/data_generation/generate_repl_pop.py +++ b/minos/data_generation/generate_repl_pop.py @@ -237,8 +237,8 @@ def generate_replenishing(projections, scotland_mode, cross_validation, inflated data_source = 'scaled_scotland_US' output_dir = 'data/replenishing/scotland_scaled' elif region == 'uk': - data_source = 'scaled_uk_US/ind' - output_dir = 'data/replenishing/uk_scaled/ind' + data_source = 'scaled_uk_US' + output_dir = 'data/replenishing/uk_scaled' # first collect and load the datafile for 2018 file_name = f"data/{data_source}/2021_US_cohort.csv" From fb019a9f1be5b12b005d604fd86dc7b5f7b38ae1 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 13 Feb 2024 13:32:19 +0000 Subject: [PATCH 143/229] Fixed problem with household upscaling where I was using the wrong year to generate our merged synthpop. Also added a little bit of code in generate_repl_pop to select the correct year to generate the replenishing pop. 2015 for CV, 2020 for synthpops, and 2021 for all else --- minos/data_generation/US_household_upscaling.py | 5 +++-- minos/data_generation/generate_repl_pop.py | 10 ++++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/minos/data_generation/US_household_upscaling.py b/minos/data_generation/US_household_upscaling.py index 998f0577..823b421a 100644 --- a/minos/data_generation/US_household_upscaling.py +++ b/minos/data_generation/US_household_upscaling.py @@ -38,6 +38,7 @@ def merge_with_synthpop_households(synthpop, msim_data, merge_column="hidp"): """ synthpop[f"new_{merge_column}"] = np.arange(synthpop.shape[0]) synthpop[merge_column] = synthpop[merge_column].astype(int) + msim_data[merge_column] = msim_data[merge_column].astype(int) merged_data = synthpop.merge(msim_data, how='left', on=merge_column) # merged_data[f"new_{merge_column}"] = merged_data[f'new_{merge_column}'] return merged_data @@ -105,7 +106,7 @@ def main(region, percentage=100, bootstrapping=False, n=100_000): raise data_zones = get_data_zones(region) - US_data = pd.read_csv("data/final_US/2021_US_cohort.csv") # only expanding on one year of US data for 2021. + US_data = pd.read_csv("data/final_US/2020_US_cohort.csv") # only expanding on one year of US data for 2021. if type(data_zones) == pd.core.series.Series: subsetted_synthpop_data = subset_zone_ids(synthpop_data, data_zones) else: @@ -142,7 +143,7 @@ def main(region, percentage=100, bootstrapping=False, n=100_000): # US_utils.check_output_dir(f"data/scaled_{region}_US/hh/") # check save directory exists or create it. # US_utils.save_file(sampled_data, f"data/scaled_{region}_US/hh/", '', 2020) US_utils.check_output_dir(f"data/scaled_{region}_US/") # check save directory exists or create it. - US_utils.save_file(sampled_data, f"data/scaled_{region}_US/", '', 2021) + US_utils.save_file(sampled_data, f"data/scaled_{region}_US/", '', 2020) if __name__ == '__main__': diff --git a/minos/data_generation/generate_repl_pop.py b/minos/data_generation/generate_repl_pop.py index 0ef4d501..8518c52f 100755 --- a/minos/data_generation/generate_repl_pop.py +++ b/minos/data_generation/generate_repl_pop.py @@ -217,6 +217,7 @@ def generate_replenishing(projections, scotland_mode, cross_validation, inflated output_dir = 'data/replenishing' data_source = 'final_US' transition_dir = 'data/transitions' + source_year = 2021 # the year from which we draw our 16 year old cohort if scotland_mode: data_source = 'scotland_US' @@ -226,6 +227,7 @@ def generate_replenishing(projections, scotland_mode, cross_validation, inflated data_source = 'final_US/cross_validation/batch1' output_dir = 'data/replenishing/cross_validation' transition_dir = 'data/transitions/cross_validation/version1' + source_year = 2015 if inflated: data_source = 'inflated_US' output_dir = 'data/replenishing/inflated' @@ -233,15 +235,18 @@ def generate_replenishing(projections, scotland_mode, cross_validation, inflated if region == 'glasgow': data_source = 'scaled_glasgow_US' output_dir = 'data/replenishing/glasgow_scaled' + source_year = 2020 elif region == 'scotland': data_source = 'scaled_scotland_US' output_dir = 'data/replenishing/scotland_scaled' + source_year = 2020 elif region == 'uk': data_source = 'scaled_uk_US' output_dir = 'data/replenishing/uk_scaled' + source_year = 2020 # first collect and load the datafile for 2018 - file_name = f"data/{data_source}/2021_US_cohort.csv" + file_name = f"data/{data_source}/{source_year}_US_cohort.csv" data = pd.read_csv(file_name) # expand and reweight the population @@ -277,7 +282,8 @@ def main(): usage='use "%(prog)s --help" for more information') parser.add_argument("-r", "--region", default="", - help="Generate replenishing population for specified synthetic scaled data. glasgow or scotland for now.") + help="Generate replenishing population for specified synthetic scaled data. glasgow or " + "scotland for now.") parser.add_argument("-s", "--scotland", action='store_true', default=False, help="Select Scotland mode to only produce replenishing using scottish sample.") parser.add_argument("-c", "--cross_validation", dest='crossval', action='store_true', default=False, From 17705a661f010226712627791b55a597bdbcbb56 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Wed, 14 Feb 2024 10:02:46 +0000 Subject: [PATCH 144/229] Fixed synthetic pop preparation for uk scaled simulation --- config/uk_scaled.yaml | 4 ++-- minos/data_generation/Makefile | 18 +++++++++--------- minos/data_generation/generate_stock_pop.py | 5 +++++ scripts/Makefile | 15 +++++++-------- 4 files changed, 23 insertions(+), 19 deletions(-) diff --git a/config/uk_scaled.yaml b/config/uk_scaled.yaml index 50ce3c00..0311fab9 100644 --- a/config/uk_scaled.yaml +++ b/config/uk_scaled.yaml @@ -1,8 +1,8 @@ randomness: key_columns: ['entrance_time', 'age'] time: - start: {year: 2021, month: 10, day: 15} - end: {year: 2036, month: 10, day: 15} + start: {year: 2020, month: 10, day: 15} + end: {year: 2035, month: 10, day: 15} step_size: 365.25 # Days num_years: 15 population: diff --git a/minos/data_generation/Makefile b/minos/data_generation/Makefile index 49674ef9..968df1e8 100644 --- a/minos/data_generation/Makefile +++ b/minos/data_generation/Makefile @@ -57,17 +57,17 @@ cv_replenishing: $(DATADIR)/replenishing/cross_validation/replenishing_pop_2015- # creating spatial populations from WS3 data -glasgow_scaled_data: $(GLASGOWSCALEDDATA)/2021_US_cohort.csv +glasgow_scaled_data: $(GLASGOWSCALEDDATA)/2020_US_cohort.csv synthetic_glasgow_repl: $(DATADIR)/replenishing/glasgow_scaled/replenishing_pop_2019-2070.csv -scotland_scaled_data : $(SCOTLANDSCALEDDATA)/2021_US_cohort.csv +scotland_scaled_data : $(SCOTLANDSCALEDDATA)/2020_US_cohort.csv synthetic_scotland_repl: $(DATADIR)/replenishing/scotland_scaled/replenishing_pop_2019-2070.csv #uk_hh_scaled_data: $(UKSCALEDDATA)/hh/2020_US_cohort.csv -uk_scaled_data: $(UKSCALEDDATA)/2021_US_cohort.csv +uk_scaled_data: $(UKSCALEDDATA)/2020_US_cohort.csv synthetic_uk_repl: $(DATADIR)/replenishing/uk_scaled/replenishing_pop_2015-2070.csv @@ -128,17 +128,17 @@ $(DATADIR)/replenishing/cross_validation/replenishing_pop_2015-2070.csv: $(FINAL ### Synthetic Populations # synthetic input for glasgow with spatial component. -$(GLASGOWSCALEDDATA)/2021_US_cohort.csv: $(FINALDATA)/2021_US_cohort.csv $(DATAGEN)/US_utils.py $(DATAGEN)/US_household_upscaling.py +$(GLASGOWSCALEDDATA)/2020_US_cohort.csv: $(FINALDATA)/2021_US_cohort.csv $(DATAGEN)/US_utils.py $(DATAGEN)/US_household_upscaling.py $(PYTHON) $(DATAGEN)/US_household_upscaling.py -r 'glasgow' -p 100 -$(DATADIR)/replenishing/glasgow_scaled/replenishing_pop_2015-2070.csv: $(GLASGOWSCALEDDATA)/2021_US_cohort.csv $(TRANSITION_DATA)/education_state/nnet/education_state_2020_2021.rds $(DATAGEN)/US_utils.py $(DATAGEN)/generate_repl_pop.py $(PERSISTJSON)/*.json $(MODULES)/r_utils.py +$(DATADIR)/replenishing/glasgow_scaled/replenishing_pop_2015-2070.csv: $(GLASGOWSCALEDDATA)/2020_US_cohort.csv $(TRANSITION_DATA)/ncigs/zip/ncigs_2020_2021.rds $(DATAGEN)/US_utils.py $(DATAGEN)/generate_repl_pop.py $(PERSISTJSON)/*.json $(MODULES)/r_utils.py $(PYTHON) $(DATAGEN)/generate_repl_pop.py --region 'glasgow' # synthetic input for glasgow with spatial component. -$(SCOTLANDSCALEDDATA)/2021_US_cohort.csv: $(FINALDATA)/2021_US_cohort.csv $(DATAGEN)/US_utils.py $(DATAGEN)/US_household_upscaling.py +$(SCOTLANDSCALEDDATA)/2020_US_cohort.csv: $(FINALDATA)/2021_US_cohort.csv $(DATAGEN)/US_utils.py $(DATAGEN)/US_household_upscaling.py $(PYTHON) $(DATAGEN)/US_household_upscaling.py -r 'scotland' -p 100 -$(DATADIR)/replenishing/scotland_scaled/replenishing_pop_2015-2070.csv: $(SCOTLANDSCALEDDATA)/2021_US_cohort.csv $(TRANSITION_DATA)/education_state/nnet/education_state_2020_2021.rds $(DATAGEN)/US_utils.py $(DATAGEN)/generate_repl_pop.py $(PERSISTJSON)/*.json $(MODULES)/r_utils.py +$(DATADIR)/replenishing/scotland_scaled/replenishing_pop_2015-2070.csv: $(SCOTLANDSCALEDDATA)/2020_US_cohort.csv $(TRANSITION_DATA)/ncigs/zip/ncigs_2020_2021.rds $(DATAGEN)/US_utils.py $(DATAGEN)/generate_repl_pop.py $(PERSISTJSON)/*.json $(MODULES)/r_utils.py $(PYTHON) $(DATAGEN)/generate_repl_pop.py --region 'scotland' ## synthetic input for UK with spatial component - HOUSEHOLDS @@ -156,8 +156,8 @@ $(DATADIR)/replenishing/scotland_scaled/replenishing_pop_2015-2070.csv: $(SCOTLA # $(PYTHON) $(DATAGEN)/generate_repl_pop.py --region 'uk' # synthetic input for UK with spatial component. -$(UKSCALEDDATA)/2021_US_cohort.csv: $(FINALDATA)/2021_US_cohort.csv $(DATAGEN)/US_utils.py $(DATAGEN)/US_household_upscaling.py +$(UKSCALEDDATA)/2020_US_cohort.csv: $(FINALDATA)/2021_US_cohort.csv $(DATAGEN)/US_utils.py $(DATAGEN)/US_household_upscaling.py $(PYTHON) $(DATAGEN)/US_household_upscaling.py -r 'uk' -p 1 -$(DATADIR)/replenishing/uk_scaled/replenishing_pop_2015-2070.csv: $(UKSCALEDDATA)/2021_US_cohort.csv $(TRANSITION_DATA)/education_state/nnet/education_state_2020_2021.rds $(DATAGEN)/US_utils.py $(DATAGEN)/generate_repl_pop.py $(PERSISTJSON)/*.json $(MODULES)/r_utils.py +$(DATADIR)/replenishing/uk_scaled/replenishing_pop_2015-2070.csv: $(UKSCALEDDATA)/2020_US_cohort.csv $(TRANSITION_DATA)/ncigs/zip/ncigs_2020_2021.rds $(DATAGEN)/US_utils.py $(DATAGEN)/generate_repl_pop.py $(PERSISTJSON)/*.json $(MODULES)/r_utils.py $(PYTHON) $(DATAGEN)/generate_repl_pop.py --region 'uk' diff --git a/minos/data_generation/generate_stock_pop.py b/minos/data_generation/generate_stock_pop.py index 4be75b2a..f0d1775c 100755 --- a/minos/data_generation/generate_stock_pop.py +++ b/minos/data_generation/generate_stock_pop.py @@ -190,6 +190,11 @@ def generate_stock(projections, cross_validation): copy_year=2020, paste_year=2021, var_type='ordinal') + data = wave_data_copy(data, + var='heating', + copy_year=2014, + paste_year=2015, + var_type='ordinal') # data = wave_data_copy(data, # var='matdep', # copy_year=2020, diff --git a/scripts/Makefile b/scripts/Makefile index 276af800..55ed0b5f 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -141,26 +141,25 @@ intervention_energyDownLiftNoSupport_scotland: setup_scotland_scaled ## Local single runs of MINOS SF12 experiments using the UK (GB) synthetic population. ############################################ -baseline: ### Baseline run of MINOS, using configuration defined in testConfig.yaml -baseline_uk: setup_uk_ind_scaled +baseline_uk: setup_uk_scaled $(PYTHON) scripts/run.py -c $(CONFIG)/uk_scaled.yaml -o SF12_uk_scaled -intervention_hhIncome_uk: setup_uk_ind_scaled +intervention_hhIncome_uk: setup_uk_scaled $(PYTHON) scripts/run.py -c $(CONFIG)/uk_scaled.yaml -o SF12_uk_scaled -i 'hhIncomeIntervention' -intervention_hhIncomeChildUplift_uk: setup_uk_ind_scaled +intervention_hhIncomeChildUplift_uk: setup_uk_scaled $(PYTHON) scripts/run.py -c $(CONFIG)/uk_scaled.yaml -o SF12_uk_scaled -i 'hhIncomeChildUplift' -intervention_PovertyLineChildUplift_uk: setup_uk_ind_scaled +intervention_PovertyLineChildUplift_uk: setup_uk_scaled $(PYTHON) scripts/run.py -c $(CONFIG)/uk_scaled.yaml -o SF12_uk_scaled -i 'hhIncomePovertyLineChildUplift' -intervention_livingWage_uk: setup_uk_ind_scaled +intervention_livingWage_uk: setup_uk_scaled $(PYTHON) scripts/run.py -c $(CONFIG)/uk_scaled.yaml -o SF12_uk_scaled -i 'livingWageIntervention' -intervention_energyDownLift_uk: setup_uk_ind_scaled +intervention_energyDownLift_uk: setup_uk_scaled $(PYTHON) scripts/run.py -c $(CONFIG)/uk_scaled.yaml -o SF12_uk_scaled -i 'energyDownlift' -intervention_energyDownLiftNoSupport_uk: setup_uk_ind_scaled +intervention_energyDownLiftNoSupport_uk: setup_uk_scaled $(PYTHON) scripts/run.py -c $(CONFIG)/uk_scaled.yaml -o SF12_uk_scaled -i 'energyDownliftNoSupport' From 2f948814b25778c309d7d6b3159229cf38d64691 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Wed, 14 Feb 2024 10:18:24 +0000 Subject: [PATCH 145/229] Changed MCS to use log-normal LMM model --- minos/modules/mental_wellbeing.py | 35 ++++++++++++------- minos/modules/r_utils.py | 15 +++++--- .../estimate_longitudinal_transitions.R | 21 +++++------ .../transitions/model_definitions_default.txt | 2 +- .../transitions/transition_model_functions.R | 16 ++++++++- 5 files changed, 59 insertions(+), 30 deletions(-) diff --git a/minos/modules/mental_wellbeing.py b/minos/modules/mental_wellbeing.py index 70109da5..3904e1a3 100755 --- a/minos/modules/mental_wellbeing.py +++ b/minos/modules/mental_wellbeing.py @@ -484,7 +484,8 @@ def setup(self, builder): 'SF_12_MCS', 'SF_12_MCS_diff', 'pidp', - 'hh_income' + 'hh_income', + 'time' ] self.population_view = builder.population.get_view(columns=view_columns) @@ -500,8 +501,8 @@ def setup(self, builder): super().setup(builder) #only need to load this once for now. - #self.gee_transition_model = r_utils.load_transitions(f"SF_12/lmm/SF_12_LMM", self.rpy2_modules, path=self.transition_dir) - self.gee_transition_model = r_utils.load_transitions(f"SF_12_MCS/glmm/SF_12_MCS_GLMM", self.rpy2_modules, path=self.transition_dir) + self.lmm_transition_model = r_utils.load_transitions(f"SF_12_MCS/lmm/SF_12_MCS_LMM", self.rpy2_modules, path=self.transition_dir) + #self.gee_transition_model = r_utils.load_transitions(f"SF_12_MCS/glmm/SF_12_MCS_GLMM", self.rpy2_modules, path=self.transition_dir) def on_time_step(self, event): """Produces new children and updates parent status on time steps. @@ -527,9 +528,9 @@ def on_time_step(self, event): std_ratio = (11/np.std(newWaveMWB["SF_12_MCS"])) newWaveMWB["SF_12_MCS"] *= (11/np.std(newWaveMWB["SF_12_MCS"])) newWaveMWB["SF_12_MCS"] -= ((std_ratio-1)*sf12_mean) - newWaveMWB["SF_12_MCS"] -= 1.5 + #newWaveMWB["SF_12_MCS"] -= 1.5 #newWaveMWB["SF_12_MCS"] += (50 - np.mean(newWaveMWB["SF_12_MCS"])) - newWaveMWB["SF_12_MCS"] = np.clip(newWaveMWB["SF_12_MCS"], 0, 100) # keep within [0, 100] bounds of SF12. + #newWaveMWB["SF_12_MCS"] = np.clip(newWaveMWB["SF_12_MCS"], 0, 100) # keep within [0, 100] bounds of SF12. newWaveMWB["SF_12_MCS_diff"] = newWaveMWB["SF_12_MCS"] - pop["SF_12_MCS"] # Update population with new SF12_MCS #print(np.mean(newWaveMWB["SF_12_MCS"])) @@ -546,14 +547,22 @@ def calculate_mwb(self, pop): Returns ------- """ - out_data = r_utils.predict_next_timestep_yj_gamma_glmm(self.gee_transition_model, - self.rpy2_modules, - current= pop, - dependent='SF_12_MCS', - reflect=True, - yeo_johnson= True, - noise_std= 0.1) # 5 for non yj, 0.35 for yj - return out_data + # nextWaveMWB = r_utils.predict_next_timestep_yj_gamma_glmm(self.gee_transition_model, + # self.rpy2_modules, + # current= pop, + # dependent='SF_12_MCS', + # reflect=True, + # yeo_johnson= True, + # noise_std= 0.1) # 5 for non yj, 0.35 for yj + + nextWaveMWB = r_utils.predict_next_timestep_yj_gaussian_lmm(self.lmm_transition_model, + self.rpy2_modules, + pop, + dependent='SF_12_MCS', + log_transform=True, + noise_std=0.025) # + + return nextWaveMWB class lmmDiffMWB(Base): diff --git a/minos/modules/r_utils.py b/minos/modules/r_utils.py index 18c88566..bf9f3c6b 100755 --- a/minos/modules/r_utils.py +++ b/minos/modules/r_utils.py @@ -11,7 +11,7 @@ from rpy2.robjects import numpy2ri import pandas as pd import numpy as np -import matplotlib.pyplot as pl +#import matplotlib.pyplot as pl def load_transitions(component, rpy2_modules, path='data/transitions/'): @@ -363,16 +363,21 @@ def predict_next_timestep_yj_gaussian_lmm(model, rpy2_modules, current, dependen #current = current.drop([dependent, 'time'], axis=1) #current["pidp"] = -current["pidp"] + # need to add tiny value to the 0 MCS values as this causes problems in log transform + if dependent == "SF_12_MCS": + current.loc[current[dependent] == 0.0, dependent] = current.loc[current[dependent] == 0.0, dependent] + 0.01 + + # Convert from pandas to R using package converter with localconverter(ro.default_converter + pandas2ri.converter): currentRDF = ro.conversion.py2rpy(current) - if dependent == "SF_12_PCS": + if dependent in ["SF_12_PCS", "SF_12_MCS"]: max_value = model.do_slot("max_value") min_value = model.do_slot("min_value") if log_transform: - # log transformation currently only for PCS + # log transformation currently only for PCS (also testing MCS) currentRDF[currentRDF.names.index(dependent)] = base.log(currentRDF.rx2(dependent)) # explicitly convert to matrix to overcome error in predict_merMod below @@ -401,7 +406,7 @@ def predict_next_timestep_yj_gaussian_lmm(model, rpy2_modules, current, dependen valid_dependents = ['hh_income', 'hh_income_new', 'nutrition_quality_new', 'nutrition_quality', - 'nutrition_quality_diff', 'SF_12_PCS'] + 'nutrition_quality_diff', 'SF_12_PCS', 'SF_12_MCS'] if dependent == "SF_12" and noise_std: prediction = prediction.ro + stats.rnorm(current.shape[0], 0, noise_std) # add gaussian noise. elif (dependent in valid_dependents) and noise_std: @@ -416,7 +421,7 @@ def predict_next_timestep_yj_gaussian_lmm(model, rpy2_modules, current, dependen #ols_data = ro.conversion.rpy2py(ols_data) prediction = ro.conversion.rpy2py(prediction) - if dependent == "SF_12_PCS": + if dependent in ["SF_12_PCS", "SF_12_MCS"]: # Final step is to clip the values to min and max seen in input data prediction = np.clip(prediction, min_value, max_value) diff --git a/minos/transitions/estimate_longitudinal_transitions.R b/minos/transitions/estimate_longitudinal_transitions.R index b806c799..08f79ddf 100644 --- a/minos/transitions/estimate_longitudinal_transitions.R +++ b/minos/transitions/estimate_longitudinal_transitions.R @@ -92,20 +92,21 @@ run_longitudinal_models <- function(transitionDir_path, transitionSourceDir_path use.weights <- TRUE } - if (dependent %in% c("SF_12_MCS")) { # 'SF_12_PCS' - do.reflect = TRUE # only SF12 continuous data is reflected to be left skewed. - } - else { - do.reflect=FALSE - } + # if (dependent %in% c()) { # 'SF_12_PCS', "SF_12_MCS" + # do.reflect = TRUE # only SF12 continuous data is reflected to be left skewed. + # } + # else { + # do.reflect=FALSE + # } + do.reflect=FALSE - if (dependent %in% c("SF_12_MCS", 'hh_income')) { # 'SF_12_PCS' + if (dependent %in% c('hh_income')) { # 'SF_12_PCS', "SF_12_MCS" do.yeo.johnson = T # } else { do.yeo.johnson = F } - if (dependent %in% c("SF_12_PCS")) { + if (dependent %in% c('SF_12_PCS', 'SF_12_MCS')) { do.log.transform <- T } else { do.log.transform <- F @@ -195,7 +196,7 @@ run_longitudinal_models <- function(transitionDir_path, transitionSourceDir_path # model <- estimate_gamma_glmm(data = sorted_df, formula = form, - include_weights = use.weights, + include_weights = F, depend = dependent, reflect=do.reflect, yeo_johnson = do.yeo.johnson) @@ -354,7 +355,7 @@ mode <- 'default' ################################################################################ # REMOVE REMOVE REMOVE REMOVE REMOVE REMOVE REMOVE REMOVE REMOVE REMOVE -#default <- T +default <- T # cross_validation <- T # REMOVE REMOVE REMOVE REMOVE REMOVE REMOVE REMOVE REMOVE REMOVE REMOVE ################################################################################ diff --git a/minos/transitions/model_definitions_default.txt b/minos/transitions/model_definitions_default.txt index 47ba32d2..8f11a473 100644 --- a/minos/transitions/model_definitions_default.txt +++ b/minos/transitions/model_definitions_default.txt @@ -16,5 +16,5 @@ NNET : housing_tenure ~ scale(age) + factor(sex) + relevel(factor(ethnicity), re GLMM : hh_income ~ scale(hh_income) + scale(hh_income_diff) + scale(age) + I(scale(age)**2) + I(scale(age)**3) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + relevel(factor(job_sec), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + (1|pidp) LMM : nutrition_quality ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + scale(hh_income) + (1|pidp) + (1|hidp) + time LMM : SF_12_PCS ~ scale(SF_12_PCS_last) + I(scale(SF_12_PCS_last)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + scale(hh_income) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(active) + factor(auditc) + (1|pidp) + time -GLMM : SF_12_MCS ~ scale(SF_12_MCS_last) + I(scale(SF_12_MCS_last)**2) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + scale(hh_income) + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + I(factor(ncigs>0)) + (1|pidp) +LMM : SF_12_MCS ~ time + scale(SF_12_MCS_last) + I(scale(SF_12_MCS_last)**2) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + scale(hh_income) + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + I(factor(ncigs>0)) + (1|pidp) ZIP : ncigs ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) | I(factor(ncigs>0)) + relevel(factor(ethnicity), ref = 'WBI') + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + relevel(factor(job_sec), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) diff --git a/minos/transitions/transition_model_functions.R b/minos/transitions/transition_model_functions.R index 6e44d193..8418e28d 100644 --- a/minos/transitions/transition_model_functions.R +++ b/minos/transitions/transition_model_functions.R @@ -153,6 +153,20 @@ estimate_longitudinal_lmm <- function(data, formula, include_weights = FALSE, de data <- replace.missing(data) #data <- drop_na(data) + + # If min == 0, then add small amount before log_transform + # if (min(data[[depend]] == 0) && (log_transform)) { + # print("Adding small value for log_transform") + # data[[depend]] <- data[[depend]] + 0.001 + # } + + if ((depend == 'SF_12_MCS') & log_transform) { + data[[depend]] <- data[[depend]] + 0.001 + } + + # remove rows with null weight values + #data <- data[!is.na(data$weight), ] + max_value <- nanmax(data[[depend]]) min_value <- nanmin(data[[depend]]) @@ -187,7 +201,7 @@ estimate_longitudinal_lmm <- function(data, formula, include_weights = FALSE, de ## LA 9/2/24 # Saving min and max value from input data for clipping in r_utils function - if (depend == 'SF_12_PCS') { + if (depend %in% c('SF_12_PCS', 'SF_12_MCS')) { attr(model,"min_value") <- min_value attr(model,"max_value") <- max_value } From 74fdf199e9f40ae34eaa35ccb2612ba36e04b957 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Wed, 14 Feb 2024 16:50:08 +0000 Subject: [PATCH 146/229] Fixed issue with intervention not being correctly added to the components list in RunPipeline --- minos/minosPipeline/RunPipeline.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/minos/minosPipeline/RunPipeline.py b/minos/minosPipeline/RunPipeline.py index 89935110..c51f6dfb 100755 --- a/minos/minosPipeline/RunPipeline.py +++ b/minos/minosPipeline/RunPipeline.py @@ -232,7 +232,8 @@ def RunPipeline(config, intervention=None): components_raw.append(line.rstrip()) if intervention is not None: - components_raw += intervention + #components_raw += intervention + components_raw.append(intervention) component_priority_map, component_name_map = get_priorities() components = [component_name_map[c] for c in components_raw if c in component_name_map] From aded0e9cfc2880836e6bfea15c010bc9dbeb7053 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Thu, 15 Feb 2024 10:44:33 +0000 Subject: [PATCH 147/229] Tweaked component priorities to separate intervention from pathways (now goes income -> intervention -> pathways). Also removed parentheses from intervention class repr()s as was causing problems with component priorities --- minos/minosPipeline/RunPipeline.py | 2 +- minos/modules/base_module.py | 2 +- minos/modules/intervention.py | 14 +++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/minos/minosPipeline/RunPipeline.py b/minos/minosPipeline/RunPipeline.py index c51f6dfb..95d481c8 100755 --- a/minos/minosPipeline/RunPipeline.py +++ b/minos/minosPipeline/RunPipeline.py @@ -174,7 +174,7 @@ def get_priorities(): # print("Everything else:\n", everything_else) - component_priorities.update({el: 6 for el in everything_else}) + component_priorities.update({el: 7 for el in everything_else}) component_priorities.update({el: 9 for el in and_finally}) # component_priorities.update({el: 8 for el in metrics_map}) diff --git a/minos/modules/base_module.py b/minos/modules/base_module.py index a6935e53..18f086df 100755 --- a/minos/modules/base_module.py +++ b/minos/modules/base_module.py @@ -13,7 +13,7 @@ PRIORITY_DEFAULT = 10 -class Base(): +class Base: @property def name(self): diff --git a/minos/modules/intervention.py b/minos/modules/intervention.py index 6a354dfb..63dcc97c 100755 --- a/minos/modules/intervention.py +++ b/minos/modules/intervention.py @@ -7,14 +7,14 @@ import logging from minos.modules.base_module import Base -class hhIncomeIntervention(): +class hhIncomeIntervention(Base): @property def name(self): return "hh_income_intervention" def __repr__(self): - return "hhIncomeIntervention()" + return "hhIncomeIntervention" # In Daedalus pre_setup was done in the run_pipeline file. This way is tidier and more modular in my opinion. def pre_setup(self, config, simulation): @@ -135,7 +135,7 @@ def name(self): return "hh_income_20_uplift" def __repr__(self): - return "hhIncomeChildUplift()" + return "hhIncomeChildUplift" def setup(self, builder): """ Initialise the module during simulation.setup(). @@ -213,7 +213,7 @@ def name(self): return "hh_income_poverty_live_20_uplift" def __repr__(self): - return "hhIncomePovertyLineChildUplift()" + return "hhIncomePovertyLineChildUplift" def setup(self, builder): """ Initialise the module during simulation.setup(). @@ -296,7 +296,7 @@ def name(self): return "living_wage_intervention" def __repr__(self): - return "livingWageIntervention()" + return "livingWageIntervention" def setup(self, builder): """ Initialise the module during simulation.setup(). @@ -412,7 +412,7 @@ def name(self): return "energy_downlift" def __repr__(self): - return "energyDownlift()" + return "energyDownlift" def setup(self, builder): """ Initialise the module during simulation.setup(). @@ -496,7 +496,7 @@ def name(self): return "energy_downlift_no_support" def __repr__(self): - return "energyDownliftNoSupport()" + return "energyDownliftNoSupport" def setup(self, builder): """ Initialise the module during simulation.setup(). From e5c95bca62393497258541bcbda3460a5f5e115c Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Thu, 15 Feb 2024 10:47:27 +0000 Subject: [PATCH 148/229] Increased runs to 100 and increased memory to 18GB for uk synthetic runs --- scripts/arc_run.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/arc_run.sh b/scripts/arc_run.sh index a6a6c5cc..e7c05e68 100755 --- a/scripts/arc_run.sh +++ b/scripts/arc_run.sh @@ -11,7 +11,7 @@ ## Email if a run aborts #$ -m a ## Select memory -#$ -l h_vmem=2G # was 15 for big runs +#$ -l h_vmem=18G # was 15 for big runs ## Choose cores. See arc website for more details. 5 high memory cores chosen here. #$ -pe smp 1 ## Set logs directories @@ -20,7 +20,7 @@ ############## SET NUMBER OF RUNS HERE ############## ## Tell computer this is an array job with tasks from 1 to N -#$ -t 1-2 +#$ -t 1-100 # create these if they dont exist. Will crash arc4 if you dont do this. mkdir -p logs From 68bef7746e132e30580692546017099e8a233450 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Fri, 16 Feb 2024 14:05:29 +0000 Subject: [PATCH 149/229] Adding ability to define the output subdirectory to a script used for collecting and compressing qaly outputs --- compress_qaly_files.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compress_qaly_files.sh b/compress_qaly_files.sh index a87f3a09..3e8ad6d8 100644 --- a/compress_qaly_files.sh +++ b/compress_qaly_files.sh @@ -7,12 +7,15 @@ base_path="/home/luke/Documents/WORK/MINOS/Minos/output/" directories=("baseline" "energyDownlift" "energyDownliftNoSupport" "livingWageIntervention") # Step 1: Ask for user input +read -p "Enter the name of the experiment subdirectory (i.e. default_config): " output_subdir read -p "Enter a unique descriptive suffix for the output filename: " suffix -# Step 2: Create a directory +# Step 2: Create a directory and append the output_subdir to base_path output_dir="QALY_analysis_$suffix" mkdir "$output_dir" +base_path=${base_path}${output_subdir} + # Step 3 & 4: Loop over directories and copy/ rename qalys.csv for dir in "${directories[@]}"; do newest_dir=$(ls -t "${base_path}${dir}/" | head -n 1) From 089872905a3dd89575e7d2f32579c5870f2afe7c Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Fri, 16 Feb 2024 14:28:48 +0000 Subject: [PATCH 150/229] Typo in filepath --- compress_qaly_files.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compress_qaly_files.sh b/compress_qaly_files.sh index 3e8ad6d8..ee1e0806 100644 --- a/compress_qaly_files.sh +++ b/compress_qaly_files.sh @@ -1,7 +1,7 @@ #!/bin/bash # Path variable -base_path="/home/luke/Documents/WORK/MINOS/Minos/output/" +base_path="output/" # List of directories directories=("baseline" "energyDownlift" "energyDownliftNoSupport" "livingWageIntervention") From d40b349c88ef21207147e43c5ec692602df0ce99 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Fri, 16 Feb 2024 14:32:09 +0000 Subject: [PATCH 151/229] Fixed another mistake in path to html files --- compress_qaly_files.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compress_qaly_files.sh b/compress_qaly_files.sh index ee1e0806..ecbdac15 100644 --- a/compress_qaly_files.sh +++ b/compress_qaly_files.sh @@ -29,7 +29,7 @@ for dir in "${directories[@]}"; do done # Step 7: Copy QALY_comparison_*.html files -cp "${base_path}minos/testing/QALY_comparison_"*.html "$output_dir/" +cp "minos/testing/QALY_comparison_"*.html "$output_dir/" # Step 8: Compress the directory tar -czvf "$output_dir.tar.gz" "$output_dir" From 836cfdf0617b21ccde1323f9f2420eab1fe2455c Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Fri, 16 Feb 2024 14:34:31 +0000 Subject: [PATCH 152/229] Fixed another typo in path to log files (not crucial for now) --- compress_qaly_files.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compress_qaly_files.sh b/compress_qaly_files.sh index ecbdac15..e2080a88 100644 --- a/compress_qaly_files.sh +++ b/compress_qaly_files.sh @@ -25,7 +25,7 @@ done # Step 5 & 6: Loop over directories and copy/ rename run_1_minos.log for dir in "${directories[@]}"; do newest_dir=$(ls -t "${base_path}${dir}/" | head -n 1) - cp "${base_path}${dir}/${newest_dir}/run_1_minos.log" "$output_dir/${dir}_1.log" + cp "${base_path}${dir}/${newest_dir}/logs/run_1_minos.log" "$output_dir/${dir}_1.log" done # Step 7: Copy QALY_comparison_*.html files From 449db8908e176d335d66445aeb4fbe669b24d708 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 19 Feb 2024 12:00:58 +0000 Subject: [PATCH 153/229] Fixed cv transitions targets in transition Makefile. Also changed the year for transition models for housing_tenure and financial_situation due to a change in the factor levels of housing_tenure in 2019. This meant that transition models where housing_tenure is a predictor (or an outcome) have to be pre-2019 for cross-validation (due to cv starting in 2015) --- minos/modules/financial_situation.py | 10 +++++++++- minos/modules/housing_tenure.py | 6 ++++-- minos/transitions/Makefile | 6 +++--- minos/utils_validation_vis.R | 4 ++-- minos/validation/cross_validation_default.Rmd | 4 ++-- 5 files changed, 20 insertions(+), 10 deletions(-) diff --git a/minos/modules/financial_situation.py b/minos/modules/financial_situation.py index cbce7d6b..286fbf98 100644 --- a/minos/modules/financial_situation.py +++ b/minos/modules/financial_situation.py @@ -102,7 +102,15 @@ def on_time_step(self, event): self.population_view.update(nextWaveFinancialPerception['financial_situation']) def calculate_financial_situation(self, pop): - year = 2020 + + if self.cross_validation: + # LA 19/2/24 + # factor levels for housing_tenure change in 2019 onwards. Therfore cv has to use model before 2019 if + # housing_tenure is included + year = 2018 + else: + year = min(self.year, 2020) + transition_model = r_utils.load_transitions(f"financial_situation/clm/financial_situation_{year}_{year + 1}", self.rpy2_modules) nextWaveFinancialPerception = r_utils.predict_next_timestep_clm(transition_model, diff --git a/minos/modules/housing_tenure.py b/minos/modules/housing_tenure.py index 5811ad65..ea06f5a6 100644 --- a/minos/modules/housing_tenure.py +++ b/minos/modules/housing_tenure.py @@ -125,8 +125,10 @@ def calculate_housing_tenure(self, pop): """ # load transition model based on year. if self.cross_validation: - # if cross-val, fix year to final year model - year = 2020 + # LA 19/2/24 + # factor levels for housing_tenure change in 2019 onwards. Therfore cv has to use model before 2019 as + # input population is derived from 2015 (currently) + year = 2018 else: year = min(self.year, 2020) diff --git a/minos/transitions/Makefile b/minos/transitions/Makefile index 9ddb42f8..932f2a76 100644 --- a/minos/transitions/Makefile +++ b/minos/transitions/Makefile @@ -49,12 +49,12 @@ $(TRANSITION_DATA)/hh_income/glmm/hh_income_new_GLMM.rds: $(FINALDATA)/2021_US_c ############# Default - Cross Validation ############# -cv_transitions: $(TRANSITION_DATA)/cross_validation/version5/ncigs/zip/ncigs_2018_2019.rds $(TRANSITION_DATA)/cross_validation/version5/SF_12/glmm/SF_12_GLMM.rds +cv_transitions: $(TRANSITION_DATA)/cross_validation/version5/ncigs/zip/ncigs_2020_2021.rds $(TRANSITION_DATA)/cross_validation/version5/SF_12_MCS/lmm/SF_12_MCS_LMM.rds -$(TRANSITION_DATA)/cross_validation/version5/ncigs/zip/ncigs_2018_2019.rds: $(FINALDATA)/cross_validation/batch5/2021_US_cohort.csv $(TRANSITION_SOURCE)/estimate_transitions.R $(TRANSITION_SOURCE)/model_definitions_default.txt $(TRANSITION_SOURCE)/transition_model_functions.R +$(TRANSITION_DATA)/cross_validation/version5/ncigs/zip/ncigs_2020_2021.rds: $(FINALDATA)/cross_validation/batch5/2021_US_cohort.csv $(TRANSITION_SOURCE)/estimate_transitions.R $(TRANSITION_SOURCE)/model_definitions_default.txt $(TRANSITION_SOURCE)/transition_model_functions.R $(RSCRIPT) $(SOURCEDIR)/transitions/estimate_transitions.R --cross_validation -$(TRANSITION_DATA)/cross_validation/version5/SF_12/glmm/SF_12_GLMM.rds: $(FINALDATA)/cross_validation/batch5/2021_US_cohort.csv $(TRANSITION_SOURCE)/estimate_longitudinal_transitions.R $(TRANSITION_SOURCE)/model_definitions_default.txt $(TRANSITION_SOURCE)/transition_model_functions.R +$(TRANSITION_DATA)/cross_validation/version5/SF_12_MCS/lmm/SF_12_MCS_LMM.rds: $(FINALDATA)/cross_validation/batch5/2021_US_cohort.csv $(TRANSITION_SOURCE)/estimate_longitudinal_transitions.R $(TRANSITION_SOURCE)/model_definitions_default.txt $(TRANSITION_SOURCE)/transition_model_functions.R $(RSCRIPT) $(SOURCEDIR)/transitions/estimate_longitudinal_transitions.R --cross_validation ############# SIPHER7 - Cross Validation ############# diff --git a/minos/utils_validation_vis.R b/minos/utils_validation_vis.R index 9ad22e44..c15217cb 100644 --- a/minos/utils_validation_vis.R +++ b/minos/utils_validation_vis.R @@ -515,12 +515,12 @@ snapshot_OP_plots <- function(raw, cv, var, target.years) { ## Yearly box plots for multiple years, compared between raw and cv runs multi_year_boxplots <- function(raw, cv, var) { raw.var <- raw %>% - dplyr::select(pidp, time, all_of(var)) %>% + dplyr::select(time, all_of(var)) %>% dplyr::filter(!.data[[var]] %in% miss.values) raw.var$source <- 'raw' cv.var <- cv %>% - dplyr::select(pidp, time, all_of(var)) %>% + dplyr::select(time, all_of(var)) %>% dplyr::filter(!.data[[var]] %in% miss.values) cv.var$source <- 'cross-validation' diff --git a/minos/validation/cross_validation_default.Rmd b/minos/validation/cross_validation_default.Rmd index ca6e191a..e1569787 100644 --- a/minos/validation/cross_validation_default.Rmd +++ b/minos/validation/cross_validation_default.Rmd @@ -460,13 +460,13 @@ q_q_comparison(raw, cv, 'nkids') ```{r} raw.nkids_max <- raw %>% - dplyr::select(pidp, time, nkids) %>% + dplyr::select(time, nkids) %>% group_by(time) %>% summarise(max_nkids = max(nkids)) %>% mutate(source = 'final_US') cv.nkids_max <- cv %>% - dplyr::select(pidp, time, nkids) %>% + dplyr::select(time, nkids) %>% group_by(time) %>% summarise(max_nkids = max(nkids)) %>% mutate(source = 'baseline_output') From 48fc9b591d6e433a748d1a0d4efd667c1b29f190 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Fri, 23 Feb 2024 10:55:45 +0000 Subject: [PATCH 154/229] Removing a bad assertion that was left in by mistake and adding a comma I forgot... --- minos/data_generation/US_format_raw_children_data.py | 2 -- minos/minosPipeline/RunPipeline.py | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/minos/data_generation/US_format_raw_children_data.py b/minos/data_generation/US_format_raw_children_data.py index 39886720..a80ab64d 100644 --- a/minos/data_generation/US_format_raw_children_data.py +++ b/minos/data_generation/US_format_raw_children_data.py @@ -10,8 +10,6 @@ def main(input_raw_data, year): - assert isinstance(year, int), f"year must be an integer, not {type(year)}" - # download children datasets in one at a time child_name = US_utils.US_file_name(year, "../UKDA-6614-stata/stata/stata13_se/", "child") # get hidp, pidp, and age. child_data = US_utils.load_file(child_name) diff --git a/minos/minosPipeline/RunPipeline.py b/minos/minosPipeline/RunPipeline.py index e7476e2b..e6611600 100755 --- a/minos/minosPipeline/RunPipeline.py +++ b/minos/minosPipeline/RunPipeline.py @@ -126,7 +126,7 @@ "GBIS": GBIS(), "goodHeatingDummy": goodHeatingDummy(), - "fossilFuelReplacementScheme": fossilFuelReplacementScheme() + "fossilFuelReplacementScheme": fossilFuelReplacementScheme(), "childUplift()": childUplift(), From e7abc99b1c1ef1801e0ac20eff135c55c571ff80 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 26 Feb 2024 11:15:34 +0000 Subject: [PATCH 155/229] Handle cases in child_ages where a new baby is born to a childless household. This resulted in a string of "0_None". These strings are now replaced with just 0 --- minos/modules/add_new_birth_cohorts.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/minos/modules/add_new_birth_cohorts.py b/minos/modules/add_new_birth_cohorts.py index dc37dbbf..8da6f22c 100755 --- a/minos/modules/add_new_birth_cohorts.py +++ b/minos/modules/add_new_birth_cohorts.py @@ -329,6 +329,12 @@ def on_time_step(self, event): population.loc[who_had_children_households, 'has_newborn'] = True population.loc[who_had_children_households, 'child_ages'] = population.loc[who_had_children_households, 'child_ages'].apply(lambda x: self.add_new_child_to_chain(x)) # add new child to children ages chain. + # LA 26/2/24 + # Problem when adding new babies to childless households + # Results in a string with structure '0_None' + # Therefore we need to remove the 'None' part of the string + population['child_ages'] = population['child_ages'].str.replace('0_None', '0') + # 2. Find individuals who have had children by pidp and increment nkids_ind by 1 #TODO future differentiation within a household of which kids belong to who in child age chains. who_had_children_individuals = population.loc[had_children, 'pidp'].index From 41b3a0a9f1548cb9506067a80d49a0f356807183 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 26 Feb 2024 11:25:28 +0000 Subject: [PATCH 156/229] Fixed the child_ages problem in a better way --- minos/modules/add_new_birth_cohorts.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/minos/modules/add_new_birth_cohorts.py b/minos/modules/add_new_birth_cohorts.py index 8da6f22c..e555db74 100755 --- a/minos/modules/add_new_birth_cohorts.py +++ b/minos/modules/add_new_birth_cohorts.py @@ -333,7 +333,8 @@ def on_time_step(self, event): # Problem when adding new babies to childless households # Results in a string with structure '0_None' # Therefore we need to remove the 'None' part of the string - population['child_ages'] = population['child_ages'].str.replace('0_None', '0') + #population['child_ages'] = population['child_ages'].str.replace('0_None', '0') + # Spotted the add_new_child_to_chain() function below and fixed it there instead. # 2. Find individuals who have had children by pidp and increment nkids_ind by 1 #TODO future differentiation within a household of which kids belong to who in child age chains. @@ -341,10 +342,11 @@ def on_time_step(self, event): population.loc[who_had_children_individuals, 'nkids_ind'] += 1 self.population_view.update(population[['nkids_ind', 'child_ages', 'nkids', 'has_newborn']]) + @staticmethod def add_new_child_to_chain(self, age_chain): value = age_chain#.values[0] - if type(value) == float or value is None: + if type(value) == float or value is None or value == 'None': return "0" else: return "0_" + age_chain @@ -363,6 +365,5 @@ def load_age_specific_fertility_rate_data(builder): def name(self): return 'nkids_age_specific_fertility' - def __repr__(self): return "nkidsFertilityAgeSpecificRates()" From 58fc89acc1f530c4136d695c5c2e71dd35b62a12 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 26 Feb 2024 15:16:55 +0000 Subject: [PATCH 157/229] Changed a method in nkidsFertilityAgeSpecificRates to static and fixed the function call --- minos/modules/add_new_birth_cohorts.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/minos/modules/add_new_birth_cohorts.py b/minos/modules/add_new_birth_cohorts.py index e555db74..a7d220e5 100755 --- a/minos/modules/add_new_birth_cohorts.py +++ b/minos/modules/add_new_birth_cohorts.py @@ -327,7 +327,7 @@ def on_time_step(self, event): who_had_children_households = population.loc[population['hidp'].isin(had_children_hidps),].index # Get all HIDPs who live in HH that has had a child population.loc[who_had_children_households, 'nkids'] += 1 population.loc[who_had_children_households, 'has_newborn'] = True - population.loc[who_had_children_households, 'child_ages'] = population.loc[who_had_children_households, 'child_ages'].apply(lambda x: self.add_new_child_to_chain(x)) # add new child to children ages chain. + population.loc[who_had_children_households, 'child_ages'] = population.loc[who_had_children_households, 'child_ages'].apply(lambda x: self.add_new_child_to_chain(x)) # add new child to children ages chain. # LA 26/2/24 # Problem when adding new babies to childless households @@ -343,7 +343,7 @@ def on_time_step(self, event): self.population_view.update(population[['nkids_ind', 'child_ages', 'nkids', 'has_newborn']]) @staticmethod - def add_new_child_to_chain(self, age_chain): + def add_new_child_to_chain(age_chain): value = age_chain#.values[0] if type(value) == float or value is None or value == 'None': From 4a84e1cfdc4d6f83bc6d5e3f5b0961668432f7cd Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 27 Feb 2024 16:55:08 +0000 Subject: [PATCH 158/229] Separated QALY outcomes targets into their own file - Makefile.QALY --- Makefile | 1 + minos/outcomes/Makefile | 193 +++++++++-------------------------- minos/outcomes/Makefile.QALY | 163 +++++++++++++++++++++++++++++ 3 files changed, 215 insertions(+), 142 deletions(-) create mode 100644 minos/outcomes/Makefile.QALY diff --git a/Makefile b/Makefile index b5f1f414..a37a85b2 100644 --- a/Makefile +++ b/Makefile @@ -144,6 +144,7 @@ include scripts/Makefile # running minos Makefile include minos/outcomes/Makefile # plotting makefile include minos/validation/Makefile # validation scripts include minos/outcomes/Makefile.maps # mapping functions +include minos/outcomes/Makefile.QALY # QALY calculations and vis include scripts/Makefile.SCP # SCP intervention scenarios #include docsrc/Makefile # sphinx makefile diff --git a/minos/outcomes/Makefile b/minos/outcomes/Makefile index 080043f2..e6fdd659 100644 --- a/minos/outcomes/Makefile +++ b/minos/outcomes/Makefile @@ -3,143 +3,6 @@ OUTCOMES=$(ROOT)/minos/outcomes #TODO: Now that the makefile is split and in the same directory as its scripts, can we do relative paths? -##################################### -# QALY Calculations -##################################### - -.phony: QALY QALY_baseline QALY_energyDownlift QALY_energyDownliftNoSupport - -QALY: - python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) - -## Default QALYs - -QALY_baseline: MODE=default_config -QALY_baseline: INTERVENTION=baseline -QALY_baseline: - python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) - -QALY_energyDownlift: MODE=default_config -QALY_energyDownlift: INTERVENTION=energyDownlift -QALY_energyDownlift: - python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) - -QALY_energyDownliftNoSupport: MODE=default_config -QALY_energyDownliftNoSupport: INTERVENTION=energyDownliftNoSupport -QALY_energyDownliftNoSupport: - python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) - -QALY_livingWage: MODE=default_config -QALY_livingWage: INTERVENTION=livingWageIntervention -QALY_livingWage: - python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) - -QALYs: QALY_baseline QALY_energysupport QALY_energynosupport QALY_livwage - $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises_diff.Rmd')" - $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" - -QALY_comparison_livingWage: QALY_baseline QALY_livingWage - $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" - firefox file://$(TESTING)/QALY_comparison_livingWage.html - - -QALY_comparisons: QALY_comparison_livingWage QALY_comparison_energyCrises - -################## ^ GENERIC QALY SCRIPT ^ ################## - -## No Replenishment QALYs (cohort) - -QALY_baseline_norepl: MODE=default_no_replenishment -QALY_baseline_norepl: INTERVENTION=baseline -QALY_baseline_norepl: - python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) - -QALY_energyDownlift_norepl: MODE=default_no_replenishment -QALY_energyDownlift_norepl: INTERVENTION=energyDownlift -QALY_energyDownlift_norepl: - python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) - -QALY_energyDownliftNoSupport_norepl: MODE=default_no_replenishment -QALY_energyDownliftNoSupport_norepl: INTERVENTION=energyDownliftNoSupport -QALY_energyDownliftNoSupport_norepl: - python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) - -QALY_livingWage_norepl: MODE=default_no_replenishment -QALY_livingWage_norepl: INTERVENTION=livingWageIntervention -QALY_livingWage_norepl: - python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) - -QALYs_norepl: QALY_baseline_norepl QALY_energysupport_norepl QALY_energynosupport_norepl QALY_livwage_norepl - $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises_diff.Rmd')" - $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" - -QALY_comparison_livingWage_norepl: QALY_baseline_norepl QALY_livwage_norepl - $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" - firefox file://$(TESTING)/QALY_comparison_livingWage.html - -## Glasgow QALYs - -QALY_baseline_glasgow: MODE=glasgow_scaled -QALY_baseline_glasgow: INTERVENTION=baseline -QALY_baseline_glasgow: - python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) - -QALY_energysupport_glasgow: MODE=glasgow_scaled -QALY_energysupport_glasgow: INTERVENTION=energyDownlift -QALY_energysupport_glasgow: - python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) - -QALY_energynosupport_glasgow: MODE=glasgow_scaled -QALY_energynosupport_glasgow: INTERVENTION=energyDownliftNoSupport -QALY_energynosupport_glasgow: - python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) - -QALY_livwage_glasgow: MODE=glasgow_scaled -QALY_livwage_glasgow: INTERVENTION=livingWageIntervention -QALY_livwage_glasgow: - python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) - -QALYs_glasgow: QALY_baseline_glasgow QALY_energysupport_glasgow QALY_energynosupport_glasgow QALY_livwage_glasgow - $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises_diff.Rmd')" - $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" - -QALY_comparison_livingWage_glasgow: QALY_baseline_glasgow QALY_livwage_glasgow - $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" - firefox file://$(TESTING)/QALY_comparison_livingWage.html - -## UK scale QALYs - -QALY_baseline_uk: MODE=SF12_uk_scaled -QALY_baseline_uk: INTERVENTION=baseline -QALY_baseline_uk: - python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) - -QALY_energysupport_uk: MODE=SF12_uk_scaled -QALY_energysupport_uk: INTERVENTION=energyDownlift -QALY_energysupport_uk: - python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) - -QALY_energynosupport_uk: MODE=SF12_uk_scaled -QALY_energynosupport_uk: INTERVENTION=energyDownliftNoSupport -QALY_energynosupport_uk: - python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) - -QALY_livwage_uk: MODE=SF12_uk_scaled -QALY_livwage_uk: INTERVENTION=livingWageIntervention -QALY_livwage_uk: - python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) - -QALYs_uk: QALY_baseline_uk QALY_energysupport_uk QALY_energynosupport_uk QALY_livwage_uk - $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises_diff.Rmd')" - $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" - -QALY_comparison_livingWage_uk: QALY_baseline_uk QALY_livwage_uk - $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" - firefox file://$(TESTING)/QALY_comparison_livingWage.html - - - - ##################################### # Post-hoc aggregation of multiple MINOS runs on bash terminal. ##################################### @@ -206,7 +69,7 @@ default_all_lineplots_MCS: default_all_lineplots # will change to SF_12_MCS when PCS is implemented all_lineplots_MCS: MODE=default_config -all_lineplots_MCS: AGG_VAR=SF_12 +all_lineplots_MCS: AGG_VAR=SF_12_MCS all_lineplots_MCS: default_all_lineplots # PCS COMING SOON THIS WON'T WORK YET! @@ -215,7 +78,7 @@ all_lineplots_PCS: AGG_VAR=SF_12_PCS all_lineplots_PCS: default_all_lineplots lineplot_livingWage: MODE=default_config -lineplot_livingWage: AGG_VAR=SF_12 +lineplot_livingWage: AGG_VAR=SF_12_PCS lineplot_livingWage: aggregate_minos_output_living_wage lineplot_livingWage: $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/testing_livingWage_intervention.Rmd')" @@ -264,6 +127,15 @@ living_wage_lineplot: MODE="default_config" living_wage_lineplot: python3 minos/outcomes/make_lineplots_macros.py $(MODE) "living_wage" +########################### +# Energy Crisis Lineplots # +########################### + +# Living Wage Intervention +energyCrisis_lineplot: MODE="default_config" +energyCrisis_lineplot: + python3 minos/outcomes/make_lineplots_macros.py $(MODE) "ebss" + ########################################## # Child Income Intervention Lineplots # ####################################### @@ -303,14 +175,51 @@ aggregate_25_relative_poverty: MODE="default_config" aggregate_25_relative_poverty: bash minos/outcomes/make_lineplots_macros.sh "25_relative_poverty" -aggregate_25_all_children: MODE="default_config" -aggregate_25_all_children: - bash minos/outcomes/make_lineplots_macros.sh $(MODE) "25_all" ############################################################# # Default config visualisations of universal credit uplifts # ############################################################# +############################## +# All Children # +############################## + +aggregate_25_all_children: MODE="default_config" +aggregate_25_all_children: + bash minos/outcomes/make_lineplots_macros.sh $(MODE) "25_all" + +aggregate_30_all_children: MODE="default_config" +aggregate_30_all_children: + python3 minos/outcomes/make_lineplots_macros.py $(MODE) "30_all" + +aggregate_35_all_children: MODE="default_config" +aggregate_35_all_children: + python3 minos/outcomes/make_lineplots_macros.py $(MODE) "35_all" + +aggregate_40_all_children: MODE="default_config" +aggregate_40_all_children: + python3 minos/outcomes/make_lineplots_macros.py $(MODE) "40_all" + +aggregate_45_all_children: MODE="default_config" +aggregate_45_all_children: + python3 minos/outcomes/make_lineplots_macros.py $(MODE) "45_all" + +aggregate_50_all_children: MODE="default_config" +aggregate_50_all_children: + python3 minos/outcomes/make_lineplots_macros.py $(MODE) "50_all" + +aggregate_75_all_children: MODE="default_config" +aggregate_75_all_children: + python3 minos/outcomes/make_lineplots_macros.py $(MODE) "75_all" + +aggregate_100_all_children: MODE="default_config" +aggregate_100_all_children: + python3 minos/outcomes/make_lineplots_macros.py $(MODE) "100_all" + +############################## +# Universal Credit # +############################## + aggregate_25_universal_credit: MODE="default_config" aggregate_25_universal_credit: python3 minos/outcomes/make_lineplots_macros.py $(MODE) "25_universal_credit" diff --git a/minos/outcomes/Makefile.QALY b/minos/outcomes/Makefile.QALY new file mode 100644 index 00000000..a643d369 --- /dev/null +++ b/minos/outcomes/Makefile.QALY @@ -0,0 +1,163 @@ + + +##################################### +# QALY Calculations +##################################### + +.phony: QALY QALY_baseline QALY_energyDownlift QALY_energyDownliftNoSupport + +QALY: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +## Default QALYs + +QALY_baseline: MODE=default_config +QALY_baseline: INTERVENTION=baseline +QALY_baseline: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_energyDownlift: MODE=default_config +QALY_energyDownlift: INTERVENTION=energyDownlift +QALY_energyDownlift: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_energyDownliftNoSupport: MODE=default_config +QALY_energyDownliftNoSupport: INTERVENTION=energyDownliftNoSupport +QALY_energyDownliftNoSupport: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_livingWage: MODE=default_config +QALY_livingWage: INTERVENTION=livingWageIntervention +QALY_livingWage: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALYs: QALY_baseline QALY_energyDownlift QALY_energyDownliftNoSupport QALY_livingWage + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises_diff.Rmd')" + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" + +QALY_comparison_livingWage: QALY_baseline QALY_livingWage + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" + firefox file://$(TESTING)/QALY_comparison_livingWage.html + +QALY_comparison_energyCrises: QALY_energyDownlift QALY_energyDownliftNoSupport + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises_diff.Rmd')" + firefox file://$(TESTING)/QALY_comparison_energyCrises_diff.html + +QALY_comparisons: QALY_comparison_livingWage QALY_comparison_energyCrises + +################################################################################## +# ^ GENERIC QALY SCRIPT ^ +################################################################################## + +## No Replenishment QALYs (cohort) + +QALY_baseline_norepl: MODE=default_no_replenishment +QALY_baseline_norepl: INTERVENTION=baseline +QALY_baseline_norepl: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_energyDownlift_norepl: MODE=default_no_replenishment +QALY_energyDownlift_norepl: INTERVENTION=energyDownlift +QALY_energyDownlift_norepl: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_energyDownliftNoSupport_norepl: MODE=default_no_replenishment +QALY_energyDownliftNoSupport_norepl: INTERVENTION=energyDownliftNoSupport +QALY_energyDownliftNoSupport_norepl: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_livingWage_norepl: MODE=default_no_replenishment +QALY_livingWage_norepl: INTERVENTION=livingWageIntervention +QALY_livingWage_norepl: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALYs_norepl: QALY_baseline_norepl QALY_energysupport_norepl QALY_energynosupport_norepl QALY_livwage_norepl + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises_diff.Rmd')" + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" + +QALY_comparison_livingWage_norepl: QALY_baseline_norepl QALY_livwage_norepl + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" + firefox file://$(TESTING)/QALY_comparison_livingWage.html + +################################################################################## +# Glasgow QALYs +################################################################################## + +QALY_baseline_glasgow: MODE=glasgow_scaled +QALY_baseline_glasgow: INTERVENTION=baseline +QALY_baseline_glasgow: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_energysupport_glasgow: MODE=glasgow_scaled +QALY_energysupport_glasgow: INTERVENTION=energyDownlift +QALY_energysupport_glasgow: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_energynosupport_glasgow: MODE=glasgow_scaled +QALY_energynosupport_glasgow: INTERVENTION=energyDownliftNoSupport +QALY_energynosupport_glasgow: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_livwage_glasgow: MODE=glasgow_scaled +QALY_livwage_glasgow: INTERVENTION=livingWageIntervention +QALY_livwage_glasgow: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALYs_glasgow: QALY_baseline_glasgow QALY_energysupport_glasgow QALY_energynosupport_glasgow QALY_livwage_glasgow + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises_diff.Rmd')" + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" + +QALY_comparison_livingWage_glasgow: QALY_baseline_glasgow QALY_livwage_glasgow + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" + firefox file://$(TESTING)/QALY_comparison_livingWage.html + +################################################################################## +# UK scale QALYs +################################################################################## + +QALY_baseline_uk: MODE=SF12_uk_scaled +QALY_baseline_uk: INTERVENTION=baseline +QALY_baseline_uk: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_energysupport_uk: MODE=SF12_uk_scaled +QALY_energysupport_uk: INTERVENTION=energyDownlift +QALY_energysupport_uk: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_energynosupport_uk: MODE=SF12_uk_scaled +QALY_energynosupport_uk: INTERVENTION=energyDownliftNoSupport +QALY_energynosupport_uk: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_livwage_uk: MODE=SF12_uk_scaled +QALY_livwage_uk: INTERVENTION=livingWageIntervention +QALY_livwage_uk: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALYs_uk: QALY_baseline_uk QALY_energysupport_uk QALY_energynosupport_uk QALY_livwage_uk + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_energyCrises_diff.Rmd')" + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" + +QALY_comparison_livingWage_uk: QALY_baseline_uk QALY_livwage_uk + $(RSCRIPT) -e "require(rmarkdown); render('$(TESTING)/QALY_comparison_livingWage.Rmd')" + firefox file://$(TESTING)/QALY_comparison_livingWage.html + + +############################################################################################ +# Scottish Child Payment Interventions +############################################################################################ + +QALY_SCP_25_UC: MODE=default_config +QALY_SCP_25_UC: INTERVENTION=25UniversalCredit +QALY_SCP_25_UC: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_SCP_100_All: MODE=default_config +QALY_SCP_100_All: INTERVENTION=100All +QALY_SCP_100_All: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_vis_SCP_100_All: + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', output_file='QALY_SCP_100_All.html', param=list(experiment='default_config/', base='baseline', intervention='100All'))" + firefox file://$(OUTCOMES)/QALY_SCP_100_All.html From 64c1e037f8d722b95fc756de949e3199f544de84 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 27 Feb 2024 16:55:52 +0000 Subject: [PATCH 159/229] Changed all references from SF_12 to SF_12_MCS in outcomes scripts --- minos/outcomes/aggregate_subset_functions.py | 2 +- minos/outcomes/make_lineplots.py | 47 ++++++++------- minos/outcomes/make_lineplots_macros.py | 62 ++++++++++---------- 3 files changed, 58 insertions(+), 53 deletions(-) diff --git a/minos/outcomes/aggregate_subset_functions.py b/minos/outcomes/aggregate_subset_functions.py index 301b9ca8..beeced32 100755 --- a/minos/outcomes/aggregate_subset_functions.py +++ b/minos/outcomes/aggregate_subset_functions.py @@ -230,7 +230,7 @@ def dynamic_subset_function(data, subset_chain_string=None, mode='default_config def get_required_intervention_variables(subset_function_string): # get required variables for intervention used in aggregate_subset_function. makes csvs load much faster. - default_variables = ["weight", "pidp", "hidp", "alive", "SF_12", 'time', "housing_quality", "hh_income", + default_variables = ["weight", "pidp", "hidp", "alive", "SF_12_MCS", 'time', "housing_quality", "hh_income", "neighbourhood_safety", "nkids", "loneliness"] if "boosted" in subset_function_string: diff --git a/minos/outcomes/make_lineplots.py b/minos/outcomes/make_lineplots.py index 60bbe187..c8042105 100644 --- a/minos/outcomes/make_lineplots.py +++ b/minos/outcomes/make_lineplots.py @@ -75,17 +75,17 @@ def aggregate_boosted_counts_and_cumulative_score(df, v): new_df["number_boosted"] = [df.shape[0]] + np.sum(df.groupby("hidp")['nkids'].max()) new_df[f'summed_{v}'] = [sum(df[v])] - if v == "SF_12" and df.shape[0] !=0: + if v == "SF_12_MCS" and df.shape[0] !=0: #df = df.loc[df['weight']>0, ] #df['weight'] = 1/df['weight'] #new_df[f"prct_below_45.6"] = sum(df['weight']*(df['SF_12'] < 45.6))/sum(df['weight']) - new_df[f"prct_below_45.6"] = sum(df['SF_12'] < 45.6)/df.shape[0] + new_df[f"prct_below_45.6"] = sum(df['SF_12_MCS'] < 45.6)/df.shape[0] if "boost_amount" in df.columns: new_df["intervention_cost"] = np.sum(df.groupby("hidp")['boost_amount'].max()) return new_df -def aggregate_csv(file, subset_function_string=None, outcome_variable="SF_12", aggregate_method=np.nanmean, +def aggregate_csv(file, subset_function_string=None, outcome_variable="SF_12_MCS", aggregate_method=np.nanmean, mode="default_config", region=None): """ @@ -111,6 +111,11 @@ def aggregate_csv(file, subset_function_string=None, outcome_variable="SF_12", a required_columns.append("ZoneID") data = pd.read_csv(file, usecols=required_columns, low_memory=True, engine='c') # low_memory could be buggy but is faster. + + # Occasionally (unsure why) we get some records with a missing weight var (== nan). Replace these with 0 + # (never seen more than 1 so I think this is fine) + data['weight'][data['weight'].isna()] = 0 + population_size = data.shape[0] if subset_function_string: data = subset_minos_data(data, subset_function_string, mode) @@ -129,7 +134,7 @@ def aggregate_csv(file, subset_function_string=None, outcome_variable="SF_12", a return agg_value -def aggregate_variables_by_year(source, tag, years, subset_func_string, v="SF_12", ref="Baseline", method=np.nanmean, +def aggregate_variables_by_year(source, tag, years, subset_func_string, v="SF_12_MCS", ref="Baseline", method=np.nanmean, mode="default_config", region=None): """ Get multiple MINOS files, subset and aggregate over some variable and aggregate method. @@ -181,7 +186,7 @@ def aggregate_variables_by_year(source, tag, years, subset_func_string, v="SF_12 aggregated_means = [None] if method == weighted_nanmean or method == child_uplift_cost_sum: - single_year_aggregates = pd.DataFrame(aggregated_means, columns = [v]) + single_year_aggregates = pd.DataFrame(aggregated_means, columns=[v]) single_year_aggregates['year'] = year single_year_aggregates['tag'] = tag aggregated_data = pd.concat([aggregated_data, single_year_aggregates]) @@ -193,8 +198,8 @@ def aggregate_variables_by_year(source, tag, years, subset_func_string, v="SF_12 aggregated_data = pd.concat([aggregated_data, single_year_aggregate]) elif method == aggregate_boosted_counts_and_cumulative_score: for i, single_year_aggregate in enumerate(aggregated_means): - if type(single_year_aggregate) != pd.DataFrame: # if no data available create a dummy frame to preserve data frame structure. - single_year_aggregate = pd.DataFrame([i], columns = ['number_boosted']) + if not isinstance(single_year_aggregate, pd.DataFrame): # if no data available create a dummy frame to preserve data frame structure. + single_year_aggregate = pd.DataFrame([i], columns=['number_boosted']) single_year_aggregate["number_boosted"] = np.nan single_year_aggregate[f"summed_{v}"] = np.nan single_year_aggregate["intervention_cost"] = np.nan @@ -333,7 +338,7 @@ def aggregate_lineplot(df, destination, prefix, v, method): # Sort out axis labels y_label = v - if v == 'SF_12': + if v == 'SF_12_MCS': y_label = 'SF12 MCS Percentage Change' if method == weighted_nanmean: @@ -396,7 +401,7 @@ def child_uplift_cost_sum(df, v, weights='weight'): return np.nansum(weights * group[v].min())/np.nansum(weights) -def main(directories, tags, subset_function_strings, prefix, mode='default_config', ref="Baseline", v="SF_12", +def main(directories, tags, subset_function_strings, prefix, mode='default_config', ref="Baseline", v="SF_12_MCS", method='nanmean', region=None): """ Main method for converting multiple sources of MINOS data into a lineplot. @@ -445,11 +450,11 @@ def main(directories, tags, subset_function_strings, prefix, mode='default_confi method=method, region=region) aggregate_long_stack = pd.concat([aggregate_long_stack, new_aggregate_data]) - if v == "SF_12" and method == weighted_nanmean: + if v == "SF_12_MCS" and method == weighted_nanmean: scaled_data = relative_scaling(aggregate_long_stack, v, ref) print("relative scaling done. plotting.. ") aggregate_lineplot(scaled_data, "plots", prefix, v, method) - elif v == "SF_12" and method == aggregate_boosted_counts_and_cumulative_score: + elif v == "SF_12_MCS" and method == aggregate_boosted_counts_and_cumulative_score: # groupby intervention and cumsum over time. aggregate_long_stack[f"{v}_AUC"] = aggregate_long_stack.groupby(['tag', 'id'])[f"summed_{v}"].cumsum() #scaled_data = relative_scaling(aggregate_long_stack, "sf12_auc", ref) @@ -515,16 +520,16 @@ def main(directories, tags, subset_function_strings, prefix, mode='default_confi print("MAIN HERE IS JUST FOR DEBUGGING. RUN MAIN IN A NOTEBOOK INSTEAD. ") #define test parameters and run. - directories = "baseline,25RelativePoverty,50RelativePoverty" - tags = "Baseline,25 Relative Poverty,50 Relative Poverty" - subset_function_strings = "who_below_living_wage_and_kids,who_boosted,who_boosted" - prefix = "baseline_25_50_relative_poverty" - mode = 'default_config' - ref = "Baseline" - v = "SF12" - method = 'nanmean' - - main(directories, tags, subset_function_strings, prefix, mode, ref, v, method) + # directories = "baseline,25RelativePoverty,50RelativePoverty" + # tags = "Baseline,25 Relative Poverty,50 Relative Poverty" + # subset_function_strings = "who_below_living_wage_and_kids,who_boosted,who_boosted" + # prefix = "baseline_25_50_relative_poverty" + # mode = 'default_config' + # ref = "Baseline" + # v = "SF12" + # method = 'nanmean' + + #main(directories, tags, subset_function_strings, prefix, mode, ref, v, method) # TODO find a way to aggregate boxplots/ridgelines together \ No newline at end of file diff --git a/minos/outcomes/make_lineplots_macros.py b/minos/outcomes/make_lineplots_macros.py index 9b4ec0f3..d650e9a6 100644 --- a/minos/outcomes/make_lineplots_macros.py +++ b/minos/outcomes/make_lineplots_macros.py @@ -14,7 +14,7 @@ def all_child_lineplot(*args): subset_function_strings = "who_kids,who_boosted" prefix = "baseline_all_child_uplift" config_mode = "default_config" - lineplot_main(directories, tags, subset_function_strings, prefix, mode=config_mode, ref="Baseline", v="SF_12", + lineplot_main(directories, tags, subset_function_strings, prefix, mode=config_mode, ref="Baseline", v="SF_12_MCS", method='nanmean') @@ -24,7 +24,7 @@ def poverty_line_child_lineplot(*args): subset_function_strings = "who_below_poverty_line_and_kids,who_boosted" prefix = "baseline_poverty_child_uplift" config_mode = "default_config" - lineplot_main(directories, tags, subset_function_strings, prefix, mode=config_mode, ref="Baseline", v="SF_12", + lineplot_main(directories, tags, subset_function_strings, prefix, mode=config_mode, ref="Baseline", v="SF_12_MCS", method='nanmean', region=None) @@ -35,7 +35,7 @@ def living_wage_lineplot(*args): prefix = "baseline_living_wage" config_mode = "default_config" ref="Baseline" - v="SF_12" + v="SF_12_MCS" method = "nanmean" lineplot_main(directories, tags, subset_function_strings, prefix, mode=config_mode, ref=ref, v=v, method=method) @@ -50,7 +50,7 @@ def epcg_and_no_support_lineplot(*args): prefix = "baseline_energy_downlift" config_mode = "default_config" ref="Baseline" - v = "SF_12" + v = "SF_12_MCS" method = "nanmean" lineplot_main(directories, tags, subset_function_strings, prefix, mode=config_mode, ref=ref, v=v, method=method) @@ -64,7 +64,7 @@ def ebss_lineplot(*args): subset_function_strings = "who_uses_energy,who_boosted" prefix = "baseline_ebss" config_mode = "default_config" - lineplot_main(directories, tags, subset_function_strings, prefix, mode=config_mode, ref="Baseline", v="SF_12", + lineplot_main(directories, tags, subset_function_strings, prefix, mode=config_mode, ref="Baseline", v="SF_12_MCS", method='nanmean') @@ -74,7 +74,7 @@ def all_five_lineplots(*args): subset_function_strings = "who_alive,who_boosted,who_boosted,who_boosted,who_boosted" prefix = "all_five_combined" config_mode = "default_config" - lineplot_main(directories, tags, subset_function_strings, prefix, mode=config_mode, ref="Baseline", v="SF_12", + lineplot_main(directories, tags, subset_function_strings, prefix, mode=config_mode, ref="Baseline", v="SF_12_MCS", method='nanmean') @@ -102,7 +102,7 @@ def glasgow_deciles_lineplot(config_mode, source, subset_function): subset_function_strings = "who_alive,who_first_simd_decile,who_second_simd_decile,who_third_simd_decile,who_fourth_simd_decile,who_fifth_simd_decile,who_sixth_simd_decile,who_seventh_simd_decile,who_eighth_simd_decile,who_ninth_simd_decile,who_tenth_simd_decile" prefix = f"25_{source}_simd_deciles" ref = "National Average" - v = "SF_12" + v = "SF_12_MCS" method = 'nanmean' lineplot_main(directories, tags, subset_function_strings, prefix, mode=config_mode, ref=ref, v=v, method=method) @@ -114,7 +114,7 @@ def poverty_line_simd_deciles_lineplot(*args): prefix = "25_poverty_simd_deciles" config_mode = "glasgow_scaled" ref = "National Average" - v = "SF_12" + v = "SF_12_MCS" method = 'nanmean' lineplot_main(directories, tags, subset_function_strings, prefix, mode=config_mode, ref=ref, v=v, method=method) @@ -126,7 +126,7 @@ def poverty_line_first_decile_lineplot(*args): prefix = "25_poverty_first_simd_decile" config_mode = "glasgow_scaled" ref = "National Average" - v = "SF_12" + v = "SF_12_MCS" method = 'nanmean' lineplot_main(directories, tags, subset_function_strings, prefix, mode=config_mode, ref=ref, v=v, method=method) @@ -138,7 +138,7 @@ def poverty_line_tenth_decile_lineplot(*args): prefix = "25_poverty_tenth_simd_decile" config_mode = "glasgow_scaled" ref = "National Average" - v = "SF_12" + v = "SF_12_MCS" method = 'nanmean' # lineplot_main(directories, tags, subset_function_strings, prefix, mode=config_mode, ref=ref, v=v, method=method) @@ -150,7 +150,7 @@ def poverty_line_fifth_decile_lineplot(*args): prefix = "25_poverty_fifth_simd_decile" config_mode = "glasgow_scaled" ref = "National Average" - v = "SF_12" + v = "SF_12_MCS" method = 'nanmean' lineplot_main(directories, tags, subset_function_strings, prefix, mode=config_mode, ref=ref, v=v, method=method) @@ -162,7 +162,7 @@ def simd_decile_baseline_lineplot(*args): prefix = "baseline_simd_deciles" config_mode = "glasgow_scaled" ref = "National Average" - v = "SF_12" + v = "SF_12_MCS" method = 'nanmean' lineplot_main(directories, tags, subset_function_strings, prefix, mode=config_mode, ref=ref, v=v, method=method) @@ -174,7 +174,7 @@ def epcg_simd_deciles_lineplot(*args): prefix = "ebss_simd_deciles" config_mode = "glasgow_scaled" ref = "National Average" - v = "SF_12" + v = "SF_12_MCS" method = 'nanmean' lineplot_main(directories, tags, subset_function_strings, prefix, mode=config_mode, ref=ref, v=v, method=method) @@ -185,7 +185,7 @@ def quintiles_lineplot(config_mode, source, region): subset_function_strings = "who_alive,who_first_simd_quintile,who_second_simd_quintile,who_third_simd_quintile,who_fourth_simd_quintile,who_fifth_simd_quintile" prefix = f"{source}_{region}_simd_quintiles_" ref = "National Average" - v = "SF_12" + v = "SF_12_MCS" method = 'nanmean' lineplot_main(directories, tags, subset_function_strings, prefix, mode=config_mode, ref=ref, v=v, method=method, region=region) @@ -202,7 +202,7 @@ def all_child(config_mode, boost_amount): subset_function_strings = "who_kids,who_boosted" prefix = f"{boost_amount}_all" ref = "Baseline" - v = "SF_12" + v = "SF_12_MCS" method = 'nanmean' lineplot_main(directories, tags, subset_function_strings, prefix, mode=config_mode, ref=ref, v=v, method=method) @@ -214,7 +214,7 @@ def relative_poverty(config_mode, boost_amount): subset_function_strings = "who_relative_poverty_and_kids,who_boosted" prefix = f"{boost_amount}_relative_poverty" ref = "Baseline" - v = "SF_12" + v = "SF_12_MCS" method = 'nanmean' lineplot_main(directories, tags, subset_function_strings, prefix, mode=config_mode, ref=ref, v=v, method=method) @@ -226,7 +226,7 @@ def universal_credit(config_mode, boost_amount, region=None): subset_function_strings = "who_universal_credit_and_kids,who_boosted" prefix = f"{boost_amount}_{region}_universal_credit" ref = "Baseline" - v = "SF_12" + v = "SF_12_MCS" method = 'nanmean' lineplot_main(directories, tags, subset_function_strings, prefix, mode=config_mode, ref=ref, v=v, method=method, region=region) @@ -238,7 +238,7 @@ def universal_credit_priority_young_mothers(config_mode, boost_amount, region=No subset_function_strings = "who_young_mothers,who_young_mothers" prefix = f"{boost_amount}_single_mothers_universal_credit" ref = "Baseline" - v = "SF_12" + v = "SF_12_MCS" method = 'nanmean' lineplot_main(directories, tags, subset_function_strings, prefix, mode=config_mode, ref=ref, v=v, method=method, region=region) @@ -250,7 +250,7 @@ def universal_credit_priority_subgroups(config_mode, boost_amount, region): subset_function_strings = "who_priority_subgroups_and_kids,who_priority_subgroups_and_kids" prefix = f"{boost_amount}_any_subgroup_universal_credit" ref = "Baseline" - v = "SF_12" + v = "SF_12_MCS" method = 'nanmean' lineplot_main(directories, tags, subset_function_strings, prefix, mode=config_mode, ref=ref, v=v, method=method, region=region) @@ -263,7 +263,7 @@ def universal_credit_multiple_priority_subgroups(config_mode, boost_amount, regi subset_function_strings = "who_multiple_priority_subgroups_and_kids,who_multiple_priority_subgroups_and_kids" prefix = f"{boost_amount}_many_subgroups_universal_credit" ref = "Baseline" - v = "SF_12" + v = "SF_12_MCS" method = 'nanmean' lineplot_main(directories, tags, subset_function_strings, prefix, mode=config_mode, ref=ref, v=v, method=method, region=region) @@ -276,7 +276,7 @@ def priority_only(config_mode, boost_amount): subset_function_strings = "who_vulnerable_subgroups,who_boosted" prefix = f"{boost_amount}_priority" ref = "Baseline" - v = "SF_12" + v = "SF_12_MCS" method = 'nanmean' lineplot_main(directories, tags, subset_function_strings, prefix, mode=config_mode, ref=ref, v=v, method=method) @@ -288,7 +288,7 @@ def UC_priority(config_mode, boost_amount): subset_function_strings = "who_universal_credit_and_kids,who_boosted,who_boosted" prefix = f"{boost_amount}_UC_and_priority" ref = "Baseline" - v = "SF_12" + v = "SF_12_MCS" method = 'nanmean' lineplot_main(directories, tags, subset_function_strings, prefix, mode=config_mode, ref=ref, v=v, method=method) @@ -300,7 +300,7 @@ def UC_relative_poverty(config_mode, boost_amount): subset_function_strings = "who_universal_credit_and_kids,who_boosted,who_boosted" prefix = f"{boost_amount}_UC_and_relative_poverty" ref = "Baseline" - v = "SF_12" + v = "SF_12_MCS" method = 'nanmean' lineplot_main(directories, tags, subset_function_strings, prefix, mode=config_mode, ref=ref, v=v, method=method) @@ -312,7 +312,7 @@ def incremental_25_to_100(config_mode, intervention_name, intervention_tag, subs subset_function_strings = f"{subset_function},who_boosted,who_boosted,who_boosted,who_boosted" prefix = f"25_100_incremental_{intervention_name}_uplift" ref = "Baseline" - v = "SF_12" + v = "SF_12_MCS" method = 'nanmean' lineplot_main(directories, tags, subset_function_strings, prefix, mode=config_mode, ref=ref, v=v, method=method) @@ -324,7 +324,7 @@ def incremental_25_to_50(config_mode, intervention_name, intervention_tag, subse subset_function_strings = f"{subset_function},who_boosted,who_boosted" prefix = f"25_50_incremental_{intervention_name}_uplift" ref = "Baseline" - v = "SF_12" + v = "SF_12_MCS" method = 'nanmean' lineplot_main(directories, tags, subset_function_strings, prefix, mode=config_mode, ref=ref, v=v, method=method) method = 'SF12_AUC' @@ -340,7 +340,7 @@ def incremental_25_to_50_by_5(config_mode, intervention_name, intervention_tag, subset_function_strings = f"{subset_function},who_boosted,who_boosted" prefix = f"{uplift_amount}_{intervention_name}_uplift" ref = "Baseline" - v = "SF_12" + v = "SF_12_MCS" method = 'nanmean' lineplot_main(directories, tags, subset_function_strings, prefix, mode=config_mode, ref=ref, v=v, method=method, region=region) uplift_amount += increment @@ -358,7 +358,7 @@ def incremental_25_to_50_by_5_together(config_mode, intervention_name, intervent subset_function_strings = f"{subset_function}" + (f",{subset_function}" * 6) prefix = f"25_50_by_5_together{intervention_name}_uplift" ref = "Baseline" - v = "SF_12" + v = "SF_12_MCS" method = 'nanmean' lineplot_main(directories, tags, subset_function_strings, prefix, mode=config_mode, ref=ref, v=v, method=method, region=region) @@ -392,10 +392,10 @@ def incremental_25_to_50_by_5_together(config_mode, intervention_name, intervent "75_UC_priority": UC_priority, "100_UC_priority": UC_priority, - "25_all": relative_poverty, - "50_all": relative_poverty, - "75_all": relative_poverty, - "100_all": relative_poverty, + "25_all": all_child, + "50_all": all_child, + "75_all": all_child, + "100_all": all_child, "25_relative_poverty_": [25], "50_relative_poverty": [50], From 559c614f3bcc26e51ee2e605ef6971979f4296a3 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 27 Feb 2024 16:57:06 +0000 Subject: [PATCH 160/229] Added a generic QALY comparison RNotebook with parameters for the experiment, baseline, and intervention so we can run multiple combinations of these things with the same script --- minos/outcomes/QALY_comparison2.Rmd | 174 ++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 minos/outcomes/QALY_comparison2.Rmd diff --git a/minos/outcomes/QALY_comparison2.Rmd b/minos/outcomes/QALY_comparison2.Rmd new file mode 100644 index 00000000..b5a51a78 --- /dev/null +++ b/minos/outcomes/QALY_comparison2.Rmd @@ -0,0 +1,174 @@ +--- +params: + experiment: '' + base: '' + intervention: '' +title: "QALY Comparisons - r`experiment` - r`intervention` - r`baseline`" +output: + html_document: + toc: true + toc_float: true + collapsed: false + number_sections: true + df_print: paged + code_folding: hide +--- + +# SETUP + +```{r "setup", include=FALSE} + +require(tidyverse) +require(ggplot2) +require(knitr) +require(here) +require(MESS) +require(scales) + +#workingDir <- "/home/luke/Documents/WORK/MINOS/" +workingDir <- normalizePath('../') +knitr::opts_knit$set(root.dir = workingDir) +knitr::opts_chunk$set( + options(scipen = 999) +) +rm(workingDir) +``` + +```{r} +source(here::here('minos', 'utils_datain.R')) +source(here::here('minos', 'utils_qaly.R')) +``` + +## Parameters + +```{r} +# experiment <- params$experiment +# base <- params$base +# intervention <- params$intervention + +experiment <- 'default_config' +baseline <- 'baseline' +intervention <- '100All' +``` + + +## Data + +```{r} +# # read in QALY files for baseline and interventions +out.path <- here::here('output', experiment) + +base.path <- get_latest_runtime_subdirectory(here::here(out.path, baseline)) +int.path <- get_latest_runtime_subdirectory(here::here(out.path, intervention)) + +base <- read.csv(paste0(base.path, 'qalys.csv')) +int <- read.csv(paste0(int.path, 'qalys.csv')) + +# combine the dataframes +combined <- rbind(base, int) +``` + +# Intervention Information + +Just going to plot some things here like how many people affected by an intervention and what the mean / total boost amounts look like. + +```{r} +ggplot(combined, aes(x = year, y = pop_boosted, group = intervention, colour = intervention)) + + geom_smooth() + + labs(title = 'Number Individuals Receiving Intervention') + +combined.diff <- combined %>% + select(run_id, year, intervention, total_boost) %>% + pivot_wider(names_from = 'intervention', + values_from = 'total_boost') %>% + mutate(diff.boost = .data[[intervention]] - .data[[baseline]]) + +ggplot(combined.diff, aes(x = year, y = diff.boost)) + + geom_smooth() + + labs(title = 'Total Boost amount by year') + + scale_y_continuous(label = comma) + +ggplot(combined, aes(x = year, y = alive_ratio, group = intervention, colour = intervention)) + + geom_smooth() + + labs(title = 'Ratio of living to dead in output dataframe') + +## incidence of mortality +deaths <- combined %>% + select(run_id, intervention, year, alive_pop, dead_pop) %>% + group_by(run_id, intervention) %>% + mutate(total_pop = alive_pop + dead_pop, + deaths_this_year = dead_pop - lag(dead_pop)) %>% + group_by(run_id, year, intervention) %>% + summarise(death_incidence = deaths_this_year / total_pop) + +ggplot(deaths, aes(x = year, y = death_incidence, group = intervention, colour = intervention)) + + geom_smooth() + + labs(title = 'Incidence of Mortality Comparison') +``` + +# QALY comparison + +```{r} +QALY_comparison(base = base, + base.name = baseline, + int = int, + int.name = intervention) +``` + +## SF12 Change + +```{r} +sf12.plots(base = base, + base.name = baseline, + int = int, + int.name = intervention) +``` + +## Area Under the Curve + +```{r} +auc.plots(base = base, + base.name = baseline, + intervention = int, + int.name = intervention) +``` + +# Cost per QALY + +The [UK Government](https://www.gov.uk/government/publications/valuation-of-risks-to-life-and-health-monetary-value-of-a-life-year-voly/a-scoping-study-on-the-valuation-of-risks-to-life-and-health-the-monetary-value-of-a-life-year-voly) has set the value of 1 QALY to be equal to $60,000. This is the number we will use for our calculations. + +```{r} +cost.per.qaly(base = base, + base.name = baseline, + int = int, + int.name = intervention, + QALY_value = 60000, # QALY value == £60,000 + int.label <- intervention) +``` + +# Incremental Cost-Effectiveness Ratio + +From [NICE guidelines](https://www.nice.org.uk/process/pmg6/chapter/assessing-cost-effectiveness) about the cost-effectiveness of health interventions (7.3), and works with a statistic called the Incremental Cost-Effectiveness Ratio (ICER). The formula for calculating the ICER can be found [here](https://en.wikipedia.org/wiki/Incremental_cost-effectiveness_ratio), but in short, it represents the average incremental cost associated with 1 additional unit of the measurement of effect. In our case, the unit of measurement of effect is a QALY. + +From the NICE guidelines, there is a general consensus that anything below £20,000 is cost effective, and anything above £30,000 is not. Between £20,000 and £30,000 is a grey area, and depends on the field and other factors to decide (i.e. if a rare or important condition is tackled by an intervention then the cost-effective threshold is higher). + +```{r} +ICER(base = base, + base.name = baseline, + int = int, + int.name = intervention, + QALY_value = 60000, + int.label = intervention) +``` + +# Total Cost of Intervention + +```{r} +intervention.cost(base = base, + base.name = baseline, + int = int, + int.name = intervention, + int.label = intervention) +``` + + From d826f031a3b49509851369ea071e7bdfb70cdd6e Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Wed, 28 Feb 2024 13:07:06 +0000 Subject: [PATCH 161/229] Changed arc4 all child uplifts target to default config --- scripts/Makefile.SCP | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/Makefile.SCP b/scripts/Makefile.SCP index 2d0b481d..c94248ac 100644 --- a/scripts/Makefile.SCP +++ b/scripts/Makefile.SCP @@ -180,8 +180,8 @@ arc4_all_scenarios: MODE=default_config arc4_all_scenarios: RUN_CONFIG=$(CONFIG)/default.yaml arc4_all_scenarios: arc4_baseline arc4_intervention_hhIncomeChildUplift arc4_intervention_PovertyLineChildUplift arc4_intervention_livingWage arc4_intervention_energyDownLift arc4_intervention_energyDownLiftNoSupport -arc4_all_child_uplifts: MODE=scaled_glasgow #MODE=default_config -arc4_all_child_uplifts: RUN_CONFIG=$(CONFIG)/glasgow_scaled.yaml#/default.yaml +arc4_all_child_uplifts: MODE=default_config #MODE=scaled_glasgow +arc4_all_child_uplifts: RUN_CONFIG=$(CONFIG)/default.yaml #/glasgow_scaled.yaml arc4_all_child_uplifts: arc4_baseline arc4_intervention_25All arc4_intervention_50All arc4_intervention_75All arc4_intervention_100All arc4_poverty_line_child_uplifts: MODE=default_config From 5762a05cc22ddea2d17bacae7a752fd8647f18fd Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Thu, 29 Feb 2024 10:30:12 +0000 Subject: [PATCH 162/229] Made a new combined target for running a selection of SCP scenarios for QALYs. Also improved the clean_logs target --- Makefile | 3 ++- scripts/Makefile.SCP | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a37a85b2..90019333 100644 --- a/Makefile +++ b/Makefile @@ -172,7 +172,8 @@ clean_out: clean_logs: ### Remove log files (including test.log, slurm, and arc logs) clean_logs: rm -rf test.log - rm -rf logs/* + rm -rf logs/log/* + rm -rf logs/errors/* clean_transitions: ### Remove model .rds files clean_transitions: diff --git a/scripts/Makefile.SCP b/scripts/Makefile.SCP index c94248ac..93b4c61c 100644 --- a/scripts/Makefile.SCP +++ b/scripts/Makefile.SCP @@ -207,3 +207,10 @@ arc4_priority_child_uplifts: arc4_scotland_baseline arc4_intervention_25Priority arc4_all_25_uplifts: MODE=default_config arc4_all_25_uplifts: RUN_CONFIG=$(CONFIG)/default.yaml arc4_all_25_uplifts: arc4_baseline arc4_intervention_25RelativePoverty arc4_intervention_25All arc4_intervention_25UniversalCredit + + + +arc4_qaly_SCPs: MODE=default_config +arc4_qaly_SCPs: RUN_CONFIG=$(CONFIG)/default.yaml +arc4_qaly_SCPs: arc4_baseline arc4_intervention_25All arc4_intervention_50All arc4_intervention_100All +arc4_qaly_SCPs: arc4_intervention_25UniversalCredit arc4_intervention_50UniversalCredit arc4_intervention_100UniversalCredit \ No newline at end of file From f3ce485452ff270ce23af37ede31b63711324765 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Thu, 29 Feb 2024 10:32:55 +0000 Subject: [PATCH 163/229] Slight tweak to the QALY calculation and tidied up the QALY outcomes makefile. Also improvements to the generic visualisation script --- minos/outcomes/Makefile.QALY | 80 ++++++++++++++++++++++++++--- minos/outcomes/QALY_calculation.py | 5 +- minos/outcomes/QALY_comparison2.Rmd | 22 ++++---- 3 files changed, 88 insertions(+), 19 deletions(-) diff --git a/minos/outcomes/Makefile.QALY b/minos/outcomes/Makefile.QALY index a643d369..ed9cea74 100644 --- a/minos/outcomes/Makefile.QALY +++ b/minos/outcomes/Makefile.QALY @@ -145,12 +145,26 @@ QALY_comparison_livingWage_uk: QALY_baseline_uk QALY_livwage_uk ############################################################################################ -# Scottish Child Payment Interventions +# Scottish Child Payment ############################################################################################ -QALY_SCP_25_UC: MODE=default_config -QALY_SCP_25_UC: INTERVENTION=25UniversalCredit -QALY_SCP_25_UC: +##################################### +# All Child +##################################### + +QALY_SCP_25_All: MODE=default_config +QALY_SCP_25_All: INTERVENTION=25All +QALY_SCP_25_All: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_SCP_50_All: MODE=default_config +QALY_SCP_50_All: INTERVENTION=50All +QALY_SCP_50_All: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_SCP_75_All: MODE=default_config +QALY_SCP_75_All: INTERVENTION=75All +QALY_SCP_75_All: python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) QALY_SCP_100_All: MODE=default_config @@ -158,6 +172,58 @@ QALY_SCP_100_All: INTERVENTION=100All QALY_SCP_100_All: python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) -QALY_vis_SCP_100_All: - $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', output_file='QALY_SCP_100_All.html', param=list(experiment='default_config/', base='baseline', intervention='100All'))" - firefox file://$(OUTCOMES)/QALY_SCP_100_All.html +##################################### +# Universal Credit +##################################### + +QALY_SCP_25_UC: MODE=default_config +QALY_SCP_25_UC: INTERVENTION=25UniversalCredit +QALY_SCP_25_UC: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_SCP_50_UC: MODE=default_config +QALY_SCP_50_UC: INTERVENTION=50UniversalCredit +QALY_SCP_50_UC: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_SCP_75_UC: MODE=default_config +QALY_SCP_75_UC: INTERVENTION=75UniversalCredit +QALY_SCP_75_UC: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_SCP_100_UC: MODE=default_config +QALY_SCP_100_UC: INTERVENTION=100UniversalCredit +QALY_SCP_100_UC: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +##################################### +# VISUALISATIONS +##################################### + +################## +# All Child +################## + +QALY_vis_SCP_25_All: QALY_baseline QALY_SCP_25_All + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(intervention='25All'), output_file = 'QALY_SCP_25_All.html')" + firefox file://$(OUTCOMES)/QALY_SCP_25_All.html + +QALY_vis_SCP_50_All: QALY_baseline QALY_SCP_50_All + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(intervention='50All'), output_file = 'QALY_SCP_50_All.html')" + firefox file://$(OUTCOMES)/QALY_SCP_50_All.html + +QALY_vis_SCP_100_All: QALY_baseline QALY_SCP_100_All + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(intervention='100All'), output_file = 'QALY_SCP_100_All.html')" + firefox file://$(OUTCOMES)/QALY_SCP_100_All.html + +################## +# Universal Credit +################## + +QALY_vis_SCP_25_UC: QALY_baseline QALY_SCP_25_UC + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(intervention='25UniversalCredit'), output_file = 'QALY_SCP_25_UC.html')" + firefox file://$(OUTCOMES)/QALY_SCP_25_UC.html + +QALY_vis_SCP_50_UC: QALY_baseline QALY_SCP_50_UC + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(intervention='50UniversalCredit'), output_file = 'QALY_SCP_50_UC.html')" + firefox file://$(OUTCOMES)/QALY_SCP_50_UC.html diff --git a/minos/outcomes/QALY_calculation.py b/minos/outcomes/QALY_calculation.py index 4c744edd..38670855 100644 --- a/minos/outcomes/QALY_calculation.py +++ b/minos/outcomes/QALY_calculation.py @@ -63,6 +63,9 @@ def aggregate_csv(filename, intervention): # drop zero weight samples df = df[df['weight'] > 0] + # drop dead people before calculating outcomes + df = df[df['alive'] != 'dead'] + # adjust SF_12 values by sampling weight df['SF_12_MCS'] = (df['SF_12_MCS'] * ((1 / df['weight']) / df['weight'].sum())) df['SF_12_PCS'] = (df['SF_12_PCS'] * ((1 / df['weight']) / df['weight'].sum())) @@ -73,7 +76,7 @@ def aggregate_csv(filename, intervention): total_boost = 0 else: # also adjust boost_amount - df['boost_amount'] = (df['boost_amount'] * ((1 / df['weight']) / df['weight'].sum())) + #df['boost_amount'] = (df['boost_amount'] * ((1 / df['weight']) / df['weight'].sum())) pop_boosted = df['income_boosted'].sum() total_boost = df['boost_amount'].sum() diff --git a/minos/outcomes/QALY_comparison2.Rmd b/minos/outcomes/QALY_comparison2.Rmd index b5a51a78..69a55f2e 100644 --- a/minos/outcomes/QALY_comparison2.Rmd +++ b/minos/outcomes/QALY_comparison2.Rmd @@ -1,9 +1,9 @@ --- params: - experiment: '' - base: '' - intervention: '' -title: "QALY Comparisons - r`experiment` - r`intervention` - r`baseline`" + experiment: 'default_config/' + base: 'baseline' + intervention: '50All' +title: "QALY Comparisons - `r params$experiment` - `r params$intervention` - `r params$baseline`" output: html_document: toc: true @@ -42,13 +42,13 @@ source(here::here('minos', 'utils_qaly.R')) ## Parameters ```{r} -# experiment <- params$experiment -# base <- params$base -# intervention <- params$intervention - -experiment <- 'default_config' -baseline <- 'baseline' -intervention <- '100All' +experiment <- params$experiment +baseline <- params$base +intervention <- params$intervention +# +# experiment <- 'default_config' +# baseline <- 'baseline' +# intervention <- '100All' ``` From 1431dd8f448767c7a256626b580d7f8757516af1 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Thu, 29 Feb 2024 11:13:20 +0000 Subject: [PATCH 164/229] Renamed the new Makefiles from Makefile. to .Makefile as they werent being correctly recognised on arc --- Makefile | 6 +++--- minos/outcomes/{Makefile.QALY => QALY.Makefile} | 0 minos/outcomes/{Makefile.maps => maps.Makefile} | 0 scripts/{Makefile.SCP => SCP.Makefile} | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename minos/outcomes/{Makefile.QALY => QALY.Makefile} (100%) rename minos/outcomes/{Makefile.maps => maps.Makefile} (100%) rename scripts/{Makefile.SCP => SCP.Makefile} (99%) diff --git a/Makefile b/Makefile index 90019333..2c86b590 100644 --- a/Makefile +++ b/Makefile @@ -143,9 +143,9 @@ include minos/transitions/Makefile # transitions Makefile include scripts/Makefile # running minos Makefile include minos/outcomes/Makefile # plotting makefile include minos/validation/Makefile # validation scripts -include minos/outcomes/Makefile.maps # mapping functions -include minos/outcomes/Makefile.QALY # QALY calculations and vis -include scripts/Makefile.SCP # SCP intervention scenarios +include minos/outcomes/maps.Makefile # mapping functions +include minos/outcomes/QALY.Makefile # QALY calculations and vis +include scripts/SCP.Makefile # SCP intervention scenarios #include docsrc/Makefile # sphinx makefile diff --git a/minos/outcomes/Makefile.QALY b/minos/outcomes/QALY.Makefile similarity index 100% rename from minos/outcomes/Makefile.QALY rename to minos/outcomes/QALY.Makefile diff --git a/minos/outcomes/Makefile.maps b/minos/outcomes/maps.Makefile similarity index 100% rename from minos/outcomes/Makefile.maps rename to minos/outcomes/maps.Makefile diff --git a/scripts/Makefile.SCP b/scripts/SCP.Makefile similarity index 99% rename from scripts/Makefile.SCP rename to scripts/SCP.Makefile index 93b4c61c..2659d63d 100644 --- a/scripts/Makefile.SCP +++ b/scripts/SCP.Makefile @@ -209,7 +209,7 @@ arc4_all_25_uplifts: RUN_CONFIG=$(CONFIG)/default.yaml arc4_all_25_uplifts: arc4_baseline arc4_intervention_25RelativePoverty arc4_intervention_25All arc4_intervention_25UniversalCredit - +arc4_qaly_SCPs: setup arc4_qaly_SCPs: MODE=default_config arc4_qaly_SCPs: RUN_CONFIG=$(CONFIG)/default.yaml arc4_qaly_SCPs: arc4_baseline arc4_intervention_25All arc4_intervention_50All arc4_intervention_100All From 3ae59b3c305fae97218d5de6eec2c2a883297292 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Thu, 29 Feb 2024 11:20:12 +0000 Subject: [PATCH 165/229] Stopped tracking a script that is only used for local testing --- scripts/local_batch.sh | 27 --------------------------- 1 file changed, 27 deletions(-) delete mode 100644 scripts/local_batch.sh diff --git a/scripts/local_batch.sh b/scripts/local_batch.sh deleted file mode 100644 index 6a8027aa..00000000 --- a/scripts/local_batch.sh +++ /dev/null @@ -1,27 +0,0 @@ - -NUMRUNS=10 -declare -a intlist=(energyDownlift energyDownliftNoSupport livingWageIntervention) - -TIME=$(date +%Y_%m_%d_%H_%M_%S) - -echo "Running $NUMRUNS runs for the Baseline, All Child Uplift, and Living Wage scenarios..." -echo "Starting with baseline..." - -## Doing the baseline runs -for i in $(seq 1 $NUMRUNS); -do - echo "Starting run #$i for the baseline scenario..." - python3 scripts/run.py -c config/default.yaml -o default_config -t "$TIME" -r "$i" -done - -## Now intervention runs -for scen in "${intlist[@]}"; -do - for i in $(seq 1 $NUMRUNS); - do - echo "Starting run #$i for the $scen scenario..." - python3 scripts/run.py -c config/default.yaml -o default_config -i "$scen" -t "$TIME" -r "$i" - done -done - -echo "COMPLETED A LOCAL BATCH RUN." From 6be9c322690770d72f0406e60c2098de21f9e49c Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Thu, 29 Feb 2024 15:48:22 +0000 Subject: [PATCH 166/229] Added some combined targets for calculating and visualising QALYs for SCP runs --- minos/outcomes/QALY.Makefile | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/minos/outcomes/QALY.Makefile b/minos/outcomes/QALY.Makefile index ed9cea74..b456a420 100644 --- a/minos/outcomes/QALY.Makefile +++ b/minos/outcomes/QALY.Makefile @@ -216,6 +216,8 @@ QALY_vis_SCP_100_All: QALY_baseline QALY_SCP_100_All $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(intervention='100All'), output_file = 'QALY_SCP_100_All.html')" firefox file://$(OUTCOMES)/QALY_SCP_100_All.html +QALYs_all_child: QALY_vis_SCP_25_All QALY_vis_SCP_50_All QALY_vis_SCP_100_All + ################## # Universal Credit ################## @@ -227,3 +229,9 @@ QALY_vis_SCP_25_UC: QALY_baseline QALY_SCP_25_UC QALY_vis_SCP_50_UC: QALY_baseline QALY_SCP_50_UC $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(intervention='50UniversalCredit'), output_file = 'QALY_SCP_50_UC.html')" firefox file://$(OUTCOMES)/QALY_SCP_50_UC.html + +QALY_vis_SCP_100_UC: QALY_baseline QALY_SCP_100_UC + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(intervention='100UniversalCredit'), output_file = 'QALY_SCP_100_UC.html')" + firefox file://$(OUTCOMES)/QALY_SCP_50_UC.html + +QALYs_UC: QALY_vis_SCP_25_UC QALY_vis_SCP_50_UC QALY_vis_SCP_100_UC From 6848591332faddfd9cc9721df0abe52fc443914a Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Thu, 29 Feb 2024 16:08:17 +0000 Subject: [PATCH 167/229] Remove calls to open the QALY notebooks in firefox as they caused failures on arc --- minos/outcomes/QALY.Makefile | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/minos/outcomes/QALY.Makefile b/minos/outcomes/QALY.Makefile index b456a420..5492e6b3 100644 --- a/minos/outcomes/QALY.Makefile +++ b/minos/outcomes/QALY.Makefile @@ -206,15 +206,15 @@ QALY_SCP_100_UC: QALY_vis_SCP_25_All: QALY_baseline QALY_SCP_25_All $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(intervention='25All'), output_file = 'QALY_SCP_25_All.html')" - firefox file://$(OUTCOMES)/QALY_SCP_25_All.html + #firefox file://$(OUTCOMES)/QALY_SCP_25_All.html QALY_vis_SCP_50_All: QALY_baseline QALY_SCP_50_All $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(intervention='50All'), output_file = 'QALY_SCP_50_All.html')" - firefox file://$(OUTCOMES)/QALY_SCP_50_All.html + #firefox file://$(OUTCOMES)/QALY_SCP_50_All.html QALY_vis_SCP_100_All: QALY_baseline QALY_SCP_100_All $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(intervention='100All'), output_file = 'QALY_SCP_100_All.html')" - firefox file://$(OUTCOMES)/QALY_SCP_100_All.html + #firefox file://$(OUTCOMES)/QALY_SCP_100_All.html QALYs_all_child: QALY_vis_SCP_25_All QALY_vis_SCP_50_All QALY_vis_SCP_100_All @@ -224,14 +224,14 @@ QALYs_all_child: QALY_vis_SCP_25_All QALY_vis_SCP_50_All QALY_vis_SCP_100_All QALY_vis_SCP_25_UC: QALY_baseline QALY_SCP_25_UC $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(intervention='25UniversalCredit'), output_file = 'QALY_SCP_25_UC.html')" - firefox file://$(OUTCOMES)/QALY_SCP_25_UC.html + #firefox file://$(OUTCOMES)/QALY_SCP_25_UC.html QALY_vis_SCP_50_UC: QALY_baseline QALY_SCP_50_UC $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(intervention='50UniversalCredit'), output_file = 'QALY_SCP_50_UC.html')" - firefox file://$(OUTCOMES)/QALY_SCP_50_UC.html + #firefox file://$(OUTCOMES)/QALY_SCP_50_UC.html QALY_vis_SCP_100_UC: QALY_baseline QALY_SCP_100_UC $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(intervention='100UniversalCredit'), output_file = 'QALY_SCP_100_UC.html')" - firefox file://$(OUTCOMES)/QALY_SCP_50_UC.html + #firefox file://$(OUTCOMES)/QALY_SCP_100_UC.html QALYs_UC: QALY_vis_SCP_25_UC QALY_vis_SCP_50_UC QALY_vis_SCP_100_UC From 15cf0d22b40218ca0773c045f866af111e1fc68a Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Fri, 1 Mar 2024 13:56:47 +0000 Subject: [PATCH 168/229] Changed fertility and tobacco module from a fixed common random number stream (i.e. fixed seeding) to variable --- minos/modules/add_new_birth_cohorts.py | 4 +--- minos/modules/hourly_wage.py | 1 + minos/modules/tobacco.py | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/minos/modules/add_new_birth_cohorts.py b/minos/modules/add_new_birth_cohorts.py index a7d220e5..f4ba4049 100755 --- a/minos/modules/add_new_birth_cohorts.py +++ b/minos/modules/add_new_birth_cohorts.py @@ -198,8 +198,6 @@ def __repr__(self): return "FertilityAgeSpecificRates()" - - class nkidsFertilityAgeSpecificRates(Base): """ A simulant-specific model for fertility and pregnancies. @@ -263,7 +261,7 @@ def setup(self, builder): requires_columns=['sex', 'ethnicity', 'nkids_ind']) # CRN stream for seeding births. - self.randomness = builder.randomness.get_stream('fertility') + self.randomness = builder.randomness.get_stream(self.generate_random_crn_key()) view_columns = ['sex', 'ethnicity', 'age', 'nkids', 'nkids_ind', 'hidp', 'pidp', "child_ages"] diff --git a/minos/modules/hourly_wage.py b/minos/modules/hourly_wage.py index d60eb627..8d07404f 100644 --- a/minos/modules/hourly_wage.py +++ b/minos/modules/hourly_wage.py @@ -10,6 +10,7 @@ import numpy as np import logging + class HourlyWage(Base): # Special methods used by vivarium. diff --git a/minos/modules/tobacco.py b/minos/modules/tobacco.py index d28b6048..e1542686 100755 --- a/minos/modules/tobacco.py +++ b/minos/modules/tobacco.py @@ -47,7 +47,7 @@ def setup(self, builder): #self.transition_coefficients = builder. # Assign randomness streams if necessary. - self.random = builder.randomness.get_stream("tobacco") + self.random = builder.randomness.get_stream(self.generate_random_crn_key()) # Determine which subset of the main population is used in this module. # columns_created is the columns created by this module. From 734f14f805b1ed15e44305ddb5f40f35bc3f2fbe Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Fri, 1 Mar 2024 14:11:14 +0000 Subject: [PATCH 169/229] Changed year range to include the jump off year --- minos/outcomes/QALY_calculation.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/minos/outcomes/QALY_calculation.py b/minos/outcomes/QALY_calculation.py index 38670855..9eeb93ff 100644 --- a/minos/outcomes/QALY_calculation.py +++ b/minos/outcomes/QALY_calculation.py @@ -14,7 +14,7 @@ from multiprocessing import Pool from itertools import repeat import glob as glob -from aggregate_subset_functions import dynamic_subset_function +#from aggregate_subset_functions import dynamic_subset_function import minos.utils as utils @@ -136,7 +136,7 @@ def main(mode, intervention): combined_output = pd.DataFrame() # use multiprocessing to read in files and aggregating - for year in years+1: + for year in years: files = glob.glob(os.path.join(batch_source, f"*{year}.csv")) # grab all files at source with suffix year.csv. # aggregate the files using multiprocessing From 6556e1460aa5d273e619c0f5330a6883b84d1f47 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Fri, 1 Mar 2024 14:48:24 +0000 Subject: [PATCH 170/229] Fixed QALY calculation for first year of sim (problem with no dead people causing error in a calculation) --- minos/outcomes/QALY_calculation.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/minos/outcomes/QALY_calculation.py b/minos/outcomes/QALY_calculation.py index 9eeb93ff..f549abc8 100644 --- a/minos/outcomes/QALY_calculation.py +++ b/minos/outcomes/QALY_calculation.py @@ -19,7 +19,7 @@ import minos.utils as utils -def aggregate_csv(filename, intervention): +def aggregate_csv(filename, intervention, year, start_year): """ Parameters @@ -56,7 +56,11 @@ def aggregate_csv(filename, intervention): # from both of these pieces of information we can calculate incidence of mortality total_pop_size = len(df) alive_pop = df['alive'].value_counts()['alive'] - dead_pop = df['alive'].value_counts()['dead'] + # no dead people in first wave + if year == start_year: + dead_pop = 0 + else: + dead_pop = df['alive'].value_counts()['dead'] # to investigate the mortality rate we can look at the ratio of dead to alive and compare across years alive_ratio = (alive_pop / total_pop_size) * 100 @@ -141,7 +145,7 @@ def main(mode, intervention): # aggregate the files using multiprocessing with Pool() as pool: - aggregated_means = pool.starmap(aggregate_csv, zip(files, repeat(intervention))) + aggregated_means = pool.starmap(aggregate_csv, zip(files, repeat(intervention), repeat(year), repeat(start_year))) new_df = pd.DataFrame(aggregated_means) new_df.columns = ['run_id', 'alive_pop', 'dead_pop', 'total_pop_size', 'pop_boosted', 'total_boost', 'alive_ratio', 'SF_12_MCS', 'SF_12_PCS'] From de2c3afdf691ae50ace8c05ede1ed5c2e7aa5085 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 4 Mar 2024 15:04:03 +0000 Subject: [PATCH 171/229] Updated the QALY calculation functions in R to move away from smoothed plots to calculated 95% confidence intervals --- minos/outcomes/QALY_comparison2.Rmd | 45 +++- minos/utils_qaly.R | 350 +++++++++++++++++++++++++++- 2 files changed, 379 insertions(+), 16 deletions(-) diff --git a/minos/outcomes/QALY_comparison2.Rmd b/minos/outcomes/QALY_comparison2.Rmd index 69a55f2e..502e6f39 100644 --- a/minos/outcomes/QALY_comparison2.Rmd +++ b/minos/outcomes/QALY_comparison2.Rmd @@ -25,8 +25,8 @@ require(here) require(MESS) require(scales) -#workingDir <- "/home/luke/Documents/WORK/MINOS/" -workingDir <- normalizePath('../') +workingDir <- "/home/luke/Documents/WORK/MINOS/PLOTS_FROM_ARC/QALY_SCP_28Feb/QALY_analysis_SCP_test/" +#workingDir <- normalizePath('../') knitr::opts_knit$set(root.dir = workingDir) knitr::opts_chunk$set( options(scipen = 999) @@ -35,8 +35,11 @@ rm(workingDir) ``` ```{r} -source(here::here('minos', 'utils_datain.R')) -source(here::here('minos', 'utils_qaly.R')) +source_dir <- '/home/luke/Documents/WORK/MINOS/Minos/minos' +#datain <- here::here(source_dir, 'utils_datain.R') + +source(here::here(source_dir, 'utils_datain.R')) +source(here::here(source_dir, 'utils_qaly.R')) ``` ## Parameters @@ -55,7 +58,7 @@ intervention <- params$intervention ## Data ```{r} -# # read in QALY files for baseline and interventions +# read in QALY files for baseline and interventions out.path <- here::here('output', experiment) base.path <- get_latest_runtime_subdirectory(here::here(out.path, baseline)) @@ -68,12 +71,32 @@ int <- read.csv(paste0(int.path, 'qalys.csv')) combined <- rbind(base, int) ``` + + +TESTING TEMPORARY TEMPORARY TEMPORARY TEMPORARY TEMPORARY TEMPORARY TEMPORARY +```{r} +# base <- read.csv('qalys_baseline.csv') +# int <- read.csv('qalys_25All.csv') +# +# combined <- rbind(base, int) +# +# baseline <- 'baseline' +# intervention <- '25All' +``` +TESTING TEMPORARY TEMPORARY TEMPORARY TEMPORARY TEMPORARY TEMPORARY TEMPORARY + + + # Intervention Information Just going to plot some things here like how many people affected by an intervention and what the mean / total boost amounts look like. ```{r} -ggplot(combined, aes(x = year, y = pop_boosted, group = intervention, colour = intervention)) + +combined.pop_boosted <- combined %>% + group_by(year, run_id, intervention) %>% + summarise(pop_boosted = sum(pop_boosted)) + +ggplot(combined.pop_boosted, aes(x = year, y = pop_boosted, group = intervention, colour = intervention)) + geom_smooth() + labs(title = 'Number Individuals Receiving Intervention') @@ -115,6 +138,7 @@ QALY_comparison(base = base, int.name = intervention) ``` + ## SF12 Change ```{r} @@ -124,6 +148,7 @@ sf12.plots(base = base, int.name = intervention) ``` + ## Area Under the Curve ```{r} @@ -133,6 +158,7 @@ auc.plots(base = base, int.name = intervention) ``` + # Cost per QALY The [UK Government](https://www.gov.uk/government/publications/valuation-of-risks-to-life-and-health-monetary-value-of-a-life-year-voly/a-scoping-study-on-the-valuation-of-risks-to-life-and-health-the-monetary-value-of-a-life-year-voly) has set the value of 1 QALY to be equal to $60,000. This is the number we will use for our calculations. @@ -142,10 +168,11 @@ cost.per.qaly(base = base, base.name = baseline, int = int, int.name = intervention, - QALY_value = 60000, # QALY value == £60,000 + QALY_value = 70000, # QALY value == £60,000 int.label <- intervention) ``` + # Incremental Cost-Effectiveness Ratio From [NICE guidelines](https://www.nice.org.uk/process/pmg6/chapter/assessing-cost-effectiveness) about the cost-effectiveness of health interventions (7.3), and works with a statistic called the Incremental Cost-Effectiveness Ratio (ICER). The formula for calculating the ICER can be found [here](https://en.wikipedia.org/wiki/Incremental_cost-effectiveness_ratio), but in short, it represents the average incremental cost associated with 1 additional unit of the measurement of effect. In our case, the unit of measurement of effect is a QALY. @@ -157,10 +184,11 @@ ICER(base = base, base.name = baseline, int = int, int.name = intervention, - QALY_value = 60000, + QALY_value = 70000, int.label = intervention) ``` + # Total Cost of Intervention ```{r} @@ -172,3 +200,4 @@ intervention.cost(base = base, ``` + diff --git a/minos/utils_qaly.R b/minos/utils_qaly.R index b9d2b3c4..d9506dbf 100644 --- a/minos/utils_qaly.R +++ b/minos/utils_qaly.R @@ -1,6 +1,10 @@ ### QALY calculation plots and utility functions +############################################################################## +# QALY Comparisons +############################################################################## + ## QALY comparisons QALY_comparison <- function(base, base.name, int, int.name) { @@ -24,12 +28,132 @@ QALY_comparison <- function(base, base.name, int, int.name) { geom_smooth() + labs(title = 'QALY change', subtitle = paste0(int.name, ' vs ', base.name)) print(p2) + + combined.QALY.change.confint <- combined.QALY.change %>% + group_by(year) %>% + summarise(n = n(), + mean_QALY_change = mean(QALYs), + margin = qt(0.975, df = n - 1) * (sd(QALYs) / sqrt(n)), + lower = mean_QALY_change - margin, + upper = mean_QALY_change + margin) + + p3 <- ggplot(combined.QALY.change.confint, aes(x = year, y = mean_QALY_change)) + + geom_ribbon(aes(ymin = lower, ymax = upper), fill = 'grey70') + + geom_line(color = "blue") + + geom_hline(yintercept = 0, linetype = 'dashed') + + labs(title = 'QALY Change', subtitle = paste0(int.name, ' vs ', base.name)) + print(p3) } -## SF12 Comparison Plots +## QALY comparisons +QALY_comparison.smooth <- function(base, base.name, int, int.name) { + + combined <- rbind(base, int) + + p1 <- ggplot(data = combined, aes(x = year, y = QALYs, group = intervention, colour = intervention)) + + geom_smooth() + + labs(title = 'QALYs per year') + print(p1) + + # Now change from baseline + combined.QALY.change <- combined %>% + select(run_id, year, intervention, QALYs) %>% + pivot_wider(names_from = 'intervention', + values_from = 'QALYs') %>% + mutate(QALYs = .data[[int.name]] - .data[[base.name]]) %>% + select(run_id, year, QALYs) + + p2 <- ggplot(data = combined.QALY.change, aes(x = year, y = QALYs)) + + geom_hline(yintercept = 0, linetype = 'dashed') + + geom_smooth() + + labs(title = 'QALY change', subtitle = paste0(int.name, ' vs ', base.name)) + print(p2) +} + +############################################################################## +# SF12 Comparison Plots +############################################################################## + # Plot mean comparison and change from baseline for both MCS and PCS. +# This version plots lineplots with 95% confidence intervals instead of +# smoothed below sf12.plots <- function(base, base.name, int, int.name) { + ## SF12 Comparison Plots + combined <- rbind(base, int) + + ## now SF12 plots for comparison + p1 <- ggplot(combined, aes(x = year, y = SF_12_MCS, group = intervention, colour = intervention, fill = intervention)) + + geom_smooth() + p2 <- ggplot(combined, aes(x = year, y = SF_12_PCS, group = intervention, colour = intervention, fill = intervention)) + + geom_smooth() + + print(p1) + print(p2) + + combined.SF12 <- combined %>% + select(run_id, year, intervention, SF_12_MCS, SF_12_PCS) %>% + pivot_wider(names_from = 'intervention', + values_from = c('SF_12_MCS', 'SF_12_PCS')) %>% + mutate(MCS_diff = .data[[paste0('SF_12_MCS_', int.name)]] - .data[[paste0('SF_12_MCS_', base.name)]], + PCS_diff = .data[[paste0('SF_12_PCS_', int.name)]] - .data[[paste0('SF_12_PCS_', base.name)]]) %>% + select(run_id, year, MCS_diff, PCS_diff) + + + p3 <- ggplot(combined.SF12, aes(x = year, y = MCS_diff)) + + geom_point() + + geom_smooth() + + geom_hline(yintercept = 0, linetype = 'dashed') + + labs(title = 'Change in SF_12_MCS', subtitle = paste0(int.name, ' vs ', base.name)) + + xlab('Year') + + ylab('Change in MCS') + + p4 <- ggplot(combined.SF12, aes(x = year, y = PCS_diff)) + + geom_point() + + geom_smooth() + + geom_hline(yintercept = 0, linetype = 'dashed') + + labs(title = 'Change in SF_12_PCS', subtitle = paste0(int.name, ' vs ', base.name)) + + xlab('Year') + + ylab('Change in PCS') + + print(p3) + print(p4) + + + combined.small <- combined.SF12 %>% + group_by(year) %>% + summarise(n = n(), + mean_MCS_diff = mean(MCS_diff), + mean_PCS_diff = mean(PCS_diff), + MCS_margin = qt(0.975, df = n - 1) * (sd(MCS_diff) / sqrt(n)), # 95% confidence intervals + PCS_margin = qt(0.975, df = n - 1) * (sd(PCS_diff) / sqrt(n))) + + p5 <- ggplot(combined.small, aes(x = year, y = mean_MCS_diff)) + + geom_ribbon(aes(ymin = mean_MCS_diff - MCS_margin, ymax = mean_MCS_diff + MCS_margin), fill = 'grey70') + + geom_line() + + geom_hline(yintercept = 0, linetype = 'dashed') + + labs(title = 'Change in SF_12_MCS', subtitle = paste0(int.name, ' vs ', base.name)) + + xlab('Year') + + ylab('Change in MCS') + + p6 <- ggplot(combined.small, aes(x = year, y = mean_PCS_diff)) + + geom_ribbon(aes(ymin = mean_PCS_diff - PCS_margin, ymax = mean_PCS_diff + PCS_margin), fill = 'grey70') + + geom_line() + + geom_hline(yintercept = 0, linetype = 'dashed') + + labs(title = 'Change in SF_12_PCS', subtitle = paste0(int.name, ' vs ', base.name)) + + xlab('Year') + + ylab('Change in PCS') + + print(p5) + print(p6) +} + + +## SF12 Comparison Plots +# Plot mean comparison and change from baseline for both MCS and PCS. +# This version plots geom_smooth instead of lineplots with confidence +# intervals as above +sf12.plots.smooth <- function(base, base.name, int, int.name) { combined <- rbind(base, int) @@ -69,8 +193,10 @@ sf12.plots <- function(base, base.name, int, int.name) { } +############################################################################## +# AUC Plots +############################################################################## -# AUC Plots: # This function will plot the total AUC (which equals the total QALYs in the # system) for 2 interventions, and the change in AUC between the 2. auc.plots <- function(base, base.name, intervention, int.name) { @@ -86,8 +212,6 @@ auc.plots <- function(base, base.name, intervention, int.name) { combined.auc <- rbind(base.auc, int.auc) - print(combined.auc) - combined.auc.plot <- combined.auc %>% group_by(intervention) %>% summarise(sd = sd(AUC), @@ -97,10 +221,19 @@ auc.plots <- function(base, base.name, intervention, int.name) { p1 <- ggplot(data = combined.auc.plot, aes(x = intervention, y = AUC, group = intervention, fill = intervention)) + geom_col() + geom_errorbar(aes(ymin = AUC - margin, ymax = AUC + margin, width = 0.4, group = intervention)) + + coord_cartesian(ylim = c(max(combined.auc.plot$AUC) - (0.001 * max(combined.auc.plot$AUC)), max(combined.auc.plot$AUC) + (0.0005 * max(combined.auc.plot$AUC)))) + labs(title = 'Total AUC comparison', subtitle = paste0(int.name, ' vs ', base.name)) + xlab('Intervention') print(p1) + auc.base <- combined.auc %>% filter(intervention == 'baseline') + auc.int <- combined.auc %>% filter(intervention == int.name) + + tt <- t.test(x = auc.base$AUC, + y = auc.int$AUC) + + print(tt) + # Another version showing difference from baseline for easier comparison combined.auc2 <- combined.auc %>% pivot_wider(names_from = 'intervention', @@ -113,8 +246,6 @@ auc.plots <- function(base, base.name, intervention, int.name) { values_to = 'AUC_difference') %>% filter(intervention != 'baseline') - print(combined.auc2) - combined.auc2.plot <- combined.auc2 %>% group_by(intervention) %>% summarise(margin = (qt(0.975, df = n() - 1) * sd(AUC_difference)) / sqrt(n()), @@ -131,11 +262,13 @@ auc.plots <- function(base, base.name, intervention, int.name) { print(paste0('Change in AUC == ', combined.auc2.plot$AUC_difference, ' +- ', combined.auc2.plot$margin)) } +############################################################################## +# COST PER QALY +############################################################################## - -## COST PER QALY # This function will calculate and plot the cost per QALY over time, as well # as the total cost change over the length of the simulation. + cost.per.qaly <- function(base, base.name, int, int.name, QALY_value, int.label) { combined <- rbind(base, int) @@ -150,6 +283,66 @@ cost.per.qaly <- function(base, base.name, int, int.name, QALY_value, int.label) labs(title = 'QALY value comparison', subtitle = paste0(int.name, ' vs ', base.name)) print(p1) + combined.cost.change <- combined.cost %>% + select(run_id, year, intervention, QALY_value) %>% + pivot_wider(names_from = 'intervention', + values_from = 'QALY_value') %>% + mutate(change_baseline = .data[[base.name]] - .data[[base.name]], + change_intervention = .data[[int.name]] - .data[[base.name]]) %>% + select(run_id, year, change_baseline, change_intervention) %>% + pivot_longer(cols = change_baseline:change_intervention, + names_to = 'intervention', + names_prefix = 'change_', + values_to = 'QALY_value_change') %>% + filter(intervention != 'baseline') %>% + mutate(intervention = int.label) + + combined.cost.change.plot <- combined.cost.change %>% + group_by(year, intervention) %>% + summarise(n = n(), + mean_QALY_value_change = mean(QALY_value_change), + margin = qt(0.975, df = n - 1) * (sd(QALY_value_change) / sqrt(n))) + + p2 <- ggplot(combined.cost.change.plot, aes(x = year, y = mean_QALY_value_change, group = intervention, colour = intervention, fill = intervention)) + + geom_ribbon(aes(ymin = mean_QALY_value_change - margin, ymax = mean_QALY_value_change + margin)) + + geom_line(color = 'black') + + geom_hline(yintercept = 0, linetype = 'dashed') + + scale_y_continuous(label = comma) + + labs(title = 'QALY value change', subtitle = paste0(int.name, ' vs ', base.name)) + + xlab('Year') + + ylab('QALY Value Difference') + print(p2) + + total.combined.cost <- combined.cost.change %>% + group_by(run_id, intervention) %>% + summarise(TotalChange = sum(QALY_value_change)) %>% + group_by(intervention) %>% + summarise(margin = (qt(0.975, df = n() - 1) * sd(TotalChange)) / sqrt(n()), + TotalChange = mean(TotalChange)) + + p3 <- ggplot(total.combined.cost, aes(x = intervention, y = TotalChange, group = intervention, fill = intervention)) + + geom_col() + + geom_errorbar(aes(ymin = TotalChange - margin, ymax = TotalChange + margin, width = 0.4, group = intervention)) + + scale_y_continuous(label = comma) + + labs(title = 'Total Change in QALY Value', subtitle = paste0(int.name, ' vs ', base.name)) + + xlab('Intervention') + print(p3) +} + +cost.per.qaly.smooth <- function(base, base.name, int, int.name, QALY_value, int.label) { + combined <- rbind(base, int) + + combined.cost <- combined %>% + #select(-alive_pop, -SF_12_MCS, -SF_12_PCS, -utility) %>% + select(run_id, year, alive_pop, total_boost, intervention, QALYs) %>% + mutate(QALY_value = QALYs * QALY_value) + + p1 <-ggplot(combined.cost, aes(x = year, y = QALY_value, group = intervention, colour = intervention)) + + geom_smooth() + + scale_y_continuous(label = comma) + + labs(title = 'QALY value comparison', subtitle = paste0(int.name, ' vs ', base.name)) + print(p1) + combined.cost.change <- combined.cost %>% select(run_id, year, intervention, QALY_value) %>% pivot_wider(names_from = 'intervention', @@ -187,6 +380,9 @@ cost.per.qaly <- function(base, base.name, int, int.name, QALY_value, int.label) print(p3) } +############################################################################## +# Incremental Cost Effective Ratio (ICER) +############################################################################## ICER <- function(base, base.name, int, int.name, QALY_value, int.label) { combined <- rbind(base, int) @@ -211,6 +407,93 @@ ICER <- function(base, base.name, int, int.name, QALY_value, int.label) { e1 <- paste('QALYs', int.name, sep='_') e0 <- paste('QALYs', base.name, sep='_') + ICER <- combined.cost.mean %>% + pivot_wider(names_from = 'intervention', + values_from = c('total_boost', 'QALYs', 'QALY_value')) %>% + mutate(ICER = (.data[[c1]] - .data[[c0]]) / (.data[[e1]] - .data[[e0]])) %>% + select(run_id, year, ICER) %>% + mutate(intervention = int.label) + + ICER.plot <- ICER %>% + group_by(year, intervention) %>% + summarise(n = n(), + mean_ICER = mean(ICER), + margin = qt(0.975, df = n - 1) * (sd(ICER) / sqrt(n))) + + p1 <- ggplot(ICER.plot, aes(x = year, y = mean_ICER, group = intervention, fill = intervention, colour = intervention)) + + geom_ribbon(aes(ymin = mean_ICER - margin, ymax = mean_ICER + margin)) + + geom_line(color = 'black') + + geom_hline(yintercept = 0, linetype = 'dashed') + + scale_y_continuous(label = comma) + + labs(title = 'ICER - Full', subtitle = paste0(int.name, ' vs ', base.name)) + print(p1) + + pc1 <- quantile(ICER$ICER, .01) + # print(pc1) + # print(min(ICER$ICER)) + pc99 <- quantile(ICER$ICER, .99) + # print(pc99) + # print(max(ICER$ICER)) + + ICER <- filter(ICER, ICER < pc99, ICER > pc1) + # print(min(ICER$ICER)) + # print(max(ICER$ICER)) + + ICER.plot2 <- ICER %>% + group_by(year, intervention) %>% + summarise(n = n(), + mean_ICER = mean(ICER), + margin = qt(0.975, df = n - 1) * (sd(ICER) / sqrt(n))) + + p2 <- ggplot(ICER.plot2, aes(x = year, y = mean_ICER, group = intervention, fill = intervention, colour = intervention)) + + geom_ribbon(aes(ymin = mean_ICER - margin, ymax = mean_ICER + margin)) + + geom_line(color = 'black') + + geom_hline(yintercept = 0, linetype = 'dashed') + + scale_y_continuous(label = comma) + + labs(title = 'ICER - Outliers Removed', subtitle = paste0(int.name, ' vs ', base.name)) + print(p2) + + ICER.final <- ICER %>% + group_by(run_id, intervention) %>% + summarise(ICER = mean(ICER)) %>% + group_by(intervention) %>% + summarise(margin = (qt(0.975, df=n() - 1) * sd(ICER)) / sqrt(n()), + ICER = mean(ICER)) + + p3 <- ggplot(ICER.final, aes(x = intervention, y = ICER, group = intervention, fill = intervention)) + + geom_col() + + geom_errorbar(aes(ymin = ICER - margin, ymax = ICER + margin, width = 0.4, group = intervention)) + + scale_y_continuous(label = comma) + + labs(title = 'ICER', subtitle = paste0(int.name, ' vs ', base.name)) + + xlab('Intervention') + print(p3) + + print(paste0('ICER == ', ICER.final$ICER, ' +- ', ICER.final$margin)) +} + +ICER.smooth <- function(base, base.name, int, int.name, QALY_value, int.label) { + combined <- rbind(base, int) + + combined.cost.mean <- combined %>% + #select(-alive_pop, -SF_12_MCS, -SF_12_PCS, -utility) %>% + select(run_id, year, total_boost, intervention, QALYs) %>% + mutate(QALY_value = QALYs * QALY_value) %>% + group_by(run_id, year, intervention) %>% + summarise(across(everything(), mean)) + + # ICER = (C1 - C0) / (E1 - E0) + # where + # C1 = cost of intervention + # E1 = effect of intervention + # C0 = cost of control group + # E0 = effect of control group + + # prepare var names + c1 <- paste('total_boost', int.name, sep='_') + c0 <- paste('total_boost', base.name, sep='_') + e1 <- paste('QALYs', int.name, sep='_') + e0 <- paste('QALYs', base.name, sep='_') + ICER <- combined.cost.mean %>% pivot_wider(names_from = 'intervention', values_from = c('total_boost', 'QALYs', 'QALY_value')) %>% @@ -253,9 +536,60 @@ ICER <- function(base, base.name, int, int.name, QALY_value, int.label) { print(paste0('ICER == ', ICER.final$ICER, ' +- ', ICER.final$margin)) } +############################################################################## +# Intervention Cost +############################################################################## + intervention.cost <- function(base, base.name, int, int.name, int.label) { combined <- rbind(base, int) + total.cost.byYear <- combined %>% + select(run_id, year, intervention, total_boost) %>% + group_by(run_id, year, intervention) %>% + summarise(total_yearly_cost = sum(total_boost)) %>% + pivot_wider(names_from = 'intervention', + values_from = 'total_yearly_cost') %>% + mutate(cost_difference = .data[[int.name]] - .data[[base.name]]) %>% + select(run_id, year, cost_difference) %>% + mutate(intervention = int.name) + + total.cost.byYear.plot <- total.cost.byYear %>% + group_by(year, intervention) %>% + summarise(n = n(), + mean_cost_difference = mean(cost_difference), + margin = qt(0.975, df = n - 1) * (sd(cost_difference) / sqrt(n))) + + p2 <- ggplot(total.cost.byYear.plot, aes(x = year, y = mean_cost_difference, group = intervention, fill = intervention, colour = intervention)) + + geom_ribbon(aes(ymin = mean_cost_difference - margin, ymax = mean_cost_difference + margin)) + + geom_line() + + labs(title = 'Intervention Cost by Year', subtitle = paste0(int.name, ' vs ', base.name)) + print(p2) + + total.cost.2021 <- total.cost.byYear %>% + filter(year == 2021) + + print(paste("Cost of intervention in the first year (2021) ==", total.cost.2021$cost_difference, sep = ' ')) + + total.cost <- total.cost.byYear %>% + group_by(intervention, run_id) %>% + summarise(cost_difference = sum(cost_difference)) %>% + group_by(intervention) %>% + summarise(margin = (qt(0.975, df=n() - 1) * sd(cost_difference)) / sqrt(n()), + cost_difference = mean(cost_difference)) + + p1 <- ggplot(total.cost, aes(x = intervention, y = cost_difference, group = intervention, fill = intervention, colour = intervention)) + + geom_col() + + geom_errorbar(aes(ymin = cost_difference - margin, ymax = cost_difference + margin, width = 0.4, group = intervention)) + + labs(title = 'Total cost of intervention', subtitle = paste0(int.name, ' vs ', base.name)) + + scale_y_continuous(label = comma) + print(p1) + + print(paste("Total cost of the intervention over the full simulation (2020-2035) ==", total.cost$cost_difference, sep = ' ')) +} + +intervention.cost.smooth <- function(base, base.name, int, int.name, int.label) { + combined <- rbind(base, int) + total.cost.byYear <- combined %>% select(run_id, year, intervention, total_boost) %>% group_by(run_id, year, intervention) %>% From 1ad38bdcdc17d8c630391a62b4aae3fe35803c05 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 4 Mar 2024 15:32:03 +0000 Subject: [PATCH 172/229] Removed start year from ICER calculation as cant calculate ICER with no intervention --- minos/outcomes/QALY_comparison2.Rmd | 52 ++++++++++++++++------------- minos/utils_qaly.R | 7 +++- 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/minos/outcomes/QALY_comparison2.Rmd b/minos/outcomes/QALY_comparison2.Rmd index 502e6f39..302ec9a3 100644 --- a/minos/outcomes/QALY_comparison2.Rmd +++ b/minos/outcomes/QALY_comparison2.Rmd @@ -25,7 +25,8 @@ require(here) require(MESS) require(scales) -workingDir <- "/home/luke/Documents/WORK/MINOS/PLOTS_FROM_ARC/QALY_SCP_28Feb/QALY_analysis_SCP_test/" +#workingDir <- "/home/luke/Documents/WORK/MINOS/PLOTS_FROM_ARC/QALY_SCP_28Feb/QALY_analysis_SCP_test/" +workingDir <- "/home/luke/Documents/WORK/MINOS/PLOTS_FROM_ARC/tmp/" #workingDir <- normalizePath('../') knitr::opts_knit$set(root.dir = workingDir) knitr::opts_chunk$set( @@ -45,43 +46,45 @@ source(here::here(source_dir, 'utils_qaly.R')) ## Parameters ```{r} -experiment <- params$experiment -baseline <- params$base -intervention <- params$intervention +# experiment <- params$experiment +# baseline <- params$base +# intervention <- params$intervention # # experiment <- 'default_config' # baseline <- 'baseline' # intervention <- '100All' + +list.files() ``` ## Data ```{r} -# read in QALY files for baseline and interventions -out.path <- here::here('output', experiment) - -base.path <- get_latest_runtime_subdirectory(here::here(out.path, baseline)) -int.path <- get_latest_runtime_subdirectory(here::here(out.path, intervention)) - -base <- read.csv(paste0(base.path, 'qalys.csv')) -int <- read.csv(paste0(int.path, 'qalys.csv')) - -# combine the dataframes -combined <- rbind(base, int) +# # read in QALY files for baseline and interventions +# out.path <- here::here('output', experiment) +# +# base.path <- get_latest_runtime_subdirectory(here::here(out.path, baseline)) +# int.path <- get_latest_runtime_subdirectory(here::here(out.path, intervention)) +# +# base <- read.csv(paste0(base.path, 'qalys.csv')) +# int <- read.csv(paste0(int.path, 'qalys.csv')) +# +# # combine the dataframes +# combined <- rbind(base, int) ``` TESTING TEMPORARY TEMPORARY TEMPORARY TEMPORARY TEMPORARY TEMPORARY TEMPORARY ```{r} -# base <- read.csv('qalys_baseline.csv') -# int <- read.csv('qalys_25All.csv') -# -# combined <- rbind(base, int) -# -# baseline <- 'baseline' -# intervention <- '25All' +base <- read.csv('qalys_base.csv') +int <- read.csv('qalys_25All.csv') + +combined <- rbind(base, int) + +baseline <- 'baseline' +intervention <- '25All' ``` TESTING TEMPORARY TEMPORARY TEMPORARY TEMPORARY TEMPORARY TEMPORARY TEMPORARY @@ -188,7 +191,6 @@ ICER(base = base, int.label = intervention) ``` - # Total Cost of Intervention ```{r} @@ -201,3 +203,7 @@ intervention.cost(base = base, +```{r} + +``` + diff --git a/minos/utils_qaly.R b/minos/utils_qaly.R index d9506dbf..245defd1 100644 --- a/minos/utils_qaly.R +++ b/minos/utils_qaly.R @@ -384,7 +384,7 @@ cost.per.qaly.smooth <- function(base, base.name, int, int.name, QALY_value, int # Incremental Cost Effective Ratio (ICER) ############################################################################## -ICER <- function(base, base.name, int, int.name, QALY_value, int.label) { +ICER <- function(base, base.name, int, int.name, QALY_value, int.label, start.year = 2021) { combined <- rbind(base, int) combined.cost.mean <- combined %>% @@ -414,6 +414,11 @@ ICER <- function(base, base.name, int, int.name, QALY_value, int.label) { select(run_id, year, ICER) %>% mutate(intervention = int.label) + # No intervention in first year so can't calculate ICER + #TODO: Find start year automatically + ICER <- ICER %>% + filter(year != start.year) + ICER.plot <- ICER %>% group_by(year, intervention) %>% summarise(n = n(), From d7a559c17acc963b27a5dcd44c232fa8122b7694 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 4 Mar 2024 15:37:17 +0000 Subject: [PATCH 173/229] Third time lucky. Switched some paths and constants back from testing to normal operation --- minos/outcomes/QALY_comparison2.Rmd | 54 ++++++++++++++--------------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/minos/outcomes/QALY_comparison2.Rmd b/minos/outcomes/QALY_comparison2.Rmd index 302ec9a3..94c4649f 100644 --- a/minos/outcomes/QALY_comparison2.Rmd +++ b/minos/outcomes/QALY_comparison2.Rmd @@ -26,8 +26,8 @@ require(MESS) require(scales) #workingDir <- "/home/luke/Documents/WORK/MINOS/PLOTS_FROM_ARC/QALY_SCP_28Feb/QALY_analysis_SCP_test/" -workingDir <- "/home/luke/Documents/WORK/MINOS/PLOTS_FROM_ARC/tmp/" -#workingDir <- normalizePath('../') +#workingDir <- "/home/luke/Documents/WORK/MINOS/PLOTS_FROM_ARC/tmp/" +workingDir <- normalizePath('../') knitr::opts_knit$set(root.dir = workingDir) knitr::opts_chunk$set( options(scipen = 999) @@ -36,55 +36,53 @@ rm(workingDir) ``` ```{r} -source_dir <- '/home/luke/Documents/WORK/MINOS/Minos/minos' +#source_dir <- '/home/luke/Documents/WORK/MINOS/Minos/minos' #datain <- here::here(source_dir, 'utils_datain.R') -source(here::here(source_dir, 'utils_datain.R')) -source(here::here(source_dir, 'utils_qaly.R')) +source(here::here('minos', 'utils_datain.R')) +source(here::here('minos', 'utils_qaly.R')) ``` ## Parameters ```{r} -# experiment <- params$experiment -# baseline <- params$base -# intervention <- params$intervention +experiment <- params$experiment +baseline <- params$base +intervention <- params$intervention # # experiment <- 'default_config' # baseline <- 'baseline' # intervention <- '100All' - -list.files() ``` ## Data ```{r} -# # read in QALY files for baseline and interventions -# out.path <- here::here('output', experiment) -# -# base.path <- get_latest_runtime_subdirectory(here::here(out.path, baseline)) -# int.path <- get_latest_runtime_subdirectory(here::here(out.path, intervention)) -# -# base <- read.csv(paste0(base.path, 'qalys.csv')) -# int <- read.csv(paste0(int.path, 'qalys.csv')) -# -# # combine the dataframes -# combined <- rbind(base, int) +# read in QALY files for baseline and interventions +out.path <- here::here('output', experiment) + +base.path <- get_latest_runtime_subdirectory(here::here(out.path, baseline)) +int.path <- get_latest_runtime_subdirectory(here::here(out.path, intervention)) + +base <- read.csv(paste0(base.path, 'qalys.csv')) +int <- read.csv(paste0(int.path, 'qalys.csv')) + +# combine the dataframes +combined <- rbind(base, int) ``` TESTING TEMPORARY TEMPORARY TEMPORARY TEMPORARY TEMPORARY TEMPORARY TEMPORARY ```{r} -base <- read.csv('qalys_base.csv') -int <- read.csv('qalys_25All.csv') - -combined <- rbind(base, int) - -baseline <- 'baseline' -intervention <- '25All' +# base <- read.csv('qalys_base.csv') +# int <- read.csv('qalys_25All.csv') +# +# combined <- rbind(base, int) +# +# baseline <- 'baseline' +# intervention <- '25All' ``` TESTING TEMPORARY TEMPORARY TEMPORARY TEMPORARY TEMPORARY TEMPORARY TEMPORARY From 6eaa9b87a77bcd4bbfc8381bf924300047db2b8d Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 5 Mar 2024 10:53:19 +0000 Subject: [PATCH 174/229] Small tweaks and better reporting of large values (formatted with commas) --- minos/outcomes/QALY_comparison2.Rmd | 14 ++++++----- minos/utils_qaly.R | 36 +++++++++++++++++++++-------- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/minos/outcomes/QALY_comparison2.Rmd b/minos/outcomes/QALY_comparison2.Rmd index 94c4649f..be479009 100644 --- a/minos/outcomes/QALY_comparison2.Rmd +++ b/minos/outcomes/QALY_comparison2.Rmd @@ -46,13 +46,13 @@ source(here::here('minos', 'utils_qaly.R')) ## Parameters ```{r} -experiment <- params$experiment -baseline <- params$base -intervention <- params$intervention +# experiment <- params$experiment +# baseline <- params$base +# intervention <- params$intervention # -# experiment <- 'default_config' -# baseline <- 'baseline' -# intervention <- '100All' +experiment <- 'default_config' +baseline <- 'baseline' +intervention <- '50All' ``` @@ -97,6 +97,8 @@ combined.pop_boosted <- combined %>% group_by(year, run_id, intervention) %>% summarise(pop_boosted = sum(pop_boosted)) +print(paste("There are", sum(is.na(combined.pop.boosted$pop_boosted)), "NA pop_boosted values in combined.pop.boosted.", sep = ' ')) + ggplot(combined.pop_boosted, aes(x = year, y = pop_boosted, group = intervention, colour = intervention)) + geom_smooth() + labs(title = 'Number Individuals Receiving Intervention') diff --git a/minos/utils_qaly.R b/minos/utils_qaly.R index 245defd1..e6164975 100644 --- a/minos/utils_qaly.R +++ b/minos/utils_qaly.R @@ -327,6 +327,8 @@ cost.per.qaly <- function(base, base.name, int, int.name, QALY_value, int.label) labs(title = 'Total Change in QALY Value', subtitle = paste0(int.name, ' vs ', base.name)) + xlab('Intervention') print(p3) + + print(paste("QALY value change across full simulation: ", format(total.combined.cost$TotalChange[1], big.mark = ',', trim = TRUE), sep = ' ')) } cost.per.qaly.smooth <- function(base, base.name, int, int.name, QALY_value, int.label) { @@ -378,6 +380,8 @@ cost.per.qaly.smooth <- function(base, base.name, int, int.name, QALY_value, int labs(title = 'Total Change in QALY Value', subtitle = paste0(int.name, ' vs ', base.name)) + xlab('Intervention') print(p3) + + print(paste("QALY value change across full simulation: ", format(total.combined.cost$TotalChange[1], big.mark = ',', trim = TRUE), sep = ' ')) } ############################################################################## @@ -473,7 +477,7 @@ ICER <- function(base, base.name, int, int.name, QALY_value, int.label, start.ye xlab('Intervention') print(p3) - print(paste0('ICER == ', ICER.final$ICER, ' +- ', ICER.final$margin)) + print(paste0('ICER == ', format(ICER.final$ICER, big.mark = ',', trim = TRUE), ' +- ', format(ICER.final$margin, big.mark = ',', trim = TRUE))) } ICER.smooth <- function(base, base.name, int, int.name, QALY_value, int.label) { @@ -538,7 +542,7 @@ ICER.smooth <- function(base, base.name, int, int.name, QALY_value, int.label) { xlab('Intervention') print(p2) - print(paste0('ICER == ', ICER.final$ICER, ' +- ', ICER.final$margin)) + print(paste0('ICER == ', format(ICER.final$ICER, big.mark = ',', trim = TRUE), ' +- ', format(ICER.final$margin, big.mark = ',', trim = TRUE))) } ############################################################################## @@ -570,10 +574,13 @@ intervention.cost <- function(base, base.name, int, int.name, int.label) { labs(title = 'Intervention Cost by Year', subtitle = paste0(int.name, ' vs ', base.name)) print(p2) - total.cost.2021 <- total.cost.byYear %>% - filter(year == 2021) + total.cost.firstYr <- total.cost.byYear %>% + filter(year == 2022) - print(paste("Cost of intervention in the first year (2021) ==", total.cost.2021$cost_difference, sep = ' ')) + print(paste("Cost of intervention in the first year (2022) ==", format(total.cost.firstYr$cost_difference[1], + big.mark = ',', + trim = TRUE), + sep = ' ')) total.cost <- total.cost.byYear %>% group_by(intervention, run_id) %>% @@ -589,7 +596,10 @@ intervention.cost <- function(base, base.name, int, int.name, int.label) { scale_y_continuous(label = comma) print(p1) - print(paste("Total cost of the intervention over the full simulation (2020-2035) ==", total.cost$cost_difference, sep = ' ')) + print(paste("Total cost of the intervention over the full simulation (2020-2035) ==", format(total.cost$cost_difference, + big.mark = ',', + trim = TRUE), + sep = ' ')) } intervention.cost.smooth <- function(base, base.name, int, int.name, int.label) { @@ -610,10 +620,13 @@ intervention.cost.smooth <- function(base, base.name, int, int.name, int.label) labs(title = 'Intervention Cost by Year', subtitle = paste0(int.name, ' vs ', base.name)) print(p2) - total.cost.2021 <- total.cost.byYear %>% - filter(year == 2021) + total.cost.firstYr <- total.cost.byYear %>% + filter(year == 2022) - print(paste("Cost of intervention in the first year (2021) ==", total.cost.2021$cost_difference, sep = ' ')) + print(paste("Cost of intervention in the first year (2022) ==", format(total.cost.firstYr$cost_difference[1], + big.mark = ',', + trim = TRUE), + sep = ' ')) total.cost <- total.cost.byYear %>% group_by(intervention, run_id) %>% @@ -629,5 +642,8 @@ intervention.cost.smooth <- function(base, base.name, int, int.name, int.label) scale_y_continuous(label = comma) print(p1) - print(paste("Total cost of the intervention over the full simulation (2020-2035) ==", total.cost$cost_difference, sep = ' ')) + print(paste("Total cost of the intervention over the full simulation (2020-2035) ==", format(total.cost$cost_difference[1], + big.mark = ',', + trim = TRUE), + sep = ' ')) } From ed00cbd44af7d439d4dd70bca533a84c76768e4f Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 5 Mar 2024 11:00:20 +0000 Subject: [PATCH 175/229] Typo in a new print statement --- minos/outcomes/QALY_comparison2.Rmd | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/minos/outcomes/QALY_comparison2.Rmd b/minos/outcomes/QALY_comparison2.Rmd index be479009..5f58eb45 100644 --- a/minos/outcomes/QALY_comparison2.Rmd +++ b/minos/outcomes/QALY_comparison2.Rmd @@ -46,13 +46,13 @@ source(here::here('minos', 'utils_qaly.R')) ## Parameters ```{r} -# experiment <- params$experiment -# baseline <- params$base -# intervention <- params$intervention +experiment <- params$experiment +baseline <- params$base +intervention <- params$intervention # -experiment <- 'default_config' -baseline <- 'baseline' -intervention <- '50All' +# experiment <- 'default_config' +# baseline <- 'baseline' +# intervention <- '50All' ``` @@ -97,7 +97,7 @@ combined.pop_boosted <- combined %>% group_by(year, run_id, intervention) %>% summarise(pop_boosted = sum(pop_boosted)) -print(paste("There are", sum(is.na(combined.pop.boosted$pop_boosted)), "NA pop_boosted values in combined.pop.boosted.", sep = ' ')) +print(paste("There are", sum(is.na(combined.pop_boosted$pop_boosted)), "NA pop_boosted values in combined.pop.boosted.", sep = ' ')) ggplot(combined.pop_boosted, aes(x = year, y = pop_boosted, group = intervention, colour = intervention)) + geom_smooth() + From 0c16f0be2bbefb1f9c86a6769fbafc41945ce176 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 5 Mar 2024 11:27:48 +0000 Subject: [PATCH 176/229] Removed all secondary modules, doing a test run with just core modules --- config/SF12_components.txt | 5 ----- config/SF12_components_full.txt | 21 +++++++++++++++++++ minos/minosPipeline/RunPipeline.py | 4 ++-- .../transitions/model_definitions_default.txt | 18 +++++----------- .../model_definitions_default_FULL_OLD.txt | 20 ++++++++++++++++++ minos/utils_qaly.R | 2 +- 6 files changed, 49 insertions(+), 21 deletions(-) create mode 100644 config/SF12_components_full.txt create mode 100644 minos/transitions/model_definitions_default_FULL_OLD.txt diff --git a/config/SF12_components.txt b/config/SF12_components.txt index 651bd3a9..44e3f203 100644 --- a/config/SF12_components.txt +++ b/config/SF12_components.txt @@ -5,14 +5,9 @@ Tobacco() Alcohol() PhysicalActivity() Neighbourhood() -Heating() Housing() -HousingTenure() lmmYJNutrition() -FinancialSituation() S7Labour() -HourlyWage() -JobSec() lmmYJIncome() Education() Ageing() diff --git a/config/SF12_components_full.txt b/config/SF12_components_full.txt new file mode 100644 index 00000000..651bd3a9 --- /dev/null +++ b/config/SF12_components_full.txt @@ -0,0 +1,21 @@ +lmmYJMWB() +lmmYJPCS() +Loneliness() +Tobacco() +Alcohol() +PhysicalActivity() +Neighbourhood() +Heating() +Housing() +HousingTenure() +lmmYJNutrition() +FinancialSituation() +S7Labour() +HourlyWage() +JobSec() +lmmYJIncome() +Education() +Ageing() +Mortality() +nkidsFertilityAgeSpecificRates() +Replenishment() \ No newline at end of file diff --git a/minos/minosPipeline/RunPipeline.py b/minos/minosPipeline/RunPipeline.py index e6611600..352eb968 100755 --- a/minos/minosPipeline/RunPipeline.py +++ b/minos/minosPipeline/RunPipeline.py @@ -159,8 +159,8 @@ intervention_kwargs_dict = { "25All": {"uplift_amount": 25, "uplift_condition": "who_kids"}, "50All": {"uplift_amount": 50, "uplift_condition": "who_kids"}, - "75All": {"uplift_amount": 50, "uplift_condition": "who_kids"}, - "100All": {"uplift_amount": 50, "uplift_condition": "who_kids"}, + "75All": {"uplift_amount": 75, "uplift_condition": "who_kids"}, + "100All": {"uplift_amount": 100, "uplift_condition": "who_kids"}, "25RelativePoverty": {"uplift_amount": 25, "uplift_condition": "who_below_poverty_line_and_kids"}, "50RelativePoverty": {"uplift_amount": 50, "uplift_condition": "who_below_poverty_line_and_kids"}, diff --git a/minos/transitions/model_definitions_default.txt b/minos/transitions/model_definitions_default.txt index 8f11a473..22b39186 100644 --- a/minos/transitions/model_definitions_default.txt +++ b/minos/transitions/model_definitions_default.txt @@ -1,20 +1,12 @@ -RF : hourly_wage ~ time + sex + age + I(age**2) + ethnicity + region + education_state + urban + job_sec -CLM : chron_disease ~ factor(chron_disease) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(marital_status) + scale(ncigs) -CLM : matdep ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(marital_status) + hhsize + factor(housing_tenure) + factor(urban) + factor(financial_situation) LOGIT : active ~ scale(age) + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + scale(hh_income) + relevel(factor(ethnicity), ref = 'WBI') -NNET : auditc ~ scale(age) + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + scale(hh_income) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + factor(financial_situation) -CLM : housing_quality ~ scale(age) + I(scale(age)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) + I(scale(hh_income)**2) + scale(hh_income_diff) + factor(housing_tenure) -CLM : loneliness ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + relevel(factor(job_sec), ref = '3') + scale(hh_income) + relevel(factor(marital_status), ref = 'Partnered') +NNET : auditc ~ scale(age) + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + scale(hh_income) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') +CLM : housing_quality ~ scale(age) + I(scale(age)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) + I(scale(hh_income)**2) + scale(hh_income_diff) +CLM : loneliness ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) + relevel(factor(marital_status), ref = 'Partnered') CLM : neighbourhood_safety ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) NNET : education_state ~ factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') -CLM : financial_situation ~ factor(financial_situation) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + relevel(factor(job_sec), ref = '3') + scale(hh_income)+ I(scale(hh_income)**2) + factor(marital_status) + factor(housing_tenure) -LOGIT : heating ~ factor(heating) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) + factor(urban) + factor(housing_tenure) + factor(financial_situation) -LOGIT : urban ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + scale(hh_income) NNET : S7_labour_state ~ factor(S7_labour_state) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + factor(region) + relevel(factor(education_state), ref = "1") -CLM : job_sec ~ factor(job_sec) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = '3') -NNET : housing_tenure ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_tenure) + factor(urban) + factor(financial_situation) + scale(hh_income) -GLMM : hh_income ~ scale(hh_income) + scale(hh_income_diff) + scale(age) + I(scale(age)**2) + I(scale(age)**3) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + relevel(factor(job_sec), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + (1|pidp) +GLMM : hh_income ~ scale(hh_income) + scale(hh_income_diff) + scale(age) + I(scale(age)**2) + I(scale(age)**3) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + scale(SF_12_MCS) + scale(SF_12_PCS) + (1|pidp) LMM : nutrition_quality ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + scale(hh_income) + (1|pidp) + (1|hidp) + time LMM : SF_12_PCS ~ scale(SF_12_PCS_last) + I(scale(SF_12_PCS_last)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + scale(hh_income) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(active) + factor(auditc) + (1|pidp) + time LMM : SF_12_MCS ~ time + scale(SF_12_MCS_last) + I(scale(SF_12_MCS_last)**2) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + scale(hh_income) + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + I(factor(ncigs>0)) + (1|pidp) -ZIP : ncigs ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) | I(factor(ncigs>0)) + relevel(factor(ethnicity), ref = 'WBI') + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + relevel(factor(job_sec), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) +ZIP : ncigs ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) | I(factor(ncigs>0)) + relevel(factor(ethnicity), ref = 'WBI') + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) diff --git a/minos/transitions/model_definitions_default_FULL_OLD.txt b/minos/transitions/model_definitions_default_FULL_OLD.txt new file mode 100644 index 00000000..8f11a473 --- /dev/null +++ b/minos/transitions/model_definitions_default_FULL_OLD.txt @@ -0,0 +1,20 @@ +RF : hourly_wage ~ time + sex + age + I(age**2) + ethnicity + region + education_state + urban + job_sec +CLM : chron_disease ~ factor(chron_disease) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(marital_status) + scale(ncigs) +CLM : matdep ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) + factor(marital_status) + hhsize + factor(housing_tenure) + factor(urban) + factor(financial_situation) +LOGIT : active ~ scale(age) + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + scale(hh_income) + relevel(factor(ethnicity), ref = 'WBI') +NNET : auditc ~ scale(age) + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + scale(hh_income) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + factor(financial_situation) +CLM : housing_quality ~ scale(age) + I(scale(age)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) + I(scale(hh_income)**2) + scale(hh_income_diff) + factor(housing_tenure) +CLM : loneliness ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + relevel(factor(job_sec), ref = '3') + scale(hh_income) + relevel(factor(marital_status), ref = 'Partnered') +CLM : neighbourhood_safety ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) +NNET : education_state ~ factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') +CLM : financial_situation ~ factor(financial_situation) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + relevel(factor(job_sec), ref = '3') + scale(hh_income)+ I(scale(hh_income)**2) + factor(marital_status) + factor(housing_tenure) +LOGIT : heating ~ factor(heating) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) + factor(urban) + factor(housing_tenure) + factor(financial_situation) +LOGIT : urban ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + scale(hh_income) +NNET : S7_labour_state ~ factor(S7_labour_state) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + factor(region) + relevel(factor(education_state), ref = "1") +CLM : job_sec ~ factor(job_sec) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'South East') + relevel(factor(education_state), ref = '3') +NNET : housing_tenure ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_tenure) + factor(urban) + factor(financial_situation) + scale(hh_income) +GLMM : hh_income ~ scale(hh_income) + scale(hh_income_diff) + scale(age) + I(scale(age)**2) + I(scale(age)**3) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + relevel(factor(job_sec), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + (1|pidp) +LMM : nutrition_quality ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + scale(hh_income) + (1|pidp) + (1|hidp) + time +LMM : SF_12_PCS ~ scale(SF_12_PCS_last) + I(scale(SF_12_PCS_last)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)**2) + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + scale(hh_income) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(active) + factor(auditc) + (1|pidp) + time +LMM : SF_12_MCS ~ time + scale(SF_12_MCS_last) + I(scale(SF_12_MCS_last)**2) + scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + scale(hh_income) + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + I(factor(ncigs>0)) + (1|pidp) +ZIP : ncigs ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) | I(factor(ncigs>0)) + relevel(factor(ethnicity), ref = 'WBI') + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + relevel(factor(job_sec), ref = '3') + scale(hh_income) + scale(SF_12_MCS) + scale(SF_12_PCS) diff --git a/minos/utils_qaly.R b/minos/utils_qaly.R index e6164975..4477eb1a 100644 --- a/minos/utils_qaly.R +++ b/minos/utils_qaly.R @@ -221,7 +221,7 @@ auc.plots <- function(base, base.name, intervention, int.name) { p1 <- ggplot(data = combined.auc.plot, aes(x = intervention, y = AUC, group = intervention, fill = intervention)) + geom_col() + geom_errorbar(aes(ymin = AUC - margin, ymax = AUC + margin, width = 0.4, group = intervention)) + - coord_cartesian(ylim = c(max(combined.auc.plot$AUC) - (0.001 * max(combined.auc.plot$AUC)), max(combined.auc.plot$AUC) + (0.0005 * max(combined.auc.plot$AUC)))) + + coord_cartesian(ylim = c(min(combined.auc.plot$AUC) - max(combined.auc.plot$margin), max(combined.auc.plot$AUC) + max(combined.auc.plot$margin))) + labs(title = 'Total AUC comparison', subtitle = paste0(int.name, ' vs ', base.name)) + xlab('Intervention') print(p1) From 5a2abef7a1cb0fa4eeadc38417c11c6aec6889fd Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Wed, 6 Mar 2024 13:33:17 +0000 Subject: [PATCH 177/229] Working on the SF12 models, tweaking noise and such. Both models improved, PCS moreso. Still further improvement required --- minos/modules/mental_wellbeing.py | 2 +- minos/modules/physical_wellbeing.py | 19 ++++++++++--------- minos/modules/r_utils.py | 2 +- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/minos/modules/mental_wellbeing.py b/minos/modules/mental_wellbeing.py index 3904e1a3..9453a512 100755 --- a/minos/modules/mental_wellbeing.py +++ b/minos/modules/mental_wellbeing.py @@ -560,7 +560,7 @@ def calculate_mwb(self, pop): pop, dependent='SF_12_MCS', log_transform=True, - noise_std=0.025) # + noise_std=3) # return nextWaveMWB diff --git a/minos/modules/physical_wellbeing.py b/minos/modules/physical_wellbeing.py index 797bdc19..dc7a33d4 100644 --- a/minos/modules/physical_wellbeing.py +++ b/minos/modules/physical_wellbeing.py @@ -135,7 +135,10 @@ def plot(self, pop, config): class lmmYJPCS(Base): - """Mental Well-Being Module""" + """ + Physical Well-Being Module + """ + # Special methods used by vivarium. @property def name(self): @@ -192,8 +195,6 @@ def setup(self, builder): 'financial_situation', 'active', 'auditc', - 'chron_disease', - 'matdep', 'region', 'education_state'] @@ -244,14 +245,14 @@ def on_time_step(self, event): newWavePWB.index = pop.index #newWavePWB["SF_12_PCS"] -= 1 - ### This chunk is to increase variance + # ### This chunk is to increase variance sf12_mean = np.mean(newWavePWB["SF_12_PCS"]) - std_ratio = (10.6/np.std(newWavePWB["SF_12_PCS"])) + std_ratio = (10/np.std(newWavePWB["SF_12_PCS"])) newWavePWB["SF_12_PCS"] *= std_ratio newWavePWB["SF_12_PCS"] -= ((std_ratio-1)*sf12_mean) - newWavePWB["SF_12_PCS"] -= 1.5 - newWavePWB["SF_12_PCS"] += (49.3 - np.mean(newWavePWB["SF_12_PCS"])) - #newWavePWB["SF_12_PCS"] = np.clip(newWavePWB["SF_12_PCS"], 0, 100) # keep within [0, 100] bounds of SF12. + newWavePWB["SF_12_PCS"] += 1 + # #newWavePWB["SF_12_PCS"] += (49.3 - np.mean(newWavePWB["SF_12_PCS"])) + # #newWavePWB["SF_12_PCS"] = np.clip(newWavePWB["SF_12_PCS"], 0, 100) # keep within [0, 100] bounds of SF12. # Clip to minimum and maximum values seen in current wave #newWavePWB["SF_12_PCS"] = np.clip(newWavePWB["SF_12_PCS"], min_PCS, max_PCS) @@ -290,7 +291,7 @@ def calculate_pwb(self, pop): pop, dependent='SF_12_PCS', log_transform=True, - noise_std=0.025) # + noise_std=2) # # nextWavePWB = r_utils.predict_next_timestep_yj_gamma_glmm(self.gee_transition_model, # self.rpy2_modules, diff --git a/minos/modules/r_utils.py b/minos/modules/r_utils.py index bf9f3c6b..154fbbb2 100755 --- a/minos/modules/r_utils.py +++ b/minos/modules/r_utils.py @@ -365,7 +365,7 @@ def predict_next_timestep_yj_gaussian_lmm(model, rpy2_modules, current, dependen # need to add tiny value to the 0 MCS values as this causes problems in log transform if dependent == "SF_12_MCS": - current.loc[current[dependent] == 0.0, dependent] = current.loc[current[dependent] == 0.0, dependent] + 0.01 + current.loc[current[dependent] <= 0.0, dependent] = 0.01 # Convert from pandas to R using package converter From cc4de88a5c8f58ce77d92553f6015d9f4cbe06df Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Wed, 6 Mar 2024 17:48:44 +0000 Subject: [PATCH 178/229] Tweaks to data in and qaly visualisation functions. Added a script for checking pathway response to intervention --- .../testing/intervention_pathway_testing.Rmd | 192 ++++++++++++++++++ minos/utils_datain.R | 20 +- minos/utils_qaly.R | 4 +- minos/validation/handovers.Rmd | 14 +- 4 files changed, 219 insertions(+), 11 deletions(-) create mode 100644 minos/testing/intervention_pathway_testing.Rmd diff --git a/minos/testing/intervention_pathway_testing.Rmd b/minos/testing/intervention_pathway_testing.Rmd new file mode 100644 index 00000000..e31d9771 --- /dev/null +++ b/minos/testing/intervention_pathway_testing.Rmd @@ -0,0 +1,192 @@ +--- +title: "Testing Interventions - QALY Focus" +output: html_notebook +--- + +# SETUP + +```{r "setup", include=FALSE} + +require(tidyverse) +require(ggplot2) +require(knitr) +require(here) + +#workingDir <- "/home/luke/Documents/WORK/MINOS/" +workingDir <- normalizePath('../') +knitr::opts_knit$set(root.dir = workingDir) +knitr::opts_chunk$set( + warning = FALSE, + message = FALSE +) +rm(workingDir) +``` + + +```{r} +source(here::here('minos', 'utils_datain.R')) +``` + + + +```{r} +output.dir <- here::here('output', 'default_config/') + +var.list <- c('hh_income', 'age', 'sex', 'ethnicity', 'region', 'education_state', 'nkids', 'hhsize', + 'housing_quality', 'neighbourhood_safety', 'loneliness', 'ncigs', 'nutrition_quality', + 'active', 'auditc', + 'S7_labour_state', + 'SF_12_MCS', 'SF_12_PCS') + +base <- read_batch_out_all_years(out.path = output.dir, + scenario = 'baseline', + var.list = var.list) +all50 <- read_batch_out_all_years(out.path = output.dir, + scenario = '50All', + var.list = var.list) +UC50 <- read_batch_out_all_years(out.path = output.dir, + scenario = '50UniversalCredit', + var.list = var.list) + +base$scen <- 'baseline' +all50$scen <- 'all50' +UC50$scen <- 'UC50' + +data <- rbind(base, all50, UC50) + +``` + +# Functions + +## Ordinal Vars + +```{r} +pathway_plots_ordinal <- function(data, var, levels = c(1,2,3)) { + df <- data %>% + group_by(time, scen, .data[[var]]) %>% + summarise(n = n()) %>% + group_by(time, scen) %>% + mutate(prop = (n / sum(n)) * 100) + + for (level in levels) { + p <- ggplot(dplyr::filter(df, .data[[var]] == level), aes(x = time, y = prop, group = scen, fill = scen, color = scen)) + + geom_line() + + labs(title = var, subtitle = level) + print(p) + } +} + +pathway_plots_continuous <- function(data, var) { + df <- data %>% + group_by(time, scen, .data[[var]]) %>% + summarise(mean_var = mean(.data[[var]])) + + p1 <- ggplot(data, aes(x = time, y = .data[[var]], group = scen, fill = scen, color = scen)) + + geom_smooth() + + labs(title = var, subtitle = 'Loess Smoothed') + + p2 <- ggplot(df, aes(x = time, y = mean_var, group = scen, fill = scen, color = scen)) + + geom_smooth() + + labs(title = var, subtitle = 'Mean') + + print(p1) + print(p2) +} +``` + + +# INCOME CHANGE DUE TO INTERVENTION + +```{r} +ggplot(data, aes(x = time, y = hh_income, group = scen, fill = scen, color = scen)) + + geom_smooth() + + labs(title = 'Household Income Comparison') + + xlab('Year') + + ylab('Mean Household Income') +``` + + +# PATHWAYS CHANGE DUE TO INTERVENTION + +## Housing Quality + +```{r} +pathway_plots_ordinal(data, var = 'housing_quality', levels = c('Low', 'Medium', 'High')) +``` + +## Neighbourhood Safety + +```{r} +pathway_plots_ordinal(data, var = 'neighbourhood_safety') +``` + + + + +## Loneliness + +```{r} +pathway_plots_ordinal(data, var = 'loneliness') +``` + +## Tobacco + +```{r} +pathway_plots_continuous(data, var = 'ncigs') +``` + +## Tobacco + +```{r} +pathway_plots_continuous(data, var = 'nutrition_quality') +``` + +## Active + +```{r} +pathway_plots_ordinal(data, var = 'active', levels = c(0,1)) +``` + + +## AUDITC + +```{r} +pathway_plots_ordinal(data, var = 'auditc', levels = c('Non-drinker', 'Low Risk', 'Increased Risk', 'High Risk')) +``` + +# OUTCOMES + +## MCS + +```{r} +pathway_plots_continuous(data, var = 'SF_12_MCS') +``` + +## PCS + +```{r} +pathway_plots_continuous(data, var = 'SF_12_PCS') +``` + + + + + + + + + + + + +```{r} + +``` + + + + + + + + diff --git a/minos/utils_datain.R b/minos/utils_datain.R index 4f5628e4..34f74555 100644 --- a/minos/utils_datain.R +++ b/minos/utils_datain.R @@ -113,13 +113,13 @@ get_latest_runtime_subdirectory <- function(path) { # scenario - string scenario name of which output files to read # year - single year of batch output to aggregate # var.list - list of variables to keep in the returned dataframe -read_agg_subset_batch_out <- function(out.path, scenario, year, var.list) { +read_batch_out_1year <- function(out.path, scenario, year, var.list) { scen.path <- paste0(out.path, scenario) scen.path <- get_latest_runtime_subdirectory(scen.path) # Create file strings using year from args target.pattern <- paste0('[0-9]*_run_id_', year, '.csv') - filepath.list <- list.files(path = scenario_out_path, + filepath.list <- list.files(path = scen.path, pattern = target.pattern, full.names = TRUE) @@ -146,6 +146,22 @@ read_agg_subset_batch_out <- function(out.path, scenario, year, var.list) { return(final) } +read_batch_out_all_years <- function(out.path, scenario, start.year=2021, end.year=2036, var.list, verbose=FALSE) { + print(paste0("Starting aggregation of output files for ", scenario, '...')) + var.list <- c('pidp', 'hidp', 'time', 'weight', var.list, 'alive') + large.df = data.frame() + for (i in start.year:end.year) { + if (verbose) { print(paste0("Aggregating files for year ", i)) } + new.df <- read_batch_out_1year(out.path, scenario, year=i, var.list) + new.df <- new.df %>% + filter(alive != 'dead') %>% + select(-alive) + large.df <- rbind(large.df, new.df) + } + print("All output files successfully aggregated.") + return(large.df) +} + ################ NOTE ################ diff --git a/minos/utils_qaly.R b/minos/utils_qaly.R index 4477eb1a..3ad80069 100644 --- a/minos/utils_qaly.R +++ b/minos/utils_qaly.R @@ -615,7 +615,7 @@ intervention.cost.smooth <- function(base, base.name, int, int.name, int.label) select(run_id, year, cost_difference) %>% mutate(intervention = int.name) - p2 <- ggplot(total.cost.byYear, aes(x = year, y = cost_difference, group = intervention, fill = intervention, colour = intervention)) + + p2 <- ggplot(total.cost.byYear, aes(x = year, y = cost_difference)) + geom_smooth() + labs(title = 'Intervention Cost by Year', subtitle = paste0(int.name, ' vs ', base.name)) print(p2) @@ -637,7 +637,7 @@ intervention.cost.smooth <- function(base, base.name, int, int.name, int.label) p1 <- ggplot(total.cost, aes(x = intervention, y = cost_difference, group = intervention, fill = intervention, colour = intervention)) + geom_col() + - geom_errorbar(aes(ymin = cost_difference - margin, ymax = cost_difference + margin, width = 0.4, group = intervention)) + + geom_errorbar(aes(ymin = cost_difference - margin, ymax = cost_difference + margin, width = 0.4)) + labs(title = 'Total cost of intervention', subtitle = paste0(int.name, ' vs ', base.name)) + scale_y_continuous(label = comma) print(p1) diff --git a/minos/validation/handovers.Rmd b/minos/validation/handovers.Rmd index 4d619efa..9413adad 100644 --- a/minos/validation/handovers.Rmd +++ b/minos/validation/handovers.Rmd @@ -125,13 +125,13 @@ base.sf12.m <- base.dat %>% sf12.m.spag <- rbind(raw.sf12.m, base.sf12.m) -# sf12.m.spag.sample <- sf12.m.spag[which(sf12.m.spag$pidp %in% pidp_sample), ] -# spaghetti_plot(sf12.m.spag.sample, 'SF_12_MCS', -# save = shall.we.save, -# save.path = save.path) -# spaghetti_highlight_max_plot(sf12.m.spag.sample, 'SF_12_MCS', -# save = shall.we.save, -# save.path = save.path) +#sf12.m.spag.sample <- sf12.m.spag[which(sf12.m.spag$pidp %in% pidp_sample), ] +spaghetti_plot(sf12.m.spag, 'SF_12_MCS', + save = shall.we.save, + save.path = save.path) +spaghetti_highlight_max_plot(sf12.m.spag, 'SF_12_MCS', + save = shall.we.save, + save.path = save.path) density_ridges(sf12.m.spag, "SF_12_MCS", save = shall.we.save, From 80384dbcae8c9f5c044623232068c71f78875da8 Mon Sep 17 00:00:00 2001 From: ld-archer Date: Thu, 7 Mar 2024 13:08:15 +0000 Subject: [PATCH 179/229] Added combined target for running SCP interventions using Glasgow scaled population --- scripts/SCP.Makefile | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/scripts/SCP.Makefile b/scripts/SCP.Makefile index 2659d63d..72ee5ebc 100644 --- a/scripts/SCP.Makefile +++ b/scripts/SCP.Makefile @@ -208,9 +208,18 @@ arc4_all_25_uplifts: MODE=default_config arc4_all_25_uplifts: RUN_CONFIG=$(CONFIG)/default.yaml arc4_all_25_uplifts: arc4_baseline arc4_intervention_25RelativePoverty arc4_intervention_25All arc4_intervention_25UniversalCredit +##################################### +## Arc4 QALY Combined Targets +##################################### arc4_qaly_SCPs: setup arc4_qaly_SCPs: MODE=default_config arc4_qaly_SCPs: RUN_CONFIG=$(CONFIG)/default.yaml arc4_qaly_SCPs: arc4_baseline arc4_intervention_25All arc4_intervention_50All arc4_intervention_100All -arc4_qaly_SCPs: arc4_intervention_25UniversalCredit arc4_intervention_50UniversalCredit arc4_intervention_100UniversalCredit \ No newline at end of file +arc4_qaly_SCPs: arc4_intervention_25UniversalCredit arc4_intervention_50UniversalCredit arc4_intervention_100UniversalCredit + +arc4_qaly_SCPs_Glasgow: setup_glasgow_scaled +arc4_qaly_SCPs_Glasgow: MODE=glasgow_scaled +arc4_qaly_SCPs_Glasgow: RUN_CONFIG=$(CONFIG)/glasgow_scaled.yaml +arc4_qaly_SCPs_Glasgow: arc4_baseline arc4_intervention_25All arc4_intervention_50All arc4_intervention_100All +arc4_qaly_SCPs_Glasgow: arc4_intervention_25UniversalCredit arc4_intervention_50UniversalCredit arc4_intervention_100UniversalCredit From 82b9f867156e083aa061e0e25d4260acf3e6ee16 Mon Sep 17 00:00:00 2001 From: RobertClay Date: Thu, 7 Mar 2024 14:52:47 +0000 Subject: [PATCH 180/229] adding in 6 further legacy benefits variables for scp --- minos/data_generation/US_format_raw.py | 17 ++++++++-- minos/data_generation/US_missing_LOCF.py | 4 ++- minos/data_generation/US_utils.py | 8 ++--- .../generate_composite_vars.py | 31 +++++++++++++++++++ 4 files changed, 52 insertions(+), 8 deletions(-) diff --git a/minos/data_generation/US_format_raw.py b/minos/data_generation/US_format_raw.py index e73c33ec..6408f43b 100755 --- a/minos/data_generation/US_format_raw.py +++ b/minos/data_generation/US_format_raw.py @@ -308,12 +308,18 @@ def format_ukhls_columns(year): 'urban_dv': 'urban', # urban or rural household. # There are dozens of benefits variables in US this seems like # the simplest and most complete for our purposes. - 'benbase4': 'universal_credit', - # receives core benefits (I.E. universal credit/means tested benefits). + # including the seven needed for scp for now. + 'benbase4': 'universal_credit', # universal credit + "benbase3" : 'child_tax_credit', # child tax credit. note this variable is fed forwards. + #"othben5" : "working_tax_credit", # working tax credit. moved further down as name changes. + "benbase2" : "job_seekers_allowance", # job seeker's allowance + "benpen4" : "pension_credit", # pension credit + "benbase1" : "income_support", # income support + "bendis2" : "employment_and_support_allowance", # esa. } # Some variables change names halfway through UKHLS. - # Assign different keys to variable names depending on year. + # Assign different key"s to variable names depending on year. # clinical depression changes in wave 10. if year < 2017: @@ -321,6 +327,11 @@ def format_ukhls_columns(year): else: attribute_dict["hcondcode38"] = "depression" + if year < 2014: + attribute_dict["bentax1"] = "working_tax_credit" + else: + attribute_dict["othben5"] = "working_tax_credit" + # All attributes have a wave dependent suffix apart from identifiersb (pidp, hidp etc.). # Adjust attribute_columns as necessary. # E.g age -> a_age, age -> b_age ... for waves of ukhls. diff --git a/minos/data_generation/US_missing_LOCF.py b/minos/data_generation/US_missing_LOCF.py index 07055838..771bd52c 100755 --- a/minos/data_generation/US_missing_LOCF.py +++ b/minos/data_generation/US_missing_LOCF.py @@ -244,7 +244,9 @@ def main(data, save=False): # note columns can be forward and back filled for immutables like ethnicity. f_columns = ['education_state', 'labour_state_raw', 'job_sec', 'heating', 'ethnicity', 'sex', 'birth_year', 'yearly_gas', 'yearly_electric', 'yearly_gas_electric', 'yearly_oil', 'yearly_other_fuel', 'smoker', - 'nkids_ind_raw'] # 'ncigs', 'ndrinks'] + 'nkids_ind_raw', 'universal_credit', 'child_tax_credit', "working_tax_credit", "job_seekers_allowance", + "pension_credit", "income_support", "employment_and_support_allowance", + ] # 'ncigs', 'ndrinks'] fb_columns = ["sex", "ethnicity", "birth_year"] # or here if they're immutable. mf_columns = ['education_state', 'nkids_ind_raw'] li_columns = ["age"] diff --git a/minos/data_generation/US_utils.py b/minos/data_generation/US_utils.py index 08e91c5d..ebdf65dc 100755 --- a/minos/data_generation/US_utils.py +++ b/minos/data_generation/US_utils.py @@ -434,8 +434,8 @@ def replace_missing_with_na(data, column_list): return data -missing_types = ['-1', '-2', '-7', '-8', '-9', - -1., -2., -7., -8., -9., - -1, -2, -7, -8, -9, - '-1.0', '-2.0', '-7.0', '-8.0', '-9.0', +missing_types = ['-1', '-2', '-7', '-8', '-9', '-10', + -1., -2., -7., -8., -9., -10. + -1, -2, -7, -8, -9, -10., + '-1.0', '-2.0', '-7.0', '-8.0', '-9.0', '-10.0', "Dont Know", "Refused", "Proxy", "Inapplicable", "Missing"] diff --git a/minos/data_generation/generate_composite_vars.py b/minos/data_generation/generate_composite_vars.py index 65a2aafe..7618b8eb 100755 --- a/minos/data_generation/generate_composite_vars.py +++ b/minos/data_generation/generate_composite_vars.py @@ -987,6 +987,35 @@ def generate_difference_variables(data): return data +def combine_legacy_benefits(data): + """ Collate legacy benefits into a single universal credit variable. returns true if eligible for any of them. + + Parameters + ---------- + data: pd.DataFrame + Minos data with the legacy benefit columns + Returns + ------- + data: pd.DataFrame + Minos data with legacy columns removed and combined into single universal credit column. + """ + + legacy_benefits = ['child_tax_credit', # child tax credit. note this variable is fed forwards. + "working_tax_credit", # working tax credit + "job_seekers_allowance", # job seeker's allowance + "pension_credit", # pension credit + "income_support", # income support + "employment_and_support_allowance", # esa. + ] + + data['new_universal_credit'] = data[legacy_benefits + ['universal_credit']].any(axis=1).astype(float) + data.loc[(data[legacy_benefits + ['universal_credit']] < 0.).any(axis=1), "new_universal_credit"] = -9.0 + data['universal_credit'] = data['new_universal_credit'] + data.drop(labels= legacy_benefits + ["new_universal_credit"], + axis=1, + inplace=True) + return data + def main(): maxyr = US_utils.get_data_maxyr() # first collect and load the datafiles for every year @@ -996,6 +1025,8 @@ def main(): data = US_utils.load_multiple_data(file_names) # generate composite variables + + data = combine_legacy_benefits(data) # combine legacy benefits into single variable. data = generate_composite_housing_quality(data) # housing_quality. data = generate_hh_income(data) # hh_income. data = calculate_hourly_wage(data) # hourly_wage From e497698b596711814040d69eedcf1bf649e424f2 Mon Sep 17 00:00:00 2001 From: RobertClay Date: Thu, 7 Mar 2024 16:10:45 +0000 Subject: [PATCH 181/229] slight tweak in make lineplots to allow for NA weights --- minos/outcomes/make_lineplots.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/minos/outcomes/make_lineplots.py b/minos/outcomes/make_lineplots.py index 60bbe187..965e76d0 100644 --- a/minos/outcomes/make_lineplots.py +++ b/minos/outcomes/make_lineplots.py @@ -383,7 +383,7 @@ def find_MINOS_years_range(file_path): def weighted_nanmean(df, v, weights="weight", scale=1): #df = df.loc[df['weight'] > 0] #df.loc[df.index, weights] = 1/df[weights] - return np.nansum(df[v] * df[weights]) / sum(df[weights]) * scale + return np.nansum(df[v] * df[weights]) / np.nansum(df[weights]) * scale #return np.nansum(df[v]) From 0c4b72fbf241a72229df2d71f5c526611481c3f2 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Fri, 8 Mar 2024 10:39:07 +0000 Subject: [PATCH 182/229] Added some make targets for visualising QALY outputs for Glasgow Scaled simulation runs --- minos/outcomes/QALY.Makefile | 20 ++++++++++++++------ minos/outcomes/QALY_comparison2.Rmd | 2 +- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/minos/outcomes/QALY.Makefile b/minos/outcomes/QALY.Makefile index 5492e6b3..bc5c0a19 100644 --- a/minos/outcomes/QALY.Makefile +++ b/minos/outcomes/QALY.Makefile @@ -205,33 +205,41 @@ QALY_SCP_100_UC: ################## QALY_vis_SCP_25_All: QALY_baseline QALY_SCP_25_All - $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(intervention='25All'), output_file = 'QALY_SCP_25_All.html')" + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/' ,intervention='25All'), output_file = 'QALY_SCP_25_All.html')" #firefox file://$(OUTCOMES)/QALY_SCP_25_All.html QALY_vis_SCP_50_All: QALY_baseline QALY_SCP_50_All - $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(intervention='50All'), output_file = 'QALY_SCP_50_All.html')" + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/' ,intervention='50All'), output_file = 'QALY_SCP_50_All.html')" #firefox file://$(OUTCOMES)/QALY_SCP_50_All.html QALY_vis_SCP_100_All: QALY_baseline QALY_SCP_100_All - $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(intervention='100All'), output_file = 'QALY_SCP_100_All.html')" + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/' ,intervention='100All'), output_file = 'QALY_SCP_100_All.html')" #firefox file://$(OUTCOMES)/QALY_SCP_100_All.html +QALYs_all_child: EXPERIMENT=default_config QALYs_all_child: QALY_vis_SCP_25_All QALY_vis_SCP_50_All QALY_vis_SCP_100_All +QALYs_all_child_glasgow: EXPERIMENT=glasgow_scaled +QALYs_all_child_glasgow: QALY_vis_SCP_25_All QALY_vis_SCP_50_All QALY_vis_SCP_100_All + ################## # Universal Credit ################## QALY_vis_SCP_25_UC: QALY_baseline QALY_SCP_25_UC - $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(intervention='25UniversalCredit'), output_file = 'QALY_SCP_25_UC.html')" + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/' ,intervention='25UniversalCredit'), output_file = 'QALY_SCP_25_UC.html')" #firefox file://$(OUTCOMES)/QALY_SCP_25_UC.html QALY_vis_SCP_50_UC: QALY_baseline QALY_SCP_50_UC - $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(intervention='50UniversalCredit'), output_file = 'QALY_SCP_50_UC.html')" + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/' ,intervention='50UniversalCredit'), output_file = 'QALY_SCP_50_UC.html')" #firefox file://$(OUTCOMES)/QALY_SCP_50_UC.html QALY_vis_SCP_100_UC: QALY_baseline QALY_SCP_100_UC - $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(intervention='100UniversalCredit'), output_file = 'QALY_SCP_100_UC.html')" + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/' ,intervention='100UniversalCredit'), output_file = 'QALY_SCP_100_UC.html')" #firefox file://$(OUTCOMES)/QALY_SCP_100_UC.html +QALYs_UC: EXPERIMENT=default_config QALYs_UC: QALY_vis_SCP_25_UC QALY_vis_SCP_50_UC QALY_vis_SCP_100_UC + +QALYs_UC_glasgow: EXPERIMENT=glasgow_scaled +QALYs_UC_glasgow: QALY_vis_SCP_25_UC QALY_vis_SCP_50_UC QALY_vis_SCP_100_UC diff --git a/minos/outcomes/QALY_comparison2.Rmd b/minos/outcomes/QALY_comparison2.Rmd index 5f58eb45..16cb76c4 100644 --- a/minos/outcomes/QALY_comparison2.Rmd +++ b/minos/outcomes/QALY_comparison2.Rmd @@ -2,7 +2,7 @@ params: experiment: 'default_config/' base: 'baseline' - intervention: '50All' + intervention: '' title: "QALY Comparisons - `r params$experiment` - `r params$intervention` - `r params$baseline`" output: html_document: From 7d116d02b242fdd1f324c1087118fd6da094e8db Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Fri, 8 Mar 2024 10:44:10 +0000 Subject: [PATCH 183/229] Removed default parameters from RNotebook as I think its causing problems on arc --- minos/outcomes/QALY.Makefile | 12 ++++++------ minos/outcomes/QALY_comparison2.Rmd | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/minos/outcomes/QALY.Makefile b/minos/outcomes/QALY.Makefile index bc5c0a19..7000981a 100644 --- a/minos/outcomes/QALY.Makefile +++ b/minos/outcomes/QALY.Makefile @@ -205,15 +205,15 @@ QALY_SCP_100_UC: ################## QALY_vis_SCP_25_All: QALY_baseline QALY_SCP_25_All - $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/' ,intervention='25All'), output_file = 'QALY_SCP_25_All.html')" + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', ,intervention='25All'), output_file = 'QALY_SCP_25_All.html')" #firefox file://$(OUTCOMES)/QALY_SCP_25_All.html QALY_vis_SCP_50_All: QALY_baseline QALY_SCP_50_All - $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/' ,intervention='50All'), output_file = 'QALY_SCP_50_All.html')" + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='50All'), output_file = 'QALY_SCP_50_All.html')" #firefox file://$(OUTCOMES)/QALY_SCP_50_All.html QALY_vis_SCP_100_All: QALY_baseline QALY_SCP_100_All - $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/' ,intervention='100All'), output_file = 'QALY_SCP_100_All.html')" + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='100All'), output_file = 'QALY_SCP_100_All.html')" #firefox file://$(OUTCOMES)/QALY_SCP_100_All.html QALYs_all_child: EXPERIMENT=default_config @@ -227,15 +227,15 @@ QALYs_all_child_glasgow: QALY_vis_SCP_25_All QALY_vis_SCP_50_All QALY_vis_SCP_10 ################## QALY_vis_SCP_25_UC: QALY_baseline QALY_SCP_25_UC - $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/' ,intervention='25UniversalCredit'), output_file = 'QALY_SCP_25_UC.html')" + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='25UniversalCredit'), output_file = 'QALY_SCP_25_UC.html')" #firefox file://$(OUTCOMES)/QALY_SCP_25_UC.html QALY_vis_SCP_50_UC: QALY_baseline QALY_SCP_50_UC - $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/' ,intervention='50UniversalCredit'), output_file = 'QALY_SCP_50_UC.html')" + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='50UniversalCredit'), output_file = 'QALY_SCP_50_UC.html')" #firefox file://$(OUTCOMES)/QALY_SCP_50_UC.html QALY_vis_SCP_100_UC: QALY_baseline QALY_SCP_100_UC - $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/' ,intervention='100UniversalCredit'), output_file = 'QALY_SCP_100_UC.html')" + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='100UniversalCredit'), output_file = 'QALY_SCP_100_UC.html')" #firefox file://$(OUTCOMES)/QALY_SCP_100_UC.html QALYs_UC: EXPERIMENT=default_config diff --git a/minos/outcomes/QALY_comparison2.Rmd b/minos/outcomes/QALY_comparison2.Rmd index 16cb76c4..0e4e547d 100644 --- a/minos/outcomes/QALY_comparison2.Rmd +++ b/minos/outcomes/QALY_comparison2.Rmd @@ -1,7 +1,7 @@ --- params: - experiment: 'default_config/' - base: 'baseline' + experiment: '' + base: '' intervention: '' title: "QALY Comparisons - `r params$experiment` - `r params$intervention` - `r params$baseline`" output: From ac2e0dce0426e2819a7727d2e771063c1cbcc321 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Fri, 8 Mar 2024 10:46:04 +0000 Subject: [PATCH 184/229] Typo in some qaly notebook parameters --- minos/outcomes/QALY.Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/minos/outcomes/QALY.Makefile b/minos/outcomes/QALY.Makefile index 7000981a..5d837674 100644 --- a/minos/outcomes/QALY.Makefile +++ b/minos/outcomes/QALY.Makefile @@ -205,7 +205,7 @@ QALY_SCP_100_UC: ################## QALY_vis_SCP_25_All: QALY_baseline QALY_SCP_25_All - $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', ,intervention='25All'), output_file = 'QALY_SCP_25_All.html')" + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='25All'), output_file = 'QALY_SCP_25_All.html')" #firefox file://$(OUTCOMES)/QALY_SCP_25_All.html QALY_vis_SCP_50_All: QALY_baseline QALY_SCP_50_All From 05cda86e6359107dcc5600145fefd7e3b981a061 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Fri, 8 Mar 2024 10:52:59 +0000 Subject: [PATCH 185/229] Trying setting the experiment parameter when visualising results, and using this as this as the argument to the QALY calculation target to produce QALYs in the correct output directory --- minos/outcomes/QALY.Makefile | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/minos/outcomes/QALY.Makefile b/minos/outcomes/QALY.Makefile index 5d837674..ee75584e 100644 --- a/minos/outcomes/QALY.Makefile +++ b/minos/outcomes/QALY.Makefile @@ -11,7 +11,7 @@ QALY: ## Default QALYs -QALY_baseline: MODE=default_config +QALY_baseline: MODE=$(EXPERIMENT) QALY_baseline: INTERVENTION=baseline QALY_baseline: python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) @@ -152,22 +152,22 @@ QALY_comparison_livingWage_uk: QALY_baseline_uk QALY_livwage_uk # All Child ##################################### -QALY_SCP_25_All: MODE=default_config +QALY_SCP_25_All: MODE=$(EXPERIMENT) QALY_SCP_25_All: INTERVENTION=25All QALY_SCP_25_All: python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) -QALY_SCP_50_All: MODE=default_config +QALY_SCP_50_All: MODE=$(EXPERIMENT) QALY_SCP_50_All: INTERVENTION=50All QALY_SCP_50_All: python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) -QALY_SCP_75_All: MODE=default_config +QALY_SCP_75_All: MODE=$(EXPERIMENT) QALY_SCP_75_All: INTERVENTION=75All QALY_SCP_75_All: python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) -QALY_SCP_100_All: MODE=default_config +QALY_SCP_100_All: MODE=$(EXPERIMENT) QALY_SCP_100_All: INTERVENTION=100All QALY_SCP_100_All: python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) @@ -176,22 +176,22 @@ QALY_SCP_100_All: # Universal Credit ##################################### -QALY_SCP_25_UC: MODE=default_config +QALY_SCP_25_UC: MODE=$(EXPERIMENT) QALY_SCP_25_UC: INTERVENTION=25UniversalCredit QALY_SCP_25_UC: python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) -QALY_SCP_50_UC: MODE=default_config +QALY_SCP_50_UC: MODE=$(EXPERIMENT) QALY_SCP_50_UC: INTERVENTION=50UniversalCredit QALY_SCP_50_UC: python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) -QALY_SCP_75_UC: MODE=default_config +QALY_SCP_75_UC: MODE=$(EXPERIMENT) QALY_SCP_75_UC: INTERVENTION=75UniversalCredit QALY_SCP_75_UC: python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) -QALY_SCP_100_UC: MODE=default_config +QALY_SCP_100_UC: MODE=$(EXPERIMENT) QALY_SCP_100_UC: INTERVENTION=100UniversalCredit QALY_SCP_100_UC: python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) From f8d5afd45bac0884a45db8e85a63045a9c1870c3 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Fri, 8 Mar 2024 11:13:58 +0000 Subject: [PATCH 186/229] Parameterised the start year so we can handle synthetic 2020 starts alongside default 2020 --- minos/outcomes/QALY.Makefile | 16 ++++++++++------ minos/outcomes/QALY_comparison2.Rmd | 5 ++++- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/minos/outcomes/QALY.Makefile b/minos/outcomes/QALY.Makefile index ee75584e..d55db9c9 100644 --- a/minos/outcomes/QALY.Makefile +++ b/minos/outcomes/QALY.Makefile @@ -205,21 +205,23 @@ QALY_SCP_100_UC: ################## QALY_vis_SCP_25_All: QALY_baseline QALY_SCP_25_All - $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='25All'), output_file = 'QALY_SCP_25_All.html')" + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='25All', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_25_All.html')" #firefox file://$(OUTCOMES)/QALY_SCP_25_All.html QALY_vis_SCP_50_All: QALY_baseline QALY_SCP_50_All - $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='50All'), output_file = 'QALY_SCP_50_All.html')" + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='50All', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_50_All.html')" #firefox file://$(OUTCOMES)/QALY_SCP_50_All.html QALY_vis_SCP_100_All: QALY_baseline QALY_SCP_100_All - $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='100All'), output_file = 'QALY_SCP_100_All.html')" + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='100All', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_100_All.html')" #firefox file://$(OUTCOMES)/QALY_SCP_100_All.html QALYs_all_child: EXPERIMENT=default_config +QALYs_all_child: STARTYEAR=2021 QALYs_all_child: QALY_vis_SCP_25_All QALY_vis_SCP_50_All QALY_vis_SCP_100_All QALYs_all_child_glasgow: EXPERIMENT=glasgow_scaled +QALYs_all_child_glasgow: STARTYEAR=2020 QALYs_all_child_glasgow: QALY_vis_SCP_25_All QALY_vis_SCP_50_All QALY_vis_SCP_100_All ################## @@ -227,19 +229,21 @@ QALYs_all_child_glasgow: QALY_vis_SCP_25_All QALY_vis_SCP_50_All QALY_vis_SCP_10 ################## QALY_vis_SCP_25_UC: QALY_baseline QALY_SCP_25_UC - $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='25UniversalCredit'), output_file = 'QALY_SCP_25_UC.html')" + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='25UniversalCredit', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_25_UC.html')" #firefox file://$(OUTCOMES)/QALY_SCP_25_UC.html QALY_vis_SCP_50_UC: QALY_baseline QALY_SCP_50_UC - $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='50UniversalCredit'), output_file = 'QALY_SCP_50_UC.html')" + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='50UniversalCredit', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_50_UC.html')" #firefox file://$(OUTCOMES)/QALY_SCP_50_UC.html QALY_vis_SCP_100_UC: QALY_baseline QALY_SCP_100_UC - $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='100UniversalCredit'), output_file = 'QALY_SCP_100_UC.html')" + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='100UniversalCredit', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_100_UC.html')" #firefox file://$(OUTCOMES)/QALY_SCP_100_UC.html QALYs_UC: EXPERIMENT=default_config +QALYs_UC: STARTYEAR=2021 QALYs_UC: QALY_vis_SCP_25_UC QALY_vis_SCP_50_UC QALY_vis_SCP_100_UC QALYs_UC_glasgow: EXPERIMENT=glasgow_scaled +QALYs_UC_glasgow: STARTYEAR=2020 QALYs_UC_glasgow: QALY_vis_SCP_25_UC QALY_vis_SCP_50_UC QALY_vis_SCP_100_UC diff --git a/minos/outcomes/QALY_comparison2.Rmd b/minos/outcomes/QALY_comparison2.Rmd index 0e4e547d..af4f1076 100644 --- a/minos/outcomes/QALY_comparison2.Rmd +++ b/minos/outcomes/QALY_comparison2.Rmd @@ -3,6 +3,7 @@ params: experiment: '' base: '' intervention: '' + start.year: '' title: "QALY Comparisons - `r params$experiment` - `r params$intervention` - `r params$baseline`" output: html_document: @@ -49,6 +50,7 @@ source(here::here('minos', 'utils_qaly.R')) experiment <- params$experiment baseline <- params$base intervention <- params$intervention +start.year <- params$start.year # # experiment <- 'default_config' # baseline <- 'baseline' @@ -188,7 +190,8 @@ ICER(base = base, int = int, int.name = intervention, QALY_value = 70000, - int.label = intervention) + int.label = intervention, + start.year = start.year) ``` # Total Cost of Intervention From a826846e54fa177354cce17d1040ef6d04c3b8ee Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 11 Mar 2024 16:40:57 +0000 Subject: [PATCH 187/229] Removed reweighting of SF12 vars in QALY calculation as it caused problems. Not required for synthpop runs anyway --- minos/outcomes/QALY_calculation.py | 7 +++---- minos/outcomes/QALY_comparison2.Rmd | 10 ++++++++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/minos/outcomes/QALY_calculation.py b/minos/outcomes/QALY_calculation.py index f549abc8..cb98a7c5 100644 --- a/minos/outcomes/QALY_calculation.py +++ b/minos/outcomes/QALY_calculation.py @@ -71,8 +71,8 @@ def aggregate_csv(filename, intervention, year, start_year): df = df[df['alive'] != 'dead'] # adjust SF_12 values by sampling weight - df['SF_12_MCS'] = (df['SF_12_MCS'] * ((1 / df['weight']) / df['weight'].sum())) - df['SF_12_PCS'] = (df['SF_12_PCS'] * ((1 / df['weight']) / df['weight'].sum())) + #df['SF_12_MCS'] = (df['SF_12_MCS'] * ((1 / df['weight']) / df['weight'].sum())) + #df['SF_12_PCS'] = (df['SF_12_PCS'] * ((1 / df['weight']) / df['weight'].sum())) # record boost information for intervention runs, set to 0 for baseline if intervention == 'baseline': @@ -175,8 +175,7 @@ def main(mode, intervention): "sub-populations.") parser.add_argument("-m", "--mode", required=True, - help="Which experiment are we calculating for? Options currently are default_config, SIPHER7," - "and SIPHER7_glasgow.") + help="Which experiment are we calculating for?") parser.add_argument("-i", "--intervention", required=False, default="baseline", help="Is this a baseline or intervention run? Which intervention if intervention?") diff --git a/minos/outcomes/QALY_comparison2.Rmd b/minos/outcomes/QALY_comparison2.Rmd index af4f1076..86f95811 100644 --- a/minos/outcomes/QALY_comparison2.Rmd +++ b/minos/outcomes/QALY_comparison2.Rmd @@ -51,10 +51,11 @@ experiment <- params$experiment baseline <- params$base intervention <- params$intervention start.year <- params$start.year -# -# experiment <- 'default_config' +# # +# experiment <- 'glasgow_scaled' # baseline <- 'baseline' # intervention <- '50All' +# start.year <- 2020 ``` @@ -88,6 +89,11 @@ TESTING TEMPORARY TEMPORARY TEMPORARY TEMPORARY TEMPORARY TEMPORARY TEMPORARY ``` TESTING TEMPORARY TEMPORARY TEMPORARY TEMPORARY TEMPORARY TEMPORARY TEMPORARY +# Sample Information + +```{r} + +``` # Intervention Information From bc2fa0e9a7b4b0308385dfe301bafd01354a9a9a Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 12 Mar 2024 14:48:27 +0000 Subject: [PATCH 188/229] Refactored QALY_comparison2.Rmd to accept multiple interventions and visualise them together --- minos/outcomes/QALY.Makefile | 4 + minos/outcomes/QALY_comparison2.Rmd | 147 +++++---- minos/utils_qaly.R | 459 ++++++++++++++++++++++------ 3 files changed, 454 insertions(+), 156 deletions(-) diff --git a/minos/outcomes/QALY.Makefile b/minos/outcomes/QALY.Makefile index d55db9c9..6bf1898a 100644 --- a/minos/outcomes/QALY.Makefile +++ b/minos/outcomes/QALY.Makefile @@ -208,6 +208,8 @@ QALY_vis_SCP_25_All: QALY_baseline QALY_SCP_25_All $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='25All', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_25_All.html')" #firefox file://$(OUTCOMES)/QALY_SCP_25_All.html +QALY_vis_SCP_50_All: EXPERIMENT=glasgow_scaled +QALY_vis_SCP_50_All: STARTYEAR=2020 QALY_vis_SCP_50_All: QALY_baseline QALY_SCP_50_All $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='50All', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_50_All.html')" #firefox file://$(OUTCOMES)/QALY_SCP_50_All.html @@ -232,6 +234,8 @@ QALY_vis_SCP_25_UC: QALY_baseline QALY_SCP_25_UC $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='25UniversalCredit', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_25_UC.html')" #firefox file://$(OUTCOMES)/QALY_SCP_25_UC.html +QALY_vis_SCP_50_UC: EXPERIMENT=glasgow_scaled +QALY_vis_SCP_50_UC: STARTYEAR=2020 QALY_vis_SCP_50_UC: QALY_baseline QALY_SCP_50_UC $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='50UniversalCredit', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_50_UC.html')" #firefox file://$(OUTCOMES)/QALY_SCP_50_UC.html diff --git a/minos/outcomes/QALY_comparison2.Rmd b/minos/outcomes/QALY_comparison2.Rmd index 86f95811..572382ea 100644 --- a/minos/outcomes/QALY_comparison2.Rmd +++ b/minos/outcomes/QALY_comparison2.Rmd @@ -47,15 +47,16 @@ source(here::here('minos', 'utils_qaly.R')) ## Parameters ```{r} -experiment <- params$experiment -baseline <- params$base -intervention <- params$intervention -start.year <- params$start.year +# experiment <- params$experiment +# baseline <- params$base +# intervention <- params$intervention +# #intervention2 <- params$intervention2 +# start.year <- params$start.year # # -# experiment <- 'glasgow_scaled' -# baseline <- 'baseline' -# intervention <- '50All' -# start.year <- 2020 +experiment <- 'glasgow_scaled' +baseline <- 'baseline' +ints <- c('50All', '50UniversalCredit') +start.year <- 2020 ``` @@ -65,14 +66,34 @@ start.year <- params$start.year # read in QALY files for baseline and interventions out.path <- here::here('output', experiment) -base.path <- get_latest_runtime_subdirectory(here::here(out.path, baseline)) -int.path <- get_latest_runtime_subdirectory(here::here(out.path, intervention)) +expts <- c(baseline, ints) + -base <- read.csv(paste0(base.path, 'qalys.csv')) -int <- read.csv(paste0(int.path, 'qalys.csv')) +combined <- data.frame() +for (expt in expts) { + path <- get_latest_runtime_subdirectory(here::here(out.path, expt)) + data <- read.csv(paste0(path, 'qalys.csv')) + combined <- rbind(combined, data) +} +``` -# combine the dataframes -combined <- rbind(base, int) + +```{r} +# # read in QALY files for baseline and interventions +# out.path <- here::here('output', experiment) +# +# base.path <- get_latest_runtime_subdirectory(here::here(out.path, baseline)) +# int.path <- get_latest_runtime_subdirectory(here::here(out.path, intervention1)) +# +# if (intervention2 != '') { +# int2.path <- get_latest_runtime_subdirectory(here::here(out.path, intervention2)) +# } +# +# base <- read.csv(paste0(base.path, 'qalys.csv')) +# int <- read.csv(paste0(int.path, 'qalys.csv')) +# +# # combine the dataframes +# combined <- rbind(base, int) ``` @@ -105,7 +126,7 @@ combined.pop_boosted <- combined %>% group_by(year, run_id, intervention) %>% summarise(pop_boosted = sum(pop_boosted)) -print(paste("There are", sum(is.na(combined.pop_boosted$pop_boosted)), "NA pop_boosted values in combined.pop.boosted.", sep = ' ')) +# print(paste("There are", sum(is.na(combined.pop_boosted$pop_boosted)), "NA pop_boosted values in combined.pop.boosted.", sep = ' ')) ggplot(combined.pop_boosted, aes(x = year, y = pop_boosted, group = intervention, colour = intervention)) + geom_smooth() + @@ -115,58 +136,60 @@ combined.diff <- combined %>% select(run_id, year, intervention, total_boost) %>% pivot_wider(names_from = 'intervention', values_from = 'total_boost') %>% - mutate(diff.boost = .data[[intervention]] - .data[[baseline]]) - -ggplot(combined.diff, aes(x = year, y = diff.boost)) + + mutate(across(all_of(ints), ~.x - .data[[baseline]], .names = "diff_{.col}")) %>% + select(run_id, year, contains('diff_')) %>% + pivot_longer(cols = contains('diff_'), + names_prefix = 'diff_', + names_to = 'scenario', + values_to = 'total_boost') + +ggplot(combined.diff, aes(x = year, y = total_boost, group = scenario, fill = scenario, color = scenario)) + geom_smooth() + labs(title = 'Total Boost amount by year') + scale_y_continuous(label = comma) - -ggplot(combined, aes(x = year, y = alive_ratio, group = intervention, colour = intervention)) + - geom_smooth() + - labs(title = 'Ratio of living to dead in output dataframe') - -## incidence of mortality -deaths <- combined %>% - select(run_id, intervention, year, alive_pop, dead_pop) %>% - group_by(run_id, intervention) %>% - mutate(total_pop = alive_pop + dead_pop, - deaths_this_year = dead_pop - lag(dead_pop)) %>% - group_by(run_id, year, intervention) %>% - summarise(death_incidence = deaths_this_year / total_pop) - -ggplot(deaths, aes(x = year, y = death_incidence, group = intervention, colour = intervention)) + - geom_smooth() + - labs(title = 'Incidence of Mortality Comparison') +# +# ggplot(combined, aes(x = year, y = alive_ratio, group = intervention, colour = intervention)) + +# geom_smooth() + +# labs(title = 'Ratio of living to dead in output dataframe') +# +# ## incidence of mortality +# deaths <- combined %>% +# select(run_id, intervention, year, alive_pop, dead_pop) %>% +# group_by(run_id, intervention) %>% +# mutate(total_pop = alive_pop + dead_pop, +# deaths_this_year = dead_pop - lag(dead_pop)) %>% +# group_by(run_id, year, intervention) %>% +# summarise(death_incidence = deaths_this_year / total_pop) +# +# ggplot(deaths, aes(x = year, y = death_incidence, group = intervention, colour = intervention)) + +# geom_smooth() + +# labs(title = 'Incidence of Mortality Comparison') ``` # QALY comparison ```{r} -QALY_comparison(base = base, - base.name = baseline, - int = int, - int.name = intervention) +# QALY_comparison(base = base, +# base.name = baseline, +# int = int, +# int.name = intervention) + +QALY_comparison(combined, ints) ``` ## SF12 Change ```{r} -sf12.plots(base = base, - base.name = baseline, - int = int, - int.name = intervention) +sf12.plots(combined, ints) ``` + ## Area Under the Curve ```{r} -auc.plots(base = base, - base.name = baseline, - intervention = int, - int.name = intervention) +auc.plots(combined, ints) ``` @@ -175,12 +198,12 @@ auc.plots(base = base, The [UK Government](https://www.gov.uk/government/publications/valuation-of-risks-to-life-and-health-monetary-value-of-a-life-year-voly/a-scoping-study-on-the-valuation-of-risks-to-life-and-health-the-monetary-value-of-a-life-year-voly) has set the value of 1 QALY to be equal to $60,000. This is the number we will use for our calculations. ```{r} -cost.per.qaly(base = base, - base.name = baseline, - int = int, - int.name = intervention, - QALY_value = 70000, # QALY value == £60,000 - int.label <- intervention) +# cost.per.qaly(base = base, +# base.name = baseline, +# int = int, +# int.name = intervention, +# QALY_value = 70000, # QALY value == £70,000 +# int.label <- intervention) ``` @@ -191,23 +214,21 @@ From [NICE guidelines](https://www.nice.org.uk/process/pmg6/chapter/assessing-co From the NICE guidelines, there is a general consensus that anything below £20,000 is cost effective, and anything above £30,000 is not. Between £20,000 and £30,000 is a grey area, and depends on the field and other factors to decide (i.e. if a rare or important condition is tackled by an intervention then the cost-effective threshold is higher). ```{r} -ICER(base = base, - base.name = baseline, - int = int, - int.name = intervention, +ICER(combined, + ints, QALY_value = 70000, - int.label = intervention, start.year = start.year) ``` + # Total Cost of Intervention ```{r} -intervention.cost(base = base, - base.name = baseline, - int = int, - int.name = intervention, - int.label = intervention) +# intervention.cost(base = base, +# base.name = baseline, +# int = int, +# int.name = intervention, +# int.label = intervention) ``` diff --git a/minos/utils_qaly.R b/minos/utils_qaly.R index 3ad80069..eac1113d 100644 --- a/minos/utils_qaly.R +++ b/minos/utils_qaly.R @@ -6,9 +6,47 @@ ############################################################################## ## QALY comparisons -QALY_comparison <- function(base, base.name, int, int.name) { - - combined <- rbind(base, int) +# QALY_comparison <- function(base, base.name, int, int.name) { +# +# combined <- rbind(base, int) +# +# p1 <- ggplot(data = combined, aes(x = year, y = QALYs, group = intervention, colour = intervention)) + +# geom_smooth() + +# labs(title = 'QALYs per year') +# print(p1) +# +# # Now change from baseline +# combined.QALY.change <- combined %>% +# select(run_id, year, intervention, QALYs) %>% +# pivot_wider(names_from = 'intervention', +# values_from = 'QALYs') %>% +# mutate(QALYs = .data[[int.name]] - .data[[base.name]]) %>% +# select(run_id, year, QALYs) +# +# p2 <- ggplot(data = combined.QALY.change, aes(x = year, y = QALYs)) + +# geom_hline(yintercept = 0, linetype = 'dashed') + +# geom_smooth() + +# labs(title = 'QALY change', subtitle = paste0(int.name, ' vs ', base.name)) +# print(p2) +# +# combined.QALY.change.confint <- combined.QALY.change %>% +# group_by(year) %>% +# summarise(n = n(), +# mean_QALY_change = mean(QALYs), +# margin = qt(0.975, df = n - 1) * (sd(QALYs) / sqrt(n)), +# lower = mean_QALY_change - margin, +# upper = mean_QALY_change + margin) +# +# p3 <- ggplot(combined.QALY.change.confint, aes(x = year, y = mean_QALY_change)) + +# geom_ribbon(aes(ymin = lower, ymax = upper), fill = 'grey70') + +# geom_line(color = "blue") + +# geom_hline(yintercept = 0, linetype = 'dashed') + +# labs(title = 'QALY Change', subtitle = paste0(int.name, ' vs ', base.name)) +# print(p3) +# } + +## QALY comparisons +QALY_comparison <- function(combined, ints) { p1 <- ggplot(data = combined, aes(x = year, y = QALYs, group = intervention, colour = intervention)) + geom_smooth() + @@ -20,28 +58,35 @@ QALY_comparison <- function(base, base.name, int, int.name) { select(run_id, year, intervention, QALYs) %>% pivot_wider(names_from = 'intervention', values_from = 'QALYs') %>% - mutate(QALYs = .data[[int.name]] - .data[[base.name]]) %>% - select(run_id, year, QALYs) - - p2 <- ggplot(data = combined.QALY.change, aes(x = year, y = QALYs)) + + #mutate(QALYs = .data[[int.name]] - .data[[base.name]]) %>% + mutate(across(all_of(ints), ~.x - .data[[baseline]], .names = "QALYs_{.col}")) %>% + select(run_id, year, contains('QALYs')) %>% + pivot_longer(cols = contains('QALYs'), + names_prefix = 'QALYs_', + names_to = 'scenario', + values_to = 'QALYs') + + p2 <- ggplot(data = combined.QALY.change, aes(x = year, y = QALYs, group = scenario, color = scenario, fill = scenario)) + geom_hline(yintercept = 0, linetype = 'dashed') + geom_smooth() + - labs(title = 'QALY change', subtitle = paste0(int.name, ' vs ', base.name)) + labs(title = 'QALY change') print(p2) combined.QALY.change.confint <- combined.QALY.change %>% - group_by(year) %>% + group_by(year, scenario) %>% summarise(n = n(), mean_QALY_change = mean(QALYs), margin = qt(0.975, df = n - 1) * (sd(QALYs) / sqrt(n)), lower = mean_QALY_change - margin, upper = mean_QALY_change + margin) - p3 <- ggplot(combined.QALY.change.confint, aes(x = year, y = mean_QALY_change)) + - geom_ribbon(aes(ymin = lower, ymax = upper), fill = 'grey70') + - geom_line(color = "blue") + + p3 <- ggplot(combined.QALY.change.confint, aes(x = year, y = mean_QALY_change, group = scenario, color = scenario, fill = scenario)) + + geom_ribbon(aes(ymin = lower, ymax = upper)) + + geom_line(color = 'black') + geom_hline(yintercept = 0, linetype = 'dashed') + - labs(title = 'QALY Change', subtitle = paste0(int.name, ' vs ', base.name)) + labs(title = 'QALY Change') + + xlab('Year') + + ylab('QALYs') print(p3) } @@ -78,10 +123,7 @@ QALY_comparison.smooth <- function(base, base.name, int, int.name) { # Plot mean comparison and change from baseline for both MCS and PCS. # This version plots lineplots with 95% confidence intervals instead of # smoothed below -sf12.plots <- function(base, base.name, int, int.name) { - ## SF12 Comparison Plots - combined <- rbind(base, int) - +sf12.plots <- function(combined, ints) { ## now SF12 plots for comparison p1 <- ggplot(combined, aes(x = year, y = SF_12_MCS, group = intervention, colour = intervention, fill = intervention)) + geom_smooth() @@ -95,24 +137,30 @@ sf12.plots <- function(base, base.name, int, int.name) { select(run_id, year, intervention, SF_12_MCS, SF_12_PCS) %>% pivot_wider(names_from = 'intervention', values_from = c('SF_12_MCS', 'SF_12_PCS')) %>% - mutate(MCS_diff = .data[[paste0('SF_12_MCS_', int.name)]] - .data[[paste0('SF_12_MCS_', base.name)]], - PCS_diff = .data[[paste0('SF_12_PCS_', int.name)]] - .data[[paste0('SF_12_PCS_', base.name)]]) %>% - select(run_id, year, MCS_diff, PCS_diff) - - - p3 <- ggplot(combined.SF12, aes(x = year, y = MCS_diff)) + + mutate(across(all_of(paste0('SF_12_MCS_', ints)), ~.x - .data[[paste0('SF_12_MCS_', baseline)]], .names = "diff_MCS_{.col}"), + across(all_of(paste0('SF_12_PCS_', ints)), ~.x - .data[[paste0('SF_12_PCS_', baseline)]], .names = "diff_PCS_{.col}")) %>% + select(run_id, year, starts_with('diff_')) %>% + rename_with(~sub("diff_MCS_SF_12_MCS_", "MCS_", .x), starts_with("diff_MCS_SF_12_MCS_")) %>% + rename_with(~sub("diff_PCS_SF_12_PCS_", "PCS_", .x), starts_with("diff_PCS_SF_12_PCS_")) %>% + pivot_longer(cols = contains(ints), + names_sep = '_', + names_to = c('SF12', 'scenario'), + values_to = 'difference') + + + p3 <- ggplot(filter(combined.SF12, SF12 == 'MCS'), aes(x = year, y = difference, group = scenario, color = scenario, fill = scenario)) + geom_point() + geom_smooth() + geom_hline(yintercept = 0, linetype = 'dashed') + - labs(title = 'Change in SF_12_MCS', subtitle = paste0(int.name, ' vs ', base.name)) + + labs(title = 'Change in SF_12_MCS') + xlab('Year') + ylab('Change in MCS') - p4 <- ggplot(combined.SF12, aes(x = year, y = PCS_diff)) + + p4 <- ggplot(filter(combined.SF12, SF12 == 'PCS'), aes(x = year, y = difference, group = scenario, color = scenario, fill = scenario)) + geom_point() + geom_smooth() + geom_hline(yintercept = 0, linetype = 'dashed') + - labs(title = 'Change in SF_12_PCS', subtitle = paste0(int.name, ' vs ', base.name)) + + labs(title = 'Change in SF_12_PCS') + xlab('Year') + ylab('Change in PCS') @@ -121,26 +169,24 @@ sf12.plots <- function(base, base.name, int, int.name) { combined.small <- combined.SF12 %>% - group_by(year) %>% + group_by(year, SF12, scenario) %>% summarise(n = n(), - mean_MCS_diff = mean(MCS_diff), - mean_PCS_diff = mean(PCS_diff), - MCS_margin = qt(0.975, df = n - 1) * (sd(MCS_diff) / sqrt(n)), # 95% confidence intervals - PCS_margin = qt(0.975, df = n - 1) * (sd(PCS_diff) / sqrt(n))) + mean_diff = mean(difference), + margin = qt(0.975, df = n - 1) * (sd(difference) / sqrt(n))) # 95% confidence intervals - p5 <- ggplot(combined.small, aes(x = year, y = mean_MCS_diff)) + - geom_ribbon(aes(ymin = mean_MCS_diff - MCS_margin, ymax = mean_MCS_diff + MCS_margin), fill = 'grey70') + + p5 <- ggplot(filter(combined.small, SF12 == 'MCS'), aes(x = year, y = mean_diff, group = scenario, color = scenario, fill = scenario)) + + geom_ribbon(aes(ymin = mean_diff - margin, ymax = mean_diff + margin), fill = 'grey70') + geom_line() + geom_hline(yintercept = 0, linetype = 'dashed') + - labs(title = 'Change in SF_12_MCS', subtitle = paste0(int.name, ' vs ', base.name)) + + labs(title = 'Change in SF_12_MCS') + xlab('Year') + ylab('Change in MCS') - p6 <- ggplot(combined.small, aes(x = year, y = mean_PCS_diff)) + - geom_ribbon(aes(ymin = mean_PCS_diff - PCS_margin, ymax = mean_PCS_diff + PCS_margin), fill = 'grey70') + + p6 <- ggplot(filter(combined.small, SF12 == 'PCS'), aes(x = year, y = mean_diff, group = scenario, color = scenario, fill = scenario)) + + geom_ribbon(aes(ymin = mean_diff - margin, ymax = mean_diff + margin), fill = 'grey70') + geom_line() + geom_hline(yintercept = 0, linetype = 'dashed') + - labs(title = 'Change in SF_12_PCS', subtitle = paste0(int.name, ' vs ', base.name)) + + labs(title = 'Change in SF_12_PCS') + xlab('Year') + ylab('Change in PCS') @@ -148,6 +194,79 @@ sf12.plots <- function(base, base.name, int, int.name) { print(p6) } +# # Plot mean comparison and change from baseline for both MCS and PCS. +# # This version plots lineplots with 95% confidence intervals instead of +# # smoothed below +# sf12.plots <- function(base, base.name, int, int.name) { +# ## SF12 Comparison Plots +# combined <- rbind(base, int) +# +# ## now SF12 plots for comparison +# p1 <- ggplot(combined, aes(x = year, y = SF_12_MCS, group = intervention, colour = intervention, fill = intervention)) + +# geom_smooth() +# p2 <- ggplot(combined, aes(x = year, y = SF_12_PCS, group = intervention, colour = intervention, fill = intervention)) + +# geom_smooth() +# +# print(p1) +# print(p2) +# +# combined.SF12 <- combined %>% +# select(run_id, year, intervention, SF_12_MCS, SF_12_PCS) %>% +# pivot_wider(names_from = 'intervention', +# values_from = c('SF_12_MCS', 'SF_12_PCS')) %>% +# mutate(MCS_diff = .data[[paste0('SF_12_MCS_', int.name)]] - .data[[paste0('SF_12_MCS_', base.name)]], +# PCS_diff = .data[[paste0('SF_12_PCS_', int.name)]] - .data[[paste0('SF_12_PCS_', base.name)]]) %>% +# select(run_id, year, MCS_diff, PCS_diff) +# +# +# p3 <- ggplot(combined.SF12, aes(x = year, y = MCS_diff)) + +# geom_point() + +# geom_smooth() + +# geom_hline(yintercept = 0, linetype = 'dashed') + +# labs(title = 'Change in SF_12_MCS', subtitle = paste0(int.name, ' vs ', base.name)) + +# xlab('Year') + +# ylab('Change in MCS') +# +# p4 <- ggplot(combined.SF12, aes(x = year, y = PCS_diff)) + +# geom_point() + +# geom_smooth() + +# geom_hline(yintercept = 0, linetype = 'dashed') + +# labs(title = 'Change in SF_12_PCS', subtitle = paste0(int.name, ' vs ', base.name)) + +# xlab('Year') + +# ylab('Change in PCS') +# +# print(p3) +# print(p4) +# +# +# combined.small <- combined.SF12 %>% +# group_by(year) %>% +# summarise(n = n(), +# mean_MCS_diff = mean(MCS_diff), +# mean_PCS_diff = mean(PCS_diff), +# MCS_margin = qt(0.975, df = n - 1) * (sd(MCS_diff) / sqrt(n)), # 95% confidence intervals +# PCS_margin = qt(0.975, df = n - 1) * (sd(PCS_diff) / sqrt(n))) +# +# p5 <- ggplot(combined.small, aes(x = year, y = mean_MCS_diff)) + +# geom_ribbon(aes(ymin = mean_MCS_diff - MCS_margin, ymax = mean_MCS_diff + MCS_margin), fill = 'grey70') + +# geom_line() + +# geom_hline(yintercept = 0, linetype = 'dashed') + +# labs(title = 'Change in SF_12_MCS', subtitle = paste0(int.name, ' vs ', base.name)) + +# xlab('Year') + +# ylab('Change in MCS') +# +# p6 <- ggplot(combined.small, aes(x = year, y = mean_PCS_diff)) + +# geom_ribbon(aes(ymin = mean_PCS_diff - PCS_margin, ymax = mean_PCS_diff + PCS_margin), fill = 'grey70') + +# geom_line() + +# geom_hline(yintercept = 0, linetype = 'dashed') + +# labs(title = 'Change in SF_12_PCS', subtitle = paste0(int.name, ' vs ', base.name)) + +# xlab('Year') + +# ylab('Change in PCS') +# +# print(p5) +# print(p6) +# } + ## SF12 Comparison Plots # Plot mean comparison and change from baseline for both MCS and PCS. @@ -199,18 +318,11 @@ sf12.plots.smooth <- function(base, base.name, int, int.name) { # This function will plot the total AUC (which equals the total QALYs in the # system) for 2 interventions, and the change in AUC between the 2. -auc.plots <- function(base, base.name, intervention, int.name) { - base.auc <- base %>% - group_by(run_id) %>% - summarise(AUC = auc(x=year, y = QALYs, type = 'spline')) %>% - mutate(intervention = base.name) - - int.auc <- intervention %>% - group_by(run_id) %>% - summarise(AUC = auc(x=year, y = QALYs, type = 'spline')) %>% - mutate(intervention = int.name) +auc.plots <- function(combined, ints) { - combined.auc <- rbind(base.auc, int.auc) + combined.auc <- combined %>% + group_by(intervention, run_id) %>% + summarise(AUC = auc(x=year, y = QALYs, type = 'spline')) combined.auc.plot <- combined.auc %>% group_by(intervention) %>% @@ -222,29 +334,30 @@ auc.plots <- function(base, base.name, intervention, int.name) { geom_col() + geom_errorbar(aes(ymin = AUC - margin, ymax = AUC + margin, width = 0.4, group = intervention)) + coord_cartesian(ylim = c(min(combined.auc.plot$AUC) - max(combined.auc.plot$margin), max(combined.auc.plot$AUC) + max(combined.auc.plot$margin))) + - labs(title = 'Total AUC comparison', subtitle = paste0(int.name, ' vs ', base.name)) + + labs(title = 'Total AUC comparison') + xlab('Intervention') print(p1) - auc.base <- combined.auc %>% filter(intervention == 'baseline') - auc.int <- combined.auc %>% filter(intervention == int.name) - - tt <- t.test(x = auc.base$AUC, - y = auc.int$AUC) + # auc.base <- combined.auc %>% filter(intervention == 'baseline') + # auc.int <- combined.auc %>% filter(intervention == int.name) + # + # tt <- t.test(x = auc.base$AUC, + # y = auc.int$AUC) + # + # print(tt) - print(tt) + #mutate(across(all_of(ints), ~.x - .data[[baseline]], .names = "diff_{.col}")) %>% # Another version showing difference from baseline for easier comparison combined.auc2 <- combined.auc %>% pivot_wider(names_from = 'intervention', values_from = c('AUC')) %>% - mutate(change_baseline = .data[[base.name]] - .data[[base.name]], - change_intervention = .data[[int.name]] - .data[[base.name]]) %>% - pivot_longer(cols = change_baseline:change_intervention, + mutate(across(all_of(ints), ~.x - .data[[baseline]], .names = "change_{.col}")) %>% + select(run_id, contains('change_')) %>% + pivot_longer(cols = contains('change_'), names_to = 'intervention', names_prefix = 'change_', - values_to = 'AUC_difference') %>% - filter(intervention != 'baseline') + values_to = 'AUC_difference') combined.auc2.plot <- combined.auc2 %>% group_by(intervention) %>% @@ -254,14 +367,83 @@ auc.plots <- function(base, base.name, intervention, int.name) { p2 <- ggplot(combined.auc2.plot, aes(x = intervention, y = AUC_difference, group = intervention, fill = intervention)) + geom_col() + geom_errorbar(aes(ymin = AUC_difference - margin, ymax = AUC_difference + margin, width = 0.4, group = intervention)) + - labs(title = 'Change in AUC', subtitle = paste0(int.name, ' vs ', base.name)) + + labs(title = 'Change in AUC') + xlab('Intervention') print(p2) - print(paste0('Change in AUC == ', combined.auc2.plot$AUC_difference, ' +- ', combined.auc2.plot$margin)) + #print(paste0('Change in AUC == ', combined.auc2.plot$AUC_difference, ' +- ', combined.auc2.plot$margin)) + + print("Table of change in AUC:") + print(combined.auc2.plot) + } +# This function will plot the total AUC (which equals the total QALYs in the +# system) for 2 interventions, and the change in AUC between the 2. +# auc.plots <- function(base, base.name, intervention, int.name) { +# base.auc <- base %>% +# group_by(run_id) %>% +# summarise(AUC = auc(x=year, y = QALYs, type = 'spline')) %>% +# mutate(intervention = base.name) +# +# int.auc <- intervention %>% +# group_by(run_id) %>% +# summarise(AUC = auc(x=year, y = QALYs, type = 'spline')) %>% +# mutate(intervention = int.name) +# +# combined.auc <- rbind(base.auc, int.auc) +# +# combined.auc.plot <- combined.auc %>% +# group_by(intervention) %>% +# summarise(sd = sd(AUC), +# margin = (qt(0.975, df = n() - 1) * sd) / sqrt(n()), +# AUC = mean(AUC)) +# +# p1 <- ggplot(data = combined.auc.plot, aes(x = intervention, y = AUC, group = intervention, fill = intervention)) + +# geom_col() + +# geom_errorbar(aes(ymin = AUC - margin, ymax = AUC + margin, width = 0.4, group = intervention)) + +# coord_cartesian(ylim = c(min(combined.auc.plot$AUC) - max(combined.auc.plot$margin), max(combined.auc.plot$AUC) + max(combined.auc.plot$margin))) + +# labs(title = 'Total AUC comparison', subtitle = paste0(int.name, ' vs ', base.name)) + +# xlab('Intervention') +# print(p1) +# +# auc.base <- combined.auc %>% filter(intervention == 'baseline') +# auc.int <- combined.auc %>% filter(intervention == int.name) +# +# tt <- t.test(x = auc.base$AUC, +# y = auc.int$AUC) +# +# print(tt) +# +# # Another version showing difference from baseline for easier comparison +# combined.auc2 <- combined.auc %>% +# pivot_wider(names_from = 'intervention', +# values_from = c('AUC')) %>% +# mutate(change_baseline = .data[[base.name]] - .data[[base.name]], +# change_intervention = .data[[int.name]] - .data[[base.name]]) %>% +# pivot_longer(cols = change_baseline:change_intervention, +# names_to = 'intervention', +# names_prefix = 'change_', +# values_to = 'AUC_difference') %>% +# filter(intervention != 'baseline') +# +# combined.auc2.plot <- combined.auc2 %>% +# group_by(intervention) %>% +# summarise(margin = (qt(0.975, df = n() - 1) * sd(AUC_difference)) / sqrt(n()), +# AUC_difference = mean(AUC_difference)) +# +# p2 <- ggplot(combined.auc2.plot, aes(x = intervention, y = AUC_difference, group = intervention, fill = intervention)) + +# geom_col() + +# geom_errorbar(aes(ymin = AUC_difference - margin, ymax = AUC_difference + margin, width = 0.4, group = intervention)) + +# labs(title = 'Change in AUC', subtitle = paste0(int.name, ' vs ', base.name)) + +# xlab('Intervention') +# +# print(p2) +# +# print(paste0('Change in AUC == ', combined.auc2.plot$AUC_difference, ' +- ', combined.auc2.plot$margin)) +# } + ############################################################################## # COST PER QALY ############################################################################## @@ -388,35 +570,125 @@ cost.per.qaly.smooth <- function(base, base.name, int, int.name, QALY_value, int # Incremental Cost Effective Ratio (ICER) ############################################################################## -ICER <- function(base, base.name, int, int.name, QALY_value, int.label, start.year = 2021) { - combined <- rbind(base, int) - - combined.cost.mean <- combined %>% - #select(-alive_pop, -SF_12_MCS, -SF_12_PCS, -utility) %>% - select(run_id, year, total_boost, intervention, QALYs) %>% - mutate(QALY_value = QALYs * QALY_value) %>% - group_by(run_id, year, intervention) %>% - summarise(across(everything(), mean)) - - # ICER = (C1 - C0) / (E1 - E0) - # where - # C1 = cost of intervention - # E1 = effect of intervention - # C0 = cost of control group - # E0 = effect of control group +# ICER <- function(base, base.name, int, int.name, QALY_value, int.label, start.year = 2021) { +# combined <- rbind(base, int) +# +# combined.cost.mean <- combined %>% +# #select(-alive_pop, -SF_12_MCS, -SF_12_PCS, -utility) %>% +# select(run_id, year, total_boost, intervention, QALYs) %>% +# mutate(QALY_value = QALYs * QALY_value) %>% +# group_by(run_id, year, intervention) %>% +# summarise(across(everything(), mean)) +# +# # ICER = (C1 - C0) / (E1 - E0) +# # where +# # C1 = cost of intervention +# # E1 = effect of intervention +# # C0 = cost of control group +# # E0 = effect of control group +# +# # prepare var names +# c1 <- paste('total_boost', int.name, sep='_') +# c0 <- paste('total_boost', base.name, sep='_') +# e1 <- paste('QALYs', int.name, sep='_') +# e0 <- paste('QALYs', base.name, sep='_') +# +# ICER <- combined.cost.mean %>% +# pivot_wider(names_from = 'intervention', +# values_from = c('total_boost', 'QALYs', 'QALY_value')) %>% +# mutate(ICER = (.data[[c1]] - .data[[c0]]) / (.data[[e1]] - .data[[e0]])) %>% +# select(run_id, year, ICER) %>% +# mutate(intervention = int.label) +# +# # No intervention in first year so can't calculate ICER +# #TODO: Find start year automatically +# ICER <- ICER %>% +# filter(year != start.year) +# +# ICER.plot <- ICER %>% +# group_by(year, intervention) %>% +# summarise(n = n(), +# mean_ICER = mean(ICER), +# margin = qt(0.975, df = n - 1) * (sd(ICER) / sqrt(n))) +# +# p1 <- ggplot(ICER.plot, aes(x = year, y = mean_ICER, group = intervention, fill = intervention, colour = intervention)) + +# geom_ribbon(aes(ymin = mean_ICER - margin, ymax = mean_ICER + margin)) + +# geom_line(color = 'black') + +# geom_hline(yintercept = 0, linetype = 'dashed') + +# scale_y_continuous(label = comma) + +# labs(title = 'ICER - Full', subtitle = paste0(int.name, ' vs ', base.name)) +# print(p1) +# +# pc1 <- quantile(ICER$ICER, .01) +# # print(pc1) +# # print(min(ICER$ICER)) +# pc99 <- quantile(ICER$ICER, .99) +# # print(pc99) +# # print(max(ICER$ICER)) +# +# ICER <- filter(ICER, ICER < pc99, ICER > pc1) +# # print(min(ICER$ICER)) +# # print(max(ICER$ICER)) +# +# ICER.plot2 <- ICER %>% +# group_by(year, intervention) %>% +# summarise(n = n(), +# mean_ICER = mean(ICER), +# margin = qt(0.975, df = n - 1) * (sd(ICER) / sqrt(n))) +# +# p2 <- ggplot(ICER.plot2, aes(x = year, y = mean_ICER, group = intervention, fill = intervention, colour = intervention)) + +# geom_ribbon(aes(ymin = mean_ICER - margin, ymax = mean_ICER + margin)) + +# geom_line(color = 'black') + +# geom_hline(yintercept = 0, linetype = 'dashed') + +# scale_y_continuous(label = comma) + +# labs(title = 'ICER - Outliers Removed', subtitle = paste0(int.name, ' vs ', base.name)) +# print(p2) +# +# ICER.final <- ICER %>% +# group_by(run_id, intervention) %>% +# summarise(ICER = mean(ICER)) %>% +# group_by(intervention) %>% +# summarise(margin = (qt(0.975, df=n() - 1) * sd(ICER)) / sqrt(n()), +# ICER = mean(ICER)) +# +# p3 <- ggplot(ICER.final, aes(x = intervention, y = ICER, group = intervention, fill = intervention)) + +# geom_col() + +# geom_errorbar(aes(ymin = ICER - margin, ymax = ICER + margin, width = 0.4, group = intervention)) + +# scale_y_continuous(label = comma) + +# labs(title = 'ICER', subtitle = paste0(int.name, ' vs ', base.name)) + +# xlab('Intervention') +# print(p3) +# +# print(paste0('ICER == ', format(ICER.final$ICER, big.mark = ',', trim = TRUE), ' +- ', format(ICER.final$margin, big.mark = ',', trim = TRUE))) +# } + +ICER <- function(combined, ints, QALY_value, start.year = 2020) { - # prepare var names - c1 <- paste('total_boost', int.name, sep='_') - c0 <- paste('total_boost', base.name, sep='_') - e1 <- paste('QALYs', int.name, sep='_') - e0 <- paste('QALYs', base.name, sep='_') + # Calculate the monetary value of QALYs + combined <- combined %>% + mutate(QALY_value_monetary = QALYs * QALY_value) - ICER <- combined.cost.mean %>% - pivot_wider(names_from = 'intervention', - values_from = c('total_boost', 'QALYs', 'QALY_value')) %>% - mutate(ICER = (.data[[c1]] - .data[[c0]]) / (.data[[e1]] - .data[[e0]])) %>% - select(run_id, year, ICER) %>% - mutate(intervention = int.label) + # Calculate mean costs and effects (QALYs) per intervention and year + combined_summary <- combined %>% + group_by(run_id, year, intervention) %>% + summarise(mean_total_boost = mean(total_boost), + mean_QALYs = mean(QALYs), + mean_QALY_value = mean(QALY_value_monetary), + .groups = 'drop') + + # Identify baseline intervention + baseline_intervention <- "baseline" # Adjust this to match the exact name of your baseline in the 'intervention' column + + # For each intervention, calculate ICER relative to the baseline + ICER <- combined_summary %>% + # Joining the table with itself on run_id and year to compare each intervention against the baseline + inner_join(combined_summary, by = c("run_id", "year"), suffix = c(".int", ".base")) %>% + # Filtering to compare each intervention to the baseline + filter(intervention.int != intervention.base, intervention.base == baseline_intervention) %>% + # Calculating ICER + mutate(ICER = (mean_total_boost.int - mean_total_boost.base) / (mean_QALYs.int - mean_QALYs.base)) %>% + # Selecting relevant columns for the final output + select(run_id, year, intervention = intervention.int, ICER) # No intervention in first year so can't calculate ICER #TODO: Find start year automatically @@ -434,7 +706,7 @@ ICER <- function(base, base.name, int, int.name, QALY_value, int.label, start.ye geom_line(color = 'black') + geom_hline(yintercept = 0, linetype = 'dashed') + scale_y_continuous(label = comma) + - labs(title = 'ICER - Full', subtitle = paste0(int.name, ' vs ', base.name)) + labs(title = 'ICER - Full') print(p1) pc1 <- quantile(ICER$ICER, .01) @@ -443,7 +715,7 @@ ICER <- function(base, base.name, int, int.name, QALY_value, int.label, start.ye pc99 <- quantile(ICER$ICER, .99) # print(pc99) # print(max(ICER$ICER)) - + # ICER <- filter(ICER, ICER < pc99, ICER > pc1) # print(min(ICER$ICER)) # print(max(ICER$ICER)) @@ -459,7 +731,7 @@ ICER <- function(base, base.name, int, int.name, QALY_value, int.label, start.ye geom_line(color = 'black') + geom_hline(yintercept = 0, linetype = 'dashed') + scale_y_continuous(label = comma) + - labs(title = 'ICER - Outliers Removed', subtitle = paste0(int.name, ' vs ', base.name)) + labs(title = 'ICER - Outliers Removed') print(p2) ICER.final <- ICER %>% @@ -473,11 +745,12 @@ ICER <- function(base, base.name, int, int.name, QALY_value, int.label, start.ye geom_col() + geom_errorbar(aes(ymin = ICER - margin, ymax = ICER + margin, width = 0.4, group = intervention)) + scale_y_continuous(label = comma) + - labs(title = 'ICER', subtitle = paste0(int.name, ' vs ', base.name)) + + labs(title = 'ICER') + xlab('Intervention') print(p3) print(paste0('ICER == ', format(ICER.final$ICER, big.mark = ',', trim = TRUE), ' +- ', format(ICER.final$margin, big.mark = ',', trim = TRUE))) + } ICER.smooth <- function(base, base.name, int, int.name, QALY_value, int.label) { From 39082b0c44494cd877cab23d3c165b3ae22b6d3e Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 12 Mar 2024 14:53:09 +0000 Subject: [PATCH 189/229] Added combined targets for visualising multiple QALY outcomes together --- minos/outcomes/QALY.Makefile | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/minos/outcomes/QALY.Makefile b/minos/outcomes/QALY.Makefile index 6bf1898a..ffee9d8c 100644 --- a/minos/outcomes/QALY.Makefile +++ b/minos/outcomes/QALY.Makefile @@ -251,3 +251,13 @@ QALYs_UC: QALY_vis_SCP_25_UC QALY_vis_SCP_50_UC QALY_vis_SCP_100_UC QALYs_UC_glasgow: EXPERIMENT=glasgow_scaled QALYs_UC_glasgow: STARTYEAR=2020 QALYs_UC_glasgow: QALY_vis_SCP_25_UC QALY_vis_SCP_50_UC QALY_vis_SCP_100_UC + +QALY_vis_SCP_all_child_glasgow: EXPERIMENT=glasgow_scaled +QALY_vis_SCP_all_child_glasgow: STARTYEAR=2020 +QALY_vis_SCP_all_child_glasgow: QALY_baseline QALY_SCP_25_All QALY_SCP_50_All QALY_SCP_100_All + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention=c('25All', '50All', '100All', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_all_child.html')" + +QALY_vis_SCP_UC_glasgow: EXPERIMENT=glasgow_scaled +QALY_vis_SCP_UC_glasgow: STARTYEAR=2020 +QALY_vis_SCP_UC_glasgow: QALY_baseline QALY_SCP_25_UC QALY_SCP_50_UC QALY_SCP_100_UC + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention=c('25UniversalCredit', '50UniversalCredit', '100UniversalCredit', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_all_child.html')" From a09d91e29cbb9608131a21cac494b94c389c0ca0 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 12 Mar 2024 15:02:54 +0000 Subject: [PATCH 190/229] =?UTF-8?q?Fixed=20typo=20in=20new=20QALY=20vis=20?= =?UTF-8?q?targets,=20and=20added=20some=20more=20for=20visualising=20both?= =?UTF-8?q?=20all=20child=20and=20UC=20interventions=20for=20a=20single=20?= =?UTF-8?q?value=20(i.e.=20=C2=A325)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- minos/outcomes/QALY.Makefile | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/minos/outcomes/QALY.Makefile b/minos/outcomes/QALY.Makefile index ffee9d8c..bf94705a 100644 --- a/minos/outcomes/QALY.Makefile +++ b/minos/outcomes/QALY.Makefile @@ -255,9 +255,24 @@ QALYs_UC_glasgow: QALY_vis_SCP_25_UC QALY_vis_SCP_50_UC QALY_vis_SCP_100_UC QALY_vis_SCP_all_child_glasgow: EXPERIMENT=glasgow_scaled QALY_vis_SCP_all_child_glasgow: STARTYEAR=2020 QALY_vis_SCP_all_child_glasgow: QALY_baseline QALY_SCP_25_All QALY_SCP_50_All QALY_SCP_100_All - $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention=c('25All', '50All', '100All', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_all_child.html')" + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention=c('25All', '50All', '100All'), start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_all_child.html')" QALY_vis_SCP_UC_glasgow: EXPERIMENT=glasgow_scaled QALY_vis_SCP_UC_glasgow: STARTYEAR=2020 QALY_vis_SCP_UC_glasgow: QALY_baseline QALY_SCP_25_UC QALY_SCP_50_UC QALY_SCP_100_UC - $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention=c('25UniversalCredit', '50UniversalCredit', '100UniversalCredit', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_all_child.html')" + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention=c('25UniversalCredit', '50UniversalCredit', '100UniversalCredit'), start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_UC.html')" + +QALY_vis_SCP_25_glasgow: EXPERIMENT=glasgow_scaled +QALY_vis_SCP_25_glasgow: STARTYEAR=2020 +QALY_vis_SCP_25_glasgow: QALY_baseline QALY_SCP_25_All QALY_SCP_25_UC + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention=c('25UniversalCredit', '25All'), start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_25.html')" + +QALY_vis_SCP_50_glasgow: EXPERIMENT=glasgow_scaled +QALY_vis_SCP_50_glasgow: STARTYEAR=2020 +QALY_vis_SCP_50_glasgow: QALY_baseline QALY_SCP_50_All QALY_SCP_50_UC + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention=c('50UniversalCredit', '50All'), start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_50.html')" + +QALY_vis_SCP_100_glasgow: EXPERIMENT=glasgow_scaled +QALY_vis_SCP_100_glasgow: STARTYEAR=2020 +QALY_vis_SCP_100_glasgow: QALY_baseline QALY_SCP_100_All QALY_SCP_100_UC + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention=c('100UniversalCredit', '100All'), start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_100.html')" From b8c613cb648b26bea7c67b5fb0428f71b9606489 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 12 Mar 2024 16:01:41 +0000 Subject: [PATCH 191/229] Forgot to remove the debug parameters... --- minos/outcomes/QALY_comparison2.Rmd | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/minos/outcomes/QALY_comparison2.Rmd b/minos/outcomes/QALY_comparison2.Rmd index 572382ea..604b138c 100644 --- a/minos/outcomes/QALY_comparison2.Rmd +++ b/minos/outcomes/QALY_comparison2.Rmd @@ -47,16 +47,16 @@ source(here::here('minos', 'utils_qaly.R')) ## Parameters ```{r} -# experiment <- params$experiment -# baseline <- params$base -# intervention <- params$intervention -# #intervention2 <- params$intervention2 -# start.year <- params$start.year +experiment <- params$experiment +baseline <- params$base +intervention <- params$intervention +#intervention2 <- params$intervention2 +start.year <- params$start.year # # -experiment <- 'glasgow_scaled' -baseline <- 'baseline' -ints <- c('50All', '50UniversalCredit') -start.year <- 2020 +# experiment <- 'glasgow_scaled' +# baseline <- 'baseline' +# ints <- c('50All', '50UniversalCredit') +# start.year <- 2020 ``` From 00de15d91a557afcef5e1f1c59f7b6336f6841c4 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 12 Mar 2024 16:17:57 +0000 Subject: [PATCH 192/229] =?UTF-8?q?Fixed=20one=20final=20typo=20in=20QALY?= =?UTF-8?q?=5Fcomparison2.Rmd=20and=20added=20=C2=A375=20interventions=20t?= =?UTF-8?q?o=20the=20combined=20qaly=5FSCP=20run=20targets?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- minos/outcomes/QALY_comparison2.Rmd | 2 +- scripts/SCP.Makefile | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/minos/outcomes/QALY_comparison2.Rmd b/minos/outcomes/QALY_comparison2.Rmd index 604b138c..ae5d8492 100644 --- a/minos/outcomes/QALY_comparison2.Rmd +++ b/minos/outcomes/QALY_comparison2.Rmd @@ -49,7 +49,7 @@ source(here::here('minos', 'utils_qaly.R')) ```{r} experiment <- params$experiment baseline <- params$base -intervention <- params$intervention +ints <- params$intervention #intervention2 <- params$intervention2 start.year <- params$start.year # # diff --git a/scripts/SCP.Makefile b/scripts/SCP.Makefile index 72ee5ebc..4d10fb39 100644 --- a/scripts/SCP.Makefile +++ b/scripts/SCP.Makefile @@ -215,11 +215,11 @@ arc4_all_25_uplifts: arc4_baseline arc4_intervention_25RelativePoverty arc4_inte arc4_qaly_SCPs: setup arc4_qaly_SCPs: MODE=default_config arc4_qaly_SCPs: RUN_CONFIG=$(CONFIG)/default.yaml -arc4_qaly_SCPs: arc4_baseline arc4_intervention_25All arc4_intervention_50All arc4_intervention_100All -arc4_qaly_SCPs: arc4_intervention_25UniversalCredit arc4_intervention_50UniversalCredit arc4_intervention_100UniversalCredit +arc4_qaly_SCPs: arc4_baseline arc4_intervention_25All arc4_intervention_50All arc4_intervention_75All arc4_intervention_100All +arc4_qaly_SCPs: arc4_intervention_25UniversalCredit arc4_intervention_50UniversalCredit arc4_intervention_75UniversalCredit arc4_intervention_100UniversalCredit arc4_qaly_SCPs_Glasgow: setup_glasgow_scaled arc4_qaly_SCPs_Glasgow: MODE=glasgow_scaled arc4_qaly_SCPs_Glasgow: RUN_CONFIG=$(CONFIG)/glasgow_scaled.yaml -arc4_qaly_SCPs_Glasgow: arc4_baseline arc4_intervention_25All arc4_intervention_50All arc4_intervention_100All -arc4_qaly_SCPs_Glasgow: arc4_intervention_25UniversalCredit arc4_intervention_50UniversalCredit arc4_intervention_100UniversalCredit +arc4_qaly_SCPs_Glasgow: arc4_baseline arc4_intervention_25All arc4_intervention_50All arc4_intervention_75All arc4_intervention_100All +arc4_qaly_SCPs_Glasgow: arc4_intervention_25UniversalCredit arc4_intervention_50UniversalCredit arc4_intervention_75UniversalCredit arc4_intervention_100UniversalCredit From c468b414a8bd2fcaf257a97d7606de1f6cc6e178 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Wed, 13 Mar 2024 10:54:58 +0000 Subject: [PATCH 193/229] Tweaked colours on one plot in QALY_comparison2.Rmd --- minos/outcomes/QALY_comparison2.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/minos/outcomes/QALY_comparison2.Rmd b/minos/outcomes/QALY_comparison2.Rmd index ae5d8492..b9e654f1 100644 --- a/minos/outcomes/QALY_comparison2.Rmd +++ b/minos/outcomes/QALY_comparison2.Rmd @@ -143,7 +143,7 @@ combined.diff <- combined %>% names_to = 'scenario', values_to = 'total_boost') -ggplot(combined.diff, aes(x = year, y = total_boost, group = scenario, fill = scenario, color = scenario)) + +ggplot(combined.diff, aes(x = year, y = total_boost, group = scenario, color = scenario)) + geom_smooth() + labs(title = 'Total Boost amount by year') + scale_y_continuous(label = comma) From ab2e4b07aed7851ff0d23990e2a90d053d6cd085 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Wed, 13 Mar 2024 11:21:27 +0000 Subject: [PATCH 194/229] =?UTF-8?q?Reduced=20the=20child=20uplift=20interv?= =?UTF-8?q?ention=20by=20=C2=A310=20for=20the=20universal=20credit=20group?= =?UTF-8?q?,=20as=20they=20are=20already=20receiving=20the=20=C2=A310=20SC?= =?UTF-8?q?P=20in=20our=20input=20populations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- minos/modules/child_poverty_interventions.py | 29 +++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/minos/modules/child_poverty_interventions.py b/minos/modules/child_poverty_interventions.py index e5518331..e0229eda 100644 --- a/minos/modules/child_poverty_interventions.py +++ b/minos/modules/child_poverty_interventions.py @@ -90,14 +90,29 @@ def on_time_step(self, event): pop = self.population_view.get(event.index, query="alive =='alive'") # print(np.mean(pop['hh_income'])) # for debugging purposes. # TODO probably a faster way to do this than resetting the whole column. - if self.uplift_condition == "who_below_poverty_line_and_kids": - pop['hh_income'] -= pop['boost_amount'] # reset boost if people move out of bottom decile. only do this for relative poverty uplift. - else: - pop['income_boosted'] = False + # if self.uplift_condition == "who_below_poverty_line_and_kids": + # pop['hh_income'] -= pop['boost_amount'] # reset boost if people move out of bottom decile. only do this for relative poverty uplift. + # else: + # pop['income_boosted'] = False + + # Reset boost vars in preparation for the new wave intervention + pop['income_boosted'] = False + pop['boost_amount'] = 0 + + # Find households to uplift from the uplift condition supplied uplifted_households = np.unique(dynamic_subset_function(pop, self.uplift_condition)['hidp']) - pop.loc[pop['hidp'].isin(uplifted_households) ,'income_boosted'] = True # set everyone who satisfies uplift condition to true. - pop['boost_amount'] = pop['income_boosted'] * get_monthly_boost_amount(pop, self.uplift_amount) # £25 per week * 30.463/7 weeks per average month * nkids. - #pop['income_boosted'] = (pop['boost_amount'] != 0) + # Households on universal credit and legacy benefits are already receiving £10 SCP in our input population + # (2020) start. Therefore we need to reduce the intervention by £10 for these people + uc_households = np.unique(dynamic_subset_function(pop, 'who_universal_credit_and_kids')['hidp']) + + pop.loc[pop['hidp'].isin(uplifted_households), 'income_boosted'] = True # set everyone who satisfies uplift condition to true. + pop['boost_amount'] = pop['income_boosted'] * get_monthly_boost_amount(pop, self.uplift_amount) # £25 per week * 30.463/7 weeks per average month * nkids. + + # reduce boost amount by £10 for those on universal credit as they are already receiving £10 + pop.loc[pop['hidp'].isin( + uc_households), 'boost_amount'] = pop['boost_amount'] - 10 + + # pop['income_boosted'] = (pop['boost_amount'] != 0) pop['hh_income'] += pop['boost_amount'] # print(np.mean(pop['hh_income'])) # for debugging. # TODO some kind of heterogeneity for people in the same household..? general inclusion of houshold compositon. From fdc2946b27af94eaa2cf8d7683cdfbd03e14ffaf Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Wed, 13 Mar 2024 12:03:19 +0000 Subject: [PATCH 195/229] =?UTF-8?q?Added=20targets=20for=20running=20SCP?= =?UTF-8?q?=20interventions=20from=20=C2=A325=20to=20=C2=A3120=20in=20?= =?UTF-8?q?=C2=A35=20increments=20for=20universal=20credit=20group.=20Also?= =?UTF-8?q?=20increased=20memory=20and=20reduced=20runtime=20in=20the=20ar?= =?UTF-8?q?c=5Frun.sh=20submission=20script?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/SCP.Makefile | 60 ++++++++++++++++++++++++++++++++++++-------- scripts/arc_run.sh | 4 +-- 2 files changed, 52 insertions(+), 12 deletions(-) diff --git a/scripts/SCP.Makefile b/scripts/SCP.Makefile index 4d10fb39..fcbcf548 100644 --- a/scripts/SCP.Makefile +++ b/scripts/SCP.Makefile @@ -125,10 +125,10 @@ arc4_intervention_25RelativePoverty: arc4_intervention_50RelativePoverty: bash scripts/arc_submit.sh -c $(RUN_CONFIG) -o $(MODE) -i '50RelativePoverty' -arc4_intervention_75RelativePoverty: setup +arc4_intervention_75RelativePoverty: bash scripts/arc_submit.sh -c $(RUN_CONFIG) -o $(MODE) -i '75RelativePoverty' -arc4_intervention_100RelativePoverty: setup +arc4_intervention_100RelativePoverty: bash scripts/arc_submit.sh -c $(RUN_CONFIG) -o $(MODE) -i '100RelativePoverty' ########################################################################## @@ -148,16 +148,51 @@ arc4_intervention_40UniversalCredit: arc4_intervention_45UniversalCredit: bash scripts/arc_submit.sh -c $(RUN_CONFIG) -o $(MODE) -i '45UniversalCredit' -arc4_intervention_50UniversalCredit: arc4_intervention_50UniversalCredit: bash scripts/arc_submit.sh -c $(RUN_CONFIG) -o $(MODE) -i '50UniversalCredit' -arc4_intervention_75UniversalCredit: setup +arc4_intervention_55UniversalCredit: + bash scripts/arc_submit.sh -c $(RUN_CONFIG) -o $(MODE) -i '55UniversalCredit' + +arc4_intervention_60UniversalCredit: + bash scripts/arc_submit.sh -c $(RUN_CONFIG) -o $(MODE) -i '60UniversalCredit' + +arc4_intervention_65UniversalCredit: + bash scripts/arc_submit.sh -c $(RUN_CONFIG) -o $(MODE) -i '65UniversalCredit' + +arc4_intervention_70UniversalCredit: + bash scripts/arc_submit.sh -c $(RUN_CONFIG) -o $(MODE) -i '70UniversalCredit' + +arc4_intervention_75UniversalCredit: bash scripts/arc_submit.sh -c $(RUN_CONFIG) -o $(MODE) -i '75UniversalCredit' -arc4_intervention_100UniversalCredit: setup +arc4_intervention_80UniversalCredit: + bash scripts/arc_submit.sh -c $(RUN_CONFIG) -o $(MODE) -i '80UniversalCredit' + +arc4_intervention_85UniversalCredit: + bash scripts/arc_submit.sh -c $(RUN_CONFIG) -o $(MODE) -i '85UniversalCredit' + +arc4_intervention_90UniversalCredit: + bash scripts/arc_submit.sh -c $(RUN_CONFIG) -o $(MODE) -i '90UniversalCredit' + +arc4_intervention_95UniversalCredit: + bash scripts/arc_submit.sh -c $(RUN_CONFIG) -o $(MODE) -i '95UniversalCredit' + +arc4_intervention_100UniversalCredit: bash scripts/arc_submit.sh -c $(RUN_CONFIG) -o $(MODE) -i '100UniversalCredit' +arc4_intervention_105UniversalCredit: + bash scripts/arc_submit.sh -c $(RUN_CONFIG) -o $(MODE) -i '105UniversalCredit' + +arc4_intervention_110UniversalCredit: + bash scripts/arc_submit.sh -c $(RUN_CONFIG) -o $(MODE) -i '110UniversalCredit' + +arc4_intervention_115UniversalCredit: + bash scripts/arc_submit.sh -c $(RUN_CONFIG) -o $(MODE) -i '115UniversalCredit' + +arc4_intervention_120UniversalCredit: + bash scripts/arc_submit.sh -c $(RUN_CONFIG) -o $(MODE) -i '120UniversalCredit' + ########################################################################## arc4_intervention_25Priority: @@ -218,8 +253,13 @@ arc4_qaly_SCPs: RUN_CONFIG=$(CONFIG)/default.yaml arc4_qaly_SCPs: arc4_baseline arc4_intervention_25All arc4_intervention_50All arc4_intervention_75All arc4_intervention_100All arc4_qaly_SCPs: arc4_intervention_25UniversalCredit arc4_intervention_50UniversalCredit arc4_intervention_75UniversalCredit arc4_intervention_100UniversalCredit -arc4_qaly_SCPs_Glasgow: setup_glasgow_scaled -arc4_qaly_SCPs_Glasgow: MODE=glasgow_scaled -arc4_qaly_SCPs_Glasgow: RUN_CONFIG=$(CONFIG)/glasgow_scaled.yaml -arc4_qaly_SCPs_Glasgow: arc4_baseline arc4_intervention_25All arc4_intervention_50All arc4_intervention_75All arc4_intervention_100All -arc4_qaly_SCPs_Glasgow: arc4_intervention_25UniversalCredit arc4_intervention_50UniversalCredit arc4_intervention_75UniversalCredit arc4_intervention_100UniversalCredit +arc4_qaly_SCPs_glasgow: setup_glasgow_scaled +arc4_qaly_SCPs_glasgow: MODE=glasgow_scaled +arc4_qaly_SCPs_glasgow: RUN_CONFIG=$(CONFIG)/glasgow_scaled.yaml +arc4_qaly_SCPs_glasgow: arc4_intervention_25UniversalCredit arc4_intervention_30UniversalCredit arc4_intervention_35UniversalCredit +arc4_qaly_SCPs_glasgow: arc4_intervention_40UniversalCredit arc4_intervention_45UniversalCredit arc4_intervention_50UniversalCredit +arc4_qaly_SCPs_glasgow: arc4_intervention_55UniversalCredit arc4_intervention_60UniversalCredit arc4_intervention_65UniversalCredit +arc4_qaly_SCPs_glasgow: arc4_intervention_70UniversalCredit arc4_intervention_75UniversalCredit arc4_intervention_80UniversalCredit +arc4_qaly_SCPs_glasgow: arc4_intervention_85UniversalCredit arc4_intervention_90UniversalCredit arc4_intervention_95UniversalCredit +arc4_qaly_SCPs_glasgow: arc4_intervention_100UniversalCredit arc4_intervention_105UniversalCredit arc4_intervention_110UniversalCredit +arc4_qaly_SCPs_glasgow: arc4_intervention_115UniversalCredit arc4_intervention_120UniversalCredit diff --git a/scripts/arc_run.sh b/scripts/arc_run.sh index e7c05e68..a5fe1ab3 100755 --- a/scripts/arc_run.sh +++ b/scripts/arc_run.sh @@ -7,11 +7,11 @@ ## Use current environment variables and modules #$ -V ## Request hours of runtime -#$ -l h_rt=48:00:00 +#$ -l h_rt=12:00:00 ## Email if a run aborts #$ -m a ## Select memory -#$ -l h_vmem=18G # was 15 for big runs +#$ -l h_vmem=22G # was 15 for big runs ## Choose cores. See arc website for more details. 5 high memory cores chosen here. #$ -pe smp 1 ## Set logs directories From bc8473cb0a176d355a2d312adb08c51f74272112 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Wed, 13 Mar 2024 12:24:19 +0000 Subject: [PATCH 196/229] Saving the local_batch.sh file --- scripts/local_batch.sh | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 scripts/local_batch.sh diff --git a/scripts/local_batch.sh b/scripts/local_batch.sh new file mode 100644 index 00000000..8fdcec19 --- /dev/null +++ b/scripts/local_batch.sh @@ -0,0 +1,30 @@ + +NUMRUNS=10 +declare -a intlist=(50All) + +TIME=$(date +%Y_%m_%d_%H_%M_%S) + +CONF=glasgow_scaled +OUTPUT_SUBDIR=glasgow_scaled + +echo "Running $NUMRUNS runs..." +echo "Starting with baseline..." + +## Doing the baseline runs +for i in $(seq 1 $NUMRUNS); +do + echo "Starting run #$i for the baseline scenario..." + python3 scripts/run.py -c config/$CONF.yaml -o $OUTPUT_SUBDIR -t "$TIME" -r "$i" +done + +## Now intervention runs +for scen in "${intlist[@]}"; +do + for i in $(seq 1 $NUMRUNS); + do + echo "Starting run #$i for the $scen scenario..." + python3 scripts/run.py -c config/$CONF.yaml -o $OUTPUT_SUBDIR -i "$scen" -t "$TIME" -r "$i" + done +done + +echo "COMPLETED A LOCAL BATCH RUN." \ No newline at end of file From f245e88514601ee652c093b69c51cd259fde090d Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Wed, 13 Mar 2024 15:31:50 +0000 Subject: [PATCH 197/229] Small fixes to get the new changes from branch 400 working here. Also small changes with the random draw from parameter estimates --- minos/modules/heating.py | 6 +++++- minos/modules/housing_tenure.py | 5 ----- minos/modules/mental_wellbeing.py | 24 +++--------------------- minos/modules/physical_wellbeing.py | 13 +++++++------ minos/transitions/estimate_transitions.R | 23 +++++++++++++---------- 5 files changed, 28 insertions(+), 43 deletions(-) diff --git a/minos/modules/heating.py b/minos/modules/heating.py index 7273fd5e..2c7d3f6a 100644 --- a/minos/modules/heating.py +++ b/minos/modules/heating.py @@ -114,7 +114,11 @@ def calculate_heating(self, pop): ------- """ # load transition model based on year. - year = 2019 + if self.cross_validation: + # if cross-val, fix year to final year model + year = 2018 + else: + year = min(self.year, 2019) #transition_model = r_utils.load_transitions(f"heating/logit/heating_{year}_{year+1}", self.rpy2Modules) if not self.transition_model or year <= 2020: self.transition_model = r_utils.load_transitions(f"heating/logit/heating_{year}_{year+1}", diff --git a/minos/modules/housing_tenure.py b/minos/modules/housing_tenure.py index 3b496601..42e2bcbc 100644 --- a/minos/modules/housing_tenure.py +++ b/minos/modules/housing_tenure.py @@ -142,11 +142,6 @@ def calculate_housing_tenure(self, pop): - if not self.transition_model or year <= 2020: - self.transition_model = r_utils.load_transitions(f"housing_tenure/nnet/housing_tenure_{year}_{year+1}", self.rpy2Modules, path=self.transition_dir) - # TODO not implemented for nnet. - #self.transition_model = r_utils.randomise_fixed_effects(self.transition_model, self.rpy2Modules, "nnet") - #transition_model = r_utils.load_transitions(f"housing_tenure/nnet/housing_tenure_{year}_{year+1}", # self.rpy2Modules, diff --git a/minos/modules/mental_wellbeing.py b/minos/modules/mental_wellbeing.py index 619bcc0a..a72df4ce 100755 --- a/minos/modules/mental_wellbeing.py +++ b/minos/modules/mental_wellbeing.py @@ -253,11 +253,7 @@ def setup(self, builder): self.max_sf12 = None #only need to load this once for now. -<<<<<<< HEAD - self.gee_transition_model = r_utils.load_transitions(f"SF_12/gee/SF_12_GEE", self.rpy2Modules) -======= self.gee_transition_model = r_utils.load_transitions(f"SF_12_MCS/gee/SF_12_MCS_GEE", self.rpy2_modules) ->>>>>>> 231-SF12_PCS def update_prediction_population(self, current_pop): """ Update longitudinal data frame of past observations with current information. @@ -314,11 +310,7 @@ def calculate_mwb(self, pop): ------- """ year = min(self.year, 2018) -<<<<<<< HEAD - return self.max_sf12 - r_utils.predict_next_timestep_gee(self.gee_transition_model, self.rpy2Modules, pop, 'SF_12') -======= return self.max_sf12_MCS - r_utils.predict_next_timestep_gee(self.gee_transition_model, self.rpy2_modules, pop, 'SF_12_MCS') ->>>>>>> 231-SF12_PCS @@ -390,13 +382,8 @@ def setup(self, builder): super().setup(builder) #only need to load this once for now. -<<<<<<< HEAD - self.gee_transition_model = r_utils.load_transitions(f"SF_12/gee_yj/SF_12_GEE_YJ", self.rpy2Modules, path=self.transition_dir) - #self.gee_transition_model = r_utils.load_transitions(f"SF_12/gee_yj_gamma/SF_12_GEE_YJ_GAMMA", self.rpy2Modules, path=self.transition_dir) -======= self.gee_transition_model = r_utils.load_transitions(f"SF_12_MCS/gee_yj/SF_12_MCS_GEE_YJ", self.rpy2_modules, path=self.transition_dir) #self.gee_transition_model = r_utils.load_transitions(f"SF_12/gee_yj_gamma/SF_12_GEE_YJ_GAMMA", self.rpy2_modules, path=self.transition_dir) ->>>>>>> 231-SF12_PCS self.history_data = self.generate_history_dataframe("final_US", [2014, 2017, 2020], view_columns) def on_time_step(self, event): @@ -514,14 +501,9 @@ def setup(self, builder): super().setup(builder) #only need to load this once for now. -<<<<<<< HEAD - #self.gee_transition_model = r_utils.load_transitions(f"SF_12/lmm/SF_12_LMM", self.rpy2Modules, path=self.transition_dir) - self.transition_model = r_utils.load_transitions(f"SF_12/glmm/SF_12_GLMM", self.rpy2Modules, path=self.transition_dir) - self.transition_model = r_utils.randomise_fixed_effects(self.transition_model, self.rpy2Modules, "glmm") -======= - self.lmm_transition_model = r_utils.load_transitions(f"SF_12_MCS/lmm/SF_12_MCS_LMM", self.rpy2_modules, path=self.transition_dir) + self.lmm_transition_model = r_utils.load_transitions(f"SF_12_MCS/lmm/SF_12_MCS_LMM", self.rpy2Modules, path=self.transition_dir) + self.lmm_transition_model = r_utils.randomise_fixed_effects(self.lmm_transition_model, self.rpy2Modules, "lmm") #self.gee_transition_model = r_utils.load_transitions(f"SF_12_MCS/glmm/SF_12_MCS_GLMM", self.rpy2_modules, path=self.transition_dir) ->>>>>>> 231-SF12_PCS def on_time_step(self, event): """Produces new children and updates parent status on time steps. @@ -575,7 +557,7 @@ def calculate_mwb(self, pop): # noise_std= 0.1) # 5 for non yj, 0.35 for yj nextWaveMWB = r_utils.predict_next_timestep_yj_gaussian_lmm(self.lmm_transition_model, - self.rpy2_modules, + self.rpy2Modules, pop, dependent='SF_12_MCS', log_transform=True, diff --git a/minos/modules/physical_wellbeing.py b/minos/modules/physical_wellbeing.py index dc7a33d4..bed70a51 100644 --- a/minos/modules/physical_wellbeing.py +++ b/minos/modules/physical_wellbeing.py @@ -162,7 +162,7 @@ def setup(self, builder): """ # Load in inputs from pre-setup. - self.rpy2_modules = builder.data.load("rpy2_modules") + self.rpy2Modules = builder.data.load("rpy2_modules") # Build vivarium objects for calculating transition probabilities. # Typically this is registering rate/lookup tables. See vivarium docs/other modules for examples. @@ -219,8 +219,9 @@ def setup(self, builder): # path=self.transition_dir) #self.glmmb_transition_model = r_utils.load_transitions(f"SF_12_PCS/glmmb/SF_12_PCS_GLMMB", self.rpy2_modules, # path=self.transition_dir) - self.lmm_transition_model = r_utils.load_transitions(f"SF_12_PCS/lmm/SF_12_PCS_LMM", self.rpy2_modules, + self.lmm_transition_model = r_utils.load_transitions(f"SF_12_PCS/lmm/SF_12_PCS_LMM", self.rpy2Modules, path=self.transition_dir) + self.lmm_transition_model = r_utils.randomise_fixed_effects(self.lmm_transition_model, self.rpy2Modules, "lmm") def on_time_step(self, event): """Produces new children and updates parent status on time steps. @@ -274,27 +275,27 @@ def calculate_pwb(self, pop): """ # nextWavePWB = r_utils.predict_next_timestep_beta_glmm(self.glmmb_transition_model, - # self.rpy2_modules, + # self.rpy2Modules, # pop, # dependent='SF_12_PCS', # reflect=True, # noise_std=0.03) # nextWavePWB = r_utils.predict_next_rf(self.rf_transition_model, - # self.rpy2_modules, + # self.rpy2Modules, # pop, # dependent='SF_12_PCS', # noise_std=1) nextWavePWB = r_utils.predict_next_timestep_yj_gaussian_lmm(self.lmm_transition_model, - self.rpy2_modules, + self.rpy2Modules, pop, dependent='SF_12_PCS', log_transform=True, noise_std=2) # # nextWavePWB = r_utils.predict_next_timestep_yj_gamma_glmm(self.gee_transition_model, - # self.rpy2_modules, + # self.rpy2Modules, # current=pop, # dependent='SF_12_PCS', # reflect=True, diff --git a/minos/transitions/estimate_transitions.R b/minos/transitions/estimate_transitions.R index 345505e9..1c7e6739 100644 --- a/minos/transitions/estimate_transitions.R +++ b/minos/transitions/estimate_transitions.R @@ -50,7 +50,7 @@ digest_params <- function(line) { run_yearly_models <- function(transitionDir_path, transitionSourceDir_path, mod_def_name, - data, + orig_data, mode) { ## Read in model definitions from file including formula and model type (OLS,CLM,etc.) @@ -58,19 +58,19 @@ run_yearly_models <- function(transitionDir_path, modDefs <- file(description = modDef_path, open="r", blocking = TRUE) ## Set some factor levels because R defaults to using alphabetical ordering - data$housing_quality <- factor(data$housing_quality, + orig_data$housing_quality <- factor(orig_data$housing_quality, levels = c('Low', 'Medium', 'High')) - data$S7_housing_quality <- factor(data$S7_housing_quality, + orig_data$S7_housing_quality <- factor(orig_data$S7_housing_quality, levels = c('No to all', 'Yes to some', 'Yes to all')) - data$S7_neighbourhood_safety <- factor(data$S7_neighbourhood_safety, + orig_data$S7_neighbourhood_safety <- factor(orig_data$S7_neighbourhood_safety, levels = c('Often', 'Some of the time', 'Hardly ever')) - data$S7_labour_state <- factor(data$S7_labour_state, + orig_data$S7_labour_state <- factor(orig_data$S7_labour_state, levels = c('FT Employed', 'PT Employed', 'Job Seeking', @@ -84,7 +84,11 @@ run_yearly_models <- function(transitionDir_path, 'High Risk')) # read file - repeat{ + repeat { + + # take copy of original data so we can transform without worry + data <- orig_data + def = readLines(modDefs, n = 1) # Read one line from the connection. if(identical(def, character(0))){break} # If the line is empty, exit. if(startsWith(def, '#')){next} # If line starts with '#', line is comment and should be ignored @@ -136,11 +140,10 @@ run_yearly_models <- function(transitionDir_path, valid_yearly_model_types = c("NNET", "OLS", "OLS_DIFF", "CLM", "GLM", "ZIP", "LOGIT", "OLS_YJ") for(year in year.range) { - if(!is.element(mod.type, valid_yearly_model_types)) - { + if(!is.element(mod.type, valid_yearly_model_types)) { print(paste0("WARNING. model ", paste0(mod.type, " not valid for yearly models. Skipping.."))) next - }# skip this iteration if model not in valid types. + } # skip this iteration if model not in valid types. # reset the formula string for each year formula.string <- formula.string.orig @@ -296,7 +299,7 @@ run_yearly_models <- function(transitionDir_path, #write_csv(coefs, file = coef.filepath) # writing tex table of coefficients. easy writing for papers and documentation. write_coefs <- T - if (write_coefs & tolower(mod.type) != "nnet") # cant write coefs for nnet using texreg. + if (write_coefs & tolower(mod.type) != "nnet") { #  cant write coefs for nnet using texreg. create.if.not.exists("data/transitions/coefficients") texreg_file <- paste0("data/transitions/coefficients/", dependent, '_', year, '_', depend.year, '.txt') texreg(model, file=texreg_file, stars = c(0.001, 0.01, 0.05, 0.1), digits=4, dcolumn=T, tabular=T) From b101680e2af39eae8c507c90ad17eec9dd0ab7c2 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Wed, 13 Mar 2024 15:37:09 +0000 Subject: [PATCH 198/229] Added baseline to a combined arc run target, and renamed for more clarity --- scripts/SCP.Makefile | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/scripts/SCP.Makefile b/scripts/SCP.Makefile index fcbcf548..f04c4361 100644 --- a/scripts/SCP.Makefile +++ b/scripts/SCP.Makefile @@ -253,13 +253,14 @@ arc4_qaly_SCPs: RUN_CONFIG=$(CONFIG)/default.yaml arc4_qaly_SCPs: arc4_baseline arc4_intervention_25All arc4_intervention_50All arc4_intervention_75All arc4_intervention_100All arc4_qaly_SCPs: arc4_intervention_25UniversalCredit arc4_intervention_50UniversalCredit arc4_intervention_75UniversalCredit arc4_intervention_100UniversalCredit -arc4_qaly_SCPs_glasgow: setup_glasgow_scaled -arc4_qaly_SCPs_glasgow: MODE=glasgow_scaled -arc4_qaly_SCPs_glasgow: RUN_CONFIG=$(CONFIG)/glasgow_scaled.yaml -arc4_qaly_SCPs_glasgow: arc4_intervention_25UniversalCredit arc4_intervention_30UniversalCredit arc4_intervention_35UniversalCredit -arc4_qaly_SCPs_glasgow: arc4_intervention_40UniversalCredit arc4_intervention_45UniversalCredit arc4_intervention_50UniversalCredit -arc4_qaly_SCPs_glasgow: arc4_intervention_55UniversalCredit arc4_intervention_60UniversalCredit arc4_intervention_65UniversalCredit -arc4_qaly_SCPs_glasgow: arc4_intervention_70UniversalCredit arc4_intervention_75UniversalCredit arc4_intervention_80UniversalCredit -arc4_qaly_SCPs_glasgow: arc4_intervention_85UniversalCredit arc4_intervention_90UniversalCredit arc4_intervention_95UniversalCredit -arc4_qaly_SCPs_glasgow: arc4_intervention_100UniversalCredit arc4_intervention_105UniversalCredit arc4_intervention_110UniversalCredit -arc4_qaly_SCPs_glasgow: arc4_intervention_115UniversalCredit arc4_intervention_120UniversalCredit +arc4_qaly_SCPs_UC_glasgow: setup_glasgow_scaled +arc4_qaly_SCPs_UC_glasgow: MODE=glasgow_scaled +arc4_qaly_SCPs_UC_glasgow: RUN_CONFIG=$(CONFIG)/glasgow_scaled.yaml +arc4_qaly_SCPs_UC_glasgow: arc4_baseline +arc4_qaly_SCPs_UC_glasgow: arc4_intervention_25UniversalCredit arc4_intervention_30UniversalCredit arc4_intervention_35UniversalCredit +arc4_qaly_SCPs_UC_glasgow: arc4_intervention_40UniversalCredit arc4_intervention_45UniversalCredit arc4_intervention_50UniversalCredit +arc4_qaly_SCPs_UC_glasgow: arc4_intervention_55UniversalCredit arc4_intervention_60UniversalCredit arc4_intervention_65UniversalCredit +arc4_qaly_SCPs_UC_glasgow: arc4_intervention_70UniversalCredit arc4_intervention_75UniversalCredit arc4_intervention_80UniversalCredit +arc4_qaly_SCPs_UC_glasgow: arc4_intervention_85UniversalCredit arc4_intervention_90UniversalCredit arc4_intervention_95UniversalCredit +arc4_qaly_SCPs_UC_glasgow: arc4_intervention_100UniversalCredit arc4_intervention_105UniversalCredit arc4_intervention_110UniversalCredit +arc4_qaly_SCPs_UC_glasgow: arc4_intervention_115UniversalCredit arc4_intervention_120UniversalCredit From 02e378e32a33ff017f8f7bcd63168fe1610aa76f Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Thu, 14 Mar 2024 10:12:32 +0000 Subject: [PATCH 199/229] =?UTF-8?q?Added=20lots=20of=20new=20targets=20for?= =?UTF-8?q?=20visualising=20QALY=20outcomes=20for=20SCP=20interventions=20?= =?UTF-8?q?from=20=C2=A325-=C2=A3120=20in=20=C2=A35=20increments.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- minos/outcomes/QALY.Makefile | 157 +++++++++++++++++++++++++++++++++-- 1 file changed, 150 insertions(+), 7 deletions(-) diff --git a/minos/outcomes/QALY.Makefile b/minos/outcomes/QALY.Makefile index bf94705a..f99f9fcf 100644 --- a/minos/outcomes/QALY.Makefile +++ b/minos/outcomes/QALY.Makefile @@ -181,21 +181,101 @@ QALY_SCP_25_UC: INTERVENTION=25UniversalCredit QALY_SCP_25_UC: python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) +QALY_SCP_30_UC: MODE=$(EXPERIMENT) +QALY_SCP_30_UC: INTERVENTION=30UniversalCredit +QALY_SCP_30_UC: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_SCP_35_UC: MODE=$(EXPERIMENT) +QALY_SCP_35_UC: INTERVENTION=35UniversalCredit +QALY_SCP_35_UC: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_SCP_40_UC: MODE=$(EXPERIMENT) +QALY_SCP_40_UC: INTERVENTION=40UniversalCredit +QALY_SCP_40_UC: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_SCP_45_UC: MODE=$(EXPERIMENT) +QALY_SCP_45_UC: INTERVENTION=45UniversalCredit +QALY_SCP_45_UC: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + QALY_SCP_50_UC: MODE=$(EXPERIMENT) QALY_SCP_50_UC: INTERVENTION=50UniversalCredit QALY_SCP_50_UC: python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) +QALY_SCP_55_UC: MODE=$(EXPERIMENT) +QALY_SCP_55_UC: INTERVENTION=55UniversalCredit +QALY_SCP_55_UC: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_SCP_60_UC: MODE=$(EXPERIMENT) +QALY_SCP_60_UC: INTERVENTION=60UniversalCredit +QALY_SCP_60_UC: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_SCP_65_UC: MODE=$(EXPERIMENT) +QALY_SCP_65_UC: INTERVENTION=65UniversalCredit +QALY_SCP_65_UC: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_SCP_70_UC: MODE=$(EXPERIMENT) +QALY_SCP_70_UC: INTERVENTION=70UniversalCredit +QALY_SCP_70_UC: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + QALY_SCP_75_UC: MODE=$(EXPERIMENT) QALY_SCP_75_UC: INTERVENTION=75UniversalCredit QALY_SCP_75_UC: python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) +QALY_SCP_80_UC: MODE=$(EXPERIMENT) +QALY_SCP_80_UC: INTERVENTION=80UniversalCredit +QALY_SCP_80_UC: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_SCP_85_UC: MODE=$(EXPERIMENT) +QALY_SCP_85_UC: INTERVENTION=85UniversalCredit +QALY_SCP_85_UC: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_SCP_90_UC: MODE=$(EXPERIMENT) +QALY_SCP_90_UC: INTERVENTION=90UniversalCredit +QALY_SCP_90_UC: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_SCP_95_UC: MODE=$(EXPERIMENT) +QALY_SCP_95_UC: INTERVENTION=95UniversalCredit +QALY_SCP_95_UC: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + QALY_SCP_100_UC: MODE=$(EXPERIMENT) QALY_SCP_100_UC: INTERVENTION=100UniversalCredit QALY_SCP_100_UC: python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) +QALY_SCP_105_UC: MODE=$(EXPERIMENT) +QALY_SCP_105_UC: INTERVENTION=105UniversalCredit +QALY_SCP_105_UC: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_SCP_110_UC: MODE=$(EXPERIMENT) +QALY_SCP_110_UC: INTERVENTION=110UniversalCredit +QALY_SCP_110_UC: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_SCP_115_UC: MODE=$(EXPERIMENT) +QALY_SCP_115_UC: INTERVENTION=115UniversalCredit +QALY_SCP_115_UC: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + +QALY_SCP_120_UC: MODE=$(EXPERIMENT) +QALY_SCP_120_UC: INTERVENTION=120UniversalCredit +QALY_SCP_120_UC: + python minos/outcomes/QALY_calculation.py -m $(MODE) -i $(INTERVENTION) + ##################################### # VISUALISATIONS ##################################### @@ -234,33 +314,90 @@ QALY_vis_SCP_25_UC: QALY_baseline QALY_SCP_25_UC $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='25UniversalCredit', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_25_UC.html')" #firefox file://$(OUTCOMES)/QALY_SCP_25_UC.html -QALY_vis_SCP_50_UC: EXPERIMENT=glasgow_scaled -QALY_vis_SCP_50_UC: STARTYEAR=2020 +QALY_vis_SCP_30_UC: QALY_baseline QALY_SCP_30_UC + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='30UniversalCredit', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_30_UC.html')" + +QALY_vis_SCP_35_UC: QALY_baseline QALY_SCP_35_UC + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='35UniversalCredit', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_35_UC.html')" + +QALY_vis_SCP_40_UC: QALY_baseline QALY_SCP_40_UC + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='40UniversalCredit', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_30_UC.html')" + +QALY_vis_SCP_45_UC: QALY_baseline QALY_SCP_45_UC + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='45UniversalCredit', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_45_UC.html')" + QALY_vis_SCP_50_UC: QALY_baseline QALY_SCP_50_UC $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='50UniversalCredit', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_50_UC.html')" #firefox file://$(OUTCOMES)/QALY_SCP_50_UC.html +QALY_vis_SCP_55_UC: QALY_baseline QALY_SCP_55_UC + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='55UniversalCredit', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_55_UC.html')" + +QALY_vis_SCP_60_UC: QALY_baseline QALY_SCP_60_UC + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='60UniversalCredit', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_60_UC.html')" + +QALY_vis_SCP_65_UC: QALY_baseline QALY_SCP_65_UC + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='65UniversalCredit', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_65_UC.html')" + +QALY_vis_SCP_70_UC: QALY_baseline QALY_SCP_70_UC + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='70UniversalCredit', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_70_UC.html')" + +QALY_vis_SCP_75_UC: QALY_baseline QALY_SCP_75_UC + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='75UniversalCredit', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_75_UC.html')" + +QALY_vis_SCP_80_UC: QALY_baseline QALY_SCP_80_UC + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='80UniversalCredit', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_80_UC.html')" + +QALY_vis_SCP_85_UC: QALY_baseline QALY_SCP_85_UC + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='85UniversalCredit', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_85_UC.html')" + +QALY_vis_SCP_90_UC: QALY_baseline QALY_SCP_90_UC + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='90UniversalCredit', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_90_UC.html')" + +QALY_vis_SCP_95_UC: QALY_baseline QALY_SCP_95_UC + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='95UniversalCredit', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_95_UC.html')" + QALY_vis_SCP_100_UC: QALY_baseline QALY_SCP_100_UC $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='100UniversalCredit', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_100_UC.html')" #firefox file://$(OUTCOMES)/QALY_SCP_100_UC.html +QALY_vis_SCP_105_UC: QALY_baseline QALY_SCP_105_UC + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='105UniversalCredit', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_105_UC.html')" + +QALY_vis_SCP_110_UC: QALY_baseline QALY_SCP_110_UC + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='110UniversalCredit', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_110_UC.html')" + +QALY_vis_SCP_115_UC: QALY_baseline QALY_SCP_115_UC + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='115UniversalCredit', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_115_UC.html')" + +QALY_vis_SCP_120_UC: QALY_baseline QALY_SCP_120_UC + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='120UniversalCredit', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_120_UC.html')" + QALYs_UC: EXPERIMENT=default_config QALYs_UC: STARTYEAR=2021 -QALYs_UC: QALY_vis_SCP_25_UC QALY_vis_SCP_50_UC QALY_vis_SCP_100_UC +QALYs_UC: QALY_vis_SCP_25_UC QALY_vis_SCP_50_UC QALY_vis_SCP_75_UC QALY_vis_SCP_100_UC QALYs_UC_glasgow: EXPERIMENT=glasgow_scaled QALYs_UC_glasgow: STARTYEAR=2020 -QALYs_UC_glasgow: QALY_vis_SCP_25_UC QALY_vis_SCP_50_UC QALY_vis_SCP_100_UC +QALYs_UC_glasgow: QALY_vis_SCP_25_UC QALY_vis_SCP_50_UC QALY_vis_SCP_75_UC QALY_vis_SCP_100_UC QALY_vis_SCP_all_child_glasgow: EXPERIMENT=glasgow_scaled QALY_vis_SCP_all_child_glasgow: STARTYEAR=2020 QALY_vis_SCP_all_child_glasgow: QALY_baseline QALY_SCP_25_All QALY_SCP_50_All QALY_SCP_100_All - $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention=c('25All', '50All', '100All'), start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_all_child.html')" + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention=c('25All', '50All', '75All', '100All'), start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_all_child.html')" QALY_vis_SCP_UC_glasgow: EXPERIMENT=glasgow_scaled QALY_vis_SCP_UC_glasgow: STARTYEAR=2020 -QALY_vis_SCP_UC_glasgow: QALY_baseline QALY_SCP_25_UC QALY_SCP_50_UC QALY_SCP_100_UC - $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention=c('25UniversalCredit', '50UniversalCredit', '100UniversalCredit'), start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_UC.html')" +QALY_vis_SCP_UC_glasgow: QALY_baseline QALY_SCP_25_UC QALY_SCP_50_UC QALY_SCP_75_UC QALY_SCP_100_UC + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention=c('25UniversalCredit', '50UniversalCredit', '75UniversalCredit', '100UniversalCredit'), start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_UC.html')" + +QALY_vis_SCP_UC_glasgow_ALL: EXPERIMENT=glasgow_scaled +QALY_vis_SCP_UC_glasgow_ALL: STARTYEAR=2020 +QALY_vis_SCP_UC_glasgow_ALL: QALY_baseline QALY_SCP_25_UC QALY_SCP_30_UC QALY_SCP_35_UC QALY_SCP_40_UC QALY_SCP_45_UC +QALY_vis_SCP_UC_glasgow_ALL: QALY_SCP_50_UC QALY_SCP_55_UC QALY_SCP_60_UC QALY_SCP_65_UC QALY_SCP_70_UC QALY_SCP_75_UC +QALY_vis_SCP_UC_glasgow_ALL: QALY_SCP_80_UC QALY_SCP_85_UC QALY_SCP_90_UC QALY_SCP_95_UC QALY_SCP_100_UC QALY_SCP_105_UC +QALY_vis_SCP_UC_glasgow_ALL: QALY_SCP_110_UC QALY_SCP_115_UC QALY_SCP_120_UC + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention=c('25UniversalCredit', '30UniversalCredit', '35UniversalCredit', '40UniversalCredit', '45UniversalCredit', '50UniversalCredit', '55UniversalCredit', '60UniversalCredit', '65UniversalCredit', '70UniversalCredit', '75UniversalCredit', '80UniversalCredit', '85UniversalCredit', '90UniversalCredit', '95UniversalCredit', '100UniversalCredit', '105UniversalCredit', '110UniversalCredit', '115UniversalCredit', '120UniversalCredit'), start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_UC.html')" QALY_vis_SCP_25_glasgow: EXPERIMENT=glasgow_scaled QALY_vis_SCP_25_glasgow: STARTYEAR=2020 @@ -272,7 +409,13 @@ QALY_vis_SCP_50_glasgow: STARTYEAR=2020 QALY_vis_SCP_50_glasgow: QALY_baseline QALY_SCP_50_All QALY_SCP_50_UC $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention=c('50UniversalCredit', '50All'), start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_50.html')" +QALY_vis_SCP_75_glasgow: EXPERIMENT=glasgow_scaled +QALY_vis_SCP_75_glasgow: STARTYEAR=2020 +QALY_vis_SCP_75_glasgow: QALY_baseline QALY_SCP_75_All QALY_SCP_75_UC + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention=c('75UniversalCredit', '75All'), start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_75.html')" + QALY_vis_SCP_100_glasgow: EXPERIMENT=glasgow_scaled QALY_vis_SCP_100_glasgow: STARTYEAR=2020 QALY_vis_SCP_100_glasgow: QALY_baseline QALY_SCP_100_All QALY_SCP_100_UC $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention=c('100UniversalCredit', '100All'), start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_100.html')" + From d34c7e8211ea86e575d90d196d261cb964203291 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Thu, 14 Mar 2024 10:25:47 +0000 Subject: [PATCH 200/229] Added the missing intervention information for larger universal credit runs in RunPipeline... --- minos/minosPipeline/RunPipeline.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/minos/minosPipeline/RunPipeline.py b/minos/minosPipeline/RunPipeline.py index 239ce9a3..d9e73748 100755 --- a/minos/minosPipeline/RunPipeline.py +++ b/minos/minosPipeline/RunPipeline.py @@ -146,8 +146,20 @@ "40UniversalCredit": childUplift(), "45UniversalCredit": childUplift(), "50UniversalCredit": childUplift(), + "55UniversalCredit": childUplift(), + "60UniversalCredit": childUplift(), + "65UniversalCredit": childUplift(), + "70UniversalCredit": childUplift(), "75UniversalCredit": childUplift(), + "80UniversalCredit": childUplift(), + "85UniversalCredit": childUplift(), + "90UniversalCredit": childUplift(), + "95UniversalCredit": childUplift(), "100UniversalCredit": childUplift(), + "105UniversalCredit": childUplift(), + "110UniversalCredit": childUplift(), + "115UniversalCredit": childUplift(), + "120UniversalCredit": childUplift(), "25Priority": childUplift(), "50Priority": childUplift(), @@ -173,8 +185,20 @@ "40UniversalCredit": {"uplift_amount": 40, "uplift_condition": "who_universal_credit_and_kids"}, "45UniversalCredit": {"uplift_amount": 45, "uplift_condition": "who_universal_credit_and_kids"}, "50UniversalCredit": {"uplift_amount": 50, "uplift_condition": "who_universal_credit_and_kids"}, + "55UniversalCredit": {"uplift_amount": 55, "uplift_condition": "who_universal_credit_and_kids"}, + "60UniversalCredit": {"uplift_amount": 60, "uplift_condition": "who_universal_credit_and_kids"}, + "65UniversalCredit": {"uplift_amount": 65, "uplift_condition": "who_universal_credit_and_kids"}, + "70UniversalCredit": {"uplift_amount": 70, "uplift_condition": "who_universal_credit_and_kids"}, "75UniversalCredit": {"uplift_amount": 75, "uplift_condition": "who_universal_credit_and_kids"}, + "80UniversalCredit": {"uplift_amount": 80, "uplift_condition": "who_universal_credit_and_kids"}, + "85UniversalCredit": {"uplift_amount": 85, "uplift_condition": "who_universal_credit_and_kids"}, + "90UniversalCredit": {"uplift_amount": 90, "uplift_condition": "who_universal_credit_and_kids"}, + "95UniversalCredit": {"uplift_amount": 95, "uplift_condition": "who_universal_credit_and_kids"}, "100UniversalCredit": {"uplift_amount": 100, "uplift_condition": "who_universal_credit_and_kids"}, + "105UniversalCredit": {"uplift_amount": 105, "uplift_condition": "who_universal_credit_and_kids"}, + "110UniversalCredit": {"uplift_amount": 110, "uplift_condition": "who_universal_credit_and_kids"}, + "115UniversalCredit": {"uplift_amount": 115, "uplift_condition": "who_universal_credit_and_kids"}, + "120UniversalCredit": {"uplift_amount": 120, "uplift_condition": "who_universal_credit_and_kids"}, "25Priority": {"uplift_amount": 25, "uplift_condition": "who_vulnerable_subgroups"}, "50Priority": {"uplift_amount": 50, "uplift_condition": "who_vulnerable_subgroups"}, From a56e85168757787b379dd1a8ed24d87d6d3e7fbf Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Thu, 14 Mar 2024 12:01:32 +0000 Subject: [PATCH 201/229] =?UTF-8?q?Added=20another=20combined=20target=20f?= =?UTF-8?q?or=20plotting=2025-50=20in=20=C2=A35=20increments=20together?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- minos/outcomes/QALY.Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/minos/outcomes/QALY.Makefile b/minos/outcomes/QALY.Makefile index f99f9fcf..ac19cbc6 100644 --- a/minos/outcomes/QALY.Makefile +++ b/minos/outcomes/QALY.Makefile @@ -391,6 +391,11 @@ QALY_vis_SCP_UC_glasgow: STARTYEAR=2020 QALY_vis_SCP_UC_glasgow: QALY_baseline QALY_SCP_25_UC QALY_SCP_50_UC QALY_SCP_75_UC QALY_SCP_100_UC $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention=c('25UniversalCredit', '50UniversalCredit', '75UniversalCredit', '100UniversalCredit'), start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_UC.html')" +QALY_vis_SCP_UC_glasgow_25_50: EXPERIMENT=glasgow_scaled +QALY_vis_SCP_UC_glasgow_25_50: STARTYEAR=2020 +QALY_vis_SCP_UC_glasgow_25_50: QALY_baseline QALY_SCP_25_UC QALY_SCP_30_UC QALY_SCP_35_UC QALY_SCP_40_UC QALY_SCP_45_UC QALY_SCP_50_UC + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention=c('25UniversalCredit', '30UniversalCredit', '35UniversalCredit', '40UniversalCredit', '45UniversalCredit', '50UniversalCredit'), start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_UC_25_50.html')"" + QALY_vis_SCP_UC_glasgow_ALL: EXPERIMENT=glasgow_scaled QALY_vis_SCP_UC_glasgow_ALL: STARTYEAR=2020 QALY_vis_SCP_UC_glasgow_ALL: QALY_baseline QALY_SCP_25_UC QALY_SCP_30_UC QALY_SCP_35_UC QALY_SCP_40_UC QALY_SCP_45_UC From 155e55071251373f82557cfbef219629ab227132 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Thu, 14 Mar 2024 13:51:12 +0000 Subject: [PATCH 202/229] Removed an extra quotation mark that was left in by accident --- minos/outcomes/QALY.Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/minos/outcomes/QALY.Makefile b/minos/outcomes/QALY.Makefile index ac19cbc6..f1bf7d68 100644 --- a/minos/outcomes/QALY.Makefile +++ b/minos/outcomes/QALY.Makefile @@ -394,7 +394,7 @@ QALY_vis_SCP_UC_glasgow: QALY_baseline QALY_SCP_25_UC QALY_SCP_50_UC QALY_SCP_75 QALY_vis_SCP_UC_glasgow_25_50: EXPERIMENT=glasgow_scaled QALY_vis_SCP_UC_glasgow_25_50: STARTYEAR=2020 QALY_vis_SCP_UC_glasgow_25_50: QALY_baseline QALY_SCP_25_UC QALY_SCP_30_UC QALY_SCP_35_UC QALY_SCP_40_UC QALY_SCP_45_UC QALY_SCP_50_UC - $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention=c('25UniversalCredit', '30UniversalCredit', '35UniversalCredit', '40UniversalCredit', '45UniversalCredit', '50UniversalCredit'), start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_UC_25_50.html')"" + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention=c('25UniversalCredit', '30UniversalCredit', '35UniversalCredit', '40UniversalCredit', '45UniversalCredit', '50UniversalCredit'), start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_UC_25_50.html')" QALY_vis_SCP_UC_glasgow_ALL: EXPERIMENT=glasgow_scaled QALY_vis_SCP_UC_glasgow_ALL: STARTYEAR=2020 From cd2831789ed896e40a763bd8d643db6cbe7a6fbd Mon Sep 17 00:00:00 2001 From: RobertClay Date: Thu, 14 Mar 2024 22:51:00 +0000 Subject: [PATCH 203/229] adding back in the line the creates council taxes in income composite generation. also managed to get year adjusted council tax as well. --- minos/data_generation/fake_council_tax.py | 49 ++++++++++++++----- .../generate_composite_vars.py | 8 ++- 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/minos/data_generation/fake_council_tax.py b/minos/data_generation/fake_council_tax.py index 0c2f5471..9d88b14a 100755 --- a/minos/data_generation/fake_council_tax.py +++ b/minos/data_generation/fake_council_tax.py @@ -4,16 +4,14 @@ import pandas as pd import numpy as np -import US_utils +from minos.data_generation import US_utils from string import ascii_uppercase as alphabet def format_bands(df): # Tax band data oddly formatted. Put it into floats for math to work. df = df.apply(lambda x: x.str.rstrip()) # remove right whitespace. - df = df.applymap(lambda x: str(x).replace('£', '')) # remove pounds. - df = df.applymap(lambda x: str(x).replace(',', '')) # remove commas. - df = df.astype(float) # to floats. + df = df.replace('[\£,]', '', regex=True).astype(float) # convert from string pounds to floats. return df @@ -65,14 +63,29 @@ def random_draw(x): # TODO can use pretty much any distribution you want here so long as it draws between these bounds. return np.random.uniform(lb, ub) - -if __name__ == '__main__': - maxyr = US_utils.get_data_maxyr() - years = np.arange(1991, maxyr) - file_names = [f"data/raw_US/{item}_US_cohort.csv" for item in years] - data = US_utils.load_multiple_data(file_names) +def main(data): + + # yearly band D council tax percentage increases from 2010/2011 to 2023/2024 + yearly_percentage_changes = [1.8, 0.0, 0.3, 0.8, 0.8,1.1,3.1,4.0,5.1,4.7,3.9,4.4,3.5,5.1,] + #backtrack from 2023/24 calculating reduction in council tax. + # formula is old_band = 1/(1+percentage_change). e.g if theres a 25 percentage increase from year 1 to year 2. + # to go from year 2 to year 1 is an 1/(1+0.25)=0.8 20 percent decrease. + + # reverse the list to work backwards. + yearly_percentage_changes = yearly_percentage_changes[::-1] + # add a one to the front. this is the starting 23/24 year where the bands are correct and dont need to do anything. + yearly_percentage_changes = [1] + yearly_percentage_changes + for i in range(len(yearly_percentage_changes)): + if i == 0: + continue + yearly_percentage_changes[i] = yearly_percentage_changes[i-1] * 1/(1+yearly_percentage_changes[i]/100) + # reverse back to correct time order. + yearly_percentage_changes = yearly_percentage_changes[::-1] + # convert to dict to allow mapping year in data to percentage change in council tax band. + yearly_percentage_changes = dict(zip(range(2009,2024), yearly_percentage_changes)) # data for conversions between LADs to region and council tax band (A, B,...) to numeric boundaries (£1200-£1400) + #https://www.completelymoved.co.uk/money/advice/council-tax-bands-table-of-all-uk-regions-compared lad_to_band = pd.read_csv("persistent_data/lad_tax_bands.csv") lad_to_region = US_utils.load_json("persistent_data/JSON/", "LAD_to_region_name.json") @@ -99,9 +112,21 @@ def random_draw(x): data["council_tax_lower"] = data.apply(lambda x: lower_bound(x, ct_bands), axis=1) # establish individual lower and upper bounds. data["council_tax_upper"] = data.apply(lambda x: upper_bound(x, ct_bands), axis=1) data["council_tax_draw"] = data.apply(random_draw, axis=1) # draw randomly between these bounds. + data['council_tax_draw'] = data['council_tax_draw']/12 # convert to monthly bill. + + data['yearly_council_tax_change'] = data['time'].replace(yearly_percentage_changes) + data['council_tax_draw'] = data['council_tax_draw'] * data['yearly_council_tax_change'] # Handle single case where someone in London gave has wrong council tax band (Band I which only exists in Wales) data['council_tax_draw'][data['council_tax_draw'].isna()] = -9 + data['council_tax'] = data['council_tax_draw'] + return data - print('Finished composite generation. Saving data...') - US_utils.save_multiple_files(data, years, "data/adj_raw_US/", "") +if __name__ == '__main__': + maxyr = US_utils.get_data_maxyr() + years = np.arange(1991, maxyr) + file_names = [f"data/raw_US/{item}_US_cohort.csv" for item in years] + data = US_utils.load_multiple_data(file_names) + main(data) + print('Finished generating council_tax_data. Saving data...') + US_utils.save_multiple_files(data, years, "data/adj_raw_US/", "") \ No newline at end of file diff --git a/minos/data_generation/generate_composite_vars.py b/minos/data_generation/generate_composite_vars.py index 65a2aafe..5cf17a72 100755 --- a/minos/data_generation/generate_composite_vars.py +++ b/minos/data_generation/generate_composite_vars.py @@ -13,6 +13,7 @@ from minos.data_generation import US_utils # import US_missing_description as USmd +import minos.data_generation.fake_council_tax as fake_council_tax # suppressing a warning that isn't a problem pd.options.mode.chained_assignment = None # default='warn' #supress SettingWithCopyWarning @@ -214,7 +215,11 @@ def generate_hh_income(data): # first calculate outgoings (set to 0 if missing (i.e. if negative)) data["hh_rent"][data["hh_rent"] < 0] = 0 data["hh_mortgage"][data["hh_mortgage"] < 0] = 0 + + data = fake_council_tax.main(data) + data['council_tax'] = data.groupby(['hidp'])['council_tax'].transform('max') data["council_tax"][data["council_tax"] < 0] = 0 + data["outgoings"] = -9 data["outgoings"] = data["hh_rent"] + data["hh_mortgage"] + data["council_tax"] @@ -226,7 +231,8 @@ def generate_hh_income(data): data = US_utils.inflation_adjustment(data, "hh_income") # now drop the intermediates - data.drop(labels=['hh_rent', 'hh_mortgage', 'council_tax', 'outgoings', 'hh_netinc', 'oecd_equiv'], + data.drop(labels=['hh_rent', 'hh_mortgage', 'council_tax', 'outgoings', 'hh_netinc', 'oecd_equiv', + 'council_tax_lower', 'council_tax_upper', 'council_tax_draw'], axis=1, inplace=True) From 24a1cf3f8d19f28ca413b36b4e981affe6e6d455 Mon Sep 17 00:00:00 2001 From: RobertClay Date: Thu, 14 Mar 2024 22:53:11 +0000 Subject: [PATCH 204/229] adding in update 23/24 council tax bands by local authority --- minos/data_generation/fake_council_tax.py | 3 +- .../23_24_LA_council_tax_bands.csv | 330 ++++++++++++++++++ 2 files changed, 332 insertions(+), 1 deletion(-) create mode 100644 persistent_data/23_24_LA_council_tax_bands.csv diff --git a/minos/data_generation/fake_council_tax.py b/minos/data_generation/fake_council_tax.py index 9d88b14a..fa2530e3 100755 --- a/minos/data_generation/fake_council_tax.py +++ b/minos/data_generation/fake_council_tax.py @@ -86,7 +86,8 @@ def main(data): # data for conversions between LADs to region and council tax band (A, B,...) to numeric boundaries (£1200-£1400) #https://www.completelymoved.co.uk/money/advice/council-tax-bands-table-of-all-uk-regions-compared - lad_to_band = pd.read_csv("persistent_data/lad_tax_bands.csv") + #lad_to_band = pd.read_csv("persistent_data/lad_tax_bands.csv") + lad_to_band = pd.read_csv("persistent_data/23_24_LA_council_tax_bands.csv") lad_to_region = US_utils.load_json("persistent_data/JSON/", "LAD_to_region_name.json") band_columns = ['Band A', 'Band B', 'Band C', 'Band D', 'Band E', diff --git a/persistent_data/23_24_LA_council_tax_bands.csv b/persistent_data/23_24_LA_council_tax_bands.csv new file mode 100644 index 00000000..924b5c5d --- /dev/null +++ b/persistent_data/23_24_LA_council_tax_bands.csv @@ -0,0 +1,330 @@ +Area ,Increase,Band A,Band B,Band C,Band D,Band E,Band F,Band G,Band H,Band I,Region +Adur,4.67%,"£1,478.26","£1,724.63","£1,971.01","£2,217.39","£2,710.14","£3,202.90","£3,695.65","£4,434.78","£5,173.91",England +Amber Valley,4.14%,"£1,390.89","£1,622.70","£1,854.51","£2,086.33","£2,549.96","£3,013.59","£3,477.22","£4,172.66","£4,868.10",England +Arun,4.93%,"£1,438.85","£1,678.66","£1,918.47","£2,158.28","£2,637.90","£3,117.51","£3,597.13","£4,316.56","£5,035.99",England +Ashfield,4.85%,"£1,529.09","£1,783.93","£2,038.78","£2,293.63","£2,803.33","£3,313.02","£3,822.72","£4,587.26","£5,351.80",England +Ashford,5.02%,"£1,400.49","£1,633.90","£1,867.31","£2,100.73","£2,567.56","£3,034.39","£3,501.22","£4,201.46","£4,901.70",England +Babergh,4.16%,"£1,356.49","£1,582.57","£1,808.66","£2,034.74","£2,486.90","£2,939.07","£3,391.23","£4,069.48","£4,747.73",England +Barking and Dagenham,6.04%,"£1,261.81","£1,472.11","£1,682.41","£1,892.71","£2,313.31","£2,733.91","£3,154.52","£3,785.42","£4,416.32",England +Barnet,5.15%,"£1,224.11","£1,428.13","£1,632.15","£1,836.17","£2,244.21","£2,652.24","£3,060.28","£3,672.34","£4,284.40",England +Barnsley,4.32%,"£1,355.43","£1,581.34","£1,807.24","£2,033.15","£2,484.96","£2,936.77","£3,388.58","£4,066.30","£4,744.02",England +Basildon,3.89%,"£1,375.32","£1,604.54","£1,833.76","£2,062.98","£2,521.42","£2,979.86","£3,438.30","£4,125.96","£4,813.62",England +Basingstoke and Deane,4.90%,"£1,302.62","£1,519.72","£1,736.82","£1,953.93","£2,388.14","£2,822.34","£3,256.55","£3,907.86","£4,559.17",England +Bassetlaw,4.84%,"£1,544.67","£1,802.12","£2,059.56","£2,317.01","£2,831.90","£3,346.79","£3,861.68","£4,634.02","£5,406.36",England +Bath and North East Somerset,5.25%,"£1,368.06","£1,596.07","£1,824.08","£2,052.09","£2,508.11","£2,964.13","£3,420.15","£4,104.18","£4,788.21",England +Bedford,3.38%,"£1,421.59","£1,658.52","£1,895.46","£2,132.39","£2,606.25","£3,080.12","£3,553.98","£4,264.78","£4,975.58",England +Bexley,5.97%,"£1,358.51","£1,584.92","£1,811.34","£2,037.76","£2,490.60","£2,943.43","£3,396.27","£4,075.52","£4,754.77",England +Birmingham,5.37%,"£1,275.35","£1,487.90","£1,700.46","£1,913.02","£2,338.14","£2,763.25","£3,188.37","£3,826.04","£4,463.71",England +Blaby,5.04%,"£1,456.16","£1,698.85","£1,941.54","£2,184.24","£2,669.63","£3,155.01","£3,640.40","£4,368.48","£5,096.56",England +Blackburn with Darwen,5.19%,"£1,413.91","£1,649.56","£1,885.22","£2,120.87","£2,592.17","£3,063.48","£3,534.78","£4,241.74","£4,948.70",England +Blackpool,5.20%,"£1,447.30","£1,688.51","£1,929.73","£2,170.95","£2,653.38","£3,135.82","£3,618.25","£4,341.90","£5,065.55",England +Bolsover,4.26%,"£1,467.14","£1,711.66","£1,956.18","£2,200.71","£2,689.76","£3,178.80","£3,667.85","£4,401.42","£5,134.99",England +Bolton,4.34%,"£1,363.09","£1,590.27","£1,817.45","£2,044.63","£2,498.99","£2,953.35","£3,407.72","£4,089.26","£4,770.80",England +Boston,4.79%,"£1,378.49","£1,608.24","£1,837.99","£2,067.74","£2,527.24","£2,986.73","£3,446.23","£4,135.48","£4,824.73",England +"Bournemouth, Christchurch and Poole",5.12%,"£1,370.23","£1,598.60","£1,826.97","£2,055.34","£2,512.08","£2,968.82","£3,425.57","£4,110.68","£4,795.79",England +Bracknell Forest,5.15%,"£1,304.53","£1,521.95","£1,739.37","£1,956.79","£2,391.63","£2,826.47","£3,261.32","£3,913.58","£4,565.84",England +Bradford,5.29%,"£1,303.86","£1,521.17","£1,738.48","£1,955.79","£2,390.41","£2,825.03","£3,259.65","£3,911.58","£4,563.51",England +Braintree,4.00%,"£1,340.25","£1,563.63","£1,787.00","£2,010.38","£2,457.13","£2,903.88","£3,350.63","£4,020.76","£4,690.89",England +Breckland,5.16%,"£1,409.61","£1,644.54","£1,879.47","£2,114.41","£2,584.28","£3,054.15","£3,524.02","£4,228.82","£4,933.62",England +Brent,6.03%,"£1,282.97","£1,496.79","£1,710.62","£1,924.45","£2,352.11","£2,779.76","£3,207.42","£3,848.90","£4,490.38",England +Brentwood,3.99%,"£1,325.73","£1,546.69","£1,767.64","£1,988.60","£2,430.51","£2,872.42","£3,314.33","£3,977.20","£4,640.07",England +Brighton and Hove,5.17%,"£1,485.89","£1,733.53","£1,981.18","£2,228.83","£2,724.13","£3,219.42","£3,714.72","£4,457.66","£5,200.60",England +Bristol,5.15%,"£1,563.49","£1,824.07","£2,084.66","£2,345.24","£2,866.40","£3,387.57","£3,908.73","£4,690.48","£5,472.23",England +Broadland,4.62%,"£1,413.83","£1,649.47","£1,885.11","£2,120.75","£2,592.03","£3,063.30","£3,534.58","£4,241.50","£4,948.42",England +Bromley,6.07%,"£1,228.13","£1,432.81","£1,637.50","£1,842.19","£2,251.57","£2,660.94","£3,070.32","£3,684.38","£4,298.44",England +Bromsgrove,4.74%,"£1,398.17","£1,631.19","£1,864.22","£2,097.25","£2,563.31","£3,029.36","£3,495.42","£4,194.50","£4,893.58",England +Broxbourne,5.07%,"£1,331.25","£1,553.12","£1,774.99","£1,996.87","£2,440.62","£2,884.37","£3,328.12","£3,993.74","£4,659.36",England +Broxtowe,4.80%,"£1,528.50","£1,783.25","£2,038.00","£2,292.75","£2,802.25","£3,311.75","£3,821.25","£4,585.50","£5,349.75",England +Buckinghamshire,5.25%,"£1,451.23","£1,693.10","£1,934.98","£2,176.85","£2,660.59","£3,144.34","£3,628.08","£4,353.70","£5,079.32",England +Burnley,4.18%,"£1,495.87","£1,745.18","£1,994.49","£2,243.80","£2,742.42","£3,241.04","£3,739.67","£4,487.60","£5,235.53",England +Bury,5.16%,"£1,453.04","£1,695.21","£1,937.38","£2,179.56","£2,663.91","£3,148.25","£3,632.60","£4,359.12","£5,085.64",England +Calderdale,5.31%,"£1,390.61","£1,622.38","£1,854.15","£2,085.92","£2,549.46","£3,012.99","£3,476.53","£4,171.84","£4,867.15",England +Cambridge,5.53%,"£1,417.44","£1,653.68","£1,889.92","£2,126.16","£2,598.64","£3,071.12","£3,543.60","£4,252.32","£4,961.04",England +Camden,6.04%,"£1,266.97","£1,478.13","£1,689.30","£1,900.46","£2,322.78","£2,745.11","£3,167.43","£3,800.92","£4,434.41",England +Cannock Chase,4.70%,"£1,387.61","£1,618.88","£1,850.15","£2,081.42","£2,543.96","£3,006.49","£3,469.03","£4,162.84","£4,856.65",England +Canterbury,4.96%,"£1,409.77","£1,644.73","£1,879.69","£2,114.65","£2,584.57","£3,054.49","£3,524.42","£4,229.30","£4,934.18",England +Castle Point,3.91%,"£1,369.65","£1,597.93","£1,826.20","£2,054.48","£2,511.03","£2,967.58","£3,424.13","£4,108.96","£4,793.79",England +Central Bedfordshire,1.29%,"£1,446.51","£1,687.59","£1,928.67","£2,169.76","£2,651.93","£3,134.10","£3,616.27","£4,339.52","£5,062.77",England +Charnwood,5.08%,"£1,412.46","£1,647.87","£1,883.28","£2,118.69","£2,589.51","£3,060.33","£3,531.15","£4,237.38","£4,943.61",England +Chelmsford,4.06%,"£1,349.89","£1,574.87","£1,799.86","£2,024.84","£2,474.80","£2,924.77","£3,374.73","£4,049.68","£4,724.63",England +Cheltenham,4.85%,"£1,372.95","£1,601.78","£1,830.60","£2,059.43","£2,517.08","£2,974.73","£3,432.38","£4,118.86","£4,805.34",England +Cherwell,4.90%,"£1,495.10","£1,744.28","£1,993.46","£2,242.65","£2,741.02","£3,239.38","£3,737.75","£4,485.30","£5,232.85",England +Cheshire East,5.26%,"£1,406.01","£1,640.35","£1,874.68","£2,109.02","£2,577.69","£3,046.36","£3,515.03","£4,218.04","£4,921.05",England +Cheshire West and Chester,5.26%,"£1,446.30","£1,687.35","£1,928.40","£2,169.45","£2,651.55","£3,133.65","£3,615.75","£4,338.90","£5,062.05",England +Chesterfield,4.12%,"£1,356.04","£1,582.05","£1,808.05","£2,034.06","£2,486.07","£2,938.09","£3,390.10","£4,068.12","£4,746.14",England +Chichester,5.03%,"£1,420.19","£1,656.88","£1,893.58","£2,130.28","£2,603.68","£3,077.07","£3,550.47","£4,260.56","£4,970.65",England +Chorley,4.14%,"£1,422.08","£1,659.09","£1,896.10","£2,133.12","£2,607.15","£3,081.17","£3,555.20","£4,266.24","£4,977.28",England +City of London,6.61%,£763.76,£891.05,"£1,018.35","£1,145.64","£1,400.23","£1,654.81","£1,909.40","£2,291.28","£2,673.16",England +Colchester,3.99%,"£1,340.94","£1,564.43","£1,787.92","£2,011.41","£2,458.39","£2,905.37","£3,352.35","£4,022.82","£4,693.29",England +Cornwall,5.33%,"£1,480.93","£1,727.75","£1,974.57","£2,221.39","£2,715.03","£3,208.67","£3,702.32","£4,442.78","£5,183.24",England +Cotswold,4.92%,"£1,375.75","£1,605.04","£1,834.34","£2,063.63","£2,522.21","£2,980.80","£3,439.38","£4,127.26","£4,815.14",England +Coventry,5.30%,"£1,457.33","£1,700.21","£1,943.10","£2,185.99","£2,671.77","£3,157.54","£3,643.32","£4,371.98","£5,100.64",England +Crawley,4.96%,"£1,399.11","£1,632.29","£1,865.47","£2,098.66","£2,565.03","£3,031.40","£3,497.77","£4,197.32","£4,896.87",England +Croydon,13.93%,"£1,493.04","£1,741.88","£1,990.72","£2,239.56","£2,737.24","£3,234.92","£3,732.60","£4,479.12","£5,225.64",England +Dacorum,5.09%,"£1,391.23","£1,623.10","£1,854.98","£2,086.85","£2,550.59","£3,014.34","£3,478.08","£4,173.70","£4,869.32",England +Darlington,5.11%,"£1,435.31","£1,674.52","£1,913.74","£2,152.96","£2,631.40","£3,109.83","£3,588.27","£4,305.92","£5,023.57",England +Dartford,4.68%,"£1,385.32","£1,616.21","£1,847.09","£2,077.98","£2,539.75","£3,001.53","£3,463.30","£4,155.96","£4,848.62",England +Derby,5.17%,"£1,339.41","£1,562.64","£1,785.87","£2,009.11","£2,455.58","£2,902.05","£3,348.52","£4,018.22","£4,687.92",England +Derbyshire Dales,4.03%,"£1,416.47","£1,652.55","£1,888.63","£2,124.71","£2,596.87","£3,069.02","£3,541.18","£4,249.42","£4,957.66",England +Doncaster,4.55%,"£1,283.97","£1,497.96","£1,711.95","£1,925.95","£2,353.94","£2,781.93","£3,209.92","£3,851.90","£4,493.88",England +Dorset Council,4.26%,"£1,591.87","£1,857.18","£2,122.50","£2,387.81","£2,918.43","£3,449.06","£3,979.68","£4,775.62","£5,571.56",England +Dover,4.93%,"£1,433.94","£1,672.93","£1,911.92","£2,150.91","£2,628.89","£3,106.87","£3,584.85","£4,301.82","£5,018.79",England +Dudley,5.40%,"£1,229.31","£1,434.20","£1,639.08","£1,843.97","£2,253.74","£2,663.51","£3,073.28","£3,687.94","£4,302.60",England +Durham,5.12%,"£1,544.09","£1,801.43","£2,058.78","£2,316.13","£2,830.83","£3,345.52","£3,860.22","£4,632.26","£5,404.30",England +Ealing,6.07%,"£1,227.26","£1,431.80","£1,636.35","£1,840.89","£2,249.98","£2,659.06","£3,068.15","£3,681.78","£4,295.41",England +East Cambridgeshire,5.46%,"£1,428.88","£1,667.02","£1,905.17","£2,143.32","£2,619.61","£3,095.91","£3,572.20","£4,286.64","£5,001.08",England +East Devon,4.99%,"£1,490.47","£1,738.88","£1,987.30","£2,235.71","£2,732.53","£3,229.36","£3,726.18","£4,471.42","£5,216.66",England +East Hampshire,5.01%,"£1,352.17","£1,577.53","£1,802.89","£2,028.25","£2,478.97","£2,929.69","£3,380.42","£4,056.50","£4,732.58",England +East Hertfordshire,4.94%,"£1,411.31","£1,646.52","£1,881.74","£2,116.96","£2,587.40","£3,057.83","£3,528.27","£4,233.92","£4,939.57",England +East Lindsey,4.94%,"£1,353.79","£1,579.42","£1,805.05","£2,030.68","£2,481.94","£2,933.20","£3,384.47","£4,061.36","£4,738.25",England +East Riding of Yorkshire,5.10%,"£1,416.00","£1,652.00","£1,888.00","£2,124.00","£2,596.00","£3,068.00","£3,540.00","£4,248.00","£4,956.00",England +East Staffordshire,4.68%,"£1,369.37","£1,597.59","£1,825.82","£2,054.05","£2,510.51","£2,966.96","£3,423.42","£4,108.10","£4,792.78",England +East Suffolk,4.13%,"£1,345.83","£1,570.14","£1,794.44","£2,018.75","£2,467.36","£2,915.97","£3,364.58","£4,037.50","£4,710.42",England +Eastbourne,4.92%,"£1,538.51","£1,794.92","£2,051.34","£2,307.76","£2,820.60","£3,333.43","£3,846.27","£4,615.52","£5,384.77",England +Eastleigh,5.84%,"£1,348.49","£1,573.24","£1,797.99","£2,022.74","£2,472.24","£2,921.73","£3,371.23","£4,045.48","£4,719.73",England +Elmbridge,3.28%,"£1,486.51","£1,734.26","£1,982.01","£2,229.76","£2,725.26","£3,220.76","£3,716.27","£4,459.52","£5,202.77",England +Enfield,6.01%,"£1,301.63","£1,518.56","£1,735.50","£1,952.44","£2,386.32","£2,820.19","£3,254.07","£3,904.88","£4,555.69",England +Epping Forest,4.11%,"£1,334.83","£1,557.30","£1,779.78","£2,002.25","£2,447.19","£2,892.14","£3,337.08","£4,004.50","£4,671.92",England +Epsom and Ewell,3.28%,"£1,470.17","£1,715.19","£1,960.22","£2,205.25","£2,695.31","£3,185.36","£3,675.42","£4,410.50","£5,145.58",England +Erewash,4.15%,"£1,369.47","£1,597.71","£1,825.95","£2,054.20","£2,510.69","£2,967.18","£3,423.67","£4,108.40","£4,793.13",England +Exeter,4.98%,"£1,445.07","£1,685.92","£1,926.76","£2,167.61","£2,649.30","£3,130.99","£3,612.68","£4,335.22","£5,057.76",England +Fareham,5.04%,"£1,315.07","£1,534.24","£1,753.42","£1,972.60","£2,410.96","£2,849.31","£3,287.67","£3,945.20","£4,602.73",England +Fenland,4.86%,"£1,475.81","£1,721.78","£1,967.75","£2,213.72","£2,705.66","£3,197.59","£3,689.53","£4,427.44","£5,165.35",England +Folkestone and Hythe,4.88%,"£1,481.41","£1,728.31","£1,975.22","£2,222.12","£2,715.92","£3,209.73","£3,703.53","£4,444.24","£5,184.95",England +Forest of Dean,4.96%,"£1,410.95","£1,646.11","£1,881.27","£2,116.43","£2,586.75","£3,057.06","£3,527.38","£4,232.86","£4,938.34",England +Fylde,3.90%,"£1,443.31","£1,683.86","£1,924.42","£2,164.97","£2,646.07","£3,127.18","£3,608.28","£4,329.94","£5,051.60",England +Gateshead,5.35%,"£1,554.64","£1,813.74","£2,072.85","£2,331.96","£2,850.17","£3,368.39","£3,886.60","£4,663.92","£5,441.24",England +Gedling,4.87%,"£1,525.30","£1,779.51","£2,033.73","£2,287.95","£2,796.38","£3,304.82","£3,813.25","£4,575.90","£5,338.55",England +Gloucester,4.82%,"£1,366.18","£1,593.88","£1,821.57","£2,049.27","£2,504.66","£2,960.06","£3,415.45","£4,098.54","£4,781.63",England +Gosport,4.97%,"£1,360.06","£1,586.74","£1,813.41","£2,040.09","£2,493.44","£2,946.80","£3,400.15","£4,080.18","£4,760.21",England +Gravesham,5.02%,"£1,402.51","£1,636.26","£1,870.01","£2,103.76","£2,571.26","£3,038.76","£3,506.27","£4,207.52","£4,908.77",England +Great Yarmouth,4.89%,"£1,399.71","£1,632.99","£1,866.27","£2,099.56","£2,566.13","£3,032.70","£3,499.27","£4,199.12","£4,898.97",England +Greenwich,6.09%,"£1,209.60","£1,411.20","£1,612.80","£1,814.40","£2,217.60","£2,620.80","£3,024.00","£3,628.80","£4,233.60",England +Guildford,3.32%,"£1,476.51","£1,722.60","£1,968.68","£2,214.77","£2,706.94","£3,199.11","£3,691.28","£4,429.54","£5,167.80",England +Hackney,6.12%,"£1,182.19","£1,379.22","£1,576.26","£1,773.29","£2,167.35","£2,561.42","£2,955.48","£3,546.58","£4,137.68",England +Halton,5.15%,"£1,357.87","£1,584.18","£1,810.49","£2,036.80","£2,489.42","£2,942.04","£3,394.67","£4,073.60","£4,752.53",England +Hammersmith and Fulham,6.39%,£870.67,"£1,015.78","£1,160.89","£1,306.00","£1,596.22","£1,886.44","£2,176.67","£2,612.00","£3,047.33",England +Harborough,4.80%,"£1,411.34","£1,646.56","£1,881.78","£2,117.01","£2,587.46","£3,057.90","£3,528.35","£4,234.02","£4,939.69",England +Haringey,5.99%,"£1,328.21","£1,549.58","£1,770.95","£1,992.32","£2,435.06","£2,877.79","£3,320.53","£3,984.64","£4,648.75",England +Harlow,3.48%,"£1,368.54","£1,596.63","£1,824.72","£2,052.81","£2,508.99","£2,965.17","£3,421.35","£4,105.62","£4,789.89",England +Harrow,5.91%,"£1,441.87","£1,682.18","£1,922.49","£2,162.80","£2,643.42","£3,124.04","£3,604.67","£4,325.60","£5,046.53",England +Hart,5.08%,"£1,385.67","£1,616.62","£1,847.56","£2,078.51","£2,540.40","£3,002.29","£3,464.18","£4,157.02","£4,849.86",England +Hartlepool,5.02%,"£1,538.01","£1,794.34","£2,050.67","£2,307.01","£2,819.68","£3,332.35","£3,845.02","£4,614.02","£5,383.02",England +Hastings,4.91%,"£1,552.11","£1,810.80","£2,069.48","£2,328.17","£2,845.54","£3,362.91","£3,880.28","£4,656.34","£5,432.40",England +Havant,4.99%,"£1,345.47","£1,569.71","£1,793.95","£2,018.20","£2,466.69","£2,915.18","£3,363.67","£4,036.40","£4,709.13",England +Havering,5.94%,"£1,392.09","£1,624.10","£1,856.11","£2,088.13","£2,552.16","£3,016.19","£3,480.22","£4,176.26","£4,872.30",England +Herefordshire,5.09%,"£1,480.93","£1,727.75","£1,974.58","£2,221.40","£2,715.04","£3,208.69","£3,702.33","£4,442.80","£5,183.27",England +Hertsmere,5.09%,"£1,383.96","£1,614.62","£1,845.28","£2,075.94","£2,537.26","£2,998.58","£3,459.90","£4,151.88","£4,843.86",England +High Peak,4.05%,"£1,378.01","£1,607.67","£1,837.34","£2,067.01","£2,526.35","£2,985.68","£3,445.02","£4,134.02","£4,823.02",England +Hillingdon,6.12%,"£1,173.64","£1,369.25","£1,564.85","£1,760.46","£2,151.67","£2,542.89","£2,934.10","£3,520.92","£4,107.74",England +Hinckley and Bosworth,5.10%,"£1,393.47","£1,625.72","£1,857.96","£2,090.21","£2,554.70","£3,019.19","£3,483.68","£4,180.42","£4,877.16",England +Horsham,4.94%,"£1,405.90","£1,640.22","£1,874.53","£2,108.85","£2,577.48","£3,046.12","£3,514.75","£4,217.70","£4,920.65",England +Hounslow,6.05%,"£1,254.35","£1,463.40","£1,672.46","£1,881.52","£2,299.64","£2,717.75","£3,135.87","£3,763.04","£4,390.21",England +Huntingdonshire,5.61%,"£1,461.27","£1,704.81","£1,948.35","£2,191.90","£2,678.99","£3,166.08","£3,653.17","£4,383.80","£5,114.43",England +Hyndburn,3.85%,"£1,446.45","£1,687.52","£1,928.59","£2,169.67","£2,651.82","£3,133.97","£3,616.12","£4,339.34","£5,062.56",England +Ipswich,4.05%,"£1,436.45","£1,675.86","£1,915.27","£2,154.68","£2,633.50","£3,112.31","£3,591.13","£4,309.36","£5,027.59",England +Isle of Wight Council,5.55%,"£1,505.60","£1,756.53","£2,007.46","£2,258.40","£2,760.27","£3,262.13","£3,764.00","£4,516.80","£5,269.60",England +Isles of Scilly,5.15%,"£1,179.45","£1,376.03","£1,572.60","£1,769.18","£2,162.33","£2,555.48","£2,948.63","£3,538.36","£4,128.09",England +Islington,6.09%,"£1,209.77","£1,411.40","£1,613.03","£1,814.66","£2,217.92","£2,621.17","£3,024.43","£3,629.32","£4,234.21",England +Kensington and Chelsea,4.35%,£961.53,"£1,121.78","£1,282.03","£1,442.29","£1,762.80","£2,083.31","£2,403.82","£2,884.58","£3,365.34",England +King's Lynn and West Norfolk,4.92%,"£1,409.32","£1,644.21","£1,879.09","£2,113.98","£2,583.75","£3,053.53","£3,523.30","£4,227.96","£4,932.62",England +Kirklees,5.26%,"£1,401.63","£1,635.24","£1,868.84","£2,102.45","£2,569.66","£3,036.87","£3,504.08","£4,204.90","£4,905.72",England +Knowsley,5.03%,"£1,433.59","£1,672.52","£1,911.45","£2,150.38","£2,628.24","£3,106.10","£3,583.97","£4,300.76","£5,017.55",England +Lambeth,6.12%,"£1,174.60","£1,370.37","£1,566.13","£1,761.90","£2,153.43","£2,544.97","£2,936.50","£3,523.80","£4,111.10",England +Lancaster,5.35%,"£1,469.94","£1,714.93","£1,959.92","£2,204.91","£2,694.89","£3,184.87","£3,674.85","£4,409.82","£5,144.79",England +Leeds,5.30%,"£1,312.25","£1,530.96","£1,749.67","£1,968.38","£2,405.80","£2,843.21","£3,280.63","£3,936.76","£4,592.89",England +Leicester,5.16%,"£1,457.01","£1,699.85","£1,942.68","£2,185.52","£2,671.19","£3,156.86","£3,642.53","£4,371.04","£5,099.55",England +Lewes,4.68%,"£1,592.11","£1,857.46","£2,122.82","£2,388.17","£2,918.87","£3,449.58","£3,980.28","£4,776.34","£5,572.40",England +Lewisham,6.02%,"£1,284.18","£1,498.21","£1,712.24","£1,926.27","£2,354.33","£2,782.39","£3,210.45","£3,852.54","£4,494.63",England +Lichfield,4.37%,"£1,371.84","£1,600.48","£1,829.12","£2,057.76","£2,515.04","£2,972.32","£3,429.60","£4,115.52","£4,801.44",England +Lincoln,4.74%,"£1,396.08","£1,628.76","£1,861.44","£2,094.12","£2,559.48","£3,024.84","£3,490.20","£4,188.24","£4,886.28",England +Liverpool,5.13%,"£1,538.37","£1,794.76","£2,051.15","£2,307.55","£2,820.34","£3,333.13","£3,845.92","£4,615.10","£5,384.28",England +Luton,5.14%,"£1,404.43","£1,638.50","£1,872.57","£2,106.64","£2,574.78","£3,042.92","£3,511.07","£4,213.28","£4,915.49",England +Maidstone,5.01%,"£1,459.75","£1,703.04","£1,946.33","£2,189.62","£2,676.20","£3,162.78","£3,649.37","£4,379.24","£5,109.11",England +Maldon,3.87%,"£1,364.34","£1,591.73","£1,819.12","£2,046.51","£2,501.29","£2,956.07","£3,410.85","£4,093.02","£4,775.19",England +Malvern Hills,4.93%,"£1,387.85","£1,619.15","£1,850.46","£2,081.77","£2,544.39","£3,007.00","£3,469.62","£4,163.54","£4,857.46",England +Manchester,5.18%,"£1,313.00","£1,531.83","£1,750.66","£1,969.50","£2,407.17","£2,844.83","£3,282.50","£3,939.00","£4,595.50",England +Mansfield,4.56%,"£1,520.39","£1,773.79","£2,027.19","£2,280.59","£2,787.39","£3,294.18","£3,800.98","£4,561.18","£5,321.38",England +Medway,5.24%,"£1,339.04","£1,562.21","£1,785.38","£2,008.56","£2,454.91","£2,901.25","£3,347.60","£4,017.12","£4,686.64",England +Melton,4.91%,"£1,426.87","£1,664.68","£1,902.50","£2,140.31","£2,615.93","£3,091.56","£3,567.18","£4,280.62","£4,994.06",England +Merton,6.05%,"£1,259.51","£1,469.43","£1,679.35","£1,889.27","£2,309.11","£2,728.94","£3,148.78","£3,778.54","£4,408.30",England +Mid Devon,5.02%,"£1,530.21","£1,785.25","£2,040.28","£2,295.32","£2,805.39","£3,315.46","£3,825.53","£4,590.64","£5,355.75",England +Mid Suffolk,3.91%,"£1,344.45","£1,568.53","£1,792.60","£2,016.68","£2,464.83","£2,912.98","£3,361.13","£4,033.36","£4,705.59",England +Mid Sussex,4.93%,"£1,425.87","£1,663.51","£1,901.15","£2,138.80","£2,614.09","£3,089.38","£3,564.67","£4,277.60","£4,990.53",England +Middlesbrough,4.25%,"£1,506.94","£1,758.09","£2,009.25","£2,260.41","£2,762.72","£3,265.04","£3,767.35","£4,520.82","£5,274.29",England +Milton Keynes,5.30%,"£1,364.08","£1,591.43","£1,818.77","£2,046.12","£2,500.81","£2,955.51","£3,410.20","£4,092.24","£4,774.28",England +Mole Valley,3.29%,"£1,462.91","£1,706.73","£1,950.55","£2,194.37","£2,682.01","£3,169.64","£3,657.28","£4,388.74","£5,120.20",England +New Forest,5.16%,"£1,393.71","£1,625.99","£1,858.27","£2,090.56","£2,555.13","£3,019.70","£3,484.27","£4,181.12","£4,877.97",England +Newark and Sherwood,4.68%,"£1,571.38","£1,833.27","£2,095.17","£2,357.07","£2,880.86","£3,404.66","£3,928.45","£4,714.14","£5,499.83",England +Newham,6.22%,"£1,085.02","£1,265.86","£1,446.69","£1,627.53","£1,989.20","£2,350.88","£2,712.55","£3,255.06","£3,797.57",England +North Devon,5.03%,"£1,519.25","£1,772.46","£2,025.67","£2,278.88","£2,785.30","£3,291.71","£3,798.13","£4,557.76","£5,317.39",England +North East Derbyshire,3.89%,"£1,429.18","£1,667.37","£1,905.57","£2,143.77","£2,620.16","£3,096.56","£3,572.95","£4,287.54","£5,002.13",England +North East Lincolnshire,4.32%,"£1,446.93","£1,688.08","£1,929.23","£2,170.39","£2,652.70","£3,135.01","£3,617.32","£4,340.78","£5,064.24",England +North Hertfordshire,4.97%,"£1,415.66","£1,651.60","£1,887.54","£2,123.49","£2,595.38","£3,067.26","£3,539.15","£4,246.98","£4,954.81",England +North Kesteven,4.92%,"£1,392.03","£1,624.03","£1,856.03","£2,088.04","£2,552.05","£3,016.06","£3,480.07","£4,176.08","£4,872.09",England +North Lincolnshire,2.44%,"£1,368.17","£1,596.19","£1,824.22","£2,052.25","£2,508.31","£2,964.36","£3,420.42","£4,104.50","£4,788.58",England +North Norfolk,4.88%,"£1,419.45","£1,656.03","£1,892.60","£2,129.18","£2,602.33","£3,075.48","£3,548.63","£4,258.36","£4,968.09",England +North Northamptonshire,5.24%,"£1,392.71","£1,624.82","£1,856.94","£2,089.06","£2,553.30","£3,017.53","£3,481.77","£4,178.12","£4,874.47",England +North Somerset,5.26%,"£1,374.74","£1,603.86","£1,832.98","£2,062.11","£2,520.36","£2,978.60","£3,436.85","£4,124.22","£4,811.59",England +North Tyneside,5.39%,"£1,408.23","£1,642.93","£1,877.63","£2,112.34","£2,581.75","£3,051.16","£3,520.57","£4,224.68","£4,928.79",England +North Warwickshire,4.00%,"£1,475.79","£1,721.76","£1,967.72","£2,213.69","£2,705.62","£3,197.55","£3,689.48","£4,427.38","£5,165.28",England +North West Leicestershire,4.90%,"£1,420.59","£1,657.36","£1,894.12","£2,130.89","£2,604.42","£3,077.95","£3,551.48","£4,261.78","£4,972.08",England +Northumberland,4.92%,"£1,498.42","£1,748.15","£1,997.89","£2,247.63","£2,747.10","£3,246.58","£3,746.05","£4,495.26","£5,244.47",England +Norwich,4.75%,"£1,456.11","£1,698.80","£1,941.48","£2,184.17","£2,669.54","£3,154.91","£3,640.28","£4,368.34","£5,096.40",England +Nottingham,5.12%,"£1,607.77","£1,875.73","£2,143.69","£2,411.65","£2,947.57","£3,483.49","£4,019.42","£4,823.30","£5,627.18",England +Nuneaton and Bedworth,4.00%,"£1,457.48","£1,700.39","£1,943.30","£2,186.22","£2,672.05","£3,157.87","£3,643.70","£4,372.44","£5,101.18",England +Oadby and Wigston,4.92%,"£1,416.43","£1,652.50","£1,888.57","£2,124.64","£2,596.78","£3,068.92","£3,541.07","£4,249.28","£4,957.49",England +Oldham,4.31%,"£1,480.02","£1,726.69","£1,973.36","£2,220.03","£2,713.37","£3,206.71","£3,700.05","£4,440.06","£5,180.07",England +Oxford,4.81%,"£1,554.96","£1,814.12","£2,073.28","£2,332.44","£2,850.76","£3,369.08","£3,887.40","£4,664.88","£5,442.36",England +Pendle,4.08%,"£1,534.36","£1,790.08","£2,045.81","£2,301.54","£2,812.99","£3,324.45","£3,835.90","£4,603.08","£5,370.26",England +Peterborough,5.80%,"£1,308.94","£1,527.10","£1,745.25","£1,963.41","£2,399.72","£2,836.04","£3,272.35","£3,926.82","£4,581.29",England +Plymouth,5.15%,"£1,407.71","£1,642.32","£1,876.94","£2,111.56","£2,580.80","£3,050.03","£3,519.27","£4,223.12","£4,926.97",England +Portsmouth,5.23%,"£1,320.51","£1,540.59","£1,760.67","£1,980.76","£2,420.93","£2,861.10","£3,301.27","£3,961.52","£4,621.77",England +Preston,4.18%,"£1,508.07","£1,759.42","£2,010.76","£2,262.11","£2,764.80","£3,267.49","£3,770.18","£4,524.22","£5,278.26",England +Reading,5.19%,"£1,504.17","£1,754.86","£2,005.55","£2,256.25","£2,757.64","£3,259.03","£3,760.42","£4,512.50","£5,264.58",England +Redbridge,6.00%,"£1,317.17","£1,536.69","£1,756.22","£1,975.75","£2,414.81","£2,853.86","£3,292.92","£3,951.50","£4,610.08",England +Redcar and Cleveland,4.22%,"£1,467.65","£1,712.26","£1,956.87","£2,201.48","£2,690.70","£3,179.91","£3,669.13","£4,402.96","£5,136.79",England +Redditch,4.85%,"£1,391.46","£1,623.37","£1,855.28","£2,087.19","£2,551.01","£3,014.83","£3,478.65","£4,174.38","£4,870.11",England +Reigate and Banstead,3.30%,"£1,496.20","£1,745.56","£1,994.93","£2,244.30","£2,743.03","£3,241.77","£3,740.50","£4,488.60","£5,236.70",England +Ribble Valley,4.35%,"£1,398.15","£1,631.17","£1,864.19","£2,097.22","£2,563.27","£3,029.32","£3,495.37","£4,194.44","£4,893.51",England +Rochdale,5.15%,"£1,479.35","£1,725.91","£1,972.47","£2,219.03","£2,712.15","£3,205.26","£3,698.38","£4,438.06","£5,177.74",England +Rochford,4.12%,"£1,384.08","£1,614.76","£1,845.44","£2,076.12","£2,537.48","£2,998.84","£3,460.20","£4,152.24","£4,844.28",England +Rossendale,4.20%,"£1,473.88","£1,719.52","£1,965.17","£2,210.82","£2,702.11","£3,193.41","£3,684.70","£4,421.64","£5,158.58",England +Rother,5.24%,"£1,551.14","£1,809.66","£2,068.18","£2,326.71","£2,843.76","£3,360.80","£3,877.85","£4,653.42","£5,428.99",England +Rotherham,4.49%,"£1,417.39","£1,653.62","£1,889.86","£2,126.09","£2,598.55","£3,071.02","£3,543.48","£4,252.18","£4,960.88",England +Rugby,4.02%,"£1,443.98","£1,684.64","£1,925.30","£2,165.97","£2,647.30","£3,128.62","£3,609.95","£4,331.94","£5,053.93",England +Runnymede,3.29%,"£1,447.05","£1,688.22","£1,929.39","£2,170.57","£2,652.92","£3,135.27","£3,617.62","£4,341.14","£5,064.66",England +Rushcliffe,4.76%,"£1,540.35","£1,797.08","£2,053.80","£2,310.53","£2,823.98","£3,337.43","£3,850.88","£4,621.06","£5,391.24",England +Rushmoor,4.99%,"£1,345.41","£1,569.65","£1,793.88","£2,018.12","£2,466.59","£2,915.06","£3,363.53","£4,036.24","£4,708.95",England +Rutland,5.28%,"£1,614.39","£1,883.45","£2,152.51","£2,421.58","£2,959.71","£3,497.84","£4,035.97","£4,843.16","£5,650.35",England +Salford,5.16%,"£1,475.69","£1,721.63","£1,967.58","£2,213.53","£2,705.43","£3,197.32","£3,689.22","£4,427.06","£5,164.90",England +Sandwell,5.39%,"£1,286.73","£1,501.18","£1,715.63","£1,930.09","£2,359.00","£2,787.91","£3,216.82","£3,860.18","£4,503.54",England +Sefton,5.11%,"£1,486.74","£1,734.53","£1,982.32","£2,230.11","£2,725.69","£3,221.27","£3,716.85","£4,460.22","£5,203.59",England +Sevenoaks,4.95%,"£1,467.99","£1,712.66","£1,957.32","£2,201.99","£2,691.32","£3,180.65","£3,669.98","£4,403.98","£5,137.98",England +Sheffield,5.22%,"£1,443.90","£1,684.55","£1,925.20","£2,165.85","£2,647.15","£3,128.45","£3,609.75","£4,331.70","£5,053.65",England +Shropshire,5.05%,"£1,400.44","£1,633.85","£1,867.25","£2,100.66","£2,567.47","£3,034.29","£3,501.10","£4,201.32","£4,901.54",England +Slough,9.30%,"£1,351.49","£1,576.73","£1,801.98","£2,027.23","£2,477.73","£2,928.22","£3,378.72","£4,054.46","£4,730.20",England +Solihull,5.38%,"£1,258.89","£1,468.71","£1,678.52","£1,888.34","£2,307.97","£2,727.60","£3,147.23","£3,776.68","£4,406.13",England +South Cambridgeshire,5.53%,"£1,450.70","£1,692.48","£1,934.26","£2,176.05","£2,659.62","£3,143.18","£3,626.75","£4,352.10","£5,077.45",England +South Derbyshire,3.88%,"£1,355.08","£1,580.93","£1,806.77","£2,032.62","£2,484.31","£2,936.01","£3,387.70","£4,065.24","£4,742.78",England +South Gloucestershire,5.04%,"£1,460.87","£1,704.35","£1,947.83","£2,191.31","£2,678.27","£3,165.22","£3,652.18","£4,382.62","£5,113.06",England +South Hams,4.91%,"£1,507.46","£1,758.70","£2,009.94","£2,261.19","£2,763.68","£3,266.16","£3,768.65","£4,522.38","£5,276.11",England +South Holland,4.93%,"£1,357.48","£1,583.73","£1,809.97","£2,036.22","£2,488.71","£2,941.21","£3,393.70","£4,072.44","£4,751.18",England +South Kesteven,4.87%,"£1,343.65","£1,567.59","£1,791.53","£2,015.47","£2,463.35","£2,911.23","£3,359.12","£4,030.94","£4,702.76",England +South Norfolk,4.70%,"£1,435.65","£1,674.93","£1,914.20","£2,153.48","£2,632.03","£3,110.58","£3,589.13","£4,306.96","£5,024.79",England +South Oxfordshire,5.01%,"£1,493.43","£1,742.33","£1,991.23","£2,240.14","£2,737.95","£3,235.76","£3,733.57","£4,480.28","£5,226.99",England +South Ribble,3.79%,"£1,428.21","£1,666.24","£1,904.27","£2,142.31","£2,618.38","£3,094.45","£3,570.52","£4,284.62","£4,998.72",England +South Staffordshire,4.94%,"£1,344.93","£1,569.08","£1,793.23","£2,017.39","£2,465.70","£2,914.01","£3,362.32","£4,034.78","£4,707.24",England +South Tyneside,5.36%,"£1,393.87","£1,626.18","£1,858.50","£2,090.81","£2,555.43","£3,020.06","£3,484.68","£4,181.62","£4,878.56",England +Southampton,5.22%,"£1,372.24","£1,600.95","£1,829.65","£2,058.36","£2,515.77","£2,973.19","£3,430.60","£4,116.72","£4,802.84",England +Southwark,6.17%,"£1,128.61","£1,316.71","£1,504.82","£1,692.92","£2,069.12","£2,445.33","£2,821.53","£3,385.84","£3,950.15",England +Spelthorne,3.27%,"£1,467.86","£1,712.50","£1,957.14","£2,201.79","£2,691.08","£3,180.36","£3,669.65","£4,403.58","£5,137.51",England +St Albans,5.01%,"£1,395.99","£1,628.66","£1,861.32","£2,093.99","£2,559.32","£3,024.65","£3,489.98","£4,187.98","£4,885.98",England +St Helens,5.16%,"£1,382.45","£1,612.86","£1,843.27","£2,073.68","£2,534.50","£2,995.31","£3,456.13","£4,147.36","£4,838.59",England +Stafford,4.68%,"£1,341.23","£1,564.77","£1,788.31","£2,011.85","£2,458.93","£2,906.00","£3,353.08","£4,023.70","£4,694.32",England +Staffordshire Moorlands,4.53%,"£1,358.79","£1,585.26","£1,811.72","£2,038.19","£2,491.12","£2,944.05","£3,396.98","£4,076.38","£4,755.78",England +Stevenage,4.96%,"£1,383.96","£1,614.62","£1,845.28","£2,075.94","£2,537.26","£2,998.58","£3,459.90","£4,151.88","£4,843.86",England +Stockport,4.31%,"£1,489.77","£1,738.07","£1,986.36","£2,234.66","£2,731.25","£3,227.84","£3,724.43","£4,469.32","£5,214.21",England +Stroud,4.93%,"£1,436.65","£1,676.09","£1,915.54","£2,154.98","£2,633.86","£3,112.75","£3,591.63","£4,309.96","£5,028.29",England +Sunderland,3.69%,"£1,266.09","£1,477.10","£1,688.11","£1,899.13","£2,321.16","£2,743.19","£3,165.22","£3,798.26","£4,431.30",England +Surrey Heath,3.27%,"£1,498.56","£1,748.32","£1,998.08","£2,247.84","£2,747.36","£3,246.88","£3,746.40","£4,495.68","£5,244.96",England +Sutton,5.96%,"£1,365.59","£1,593.18","£1,820.78","£2,048.38","£2,503.58","£2,958.77","£3,413.97","£4,096.76","£4,779.55",England +Swale,4.99%,"£1,396.29","£1,629.01","£1,861.72","£2,094.44","£2,559.87","£3,025.30","£3,490.73","£4,188.88","£4,887.03",England +Swindon,5.26%,"£1,390.80","£1,622.60","£1,854.40","£2,086.20","£2,549.80","£3,013.40","£3,477.00","£4,172.40","£4,867.80",England +Tameside,5.16%,"£1,390.89","£1,622.70","£1,854.51","£2,086.33","£2,549.96","£3,013.59","£3,477.22","£4,172.66","£4,868.10",England +Tamworth,4.73%,"£1,341.96","£1,565.62","£1,789.28","£2,012.94","£2,460.26","£2,907.58","£3,354.90","£4,025.88","£4,696.86",England +Tandridge,3.30%,"£1,501.65","£1,751.93","£2,002.20","£2,252.48","£2,753.03","£3,253.58","£3,754.13","£4,504.96","£5,255.79",England +Teignbridge,5.19%,"£1,519.53","£1,772.79","£2,026.04","£2,279.30","£2,785.81","£3,292.32","£3,798.83","£4,558.60","£5,318.37",England +Telford and Wrekin,2.60%,"£1,290.40","£1,505.47","£1,720.53","£1,935.60","£2,365.73","£2,795.87","£3,226.00","£3,871.20","£4,516.40",England +Tendring,3.92%,"£1,332.40","£1,554.47","£1,776.53","£1,998.60","£2,442.73","£2,886.87","£3,331.00","£3,997.20","£4,663.40",England +Test Valley,5.08%,"£1,330.55","£1,552.30","£1,774.06","£1,995.82","£2,439.34","£2,882.85","£3,326.37","£3,991.64","£4,656.91",England +Tewkesbury,4.92%,"£1,352.61","£1,578.05","£1,803.48","£2,028.92","£2,479.79","£2,930.66","£3,381.53","£4,057.84","£4,734.15",England +Thanet,4.97%,"£1,448.78","£1,690.24","£1,931.70","£2,173.17","£2,656.10","£3,139.02","£3,621.95","£4,346.34","£5,070.73",England +Three Rivers,5.03%,"£1,399.03","£1,632.20","£1,865.37","£2,098.54","£2,564.88","£3,031.22","£3,497.57","£4,197.08","£4,896.59",England +Thurrock,9.44%,"£1,265.94","£1,476.93","£1,687.92","£1,898.91","£2,320.89","£2,742.87","£3,164.85","£3,797.82","£4,430.79",England +Tonbridge and Malling,4.96%,"£1,444.47","£1,685.22","£1,925.96","£2,166.71","£2,648.20","£3,129.69","£3,611.18","£4,333.42","£5,055.66",England +Torbay,5.13%,"£1,427.47","£1,665.38","£1,903.29","£2,141.20","£2,617.02","£3,092.84","£3,568.67","£4,282.40","£4,996.13",England +Torridge,5.15%,"£1,503.51","£1,754.10","£2,004.68","£2,255.27","£2,756.44","£3,257.61","£3,758.78","£4,510.54","£5,262.30",England +Tower Hamlets,4.02%,"£1,054.01","£1,229.68","£1,405.35","£1,581.02","£1,932.36","£2,283.69","£2,635.03","£3,162.04","£3,689.05",England +Trafford,5.18%,"£1,252.05","£1,460.73","£1,669.40","£1,878.08","£2,295.43","£2,712.78","£3,130.13","£3,756.16","£4,382.19",England +Tunbridge Wells,5.19%,"£1,423.45","£1,660.69","£1,897.93","£2,135.17","£2,609.65","£3,084.13","£3,558.62","£4,270.34","£4,982.06",England +Uttlesford,4.02%,"£1,363.11","£1,590.30","£1,817.48","£2,044.67","£2,499.04","£2,953.41","£3,407.78","£4,089.34","£4,770.90",England +Vale of White Horse,5.01%,"£1,487.07","£1,734.91","£1,982.75","£2,230.60","£2,726.29","£3,221.98","£3,717.67","£4,461.20","£5,204.73",England +Wakefield,5.20%,"£1,320.36","£1,540.42","£1,760.48","£1,980.54","£2,420.66","£2,860.78","£3,300.90","£3,961.08","£4,621.26",England +Walsall,3.56%,"£1,507.37","£1,758.59","£2,009.82","£2,261.05","£2,763.51","£3,265.96","£3,768.42","£4,522.10","£5,275.78",England +Waltham Forest,5.96%,"£1,370.37","£1,598.76","£1,827.15","£2,055.55","£2,512.34","£2,969.13","£3,425.92","£4,111.10","£4,796.28",England +Wandsworth,5.59%,£614.21,£716.57,£818.94,£921.31,"£1,126.05","£1,330.78","£1,535.52","£1,842.62","£2,149.72",England +Warrington,5.12%,"£1,379.95","£1,609.94","£1,839.93","£2,069.92","£2,529.90","£2,989.88","£3,449.87","£4,139.84","£4,829.81",England +Warwick,3.79%,"£1,429.54","£1,667.79","£1,906.05","£2,144.31","£2,620.82","£3,097.34","£3,573.85","£4,288.62","£5,003.39",England +Watford,4.90%,"£1,422.99","£1,660.15","£1,897.31","£2,134.48","£2,608.81","£3,083.14","£3,557.47","£4,268.96","£4,980.45",England +Waverley,3.41%,"£1,508.52","£1,759.94","£2,011.36","£2,262.78","£2,765.62","£3,268.46","£3,771.30","£4,525.56","£5,279.82",England +Wealden,4.93%,"£1,575.35","£1,837.91","£2,100.47","£2,363.03","£2,888.15","£3,413.26","£3,938.38","£4,726.06","£5,513.74",England +Welwyn Hatfield,4.99%,"£1,413.79","£1,649.42","£1,885.06","£2,120.69","£2,591.95","£3,063.22","£3,534.48","£4,241.38","£4,948.28",England +West Berkshire,5.19%,"£1,435.10","£1,674.28","£1,913.46","£2,152.65","£2,631.02","£3,109.38","£3,587.75","£4,305.30","£5,022.85",England +West Devon,5.21%,"£1,564.53","£1,825.28","£2,086.03","£2,346.79","£2,868.30","£3,389.81","£3,911.32","£4,693.58","£5,475.84",England +West Lancashire,4.27%,"£1,435.01","£1,674.17","£1,913.34","£2,152.51","£2,630.85","£3,109.18","£3,587.52","£4,305.02","£5,022.52",England +West Lindsey,4.94%,"£1,407.14","£1,641.66","£1,876.18","£2,110.71","£2,579.76","£3,048.80","£3,517.85","£4,221.42","£4,924.99",England +West Northamptonshire,5.15%,"£1,430.39","£1,668.78","£1,907.18","£2,145.58","£2,622.38","£3,099.17","£3,575.97","£4,291.16","£5,006.35",England +West Oxfordshire,4.98%,"£1,471.91","£1,717.23","£1,962.55","£2,207.87","£2,698.51","£3,189.14","£3,679.78","£4,415.74","£5,151.70",England +West Suffolk,4.36%,"£1,361.00","£1,587.83","£1,814.66","£2,041.50","£2,495.17","£2,948.83","£3,402.50","£4,083.00","£4,763.50",England +Westminster,5.54%,£609.19,£710.72,£812.25,£913.78,"£1,116.84","£1,319.90","£1,522.97","£1,827.56","£2,132.15",England +Wigan,5.19%,"£1,221.15","£1,424.68","£1,628.20","£1,831.73","£2,238.78","£2,645.83","£3,052.88","£3,663.46","£4,274.04",England +Wiltshire,5.53%,"£1,477.53","£1,723.78","£1,970.03","£2,216.29","£2,708.80","£3,201.31","£3,693.82","£4,432.58","£5,171.34",England +Winchester,5.04%,"£1,364.56","£1,591.99","£1,819.41","£2,046.84","£2,501.69","£2,956.55","£3,411.40","£4,093.68","£4,775.96",England +Windsor and Maidenhead,5.28%,"£1,069.23","£1,247.43","£1,425.63","£1,603.84","£1,960.25","£2,316.66","£2,673.07","£3,207.68","£3,742.29",England +Wirral,5.14%,"£1,438.71","£1,678.49","£1,918.27","£2,158.06","£2,637.63","£3,117.20","£3,596.77","£4,316.12","£5,035.47",England +Woking,3.28%,"£1,499.18","£1,749.04","£1,998.90","£2,248.77","£2,748.50","£3,248.22","£3,747.95","£4,497.54","£5,247.13",England +Wokingham,5.10%,"£1,438.23","£1,677.94","£1,917.64","£2,157.35","£2,636.76","£3,116.17","£3,595.58","£4,314.70","£5,033.82",England +Wolverhampton,5.34%,"£1,456.39","£1,699.12","£1,941.85","£2,184.58","£2,670.04","£3,155.50","£3,640.97","£4,369.16","£5,097.35",England +Worcester,4.89%,"£1,357.54","£1,583.80","£1,810.05","£2,036.31","£2,488.82","£2,941.34","£3,393.85","£4,072.62","£4,751.39",England +Worthing,4.93%,"£1,422.16","£1,659.19","£1,896.21","£2,133.24","£2,607.29","£3,081.35","£3,555.40","£4,266.48","£4,977.56",England +Wychavon,4.88%,"£1,334.93","£1,557.42","£1,779.91","£2,002.40","£2,447.38","£2,892.35","£3,337.33","£4,004.80","£4,672.27",England +Wyre,4.18%,"£1,434.57","£1,673.67","£1,912.76","£2,151.86","£2,630.05","£3,108.24","£3,586.43","£4,303.72","£5,021.01",England +Wyre Forest,4.97%,"£1,407.69","£1,642.30","£1,876.91","£2,111.53","£2,580.76","£3,049.99","£3,519.22","£4,223.06","£4,926.90",England +York,5.07%,"£1,306.42","£1,524.16","£1,741.89","£1,959.63","£2,395.10","£2,830.58","£3,266.05","£3,919.26","£4,572.47",England +Blaenau Gwent,3.96%,"£1,454.56","£1,696.98","£1,939.41","£2,181.84","£2,666.69","£3,151.55","£3,636.40","£4,363.68","£5,090.96",Wales +Bridgend,5.18%,"£1,368.27","£1,596.31","£1,824.35","£2,052.40","£2,508.49","£2,964.58","£3,420.67","£4,104.80","£4,788.93",Wales +Caerphilly,7.65%,"£1,128.87","£1,317.02","£1,505.16","£1,693.31","£2,069.60","£2,445.89","£2,822.18","£3,386.62","£3,951.06",Wales +Cardiff,4.59%,"£1,143.69","£1,334.31","£1,524.92","£1,715.54","£2,096.77","£2,478.00","£2,859.23","£3,431.08","£4,002.93",Wales +Carmarthenshire,7.23%,"£1,272.45","£1,484.52","£1,696.59","£1,908.67","£2,332.82","£2,756.97","£3,181.12","£3,817.34","£4,453.56",Wales +Ceredigion,7.37%,"£1,272.15","£1,484.18","£1,696.20","£1,908.23","£2,332.28","£2,756.33","£3,180.38","£3,816.46","£4,452.54",Wales +Conwy,8.91%,"£1,309.21","£1,527.41","£1,745.62","£1,963.82","£2,400.22","£2,836.63","£3,273.03","£3,927.64","£4,582.25",Wales +Denbighshire,4.01%,"£1,285.23","£1,499.43","£1,713.63","£1,927.84","£2,356.25","£2,784.66","£3,213.07","£3,855.68","£4,498.29",Wales +Flintshire,5.01%,"£1,270.93","£1,482.75","£1,694.58","£1,906.40","£2,330.04","£2,753.69","£3,177.33","£3,812.80","£4,448.27",Wales +Gwynedd,4.97%,"£1,324.80","£1,545.60","£1,766.40","£1,987.20","£2,428.80","£2,870.40","£3,312.00","£3,974.40","£4,636.80",Wales +Isle of Anglesey,5.02%,"£1,216.87","£1,419.68","£1,622.49","£1,825.30","£2,230.92","£2,636.54","£3,042.17","£3,650.60","£4,259.03",Wales +Merthyr Tydfil,5.12%,"£1,436.60","£1,676.03","£1,915.46","£2,154.90","£2,633.77","£3,112.63","£3,591.50","£4,309.80","£5,028.10",Wales +Monmouthshire,6.10%,"£1,306.63","£1,524.40","£1,742.17","£1,959.94","£2,395.48","£2,831.02","£3,266.57","£3,919.88","£4,573.19",Wales +Neath Port Talbot,4.98%,"£1,408.02","£1,642.69","£1,877.36","£2,112.03","£2,581.37","£3,050.71","£3,520.05","£4,224.06","£4,928.07",Wales +Newport,8.17%,"£1,141.93","£1,332.25","£1,522.58","£1,712.90","£2,093.54","£2,474.19","£2,854.83","£3,425.80","£3,996.77",Wales +Pembrokeshire,7.44%,"£1,130.71","£1,319.16","£1,507.62","£1,696.07","£2,072.97","£2,449.88","£2,826.78","£3,392.14","£3,957.50",Wales +Powys,5.50%,"£1,275.59","£1,488.19","£1,700.79","£1,913.39","£2,338.59","£2,763.78","£3,188.98","£3,826.78","£4,464.58",Wales +Rhondda Cynon Taf,4.55%,"£1,314.68","£1,533.79","£1,752.90","£1,972.02","£2,410.25","£2,848.47","£3,286.70","£3,944.04","£4,601.38",Wales +Swansea,6.23%,"£1,262.37","£1,472.76","£1,683.15","£1,893.55","£2,314.34","£2,735.13","£3,155.92","£3,787.10","£4,418.28",Wales +Torfaen,2.71%,"£1,234.37","£1,440.10","£1,645.83","£1,851.56","£2,263.02","£2,674.47","£3,085.93","£3,703.12","£4,320.31",Wales +Vale of Glamorgan,5.29%,"£1,229.25","£1,434.12","£1,638.99","£1,843.87","£2,253.62","£2,663.37","£3,073.12","£3,687.74","£4,302.36",Wales +Wrexham,5.84%,"£1,228.73","£1,433.51","£1,638.30","£1,843.09","£2,252.67","£2,662.24","£3,071.82","£3,686.18","£4,300.54",Scotland +Aberdeenshire,4.00%,£928.95,"£1,083.77","£1,238.59","£1,393.42","£1,703.07","£2,012.72","£2,322.37","£2,786.84","£3,251.31",Scotland +Angus,6.00%,£877.79,"£1,024.08","£1,170.38","£1,316.68","£1,609.28","£1,901.87","£2,194.47","£2,633.36","£3,072.25",Scotland +City of Edinburgh,5.00%,£965.13,"£1,125.98","£1,286.83","£1,447.69","£1,769.40","£2,091.11","£2,412.82","£2,895.38","£3,377.94",Scotland +Clackmannanshire,5.00%,£940.64,"£1,097.41","£1,254.19","£1,410.96","£1,724.51","£2,038.05","£2,351.60","£2,821.92","£3,292.24",Scotland +East Ayrshire,5.00%,£991.63,"£1,156.90","£1,322.17","£1,487.44","£1,817.98","£2,148.52","£2,479.07","£2,974.88","£3,470.69",Scotland +East Dunbartonshire,5.00%,£943.77,"£1,101.07","£1,258.36","£1,415.66","£1,730.25","£2,044.84","£2,359.43","£2,831.32","£3,303.21",Scotland +East Lothian,7.00%,£957.08,"£1,116.59","£1,276.11","£1,435.62","£1,754.65","£2,073.67","£2,392.70","£2,871.24","£3,349.78",Scotland +East Renfrewshire,6.00%,£943.48,"£1,100.73","£1,257.97","£1,415.22","£1,729.71","£2,044.21","£2,358.70","£2,830.44","£3,302.18",Scotland +Falkirk,7.00%,£909.21,"£1,060.75","£1,212.28","£1,363.82","£1,666.89","£1,969.96","£2,273.03","£2,727.64","£3,182.25",Scotland +Fife,5.00%,£923.45,"£1,077.36","£1,231.27","£1,385.18","£1,693.00","£2,000.81","£2,308.63","£2,770.36","£3,232.09",Scotland +Highland,4.00%,£951.45,"£1,110.03","£1,268.60","£1,427.18","£1,744.33","£2,061.48","£2,378.63","£2,854.36","£3,330.09",Scotland +Inverclyde,5.30%,£953.18,"£1,112.04","£1,270.91","£1,429.77","£1,747.50","£2,065.22","£2,382.95","£2,859.54","£3,336.13",Scotland +Midlothian,5.00%,"£1,009.82","£1,178.12","£1,346.43","£1,514.73","£1,851.34","£2,187.94","£2,524.55","£3,029.46","£3,534.37",Scotland +Moray,5.00%,£953.79,"£1,112.76","£1,271.72","£1,430.69","£1,748.62","£2,066.55","£2,384.48","£2,861.38","£3,338.28",Scotland +North Ayrshire,5.00%,£968.08,"£1,129.43","£1,290.77","£1,452.12","£1,774.81","£2,097.51","£2,420.20","£2,904.24","£3,388.28",Scotland +North Lanarkshire,5.00%,£880.52,"£1,027.27","£1,174.03","£1,320.78","£1,614.29","£1,907.79","£2,201.30","£2,641.56","£3,081.82",Scotland +Renfrewshire,6.00%,£957.45,"£1,117.02","£1,276.59","£1,436.17","£1,755.32","£2,074.47","£2,393.62","£2,872.34","£3,351.06",Scotland +Scottish Borders,5.00%,£904.07,"£1,054.75","£1,205.43","£1,356.11","£1,657.47","£1,958.82","£2,260.18","£2,712.22","£3,164.26",Scotland +Shetland Islands,4.50%,£840.41,£980.47,"£1,120.54","£1,260.61","£1,540.75","£1,820.88","£2,101.02","£2,521.22","£2,941.42",Scotland +South Ayrshire,5.00%,£968.77,"£1,130.23","£1,291.70","£1,453.16","£1,776.08","£2,099.01","£2,421.93","£2,906.32","£3,390.71",Scotland +South Lanarkshire,5.50%,£867.21,"£1,011.74","£1,156.27","£1,300.81","£1,589.88","£1,878.95","£2,168.02","£2,601.62","£3,035.22",Scotland +Stirling,7.00%,£987.67,"£1,152.28","£1,316.89","£1,481.50","£1,810.72","£2,139.94","£2,469.17","£2,963.00","£3,456.83",Scotland +West Dunbartonshire,5.00%,£932.65,"£1,088.09","£1,243.54","£1,398.98","£1,709.86","£2,020.75","£2,331.63","£2,797.96","£3,264.29",Scotland +West Lothian,5.80%,£927.31,"£1,081.86","£1,236.41","£1,390.96","£1,700.06","£2,009.16","£2,318.27","£2,781.92","£3,245.57",Scotland From 88d35b0fd12d59210b9130e147cc766f4707be3a Mon Sep 17 00:00:00 2001 From: RobertClay Date: Thu, 14 Mar 2024 23:02:36 +0000 Subject: [PATCH 205/229] typo in new csv colnames --- persistent_data/23_24_LA_council_tax_bands.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/persistent_data/23_24_LA_council_tax_bands.csv b/persistent_data/23_24_LA_council_tax_bands.csv index 924b5c5d..5461054f 100644 --- a/persistent_data/23_24_LA_council_tax_bands.csv +++ b/persistent_data/23_24_LA_council_tax_bands.csv @@ -1,4 +1,4 @@ -Area ,Increase,Band A,Band B,Band C,Band D,Band E,Band F,Band G,Band H,Band I,Region +Area,Increase,Band A,Band B,Band C,Band D,Band E,Band F,Band G,Band H,Band I,Region Adur,4.67%,"£1,478.26","£1,724.63","£1,971.01","£2,217.39","£2,710.14","£3,202.90","£3,695.65","£4,434.78","£5,173.91",England Amber Valley,4.14%,"£1,390.89","£1,622.70","£1,854.51","£2,086.33","£2,549.96","£3,013.59","£3,477.22","£4,172.66","£4,868.10",England Arun,4.93%,"£1,438.85","£1,678.66","£1,918.47","£2,158.28","£2,637.90","£3,117.51","£3,597.13","£4,316.56","£5,035.99",England From f0ab4f98ee4fedd31569036c02aa338db8272447 Mon Sep 17 00:00:00 2001 From: RobertClay Date: Thu, 14 Mar 2024 23:03:01 +0000 Subject: [PATCH 206/229] keep council tax variable and plot in handovers to check after baseline run. --- minos/data_generation/generate_composite_vars.py | 2 +- minos/validation/handovers.Rmd | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/minos/data_generation/generate_composite_vars.py b/minos/data_generation/generate_composite_vars.py index 5cf17a72..4686d7ab 100755 --- a/minos/data_generation/generate_composite_vars.py +++ b/minos/data_generation/generate_composite_vars.py @@ -231,7 +231,7 @@ def generate_hh_income(data): data = US_utils.inflation_adjustment(data, "hh_income") # now drop the intermediates - data.drop(labels=['hh_rent', 'hh_mortgage', 'council_tax', 'outgoings', 'hh_netinc', 'oecd_equiv', + data.drop(labels=['hh_rent', 'hh_mortgage', 'outgoings', 'hh_netinc', 'oecd_equiv', 'council_tax_lower', 'council_tax_upper', 'council_tax_draw'], axis=1, inplace=True) diff --git a/minos/validation/handovers.Rmd b/minos/validation/handovers.Rmd index aa2b8a95..2392a052 100644 --- a/minos/validation/handovers.Rmd +++ b/minos/validation/handovers.Rmd @@ -78,6 +78,8 @@ shall.we.save <- FALSE handover_boxplots(raw.dat, base.dat, 'hh_income') handover_lineplots(raw.dat, base.dat, "hh_income") +handover_boxplots(raw.dat, base.dat, 'council_tax') +handover_lineplots(raw.dat, base.dat, "council_tax") ``` ### Spaghetti From d935dc8c1b502b6d9939253d400c34f3ff876ed9 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Fri, 15 Mar 2024 10:26:18 +0000 Subject: [PATCH 207/229] WIP to get QALYs by subpopulation --- minos/outcomes/QALY_calculation.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/minos/outcomes/QALY_calculation.py b/minos/outcomes/QALY_calculation.py index cb98a7c5..35de3569 100644 --- a/minos/outcomes/QALY_calculation.py +++ b/minos/outcomes/QALY_calculation.py @@ -19,7 +19,7 @@ import minos.utils as utils -def aggregate_csv(filename, intervention, year, start_year): +def aggregate_csv(filename, intervention, year, start_year, subpop=None): """ Parameters @@ -32,10 +32,7 @@ def aggregate_csv(filename, intervention, year, start_year): Returns ------- - : vector - Vector of information about that specific run for that specific year. This is to be aggregated in the - Multiprocessing pool to generate a dataframe with each row corresponding to a specific run in a specific - intervention. + """ df = pd.read_csv(filename, low_memory=False) #if subset_func_string: @@ -122,7 +119,7 @@ def calculate_qaly(df): return df -def main(mode, intervention): +def main(mode, intervention, subpop=None): # set file directory file_dir = os.path.join('output/', mode, intervention) @@ -178,10 +175,19 @@ def main(mode, intervention): help="Which experiment are we calculating for?") parser.add_argument("-i", "--intervention", required=False, default="baseline", help="Is this a baseline or intervention run? Which intervention if intervention?") + parser.add_argument("-s", "--subpopulation", default=None, + help="Which subpopulation on which to calculate QALYs.") args = parser.parse_args() mode = args.mode intervention = args.intervention + subpop = args.subpopulation + + acceptable_subpopulations = ['UC_kids', 'age_groups'] + if subpop not in acceptable_subpopulations: + raise ValueError(f"Provided subpopulation not available for subsetting. " + f"See below for acceptable subpopulations: " + f"{acceptable_subpopulations}") - main(mode, intervention) + main(mode, intervention, subpop) From 26ddbeed0eb3d462e213307e38eb960e21882d26 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Fri, 15 Mar 2024 10:27:00 +0000 Subject: [PATCH 208/229] Added some diagnostic plots to handovers looking at council_tax --- minos/validation/handovers.Rmd | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/minos/validation/handovers.Rmd b/minos/validation/handovers.Rmd index 75686745..17cf385c 100644 --- a/minos/validation/handovers.Rmd +++ b/minos/validation/handovers.Rmd @@ -106,6 +106,13 @@ handover_lineplots(raw.dat, base.dat, "hh_income") # rm(raw.inc, base.inc, income.spag) ``` +## Council Tax + +```{r} +handover_boxplots(raw.dat, base.dat, 'council_tax') +handover_lineplots(raw.dat, base.dat, "council_tax") +``` + ## SF_12_MCS From 62f412e6fd5760ca0ce9ea8882b595bbb7419ec2 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 19 Mar 2024 11:52:29 +0000 Subject: [PATCH 209/229] Fixed age_bucket bins --- minos/utils.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/minos/utils.py b/minos/utils.py index aea38d4e..04121d62 100755 --- a/minos/utils.py +++ b/minos/utils.py @@ -150,9 +150,12 @@ def get_age_bucket(simulation_data): """ # Age buckets based on the file names - cut_bins = [-1, 16, 20, 25, 30, 45, 60, 75, 200] + cut_bins = [-1, 15, 19, 24, 29, 44, 59, 74, 200] cut_labels = ["0to15", "16to19", "20to24", "25to29", "30to44", "45to59", "60to74", "75plus"] - simulation_data.loc[:, "age_bucket"] = pd.cut(simulation_data['age'], bins=cut_bins, labels=cut_labels) + simulation_data.loc[:, "age_bucket"] = pd.cut(simulation_data['age'], + bins=cut_bins, + right=True, + labels=cut_labels) return simulation_data From 41f28c08aa5a7f6eafd64c5ef04cfc7a84741ef7 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 19 Mar 2024 14:42:02 +0000 Subject: [PATCH 210/229] Added a function whose only purpose is to arrange the columns in our population dataframe to make it easier to find important columns. I wrote this function fueled by pure anger and frustration from not having written this function 2 years ago --- minos/minosPipeline/RunPipeline.py | 48 ++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/minos/minosPipeline/RunPipeline.py b/minos/minosPipeline/RunPipeline.py index d9e73748..444c5561 100755 --- a/minos/minosPipeline/RunPipeline.py +++ b/minos/minosPipeline/RunPipeline.py @@ -404,6 +404,8 @@ def RunPipeline(config, intervention=None): # File name and save output_data_filename = get_output_data_filename(config) output_file_path = os.path.join(config.run_output_dir, output_data_filename) + # order columns for better outputs + pop = arrange_output_columns(pop, config) pop.to_csv(output_file_path) print("Saved initial data to: ", output_file_path) logging.info(f"Saved initial data to: {output_file_path}") @@ -432,6 +434,8 @@ def RunPipeline(config, intervention=None): output_data_filename = get_output_data_filename(config, year) output_file_path = os.path.join(config.run_output_dir, output_data_filename) + # Order output columns + pop = arrange_output_columns(pop, config) pop.to_csv(output_file_path) print("Saved data to: ", output_file_path) logging.info(f"Saved data to: {output_file_path}") @@ -466,3 +470,47 @@ def get_output_data_filename(config, year=0): output_data_filename += f"{config.time.start.year + year}.csv" return output_data_filename + + +def arrange_output_columns(data, config): + """ + The purpose of this function is to arrange the columns in the output dataframe to be in a more useful configuration, + where we can locate important columns more easily than just searching through the dataframe as we have been doing. + + The order I am planning on is: + - Unique ID columns (pidp, hidp) + - Time + - Demographic vars + #- Mortality associated vars (alive, cause of death, exit_time, years of life lost) + - Financial vars + - Pathways + - Outcome (SF12 vars or Equivalent Income) + - Secondary vars + + Parameters + ---------- + data + + Returns + ------- + + """ + # TODO: Organise this better so that sf12 and S7 vars are correctly specified. Did this quick so not complete. + + run_agnostic_vars = ['pidp', 'hidp', 'time', + 'age', 'sex', 'ethnicity', 'region', 'education_state', 'marital_status', 'hhsize', + #'alive', 'cause_of_death', 'exit_time', 'years_of_life_lost', + 'hh_income', 'hourly_wage', 'council_tax', + 'loneliness', 'housing_quality', 'neighbourhood_safety', 'ncigs', 'nutrition_quality', + 'auditc', 'active', + 'SF_12_PCS', 'SF_12_PCS_diff', 'SF_12_MCS', 'SF_12_MCS_diff'] + + sf12_specific = [] + + S7_specific = [] + + # Add specified vars in lists above to the start of the dataframe, and add in remaining cols at the end in their + # current order + data = data[run_agnostic_vars + [c for c in data.columns if c not in run_agnostic_vars]] + + return data \ No newline at end of file From 37856875b0da0801b44c423da887a724ec4447e7 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 19 Mar 2024 14:46:03 +0000 Subject: [PATCH 211/229] More combined targets for different combinations of SCP runs, and corresponding targets in the QALY.Makefile to analyse them --- minos/outcomes/QALY.Makefile | 6 +++--- scripts/SCP.Makefile | 10 ++++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/minos/outcomes/QALY.Makefile b/minos/outcomes/QALY.Makefile index f1bf7d68..64ac6c15 100644 --- a/minos/outcomes/QALY.Makefile +++ b/minos/outcomes/QALY.Makefile @@ -383,13 +383,13 @@ QALYs_UC_glasgow: QALY_vis_SCP_25_UC QALY_vis_SCP_50_UC QALY_vis_SCP_75_UC QALY_ QALY_vis_SCP_all_child_glasgow: EXPERIMENT=glasgow_scaled QALY_vis_SCP_all_child_glasgow: STARTYEAR=2020 -QALY_vis_SCP_all_child_glasgow: QALY_baseline QALY_SCP_25_All QALY_SCP_50_All QALY_SCP_100_All +QALY_vis_SCP_all_child_glasgow: QALY_baseline QALY_SCP_25_All QALY_SCP_50_All QALY_SCP_75_All QALY_SCP_100_All $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention=c('25All', '50All', '75All', '100All'), start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_all_child.html')" QALY_vis_SCP_UC_glasgow: EXPERIMENT=glasgow_scaled QALY_vis_SCP_UC_glasgow: STARTYEAR=2020 QALY_vis_SCP_UC_glasgow: QALY_baseline QALY_SCP_25_UC QALY_SCP_50_UC QALY_SCP_75_UC QALY_SCP_100_UC - $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention=c('25UniversalCredit', '50UniversalCredit', '75UniversalCredit', '100UniversalCredit'), start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_UC.html')" + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention=c('25UniversalCredit', '50UniversalCredit', '75UniversalCredit', '100UniversalCredit'), start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_UC_25_100.html')" QALY_vis_SCP_UC_glasgow_25_50: EXPERIMENT=glasgow_scaled QALY_vis_SCP_UC_glasgow_25_50: STARTYEAR=2020 @@ -402,7 +402,7 @@ QALY_vis_SCP_UC_glasgow_ALL: QALY_baseline QALY_SCP_25_UC QALY_SCP_30_UC QALY_SC QALY_vis_SCP_UC_glasgow_ALL: QALY_SCP_50_UC QALY_SCP_55_UC QALY_SCP_60_UC QALY_SCP_65_UC QALY_SCP_70_UC QALY_SCP_75_UC QALY_vis_SCP_UC_glasgow_ALL: QALY_SCP_80_UC QALY_SCP_85_UC QALY_SCP_90_UC QALY_SCP_95_UC QALY_SCP_100_UC QALY_SCP_105_UC QALY_vis_SCP_UC_glasgow_ALL: QALY_SCP_110_UC QALY_SCP_115_UC QALY_SCP_120_UC - $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention=c('25UniversalCredit', '30UniversalCredit', '35UniversalCredit', '40UniversalCredit', '45UniversalCredit', '50UniversalCredit', '55UniversalCredit', '60UniversalCredit', '65UniversalCredit', '70UniversalCredit', '75UniversalCredit', '80UniversalCredit', '85UniversalCredit', '90UniversalCredit', '95UniversalCredit', '100UniversalCredit', '105UniversalCredit', '110UniversalCredit', '115UniversalCredit', '120UniversalCredit'), start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_UC.html')" + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention=c('25UniversalCredit', '30UniversalCredit', '35UniversalCredit', '40UniversalCredit', '45UniversalCredit', '50UniversalCredit', '55UniversalCredit', '60UniversalCredit', '65UniversalCredit', '70UniversalCredit', '75UniversalCredit', '80UniversalCredit', '85UniversalCredit', '90UniversalCredit', '95UniversalCredit', '100UniversalCredit', '105UniversalCredit', '110UniversalCredit', '115UniversalCredit', '120UniversalCredit'), start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_UC_ALL.html')" QALY_vis_SCP_25_glasgow: EXPERIMENT=glasgow_scaled QALY_vis_SCP_25_glasgow: STARTYEAR=2020 diff --git a/scripts/SCP.Makefile b/scripts/SCP.Makefile index f04c4361..68e12688 100644 --- a/scripts/SCP.Makefile +++ b/scripts/SCP.Makefile @@ -45,6 +45,8 @@ intervention_100RelativePoverty: setup intervention_25UniversalCredit_scotland: setup_scotland_scaled $(PYTHON) scripts/run.py -c config/scotland_scaled.yaml -o scotland_scaled -i '25UniversalCredit' +intervention_25UniversalCredit_glasgow: setup_glasgow_scaled + $(PYTHON) scripts/run.py -c config/glasgow_scaled.yaml -o glasgow_scaled -i '25UniversalCredit' intervention_25UniversalCredit: setup $(PYTHON) scripts/run.py -c $(RUN_CONFIG) -o $(MODE) -i '25UniversalCredit' @@ -248,10 +250,10 @@ arc4_all_25_uplifts: arc4_baseline arc4_intervention_25RelativePoverty arc4_inte ##################################### arc4_qaly_SCPs: setup -arc4_qaly_SCPs: MODE=default_config -arc4_qaly_SCPs: RUN_CONFIG=$(CONFIG)/default.yaml -arc4_qaly_SCPs: arc4_baseline arc4_intervention_25All arc4_intervention_50All arc4_intervention_75All arc4_intervention_100All -arc4_qaly_SCPs: arc4_intervention_25UniversalCredit arc4_intervention_50UniversalCredit arc4_intervention_75UniversalCredit arc4_intervention_100UniversalCredit +arc4_qaly_SCPs: MODE=scotland_scaled +arc4_qaly_SCPs: RUN_CONFIG=$(CONFIG)/scotland_scaled.yaml +arc4_qaly_SCPs: arc4_baseline arc4_intervention_25UniversalCredit arc4_intervention_50UniversalCredit +arc4_qaly_SCPs: arc4_intervention_75UniversalCredit arc4_intervention_100UniversalCredit arc4_qaly_SCPs_UC_glasgow: setup_glasgow_scaled arc4_qaly_SCPs_UC_glasgow: MODE=glasgow_scaled From 248f1e2befb9b08a7d480436d1004cd6b7771f98 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 19 Mar 2024 14:49:08 +0000 Subject: [PATCH 212/229] Tweaks and small changes to the QALY_comparison script, and some updates to utils functions. Notably, have added an argument to the singular datain function to remove zero weight people for handovers. Has a noticeable difference so clearly important --- minos/outcomes/QALY_comparison2.Rmd | 12 +- minos/utils_SCP_vis.R | 420 ++++++++++++++++++++++++++++ minos/utils_datain.R | 6 +- minos/utils_qaly.R | 2 +- minos/utils_validation_vis.R | 8 +- 5 files changed, 433 insertions(+), 15 deletions(-) create mode 100644 minos/utils_SCP_vis.R diff --git a/minos/outcomes/QALY_comparison2.Rmd b/minos/outcomes/QALY_comparison2.Rmd index b9e654f1..d9d35c7e 100644 --- a/minos/outcomes/QALY_comparison2.Rmd +++ b/minos/outcomes/QALY_comparison2.Rmd @@ -50,12 +50,11 @@ source(here::here('minos', 'utils_qaly.R')) experiment <- params$experiment baseline <- params$base ints <- params$intervention -#intervention2 <- params$intervention2 start.year <- params$start.year -# # -# experiment <- 'glasgow_scaled' +# # +# experiment <- 'arc_batch_10' # baseline <- 'baseline' -# ints <- c('50All', '50UniversalCredit') +# ints <- c('25UniversalCredit', '50UniversalCredit', '75UniversalCredit', '100UniversalCredit') # start.year <- 2020 ``` @@ -169,11 +168,6 @@ ggplot(combined.diff, aes(x = year, y = total_boost, group = scenario, color = s # QALY comparison ```{r} -# QALY_comparison(base = base, -# base.name = baseline, -# int = int, -# int.name = intervention) - QALY_comparison(combined, ints) ``` diff --git a/minos/utils_SCP_vis.R b/minos/utils_SCP_vis.R new file mode 100644 index 00000000..af9b898f --- /dev/null +++ b/minos/utils_SCP_vis.R @@ -0,0 +1,420 @@ +## SCRIPT for holding plotting functions for SCP plot script. + + +SF12.change.full.sample.single <- function(df.list, scen.list) { + single.mean <- function(data, scen) { + newvar.name <- paste0(scen, '.SF12') + data <- data %>% + select(time, SF_12) %>% + filter(SF_12 != -8) %>% + group_by(time) %>% + summarise(!!sym(newvar.name) := mean(SF_12, na.rm =TRUE)) + return(data) + } + + df.list <- mapply(single.mean, df.list, scen.list, SIMPLIFY = FALSE) + + combined <- df.list %>% + reduce(full_join, by='time') + + # List of intervention columns + int_cols <- grep("int\\d+.SF12", names(combined), value = TRUE) + + # Create the new dataframe with differences + combined <- combined %>% + mutate(across(all_of(int_cols), ~ . - base.SF12, .names = "diff{col}")) %>% + select(time, starts_with("diff")) %>% + rename_with(~ str_replace_all(., "diffint(\\d+).SF12", "diff\\1")) + + combined <- combined %>% + pivot_longer(cols = starts_with('diff'), + names_to = 'scen', + values_to = 'diff') + + #return(combined) + + scen.list <- scen.list[-1] + + #TODO: Rename factor levels + #correct factor levels for plot + # combined$scen <- factor(combined$scen, + # levels = scen.list) + + #return(combined) + + p1 <- ggplot(combined, aes(x = time, y = diff, group = scen, colour = scen)) + + geom_line() + + geom_hline(yintercept = 0, linetype='dashed') + + labs(title = 'Relative change in SF12 MCS', subtitle = 'Whole population') + + xlab('Year') + + ylab('Difference') + + print(p1) +} + +SF12.change.full.sample.batch <- function(df.list, scen.list) { + batch.mean <- function(data, scen) { + newvar.name <- paste0(scen, '.SF12') + data <- data %>% + select(time, run_id, SF_12) %>% + filter(SF_12 != -8) %>% + group_by(time, run_id) %>% + summarise(!!sym(newvar.name) := mean(SF_12, na.rm =TRUE)) + return(data) + } + + df.list <- mapply(batch.mean, df.list, scen.list, SIMPLIFY = FALSE) + + #df.list <- list(base, int1, int2, int3, int4) + combined <- df.list %>% + reduce(full_join, by=c('time', 'run_id')) + + # List of intervention columns + int_cols <- grep("int\\d+.SF12", names(combined), value = TRUE) + + # Create the new dataframe with differences + combined <- combined %>% + mutate(across(all_of(int_cols), ~ . - base.SF12, .names = "diff{col}")) %>% + select(time, starts_with("diff")) %>% + rename_with(~ str_replace_all(., "diffint(\\d+).SF12", "diff\\1")) + + combined <- combined %>% + pivot_longer(cols = starts_with('diff'), + names_to = 'scen', + values_to = 'diff') + + # combined <- combined %>% + # select(time, run_id, contains('diff')) %>% + # pivot_longer(cols = diff25:diff100, + # names_to = 'scen', + # values_to = 'diff') + + # TODO: Rename factor levels + # correct factor levels for plot + # combined$scen <- factor(combined$scen, + # levels = c('diff100', 'diff75', 'diff50', 'diff25')) + + p1 <- ggplot(combined, aes(x = time, y = diff, group = scen, colour = scen)) + + geom_smooth() + + geom_hline(yintercept = 0, linetype='dashed') + + labs(title = 'Relative change in SF12 MCS', subtitle = 'Whole population') + + xlab('Year') + + ylab('Difference') + + print(p1) +} + +SF12.change.families.single <- function(df.list, scen.list) { + single.mean <- function(data, scen) { + newvar.name <- paste0(scen, '.SF12') + data <- data %>% + select(time, SF_12, nkids) %>% + filter(nkids > 0) %>% + filter(SF_12 != -8) %>% + group_by(time) %>% + summarise(!!sym(newvar.name) := mean(SF_12, na.rm =TRUE)) + return(data) + } + ########################################### CHANGE EVERYTHING FROM WEIGHTED MEAN TO MEAN ########################################### + + df.list <- mapply(single.mean, df.list, scen.list, SIMPLIFY = FALSE) + + combined <- df.list %>% + reduce(full_join, by='time') + + # List of intervention columns + int_cols <- grep("int\\d+.SF12", names(combined), value = TRUE) + + # Create the new dataframe with differences + combined <- combined %>% + mutate(across(all_of(int_cols), ~ . - base.SF12, .names = "diff{col}")) %>% + select(time, starts_with("diff")) %>% + rename_with(~ str_replace_all(., "diffint(\\d+).SF12", "diff\\1")) + + combined <- combined %>% + pivot_longer(cols = starts_with('diff'), + names_to = 'scen', + values_to = 'diff') + + p1 <- ggplot(combined, aes(x = time, y = diff, group = scen, colour = scen)) + + geom_line() + + geom_hline(yintercept = 0, linetype='dashed') + + labs(title = 'Relative change in SF12 MCS', subtitle = 'Households with Children') + + xlab('Year') + + ylab('Difference') + + print(p1) +} + +SF12.change.families.batch <- function(df.list, scen.list) { + batch.mean <- function(data, scen) { + newvar.name <- paste0(scen, '.SF12') + data <- data %>% + select(time, run_id, SF_12, nkids) %>% + filter(nkids > 0) %>% + filter(SF_12 != -8) %>% + group_by(time, run_id) %>% + summarise(!!sym(newvar.name) := mean(SF_12, na.rm =TRUE)) + return(data) + } + + df.list <- mapply(batch.mean, df.list, scen.list, SIMPLIFY = FALSE) + + combined <- df.list %>% + reduce(full_join, by=c('time', 'run_id')) + + # List of intervention columns + int_cols <- grep("int\\d+.SF12", names(combined), value = TRUE) + + # Create the new dataframe with differences + combined <- combined %>% + mutate(across(all_of(int_cols), ~ . - base.SF12, .names = "diff{col}")) %>% + select(time, starts_with("diff")) %>% + rename_with(~ str_replace_all(., "diffint(\\d+).SF12", "diff\\1")) + + combined <- combined %>% + pivot_longer(cols = starts_with('diff'), + names_to = 'scen', + values_to = 'diff') + + p1 <- ggplot(combined, aes(x = time, y = diff, group = scen, colour = scen)) + + geom_smooth() + + geom_hline(yintercept = 0, linetype='dashed') + + labs(title = 'Relative change in SF12 MCS', subtitle = 'Households with Children') + + xlab('Year') + + ylab('Difference') + + print(p1) +} + +SF12.change.treated.single <- function(df.list, scen.list) { + single.mean <- function(data, scen, boosted) { + # define varname + newvar.name <- paste0(scen, '.SF12') + # prepare data + data <- data %>% + select(time, SF_12) %>% + filter(boosted == TRUE) %>% + group_by(time) %>% + summarise(!!sym(newvar.name) := mean(SF_12, na.rm =TRUE)) + return(data) + } + + #TODO: FIX THIS! NEED DIFFERENT BASELINE FOR EACH INT?? THINK SO! + # NEED A FUNCTION TO DO THE DIFF CALCULATION AND ANOTHER TO RUN IT THROUGH + # EACH DF + # get boosted pidps + # boosted <- df.list[[2]] %>% + # select(pidp, income_boosted) %>% + # filter(income_boosted == 'True') + # rm(tmp) + + df.list <- mapply(single.mean, df.list, scen.list, SIMPLIFY = FALSE) + + combined <- df.list %>% + reduce(full_join, by='time') + + # List of intervention columns + int_cols <- grep("int\\d+.SF12", names(combined), value = TRUE) + + # Create the new dataframe with differences + combined <- combined %>% + mutate(across(all_of(int_cols), ~ . - base.SF12, .names = "diff{col}")) %>% + select(time, starts_with("diff")) %>% + rename_with(~ str_replace_all(., "diffint(\\d+).SF12", "diff\\1")) + + combined <- combined %>% + pivot_longer(cols = starts_with('diff'), + names_to = 'scen', + values_to = 'diff') + + p1 <- ggplot(combined, aes(x = time, y = diff, group = scen, colour = scen)) + + geom_line() + + geom_hline(yintercept = 0, linetype='dashed') + + labs(title = 'Relative change in SF12 MCS', subtitle = 'Treated Population') + + xlab('Year') + + ylab('Difference') + + print(p1) +} + +SF12.change.treated.batch <- function(df.list, scen.list) { + batch.mean <- function(data, scen) { + # define varname + newvar.name <- paste0(scen, '.SF12') + # prepare data + data <- data %>% + select(time, boosted, run_id, SF_12) %>% + filter(boosted == TRUE) %>% + group_by(time, run_id) %>% + summarise(!!sym(newvar.name) := mean(SF_12, na.rm =TRUE)) + return(data) + } + + #TODO: FIX THIS! NEED DIFFERENT BASELINE FOR EACH INT?? THINK SO! + # NEED A FUNCTION TO DO THE DIFF CALCULATION AND ANOTHER TO RUN IT THROUGH + # EACH DF + # get boosted pidps + # boosted <- df.list[[2]] %>% + # select(pidp, income_boosted) %>% + # filter(income_boosted == 'True') + + df.list <- mapply(batch.mean, df.list, scen.list, SIMPLIFY = FALSE) + + combined <- df.list %>% + reduce(full_join, by=c('time', 'run_id')) + + return(combined) + + # List of intervention columns + int_cols <- grep("int\\d+.SF12", names(combined), value = TRUE) + + # Create the new dataframe with differences + combined <- combined %>% + mutate(across(all_of(int_cols), ~ . - base.SF12, .names = "diff{col}")) %>% + select(time, starts_with("diff")) %>% + rename_with(~ str_replace_all(., "diffint(\\d+).SF12", "diff\\1")) + + combined <- combined %>% + pivot_longer(cols = starts_with('diff'), + names_to = 'scen', + values_to = 'diff') + + p1 <- ggplot(combined, aes(x = time, y = diff, group = scen, colour = scen)) + + geom_smooth() + + geom_hline(yintercept = 0, linetype='dashed') + + labs(title = 'Relative change in SF12 MCS', subtitle = 'Treated Population') + + xlab('Year') + + ylab('Difference') + + print(p1) +} + +SF12.by.simd.deciles <- function(df.list, scen.list, start.year = 2020, batch=FALSE) { + single.mean <- function(data, scen, start.year) { + newvar.name <- paste0(scen, '.SF12') + data <- data %>% + select(time, simd_decile, SF_12) %>% + filter(time >= start.year) %>% + group_by(time, simd_decile) %>% + summarise(!!sym(newvar.name) := mean(SF_12, na.rm =TRUE)) + return(data) + } + batch.mean <- function(data, scen, start.year) { + newvar.name <- paste0(scen, '.SF12') + data <- data %>% + select(time, simd_decile, run_id, SF_12) %>% + filter(time >= start.year) %>% + group_by(time, simd_decile, run_id) %>% + summarise(!!sym(newvar.name) := mean(SF_12)) + return(data) + } + + if (batch) { + mod.list <- mapply(batch.mean, int.list, scen.list, start.year, SIMPLIFY = FALSE) + combined <- mod.list %>% + reduce(full_join, by=c('time', 'simd_decile', 'run_id')) + } else { + mod.list <- mapply(single.mean, int.list, scen.list, start.year, SIMPLIFY = FALSE) + combined <- mod.list %>% + reduce(full_join, by=c('time', 'simd_decile')) + } + + combined$simd_decile <- as.factor(combined$simd_decile) + + int.labels <- scen.list[-1] + + for (int in int.labels) { + int.name <- paste0(int, '.SF12') + if (batch) { + p1 <- ggplot(combined, aes(x = time, group = simd_decile, colour = simd_decile)) + + geom_smooth(aes(y = base.SF12)) + + geom_smooth(aes(y = .data[[int.name]]), linetype='dashed') + + labs(title = 'SF12 MCS Deciles', subtitle = paste0('Baseline vs ', int)) + + xlab('Year') + + ylab('Difference') + } else { + p1 <- ggplot(combined, aes(x = time, group = simd_decile, colour = simd_decile)) + + geom_line(aes(y = base.SF12)) + + geom_line(aes(y = .data[[int.name]]), linetype='dashed') + + labs(title = 'SF12 MCS Deciles', subtitle = int) + + xlab('Year') + + ylab('Difference') + } + print(p1) + } +} + +SF12.change.by.deciles <- function(df.list, scen.list, start.year = 2020, batch = FALSE) { + single.mean <- function(data, scen, start.year) { + newvar.name <- paste0(scen, '.SF12') + data <- data %>% + select(time, simd_decile, SF_12) %>% + filter(time >= start.year) %>% + group_by(time, simd_decile) %>% + summarise(!!sym(newvar.name) := mean(SF_12, na.rm =TRUE)) + return(data) + } + batch.mean <- function(data, scen, start.year) { + newvar.name <- paste0(scen, '.SF12') + data <- data %>% + select(time, simd_decile, run_id, SF_12) %>% + filter(time >= start.year) %>% + group_by(time, simd_decile, run_id) %>% + summarise(!!sym(newvar.name) := mean(SF_12)) + return(data) + } + + if (batch) { + mod.list <- mapply(batch.mean, int.list, scen.list, start.year, SIMPLIFY = FALSE) + combined <- mod.list %>% + reduce(full_join, by=c('time', 'simd_decile', 'run_id')) + } else { + mod.list <- mapply(single.mean, int.list, scen.list, start.year, SIMPLIFY = FALSE) + combined <- mod.list %>% + reduce(full_join, by=c('time', 'simd_decile')) + } + + combined$simd_decile <- as.factor(combined$simd_decile) + + # List of intervention columns + int_cols <- grep("int\\d+.SF12", names(combined), value = TRUE) + + # Create the new dataframe with differences + combined <- combined %>% + mutate(across(all_of(int_cols), ~ . - base.SF12, .names = "diff{col}")) %>% + select(time, simd_decile, starts_with("diff")) %>% + rename_with(~ str_replace_all(., "diffint(\\d+).SF12", "diff\\1")) + + combined <- combined %>% + pivot_longer(cols = starts_with('diff'), + names_to = 'scen', + values_to = 'diff') + + if (batch) { + p1 <- ggplot(combined, aes(x = time, y = diff, group = simd_decile, colour = simd_decile)) + + geom_smooth() + + geom_hline(yintercept = 0, linetype='dashed') + + facet_wrap(. ~ scen, ncol=1) + + labs(title = 'Relative change in SF12 MCS', subtitle = 'Whole population') + + xlab('Year') + + ylab('Difference') + } else { + p1 <- ggplot(combined, aes(x = time, y = diff, group = simd_decile, colour = simd_decile)) + + geom_line() + + geom_hline(yintercept = 0, linetype='dashed') + + facet_wrap(. ~ scen, ncol=1) + + labs(title = 'Relative change in SF12 MCS', subtitle = 'Whole population') + + xlab('Year') + + ylab('Difference') + } + + ggsave(filename = 'test_plot.png', + plot = p1, + path = here::here('plots', 'SCOTGOVWORK'), + width = 9, + height = 20) + + print(p1) +} + diff --git a/minos/utils_datain.R b/minos/utils_datain.R index 34f74555..239a6ffd 100644 --- a/minos/utils_datain.R +++ b/minos/utils_datain.R @@ -13,7 +13,7 @@ create.if.not.exists <- function(path) { # Args: # out.path - path to top level output directory # scenario - string scenario name of which output files to read -read_singular_local_out <- function(out.path, scenario, drop.dead = FALSE) { +read_singular_local_out <- function(out.path, scenario, drop.dead = FALSE, drop.zero.weight = FALSE) { ## Start with scenario name # attach full output path # get runtime directory @@ -33,6 +33,10 @@ read_singular_local_out <- function(out.path, scenario, drop.dead = FALSE) { dat <- dat %>% filter(alive != 'dead') } + if(drop.zero.weight) { + dat <- dat %>% + filter(weight > 0) + } return(dat) } diff --git a/minos/utils_qaly.R b/minos/utils_qaly.R index eac1113d..0c85f9a3 100644 --- a/minos/utils_qaly.R +++ b/minos/utils_qaly.R @@ -80,7 +80,7 @@ QALY_comparison <- function(combined, ints) { lower = mean_QALY_change - margin, upper = mean_QALY_change + margin) - p3 <- ggplot(combined.QALY.change.confint, aes(x = year, y = mean_QALY_change, group = scenario, color = scenario, fill = scenario)) + + p3 <- ggplot(combined.QALY.change.confint, aes(x = year, y = mean_QALY_change, group = scenario, color = scenario)) + # fill = scenario geom_ribbon(aes(ymin = lower, ymax = upper)) + geom_line(color = 'black') + geom_hline(yintercept = 0, linetype = 'dashed') + diff --git a/minos/utils_validation_vis.R b/minos/utils_validation_vis.R index c15217cb..6a95ef11 100644 --- a/minos/utils_validation_vis.R +++ b/minos/utils_validation_vis.R @@ -571,12 +571,12 @@ q_q_comparison <- function(raw, cv, var) { handover_boxplots <- function(raw, baseline, var) { raw.var <- raw %>% - dplyr::select(pidp, time, all_of(var)) %>% + dplyr::select(time, all_of(var)) %>% dplyr::filter(!.data[[var]] %in% miss.values) raw.var$source <- 'final_US' baseline.var <- baseline %>% - dplyr::select(pidp, time, all_of(var)) %>% + dplyr::select(time, all_of(var)) %>% dplyr::filter(!.data[[var]] %in% miss.values) baseline.var$source <- 'baseline_output' @@ -600,14 +600,14 @@ handover_boxplots <- function(raw, baseline, var) { handover_lineplots <- function(raw, base, var) { # GENERALISE THIS AND DOCSTRING raw.means <- raw %>% - dplyr::select(pidp, time, var) %>% + dplyr::select(time, var) %>% dplyr::filter(!.data[[var]] %in% miss.values) %>% group_by(time) %>% summarise(summary_var = mean(.data[[var]], na.rm = TRUE)) %>% mutate(source = 'final_US') base.means <- base %>% - dplyr::select(pidp, time, var) %>% + dplyr::select(time, var) %>% dplyr::filter(!.data[[var]] %in% miss.values) %>% group_by(time) %>% summarise(summary_var = mean(!!sym(var))) %>% From 6f8c0fe9f95fe8716c6a2aee7bbe9d48168cdfe9 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 19 Mar 2024 14:50:46 +0000 Subject: [PATCH 213/229] Changed some targets to run for all Scotland instead of Glasgow --- minos/outcomes/QALY.Makefile | 4 ++++ scripts/SCP.Makefile | 10 +++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/minos/outcomes/QALY.Makefile b/minos/outcomes/QALY.Makefile index 64ac6c15..32c711d8 100644 --- a/minos/outcomes/QALY.Makefile +++ b/minos/outcomes/QALY.Makefile @@ -381,6 +381,10 @@ QALYs_UC_glasgow: EXPERIMENT=glasgow_scaled QALYs_UC_glasgow: STARTYEAR=2020 QALYs_UC_glasgow: QALY_vis_SCP_25_UC QALY_vis_SCP_50_UC QALY_vis_SCP_75_UC QALY_vis_SCP_100_UC +QALYs_UC_scotland: EXPERIMENT=scotland_scaled +QALYs_UC_scotland: STARTYEAR=2020 +QALYs_UC_scotland: QALY_vis_SCP_25_UC QALY_vis_SCP_50_UC QALY_vis_SCP_75_UC QALY_vis_SCP_100_UC + QALY_vis_SCP_all_child_glasgow: EXPERIMENT=glasgow_scaled QALY_vis_SCP_all_child_glasgow: STARTYEAR=2020 QALY_vis_SCP_all_child_glasgow: QALY_baseline QALY_SCP_25_All QALY_SCP_50_All QALY_SCP_75_All QALY_SCP_100_All diff --git a/scripts/SCP.Makefile b/scripts/SCP.Makefile index 68e12688..a6d66091 100644 --- a/scripts/SCP.Makefile +++ b/scripts/SCP.Makefile @@ -249,11 +249,11 @@ arc4_all_25_uplifts: arc4_baseline arc4_intervention_25RelativePoverty arc4_inte ## Arc4 QALY Combined Targets ##################################### -arc4_qaly_SCPs: setup -arc4_qaly_SCPs: MODE=scotland_scaled -arc4_qaly_SCPs: RUN_CONFIG=$(CONFIG)/scotland_scaled.yaml -arc4_qaly_SCPs: arc4_baseline arc4_intervention_25UniversalCredit arc4_intervention_50UniversalCredit -arc4_qaly_SCPs: arc4_intervention_75UniversalCredit arc4_intervention_100UniversalCredit +arc4_qaly_SCPs_Scotland: setup +arc4_qaly_SCPs_Scotland: MODE=scotland_scaled +arc4_qaly_SCPs_Scotland: RUN_CONFIG=$(CONFIG)/scotland_scaled.yaml +arc4_qaly_SCPs_Scotland: arc4_baseline arc4_intervention_25UniversalCredit arc4_intervention_50UniversalCredit +arc4_qaly_SCPs_Scotland: arc4_intervention_75UniversalCredit arc4_intervention_100UniversalCredit arc4_qaly_SCPs_UC_glasgow: setup_glasgow_scaled arc4_qaly_SCPs_UC_glasgow: MODE=glasgow_scaled From 407a9e162bb9ac4315f6eb9ea8412b4127da3e74 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 19 Mar 2024 16:13:08 +0000 Subject: [PATCH 214/229] Fixed combined target for producing the scotland scaled data --- Makefile | 2 +- minos/data_generation/Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 2c86b590..af5f944e 100644 --- a/Makefile +++ b/Makefile @@ -119,7 +119,7 @@ setup_glasgow_scaled: install synthetic_glasgow_data transitions_default synthet setup_glasgow_scaled_S7: install synthetic_glasgow_data transitions_SIPHER7 synthetic_glasgow_repl -setup_scotland_scaled: install synthetic_glasgow_data transitions_default synthetic_scotland_repl +setup_scotland_scaled: install synthetic_scotland_data transitions_default synthetic_scotland_repl setup_scotland_scaled_S7: install synthetic_glasgow_data transitions_SIPHER7 synthetic_scotland_repl diff --git a/minos/data_generation/Makefile b/minos/data_generation/Makefile index ad200c82..4067664d 100644 --- a/minos/data_generation/Makefile +++ b/minos/data_generation/Makefile @@ -9,7 +9,7 @@ data: raw_data corrected_data composite_data complete_data final_data synthetic_glasgow_data: data glasgow_scaled_data -synthetic_scotland_data: data scotland_scaled_data synthetic_scotland_repl +synthetic_scotland_data: data scotland_scaled_data synthetic_uk_data: data uk_scaled_data From 091c333cb27134807fff77a3afd479d889747907 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 19 Mar 2024 17:37:27 +0000 Subject: [PATCH 215/229] More WIP stuff for subpopulation qalys --- minos/outcomes/QALY_calculation.py | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/minos/outcomes/QALY_calculation.py b/minos/outcomes/QALY_calculation.py index 35de3569..f309e573 100644 --- a/minos/outcomes/QALY_calculation.py +++ b/minos/outcomes/QALY_calculation.py @@ -14,12 +14,12 @@ from multiprocessing import Pool from itertools import repeat import glob as glob -#from aggregate_subset_functions import dynamic_subset_function +from aggregate_subset_functions import dynamic_subset_function import minos.utils as utils -def aggregate_csv(filename, intervention, year, start_year, subpop=None): +def aggregate_csv(filename, intervention, year, start_year, subset_func_string=None): """ Parameters @@ -35,9 +35,9 @@ def aggregate_csv(filename, intervention, year, start_year, subpop=None): """ df = pd.read_csv(filename, low_memory=False) - #if subset_func_string: - #df = dynamic_subset_function(df, subset_func_string, mode) - #print(f"For substring chain {subset_func_string} there are {df.shape[0]} eligible individuals in the dataset.") + if subset_func_string: + df = dynamic_subset_function(df, subset_func_string, mode) + print(f"For substring chain {subset_func_string} there are {df.shape[0]} eligible individuals in the dataset.") # get the run_id from the filename and attach to the dataset (if batch run) filename_nopath = filename.split(sep='/')[-1] @@ -119,7 +119,7 @@ def calculate_qaly(df): return df -def main(mode, intervention, subpop=None): +def main(mode, intervention, subset_func_string=None): # set file directory file_dir = os.path.join('output/', mode, intervention) @@ -142,10 +142,12 @@ def main(mode, intervention, subpop=None): # aggregate the files using multiprocessing with Pool() as pool: - aggregated_means = pool.starmap(aggregate_csv, zip(files, repeat(intervention), repeat(year), repeat(start_year))) + aggregated_means = pool.starmap(aggregate_csv, zip(files, repeat(intervention), repeat(year), + repeat(start_year), repeat(subset_func_string))) new_df = pd.DataFrame(aggregated_means) - new_df.columns = ['run_id', 'alive_pop', 'dead_pop', 'total_pop_size', 'pop_boosted', 'total_boost', 'alive_ratio', 'SF_12_MCS', 'SF_12_PCS'] + new_df.columns = ['run_id', 'alive_pop', 'dead_pop', 'total_pop_size', 'pop_boosted', 'total_boost', + 'alive_ratio', 'SF_12_MCS', 'SF_12_PCS'] new_df['year'] = year new_df['intervention'] = intervention combined_output = pd.concat([combined_output, new_df]) @@ -175,19 +177,13 @@ def main(mode, intervention, subpop=None): help="Which experiment are we calculating for?") parser.add_argument("-i", "--intervention", required=False, default="baseline", help="Is this a baseline or intervention run? Which intervention if intervention?") - parser.add_argument("-s", "--subpopulation", default=None, + parser.add_argument("-s", "--subset", default=None, help="Which subpopulation on which to calculate QALYs.") args = parser.parse_args() mode = args.mode intervention = args.intervention - subpop = args.subpopulation + subset_func_string = args.subset - acceptable_subpopulations = ['UC_kids', 'age_groups'] - if subpop not in acceptable_subpopulations: - raise ValueError(f"Provided subpopulation not available for subsetting. " - f"See below for acceptable subpopulations: " - f"{acceptable_subpopulations}") - - main(mode, intervention, subpop) + main(mode, intervention, subset_func_string) From b4ec0ce08c5a40add8229871acc608a1b55b5e58 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 19 Mar 2024 17:42:56 +0000 Subject: [PATCH 216/229] Fixed a target for visualising qalys on all child intervention runs --- minos/outcomes/QALY.Makefile | 13 ++++++++++--- scripts/SCP.Makefile | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/minos/outcomes/QALY.Makefile b/minos/outcomes/QALY.Makefile index 32c711d8..dfb91459 100644 --- a/minos/outcomes/QALY.Makefile +++ b/minos/outcomes/QALY.Makefile @@ -288,19 +288,26 @@ QALY_vis_SCP_25_All: QALY_baseline QALY_SCP_25_All $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='25All', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_25_All.html')" #firefox file://$(OUTCOMES)/QALY_SCP_25_All.html -QALY_vis_SCP_50_All: EXPERIMENT=glasgow_scaled -QALY_vis_SCP_50_All: STARTYEAR=2020 + QALY_vis_SCP_50_All: QALY_baseline QALY_SCP_50_All $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='50All', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_50_All.html')" #firefox file://$(OUTCOMES)/QALY_SCP_50_All.html +QALY_vis_SCP_50_All: QALY_baseline QALY_SCP_75_All + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='75All', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_75_All.html')" + QALY_vis_SCP_100_All: QALY_baseline QALY_SCP_100_All $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='100All', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_100_All.html')" #firefox file://$(OUTCOMES)/QALY_SCP_100_All.html +QALY_vis_SCP_25_100_All: EXPERIMENT=default_config +QALY_vis_SCP_25_100_All: STARTYEAR=2021 +QALY_vis_SCP_25_100_All: QALY_baseline QALY_SCP_25_All QALY_SCP_50_All QALY_SCP_75_All QALY_SCP_100_All + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention=c('25All', '50All', '75All', '100All'), start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_25_100_All.html')" + QALYs_all_child: EXPERIMENT=default_config QALYs_all_child: STARTYEAR=2021 -QALYs_all_child: QALY_vis_SCP_25_All QALY_vis_SCP_50_All QALY_vis_SCP_100_All +QALYs_all_child: QALY_vis_SCP_25_All QALY_vis_SCP_50_All QALY_vis_SCP_75_All QALY_vis_SCP_100_All QALYs_all_child_glasgow: EXPERIMENT=glasgow_scaled QALYs_all_child_glasgow: STARTYEAR=2020 diff --git a/scripts/SCP.Makefile b/scripts/SCP.Makefile index a6d66091..e0800740 100644 --- a/scripts/SCP.Makefile +++ b/scripts/SCP.Makefile @@ -249,7 +249,7 @@ arc4_all_25_uplifts: arc4_baseline arc4_intervention_25RelativePoverty arc4_inte ## Arc4 QALY Combined Targets ##################################### -arc4_qaly_SCPs_Scotland: setup +arc4_qaly_SCPs_Scotland: setup_scotland_scaled arc4_qaly_SCPs_Scotland: MODE=scotland_scaled arc4_qaly_SCPs_Scotland: RUN_CONFIG=$(CONFIG)/scotland_scaled.yaml arc4_qaly_SCPs_Scotland: arc4_baseline arc4_intervention_25UniversalCredit arc4_intervention_50UniversalCredit From 03a84caad8a81e090937cb853f555b50f406a081 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 19 Mar 2024 17:45:58 +0000 Subject: [PATCH 217/229] Fix a duplicated target --- minos/outcomes/QALY.Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/minos/outcomes/QALY.Makefile b/minos/outcomes/QALY.Makefile index dfb91459..d3e5d676 100644 --- a/minos/outcomes/QALY.Makefile +++ b/minos/outcomes/QALY.Makefile @@ -293,7 +293,7 @@ QALY_vis_SCP_50_All: QALY_baseline QALY_SCP_50_All $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='50All', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_50_All.html')" #firefox file://$(OUTCOMES)/QALY_SCP_50_All.html -QALY_vis_SCP_50_All: QALY_baseline QALY_SCP_75_All +QALY_vis_SCP_75_All: QALY_baseline QALY_SCP_75_All $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention='75All', start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_75_All.html')" QALY_vis_SCP_100_All: QALY_baseline QALY_SCP_100_All From 5a47a80eca67ab855c735777ab787b74e0a4f4a3 Mon Sep 17 00:00:00 2001 From: ld-archer Date: Thu, 21 Mar 2024 12:08:40 +0000 Subject: [PATCH 218/229] Added 80:20 inequality ratios for continuous variables --- minos/utils_validation_vis.R | 30 ++++++++++++++++++++++++++++-- minos/validation/handovers.Rmd | 25 ++++++++++++++++++++++--- 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/minos/utils_validation_vis.R b/minos/utils_validation_vis.R index 6a95ef11..3761ab7f 100644 --- a/minos/utils_validation_vis.R +++ b/minos/utils_validation_vis.R @@ -138,7 +138,7 @@ handover_ordinal <- function(raw.dat, base.dat, var, save=FALSE) { group_by(time, .data[[var]]) %>% count() %>% mutate(source = 'baseline_output') - + raw.var[[var]] <- as.factor(raw.var[[var]]) base.var[[var]] <- as.factor(base.var[[var]]) @@ -583,7 +583,7 @@ handover_boxplots <- function(raw, baseline, var) { combined <- rbind(raw.var, baseline.var) combined$time <- as.factor(combined$time) combined <- drop_na(combined) - + if (var %in% c('hh_income', 'equivalent_income')) { combined <- filter(combined, .data[[var]] < quantile(.data[[var]], 0.99), .data[[var]] > quantile(.data[[var]], 0.01)) } else if (var %in% c('ncigs', 'hourly_wage')) { @@ -624,3 +624,29 @@ handover_lineplots <- function(raw, base, var) { xlab('Year') + ylab(var) } + +handover_inequality_80_20 <- function(raw.dat, base.dat, var) { + raw.income_inequality <- raw.dat %>% + group_by(time) %>% + summarise(percentile_20 = quantile(.data[[var]], probs = c(.20)), + percentile_80 = quantile(.data[[var]], probs = c(.80))) %>% + mutate(ineq_80_20 = percentile_80 - percentile_20, + source = 'raw') + + base.income_inequality <- base.dat %>% + group_by(time) %>% + summarise(percentile_20 = quantile(.data[[var]], probs = c(.20)), + percentile_80 = quantile(.data[[var]], probs = c(.80))) %>% + mutate(ineq_80_20 = percentile_80 - percentile_20, + source = 'simulated') + + combined <- rbind(raw.income_inequality, base.income_inequality) + + p1 <- ggplot(combined, aes(x = time, y = ineq_80_20, group = source, color = source)) + + geom_line() + + geom_vline(xintercept = start.year, linetype='dashed') + + labs(title = paste0('80:20 Ratio ', var)) + + xlab('Year') + + ylab('80:20 Ratio') + print(p1) +} diff --git a/minos/validation/handovers.Rmd b/minos/validation/handovers.Rmd index 3e41a9e0..a29cf47a 100644 --- a/minos/validation/handovers.Rmd +++ b/minos/validation/handovers.Rmd @@ -1,4 +1,5 @@ --- + title: "MINOS Handover Plots" output: html_document: @@ -55,7 +56,6 @@ raw.dat <- do.call(rbind, lapply(raw.files, read.csv)) out.path <- here::here('output', 'default_config/') base.dat <- read_singular_local_out(out.path, 'baseline', drop.dead = TRUE) -#base.dat <- read_singular_local_out(out.path, 'goodHeatingDummy', drop.dead = TRUE) ``` ## Constants @@ -78,8 +78,6 @@ shall.we.save <- FALSE handover_boxplots(raw.dat, base.dat, 'hh_income') handover_lineplots(raw.dat, base.dat, "hh_income") -handover_boxplots(raw.dat, base.dat, 'council_tax') -handover_lineplots(raw.dat, base.dat, "council_tax") ``` ### Spaghetti @@ -108,6 +106,13 @@ handover_lineplots(raw.dat, base.dat, "council_tax") # rm(raw.inc, base.inc, income.spag) ``` +### Income Inequality + +```{r} +handover_inequality_80_20(raw.dat, base.dat, var = 'hh_income') +``` + + ## Council Tax ```{r} @@ -149,6 +154,13 @@ density_ridges(sf12.m.spag, "SF_12_MCS", rm(raw.sf12.m, base.sf12.m, sf12.m.spag, sf12.m.spag.sample) ``` +### Inequality + +```{r} +handover_inequality_80_20(raw.dat, base.dat, var = 'SF_12_MCS') +``` + + ## SF_12_PCS ```{r} @@ -177,6 +189,13 @@ density_ridges(sf12.p.spag, "SF_12_PCS", rm(raw.sf12.p, base.sf12.p, sf12.p.spag) ``` +### Inequality + +```{r} +handover_inequality_80_20(raw.dat, base.dat, var = 'SF_12_PCS') +``` + + ### Histograms I have a hypothesis that the longer we run through simulation, the more simulants are predicted a negative PCS value which is then clipped to a 0. Histograms over a few snapshots of time should show if this is happening. From 5a961bfc4d8f405d11b5f62cfcb063df96e1640e Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 8 Apr 2024 10:41:49 +0100 Subject: [PATCH 219/229] Added handovers plots for age structure and population counts --- minos/validation/handovers.Rmd | 45 ++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/minos/validation/handovers.Rmd b/minos/validation/handovers.Rmd index 3e41a9e0..3c9d52bf 100644 --- a/minos/validation/handovers.Rmd +++ b/minos/validation/handovers.Rmd @@ -53,9 +53,8 @@ source(here::here('minos', 'validation', 'utils.r')) raw.files <- list.files(here::here('data', 'final_US'), pattern='[0-9]{4}_US_cohort.csv', full.names = TRUE) raw.dat <- do.call(rbind, lapply(raw.files, read.csv)) -out.path <- here::here('output', 'default_config/') -base.dat <- read_singular_local_out(out.path, 'baseline', drop.dead = TRUE) -#base.dat <- read_singular_local_out(out.path, 'goodHeatingDummy', drop.dead = TRUE) +out.path <- here::here('output', 'glasgow_scaled/') +base.dat <- read_singular_local_out(out.path, 'baseline', drop.dead = TRUE, drop.zero.weight = TRUE) ``` ## Constants @@ -68,6 +67,44 @@ create.if.not.exists(save.path) shall.we.save <- FALSE ``` +# Age Structure + +```{r} +# assign to age_bucket in raw data +cut_bins <- c(-1, 15, 19, 24, 29, 44, 59, 74, 200) +cut_labels <- c("0to15", "16to19", "20to24", "25to29", "30to44", "45to59", "60to74", "75plus") + +raw.dat$age_bucket <- cut(raw.dat$age, + breaks = cut_bins, + labels = cut_labels) + +handover_ordinal(raw.dat, base.dat, var = 'age_bucket', save = shall.we.save) +``` + +# Population Counts + +```{r} +raw.alive <- raw.dat %>% + group_by(time) %>% + summarise(n = n()) + +base.alive <- base.dat %>% + group_by(time) %>% + summarise(n = n()) + +#handover_boxplots(raw.alive, base.alive, 'n') +handover_lineplots(raw.alive, base.alive, "n") + +rm(raw.alive, base.alive) +``` + +# Universal Credit + +```{r} +handover_ordinal(raw.dat, base.dat, var = 'universal_credit', save = shall.we.save) +``` + + # PATHWAYS ## Income @@ -78,8 +115,6 @@ shall.we.save <- FALSE handover_boxplots(raw.dat, base.dat, 'hh_income') handover_lineplots(raw.dat, base.dat, "hh_income") -handover_boxplots(raw.dat, base.dat, 'council_tax') -handover_lineplots(raw.dat, base.dat, "council_tax") ``` ### Spaghetti From d97f8d1eed993848305653f7c978df6590825f7e Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 8 Apr 2024 11:44:22 +0100 Subject: [PATCH 220/229] Tweaked combined targets so that each runs the correct setup target --- scripts/SCP.Makefile | 57 ++++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/scripts/SCP.Makefile b/scripts/SCP.Makefile index e0800740..e1513c9b 100644 --- a/scripts/SCP.Makefile +++ b/scripts/SCP.Makefile @@ -9,32 +9,32 @@ RUN_CONFIG=$(CONFIG)/default.yaml # Child Uplifts Over Amount and Condition # ########################################### -intervention_25All: setup +intervention_25All: $(PYTHON) scripts/run.py -c $(RUN_CONFIG) -o $(MODE) -i '25All' -intervention_50All: setup +intervention_50All: $(PYTHON) scripts/run.py -c $(RUN_CONFIG) -o $(MODE) -i '50All' -intervention_75All: setup +intervention_75All: $(PYTHON) scripts/run.py -c $(RUN_CONFIG) -o $(MODE) -i '75All' -intervention_100All: setup +intervention_100All: $(PYTHON) scripts/run.py -c $(RUN_CONFIG) -o $(MODE) -i '100All' ########################################################################## # Relative Poverty Interventions. ########################################################################## -intervention_25RelativePoverty: setup +intervention_25RelativePoverty: $(PYTHON) scripts/run.py -c $(RUN_CONFIG) -o $(MODE) -i '25RelativePoverty' -intervention_50RelativePoverty: setup +intervention_50RelativePoverty: $(PYTHON) scripts/run.py -c $(RUN_CONFIG) -o $(MODE) -i '50RelativePoverty' -intervention_75RelativePoverty: setup +intervention_75RelativePoverty: $(PYTHON) scripts/run.py -c $(RUN_CONFIG) -o $(MODE) -i '75RelativePoverty' -intervention_100RelativePoverty: setup +intervention_100RelativePoverty: $(PYTHON) scripts/run.py -c $(RUN_CONFIG) -o $(MODE) -i '100RelativePoverty' ########################################################################## @@ -48,44 +48,44 @@ intervention_25UniversalCredit_scotland: setup_scotland_scaled intervention_25UniversalCredit_glasgow: setup_glasgow_scaled $(PYTHON) scripts/run.py -c config/glasgow_scaled.yaml -o glasgow_scaled -i '25UniversalCredit' -intervention_25UniversalCredit: setup +intervention_25UniversalCredit: $(PYTHON) scripts/run.py -c $(RUN_CONFIG) -o $(MODE) -i '25UniversalCredit' -intervention_30UniversalCredit: setup +intervention_30UniversalCredit: $(PYTHON) scripts/run.py -c $(RUN_CONFIG) -o $(MODE) -i '30UniversalCredit' -intervention_35UniversalCredit: setup +intervention_35UniversalCredit: $(PYTHON) scripts/run.py -c $(RUN_CONFIG) -o $(MODE) -i '35UniversalCredit' -intervention_40UniversalCredit: setup +intervention_40UniversalCredit: $(PYTHON) scripts/run.py -c $(RUN_CONFIG) -o $(MODE) -i '40UniversalCredit' -intervention_45UniversalCredit: setup +intervention_45UniversalCredit: $(PYTHON) scripts/run.py -c $(RUN_CONFIG) -o $(MODE) -i '45UniversalCredit' -intervention_50UniversalCredit: setup +intervention_50UniversalCredit: $(PYTHON) scripts/run.py -c $(RUN_CONFIG) -o $(MODE) -i '50UniversalCredit' -intervention_75UniversalCredit: setup +intervention_75UniversalCredit: $(PYTHON) scripts/run.py -c $(RUN_CONFIG) -o $(MODE) -i '75UniversalCredit' -intervention_100UniversalCredit: setup +intervention_100UniversalCredit: $(PYTHON) scripts/run.py -c $(RUN_CONFIG) -o $(MODE) -i '100UniversalCredit' ########################################################################## # Priority Subgroups ########################################################################## -intervention_25Priority: setup +intervention_25Priority: $(PYTHON) scripts/run.py -c $(RUN_CONFIG) -o $(MODE) -i '25Priority' -intervention_50Priority: setup +intervention_50Priority: $(PYTHON) scripts/run.py -c $(RUN_CONFIG) -o $(MODE) -i '50Priority' -intervention_75Priority: setup +intervention_75Priority: $(PYTHON) scripts/run.py -c $(RUN_CONFIG) -o $(MODE) -i '75Priority' -intervention_100Priority: setup +intervention_100Priority: $(PYTHON) scripts/run.py -c $(RUN_CONFIG) -o $(MODE) -i '100Priority' @@ -107,16 +107,16 @@ universal_credit_child_uplifts: baseline intervention_25UniversalCredit interven ## Running MINOS scenarios on Arc4 ##################################### -arc4_intervention_25All: setup +arc4_intervention_25All: bash scripts/arc_submit.sh -c $(RUN_CONFIG) -o $(MODE) -i '25All' -arc4_intervention_50All: setup +arc4_intervention_50All: bash scripts/arc_submit.sh -c $(RUN_CONFIG) -o $(MODE) -i '50All' -arc4_intervention_75All: setup +arc4_intervention_75All: bash scripts/arc_submit.sh -c $(RUN_CONFIG) -o $(MODE) -i '75All' -arc4_intervention_100All: setup +arc4_intervention_100All: bash scripts/arc_submit.sh -c $(RUN_CONFIG) -o $(MODE) -i '100All' ########################################################################## @@ -213,34 +213,39 @@ arc4_intervention_100Priority: setup ## Arc4 Combined Targets ##################################### +arc4_all_scenarios: setup arc4_all_scenarios: MODE=default_config arc4_all_scenarios: RUN_CONFIG=$(CONFIG)/default.yaml arc4_all_scenarios: arc4_baseline arc4_intervention_hhIncomeChildUplift arc4_intervention_PovertyLineChildUplift arc4_intervention_livingWage arc4_intervention_energyDownLift arc4_intervention_energyDownLiftNoSupport +arc4_all_child_uplifts: setup arc4_all_child_uplifts: MODE=default_config #MODE=scaled_glasgow arc4_all_child_uplifts: RUN_CONFIG=$(CONFIG)/default.yaml #/glasgow_scaled.yaml arc4_all_child_uplifts: arc4_baseline arc4_intervention_25All arc4_intervention_50All arc4_intervention_75All arc4_intervention_100All +arc4_poverty_line_child_uplifts: setup arc4_poverty_line_child_uplifts: MODE=default_config arc4_poverty_line_child_uplifts: RUN_CONFIG=$(CONFIG)/default.yaml arc4_poverty_line_child_uplifts: arc4_baseline arc4_intervention_25RelativePoverty arc4_intervention_50RelativePoverty arc4_intervention_75RelativePoverty arc4_intervention_100RelativePoverty -arc4_scotland_universal_credit_child_uplifts: +arc4_scotland_universal_credit_child_uplifts: setup_scotland_scaled arc4_scotland_universal_credit_child_uplifts: MODE=scaled_scotland #MODE=default_config arc4_scotland_universal_credit_child_uplifts: RUN_CONFIG=$(CONFIG)/scotland_scaled.yaml#/default.yaml arc4_scotland_universal_credit_child_uplifts: arc4_baseline arc4_intervention_25UniversalCredit arc4_intervention_30UniversalCredit arc4_intervention_35UniversalCredit arc4_intervention_40UniversalCredit arc4_intervention_45UniversalCredit arc4_intervention_50UniversalCredit -arc4_universal_credit_child_uplifts: +arc4_universal_credit_child_uplifts: setup arc4_universal_credit_child_uplifts: MODE=default_config arc4_universal_credit_child_uplifts: RUN_CONFIG=$(CONFIG)/default.yaml arc4_universal_credit_child_uplifts: arc4_baseline arc4_intervention_25UniversalCredit arc4_intervention_30UniversalCredit arc4_intervention_35UniversalCredit arc4_intervention_40UniversalCredit arc4_intervention_45UniversalCredit arc4_intervention_50UniversalCredit +arc4_priority_child_uplifts: setup_glasgow_scaled arc4_priority_child_uplifts: MODE=scaled_glasgow #MODE=default_config arc4_priority_child_uplifts: RUN_CONFIG=$(CONFIG)/glasgow_scaled.yaml#/default.yaml arc4_priority_child_uplifts: arc4_scotland_baseline arc4_intervention_25Priority arc4_intervention_50Priority arc4_intervention_75Priority arc4_intervention_100Priority +arc4_all_25_uplifts: setup arc4_all_25_uplifts: MODE=default_config arc4_all_25_uplifts: RUN_CONFIG=$(CONFIG)/default.yaml arc4_all_25_uplifts: arc4_baseline arc4_intervention_25RelativePoverty arc4_intervention_25All arc4_intervention_25UniversalCredit From fb4f753ad9caad20d9e4e9b41d1bf5b949f5a8f3 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 8 Apr 2024 14:59:07 +0100 Subject: [PATCH 221/229] Got council tax working again with some plots in handovers to check it --- minos/data_generation/Makefile | 3 +-- minos/data_generation/fake_council_tax.py | 16 +++++++++++++--- .../generate_composite_vars.py | 3 +-- minos/validation/handovers.Rmd | 19 +++++++++++++++++-- 4 files changed, 32 insertions(+), 9 deletions(-) diff --git a/minos/data_generation/Makefile b/minos/data_generation/Makefile index 4067664d..a29187ac 100644 --- a/minos/data_generation/Makefile +++ b/minos/data_generation/Makefile @@ -5,7 +5,6 @@ data: ### Run all four levels of data generation from raw Understanding Society data to imputed data in the correct ### format with composite variables generated data: raw_data corrected_data composite_data complete_data final_data -# adj_raw_data synthetic_glasgow_data: data glasgow_scaled_data @@ -82,7 +81,7 @@ $(RAWDATA)/2021_US_cohort.csv: $(DATAGEN)/US_format_raw.py $(DATAGEN)/US_utils.p $(ADJRAWDATA)/2021_US_cohort.csv: $(RAWDATA)/2021_US_cohort.csv $(DATAGEN)/fake_council_tax.py $(DATAGEN)/US_utils.py $(PERSISTJSON)/*.json $(PYTHON) $(DATAGEN)/fake_council_tax.py -$(CORRECTDATA)/2021_US_cohort.csv: $(RAWDATA)/2021_US_cohort.csv $(DATAGEN)/US_missing_main.py $(DATAGEN)/US_utils.py $(DATAGEN)/US_missing_deterministic.py $(DATAGEN)/US_missing_LOCF.py $(DATAGEN)/US_missing_description.py $(DATAGEN)/US_missing_data_correction.py $(PERSISTJSON)/*.json +$(CORRECTDATA)/2021_US_cohort.csv: $(ADJRAWDATA)/2021_US_cohort.csv $(DATAGEN)/US_missing_main.py $(DATAGEN)/US_utils.py $(DATAGEN)/US_missing_deterministic.py $(DATAGEN)/US_missing_LOCF.py $(DATAGEN)/US_missing_description.py $(DATAGEN)/US_missing_data_correction.py $(PERSISTJSON)/*.json $(PYTHON) $(DATAGEN)/US_missing_main.py $(COMPOSITEDATA)/2021_US_cohort.csv: $(RAWDATA)/2021_US_cohort.csv $(CORRECTDATA)/2021_US_cohort.csv $(DATAGEN)/US_utils.py $(DATAGEN)/generate_composite_vars.py $(PERSISTJSON)/*.json diff --git a/minos/data_generation/fake_council_tax.py b/minos/data_generation/fake_council_tax.py index fa2530e3..154f33a0 100755 --- a/minos/data_generation/fake_council_tax.py +++ b/minos/data_generation/fake_council_tax.py @@ -63,6 +63,7 @@ def random_draw(x): # TODO can use pretty much any distribution you want here so long as it draws between these bounds. return np.random.uniform(lb, ub) + def main(data): # yearly band D council tax percentage increases from 2010/2011 to 2023/2024 @@ -82,7 +83,7 @@ def main(data): # reverse back to correct time order. yearly_percentage_changes = yearly_percentage_changes[::-1] # convert to dict to allow mapping year in data to percentage change in council tax band. - yearly_percentage_changes = dict(zip(range(2009,2024), yearly_percentage_changes)) + yearly_percentage_changes = dict(zip(range(2009, 2024), yearly_percentage_changes)) # data for conversions between LADs to region and council tax band (A, B,...) to numeric boundaries (£1200-£1400) #https://www.completelymoved.co.uk/money/advice/council-tax-bands-table-of-all-uk-regions-compared @@ -113,7 +114,7 @@ def main(data): data["council_tax_lower"] = data.apply(lambda x: lower_bound(x, ct_bands), axis=1) # establish individual lower and upper bounds. data["council_tax_upper"] = data.apply(lambda x: upper_bound(x, ct_bands), axis=1) data["council_tax_draw"] = data.apply(random_draw, axis=1) # draw randomly between these bounds. - data['council_tax_draw'] = data['council_tax_draw']/12 # convert to monthly bill. + data['council_tax_draw'] = data['council_tax_draw'] / 12 # convert to monthly bill. data['yearly_council_tax_change'] = data['time'].replace(yearly_percentage_changes) data['council_tax_draw'] = data['council_tax_draw'] * data['yearly_council_tax_change'] @@ -121,13 +122,22 @@ def main(data): # Handle single case where someone in London gave has wrong council tax band (Band I which only exists in Wales) data['council_tax_draw'][data['council_tax_draw'].isna()] = -9 data['council_tax'] = data['council_tax_draw'] + + # Remove intermediate columns + # now drop the intermediates + data.drop(labels=['council_tax_lower', 'council_tax_upper', 'council_tax_draw'], + axis=1, + inplace=True) + return data + if __name__ == '__main__': + maxyr = US_utils.get_data_maxyr() years = np.arange(1991, maxyr) file_names = [f"data/raw_US/{item}_US_cohort.csv" for item in years] data = US_utils.load_multiple_data(file_names) main(data) print('Finished generating council_tax_data. Saving data...') - US_utils.save_multiple_files(data, years, "data/adj_raw_US/", "") \ No newline at end of file + US_utils.save_multiple_files(data, years, "data/adj_raw_US/", "") diff --git a/minos/data_generation/generate_composite_vars.py b/minos/data_generation/generate_composite_vars.py index 26439a50..71865da2 100755 --- a/minos/data_generation/generate_composite_vars.py +++ b/minos/data_generation/generate_composite_vars.py @@ -231,8 +231,7 @@ def generate_hh_income(data): data = US_utils.inflation_adjustment(data, "hh_income") # now drop the intermediates - data.drop(labels=['hh_rent', 'hh_mortgage', 'outgoings', 'hh_netinc', 'oecd_equiv', - 'council_tax_lower', 'council_tax_upper', 'council_tax_draw'], + data.drop(labels=['hh_rent', 'hh_mortgage', 'outgoings', 'hh_netinc', 'oecd_equiv'], axis=1, inplace=True) diff --git a/minos/validation/handovers.Rmd b/minos/validation/handovers.Rmd index c44abfde..6b19aa5b 100644 --- a/minos/validation/handovers.Rmd +++ b/minos/validation/handovers.Rmd @@ -54,8 +54,8 @@ source(here::here('minos', 'validation', 'utils.r')) raw.files <- list.files(here::here('data', 'final_US'), pattern='[0-9]{4}_US_cohort.csv', full.names = TRUE) raw.dat <- do.call(rbind, lapply(raw.files, read.csv)) -out.path <- here::here('output', 'glasgow_scaled/') -#out.path <- here::here('output', 'default_config/') +#out.path <- here::here('output', 'glasgow_scaled/') +out.path <- here::here('output', 'default_config/') base.dat <- read_singular_local_out(out.path, 'baseline', drop.dead = TRUE) ``` @@ -83,6 +83,21 @@ raw.dat$age_bucket <- cut(raw.dat$age, handover_ordinal(raw.dat, base.dat, var = 'age_bucket', save = shall.we.save) ``` +## Max Age + +```{r} +raw.maxAge <- raw.dat %>% + group_by(time) %>% + summarise(max_age = max(age)) + +base.maxAge <- base.dat %>% + group_by(time) %>% + summarise(max_age = max(age)) + +handover_lineplots(raw.maxAge, base.maxAge, 'max_age') +``` + + # Population Counts ```{r} From 17121b08dca3c90db1ab9453cb11ffac6dc64f12 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 8 Apr 2024 15:44:13 +0100 Subject: [PATCH 222/229] Change year range of education model to 2020-2021 --- minos/transitions/estimate_transitions.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/minos/transitions/estimate_transitions.R b/minos/transitions/estimate_transitions.R index 1c7e6739..ecda95af 100644 --- a/minos/transitions/estimate_transitions.R +++ b/minos/transitions/estimate_transitions.R @@ -160,7 +160,7 @@ run_yearly_models <- function(transitionDir_path, # nutrition_quality only estimated for 2018 if(dependent == 'nutrition_quality' & !year %in% c(2014, 2016, 2018)) { next } # labour_state only estimated for 2018 - if(dependent == 'education_state' & year != 2018) { next } + if(dependent == 'education_state' & year != 2020) { next } # loneliness only estimated for waves starting 2017 and 2018 if(dependent == 'loneliness' & !year > 2016) { next } # neighbourhood only estimated for wave 2011, 2014, and 2017 From 1054e910442c459945f1fb3097e87baf8df7364e Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Thu, 11 Apr 2024 11:09:55 +0100 Subject: [PATCH 223/229] Improved QALY_comparison visualisation functions and script --- minos/outcomes/QALY_comparison2.Rmd | 118 +++++++++++++++++++++++----- minos/utils_qaly.R | 24 ++++-- minos/utils_validation_vis.R | 2 +- 3 files changed, 116 insertions(+), 28 deletions(-) diff --git a/minos/outcomes/QALY_comparison2.Rmd b/minos/outcomes/QALY_comparison2.Rmd index d9d35c7e..72311200 100644 --- a/minos/outcomes/QALY_comparison2.Rmd +++ b/minos/outcomes/QALY_comparison2.Rmd @@ -19,12 +19,12 @@ output: ```{r "setup", include=FALSE} -require(tidyverse) -require(ggplot2) -require(knitr) -require(here) -require(MESS) -require(scales) +library(tidyverse) +library(ggplot2) +library(knitr) +library(here) +library(MESS) +library(scales) #workingDir <- "/home/luke/Documents/WORK/MINOS/PLOTS_FROM_ARC/QALY_SCP_28Feb/QALY_analysis_SCP_test/" #workingDir <- "/home/luke/Documents/WORK/MINOS/PLOTS_FROM_ARC/tmp/" @@ -52,10 +52,13 @@ baseline <- params$base ints <- params$intervention start.year <- params$start.year # # -# experiment <- 'arc_batch_10' +#experiment <- 'default_batch_10' +# experiment <- 'default_config' # baseline <- 'baseline' -# ints <- c('25UniversalCredit', '50UniversalCredit', '75UniversalCredit', '100UniversalCredit') -# start.year <- 2020 +# #ints <- c('25UniversalCredit', '50UniversalCredit', '75UniversalCredit', '100UniversalCredit') +# ints <- c('25All', '50All', '75All', '100All') +# #ints <- c('25All', '50All', '100All') +# start.year <- 2021 ``` @@ -99,13 +102,20 @@ for (expt in expts) { TESTING TEMPORARY TEMPORARY TEMPORARY TEMPORARY TEMPORARY TEMPORARY TEMPORARY ```{r} -# base <- read.csv('qalys_base.csv') -# int <- read.csv('qalys_25All.csv') -# -# combined <- rbind(base, int) +# out.path <- here::here('output', 'tmp_down2/') # # baseline <- 'baseline' -# intervention <- '25All' +# ints <- c('25All', '50All', '75All', '100All') +# +# start.year <- 2021 +# +# expts <- c(baseline, ints) +# +# combined <- data.frame() +# for (expt in expts) { +# data <- read.csv(paste0(out.path, expt, '_qalys.csv')) +# combined <- rbind(combined, data) +# } ``` TESTING TEMPORARY TEMPORARY TEMPORARY TEMPORARY TEMPORARY TEMPORARY TEMPORARY @@ -118,19 +128,29 @@ TESTING TEMPORARY TEMPORARY TEMPORARY TEMPORARY TEMPORARY TEMPORARY TEMPORARY # Intervention Information -Just going to plot some things here like how many people affected by an intervention and what the mean / total boost amounts look like. +## Boosted Population ```{r} combined.pop_boosted <- combined %>% - group_by(year, run_id, intervention) %>% - summarise(pop_boosted = sum(pop_boosted)) + group_by(year, intervention, run_id) %>% + summarise(pop_boosted = sum(pop_boosted, na.rm = TRUE)) # print(paste("There are", sum(is.na(combined.pop_boosted$pop_boosted)), "NA pop_boosted values in combined.pop.boosted.", sep = ' ')) -ggplot(combined.pop_boosted, aes(x = year, y = pop_boosted, group = intervention, colour = intervention)) + - geom_smooth() + +combined.pop_boosted.boxplot <- combined.pop_boosted %>% + filter(year != start.year) %>% + filter(intervention != baseline) + +ggplot(combined.pop_boosted.boxplot, aes(x = year, y = pop_boosted, group = interaction(year, intervention), color = intervention)) + + geom_boxplot() + labs(title = 'Number Individuals Receiving Intervention') +``` + +## Boost Amount + +```{r} + combined.diff <- combined %>% select(run_id, year, intervention, total_boost) %>% pivot_wider(names_from = 'intervention', @@ -172,6 +192,66 @@ QALY_comparison(combined, ints) ``` +```{r} +# #QALY_comparison <- function(combined, ints) { +# +# p1 <- ggplot(data = combined, aes(x = year, y = QALYs, group = intervention, colour = intervention)) + +# geom_smooth() + +# labs(title = 'QALYs per year') +# print(p1) +# +# # Now change from baseline +# combined.QALY.change <- combined %>% +# select(run_id, year, intervention, QALYs) %>% +# pivot_wider(names_from = 'intervention', +# values_from = 'QALYs') %>% +# #mutate(QALYs = .data[[int.name]] - .data[[base.name]]) %>% +# mutate(across(all_of(ints), ~.x - .data[[baseline]], .names = "QALYs_{.col}")) %>% +# select(run_id, year, contains('QALYs')) %>% +# pivot_longer(cols = contains('QALYs'), +# names_prefix = 'QALYs_', +# names_to = 'scenario', +# values_to = 'QALYs') +# +# p2 <- ggplot(data = combined.QALY.change, aes(x = year, y = QALYs, group = scenario, color = scenario, fill = scenario)) + +# geom_hline(yintercept = 0, linetype = 'dashed') + +# geom_smooth() + +# labs(title = 'QALY change') +# print(p2) +# +# combined.QALY.change$year <- as.factor(combined.QALY.change$year) +# combined.QALY.change$scenario <- as.factor(combined.QALY.change$scenario) +# +# p3 <- ggplot(data = combined.QALY.change, aes(x = year, y = QALYs, group = interaction(year, scenario), fill = scenario)) + +# geom_boxplot() + +# stat_summary(fun=mean, geom="line", aes(group=scenario), linetype = 'dashed') + +# stat_summary(fun=mean, geom="point", aes(group=scenario)) +# print(p3) +# +# +# +# combined.QALY.change.confint <- combined.QALY.change %>% +# group_by(year, scenario) %>% +# summarise(n = n(), +# mean_QALY_change = mean(QALYs), +# margin = qt(0.975, df = n - 1) * (sd(QALYs) / sqrt(n)), +# lower = mean_QALY_change - margin, +# upper = mean_QALY_change + margin) +# +# p4 <- ggplot(combined.QALY.change.confint, aes(x = year, y = mean_QALY_change, group = scenario, color = scenario, fill = scenario)) + # +# geom_ribbon(aes(ymin = lower, ymax = upper), linetype = 'dotted', alpha = 0.5) + +# geom_line(aes(color = scenario), linetype = 'dashed') + +# geom_hline(yintercept = 0, linetype = 'dashed') + +# labs(title = 'QALY Change') + +# xlab('Year') + +# ylab('QALYs') +# print(p4) +# +# +# #} +``` + + ## SF12 Change ```{r} diff --git a/minos/utils_qaly.R b/minos/utils_qaly.R index 0c85f9a3..e3dacde0 100644 --- a/minos/utils_qaly.R +++ b/minos/utils_qaly.R @@ -72,6 +72,14 @@ QALY_comparison <- function(combined, ints) { labs(title = 'QALY change') print(p2) + p3 <- ggplot(data = combined.QALY.change, aes(x = year, y = QALYs, group = interaction(year, scenario), fill = scenario)) + + geom_boxplot() + + stat_summary(fun=mean, geom="line", aes(group=scenario), linetype = 'dashed') + + stat_summary(fun=mean, geom="point", aes(group=scenario)) + + xlab('Year') + + ylab('QALYs') + print(p3) + combined.QALY.change.confint <- combined.QALY.change %>% group_by(year, scenario) %>% summarise(n = n(), @@ -80,14 +88,14 @@ QALY_comparison <- function(combined, ints) { lower = mean_QALY_change - margin, upper = mean_QALY_change + margin) - p3 <- ggplot(combined.QALY.change.confint, aes(x = year, y = mean_QALY_change, group = scenario, color = scenario)) + # fill = scenario - geom_ribbon(aes(ymin = lower, ymax = upper)) + - geom_line(color = 'black') + + p4 <- ggplot(combined.QALY.change.confint, aes(x = year, y = mean_QALY_change, group = scenario, color = scenario, fill = scenario)) + # + geom_ribbon(aes(ymin = lower, ymax = upper), linetype = 'dotted', alpha = 0.5) + + geom_line(aes(color = scenario), linetype = 'dashed') + geom_hline(yintercept = 0, linetype = 'dashed') + labs(title = 'QALY Change') + xlab('Year') + ylab('QALYs') - print(p3) + print(p4) } @@ -175,16 +183,16 @@ sf12.plots <- function(combined, ints) { margin = qt(0.975, df = n - 1) * (sd(difference) / sqrt(n))) # 95% confidence intervals p5 <- ggplot(filter(combined.small, SF12 == 'MCS'), aes(x = year, y = mean_diff, group = scenario, color = scenario, fill = scenario)) + - geom_ribbon(aes(ymin = mean_diff - margin, ymax = mean_diff + margin), fill = 'grey70') + - geom_line() + + geom_ribbon(aes(ymin = mean_diff - margin, ymax = mean_diff + margin), linetype = 'dotted', alpha = 0.5) + + geom_line(aes(color = scenario), linetype = 'dashed') + geom_hline(yintercept = 0, linetype = 'dashed') + labs(title = 'Change in SF_12_MCS') + xlab('Year') + ylab('Change in MCS') p6 <- ggplot(filter(combined.small, SF12 == 'PCS'), aes(x = year, y = mean_diff, group = scenario, color = scenario, fill = scenario)) + - geom_ribbon(aes(ymin = mean_diff - margin, ymax = mean_diff + margin), fill = 'grey70') + - geom_line() + + geom_ribbon(aes(ymin = mean_diff - margin, ymax = mean_diff + margin), linetype = 'dotted', alpha = 0.5) + + geom_line(aes(color = scenario), linetype = 'dashed') + geom_hline(yintercept = 0, linetype = 'dashed') + labs(title = 'Change in SF_12_PCS') + xlab('Year') + diff --git a/minos/utils_validation_vis.R b/minos/utils_validation_vis.R index 3761ab7f..0cf39bfa 100644 --- a/minos/utils_validation_vis.R +++ b/minos/utils_validation_vis.R @@ -591,7 +591,7 @@ handover_boxplots <- function(raw, baseline, var) { combined <- filter(combined, .data[[var]] < quantile(.data[[var]], 0.99), !.data[[var]] == 0) } - ggplot(data = combined, aes(x = time, y = .data[[var]], group = interaction(time, source), fill= source)) + + ggplot(data = combined, aes(x = time, y = .data[[var]], group = interaction(time, source), fill = source)) + geom_boxplot(notch=TRUE) + labs(title = paste0(var, ': Yearly box plots')) } From 526a458e653b95ea58bac6510092017af9d6841e Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Thu, 18 Apr 2024 07:41:40 +0100 Subject: [PATCH 224/229] Added plots to handovers for visualising counts of extreme old age groups. Also started an R Notebook for the PCS QALY work --- minos/testing/PCS_QALYs.Rmd | 71 +++++++++++++++++++++++++ minos/utils_validation_vis.R | 4 +- minos/validation/handovers.Rmd | 96 ++++++++++++++++++++++++++++++++++ 3 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 minos/testing/PCS_QALYs.Rmd diff --git a/minos/testing/PCS_QALYs.Rmd b/minos/testing/PCS_QALYs.Rmd new file mode 100644 index 00000000..2acf13e5 --- /dev/null +++ b/minos/testing/PCS_QALYs.Rmd @@ -0,0 +1,71 @@ +--- +title: "Physical Component Score and QALYs" +output: + html_document: + toc: true + toc_float: true + collapsed: false + number_sections: true + df_print: paged + code_folding: hide +--- + +# Introduction + + + +```{r "setup-environment", include=False} +require(tidyverse) +require(ggplot2) +require(ggridges) # for densities over time plots. +require(knitr) +require(here) +require(dplyr) +require(tidyr) +require(purrr) +require(gghighlight) + +#workingDir <- "/home/luke/Documents/WORK/MINOS/" +workingDir <- normalizePath('../') +knitr::opts_knit$set(root.dir = workingDir) +knitr::opts_chunk$set( + warning = FALSE, + message = FALSE +) +rm(workingDir) +``` + +```{r "setup-utils", include=False} +source(here::here('minos', 'utils_datain.R')) +source(here::here('minos', 'utils_validation_vis.R')) +source(here::here('minos', 'validation', 'utils.r')) +source(here::here('minos', 'utils_qaly.R')) +``` + +```{r "setup-parameters", include=False} +#experiment <- 'glasgow_scaled' +#baseline <- 'baseline' +#ints <- c('50All', '50UniversalCredit') +#start.year <- 2020 +``` + +```{r "setup-data", include=False} +# Read raw datafiles in +raw.files <- list.files(here::here('data', 'final_US'), pattern='[0-9]{4}_US_cohort.csv', full.names = TRUE) +raw.dat <- do.call(rbind, lapply(raw.files, read.csv)) + +#out.path <- here::here('output', 'glasgow_scaled/') +out.path <- here::here('output', 'default_config/') +base.dat <- read_singular_local_out(out.path, 'baseline', drop.dead = TRUE) +``` + +# Tran + +```{r} + +``` + + + + + diff --git a/minos/utils_validation_vis.R b/minos/utils_validation_vis.R index 0cf39bfa..83bf796b 100644 --- a/minos/utils_validation_vis.R +++ b/minos/utils_validation_vis.R @@ -126,14 +126,14 @@ handover_continuous <- function(raw.dat, base.dat, var, save = FALSE) { handover_ordinal <- function(raw.dat, base.dat, var, save=FALSE) { raw.var <- raw.dat %>% - dplyr::select(pidp, time, all_of(var)) %>% + dplyr::select(time, all_of(var)) %>% filter(!.data[[var]] %in% miss.values) %>% group_by(time, .data[[var]]) %>% count() %>% mutate(source = 'final_US') base.var <- base.dat %>% - dplyr::select(pidp, time, all_of(var)) %>% + dplyr::select(time, all_of(var)) %>% filter(!.data[[var]] %in% miss.values) %>% group_by(time, .data[[var]]) %>% count() %>% diff --git a/minos/validation/handovers.Rmd b/minos/validation/handovers.Rmd index 6b19aa5b..48e81780 100644 --- a/minos/validation/handovers.Rmd +++ b/minos/validation/handovers.Rmd @@ -95,6 +95,102 @@ base.maxAge <- base.dat %>% summarise(max_age = max(age)) handover_lineplots(raw.maxAge, base.maxAge, 'max_age') + +## Check counts in very old age groups +brks = c(75, 80, 85, 90, 95, 100, 105, 110) +lbls = c("75to80", "80to85", "85to90", "90to95", "95to100", "100to105", "105to110") + +raw.countOldAge <- raw.dat %>% + filter(age > 75) %>% + mutate(age_group = cut(age, + breaks = brks, + labels = lbls)) %>% + group_by(time, age_group) %>% + summarise(age_group_counts = n()) %>% + mutate(source = 'observed') + +base.countOldAge <- base.dat %>% + filter(age > 75) %>% + mutate(age_group = cut(age, + breaks = brks, + labels = lbls)) %>% + group_by(time, age_group) %>% + summarise(age_group_counts = n()) %>% + mutate(source = 'predicted') + +combined <- rbind(raw.countOldAge, base.countOldAge) + +ggplot(combined, aes(x = time, y = age_group_counts, group = age_group, colour = age_group)) + + geom_line() + + geom_vline(xintercept = 2021, linetype = 'dashed') + + labs(title = 'Counts by older age group', subtitle = '75 to 110 by 5 year groups') + + xlab('Year') + + ylab('Count') + + + + + +raw.countOldAge <- raw.dat %>% + filter(age > 75) %>% + mutate(age_group = cut(age, + breaks = brks, + labels = lbls)) + +base.countOldAge <- base.dat %>% + filter(age > 75) %>% + mutate(age_group = cut(age, + breaks = brks, + labels = lbls)) + +handover_ordinal(raw.countOldAge, base.countOldAge, var = 'age_group', save = shall.we.save) + +``` + +```{r} +# Having a go at an age pyramid, hopefully animated + +brks = c(75, 80, 85, 90, 95, 100, 105, 110) +lbls = c("75to80", "80to85", "85to90", "90to95", "95to100", "100to105", "105to110") + +var <- 'age_group' + +# start with calculating percentages +#raw.oldAgePct +#base.oldAgePct + +raw.oldAgePct <- raw.dat %>% + filter(age > 75) %>% + mutate(age_group = cut(age, + breaks = brks, + labels = lbls)) %>% + dplyr::select(time, age_group) %>% + group_by(time, age_group) %>% + count() %>% + mutate(source = 'predicted') + +base.oldAgePct <- base.dat %>% + filter(age > 75) %>% + mutate(age_group = cut(age, + breaks = brks, + labels = lbls)) %>% + dplyr::select(time, all_of(var)) %>% + #filter(!.data[[var]] %in% miss.values) %>% + group_by(time, .data[[var]]) %>% + count() %>% + mutate(source = 'observed') + +raw.oldAgePct[[var]] <- as.factor(raw.oldAgePct[[var]]) +base.oldAgePct[[var]] <- as.factor(base.oldAgePct[[var]]) + +merged <- rbind(raw.oldAgePct, base.oldAgePct) +merged[[var]] <- as.factor(merged[[var]]) + + +var.norm <- merged %>% + group_by(time) %>% + mutate(total = sum(n)) %>% + mutate(prct = (n / total) * 100) ``` From ac16527ba10144262e16d7dc63eac6a6344a40b0 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 22 Apr 2024 12:03:20 +0100 Subject: [PATCH 225/229] Created some functions for plotting animated age pyramids, but leaving out of handovers for now as they dont render properly in html notebooks --- minos/utils_validation_vis.R | 70 +++++++++++++++++++++++ minos/validation/handovers.Rmd | 100 +++++---------------------------- 2 files changed, 84 insertions(+), 86 deletions(-) diff --git a/minos/utils_validation_vis.R b/minos/utils_validation_vis.R index 83bf796b..29d0a298 100644 --- a/minos/utils_validation_vis.R +++ b/minos/utils_validation_vis.R @@ -650,3 +650,73 @@ handover_inequality_80_20 <- function(raw.dat, base.dat, var) { ylab('80:20 Ratio') print(p1) } + + +## Age Pyramid + +# First, let's create a function to generate the age pyramid for a specific year +create_age_pyramid <- function(df_year, time) { + ggplot(df_year, aes(x = age_group, y = n, fill = source)) + + geom_bar(stat = "identity", position = "identity") + + scale_fill_manual(values = c("observed" = "blue", "predicted" = "red")) + + coord_flip() + + labs(title = paste("Age Pyramid - Year", time), + x = "Age Group", y = "Count") + + theme_minimal() +} + +age_pyramid <- function(raw.dat, base.dat) { + + brks = c(75, 80, 85, 90, 95, 100, 105, 110) + lbls = c("75to80", "80to85", "85to90", "90to95", "95to100", "100to105", "105to110") + var <- 'age_group' + + raw.oldAgePct <- raw.dat %>% + filter(age > 75) %>% + mutate(age_group = cut(age, + breaks = brks, + labels = lbls)) %>% + dplyr::select(time, age_group) %>% + group_by(time, age_group) %>% + count() %>% + mutate(source = 'predicted') + + base.oldAgePct <- base.dat %>% + filter(age > 75) %>% + mutate(age_group = cut(age, + breaks = brks, + labels = lbls)) %>% + dplyr::select(time, age_group) %>% + group_by(time, age_group) %>% + count() %>% + mutate(source = 'observed') + + raw.oldAgePct[[var]] <- as.factor(raw.oldAgePct[[var]]) + base.oldAgePct[[var]] <- as.factor(base.oldAgePct[[var]]) + + merged <- rbind(raw.oldAgePct, base.oldAgePct) + merged[[var]] <- as.factor(merged[[var]]) + + + var.norm <- merged %>% + group_by(time) %>% + mutate(total = sum(n)) %>% + mutate(prct = (n / total) * 100) + + # Now, let's create the animation + pyramid_animation <- ggplot(var.norm, aes(group = time)) + + transition_states(time, transition_length = 1, state_length = 1) + + enter_fade() + + exit_fade() + + geom_bar(aes(x = age_group, y = n, fill = source), + stat = "identity", position = "identity") + + scale_fill_manual(values = c("observed" = "blue", "predicted" = "red")) + + coord_flip() + + labs(title = "Age Pyramid - Year: {closest_state}", + x = "Age Group", y = "Count") + + theme_minimal() + + shadow_mark() + + #anim_save("age_pyramid_animation.gif", pyramid_animation) + animate(pyramid_animation, renderer = gifski_renderer("age_pyramid_animation.gif")) +} diff --git a/minos/validation/handovers.Rmd b/minos/validation/handovers.Rmd index 48e81780..df8a9ae0 100644 --- a/minos/validation/handovers.Rmd +++ b/minos/validation/handovers.Rmd @@ -19,15 +19,16 @@ Handover plots are not strictly statistical validation, but more a type of 'sani ```{r "setup", include=FALSE} -require(tidyverse) -require(ggplot2) -require(ggridges) # for densities over time plots. -require(knitr) -require(here) -require(dplyr) -require(tidyr) -require(purrr) -require(gghighlight) +library(tidyverse) +library(ggplot2) +library(ggridges) # for densities over time plots. +library(knitr) +library(here) +library(dplyr) +library(tidyr) +library(purrr) +library(gghighlight) +library(gganimate) #workingDir <- "/home/luke/Documents/WORK/MINOS/" workingDir <- normalizePath('../') @@ -95,42 +96,15 @@ base.maxAge <- base.dat %>% summarise(max_age = max(age)) handover_lineplots(raw.maxAge, base.maxAge, 'max_age') +``` + +# Old Age Counts +```{r} ## Check counts in very old age groups brks = c(75, 80, 85, 90, 95, 100, 105, 110) lbls = c("75to80", "80to85", "85to90", "90to95", "95to100", "100to105", "105to110") -raw.countOldAge <- raw.dat %>% - filter(age > 75) %>% - mutate(age_group = cut(age, - breaks = brks, - labels = lbls)) %>% - group_by(time, age_group) %>% - summarise(age_group_counts = n()) %>% - mutate(source = 'observed') - -base.countOldAge <- base.dat %>% - filter(age > 75) %>% - mutate(age_group = cut(age, - breaks = brks, - labels = lbls)) %>% - group_by(time, age_group) %>% - summarise(age_group_counts = n()) %>% - mutate(source = 'predicted') - -combined <- rbind(raw.countOldAge, base.countOldAge) - -ggplot(combined, aes(x = time, y = age_group_counts, group = age_group, colour = age_group)) + - geom_line() + - geom_vline(xintercept = 2021, linetype = 'dashed') + - labs(title = 'Counts by older age group', subtitle = '75 to 110 by 5 year groups') + - xlab('Year') + - ylab('Count') - - - - - raw.countOldAge <- raw.dat %>% filter(age > 75) %>% mutate(age_group = cut(age, @@ -144,54 +118,8 @@ base.countOldAge <- base.dat %>% labels = lbls)) handover_ordinal(raw.countOldAge, base.countOldAge, var = 'age_group', save = shall.we.save) - ``` -```{r} -# Having a go at an age pyramid, hopefully animated - -brks = c(75, 80, 85, 90, 95, 100, 105, 110) -lbls = c("75to80", "80to85", "85to90", "90to95", "95to100", "100to105", "105to110") - -var <- 'age_group' - -# start with calculating percentages -#raw.oldAgePct -#base.oldAgePct - -raw.oldAgePct <- raw.dat %>% - filter(age > 75) %>% - mutate(age_group = cut(age, - breaks = brks, - labels = lbls)) %>% - dplyr::select(time, age_group) %>% - group_by(time, age_group) %>% - count() %>% - mutate(source = 'predicted') - -base.oldAgePct <- base.dat %>% - filter(age > 75) %>% - mutate(age_group = cut(age, - breaks = brks, - labels = lbls)) %>% - dplyr::select(time, all_of(var)) %>% - #filter(!.data[[var]] %in% miss.values) %>% - group_by(time, .data[[var]]) %>% - count() %>% - mutate(source = 'observed') - -raw.oldAgePct[[var]] <- as.factor(raw.oldAgePct[[var]]) -base.oldAgePct[[var]] <- as.factor(base.oldAgePct[[var]]) - -merged <- rbind(raw.oldAgePct, base.oldAgePct) -merged[[var]] <- as.factor(merged[[var]]) - - -var.norm <- merged %>% - group_by(time) %>% - mutate(total = sum(n)) %>% - mutate(prct = (n / total) * 100) -``` # Population Counts From 23a8f2cac736352e615bcda44e02c15875197899 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Mon, 22 Apr 2024 12:03:41 +0100 Subject: [PATCH 226/229] Additional R packages for animated age pyramids --- environment.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/environment.yml b/environment.yml index ef2562a5..5b1d430b 100644 --- a/environment.yml +++ b/environment.yml @@ -6,6 +6,8 @@ dependencies: # Added pip and python at top to avoid conda giving grumpy warning - pip - python_abi=3.9=*cp* - python=3.9 + - libgdal-dev + - cargo #- conda-forge::conda- # For alternative method of "conda build ."; required conda-build to be installed - r-base=4.1.3 - r-sf @@ -37,6 +39,8 @@ dependencies: # Added pip and python at top to avoid conda giving grumpy warning - r-glmmTMB - r-MESS - r-scales + - r-gganimate + - r-gifski - gcc - pip: # Same packages as were in "requirements.txt" except minos and vivarium, as both installed during "make install" - rpy2 From cf1e01044aba863f75e74749ec958c9b198e06e9 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 23 Apr 2024 12:02:14 +0100 Subject: [PATCH 227/229] Added a new visualisation target --- minos/outcomes/QALY.Makefile | 5 +++++ minos/outcomes/QALY_calculation.py | 3 +-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/minos/outcomes/QALY.Makefile b/minos/outcomes/QALY.Makefile index d3e5d676..67de0c48 100644 --- a/minos/outcomes/QALY.Makefile +++ b/minos/outcomes/QALY.Makefile @@ -384,6 +384,11 @@ QALYs_UC: EXPERIMENT=default_config QALYs_UC: STARTYEAR=2021 QALYs_UC: QALY_vis_SCP_25_UC QALY_vis_SCP_50_UC QALY_vis_SCP_75_UC QALY_vis_SCP_100_UC +QALY_vis_SCP_25_100_UC: EXPERIMENT=default_config +QALY_vis_SCP_25_100_UC: STARTYEAR=2021 +QALY_vis_SCP_25_100_UC: QALY_baseline QALY_SCP_25_UC QALY_SCP_50_UC QALY_SCP_75_UC QALY_SCP_100_UC + $(RSCRIPT) -e "require(rmarkdown); render('$(OUTCOMES)/QALY_comparison2.Rmd', params = list(experiment='$(EXPERIMENT)/', base='baseline', intervention=c('25UniversalCredit', '50UniversalCredit', '75UniversalCredit', '100UniversalCredit'), start.year='$(STARTYEAR)'), output_file = 'QALY_SCP_25_100_UC.html')" + QALYs_UC_glasgow: EXPERIMENT=glasgow_scaled QALYs_UC_glasgow: STARTYEAR=2020 QALYs_UC_glasgow: QALY_vis_SCP_25_UC QALY_vis_SCP_50_UC QALY_vis_SCP_75_UC QALY_vis_SCP_100_UC diff --git a/minos/outcomes/QALY_calculation.py b/minos/outcomes/QALY_calculation.py index f309e573..0f3b2634 100644 --- a/minos/outcomes/QALY_calculation.py +++ b/minos/outcomes/QALY_calculation.py @@ -31,8 +31,7 @@ def aggregate_csv(filename, intervention, year, start_year, subset_func_string=N from data. Returns - ------- - + ------ """ df = pd.read_csv(filename, low_memory=False) if subset_func_string: From 9eecc2a68d221c18937f2d511a59cbd5fa7592fa Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 23 Apr 2024 12:03:00 +0100 Subject: [PATCH 228/229] Additions to the PCS QALY notebook for sharing around SIPHER --- minos/testing/PCS_QALYs.Rmd | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/minos/testing/PCS_QALYs.Rmd b/minos/testing/PCS_QALYs.Rmd index 2acf13e5..3f5db5df 100644 --- a/minos/testing/PCS_QALYs.Rmd +++ b/minos/testing/PCS_QALYs.Rmd @@ -57,12 +57,34 @@ raw.dat <- do.call(rbind, lapply(raw.files, read.csv)) #out.path <- here::here('output', 'glasgow_scaled/') out.path <- here::here('output', 'default_config/') base.dat <- read_singular_local_out(out.path, 'baseline', drop.dead = TRUE) + +trans.path <- here::here('data', 'transitions/') ``` -# Tran +# PCS Pathways + +1. Housing Quality +2. Nutrition Quality +3. Tobacco Use +4. Loneliness +5. Physical Activity (new) +6. Alcohol Use Disorders (new) + +## Transition Model + +The formula we use for estimating a transition model for PCS is shown below: + +> SF_12_PCS ~ scale(SF_12_PCS_last) + I(scale(SF_12_PCS_last)\*\*2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + scale(age) + I(scale(age)\*\*2) + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + scale(hh_income) + scale(nutrition_quality) + I(factor(ncigs>0)) + factor(loneliness) + factor(active) + factor(auditc) + (1|pidp) + time + +This can be grouped into categories: - Previous values - Demographics - Pathways - Random term + +We use a Linear Mixed-effects Model (LMM) to calculate transition probabilities + ```{r} +pcs.mod <- readRDS(file = paste0(trans.path, 'SF_12_PCS/lmm/SF_12_PCS_LMM.rds')) +summary(pcs.mod) ``` From aa8dc9a114314e941a2b5c29bf89ef01b77b1416 Mon Sep 17 00:00:00 2001 From: Luke Archer Date: Tue, 22 Oct 2024 15:18:29 +0100 Subject: [PATCH 229/229] Additions and updating of the PCS_pathway_justifications script --- minos/data_generation/generate_composite_vars.py | 9 +++++---- minos/modules/alcohol.py | 2 +- minos/modules/physical_activity.py | 1 + minos/transitions/estimate_transitions.R | 8 ++++---- minos/transitions/model_definitions_default.txt | 2 +- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/minos/data_generation/generate_composite_vars.py b/minos/data_generation/generate_composite_vars.py index 71865da2..4dcc0397 100755 --- a/minos/data_generation/generate_composite_vars.py +++ b/minos/data_generation/generate_composite_vars.py @@ -1011,10 +1011,11 @@ def calculate_auditc_score(data): # Ordinal values data['auditc'] = '-9' - data['auditc'][data['auditc_score'] == 0] = 'Non-drinker' # 0 score is non-drinker - data['auditc'][data['auditc_score'].isin(range(1, 5))] = 'Low Risk' # 1-4 is sensible - data['auditc'][data['auditc_score'].isin(range(5, 8))] = 'Increased Risk' # 5-7 is hazardous - data['auditc'][data['auditc_score'] >= 8] = 'High Risk' # 8-12 is harmful + #data['auditc'][data['auditc_score'] == 0] = 'Non-drinker' # 0 score is non-drinker + data['auditc'][data['auditc_score'].isin(range(0, 5))] = 'Low Risk' # 0-4 is low risk + data['auditc'][data['auditc_score'].isin(range(5, 8))] = 'Increasing Risk' # 5-7 is increasing risk + data['auditc'][data['auditc_score'].isin(range(8, 11))] = 'Higher Risk' # 8-10 is higher risk + data['auditc'][data['auditc_score'] >= 11] = 'Possible Dependence' # 11-12 is possible dependence data.drop(labels=['auditc1', 'auditc2', 'auditc3', 'auditc4', 'auditc5', 'temp_auditc1', 'temp_auditc2', 'temp_auditc3', diff --git a/minos/modules/alcohol.py b/minos/modules/alcohol.py index d03d2aee..06783f91 100755 --- a/minos/modules/alcohol.py +++ b/minos/modules/alcohol.py @@ -128,7 +128,7 @@ def calculate_alcohol(self, pop): else: year = min(self.year, 2018) - cols = ['Non-drinker', 'Low Risk', 'Increased Risk', 'High Risk'] + cols = ['Low Risk', 'Increasing Risk', 'Higher Risk', 'Possible Dependence'] transition_model = r_utils.load_transitions(f"auditc/nnet/auditc_{year}_{year + 1}", self.rpy2Modules, diff --git a/minos/modules/physical_activity.py b/minos/modules/physical_activity.py index 8fa150bd..65dbde0a 100644 --- a/minos/modules/physical_activity.py +++ b/minos/modules/physical_activity.py @@ -56,6 +56,7 @@ def setup(self, builder): "age", "education_state", "hh_income", + 'region', "active"] self.population_view = builder.population.get_view(columns=view_columns) diff --git a/minos/transitions/estimate_transitions.R b/minos/transitions/estimate_transitions.R index ecda95af..e0e562fc 100644 --- a/minos/transitions/estimate_transitions.R +++ b/minos/transitions/estimate_transitions.R @@ -78,10 +78,10 @@ run_yearly_models <- function(transitionDir_path, 'Family Care', 'Not Working')) orig_data$auditc <- factor(orig_data$auditc, - levels = c('Non-drinker', - 'Low Risk', - 'Increased Risk', - 'High Risk')) + levels = c('Low Risk', + 'Increasing Risk', + 'Higher Risk', + 'Possible Dependence')) # read file repeat { diff --git a/minos/transitions/model_definitions_default.txt b/minos/transitions/model_definitions_default.txt index 22b39186..8686f341 100644 --- a/minos/transitions/model_definitions_default.txt +++ b/minos/transitions/model_definitions_default.txt @@ -1,4 +1,4 @@ -LOGIT : active ~ scale(age) + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + scale(hh_income) + relevel(factor(ethnicity), ref = 'WBI') +LOGIT : active ~ scale(age) + factor(sex) + relevel(factor(education_state), ref = '3') + factor(region) + scale(SF_12_MCS) + scale(SF_12_PCS) + scale(hh_income) + relevel(factor(ethnicity), ref = 'WBI') NNET : auditc ~ scale(age) + factor(sex) + relevel(factor(education_state), ref = '3') + scale(SF_12_MCS) + scale(SF_12_PCS) + scale(hh_income) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') CLM : housing_quality ~ scale(age) + I(scale(age)**2) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) + I(scale(hh_income)**2) + scale(hh_income_diff) CLM : loneliness ~ scale(age) + factor(sex) + relevel(factor(ethnicity), ref = 'WBI') + relevel(factor(region), ref = 'Scotland') + relevel(factor(education_state), ref = "1") + factor(housing_quality) + factor(neighbourhood_safety) + factor(loneliness) + scale(nutrition_quality) + scale(ncigs) + scale(hh_income) + relevel(factor(marital_status), ref = 'Partnered')