Basic Workflow 1: Fundamentals of Cytoscape RESTful API

by Keiichiro Ono


Introduction

This is an introduction to Cytoscape via RESTful API. You will learn how to access Cytoscape via RESTful API.

Prerequisites

System Requirments


Questions or Feature Requests?

Please send them to our mailing list

1. Import Python Libraries and Basic Setup

Libraries

In this tutorial, we will use several popular Python libraries to make this workflow more realistic.

HTTP Client

Since you need to access Cytoscape via RESTful API, HTTP client library is the most important tool you need to understand. In this example, we use Requests library to simplify API call code.

JSON Encoding and Decoding

Data will be exchanged as JSON between Cytoscape and Python code. Python has built-in support for JSON and we will use it in this workflow.

Basic Setup for the API

At this point, there is only one option for the cy-rest module: port number.

URL to Access Cytoscape REST API

We assume you are running Cytoscape desktop application and IPython Notebook server on a same machine. To access Cytoscape REST API, use the following URL:

http://localhost:PORT_NUMBER/v1/

where v1 is the current version number of API. Once the final release is ready, we guarantee compatibility of your scripts as long as major version number is the same.

Note

Of course, you can run Cytoscape and IPython server on different machines. In that case, you need to change the URL to the machine running Cytoscape desktop. Also, you need to open the port.

Change Port Number

By default, port number used by cy-rest module is 1234. To change this, you need set a global Cytoscape property from Edit-->Preserences-->Properties... and add a new property port.number.


In [1]:
import requests
import json

# Basic Setup
PORT_NUMBER = 1234
BASE = 'http://localhost:' + str(PORT_NUMBER) + '/v1/'

# Header for posting data to the server as JSON
HEADERS = {'Content-Type': 'application/json'}

2. Test Cytoscape REST API

Understand REST Basics

HTTP Verb Description
GET Retrieving resources (in most cases, it is Cytoscape data objects, such as networks or tables)
POST Creating resources
PUT Changing/replacing resources or collections
DELETE Deleting resources

Check the status of server

First, send a simple request and check the server status.


In [6]:
# Get server status
res = requests.get(BASE)

Roundtrip between JSON and Python Object

Now, res object contains return value of API as JSON. Let's convert it into Python object:


In [13]:
status_object = json.loads(res.content);
server_name = status_object['server']
print('Your server is ' + server_name)


Your server is Cytoscape RESTful API version 1.0.0

You can convert it again into human-readable text:


In [14]:
print(json.dumps(status_object, indent=4))


{
    "karaf.version": "2.2.11", 
    "freeMemoryMB": 415, 
    "processors": 8, 
    "usedMemoryMB": 252, 
    "os.version": "10.9.4", 
    "totalMemoryMB": 668, 
    "java.version": "1.7.0_67", 
    "server": "Cytoscape RESTful API version 1.0.0", 
    "os.name": "Mac OS X", 
    "maxMemoryMB": 28217
}

If you are comfortable with this data type conversion, you are ready to go!


3. Import Networks from various data sources

There are many ways to load networks into Cytoscape from REST API:

  • Load from files
  • Load from web services
  • Send Cytoscape.js style JSON directly to Cytoscape
  • Send edgelist

3.1 Create networks from local files and URLs

Let's start from a simple file loading examples. The POST method is used to create new Cytoscape objects. For example,

POST http://localhost:1234/v1/networks

means create new network(s) by specified method. If you want to create networks from files on your machine or remote servers, all you need to do is create a list of file locations and post it to Cytoscape.


In [25]:
# Small utility function to create networks from list of URLs
def create_from_list(network_list):
    server_res = requests.post(BASE + 'networks?source=url', data=json.dumps(network_list), headers=HEADERS)
    return json.loads(server_res.content)



# This is not necessary if you directly specify absolute file path like "/Users/foo/bar/sample_data/yeast_network.json"
import os
filepath = os.path.abspath('sample_data/yeast_network.json')

# Array of data source. 
network_files = [
    # Local file in this example data directory
    'file://' + filepath,

    # SIF file on a web server
    'http://chianti.ucsd.edu/cytoscape-data/galFiltered.sif'
    
    # And of course, you can add as many files as you need...
]


# Create!
print(json.dumps(create_from_list(network_files), indent=4))


[
    {
        "source": "file:///Users/kono/prog/git/cy-rest/examples/python/basic/sample_data/yeast_network.json", 
        "networkSUID": [
            21630
        ]
    }, 
    {
        "source": "http://chianti.ucsd.edu/cytoscape-data/galFiltered.sif", 
        "networkSUID": [
            22754
        ]
    }
]

3.2 Create networks from public RESTful web services

There are many public network data services. If the service supports Cytoscape-readable file formats, you can specify the query URL as a network location. For example, the following URL calls PSICQUIC web service and returns the search result in PSIMI 2.5 XML format. Since Cytoscape supports PSIMI2.5 files by default, this automatically creates a network from the response from the web service.


In [26]:
# This may take a while because Cytoscape fetch the data from a server in UK...
queries = [ 'http://www.ebi.ac.uk/Tools/webservices/psicquic/intact/webservices/current/search/query/brca1?format=xml25' ]
print(json.dumps(create_from_list(queries), indent=4))


[
    {
        "source": "http://www.ebi.ac.uk/Tools/webservices/psicquic/intact/webservices/current/search/query/brca1?format=xml25", 
        "networkSUID": [
            24168
        ]
    }
]

And of course, you can mix local files, URLs, and list of web service queries in a same list:


In [29]:
mixed = [
    'file://' + filepath,
    'http://chianti.ucsd.edu/cytoscape-data/galFiltered.sif',
    'http://www.ebi.ac.uk/Tools/webservices/psicquic/intact/webservices/current/search/query/brca2?format=xml25'
]
print(json.dumps(create_from_list(mixed), indent=4))


[
    {
        "source": "file:///Users/kono/prog/git/cy-rest/examples/python/basic/sample_data/yeast_network.json", 
        "networkSUID": [
            30778
        ]
    }, 
    {
        "source": "http://chianti.ucsd.edu/cytoscape-data/galFiltered.sif", 
        "networkSUID": [
            31902
        ]
    }, 
    {
        "source": "http://www.ebi.ac.uk/Tools/webservices/psicquic/intact/webservices/current/search/query/brca2?format=xml25", 
        "networkSUID": [
            33316
        ]
    }
]

3.3 Create networks from Python objects

And this is the most powerful feature in Cytoscape REST API. You can easily convert Python objects into Cytoscape networks, tables, or Visual Styles

How does this work?

Cytoscape REST API sends and receives data as JSON. For networks, it uses Cytoscape.js style JSON (support for more file formats are comming!). You can programmatically generates networks by converting Python dictionary into JSON.

3.3.1 Prepare Network as Cytoscape.js JSON

Let's start with the simplest network JSON:


In [101]:
# Start from a clean slate: remove all networks from current session
requests.delete(BASE + 'networks')

# Manually generates JSON as dictionary

empty_network = {
        'data': {
            'name': 'I\'m empty!'
        },
        'elements': {
            'nodes':[],
            'edges':[]
        }
}

res3 = requests.post(BASE + 'networks?collection=My%20Collection', data=json.dumps(empty_network), headers=HEADERS)
res3_dict = json.loads(res3.content)
net_suid = res3_dict['networkSUID']
print('Empty network has SUID ' + str(net_suid))


Empty network has SUID 38868

Since it's a simple Python dictionary, it is easy to add data to the network. Let's add some nodes and edges:


In [59]:
import copy

# Create a copy of the empty network object
small_network = copy.deepcopy(empty_network)

# Sequence of letters (a-z)
seq_letters = list(map(chr, range(ord('a'), ord('z')+1)))

# Build nodes and edges (in functional way)
build_node = lambda x: {'data': { 'id': x }}
abc_nodes = map(build_node, seq_letters)

build_edge = lambda x: {'data': { 'source': x, 'target': 'a' }}
rand_edges = map(build_edge, seq_letters)

small_network['elements']['nodes'] = abc_nodes
small_network['elements']['edges'] = rand_edges
small_network['data']['name'] = 'A is the hub.'

# Uncomment this if you want to see the actual JSON object
# print(json.dumps(small_network, indent=4))

res3 = requests.post(BASE + 'networks?collection=My%20Collection2', data=json.dumps(small_network), headers=HEADERS)
res3_dict = json.loads(res3.content)
new_suid = res3_dict['networkSUID']

# Apply layout
requests.get(BASE + 'apply/layouts/force-directed/' + str(new_suid))


Out[59]:
<Response [200]>

It's simple, isn't it?

3.3.2 Prepare Network as edgelist

Edgelist is a minimalistic data format for networks and it is widely used in popular libraries including NetworkX and igraph. Preparing edgelist in Python is straightforward. You just need to prepare a list of edges as string like:

a b
b c
a c

In Python, there are many ways to generate string like this. Here is a naive approach:


In [112]:
numbers = range(1,101)
el_builder = lambda x:  str(x) + '\t' + str(1) if x is 100 else str(x) + '\t' + str(x+1) + '\n'

res3 = requests.post(BASE + 'networks?format=edgelist&collection=Ring', data=reduce(lambda x, y: x + y, map(el_builder, numbers)), headers=HEADERS)

res3_dict = json.loads(res3.content)
circle_suid = res3_dict['networkSUID']
requests.get(BASE + 'apply/layouts/circular/' + str(circle_suid))


Out[112]:
<Response [200]>

Discussion

In this section, we've learned how to generate networks programmatically from Python. But for real world problems, it is not a good idea to use low level Python objects to generate networks because there are lots of cool libraries such as NetworkX or igraph. In the next session, let's use those to analyze real network data and visualize them in Cytoscape.