def partition_asymmetries(neurites, neurite_type=NeuriteType.all, variant='branch-order'): """Partition asymmetry at bifurcation points of a collection of neurites. Variant: length is a different definition, as the absolute difference in downstream path lenghts, relative to the total neurite path length """ if variant not in {'branch-order', 'length'}: raise ValueError('Please provide a valid variant for partition asymmetry,\ found %s' % variant) if variant == 'branch-order': return map(bifurcationfunc.partition_asymmetry, iter_sections(neurites, iterator_type=Section.ibifurcation_point, neurite_filter=is_type(neurite_type))) asymmetries = list() for neurite in iter_neurites(neurites, filt=is_type(neurite_type)): neurite_length = total_length_per_neurite(neurite)[0] for section in iter_sections(neurite, iterator_type=Section.ibifurcation_point, neurite_filter=is_type(neurite_type)): pathlength_diff = abs(sectionfunc.downstream_pathlength(section.children[0]) - sectionfunc.downstream_pathlength(section.children[1])) asymmetries.append(pathlength_diff / neurite_length) return asymmetries
def trunk_section_lengths(nrn, neurite_type=NeuriteType.all): """List of lengths of trunk sections of neurites in a neuron.""" neurite_filter = is_type(neurite_type) return [ morphmath.section_length(s.root_node.points) for s in nrn.neurites if neurite_filter(s) ]
def terminal_path_lengths_per_neurite(neurites, neurite_type=NeuriteType.all): """Get the path lengths to each terminal point per neurite in a collection""" return list( sectionfunc.section_path_length(s) for n in iter_neurites(neurites, filt=is_type(neurite_type)) for s in iter_sections(n, iterator_type=Tree.ileaf) )
def trunk_origin_radii(nrn, neurite_type=NeuriteType.all): """Radii of the trunk sections of neurites in a neuron.""" neurite_filter = is_type(neurite_type) return [ s.root_node.points[0][COLS.R] for s in nrn.neurites if neurite_filter(s) ]
def sholl_frequency(nrn, neurite_type=NeuriteType.all, bins=10): """Perform Sholl frequency calculations on a population of neurites. Args: nrn(morph): nrn or population neurite_type(NeuriteType): which neurites to operate on bins(iterable of floats|int): binning to use for the Sholl radii. If ``int`` is used then \ it sets the number of bins in the interval between min and max radii of ``nrn``. Note: Given a neuron, the soma center is used for the concentric circles, which range from the soma radii, and the maximum radial distance in steps of `step_size`. When a population is given, the concentric circles range from the smallest soma radius to the largest radial neurite distance. Finally, each segment of the neuron is tested, so a neurite that bends back on itself, and crosses the same Sholl radius will get counted as having crossed multiple times. """ nrns = neuron_population(nrn) if isinstance(bins, int): min_soma_edge = min(neuron.soma.radius for neuron in nrns) max_radii = np.max([np.abs(bounding_box(neuron)) for neuron in nrns]) bins = np.linspace(min_soma_edge, max_radii, bins) return sum( sholl_crossings(neuron, neuron.soma.center, bins, is_type( neurite_type)) for neuron in nrns)
def trunk_origin_elevations(nrn, neurite_type=NeuriteType.all): """Get a list of all the trunk origin elevations of a neuron or population. The elevation is defined as the angle between x-axis and the vector defined by (initial tree point - soma center) on the x-y half-plane. The range of the elevation angle [-pi/2, pi/2] radians """ neurite_filter = is_type(neurite_type) nrns = neuron_population(nrn) def _elevation(section, soma): """Elevation of a section.""" vector = morphmath.vector(section[0], soma.center) norm_vector = np.linalg.norm(vector) if norm_vector >= np.finfo(type(norm_vector)).eps: return np.arcsin(vector[COLS.Y] / norm_vector) raise ValueError( "Norm of vector between soma center and section is almost zero.") return [ _elevation(s.root_node.points, n.soma) for n in nrns for s in n.neurites if neurite_filter(s) ]
def partition_asymmetries(neurites, neurite_type=NeuriteType.all): '''Partition asymmetry at bifurcation points of a collection of neurites''' return map( _bifurcationfunc.partition_asymmetry, iter_sections(neurites, iterator_type=Tree.ibifurcation_point, neurite_filter=is_type(neurite_type)))
def n_sections(neurites, neurite_type=NeuriteType.all, iterator_type=Tree.ipreorder): '''Number of sections in a collection of neurites''' return sum(1 for _ in iter_sections(neurites, iterator_type=iterator_type, neurite_filter=is_type(neurite_type)))
def bifurcation_partitions(neurites, neurite_type=NeuriteType.all): """Partition at bifurcation points of a collection of neurites.""" return map( bifurcationfunc.bifurcation_partition, iter_sections(neurites, iterator_type=Tree.ibifurcation_point, neurite_filter=is_type(neurite_type)))
def trunk_origin_elevations(nrn, neurite_type=NeuriteType.all): '''Get a list of all the trunk origin elevations of a neuron or population The elevation is defined as the angle between x-axis and the vector defined by (initial tree point - soma center) on the x-y half-plane. The range of the elevation angle [-pi/2, pi/2] radians ''' neurite_filter = is_type(neurite_type) nrns = neuron_population(nrn) def _elevation(section, soma): '''Elevation of a section''' vector = morphmath.vector(section[0], soma.center) norm_vector = np.linalg.norm(vector) if norm_vector >= np.finfo(type(norm_vector)).eps: return np.arcsin(vector[COLS.Y] / norm_vector) else: raise ValueError("Norm of vector between soma center and section is almost zero.") return [_elevation(s.root_node.points, n.soma) for n in nrns for s in n.neurites if neurite_filter(s)]
def trunk_vectors(nrn, neurite_type=NeuriteType.all): """Calculates the vectors between all the trunks of the neuron and the soma center.""" neurite_filter = is_type(neurite_type) nrns = neuron_population(nrn) return np.array([morphmath.vector(s.root_node.points[0], n.soma.center) for n in nrns for s in n.neurites if neurite_filter(s)])
def section_radial_distances(neurites, neurite_type=NeuriteType.all, origin=None): """Remote bifurcation angles in a collection of neurites""" dist = [] for n in iter_neurites(neurites, filt=is_type(neurite_type)): pos = n.root_node.points[0] if origin is None else origin dist.extend([sectionfunc.section_radial_distance(s, pos) for s in n.iter_sections()]) return dist
def partition_pairs(neurites, neurite_type=NeuriteType.all): '''Partition pairs at bifurcation points of a collection of neurites. Partition pait is defined as the number of bifurcations at the two daughters of the bifurcating section''' return map(_bifurcationfunc.partition_pair, iter_sections(neurites, iterator_type=Tree.ibifurcation_point, neurite_filter=is_type(neurite_type)))
def principal_direction_extents(neurites, neurite_type=NeuriteType.all, direction=0): '''Principal direction extent of neurites in neurons''' def _pde(neurite): '''Get the PDE of a single neurite''' # Get the X, Y,Z coordinates of the points in each section points = neurite.points[:, :3] return morphmath.principal_direction_extent(points)[direction] return map(_pde, iter_neurites(neurites, filt=is_type(neurite_type)))
def segment_lengths(neurites, neurite_type=NeuriteType.all): """Lengths of the segments in a collection of neurites""" def _seg_len(sec): """list of segment lengths of a section""" return np.linalg.norm(np.diff(sec.points[:, : COLS.R], axis=0), axis=1) neurite_filter = is_type(neurite_type) return [s for ss in iter_sections(neurites, neurite_filter=neurite_filter) for s in _seg_len(ss)]
def map_segments(func, neurites, neurite_type): ''' Map `func` to all the segments in a collection of neurites `func` accepts a section and returns list of values corresponding to each segment. ''' neurite_filter = is_type(neurite_type) return [ s for ss in iter_sections(neurites, neurite_filter=neurite_filter) for s in func(ss) ]
def principal_direction_extents(neurites, neurite_type=NeuriteType.all, direction=0): """Principal direction extent of neurites in neurons.""" def _pde(neurite): """Get the PDE of a single neurite.""" # Get the X, Y,Z coordinates of the points in each section points = neurite.points[:, :3] return morphmath.principal_direction_extent(points)[direction] return [_pde(neurite) for neurite in iter_neurites(neurites, filt=is_type(neurite_type))]
def total_area_per_neurite(neurites, neurite_type=NeuriteType.all): '''Surface area in a collection of neurites. The area is defined as the sum of the area of the sections. ''' return [ neurite.area for neurite in iter_neurites(neurites, filt=is_type(neurite_type)) ]
def section_radial_distances(neurites, neurite_type=NeuriteType.all, origin=None): '''Remote bifurcation angles in a collection of neurites''' dist = [] for n in iter_neurites(neurites, filt=is_type(neurite_type)): pos = n.root_node.points[0] if origin is None else origin dist.extend([sectionfunc.section_radial_distance(s, pos) for s in n.iter_sections()]) return dist
def principal_direction_extents(neurites, neurite_type=NeuriteType.all, direction=0): '''Principal direction extent of neurites in neurons''' def _pde(neurite): '''Get the PDE of a single neurite''' # Get the X, Y,Z coordinates of the points in each section points = neurite.points[:, :3] return mm.principal_direction_extent(points)[direction] return map(_pde, iter_neurites(neurites, filt=is_type(neurite_type)))
def segment_radii(neurites, neurite_type=NeuriteType.all): """arithmetic mean of the radii of the points in segments in a collection of neurites""" def _seg_radii(sec): """vectorized mean radii""" pts = sec.points[:, COLS.R] return np.divide(np.add(pts[:-1], pts[1:]), 2.0) neurite_filter = is_type(neurite_type) return [s for ss in iter_sections(neurites, neurite_filter=neurite_filter) for s in _seg_radii(ss)]
def segment_midpoints(neurites, neurite_type=NeuriteType.all): """Return a list of segment mid-points in a collection of neurites""" def _seg_midpoint(sec): """Return the mid-points of segments in a section""" pts = sec.points return np.divide(np.add(pts[:-1], pts[1:])[:, :3], 2.0) neurite_filter = is_type(neurite_type) return [s for ss in iter_sections(neurites, neurite_filter=neurite_filter) for s in _seg_midpoint(ss)]
def map_sections(fun, neurites, neurite_type=NeuriteType.all, iterator_type=Tree.ipreorder): '''Map `fun` to all the sections in a collection of neurites''' return map( fun, iter_sections(neurites, iterator_type=iterator_type, neurite_filter=is_type(neurite_type)))
def segment_lengths(neurites, neurite_type=NeuriteType.all): '''Lengths of the segments in a collection of neurites''' def _seg_len(sec): '''list of segment lengths of a section''' vecs = np.diff(sec.points, axis=0)[:, :3] return np.sqrt([np.dot(p, p) for p in vecs]) neurite_filter = is_type(neurite_type) return [s for ss in iter_sections(neurites, neurite_filter=neurite_filter) for s in _seg_len(ss)]
def sibling_ratios(neurites, neurite_type=NeuriteType.all, method='first'): '''Sibling ratios at bifurcation points of a collection of neurites. The sibling ratio is the ratio between the diameters of the smallest and the largest child. It is a real number between 0 and 1. Method argument allows one to consider mean diameters along the child section instead of diameter of the first point. ''' return map( lambda bif_point: _bifurcationfunc.sibling_ratio(bif_point, method), iter_sections(neurites, iterator_type=Tree.ibifurcation_point, neurite_filter=is_type(neurite_type)))
def segment_lengths(neurites, neurite_type=NeuriteType.all): '''Lengths of the segments in a collection of neurites''' def _seg_len(sec): '''list of segment lengths of a section''' return np.linalg.norm(np.diff(sec.points[:, :COLS.R], axis=0), axis=1) neurite_filter = is_type(neurite_type) return [ s for ss in iter_sections(neurites, neurite_filter=neurite_filter) for s in _seg_len(ss) ]
def neurite_volume_density(neurites, neurite_type=NeuriteType.all): '''Get the volume density per neurite The volume density is defined as the ratio of the neurite volume and the volume of the neurite's enclosung convex hull ''' def vol_density(neurite): '''volume density of a single neurite''' return neurite.volume / convex_hull(neurite).volume return list(vol_density(n) for n in iter_neurites(neurites, filt=is_type(neurite_type)))
def diameter_power_relations(neurites, neurite_type=NeuriteType.all, method='first'): """Calculate the diameter power relation at a bifurcation point. Diameter power relation is defined in https://www.ncbi.nlm.nih.gov/pubmed/18568015 This quantity gives an indication of how far the branching is from the Rall ratio (when =1). """ return (bifurcationfunc.diameter_power_relation(bif_point, method) for bif_point in iter_sections(neurites, iterator_type=Section.ibifurcation_point, neurite_filter=is_type(neurite_type)))
def section_radial_distances(neurites, neurite_type=NeuriteType.all, origin=None, iterator_type=Tree.ipreorder): '''Section radial distances in a collection of neurites. The iterator_type can be used to select only terminal sections (ileaf) or only bifurcations (ibifurcation_point).''' dist = [] for n in iter_neurites(neurites, filt=is_type(neurite_type)): pos = n.root_node.points[0] if origin is None else origin dist.extend(sectionfunc.section_radial_distance(s, pos) for s in iter_sections(n, iterator_type=iterator_type)) return dist
def segment_midpoints(neurites, neurite_type=NeuriteType.all): '''Return a list of segment mid-points in a collection of neurites''' def _seg_midpoint(sec): '''Return the mid-points of segments in a section''' pts = sec.points return np.divide(np.add(pts[:-1], pts[1:])[:, :3], 2.0) neurite_filter = is_type(neurite_type) return [ s for ss in iter_sections(neurites, neurite_filter=neurite_filter) for s in _seg_midpoint(ss) ]
def segment_radii(neurites, neurite_type=NeuriteType.all): '''arithmetic mean of the radii of the points in segments in a collection of neurites''' def _seg_radii(sec): '''vectorized mean radii''' pts = sec.points[:, COLS.R] return np.divide(np.add(pts[:-1], pts[1:]), 2.0) neurite_filter = is_type(neurite_type) return [ s for ss in iter_sections(neurites, neurite_filter=neurite_filter) for s in _seg_radii(ss) ]
def neurite_volume_density(neurites, neurite_type=NeuriteType.all): """Get the volume density per neurite The volume density is defined as the ratio of the neurite volume and the volume of the neurite's enclosung convex hull """ def vol_density(neurite): """volume density of a single neurite""" return neurite.volume / convex_hull(neurite).volume return list(vol_density(n) for n in iter_neurites(neurites, filt=is_type(neurite_type)))
def segment_radial_distances(neurites, neurite_type=NeuriteType.all, origin=None): '''Lengths of the segments in a collection of neurites''' def _seg_rd(sec, pos): '''list of radial distances of all segments of a section''' mid_pts = np.divide(np.add(sec.points[:-1], sec.points[1:])[:, :3], 2.0) return np.sqrt([morphmath.point_dist2(p, pos) for p in mid_pts]) dist = [] for n in iter_neurites(neurites, filt=is_type(neurite_type)): pos = n.root_node.points[0] if origin is None else origin dist.extend([s for ss in n.iter_sections() for s in _seg_rd(ss, pos)]) return dist
def segment_radial_distances(neurites, neurite_type=NeuriteType.all, origin=None): '''Lengths of the segments in a collection of neurites''' def _seg_rd(sec, pos): '''list of radial distances of all segments of a section''' mid_pts = np.divide(np.add(sec.points[:-1], sec.points[1:])[:, :3], 2.0) return np.sqrt([mm.point_dist2(p, pos) for p in mid_pts]) dist = [] for n in iter_neurites(neurites, filt=is_type(neurite_type)): pos = n.root_node.points[0] if origin is None else origin dist.extend([s for ss in n.iter_sections() for s in _seg_rd(ss, pos)]) return dist
def segment_radial_distances(neurites, neurite_type=NeuriteType.all, origin=None): """Returns the list of distances between all segment mid points and origin.""" def _radial_distances(sec, pos): """List of distances between the mid point of each segment and pos.""" mid_pts = 0.5 * (sec.points[:-1, COLS.XYZ] + sec.points[1:, COLS.XYZ]) return np.linalg.norm(mid_pts - pos[COLS.XYZ], axis=1) dist = [] for n in iter_neurites(neurites, filt=is_type(neurite_type)): pos = n.root_node.points[0] if origin is None else origin dist.extend([s for ss in n.iter_sections() for s in _radial_distances(ss, pos)]) return dist
def segment_taper_rates(neurites, neurite_type=NeuriteType.all): """taper rates of the segments in a collection of neurites The taper rate is defined as the absolute radii differences divided by length of the section """ def _seg_taper_rates(sec): """vectorized taper rates""" pts = sec.points[:, : COLS.TYPE] diff = np.diff(pts, axis=0) distance = np.linalg.norm(diff[:, : COLS.R], axis=1) return np.divide(2 * np.abs(diff[:, COLS.R]), distance) neurite_filter = is_type(neurite_type) return [s for ss in iter_sections(neurites, neurite_filter=neurite_filter) for s in _seg_taper_rates(ss)]
def segment_path_lengths(neurites, neurite_type=NeuriteType.all): """Returns pathlengths between all non-root points and their root point.""" pathlength = {} neurite_filter = is_type(neurite_type) def _get_pathlength(section): if section.id not in pathlength: if section.parent: pathlength[section.id] = section.parent.length + _get_pathlength(section.parent) else: pathlength[section.id] = 0 return pathlength[section.id] result = [_get_pathlength(section) + np.cumsum(sectionfunc.segment_lengths(section)) for section in iter_sections(neurites, neurite_filter=neurite_filter)] return np.hstack(result) if result else np.array([])
def section_path_lengths(neurites, neurite_type=NeuriteType.all): '''Path lengths of a collection of neurites ''' # Calculates and stores the section lengths in one pass, # then queries the lengths in the path length iterations. # This avoids repeatedly calculating the lengths of the # same sections. dist = {} neurite_filter = is_type(neurite_type) for s in iter_sections(neurites, neurite_filter=neurite_filter): dist[s] = s.length def pl2(node): '''Calculate the path length using cached section lengths''' return sum(dist[n] for n in node.iupstream()) return map_sections(pl2, neurites, neurite_type=neurite_type)
def segment_taper_rates(neurites, neurite_type=NeuriteType.all): '''taper rates of the segments in a collection of neurites The taper rate is defined as the absolute radii differences divided by length of the section ''' def _seg_taper_rates(sec): '''vectorized taper rates''' pts = sec.points[:, :COLS.TYPE] diff = np.diff(pts, axis=0) distance = np.linalg.norm(diff[:, :COLS.R], axis=1) return np.divide(2 * np.abs(diff[:, COLS.R]), distance) neurite_filter = is_type(neurite_type) return [ s for ss in iter_sections(neurites, neurite_filter=neurite_filter) for s in _seg_taper_rates(ss) ]
def section_path_lengths(neurites, neurite_type=NeuriteType.all): """Path lengths of a collection of neurites """ # Calculates and stores the section lengths in one pass, # then queries the lengths in the path length iterations. # This avoids repeatedly calculating the lengths of the # same sections. dist = {} neurite_filter = is_type(neurite_type) for s in iter_sections(neurites, neurite_filter=neurite_filter): dist[s] = s.length def pl2(node): """Calculate the path length using cached section lengths""" return sum(dist[n] for n in node.iupstream()) return map_sections(pl2, neurites, neurite_type=neurite_type)
def trunk_origin_azimuths(nrn, neurite_type=NeuriteType.all): '''Get a list of all the trunk origin azimuths of a neuron or population The azimuth is defined as Angle between x-axis and the vector defined by (initial tree point - soma center) on the x-z plane. The range of the azimuth angle [-pi, pi] radians ''' neurite_filter = is_type(neurite_type) nrns = nrn.neurons if hasattr(nrn, 'neurons') else [nrn] def _azimuth(section, soma): '''Azimuth of a section''' vector = mm.vector(section[0], soma.center) return np.arctan2(vector[COLS.Z], vector[COLS.X]) return [_azimuth(s.root_node.points, n.soma) for n in nrns for s in n.neurites if neurite_filter(s)]
def segment_path_lengths(neurites, neurite_type=NeuriteType.all): '''Returns pathlengths between all non-root points and their root point''' pathlength = {} neurite_filter = is_type(neurite_type) def _get_pathlength(section): if section.id not in pathlength: if section.parent: pathlength[ section.id] = section.parent.length + _get_pathlength( section.parent) else: pathlength[section.id] = 0 return pathlength[section.id] return np.hstack([ _get_pathlength(section) + sectionfunc.segment_lengths(section) for section in iter_sections(neurites, neurite_filter=neurite_filter) ])
def trunk_origin_azimuths(nrn, neurite_type=NeuriteType.all): """Get a list of all the trunk origin azimuths of a neuron or population. The azimuth is defined as Angle between x-axis and the vector defined by (initial tree point - soma center) on the x-z plane. The range of the azimuth angle [-pi, pi] radians """ neurite_filter = is_type(neurite_type) nrns = neuron_population(nrn) def _azimuth(section, soma): """Azimuth of a section.""" vector = morphmath.vector(section[0], soma.center) return np.arctan2(vector[COLS.Z], vector[COLS.X]) return [_azimuth(s.root_node.points, n.soma) for n in nrns for s in n.neurites if neurite_filter(s)]
def partition_asymmetries(neurites, neurite_type=NeuriteType.all): '''Partition asymmetry at bifurcation points of a collection of neurites''' return map(_bifurcationfunc.partition_asymmetry, iter_sections(neurites, iterator_type=Tree.ibifurcation_point, neurite_filter=is_type(neurite_type)))
def trunk_origin_radii(nrn, neurite_type=NeuriteType.all): '''radii of the trunk sections of neurites in a neuron''' neurite_filter = is_type(neurite_type) return [s.root_node.points[0][COLS.R] for s in nrn.neurites if neurite_filter(s)]
def total_area_per_neurite(neurites, neurite_type=NeuriteType.all): '''Surface area in a collection of neurites. The area is defined as the sum of the area of the sections. ''' return [neurite.area for neurite in iter_neurites(neurites, filt=is_type(neurite_type))]
def trunk_section_lengths(nrn, neurite_type=NeuriteType.all): '''list of lengths of trunk sections of neurites in a neuron''' neurite_filter = is_type(neurite_type) return [mm.section_length(s.root_node.points) for s in nrn.neurites if neurite_filter(s)]
def bifurcation_partitions(neurites, neurite_type=NeuriteType.all): """Partition at bifurcation points of a collection of neurites""" return map( _bifurcationfunc.bifurcation_partition, iter_sections(neurites, iterator_type=Tree.ibifurcation_point, neurite_filter=is_type(neurite_type)), )
def map_sections(fun, neurites, neurite_type=NeuriteType.all, iterator_type=Tree.ipreorder): """Map `fun` to all the sections in a collection of neurites""" return map(fun, iter_sections(neurites, iterator_type=iterator_type, neurite_filter=is_type(neurite_type)))
def n_sections(neurites, neurite_type=NeuriteType.all, iterator_type=Tree.ipreorder): """Number of sections in a collection of neurites""" return sum(1 for _ in iter_sections(neurites, iterator_type=iterator_type, neurite_filter=is_type(neurite_type)))
def total_length_per_neurite(neurites, neurite_type=NeuriteType.all): """Get the path length per neurite in a collection""" return list(sum(s.length for s in n.iter_sections()) for n in iter_neurites(neurites, filt=is_type(neurite_type)))
def total_volume_per_neurite(neurites, neurite_type=NeuriteType.all): """Get the volume per neurite in a collection""" return list(sum(s.volume for s in n.iter_sections()) for n in iter_neurites(neurites, filt=is_type(neurite_type)))
def n_segments(neurites, neurite_type=NeuriteType.all): """Number of segments in a collection of neurites""" return sum(len(s.points) - 1 for s in iter_sections(neurites, neurite_filter=is_type(neurite_type)))
def n_neurites(neurites, neurite_type=NeuriteType.all): """Number of neurites in a collection of neurites""" return sum(1 for _ in iter_neurites(neurites, filt=is_type(neurite_type)))
def number_of_sections_per_neurite(neurites, neurite_type=NeuriteType.all): """Get the number of sections per neurite in a collection of neurites""" return list(sum(1 for _ in n.iter_sections()) for n in iter_neurites(neurites, filt=is_type(neurite_type)))
def bifurcation_partitions(neurites, neurite_type=NeuriteType.all): '''Partition at bifurcation points of a collection of neurites''' return map(bifurcation_partition, iter_sections(neurites, iterator_type=Tree.ibifurcation_point, neurite_filter=is_type(neurite_type)))