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

Cervical cancer #1287

Open
wants to merge 78 commits into
base: master
Choose a base branch
from
Open

Cervical cancer #1287

wants to merge 78 commits into from

Conversation

andrew-phillips-1
Copy link
Collaborator

Here's a first draft of this module. I need to do another search to try to find additional data for calibration.

I couldn't upload the draft write as it was asking me to use the command line and Git LFS.

Copy link
Collaborator Author

@andrew-phillips-1 andrew-phillips-1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CervicalCancer.docx
Here is the draft write-up

src/tlo/methods/cervical_cancer.py Outdated Show resolved Hide resolved
src/tlo/methods/cervical_cancer.py Outdated Show resolved Hide resolved
Comment on lines +331 to +332
# this was not assigned here at outset because baseline value of hv_inf was not accessible - it is assigned
# st start of main polling event below
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think maybe Tara has changed this in the HIV module as I remember discussing with her.


# ----- SCHEDULE LOGGING EVENTS -----
# Schedule logging event to happen immediately
sim.schedule_event(CervicalCancerLoggingEvent(self), sim.date + DateOffset(months=0))
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know. I think I copied this from another module. Shall I change both to immediate ?

sim.schedule_event(CervicalCancerLoggingEvent(self), sim.date + DateOffset(months=0))

# ----- SCHEDULE MAIN POLLING EVENTS -----
# Schedule main polling event to happen immediately
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or shall I schedule it immediately ?

@@ -20,7 +20,7 @@
from tlo.util import random_date

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
logger.setLevel(logging.CRITICAL)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes thanks

@@ -16,7 +16,7 @@
from tlo.progressbar import ProgressBar

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
logger.setLevel(logging.CRITICAL)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks

@@ -82,7 +82,7 @@ def __init__(self, *, start_date: Date, seed: int = None, log_config: dict = Non
self.rng = np.random.RandomState(np.random.MT19937(self._seed_seq))

def configure_logging(self, filename: str = None, directory: Union[Path, str] = "./outputs",
custom_levels: Dict[str, int] = None, suppress_stdout: bool = False):
custom_levels: Dict[str, int] = None, suppress_stdout: bool = True):
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK

Comment on lines 231 to 232
# print(stats_dict)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes I will keep in while I am still thinking of using it.

Comment on lines +177 to +180
# todo: not sure what is wrong with this assert as I am fairly certain the intended assert is true

# assert set(sim.modules['SymptomManager'].who_has('vaginal_bleeding')).issubset(
# df.index[df.ce_cc_ever])
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So how do I change that ?

@tbhallett tbhallett marked this pull request as ready for review June 19, 2024 19:54
Copy link
Collaborator

@thewati thewati left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps logging must start when polling starts or a month after polling judging by this parameter r_vaginal_bleeding_cc_stage1.

# Conflicts:
#	resources/ResourceFile_Bladder_Cancer.xlsx
#	src/tlo/methods/enhanced_lifestyle.py
#	src/tlo/methods/hsi_generic_first_appts.py
#	src/tlo/simulation.py
@tbhallett
Copy link
Collaborator

I'm merging in master on this.
I've rolled back the updates the simulation.py and hsi_events.py and enhanced_lifestyle.py and ResourceFile_BladderCancer.xlsx

and refactored to include the logic intended when presenting at first appointment (non-emergency), namely. (@andrew-phillips-1 - this is because since you started work on this, we've refactored this so that the actions to do in that appointment and coded into the module itself.)

            # If the symptoms include vaginal bleeding:
            if 'vaginal_bleeding' in symptoms:
                schedule_hsi(
                    HSI_CervicalCancerPresentationVaginalBleeding(
                        person_id=person_id,
                        module=sim.modules['CervicalCancer']
                    ),
                    priority=0,
                    topen=sim.date,
                    tclose=None)

            if 'chosen_via_screening_for_cin_cervical_cancer' in symptoms:
                schedule_hsi(
                    HSI_CervicalCancer_AceticAcidScreening(
                        person_id=person_id,
                        module=sim.modules['CervicalCancer']
                    ),
                    priority=0,
                    topen=sim.date,
                    tclose=None)


            if 'chosen_xpert_screening_for_hpv_cervical_cancer' in symptoms:
                schedule_hsi(
                    HSI_CervicalCancer_XpertHPVScreening(
                        person_id=person_id,
                        module=sim.modules['CervicalCancer']
                    ),
                    priority=0,
                    topen=sim.date,
                    tclose=None)

src/tlo/methods/cervical_cancer.py Show resolved Hide resolved
odds_ratio_health_seeking_in_adults=1.00)
)

# in order to implement screening for cervical cancer creating a dummy symptom - likely there is a better way
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment is right here -- this is having the desired effect, but we would rather not use the 'symptoms' in this way.

Instead, for the individuals that are chosen to be screened, create and schedule the HSI event directly.

e.g. for each indivisual to be screened... make an HSI_Event_CervicalCancer_Screening..... and in that event, do whatever is required for the sceening. (might be the same as happens in the generic appointment, in which case point them both to the same function)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks - I will see if I can make that work.


# ----- HSI FOR PALLIATIVE CARE -----
on_palliative_care_at_initiation = df.index[df.is_alive & ~pd.isnull(df.ce_date_palliative_care)]
for person_id in on_palliative_care_at_initiation:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for scheduling the same class of HSI_Event to multiple people, more efficient to use schedule_batch_of_individual_hsi_events

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK I will look into how to do that

p = self.sim.modules['CervicalCancer'].parameters

# ------------------- SET INITIAL CE_HPV_CC_STATUS -------------------------------------------------------------------
# this was done here and not at outset because baseline value of hv_inf was not accessible
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is interesting. As HIV is declared a dependency, it should be registered first, and so these properties be available by the time of running initialise_simulation and initialise_population. Could Wati/Emmanuel have a look at this, please?

If a one-off job is essential (though not sure it should be )... I think neater to put this into its own non-recurring event, and schedule it to occur on first day of simulation only. (That way it doesnt clutter up the main polling event)


# -------------------- ACQUISITION AND PROGRESSION OF CANCER (ce_hpv_cc_status) -----------------------------------

df.ce_new_stage_this_month = False
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is being broadcast. it should be lmited to those with is_alive: ie. df.loc[df.is_alive, 'cc_new_stage_this_month'] = False

As I expect this is going to be over-written (further down) it would be more efiicent to not write it into the main sim.population.props df yet (reading/writing there is time-consuming), and instead do one write to it at the end of the event, when everything is settled.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok will try to amend


idx_gets_new_stage = gets_new_stage[gets_new_stage].index

# print(stage, lm, gets_new_stage, idx_gets_new_stage)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# print(stage, lm, gets_new_stage, idx_gets_new_stage)

df.loc[idx_gets_new_stage, 'ce_hpv_cc_status'] = stage
df.loc[idx_gets_new_stage, 'ce_new_stage_this_month'] = True

df['ce_cc_ever'] = ((df.ce_hpv_cc_status == 'stage1') | (df.ce_hpv_cc_status == 'stage2a')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is also broadcasting to all dataframe (including dead peple and never alive people, potentially).

Also, it will over-write to False those people not in any of those categories. I can see that this will not violate the logic, but the safest thing would be to also include in the chanied union statement the current value, in order to absolute prevent reversions... i.e. add in ce_cc_ever on the end of this line.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK thanks

disease_module=self.module
)

self.sim.modules['SymptomManager'].change_symptom(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see comment above about symptoms

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

.when(2, p['rr_hpv_vaccinated']),
Predictor('age_years', conditions_are_mutually_exclusive=True)
.when('.between(0,15)', 0.0)
.when('.between(50,110)', p['rr_hpv_age50plus']),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.when('.between(50,110)', p['rr_hpv_age50plus']),
.when('>50'),p['rr_hpv_age50plus'])```

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done thanks

LinearModelType.MULTIPLICATIVE,
p['r_cin1_hpv'],
Predictor('ce_hpv_cc_status').when('hpv', 1.0).otherwise(0.0),
Predictor('hv_inf', conditions_are_mutually_exclusive=True)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lots of these are also 'conditions_are_exhaustive=True' too

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed


# Assign daly_wt to those with cancer stages before stage4 and have either never been treated or are no longer
# in the stage in which they were treated
disability_series_for_alive_persons.loc[
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit surprised this works, because the masks being used are wrt to df, but the indexing into a series with a difference index. Maybe it only works as long as everyone is alive!?

src/tlo/methods/cervical_cancer.py Show resolved Hide resolved
disease_module=self.module
)

# vaccinating 9 year old girls - this only uncommented for testing - vaccination is controlled by epi
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uncomment?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we don't need that code as vaccination is controlled elsewhere


for person_id in selected_to_die:
self.sim.schedule_event(
InstantaneousDeath(self.module, person_id, "CervicalCancer"), self.sim.date
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ideally, we'd distribute in time these deaths so that they don;t clump on first of the month (but this is a not a big deal).

The utilility function random_date (in utils) can be used

def __init__(self, module, person_id):
super().__init__(module, person_id=person_id)

self.TREATMENT_ID = "CervicalCancer_XpertHPVScreening"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think some equipment declaration needed for this (and others?) HSI_Events?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes I will be talking to Joe about that

assert not pd.isnull(df.at[person_id, "ce_date_diagnosis"])
assert not pd.isnull(df.at[person_id, "ce_date_treatment"])

days_threshold_365 = 365
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could use pd.Dateoffset(years =...) instead of the number of days for ease for reading/comprehension

out.update({"n_diagnosed_1_year_ago": n_diagnosed_1_year_ago})
out.update({"n_diagnosed_1_year_ago_died": n_diagnosed_1_year_ago_died})

print(self.sim.date, 'total_none:', out['total_none'], 'total_hpv:', out['total_hpv'], 'total_cin1:',out['total_cin1'],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move to using the logger:
i.e. logger.info(key='cervical_cancer_stats_every_month', description='XX', data=out)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
PR priorities
Ready for EM review
Development

Successfully merging this pull request may close these issues.

None yet

4 participants