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!