Skip to content

Commit

Permalink
Merge 46db273 into 9690d37
Browse files Browse the repository at this point in the history
  • Loading branch information
MengLiuPurdue committed Nov 26, 2018
2 parents 9690d37 + 46db273 commit a1b669e
Show file tree
Hide file tree
Showing 3 changed files with 253 additions and 3 deletions.
196 changes: 194 additions & 2 deletions localgraphclustering/GraphLocal.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,17 @@
import warnings
import collections as cole
from .cpp import *
import random

import gzip
import bz2
import lzma

import multiprocessing as mp

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

def _load_from_shared(sabuf, dtype, shape):
return np.frombuffer(sabuf, dtype=dtype).reshape(shape)

Expand Down Expand Up @@ -558,8 +562,6 @@ def local_extrema(self,vals,strict=False,reverse=False):
Parameters
----------
G: GraphLocal
vals: Sequence[float]
a feature value per node used to find the ex against each other, i.e. conductance
Expand Down Expand Up @@ -616,3 +618,193 @@ def local_extrema(self,vals,strict=False,reverse=False):
minvals = vals[minverts]

return minverts, minvals

def draw(self,coords,alpha=1.0,nodesize=5,linewidth=1,
nodealpha=1.0,edgealpha=0.01,nodecolor='r',
edgecolor='k',nodemarker='o',setalpha=1.0,
setcolor='y',axs=None,fig=None,nodeset=None,
groups=None):
"""
standard drawing function of GraphLocal object
Parameters
----------
coords: a n-by-2 or n-by-3 array with coordinates for each node of the graph.
Optional parameters
------------------
alpha: float (1.0 by default)
the overall alpha scaling of the plot, [0,1]
nodealpha: float (1.0 by default)
the overall node alpha scaling of the plot, [0, 1]
edgealpha: float (1.0 by default)
the overall edge alpha scaling of the plot, [0, 1]
setalpha: float (1.0 by default)
the overall set alpha scaling of the plot, [0, 1]
nodecolor: string or RGB ('r' by default)
edgecolor: string or RGB ('k' by default)
setcolor: string or RGB ('y' by default)
nodemarker: string ('o' by default)
nodesize: float (5.0 by default)
linewidth: float (1.0 by default)
nodeset: Sequence[int] (None by default)
a set of nodes to highlight
groups: Sequence[Sequence[int]] (None by default)
node partitions, different colors will be assigned to different groups
axs,fig: None,None (default)
by default it will create a new figure, or this will plot in axs if not None.
Returns
-------
a dictionary with:
fig, ax, nodes, edges, setnodes, setedges, groupnodes, groupedges
these are the handles to the actual plot elements, so that you could change
values after the fact.
"""
if axs == None:
fig = plt.figure()
if len(coords[0]) == 3:
axs = fig.add_subplot(111, projection='3d')
else:
axs = fig.add_subplot(111)
axs.set_axis_off()
nodeset = set(nodeset) if nodeset is not None else set()
nodelist_in = []
nodelist_out = []
for i in range(self._num_vertices):
if i in nodeset:
nodelist_in.append(i)
else:
nodelist_out.append(i)
N = nx.Graph()
coo = self.adjacency_matrix.tocoo()
#color, coords and alpha information are stored directly in node attributes
N.add_nodes_from(list(zip(nodelist_out,[{'pos':coords[i],'alpha':alpha*nodealpha,'color':nodecolor} for i in nodelist_out])))
N.add_nodes_from(list(zip(nodelist_in,[{'pos':coords[i],'alpha':alpha*setalpha,'color':setcolor} for i in nodelist_in])))
edge_list = [(coo.row[i],coo.col[i]) for i in range(self._num_edges)]
#color information is stored directly in edge information
N.add_edges_from(edge_list,color=edgecolor)
setedges = []
for i in range(self._num_edges):
if coo.row[i] in nodeset and coo.col[i] in nodeset:
setedges.append((coo.row[i],coo.col[i]))

#reassign node colors based on partition
groupedges = None
if groups is not None:
groupedges = [[] for i in range(len(groups))]
number_of_colors = len(groups)
color = ["#"+''.join([random.choice('0123456789ABCDEF') for j in range(6)])
for i in range(number_of_colors)]
for i,g in enumerate(groups):
nx.set_node_attributes(N,{k:{'color':v} for (k,v) in zip(g,[color[i]]*len(g))})
sg = set(g)
for j in g:
for k in range(self.ai[j],self.ai[j+1]):
if self.aj[k] >= j and self.aj[k] in sg:
groupedges[i].append((j,self.aj[k]))

if len(coords[0]) == 3:
self.draw_nx_3d(N,axs,nodemarker,nodesize,alpha*edgealpha,linewidth)
else:
self.draw_nx(N,axs,nodemarker,nodesize,alpha*edgealpha,linewidth)

ret_dict = {"fig":fig,"ax":axs,"nodes":list(N.nodes),"edges":list(N.edges),
"setnodes":nodelist_in,"setedges":setedges,"nx_graph":N,"groupnodes":groups,
"groupedges":groupedges}
return ret_dict

@staticmethod
def draw_nx(N,axs,nodemarker='o',nodesize=5,edgealpha=0.01,linewidth=1):
"""
a static method to draw a networkx instance, the plot will be based on node and edge attributes,
valid node attributes include "alpha", "color" and "pos". valid edge attributes include "color",
designed to modify the plot returned by calling "draw" function
Parameters
----------
N: networkx object
axs: matplotlib axes
nodemarker: 'o' by default
nodesize: 5 by default
edgealpha: 0.01 by default
linewidth: 1 by default
"""
nnodes = nx.number_of_nodes(N)
node_alpha_list = [0]*nnodes
node_color_list = ['']*nnodes
for i,node in enumerate(N.nodes(data=True)):
node_alpha_list[i] = node[1]['alpha']
node_color_list[i] = node[1]['color']
nedges = nx.number_of_edges(N)
edge_color_list = ['']*nedges
for i,edge in enumerate(N.edges(data=True)):
edge_color_list[i] = edge[2]['color']
nx.draw_networkx_nodes(N,pos=nx.get_node_attributes(N,'pos'),node_size=nodesize,ax=axs,alpha=node_alpha_list,
node_color=node_color_list,node_shape=nodemarker)
nx.draw_networkx_edges(N,pos=nx.get_node_attributes(N,'pos'),ax=axs,edge_color=edge_color_list,alpha=edgealpha,
linewidths=linewidth)

@staticmethod
def draw_nx_3d(N,axs,nodemarker='o',nodesize=5,edgealpha=0.01,linewidth=1,angle=30):
"""
a static method to draw a networkx instance, the plot will be based on node and edge attributes,
valid node attributes include "alpha", "color" and "pos". valid edge attributes include "color",
designed to modify the plot returned by calling "draw" function
Parameters
----------
N: networkx object
axs: matplotlib axes
nodemarker: 'o' by default
nodesize: 5 by default
edgealpha: 0.01 by default
linewidth: 1 by default
angle: view angle, 30 by default
"""
pos = nx.get_node_attributes(N,'pos')
# Loop on the pos dictionary to extract the x,y,z coordinates of each node
for key, value in N.nodes(data=True):
coord = value['pos']
# Scatter plot
axs.scatter(coord[0],coord[1],coord[2],c=value['color'],alpha=value['alpha'],
marker=nodemarker,s=nodesize)

# Loop on the list of edges to get the x,y,z, coordinates of the connected nodes
# Those two points are the extrema of the line to be plotted
for i,edge in enumerate(N.edges(data=True)):
x = np.array((pos[edge[0]][0], pos[edge[1]][0]))
y = np.array((pos[edge[0]][1], pos[edge[1]][1]))
z = np.array((pos[edge[0]][2], pos[edge[1]][2]))

# Plot the connecting lines
axs.plot(x,y,z,c=edge[2]['color'],alpha=edgealpha,linewidth=linewidth)

# Set the initial view
axs.view_init(30, angle)
58 changes: 58 additions & 0 deletions localgraphclustering/tests/test_algs.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,25 @@
from localgraphclustering import *
import time
import numpy as np
import networkx as nx
import random

def load_example_graph(vtype,itype):
return GraphLocal("localgraphclustering/tests/data/dolphins.edges",separator=" ",vtype=vtype,itype=itype)

def generate_random_3Dgraph(n_nodes, radius, seed=None):

if seed is not None:
random.seed(seed)

# Generate a dict of positions
pos = {i: (random.uniform(0, 1), random.uniform(0, 1), random.uniform(0, 1)) for i in range(n_nodes)}

# Create random 3D network
G = nx.random_geometric_graph(n_nodes, radius, pos=pos)

return G

def test_GraphLocal_methods():
g = load_example_graph(np.uint32,np.uint32)
g.largest_component()
Expand Down Expand Up @@ -33,6 +48,49 @@ def test_GraphLocal_methods():
# Test graph with more than one components
G = GraphLocal("notebooks/datasets/neuro-fmri-01.edges",file_type = "edgelist", separator = " ", header = True)

# Test drawing fuinctions
g = GraphLocal('notebooks/datasets/JohnsHopkins.graphml','graphml','\t')
ld_coord = np.loadtxt('notebooks/datasets/JohnHopkins_coord.xy', dtype = 'Float64')
idx = np.argsort(ld_coord[:,0])
coords = []
for i in range(g._num_vertices):
coords.append(ld_coord[idx[i],1:3])
coords = np.array(coords)
# Call the global spectral partitioning algorithm.
eig2 = fiedler(g)[0]

# Round the eigenvector
output_sc = sweep_cut(g,eig2)

# Extract the partition for g and store it.
eig2_rounded = output_sc[0]
ret_dict = g.draw(coords,edgealpha=0.01,nodealpha=0.5,nodeset=eig2_rounded)

N = ret_dict["nx_graph"]
# Change set nodes to blue
nx.set_node_attributes(N,{k:{'color':'b'} for k in ret_dict["setnodes"]})
GraphLocal.draw_nx(N,ret_dict['ax'])

# Change set edges to green
nx.set_edge_attributes(N,{k:{'color':'g'} for k in ret_dict["setedges"]})
GraphLocal.draw_nx(N,ret_dict['ax'])

N = generate_random_3Dgraph(n_nodes=200, radius=0.25, seed=1)
pos = list(nx.get_node_attributes(N,'pos').values())
G = GraphLocal()
G = G.from_networkx(N)
ret_dict = G.draw(pos,edgealpha=0.01,nodealpha=0.5,nodeset=range(100,150),groups=[range(50),range(50,100)])

N = ret_dict["nx_graph"]
# Change set nodes to blue
nx.set_node_attributes(N,{k:{'color':'b'} for k in ret_dict["setnodes"]})
GraphLocal.draw_nx_3d(N,ret_dict['ax'])

# Change set edges to green
nx.set_edge_attributes(N,{k:{'color':'g'} for k in ret_dict["setedges"]})
GraphLocal.draw_nx_3d(N,ret_dict['ax'])


def test_sweepcut_self_loop():
""" This is a regression test for sweep-cuts with self-loops """
g = GraphLocal()
Expand Down
2 changes: 1 addition & 1 deletion notebooks/examples_with_visualization.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -1410,7 +1410,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.4"
"version": "3.6.5"
}
},
"nbformat": 4,
Expand Down

0 comments on commit a1b669e

Please sign in to comment.