The GraphGroup

The GraphGroup is specialized on managing and handling graph-related data. The group defines a graph via data groups and data containers that store nodes, edges, and optionally their properties.


Creating a GraphGroup

The GraphGroup holds the following, customizable variables that describe in which containers or attributes to find the info on the nodes and edges:

  • _GG_node_container = "nodes": The name of the container or group (node_container) containing the node data

  • _GG_edge_container = "edges": The name of the container or group (edge_container) containing the edge data

  • _GG_attr_directed = "directed": The GraphGroup attribute (boolean) describing whether the graph is directed or not

  • _GG_attr_parallel = "parallel": The GraphGroup attribute (boolean) describing whether the graph allows for parallel edges or not

  • _GG_attr_edge_container_is_transposed = "edge_container_is_transposed": The GraphGroup attribute (boolean) describing whether the edge container is transposed, i.e., has the shape (edge-tuple-size, edge-number)

  • _GG_attr_keep_dim = "keep_dim": The GraphGroup attribute (iterable) describing which dimensions are not to be squeezed during data selection.

If you do not change anything, the default values are taken.

The GraphGroup only holds and manages graph data but itself is not a graph, in the sense that no functionality such as finding the neighborhood of a node is implemented there. Instead, the GraphGroup uses the networkx library and its interface for creating Graph objects. In the following, an overview is given how graphs can be created from a GraphGroup.

Creating Graphs from a GraphGroup

The GraphGroup contains the create_graph() method that creates a graph from the containers and groups that are part of the GraphGroup together with information provided by the GraphGroup attributes. However, the function also allows you to explicitly set graph properties such as whether the graph should be directed or allow for parallel edges. The function returns a networkx graph object corresponding to the provided data and information.

The create_graph() function further allows you to optionally set node and edge properties by specifying node_props or edge_props lists.

Any data can be pre-selected using the sel and isel arguments. The selectors are applied to all data involved, i.e., to node, edge, as well as property data. It is also possible to provide both sel and isel as long as the intersection of both key-sets is empty.

Warning

Invalid keys in sel and isel are ignored silently. This means that the node, edge, and property data need not have the same set of dimensions in order to apply a selection. Moreover, all dimensions of size 1 are squeezed, hence no selection has to be specified in such scenarios, i.e. when the selection is unambiguous.

If you have node/edge data that changes over time, you can select along the time dimension directly via the at_times or the at_time_idx argument. This sets or overwrites the respective entry in the sel or isel dicts.

The following example demonstrates the graph creation: Let us assume that we have a graph with static nodes and dynamic edges, each with dynamic properties. The dynamic data, stored as TimeSeriesGroup, is given for two points in time. The resulting data tree looks as follows:

# graph_group                   <GraphGroup, 4 members, 2 attributes>
# └┬ nodes                      <XrDataContainer, ..., shape (10,), 0 attributes>
#  ├ some_node_prop             <XrDataContainer, ..., shape (2,10), 0 attributes>
#  ├ edges                      <TimeSeriesGroup, 2 members, 0 attributes>
#    └┬ 0                       <XrDataContainer, ..., shape (9,2), 0 attributes>
#     └ 10                      <XrDataContainer, ..., shape (6,2), 0 attributes>
#  ├ some_edge_prop             <TimeSeriesGroup, 2 members, 0 attributes>
#    └┬ 0                       <XrDataContainer, ..., shape (9,), 0 attributes>
#     └ 10                      <XrDataContainer, ..., shape (6,), 0 attributes>
#  └ other_edge_prop            <XrDataContainer, ..., shape (6,), 0 attributes>

Let’s now create a graph from the GraphGroup:

# Create the initial graph from the graph group without node/edge properties
g = graph_group.create_graph(at_time=0) # time specified by value

# Now, create the final graph with `some_node_prop` as node property and
# `some_edge_prop` as edge property.
g = graph_group.create_graph(at_time_idx=-1, # time specified via index
                             node_props=["some_node_prop"],
                             edge_props=["some_edge_prop"])

Hint

Graph creation might fail for graphs with a single node (edge) due to the node (edge) dimension (of size 1) being squeezed out. It is therefore strongly recommended to specify the node and edge dimension names in the _GG_attr_keep_dim group attribute. Alternatively, they can be specified via the keep_dim argument in create_graph(), set_node_property(), and set_edge_property().

Setting Graph Properties

If you already have a networkx graph object, you can set node or edge properties using the set_node_property() or set_edge_property() function. Properties can be added from:

In both cases, name will be the name of the node or edge property in the networkx graph.

Again, the data can be pre-selected using the sel, isel, at_time, and at_time_idx arguments.

In the example below, the other_edge_prop data stored inside the graph group is added as edge property. Note that time specification is required here, even though other_edge_prop is one-dimensional, because edge_container contains edge data for multiple times.

# Set the edge property manually from the `other_edge_prop` data container
# and select the data of the last time step
graph_group.set_edge_property(g=g, name="other_edge_prop", at_time_idx=-1)

Node properties can be added analogously.

Note

Node (edge) properties can only be added for those nodes (edges) that are available in the node_container (edge_container). By default, property data is assumed to be aligned with the GraphGroups node (edge) data. However, it can be aligned with the latter via xarray.align by setting align to True in set_node_property() (set_edge_property()). The indexes of the node_container (edge_container) are used for the alignment in each dimension. If the class variable _GG_WARN_UPON_BAD_ALIGN is set to True (default: True), warnings on possible pitfalls are given.

Loading External Data as Graph Property

If you want to add a graph property from data that is not stored inside the GraphGroup (e.g., you have pre-processed some data), this can be realized in two different ways:

  • Making use of the property_maps.

    After registering external data with a key using register_property_map(), it will be permanently available via the provided key, i.e., the key can be passed as name in the set_*_property functions.

  • Loading the external data directly by passing it via the data argument in the respective set_*_property function.

    The name argument then sets the name of the property.

Have a look at a small example where some external data ext_data is added to the graph as a node property:

# Make the external data available in the graph group under the given key
graph_group.register_property_map("my_ext_node_prop", data=ext_data)

# Use the newly created key to set the external data as node property
graph_group.set_node_property(g=g, name="my_ext_node_prop")

# Alternatively, load the external data directly via the `data` argument
graph_group.set_node_property(g=g, name="my_ext_node_prop", data=ext_data)