import time
import logging
from graphit import Graph
from graphit.graph_algorithms import node_neighbors
logging.basicConfig(level=logging.WARN)
Tutorial 3: Working with graph selections¶
Tutorial 2 demonstrated how to retrieve/query specific nodes and edges from a graph. In graphit these node and edge selections are regarded subgraphs of the parent graph they are selected from. In this tutorial you will take a closer look at these subgraphs and the special properties they have.
Subgraphs are ‘views’ on the main graph¶
The various node and edge query and selection tools that graphit offers are powerfull in selecting subgraphs based on a wide range of criteria. The subgraphs returned are not a copy but a ‘view’ on the data in the main graph similar to dictionary views in Python. graphit aims in using views as often as possible for graph operations.
Views are convenient because they are fast compared to making a full
copies and do not fragment the data. The latter means that data
modifications made in a ‘view’ effect the data stored in the source
graph only and are synchronized automatically in all other views on the
same data. The source or origin Graph
representing the full node
and edge data set can always be obtained from any selection using the
origin
attribute.
1.1 Creating subgraphs¶
Create a subgraph using getnodes
or getedges
by default returns
a Graph
object with a view on the respective selection only. In both
cases this means a view on the nodes, the edges connecting them and
adjacency reflecting the neighbours of the nodes. Adjacency itself is
not a view as both keys (source nodes) and the values (list of
neighbouring nodes) will need to be adjusted to reflect the node and
edge selection.
g = Graph()
g.add_edges([(1, 2), (2, 4), (4, 5), (4, 6), (5, 7), (6, 8), (7, 9)], node_from_edge=True)
# Making a node selection using getnodes
nsub1 = g.getnodes([4,5,6])
print(nsub1.nodes.keys())
print(nsub1.edges.keys())
print(nsub1.adjacency())
# Nodes, edges and adjacency are 'views'
print(nsub1.nodes.is_view, nsub1.edges.is_view)
# Selecting nodes from another selection
nsub2 = nsub1.getnodes([5,8])
print(nsub2.nodes.keys())
print(nsub2.edges.keys())
print(nsub2.adjacency())
# Selecting nodes that do not exist will not work
nsub3 = nsub1.getnodes(8)
# The source graph for the selections remains available
print(nsub1.origin == g, nsub2.origin == g)
# Data modifications in views are made on the parent data object and synchronized
print(g.nodes[5])
nsub1.nodes[5]['key'] = 15
print(g.nodes[5], nsub2.nodes[5])
# Making an edge selection using getedges works in the same way as getnodes
esub1 = g.getedges([(2, 4), (4, 2), (4, 5), (5, 4), (4, 6)])
print(esub1.nodes.keys())
print(esub1.edges.keys())
print(esub1.adjacency())
1.2 Subgraph connectivity¶
Subgraphs created using getnodes
, getedges
or other selection
and query methods return Graph
objects with a view on the given
selection. Although they seem to be isolated subgraphs they maintain
connectivity with the origin graph by default.
Connectivity behaviour is controlled using the Graph.masked
attribute. False by default maintains connectivity with the origin graph
while set to False treats the selection as an isolated subgraph.
This is an important functionality as it for instance allows selection
of a node while still maintaining information on the environment it is
embedded in (like neighbours), masked
set to true on the other hand
treats the subgraph as an isolated graph even if it is connected to or
embedded in a larger graph.
# Get neighbours outside subgraph by default
print(node_neighbors(g, 6))
print(node_neighbors(nsub1, 6))
# Set masked only considers the subgraph nodes as neighbours
nsub1.masked = True
print(node_neighbors(nsub1, 6))
1.3 Everything is a view¶
graphit aims to use views as often as possible when operating on graph selections originating from the same origin graph. Only when operations will or are likely to return a graph with a distinct topology not found in the input graphs will it return a new independant graph.
1.3 Copying graphs¶
Moving from a graph with a ‘view’ on the origin graph to an independent
graph is handled by the Graph.copy
method that returns a (deep)copy.
cp = nsub1.copy()
print(cp)
print(cp.origin != nsub1.origin)
print(cp.nodes.is_view, cp.edges.is_view, cp.adjacency.is_view)
print(cp.nodes.keys())