def test_preexisting_conditions(self): """ Test the distribution of the preexisting conditions """ def _get_id(cond): return PREEXISTING_CONDITIONS[cond][0].id def _get_probability(cond_probs, age, sex): if isinstance(cond_probs, str): cond_probs = PREEXISTING_CONDITIONS[cond_probs] return next((c_prob.probability for c_prob in cond_probs if age < c_prob.age and (c_prob.sex in ('a', sex)))) n_people = 10000 rng = np.random.RandomState(1234) c_ids = set() for c_name, c_probs in PREEXISTING_CONDITIONS.items(): c_id = c_probs[0].id self.assertNotIn(c_id, c_ids) c_ids.add(c_id) # TODO: Implement test for stroke and pregnant if c_id in (_get_id('stroke'), _get_id('pregnant')): continue for c_prob in c_probs: self.assertEqual(c_prob.id, c_probs[0].id) age, sex = c_prob.age - 1, c_prob.sex expected_prob = c_prob.probability if c_id in (_get_id('cancer'), _get_id('COPD')): modifer_prob = _get_probability('smoker', age, sex) expected_prob = expected_prob * (1.3 * modifer_prob + 0.95 * (1 - modifer_prob)) if c_id == _get_id('heart_disease'): modifer_prob = _get_probability('diabetes', age, sex) + \ _get_probability('smoker', age, sex) expected_prob = expected_prob * (2 * modifer_prob + 0.5 * (1 - modifer_prob)) if c_id == _get_id('immuno-suppressed'): modifer_prob = _get_probability('cancer', age, sex) expected_prob = expected_prob * (1.2 * modifer_prob + 0.98 * (1 - modifer_prob)) if c_id == _get_id('lung_disease'): expected_prob = _get_probability('asthma', age, sex) + \ _get_probability('COPD', age, sex) computed_dist = [_get_preexisting_conditions(age, sex, rng) for _ in range(n_people)] prob = len([1 for conditions in computed_dist if c_name in conditions]) / n_people self.assertAlmostEqual(prob, expected_prob, delta=0 if not expected_prob else max(0.015, expected_prob * 0.05), msg=f"Computation of the preexisting conditions [{c_name}] yielded an " f"unexpected probability for age {age} and sex {sex}")
def test_preexisting_conditions(self): """ Test the distribution of the preexisting conditions """ n_people = 10000 rng = np.random.RandomState(1234) for c_name, c_prob in PREEXISTING_CONDITIONS.items(): for p in c_prob: computed_dist = [ _get_preexisting_conditions(p.age - 1, p.sex, rng) for _ in range(n_people) ] prob = len([cd for cd in computed_dist if p.name in cd ]) / n_people self.assertTrue( abs(p.probability - prob) < 0.01, msg=f"Computation of the preexisting conditions" f"yielded a different probability than expected" f"at {prob} instead of {p.probability}")
def __init__(self, env, name, age, rng, infection_timestamp, household, workplace, profession, rho=0.3, gamma=0.21, symptoms=[], test_results=None): self.last_date = env.timestamp.date() self.env = env self._events = [] self.name = f"human:{name}" self.rng = rng self.profession = profession self.is_healthcare_worker = True if profession == "healthcare" else False self.assign_household(household) self.workplace = workplace self.rho = rho self.gamma = gamma self.age = age self.sex = _get_random_sex(self.rng) self.preexisting_conditions = _get_preexisting_conditions(self.age, self.sex, self.rng) age_modifier = 2 if self.age > 40 or self.age < 12 else 2 # &carefulness if self.rng.rand() < P_CAREFUL_PERSON: self.carefulness = (round(self.rng.normal(55, 10)) + self.age/2) / 100 else: self.carefulness = (round(self.rng.normal(25, 10)) + self.age/2) / 100 self.has_app = self.rng.rand() < (P_HAS_APP / age_modifier) + (self.carefulness / 2) # logged info can be quite different self.has_logged_info = self.has_app and self.rng.rand() < self.carefulness self.obs_is_healthcare_worker = True if self.is_healthcare_worker and rng.random()<0.9 else False # 90% of the time, healthcare workers will declare it self.obs_age = self.age if self.has_app and self.has_logged_info else None self.obs_sex = self.sex if self.has_app and self.has_logged_info else None self.obs_preexisting_conditions = self.preexisting_conditions if self.has_app and self.has_logged_info else None self.rest_at_home = False # to track mobility due to symptoms self.visits = Visits() self.travelled_recently = self.rng.rand() > 0.9 # &symptoms, &viral-load # probability of being asymptomatic is basically 50%, but a bit less if you're older and a bit more if you're younger self.is_asymptomatic = self.rng.rand() < (BASELINE_P_ASYMPTOMATIC - (self.age - 50) * 0.5) / 100 self.asymptomatic_infection_ratio = ASYMPTOMATIC_INFECTION_RATIO if self.is_asymptomatic else 0.0 # draw a beta with the distribution in documents self.viral_load_plateau_height, self.viral_load_plateau_start, self.viral_load_plateau_end, self.viral_load_recovered = _sample_viral_load_piecewise(rng, age=age) self.infectiousness_onset_days = self.rng.normal(loc=INFECTIOUSNESS_ONSET_DAYS_AVG, scale=INFECTIOUSNESS_ONSET_DAYS_STD) self.incubation_days = self.infectiousness_onset_days + self.viral_load_plateau_start + self.rng.normal(loc=SYMPTOM_ONSET_WRT_VIRAL_LOAD_PEAK_AVG, scale=SYMPTOM_ONSET_WRT_VIRAL_LOAD_PEAK_STD) self.recovery_days = self.infectiousness_onset_days + self.viral_load_recovered self.test_result, self.test_type = None, None # Indicates whether this person will show severe signs of illness. self.infection_timestamp = infection_timestamp self.cold_timestamp = self.env.timestamp if self.rng.random() < P_COLD else None self.flu_timestamp = self.env.timestamp if self.rng.random() < P_FLU else None # different from asymptomatic self.recovered_timestamp = datetime.datetime.min self.is_immune = False # different from asymptomatic self.can_get_really_sick = self.rng.random() >= 0.8 + (age/100) self.can_get_extremely_sick = self.can_get_really_sick and self.rng.random() >= 0.7 # &severe; 30% of severe cases need ICU self.never_recovers = self.rng.random() <= P_NEVER_RECOVERS[min(math.floor(self.age/10),8)] self.obs_hospitalized = False self.obs_in_icu = False # counters and memory self.r0 = [] self.has_logged_symptoms = False self.has_logged_test = False self.last_state = self.state self.n_infectious_contacts = 0 self.last_date_to_check_symptoms = self.env.timestamp.date() # symptoms self.symptom_start_time = None self.all_cold_symptoms = _get_cold_symptoms_v2(self.age, self.rng, self.carefulness, self.preexisting_conditions, self.can_get_really_sick, self.can_get_extremely_sick) self.all_flu_symptoms = _get_flu_symptoms_v2(self.age, self.rng, self.carefulness, self.preexisting_conditions, self.can_get_really_sick, self.can_get_extremely_sick) self.all_covid_symptoms = _get_covid_symptoms(self.viral_load_plateau_start, self.viral_load_plateau_end, self.viral_load_recovered, age=self.age, incubation_days=self.incubation_days, really_sick=self.can_get_really_sick, extremely_sick=self.can_get_extremely_sick, rng=self.rng, preexisting_conditions=self.preexisting_conditions) self.all_symptoms, self.cold_symptoms, self.flu_symptoms, self.covid_symptoms = [], [], [], [] # habits self.avg_shopping_time = _draw_random_discreet_gaussian(AVG_SHOP_TIME_MINUTES, SCALE_SHOP_TIME_MINUTES, self.rng) self.scale_shopping_time = _draw_random_discreet_gaussian(AVG_SCALE_SHOP_TIME_MINUTES, SCALE_SCALE_SHOP_TIME_MINUTES, self.rng) self.avg_exercise_time = _draw_random_discreet_gaussian(AVG_EXERCISE_MINUTES, SCALE_EXERCISE_MINUTES, self.rng) self.scale_exercise_time = _draw_random_discreet_gaussian(AVG_SCALE_EXERCISE_MINUTES, SCALE_SCALE_EXERCISE_MINUTES, self.rng) self.avg_working_minutes = _draw_random_discreet_gaussian(AVG_WORKING_MINUTES, SCALE_WORKING_MINUTES, self.rng) self.scale_working_minutes = _draw_random_discreet_gaussian(AVG_SCALE_WORKING_MINUTES, SCALE_SCALE_WORKING_MINUTES, self.rng) self.avg_hospital_hours = _draw_random_discreet_gaussian(AVG_HOSPITAL_HOURS, SCALE_HOSPITAL_HOURS, self.rng) self.scale_hospital_hours = _draw_random_discreet_gaussian(AVG_SCALE_HOSPITAL_HOURS, SCALE_SCALE_HOSPITAL_HOURS, self.rng) self.avg_misc_time = _draw_random_discreet_gaussian(AVG_MISC_MINUTES, SCALE_MISC_MINUTES, self.rng) self.scale_misc_time = _draw_random_discreet_gaussian(AVG_SCALE_MISC_MINUTES, SCALE_SCALE_MISC_MINUTES, self.rng) #getting the number of shopping days and hours from a distribution self.number_of_shopping_days = _draw_random_discreet_gaussian(AVG_NUM_SHOPPING_DAYS, SCALE_NUM_SHOPPING_DAYS, self.rng) self.number_of_shopping_hours = _draw_random_discreet_gaussian(AVG_NUM_SHOPPING_HOURS, SCALE_NUM_SHOPPING_HOURS, self.rng) #getting the number of exercise days and hours from a distribution self.number_of_exercise_days = _draw_random_discreet_gaussian(AVG_NUM_EXERCISE_DAYS, SCALE_NUM_EXERCISE_DAYS, self.rng) self.number_of_exercise_hours = _draw_random_discreet_gaussian(AVG_NUM_EXERCISE_HOURS, SCALE_NUM_EXERCISE_HOURS, self.rng) #Multiple shopping days and hours self.shopping_days = self.rng.choice(range(7), self.number_of_shopping_days) self.shopping_hours = self.rng.choice(range(7, 20), self.number_of_shopping_hours) #Multiple exercise days and hours self.exercise_days = self.rng.choice(range(7), self.number_of_exercise_days) self.exercise_hours = self.rng.choice(range(7, 20), self.number_of_exercise_hours) #Limiting the number of hours spent shopping per week self.max_shop_per_week = _draw_random_discreet_gaussian(AVG_MAX_NUM_SHOP_PER_WEEK, SCALE_MAX_NUM_SHOP_PER_WEEK, self.rng) self.count_shop=0 # Limiting the number of hours spent exercising per week self.max_exercise_per_week = _draw_random_discreet_gaussian(AVG_MAX_NUM_EXERCISE_PER_WEEK, SCALE_MAX_NUM_EXERCISE_PER_WEEK, self.rng) self.count_exercise=0 self.work_start_hour = self.rng.choice(range(7, 12), 3)
def __init__(self, env, name, age, rng, infection_timestamp, household, workplace, rho=0.3, gamma=0.21, symptoms=[], test_results=None): self.env = env self.events = [] self.name = name self.rng = rng self.age = _get_random_age(self.rng) self.sex = _get_random_sex(self.rng) self.preexisting_conditions = _get_preexisting_conditions( self.age, self.sex, self.rng) self.household = household self.workplace = workplace self.location = household self.rho = rho self.gamma = gamma self.visits = Visits() self.travelled_recently = self.rng.rand() > 0.9 # &carefullness if self.rng.rand() < P_CAREFUL_PERSON: self.carefullness = round(self.rng.normal(55, 10)) + self.age / 2 else: self.carefullness = round(self.rng.normal(25, 10)) + self.age / 2 age_modifier = 1 if self.age > 40 or self.age < 12: age_modifier = 2 self.has_cold = self.rng.rand() < P_COLD * age_modifier self.has_flu = self.rng.rand() < P_FLU * age_modifier self.has_app = self.rng.rand() < (P_HAS_APP / age_modifier) + ( self.carefullness / 2) self.incubation_days = _draw_random_discreet_gaussian( AVG_INCUBATION_DAYS, SCALE_INCUBATION_DAYS, self.rng) # Indicates whether this person will show severe signs of illness. self.infection_timestamp = infection_timestamp self.recovered_timestamp = datetime.datetime.min self.really_sick = self.is_exposed and self.rng.random() >= 0.9 self.extremely_sick = self.really_sick and self.rng.random( ) >= 0.7 # &severe; 30% of severe cases need ICU self.never_recovers = self.rng.random() >= 0.99 # &symptoms, &viral-load # probability of being asymptomatic is basically 50%, but a bit less if you're older # and a bit more if you're younger self.is_asymptomatic = self.rng.rand() > (BASELINE_P_ASYMPTOMATIC - (self.age - 50) * 0.5) / 100 self.asymptomatic_infection_ratio = 0.0 if self.is_asymptomatic: self.asymptomatic_infection_ratio = ASYMPTOMATIC_INFECTION_RATIO # draw a beta with the distribution in documents self.recovery_days = _draw_random_discreet_gaussian( AVG_RECOVERY_DAYS, SCALE_RECOVERY_DAYS, self.rng) # make it IQR &recovery self.viral_load_plateau_height, self.viral_load_plateau_start, self.viral_load_plateau_end, self.viral_load_recovered = _sample_viral_load_piecewise( rng, age=age) self.all_symptoms_array = _get_all_symptoms_array( np.ndarray.item(self.viral_load_plateau_start), np.ndarray.item(self.viral_load_plateau_end), np.ndarray.item(self.viral_load_recovered), age=self.age, incubation_days=self.incubation_days, really_sick=self.really_sick, extremely_sick=self.extremely_sick, rng=self.rng, preexisting_conditions=self.preexisting_conditions) # counters and memory self.r0 = [] self.has_logged_symptoms = False self.has_logged_test = False self.n_infectious_contacts = 0 self.last_state = self.state # habits self.avg_shopping_time = _draw_random_discreet_gaussian( AVG_SHOP_TIME_MINUTES, SCALE_SHOP_TIME_MINUTES, self.rng) self.scale_shopping_time = _draw_random_discreet_gaussian( AVG_SCALE_SHOP_TIME_MINUTES, SCALE_SCALE_SHOP_TIME_MINUTES, self.rng) self.avg_exercise_time = _draw_random_discreet_gaussian( AVG_EXERCISE_MINUTES, SCALE_EXERCISE_MINUTES, self.rng) self.scale_exercise_time = _draw_random_discreet_gaussian( AVG_SCALE_EXERCISE_MINUTES, SCALE_SCALE_EXERCISE_MINUTES, self.rng) self.avg_working_hours = _draw_random_discreet_gaussian( AVG_WORKING_MINUTES, SCALE_WORKING_MINUTES, self.rng) self.scale_working_hours = _draw_random_discreet_gaussian( AVG_SCALE_WORKING_MINUTES, SCALE_SCALE_WORKING_MINUTES, self.rng) self.avg_misc_time = _draw_random_discreet_gaussian( AVG_MISC_MINUTES, SCALE_MISC_MINUTES, self.rng) self.scale_misc_time = _draw_random_discreet_gaussian( AVG_SCALE_MISC_MINUTES, SCALE_SCALE_MISC_MINUTES, self.rng) #getting the number of shopping days and hours from a distribution self.number_of_shopping_days = _draw_random_discreet_gaussian( AVG_NUM_SHOPPING_DAYS, SCALE_NUM_SHOPPING_DAYS, self.rng) self.number_of_shopping_hours = _draw_random_discreet_gaussian( AVG_NUM_SHOPPING_HOURS, SCALE_NUM_SHOPPING_HOURS, self.rng) #getting the number of exercise days and hours from a distribution self.number_of_exercise_days = _draw_random_discreet_gaussian( AVG_NUM_EXERCISE_DAYS, SCALE_NUM_EXERCISE_DAYS, self.rng) self.number_of_exercise_hours = _draw_random_discreet_gaussian( AVG_NUM_EXERCISE_HOURS, SCALE_NUM_EXERCISE_HOURS, self.rng) #Multiple shopping days and hours self.shopping_days = self.rng.choice(range(7), self.number_of_shopping_days) self.shopping_hours = self.rng.choice(range(7, 20), self.number_of_shopping_hours) #Multiple exercise days and hours self.exercise_days = self.rng.choice(range(7), self.number_of_exercise_days) self.exercise_hours = self.rng.choice(range(7, 20), self.number_of_exercise_hours) #Limiting the number of hours spent shopping per week self.max_shop_per_week = _draw_random_discreet_gaussian( AVG_MAX_NUM_SHOP_PER_WEEK, SCALE_MAX_NUM_SHOP_PER_WEEK, self.rng) self.count_shop = 0 #Limiting the number of hours spent exercising per week self.max_exercise_per_week = _draw_random_discreet_gaussian( AVG_MAX_NUM_EXERCISE_PER_WEEK, SCALE_MAX_NUM_EXERCISE_PER_WEEK, self.rng) self.count_exercise = 0 self.work_start_hour = self.rng.choice(range(7, 12))