# -*- coding: utf-8 -*-
"""
Node Filters
------------
A node filter is a function that takes two arguments: a :class:`pybel.BELGraph` and a node tuple. It returns a boolean
representing whether the node passed the given test.
This module contains a set of default functions for filtering lists of nodes and building node filtering functions.
A general use for a node filter function is to use the built-in :func:`filter` in code like
:code:`filter(your_node_filter, graph)`
"""
from collections import Iterable
from pybel.constants import FUNCTION, LABEL, NAMESPACE, PATHOLOGY
from pybel.struct.filters import build_node_data_search, build_node_key_search, data_missing_key_builder
from pybel.struct.filters.node_filters import count_passed_node_filter
__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',
]
[docs]def summarize_node_filter(graph, node_filters):
"""Prints a summary of the number of nodes passing a given set of filters
:param pybel.BELGraph graph: A BEL graph
:param node_filters: A node filter or list/tuple of node filters
:type node_filters: types.FunctionType or iter[types.FunctionType]
"""
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(nbunch):
"""Builds a filter that only passes on nodes in the given list
:param iter[tuple] nbunch: An iterable of BEL nodes
:return: A node filter (graph, node) -> bool
:rtype: types.FunctionType
"""
node_set = set(nbunch)
def inclusion_filter(graph, node):
"""Passes only for a node that is in the enclosed node list
:param pybel.BELGraph graph: A BEL Graph
:param BaseEntity node:: A BEL node
:return: If the node is contained within the enclosed node list
:rtype: bool
"""
return node in node_set
return inclusion_filter
[docs]def node_exclusion_filter_builder(nodes):
"""Builds a filter that fails on nodes in the given list
:param nodes: A list of nodes
:type nodes: list
:return: A node filter (graph, node) -> bool
:rtype: types.FunctionType
"""
node_set = set(nodes)
def exclusion_filter(graph, node):
"""Passes only for a node that isn't in the enclosed node list
:param pybel.BELGraph graph: A BEL Graph
:param BaseEntity node:: A BEL node
:return: If the node isn't contained within the enclosed node list
:rtype: bool
"""
return node not in node_set
return exclusion_filter
def _single_function_inclusion_filter_builder(func):
def function_inclusion_filter(graph, node):
"""Pass only for a node that has the enclosed function.
:param BELGraph graph: A BEL Graph
:param BaseEntity node:: A BEL node
:return: If the node doesn't have the enclosed function
:rtype: bool
"""
return node[FUNCTION] == func
return function_inclusion_filter
def _collection_function_inclusion_builder(funcs):
funcs = set(funcs)
def functions_inclusion_filter(graph, node):
"""Pass only for a node that is one of the enclosed functions.
:param BELGraph graph: A BEL Graph
:param BaseEntity node:: A BEL node
:return: If the node doesn't have the enclosed functions
:rtype: bool
"""
return node[FUNCTION] in funcs
return functions_inclusion_filter
[docs]def function_inclusion_filter_builder(func):
"""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
:type func: str or iter[str]
:return: A node predicate
:rtype: (pybel.BELGraph, tuple) -> bool
"""
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):
"""Build a filter that fails on nodes of the given function(s).
:param func: A BEL Function or list/set/tuple of BEL functions
:type func: str or list[str] or tuple[str] or set[str]
:return: A node predicate
:rtype: (pybel.BELGraph, tuple) -> bool
"""
if isinstance(func, str):
def function_exclusion_filter(graph, node):
"""Pass only for a node that doesn't have the enclosed function.
:param pybel.BELGraph graph: A BEL Graph
:param BaseEntity node:: A BEL node
:return: If the node doesn't have the enclosed function
:rtype: bool
"""
return node[FUNCTION] != func
return function_exclusion_filter
elif isinstance(func, Iterable):
functions = set(func)
def functions_exclusion_filter(graph, node):
"""Pass only for a node that doesn't have the enclosed functions.
:param pybel.BELGraph graph: A BEL Graph
:param BaseEntity node:: A BEL node
:return: If the node doesn't have the enclosed functions
:rtype: bool
"""
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, namespace):
"""Build a filter function for matching the given BEL function with the given namespace or namespaces.
:param str func: A BEL function
:param str or iter[str] namespace: The namespace to serach by
:return: A node predicate
:rtype: (pybel.BELGraph, tuple) -> bool
"""
if isinstance(namespace, str):
def function_namespace_filter(graph, node):
"""Passes only for nodes that have the enclosed function and enclosed namespace
:param pybel.BELGraph graph: A BEL Graph
:param BaseEntity node:: A BEL node
:rtype: bool
"""
if func != node[FUNCTION]:
return False
return NAMESPACE in node and node[NAMESPACE] == namespace
return function_namespace_filter
elif isinstance(namespace, Iterable):
namespaces = set(namespace)
def function_namespaces_filter(graph, node):
"""Passes only for nodes that have the enclosed function and namespace in the enclose set
:param pybel.BELGraph graph: A BEL Graph
:param BaseEntity node:: A BEL node
:rtype: bool
"""
if func != node[FUNCTION]:
return False
return NAMESPACE in node and node[NAMESPACE] in namespaces
return function_namespaces_filter
raise ValueError('Invalid type for argument: {}'.format(namespace))
[docs]def namespace_inclusion_builder(namespace):
"""Build a predicate for namespace inclusion.
:param str or iter[str] namespace: A namespace or iter of namespaces
:return: A node predicate
:rtype: (pybel.BELGraph, tuple) -> bool
"""
if isinstance(namespace, str):
def namespace_filter(graph, node):
"""Passes only for a node that has the enclosed namespace
:param pybel.BELGraph graph: A BEL Graph
:param BaseEntity node:: A BEL node
:rtype: bool
"""
return NAMESPACE in node and node[NAMESPACE] == namespace
return namespace_filter
elif isinstance(namespace, Iterable):
namespaces = set(namespace)
def namespaces_filter(graph, node):
"""Pass only for a node that has a namespace in the enclosed set.
:param pybel.BELGraph graph: A BEL Graph
:param BaseEntity node:: A BEL node
:rtype: bool
"""
return NAMESPACE in node and node[NAMESPACE] in namespaces
return namespaces_filter
raise ValueError('Invalid type for argument: {}'.format(namespace))
[docs]def data_contains_key_builder(key):
"""Build a filter that passes only on nodes that have the given key in their data dictionary.
:param str key: A key for the node's data dictionary
:return: A node predicate
:rtype: (pybel.BELGraph, tuple) -> bool
"""
def data_contains_key(graph, node):
"""Pass only for a node that contains the enclosed key in its data dictionary.
:param pybel.BELGraph graph: A BEL Graph
:param BaseEntity node:: A BEL node
:return: If the node contains the enclosed key in its data dictionary
:rtype: bool
"""
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