Skip to content

Multinomial Simpson Paradox

this notebook shows a model for a multinomial Simpson paradox.

Creative Commons LicenseaGrUMinteractive online version
import pandas as pd
import pyagrum as gum
import pyagrum.lib.notebook as gnb
import pyagrum.causal as csl
import pyagrum.causal.notebook as cslnb
## building a model including a Simpson's paradox
import scipy.stats as stats
bn = gum.fastBN("A[0,99]->B[0:40:200]<-C[0,5]->A")
bn.cpt("C").fillFromDistribution(stats.uniform, loc=0, scale=5)
bn.cpt("A").fillFromDistribution(stats.uniform, loc="C*12", scale=30)
bn.cpt("B").fillFromDistribution(stats.norm, loc="5+C*4-int(A/8)", scale=2);
# generating a CSV, taking this model as the causal one.
gum.generateSample(bn, 400, "out/sample.csv", with_labels=False)
df = pd.read_csv("out/sample.csv")
df.plot.scatter(x="A", y="B", c="C", colormap="tab20");

svg

cm = csl.CausalModel(bn)
_, p, _ = csl.causalImpact(cm, on="B", doing="A")
## building an Markov-equivalent model, generating a CSV, taking this model as the causal one.
bn2 = gum.BayesNet(bn)
bn2.reverseArc("C", "A")
gum.generateSample(bn2, 400, "out/sample2.csv", with_labels=False)
df2 = pd.read_csv("out/sample2.csv")
cm2 = csl.CausalModel(bn2)
_, p2, _ = csl.causalImpact(cm2, on="B", doing="A")

The observationnal model and its paradoxal structure (exactly the same with the second Markov-equivalent model)

Section titled “The observationnal model and its paradoxal structure (exactly the same with the second Markov-equivalent model)”
gnb.flow.row(
gnb.getBN(bn),
df.plot.scatter(x="A", y="B"),
df.plot.scatter(x="A", y="B", c="C", colormap="tab20"),
captions=["the observationnal model", "the trend is increasing", "the trend is decreasing for any value for C !"],
)
gnb.flow.row(
gnb.getBN(bn2),
df2.plot.scatter(x="A", y="B"),
df2.plot.scatter(x="A", y="B", c="C", colormap="tab20"),
captions=["the Markov-equivalent model", "the trend is increasing", "the trend is decreasing for any value for C !"],
)
G C C B B C->B A A C->A A->B
the observationnal model
PyAgrum inline image
the trend is increasing
PyAgrum inline image
the trend is decreasing for any value for C !
G C C B B C->B A A A->C A->B
the Markov-equivalent model
PyAgrum inline image
the trend is increasing
PyAgrum inline image
the trend is decreasing for any value for C !

The paradox is revealed in the trend of the inferred means : the means are increasing with the value of AA except for any value of CC

Section titled “The paradox is revealed in the trend of the inferred means : the means are increasing with the value of AAA except for any value of CCC …”
gum.config["notebook", "histogram_epsilon"] = 0.001
gum.config["notebook", "histogram_discretized_scale"] = 0.4
for a in [10, 20, 30]:
gnb.flow.add_html(gnb.getPosterior(bn, target="B", evs={"A": a}), f"$P(B|A={a})$")
gnb.flow.new_line()
for a in [10, 20, 30]:
gnb.flow.add_html(gnb.getPosterior(bn, target="B", evs={"A": a, "C": 0}), f"P(B | $A={a},C=0)$")
gnb.flow.new_line()
for a in [10, 20, 30]:
gnb.flow.add_html(gnb.getPosterior(bn, target="B", evs={"A": a, "C": 2}), f"P(B | $A={a},C=2$)")
gnb.flow.new_line()
for a in [10, 20, 30]:
gnb.flow.add_html(gnb.getPosterior(bn, target="B", evs={"A": a, "C": 4}), f"P(B | $A={a},C=4$)")
gnb.flow.display()
PyAgrum inline image
$P(B|A=10)$
PyAgrum inline image
$P(B|A=20)$
PyAgrum inline image
$P(B|A=30)$

PyAgrum inline image
P(B | $A=10,C=0)$
PyAgrum inline image
P(B | $A=20,C=0)$
PyAgrum inline image
P(B | $A=30,C=0)$

PyAgrum inline image
P(B | $A=10,C=2$)
PyAgrum inline image
P(B | $A=20,C=2$)
PyAgrum inline image
P(B | $A=30,C=2$)

PyAgrum inline image
P(B | $A=10,C=4$)
PyAgrum inline image
P(B | $A=20,C=4$)
PyAgrum inline image
P(B | $A=30,C=4$)

Now that the paradoxal structure is understood and the paradox is revealed, will we choose to observe CC (or not) before deciding to increase or decrease AA (with the goal to maximize BB) ?

Section titled “Now that the paradoxal structure is understood and the paradox is revealed, will we choose to observe CCC (or not) before deciding to increase or decrease AAA (with the goal to maximize BBB) ?”

Of course, it depends on the causal structure of the problem !

gnb.flow.add_html(cslnb.getCausalModel(cm), "the first causal model")
gnb.flow.new_line()
for v in [10, 20, 30]:
gnb.flow.add_html(gnb.getProba(p.extract({"A": v})), f"Doing $A={v}$")
gnb.flow.display()
A A B B A->B C C C->A C->B
the first causal model

PyAgrum inline image
Doing $A=10$
PyAgrum inline image
Doing $A=20$
PyAgrum inline image
Doing $A=30$

If CC is cause for AA, observing CC really gives a new information about BB.

Section titled “If CCC is cause for AAA, observing CCC really gives a new information about BBB.”
gnb.flow.add_html(cslnb.getCausalModel(cm2), "the second causal model")
gnb.flow.new_line()
for v in [10, 20, 30]:
gnb.flow.add_html(gnb.getProba(p2.extract({"A": v})), f"Doing $A={v}$")
gnb.flow.display()
A A B B A->B C C A->C C->B
the second causal model

PyAgrum inline image
Doing $A=10$
PyAgrum inline image
Doing $A=20$
PyAgrum inline image
Doing $A=30$

if AA is cause for CC, observing CC may lead to misinterpretations about the causal role of AA.

Section titled “if AAA is cause for CCC, observing CCC may lead to misinterpretations about the causal role of AAA.”