Conditional Pathways
In the last example we saw how each demographic can be given a different disease pathway. In this section, we will see how to use move functions to move individuals between pathways. We did this before when we modelled quarantine. Now, we will see how, by using different disease stages, we can create a range of different pathways through which different individuals can conditionally progress.
Modelling a hospital
In this example we will create a model of a hospital. The demographics involved will be;
patients: Patients at the hospital who are infected with the lurgystaff: Hospital staff who care for the patientshome: The general population who are neither patients or staff
Members of the staff and home demographics will move along the
stages of the lurgy according to lurgy4.json. Hospital patients
will move through two hospital states, which we will refer to as
H1 and H2. These are defined in the file lurgy_hospital.json,
which you should create and copy in the below;
{ "name" : "The Lurgy (hospital patients)",
"version" : "June 10th 2020",
"author(s)" : "Christopher Woods",
"contact(s)" : "christopher.woods@bristol.ac.uk",
"reference(s)" : "Completely ficticious disease - no references",
"beta" : [0.0, 0.0, 0.2, 0.2, 0.0],
"progress" : [1.0, 1.0, 0.2, 0.2, 0.0],
"too_ill_to_move" : [0.0, 0.0, 1.0, 1.0, 1.0],
"contrib_foi" : [0.0, 0.0, 1.0, 1.0, 0.0]
}
Note
Every disease must include the * and E stages, which are
stages 0 and 1, and the R stage which is the last stage. This
means that H1 and H2 are stages 2 and 3 in this file, both
of which have beta == 0.2, progress == 0.2 and
too_ill_to_move == 1.0. While the H1 and H2 stages
are identical now, we will look to change them later in this
tutorial.
The aim of the model will be that 20% of those suffering in the I2
stage of lurgy4.json will be moved to the H1 stage of
lurgy_hospital.json, and will then progress to H2 and then
R in the hospital.
We will next set that 1% of the worker population are hospital staff, while,
initially, nobody has the lurgy, and so there are no hospital patients.
We can set this using the file demographics.json, which you should
create and copy in the below;
{
"demographics" : ["home", "staff", "patients"],
"work_ratios" : [ 0.99, 0.01, 0.00 ],
"play_ratios" : [ 1.00, 0.00, 0.00 ],
"diseases" : [ null, null, "lurgy_hospital" ]
}
Next, we need a mixing function that will model the interactions between hospital staff, patients and the general population. We will use the following interaction matrix;
home |
staff |
patients |
|
|---|---|---|---|
home |
1.0 |
1.0 |
0.0 |
staff |
0.0 |
0.1 |
0.1 |
patients |
0.0 |
0.1 |
0.0 |
This matrix sets that the home demographic is taking no precautions,
and are fully exposed to each other, and also fully exposed to members
of the staff demographic.
The staff demographic are taking lots of precautions, and are not
exposed to the home demographic, and only lightly exposed to other
staff or members of the patient demographic (matrix values
are 0.1).
Finally, the patients group are isolated from one another, and so
do not infect one another. At the moment this is moot, as all patients
in this model are already infected. However, you could add a susceptible
patient population who do not have the lurgy, and then use this final
element in the matrix to control the force of infection between patients.
Note
This is not an entirely realistic interaction matrix, but is set up
to demonstrate how the only route of infection of staff is from
patients.
To use this interaction matrix, create a mixer in mix_hospital.py
and copy in the below.
from metawards.mixers import merge_using_matrix
def mix_shield(network, **kwargs):
matrix = [ [1.0, 1.0, 0.0],
[0.0, 0.1, 0.1],
[0.0, 0.1, 0.0] ]
network.demographics.interaction_matrix = matrix
return [merge_using_matrix]
Note
This is essentially an identical mixing function as that used in the shielding example.
Note
Note that we are using merge_using_matrix().
This may not be the right choice depending on how we want the
population dynamics to mix, e.g.
merge_matrix_single_population() or
merge_matrix_multi_population() may
be a better choice. See here for more information.
Next, we want to make sure that we have output the populations in the
H1 and H2 states. We can do this by creating a custom extractor
that writes the populations of all of the disease stages for just
the patients demographic to a file called patients.csv. To do this,
create the file extract_patients.py and copy in the below;
from metawards.extractors import extract_default
def output_patients(network, population, workspace, output_dir, **kwargs):
# Open the file "patients.csv" in the output directory,
# using the supplied headers for the columns
FILE = output_dir.open("patients.csv",
headers=["day", "*", "E", "H1", "H2", "R"],
sep=",")
# Now get the workspace for the "patients" demographic
index = network.demographics.get_index("patients")
subspace = workspace.subspaces[index]
# The total population at each infection stage is the sum
# of the work and play infections
inf_tot = [inf + pinf for inf, pinf in
zip(subspace.inf_tot, subspace.pinf_tot)]
FILE.write(str(population.day) + ",")
FILE.write(",".join([str(x) for x in inf_tot]) + "\n")
def extract_patients(**kwargs):
# return all of the functions from "extract_default"
# plus our new "output_patients"
funcs = extract_default(**kwargs)
funcs.append(output_patients)
return funcs
Conditionally moving to hospital
The new part of this example is that we need to add a move function that
will move 20% of individuals who are in the I2 stage to
hospital, in the H1 stage. We can do this by writing a move function
into the file move_hospital.py, which you should create and
copy in the below;
from metawards.movers import go_stage
def move_hospital(**kwargs):
func = lambda **kwargs: go_stage(go_from=["home", "staff"],
go_to="patients",
from_stage=4,
to_stage=2,
fraction=0.2,
**kwargs)
return [func]
This move function returns go_stage(). This is
very similar to go_to(), except you also specify
the from_stage and to_stage, which are the stage(s) to move from,
and the stage to move to. In this case, we will move 20% of individuals
from the I2
stage from the home and staff demographics, which is stage 4
of lurgy4.json. We will move these individuals to stage 2, which is
H2, in the patients demographic.
Now this is set, we can run the model using;
metawards -D demographics.json -d lurgy4 --mixer mix_hospital --mover move_hospital --extract extract_hospital -a ExtraSeedsLondon.dat
You should see that the epidemic starts in the home demographic, with
no infections in staff until after a number of the more ill home
individuals are moved to hospital as patients. In this model, the
~70,000 staff are overwhelmed with millions of patients. This means that,
despite their care, and the lack of interaction with home,
all of the members of the staff demographic become infected within
the first ~130 days of the epidemic.
You can plot this using the command;
metawards-plot -i output/trajectory.csv.bz2
The resulting plot should look something like this;
You can see that the infections for the patients lag behind that of the
general population, as individuals move to hospital at a late stage in the
disease, and then remain in hospital for a long time. You can also see
in the IW plot how the staff are quickly overwhelmed and are all
infected within ~4 months.
In addition to this plot, you will also see in the output directory
the patients.csv.bz2 file that was written by extract_patients.
We can load this into R, pandas or Excel to find the maximum occupancy
in the hospital for the two different stages.
>>> import pandas as pd
>>> df = pd.read_csv("output/patients.csv.bz2")
>>> df["H1"].max()
1799688
>>> df["H2"].max()
2166228
>>> df[ df["H1"] == df["H1"].max() ]
day * E H1 H2 R
126 126 0 0 1799688 2090894 6647271
>>> df[ df["H2"] == df["H2"].max() ]
day * E H1 H2 R
130 130 0 0 1742752 2166228 8348305
This shows that days 126-130 were particularly difficult, with nearly 4 million patients in hospital, while nearly all staff were infected.