diff --git a/src/tlo/methods/labour.py b/src/tlo/methods/labour.py index ff4e6f88dd..0f07377e52 100644 --- a/src/tlo/methods/labour.py +++ b/src/tlo/methods/labour.py @@ -73,6 +73,7 @@ def __init__(self, name=None): # Finally define a dictionary which will hold the required consumables for each intervention self.item_codes_lab_consumables = dict() + INIT_DEPENDENCIES = {'Demography'} OPTIONAL_INIT_DEPENDENCIES = {'Stunting'} @@ -1175,7 +1176,7 @@ def set_date_of_labour(self, individual_id): # we determine if she will go into labour post term (42+ weeks) if self.rng.random_sample() < self.la_linear_models['post_term_labour'].predict( - df.loc[[individual_id]])[individual_id]: + df.loc[[individual_id], ['li_bmi']])[individual_id]: df.at[individual_id, 'la_due_date_current_pregnancy'] = \ (df.at[individual_id, 'date_of_last_pregnancy'] + pd.DateOffset( @@ -1206,7 +1207,28 @@ def predict(self, eq, person_id): """ df = self.sim.population.props mni = self.sim.modules['PregnancySupervisor'].mother_and_newborn_info - person = df.loc[[person_id]] + + # we only need those properties required to evaluate the function + person_properties = eq.predict.person_properties + + # if the simulation is running without stunting or cardio metabolic disorders modules + # then we need to remove the properties + if "Stunting" not in self.sim.modules and "un_HAZ_category" in person_properties: + person_properties = list(person_properties) + person_properties.remove("un_HAZ_category") + + if "CardioMetabolicDisorders" not in self.sim.modules and "nc_hypertension" in person_properties: + person_properties = list(person_properties) + person_properties.remove("nc_hypertension") + + if not person_properties: + # we do need to pass a valid dataframe, despite not actually needing any properties :( + person_properties = ['is_alive'] + + person = df.loc[ + [person_id], + person_properties + ] # We define specific external variables used as predictors in the equations defined below has_rbt = mni[person_id]['received_blood_transfusion'] @@ -1249,7 +1271,11 @@ def check_labour_can_proceed(self, individual_id): :returns True/False if labour can proceed """ df = self.sim.population.props - person = df.loc[individual_id] + person = df.loc[ + individual_id, + ['is_alive', 'is_pregnant', 'la_currently_in_labour', 'la_due_date_current_pregnancy', + 'ac_admitted_for_immediate_delivery', 'ps_gestational_age_in_weeks'] + ] # If the mother has died OR has lost her pregnancy OR is already in labour then the labour events wont run if not person.is_alive or not person.is_pregnant or person.la_currently_in_labour: @@ -1259,8 +1285,7 @@ def check_labour_can_proceed(self, individual_id): return False # If she is alive, pregnant, not in labour AND her due date is today then the event will run - if person.is_alive and person.is_pregnant and (person.la_due_date_current_pregnancy == self.sim.date) \ - and not person.la_currently_in_labour: + if person.la_due_date_current_pregnancy == self.sim.date: # If the woman in not currently an inpatient then we assume this is her normal labour if person.ac_admitted_for_immediate_delivery == 'none': @@ -1278,11 +1303,9 @@ def check_labour_can_proceed(self, individual_id): f'at gestation {person.ps_gestational_age_in_weeks}') return True - # If she is alive, pregnant, not in labour BUT her due date is not today, however shes been admitted then we + # If she is alive, pregnant, not in labour BUT her due date is not today, however she's been admitted then we # labour can progress as she requires early delivery - if person.is_alive and person.is_pregnant and not person.la_currently_in_labour and \ - (person.la_due_date_current_pregnancy != self.sim.date) and (person.ac_admitted_for_immediate_delivery != - 'none'): + if person.ac_admitted_for_immediate_delivery != 'none': logger.debug(key='message', data=f'person {individual_id} has just reached LabourOnsetEvent on ' f'{self.sim.date}- they have been admitted for delivery due to ' f'complications in the antenatal period and will now progress into the ' @@ -2454,12 +2477,16 @@ def apply(self, individual_id): # been admitted antenatally for delivery will be delivering in hospital and that is scheduled accordingly if df.at[individual_id, 'ac_admitted_for_immediate_delivery'] == 'none': + individual = df.loc[ + [individual_id], + ['age_years', 'li_urban', 'la_parity', 'li_ed_lev', 'li_wealth', 'li_mar_stat'] + ] # Here we calculate this womans predicted risk of home birth and health centre birth pred_hb_delivery = self.module.la_linear_models['probability_delivery_at_home'].predict( - df.loc[[individual_id]])[individual_id] + individual)[individual_id] pred_hc_delivery = self.module.la_linear_models['probability_delivery_health_centre'].predict( - df.loc[[individual_id]])[individual_id] + individual)[individual_id] pred_hp_delivery = params['probability_delivery_hospital'] # The denominator is calculated @@ -2707,7 +2734,7 @@ def __init__(self, module, mother_id): def apply(self, mother_id): df = self.sim.population.props - person = df.loc[mother_id] + person = df.loc[mother_id, ['is_alive', 'la_intrapartum_still_birth', 'ps_multiple_pregnancy', 'is_pregnant']] mni = self.sim.modules['PregnancySupervisor'].mother_and_newborn_info params = self.module.current_parameters @@ -2747,6 +2774,7 @@ def apply(self, mother_id): else: self.sim.do_birth(mother_id) + # refresh the reference to the dataframe as the do_birth function might have changed the underlying object df = self.sim.population.props # If the mother survived labour but experienced a stillbirth we reset all the relevant pregnancy variables now @@ -2786,10 +2814,14 @@ def apply(self, mother_id): else: # We use a linear model to determine if women without complications will receive any postnatal care + mother = df.loc[ + [mother_id], + ['age_years', 'li_urban', 'li_wealth', 'la_parity', 'ac_total_anc_visits_current_pregnancy'] + ] prob_pnc = self.module.la_linear_models['postnatal_check'].predict( - df.loc[[mother_id]], - mode_of_delivery=pd.Series(mni[mother_id]['mode_of_delivery'], index=df.loc[[mother_id]].index), - delivery_setting=pd.Series(mni[mother_id]['delivery_setting'], index=df.loc[[mother_id]].index) + mother, + mode_of_delivery=pd.Series(mni[mother_id]['mode_of_delivery'], index=mother.index), + delivery_setting=pd.Series(mni[mother_id]['delivery_setting'], index=mother.index) )[mother_id] has_comps = False diff --git a/src/tlo/methods/labour_lm.py b/src/tlo/methods/labour_lm.py index 83c2360706..f9700e1ba8 100644 --- a/src/tlo/methods/labour_lm.py +++ b/src/tlo/methods/labour_lm.py @@ -23,6 +23,16 @@ def predict_for_dataframe(self, df, rng=None, **externals): import pandas as pd +def person_properties(keys): + """Decorator to specify the person's properties needed to evaluate the function""" + keys_t = tuple(keys) + def decorator(func): + func.person_properties = keys_t + return func + return decorator + + +@person_properties(['li_bmi']) def predict_post_term_labour(self, df, rng=None, **externals): """ Individual level linear model which predicts an individuals probability of post term labour. Risk is increased in @@ -40,6 +50,7 @@ def predict_post_term_labour(self, df, rng=None, **externals): return pd.Series(data=[result], index=df.index) +@person_properties(['age_years', 'li_mar_stat', 'li_wealth', 'li_ed_lev', 'li_urban']) def predict_parity(self, df, rng=None, **externals): """ Population level linear model (additive) which returns a df containing the predicted parity (previous number of @@ -70,6 +81,7 @@ def predict_parity(self, df, rng=None, **externals): return result +@person_properties(['un_HAZ_category']) def predict_obstruction_cpd_ip(self, df, rng=None, **externals): """ Individual level linear model which predicts an individuals probability of developing obstructed labour @@ -91,6 +103,7 @@ def predict_obstruction_cpd_ip(self, df, rng=None, **externals): return pd.Series(data=[result], index=df.index) +@person_properties(['ps_premature_rupture_of_membranes', 'ac_received_abx_for_prom']) def predict_sepsis_chorioamnionitis_ip(self, df, rng=None, **externals): """ Individual level linear model which predicts an individuals probability of developing sepsis secondary @@ -111,6 +124,7 @@ def predict_sepsis_chorioamnionitis_ip(self, df, rng=None, **externals): return pd.Series(data=[result], index=df.index) +@person_properties([]) def predict_sepsis_endometritis_pp(self, df, rng=None, **externals): """ Individual level linear model which predicts an individuals probability of developing sepsis secondary @@ -128,6 +142,7 @@ def predict_sepsis_endometritis_pp(self, df, rng=None, **externals): return pd.Series(data=[result], index=df.index) +@person_properties([]) def predict_sepsis_skin_soft_tissue_pp(self, df, rng=None, **externals): """ Individual level linear model which predicts an individuals probability of developing sepsis secondary @@ -145,6 +160,7 @@ def predict_sepsis_skin_soft_tissue_pp(self, df, rng=None, **externals): return pd.Series(data=[result], index=df.index) +@person_properties([]) def predict_sepsis_urinary_tract_pp(self, df, rng=None, **externals): """ Individual level linear model which predicts an individuals probability of developing sepsis secondary @@ -160,6 +176,7 @@ def predict_sepsis_urinary_tract_pp(self, df, rng=None, **externals): return pd.Series(data=[result], index=df.index) +@person_properties(['la_sepsis_treatment']) def predict_sepsis_death(self, df, rng=None, **externals): """ Individual level linear model which predicts an individuals probability of death due to postpartum sepsis. @@ -175,6 +192,8 @@ def predict_sepsis_death(self, df, rng=None, **externals): return pd.Series(data=[result], index=df.index) +@person_properties(['la_eclampsia_treatment', 'ac_mag_sulph_treatment', 'la_maternal_hypertension_treatment', + 'ac_iv_anti_htn_treatment']) def predict_eclampsia_death(self, df, rng=None, **externals): """ This is an individual level linear model which predicts an individuals probability of death due to eclampsia. @@ -195,6 +214,7 @@ def predict_eclampsia_death(self, df, rng=None, **externals): return pd.Series(data=[result], index=df.index) +@person_properties(['la_maternal_hypertension_treatment', 'ac_iv_anti_htn_treatment']) def predict_severe_pre_eclamp_death(self, df, rng=None, **externals): """ Individual level linear model which predicts an individuals probability of death due to severe @@ -211,6 +231,7 @@ def predict_severe_pre_eclamp_death(self, df, rng=None, **externals): return pd.Series(data=[result], index=df.index) +@person_properties(['la_previous_cs_delivery', 'ps_htn_disorders']) def predict_placental_abruption_ip(self, df, rng=None, **externals): """ Individual level linear model which predicts an individuals probability of developing placental @@ -230,6 +251,7 @@ def predict_placental_abruption_ip(self, df, rng=None, **externals): return pd.Series(data=[result], index=df.index) +@person_properties(['ps_placenta_praevia', 'ps_placental_abruption', 'la_placental_abruption']) def predict_antepartum_haem_ip(self, df, rng=None, **externals): """ Individual level linear model which predicts an individuals probability of developing an antepartum @@ -249,6 +271,7 @@ def predict_antepartum_haem_ip(self, df, rng=None, **externals): return pd.Series(data=[result], index=df.index) +@person_properties([]) def predict_antepartum_haem_death(self, df, rng=None, **externals): """ Individual level linear model which predicts an individuals probability of death due to antepartum @@ -268,6 +291,8 @@ def predict_antepartum_haem_death(self, df, rng=None, **externals): return pd.Series(data=[result], index=df.index) +@person_properties(['pn_htn_disorders', 'nc_hypertension', 'ps_multiple_pregnancy', 'la_placental_abruption', + 'ps_placental_abruption']) def predict_pph_uterine_atony_pp(self, df, rng=None, **externals): """ Individual level linear model which predicts an individuals probability of developing a postpartum haemorrhage due @@ -298,6 +323,7 @@ def predict_pph_uterine_atony_pp(self, df, rng=None, **externals): return pd.Series(data=[result], index=df.index) +@person_properties([]) def predict_pph_retained_placenta_pp(self, df, rng=None, **externals): """ Individual level linear model which predicts an individuals probability of developing a postpartum @@ -313,6 +339,7 @@ def predict_pph_retained_placenta_pp(self, df, rng=None, **externals): return pd.Series(data=[result], index=df.index) +@person_properties(['la_postpartum_haem_treatment']) def predict_postpartum_haem_pp_death(self, df, rng=None, **externals): """ Individual level linear model which predicts an individuals probability of death following a postpartum @@ -333,6 +360,7 @@ def predict_postpartum_haem_pp_death(self, df, rng=None, **externals): return pd.Series(data=[result], index=df.index) +@person_properties(['la_parity', 'la_previous_cs_delivery', 'la_obstructed_labour']) def predict_uterine_rupture_ip(self, df, rng=None, **externals): """ Population level linear model to allow for the model to be scaled at initialisation of the simulation. The model @@ -352,6 +380,7 @@ def predict_uterine_rupture_ip(self, df, rng=None, **externals): return result +@person_properties(['la_uterine_rupture_treatment', 'la_has_had_hysterectomy']) def predict_uterine_rupture_death(self, df, rng=None, **externals): """ Individual level linear model which predicts an individuals probability of death following a uterine @@ -371,6 +400,9 @@ def predict_uterine_rupture_death(self, df, rng=None, **externals): return pd.Series(data=[result], index=df.index) +@person_properties(['is_alive', 'la_uterine_rupture', 'la_obstructed_labour', 'la_antepartum_haem', + 'ps_antepartum_haemorrhage', 'ps_htn_disorders', 'la_sepsis', 'ps_chorioamnionitis', + 'ps_multiple_pregnancy']) def predict_intrapartum_still_birth(self, df, rng=None, **externals): """ Individual level linear model which predicts an individuals probability of experiencing an intrapartum @@ -410,6 +442,7 @@ def predict_intrapartum_still_birth(self, df, rng=None, **externals): return pd.Series(data=[result], index=df.index) +@person_properties(['age_years', 'li_wealth', 'la_parity', 'li_mar_stat', 'li_urban']) def predict_probability_delivery_health_centre(self, df, rng=None, **externals): """ Population level to allow for scaling at the initialisation of the simulation. This model predicts an @@ -444,6 +477,7 @@ def predict_probability_delivery_health_centre(self, df, rng=None, **externals): return result +@person_properties(['age_years', 'li_urban', 'la_parity', 'li_ed_lev', 'li_wealth', 'li_mar_stat']) def predict_probability_delivery_at_home(self, df, rng=None, **externals): """ Population level to allow for scaling at the initialisation of the simulation. This model predicts an @@ -481,6 +515,7 @@ def predict_probability_delivery_at_home(self, df, rng=None, **externals): return result +@person_properties(['age_years', 'li_urban', 'la_parity', 'li_wealth', 'ac_total_anc_visits_current_pregnancy']) def predict_postnatal_check(self, df, rng=None, **externals): """ Population level to allow for scaling at the initialisation of the simulation. This model predicts an