简介: PGMPY的全称是Probability Graph Model in PYthon,顾名思义是基于python开发的概率图模型工具包,包含贝叶斯网模型、动态贝叶斯网模型、结构方程模型、NoisyOr模型、马尔可夫网模型、联合树模型、团树模型、因子图模型、马尔可夫链等。利用该工具包,可进行贝叶斯网推理、参数学习、结构学习,和因果推理等。
PGMPY支持的Python最低版本为3.7。在pypi、anaconda以及国内各镜像平台都有部署。可通过如下命令安装:
pip install pgmpy
或
conda install -c ankurnkan pgmpy
也可通过github安装最新开发版:
pip install git+https://github.com/pgmpy/pgmpy.git@dev
PGMPY采用MIT开源协议,可自由使用、复制、修改,限制较少。
class pgmpy.base.DAG(ebunch=None, latents={})
该类是所有有向图模型的基类。参数ebunch是边集合,见下方示例。
首先,我们可以构建空图:
>>> from pgmpy.base import DAG
>>> G = DAG()
可通过加节点或加边的方式扩充图:
>>> G.add_node(node='a') # 添加一个节点
>>> G.add_nodes_from(nodes=['a','b']) # 添加一组节点
>>> G.add_edge(u='a',v='b') # 添加一条边
>>> G.add_edges_from(ebunch=[('a','b'),('b','c')]) # 添加一组边
>>> G.nodes()
NodeView(('a', 'b', 'c'))
>>> G.edges()
OutEdgeView([('a', 'b'), ('b', 'c')])
若在添加边时,边的节点不在图中,则节点会被自动添加。
重复添加边和节点不会报错。
针对G,还有一些符合python语法的快捷操作:
>>> 'a' in G # 检查节点a是否在G中
True
>>> len(G) # G中节点数
3
在贝叶斯网专题3中介绍过迹的概念,表示一条通路。有效迹(active trail)是指在给定某些观察变量后,仍有效的通路。比如x->z->y通路,给定z后经过x的迹只有x,若z未知,则经过x的迹为x->z->y。分连和汇连情况参考前面给出的专题3.
PGMPY通过函数activate_trail_nodes来判断有效迹。
active_trail_nodes(variables,ovserved=None,include_latents=False)
输入参数:
- variables(str or array): 经过这些节点去寻找迹
- observed(list of nodes): 观察到的节点
- include_latents(boolean): 是否包含隐变量
例:
>>> from pgmpy.base import DAG
>>> student = DAG()
>>> student.add_nodes_from(['diff', 'intel', 'grades'])
>>> student.add_edges_from([('diff', 'grades'), ('intel', 'grades')])
>>> student.active_trail_nodes('diff')
{'diff': {'diff', 'grades'}}
>>> student.active_trail_nodes(['diff', 'intel'], observed='grades')
{'diff': {'diff', 'intel'}, 'intel': {'diff', 'intel'}}
因果推理中的do算子,do(X)即删除所有指向X节点的边,并将X设定为给定的值x。
do(nodes, inplace=False)
输入参数:
- nodes(list, array-like): 应用do算子的节点名列表
- inplace(boolean (default: False)): 若为True,则在原图上就地修剪,否则返回一个新图
返回值:
- (pgmpy.base.DAG): 修改后的DAG图
例:
>>> from pgmpy.base import DAG
>>> graph = DAG()
>>> graph.add_edges_from([(‘X’, ‘A’),(‘A’, ‘Y’),(‘A’, ‘B’)])
>>> graph_do_A = graph.do(‘A’)
>>> graph_do_A.edges
OutEdgeView([(‘A’, ‘B’), (‘A’, ‘Y’)])
将DAG图转化为无向图时,为了保持变量之间的相关关系,对于汇连节点,应增加父节点之间的关联边,转换后的无向图称为端正图(moral graph)。
get_immoralities()
返回值:
- (set): DAG图转换为端正图所需要添加的边集
例:
>>> from pgmpy.base import DAG
>>> student = DAG()
>>> student.add_edges_from([('diff', 'grade'), ('intel', 'grade'),
... ('intel', 'SAT'), ('grade', 'letter')])
>>> student.get_immoralities()
{('diff', 'intel')}
通过检查d-分隔计算变量之间的独立性。
get_independencies(latex=False, include_latents=False)
输入参数:
- latex(boolean): 若为True,则输出用latex表达的独立性断言
- include_latents(boolean): 若为True,则输出的独立性断言中包含隐变量,否则不包括隐变量
例:
>>> from pgmpy.base import DAG
>>> chain = DAG([('X', 'Y'), ('Y', 'Z')])
>>> chain.get_independencies()
(X ⟂ Z | Y)
(Z ⟂ X | Y)
get_leaves()
返回值:
- (list): 图的叶节点列表
例:
>>> from pgmpy.base import DAG
>>> graph = DAG([('A', 'B'), ('B', 'C'), ('B', 'D')])
>>> graph.get_leaves()
['C', 'D']
get_roots()
返回值:
- (list): 图的根节点列表
例:
>>> from pgmpy.base import DAG
>>> graph = DAG([('A', 'B'), ('B', 'C'), ('B', 'D'), ('E', 'B')])
>>> graph.get_roots()
['A', 'E']
get_parents(node)
输入参数:
- node(string, int or any hashable python object): 目标节点
返回值:
- (list): 目标节点的父节点列表
例:
>>> from pgmpy.base import DAG
>>> G = DAG(ebunch=[('diff', 'grade'), ('intel', 'grade')])
>>> G.get_parents(node='grade')
['diff', 'intel']
在贝叶斯网专题3中介绍过马尔可夫边界的概念:一个节点的马尔可夫边界包括其父节点、子节点,以及子节点的父节点。给定某节点的马尔可夫边界,该节点与贝叶斯网中其它节点独立。
get_markov_blanket(node)
输入参数:
- node(string, int or any hashable python object): 待计算马尔可夫边界的节点
返回值:
- (list): 马尔可夫边界中的节点列表
例:
>>> from pgmpy.base import DAG
>>> from pgmpy.factors.discrete import TabularCPD
>>> G = DAG([('x', 'y'), ('z', 'y'), ('y', 'w'), ('y', 'v'), ('u', 'w'),
('s', 'v'), ('w', 't'), ('w', 'm'), ('v', 'n'), ('v', 'q')])
>>> G.get_markov_blanket('y')
['s', 'w', 'x', 'u', 'z', 'v']
static get_random(n_nodes=5, edge_prob=0.5, latents=False)
输入参数:
- n_nodes(int): 生成图的节点数
- edge_prob(float): 任意两节点之间存在边的概率
- latents(bool): 是否包含隐变量
返回值:
- (pgmpy.base.DAG): 随机生成的DAG图
例:
>>> from pgmpy.base import DAG
>>> random_dag = DAG.get_random(n_nodes=10, edge_prob=0.3)
>>> random_dag.nodes()
NodeView((0, 1, 2, 3, 4, 5, 6, 7, 8, 9))
>>> random_dag.edges()
OutEdgeView([(0, 6), (1, 6), (1, 7), (7, 9), (2, 5), (2, 7), (2, 8), (5, 9), (3, 7)])
判断两个节点之间是否存在有效迹。
is_dconnected(start, end, observed=None)
输入参数:
- start(int, str, any hashable python object): 有效迹起点
- end(int,str, any hashable python object): 有效迹终点
- observed(list): 已观测节点列表,会根据已观测节点计算有效迹
返回值:
- (bool): 起点和终点间是否存在有效迹
例:
>>> from pgmpy.base import DAG
>>> student = DAG()
>>> student.add_nodes_from(['diff', 'intel', 'grades', 'letter', 'sat'])
>>> student.add_edges_from([('diff', 'grades'), ('intel', 'grades'), ('grades', 'letter'),
... ('intel', 'sat')])
>>> student.is_dconnected('diff', 'intel')
False
>>> student.is_dconnected('grades', 'sat')
True
两个DAG图端正化后,若具有相同拓扑结构,则称为独立性等价(I-equivalent)。
is_iequivalent(model)
输入参数:
- model(pgmpy.base.DAG): 判断是否独立性等价的DAG对象
返回值:
- (bool): 是否独立性等价
例:
>>> from pgmpy.base import DAG
>>> G = DAG()
>>> G.add_edges_from([('V', 'W'), ('W', 'X'),
... ('X', 'Y'), ('Z', 'Y')])
>>> G1 = DAG()
>>> G1.add_edges_from([('W', 'V'), ('X', 'W'),
... ('X', 'Y'), ('Z', 'Y')])
>>> G.is_iequivalent(G1)
True
在贝叶斯网专题3中介绍过局部马尔可夫性的概念:给定某节点的父节点集,则该节点条件独立于它的所有非后代节点。
local_independencies(variables)
输入变量:
- variables(str or array like): 寻找局部独立的目标节点
返回值:
- (Independencies对象): 独立性断言
例:
>>> from pgmpy.base import DAG
>>> student = DAG()
>>> student.add_edges_from([('diff', 'grade'), ('intel', 'grade'),
>>> ('grade', 'letter'), ('intel', 'SAT')])
>>> ind = student.local_independencies('grade')
>>> ind
(grade ⟂ SAT | diff, intel)
寻找最小d分隔节点集的算法可参考文献:Algorithm 4, Page 10: Tian, Jin, Azaria Paz, and Judea Pearl. Finding minimal d-separators. Computer Science Department, University of California, 1998.
minimal_dseparator(start, end)
输入变量:
- start(node): 第一个节点
- end(node): 第二个节点
返回值:
- (list): 将第一个节点和第二个节点d分隔的最小节点集
例:
>>> dag = DAG([('A', 'B'), ('B', 'C')])
>>> dag.minimal_dseparator(start='A', end='C')
{'B'}
将DAG图端正化为无向图,其中所有的汇连结构x->z<-y都增加x与y之间的无向边。
moralize()
返回值:
- (pgmpy.base.UndirectedGraph): 端正化后的无向图
例:
>>> from pgmpy.base import DAG
>>> G = DAG(ebunch=[('diff', 'grade'), ('intel', 'grade')])
>>> moral_graph = G.moralize()
>>> moral_graph.edges()
EdgeView([('intel', 'grade'), ('intel', 'diff'), ('grade', 'diff')])
pgmpy支持将DAG图导出为daft.PGM对象(https://docs.daft-pgm.org/en/latest/),该对象可调用show()函数绘制出高质量的图形。
to_daft(node_pos=‘circular’, latex=True, pgm_params={}, edge_params={}, node_params={})
输入参数:
- node_pos(str or dict): 若是字符串,则只能是如下几种: circular, kamada_kawai, planar, random, shell, sprint, spectral, spiral. 其含义可参考networkx绘图工具的文档https://networkx.org/documentation/stable//reference/drawing.html#module-networkx.drawing.layout。若为字典,则可枚举各节点的位置,格式为{node1:(x轴坐标, y轴坐标), …}
- latex(bool): 是否使用latex渲染节点名
- pgm_params(dict): 传递给daft.PGM对象初始化函数的参数,格式为{param_name: param_value, …}
- edge_params(dict): 传递给daft.add_edge方法的参数,格式为{(u1,v1):{param_name: param_value, …}, …}
- node_params(dict): 传递给daft.add_node方法的参数,格式为{node1:{param_name: param_value, …}, …}
返回值:
- (daft.PGM object): 可用于绘制DAG图的Daft对象
例:
>>> from pgmpy.base import DAG
>>> dag = DAG([('a', 'b'), ('b', 'c'), ('d', 'c')])
>>> dag.to_daft(node_pos={'a': (0, 0), 'b': (1, 0), 'c': (2, 0), 'd': (1, 1)})
>>> dag.to_daft(node_pos="circular")
>>> dag.to_daft(node_pos="circular", pgm_params={'observed_style': 'inner'})
>>> dag.to_daft(node_pos="circular",
... edge_params={('a', 'b'): {'label': 2}},
... node_params={'a': {'shape': 'rectangle'}})
若DAG图中存在两个节点有双向边,则将这两条有向边合并为一条无向边。处理以后得到的图则为部分有向无回路图(PDAG)。
to_pdag()
返回值:
- (pgmpy.base.PDAG): 返回对应的PDGA图
class pgmpy.base.PDAG(directed_ebunch=[], undirected_ebunch=[], latents=[])
该类是部分有向无回路图模型的基类。参数ebunch是边集合。PDAG可同时包含有向边和无向边,其中无向边表示双向边,如边X-Y表示X->Y和X<-Y.
copy()
返回值:
- (pgmpy.base.PDAG): 返回一个副本
to_dag(required_edges=[])
输入参数:
- required_edges(list, array-like of 2-tuples): 需要在DAG图中包含的边集
返回值:
- (pgmpy.base.DAG): 转换得到的DAG图