Consider the tree below.
import matplotlib.pyplot as plt
import networkx as nx
import pydot
from networkx.drawing.nx_pydot import graphviz_layout
T = nx.balanced_tree(2, 5)
for line in nx.generate_adjlist(T):
print(line)
pos = graphviz_layout(T, prog="dot")
nx.draw(T, pos, node_color="y", edge_color='#909090', node_size=200, with_labels=True)
plt.show()
How can I draw this left to right so that the whole image is rotated by 90 degrees with the root on the right?
If you want to have fine-grained control over node positions (which includes rotating the whole graph) you can actually set each node's position explicitly. Here's a way to do that that produces a 'centred' hierarchy, left to right.
import itertools
import matplotlib.pyplot as plt
import networkx as nx
plt.figure(figsize=(12,8))
subset_sizes = [1, 2, 4, 8, 16, 32]
def multilayered_graph(*subset_sizes):
extents = nx.utils.pairwise(itertools.accumulate((0,) + subset_sizes))
layers = [range(start, end) for start, end in extents]
G = nx.Graph()
for (i, layer) in enumerate(layers):
G.add_nodes_from(layer, layer=i)
for layer1, layer2 in nx.utils.pairwise(layers):
G.add_edges_from(itertools.product(layer1, layer2))
return G
# Instantiate the graph
G = multilayered_graph(*subset_sizes)
# use the multipartite layout
pos = nx.multipartite_layout(G, subset_key="layer")
nodes = G.nodes
nodes_0 = set([n for n in nodes if G.nodes[n]['layer']==0])
nodes_1 = set([n for n in nodes if G.nodes[n]['layer']==1])
nodes_2 = set([n for n in nodes if G.nodes[n]['layer']==2])
nodes_3 = set([n for n in nodes if G.nodes[n]['layer']==3])
nodes_4 = set([n for n in nodes if G.nodes[n]['layer']==4])
nodes_5 = set([n for n in nodes if G.nodes[n]['layer']==5])
# setup a position list
pos = dict()
base = 128
thisList = list(range(-int(base/2),int(base/2),1))
# then assign nodes to indices
pos.update( (n, (10, thisList[int(base/2)::int(base/2)][i])) for i, n in enumerate(nodes_0) )
pos.update( (n, (40, thisList[int(base/4)::int(base/2)][i])) for i, n in enumerate(nodes_1) )
pos.update( (n, (60, thisList[int(base/8)::int(base/4)][i])) for i, n in enumerate(nodes_2) )
pos.update( (n, (80, thisList[int(base/16)::int(base/8)][i])) for i, n in enumerate(nodes_3) )
pos.update( (n, (100, thisList[int(base/32)::int(base/16)][i])) for i, n in enumerate(nodes_4) )
pos.update( (n, (120, thisList[int(base/64)::int(base/32)][i])) for i, n in enumerate(nodes_5) )
nx.draw(G, pos, node_color='y', edge_color='grey', with_labels=True)
plt.show()
By using a position list, you can easily transform this graph into any number of alignments or rotations.
Notes
add nodes with a layer key and use multipartite_layout to make the graph layered
setup a "position list" based on the number of nodes in your widest layer (to make the layout centre-aligned, use a zero-centred list)
To assign positions in each layer use basic Python list slice/skip notation to grab the right number of positions, spaced the appropriate amount apart, starting at the right position for the alignment you want
You can do this with the rankdir attribute from graphviz, which can be set on a networkx graph by:
T.graph["graph"] = dict(rankdir="RL")
networkx issue #3547 gives some more info about setting graph attributes.
Related
if i want to simulate my network like this picture but Bipartite = 4 and increase my nodes to 600 node (n=150 l =150 s=150 and m=150) and also have 685 edges randomly...how can write simulation coding to make this network? can anyone help me ? enter code herethank you so much for your attention
also this picture coding here
import networkx as nx
import matplotlib.pyplot as plt
import random
from networkx.algorithms import bipartite
B = nx.Graph()
n = [1,2,3,4]
l = [*'abc']
B.add_nodes_from(n, bipartite=0)
B.add_nodes_from(l, bipartite=1)
B.add_edges_from([(1, "a"), (1, "b"), (2, "b"), (2, "c"), (3, "c"), (4, "a")])
pos = dict()
pos.update( (n, (1, i)) for i, n in enumerate(n) )
pos.update( (n, (2, i)) for i, n in enumerate(l) )
nx.draw_networkx(B, pos=pos)
nx.draw_networkx_nodes(B, pos=pos, nodelist=n)
plt.axis('off')
plt.show()
You want to draw a 4-partite graph. You can generalize your code above to draw a k-partite graph, where k=4 here, as shown below, using the following code (you can change the node color, type and font color if you want).
B = nx.Graph()
nodes = {}
k = 4
n = 150
ne = 685
e = 229 # since you want total 685 edges, you can have ~229 edges in between two
# adjacent sets of nodes
for i in range(k):
nodes[i] = list(range(n*i, n*(i+1)))
for i in range(k):
B.add_nodes_from(nodes[i], bipartite=k)
edges = []
for j in range(k-1):
for i in range(e):
edges.append((random.choice(nodes[j]), random.choice(nodes[j+1])))
B.add_edges_from(edges[:ne])
pos = dict()
for j in range(k):
pos.update( (n, (j, i)) for i, n in enumerate(nodes[j]) )
plt.figure(figsize=(20,40))
nx.draw_networkx(B, pos=pos)
plt.axis('off')
plt.show()
I managed to produce the graph correctly, but with some more testing noted inconsistent result for the following two different line of codes:
colors = [h.edge[i][j]['color'] for (i,j) in h.edges_iter()]
widths = [h.edge[i][j]['width'] for (i,j) in h.edges_iter()]
nx.draw_circular(h, edge_color=colors, width=widths)
This approach results in consistent output, while the following produces wrong color/size per the orders of edges:
colors = list(nx.get_edge_attributes(h,'color').values())
widths = list(nx.get_edge_attributes(h,'width').values())
nx.draw_circular(h, edge_color=colors, width=widths)
However, it looks to me the above two lines both rely on the function call to return the attributes per the order of edges. Why the different results?
It looks a bit clumsy to me to access attributes with h[][][]; is it possible to access it by dot convention, e.g. edge.color for edge in h.edges().
Or did I miss anything?
The order of the edges passed to the drawing functions are important. If you don't specify (using the edges keyword) you'll get the default order of G.edges(). It is safest to explicitly give the parameter like this:
import networkx as nx
G = nx.Graph()
G.add_edge(1,2,color='r',weight=2)
G.add_edge(2,3,color='b',weight=4)
G.add_edge(3,4,color='g',weight=6)
pos = nx.circular_layout(G)
edges = G.edges()
colors = [G[u][v]['color'] for u,v in edges]
weights = [G[u][v]['weight'] for u,v in edges]
nx.draw(G, pos, edges=edges, edge_color=colors, width=weights)
This results in an output like this:
Dictionaries are the underlying data structure used for NetworkX graphs, and as of Python 3.7+ they maintain insertion order.
This means that we can safely use nx.get_edge_attributes to retrieve edge attributes since we are guaranteed to have the same edge order in every run of Graph.edges() (which is internally called by get_edge_attributes).
So when plotting, we can directly set attributes such as edge_color and width from the result returned by get_edge_attributes. Here's an example:
G = nx.Graph()
G.add_edge(0,1,color='r',weight=2)
G.add_edge(1,2,color='g',weight=4)
G.add_edge(2,3,color='b',weight=6)
G.add_edge(3,4,color='y',weight=3)
G.add_edge(4,0,color='m',weight=1)
colors = nx.get_edge_attributes(G,'color').values()
weights = nx.get_edge_attributes(G,'weight').values()
pos = nx.circular_layout(G)
nx.draw(G, pos,
edge_color=colors,
width=list(weights),
with_labels=True,
node_color='lightgreen')
if you want to avoid adding edge colors and alphas / width manually, you may also find this function helpful:
def rgb_to_hex(rgb):
return '#%02x%02x%02x' % rgb
adjacency_matrix = np.array([[0, 0, 0.5], [1, 0, 1], [1, 0.5, 0]]))
n_graphs = 5
fig, axs = plt.subplots(1, len(n_graphs), figsize=(19,2.5))
for graph in range(n_graphs):
pos = {0: (1, 0.9), 1: (0.9, 1), 2: (1.1, 1)}
# draw DAG graph from adjacency matrix
gr = nx.from_numpy_matrix(adjacency_matrix, create_using=nx.DiGraph)
weights = nx.get_edge_attributes(gr, "weight")
# adding nodes
all_rows = range(0, adjacency_matrix.shape[0])
for n in all_rows:
gr.add_node(n)
# getting edges
edges = gr.edges()
# weight and color of edges
scaling_factor = 4 # to emphasise differences
alphas = [weights[edge] * scaling_factor for edge in edges]
colors = [rgb_to_hex(tuple(np.repeat(int(255 * (1-
weights[edge])),3))) for edge in edges]
# draw graph
nx.draw(gr,
pos,
ax=axs[graph],
edgecolors='black',
node_color='white',
node_size=2000,
labels={0: "A", 1: "B", 2: "C"},
font_weight='bold',
linewidths=2,
with_labels=True,
connectionstyle="arc3,rad=0.15",
edge_color=colors,
width=alphas)
plt.tight_layout()
I would like to find the node degree with edges of certain labels, when I use the the degree() method of networkx it gives me a dic with {:} but how to get the labels of those edges?. Any help including the info on what other graph analysis packages has this provision, is appreciated.
In igraph, you want g.es.select(_source=i). This gets all of the edges whose source vertex is i, at which point you can ask for the attributes of those edges:
>>> g = igraph.Graph.Full(3) #complete graph on three vertices
>>> g.es['label'] = ['a','b','c'] #sets the first edge to have 'label' value 'a', etc.
>>> g.es.select(_source=0)['label']
['a', 'b']
Nodes' degree by certain edges labels
Random graph functions
def rand_gen(m):
yield random.randint(0,m)
def get_edges(n):
tpl = []
for _ in range(n):
for x,y in zip(rand_gen(n), rand_gen(n)):
tpl.append((x,y, {random.choice(['a','b','c']): random.randint(0,10)}))
return tpl
Create graph
g = nx.Graph()
e = get_edges(10)
g.add_edges_from(e)
pos = nx.spring_layout(g)
nx.draw_networkx_nodes(g,pos)
nx.draw_networkx_labels(g,pos, font_color='w')
nx.draw_networkx_edges(g,pos)
nx.draw_networkx_edge_labels(g,pos, edge_labels={(x,y):l.keys()[0] for x,y,l in g.edges(data=True)})
plt.axis('off')
plt.show()
Select by label
nds = []
nds.extend([n1, n2] for n1,n2,l in g.edges(data=True) if l.keys()[0] == 'a')
nds = [x for y in nds for x in y]
Get the degree of unique labels
[nx.degree(g,x) for x in set(nds)]
[out]
[2, 3, 2, 2, 5]
I have an unweighted graph created with networkx for which I would like to calculate the weight of edges between nodes based on the count/frequency of an edge occurrence. An edge in my graph can occur more than once but the frequency of an edge appearance is not known in advance. The purpose is to visualize the edges based on the weight (e.g. count/frequency) of moves between connected nodes. Essentially, I'd like to create a network traffic map of movement between connected nodes, and visualize based on color or edge width. E.g., edge from node 0 to 1 has 10 movements between them, and node 1 to 2 has 5, so edge 0-1 would be visualized using a different edge color/size.
How can I calculate the weight of edges between two nodes, on the fly (after adding them to the graph with g.add_edges_from()), and then reapply to my graph for visualization? Below is a sample of my graph, data, and code I've used to create the graph initially and a solution I attempted that failed.
Graph
Sample Data
Cluster centroids(nodes)
cluster_label,latitude,longitude
0,39.18193382,-77.51885109
1,39.18,-77.27
2,39.17917928,-76.6688633
3,39.1782,-77.2617
4,39.1765,-77.1927
5,39.1762375,-76.8675441
6,39.17468,-76.8204499
7,39.17457332,-77.2807235
8,39.17406072,-77.274685
9,39.1731621,-77.2716502
10,39.17,-77.27
Trajectories(edges)
user_id,trajectory
11011.0,"[[340, 269], [269, 340]]"
80973.0,"[[398, 279]]"
608473.0,"[[69, 28]]"
2139671.0,"[[382, 27], [27, 285]]"
3945641.0,"[[120, 422], [422, 217], [217, 340], [340, 340]]"
5820642.0,"[[458, 442]]"
6060732.0,"[[291, 431]]"
6912362.0,"[[68, 27]]"
7362602.0,"[[112, 269]]"
8488782.0,"[[133, 340], [340, 340]]"
Code
import csv
import networkx as nx
import pandas as pd
import community
import matplotlib.pyplot as plt
import time
import mplleaflet
g = nx.MultiGraph()
df = pd.read_csv('cluster_centroids.csv', delimiter=',')
df['pos'] = list(zip(df.longitude,df.latitude))
dict_pos = dict(zip(df.cluster_label,df.pos))
#print dict_pos
for row in csv.reader(open('edges.csv', 'r')):
if '[' in row[1]: #
g.add_edges_from(eval(row[1]))
# Plotting with mplleaflet
fig, ax = plt.subplots()
nx.draw_networkx_nodes(g,pos=dict_pos,node_size=50,node_color='b')
nx.draw_networkx_edges(g,pos=dict_pos,linewidths=0.01,edge_color='k', alpha=.05)
nx.draw_networkx_labels(g,dict_pos)
mplleaflet.show(fig=ax.figure)
I have tried using g.add_weighted_edges_from() and adding weight=1 as an attribute, but have not had any luck. I also tried using this which also did not work:
for u,v,d in g.edges():
d['weight'] = 1
g.edges(data=True)
edges = g.edges()
weights = [g[u][v]['weight'] for u,v in edges]
Since this went unanswered, a 2nd question on this topic was opened (here: Python/NetworkX: Add Weights to Edges by Frequency of Edge Occurance) which received responses. To add weights to edges based on count of edge occurrence:
g = nx.MultiDiGraph()
df = pd.read_csv('G:\cluster_centroids.csv', delimiter=',')
df['pos'] = list(zip(df.longitude,df.latitude))
dict_pos = dict(zip(df.cluster_label,df.pos))
#print dict_pos
for row in csv.reader(open('G:\edges.csv', 'r')):
if '[' in row[1]: #
g.add_edges_from(eval(row[1]))
for u, v, d in g.edges(data=True):
d['weight'] = 1
for u,v,d in g.edges(data=True):
print u,v,d
To scale color and edge width based on the above count:
minLineWidth = 0.25
for u, v, d in g.edges(data=True):
d['weight'] = c[u, v]*minLineWidth
edges,weights = zip(*nx.get_edge_attributes(g,'weight').items())
values = range(len(g.edges())
jet = cm = plt.get_cmap('YlOrRd')
cNorm = colors.Normalize(vmin=0, vmax=values[-1])
scalarMap = cmx.ScalarMappable(norm=cNorm, cmap=jet)
colorList = []
for i in range(len(g.edges()):
colorVal = scalarMap.to_rgba(values[i])
colorList.append(colorVal)
and passing width=[d['weight'] for u,v, d in g.edges(data=True)], edge_color=colorList as arguments in nx.draw_networkx_edges()
I managed to produce the graph correctly, but with some more testing noted inconsistent result for the following two different line of codes:
colors = [h.edge[i][j]['color'] for (i,j) in h.edges_iter()]
widths = [h.edge[i][j]['width'] for (i,j) in h.edges_iter()]
nx.draw_circular(h, edge_color=colors, width=widths)
This approach results in consistent output, while the following produces wrong color/size per the orders of edges:
colors = list(nx.get_edge_attributes(h,'color').values())
widths = list(nx.get_edge_attributes(h,'width').values())
nx.draw_circular(h, edge_color=colors, width=widths)
However, it looks to me the above two lines both rely on the function call to return the attributes per the order of edges. Why the different results?
It looks a bit clumsy to me to access attributes with h[][][]; is it possible to access it by dot convention, e.g. edge.color for edge in h.edges().
Or did I miss anything?
The order of the edges passed to the drawing functions are important. If you don't specify (using the edges keyword) you'll get the default order of G.edges(). It is safest to explicitly give the parameter like this:
import networkx as nx
G = nx.Graph()
G.add_edge(1,2,color='r',weight=2)
G.add_edge(2,3,color='b',weight=4)
G.add_edge(3,4,color='g',weight=6)
pos = nx.circular_layout(G)
edges = G.edges()
colors = [G[u][v]['color'] for u,v in edges]
weights = [G[u][v]['weight'] for u,v in edges]
nx.draw(G, pos, edges=edges, edge_color=colors, width=weights)
This results in an output like this:
Dictionaries are the underlying data structure used for NetworkX graphs, and as of Python 3.7+ they maintain insertion order.
This means that we can safely use nx.get_edge_attributes to retrieve edge attributes since we are guaranteed to have the same edge order in every run of Graph.edges() (which is internally called by get_edge_attributes).
So when plotting, we can directly set attributes such as edge_color and width from the result returned by get_edge_attributes. Here's an example:
G = nx.Graph()
G.add_edge(0,1,color='r',weight=2)
G.add_edge(1,2,color='g',weight=4)
G.add_edge(2,3,color='b',weight=6)
G.add_edge(3,4,color='y',weight=3)
G.add_edge(4,0,color='m',weight=1)
colors = nx.get_edge_attributes(G,'color').values()
weights = nx.get_edge_attributes(G,'weight').values()
pos = nx.circular_layout(G)
nx.draw(G, pos,
edge_color=colors,
width=list(weights),
with_labels=True,
node_color='lightgreen')
if you want to avoid adding edge colors and alphas / width manually, you may also find this function helpful:
def rgb_to_hex(rgb):
return '#%02x%02x%02x' % rgb
adjacency_matrix = np.array([[0, 0, 0.5], [1, 0, 1], [1, 0.5, 0]]))
n_graphs = 5
fig, axs = plt.subplots(1, len(n_graphs), figsize=(19,2.5))
for graph in range(n_graphs):
pos = {0: (1, 0.9), 1: (0.9, 1), 2: (1.1, 1)}
# draw DAG graph from adjacency matrix
gr = nx.from_numpy_matrix(adjacency_matrix, create_using=nx.DiGraph)
weights = nx.get_edge_attributes(gr, "weight")
# adding nodes
all_rows = range(0, adjacency_matrix.shape[0])
for n in all_rows:
gr.add_node(n)
# getting edges
edges = gr.edges()
# weight and color of edges
scaling_factor = 4 # to emphasise differences
alphas = [weights[edge] * scaling_factor for edge in edges]
colors = [rgb_to_hex(tuple(np.repeat(int(255 * (1-
weights[edge])),3))) for edge in edges]
# draw graph
nx.draw(gr,
pos,
ax=axs[graph],
edgecolors='black',
node_color='white',
node_size=2000,
labels={0: "A", 1: "B", 2: "C"},
font_weight='bold',
linewidths=2,
with_labels=True,
connectionstyle="arc3,rad=0.15",
edge_color=colors,
width=alphas)
plt.tight_layout()