- 机器学习
- 数据分析
【免费下载链接】dowhy
DoWhy is a Python library for causal inference that supports explicit modeling and testing of causal assumptions. DoWhy is based on a unified language for causal inference, combining causal graphical models and potential outcomes frameworks.
本文围绕 DoWhy 的dowhy.graph_learners包展开,基于该包的 API 文档页 dowhy.graph_learners.rst 与包内全部源码,讲清三件事:包中CDT、GES、LINGAM三个图发现子模块各自的实现与输入输出约定;get_discovery_class_object/get_library_class_object这套工厂式动态导入机制如何把字符串方法名解析为可执行的发现器;以及CausalModel.learn_graph()的完整调用链、结果如何从邻接矩阵转换为 DoWhy 可消费的 DOT 图,和该模块当前的弃用状态与替代路线。读完本文,你能够准确使用(或判断是否继续使用该)图发现接口,并知道如何在 DoWhy 的识别与估计流程中接入外部发现的因果图。
1. 包的定位:一个薄封装层,而非算法实现
dowhy.graph_learners的文档页由 Sphinx 的automodule指令生成,覆盖四个目标:dowhy.graph_learners.cdt、dowhy.graph_learners.ges、dowhy.graph_learners.lingam三个子模块以及包本体(即 dowhy/graph_learners/init.py 中的函数)。
需要首先明确该包在 DoWhy 架构中的位置:DoWhy 本身不实现因果图发现算法,而是为外部发现结果提供一个标准化的接入层。官方用户指南 learning_causal_structure.rst 说明:图发现算法(如基于独立性检验、基于评分搜索的算法)只能给出与数据一致的候选图,完全非参数设定下从观测数据还原真实结构是可证明不可能的,因此用户应将学习到的图结合领域知识检视、修改后交给 DoWhy,唯一约束是算法需输出有向无环图(DAG)。
更重要的是,该包目前处于弃用状态。从源码结构看,调用入口CausalModel.learn_graph()在 dowhy/causal_model.py 中会先发出DeprecationWarning:
CausalModel.learn_graph() and the dowhy.graph_learners module are deprecated and will be removed in a future major release.
弃用信息明确建议直接使用causal-learn或dodiscover等库完成图发现,再把结果图通过CausalModel的graph参数传入。第 7 节会给出具体的迁移写法。
2. 基类 GraphLearner:约定统一的状态与接口
三个子模块共同继承的基类定义在 dowhy/graph_learner.py,仅有两个成员,却约定了整个包的契约:
class GraphLearner: """Base class for causal discovery methods.""" def __init__(self, data, library_class, *args, **kwargs): self._data = data self._labels = list(self._data.columns) self._adjacency_matrix = None self._graph_dot = None def learn_graph(self): """Discover causal graph and the graph in DOT format.""" raise NotImplementedError可以归纳出两条核心约定:
- 输入约定:构造参数
data是 pandas DataFrame,基类自动以list(self._data.columns)初始化_labels,即列名即节点名——后续邻接矩阵到图节点的映射依赖于此; - 输出约定:子类实现
learn_graph(labels=None),把底层库的结果收敛为两个内部状态:self._adjacency_matrix(numpy 邻接矩阵)和self._graph_dot(DoWhy 认可的 DOT 字符串),并返回 DOT 字符串。
labels参数是可选的节点名覆盖入口:若传入则用其替代 DataFrame 列名,若不传则沿用基类初始化的_labels。三个子模块的实现都严格遵循这一模式。
3. 三个子模块的实现对比
3.1 cdt 子模块:CDT 类
dowhy/graph_learners/cdt.py 封装 Causal Discovery Toolbox(CDT)。其__init__通过工厂函数get_library_class_object(full_method_name)按“模块路径.类名”的形式实例化底层方法,learn_graph的调用链是:
def learn_graph(self, labels=None): graph = self._method.predict(self._data) # CDT 算法返回 NetworkX 图 self._adjacency_matrix = nx.to_numpy_array(graph) # 转邻接矩阵 self._adjacency_matrix = np.asarray(self._adjacency_matrix) if labels is not None: self._labels = labels self._graph_dot = adjacency_matrix_to_graph(self._adjacency_matrix, self._labels) self._graph_dot = str_to_dot(self._graph_dot.source) return self._graph_dot特点:CDT 的predict直接返回 NetworkX 图对象,因此 CDT 是三个封装中唯一显式执行“NetworkX 图 → 邻接矩阵”转换(nx.to_numpy_array)的实现,其余两个库本来就输出矩阵。
3.2 ges 子模块:GES 类
dowhy/graph_learners/ges.py 封装 GES(Greedy Equivalence Search)库。它与另两个封装的差异在于:__init__不实例化类,而是import_module(full_method_name)直接导入模块本身,即self._method是模块对象,调用的是模块级函数fit_bic:
self._adjacency_matrix, self.score = self._method.fit_bic(self._data.to_numpy())注意两点实现细节:输入是self._data.to_numpy()(纯 numpy 数组,标签由_labels单独维护);fit_bic同时返回邻接矩阵和 BIC 评分,评分被保存在self.score,可供调用方检查拟合优劣。
3.3 lingam 子模块:LINGAM 类
dowhy/graph_learners/lingam.py 封装 lingam 库(LiNGAM 一族算法,包括 VanillaLiNGAM、DirectLiNGAM 等),走与 CDT 相同的工厂路径:get_library_class_object("lingam.VanillaLiNGAM")会按最后一个点号切分出模块lingam与类VanillaLiNGAM并实例化。learn_graph的调用链是:
self._method.fit(self._data) self._adjacency_matrix = self._method.adjacency_matrix_即调用 sklearn 风格的fit后读取adjacency_matrix_属性。与另两者相比,LINGAM 类没有对矩阵做np.asarray归一化,直接使用库返回的数组。
三个封装殊途同归:无论底层库返回 NetworkX 图还是 numpy 数组,最终都汇入统一的adjacency_matrix_to_graph+str_to_dot转换管线(见第 5 节)。
4. 包本体的工厂式动态加载机制
文档页的 “Module contents” 一节对应 dowhy/graph_learners/init.py,它提供两个工厂函数,是整个包“用字符串驱动算法选择”的关键。
4.1 get_discovery_class_object:选择 DoWhy 内部的封装类
def get_discovery_class_object(method_name, *args, **kwargs): """Import class from graph_learners.""" try: module_name = method_name class_name = module_name.upper() discovery_module = import_module("." + module_name, package="dowhy.graph_learners") discovery_class = getattr(discovery_module, class_name) if not issubclass(discovery_class, GraphLearner): raise TypeError("Causal discovery class must inherit from GraphLearner") except (AttributeError, ImportError): raise ImportError("{} is not an existing causal discovery method.".format(method_name)) return discovery_class其约定是:传入库名(如"cdt"、"ges"、"lingam"),函数动态导入dowhy.graph_learners.<库名>子模块,取出同名大写的类(CDT/GES/LINGAM),并强制校验其必须是GraphLearner子类;任何一环缺失都收敛为一条可读的ImportError。这个“模块名 = 类名小写”的映射约定解释了为什么三个封装类的类名被设计为各自库名的大写形式。
4.2 get_library_class_object:定位第三方库中的具体算法类
def get_library_class_object(module_method_name, *args, **kwargs): """Import library for causal inference.""" try: (module_name, _, class_name) = module_method_name.rpartition(".") discovery_module = import_module(module_name) discovery_class = getattr(discovery_module, class_name) except (AttributeError, AssertionError, ImportError): raise ImportError( "Error loading {}.{}. Double-check the method name and ensure that all " "library dependencies are installed.".format(module_name, class_name) ) return discovery_class与上一函数不同,它处理的是外部库的完整路径,用rpartition(".")从最后一个点号切分,例如"cdt.causality.graph.LiNGAM"切出模块cdt.causality.graph与类LiNGAM。错误提示中特别提到“确保依赖已安装”——这也解释了为什么使用这些封装前需要单独安装 CDT、GES 或 lingam,它们不是 DoWhy 的硬依赖。两个工厂函数共同构成两级路由:库名 → DoWhy 封装类 → 第三方算法类。
5. 结果转换管线:邻接矩阵如何变成 DoWhy 的 DOT 图
三个子模块的learn_graph都复用 dowhy/utils/graph_operations.py 中的两个工具函数,这是理解输出格式的关键。
adjacency_matrix_to_graph(adjacency_matrix, labels=None):
- 校验矩阵必须是二维(否则抛
ValueError),并把np.matrix归一化为 ndarray; - 边阈值:只有
np.abs(adjacency_matrix) > 0.01的项才被视为边。这意味着底层算法输出的弱因果强度(|w| ≤ 0.01)会被静默丢弃,图的结构由该阈值隐含决定; - 节点标签缺省时按
x0, x1, x2, ...命名,因此实践中应确保_labels(DataFrame 列名)被正确传递; - 输出为
graphviz.Digraph对象,DOT 文本在其.source属性中。
str_to_dot(string)则做格式归一化:把 graphviz 输出的多行文本压成以分号分隔的单行、去除制表符,并剥掉外层digraph { ... }包裹(graph[:9] + graph[10:-2] + graph[-1]),产出 DoWhy 的CausalGraph可直接解析的 DOT 片段。这一“压缩成单行分号分隔”的格式正是 DoWhy 图字符串的内部表示。
6. 完整调用链:CausalModel.learn_graph() 如何使用本包
用户侧入口是CausalModel.learn_graph,定义在 dowhy/causal_model.py,签名为learn_graph(self, method_name="cdt.causality.graph.LiNGAM", *args, **kwargs)。执行流程为:
- 发出
DeprecationWarning(见第 1 节); method_name.split(".", maxsplit=1)取第一段作为库名,交给get_discovery_class_object得到封装类;- 以
causal_discovery_class(self._data, method_name, *args, **kwargs)构造实例,并调用model.learn_graph()得到 DOT 字符串; - 用该 DOT 字符串调用
self.init_graph(graph=graph, identify_vars=False)初始化CausalGraph并返回。
因此一次典型调用是:
import dowhy import pandas as pd data = pd.DataFrame({ "x": [0.1, 0.4, 0.5, 0.7], "y": [0.3, 0.6, 0.8, 0.9], "z": [0.2, 0.5, 0.7, 1.0], }) model = dowhy.CausalModel(data=data, treatment="y", outcome="z") # 默认即 cdt.causality.graph.LiNGAM;也可换成 "lingam.VanillaLiNGAM" 等 graph = model.learn_graph() # 返回 CausalGraph 实例注意*args, **kwargs会透传给第三方算法类的构造器,因此算法超参数(如 LiNGAM 的实现变体参数)可以直接从这里传入,这与各封装类构造函数里library_class(*args, **kwargs)的透传方式对应。
测试侧的证据在 tests/test_causal_model.py 的test_learn_graph_initializes_the_graph:它注入一个仅继承GraphLearner的 stub 学习器(返回一个三节点 DiGraph 的 DOT 字符串),调用model.learn_graph("stub")后断言graph is model._graph且节点集合为{"W0", "v0", "y"}。该测试从两个方向印证了第 4、5 节的机制:动态导入按“模块名.大写类名”匹配 stub 模块,且learn_graph的返回值确实被回灌进CausalModel._graph,后续identify_effect、estimate_effect等步骤都在这张图上运行。
官方示例笔记本 dowhy_causal_discovery_example.ipynb 演示了基于 CDT 的完整发现流程,可作为本文调用链的端到端参照。
7. 迁移指引:弃用状态下的替代路线
由于learn_graph()与dowhy.graph_learners已被标记为弃用且“将在未来主版本移除”,在新代码中建议采用官方指南推荐的外部发现 +graph参数注入的路线:
from cdt.causality.graph import LiNGAM causal_graph = LiNGAM().predict(dataset) # 直接得到 NetworkX 图 # 将外部发现结果转为 DoWhy 可解析的图字符串后传入 model = dowhy.CausalModel( data=dataset, treatment="y", outcome="z", graph="digraph {y -> z}", # 由 causal_graph 转换/检视后得到的 DOT )这条路线的好处是发现算法完全由 causal-learn / dodiscover / CDT 等专用库承担(功能面与算法迭代都优于本包内置的三个封装),而 DoWhy 只保留它擅长的图识别、效应估计与可反驳检验环节。如果你仍在维护基于learn_graph的旧代码,可以:保持现状并容忍DeprecationWarning、用warnings.filterwarnings显式过滤、或按上文改为外部发现 +graph注入;在迁移期间,本包CDT/GES/LINGAM三类的行为(弱边 0.01 阈值、labels 覆盖、DOT 单行化输出)仍然如源码所示,可作为排查旧代码行为差异的参照。
8. 小结
dowhy.graph_learners是一个职责单一、结构清晰的接入层:
- 基类契约(dowhy/graph_learner.py):DataFrame 输入、
learn_graph(labels=None)输出 DOT; - 三个子模块(cdt.py、ges.py、lingam.py):分别适配 CDT 的 NetworkX 图输出、GES 的模块级
fit_bic函数与 lingam 的 sklearn 风格fit+adjacency_matrix_属性,统一收敛到adjacency_matrix_to_graph/str_to_dot管线; - 双级工厂函数(init.py):
get_discovery_class_object按库名路由到内部封装,get_library_class_object按“模块路径.类名”定位第三方算法; - 入口与状态:
CausalModel.learn_graph()(dowhy/causal_model.py)完成字符串解析、实例化、图学习并经init_graph(identify_vars=False)落地为CausalGraph,测试用例 tests/test_causal_model.py 验证了回灌行为。
在DeprecationWarning已发出的背景下,理解该包的价值在于:读懂旧代码中图发现的真实行为(阈值、标签、DOT 格式),并据此平滑迁移到外部发现库 +graph参数注入的新范式。
- 机器学习
- 数据分析
【免费下载链接】dowhy
DoWhy is a Python library for causal inference that supports explicit modeling and testing of causal assumptions. DoWhy is based on a unified language for causal inference, combining causal graphical models and potential outcomes frameworks.
相关推荐
探索PowerToys动态模块加载机制:打造高效Windows生产力工具
探索PowerToys动态模块加载机制:打造高效Windows生产力工具 PowerToys作为一款强大的Windows系统实用工具集,其核心优势在于能够动态加
桌面应用开发工具深度解析 DoWhy 的 dowhy.gcm 包:图形因果模型 (GCM) 完整 API 与实战指南
深度解析 DoWhy 的 dowhy.gcm 包:图形因果模型 GCM 完整 API 与实战指南 本文以 DoWhy 官方 API 参考文档 dowhy.gcm
机器学习数据分析drawio-desktop Linux 构建完整指南:如何生成 AppImage 与 deb 安装包
drawio desktop Linux 构建完整指南:如何生成 AppImage 与 deb 安装包 drawio desktop 是 draw.io 的官方
桌面应用图形学
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考