Case disjunction

Limitations

Built-in python conditionnal structures are not compatible with vector calculus.
The following formula would for instance break:

# THIS IS NOT A VALID OPENFISCA FORMULA
def formula(person, period):
    salary = person('salary', period)
    if salary < 1000:
        return 200
    else:
        return 0

Some solutions though exist to emulate these structures.

Simple multiplication

Applying a condition is in many cases equivalent to a simple multiplication. For instance, our previous example can be rewritten:

def formula(person, period):
    condition_salary = person('salary', period) < 1000
    return condition_salary * 200

For a person, if condition_salary is True (equivalent to 1 in logical algebra), the returned result will be 200. However, if condition_salary is False (equivalent to 0), the returned result will be 0.

Ternary condition

Let's now write a formula that still returns 200 if the person salary is lower than 1000, but 100 if this condition is not met.

The helper function where offers a simple syntax to handle these cases.

def formula(person, period):
    condition_salary = person('salary', period) < 1000
    return where(condition_salary, 200, 100)

where takes 3 arguments: the condition, the value to return if the condition is met, and the value to return otherwise.

Multiples conditions

Let's consider a more complex case, where we want to attribute to a person:

  • 200 if their salary is less than 500
  • 100 if their salary is strictly more than 500, but less than 1000
  • 50 if their salary is strictly more than 1000, but less than 1500
  • 0 otherwise

We can use the helper function select to implement this behaviour:

def formula(person, period):
    salary = person('salary', period)
    return select(
        [salary <= 500, salary <= 1000, salary <= 1500, salary > 1500],
        [200, 100, 50, 0],
        )

select takes two arguments:

  • A list of conditions
  • A list of values

If the first condition is met, the first value will be returned, without considering the other conditions. For instance, if salary = 100, salary <= 500 is true and therefore 200 will be returned. It doesn't matter that salary <= 1000 is also true.

If the first condition is not met, then only the second condition will be considered.

If no condition is met, 0 will be returned. The previous formula is thus strictly equivalent to:

def formula(person, period):
    salary = person('salary', period)
    return select(
        [salary <= 500, salary <= 1000, salary <= 1500],
        [200, 100, 50],
        )

Complex conditions

Complex conditions can be coded combining * as and and + as or.

For instance, let's consider that a person will be granted 200 if either:

- They are more than 25 *and* make less than `1000` per month
- They are in a situation of handicap
def formula(person, period):
    condition_age = person('age') >= 25
    condition_salary = person('salary', period) < 1000
    condition_handicap = person('handicap')
    condition = condition_age * condition_salary + condition_handicap
    return condition * 200

It is considered a good practice to always use helpers where and select when they are relevant, and not to emulate their behaviour manually with logical operations.

Using a dynamic parameter calculation

If the result of your formula depends on a variable that can take only a finite amount of values, check out the Computing a parameter that depends on a variable section.