A simple 4 layer contact network based on survey data

Assume we want to build a simple contact network model consisting of 4 location types: households, school classes, work places and cities. To increase realism, we use survey data to create the population of agents and to define some of the location properties.

[1]:
import pop2net as p2n
from pop2net.data_fakers.soep import soep_faker

In this example, we only use fake survey data (but of course you should real survey data here):

[2]:
df_soep = soep_faker.soep(size=1000)
df_soep.head()
[2]:
age gender work_hours_day nace2_division hid pid
0 65.0 male 7.224629 75 7270 4993
1 44.0 female 0.000000 -2 7270 3968
2 78.0 female 3.583111 84 2836 174
3 3.0 female 0.000000 -2 2836 9598
4 8.0 male 0.000000 -2 2836 1382

The first contact layer Home is a location class where agents of one household meet each other for 12 hours. We use the agent attribute hid(household id), which is provided by the survey data, to group the agents in their empirical households.

[3]:
class Home(p2n.LocationDesigner):
    def split(self, agent):
        """Group the agents by their household id."""
        return agent.hid

    def weight(self, agent):
        """Weight the connection between the agent and the Home by 12."""
        return 12

The second layer models work places. The agents are grouped by their NACE2 division which is provided in the survey data. The connection is weighted by the agents’ empirical work hours given by the survey data.

[4]:
class Work(p2n.LocationDesigner):
    n_agents = 10

    def filter(self, agent):
        """Ignore agents that have 0 work hours or an invalid NACE2 value."""
        return True if agent.work_hours_day > 0 and agent.nace2_division > 0 else False

    def split(self, agent):
        """Group agents by NACE2 division."""
        return agent.nace2_division

    def weight(self, agent):
        """Weight the connection between the agent and the Work instance
        by the agent's daily work hours."""
        return agent.work_hours_day

The third type of location are cities. We build 2 of them. Using stick_together(), we make sure that agents of the same household live in the same city.

[5]:
class City(p2n.LocationDesigner):
    n_locations = 2

    def stick_together(self, agent):
        """Keep agents of the same household together when assigning the agents to cities."""
        return agent.hid

The fourth contact layer models a school consisting of multiple classrooms including agents of the same age. Using nest() we ensure that children from the same city visit the same school.

[6]:
class School(p2n.LocationDesigner):
    n_agents = 15  # Set the number of agents to 15.

    def filter(self, agent):
        """Ignore agents younger than 6 or older than 18."""
        return True if 6 <= agent.age <= 18 else False

    def split(self, agent):
        """Group the agents by age."""
        return agent.age

    def weight(self, agent):
        """Weight the connection between the agent and the School by 6."""
        return 6

    def nest(self):
        """Nest this location type within the location type City."""
        return "City"

Create the necessary pop2net objects:

[7]:
model = p2n.Model()
creator = p2n.Creator(model=model)
inspector = p2n.NetworkInspector(model=model)

In the following we build the network. 100 rows are sampled from the df_soep and are translated into agents. The argument sample_level ensures that we always sample complete households. Using the argument location classes we can define which contact layers we want to use to build our network.

[8]:
creator.create(
    df=df_soep,
    n_agents=100,
    sample_level="hid",
    location_designers=[
        Home,
        City,
        Work,
        School,
    ],
)

inspector.plot_networks(location_color="label")
[9]:
inspector.eval_affiliations()


______________________________________
Number of locations
______________________________________

location_label
Home      31
Work      25
School    10
City       2
Name: count, dtype: int64


______________________________________
Number of agents per location
______________________________________

                     mean       std   min    25%   50%    75%   max
location_label
City            51.500000  4.949747  48.0  49.75  51.5  53.25  55.0
Home             3.322581  1.868816   1.0   2.00   3.0   4.00  10.0
School           1.300000  0.483046   1.0   1.00   1.0   1.75   2.0
Work             1.680000  1.676305   1.0   1.00   1.0   2.00   9.0


______________________________________
Number of affiliated locations per agent
______________________________________

mean    2.533981
std     0.520474
min     2.000000
25%     2.000000
50%     3.000000
75%     3.000000
max     4.000000
Name: n_affiliated_locations, dtype: float64

Maybe you have noticed that some of the school classes or work places are undercrowded because the overall population is too small. Let’s create a new network model and increase the population size to 5000 agents:

[10]:
model = p2n.Model()
creator = p2n.Creator(model=model)
inspector = p2n.NetworkInspector(model=model)

creator.create(
    df=df_soep,
    n_agents=5000,
    sample_level="hid",
    location_designers=[
        Home,
        City,
        Work,
        School,
    ],
)
[10]:
(AgentList (5002 objects), LocationList (1827 objects))

The table below shows that now the population is large enough to fill all locations as we wanted.

[11]:
inspector.eval_affiliations()


______________________________________
Number of locations
______________________________________

location_label
Home      1576
Work       220
School      29
City         2
Name: count, dtype: int64


______________________________________
Number of agents per location
______________________________________

                       mean       std     min     25%     50%     75%     max
location_label
City            2501.000000  0.000000  2501.0  2501.0  2501.0  2501.0  2501.0
Home               3.173858  1.477957     1.0     2.0     3.0     4.0    10.0
School            13.275862  5.364543     4.0    10.0    13.0    16.0    22.0
Work               9.363636  1.947846     2.0    10.0    10.0    10.0    14.0


______________________________________
Number of affiliated locations per agent
______________________________________

mean    2.488804
std     0.507073
min     2.000000
25%     2.000000
50%     2.000000
75%     3.000000
max     4.000000
Name: n_affiliated_locations, dtype: float64