Source code for py4cytoscape.networks

# -*- coding: utf-8 -*-

"""Functions for NETWORK management and retrieving information on networks, nodes
and edges. Includes all functions that result in the creation of a new network
in Cytoscape, in addition to funcitons that extract network models into
other useful objects.

I. General network functions
II. General node functions
III. General edge functions
IV. Network creation
V. Network extraction
VI. Internal functions

Note:
     See the ``Network Selection`` section for all selection-related functions.

     See the ``Utils`` section for functions that convert node and edge names to SUIDs, and vice versa.
"""

"""Copyright 2020-2022 The Cytoscape Consortium

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 
documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit 
persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the 
Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 
THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""

# ==============================================================================
# I. General network functions
# ------------------------------------------------------------------------------

# External library imports
import sys
import time
import warnings
import pandas as pd
import igraph as ig
import networkx as nx

# Internal module imports
from . import commands
from . import tables
from . import network_selection
from . import layouts
from . import session
from . import sandbox

# Internal module convenience imports
from .py4cytoscape_utils import *
from .py4cytoscape_logger import cy_log
from .py4cytoscape_tuning import MODEL_PROPAGATION_SECS, CATCHUP_NETWORK_SECS, CATCHUP_NETWORK_TIMEOUT_SECS
from .exceptions import CyError
from .py4cytoscape_sandbox import get_abs_sandbox_path

def __init__(self):
    pass


[docs]@cy_log def set_current_network(network=None, base_url=DEFAULT_BASE_URL): """Selects the given network as "current". Args: network (SUID or str or None): Network name or SUID of the network that you want set as current base_url (str): Ignore unless you need to specify a custom domain, port or version to connect to the CyREST API. Default is http://127.0.0.1:1234 and the latest version of the CyREST API supported by this version of py4cytoscape. Returns: dict: {} (empty dict) Raises: ValueError: if server response has no JSON CyError: if network name or SUID doesn't exist requests.exceptions.RequestException: if can't connect to Cytoscape or Cytoscape returns an error Examples: >>> set_current_network() # sets current network to current {} >>> set_current_network('MyNetwork') # sets network named 'MyNetwork' as current {} >>> set_current_network(1502) # sets network having SUID 1502 as current {} """ suid = get_network_suid(network) cmd = f'network set current network="SUID:{suid}"' res = commands.commands_post(cmd, base_url=base_url) # TODO: Put double quotes around SUID return res
[docs]@cy_log def rename_network(title, network=None, base_url=DEFAULT_BASE_URL): """Sets a new name for a network. Duplicate network names are not allowed Args: title (str): New name for the network network (SUID or str or None): name or SUID of the network that you want to rename; default is "current" network base_url (str): Ignore unless you need to specify a custom domain, port or version to connect to the CyREST API. Default is http://127.0.0.1:1234 and the latest version of the CyREST API supported by this version of py4cytoscape. Returns: dict: server JSON response Raises: ValueError: if server response has no JSON CyError: if network name or SUID doesn't exist requests.exceptions.RequestException: if can't connect to Cytoscape or Cytoscape returns an error Examples: >>> rename_network('renamed network') # changes "current" network's name to "renamed network" {'network': 22752, 'title': 'renamed network'} >>> rename_network('renamed network', 'MyNetwork') # changes network named 'MyNetwork' to be named "renamed network" {'network': 22752, 'title': 'renamed network'} >>> rename_network('renamed network', 1502) # sets network having SUID 1502 to be named "renamed network" {'network': 1502, 'title': 'renamed network'} """ old_suid = get_network_suid(network, base_url=base_url) cmd = f'network rename name="{title}" sourceNetwork="SUID:{old_suid}"' # TODO: Put double quotes around SUID return commands.commands_post(cmd, base_url=base_url)
[docs]@cy_log def get_network_count(base_url=DEFAULT_BASE_URL): """Get the number of Cytoscape networks in the current Cytoscape session. Args: base_url (str): Ignore unless you need to specify a custom domain, port or version to connect to the CyREST API. Default is http://127.0.0.1:1234 and the latest version of the CyREST API supported by this version of py4cytoscape. Returns: int: count of networks Raises: ValueError: if server response has no JSON requests.exceptions.RequestException: if can't connect to Cytoscape or Cytoscape returns an error Examples: >>> get_network_count() 3 """ res = commands.cyrest_get('networks/count', base_url=base_url) return list(res.values())[0]
[docs]@cy_log def get_network_name(suid=None, base_url=DEFAULT_BASE_URL): """Get the name of a network. Args: suid (SUID or str or None): SUID of the network; default is current network. If a name is provided, then it is validated and returned. base_url (str): Ignore unless you need to specify a custom domain, port or version to connect to the CyREST API. Default is http://127.0.0.1:1234 and the latest version of the CyREST API supported by this version of py4cytoscape. Returns: str: network name Raises: ValueError: if server response has no JSON CyError: if network name or SUID doesn't exist requests.exceptions.RequestException: if can't connect to Cytoscape or Cytoscape returns an error Examples: >>> get_network_name() # get name of current network galFiltered.sif >>> get_network_name(22752) # get name of network having SUID galFiltered.sif >>> get_network_name('galFiltered.sif') # verify that current network is galFiltered.sif galFiltered.sif Note: Together with get_network_suid, this function attempts to handle all of the multiple ways we support network referencing (e.g., title, SUID, 'current', and NULL). These functions are then used by all other functions that take a ``network`` argument. """ if isinstance(suid, str): # title provided if suid == 'current': network_suid = get_network_suid(base_url=base_url) else: net_names = get_network_list(base_url=base_url) if suid in net_names: return suid else: raise CyError(f'Network does not exist for SUID "{suid}"') elif isinstance(suid, int): # suid provided network_suid = suid else: network_suid = get_network_suid(base_url=base_url) res = commands.cyrest_get('networks.names', {'column': 'suid', 'query': network_suid}, base_url=base_url) return res[0]['name']
[docs]@cy_log def get_network_suid(title=None, base_url=DEFAULT_BASE_URL): """Get the SUID of a network. Args: suid (SUID or str or None): Name of the network; default is "current" network. If an SUID is provided, then it is validated and returned. base_url (str): Ignore unless you need to specify a custom domain, port or version to connect to the CyREST API. Default is http://127.0.0.1:1234 and the latest version of the CyREST API supported by this version of py4cytoscape. Returns: int: network SUID Raises: ValueError: if server response has no JSON CyError: if network name or SUID doesn't exist requests.exceptions.RequestException: if can't connect to Cytoscape or Cytoscape returns an error Examples: >>> get_network_suid() # get SUID of current network 22752 >>> get_network_suid('galFiltered.sif') # get SUID of network having name 22752 >>> get_network_suid(22752) # verify that current network has SUID 22752 22752 Notes: Together with getNetworkSuid, this function attempts to handle all of the multiple ways we support network referencing (e.g., title, SUID, 'current', and NULL). These functions are then used by all other functions that take a "network" argument. """ if isinstance(title, str): # Title was provided if title == 'current': network_title = title else: net_names = get_network_list(base_url=base_url) if title in net_names: network_title = title else: raise CyError(f'Network does not exist for name "{title}"') elif isinstance(title, int): # SUID was provided net_suids = commands.cyrest_get('networks', base_url=base_url) if title in net_suids: return title raise CyError(f'Network does not exist for SUID "{title}"') else: # Don't understand, so use current network network_title = 'current' # Make requested network current and return its SUID cmd = f'network get attribute network="{network_title}" namespace="default" columnList="SUID"' response = commands.commands_post(cmd, base_url=base_url) return int(response[0]['SUID'])
[docs]@cy_log def get_network_list(base_url=DEFAULT_BASE_URL, *, get_suids=False): """Returns the list of Cytoscape network names in the current Cytoscape session. Args: base_url (str): Ignore unless you need to specify a custom domain, port or version to connect to the CyREST API. Default is http://127.0.0.1:1234 and the latest version of the CyREST API supported by this version of py4cytoscape. get_suids (bool): False returns a list of network names; True returns names and SUIDs Returns: list: network names or dictionaries (network name, SUID) Raises: ValueError: if server response has no JSON requests.exceptions.RequestException: if can't connect to Cytoscape or Cytoscape returns an error Examples: >>> get_network_list() ['yeastHighQuality.sif', 'galFiltered.sif'] >>> get_network_list(get_suids=True) [{"name": 'yeastHighQuality.sif', 'suid':102003}, {"name": 'galFiltered.sif', 'suid':104002}] """ cy_networks_suids = commands.cyrest_get('networks.names', base_url=base_url) if get_suids: return [{'name': x['name'], 'suid': x['SUID']} for x in cy_networks_suids] else: return [x['name'] for x in cy_networks_suids]
[docs]@cy_log def export_network(filename=None, type='SIF', network=None, base_url=DEFAULT_BASE_URL, *, overwrite_file=False): """Export a network to one of mulitple file formats. Args: filename (str): Full path or path relative to current working directory, in addition to the name of the file. Extension is automatically added based on the ``type`` argument. If blank, then the current network name is used. type (str): File type. SIF (default), CX, cyjs, graphML, NNF, xGMML. network (SUID or str or None): Name or SUID of a network or view. Default is the "current" network active in Cytoscape. base_url (str): Ignore unless you need to specify a custom domain, port or version to connect to the CyREST API. Default is http://127.0.0.1:1234 and the latest version of the CyREST API supported by this version of py4cytoscape. overwrite_file (bool): False allows Cytoscape show a message box before overwriting the file if the file already exists; True allows Cytoscape to overwrite it without asking Returns: dict: server JSON response Raises: ValueError: if server response has no JSON CyError: if file exists and user opts to not overwrite it requests.exceptions.RequestException: if can't connect to Cytoscape or Cytoscape returns an error Examples: >>> export_network('/path/filename','SIF') { 'data': {'file': 'C:\\Users\\CyDeveloper\\xx'}, 'errors': [] } >>> export_network('/path/filename','SIF', overwrite_file=True) # overwrite file without first asking { 'data': {'file': 'C:\\Users\\CyDeveloper\\xx'}, 'errors': [] } """ cmd = 'network export' # a good start # filename must be supplied if not filename: filename = get_network_name(network) # optional args if network is not None: cmd += ' network="SUID:' + str(get_network_suid(network, base_url=base_url)) + '"' type = type.upper() if type == 'CYS': narrate('Saving session as a CYS file...') return session.save_session(filename=filename, base_url=base_url) else: # e.g., CX, CYJS, GraphML, NNF, SIF, XGMML if type == 'GRAPHML': type = 'GraphML' cmd += ' options="' + type + '"' ext = '.' + type.lower() if re.search(ext + '$', filename) is None: filename += ext file_info = sandbox.sandbox_get_file_info(filename, base_url=base_url) if len(file_info['modifiedTime']) and file_info['isFile']: if overwrite_file: sandbox.sandbox_remove_file(filename, base_url=base_url) else: narrate('This file already exists. A Cytoscape popup will be generated to confirm overwrite.') full_filename = file_info['filePath'] return commands.commands_post(f'{cmd} OutputFile="{full_filename}"', base_url=base_url)
[docs]@cy_log def delete_network(network=None, base_url=DEFAULT_BASE_URL): """Delete a network from the current Cytoscape session. Args: network (SUID or str or None): Name or SUID of a network or view. Default is the "current" network active in Cytoscape. base_url (str): Ignore unless you need to specify a custom domain, port or version to connect to the CyREST API. Default is http://127.0.0.1:1234 and the latest version of the CyREST API supported by this version of py4cytoscape. Returns: str: '' Raises: CyError: if network name or SUID doesn't exist requests.exceptions.RequestException: if can't connect to Cytoscape or Cytoscape returns an error Examples: >>> delete_network() # delete the current network '' >>> delete_network(22752) # delete network having SUID '' >>> delete_network('galFiltered.sif') # delete network having name '' """ suid = get_network_suid(network) res = commands.cyrest_delete(f'networks/{suid}', base_url=base_url, require_json=False) return res
[docs]@cy_log def delete_all_networks(base_url=DEFAULT_BASE_URL): """Delete all networks from the current Cytoscape session. Args: base_url (str): Ignore unless you need to specify a custom domain, port or version to connect to the CyREST API. Default is http://127.0.0.1:1234 and the latest version of the CyREST API supported by this version of py4cytoscape. Returns: str: '' Raises: requests.exceptions.RequestException: if can't connect to Cytoscape or Cytoscape returns an error Examples: >>> delete_all_networks() """ res = commands.cyrest_delete('networks', base_url=base_url, require_json=False) return res
# ============================================================================== # II. General node functions # ------------------------------------------------------------------------------
[docs]@cy_log def get_first_neighbors(node_names=None, as_nested_list=False, network=None, base_url=DEFAULT_BASE_URL): """Returns a non-redundant list of first neighbors of the supplied list of nodes or current node selection. Args: node_names (str or list or int or None): List of nodes (as ``list`` of node names or SUIDs, comma-separated string of node names or SUIDs, or scalar node name or SUID). Node names should be found in the ``name`` column of the ``node table``. Default is currently selected nodes. as_nested_list (bool): Whether to return lists of neighbors per query node. network (SUID or str or None): Name or SUID of a network or view. Default is the "current" network active in Cytoscape. base_url (str): Ignore unless you need to specify a custom domain, port or version to connect to the CyREST API. Default is http://127.0.0.1:1234 and the latest version of the CyREST API supported by this version of py4cytoscape. Returns: list: deduped list of nodes neighboring specified nodes. If as_nested_list parameter is True, a list of neighbor node lists, one per specified node Raises: CyError: if network name or SUID doesn't exist, if no nodes are selected, or if node doesn't exist requests.exceptions.RequestException: if can't connect to Cytoscape or Cytoscape returns an error Examples: >>> get_first_neighbors(node_names = None, as_nested_list=True) [['YBR020W', ['YGL035C', 'YOL051W', 'YPL248C', 'YML051W']], ['YGL035C', ['YLR044C', 'YLR377C', ...]], ...] >>> get_first_neighbors(['YBR020W', 'YGL035C'], as_nested_list=False) ['YGL035C', 'YOL051W', 'YPL248C', 'YML051W', 'YLR044C', 'YLR377C', 'YIL162W', ... ] >>> get_first_neighbors('YBR020W, YGL035C'], as_nested_list=False) ['YGL035C', 'YOL051W', 'YPL248C', 'YML051W', 'YLR044C', 'YLR377C', 'YIL162W', ... ] >>> get_first_neighbors('YBR020W', as_nested_list=False) ['YGL035C', 'YOL051W', 'YPL248C', 'YML051W'] >>> get_first_neighbors([515677, 515678], as_nested_list=False) ['YGL035C', 'YOL051W', 'YPL248C', 'YML051W', 'YLR044C', 'YLR377C', 'YIL162W', ... ] >>> get_first_neighbors('515677, 515678', as_nested_list=False) ['YGL035C', 'YOL051W', 'YPL248C', 'YML051W', 'YLR044C', 'YLR377C', 'YIL162W', ... ] >>> get_first_neighbors(515677, as_nested_list=False) ['YGL035C', 'YOL051W', 'YPL248C', 'YML051W'] Note: To identify a node whose name contains a comma, use '\\\\' to escape the comma. For example, 'node1, node\\\\,2' identifies 'node1' and 'node,2'. See Also: :meth:`select_nodes`, :meth:`select_first_neighbors` """ # TODO: This looks very inefficient because for each node, the entire node table is fetched from Cytoscape and the neighbor list is de-dupped ... verify this and maybe do better if node_names is None: node_names = network_selection.get_selected_nodes(network=network, base_url=base_url) else: node_names = normalize_list(node_names) if node_names is None or len(node_names) == 0: return None net_suid = get_network_suid(network, base_url=base_url) neighbor_names = [] for node_name in node_names: # get first neighbors for each node node_suid = node_name_to_node_suid([node_name], net_suid, base_url=base_url, unique_list=True)[0] first_neighbors_suids = commands.cyrest_get( f'networks/{net_suid}/nodes/{node_suid}/neighbors', base_url=base_url) first_neighbors_names = node_suid_to_node_name(first_neighbors_suids, net_suid, base_url=base_url) if as_nested_list: neighbor_names.append([node_name, first_neighbors_names]) else: neighbor_names += first_neighbors_names neighbor_names = list(dict.fromkeys(neighbor_names)) # dedup list return neighbor_names
[docs]@cy_log def add_cy_nodes(node_names, skip_duplicate_names=True, network=None, base_url=DEFAULT_BASE_URL): """Add one or more nodes to a Cytoscape network. Args: node_names (list or None): A ``list`` of node names skip_duplicate_names (bool): Skip adding a node if a node with the same name is already in the network. If ``FALSE`` then a duplicate node (with a unique SUID) will be added. network (SUID or str or None): Name or SUID of a network or view. Default is the "current" network active in Cytoscape. base_url (str): Ignore unless you need to specify a custom domain, port or version to connect to the CyREST API. Default is http://127.0.0.1:1234 and the latest version of the CyREST API supported by this version of py4cytoscape. Returns: list: A ``list`` of ``named lists`` of name and SUID for each node added. Raises: CyError: if network name or SUID doesn't exist requests.exceptions.RequestException: if can't connect to Cytoscape or Cytoscape returns an error Examples: >>> add_cy_nodes(['newnode1', 'newnode2'], skip_duplicate_names=False) [{"name": "newnode1", "SUID": 1459}, {"name": "newnode2", "SUID": 1460}] >>> add_cy_nodes('newnode1, newnode2', skip_duplicate_names=False) [{"name": "newnode1", "SUID": 1459}, {"name": "newnode2", "SUID": 1460}] >>> add_cy_nodes(['newnode2', 'newnode3'], skip_duplicate_names=True) [{"name": "newnode3", "SUID": 1460}] Note: To identify a node whose name contains a comma, use '\\\\' to escape the comma. For example, 'node1, node\\\\,2' identifies 'node1' and 'node,2'. """ net_suid = get_network_suid(network, base_url=base_url) node_names = normalize_list(node_names) if skip_duplicate_names: all_nodes_list = get_all_nodes(net_suid, base_url=base_url) node_names = list(set(node_names) - set(all_nodes_list)) res = commands.cyrest_post(f'networks/{net_suid}/nodes', body=node_names, base_url=base_url) return res
[docs]@cy_log def get_node_count(network=None, base_url=DEFAULT_BASE_URL): """Reports the number of nodes in the network. Args: network (SUID or str or None): Name or SUID of a network or view. Default is the "current" network active in Cytoscape. base_url (str): Ignore unless you need to specify a custom domain, port or version to connect to the CyREST API. Default is http://127.0.0.1:1234 and the latest version of the CyREST API supported by this version of py4cytoscape. Returns: int: count of nodes in network. Raises: ValueError: if server response has no JSON CyError: if network name or SUID doesn't exist requests.exceptions.RequestException: if can't connect to Cytoscape or Cytoscape returns an error Examples: >>> get_node_count() 6 >>> get_node_count(52) 6 >>> get_node_count('galFiltered.sif') 6 """ net_suid = get_network_suid(network, base_url=base_url) res = commands.cyrest_get(f'networks/{net_suid}/nodes/count', base_url=base_url) return res['count']
[docs]@cy_log def get_all_nodes(network=None, base_url=DEFAULT_BASE_URL): """Retrieve the names of all the nodes in the network. Args: network (SUID or str or None): Name or SUID of a network or view. Default is the "current" network active in Cytoscape. base_url (str): Ignore unless you need to specify a custom domain, port or version to connect to the CyREST API. Default is http://127.0.0.1:1234 and the latest version of the CyREST API supported by this version of py4cytoscape. Returns: list: a ``list`` of nodes in the network Raises: ValueError: if server response has no JSON CyError: if network name or SUID doesn't exist requests.exceptions.RequestException: if can't connect to Cytoscape or Cytoscape returns an error Examples: >>> get_all_nodes() ['YDL194W', 'YDR277C', 'YBR043C', ... ] """ net_suid = get_network_suid(network, base_url=base_url) n_count = get_node_count(net_suid, base_url=base_url) if n_count == 0: return None res = commands.cyrest_get(f'networks/{net_suid}/tables/defaultnode/columns/name', base_url=base_url) return res['values']
# ============================================================================== # III. General edge functions # ------------------------------------------------------------------------------
[docs]@cy_log def add_cy_edges(source_target_list, edge_type='interacts with', directed=False, network=None, base_url=DEFAULT_BASE_URL): """Add one or more edges to a Cytoscape network by listing source and target node pairs. Args: source_target_list (list or list of lists): Source and target node pairs edgeType (str): The type of interaction. Default is 'interacts with'. directed (bool): Indicates whether interactions are directed. Default is ``FALSE``. network (SUID or str or None): Name or SUID of a network or view. Default is the "current" network active in Cytoscape. base_url (str): Ignore unless you need to specify a custom domain, port or version to connect to the CyREST API. Default is http://127.0.0.1:1234 and the latest version of the CyREST API supported by this version of py4cytoscape. Returns: list of dicts: A ``list`` of dicts for each edge (SUID, source, target) added. Raises: CyError: if network name or SUID doesn't exist, or if a source or target can't resolve to just one node. requests.exceptions.RequestException: if can't connect to Cytoscape or Cytoscape returns an error Examples: >>> add_cy_edges(['YLR075W', 'YKL028W']) [{'SUID': 2884, 'source': 1552, 'target': 1698}] >>> add_cy_edges([['YKL028W', 'YJR066W'], ['YJR066W', 'YLR452C'], ['YGR046W', 'YLR452C']]) [{'SUID': 2886, 'source': 1698, 'target': 1645}, {'SUID': 2887, 'source': 1645, 'target': 1534} ...] Note: To identify a node whose name contains a comma, use '\\\\' to escape the comma. For example, 'node1, node\\\\,2' identifies 'node1' and 'node,2'. """ net_suid = get_network_suid(network, base_url=base_url) # Create list of all nodes in order presented # TODO: Find out what should happen if node name maps to multiple nodes if len(source_target_list) == 2 \ and isinstance(source_target_list, list) \ and isinstance(source_target_list[0], str) \ and isinstance(source_target_list[1], str): flat_source_target_list = source_target_list else: flat_source_target_list = [item for sublist in source_target_list for item in sublist] edge_suid_list = node_name_to_node_suid(flat_source_target_list, net_suid, base_url=base_url) # Verify that each edge is unambiguous if True in [True if isinstance(x, list) else False for x in edge_suid_list]: raise CyError('More than one node found for a given source or target - no edges added') # Note: use str() for edge SUIDs in case the SUIDs exceed JSON's int encoding edge_data = [{'source': str(edge_suid_list[x]), 'target': str(edge_suid_list[x + 1]), 'directed': directed, 'interaction': edge_type} for x in range(0, len(edge_suid_list) - 1, 2)] res = commands.cyrest_post(f'networks/{net_suid}/edges', body=edge_data, base_url=base_url) return res
[docs]@cy_log def get_edge_count(network=None, base_url=DEFAULT_BASE_URL): """Reports the number of the edges in the network. Args: network (SUID or str or None): Name or SUID of a network or view. Default is the "current" network active in Cytoscape. base_url (str): Ignore unless you need to specify a custom domain, port or version to connect to the CyREST API. Default is http://127.0.0.1:1234 and the latest version of the CyREST API supported by this version of py4cytoscape. Returns: int: count of edges in network. Raises: ValueError: if server response has no JSON CyError: if network name or SUID doesn't exist requests.exceptions.RequestException: if can't connect to Cytoscape or Cytoscape returns an error Examples: >>> get_edge_count() 6 >>> get_edge_count(52) 6 >>> get_edge_count('galFiltered.sif') 6 """ net_suid = get_network_suid(network, base_url=base_url) res = commands.cyrest_get(f'networks/{net_suid}/edges/count', base_url=base_url) return res['count']
[docs]@cy_log def get_edge_info(edges, network=None, base_url=DEFAULT_BASE_URL): """Returns source, target and edge table row values. Args: edges (str or list or int): List of edges (as ``list`` of edge names or SUIDs, comma-separated string of edge names or SUIDs, or scalar edge name or SUID). Edge names should be found in the ``name`` column of the ``edge table``. edges (list): list of SUIDs or names of edges, i.e., values in the "name" column. Can also input single edge. network (SUID or str or None): Name or SUID of a network or view. Default is the "current" network active in Cytoscape. base_url (str): Ignore unless you need to specify a custom domain, port or version to connect to the CyREST API. Default is http://127.0.0.1:1234 and the latest version of the CyREST API supported by this version of py4cytoscape. Returns: list of dicts: list of dicts describing each edge Raises: ValueError: if server response has no JSON CyError: if network name or SUID doesn't exist requests.exceptions.RequestException: if can't connect to Cytoscape or Cytoscape returns an error Examples: >>> get_edge_info(['YDR277C (pp) YDL194W', 'YDR277C (pp) YJR022W']) [{'source': 2919, 'target': 2918, 'SUID': 3248, 'shared name': 'YDR277C (pp) YDL194W', 'shared interaction': 'pp', 'name': 'YDR277C (pp) YDL194W', 'selected': False, 'interaction': 'pp', 'EdgeBetweenness': 496.0}, {'source': 2919, 'target': 3220, 'SUID': 3249, 'shared name': 'YDR277C (pp) YJR022W', 'shared interaction': 'pp', 'name': 'YDR277C (pp) YJR022W', 'selected': False, 'interaction': 'pp', 'EdgeBetweenness': 988.0}] >>> get_edge_info('YDR277C (pp) YDL194W, YDR277C (pp) YJR022W') [{'source': 2919, 'target': 2918, 'SUID': 3248, 'shared name': 'YDR277C (pp) YDL194W', 'shared interaction': 'pp', 'name': 'YDR277C (pp) YDL194W', 'selected': False, 'interaction': 'pp', 'EdgeBetweenness': 496.0}, {'source': 2919, 'target': 3220, 'SUID': 3249, 'shared name': 'YDR277C (pp) YJR022W', 'shared interaction': 'pp', 'name': 'YDR277C (pp) YJR022W', 'selected': False, 'interaction': 'pp', 'EdgeBetweenness': 988.0}] >>> get_edge_info('YDR277C (pp) YDL194W') [{'source': 2919, 'target': 2918, 'SUID': 3248, 'shared name': 'YDR277C (pp) YDL194W', 'shared interaction': 'pp', 'name': 'YDR277C (pp) YDL194W', 'selected': False, 'interaction': 'pp', 'EdgeBetweenness': 496.0}] >>> get_edge_info([3248, 3249]) [{'source': 2919, 'target': 2918, 'SUID': 3248, 'shared name': 'YDR277C (pp) YDL194W', 'shared interaction': 'pp', 'name': 'YDR277C (pp) YDL194W', 'selected': False, 'interaction': 'pp', 'EdgeBetweenness': 496.0}, {'source': 2919, 'target': 3220, 'SUID': 3249, 'shared name': 'YDR277C (pp) YJR022W', 'shared interaction': 'pp', 'name': 'YDR277C (pp) YJR022W', 'selected': False, 'interaction': 'pp', 'EdgeBetweenness': 988.0}] >>> get_edge_info(3248) [{'source': 2919, 'target': 2918, 'SUID': 3248, 'shared name': 'YDR277C (pp) YDL194W', 'shared interaction': 'pp', 'name': 'YDR277C (pp) YDL194W', 'selected': False, 'interaction': 'pp', 'EdgeBetweenness': 496.0}] Notes: This function is kinda slow. It takes approximately 70ms per edge to return a result, e.g., 850 edges will take one minute. """ net_suid = get_network_suid(network, base_url=base_url) edges = normalize_list(edges) def convert_edge_name_to_edge_info(edge_name): edge_suid = edge_name_to_edge_suid(edge_name, network, base_url=base_url) res = commands.cyrest_get('networks/%s/edges/%s' % (net_suid, edge_suid[0]), base_url=base_url) return res['data'] edge_info = [convert_edge_name_to_edge_info(x) for x in edges] # TODO: Verify that it's always OK to return a list instead of a single dict ... this happens in many places return edge_info
[docs]@cy_log def get_all_edges(network=None, base_url=DEFAULT_BASE_URL): """Retrieve the names of all the edges in the network. Args: network (SUID or str or None): Name or SUID of a network or view. Default is the "current" network active in Cytoscape. base_url (str): Ignore unless you need to specify a custom domain, port or version to connect to the CyREST API. Default is http://127.0.0.1:1234 and the latest version of the CyREST API supported by this version of py4cytoscape. Returns: list: a ``list`` of edges in the network Raises: ValueError: if server response has no JSON CyError: if network name or SUID doesn't exist requests.exceptions.RequestException: if can't connect to Cytoscape or Cytoscape returns an error Examples: >>> get_all_edges() ['YDR277C (pp) YDL194W', 'YDR277C (pp) YJR022W', 'YPR145W (pp) YMR117C', ...] """ net_suid = get_network_suid(network, base_url=base_url) e_count = get_edge_count(network, base_url=base_url) if e_count == 0: return None res = commands.cyrest_get(f'networks/{net_suid}/tables/defaultedge/columns/name', base_url=base_url) return res['values']
# ============================================================================== # IV. Network creation # ------------------------------------------------------------------------------
[docs]@cy_log def clone_network(network=None, base_url=DEFAULT_BASE_URL): """Makes a copy of a Cytoscape Network with all of its edges and nodes. Args: network (SUID or str or None): Name or SUID of a network or view. Default is the "current" network active in Cytoscape. base_url (str): Ignore unless you need to specify a custom domain, port or version to connect to the CyREST API. Default is http://127.0.0.1:1234 and the latest version of the CyREST API supported by this version of py4cytoscape. Returns: int: The ``SUID`` of the new network Raises: ValueError: if server response has no JSON CyError: if network name or SUID doesn't exist requests.exceptions.RequestException: if can't connect to Cytoscape or Cytoscape returns an error Examples: >>> clone_network() 1477 """ net_suid = get_network_suid(network, base_url=base_url) res = commands.commands_post(f'network clone network="SUID:{net_suid}"', base_url=base_url) # TODO: Put double quotes around SUID return res['network']
[docs]@cy_log def create_subnetwork(nodes=None, nodes_by_col='SUID', edges=None, edges_by_col='SUID', exclude_edges=False, subnetwork_name=None, network=None, base_url=DEFAULT_BASE_URL): """Copies a subset of nodes and edges into a newly created subnetwork. Args: nodes (str or list or int or None): List of nodes or keyword: selected, unselected or all. If node list: ``list`` of node names or SUIDs, comma-separated string of node names or SUIDs, or scalar node name or SUID. Node names should be found in the ``SUID`` column of the ``node table`` unless specified in ``nodes_by_col``. If list is None, default is currently selected nodes. nodes_by_col (str): name of node table column corresponding to provided nodes list; default is 'SUID' edges (str or list or int or None): List of edges or keyword: selected, unselected or all. If edge list: ``list`` of edge names or SUIDs, comma-separated string of edge names or SUIDs, or scalar edge name or SUID. Edge names should be found in the ``SUID`` column of the ``edge table`` unless specified in ``edges_by_col``. If list is None, default is currently selected edges. edges_by_col (str): name of edge table column corresponding to provided edges list; default is 'SUID' exclude_edges (bool): whether to exclude connecting edges; default is FALSE subnetwork_name (str): name of new subnetwork to be created; default is to add a numbered suffix to source network name network (SUID or str or None): Name or SUID of a network or view. Default is the "current" network active in Cytoscape. base_url (str): Ignore unless you need to specify a custom domain, port or version to connect to the CyREST API. Default is http://127.0.0.1:1234 and the latest version of the CyREST API supported by this version of py4cytoscape. Returns: int: The ``SUID`` of the new subnetwork Raises: ValueError: if server response has no JSON CyError: if network name or SUID doesn't exist requests.exceptions.RequestException: if can't connect to Cytoscape or Cytoscape returns an error Examples: >>> create_subnetwork(nodes='all') # choose all selected and unselected nodes 1477 >>> create_subnetwork(edges='selected') # choose only nodes whose edges are selected, and include those edges 1477 >>> create_subnetwork(nodes=['RAP1', 'HIS4', 'PDC1', 'RPL18A'], nodes_by_col='COMMON', subnetwork_name=base_name+'xx') 1477 >>> create_subnetwork(nodes='RAP1, HIS4, PDC1, RPL18A', nodes_by_col='COMMON', subnetwork_name=base_name+'xx') 1477 >>> create_subnetwork(nodes=[1502, 1555, 1560, 1701], subnetwork_name=base_name+'xx') 1477 Note: To identify a node whose name contains a comma, use '\\\\' to escape the comma. For example, 'node1, node\\\\,2' identifies 'node1' and 'node,2'. """ # TODO: Verify that node and edge names can't contain blanks or commas title = get_network_suid(network, base_url=base_url) exclude_edges = 'true' if exclude_edges else 'false' if isinstance(nodes, str) and nodes in ['all', 'selected', 'unselected']: nodes_by_col = None if isinstance(edges, str) and edges in ['all', 'selected', 'unselected']: edges_by_col = None json_sub = {'source': 'SUID:' + str(title), 'excludeEdges': exclude_edges, 'nodeList': prep_post_query_lists(nodes, nodes_by_col), 'edgeList': prep_post_query_lists(edges, edges_by_col)} if not subnetwork_name is None: json_sub['networkName'] = subnetwork_name res = commands.cyrest_post('commands/network/create', body=json_sub, base_url=base_url) return res['data']['network']
[docs]@cy_log def create_network_from_igraph(igraph, title='From igraph', collection='My Igraph Network Collection', base_url=DEFAULT_BASE_URL): """Create a Cytoscape network from an igraph network. Takes an igraph network and generates dataframes for nodes and edges to send to the createNetwork function. Returns the network.suid and applies the perferred layout set in Cytoscape preferences. Notes: Vertices and edges from the igraph network will be translated into nodes and edges in Cytoscape. Associated attributes will also be passed to Cytoscape as node and edge table columns. Note: undirected networks will be implicitly modeled as directed in Cytoscape. Conversion back via ``createIgraphFromNetwork`` will result in a directed network. Also note: igraph attributes of type "other" denoted by "x" are converted to "String" in Cytoscape. Note that the extra ``id`` column is created in the node table because the ``id`` column is mandatory in the cytoscape.js format, which is what is sent to Cytoscape. Args: igraph (igraph): igraph network object title (str): network name collection (str): network collection name base_url (str): Ignore unless you need to specify a custom domain, port or version to connect to the CyREST API. Default is http://127.0.0.1:1234 and the latest version of the CyREST API supported by this version of py4cytoscape. Returns: int: The ``SUID`` of the new network Raises: ValueError: if server response has no JSON KeyError: igraph network doesn't contain required node or edge attributes CyError: if network name or SUID doesn't exist requests.exceptions.RequestException: if can't connect to Cytoscape or Cytoscape returns an error Examples: >>> g = ig.Graph() >>> g.add_vertices(3) >>> g.vs['name'] = ['RAP1', 'HIS4', 'HIS3'] >>> g.add_edges([(0, 1), (1, 2)]) >>> g.es['interaction'] = ['enhances', 'inhibits'] >>> create_network_from_igraph(g, 'new graph', 'my collection') 138775 See Also: :meth:`create_network_from_data_frames`, :meth:`create_igraph_from_network` """ # TODO: Verify the type "other" behavior described above # TODO: Verify the undeclared parameter behaviors claimed in the R documents # TODO: Is this really faithful to the R implementation? def rename_dup_columns(col_list, col_name): # See if a column name exists, and if so, rename all other same-name columns. # Especially important for graphs that come with a ``source`` or ``target`` name # created by get_edge_dataframe (which creates these columns) when these columns # were already in the graph as a result of creating the graph from a Cytoscape # network. first_index = col_list.index(col_name) replacement_name = col_name + '.original' new_cols = [replacement_name if i != first_index and col_list[i] == col_name else col_list[i] for i in range(len(col_list))] return new_cols # Get nodes as a table indexed by node number ... assume every node has at least a 'name' attribute as a string node_df = ig.Graph.get_vertex_dataframe(igraph) node_df['name'] = node_df['name'].astype(str) # Get edges as a table ... assume every edge has a 'source' and 'target' column as indexes into node table edge_df = ig.Graph.get_edge_dataframe(igraph) # Make sure interaction is a string if 'interaction' in edge_df.columns: edge_df['interaction'] = edge_df['interaction'].astype(str) try: # If the incoming graph contained a column named 'source' or 'target', rename them edge_cols = rename_dup_columns(list(edge_df.columns), 'source') edge_df.columns = rename_dup_columns(edge_cols, 'target') except: narrate('Edge table missing "source" or "target" attribute') # iGraph creates this df, so should never happen edge_df = None if edge_df is not None: try: # Convert edge 'source' and 'target' values from index into node table to actual node name edge_df['source'] = edge_df['source'].apply(lambda x: node_df['name'][x]) edge_df['target'] = edge_df['target'].apply(lambda x: node_df['name'][x]) except: narrate('Not all edge sources or targets resolve to vertex names') # iGraph creates this df, so should never happen edge_df = None if len(node_df.index) == 0: node_df = None if edge_df is not None and len(edge_df.index) == 0: edge_df = None return create_network_from_data_frames(nodes=node_df, edges=edge_df, title=title, collection=collection, base_url=base_url, node_id_list='name')
[docs]@cy_log def create_network_from_networkx(netx, title='From networkx', collection='My NetworkX Network Collection', base_url=DEFAULT_BASE_URL): """Create a Cytoscape network from a NetworkX graph. Args: netx (Graph, DiGraph, MultiGraph or MultiDiGraph): networkx object title (str): network name collection (str): network collection name base_url (str): Ignore unless you need to specify a custom domain, port or version to connect to the CyREST API. Default is http://127.0.0.1:1234 and the latest version of the CyREST API supported by this version of py4cytoscape. Returns: int: The ``SUID`` of the new network Raises: requests.exceptions.RequestException: if can't connect to Cytoscape or Cytoscape returns an error Examples: >>> create_network_from_networkx(netx) 31766 >>> create_network_from_networkx(netx, 'Cool Networkx', 'Collection of Cool Networks') 31766 See Also: :meth:`create_networkx_from_network` """ netx_nodes = netx.nodes(data=True) # returns list of tuples (name, attrs) netx_node_list = [{**attrs, 'name': name} for name, attrs in netx_nodes] node_df = pd.DataFrame.from_records(netx_node_list) netx_edges = netx.edges(data=True) # returns list of tuples (src, targ, attrs) netx_edge_list = [{**attrs, 'source': src, 'target': targ} for src, targ, attrs in netx_edges] edge_df = pd.DataFrame.from_records(netx_edge_list) # TODO: This will blow if there are no edges or no nodes ... so will create_network_from_igraph() ... will R blow, too? # Make sure critical attributes are strings node_df['name'] = node_df['name'].astype(str) edge_df['source'] = edge_df['source'].astype(str) edge_df['target'] = edge_df['target'].astype(str) if 'interaction' in edge_df.columns: edge_df['interaction'] = edge_df['interaction'].astype(str) if len(node_df.index) == 0: node_df = None if len(edge_df.index) == 0: edge_df = None return create_network_from_data_frames(nodes=node_df, edges=edge_df, title=title, collection=collection, base_url=base_url, node_id_list='name')
[docs]@cy_log def create_network_from_data_frames(nodes=None, edges=None, title='From dataframe', collection='My Dataframe Network Collection', base_url=DEFAULT_BASE_URL, *, node_id_list='id', source_id_list='source', target_id_list='target', interaction_type_list='interaction'): """Create a network from data frames. Takes data frames for nodes and edges, as well as naming parameters to generate the JSON data format required by the "networks" POST operation via CyREST. Returns the network.suid and applies the preferred layout set in Cytoscape preferences. Notes: ``nodes`` should contain a column of character strings named: id. This name can be overridden by the arg: ``node_id_list``. Additional columns are loaded as node attributes. ``edges`` should contain columns of character strings named: source, target and interaction. These names can be overridden by args: source_id_list, target_id_list, interaction_type_list. Additional columns are loaded as edge attributes. The ``interaction`` list can contain a single value to apply to all rows; and if excluded altogether, the interaction type will be set to "interacts with". NOTE: attribute values of types (num) will be imported as (Double); (int) as (Integer); (chr) as (String); and (logical) as (Boolean). (Lists) will be imported as (Lists) in CyREST v3.9+. Note that the extra ``id`` column is created in the node table because the ``id`` column is mandatory in the cytoscape.js format, which is what is sent to Cytoscape. Args: nodes (DataFrame): see details and examples below; default NULL to derive nodes from edge sources and targets edges (DataFrame): see details and examples below; default NULL for disconnected set of nodes title (str): network name collection (str): network collection name base_url (str): Ignore unless you need to specify a custom domain, port or version to connect to the CyREST API. Default is http://127.0.0.1:1234 and the latest version of the CyREST API supported by this version of py4cytoscape. * : node_id_list (str): Name of column in ``nodes`` containing node id source_id_list (str): Name of column in ``edges`` containing source node name target_id_list (str): Name of column in ``edges`` containing target node name interaction_type_list (str): Name of column in ``edges`` containing interaction name Returns: int: The ``SUID`` of the new network Raises: ValueError: if server response has no JSON CyError: if network name or SUID doesn't exist requests.exceptions.RequestException: if can't connect to Cytoscape or Cytoscape returns an error Examples: >>> node_data = {'id':["node 0","node 1","node 2","node 3"], >>> 'group':["A","A","B","B"], >>> 'score':[20,10,15,5]} >>> nodes = df.DataFrame(data=node_data, columns=['id', 'group', 'score']) >>> edge_data = {'source':["node 0","node 0","node 0","node 2"], >>> 'target':["node 1","node 2","node 3","node 3"], >>> 'interaction':["inhibits","interacts","activates","interacts"], >>> 'weight':[5.1,3.0,5.2,9.9]} >>> edges = df.DataFrame(data=edge_data, columns=['source', 'target', 'interaction', 'weight']) >>> >>> create_network_from_data_frames(nodes, edges, title='From node & edge dataframe') 1477 """ def compute_edge_name(source, target, interaction): return source + ' (' + interaction + ') ' + target # Create a node list even if we have to use the edges lists to infer nodes if nodes is None: if not edges is None: id_list = [] for source, target in zip(edges['source'].values, edges['target'].values): id_list.append(source) id_list.append(target) nodes = pd.DataFrame(data=id_list, columns=['id']) else: raise CyError('Must provide either nodes or edges') # create the JSON for a node list ... in cytoscape.js format NODE_ID_COL_NAME = 'id' json_nodes = [{'data': {NODE_ID_COL_NAME: node}} for node in nodes[node_id_list]] # create the JSON for an edge list ... in cytoscape.js format json_edges = [] if not edges is None: if not interaction_type_list in edges.columns: edges[interaction_type_list] = 'interacts with' edges_sub = edges[[source_id_list, target_id_list, interaction_type_list]] json_edges = [{'data': {'name': compute_edge_name(source, target, interaction), 'source': source, 'target': target, 'interaction': interaction}} for source, target, interaction in zip(edges_sub[source_id_list], edges_sub[target_id_list], edges_sub[interaction_type_list])] # create the full JSON for a cytoscape.js-style network ... see http://manual.cytoscape.org/en/stable/Supported_Network_File_Formats.html#cytoscape-js-json # Note that no node or edge attributes are included in this version of the network json_network = {'data': [{'name': title}], 'elements': {'nodes': json_nodes, 'edges': json_edges}} # call Cytoscape to create this network and return the SUID network_suid = commands.cyrest_post('networks', parameters={'title': title, 'collection': collection}, body=json_network, base_url=base_url)['networkSUID'] # TODO: There appears to be a race condition here ... the view isn't set for a while. Without an explicit delay, the # "vizmap apply" command below fails for lack of a valid view. So, we'll retry # the problem operations until they succeed (see _delay_until_stable() calls below) # Keep cycling until Cytoscape is able to return table information ... safe after that _delay_until_stable(lambda: get_network_suid(network_suid, base_url=base_url) is not None, 'verifying network SUID', vote_count=10) # drop the SUID column if one is present nodes = nodes.drop(['SUID'], axis=1, errors='ignore') # load node attributes into Cytoscape network if len(set(nodes.columns) - {node_id_list}) != 0: tables.load_table_data(nodes, data_key_column=node_id_list, table_key_column=NODE_ID_COL_NAME, network=network_suid, base_url=base_url) if not edges is None: # get rid of SUID column if one is present edges = edges.drop(['SUID'], axis=1, errors='ignore') # create edge name out of source/interaction/target edge_names = [compute_edge_name(source, target, interaction) for source, interaction, target in zip(edges[source_id_list], edges[interaction_type_list], edges[target_id_list])] edges['name'] = edge_names # find out the SUID of each node so it can be used in a multigraph if needed edges['data.key.column'] = edge_name_to_edge_suid(edge_names, network_suid, base_url=base_url, unique_list=True) # if the edge list looks real, add the edge attributes (if any) if len(set(edges.columns) - set(['source', 'target', 'interaction', 'name', 'data.key.column'])) != 0: tables.load_table_data(edges, data_key_column='data.key.column', table='edge', table_key_column='SUID', network=network_suid, base_url=base_url) narrate('Applying default style...') _delay_until_stable(lambda: commands.commands_post('vizmap apply styles="default"', base_url=base_url) is not None, 'apply vizmap') narrate('Applying preferred layout') _delay_until_stable(lambda: layouts.layout_network(network=network_suid, base_url=base_url) is not None, 'layout network') # TODO: Verify that attribute types are properly set in Cytoscape return network_suid
[docs]@cy_log def create_network_from_cytoscapejs(cytoscapejs, title=None, collection='My CytoscapeJS Network Collection', base_url=DEFAULT_BASE_URL): """Create a network from CytoscapeJS JSON. Takes the dict-encoded JSON formatted for a CytoscapeJS network (including node coordinates) and creates a Cytoscape network. Both nodes and edges can have named attribute values, where the names become Cytoscape columns and the values are associated with their node or edge. Once the network is created, it becomes the Cytoscape current network. Notes: If ``title`` is supplied, it is used as the Cytoscape network name. Otherwise, if the JSON's ['data']['name'] element is present, its value is used as Cytoscape's network name. If neither, the network gets a generic name. If ``collection`` is supplied, it is used as the collection to contain the network, even if the collection must be created first. If ``collection`` is None, the new network is created in an unnamed collection. Args: cytoscapejs (dict): network (nodes, edges, attributes, node positions and metadata) in CytoscapeJS format title (str): network name (None means use the name in ``cytoscapejs`` ... if no name, use a generic name) collection (str): collection name (None means create an unnamed collection) base_url (str): Ignore unless you need to specify a custom domain, port or version to connect to the CyREST API. Default is http://127.0.0.1:1234 and the latest version of the CyREST API supported by this version of py4cytoscape. Returns: int: The ``SUID`` of the new network Raises: requests.exceptions.RequestException: if can't connect to Cytoscape or Cytoscape returns an error Examples: >>> create_network_from_cytoscapejs({...}) 213043 >>> create_network_from_cytoscapejs({...}, 'new network', 'new collection') 220209 See Also: :meth:`create_cytoscapejs_from_network`, :meth:`import_network_from_file` """ # Set up for CytoscapeJS JSON format params = {'format': 'json'} # If caller explicitly supplied a null title, let Cytoscape figure it out from the JSON if title is None: if 'data' in cytoscapejs and 'name' in cytoscapejs['data']: title = cytoscapejs['data']['name'] else: title = 'From cytoscapejs' params['title'] = title # If caller explicitly supplied a null collection, let Cytoscape create a no-named collection if collection is not None: params['collection'] = collection # Send JSON to Cytoscape to create new network/view res = commands.cyrest_post('networks', params, cytoscapejs, base_url=base_url) return res['networkSUID']
[docs]@cy_log def import_network_from_tabular_file(file=None, first_row_as_column_names=False, start_load_row=1, column_type_list='s,i,t', delimiters='\\,,\t', base_url=DEFAULT_BASE_URL): """Loads a network from specified file. Note: To load a tabular file from cloud storage, use the file's URL and the ``sandbox_url_to`` function to download the file to a sandbox, and then use ``import_network_from_tabular_file`` to load it from there. Args: file (str): Name of file in any of the supported tabular formats (e.g., csv, tsv, Excel, etc). first_row_as_column_names (bool): True if first row contributes column names but no data values start_load_row (int): 1-based row to start reading data ... after column name row, if present column_type_list (str): comma-separated map of column types ordered by column index (e.g. "source,target,interaction,source attribute,target attribute,edge attribute,skip" or just "s,t,i,sa,ta,ea,x"); defaults to "s,i,t" delimiters (str): comma-separated list of characters that can separate columns ... ``\\\\,`` is a comma, ``\\t`` is a tab base_url (str): Ignore unless you need to specify a custom domain, port or version to connect to the CyREST API. Default is http://127.0.0.1:1234 and the latest version of the CyREST API supported by this version of py4cytoscape. Returns: dict: {"networks": [network suid], "views": [suid for views]} where networks and views lists have length 1 Raises: CyError: if file cannot be found or loaded, or if error in tabular_params list requests.exceptions.RequestException: if can't connect to Cytoscape or Cytoscape returns an error Examples: >>> import_network_from_tabular_file('data/yeastHighQuality.sif') # import a SIF-formatted network {'networks': [131481], 'views': [131850]} >>> import_network_from_tabular_file('data/disease.net.default.xlsx') # import an Excel file that has no header row {'networks': [131481], 'views': [131850]} >>> import_network_from_tabular_file('data/disease.net.default.txt') # import a text file that has no header row {'networks': [131481], 'views': [131850]} >>> import_network_from_tabular_file('data/disease.net.interaction.txt', # import ' '-delimited header row and data >>> first_row_as_column_names=True, >>> start_load_row=1, >>> column_type_list='s,t,x,i', >>> delimiters=' ') {'networks': [131481], 'views': [131850]} """ file = get_abs_sandbox_path(file) # As of 3.9, the column_type_list is sufficient for specifying the layout of a data line. However, # per CYTOSCAPE-12764, pre-3.9 Cytoscape has trouble with the "interaction" tag. To accommodate all # Cytoscape versions, we provide explicit indexes for source, target and interaction columns. type_list = column_type_list.lower().split(',') index_params = '' if 's' in type_list: index_params += f' indexColumnSourceInteraction="{type_list.index("s") + 1}"' if 'source' in type_list: index_params += f' indexColumnSourceInteraction="{type_list.index("source") + 1}"' if 't' in type_list: index_params += f' indexColumnTargetInteraction="{type_list.index("t") + 1}"' if 'target' in type_list: index_params += f' indexColumnTargetInteraction="{type_list.index("target") + 1}"' if 'i' in type_list: index_params += f' indexColumnTypeInteraction="{type_list.index("i") + 1}"' if 'interaction' in type_list: index_params += f' indexColumnTypeInteraction="{type_list.index("interaction") + 1}"' res = commands.commands_post( f'network import file file="{file}" firstRowAsColumnNames="{first_row_as_column_names}" startLoadRow="{start_load_row}"{index_params} columnTypeList="{column_type_list}" delimiters="{delimiters}"', base_url=base_url) # should not be necessary, but is because "network load file" doesn't actually set the current network # until after it's done. So, without the sleep(), setting the current network will be superceded by # "network load file"'s own network. This is race condition that can be solved by "network load file" # not returning until it's actually done. # TODO: Fix this race condition time.sleep(CATCHUP_NETWORK_SECS) return res
[docs]@cy_log def import_network_from_file(file=None, base_url=DEFAULT_BASE_URL): """Loads a network from specified file. Note: To load a network file from cloud storage, use the file's URL and the ``sandbox_url_to`` function to download the file to a sandbox, and then use ``import_network_from_file`` to load it from there. Args: file (str): Name of file in any of the supported formats (e.g., SIF, GML, xGMML, etc). If None, a demo network file in SIF format is loaded. base_url (str): Ignore unless you need to specify a custom domain, port or version to connect to the CyREST API. Default is http://127.0.0.1:1234 and the latest version of the CyREST API supported by this version of py4cytoscape. Returns: dict: {"networks": [network suid], "views": [suid for views]} where networks and views lists have length 1 Raises: CyError: if file cannot be found or loaded requests.exceptions.RequestException: if can't connect to Cytoscape or Cytoscape returns an error Examples: >>> import_network_from_file() # import demo network {'networks': [131481], 'views': [131850]} >>> import_network_from_file('data/yeastHighQuality.sif') {'networks': [131481], 'views': [131850]} See Also: :meth:`create_network_from_cytoscapejs` """ if file is None: file = 'sampleData/galFiltered.sif' else: file = get_abs_sandbox_path(file) res = commands.commands_post(f'network load file file="{file}"', base_url=base_url) # TODO: Fix R documentation to match what's really returned # TODO: Put double quotes around file # should not be necessary, but is because "network load file" doesn't actually set the current network # until after it's done. So, without the sleep(), setting the current network will be superceded by # "network load file"'s own network. This is race condition that can be solved by "network load file" # not returning until it's actually done. # TODO: Fix this race condition time.sleep(CATCHUP_NETWORK_SECS) return res
# ============================================================================== # V. Network extraction # ------------------------------------------------------------------------------
[docs]@cy_log def create_igraph_from_network(network=None, base_url=DEFAULT_BASE_URL): """Create an igraph network from a Cytoscape network. Notes: Takes a Cytoscape network and translates it nodes and edges into vertices and edges in igraph. Associated table columns will also be passed to igraph as vertex and edge attributes. All networks are implicitly modeled as directed in Cytoscape. Round-trip conversion of an undirected network in igraph via ``createNetworkFromIgraph`` to Cytoscape and back to igraph will result in a directed network. Args: network (SUID or str or None): Name or SUID of a network or view. Default is the "current" network active in Cytoscape. base_url (str): Ignore unless you need to specify a custom domain, port or version to connect to the CyREST API. Default is http://127.0.0.1:1234 and the latest version of the CyREST API supported by this version of py4cytoscape. Returns: igraph: The new ``igraph`` object Raises: ValueError: if server response has no JSON CyError: if network name or SUID doesn't exist requests.exceptions.RequestException: if can't connect to Cytoscape or Cytoscape returns an error Examples: >>> create_igraph_from_network() IGRAPH DN-- 330 359 -- + attr: AverageShortestPathLength (v), BetweennessCentrality (v), COMMON (v), ClosenessCentrality (v), ClusteringCoefficient (v), Degree (v), Eccentricity (v), IsSingleNode (v), NeighborhoodConnectivity (v), NumberOfDirectedEdges (v), NumberOfUndirectedEdges (v), PartnerOfMultiEdgedNodePairs (v), Radiality (v), SelfLoops (v), Stress (v), TopologicalCoefficient (v), degree.layout (v), gal1RGexp (v), gal1RGsig (v), gal4RGexp (v), gal4RGsig (v), gal80Rexp (v), gal80Rsig (v), isExcludedFromPaths (v), name (v), selected (v), shared name (v), EdgeBetweenness (e), interaction (e), name (e), selected (e), shared interaction (e), shared name (e), source (e), target (e) + edges (vertex names): YML064C->YLR284C, YML064C->YHR198C, YKL074C->YGL035C, YDL081C->YLR340W, ... See Also: :meth:`create_network_from_data_frames`, :meth:`create_network_from_igraph` """ suid = get_network_suid(network, base_url=base_url) # get dataframes cyedges = tables.get_table_columns('edge', network=suid, base_url=base_url) cynodes = tables.get_table_columns('node', network=suid, base_url=base_url) # check for source and target columns ... if they're not present, dig them out of the full name if not {'source', 'target'} <= set(cyedges.columns): src_trg = parse_edges(cyedges['name']) cyedges['source'] = [x[0] for x in src_trg] cyedges['target'] = [x[2] for x in src_trg] # set up iGraph vertices ... first create vertex by naming it, then pile on attributes # Tutorial: https://igraph.org/python/doc/tutorial/tutorial.html # Source: https://github.com/igraph/igraph/blob/master/src/igraph/__init__.py g = ig.Graph(directed=True) # add all nodes and their attributes g.add_vertices(list(cynodes['name'])) for col in cynodes.columns: if not col in ['name', 'SUID']: g.vs[col] = list(cynodes[col]) # add all edges and their nodes g.add_edges([(src, trg) for src, trg in zip(cyedges['source'], cyedges['target'])]) # TODO: Find out why this rename happens ... is this an iGraph thing? ... how does the roundtrip work? # cyedges.rename(columns={'source': 'from', 'target': 'to'}, inplace=True) for col in cyedges.columns: if not col in ['SUID']: g.es[col] = list(cyedges[col]) return g
[docs]@cy_log def create_networkx_from_network(network=None, base_url=DEFAULT_BASE_URL): """Return the Cytoscape network as a networkx multi-di-graph. Args: network (SUID or str or None): Name or SUID of a network or view. Default is the "current" network active in Cytoscape. base_url (str): Ignore unless you need to specify a custom domain, port or version to connect to the CyREST API. Default is http://127.0.0.1:1234 and the latest version of the CyREST API supported by this version of py4cytoscape. Returns: MultiDiGraph: The new ``networkx`` object Raises: CyError: if network name or SUID doesn't exist requests.exceptions.RequestException: if can't connect to Cytoscape or Cytoscape returns an error Examples: >>> n = create_networkx_from_network(network='galFiltered.sif') >>> print(nx.info(n)) Name: Type: MultiDiGraph Number of nodes: 330 Number of edges: 359 Average in degree: 1.0879 Average out degree: 1.0879 See Also: :meth:`create_network_from_networkx` """ suid = get_network_suid(network, base_url=base_url) # get dataframes cyedges = tables.get_table_columns('edge', network=suid, base_url=base_url) cynodes = tables.get_table_columns('node', network=suid, base_url=base_url) # check for source and target columns ... if they're not present, dig them out of the full name if not {'source', 'target'} <= set(cyedges.columns): src_trg = parse_edges(cyedges['name']) cyedges['source'] = [x[0] for x in src_trg] cyedges['target'] = [x[2] for x in src_trg] # Create a list of edges as tuples (src, targ, suid, attrs) with 'source' & 'target' removed from attrs edges_dict = cyedges.to_dict(orient='records') e_bunch = [(row['source'], row['target'], row['SUID'], {k: row[k] for k in row if k not in {'source', 'target'}} ) for row in edges_dict] # Create a list of nodes as tuples (name, attrs) with 'name' removed from attrs nodes_dict = cynodes.to_dict(orient='records') n_bunch = [(row['name'], {k: row[k] for k in row if k not in {'name'}}) for row in nodes_dict] # Create the networkx graph modeled as directed edges with ability to have multiple edges connecting two nodes md_graph = nx.MultiDiGraph() md_graph.add_edges_from(e_bunch) md_graph.add_nodes_from(n_bunch) return md_graph
[docs]@cy_log def create_cytoscapejs_from_network(network=None, base_url=DEFAULT_BASE_URL): """Create a Cytoscape JS representation of a Cytoscape network. Notes: Takes a Cytoscape network and translates its nodes and edges into a dict corresponding to a Cytoscape JS JSON document. Attributes, node positions and network metadata are also captured. Args: network (SUID or str or None): Name or SUID of a network or view. Default is the "current" network active in Cytoscape. base_url (str): Ignore unless you need to specify a custom domain, port or version to connect to the CyREST API. Default is http://127.0.0.1:1234 and the latest version of the CyREST API supported by this version of py4cytoscape. Returns: dict: The Cytoscape JS object Raises: CyError: if network name or SUID doesn't exist requests.exceptions.RequestException: if can't connect to Cytoscape or Cytoscape returns an error Examples: >>> create_cytoscapejs_from_network() {'format_version': '1.0', 'generated_by': 'cytoscape-3.9.1', 'target_cytoscapejs_version': '~2.1', 'data': { 'shared_name': 'galFiltered.sif', 'Dataset_Name': 'Yeast Perturbation Network', '__Annotations': [''], 'name': 'galFiltered.sif', 'SUID': 3027163, 'selected': True}, 'elements': {'nodes': [ ... ], 'edges': [ ... ]} See Also: :meth:`create_network_from_cytoscapejs` """ net_suid = get_network_suid(network, base_url=base_url) res = commands.cyrest_get(f'networks/{net_suid}/views/first', base_url=base_url) return res
# ============================================================================== # VI. Internal functions # # Dev Notes: Prefix internal functions with a '_'. Skip doc_strings for these # functions. # ------------------------------------------------------------------------------ def _delay_until_stable(attempt_op, error_text, vote_count=1): catchup_network_timeout = time.time() + CATCHUP_NETWORK_TIMEOUT_SECS is_stable = False while not is_stable and time.time() < catchup_network_timeout: try: votes = 1 is_stable = attempt_op() while votes < vote_count and is_stable: votes += 1 is_stable = attempt_op() except: is_stable = False if not is_stable: # print(f'Sleeping for {CATCHUP_NETWORK_SECS} seconds') time.sleep(CATCHUP_NETWORK_SECS) if not is_stable: raise CyError(f'Timeout trying to {error_text}')