Skip to contents

Note - the replica library is currently very experimental. It is still undergoing active development and testing. Function, class and method names, arguments and syntax may change without the use of deprecation conventions. This library should currently be used only for exploratory purposes.

Motivation

Synthetic population generation often begins with aggregate population statistics rather than individual-level records.

Examples include:

  • population counts by neighbourhood;

  • age-group marginals;

  • demographic contingency tables;

  • census tabulations.

The first step in many population-synthesis workflows is to convert these aggregate counts into a synthetic population of individual agents.

The make_agents() function performs this task by expanding aggregate counts into one row per synthetic individual. This function aims to implement in R the initial step of an algorithm described by de Mooij et al. (2024).

This vignette demonstrates how to create synthetic agents from marginal distributions and contingency tables and shows how these agents can subsequently be enriched and grouped using the remainder of the replica workflow.

Introduction

The workflow supported by replica can be summarised as:

Aggregate Counts ↓ make_agents() ↓ Synthetic Agents ↓ ReplicaAdder ↓ ReplicaGrouper ↓ Validation

This vignette focuses on the first stage: creating a synthetic population of individual agents.

Example 1: Creating agents from a spatial marginal

Suppose we know the number of residents in each neighbourhood.

neighbourhoods <- data.frame(neighb_code = c( "N1", "N2", "N3" ), 
                            count = c( 100, 150, 75 ) ) 
neighbourhoods
##   neighb_code count
## 1          N1   100
## 2          N2   150
## 3          N3    75

The table contains aggregate counts rather than individual agents. We can convert this table to a table of individual agents:

agents <- make_agents(
  neighbourhoods
)

head(agents)
##     agent_id neighb_code
##       <char>      <char>
## 1: Agent_001          N1
## 2: Agent_002          N1
## 3: Agent_003          N1
## 4: Agent_004          N1
## 5: Agent_005          N1
## 6: Agent_006          N1

Each row now represents a synthetic individual.

We can confirm the number of generated agents:

nrow(agents)
## [1] 325

The generated population can also be aggregated back into a contingency table.

synthetic_population_to_contingency(

  agents,

  "neighb_code"

)
##   neighb_code count
## 1          N1   100
## 2          N2   150
## 3          N3    75

The reconstructed counts should match the source table used to generate the agents.

Example 2: Creating agents from an age marginal

The same function can be used with any marginal distribution.

age_margin <- data.frame(

  age_group = c(
    "0-17",
    "18-64",
    "65+"
  ),

  count = c(
    200,
    600,
    200
  )

)

age_margin
##   age_group count
## 1      0-17   200
## 2     18-64   600
## 3       65+   200
agents <- make_agents(
  age_margin
)

head(agents)
##      agent_id age_group
##        <char>    <char>
## 1: Agent_0001      0-17
## 2: Agent_0002      0-17
## 3: Agent_0003      0-17
## 4: Agent_0004      0-17
## 5: Agent_0005      0-17
## 6: Agent_0006      0-17

Each synthetic agent now contains an age-group assignment.

Summarise the resulting population:

##   age_group count
## 1      0-17   200
## 2     18-64   600
## 3       65+   200

Example 3: Creating agents from a contingency table

More detailed synthetic populations can be generated from contingency tables.

age_gender <- data.frame(

  age_group = c(
    "0-17",
    "0-17",
    "18-64",
    "18-64",
    "65+",
    "65+"
  ),

  gender = c(
    "Male",
    "Female",
    "Male",
    "Female",
    "Male",
    "Female"
  ),

  count = c(
    100,
    90,
    310,
    290,
    110,
    100
  )

)

age_gender
##   age_group gender count
## 1      0-17   Male   100
## 2      0-17 Female    90
## 3     18-64   Male   310
## 4     18-64 Female   290
## 5       65+   Male   110
## 6       65+ Female   100

Generate agents:

agents <- make_agents(
  age_gender
)

Inspect the resulting population:

head(agents)
##      agent_id age_group gender
##        <char>    <char> <char>
## 1: Agent_0001      0-17   Male
## 2: Agent_0002      0-17   Male
## 3: Agent_0003      0-17   Male
## 4: Agent_0004      0-17   Male
## 5: Agent_0005      0-17   Male
## 6: Agent_0006      0-17   Male

We can compare the generated population with the original contingency table.

synthetic_population_to_contingency(agents, 
                                    columns = c("age_group", "gender"))
##   age_group gender count
## 1      0-17   Male   100
## 2      0-17 Female    90
## 3     18-64   Male   310
## 4     18-64 Female   290
## 5       65+   Male   110
## 6       65+ Female   100

The reconstructed contingency table should reproduce the original counts exactly.

This property makes make_agents() a useful foundation for more sophisticated synthetic-population workflows.

Add agent attributes

The agents created by make_agents() typically contain only a subset of the characteristics required by a simulation model.

Additional attributes can be assigned using ReplicaAdder. The use of this class is demonstrated in the “Assigning Attributes Using Contingency Tables” vignette.

Assign agents to households

After additional attributes have been assigned, agents can be grouped into households.

To do this, use the ReplicaStructure and ReplicaGrouper classes. These are described in another vignette.

Key takeaways

In this vignette we:

  • introduced synthetic-agent generation;

  • created agents from spatial marginals;

  • created agents from demographic marginals;

  • created agents from contingency tables;

  • verified that aggregate distributions were preserved;

  • connected agent generation to the broader replica workflow.

References

de Mooij J, Sonnenschein T, Pellegrino M, Dastani M, Ettema D, Logan B and Verstegen JA (2024).

GenSynthPop: generating a spatially explicit synthetic population of individuals and households from aggregated data.

Autonomous Agents and Multi-Agent Systems. https://link.springer.com/article/10.1007/s10458-024-09680-7