Tuesday, 3/24/2015
You can visualize network data set using easy-to-use Cytoscape GUI and you can save the final result as a session file. But if you need to create similar visualizations, you need to repeat the manual (point-and-click) operations again. This will be more and more time consuming process as your data sets grows. Good news is, you can avoid it by writing your visualization workflow as Notebooks!
In [1]:
import requests
import json
from IPython.display import Image
import pandas as pd
# Basic Setup
PORT_NUMBER = 1234
IP = '137.110.137.158' # Dont' forget to update this!!!!!!!!!!!!!!!
BASE = 'http://' + IP + ':' + str(PORT_NUMBER) + '/v1/'
HEADERS = {'Content-Type': 'application/json'}
# Utulity to POST object
def create(param, dict_data):
return requests.post(BASE + param, data=json.dumps(dict_data), headers=HEADERS)
def update(param, dict_data):
return requests.put(BASE + param, data=json.dumps(dict_data), headers=HEADERS)
# Start from a clean slate!
requests.delete(BASE + 'session')
Out[1]:
To visuaize your data with Cytoscape, you need to understand its data-to-view mapping mechanism called Visual Style.
Visual Style is a collection of instructions how to map your data to visual properties (or visual variables). To use it to make effective visualizations, you need to understand the following concepts.
In [46]:
# Get a list of all available Visual Styles
vs_url = BASE + 'styles'
res = requests.get(vs_url)
style_df = pd.DataFrame(res.json(), columns=['Style Name'])
style_df
Out[46]:
Visual Properties, sometimes called visual variables in other applications, are properties of viewable objects, such as color, shape, opacity, size, etc.
You can get the complete list of Visual Properties with the following code:
In [47]:
vps_url = BASE + 'styles/visualproperties'
print(vps_url)
# Let's make it a bit more human readable
res = requests.get(vps_url)
vp_df = pd.DataFrame(res.json(), columns=['visualProperty', 'name', 'targetDataType', 'default'])
vp_df.tail()
Out[47]:
In [48]:
res = requests.get(vps_url + '/NODE_SHAPE/values')
node_shapes = pd.DataFrame(res.json()['values'], columns=['Node Shapes'])
node_shapes
Out[48]:
In [49]:
directed_style_url = BASE + 'styles/default/defaults'
print(directed_style_url)
res = requests.get(directed_style_url)
defaults_df = pd.DataFrame(res.json()['defaults'], columns=['visualProperty', 'value'])
defaults_df.head()
Out[49]:
Let's try with actual network data.
In [50]:
# Load a network file
f = open('data/yeast.json', 'r')
cyjs_network = json.load(f)
cyjs_network['data']['name'] = 'Yeast 1'
res = create('networks', cyjs_network)
suid = res.json()['networkSUID']
# Apply Visual Style "default"
requests.get(BASE + 'apply/styles/default')
requests.get(BASE + 'apply/layouts/degree-circle/' + str(suid))
paints = defaults_df[defaults_df['visualProperty'].str.contains('COLOR')]
paints.tail(10)
Out[50]:
In [51]:
Image(BASE+'networks/' + str(suid) + '/views/first.png')
Out[51]:
In [52]:
# Let's change some default values.
new_values = [
{
'visualProperty':'NODE_FILL_COLOR',
'value': '#7080BF'
},
{
'visualProperty':'NODE_BORDER_WIDTH',
'value': 0
},
{
'visualProperty':'NODE_LABEL_FONT_SIZE',
'value': 90
},
{
'visualProperty':'NODE_WIDTH',
'value': 60
},
{
'visualProperty': 'EDGE_WIDTH',
'value': 7
},
{
'visualProperty': 'NODE_TRANSPARENCY',
'value': 220
},
{
'visualProperty': 'EDGE_TRANSPARENCY',
'value': 200
}
]
update('styles/default/defaults', new_values)
Image(BASE+'networks/' + str(suid) + '/views/first.png')
Out[52]:
In [53]:
# Write your code here...
In [54]:
# Create a Passthrough mapping
new_passthrough_mapping = {
'mappingType': 'passthrough',
'mappingColumn': 'degree.layout',
'mappingColumnType': 'Integer',
'visualProperty': 'NODE_LABEL',
}
# Create a new style by POSTing the object
create('styles/default/mappings', [new_passthrough_mapping])
Image(BASE+'networks/' + str(suid) + '/views/first.png')
Out[54]:
Discrete mapping is the one maps discrete data points to any Visual Properties. Here is the list of typical discrete mappings:
In [55]:
# Create discrete mapping
new_disc_mapping = {
'mappingType': 'discrete',
'mappingColumn': 'interaction',
'mappingColumnType': 'String',
'visualProperty': 'EDGE_STROKE_UNSELECTED_PAINT',
'map':[
{
'key': 'pp',
'value': '#8899aa'
},
{
'key': 'pd',
'value': '#4876cc'
}
]
}
line_style_mapping = {
'mappingType': 'discrete',
'mappingColumn': 'interaction',
'mappingColumnType': 'String',
'visualProperty': 'EDGE_LINE_STYLE',
'map':[
{
'key': 'pp',
'value': 'SOLID'
},
{
'key': 'pd',
'value': 'LONG_DASH'
}
]
}
create('styles/default/mappings', [new_disc_mapping])
Image(BASE+'networks/' + str(suid) + '/views/first.png')
Out[55]:
In [56]:
new_cont_mapping = {
'mappingType': 'continuous',
'mappingColumn': 'degree.layout',
'mappingColumnType': 'Integer',
'visualProperty': 'NODE_HEIGHT',
'points':[
{
'value': 1,
'lesser': '20',
'equal': '20',
'greater': '20'
},
{
'value': 20,
'lesser': '3000',
'equal': '3000',
'greater': '3000'
}
]
}
new_color_mapping = {
'mappingType': 'continuous',
'mappingColumn': 'degree.layout',
'mappingColumnType': 'Integer',
'visualProperty': 'NODE_FILL_COLOR',
'points':[
{
'value': 1,
'lesser': '#EEEEEE',
'equal': '#EEEEEE',
'greater': '#EEEEEE'
},
{
'value': 20,
'lesser': '#333333',
'equal': '#333333',
'greater': '#333333'
}
]
}
create('styles/default/mappings', [new_cont_mapping, new_color_mapping])
Image(BASE+'networks/' + str(suid) + '/views/first.png')
Out[56]:
Now you can see the new mapping in the Cytoscape's Control Panel:
In [57]:
# Apply force-directd layout
requests.get(BASE + 'apply/layouts/force-directed/' + str(suid))
Image(BASE+'networks/' + str(suid) + '/views/first.png')
Out[57]:
OK, now you can create any mappings from Python. But editing code like this manyally is a pain...
The true power of workflow-as-code is its reproducibility. You can easily apply your existing code to other data sets. The first step to reusability is writing common tasks as functions.
In [58]:
def get_basic_mapping(map_type, column, column_type, vp):
new_mapping = {
'mappingType': map_type,
'mappingColumn': column,
'mappingColumnType': column_type,
'visualProperty': vp,
}
return new_mapping
def get_discrete_mapping(column, column_type, vp):
mapping = get_basic_mapping('discrete', column, column_type, vp)
mapping['map'] = []
return mapping
def get_continuous_mapping(column, column_type, vp):
mapping = get_basic_mapping('continuous', column, column_type, vp)
mapping['points'] = []
return mapping
def get_passthrough_mapping(column, column_type, vp):
return get_basic_mapping('passthrough', column, column_type, vp)
And now you can do all kinds of crazy thing if you want...
In [59]:
# Find network ID
res = requests.get(BASE + 'networks/' + str(suid) + '/nodes')
node_suids = res.json()
import random
random_colors = []
for node in node_suids:
entry = {
'key': str(node),
'value': '#' + format(random.randint(1,255), 'X') + format(random.randint(1,255), 'X') + format(random.randint(1,255), 'X')
}
random_colors.append(entry)
random_color_mapping = get_discrete_mapping('SUID', 'Long', 'NODE_FILL_COLOR')
random_color_mapping['map'] = random_colors
create('styles/default/mappings', [random_color_mapping])
Image(BASE+'networks/' + str(suid) + '/views/first.png')
Out[59]:
And it is easy to reset it, by simply deleting the mapping.
In [60]:
res = requests.delete(BASE + 'styles/default/mappings/NODE_FILL_COLOR')
Image(BASE+'networks/' + str(suid) + '/views/first.png')
Out[60]:
In general, Cytoscape visualizations are controlled by Visual Styles. However, in some cases, you may want to edit the view directly. Typical example is layout. cyREST provides low level API to access View objects. In this section, you will learn how to set view values through writing your own layout algorithm.
You can move node (x,y) positions by setting new values for two Visual Properties:
The following is a simple example to arrange all nodes in one line.
In [61]:
# Find the Network View ID
res = requests.get(BASE + 'networks/' + str(suid) + '/views')
view_id = res.json()[0]
DISPLACEMENT = 64
def linear_layout(suid, node_list):
# Node X Position = size
position_list= []
start_pos = 0
for node in node_list:
position = {
'SUID': node,
'view':[
{
'visualProperty': 'NODE_X_LOCATION',
'value': start_pos
},
{
'visualProperty': 'NODE_Y_LOCATION',
'value': 0
}
]
}
position_list.append(position)
start_pos = start_pos + DISPLACEMENT
return position_list
# Add edge bend information
bend = [
{
'visualProperty':'EDGE_BEND',
'value': '0.5,-0.9,0.9'
}
]
update('styles/default/defaults', bend)
posx = linear_layout(view_id, node_suids)
node_views_url = 'networks/' + str(suid) + '/views/' + str(view_id) + '/nodes'
res = update(node_views_url, posx)
requests.get(BASE + 'apply/fit/' + str(suid))
image_url = BASE+'networks/' + str(suid) + '/views/' + str(view_id) + '.png?h=700'
print(image_url)
res = requests.get(image_url)
Image(res.content)
Out[61]:
In [62]:
res = requests.get(BASE + 'networks/' + str(suid) + '/tables/defaultnode')
rows = res.json()['rows']
node_table_df = pd.DataFrame(rows)
node_table_df.head(10)
Out[62]:
In [63]:
sorted_nodes = node_table_df.sort_index(by=['degree_layout'])
sorted_nodes.tail(10)
Out[63]:
In [64]:
sorted_node_suids = sorted_nodes['SUID']
pos2 = sorted_node_suids.apply(lambda x: str(x))
posx = linear_layout(view_id, pos2.tolist())
res = update(node_views_url, posx)
requests.get(BASE + 'apply/fit/' + str(suid))
create('styles/default/mappings', [new_color_mapping])
image_url = BASE+'networks/' + str(suid) + '/views/' + str(view_id) + '.png?h=650'
print(image_url)
res = requests.get(image_url)
Image(res.content)
Out[64]:
In [65]:
session_file_name = {
'file': '/Users/kono/Desktop/yeast_session.cys'
}
res = requests.post(BASE + 'session', params=session_file_name, data=None, headers=HEADERS)
print(json.dumps(res.json(), indent=4))