Heterogeneous Morphologies¶
Definition¶
A heterogeneous morphology consists of zero or more homogeneous and at least one heterogeneous neurite tree extending from the soma. A heterogeneous neurite tree consists of multiple sub-neurites with different types (ie: basal and axon).
A typical example of a heterogeneous neurite is the axon-carrying dendrite, in which the axon sprouts from the basal dendrite.
Identification¶
Heterogeneous neurites can be identified using the Neurite::is_heterogeneous
method:
from neurom import load_morphology
from neurom.core.morphology import iter_neurites
m = load_morphology('tests/data/swc/heterogeneous_morphology.swc')
print([neurite.is_heterogeneous() for neurite in m.neurites])
which would return [False, True, False]
, meaning the 2nd neurite extending from the soma contains multiple neurite types.
sub-neurite views of heterogeneous neurites¶
Default mode¶
NeuroM does not take into account heterogeneous sub-neurites by default. A heterogeneous neurite is treated as a homogeneous one, the type of which is determined by the first section of the tree. For example:
basal, axon_carrying_dendrite, apical = list(iter_neurites(m))
print(basal.type, axon_carrying_dendrite.type, apical.type)
NeuriteType.basal_dendrite NeuriteType.basal_dendrite NeuriteType.apical_dendrite
In other words, the axon-carrying dendrite would be treated as a basal dendrite.
For feature extraction and checks, the axon-carrying dendrite is treated as a basal dendrite.
Features, for which an axon neurite type is passed, do not have access to the axonal part of the neurite.
For instance, the number of basal and axon neurites will be two and zero respectively.
A feature such as total_volume
would include the entire axon-carrying dendrite, without separating between basal and axon types.
subtree mode¶
The Population
, Morphology
and Neurite
objects have a boolean attribute named process_subtrees
which is set to False
by default.
The value of this attribute can be set to True
in order to take into account heterogeneous sub-neurites.
m.process_subtrees = True
basal, axon_carrying_basal, apical = list(iter_neurites(m))
print(basal.type, axon_carrying_basal.type, apical.type)
NeuriteType.basal_dendrite NeuriteType.axon_carrying_dendrite NeuriteType.apical_dendrite
In the example above, two views of the axon-carrying dendrite have been created: the basal dendrite view and the axon view.
Given that the topology of the morphology is not modified, the sub-neurites specify as their root_node
the same section of the homogeneous neurite.
So, in this case, both the basal and axon views start at the same section but then the filters used in iterators are different.
This also means that the sub-trees have no ‘trunk’, as a trunk is defined as a section connected to the soma.
Extract features from heterogeneous morphologies¶
Neurite¶
Neurite objects have been extended to include a process_subtrees
flag, which can be used to apply a feature on a heterogeneous neurite.
from neurom import NeuriteType
from neurom.features.neurite import number_of_sections
axon_carrying_dendrite = m.neurites[1]
axon_carrying_dendrite.process_subtrees = True
total_sections = number_of_sections(axon_carrying_dendrite)
basal_sections = number_of_sections(axon_carrying_dendrite, section_type=NeuriteType.basal_dendrite)
axon_sections = number_of_sections(axon_carrying_dendrite, section_type=NeuriteType.axon)
print(total_sections, basal_sections, axon_sections)
9 4 5
Not specifying a section_type
is equivalent to passing NeuriteType.all
and it will use all sections as done historically, even if process_subtrees
is set to True
.
Morphology¶
Morphology objects have been extended to include the process_subtrees
flag, which allows to use the sub-neurites.
from neurom.features.morphology import number_of_neurites
m.process_subtrees = False
total_neurites_wout_subneurites = number_of_neurites(m)
m.process_subtrees = True
total_neurites_with_subneurites = number_of_neurites(m)
print("A:", total_neurites_wout_subneurites, total_neurites_with_subneurites)
m.process_subtrees = False
number_of_axon_neurites_wout = number_of_neurites(m, neurite_type=NeuriteType.axon)
m.process_subtrees = True
number_of_axon_neurites_with = number_of_neurites(m, neurite_type=NeuriteType.axon)
print("B:", number_of_axon_neurites_wout, number_of_axon_neurites_with)
m.process_subtrees = False
number_of_basal_neurites_wout = number_of_neurites(m, neurite_type=NeuriteType.basal_dendrite)
m.process_subtrees = True
number_of_basal_neurites_with = number_of_neurites(m, neurite_type=NeuriteType.basal_dendrite)
print("C:", number_of_basal_neurites_wout, number_of_basal_neurites_with)
A: 3 3
B: 0 1
C: 2 2
In the example above, the total number of neurites is the same when the subtrees are enabled (see A
in the print out.)
This is because the axonal and basal parts of the axon-carrying dendrite are counted as one neurite.
Specifying a neurite_type
, allows to consider sub-neurites.
Therefore, the number of axons without subtrees is 0, whereas it is 1 when subtrees are enabled (see B
in the print out.)
However, for basal dendrites the number does not change (2) because the axon-carrying dendrite is perceived as basal dendrite in the default case (see C
.)
features.get¶
features.get
can be used with respect to what has been mentioned above for neurite and morphology features.
from neurom import features
m.process_subtrees = True
n_neurites = features.get("number_of_neurites", m)
n_sections = features.get("number_of_sections", m, neurite_type=NeuriteType.axon)
print(f"Neurites: {n_neurites}, Sections: {n_sections}")
Neurites: 3, Sections: 5
Warning
The features.get
function can be used with either the neurite_type
or the section_type
parameter, depending on what type of object the feature is applied.
When the feature is applied to a Population
or to a Morphology
object, only the neurite_type
parameter is accepted.
While the feature is applied to a Neurite
or to a list of Neurite
objects, only the section_type
parameter is accepted.
Conventions & Incompatibilities¶
Heterogeneous Forks¶
A heterogeneous bifurcation/fork, i.e. a section with children of different types, is ignored when features on bifurcations are calculated. It is not meaningful to calculate features, such as bifurcation angles, on transitional forks where the downstream subtrees have different types.
Incompatible features with subtrees¶
The following features are not compatible with subtrees:
trunk_origin_azimuths
trunk_origin_elevations
trunk_angles
Because they require the neurites to be rooted at the soma.
This is not true for sub-neurites.
Therefore, passing a Neurite object with a process_subtrees
flag set to True
will result in an empty list.