def has_valid_soma(data_wrapper): """Check if a data block has a valid soma. Returns: CheckResult with result """ try: make_soma(data_wrapper.soma_points()) return CheckResult(True) except SomaError: return CheckResult(False)
def has_no_jumps(neuron, max_distance=30.0, axis='z'): '''Check if there are jumps (large movements in the `axis`) Arguments: neuron(Neuron): The neuron object to test max_distance(float): value above which consecutive z-values are considered a jump axis(str): one of x/y/z, which axis to check for jumps Returns: CheckResult with result list of ids of bad sections ''' bad_ids = [] axis = { 'x': COLS.X, 'y': COLS.Y, 'z': COLS.Z, }[axis.lower()] for neurite in iter_neurites(neuron): section_segment = ((sec, seg) for sec in iter_sections(neurite) for seg in iter_segments(sec)) for sec, (p0, p1) in islice(section_segment, 1, None): # Skip neurite root segment if max_distance < abs(p0[axis] - p1[axis]): bad_ids.append((sec.id, [p0, p1])) return CheckResult(len(bad_ids) == 0, bad_ids)
def has_no_dangling_branch(neuron): '''Check if the neuron has dangling neurites''' soma_center = neuron.soma.points[:, COLS.XYZ].mean(axis=0) recentered_soma = neuron.soma.points[:, COLS.XYZ] - soma_center radius = np.linalg.norm(recentered_soma, axis=1) soma_max_radius = radius.max() def is_dangling(neurite): '''Is the neurite dangling ?''' starting_point = neurite.points[1][COLS.XYZ] if np.linalg.norm(starting_point - soma_center) - soma_max_radius <= 12.: return False if neurite.type != NeuriteType.axon: return True all_points = list( chain.from_iterable(n.points[1:] for n in iter_neurites(neurite) if n.type != NeuriteType.axon)) res = [ np.linalg.norm(starting_point - p[COLS.XYZ]) >= 2 * p[COLS.R] + 2 for p in all_points ] return all(res) bad_ids = [(n.root_node.id, [n.root_node.points[1]]) for n in iter_neurites(neuron) if is_dangling(n)] return CheckResult(len(bad_ids) == 0, bad_ids)
def has_no_single_children(neuron): """Check if the neuron has sections with only one child section.""" bad_ids = [ section.id for section in iter_sections(neuron) if len(section.children) == 1 ] return CheckResult(len(bad_ids) == 0, bad_ids)
def has_no_narrow_neurite_section(neuron, neurite_filter, radius_threshold=0.05, considered_section_min_length=50): '''Check if the neuron has dendrites with narrow sections Arguments: neuron(Neuron): The neuron object to test neurite_filter(callable): filter the neurites by this callable radius_threshold(float): radii below this are considered narro considered_section_min_length(float): sections with length below this are not taken into account Returns: CheckResult with result. result.info contains the narrow section ids and their first point ''' considered_sections = ( sec for sec in iter_sections(neuron, neurite_filter=neurite_filter) if sec.length > considered_section_min_length) def narrow_section(section): '''Select narrow sections''' return section.points[:, COLS.R].mean() < radius_threshold bad_ids = [(section.id, section.points[1]) for section in considered_sections if narrow_section(section)] return CheckResult(len(bad_ids) == 0, bad_ids)
def has_no_fat_ends(neuron, multiple_of_mean=2.0, final_point_count=5): '''Check if leaf points are too large Arguments: neuron(Neuron): The neuron object to test multiple_of_mean(float): how many times larger the final radius has to be compared to the mean of the final points final_point_count(int): how many points to include in the mean Returns: CheckResult with result list of ids of bad sections Note: A fat end is defined as a leaf segment whose last point is larger by a factor of `multiple_of_mean` than the mean of the points in `final_point_count` ''' bad_ids = [] for leaf in _nf.iter_sections(neuron.neurites, iterator_type=Tree.ileaf): mean_radius = np.mean(leaf.points[1:][-final_point_count:, COLS.R]) if mean_radius * multiple_of_mean <= leaf.points[-1, COLS.R]: bad_ids.append((leaf.id, leaf.points[-1:])) return CheckResult(len(bad_ids) == 0, bad_ids)
def has_soma_points(data_wrapper): """Checks if the TYPE column of raw data block has an element of type soma. Returns: CheckResult with result """ db = data_wrapper.data_block return CheckResult(POINT_TYPE.SOMA in db[:, COLS.TYPE], None)
def has_valid_neurites(data_wrapper): """Check if any neurites can be reconstructed from data block. Returns: CheckResult with result """ n, _ = make_neurites(data_wrapper) return CheckResult(len(n) > 0)
def _try(checker, neuron): """Try to apply a checker, returns True if exception raised, so the checker is bypassed.""" try: return checker(neuron) except Exception as e: # pylint: disable=broad-except L.exception("%s failed on %s", checker, morph_path) L.exception(e, exc_info=True) return CheckResult(True)
def has_sequential_ids(data_wrapper): """Check that IDs are increasing and consecutive. returns tuple (bool, list of IDs that are not consecutive with their predecessor) """ db = data_wrapper.data_block ids = db[:, COLS.ID] steps = ids[np.where(np.diff(ids) != 1)[0] + 1].astype(int) return CheckResult(len(steps) == 0, steps)
def has_no_narrow_start(neuron, frac=0.9): '''Check if neurites have a narrow start Returns: CheckResult with a list of all first segments of neurites with a narrow start ''' bad_ids = [(neurite.root_node.id, [neurite.root_node.points[1]]) for neurite in neuron.neurites if neurite.root_node.points[1][COLS.R] < frac * neurite.root_node.points[2][COLS.R]] return CheckResult(len(bad_ids) == 0, bad_ids)
def has_all_monotonic_neurites(neuron, tol=1e-6): '''Check that a neuron has only neurites that are monotonic Arguments: neuron(Neuron): The neuron object to test tol(float): tolerance Returns: CheckResult with result ''' return CheckResult(len(get_nonmonotonic_neurites(neuron, tol)) == 0)
def no_missing_parents(data_wrapper): '''Check that all points have existing parents Point's parent ID must exist and parent must be declared before child. Returns: CheckResult with result and list of IDs that have no parent ''' db = data_wrapper.data_block ids = np.setdiff1d(db[:, COLS.P], db[:, COLS.ID])[1:] return CheckResult(len(ids) == 0, ids.astype(np.int) + 1)
def has_nonzero_soma_radius(neuron, threshold=0.0): '''Check if soma radius not above threshold Arguments: neuron(Neuron): The neuron object to test threshold: value above which the soma radius is considered to be non-zero Returns: CheckResult with result ''' return CheckResult(neuron.soma.radius > threshold)
def has_increasing_ids(data_wrapper): """Check that IDs are increasing. Returns: CheckResult with result and list of IDs that are inconsistent with their predecessor """ db = data_wrapper.data_block ids = db[:, COLS.ID] steps = ids[np.where(np.diff(ids) <= 0)[0] + 1].astype(int) return CheckResult(len(steps) == 0, steps)
def has_no_root_node_jumps(neuron, radius_multiplier=2): ''' Check that the neurites have their first point not further than `radius_multiplier * soma radius` from the soma center''' bad_ids = [] for neurite in iter_neurites(neuron): p0 = neurite.root_node.points[0, COLS.XYZ] distance = np.linalg.norm(p0 - neuron.soma.center) if distance > radius_multiplier * neuron.soma.radius: bad_ids.append((neurite.root_node.id, [p0])) return CheckResult(len(bad_ids) == 0, bad_ids)
def has_axon(neuron, treefun=_read_neurite_type): '''Check if a neuron has an axon Arguments: neuron(Neuron): The neuron object to test treefun: Optional function to calculate the tree type of neuron's neurites Returns: CheckResult with result ''' return CheckResult(NeuriteType.axon in (treefun(n) for n in neuron.neurites))
def has_all_finite_radius_neurites(data_wrapper, threshold=0.0): """Check that all points with neurite type have a finite radius. Returns: CheckResult with result and list of IDs of neurite points with zero radius """ db = data_wrapper.data_block neurite_ids = np.in1d(db[:, COLS.TYPE], POINT_TYPE.NEURITES) zero_radius_ids = db[:, COLS.R] <= threshold bad_pts = np.array(db[neurite_ids & zero_radius_ids][:, COLS.ID], dtype=int).tolist() return CheckResult(len(bad_pts) == 0, bad_pts)
def test_generate_annotation(): checker_ok = CheckResult(True) checker_not_ok = CheckResult(False, [('section 1', [[1, 2, 3], [4, 5, 6]]), ('section 2', [[7, 8, 9], [10, 11, 12]])]) settings = {'color': 'blue', 'label': 'circle', 'name': 'dangling'} assert generate_annotation(checker_ok, settings) == "" correct_result = """ (circle ; MUK_ANNOTATION (Color blue) ; MUK_ANNOTATION (Name "dangling") ; MUK_ANNOTATION ( 1.00 2.00 3.00 0.50) ; MUK_ANNOTATION ( 4.00 5.00 6.00 0.50) ; MUK_ANNOTATION ( 7.00 8.00 9.00 0.50) ; MUK_ANNOTATION ( 10.00 11.00 12.00 0.50) ; MUK_ANNOTATION ) ; MUK_ANNOTATION """ assert generate_annotation(checker_not_ok, settings) == correct_result
def has_no_flat_neurites(neuron, tol=0.1, method='ratio'): '''Check that a neuron has no flat neurites Arguments: neuron(Neuron): The neuron object to test tol(float): tolerance method(string): way of determining flatness, 'tolerance', 'ratio' \ as described in :meth:`neurom.check.morphtree.get_flat_neurites` Returns: CheckResult with result ''' return CheckResult(len(get_flat_neurites(neuron, tol, method)) == 0)
def has_apical_dendrite(neuron, min_number=1, treefun=_read_neurite_type): """Check if a neuron has apical dendrites. Arguments: neuron(Neuron): The neuron object to test min_number: minimum number of apical dendrites required treefun: Optional function to calculate the tree type of neuron's neurites Returns: CheckResult with result """ types = [treefun(n) for n in neuron.neurites] return CheckResult(types.count(NeuriteType.apical_dendrite) >= min_number)
def test_generate_annotation(): checker_ok = CheckResult(True) checker_not_ok = CheckResult(False, [('section 1', [[1, 2, 3], [4, 5, 6]]), ('section 2', [[7, 8, 9], [10, 11, 12]])]) settings = {'color': 'blue', 'label': 'circle', 'name': 'dangling'} nt.assert_equal(generate_annotation(checker_ok, settings), "") correct_result = """ (circle ; MUK_ANNOTATION (Color blue) ; MUK_ANNOTATION (Name "dangling") ; MUK_ANNOTATION (1 2 3 0.50) ; MUK_ANNOTATION (4 5 6 0.50) ; MUK_ANNOTATION (7 8 9 0.50) ; MUK_ANNOTATION (10 11 12 0.50) ; MUK_ANNOTATION ) ; MUK_ANNOTATION """ nt.assert_equal(generate_annotation(checker_not_ok, settings), correct_result)
def has_no_narrow_start(neuron, frac=0.9): '''Check if neurites have a narrow start Arguments: neuron(Neuron): The neuron object to test frac(float): Ratio that the second point must be smaller than the first Returns: CheckResult with a list of all first segments of neurites with a narrow start ''' bad_ids = [(neurite.root_node.id, [neurite.root_node.points[1]]) for neurite in neuron.neurites if neurite.root_node.points[1][COLS.R] < frac * neurite.root_node.points[2][COLS.R]] return CheckResult(len(bad_ids) == 0, bad_ids)
def is_single_tree(data_wrapper): """Check that data forms a single tree. Only the first point has ID of -1. Returns: CheckResult with result and list of IDs Note: This assumes no_missing_parents passed. """ db = data_wrapper.data_block bad_ids = db[db[:, COLS.P] == -1][1:, COLS.ID] return CheckResult(len(bad_ids) == 0, bad_ids.tolist())
def has_all_nonzero_section_lengths(neuron, threshold=0.0): '''Check presence of neuron sections with length not above threshold Arguments: neuron(Neuron): The neuron object to test threshold(float): value above which a section length is considered to be non-zero Returns: CheckResult with result including list of ids of bad sections ''' bad_ids = [s.id for s in _nf.iter_sections(neuron.neurites) if section_length(s.points) <= threshold] return CheckResult(len(bad_ids) == 0, bad_ids)
def has_no_dangling_branch(neuron): """Check if the neuron has dangling neurites. Are considered dangling - dendrites whose first point is too far from the soma center - axons whose first point is too far from the soma center AND from any point belonging to a dendrite Arguments: neuron(Neuron): The neuron object to test Returns: CheckResult with a list of all first segments of dangling neurites """ if len(neuron.soma.points) == 0: raise NeuroMError( 'Can\'t check for dangling neurites if there is no soma') soma_center = neuron.soma.points[:, COLS.XYZ].mean(axis=0) recentered_soma = neuron.soma.points[:, COLS.XYZ] - soma_center radius = np.linalg.norm(recentered_soma, axis=1) soma_max_radius = radius.max() dendritic_points = np.array( list( chain.from_iterable(n.points for n in iter_neurites(neuron) if n.type != NeuriteType.axon))) def is_dangling(neurite): """Is the neurite dangling ?.""" starting_point = neurite.points[0][COLS.XYZ] if np.linalg.norm(starting_point - soma_center) - soma_max_radius <= 12.: return False if neurite.type != NeuriteType.axon: return True distance_to_dendrites = np.linalg.norm(dendritic_points[:, COLS.XYZ] - starting_point, axis=1) return np.all( distance_to_dendrites >= 2 * dendritic_points[:, COLS.R] + 2) bad_ids = [(n.root_node.id, [n.root_node.points[0]]) for n in iter_neurites(neuron) if is_dangling(n)] return CheckResult(len(bad_ids) == 0, bad_ids)
def has_all_nonzero_segment_lengths(neuron, threshold=0.0): '''Check presence of neuron segments with length not above threshold Arguments: neuron(Neuron): The neuron object to test threshold(float): value above which a segment length is considered to be non-zero Returns: CheckResult with result including list of (section_id, segment_id) of zero length segments ''' bad_ids = [] for sec in _nf.iter_sections(neuron): p = sec.points for i, s in enumerate(zip(p[:-1], p[1:])): if segment_length(s) <= threshold: bad_ids.append((sec.id, i)) return CheckResult(len(bad_ids) == 0, bad_ids)
def has_all_nonzero_neurite_radii(neuron, threshold=0.0): '''Check presence of neurite points with radius not above threshold Arguments: neuron(Neuron): The neuron object to test threshold: value above which a radius is considered to be non-zero Returns: CheckResult with result including list of (section ID, point ID) pairs of zero-radius points ''' bad_ids = [] seen_ids = set() for s in _nf.iter_sections(neuron): for i, p in enumerate(s.points): info = (s.id, i) if p[COLS.R] <= threshold and info not in seen_ids: seen_ids.add(info) bad_ids.append(info) return CheckResult(len(bad_ids) == 0, bad_ids)
def has_no_narrow_neurite_section(neuron, neurite_filter, radius_threshold=0.05, considered_section_min_length=50): '''Check if the neuron has dendrites with narrow sections sections below 'considered_section_min_length' are not taken into account Returns: CheckResult with result. result.info contains the narrow section ids and their first point ''' considered_sections = ( sec for sec in iter_sections(neuron, neurite_filter=neurite_filter) if sec.length > considered_section_min_length) def narrow_section(section): '''Select narrow sections''' return section.points[:, COLS.R].mean() < radius_threshold bad_ids = [(section.id, section.points[1]) for section in considered_sections if narrow_section(section)] return CheckResult(len(bad_ids) == 0, bad_ids)
def has_no_jumps(neuron, max_distance=30.0, axis='z'): '''Check if there are jumps (large movements in the `axis`) Arguments: neuron(Neuron): The neuron object to test max_z_distance(float): value above which consecutive z-values are considered a jump axis(str): one of x/y/z, which axis to check for jumps Returns: CheckResult with result list of ids bad sections ''' bad_ids = [] axis = { 'x': COLS.X, 'y': COLS.Y, 'z': COLS.Z, }[axis.lower()] for sec in _nf.iter_sections(neuron): for i, (p0, p1) in enumerate(iter_segments(sec)): info = (sec.id, i) if max_distance < abs(p0[axis] - p1[axis]): bad_ids.append(info) return CheckResult(len(bad_ids) == 0, bad_ids)