knitr::opts_chunk$set(
    message = FALSE,
    warning = FALSE,
    include = TRUE,
    error = FALSE
)

options(scipen = 999, digits = 7)

library(tidyverse)
library(kableExtra)

1 TwinLife sample

Note that you cannot run the code here unless you have requested and obtained the TwinLife data. You do not need to do this however, to reproduce the tutorial code in the RMarkdown document 02_tutorial.Rmd. The synthetic dataset we produce here is available on our OSF project and can be used for that purpose. The comparisons to the Raw estimates (Figures 3 and 4 in the manuscript, see document 03_norm_comparisons) can also be reproduced based on the synthetic dataset. The comparisons to the Manual norms cannot be made reproducible as we failed to obtain permission to share those values.

1.1 Preprocessing

# Load dataset and define missing values as NA without losing the different types of missing values in the dataset
tl_1 <- haven::read_dta("../unshareable_data/raw/ZA6701_person_wid1_v7-1-0.dta") %>% 
  # define variously coded NAs as NAs explicitly
  codebook::detect_missing(ninety_nine_problems = T) %>% 
  # total sum scores of the sum scores of all 4 subtests, automatically excludes invalid and NA subtests
  mutate(cft = igf0182 + igf0282 + igf0382 + igf0482,
  # divide age in months variable by 12 for easier interpretablility
         age = age0101/12,
  # create logical sex variable
         male = sex == 1, 
  # create age groups corresponding to those in the manual
  age_group = case_when(
                         age0100 == 11 ~ '11',
                         age0100 == 12 ~ '12',
                         age0100 == 13 ~ '13',
                         age0100 == 14 ~ '14',
                         age0100 == 15 ~ '15',
                         age0100 == 16 ~ '16',
                         age0100 >= 17 & age0100 <= 19 ~ '17-19',
                         age0100 >= 20 & age0100 <= 24 ~ '20-24',
                         age0100 >= 25 & age0100 <= 29 ~ '25-29',
                         age0100 >= 30 & age0100 <= 34 ~ '30-34',
                         age0100 >= 35 & age0100 <= 39 ~ '35-39',
                         age0100 >= 40 & age0100 <= 44 ~ '40-44',
                         age0100 >= 45 & age0100 <= 49 ~ '45-49',
                         age0100 >= 50 & age0100 <= 54 ~ '50-54',
                         age0100 >= 55 & age0100 <= 59 ~ '55-59',
                         age0100 >= 60 & age0100 <= 64 ~ '60-64',
                         TRUE ~ NA_character_))
  
tl_2 <- tl_1 %>%
  # since the migration variable in the census encodes information about both one's own and the parent's migration background and experience ("Migrationshintergrund und -erfahrung"), we start by creating a variable indicating whether the person and/or their parents is/are born abroad
  # assume German citizenship lacking information about citizenship
  mutate(mig0520 = coalesce(mig0520, 1), 
         born_abroad = case_when(
           # if the person themselves is not born in germany, then born_abroad = "self"
            mig2000 != 1 ~ 'self',
           # if the person and both their parents are born in germany, then born_abroad = "none"
            mig2000 == 1 & mig3100 == 1 & mig3200 == 1 ~ 'none',
           # etc..
            mig3100 != 1 & mig3200 != 1 ~ 'both_parents',
            mig3100 != 1 ~ 'one_parent',
            mig3200 != 1 ~ 'one_parent',
           # if no information is available, assume both the person and their parents are born in germany
            TRUE ~ "none"), 
         mig = case_when(
           # if the person is born abroad and is a Citizen... 
            born_abroad == "self" & mig0520 == 1 ~ "Citizen: Own mig experience",
           # etc..
            born_abroad == "self" & mig0520 != 1 ~ "Non-citizen: Own mig experience",
            born_abroad != "self" & mig0520 != 1 ~ "Non-citizen: No own mig experience",
            born_abroad == "one_parent" & mig0520 == 1 ~ "Citizen: Mig background from one parent",
            born_abroad == "both_parents" & mig0520 == 1 ~ "Citizen: Mig background from both parents",
            born_abroad == "none" & mig0520 == 1 ~ "Citizen: No mig background"))




# create an education variable corresponding to the census tables
tl <- tl_2 %>% 
  mutate(eca0108 = str_sub(as.character(as_factor(eca0108)), 4, -1),
         school_type = case_when(
           # we had to collapse some categories unto one another in order to ensure correspondance to census categories while attempting to minimise data loss. school types in the comments on the right are what the numerical codes refer to
            edu0400 == 1 ~ "ST1: Primary", # Grundschule
            edu0400 == 2 ~ "ST6: Other school", # Orientierungsschule
            edu0400 == 3 ~ "ST2: Lower secondary", # Hauptschule
            edu0400 == 4 ~ "ST3: Intermediate secondary", # Realschule
            edu0400 == 5 ~ "ST6: Other school", # Verbundene Haupt- und Realschule (auch Sekundar-, Real-, Regel-, Mittel-, Ober- und Wirtschaftsschule, regionale Schule, erweiterte Realschule)
            edu0400 == 6 ~ "ST5: Comprehensive school", # Gesamtschule
            edu0400 == 7 ~ "ST6: Other school", # Waldorfschule
            edu0400 == 8 ~ "ST4: Upper secondary", # Gymnasium (auch Kolleg)
            edu0400 == 9 ~ "ST6: Other school", # Sonderschule/Förderschule
            edu0400 == 10 ~ "ST6: Other school", # Andere Schule
            # "Entfällt, da kein/e Schüler/-innen" is a category in the census so we gave kids who don't have a school type category and who specified as an answer to another question that they no longer go to school this category
            edu0100 == 3 ~ "ST7: No longer at school"),  # "ich gehe nicht mehr in die Schule"
         isced = as.factor(case_when(
           # in TwinLife,isced code is coded starting with age 15 but we use it starting with 19 (see code further below). if the person is still at school at age 19 or older, we assume they  have a primary school certificate ("ISCED 1: Primary")
            eca0108 == "3] -83: in school or training/not in school yet" ~ "ISCED 1: Primary",
           # Exclude not codable 
            eca0108 == "9] -89: not codable" ~ NA_character_,
           # Those are kids younger than 15
            eca0108 == "5] -95: doesn't apply (screened out)" ~ NA_character_,
           # we don't change any coding here, only translation/explanation
            eca0108 == "level 1" ~   "ISCED 1: Primary",
            eca0108 == "level 2a" ~  "ISCED 2: Lower secondary",
            eca0108 == "level 3a" ~  "ISCED 3a: Upper secondary, general",
            eca0108 == "level 3b" ~  "ISCED 3b: Upper secondary, vocational",
            eca0108 == "level 3c" ~  "ISCED 3b: Upper secondary, vocational",
            eca0108 == "level 4a" ~  "ISCED 4: Post-secondary",
            eca0108 == "level 5a" ~  "ISCED 5a: Tertiary, e.g., college",
            eca0108 == " level 5b" ~ "ISCED 5b: Tertiary, e.g., Berufsakademie",
            eca0108 == " level 6" ~  "ISCED 6: PhD")),
           # the final education variable combines the variable school type (age <19) and isced (age > 18)
         educ = as.factor(case_when(
           age0100 <= 18 ~ school_type, 
           age0100 >= 19 ~ isced)),
           # set a large category (upper secondary, vocational) as the reference category of the educ factor
         educ = relevel(educ, 4),
           # same for isced
         isced = relevel(isced, 4)) %>%
 filter(# filter out those who didn't take the IQ test
        !is.na(cft) &
        # filter kids aged <11 because no school type information
        age >= 11 &
        # exclude kids aged >10 & <19 who don't have school type info and everyone >18 who doesn't have ISCED information, i.e., n = 106
         !is.na(educ)
        )


# save dataset
save(tl, file="../unshareable_data/preprocessed/tl.Rda")

1.2 Synthetic TL sample for the sake of reproducing the tutorial code

reduced_tl <- tl %>%
filter(ptyp != 1) %>%
select(cft, age, male, mig, educ)

synthetic_tl <- synthpop::syn(reduced_tl, seed = 14)
## 
## Variable(s): mig have been changed for synthesis from character to factor.
## 
## Synthesis
## -----------
##  cft age male mig educ
summary(synthetic_tl)
## Synthetic object with one synthesis using methods:
##      cft      age     male      mig     educ 
## "sample"   "cart"   "cart"   "cart"   "cart" 
## 
##       cft             age           male             mig           
##  Min.   : 7.00   Min.   :11.00   Mode :logical   Length:10059      
##  1st Qu.:32.00   1st Qu.:18.83   FALSE:5829      Class :character  
##  Median :38.00   Median :39.25   TRUE :4230      Mode  :character  
##  Mean   :37.36   Mean   :35.36                                     
##  3rd Qu.:43.00   3rd Qu.:48.58                                     
##  Max.   :55.00   Max.   :74.67                                     
##                                                                    
##                                        educ     
##  ISCED 5a: Tertiary, e.g., college       :2266  
##  ISCED 3b: Upper secondary, vocational   :2094  
##  ST4: Upper secondary                    :1249  
##  ISCED 5b: Tertiary, e.g., Berufsakademie:1081  
##  ISCED 4: Post-secondary                 : 672  
##  ISCED 3a: Upper secondary, general      : 517  
##  (Other)                                 :2180
synthpop::compare(synthetic_tl, reduced_tl, stat = "counts")
## 
## Comparing counts observed with synthetic

## Press return for next variable(s):

## 
## Selected utility measures:
##          pMSE   S_pMSE df
## cft  0.000045 1.806774  4
## age  0.000028 1.106727  4
## male 0.000006 0.899838  1
## mig  0.000024 0.786009  5
## educ 0.000129 1.488444 14
synthetic_tl <- synthetic_tl$syn %>%
 mutate(age0100 = round(age),
        age_group = case_when(
                         age0100 == 11 ~ '11',
                         age0100 == 12 ~ '12',
                         age0100 == 13 ~ '13',
                         age0100 == 14 ~ '14',
                         age0100 == 15 ~ '15',
                         age0100 == 16 ~ '16',
                         age0100 >= 17 & age0100 <= 19 ~ '17-19',
                         age0100 >= 20 & age0100 <= 24 ~ '20-24',
                         age0100 >= 25 & age0100 <= 29 ~ '25-29',
                         age0100 >= 30 & age0100 <= 34 ~ '30-34',
                         age0100 >= 35 & age0100 <= 39 ~ '35-39',
                         age0100 >= 40 & age0100 <= 44 ~ '40-44',
                         age0100 >= 45 & age0100 <= 49 ~ '45-49',
                         age0100 >= 50 & age0100 <= 54 ~ '50-54',
                         age0100 >= 55 & age0100 <= 59 ~ '55-59',
                         age0100 >= 60 & age0100 <= 64 ~ '60-64',
                         TRUE ~ NA_character_))


# save synthetic dataset
save(synthetic_tl, file="data/simulated/synthetic_tl.Rda")

2 CFT manual norms

The raw means and SDs manually extracted from the CFT-20R manual had to be redacted since Hogrefe (publisher of the manual) did not allow us to share them.

# manual_norms <- tibble(
#          age_group = c('11:1-11:6', '11:7-12', '12:1-12:6', '12:7-13', '13:1-13:6', '13:7-14', '14:1-14:6', '14:7-15', '15:1-16', '16:1-17', '17:1-19:11', '20-24', '25-29', '30-34', '35-39', '40-44', '45-49', '50-54', '55-59' ,'60-64'),
#          n_manual =    c(               R E D A C T E D             ),
#          mean_manual = c(               R E D A C T E D             ),
#          sd_manual =   c(               R E D A C T E D             )) %>%
#   filter(!is.na(age_group)) %>%
#   mutate(age_group = case_when(
#                          age_group == "10:1-10:6" | age_group == "10:7-11" ~ '10',
#                          age_group == "11:1-11:6" | age_group == "11:7-12" ~ '11',
#                          age_group == "12:1-12:6" | age_group == "12:7-13" ~ '12',
#                          age_group == "13:1-13:6" | age_group == "13:7-14" ~ '13',
#                          age_group == "14:1-14:6" | age_group == "14:7-15" ~ '14',
#                          age_group == "15:1-16"                            ~ '15',
#                          age_group == "16:1-17"                            ~ '16',
#                          age_group == "17:1-19:11"                         ~ '17-19',
#                          TRUE ~ age_group)) %>%
#   group_by(age_group) %>%
#   summarise(Manual_n = sum(n_manual),
#             Manual_mean = mean(mean_manual),
#             Manual_sd = sqrt(mean(sd_manual^2)),
#             Manual_se_of_mean = Manual_sd/sqrt(Manual_n))

# save(manual_norms, file="../unshareable_data/preprocessed/manual_norms.Rda")
# write.csv(manual_norms, file = "../unshareable_data/preprocessed/manual_norms.csv")

3 Census tables

3.1 Main poststratification table

census_school_type_raw <- readxl::read_excel("data/raw/729305_Zensus2011_Bildung_Schulform.xlsx", 
    sheet = "Migration und Schulform") 

census_school_type <- census_school_type_raw %>% 
  # remove redundant columns of higher order categories (e.g., Deutsche mit Migrationshintergrund)
  select(1:2, 21:28, 45:60, 69:76, 85:100) %>%
  # exclude sex and age columns
  select(3:50) %>%
  # before iteratively naming the 48 columns, 8 (school types) * 6 (migration background) 
  set_names(map(c("Citizen: No mig background", # Personen ohne Migrationshintergrund
                  "Non-citizen: Own mig experience", # Ausländer/-innen mit eigener Migrationserfahrung
                  "Non-citizen: No own mig experience", # Ausländer/-innen ohne eigene Migrationserfahrung
                  "Citizen: Own mig experience", # Deutsche mit eigener Migrationserfahrung
                  "Citizen: Mig background from both parents", # Deutsche mit beidseitigem Migrationshintergrund
                  "Citizen: Mig background from one parent"), # Deutsche mit einseitigem Migrationshintergrund
                ~ paste0(.x, "_", c("total", 
                                    "ST7: No longer at school", # Entfällt, da kein/e Schüler/-innen
                                    "ST1: Primary", # Grundschule
                                    "ST2: Lower secondary", # Hauptschule
                                    "ST3: Intermediate secondary", # Realschule
                                    "ST4: Upper secondary", # Gymnasium
                                    "ST5: Comprehensive school", # Gesamtschule
                                    "ST6: Other school"))) %>% # Sonstige Schule
            unlist()) %>%
  # recover sex and age columns
  mutate(age = as.numeric(parse_number(census_school_type_raw[[2]])),
         male = census_school_type_raw[[1]],
         ) %>% 
  relocate(c(age,male)) %>% 
  mutate(male = ifelse(row_number() >= 115 & row_number() <= 215, TRUE, FALSE)) %>% 
  slice(-(1:113), -215) %>% 
  # disentangle mig and educ variables from one another
  pivot_longer(cols = 3:50,
               names_to = c("mig", "school_type"),
               names_sep = "_",
               values_to = "census_n") %>%
 # filter(census_n != "/") %>% 
  # set censored cells to 0
  mutate(census_n = ifelse(census_n == "/", 0, as.numeric(census_n)))



census_ISCED_raw <- readxl::read_excel("data/raw/729305_Zensus2011_Bildung_ISCED.xlsx", sheet = "Migration und ISCED")

census_ISCED <- census_ISCED_raw %>% 
  set_names(paste0("var", 1:133)) %>% 
  select(-(
    census_ISCED_raw %>% 
    summarise(across(everything(), ~ any(str_detect(., "ISCED-Ebene")) & any(str_detect(., "Insgesamt")))) %>% 
    unlist() %>% 
    which()
  )) %>% 
  select(1:2, var24:var34, var57:var78, var90:var100, var112:var133) %>% 
  select(3:56) %>%
  set_names(map(c("Citizen: No mig background", # Personen ohne Migrationshintergrund
                  "Non-citizen: Own mig experience", # Ausländer/-innen mit eigener Migrationserfahrung
                  "Non-citizen: No own mig experience", # Ausländer/-innen ohne eigene Migrationserfahrung
                  "Citizen: Own mig experience", # Deutsche mit eigener Migrationserfahrung
                  "Citizen: Mig background from both parents", # Deutsche mit beidseitigem Migrationshintergrun,
                  "Citizen: Mig background from one parent"), # Deutsche mit einseitigem Migrationshintergrund
         ~ paste0(.x, "_", c("total",
                             "ISCED 1: Primary", # ISCED-Ebene 1 = Primärbereich
                             "ISCED 2: Lower secondary", # ISCED-Ebene 2 = Sekundarbereich I
                             "ISCED 3a: Upper secondary, general", # Sekundarbereich II A, allgemein bildend
                             "ISCED 3b: Upper secondary, vocational", # Sekundarbereich II B, beruflich
                             "ISCED 4: Post-secondary", # ISCED-Ebene 4 = Postsekundäre nichttertiäre Bildung
                             "ISCED 5a: Tertiary, e.g., college", # ISCED-Ebene 5 = Erste Stufe der tertiären Bildung, Tertiärbereich A
                             "ISCED 5b: Tertiary, e.g., Berufsakademie", # ISCED-Ebene 5 = Erste Stufe der tertiären Bildung, Tertiärbereich B
                             "ISCED 6: PhD" # ISCED-Ebene 6 = Zweite Stufe der tertiären Bildung
                             ))) %>% 
            unlist()) %>% 
  mutate(age = as.numeric(parse_number(census_ISCED_raw[[2]])),
         male = census_ISCED_raw[[1]]) %>% 
  relocate(c(age,male)) %>% 
  mutate(male = ifelse(row_number() >= 99 & row_number() <= 186, TRUE, FALSE)) %>% 
  slice(-(1:99), -186) %>% 
  pivot_longer(cols = 3:56,
               names_to = c("mig", "isced"),
               names_sep = "_",
               values_to = "census_n") %>%
 # filter(census_n != "/") %>% 
  mutate(census_n = ifelse(census_n == "/", 0, as.numeric(census_n)))


# 11 because that's the minimum age for the educ and mig variables we used in the TL sample
census_school_type_11_18 <- census_school_type %>% 
  filter((age >= 11 & age <= 18) & school_type != "total") %>% 
  rename(educ = "school_type") %>% 
  relocate(educ, .after = "mig")


census_ISCED_19_79 <- census_ISCED %>% 
  filter((age >= 19 & age <= 79) & isced != "total") %>% 
  rename(educ = "isced")

census <- census_school_type_11_18 %>% 
  bind_rows(census_ISCED_19_79) 

# save(census, file="data/preprocessed/census.Rda")

# print random 14 rows/cells/subgroups/combinations out of the total 6528 in the poststratification table
census %>% slice_sample(n = 14) %>% kable %>% kable_styling(full_width = FALSE)
age male mig educ census_n
57 FALSE Citizen: Mig background from one parent ISCED 3b: Upper secondary, vocational 0
50 FALSE Non-citizen: No own mig experience ISCED 5a: Tertiary, e.g., college 0
24 TRUE Non-citizen: No own mig experience ISCED 6: PhD 0
44 TRUE Non-citizen: Own mig experience ISCED 5a: Tertiary, e.g., college 6010
51 FALSE Citizen: Own mig experience ISCED 6: PhD 0
53 TRUE Citizen: Own mig experience ISCED 1: Primary 3070
75 TRUE Citizen: No mig background ISCED 5a: Tertiary, e.g., college 42090
36 TRUE Citizen: No mig background ISCED 1: Primary 4720
71 FALSE Non-citizen: Own mig experience ISCED 3a: Upper secondary, general 440
77 FALSE Non-citizen: No own mig experience ISCED 1: Primary 0
73 TRUE Citizen: Own mig experience ISCED 1: Primary 3350
34 TRUE Non-citizen: Own mig experience ISCED 3a: Upper secondary, general 3300
54 FALSE Citizen: Mig background from both parents ISCED 3a: Upper secondary, general 0
27 FALSE Citizen: Mig background from one parent ISCED 1: Primary 0

3.2 Totals for the disparities plot

age_marginals <- census_school_type_raw %>%
  # total column, rows for ages 11 through 79
  select(3) %>% 
  slice(23:91) %>% 
  mutate(n = as.numeric(`...3`),
         category = as.character(11:79),
         percentage = n/sum(n)*100,
         source = "census",
         variable = "age") %>% 
  select(-1)

sex_marginals <- census_school_type_raw %>%
  # total column, rows for ages 11 through 79
  select(3) %>% 
  slice((125:193), (227:295)) %>% 
  mutate(category = as.character(ifelse(row_number() < 70, T, F)),
         n = as.numeric(`...3`)) %>% 
  group_by(category) %>% 
  summarize(n = sum(n)) %>% 
  mutate(source = "census",
         variable = "male",
         percentage = n/sum(n)*100)

school_type_marginals <- census_school_type_raw %>% 
  select(14:20) %>% 
  slice(23:30) %>% 
  set_names(paste0(c("ST7: No longer at school", # Entfällt, da kein/e Schüler/-innen
                     "ST1: Primary", # Grundschule
                     "ST2: Lower secondary", # Hauptschule
                     "ST3: Intermediate secondary", # Realschule
                     "ST4: Upper secondary", # Gymnasium
                     "ST5: Comprehensive school", # Gesamtschule
                     "ST6: Other school"))) %>% # Sonstige Schule  
  mutate_all(as.numeric) %>% 
  summarise(across(everything(), ~sum(., na.rm = TRUE))) %>% 
  pivot_longer(cols = everything(), names_to = "category", values_to = "n")
  
  
educ_marginals <- census_ISCED_raw %>% 
  select(14, 15, 17:19, 21, 22, 23) %>% 
    slice(17:77) %>% 
  set_names(paste0(c("ISCED 1: Primary",                 
                     "ISCED 2: Lower secondary",          
                     "ISCED 3a: Upper secondary, general", 
                     "ISCED 3b: Upper secondary, vocational",
                     "ISCED 4: Post-secondary",           
                     "ISCED 5a: Tertiary, e.g., college",   
                     "ISCED 5b: Tertiary, e.g., Berufsakademie",
                     "ISCED 6: PhD"))) %>% 
  mutate_all(as.numeric) %>% 
  summarise(across(everything(), ~sum(., na.rm = TRUE))) %>% 
  pivot_longer(cols = everything(), names_to = "category", values_to = "n") %>% 
  full_join(school_type_marginals, by = "category") %>% 
  mutate(n = ifelse((is.na(n.x) | is.na(n.y)), coalesce(n.x, n.y), n.x + n.y),
         source = "census",
         variable = "educ",
         percentage = n/sum(n)*100) %>% 
  select(-c(n.x,n.y))

mig_marginals <- census_school_type_raw %>% 
  select(4, 7, 8, 10, 12, 13) %>% 
  slice(23:91) %>% 
  set_names(paste0(c("Citizen: No mig background", 
                     "Non-citizen: Own mig experience", 
                     "Non-citizen: No own mig experience", 
                     "Citizen: Own mig experience",
                     "Citizen: Mig background from both parents",
                     "Citizen: Mig background from one parent"))) %>% 
  mutate_all(as.numeric) %>% 
  summarise(across(everything(), ~sum(., na.rm = TRUE))) %>% 
  pivot_longer(cols = everything(), names_to = "category", values_to = "n") %>% 
  mutate(source = "census",
         variable = "mig",
         percentage = n/sum(n)*100)
                     
census_marginals <- bind_rows(age_marginals, educ_marginals, sex_marginals, mig_marginals)

save(census_marginals, file="data/preprocessed/census_marginals.Rda")

4 Session info

sessionInfo()
## R version 4.2.2 (2022-10-31)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Rocky Linux 8.8 (Green Obsidian)
## 
## Matrix products: default
## BLAS/LAPACK: /software/all/FlexiBLAS/3.2.1-GCC-12.2.0/lib64/libflexiblas.so.3.2
## 
## locale:
##  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
##  [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
##  [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
##  [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
##  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
## [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       
## 
## attached base packages:
## [1] stats     graphics  grDevices datasets  utils     methods   base     
## 
## other attached packages:
##  [1] kableExtra_1.3.4 forcats_1.0.0    stringr_1.5.1    dplyr_1.1.4     
##  [5] purrr_1.0.2      readr_2.1.4      tidyr_1.3.0      tibble_3.2.1    
##  [9] ggplot2_3.4.4    tidyverse_1.3.2 
## 
## loaded via a namespace (and not attached):
##   [1] TH.data_1.1-2        googledrive_2.1.1    colorspace_2.1-0    
##   [4] class_7.3-21         modeltools_0.2-23    mipfp_3.2.1         
##   [7] fs_1.6.3             rmutil_1.1.10        rstudioapi_0.15.0   
##  [10] proxy_0.4-27         farver_2.1.1         fansi_1.0.5         
##  [13] mvtnorm_1.2-3        lubridate_1.9.3      coin_1.4-3          
##  [16] ranger_0.16.0        xml2_1.3.5           codetools_0.2-19    
##  [19] splines_4.2.2        cachem_1.0.8         libcoin_1.0-10      
##  [22] knitr_1.45           jsonlite_1.8.7       broom_1.0.5         
##  [25] dbplyr_2.4.0         compiler_4.2.2       httr_1.4.7          
##  [28] backports_1.4.1      Matrix_1.5-3         fastmap_1.1.1       
##  [31] gargle_1.5.2         strucchange_1.5-3    cli_3.6.1           
##  [34] htmltools_0.5.7      tools_4.2.2          gtable_0.3.4        
##  [37] glue_1.6.2           broman_0.80          Rcpp_1.0.11         
##  [40] cellranger_1.1.0     jquerylib_0.1.4      vctrs_0.6.4         
##  [43] svglite_2.1.2        xfun_0.41            proto_1.0.0         
##  [46] rvest_1.0.3          timechange_0.2.0     lifecycle_1.0.4     
##  [49] cmm_1.0              renv_1.0.3           googlesheets4_1.1.1 
##  [52] polspline_1.1.24     MASS_7.3-58.3        zoo_1.8-12          
##  [55] scales_1.2.1         hms_1.1.3            parallel_4.2.2      
##  [58] sandwich_3.0-2       yaml_2.3.7           sass_0.4.7          
##  [61] labelled_2.12.0      rpart_4.1.19         stringi_1.8.2       
##  [64] highr_0.10           randomForest_4.7-1.1 e1071_1.7-13        
##  [67] truncnorm_1.0-9      rlang_1.1.2          pkgconfig_2.0.3     
##  [70] systemfonts_1.0.5    matrixStats_1.1.0    Rsolnp_1.16         
##  [73] evaluate_0.23        lattice_0.22-5       labeling_0.4.3      
##  [76] codebook_0.9.2       synthpop_1.8-0       tidyselect_1.2.0    
##  [79] plyr_1.8.9           magrittr_2.0.3       R6_2.5.1            
##  [82] generics_0.1.3       multcomp_1.4-25      DBI_1.1.3           
##  [85] pillar_1.9.0         haven_2.5.3          foreign_0.8-84      
##  [88] withr_2.5.2          survival_3.5-5       nnet_7.3-18         
##  [91] modelr_0.1.11        crayon_1.5.2         KernSmooth_2.23-20  
##  [94] utf8_1.2.4           party_1.3-13         tzdb_0.4.0          
##  [97] rmarkdown_2.25       grid_4.2.2           readxl_1.4.3        
## [100] reprex_2.0.2         digest_0.6.33        classInt_0.4-10     
## [103] webshot_0.5.5        numDeriv_2016.8-1.1  stats4_4.2.2        
## [106] munsell_0.5.0        viridisLite_0.4.2    bslib_0.6.0