Visualizing the Taxes and Transfers System#

How to create a plot#

To help you understand how GETTSIM works internally and how you are able to implement custom reforms, you can visualize the tax and transfer system. This tutorial explains how to create a graphic and what information you can get from it. It also explains GETTSIM’s design to some extent.

[1]:
from gettsim import plot_dag, set_up_policy_environment

For the visualization, we need to set up our policy environment.

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

Functions inside GETTSIM are a little bit special. Take for example abgelt_st_tu which is documented here. The signature of the function is

def abgelt_st_tu(zu_verst_kapitaleink_tu, abgelt_st_params):
    pass

This functions has two arguments and one of them passes parameters to the function. Most functions require some parameters, but it is not necessary. The names of the arguments correspond to either a variable in the data provided by the user or to another function which, in turn, also relies on some arguments.

Here, abgelt_st_params is a paramter file which includes paramters needed to calculate the capital income tax. zu_verst_kapitaleink_tu on the other hand is itself a function which is documented here. By using zu_verst_kapitaleink_tu as an argument name, GETTSIM knows to pass the data computed by the function zu_verst_kapitaleink_tu to abgelt_st_tu.

This dependency relationship can be analyzed for all functions passed to GETTSIM and be visualized in a dag. Below you can see a plot of all variables which are directly connected to zu_verst_kapitaleink_tu. The arrows point from dependencies to dependents. Each node is either a functions or a variable.

[3]:
plot_dag(
    functions=policy_functions,
    selectors=[{"node": "zu_verst_kapitaleink_tu", "type": "neighbors"}],
).show()

The general interface of the plotting function is similar to compute_taxes_and_transfers(), but without the data and params argument. Here is the complete signature.

[4]:
plot_dag?

In the following, you see many ways to either select different subsets of the graph or style the plot.

Orientation#

By default orientation of graph is vertical. It is possible to change the orientation of graph to horizontal one by setting plot_dag(..., orientation='h')

[5]:
plot_dag(
    functions=policy_functions,
    selectors=[{"node": "zu_verst_kapitaleink_tu", "type": "neighbors"}],
    orientation="h",
).show()

Labels#

By default (when show_labels is set to its default of None), all all names are displayed next to the node whenever the number of nodes is smaller than 10. For 10 nodes or more like in the example below, the names are by default displayed next to the node only when hovering over it.

[6]:
plot_dag(
    functions=policy_functions,
    selectors=[{"node": "geringfügig_beschäftigt", "type": "descendants"}],
    orientation="h",
).show()

show_labels can be also set to True or False to enfore displaying or not displaying all labels.

[7]:
selector = {"type": "descendants", "node": "geringfügig_beschäftigt"}
plot_dag(
    functions=policy_functions, show_labels=False, selectors=selector, orientation="h"
).show()

Hover info (source code)#

It is also possible to address the source code of the function. By setting plot_dag(..., hover_source_code=True)

[8]:
plot_dag(
    functions=policy_functions,
    selectors=[{"node": "zu_verst_kapitaleink_tu", "type": "neighbors"}],
    orientation="h",
    hover_source_code=True,
).show()

Selectors#

Selectors allow you to visualize only a subset of the complete graph of the tax and transfer systems. They can be passed to the selectors argument of the plot_dag() function. There exist some ways to define a selector and they can be combined with one another. Let us discuss each selector on its own first.

Basics#

It is always possible to pass a string or a list of strings to selectors. In this case only the given nodes are displayed in the plot.

[9]:
selectors = "kapitaleink_brutto_tu"

plot_dag(functions=policy_functions, selectors=selectors, orientation="h").show()

Using a list of variable names, we can select multiple nodes.

[10]:
selectors = ["kapitaleink_brutto_tu", "zu_verst_kapitaleink_tu"]

plot_dag(functions=policy_functions, selectors=selectors, orientation="h").show()

Passing a string or a list of strings to selectors is actually a shortcut for the richer interface for selecting nodes. Selectors are usually represented as dictionaries. The corresponding dictionary for selecting a list of nodes is

[11]:
selector = {
    "type": "nodes",
    "node": ["kapitaleink_brutto_tu", "zu_verst_kapitaleink_tu"],
    "select": True,  # optional
}

Let us go through the keys of the dictionary one by one.

  1. "type" specifies the type of the selector. For a single node or a list of nodes the type is "nodes".

  2. "node" always refers to the node or nodes to which the selector is applied. In this case, it is the list of node names.

  3. "select" specifies whether the nodes should be selected or de-selected. If you do not specify "select" it is assumed to be True.Let us go through the keys of the dictionary one by one.

De-selecting Nodes#

It is also possible to specify selectors which de-select some nodes. Note that,

  • De-selectors are applied after nodes have been selected.

  • If no selectors are provided, de-selectors de-select nodes from the complete DAG.

  • Selection and de-selection works for all selector types which follow.

For a simple and silly example, we want to reproduce the graph with the single node for kapitaleink_brutto_tu after, but starting from the last plot which also showed zu_verst_kapitaleink_tu.

First, we define the selectors. The first selector or dictionary in the list selects the two nodes. Note that the "select" key is True by default. The second key in the de-selects "zu_verst_kapitaleink_tu".

[12]:
selectors = [
    {
        "type": "nodes",
        "node": ["kapitaleink_brutto_tu", "zu_verst_kapitaleink_tu"],
    },
    {
        "type": "nodes",
        "node": "zu_verst_kapitaleink_tu",
        "select": False,
    },
]
plot_dag(functions=policy_functions, selectors=selectors, orientation="v").show()

Ancestors and Descendants#

Two other types of selectors allow you to pick one node and all nodes which appear before or after this node. We call the nodes ancestors or descendants, respectively. To select "zu_verst_kapitaleink_tu" which is the calculated taxable capital income per tax unit and all its ancestors, do the following.

[13]:
selector = {"type": "ancestors", "node": "zu_verst_kapitaleink_tu"}
plot_dag(functions=policy_functions, selectors=selector, orientation="h").show()

To see the variables which are explicitly and implicitly dependent on the information in "geringfügig_beschäftigt" use the type "descendants".

[14]:
selector = {"type": "descendants", "node": "geringfügig_beschäftigt"}
plot_dag(functions=policy_functions, selectors=selector, orientation="h").show()

Neighbors#

Another common way to look at a graph is to visualize a node and its neighbors, its ancestors and descendants. Let us take a look at "kapitaleink_brutto_tu" again and visualize its direct neighbors.

[15]:
selector = {"type": "neighbors", "node": "kapitaleink_brutto_tu"}
plot_dag(functions=policy_functions, selectors=selector, orientation="h").show()

It is also possible to look at more distant neighbors or neighbors of order 2, 3, … . This can be done by the "order" key which is 1 by default.

[16]:
selector = {"type": "neighbors", "node": "kapitaleink_brutto_tu", "order": 2}
plot_dag(functions=policy_functions, selectors=selector, orientation="h").show()