How to draw a bipartite graph - python

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()

Related

Is there an easy way to calculate the graph edit distance of two graphs with a brut force algorithm?

Without using the networkx built-in function, is there an easy brut force algorithm that calculates the graph edit distance between G1 and G2?
I searched on the Internet, but I could only find hard optimal solutions
Here is a very inefficient process, but it does solve the problem. The idea will be to do a Dijkstra-like shortest-path search from the "edit graph", where the nodes in the edit graph represent Graphs, there is an edge between two nodes G_a, G_b in an edit graph if there is an edit that takes G_a to G_b. Note, you will need you will need a function is_isomorphic(Gx,Gy) which tests whether Gx and Gy are isomorphic graphs (which you can check by checking all the node permutations, again very costly).
The algorithm to find shortest edit distance would be as follows:
initialize: current_node as G1,
visited_nodes as G1,
current_minimum_cost_dictionary={G1:0}
#overload == for Graph Class so that G==F iff is_isomorphic(G,F)
while G2 not in visited nodes:
cost = current_minimum_cost_dictionary[G2]
neighbor_graphs_set = calculate_distinct_neighbor_nodes(current_node)
for graph in neighbor_graphs_set:
if graph not in visited_nodes:
visited_nodes.push(graph)
current_minimum_cost_dictionary[graph] = cost + 1
else:
current_minimum_cost_dictionary[graph] = min(cost + 1, current_minimum_cost_dictionary[graph])
#set current_node to the Graph with the minimum value in current_minimum_cost
return current_minimum_cost_dictionary(G2)
calculate_distinct_neighbor_nodes returns the set of distinct graphs (up to isomorphism)that are neighbors of the input graph. two graphs x,y are neighbors if x can be obtained from y by 1 edit (or viceversa).
According to Juan Carlos Ramirez reply (who gave a pseudo code of the algorithm), I finally implemented the ugly brutforce algorithm I was expecting. As mentionned in the conversation, it only deals with small graphs as the complexity is exponential. The following Python code uses:
networkx (for graph manipulation)
algorithmx (for graph 2D-rendering)
from networkx.classes.function import nodes
import algorithmx
import networkx as nx
from algorithmx.networkx import add_graph
canvas1 = algorithmx.jupyter_canvas()
canvas2 = algorithmx.jupyter_canvas()
# Create a directed graph
G1 = nx.Graph()
G2 = nx.Graph()
# G1.add_edges_from([(1, 2), (7, 4), (2, 7),(3, 7)])
# G2.add_edges_from([(1, 2), (3, 4), (2, 3), (3, 5)])
G1.add_edges_from([(1, 2), (5, 4), (2, 5),(3, 5)])
G2.add_edges_from([(1, 2), (3, 4), (2, 3), (3, 1)])
def graph_distance(G1, G2):
tmp_graphs = []
next_graphs = [G1]
dist = 0
nId = 1000
while 1:
print(dist)
for graph in next_graphs:
if nx.is_isomorphic(graph, G2): # Check isomorphism for each built graph
return dist
new_graph = graph.copy()
new_graph.add_node(len(graph.nodes))
tmp_graphs.append(new_graph) # Add one vertex (that will be connected to the rest of the graph in the next iterations)
graph_copy = graph.copy()
for node in graph.nodes : # Add edge
for newNeighbour in graph.nodes :
if not graph.has_edge(node, newNeighbour):
new_graph = graph.copy()
new_graph.add_edge(node, newNeighbour)
tmp_graphs.append(new_graph)
for node in graph.nodes : # Delete edge
for newNeighbour in graph.nodes:
if graph.has_edge(node, newNeighbour):
new_graph = graph.copy()
new_graph.remove_edge(node, newNeighbour)
tmp_graphs.append(new_graph)
for node in graph.nodes : # Delete vetex
new_graph = graph.copy()
new_graph.remove_node(node)
tmp_graphs.append(new_graph)
dist+=1
next_graphs = tmp_graphs
tmp_graphs = []
print("Graph edit distance is:",graph_distance(G1, G2))
add_graph(canvas1, G1)
add_graph(canvas2, G2)
canvas1
# canvas2
Uncomment canvas1/canvas2 to display the one you want

How to draw trees left to right

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.

Networkx apparently scrambling color list python [duplicate]

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()

How to get degrees with certain edge labels using python

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]

networkx - change color/width according to edge attributes - inconsistent result

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()

Categories