This is a sample Jupyter Notebook to demonstrate how users can manipulate Cytoscape network views directly from Python code.
py2cytoscpae already has the Style API to create and update Visual Styles. There are different use cases for these two APIs:
from py2cytoscape.data.cynetwork import CyNetwork
from py2cytoscape.data.cyrest_client import CyRestClient
import pandas as pd
import json
# Create REST client for Cytoscape
cy = CyRestClient()
# Reset current session for fresh start
cy.session.delete()
# Load a sample network
network1 = cy.network.create_from('http://chianti.ucsd.edu/~kono/data/galFiltered.sif')
# You can check all available Visual Properties with this function call:
vps = pd.Series(cy.style.vps.get_all())
vps.head(20)
DING_RENDERING_ENGINE_ROOT Ding Rndering Engine Root Visual Property EDGE Edge Visual Property EDGE_BEND Edge Bend EDGE_CURVED Edge Curved EDGE_LABEL Edge Label EDGE_LABEL_COLOR Edge Label Color EDGE_LABEL_FONT_FACE Edge Label Font Face EDGE_LABEL_FONT_SIZE Edge Label Font Size EDGE_LABEL_TRANSPARENCY Edge Label Transparency EDGE_LINE_TYPE Edge Line Type EDGE_PAINT Edge Paint EDGE_SELECTED Edge Selected EDGE_SELECTED_PAINT Edge Color (Selected) EDGE_SOURCE_ARROW_SELECTED_PAINT Edge Source Arrow Selected Paint EDGE_SOURCE_ARROW_SHAPE Edge Source Arrow Shape EDGE_SOURCE_ARROW_UNSELECTED_PAINT Edge Source Arrow Unselected Paint EDGE_STROKE_SELECTED_PAINT Edge Stroke Color (Selected) EDGE_STROKE_UNSELECTED_PAINT Edge Stroke Color (Unselected) EDGE_TARGET_ARROW_SELECTED_PAINT Edge Target Arrow Selected Paint EDGE_TARGET_ARROW_SHAPE Edge Target Arrow Shape dtype: object
There are twol options to get current views from Cytoscape:
CyNetworkView is a reference to a network view in your current Cytoscape session. This means CyNetworkView objects do not include actual data, such as node size or edge stroke colors. Instead, they hold a location of the data and create actual REST calls when you execute get/update methods for nodes and edges.
# Get views for a network: Cytoscape "may" have multiple views, and that's why it returns list instead of an object.
view_id_list = network1.get_views()
# Display IDs of available views
print(view_id_list)
# The "format" option specify the return type.
view1 = network1.get_view(view_id_list[0], format='view')
# This is a CyNetworkView object, not dictionary
print(view1)
[23617] <py2cytoscape.data.network_view.CyNetworkView object at 0x10f30f710>
If you prefer standard Python objects, you can use 'json' option to get Cytoscape network as Cytoscape.js style JSON. This is a network plus (x, y) locations. it DOES NOT includes any other view information.
view2 = network1.get_view(view_id_list[0], format='json')
#print(view2)
Once you have CyNetworkView object, you have access to all Visual Properties for nodes, edges, and the network.
# Get node/edge views as a Python dictionary
node_views_dict = view1.get_node_views_as_dict()
edge_views_dict = view1.get_edge_views_as_dict()
#print(json.dumps(node_views_dict, indent=4))
#print(json.dumps(edge_views_dict, indent=4))
Since it's a simple dictionary, you can easily convert it into Data Frame.
# Convert it into Pandas DataFrame
nv_df = pd.DataFrame.from_dict(node_views_dict, orient='index' )
# Extract specific Visual Property values...
node_location_df = nv_df[['NODE_X_LOCATION', 'NODE_Y_LOCATION']]
node_location_df.head()
NODE_X_LOCATION | NODE_Y_LOCATION | |
---|---|---|
22924 | -680 | 100 |
22925 | -680 | -300 |
22927 | -120 | -140 |
22928 | -360 | 340 |
22930 | -600 | -20 |
# Get views as NodeView/EdgeView objects
node_views = view1.get_node_views()
# Pick the first node view object
nv1 = node_views[0]
# Get details about the node
nv1_values = nv1.get_values()
# Convert to Data Frame
single_node_df = pd.DataFrame.from_dict(nv1_values, orient='index')
single_node_df.tail(20)
0 | |
---|---|
NODE_TOOLTIP | |
NODE_DEPTH | 0 |
NODE_LABEL_WIDTH | 200 |
NODE_LABEL_FONT_SIZE | 12 |
NODE_CUSTOMGRAPHICS_9 | org.cytoscape.ding.customgraphics.NullCustomGr... |
NODE_CUSTOMGRAPHICS_8 | org.cytoscape.ding.customgraphics.NullCustomGr... |
NODE_CUSTOMGRAPHICS_5 | org.cytoscape.ding.customgraphics.NullCustomGr... |
NODE_CUSTOMGRAPHICS_4 | org.cytoscape.ding.customgraphics.NullCustomGr... |
NODE_BORDER_STROKE | SOLID |
NODE_CUSTOMGRAPHICS_6 | org.cytoscape.ding.customgraphics.NullCustomGr... |
NODE_CUSTOMGRAPHICS_1 | org.cytoscape.ding.customgraphics.NullCustomGr... |
NODE_CUSTOMGRAPHICS_3 | org.cytoscape.ding.customgraphics.NullCustomGr... |
NODE_CUSTOMGRAPHICS_2 | org.cytoscape.ding.customgraphics.NullCustomGr... |
NODE_HEIGHT | 30 |
NODE_Z_LOCATION | 0 |
NODE_PAINT | #0099CC |
NODE_TRANSPARENCY | 255 |
NODE_LABEL_POSITION | C,C,c,0.00,0.00 |
NODE_CUSTOMGRAPHICS_7 | org.cytoscape.ding.customgraphics.NullCustomGr... |
NODE_VISIBLE | True |
# Access single value
nv1_color = nv1.get_value('NODE_FILL_COLOR')
print(nv1_color)
#0099CC
Setting values are also easy with py2cytoscape. All you have to do is creating a dictionary for the target nodes and edges.
If you call get/set methods for single node/edge view, it actually calls Cytoscape REST API through HTTP. If you need to update hundreds or thousands of nodes/edges, I strongly recommend update multiple objects at once.
# Switch current visual stye to a simple one...
minimal_style = cy.style.create('Minimal')
cy.style.apply(style=minimal_style, network=network1)
# Change background color: simply pass key-value pair
view1.update_network_view(visual_property='NETWORK_BACKGROUND_PAINT', value='black')
# Get SUID of all nodes
target_nodes = network1.get_nodes()
# Generate random colors for each node
import random
def get_color():
r = random.randint(0, 255)
g = random.randint(0, 255)
b = random.randint(0, 255)
return '#' + hex(r)[2:] + hex(g)[2:] + hex(b)[2:]
# Assign key-value pair. For this example, node SUID to color.
def generate_randome_color_dict(node_ids):
new_values = {}
for n in node_ids:
new_values[n] = get_color()
return new_values
new_values = generate_randome_color_dict(target_nodes)
# Set new values for a set of nodes. In this case, all nodes in the network
view1.update_node_views(visual_property='NODE_FILL_COLOR', values=new_values)
Now you can see something like this in the Cytoscape window. Let's embed the view as a static PNG image:
from IPython.display import Image
Image(network1.get_png())
Creating a custom layout programmatically is easy with py2cytoscape:
import math
all_nodes = network1.get_nodes()
idx = 0
x_locations = {}
for n in all_nodes:
x_locations[n] = idx*30
idx += 1
idx = 0
y_locations = {}
for n in all_nodes:
y_locations[n] = math.sin(idx/math.pi)*300
idx += 1
view1.update_node_views(visual_property='NODE_X_LOCATION', values=x_locations)
view1.update_node_views(visual_property='NODE_Y_LOCATION', values=y_locations)
Image(network1.get_png())
For small networks, cyREST is fast enough to emulate annimation. There are many ways to create animations, and here is a naive way to implement it:
def set_value(node_ids, val):
new_values = {}
for n in node_ids:
new_values[n] = val
return new_values
import math
import time
base_size = 30
for i in range(1,100):
mult = i * 0.2
view1.update_node_views(visual_property='NODE_TRANSPARENCY', values=set_value(node_ids, 30+abs(math.sin(mult/math.pi)*255 )))
view1.update_node_views(visual_property='NODE_WIDTH', values=set_value(node_ids, base_size + math.sin(i/math.pi)*10 ))
view1.update_node_views(visual_property='NODE_HEIGHT', values=set_value(node_ids, base_size + math.sin(i/math.pi)*10 ))
time.sleep(0.03)
You can optimize the code above for higher frame rate...
from py2cytoscape.data.util_network import NetworkUtil as util
cy.style.apply(style=minimal_style, network=network1)
cy.layout.apply(name='force-directed', network=network1)
name_map = util.name2suid(network1)
center_node = name_map['YMR043W']
# Chenge Center node view
view1.update_node_views(visual_property='NODE_FILL_COLOR', values=set_value([center_node], 'yellow'))
view1.update_node_views(visual_property='NODE_WIDTH', values=set_value([center_node], 60))
view1.update_node_views(visual_property='NODE_HEIGHT', values=set_value([center_node], 60))
node_ids = network1.get_neighbours(center_node)
adj_edges = network1.get_adjacent_edges(center_node)
view1.update_edge_views(visual_property='EDGE_STROKE_UNSELECTED_PAINT', values=set_value(adj_edges, 'red'))
view1.update_node_views(visual_property='NODE_FILL_COLOR', values=set_value(node_ids, 'red'))
base_size = 30
for i in range(1, 200):
mult = i * 0.2
# view1.update_node_views(visual_property='NODE_TRANSPARENCY', values=set_value(node_ids, 30+abs(math.sin(mult/math.pi)*255 )))
view1.update_node_views(visual_property='NODE_WIDTH', values=set_value(node_ids, base_size + math.sin(i/math.pi)*10 ))
view1.update_node_views(visual_property='NODE_HEIGHT', values=set_value(node_ids, base_size + math.sin(i/math.pi)*10 ))
view1.update_edge_views(visual_property='EDGE_WIDTH', values=set_value(adj_edges, 3 + math.sin(i/math.pi)*2))
time.sleep(0.03)
You can use Data Frame to update network view. For this feature, you need to use SUID for the index of DataFrame object.
# Grab list of nodes and edges
node_suids = network1.get_nodes()
edge_suids = network1.get_edges()
# DataFrame for node views
df1 = pd.DataFrame(index=node_suids,
columns=['NODE_FILL_COLOR', 'NODE_WIDTH', 'NODE_HEIGHT', 'NODE_X_LOCATION', 'NODE_Y_LOCATION'])
counter = 0
base_size = 40
for index, row in df1.iterrows():
row['NODE_FILL_COLOR'] = '#00FFaa'
row['NODE_WIDTH'] = base_size + math.sin(counter/math.pi)*(-30)
row['NODE_HEIGHT'] = base_size + math.sin(counter/math.pi)*(-30)
row['NODE_Y_LOCATION'] = math.sin(counter/math.pi)*200
row['NODE_X_LOCATION'] = counter*20
counter += 1
df1.head(10)
NODE_FILL_COLOR | NODE_WIDTH | NODE_HEIGHT | NODE_X_LOCATION | NODE_Y_LOCATION | |
---|---|---|---|---|---|
23614 | #00FFaa | 40 | 40 | 0 | 0 |
23612 | #00FFaa | 30.61115 | 30.61115 | 20 | 62.59236 |
23610 | #00FFaa | 22.16558 | 22.16558 | 40 | 118.8962 |
23606 | #00FFaa | 15.51181 | 15.51181 | 60 | 163.2546 |
23603 | #00FFaa | 11.31833 | 11.31833 | 80 | 191.2111 |
23600 | #00FFaa | 10.00646 | 10.00646 | 100 | 199.9569 |
23598 | #00FFaa | 11.708 | 11.708 | 120 | 188.6133 |
23596 | #00FFaa | 16.25199 | 16.25199 | 140 | 158.32 |
23592 | #00FFaa | 23.18192 | 23.18192 | 160 | 112.1206 |
23590 | #00FFaa | 31.80153 | 31.80153 | 180 | 54.65648 |
# DataFrame for edge views
df2 = pd.DataFrame(index=edge_suids,
columns=['EDGE_WIDTH', 'EDGE_STROKE_UNSELECTED_PAINT'])
counter = 0
for index, row in df2.iterrows():
row['EDGE_STROKE_UNSELECTED_PAINT'] = '#aaaaaa'
row['EDGE_WIDTH'] = 2 + abs(math.sin(counter/math.pi)*5)
counter += 1
df2.head(10)
EDGE_WIDTH | EDGE_STROKE_UNSELECTED_PAINT | |
---|---|---|
23615 | 2 | #aaaaaa |
23613 | 3.564809 | #aaaaaa |
23607 | 4.972404 | #aaaaaa |
23605 | 6.081366 | #aaaaaa |
23604 | 6.780278 | #aaaaaa |
23602 | 6.998923 | #aaaaaa |
23601 | 6.715334 | #aaaaaa |
23599 | 5.958001 | #aaaaaa |
23597 | 4.803014 | #aaaaaa |
23581 | 3.366412 | #aaaaaa |
# Apply it!
view1.batch_update_node_views(df1)
view1.batch_update_edge_views(df2)
Image(network1.get_png())
from py2cytoscape.cytoscapejs import viewer as cyjs
cyjs.render(network1.get_first_view(), 'default2', background='#efefef')