16

Converting NetworkX to Graph-Tool

 1 year ago
source link: https://bbengfort.github.io/2016/06/graph-tool-from-networkx/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Converting NetworkX to Graph-Tool

June 23, 2016 · 3 min · Benjamin Bengfort

A Directed Graph Visualization Generated by Graph-Tool

This week I discovered graph-tool, a Python library for network analysis and visualization that is implemented in C++ with Boost. As a result, it can quickly and efficiently perform manipulations, statistical analyses of Graphs, and draw them in a visual pleasing style. It’s like using Python with the performance of C++, and I was rightly excited:

It's a bear to get setup, but once you do things get pretty nice. Moving my network viz over to it now!

— Benjamin Bengfort (@bbengfort) June 24, 2016

The visualization piece also excited me; as I tweeted, graph-tool sits between matplotlib and Gephi. It does a better job than matplotlib at the visualization, including things like edge curvature and directionality markers that are very difficult to do in native matplotlib. The graphs are very comparative to Gephi renderings, though it is probably a lot easier to do in Gephi then coding in graph-tool.

Because graph-tool is a C++ implementation, you can’t simply pip install it (giant bummer). I used homebrew to install it, and got it working outside of a virtual environment. I am not sure how to add it to a virtualenv or to the requirements of a project yet, but I imagine I will simply lazy load the module and raise an exception if a graph tool function is called (or gracefully fallback to NetworkX).

Ok, enough loving on graph-tool. Let’s do what we came here to do today, and that’s convert a NetworkX graph into a graph-tool graph.

Converting a NetworkX Graph to Graph-Tool#

Both NetworkX and Graph-Tool support property graphs, a data model that allows graphs, vertices, and edges to have arbitrary key-value pairs associated with them. In NetworkX the property graphs are implemented as a Python dictionary, and as a result, you can use them just like you’d use a dictionary. However, in graph-tool these properties are maintained as a PropertyMap, a typed object that must be defined before the property can be added to a graph element. This and other C++ requirements make graph-tool Graphs a harder to generate and work with, though the results are worth it.

First a note:

import networkx as nx
import graph_tool.all as gt

We will refer to networkx as nx and graph-tool as gt and prefix variables accordingly, e.g. nxG and gtG refer to the networkx and graph-tool graphs respectively. The snippet is long, but hopefully well commented for readability.

import networkx as nx import graph_tool as gt

def get_prop_type(value, key=None): """ Performs typing and value conversion for the graph_tool PropertyMap class. If a key is provided, it also ensures the key is in a format that can be used with the PropertyMap. Returns a tuple, (type name, value, key) """ if isinstance(key, unicode): # Encode the key as ASCII key = key.encode('ascii', errors='replace')

# Deal with the value if isinstance(value, bool): tname = 'bool'

elif isinstance(value, int): tname = 'float' value = float(value)

elif isinstance(value, float): tname = 'float'

elif isinstance(value, unicode): tname = 'string' value = value.encode('ascii', errors='replace')

elif isinstance(value, dict): tname = 'object'

else: tname = 'string' value = str(value)

return tname, value, key

def nx2gt(nxG): """ Converts a networkx graph to a graph-tool graph. """ # Phase 0: Create a directed or undirected graph-tool Graph gtG = gt.Graph(directed=nxG.is_directed())

# Add the Graph properties as "internal properties" for key, value in nxG.graph.items(): # Convert the value and key into a type for graph-tool tname, value, key = get_prop_type(value, key)

prop = gtG.new_graph_property(tname) # Create the PropertyMap gtG.graph_properties[key] = prop # Set the PropertyMap gtG.graph_properties[key] = value # Set the actual value

# Phase 1: Add the vertex and edge property maps # Go through all nodes and edges and add seen properties # Add the node properties first nprops = set() # cache keys to only add properties once for node, data in nxG.nodes_iter(data=True):

# Go through all the properties if not seen and add them. for key, val in data.items(): if key in nprops: continue # Skip properties already added

# Convert the value and key into a type for graph-tool tname, _, key = get_prop_type(val, key)

prop = gtG.new_vertex_property(tname) # Create the PropertyMap gtG.vertex_properties[key] = prop # Set the PropertyMap

# Add the key to the already seen properties nprops.add(key)

# Also add the node id: in NetworkX a node can be any hashable type, but # in graph-tool node are defined as indices. So we capture any strings # in a special PropertyMap called 'id' -- modify as needed! gtG.vertex_properties['id'] = gtG.new_vertex_property('string')

# Add the edge properties second eprops = set() # cache keys to only add properties once for src, dst, data in nxG.edges_iter(data=True):

# Go through all the edge properties if not seen and add them. for key, val in data.items(): if key in eprops: continue # Skip properties already added

# Convert the value and key into a type for graph-tool tname, _, key = get_prop_type(val, key)

prop = gtG.new_edge_property(tname) # Create the PropertyMap gtG.edge_properties[key] = prop # Set the PropertyMap

# Add the key to the already seen properties eprops.add(key)

# Phase 2: Actually add all the nodes and vertices with their properties # Add the nodes vertices = {} # vertex mapping for tracking edges later for node, data in nxG.nodes_iter(data=True):

# Create the vertex and annotate for our edges later v = gtG.add_vertex() vertices[node] = v

# Set the vertex properties, not forgetting the id property data['id'] = str(node) for key, value in data.items(): gtG.vp[key][v] = value # vp is short for vertex_properties

# Add the edges for src, dst, data in nxG.edges_iter(data=True):

# Look up the vertex structs from our vertices mapping and add edge. e = gtG.add_edge(vertices[src], vertices[dst])

# Add the edge properties for key, value in data.items(): gtG.ep[key][e] = value # ep is short for edge_properties

# Done, finally! return gtG

if __name__ == '__main__':

# Create the networkx graph nxG = nx.Graph(name="Undirected Graph") nxG.add_node("v1", name="alpha", color="red") nxG.add_node("v2", name="bravo", color="blue") nxG.add_node("v3", name="charlie", color="blue") nxG.add_node("v4", name="hub", color="purple") nxG.add_node("v5", name="delta", color="red") nxG.add_node("v6", name="echo", color="red")

nxG.add_edge("v1", "v2", weight=0.5, label="follows") nxG.add_edge("v1", "v3", weight=0.25, label="follows") nxG.add_edge("v2", "v4", weight=0.05, label="follows") nxG.add_edge("v3", "v4", weight=0.35, label="follows") nxG.add_edge("v5", "v4", weight=0.65, label="follows") nxG.add_edge("v6", "v4", weight=0.53, label="follows") nxG.add_edge("v5", "v6", weight=0.21, label="follows")

for item in nxG.edges_iter(data=True): print item

# Convert to graph-tool graph gtG = nx2gt(nxG) gtG.list_properties()

As you can see, converting a networkx graph or indeed even creating a graph-tool graph is very involved primarily because of the typing requirements of the C++ implementation. We haven’t even dealt with pitfalls like other Python objects like datetimes and the like. In case you didn’t want to inspect the code in detail, the phases are as follows:

  1. Create a graph-tool graph, using is_directed to determine the type.
  2. Add all the graph properties from nxG.graph
  3. Iterate through the nodes and add all their properties
  4. Create a special id property for nodes since nx uses any hashable type
  5. Iterate through the edges and add all their properties
  6. Iterate through the nodes (again) and add them and their values to the graph
  7. Iterate through the edges, use a look up to add them and their values

Potentially there is a mechanism to clean up and make this better, faster or stronger - but I think the main point is to illustrate how to get graph-tool graphs going so that you can use their excellent analytics and visualization tools!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK