The Effect of Education and Experience on Salary (p251)
Authors: Aymen Merrouche and Pierre-Henri Wuillemin.
This notebook follows the example from “The Book Of Why” (Pearl, 2018) chapter 8 page 251
Counterfactuals for Education and Salary
In [1]:
from IPython.display import display, Math, Latex,HTML
import pyAgrum as gum
import pyAgrum.lib.notebook as gnb
import pyAgrum.causal as csl
import pyAgrum.causal.notebook as cslnb
import os
import math
import numpy as np
import scipy.stats
In this example we are interested in the effect of experience and education on the salary of an employee, we are in possession of the following data:
Employé | EX(u) | ED(u) | \(S_{0}(u)\) | \(S_{1}(u)\) | \(S_{2}(u)\) |
---|---|---|---|---|---|
Alice | 8 | 0 | 86,000 | ? | ? |
Bert | 9 | 1 | ? | 92,500 | ? |
Caroline | 9 | 2 | ? | ? | 97,000 |
David | 8 | 1 | ? | 91,000 | ? |
Ernest | 12 | 1 | ? | 100,000 | ? |
Frances | 13 | 0 | 97,000 | ? | ? |
etc |
\(EX(u)\) : years of experience of employee \(u\). [0,20]
\(ED(u)\) : Level of education of employee \(u\) (0:high school degree (low), 1:college degree (medium), 2:graduate degree (high)) [0,2]
\(S_{i}(u)\) [65k,150k] :
salary (observable) of employee \(u\) if \(i = ED(u)\),
Potential outcome (unobservable) if \(i \not = ED(u)\), salary of employee \(u\) if he had a level of education of \(i\).
We are left with the previous data and we want to answer the counterfactual question What would Alice’s salary be if she attended college ? (i.e. \(S_{1}(Alice)\))
A Causal model
In this model it is assumed that an employee’s salary is determined by his level of education and his experience. Years of experience are also affected by the level of education. Having a higher level of education means spending more time studying hence less experience.
In [2]:
edex = gum.fastBN("Ux[-2,10]->experience[0,20]<-education{low|medium|high}->salary[65,150]<-Us[0,25];experience->salary")
edex
Out[2]:
However counterfactual queries are specific to one datapoint (in our case Alice), we need to add additional variables to our model to allow for individual variations: * Us : unobserved variables that affect salary.[0,25k] * Ux : unobserved variables that affect experience.[-2,10]
In [3]:
# no prior information about the individual (datapoint)
edex.cpt("Us").fillWith(1).normalize()
edex.cpt("Ux").fillWith(1).normalize()
# education level(supposed)
edex.cpt("education")[:] = [0.4, 0.4, 0.2]
Experience listens to Education and Ux :
In [4]:
edex.cpt("experience").fillFromFunction("10-4*education+Ux")
edex.cpt("experience")
Out[4]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| ||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | |
0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0000 | 0.0000 | ||
0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0000 | ||
0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 1.0000 | 0.0000 | ||
0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 1.0000 | ||
| 0.0000 | 0.0000 | 0.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | |
0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
| 1.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | |
0.0000 | 1.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
0.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
0.0000 | 0.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
0.0000 | 0.0000 | 0.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 1.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 |
Salary listens to Education, Experience and Us :
In [5]:
edex.cpt("salary").fillFromFunction("round(65+2.5*experience+5*education+Us)");
In [6]:
gnb.showInference(edex,size="10")
Our question was : What would Alice’s salary be if she attended college ?
To answer this counterfactual question we will follow the three steps algorithm from “The Book Of Why” (Pearl 2018) chapter 8 page 253 :
Step 1 : Abduction for counterfactual
Use the data to retrieve all the information that characterizes Alice
From the data we can retrieve Alice’s profile : * \(Ed(Alice)\) : 0 * \(Ex(Alice)\) : 8 * \(S_{0}(Alice)\) : 86k
We will use Alice’s profile to get \(U_s\) and \(U_x\), which tell Alice apart from the rest of the data.
In [7]:
ie=gum.LazyPropagation(edex)
ie.setEvidence({'experience':8, 'education': 'low', 'salary' : "86"})
ie.makeInference()
newUs = ie.posterior("Us")
gnb.showProba(newUs)
In [8]:
ie=gum.LazyPropagation(edex)
ie.setEvidence({'experience':8, 'education': 'low', 'salary' : "86"})
ie.makeInference()
newUx = ie.posterior("Ux")
gnb.showProba(newUx)
In [9]:
gnb.showInference(edex,evs={'experience':8, 'education': "low", 'salary' : "86"},targets={'Ux','Us'})
Step 2 & 3 : Action And Prediction for counterfactual
Change the model to match the hypothesis implied by the query (if she had attended university) and then use the data that characterizes Alice to calculate her salary.
We create a counterfactual world with Alice’s idiosyncratic factors, and we operate the intervention:
In [10]:
# the counterfactual world
edexCounterfactual = gum.BayesNet(edex)
In [11]:
# we replace the prior probabilities of idiosyncratic factors with potentials calculated earlier
edexCounterfactual.cpt("Ux").fillWith(newUx)
edexCounterfactual.cpt("Us").fillWith(newUs)
gnb.showInference(edexCounterfactual,size="10")
print("counterfactual world created")
counterfactual world created
In [12]:
# We operate the intervention
edexModele = csl.CausalModel(edexCounterfactual)
cslnb.showCausalImpact(edexModele,"salary",
doing="education",
values={"education":"medium"})
In the previous query, Alice’s salary if she attended college was lower than her actual salary, that’s because in the counterfactual world where she attended college she had less time to work hence her diminished salary.
We can prove it perfoming a complete inference in the counterfactual world. Since education has no parents in our model (no graph surgery, no causes to emancipate it from), an intervention is equivalent to an observation, the only thing we need to do is to set the value of education:
In [13]:
gnb.showInference(edexCounterfactual,targets={"salary",'experience'},evs={'education':"medium"},size="10")
Indeed the expected “experience” decreased.
The result (salary if she had attended college) is given by the formaula:
Where variables marked with an asterisk are inobservable.
Alice’s salary would be \(\$81\) if she had attended college !
Using pyAgrum.causal.counterfactual
In pyAgrum, we can directly use a function that answers counterfactual queries using the previous algorithm.
In [14]:
help(csl.counterfactual)
Help on function counterfactual in module pyAgrum.causal._causalImpact:
counterfactual(cm: pyAgrum.causal._CausalModel.CausalModel, profile: Optional[Dict[str, int]], on: Union[str, Set[str]], whatif: Union[str, Set[str]], values: Optional[Dict[str, int]] = None) -> 'pyAgrum.Potential'
Determines the estimation of a counterfactual query following the the three steps algorithm from "The Book Of Why"
(Pearl 2018) chapter 8 page 253.
Determines the estimation of the counterfactual query: Given the "profile" (dictionary <variable name>:<value>),what
would variables in "on" (single or list of variables) be if variables in "whatif" (single or list of variables) had
been as specified in "values" (dictionary <variable name>:<value>)(optional).
This is done according to the following algorithm:
-Step 1-2: compute the twin causal model
-Step 3 : determine the causal impact of the interventions specified in "whatif" on the single or list of
variables "on" in the causal model.
This function returns the potential calculated in step 3, representing the probability distribution of "on" given
the interventions "whatif", if it had been as specified in "values" (if "values" is omitted, every possible value of
"whatif")
Parameters
----------
cm: CausalModel
profile: Dict[str,int] default=None
evidence
on: variable name or variable names set
the variable(s) of interest
whatif: str|Set[str]
idiosyncratic nodes
values: Dict[str,int]
values for certain variables in whatif.
Returns
-------
pyAgrum.Potential
the computed counterfactual impact
Let’s try with the previous query
In [15]:
pot=csl.counterfactual(cm = csl.CausalModel(edex),
profile = {'experience':8, 'education': "low", 'salary' : "86"},
whatif={"education"},
on={"salary"},
values = {"education" : "medium"})
In [16]:
gnb.showProba(pot)
We get the same result !
multiple conterfactuals
We get every potential outcome :
In [17]:
pot=csl.counterfactual(cm = csl.CausalModel(edex),
profile = {'experience':8, 'education': 'low', 'salary' : '86'},
whatif={"education"},
on={"salary"})
In [18]:
# pot contains the result for all value of education
for label in pot.variable("education").labels():
gnb.flow.row(f"for education = {label}",gnb.getProba(pot.extract({"education":label})))
What would Alice’s salary be if she had attended college and had 8 years of experience ?
In [19]:
pot=csl.counterfactual(cm = csl.CausalModel(edex),
profile = {'experience':8, 'education': 'low', 'salary' : '86'},
whatif={"education", "experience"},on={"salary"},
values = {"education" : 'medium', "experience" : 8})
In [20]:
gnb.showProba(pot)
if she attended college and had 8 years of experience Alice’s salary would be 91k !
In the previous query, Alice’s salary if she attended college was lower than her actual salary, that’s because in the counterfactual world where she attended college she had less time to work hence her diminished salary.
In this query, Alice’s counterfactual salary was higher than her actual salary (+5k corresponding to one level of education), that’s because in the counterfactual world Alice attended college and still had time to work 8 years, so her salary went up.
if she had more experience Some counterfactual can not be computer : With this profile, an experience of 10 is nont possible…
In [21]:
pot=csl.counterfactual(cm = csl.CausalModel(edex),
profile = {'experience':8, 'education': 'low', 'salary' : '86'},
whatif={"experience"},
on={"salary"},
values = {"experience" : 12})
pot
Out[21]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan | nan |
Indeed experience can not be 12
In [22]:
twin=csl.counterfactualModel(cm = csl.CausalModel(edex),
profile = {'experience':8, 'education': 'low', 'salary' : '86'},
whatif={"experience"})
gnb.showInference(twin.observationalBN(),size="10",evs={'education': 0, 'salary' : '86'})
CounterfactualS as a function
We can now fill (most of) the holes in :
Employé | EX(u) | ED(u) | \(S_{0}(u)\) | \(S_{1}(u)\) | \(S_{2}(u)\) |
---|---|---|---|---|---|
Alice | 8 | 0 | 86,000 | ? | ? |
Bert | 9 | 1 | ? | 92,500 | ? |
Caroline | 9 | 2 | ? | ? | 97,000 |
David | 8 | 1 | ? | 91,000 | ? |
Ernest | 12 | 1 | ? | 100,000 | ? |
Frances | 13 | 0 | 97,000 | ? | ? |
etc |
In [23]:
def mean(p):
return sum([p.variable(0).numerical(i)*p[i] for i in range(p.variable(0).domainSize())])
def affCounterfactualForStudent(model,name,ex,ed,sa,value):
try:
s0=csl.counterfactual(cm = model,
profile = {'experience':str(ex), 'education': ed, 'salary' : str(sa)},
whatif={"education"},
on={"salary"},
values = {"education" : value})
print("{:5.1f}| ".format(mean(s0)),end="")
except:
print(" -- | ",end="")
def forStudent(model,name,ex,ed,sa):
print("| {:20}| {:2.0f}| {:7}| {:5.1f}|| ".format(name,ex,ed,sa),end="")
for value in ['low','medium','high']:
affCounterfactualForStudent(model,name,ex,ed,sa,value)
print()
print("| Name | Ex| Ed | S || s0 | s1 | s2 |")
print("------------------------------------------------------------------")
d=csl.CausalModel(edex)
forStudent(d,"Alice",8,"low",86)
forStudent(d,"Bert",9,"medium",92)
forStudent(d,"Caroline",9,"high",97)
forStudent(d,"David",8,"medium",91)
forStudent(d,"Ernest",12,"medium",100)
forStudent(d,"Frances",13,"low",97)
| Name | Ex| Ed | S || s0 | s1 | s2 |
------------------------------------------------------------------
| Alice | 8| low | 86.0|| 86.0| 81.0| 76.0|
| Bert | 9| medium | 92.0|| 98.0| 92.0| 88.0|
| Caroline | 9| high | 97.0|| -- | -- | -- |
| David | 8| medium | 91.0|| 96.0| 91.0| 86.0|
| Ernest | 12| medium | 100.0|| 105.0| 100.0| 95.0|
| Frances | 13| low | 97.0|| -- | -- | -- |
Note that the holes that can not be filled come from the deterministic modelisation. See the notebook 65-Causality-Counterfactual for a ‘noisy’ version that allows to fill all the holes.
In [ ]: