Esercizio - Aggiustamento front-door ed effetto del fumo sul cancro (laboratorio 8)
Testo (laboratorio 8, Causal inference - Part B, esercizi 3, 4 e 5). Il confondente tra e non è osservato (nella libreria è un arco tratteggiato «latente» tra e ), ma esistono variabili intermedie osservate.
- Creare la rete con nodi , archi , , e un arco latente tra e ; trovare tutti gli insiemi validi per il criterio front-door.
- Creare la rete con nodi , archi , , , e un arco latente tra e ; trovare tutti gli insiemi validi per il criterio front-door.
- Ripetendo l'esempio del fumo, si hanno i dati di un esperimento ipotetico con 400 fumatori e 400 non fumatori. Scrivere un programma Python che implementi l'aggiustamento front-door e calcoli l'effetto causale del fumo sul cancro, .
Teoria: Interventi e operatore do - criterio backdoor e aggiustamentoOsservare $X=x$ e forzare $X=x$ sono cose diverse: $P(y\mid x)\ne P(y\mid do(x))$ quando c'è un confondente. L'intervento $do(X=x)$ si calcola con la «chirurgia» del grafo (si tolgono le frecce entranti in X) e si traduce in probabilità osservazionali con la formula di aggiustamento $P(y\mid do(x))=\sum_z P(y\mid x,z)P(z)$, dove $Z$ sono i genitori di X, o più in generale un insieme S che soddisfa il criterio backdoor: blocca ogni cammino backdoor (cammino da X a Y che inizia con una freccia entrante in X) e non contiene discendenti di X. Effetto causale medio $ACE=P(y\mid do(x))-P(y\mid do(\neg x))$. Se il confondente non è osservabile ma c'è un mediatore Z con X→Z→Y, vale il front-door $P(y\mid do(x))=\sum_z P(z\mid x)\sum_{x'}P(y\mid x',z)P(x')$. Mediazione: effetto diretto controllato CDE con $do(x),do(z)$. Se due insiemi backdoor danno risultati diversi sui dati, il modello è sbagliato. Argomento del corso da 9 CFU.Interventi e operatore do - criterio backdoor e aggiustamento → (criterio e formula front-door). Per le funzioni dei grafi si usano quelle di Esercizio - Cammini backdoor e insiemi di aggiustamento validi (laboratorio 8).
Criterio front-door
Un insieme soddisfa il criterio front-door per se
- blocca ogni cammino diretto da a (tutti passano per );
- non ci sono cammini backdoor aperti da a ;
- tutti i cammini backdoor da a sono bloccati da .
In tal caso . La libreria rappresenta il confondente come arco latente; qui lo si rappresenta con un nodo presente nel grafo ma non tra i nodi osservati (non si può condizionare su ):
from e4lib import * # figli, genitori, discendenti, cammini, bloccato (funzioni dell'esercizio precedente)
def cammini_diretti(E, x, y):
return [p for p in cammini(E, x, y) if all((a, b) in E for a, b in zip(p, p[1:]))]
def backdoor_verso(E, x, z): # cammini da x a z il cui primo arco entra in x
return [p for p in cammini(E, x, z) if (p[1], x) in E]
def insiemi_frontdoor(E, x, y, osservati):
out = []
for r in range(1, len(osservati) + 1):
for S in combinations(sorted(osservati), r):
S = set(S)
ok1 = all(set(p[1:-1]) & S for p in cammini_diretti(E, x, y)) # intercetta ogni cammino diretto
ok2 = all(bloccato(E, p, set()) for s in S for p in backdoor_verso(E, x, s)) # nessun backdoor aperto da x a S
ok3 = all(bloccato(E, p, {x}) for s in S for p in cammini(E, s, y) if (p[1], s) in E) # backdoor da S a y chiusi da x
if ok1 and ok2 and ok3: out.append(sorted(S))
return outSoluzione
3. Catena
Grafico interattivo: Esercizio 3: X → A → B → Y con confondente non osservato U (tratteggiato) tra X e Y
E3 = {("X", "A"), ("A", "B"), ("B", "Y"), ("U", "X"), ("U", "Y")}
print(insiemi_frontdoor(E3, "X", "Y", {"A", "B"}))Output: [['A'], ['B'], ['A', 'B']], lo stesso di get_all_frontdoor_adjustment_sets('X','Y') (frozenset({{A},{B},{A,B}})). Ragionamento: l'unico cammino diretto è , quindi , e lo intercettano. Il solo cammino backdoor da a o a passa per () dove è un collider non condizionato: già bloccato. I cammini backdoor da (o ) a passano tutti per () e sono bloccati dal condizionare su .
4. Due mediatori in parallelo
Grafico interattivo: Esercizio 4: X → A → Y e X → B → Y con confondente non osservato U (tratteggiato)
E4 = {("X", "A"), ("A", "Y"), ("X", "B"), ("B", "Y"), ("U", "X"), ("U", "Y")}
print(insiemi_frontdoor(E4, "X", "Y", {"A", "B"}))Output: [['A', 'B']]. Ci sono due cammini diretti, e : da solo lascia aperto quello per (condizione 1 violata), e viceversa. Serve .
Caso impossibile (slide). Se è causa anche del mediatore (), insiemi_frontdoor restituisce []: il cammino backdoor da a , , non passa per e non è bloccato (condizione 3 violata), né si può condizionare su .
5. Fumo, catrame e cancro
Il grafo è con (il «gene del fumo», o un fattore come l'età) causa non osservata di e : fumo, deposito di catrame, cancro.
Grafico interattivo: Front-door: il gene U (non osservato, tratteggiato) è causa comune di X (fumo) e Y (cancro); Z (catrame) è osservato
Dal codice del corso si leggono le tabelle (indici t=vero, f=falso):
- : per fumatori e per non fumatori;
- , ;
- ; ; ; .
Formula front-door:
Con i numeri: (la parentesi quadra interna è la media su pesata con : è , l'effetto del catrame sul cancro, che si ricava dal backdoor .) In frazioni esatte è .
import numpy as np
t, f = 0, 1 # indici: 0 = vero, 1 = falso
P_X = np.array([400 / 800, 400 / 800])
P_Z_X = np.array([[380 / 400, 20 / 400], [20 / 400, 380 / 400]]) # P(z | x): [z][x]
P_Y_ZX = np.array([[[57 / 380, 19 / 20], [2 / 20, 342 / 380]],
[[323 / 380, 1 / 20], [18 / 20, 38 / 380]]]) # P(y | z, x): [y][z][x]
def front_door(x):
return sum(P_Z_X[z, x] * sum(P_Y_ZX[t, z, xp] * P_X[xp] for xp in (t, f)) for z in (t, f))
print(front_door(t), front_door(f), front_door(t) - front_door(f))Output: 0.5475 0.5025 0.04500000000000004. Quindi . Per il caso senza fumo e . L'ACE del fumo è : fumare aumenta di 4.5 punti percentuali la probabilità di cancro.
Perché è un risultato controintuitivo di proposito. I dati osservazionali dicono l'opposto: tra i fumatori il cancro è , tra i non fumatori , come se fumare proteggesse. Una spiegazione possibile: in alcuni paesi i fumatori sono molti più giovani dei non fumatori, e i vecchi si ammalano di più. L'età è un confondente di fumo e cancro, ma non è osservata, quindi non si può aggiustare su di essa. L'unica alternativa sarebbe un esperimento randomizzato (forzare a fumare metà persone), impossibile; con il catrame, osservato, il front-door recupera comunque l'effetto causale.
Verifica del metodo (modello inventato da me, non del corso). SCM booleano: , per , , per e per . Il calcolo esatto dal modello dà e ; il front-door applicato alla sola congiunta osservata di (senza ) dà gli stessi e ; la simulazione con due milioni di campioni dà e . Il confronto osservazionale ( contro ) è distorto dalla causa comune.
Versione ripasso
- Criterio front-door per : (1) intercetta tutti i cammini diretti ; (2) nessun backdoor aperto da a ; (3) i backdoor da a sono bloccati da . Formula: (Interventi e operatore do - criterio backdoor e aggiustamentoOsservare $X=x$ e forzare $X=x$ sono cose diverse: $P(y\mid x)\ne P(y\mid do(x))$ quando c'è un confondente. L'intervento $do(X=x)$ si calcola con la «chirurgia» del grafo (si tolgono le frecce entranti in X) e si traduce in probabilità osservazionali con la formula di aggiustamento $P(y\mid do(x))=\sum_z P(y\mid x,z)P(z)$, dove $Z$ sono i genitori di X, o più in generale un insieme S che soddisfa il criterio backdoor: blocca ogni cammino backdoor (cammino da X a Y che inizia con una freccia entrante in X) e non contiene discendenti di X. Effetto causale medio $ACE=P(y\mid do(x))-P(y\mid do(\neg x))$. Se il confondente non è osservabile ma c'è un mediatore Z con X→Z→Y, vale il front-door $P(y\mid do(x))=\sum_z P(z\mid x)\sum_{x'}P(y\mid x',z)P(x')$. Mediazione: effetto diretto controllato CDE con $do(x),do(z)$. Se due insiemi backdoor danno risultati diversi sui dati, il modello è sbagliato. Argomento del corso da 9 CFU.Interventi e operatore do - criterio backdoor e aggiustamento →). Il confondente non osservato è un nodo fuori dai nodi osservati.
- Es. 3 (, latente ): . Es. 4 (, ): solo (due cammini diretti). Impossibile se causa anche il mediatore.
- Es. 5 (fumo). ; , ; , , , .
- Calcolo: ; ; ACE .
- Osservazione contro intervento: , (come se fumare proteggesse) per via del confondente non osservato (per esempio l'età).
- Codice:
front_door(x) = sum_z P_Z_X[z, x] * sum_xp P_Y_ZX[t, z, xp] * P_X[xp]con tabelle numpy[z][x]e[y][z][x]. - Errori tipici: usare al posto di nella somma interna; scambiare l'ordine degli indici
[y][z][x]; credere ai dati osservazionali; applicare il front-door con un singolo mediatore quando i cammini diretti sono due.