Multiple pathways with Python

MetaWards uses metawards.Demographics to model different groups of individuals in different ways. Individuals can move between different demographics, and different demographics can experience different disease models and move within different networks. This is very powerful, and enables MetaWards to model multiple pathways for different individuals.

This is explored in more depth in the tutorial. For this quick start guide, we will create three demographics;

  • students : experience a mild version of the lurgy and travel to school each day

  • teachers : experience the normal version of the lurgy, and also travel to school each day

  • default : experience the normal version of the lurgy and either travel to work each day or stay home and play

Creating a mild disease

First, we need to save the current version of the lurgy to a file called lurgy.json.bz2.

>>> lurgy.to_json("lurgy.json.bz2")

Next, we must create a milder version of the lurgy and save this to mild_lurgy.json.bz2 using;

>>> mild_lurgy = mw.Disease("mild_lurgy")
>>> mild_lurgy.add("E", progress=0.25, beta=0.0)
>>> mild_lurgy.add("I", progress=0.5, beta=0.2)
>>> mild_lurgy.add("R")
>>> mild_lurgy.to_json("mild_lurgy.json.bz2")

Creating the networks

We now need to create the three networks for the three demographics. We will start with the students, who will move between home and school. This will be saved to students.json.bz2.

>>> home = mw.Ward("home")
>>> school = mw.Ward("school")
>>> home.add_workers(3000, destination=school)
>>> students = mw.Wards()
>>> students.add(home)
>>> students.add(school)
>>> students.to_json("students.json.bz2")

We will next do the same for the teachers, who will also move between home and school (saving to teachers.json.bz2).

>>> home = mw.Ward("home")
>>> school = mw.Ward("school")
>>> home.add_workers(200, destination=school)
>>> teachers = mw.Wards()
>>> teachers.add(home)
>>> teachers.add(school)
>>> teachers.to_json("teachers.json.bz2")

Next, we will create the default network. This will consist of some players who stay at home, and workers who go to work.

>>> home = mw.Ward("home")
>>> work = mw.Ward("work")
>>> home.set_num_players(10000)
>>> home.add_workers(7000, destination=work)
>>> default = mw.Wards()
>>> default.add(home)
>>> default.add(work)
>>> default.to_json("default.json.bz2")

Creating the demographics

Next, we create the demographics. We do this by creating Demographic objects for each demographic that specify the network and disease to use for each group. These are then combined into a single Demographics object.

>>> students = mw.Demographic("students",
                              disease="mild_lurgy.json.bz2",
                              network="students.json.bz2")
>>> teachers = mw.Demographic("teachers",
                              disease="lurgy.json.bz2",
                              network="teachers.json.bz2")
>>> default = mw.Demographic("default",
                             disease="lurgy.json.bz2",
                             network="default.json.bz2")
>>> demographics = mw.Demographics()
>>> demographics.add(default)
>>> demographics.add(teachers)
>>> demographics.add(students)
>>> print(demographics)

[
  Demographic(name='default', work_ratio=0.0, play_ratio=0.0, disease=lurgy.json.bz2, network='default.json.bz2')
  Demographic(name='teachers', work_ratio=0.0, play_ratio=0.0, disease=lurgy.json.bz2, network='teachers.json.bz2')
  Demographic(name='students', work_ratio=0.0, play_ratio=0.0, disease=mild_lurgy.json.bz2, network='students.json.bz2')
]

Running the model

We can run the model by passing in the demographics. Note that we don’t need to specify the model as this is now fully specified in the demographics.

>>> results = mw.run(disease=lurgy, demographics=demographics,
                     additional="1, 5, home, default", silent=True)

Note

We have added default to the additional seeding to specify that the initial infections will be in this demographic. This is needed as a current limitation of MetaWards is that you can only seed infections in players, and only the default demographic in this example has players.

You can then process and graph the results as before;

>>> df = pd.read_csv(results)
>>> df.plot.line(x="day", y=["S","E","I","IR","R"])

When you do this, you will notice that the number of susceptibles falls until it reaches a number above 3200. This is because we seeded the outbreak in the default demographic. By default, demographics do not mix with each other, and so the outbreak does not spread to the teachers or students.

We can control the amount of mixing of demographics using the mixer argument. This specifies a mixing function to use. We will use mix_evenly(), which sets that all demographics will mix evenly with each other.

>>> results = mw.run(disease=lurgy, demographics=demographics,
                     additional="1, 5, home, default",
                     mixer="mix_evenly", silent=True)
>>> df = pd.read_csv(results)
>>> df.plot.line(x="day", y=["S","E","I","IR","R"])

Now you should see that the outbreak spreads through the entire population.

Note

The trajectory.csv.bz2 file in the output directory of the run contains the trajectory for each of the demographics in each disease state. You can load this to generate demographic graphs.

Complete code

The complete Python code for this part of the getting started guide is re-copied below (this continues from the code in the last part);

# save the lurgy to disk
lurgy.to_json("lurgy.json.bz2")

# create a milder lurgy and save to disk
mild_lurgy = mw.Disease("mild_lurgy")
mild_lurgy.add("E", progress=0.25, beta=0.0)
mild_lurgy.add("I", progress=0.5, beta=0.2)
mild_lurgy.add("R")
mild_lurgy.to_json("mild_lurgy.json.bz2")

# create the students network
home = mw.Ward("home")
school = mw.Ward("school")
home.add_workers(3000, destination=school)
students = mw.Wards()
students.add(home)
students.add(school)
students.to_json("students.json.bz2")

# create the teachers network
home = mw.Ward("home")
school = mw.Ward("school")
home.add_workers(200, destination=school)
teachers = mw.Wards()
teachers.add(home)
teachers.add(school)
teachers.to_json("teachers.json.bz2")

# create the default network
home = mw.Ward("home")
work = mw.Ward("work")
home.set_num_players(10000)
home.add_workers(7000, destination=work)
default = mw.Wards()
default.add(home)
default.add(work)
default.to_json("default.json.bz2")

# now create the demographics
students = mw.Demographic("students",
                          disease="mild_lurgy.json.bz2",
                          network="students.json.bz2")
teachers = mw.Demographic("teachers",
                          disease="lurgy.json.bz2",
                          network="teachers.json.bz2")
default = mw.Demographic("default",
                         disease="lurgy.json.bz2",
                         network="default.json.bz2")
demographics = mw.Demographics()
demographics.add(default)
demographics.add(teachers)
demographics.add(students)

# run the model
results = mw.run(disease=lurgy, demographics=demographics,
                 additional="1, 5, home, default",
                 mixer="mix_evenly", silent=True)

# graph the results
df = pd.read_csv(results)
df.plot.line(x="day", y=["S","E","I","IR","R"])

What’s next?

This was a quick start guide to show some of the capabilities of MetaWards. To learn more, e.g. how to create custom iterators to model lockdowns, how to write extractors to get more detailed information output, how to write mixers for modelling shielding etc., or how to write movers to model conditional branching, please do now follow the tutorial.