動態

詳情 返回 返回

Python因果分析選哪個?六個貝葉斯推斷庫實測對比(含代碼示例) - 動態 詳情

Python 生態裏能用的因果庫有很多選哪個往往要看你對模型的理解程度,以及項目對“可解釋性”的要求。這篇文章將對比了六個目前社區中最常用的因果推斷庫:Bnlearn、Pgmpy、CausalNex、DoWhy、PyAgrum 和 CausalImpact

貝葉斯因果模型

在因果推斷裏所有變量可以粗略分成兩種:驅動變量(driver variables)乘客變量(passenger variables)。驅動變量會直接影響結果,而乘客變量雖然跟結果有關但並不直接影響結果。區分這兩者是整個因果分析的關鍵。比如在預測性維護或設備故障分析裏,如果能識別出“導致故障”的那幾個變量,後續的監控與優化策略就能有針對性地落地。

有時候,看似無關的變量其實藏着重要的效應。比如説假設某個工廠的發動機故障率在不同地區差異很大,你可能認為這是地理差異,其實真正的原因可能是工廠裏濕度、保養週期或人員經驗這樣的隱含驅動因子。因果推斷的價值就在這裏——它幫助區分“看上去相關”和“真正原因”的區別。

相比純預測模型,因果推斷更像是在回答“為什麼”,而不是“多少”。通過找出系統中真正起作用的變量,才能解釋模型的行為,也才能對系統做出有效干預。

數據集與整體實驗思路

為了讓對比更直觀,所有實驗都使用相同的數據:Census Income 數據集。這個經典的數據集包含 48,842 條記錄14 個變量,多數為離散特徵。目標也很簡單:擁有研究生(postgraduate)學歷是否能顯著提高年收入超過 50K 美元的概率?

下面的代碼用於載入和清理數據。連續變量與敏感特徵(如性別、種族等)被移除,以便專注在離散特徵的因果結構學習上。

 # 安裝
pip install datazets

# 導入庫
import datazets as dz  
import pandas as pd  
import numpy as np  
import matplotlib.pyplot as plt  

# 導入數據集並刪除連續型與敏感特徵
df = dz.import_example(data='census_income')

# 數據清洗
drop_cols = ['age', 'fnlwgt', 'education-num', 'capital-gain', 'capital-loss', 'hours-per-week', 'race', 'sex']
df.drop(labels=drop_cols, axis=1, inplace=True)

# 打印樣本
 df.head()

1、Bnlearn

bnlearn

是一個封裝度很高的貝葉斯網絡庫,幾乎把因果分析的標準流程都集成在一套 API 裏。它支持離散、連續和混合類型數據,可以進行 結構學習參數學習(CPD 估計)推理(inference)合成數據生成(synthetic data generation)。更重要的是,它把常用的獨立性檢驗、評分函數、拓撲排序、模型比較和可視化都做成了開箱即用的函數。

下面是使用 HillClimbSearch 和 BIC 評分方法學習結構的完整流程代碼

 # 安裝
pip install bnlearn

# 加載庫
import bnlearn as bn  

# 結構學習
model = bn.structure_learning.fit(df, methodtype='hillclimbsearch', scoretype='bic')  

# 檢驗邊顯著性並剪枝
model = bn.independence_test(model, df, test="chi_square", alpha=0.05, prune=True)  

# 參數學習(可選)
model = bn.parameter_learning.fit(model, df)  

# 繪製圖形
G = bn.plot(model, interactive=False)  
dotgraph = bn.plot_graphviz(model)
 dotgraph.view(filename=r'c:/temp/bnlearn_plot')

模型學得的 DAG(有向無環圖)結構如下:

靜態圖展示了

bnlearn

學出的 Census Income 因果結構圖。交互式版本可以直接查看每條邊的條件概率分佈。

DAG 學成之後,可以直接執行推理。例如計算當教育水平為博士時,收入大於 50K 的後驗概率:

 # 開始推理
 query = bn.inference.fit(model, variables=['salary'], evidence={'education':'Doctorate'})
 print(query)

結果如下:

 salary <=50K : 29.1%
 salary >50K  : 70.9%

這個概率幾乎符合我們的理解:博士學歷的確帶來更高的收入區間。

換成高中畢業(

HS-grad

)再試一次:

 query = bn.inference.fit(model, variables=['salary'], evidence={'education':'HS-grad'})

得到:

 salary <=50K : 83.8%
 salary >50K  : 16.2%

還可以組合多個條件,例如:

 # 當 education=Doctorate 且 marital-status=Never-married 時,預測 workclass
 query = bn.inference.fit(model, variables=['workclass'], evidence={'education':'Doctorate', 'marital-status':'Never-married'})

從整體來看,

bnlearn

適合快速構建因果結構並做概率推理,功能完整。輸入數據可為離散、連續或混合類型。對於想在業務場景中快速驗證因果假設的人,這個庫的學習曲線非常平緩。

2、 Pgmpy

Pgmpy

是一個更偏底層的概率圖模型庫,如果説

bnlearn

是“開箱即用”那

pgmpy

更像是一套“拼裝工具箱”。他的靈活性非常高,但也意味着需要較強的貝葉斯建模功底。

這兩個庫的功能其實有重疊,因為

bnlearn

的底層實現部分依賴

pgmpy

。但在

pgmpy

中,所有步驟都要自己搭建:數據處理、建模、參數估計、推理、可視化都要自己寫。

下面是用

HillClimbSearch

BIC

評分方法做結構學習的例子:

 # 安裝 pgmpy
pip install pgmpy

# 導入函數
from pgmpy.estimators import HillClimbSearch, BicScore, BayesianEstimator
from pgmpy.models import BayesianNetwork, NaiveBayes
from pgmpy.inference import VariableElimination

# 導入數據並刪除連續與敏感特徵
df = bn.import_example(data='census_income')
drop_cols = ['age', 'fnlwgt', 'education-num', 'capital-gain', 'capital-loss', 'hours-per-week', 'race', 'sex']
df.drop(labels=drop_cols, axis=1, inplace=True)

# 創建估計器與評分方法
est = HillClimbSearch(df)
scoring_method = BicScore(df)

# 建立模型並打印邊
model = est.estimate(scoring_method=scoring_method)
 print(model.edges())

建好結構後需要手動擬合條件概率分佈(CPD):

 vec = {  
    'source': ['education', 'marital-status', 'occupation', 'relationship', 'relationship', 'salary'],  
    'target': ['occupation', 'relationship', 'workclass', 'education', 'salary', 'education'],  
    'weight': [True, True, True, True, True, True]  
}  
vec = pd.DataFrame(vec)

# 創建貝葉斯模型
bayesianmodel = BayesianNetwork(vec)

# 擬合模型
bayesianmodel.fit(df, estimator=BayesianEstimator, prior_type='bdeu', equivalent_sample_size=1000)

# 推理
model_infer = VariableElimination(bayesianmodel)
query = model_infer.query(variables=['salary'], evidence={'education':'Doctorate'})
 print(query)

輸出結果與

bnlearn

基本一致:

 salary <=50K : 29.1%
 salary >50K  : 70.9%

不同的是,這個過程需要你自己定義 DAG、參數估計和推理方式。這個庫靈活性很高,但顯然更適合研究者或開發自定義因果框架的場景,而不是想“直接上手跑”的業務人員。

3、CausalNex

CausalNex

是一個專注於從數據中學習因果圖並量化因果效應的 Python 庫。它只支持離散分佈,這點很重要。所以在建模前必須把所有連續變量或高基數特徵離散化,否則模型無法擬合。

文檔裏也明確提到,如果特徵太多、狀態太複雜,模型效果會明顯下降。不過好處是,它提供了一些便捷函數來幫助降低類別數量和處理標籤編碼。

下面是一個完整的示例,仍然使用同樣的 Census Income 數據。第一步需要把所有類別變量轉為數值型(因為 NOTEARS 算法要求矩陣計算):

 # 安裝
pip install causalnex

# 導入庫
from causalnex.structure.notears import from_pandas
from causalnex.network import BayesianNetwork
import networkx as nx
import datazets as dz
from sklearn.preprocessing import LabelEncoder
import matplotlib.pyplot as plt
le = LabelEncoder()

# 導入數據並刪除連續與敏感特徵
df = dz.get(data='census_income')

drop_cols = ['age', 'fnlwgt', 'education-num', 'capital-gain', 'capital-loss', 'hours-per-week', 'race', 'sex']
df.drop(labels=drop_cols, axis=1, inplace=True)

# 轉換為數值型
df_num = df.copy()
for col in df_num.columns:
     df_num[col] = le.fit_transform(df_num[col])

結構學習部分用的是 NOTEARS 算法,可以自動從相關矩陣中推導 DAG。不過輸出可能太密集,需要設定閾值過濾掉弱邊:

 # 結構學習
sm = from_pandas(df_num)

# 閾值剪枝
sm.remove_edges_below_threshold(0.8)

# 繪圖
plt.figure(figsize=(15,10))
edge_width = [d['weight']*0.3 for (u,v,d) in sm.edges(data=True)]
 nx.draw_networkx(sm, node_size=400, arrowsize=20, alpha=0.6, edge_color='b', width=edge_width)


上圖是

CausalNex

學出的結構圖。沒有標籤的節點表示因為閾值太高被剪掉的弱邊。也可以通過調整參數

w_threshold

控制網絡稀疏度,或者用

tabu_edges

禁止某些邊生成。

建好結構後,需要學習節點的條件概率分佈:

 # 第一步:創建 BayesianNetwork 實例
bn = BayesianNetwork(sm)

# 第二步:降低分類特徵基數(可選)
# 第三步:定義每個節點的狀態字典
# 第四步:擬合節點狀態
bn = bn.fit_node_states(df)

# 第五步:擬合條件概率分佈
bn = bn.fit_cpds(df, method="BayesianEstimator", bayes_prior="K2")

# 輸出某個節點的 CPD
result = bn.cpds["education"]
 print(result)
CausalNex

的可解釋性和結構學習能力都不錯,但預處理要求多、類型限制嚴格,對 Python 版本也挑剔(僅兼容 3.6–3.10)。如果只想跑標準數據,它表現穩定,但要集成到更復雜的生產環境,需要額外工作。

4、DoWhy

DoWhy

是一個專注於因果推斷驗證的庫,設計理念與前面提到的貝葉斯網絡工具完全不同。它並不嘗試從數據中直接學習因果圖,而是要求用户顯式定義因果假設,包括:

  • 結果變量(outcome variable)
  • 處理變量(treatment variable)
  • 潛在混雜變量(common causes)

也就是説

DoWhy

更像是一個驗證框架用來系統地質疑和檢驗你提出的因果假設,而不是生成因果結構。

如果沒有提供 DAG(因果圖),它會自動把所有變量連接到結果與處理變量上。但實際使用中最好結合領域知識自行定義 DAG,否則模型會過度簡化。

使用同樣的 Census Income 數據集,只是處理變量定義為“是否擁有博士學位”:

 # 安裝  
pip install dowhy

# 導入庫  
from dowhy import CausalModel  
import dowhy.datasets  
import datazets as dz  
from sklearn.preprocessing import LabelEncoder  
import numpy as np  
le = LabelEncoder()  

# 導入數據並刪除連續和敏感特徵  
df = dz.get(data='census_income')  

drop_cols = ['age', 'fnlwgt', 'education-num', 'capital-gain', 'capital-loss', 'hours-per-week', 'race', 'sex']  
df.drop(labels=drop_cols, axis=1, inplace=True)  

# 處理變量必須為二元  
df['education'] = df['education']=='Doctorate'  

# 將數據轉換為數值型  
df_num = df.copy()  
for col in df_num.columns:  
    df_num[col] = le.fit_transform(df_num[col])  

# 指定處理變量、結果變量和潛在混雜變量  
treatment = "education"  
outcome = "salary"  

# Step 1: 創建因果圖
model = CausalModel(  
        data=df_num,  
        treatment=treatment,  
        outcome=outcome,  
        common_causes=list(df.columns[~np.isin(df.columns, [treatment, outcome])]),  
        graph_builder='ges',  
        alpha=0.05,  
        )  

# 查看模型  
 model.view_model()

上圖展示了

DoWhy

自動生成的 DAG,結果變量為“salary”,處理變量為“education”。可以看到,模型假設教育水平會影響薪資,並且兩者都可能受婚姻狀態、工作類型等混雜因素的干擾。

接下來是識別和估計因果效應的步驟:

 # Step 2: 識別因果效應  
 identified_estimand = model.identify_effect(proceed_when_unidentifiable=True)
 print(identified_estimand)

一旦模型識別出可估計的因果效應,就可以計算平均處理效應(ATE):

 # Step 3: 估計效應  
 estimate = model.estimate_effect(identified_estimand, method_name="backdoor.propensity_score_stratification")
 print(estimate)

輸出的平均效應值約為 0.47,説明博士學位與高收入存在明顯的正向因果關係。最後再做穩健性檢驗:

 # Step 4: 穩健性驗證  
 refute_results = model.refute_estimate(identified_estimand, estimate, method_name="random_common_cause")

DoWhy在學術界非常常見,優點是框架清晰、假設透明、驗證邏輯完備; 缺點是對輸入要求高(變量需二值化、分類需數值編碼),且不能從數據自動學習結構。

換句話説,它假設你已經知道潛在的因果關係,現在只想驗證這個假設是否合理。

5、PyAgrum

PyAgrum

是另一個功能相對完整的概率圖模型庫。它支持貝葉斯網絡、馬爾可夫網絡以及決策圖的構建,能做結構學習、參數學習、推理和可視化。並且語法偏工程化,比

pgmpy

直觀一些,但要求所有變量都必須是離散型。

如果數據裏有缺失值或連續變量需要進行處理,否則算法會直接報錯。下面的例子展示了從數據清洗到結構學習的完整流程:

 # 安裝  
pip install pyagrum  

# 可選:安裝可視化依賴  
pip install setgraphviz

import datazets as dz  
import pandas as pd  
import pyagrum as gum  
import pyagrum.lib.notebook as gnb  
import pyagrum.lib.explain as explain  
import pyagrum.lib.bn_vs_bn as bnvsbn  

# 導入可視化設置  
from setgraphviz import setgraphviz  
setgraphviz()  

# 導入數據並刪除連續與敏感特徵  
df = dz.get(data='census_income')  

drop_cols = ['age', 'fnlwgt', 'education-num', 'capital-gain', 'capital-loss', 'hours-per-week', 'race', 'sex']  
df.drop(labels=drop_cols, axis=1, inplace=True)  

# 清理缺失值  
df2 = df.dropna().copy()  
df2 = df2.fillna("missing").replace("?", "missing")  

# 所有列轉換為分類類型  
for col in df2.columns:  
    df2[col] = df2[col].astype("category")  

# 創建學習器  
learner = gum.BNLearner(df2)  
learner.useScoreBIC()  
learner.useGreedyHillClimbing()  

# 學習結構  
bn = learner.learnBN()  

# 學習參數  
bn2 = learner.learnParameters(bn.dag())  

# 可視化結果  
 gnb.showBN(bn2)

上圖展示了

PyAgrum

學到的貝葉斯網絡結構。節點之間的箭頭表示潛在的因果方向。

它提供了一系列輔助模塊,可以比較不同結構(

bn_vs_bn

)、解釋單個節點(

explain

)或生成報告。

PyAgrum

的特點是:學習過程透明、支持約束學習(可指定禁止/強制邊),但代價是輸入準備相對繁瑣。 沒有自動缺失值處理、連續特徵必須離散化,而且某些函數依賴 Graphviz 進行可視化。

6、CausalImpact

CausalImpact

原本是 Google 的開源項目,用於通過貝葉斯結構時間序列模型來衡量干預效果。 它與前面幾個庫不同,不學習因果圖而是針對時間序列數據計算干預帶來的量化影響。

常見場景是評估營銷活動、價格調整或新功能上線的效果。比如,一個電商網站想知道廣告活動是否帶來了實際銷售提升。

CausalImpact

會先用干預前的數據建立基線模型,再將干預後的實際觀測值與預測值比較,計算“反事實差異”。

下面是一個簡潔的示例,構造 100 個樣本點,並在第 71 個時間點之後人為引入干預效應:

 # 安裝  
pip install causalimpact

# 導入庫  
import numpy as np  
import pandas as pd  
from statsmodels.tsa.arima_process import arma_generate_sample  
import matplotlib.pyplot as plt  
from causalimpact import CausalImpact  

# 生成樣本  
x1 = arma_generate_sample(ar=[0.999], ma=[0.9], nsample=100) + 100  
y = 1.2 * x1 + np.random.randn(100)  
y[71:] = y[71:] + 10  
data = pd.DataFrame(np.array([y, x1]).T, columns=["y","x1"])  

# 初始化模型  
impact = CausalImpact(data, pre_period=[0,69], post_period=[71,99])  

# 執行推斷  
impact.run()  

# 繪圖與結果  
impact.plot()  
 impact.summary()


圖中上半部分顯示實際值與模型預測值的對比;中間部分是兩者的差值(即時效應);下半部分是累計效應。

結果顯示,干預帶來了顯著提升,概率為 100%,P 值為 0。這種方法假設干預前後協變量與響應變量的關係穩定,且協變量本身不受干預影響。若滿足這些假設,

CausalImpact

在定量評估干預效應方面非常可靠。

總結

六個庫的思路差異很大。從整體來看,可以分成兩類:

結構學習型

Bnlearn

Pgmpy

CausalNex

PyAgrum

用於發現變量間的潛在因果關係,適合探索性分析與結構建模。

因果效應型

DoWhy

CausalImpact

側重在給定結構或時間序列下量化干預效果,適合政策分析或實驗驗證。

從工程角度看:

Bnlearn

的易用性最高,接口友好、兼容性強,是入門和快速驗證的好工具;

Pgmpy

靈活性最強,適合深度研究與自定義算法;

CausalNex

擁有良好的可解釋圖形界面,但依賴特定 Python 版本;

DoWhy

是學術研究常用工具,強調理論嚴謹性;

PyAgrum

穩定、透明,但數據準備較重;

CausalImpact

專注時間序列因果評估。

六個因果庫覆蓋了從結構學習到因果效應估計的完整路徑。

Bnlearn

Pgmpy

CausalNex

PyAgrum

負責構建網絡、識別驅動因素;

DoWhy

CausalImpact

則更像“量化工具”,用於評估處理或干預帶來的實際影響。

https://avoid.overfit.cn/post/dc97c8f709bc4c56bfec860dd800d75c

Add a new 評論

Some HTML is okay.