for u, v in graph.edges():<EOL><INDENT>relation = pair_is_consistent(graph, u, v)<EOL>if not relation:<EOL><INDENT>continue<EOL><DEDENT>edges = [(u, v, k) for k in graph[u][v]]<EOL>graph.remove_edges_from(edges)<EOL>graph.add_edge(u, v, attr_dict={RELATION: relation})<EOL><DEDENT>

Collapse consistent edges together.

    .. warning:: This operation doesn't preserve evidences or other annotations


@transformation<EOL>def collapse_to_protein_interactions(graph: BELGraph) -> BELGraph:

rv: BELGraph = graph.copy()<EOL>collapse_to_genes(rv)<EOL>def is_edge_ppi(_: BELGraph, u: BaseEntity, v: BaseEntity, __: str) -> bool:<EOL><INDENT>"""<STR_LIT>"""<EOL>return isinstance(u, Gene) and isinstance(v, Gene)<EOL><DEDENT>return get_subgraph_by_edge_filter(rv, edge_predicates=[has_polarity, is_edge_ppi])<EOL>

Collapse to a graph made of only causal gene/protein edges.


@in_place_transformation<EOL>def collapse_nodes_with_same_names(graph: BELGraph) -> None:

survivor_mapping = defaultdict(set) <EOL>victims = set() <EOL>it = tqdm(itt.combinations(graph, r=<NUM_LIT:2>), total=graph.number_of_nodes() * (graph.number_of_nodes() - <NUM_LIT:1>) / <NUM_LIT:2>)<EOL>for a, b in it:<EOL><INDENT>if b in victims:<EOL><INDENT>continue<EOL><DEDENT>a_name, b_name = a.get(NAME), b.get(NAME)<EOL>if not a_name or not b_name or a_name.lower() != b_name.lower():<EOL><INDENT>continue<EOL><DEDENT>if a.keys() != b.keys():  <EOL><INDENT>continue<EOL><DEDENT>for k in set(a.keys()) - {NAME, NAMESPACE}:<EOL><INDENT>if a[k] != b[k]:  <EOL><INDENT>continue<EOL><DEDENT><DEDENT>survivor_mapping[a].add(b)<EOL>victims.add(b)<EOL><DEDENT>collapse_nodes(graph, survivor_mapping)<EOL>

Collapse all nodes with the same name, merging namespaces by picking first alphabetical one.


for u, v, k, d in graph.edges(data=True, keys=True):<EOL><INDENT>if d[RELATION] in TWO_WAY_RELATIONS:<EOL><INDENT>infer_missing_backwards_edge(graph, u, v, k)<EOL><DEDENT><DEDENT>

Add edges to the graph when a two way edge exists, and the opposite direction doesn't exist.

    Use: two way edges from BEL definition and/or axiomatic inverses of membership relations

    :param pybel.BELGraph graph: A BEL graph


def infer_missing_backwards_edge(graph, u, v, k):

if u in graph[v]:<EOL><INDENT>for attr_dict in graph[v][u].values():<EOL><INDENT>if attr_dict == graph[u][v][k]:<EOL><INDENT>return<EOL><DEDENT><DEDENT><DEDENT>graph.add_edge(v, u, key=k, **graph[u][v][k])<EOL>

Add the same edge, but in the opposite direction if not already present.

    :type graph: pybel.BELGraph
    :type u: tuple
    :type v: tuple
    :type k: int


for u, v in itt.combinations(subgraph, <NUM_LIT:2>):<EOL><INDENT>if not graph.has_edge(u, v):<EOL><INDENT>continue<EOL><DEDENT>for k in graph[u][v]:<EOL><INDENT>if k < <NUM_LIT:0>:<EOL><INDENT>subgraph.add_edge(u, v, key=k, **graph[u][v][k])<EOL><DEDENT><DEDENT><DEDENT>

Add the missing unqualified edges between entities in the subgraph that are contained within the full graph.

    :param pybel.BELGraph graph: The full BEL graph
    :param pybel.BELGraph subgraph: The query BEL subgraph


@in_place_transformation<EOL>def enrich_pubmed_citations(graph: BELGraph, manager: Manager) -> Set[str]:

pmids = get_pubmed_identifiers(graph)<EOL>pmid_data, errors = get_citations_by_pmids(manager=manager, pmids=pmids)<EOL>for u, v, k in filter_edges(graph, has_pubmed):<EOL><INDENT>pmid = graph[u][v][k][CITATION][CITATION_REFERENCE].strip()<EOL>if pmid not in pmid_data:<EOL><INDENT>log.warning('<STR_LIT>', pmid)<EOL>errors.add(pmid)<EOL>continue<EOL><DEDENT>graph[u][v][k][CITATION].update(pmid_data[pmid])<EOL><DEDENT>return errors<EOL>

Overwrite all PubMed citations with values from NCBI's eUtils lookup service.

    :return: A set of PMIDs for which the eUtils service crashed


@uni_in_place_transformation<EOL>def update_context(universe: BELGraph, graph: BELGraph):

for namespace in get_namespaces(graph):<EOL><INDENT>if namespace in universe.namespace_url:<EOL><INDENT>graph.namespace_url[namespace] = universe.namespace_url[namespace]<EOL><DEDENT>elif namespace in universe.namespace_pattern:<EOL><INDENT>graph.namespace_pattern[namespace] = universe.namespace_pattern[namespace]<EOL><DEDENT>else:<EOL><INDENT>log.warning('<STR_LIT>', namespace)<EOL><DEDENT><DEDENT>for annotation in get_annotations(graph):<EOL><INDENT>if annotation in universe.annotation_url:<EOL><INDENT>graph.annotation_url[annotation] = universe.annotation_url[annotation]<EOL><DEDENT>elif annotation in universe.annotation_pattern:<EOL><INDENT>graph.annotation_pattern[annotation] = universe.annotation_pattern[annotation]<EOL><DEDENT>elif annotation in universe.annotation_list:<EOL><INDENT>graph.annotation_list[annotation] = universe.annotation_list[annotation]<EOL><DEDENT>else:<EOL><INDENT>log.warning('<STR_LIT>', annotation)<EOL><DEDENT><DEDENT>

Update the context of a subgraph from the universe of all knowledge.


@in_place_transformation<EOL>def highlight_nodes(graph: BELGraph, nodes: Optional[Iterable[BaseEntity]] = None, color: Optional[str]=None):

color = color or NODE_HIGHLIGHT_DEFAULT_COLOR<EOL>for node in nodes if nodes is not None else graph:<EOL><INDENT>graph.node[node][NODE_HIGHLIGHT] = color<EOL><DEDENT>

Adds a highlight tag to the given nodes.

    :param graph: A BEL graph
    :param nodes: The nodes to add a highlight tag on
    :param color: The color to highlight (use something that works with CSS)


def is_node_highlighted(graph: BELGraph, node: BaseEntity) -> bool:

return NODE_HIGHLIGHT in graph.node[node]<EOL>

Returns if the given node is highlighted.

    :param graph: A BEL graph
    :param node: A BEL node
    :type node: tuple
    :return: Does the node contain highlight information?
    :rtype: bool


@in_place_transformation<EOL>def remove_highlight_nodes(graph: BELGraph, nodes: Optional[Iterable[BaseEntity]]=None) -> None:

for node in graph if nodes is None else nodes:<EOL><INDENT>if is_node_highlighted(graph, node):<EOL><INDENT>del graph.node[node][NODE_HIGHLIGHT]<EOL><DEDENT><DEDENT>

Removes the highlight from the given nodes, or all nodes if none given.

    :param graph: A BEL graph
    :param nodes: The list of nodes to un-highlight


@in_place_transformation<EOL>def highlight_edges(graph: BELGraph, edges=None, color: Optional[str]=None) -> None:

color = color or EDGE_HIGHLIGHT_DEFAULT_COLOR<EOL>for u, v, k, d in edges if edges is not None else graph.edges(keys=True, data=True):<EOL><INDENT>graph[u][v][k][EDGE_HIGHLIGHT] = color<EOL><DEDENT>

Adds a highlight tag to the given edges.

    :param graph: A BEL graph
    :param edges: The edges (4-tuples of u, v, k, d) to add a highlight tag on
    :type edges: iter[tuple]
    :param str color: The color to highlight (use something that works with CSS)


def is_edge_highlighted(graph: BELGraph, u, v, k) -> bool:

return EDGE_HIGHLIGHT in graph[u][v][k]<EOL>

Returns if the given edge is highlighted.

    :param graph: A BEL graph
    :return: Does the edge contain highlight information?
    :rtype: bool


@in_place_transformation<EOL>def remove_highlight_edges(graph: BELGraph, edges=None):

for u, v, k, _ in graph.edges(keys=True, data=True) if edges is None else edges:<EOL><INDENT>if is_edge_highlighted(graph, u, v, k):<EOL><INDENT>del graph[u][v][k][EDGE_HIGHLIGHT]<EOL><DEDENT><DEDENT>

Remove the highlight from the given edges, or all edges if none given.

    :param graph: A BEL graph
    :param edges: The edges (4-tuple of u,v,k,d) to remove the highlight from)
    :type edges: iter[tuple]


@uni_in_place_transformation<EOL>def highlight_subgraph(universe: BELGraph, graph: BELGraph):

highlight_nodes(universe, graph)<EOL>highlight_edges(universe, graph.edges())<EOL>

Highlight all nodes/edges in the universe that in the given graph.

    :param universe: The universe of knowledge
    :param graph: The BEL graph to mutate


@in_place_transformation<EOL>def remove_highlight_subgraph(graph: BELGraph, subgraph: BELGraph):

remove_highlight_nodes(graph, subgraph.nodes())<EOL>remove_highlight_edges(graph, subgraph.edges())<EOL>

Remove the highlight from all nodes/edges in the graph that are in the subgraph.

    :param graph: The BEL graph to mutate
    :param subgraph: The subgraph from which to remove the highlighting


@transformation<EOL>def random_by_nodes(graph: BELGraph, percentage: Optional[float] = None) -> BELGraph:

percentage = percentage or <NUM_LIT><EOL>assert <NUM_LIT:0> < percentage <= <NUM_LIT:1><EOL>nodes = graph.nodes()<EOL>n = int(len(nodes) * percentage)<EOL>subnodes = random.sample(nodes, n)<EOL>result = graph.subgraph(subnodes)<EOL>update_node_helper(graph, result)<EOL>return result<EOL>

Get a random graph by inducing over a percentage of the original nodes.

    :param graph: A BEL graph
    :param percentage: The percentage of edges to keep


@transformation<EOL>def random_by_edges(graph: BELGraph, percentage: Optional[float] = None) -> BELGraph:

percentage = percentage or <NUM_LIT><EOL>assert <NUM_LIT:0> < percentage <= <NUM_LIT:1><EOL>edges = graph.edges(keys=True)<EOL>n = int(graph.number_of_edges() * percentage)<EOL>subedges = random.sample(edges, n)<EOL>rv = graph.fresh_copy()<EOL>for u, v, k in subedges:<EOL><INDENT>safe_add_edge(rv, u, v, k, graph[u][v][k])<EOL><DEDENT>update_node_helper(graph, rv)<EOL>return rv<EOL>

Get a random graph by keeping a certain percentage of original edges.

    :param graph: A BEL graph
    :param percentage: What percentage of eges to take


@transformation<EOL>def shuffle_node_data(graph: BELGraph, key: str, percentage: Optional[float] = None) -> BELGraph:

percentage = percentage or <NUM_LIT><EOL>assert <NUM_LIT:0> < percentage <= <NUM_LIT:1><EOL>n = graph.number_of_nodes()<EOL>swaps = int(percentage * n * (n - <NUM_LIT:1>) / <NUM_LIT:2>)<EOL>result: BELGraph = graph.copy()<EOL>for _ in range(swaps):<EOL><INDENT>s, t = random.sample(result.node, <NUM_LIT:2>)<EOL>result.nodes[s][key], result.nodes[t][key] = result.nodes[t][key], result.nodes[s][key]<EOL><DEDENT>return result<EOL>

Shuffle the node's data.

    Useful for permutation testing.

    :param graph: A BEL graph
    :param key: The node data dictionary key
    :param percentage: What percentage of possible swaps to make


@transformation<EOL>def shuffle_relations(graph: BELGraph, percentage: Optional[str] = None) -> BELGraph:

percentage = percentage or <NUM_LIT><EOL>assert <NUM_LIT:0> < percentage <= <NUM_LIT:1><EOL>n = graph.number_of_edges()<EOL>swaps = int(percentage * n * (n - <NUM_LIT:1>) / <NUM_LIT:2>)<EOL>result: BELGraph = graph.copy()<EOL>edges = result.edges(keys=True)<EOL>for _ in range(swaps):<EOL><INDENT>(s1, t1, k1), (s2, t2, k2) = random.sample(edges, <NUM_LIT:2>)<EOL>result[s1][t1][k1], result[s2][t2][k2] = result[s2][t2][k2], result[s1][t1][k1]<EOL><DEDENT>return result<EOL>

Shuffle the relations.

    Useful for permutation testing.

    :param graph: A BEL graph
    :param percentage: What percentage of possible swaps to make


@main.group()<EOL>@connection_option<EOL>@click.pass_context<EOL>def io(ctx, connection: str):

ctx.obj = Manager(connection=connection)<EOL>


@namespace.command()<EOL>@click.argument('<STR_LIT:name>')<EOL>@click.argument('<STR_LIT>')<EOL>@click.argument('<STR_LIT>')<EOL>@click.argument('<STR_LIT>')<EOL>@click.option('<STR_LIT>', default=getuser())<EOL>@click.option('<STR_LIT>')<EOL>@click.option('<STR_LIT>')<EOL>@click.option('<STR_LIT>')<EOL>@click.option('<STR_LIT>')<EOL>@click.option('<STR_LIT>')<EOL>@click.option('<STR_LIT>', default=sys.stdin, help="<STR_LIT>")<EOL>@click.option('<STR_LIT>', type=click.File('<STR_LIT:w>'), default=sys.stdout)<EOL>def write(name, keyword, domain, citation, author, description, species, version, contact, license, values, output):

write_namespace(<EOL>name, keyword, domain, author, citation, values,<EOL>namespace_description=description,<EOL>namespace_species=species,<EOL>namespace_version=version,<EOL>author_contact=contact,<EOL>author_copyright=license,<EOL>file=output,<EOL>)<EOL>


@namespace.command()<EOL>@click.option('<STR_LIT>', '<STR_LIT>', type=click.File('<STR_LIT:r>'), default=sys.stdin, help="<STR_LIT>")<EOL>@click.option('<STR_LIT>', '<STR_LIT>', type=click.File('<STR_LIT:w>'), default=sys.stdout,<EOL>help="<STR_LIT>")<EOL>def convert_to_annotation(file, output):

resource = parse_bel_resource(file)<EOL>write_annotation(<EOL>keyword=resource['<STR_LIT>']['<STR_LIT>'],<EOL>values={k: '<STR_LIT>' for k in resource['<STR_LIT>']},<EOL>citation_name=resource['<STR_LIT>']['<STR_LIT>'],<EOL>description=resource['<STR_LIT>']['<STR_LIT>'],<EOL>file=output,<EOL>)<EOL>

Convert a namespace file to an annotation file.


@annotation.command()<EOL>@click.option('<STR_LIT>', '<STR_LIT>', type=click.File('<STR_LIT:r>'), default=sys.stdin, help="<STR_LIT>")<EOL>@click.option('<STR_LIT>', '<STR_LIT>', type=click.File('<STR_LIT:w>'), default=sys.stdout,<EOL>help="<STR_LIT>")<EOL>@click.option('<STR_LIT>', help="<STR_LIT>")<EOL>def convert_to_namespace(file, output, keyword):

Convert an annotation file to a namespace file.


Build a template BEL document with the given PubMed identifiers.


@document.command()<EOL>@click.argument('<STR_LIT>', nargs=-<NUM_LIT:1>)<EOL>@connection_option<EOL>@click.option('<STR_LIT>', '<STR_LIT>', type=click.File('<STR_LIT:r>'), default=sys.stdin, help='<STR_LIT>')<EOL>@click.option('<STR_LIT>', '<STR_LIT>', help='<STR_LIT>'.format(os.getcwd()))<EOL>def serialize_namespaces(namespaces, connection: str, path, directory):

from .definition_utils import export_namespaces<EOL>graph = from_lines(path, manager=connection)<EOL>export_namespaces(namespaces, graph, directory)<EOL>

Parse a BEL document then serializes the given namespaces (errors and all) to the given directory.


@io.command()<EOL>@graph_pickle_argument<EOL>@click.option('<STR_LIT>', '<STR_LIT>', type=click.File('<STR_LIT:w>'), default=sys.stdout)<EOL>def get_pmids(graph: BELGraph, output: TextIO):

for pmid in get_pubmed_identifiers(graph):<EOL><INDENT>click.echo(pmid, file=output)<EOL><DEDENT>

Output PubMed identifiers from a graph to a stream.


@in_place_transformation<EOL>def overlay_data(graph: BELGraph,<EOL>data: Mapping[BaseEntity, Any],<EOL>label: Optional[str] = None,<EOL>overwrite: bool = False,<EOL>) -> None:

if label is None:<EOL><INDENT>label = WEIGHT<EOL><DEDENT>for node, value in data.items():<EOL><INDENT>if node not in graph:<EOL><INDENT>log.debug('<STR_LIT>', node)<EOL>continue<EOL><DEDENT>if label in graph.nodes[node] and not overwrite:<EOL><INDENT>log.debug('<STR_LIT>', label, node)<EOL>continue<EOL><DEDENT>graph.nodes[node][label] = value<EOL><DEDENT>

Overlays tabular data on the network

    :param graph: A BEL Graph
    :param data: A dictionary of {tuple node: data for that node}
    :param label: The annotation label to put in the node dictionary
    :param overwrite: Should old annotations be overwritten?


@in_place_transformation<EOL>def overlay_type_data(graph: BELGraph,<EOL>data: Mapping[str, float],<EOL>func: str,<EOL>namespace: str,<EOL>label: Optional[str] = None,<EOL>overwrite: bool = False,<EOL>impute: Optional[float] = None,<EOL>) -> None:

new_data = {<EOL>node: data.get(node[NAME], impute)<EOL>for node in filter_nodes(graph, function_namespace_inclusion_builder(func, namespace))<EOL>}<EOL>overlay_data(graph, new_data, label=label, overwrite=overwrite)<EOL>

Overlay tabular data on the network for data that comes from an data set with identifiers that lack
    namespaces.

    For example, if you want to overlay differential gene expression data from a table, that table
    probably has HGNC identifiers, but no specific annotations that they are in the HGNC namespace or
    that the entities to which they refer are RNA.

    :param graph: A BEL Graph
    :param dict data: A dictionary of {name: data}
    :param func: The function of the keys in the data dictionary
    :param namespace: The namespace of the keys in the data dictionary
    :param label: The annotation label to put in the node dictionary
    :param overwrite: Should old annotations be overwritten?
    :param impute: The value to use for missing data


def load_differential_gene_expression(path: str,<EOL>gene_symbol_column: str = '<STR_LIT>',<EOL>logfc_column: str = '<STR_LIT>',<EOL>aggregator: Optional[Callable[[List[float]], float]] = None,<EOL>) -> Mapping[str, float]:

if aggregator is None:<EOL><INDENT>aggregator = np.median<EOL><DEDENT>df = pd.read_csv(path)<EOL>assert gene_symbol_column in df.columns<EOL>assert logfc_column in df.columns<EOL>df = df.loc[df[gene_symbol_column].notnull(), [gene_symbol_column, logfc_column]]<EOL>values = defaultdict(list)<EOL>for _, gene_symbol, log_fold_change in df.itertuples():<EOL><INDENT>values[gene_symbol].append(log_fold_change)<EOL><DEDENT>return {<EOL>gene_symbol: aggregator(log_fold_changes)<EOL>for gene_symbol, log_fold_changes in values.items()<EOL>}<EOL>

Load and pre-process a differential gene expression data.

    :param path: The path to the CSV
    :param gene_symbol_column: The header of the gene symbol column in the data frame
    :param logfc_column: The header of the log-fold-change column in the data frame
    :param aggregator: A function that aggregates a list of differential gene expression values. Defaults to
                       :func:`numpy.median`. Could also use: :func:`numpy.mean`, :func:`numpy.average`,
                       :func:`numpy.min`, or :func:`numpy.max`
    :return: A dictionary of {gene symbol: log fold change}


def summarize_edge_filter(graph: BELGraph, edge_predicates: EdgePredicates) -> None:

passed = count_passed_edge_filter(graph, edge_predicates)<EOL>print('<STR_LIT>'.format(<EOL>passed, graph.number_of_edges(),<EOL>(<EOL>'<STR_LIT:U+002CU+0020>'.join(edge_filter.__name__ for edge_filter in edge_predicates)<EOL>if isinstance(edge_predicates, Iterable) else<EOL>edge_predicates.__name__<EOL>)<EOL>))<EOL>

Print a summary of the number of edges passing a given set of filters.


def build_edge_data_filter(annotations: Mapping, partial_match: bool = True) -> EdgePredicate:

@edge_predicate<EOL>def annotation_dict_filter(data: EdgeData) -> bool:<EOL><INDENT>"""<STR_LIT>"""<EOL>return subdict_matches(data, annotations, partial_match=partial_match)<EOL><DEDENT>return annotation_dict_filter<EOL>

Build a filter that keeps edges whose data dictionaries are super-dictionaries to the given dictionary.

    :param annotations: The annotation query dict to match
    :param partial_match: Should the query values be used as partial or exact matches? Defaults to :code:`True`.


def build_pmid_inclusion_filter(pmids: Strings) -> EdgePredicate:

if isinstance(pmids, str):<EOL><INDENT>@edge_predicate<EOL>def pmid_inclusion_filter(data: EdgeData) -> bool:<EOL><INDENT>"""<STR_LIT>"""<EOL>return has_pubmed(data) and data[CITATION][CITATION_REFERENCE] == pmids<EOL><DEDENT><DEDENT>elif isinstance(pmids, Iterable):<EOL><INDENT>pmids = set(pmids)<EOL>@edge_predicate<EOL>def pmid_inclusion_filter(data: EdgeData) -> bool:<EOL><INDENT>"""<STR_LIT>"""<EOL>return has_pubmed(data) and data[CITATION][CITATION_REFERENCE] in pmids<EOL><DEDENT><DEDENT>else:<EOL><INDENT>raise TypeError<EOL><DEDENT>return pmid_inclusion_filter<EOL>

Pass for edges with citations whose references are one of the given PubMed identifiers.

    :param pmids: A PubMed identifier or list of PubMed identifiers to filter for


def build_pmid_exclusion_filter(pmids: Strings) -> EdgePredicate:

if isinstance(pmids, str):<EOL><INDENT>@edge_predicate<EOL>def pmid_exclusion_filter(data: EdgeData) -> bool:<EOL><INDENT>"""<STR_LIT>"""<EOL>return has_pubmed(data) and data[CITATION][CITATION_REFERENCE] != pmids<EOL><DEDENT><DEDENT>elif isinstance(pmids, Iterable):<EOL><INDENT>pmids = set(pmids)<EOL>@edge_predicate<EOL>def pmid_exclusion_filter(data: EdgeData) -> bool:<EOL><INDENT>"""<STR_LIT>"""<EOL>return has_pubmed(data) and data[CITATION][CITATION_REFERENCE] not in pmids<EOL><DEDENT><DEDENT>else:<EOL><INDENT>raise TypeError<EOL><DEDENT>return pmid_exclusion_filter<EOL>

Fail for edges with citations whose references are one of the given PubMed identifiers.

    :param pmids: A PubMed identifier or list of PubMed identifiers to filter against


def build_author_inclusion_filter(authors: Strings) -> EdgePredicate:

if isinstance(authors, str):<EOL><INDENT>@edge_predicate<EOL>def author_filter(data: EdgeData) -> bool:<EOL><INDENT>"""<STR_LIT>"""<EOL>return has_authors(data) and authors in data[CITATION][CITATION_AUTHORS]<EOL><DEDENT><DEDENT>elif isinstance(authors, Iterable):<EOL><INDENT>authors = set(authors)<EOL>@edge_predicate<EOL>def author_filter(data: EdgeData) -> bool:<EOL><INDENT>"""<STR_LIT>"""<EOL>return has_authors(data) and any(<EOL>author in data[CITATION][CITATION_AUTHORS]<EOL>for author in authors<EOL>)<EOL><DEDENT><DEDENT>else:<EOL><INDENT>raise TypeError<EOL><DEDENT>return author_filter<EOL>

Pass only for edges with author information that matches one of the given authors.

    :param authors: The author or list of authors to filter by


def node_has_namespace(node: BaseEntity, namespace: str) -> bool:

ns = node.get(NAMESPACE)<EOL>return ns is not None and ns == namespace<EOL>

Pass for nodes that have the given namespace.


def node_has_namespaces(node: BaseEntity, namespaces: Set[str]) -> bool:

ns = node.get(NAMESPACE)<EOL>return ns is not None and ns in namespaces<EOL>

Pass for nodes that have one of the given namespaces.


def build_source_namespace_filter(namespaces: Strings) -> EdgePredicate:

if isinstance(namespaces, str):<EOL><INDENT>def source_namespace_filter(_, u: BaseEntity, __, ___) -> bool:<EOL><INDENT>return node_has_namespace(u, namespaces)<EOL><DEDENT><DEDENT>elif isinstance(namespaces, Iterable):<EOL><INDENT>namespaces = set(namespaces)<EOL>def source_namespace_filter(_, u: BaseEntity, __, ___) -> bool:<EOL><INDENT>return node_has_namespaces(u, namespaces)<EOL><DEDENT><DEDENT>else:<EOL><INDENT>raise TypeError<EOL><DEDENT>return source_namespace_filter<EOL>

Pass for edges whose source nodes have the given namespace or one of the given namespaces.

    :param namespaces: The namespace or namespaces to filter by


def build_target_namespace_filter(namespaces: Strings) -> EdgePredicate:

if isinstance(namespaces, str):<EOL><INDENT>def target_namespace_filter(_, __, v: BaseEntity, ___) -> bool:<EOL><INDENT>return node_has_namespace(v, namespaces)<EOL><DEDENT><DEDENT>elif isinstance(namespaces, Iterable):<EOL><INDENT>namespaces = set(namespaces)<EOL>def target_namespace_filter(_, __, v: BaseEntity, ___) -> bool:<EOL><INDENT>return node_has_namespaces(v, namespaces)<EOL><DEDENT><DEDENT>else:<EOL><INDENT>raise TypeError<EOL><DEDENT>return target_namespace_filter<EOL>

Only passes for edges whose target nodes have the given namespace or one of the given namespaces

    :param namespaces: The namespace or namespaces to filter by


@in_place_transformation<EOL>def remove_nodes_by_function(graph: BELGraph, func: Strings) -> None:

remove_filtered_nodes(graph, function_inclusion_filter_builder(func))<EOL>

Remove nodes with the given function.

    This could be useful directly to remove pathologies.


@in_place_transformation<EOL>def remove_nodes_by_namespace(graph: BELGraph, namespace: Strings) -> None:

remove_filtered_nodes(graph, namespace_inclusion_builder(namespace))<EOL>

Remove nodes with the given namespace.

    This might be useful to exclude information learned about distant species, such as excluding all information
    from MGI and RGD in diseases where mice and rats don't give much insight to the human disease mechanism.


@register_deprecated('<STR_LIT>')<EOL>@in_place_transformation<EOL>def remove_mouse_nodes(graph: BELGraph) -> None:

remove_nodes_by_namespace(graph, ['<STR_LIT>', '<STR_LIT>'])<EOL>

Remove nodes using the MGI and MGIID namespaces.


@register_deprecated('<STR_LIT>')<EOL>@in_place_transformation<EOL>def remove_rat_nodes(graph: BELGraph) -> None:

Remove nodes using the RGD and RGDID namespaces.


@in_place_transformation<EOL>def remove_nodes_by_function_namespace(graph: BELGraph, func: str, namespace: Strings) -> None:

remove_filtered_nodes(graph, function_namespace_inclusion_builder(func, namespace))<EOL>

Remove nodes with the given function and namespace.

    This might be useful to exclude information learned about distant species, such as excluding all information
    from MGI and RGD in diseases where mice and rats don't give much insight to the human disease mechanism.


def summarize_node_filter(graph: BELGraph, node_filters: NodePredicates) -> None:

passed = count_passed_node_filter(graph, node_filters)<EOL>print('<STR_LIT>'.format(passed, graph.number_of_nodes()))<EOL>

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


def node_inclusion_filter_builder(nodes: Iterable[BaseEntity]) -> NodePredicate:

node_set = set(nodes)<EOL>def inclusion_filter(_: BELGraph, node: BaseEntity) -> bool:<EOL><INDENT>"""<STR_LIT>"""<EOL>return node in node_set<EOL><DEDENT>return inclusion_filter<EOL>

Build a filter that only passes on nodes in the given list.

    :param nodes: An iterable of BEL nodes


def node_exclusion_filter_builder(nodes: Iterable[BaseEntity]) -> NodePredicate:

node_set = set(nodes)<EOL>def exclusion_filter(_: BELGraph, node: BaseEntity) -> bool:<EOL><INDENT>"""<STR_LIT>"""<EOL>return node not in node_set<EOL><DEDENT>return exclusion_filter<EOL>

Build a filter that fails on nodes in the given list.


def function_inclusion_filter_builder(func: Strings) -> NodePredicate:

if isinstance(func, str):<EOL><INDENT>return _single_function_inclusion_filter_builder(func)<EOL><DEDENT>elif isinstance(func, Iterable):<EOL><INDENT>return _collection_function_inclusion_builder(func)<EOL><DEDENT>raise ValueError('<STR_LIT>'.format(func))<EOL>

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


def function_exclusion_filter_builder(func: Strings) -> NodePredicate:

if isinstance(func, str):<EOL><INDENT>def function_exclusion_filter(_: BELGraph, node: BaseEntity) -> bool:<EOL><INDENT>"""<STR_LIT>"""<EOL>return node[FUNCTION] != func<EOL><DEDENT>return function_exclusion_filter<EOL><DEDENT>elif isinstance(func, Iterable):<EOL><INDENT>functions = set(func)<EOL>def functions_exclusion_filter(_: BELGraph, node: BaseEntity) -> bool:<EOL><INDENT>"""<STR_LIT>"""<EOL>return node[FUNCTION] not in functions<EOL><DEDENT>return functions_exclusion_filter<EOL><DEDENT>raise ValueError('<STR_LIT>'.format(func))<EOL>

Build a filter that fails on nodes of the given function(s).

    :param func: A BEL Function or list/set/tuple of BEL functions


def function_namespace_inclusion_builder(func: str, namespace: Strings) -> NodePredicate:

if isinstance(namespace, str):<EOL><INDENT>def function_namespaces_filter(_: BELGraph, node: BaseEntity) -> bool:<EOL><INDENT>"""<STR_LIT>"""<EOL>if func != node[FUNCTION]:<EOL><INDENT>return False<EOL><DEDENT>return NAMESPACE in node and node[NAMESPACE] == namespace<EOL><DEDENT><DEDENT>elif isinstance(namespace, Iterable):<EOL><INDENT>namespaces = set(namespace)<EOL>def function_namespaces_filter(_: BELGraph, node: BaseEntity) -> bool:<EOL><INDENT>"""<STR_LIT>"""<EOL>if func != node[FUNCTION]:<EOL><INDENT>return False<EOL><DEDENT>return NAMESPACE in node and node[NAMESPACE] in namespaces<EOL><DEDENT><DEDENT>else:<EOL><INDENT>raise ValueError('<STR_LIT>'.format(namespace))<EOL><DEDENT>return function_namespaces_filter<EOL>

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


def data_contains_key_builder(key: str) -> NodePredicate:

def data_contains_key(_: BELGraph, node: BaseEntity) -> bool:<EOL><INDENT>"""<STR_LIT>"""<EOL>return key in node<EOL><DEDENT>return data_contains_key<EOL>

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 variants_of(<EOL>graph: BELGraph,<EOL>node: Protein,<EOL>modifications: Optional[Set[str]] = None,<EOL>) -> Set[Protein]:

if modifications:<EOL><INDENT>return _get_filtered_variants_of(graph, node, modifications)<EOL><DEDENT>return {<EOL>v<EOL>for u, v, key, data in graph.edges(keys=True, data=True)<EOL>if (<EOL>u == node<EOL>and data[RELATION] == HAS_VARIANT<EOL>and pybel.struct.has_protein_modification(v)<EOL>)<EOL>}<EOL>


def get_variants_to_controllers(<EOL>graph: BELGraph,<EOL>node: Protein,<EOL>modifications: Optional[Set[str]] = None,<EOL>) -> Mapping[Protein, Set[Protein]]:

rv = defaultdict(set)<EOL>variants = variants_of(graph, node, modifications)<EOL>for controller, variant, data in graph.in_edges(variants, data=True):<EOL><INDENT>if data[RELATION] in CAUSAL_RELATIONS:<EOL><INDENT>rv[variant].add(controller)<EOL><DEDENT><DEDENT>return rv<EOL>

Get a mapping from variants of the given node to all of its upstream controllers.


def insert_graph(self, graph: BELGraph, **_kwargs) -> Network:

result = _Namespace()<EOL>result.id = len(self.networks)<EOL>self.networks[result.id] = graph<EOL>return result<EOL>

Insert a graph and return the resulting ORM object (mocked).


def get_graph_by_id(self, network_id: int) -> BELGraph:

return self.networks[network_id]<EOL>


def get_graphs_by_ids(self, network_ids: Iterable[int]) -> List[BELGraph]:

return [<EOL>self.networks[network_id]<EOL>for network_id in network_ids<EOL>]<EOL>

Get several graphs by their identifiers.


def group_dict_set(iterator: Iterable[Tuple[A, B]]) -> Mapping[A, Set[B]]:

d = defaultdict(set)<EOL>for key, value in iterator:<EOL><INDENT>d[key].add(value)<EOL><DEDENT>return dict(d)<EOL>

Make a dict that accumulates the values for each key in an iterator of doubles.


def get_edge_relations(graph: BELGraph) -> Mapping[Tuple[BaseEntity, BaseEntity], Set[str]]:

return group_dict_set(<EOL>((u, v), d[RELATION])<EOL>for u, v, d in graph.edges(data=True)<EOL>)<EOL>

Build a dictionary of {node pair: set of edge types}.


def count_unique_relations(graph: BELGraph) -> Counter:

return Counter(itt.chain.from_iterable(get_edge_relations(graph).values()))<EOL>

Return a histogram of the different types of relations present in a graph.

    Note: this operation only counts each type of edge once for each pair of nodes


def get_annotations_containing_keyword(graph: BELGraph, keyword: str) -> List[Mapping[str, str]]:

return [<EOL>{<EOL>'<STR_LIT>': annotation,<EOL>'<STR_LIT:value>': value<EOL>}<EOL>for annotation, value in iter_annotation_value_pairs(graph)<EOL>if keyword.lower() in value.lower()<EOL>]<EOL>

Get annotation/value pairs for values for whom the search string is a substring

    :param graph: A BEL graph
    :param keyword: Search for annotations whose values have this as a substring


def count_annotation_values(graph: BELGraph, annotation: str) -> Counter:

return Counter(iter_annotation_values(graph, annotation))<EOL>

Count in how many edges each annotation appears in a graph

    :param graph: A BEL graph
    :param annotation: The annotation to count
    :return: A Counter from {annotation value: frequency}


def count_annotation_values_filtered(graph: BELGraph,<EOL>annotation: str,<EOL>source_predicate: Optional[NodePredicate] = None,<EOL>target_predicate: Optional[NodePredicate] = None,<EOL>) -> Counter:

if source_predicate and target_predicate:<EOL><INDENT>return Counter(<EOL>data[ANNOTATIONS][annotation]<EOL>for u, v, data in graph.edges(data=True)<EOL>if edge_has_annotation(data, annotation) and source_predicate(graph, u) and target_predicate(graph, v)<EOL>)<EOL><DEDENT>elif source_predicate:<EOL><INDENT>return Counter(<EOL>data[ANNOTATIONS][annotation]<EOL>for u, v, data in graph.edges(data=True)<EOL>if edge_has_annotation(data, annotation) and source_predicate(graph, u)<EOL>)<EOL><DEDENT>elif target_predicate:<EOL><INDENT>return Counter(<EOL>data[ANNOTATIONS][annotation]<EOL>for u, v, data in graph.edges(data=True)<EOL>if edge_has_annotation(data, annotation) and target_predicate(graph, u)<EOL>)<EOL><DEDENT>else:<EOL><INDENT>return Counter(<EOL>data[ANNOTATIONS][annotation]<EOL>for u, v, data in graph.edges(data=True)<EOL>if edge_has_annotation(data, annotation)<EOL>)<EOL><DEDENT>

Count in how many edges each annotation appears in a graph, but filter out source nodes and target nodes.

    See :func:`pybel_tools.utils.keep_node` for a basic filter.

    :param graph: A BEL graph
    :param annotation: The annotation to count
    :param source_predicate: A predicate (graph, node) -> bool for keeping source nodes
    :param target_predicate: A predicate (graph, node) -> bool for keeping target nodes
    :return: A Counter from {annotation value: frequency}


def pair_is_consistent(graph: BELGraph, u: BaseEntity, v: BaseEntity) -> Optional[str]:

relations = {data[RELATION] for data in graph[u][v].values()}<EOL>if <NUM_LIT:1> != len(relations):<EOL><INDENT>return<EOL><DEDENT>return list(relations)[<NUM_LIT:0>]<EOL>

Return if the edges between the given nodes are consistent, meaning they all have the same relation.

    :return: If the edges aren't consistent, return false, otherwise return the relation type


def get_contradictory_pairs(graph: BELGraph) -> Iterable[Tuple[BaseEntity, BaseEntity]]:

for u, v in graph.edges():<EOL><INDENT>if pair_has_contradiction(graph, u, v):<EOL><INDENT>yield u, v<EOL><DEDENT><DEDENT>

Iterates over contradictory node pairs in the graph based on their causal relationships

    :return: An iterator over (source, target) node pairs that have contradictory causal edges


def get_consistent_edges(graph: BELGraph) -> Iterable[Tuple[BaseEntity, BaseEntity]]:

for u, v in graph.edges():<EOL><INDENT>if pair_is_consistent(graph, u, v):<EOL><INDENT>yield u, v<EOL><DEDENT><DEDENT>

Yield pairs of (source node, target node) for which all of their edges have the same type of relation.

    :return: An iterator over (source, target) node pairs corresponding to edges with many inconsistent relations


def pair_has_contradiction(graph: BELGraph, u: BaseEntity, v: BaseEntity) -> bool:

relations = {data[RELATION] for data in graph[u][v].values()}<EOL>return relation_set_has_contradictions(relations)<EOL>

Check if a pair of nodes has any contradictions in their causal relationships.

    Assumes both nodes are in the graph.


def relation_set_has_contradictions(relations: Set[str]) -> bool:

has_increases = any(relation in CAUSAL_INCREASE_RELATIONS for relation in relations)<EOL>has_decreases = any(relation in CAUSAL_DECREASE_RELATIONS for relation in relations)<EOL>has_cnc = any(relation == CAUSES_NO_CHANGE for relation in relations)<EOL>return <NUM_LIT:1> < sum([has_cnc, has_decreases, has_increases])<EOL>

Return if the set of BEL relations contains a contradiction.


def plot_summary_axes(graph: BELGraph, lax, rax, logx=True):

ntc = count_functions(graph)<EOL>etc = count_relations(graph)<EOL>df = pd.DataFrame.from_dict(dict(ntc), orient='<STR_LIT:index>')<EOL>df_ec = pd.DataFrame.from_dict(dict(etc), orient='<STR_LIT:index>')<EOL>df.sort_values(<NUM_LIT:0>, ascending=True).plot(kind='<STR_LIT>', logx=logx, ax=lax)<EOL>lax.set_title('<STR_LIT>'.format(graph.number_of_nodes()))<EOL>df_ec.sort_values(<NUM_LIT:0>, ascending=True).plot(kind='<STR_LIT>', logx=logx, ax=rax)<EOL>rax.set_title('<STR_LIT>'.format(graph.number_of_edges()))<EOL>

Plots your graph summary statistics on the given axes.

    After, you should run :func:`plt.tight_layout` and you must run :func:`plt.show` to view.

    Shows:
    1. Count of nodes, grouped by function type
    2. Count of edges, grouped by relation type

    :param pybel.BELGraph graph: A BEL graph
    :param lax: An axis object from matplotlib
    :param rax: An axis object from matplotlib

    Example usage:

    >>> import matplotlib.pyplot as plt
    >>> from pybel import from_pickle
    >>> from pybel_tools.summary import plot_summary_axes
    >>> graph = from_pickle('~/dev/bms/aetionomy/parkinsons.gpickle')
    >>> fig, axes = plt.subplots(1, 2, figsize=(10, 4))
    >>> plot_summary_axes(graph, axes[0], axes[1])
    >>> plt.tight_layout()
    >>> plt.show()


def plot_summary(graph: BELGraph, plt, logx=True, **kwargs):

fig, axes = plt.subplots(<NUM_LIT:1>, <NUM_LIT:2>, **kwargs)<EOL>lax = axes[<NUM_LIT:0>]<EOL>rax = axes[<NUM_LIT:1>]<EOL>plot_summary_axes(graph, lax, rax, logx=logx)<EOL>plt.tight_layout()<EOL>return fig, axes<EOL>

Plots your graph summary statistics. This function is a thin wrapper around :func:`plot_summary_axis`. It
    automatically takes care of building figures given matplotlib's pyplot module as an argument. After, you need
    to run :func:`plt.show`.

    :code:`plt` is given as an argument to avoid needing matplotlib as a dependency for this function

    Shows:

    1. Count of nodes, grouped by function type
    2. Count of edges, grouped by relation type

    :param plt: Give :code:`matplotlib.pyplot` to this parameter
    :param kwargs: keyword arguments to give to :func:`plt.subplots`

    Example usage:

    >>> import matplotlib.pyplot as plt
    >>> from pybel import from_pickle
    >>> from pybel_tools.summary import plot_summary
    >>> graph = from_pickle('~/dev/bms/aetionomy/parkinsons.gpickle')
    >>> plot_summary(graph, plt, figsize=(10, 4))
    >>> plt.show()


def count_subgraph_sizes(graph: BELGraph, annotation: str = '<STR_LIT>') -> Counter[int]:

return count_dict_values(group_nodes_by_annotation(graph, annotation))<EOL>

Count the number of nodes in each subgraph induced by an annotation.

    :param annotation: The annotation to group by and compare. Defaults to 'Subgraph'
    :return: A dictionary from {annotation value: number of nodes}


def calculate_subgraph_edge_overlap(<EOL>graph: BELGraph,<EOL>annotation: str = '<STR_LIT>'<EOL>) -> Tuple[<EOL>Mapping[str, EdgeSet],<EOL>Mapping[str, Mapping[str, EdgeSet]],<EOL>Mapping[str, Mapping[str, EdgeSet]],<EOL>Mapping[str, Mapping[str, float]],<EOL>]:

sg2edge = defaultdict(set)<EOL>for u, v, d in graph.edges(data=True):<EOL><INDENT>if not edge_has_annotation(d, annotation):<EOL><INDENT>continue<EOL><DEDENT>sg2edge[d[ANNOTATIONS][annotation]].add((u, v))<EOL><DEDENT>subgraph_intersection = defaultdict(dict)<EOL>subgraph_union = defaultdict(dict)<EOL>result = defaultdict(dict)<EOL>for sg1, sg2 in itt.product(sg2edge, repeat=<NUM_LIT:2>):<EOL><INDENT>subgraph_intersection[sg1][sg2] = sg2edge[sg1] & sg2edge[sg2]<EOL>subgraph_union[sg1][sg2] = sg2edge[sg1] | sg2edge[sg2]<EOL>result[sg1][sg2] = len(subgraph_intersection[sg1][sg2]) / len(subgraph_union[sg1][sg2])<EOL><DEDENT>return sg2edge, subgraph_intersection, subgraph_union, result<EOL>

Build a DatafFame to show the overlap between different sub-graphs.

    Options:
    1. Total number of edges overlap (intersection)
    2. Percentage overlap (tanimoto similarity)

    :param graph: A BEL graph
    :param annotation: The annotation to group by and compare. Defaults to 'Subgraph'
    :return: {subgraph: set of edges}, {(subgraph 1, subgraph2): set of intersecting edges},
            {(subgraph 1, subgraph2): set of unioned edges}, {(subgraph 1, subgraph2): tanimoto similarity},


def summarize_subgraph_edge_overlap(graph: BELGraph, annotation: str = '<STR_LIT>') -> Mapping[str, Mapping[str, float]]:

_, _, _, subgraph_overlap = calculate_subgraph_edge_overlap(graph, annotation)<EOL>return subgraph_overlap<EOL>

Return a similarity matrix between all subgraphs (or other given annotation).

    :param annotation: The annotation to group by and compare. Defaults to :code:`"Subgraph"`
    :return: A similarity matrix in a dict of dicts
    :rtype: dict


def summarize_subgraph_node_overlap(graph: BELGraph, node_predicates=None, annotation: str = '<STR_LIT>'):

r1 = group_nodes_by_annotation_filtered(graph, node_predicates=node_predicates, annotation=annotation)<EOL>return calculate_tanimoto_set_distances(r1)<EOL>

Calculate the subgraph similarity tanimoto similarity in nodes passing the given filter.

    Provides an alternate view on subgraph similarity, from a more node-centric view


def rank_subgraph_by_node_filter(graph: BELGraph,<EOL>node_predicates: Union[NodePredicate, Iterable[NodePredicate]],<EOL>annotation: str = '<STR_LIT>',<EOL>reverse: bool = True,<EOL>) -> List[Tuple[str, int]]:

r1 = group_nodes_by_annotation_filtered(graph, node_predicates=node_predicates, annotation=annotation)<EOL>r2 = count_dict_values(r1)<EOL>return sorted(r2.items(), key=itemgetter(<NUM_LIT:1>), reverse=reverse)<EOL>

Rank sub-graphs by which have the most nodes matching an given filter.

    A use case for this function would be to identify which subgraphs contain the most differentially expressed
    genes.

    >>> from pybel import from_pickle
    >>> from pybel.constants import GENE
    >>> from pybel_tools.integration import overlay_type_data
    >>> from pybel_tools.summary import rank_subgraph_by_node_filter
    >>> import pandas as pd
    >>> graph = from_pickle('~/dev/bms/aetionomy/alzheimers.gpickle')
    >>> df = pd.read_csv('~/dev/bananas/data/alzheimers_dgxp.csv', columns=['Gene', 'log2fc'])
    >>> data = {gene: log2fc for _, gene, log2fc in df.itertuples()}
    >>> overlay_type_data(graph, data, 'log2fc', GENE, 'HGNC', impute=0.0)
    >>> results = rank_subgraph_by_node_filter(graph, lambda g, n: 1.3 < abs(g[n]['log2fc']))


def get_namespaces_with_incorrect_names(graph: BELGraph) -> Set[str]:

return {<EOL>exc.namespace<EOL>for _, exc, _ in graph.warnings<EOL>if isinstance(exc, (MissingNamespaceNameWarning, MissingNamespaceRegexWarning))<EOL>}<EOL>

Return the set of all namespaces with incorrect names in the graph.


def get_undefined_namespaces(graph: BELGraph) -> Set[str]:

return {<EOL>exc.namespace<EOL>for _, exc, _ in graph.warnings<EOL>if isinstance(exc, UndefinedNamespaceWarning)<EOL>}<EOL>

Get all namespaces that are used in the BEL graph aren't actually defined.


def get_incorrect_names_by_namespace(graph: BELGraph, namespace: str) -> Set[str]:

return {<EOL>exc.name<EOL>for _, exc, _ in graph.warnings<EOL>if isinstance(exc, (MissingNamespaceNameWarning, MissingNamespaceRegexWarning)) and exc.namespace == namespace<EOL>}<EOL>

Return the set of all incorrect names from the given namespace in the graph.

    :return: The set of all incorrect names from the given namespace in the graph


def get_undefined_namespace_names(graph: BELGraph, namespace: str) -> Set[str]:

return {<EOL>exc.name<EOL>for _, exc, _ in graph.warnings<EOL>if isinstance(exc, UndefinedNamespaceWarning) and exc.namespace == namespace<EOL>}<EOL>

Get the names from a namespace that wasn't actually defined.

    :return: The set of all names from the undefined namespace


def get_incorrect_names(graph: BELGraph) -> Mapping[str, Set[str]]:

return {<EOL>namespace: get_incorrect_names_by_namespace(graph, namespace)<EOL>for namespace in get_namespaces(graph)<EOL>}<EOL>

Return the dict of the sets of all incorrect names from the given namespace in the graph.

    :return: The set of all incorrect names from the given namespace in the graph


def get_undefined_annotations(graph: BELGraph) -> Set[str]:

return {<EOL>exc.annotation<EOL>for _, exc, _ in graph.warnings<EOL>if isinstance(exc, UndefinedAnnotationWarning)<EOL>}<EOL>

Get all annotations that aren't actually defined.

    :return: The set of all undefined annotations


def calculate_incorrect_name_dict(graph: BELGraph) -> Mapping[str, str]:

missing = defaultdict(list)<EOL>for _, e, ctx in graph.warnings:<EOL><INDENT>if not isinstance(e, (MissingNamespaceNameWarning, MissingNamespaceRegexWarning)):<EOL><INDENT>continue<EOL><DEDENT>missing[e.namespace].append(e.name)<EOL><DEDENT>return dict(missing)<EOL>

Group all of the incorrect identifiers in a dict of {namespace: list of erroneous names}.

    :return: A dictionary of {namespace: list of erroneous names}


def calculate_error_by_annotation(graph: BELGraph, annotation: str) -> Mapping[str, List[str]]:

results = defaultdict(list)<EOL>for _, exc, ctx in graph.warnings:<EOL><INDENT>if not ctx or not edge_has_annotation(ctx, annotation):<EOL><INDENT>continue<EOL><DEDENT>values = ctx[ANNOTATIONS][annotation]<EOL>if isinstance(values, str):<EOL><INDENT>results[values].append(exc.__class__.__name__)<EOL><DEDENT>elif isinstance(values, Iterable):<EOL><INDENT>for value in values:<EOL><INDENT>results[value].append(exc.__class__.__name__)<EOL><DEDENT><DEDENT><DEDENT>return dict(results)<EOL>

Group the graph by a given annotation and builds lists of errors for each.

    :return: A dictionary of {annotation value: list of errors}


def group_errors(graph: BELGraph) -> Mapping[str, List[int]]:

warning_summary = defaultdict(list)<EOL>for _, exc, _ in graph.warnings:<EOL><INDENT>warning_summary[str(exc)].append(exc.line_number)<EOL><DEDENT>return dict(warning_summary)<EOL>

Group the errors together for analysis of the most frequent error.

    :return: A dictionary of {error string: list of line numbers}


def get_most_common_errors(graph: BELGraph, n: Optional[int] = <NUM_LIT:20>):

return count_dict_values(group_errors(graph)).most_common(n)<EOL>

Get the (n) most common errors in a graph.


def get_names_including_errors_by_namespace(graph: BELGraph, namespace: str) -> Set[str]:

return get_names_by_namespace(graph, namespace) | get_incorrect_names_by_namespace(graph, namespace)<EOL>

Takes the names from the graph in a given namespace (:func:`pybel.struct.summary.get_names_by_namespace`) and
    the erroneous names from the same namespace (:func:`get_incorrect_names_by_namespace`) and returns them together
    as a unioned set

    :return: The set of all correct and incorrect names from the given namespace in the graph


def get_names_including_errors(graph: BELGraph) -> Mapping[str, Set[str]]:

return {<EOL>namespace: get_names_including_errors_by_namespace(graph, namespace)<EOL>for namespace in get_namespaces(graph)<EOL>}<EOL>

Takes the names from the graph in a given namespace and the erroneous names from the same namespace and returns
    them together as a unioned set

    :return: The dict of the sets of all correct and incorrect names from the given namespace in the graph


def get_causal_out_edges(<EOL>graph: BELGraph,<EOL>nbunch: Union[BaseEntity, Iterable[BaseEntity]],<EOL>) -> Set[Tuple[BaseEntity, BaseEntity]]:

return {<EOL>(u, v)<EOL>for u, v, k, d in graph.out_edges(nbunch, keys=True, data=True)<EOL>if is_causal_relation(graph, u, v, k, d)<EOL>}<EOL>

Get the out-edges to the given node that are causal.

    :return: A set of (source, target) pairs where the source is the given node


def get_causal_in_edges(<EOL>graph: BELGraph,<EOL>nbunch: Union[BaseEntity, Iterable[BaseEntity]],<EOL>) -> Set[Tuple[BaseEntity, BaseEntity]]:

return {<EOL>(u, v)<EOL>for u, v, k, d in graph.in_edges(nbunch, keys=True, data=True)<EOL>if is_causal_relation(graph, u, v, k, d)<EOL>}<EOL>

Get the in-edges to the given node that are causal.

    :return: A set of (source, target) pairs where the target is the given node


def get_causal_source_nodes(graph: BELGraph, func: str) -> Set[BaseEntity]:

return {<EOL>node<EOL>for node in graph<EOL>if node.function == func and is_causal_source(graph, node)<EOL>}<EOL>

Return a set of all nodes that have an in-degree of 0.

    This likely means that it is an external perturbagen and is not known to have any causal origin from within the
    biological system. These nodes are useful to identify because they generally don't provide any mechanistic insight.


def get_causal_central_nodes(graph: BELGraph, func: str) -> Set[BaseEntity]:

return {<EOL>node<EOL>for node in graph<EOL>if node.function == func and is_causal_central(graph, node)<EOL>}<EOL>

Return a set of all nodes that have both an in-degree > 0 and out-degree > 0.

    This means that they are an integral part of a pathway, since they are both produced and consumed.


def get_causal_sink_nodes(graph: BELGraph, func) -> Set[BaseEntity]:

return {<EOL>node<EOL>for node in graph<EOL>if node.function == func and is_causal_sink(graph, node)<EOL>}<EOL>

Returns a set of all ABUNDANCE nodes that have an causal out-degree of 0.

    This likely means that the knowledge assembly is incomplete, or there is a curation error.


def get_degradations(graph: BELGraph) -> Set[BaseEntity]:

return get_nodes(graph, is_degraded)<EOL>


def get_activities(graph: BELGraph) -> Set[BaseEntity]:

return get_nodes(graph, has_activity)<EOL>

Get all nodes that have molecular activities.


def get_translocated(graph: BELGraph) -> Set[BaseEntity]:

return get_nodes(graph, is_translocated)<EOL>


def count_top_centrality(graph: BELGraph, number: Optional[int] = <NUM_LIT:30>) -> Mapping[BaseEntity, int]:

dd = nx.betweenness_centrality(graph)<EOL>dc = Counter(dd)<EOL>return dict(dc.most_common(number))<EOL>


def get_modifications_count(graph: BELGraph) -> Mapping[str, int]:

return remove_falsy_values({<EOL>'<STR_LIT>': len(get_translocated(graph)),<EOL>'<STR_LIT>': len(get_degradations(graph)),<EOL>'<STR_LIT>': len(get_activities(graph)),<EOL>})<EOL>


def remove_falsy_values(counter: Mapping[Any, int]) -> Mapping[Any, int]:

return {<EOL>label: count<EOL>for label, count in counter.items()<EOL>if count<EOL>}<EOL>


def _generate_citation_dict(graph: BELGraph) -> Mapping[str, Mapping[Tuple[BaseEntity, BaseEntity], str]]:

results = defaultdict(lambda: defaultdict(set))<EOL>for u, v, data in graph.edges(data=True):<EOL><INDENT>if CITATION not in data:<EOL><INDENT>continue<EOL><DEDENT>results[data[CITATION][CITATION_TYPE]][u, v].add(data[CITATION][CITATION_REFERENCE].strip())<EOL><DEDENT>return dict(results)<EOL>

Prepare a citation data dictionary from a graph.

    :return: A dictionary of dictionaries {citation type: {(source, target): citation reference}


def get_pmid_by_keyword(keyword: str,<EOL>graph: Optional[BELGraph] = None,<EOL>pubmed_identifiers: Optional[Set[str]] = None,<EOL>) -> Set[str]:

if pubmed_identifiers is not None:<EOL><INDENT>return {<EOL>pubmed_identifier<EOL>for pubmed_identifier in pubmed_identifiers<EOL>if pubmed_identifier.startswith(keyword)<EOL>}<EOL><DEDENT>if graph is None:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>return {<EOL>pubmed_identifier<EOL>for pubmed_identifier in iterate_pubmed_identifiers(graph)<EOL>if pubmed_identifier.startswith(keyword)<EOL>}<EOL>

Get the set of PubMed identifiers beginning with the given keyword string.

    :param keyword: The beginning of a PubMed identifier
    :param graph: A BEL graph
    :param pubmed_identifiers: A set of pre-cached PubMed identifiers
    :return: A set of PubMed identifiers starting with the given string


def count_pmids(graph: BELGraph) -> Counter:

return Counter(iterate_pubmed_identifiers(graph))<EOL>

Count the frequency of PubMed documents in a graph.

    :return: A Counter from {(pmid, name): frequency}


def count_citations(graph: BELGraph, **annotations) -> Counter:

citations = defaultdict(set)<EOL>annotation_dict_filter = build_edge_data_filter(annotations)<EOL>for u, v, _, d in filter_edges(graph, annotation_dict_filter):<EOL><INDENT>if CITATION not in d:<EOL><INDENT>continue<EOL><DEDENT>citations[u, v].add((d[CITATION][CITATION_TYPE], d[CITATION][CITATION_REFERENCE].strip()))<EOL><DEDENT>return Counter(itt.chain.from_iterable(citations.values()))<EOL>

Counts the citations in a graph based on a given filter

    :param graph: A BEL graph
    :param dict annotations: The annotation filters to use
    :return: A counter from {(citation type, citation reference): frequency}


def count_citations_by_annotation(graph: BELGraph, annotation: str) -> Mapping[str, typing.Counter[str]]:

citations = defaultdict(lambda: defaultdict(set))<EOL>for u, v, data in graph.edges(data=True):<EOL><INDENT>if not edge_has_annotation(data, annotation) or CITATION not in data:<EOL><INDENT>continue<EOL><DEDENT>k = data[ANNOTATIONS][annotation]<EOL>citations[k][u, v].add((data[CITATION][CITATION_TYPE], data[CITATION][CITATION_REFERENCE].strip()))<EOL><DEDENT>return {k: Counter(itt.chain.from_iterable(v.values())) for k, v in citations.items()}<EOL>

Group the citation counters by subgraphs induced by the annotation.

    :param graph: A BEL graph
    :param annotation: The annotation to use to group the graph
    :return: A dictionary of Counters {subgraph name: Counter from {citation: frequency}}


def count_authors(graph: BELGraph) -> typing.Counter[str]:

return Counter(graph._iterate_authors())<EOL>

Count the number of edges in which each author appears.


def count_author_publications(graph: BELGraph) -> typing.Counter[str]:

authors = group_as_dict(_iter_author_publiations(graph))<EOL>return Counter(count_dict_values(count_defaultdict(authors)))<EOL>

Count the number of publications of each author to the given graph.


def get_authors(graph: BELGraph) -> Set[str]:

return set(graph._iterate_authors())<EOL>

Get the set of all authors in the given graph.


def get_authors_by_keyword(keyword: str, graph=None, authors=None) -> Set[str]:

keyword_lower = keyword.lower()<EOL>if authors is not None:<EOL><INDENT>return {<EOL>author<EOL>for author in authors<EOL>if keyword_lower in author.lower()<EOL>}<EOL><DEDENT>if graph is None:<EOL><INDENT>raise ValueError('<STR_LIT>')<EOL><DEDENT>return {<EOL>author<EOL>for author in get_authors(graph)<EOL>if keyword_lower in author.lower()<EOL>}<EOL>

Get authors for whom the search term is a substring.

    :param pybel.BELGraph graph: A BEL graph
    :param keyword: The keyword to search the author strings for
    :param set[str] authors: An optional set of pre-cached authors calculated from the graph
    :return: A set of authors with the keyword as a substring


def count_authors_by_annotation(graph: BELGraph, annotation: str = '<STR_LIT>') -> Mapping[str, typing.Counter[str]]:

authors = group_as_dict(_iter_authors_by_annotation(graph, annotation=annotation))<EOL>return count_defaultdict(authors)<EOL>

Group the author counters by sub-graphs induced by the annotation.

    :param graph: A BEL graph
    :param annotation: The annotation to use to group the graph
    :return: A dictionary of Counters {subgraph name: Counter from {author: frequency}}


def get_evidences_by_pmid(graph: BELGraph, pmids: Union[str, Iterable[str]]):

result = defaultdict(set)<EOL>for _, _, _, data in filter_edges(graph, build_pmid_inclusion_filter(pmids)):<EOL><INDENT>result[data[CITATION][CITATION_REFERENCE]].add(data[EVIDENCE])<EOL><DEDENT>return dict(result)<EOL>

Get a dictionary from the given PubMed identifiers to the sets of all evidence strings associated with each
    in the graph.

    :param graph: A BEL graph
    :param pmids: An iterable of PubMed identifiers, as strings. Is consumed and converted to a set.
    :return: A dictionary of {pmid: set of all evidence strings}
    :rtype: dict


def count_citation_years(graph: BELGraph) -> typing.Counter[int]:

result = defaultdict(set)<EOL>for _, _, data in graph.edges(data=True):<EOL><INDENT>if CITATION not in data or CITATION_DATE not in data[CITATION]:<EOL><INDENT>continue<EOL><DEDENT>try:<EOL><INDENT>dt = _ensure_datetime(data[CITATION][CITATION_DATE])<EOL>result[dt.year].add((data[CITATION][CITATION_TYPE], data[CITATION][CITATION_REFERENCE]))<EOL><DEDENT>except Exception:<EOL><INDENT>continue<EOL><DEDENT><DEDENT>return count_dict_values(result)<EOL>

Count the number of citations from each year.


def get_citation_years(graph: BELGraph) -> List[Tuple[int, int]]:

return create_timeline(count_citation_years(graph))<EOL>

Create a citation timeline counter from the graph.


def create_timeline(year_counter: typing.Counter[int]) -> List[Tuple[int, int]]:

if not year_counter:<EOL><INDENT>return []<EOL><DEDENT>from_year = min(year_counter) - <NUM_LIT:1><EOL>until_year = datetime.now().year + <NUM_LIT:1><EOL>return [<EOL>(year, year_counter.get(year, <NUM_LIT:0>))<EOL>for year in range(from_year, until_year)<EOL>]<EOL>

Complete the Counter timeline.

    :param Counter year_counter: counter dict for each year
    :return: complete timeline


def count_confidences(graph: BELGraph) -> typing.Counter[str]:

return Counter(<EOL>(<EOL>'<STR_LIT:None>'<EOL>if ANNOTATIONS not in data or '<STR_LIT>' not in data[ANNOTATIONS] else<EOL>list(data[ANNOTATIONS]['<STR_LIT>'])[<NUM_LIT:0>]<EOL>)<EOL>for _, _, data in graph.edges(data=True)<EOL>if CITATION in data  <EOL>)<EOL>


def pairwise(iterable: Iterable[X]) -> Iterable[Tuple[X, X]]:

a, b = itt.tee(iterable)<EOL>next(b, None)<EOL>return zip(a, b)<EOL>

Iterate over pairs in list s -> (s0,s1), (s1,s2), (s2, s3), ...


def count_defaultdict(dict_of_lists: Mapping[X, List[Y]]) -> Mapping[X, typing.Counter[Y]]:

return {<EOL>k: Counter(v)<EOL>for k, v in dict_of_lists.items()<EOL>}<EOL>

Count the number of elements in each value of the dictionary.


def count_dict_values(dict_of_counters: Mapping[X, Sized]) -> typing.Counter[X]:

return Counter({<EOL>k: len(v)<EOL>for k, v in dict_of_counters.items()<EOL>})<EOL>

Count the number of elements in each value (can be list, Counter, etc).

    :param dict_of_counters: A dictionary of things whose lengths can be measured (lists, Counters, dicts)
    :return: A Counter with the same keys as the input but the count of the length of the values list/tuple/set/Counter


def set_percentage(x: Iterable[X], y: Iterable[X]) -> float:

a, b = set(x), set(y)<EOL>if not a:<EOL><INDENT>return <NUM_LIT:0.0><EOL><DEDENT>return len(a & b) / len(a)<EOL>

What percentage of x is contained within y?

    :param set x: A set
    :param set y: Another set
    :return: The percentage of x contained within y


def tanimoto_set_similarity(x: Iterable[X], y: Iterable[X]) -> float:

a, b = set(x), set(y)<EOL>union = a | b<EOL>if not union:<EOL><INDENT>return <NUM_LIT:0.0><EOL><DEDENT>return len(a & b) / len(union)<EOL>


def min_tanimoto_set_similarity(x: Iterable[X], y: Iterable[X]) -> float:

a, b = set(x), set(y)<EOL>if not a or not b:<EOL><INDENT>return <NUM_LIT:0.0><EOL><DEDENT>return len(a & b) / min(len(a), len(b))<EOL>

Calculate the tanimoto set similarity using the minimum size.

    :param set x: A set
    :param set y: Another set
    :return: The similarity between


def calculate_single_tanimoto_set_distances(target: Iterable[X], dict_of_sets: Mapping[Y, Set[X]]) -> Mapping[Y, float]:

target_set = set(target)<EOL>return {<EOL>k: tanimoto_set_similarity(target_set, s)<EOL>for k, s in dict_of_sets.items()<EOL>}<EOL>

Return a dictionary of distances keyed by the keys in the given dict.

    Distances are calculated based on pairwise tanimoto similarity of the sets contained

    :param set target: A set
    :param dict_of_sets: A dict of {x: set of y}
    :type dict_of_sets: dict
    :return: A similarity dicationary based on the set overlap (tanimoto) score between the target set and the sets in
            dos
    :rtype: dict


def calculate_tanimoto_set_distances(dict_of_sets: Mapping[X, Set]) -> Mapping[X, Mapping[X, float]]:

result: Dict[X, Dict[X, float]] = defaultdict(dict)<EOL>for x, y in itt.combinations(dict_of_sets, <NUM_LIT:2>):<EOL><INDENT>result[x][y] = result[y][x] = tanimoto_set_similarity(dict_of_sets[x], dict_of_sets[y])<EOL><DEDENT>for x in dict_of_sets:<EOL><INDENT>result[x][x] = <NUM_LIT:1.0><EOL><DEDENT>return dict(result)<EOL>

Return a distance matrix keyed by the keys in the given dict.

    Distances are calculated based on pairwise tanimoto similarity of the sets contained.

    :param dict_of_sets: A dict of {x: set of y}
    :return: A similarity matrix based on the set overlap (tanimoto) score between each x as a dict of dicts


def calculate_global_tanimoto_set_distances(dict_of_sets: Mapping[X, Set]) -> Mapping[X, Mapping[X, float]]:

universe = set(itt.chain.from_iterable(dict_of_sets.values()))<EOL>universe_size = len(universe)<EOL>result: Dict[X, Dict[X, float]] = defaultdict(dict)<EOL>for x, y in itt.combinations(dict_of_sets, <NUM_LIT:2>):<EOL><INDENT>result[x][y] = result[y][x] = <NUM_LIT:1.0> - len(dict_of_sets[x] | dict_of_sets[y]) / universe_size<EOL><DEDENT>for x in dict_of_sets:<EOL><INDENT>result[x][x] = <NUM_LIT:1.0> - len(x) / universe_size<EOL><DEDENT>return dict(result)<EOL>

r"""Calculate an alternative distance matrix based on the following equation.

    .. math:: distance(A, B)=1- \|A \cup B\| / \| \cup_{s \in S} s\|

    :param dict_of_sets: A dict of {x: set of y}
    :return: A similarity matrix based on the alternative tanimoto distance as a dict of dicts


labels = sorted(d, key=d.get)<EOL>index = range(len(labels))<EOL>plt.yticks(index, labels)<EOL>plt.barh(index, [d[v] for v in labels])<EOL>if title is not None:<EOL><INDENT>plt.title(title)<EOL><DEDENT>

A convenience function for plotting a horizontal bar plot from a Counter


def barv(d, plt, title=None, rotation='<STR_LIT>'):

labels = sorted(d, key=d.get, reverse=True)<EOL>index = range(len(labels))<EOL>plt.xticks(index, labels, rotation=rotation)<EOL>plt.bar(index, [d[v] for v in labels])<EOL>if title is not None:<EOL><INDENT>plt.title(title)<EOL><DEDENT>

A convenience function for plotting a vertical bar plot from a Counter


def safe_add_edge(graph, u, v, key, attr_dict, **attr):

if key < <NUM_LIT:0>:<EOL><INDENT>graph.add_edge(u, v, key=key, attr_dict=attr_dict, **attr)<EOL><DEDENT>else:<EOL><INDENT>graph.add_edge(u, v, attr_dict=attr_dict, **attr)<EOL><DEDENT>

Adds an edge while preserving negative keys, and paying no respect to positive ones

    :param pybel.BELGraph graph: A BEL Graph
    :param tuple u: The source BEL node
    :param tuple v: The target BEL node
    :param int key: The edge key. If less than zero, corresponds to an unqualified edge, else is disregarded
    :param dict attr_dict: The edge data dictionary
    :param dict attr: Edge data to assign via keyword arguments


def prepare_c3(data: Union[List[Tuple[str, int]], Mapping[str, int]],<EOL>y_axis_label: str = '<STR_LIT:y>',<EOL>x_axis_label: str = '<STR_LIT:x>',<EOL>) -> str:

if not isinstance(data, list):<EOL><INDENT>data = sorted(data.items(), key=itemgetter(<NUM_LIT:1>), reverse=True)<EOL><DEDENT>try:<EOL><INDENT>labels, values = zip(*data)<EOL><DEDENT>except ValueError:<EOL><INDENT>log.info(f'<STR_LIT>')<EOL>labels, values = [], []<EOL><DEDENT>return json.dumps([<EOL>[x_axis_label] + list(labels),<EOL>[y_axis_label] + list(values),<EOL>])<EOL>

Prepares C3 JSON for making a bar chart from a Counter

    :param data: A dictionary of {str: int} to display as bar chart
    :param y_axis_label: The Y axis label
    :param x_axis_label: X axis internal label. Should be left as default 'x')
    :return: A JSON dictionary for making a C3 bar chart


def prepare_c3_time_series(data: List[Tuple[int, int]], y_axis_label: str = '<STR_LIT:y>', x_axis_label: str = '<STR_LIT:x>') -> str:

years, counter = zip(*data)<EOL>years = [<EOL>datetime.date(year, <NUM_LIT:1>, <NUM_LIT:1>).isoformat()<EOL>for year in years<EOL>]<EOL>return json.dumps([<EOL>[x_axis_label] + list(years),<EOL>[y_axis_label] + list(counter)<EOL>])<EOL>

Prepare C3 JSON string dump for a time series.

    :param data: A list of tuples [(year, count)]
    :param y_axis_label: The Y axis label
    :param x_axis_label: X axis internal label. Should be left as default 'x')


def calculate_betweenness_centality(graph: BELGraph, number_samples: int = CENTRALITY_SAMPLES) -> Counter:

try:<EOL><INDENT>res = nx.betweenness_centrality(graph, k=number_samples)<EOL><DEDENT>except Exception:<EOL><INDENT>res = nx.betweenness_centrality(graph)<EOL><DEDENT>return Counter(res)<EOL>

Calculate the betweenness centrality over nodes in the graph.

    Tries to do it with a certain number of samples, but then tries a complete approach if it fails.


def get_circulations(elements: T) -> Iterable[T]:

for i in range(len(elements)):<EOL><INDENT>yield elements[i:] + elements[:i]<EOL><DEDENT>

Iterate over all possible circulations of an ordered collection (tuple or list).

    Example:

    >>> list(get_circulations([1, 2, 3]))
    [[1, 2, 3], [2, 3, 1], [3, 1, 2]]


def canonical_circulation(elements: T, key: Optional[Callable[[T], bool]] = None) -> T:

return min(get_circulations(elements), key=key)<EOL>

Get get a canonical representation of the ordered collection by finding its minimum circulation with the
    given sort key


return VERSION<EOL>


def self_edge_filter(_: BELGraph, source: BaseEntity, target: BaseEntity, __: str) -> bool:

return source == target<EOL>

Check if the source and target nodes are the same.


def has_protein_modification_increases_activity(graph: BELGraph,<EOL>source: BaseEntity,<EOL>target: BaseEntity,<EOL>key: str,<EOL>) -> bool:

edge_data = graph[source][target][key]<EOL>return has_protein_modification(graph, source) and part_has_modifier(edge_data, OBJECT, ACTIVITY)<EOL>

Check if pmod of source causes activity of target.


@edge_predicate<EOL>def has_degradation_increases_activity(data: Dict) -> bool:

return part_has_modifier(data, SUBJECT, DEGRADATION) and part_has_modifier(data, OBJECT, ACTIVITY)<EOL>

Check if the degradation of source causes activity of target.


@edge_predicate<EOL>def has_translocation_increases_activity(data: Dict) -> bool:

return part_has_modifier(data, SUBJECT, TRANSLOCATION) and part_has_modifier(data, OBJECT, ACTIVITY)<EOL>

Check if the translocation of source causes activity of target.


def complex_has_member(graph: BELGraph, complex_node: ComplexAbundance, member_node: BaseEntity) -> bool:

return any(  <EOL>v == member_node<EOL>for _, v, data in graph.out_edges(complex_node, data=True)<EOL>if data[RELATION] == HAS_COMPONENT<EOL>)<EOL>

Does the given complex contain the member?


def complex_increases_activity(graph: BELGraph, u: BaseEntity, v: BaseEntity, key: str) -> bool:

return (<EOL>isinstance(u, (ComplexAbundance, NamedComplexAbundance)) and<EOL>complex_has_member(graph, u, v) and<EOL>part_has_modifier(graph[u][v][key], OBJECT, ACTIVITY)<EOL>)<EOL>

Return if the formation of a complex with u increases the activity of v.


for u, v, key, data in graph.edges(keys=True, data=True):<EOL><INDENT>if u != v:<EOL><INDENT>continue<EOL><DEDENT>bel = graph.edge_to_bel(u, v, data)<EOL>line = data.get(LINE)<EOL>if line is None:<EOL><INDENT>continue  <EOL><DEDENT>elif has_protein_modification_increases_activity(graph, u, v, key):<EOL><INDENT>print(line, '<STR_LIT>', bel)<EOL>find_related(graph, v, data)<EOL><DEDENT>elif has_degradation_increases_activity(data):<EOL><INDENT>print(line, '<STR_LIT>', bel)<EOL>find_related(graph, v, data)<EOL><DEDENT>elif has_translocation_increases_activity(data):<EOL><INDENT>print(line, '<STR_LIT>', bel)<EOL>find_related(graph, v, data)<EOL><DEDENT>elif complex_increases_activity(graph, u, v, key):<EOL><INDENT>print(line, '<STR_LIT>', bel)<EOL>find_related(graph, v, data)<EOL><DEDENT>elif has_same_subject_object(graph, u, v, key):<EOL><INDENT>print(line, '<STR_LIT>', bel)<EOL><DEDENT>else:<EOL><INDENT>print(line, '<STR_LIT>', bel)<EOL><DEDENT><DEDENT>

Find edges that are A - A, meaning that some conditions in the edge best describe the interaction.


def compare(graph: BELGraph, annotation: str = '<STR_LIT>') -> Mapping[str, Mapping[str, float]]:

canonical_mechanisms = get_subgraphs_by_annotation(graph, annotation)<EOL>canonical_nodes = _transform_graph_dict_to_node_dict(canonical_mechanisms)<EOL>candidate_mechanisms = generate_bioprocess_mechanisms(graph)<EOL>candidate_nodes = _transform_graph_dict_to_node_dict(candidate_mechanisms)<EOL>results: Dict[str, Dict[str, float]] = defaultdict(dict)<EOL>it = itt.product(canonical_nodes.items(), candidate_nodes.items())<EOL>for (canonical_name, canonical_graph), (candidate_bp, candidate_graph) in it:<EOL><INDENT>tanimoto = tanimoto_set_similarity(candidate_nodes, canonical_nodes)<EOL>results[canonical_name][candidate_bp] = tanimoto<EOL><DEDENT>return dict(results)<EOL>

Compare generated mechanisms to actual ones.

    1. Generates candidate mechanisms for each biological process
    2. Gets sub-graphs for all NeuroMMSig signatures
    3. Make tanimoto similarity comparison for all sets

    :return: A dictionary table comparing the canonical subgraphs to generated ones


def get_neurommsig_scores(graph: BELGraph,<EOL>genes: List[Gene],<EOL>annotation: str = '<STR_LIT>',<EOL>ora_weight: Optional[float] = None,<EOL>hub_weight: Optional[float] = None,<EOL>top_percent: Optional[float] = None,<EOL>topology_weight: Optional[float] = None,<EOL>preprocess: bool = False<EOL>) -> Optional[Mapping[str, float]]:

if preprocess:<EOL><INDENT>graph = neurommsig_graph_preprocessor.run(graph)<EOL><DEDENT>if not any(gene in graph for gene in genes):<EOL><INDENT>logger.debug('<STR_LIT>')<EOL>return<EOL><DEDENT>subgraphs = get_subgraphs_by_annotation(graph, annotation=annotation)<EOL>return get_neurommsig_scores_prestratified(<EOL>subgraphs=subgraphs,<EOL>genes=genes,<EOL>ora_weight=ora_weight,<EOL>hub_weight=hub_weight,<EOL>top_percent=top_percent,<EOL>topology_weight=topology_weight,<EOL>)<EOL>

Preprocess the graph, stratify by the given annotation, then run the NeuroMMSig algorithm on each.

    :param graph: A BEL graph
    :param genes: A list of gene nodes
    :param annotation: The annotation to use to stratify the graph to subgraphs
    :param ora_weight: The relative weight of the over-enrichment analysis score from
     :py:func:`neurommsig_gene_ora`. Defaults to 1.0.
    :param hub_weight: The relative weight of the hub analysis score from :py:func:`neurommsig_hubs`.
     Defaults to 1.0.
    :param top_percent: The percentage of top genes to use as hubs. Defaults to 5% (0.05).
    :param topology_weight: The relative weight of the topolgical analysis core from
     :py:func:`neurommsig_topology`. Defaults to 1.0.
    :param preprocess: If true, preprocess the graph.
    :return: A dictionary from {annotation value: NeuroMMSig composite score}

    Pre-processing steps:

    1. Infer the central dogma with :func:``
    2. Collapse all proteins, RNAs and miRNAs to genes with :func:``
    3. Collapse variants to genes with :func:``


def get_neurommsig_scores_prestratified(subgraphs: Mapping[str, BELGraph],<EOL>genes: List[Gene],<EOL>ora_weight: Optional[float] = None,<EOL>hub_weight: Optional[float] = None,<EOL>top_percent: Optional[float] = None,<EOL>topology_weight: Optional[float] = None,<EOL>) -> Optional[Mapping[str, float]]:

return {<EOL>name: get_neurommsig_score(<EOL>graph=subgraph,<EOL>genes=genes,<EOL>ora_weight=ora_weight,<EOL>hub_weight=hub_weight,<EOL>top_percent=top_percent,<EOL>topology_weight=topology_weight,<EOL>)<EOL>for name, subgraph in subgraphs.items()<EOL>}<EOL>

Takes a graph stratification and runs neurommsig on each

    :param subgraphs: A pre-stratified set of graphs
    :param genes: A list of gene nodes
    :param ora_weight: The relative weight of the over-enrichment analysis score from
     :py:func:`neurommsig_gene_ora`. Defaults to 1.0.
    :param hub_weight: The relative weight of the hub analysis score from :py:func:`neurommsig_hubs`.
     Defaults to 1.0.
    :param top_percent: The percentage of top genes to use as hubs. Defaults to 5% (0.05).
    :param topology_weight: The relative weight of the topolgical analysis core from
     :py:func:`neurommsig_topology`. Defaults to 1.0.
    :return: A dictionary from {annotation value: NeuroMMSig composite score}

    Pre-processing steps:

    1. Infer the central dogma with :func:``
    2. Collapse all proteins, RNAs and miRNAs to genes with :func:``
    3. Collapse variants to genes with :func:``


def get_neurommsig_score(graph: BELGraph,<EOL>genes: List[Gene],<EOL>ora_weight: Optional[float] = None,<EOL>hub_weight: Optional[float] = None,<EOL>top_percent: Optional[float] = None,<EOL>topology_weight: Optional[float] = None) -> float:

ora_weight = ora_weight or <NUM_LIT:1.0><EOL>hub_weight = hub_weight or <NUM_LIT:1.0><EOL>topology_weight = topology_weight or <NUM_LIT:1.0><EOL>total_weight = ora_weight + hub_weight + topology_weight<EOL>genes = list(genes)<EOL>ora_score = neurommsig_gene_ora(graph, genes)<EOL>hub_score = neurommsig_hubs(graph, genes, top_percent=top_percent)<EOL>topology_score = neurommsig_topology(graph, genes)<EOL>weighted_sum = (<EOL>ora_weight * ora_score +<EOL>hub_weight * hub_score +<EOL>topology_weight * topology_score<EOL>)<EOL>return weighted_sum / total_weight<EOL>

Calculate the composite NeuroMMSig Score for a given list of genes.

    :param graph: A BEL graph
    :param genes: A list of gene nodes
    :param ora_weight: The relative weight of the over-enrichment analysis score from
     :py:func:`neurommsig_gene_ora`. Defaults to 1.0.
    :param hub_weight: The relative weight of the hub analysis score from :py:func:`neurommsig_hubs`.
     Defaults to 1.0.
    :param top_percent: The percentage of top genes to use as hubs. Defaults to 5% (0.05).
    :param topology_weight: The relative weight of the topolgical analysis core from
     :py:func:`neurommsig_topology`. Defaults to 1.0.
    :return: The NeuroMMSig composite score


def neurommsig_gene_ora(graph: BELGraph, genes: List[Gene]) -> float:

graph_genes = set(get_nodes_by_function(graph, GENE))<EOL>return len(graph_genes.intersection(genes)) / len(graph_genes)<EOL>

Calculate the percentage of target genes mappable to the graph.

    Assume: graph central dogma inferred, collapsed to genes, collapsed variants


def neurommsig_hubs(graph: BELGraph, genes: List[Gene], top_percent: Optional[float] = None) -> float:

top_percent = top_percent or <NUM_LIT><EOL>if graph.number_of_nodes() < <NUM_LIT:20>:<EOL><INDENT>logger.debug('<STR_LIT>')<EOL>return <NUM_LIT:0.0><EOL><DEDENT>graph_genes = set(get_nodes_by_function(graph, GENE))<EOL>bc = Counter({<EOL>node: betweenness_centrality<EOL>for node, betweenness_centrality in calculate_betweenness_centality(graph).items()<EOL>if node in graph_genes<EOL>})<EOL>number_central_nodes = int(len(graph_genes) * top_percent)<EOL>if number_central_nodes < <NUM_LIT:1>:<EOL><INDENT>number_central_nodes = <NUM_LIT:1><EOL><DEDENT>number_mappable_central_nodes = sum(<EOL>node in genes<EOL>for node in bc.most_common(number_central_nodes)<EOL>)<EOL>return number_mappable_central_nodes / number_central_nodes<EOL>

Calculate the percentage of target genes mappable to the graph.

    Assume: graph central dogma inferred, collapsed to genes, collapsed variants, graph has more than 20 nodes

    :param graph: A BEL graph
    :param genes: A list of nodes
    :param top_percent: The percentage of top genes to use as hubs. Defaults to 5% (0.05).


def neurommsig_topology(graph: BELGraph, nodes: List[BaseEntity]) -> float:

nodes = list(nodes)<EOL>number_nodes = len(nodes)<EOL>if number_nodes <= <NUM_LIT:1>:<EOL><INDENT>return <NUM_LIT:0.0><EOL><DEDENT>unnormalized_sum = sum(<EOL>u in graph[v]<EOL>for u, v in itt.product(nodes, repeat=<NUM_LIT:2>)<EOL>if v in graph and u != v<EOL>)<EOL>return unnormalized_sum / (number_nodes * (number_nodes - <NUM_LIT:1.0>))<EOL>

Calculate the node neighbor score for a given list of nodes.

    -  Doesn't consider self loops

    .. math::

         \frac{\sum_i^n N_G[i]}{n*(n-1)}


logging.basicConfig(level=logging.INFO)<EOL>log.setLevel(logging.INFO)<EOL>bms_base = get_bms_base()<EOL>neurommsig_base = get_neurommsig_base()<EOL>neurommsig_excel_dir = os.path.join(neurommsig_base, '<STR_LIT>', '<STR_LIT>', '<STR_LIT>')<EOL>nift_values = get_nift_values()<EOL>log.info('<STR_LIT>')<EOL>ad_path = os.path.join(neurommsig_excel_dir, '<STR_LIT>', '<STR_LIT>')<EOL>ad_df = preprocess(ad_path)<EOL>with open(os.path.join(bms_base, '<STR_LIT>', '<STR_LIT>', '<STR_LIT>'), '<STR_LIT:w>') as ad_file:<EOL><INDENT>write_neurommsig_bel(ad_file, ad_df, mesh_alzheimer, nift_values)<EOL><DEDENT>log.info('<STR_LIT>')<EOL>pd_path = os.path.join(neurommsig_excel_dir, '<STR_LIT>', '<STR_LIT>')<EOL>pd_df = preprocess(pd_path)<EOL>with open(os.path.join(bms_base, '<STR_LIT>', '<STR_LIT>', '<STR_LIT>'), '<STR_LIT:w>') as pd_file:<EOL><INDENT>write_neurommsig_bel(pd_file, pd_df, mesh_parkinson, nift_values)<EOL><DEDENT>

Convert the Alzheimer's and Parkinson's disease NeuroMMSig excel sheets to BEL.


if not os.path.exists(path):<EOL><INDENT>raise ValueError("<STR_LIT>" % path)<EOL><DEDENT>df = pd.read_excel(path, sheetname=<NUM_LIT:0>, header=<NUM_LIT:0>)<EOL>df.iloc[:, <NUM_LIT:0>] = pd.Series(df.iloc[:, <NUM_LIT:0>]).fillna(method='<STR_LIT>')<EOL>df = df[df.ix[:, <NUM_LIT:1>].notnull()]<EOL>df = df.reset_index(drop=True)<EOL>df.ix[:, <NUM_LIT:2>].fillna(<NUM_LIT:0>, inplace=True)<EOL>if (df.ix[:, <NUM_LIT:1>].isnull().sum()) != <NUM_LIT:0>:<EOL><INDENT>raise ValueError("<STR_LIT>")<EOL><DEDENT>return df<EOL>

Preprocess the excel sheet

    :param filepath: filepath of the excel data
    :return: df: pandas dataframe with excel data
    :rtype: pandas.DataFrame


def munge_cell(cell, line=None, validators=None):

if pd.isnull(cell) or isinstance(cell, int):<EOL><INDENT>return None<EOL><DEDENT>c = '<STR_LIT:U+0020>'.join(cell.split())<EOL>if validators is not None and all(re.match(validator, c) is None for validator in validators):<EOL><INDENT>if line:<EOL><INDENT>log.info("<STR_LIT>", line, c)<EOL><DEDENT>return None<EOL><DEDENT>return [x.strip() for x in str(c).strip().split('<STR_LIT:U+002C>')]<EOL>

:param cell:
:param line:
:param validators:
:return:


def preprocessing_br_projection_excel(path: str) -> pd.DataFrame:

if not os.path.exists(path):<EOL><INDENT>raise ValueError("<STR_LIT>" % path)<EOL><DEDENT>return pd.read_excel(path, sheetname=<NUM_LIT:0>, header=<NUM_LIT:0>)<EOL>

Preprocess the excel file.

    Parameters
    ----------
    path : Filepath of the excel sheet


def get_nift_values() -> Mapping[str, str]:

r = get_bel_resource(NIFT)<EOL>return {<EOL>name.lower(): name<EOL>for name in r['<STR_LIT>']<EOL>}<EOL>

Extract the list of NIFT names from the BEL resource and builds a dictionary mapping from the lowercased version
    to the uppercase version.


def write_neurommsig_bel(file,<EOL>df: pd.DataFrame,<EOL>disease: str,<EOL>nift_values: Mapping[str, str],<EOL>):

write_neurommsig_biolerplate(disease, file)<EOL>missing_features = set()<EOL>fixed_caps = set()<EOL>nift_value_originals = set(nift_values.values())<EOL>graph = BELGraph(<EOL>name=f'<STR_LIT>',<EOL>description=f'<STR_LIT>',<EOL>authors='<STR_LIT>',<EOL>contact='<STR_LIT>',<EOL>version=time.strftime('<STR_LIT>'),<EOL>)<EOL>for pathway, pathway_df in df.groupby(pathway_column):<EOL><INDENT>sorted_pathway_df = pathway_df.sort_values(genes_column)<EOL>sliced_df = sorted_pathway_df[columns].itertuples()<EOL>for _, gene, pubmeds, lit_snps, gwas_snps, ld_block_snps, clinical_features, clinical_snps in sliced_df:<EOL><INDENT>gene = ensure_quotes(gene)<EOL>for snp in itt.chain(lit_snps or [], gwas_snps or [], ld_block_snps or [], clinical_snps or []):<EOL><INDENT>if not snp.strip():<EOL><INDENT>continue<EOL><DEDENT>graph.add_association(<EOL>Gene('<STR_LIT>', gene),<EOL>Gene('<STR_LIT>', snp),<EOL>evidence='<STR_LIT>',<EOL>citation='<STR_LIT>',<EOL>annotations={<EOL>'<STR_LIT>': disease,<EOL>},<EOL>)<EOL><DEDENT>for clinical_feature in clinical_features or []:<EOL><INDENT>if not clinical_feature.strip():<EOL><INDENT>continue<EOL><DEDENT>if clinical_feature.lower() not in nift_values:<EOL><INDENT>missing_features.add(clinical_feature)<EOL>continue<EOL><DEDENT>if clinical_feature not in nift_value_originals:<EOL><INDENT>fixed_caps.add((clinical_feature, nift_values[clinical_feature.lower()]))<EOL>clinical_feature = nift_values[clinical_feature.lower()]  <EOL><DEDENT>graph.add_association(<EOL>Gene('<STR_LIT>', gene),<EOL>Abundance('<STR_LIT>', clinical_feature),<EOL>evidence='<STR_LIT>',<EOL>citation='<STR_LIT>',<EOL>annotations={<EOL>'<STR_LIT>': disease,<EOL>},<EOL>)<EOL>if clinical_snps:<EOL><INDENT>for clinical_snp in clinical_snps:<EOL><INDENT>graph.add_association(<EOL>Gene('<STR_LIT>', clinical_snp),<EOL>Abundance('<STR_LIT>', clinical_feature),<EOL>evidence='<STR_LIT>',<EOL>citation='<STR_LIT>',<EOL>annotations={<EOL>'<STR_LIT>': disease,<EOL>},<EOL>)<EOL><DEDENT><DEDENT><DEDENT><DEDENT><DEDENT>if missing_features:<EOL><INDENT>log.warning('<STR_LIT>', disease)<EOL>for feature in missing_features:<EOL><INDENT>log.warning(feature)<EOL><DEDENT><DEDENT>if fixed_caps:<EOL><INDENT>log.warning('<STR_LIT>')<EOL>for broken, fixed in fixed_caps:<EOL><INDENT>log.warning('<STR_LIT>', broken, fixed)<EOL><DEDENT><DEDENT>

Writes the NeuroMMSigDB excel sheet to BEL

    :param file: a file or file-like that can be writen to
    :param df:
    :param disease:
    :param nift_values: a dictionary of lowercased to normal names in NIFT


bms_base = os.environ.get('<STR_LIT>')<EOL>if bms_base is None:<EOL><INDENT>raise RuntimeError("""<STR_LIT>""")<EOL><DEDENT>return bms_base<EOL>

Get the path to the BMS git repository from the environment or thrown an exception.

    :raises: RuntimeError


neurommsig_base = os.environ.get('<STR_LIT>')<EOL>if neurommsig_base is None:<EOL><INDENT>raise RuntimeError("""<STR_LIT>""")<EOL><DEDENT>return neurommsig_base<EOL>

Get the path to the NeuroMMSig git repository from the environment or thrown an exception.

    :raises: RuntimeError


def get_contradiction_summary(graph: BELGraph) -> Iterable[Tuple[BaseEntity, BaseEntity, str]]:

for u, v in set(graph.edges()):<EOL><INDENT>relations = {data[RELATION] for data in graph[u][v].values()}<EOL>if relation_set_has_contradictions(relations):<EOL><INDENT>yield u, v, relations<EOL><DEDENT><DEDENT>

Yield triplets of (source node, target node, set of relations) for (source node, target node) pairs
    that have multiple, contradictory relations.


def get_regulatory_pairs(graph: BELGraph) -> Set[NodePair]:

cg = get_causal_subgraph(graph)<EOL>results = set()<EOL>for u, v, d in cg.edges(data=True):<EOL><INDENT>if d[RELATION] not in CAUSAL_INCREASE_RELATIONS:<EOL><INDENT>continue<EOL><DEDENT>if cg.has_edge(v, u) and any(dd[RELATION] in CAUSAL_DECREASE_RELATIONS for dd in cg[v][u].values()):<EOL><INDENT>results.add((u, v))<EOL><DEDENT><DEDENT>return results<EOL>

Find pairs of nodes that have mutual causal edges that are regulating each other such that ``A -> B`` and
    ``B -| A``.

    :return: A set of pairs of nodes with mutual causal edges


def get_chaotic_pairs(graph: BELGraph) -> SetOfNodePairs:

cg = get_causal_subgraph(graph)<EOL>results = set()<EOL>for u, v, d in cg.edges(data=True):<EOL><INDENT>if d[RELATION] not in CAUSAL_INCREASE_RELATIONS:<EOL><INDENT>continue<EOL><DEDENT>if cg.has_edge(v, u) and any(dd[RELATION] in CAUSAL_INCREASE_RELATIONS for dd in cg[v][u].values()):<EOL><INDENT>results.add(tuple(sorted([u, v], key=str)))<EOL><DEDENT><DEDENT>return results<EOL>

Find pairs of nodes that have mutual causal edges that are increasing each other such that ``A -> B`` and
    ``B -> A``.

    :return: A set of pairs of nodes with mutual causal edges


def get_dampened_pairs(graph: BELGraph) -> SetOfNodePairs:

cg = get_causal_subgraph(graph)<EOL>results = set()<EOL>for u, v, d in cg.edges(data=True):<EOL><INDENT>if d[RELATION] not in CAUSAL_DECREASE_RELATIONS:<EOL><INDENT>continue<EOL><DEDENT>if cg.has_edge(v, u) and any(dd[RELATION] in CAUSAL_DECREASE_RELATIONS for dd in cg[v][u].values()):<EOL><INDENT>results.add(tuple(sorted([u, v], key=str)))<EOL><DEDENT><DEDENT>return results<EOL>

Find pairs of nodes that have mutual causal edges that are decreasing each other such that ``A -| B`` and
    ``B -| A``.

    :return: A set of pairs of nodes with mutual causal edges


def get_correlation_graph(graph: BELGraph) -> Graph:

result = Graph()<EOL>for u, v, d in graph.edges(data=True):<EOL><INDENT>if d[RELATION] not in CORRELATIVE_RELATIONS:<EOL><INDENT>continue<EOL><DEDENT>if not result.has_edge(u, v):<EOL><INDENT>result.add_edge(u, v, **{d[RELATION]: True})<EOL><DEDENT>elif d[RELATION] not in result[u][v]:<EOL><INDENT>log.log(<NUM_LIT:5>, '<STR_LIT>', u, v)<EOL>result[u][v][d[RELATION]] = True<EOL>result[v][u][d[RELATION]] = True<EOL><DEDENT><DEDENT>return result<EOL>

Extract an undirected graph of only correlative relationships.


def get_correlation_triangles(graph: BELGraph) -> SetOfNodeTriples:

return {<EOL>tuple(sorted([n, u, v], key=str))<EOL>for n in graph<EOL>for u, v in itt.combinations(graph[n], <NUM_LIT:2>)<EOL>if graph.has_edge(u, v)<EOL>}<EOL>

Return a set of all triangles pointed by the given node.


def get_triangles(graph: DiGraph) -> SetOfNodeTriples:

return {<EOL>tuple(sorted([a, b, c], key=str))<EOL>for a, b in graph.edges()<EOL>for c in graph.successors(b)<EOL>if graph.has_edge(c, a)<EOL>}<EOL>

Get a set of triples representing the 3-cycles from a directional graph.

    Each 3-cycle is returned once, with nodes in sorted order.


def get_separate_unstable_correlation_triples(graph: BELGraph) -> Iterable[NodeTriple]:

cg = get_correlation_graph(graph)<EOL>for a, b, c in get_correlation_triangles(cg):<EOL><INDENT>if POSITIVE_CORRELATION in cg[a][b] and POSITIVE_CORRELATION in cg[b][c] and NEGATIVE_CORRELATION incg[a][c]:<EOL><INDENT>yield b, a, c<EOL><DEDENT>if POSITIVE_CORRELATION in cg[a][b] and NEGATIVE_CORRELATION in cg[b][c] and POSITIVE_CORRELATION incg[a][c]:<EOL><INDENT>yield a, b, c<EOL><DEDENT>if NEGATIVE_CORRELATION in cg[a][b] and POSITIVE_CORRELATION in cg[b][c] and POSITIVE_CORRELATION incg[a][c]:<EOL><INDENT>yield c, a, b<EOL><DEDENT><DEDENT>

Yield all triples of nodes A, B, C such that ``A pos B``, ``A pos C``, and ``B neg C``.

    :return: An iterator over triples of unstable graphs, where the second two are negative


def get_mutually_unstable_correlation_triples(graph: BELGraph) -> Iterable[NodeTriple]:

cg = get_correlation_graph(graph)<EOL>for a, b, c in get_correlation_triangles(cg):<EOL><INDENT>if all(NEGATIVE_CORRELATION in x for x in (cg[a][b], cg[b][c], cg[a][c])):<EOL><INDENT>yield a, b, c<EOL><DEDENT><DEDENT>

Yield triples of nodes (A, B, C) such that ``A neg B``, ``B neg C``, and ``C neg A``.


def jens_transformation_alpha(graph: BELGraph) -> DiGraph:

result = DiGraph()<EOL>for u, v, d in graph.edges(data=True):<EOL><INDENT>relation = d[RELATION]<EOL>if relation == POSITIVE_CORRELATION:<EOL><INDENT>result.add_edge(u, v)<EOL>result.add_edge(v, u)<EOL><DEDENT>elif relation in CAUSAL_INCREASE_RELATIONS:<EOL><INDENT>result.add_edge(u, v)<EOL><DEDENT>elif relation in CAUSAL_DECREASE_RELATIONS:<EOL><INDENT>result.add_edge(v, u)<EOL><DEDENT><DEDENT>return result<EOL>

Apply Jens' transformation (Type 1) to the graph.

    1. Induce a sub-graph over causal + correlative edges
    2. Transform edges by the following rules:
        - increases => increases
        - decreases => backwards increases
        - positive correlation => two way increases
        - negative correlation => delete

    The resulting graph can be used to search for 3-cycles, which now symbolize unstable triplets where ``A -> B``,
    ``A -| C`` and ``B positiveCorrelation C``.


def jens_transformation_beta(graph: BELGraph) -> DiGraph:

result = DiGraph()<EOL>for u, v, d in graph.edges(data=True):<EOL><INDENT>relation = d[RELATION]<EOL>if relation == NEGATIVE_CORRELATION:<EOL><INDENT>result.add_edge(u, v)<EOL>result.add_edge(v, u)<EOL><DEDENT>elif relation in CAUSAL_INCREASE_RELATIONS:<EOL><INDENT>result.add_edge(v, u)<EOL><DEDENT>elif relation in CAUSAL_DECREASE_RELATIONS:<EOL><INDENT>result.add_edge(u, v)<EOL><DEDENT><DEDENT>return result<EOL>

Apply Jens' Transformation (Type 2) to the graph.

    1. Induce a sub-graph over causal and correlative relations
    2. Transform edges with the following rules:
        - increases => backwards decreases
        - decreases => decreases
        - positive correlation => delete
        - negative correlation => two way decreases

    The resulting graph can be used to search for 3-cycles, which now symbolize stable triples where ``A -> B``,
    ``A -| C`` and ``B negativeCorrelation C``.


def get_jens_unstable(graph: BELGraph) -> Iterable[NodeTriple]:

r = jens_transformation_alpha(graph)<EOL>return get_triangles(r)<EOL>

Yield triples of nodes (A, B, C) where ``A -> B``, ``A -| C``, and ``C positiveCorrelation A``.

    Calculated efficiently using the Jens Transformation.


def get_increase_mismatch_triplets(graph: BELGraph) -> Iterable[NodeTriple]:

return _get_mismatch_triplets_helper(graph, CAUSAL_INCREASE_RELATIONS)<EOL>

Yield triples of nodes (A, B, C) where ``A -> B``, ``A -> C``, and ``C negativeCorrelation A``.


def get_decrease_mismatch_triplets(graph: BELGraph) -> Iterable[NodeTriple]:

return _get_mismatch_triplets_helper(graph, CAUSAL_DECREASE_RELATIONS)<EOL>

Yield triples of nodes (A, B, C) where ``A -| B``, ``A -| C``, and ``C negativeCorrelation A``.


def get_chaotic_triplets(graph: BELGraph) -> Iterable[NodeTriple]:

return _get_disregulated_triplets_helper(graph, CAUSAL_INCREASE_RELATIONS)<EOL>

Yield triples of nodes (A, B, C) that mutually increase each other, such as when ``A -> B``, ``B -> C``, and
    ``C -> A``.


def get_dampened_triplets(graph: BELGraph) -> Iterable[NodeTriple]:

return _get_disregulated_triplets_helper(graph, CAUSAL_DECREASE_RELATIONS)<EOL>

Yield triples of nodes (A, B, C) that mutually decreases each other, such as when ``A -| B``,
    ``B -| C``, and ``C -| A``.


def summarize_stability(graph: BELGraph) -> Mapping[str, int]:

regulatory_pairs = get_regulatory_pairs(graph)<EOL>chaotic_pairs = get_chaotic_pairs(graph)<EOL>dampened_pairs = get_dampened_pairs(graph)<EOL>contraditory_pairs = get_contradiction_summary(graph)<EOL>separately_unstable_triples = get_separate_unstable_correlation_triples(graph)<EOL>mutually_unstable_triples = get_mutually_unstable_correlation_triples(graph)<EOL>jens_unstable_triples = get_jens_unstable(graph)<EOL>increase_mismatch_triples = get_increase_mismatch_triplets(graph)<EOL>decrease_mismatch_triples = get_decrease_mismatch_triplets(graph)<EOL>chaotic_triples = get_chaotic_triplets(graph)<EOL>dampened_triples = get_dampened_triplets(graph)<EOL>return {<EOL>'<STR_LIT>': _count_or_len(regulatory_pairs),<EOL>'<STR_LIT>': _count_or_len(chaotic_pairs),<EOL>'<STR_LIT>': _count_or_len(dampened_pairs),<EOL>'<STR_LIT>': _count_or_len(contraditory_pairs),<EOL>'<STR_LIT>': _count_or_len(separately_unstable_triples),<EOL>'<STR_LIT>': _count_or_len(mutually_unstable_triples),<EOL>'<STR_LIT>': _count_or_len(jens_unstable_triples),<EOL>'<STR_LIT>': _count_or_len(increase_mismatch_triples),<EOL>'<STR_LIT>': _count_or_len(decrease_mismatch_triples),<EOL>'<STR_LIT>': _count_or_len(chaotic_triples),<EOL>'<STR_LIT>': _count_or_len(dampened_triples)<EOL>}<EOL>


<EOL>hypotheses = defaultdict(set)<EOL>increases = defaultdict(set)<EOL>decreases = defaultdict(set)<EOL>for u, v, d in graph.edges(data=True):<EOL><INDENT>hypotheses[u].add(v)<EOL>if d[RELATION] in CAUSAL_INCREASE_RELATIONS:<EOL><INDENT>increases[u].add(v)<EOL><DEDENT>elif d[RELATION] in CAUSAL_DECREASE_RELATIONS:<EOL><INDENT>decreases[u].add(v)<EOL><DEDENT><DEDENT>correct = defaultdict(int)<EOL>contra = defaultdict(int)<EOL>ambiguous = defaultdict(int)<EOL>missing = defaultdict(int)<EOL>for controller, downstream_nodes in hypotheses.items():<EOL><INDENT>if len(downstream_nodes) < <NUM_LIT:4>:<EOL><INDENT>continue  <EOL><DEDENT>for node in downstream_nodes:<EOL><INDENT>if node in increases[controller] and node in decreases[controller]:<EOL><INDENT>ambiguous[controller] += <NUM_LIT:1><EOL><DEDENT>elif node in increases[controller]:<EOL><INDENT>if graph.node[node][tag] == <NUM_LIT:1>:<EOL><INDENT>correct[controller] += <NUM_LIT:1><EOL><DEDENT>elif graph.node[node][tag] == -<NUM_LIT:1>:<EOL><INDENT>contra[controller] += <NUM_LIT:1><EOL><DEDENT><DEDENT>elif node in decreases[controller]:<EOL><INDENT>if graph.node[node][tag] == <NUM_LIT:1>:<EOL><INDENT>contra[controller] += <NUM_LIT:1><EOL><DEDENT>elif graph.node[node][tag] == -<NUM_LIT:1>:<EOL><INDENT>correct[controller] += <NUM_LIT:1><EOL><DEDENT><DEDENT>else:<EOL><INDENT>missing[controller] += <NUM_LIT:1><EOL><DEDENT><DEDENT><DEDENT>controllers = {<EOL>controller<EOL>for controller, downstream_nodes in hypotheses.items()<EOL>if <NUM_LIT:4> <= len(downstream_nodes)<EOL>}<EOL>concordance_scores = {<EOL>controller: scipy.stats.beta(<NUM_LIT:0.5>, correct[controller], contra[controller])<EOL>for controller in controllers<EOL>}<EOL>population = {<EOL>node<EOL>for controller in controllers<EOL>for node in hypotheses[controller]<EOL>}<EOL>population_size = len(population)<EOL>return pandas.DataFrame({<EOL>'<STR_LIT>': contra,<EOL>'<STR_LIT>': correct,<EOL>'<STR_LIT>': concordance_scores<EOL>})<EOL>

Run the reverse causal reasoning algorithm on a graph.

    Steps:

    1. Get all downstream controlled things into map (that have at least 4 downstream things)
    2. calculate population of all things that are downstream controlled

    .. note:: Assumes all nodes have been pre-tagged with data

    :param pybel.BELGraph graph:
    :param str tag: The key for the nodes' data dictionaries that corresponds to the integer value for its differential
                    expression.


def get_cutoff(value: float, cutoff: Optional[float] = None) -> int:

cutoff = cutoff if cutoff is not None else <NUM_LIT:0><EOL>if value > cutoff:<EOL><INDENT>return <NUM_LIT:1><EOL><DEDENT>if value < (-<NUM_LIT:1> * cutoff):<EOL><INDENT>return - <NUM_LIT:1><EOL><DEDENT>return <NUM_LIT:0><EOL>

Assign if a value is greater than or less than a cutoff.


def edge_concords(graph, u, v, k, d, key, cutoff: Optional[float] = None) -> Concordance:

if key not in graph.nodes[u] or key not in graph.nodes[v]:<EOL><INDENT>return Concordance.unassigned<EOL><DEDENT>relation = d[RELATION]<EOL>if relation not in (UP | DOWN | {CAUSES_NO_CHANGE}):<EOL><INDENT>return Concordance.unassigned<EOL><DEDENT>source_regulation = get_cutoff(graph.nodes[u][key], cutoff=cutoff)<EOL>target_regulation = get_cutoff(graph.nodes[v][key], cutoff=cutoff)<EOL>if source_regulation == <NUM_LIT:1>:<EOL><INDENT>if target_regulation == <NUM_LIT:1> and relation in UP:<EOL><INDENT>return Concordance.correct<EOL><DEDENT>elif target_regulation == <NUM_LIT:1> and relation in DOWN:<EOL><INDENT>return Concordance.incorrect<EOL><DEDENT>elif target_regulation == -<NUM_LIT:1> and relation in UP:<EOL><INDENT>return Concordance.incorrect<EOL><DEDENT>elif target_regulation == -<NUM_LIT:1> and relation in DOWN:<EOL><INDENT>return Concordance.correct<EOL><DEDENT>elif target_regulation == <NUM_LIT:0> and relation in (DOWN | UP):<EOL><INDENT>return Concordance.incorrect<EOL><DEDENT>elif target_regulation == <NUM_LIT:0> and relation == CAUSES_NO_CHANGE:<EOL><INDENT>return Concordance.correct<EOL><DEDENT>elif target_regulation in {<NUM_LIT:1>, -<NUM_LIT:1>} and relation == CAUSES_NO_CHANGE:<EOL><INDENT>return Concordance.incorrect<EOL><DEDENT>else:<EOL><INDENT>log.warning('<STR_LIT>', u, source_regulation, relation, v, target_regulation)<EOL>return Concordance.ambiguous<EOL><DEDENT><DEDENT>elif source_regulation == -<NUM_LIT:1>:<EOL><INDENT>if target_regulation == <NUM_LIT:1> and relation in UP:<EOL><INDENT>return Concordance.incorrect<EOL><DEDENT>elif target_regulation == <NUM_LIT:1> and relation in DOWN:<EOL><INDENT>return Concordance.correct<EOL><DEDENT>elif target_regulation == -<NUM_LIT:1> and relation in UP:<EOL><INDENT>return Concordance.correct<EOL><DEDENT>elif target_regulation == -<NUM_LIT:1> and relation in DOWN:<EOL><INDENT>return Concordance.incorrect<EOL><DEDENT>elif target_regulation == <NUM_LIT:0> and relation in (DOWN | UP):<EOL><INDENT>return Concordance.incorrect<EOL><DEDENT>elif target_regulation == <NUM_LIT:0> and relation == CAUSES_NO_CHANGE:<EOL><INDENT>return Concordance.correct<EOL><DEDENT>elif target_regulation in {<NUM_LIT:1>, -<NUM_LIT:1>} and relation == CAUSES_NO_CHANGE:<EOL><INDENT>return Concordance.incorrect<EOL><DEDENT>else:<EOL><INDENT>log.warning('<STR_LIT>', u, source_regulation, relation, v, target_regulation)<EOL>return Concordance.ambiguous<EOL><DEDENT><DEDENT>else:  <EOL><INDENT>if target_regulation == <NUM_LIT:0> and relation == CAUSES_NO_CHANGE:<EOL><INDENT>return Concordance.correct<EOL><DEDENT>return Concordance.ambiguous<EOL><DEDENT>

:param pybel.BELGraph graph: A BEL graph
:param u:
:param v:
:param k:
:param d:
:param str key: The node data dictionary key storing the logFC
:param float cutoff: The optional logFC cutoff for significance
:rtype: Concordance


def calculate_concordance_helper(graph: BELGraph,<EOL>key: str,<EOL>cutoff: Optional[float] = None,<EOL>) -> Tuple[int, int, int, int]:

scores = defaultdict(int)<EOL>for u, v, k, d in graph.edges(keys=True, data=True):<EOL><INDENT>c = edge_concords(graph, u, v, k, d, key, cutoff=cutoff)<EOL>scores[c] += <NUM_LIT:1><EOL><DEDENT>return (<EOL>scores[Concordance.correct],<EOL>scores[Concordance.incorrect],<EOL>scores[Concordance.ambiguous],<EOL>scores[Concordance.unassigned],<EOL>)<EOL>

Help calculate network-wide concordance

    Assumes data already annotated with given key

    :param graph: A BEL graph
    :param key: The node data dictionary key storing the logFC
    :param cutoff: The optional logFC cutoff for significance


def calculate_concordance(graph: BELGraph, key: str, cutoff: Optional[float] = None,<EOL>use_ambiguous: bool = False) -> float:

correct, incorrect, ambiguous, _ = calculate_concordance_helper(graph, key, cutoff=cutoff)<EOL>try:<EOL><INDENT>return correct / (correct + incorrect + (ambiguous if use_ambiguous else <NUM_LIT:0>))<EOL><DEDENT>except ZeroDivisionError:<EOL><INDENT>return -<NUM_LIT:1.0><EOL><DEDENT>

Calculates network-wide concordance.

    Assumes data already annotated with given key

    :param graph: A BEL graph
    :param key: The node data dictionary key storing the logFC
    :param cutoff: The optional logFC cutoff for significance
    :param use_ambiguous: Compare to ambiguous edges as well


def one_sided(value: float, distribution: List[float]) -> float:

assert distribution<EOL>return sum(value < element for element in distribution) / len(distribution)<EOL>

Calculate the one-sided probability of getting a value more extreme than the distribution.