Changing Parameters of the Taxes and Transfers System#

This tutorial focuses on the policy_params of GETTSIM, one of the two objects returned by the function set_up_policy_environment. It is a dictionary that contains all the date-specific parameters (e.g. level of unemployment benefit) that are necessary to compute the target variables we are interested in. GETTSIM not only provides current and past policy environments, but also allows users to alter policies for instance by changing parameters.

[1]:
import copy

import numpy as np
import pandas as pd
import plotly.express as px
from gettsim import (
    compute_taxes_and_transfers,
    create_synthetic_data,
    set_up_policy_environment,
)

Example: Increasing Child Benefit#

In this tutorial, we will implement such a policy change: a raise of the child benefit by 20 € for each child.

Small Recap: Kindergeld is a child benefit that can be claimed by parents in Germany. Mainly lower-income families benefit from Kindergeld. For higher-income families, the tax credits for children are more advantageous, so that they do not receive Kindergeld. The child benefit and how they are implemented in GETTSIM is explained in more detail in the advanced usage tutorial.

To implement the raise in GETTSIM, we will change relevant parameters in the existing system by editing the policy parameters that shape the policy environment.

Finding the Relevant Parameter#

Firstly, we can load the existing policy environment for the year 2020 using the function set_up_policy_environment.

[2]:
policy_params, policy_functions = set_up_policy_environment("2020")

We can open the dictionary with all the parameters by typing policy_params. The parameters are saved to a nested dictionary, meaning that the dictionary consists of multiple further dictionaries. This can make displaying the parameter values look confusing at first sight.

To get an idea of the structure of the dictionary, we will work us through it step by step. First we take a look at the keys of the dict. The first layer of keys represent different policy groups.

[3]:
print(*policy_params.keys(), sep="\n")
eink_st
eink_st_abzuege
soli_st
arbeitsl_geld
sozialv_beitr
unterhalt
unterhaltsvors
abgelt_st
wohngeld
kinderzuschl
kindergeld
elterngeld
ges_rente
arbeitsl_geld_2
grunds_im_alter
lohn_st

Since we are interested in altering child benefits, we will select the key “kindergeld” to inspect the according parameters further. The parameters are saved to a sub-dictionary which can be selected using policy_params["kindergeld"]. It contains the keys to different parameter groups that affect child benefits in the German system:

[4]:
print(*policy_params["kindergeld"].keys(), sep="\n")
altersgrenze
kindergeld
einkommensgrenze
stundengrenze
kinderbonus
datum
  • kindergeld_altersgrenze is the maximum age of a child that is entitled to Kindergeld.

  • kindergeld is the amount of money that parents receive for their children.

  • kindergeld_einkommensgrenze is the maximum annual income of a child that is still entitled to Kindergeld.

  • kindergeld_stundengrenze is the maximum number of weekly working hours of a child that is entitled to Kindergeld.

  • datum specifies the date to which the parameters apply in the German taxes and transfers system.

The reform we are simulating influences the amount of money that parents receive for their children. Let us take a look at the value of this parameter:

[5]:
policy_params["kindergeld"]["kindergeld"]
[5]:
{1: 204, 2: 204, 3: 210, 4: 235}

For the first and second child, the monthly Kindergeld is 204€. For the third child, monthly Kindergeld is 210€. For each additional child, Kindergeld is 235€.

Changing the Parameter#

To implement the bonus, we create a new parameter dictionary by creating a copy of the original which we can then alter.

[6]:
policy_params_new = copy.deepcopy(policy_params)

Using a loop, we add the 100€ to each entry of our new policy_params_with_bonus["kindergeld"]["kindergeld"].

[7]:
# Loop through policy paramaters to add the special child bonus.
for n in policy_params_new["kindergeld"]["kindergeld"]:
    policy_params_new["kindergeld"]["kindergeld"][n] += 20

Now we can check if the ["kindergeld"]["kindergeld"] entries in the new parameter dictionary (policy_params_new) are as expected:

[8]:
policy_params_new["kindergeld"]["kindergeld"]
[8]:
{1: 224, 2: 224, 3: 230, 4: 255}

It worked, we raised all values by 20!

Applying the Edited Parameter Dictionary to Simulated Households#

We use simulated data to illustrate the impact of the bonus we added to the Kindergeld parameters. For simplicity, we will only look at households with two children and two parents.

[9]:
data = create_synthetic_data(
    n_adults=2,
    n_children=2,
    specs_heterogeneous={
        "bruttolohn_m": [[i, 0, 0, 0] for i in np.linspace(1000, 8000, 701)]
    },
)

# Compute number of children in household and add it to data.
children = compute_taxes_and_transfers(
    data=data,
    params=policy_params,
    targets="anz_kinder_bis_17_hh",
    functions=policy_functions,
)
# Compute sum of pension contributions in household and add it to data.
sum_ges_rente_priv_rente_m = compute_taxes_and_transfers(
    data=data,
    params=policy_params,
    targets="sum_ges_rente_priv_rente_m",
    functions=policy_functions,
)
data["anz_kinder_bis_17_hh"] = children["anz_kinder_bis_17_hh"]
data["sum_ges_rente_priv_rente_m"] = sum_ges_rente_priv_rente_m[
    "sum_ges_rente_priv_rente_m"
]
data.head()
[9]:
p_id hh_id tu_id hh_typ hat_kinder alleinerz weiblich alter kind in_ausbildung ... y_pflichtbeitr_ab_40 anwartschaftszeit arbeitssuchend m_durchg_alg1_bezug sozialv_pflicht_5j kind_unterh_anspr_m kind_unterh_erhalt_m steuerklasse anz_kinder_bis_17_hh sum_ges_rente_priv_rente_m
0 0 0 0 couple_2_children True False False 35 False False ... 0.0 False False 0.0 0.0 0.0 0.0 0 2 0.0
1 1 0 0 couple_2_children True False True 35 False False ... 0.0 False False 0.0 0.0 0.0 0.0 0 2 0.0
2 2 0 0 couple_2_children False False False 8 True True ... 0.0 False False 0.0 0.0 0.0 0.0 0 2 0.0
3 3 0 0 couple_2_children False False True 3 True True ... 0.0 False False 0.0 0.0 0.0 0.0 0 2 0.0
4 4 1 1 couple_2_children True False False 35 False False ... 0.0 False False 0.0 0.0 0.0 0.0 0 2 0.0

5 rows × 68 columns

This simulated data set shows 701 households of two adults and their two children. The households/tax units only vary in their income.

First, we compute the Kindergeld with the original parameters:

[10]:
kindergeld_status_quo = compute_taxes_and_transfers(
    data=data,
    params=policy_params,
    functions=policy_functions,
    targets=["anz_erwachsene_tu", "zu_verst_eink_mit_kinderfreib_tu"],
    columns_overriding_functions=["anz_kinder_bis_17_hh", "sum_ges_rente_priv_rente_m"],
)

kindergeld_status_quo
[10]:
anz_erwachsene_tu zu_verst_eink_mit_kinderfreib_tu
0 2 0.00
1 2 0.00
2 2 0.00
3 2 0.00
4 2 0.00
... ... ...
2799 2 67926.87
2800 2 68046.87
2801 2 68046.87
2802 2 68046.87
2803 2 68046.87

2804 rows × 2 columns

[11]:
kindergeld_status_quo = compute_taxes_and_transfers(
    data=data,
    params=policy_params,
    functions=policy_functions,
    targets="kindergeld_m_tu",
    columns_overriding_functions=["anz_kinder_bis_17_hh", "sum_ges_rente_priv_rente_m"],
)

kindergeld_status_quo[["kindergeld_m_tu"]]
[11]:
kindergeld_m_tu
0 408.0
1 408.0
2 408.0
3 408.0
4 408.0
... ...
2799 408.0
2800 408.0
2801 408.0
2802 408.0
2803 408.0

2804 rows × 1 columns

Now we do exactly the same, but using the policy_params_new.

[12]:
kindergeld_new = compute_taxes_and_transfers(
    data=data,
    functions=policy_functions,
    params=policy_params_new,
    targets="kindergeld_m_tu",
    columns_overriding_functions=["anz_kinder_bis_17_hh", "sum_ges_rente_priv_rente_m"],
)
kindergeld_new[["kindergeld_m_tu"]]
[12]:
kindergeld_m_tu
0 448.0
1 448.0
2 448.0
3 448.0
4 448.0
... ...
2799 448.0
2800 448.0
2801 448.0
2802 448.0
2803 448.0

2804 rows × 1 columns

Compare Results#

Using the results shown above, we can now easily visualize and compare the amount of Kindergeld for different income groups in the two scenarios. Therefore, we reorganize our data. We create a DataFrame with the total monthly income of each tax unit and the Kindergeld they receive with the policy_params_new and the policy_params:

[13]:
# Group data by tax unit id and sum the gross monthly income.
total_income_m_tu = data.groupby("tu_id")["bruttolohn_m"].sum()
total_income_m_tu.tail(10)
[13]:
tu_id
691    7910.0
692    7920.0
693    7930.0
694    7940.0
695    7950.0
696    7960.0
697    7970.0
698    7980.0
699    7990.0
700    8000.0
Name: bruttolohn_m, dtype: float64
[14]:
# Create DataFrame with relevant columns for plotting.
df = pd.DataFrame()
df["Status Quo"] = kindergeld_status_quo["kindergeld_m_tu"]
df["After raise"] = kindergeld_new["kindergeld_m_tu"]
df["tu_id"] = data["tu_id"]
df = df.drop_duplicates("tu_id").set_index("tu_id")
df["Income (per tax unit)"] = total_income_m_tu

We can illustrate the effect of the special child bonus of 2020 by plotting the monthly Kindergeld against the income on tax unit level.

[15]:
fig = px.line(
    data_frame=df,
    x="Income (per tax unit)",
    y=["Status Quo", "After raise"],
    title="Effects of hypothetical raise of Kindergeld",
)
fig.update_layout(
    xaxis_title="Monthly gross income in € (per tax unit)",
    yaxis_title="Kindergeld in € per month",
)

Unsurprisingly, the policy reform increases the amount of money that tax units receive if they are entitled to Kindergeld.

Of course this is a very basic visualization and analysis. Have a look at the policy functions tutorial to see a more sophisticated approach for this example taking into account the interaction with ALG-II. At the same time you will learn learn how to change and add functions to a policy environment!