Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update parameters to enable interventions: a simpler and more reader-friendly way #1081

Conversation

EvaJanouskova
Copy link
Collaborator

@EvaJanouskova EvaJanouskova commented Aug 23, 2023

update_params_for_interventions() fnc is re-written so that all the calculations are not done again and among those calculations multiplied by the interventions multipliers, instead the final probs without interventions are multiplied by those multipliers.

The initiation probs 'p_stop_per_month' with intervention calculated with the old version versus new version of contraception_initiation_with_interv are the same if up to 16 decimals are considered.
(I have also removed the calculations for the years before the interventions started, as they are not needed at that time.)

The initiation probs after birth 'p_start_after_birth_below30' and 'p_start_after_birth_30plus' calculated with the old version versus new version of contraception_initiation_after_birth_with_interv are the same if up to 5 decimals are considered.

@tbhallett
Copy link
Collaborator

@EvaJanouskova -- we have a conflict on this one, I see. Please could you resolve?

@EvaJanouskova EvaJanouskova force-pushed the EvaJ/contraception_improvement/better_implementation_of_intervs branch from ce5795d to db4fa21 Compare November 8, 2023 14:47
@EvaJanouskova
Copy link
Collaborator Author

EvaJanouskova commented Nov 8, 2023

@tbhallett , I rebased the branch on master, it should help.

@EvaJanouskova EvaJanouskova force-pushed the EvaJ/contraception_improvement/better_implementation_of_intervs branch from db4fa21 to 0f5d63b Compare November 8, 2023 14:55
@tbhallett
Copy link
Collaborator

@EvaJanouskova -- the changes per se here look great. But there is a conflict to resolve before we can we merge. Please could you sort out the conflict? Thanks

@EvaJanouskova EvaJanouskova force-pushed the EvaJ/contraception_improvement/better_implementation_of_intervs branch from 0f5d63b to 8b735a2 Compare February 8, 2024 18:08
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
@EvaJanouskova EvaJanouskova force-pushed the EvaJ/contraception_improvement/better_implementation_of_intervs branch from 8b735a2 to 84d9b16 Compare February 9, 2024 18:52
@tbhallett tbhallett merged commit 80a1c40 into master Feb 11, 2024
56 checks passed
@tbhallett tbhallett deleted the EvaJ/contraception_improvement/better_implementation_of_intervs branch February 11, 2024 10:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants