Skip to content

Commit

Permalink
Replace iter_* methods by properties in core objects and improve iter…
Browse files Browse the repository at this point in the history
…_segments (#1054)

* Improve iter_segments and add segment methods in core objects

* Rename iterator_type into section_iterator

* Remove iter_* methods and use consistent properties

* Use iter_sections instead of obj.sections in check.morphtree

* Update doc
  • Loading branch information
adrien-berchet authored Apr 18, 2024
1 parent 4672c78 commit 90224b1
Show file tree
Hide file tree
Showing 8 changed files with 190 additions and 904 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Changelog
Version 4.0.0
-------------

- Replace ``iter_*`` methods by properties in core objects and improve ``iter_segments``. (#1054)
- NeuriteType extended to allow mixed type declarations as tuple of ints. (#1071)
- All features return built-in types (#1064)
- Morphology class also allows mutable morphio objects to be passed explicitly. (#1049)
Expand Down
21 changes: 21 additions & 0 deletions doc/source/migration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,27 @@ The following modules have been deprecated:
- ``neurom/check/neuron_checks.py`` (use ``neurom/check/morphology_checks.py``)
- ``neurom/viewer.py`` (use ``from neurom.view import plot_[morph|morph3d|dendrogram]``)

New and deprecated methods in core classes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The ``neurom.core.morphology.Neurite.iter_sections()`` has been deprecated. It is now possible to
access lower scale elements of any core class using properties:

- ``neurom.core.morphology.Section.segments``
- ``neurom.core.morphology.Section.points``
- ``neurom.core.morphology.Neurite.sections``
- ``neurom.core.morphology.Neurite.segments``
- ``neurom.core.morphology.Neurite.points``
- ``neurom.core.morphology.Morphology.neurites``
- ``neurom.core.morphology.Morphology.sections``
- ``neurom.core.morphology.Morphology.segments``
- ``neurom.core.morphology.Morphology.points``

Note that these properties return all elements in a list. It is possible to use
``neurom.core.morphology.iter_neurites()``, ``neurom.core.morphology.iter_sections()``,
``neurom.core.morphology.iter_segments()`` and ``neurom.core.morphology.iter_points()`` to get a
generator or to filter the elements.

Breaking changes in Morphology class
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
2 changes: 1 addition & 1 deletion examples/radius_of_gyration.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ def main():
print(
[
(
sum(len(s.points) - 1 for s in nrte.iter_sections()),
sum(len(s.points) - 1 for s in nrte.sections),
radius_of_gyration(nrte),
nrte.type,
)
Expand Down
5 changes: 3 additions & 2 deletions neurom/check/morphtree.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

from neurom import morphmath as mm
from neurom.core.dataformat import COLS
from neurom.core.morphology import iter_sections
from neurom.morphmath import principal_direction_extent


Expand All @@ -47,7 +48,7 @@ def is_monotonic(neurite, tol):
Returns:
True if neurite monotonic
"""
for node in neurite.iter_sections():
for node in iter_sections(neurite):
# check that points in section satisfy monotonicity
sec = node.points
for point_id in range(len(sec) - 1):
Expand Down Expand Up @@ -171,7 +172,7 @@ def is_inside_cylinder(seg1, seg2):
return not is_in_the_same_verse(seg1, seg2) and is_seg1_overlapping_with_seg2(seg1, seg2)

# filter out single segment sections
section_itr = (sec for sec in neurite.iter_sections() if sec.points.shape[0] > 2)
section_itr = (sec for sec in iter_sections(neurite) if sec.points.shape[0] > 2)
for sec in section_itr:
# group each section's points intro triplets
segment_pairs = list(filter(is_not_zero_seg, pair(sec.points)))
Expand Down
50 changes: 32 additions & 18 deletions neurom/core/morphology.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,11 @@ def __hash__(self):
"""Hash of its id."""
return self.id

@property
def segments(self):
"""The array of all segments of the neurite."""
return list(iter_segments(self))

@property
def points(self):
"""Returns the section list of points the NeuroM way (points + radius)."""
Expand Down Expand Up @@ -325,7 +330,11 @@ def iter_sections(


def iter_segments(
obj, neurite_filter=None, neurite_order=NeuriteIter.FileOrder, section_filter=None
obj,
neurite_filter=None,
neurite_order=NeuriteIter.FileOrder,
section_filter=None,
section_iterator=Section.ipreorder,
):
"""Return an iterator to the segments in a collection of neurites.
Expand All @@ -336,6 +345,12 @@ def iter_segments(
- NeuriteIter.FileOrder: order of appearance in the file
- NeuriteIter.NRN: NRN simulator order: soma -> axon -> basal -> apical
section_filter: optional section level filter
section_iterator: section iteration order within a given neurite. Must be one of:
Section.ipreorder: Depth-first pre-order iteration of tree nodes
Section.ipostorder: Depth-first post-order iteration of tree nodes
Section.iupstream: Iterate from a tree node to the root nodes
Section.ibifurcation_point: Iterator to bifurcation points
Section.ileaf: Iterator to all leaves of a tree
Note:
This is a convenience function provided for generic access to
Expand All @@ -347,6 +362,7 @@ def iter_segments(
if isinstance(obj, Section)
else iter_sections(
obj,
iterator_type=section_iterator,
neurite_filter=neurite_filter,
neurite_order=neurite_order,
section_filter=section_filter,
Expand Down Expand Up @@ -443,6 +459,16 @@ def subtree_types(self):

return subtree_types

@property
def sections(self):
"""The array of all sections."""
return list(iter_sections(self))

@property
def segments(self):
"""The array of all segments of the neurite."""
return list(iter_segments(self))

@property
def points(self):
"""Array with all the points in this neurite.
Expand Down Expand Up @@ -492,23 +518,6 @@ def is_heterogeneous(self) -> bool:
"""Returns true if the neurite consists of more that one section types."""
return self.morphio_root_node.is_heterogeneous()

def iter_sections(self, order=Section.ipreorder, neurite_order=NeuriteIter.FileOrder):
"""Iteration over section nodes.
Arguments:
order: section iteration order within a given neurite. Must be one of:
Section.ipreorder: Depth-first pre-order iteration of tree nodes
Section.ipostorder: Depth-first post-order iteration of tree nodes
Section.iupstream: Iterate from a tree node to the root nodes
Section.ibifurcation_point: Iterator to bifurcation points
Section.ileaf: Iterator to all leaves of a tree
neurite_order: order upon which neurites should be iterated. Values:
- NeuriteIter.FileOrder: order of appearance in the file
- NeuriteIter.NRN: NRN simulator order: soma -> axon -> basal -> apical
"""
return iter_sections(self, iterator_type=order, neurite_order=neurite_order)

def __eq__(self, other):
"""If root node ids and types are equal."""
return (
Expand Down Expand Up @@ -572,6 +581,11 @@ def sections(self):
"""The array of all sections, excluding the soma."""
return list(iter_sections(self))

@property
def segments(self):
"""The array of all segments of the sections."""
return list(iter_segments(self))

@property
def points(self):
"""Returns the list of points."""
Expand Down
6 changes: 1 addition & 5 deletions neurom/features/morphology.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,11 +153,7 @@ def max_radial_distance(morph, origin=None, neurite_type=NeuriteType.all):

@feature(shape=(...,))
def section_radial_distances(morph, origin=None, neurite_type=NeuriteType.all):
"""Section radial distances.
The iterator_type can be used to select only terminal sections (ileaf)
or only bifurcations (ibifurcation_point).
"""
"""Section radial distances."""
origin = morph.soma.center if origin is None else origin

return list(
Expand Down
Loading

0 comments on commit 90224b1

Please sign in to comment.