def add_edge(self, s, t, attrs=None, **attr): """Add an edge to a graph. Parameters ---------- graph : networkx.(Di)Graph s : hashable, source node id. t : hashable, target node id. attrs : dict Edge attributes. """ if attrs is None: attrs = attr else: try: attrs.update(attr) except AttributeError: raise ReGraphError( "The attr_dict argument must be a dictionary.") new_attrs = safe_deepcopy_dict(attrs) if s not in self.nodes(): raise GraphError("Node '{}' does not exist!".format(s)) if t not in self.nodes(): raise GraphError("Node '{}' does not exist!".format(t)) normalize_attrs(new_attrs) if (s, t) in self.edges(): raise GraphError("Edge '{}'->'{}' already exists!".format(s, t)) self._graph.add_edge(s, t, **new_attrs)
def clone_node(self, node_id, name=None): """Clone node. Create a new node, a copy of a node with `node_id`, and reconnect it with all the adjacent nodes of `node_id`. Parameters ---------- node_id : hashable, Id of a node to clone. name : hashable, optional Id for the clone, if is not specified, new id will be generated. Returns ------- new_node : hashable Id of the new node corresponding to the clone Raises ------ GraphError If node wiht `node_id` does not exists or a node with `name` (clone's name) already exists. """ if node_id not in self.nodes(): raise GraphError("Node '{}' does not exist!".format(node_id)) # generate new name for a clone if name is None: i = 1 new_node = str(node_id) + str(i) while new_node in self.nodes(): i += 1 new_node = str(node_id) + str(i) else: if name in self.nodes(): raise GraphError("Node '{}' already exists!".format(name)) else: new_node = name self.add_node(new_node, self.get_node(node_id)) # Connect all the edges self.add_edges_from( set([(n, new_node) for n, _ in self.in_edges(node_id) if (n, new_node) not in self.edges()])) self.add_edges_from( set([(new_node, n) for _, n in self.out_edges(node_id) if (new_node, n) not in self.edges()])) # Copy the attributes of the edges for s, t in self.in_edges(node_id): self.set_edge(s, new_node, safe_deepcopy_dict(self.get_edge(s, t))) for s, t in self.out_edges(node_id): self.set_edge(new_node, t, safe_deepcopy_dict(self.get_edge(s, t))) return new_node
def add_edge(graph, s, t, attrs=None, **attr): """Add an edge to a graph. Parameters ---------- graph : networkx.(Di)Graph s : hashable, source node id. t : hashable, target node id. attrs : dict Edge attributes. Raises ------ ReGraphError If `attrs` is not a dictionary GraphError If either one of the nodes does not exist in the graph or an edge between `s` and `t` already exists. """ # Set up attribute dict (from Networkx to preserve the signature). if attrs is None: attrs = attr else: try: attrs.update(attr) except AttributeError: raise ReGraphError( "The attr_dict argument must be a dictionary." ) new_attrs = deepcopy(attrs) if s not in graph.nodes(): raise GraphError("Node '%s' does not exist!" % s) if t not in graph.nodes(): raise GraphError("Node '%s' does not exist!" % t) normalize_attrs(new_attrs) if graph.is_directed(): if (s, t) in graph.edges(): raise GraphError( "Edge '%s'->'%s' already exists!" % (s, t) ) graph.add_edge(s, t, new_attrs) else: if (s, t) in graph.edges() or (t, s) in graph.edges(): raise GraphError( "Edge '%s'->'%s' already exists!" % (s, t) ) graph.add_edge(s, t) graph.edge[s][t] = new_attrs graph.edge[t][s] = new_attrs
def remove_node_attrs(self, node_id, attrs): """Remove attrs of a node specified by attrs_dict. Parameters ---------- node_id : hashable Node whose attributes to remove. attrs : dict Dictionary with attributes to remove. Raises ------ GraphError If a node with the specified id does not exist. """ if node_id not in self.nodes(): raise GraphError("Node '%s' does not exist!" % str(node_id)) elif attrs is None: warnings.warn( "You want to remove attrs from '{}' with an empty attrs_dict!". format(node_id), GraphAttrsWarning) node_attrs = safe_deepcopy_dict(self.get_node(node_id)) remove_attrs(node_attrs, attrs, normalize=True) self.update_node_attrs(node_id, node_attrs)
def update_edge_attrs(graph, s, t, attrs): """Update attributes of an edge. Parameters ---------- graph : networkx.(Di)Graph s : hashable, source node id. t : hashable, target node id. attrs : dict New attributes to assign to the edge Raises ------ GraphError If an edge between `s` and `t` does not exist. """ if not graph.has_edge(s, t): raise GraphError("Edge '%s->%s' does not exist!" % (str(s), str(t))) elif attrs is None: warnings.warn( "You want to update '%s->%s' attrs with an empty attrs_dict" % (str(s), str(t)), GraphAttrsWarning) else: new_attrs = deepcopy(attrs) normalize_attrs(new_attrs) graph.edge[s][t] = new_attrs if not graph.is_directed(): graph.edge[t][s] = new_attrs
def set_node_attrs(self, node_id, attrs, normalize=True, update=True): """Set node attrs. Parameters ---------- node_id : hashable Id of the node to update attrs : dict Dictionary with new attributes to set normalize : bool, optional Flag, when set to True attributes are normalized to be set-valued. True by default update : bool, optional Flag, when set to True attributes whose keys are not present in attrs are removed, True by default Raises ------ GraphError If a node `node_id` does not exist. """ if node_id not in self.nodes(): raise GraphError("Node '{}' does not exist!".format(node_id)) node_attrs = safe_deepcopy_dict(self.get_node(node_id)) set_attrs(node_attrs, attrs, normalize, update) self.update_node_attrs(node_id, node_attrs, normalize)
def update_node_attrs(graph, node_id, attrs): """Update attributes of a node. Parameters ---------- graph : networkx.(Di)Graph node_id : hashable, node to update. attrs : dict New attributes to assign to the node Raises ------ GraphError If a node with the specified id does not exist. """ new_attrs = deepcopy(attrs) if node_id not in graph.nodes(): raise GraphError("Node '%s' does not exist!" % str(node_id)) elif new_attrs is None: warnings.warn( "You want to update '%s' attrs with an empty attrs_dict!" % node_id, GraphAttrsWarning) else: normalize_attrs(new_attrs) graph.node[node_id] = new_attrs
def remove_edge_attrs(graph, s, t, attrs): """Remove attrs of an edge specified by attrs. Parameters ---------- graph : networkx.(Di)Graph s : hashable, source node id. t : hashable, target node id. attrs : dict Dictionary with attributes to remove. Raises ------ GraphError If an edge between `s` and `t` does not exist. """ if not graph.has_edge(s, t): raise GraphError("Edge %s-%s does not exist" % (str(s), str(t))) else: normalize_attrs(attrs) old_attrs = get_edge(graph, s, t) for key, value in attrs.items(): if key in old_attrs: new_set = old_attrs[key].difference(value) if new_set: old_attrs[key] = new_set else: del old_attrs[key] set_edge(graph, s, t, old_attrs)
def predecessors(self, node_id): """Return the set of predecessors.""" if node_id not in self.nodes(): raise GraphError( "Node '{}' does not exist in the graph".format(node_id)) return self._graph.predecessors(node_id)
def update_edge_attrs(self, s, t, attrs, normalize=True): """Update attributes of a node. Parameters ---------- s : hashable, source node of the edge to update. t : hashable, target node of the edge to update. attrs : dict New attributes to assign to the node """ if not self._graph.has_edge(s, t): raise GraphError("Edge '{}->{}' does not exist!".format(s, t)) if attrs is None: warnings.warn( "You want to update '{}->{}' attrs with an empty attrs_dict". format(s, t), GraphAttrsWarning) if normalize is True: normalize_attrs(attrs) attrs_to_remove = set() for k in self._graph.adj[s][t].keys(): if k not in attrs.keys(): attrs_to_remove.add(k) self._graph.add_edge(s, t, **attrs) for k in attrs_to_remove: del self._graph.adj[s][t][k]
def update_node_attrs(self, node_id, attrs, normalize=True): """Update attributes of a node. Parameters ---------- node_id : hashable, node to update. attrs : dict New attributes to assign to the node """ new_attrs = safe_deepcopy_dict(attrs) if node_id not in self.nodes(): raise GraphError("Node '{}' does not exist!".format(node_id)) elif new_attrs is None: warnings.warn( "You want to update '{}' attrs with an empty attrs_dict!". format(node_id), GraphAttrsWarning) else: if normalize is True: normalize_attrs(new_attrs) attrs_to_remove = set() for k in self._graph.nodes[node_id].keys(): if k not in new_attrs.keys(): attrs_to_remove.add(k) self._graph.add_node(node_id, **new_attrs) for k in attrs_to_remove: del self._graph.nodes[node_id][k]
def set_edge(graph, s, t, attrs): """Set edge attrs. Parameters ---------- graph : networkx.(Di)Graph s : hashable, source node id. t : hashable, target node id. attrs : dictionary Dictionary with attributes to set. Raises ------ GraphError If an edge between `s` and `t` does not exist. """ new_attrs = deepcopy(attrs) if not graph.has_edge(s, t): raise GraphError( "Edge %s->%s does not exist" % (str(s), str(t))) normalize_attrs(new_attrs) graph.edge[s][t] = new_attrs if not graph.is_directed(): graph.edge[t][s] = new_attrs
def set_edge_attrs(self, s, t, attrs, normalize=True, update=True): """Set edge attrs. Parameters ---------- attrs : dict Dictionary with new attributes to set normalize : bool, optional Flag, when set to True attributes are normalized to be set-valued. True by default update : bool, optional Flag, when set to True attributes whose keys are not present in attrs are removed, True by default Raises ------ GraphError If an edge between `s` and `t` does not exist. """ if not self.exists_edge(s, t): raise GraphError("Edge {}->{} does not exist".format(s, t)) edge_attrs = safe_deepcopy_dict(self.get_edge(s, t)) set_attrs(edge_attrs, attrs, normalize, update) self.update_edge_attrs(s, t, edge_attrs, normalize=normalize)
def add_node(graph, node_id, attrs=None): """Add a node to a graph. Parameters ---------- graph : networkx.(Di)Graph node_id : hashable Prefix that is prepended to the new unique name. attrs : dict, optional Node attributes. Raises ------- regraph.exceptions.GraphError Raises an error if node already exists in the graph. """ new_attrs = deepcopy(attrs) if new_attrs is None: new_attrs = dict() if node_id not in graph.nodes(): graph.add_node(node_id) normalize_attrs(new_attrs) graph.node[node_id] = new_attrs else: raise GraphError("Node '%s' already exists!" % node_id)
def add_node_attrs(graph, node, attrs): """Add new attributes to a node. Parameters ---------- graph : networkx.(Di)Graph node : hashable Id of a node to add attributes to. attrs : dict Attributes to add. Raises ------ GraphError If a node with the specified id does not exist. """ if node not in graph.nodes(): raise GraphError("Node '%s' does not exist!" % str(node)) normalize_attrs(attrs) node_attrs = graph.node[node] if node_attrs is None: graph.node[node] = copy.deepcopy(attrs) else: for key in attrs: if key in node_attrs: # node_attrs[key] = hyb_union(node_attrs[key], attrs_dict[key]) node_attrs[key] = node_attrs[key].union(attrs[key]) else: node_attrs[key] = attrs[key]
def remove_node_attrs(graph, node_id, attrs): """Remove attrs of a node specified by attrs_dict. Parameters ---------- graph : networkx.(Di)Graph node_id : hashable Node whose attributes to remove. attrs : dict Dictionary with attributes to remove. Raises ------ GraphError If a node with the specified id does not exist. """ if node_id not in graph.nodes(): raise GraphError("Node '%s' does not exist!" % str(node_id)) elif attrs is None: warnings.warn( "You want to remove attrs from '%s' with an empty attrs_dict!" % node_id, GraphAttrsWarning) elif graph.node[node_id] is None: warnings.warn("Node '%s' does not have any attribute!" % node_id, GraphAttrsWarning) else: normalize_attrs(attrs) old_attrs = graph.node[node_id] for key, value in attrs.items(): if key in old_attrs: new_set = old_attrs[key].difference(value) if not new_set: del old_attrs[key] else: old_attrs[key] = new_set
def add_edge_attrs(graph, s, t, attrs): """Add attributes of an edge in a graph. Parameters ---------- graph : networkx.(Di)Graph s : hashable, source node id. t : hashable, target node id. attrs : dict Dictionary with attributes to remove. Raises ------ GraphError If an edge between `s` and `t` does not exist. """ new_attrs = deepcopy(attrs) if not graph.has_edge(s, t): raise (GraphError("Edge '%s->%s' does not exist" % (str(s), str(t)))) elif new_attrs is None: pass else: normalize_attrs(new_attrs) edge_attrs = get_edge(graph, s, t) for key, value in new_attrs.items(): if key in edge_attrs: edge_attrs[key] = edge_attrs[key].union(value) else: edge_attrs[key] = value set_edge(graph, s, t, edge_attrs)
def get_relabeled_graph(graph, mapping): """Return a graph with node labeling specified in the mapping. Similar to networkx.relabel.relabel_nodes: https://networkx.github.io/documentation/development/_modules/networkx/relabel.html Parameters ---------- graph : networkx.(Di)Graph mapping: dict A dictionary with keys being old node ids and their values being new id's of the respective nodes. Returns ------- g : networkx.(Di)Graph New graph object isomorphic to the `graph` with the relabled nodes. Raises ------ ReGraphError If new id's do not define a set of distinct node id's. See also -------- regraph.primitives.relabel_nodes """ g = type(graph)() old_nodes = set(mapping.keys()) for old_node in old_nodes: try: new_node = mapping[old_node] except KeyError: continue try: g.add_node(new_node, graph.node[old_node]) except KeyError: raise GraphError("Node '%s' does not exist!" % old_node) new_edges = list() attributes = dict() for s, t in graph.edges(): new_edges.append((mapping[s], mapping[t])) attributes[(mapping[s], mapping[t])] =\ graph.edge[s][t] add_edges_from(g, new_edges) for s, t in g.edges(): if g.is_directed(): set_edge(g, s, t, attributes[(s, t)]) else: if (s, t) in attributes.keys(): set_edge(g, s, t, attributes[(s, t)]) else: set_edge(g, s, t, attributes[(t, s)]) return g
def remove_edge(self, s, t): """Remove edge from the graph. Parameters ---------- graph : networkx.(Di)Graph s : hashable, source node id. t : hashable, target node id. """ if (s, t) not in self.edges(): raise GraphError("Edge '{}->{}' does not exist!".format(s, t)) self._graph.remove_edge(s, t)
def remove_node(self, node_id): """Remove node. Parameters ---------- graph : networkx.(Di)Graph node_id : hashable, node to remove. """ if node_id in self.nodes(): self._graph.remove_node(node_id) else: raise GraphError("Node '{}' does not exist!".format(node_id)) return
def get_relabeled_graph(graph, mapping): """Return a graph with node labeling specified in the mapping. Parameters ---------- graph : networkx.(Di)Graph mapping: dict A dictionary with keys being old node ids and their values being new id's of the respective nodes. Returns ------- g : networkx.(Di)Graph New graph object isomorphic to the `graph` with the relabled nodes. Raises ------ ReGraphError If new id's do not define a set of distinct node id's. See also -------- regraph.primitives.relabel_nodes """ g = nx.DiGraph() old_nodes = set(mapping.keys()) for old_node in old_nodes: try: new_node = mapping[old_node] except KeyError: continue try: g.add_node(new_node, **graph.get_node(old_node)) except KeyError: raise GraphError("Node '%s' does not exist!" % old_node) new_edges = list() attributes = dict() for s, t in graph.edges(): new_edges.append((mapping[s], mapping[t])) attributes[(mapping[s], mapping[t])] =\ graph.get_edge(s, t) g.add_edges_from(new_edges) for s, t in g.edges(): nx.set_edge_attributes(g, {(s, t): attributes[(s, t)]}) return g
def add_node(self, node_id, attrs=None): """Abstract method for adding a node. Parameters ---------- node_id : hashable Prefix that is prepended to the new unique name. attrs : dict, optional Node attributes. """ if attrs is None: new_attrs = dict() else: new_attrs = safe_deepcopy_dict(attrs) normalize_attrs(new_attrs) if node_id not in self.nodes(): self._graph.add_node(node_id, **new_attrs) return node_id else: raise GraphError("Node '{}' already exists!".format(node_id))
def remove_edge(graph, s, t): """Remove edge from a graph. Parameters ---------- graph : networkx.(Di)Graph s : hashable, source node id. t : hashable, target node id. Raises ------ GraphError If edge between `s` and `t` does not exist. """ if graph.is_directed(): if (s, t) not in graph.edges(): raise GraphError("Edge '%s->%s' does not exist!" % (str(s), str(t))) graph.remove_edge(s, t)
def add_node_attrs(self, node, attrs): """Add new attributes to a node. Parameters ---------- node : hashable Id of a node to add attributes to. attrs : dict Attributes to add. Raises ------ GraphError If a node `node_id` does not exist. """ if node not in self.nodes(): raise GraphError("Node '{}' does not exist!".format(node)) node_attrs = safe_deepcopy_dict(self.get_node(node)) add_attrs(node_attrs, attrs, normalize=True) self.update_node_attrs(node, node_attrs)
def remove_node(graph, node_id): """Remove node. Parameters ---------- graph : networkx.(Di)Graph node_id : hashable, node to remove. Raises ------ GraphError If a node with the specified id does not exist. """ if node_id in graph.nodes(): neighbors = set(graph.__getitem__(node_id).keys()) neighbors -= {node_id} graph.remove_node(node_id) else: raise GraphError("Node %s does not exist!" % str(node_id)) return
def remove_edge_attrs(self, s, t, attrs): """Remove attrs of an edge specified by attrs. Parameters ---------- s : hashable Source node id. t : hashable Target node id. attrs : dict Dictionary with attributes to remove. Raises ------ GraphError If an edge between `s` and `t` does not exist. """ if not self.exists_edge(s, t): raise GraphError("Edge {}->{} does not exist".format(s, t)) edge_attrs = safe_deepcopy_dict(self.get_edge(s, t)) remove_attrs(edge_attrs, attrs, normalize=True) self.update_edge_attrs(s, t, edge_attrs)
def merge_nodes(graph, nodes, node_id=None, method="union", edge_method="union"): """Merge a list of nodes. Parameters ---------- graph : nx.(Di)Graph nodes : iterable Collection of node id's to merge. node_id : hashable, optional Id of a new node corresponding to the result of merge. method : optional Method of node attributes merge: if `"union"` the resulting node will contain the union of all attributes of the merged nodes, if `"intersection"`, the resulting node will contain their intersection. Default value is `"union"`. edge_method : optional Method of edge attributes merge: if `"union"` the edges that were merged will contain the union of all attributes, if `"intersection"` -- their ntersection. Default value is `"union"`. Returns ------- node_id : hashable Id of a new node corresponding to the result of merge. Raises ------ ReGraphError If unknown merging method is provided GraphError If some nodes from `nodes` do not exist in the graph. Examples -------- >>> g = nx.DiGraph() >>> add_nodes_from(g, [(1, {"a": 1, "b": 1}), 2, (3, {"a": 3, "c": 3})]) >>> add_edges_from(g, [(1, 3), (1, 2), (2, 3)]) >>> merge_nodes(g, [1, 3], "merged_node") >>> g.nodes() ["merged_node", 2] >>> g.edges() [("merged_node", "merged_node"), ("merged_node", 2), (2, "merged_node")] >>> g.node["merged_node"] {"a": {1, 3}, "b": {1}, "c": {3}} """ if len(nodes) == 1: if node_id is not None: relabel_node(graph, nodes[0], node_id) elif len(nodes) > 1: if method is None: method = "union" if edge_method is None: method = "union" # Generate name for new node if node_id is None: node_id = "_".join([str(n) for n in nodes]) elif node_id in graph.nodes() and (node_id not in nodes): raise GraphError("New name for merged node is not valid: " "node with name '%s' already exists!" % node_id) # Merge data attached to node according to the method specified # restore proper connectivity if method == "union": attr_accumulator = {} elif method == "intersection": attr_accumulator = deepcopy(graph.node[nodes[0]]) else: raise ReGraphError("Merging method '%s' is not defined!" % method) self_loop = False self_loop_attrs = {} if graph.is_directed(): source_nodes = set() target_nodes = set() source_dict = {} target_dict = {} else: neighbors = set() neighbors_dict = {} all_neighbors = set() for node in nodes: all_neighbors |= set(graph.__getitem__(node).keys()) attr_accumulator = merge_attributes(attr_accumulator, graph.node[node], method) if graph.is_directed(): in_edges = graph.in_edges(node) out_edges = graph.out_edges(node) # manage self loops for s, t in in_edges: if s in nodes: self_loop = True if len(self_loop_attrs) == 0: self_loop_attrs = graph.edge[s][t] else: self_loop_attrs = merge_attributes( self_loop_attrs, graph.edge[s][t], edge_method) for s, t in out_edges: if t in nodes: self_loop = True if len(self_loop_attrs) == 0: self_loop_attrs = graph.edge[s][t] else: self_loop_attrs = merge_attributes( self_loop_attrs, graph.edge[s][t], edge_method) source_nodes.update( [n if n not in nodes else node_id for n, _ in in_edges]) target_nodes.update( [n if n not in nodes else node_id for _, n in out_edges]) for edge in in_edges: if not edge[0] in source_dict.keys(): attrs = graph.edge[edge[0]][edge[1]] source_dict.update({edge[0]: attrs}) else: attrs = merge_attributes(source_dict[edge[0]], graph.edge[edge[0]][edge[1]], edge_method) source_dict.update({edge[0]: attrs}) for edge in out_edges: if not edge[1] in target_dict.keys(): attrs = graph.edge[edge[0]][edge[1]] target_dict.update({edge[1]: attrs}) else: attrs = merge_attributes(target_dict[edge[1]], graph.edge[edge[0]][edge[1]], edge_method) target_dict.update({edge[1]: attrs}) else: for n in graph.neighbors(node): if n in nodes: self_loop = True if len(self_loop_attrs) == 0: self_loop_attrs = graph.edge[n][node] else: self_loop_attrs = merge_attributes( self_loop_attrs, graph.edge[n][node], edge_method) neighbors.update( [n for n in graph.neighbors(node) if n not in nodes]) for n in graph.neighbors(node): if n not in nodes: if n not in neighbors_dict.keys(): attrs = graph.edge[n][node] neighbors_dict.update({n: attrs}) else: attrs = merge_attributes(neighbors_dict[n], graph.edge[n][node], edge_method) neighbors_dict.update({n: attrs}) graph.remove_node(node) all_neighbors -= {node} add_node(graph, node_id, attr_accumulator) all_neighbors.add(node_id) if graph.is_directed(): if self_loop: add_edges_from(graph, [(node_id, node_id)]) graph.edge[node_id][node_id] = self_loop_attrs for n in source_nodes: if not exists_edge(graph, n, node_id): add_edge(graph, n, node_id) for n in target_nodes: if not exists_edge(graph, node_id, n): add_edge(graph, node_id, n) # Attach accumulated attributes to edges for node, attrs in source_dict.items(): if node not in nodes: graph.edge[node][node_id] = attrs for node, attrs in target_dict.items(): if node not in nodes: graph.edge[node_id][node] = attrs else: if self_loop: add_edges_from(graph, [(node_id, node_id)]) graph.edge[node_id][node_id] = self_loop_attrs add_edges_from(graph, [(n, node_id) for n in neighbors]) # Attach accumulated attributes to edges for node, attrs in neighbors_dict.items(): if node not in nodes: graph.edge[node][node_id] = attrs graph.edge[node_id][node] = attrs return node_id
def get_relabeled_graph(self, mapping, raw=False): """Return a graph with node labeling specified in the mapping. Parameters ---------- graph : networkx.(Di)Graph mapping: dict A dictionary with keys being old node ids and their values being new id's of the respective nodes. Returns ------- g : networkx.(Di)Graph New graph object isomorphic to the `graph` with the relabled nodes. Raises ------ ReGraphError If new id's do not define a set of distinct node id's. See also -------- regraph.primitives.relabel_nodes """ g = nx.DiGraph() old_nodes = set(mapping.keys()) for old_node in old_nodes: try: new_node = mapping[old_node] except KeyError: pass try: g.add_node(new_node, **self.get_node(old_node)) except KeyError: raise GraphError("Node '%s' does not exist!" % old_node) new_edges = list() attributes = dict() for s, t in self.edges(): new_s = None new_t = None try: new_s = mapping[s] except KeyError: pass try: new_t = mapping[t] except KeyError: pass if new_s and new_t: new_edges.append((new_s, new_t)) attributes[(new_s, new_t)] = self.get_edge(s, t) g.add_edges_from(new_edges) for s, t in g.edges(): for k, v in attributes[(s, t)].items(): g.adj[s][t][k] = v if not raw: new_obj = NXGraph() new_obj._graph = g return new_obj return g
def clone_node(graph, node_id, name=None): """Clone node. Create a new node, a copy of a node with `node_id`, and reconnect it with all the adjacent nodes of `node_id`. Parameters ---------- graph : networkx.(Di)Graph node_id : id of a node to clone. name : id for the clone, optional If is not specified, new id will be generated. Returns ------- new_node : hashable, clone's id Raises ------ GraphError If node wiht `node_id` does not exists or a node with `name` (clone's name) already exists. Examples -------- >>> g = nx.DiGraph() >>> add_nodes_from(g, [1, 2, 3]) >>> add_edges_from(g, [(1, 2), (3, 2)]) >>> clone_node(g, 2, "2_clone") >>> g.nodes() [1, 2, "2_clone", 3] >>> g.edges() [(1, 2), (1, "2_clone"), (3, 2), (3, "2_clone")] """ if node_id not in graph.nodes(): raise GraphError("Node '%s' does not exist!" % str(node_id)) # generate new name for a clone if name is None: i = 1 new_node = str(node_id) + str(i) while new_node in graph.nodes(): i += 1 new_node = str(node_id) + str(i) else: if name in graph.nodes(): raise GraphError("Node '%s' already exists!" % str(name)) else: new_node = name graph.add_node(new_node, deepcopy(graph.node[node_id])) # Connect all the edges if graph.is_directed(): add_edges_from(graph, [(n, new_node) for n, _ in graph.in_edges(node_id)]) add_edges_from(graph, [(new_node, n) for _, n in graph.out_edges(node_id)]) # Copy the attributes of the edges for s, t in graph.in_edges(node_id): graph.edge[s][new_node] = deepcopy(graph.edge[s][t]) for s, t in graph.out_edges(node_id): graph.edge[new_node][t] = deepcopy(graph.edge[s][t]) else: add_edges_from(graph, [(n, new_node) for n in graph.neighbors(node_id)]) # Copy the attributes of the edges for n in graph.neighbors(node_id): graph.edge[new_node][n] = deepcopy(graph.edge[n][node_id]) graph.edge[n][new_node] = graph.edge[new_node][n] return new_node
def merge_nodes(self, nodes, node_id=None, method="union", edge_method="union"): """Merge a list of nodes. Parameters ---------- nodes : iterable Collection of node id's to merge. node_id : hashable, optional Id of a new node corresponding to the result of merge. method : optional Method of node attributes merge: if `"union"` the resulting node will contain the union of all attributes of the merged nodes, if `"intersection"`, the resulting node will contain their intersection. Default value is `"union"`. edge_method : optional Method of edge attributes merge: if `"union"` the edges that were merged will contain the union of all attributes, if `"intersection"` -- their ntersection. Default value is `"union"`. """ if len(nodes) > 1: if method is None: method = "union" if edge_method is None: method = "union" # Generate name for new node if node_id is None: node_id = "_".join(sorted([str(n) for n in nodes])) if node_id in self.nodes(): node_id = self.generate_new_node_id(node_id) elif node_id in self.nodes() and (node_id not in nodes): raise GraphError("New name for merged node is not valid: " "node with name '%s' already exists!" % node_id) # Merge data attached to node according to the method specified # restore proper connectivity if method == "union": attr_accumulator = {} elif method == "intersection": attr_accumulator = safe_deepcopy_dict(self.get_node(nodes[0])) else: raise ReGraphError( "Merging method '{}' is not defined!".format(method)) self_loop = False self_loop_attrs = {} source_nodes = set() target_nodes = set() source_dict = {} target_dict = {} for node in nodes: attr_accumulator = merge_attributes(attr_accumulator, self.get_node(node), method) in_edges = self.in_edges(node) out_edges = self.out_edges(node) # manage self loops for s, t in in_edges: if s in nodes: self_loop = True if len(self_loop_attrs) == 0: self_loop_attrs = self.get_edge(s, t) else: self_loop_attrs = merge_attributes( self_loop_attrs, self.get_edge(s, t), edge_method) for s, t in out_edges: if t in nodes: self_loop = True if len(self_loop_attrs) == 0: self_loop_attrs = self.get_edge(s, t) else: self_loop_attrs = merge_attributes( self_loop_attrs, self.get_edge(s, t), edge_method) source_nodes.update( [n if n not in nodes else node_id for n, _ in in_edges]) target_nodes.update( [n if n not in nodes else node_id for _, n in out_edges]) for edge in in_edges: if not edge[0] in source_dict.keys(): attrs = self.get_edge(edge[0], edge[1]) source_dict.update({edge[0]: attrs}) else: attrs = merge_attributes( source_dict[edge[0]], self.get_edge(edge[0], edge[1]), edge_method) source_dict.update({edge[0]: attrs}) for edge in out_edges: if not edge[1] in target_dict.keys(): attrs = self.get_edge(edge[0], edge[1]) target_dict.update({edge[1]: attrs}) else: attrs = merge_attributes( target_dict[edge[1]], self.get_edge(edge[0], edge[1]), edge_method) target_dict.update({edge[1]: attrs}) self.remove_node(node) self.add_node(node_id, attr_accumulator) if self_loop: self.add_edges_from([(node_id, node_id)]) self.set_edge(node_id, node_id, self_loop_attrs) for n in source_nodes: if not self.exists_edge(n, node_id): self.add_edge(n, node_id) for n in target_nodes: if not self.exists_edge(node_id, n): self.add_edge(node_id, n) # Attach accumulated attributes to edges for node, attrs in source_dict.items(): if node not in nodes: self.set_edge(node, node_id, attrs) for node, attrs in target_dict.items(): if node not in nodes: self.set_edge(node_id, node, attrs) return node_id else: raise ReGraphError( "More than two nodes should be specified for merging!")