Networkx: Network with parallel edges - python

I need to develop a bipartite network where nodes come from the 'id_emp' and 'name_dep' columns of the dataframe below:
import networkx as nx
import pandas as pd
df = pd.DataFrame({'id_emp':[13524791000109, 12053850000137, 4707821000113, 4707821000114],
'name_emp':['Cristiano', 'Gaúcho', 'Fenômeno','Angelin'],
'name_dep': ['Ronaldo','Ronaldo', 'Ronaldo', 'Ronaldo'],
'peso': [8,9,10,11]})
On the edges:
1 - They have weight, determined by the 'weight' column;
2 - They must be directed;
3 - In orgininal work must demonstrate a relation of exchange, then between two nodes, there must be two parallel edges;
I am trying with the following code:
G = nx.MultiDiGraph()
G.add_nodes_from(df['id_emp'], bipartite = 0)
deputados = [v for v in G.nodes if G.nodes[v]['bipartite'] == 0]
G.add_nodes_from(df['name_dep'], bipartite = 1)
empresa = [v for v in G.nodes if G.nodes[v]['bipartite'] == 1]
G.add_weighted_edges_from(df[['id_emp', 'name_dep', 'peso']].values)
edge_width = [a[2]['weight']//2 for a in G.edges(data=True)]
deputado = "Ronaldo"
lista_subset_graph = list(df[df["name_dep"] == deputado]["id_emp"]) + [deputado]
H = G.subgraph(lista_subset_graph)
nx.draw_networkx_nodes(H, pos = nx.spring_layout(H), nodelist = deputados, node_color='#bfbf7f', node_shape="h", node_size=3000, with_labels = True)
nx.draw_networkx_nodes(H, pos = nx.spring_layout(H), nodelist = empresa, node_color='#9f9fff', node_size=3000, with_labels = True)
nx.draw_networkx_edges(H, pos = nx.spring_layout(H),width=edge_width, alpha=0.2)
#nx.draw(H, style = "solid", with_labels = True)
plt.figure(figsize=(7.5, 7.5))
plt.show()
Output:
When I comment the lines started by nx.draw_networkx_nodes and nx.draw_networkx_edges and uncomment the line started by nx.draw the output is as follows:
I would like to keep a preview as the first image that meets the specs for the edges.

Related

Ignore non-connected nodes in NetworkX Graph

I have a network diagram where a node is connected to another node which in-turn is connected to another node, worked through a logic to ignore when there is only one neighbour but that is still leaving few bits back. The attached diagram does a better job explaining it
I need to only keep where a red-node is connected to a blue-node or another red-node through a green node. Data used
for u, v in CA_new.edges:
if len(list(CA_new.neighbors(v))) == 1 and len(list(CA_new.neighbors(u))) == 1:
removeNodeslist.append(v)
removeNodeslist.append(u)
else:
keepNodeslist.append(v)
keepNodeslist.append(u)
CA_new.remove_nodes_from(removeNodeslist)
From your comments, it sounds like you’re looking for the connected components that contain at least two parent nodes, and at least one of those parents has a colored edge. With that in mind, you can do something like the following:
import numpy as np
import networkx as nx
import pandas as pd
data = {
'Parent': list("EEABEHDILGKCDBLLFBCCJ"),
'Child': ["X1","X2","Y1","Y1","Y1","M1","N3","N4","N5","N7","N8","M1","M2",
"M3","M4","M5","M6","M7","M8","M9","P7"],
'Colour': list("NNNNYYNYNNNYNNNNYNNNN")
}
df = pd.DataFrame(data)
G = nx.from_pandas_edgelist(df, source = 'Parent', target = 'Child')
parent_set = set(df['Parent'])
colored_parent_set = set(df.loc[df['Colour']=='Y','Parent'])
node_set = set()
for comp in nx.connected_components(G):
if (len(comp & parent_set) >= 2 and
comp & colored_parent_set):
node_set |= comp
H = G.subgraph(node_set)
colors = ['limegreen']*len(H)
for i,n in enumerate(H.nodes):
if n in colored_parent_set:
colors[i] = "red"
elif n in parent_set:
colors[i] = "deepskyblue"
nx.draw(H, node_color = colors, with_labels = True)
Here's the result that I get:

Changing the colors of nodes based on certain values in Networkx

I have a set of nodes with an adjacency matrix. I want to color these nodes based on the array P such that node 1 = P[0], node 2 = P[1], node 3 = P[2] and so on with a colorbar showing the range of values. The current and expected outputs are presented.
import numpy as np
import networkx as nx
G = nx.grid_2d_graph(3,3)
new_nodes = {e: n for n, e in enumerate(G.nodes, start=1)}
new_edges = [(new_nodes[e1], new_nodes[e2]) for e1, e2 in G.edges]
G = nx.Graph()
G.add_edges_from(new_edges)
nx.draw(G, with_labels=True)
A1 = nx.adjacency_matrix(G)
A=A1.toarray()
print([A])
P=np.array([10.5,20.7,30.7,40.1,50.6,60.3,70.6,80.9,90.8])
The current output is
enter image description here
The expected output is
enter image description here
If you want to color your nodes, you can pass to the draw function a list of color to bind to each nodes.
Those color can be computed as hexa decimal values from any range you decided to target. In my proposition below : your P vector holds values between 0 and 100, while color values can be beteween 0 and 255, coded in hexadecimal.
Proposition:
P=np.array([10,20,30,40,50,60,70,80,90])
color_hex_values = [ hex(int(e*255/100))[2:] for e in P ]
print(color_hex_values)
nx.draw(G, with_labels=True,node_color=[f"#00{hv}00" for hv in color_hex_values] )

spacing nodes at networkx/plotly network and labeling

I created a network using networkx and plotly as following:
edges = df.stack().reset_index()
edges.columns = ['var_1','var_2','correlation']
edges = edges.loc[ (edges['correlation'] < -0.6) | (edges['correlation'] > 0.6) & (edges['var_1'] != edges['var_2']) ].copy()
#create undirected graph with weights corresponding to the correlation magnitude
G0 = nx.from_pandas_edgelist(edges, 'var_1', 'var_2', edge_attr=['correlation'])
mst = G0
# assign colours to edges depending on positive or negative correlation
# assign edge thickness depending on magnitude of correlation
edge_colours = []
edge_width = []
for key, value in nx.get_edge_attributes(mst, 'correlation').items():
edge_colours.append(assign_colour(value))
edge_width.append(assign_thickness(value))
node_size = []
degrees = [val for key, val in dict(G0.degree).items()]
max_deg = max(degrees)
min_deg = min(degrees)
for value in degrees:
node_size.append(assign_node_size(value,min_deg,max_deg))
#draw the network:
nx.draw(mst, pos=nx.fruchterman_reingold_layout(mst),
node_size=15, edge_color=edge_colours, node_colour="black",
edge_width=0.2)
plt.show()
def get_coordinates(G=mst):
"""Returns the positions of nodes and edges in a format for Plotly to draw the network"""
# get list of node positions
pos = nx.fruchterman_reingold_layout(mst)
Xnodes = [pos[n][0] for n in mst.nodes()]
Ynodes = [pos[n][1] for n in mst.nodes()]
Xedges_red = []
Yedges_red = []
Xedges_green = []
Yedges_green = []
def insert_edge(Xedges, Yedges):
Xedges.extend([pos[e[0]][0], pos[e[1]][0], None])
Yedges.extend([pos[e[0]][1], pos[e[1]][1], None])
search_dict = nx.get_edge_attributes(mst, 'correlation')
for e in mst.edges():
correlation = search_dict[(e[0], e[1])]
if correlation <= 0 : # red_edges
insert_edge(Xedges_red, Yedges_red)
else:
insert_edge(Xedges_green, Yedges_green)
# x coordinates of the nodes defining the edge e
return Xnodes, Ynodes, Xedges_red, Yedges_red, Xedges_green, Yedges_green
node_label = list(mst.nodes())
node_label = [fix_string(x) for x in node_label]
# get coordinates for nodes and edges
Xnodes, Ynodes, Xedges_red, Yedges_red, Xedges_green, Yedges_green = get_coordinates()
external_data = [list(x) for x in coding_names_df.values]
external_data = {fix_string(x[0]): x[1] for x in external_data}
external_data2 = [list(y) for y in coding_names_df.values]
external_data2 = {fix_string(y[0]): y[2] for y in external_data2}
external_data3 = [list(z) for z in coding_names_df.values]
external_data3 = {fix_string(z[0]): z[3] for z in external_data3}
external_data4 = [list(s) for s in coding_names_df.values]
external_data4 = {fix_string(s[0]): s[4] for s in external_data4}
# =============================================================================
description = [f"<b>{index}) {node}</b>"
"<br><br>Realm: " +
"<br>" + external_data.get(node, 'No external data found') +
"<br><br>Type: " +
"<br>" + external_data2.get(node, 'No external data found')
for index, node in enumerate(node_label)]
# =============================================================================
# def nodes colours:
node_colour = [assign_node_colour(node, external_data3, coding_names_df) for node in node_label]
node_shape = [assign_node_shape(node, external_data4, coding_names_df) for node in node_label]
# edges
# negative:
tracer_red = go.Scatter(x=Xedges_red, y=Yedges_red,
mode='lines',
line= dict(color="#FA0000", width=1),
hoverinfo='none',
showlegend=False)
# positive:
tracer_green = go.Scatter(x=Xedges_green, y=Yedges_green,
mode='lines',
line= dict(color= "#29A401", width=1),
hoverinfo='none',
showlegend=False)
# nodes
tracer_marker = go.Scatter(x=Xnodes, y=Ynodes,
mode='markers+text',
textposition='top center',
marker=dict(size=node_size,
line=dict(width=0.8, color='black'),
color=node_colour,
symbol=node_shape),
hovertext=description,
hoverinfo='text',
textfont=dict(size=7),
showlegend=False)
axis_style = dict(title='',
titlefont=dict(size=20),
showgrid=False,
zeroline=False,
showline=False,
ticks='',
showticklabels=False)
layout = dict(title='',
width=1300,
height=900,
autosize=False,
showlegend=False,
xaxis=axis_style,
yaxis=axis_style,
hovermode='closest',
plot_bgcolor = '#fff')
fig = dict(data=[tracer_red, tracer_green, tracer_marker], layout=layout)
display(HTML("""<p>Node sizes are proportional to the size of annualised returns.<br>
Node colours signify positive or negative returns since beginning of the timeframe.</p> """))
plot(fig)
and I got this plot: network
I want to add labels, but it's getting too crowded (especially in the middle)
so I have two questions:
How can I spacing the middle? (but still to keep the fruchterman_reingold possition)
How can I add just a few specific labels?
any help will be graet! Tnx :)
Something you could try is setting the k parameter in the layout algorithm, which as mentioned in the docs it sets:
k: (float (default=None)) – Optimal distance between nodes. If None the distance is set to 1/sqrt(n) where n is the number of nodes. Increase this value to move nodes farther apart.
So by playing a bit with this value, and increasing accordingly we can get a more spread out layout and avoid overlap between node labels.
Here's a simple example to illustrate what the behavior is:
result_set = {('plant','tree'), ('tree','oak'), ('flower', 'rose'), ('flower','daisy'), ('plant','flower'), ('tree','pine'), ('plant','roots'), ('animal','fish'),('animal','bird'), ('bird','robin'), ('bird','falcon'), ('animal', 'homo'),('homo','homo-sapiens'), ('animal','reptile'), ('reptile','snake'),('fungi','mushroom'), ('fungi','mold'), ('fungi','toadstool'),('reptile','crocodile'), ('mushroom','Portabello'), ('mushroom','Shiitake'),('pine','roig'),('pine','pinyer'), ('tree','eucaliptus'),('rose','Floribunda'),('rose','grandiflora')}
G=nx.from_edgelist(result_set)
pos=nx.fruchterman_reingold_layout(G)
plt.figure(figsize=(8,5))
nx.draw(G, pos=pos,
with_labels=True,
nodesize=1000,
node_color='lightgreen')
And if we increase the value of k to say 0.5, we get a nice spreading of the nodes in the layout:
pos_spaced=nx.fruchterman_reingold_layout(G, k=0.5, iterations=100)
plt.figure(figsize=(10,6))
nx.draw(G,
pos=pos_spaced,
with_labels=True,
nodesize=1000,
node_color='lightgreen')
How can I add just a few specific labels?
For this you set the labels parameters in draw to a dictionary containing the labels you want displayed. In the case the node names are the same as the labels, just create a dictionary mapping node->node as follows:
show_labels = ['plant', 'tree', 'oak', 'eucaliptus']
pos_spaced=nx.fruchterman_reingold_layout(G, k=0.54, iterations=100)
plt.figure(figsize=(10,6))
nx.draw(G,
pos=pos_spaced,
with_labels=True,
nodesize=1000,
labels=dict(zip(show_labels,show_labels)),
node_color='lightgreen')

How to create a colormap with the distance of routes

Currently I have a plot that shows all the shortest paths between all the nodes in my network and my target:
Now I would like to make a cmap, where I would color the origin nodes and the edges based on the distance of the shortest path. Can anyone help me?
Here is what I have:
import networkx as nx
import matplotlib.pyplot as plt
import osmnx as ox
import pandas as pd
import geopandas as gpd
from shapely.wkt import loads as load_wkt
ox.config(log_console=True, use_cache=True)
place = {'city': 'Lisbon', 'country': 'Portugal'}
G = ox.graph_from_place(place, network_type='drive')
G = ox.project_graph(G)
hospitals = ox.pois_from_place(place, amenities=['hospital'])
hosp_1 = hospitals.iloc[21]['geometry'] # Hospital Santa Maria (Polygon)
def poly_centroide(polygon):
# Gives me the coordinates of the center point of the Polygon
p1 = load_wkt(polygon)
centroide = p1.centroid.wkt
return centroide
polygon_1 = str(hosp_1)
coord_1_str = poly_centroide(polygon_1)
coord_1 = (38.74817825481225, -9.160815118526642) # Coordinates Hospital Santa Maria
target_1 = ox.get_nearest_node(G, coord_1)
routes = []
for node in G.nodes:
try:
route = nx.shortest_path(G, node, target_1)
routes.append(route)
except nx.exception.NetworkXNoPath:
continue
fig, ax = ox.plot_graph_routes(G, routes, edge_linewidth=0.2, node_size=5, route_linewidth=1)
plt.show()
Now I would like to know how to create the cmap where the colors of the nodes and edges are based on the distance of the shortest path.
I suspect it could be done with nx.dra() but I have no idea how...
Thank you in advance.
I have slightly added to your code. This will help in colouring the nodes based on their topological distance (since you did not pass any specific weight while calculating the shortest path, the shortest path is calculated based on the number of edges needed to be traversed to reach the destination as each edge is assigned a weight of 1).
I start after target_1 = ox.get_nearest_node(G, coord_1)
Obtain the nodes and edges geodataframes from the graph. We need the nodes geodataframe for this purpose.
nodes, edges = ox.graph_to_gdfs(G, nodes=True, edges=True)
We then calculate the shortest path, the shortest path length, and assign the latter to a new column in the nodes geodataframe.
nodes['shortest_route_length_to_target'] = 0
routes = []
route_lengths = []
i = 0
for node in G.nodes:
try:
route = nx.shortest_path(G, node, target_1)
route_length = nx.shortest_path_length(G, node, target_1)
routes.append(route)
route_lengths.append(route_length)
nodes['shortest_route_length_to_target'][node] = route_length
except nx.exception.NetworkXNoPath:
continue
Now we define the following functions. You will notice that these functions are the ones already existing in the file plot.py but are slightly modified for this purpose.
import numpy as np
import matplotlib.cm as cm
def get_colors(n, cmap='viridis', start=0., stop=1., alpha=1.,):
colors = [cm.get_cmap(cmap)(x) for x in np.linspace(start, stop, n)]
colors = [(r, g, b, alpha) for r, g, b, _ in colors]
return colors
def get_node_colors_by_attr(G, attr, num_bins=None, cmap='viridis', start=0, stop=1, na_color='none'):
if num_bins is None:
num_bins=len(G.nodes())
bin_labels = range(num_bins)
# attr_values = pd.Series([data[attr] for node, data in G.nodes(data=True)])
attr_values = pd.Series(nodes[attr].values)
cats = pd.qcut(x=attr_values, q=num_bins, labels=bin_labels)
colors = get_colors(num_bins, cmap, start, stop)
node_colors = [colors[int(cat)] if pd.notnull(cat) else na_color for cat in cats]
return node_colors
Now the following lines of code will give you your desired output.
nc = get_node_colors_by_attr(G, attr = 'shortest_route_length_to_target', num_bins=20,)
fig, ax = ox.plot_graph(G, node_color = nc, fig_height=20,)
You could vary the colormap (cmap) or the number of bins (num_bins) you wish to discretise the route_lengths values into.

Python Igraph Infomap node labels on graph

I have graph showed on first image, and I used infomap from igraph to make another, but on it I dont have node labels and that is so important fo me. How to add them, I tried everything but no node labels. It is strange that prints name of nodes like this,but no labels on graph:
IGRAPH U--- 86 41 --
+ edges:
0--57 65--67 2--66 2--67 2--23 66--67 57--68 68--81 6--66 33--74 74--84 37--74
32--76 57--81 32--84 37--84 70--84 32--85 58--85 22--24 22--23 23--31 24--25
24--31 30--31 32--33 32--57 42--48 42--46 42--55 43--48 43--55 43--47 44--49
46--48 46--55 47--54 47--55 49--51 54--55 57--58
Also,how to save that community graph to png or similar?
Code:
def nlargest_indices_orig(full, n):
full = full.copy()
x = np.zeros(n)
y = np.zeros(n)
for idx in range(n):
x[idx] = np.unravel_index(full.argmax(), full.shape)[0]
y[idx] = np.unravel_index(full.argmax(), full.shape)[1]
full[full == full.max()] = 0.
return x, y
labels=range(90)
o1 = scipy.io.loadmat('out.mat')
X=(o1['out'])
K=np.zeros((90,90))
m, n = np.shape(X)
print(m)
print(n)
G = nx.Graph()
#X[np.where(X<0.5)]=0
for i in range(90):
for j in range(90):
if X[i,j]>0:
s=labels[i]
b=labels[j]
w=X[i,j]
G.add_edge(s,b,weight=w)
B=G.edges()
print(len(B))
ND=len(B)
print('Grana ukupno')
print(ND)
procenat=round(0.1*ND)
print('procenat')
print(procenat)
x,y=nlargest_indices_orig(X, procenat)
s1=x
s2=y
for i in s1:
K[s1[i],s2[i]]=X[s1[i],s2[i]]
np.fill_diagonal(K, 0)
print('K')
print(K)
F = nx.Graph()
for i in range(90):
for j in range(90):
if K[i,j]>0:
s=labels[i]
b=labels[j]
w=X[i,j]
F.add_edge(s,b,weight=w)
edgewidth=[]
edgelabels={}
pos = nx.spring_layout(F) # position the nodes by force layout
plt.figure()
plt.axis('off')
print('Weighted graph')
for (u,v,d) in F.edges(data=True):
print(u,v,d)
edgewidth.append(d['weight'])
edgelabels[(u,v)] = d['weight']
nx.draw_networkx_edges(F,pos,width=edgewidth,edge_color='r')
nx.draw_networkx_nodes(F,pos, alpha=0.8, node_size=400,node_color='w',scale=100)
nx.draw_networkx_labels(F,pos, font_size=12)
pylab.savefig('Graf-s-10%.png')
plt.show()
A = F.edges()
E = ig.Graph(A)
print('E')
print(E)
community = E.community_infomap()
ig.plot( community,names=edgelabels)
You have two options to show labels on an igraph plot: either pass vertex_label parameter to igraph.plot(), or set the label vertex attribute, and igraph will use that automatically at plotting. To save the figure, simply call the save() method of the plot object. The format will be set automatically by the extension.
import igraph
g = igraph.Graph.Barabasi(n = 8, m = 2)
labels = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
p = igraph.plot(g,
vertex_label = labels,
vertex_size = 32,
vertex_frame_width = 0.0,
vertex_color = '#AAAAFF')
p.save('plot1.png')
Or creating the label vertex attribute:
g.vs['label'] = labels
igraph.plot(g)
Or if you want the indices to be used as labels:
igraph.plot(g, vertex_label = range(g.vcount()))

Categories

Resources