From 458903789d3ee92f6574c820e3a6ef7e7ed292a3 Mon Sep 17 00:00:00 2001 From: Eleftherios Zisis Date: Mon, 9 May 2022 09:26:07 +0200 Subject: [PATCH] Morphology level radial distance features use the soma as reference point (#1030) Use soma center as a reference point for morphology-level radial distance features. Radial distance features on the morphology level were using as reference point the root of each neurite. While this is ok when per-neurite features are calculated, it is incorrect when the radial distance features are calculated for the entire cell. This change renders soma as the reference point for calculating the radial distances, not the root of each neurite. When the same features are used per-neurite the old behavior of using the neurite root is still the same. Co-authored-by: Adrien Berchet --- neurom/features/morphology.py | 70 ++++++- neurom/features/neurite.py | 47 +++-- tests/features/test_get_features.py | 44 +++- tests/test_mixed.py | 304 ++++++++++++++++++++-------- 4 files changed, 354 insertions(+), 111 deletions(-) diff --git a/neurom/features/morphology.py b/neurom/features/morphology.py index c36525c2..fc0175fe 100644 --- a/neurom/features/morphology.py +++ b/neurom/features/morphology.py @@ -59,7 +59,7 @@ from neurom.core.types import NeuriteType from neurom.exceptions import NeuroMError from neurom.features import feature, NameSpace, neurite as nf, section as sf -from neurom.utils import str_to_plane +from neurom.utils import str_to_plane, flatten from neurom.morphmath import convex_hull @@ -115,15 +115,77 @@ def soma_radius(morph): @feature(shape=()) def max_radial_distance(morph, origin=None, neurite_type=NeuriteType.all, use_subtrees=False): """Get the maximum radial distances of the termination sections.""" + origin = morph.soma.center if origin is None else origin + term_radial_distances = _map_neurites( partial(nf.max_radial_distance, origin=origin), - morph, - neurite_type=neurite_type, - use_subtrees=use_subtrees, + morph, neurite_type, use_subtrees ) return max(term_radial_distances) if term_radial_distances else 0. +@feature(shape=(...,)) +def section_radial_distances(morph, origin=None, neurite_type=NeuriteType.all, use_subtrees=False): + """Section radial distances. + + The iterator_type can be used to select only terminal sections (ileaf) + or only bifurcations (ibifurcation_point). + """ + origin = morph.soma.center if origin is None else origin + + return list(flatten(_map_neurites( + partial(nf.section_radial_distances, origin=origin), + morph=morph, + neurite_type=neurite_type, + use_subtrees=use_subtrees, + ))) + + +@feature(shape=(...,)) +def section_term_radial_distances( + morph, origin=None, neurite_type=NeuriteType.all, use_subtrees=False +): + """Get the radial distances of the termination sections.""" + origin = morph.soma.center if origin is None else origin + + return list(flatten(_map_neurites( + partial(nf.section_term_radial_distances, origin=origin), + morph=morph, + neurite_type=neurite_type, + use_subtrees=use_subtrees + ))) + + +@feature(shape=(...,)) +def section_bif_radial_distances( + morph, origin=None, neurite_type=NeuriteType.all, use_subtrees=False +): + """Get the radial distances of the bifurcation sections.""" + origin = morph.soma.center if origin is None else origin + + return list(flatten(_map_neurites( + partial(nf.section_bif_radial_distances, origin=origin), + morph=morph, + neurite_type=neurite_type, + use_subtrees=use_subtrees, + ))) + + +@feature(shape=(...,)) +def segment_radial_distances( + morph, origin=None, neurite_type=NeuriteType.all, use_subtrees=False +): + """Ger the radial distances of the segments.""" + origin = morph.soma.center if origin is None else origin + + return list(flatten(_map_neurites( + partial(nf.segment_radial_distances, origin=origin), + morph=morph, + neurite_type=neurite_type, + use_subtrees=use_subtrees, + ))) + + @feature(shape=(...,)) def number_of_sections_per_neurite(morph, neurite_type=NeuriteType.all, use_subtrees=False): """List of numbers of sections per neurite.""" diff --git a/neurom/features/neurite.py b/neurom/features/neurite.py index 7c972779..2669f67f 100644 --- a/neurom/features/neurite.py +++ b/neurom/features/neurite.py @@ -81,15 +81,6 @@ def homogeneous_filter(section): return list(map(fun, filter(filt, iterator_type(neurite.root_node)))) -@feature(shape=()) -def max_radial_distance(neurite, origin=None, section_type=NeuriteType.all): - """Get the maximum radial distances of the termination sections.""" - term_radial_distances = section_term_radial_distances( - neurite, origin=origin, section_type=section_type - ) - return max(term_radial_distances) if term_radial_distances else 0. - - @feature(shape=()) def number_of_segments(neurite, section_type=NeuriteType.all): """Number of segments.""" @@ -423,34 +414,48 @@ def diameter_power_relations(neurite, method='first', section_type=NeuriteType.a ) +def _radial_distances(neurite, origin, iterator_type, section_type): + + if origin is None: + origin = neurite.root_node.points[0] + + return _map_sections( + partial(sf.section_radial_distance, origin=origin), + neurite=neurite, + iterator_type=iterator_type, + section_type=section_type + ) + + @feature(shape=(...,)) -def section_radial_distances( - neurite, origin=None, iterator_type=Section.ipreorder, section_type=NeuriteType.all -): +def section_radial_distances(neurite, origin=None, section_type=NeuriteType.all): """Section radial distances. The iterator_type can be used to select only terminal sections (ileaf) or only bifurcations (ibifurcation_point). """ - pos = neurite.root_node.points[0] if origin is None else origin - return _map_sections(partial(sf.section_radial_distance, origin=pos), - neurite, - iterator_type, - section_type=section_type) + return _radial_distances(neurite, origin, Section.ipreorder, section_type) @feature(shape=(...,)) def section_term_radial_distances(neurite, origin=None, section_type=NeuriteType.all): """Get the radial distances of the termination sections.""" - return section_radial_distances(neurite, origin, Section.ileaf, section_type=section_type) + return _radial_distances(neurite, origin, Section.ileaf, section_type) + + +@feature(shape=()) +def max_radial_distance(neurite, origin=None, section_type=NeuriteType.all): + """Get the maximum radial distances of the termination sections.""" + term_radial_distances = section_term_radial_distances( + neurite, origin=origin, section_type=section_type + ) + return max(term_radial_distances) if term_radial_distances else 0. @feature(shape=(...,)) def section_bif_radial_distances(neurite, origin=None, section_type=NeuriteType.all): """Get the radial distances of the bf sections.""" - return section_radial_distances( - neurite, origin, Section.ibifurcation_point, section_type=section_type - ) + return _radial_distances(neurite, origin, Section.ibifurcation_point, section_type) @feature(shape=(...,)) diff --git a/tests/features/test_get_features.py b/tests/features/test_get_features.py index 65ab30d5..8f48c912 100644 --- a/tests/features/test_get_features.py +++ b/tests/features/test_get_features.py @@ -112,23 +112,23 @@ def test_number_of_sections(): def test_max_radial_distance(): assert_allclose( features.get('max_radial_distance', POP), - [99.58945832, 94.43342439, 1053.77939245]) + [99.62086, 94.43019, 1072.9137]) assert_allclose( features.get('max_radial_distance', POP, neurite_type=NeuriteType.all), - [99.58945832, 94.43342439, 1053.77939245]) + [99.62086, 94.43019, 1072.9137]) assert_allclose( features.get('max_radial_distance', POP, neurite_type=NeuriteType.axon), - [82.442545, 82.442545, 1053.779392]) + [82.52528, 82.44438, 1072.9137]) assert_allclose( features.get('max_radial_distance', POP, neurite_type=NeuriteType.basal_dendrite), - [94.43342563, 94.43342439, 207.56977859]) + [94.36033, 94.43019, 209.92587]) assert_allclose( - features.get('max_radial_distance', NRN), 99.58945832) + features.get('max_radial_distance', NRN), 99.62086) assert_allclose( - features.get('max_radial_distance', NRN, neurite_type=NeuriteType.all), 99.58945832) + features.get('max_radial_distance', NRN, neurite_type=NeuriteType.all), 99.62086) assert_allclose(features.get( - 'max_radial_distance', NRN, neurite_type=NeuriteType.apical_dendrite), 99.589458) + 'max_radial_distance', NRN, neurite_type=NeuriteType.apical_dendrite), 99.62086) assert_allclose( features.get('max_radial_distance', NRN.neurites), @@ -771,7 +771,19 @@ def test_section_strahler_orders(): def test_section_bif_radial_distances(): + + # the feature applied on morph calculates radial distance from soma trm_rads = features.get('section_bif_radial_distances', NRN, neurite_type=nm.AXON) + + assert_allclose( + trm_rads, + [8.92228 , 16.825268, 23.152378, 30.262894, 36.71048 , + 44.049297, 52.00228 , 59.510105, 66.33529 , 74.134636] + ) + + # the feature applied per neurite calculates radial distance from root + trm_rads = features.get('section_bif_radial_distances', NRN.neurites[3]) + assert_allclose(trm_rads, [8.842008561870646, 16.7440421479104, @@ -786,7 +798,25 @@ def test_section_bif_radial_distances(): def test_section_term_radial_distances(): + trm_rads = features.get('section_term_radial_distances', NRN, neurite_type=nm.APICAL_DENDRITE) + + print(trm_rads) + assert_allclose(trm_rads, + [16.258472, + 26.040075, + 33.35425 , + 42.755745, + 52.41365 , + 59.476284, + 67.11225 , + 80.00984 , + 87.13672 , + 97.284706, + 99.62086 ]) + + apical = NRN.neurites[0] + trm_rads = features.get('section_term_radial_distances', apical, section_type=nm.APICAL_DENDRITE) assert_allclose(trm_rads, [16.22099879395879, 25.992977561564082, diff --git a/tests/test_mixed.py b/tests/test_mixed.py index fe05b9fc..00d4187d 100644 --- a/tests/test_mixed.py +++ b/tests/test_mixed.py @@ -245,7 +245,7 @@ def population(mixed_morph): return Population([mixed_morph, mixed_morph]) -def _assert_feature_equal(values, expected_values): +def _assert_feature_equal(values, expected_values, per_neurite=False): def innermost_value(iterable): while isinstance(iterable, collections.abc.Iterable): @@ -260,24 +260,34 @@ def innermost_value(iterable): a, b, err_msg=f"ACTUAL: {a}\nDESIRED: {b}", verbose=False ) - # handle empty lists because allclose always passes in that case. - # See: https://github.com/numpy/numpy/issues/11071 - if isinstance(values, collections.abc.Iterable): - if isinstance(expected_values, collections.abc.Iterable): - if isinstance(innermost_value(values), (float, np.floating)): - npt.assert_allclose(values, expected_values, atol=1e-5) + def check(values, expected_values): + # handle empty lists because allclose always passes in that case. + # See: https://github.com/numpy/numpy/issues/11071 + if isinstance(values, collections.abc.Iterable): + if isinstance(expected_values, collections.abc.Iterable): + if isinstance(innermost_value(values), (float, np.floating)): + npt.assert_allclose(values, expected_values, atol=1e-5) + else: + assert_equal(values, expected_values) else: assert_equal(values, expected_values) else: - assert_equal(values, expected_values) - else: - if isinstance(expected_values, collections.abc.Iterable): - assert_equal(values, expected_values) - else: - if isinstance(values, (float, np.floating)): - npt.assert_allclose(values, expected_values, atol=1e-5) - else: + if isinstance(expected_values, collections.abc.Iterable): assert_equal(values, expected_values) + else: + if isinstance(values, (float, np.floating)): + npt.assert_allclose(values, expected_values, atol=1e-5) + else: + assert_equal(values, expected_values) + + if per_neurite: + + assert len(values) == len(expected_values) + for neurite_values, expected_neurite_values in zip(values, expected_values): + check(neurite_values, expected_neurite_values) + else: + check(values, expected_values) + def _dispatch_features(features, mode=None): @@ -401,19 +411,19 @@ def _morphology_features(mode): # with subtrees AoD subtrees are considered separately and the distance is calculated # from their respective roots. [1, 4] is the furthest point in this case "kwargs": {"neurite_type": NeuriteType.all}, - "expected_wout_subtrees": 3.741657, - "expected_with_subtrees": 3.316625, + "expected_wout_subtrees": 4.472136, + "expected_with_subtrees": 4.472136, }, { # with a global origin, AoD axon subtree [2, 4] is always furthest from soma "kwargs": {"neurite_type": NeuriteType.all, "origin": np.array([0., 0., 0.])}, - "expected_wout_subtrees": 4.47213595499958, - "expected_with_subtrees": 4.47213595499958, + "expected_wout_subtrees": 4.472136, + "expected_with_subtrees": 4.472136, }, { "kwargs": {"neurite_type": NeuriteType.basal_dendrite}, - "expected_wout_subtrees": 3.741657, - "expected_with_subtrees": 3.316625, + "expected_wout_subtrees": 4.472136, + "expected_with_subtrees": 4.24264, }, { "kwargs": {"neurite_type": NeuriteType.basal_dendrite, "origin": np.array([0., 0., 0.])}, @@ -424,7 +434,7 @@ def _morphology_features(mode): { "kwargs": {"neurite_type": NeuriteType.axon}, "expected_wout_subtrees": 0.0, - "expected_with_subtrees": 2.44949, + "expected_with_subtrees": 4.472136, }, { "kwargs": {"neurite_type": NeuriteType.axon, "origin": np.array([0., 0., 0.])}, @@ -1154,35 +1164,35 @@ def _morphology_features(mode): # the root of the subtree is considered "kwargs": {"neurite_type": NeuriteType.all}, "expected_wout_subtrees": - [1. , 2. , 2.236068, 2.236068, 1.414214] + - [1.414214, 3.162278, 3.316625, 3.316625] + - [2.828427, 3.605551, 3.605551, 3.741657, 3.741657] + - [1., 2., 2.236068, 2.236068, 1.414214], + [2., 3., 3.162278, 3.162278, 2.236068] + + [2.236068, 4.123106, 4.24264 , 4.24264] + + [3.605551, 4.472136, 4.24264 , 4.358899, 4.358899] + + [2., 3., 3.162278, 3.162278, 2.236068], "expected_with_subtrees": - [1. , 2. , 2.236068, 2.236068, 1.414214] + - [1.414214, 3.162278, 3.316625, 3.316625] + - [1.414214, 2.236068, 2.236068, 2.44949 , 2.44949] + - [1., 2., 2.236068, 2.236068, 1.414214], + [2., 3., 3.162278, 3.162278, 2.236068] + + [2.236068, 4.123106, 4.24264 , 4.24264] + + [3.605551, 4.472136, 4.24264 , 4.358899, 4.358899] + + [2., 3., 3.162278, 3.162278, 2.236068], }, { "kwargs": {"neurite_type": NeuriteType.basal_dendrite}, "expected_wout_subtrees": - [1. , 2. , 2.236068, 2.236068, 1.414214] + - [1.414214, 3.162278, 3.316625, 3.316625] + - [2.828427, 3.605551, 3.605551, 3.741657, 3.741657], + [2., 3., 3.162278, 3.162278, 2.236068] + + [2.236068, 4.123106, 4.24264 , 4.24264] + + [3.605551, 4.472136, 4.24264 , 4.358899, 4.358899], "expected_with_subtrees": - [1. , 2. , 2.236068, 2.236068, 1.414214] + - [1.414214, 3.162278, 3.316625, 3.316625], + [2., 3., 3.162278, 3.162278, 2.236068] + + [2.236068, 4.123106, 4.24264 , 4.24264], }, { "kwargs": {"neurite_type": NeuriteType.axon}, "expected_wout_subtrees": [], - "expected_with_subtrees": [1.414214, 2.236068, 2.236068, 2.44949 , 2.44949], + "expected_with_subtrees": [3.605551, 4.472136, 4.24264 , 4.358899, 4.358899], }, { "kwargs": {"neurite_type": NeuriteType.apical_dendrite}, - "expected_wout_subtrees": [1., 2., 2.236068, 2.236068, 1.414214], - "expected_with_subtrees": [1., 2., 2.236068, 2.236068, 1.414214], + "expected_wout_subtrees": [2., 3., 3.162278, 3.162278, 2.236068], + "expected_with_subtrees": [2., 3., 3.162278, 3.162278, 2.236068], } ], @@ -1190,35 +1200,35 @@ def _morphology_features(mode): { "kwargs": {"neurite_type": NeuriteType.all}, "expected_wout_subtrees": - [2.236068, 2.236068, 1.414214] + - [3.316625, 3.316625] + - [3.605551, 3.741657, 3.741657] + - [2.236068, 2.236068, 1.414214], + [3.162278, 3.162278, 2.236068] + + [4.24264 , 4.24264] + + [4.472136, 4.358899, 4.358899] + + [3.162278, 3.162278, 2.236068], "expected_with_subtrees": - [2.236068, 2.236068, 1.414214] + - [3.316625, 3.316625] + - [2.236068, 2.44949 , 2.44949] + - [2.236068, 2.236068, 1.414214], + [3.162278, 3.162278, 2.236068] + + [4.24264 , 4.24264] + + [4.472136, 4.358899, 4.358899] + + [3.162278, 3.162278, 2.236068], }, { "kwargs": {"neurite_type": NeuriteType.basal_dendrite}, "expected_wout_subtrees": - [2.236068, 2.236068, 1.414214] + - [3.316625, 3.316625] + - [3.605551, 3.741657, 3.741657], + [3.162278, 3.162278, 2.236068] + + [4.24264 , 4.24264] + + [4.472136, 4.358899, 4.358899], "expected_with_subtrees": - [2.236068, 2.236068, 1.414214] + - [3.316625, 3.316625], + [3.162278, 3.162278, 2.236068] + + [4.24264 , 4.24264], }, { "kwargs": {"neurite_type": NeuriteType.axon}, "expected_wout_subtrees": [], - "expected_with_subtrees": [2.236068, 2.44949 , 2.44949], + "expected_with_subtrees": [4.472136, 4.358899, 4.358899], }, { "kwargs": {"neurite_type": NeuriteType.apical_dendrite}, - "expected_wout_subtrees": [2.236068, 2.236068, 1.414214], - "expected_with_subtrees": [2.236068, 2.236068, 1.414214], + "expected_wout_subtrees": [3.162278, 3.162278, 2.236068], + "expected_with_subtrees": [3.162278, 3.162278, 2.236068], } ], @@ -1229,25 +1239,25 @@ def _morphology_features(mode): # heterogeneous forks are not valid forking points "kwargs": {"neurite_type": NeuriteType.all}, "expected_wout_subtrees": - [1., 2., 1.414214, 3.162278, 2.828427, 3.605551, 1., 2.], + [2., 3., 2.236068, 4.123106, 3.605551, 4.24264 , 2., 3.], "expected_with_subtrees": - [1., 2., 3.162278, 1.414214, 2.236068, 1., 2.], + [2., 3., 4.123106, 3.605551, 4.24264 , 2., 3.], }, { "kwargs": {"neurite_type": NeuriteType.basal_dendrite}, "expected_wout_subtrees": - [1., 2., 1.414214, 3.162278, 2.828427, 3.605551], - "expected_with_subtrees": [1., 2., 3.162278], + [2., 3., 2.236068, 4.123106, 3.605551, 4.24264], + "expected_with_subtrees": [2., 3., 4.123106], }, { "kwargs": {"neurite_type": NeuriteType.axon}, "expected_wout_subtrees": [], - "expected_with_subtrees": [1.414214, 2.236068], + "expected_with_subtrees": [3.605551, 4.24264], }, { "kwargs": {"neurite_type": NeuriteType.apical_dendrite}, - "expected_wout_subtrees": [1., 2.], - "expected_with_subtrees": [1., 2.], + "expected_wout_subtrees": [2., 3.], + "expected_with_subtrees": [2., 3.], } ], "section_end_distances": [ @@ -1602,35 +1612,35 @@ def _morphology_features(mode): # the root of the subtree is considered "kwargs": {"neurite_type": NeuriteType.all}, "expected_wout_subtrees": - [0.5, 1.5, 2.061553, 2.061553, 1.118034] + - [0.707107, 2.236068, 3.201562, 3.201562] + - [2.12132 , 3.201562, 3.201562, 3.640055, 3.640055] + - [0.5, 1.5, 2.061553, 2.061553, 1.118034], + [1.5, 2.5, 3.041381, 3.041381, 2.061553] + + [1.581139, 3.162278, 4.153312, 4.153312] + + [2.915476, 4.031129, 3.905125, 4.272002, 4.272002] + + [1.5, 2.5, 3.041381, 3.041381, 2.061553], "expected_with_subtrees": - [0.5, 1.5, 2.061553, 2.061553, 1.118034] + - [0.707107, 2.236068, 3.201562, 3.201562] + - [0.707107, 1.802776, 1.802776, 2.291288, 2.291288] + - [0.5, 1.5, 2.061553, 2.061553, 1.118034], + [1.5, 2.5, 3.041381, 3.041381, 2.061553] + + [1.581139, 3.162278, 4.153312, 4.153312] + + [2.915476, 4.031129, 3.905125, 4.272002, 4.272002] + + [1.5, 2.5, 3.041381, 3.041381, 2.061553], }, { "kwargs": {"neurite_type": NeuriteType.basal_dendrite}, "expected_wout_subtrees": - [0.5, 1.5, 2.061553, 2.061553, 1.118034] + - [0.707107, 2.236068, 3.201562, 3.201562] + - [2.12132 , 3.201562, 3.201562, 3.640055, 3.640055], + [1.5, 2.5, 3.041381, 3.041381, 2.061553] + + [1.581139, 3.162278, 4.153312, 4.153312] + + [2.915476, 4.031129, 3.905125, 4.272002, 4.272002], "expected_with_subtrees": - [0.5, 1.5, 2.061553, 2.061553, 1.118034] + - [0.707107, 2.236068, 3.201562, 3.201562], + [1.5, 2.5, 3.041381, 3.041381, 2.061553] + + [1.581139, 3.162278, 4.153312, 4.153312], }, { "kwargs": {"neurite_type": NeuriteType.axon}, "expected_wout_subtrees": [], - "expected_with_subtrees": [0.707107, 1.802776, 1.802776, 2.291288, 2.291288], + "expected_with_subtrees": [2.915476, 4.031129, 3.905125, 4.272002, 4.272002], }, { "kwargs": {"neurite_type": NeuriteType.apical_dendrite}, - "expected_wout_subtrees": [0.5, 1.5, 2.061553, 2.061553, 1.118034], - "expected_with_subtrees": [0.5, 1.5, 2.061553, 2.061553, 1.118034], + "expected_wout_subtrees": [1.5, 2.5, 3.041381, 3.041381, 2.061553], + "expected_with_subtrees": [1.5, 2.5, 3.041381, 3.041381, 2.061553], }, ], "segment_midpoints": [ @@ -2180,6 +2190,142 @@ def _neurite_features(): "expected": [np.nan, np.nan, 0.2356194583819102], }, ], + "section_radial_distances": [ + { + "kwargs": {"section_type": NeuriteType.all}, + "expected": [ + [1.0, 2.0, 2.236068, 2.236068, 1.4142135], + [1.4142135, 3.1622777, 3.3166249, 3.3166249, 2.828427, 3.6055512, 3.6055512, 3.7416575, 3.7416575], + [1.0, 2.0, 2.236068, 2.236068, 1.4142135], + ], + }, + { + "kwargs": {"section_type": NeuriteType.basal_dendrite}, + "expected": [ + [1.0, 2.0, 2.236068, 2.236068, 1.4142135], + [1.414214, 3.162278, 3.316625, 3.316625], + [], + ], + }, + { + "kwargs": {"section_type": NeuriteType.axon}, + "expected": [ + [], + [2.828427, 3.605551, 3.605551, 3.741657, 3.741657], + [], + ], + }, + { + "kwargs": {"section_type": NeuriteType.apical_dendrite}, + "expected": [ + [], + [], + [1., 2., 2.236068, 2.236068, 1.414214], + ], + }, + ], + "section_bif_radial_distances": [ + { + "kwargs": {"section_type": NeuriteType.all}, + "expected": [ + [1., 2.], + [1.414214, 3.162278, 2.828427, 3.605551], + [1., 2.], + ], + }, + { + "kwargs": {"section_type": NeuriteType.basal_dendrite}, + "expected": [ + [1., 2.], + [3.162278], + [], + ], + }, + { + "kwargs": {"section_type": NeuriteType.axon}, + "expected": [ + [], + [2.828427, 3.605551], + [], + ], + }, + { + "kwargs": {"section_type": NeuriteType.apical_dendrite}, + "expected": [ + [], + [], + [1., 2.], + ], + }, + ], + "section_term_radial_distances": [ + { + "kwargs": {"section_type": NeuriteType.all}, + "expected": [ + [2.236068, 2.236068, 1.414214], + [3.316625, 3.316625, 3.605551, 3.741657, 3.741657], + [2.236068, 2.236068, 1.414214], + ], + }, + { + "kwargs": {"section_type": NeuriteType.basal_dendrite}, + "expected": [ + [2.236068, 2.236068, 1.414214], + [3.316625, 3.316625], + [], + ], + }, + { + "kwargs": {"section_type": NeuriteType.axon}, + "expected": [ + [], + [3.605551, 3.741657, 3.741657], + [], + ], + }, + { + "kwargs": {"section_type": NeuriteType.apical_dendrite}, + "expected": [ + [], + [], + [2.236068, 2.236068, 1.414214], + ], + }, + ], + "segment_radial_distances": [ + { + "kwargs": {"section_type": NeuriteType.all}, + "expected": [ + [0.5 , 1.5 , 2.061553, 2.061553, 1.118034], + [0.707107, 2.236068, 3.201562, 3.201562, 2.12132 , 3.201562, 3.201562, 3.640055, 3.640055], + [0.5 , 1.5 , 2.061553, 2.061553, 1.118034], + ], + }, + { + "kwargs": {"section_type": NeuriteType.basal_dendrite}, + "expected": [ + [0.5 , 1.5 , 2.061553, 2.061553, 1.118034], + [0.707107, 2.236068, 3.201562, 3.201562], + [], + ], + }, + { + "kwargs": {"section_type": NeuriteType.axon}, + "expected": [ + [], + [2.12132 , 3.201562, 3.201562, 3.640055, 3.640055], + [], + ], + }, + { + "kwargs": {"section_type": NeuriteType.apical_dendrite}, + "expected": [ + [], + [], + [0.5 , 1.5 , 2.061553, 2.061553, 1.118034], + ], + }, + ] } # features that exist in both the neurite and morphology level, which indicates a different @@ -2189,7 +2335,7 @@ def _neurite_features(): ) assert not features_not_tested, ( - "The following morphology tests need to be included in the tests:\n\n" + + "The following morphology tests need to be included in the mixed neurite tests:\n\n" + "\n".join(sorted(features_not_tested)) + "\n" ) @@ -2201,4 +2347,4 @@ def test_morphology__neurite_features(feature_name, kwargs, expected, mixed_morp with warnings.catch_warnings(): warnings.simplefilter("ignore") values = get(feature_name, mixed_morph.neurites, **kwargs) - _assert_feature_equal(values, expected) + _assert_feature_equal(values, expected, per_neurite=True)