Net income after taxes and benefits¶
This shows a range of Ontario households.
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
from .autonotebook import tqdm as notebook_tqdm
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
Cell In[1], line 43
31 hh = make_hh(adults, children, child_age)
32 baseline_hh = Simulation(situation=hh)
33 l.append(
34 pd.DataFrame(
35 dict(
36 adults=adults,
37 children=children,
38 child_age=child_age,
39 # Reshape combined array to get head's varied earnings.
40 employment_income=baseline_hh.calculate(
41 "employment_income", YEAR
42 )[0 :: (adults + children)],
---> 43 household_net_income=baseline_hh.calculate(
44 "household_net_income", YEAR
45 ),
46 mtr=baseline_hh.calculate("marginal_tax_rate", YEAR)[
47 0 :: (adults + children)
48 ],
49 )
50 )
51 )
53 df = pd.concat(l)
54 df
File /opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/site-packages/policyengine_core/simulations/simulation.py:491, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
488 np.random.seed(hash(variable_name + str(period)) % 1000000)
490 try:
--> 491 result = self._calculate(variable_name, period)
492 if isinstance(result, EnumArray) and decode_enums:
493 result = result.decode_to_str()
File /opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/site-packages/policyengine_core/simulations/simulation.py:721, in Simulation._calculate(self, variable_name, period)
719 try:
720 self._check_for_cycle(variable.name, period)
--> 721 array = self._run_formula(variable, population, period)
723 # If no result, use the default value and cache it
724 if array is None:
725 # Check if the variable has a previously defined value
File /opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/site-packages/policyengine_core/simulations/simulation.py:946, in Simulation._run_formula(self, variable, population, period)
944 for added_variable in adds_list:
945 if added_variable in self.tax_benefit_system.variables:
--> 946 values = values + self.calculate(
947 added_variable, period, map_to=variable.entity.key
948 )
949 else:
950 try:
File /opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/site-packages/policyengine_core/simulations/simulation.py:491, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
488 np.random.seed(hash(variable_name + str(period)) % 1000000)
490 try:
--> 491 result = self._calculate(variable_name, period)
492 if isinstance(result, EnumArray) and decode_enums:
493 result = result.decode_to_str()
File /opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/site-packages/policyengine_core/simulations/simulation.py:721, in Simulation._calculate(self, variable_name, period)
719 try:
720 self._check_for_cycle(variable.name, period)
--> 721 array = self._run_formula(variable, population, period)
723 # If no result, use the default value and cache it
724 if array is None:
725 # Check if the variable has a previously defined value
File /opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/site-packages/policyengine_core/simulations/simulation.py:946, in Simulation._run_formula(self, variable, population, period)
944 for added_variable in adds_list:
945 if added_variable in self.tax_benefit_system.variables:
--> 946 values = values + self.calculate(
947 added_variable, period, map_to=variable.entity.key
948 )
949 else:
950 try:
File /opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/site-packages/policyengine_core/simulations/simulation.py:491, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
488 np.random.seed(hash(variable_name + str(period)) % 1000000)
490 try:
--> 491 result = self._calculate(variable_name, period)
492 if isinstance(result, EnumArray) and decode_enums:
493 result = result.decode_to_str()
File /opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/site-packages/policyengine_core/simulations/simulation.py:721, in Simulation._calculate(self, variable_name, period)
719 try:
720 self._check_for_cycle(variable.name, period)
--> 721 array = self._run_formula(variable, population, period)
723 # If no result, use the default value and cache it
724 if array is None:
725 # Check if the variable has a previously defined value
File /opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/site-packages/policyengine_core/simulations/simulation.py:1011, in Simulation._run_formula(self, variable, population, period)
1009 array = formula(population, period)
1010 else:
-> 1011 array = formula(population, period, parameters_at)
1013 return array
File ~/work/policyengine-canada/policyengine-canada/policyengine_canada/variables/gov/cra/tax/income/credits/climate_action/climate_action_incentive.py:13, in climate_action_incentive.formula(household, period, parameters)
12 def formula(household, period, parameters):
---> 13 amount = household("climate_action_incentive_pre_rural", period)
14 rural = household("is_rural", period)
15 rural_percent_bonus = parameters(
16 period
17 ).gov.cra.tax.income.credits.climate_action_incentive.rural
File /opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/site-packages/policyengine_core/populations/group_population.py:38, in GroupPopulation.__call__(self, variable_name, period, options)
36 return self.sum(self.members(variable_name, period, options))
37 else:
---> 38 return super().__call__(variable_name, period, options)
File /opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/site-packages/policyengine_core/populations/population.py:142, in Population.__call__(self, variable_name, period, options)
138 return self.simulation.calculate_divide(
139 variable_name, period, **calculate_kwargs
140 )
141 else:
--> 142 return self.simulation.calculate(
143 variable_name, period, **calculate_kwargs
144 )
File /opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/site-packages/policyengine_core/simulations/simulation.py:491, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
488 np.random.seed(hash(variable_name + str(period)) % 1000000)
490 try:
--> 491 result = self._calculate(variable_name, period)
492 if isinstance(result, EnumArray) and decode_enums:
493 result = result.decode_to_str()
File /opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/site-packages/policyengine_core/simulations/simulation.py:721, in Simulation._calculate(self, variable_name, period)
719 try:
720 self._check_for_cycle(variable.name, period)
--> 721 array = self._run_formula(variable, population, period)
723 # If no result, use the default value and cache it
724 if array is None:
725 # Check if the variable has a previously defined value
File /opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/site-packages/policyengine_core/simulations/simulation.py:946, in Simulation._run_formula(self, variable, population, period)
944 for added_variable in adds_list:
945 if added_variable in self.tax_benefit_system.variables:
--> 946 values = values + self.calculate(
947 added_variable, period, map_to=variable.entity.key
948 )
949 else:
950 try:
File /opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/site-packages/policyengine_core/simulations/simulation.py:491, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
488 np.random.seed(hash(variable_name + str(period)) % 1000000)
490 try:
--> 491 result = self._calculate(variable_name, period)
492 if isinstance(result, EnumArray) and decode_enums:
493 result = result.decode_to_str()
File /opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/site-packages/policyengine_core/simulations/simulation.py:721, in Simulation._calculate(self, variable_name, period)
719 try:
720 self._check_for_cycle(variable.name, period)
--> 721 array = self._run_formula(variable, population, period)
723 # If no result, use the default value and cache it
724 if array is None:
725 # Check if the variable has a previously defined value
File /opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/site-packages/policyengine_core/simulations/simulation.py:1011, in Simulation._run_formula(self, variable, population, period)
1009 array = formula(population, period)
1010 else:
-> 1011 array = formula(population, period, parameters_at)
1013 return array
File ~/work/policyengine-canada/policyengine-canada/policyengine_canada/variables/gov/cra/tax/income/credits/climate_action/climate_action_incentive_person.py:14, in climate_action_incentive_person.formula(person, period, parameters)
12 def formula(person, period, parameters):
13 province = person.household("province_code_str", period)
---> 14 category = person("climate_action_incentive_category", period)
15 amounts = parameters(
16 period
17 ).gov.cra.tax.income.credits.climate_action_incentive.amount
18 return amounts[category][province]
File /opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/site-packages/policyengine_core/populations/population.py:142, in Population.__call__(self, variable_name, period, options)
138 return self.simulation.calculate_divide(
139 variable_name, period, **calculate_kwargs
140 )
141 else:
--> 142 return self.simulation.calculate(
143 variable_name, period, **calculate_kwargs
144 )
File /opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/site-packages/policyengine_core/simulations/simulation.py:491, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
488 np.random.seed(hash(variable_name + str(period)) % 1000000)
490 try:
--> 491 result = self._calculate(variable_name, period)
492 if isinstance(result, EnumArray) and decode_enums:
493 result = result.decode_to_str()
File /opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/site-packages/policyengine_core/simulations/simulation.py:785, in Simulation._calculate(self, variable_name, period)
777 array = np.array(
778 [
779 item.index if isinstance(item, Enum) else item
780 for item in array
781 ]
782 )
783 array = EnumArray(array, variable.possible_values)
--> 785 array = self._cast_formula_result(array, variable)
786 holder.put_in_cache(array, period, self.branch_name)
788 except SpiralError:
File /opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/site-packages/policyengine_core/simulations/simulation.py:1059, in Simulation._cast_formula_result(self, value, variable)
1057 def _cast_formula_result(self, value: Any, variable: str) -> ArrayLike:
1058 if variable.value_type == Enum and not isinstance(value, EnumArray):
-> 1059 return variable.possible_values.encode(value)
1061 if not isinstance(value, np.ndarray):
1062 population = self.get_variable_population(variable.name)
File /opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/site-packages/policyengine_core/enums/enum.py:100, in Enum.encode(cls, array)
98 invalid_values = np.unique(array[invalid_mask])
99 valid_names = [item.name for item in cls]
--> 100 raise ValueError(
101 f"Invalid value(s) {invalid_values.tolist()} for enum "
102 f"{cls.__name__}. Valid values are: {valid_names}"
103 )
104 elif array.dtype.kind in {"i", "u"}:
105 # Integer array - already indices
106 indices = array
ValueError: Invalid value(s) ['0'] for enum ClimateActionIncentiveCategory. Valid values are: ['HEAD', 'SPOUSE', 'ELDEST_CHILD_IN_SINGLE_PARENT_HOUSEHOLD', 'OTHER_CHILD']
array([18395.], 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",
)