University of Florida provides a large sparse matrix collection: https://sparse.tamu.edu/.
Most of these matrices are adjacency matrices of some graphs. Here we illustrate how such a matrix can be converted to an igraph.Graph
instance, with a 3d layout, plotted via Plotly.
import numpy as np
import igraph as ig
from scipy.io import mmread
from scipy.sparse import issparse
import plotly.graph_objs as go
Function that returns an igraph.Graph
from a sparse matrix:
def igraph_from_sparse(A):
# Generate an undirected igraph.Graph from a symmetric sparse matrix read from a
# <MatrixMarket matrix coordinate pattern symmetric> file (mtx extension)
if not issparse(A):
raise ValueError('Your matrix is not sparse')
n_rows, n_cols = A.shape
if n_rows != n_cols:
raise ValueError('The adjacency matrix should be a square matrix ')
G = ig.Graph(n=n_rows, directed=False)
for i, row in enumerate(A.tolil().rows):
J = list(filter((i).__le__, row))
G.add_edges([(i,j) for j in J])
return G
Define a function that associates the nodes' distances to the mean position. These numerical values are then mapped to a color in a colorscale:
def dist_mean(position, axis=1):
# returns the distance of each node position to the mean position
position=np.asarray(position)
pos_mean = np.mean(position, axis=0)
return np.sum(np.sqrt((position - pos_mean) ** 2), axis=1)
The following three functions extract data for plotting the graph nodes and edges as scatter3d
Plotly plots:
def get_plotly_data(E, coords):
# E is the list of tuples representing the graph edges
# coords is the list of node coordinates
r, c = np.asarray(coords).shape
if c != 3:
raise ValueError('Node coordinates are not 3d')
Xnodes=[coords[k][0] for k in range(r)]# x-coordinates of nodes
Ynodes=[coords[k][1] for k in range(r)]# y-coordnates of nodes
Znodes=[coords[k][2] for k in range(r)]# z-coords of nodes
Xedges=[]
Yedges=[]
Zedges=[]
for e in E:
Xedges.extend([coords[e[0]][0], coords[e[1]][0], None])
Yedges.extend([coords[e[0]][1], coords[e[1]][1], None])
Zedges.extend([coords[e[0]][2], coords[e[1]][2], None])
return Xnodes, Ynodes, Znodes, Xedges, Yedges, Zedges
def get_node_trace(x, y, z, labels=None, marker_size=4, marker_color= 'blue', colorscale='Viridis',
reversescale=False, line_color='rgb(50,50,50)', line_width=0):
if isinstance(marker_color, str):
colorscale=None
showscale=False
return dict(type='scatter3d',
x=x,
y=y,
z=z,
mode='markers',
marker=dict(size=marker_size,
color= marker_color,
colorscale=colorscale,
reversescale=reversescale,
showscale=showscale,
colorbar=dict(thickness=20, ticklen=4, len=0.65),
line=dict(color=line_color,
width=line_width)),
text=labels,
hoverinfo='text')
def get_edge_trace(x, y, z, linecolor='rgb(150,150,150)', linewidth=1.5):
return dict(type='scatter3d',
x=x,
y=y,
z=z,
mode='lines',
line=dict(color=linecolor,
width=linewidth),
hoverinfo='none')
Read the adjacency matrix from a MTX file (the MTX file format is presented here: http://networkrepository.com/mtx-matrix-market-format.html)).
This MTX (or MM) file was derived from a FEM (Finite Element) problem, by the AG-Monien group.
The 3d data set of node positions associated by a 3d graph layout can be used for its topological data analysis, and manifold recognition https://arxiv.org/abs/1806.08460.
A = mmread('Data/airfoil1.mtx')
type(A)
scipy.sparse.coo.coo_matrix
Define an igraph.Graph
from this sparse matrix:
H = igraph_from_sparse(A)
len(H.vs)
4253
nodes = list(range(len(H.vs)))
edges= [e.tuple for e in H.es]
Associate node locations, via a 3d igraph.Layout
, like Kamada-Kawai - 'kk3d'
or Fruchtenberg-Reingold - 'fr3d'
layout:
#pos = H.layout('kk3d') # pos is a list of 3 lists
#pos =np.asarray(pos)
Scale the node positions, dividing each coordinate by max coordinates, and save pos
as a npy file to avoid graph layout computations at each notebook run:
#pos=pos/np.max(pos, axis=0)
#np.save('airfoil1-kk3d.npy', pos)
pos=np.load('Data/airfoil1-kk3d.npy')
pos.shape
(4253, 3)
Define the Plotly layout of the plot:
width = 850
height = 850
title = "3d network from a sparse matrix, derived from a FEM problem"+\
"<br>Data: <a href='https://www.cise.ufl.edu/research/sparse/matrices/AG-Monien/'>[1]</a>"
axis = dict(showbackground=True,
backgroundcolor="rgb(230, 230,230)",
gridcolor="rgb(255, 255, 255)",
zerolinecolor="rgb(255, 255, 255)")
x_eye, y_eye, z_eye = 1.45, 1.45, 0.85 #camera eye position
layout3d = dict(title=title,
font= dict(family='Balto'),
showlegend=False,
autosize=False,
width=width,
height=height,
scene=dict(xaxis= dict(axis),
yaxis=dict(axis),
zaxis=dict(axis),
aspectratio=dict(x=1., y=1., z=1),
camera=dict(eye=dict(x=x_eye, y=y_eye, z=z_eye))),
hovermode='closest',
)
Xn, Yn, Zn, Xe, Ye, Ze = get_plotly_data(edges, pos)
trace1 = get_edge_trace(Xe, Ye, Ze)
trace2 = get_node_trace(Xn, Yn, Zn, nodes, marker_size=2.5)
fw = go.FigureWidget(data=[trace1, trace2], layout=layout3d)
#fw
import plotly.plotly as py
py.sign_in('empet','api-key')
py.iplot(fw, filename='airfoil1-blue')
Let us update the FigureWidget
to color the graph nodes according to distance to mean:
pl_brewer = [[0.0, '#006837'], #from green to red http://colorbrewer2.org/#type=diverging&scheme=RdYlGn&n=11
[0.1, '#1a9850'],
[0.2, '#66bd63'],
[0.3, '#a6d96a'],
[0.4, '#d9ef8b'],
[0.5, '#ffffbf'],
[0.6, '#fee08b'],
[0.7, '#fdae61'],
[0.8, '#f46d43'],
[0.9, '#d73027'],
[1.0, '#a50026']]
fw.data[1].marker.update(color= dist_mean(pos),#
colorscale=pl_brewer,
showscale=True)
#fw
py.iplot(fw, filename='airfoil1kk')
from IPython.core.display import HTML
def css_styling():
styles = open("./custom.css", "r").read()
return HTML(styles)
css_styling()