Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 48 additions & 23 deletions src/create_initial_states/create_vaccination_priority.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,19 +95,13 @@
import pandas as pd


def create_vaccination_rank(vaccination_group, share_refuser, seed):
def create_vaccination_rank(vaccination_group):
"""Create the order in which individuals get vaccinated, including refusers.

Args:
vaccination_group (pandas.Series): index is the same as that of states.
Low values indicate individuals that have a high priority to be
vaccinated.
share_refuser (float): share of individuals (irrespective of their
vaccination group) that refuse to be vaccinated.

.. warning::
This share must also be passed to the vaccination model!
seed (int)

Returns:
vaccination_order (pandas.Series): same index as that of
Expand All @@ -117,21 +111,52 @@ def create_vaccination_rank(vaccination_group, share_refuser, seed):
rest.

"""
np.random.seed(seed)
sampled_to_refuse = np.random.choice(
a=[True, False],
size=len(vaccination_group),
p=[share_refuser, 1 - share_refuser],
)
refuser_value = vaccination_group.max() + 1
with_refusers = vaccination_group.where(~sampled_to_refuse, refuser_value)
vaccination_order = with_refusers.rank(method="first", pct=True)
vaccination_order = vaccination_group.rank(method="first", pct=True)
min_at_zero = vaccination_order - vaccination_order.min()
scaled = min_at_zero / min_at_zero.max()
return scaled


def create_vaccination_group(states, seed):
def add_refuser_value_to_vaccination_group(
states, share_refuser_adult, share_refuser_youth, seed
):
"""Create the vaccination group giving refusers the highest value.

Args:
states (pandas.DataFrame): states DataFrame, must contain
"vaccination_priority_group" and "age".
share_refuser_adult (float): share of adults (irrespective of their
vaccination group) that refuse to be vaccinated.
share_refuser_youth (float): share of youths that refuse to be vaccinated.
seed (int): seed

"""
np.random.seed(seed)
refuser_value = states["vaccination_priority_group"].max() + 1
is_adult = states["age"] >= 16

adults_sampled_to_refuse = np.random.choice(
a=[True, False],
size=len(states),
p=[share_refuser_adult, 1 - share_refuser_adult],
)
youths_sampled_to_refuse = np.random.choice(
a=[True, False],
size=len(states),
p=[share_refuser_youth, 1 - share_refuser_youth],
)

with_adult_refusers = states["vaccination_priority_group"].where(
~adults_sampled_to_refuse | ~is_adult, refuser_value
)
with_all_refusers = with_adult_refusers.where(
~youths_sampled_to_refuse | is_adult, refuser_value
)
with_all_refusers.name = None
return with_all_refusers


def create_vaccination_group_without_refusers(states, seed):
"""Put individuals into vaccination priority groups based on age and work.

Args:
Expand All @@ -147,7 +172,7 @@ def create_vaccination_group(states, seed):

"""
np.random.seed(seed)
is_adult = states["age"] >= 18
is_adult = states["age"] >= 16

vaccination_group = pd.Series(np.nan, index=states.index)

Expand Down Expand Up @@ -222,7 +247,7 @@ def _get_third_priority(states, vaccination_group):
"(60 <= age < 70) | educ_worker | work_contact_priority > 0.88"
)
third_priority_non_random = states.eval(third_priority_non_random_str)
n_third_priority_random = 0.03 * (states["age"] >= 18).sum()
n_third_priority_random = 0.03 * (states["age"] >= 16).sum()
third_priority_sampled = _sample_from_subgroups(
n_to_sample=n_third_priority_random,
states=states,
Expand Down Expand Up @@ -274,7 +299,7 @@ def _sample_from_subgroups(
n_young = int((1 - share_to_sample_above_age_cutoff) * n_to_sample)
n_old = int(share_to_sample_above_age_cutoff * n_to_sample)

pool = states[(states["age"] >= 18) & vaccination_groups_so_far.isnull()]
pool = states[(states["age"] >= 16) & vaccination_groups_so_far.isnull()]
young_pool = pool[pool["age"] < age_cutoff].index
old_pool = pool[pool["age"] >= age_cutoff].index

Expand All @@ -298,14 +323,14 @@ def _check_vaccination_group(vaccination_group, states):
youth_groups = vaccination_group[(12 <= states["age"]) & (states["age"] <= 16)]
assert (youth_groups == 5).all()

adult_groups = vaccination_group[states["age"] >= 18]
adult_groups = vaccination_group[states["age"] >= 16]
share_group_1 = (adult_groups == 1).mean()
share_group_2 = (adult_groups == 2).mean()
share_group_3 = (adult_groups == 3).mean()
share_group_4 = (adult_groups == 4).mean()
assert 0.08 < share_group_1 < 0.10, share_group_1
assert 0.15 < share_group_2 < 0.17, share_group_2
assert 0.18 < share_group_3 < 0.19, share_group_3
assert 0.15 < share_group_2 < 0.175, share_group_2
assert 0.175 < share_group_3 < 0.19, share_group_3
assert 0.55 < share_group_4 < 0.6, share_group_4
res_shares = adult_groups.value_counts(normalize=True)
target_shares = pd.Series([0.09, 0.15, 0.19, 0.57], index=[1, 2, 3, 4])
Expand Down
6 changes: 5 additions & 1 deletion src/create_initial_states/task_build_full_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,11 @@ def task_create_full_params(depends_on, produces):
# 12% are undecided. 8% are opposed to being vaccinated.
# We assume that 15% will refuse to be vaccinated.
# source: https://bit.ly/3c9mTgX (publication date: 2021-03-02)
params.loc[("vaccinations", "share_refuser", "share_refuser"), "value"] = 0.15
params.loc[("vaccinations", "share_refuser", "adult"), "value"] = 0.15
# for 12 to 15 year olds, there are more refusers: 44 to 66% are willing to
# vaccinate their child (https://bit.ly/3BdHHgJ). Thus, for them the share
# of refusers is 34 to 56%
params.loc[("vaccinations", "share_refuser", "youth"), "value"] = 0.45

# source: https://bit.ly/3gHlcKd (section 3.5, 2021-03-09, accessed 2021-04-28)
# 82% say they would get a PCR test after a positive rapid test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
add_contact_model_group_ids,
)
from src.create_initial_states.create_vaccination_priority import (
create_vaccination_group,
add_refuser_value_to_vaccination_group,
)
from src.create_initial_states.create_vaccination_priority import (
create_vaccination_group_without_refusers,
)
from src.create_initial_states.create_vaccination_priority import (
create_vaccination_rank,
Expand Down Expand Up @@ -81,8 +84,11 @@ def task_create_initial_states_microcensus(depends_on, n_hhs, produces):
other_daily_dist = pd.read_pickle(depends_on["other_daily_dist"])
other_weekly_dist = pd.read_pickle(depends_on["other_weekly_dist"])
params = pd.read_pickle(depends_on["params"])
no_vaccination_share = params.loc[
("vaccinations", "share_refuser", "share_refuser"), "value"
no_vaccination_share_adult = params.loc[
("vaccinations", "share_refuser", "adult"), "value"
]
no_vaccination_share_youth = params.loc[
("vaccinations", "share_refuser", "youth"), "value"
]

df = _build_initial_states(
Expand All @@ -94,7 +100,8 @@ def task_create_initial_states_microcensus(depends_on, n_hhs, produces):
other_weekly_dist=other_weekly_dist,
n_households=n_hhs,
seed=3933,
no_vaccination_share=no_vaccination_share,
no_vaccination_share_adult=no_vaccination_share_adult,
no_vaccination_share_youth=no_vaccination_share_youth,
)
df.to_parquet(produces)

Expand All @@ -108,7 +115,8 @@ def _build_initial_states(
other_weekly_dist,
n_households,
seed,
no_vaccination_share,
no_vaccination_share_adult,
no_vaccination_share_youth,
):
mc = _prepare_microcensus(mc)

Expand Down Expand Up @@ -146,9 +154,17 @@ def _build_initial_states(
df["adult_in_hh_at_home"] = adult_at_home.groupby(df["hh_id"]).transform(np.any)
df["educ_contact_priority"] = _create_educ_contact_priority(df)

df["vaccination_group"] = create_vaccination_group(states=df, seed=484)
df["vaccination_priority_group"] = create_vaccination_group_without_refusers(
states=df, seed=484
)
df["vaccination_group_with_refuser_group"] = add_refuser_value_to_vaccination_group(
states=df,
share_refuser_adult=no_vaccination_share_adult,
share_refuser_youth=no_vaccination_share_youth,
seed=909,
)
df["vaccination_rank"] = create_vaccination_rank(
df["vaccination_group"], share_refuser=no_vaccination_share, seed=909
vaccination_group=df["vaccination_group_with_refuser_group"]
)

# This is uncorrelated with the work contact priority.
Expand Down Expand Up @@ -339,7 +355,7 @@ def _only_keep_relevant_columns(df):
"hh_model_group_id",
"adult_in_hh_at_home",
"educ_contact_priority",
"vaccination_group",
"vaccination_group_with_refuser_group",
"vaccination_rank",
"rapid_test_compliance",
"quarantine_compliance",
Expand Down Expand Up @@ -370,6 +386,7 @@ def _only_keep_relevant_columns(df):
"index",
"stays_home_when_schools_close", # not used at the moment
"work_type",
"vaccination_priority_group",
]

assert set(keep + to_drop) == set(df.columns)
Expand Down
89 changes: 57 additions & 32 deletions src/policies/find_people_to_vaccinate.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import warnings

import numpy as np
import pandas as pd
from sid import get_date

Expand All @@ -8,37 +9,46 @@ def find_people_to_vaccinate(
receives_vaccine, # noqa: U100
states,
params,
seed, # noqa: U100
seed,
vaccination_shares,
init_start,
):
"""Find people that have to be vaccinated on a given day.

On the init_start date all individuals that should have been vaccinated until
that day get vaccinated. Since vaccinations take effect within three weeks
and the burn in period is four weeks this does not lead to jumps in the
simulation period.
On the init_start date all individuals that should have been vaccinated
until that day get vaccinated. Since vaccinations take effect within three
weeks and the burn in period is four weeks this does not lead to jumps in
the simulation period.

On June 7 the vaccination priorization was lifted in Germany. On May 28 the
EMA approved the first vaccine for youths from 12 to 15. However, until July
15 less than 5% of 12 to 17 year olds were vaccinated and the STIKO only
recommended the CoViD vaccine for healthy 12 to 17 year olds in August.

We model this by vaccinating individuals 12 and older that are not refusers
randomly starting on July 15.

Sources:
- https://bit.ly/2WpIegM
- https://bit.ly/3zlzIhe

Args:
states (pandas.DataFrame): States DataFrame that must contain the
column vaccination_rank. This column is a float with values
between zero and one. Low values mean that people get
vaccinated early.
params (pandas.DataFrame): not used.
seed (int): not used.
vaccination_shares (pandas.Series): Series with a date index. For each
day the value indicates the share of people who get vaccinated on
that day.
no_vaccination_share (float): Share of people who refuse to get
vaccinated.
init_start (pd.Timestamp): start date of the burn in period. On the
init_start all vaccinations that have been done until then are
handed out on that day.
column vaccination_rank. This column is a float with values between zero
and one. Low values mean that people get vaccinated early.
params (pandas.DataFrame): params DataFrame that contains a row labeled
("vaccinations", "share_refuser", "adult").
seed (int): used after 2021-06-07.
vaccination_shares (pandas.Series): Series with a date index. For each day the
value indicates the share of people who get vaccinated on that day.
init_start (pd.Timestamp): start date of the burn in period. On the init_start
all vaccinations that have been done until then are handed out on that day.

"""
np.random.seed(seed)
date = get_date(states)
no_vaccination_share = params.loc[
("vaccinations", "share_refuser", "share_refuser"), "value"
no_vaccination_share_adult = params.loc[
("vaccinations", "share_refuser", "adult"), "value"
]

if not (vaccination_shares < 0.05).all():
Expand All @@ -47,18 +57,33 @@ def find_people_to_vaccinate(
"If this was intended, simply ignore the warning.",
)

cutoffs = vaccination_shares.sort_index().cumsum()
# set all cutoffs before the init_start to 0.
# that way on the init_start date everyone who should have been vaccinated
# until that day gets vaccinated.
cutoffs[: init_start - pd.Timedelta(days=1)] = 0
if date < pd.Timestamp("2021-07-15"): # priorisation. only >= 16 get vaccinated.
cutoffs = vaccination_shares.sort_index().cumsum()
# set all cutoffs before the init_start to 0.
# that way on the init_start date everyone who should have been vaccinated
# until that day gets vaccinated.
cutoffs[: init_start - pd.Timedelta(days=1)] = 0

lower_candidate = cutoffs[date - pd.Timedelta(days=1)]
upper_candidate = cutoffs[date]
lower = min(lower_candidate, 1 - no_vaccination_share_adult)
upper = min(upper_candidate, 1 - no_vaccination_share_adult)

lower_candidate = cutoffs[date - pd.Timedelta(days=1)]
upper_candidate = cutoffs[date]
lower = min(lower_candidate, 1 - no_vaccination_share)
upper = min(upper_candidate, 1 - no_vaccination_share)
to_vaccinate = (lower <= states["vaccination_rank"]) & (
states["vaccination_rank"] < upper
)
else: # no priorisation. >= 12 get vaccinated
unvaccinated = ~states["ever_vaccinated"]
eligible = states["age"] >= 12

# the highest value denotes refusers
willing = (
states["vaccination_group_with_refuser_group"]
< states["vaccination_group_with_refuser_group"].max()
)
pool = states[unvaccinated & eligible & willing].index
n_to_draw = int(vaccination_shares[date] * len(states))
sampled = np.random.choice(pool, n_to_draw, replace=False)
to_vaccinate = pd.Series(states.index.isin(sampled), index=states.index)

to_vaccinate = (lower <= states["vaccination_rank"]) & (
states["vaccination_rank"] < upper
)
return to_vaccinate
Loading