# It is recommended to assign this module to a variable called: event_demog_marriage
# for example: event_demog_marriage <- modules::use('modules/demography/marriage.R')
modules::import('dymiumCore')
modules::import("here")
modules::import("data.table")
modules::import("checkmate")
modules::import("purrr", "flatten_int")
modules::import("R6", "R6Class")
modules::export('^run$|^REQUIRED_MODELS$') # default exported functions
modules::expose(here::here("modules/demography/logger.R"))
helpers <- modules::use(here::here("modules/demography/helpers.R"))
constants <- modules::use(here::here("modules/demography/constants.R"))
REQUIRED_MODELS <- c("marriage_cohab_male",
"marriage_no_cohab_male",
"marriage_no_cohab_female")
#' Marriage
#'
#' @param world a [dymiumCore::World] object
#' @param model a model object or a list of model objects
#' @param target a positive integers or a list of positive integers
#' @param time_steps positive integer()
#'
#' @return object
run <- function(world, model = NULL, target = NULL, time_steps = NULL) {
checkmate::assert_r6(world, classes = "World")
# early return if `time_steps` is not the current time
if (!dymiumCore::is_scheduled(time_steps)) {
return(invisible(world))
}
lg$info("Running Marriage")
# get references of the relavant entities in this event
Pop <- world$get("Population")
Ind <- world$get("Individual")
Hh <- world$get("Household")
# check the model argument
model <- pick_models(model, world, REQUIRED_MODELS)
## cohabitation prior to marriage -------
TransMarriageCohabited <-
TransitionMarriageCohabited$new(
x = Ind,
model = model$marriage_cohab_male,
target = target$marriage_cohab
)
cohabiting_male_to_marry_ids <-
TransMarriageCohabited$get_result()[response == "yes", id]
Pop$log(
desc = "cnt:marriages_from_cohabitation",
value = length(cohabiting_male_to_marry_ids))
if (length(cohabiting_male_to_marry_ids) > 0) {
cohabiting_female_to_marry_ids <-
Ind$get_partner(ids = cohabiting_male_to_marry_ids)
cohabiting_person_to_marry_ids <-
c(cohabiting_male_to_marry_ids, cohabiting_female_to_marry_ids)
# update marital status
Ind$get_data(copy = FALSE) %>%
.[get(Ind$get_id_col()) %in% cohabiting_person_to_marry_ids,
marital_status := constants$IND$MARITAL_STATUS$MARRIED]
#' Check to make sure that the duplicated ids come from same-sex cohabiting
#' individuals to get married.
if (length(unique(cohabiting_person_to_marry_ids)) !=
length(cohabiting_person_to_marry_ids)) {
tab <- table(cohabiting_person_to_marry_ids)
if (any(tab > 2)) {
stop("There are some ids that appear more than twice. Please debug or report this.")
}
potential_same_sex_ind_ids <- as.integer(names(tab[tab != 1]))
#' pssind = potential_same_sex_ind
pssind_sex <- Ind$get_attr("sex", ids = potential_same_sex_ind_ids)
pssind_partner_ids <- Ind$get_attr("partner_id", ids = potential_same_sex_ind_ids)
pssind_partner_sex <- Ind$get_attr("sex", ids = pssind_partner_ids)
if (all(pssind_sex != pssind_partner_sex)) {
stop("There are duplicated ids of opposite couples in the marriage from cohabitation process: ",
paste0(potential_same_sex_ind_ids, collapse = ", "))
} else {
cohabiting_person_to_marry_ids <- unique(cohabiting_person_to_marry_ids)
}
}
checkmate::assert_integerish(
x = cohabiting_person_to_marry_ids,
lower = 1,
unique = T
)
add_history(
entity = Ind,
ids = cohabiting_person_to_marry_ids,
event = constants$EVENT$MARRIAGE_FROM_COHABITATION
)
n_marriage_cohab <- length(cohabiting_male_to_marry_ids)
} else {
n_marriage_cohab <- 0
}
## no cohabitation prior to marriage -------
# decide whether to find a partner to marry with
TransMarriageNoCohabitationMale <-
TransitionMarriageNoCohabitationMale$new(
x = Ind,
model = model$marriage_no_cohab_male,
target = target$marriage_no_cohab_male
)
TransMarriageNoCohabitationFemale <-
TransitionMarriageNoCohabitationFemale$new(
x = Ind,
model = model$marriage_no_cohab_female,
target = target$marriage_no_cohab_female
)
active_male_ids <- TransMarriageNoCohabitationMale$get_result()[response == "yes", id]
active_female_ids <- TransMarriageNoCohabitationFemale$get_result()[response == "yes", id]
# log
lg$info("{length(active_male_ids)} males and {length(active_female_ids)} \\
are entering the marriage market (ratio={ratio}:1).",
ratio = round(length(active_male_ids) / length(active_female_ids), 2))
Pop$log(
desc = "cnt:individuals_male_entered_marriage_market",
value = length(active_male_ids))
Pop$log(
desc = "cnt:individuals_female_entered_marriage_market",
value = length(active_female_ids))
## update individuals and their households -------
if (length(active_male_ids) > 0 && length(active_female_ids) > 0) {
# find a match
MarriageMarket <-
OptimalMarriageMarket$new(agentset_A = Ind$get_data(ids = active_male_ids),
agentset_B = Ind$get_data(ids = active_female_ids))
matches <-
MarriageMarket$simulate(method = "one-to-one") %>%
# remove no match
data.table:::na.omit.data.table(.) %>%
# remove potential incest matches
.[!Ind$living_together(id_A, id_B), ]
Pop$log(desc = "cnt:marriages_no_cohabitation",
value = nrow(matches))
# update partner relationship
Ind$add_relationship(ids = matches[["id_A"]],
target_ids = matches[["id_B"]],
type = "partner")
# update marital status
Ind$get_data(copy = FALSE) %>%
.[get(Ind$get_id_col()) %in% matches[, c(id_A, id_B)],
marital_status := constants$IND$MARITAL_STATUS$MARRIED]
# move-out together or move-in with partner?
# - if the male partner is living with his parent then move out to create a new household
# - if the male partner is not living with his parent then move in
# Note that, any resident children will be moved with their parent
move_out_decision_flag <-
matches[,
.(living_with_father = Ind$living_together(self_ids = id_A,
target_ids = Ind$get_attr(x = "father_id",
ids = id_A)),
living_with_mother = Ind$living_together(self_ids = id_A,
target_ids = Ind$get_attr(x = "mother_id",
ids = id_A)))] %>%
rowSums(na.rm = T) %>%
{. == 0}
move_out_decision_flag <- rep(TRUE, length(move_out_decision_flag))
Ind$log(desc = "cnt:marriage-merged_household", value = sum(!move_out_decision_flag))
Ind$log(desc = "cnt:marriage-create_new_household", value = sum(move_out_decision_flag))
# create new households that are emptied (no members yet)
Hh$add(n = sum(move_out_decision_flag))
# assign new emptied household ids to those that are moving out
matches[move_out_decision_flag, hid := Hh$get_new_agent_ids()]
# assign male household ids to those that are moving in
matches[!move_out_decision_flag, hid := Ind$get_household_ids(id_A)]
# get resident children
matches[, `:=`(
id_A_resident_children = Ind$get_resident_children(id_A),
id_B_resident_children = Ind$get_resident_children(id_B)
)]
# assign new household ids for the children to move to with their parents
resident_children <-
rbind(matches[, .(hid, resident_children = id_A_resident_children)],
matches[, .(hid, resident_children = id_B_resident_children)]) %>%
.[, lapply(.SD, unlist), by = hid] %>%
data.table:::na.omit.data.table()
#' remove resident children that are entering also cohabitation -----------
#' It is highly likely that newly cohabited people will be parted from their
#' partners if this is not applied.
resident_children <-
resident_children[!resident_children %in% matches[, c(id_A, id_B)]]
# now get moving!
Pop$leave_household(ind_ids = matches[move_out_decision_flag, ][["id_A"]])
Pop$join_household(ind_ids = matches[move_out_decision_flag, ][["id_A"]],
hh_ids = matches[move_out_decision_flag, ][["hid"]])
Pop$leave_household(ind_ids = matches[["id_B"]])
Pop$join_household(ind_ids = matches[["id_B"]],
hh_ids = matches[["hid"]])
if (nrow(resident_children) != 0) {
Pop$leave_household(ind_ids = resident_children[["resident_children"]])
Pop$join_household(ind_ids = resident_children[["resident_children"]],
hh_ids = resident_children[["hid"]])
}
add_history(entity = Pop$get("Individual"),
ids = matches[, c(id_A, id_B)],
event = constants$EVENT$MARRIAGE)
# record household size of the new households
Hh$get_data(ids = Hh$get_new_agent_ids()) %>%
.[, .(N = .N, hid = list(list(hid))), by = .(hhsize)] %>%
Hh$log(desc = "tab:hhsize_after_marriage_join", value = .)
n_marriages_no_cohab <- nrow(matches)
} else {
n_marriages_no_cohab <- 0
}
n_marriages <- n_marriage_cohab + n_marriages_no_cohab
lg$info("There were {n_marriages} marriages occured \\
(priorly cohabited: {n_marriage_cohab}, \\
did not cohabited: {n_marriages_no_cohab})")
Pop$log(
desc = "cnt:marriages",
value = n_marriages)
invisible(world)
}
# private utility functions (.util_*) -------------------------------------
allow_same_sex_partner <- function(Ind, ids) {
}
# Marriage Market classes -------------------------------------------------
StochasticMarriageMarket <- R6::R6Class(
classname = "StochasticMarriageMarket",
inherit = MatchingMarketStochastic,
public = list(
matching_score_A = function(matching_problem = self$matching_problem, idx_A, idx_B) {
scores <- 1 / (1 + abs(
matching_problem$agentset_A[["age"]][idx_A] -
matching_problem$agentset_B[["age"]][idx_B]
))
},
matching_score_B = function(matching_problem = self$matching_problem, idx_B, idx_A) {
scores <- 1 / (1 + abs(
matching_problem$agentset_B[["age"]][idx_B] -
matching_problem$agentset_A[["age"]][idx_A]
))
}
)
)
OptimalMarriageMarket <- R6::R6Class(
classname = "OptimalMarriageMarket",
inherit = MatchingMarketOptimal,
public = list(
matching_score_A = function(matching_problem, idx_A, idx_B) {
outer(X = matching_problem$agentset_B[["age"]][idx_B], # reviewers, rows
Y = matching_problem$agentset_A[["age"]][idx_A], # proposers, columns
function(x, y) {
1 / (1 + abs(x - y))
})
},
matching_score_B = function(matching_problem, idx_B, idx_A) {
outer(X = matching_problem$agentset_A[["age"]][idx_A], # reviewers, rows
Y = matching_problem$agentset_B[["age"]][idx_B], # proposers, columns
function(x, y) {
1 / (1 + abs(x - y))
})
}
)
)
# TransitionMarriageNotCohabited ------------------------------------------
# filter by targeted agent to avoid creating two classes with almost
# the same functionality: Male and Female
TransitionMarriageNoCohabitationMale <- R6Class(
classname = "TransitionMarriageNoCohabitationMale",
inherit = TransitionClassification,
public = list(
filter = function(.data) {
.data %>%
helpers$FilterAgent$Ind$can_marry(.) %>%
.[sex == constants$IND$SEX$MALE,]
},
mutate = function(.data) {
Ind <- private$.AgtObj
.data %>%
helpers$DeriveVar$IND$hhadult(x = ., Ind) %>%
helpers$DeriveVar$IND$has_children(x = ., Ind) %>%
helpers$DeriveVar$IND$has_resident_children(x = ., Ind) %>%
helpers$DeriveVar$IND$n_resident_children(x = ., Ind) %>%
helpers$DeriveVar$IND$age_youngest_resident_child(x = ., Ind) %>%
helpers$DeriveVar$IND$age_youngest_child(x = ., Ind) %>%
helpers$DeriveVar$IND$age5(x = ., Ind) %>%
helpers$DeriveVar$IND$hhsize(x = ., Ind) %>%
helpers$DeriveVar$IND$n_children(x = ., Ind)
}
)
)
TransitionMarriageNoCohabitationFemale <- R6Class(
classname = "TransitionMarriageNoCohabitationFemale",
inherit = TransitionClassification,
public = list(
filter = function(.data) {
.data %>%
helpers$FilterAgent$Ind$can_marry(.) %>%
.[sex == constants$IND$SEX$FEMALE,]
},
mutate = function(.data) {
Ind <- private$.AgtObj
.data %>%
helpers$DeriveVar$IND$hhadult(x = ., Ind) %>%
helpers$DeriveVar$IND$has_children(x = ., Ind) %>%
helpers$DeriveVar$IND$has_resident_children(x = ., Ind) %>%
helpers$DeriveVar$IND$n_resident_children(x = ., Ind) %>%
helpers$DeriveVar$IND$age_youngest_resident_child(x = ., Ind) %>%
helpers$DeriveVar$IND$age_youngest_child(x = ., Ind) %>%
helpers$DeriveVar$IND$age5(x = ., Ind) %>%
helpers$DeriveVar$IND$hhsize(x = ., Ind) %>%
helpers$DeriveVar$IND$n_children(x = ., Ind)
}
)
)
# TransitionMarriageCohabited ---------------------------------------------
TransitionMarriageCohabited <- R6Class(
classname = "TransitionMarriageCohabited",
inherit = TransitionClassification,
public = list(
filter = function(.data) {
.data %>%
helpers$FilterAgent$Ind$can_marry_from_cohabitation(.) %>%
.[sex == constants$IND$SEX$MALE]
},
mutate = function(.data) {
Ind <- private$.AgtObj
.data %>%
helpers$DeriveVar$IND$hhadult(x = ., Ind) %>%
helpers$DeriveVar$IND$has_children(x = ., Ind) %>%
helpers$DeriveVar$IND$has_resident_children(x = ., Ind) %>%
helpers$DeriveVar$IND$n_resident_children(x = ., Ind) %>%
helpers$DeriveVar$IND$age_youngest_resident_child(x = ., Ind) %>%
helpers$DeriveVar$IND$age_youngest_child(x = ., Ind) %>%
helpers$DeriveVar$IND$age5(x = ., Ind) %>%
helpers$DeriveVar$IND$hhsize(x = ., Ind) %>%
helpers$DeriveVar$IND$n_children(x = ., Ind)
}
)
)
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.