Source code for neuron_morphology.features.intrinsic

from typing import Optional, List
from functools import partial

from neuron_morphology.feature_extractor.data import Data
from neuron_morphology.features.statistics.coordinates import COORD_TYPE

from neuron_morphology.feature_extractor.marked_feature import marked
from neuron_morphology.feature_extractor.mark import Intrinsic


@marked(Intrinsic)
def num_tips(
        data: Data,
        node_types: Optional[List] = None,
        ):
    """
        Calculate number of tips

        Parameters
        ----------

        data: Data Object containing a morphology

        node_types: a list of node types (see neuron_morphology constants)

    """
    # Alternative method:
    num_tips = len(COORD_TYPE.TIP.get_coordinates(data.morphology,
                                                  node_types=node_types))
    return num_tips


@marked(Intrinsic)
def num_nodes(
        data: Data,
        node_types: Optional[List] = None,
        ):
    """
        Calculate number of nodes of a given type

        Parameters
        ----------

        data: Data Object containing a morphology

        node_types: a list of node types (see neuron_morphology constants)

    """
    num_nodes = len(data.morphology.get_node_by_types(node_types))
    return num_nodes


[docs]def child_ids_by_type(node_id, morphology, node_types=None): """ Helper function for the traversal functions""" node = morphology.node_by_id(node_id) children = morphology.get_children( node, node_types=node_types) return [child['id'] for child in children]
[docs]def calculate_branches_from_root(morphology, root, node_types=None): """ Calculate the number of branches of a specific neuron type in a morphology. A branch is defined as being between two bifurcations or between a bifurcation and a tip if a node has three or more children, it is treated as succesive bifurcations, e.g a trifurcation: _/_/__ creates 4 branches since the branch between the two bifurcations counts Parameters ---------- morphology: a morphology object root: the root node to traverse from node_types: a list of node types (see neuron_morphology constants) """ counter = {'num_branches': 0} def branch_visitor(node, counter, node_types): num_children = len(morphology.get_children(node, node_types=node_types)) if num_children > 1: # branches + (implicit branches from successive bifurcations) counter['num_branches'] += num_children + (num_children - 2) elif counter['num_branches'] == 0: # still count root with one child counter['num_branches'] += 1 visitor = partial(branch_visitor, counter=counter, node_types=node_types) neighbor_cb = partial(child_ids_by_type, morphology=morphology, node_types=node_types) morphology.breadth_first_traversal(visitor, start_id=morphology.node_id_cb(root), neighbor_cb=neighbor_cb) return counter['num_branches']
@marked(Intrinsic) def num_branches( data: Data, node_types: Optional[List] = None, ): """ Calculate number of branches Parameters ---------- data: Data Object containing a morphology node_types: a list of node types (see neuron_morphology constants) """ morphology = data.morphology roots = morphology.get_roots() num_branches = 0 for root in roots: num_branches += calculate_branches_from_root( morphology, root, node_types=node_types) return num_branches
[docs]def calculate_mean_fragmentation_from_root(morphology, root, node_types=None): """ Calculate the mean fragmentation from a root in a morphology. Mean fragmentation is the number of compartments over the number of branches. A branch is defined as being between two bifurcations or between a bifurcation and a tip if a node has three or more children, it is treated as succesive bifurcations, e.g a trifurcation: _/_/__ creates 4 branches since the branch between the two bifurcations counts Parameters ---------- morphology: a morphology object root: the root node to traverse from node_types: a list of node types (see neuron_morphology constants) """ counter = {'num_branches': 0, 'num_compartments': 0} def branch_visitor(node, counter, node_types): num_children = len(morphology.get_children(node, node_types=node_types)) if num_children > 1: # branches + (implicit branches from successive bifurcations) counter['num_branches'] += num_children + (num_children - 2) counter['num_compartments'] += num_children + (num_children - 2) elif num_children == 1: counter['num_compartments'] += 1 if counter['num_branches'] == 0: # still count root with one child counter['num_branches'] += 1 visitor = partial(branch_visitor, counter=counter, node_types=node_types) neighbor_cb = partial(child_ids_by_type, morphology=morphology, node_types=node_types) morphology.breadth_first_traversal(visitor, start_id=morphology.node_id_cb(root), neighbor_cb=neighbor_cb) mean_fragmentation = counter['num_compartments'] / counter['num_branches'] return (mean_fragmentation, counter['num_branches'], counter['num_compartments'])
@marked(Intrinsic) def mean_fragmentation( data: Data, node_types: Optional[List] = None, ): """ Calculate the mean number of compartments per branch Parameters ---------- data: Data Object containing a morphology node_types: a list of node types (see neuron_morphology constants) """ morphology = data.morphology roots = morphology.get_roots() num_branches = 0 num_compartments = 0 for root in roots: (_, local_branches, local_compartments) = \ calculate_mean_fragmentation_from_root( morphology, root, node_types=node_types) num_branches += local_branches num_compartments += local_compartments mean_fragmentation = num_compartments / num_branches return mean_fragmentation
[docs]def calculate_max_branch_order_from_root(morphology, root, node_types=None): """ Calculate the maximum number of branches from a root to a tip in a morphology. A branch is defined as being between two bifurcations or between a bifurcation and a tip Unlike mean_fragmentation and num_branches, if a node has multiple children it is counted as a single bifurcation point Parameters ---------- morphology: a morphology object root: the root node to traverse from node_types: a list of node types (see neuron_morphology constants) """ root_id = morphology.node_id_cb(root) counter = {'branches_to_node': {root_id: 0}, 'max_branches': 0} def branch_visitor(node, counter, node_types): cur_branches = counter['branches_to_node'][node['id']] children = morphology.get_children(node, node_types) num_children = len(children) if num_children > 1: for child in children: counter['branches_to_node'][child['id']] = \ cur_branches + 1 elif num_children == 1: if cur_branches == 0: # create a branch even if its just the root + one node counter['branches_to_node'][children[0]['id']] = 1 else: counter['branches_to_node'][children[0]['id']] = cur_branches elif num_children == 0: if cur_branches > counter['max_branches']: counter['max_branches'] = cur_branches visitor = partial(branch_visitor, counter=counter, node_types=node_types) neighbor_cb = partial(child_ids_by_type, morphology=morphology, node_types=node_types) morphology.depth_first_traversal(visitor, start_id=root_id, neighbor_cb=neighbor_cb) return counter['max_branches']
@marked(Intrinsic) def max_branch_order( data: Data, node_types: Optional[List] = None, ): """ Calculate mean fragmentation Parameters ---------- data: Data Object containing a morphology node_types: a list of node types (see neuron_morphology constants) """ morphology = data.morphology roots = morphology.get_roots() max_branch_order = 0 for root in roots: local_max = calculate_max_branch_order_from_root( morphology, root, node_types=node_types) if local_max > max_branch_order: max_branch_order = local_max return max_branch_order