Skip to content

Commit 80a1c40

Browse files
update parameters to enable interventions: a simpler and more reader-friendly way (#1081)
* co: more-readable update_params_for_interventions() fnc diff --git src/tlo/methods/contraception.py src/tlo/methods/contraception.py index aa1efa250..c3e8f9f77 100644 --- src/tlo/methods/contraception.py +++ src/tlo/methods/contraception.py @@ -596,122 +596,37 @@ class Contraception(Module): processed_params = self.processed_params - def expand_to_age_years(values_by_age_groups, ages_by_year): - _d = dict(zip(['15-19', '20-24', '25-29', '30-34', '35-39', '40-44', '45-49'], values_by_age_groups)) - return np.array( - [_d[self.sim.modules['Demography'].AGE_RANGE_LOOKUP[_age_year]] for _age_year in ages_by_year] - ) + def contraception_initiation_with_interv(p_start_per_month_without_interv): + """Increase the probabilities of a woman starting modern contraceptives due to Pop intervention being + applied.""" + # TODO: remove the keys before intervention year + p_start_per_month_with_interv = {} + for year, age_method_df in p_start_per_month_without_interv.items(): + p_start_per_month_with_interv[year] = age_method_df * self.parameters['Interventions_Pop'].loc[0] + return p_start_per_month_with_interv - def time_age_trend_in_initiation(): - """The age-specific effect of calendar year on the probability of starting use of contraceptive - (multiplicative effect). Values are chosen to induce a trend in age-specific fertility consistent with - the WPP estimates.""" + def contraception_initiation_after_birth_with_interv(p_start_after_birth_without_interv): + """Increase the probabilities of a woman starting modern contraceptives following giving birth due to PPFP + intervention being applied.""" + # Exclude prob of 'not_using' + p_start_after_birth_with_interv = p_start_after_birth_without_interv.copy().drop('not_using') - _years = np.arange(2010, 2101) - _ages = np.arange(15, 50) + # Apply PPFP intervention multipliers (ie increase probs of modern methods) + p_start_after_birth_with_interv = \ + p_start_after_birth_with_interv.mul(self.parameters['Interventions_PPFP'].loc[0]) - _init_over_time = np.exp(+0.05 * np.minimum(2020 - 2010, (_years - 2010))) * np.maximum(1.0, np.exp( - +0.01 * (_years - 2020))) - _init_over_time_modification_by_age = 1.0 / expand_to_age_years([1.0, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5], _ages) - _init = np.outer(_init_over_time, _init_over_time_modification_by_age) + # Return reduced prob of 'not_using' + p_start_after_birth_with_interv = pd.Series((1.0 - p_start_after_birth_with_interv.sum()), + index=['not_using']).append(p_start_after_birth_with_interv) - return pd.DataFrame(index=_years, columns=_ages, data=_init) + return p_start_after_birth_with_interv - def avoid_sterilization_below30(probs): - """Prevent women below 30 years having female sterilization and adjust the probability for women 30 and over - to preserve the overall probability of initiating sterilization.""" - # Input 'probs' must include probs for all methods including 'not_using' - assert set(probs.index) == set(self.all_contraception_states) - - # Prevent women below 30 years having 'female_sterilization' - probs_below30 = probs.copy() - probs_below30['female_sterilization'] = 0.0 - # Scale so that the probability of all outcomes sum to 1.0 - probs_below30 = probs_below30 / probs_below30.sum() - assert np.isclose(1.0, probs_below30.sum()) - - # Increase prob of 'female_sterilization' in older women accordingly - probs_30plus = probs.copy() - probs_30plus['female_sterilization'] = ( - probs.loc['female_sterilization'] / - self.ratio_n_females_30_49_to_15_49_in_2010 - ) - # Scale so that the probability of all outcomes sum to 1.0 - probs_30plus = probs_30plus / probs_30plus.sum() - assert np.isclose(1.0, probs_30plus.sum()) - - return probs_below30, probs_30plus - - def contraception_initiation_with_interv(): - """Generate the probability per month of a woman initiating onto each contraceptive, by the age (in whole - years) if FP interventions are applied.""" - - # Probability of initiation by method per month (average over all ages) - p_init_by_method = self.parameters['Initiation_ByMethod'].loc[0] - - # Prevent women below 30 years having 'female_sterilization' while preserving the overall probability of - # 'female_sterilization' initiation - p_init_by_method_below30, p_init_by_method_30plus = avoid_sterilization_below30(p_init_by_method) - - # Effect of age - age_effect = 1.0 + self.parameters['Initiation_ByAge'].set_index('age')['r_init1_age'].rename_axis( - "age_years") - - # Year effect - year_effect = time_age_trend_in_initiation() - - def apply_intervention_age_year_effects(probs_below30, probs_30plus): - # Apply Pop intervention - probs_by_method_below30 = \ - probs_below30.copy().drop('not_using').mul(self.parameters['Interventions_Pop'].loc[0]) - probs_by_method_30plus = \ - probs_30plus.copy().drop('not_using').mul(self.parameters['Interventions_Pop'].loc[0]) - # Assemble into age-specific data-frame: - p_init = dict() - for year in year_effect.index: - - p_init_this_year = dict() - for a in age_effect.index: - if a < 30: - p_init_this_year[a] = probs_by_method_below30 * age_effect.at[a] * year_effect.at[year, a] - else: - p_init_this_year[a] = probs_by_method_30plus * age_effect.at[a] * year_effect.at[year, a] - p_init_this_year_df = pd.DataFrame.from_dict(p_init_this_year, orient='index') - - # Check correct format of age/method data-frame - assert set(p_init_this_year_df.columns) == set(self.all_contraception_states - {'not_using'}) - assert (p_init_this_year_df.index == range(15, 50)).all() - assert (p_init_this_year_df >= 0.0).all().all() - - p_init[year] = p_init_this_year_df - - return p_init - - return apply_intervention_age_year_effects(p_init_by_method_below30, p_init_by_method_30plus) - - def contraception_initiation_after_birth_with_interv(): - """Get the probability of a woman starting a contraceptive following giving birth if FP interventions are - applied. Avoid sterilization in women below 30 years old.""" - - # Get initiation probabilities of contraception methods after birth from read-in Excel sheet - p_start_after_birth = self.parameters['Initiation_AfterBirth'].loc[0].drop('not_using') - - # Apply PPFP intervention multipliers - p_start_after_birth = p_start_after_birth.mul(self.parameters['Interventions_PPFP'].loc[0]) - - # Add 'not_using' to initiation probabilities of contraception methods after birth - p_start_after_birth = pd.concat( - ( - pd.Series((1.0 - p_start_after_birth.sum()), index=['not_using']), - p_start_after_birth - ) - ) - - return avoid_sterilization_below30(p_start_after_birth) - - processed_params['p_start_per_month'] = contraception_initiation_with_interv() - processed_params['p_start_after_birth_below30'], processed_params['p_start_after_birth_30plus'] =\ - contraception_initiation_after_birth_with_interv() + processed_params['p_start_per_month'] = \ + contraception_initiation_with_interv(processed_params['p_start_per_month']) + processed_params['p_start_after_birth_below30'] = \ + contraception_initiation_after_birth_with_interv(processed_params['p_start_after_birth_below30']) + processed_params['p_start_after_birth_30plus'] = \ + contraception_initiation_after_birth_with_interv(processed_params['p_start_after_birth_30plus']) return processed_params * co: do not save init probs for years before current sim year
1 parent 8bdbacc commit 80a1c40

File tree

1 file changed

+31
-116
lines changed

1 file changed

+31
-116
lines changed

src/tlo/methods/contraception.py

+31-116
Original file line numberDiff line numberDiff line change
@@ -596,122 +596,37 @@ def update_params_for_interventions(self):
596596

597597
processed_params = self.processed_params
598598

599-
def expand_to_age_years(values_by_age_groups, ages_by_year):
600-
_d = dict(zip(['15-19', '20-24', '25-29', '30-34', '35-39', '40-44', '45-49'], values_by_age_groups))
601-
return np.array(
602-
[_d[self.sim.modules['Demography'].AGE_RANGE_LOOKUP[_age_year]] for _age_year in ages_by_year]
603-
)
604-
605-
def time_age_trend_in_initiation():
606-
"""The age-specific effect of calendar year on the probability of starting use of contraceptive
607-
(multiplicative effect). Values are chosen to induce a trend in age-specific fertility consistent with
608-
the WPP estimates."""
609-
610-
_years = np.arange(2010, 2101)
611-
_ages = np.arange(15, 50)
612-
613-
_init_over_time = np.exp(+0.05 * np.minimum(2020 - 2010, (_years - 2010))) * np.maximum(1.0, np.exp(
614-
+0.01 * (_years - 2020)))
615-
_init_over_time_modification_by_age = 1.0 / expand_to_age_years([1.0, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5], _ages)
616-
_init = np.outer(_init_over_time, _init_over_time_modification_by_age)
617-
618-
return pd.DataFrame(index=_years, columns=_ages, data=_init)
619-
620-
def avoid_sterilization_below30(probs):
621-
"""Prevent women below 30 years having female sterilization and adjust the probability for women 30 and over
622-
to preserve the overall probability of initiating sterilization."""
623-
# Input 'probs' must include probs for all methods including 'not_using'
624-
assert set(probs.index) == set(self.all_contraception_states)
625-
626-
# Prevent women below 30 years having 'female_sterilization'
627-
probs_below30 = probs.copy()
628-
probs_below30['female_sterilization'] = 0.0
629-
# Scale so that the probability of all outcomes sum to 1.0
630-
probs_below30 = probs_below30 / probs_below30.sum()
631-
assert np.isclose(1.0, probs_below30.sum())
632-
633-
# Increase prob of 'female_sterilization' in older women accordingly
634-
probs_30plus = probs.copy()
635-
probs_30plus['female_sterilization'] = (
636-
probs.loc['female_sterilization'] /
637-
self.ratio_n_females_30_49_to_15_49_in_2010
638-
)
639-
# Scale so that the probability of all outcomes sum to 1.0
640-
probs_30plus = probs_30plus / probs_30plus.sum()
641-
assert np.isclose(1.0, probs_30plus.sum())
642-
643-
return probs_below30, probs_30plus
644-
645-
def contraception_initiation_with_interv():
646-
"""Generate the probability per month of a woman initiating onto each contraceptive, by the age (in whole
647-
years) if FP interventions are applied."""
648-
649-
# Probability of initiation by method per month (average over all ages)
650-
p_init_by_method = self.parameters['Initiation_ByMethod'].loc[0]
651-
652-
# Prevent women below 30 years having 'female_sterilization' while preserving the overall probability of
653-
# 'female_sterilization' initiation
654-
p_init_by_method_below30, p_init_by_method_30plus = avoid_sterilization_below30(p_init_by_method)
655-
656-
# Effect of age
657-
age_effect = 1.0 + self.parameters['Initiation_ByAge'].set_index('age')['r_init1_age'].rename_axis(
658-
"age_years")
659-
660-
# Year effect
661-
year_effect = time_age_trend_in_initiation()
662-
663-
def apply_intervention_age_year_effects(probs_below30, probs_30plus):
664-
# Apply Pop intervention
665-
probs_by_method_below30 = \
666-
probs_below30.copy().drop('not_using').mul(self.parameters['Interventions_Pop'].loc[0])
667-
probs_by_method_30plus = \
668-
probs_30plus.copy().drop('not_using').mul(self.parameters['Interventions_Pop'].loc[0])
669-
# Assemble into age-specific data-frame:
670-
p_init = dict()
671-
for year in year_effect.index:
672-
673-
p_init_this_year = dict()
674-
for a in age_effect.index:
675-
if a < 30:
676-
p_init_this_year[a] = probs_by_method_below30 * age_effect.at[a] * year_effect.at[year, a]
677-
else:
678-
p_init_this_year[a] = probs_by_method_30plus * age_effect.at[a] * year_effect.at[year, a]
679-
p_init_this_year_df = pd.DataFrame.from_dict(p_init_this_year, orient='index')
680-
681-
# Check correct format of age/method data-frame
682-
assert set(p_init_this_year_df.columns) == set(self.all_contraception_states - {'not_using'})
683-
assert (p_init_this_year_df.index == range(15, 50)).all()
684-
assert (p_init_this_year_df >= 0.0).all().all()
685-
686-
p_init[year] = p_init_this_year_df
687-
688-
return p_init
689-
690-
return apply_intervention_age_year_effects(p_init_by_method_below30, p_init_by_method_30plus)
691-
692-
def contraception_initiation_after_birth_with_interv():
693-
"""Get the probability of a woman starting a contraceptive following giving birth if FP interventions are
694-
applied. Avoid sterilization in women below 30 years old."""
695-
696-
# Get initiation probabilities of contraception methods after birth from read-in Excel sheet
697-
p_start_after_birth = self.parameters['Initiation_AfterBirth'].loc[0].drop('not_using')
698-
699-
# Apply PPFP intervention multipliers
700-
p_start_after_birth = p_start_after_birth.mul(self.parameters['Interventions_PPFP'].loc[0])
701-
702-
# Add 'not_using' to initiation probabilities of contraception methods after birth
703-
p_start_after_birth = pd.concat(
704-
(
705-
pd.Series((1.0 - p_start_after_birth.sum()), index=['not_using']),
706-
p_start_after_birth
707-
)
708-
)
709-
710-
return avoid_sterilization_below30(p_start_after_birth)
711-
712-
processed_params['p_start_per_month'] = contraception_initiation_with_interv()
713-
processed_params['p_start_after_birth_below30'], processed_params['p_start_after_birth_30plus'] =\
714-
contraception_initiation_after_birth_with_interv()
599+
def contraception_initiation_with_interv(p_start_per_month_without_interv):
600+
"""Increase the probabilities of a woman starting modern contraceptives due to Pop intervention being
601+
applied."""
602+
p_start_per_month_with_interv = {}
603+
for year, age_method_df in p_start_per_month_without_interv.items():
604+
if year >= self.sim.date.year:
605+
p_start_per_month_with_interv[year] = age_method_df * self.parameters['Interventions_Pop'].loc[0]
606+
return p_start_per_month_with_interv
607+
608+
def contraception_initiation_after_birth_with_interv(p_start_after_birth_without_interv):
609+
"""Increase the probabilities of a woman starting modern contraceptives following giving birth due to PPFP
610+
intervention being applied."""
611+
# Exclude prob of 'not_using'
612+
p_start_after_birth_with_interv = p_start_after_birth_without_interv.copy().drop('not_using')
613+
614+
# Apply PPFP intervention multipliers (ie increase probs of modern methods)
615+
p_start_after_birth_with_interv = \
616+
p_start_after_birth_with_interv.mul(self.parameters['Interventions_PPFP'].loc[0])
617+
618+
# Return reduced prob of 'not_using'
619+
p_start_after_birth_with_interv = pd.Series((1.0 - p_start_after_birth_with_interv.sum()),
620+
index=['not_using']).append(p_start_after_birth_with_interv)
621+
622+
return p_start_after_birth_with_interv
623+
624+
processed_params['p_start_per_month'] = \
625+
contraception_initiation_with_interv(processed_params['p_start_per_month'])
626+
processed_params['p_start_after_birth_below30'] = \
627+
contraception_initiation_after_birth_with_interv(processed_params['p_start_after_birth_below30'])
628+
processed_params['p_start_after_birth_30plus'] = \
629+
contraception_initiation_after_birth_with_interv(processed_params['p_start_after_birth_30plus'])
715630

716631
return processed_params
717632

0 commit comments

Comments
 (0)