Skip to content

Commit c07383f

Browse files
Param Revamp - Stunting (#1722)
* param revamp stunting * change rr_stunting_per_diarrhoeal_episode to local * clean-up parameter_values.csv --------- Co-authored-by: Tim Hallett <39991060+tbhallett@users.noreply.github.com>
1 parent 3ad366e commit c07383f

File tree

3 files changed

+43
-20
lines changed

3 files changed

+43
-20
lines changed

resources/ResourceFile_Stunting/Parameter_values.csv

Lines changed: 0 additions & 3 deletions
This file was deleted.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:b306a546a4680e4459cc878ef8e04475cb29a9d2022edad9bcea240dbf76f4e1
3+
size 3117

src/tlo/methods/stunting.py

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,25 @@ class Stunting(Module, GenericFirstAppointmentsMixin):
135135
'prob_stunting_diagnosed_at_generic_appt': Parameter(
136136
Types.REAL,
137137
'Probability of a stunted or severely stunted person being checked and correctly diagnosed'),
138+
139+
# Age and classification thresholds
140+
'max_age_for_stunting_years': Parameter(
141+
Types.REAL,
142+
'Maximum age (in years) for which stunting assessment and interventions apply'),
143+
'haz_threshold_stunted': Parameter(
144+
Types.REAL,
145+
'HAZ score threshold below which a child is considered stunted'),
146+
'haz_threshold_severe_stunted': Parameter(
147+
Types.REAL,
148+
'HAZ score threshold below which a child is considered severely stunted'),
149+
150+
# Event timing
151+
'main_polling_frequency_months': Parameter(
152+
Types.INT,
153+
'Frequency in months for the main stunting polling event'),
154+
'followup_appointment_months': Parameter(
155+
Types.INT,
156+
'Interval in months for scheduling follow-up appointments after stunting treatment'),
138157
}
139158

140159
PROPERTIES = {
@@ -153,7 +172,7 @@ def __init__(self, name=None):
153172

154173
def read_parameters(self, resourcefilepath: Optional[Path]=None):
155174
self.load_parameters_from_dataframe(
156-
read_csv_files(resourcefilepath / 'ResourceFile_Stunting', files='Parameter_values')
175+
read_csv_files(resourcefilepath / 'ResourceFile_Stunting', files='parameter_values')
157176
)
158177

159178
def initialise_population(self, population):
@@ -171,11 +190,13 @@ def get_probs_stunting(_agegp):
171190
mean, stdev = p[f'prev_HAZ_distribution_age_{_agegp[0]}_{_agegp[1]}mo']
172191
haz_distribution = norm(loc=mean, scale=stdev)
173192

174-
# Compute proportion "stunted" (HAZ <-2)
175-
p_stunted = haz_distribution.cdf(-2.0)
193+
# Compute proportion "stunted" (HAZ < threshold)
194+
p_stunted = haz_distribution.cdf(p['haz_threshold_stunted'])
176195

177-
# Compute proportion "severely stunted" given "stunted" (HAZ <-3 given HAZ <-2)
178-
p_severely_stunted_given_stunted = haz_distribution.cdf(-3.0) / haz_distribution.cdf(-2.0)
196+
# Compute proportion "severely stunted" given "stunted"
197+
# (HAZ < severe threshold given HAZ < stunted threshold)
198+
p_severely_stunted_given_stunted = (haz_distribution.cdf(p['haz_threshold_severe_stunted']) /
199+
haz_distribution.cdf(p['haz_threshold_stunted']))
179200

180201
# Return results needed as named tuple:
181202
result = namedtuple('probs', ['prob_stunting', 'prob_severe_given_stunting'])
@@ -308,7 +329,7 @@ def do_at_generic_first_appt(
308329

309330
# Schedule the HSI for provision of treatment based on the probability of
310331
# stunting diagnosis, provided the necessary symptoms are there.
311-
if individual_properties["age_years"] <= 5 and is_stunted:
332+
if individual_properties["age_years"] <= self.parameters["max_age_for_stunting_years"] and is_stunted:
312333
# Schedule the HSI for provision of treatment based on the probability of
313334
# stunting diagnosis
314335
if p_stunting_diagnosed > self.rng.random_sample():
@@ -350,7 +371,7 @@ def make_lm_prob_becomes_stunted(self):
350371
).when(
351372
"4 <= age_exact_years < 5", p["base_inc_rate_stunting_by_agegp"][5]
352373
).when(
353-
"age_exact_years >= 5", 0.0
374+
f"age_exact_years >= {p['max_age_for_stunting_years']}", 0.0
354375
),
355376
Predictor('li_wealth',
356377
conditions_are_mutually_exclusive=True).when(1, 1.0)
@@ -411,7 +432,7 @@ def make_lm_prob_progression_to_severe_stunting(self):
411432
p['r_progression_severe_stunting_by_agegp'][5]
412433
)
413434
.when(
414-
'age_exact_years >= 5.0', 1.0
435+
f'age_exact_years >= {p["max_age_for_stunting_years"]}', 1.0
415436
),
416437
Predictor('un_ever_wasted',
417438
conditions_are_exhaustive=True,
@@ -444,7 +465,7 @@ class StuntingPollingEvent(RegularEvent, PopulationScopeEventMixin):
444465
"""Regular event that controls the onset of stunting, progression to severe stunting and natural recovery."""
445466

446467
def __init__(self, module):
447-
super().__init__(module, frequency=DateOffset(months=1))
468+
super().__init__(module, frequency=DateOffset(months=module.parameters['main_polling_frequency_months']))
448469
assert isinstance(module, Stunting)
449470

450471
def apply(self, population):
@@ -459,7 +480,7 @@ def apply(self, population):
459480

460481
# Onset of Stunting
461482
eligible_for_stunting = (df.is_alive &
462-
(df.age_exact_years < 5.0) &
483+
(df.age_exact_years < self.module.parameters['max_age_for_stunting_years']) &
463484
(df.un_HAZ_category == 'HAZ>=-2'))
464485
idx_will_be_stunted = self.apply_model(
465486
model=models.lm_prob_becomes_stunted,
@@ -470,7 +491,7 @@ def apply(self, population):
470491

471492
# Recovery from Stunting
472493
eligible_for_recovery = (df.is_alive &
473-
(df.age_exact_years < 5.0) &
494+
(df.age_exact_years < self.module.parameters['max_age_for_stunting_years']) &
474495
(df.un_HAZ_category != 'HAZ>=-2') &
475496
~df.index.isin(idx_will_be_stunted))
476497
idx_will_recover = self.apply_model(
@@ -482,7 +503,7 @@ def apply(self, population):
482503

483504
# Progression to Severe Stunting
484505
eligible_for_progression = (df.is_alive &
485-
(df.age_exact_years < 5.0) &
506+
(df.age_exact_years < self.module.parameters['max_age_for_stunting_years']) &
486507
(df.un_HAZ_category == '-3<=HAZ<-2') & ~df.index.isin(idx_will_be_stunted)
487508
& ~df.index.isin(idx_will_recover))
488509
idx_will_progress = self.apply_model(
@@ -527,8 +548,9 @@ def apply(self, population):
527548
"""Log the current distribution of stunting classification by age"""
528549
df = population.props
529550

530-
subset = df.loc[df.is_alive & (df.age_years < 5)].copy()
531-
subset["age_years"] = pd.Categorical(subset["age_years"], categories=range(5))
551+
max_age = int(self.module.parameters['max_age_for_stunting_years'])
552+
subset = df.loc[df.is_alive & (df.age_years < max_age)].copy()
553+
subset["age_years"] = pd.Categorical(subset["age_years"], categories=range(max_age))
532554
d_to_log = subset.groupby(
533555
by=['age_years', 'un_HAZ_category']).size().sort_index().to_dict()
534556

@@ -564,8 +586,9 @@ def apply(self, person_id, squeeze_factor):
564586
if not person.is_alive:
565587
return
566588

567-
# Only do anything if child is under 5 and remains stunted
568-
if (person.age_years < 5) and (person.un_HAZ_category in ['HAZ<-3', '-3<=HAZ<-2']):
589+
# Only do anything if child is under max age and remains stunted
590+
if (person.age_years < self.module.parameters['max_age_for_stunting_years']) and \
591+
(person.un_HAZ_category in ['HAZ<-3', '-3<=HAZ<-2']):
569592

570593
# Provide supplementary feeding if consumable available, otherwise provide 'education only' (which has a
571594
# different probability of success).
@@ -585,7 +608,7 @@ def apply(self, person_id, squeeze_factor):
585608
self.sim.modules['HealthSystem'].schedule_hsi_event(
586609
hsi_event=self,
587610
priority=2, # <-- lower priority that for wasting and most other HSI
588-
topen=self.sim.date + pd.DateOffset(months=6)
611+
topen=self.sim.date + pd.DateOffset(months=self.module.parameters['followup_appointment_months'])
589612
)
590613

591614

0 commit comments

Comments
 (0)