#!/usr/bin/env python
# coding: utf-8
# ##
Plotly interactive Human Disease Network
Selecting the neighboring disorders on node click ##
# The nodes of the human disease network (HDN) are disorders,
# and two disorders are connected if they share a genetic component. In order to identify the disorders that have common genetic components
# with a given node, we are clicking that node and `on_click` is executed a callback that performs a kind of neighboring nodes selection.
# In[1]:
import platform
print(f'Python version: {platform.python_version()}')
# In[2]:
import plotly
plotly.__version__
# In[3]:
import igraph as ig
import numpy as np
import ast
# In[4]:
import plotly.graph_objs as go
import ipywidgets as ipw
# Read the `gml` file as an `igraph.Graph`:
# In[5]:
K = ig.Graph.Read_GML('human-disease.gml')
# The following function extracts from the graph K, the data needed to plot it via Plotly:
# In[6]:
def get_Plotly_netw(G, graph_layout, title='Plotly Interactive Human Disease Network',
source=None, flip='lr', width=950, height=790): #h=850
# G is an igraph.Graph instance
# graph_layout is an igraph.Layout instance
# title - the network title
# flip is one of the strings 'lr', 'ud' to get a pseudo-flip effect
# the igraph.Layout is referenced to the screen system of coords, and is upside-down flipped chonging the sign of y-coords
# the global HDN looks better with the left-right flipped layout, by changing the x-coords sign.
#width and height are the sizes of the plot area
graph_layout = np.array(graph_layout)
if flip == 'lr':
graph_layout[:,0] =- graph_layout[:,0]
elif flip == 'ud':
graph_layout[:,1] =- graph_layout[:,1]
else:
raise ValueError('There is no such a flip type')
m = len(G.vs)
graph_edges = [e.tuple for e in G.es]# represent edges as tuples of end vertex indices
Xn = [graph_layout[k][0] for k in range(m)]#x-coordinates of graph nodes(vertices)
Yn = [graph_layout[k][1] for k in range(m)]#y-coordinates -"-
Xe = []#list of edge ends x-coordinates
Ye = []#list of edge ends y-coordinates
for e in graph_edges:
Xe.extend([graph_layout[e[0]][0],graph_layout[e[1]][0], None])
Ye.extend([graph_layout[e[0]][1],graph_layout[e[1]][1], None])
size = [vertex['size'] for vertex in G.vs]
#define the Plotly graphical objects
plotly_edges = go.Scatter(x=Xe,
y=Ye,
mode='lines',
line=dict(color='rgb(180,180,180)', width=0.75),
hoverinfo='none')
plotly_vertices = go.Scatter(x=Xn,
y=Yn,
mode='markers',
name='',
marker=dict(symbol='circle-dot',
size=size,
color=[vertex['color'] for vertex in G.vs],
line=dict(color='rgb(50,50,50)', width=0.5)
),
text=[f"{vertex['label']}
({vertex['disclass']})" for vertex in G.vs],
hoverinfo='text')
#Define the Plotly plot layout:
plotly_plot_layout = dict(title=title,
width=width,
height=height,
showlegend=False,
xaxis_visible=False,
yaxis_visible=False,
margin=dict(t=100, b=5),
hovermode='closest',
paper_bgcolor='rgb(235,235,235)',
template='none'
)
if source is not None:
annotations = [dict(showarrow=False,
text=source,
xref='paper',
yref='paper',
x=0,
y=-0.1,
xanchor='left',
yanchor='bottom',
font=dict(size=14)
)]
else:
annotations = []
disorder_name = [vertex['label'] for vertex in G.vs]
for k, s in enumerate(size):
if s>= 20:
annotations.append(dict(text=disorder_name[k],
x=graph_layout[k][0],
y=graph_layout[k][1],
xref='x1', yref='y1',
font=dict(color='rgb(50,50,50)', size=10),
showarrow=False))
plotly_plot_layout.update(annotations=annotations)
return go.FigureWidget(data=[plotly_edges, plotly_vertices], layout=plotly_plot_layout)
# Since the position of each node is recorded as a string, we convert it to a tuple of floats:
# In[7]:
pos = [ast.literal_eval(v['position']) for v in K.vs]
HDN_layout = np.array(pos)
# In[8]:
fig = get_Plotly_netw(K, HDN_layout)
# The callback function associated to a node click event, extracts the neighbhoring nodes of the clicked one, color the subset
# of these nodes with the same color, at choice, and the complementary nodes with gray. At the same time a `Text` widget displays the label and disorder class of the clicked disorder.
# In[9]:
textbox = ipw.Text(value='',
description='Neighbors of:',
disabled=False,
continuous_update=True)
textbox.layout = dict(margin='-35px 10px 10px 410px', width='450px') # place the textbox at the bottom right corner
# of the plotarea
# In[10]:
def select_neighbors(trace, points, state):
if not points.point_inds:
return
ind = points.point_inds[0]
new_color = np.array(['rgba(230, 230, 230, 0.9)']*len(trace.x))
# extract the indices of the clicked vertex neighbors
I = np.append(np.unique(np.array(K.neighbors(ind, mode='out'))), [ind])
new_color[I] = K.vs[ind]['color'] #color all neighbors with the color of the clicked node or with 'rgba(240, 0, 0, 0.9)'
trace.marker.color = new_color
textbox.value = f"{K.vs[ind]['label']} ({K.vs[ind]['disclass']})"
# In[11]:
fig.data[1].on_click(select_neighbors)
ipw.VBox([fig, textbox])
# In[12]:
get_ipython().run_cell_magic('html', '', "\n")
# In[ ]:
# In[ ]: