Does NetworkX have a built-in way of scaling the nodes and edges proportional to the adjacency matrix frequency / node-node frequency? I am trying to scale the size of the nodes and text based on the adjacency matrix frequency and the weight of the edge based on the node-node frequency. I have created a frequency attribute for the graph, but that doesn't solve my problem of passing information to the graph about the node-node frequency.
So two part question:
1) What are best practices transferring an adjacency matrix into a networkX graph?
2) How do I use that information to scale the size of the nodes and the weight of the edges?
## Compute Graph (G)
G = nx.Graph(A)
## Add frequency of word as attribute of graph
def Freq_Attribute(G, A):
frequency = {} # Dictionary Declaration
for node in G.nodes():
frequency[str(node)] = A[str(node)][str(node)]
return nx.set_node_attributes(G, 'frequency', frequency)
Freq_Attribute(g,A) # Adds attribute frequency to graph, for font scale
## Plot Graph with Labels
plt.figure(1, figsize=(10,10))
# Set location of nodes as the default
pos = nx.spring_layout(G, k=0.50, iterations=30)
# Nodes
node_size = 10000
nodes1 = nx.draw_networkx_nodes(G,pos,
node_color='None',
node_size=node_size,
alpha=1.0) # nodelist=[0,1,2,3],
nodes1.set_edgecolor('#A9C1CD') # Set edge color to black
# Edges
edges = nx.draw_networkx_edges(G,pos,width=1,alpha=0.05,edge_color='black')
edges.set_zorder(3)
# Labels
nx.draw_networkx_labels(G,pos,labels=nx.get_node_attributes(G,'label'),
font_size=16,
font_color='#062D40',
font_family='arial') # sans-serif, Font=16
# node_labels = nx.get_node_attributes(g, 'name')
# Use 'g.graph' to find attribute(s): {'name': 'words'}
plt.axis('off')
#plt.show()
I have tried setting label font_size, but this didn't work.:
font_size=nx.get_node_attributes(G,'frequency')) + 8)
I tried the following to match your need:
import networkx as nx
import matplotlib.pyplot as plt
## create nx graph from adjacency matrix
def create_graph_from_adj(A):
# A=[(n1, n2, freq),....]
G = nx.Graph()
for a in A:
G.add_edge(a[0], a[1], freq=a[2])
return G
A = [(0, 1, 0.5), (1, 2, 1.0), (2, 3, 0.8), (0, 2, 0.2), (3, 4, 0.1), (2, 4, 0.6)]
## Compute Graph (G)
G = create_graph_from_adj(A)
plt.subplot(121)
# Set location of nodes as the default
spring_pose = nx.spring_layout(G, k=0.50, iterations=30)
nx.draw_networkx(G,pos=spring_pose)
plt.subplot(122)
# Nodes
default_node_size = 300
default_label_size = 12
node_size_by_freq = []
label_size_by_freq = []
for n in G.nodes():
sum_freq_in = sum([G.edge[n][t]['freq'] for t in G.neighbors(n)])
node_size_by_freq.append(sum_freq_in*default_node_size)
label_size_by_freq.append(int(sum_freq_in*default_label_size))
nx.draw_networkx_nodes(G,pos=spring_pose,
node_color='red',
node_size=node_size_by_freq,
alpha=1.0)
nx.draw_networkx_labels(G,pos=spring_pose,
font_size=12, #label_size_by_freq is not allowed
font_color='#062D40',
font_family='arial')
# Edges
default_width = 5.0
edge_width_by_freq = []
for e in G.edges():
edge_width_by_freq.append(G.edge[e[0]][e[1]]['freq']*default_width)
nx.draw_networkx_edges(G,pos=spring_pose,
width=edge_width_by_freq,
alpha=1.0,
edge_color='black')
plt.show()
First of all, the adjacency reaction is not given in Matrix format, but IMHO that's too tedious.
Secondly, nx.draw_networkx_labels does not allow different font size for the labels. Can't help there.
Last, the edge width and node size however allows that. So they are scaled based on its frequency and summation of incoming frequency, respectively.
Hope it helps.
Related
I'm working on a visualization project in networkx and plotly. Is there a way to create a 3D graph that resembles how a human brain looks like in networkx and then to visualize it with plotly (so it will be interactive)?
The idea is to have the nodes on the outside (or only show the nodes if it's easier) and to color a set of them differently like the image above
To start, this code is heavily borrowed from Matteo Mancini, which he describes here and he has released under the MIT license.
In the original code, networkx is not used, so it's clear you don't actually need networkx to accomplish your goal. If this is not a strict requirement, I would consider using his original code and reworking it to fit your input data.
Since you listed networkx as a requirement, I simply reworked his code to take a networkx Graph object with certain node attributes such as 'color' and 'coord' to be used for those marker characteristics in the final plotly scatter. I just chose the first ten points in the dataset to color red, which is why they aren't grouped.
The full copy-pasteable code is below. The screenshot here obviously isn't interactive, but you can try the demo here on Google Colab.
To download files if in Jupyter notebook on Linux/Mac:
!wget https://github.com/matteomancini/neurosnippets/raw/master/brainviz/interactive-network/lh.pial.obj
!wget https://github.com/matteomancini/neurosnippets/raw/master/brainviz/interactive-network/icbm_fiber_mat.txt
!wget https://github.com/matteomancini/neurosnippets/raw/master/brainviz/interactive-network/fs_region_centers_68_sort.txt
!wget https://github.com/matteomancini/neurosnippets/raw/master/brainviz/interactive-network/freesurfer_regions_68_sort_full.txt
Otherwise: download the required files here.
Code:
import numpy as np
import plotly.graph_objects as go
import networkx as nx # New dependency
def obj_data_to_mesh3d(odata):
# odata is the string read from an obj file
vertices = []
faces = []
lines = odata.splitlines()
for line in lines:
slist = line.split()
if slist:
if slist[0] == 'v':
vertex = np.array(slist[1:], dtype=float)
vertices.append(vertex)
elif slist[0] == 'f':
face = []
for k in range(1, len(slist)):
face.append([int(s) for s in slist[k].replace('//','/').split('/')])
if len(face) > 3: # triangulate the n-polyonal face, n>3
faces.extend([[face[0][0]-1, face[k][0]-1, face[k+1][0]-1] for k in range(1, len(face)-1)])
else:
faces.append([face[j][0]-1 for j in range(len(face))])
else: pass
return np.array(vertices), np.array(faces)
with open("lh.pial.obj", "r") as f:
obj_data = f.read()
[vertices, faces] = obj_data_to_mesh3d(obj_data)
vert_x, vert_y, vert_z = vertices[:,:3].T
face_i, face_j, face_k = faces.T
cmat = np.loadtxt('icbm_fiber_mat.txt')
nodes = np.loadtxt('fs_region_centers_68_sort.txt')
labels=[]
with open("freesurfer_regions_68_sort_full.txt", "r") as f:
for line in f:
labels.append(line.strip('\n'))
# Instantiate Graph and add nodes (with their coordinates)
G = nx.Graph()
for idx, node in enumerate(nodes):
G.add_node(idx, coord=node)
# Add made-up colors for the nodes as node attribute
colors_data = {node: ('gray' if node > 10 else 'red') for node in G.nodes}
nx.set_node_attributes(G, colors_data, name="color")
# Add edges
[source, target] = np.nonzero(np.triu(cmat)>0.01)
edges = list(zip(source, target))
G.add_edges_from(edges)
# Get node coordinates from node attribute
nodes_x = [data['coord'][0] for node, data in G.nodes(data=True)]
nodes_y = [data['coord'][1] for node, data in G.nodes(data=True)]
nodes_z = [data['coord'][2] for node, data in G.nodes(data=True)]
edge_x = []
edge_y = []
edge_z = []
for s, t in edges:
edge_x += [nodes_x[s], nodes_x[t]]
edge_y += [nodes_y[s], nodes_y[t]]
edge_z += [nodes_z[s], nodes_z[t]]
# Get node colors from node attribute
node_colors = [data['color'] for node, data in G.nodes(data=True)]
fig = go.Figure()
# Changed color and opacity kwargs
fig.add_trace(go.Mesh3d(x=vert_x, y=vert_y, z=vert_z, i=face_i, j=face_j, k=face_k,
color='gray', opacity=0.1, name='', showscale=False, hoverinfo='none'))
fig.add_trace(go.Scatter3d(x=nodes_x, y=nodes_y, z=nodes_z, text=labels,
mode='markers', hoverinfo='text', name='Nodes',
marker=dict(
size=5, # Changed node size...
color=node_colors # ...and color
)
))
fig.add_trace(go.Scatter3d(x=edge_x, y=edge_y, z=edge_z,
mode='lines', hoverinfo='none', name='Edges',
opacity=0.3, # Added opacity kwarg
line=dict(color='pink') # Added line color
))
fig.update_layout(
scene=dict(
xaxis=dict(showticklabels=False, visible=False),
yaxis=dict(showticklabels=False, visible=False),
zaxis=dict(showticklabels=False, visible=False),
),
width=800, height=600
)
fig.show()
Based on the clarified requirements, I took a new approach:
Download accurate brain mesh data from BrainNet Viewer github repo;
Plot a random graph with 3D-coordinates using Kamada-Kuwai cost function in three dimensions centered in a sphere containing the brain mesh;
Radially expand the node positions away from the center of the brain mesh and then shift them back to the closest vertex actually on the brain mesh;
Color some nodes red based on an arbitrary distance criterion from a randomly selected mesh vertex;
Fiddle with a bunch of plotting parameters to make it look decent.
There is a clearly delineated spot to add in different graph data as well as change the logic by which the node colors are decided. The key parameters to play with so that things look decent after introducing new graph data are:
scale_factor: This changes how much the original Kamada-Kuwai calculated coordinates are translated radially away from the center of the brain mesh before they are snapped back to its surface. Larger values will make more nodes snap to the outer surface of the brain. Smaller values will leave more nodes positioned on the surfaces between the two hemispheres.
opacity of the lines in the edge trace: Graphs with more edges will quickly clutter up field of view and make the overall brain shape less visible. This speaks to my biggest dissatisfaction with this overall approach -- that edges which appear outside of the mesh surface make it harder to see the overall shape of the mesh, especially between the temporal lobes.
My other biggest caveat here is that there is no attempt has been made to check whether any nodes positioned on the brain surface happen to coincide or have any sort of equal spacing.
Here is a screenshot and the live demo on Colab. Full copy-pasteable code below.
There are a whole bunch of asides that could be discussed here, but for brevity I will only note two:
Folks interested in this topic but feeling overwhelmed by programming details should absolutely check out BrainNet Viewer;
There are plenty of other brain meshes in the BrainNet Viewer github repo that could be used. Even better, if you have any mesh which can be formatted or reworked to be compatible with this approach, you could at least try wrapping a set of nodes around any other non-brain and somewhat round-ish mesh representing any other object.
import plotly.graph_objects as go
import numpy as np
import networkx as nx
import math
def mesh_properties(mesh_coords):
"""Calculate center and radius of sphere minimally containing a 3-D mesh
Parameters
----------
mesh_coords : tuple
3-tuple with x-, y-, and z-coordinates (respectively) of 3-D mesh vertices
"""
radii = []
center = []
for coords in mesh_coords:
c_max = max(c for c in coords)
c_min = min(c for c in coords)
center.append((c_max + c_min) / 2)
radius = (c_max - c_min) / 2
radii.append(radius)
return(center, max(radii))
# Download and prepare dataset from BrainNet repo
coords = np.loadtxt(np.DataSource().open('https://raw.githubusercontent.com/mingruixia/BrainNet-Viewer/master/Data/SurfTemplate/BrainMesh_Ch2_smoothed.nv'), skiprows=1, max_rows=53469)
x, y, z = coords.T
triangles = np.loadtxt(np.DataSource().open('https://raw.githubusercontent.com/mingruixia/BrainNet-Viewer/master/Data/SurfTemplate/BrainMesh_Ch2_smoothed.nv'), skiprows=53471, dtype=int)
triangles_zero_offset = triangles - 1
i, j, k = triangles_zero_offset.T
# Generate 3D mesh. Simply replace with 'fig = go.Figure()' or turn opacity to zero if seeing brain mesh is not desired.
fig = go.Figure(data=[go.Mesh3d(x=x, y=y, z=z,
i=i, j=j, k=k,
color='lightpink', opacity=0.5, name='', showscale=False, hoverinfo='none')])
# Generate networkx graph and initial 3-D positions using Kamada-Kawai path-length cost-function inside sphere containing brain mesh
G = nx.gnp_random_graph(200, 0.02, seed=42) # Replace G with desired graph here
mesh_coords = (x, y, z)
mesh_center, mesh_radius = mesh_properties(mesh_coords)
scale_factor = 5 # Tune this value by hand to have more/fewer points between the brain hemispheres.
pos_3d = nx.kamada_kawai_layout(G, dim=3, center=mesh_center, scale=scale_factor*mesh_radius)
# Calculate final node positions on brain surface
pos_brain = {}
for node, position in pos_3d.items():
squared_dist_matrix = np.sum((coords - position) ** 2, axis=1)
pos_brain[node] = coords[np.argmin(squared_dist_matrix)]
# Prepare networkx graph positions for plotly node and edge traces
nodes_x = [position[0] for position in pos_brain.values()]
nodes_y = [position[1] for position in pos_brain.values()]
nodes_z = [position[2] for position in pos_brain.values()]
edge_x = []
edge_y = []
edge_z = []
for s, t in G.edges():
edge_x += [nodes_x[s], nodes_x[t]]
edge_y += [nodes_y[s], nodes_y[t]]
edge_z += [nodes_z[s], nodes_z[t]]
# Decide some more meaningful logic for coloring certain nodes. Currently the squared distance from the mesh point at index 42.
node_colors = []
for node in G.nodes():
if np.sum((pos_brain[node] - coords[42]) ** 2) < 1000:
node_colors.append('red')
else:
node_colors.append('gray')
# Add node plotly trace
fig.add_trace(go.Scatter3d(x=nodes_x, y=nodes_y, z=nodes_z,
#text=labels,
mode='markers',
#hoverinfo='text',
name='Nodes',
marker=dict(
size=5,
color=node_colors
)
))
# Add edge plotly trace. Comment out or turn opacity to zero if not desired.
fig.add_trace(go.Scatter3d(x=edge_x, y=edge_y, z=edge_z,
mode='lines',
hoverinfo='none',
name='Edges',
opacity=0.1,
line=dict(color='gray')
))
# Make axes invisible
fig.update_scenes(xaxis_visible=False,
yaxis_visible=False,
zaxis_visible=False)
# Manually adjust size of figure
fig.update_layout(autosize=False,
width=800,
height=800)
fig.show()
A possible way to do that:
import networkx as nx
import random
import numpy as np
import plotly.express as px
import plotly.graph_objects as go
from stl import mesh
# function to convert stl 3d-model to mesh
# Taken from : https://chart-studio.plotly.com/~empet/15276/converting-a-stl-mesh-to-plotly-gomes/#/
def stl2mesh3d(stl_mesh):
# stl_mesh is read by nympy-stl from a stl file; it is an array of faces/triangles (i.e. three 3d points)
# this function extracts the unique vertices and the lists I, J, K to define a Plotly mesh3d
p, q, r = stl_mesh.vectors.shape #(p, 3, 3)
# the array stl_mesh.vectors.reshape(p*q, r) can contain multiple copies of the same vertex;
# extract unique vertices from all mesh triangles
vertices, ixr = np.unique(stl_mesh.vectors.reshape(p*q, r), return_inverse=True, axis=0)
I = np.take(ixr, [3*k for k in range(p)])
J = np.take(ixr, [3*k+1 for k in range(p)])
K = np.take(ixr, [3*k+2 for k in range(p)])
return vertices, I, J, K
# Let's use a toy "brain" stl file. You can get it from my Dropbox: https://www.dropbox.com/s/lav2opci8vekaep/brain.stl?dl=0
#
# Note: I made it quick and dirty whith Blender and is not supposed to be an accurate representation
# of an actual brain. You can put your own model here.
my_mesh = mesh.Mesh.from_file('brain.stl')
vertices, I, J, K = stl2mesh3d(my_mesh)
x, y, z = vertices.T # x,y,z contain the stl vertices
# Let's generate a random spatial graph:
# Note: spatial graphs have a "pos" (position) attribute
# pos = nx.get_node_attributes(G, "pos")
G = nx.random_geometric_graph(30, 0.3, dim=3) # in dimension 3 --> pos = [x,y,z]
#nx.draw(G)
print('Nb. of nodes: ',G.number_of_nodes(), 'Nb. of edges: ',G.number_of_edges())
# Take G.number_of_nodes() of nodes and attribute them randomly to points in the list of vertices of the STL model:
# That is, we "scatter" the nodes on the brain surface:
Vec3dList=list(np.array(random.sample(list(vertices), G.number_of_nodes())))
for i in range(len(Vec3dList)):
G.nodes[i]['pos']=Vec3dList[i]
# Create nodes and edges graph objects:
# Code from: https://plotly.com/python/network-graphs/ modified to work with 3d graphs
edge_x = []
edge_y = []
edge_z = []
for edge in G.edges():
x0, y0, z0 = G.nodes[edge[0]]['pos']
x1, y1, z1 = G.nodes[edge[1]]['pos']
edge_x.append(x0)
edge_x.append(x1)
edge_x.append(None)
edge_y.append(y0)
edge_y.append(y1)
edge_y.append(None)
edge_z.append(z0)
edge_z.append(z1)
edge_z.append(None)
edge_trace = go.Scatter3d(
x=edge_x, y=edge_y, z=edge_z,
line=dict(width=2, color='#888'),
hoverinfo='none',
opacity=.3,
mode='lines')
node_x = []
node_y = []
node_z = []
for node in G.nodes():
X, Y, Z = G.nodes[node]['pos']
node_x.append(X)
node_y.append(Y)
node_z.append(Z)
node_trace = go.Scatter3d(
x=node_x, y=node_y,z=node_z,
mode='markers',
hoverinfo='text',
marker=dict(
showscale=True,
# colorscale options
#'Greys' | 'YlGnBu' | 'Greens' | 'YlOrRd' | 'Bluered' | 'RdBu' |
#'Reds' | 'Blues' | 'Picnic' | 'Rainbow' | 'Portland' | 'Jet' |
#'Hot' | 'Blackbody' | 'Earth' | 'Electric' | 'Viridis' |
colorscale='YlGnBu',
reversescale=True,
color=[],
size=5,
colorbar=dict(
thickness=15,
title='Node Connections',
xanchor='left',
titleside='right'
),
line_width=10))
node_adjacencies = []
node_text = []
for node, adjacencies in enumerate(G.adjacency()):
node_adjacencies.append(len(adjacencies[1]))
node_text.append('# of connections: '+str(len(adjacencies[1])))
node_trace.marker.color = node_adjacencies
node_trace.text = node_text
colorscale= [[0, '#e5dee5'], [1, '#e5dee5']]
mesh3D = go.Mesh3d(
x=x,
y=y,
z=z,
i=I,
j=J,
k=K,
flatshading=False,
colorscale=colorscale,
intensity=z,
name='Brain',
opacity=0.25,
hoverinfo='none',
showscale=False)
title = "Brain"
layout = go.Layout(paper_bgcolor='rgb(1,1,1)',
title_text=title, title_x=0.5,
font_color='white',
width=800,
height=800,
scene_camera=dict(eye=dict(x=1.25, y=-1.25, z=1)),
scene_xaxis_visible=False,
scene_yaxis_visible=False,
scene_zaxis_visible=False)
fig = go.Figure(data=[mesh3D, edge_trace, node_trace], layout=layout)
fig.data[0].update(lighting=dict(ambient= .2,
diffuse= 1,
fresnel= 1,
specular= 1,
roughness= .1,
facenormalsepsilon=0))
fig.data[0].update(lightposition=dict(x=3000,
y=3000,
z=10000));
fig.show()
Below, the result. As you can see the result is not that great... But, maybe, you can improve on it.
Best regards
Vispy library might be useful https://github.com/vispy/vispy.
I think you can use the following examples.
3D brain mesh viewer
1ex output
Plot various views of a structural MRI.
2ex output
Clipping planes with volume and markers
3ex output
These examples are interactive.
Regards!
I have an example.cvc file containing the following values:
pos= [180 270 360 450 540 630 720 810]
mean_values= [(270,630), (540,720),(270,810),(450,630),(180,360), (180,540),(450,810),(360,540),(180,720),(630,810),(270,450),(360,720)]
The mean values are basically from 2d gaussian mixture model. Based on my understanding labels can represent vertices (8) and mean_values can be called edges (12).
With regards to data: pos basically represented by the blue and yellow circles values while means values correspond to which one is related to which pos. So for example, among 180,360,540 and 720 label values, 180 connected to 360,540, 720 as seen by arrows and represented by following means values: [(180,360), (180,540),(180,720)] and the similar result can be found with other pos,
Any idea of how to get such a result using igraph. I did a couple of searches but did not get any idea. I am new to igraph, any help would be appreciated. Thanks in advance
First, you need to compute the closest set of points like this (Note that I took th e liberty of reading the data from a list instead of a file, you can change it accordingly):
import numpy as np
a = np.asarray([640., 270., 450., 230., 180., 540., 270., 180., 450., 360., 610.,
360.])
b = np.asarray([810., 630., 810., 760., 360., 720., 450., 540., 630., 720., 810.,
540.])
closest_mapping = []
for node in a:
# Subtract node from each element in b
# and get the absolute value
dist_list = np.absolute(np.array(b) - node)
# Find the element in b with the min. absolute value
min_element = b[np.argmin(dist_list)]
# Create a tuple of (node, min_element) and add it to list.
# This will be used to plot a graph later.
# Note that the second element is stored as a string.
closest_mapping.append((node, str(min_element)))
The reason why I have stored the second element as string will be cleared when we plot the graph. You can see the points yourself to verify
print(closest_mapping)
#[(640.0, '630.0'),
# (270.0, '360.0'),
# (450.0, '450.0'),
# (230.0, '360.0'),
# (180.0, '360.0'),
# (540.0, '540.0'),
# (270.0, '360.0'),
# (180.0, '360.0'),
# (450.0, '450.0'),
# (360.0, '360.0'),
# (610.0, '630.0'),
# (360.0, '360.0')]
I don't know how to plot a bipartite graph using igraph, so I will be using NetworkX for this:
import networkx as nx
# Create an empty graph
G = nx.Graph()
# Add the edges from the list we created
G.add_edges_from(closest_mapping)
# Create a bipartite layout
pos = nx.bipartite_layout(G, a)
# Draw the Graph
nx.draw(G, pos, with_labels=True, node_size=900, node_color='y')
The nodes on the left side are from A while the nodes on the right are from B.
If you want to find closest for every pair of nodes
import numpy as np
import networkx as nx
a = np.asarray([640., 270., 450., 230., 180., 540., 270., 180., 450., 360., 610.,
360.])
b = np.asarray([810., 630., 810., 760., 360., 720., 450., 540., 630., 720., 810.,
540.])
closest_mapping = []
# Find mapping for A->B
for node in a:
# Subtract node from each element in b
# and get the absolute value
dist_list = np.absolute(np.array(b) - node)
# Find the element in b with the min. absolute value
min_element = b[np.argmin(dist_list)]
# Create a tuple of (node, min_element) and add it to list.
# This will be used to plot a graph later.
# Note that the second element is stored as a string.
closest_mapping.append((node, str(min_element)))
# Find Mapping for B->A
for node in b:
# Subtract node from each element in b
# and get the absolute value
dist_list = np.absolute(np.array(a) - node)
# Find the element in b with the min. absolute value
min_element = a[np.argmin(dist_list)]
# Create a tuple of (node, min_element) and add it to list.
# This will be used to plot a graph later.
# Note that the first element is stored as a string.
closest_mapping.append((str(node), min_element))
# Create an empty graph
G = nx.Graph()
# Add the edges from the list we created
G.add_edges_from(closest_mapping)
# Create a bipartite layout
pos = nx.bipartite_layout(G, a)
# Draw the Graph
nx.draw(G, pos, with_labels=True, node_size=900, node_color='y')
Note The reason why nodes from list B are stored as strings is that if they are kept as integers/floats and they have the same value in A the Graph will not be bipartite (It will not register duplicate nodes even though both are logically different, hence I kept one list of nodes as string).
UPDATE:
Based on the updated question, you directly add the nodes and edges using NetworkX like this:
import networkx as nx
pos= [180, 270, 360, 450, 540, 630, 720, 810]
mean_values= [(270,630), (540,720), (270,810), (450,630), (180,360),
(180,540), (450,810), (360,540), (180,720), (630,810),
(270,450), (360,720)]
G = nx.Graph()
G.add_nodes_from(pos)
G.add_edges_from(mean_values)
pos = nx.spring_layout(G)
nx.draw(G, pos, node_size=500, with_labels=True, node_color='y')
You can use nx.DiGraph for directed edges like this
import networkx as nx
pos= [180, 270, 360, 450, 540, 630, 720, 810]
mean_values= [(270,630), (540,720), (270,810), (450,630), (180,360),
(180,540), (450,810), (360,540), (180,720), (630,810),
(270,450), (360,720)]
G = nx.DiGraph()
G.add_nodes_from(pos)
G.add_edges_from(mean_values)
pos = nx.spring_layout(G)
nx.draw(G, pos, node_size=500, with_labels=True, node_color='y')
References:
Bipartite Graphs in NetworkX
Bipartite Layout
Numpy argmin
I have a graph with lots of nodes. I am going to segregate them based on their timesteps. Let's say I have 3 timesteps and I have 5 nodes for each time step. So I want to create a 3-columned graph where each column has 5 nodes. So if we imagine, it needs to like a bipartite layout.
But this is not a bipartite graph and hence I am not sure how to proceed
# Greate your Bipartite graph object
G = nx.Graph()
y_out = [6, 6, 12 ,8, 23, 23]
# Add nodes to set 0 and 1
G.add_nodes_from(y_out, bipartite=0)
G.add_nodes_from(['a6', 'b6', 'c12', 'd8', 'd23', 'e23'], bipartite=1)
# # Add edges between exclusive sets
# G.add_edges_from([dets_tuple])
top_nodes = {n for n, d in G.nodes(data=True) if d['bipartite'] == 0}
bottom_nodes = set(G) - top_nodes
print("top:", top_nodes)
print("bottom: " ,bottom_nodes)
# Visualize the graph
plt.subplot(121)
nx.draw_networkx(
G, pos=nx.drawing.layout.bipartite_layout(G, top_nodes))
plt.show()
UPDATED CODE:
unique_frames = set(y_out[:, 0])
print("Generate dynamic graph")
# Visualize the graph
plt.subplot(121)
for i in unique_frames:
# find indices that belong to the same frame
idx = np.where(y_out[:, 0] == i)
print("Comparing frame no: ", i)
print("Idx : ", idx)
# embed()
curr_nodes = np.asarray(idx)[0]
# Add nodes to set 0 and 1
G.add_nodes_from(curr_nodes, bipartite=i)
# compute partition nodes
top_nodes = {n for n, d in G.nodes(data=True) if d['bipartite'] == i}
# Draw Graph
nx.draw_networkx(G, pos=nx.drawing.layout.bipartite_layout(G, top_nodes))
plt.show(block=False)
plt.pause(0.5)
This literally shows me a bipartite graph. I have plugged in the nodes temporarily so that it respects the bipartite rule. But ideally I can not have one. How can I construct a normal multiple connected graph with a bipartite layout
Update: The new updated code is creating graphs, but on the same column. I not sure how to space them horizontally
I am working with NetworkX in order to evaluate the dynamics that occur in a network. Then I visually represent the result. I work with the following:
#Step 1: Creating the original network
import networkx as nx
import matplotlib.pyplotas plt
N=100
G=nx.grid_2d_graph(N,N) #2D regular graph of 10000 nodes
pos = dict( (n, n) for n in G.nodes() ) #Dict of positions
labels = dict( ((i, j), i + (N-1-j) * N ) for i, j in G.nodes() )
nx.relabel_nodes(G,labels,False)
pos = {y:x for x,y in labels.iteritems()} #Renamed positions. Position (0,0) is in the upper left corner now.
H=G.copy()
#Step 2: Dynamics modeling block
#Step 3: Plotting the resulting network
G2=dict((k, v) for k, v in status_nodes_model.items() if v < 1) #All nodes that must be removed from the regular grid
H.remove_nodes_from(G2)
nx.draw_networkx(H, pos=pos, with_labels=False, node_size = 10)
plt.xlim(-20,120,10)
plt.xticks(numpy.arange(-20, 130, 20.0))
plt.ylim(-20,120,10)
plt.yticks(numpy.arange(-20, 130, 20.0))
plt.axis('on')
plt.plot((0, 100), (0, 0), '0.60') #Line1 - visual boundary for failure stages
plt.plot((0, 0), (0, 100), '0.60') #Line2 - visual boundary for failure stages
plt.plot((0, 100), (100, 100), '0.60') #Line3 - visual boundary for failure stages
plt.plot((100, 100), (0, 100), '0.60') #Line4 - visual boundary for failure stages
title_string=('Lattice Network, Stage 2')
subtitle_string=('Impact & dynamics | Active nodes: '+str(act_nodes_model)+' | Failed nodes: '+str(failed_nodes_haz+failed_nodes_model)+' | Total failure percentage: '+str(numpy.around(100*(failed_nodes_haz+failed_nodes_model)/10000,2))+'%')
plt.suptitle(title_string, y=0.99, fontsize=17)
plt.title(subtitle_string, fontsize=8)
What I visually get is an UNWANTED MIRRORING. Please see below:
My question: is there a way of mirroring the output image both horizontally and vertically, so that the image to the right is obtained, but with readable titles and labels?
You just need to change the values in pos.
my_pos = {}
for key in pos.keys():
x,y = pos[key]
my_pos[key] = (-x,-y)#or whatever function you want
nx.draw_networkx(H, pos=my_pos, with_labels=False, node_size = 10)
I'm trying to visualize a few graphs whose nodes represent different objects. I want to create an image that looks like the one here:
Basically, I need a 3D plot and the ability to draw edges between nodes on the same level or nodes on different levels.
This answer below may not be a complete solution, but is a working demo for rendering 3D graphs using networkx.
networkx as such cannot render 3D graphs. We will have to install mayavi for that to happen.
import networkx as nx
import matplotlib.pyplot as plt
import numpy as np
from mayavi import mlab
import random
def draw_graph3d(graph, graph_colormap='winter', bgcolor = (1, 1, 1),
node_size=0.03,
edge_color=(0.8, 0.8, 0.8), edge_size=0.002,
text_size=0.008, text_color=(0, 0, 0)):
H=nx.Graph()
# add edges
for node, edges in graph.items():
for edge, val in edges.items():
if val == 1:
H.add_edge(node, edge)
G=nx.convert_node_labels_to_integers(H)
graph_pos=nx.spring_layout(G, dim=3)
# numpy array of x,y,z positions in sorted node order
xyz=np.array([graph_pos[v] for v in sorted(G)])
# scalar colors
scalars=np.array(G.nodes())+5
mlab.figure(1, bgcolor=bgcolor)
mlab.clf()
#----------------------------------------------------------------------------
# the x,y, and z co-ordinates are here
# manipulate them to obtain the desired projection perspective
pts = mlab.points3d(xyz[:,0], xyz[:,1], xyz[:,2],
scalars,
scale_factor=node_size,
scale_mode='none',
colormap=graph_colormap,
resolution=20)
#----------------------------------------------------------------------------
for i, (x, y, z) in enumerate(xyz):
label = mlab.text(x, y, str(i), z=z,
width=text_size, name=str(i), color=text_color)
label.property.shadow = True
pts.mlab_source.dataset.lines = np.array(G.edges())
tube = mlab.pipeline.tube(pts, tube_radius=edge_size)
mlab.pipeline.surface(tube, color=edge_color)
mlab.show() # interactive window
# create tangled hypercube
def make_graph(nodes):
def make_link(graph, i1, i2):
graph[i1][i2] = 1
graph[i2][i1] = 1
n = len(nodes)
if n == 1: return {nodes[0]:{}}
nodes1 = nodes[0:n/2]
nodes2 = nodes[n/2:]
G1 = make_graph(nodes1)
G2 = make_graph(nodes2)
# merge G1 and G2 into a single graph
G = dict(G1.items() + G2.items())
# link G1 and G2
random.shuffle(nodes1)
random.shuffle(nodes2)
for i in range(len(nodes1)):
make_link(G, nodes1[i], nodes2[i])
return G
# graph example
nodes = range(10)
graph = make_graph(nodes)
draw_graph3d(graph)
This code was modified from one of the examples here.
Please post the code in this case, when you succeed in reaching the objective.