因果圖:節點與箭頭
在上一篇指南裡,我們遇到一個殘酷的事實:兩件事一起變動,原因可能與「其中一個造成另一個」毫無關係。相關不等於因果。但這留下一個實務上的問題沒有解答——當我們在資料裡「確實」看到關聯時,要怎麼判斷它是不是因果,又得做什麼才能量到真正的效果?解決這件事最有用的工具不是公式,而是一張圖。
這張圖叫做 因果有向無環圖(causal DAG,directed acyclic graph 的縮寫)。別被名字嚇到,它其實就是一些圓點與箭頭。每個圓點稱為節點(node),代表你在乎的一個變數——例如一個人的喝咖啡量、他得心臟病的風險,或他抽不抽菸。每個箭頭從「原因」指向「它直接影響的東西」:如果我們相信抽菸會直接提高心臟病風險,就從「抽菸」畫一個箭頭到「心臟病」。
名字裡有兩個詞帶有真正的意義。「有向」(directed)指每個箭頭都有方向——「A 造成 B」和「B 造成 A」並不一樣。「無環」(acyclic)指你永遠不可能順著箭頭繞一圈回到出發點:一個變數不能(即使是間接地)造成它自己。這兩條規則合起來,逼你把「什麼影響什麼」這個故事清楚地寫下來,而不是一團模糊的亂線。
再多一個詞我們就能開始了。「路徑」(path)是指:先暫時不管箭頭方向時,連接兩個變數的任何一串箭頭。有些路徑是從因到果的真正因果路線——咖啡 →…→ 心臟病。另一些路徑則是偷偷繞道,會製造出關聯、卻沒有任何因果順著它流動。這篇指南的整個重點,就是學會分辨這兩種路徑。而有一個說法會一直出現:「控制」一個變數(也叫「調整」或「條件化」)的意思,就是「拿同類比同類」——例如,分別在抽菸者與不抽菸者「之內」看咖啡與心臟病的關係,而不是全部混在一起。
干擾因子:共同的原因
這裡有個真實、騙了大家好多年的例子。研究發現重度咖啡飲用者心臟病較多。咖啡會傷心臟嗎?光看資料你會說「會」。但有第三個變數潛伏其中:抽菸。會抽菸的人通常也喝比較多咖啡,而抽菸本身就會提高心臟病風險。抽菸是我們研究的東西(咖啡)與結果(心臟病)兩者「共同的原因」。
像抽菸這樣的變數稱為干擾因子(confounder)。它在因果圖上的標誌,是有「兩個」箭頭從它指出去:一個指向處理(treatment)、一個指向結果。這個形狀——處理 ← 干擾因子 → 結果——叫做「分岔」(fork),它在咖啡與心臟病之間打開了一條後門路徑,這條路徑帶著一個假的、非因果的關聯。我們量到了這個關聯,卻誤把它當成因果。
一張三節點的圖。一個箭頭從「抽菸」指向「咖啡」,另一個從「抽菸」指向「心臟病」,還有一個獨立的箭頭從「咖啡」指向「心臟病」。「抽菸」位於上方,是共同原因。
那要怎麼拿到真正的效果?我們要「拿同類比同類」。把資料依干擾因子切開:只在抽菸者之中看咖啡與心臟病的關係,再只在不抽菸者之中看一次。在每一組裡,抽菸已經被固定住,就再也無法製造假的連結。如果咖啡與心臟病的關聯在兩組之中都縮到幾乎為零,那原本的關聯就是干擾、而不是因果。這種「切開再比較」,正是統計學家說的「控制」、「調整」或「條件化」干擾因子的意思。
把「我們實際量到的東西」想像成兩部分之和,會很有幫助。
每個被干擾的比較,都是「真正的效果」加上一個來自「打開的後門路徑」的偏誤項。控制干擾因子會關掉那條路徑、把偏誤歸零——「前提是」你量到了、也調整了對的那些變數。
對撞因子:為什麼「控制」反而會害了你
到這裡,你可能會想採用一條簡單的規則:「拿不準時,全部都控制就對了。」把每個變數都丟進迴歸,讓數學自己去理清。這是資料分析中最常見、也最傷的錯誤之一,原因就在一種叫做「對撞因子」的東西。
對撞因子(collider)是干擾因子的鏡像。它不是兩個箭頭指出去,而是兩個箭頭指「進來」——它是兩個變數的「共同結果」:原因 A → 對撞因子 ← 原因 B。兩個原因在它身上「對撞」。令人意外的地方來了:經過對撞因子的這條路徑,本來自然「沒有」關聯——這條路徑預設是「關上的」。但你一旦控制了對撞因子,就會「打開」這條路徑,憑空製造出一個原本不存在的關聯。
一個例子就能讓你秒懂。想像在一般大眾裡,一個演員的「演技」與「外貌」完全無關——知道其中一個,對另一個一無所知。現在只看「被選上演了一部賣座電影」的演員。要被選上,你通常得「演技很強」或「外貌很出眾」(或兩者皆是)。「被選上」就是一個對撞因子:演技 → 被選上 ← 外貌。在被選上的這群人之中,這兩項特質現在變成「負相關」了——最漂亮的明星往往演技較弱,而演技最精湛的演員往往長相較普通。這不是因為美貌會侵蝕演技,而是因為我們依著它們的「共同結果」做了篩選。
一張圖:「演技」與「外貌」各有一個箭頭指向第三個節點「被選上」,「被選上」用方框框起來,表示正在對它做條件化。「演技」與「外貌」之間沒有直接的箭頭。
這不是什麼罕見的特例。每當你只分析這個世界裡「被篩過的一小片」——只看有購買的顧客、只看病到需要住院的病人、只看撐到第二週的使用者——你就是在對一個對撞因子做條件化,因為「進入這一片」是許多事情的共同結果。這正是選擇偏誤背後的機制。實務上的教訓很直白:不要只因為「你能控制」就去控制一個變數。控制會關上分岔(好事——它消滅干擾),卻會打開對撞(壞事——它製造偏誤)。你得知道哪個是哪個,而要做到這點,你需要那張圖。
中介因子:別擋住你正在研究的那條路
變數還有第三種角色,連謹慎的分析者都會在這裡跌倒。中介因子(mediator)位在因果路徑「之上」,介於因與果之間:處理 → 中介因子 → 結果。它是「原因產生結果的其中一條途徑」——它承載了一部分真正的因果故事,而不是偽造一個。
假設「教育程度愈高、收入真的愈高」。其中一個重要原因是:教育幫你找到收入較高的那「種」工作:教育 → 工作種類 → 收入。工作種類就是中介因子。現在想想,如果你「控制工作種類」——也就是去比較「同一種工作」裡,高教育與低教育者的收入,會發生什麼事?你剛剛擋住了「教育帶來回報」的主要途徑。剩下的那個效果(在固定工作下、教育的「直接」效果)看起來會很小,於是你可能會誤判:教育對收入幾乎沒影響。
所以「控制一個變數」這個同樣的統計動作,是對、是錯、還是有害,完全取決於這個變數在因果故事裡的「角色」:控制干擾因子(共同原因)以消除偏誤;放著中介因子(路徑上的一站)別動以保住效果;而絕不要控制對撞因子(共同結果),否則你會無中生有地製造偏誤。在這三種情況下,變數的名字和它的數字看起來一模一樣。只有那張圖能把它們分開。
用白話說後門準則
現在我們可以說出那條把一切綁在一起的規則了。它叫做後門準則(backdoor criterion),它回答了那個核心問題:要量到「處理 X 對結果 Y」的真正因果效果,我必須控制哪些變數?
先說名字。「後門路徑」是指:任何從 X 到 Y、而且「離開 X 時是經由一個指向 X 的箭頭」的路徑——也就是形如 X ← …— Y 的路徑。這些是非因果的側門小路(每個干擾因子都會打開一條)。真正的因果效果只從「前門」流出:X → …→ Y。我們的任務,是擋住每一條後門路徑,同時讓前門大大敞開。
一條路徑是「開」還是「被擋住」,取決於關於它「中間節點」的三條小規則:
- 「鏈」(A → B → C)與「分岔」(A ← B → C)預設是「開」的。控制中間變數 B 會把它們關上。(分岔就是干擾——這正是「控制有幫助」的情況。)
- 「對撞」(A → B ← C)預設是「關」的。控制中間變數 B——或控制任何「由 B 造成的東西」——都會把它「打開」。(這正是「控制有害」的情況。)
- 只要路徑上「任何一個」節點被擋住,整條路徑就被擋住了。所以要讓一整條路徑失效,你只需要在「一個」點上擋住它。
現在說後門準則本身,兩個條款。一組變數 Z 能讓你「識別」(identify)X 對 Y 的因果效果,如果:(1) 控制 Z 能擋住從 X 到 Y 的「每一條」後門路徑;而且 (2) Z 裡「不包含任何 X 的後代」——也就是任何由 X 造成的東西,這就排除了中介因子,以及任何位在處理下游的對撞因子。找到這樣的一組 Z、對它做調整,你量到的關聯就會變成因果效果。
當 Z 滿足這條準則時,把「各組內的效果」合起來的做法有個名字——調整公式(adjustment formula)。用白話說:先算出 Z 的每個層級「之內」的效果,再把這些效果平均起來,每個層級用「它在整個母體中有多常見」來加權。
後門調整公式。左邊的 do(X=x) 意思是「如果我們介入、把 X 設成 x」——這是因果量——而不只是「觀察到 X=x」。關鍵在權重 Pr(Z=z):用的是「整個母體」的組成,而不是各處理組「各自」的組成。用各組自己的組成,正是接下來「辛普森悖論」背後的錯誤。
用有向無環圖解釋辛普森悖論
到目前為止的一切,會在一個著名的謎題上爆發出來。辛普森悖論(Simpson's paradox)指的是:一個在「每個子群」裡都成立的型態,一旦把各群合併起來,竟然「反轉」了。它看起來像個矛盾,其實只是干擾披上了一件戲劇化的外衣。
這裡是一個經典、形狀真實的比較:新療法對上舊療法治療腎結石的康復人數。病人分成兩種嚴重程度,輕症與重症,而且——關鍵在這——醫生比較常把積極的新療法用在重症身上。分別看每個嚴重程度,新療法兩次都贏。輕症:新療法治好 87 人中的 81 人(93%),舊療法治好 270 人中的 234 人(87%)。重症:新療法治好 263 人中的 192 人(73%),舊療法治好 80 人中的 55 人(69%)。然而把所有人合併起來,卻反轉了:在全體病人中,新療法治好 350 人中的 273 人(78%),舊療法卻治好 350 人中的 289 人(83%)。新療法在輕症較好、在重症也較好,整體卻不知怎地反而較差。
為什麼會反轉?「嚴重程度」是一個干擾因子:它驅動病人接受哪種治療(嚴重 → 新療法),「也」驅動康復(嚴重 → 康復率較低)。把它畫出來——嚴重程度 → 治療、嚴重程度 → 康復、治療 → 康復——這正是前面那個干擾因子三角。新療法被分到的是難治的病例。合併後的 78% 對 83%,說話的是那條打開的後門路徑,不是治療本身。
那醫生該照哪個數字?該照「各嚴重程度之內」的那組。它們拿同類比同類;而合併後的數字,是拿一個「大多是重症」的群去比一個「大多是輕症」的群。前面的調整公式講的正是這件事:用「整體有多常見」去加權每個嚴重程度的結果。合併後的比率作了弊——它用「各治療各自的」病例組成去加權,而兩邊的組成天差地遠,這正是讓比較反轉的原因。
合併後的比率,用 Pr(z | 新)——也就是這一組「自己的」病例組成——去加權每個嚴重程度;而舊療法那一組用的是另一組不同的組成 Pr(z | 舊)。在相同的「各組數字」上套不同的權重,正是反轉的成因。調整公式則改用兩組「共用」的母體組成 Pr(z),給出誠實的比較。
import pandas as pd
# one row per patient: severity ('mild'/'severe'), treatment ('new'/'old'), cured (1/0)
# Crude pooled cure rate — the MISLEADING number
df.groupby('treatment')['cured'].mean()
# Adjusted view — cure rate within each severity stratum (the honest comparison)
df.groupby(['severity', 'treatment'])['cured'].mean()一張互動圖,比較新舊療法的康復率:先依輕症與重症分開顯示(新療法在兩者都較高),再合併所有病人顯示(新療法反而較低),呈現這個反轉。
畫出你自己的因果圖
你不需要什麼花俏的軟體就能享受到這一切的好處——一張餐巾紙就夠了。真正保護你的,是「在你跑迴歸之前先把圖畫出來」這個紀律。這裡有一套你下次面對真實問題時就能用的流程。
- 點名兩位主角:寫下你的處理 X(你想知道其效果的東西)與結果 Y,並畫出你想量的那個箭頭 X → Y。
- 腦力激盪其他重要的變數——任何可能造成 X、造成 Y,或被它們造成的東西。為每一個加一個節點,連你沒量到的也要加。
- 為你相信的每一個「直接原因」畫一個箭頭,永遠從因指向果。當你其實只是想說「它們相關」時,忍住別畫箭頭——只有直接的因果才配得上一個箭頭。然後檢查沒有任何迴圈。
- 依形狀把每個額外的變數分類:X 與 Y 的共同原因(干擾因子)、X → Y 路徑上的一站(中介因子),或共同結果(對撞因子)。
- 套用後門準則:列出每一條後門路徑,挑一組 Z 把它們全部擋住——控制干擾因子,同時「刻意不」控制中介因子或對撞因子(或任何位在 X 下游的東西)。
- 標出你「無法量到」的干擾因子。每一個都是你分析中一個誠實的破洞——在你的報告裡把它指出來,而不是把它藏起來。
把整條脈絡放在眼裡。一張圖不會讓一個因果主張變成真的;它只是讓你的假設「現形」,再依這些假設告訴你該控制什麼。最難的威脅——一個你從未量到的干擾因子、一個你畫錯的箭頭——住在「假設」裡,而不是在算術裡。當你無法靠「量更多」繞過它們時,你就會去找下一篇指南裡的那些設計:讓世界替你做隨機分派的「自然實驗」。先畫箭頭,再信數字。