Different Ways to Load Policy Functions#

Introduction#

This how-to guide is about the different ways you can pass policy functions to the functions argument of compute_taxes_and_transfers() which will extend or replace parts of the current policy environment.

[1]:
from gettsim import config, set_up_policy_environment

This tutorial starts after the user called set_up_policy_environment and received the policy_parameters and policy_functions.

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

A Single Function#

One way to pass a single function to the tax and transfer system is alongside the policy_functions. As an example, we create a function called kindergeld_m_hh. The function has no body because it is irrelevant for this guide. The function can be passed to compute_taxes_and_transfers alongside the policy_functions by placing both objects in a list.

[3]:
def kindergeld_m_hh():
    pass

Here is how you would call compute_taxes_and_transfers:

df = compute_taxes_and_transfers(
    data=data,
    params=policy_params,
    functions=[policy_functions, kindergeld_m_hh],
    targets="kindergeld_m",
)

There are three important points.

  1. Note that, kindergeld_m_hh has the same function name as a pre-defined function inside gettsim. Thus, the internal function will be replaced with this version.

  2. In general, if there are multiple functions with the same name, internal functions have the lowest precedence. After that, the elements in the list passed to the functions argument are evaluated element by element. The leftmost element has the lowest precedence and the rightmost element the highest.

  3. If policy_functions would not be necessary for this example, you can also directly pass the kindergeld_m_hh function to the functions argument.

    df = compute_taxes_and_transfers(
        ...,
        functions=kindergeld_m_hh,
        ...,
    )
    

Multiple Functions#

If you want to pass multiple functions to compute_taxes_and_transfers, add all functions to the list. Assume we also want to override kindergeld_m and have a function for that as well. Then, the call looks like this:

df = compute_taxes_and_transfers(
    data=data,
    params=policy_params,
    functions=[policy_functions, kindergeld_m_hh, kindergeld_m],
    targets="kindergeld_m",
)

Renaming Functions#

Assume you want to analyze the effects of multiple different child benefit policies with different functions. Usually, you would group them in one module, say kindergeld.py. You cannot give them all the same name and it is also preferred to give them self-explanatory names.

[4]:
# Content of kindergeld.py


def kindergeld_m_hh_constant_per_hh():
    pass


def kindergeld_m_hh_constant_per_child():
    pass

Since the functions do not have the same name as the original function kindergeld_m_hh, they would not override this function if we pass one of them to compute_taxes_and_transfers. To solve this issue, we can use dictionaries.

The keys of the dictionary are the names which will be used for the functions which are the values of the dictionary. The following code snippet shows the pseudo-code for the task (note that the index of simulated_data would eventually contain repeated values, in an actual application you would want to add a level indicating the type of function used):

simulated_data = []
for func in [
    kindergeld_m_hh_constant_per_hh,
    kindergeld_m_hh_constant_per_child,
]:
    df = compute_taxes_and_transfers(
        data=data,
        params=policy_params,
        functions=[policy_functions, {"kindergeld_m_hh": func}],
        targets="kindergeld_m",
    )
    simulated_data = pd.concat(objs=[simulated_data, df])

Paths to Packages or Modules#

If you have heavily extended the tax and transfer system to your needs, you might have a .py file with several functions or even several .py files with several functions. Assume you have a folder taxes which has the following directory structure:

taxes
│   abgelt_st.py
│   eink_st.py
│   favorability_check.py
│   kindergeld.py
│   soli_st.py
│   __init__.py
│
├───zu_verst_eink
│       eink.py
│       freibeträge.py
│       vorsorgeaufw.py
│       zu_verst_eink.py
│       __init__.py

Indeed, the folder structure exists inside gettsim and we can locate it with

[5]:
path = config.RESOURCE_DIR.joinpath("taxes")
path
[5]:
PosixPath('/home/docs/checkouts/readthedocs.org/user_builds/gettsim/checkouts/stable/src/_gettsim/taxes')

To collect only functions from abgelt_st.py, you can pass the path to the file as a string or as a pathlib.Path.

[6]:
path_str = str(path)
path_str
[6]:
'/home/docs/checkouts/readthedocs.org/user_builds/gettsim/checkouts/stable/src/_gettsim/taxes'
[7]:
# Path as string.
functions = path_str + "\\abgelt_st.py"
functions
[7]:
'/home/docs/checkouts/readthedocs.org/user_builds/gettsim/checkouts/stable/src/_gettsim/taxes\\abgelt_st.py'
[8]:
functions = path / "abgelt_st.py"
functions
[8]:
PosixPath('/home/docs/checkouts/readthedocs.org/user_builds/gettsim/checkouts/stable/src/_gettsim/taxes/abgelt_st.py')

To collect multiple Python files, use a list of paths or strings.

[9]:
functions = [path / "eink_st.py", path / "zu_verst_eink" / "eink.py"]
functions
[9]:
[PosixPath('/home/docs/checkouts/readthedocs.org/user_builds/gettsim/checkouts/stable/src/_gettsim/taxes/eink_st.py'),
 PosixPath('/home/docs/checkouts/readthedocs.org/user_builds/gettsim/checkouts/stable/src/_gettsim/taxes/zu_verst_eink/eink.py')]

To collect all Python files in the folder taxes, use

[10]:
functions = list(path.glob("*.py"))
functions
[10]:
[PosixPath('/home/docs/checkouts/readthedocs.org/user_builds/gettsim/checkouts/stable/src/_gettsim/taxes/soli_st.py'),
 PosixPath('/home/docs/checkouts/readthedocs.org/user_builds/gettsim/checkouts/stable/src/_gettsim/taxes/__init__.py'),
 PosixPath('/home/docs/checkouts/readthedocs.org/user_builds/gettsim/checkouts/stable/src/_gettsim/taxes/lohn_st.py'),
 PosixPath('/home/docs/checkouts/readthedocs.org/user_builds/gettsim/checkouts/stable/src/_gettsim/taxes/eink_st.py'),
 PosixPath('/home/docs/checkouts/readthedocs.org/user_builds/gettsim/checkouts/stable/src/_gettsim/taxes/abgelt_st.py')]

If you want to pass all functions defined in Python files in taxes and directories below, you can simply pass a directory as the function. Paths to directories will be recursively searched for Python files.

[11]:
functions = str(path)
functions
[11]:
'/home/docs/checkouts/readthedocs.org/user_builds/gettsim/checkouts/stable/src/_gettsim/taxes'
[12]:
functions = path
functions
[12]:
PosixPath('/home/docs/checkouts/readthedocs.org/user_builds/gettsim/checkouts/stable/src/_gettsim/taxes')

Modules#

To load all functions from a module, you can provide an import statement as a string.

[13]:
functions = "gettsim.taxes.abgelt_st"
functions
[13]:
'gettsim.taxes.abgelt_st'
[ ]: