From a63c2d23540079530d984a1a9efa0b67e4c0fd9d Mon Sep 17 00:00:00 2001 From: Meng Liu Date: Mon, 26 Nov 2018 00:39:41 -0500 Subject: [PATCH 1/4] first version of standard drawing method --- localgraphclustering/GraphLocal.py | 192 +++++++++++++++++++- localgraphclustering/tests/test_algs.py | 57 ++++++ notebooks/examples_with_visualization.ipynb | 2 +- 3 files changed, 248 insertions(+), 3 deletions(-) diff --git a/localgraphclustering/GraphLocal.py b/localgraphclustering/GraphLocal.py index 9e50d77..25992dc 100755 --- a/localgraphclustering/GraphLocal.py +++ b/localgraphclustering/GraphLocal.py @@ -558,8 +558,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 @@ -616,3 +614,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 != 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 != 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) diff --git a/localgraphclustering/tests/test_algs.py b/localgraphclustering/tests/test_algs.py index ed90547..3b43a2f 100644 --- a/localgraphclustering/tests/test_algs.py +++ b/localgraphclustering/tests/test_algs.py @@ -1,10 +1,24 @@ from localgraphclustering import * import time import numpy as np +import networkx as nx 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() @@ -33,6 +47,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=n, 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() diff --git a/notebooks/examples_with_visualization.ipynb b/notebooks/examples_with_visualization.ipynb index 4632e41..9a1926b 100644 --- a/notebooks/examples_with_visualization.ipynb +++ b/notebooks/examples_with_visualization.ipynb @@ -1410,7 +1410,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.4" + "version": "3.6.5" } }, "nbformat": 4, From 46db2739fd8051edc32d2d997646e2dc14993892 Mon Sep 17 00:00:00 2001 From: Meng Liu Date: Mon, 26 Nov 2018 00:56:50 -0500 Subject: [PATCH 2/4] first version of standard drawing method --- localgraphclustering/GraphLocal.py | 8 ++++++-- localgraphclustering/tests/test_algs.py | 3 ++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/localgraphclustering/GraphLocal.py b/localgraphclustering/GraphLocal.py index 25992dc..7c34a51 100755 --- a/localgraphclustering/GraphLocal.py +++ b/localgraphclustering/GraphLocal.py @@ -8,6 +8,7 @@ import warnings import collections as cole from .cpp import * +import random import gzip import bz2 @@ -15,6 +16,9 @@ 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) @@ -679,7 +683,7 @@ def draw(self,coords,alpha=1.0,nodesize=5,linewidth=1, else: axs = fig.add_subplot(111) axs.set_axis_off() - nodeset = set(nodeset) if nodeset != None else set() + nodeset = set(nodeset) if nodeset is not None else set() nodelist_in = [] nodelist_out = [] for i in range(self._num_vertices): @@ -702,7 +706,7 @@ def draw(self,coords,alpha=1.0,nodesize=5,linewidth=1, #reassign node colors based on partition groupedges = None - if groups != 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)]) diff --git a/localgraphclustering/tests/test_algs.py b/localgraphclustering/tests/test_algs.py index 3b43a2f..34f322a 100644 --- a/localgraphclustering/tests/test_algs.py +++ b/localgraphclustering/tests/test_algs.py @@ -2,6 +2,7 @@ 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) @@ -74,7 +75,7 @@ def test_GraphLocal_methods(): 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=n, radius=0.25, seed=1) + 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) From 225edb6b5a42135fc09127b8a1ef671e7f655281 Mon Sep 17 00:00:00 2001 From: Meng Liu Date: Mon, 26 Nov 2018 17:47:40 -0500 Subject: [PATCH 3/4] new drawing template without networkx --- localgraphclustering/GraphLocal.py | 174 +++++++++--------------- localgraphclustering/tests/test_algs.py | 18 --- 2 files changed, 63 insertions(+), 129 deletions(-) diff --git a/localgraphclustering/GraphLocal.py b/localgraphclustering/GraphLocal.py index 7c34a51..d5fbdf4 100755 --- a/localgraphclustering/GraphLocal.py +++ b/localgraphclustering/GraphLocal.py @@ -18,6 +18,9 @@ import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D +from matplotlib.collections import LineCollection +from mpl_toolkits.mplot3d.art3d import Line3DCollection +from matplotlib.colors import to_rgb,to_rgba def _load_from_shared(sabuf, dtype, shape): return np.frombuffer(sabuf, dtype=dtype).reshape(shape) @@ -620,10 +623,10 @@ def local_extrema(self,vals,strict=False,reverse=False): 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): + 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 @@ -672,11 +675,9 @@ def draw(self,coords,alpha=1.0,nodesize=5,linewidth=1, ------- 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. + fig, ax, setnodes, groupnodes """ - if axs == None: + if axs is None: fig = plt.figure() if len(coords[0]) == 3: axs = fig.add_subplot(111, projection='3d') @@ -691,120 +692,71 @@ def draw(self,coords,alpha=1.0,nodesize=5,linewidth=1, 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])) - + #store color information for each node + node_color_list = np.empty((self._num_vertices,4)) + node_color_list[nodelist_out] = to_rgba(nodecolor,alpha*nodealpha) + node_color_list[nodelist_in] = to_rgba(setcolor,alpha*setalpha) #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])) - + for k in g: + node_color_list[k] = to_rgba(color[i],alpha*nodealpha) if len(coords[0]) == 3: - self.draw_nx_3d(N,axs,nodemarker,nodesize,alpha*edgealpha,linewidth) + self.draw_3d(coords,axs,nodemarker=nodemarker,nodesize=nodesize,edgealpha=alpha*edgealpha, + linewidth=linewidth,node_color_list=node_color_list) else: - self.draw_nx(N,axs,nodemarker,nodesize,alpha*edgealpha,linewidth) + self.draw_2d(coords,axs,nodemarker=nodemarker,nodesize=nodesize,edgealpha=alpha*edgealpha, + linewidth=linewidth,node_color_list=node_color_list) - 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} + ret_dict = {"fig":fig,"ax":axs,"setnodes":nodelist_in,"groupnodes":groups} 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) - + def draw_star(center,points,pos,edge_pos): + for i,p in enumerate(points): + if p >= center: + edge_pos.append([pos[center],pos[p]]) + + def draw_2d(self,pos,axs,nodemarker='o',nodesize=5,edgealpha=0.01,linewidth=1, + node_color_list=None,edgecolor='k',nodecolor='r',node_list=None): + if node_color_list is not None: + node_collection = axs.scatter([p[0] for p in pos],[p[1] for p in pos],c=node_color_list,s=nodesize,marker=nodemarker) + else: + node_collection = axs.scatter([p[0] for p in pos],[p[1] for p in pos],c=nodecolor,s=nodesize,marker=nodemarker) + #make sure nodes are on the top + node_collection.set_zorder(2) + node_list = range(self._num_vertices) if node_list is None else node_list + edge_pos = [] + for i in node_list: + self.draw_star(i,self.aj[self.ai[i]:self.ai[i+1]],pos,edge_pos) + edge_pos = np.asarray(edge_pos) + edge_collection = LineCollection(edge_pos,colors=to_rgba(edgecolor,edgealpha),linewidths=linewidth) + #make sure edges are at the bottom + edge_collection.set_zorder(1) + axs.add_collection(edge_collection) + axs.autoscale() + + def draw_3d(self,pos,axs,nodemarker='o',nodesize=5,edgealpha=0.01,linewidth=1, + node_color_list=None,angle=30,edgecolor='k',nodecolor='r',node_list=None): + if node_color_list is not None: + node_collection = axs.scatter([p[0] for p in pos],[p[1] for p in pos],[p[2] for p in pos],c=node_color_list, + s=nodesize,marker=nodemarker,zorder=2) + else: + node_collection = axs.scatter([p[0] for p in pos],[p[1] for p in pos],[p[2] for p in pos],c=nodecolor, + s=nodesize,marker=nodemarker,zorder=2) + #make sure nodes are on the top + node_list = range(self._num_vertices) if node_list is None else node_list + edge_pos = [] + for i in node_list: + self.draw_star(i,self.aj[self.ai[i]:self.ai[i+1]],pos,edge_pos) + edge_pos = np.asarray(edge_pos) + edge_collection = Line3DCollection(edge_pos,colors=to_rgba(edgecolor,edgealpha),linewidths=linewidth) + #make sure edges are at the bottom + edge_collection.set_zorder(1) + axs.add_collection(edge_collection) + axs.autoscale() # Set the initial view axs.view_init(30, angle) diff --git a/localgraphclustering/tests/test_algs.py b/localgraphclustering/tests/test_algs.py index 34f322a..456e090 100644 --- a/localgraphclustering/tests/test_algs.py +++ b/localgraphclustering/tests/test_algs.py @@ -66,30 +66,12 @@ def test_GraphLocal_methods(): 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 """ From 3dd9e69512e9594c77486005d3ba08f7e7b736f7 Mon Sep 17 00:00:00 2001 From: Meng Liu Date: Tue, 27 Nov 2018 00:46:50 -0500 Subject: [PATCH 4/4] more improvement on the template --- localgraphclustering/GraphLocal.py | 107 ++++++++++++++++-------- localgraphclustering/tests/test_algs.py | 5 +- 2 files changed, 77 insertions(+), 35 deletions(-) diff --git a/localgraphclustering/GraphLocal.py b/localgraphclustering/GraphLocal.py index d5fbdf4..0a65b98 100755 --- a/localgraphclustering/GraphLocal.py +++ b/localgraphclustering/GraphLocal.py @@ -21,6 +21,8 @@ from matplotlib.collections import LineCollection from mpl_toolkits.mplot3d.art3d import Line3DCollection from matplotlib.colors import to_rgb,to_rgba +from matplotlib.colors import LinearSegmentedColormap +from matplotlib.colors import Normalize def _load_from_shared(sabuf, dtype, shape): return np.frombuffer(sabuf, dtype=dtype).reshape(shape) @@ -626,7 +628,7 @@ 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): + groups=None,values=None,cm="Reds"): """ standard drawing function of GraphLocal object @@ -671,6 +673,12 @@ def draw(self,coords,alpha=1.0,nodesize=5,linewidth=1, axs,fig: None,None (default) by default it will create a new figure, or this will plot in axs if not None. + values: Sequence[float] (None by default) + used to determine node colors in a colormap, should have the same length as coords + + cm: string ("Reds" by default) + colormap + Returns ------- @@ -692,24 +700,43 @@ def draw(self,coords,alpha=1.0,nodesize=5,linewidth=1, nodelist_in.append(i) else: nodelist_out.append(i) - #store color information for each node - node_color_list = np.empty((self._num_vertices,4)) - node_color_list[nodelist_out] = to_rgba(nodecolor,alpha*nodealpha) - node_color_list[nodelist_in] = to_rgba(setcolor,alpha*setalpha) - #reassign node colors based on partition - if groups is not None: - 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): - for k in g: - node_color_list[k] = to_rgba(color[i],alpha*nodealpha) + + if values is not None: + #when values are provided, use values and predefined colormap to determine colors + cm = plt.get_cmap(cm) + node_color_list = np.reshape(np.array(values),len(coords)) + vmin = min(values) + vmax = max(values) + else: + #when values are not provided, use customed colormap to determine colors + colors = [to_rgba(nodecolor,alpha*nodealpha),to_rgba(setcolor,alpha*setalpha)] + if groups is not None: + colorset = set([to_rgb(c) for c in colors]) + number_of_colors = len(groups) + for i in range(number_of_colors): + new_color = to_rgb("#"+''.join([random.choice('0123456789ABCDEF') for j in range(6)])) + #make sure color hasn't already existed + while new_color in colorset: + new_color = to_rgb("#"+''.join([random.choice('0123456789ABCDEF') for j in range(6)])) + colorset.add(new_color) + colors.append(to_rgba(new_color,alpha*nodealpha)) + cm = LinearSegmentedColormap.from_list('my_cmap',colors,N=256,gamma=1.0) + node_color_list = np.zeros(self._num_vertices) + node_color_list[nodelist_in] = 1.0/(len(colors)-1) + if groups is not None: + for i,g in enumerate(groups): + node_color_list[g] = (2+i)*1.0/(len(colors)-1) + vmin = 0.0 + vmax = 1.0 + if len(coords[0]) == 3: - self.draw_3d(coords,axs,nodemarker=nodemarker,nodesize=nodesize,edgealpha=alpha*edgealpha, - linewidth=linewidth,node_color_list=node_color_list) + self.draw_3d(coords,axs,cm,nodemarker=nodemarker,nodesize=nodesize,edgealpha=alpha*edgealpha, + linewidth=linewidth,node_color_list=node_color_list,vmin=vmin,vmax=vmax,nodelist_in=nodelist_in, + nodelist_out=nodelist_out,setalpha=alpha*setalpha,nodealpha=alpha*nodealpha,use_values=(values is not None)) else: - self.draw_2d(coords,axs,nodemarker=nodemarker,nodesize=nodesize,edgealpha=alpha*edgealpha, - linewidth=linewidth,node_color_list=node_color_list) + self.draw_2d(coords,axs,cm,nodemarker=nodemarker,nodesize=nodesize,edgealpha=alpha*edgealpha, + linewidth=linewidth,node_color_list=node_color_list,vmin=vmin,vmax=vmax,nodelist_in=nodelist_in, + nodelist_out=nodelist_out,setalpha=alpha*setalpha,nodealpha=alpha*nodealpha,use_values=(values is not None)) ret_dict = {"fig":fig,"ax":axs,"setnodes":nodelist_in,"groupnodes":groups} return ret_dict @@ -719,15 +746,21 @@ def draw_star(center,points,pos,edge_pos): for i,p in enumerate(points): if p >= center: edge_pos.append([pos[center],pos[p]]) - - def draw_2d(self,pos,axs,nodemarker='o',nodesize=5,edgealpha=0.01,linewidth=1, - node_color_list=None,edgecolor='k',nodecolor='r',node_list=None): - if node_color_list is not None: - node_collection = axs.scatter([p[0] for p in pos],[p[1] for p in pos],c=node_color_list,s=nodesize,marker=nodemarker) + + def draw_2d(self,pos,axs,cm,nodemarker='o',nodesize=5,edgealpha=0.01,linewidth=1, + node_color_list=None,edgecolor='k',nodecolor='r',node_list=None,nodelist_in=None, + nodelist_out=None,setalpha=1.0,nodealpha=1.0,use_values=False,vmin=0.0,vmax=1.0): + if use_values: + axs.scatter([p[0] for p in pos[nodelist_in]],[p[1] for p in pos[nodelist_in]],c=node_color_list[nodelist_in], + s=nodesize,marker=nodemarker,cmap=cm,norm=Normalize(vmin=vmin,vmax=vmax),alpha=setalpha,zorder=2) + axs.scatter([p[0] for p in pos[nodelist_out]],[p[1] for p in pos[nodelist_out]],c=node_color_list[nodelist_out], + s=nodesize,marker=nodemarker,cmap=cm,norm=Normalize(vmin=vmin,vmax=vmax),alpha=nodealpha,zorder=2) else: - node_collection = axs.scatter([p[0] for p in pos],[p[1] for p in pos],c=nodecolor,s=nodesize,marker=nodemarker) - #make sure nodes are on the top - node_collection.set_zorder(2) + if node_color_list is not None: + axs.scatter([p[0] for p in pos],[p[1] for p in pos],c=node_color_list,s=nodesize,marker=nodemarker, + cmap=cm,norm=Normalize(vmin=vmin,vmax=vmax),zorder=2) + else: + axs.scatter([p[0] for p in pos],[p[1] for p in pos],c=nodecolor,s=nodesize,marker=nodemarker,zorder=2) node_list = range(self._num_vertices) if node_list is None else node_list edge_pos = [] for i in node_list: @@ -738,16 +771,22 @@ def draw_2d(self,pos,axs,nodemarker='o',nodesize=5,edgealpha=0.01,linewidth=1, edge_collection.set_zorder(1) axs.add_collection(edge_collection) axs.autoscale() - - def draw_3d(self,pos,axs,nodemarker='o',nodesize=5,edgealpha=0.01,linewidth=1, - node_color_list=None,angle=30,edgecolor='k',nodecolor='r',node_list=None): - if node_color_list is not None: - node_collection = axs.scatter([p[0] for p in pos],[p[1] for p in pos],[p[2] for p in pos],c=node_color_list, - s=nodesize,marker=nodemarker,zorder=2) + + def draw_3d(self,pos,axs,cm,nodemarker='o',nodesize=5,edgealpha=0.01,linewidth=1, + node_color_list=None,angle=30,edgecolor='k',nodecolor='r',node_list=None, + nodelist_in=None,nodelist_out=None,setalpha=1.0,nodealpha=1.0,use_values=False,vmin=0.0,vmax=1.0): + if use_values: + axs.scatter([p[0] for p in pos[nodelist_in]],[p[1] for p in pos[nodelist_in]],[p[2] for p in pos[nodelist_in]],c=node_color_list[nodelist_in], + s=nodesize,marker=nodemarker,cmap=cm,norm=Normalize(vmin=vmin,vmax=vmax),zorder=2,alpha=setalpha) + axs.scatter([p[0] for p in pos[nodelist_out]],[p[1] for p in pos[nodelist_out]],[p[2] for p in pos[nodelist_out]],c=node_color_list[nodelist_out], + s=nodesize,marker=nodemarker,cmap=cm,norm=Normalize(vmin=vmin,vmax=vmax),zorder=2,alpha=nodealpha) else: - node_collection = axs.scatter([p[0] for p in pos],[p[1] for p in pos],[p[2] for p in pos],c=nodecolor, - s=nodesize,marker=nodemarker,zorder=2) - #make sure nodes are on the top + if node_color_list is not None: + axs.scatter([p[0] for p in pos],[p[1] for p in pos],[p[2] for p in pos],c=node_color_list, + s=nodesize,marker=nodemarker,cmap=cm,norm=Normalize(vmin=vmin,vmax=vmax),zorder=2) + else: + axs.scatter([p[0] for p in pos],[p[1] for p in pos],[p[2] for p in pos],c=nodecolor, + s=nodesize,marker=nodemarker,zorder=2) node_list = range(self._num_vertices) if node_list is None else node_list edge_pos = [] for i in node_list: diff --git a/localgraphclustering/tests/test_algs.py b/localgraphclustering/tests/test_algs.py index 456e090..fff4253 100644 --- a/localgraphclustering/tests/test_algs.py +++ b/localgraphclustering/tests/test_algs.py @@ -64,12 +64,15 @@ def test_GraphLocal_methods(): # 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,values=eig2) ret_dict = g.draw(coords,edgealpha=0.01,nodealpha=0.5,nodeset=eig2_rounded) N = generate_random_3Dgraph(n_nodes=200, radius=0.25, seed=1) - pos = list(nx.get_node_attributes(N,'pos').values()) + pos = np.array(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)], + values=[random.uniform(0, 1) for i in range(200)]) ret_dict = G.draw(pos,edgealpha=0.01,nodealpha=0.5,nodeset=range(100,150),groups=[range(50),range(50,100)])