Source code for pywhy_graphs.networkx.algorithms.causal.convert

from copy import deepcopy

import networkx as nx

import pywhy_graphs.networkx as pywhy_nx

__all__ = ["bidirected_to_unobserved_confounder"]


[docs] def bidirected_to_unobserved_confounder( G, directed_edge_name="directed", bidirected_edge_name="bidirected", uc_label="Unobserved Confounders", ): """Convert all bidirected edges to unobserved confounders. Parameters ---------- G : MixedEdgeGraph A causal graph with bidirected edges. directed_edge_name : str The name of the graph representing directed edges in ``G``. bidirected_edge_name : str The name of the graph representing bidirected edges in ``G``. uc_label : str The ``label`` of the unobserved variables that are added in. Returns ------- G_copy : DiGraph A networkx DiGraph that is a fully specified DAG with unobserved variables added in place of bidirected edges. Notes ----- This converts bidirected to unobserved confounding variables, that are unobserved nodes that have a directed edge pointing to the two variables that were connected with a bidirected edge. .. warning: This does not work for graphs with undirected edges yet. """ if not isinstance(G, pywhy_nx.MixedEdgeGraph): raise RuntimeError( "converting bidirected to confounders should only be run on a MixedEdgeGraph." ) uc_label = "Unobserved Confounders" G_copy = nx.DiGraph() G_copy.add_nodes_from((n, deepcopy(d)) for n, d in G.nodes.items()) G_copy.graph = deepcopy(G.graph) # add directed edges in G_copy.add_edges_from(G.get_graphs(edge_type=directed_edge_name).edges) # for every bidirected edge, add a new node bidirected_sub_graph = G.get_graphs(edge_type=bidirected_edge_name) for idx, latent_edge in enumerate(bidirected_sub_graph.edges): G_copy.add_node(f"U{idx}", label=uc_label, observed="no") # then add edges from the new UC to the nodes G_copy.add_edge(f"U{idx}", latent_edge[0]) G_copy.add_edge(f"U{idx}", latent_edge[1]) return G_copy