18.1 Oksági cél és megfigyelési torzítás

A cél egy konkrét oksági kérdés: ha a Northstar-rendeléseket priority_flag=1 (elsőbbségi kezelés) beavatkozással látnánk el, mennyivel változna a késés valószínűsége a target_late kimenetben. A megfigyelési egység egy rendelés. Az input a orders.csv sémája: order_id, customer_id, order_ts, promised_days=7, quantity_sum, basket_minor, shipping_fee_minor, distance_km, priority_flag, továbbá utólag ismert delivery_days és target_late. A predikciós projekt céljától eltérően itt nem azt kérdezzük, hogy egy rendelés várhatóan késni fog-e, hanem azt, hogy a priority_flag megváltoztatása okoz-e késéscsökkenést.

A nyers megfigyelési asszociáció P(target_late=1∣priority_flag=1)−P(target_late=1∣priority_flag=0)P(target\_late=1 \mid priority\_flag=1)-P(target\_late=1 \mid priority\_flag=0) nem automatikus oksági hatás. Ha például a nagyobb távolságú rendelések gyakrabban kapnak prioritást, és a távolság a késést is növeli, akkor a prioritásos csoport más distance_km-összetételű, mint a nem prioritásos. Az induló feladat: mielőtt a módszertant látná, fogalmazzon meg legalább egy ilyen közös okot, és mondja meg, milyen irányba torzíthatja a nyers kockázati különbséget a distance_km, ha a nagyobb távolság növeli mind a priority_flag esélyét, mind a target_late esélyét.

18.1.1 Potenciális kimenet és a beavatkozás jelölése

Minden i=1,…,ni=1,\dots,n rendeléshez két potenciális kimenet tartozik:

Itt Yi(a)∈{0,1}Y_i(a)\in\{0,1\} bináris, tehát nincs mértékegysége. A beavatkozás Ai∈{0,1}A_i\in\{0,1\} a ténylegesen megfigyelt kezelés. A fundamental problem of causal inference, hogy egy rendelésen a két érték közül egyszerre csak az egyik figyelhető meg; a megfigyelt kimenet:

Yi=AiYi(1)+(1−Ai)Yi(0).Y_i = A_iY_i(1) + (1-A_i)Y_i(0).

A jobb oldal elemenkénti vektor: ha Ai=1A_i=1, az első tag marad; ha Ai=0A_i=0, a második. Egy kis kontrollpélda:

AA Y(0)Y(0) Y(1)Y(1) megfigyelt YY
0 0 1 0
1 1 1 1
0 0 0 0
1 1 0 0

A Y(0),Y(1)Y(0), Y(1) oszlopok hipotetikusak; a táblázatban csak AA és YY mérhető. Az egyéni oksági hatás τi=Yi(1)−Yi(0)\tau_i=Y_i(1)-Y_i(0), ami bináris kimenetnél −1-1, 00 vagy 11 lehet. Az átlagos oksági hatás, angolul average treatment effect, a populációban:

ATE=E[Y(1)−Y(0)]=E[Y(1)]−E[Y(0)].ATE = E[Y(1)-Y(0)] = E[Y(1)] - E[Y(0)].

A fenti négy soron a potenciális különbségek: 11, 00, 00, −1-1, tehát a példabeli ATE −0,00-0{,}00? Pontosabban (−1+0+0+1)/4=0(-1+0+0+1)/4=0? Ellenőrizzük: Y(1)−Y(0)Y(1)-Y(0) soronként 1,0,0,−11,0,0,-1; összeg 00, átlag 00. Ez jó emlékeztető: az egyéni hatások lehetnek nem nullák, de az átlag lehet nulla.

A beavatkozás (intervention) nem passzív megfigyelés, hanem a kezelés külső beállítása; a jelölés do(A=1)do(A=1), amit a potenciális kimeneti nyelvben Y(1)Y(1) jelöl. Ha egy gráfban a priority_flag csomópontra beavatkozunk, a belé mutató nyilak eltűnnek, mert a kezelést ezentúl nem a távolság vagy a kosárérték határozza meg.

18.1.2 Zavaró, mediátor, collider és azonosíthatóság

Egy egyszerű irányított aciklikus gráf, DAG, az ok-okozati feltevéseket rögzíti. A nyíl jelentése: közvetlen ok. Három szerepet kell élesen elkülöníteni.

A zavaró változó (confounder) közös ok: hat a kezelésre és a kimenetre egyaránt. A rendelési példában a distance_km zavaró, mert nagyobb távolságnál gyakrabban választanak prioritást, és a távolság a késést is növeli; így a priority_flag és a target_late közötti nyers kapcsolat részben a távolságból származik. Gráfosan C→AC \to A és C→YC \to Y. Ha CC megfigyelt, akkor a A←C→YA\leftarrow C \to Y hátsó ajtó út a CC-re való rétegzéssel vagy kiigazítással blokkolható. A rétegzett kiigazítási, standardizálási formula bináris YY esetén:

E[Y(a)]=∑cP(Y=1∣A=a,C=c)P(C=c),a∈{0,1},E[Y(a)] = \sum_c P(Y=1 \mid A=a, C=c)\,P(C=c),\qquad a\in\{0,1\},

ahol cc a zavaró lehetséges értékeit futja be. A képlet a feltételes kockázatokat a zavaró populációs eloszlásával súlyozza; a becsült ATE a két standardizált érték különbsége. A P(C=c)P(C=c) itt a teljes mintabeli marginális arány, nem az AA-feltételes arány.

A mediátor (közvetítő változó) a kezelés és a kimenet közötti ok-okozati lánc közbülső lépése: A→M→YA\to M\to Y. Ha például az elsőbbségi kezelés korábbi dispatch_hour csomagfeladást okoz, és ez csökkenti a késést, akkor a dispatch_hour mediátor. A teljes oksági hatás tartalmazza a mediátoron átmenő közvetett hatást is; ha a modellben a mediátorra is feltételesítünk, blokkoljuk a lánc egy részét, ezért a becslés nem a teljes hatás lesz. A teljes hatás becslésekor a mediátor tiltott kontroll.

A collider (ütköző változó) két másik változó közös okozata: A→C←YA\to C\leftarrow Y. Ha egy colliderre feltételesítünk, nem-oksági asszociáció nyílhat meg a két ok között. A klasszikus kis példa: legyen a panasz CC értéke 11, ha a rendelés prioritást kapott vagy késett; csak a panasznál megfigyelt rendeléseket látjuk. A C=1C=1 szűrés után az AA és YY negatív asszociációt mutat, pedig a teljes populációban függetlenek lehetnek. A colliderre való feltételesítés tehát szelekciós torzítást okoz; ez is tiltott kontroll, kivéve ha kifejezetten a kiválasztási folyamatot akarjuk korrigálni, és a teljes cella ismert.

Az azonosíthatóság (identification) az a kérdés, hogy a megfigyelt eloszlásból — a DAG-feltevésekkel együtt — egyértelműen meghatározható-e a cél oksági mennyiség. A confounder-DAG esetén igen, ha CC mérve van, mert a kiigazítási formula azonosítja az E[Y(a)]E[Y(a)] értékeket. Ha van nem mért zavaró UU, amely AA-ra és YY-ra is hat, akkor az azonosítás nem teljes; a kiigazítás nem távolítja el a torzítást. A collider-szelekciós példában a C=0C=0 cella hiányzik, ezért a teljes populáció ATE-je nem azonosítható a kiválasztott mintából. A regressziós kiigazítás (OLS a lineáris algebra és statisztika találkozásánál) akkor oksági, ha a DAG hátsó ajtó útjai blokkoltak és a modell funkcionális formája helyes; a p-érték önmagában nem azonosítási bizonyíték. A feltételes valószínűség (Esemény, feltételes valószínűség és Bayes) itt úgy tér vissza, hogy a megfigyelt P(Y=1∣A=1)P(Y=1\mid A=1) nem azonos a beavatkozás utáni P(Y(1)=1)P(Y(1)=1) értékkel; a különbség éppen a közös okból származó torzítás.

A matematikai híd röviden: a regresszió a feltételes várható értéket becsli, de a feltételes várható érték csak akkor egyenlő a potenciális kimenet várható értékével, ha a feltételhalmaz blokkolja a hátsó ajtó utakat. Ellenkező esetben a jó illeszkedésű modell is torzított következtetést ad.

18.1.3 Teljes minimális munkapélda: két folyamat, asszociáció eltérő okból

Az alábbi szimuláció két DAG-ot állít elő. Mindkettőben van megfigyelt asszociáció AA és YY között, de az ok teljesen más: az elsőben zavaró okozza, a másodikban collider-szelekció. A szimuláció a Python standard numerikus könyvtáraival, numpy és pandas segítségével fut; a seed rögzíti a véletlent. A kimenet szemléltető, nem ebben a generálási módban futtatott mérés.

Az első DAG: C→AC\to A, C→YC\to Y, és nincs A→YA\to Y nyíl; a valódi ATE 0.

A második DAG: A→CA\to C, Y→CY\to C, ahol C=1C=1 jelenti a megfigyelt panaszcellát; a megfigyelt minta csak C=1C=1. A valódi ATE 0, mert AA és YY független a teljes populációban.

import numpy as np
import pandas as pd

rng = np.random.default_rng(20261020)
n = 60_000

# 1. folyamat: confounder; valódi ATE = 0
# C -> A, C -> Y; nincs A -> Y
c1 = rng.binomial(1, 0.5, n)
a1 = rng.binomial(1, 0.2 + 0.4 * c1, n)   # P(A=1|C=0)=0.2, P(A=1|C=1)=0.6
y1 = rng.binomial(1, 0.1 + 0.3 * c1, n)   # P(Y=1|C=0)=0.1, P(Y=1|C=1)=0.4

df1 = pd.DataFrame({"A": a1, "Y": y1, "C": c1})

# Nyers kockázati különbség A=1 vs A=0
rd_raw_1 = (
    df1.loc[df1.A == 1, "Y"].mean()
    - df1.loc[df1.A == 0, "Y"].mean()
)

# Standardizálás a confounder C szerint
def standardized_rd(df):
    cell = {}
    for c in [0, 1]:
        for a in [0, 1]:
            cell[(a, c)] = df.loc[(df.C == c) & (df.A == a), "Y"].mean()
    p_c = df.C.value_counts(normalize=True).sort_index()
    e_y1 = cell[(1, 0)] * p_c[0] + cell[(1, 1)] * p_c[1]
    e_y0 = cell[(0, 0)] * p_c[0] + cell[(0, 1)] * p_c[1]
    return e_y1 - e_y0

rd_adj_1 = standardized_rd(df1)

# 2. folyamat: collider; valódi ATE = 0
# A és Y független; C = A OR Y, és csak C=1-et figyelünk meg
a2 = rng.binomial(1, 0.5, n)
y2 = rng.binomial(1, 0.5, n)
c2 = np.maximum(a2, y2)          # C=1, ha A=1 vagy Y=1
df2_all = pd.DataFrame({"A": a2, "Y": y2, "C": c2})
df2_obs = df2_all[df2_all.C == 1]

rd_all_2 = (
    df2_all.loc[df2_all.A == 1, "Y"].mean()
    - df2_all.loc[df2_all.A == 0, "Y"].mean()
)
rd_obs_2 = (
    df2_obs.loc[df2_obs.A == 1, "Y"].mean()
    - df2_obs.loc[df2_obs.A == 0, "Y"].mean()
)

print("1. confounder: nyers RD =", round(rd_raw_1, 3),
      "| C-re igazított RD =", round(rd_adj_1, 3))
print("2. collider: teljes populáció RD =", round(rd_all_2, 3),
      "| C=1 megfigyelt RD =", round(rd_obs_2, 3))

A várható, elméleti kimenet a megadott generáló valószínűségekből:

1. confounder: nyers RD = 0.125 | C-re igazított RD = 0.0
2. collider: teljes populáció RD = 0.0 | C=1 megfigyelt RD = -0.5

A szimulációs zaj miatt n=60000n=60\,000 esetén a harmadik tizedesjegyben ±0,01\pm0{,}01 nagyságrendű eltérés elfogadható; a kimenetet helyi futtatással kell rögzíteni.

18.1.4 Eredmény értelmezése

A confounder-folyamatban a nyers kockázati különbség várhatóan 0,1250{,}125, pedig az AA-ból YY-ra nincs közvetlen hatás. Kézi kontroll: a DAG szerint AA és YY feltételesen függetlenek CC-től, ezért mindkét CC-rétegben ugyanaz a feltételes kockázat, és a standardizálás pontosan nullát ad. Az analitikus nyers érték is ellenőrizhető: a súlyozott feltételes valószínűségekből P(Y=1∣A=1)=0,325P(Y=1\mid A=1)=0{,}325 és P(Y=1∣A=0)=0,200P(Y=1\mid A=0)=0{,}200, különbség 0,1250{,}125.

A collider-folyamatban a teljes populációban AA és YY független, ezért az RD nulla; a C=1C=1 szűrt mintában viszont a közös ok-okozat feltétele negatív asszociációt generál. A determinisztikus C=A∨YC=A\lor Y esetben a C=1C=1 minta kontingenciatáblája a három lehetséges cellát (1,1),(1,0),(0,1)(1,1), (1,0), (0,1) egyenlő arányban tartalmazza, a (0,0)(0,0) cella hiányzik, ezért A=1A=1-nél Y=1Y=1 arány 1/21/2, A=0A=0-nál 11, különbség −0,5-0{,}5. Ez a szám a kiválasztási torzítás, nem oksági hatás.

A labor-átadandó követelmény is ez: két folyamatot rajzolunk, amelyek erős, de okilag eltérően keletkező asszociációt adnak. Az elsőnél a következtetési korlát az, hogy a hatás csak akkor azonosítható, ha a zavaró CC mérve van; a másodiknál az, hogy a C=1C=1-re szűrt minta önmagában nem azonosítja a teljes ATE-t, mert a nullcellát nem látjuk.

Azonosítási feltételek: megfigyeléses kiigazítással oksági hatás csak konzisztencia, feltételes kicserélhetőség (nincs kezeletlen zavarás) és pozitivitás/overlap megfelelő változatai mellett azonosítható. A DAG önmagában nem bizonyítja, hogy ezek az empirikus esetben teljesülnek.

18.1.5 Feladatok

  1. Értelmezés. Adott a DAG: distance_km -> priority_flag, distance_km -> target_late, priority_flag -> dispatch_hour, dispatch_hour -> target_late.

    1. Mi a distance_km és mi a dispatch_hour szerepe? (b) Mely változó(k) szerint kell igazítani, ha a priority_flag teljes oksági hatását akarjuk a target_late-re? (c) Mi történik, ha a regresszióban a dispatch_hour is szerepel?
  2. Implementálás. Írjon szimulációt, amelyben A→M→YA\to M\to Y és A→YA\to Y is van, nincs zavaró. Legyen P(A=1)=0,4P(A=1)=0{,}4, P(M=1∣A)=0,3+0,4AP(M=1\mid A)=0{,}3+0{,}4A, és P(Y=1∣A,M)=0,1+0,2A+0,2MP(Y=1\mid A,M)=0{,}1+0{,}2A+0{,}2M. Használjon n=50_000 és seed=7 értéket. Számítsa ki a nyers kockázati különbséget, az MM-re standardizált rétegzett becslést, és adja meg a teljes ATE elméleti értékét.

  3. Hibadiagnózis. Egy elemző az 1. feladat DAG-jához illeszti a smf.logit('target_late ~ priority_flag + dispatch_hour + distance_km', data=orders).fit() modellt. A priority_flag együtthatójára p<0.001 adódik, ezért azt állítja, hogy a prioritás beavatkozás hatásos. Soroljon fel legalább két okot, amiért ez az állítás nem következik a modellből, és javasoljon egy ellenőrzést.

  4. Megváltozott feltétel. A collider-példában most a naplófájl a C=0C=0 eseteket is tartalmazza, tehát a teljes df2_all tábla rendelkezésre áll. Módosítsa az elemzést úgy, hogy a teljes adaton számol, és értelmezze az ATE-t. Mi változik a DAG-ban?

18.1.6 Megoldások és indoklás

    1. A distance_km zavaró (confounder), mert hat a kezelésre és a kimenetre is. A dispatch_hour mediátor, mert a priority_flag rajta keresztül is hat a késésre. (b) A teljes hatáshoz csak a zavarót kell blokkolni: distance_km szerint rétegezni vagy regresszióban kontrollálni. (c) A dispatch_hour kontrollja blokkolja a priority_flag -> dispatch_hour -> target_late közvetett utat, ezért a becslés a teljes hatásnál kisebb, jellemzően a közvetlen hatáshoz közelebbi értéket ad; a teljes hatás becslésekor ez tiltott kontroll.
  1. A módosított kód:
rng = np.random.default_rng(7)
n = 50_000
a3 = rng.binomial(1, 0.4, n)
m3 = rng.binomial(1, 0.3 + 0.4 * a3, n)
y3 = rng.binomial(1, 0.1 + 0.2 * a3 + 0.2 * m3, n)

df3 = pd.DataFrame({"A": a3, "M": m3, "Y": y3})

rd_raw_3 = (
    df3.loc[df3.A == 1, "Y"].mean()
    - df3.loc[df3.A == 0, "Y"].mean()
)

# M-re standardizált rétegzett becslés
cell3 = {}
for m in [0, 1]:
    for a in [0, 1]:
        cell3[(a, m)] = df3.loc[(df3.M == m) & (df3.A == a), "Y"].mean()
p_m = df3.M.value_counts(normalize=True).sort_index()
e_y1_m = cell3[(1, 0)] * p_m[0] + cell3[(1, 1)] * p_m[1]
e_y0_m = cell3[(0, 0)] * p_m[0] + cell3[(0, 1)] * p_m[1]
rd_adj_m = e_y1_m - e_y0_m

print("nyers RD =", round(rd_raw_3, 3),
      "| M-re igazított RD =", round(rd_adj_m, 3))

Az elméleti teljes ATE kiszámítása: E[Y(0)]=0,3⋅0,3+0,7⋅0,1=0,16E[Y(0)] = 0{,}3\cdot0{,}3+0{,}7\cdot0{,}1=0{,}16, E[Y(1)]=0,7⋅0,5+0,3⋅0,3=0,44E[Y(1)] = 0{,}7\cdot0{,}5+0{,}3\cdot0{,}3=0{,}44, tehát ATE =0,28=0{,}28. A nyers RD várt értéke 0,280{,}28, mert nincs zavaró; az MM-re igazított RD várt értéke 0,200{,}20, mert minden MM-rétegben a generáló képlet AA-együtthatója 0,20{,}2. A különbség a mediátoron átmenő közvetett hatás blokkolásából adódik. A tűrés az n=50000n=50\,000-es szimulációhoz körülbelül ±0,01\pm0{,}01.

  1. A hibák: (a) a dispatch_hour mediátor kontrollja torzítja a teljes hatást; (b) a megfigyelési adat nem randomizált, ezért a feltételes regressziós együttható csak akkor oksági, ha minden zavaró mérve van és a logit modell jól specifikált; a p-érték a nullmodell szerinti szélsőség, nem oksági azonosítás (IV. ellenőrző pont: korrekt statisztikai állítás); lehetnek nem mért zavarók. Ellenőrzésként randomizált kísérlet vagy a jövő alfejezetben tárgyalt kísérleti protokoll szerinti allokáció használható.

  2. A teljes df2_all mintán a nyers RD várhatóan 00, mert AA és YY függetlenek a generáló folyamatban. A collider-szelekció megszűnik, mert a (A=0,Y=0)(A=0,Y=0) cella is megfigyelt. A DAG-ban nincs többé a C=1C=1-re való feltételesítés, tehát az ATE azonosítható, és a becslés nulla. Az azonosíthatóságot a teljes megfigyelés állítja helyre, nem a modell bonyolultsága.

18.1.7 Gyakori tévedés ellenpéldával

Jó prediktív pontosságból nem következik oksági azonosíthatóság. Az első szimulációban a priority_flag-ből jobban jósolható a target_late, mint a puszta alaparányból: az A=1A=1 csoportbeli késési arány 0,3250{,}325, az A=0A=0 csoportbeli 0,2000{,}200, szemben a 0,250{,}25-ös marginálissal. Egy modell, amely az AA-t használja, csökkenti a Brier-veszteséget, mégis a valódi beavatkozási hatás nulla. A collider-mintában ugyanez a helyzet: a modell a C=1C=1 adaton hasznos prediktort talál, de a teljes populációban a kezelés nem befolyásolja a kimenetet. A predikció a megfigyelt eloszlást tanulja; az oksági kérdés a meg nem figyelt potenciális kimenetekről szól. A regresszió (OLS a lineáris algebra és statisztika találkozásánál) és a GLM (GLM: bináris és számlálási cél) tehát nem teszik automatikusan okságivá a következtetést; ehhez a DAG azonosítási feltételei kellenek.

Önellenőrzés: ellenőrizze, hogy a confounder-standardizálásnál a P(C=c)P(C=c) súlyokat a teljes mintából vette-e, nem az AA-feltételes csoportból. Ha a súlyokat felcseréli, a torzító összetételhatást újra bevezeti. A collider-példában pedig kérdezze meg: a (0,0)(0,0) cella a szűrés miatt hiányzik, vagy a valós mechanizmusban nem is lehetséges? A válasz eldönti, hogy a teljes ATE azonosítható-e.

18.1.8 Rövid összefoglaló

A potenciális kimenet Y(a)Y(a) a beavatkozás utáni kimenetet írja le, a megfigyelt Y=AiYi(1)+(1−Ai)Yi(0)Y=A_iY_i(1)+(1-A_i)Y_i(0). A zavaró közös ok; rétegzéssel blokkolható, ha mérve van, és a kiigazítási formula a feltételes kockázatokat a zavaró marginális eloszlásával súlyozza. A mediátor és a collider tiltott kontroll a teljes hatás becslésekor: előbbi blokkolja a közvetett utat, utóbbi szelekciós asszociációt nyit. Azonosíthatóság nélkül a jó predikció sem oksági következtetés. Következő lépésként Randomizálás és kísérleti protokoll alfejezetben a randomizálást és a kísérleti protokollt építjük fel, amely a zavaró feltételezése nélkül teszi azonosíthatóvá az ATE-t.