Cytoscape CX2 export¶
Cytoscape reads networks in the CX2 format/). CX2 has no hyperedges, so the export asks how to project them. This notebook compares the three projections and reads the network back from CX2.
To run this notebook, create the environment from
cytoscape-cx2-export.yml. See
Reproduce a tutorial.
Build a graph with a hyperedge¶
In [1]:
Copied!
import annnet as an
anet = an.AnnNet(directed=True)
anet.add_nodes(['A', 'B', 'C', 'D', 'E'])
anet.add_edges('A', 'B', edge_id='binary_edge', weight=1.0)
anet.add_edges('A', 'B', edge_id='parallel_edge', weight=0.65)
anet.add_edges('B', 'C', edge_id='complex_edge', directed=False, weight=0.8)
anet.add_edges(src=['B', 'C'], tgt=['D', 'E'], edge_id='reaction', directed=True, weight=2.0)
anet.attrs.update(
'nodes',
{
'A': {'label': 'ligand'},
'B': {'label': 'enzyme'},
'C': {'label': 'cofactor'},
'D': {'label': 'product'},
'E': {'label': 'byproduct'},
},
)
anet.attrs.table('edges', derived=True).select(
['edge_id', 'kind', 'source', 'target', 'head', 'tail']
)
import annnet as an
anet = an.AnnNet(directed=True)
anet.add_nodes(['A', 'B', 'C', 'D', 'E'])
anet.add_edges('A', 'B', edge_id='binary_edge', weight=1.0)
anet.add_edges('A', 'B', edge_id='parallel_edge', weight=0.65)
anet.add_edges('B', 'C', edge_id='complex_edge', directed=False, weight=0.8)
anet.add_edges(src=['B', 'C'], tgt=['D', 'E'], edge_id='reaction', directed=True, weight=2.0)
anet.attrs.update(
'nodes',
{
'A': {'label': 'ligand'},
'B': {'label': 'enzyme'},
'C': {'label': 'cofactor'},
'D': {'label': 'product'},
'E': {'label': 'byproduct'},
},
)
anet.attrs.table('edges', derived=True).select(
['edge_id', 'kind', 'source', 'target', 'head', 'tail']
)
Out[1]:
shape: (4, 6)
| edge_id | kind | source | target | head | tail |
|---|---|---|---|---|---|
| str | str | str | str | list[str] | list[str] |
| "binary_edge" | "binary" | "A" | "B" | null | null |
| "parallel_edge" | "binary" | "A" | "B" | null | null |
| "complex_edge" | "binary" | "B" | "C" | null | null |
| "reaction" | "hyper" | null | null | ["B", "C"] | ["D", "E"] |
In [2]:
Copied!
from annnet.utils import plotting
plotting.plot(anet, backend='graphviz', show_edge_labels=True)
from annnet.utils import plotting
plotting.plot(anet, backend='graphviz', show_edge_labels=True)
Out[2]:
Compare hyperedge export modes¶
In [3]:
Copied!
for mode in ['skip', 'expand', 'reify']:
cx2 = an.to_cx2(anet, hyperedges=mode, export_name=f'annnet-{mode}')
edge_aspect = next(item['edges'] for item in cx2 if 'edges' in item)
node_aspect = next(item['nodes'] for item in cx2 if 'nodes' in item)
print(mode, 'nodes:', len(node_aspect), 'edges:', len(edge_aspect))
for mode in ['skip', 'expand', 'reify']:
cx2 = an.to_cx2(anet, hyperedges=mode, export_name=f'annnet-{mode}')
edge_aspect = next(item['edges'] for item in cx2 if 'edges' in item)
node_aspect = next(item['nodes'] for item in cx2 if 'nodes' in item)
print(mode, 'nodes:', len(node_aspect), 'edges:', len(edge_aspect))
skip nodes: 5 edges: 3 expand nodes: 5 edges: 7 reify nodes: 6 edges: 7
Round-trip through CX2¶
The reify projection adds one node for each hyperedge. AnnNet also writes a
manifest into the CX2 file, so from_cx2 restores the hyperedge.
In [4]:
Copied!
import json
cx2 = an.to_cx2(anet, hyperedges='reify', export_name='annnet-reified')
restored_anet = an.from_cx2(cx2)
print('restored shape:', restored_anet.shape)
print('restored hyperedges:', sorted(restored_anet.hyperedge_definitions))
print(json.dumps(cx2[:3], indent=2))
import json
cx2 = an.to_cx2(anet, hyperedges='reify', export_name='annnet-reified')
restored_anet = an.from_cx2(cx2)
print('restored shape:', restored_anet.shape)
print('restored hyperedges:', sorted(restored_anet.hyperedge_definitions))
print(json.dumps(cx2[:3], indent=2))
restored shape: (5, 4)
restored hyperedges: ['reaction']
[
{
"CXVersion": "2.0",
"hasFragments": false
},
{
"metaData": [
{
"name": "attributeDeclarations",
"elementCount": 1
},
{
"name": "networkAttributes",
"elementCount": 1
},
{
"name": "nodes",
"elementCount": 6
},
{
"name": "edges",
"elementCount": 7
}
]
},
{
"attributeDeclarations": [
{
"nodes": {
"label": {
"d": "string"
},
"name": {
"d": "string"
},
"is_hyperedge": {
"d": "boolean"
},
"eid": {
"d": "string"
},
"tag": {
"d": "string"
},
"reaction": {
"d": "string"
}
},
"edges": {
"interaction": {
"d": "string"
},
"weight": {
"d": "double"
},
"edge_id": {
"d": "string"
},
"role": {
"d": "string"
}
},
"networkAttributes": {
"__AnnNet_Manifest__": {
"d": "string"
},
"name": {
"d": "string"
},
"directed": {
"d": "boolean"
}
}
}
]
}
]
skip, expand, and reify are explicit choices about what Cytoscape
shows. skip leaves out the hyperedges, expand replaces each hyperedge with
binary edges, and reify adds a node for each hyperedge.