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.
Related
I am trying to create an interactive plot using netgraph and networkx.
I would like the plot to allow movement of the nodes and as the nodes move, the edges and edge_labels will also dynamically update.
Moving the nodes was addressed by the author of netgraph here . Now when I make a simpler plot and try to label an edge, the label stays static, and is sometimes not even on an edge.
It seems like handling edge_positions similar to node_positions on the last two lines should at least address the label not moving. Why the label isn't anchored to a particular edge is still puzzling. Does anyone know if the desired effect is achievable?
Here is a snip before moving anything:
Here is a snip after moving the bottom-right node to the top left:
Here is my current code:
import matplotlib.pyplot as plt
import networkx as nx
import netgraph # pip install netgraph
#Graph creation:
G=nx.Graph(type="")
for i in range(6):
G.add_node(i,shape="o")
#Changing shape for two nodes
G.nodes[1]['shape'] = "v"
G.nodes[5]['shape'] = "v"
#Add edges
G.add_edge(1,2)
G.add_edge(4,5)
G.add_edge(0,4)
G.add_edge(2,3)
G.add_edge(2,4)
labs={(1,2):"1 to 2"}
nx.draw_networkx_edge_labels(G, pos=nx.spring_layout(G),edge_labels=labs)
#Get node shapes
node_shapes = nx.get_node_attributes(G,"shape")
# Create an interactive plot.
# NOTE: you must retain a reference to the object instance!
# Otherwise the whole thing will be garbage collected after the initial draw
# and you won't be able to move the plot elements around.
pos = nx.layout.spring_layout(G)
######## drag nodes around #########
# To access the new node positions:
plot_instance = netgraph.InteractiveGraph(G, node_shape=node_shapes, node_positions=pos, edge_positions=pos)
node_positions = plot_instance.node_positions
edge_positions = plot_instance.edge_positions
To have it as formal answer, you need to add to the InteractiveGraph object the information that you want to draw (and move) the edge labels, i.e. the following
netgraph.InteractiveGraph(G,
node_shape=node_shapes,
node_positions=pos,
edge_positions=pos,
edge_labels=labs)
(With emphasis on the last parameter)
As you already, noticed then you don't need the call of nx.draw_networkx_edge_labels.
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
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.
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.
I am working with a regular network (grid) that I create as:
import networkx as nx
N=100
def graph_creating(N):
G=nx.grid_2d_graph(N,N)
pos = dict( (n, n) for n in G.nodes() ) #Dictionary of all positions
return G, pos
I have two ways of iterating the code. In both I remove nodes from the network and attempt to draw it. Depending on whether I create the network initially or within the loop, I get a different behavior when I plot it.
My problem: I want to plot the grid after stage 1 and after stage 2, so to make comparisons with the unaltered grid/graph. I fail to do it properly because:
If the original grid is created outside the for loop, I correctly get the first plots but later plots are empty as the graph is never restored back to its unaltered statu;
If the original grid is created inside the for loop, I always end up having the unaltered grid plotted, as if the removal had no effect on it.
Where else should the graph-creating block be placed, in order to be able to plot the graph right after stage 1 and 2?
version 1 the graph is created outside the for loop:
G, pos = graph_creating(N)
nodelist = G.nodes()
for counter in range(5):
G1 = nodelist[2*counter:2*counter+1]
G.remove_nodes_from(G1)
nx.draw_networkx(G, pos = pos)
figurename = 'file{0}.png'.format(counter)
plt.savefig(figurename)
G2=nodelist[2*counter+1:2*counter+2]
G.remove_nodes_from(G2)
nx.draw_networkx(G,pos=pos)
#it's not clear from your original question if you save this figure or not
Result: only the first iteration produces correct plots. Later plots are empty as the graph is never restored back to its unaltered status.
version 2 the graph is created inside the for loop:
for counter in range(5):
G, pos = graph_creating(N)
nodelist = G.nodes()
G1 = nodelist[2*counter:2*counter+1]
G.remove_nodes_from(G1)
nx.draw_networkx(G, pos = pos)
figurename = 'file{0}.png'.format(counter)
plt.savefig(figurename)
G2=nodelist[2*counter+1:2*counter+2]
G.remove_nodes_from(G2)
nx.draw_networkx(G,pos=pos)
#it's not clear from your original question if you save this figure or not
Result: the calls to nx.draw_networkx result in the unaltered graph being plotted for each iteration. I wonder if the problem is in the way I call that function, as it always plots the graph with no failed nodes. Why do I have this plotting problem?.
Do your graph creating block outside the for loop.
Inside the for-loop create a copy of the graph using tmp_G = G.copy()
when using draw_netwokx or remove_nodes_from use tmp_G instead of G
When you want to compare the altered graphs to the original one call the draw_networkx function using graph G