Counterfactual : the Effect of Education and Experience on Salary
![]() | ![]() |
This notebook is a ‘probabilistic’ (or noisy) version following the example from “The Book Of Why” (Pearl, 2018) chapter 8 page 251 (see the notebooks BoW-c8p* below).
Counterfactuals
Section titled “Counterfactuals”import pyagrum as gumimport pyagrum.lib.notebook as gnbimport scipy.statsIn 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 |
- : years of experience of employee . [0,20]
- : Level of education of employee (0:high school degree (low), 1:college degree (medium), 2:graduate degree (high)) [0,2]
- [65k,150k] :
- salary (observable) of employee if ,
- Potential outcome (unobservable) if , salary of employee if he had a level of education of .
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. )
We create the causal diagram
Section titled “We create the causal diagram”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.
edex = gum.fastBN("Ux[-2,10]->experience[0,20]<-education[0,2]->salary[65,150];experience->salary<-Us[0,25]")edexHowever 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]
## 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]## To have probabilistic results, we add a perturbation. (Gaussian around the exact values)## we calculate a gaussian distribution
std = 1Experience listens to Education and Ux :
edex.cpt("experience").fillFromDistribution(scipy.stats.norm, loc="10-4*education+Ux", scale=std)edex.cpt("experience")|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| ||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
| 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0001 | 0.0044 | 0.0540 | 0.2420 | 0.3989 | 0.2420 | 0.0540 | 0.0044 | 0.0001 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | |
| 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0001 | 0.0044 | 0.0540 | 0.2420 | 0.3989 | 0.2420 | 0.0540 | 0.0044 | 0.0001 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
| 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0001 | 0.0044 | 0.0540 | 0.2420 | 0.3989 | 0.2420 | 0.0540 | 0.0044 | 0.0001 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
| 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0001 | 0.0044 | 0.0540 | 0.2420 | 0.3989 | 0.2420 | 0.0540 | 0.0044 | 0.0001 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
| 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0001 | 0.0044 | 0.0540 | 0.2420 | 0.3989 | 0.2420 | 0.0540 | 0.0044 | 0.0001 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
| 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0001 | 0.0044 | 0.0540 | 0.2420 | 0.3989 | 0.2420 | 0.0540 | 0.0044 | 0.0001 | 0.0000 | 0.0000 | 0.0000 | ||
| 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0001 | 0.0044 | 0.0540 | 0.2420 | 0.3989 | 0.2420 | 0.0540 | 0.0044 | 0.0001 | 0.0000 | 0.0000 | ||
| 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0001 | 0.0044 | 0.0540 | 0.2420 | 0.3989 | 0.2420 | 0.0540 | 0.0044 | 0.0001 | 0.0000 | ||
| 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0001 | 0.0044 | 0.0540 | 0.2420 | 0.3989 | 0.2420 | 0.0540 | 0.0044 | 0.0001 | ||
| 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0001 | 0.0044 | 0.0540 | 0.2420 | 0.3990 | 0.2420 | 0.0540 | 0.0044 | ||
| 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0001 | 0.0045 | 0.0542 | 0.2431 | 0.4008 | 0.2431 | 0.0542 | ||
| 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0001 | 0.0047 | 0.0573 | 0.2570 | 0.4238 | 0.2570 | ||
| 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0002 | 0.0063 | 0.0772 | 0.3459 | 0.5703 | ||
|
| 0.0001 | 0.0044 | 0.0540 | 0.2420 | 0.3989 | 0.2420 | 0.0540 | 0.0044 | 0.0001 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | |
| 0.0000 | 0.0001 | 0.0044 | 0.0540 | 0.2420 | 0.3989 | 0.2420 | 0.0540 | 0.0044 | 0.0001 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
| 0.0000 | 0.0000 | 0.0001 | 0.0044 | 0.0540 | 0.2420 | 0.3989 | 0.2420 | 0.0540 | 0.0044 | 0.0001 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
| 0.0000 | 0.0000 | 0.0000 | 0.0001 | 0.0044 | 0.0540 | 0.2420 | 0.3989 | 0.2420 | 0.0540 | 0.0044 | 0.0001 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
| 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0001 | 0.0044 | 0.0540 | 0.2420 | 0.3989 | 0.2420 | 0.0540 | 0.0044 | 0.0001 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
| 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0001 | 0.0044 | 0.0540 | 0.2420 | 0.3989 | 0.2420 | 0.0540 | 0.0044 | 0.0001 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
| 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0001 | 0.0044 | 0.0540 | 0.2420 | 0.3989 | 0.2420 | 0.0540 | 0.0044 | 0.0001 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
| 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0001 | 0.0044 | 0.0540 | 0.2420 | 0.3989 | 0.2420 | 0.0540 | 0.0044 | 0.0001 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
| 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0001 | 0.0044 | 0.0540 | 0.2420 | 0.3989 | 0.2420 | 0.0540 | 0.0044 | 0.0001 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
| 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0001 | 0.0044 | 0.0540 | 0.2420 | 0.3989 | 0.2420 | 0.0540 | 0.0044 | 0.0001 | 0.0000 | 0.0000 | 0.0000 | ||
| 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0001 | 0.0044 | 0.0540 | 0.2420 | 0.3989 | 0.2420 | 0.0540 | 0.0044 | 0.0001 | 0.0000 | 0.0000 | ||
| 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0001 | 0.0044 | 0.0540 | 0.2420 | 0.3989 | 0.2420 | 0.0540 | 0.0044 | 0.0001 | 0.0000 | ||
| 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0001 | 0.0044 | 0.0540 | 0.2420 | 0.3989 | 0.2420 | 0.0540 | 0.0044 | 0.0001 | ||
|
| 0.5703 | 0.3459 | 0.0772 | 0.0063 | 0.0002 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | |
| 0.2570 | 0.4238 | 0.2570 | 0.0573 | 0.0047 | 0.0001 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
| 0.0542 | 0.2431 | 0.4008 | 0.2431 | 0.0542 | 0.0045 | 0.0001 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
| 0.0044 | 0.0540 | 0.2420 | 0.3990 | 0.2420 | 0.0540 | 0.0044 | 0.0001 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
| 0.0001 | 0.0044 | 0.0540 | 0.2420 | 0.3989 | 0.2420 | 0.0540 | 0.0044 | 0.0001 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
| 0.0000 | 0.0001 | 0.0044 | 0.0540 | 0.2420 | 0.3989 | 0.2420 | 0.0540 | 0.0044 | 0.0001 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
| 0.0000 | 0.0000 | 0.0001 | 0.0044 | 0.0540 | 0.2420 | 0.3989 | 0.2420 | 0.0540 | 0.0044 | 0.0001 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
| 0.0000 | 0.0000 | 0.0000 | 0.0001 | 0.0044 | 0.0540 | 0.2420 | 0.3989 | 0.2420 | 0.0540 | 0.0044 | 0.0001 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
| 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0001 | 0.0044 | 0.0540 | 0.2420 | 0.3989 | 0.2420 | 0.0540 | 0.0044 | 0.0001 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
| 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0001 | 0.0044 | 0.0540 | 0.2420 | 0.3989 | 0.2420 | 0.0540 | 0.0044 | 0.0001 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
| 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0001 | 0.0044 | 0.0540 | 0.2420 | 0.3989 | 0.2420 | 0.0540 | 0.0044 | 0.0001 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
| 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0001 | 0.0044 | 0.0540 | 0.2420 | 0.3989 | 0.2420 | 0.0540 | 0.0044 | 0.0001 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
| 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | 0.0001 | 0.0044 | 0.0540 | 0.2420 | 0.3989 | 0.2420 | 0.0540 | 0.0044 | 0.0001 | 0.0000 | 0.0000 | 0.0000 | 0.0000 | ||
Salary listens to Education, Experience and Us :
edex.cpt("salary").fillFromDistribution(scipy.stats.norm, loc="65+2.51*experience+5*education+Us", scale=std)gnb.showInference(edex)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
Section titled “Step 1 : Abduction”Use the data to retrieve all the information that characterizes Alice
From the data we can retrieve Alice’s profile :
- : 0
- : 8
- : 86k
We will use Alice’s profile to get and , which tell Alice apart from the rest of the data.
ie = gum.LazyPropagation(edex)ie.setEvidence({"education": 0, "salary": "86", "experience": 8})ie.makeInference()newUs = ie.posterior("Us")gnb.showProba(newUs)ie = gum.LazyPropagation(edex)ie.setEvidence({"education": 0, "salary": "86", "experience": 8})ie.makeInference()newUx = ie.posterior("Ux")gnb.showProba(newUx)gnb.showInference(edex, evs={"education": 0, "salary": "86", "experience": 8}, targets={"Ux", "Us"})Step 2 & 3 : Action And Prediction
Section titled “Step 2 & 3 : Action And Prediction”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:
## the counterfactual worldedexCounterfactual = gum.BayesNet(edex)## we replace the prior probabilities of idiosynatric factors with potentials calculated earlieredexCounterfactual.cpt("Ux").fillWith(newUx)edexCounterfactual.cpt("Us").fillWith(newUs)gnb.showInference(edexCounterfactual, size="10")print("counterfactual world created")counterfactual world created## We operate the interventionedexModele = gum.CausalModel(edexCounterfactual)gnb.showCausalImpact(edexModele, "salary", doing="education", values={"education": "1"})Alice’s salary if she attended college is 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:
gnb.showInference(edexCounterfactual, targets={"salary", "experience"}, evs={"education": "1"}, 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.
formula, adj, exp = gum.causalImpact(edexModele, on="salary", doing="education", values={"education": "1"})gnb.showProba(adj)salary = edexModele.observationalBN()["salary"]adj.expectedValue(lambda v: salary.numerical(v["salary"]))82.41408794898388Alice’s salary would be \82.4$ if she had attended college !
Section titled “S1(Alice)=82kS_1(Alice) = 82k Alice’s salary would be \82.4$ if she had attended college !”pyagrum.counterfactual
Section titled “pyagrum.counterfactual”In pyAgrum, we can directly use a function that answers counterfactual queries using the previous algorithm.
help(gum.counterfactual)Help on function counterfactual in module pyagrum.pyagrum:
counterfactual(cm, *, on, whatif, profile=None, values=None) Compute a counterfactual distribution using Pearl's twin network method.
Answers the question: 'Given that we observed *profile*, what would *on* have been if *whatif* had been set as specified in *values*?'
The computation follows the three-step algorithm from Pearl (2018), *The Book of Why*, chapter 8: abduction (update parentless node priors from the profile), action (apply do(whatif) on the twin model), and prediction (evaluate the causal effect on the twin).
Parameters ---------- cm : pyagrum.CausalModel The causal model. on : str or set of str Target variable(s) of the counterfactual query. A single string is automatically converted to a one-element set. whatif : str or set of str Variable(s) whose values are changed in the counterfactual scenario. A single string is automatically converted to a one-element set. profile : dict of str → str, optional The factual observation as ``{variable_name: value_name}``. This grounds the counterfactual (step 1: abduction). Default is empty (no factual observation). values : dict of str → str, optional Counterfactual values for the *whatif* variables as ``{variable_name: value_name}``. If omitted, the full distribution over all *whatif* values is returned.
Returns ------- pyagrum.Tensor The counterfactual distribution P(on | do(whatif)) evaluated on the twin model, optionally sliced by *values*.
Examples -------- >>> import pyagrum as gum >>> bn = pyagrum.BayesNet.fastPrototype('X->Y->Z') >>> cm = pyagrum.CausalModel(bn) >>> t = pyagrum.counterfactual(cm, on='Z', whatif='X', ... profile={'Y': 'True'}, values={'X': 'False'})Let’s try with the previous query :
Section titled “Let’s try with the previous query :”cm_edex = gum.CausalModel(edex)pot = gum.counterfactual( cm=cm_edex, profile={"education": 0, "experience": 8, "salary": "86"}, whatif={"education"}, on={"salary"}, values={"education": 1},)gnb.showProba(pot)We get the same result !
If we omit values:
Section titled “If we omit values:”We get every potential outcome :
pot = gum.counterfactual( cm=cm_edex, profile={"experience": 8, "education": "0", "salary": "86"}, whatif={"education"}, on={"salary"})## pot contains the result for all value of educationfor label in pot.variable("education").labels(): gnb.flow.row(f"for education = {label}", gnb.getProba(pot.extract({"education": label}))) gnb.flow.display()What would Alice’s salary be if she had attended college and had 8 years of experience ?
Section titled “What would Alice’s salary be if she had attended college and had 8 years of experience ?”pot = gum.counterfactual( cm=cm_edex, profile={"experience": 8, "education": 0, "salary": "86"}, whatif={"education", "experience"}, on={"salary"}, values={"education": 1, "experience": 8},)gnb.showProba(pot)if she attended college and had 8 years of experience Alice’s salary would be 91k !
Section titled “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 :
Section titled “if she had more experience :”pot = gum.counterfactual( cm=cm_edex, profile={"experience": 8, "education": 0, "salary": "86"}, whatif={"experience"}, on={"salary"}, values={"experience": 12},)gnb.showProba(pot)indeed experience can not be 12
Section titled “indeed experience can not be 12”import pyagrum.lib.notebook as gnb
twin = gum.counterfactualModel( cm=gum.CausalModel(edex), profile={"experience": 8, "education": 0, "salary": "86"}, whatif={"experience"})print(twin.causalDAG()){0<Ux>,1<experience>,2<education>,3<salary>,4<Us>} , {0->1,2->1,2->3,1->3,4->3}gnb.showInference(twin.observationalBN(), size="10")CounterfactualS as a function
Section titled “CounterfactualS as a function”We can now fill the holes in :
Section titled “We can now fill 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 |
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 = gum.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}| s{:1}|{:3.0f}| | ".format(name, ex, ed, sa), end="") for value in range(3): affCounterfactualForStudent(model, name, ex, ed, sa, value) print()
print("| Name | Ex|Ed | S | | s0 | s1 | s2 |")print("----------------------------------- ----------------------")d = gum.CausalModel(edex)forStudent(d, "Alice", 8, 0, 86)forStudent(d, "Bert", 9, 1, 92)forStudent(d, "Caroline", 9, 2, 97)forStudent(d, "David", 8, 1, 91)forStudent(d, "Ernest", 12, 1, 100)forStudent(d, "Frances", 13, 0, 97)| Name | Ex|Ed | S | | s0 | s1 | s2 |----------------------------------- ----------------------| Alice | 8| s0| 86| | 87.5| 82.4| 78.2|| Bert | 9| s1| 92| | 97.9| 92.9| 87.9|| Caroline | 9| s2| 97| | 107.9| 102.9| 97.9|| David | 8| s1| 91| | 96.2| 91.1| 86.1|| Ernest | 12| s1|100| | 105.6| 100.6| 95.5|| Frances | 13| s0| 97| | 97.9| 92.9| 87.8|Note that, contraty to the notebook ‘BoW-c8p251*’, there is no equality between the input salary (86 for Alice) and the expected counterfactual if Alice had this salary (For alice, ). Of course, this is due to noise that we introduced in the model.
Note also that this “noisy” version allow to answer that can not be answered in the deterministic version in ‘BoW-c8p251*’.
