本文参考微软dowhy官网文档,并参考相关博客进行整理而来,官方地址:https://github.com/py-why/dowhy
因果推理是基于观察数据进行反事实估计,分析干预与结果之间的因果关系。
DoWhy是微软发布的端到端因果推断Python库,主要特点是:
DoWhy 的整个因果推断过程可以划分为四大步骤:
先看一张流程图:
对四个流程进行解释说明:
为每个问题创建一个因果图模型,以保证因果假设的明确性。该因果图不需要是完整的,你可以只提供部分图,来表示某些变量的先验知识(即指定其类型),DoWhy 支持自动将剩余的变量视为潜在的混杂因子。
目前,DoWhy支持以下形式的因果假设:
基于构建的因果图,DoWhy 会基于所有可能的方式来识别因果效应。识别方式包含:
基于估计干预分配的方法(Methods based on estimating the treatment assignment)
基于估计结果模型的方法(Methods based on estimating the outcome model)
基于工具变量等式的方法(Methods based on the instrumental variable equation)
基于前门准则和一般中介的方法(Methods for front-door criterion and general mediation)
DoWhy 支持多种反驳方法来验证估计的正确性,具体列表如下:
依旧以微软官方文档为主进行学习,其中包含基础案例和酒店住宿分析问题。
windows环境下,请在python3.8以上的环境下进行安装。
pip install dowhy
或
conda install -c conda-forge dowhy
其次是需要安装pygraphviz,windows具体安装教程参考:传送门
from dowhy import CausalModel
import dowhy.datasets# Load some sample data
data = dowhy.datasets.linear_dataset(beta=10, # 因果效应值num_common_causes=5, # 混杂因子,用w表示,作用于干预变量和结果变量num_instruments=2, # 工具变量,用 Z 表示,作用于干预变量(间接影响结果)num_samples=10000, # 样本数量treatment_is_binary=True) # 干预为二元变量,用 v 表示
df = data["df"] # DoWhy 使用 pandas 的 dataframe 来载入数据
print(df.head(5))
结果输出如下:
Z0 Z1 W0 W1 W2 W3 W4 v0 y
0 0.0 0.478130 0.313025 0.274645 1.794765 -1.551661 1.250724 True 17.033979
1 0.0 0.546833 -0.197845 -2.404349 1.120160 -1.011344 0.812087 True 3.074740
2 0.0 0.874135 1.047078 -0.056460 0.440413 -0.067349 0.970203 True 15.530070
3 0.0 0.913483 0.934282 -1.180582 0.498135 -1.079074 -1.659924 True 5.046809
4 0.0 0.366969 -0.364818 0.630643 -0.330043 0.572139 -0.433893 True 11.004590
输出 gml_graph,内容一致只是表达形式不同,查看一下构建的,代码如下:
print(data["dot_graph"])
结果如下:
digraph {v0->y;W0-> v0; W1-> v0; W2-> v0; W3-> v0; W4-> v0;Z0-> v0; Z1-> v0;W0-> y; W1-> y; W2-> y; W3-> y; W4-> y;}
model=CausalModel(data = df,treatment=data["treatment_name"],outcome=data["outcome_name"],graph=data["gml_graph"])
model.view_model() # 对构建的因果图可视化
from IPython.display import Image, display
display(Image(filename="causal_model.png"))
执行代码后,结果如下:
上图包含了数据中给定的先验知识,我们可以利用这张图来识别因果效应(从因果估计量到概率表达式)并进行估计。
可以脱离于数据,仅根据图进行识别,其给出的结果是一个用于计算的「表达式」。具体的代码如下:
identified_estimand = model.identify_effect()
print(identified_estimand)
输出结果如下:
Estimand type: EstimandType.NONPARAMETRIC_ATE### Estimand : 1
Estimand name: backdoor
Estimand expression:d
─────(E[y|W1,W0,W4,W2,W3])
d[v₀]
Estimand assumption 1, Unconfoundedness: If U→{v0} and U→y then P(y|v0,W1,W0,W4,W2,W3,U) = P(y|v0,W1,W0,W4,W2,W3)### Estimand : 2
Estimand name: iv
Estimand expression:⎡ -1⎤⎢ d ⎛ d ⎞ ⎥
E⎢─────────(y)⋅⎜─────────([v₀])⎟ ⎥⎣d[Z₁ Z₀] ⎝d[Z₁ Z₀] ⎠ ⎦
Estimand assumption 1, As-if-random: If U→→y then ¬(U →→{Z1,Z0})
Estimand assumption 2, Exclusion: If we remove {Z1,Z0}→{v0}, then ¬({Z1,Z0}→y)### Estimand : 3
Estimand name: frontdoor
No such variable(s) found!
estimate = model.estimate_effect(identified_estimand,method_name="backdoor.propensity_score_stratification")
print(estimate)
输出结果如下:
*** Causal Estimate ***## Identified estimand
Estimand type: EstimandType.NONPARAMETRIC_ATE### Estimand : 1
Estimand name: backdoor
Estimand expression:d
─────(E[y|W1,W0,W4,W2,W3])
d[v₀]
Estimand assumption 1, Unconfoundedness: If U→{v0} and U→y then P(y|v0,W1,W0,W4,W2,W3,U) = P(y|v0,W1,W0,W4,W2,W3)## Realized estimand
b: y~v0+W1+W0+W4+W2+W3
Target units: ate## Estimate
Mean value: 9.942351552191596
可以通过 target_units 参数来选择因果效应分析的群体,如 ate(群体层面)、att(干预组)、ate(对照组)。也可以指定结果修改变量来分析不同变量对结果的影响。
反驳阶段参考其他博客内容进行实验,采用多种方式验证。
添加一个随机的混杂因子变量
res_random=model.refute_estimate(identified_estimand, estimate, method_name="random_common_cause")
print(res_random)
输出结果如下:
Refute: Add a random common cause
Estimated effect:9.942351552191596
New effect:9.942351552191594
p value:2.0
添加一个未观测的混杂因子变量
res_unobserved=model.refute_estimate(identified_estimand, estimate, method_name="add_unobserved_common_cause", confounders_effect_on_treatment="binary_flip", confounders_effect_on_outcome="linear", effect_strength_on_treatment=0.01, effect_strength_on_outcome=0.02)
print(res_unobserved)
输出结果如下:
Refute: Add an Unobserved Common Cause
Estimated effect:9.942351552191596
New effect:9.451291104262298
用随机变量代替干预
res_placebo=model.refute_estimate(identified_estimand, estimate, method_name="placebo_treatment_refuter", placebo_type="permute")
print(res_placebo)
输出结果如下:
Refute: Use a Placebo Treatment
Estimated effect:9.942351552191596
New effect:-0.014386044006960131
p value:0.8999999999999999
移除数据的一个随机子集
res_subset=model.refute_estimate(identified_estimand, estimate, method_name="data_subset_refuter", subset_fraction=0.9)
print(res_subset)
输出结果如下:
Refute: Use a subset of data
Estimated effect:9.942351552191596
New effect:9.934071804782558
p value:0.8600000000000001
我们可以通过 random_seed 参数来保证结果的可重现性。
跟着官网的实验跑一趟,对该分析工具认识加深,特别是工具安装和使用,分析的结果可呈现。对该工具的认识也加深不少。
参考链接: