Net income after taxes and benefits

This shows a range of Ontario households.

Hide code cell source
from policyengine_canada import Microsimulation, Simulation
from policyengine_core.reforms import Reform
from policyengine_canada.model_api import *
import pandas as pd

YEAR = 2023


def make_hh(adults, children, child_age):
    people = dict(head=dict(age=30))
    members = ["head"]
    if adults == 2:
        people["spouse"] = dict(age=30)
        members += ["spouse"]
    for i in range(children):
        people[f"child{i}"] = dict(age=child_age)
        members += [f"child{i}"]
    return dict(
        people=people,
        households=dict(household=dict(members=members, province="ONTARIO")),
        # Same impact for households with $250k+ income, so show up to $300k.
        # $1k increments.
        axes=[[dict(name="employment_income", count=301, min=0, max=300_000)]],
    )


l = []
for adults in [1, 2]:
    for children in [0, 1, 2, 3]:
        for child_age in [1]:
            hh = make_hh(adults, children, child_age)
            baseline_hh = Simulation(situation=hh)
            l.append(
                pd.DataFrame(
                    dict(
                        adults=adults,
                        children=children,
                        child_age=child_age,
                        # Reshape combined array to get head's varied earnings.
                        employment_income=baseline_hh.calculate(
                            "employment_income", YEAR
                        )[0 :: (adults + children)],
                        household_net_income=baseline_hh.calculate(
                            "household_net_income", YEAR
                        ),
                        mtr=baseline_hh.calculate("marginal_tax_rate", YEAR)[
                            0 :: (adults + children)
                        ],
                    )
                )
            )

df = pd.concat(l)
df
adults children child_age employment_income household_net_income mtr
0 1 0 1 0.0 373.000000 0.000000
1 1 0 1 1000.0 1373.000000 0.000000
2 1 0 1 2000.0 2373.000000 0.000000
3 1 0 1 3000.0 3373.000000 -0.270000
4 1 0 1 4000.0 4643.000000 -0.270000
... ... ... ... ... ... ...
296 2 3 1 296000.0 205400.062500 0.461594
297 2 3 1 297000.0 205938.468750 0.461594
298 2 3 1 298000.0 206476.875000 0.461609
299 2 3 1 299000.0 207015.265625 0.461594
300 2 3 1 300000.0 207553.671875 0.461609

2408 rows × 6 columns

Hide code cell source
from policyengine_canada import Simulation

sim = Simulation(
    situation=dict(
        people=dict(
            person=dict(
                age=30,
                employment_income=20_000,
            )
        )
    )
)

sim.calculate("household_net_income")
array([21633.], dtype=float32)

Here’s an example of using axes to calculate how variables relate to each other. Income tax is a progressive schedule (for an example), and people over 65 are exempt. The chart below plots income tax by income and age together.

import plotly.express as px

px.line(
    df,
    "employment_income",
    "household_net_income",
    color="children",
    facet_col="adults",
    title="Household net income by employment income",
)
px.line(
    df,
    "employment_income",
    "mtr",
    color="children",
    facet_col="adults",
    title="Marginal tax rate by employment income",
)