#!/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[ ]: