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 :
try:
simulation.calculate('af', '2015')
except Exception as e:
print(e)
Unable to compute variable af for period 2015 : af must be computed for a whole month. You can use the ADD option to sum af over the requested period, or change the requested period to "period.first_month".
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.99], 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) :
try:
simulation.calculate('irpp', period = '2015-01')
except Exception as e:
print(e)
Unable to compute variable irpp for period 2015-01 : irpp must be computed for a whole year. You can use the DIVIDE option to get an estimate of irpp by dividing the yearly value by 12, or change the requested period to "period.this_year".
It must be called on an annual basis :
simulation.calculate('irpp', period = '2015') #calculate variable irpp for 2015
array([-2400.528], dtype=float32)
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.
simulation.calculate_add("af", "2015")
array([1559.88], dtype=float32)
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
annual_af
array([1559.88], dtype=float32)
We thus see that annual_af is equal to simulation.calculate_add('af', "2014").
We can test that :
simulation.calculate_add('af','2015') == annual_af
array([ True])
simulation.calculate_divide("irpp", "2015-01")
array([-200.044], dtype=float32)
simulation.calculate("irpp", "2015") / 12
array([-200.044], dtype=float32)
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.528] [ True]
We can look at periods docstring to understand how it works :
print(periods.period.__doc__)
Return a new period, aka a triple (unit, start_instant, size). >>> period(u'2014') Period((YEAR, Instant((2014, 1, 1)), 1)) >>> period(u'year:2014') Period((YEAR, Instant((2014, 1, 1)), 1)) >>> period(u'2014-2') Period((MONTH, Instant((2014, 2, 1)), 1)) >>> period(u'2014-02') Period((MONTH, Instant((2014, 2, 1)), 1)) >>> period(u'month:2014-2') Period((MONTH, Instant((2014, 2, 1)), 1)) >>> period(u'year:2014-2') Period((YEAR, Instant((2014, 2, 1)), 1))
A Period
object is initialized with three parameters:
periods.Period('day', periods.Instant((2014, 3, 01)), 32)
would be a period going from the first of March to the first of april
period = periods.Period(('day', periods.Instant((2014, 3, 1)), 32))
period.unit, period.start, period.size
('day', Instant((2014, 3, 1)), 32)
If we want to calculate the 'allocation familiales' from the April to July.
af_april_to_july = simulation.calculate_add(
'af',
period = periods.Period(('month', periods.Instant((2014, 3, 1)), 4))
)
print(af_april_to_july)
[519.18]
A simplificated version of period declaration exists with the symbol: ":"
.
month:year-month:n
means n months beginning at the month, year.
Example with af_april_to_july
:
af_april_to_july = simulation.calculate_add('af', period = periods.period("month:2014-04:4"))
print(af_april_to_july)
[519.96]
A simplified instant declaration of instant also exists
periods.instant("2014-01")
Instant((2014, 1, 1))
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 initialise a new scenario
scenario_over_years = tax_benefit_system.new_scenario()
scenario_over_years.init_single_entity(
period = 'year:2014:3', # three years starting in 2014
parent1 = dict(
date_naissance = 1975,
salaire_de_base = 50000 * 3,
# Multiplication by 3 is need so salaire de base is 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', period = 'year:2014-01:2') # for 2014 and 2015
array([-4813.6865], 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.6865] [-4826.3174]
These formulas are still working for variables defined on a monthly basis.
simulation_over_years.calculate_add('af', 'month:2015-01:2')
array([324.97363], 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 = 'year: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', 'year:2014:3') # for 2014, 2015 and 2016
array([-7362.9565], 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')