Skip to content

Commit

Permalink
Add new features total_width, total_height and total_depth (#974)
Browse files Browse the repository at this point in the history
  • Loading branch information
eleftherioszisis committed Oct 29, 2021
1 parent 819e5ee commit 5a48859
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
*.pyc
*~
.coverage
.coverage.*
coverage.xml
*venv*
*egg-info*
.eggs
Expand All @@ -16,3 +18,5 @@ dist/*
.ipynb_checkpoints
.tox
pip-wheel-metadata
morphology-2D.html
morphology-3D.html
4 changes: 4 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
Changelog
=========

Version 3.1.0
-------------
- Add morphology features total_width, total_height and total_depth

Version 3.0.2
-------------
- Fix 'raw' mode in ``neurom stats``.
Expand Down
37 changes: 37 additions & 0 deletions neurom/features/morphology.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
from neurom.core.types import NeuriteType
from neurom.features import feature, NameSpace, neurite as nf


feature = partial(feature, namespace=NameSpace.NEURON)


Expand Down Expand Up @@ -294,3 +295,39 @@ def sholl_frequency(morph, neurite_type=NeuriteType.all, step_size=10, bins=None
bins = np.arange(min_soma_edge, min_soma_edge + max_radii, step_size)

return sholl_crossings(morph, morph.soma.center, bins, neurite_type)


def _extent_along_axis(morph, axis, neurite_type):
"""Returns the total extent of the morpholog neurites.
The morphology is filtered by neurite type and the extent is calculated
along the coordinate axis direction (e.g. COLS.X).
"""
it_points = (
p
for n in iter_neurites(morph, filt=is_type(neurite_type))
for p in n.points[:, axis]
)
try:
return abs(np.ptp(np.fromiter(it_points, dtype=np.float32)))
except ValueError:
# a ValueError is thrown when there are no points passed to ptp
return 0.0


@feature(shape=())
def total_width(morph, neurite_type=NeuriteType.all):
"""Extent of morphology along axis x."""
return _extent_along_axis(morph, axis=COLS.X, neurite_type=neurite_type)


@feature(shape=())
def total_height(morph, neurite_type=NeuriteType.all):
"""Extent of morphology along axis y."""
return _extent_along_axis(morph, axis=COLS.Y, neurite_type=neurite_type)


@feature(shape=())
def total_depth(morph, neurite_type=NeuriteType.all):
"""Extent of morphology along axis z."""
return _extent_along_axis(morph, axis=COLS.Z, neurite_type=neurite_type)
53 changes: 53 additions & 0 deletions tests/features/test_get_features.py
Original file line number Diff line number Diff line change
Expand Up @@ -756,3 +756,56 @@ def test_principal_direction_extents():
346.83281498399697]
p = features.get('principal_direction_extents', m)
assert_allclose(p, p_ref, rtol=1e-6)


def test_total_width():

assert_allclose(
features.get('total_width', NRN),
105.0758
)

assert_allclose(
features.get('total_width', NRN, neurite_type=nm.AXON),
33.25306
)

assert_allclose(
features.get('total_width', NRN, neurite_type=nm.BASAL_DENDRITE),
104.57807
)


def test_total_height():

assert_allclose(
features.get('total_height', NRN),
106.11643
)

assert_allclose(
features.get('total_height', NRN, neurite_type=nm.AXON),
57.60017
)

assert_allclose(
features.get('total_height', NRN, neurite_type=nm.BASAL_DENDRITE),
48.516262
)

def test_total_depth():

assert_allclose(
features.get('total_depth', NRN),
54.204086
)

assert_allclose(
features.get('total_depth', NRN, neurite_type=nm.AXON),
49.70138
)

assert_allclose(
features.get('total_depth', NRN, neurite_type=nm.BASAL_DENDRITE),
51.64143
)
52 changes: 52 additions & 0 deletions tests/features/test_morphology.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,3 +278,55 @@ def test_sholl_analysis_custom():
""")
assert (list(morphology.sholl_crossings(morph_C, center, radii=radii)) ==
[2, 2, 2, 2, 2, 2, 10, 10])


def test_extent_along_axis():
morph = load_swc("""
1 1 0 0 0 1. -1
2 3 0 -60 0 1. 1
3 3 80 0 2 1. 2
4 4 0 60 3 1. 1
5 4 -80 0. 0 1. 4
""")
assert_almost_equal(morphology._extent_along_axis(morph, 0, NeuriteType.all), 160.0)
assert_almost_equal(morphology._extent_along_axis(morph, 1, NeuriteType.all), 120.0)
assert_almost_equal(morphology._extent_along_axis(morph, 2, NeuriteType.all), 3.0)


def test_total_width():
morph = load_swc("""
1 1 0 0 0 1. -1
2 3 0 -60 0 1. 1
3 3 80 0 2 1. 2
4 4 0 60 3 1. 1
5 4 -80 0. 0 1. 4
""")
assert_almost_equal(morphology.total_width(morph, neurite_type=NeuriteType.axon), 0.0)
assert_almost_equal(morphology.total_width(morph, neurite_type=NeuriteType.basal_dendrite), 80.0)
assert_almost_equal(morphology.total_width(morph, neurite_type=NeuriteType.apical_dendrite), 80.0)


def test_total_height():
morph = load_swc("""
1 1 0 0 0 1. -1
2 3 0 -60 0 1. 1
3 3 80 0 2 1. 2
4 4 0 60 3 1. 1
5 4 -80 0. 0 1. 4
""")
assert_almost_equal(morphology.total_height(morph, neurite_type=NeuriteType.axon), 0.0)
assert_almost_equal(morphology.total_height(morph, neurite_type=NeuriteType.basal_dendrite), 60.0)
assert_almost_equal(morphology.total_height(morph, neurite_type=NeuriteType.apical_dendrite), 60.0)


def test_total_depth():
morph = load_swc("""
1 1 0 0 0 1. -1
2 3 0 -60 0 1. 1
3 3 80 0 2 1. 2
4 4 0 60 3 1. 1
5 4 -80 0. 0 1. 4
""")
assert_almost_equal(morphology.total_depth(morph, neurite_type=NeuriteType.axon), 0.0)
assert_almost_equal(morphology.total_depth(morph, neurite_type=NeuriteType.basal_dendrite), 2.0)
assert_almost_equal(morphology.total_depth(morph, neurite_type=NeuriteType.apical_dendrite), 3.0)

0 comments on commit 5a48859

Please sign in to comment.