I am plotting directed graph using networkx in python. However, I found that arrow head of edge is thick from one end instead of pointed arrow. I want to change the thick edge to pointed arrow. Here is my code, actual output, and desired output:
import networkx as nx
import matplotlib.pyplot as plt
G=nx.DiGraph()
item = [1,2]
G.add_edge(*item) #color = item[-1], weight = 2)
pos = nx.circular_layout(G)
nx.draw(G, pos, with_labels = True, edge_color = 'b')
plt.show()
Output
Desired output:
Any suggestion would be really helpful?
This may be a late answer, but in the new version networkx 2.1 you can set the arrow type by using the arrowstyle and arrowsize parameter.
import networkx as nx
import matplotlib.pyplot as plt
G=nx.DiGraph()
item = [1,2]
G.add_edge(*item) #color = item[-1], weight = 2)
pos = nx.circular_layout(G)
nx.draw(G, pos, with_labels = True, edge_color = 'b', arrowsize=20, arrowstyle='fancy')
plt.show()
You can go to the documentation for details: https://networkx.github.io/documentation/stable/reference/generated/networkx.drawing.nx_pylab.draw_networkx_edges.html#networkx.drawing.nx_pylab.draw_networkx_edges
Good news. This is possible now with the development version of networkx (will be networkx-2.1). Your code now now draws arrows
In [5]: import networkx as nx
...: import matplotlib.pyplot as plt
...:
...: G=nx.DiGraph()
...:
...: item = [1,2]
...:
...: G.add_edge(*item) #color = item[-1], weight = 2)
...:
...: pos = nx.circular_layout(G)
...: nx.draw(G, pos, with_labels = True, edge_color = 'b')
...: plt.show()
...:
This is not possible with nx.draw for now. According to https://github.com/networkx/networkx/blob/master/networkx/drawing/nx_pylab.py#L584 , it hasn't been implemented yet.
However, you can try to use graphviz to plot your graph. Networkx has an interface to convert your graph to graphviz object easily. See https://networkx.github.io/documentation/stable/reference/drawing.html#module-networkx.drawing.nx_agraph.
Related
I have a large dataframe(df) with Weight ,Source Node, target columns.
This is small sample of the df:
SourceNode
target
Weight
176890
657826
201
136578
589231
300
143873
457139
50
134589
892147
550
198345
678931
350
112443
525188
600
336128
689313
1500
Source node and target column are object and weight is int data type.
I am trying to create a large network graph which shows the connectivity from source node to target column (unidirect) and the edge need to reflect the weight value( like by stronger bigger connection).
I am getting very compact graph. I replaced kamada_kawai_layout with spring_layout and circular_layout but non have worked, Any suggestion how to make it larger and more spread?
I am also want to add the source node color as well
Here is my code:
import networkx as nx
G= nx.from_pandas_edgelist(df, source = 'SourceNode', target='target' , edge_attr= 'Weight')
edges = G.edges.data()
pos = nx.kamada_kawai_layout(G)
edge_width = [e[2]['Weight']/1000 for e in edges]
nx.draw(G, pos=pos, with_labels=True) # draw nodes (and edges!)
nx.draw_networkx_edges(G, pos=pos, width=edge_width)
I am adding this line for SourceNode color , but it doesn't work :
color_map = ['blue' if node == df['target'] else 'black' for node in G]
graph = nx.draw_networkx(G,pos, node_color=color_map)
I am very new to python. I appreciate any help.
Do you have, or can you install, matplotlib? NetworkX depends on other software for visualization. Try something like this:
import networkx as nx
import pandas as pd
# These lines probably aren't necessary on most machines
# import matplotlib
# matplotlib.use("TKAgg")
import matplotlib.pyplot as plt
df = pd.DataFrame({
"Source": [1, 1, 2, 2, 3],
"Target": [2, 3, 3, 4, 5],
"Weight": [5, 2, 3, 6, 1]
})
G = nx.from_pandas_edgelist(
df,
source = 'Source',
target='Target' ,
edge_attr= 'Weight'
)
edges = G.edges.data()
edge_width = [e[2]["Weight"] for e in edges]
pos = nx.kamada_kawai_layout(G)
nx.draw(G, pos = pos, with_labels = True, width = edge_width)
plt.show()
nodes = G.nodes
color_map = ["blue" if node < 3 else "green" for node in nodes]
nx.draw(G, pos = pos, with_labels = True, width = edge_width, node_color = color_map)
plt.show()
To make more sparse, you might just need to make the window bigger and/or everything else smaller:
fig, ax = plt.subplots()
figsize = fig.get_size_inches()
nx.draw(
G, pos = pos, with_labels = True,
node_size = 100,
width = [ew/10 for ew in edge_width],
node_color = color_map,
ax = ax
)
fig.set_size_inches(figsize * 2)
fig.show()
Other Options
For visualizing large graphs, have you tried Gephi? From the NetworkX docs:
NetworkX provides basic functionality for visualizing graphs, but its main goal is to enable graph analysis rather than perform graph visualization. In the future, graph visualization functionality may be removed from NetworkX or only available as an add-on package.
I am trying to create bipartite of certain nodes, for small numbers it looks perfectly fine:
Image for around 30 nodes
Unfortunately, this isn't the case for more nodes like this one:
Image for more nodes
My code for determining the position of each node looks something like this:
pos = {}
pos[SOURCE_STRING] = (0, width/2)
row = 0
for arr in left_side.keys():
pos[str(arr).replace(" ","")]=(NODE_SIZE, row)
row += NODE_SIZE
row = 0
for arr in right_side.keys():
pos[str(arr).replace(" ","")]=(2*NODE_SIZE,row)
row += NODE_SIZE
pos[SINK_STRING] = (3*NODE_SIZE, width/2)
return pos
And then I feed it to the DiGraph class:
G = nx.DiGraph()
G.add_nodes_from(nodes)
G.add_edges_from(edges, len=1)
nx.draw(G, pos=pos ,node_shape = "s", with_labels = True,node_size=NODE_SIZE)
This doesn't make much sense since they should be in the same distance from each other since NODE_SIZE is constant it doesn't change for the rest of the program.
Following this thread:
Bipartite graph in NetworkX
Didn't help me either.
Can something be done about this?
Edit(Following Paul Brodersen Advice using netGraph:
Used this documentation: netgraph doc
And still got somewhat same results, such as:
netgraph try
Using edges and different positions, also played with node size, with no success.
Code:
netgraph.Graph(edges, node_layout='bipartite', node_labels=True)
plt.show()
In your netgraph call, you are not changing the node size.
My suggestion with 30 nodes:
import numpy as np
import matplotlib.pyplot as plt
from netgraph import Graph
edges = np.vstack([np.random.randint(0, 15, 60),
np.random.randint(16, 30, 60)]).T
Graph(edges, node_layout='bipartite', node_size=0.5, node_labels=True, node_label_offset=0.1, edge_width=0.1)
plt.show()
With 100 nodes:
import numpy as np
import matplotlib.pyplot as plt
from netgraph import Graph
edges = np.vstack([np.random.randint(0, 50, 200),
np.random.randint(51, 100, 200)]).T
Graph(edges, node_layout='bipartite', node_size=0.5, node_labels=True, node_label_offset=0.1, edge_width=0.1)
plt.show()
just wrote a code to highlight flow in networkx,
I added nodes with coordinates and also connected them as I wish, but when I was going to use different colors to show the weight of edges, I found the color of some edges are not match with the weight value I defined. I tried to find the reason, when I print the info of edges and it is fine with all attributes including index and color value, so I guess the problem I wrote a wrong command on drawing the edges. But I am really wish to know how to solve this. My codes is as follows, and many thanks!!!`import pandas as pd
import matplotlib.pyplot as plt
import matplotlib as mpl
import matplotlib.colors as colors
import matplotlib.cm as cmx
import networkx as nx
import numpy as np
import mpld3
df = pd.read_excel('PARA.simpleNTS.XLSX', sheet_name='NodePar', usecols="A:E", nrows=67)
df.set_index('gasNode', inplace=True)
gasNodePar = dict(df.to_dict(orient='index'))
df = pd.read_excel('PARA.simpleNTS.XLSX', sheet_name='ArcPar', usecols="A:M", nrows=76)
df.set_index('a', inplace=True)
arcPar = dict(df.to_dict(orient='index'))
df = pd.read_excel('PARA.simpleNTS.XLSX', sheet_name='pipeflow', usecols="A:M", nrows=76)
df.set_index('a', inplace=True)
flow = dict(df.to_dict(orient='index'))
df=pd.read_excel('PARA.simpleNTS.xlsx',sheet_name='EnergyPrice', usecols="A:D", nrows=24)
df.set_index('Time', inplace=True)
EnergyPrice = dict(df.to_dict(orient='index'))
T = [t for t, t_info in EnergyPrice.items()]
A = [a for a, a_info in arcPar.items()]
F=[]
for a in A:
b=abs(flow[a]['t1'])
F.append(b)
##coordinates of nodes
x_c=[]
y_c=[]
I = [i for i, i_info in gasNodePar.items()]
for i in I:
x_c.append(gasNodePar[i]['x'])
y_c.append(gasNodePar[i]['y'])
##nodes mapping
x=[]
y=[]
c=[] # list
for a in A:
x.append(arcPar[a]['fnode'])
y.append(arcPar[a]['tnode'])
for k in range (67):
c.append([x_c[k],y_c[k]])
G = nx.Graph()
num = 67+1
nodes=I
G.add_nodes_from(nodes)
for k in range (76):
G.add_edge(x[k],y[k],weight=F[k],label=A[k],edge_color=F[k])
coordinates = c
vnode= np.array(coordinates)
npos = dict(zip(nodes, vnode))
nlabels = dict(zip(nodes, nodes))
#edge_labels=nx.get_edge_attributes(G,'label')
pos = nx.spring_layout(G, pos=npos)
nx.draw_networkx_nodes(G, npos, node_size=20, node_color="b")
nx.draw_networkx_edges(G, npos, edge_color=F,width=5,edge_cmap=plt.cm.coolwarm)
nx.draw_networkx_labels(G, npos, nlabels,font_size=10)
color=F
vmin=min(color)
vmax=max(color)
cmap=plt.cm.coolwarm
sm = plt.cm.ScalarMappable(cmap=cmap, norm=plt.Normalize(vmin = vmin, vmax=2))
cbar = plt.colorbar(sm)
tick_font_size = 20
cbar.ax.tick_params(labelsize=tick_font_size)
plt.rcParams["figure.dpi"] = 100
plt.rcParams["figure.figsize"] = (10,10)
plt.show()
`
I wonder how I can add the edge weights to my Bokeh plot (from NetworkX), preferably for edge_renderer.hover_glyph, like in this picture:
A possible answer for this problem is using nx.get_edge_attributes and pass the values to draw_networkx_edge_labels as the labels for the edges.
Here's the so awaited code:
pos=nx.spring_layout(G)
nx.draw_networkx(G, pos)
labels = nx.get_edge_attributes(G,'weight')
nx.draw_networkx_edge_labels(G,pos,edge_labels=labels)
Here's a full working example:
import networkx as nx
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(4,4))
G = nx.Graph()
G.add_node('A', pos="1,3!", demand=-10)
G.add_node('B', pos="3,5!")
G.add_node('C', pos="3,1!")
G.add_node('D', pos="4,3!")
G.add_node('E', pos="5,5!")
G.add_node('F', pos="5,1!")
G.add_node('G', pos="7,3!", demand=10)
G.add_edge('A','B',weight=20,capacity=20,label='(20,20)')
G.add_edge('A','C',weight=7,capacity=15,label='(7,15)')
G.add_edge('B','E',weight=16,capacity=21,label='(16,21)')
G.add_edge('B','D',weight=4,capacity=4,label='(4,4)')
G.add_edge('C','F',weight=3,capacity=9,label='(3,9)')
G.add_edge('C','D',weight=4,capacity=4,label='(4,4)')
G.add_edge('D','E',weight=2,capacity=2,label='(2,2)')
G.add_edge('D','F',weight=6,capacity=10,label='(6,10)')
G.add_edge('E','G',weight=18,capacity=24,label='(18,24)')
G.add_edge('F','G',weight=9,capacity=10,label='(9,10)')
pos=nx.spring_layout(G)
nx.draw_networkx(G, pos)
labels = nx.get_edge_attributes(G,'weight')
nx.draw_networkx_edge_labels(G,pos,edge_labels=labels)
The following code tries to place a label for each node apart from the one that is by default included by NetworkX/Matplotlib. The original positions of the nodes are obtained through the call to "nx.spring_layout(g)".
The problem is that, when it comes to draw with Matplotlib the labels, the latter are misplaced, as it can be seen in the attached graph.
Should I be doing something differently?
import logging
import networkx as nx
import matplotlib.pyplot as plt
__log = logging.getLogger(__name__)
g = nx.Graph()
nodes = ['shield', 'pcb-top', 'pcb-config', 'chassis']
for k in nodes:
g.add_node(k)
plt.figure(figsize=(8, 11), dpi=150)
nx.draw(g, with_labels=True)
node_cfg = nx.spring_layout(g)
for k, node in node_cfg.items():
__log.debug('node = %s #(%.6f, %.6f)', k, node[0], node[1])
plt.text(node[0], node[1], k, bbox={'color': 'grey'})
plt.savefig('test.png')
Use the same position information for the network drawing as for the labels.
node_cfg = nx.spring_layout(g)
plt.figure(figsize=(8, 11), dpi=150)
nx.draw(g, pos=node_cfg, with_labels=True)