Source code for pybel_tools.filters.node_filters

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

"""Node filters to supplement :mod:`pybel.struct.filters.node_filters`."""

from collections import defaultdict
from typing import Iterable, Mapping, Optional, Set

import pybel
from pybel import BELGraph
from pybel.constants import CAUSAL_RELATIONS, FUNCTION, HAS_VARIANT, LABEL, NAMESPACE, PATHOLOGY, RELATION
from pybel.dsl import BaseEntity, Protein, ProteinModification
from pybel.struct.filters import (
    build_node_data_search, build_node_key_search, count_passed_node_filter, data_missing_key_builder,
)
from pybel.struct.filters.typing import NodePredicate, NodePredicates
from pybel.typing import Strings

__all__ = [
    'summarize_node_filter',
    'node_inclusion_filter_builder',
    'node_exclusion_filter_builder',
    'function_inclusion_filter_builder',
    'function_exclusion_filter_builder',
    'function_namespace_inclusion_builder',
    'namespace_inclusion_builder',
    'data_contains_key_builder',
    'data_missing_key_builder',
    'node_has_label',
    'node_missing_label',
    'include_pathology_filter',
    'exclude_pathology_filter',
    'build_node_data_search',
    'build_node_key_search',
    'variants_of',
    'get_variants_to_controllers',
]


[docs]def summarize_node_filter(graph: BELGraph, node_filters: NodePredicates) -> None: """Print a summary of the number of nodes passing a given set of filters. :param graph: A BEL graph :param node_filters: A node filter or list/tuple of node filters """ passed = count_passed_node_filter(graph, node_filters) print('{}/{} nodes passed'.format(passed, graph.number_of_nodes()))
# Example filters
[docs]def node_inclusion_filter_builder(nodes: Iterable[BaseEntity]) -> NodePredicate: """Build a filter that only passes on nodes in the given list. :param nodes: An iterable of BEL nodes """ node_set = set(nodes) def inclusion_filter(_: BELGraph, node: BaseEntity) -> bool: """Pass only for a node that is in the enclosed node list. :return: If the node is contained within the enclosed node list """ return node in node_set return inclusion_filter
[docs]def node_exclusion_filter_builder(nodes: Iterable[BaseEntity]) -> NodePredicate: """Build a filter that fails on nodes in the given list.""" node_set = set(nodes) def exclusion_filter(_: BELGraph, node: BaseEntity) -> bool: """Pass only for a node that isn't in the enclosed node list :return: If the node isn't contained within the enclosed node list """ return node not in node_set return exclusion_filter
def _single_function_inclusion_filter_builder(func: str) -> NodePredicate: def function_inclusion_filter(_: BELGraph, node: BaseEntity) -> bool: """Pass only for a node that has the enclosed function. :return: If the node doesn't have the enclosed function """ return node.function == func return function_inclusion_filter def _collection_function_inclusion_builder(funcs: Iterable[str]) -> NodePredicate: funcs = set(funcs) def functions_inclusion_filter(_: BELGraph, node: BaseEntity) -> bool: """Pass only for a node that is one of the enclosed functions. :return: If the node doesn't have the enclosed functions """ return node.function in funcs return functions_inclusion_filter
[docs]def function_inclusion_filter_builder(func: Strings) -> NodePredicate: """Build a filter that only passes on nodes of the given function(s). :param func: A BEL Function or list/set/tuple of BEL functions """ if isinstance(func, str): return _single_function_inclusion_filter_builder(func) elif isinstance(func, Iterable): return _collection_function_inclusion_builder(func) raise ValueError('Invalid type for argument: {}'.format(func))
[docs]def function_exclusion_filter_builder(func: Strings) -> NodePredicate: """Build a filter that fails on nodes of the given function(s). :param func: A BEL Function or list/set/tuple of BEL functions """ if isinstance(func, str): def function_exclusion_filter(_: BELGraph, node: BaseEntity) -> bool: """Pass only for a node that doesn't have the enclosed function. :return: If the node doesn't have the enclosed function """ return node[FUNCTION] != func return function_exclusion_filter elif isinstance(func, Iterable): functions = set(func) def functions_exclusion_filter(_: BELGraph, node: BaseEntity) -> bool: """Pass only for a node that doesn't have the enclosed functions. :return: If the node doesn't have the enclosed functions """ return node[FUNCTION] not in functions return functions_exclusion_filter raise ValueError('Invalid type for argument: {}'.format(func))
[docs]def function_namespace_inclusion_builder(func: str, namespace: Strings) -> NodePredicate: """Build a filter function for matching the given BEL function with the given namespace or namespaces. :param func: A BEL function :param namespace: The namespace to search by """ if isinstance(namespace, str): def function_namespaces_filter(_: BELGraph, node: BaseEntity) -> bool: """Pass only for nodes that have the enclosed function and enclosed namespace.""" if func != node[FUNCTION]: return False return NAMESPACE in node and node[NAMESPACE] == namespace elif isinstance(namespace, Iterable): namespaces = set(namespace) def function_namespaces_filter(_: BELGraph, node: BaseEntity) -> bool: """Pass only for nodes that have the enclosed function and namespace in the enclose set.""" if func != node[FUNCTION]: return False return NAMESPACE in node and node[NAMESPACE] in namespaces else: raise ValueError('Invalid type for argument: {}'.format(namespace)) return function_namespaces_filter
[docs]def data_contains_key_builder(key: str) -> NodePredicate: # noqa: D202 """Build a filter that passes only on nodes that have the given key in their data dictionary. :param key: A key for the node's data dictionary """ def data_contains_key(_: BELGraph, node: BaseEntity) -> bool: """Pass only for a node that contains the enclosed key in its data dictionary. :return: If the node contains the enclosed key in its data dictionary """ return key in node return data_contains_key
#: Passes for nodes that have been annotated with a label node_has_label = data_contains_key_builder(LABEL) #: Fails for nodes that have been annotated with a label node_missing_label = data_missing_key_builder(LABEL) # Default Filters #: A filter that passes for nodes that are :data:`pybel.constants.PATHOLOGY` include_pathology_filter = function_inclusion_filter_builder(PATHOLOGY) #: A filter that fails for nodes that are :data:`pybel.constants.PATHOLOGY` exclude_pathology_filter = function_exclusion_filter_builder(PATHOLOGY) # TODO node filter that is false for abundances with no in-edges def namespace_inclusion_builder(namespace) -> NodePredicate: # noqa: D202 """""" def has_namespace(_: BELGraph, node: BaseEntity): return node.get(NAMESPACE) == namespace return has_namespace
[docs]def variants_of( graph: BELGraph, node: Protein, modifications: Optional[Set[str]] = None, ) -> Set[Protein]: """Returns all variants of the given node.""" if modifications: return _get_filtered_variants_of(graph, node, modifications) return { v for u, v, key, data in graph.edges(keys=True, data=True) if ( u == node and data[RELATION] == HAS_VARIANT and pybel.struct.has_protein_modification(v) ) }
def _get_filtered_variants_of(graph, node, modifications): return { v for u, v, key, data in graph.edges(keys=True, data=True) if ( u == node and data[RELATION] == HAS_VARIANT and pybel.struct.has_protein_modification(v) and any( variant['identifier']['name'] in modifications for variant in v.variants if 'identifier' in variant if isinstance(variant, ProteinModification) ) ) }
[docs]def get_variants_to_controllers( graph: BELGraph, node: Protein, modifications: Optional[Set[str]] = None, ) -> Mapping[Protein, Set[Protein]]: """Get a mapping from variants of the given node to all of its upstream controllers.""" rv = defaultdict(set) variants = variants_of(graph, node, modifications) for controller, variant, data in graph.in_edges(variants, data=True): if data[RELATION] in CAUSAL_RELATIONS: rv[variant].add(controller) return rv