This notebook aim to show easily how periods work in OpenFisca.
We need to initialize a simulation.
from openfisca_france import FranceTaxBenefitSystem
tax_benefit_system = FranceTaxBenefitSystem()
scenario = tax_benefit_system.new_scenario()
scenario.init_single_entity(
period = 2015,
parent1 = dict(
age = 30,
salaire_de_base = 50000,
),
enfants = [
dict(age = 12),
dict(age = 18),
],
)
simulation = scenario.new_simulation()
A simulation's variable is calculated for a specific period.
This specific time interval have to be given when calling a variable to avoid computation problem.
Some variables are computed over the month.
For exemple the variable allocation familiales
.
If called on an annual basis, an error is displayed :
simulation.calculate('af', '2015')
AssertionErrorTraceback (most recent call last) <ipython-input-2-8a82c7f215b6> in <module>() ----> 1 simulation.calculate('af', '2015') /home/notebook/virtualenvs/openfisca/local/lib/python2.7/site-packages/openfisca_core/simulations.pyc in calculate(self, column_name, period, **parameters) 72 if period is None: 73 period = self.period ---> 74 return self.compute(column_name, period = period, **parameters).array 75 76 def calculate_add(self, column_name, period = None, **parameters): /home/notebook/virtualenvs/openfisca/local/lib/python2.7/site-packages/openfisca_core/simulations.pyc in compute(self, column_name, period, **parameters) 157 caller_input_variables_infos.append(variable_infos) 158 holder = self.get_or_new_holder(column_name) --> 159 result = holder.compute(period = period, **parameters) 160 if print_trace: 161 self.print_trace(variable_name=column_name, period=period, **print_trace_kwargs) /home/notebook/virtualenvs/openfisca/local/lib/python2.7/site-packages/openfisca_core/holders.pyc in compute(self, period, **parameters) 140 assert accept_other_period or formula_dated_holder.period == period, \ 141 "Requested period {} differs from {} returned by variable {}".format(period, --> 142 formula_dated_holder.period, column.name) 143 return formula_dated_holder 144 array = np.empty(entity.count, dtype = column.dtype) AssertionError: Requested period 2015 differs from 2015-01 returned by variable af
This assertion error :
"Requested period 2015 differs from 2015-01 returned by variable af
"
means exactly the fact that af
are computed for a month, therefore the variable returned value for 2015-01
.
But when called on its period (monthly), the result of the variable's formula will be returned
simulation.calculate('af', '2015-01') #calculate variable af for January 2015
array([ 129.99000549], dtype=float32)
Other formulas only works on a annual basis, thus a monthly call will not work, e.g irpp (impôt sur le revenu) :
simulation.calculate('irpp', '2015-01')
AssertionErrorTraceback (most recent call last) <ipython-input-4-c5f820ae8a5c> in <module>() ----> 1 simulation.calculate('irpp', '2015-01') /home/notebook/virtualenvs/openfisca/local/lib/python2.7/site-packages/openfisca_core/simulations.pyc in calculate(self, column_name, period, **parameters) 72 if period is None: 73 period = self.period ---> 74 return self.compute(column_name, period = period, **parameters).array 75 76 def calculate_add(self, column_name, period = None, **parameters): /home/notebook/virtualenvs/openfisca/local/lib/python2.7/site-packages/openfisca_core/simulations.pyc in compute(self, column_name, period, **parameters) 157 caller_input_variables_infos.append(variable_infos) 158 holder = self.get_or_new_holder(column_name) --> 159 result = holder.compute(period = period, **parameters) 160 if print_trace: 161 self.print_trace(variable_name=column_name, period=period, **print_trace_kwargs) /home/notebook/virtualenvs/openfisca/local/lib/python2.7/site-packages/openfisca_core/holders.pyc in compute(self, period, **parameters) 140 assert accept_other_period or formula_dated_holder.period == period, \ 141 "Requested period {} differs from {} returned by variable {}".format(period, --> 142 formula_dated_holder.period, column.name) 143 return formula_dated_holder 144 array = np.empty(entity.count, dtype = column.dtype) AssertionError: Requested period 2015-01 differs from 2015 returned by variable irpp
It must be called on an annual basis :
simulation.calculate('irpp', '2015') #calculate variable irpp for 2015
array([-2400.52807617], dtype=float32)
When a variable is called without period, the simulation's default period is used.
simulation.period
Period((u'year', Instant((2015, 1, 1)), 1))
Here default simulation period is a year. Thus the variable irpp
can be computed but not the variable af
.
print(simulation.calculate('irpp'))
[-2400.52807617]
In this case simulation.calculate('irpp') is equivalent to simulation.calculate('irpp', '2015')
print(simulation.calculate('irpp') == simulation.calculate('irpp', '2015'))
[ True]
WARNING : this demonstration shows the necessity of being aware over which kind of period each measure you want to compute is based on.
If you've forgotten over which kind of period (year or month) your variable is defined in the legislation, you may check in the legislation explorer.
The information is located in the code source.
Example for the irpp, a tax with annual definition, you will find the period at the end of the website page:
Specific period calls insure that no errors are made by the user.
If a annual based variable is asked to be computed monthly, the software returns an error.
But there is solutions to get the result of a given variable on another kind of period, a annual based variable over a month for example.
The calculate_add method has the same behavior as calculate, except that it sum all variables given their instant of calculus over a period lenght.
print simulation.calculate_add("af", "2015")
[ 1559.88000488]
This result is equivalent to do sum all monthly calculate over the period.
annual_af = 0 #create annual_af equals to 0
for month in range(1,13): # [1,2,...,11,12]
annual_af += simulation.calculate("af", '2015-{}'.format(month)) #add recursively af for all month in 2014
print annual_af
[ 1559.88000488]
We thus see that annual_af is equal to simulation.calculate_add('af', "2014").
We can test that :
print simulation.calculate_add('af','2015') == annual_af
[ True]
simulation.calculate_divide("irpp", "2015-01")
array([-200.04400635], dtype=float32)
simulation.calculate("irpp", "2015")/12
array([-200.04400635], dtype=float32)
Be warned, when a variable is calculated on a monthly basis with calculate_divide (or add), the result is stored in cache.
Now you can do :
simulation.calculate('irpp', '2015-01')
array([-200.04400635], dtype=float32)
Some variables are not constant over the year but you might want it as an average value for each month.
For exemple variable 'ars'
(allocation de rentrée scolaire), is computed only in september. To smooth it over the year, we must use calculate_add_divide. It will simply sum it over a given period, and then divide it over sub-periods of that period.
simulation.calculate('ars', '2014')/12
array([ 65.20347595], dtype=float32)
simulation.calculate_add_divide('ars', "2014-08")
array([ 49.92318344], dtype=float32)
Que fait add_divide ?
simulation.compute_add_divide??
We can also use more exotic period by using the class periods.
Actually when we do : simulation.calculate('irpp','2015')
, the '2015' string is converted into an object period.
A more explicit way to do this, is : simulation.calculate('irpp', periods.period('2015'))
from openfisca_core import periods # OpenFisca_core is the architecture of OpenFisca
periods.period('2015')
Period((u'year', Instant((2015, 1, 1)), 1))
print(simulation.calculate('irpp', periods.period('2015')))
print(simulation.calculate('irpp', periods.period('2015'))== simulation.calculate('irpp', 2015))
[-2400.52807617] [ True]
We can look at periods docstring to understand how it works :
print periods.period.__doc__[: 280] #periods.period? would also display the help, suppress the brackets to get full doc
Return a new period, aka a triple (unit, start_instant, size). >>> period(u'2014') Period((u'year', Instant((2014, 1, 1)), 1)) >>> period(u'2014:2') Period((u'year', Instant((2014, 1, 1)), 2)) >>> period(u'2014-2') Period((u'month', Instant((2014, 2, 1)),
A period is constituted of three parameters:
periods.period('day','2014-03-01', 32) would be a period going from the first of March to the first of april
If we want to calculate the 'allocation familiales' from the April to July.
af_march_to_july = simulation.calculate_add('af' ,periods.period('month', '2015-04', 4))
print af_march_to_july
[ 519.96002197]
A simplificated version of period declaration exists with the symbol: ":"
.
year-month:n
means n months beginning at the month, year.
Example with af_march_to_july
:
af_march_to_july = simulation.calculate_add('af' ,'2015-04:4')
print af_march_to_july
[ 519.96002197]
Computing over several years needs to rethink how we've declared the scenario:
the starting scenario has, as period
, one year, 2015
, so it won't be able to compute anything outside this time interval.
The idea is to change the period over which the scenario applies, using the syntax shown previously.
WARNING : handling the stretching of values
Doing that defines the other variables for the entire time interval including their values.
#we have to give a new scenario
scenario_over_years = tax_benefit_system.new_scenario()
scenario_over_years.init_single_entity(
period = '2014:3', # it means three years starting in 2014
parent1 = dict(
date_naissance = 1975,
salaire_de_base = 50000 * 3 , # if we want 50000 for each of the three years
),
enfants = [
dict(date_naissance = 2001),
dict(date_naissance = 1999),
],
)
simulation_over_years = scenario_over_years.new_simulation()
WARNING : handling the age
We've changed the variable age
to date_naissance
in order that the individuals get older. If we've kept the age
they would have the same age for the entire period.
Now we can compute the irpp
with the function previously used : calculate_add
simulation_over_years.calculate_add('irpp', '2014:2') # for 2014 and 2015
array([-4813.68652344], dtype=float32)
It takes in consideration the legislation change between the two years.
Therefore it equals the sum of irpp
for each year but not the double of irpp
for one year.
print(simulation_over_years.calculate('irpp', '2015') + simulation_over_years.calculate('irpp', '2014'))
print(simulation_over_years.calculate_add('irpp', '2014')*2)
[-4813.68652344] [-4826.31738281]
These formulas are still working for variables defined on a monthly basis.
simulation_over_years.calculate_add('af', '2015:2')
array([ 4616.37402344], dtype=float32)
You might want to make evolve some given values over the years.
The tool for it will be to use a Python dictionnary.
For example, if you want to give a wage evolution :
scenario_over_years = tax_benefit_system.new_scenario()
scenario_over_years.init_single_entity(
period = '2014:3',
parent1 = dict(
date_naissance = 1975,
salaire_de_base = {'2014':50000, '2015': 50500, '2016':51000},
),
enfants = [
dict(date_naissance = 2001),
dict(date_naissance = 1999),
],
)
simulation_over_years = scenario_over_years.new_simulation()
simulation_over_years.calculate_add('irpp', '2014:3') # for 2014, 2015 and 2016
array([-7376.67675781], dtype=float32)
Command line to get the Notebook's Table of Contents:
%%javascript
$.getScript('https://kmahelona.github.io/ipython_notebook_goodies/ipython_notebook_toc.js')