causal_networkx.ADMG#

class causal_networkx.ADMG(incoming_graph_data=None, incoming_latent_data=None, incoming_selection_bias=None, **attr)[source]#

Acyclic directed mixed graph (ADMG).

A causal graph with two different edge types: bidirected and traditional directed edges. Directed edges constitute causal relations as a causal DAG did, while bidirected edges constitute the presence of a latent confounder.

Parameters:

incoming_graph_data : input graph (optional, default: None)

Data to initialize directed edge graph. The edges in this graph represent directed edges between observed variables, which are represented using a networkx.DiGraph, so accepts any arguments from the networkx.DiGraph class. There must be no cycles in this graph structure.

incoming_latent_data : input graph (optional, default: None)

Data to initialize bidirected edge graph. The edges in this graph represent bidirected edges, which are represented using a networkx.Graph, so accepts any arguments from the networkx.Graph class.

incoming_selection_bias : input graph (optional, default: None)

Data to initialize selection bias graph. Currently, not used or implemented.

attr : keyword arguments, optional (default= no attributes)

Attributes to add to graph as key=value pairs.

Notes

The data structure underneath the hood is stored in two networkx graphs: networkx.Graph and networkx.DiGraph to represent the latent unobserved confounders and observed variables. These data structures should never be modified directly, but should use the ADMG class methods.

  • Bidirected edges (<->, indicating latent confounder) = networkx.Graph

  • Normal directed edges (<-, ->, indicating causal relationship) = networkx.DiGraph

Nodes are defined as any nodes defined in the underlying DiGraph and Graph. I.e. Any node connected with either a bidirected, or normal directed edge. Adding edges and bidirected edges are performed separately in different functions, compared to networkx.

Subclassing: All causal graphs are a mixture of graphs that represent the different types of edges possible. For example, a causal graph consists of two types of edges, directed, and bidirected. Each type of edge has the following operations:

  • has_<edge_type>_edge: Check if graph has this specific type of edge.

  • add_<edge_type>_edge: Add a specific edge type to the graph.

  • remove_<edge_type>_edge: Remove a specific edge type to the graph.

All nodes are “stored” in self.dag, which allows for isolated nodes that only have say bidirected edges pointing to it.

Attributes:

bidirected_edges

Directed edges.

c_components

Generate confounded components of the graph.

edges

Directed edges.

name

Name as a string identifier of the graph.

nodes

Return the nodes within the DAG.

Methods

add_bidirected_edge(u_of_edge, v_of_edge, **attr)

Add a bidirected edge between u and v.

add_bidirected_edges_from(ebunch, **attr)

Add bidirected edges in a bunch.

add_chain(node_chain)

Add a causal chain.

add_edge(u_of_edge, v_of_edge, **attr)

Add an edge between u and v.

add_edges_from(ebunch, **attr)

Add directed edges.

add_node(node_for_adding, **attr)

Add node to causal graph.

add_nodes_from(nodes_for_adding, **attr)

Add nodes to causal graph.

adjacencies(u)

Get all adjacent nodes to u.

all_edges()

Get dictionary of all the edges by edge type.

ancestors(source)

Ancestors of 'source' node with directed path.

children(n)

Return an iterator over children of node n.

clear()

Remove all nodes and edges in graphs.

clear_edges()

Remove all edges from causal graph without removing nodes.

compute_full_graph([to_networkx])

Compute the full graph.

copy()

Return a copy of the causal graph.

degree(n)

Compute the degree of the DiGraph.

descendants(source)

Descendants of 'source' node with directed path.

do(nodes)

Apply a do-intervention on nodes to causal graph.

draw()

Draws causal graph.

dummy_sample()

Sample an empty dataframe with columns as the nodes.

edge_subgraph(edges)

Create a causal subgraph of just certain edges.

get_edge_data(u, v[, default])

Get edge data from underlying DiGraph.

has_adjacency(u, v)

Check if there is any edge between u and v.

has_bidirected_edge(u, v)

Check if graph has bidirected edge (u, v).

has_edge(u, v)

Check if graph has edge (u, v).

has_node(n)

Check if graph has node 'n'.

in_degree()

In degree view of DAG.

is_acyclic()

Check if graph is acyclic.

is_node_common_cause(node[, exclude_nodes])

Check if a node is a common cause within the graph.

is_unshielded_collider(a, b, c)

Check if unshielded collider.

markov_blanket_of(node)

Compute the markov blanket of a node.

neighbors(node)

Neighbors view of DAG.

number_of_bidirected_edges([u, v])

Return number of bidirected edges in graph.

number_of_edges()

Return number of edges in graph.

number_of_nodes()

Return number of nodes in graph.

order()

Return the order of the DiGraph.

out_degree()

Out degree view of DAG.

parents(n)

Return an iterator over parents of node n.

predecessors(u)

Return predecessors of node u.

relabel_nodes(mapping[, copy])

Relabel the nodes of the graph G according to a given mapping.

remove_bidirected_edge(u_of_edge, v_of_edge)

Remove a bidirected edge between u and v.

remove_edge(u, v)

Remove directed edge.

remove_edges_from(ebunch)

Remove directed edges.

remove_node(n)

Remove node in causal graphs.

remove_nodes_from(ebunch)

Remove nodes from causal graph.

sample([n])

Sample from a graph.

set_nodes_as_latent_confounders(nodes)

Set nodes as latent unobserved confounders.

size([weight])

Return the total number of edges possibly with weights.

soft_do(nodes[, dependencies])

Apply a soft-intervention on node to causal graph.

spouses(node)

Get other parents of the children of a node (spouses).

subgraph(nodes)

Create a causal subgraph of just certain nodes.

successors(u)

Return successors of node u.

to_adjacency_graph()

Compute an adjacency undirected graph.

to_dot_graph([to_dagitty])

Convert to 'dot' graph representation as a string.

to_networkx()

Converts causal graphs to networkx.

to_numpy()

Convert to a matrix representation.

tomag()

Convert corresponding causal DAG to a MAG.

is_directed

is_multigraph

save

__contains__(n)#

Return True if n is a node, False otherwise. Use: ‘n in G’.

Examples

>>> G = nx.path_graph(4)  # or DiGraph, MultiGraph, MultiDiGraph, etc
>>> 1 in G
True
__hash__()#

Return hash(self).

Return type:

int

add_bidirected_edge(u_of_edge, v_of_edge, **attr)[source]#

Add a bidirected edge between u and v.

The nodes u and v will be automatically added if they are not already in the graph. Moreover, they will be added to the underlying DiGraph, which stores all regular directed edges.

Parameters:

u_of_edge, v_of_edge : nodes

Nodes can be, for example, strings or numbers. Nodes must be hashable (and not None) Python objects.

attr : keyword arguments, optional

Edge data (or labels or objects) can be assigned using keyword arguments.

See also

networkx.Graph.add_edges_from

add a collection of edges

networkx.Graph.add_edge

add an edge

Notes

Return type:

None

add_bidirected_edges_from(ebunch, **attr)[source]#

Add bidirected edges in a bunch.

add_chain(node_chain)#

Add a causal chain.

add_edge(u_of_edge, v_of_edge, **attr)#

Add an edge between u and v.

The nodes u and v will be automatically added if they are not already in the graph.

Edge attributes can be specified with keywords or by directly accessing the edge’s attribute dictionary. See examples below.

Parameters:

u_of_edge, v_of_edge : nodes

Nodes can be, for example, strings or numbers. Nodes must be hashable (and not None) Python objects.

attr : keyword arguments, optional

Edge data (or labels or objects) can be assigned using keyword arguments.

See also

add_edges_from

add a collection of edges

Notes

Adding an edge that already exists updates the edge data.

Many NetworkX algorithms designed for weighted graphs use an edge attribute (by default weight) to hold a numerical value.

Examples

The following all add the edge e=(1, 2) to graph G:

>>> G = nx.Graph()  # or DiGraph, MultiGraph, MultiDiGraph, etc
>>> e = (1, 2)
>>> G.add_edge(1, 2)  # explicit two-node form
>>> G.add_edge(*e)  # single edge as tuple of two nodes
>>> G.add_edges_from([(1, 2)])  # add edges from iterable container

Associate data to edges using keywords:

>>> G.add_edge(1, 2, weight=3)
>>> G.add_edge(1, 3, weight=7, capacity=15, length=342.7)

For non-string attribute keys, use subscript notation.

>>> G.add_edge(1, 2)
>>> G[1][2].update({0: 5})
>>> G.edges[1, 2].update({0: 5})
add_edges_from(ebunch, **attr)#

Add directed edges.

add_node(node_for_adding, **attr)#

Add node to causal graph.

add_nodes_from(nodes_for_adding, **attr)#

Add nodes to causal graph.

adjacencies(u)#

Get all adjacent nodes to u.

Adjacencies are defined as any type of edge to node ‘u’.

all_edges()#

Get dictionary of all the edges by edge type.

ancestors(source)#

Ancestors of ‘source’ node with directed path.

property bidirected_edges#

Directed edges.

property c_components: List[Set]#

Generate confounded components of the graph.

TODO: Improve runtime since this iterates over a list twice.

Returns:

comp : list of set

The c-components.

:rtype:py:class:List[Set]
children(n)#

Return an iterator over children of node n.

Children of node ‘n’ are nodes with a directed edge from ‘n’ to that node. For example, ‘n’ -> ‘x’, ‘n’ -> ‘y’. Nodes only connected via a bidirected edge are not considered children: ‘n’ <-> ‘y’.

Parameters:

n : node

A node in the causal DAG.

Returns:

children : Iterator

An iterator of the children of node ‘n’.

clear()#

Remove all nodes and edges in graphs.

clear_edges()#

Remove all edges from causal graph without removing nodes.

Clears edges in the DiGraph and the bidirected undirected graph.

compute_full_graph(to_networkx=False)[source]#

Compute the full graph.

Converts all bidirected edges to latent unobserved common causes. That is, if ‘x <-> y’, then it will transform to ‘x <- [z] -> y’ where [z] is “unobserved”.

TODO: add selection edges too

Returns:

full_graph : nx.DiGraph

The full graph.

Notes

The computation of the full graph is optimized by memoization of the hash of the edge list. When the hash does not change, it implies the edge list has not changed.

Thus the conversion will not occur and the full graph will be read from memory.

copy()#

Return a copy of the causal graph.

degree(n)#

Compute the degree of the DiGraph.

descendants(source)#

Descendants of ‘source’ node with directed path.

do(nodes)[source]#

Apply a do-intervention on nodes to causal graph.

Parameters:

nodes : list of nodes | node

Either a single node, or list of nodes.

Returns:

causal_graph : ADMG

The mutilated causal graph.

Raises:

ValueError

_description_

draw()[source]#

Draws causal graph.

For custom parametrizations, use graphviz or networkx drawers directly with the self.dag and self.c_component_graph.

dummy_sample()#

Sample an empty dataframe with columns as the nodes.

Used for oracle testing.

edge_subgraph(edges)[source]#

Create a causal subgraph of just certain edges.

property edges#

Directed edges.

get_edge_data(u, v, default=None)#

Get edge data from underlying DiGraph.

has_adjacency(u, v)#

Check if there is any edge between u and v.

has_bidirected_edge(u, v)[source]#

Check if graph has bidirected edge (u, v).

has_edge(u, v)#

Check if graph has edge (u, v).

has_node(n)#

Check if graph has node ‘n’.

in_degree()#

In degree view of DAG.

is_acyclic()[source]#

Check if graph is acyclic.

is_node_common_cause(node, exclude_nodes=None)#

Check if a node is a common cause within the graph.

Parameters:

node : node

A node in the graph.

exclude_nodes : list, optional

Set of nodes to exclude from consideration, by default None.

Returns:

is_common_cause : bool

Whether or not the node is a common cause or not.

is_unshielded_collider(a, b, c)[source]#

Check if unshielded collider.

markov_blanket_of(node)#

Compute the markov blanket of a node.

When computing the Markov blanket for an ADMG, we can use the definition presented in [1], where the Markov blanket is a subset, S of variables in the graph, where a subset, S' is called a Markov blanket if it satisfies the condition:

\[X \perp S | S'\]
Parameters:

node : node

The node to compute Markov blanket for.

Returns:

markov_blanket : set

A set of parents, children and spouses of the node.

References

Return type:

Set

property name#

Name as a string identifier of the graph.

This graph attribute appears in the attribute dict G.graph keyed by the string “name”. as well as an attribute (technically a property) G.name. This is entirely user controlled.

neighbors(node)#

Neighbors view of DAG.

property nodes#

Return the nodes within the DAG.

Ignores the c-component graph nodes.

number_of_bidirected_edges(u=None, v=None)[source]#

Return number of bidirected edges in graph.

number_of_edges()#

Return number of edges in graph.

number_of_nodes()#

Return number of nodes in graph.

order()#

Return the order of the DiGraph.

out_degree()#

Out degree view of DAG.

parents(n)#

Return an iterator over parents of node n.

Parents of node ‘n’ are nodes with a directed edge from ‘n’ to that node. For example, ‘n’ <- ‘x’, ‘n’ <- ‘y’. Nodes only connected via a bidirected edge are not considered parents: ‘n’ <-> ‘y’.

Parameters:

n : node

A node in the causal DAG.

Returns:

parents : Iterator

An iterator of the parents of node ‘n’.

predecessors(u)#

Return predecessors of node u.

A predecessor is defined as nodes with a directed edge to ‘u’. That is ‘v’ -> ‘u’. A bidirected edge would not qualify as a predecessor.

relabel_nodes(mapping, copy=True)#

Relabel the nodes of the graph G according to a given mapping.

Parameters:

mapping : dict

A dictionary with the old labels as keys and new labels as values. A partial mapping is allowed. Mapping 2 nodes to a single node is allowed. Any non-node keys in the mapping are ignored.

copy : bool (optional, default=True)

If True return a copy, or if False relabel the nodes in place.

Returns:

G : instance of causal DAG

A copy (if copy is True) of the relabeled graph.

remove_bidirected_edge(u_of_edge, v_of_edge, remove_isolate=True)[source]#

Remove a bidirected edge between u and v.

The nodes u and v will be automatically added if they are not already in the graph.

Parameters:

u_of_edge, v_of_edge : nodes

Nodes can be, for example, strings or numbers. Nodes must be hashable (and not None) Python objects.

remove_isolate : bool

Whether or not to remove isolated nodes after the removal of the bidirected edge. Default is True.

See also

networkx.MultiDiGraph.add_edges_from

add a collection of edges

networkx.MultiDiGraph.add_edge

add an edge

Notes

Return type:

None

remove_edge(u, v)#

Remove directed edge.

remove_edges_from(ebunch)#

Remove directed edges.

remove_node(n)#

Remove node in causal graphs.

remove_nodes_from(ebunch)#

Remove nodes from causal graph.

sample(n=1000)#

Sample from a graph.

set_nodes_as_latent_confounders(nodes)#

Set nodes as latent unobserved confounders.

Note that this only works if the original node is a common cause of some variables in the graph.

Parameters:

nodes : list

A list of nodes to set. They must all be common causes of variables within the graph.

Returns:

graph : ADMG

The mixed-edge causal graph that results.

size(weight=None)#

Return the total number of edges possibly with weights.

soft_do(nodes, dependencies='original')[source]#

Apply a soft-intervention on node to causal graph.

Parameters:

nodes : nodes

A node within the graph.

dependencies : list of nodes | str, optional

What dependencies are now relevant for the node, by default ‘original’, which keeps all original directed edges (this still removes the bidirected edges). If a list of nodes, then it will add directed edges from those nodes to the node.

Returns:

causal_graph : ADMG

The mutilated graph.

spouses(node)#

Get other parents of the children of a node (spouses).

Return type:

Set

subgraph(nodes)[source]#

Create a causal subgraph of just certain nodes.

successors(u)#

Return successors of node u.

A successor is defined as nodes with a directed edge from ‘u’. That is ‘u’ -> ‘v’. A bidirected edge would not qualify as a successor.

to_adjacency_graph()#

Compute an adjacency undirected graph.

Two nodes are considered adjacent if there exist any type of edge between the two nodes.

Return type:

Graph

to_dot_graph(to_dagitty=False)#

Convert to ‘dot’ graph representation as a string.

The DOT language for graphviz is what is commonly used in R’s dagitty package. This is a string representation of the graph. However, this converts to a string format that is not 100% representative of DOT [1]. See Notes for more information.

Parameters:

to_dagitty : bool

Whether to conform to the Dagitty format, where the string begins with dag { instead of strict digraph {.

Returns:

dot_graph : str

A string representation in DOT format for the graph.

Notes

The output of this function can be immediately plugged into the dagitty online portal for drawing a graph.

For example, if we have a mixed edge graph, with directed and bidirected arrows (i.e. a causal DAG). Specifically, if we had 0 -> 1 with a latent confounder, we would get the following output:

strict digraph {
    0;
    1;
    0 -> 1;
    0 <-> 1;
}

To represent for example a bidirected edge, A <-> B, the DOT format would make you use A -> B [dir=both], but this is not as intuitive. A <-> B also complies with dagitty and other approaches to drawing graphs in Python/R.

References

[1] https://github.com/pydot/pydot

to_networkx()#

Converts causal graphs to networkx.

to_numpy()#

Convert to a matrix representation.

A single 2D numpy array is returned, since a PAG only maps one edge between any two nodes.

Returns:

numpy_graph : np.ndarray of shape (n_nodes, n_nodes)

The causal graph with values specified as a string character. For example, if A has a directed edge to B, then the array at indices for A and B has '->'.

Notes

In R’s pcalg package, the following encodes the types of edges as an array. We will follow the same encoding for our numpy array representation.

References

https://rdrr.io/cran/pcalg/man/fci.html

tomag()[source]#

Convert corresponding causal DAG to a MAG.

Examples using causal_networkx.ADMG#