Python Igraph community cluster colors - python

I want to have clusters in different colours after community infomap, but problem is when I deleted single nodes it makes a mess an each node is different color or everything is red. How to do that in python?
Code:
E = ig.Graph(edges)
E.vs\['label'\] = labels
degree = 0
community = E.community_infomap()
cg = community.graph
singletons = cg.vs.select(_degree = 0)
cg.delete_vertices(singletons)
color_list =['red','blue','green','cyan','pink','orange','grey','yellow','white','black','purple' ]
ig.plot(cg)

It is not clear how did you try to assign colors to vertices. You should be aware that igraph reindexes vertices and edges upon deletion and addition of either vertices or edges. This reindexing should be considered unpredictable, the only things we know that indices go from 0 to n-1 at all time, and attributes remain assigned to the correct vertex or edge. Considering these, you can either do the deletion before or after the community detection, only you need to assign colors to a vertex attribute:
import igraph
g = igraph.Graph.Barabasi(n = 20, m = 1)
i = g.community_infomap()
pal = igraph.drawing.colors.ClusterColoringPalette(len(i))
g.vs['color'] = pal.get_many(i.membership)
igraph.plot(g)
Now let's see what happens if we delete a vertex:
colors_original = pal.get_many(i.membership)
g.delete_vertices([7])
# the clustering object is still the same length
# (so it is not valid any more, you can't be sure
# if the i.th vertex in the clustering is the
# i.th one in the graph)
len(i) # 20
# while the graph has less vertices
g.vcount() # 19
# if we plot this with the original colors, indeed we get a mess:
igraph.plot(g, vertex_color = colors_original)
But the colors in the g.vs['color'] vertex attribute are still correct, they show the clusters, only the deleted vertex is missing (from the dark blue cluster):
igraph.plot(g,
vertex_color = g.vs['color']) # this is automatic, I am
# writing here explicitely only to be clear

I found solution. First delete single nodes than convert to igraph and do community.

Related

How to display graph in Pyvis more clearly?

I want to visualize a graph in Pyvis which its nodes has labels. I am completely able to visualize it in Pyvis but my problem is about the ways of visualizing it. The graph displayed in Pyvis is not clear and edges are messed up. Is there any way to visualize the graph more clear?
The image below shows the graph.
For example in the graph, node 15 is displayed well. I want other nodes to be displayed in a clear way that the connections can be displayed more clearly
Update:
This is the code i use for drawing graph using Pyvis:
def showGraph(FileName, labelList):
Txtfile = open("./results.txt")
G = nx.read_weighted_edgelist(Txtfile)
Txtfile.close()
palette = (sns.color_palette("Pastel1", n_colors=len(set(labelList.values()))))
palette = palette.as_hex()
colorDict = {}
counter = 0
for i in palette:
colorDict[counter] = i
counter += 1
N = Network(height='100%', width='100%', directed=False, notebook=False)
for n in G.nodes:
N.add_node(n, color=(colorDict[labelList[n]]), size=5)
for e in G.edges.data():
N.add_edge(e[0], e[1], title=str(e[2]), value=e[2]['weight'])
N.show('result.html')
results.txt is my edge list file and labelList holds label of each node. Labels are numerical. For example label of node 48 is 5, it can be anything. I use labels to give different colors to nodes.
The NetworkX circular layouts tend to make individual nodes and the connections between them easier to see, so you could try that as long as you don't want nodes to move (without dragging) after you've drawn them.
Before creating your pyvis network, run the following on your NetworkX graph to create a dictionary that will be keyed by node and have (x, y) positions as values. You might need to mess around with the scale parameter a bit to see what works best for you.
pos = nx.circular_layout(G, scale = 1000)
You can then add x and y values from pos to your pyvis network when you add each node. Adding physics = False keeps the nodes in one place unless you click and drag them around.
for n in G.nodes:
N.add_node(n,
color=(colorDict[labelList[n]]),
size=5,
x = pos[n][0],
y = pos[n][1],
physics = False)
I'm not sure how the edge weights will play into things, so you should probably also add physics = False to the add_edge parameters to ensure that nothing will move.
Since I didn't have your original data, I just generated a random graph with 10 nodes and this was the result in pyvis.

Check if a random graph coloring is correct (Python)

So i'm trying a different approach to graph coloring, what i did basically is assign randomly colors to the nodes of a graph and what i want to do is, after assigning those colors, check if that coloring is correct (no adjacent nodes having the same color) in other words, going through the nodes and their respective colors and make sure no adjacent nodes have that same color.
Here is what i've done so far :
def approx_color(graph):
colors = [1,2,3,4,5,6,7,8,9]
xr = random.randint(0, len(graph.nodes))
s_c = []
for i in range(len(graph.nodes)):
s_c.append(random.choice(colors))
colored = dict(zip(graph.nodes,s_c))
print(colored)
EDIT :
The "graph" variable is a graph generated by networkx library, and graph.nodes() graph.edges() being a list of the nodes and edges of the graph
For the first part you can directly use random.choices()
def approx_color(graph):
colors = [1,2,3,4,5,6,7,8,9]
s_c = random.choices(colors, k=graph.order())
colored = dict(zip(graph.nodes(), s_c))
A graph coloring is correct if no two adjacent vertices are of the same color.
So you just have to iterate over the nodes and check their neighbors.
So you can just define a function that check for the condition to be valid.
for u in graph.nodes():
for v in graph.neighbors(u):
if colored[u] == colored[v]:
return False
return True

Python - Plot Node Hierarchy using iGraph

I have a dataset with staff and their job roles, and each job role is assigned a code: 0 for top-management, 1 for middle-management, and 2 for general staff. I now want to plot these roles using a hierarchical graph, so that all code 0 staff are on the top, 1 in the middle, and 2 at the bottom. I've found the layout in iGraph to do this (see below), however don't know how to control which nodes appear where. Is there a parameter that I'm missing to control this? Any help would be appreciated.
CSV:
https://github.com/Laurie-Bamber/Enron_Corpus/blob/master/15Below_60Employees_1.csv
Role Codes:
https://github.com/Laurie-Bamber/Enron_Corpus/blob/master/Dict_role_code.csv
GML:
https://github.com/Laurie-Bamber/Enron_Corpus/blob/master/15Below_60Employees_1.gml
P.S. edges refer to emails between staff, not measures of hierarchy.
Code:
G = Graph.Read_GML('Test.gml')
visual_style['layout'] = G.layout_reingold_tilford()
plot(G, **visual_style)
I am proposing a solution with a slight modification to what you asked for. If you plot the levels vertically and the people at a role level horizontally, there are many people at one level so the labels run into each other. Instead, I am plotting the role levels horizontally and the individuals at a level are spread out vertically, leaving plenty of room to see the labels.
I do not think that there is a pre-built layout function that does what you are asking. However, it is not very hard to make your own layout. The essential part of doing that is to assign x-y coordinates where you want the nodes to be plotted. After that, you can just use the Layout function to convert the coordinates into a layout object.
My scheme for assigning x-y coordinates will be that the x coordinate will be the role level ( 1,2, or 3). I will just assign y-coordinates by making each node at a role level one higher than the previous node at that level. I use a small dictionary to keep track of what height comes next for each of the levels.
I will use the file names of the files that you provided and will assume that these files are in the current working directory.
import csv
from igraph import *
## Load graph
G = Graph.Read_GML('15Below_60Employees_1.gml')
## Load role levels
reader = csv.reader(open('Dict_role_code.csv'))
dx = dict(reader)
## Create a layout
height = { '1':0, '2':0, '3':0 }
COORD = []
for L in G.vs['label']:
height[dx[L]] = height[dx[L]] + 1
COORD.append((float(dx[L]), height[dx[L]]))
LO = Layout(COORD)
## Create the style
visual_style = {}
visual_style['vertex_size'] = 8
visual_style['vertex_frame_color'] = 'orange'
visual_style['layout'] = LO
visual_style['margin'] = 60
visual_style['edge_color'] = '#00000044'
plot(G, **visual_style)
I think that this provides you with a good starting place. You can tweak the placement from here.

Is it possible to control the order that nodes are drawn using NetworkX in python?

I have a large graph object with many nodes that I am trying to graph. Due to the large number of nodes, many are being drawn one over another. This in itself is not a problem. However, a small percentage of nodes have node attributes which dictate their colour.
Ideally I would be able to draw the graph in such a way that nodes with this property are drawn last, on top of the other nodes, so that it is possible to see their distribution across the graph.
The code I have so far used to generate the graph is shown below:
import networkx as nx
import matplotlib.pyplot as plt
import numpy as np
import os
import pickle
from pathlib import Path
def openFileAtPath(filePath):
print('Opening file at: ' + filePath)
with open(filePath, 'rb') as input:
file = pickle.load(input)
return file
# Pre manipulation path
g = openFileAtPath('../initialGraphs/wordNetadj_dictionary1.11.pkl')
# Post manipulation path
# g = openFileAtPath('../manipulatedGraphs/wordNetadj_dictionary1.11.pkl')
print('Fetching SO scores')
scores = list()
for node in g.nodes:
scores.append(g.node[node]['weight'])
print('Drawing network')
nx.draw(g,
with_labels=False,
cmap=plt.get_cmap('RdBu'),
node_color=scores,
node_size=40,
font_size=8)
plt.show()
And currently the output is as shown:
This graph object itself has taken a relatively long time to generate and is computationally intensive, so ideally I wouldn't have to remake the graph from scratch.
However, I am fairly sure that the graph is drawn in the same order that the nodes were added to the graph object. I have searched for a way of changing the order that the nodes are stored within the graph object, but given directional graphs actually have an order, my searches always end up with answers showing me how to reverse the direction of a graph.
So, is there a way to dictate the order in which nodes are drawn, or alternatively, change the order that nodes are stored inside some graph object.
Potentially worthy of a second question, but the edges are also blocked out by the large number of nodes. Is there a way to draw the edges above the nodes behind them?
Piggybacking off Paul Brodersen's answer, if you want different nodes to be in the foreground and background, I think you should do the following:
For all nodes that belong in the same layer, draw the subgraph corresponding to the nodes, and set the , as follows:
pos = {...} # some dictionary of node positions, required for the function below
H = G.subgraph(nbunch)
collection = nx.draw_networkx_nodes(H, pos)
collection.set_zorder(zorder)
Do this for every group of nodes that belong in the same level. It's tedious, but it will do the trick. Here is a toy example that I created based on looking up this question as part of my own research
import matplotlib as mpl
mpl.use('agg')
import pylab
import networkx as nx
G = nx.Graph()
G.add_path([1, 2, 3, 4])
pos = {1 : (0, 0), 2 : (0.5, 0), 3 : (1, 0), 4 : (1.5, 0)}
for node in G.nodes():
H = G.subgraph([node])
collection = nx.draw_networkx_nodes(H, pos)
collection.set_zorder(node)
pylab.plot([0, 2], [0, 0], zorder=2.5)
pylab.savefig('nodes_zorder.pdf', format='pdf')
pylab.close()
This makes a graph, and then puts the each node at a successively higher level going from left to right, so the leftmost node is farthest in the background and the rightmost node is farthest in the foreground. It then draws a straight line whose zorder is 2. As a result, it comes in front of the two left nodes, and behind the two right nodes. Here is the result.
draw is a wrapper around draw_networkx_nodes and draw_networkx_edges.
Unlike draw, the two functions return their respective artists ( PathCollection and LineCollection, IIRC). These are your standard matplotlib artists, and as as such their relative draw order can be controlled via their zorder attribute.

Maya API Python symmetry table with MRichSelection?

I was wondering if there is a way to access the symmetry table of the MRichSelection having as a result the positive, the seam and the negative side with the positive and the negative ordered by vertex id correspondence. ie: vertex id 15 is the symmetry correlated to vert id 350. They are both at index 5 in the positive and negative list.
I know I can achieve something similar using the filterXpand, but I believe the lists are not ordered in the way I can access the opposite vertex.
I don't know if you ever found a solution to this, but I will post mine for future TD's looking for a solution.
So let's assume you want to get the corresponding verts between left and right on the YZ plane. you have 2 different options. Using the MRichSelection to handle you symmetry table. Or calculate the vert yourself, by getting the smallest distance vector on the opposite side. Note: if you use the MRichSelection method, you will need to make sure that symmetry mode is enbaled in the viewport.
I will show both answers, so lets get started:
Also note: I will be calculating the YZ Plane, as mentioned earlier. So adjust to your liking if needed.
Solution 1(Calculating yourself):
#importing the OpenMaya Module
from maya.api import OpenMaya as om
#converting selected object into MObject and MFnMesh functionset
mSel=om.MSelectionList()
mSel.add(cmds.ls(sl=1)[0])
mObj=mSel.getDagPath(0)
mfnMesh=om.MFnMesh(mObj)
#getting our basePoints
baseShape = mfnMesh.getPoints()
#this function can be used to revert the object back to the baseShape
mfnMesh.setPoints(baseShape)
#getting l and r verts
mtol=0.02# this will be our mid tolerance, if the mesh is not completely symmetric on the mid
lVerts=[]#for storing left Verts
rVerts=[]#for storing right Verts
mVerts=[]#for storing mid Verts
corrVerts={} #for storing correspondign verts
for i in range(mfnMesh.numVertices): #iteratign through all the verts on the mesh
thisPoint = mfnMesh.getPoint(i) #getting current point position
if thisPoint.x>0+mtol: # if pointValue on x axis is bigger than 0+midTolerance
lVerts.append((i, thisPoint))#append to left vert storage list(i = vert index, thisPoint = vert Mpoint position)
elif thisPoint.x<0-mtol: #opposite of left vert calculation
rVerts.append((i, thisPoint))
else: #if none of the above, assign to mid verts
mVerts.append((i, thisPoint))
rVertspoints=[i for v,i in rVerts] #getting the vert mPoint positions of the right side
for vert, mp in lVerts: #going through our left points, unpacking our vert index and mPoint position()
nmp=om.MPoint(-mp.x, mp.y, mp.z) #storing the reversed mpoint of the left side vert
rp = mfnMesh.getClosestPoint(nmp)#getting the closest point on the mesh
if rp[0] in rVertspoints: #cheking if the point is in the right side
corrVerts[vert] = rVerts[rVertspoints.index(rp[0])][0] #adding it if it is true
else:#if it is not, calculate closest vert
#iterating through rVertspoints and find smallest distance
dList=[nmp.distanceTo(rVert) for rVert in rVertspoints]#distance list for each vert based on input point
mindist = min(dList)#getting the closest distance
corrVerts[vert] = rVerts[dList.index(mindist)][0]#adding the vert
#now the corrVerts will have stored the corresponding vertices from left to right
Solution 2(using MRichSelection):
#MAKE SURE SYMMETRY IN THE VIEWPORT IS TURNED ON TO WORK! (will also work with topological symmetry)
#importing the OpenMaya Module
from maya.api import OpenMaya as om
#converting selected object into MObject and MFnMesh functionset
mSel=om.MSelectionList()
mSel.add(cmds.ls(sl=1)[0])
mObj=mSel.getDagPath(0)
mfnMesh=om.MFnMesh(mObj)
#getting our basePoints
baseShape = mfnMesh.getPoints()
#this function can be used to revert the object back to the baseShape
mfnMesh.setPoints(baseShape)
#getting l and r verts
mtol=0.02# this will be our mid tolerance, if the mesh is not completely symmetric on the mid
lVerts=[]#for storing left Verts
corrVerts={} #for storing correspondign verts
for i in range(mfnMesh.numVertices): #iteratign through all the verts on the mesh
thisPoint = mfnMesh.getPoint(i) #getting current point position
if thisPoint.x>0+mtol: # if pointValue on x axis is bigger than 0+midTolerance
lVerts.append((i, thisPoint))#append to left vert storage list(i = vert index, thisPoint = vert Mpoint position)
#selecting our verts with symmetry on
SymSelection = cmds.select(["%s.vtx[%s]"%(mObj,i) for i,v in lVerts], sym=True)
#getting the rich selection. it will store the symmetry iformation for us
mRichBase = om.MGlobal.getRichSelection()
lCor = mRichBase.getSelection()#this will store our lSide verts as an MSelectionList
rCor = mRichBase.getSymmetry()#this will symmetry verts as an MSelectionList
mitL = om.MItSelectionList(lCor)#creating iterative lists so we can get the components
mitR = om.MItSelectionList(rCor)
while not mitL.isDone():#iterating through the left list
mitLComp = mitL.getComponent()#getting dag path and components of leftside
mitRComp = mitR.getComponent()#getting dag path and components of rightside
mitLCorVert = om.MItMeshVertex(mitLComp[0], mitLComp[1]) #creating our iterative vertex lists
mitRCorVert = om.MItMeshVertex(mitRComp[0], mitRComp[1])
while not mitLCorVert.isDone():#iterating through our verts
corrVerts[mitLCorVert.index()] = mitRCorVert.index()#adding corresponding verts to our dictionary
mitLCorVert.next()#go to next vert. needed to stop loop
mitRCorVert.next()#go to next vert. needed to stop loop
mitL.next()#go to next selection in list if more. needed to stop loop
mitR.next()#go to next selection in list if more. needed to stop loop
cmds.select(cl=1)#deseleting our verts
#now the corrVerts will have stored the corresponding vertices from left to right
Hope it will help you all, looking for a few solutions.
Cheers
Bjarke Rauff, Rigging TD.
The answer by #Bjarke Rauff was very helpful, wanted to add a note about speed.
MFnMesh.getClosestPoint() builds an octree to efficiently find the point, but it will do that on every call. A mesh with 100k points can take up to 45s to process.
Use a MMeshIntersector() to cache the data between lookups. This speeds up the table creation by 900x for 100k points to .05s.
mesh # MDagpath obj to poly
flip_matrix # MTransformMatrix to flop the point
itMesh = om.MItMeshPolygon(mesh)
mesh.extendToShape()
matrix = mesh.inclusiveMatrix()
node = mesh.node()
intersector = om.MMeshIntersector()
intersector.create(node, matrix)
if not (intersector.isCreated):
print("Failed to create mesh intersector")
return
flipped_ids={}
while not itMesh.isDone():
id = itMesh.index()
face_center = itMesh.center()
# flop the point across the axis
flipped_point = face_center*flip_matrix
MpointOnMesh = intersector.getClosestPoint(flipped_point)
if MpointOnMesh is not None:
# get face id property from MPointOnMesh
flipped_id = MpointOnMesh.face
flipped_ids[id] = flipped_id
else:
print("No intersection")
itMesh.next()
NOTE
I tried hash tables with a tuple of the point as the key, but the point positions had slight variations, even with rounding, which created different hashes.
I've tested the MRichSelection approach and it doesn't actually work consistently in practice. It seems like it works when you have a perfectly mirrored mesh, but that can't be assumed. The component lists are not necessarily in sync.

Categories