Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Endogenous Bedarfsgemeinschaften #778

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 14 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
4 changes: 3 additions & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ releases are available on [Anaconda.org](https://anaconda.org/conda-forge/gettsi

## Unpublished

- {gh}`772` Add Mindesteinkommen check to Wohngeld, calculate anz_eig_kind_bis_24
- {gh}`778` Endogenous calculation of `bg_id` and `wthh_id` if there is at most one
Familiengemeinschaft in the household ({ghuser}`MImmesberger`, {ghuser}`hmgaudecker`).
- {gh}`777` Add Mindesteinkommen check to Wohngeld, calculate anz_eig_kind_bis_24
MImmesberger marked this conversation as resolved.
Show resolved Hide resolved
instead of requiring it as an input variable ({ghuser}`MImmesberger`).
- {gh}`771` Move SGB II Regelsatz calculation from BG to individual level
({ghuser}`MImmesberger`).
Expand Down
4 changes: 1 addition & 3 deletions docs/geps/gep-01.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,7 @@ GETTSIM knows about the following units:
- `hh_id`: Haushalt, individuals living together in a household in the Wohngeld sense
(§5 WoGG).
- `wthh_id`: Wohngeldrechtlicher Teilhaushalt, i.e. members of a household for whom the
priority check for Wohngeld/ALG2 yields the same result ∈ {True, False}. This unit is
based on the priority check via `wohngeld_vorrang_bg` and
`wohngeld_kinderzuschl_vorrang_bg`.
priority check for Wohngeld/ALG2 yields the same result ∈ {True, False}.
- `fg_id`: Familiengemeinschaft. Maximum of two generations, the relevant unit for
Bürgergeld / Arbeitslosengeld 2. Another way to think about this is the potential
Bedarfsgemeinschaft before making checks for whether children have enough income fend
Expand Down
9 changes: 9 additions & 0 deletions docs/gettsim_objects/input_variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,15 @@ Identifier of person that claims Kindergeld for the particular child

Type: int

(wohngeld_kinderzuschl_statt_arbeitsl_geld_2)=

## \`wohngeld_kinderzuschl_statt_arbeitsl_geld_2\`\`

Individual may have claim on Wohngeld and Kinderzuschlag instead of Arbeitslosengeld II
MImmesberger marked this conversation as resolved.
Show resolved Hide resolved
/ Bürgergeld

Type: bool

(wohnort_ost)=

## `wohnort_ost`
Expand Down
16 changes: 6 additions & 10 deletions docs/tutorials/policy_functions.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -76,21 +76,17 @@
"outputs": [],
"source": [
"def arbeitsl_geld_2_m_bg(\n",
" arbeitsl_geld_2_vor_vorrang_m_bg,\n",
" # wohngeld_vorrang_bg,\n",
" # kinderzuschl_vorrang_bg,\n",
" # wohngeld_kinderzuschl_vorrang_bg,\n",
" arbeitsl_geld_2_anspruchshöhe_m_bg,\n",
" # wohngeld_kinderzuschl_statt_arbeitsl_geld_2_bg,\n",
" erwachsene_alle_rentner_hh,\n",
"):\n",
" if (\n",
" # wohngeld_vorrang_bg\n",
" # | kinderzuschl_vorrang_bg\n",
" # | wohngeld_kinderzuschl_vorrang_bg\n",
" erwachsene_alle_rentner_hh\n",
" # (not wohngeld_kinderzuschl_statt_arbeitsl_geld_2_bg) and\n",
" not erwachsene_alle_rentner_hh\n",
" ):\n",
" out = 0.0\n",
" out = arbeitsl_geld_2_anspruchshöhe_m_bg\n",
" else:\n",
" out = arbeitsl_geld_2_vor_vorrang_m_bg\n",
" out = 0.0\n",
"\n",
" return out"
]
Expand Down
3 changes: 2 additions & 1 deletion src/_gettsim/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,8 @@ def set_array_backend(backend: str):
"p_id_ehepartner": int,
"p_id_einstandspartner": int,
"vermögen_bedürft": float,
"eigenbedarf_gedeckt": bool,
"arbeitsl_geld_2_eigenbedarf_gedeckt": bool,
MImmesberger marked this conversation as resolved.
Show resolved Hide resolved
"wohngeld_kinderzuschl_statt_arbeitsl_geld_2": bool,
"gemeinsam_veranlagt": bool,
"bruttolohn_m": float,
"alter": int,
Expand Down
40 changes: 40 additions & 0 deletions src/_gettsim/demographic_vars.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import numpy

from _gettsim.config import SUPPORTED_GROUPINGS
from _gettsim.shared import join_numpy, policy_info

aggregate_by_p_id_demographic_vars = {
"ges_pflegev_anz_kinder_bis_24_elternteil_1": {
Expand Down Expand Up @@ -175,6 +176,45 @@ def erwachsen(kind: bool) -> bool:
return out


@policy_info(skip_vectorization=True)
def ist_kind_in_fg(
p_id: numpy.ndarray[int],
fg_id: numpy.ndarray[int],
p_id_elternteil_1: numpy.ndarray[int],
p_id_elternteil_2: numpy.ndarray[int],
) -> numpy.ndarray[bool]:
"""Person is a child in the Familiengemeinschaft.

Parameters
----------
p_id
See basic input variable :ref:`p_id <p_id>`.
fg_id
See :func:`fg_id`.
p_id_elternteil_1
See basic input variable :ref:`p_id_elternteil_1 <p_id_elternteil_1>`.
p_id_elternteil_2
See basic input variable :ref:`p_id_elternteil_2 <p_id_elternteil_2>`.

Returns
-------
"""
fg_id_elternteil_1 = join_numpy(
p_id_elternteil_1,
p_id,
fg_id,
value_if_foreign_key_is_missing=-1,
)
fg_id_elternteil_2 = join_numpy(
p_id_elternteil_2,
p_id,
fg_id,
value_if_foreign_key_is_missing=-1,
)

return (fg_id_elternteil_1 == fg_id) | (fg_id_elternteil_2 == fg_id)


def erwachsene_alle_rentner_hh(anz_erwachsene_hh: int, anz_rentner_hh: int) -> bool:
"""Calculate if all adults in the household are pensioners.

Expand Down
115 changes: 107 additions & 8 deletions src/_gettsim/groupings.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,68 @@
def create_groupings() -> dict[str, Callable]:
return {
"wthh_id": wthh_id_numpy,
"wthh_id_endogen": wthh_id_endogen_numpy,
"fg_id": fg_id_numpy,
"bg_id": bg_id_numpy,
"bg_id_endogen": bg_id_endogen_numpy,
"eg_id": eg_id_numpy,
"ehe_id": ehe_id_numpy,
"sn_id": sn_id_numpy,
}


def bg_id_numpy(
hh_id: numpy.ndarray[int],
fg_id: numpy.ndarray[int],
alter: numpy.ndarray[int],
eigenbedarf_gedeckt: numpy.ndarray[bool],
arbeitsl_geld_2_eigenbedarf_gedeckt: numpy.ndarray[bool],
) -> numpy.ndarray[int]:
"""
Compute the ID of the Bedarfsgemeinschaft for each person.
ID of Bedarfsgemeinschaften.

If not overwritten by user-provided bg_ids, all children who cover their needs are
separated from the parental Bedarfsgemeinschaft.
"""
_fail_if_more_than_one_fg_in_hh(hh_id, fg_id)

counter = Counter()
result = []

for index, current_fg_id in enumerate(fg_id):
current_alter = alter[index]
current_eigenbedarf_gedeckt = eigenbedarf_gedeckt[index]
current_arbeitsl_geld_2_eigenbedarf_gedeckt = (
arbeitsl_geld_2_eigenbedarf_gedeckt[index]
)
# TODO(@MImmesberger): Remove hard-coded number
# https://github.com/iza-institute-of-labor-economics/gettsim/issues/668
if current_alter < 25 and current_eigenbedarf_gedeckt:
if current_alter < 25 and current_arbeitsl_geld_2_eigenbedarf_gedeckt:
counter[current_fg_id] += 1
result.append(current_fg_id * 100 + counter[current_fg_id])
else:
result.append(current_fg_id * 100)

return numpy.asarray(result)


def bg_id_endogen_numpy(

Check warning on line 53 in src/_gettsim/groupings.py

View check run for this annotation

Codecov / codecov/patch

src/_gettsim/groupings.py#L53

Added line #L53 was not covered by tests
fg_id: numpy.ndarray[int],
alle_wohngeld_kinderzuschl_statt_arbeitsl_geld_2_fg: numpy.ndarray[bool],
ist_kind_in_fg: numpy.ndarray[bool],
wohngeld_kinderzuschl_statt_arbeitsl_geld_2_endogen: numpy.ndarray[bool],
) -> numpy.ndarray[int]:
"""
Compute the ID of the Bedarfsgemeinschaft endogenously for each person.
"""
counter = Counter()
result = []

Check warning on line 63 in src/_gettsim/groupings.py

View check run for this annotation

Codecov / codecov/patch

src/_gettsim/groupings.py#L62-L63

Added lines #L62 - L63 were not covered by tests

for index, current_fg_id in enumerate(fg_id):
current_wog_kiz_statt_alg_2 = (

Check warning on line 66 in src/_gettsim/groupings.py

View check run for this annotation

Codecov / codecov/patch

src/_gettsim/groupings.py#L65-L66

Added lines #L65 - L66 were not covered by tests
wohngeld_kinderzuschl_statt_arbeitsl_geld_2_endogen[index]
)
if alle_wohngeld_kinderzuschl_statt_arbeitsl_geld_2_fg[index]:
result.append(current_fg_id * 100)
elif ist_kind_in_fg[index] and current_wog_kiz_statt_alg_2:

Check warning on line 71 in src/_gettsim/groupings.py

View check run for this annotation

Codecov / codecov/patch

src/_gettsim/groupings.py#L69-L71

Added lines #L69 - L71 were not covered by tests
counter[current_fg_id] += 1
result.append(current_fg_id * 100 + counter[current_fg_id])
else:
Expand Down Expand Up @@ -216,17 +253,79 @@

def wthh_id_numpy(
hh_id: numpy.ndarray[int],
wohngeld_vorrang_bg: numpy.ndarray[bool],
wohngeld_kinderzuschl_vorrang_bg: numpy.ndarray[bool],
fg_id: numpy.ndarray[int],
arbeitsl_geld_2_eigenbedarf_gedeckt: numpy.ndarray[bool],
ist_kind_in_fg: numpy.ndarray[bool],
) -> numpy.ndarray[int]:
"""
ID of the wohngeldrechtlicher Teilhaushalt.

If not overwritten by user-provided wthh_ids, children who cover their needs are in
the Wohngeld wthh and parents and children who do not cover their needs are in the
Arbeitslosengeld II / Bürgergeld wthh.
"""
_fail_if_more_than_one_fg_in_hh(hh_id, fg_id)

result = []
# Create candidate wthh_ids
for index, current_hh_id in enumerate(hh_id):
# Put children with covered needs in the Wohngeld wthh
if ist_kind_in_fg[index] and arbeitsl_geld_2_eigenbedarf_gedeckt[index]:
result.append(current_hh_id * 100 + 1)
# Parents and children who do not cover needs in ALG II wthh
else:
result.append(current_hh_id * 100)

return numpy.asarray(result)


def wthh_id_endogen_numpy(

Check warning on line 282 in src/_gettsim/groupings.py

View check run for this annotation

Codecov / codecov/patch

src/_gettsim/groupings.py#L282

Added line #L282 was not covered by tests
hh_id: numpy.ndarray[int],
wohngeld_kinderzuschl_statt_arbeitsl_geld_2_endogen: numpy.ndarray[bool],
) -> numpy.ndarray[int]:
"""
Compute the ID of the wohngeldrechtlicher Teilhaushalt.
Compute the ID of the wohngeldrechtlicher Teilhaushalt endogenously.
"""
result = []
for index, current_hh_id in enumerate(hh_id):
if wohngeld_vorrang_bg[index] or wohngeld_kinderzuschl_vorrang_bg[index]:
if wohngeld_kinderzuschl_statt_arbeitsl_geld_2_endogen[index]:

Check warning on line 291 in src/_gettsim/groupings.py

View check run for this annotation

Codecov / codecov/patch

src/_gettsim/groupings.py#L291

Added line #L291 was not covered by tests
result.append(current_hh_id * 100 + 1)
else:
result.append(current_hh_id * 100)

return numpy.asarray(result)


def _fail_if_more_than_one_fg_in_hh(

Check warning on line 299 in src/_gettsim/groupings.py

View check run for this annotation

Codecov / codecov/patch

src/_gettsim/groupings.py#L299

Added line #L299 was not covered by tests
hh_id: numpy.ndarray[int],
fg_id: numpy.ndarray[int],
):
"""
Fail if there is more than one `fg_id` in a household.

GETTSIM does not support the endogenous creation of Bedarfsgemeinschaften in this
case. The user has to provide `bg_id`, `wthh_id` and
`wohngeld_kinderzuschl_statt_arbeitsl_geld_2` themselves.

Parameters
----------
hh_id : numpy.ndarray[int]
Array of household IDs.
fg_id : numpy.ndarray[int]
Array of family group IDs.
"""
unique_hh_ids = numpy.unique(hh_id)
hh_ids_with_multiple_fgs_list = []
for this_hh_id in unique_hh_ids:
# Find all family group IDs for the current household ID
fg_ids_in_hh = fg_id[hh_id == this_hh_id]
if len(numpy.unique(fg_ids_in_hh)) > 1:
hh_ids_with_multiple_fgs_list.append(this_hh_id)

Check warning on line 323 in src/_gettsim/groupings.py

View check run for this annotation

Codecov / codecov/patch

src/_gettsim/groupings.py#L323

Added line #L323 was not covered by tests
hh_ids_with_multiple_fgs = set(hh_ids_with_multiple_fgs_list)
error_msg = (
"There are households with more than one `fg_id`. GETTSIM does not support the "
"endogenous creation of Bedarfsgemeinschaften in this case yet. Please provide "
"`bg_id` and `wthh_id` yourself for the following households: "
f"{hh_ids_with_multiple_fgs}."
)
assert len(hh_ids_with_multiple_fgs) == 0, error_msg
2 changes: 1 addition & 1 deletion src/_gettsim/parameters/wohngeld.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1970,7 +1970,7 @@ klimakomponente_m:
5: 39.20
jede_weitere_person: 4.8
rounding:
wohngeld_anspruchshöhe_m_bg:
wohngeld_anspruchshöhe_m_fg:
1970-01-01:
base: 1
direction: nearest
Expand Down
2 changes: 1 addition & 1 deletion src/_gettsim/synthetic.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ def create_constant_across_households_variables(df, n_adults, n_children, policy
"gemeinsam_veranlagt": (
df["kind"] == False if n_adults == 2 else False # noqa: E712
),
"eigenbedarf_gedeckt": False,
"arbeitsl_geld_2_eigenbedarf_gedeckt": False,
"mietstufe": 3,
"geburtsmonat": 1,
"geburtstag": 1,
Expand Down
Loading
Loading