Source code for xgi.convert.hif_dict

"""Methods for converting to/from HIF standard."""

from collections import defaultdict

from ..core import DiHypergraph, Hypergraph, SimplicialComplex
from ..utils import IDDict
from .bipartite_edges import to_bipartite_edgelist

__all__ = ["to_hif_dict", "from_hif_dict"]


[docs]def to_hif_dict(H): """ A function to create a dictionary according to the HIF standard from a higher-order network. For more information, see the HIF `project <https://github.com/pszufe/HIF_validators>`_. Parameters ---------- H: Hypergraph, DiHypergraph, or SimplicialComplex object The specified higher-order network Returns ------- defaultdict A dict according to the HIF standard. """ data = defaultdict(list) data["metadata"] = {} data["metadata"].update(H._net_attr) if isinstance(H, SimplicialComplex): data["network-type"] = "asc" elif isinstance(H, Hypergraph): data["network-type"] = "undirected" elif isinstance(H, DiHypergraph): data["network-type"] = "directed" # get node data isolates = set(H.nodes.isolates()) nodes_with_attrs = set(n for n in H.nodes if H.nodes[n]) for n in isolates.union(nodes_with_attrs): attr = {"attrs": H.nodes[n]} if H.nodes[n] else {} data["nodes"].append(IDDict({"node": n}) + attr) empty = set(H.edges.empty()) edges_with_attrs = set(e for e in H.edges if H.edges[e]) for e in empty.union(edges_with_attrs): attr = {"attrs": H.edges[e]} if H.edges[e] else {} data["edges"].append(IDDict({"edge": e}) + attr) # hyperedge dict if data["network-type"] == "directed": _convert_d = lambda d: "tail" if d == "in" else "head" data["incidences"] = [ IDDict({"edge": e, "node": n, "direction": _convert_d(d)}) for n, e, d in to_bipartite_edgelist(H) ] elif data["network-type"] in {"undirected", "asc"}: data["incidences"] = [ IDDict({"edge": e, "node": n}) for n, e in to_bipartite_edgelist(H) ] return data
[docs]def from_hif_dict(data, nodetype=None, edgetype=None): """ A function to read a dictionary that follows the HIF standard. For more information, see the HIF `project <https://github.com/pszufe/HIF_validators>`_. Parameters ---------- data: dict A dictionary in the hypergraph JSON format nodetype: type, optional Type that the node IDs will be cast to edgetype: type, optional Type that the edge IDs will be cast to Returns ------- A Hypergraph, SimplicialComplex, or DiHypergraph object The loaded network """ def _empty_edge(network_type): if network_type in {"asc", "undirected"}: return set() else: return (set(), set()) def _convert_id(i, idtype): if idtype: try: return idtype(i) except ValueError as e: raise TypeError(f"Failed to convert ID {i} to type {idtype}.") from e else: return i _convert_d = lambda d: "in" if d == "tail" else "out" if "network-type" in data: network_type = data["network-type"] else: network_type = "undirected" if network_type in {"asc", "undirected"}: H = Hypergraph() elif network_type == "directed": H = DiHypergraph() # Import network metadata if "metadata" in data: H._net_attr.update(data["metadata"]) for record in data["incidences"]: n = _convert_id(record["node"], nodetype) e = _convert_id(record["edge"], edgetype) if network_type == "directed": d = record["direction"] d = _convert_d(d) # convert from head/tail to in/out H.add_node_to_edge(e, n, d) else: H.add_node_to_edge(e, n) # import node attributes if they exist if "nodes" in data: for record in data["nodes"]: n = _convert_id(record["node"], nodetype) if "attrs" in record: attr = record["attrs"] else: attr = {} if n not in H._node: H.add_node(n, **attr) else: H.set_node_attributes({n: attr}) # import edge attributes if they exist if "edges" in data: for record in data["edges"]: e = _convert_id(record["edge"], edgetype) if "attrs" in record: attr = record["attrs"] else: attr = {} if e not in H._edge: H.add_edge(_empty_edge(network_type), e, **attr) else: H.set_edge_attributes({e: attr}) if network_type == "asc": H = SimplicialComplex(H) return H