Extendable projection of social contact matrices

NUMBATs, Monash University

Nicholas Tierney, Michael Lydeamore, Nick Golding, Chitra Saraswati, Aarathy Babu

The Kids Research Institute

2024-10-24

What this talk is about

  • How I wrote software for infectious disease modelling

  • Touch on the overall approach for methods. A large portion of my role was on software and interface design.

  • Why we needed our own method + software

  • Discuss applications of contact matrices

  • Advocate for software design + statistical thinking: the way of the research software engineer.

The story so far

  • 2008-2012: Undergrad + honours in Psychology @ UQ

  • 2013-2017: PhD Statistics @ QUT

    • Exploratory Data Analysis (EDA)
    • Bayesian / Geospatial statistics / Optimal placement
  • 2018 - 2020: Research Fellow / Lecturer @ Monash

    • Design and improve tools for EDA with Di Cook
  • 2021 - 2024: Research Software Engineer @ Telethon Kids Institute The Kids Research Institute

    • Maintain and design tools for data analysis

An interlude

Define: Research Software Engineer

A Research Software Engineer (RSE) combines professional software engineering expertise with an intimate understanding of research.

– (from https://society-rse.org/about/)

What sorts of things does an RSE do?

  • Create software to solve research problems

  • Develop tools that abstract the right components to facilitate research

  • Help researchers to find and learn good tools

  • Support researchers with (computational) reproducibility

  • (Pretty much what you do here at NUMBATs!)

visdat::vis_dat(airquality)

naniar::gg_miss_upset(airquality)

brolgar - take spaghetti

brolgar - spread spaghetti

greta is R code

stan

data {
  real alpha;
  real beta;
  real<lower=0> sigma2;
  int<lower=0> J;
  array[J] int y;
  vector[J] Z;
  array[J] int n;
}
transformed data {
  real<lower=0> sigma;
  sigma = sqrt(sigma2);
}
parameters {
  real theta1;
  real theta2;
  vector[J] X;
}
model {
  array[J] real p;
  theta1 ~ normal(0, 32); // 32^2 = 1024 
  theta2 ~ normal(0, 32);
  X ~ normal(alpha + beta * Z, sigma);
  y ~ binomial_logit(n, theta1 + theta2 * X);
}

JAGS

for(j in 1 : J) {
   y[j] ~ dbin(p[j], n[j])
   logit(p[j]) <- theta[1] + theta[2] * X[j]
   X[j] ~ dnorm(mu[j], tau)
   mu[j] <- alpha + beta * Z[j]
}
theta[1] ~ dnorm(0.0, 0.001)
theta[2] ~ dnorm(0.0, 0.001)

greta

theta <- normal(0, 32, dim = 2)
mu <- alpha + beta * Z
X <- normal(mu, sigma)
p <- ilogit(theta[1] + theta[2] * X)
distribution(y) <- binomial(n, p)

google tensorflow

  • automatic differentiation
  • efficient linear algebra
  • highly parallel

extendable

greta.gp

Extends greta to let you define Gaussian processes as part of your model. It provides a syntax to create and combine GP kernels, and use them to define either full rank or sparse Gaussian processes.

# kernel & GP
kernel <- rbf(rbf_len, rbf_var) + 
            bias(1)
f <- gp(x, kernel)
# likelihood
distribution(y) <- normal(f, obs_sd)
# prediction
f_plot <- project(f, x_plot)

Joining an infectious disease team

  • August, 2021: Australia is working on the plan to reopen, part of “The Doherty Report”

  • They need more hands to help with data analysis and software development

  • I joined to assist for ~4 months

  • Helped develop pipelines + software for disease modelling, establish code base for people to contribute

Soliloquy

Social spread of disease

  • Diseases like COVID19 and Influenza spread through face to face social contact
  • Describe which 3 people had contact:

    • James has had contact with Luke

    • Nick has had contact with neither

Example: visually

Example: matrix

      James  Luke  Nick
James    NA  TRUE FALSE
Luke   TRUE    NA FALSE
Nick  FALSE FALSE    NA

Logical –> Numeric

People –> Age groups

What do you do with a contact matrix?

  • If we know the number of times people have contact, we can model:
    • Which age groups might spread COVID
    • How many COVID cases would get transmitted
    • How vaccination reduces transmission
  • Do the above for different areas in Australia

Do we know how much contact people have?

  • We don’t. Well, not in Australia. Yet.

  • You need to conduct a survey where people diary the amount and manner of daily contacts they have.

  • Mossong et al have this for 8 countries in Europe

  • Referred to as the POLYMOD study

POLYMOD

Published in 2008, Mossong et. al undertook a contact diary study of 7290 participants across Europe.

It remains the most widely cited contact diary study.

Method of Mossong et al

  • Random weekday: record every person in contact with.
  • Physical contact:
    • skin-to-skin contact (kiss or handshake)
  • Non-physical contact:
    • two-way conversation with 3+ words in physical presence of a person

Method of Mossong et al

  • Participants provide info on:
    • age and sex of each contact person
    • location (home, work, school, etc)
    • time spent together
    • frequency of usual contacts with this individual

Contact surveys are 💰 💰 💰

  • Part of why we don’t have them in Australia
  • Can we get a contact matrix for a country not on the list?
  • We need to have a best guess from existing surveys

Synthetic contact matrices

  • Pre-existing statistical methodologies project empirical contact matrices to new countries.
  • New projected contact matrices: “synthetic contact matrices”
  • Use existing contact surveys (e.g., POLYMOD)
  • Use setting-specific survey data on household, school, classroom, and workplace composition
  • Combine with empirical data on contact patterns in Europe using statistical models

Prem et al

  • One of the most widely used approaches to synthetic contact matrices
  • Extensively applied across infectious diseases applications
  • Initially provided 152 matrices for 152 countries (177 later in 2020)

Creating synthetic contact matrices

  • Prem et. al formed a model that predicts the number of contacts based on the age of participants in different settings (Home, Work, School, Other)
  • \(\text{n_contacts} \sim age_{ij} + setting_{k}\)
  • This model then allows for prediction to settings that are outside the original POLYMOD countries.
  • They provide new contact matrices for a given country

Populations are different within a country

Populations are different within a country

Populations are different within a country

What we want

  • We want to be able use a population age distribution
monash <- abs_age_lga("Monash (C)")
monash
# A tibble: 18 × 4 (conmat_population)
 - age: lower.age.limit
 - population: population
   lga        lower.age.limit  year population
   <chr>                <dbl> <dbl>      <dbl>
 1 Monash (C)               0  2020       9062
 2 Monash (C)               5  2020      11155
 3 Monash (C)              10  2020      11023
 4 Monash (C)              15  2020      12197
 5 Monash (C)              20  2020      23162
 6 Monash (C)              25  2020      19514
 7 Monash (C)              30  2020      14832
 8 Monash (C)              35  2020      13577
 9 Monash (C)              40  2020      12410
10 Monash (C)              45  2020      12572
11 Monash (C)              50  2020      11444
12 Monash (C)              55  2020      10474
13 Monash (C)              60  2020       9516
14 Monash (C)              65  2020       8479
15 Monash (C)              70  2020       7786
16 Monash (C)              75  2020       6519
17 Monash (C)              80  2020       5418
18 Monash (C)              85  2020       5796

What we want

  • Input population age distribution and get out a contact matrix.
extrapolate_from_data(
  population = monash
)

How do we extend Prem’s method?

  • Code is provided, but a few key issues:

    • It was code not written for reuse (code vs software)

      • No interface to specify a given country/region

      • Unclear which code matches which methods

      • Australia in original 152 countries, not in updated estimates provided in 2020.

An alternative approach

  • We developed an open-source approach

  • The R package conmat

  • It can be applied to any input population (big or small).

  • github.com/idem-lab/conmat

  • install using:

remotes::install_github("idem-lab/conmat")

Model details

  • These models don’t have much to go on. They are trained on the number of contacts between age groups \(i\) and \(j\):

\[\begin{aligned} c_{ij} = \beta_0 + \beta_1(|i-j|) + &\beta_2(|i-j|^2) + \beta_3(i\times j) + \beta_4(i+j) +\\ &\beta_5\max(i, j) + \beta_6\min(i, j) \end{aligned} \]

Tip

We want to fit this as a generalised additive model, so actually we have splines on these terms to satisfy the smoothness requirements. This is very similar to the approach by Prem et. al who perform post-hoc smoothing of the parameters after MCMC.

Model details

  • All that is needed to make predictions is the number of individuals in different age groups.
  • This is easily obtainable information from most statistics bureaus/governments.

Data sources

The socialmixr package has this information for most countries:

socialmixr::wpp_age("Australia", 2015)
     country lower.age.limit year population
1  Australia               0 2015    1545114
2  Australia               5 2015    1521110
3  Australia              10 2015    1416482
4  Australia              15 2015    1480341
5  Australia              20 2015    1663584
6  Australia              25 2015    1763281
7  Australia              30 2015    1753076
8  Australia              35 2015    1574157
9  Australia              40 2015    1655520
10 Australia              45 2015    1559506
11 Australia              50 2015    1569432
12 Australia              55 2015    1451869
13 Australia              60 2015    1281612
14 Australia              65 2015    1153780
15 Australia              70 2015     843695
16 Australia              75 2015     628870
17 Australia              80 2015     466797
18 Australia              85 2015     303108
19 Australia              90 2015     135089
20 Australia              95 2015      29268
21 Australia             100 2015       3855

And for inside Australia, conmat provides some ABS functions:

conmat::abs_age_lga("Monash (C)")
# A tibble: 18 × 4 (conmat_population)
 - age: lower.age.limit
 - population: population
   lga        lower.age.limit  year population
   <chr>                <dbl> <dbl>      <dbl>
 1 Monash (C)               0  2020       9062
 2 Monash (C)               5  2020      11155
 3 Monash (C)              10  2020      11023
 4 Monash (C)              15  2020      12197
 5 Monash (C)              20  2020      23162
 6 Monash (C)              25  2020      19514
 7 Monash (C)              30  2020      14832
 8 Monash (C)              35  2020      13577
 9 Monash (C)              40  2020      12410
10 Monash (C)              45  2020      12572
11 Monash (C)              50  2020      11444
12 Monash (C)              55  2020      10474
13 Monash (C)              60  2020       9516
14 Monash (C)              65  2020       8479
15 Monash (C)              70  2020       7786
16 Monash (C)              75  2020       6519
17 Monash (C)              80  2020       5418
18 Monash (C)              85  2020       5796

Demonstration of conmat

library(conmat)
monash <- abs_age_lga("Monash (C)")
monash
# A tibble: 18 × 4 (conmat_population)
 - age: lower.age.limit
 - population: population
   lga        lower.age.limit  year population
   <chr>                <dbl> <dbl>      <dbl>
 1 Monash (C)               0  2020       9062
 2 Monash (C)               5  2020      11155
 3 Monash (C)              10  2020      11023
 4 Monash (C)              15  2020      12197
 5 Monash (C)              20  2020      23162
 6 Monash (C)              25  2020      19514
 7 Monash (C)              30  2020      14832
 8 Monash (C)              35  2020      13577
 9 Monash (C)              40  2020      12410
10 Monash (C)              45  2020      12572
11 Monash (C)              50  2020      11444
12 Monash (C)              55  2020      10474
13 Monash (C)              60  2020       9516
14 Monash (C)              65  2020       8479
15 Monash (C)              70  2020       7786
16 Monash (C)              75  2020       6519
17 Monash (C)              80  2020       5418
18 Monash (C)              85  2020       5796

Extrapolate to a new population

synthetic_monash <- extrapolate_polymod(
 population = monash,
 age_breaks = c(seq(0, 80, by = 5), Inf)
)
synthetic_monash

Contact matrix: Monash home

autoplot(synthetic_monash$home)

Contact matrix: Monash work

autoplot(synthetic_monash$work)

Contact matrix: Monash all

autoplot(synthetic_monash)

Contact matrix: Australia

autoplot(synthetic_polymod_oz$home)

Australia vs Monash

Thoughts on design, software vs code, and workflows

Challenges in Design

  • Model fitting is fixed and rigid
    • Although there are arguments for age_break
    • You cannot change the model formula (yet)
    • Easy-to-use at cost of flexibility
synthetic_monash <- extrapolate_polymod(
 population = monash,
 age_breaks = c(seq(0, 75, by = 5), Inf)
)

Comment: Software vs code

  • It is great that Prem et al provided their estimates, and their code
  • Code describes how you did something
  • Software is designed for reuse
  • Github Issues are very useful
  • Extra contributions can be implemented as pull requests which can be tested with GH actions

Comment: Workflows

Circling back: why use conmat

Why should I use conmat? It’s easy!

world_data <- socialmixr::wpp_age()
population <- age_population(
    data = world_data,
    location_col = country,
    location = "Australia",
    age_col = lower.age.limit,
    year_col = year,
    year = 2015
)

population
# A tibble: 21 × 5 (conmat_population)
 - age: lower.age.limit
 - population: population
   country    year population lower.age.limit upper.age.limit
   <chr>     <int>      <dbl>           <dbl>           <dbl>
 1 Australia  2015    1545114               0               4
 2 Australia  2015    1521110               5               9
 3 Australia  2015    1416482              10              14
 4 Australia  2015    1480341              15              19
 5 Australia  2015    1663584              20              24
 6 Australia  2015    1763281              25              29
 7 Australia  2015    1753076              30              34
 8 Australia  2015    1574157              35              39
 9 Australia  2015    1655520              40              44
10 Australia  2015    1559506              45              49
# ℹ 11 more rows

Why should I use conmat?

Getting a contact matrix is easy:

contact_rates <- extrapolate_polymod(
    population = population,
    age_breaks = c(seq(0, 100, by = 5), Inf)
)

Why should I use conmat?

contact_rates$home
                 [0,5)       [5,10)      [10,15)      [15,20)      [20,25)
[0,5)     5.802591e-01 4.813549e-01 2.402471e-01 1.620169e-01 2.321232e-01
[5,10)    4.646578e-01 7.514361e-01 4.946728e-01 1.791579e-01 1.174632e-01
[10,15)   2.269854e-01 4.841612e-01 7.948923e-01 4.093514e-01 1.390883e-01
[15,20)   1.577753e-01 1.807368e-01 4.219247e-01 6.808458e-01 3.419038e-01
[20,25)   2.432882e-01 1.275370e-01 1.542955e-01 3.679832e-01 5.807800e-01
[25,30)   4.886107e-01 2.352219e-01 1.226892e-01 1.477810e-01 3.335959e-01
[30,35)   6.641648e-01 5.188639e-01 2.431926e-01 1.258683e-01 1.448990e-01
[35,40)   4.878934e-01 6.644817e-01 4.999019e-01 2.317582e-01 1.165367e-01
[40,45)   2.582931e-01 4.382340e-01 5.778488e-01 4.314489e-01 1.936253e-01
[45,50)   1.687273e-01 2.152100e-01 3.566710e-01 4.708383e-01 3.386774e-01
[50,55)   1.689770e-01 1.393551e-01 1.740598e-01 2.883215e-01 3.645253e-01
[55,60)   1.852236e-01 1.352834e-01 1.089212e-01 1.338642e-01 2.115404e-01
[60,65)   1.559059e-01 1.373831e-01 9.632628e-02 7.402672e-02 8.744629e-02
[65,70)   9.671786e-02 1.081266e-01 8.924898e-02 5.758573e-02 4.288228e-02
[70,75)   5.304213e-02 6.517089e-02 6.630047e-02 4.931502e-02 3.157968e-02
[75,80)   3.063155e-02 3.655708e-02 3.961536e-02 3.605985e-02 2.759811e-02
[80,85)   1.701003e-02 2.046395e-02 2.097059e-02 2.037989e-02 1.984339e-02
[85,90)   6.793228e-03 9.262530e-03 9.350083e-03 8.704453e-03 9.446582e-03
[90,95)   1.485635e-03 2.547974e-03 2.852779e-03 2.654936e-03 2.853791e-03
[95,100)  1.550040e-04 3.807001e-04 5.280096e-04 5.572703e-04 6.106408e-04
[100,Inf) 4.001678e-06 1.569944e-05 2.851021e-05 3.613939e-05 4.352133e-05
               [25,30)      [30,35)      [35,40)      [40,45)      [45,50)
[0,5)     4.406440e-01 5.982784e-01 4.552888e-01 2.465532e-01 1.640271e-01
[5,10)    2.047720e-01 4.511788e-01 5.985671e-01 4.038050e-01 2.019578e-01
[10,15)   1.045372e-01 2.069748e-01 4.407441e-01 5.211368e-01 3.275955e-01
[15,20)   1.297843e-01 1.104135e-01 2.106083e-01 4.010565e-01 4.457390e-01
[20,25)   3.153175e-01 1.368029e-01 1.139796e-01 1.937146e-01 3.450794e-01
[25,30)   5.243732e-01 3.144551e-01 1.356807e-01 1.013971e-01 1.623030e-01
[30,35)   3.148155e-01 4.980613e-01 2.954307e-01 1.163616e-01 8.325675e-02
[35,40)   1.311236e-01 2.851813e-01 4.517921e-01 2.542122e-01 9.813646e-02
[40,45)   9.579716e-02 1.098093e-01 2.485196e-01 4.075377e-01 2.345534e-01
[45,50)   1.505639e-01 7.714654e-02 9.420239e-02 2.303081e-01 4.062297e-01
[50,55)   2.628503e-01 1.238732e-01 6.901509e-02 9.188973e-02 2.408397e-01
[55,60)   2.718109e-01 2.122671e-01 1.101458e-01 6.639312e-02 9.279330e-02
[60,65)   1.446095e-01 2.063714e-01 1.771966e-01 9.622107e-02 5.879160e-02
[65,70)   5.498418e-02 1.035664e-01 1.608957e-01 1.394100e-01 7.437032e-02
[70,75)   2.651484e-02 3.913120e-02 7.821431e-02 1.191438e-01 1.011711e-01
[75,80)   2.059216e-02 1.972887e-02 2.968419e-02 5.702188e-02 8.691144e-02
[80,85)   1.798484e-02 1.488353e-02 1.402809e-02 2.034797e-02 4.046827e-02
[85,90)   1.090387e-02 1.044056e-02 8.227622e-03 7.649566e-03 1.201023e-02
[90,95)   3.633180e-03 4.228353e-03 3.796308e-03 3.023101e-03 3.099944e-03
[95,100)  7.478837e-04 9.265512e-04 1.031428e-03 9.725087e-04 8.415508e-04
[100,Inf) 5.166363e-05 6.101144e-05 7.499761e-05 8.726638e-05 8.258968e-05
               [50,55)      [55,60)      [60,65)      [65,70)      [70,75)
[0,5)     1.687022e-01 1.961683e-01 0.1832025702 0.1331132999 0.0918863354
[5,10)    1.343025e-01 1.383072e-01 0.1558369131 0.1436531774 0.1089811502
[10,15)   1.641843e-01 1.089894e-01 0.1069433218 0.1160533779 0.1085141367
[15,20)   2.803165e-01 1.380623e-01 0.0847102640 0.0771805911 0.0831931640
[20,25)   3.814376e-01 2.348162e-01 0.1076993145 0.0618578959 0.0573376911
[25,30)   2.909892e-01 3.192085e-01 0.1884261254 0.0839126928 0.0509323900
[30,35)   1.372914e-01 2.495673e-01 0.2692099753 0.1582363271 0.0752533160
[35,40)   7.383723e-02 1.250082e-01 0.2231323234 0.2372997628 0.1451958462
[40,45)   9.610865e-02 7.366441e-02 0.1184517518 0.2010068440 0.2162238279
[45,50)   2.473381e-01 1.010924e-01 0.0710647072 0.1052892436 0.1802835840
[50,55)   4.428859e-01 2.631261e-01 0.0980390321 0.0637188708 0.0974964688
[55,60)   2.480418e-01 4.500001e-01 0.2527757109 0.0905781696 0.0621663591
[60,65)   8.329582e-02 2.278236e-01 0.4239011662 0.2392786574 0.0895750648
[65,70)   4.622177e-02 6.970135e-02 0.2042952564 0.3920370894 0.2228202559
[70,75)   5.618912e-02 3.800650e-02 0.0607611916 0.1770269961 0.3268365761
[75,80)   7.927543e-02 4.788099e-02 0.0330311910 0.0500190560 0.1358156077
[80,85)   6.696845e-02 6.405183e-02 0.0377136045 0.0242834312 0.0344473988
[85,90)   2.589055e-02 4.275948e-02 0.0381342648 0.0207240237 0.0126427866
[90,95)   5.199957e-03 1.089333e-02 0.0165873700 0.0135501574 0.0068514835
[95,100)  8.791712e-04 1.406558e-03 0.0027504099 0.0038776570 0.0029390575
[100,Inf) 6.969596e-05 7.464274e-05 0.0001226298 0.0002243481 0.0002723907
               [75,80)      [80,85)      [85,90)      [90,95)     [95,100)
[0,5)     0.0695641984 0.0532109912 3.471403e-02 1.729263e-02 0.0058435830
[5,10)    0.0801412678 0.0617950322 4.569054e-02 2.862937e-02 0.0138543803
[10,15)   0.0850002537 0.0619793297 4.514235e-02 3.137307e-02 0.0188069300
[15,20)   0.0797478903 0.0620835720 4.331605e-02 3.009411e-02 0.0204588230
[20,25)   0.0656899136 0.0650600964 5.059482e-02 3.481559e-02 0.0241281820
[25,30)   0.0518553897 0.0623847088 6.178521e-02 4.689333e-02 0.0312640673
[30,35)   0.0497383729 0.0516862432 5.922775e-02 5.463775e-02 0.0387773632
[35,40)   0.0722403975 0.0470254386 4.505482e-02 4.735311e-02 0.0416690143
[40,45)   0.1356627465 0.0666837054 4.095132e-02 3.686412e-02 0.0384089094
[45,50)   0.2030314881 0.1302208821 6.313202e-02 3.711697e-02 0.0326351958
[50,55)   0.1803275843 0.2098327714 1.325186e-01 6.062553e-02 0.0331983468
[55,60)   0.1026709549 0.1891888359 2.063141e-01 1.197230e-01 0.0500681572
[60,65)   0.0638369340 0.1003980607 1.658346e-01 1.643075e-01 0.0882398189
[65,70)   0.0825348941 0.0551939725 7.694638e-02 1.145984e-01 0.1062162347
[70,75)   0.1780478029 0.0622046521 3.729424e-02 4.603665e-02 0.0639608777
[75,80)   0.2379470305 0.1226411587 3.901666e-02 2.061227e-02 0.0235174032
[80,85)   0.0890341775 0.1467765207 6.996593e-02 2.039686e-02 0.0100720633
[85,90)   0.0173395593 0.0428305924 6.727551e-02 3.055193e-02 0.0085097343
[90,95)   0.0040215520 0.0054816419 1.341277e-02 2.108756e-02 0.0091691088
[95,100)  0.0014166741 0.0008357541 1.153476e-03 2.831000e-03 0.0045419826
[100,Inf) 0.0001757175 0.0000815589 5.608818e-05 9.113023e-05 0.0002356887
             [100,Inf)
[0,5)     0.0015969450
[5,10)    0.0060478325
[10,15)   0.0107494942
[15,20)   0.0140445284
[20,25)   0.0182033974
[25,30)   0.0228616687
[30,35)   0.0270291084
[35,40)   0.0320725374
[40,45)   0.0364835436
[45,50)   0.0339033995
[50,55)   0.0278587842
[55,60)   0.0281256815
[60,65)   0.0416461592
[65,70)   0.0650512285
[70,75)   0.0627494825
[75,80)   0.0308777682
[80,85)   0.0104045396
[85,90)   0.0043801633
[90,95)   0.0031243614
[95,100)  0.0024948840
[100,Inf) 0.0003784101

Why should I use conmat? You can use a custom population

flat_population_data <- tibble(
    country = "flatland",
    year = 2024,
    population = 1000,
    lower.age.limit = seq(0, 75, by=5)
)

flat_population <- age_population(
    data = flat_population_data,
    location_col = country,
    location = "flatland",
    age_col = lower.age.limit,
    year_col = year,
    year = 2024
)

flat_contact_rates <- extrapolate_polymod(
    population = flat_population,
    age_breaks = c(seq(0, 75, by=5), Inf)
)

Why should I use conmat? You can use a custom population

flat_contact_rates$home
              [0,5)     [5,10)    [10,15)    [15,20)    [20,25)    [25,30)
[0,5)    0.56828986 0.47107930 0.23528412 0.15828319 0.22562515 0.42964988
[5,10)   0.47107930 0.76196823 0.50231004 0.18255328 0.11883529 0.20659277
[10,15)  0.23528412 0.50231004 0.82299109 0.42595334 0.14470685 0.10812909
[15,20)  0.15828319 0.18255328 0.42595334 0.68440444 0.34414245 0.13015930
[20,25)  0.22562515 0.11883529 0.14470685 0.34414245 0.54276791 0.29342160
[25,30)  0.42964988 0.20659277 0.10812909 0.13015930 0.29342160 0.46253966
[30,35)  0.58525292 0.45701473 0.21462152 0.11113527 0.12706493 0.27630913
[35,40)  0.44529458 0.60639263 0.45689173 0.21281962 0.10667273 0.11928571
[40,45)  0.24127232 0.40933635 0.53934055 0.40417570 0.18192135 0.08953615
[45,50)  0.16037541 0.20465635 0.33875107 0.44747109 0.32351471 0.14362854
[50,55)  0.16492113 0.13600777 0.16964705 0.28029573 0.35573859 0.25736993
[55,60)  0.19180402 0.14014100 0.11268901 0.13779345 0.21749363 0.28104124
[60,65)  0.17876427 0.15784117 0.11080587 0.08487633 0.09950725 0.16466518
[65,70)  0.12947242 0.14506103 0.12018401 0.07774984 0.05748962 0.07309408
[70,75)  0.08932050 0.10972788 0.11192484 0.08381432 0.05375817 0.04473290
[75,Inf) 0.05512666 0.06576018 0.07106603 0.06492533 0.05030197 0.03782410
            [30,35)    [35,40)    [40,45)    [45,50)    [50,55)    [55,60)
[0,5)    0.58525292 0.44529458 0.24127232 0.16037541 0.16492113 0.19180402
[5,10)   0.45701473 0.60639263 0.40933635 0.20465635 0.13600777 0.14014100
[10,15)  0.21462152 0.45689173 0.53934055 0.33875107 0.16964705 0.11268901
[15,20)  0.11113527 0.21281962 0.40417570 0.44747109 0.28029573 0.13779345
[20,25)  0.12706493 0.10667273 0.18192135 0.32351471 0.35573859 0.21749363
[25,30)  0.27630913 0.11928571 0.08953615 0.14362854 0.25736993 0.28104124
[30,35)  0.43869100 0.26005794 0.10254775 0.07332365 0.12101602 0.22021310
[35,40)  0.26005794 0.41225918 0.23226955 0.08961730 0.06739027 0.11438638
[40,45)  0.10254775 0.23226955 0.38043054 0.21889793 0.08962625 0.06884477
[45,50)  0.07332365 0.08961730 0.21889793 0.38628120 0.23490447 0.09578732
[50,55)  0.12101602 0.06739027 0.08962625 0.23490447 0.43255518 0.25611626
[55,60)  0.22021310 0.11438638 0.06884477 0.09578732 0.25611626 0.46618257
[60,65)  0.23655846 0.20418776 0.11121066 0.06761476 0.09516248 0.26086699
[65,70)  0.13798606 0.21578768 0.18859484 0.10093986 0.06227115 0.09317464
[70,75)  0.06552314 0.13088028 0.20091654 0.17270637 0.09635242 0.06459090
[75,Inf) 0.03588289 0.05306184 0.10103208 0.15531574 0.14574253 0.09020024
            [60,65)    [65,70)    [70,75)   [75,Inf)
[0,5)    0.17876427 0.12947242 0.08932050 0.06737703
[5,10)   0.15784117 0.14506103 0.10972788 0.08037355
[10,15)  0.11080587 0.12018401 0.11192484 0.08685848
[15,20)  0.08487633 0.07774984 0.08381432 0.07935318
[20,25)  0.09950725 0.05748962 0.05375817 0.06148018
[25,30)  0.16466518 0.07309408 0.04473290 0.04622945
[30,35)  0.23655846 0.13798606 0.06552314 0.04385687
[35,40)  0.20418776 0.21578768 0.13088028 0.06485336
[40,45)  0.11121066 0.18859484 0.20091654 0.12348365
[45,50)  0.06761476 0.10093986 0.17270637 0.18983035
[50,55)  0.09516248 0.06227115 0.09635242 0.17812976
[55,60)  0.26086699 0.09317464 0.06459090 0.11024474
[60,65)  0.48737980 0.27379182 0.10227898 0.07483573
[65,70)  0.27379182 0.52813388 0.29877425 0.11049744
[70,75)  0.10227898 0.29877425 0.55330597 0.29782838
[75,Inf) 0.06122924 0.09040699 0.24367777 0.41218989

Future Directions

  • Implement other social contact diaries beyond POLYMOD
  • Pre compute new matrices for countries/regions
  • Allow for using sub-yearly age groups
  • Validating against Prem and other methods
  • Interface to changing GAM model terms or specify model

Conclusion

  • Contact matrices are frequently used in infectious diseases modelling

  • Standard Prem et. al matrices are not written for re-use

  • conmat provides an interface to easily generate and update models from any data source

  • Can integrate vaccination, calculate next generation matrices, and a handful of other features

  • It’s available right now: github.com/idem-lab/conmat

Acknowledgements

Chitra Saraswati

Aarathy Babu

Nick Tierney

Nick Golding

SPECTRUM-SPARK Seed Funding

Learning more

github.com/idem-lab/conmat

njtierney.github.io/conmat-numbat-2024

njtierney.com

njtierney@aus.social

njtierney

nicholas.tierney@gmail.com

End.

Extras

The PCT

  • ~2,650 Miles (~4,270Km)
  • Mexico to Canada
  • 151 Days
  • 6 pairs of shoes
  • 3 pairs of sunglasses
  • Blogged at: <njt.micro.blog>
  • Some photos (shown on laptop)

Create A Next Generation Matrix

  • Once infected, a person can transmit an infectious disease to another, creating generations of infected individuals.
  • We can define a matrix describing the number of newly infected individuals in age groups, for consecutive generations.
  • This matrix is called a next generation matrix (NGM).

Create A Next Generation Matrix

ngm_monash <- generate_ngm(
  x = synthetic_monash,
  age_breaks = c(seq(0, 80, by = 5), Inf),
  R_target = 1.5
)
ngm_monash

Apply Vaccination

  • Applies the effect of vaccination on the next generation of infections, to understand and describe the reduction of acquisition and transmission in each age group.

  • Takes the following arguments:

    • ngm - a Next Generation Matrix
    • data - A data frame with location specifics
    • which columns are related to “coverage”, “acquisition”, and “transmission” in the data col

Example vaccination data

vaccination_effect_example_data
# A tibble: 17 × 4
   age_band coverage acquisition transmission
   <chr>       <dbl>       <dbl>        <dbl>
 1 0-4         0           0            0    
 2 5-11        0.782       0.583        0.254
 3 12-15       0.997       0.631        0.295
 4 16-19       0.965       0.786        0.469
 5 20-24       0.861       0.774        0.453
 6 25-29       0.997       0.778        0.458
 7 30-34       0.998       0.803        0.493
 8 35-39       0.998       0.829        0.533
 9 40-44       0.999       0.841        0.551
10 45-49       0.993       0.847        0.562
11 50-54       0.999       0.857        0.579
12 55-59       0.996       0.864        0.591
13 60-64       0.998       0.858        0.581
14 65-69       0.999       0.864        0.591
15 70-74       0.999       0.867        0.597
16 75-79       0.999       0.866        0.595
17 80+         0.999       0.844        0.556

Apply vaccination

ngm_vacc_monash <- apply_vaccination(
  ngm = ngm_monash,
  data = vaccination_effect_example_data,
  coverage_col = coverage,
  acquisition_col = acquisition,
  transmission_col = transmission
)

ngm_vacc_monash

How does the model work?

fit_single_contact_model <- function(contact_data, population) {

  # programatically add the offset term to the formula, so the model defines
  # information about the setting, without us having to pass it through to the
  # prediction data
  formula_no_offset <- contacts ~
    # deviation of contact age distribution from population age distribution
    s(age_to) +
    # number of contacts by age
    s(age_from) +
    # intergenerational contact patterns - enables the off-diagonals
    s(abs(age_from - age_to)) +
    # interaction between intergenerational patterns and age_from, to remove
    # ridge for some ages and settings
    s(abs(age_from - age_to), age_from) +
    # probabilities of both attending (any) school/work
    school_probability +
    work_probability
  
  # choose the offset variable based on the setting
  setting <- contact_data$setting[1]
  offset_variable <- switch(
    setting,
    school = "log_contactable_population_school",
    "log_contactable_population"
  )
  
  # add multiplicative offset for population contactable, to enable
  # extrapolation to new demographies
  # in mgcv, this part of the offset gets used in prediction, which 
  # is what we want. Those are the "contactable" parts, which we use
  # to extrapolate to new demographics.
  formula_offset <- sprintf("~. + offset(%s)", offset_variable)
  formula <- update(formula_no_offset, formula_offset)
  
  # contact model for all locations together
  contact_data %>%
    add_modelling_features() %>%
      # The modelling features added here are:
        # the school and work offsets
        # pop_age_to (interpolated population)
        # `log_contactable_population_school`, and ` log_contactable_population`
      population = population
    mgcv::bam(
      formula = formula,
      family = stats::poisson,
      # add number of participants as a multilpicative offset here rather than in
      # the formula, so it is not needed for prediction,
      # NOTE: the offset of participants allows us to get the rate per person
      offset = log(participants),
      data = .
    )
  
}

Alt approach to conmat model

# fit a model
library(conmat)
monash_age_pop <- abs_age_lga("Monash (C)")
monash_age_pop
# A tibble: 18 × 4 (conmat_population)
 - age: lower.age.limit
 - population: population
   lga        lower.age.limit  year population
   <chr>                <dbl> <dbl>      <dbl>
 1 Monash (C)               0  2020       9062
 2 Monash (C)               5  2020      11155
 3 Monash (C)              10  2020      11023
 4 Monash (C)              15  2020      12197
 5 Monash (C)              20  2020      23162
 6 Monash (C)              25  2020      19514
 7 Monash (C)              30  2020      14832
 8 Monash (C)              35  2020      13577
 9 Monash (C)              40  2020      12410
10 Monash (C)              45  2020      12572
11 Monash (C)              50  2020      11444
12 Monash (C)              55  2020      10474
13 Monash (C)              60  2020       9516
14 Monash (C)              65  2020       8479
15 Monash (C)              70  2020       7786
16 Monash (C)              75  2020       6519
17 Monash (C)              80  2020       5418
18 Monash (C)              85  2020       5796

Alt approach to conmat model

polymod_contact_data <- get_polymod_setting_data()
polymod_survey_data <- get_polymod_population()

Alt approach to conmat model

setting_models <- fit_setting_contacts(
  contact_data_list = polymod_contact_data,
  population = polymod_survey_data
  )

str(setting_models)
List of 4
 $ home  :List of 54
  ..$ coefficients     : Named num [1:57] 0.617 -0.347 0.537 -68.875 4.169 ...
  .. ..- attr(*, "names")= chr [1:57] "(Intercept)" "school_probability" "work_probability" "s(gam_age_offdiag).1" ...
  ..$ db.drho          : num [1:55, 1:6] -0.00151 0.00455 0.00278 -0.91056 -0.334 ...
  ..$ gcv.ubre         : Named num 14790
  .. ..- attr(*, "names")= chr "fREML"
  ..$ mgcv.conv        :List of 2
  .. ..$ iter   : int 10
  .. ..$ message: chr "full convergence"
  ..$ rank             : int 55
  ..$ Ve               : num [1:57, 1:57] 0.001244 -0.000312 -0.003213 -0.075219 -0.013809 ...
  ..$ scale.estimated  : logi FALSE
  ..$ outer.info       :List of 4
  .. ..$ conv: chr "full convergence"
  .. ..$ iter: int 10
  .. ..$ grad: num [1:6] 7.09e-08 -1.92e-05 -1.44e-10 -4.01e-10 -1.35e-11 ...
  .. ..$ hess: num [1:6, 1:6] 3.50 3.76e-06 6.54e-03 -2.02e-02 -1.70e-03 ...
  ..$ optimizer        : chr [1:2] "perf" "newton"
  ..$ scale            : num 1
  ..$ sig2             : num 1
  ..$ sp               : Named num [1:6] 1.16e-03 1.03e+03 1.29e-01 2.06e-01 3.56e-02 ...
  .. ..- attr(*, "names")= chr [1:6] "s(gam_age_offdiag)" "s(gam_age_offdiag_2)" "s(gam_age_diag_prod)" "s(gam_age_diag_sum)" ...
  ..$ edf              : Named num [1:57] 1 1 1 0.903 0.976 ...
  .. ..- attr(*, "names")= chr [1:57] "(Intercept)" "school_probability" "work_probability" "s(gam_age_offdiag).1" ...
  ..$ edf1             : num [1:57] 1 1 1 0.989 0.995 ...
  ..$ edf2             : num [1:57] 1 1 1 1.08 0.975 ...
  ..$ hat              : num [1:57] 1 1 1 1 0.999 ...
  ..$ Vp               : num [1:57, 1:57] 0.001437 -0.000329 -0.00384 -0.097236 -0.017076 ...
  ..$ Vc               : num [1:57, 1:57] 0.001587 -0.000156 -0.004901 -0.10023 -0.016791 ...
  ..$ V.sp             : num [1:6, 1:6] 0.285793 -0.055921 0.001153 0.006059 0.000227 ...
  ..$ R                : num [1:57, 1:57] -117.23 72.94 8.84 -78.92 0 ...
  .. ..- attr(*, "dimnames")=List of 2
  .. .. ..$ : NULL
  .. .. ..$ : NULL
  ..$ iter             : int 8
  ..$ wt               : num [1:8787] 6.22 6.52 6.59 6.44 6.12 ...
  ..$ y                : int [1:8787] 10 7 12 14 12 6 8 9 6 6 ...
  ..$ prior.weights    : num [1:8787] 1 1 1 1 1 1 1 1 1 1 ...
  ..$ assign           : int [1:3] 0 1 2
  ..$ boundary         : logi FALSE
  ..$ call             : language mgcv::bam(formula = formula, family = stats::poisson, data = ., offset = log(participants))
  ..$ cmX              : Named num [1:57] 1.00 3.86e-02 1.42e-01 -2.69e-14 -5.18e-15 ...
  .. ..- attr(*, "names")= chr [1:57] "(Intercept)" "school_probability" "work_probability" "" ...
  ..$ control          :List of 19
  .. ..$ nthreads    : num 1
  .. ..$ ncv.threads : num 1
  .. ..$ irls.reg    : num 0
  .. ..$ epsilon     : num 1e-07
  .. ..$ maxit       : num 200
  .. ..$ trace       : logi FALSE
  .. ..$ mgcv.tol    : num 1e-07
  .. ..$ mgcv.half   : num 15
  .. ..$ rank.tol    : num 1.49e-08
  .. ..$ nlm         :List of 6
  .. .. ..$ ndigit           : num 7
  .. .. ..$ gradtol          : num 1e-06
  .. .. ..$ stepmax          : num 2
  .. .. ..$ steptol          : num 1e-04
  .. .. ..$ iterlim          : num 200
  .. .. ..$ check.analyticals: logi FALSE
  .. ..$ optim       :List of 1
  .. .. ..$ factr: num 1e+07
  .. ..$ newton      :List of 5
  .. .. ..$ conv.tol: num 1e-06
  .. .. ..$ maxNstep: num 5
  .. .. ..$ maxSstep: num 2
  .. .. ..$ maxHalf : num 30
  .. .. ..$ use.svd : logi FALSE
  .. ..$ idLinksBases: logi TRUE
  .. ..$ scalePenalty: logi TRUE
  .. ..$ efs.lspmax  : num 15
  .. ..$ efs.tol     : num 0.1
  .. ..$ keepData    : logi FALSE
  .. ..$ scale.est   : chr "fletcher"
  .. ..$ edge.correct: logi FALSE
  ..$ converged        : logi TRUE
  ..$ data             : logi NA
  ..$ df.null          : int 8787
  ..$ df.residual      : num 8753
  ..$ family           :List of 13
  .. ..$ family    : chr "poisson"
  .. ..$ link      : chr "log"
  .. ..$ linkfun   :function (mu)  
  .. ..$ linkinv   :function (eta)  
  .. ..$ variance  :function (mu)  
  .. ..$ dev.resids:function (y, mu, wt)  
  .. ..$ aic       :function (y, n, mu, wt, dev)  
  .. ..$ mu.eta    :function (eta)  
  .. ..$ initialize:  expression({  if (any(y < 0))  stop("negative values not allowed for the 'Poisson' family")  n <- rep.int(1, nobs| __truncated__
  .. ..$ validmu   :function (mu)  
  .. ..$ valideta  :function (eta)  
  .. ..$ simulate  :function (object, nsim)  
  .. ..$ dispersion: num 1
  .. ..- attr(*, "class")= chr "family"
  ..$ formula          :Class 'formula'  language contacts ~ s(gam_age_offdiag) + s(gam_age_offdiag_2) + s(gam_age_diag_prod) +      s(gam_age_diag_sum) + s(gam_ag| __truncated__ ...
  .. .. ..- attr(*, ".Environment")=<environment: R_GlobalEnv> 
  ..$ method           : chr "fREML"
  ..$ min.edf          : num 9
  ..$ model            :'data.frame':   8787 obs. of  11 variables:
  .. ..$ contacts                          : int [1:8787] 10 7 12 14 12 6 8 9 6 6 ...
  .. ..$ school_probability                : num [1:8787] 0 0 0 0 0 0 0 0 0 0 ...
  .. ..$ work_probability                  : num [1:8787] 0 0 0 0 0 0 0 0 0 0 ...
  .. ..$ offset(log_contactable_population): num [1:8787] -4.67 -4.66 -4.65 -4.63 -4.62 ...
  .. ..$ gam_age_offdiag                   : num [1:8787] 0 1 2 3 4 5 6 7 8 9 ...
  .. ..$ gam_age_offdiag_2                 : num [1:8787] 0 1 4 9 16 25 36 49 64 81 ...
  .. ..$ gam_age_diag_prod                 : num [1:8787] 0 0 0 0 0 0 0 0 0 0 ...
  .. ..$ gam_age_diag_sum                  : num [1:8787] 0 1 2 3 4 5 6 7 8 9 ...
  .. ..$ gam_age_pmax                      : num [1:8787] 0 1 2 3 4 5 6 7 8 9 ...
  .. ..$ gam_age_pmin                      : num [1:8787] 0 0 0 0 0 0 0 0 0 0 ...
  .. ..$ (offset)                          : num [1:8787] 4.52 4.52 4.52 4.52 4.52 ...
  .. ..- attr(*, "terms")=Classes 'terms', 'formula'  language contacts ~ school_probability + work_probability + offset(log_contactable_population) +      gam_age_offdiag + ga| __truncated__ ...
  .. .. .. ..- attr(*, "variables")= language list(contacts, school_probability, work_probability, offset(log_contactable_population),      gam_age_offdiag, ga| __truncated__ ...
  .. .. .. ..- attr(*, "offset")= int 4
  .. .. .. ..- attr(*, "factors")= int [1:10, 1:8] 0 1 0 0 0 0 0 0 0 0 ...
  .. .. .. .. ..- attr(*, "dimnames")=List of 2
  .. .. .. .. .. ..$ : chr [1:10] "contacts" "school_probability" "work_probability" "offset(log_contactable_population)" ...
  .. .. .. .. .. ..$ : chr [1:8] "school_probability" "work_probability" "gam_age_offdiag" "gam_age_offdiag_2" ...
  .. .. .. ..- attr(*, "term.labels")= chr [1:8] "school_probability" "work_probability" "gam_age_offdiag" "gam_age_offdiag_2" ...
  .. .. .. ..- attr(*, "order")= int [1:8] 1 1 1 1 1 1 1 1
  .. .. .. ..- attr(*, "intercept")= int 1
  .. .. .. ..- attr(*, "response")= int 1
  .. .. .. ..- attr(*, ".Environment")=<environment: R_GlobalEnv> 
  .. .. .. ..- attr(*, "predvars")= language list(contacts, school_probability, work_probability, offset(log_contactable_population),      gam_age_offdiag, ga| __truncated__ ...
  .. .. .. ..- attr(*, "dataClasses")= Named chr [1:11] "numeric" "numeric" "numeric" "numeric" ...
  .. .. .. .. ..- attr(*, "names")= chr [1:11] "contacts" "school_probability" "work_probability" "offset(log_contactable_population)" ...
  ..$ nsdf             : int 3
  ..$ offset           : num [1:8787] -0.1527 -0.1387 -0.1246 -0.1105 -0.0964 ...
  ..$ pterms           :Classes 'terms', 'formula'  language contacts ~ school_probability + work_probability + offset(log_contactable_population)
  .. .. ..- attr(*, "variables")= language list(contacts, school_probability, work_probability, offset(log_contactable_population))
  .. .. ..- attr(*, "offset")= int 4
  .. .. ..- attr(*, "factors")= int [1:4, 1:2] 0 1 0 0 0 0 1 0
  .. .. .. ..- attr(*, "dimnames")=List of 2
  .. .. .. .. ..$ : chr [1:4] "contacts" "school_probability" "work_probability" "offset(log_contactable_population)"
  .. .. .. .. ..$ : chr [1:2] "school_probability" "work_probability"
  .. .. ..- attr(*, "term.labels")= chr [1:2] "school_probability" "work_probability"
  .. .. ..- attr(*, "order")= int [1:2] 1 1
  .. .. ..- attr(*, "intercept")= int 1
  .. .. ..- attr(*, "response")= int 1
  .. .. ..- attr(*, ".Environment")=<environment: R_GlobalEnv> 
  .. .. ..- attr(*, "predvars")= language list(contacts, school_probability, work_probability, offset(log_contactable_population))
  .. .. ..- attr(*, "dataClasses")= Named chr [1:5] "numeric" "numeric" "numeric" "numeric" ...
  .. .. .. ..- attr(*, "names")= chr [1:5] "contacts" "school_probability" "work_probability" "offset(log_contactable_population)" ...
  ..$ pred.formula     :Class 'formula'  language ~school_probability + work_probability + log_contactable_population + gam_age_offdiag +      gam_age_offdiag_2 + | __truncated__ ...
  .. .. ..- attr(*, ".Environment")=<environment: R_GlobalEnv> 
  ..$ smooth           :List of 6
  .. ..$ :List of 29
  .. .. ..$ term          : chr "gam_age_offdiag"
  .. .. ..$ bs.dim        : num 10
  .. .. ..$ fixed         : logi FALSE
  .. .. ..$ dim           : int 1
  .. .. ..$ p.order       : num 0
  .. .. ..$ by            : chr "NA"
  .. .. ..$ label         : chr "s(gam_age_offdiag)"
  .. .. ..$ xt            : NULL
  .. .. ..$ id            : NULL
  .. .. ..$ sp            : Named num -1
  .. .. .. ..- attr(*, "names")= chr "s(gam_age_offdiag)"
  .. .. ..$ drop.null     : num 0
  .. .. ..$ S             :List of 1
  .. .. .. ..$ : num [1:9, 1:9] 2.558 -0.769 4.813 -1.005 4.801 ...
  .. .. ..$ UZ            : num [1:103, 1:10] -3.97e-06 -3.66e-06 -3.37e-06 -3.09e-06 -2.83e-06 ...
  .. .. ..$ Xu            : num [1:101, 1] -32 -31 -30 -29 -28 ...
  .. .. ..$ df            : num 9
  .. .. ..$ shift         : num [1(1d)] 32
  .. .. ..$ rank          : num 8
  .. .. ..$ null.space.dim: num 1
  .. .. ..$ plot.me       : logi TRUE
  .. .. ..$ side.constrain: logi TRUE
  .. .. ..$ repara        : logi TRUE
  .. .. ..$ C             : num 0
  .. .. ..$ S.scale       : num 2.27e-05
  .. .. ..$ Cp            : num 0
  .. .. ..$ vn            : chr "gam_age_offdiag"
  .. .. ..$ first.para    : num 4
  .. .. ..$ last.para     : num 12
  .. .. ..$ first.sp      : num 1
  .. .. ..$ last.sp       : num 1
  .. .. ..- attr(*, "class")= chr [1:2] "tprs.smooth" "mgcv.smooth"
  .. .. ..- attr(*, "qrc")= 'sweepDrop' num [1:10] 9 -0.938 -0.423 -0.743 -0.413 ...
  .. .. ..- attr(*, "nCons")= num 1
  .. ..$ :List of 29
  .. .. ..$ term          : chr "gam_age_offdiag_2"
  .. .. ..$ bs.dim        : num 10
  .. .. ..$ fixed         : logi FALSE
  .. .. ..$ dim           : int 1
  .. .. ..$ p.order       : num 0
  .. .. ..$ by            : chr "NA"
  .. .. ..$ label         : chr "s(gam_age_offdiag_2)"
  .. .. ..$ xt            : NULL
  .. .. ..$ id            : NULL
  .. .. ..$ sp            : Named num -1
  .. .. .. ..- attr(*, "names")= chr "s(gam_age_offdiag_2)"
  .. .. ..$ drop.null     : num 0
  .. .. ..$ S             :List of 1
  .. .. .. ..$ : num [1:9, 1:9] 1.75 -1.68 -3.12 -2.03 3.03 ...
  .. .. ..$ UZ            : num [1:103, 1:10] 1.54e-12 1.54e-12 1.54e-12 1.53e-12 1.51e-12 ...
  .. .. ..$ Xu            : num [1:101, 1] -1533 -1532 -1529 -1524 -1517 ...
  .. .. ..$ df            : num 9
  .. .. ..$ shift         : num [1(1d)] 1533
  .. .. ..$ rank          : num 8
  .. .. ..$ null.space.dim: num 1
  .. .. ..$ plot.me       : logi TRUE
  .. .. ..$ side.constrain: logi TRUE
  .. .. ..$ repara        : logi TRUE
  .. .. ..$ C             : num 0
  .. .. ..$ S.scale       : num 2.23e-11
  .. .. ..$ Cp            : num 0
  .. .. ..$ vn            : chr "gam_age_offdiag_2"
  .. .. ..$ first.para    : num 13
  .. .. ..$ last.para     : num 21
  .. .. ..$ first.sp      : num 2
  .. .. ..$ last.sp       : num 2
  .. .. ..- attr(*, "class")= chr [1:2] "tprs.smooth" "mgcv.smooth"
  .. .. ..- attr(*, "qrc")= 'sweepDrop' num [1:10] 9 0.961 0.691 -0.87 0.676 ...
  .. .. ..- attr(*, "nCons")= num 1
  .. ..$ :List of 29
  .. .. ..$ term          : chr "gam_age_diag_prod"
  .. .. ..$ bs.dim        : num 10
  .. .. ..$ fixed         : logi FALSE
  .. .. ..$ dim           : int 1
  .. .. ..$ p.order       : num 0
  .. .. ..$ by            : chr "NA"
  .. .. ..$ label         : chr "s(gam_age_diag_prod)"
  .. .. ..$ xt            : NULL
  .. .. ..$ id            : NULL
  .. .. ..$ sp            : Named num -1
  .. .. .. ..- attr(*, "names")= chr "s(gam_age_diag_prod)"
  .. .. ..$ drop.null     : num 0
  .. .. ..$ S             :List of 1
  .. .. .. ..$ : num [1:9, 1:9] 4.08 3.48 -8.2 -5.06 -7.78 ...
  .. .. ..$ UZ            : num [1:2002, 1:10] -2.38e-13 -2.37e-13 -2.37e-13 -2.36e-13 -2.36e-13 ...
  .. .. ..$ Xu            : num [1:2000, 1] -2152 -2150 -2149 -2146 -2145 ...
  .. .. ..$ df            : num 9
  .. .. ..$ shift         : num [1(1d)] 2152
  .. .. ..$ rank          : num 8
  .. .. ..$ null.space.dim: num 1
  .. .. ..$ plot.me       : logi TRUE
  .. .. ..$ side.constrain: logi TRUE
  .. .. ..$ repara        : logi TRUE
  .. .. ..$ C             : num 0
  .. .. ..$ S.scale       : num 2.26e-11
  .. .. ..$ Cp            : num 0
  .. .. ..$ vn            : chr "gam_age_diag_prod"
  .. .. ..$ first.para    : num 22
  .. .. ..$ last.para     : num 30
  .. .. ..$ first.sp      : num 3
  .. .. ..$ last.sp       : num 3
  .. .. ..- attr(*, "class")= chr [1:2] "tprs.smooth" "mgcv.smooth"
  .. .. ..- attr(*, "qrc")= 'sweepDrop' num [1:10] 9 -0.947 0.262 0.763 -0.162 ...
  .. .. ..- attr(*, "nCons")= num 1
  .. ..$ :List of 29
  .. .. ..$ term          : chr "gam_age_diag_sum"
  .. .. ..$ bs.dim        : num 10
  .. .. ..$ fixed         : logi FALSE
  .. .. ..$ dim           : int 1
  .. .. ..$ p.order       : num 0
  .. .. ..$ by            : chr "NA"
  .. .. ..$ label         : chr "s(gam_age_diag_sum)"
  .. .. ..$ xt            : NULL
  .. .. ..$ id            : NULL
  .. .. ..$ sp            : Named num -1
  .. .. .. ..- attr(*, "names")= chr "s(gam_age_diag_sum)"
  .. .. ..$ drop.null     : num 0
  .. .. ..$ S             :List of 1
  .. .. .. ..$ : num [1:9, 1:9] 4.97 1.09 11.11 -1.66 -11.93 ...
  .. .. ..$ UZ            : num [1:193, 1:10] 4.62e-07 4.42e-07 4.23e-07 4.04e-07 3.85e-07 ...
  .. .. ..$ Xu            : num [1:191, 1] -93 -92 -91 -90 -89 ...
  .. .. ..$ df            : num 9
  .. .. ..$ shift         : num [1(1d)] 93
  .. .. ..$ rank          : num 8
  .. .. ..$ null.space.dim: num 1
  .. .. ..$ plot.me       : logi TRUE
  .. .. ..$ side.constrain: logi TRUE
  .. .. ..$ repara        : logi TRUE
  .. .. ..$ C             : num 0
  .. .. ..$ S.scale       : num 2.88e-06
  .. .. ..$ Cp            : num 0
  .. .. ..$ vn            : chr "gam_age_diag_sum"
  .. .. ..$ first.para    : num 31
  .. .. ..$ last.para     : num 39
  .. .. ..$ first.sp      : num 4
  .. .. ..$ last.sp       : num 4
  .. .. ..- attr(*, "class")= chr [1:2] "tprs.smooth" "mgcv.smooth"
  .. .. ..- attr(*, "qrc")= 'sweepDrop' num [1:10] 9 0.951 0.292 0.558 -0.351 ...
  .. .. ..- attr(*, "nCons")= num 1
  .. ..$ :List of 29
  .. .. ..$ term          : chr "gam_age_pmax"
  .. .. ..$ bs.dim        : num 10
  .. .. ..$ fixed         : logi FALSE
  .. .. ..$ dim           : int 1
  .. .. ..$ p.order       : num 0
  .. .. ..$ by            : chr "NA"
  .. .. ..$ label         : chr "s(gam_age_pmax)"
  .. .. ..$ xt            : NULL
  .. .. ..$ id            : NULL
  .. .. ..$ sp            : Named num -1
  .. .. .. ..- attr(*, "names")= chr "s(gam_age_pmax)"
  .. .. ..$ drop.null     : num 0
  .. .. ..$ S             :List of 1
  .. .. .. ..$ : num [1:9, 1:9] 4.151 -0.822 7.91 -1.089 8.153 ...
  .. .. ..$ UZ            : num [1:103, 1:10] -4.38e-06 -4.04e-06 -3.72e-06 -3.41e-06 -3.12e-06 ...
  .. .. ..$ Xu            : num [1:101, 1] -62.5 -61.5 -60.5 -59.5 -58.5 ...
  .. .. ..$ df            : num 9
  .. .. ..$ shift         : num [1(1d)] 62.5
  .. .. ..$ rank          : num 8
  .. .. ..$ null.space.dim: num 1
  .. .. ..$ plot.me       : logi TRUE
  .. .. ..$ side.constrain: logi TRUE
  .. .. ..$ repara        : logi TRUE
  .. .. ..$ C             : num 0
  .. .. ..$ S.scale       : num 1.7e-05
  .. .. ..$ Cp            : num 0
  .. .. ..$ vn            : chr "gam_age_pmax"
  .. .. ..$ first.para    : num 40
  .. .. ..$ last.para     : num 48
  .. .. ..$ first.sp      : num 5
  .. .. ..$ last.sp       : num 5
  .. .. ..- attr(*, "class")= chr [1:2] "tprs.smooth" "mgcv.smooth"
  .. .. ..- attr(*, "qrc")= 'sweepDrop' num [1:10] 9 -0.943 0.625 -0.705 0.621 ...
  .. .. ..- attr(*, "nCons")= num 1
  .. ..$ :List of 29
  .. .. ..$ term          : chr "gam_age_pmin"
  .. .. ..$ bs.dim        : num 10
  .. .. ..$ fixed         : logi FALSE
  .. .. ..$ dim           : int 1
  .. .. ..$ p.order       : num 0
  .. .. ..$ by            : chr "NA"
  .. .. ..$ label         : chr "s(gam_age_pmin)"
  .. .. ..$ xt            : NULL
  .. .. ..$ id            : NULL
  .. .. ..$ sp            : Named num -1
  .. .. .. ..- attr(*, "names")= chr "s(gam_age_pmin)"
  .. .. ..$ drop.null     : num 0
  .. .. ..$ S             :List of 1
  .. .. .. ..$ : num [1:9, 1:9] 3.642 0.732 6.581 -0.913 6.554 ...
  .. .. ..$ UZ            : num [1:93, 1:10] -6.87e-06 -6.26e-06 -5.67e-06 -5.11e-06 -4.59e-06 ...
  .. .. ..$ Xu            : num [1:91, 1] -30.5 -29.5 -28.5 -27.5 -26.5 ...
  .. .. ..$ df            : num 9
  .. .. ..$ shift         : num [1(1d)] 30.5
  .. .. ..$ rank          : num 8
  .. .. ..$ null.space.dim: num 1
  .. .. ..$ plot.me       : logi TRUE
  .. .. ..$ side.constrain: logi TRUE
  .. .. ..$ repara        : logi TRUE
  .. .. ..$ C             : num 0
  .. .. ..$ S.scale       : num 2.24e-05
  .. .. ..$ Cp            : num 0
  .. .. ..$ vn            : chr "gam_age_pmin"
  .. .. ..$ first.para    : num 49
  .. .. ..$ last.para     : num 57
  .. .. ..$ first.sp      : num 6
  .. .. ..$ last.sp       : num 6
  .. .. ..- attr(*, "class")= chr [1:2] "tprs.smooth" "mgcv.smooth"
  .. .. ..- attr(*, "qrc")= 'sweepDrop' num [1:10] 9 -0.939 -0.671 -0.745 0.669 ...
  .. .. ..- attr(*, "nCons")= num 1
  ..$ terms            :Classes 'terms', 'formula'  language contacts ~ school_probability + work_probability + offset(log_contactable_population) +      gam_age_offdiag + ga| __truncated__ ...
  .. .. ..- attr(*, "variables")= language list(contacts, school_probability, work_probability, offset(log_contactable_population),      gam_age_offdiag, ga| __truncated__ ...
  .. .. ..- attr(*, "offset")= int 4
  .. .. ..- attr(*, "factors")= int [1:10, 1:8] 0 1 0 0 0 0 0 0 0 0 ...
  .. .. .. ..- attr(*, "dimnames")=List of 2
  .. .. .. .. ..$ : chr [1:10] "contacts" "school_probability" "work_probability" "offset(log_contactable_population)" ...
  .. .. .. .. ..$ : chr [1:8] "school_probability" "work_probability" "gam_age_offdiag" "gam_age_offdiag_2" ...
  .. .. ..- attr(*, "term.labels")= chr [1:8] "school_probability" "work_probability" "gam_age_offdiag" "gam_age_offdiag_2" ...
  .. .. ..- attr(*, "order")= int [1:8] 1 1 1 1 1 1 1 1
  .. .. ..- attr(*, "intercept")= int 1
  .. .. ..- attr(*, "response")= int 1
  .. .. ..- attr(*, ".Environment")=<environment: R_GlobalEnv> 
  .. .. ..- attr(*, "predvars")= language list(contacts, school_probability, work_probability, offset(log_contactable_population),      gam_age_offdiag, ga| __truncated__ ...
  .. .. ..- attr(*, "dataClasses")= Named chr [1:11] "numeric" "numeric" "numeric" "numeric" ...
  .. .. .. ..- attr(*, "names")= chr [1:11] "contacts" "school_probability" "work_probability" "offset(log_contactable_population)" ...
  ..$ var.summary      :List of 9
  .. ..$ school_probability        : num [1:3] 0 0.00147 0.90552
  .. ..$ work_probability          : num [1:3] 0 0.0183 0.58
  .. ..$ log_contactable_population: num [1:3] -9.34 -4.5 -4.16
  .. ..$ gam_age_offdiag           : num [1:3] 0 28 100
  .. ..$ gam_age_offdiag_2         : num [1:3] 0 784 10000
  .. ..$ gam_age_diag_prod         : num [1:3] 0 1596 9000
  .. ..$ gam_age_diag_sum          : num [1:3] 0 93 190
  .. ..$ gam_age_pmax              : num [1:3] 0 66 100
  .. ..$ gam_age_pmin              : num [1:3] 0 27 90
  ..$ weights          : num [1:8787] 6.22 6.52 6.59 6.44 6.12 ...
  ..$ xlevels          : Named list()
  ..$ NA.action        :function (object, ...)  
  ..$ linear.predictors: num [1:8787] 1.83 1.87 1.88 1.86 1.81 ...
  ..$ fitted.values    : num [1:8787] 6.22 6.52 6.59 6.44 6.12 ...
  ..$ residuals        : num [1:8787] 1.393 0.187 1.89 2.573 2.098 ...
  ..$ deviance         : num 10296
  ..$ aic              : num 26986
  ..$ null.deviance    : num 44061
  ..- attr(*, "class")= chr [1:4] "bam" "gam" "glm" "lm"
 $ work  :List of 54
  ..$ coefficients     : Named num [1:57] -9.56e-01 -3.50 2.16 4.35e-05 -7.51e-06 ...
  .. ..- attr(*, "names")= chr [1:57] "(Intercept)" "school_probability" "work_probability" "s(gam_age_offdiag).1" ...
  ..$ db.drho          : num [1:55, 1:6] 1.99e-07 -5.71e-06 -2.08e-06 -2.50e-04 -6.47e-05 ...
  ..$ gcv.ubre         : Named num 24356
  .. ..- attr(*, "names")= chr "fREML"
  ..$ mgcv.conv        :List of 2
  .. ..$ iter   : int 13
  .. ..$ message: chr "full convergence"
  ..$ rank             : int 55
  ..$ Ve               : num [1:57, 1:57] 1.16e-02 -6.12e-03 -1.49e-02 1.64e-07 -2.79e-07 ...
  ..$ scale.estimated  : logi FALSE
  ..$ outer.info       :List of 4
  .. ..$ conv: chr "full convergence"
  .. ..$ iter: int 13
  .. ..$ grad: num [1:6] -1.08e-04 6.46e-05 1.01e-05 -7.70e-06 -3.87e-06 ...
  .. ..$ hess: num [1:6, 1:6] 1.08e-04 -6.48e-05 -1.01e-05 7.75e-06 3.89e-06 ...
  ..$ optimizer        : chr [1:2] "perf" "newton"
  ..$ scale            : num 1
  ..$ sig2             : num 1
  ..$ sp               : Named num [1:6] 3.48e+03 9.06e-02 2.01e-02 8.45e-04 3.53e-03 ...
  .. ..- attr(*, "names")= chr [1:6] "s(gam_age_offdiag)" "s(gam_age_offdiag_2)" "s(gam_age_diag_prod)" "s(gam_age_diag_sum)" ...
  ..$ edf              : Named num [1:57] 1.00 1.00 1.00 -1.36e-05 2.30e-05 ...
  .. ..- attr(*, "names")= chr [1:57] "(Intercept)" "school_probability" "work_probability" "s(gam_age_offdiag).1" ...
  ..$ edf1             : num [1:57] 1.00 1.00 1.00 -1.18e-05 3.44e-05 ...
  ..$ edf2             : num [1:57] 1.00 1.00 1.00 3.16e-05 3.91e-05 ...
  ..$ hat              : num [1:57] 1 0.986 1 1 0.944 ...
  ..$ Vp               : num [1:57, 1:57] 2.12e-02 -6.60e-03 -1.73e-02 -5.76e-08 -4.47e-07 ...
  ..$ Vc               : num [1:57, 1:57] 2.87e-02 7.58e-03 -1.63e-02 -8.04e-07 -2.24e-07 ...
  ..$ V.sp             : num [1:6, 1:6] 9242.0084 0.3825 0.2028 -0.1229 -0.0125 ...
  ..$ R                : num [1:57, 1:57] -132 0 0 0 0 ...
  .. ..- attr(*, "dimnames")=List of 2
  .. .. ..$ : NULL
  .. .. ..$ : NULL
  ..$ iter             : int 11
  ..$ wt               : num [1:8787] 0.00929 0.01031 0.01129 0.01219 0.01302 ...
  ..$ y                : int [1:8787] 0 0 0 0 0 0 0 0 0 0 ...
  ..$ prior.weights    : num [1:8787] 1 1 1 1 1 1 1 1 1 1 ...
  ..$ assign           : int [1:3] 0 1 2
  ..$ boundary         : logi FALSE
  ..$ call             : language mgcv::bam(formula = formula, family = stats::poisson, data = ., offset = log(participants))
  ..$ cmX              : Named num [1:57] 1.00 3.86e-02 1.42e-01 -2.69e-14 -5.18e-15 ...
  .. ..- attr(*, "names")= chr [1:57] "(Intercept)" "school_probability" "work_probability" "" ...
  ..$ control          :List of 19
  .. ..$ nthreads    : num 1
  .. ..$ ncv.threads : num 1
  .. ..$ irls.reg    : num 0
  .. ..$ epsilon     : num 1e-07
  .. ..$ maxit       : num 200
  .. ..$ trace       : logi FALSE
  .. ..$ mgcv.tol    : num 1e-07
  .. ..$ mgcv.half   : num 15
  .. ..$ rank.tol    : num 1.49e-08
  .. ..$ nlm         :List of 6
  .. .. ..$ ndigit           : num 7
  .. .. ..$ gradtol          : num 1e-06
  .. .. ..$ stepmax          : num 2
  .. .. ..$ steptol          : num 1e-04
  .. .. ..$ iterlim          : num 200
  .. .. ..$ check.analyticals: logi FALSE
  .. ..$ optim       :List of 1
  .. .. ..$ factr: num 1e+07
  .. ..$ newton      :List of 5
  .. .. ..$ conv.tol: num 1e-06
  .. .. ..$ maxNstep: num 5
  .. .. ..$ maxSstep: num 2
  .. .. ..$ maxHalf : num 30
  .. .. ..$ use.svd : logi FALSE
  .. ..$ idLinksBases: logi TRUE
  .. ..$ scalePenalty: logi TRUE
  .. ..$ efs.lspmax  : num 15
  .. ..$ efs.tol     : num 0.1
  .. ..$ keepData    : logi FALSE
  .. ..$ scale.est   : chr "fletcher"
  .. ..$ edge.correct: logi FALSE
  ..$ converged        : logi TRUE
  ..$ data             : logi NA
  ..$ df.null          : int 8787
  ..$ df.residual      : num 8755
  ..$ family           :List of 13
  .. ..$ family    : chr "poisson"
  .. ..$ link      : chr "log"
  .. ..$ linkfun   :function (mu)  
  .. ..$ linkinv   :function (eta)  
  .. ..$ variance  :function (mu)  
  .. ..$ dev.resids:function (y, mu, wt)  
  .. ..$ aic       :function (y, n, mu, wt, dev)  
  .. ..$ mu.eta    :function (eta)  
  .. ..$ initialize:  expression({  if (any(y < 0))  stop("negative values not allowed for the 'Poisson' family")  n <- rep.int(1, nobs| __truncated__
  .. ..$ validmu   :function (mu)  
  .. ..$ valideta  :function (eta)  
  .. ..$ simulate  :function (object, nsim)  
  .. ..$ dispersion: num 1
  .. ..- attr(*, "class")= chr "family"
  ..$ formula          :Class 'formula'  language contacts ~ s(gam_age_offdiag) + s(gam_age_offdiag_2) + s(gam_age_diag_prod) +      s(gam_age_diag_sum) + s(gam_ag| __truncated__ ...
  .. .. ..- attr(*, ".Environment")=<environment: R_GlobalEnv> 
  ..$ method           : chr "fREML"
  ..$ min.edf          : num 9
  ..$ model            :'data.frame':   8787 obs. of  11 variables:
  .. ..$ contacts                          : int [1:8787] 0 0 0 0 0 0 0 0 0 0 ...
  .. ..$ school_probability                : num [1:8787] 0 0 0 0 0 0 0 0 0 0 ...
  .. ..$ work_probability                  : num [1:8787] 0 0 0 0 0 0 0 0 0 0 ...
  .. ..$ offset(log_contactable_population): num [1:8787] -4.67 -4.66 -4.65 -4.63 -4.62 ...
  .. ..$ gam_age_offdiag                   : num [1:8787] 0 1 2 3 4 5 6 7 8 9 ...
  .. ..$ gam_age_offdiag_2                 : num [1:8787] 0 1 4 9 16 25 36 49 64 81 ...
  .. ..$ gam_age_diag_prod                 : num [1:8787] 0 0 0 0 0 0 0 0 0 0 ...
  .. ..$ gam_age_diag_sum                  : num [1:8787] 0 1 2 3 4 5 6 7 8 9 ...
  .. ..$ gam_age_pmax                      : num [1:8787] 0 1 2 3 4 5 6 7 8 9 ...
  .. ..$ gam_age_pmin                      : num [1:8787] 0 0 0 0 0 0 0 0 0 0 ...
  .. ..$ (offset)                          : num [1:8787] 4.52 4.52 4.52 4.52 4.52 ...
  .. ..- attr(*, "terms")=Classes 'terms', 'formula'  language contacts ~ school_probability + work_probability + offset(log_contactable_population) +      gam_age_offdiag + ga| __truncated__ ...
  .. .. .. ..- attr(*, "variables")= language list(contacts, school_probability, work_probability, offset(log_contactable_population),      gam_age_offdiag, ga| __truncated__ ...
  .. .. .. ..- attr(*, "offset")= int 4
  .. .. .. ..- attr(*, "factors")= int [1:10, 1:8] 0 1 0 0 0 0 0 0 0 0 ...
  .. .. .. .. ..- attr(*, "dimnames")=List of 2
  .. .. .. .. .. ..$ : chr [1:10] "contacts" "school_probability" "work_probability" "offset(log_contactable_population)" ...
  .. .. .. .. .. ..$ : chr [1:8] "school_probability" "work_probability" "gam_age_offdiag" "gam_age_offdiag_2" ...
  .. .. .. ..- attr(*, "term.labels")= chr [1:8] "school_probability" "work_probability" "gam_age_offdiag" "gam_age_offdiag_2" ...
  .. .. .. ..- attr(*, "order")= int [1:8] 1 1 1 1 1 1 1 1
  .. .. .. ..- attr(*, "intercept")= int 1
  .. .. .. ..- attr(*, "response")= int 1
  .. .. .. ..- attr(*, ".Environment")=<environment: R_GlobalEnv> 
  .. .. .. ..- attr(*, "predvars")= language list(contacts, school_probability, work_probability, offset(log_contactable_population),      gam_age_offdiag, ga| __truncated__ ...
  .. .. .. ..- attr(*, "dataClasses")= Named chr [1:11] "numeric" "numeric" "numeric" "numeric" ...
  .. .. .. .. ..- attr(*, "names")= chr [1:11] "contacts" "school_probability" "work_probability" "offset(log_contactable_population)" ...
  ..$ nsdf             : int 3
  ..$ offset           : num [1:8787] -0.1527 -0.1387 -0.1246 -0.1105 -0.0964 ...
  ..$ pterms           :Classes 'terms', 'formula'  language contacts ~ school_probability + work_probability + offset(log_contactable_population)
  .. .. ..- attr(*, "variables")= language list(contacts, school_probability, work_probability, offset(log_contactable_population))
  .. .. ..- attr(*, "offset")= int 4
  .. .. ..- attr(*, "factors")= int [1:4, 1:2] 0 1 0 0 0 0 1 0
  .. .. .. ..- attr(*, "dimnames")=List of 2
  .. .. .. .. ..$ : chr [1:4] "contacts" "school_probability" "work_probability" "offset(log_contactable_population)"
  .. .. .. .. ..$ : chr [1:2] "school_probability" "work_probability"
  .. .. ..- attr(*, "term.labels")= chr [1:2] "school_probability" "work_probability"
  .. .. ..- attr(*, "order")= int [1:2] 1 1
  .. .. ..- attr(*, "intercept")= int 1
  .. .. ..- attr(*, "response")= int 1
  .. .. ..- attr(*, ".Environment")=<environment: R_GlobalEnv> 
  .. .. ..- attr(*, "predvars")= language list(contacts, school_probability, work_probability, offset(log_contactable_population))
  .. .. ..- attr(*, "dataClasses")= Named chr [1:5] "numeric" "numeric" "numeric" "numeric" ...
  .. .. .. ..- attr(*, "names")= chr [1:5] "contacts" "school_probability" "work_probability" "offset(log_contactable_population)" ...
  ..$ pred.formula     :Class 'formula'  language ~school_probability + work_probability + log_contactable_population + gam_age_offdiag +      gam_age_offdiag_2 + | __truncated__ ...
  .. .. ..- attr(*, ".Environment")=<environment: R_GlobalEnv> 
  ..$ smooth           :List of 6
  .. ..$ :List of 29
  .. .. ..$ term          : chr "gam_age_offdiag"
  .. .. ..$ bs.dim        : num 10
  .. .. ..$ fixed         : logi FALSE
  .. .. ..$ dim           : int 1
  .. .. ..$ p.order       : num 0
  .. .. ..$ by            : chr "NA"
  .. .. ..$ label         : chr "s(gam_age_offdiag)"
  .. .. ..$ xt            : NULL
  .. .. ..$ id            : NULL
  .. .. ..$ sp            : Named num -1
  .. .. .. ..- attr(*, "names")= chr "s(gam_age_offdiag)"
  .. .. ..$ drop.null     : num 0
  .. .. ..$ S             :List of 1
  .. .. .. ..$ : num [1:9, 1:9] 2.558 -0.769 4.813 -1.005 4.801 ...
  .. .. ..$ UZ            : num [1:103, 1:10] -3.97e-06 -3.66e-06 -3.37e-06 -3.09e-06 -2.83e-06 ...
  .. .. ..$ Xu            : num [1:101, 1] -32 -31 -30 -29 -28 ...
  .. .. ..$ df            : num 9
  .. .. ..$ shift         : num [1(1d)] 32
  .. .. ..$ rank          : num 8
  .. .. ..$ null.space.dim: num 1
  .. .. ..$ plot.me       : logi TRUE
  .. .. ..$ side.constrain: logi TRUE
  .. .. ..$ repara        : logi TRUE
  .. .. ..$ C             : num 0
  .. .. ..$ S.scale       : num 2.27e-05
  .. .. ..$ Cp            : num 0
  .. .. ..$ vn            : chr "gam_age_offdiag"
  .. .. ..$ first.para    : num 4
  .. .. ..$ last.para     : num 12
  .. .. ..$ first.sp      : num 1
  .. .. ..$ last.sp       : num 1
  .. .. ..- attr(*, "class")= chr [1:2] "tprs.smooth" "mgcv.smooth"
  .. .. ..- attr(*, "qrc")= 'sweepDrop' num [1:10] 9 -0.938 -0.423 -0.743 -0.413 ...
  .. .. ..- attr(*, "nCons")= num 1
  .. ..$ :List of 29
  .. .. ..$ term          : chr "gam_age_offdiag_2"
  .. .. ..$ bs.dim        : num 10
  .. .. ..$ fixed         : logi FALSE
  .. .. ..$ dim           : int 1
  .. .. ..$ p.order       : num 0
  .. .. ..$ by            : chr "NA"
  .. .. ..$ label         : chr "s(gam_age_offdiag_2)"
  .. .. ..$ xt            : NULL
  .. .. ..$ id            : NULL
  .. .. ..$ sp            : Named num -1
  .. .. .. ..- attr(*, "names")= chr "s(gam_age_offdiag_2)"
  .. .. ..$ drop.null     : num 0
  .. .. ..$ S             :List of 1
  .. .. .. ..$ : num [1:9, 1:9] 1.75 -1.68 -3.12 -2.03 3.03 ...
  .. .. ..$ UZ            : num [1:103, 1:10] 1.54e-12 1.54e-12 1.54e-12 1.53e-12 1.51e-12 ...
  .. .. ..$ Xu            : num [1:101, 1] -1533 -1532 -1529 -1524 -1517 ...
  .. .. ..$ df            : num 9
  .. .. ..$ shift         : num [1(1d)] 1533
  .. .. ..$ rank          : num 8
  .. .. ..$ null.space.dim: num 1
  .. .. ..$ plot.me       : logi TRUE
  .. .. ..$ side.constrain: logi TRUE
  .. .. ..$ repara        : logi TRUE
  .. .. ..$ C             : num 0
  .. .. ..$ S.scale       : num 2.23e-11
  .. .. ..$ Cp            : num 0
  .. .. ..$ vn            : chr "gam_age_offdiag_2"
  .. .. ..$ first.para    : num 13
  .. .. ..$ last.para     : num 21
  .. .. ..$ first.sp      : num 2
  .. .. ..$ last.sp       : num 2
  .. .. ..- attr(*, "class")= chr [1:2] "tprs.smooth" "mgcv.smooth"
  .. .. ..- attr(*, "qrc")= 'sweepDrop' num [1:10] 9 0.961 0.691 -0.87 0.676 ...
  .. .. ..- attr(*, "nCons")= num 1
  .. ..$ :List of 29
  .. .. ..$ term          : chr "gam_age_diag_prod"
  .. .. ..$ bs.dim        : num 10
  .. .. ..$ fixed         : logi FALSE
  .. .. ..$ dim           : int 1
  .. .. ..$ p.order       : num 0
  .. .. ..$ by            : chr "NA"
  .. .. ..$ label         : chr "s(gam_age_diag_prod)"
  .. .. ..$ xt            : NULL
  .. .. ..$ id            : NULL
  .. .. ..$ sp            : Named num -1
  .. .. .. ..- attr(*, "names")= chr "s(gam_age_diag_prod)"
  .. .. ..$ drop.null     : num 0
  .. .. ..$ S             :List of 1
  .. .. .. ..$ : num [1:9, 1:9] 4.08 3.48 -8.2 -5.06 -7.78 ...
  .. .. ..$ UZ            : num [1:2002, 1:10] -2.38e-13 -2.37e-13 -2.37e-13 -2.36e-13 -2.36e-13 ...
  .. .. ..$ Xu            : num [1:2000, 1] -2152 -2150 -2149 -2146 -2145 ...
  .. .. ..$ df            : num 9
  .. .. ..$ shift         : num [1(1d)] 2152
  .. .. ..$ rank          : num 8
  .. .. ..$ null.space.dim: num 1
  .. .. ..$ plot.me       : logi TRUE
  .. .. ..$ side.constrain: logi TRUE
  .. .. ..$ repara        : logi TRUE
  .. .. ..$ C             : num 0
  .. .. ..$ S.scale       : num 2.26e-11
  .. .. ..$ Cp            : num 0
  .. .. ..$ vn            : chr "gam_age_diag_prod"
  .. .. ..$ first.para    : num 22
  .. .. ..$ last.para     : num 30
  .. .. ..$ first.sp      : num 3
  .. .. ..$ last.sp       : num 3
  .. .. ..- attr(*, "class")= chr [1:2] "tprs.smooth" "mgcv.smooth"
  .. .. ..- attr(*, "qrc")= 'sweepDrop' num [1:10] 9 -0.947 0.262 0.763 -0.162 ...
  .. .. ..- attr(*, "nCons")= num 1
  .. ..$ :List of 29
  .. .. ..$ term          : chr "gam_age_diag_sum"
  .. .. ..$ bs.dim        : num 10
  .. .. ..$ fixed         : logi FALSE
  .. .. ..$ dim           : int 1
  .. .. ..$ p.order       : num 0
  .. .. ..$ by            : chr "NA"
  .. .. ..$ label         : chr "s(gam_age_diag_sum)"
  .. .. ..$ xt            : NULL
  .. .. ..$ id            : NULL
  .. .. ..$ sp            : Named num -1
  .. .. .. ..- attr(*, "names")= chr "s(gam_age_diag_sum)"
  .. .. ..$ drop.null     : num 0
  .. .. ..$ S             :List of 1
  .. .. .. ..$ : num [1:9, 1:9] 4.97 1.09 11.11 -1.66 -11.93 ...
  .. .. ..$ UZ            : num [1:193, 1:10] 4.62e-07 4.42e-07 4.23e-07 4.04e-07 3.85e-07 ...
  .. .. ..$ Xu            : num [1:191, 1] -93 -92 -91 -90 -89 ...
  .. .. ..$ df            : num 9
  .. .. ..$ shift         : num [1(1d)] 93
  .. .. ..$ rank          : num 8
  .. .. ..$ null.space.dim: num 1
  .. .. ..$ plot.me       : logi TRUE
  .. .. ..$ side.constrain: logi TRUE
  .. .. ..$ repara        : logi TRUE
  .. .. ..$ C             : num 0
  .. .. ..$ S.scale       : num 2.88e-06
  .. .. ..$ Cp            : num 0
  .. .. ..$ vn            : chr "gam_age_diag_sum"
  .. .. ..$ first.para    : num 31
  .. .. ..$ last.para     : num 39
  .. .. ..$ first.sp      : num 4
  .. .. ..$ last.sp       : num 4
  .. .. ..- attr(*, "class")= chr [1:2] "tprs.smooth" "mgcv.smooth"
  .. .. ..- attr(*, "qrc")= 'sweepDrop' num [1:10] 9 0.951 0.292 0.558 -0.351 ...
  .. .. ..- attr(*, "nCons")= num 1
  .. ..$ :List of 29
  .. .. ..$ term          : chr "gam_age_pmax"
  .. .. ..$ bs.dim        : num 10
  .. .. ..$ fixed         : logi FALSE
  .. .. ..$ dim           : int 1
  .. .. ..$ p.order       : num 0
  .. .. ..$ by            : chr "NA"
  .. .. ..$ label         : chr "s(gam_age_pmax)"
  .. .. ..$ xt            : NULL
  .. .. ..$ id            : NULL
  .. .. ..$ sp            : Named num -1
  .. .. .. ..- attr(*, "names")= chr "s(gam_age_pmax)"
  .. .. ..$ drop.null     : num 0
  .. .. ..$ S             :List of 1
  .. .. .. ..$ : num [1:9, 1:9] 4.151 -0.822 7.91 -1.089 8.153 ...
  .. .. ..$ UZ            : num [1:103, 1:10] -4.38e-06 -4.04e-06 -3.72e-06 -3.41e-06 -3.12e-06 ...
  .. .. ..$ Xu            : num [1:101, 1] -62.5 -61.5 -60.5 -59.5 -58.5 ...
  .. .. ..$ df            : num 9
  .. .. ..$ shift         : num [1(1d)] 62.5
  .. .. ..$ rank          : num 8
  .. .. ..$ null.space.dim: num 1
  .. .. ..$ plot.me       : logi TRUE
  .. .. ..$ side.constrain: logi TRUE
  .. .. ..$ repara        : logi TRUE
  .. .. ..$ C             : num 0
  .. .. ..$ S.scale       : num 1.7e-05
  .. .. ..$ Cp            : num 0
  .. .. ..$ vn            : chr "gam_age_pmax"
  .. .. ..$ first.para    : num 40
  .. .. ..$ last.para     : num 48
  .. .. ..$ first.sp      : num 5
  .. .. ..$ last.sp       : num 5
  .. .. ..- attr(*, "class")= chr [1:2] "tprs.smooth" "mgcv.smooth"
  .. .. ..- attr(*, "qrc")= 'sweepDrop' num [1:10] 9 -0.943 0.625 -0.705 0.621 ...
  .. .. ..- attr(*, "nCons")= num 1
  .. ..$ :List of 29
  .. .. ..$ term          : chr "gam_age_pmin"
  .. .. ..$ bs.dim        : num 10
  .. .. ..$ fixed         : logi FALSE
  .. .. ..$ dim           : int 1
  .. .. ..$ p.order       : num 0
  .. .. ..$ by            : chr "NA"
  .. .. ..$ label         : chr "s(gam_age_pmin)"
  .. .. ..$ xt            : NULL
  .. .. ..$ id            : NULL
  .. .. ..$ sp            : Named num -1
  .. .. .. ..- attr(*, "names")= chr "s(gam_age_pmin)"
  .. .. ..$ drop.null     : num 0
  .. .. ..$ S             :List of 1
  .. .. .. ..$ : num [1:9, 1:9] 3.642 0.732 6.581 -0.913 6.554 ...
  .. .. ..$ UZ            : num [1:93, 1:10] -6.87e-06 -6.26e-06 -5.67e-06 -5.11e-06 -4.59e-06 ...
  .. .. ..$ Xu            : num [1:91, 1] -30.5 -29.5 -28.5 -27.5 -26.5 ...
  .. .. ..$ df            : num 9
  .. .. ..$ shift         : num [1(1d)] 30.5
  .. .. ..$ rank          : num 8
  .. .. ..$ null.space.dim: num 1
  .. .. ..$ plot.me       : logi TRUE
  .. .. ..$ side.constrain: logi TRUE
  .. .. ..$ repara        : logi TRUE
  .. .. ..$ C             : num 0
  .. .. ..$ S.scale       : num 2.24e-05
  .. .. ..$ Cp            : num 0
  .. .. ..$ vn            : chr "gam_age_pmin"
  .. .. ..$ first.para    : num 49
  .. .. ..$ last.para     : num 57
  .. .. ..$ first.sp      : num 6
  .. .. ..$ last.sp       : num 6
  .. .. ..- attr(*, "class")= chr [1:2] "tprs.smooth" "mgcv.smooth"
  .. .. ..- attr(*, "qrc")= 'sweepDrop' num [1:10] 9 -0.939 -0.671 -0.745 0.669 ...
  .. .. ..- attr(*, "nCons")= num 1
  ..$ terms            :Classes 'terms', 'formula'  language contacts ~ school_probability + work_probability + offset(log_contactable_population) +      gam_age_offdiag + ga| __truncated__ ...
  .. .. ..- attr(*, "variables")= language list(contacts, school_probability, work_probability, offset(log_contactable_population),      gam_age_offdiag, ga| __truncated__ ...
  .. .. ..- attr(*, "offset")= int 4
  .. .. ..- attr(*, "factors")= int [1:10, 1:8] 0 1 0 0 0 0 0 0 0 0 ...
  .. .. .. ..- attr(*, "dimnames")=List of 2
  .. .. .. .. ..$ : chr [1:10] "contacts" "school_probability" "work_probability" "offset(log_contactable_population)" ...
  .. .. .. .. ..$ : chr [1:8] "school_probability" "work_probability" "gam_age_offdiag" "gam_age_offdiag_2" ...
  .. .. ..- attr(*, "term.labels")= chr [1:8] "school_probability" "work_probability" "gam_age_offdiag" "gam_age_offdiag_2" ...
  .. .. ..- attr(*, "order")= int [1:8] 1 1 1 1 1 1 1 1
  .. .. ..- attr(*, "intercept")= int 1
  .. .. ..- attr(*, "response")= int 1
  .. .. ..- attr(*, ".Environment")=<environment: R_GlobalEnv> 
  .. .. ..- attr(*, "predvars")= language list(contacts, school_probability, work_probability, offset(log_contactable_population),      gam_age_offdiag, ga| __truncated__ ...
  .. .. ..- attr(*, "dataClasses")= Named chr [1:11] "numeric" "numeric" "numeric" "numeric" ...
  .. .. .. ..- attr(*, "names")= chr [1:11] "contacts" "school_probability" "work_probability" "offset(log_contactable_population)" ...
  ..$ var.summary      :List of 9
  .. ..$ school_probability        : num [1:3] 0 0.00147 0.90552
  .. ..$ work_probability          : num [1:3] 0 0.0183 0.58
  .. ..$ log_contactable_population: num [1:3] -9.34 -4.5 -4.16
  .. ..$ gam_age_offdiag           : num [1:3] 0 28 100
  .. ..$ gam_age_offdiag_2         : num [1:3] 0 784 10000
  .. ..$ gam_age_diag_prod         : num [1:3] 0 1596 9000
  .. ..$ gam_age_diag_sum          : num [1:3] 0 93 190
  .. ..$ gam_age_pmax              : num [1:3] 0 66 100
  .. ..$ gam_age_pmin              : num [1:3] 0 27 90
  ..$ weights          : num [1:8787] 0.00929 0.01031 0.01129 0.01219 0.01302 ...
  ..$ xlevels          : Named list()
  ..$ NA.action        :function (object, ...)  
  ..$ linear.predictors: num [1:8787] -4.68 -4.57 -4.48 -4.41 -4.34 ...
  ..$ fitted.values    : num [1:8787] 0.00929 0.01031 0.01129 0.01219 0.01302 ...
  ..$ residuals        : num [1:8787] -0.136 -0.144 -0.15 -0.156 -0.161 ...
  ..$ deviance         : num 13263
  ..$ aic              : num 23512
  ..$ null.deviance    : num 48608
  ..- attr(*, "class")= chr [1:4] "bam" "gam" "glm" "lm"
 $ school:List of 54
  ..$ coefficients     : Named num [1:57] -7.622 -1.818 -0.255 -56.568 11.422 ...
  .. ..- attr(*, "names")= chr [1:57] "(Intercept)" "school_probability" "work_probability" "s(gam_age_offdiag).1" ...
  ..$ db.drho          : num [1:55, 1:6] 0.0468 0.0125 0.0408 -3.5456 -3.6435 ...
  ..$ gcv.ubre         : Named num 23077
  .. ..- attr(*, "names")= chr "fREML"
  ..$ mgcv.conv        :List of 2
  .. ..$ iter   : int 16
  .. ..$ message: chr "full convergence"
  ..$ rank             : int 55
  ..$ Ve               : num [1:57, 1:57] 1.28177 0.00594 0.01103 0.32637 0.04817 ...
  ..$ scale.estimated  : logi FALSE
  ..$ outer.info       :List of 4
  .. ..$ conv: chr "full convergence"
  .. ..$ iter: int 16
  .. ..$ grad: num [1:6] -1.24e-04 -9.10e-05 -5.40e-05 -5.08e-05 -3.23e-05 ...
  .. ..$ hess: num [1:6, 1:6] 3.014643 0.000124 0.15623 0.159387 0.092433 ...
  ..$ optimizer        : chr [1:2] "perf" "newton"
  ..$ scale            : num 1
  ..$ sig2             : num 1
  ..$ sp               : Named num [1:6] 1.10e-03 2.36e+01 6.19e-05 6.77e-06 7.52e-05 ...
  .. ..- attr(*, "names")= chr [1:6] "s(gam_age_offdiag)" "s(gam_age_offdiag_2)" "s(gam_age_diag_prod)" "s(gam_age_diag_sum)" ...
  ..$ edf              : Named num [1:57] 1 1 1 0.751 0.917 ...
  .. ..- attr(*, "names")= chr [1:57] "(Intercept)" "school_probability" "work_probability" "s(gam_age_offdiag).1" ...
  ..$ edf1             : num [1:57] 1 1 1 0.896 0.961 ...
  ..$ edf2             : num [1:57] 1 1 1 0.896 0.961 ...
  ..$ hat              : num [1:57] 1 1 1 1 1 ...
  ..$ Vp               : num [1:57, 1:57] 2.10274 0.00884 0.01803 0.50174 0.07166 ...
  ..$ Vc               : num [1:57, 1:57] 3.6672 0.0187 0.0399 -1.8361 -0.8078 ...
  ..$ V.sp             : num [1:6, 1:6] 0.3363 -0.4086 -0.04 -0.0356 -0.0176 ...
  ..$ R                : num [1:57, 1:57] -133.14 30.083 -25.536 -0.245 23.378 ...
  .. ..- attr(*, "dimnames")=List of 2
  .. .. ..$ : NULL
  .. .. ..$ : NULL
  ..$ iter             : int 19
  ..$ wt               : num [1:8787] 12.19 10.42 8.21 6.04 4.2 ...
  ..$ y                : int [1:8787] 13 4 2 1 1 3 0 0 0 0 ...
  ..$ prior.weights    : num [1:8787] 1 1 1 1 1 1 1 1 1 1 ...
  ..$ assign           : int [1:3] 0 1 2
  ..$ boundary         : logi FALSE
  ..$ call             : language mgcv::bam(formula = formula, family = stats::poisson, data = ., offset = log(participants))
  ..$ cmX              : Named num [1:57] 1.00 3.86e-02 1.42e-01 -2.69e-14 -5.18e-15 ...
  .. ..- attr(*, "names")= chr [1:57] "(Intercept)" "school_probability" "work_probability" "" ...
  ..$ control          :List of 19
  .. ..$ nthreads    : num 1
  .. ..$ ncv.threads : num 1
  .. ..$ irls.reg    : num 0
  .. ..$ epsilon     : num 1e-07
  .. ..$ maxit       : num 200
  .. ..$ trace       : logi FALSE
  .. ..$ mgcv.tol    : num 1e-07
  .. ..$ mgcv.half   : num 15
  .. ..$ rank.tol    : num 1.49e-08
  .. ..$ nlm         :List of 6
  .. .. ..$ ndigit           : num 7
  .. .. ..$ gradtol          : num 1e-06
  .. .. ..$ stepmax          : num 2
  .. .. ..$ steptol          : num 1e-04
  .. .. ..$ iterlim          : num 200
  .. .. ..$ check.analyticals: logi FALSE
  .. ..$ optim       :List of 1
  .. .. ..$ factr: num 1e+07
  .. ..$ newton      :List of 5
  .. .. ..$ conv.tol: num 1e-06
  .. .. ..$ maxNstep: num 5
  .. .. ..$ maxSstep: num 2
  .. .. ..$ maxHalf : num 30
  .. .. ..$ use.svd : logi FALSE
  .. ..$ idLinksBases: logi TRUE
  .. ..$ scalePenalty: logi TRUE
  .. ..$ efs.lspmax  : num 15
  .. ..$ efs.tol     : num 0.1
  .. ..$ keepData    : logi FALSE
  .. ..$ scale.est   : chr "fletcher"
  .. ..$ edge.correct: logi FALSE
  ..$ converged        : logi TRUE
  ..$ data             : logi NA
  ..$ df.null          : int 8787
  ..$ df.residual      : num 8744
  ..$ family           :List of 13
  .. ..$ family    : chr "poisson"
  .. ..$ link      : chr "log"
  .. ..$ linkfun   :function (mu)  
  .. ..$ linkinv   :function (eta)  
  .. ..$ variance  :function (mu)  
  .. ..$ dev.resids:function (y, mu, wt)  
  .. ..$ aic       :function (y, n, mu, wt, dev)  
  .. ..$ mu.eta    :function (eta)  
  .. ..$ initialize:  expression({  if (any(y < 0))  stop("negative values not allowed for the 'Poisson' family")  n <- rep.int(1, nobs| __truncated__
  .. ..$ validmu   :function (mu)  
  .. ..$ valideta  :function (eta)  
  .. ..$ simulate  :function (object, nsim)  
  .. ..$ dispersion: num 1
  .. ..- attr(*, "class")= chr "family"
  ..$ formula          :Class 'formula'  language contacts ~ s(gam_age_offdiag) + s(gam_age_offdiag_2) + s(gam_age_diag_prod) +      s(gam_age_diag_sum) + s(gam_ag| __truncated__ ...
  .. .. ..- attr(*, ".Environment")=<environment: R_GlobalEnv> 
  ..$ method           : chr "fREML"
  ..$ min.edf          : num 9
  ..$ model            :'data.frame':   8787 obs. of  11 variables:
  .. ..$ contacts                                 : int [1:8787] 13 4 2 1 1 3 0 0 0 0 ...
  .. ..$ school_probability                       : num [1:8787] 0 0 0 0 0 0 0 0 0 0 ...
  .. ..$ work_probability                         : num [1:8787] 0 0 0 0 0 0 0 0 0 0 ...
  .. ..$ offset(log_contactable_population_school): num [1:8787] -4.67 -4.66 -4.65 -4.63 -4.62 ...
  .. ..$ gam_age_offdiag                          : num [1:8787] 0 1 2 3 4 5 6 7 8 9 ...
  .. ..$ gam_age_offdiag_2                        : num [1:8787] 0 1 4 9 16 25 36 49 64 81 ...
  .. ..$ gam_age_diag_prod                        : num [1:8787] 0 0 0 0 0 0 0 0 0 0 ...
  .. ..$ gam_age_diag_sum                         : num [1:8787] 0 1 2 3 4 5 6 7 8 9 ...
  .. ..$ gam_age_pmax                             : num [1:8787] 0 1 2 3 4 5 6 7 8 9 ...
  .. ..$ gam_age_pmin                             : num [1:8787] 0 0 0 0 0 0 0 0 0 0 ...
  .. ..$ (offset)                                 : num [1:8787] 4.52 4.52 4.52 4.52 4.52 ...
  .. ..- attr(*, "terms")=Classes 'terms', 'formula'  language contacts ~ school_probability + work_probability + offset(log_contactable_population_school) +      gam_age_offdi| __truncated__ ...
  .. .. .. ..- attr(*, "variables")= language list(contacts, school_probability, work_probability, offset(log_contactable_population_school),      gam_age_offd| __truncated__ ...
  .. .. .. ..- attr(*, "offset")= int 4
  .. .. .. ..- attr(*, "factors")= int [1:10, 1:8] 0 1 0 0 0 0 0 0 0 0 ...
  .. .. .. .. ..- attr(*, "dimnames")=List of 2
  .. .. .. .. .. ..$ : chr [1:10] "contacts" "school_probability" "work_probability" "offset(log_contactable_population_school)" ...
  .. .. .. .. .. ..$ : chr [1:8] "school_probability" "work_probability" "gam_age_offdiag" "gam_age_offdiag_2" ...
  .. .. .. ..- attr(*, "term.labels")= chr [1:8] "school_probability" "work_probability" "gam_age_offdiag" "gam_age_offdiag_2" ...
  .. .. .. ..- attr(*, "order")= int [1:8] 1 1 1 1 1 1 1 1
  .. .. .. ..- attr(*, "intercept")= int 1
  .. .. .. ..- attr(*, "response")= int 1
  .. .. .. ..- attr(*, ".Environment")=<environment: R_GlobalEnv> 
  .. .. .. ..- attr(*, "predvars")= language list(contacts, school_probability, work_probability, offset(log_contactable_population_school),      gam_age_offd| __truncated__ ...
  .. .. .. ..- attr(*, "dataClasses")= Named chr [1:11] "numeric" "numeric" "numeric" "numeric" ...
  .. .. .. .. ..- attr(*, "names")= chr [1:11] "contacts" "school_probability" "work_probability" "offset(log_contactable_population_school)" ...
  ..$ nsdf             : int 3
  ..$ offset           : num [1:8787] -0.1527 -0.1387 -0.1246 -0.1105 -0.0964 ...
  ..$ pterms           :Classes 'terms', 'formula'  language contacts ~ school_probability + work_probability + offset(log_contactable_population_school)
  .. .. ..- attr(*, "variables")= language list(contacts, school_probability, work_probability, offset(log_contactable_population_school))
  .. .. ..- attr(*, "offset")= int 4
  .. .. ..- attr(*, "factors")= int [1:4, 1:2] 0 1 0 0 0 0 1 0
  .. .. .. ..- attr(*, "dimnames")=List of 2
  .. .. .. .. ..$ : chr [1:4] "contacts" "school_probability" "work_probability" "offset(log_contactable_population_school)"
  .. .. .. .. ..$ : chr [1:2] "school_probability" "work_probability"
  .. .. ..- attr(*, "term.labels")= chr [1:2] "school_probability" "work_probability"
  .. .. ..- attr(*, "order")= int [1:2] 1 1
  .. .. ..- attr(*, "intercept")= int 1
  .. .. ..- attr(*, "response")= int 1
  .. .. ..- attr(*, ".Environment")=<environment: R_GlobalEnv> 
  .. .. ..- attr(*, "predvars")= language list(contacts, school_probability, work_probability, offset(log_contactable_population_school))
  .. .. ..- attr(*, "dataClasses")= Named chr [1:5] "numeric" "numeric" "numeric" "numeric" ...
  .. .. .. ..- attr(*, "names")= chr [1:5] "contacts" "school_probability" "work_probability" "offset(log_contactable_population_school)" ...
  ..$ pred.formula     :Class 'formula'  language ~school_probability + work_probability + log_contactable_population_school +      gam_age_offdiag + gam_age_offdi| __truncated__ ...
  .. .. ..- attr(*, ".Environment")=<environment: R_GlobalEnv> 
  ..$ smooth           :List of 6
  .. ..$ :List of 29
  .. .. ..$ term          : chr "gam_age_offdiag"
  .. .. ..$ bs.dim        : num 10
  .. .. ..$ fixed         : logi FALSE
  .. .. ..$ dim           : int 1
  .. .. ..$ p.order       : num 0
  .. .. ..$ by            : chr "NA"
  .. .. ..$ label         : chr "s(gam_age_offdiag)"
  .. .. ..$ xt            : NULL
  .. .. ..$ id            : NULL
  .. .. ..$ sp            : Named num -1
  .. .. .. ..- attr(*, "names")= chr "s(gam_age_offdiag)"
  .. .. ..$ drop.null     : num 0
  .. .. ..$ S             :List of 1
  .. .. .. ..$ : num [1:9, 1:9] 2.558 -0.769 4.813 -1.005 4.801 ...
  .. .. ..$ UZ            : num [1:103, 1:10] -3.97e-06 -3.66e-06 -3.37e-06 -3.09e-06 -2.83e-06 ...
  .. .. ..$ Xu            : num [1:101, 1] -32 -31 -30 -29 -28 ...
  .. .. ..$ df            : num 9
  .. .. ..$ shift         : num [1(1d)] 32
  .. .. ..$ rank          : num 8
  .. .. ..$ null.space.dim: num 1
  .. .. ..$ plot.me       : logi TRUE
  .. .. ..$ side.constrain: logi TRUE
  .. .. ..$ repara        : logi TRUE
  .. .. ..$ C             : num 0
  .. .. ..$ S.scale       : num 2.27e-05
  .. .. ..$ Cp            : num 0
  .. .. ..$ vn            : chr "gam_age_offdiag"
  .. .. ..$ first.para    : num 4
  .. .. ..$ last.para     : num 12
  .. .. ..$ first.sp      : num 1
  .. .. ..$ last.sp       : num 1
  .. .. ..- attr(*, "class")= chr [1:2] "tprs.smooth" "mgcv.smooth"
  .. .. ..- attr(*, "qrc")= 'sweepDrop' num [1:10] 9 -0.938 -0.423 -0.743 -0.413 ...
  .. .. ..- attr(*, "nCons")= num 1
  .. ..$ :List of 29
  .. .. ..$ term          : chr "gam_age_offdiag_2"
  .. .. ..$ bs.dim        : num 10
  .. .. ..$ fixed         : logi FALSE
  .. .. ..$ dim           : int 1
  .. .. ..$ p.order       : num 0
  .. .. ..$ by            : chr "NA"
  .. .. ..$ label         : chr "s(gam_age_offdiag_2)"
  .. .. ..$ xt            : NULL
  .. .. ..$ id            : NULL
  .. .. ..$ sp            : Named num -1
  .. .. .. ..- attr(*, "names")= chr "s(gam_age_offdiag_2)"
  .. .. ..$ drop.null     : num 0
  .. .. ..$ S             :List of 1
  .. .. .. ..$ : num [1:9, 1:9] 1.75 -1.68 -3.12 -2.03 3.03 ...
  .. .. ..$ UZ            : num [1:103, 1:10] 1.54e-12 1.54e-12 1.54e-12 1.53e-12 1.51e-12 ...
  .. .. ..$ Xu            : num [1:101, 1] -1533 -1532 -1529 -1524 -1517 ...
  .. .. ..$ df            : num 9
  .. .. ..$ shift         : num [1(1d)] 1533
  .. .. ..$ rank          : num 8
  .. .. ..$ null.space.dim: num 1
  .. .. ..$ plot.me       : logi TRUE
  .. .. ..$ side.constrain: logi TRUE
  .. .. ..$ repara        : logi TRUE
  .. .. ..$ C             : num 0
  .. .. ..$ S.scale       : num 2.23e-11
  .. .. ..$ Cp            : num 0
  .. .. ..$ vn            : chr "gam_age_offdiag_2"
  .. .. ..$ first.para    : num 13
  .. .. ..$ last.para     : num 21
  .. .. ..$ first.sp      : num 2
  .. .. ..$ last.sp       : num 2
  .. .. ..- attr(*, "class")= chr [1:2] "tprs.smooth" "mgcv.smooth"
  .. .. ..- attr(*, "qrc")= 'sweepDrop' num [1:10] 9 0.961 0.691 -0.87 0.676 ...
  .. .. ..- attr(*, "nCons")= num 1
  .. ..$ :List of 29
  .. .. ..$ term          : chr "gam_age_diag_prod"
  .. .. ..$ bs.dim        : num 10
  .. .. ..$ fixed         : logi FALSE
  .. .. ..$ dim           : int 1
  .. .. ..$ p.order       : num 0
  .. .. ..$ by            : chr "NA"
  .. .. ..$ label         : chr "s(gam_age_diag_prod)"
  .. .. ..$ xt            : NULL
  .. .. ..$ id            : NULL
  .. .. ..$ sp            : Named num -1
  .. .. .. ..- attr(*, "names")= chr "s(gam_age_diag_prod)"
  .. .. ..$ drop.null     : num 0
  .. .. ..$ S             :List of 1
  .. .. .. ..$ : num [1:9, 1:9] 4.08 3.48 -8.2 -5.06 -7.78 ...
  .. .. ..$ UZ            : num [1:2002, 1:10] -2.38e-13 -2.37e-13 -2.37e-13 -2.36e-13 -2.36e-13 ...
  .. .. ..$ Xu            : num [1:2000, 1] -2152 -2150 -2149 -2146 -2145 ...
  .. .. ..$ df            : num 9
  .. .. ..$ shift         : num [1(1d)] 2152
  .. .. ..$ rank          : num 8
  .. .. ..$ null.space.dim: num 1
  .. .. ..$ plot.me       : logi TRUE
  .. .. ..$ side.constrain: logi TRUE
  .. .. ..$ repara        : logi TRUE
  .. .. ..$ C             : num 0
  .. .. ..$ S.scale       : num 2.26e-11
  .. .. ..$ Cp            : num 0
  .. .. ..$ vn            : chr "gam_age_diag_prod"
  .. .. ..$ first.para    : num 22
  .. .. ..$ last.para     : num 30
  .. .. ..$ first.sp      : num 3
  .. .. ..$ last.sp       : num 3
  .. .. ..- attr(*, "class")= chr [1:2] "tprs.smooth" "mgcv.smooth"
  .. .. ..- attr(*, "qrc")= 'sweepDrop' num [1:10] 9 -0.947 0.262 0.763 -0.162 ...
  .. .. ..- attr(*, "nCons")= num 1
  .. ..$ :List of 29
  .. .. ..$ term          : chr "gam_age_diag_sum"
  .. .. ..$ bs.dim        : num 10
  .. .. ..$ fixed         : logi FALSE
  .. .. ..$ dim           : int 1
  .. .. ..$ p.order       : num 0
  .. .. ..$ by            : chr "NA"
  .. .. ..$ label         : chr "s(gam_age_diag_sum)"
  .. .. ..$ xt            : NULL
  .. .. ..$ id            : NULL
  .. .. ..$ sp            : Named num -1
  .. .. .. ..- attr(*, "names")= chr "s(gam_age_diag_sum)"
  .. .. ..$ drop.null     : num 0
  .. .. ..$ S             :List of 1
  .. .. .. ..$ : num [1:9, 1:9] 4.97 1.09 11.11 -1.66 -11.93 ...
  .. .. ..$ UZ            : num [1:193, 1:10] 4.62e-07 4.42e-07 4.23e-07 4.04e-07 3.85e-07 ...
  .. .. ..$ Xu            : num [1:191, 1] -93 -92 -91 -90 -89 ...
  .. .. ..$ df            : num 9
  .. .. ..$ shift         : num [1(1d)] 93
  .. .. ..$ rank          : num 8
  .. .. ..$ null.space.dim: num 1
  .. .. ..$ plot.me       : logi TRUE
  .. .. ..$ side.constrain: logi TRUE
  .. .. ..$ repara        : logi TRUE
  .. .. ..$ C             : num 0
  .. .. ..$ S.scale       : num 2.88e-06
  .. .. ..$ Cp            : num 0
  .. .. ..$ vn            : chr "gam_age_diag_sum"
  .. .. ..$ first.para    : num 31
  .. .. ..$ last.para     : num 39
  .. .. ..$ first.sp      : num 4
  .. .. ..$ last.sp       : num 4
  .. .. ..- attr(*, "class")= chr [1:2] "tprs.smooth" "mgcv.smooth"
  .. .. ..- attr(*, "qrc")= 'sweepDrop' num [1:10] 9 0.951 0.292 0.558 -0.351 ...
  .. .. ..- attr(*, "nCons")= num 1
  .. ..$ :List of 29
  .. .. ..$ term          : chr "gam_age_pmax"
  .. .. ..$ bs.dim        : num 10
  .. .. ..$ fixed         : logi FALSE
  .. .. ..$ dim           : int 1
  .. .. ..$ p.order       : num 0
  .. .. ..$ by            : chr "NA"
  .. .. ..$ label         : chr "s(gam_age_pmax)"
  .. .. ..$ xt            : NULL
  .. .. ..$ id            : NULL
  .. .. ..$ sp            : Named num -1
  .. .. .. ..- attr(*, "names")= chr "s(gam_age_pmax)"
  .. .. ..$ drop.null     : num 0
  .. .. ..$ S             :List of 1
  .. .. .. ..$ : num [1:9, 1:9] 4.151 -0.822 7.91 -1.089 8.153 ...
  .. .. ..$ UZ            : num [1:103, 1:10] -4.38e-06 -4.04e-06 -3.72e-06 -3.41e-06 -3.12e-06 ...
  .. .. ..$ Xu            : num [1:101, 1] -62.5 -61.5 -60.5 -59.5 -58.5 ...
  .. .. ..$ df            : num 9
  .. .. ..$ shift         : num [1(1d)] 62.5
  .. .. ..$ rank          : num 8
  .. .. ..$ null.space.dim: num 1
  .. .. ..$ plot.me       : logi TRUE
  .. .. ..$ side.constrain: logi TRUE
  .. .. ..$ repara        : logi TRUE
  .. .. ..$ C             : num 0
  .. .. ..$ S.scale       : num 1.7e-05
  .. .. ..$ Cp            : num 0
  .. .. ..$ vn            : chr "gam_age_pmax"
  .. .. ..$ first.para    : num 40
  .. .. ..$ last.para     : num 48
  .. .. ..$ first.sp      : num 5
  .. .. ..$ last.sp       : num 5
  .. .. ..- attr(*, "class")= chr [1:2] "tprs.smooth" "mgcv.smooth"
  .. .. ..- attr(*, "qrc")= 'sweepDrop' num [1:10] 9 -0.943 0.625 -0.705 0.621 ...
  .. .. ..- attr(*, "nCons")= num 1
  .. ..$ :List of 29
  .. .. ..$ term          : chr "gam_age_pmin"
  .. .. ..$ bs.dim        : num 10
  .. .. ..$ fixed         : logi FALSE
  .. .. ..$ dim           : int 1
  .. .. ..$ p.order       : num 0
  .. .. ..$ by            : chr "NA"
  .. .. ..$ label         : chr "s(gam_age_pmin)"
  .. .. ..$ xt            : NULL
  .. .. ..$ id            : NULL
  .. .. ..$ sp            : Named num -1
  .. .. .. ..- attr(*, "names")= chr "s(gam_age_pmin)"
  .. .. ..$ drop.null     : num 0
  .. .. ..$ S             :List of 1
  .. .. .. ..$ : num [1:9, 1:9] 3.642 0.732 6.581 -0.913 6.554 ...
  .. .. ..$ UZ            : num [1:93, 1:10] -6.87e-06 -6.26e-06 -5.67e-06 -5.11e-06 -4.59e-06 ...
  .. .. ..$ Xu            : num [1:91, 1] -30.5 -29.5 -28.5 -27.5 -26.5 ...
  .. .. ..$ df            : num 9
  .. .. ..$ shift         : num [1(1d)] 30.5
  .. .. ..$ rank          : num 8
  .. .. ..$ null.space.dim: num 1
  .. .. ..$ plot.me       : logi TRUE
  .. .. ..$ side.constrain: logi TRUE
  .. .. ..$ repara        : logi TRUE
  .. .. ..$ C             : num 0
  .. .. ..$ S.scale       : num 2.24e-05
  .. .. ..$ Cp            : num 0
  .. .. ..$ vn            : chr "gam_age_pmin"
  .. .. ..$ first.para    : num 49
  .. .. ..$ last.para     : num 57
  .. .. ..$ first.sp      : num 6
  .. .. ..$ last.sp       : num 6
  .. .. ..- attr(*, "class")= chr [1:2] "tprs.smooth" "mgcv.smooth"
  .. .. ..- attr(*, "qrc")= 'sweepDrop' num [1:10] 9 -0.939 -0.671 -0.745 0.669 ...
  .. .. ..- attr(*, "nCons")= num 1
  ..$ terms            :Classes 'terms', 'formula'  language contacts ~ school_probability + work_probability + offset(log_contactable_population_school) +      gam_age_offdi| __truncated__ ...
  .. .. ..- attr(*, "variables")= language list(contacts, school_probability, work_probability, offset(log_contactable_population_school),      gam_age_offd| __truncated__ ...
  .. .. ..- attr(*, "offset")= int 4
  .. .. ..- attr(*, "factors")= int [1:10, 1:8] 0 1 0 0 0 0 0 0 0 0 ...
  .. .. .. ..- attr(*, "dimnames")=List of 2
  .. .. .. .. ..$ : chr [1:10] "contacts" "school_probability" "work_probability" "offset(log_contactable_population_school)" ...
  .. .. .. .. ..$ : chr [1:8] "school_probability" "work_probability" "gam_age_offdiag" "gam_age_offdiag_2" ...
  .. .. ..- attr(*, "term.labels")= chr [1:8] "school_probability" "work_probability" "gam_age_offdiag" "gam_age_offdiag_2" ...
  .. .. ..- attr(*, "order")= int [1:8] 1 1 1 1 1 1 1 1
  .. .. ..- attr(*, "intercept")= int 1
  .. .. ..- attr(*, "response")= int 1
  .. .. ..- attr(*, ".Environment")=<environment: R_GlobalEnv> 
  .. .. ..- attr(*, "predvars")= language list(contacts, school_probability, work_probability, offset(log_contactable_population_school),      gam_age_offd| __truncated__ ...
  .. .. ..- attr(*, "dataClasses")= Named chr [1:11] "numeric" "numeric" "numeric" "numeric" ...
  .. .. .. ..- attr(*, "names")= chr [1:11] "contacts" "school_probability" "work_probability" "offset(log_contactable_population_school)" ...
  ..$ var.summary      :List of 9
  .. ..$ school_probability               : num [1:3] 0 0.00147 0.90552
  .. ..$ work_probability                 : num [1:3] 0 0.0183 0.58
  .. ..$ log_contactable_population_school: num [1:3] -9.34 -4.5 -0.78
  .. ..$ gam_age_offdiag                  : num [1:3] 0 28 100
  .. ..$ gam_age_offdiag_2                : num [1:3] 0 784 10000
  .. ..$ gam_age_diag_prod                : num [1:3] 0 1596 9000
  .. ..$ gam_age_diag_sum                 : num [1:3] 0 93 190
  .. ..$ gam_age_pmax                     : num [1:3] 0 66 100
  .. ..$ gam_age_pmin                     : num [1:3] 0 27 90
  ..$ weights          : num [1:8787] 12.19 10.42 8.21 6.04 4.2 ...
  ..$ xlevels          : Named list()
  ..$ NA.action        :function (object, ...)  
  ..$ linear.predictors: num [1:8787] 2.5 2.34 2.11 1.8 1.43 ...
  ..$ fitted.values    : num [1:8787] 12.19 10.42 8.21 6.04 4.2 ...
  ..$ residuals        : num [1:8787] 0.228 -2.277 -2.604 -2.545 -1.877 ...
  ..$ deviance         : num 12770
  ..$ aic              : num 19174
  ..$ null.deviance    : num 133524
  ..- attr(*, "class")= chr [1:4] "bam" "gam" "glm" "lm"
 $ other :List of 54
  ..$ coefficients     : Named num [1:57] 0.926 -0.86 -1.082 40.002 -6.537 ...
  .. ..- attr(*, "names")= chr [1:57] "(Intercept)" "school_probability" "work_probability" "s(gam_age_offdiag).1" ...
  ..$ db.drho          : num [1:55, 1:6] 0.00142 -0.00374 -0.00655 4.02079 0.4178 ...
  ..$ gcv.ubre         : Named num 17677
  .. ..- attr(*, "names")= chr "fREML"
  ..$ mgcv.conv        :List of 2
  .. ..$ iter   : int 12
  .. ..$ message: chr "full convergence"
  ..$ rank             : int 55
  ..$ Ve               : num [1:57, 1:57] 0.003973 -0.000963 -0.004959 -0.024502 -0.001923 ...
  ..$ scale.estimated  : logi FALSE
  ..$ outer.info       :List of 4
  .. ..$ conv: chr "full convergence"
  .. ..$ iter: int 12
  .. ..$ grad: num [1:6] 2.94e-09 3.38e-09 -1.71e-10 -1.84e-10 -9.56e-11 ...
  .. ..$ hess: num [1:6, 1:6] 1.65562 0.13578 -0.00797 -0.01038 -0.00135 ...
  ..$ optimizer        : chr [1:2] "perf" "newton"
  ..$ scale            : num 1
  ..$ sig2             : num 1
  ..$ sp               : Named num [1:6] 1.05e-03 8.71e-04 2.78e-05 4.30e-06 1.03e-04 ...
  .. ..- attr(*, "names")= chr [1:6] "s(gam_age_offdiag)" "s(gam_age_offdiag_2)" "s(gam_age_diag_prod)" "s(gam_age_diag_sum)" ...
  ..$ edf              : Named num [1:57] 1 1 1 0.303 0.525 ...
  .. ..- attr(*, "names")= chr [1:57] "(Intercept)" "school_probability" "work_probability" "s(gam_age_offdiag).1" ...
  ..$ edf1             : num [1:57] 1 1 1 0.312 0.533 ...
  ..$ edf2             : num [1:57] 1 1 1 0.353 0.4 ...
  ..$ hat              : num [1:57] 1 1 1 1 1 ...
  ..$ Vp               : num [1:57, 1:57] 0.004087 -0.000984 -0.004991 -0.033431 -0.001424 ...
  ..$ Vc               : num [1:57, 1:57] 0.0042 -0.00106 -0.00505 0.02959 0.00869 ...
  ..$ V.sp             : num [1:6, 1:6] 0.607825 -0.046479 0.000519 0.00084 -0.000192 ...
  ..$ R                : num [1:57, 1:57] -145.1 87.9 26.7 -92.3 0 ...
  .. ..- attr(*, "dimnames")=List of 2
  .. .. ..$ : NULL
  .. .. ..$ : NULL
  ..$ iter             : int 9
  ..$ wt               : num [1:8787] 7.99 6.36 5.18 4.3 3.62 ...
  ..$ y                : int [1:8787] 7 7 11 11 5 5 4 4 2 3 ...
  ..$ prior.weights    : num [1:8787] 1 1 1 1 1 1 1 1 1 1 ...
  ..$ assign           : int [1:3] 0 1 2
  ..$ boundary         : logi FALSE
  ..$ call             : language mgcv::bam(formula = formula, family = stats::poisson, data = ., offset = log(participants))
  ..$ cmX              : Named num [1:57] 1.00 3.86e-02 1.42e-01 -2.69e-14 -5.18e-15 ...
  .. ..- attr(*, "names")= chr [1:57] "(Intercept)" "school_probability" "work_probability" "" ...
  ..$ control          :List of 19
  .. ..$ nthreads    : num 1
  .. ..$ ncv.threads : num 1
  .. ..$ irls.reg    : num 0
  .. ..$ epsilon     : num 1e-07
  .. ..$ maxit       : num 200
  .. ..$ trace       : logi FALSE
  .. ..$ mgcv.tol    : num 1e-07
  .. ..$ mgcv.half   : num 15
  .. ..$ rank.tol    : num 1.49e-08
  .. ..$ nlm         :List of 6
  .. .. ..$ ndigit           : num 7
  .. .. ..$ gradtol          : num 1e-06
  .. .. ..$ stepmax          : num 2
  .. .. ..$ steptol          : num 1e-04
  .. .. ..$ iterlim          : num 200
  .. .. ..$ check.analyticals: logi FALSE
  .. ..$ optim       :List of 1
  .. .. ..$ factr: num 1e+07
  .. ..$ newton      :List of 5
  .. .. ..$ conv.tol: num 1e-06
  .. .. ..$ maxNstep: num 5
  .. .. ..$ maxSstep: num 2
  .. .. ..$ maxHalf : num 30
  .. .. ..$ use.svd : logi FALSE
  .. ..$ idLinksBases: logi TRUE
  .. ..$ scalePenalty: logi TRUE
  .. ..$ efs.lspmax  : num 15
  .. ..$ efs.tol     : num 0.1
  .. ..$ keepData    : logi FALSE
  .. ..$ scale.est   : chr "fletcher"
  .. ..$ edge.correct: logi FALSE
  ..$ converged        : logi TRUE
  ..$ data             : logi NA
  ..$ df.null          : int 8787
  ..$ df.residual      : num 8738
  ..$ family           :List of 13
  .. ..$ family    : chr "poisson"
  .. ..$ link      : chr "log"
  .. ..$ linkfun   :function (mu)  
  .. ..$ linkinv   :function (eta)  
  .. ..$ variance  :function (mu)  
  .. ..$ dev.resids:function (y, mu, wt)  
  .. ..$ aic       :function (y, n, mu, wt, dev)  
  .. ..$ mu.eta    :function (eta)  
  .. ..$ initialize:  expression({  if (any(y < 0))  stop("negative values not allowed for the 'Poisson' family")  n <- rep.int(1, nobs| __truncated__
  .. ..$ validmu   :function (mu)  
  .. ..$ valideta  :function (eta)  
  .. ..$ simulate  :function (object, nsim)  
  .. ..$ dispersion: num 1
  .. ..- attr(*, "class")= chr "family"
  ..$ formula          :Class 'formula'  language contacts ~ s(gam_age_offdiag) + s(gam_age_offdiag_2) + s(gam_age_diag_prod) +      s(gam_age_diag_sum) + s(gam_ag| __truncated__ ...
  .. .. ..- attr(*, ".Environment")=<environment: R_GlobalEnv> 
  ..$ method           : chr "fREML"
  ..$ min.edf          : num 9
  ..$ model            :'data.frame':   8787 obs. of  11 variables:
  .. ..$ contacts                          : int [1:8787] 7 7 11 11 5 5 4 4 2 3 ...
  .. ..$ school_probability                : num [1:8787] 0 0 0 0 0 0 0 0 0 0 ...
  .. ..$ work_probability                  : num [1:8787] 0 0 0 0 0 0 0 0 0 0 ...
  .. ..$ offset(log_contactable_population): num [1:8787] -4.67 -4.66 -4.65 -4.63 -4.62 ...
  .. ..$ gam_age_offdiag                   : num [1:8787] 0 1 2 3 4 5 6 7 8 9 ...
  .. ..$ gam_age_offdiag_2                 : num [1:8787] 0 1 4 9 16 25 36 49 64 81 ...
  .. ..$ gam_age_diag_prod                 : num [1:8787] 0 0 0 0 0 0 0 0 0 0 ...
  .. ..$ gam_age_diag_sum                  : num [1:8787] 0 1 2 3 4 5 6 7 8 9 ...
  .. ..$ gam_age_pmax                      : num [1:8787] 0 1 2 3 4 5 6 7 8 9 ...
  .. ..$ gam_age_pmin                      : num [1:8787] 0 0 0 0 0 0 0 0 0 0 ...
  .. ..$ (offset)                          : num [1:8787] 4.52 4.52 4.52 4.52 4.52 ...
  .. ..- attr(*, "terms")=Classes 'terms', 'formula'  language contacts ~ school_probability + work_probability + offset(log_contactable_population) +      gam_age_offdiag + ga| __truncated__ ...
  .. .. .. ..- attr(*, "variables")= language list(contacts, school_probability, work_probability, offset(log_contactable_population),      gam_age_offdiag, ga| __truncated__ ...
  .. .. .. ..- attr(*, "offset")= int 4
  .. .. .. ..- attr(*, "factors")= int [1:10, 1:8] 0 1 0 0 0 0 0 0 0 0 ...
  .. .. .. .. ..- attr(*, "dimnames")=List of 2
  .. .. .. .. .. ..$ : chr [1:10] "contacts" "school_probability" "work_probability" "offset(log_contactable_population)" ...
  .. .. .. .. .. ..$ : chr [1:8] "school_probability" "work_probability" "gam_age_offdiag" "gam_age_offdiag_2" ...
  .. .. .. ..- attr(*, "term.labels")= chr [1:8] "school_probability" "work_probability" "gam_age_offdiag" "gam_age_offdiag_2" ...
  .. .. .. ..- attr(*, "order")= int [1:8] 1 1 1 1 1 1 1 1
  .. .. .. ..- attr(*, "intercept")= int 1
  .. .. .. ..- attr(*, "response")= int 1
  .. .. .. ..- attr(*, ".Environment")=<environment: R_GlobalEnv> 
  .. .. .. ..- attr(*, "predvars")= language list(contacts, school_probability, work_probability, offset(log_contactable_population),      gam_age_offdiag, ga| __truncated__ ...
  .. .. .. ..- attr(*, "dataClasses")= Named chr [1:11] "numeric" "numeric" "numeric" "numeric" ...
  .. .. .. .. ..- attr(*, "names")= chr [1:11] "contacts" "school_probability" "work_probability" "offset(log_contactable_population)" ...
  ..$ nsdf             : int 3
  ..$ offset           : num [1:8787] -0.1527 -0.1387 -0.1246 -0.1105 -0.0964 ...
  ..$ pterms           :Classes 'terms', 'formula'  language contacts ~ school_probability + work_probability + offset(log_contactable_population)
  .. .. ..- attr(*, "variables")= language list(contacts, school_probability, work_probability, offset(log_contactable_population))
  .. .. ..- attr(*, "offset")= int 4
  .. .. ..- attr(*, "factors")= int [1:4, 1:2] 0 1 0 0 0 0 1 0
  .. .. .. ..- attr(*, "dimnames")=List of 2
  .. .. .. .. ..$ : chr [1:4] "contacts" "school_probability" "work_probability" "offset(log_contactable_population)"
  .. .. .. .. ..$ : chr [1:2] "school_probability" "work_probability"
  .. .. ..- attr(*, "term.labels")= chr [1:2] "school_probability" "work_probability"
  .. .. ..- attr(*, "order")= int [1:2] 1 1
  .. .. ..- attr(*, "intercept")= int 1
  .. .. ..- attr(*, "response")= int 1
  .. .. ..- attr(*, ".Environment")=<environment: R_GlobalEnv> 
  .. .. ..- attr(*, "predvars")= language list(contacts, school_probability, work_probability, offset(log_contactable_population))
  .. .. ..- attr(*, "dataClasses")= Named chr [1:5] "numeric" "numeric" "numeric" "numeric" ...
  .. .. .. ..- attr(*, "names")= chr [1:5] "contacts" "school_probability" "work_probability" "offset(log_contactable_population)" ...
  ..$ pred.formula     :Class 'formula'  language ~school_probability + work_probability + log_contactable_population + gam_age_offdiag +      gam_age_offdiag_2 + | __truncated__ ...
  .. .. ..- attr(*, ".Environment")=<environment: R_GlobalEnv> 
  ..$ smooth           :List of 6
  .. ..$ :List of 29
  .. .. ..$ term          : chr "gam_age_offdiag"
  .. .. ..$ bs.dim        : num 10
  .. .. ..$ fixed         : logi FALSE
  .. .. ..$ dim           : int 1
  .. .. ..$ p.order       : num 0
  .. .. ..$ by            : chr "NA"
  .. .. ..$ label         : chr "s(gam_age_offdiag)"
  .. .. ..$ xt            : NULL
  .. .. ..$ id            : NULL
  .. .. ..$ sp            : Named num -1
  .. .. .. ..- attr(*, "names")= chr "s(gam_age_offdiag)"
  .. .. ..$ drop.null     : num 0
  .. .. ..$ S             :List of 1
  .. .. .. ..$ : num [1:9, 1:9] 2.558 -0.769 4.813 -1.005 4.801 ...
  .. .. ..$ UZ            : num [1:103, 1:10] -3.97e-06 -3.66e-06 -3.37e-06 -3.09e-06 -2.83e-06 ...
  .. .. ..$ Xu            : num [1:101, 1] -32 -31 -30 -29 -28 ...
  .. .. ..$ df            : num 9
  .. .. ..$ shift         : num [1(1d)] 32
  .. .. ..$ rank          : num 8
  .. .. ..$ null.space.dim: num 1
  .. .. ..$ plot.me       : logi TRUE
  .. .. ..$ side.constrain: logi TRUE
  .. .. ..$ repara        : logi TRUE
  .. .. ..$ C             : num 0
  .. .. ..$ S.scale       : num 2.27e-05
  .. .. ..$ Cp            : num 0
  .. .. ..$ vn            : chr "gam_age_offdiag"
  .. .. ..$ first.para    : num 4
  .. .. ..$ last.para     : num 12
  .. .. ..$ first.sp      : num 1
  .. .. ..$ last.sp       : num 1
  .. .. ..- attr(*, "class")= chr [1:2] "tprs.smooth" "mgcv.smooth"
  .. .. ..- attr(*, "qrc")= 'sweepDrop' num [1:10] 9 -0.938 -0.423 -0.743 -0.413 ...
  .. .. ..- attr(*, "nCons")= num 1
  .. ..$ :List of 29
  .. .. ..$ term          : chr "gam_age_offdiag_2"
  .. .. ..$ bs.dim        : num 10
  .. .. ..$ fixed         : logi FALSE
  .. .. ..$ dim           : int 1
  .. .. ..$ p.order       : num 0
  .. .. ..$ by            : chr "NA"
  .. .. ..$ label         : chr "s(gam_age_offdiag_2)"
  .. .. ..$ xt            : NULL
  .. .. ..$ id            : NULL
  .. .. ..$ sp            : Named num -1
  .. .. .. ..- attr(*, "names")= chr "s(gam_age_offdiag_2)"
  .. .. ..$ drop.null     : num 0
  .. .. ..$ S             :List of 1
  .. .. .. ..$ : num [1:9, 1:9] 1.75 -1.68 -3.12 -2.03 3.03 ...
  .. .. ..$ UZ            : num [1:103, 1:10] 1.54e-12 1.54e-12 1.54e-12 1.53e-12 1.51e-12 ...
  .. .. ..$ Xu            : num [1:101, 1] -1533 -1532 -1529 -1524 -1517 ...
  .. .. ..$ df            : num 9
  .. .. ..$ shift         : num [1(1d)] 1533
  .. .. ..$ rank          : num 8
  .. .. ..$ null.space.dim: num 1
  .. .. ..$ plot.me       : logi TRUE
  .. .. ..$ side.constrain: logi TRUE
  .. .. ..$ repara        : logi TRUE
  .. .. ..$ C             : num 0
  .. .. ..$ S.scale       : num 2.23e-11
  .. .. ..$ Cp            : num 0
  .. .. ..$ vn            : chr "gam_age_offdiag_2"
  .. .. ..$ first.para    : num 13
  .. .. ..$ last.para     : num 21
  .. .. ..$ first.sp      : num 2
  .. .. ..$ last.sp       : num 2
  .. .. ..- attr(*, "class")= chr [1:2] "tprs.smooth" "mgcv.smooth"
  .. .. ..- attr(*, "qrc")= 'sweepDrop' num [1:10] 9 0.961 0.691 -0.87 0.676 ...
  .. .. ..- attr(*, "nCons")= num 1
  .. ..$ :List of 29
  .. .. ..$ term          : chr "gam_age_diag_prod"
  .. .. ..$ bs.dim        : num 10
  .. .. ..$ fixed         : logi FALSE
  .. .. ..$ dim           : int 1
  .. .. ..$ p.order       : num 0
  .. .. ..$ by            : chr "NA"
  .. .. ..$ label         : chr "s(gam_age_diag_prod)"
  .. .. ..$ xt            : NULL
  .. .. ..$ id            : NULL
  .. .. ..$ sp            : Named num -1
  .. .. .. ..- attr(*, "names")= chr "s(gam_age_diag_prod)"
  .. .. ..$ drop.null     : num 0
  .. .. ..$ S             :List of 1
  .. .. .. ..$ : num [1:9, 1:9] 4.08 3.48 -8.2 -5.06 -7.78 ...
  .. .. ..$ UZ            : num [1:2002, 1:10] -2.38e-13 -2.37e-13 -2.37e-13 -2.36e-13 -2.36e-13 ...
  .. .. ..$ Xu            : num [1:2000, 1] -2152 -2150 -2149 -2146 -2145 ...
  .. .. ..$ df            : num 9
  .. .. ..$ shift         : num [1(1d)] 2152
  .. .. ..$ rank          : num 8
  .. .. ..$ null.space.dim: num 1
  .. .. ..$ plot.me       : logi TRUE
  .. .. ..$ side.constrain: logi TRUE
  .. .. ..$ repara        : logi TRUE
  .. .. ..$ C             : num 0
  .. .. ..$ S.scale       : num 2.26e-11
  .. .. ..$ Cp            : num 0
  .. .. ..$ vn            : chr "gam_age_diag_prod"
  .. .. ..$ first.para    : num 22
  .. .. ..$ last.para     : num 30
  .. .. ..$ first.sp      : num 3
  .. .. ..$ last.sp       : num 3
  .. .. ..- attr(*, "class")= chr [1:2] "tprs.smooth" "mgcv.smooth"
  .. .. ..- attr(*, "qrc")= 'sweepDrop' num [1:10] 9 -0.947 0.262 0.763 -0.162 ...
  .. .. ..- attr(*, "nCons")= num 1
  .. ..$ :List of 29
  .. .. ..$ term          : chr "gam_age_diag_sum"
  .. .. ..$ bs.dim        : num 10
  .. .. ..$ fixed         : logi FALSE
  .. .. ..$ dim           : int 1
  .. .. ..$ p.order       : num 0
  .. .. ..$ by            : chr "NA"
  .. .. ..$ label         : chr "s(gam_age_diag_sum)"
  .. .. ..$ xt            : NULL
  .. .. ..$ id            : NULL
  .. .. ..$ sp            : Named num -1
  .. .. .. ..- attr(*, "names")= chr "s(gam_age_diag_sum)"
  .. .. ..$ drop.null     : num 0
  .. .. ..$ S             :List of 1
  .. .. .. ..$ : num [1:9, 1:9] 4.97 1.09 11.11 -1.66 -11.93 ...
  .. .. ..$ UZ            : num [1:193, 1:10] 4.62e-07 4.42e-07 4.23e-07 4.04e-07 3.85e-07 ...
  .. .. ..$ Xu            : num [1:191, 1] -93 -92 -91 -90 -89 ...
  .. .. ..$ df            : num 9
  .. .. ..$ shift         : num [1(1d)] 93
  .. .. ..$ rank          : num 8
  .. .. ..$ null.space.dim: num 1
  .. .. ..$ plot.me       : logi TRUE
  .. .. ..$ side.constrain: logi TRUE
  .. .. ..$ repara        : logi TRUE
  .. .. ..$ C             : num 0
  .. .. ..$ S.scale       : num 2.88e-06
  .. .. ..$ Cp            : num 0
  .. .. ..$ vn            : chr "gam_age_diag_sum"
  .. .. ..$ first.para    : num 31
  .. .. ..$ last.para     : num 39
  .. .. ..$ first.sp      : num 4
  .. .. ..$ last.sp       : num 4
  .. .. ..- attr(*, "class")= chr [1:2] "tprs.smooth" "mgcv.smooth"
  .. .. ..- attr(*, "qrc")= 'sweepDrop' num [1:10] 9 0.951 0.292 0.558 -0.351 ...
  .. .. ..- attr(*, "nCons")= num 1
  .. ..$ :List of 29
  .. .. ..$ term          : chr "gam_age_pmax"
  .. .. ..$ bs.dim        : num 10
  .. .. ..$ fixed         : logi FALSE
  .. .. ..$ dim           : int 1
  .. .. ..$ p.order       : num 0
  .. .. ..$ by            : chr "NA"
  .. .. ..$ label         : chr "s(gam_age_pmax)"
  .. .. ..$ xt            : NULL
  .. .. ..$ id            : NULL
  .. .. ..$ sp            : Named num -1
  .. .. .. ..- attr(*, "names")= chr "s(gam_age_pmax)"
  .. .. ..$ drop.null     : num 0
  .. .. ..$ S             :List of 1
  .. .. .. ..$ : num [1:9, 1:9] 4.151 -0.822 7.91 -1.089 8.153 ...
  .. .. ..$ UZ            : num [1:103, 1:10] -4.38e-06 -4.04e-06 -3.72e-06 -3.41e-06 -3.12e-06 ...
  .. .. ..$ Xu            : num [1:101, 1] -62.5 -61.5 -60.5 -59.5 -58.5 ...
  .. .. ..$ df            : num 9
  .. .. ..$ shift         : num [1(1d)] 62.5
  .. .. ..$ rank          : num 8
  .. .. ..$ null.space.dim: num 1
  .. .. ..$ plot.me       : logi TRUE
  .. .. ..$ side.constrain: logi TRUE
  .. .. ..$ repara        : logi TRUE
  .. .. ..$ C             : num 0
  .. .. ..$ S.scale       : num 1.7e-05
  .. .. ..$ Cp            : num 0
  .. .. ..$ vn            : chr "gam_age_pmax"
  .. .. ..$ first.para    : num 40
  .. .. ..$ last.para     : num 48
  .. .. ..$ first.sp      : num 5
  .. .. ..$ last.sp       : num 5
  .. .. ..- attr(*, "class")= chr [1:2] "tprs.smooth" "mgcv.smooth"
  .. .. ..- attr(*, "qrc")= 'sweepDrop' num [1:10] 9 -0.943 0.625 -0.705 0.621 ...
  .. .. ..- attr(*, "nCons")= num 1
  .. ..$ :List of 29
  .. .. ..$ term          : chr "gam_age_pmin"
  .. .. ..$ bs.dim        : num 10
  .. .. ..$ fixed         : logi FALSE
  .. .. ..$ dim           : int 1
  .. .. ..$ p.order       : num 0
  .. .. ..$ by            : chr "NA"
  .. .. ..$ label         : chr "s(gam_age_pmin)"
  .. .. ..$ xt            : NULL
  .. .. ..$ id            : NULL
  .. .. ..$ sp            : Named num -1
  .. .. .. ..- attr(*, "names")= chr "s(gam_age_pmin)"
  .. .. ..$ drop.null     : num 0
  .. .. ..$ S             :List of 1
  .. .. .. ..$ : num [1:9, 1:9] 3.642 0.732 6.581 -0.913 6.554 ...
  .. .. ..$ UZ            : num [1:93, 1:10] -6.87e-06 -6.26e-06 -5.67e-06 -5.11e-06 -4.59e-06 ...
  .. .. ..$ Xu            : num [1:91, 1] -30.5 -29.5 -28.5 -27.5 -26.5 ...
  .. .. ..$ df            : num 9
  .. .. ..$ shift         : num [1(1d)] 30.5
  .. .. ..$ rank          : num 8
  .. .. ..$ null.space.dim: num 1
  .. .. ..$ plot.me       : logi TRUE
  .. .. ..$ side.constrain: logi TRUE
  .. .. ..$ repara        : logi TRUE
  .. .. ..$ C             : num 0
  .. .. ..$ S.scale       : num 2.24e-05
  .. .. ..$ Cp            : num 0
  .. .. ..$ vn            : chr "gam_age_pmin"
  .. .. ..$ first.para    : num 49
  .. .. ..$ last.para     : num 57
  .. .. ..$ first.sp      : num 6
  .. .. ..$ last.sp       : num 6
  .. .. ..- attr(*, "class")= chr [1:2] "tprs.smooth" "mgcv.smooth"
  .. .. ..- attr(*, "qrc")= 'sweepDrop' num [1:10] 9 -0.939 -0.671 -0.745 0.669 ...
  .. .. ..- attr(*, "nCons")= num 1
  ..$ terms            :Classes 'terms', 'formula'  language contacts ~ school_probability + work_probability + offset(log_contactable_population) +      gam_age_offdiag + ga| __truncated__ ...
  .. .. ..- attr(*, "variables")= language list(contacts, school_probability, work_probability, offset(log_contactable_population),      gam_age_offdiag, ga| __truncated__ ...
  .. .. ..- attr(*, "offset")= int 4
  .. .. ..- attr(*, "factors")= int [1:10, 1:8] 0 1 0 0 0 0 0 0 0 0 ...
  .. .. .. ..- attr(*, "dimnames")=List of 2
  .. .. .. .. ..$ : chr [1:10] "contacts" "school_probability" "work_probability" "offset(log_contactable_population)" ...
  .. .. .. .. ..$ : chr [1:8] "school_probability" "work_probability" "gam_age_offdiag" "gam_age_offdiag_2" ...
  .. .. ..- attr(*, "term.labels")= chr [1:8] "school_probability" "work_probability" "gam_age_offdiag" "gam_age_offdiag_2" ...
  .. .. ..- attr(*, "order")= int [1:8] 1 1 1 1 1 1 1 1
  .. .. ..- attr(*, "intercept")= int 1
  .. .. ..- attr(*, "response")= int 1
  .. .. ..- attr(*, ".Environment")=<environment: R_GlobalEnv> 
  .. .. ..- attr(*, "predvars")= language list(contacts, school_probability, work_probability, offset(log_contactable_population),      gam_age_offdiag, ga| __truncated__ ...
  .. .. ..- attr(*, "dataClasses")= Named chr [1:11] "numeric" "numeric" "numeric" "numeric" ...
  .. .. .. ..- attr(*, "names")= chr [1:11] "contacts" "school_probability" "work_probability" "offset(log_contactable_population)" ...
  ..$ var.summary      :List of 9
  .. ..$ school_probability        : num [1:3] 0 0.00147 0.90552
  .. ..$ work_probability          : num [1:3] 0 0.0183 0.58
  .. ..$ log_contactable_population: num [1:3] -9.34 -4.5 -4.16
  .. ..$ gam_age_offdiag           : num [1:3] 0 28 100
  .. ..$ gam_age_offdiag_2         : num [1:3] 0 784 10000
  .. ..$ gam_age_diag_prod         : num [1:3] 0 1596 9000
  .. ..$ gam_age_diag_sum          : num [1:3] 0 93 190
  .. ..$ gam_age_pmax              : num [1:3] 0 66 100
  .. ..$ gam_age_pmin              : num [1:3] 0 27 90
  ..$ weights          : num [1:8787] 7.99 6.36 5.18 4.3 3.62 ...
  ..$ xlevels          : Named list()
  ..$ NA.action        :function (object, ...)  
  ..$ linear.predictors: num [1:8787] 2.08 1.85 1.64 1.46 1.29 ...
  ..$ fitted.values    : num [1:8787] 7.99 6.36 5.18 4.3 3.62 ...
  ..$ residuals        : num [1:8787] -0.358 0.25 2.222 2.698 0.683 ...
  ..$ deviance         : num 14913
  ..$ aic              : num 33995
  ..$ null.deviance    : num 76245
  ..- attr(*, "class")= chr [1:4] "bam" "gam" "glm" "lm"
 - attr(*, "class")= chr [1:2] "setting_contact_model" "list"

Alt approach to conmat model

synthetic_settings_5y_monash <- predict_setting_contacts(
  population = monash_age_pop,
  contact_model = setting_models,
  age_breaks = c(seq(0, 85, by = 5), Inf)
  )

synthetic_settings_5y_monash

In code: How does the model work?

(simplified)

mgcv::bam(
  formula = contacts ~
    # deviation of contact age distribution from population age distribution
    s(age_to) +
    # number of contacts by age
    s(age_from) +
    # intergenerational contact patterns - enables the off-diagonals
    s(abs(age_from - age_to)) +
    # interaction between intergenerational patterns and age_from, to remove
    # ridge for some ages and settings
    s(abs(age_from - age_to), age_from) +
    # probabilities of both attending (any) school/work
    school_probability +
    work_probability +
    offset(school_popn/work_popn),
    family = stats::poisson,
    # NOTE: the offset of participants allows us to get the rate per person
    offset = log(participants)
    )

This is the last slide.