Source code for neuron_morphology.features.size

from typing import Optional, List, Union, Dict, Any
from statistics import mean
from collections import defaultdict
from functools import partial

from neuron_morphology.morphology import Morphology
from neuron_morphology.constants import SOMA
from neuron_morphology.feature_extractor.marked_feature import marked
from neuron_morphology.feature_extractor.mark import (
    Geometric, RequiresRadii, RequiresRoot)
from neuron_morphology.feature_extractor.data import (
    MorphologyLike, get_morphology)

@marked(Geometric)
def total_length(
    data: MorphologyLike, 
    node_types: Optional[List[int]] = None
) -> float:
    """ Calculate the total length across all compartments in a reconstruction

    Parameters
    ----------
    data : the input reconstruction
    node_types : if provided, restrict the calculation to compartments 
        involving these types

    Returns
    -------
    The sum of segment lengths across all segments in the reconstruction

    Notes
    -----
    Excludes compartments where the parent is:
        1. the soma
        2. a root of the reconstruction
    The logic here is that the soma root is likely to substantially overlap any 
    of its compartments, while non-root soma nodes will be closer to the soma 
    surface.

    """

    morphology = get_morphology(data)

    nodes = morphology.get_node_by_types(node_types)
    compartment_list = morphology.get_compartments(nodes, node_types)

    total = 0.0
    for compartment in compartment_list:
        first_node_in_compartment = compartment[0]
        if first_node_in_compartment['type'] is SOMA \
            and not morphology.parent_of(first_node_in_compartment):
            continue
        total += morphology.get_compartment_length(compartment)

    return total


@marked(RequiresRadii)
@marked(Geometric)
def total_surface_area(
    data: MorphologyLike, 
    node_types: Optional[List[int]] = None
) -> float:
    """ Calculates the sum of lateral surface areas across all comparments 
    (linked pairs of nodes) in a reconstruction. This approximates the total 
    surface area of the reconstruction. See 
    Morphology.get_compartment_surface_area for details.

    Parameters
    ----------
    data : The reconstruction whose surface area will be computed
    node_types : restrict the calculation to compartments involving these node 
        types

    Returns
    -------
    The sum of compartment lateral surface areas across this reconstruction

    """

    morphology: Morphology = get_morphology(data)
    nodes = morphology.get_node_by_types(node_types)
    compartments = morphology.get_compartments(nodes, node_types)

    return sum(map(morphology.get_compartment_surface_area, compartments))


@marked(RequiresRadii)
@marked(Geometric)
def total_volume(
    data: MorphologyLike, 
    node_types: Optional[List[int]] = None
) -> float:
    """ Calculates the sum of volumes across all comparments (linked pairs of 
    nodes) in a reconstruction. This approximates the total volume of the 
    reconstruction. See Morphology.get_compartment_volume for details.

    Parameters
    ----------
    data : The reconstruction whose volume will be computed
    node_types : restrict the calculation to compartments involving these node 
        types

    Returns
    -------
    The sum of compartment volumes across this reconstruction

    """
    
    morphology = get_morphology(data)
    nodes = morphology.get_node_by_types(node_types)
    compartments = morphology.get_compartments(nodes, node_types)

    return sum(map(morphology.get_compartment_volume, compartments))


@marked(RequiresRadii)
def mean_diameter(
    data: MorphologyLike, 
    node_types: Optional[List[int]] = None
) -> float:
    """ Calculates the mean diameter of all nodes
    
    Parameters
    ----------
    morphology : The reconstruction whose mean diameter
    node_types : restrict the calculation to compartments involving these node 
        types

    Returns
    -------
    The average diameter across selected nodes

    """

    morphology = get_morphology(data)

    return 2 * mean(
        node["radius"] for node in morphology.get_node_by_types(node_types)
    )



[docs]def parent_daughter_ratio_visitor( node: Dict[str, Any], morphology: Morphology, counters: Dict[str, Union[int, float]], node_types: Optional[List[int]] = None ): """ Calculates for a single node the ratio of the node's parent's radius to the node's radius. Stores these values in a provided dictionary. Parameters ---------- node : The node under consideration morphology : The reconstruction to which this node belongs counters : a dictionary used for storing running ratio totals and counts. node_types : skip nodes not of one of these types Notes ----- see mean_parent_daughter_ratio for usage """ parent = morphology.parent_of(node) if parent is None: return if node_types is not None: if node["type"] not in node_types or parent["type"] not in node_types: return counters["ratio_sum"] += parent["radius"] / node["radius"] counters["ratio_count"] += 1
@marked(RequiresRadii) def mean_parent_daughter_ratio( data: MorphologyLike, node_types: Optional[List[int]] = None ) -> float: """ Calculate the average ratio of parent radii to child radii across a reconstruction. Parameters ---------- data : The reconstruction whose mean parent daugther ratio will be computed node_types : restrict the calculation to compartments involving these node types Notes ----- Note that this function differs from the L-measure parent daughter ratio, which calculates the ratio of the child node size to the parent. Note also that both the parent and child must be in node_types in order for a compartment to be included in the calculation """ morphology = get_morphology(data) roots = morphology.get_roots() counters: Dict[str, int] = defaultdict(lambda *a, **k: 0) visitor = partial( parent_daughter_ratio_visitor, morphology=morphology, counters=counters, node_types=node_types ) for root in roots: morphology.breadth_first_traversal( visitor, start_id=morphology.node_id_cb(root) ) return counters["ratio_sum"] / counters["ratio_count"] @marked(RequiresRoot) @marked(Geometric) def max_euclidean_distance( data: MorphologyLike, node_types: Optional[List[int]] = None ) -> float: """Calculate the furthest distance, in 3-space, of a compartment's end from the soma. This is equivalent to the distance to the furthest SWC node. Parameters ---------- data: The reconstruction whose max euclidean distance will be calculated node_types: restrict consideration to these types Returns ------- The distance between the soma and the farthest-from-soma node in this morphology. """ morphology = get_morphology(data) soma = morphology.get_root() return max( morphology.euclidean_distance(soma, node) for node in morphology.get_node_by_types(node_types) )