Colouring edges by weight in networkx - python

I have only found something similar to what I want here:
Coloring networkx edges based on weight
However I can't seem to apply this to my problem. I have a graph with weighted edges, but the weights aren't unique (so there are like 15 edges with weight 1). I want to colour my edges based on the weight they have, the lower the weight the lighter the colour.
I tried to apply the method suggested in the above question, but from what I understand this requires the weights to be unique on each edge?
So far I've produced a list in ascending order of the different edge weights and wanted to use this to classify the possible edge colours. I'm trying to avoid drawing the edges by weight as I may need to draw a very large graph in the future with a huge range of weights on the edges.
If it's unclear let me know in comments and I'll give more specific info.
Thanks!
EDIT:
def draw_graph(target):
nlist = [target]+G.neighbors(target)
H=nx.subgraph(G, nlist)
n=H.number_of_edges()
colours = range(n)
labels,weights = colour_and_label_edges(H)
pos = nx.spring_layout(H)
nx.draw(H, pos, node_color='#A0CBE2',edge_color=colours, node_size=100, edge_cmap=plt.cm.Blues, width=0.5, with_labels=False)
nx.draw_networkx_edge_labels(H, pos, edge_labels=labels)
plt.savefig("Graphs/edge_colormap_%s.png" % target) # save as png
plt.show() # display
pass
def colour_and_label_edges(graph):
d={}
for (u,v) in graph.edges():
d[u,v]=graph[u][v]['weight']
temp=[]
for val in d.values():
if val not in temp:
temp.append(val)
weights = sorted(temp,key=int)
return d, weights
The above code is incomplete, but the idea is the function gives me a list of the weights, as so:
[1, 2, 3, 4, 5, 6, 9, 10, 16, 21, 47, 89, 124, 134, 224]
I then want to use this list to assign each weight a colour, the higher the weight the darker the colour. (I've used a very small subgraph for this example relative to the data set). Hope that clears it up a little :S

You can use the edge weights and a colormap to draw them. You might want t a different colormap from the one below.
import matplotlib.pyplot as plt
import networkx as nx
import random
G = nx.gnp_random_graph(10,0.3)
for u,v,d in G.edges(data=True):
d['weight'] = random.random()
edges,weights = zip(*nx.get_edge_attributes(G,'weight').items())
pos = nx.spring_layout(G)
nx.draw(G, pos, node_color='b', edgelist=edges, edge_color=weights, width=10.0, edge_cmap=plt.cm.Blues)
plt.savefig('edges.png')

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.

Generate a directed Graph using Python Library any python library

I am implementing Bellman ford's algorithm from GeeksForGeeks in Python. I want to generate the Graph(The Diagramatic form and not in dictionary type-which is easy) using some library like pyplot or networkx or something similar. I want the graph UI to contain the nodes,edges and the respective cost.
from collections import defaultdict
#Class to represent a graph
class Graph:
def __init__(self,vertices):
self.V= vertices #No. of vertices
self.graph = [] # default dictionary to store graph
# function to add an edge to graph
def addEdge(self,u,v,w):
self.graph.append([u, v, w])
# utility function used to print the solution
def printArr(self, dist):
print("Vertex Distance from Source")
for i in range(self.V):
print("%d \t\t %d" % (i, dist[i]))
# The main function that finds shortest distances from src to
# all other vertices using Bellman-Ford algorithm. The function
# also detects negative weight cycle
def BellmanFord(self, src):
# Step 1: Initialize distances from src to all other vertices
# as INFINITE
dist = [float("Inf")] * self.V
dist[src] = 0
# Step 2: Relax all edges |V| - 1 times. A simple shortest
# path from src to any other vertex can have at-most |V| - 1
# edges
for i in range(self.V - 1):
# Update dist value and parent index of the adjacent vertices of
# the picked vertex. Consider only those vertices which are still in
# queue
for u, v, w in self.graph:
if dist[u] != float("Inf") and dist[u] + w < dist[v]:
dist[v] = dist[u] + w
# Step 3: check for negative-weight cycles. The above step
# guarantees shortest distances if graph doesn't contain
# negative weight cycle. If we get a shorter path, then there
# is a cycle.
for u, v, w in self.graph:
if dist[u] != float("Inf") and dist[u] + w < dist[v]:
print "Graph contains negative weight cycle"
return
# print all distance
self.printArr(dist)
g = Graph(5)
g.addEdge(0, 1, -1)
g.addEdge(0, 2, 4)
g.addEdge(1, 2, 3)
g.addEdge(1, 3, 2)
g.addEdge(1, 4, 2)
g.addEdge(3, 2, 5)
g.addEdge(3, 1, 1)
g.addEdge(4, 3, -3)
The graph that I want either in terminal or in separate file is(based on above code):
The link of documentation by ekiim is highly useful. This is the code that I did for plotting graph:
import networkx as nx
import matplotlib.pyplot as plt
G=nx.DiGraph()
G.add_node(0),G.add_node(1),G.add_node(2),G.add_node(3),G.add_node(4)
G.add_edge(0, 1),G.add_edge(1, 2),G.add_edge(0, 2),G.add_edge(1, 4),G.add_edge(1, 3),G.add_edge(3, 2),G.add_edge(3,1),G.add_edge(4,3)
nx.draw(G, with_labels=True, font_weight='bold')
plt.show()
This code prints the directed graph without cost. I tried printing with cost but the output was highly distorted with costs jumbled up. Some costs were written in blank spaces while only one or two were present on the edges. Hence if someone knows to implement that it would be highly useful.
If you check this tutorial for networkx, you'll see how easy, is to create a directed graph, plus, plotting it.
Pretty much, It's the same for a directed or simple graph, (API wise), and the plotting, is also simple enough and uses Matplotlib to generate it.
You could make a Tk app, that allows you to input, manually the nodes, and Edges, and store them in ListBoxes, and plot a graph, in function of that, this won't be drag and drop, but still, it helps you visualize the graph on the fly.
and this Matplotlib tutorial, will give you the idea how to embed it in a TK app.

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.

partition graph into sungraphs based on node's attribute NetworkX

I'm using Networkx to compute some measures of a graph such as diameter, clustering coefficient, etc. It's straight forward how to do this for graph as a whole. What I'm interested in is finding these measures between nodes that have same attribute(say color). I'm thinking if I could partition the graph into different sub graphs, where nodes in each sub graph are of the same color, then I could accomplish go ahead and measure diameter in this sub graph. So my question is: Is there a way to partition a graph into sub graphs which contain nodes of same color?
I would really appreciate any insight.
Use Graph.subgraph(nodes)
NetworkX 2.x+:
Demo
import networkx as nx
G = nx.Graph()
G.add_nodes_from([1, 2, 3], color="red")
G.add_nodes_from([4, 5, 6])
G.nodes # NodeView((1, 2, 3, 4, 5, 6))
# create generator
nodes = (
node
for node, data
in G.nodes(data=True)
if data.get("color") == "red"
)
subgraph = G.subgraph(nodes)
subgraph.nodes # NodeView((1, 2, 3))
older NetworkX's
Iterate over (Graph.iter_nodes()) and filter the nodes based on your criteria. Pass that list to Graph.subgraph() and it'll return a copy of those nodes and their internal edges.
For example:
G = nx.Graph()
# ... build or do whatever to the graph
nodes = (n for n, d in G.nodes_iter(data=True)) if d.get('color') == 'red')
subgraph = G.subgraph(nodes)

Unusual behavior in networkx node coloring

I'm drawing to draw a simple undirected graph with networkx and I'm having trouble coloring the nodes according to a specific attribute. I created a dictionary, in which case the key references a list of nodes that contain that attribute. It looks like so:
{
1 : [1, 2, 3, 4],
3 : [9, 11, 10, 8],
2 : [7, 5, 6]
}
I would like to render the graph so that each set of nodes is colored differently. The keys are used to access a specific color. I draw the graph like so:
colors = [(random.random(), random.random(), random.random()) for i in xrange(0, 3)]
pos = nx.circular_layout(G)
for k,v in node_list_dict.iteritems():
nc = colors[int(k)-1]
nx.draw_networkx_nodes(G, pos, nodelist=v, node_color=nc)
nx.draw_networkx_edges(G, pos)
nx.draw_networkx_labels(G, pos)
This almost works, but results in an image like:
So it appears that the first two sets render correctly, but the last one does not. Any idea as to why? I looked at the documentation but I don't see why it's failing.
Also, the colors list usually ends up like so (when I mean usually I mean the colors generated are somewhat random)
[(0.982864745272968, 0.038693538759121182, 0.03869353875912118), (0.12848750206109338, 0.9956534627440381, 0.12848750206109338), (0.050388282183359334, 0.050388282183359334, 0.9916284269963801)]
You seem to have discovered an interesting broadcasting problem.
The trigger for this issue is when len(v) == len(nc), as according to the documentation:
node_color : color string, or array of floats
Node color. Can be a single color format string (default='r'),
or a sequence of colors with the same length as nodelist.
If numeric values are specified they will be mapped to
colors using the cmap and vmin,vmax parameters. See
matplotlib.scatter for more details.
so when len(v) != 3 nc is interpreted as RGB triple, when len(v) == 3, the floats in nc are mapped to RGB via the default color map (which is jet).
A possible work-around for this is
for k,v in node_list_dict.iteritems():
nc = colors[int(k)-1]
if len(v) == 3:
nc = nc + (1,)
nx.draw_networkx_nodes(G, pos, nodelist=v, node_color=nc)
which turns nc into a RGBA tuple, which won't trigger the usage of the color map.
If you want to track this down, the main work of draw_network_nodes is done here by a call to scatter. There is a comment in the scatter function (here) that marks this as a known problem and states how the ambiguity is broken. If you feel really strongly about this, you can create an issue on github, but I don't think it will get traction, as this is an explicit design decision.

Categories