"""
Stunting Module
Overview
--------
The Stunting module determines the prevalence of stunting for children under 5 years old. A polling event runs
every month and determines the risk of onset of non-severe stunting, progression to severe stunting or natural
recovery. The Generic HSI calls `do_routine_assessment_for_chronic_undernutrition` for any HSI with a child under
5 years old: if they have any stunting they are provided with an intervention - `HSI_Stunting_ComplementaryFeeding`.
"""
from collections import namedtuple
from pathlib import Path
from typing import Union
import numpy as np
import pandas as pd
from scipy.stats import norm
from tlo import DAYS_IN_YEAR, DateOffset, Module, Parameter, Property, Types, logging
from tlo.events import IndividualScopeEventMixin, PopulationScopeEventMixin, RegularEvent
from tlo.lm import LinearModel, LinearModelType, Predictor
from tlo.methods import Metadata
from tlo.methods.healthsystem import HSI_Event
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
# ---------------------------------------------------------------------------------------------------------
# MODULE DEFINITION
# ---------------------------------------------------------------------------------------------------------
[docs]
class Stunting(Module):
"""This is the disease module for Stunting"""
INIT_DEPENDENCIES = {'Demography', 'Wasting', 'NewbornOutcomes', 'Diarrhoea', 'Hiv'}
METADATA = {
Metadata.DISEASE_MODULE,
Metadata.USES_HEALTHSYSTEM,
}
PARAMETERS = {
# Prevalence of stunting by age group at initiation
'prev_HAZ_distribution_age_0_5mo': Parameter(
Types.LIST, 'Distribution of HAZ among less than 6 months of age in 2015 (mean, standard deviation)'),
'prev_HAZ_distribution_age_6_11mo': Parameter(
Types.LIST, 'Distribution of HAZ among 6 months and 1 year of age in 2015 (mean, standard deviation)'),
'prev_HAZ_distribution_age_12_23mo': Parameter(
Types.LIST, 'Distribution of HAZ among 1 year olds in 2015 (mean, standard deviation)'),
'prev_HAZ_distribution_age_24_35mo': Parameter(
Types.LIST, 'Distribution of HAZ among 2 year olds in 2015 (mean, standard deviation)'),
'prev_HAZ_distribution_age_36_47mo': Parameter(
Types.LIST, 'Distribution of HAZ among 3 year olds in 2015 (mean, standard deviation)'),
'prev_HAZ_distribution_age_48_59mo': Parameter(
Types.LIST, 'Distribution of HAZ among 4 year olds in 2015 (mean, standard deviation)'),
# Risk factors for stunting prevalence at initiation
'or_stunting_male': Parameter(
Types.REAL, 'Odds ratio of stunting if male gender'),
'or_stunting_preterm_and_AGA': Parameter(
Types.REAL, 'Odds ratio of stunting if born preterm and adequate for gestational age'),
'or_stunting_SGA_and_term': Parameter(
Types.REAL, 'Odds ratio of stunting if born term and small for geatational age'),
'or_stunting_SGA_and_preterm': Parameter(
Types.REAL, 'Odds ratio of stunting if born preterm and small for gestational age'),
'or_stunting_hhwealth_Q5': Parameter(
Types.REAL, 'Odds ratio of stunting if household wealth is poorest Q5, ref group Q1'),
'or_stunting_hhwealth_Q4': Parameter(
Types.REAL, 'Odds ratio of stunting if household wealth is poorer Q4, ref group Q1'),
'or_stunting_hhwealth_Q3': Parameter(
Types.REAL, 'Odds ratio of stunting if household wealth is middle Q3, ref group Q1'),
'or_stunting_hhwealth_Q2': Parameter(
Types.REAL, 'Odds ratio of stunting if household wealth is richer Q2, ref group Q1'),
# The incidence of stunting
'base_inc_rate_stunting_by_agegp': Parameter(
Types.LIST,
'Baseline incidence rate per year of stunting by age group (1-5, 6-11, 12-23, 24-35, 36-47, 48-59mo'),
'rr_stunting_preterm_and_AGA': Parameter(
Types.REAL, 'Relative risk of stunting if born preterm and adequate for gestational age'),
'rr_stunting_SGA_and_term': Parameter(
Types.REAL, 'Relative risk of stunting if born term and small for gestational age'),
'rr_stunting_SGA_and_preterm': Parameter(
Types.REAL, 'Relative risk of stunting if born preterm and small for gestational age'),
'rr_stunting_prior_wasting': Parameter(
Types.REAL, 'Relative risk of stunting if prior wasting in the last 3 months'),
'rr_stunting_untreated_HIV': Parameter(
Types.REAL, 'Relative risk of stunting for untreated HIV+'),
'rr_stunting_wealth_level': Parameter(
Types.REAL, 'Relative risk of stunting if wealth-level is greater than 1 compared 1'),
'rr_stunting_no_exclusive_breastfeeding': Parameter(
Types.REAL, 'Relative risk of stunting for not exclusively breastfed babies < 6 months'),
'rr_stunting_no_continued_breastfeeding': Parameter(
Types.REAL, 'Relative risk of stunting for not continued breastfed infants 6-24 months'),
'rr_stunting_per_diarrhoeal_episode': Parameter(
Types.REAL, 'Relative risk of stunting for recent diarrhoea episode'),
# Progression to severe stunting
'r_progression_severe_stunting_by_agegp': Parameter(
Types.LIST,
'Rates per year of progression to severe stunting by age group (1-5, 6-11, 12-23, 24-35, 36-47, 48-59mo'),
'rr_progress_severe_stunting_if_prior_wasting': Parameter(
Types.REAL, 'Relative risk of severe stunting if previously wasted'),
'rr_progress_severe_stunting_untreated_HIV': Parameter(
Types.REAL, 'Relative risk of severe stunting for untreated HIV+'),
# Natural recovery from stunting
'mean_years_to_1stdev_natural_improvement_in_stunting': Parameter(
Types.REAL,
'Mean time (in years) to a one standard deviation improvement in stunting without any treatment.'),
# The effect of treatment
'effectiveness_of_complementary_feeding_education_in_stunting_reduction': Parameter(
Types.REAL,
'Probability of stunting being reduced by one standard deviation (category) by education about '
'supplementary feeding (but not supplying supplementary feeding consmables).'),
'effectiveness_of_food_supplementation_in_stunting_reduction': Parameter(
Types.REAL,
'Probability of stunting being reduced by one standard deviation (category) by supplementary feeding.'),
# The probability of a (severe) stunting person being checked and correctly diagnosed
'prob_stunting_diagnosed_at_generic_appt': Parameter(
Types.REAL,
'Probability of a stunted or severely stunted person being checked and correctly diagnosed'),
}
PROPERTIES = {
'un_HAZ_category': Property(Types.CATEGORICAL,
'Indicator of current stunting status - the height-for-age z-score category:'
'"HAZ>=-2" == No Stunting (within 2 standard deviations of mean); '
'"-3<=HAZ<-2" == Non-Severe Stunting (2-3 standard deviations from mean); '
'"HAZ<-3 == Severe Stunting (more than 3 standard deviations from mean)',
categories=['HAZ<-3', '-3<=HAZ<-2', 'HAZ>=-2']),
}
[docs]
def __init__(self, name=None, resourcefilepath=None):
super().__init__(name)
self.resourcefilepath = resourcefilepath
self.models = None # (Will store the models used in the module)
self.cons_item_codes = None # (Will store consumable item codes)
[docs]
def read_parameters(self, data_folder):
self.load_parameters_from_dataframe(
pd.read_excel(
Path(self.resourcefilepath) / 'ResourceFile_Stunting.xlsx', sheet_name='Parameter_values')
)
[docs]
def initialise_population(self, population):
"""Set initial prevalence of stunting according to distributions provided in parameters"""
df = population.props
p = self.parameters
# Set default for property
df.loc[df.is_alive, 'un_HAZ_category'] = 'HAZ>=-2'
def get_probs_stunting(_agegp):
"""For the a given HAZ distribution (specified in the parameters by age-group), find the odds of
a value <-2 (= 'stunted') and the odds of a value <-3 given a value <-2 (= 'severely stunted')."""
mean, stdev = p[f'prev_HAZ_distribution_age_{_agegp[0]}_{_agegp[1]}mo']
haz_distribution = norm(loc=mean, scale=stdev)
# Compute proportion "stunted" (HAZ <-2)
p_stunted = haz_distribution.cdf(-2.0)
# Compute proportion "severely stunted" given "stunted" (HAZ <-3 given HAZ <-2)
p_severely_stunted_given_stunted = haz_distribution.cdf(-3.0) / haz_distribution.cdf(-2.0)
# Return results needed as named tuple:
result = namedtuple('probs', ['prob_stunting', 'prob_severe_given_stunting'])
return result(
p_stunted,
p_severely_stunted_given_stunted
)
def make_scaled_linear_model_stunting(target_prob, mask):
def make_linear_model_stunting(intercept=1.0):
return LinearModel(
LinearModelType.LOGISTIC,
intercept,
Predictor('sex').when('M', p['or_stunting_male']),
Predictor('li_wealth').when(2, p['or_stunting_hhwealth_Q2'])
.when(3, p['or_stunting_hhwealth_Q3'])
.when(4, p['or_stunting_hhwealth_Q4'])
.when(5, p['or_stunting_hhwealth_Q5']),
Predictor().when('(nb_size_for_gestational_age == "small_for_gestational_age") '
'& (nb_late_preterm == False) & (nb_early_preterm == False)',
p['or_stunting_SGA_and_term']),
Predictor().when('(nb_size_for_gestational_age == "small_for_gestational_age") '
'& ((nb_late_preterm == True) | (nb_early_preterm == True))',
p['or_stunting_SGA_and_preterm']),
Predictor().when('(nb_size_for_gestational_age == "average_for_gestational_age") '
'& ((nb_late_preterm == True) | (nb_early_preterm == True))',
p['or_stunting_preterm_and_AGA'])
)
unscaled_lm = make_linear_model_stunting()
actual_prob = unscaled_lm.predict(df.loc[mask]).mean()
actual_odds = actual_prob / (1.0 - actual_prob)
target_odds = target_prob / (1.0 - target_prob)
return make_linear_model_stunting(
intercept=target_odds / actual_odds if not np.isnan(actual_prob) else target_odds
)
for agegp in [(0, 5), (6, 11), (12, 23), (24, 35), (36, 47), (48, 59)]: # in months
p_stunting = get_probs_stunting(agegp)
low_bound_age_in_years = agegp[0] / 12.0
high_bound_age_in_years = (1 + agegp[1]) / 12.0
mask = (
df.is_alive
& (df.age_exact_years >= low_bound_age_in_years)
& (df.age_exact_years < high_bound_age_in_years)
)
stunted = make_scaled_linear_model_stunting(target_prob=p_stunting.prob_stunting, mask=mask).predict(
df.loc[mask], self.rng, squeeze_single_row_output=False)
severely_stunted_idx = stunted.loc[
stunted & (self.rng.random_sample(len(stunted)) < p_stunting.prob_severe_given_stunting)].index
stunted_but_not_severe_idx = stunted[stunted].index.difference(severely_stunted_idx)
df.loc[stunted_but_not_severe_idx, "un_HAZ_category"] = '-3<=HAZ<-2'
df.loc[severely_stunted_idx, "un_HAZ_category"] = 'HAZ<-3'
[docs]
def initialise_simulation(self, sim):
"""Prepare for simulation to start"""
# Establish the models
self.models = Models(self)
# Schedule the main polling event to begin on the first day of the simulation
sim.schedule_event(StuntingPollingEvent(self), sim.date)
# Schedule the logging event to begin on the first day of the simulation
sim.schedule_event(StuntingLoggingEvent(self), sim.date)
# Look-up consumable item codes
self.look_up_consumable_item_codes()
[docs]
def on_birth(self, mother_id, child_id):
"""Set that on birth there is no stunting"""
self.sim.population.props.loc[child_id, 'un_HAZ_category'] = 'HAZ>=-2'
[docs]
def look_up_consumable_item_codes(self):
"""Look up the item codes that used in the HSI in the module"""
get_item_codes = self.sim.modules['HealthSystem'].get_item_code_from_item_name
self.cons_item_codes = dict()
self.cons_item_codes['supplementary_feeding'] = get_item_codes('Supplementary spread, sachet 92g/CAR-150')
self.cons_item_codes['education_for_supplementary_feeding'] = get_item_codes(
'Complementary feeding--education only drugs/supplies to service a client')
[docs]
def do_onset(self, idx: pd.Index):
"""Represent the onset of stunting for the person_id given in `idx`"""
df = self.sim.population.props
df.loc[idx, 'un_HAZ_category'] = '-3<=HAZ<-2'
[docs]
def do_progression(self, idx: pd.Index):
"""Represent the progression to severe stunting for the person_id given in `idx`"""
df = self.sim.population.props
df.loc[idx, 'un_HAZ_category'] = 'HAZ<-3'
[docs]
def do_recovery(self, idx: Union[list, pd.Index]):
"""Represent the recovery from stunting for the person_id given in `idx`. Recovery causes the person to move
'up' one level: i.e. 'HAZ<-3' --> '-3<=HAZ<-2' or '-3<=HAZ<-2' --> 'HAZ>=-2'"""
df = self.sim.population.props
df.loc[idx, 'un_HAZ_category'] = df.loc[idx, 'un_HAZ_category'].map({
'HAZ<-3': '-3<=HAZ<-2',
'-3<=HAZ<-2': 'HAZ>=-2'
})
[docs]
def do_routine_assessment_for_chronic_undernutrition(self, person_id):
"""This is called by the a generic HSI event for every child aged less than 5 years. It assesses stunting
and schedules an HSI as needed."""
df = self.sim.population.props
person = df.loc[person_id]
is_stunted = person.un_HAZ_category in ('HAZ<-3', '-3<=HAZ<-2')
p_stunting_diagnosed = self.parameters['prob_stunting_diagnosed_at_generic_appt']
if not is_stunted:
return
# Schedule the HSI for provision of treatment based on the probability of stunting diagnosis
if p_stunting_diagnosed > self.rng.random_sample():
self.sim.modules['HealthSystem'].schedule_hsi_event(
hsi_event=HSI_Stunting_ComplementaryFeeding(module=self, person_id=person_id),
priority=2, # <-- lower priority that for wasting and most other HSI
topen=self.sim.date)
[docs]
def do_treatment(self, person_id, prob_success):
"""Represent the treatment with supplementary feeding. If treatment is successful, effect the recovery
of the person immediately."""
if prob_success > self.rng.random_sample():
self.do_recovery([person_id])
class Models:
def __init__(self, module):
self.module = module
self.p = module.parameters
self.lm_prob_becomes_stunted = self.make_lm_prob_becomes_stunted()
self.lm_prob_progression_to_severe_stunting = self.make_lm_prob_progression_to_severe_stunting()
self.lm_prob_natural_recovery = self.make_lm_prob_natural_recovery()
def make_lm_prob_becomes_stunted(self):
"""Returns LinearModel for the probability per year of becoming stunted."""
p = self.p
return LinearModel.multiplicative(
Predictor(
conditions_are_exhaustive=True, conditions_are_mutually_exclusive=True
).when(
"age_exact_years < 0.5", p["base_inc_rate_stunting_by_agegp"][0]
).when(
"0.5 <= age_exact_years < 1", p["base_inc_rate_stunting_by_agegp"][1]
).when(
"1 <= age_exact_years < 2", p["base_inc_rate_stunting_by_agegp"][2]
).when(
"2 <= age_exact_years < 3", p["base_inc_rate_stunting_by_agegp"][3]
).when(
"3 <= age_exact_years < 4", p["base_inc_rate_stunting_by_agegp"][4]
).when(
"4 <= age_exact_years < 5", p["base_inc_rate_stunting_by_agegp"][5]
).when(
"age_exact_years >= 5", 0.0
),
Predictor('li_wealth',
conditions_are_mutually_exclusive=True).when(1, 1.0)
.otherwise(p['rr_stunting_wealth_level']),
Predictor('gi_number_of_episodes').apply(lambda x: p['rr_stunting_per_diarrhoeal_episode'] ** x),
Predictor('un_ever_wasted',
conditions_are_exhaustive=True,
conditions_are_mutually_exclusive=True).when(True, p['rr_stunting_prior_wasting'])
.when(False, 1.0),
Predictor().when('(nb_size_for_gestational_age == "small_for_gestational_age") '
'& (nb_late_preterm == False) & (nb_early_preterm == False)',
p['rr_stunting_SGA_and_term']),
Predictor().when('(nb_size_for_gestational_age == "small_for_gestational_age") '
'& ((nb_late_preterm == True) | (nb_early_preterm == True))',
p['rr_stunting_SGA_and_preterm']),
Predictor().when('(nb_size_for_gestational_age == "average_for_gestational_age") '
'& ((nb_late_preterm == True) | (nb_early_preterm == True))',
p['rr_stunting_preterm_and_AGA']),
Predictor().when('(hv_inf == True) & (hv_art == "not")', p['rr_stunting_untreated_HIV']),
Predictor().when('((nb_breastfeeding_status == "non_exclusive") | '
'(nb_breastfeeding_status == "none")) & (age_exact_years < 0.5)',
p['rr_stunting_no_exclusive_breastfeeding']),
Predictor().when(
'(nb_breastfeeding_status == "none") & (0.5 <= age_exact_years < 2.0)',
p['rr_stunting_no_continued_breastfeeding']
),
)
def make_lm_prob_progression_to_severe_stunting(self):
"""Return LinearModel for the probability per year of progressing from a state of being Stunted (but not severe)
(-3 < HAZ <= -2) to a state of Severe Stunting (HAZ <= -3)"""
p = self.p
return LinearModel.multiplicative(
Predictor(
conditions_are_exhaustive=True, conditions_are_mutually_exclusive=True
).when(
'age_exact_years < 0.5', p['r_progression_severe_stunting_by_agegp'][0]
)
.when(
'0.5 <= age_exact_years < 1.0',
p['r_progression_severe_stunting_by_agegp'][1]
)
.when(
'1.0 <= age_exact_years < 2.0',
p['r_progression_severe_stunting_by_agegp'][2]
)
.when(
'2.0 <= age_exact_years < 3.0',
p['r_progression_severe_stunting_by_agegp'][3]
)
.when(
'3.0 <= age_exact_years < 4.0',
p['r_progression_severe_stunting_by_agegp'][4]
)
.when(
'4.0 <= age_exact_years < 5.0',
p['r_progression_severe_stunting_by_agegp'][5]
)
.when(
'age_exact_years >= 5.0', 1.0
),
Predictor('un_ever_wasted',
conditions_are_exhaustive=True,
conditions_are_mutually_exclusive=True
).when(True, p['rr_progress_severe_stunting_if_prior_wasting'])
.when(False, 1.0),
Predictor().when('(hv_inf == True) & (hv_art == "not")', p['rr_stunting_untreated_HIV']),
)
def make_lm_prob_natural_recovery(self):
"""Return LinearModel for the probability per year of improving by one category (one HAZ standard deviation)
i.e. from being severely stunted (HAZ<-3) to non-severely stunted (-3<HAZ<=-2); and from being non-severely
stunted to not Stunted (HAZ>-2)."""
p = self.p
mean_years = p['mean_years_to_1stdev_natural_improvement_in_stunting']
prob_recovery_per_year = 1.0 - np.exp(-1.0 / mean_years)
return LinearModel(
LinearModelType.MULTIPLICATIVE,
intercept=prob_recovery_per_year
)
# ---------------------------------------------------------------------------------------------------------
# NATURAL HISTORY EVENTS
# ---------------------------------------------------------------------------------------------------------
[docs]
class StuntingPollingEvent(RegularEvent, PopulationScopeEventMixin):
"""Regular event that controls the onset of stunting, progression to severe stunting and natural recovery."""
[docs]
def __init__(self, module):
super().__init__(module, frequency=DateOffset(months=1))
assert isinstance(module, Stunting)
[docs]
def apply(self, population):
"""
* Determine who will be onset for stunting (among those not stunted) and effect that change;
* Determine who will be onset for recovery (among those stunted) and effect that change.
* Determine who will be onset for progression (among those stunted but not severely) and effect that change;
"""
df = population.props
models = self.module.models
days_until_next_polling_event = (self.sim.date + self.frequency - self.sim.date) / np.timedelta64(1, 'D')
# Onset of Stunting
eligible_for_stunting = (df.is_alive &
(df.age_exact_years < 5.0) &
(df.un_HAZ_category == 'HAZ>=-2'))
idx_will_be_stunted = self.apply_model(
model=models.lm_prob_becomes_stunted,
mask=eligible_for_stunting,
days_exposed_to_risk=days_until_next_polling_event
)
self.module.do_onset(idx_will_be_stunted)
# Recovery from Stunting
eligible_for_recovery = (df.is_alive &
(df.age_exact_years < 5.0) &
(df.un_HAZ_category != 'HAZ>=-2') &
~df.index.isin(idx_will_be_stunted))
idx_will_recover = self.apply_model(
model=models.lm_prob_natural_recovery,
mask=eligible_for_recovery,
days_exposed_to_risk=days_until_next_polling_event
)
self.module.do_recovery(idx_will_recover)
# Progression to Severe Stunting
eligible_for_progression = (df.is_alive &
(df.age_exact_years < 5.0) &
(df.un_HAZ_category == '-3<=HAZ<-2') & ~df.index.isin(idx_will_be_stunted)
& ~df.index.isin(idx_will_recover))
idx_will_progress = self.apply_model(
model=models.lm_prob_progression_to_severe_stunting,
mask=eligible_for_progression,
days_exposed_to_risk=days_until_next_polling_event
)
self.module.do_progression(idx_will_progress)
[docs]
def apply_model(self, model, mask, days_exposed_to_risk):
"""Return the person_ids selected for an event of occur to in a given period (in days), as specified by a
LinearModel that provides the annual risk of the event.
* Looks-up annual probability of the event using a linear model supplied in `model` to a population masked with
`mask`
* Converts the annual probability to a probability of the event occurring during the number of
`days_exposed_to_risk`
* Randomly selects which individuals will have the events
"""
df = self.sim.population.props
rng = self.module.rng
annual_prob = model.predict(df.loc[mask]).clip(upper=1.0)
cum_prob_over_days_exposed = 1.0 - (1.0 - annual_prob) ** (days_exposed_to_risk / DAYS_IN_YEAR)
assert pd.notnull(cum_prob_over_days_exposed).all()
return mask[mask].index[cum_prob_over_days_exposed > rng.random_sample(mask.sum())]
# ---------------------------------------------------------------------------------------------------------
# Logging
# ---------------------------------------------------------------------------------------------------------
[docs]
class StuntingLoggingEvent(RegularEvent, PopulationScopeEventMixin):
"""Logging event occurring every year"""
[docs]
def __init__(self, module):
super().__init__(module, frequency=DateOffset(years=1))
assert isinstance(module, Stunting)
[docs]
def apply(self, population):
"""Log the current distribution of stunting classification by age"""
df = population.props
d_to_log = df.loc[df.is_alive & (df.age_years < 5)].groupby(
by=['age_years', 'un_HAZ_category']).size().sort_index().to_dict()
def convert_keys_to_string(d):
return {str(k): v for k, v in d.items()}
logger.info(
key='prevalence',
data=convert_keys_to_string(d_to_log),
description='Current number of children in each stunting category by single year of age.'
)
# ---------------------------------------------------------------------------------------------------------
# HSI
# ---------------------------------------------------------------------------------------------------------
[docs]
class HSI_Stunting_ComplementaryFeeding(HSI_Event, IndividualScopeEventMixin):
"""HSI for complementary feeding, either with the provision of supplementary foods or with education only."""
[docs]
def __init__(self, module, person_id):
super().__init__(module, person_id=person_id)
self.TREATMENT_ID = 'Undernutrition_Feeding'
self.EXPECTED_APPT_FOOTPRINT = self.make_appt_footprint({'U5Malnutr': 1})
self.ACCEPTED_FACILITY_LEVEL = '1a'
[docs]
def apply(self, person_id, squeeze_factor):
df = self.sim.population.props
person = df.loc[person_id]
if not person.is_alive:
return
# Only do anything if child is under 5 and remains stunted
if (person.age_years < 5) and (person.un_HAZ_category in ['HAZ<-3', '-3<=HAZ<-2']):
# Provide supplementary feeding if consumable available, otherwise provide 'education only' (which has a
# different probability of success).
if self.get_consumables(item_codes=self.module.cons_item_codes['supplementary_feeding']):
self.module.do_treatment(person_id, prob_success=self.module.parameters[
'effectiveness_of_food_supplementation_in_stunting_reduction'])
else:
# Request consumables for provision of education for supplementary feeding, but do not let
# non-availability prevent provision of the intervention.
_ = self.get_consumables(
item_codes=self.module.cons_item_codes['education_for_supplementary_feeding'])
self.module.do_treatment(person_id, prob_success=self.module.parameters[
'effectiveness_of_complementary_feeding_education_in_stunting_reduction'])
# Schedule a further instance of this HSI for monitoring and resupply of consumables.
self.sim.modules['HealthSystem'].schedule_hsi_event(
hsi_event=self,
priority=2, # <-- lower priority that for wasting and most other HSI
topen=self.sim.date + pd.DateOffset(months=6)
)
# ---------------------------------------------------------------------------------------------------------
# ACCESSORIES FOR TESTING
# ---------------------------------------------------------------------------------------------------------
[docs]
class StuntingPropertiesOfOtherModules(Module):
"""For the purpose of the testing, this module generates the properties upon which the Stunting module relies"""
INIT_DEPENDENCIES = {'Demography'}
# Though this module provides some properties from NewbornOutcomes we do not list
# NewbornOutcomes in the ALTERNATIVE_TO set to allow using in conjunction with
# SimplifiedBirths which can also be used as an alternative to NewbornOutcomes
ALTERNATIVE_TO = {'Hiv', 'Wasting', 'Diarrhoea'}
PROPERTIES = {
'hv_inf': Property(Types.BOOL, 'temporary property'),
'hv_art': Property(Types.CATEGORICAL, 'temporary property',
categories=['not', 'on_VL_suppressed', 'on_not_VL_suppressed']),
'nb_low_birth_weight_status': Property(Types.CATEGORICAL, 'temporary property',
categories=['extremely_low_birth_weight', 'very_low_birth_weight',
'low_birth_weight', 'normal_birth_weight']),
'nb_size_for_gestational_age': Property(Types.CATEGORICAL, 'temporary property',
categories=['small_for_gestational_age',
'average_for_gestational_age']),
'nb_late_preterm': Property(Types.BOOL, 'temporary property'),
'nb_early_preterm': Property(Types.BOOL, 'temporary property'),
'nb_breastfeeding_status': Property(Types.CATEGORICAL, 'temporary property',
categories=['none', 'non_exclusive', 'exclusive']),
'un_ever_wasted': Property(Types.BOOL, 'temporary property'),
'gi_number_of_episodes': Property(Types.INT, 'temporary property')
}
[docs]
def __init__(self, name=None):
super().__init__(name)
[docs]
def read_parameters(self, data_folder):
pass
[docs]
def initialise_population(self, population):
df = population.props
df.loc[df.is_alive, 'hv_inf'] = False
df.loc[df.is_alive, 'hv_art'] = 'not'
df.loc[df.is_alive, 'nb_low_birth_weight_status'] = 'normal_birth_weight'
df.loc[df.is_alive, 'nb_size_for_gestational_age'] = 'average_for_gestational_age'
df.loc[df.is_alive, 'nb_late_preterm'] = False
df.loc[df.is_alive, 'nb_early_preterm'] = False
df.loc[df.is_alive, 'nb_breastfeeding_status'] = 'exclusive'
df.loc[df.is_alive, 'un_ever_wasted'] = False
df.loc[df.is_alive, 'gi_number_of_episodes'] = 0
[docs]
def initialise_simulation(self, sim):
pass
[docs]
def on_birth(self, mother, child):
df = self.sim.population.props
df.at[child, 'hv_inf'] = False
df.at[child, 'hv_art'] = 'not'
df.at[child, 'nb_low_birth_weight_status'] = 'normal_birth_weight'
df.at[child, 'nb_size_for_gestational_age'] = 'average_for_gestational_age'
df.at[child, 'nb_late_preterm'] = False
df.at[child, 'nb_early_preterm'] = False
df.at[child, 'nb_breastfeeding_status'] = 'exclusive'
df.at[child, 'un_ever_wasted'] = False
df.at[child, 'gi_number_of_episodes'] = 0