Skip to content

Commit 5166fdb

Browse files
committed
co: more-readable update_params_for_interventions() fnc
diff --git src/tlo/methods/contraception.py src/tlo/methods/contraception.py index 94f2c7108..bc118d4e5 100644 --- src/tlo/methods/contraception.py +++ src/tlo/methods/contraception.py @@ -611,121 +611,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.n_female3049_in_2010 / self.n_female1549_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 @@ -750,7 +666,6 @@ class Contraception(Module): get_items_from_pkg = self.sim.modules['HealthSystem'].get_item_codes_from_package_name _cons_codes = dict() - # items for each method that requires an HSI to switch to _cons_codes['pill'] = get_items_from_pkg('Pill') _cons_codes['male_condom'] = get_items_from_pkg('Male condom') _cons_codes['other_modern'] = get_items_from_pkg('Female Condom') @@ -760,7 +675,6 @@ class Contraception(Module): _cons_codes['implant'] = get_items_from_pkg('Implant') _cons_codes['female_sterilization'] = get_items_from_pkg('Female sterilization') assert set(_cons_codes.keys()) == set(self.states_that_may_require_HSI_to_switch_to) - # items used when initiating a modern reliable method after not using or switching from non-reliable method _cons_codes['co_initiation'] = get_items_from_pkg('Contraception initiation') return _cons_codes
1 parent a91cf53 commit 5166fdb

File tree

1 file changed

+31
-117
lines changed

1 file changed

+31
-117
lines changed

src/tlo/methods/contraception.py

+31-117
Original file line numberDiff line numberDiff line change
@@ -611,121 +611,37 @@ def update_params_for_interventions(self):
611611

612612
processed_params = self.processed_params
613613

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

730646
return processed_params
731647

@@ -750,7 +666,6 @@ def get_item_code_for_each_contraceptive(self):
750666
get_items_from_pkg = self.sim.modules['HealthSystem'].get_item_codes_from_package_name
751667

752668
_cons_codes = dict()
753-
# items for each method that requires an HSI to switch to
754669
_cons_codes['pill'] = get_items_from_pkg('Pill')
755670
_cons_codes['male_condom'] = get_items_from_pkg('Male condom')
756671
_cons_codes['other_modern'] = get_items_from_pkg('Female Condom')
@@ -760,7 +675,6 @@ def get_item_code_for_each_contraceptive(self):
760675
_cons_codes['implant'] = get_items_from_pkg('Implant')
761676
_cons_codes['female_sterilization'] = get_items_from_pkg('Female sterilization')
762677
assert set(_cons_codes.keys()) == set(self.states_that_may_require_HSI_to_switch_to)
763-
# items used when initiating a modern reliable method after not using or switching from non-reliable method
764678
_cons_codes['co_initiation'] = get_items_from_pkg('Contraception initiation')
765679

766680
return _cons_codes

0 commit comments

Comments
 (0)