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 graph and what information you can get from it.
[2]:
from gettsim import Labels, plot
The simplest way to create a plot is to use the plot.dag.tt
function, passing only a policy date.
Unfortunately, this is also unreadable due to the complexity of the tax and transfers system when looked at in its entirety.
[3]:
plot.dag.tt(policy_date_str="2025-01-01")
Looking at the signature of plot.dag.tt
, we see that it takes three types of arguments:
DAG-plotting options:
primary_nodes
selection_type
selection_depth
include_params
show_node_description
Arguments to
main
, which are the same as thereAny remaining keyword arguments are passed to
plotly.graph_objects.Figure.update
[4]:
plot.dag.tt?
A more flexible way of looking at portions of the graph is to make use of the arguments primary_nodes
and selection_type
of plot.dag.tt()
.
Focusing on a subset of the system using the plotting interface#
Primary nodes allow you to visualize only a subset of the complete graph of the tax and transfer systems. They can be passed to the primary_nodes
argument of the plot.dag.tt()
function. This is useful only in conjunction with specifying a selection type — in case you leave that out or set it to None
, the complete graph is displayed (i.e., primary_nodes
does not do anything).
Other than None
, selection_type
may take one of four values:
"neighbors"
: The neighbors (parents and children) of the primary nodes are displayed."descendants"
: All descendants of the primary nodes are displayed."ancestors"
: All ancestors of the primary nodes are displayed."all_paths"
: All paths between the primary nodes are displayed (including any other nodes lying on these paths). You must pass at least two primary nodes.
We’ll go through these in turn.
Selection type: Neighbors#
This is the example above. We pick one primary node ("betrag_y_sn"
in the "einkommensteuer"
/ "abgeltungssteuer"
namespace) and it is displayed together with its parents ("zu_versteuernde_kapitalerträge_y_sn"
and "satz"
in the same namespace) and its child ("betrag_y_sn"
in the "solidaritätszuschlag"
namespace).
[5]:
plot.dag.tt(
policy_date_str="2025-01-01",
primary_nodes=["einkommensteuer__abgeltungssteuer__betrag_y_sn"],
selection_type="neighbors",
)
By changing the selection_depth
argument from its default of 1 for neighbors, you can look at a broader vicinity in both directions. Setting it to 2 will include all grandparents and grandchildren, and so on.
[6]:
plot.dag.tt(
policy_date_str="2025-01-01",
primary_nodes=["einkommensteuer__abgeltungssteuer__betrag_y_sn"],
selection_type="neighbors",
selection_depth=2,
)
Selection type: Ancestors#
This takes a node and plots it along with all of its ancestors. Picking again "betrag_y_sn"
in the "einkommensteuer"
/ "abgeltungssteuer"
namespace, we get its two parents as above. Among them, "satz"
has no ancestors because it is a parameter of the tax system. However, "zu_versteuernde_kapitalerträge_y_sn"
has a much longer set of preceding nodes, for setting up the tax unit from primitives (marital status, joint filing) and calculating the taxable portion of all
capital income.
[7]:
plot.dag.tt(
policy_date_str="2025-01-01",
primary_nodes=["einkommensteuer__abgeltungssteuer__betrag_y_sn"],
selection_type="ancestors",
)
Again, we can use the selection_depth
argument to control how many levels of the graph we look at:
[8]:
plot.dag.tt(
policy_date_str="2025-01-01",
primary_nodes=["einkommensteuer__abgeltungssteuer__betrag_y_sn"],
selection_type="ancestors",
selection_depth=3,
)
As it happens, this is the same (up to possible layout differences) as letting the graph know that sn_id
is an input column:
[9]:
plot.dag.tt(
policy_date_str="2025-01-01",
primary_nodes=["einkommensteuer__abgeltungssteuer__betrag_y_sn"],
selection_type="ancestors",
labels=Labels(input_columns=["sn_id"]),
include_warn_nodes=False,
)
Selection type: Descendants#
This takes a node and plots it along with all of its descendants. Picking again "betrag_y_sn"
in the "einkommensteuer"
/ "abgeltungssteuer"
namespace and , we get a long DAG describing its descendants because the amount of Solidaritätszuschlag paid impacts the means-tested transfers Bürgergeld, Wohngeld, and Kinderzuschlag.
[10]:
plot.dag.tt(
policy_date_str="2025-01-01",
primary_nodes=["einkommensteuer__abgeltungssteuer__betrag_y_sn"],
selection_type="descendants",
)
Selection type: All paths#
This takes a set of nodes and plots all paths connecting them, along with any nodes lying on these paths. This can be very useful to understand the connections between two or more nodes. Picking again "betrag_y_sn"
in the "einkommensteuer"
/ "abgeltungssteuer"
namespace, we additionally add "betrag_m_bg"
in the "bürgergeld"
namespace.
This ends up being almost the same as the previous example, except that unrelated nodes (those in the "grundsicherung_im_alter"
namespace and the amounts of Wohngeld and Kinderzuschlag) are not displayed.
[11]:
plot.dag.tt(
policy_date_str="2025-01-01",
primary_nodes=[
"einkommensteuer__abgeltungssteuer__betrag_y_sn",
"bürgergeld__betrag_m_bg",
],
selection_type="all_paths",
)