# A few tools for downloading the data
from io import StringIO
import requests
from neuron_morphology.swc_io import morphology_from_swc
from neuron_morphology.feature_extractor.data import Data
def data_from_url(morphology_url):
morphology_swc = StringIO(requests.get(morphology_url).text)
# Feature functions expect a Data object - in this case just a wrapper for a Morphology
# If we were working with additional data (say, layer annotations) we would store these here as well
return Data(morphology_from_swc(morphology_swc))
# fetch a published reconstruction
test_data = data_from_url("http://celltypes.brain-map.org/api/v2/well_known_file_download/491120375")
more_test_data = data_from_url("http://celltypes.brain-map.org/api/v2/well_known_file_download/692297222")
import matplotlib.pyplot as plt
nodes = test_data.morphology.nodes()
x = [node['x'] for node in nodes]
y = [node['y'] for node in nodes]
z = [node['z'] for node in nodes]
fig, ax = plt.subplots(1, 2)
ax[0].scatter(x, y, s=0.1)
ax[1].scatter(z, y, s=0.1)
<matplotlib.collections.PathCollection at 0x7fdf773e3a50>
See the features subpackage for the full list.
from neuron_morphology.features.branching.bifurcations import num_outer_bifurcations
num_outer_bifurcations
Signature: num_outer_bifurcations(data: neuron_morphology.feature_extractor.data.Data, node_types: Union[List[int], NoneType] = None) -> int Marks: ['RequiresRoot', 'BifurcationFeatures'] Requires: frozenset() Help: Feature Extractor interface to calculate_outer_bifurcations. Returns the number of bifurcations (branch points), excluding those too close to the root (threshold is 1/2 the max distance from the root to any node). Parameters ---------- data : Holds a morphology object. No additional data is required node_types : Restrict included nodes to these types. See neuron_morphology.constants for avaiable node types.
# we can call this just like any function
num_outer_bifurcations(test_data)
6
You can look in default features for a list of the features that are run by default.
from neuron_morphology.feature_extractor.feature_extractor import FeatureExtractor
from neuron_morphology.features.dimension import dimension
from neuron_morphology.features.intrinsic import num_branches, num_tips
# a utility for flattening outputs
from neuron_morphology.feature_extractor.utilities import unnest
features = [
num_outer_bifurcations,
dimension,
num_branches,
num_tips
]
# make a pipeline
results = (
FeatureExtractor()
.register_features(features)
.extract(test_data)
.results
)
unnest(results)
/home/nile/Desktop/neuron_morphology/neuron_morphology/feature_extractor/mark.py:118: UserWarning: This morphology is not uniquely rooted! Found 7 root nodes. Features using the root node of this morphology may not select that node consistently. Some or all of these root nodes may not be soma nodes. f"This morphology is not uniquely rooted! Found {num_roots} "
{'num_outer_bifurcations': 6, 'dimension.width': 598.6509, 'dimension.height': 602.6546, 'dimension.depth': 71.5631, 'dimension.min_xyz': array([-252.2122, -220.8229, -16.1489]), 'dimension.max_xyz': array([346.4387, 381.8317, 55.4142]), 'dimension.bias_xyz': array([ 94.2265, 161.0088, 39.2653]), 'num_branches': 234, 'num_tips': 122}
... or other parameters
from neuron_morphology.feature_extractor.feature_specialization import NEURITE_SPECIALIZATIONS, AxonSpec
from neuron_morphology.feature_extractor.marked_feature import specialize
print(f"marks: {AxonSpec.marks}")
print(f"kwargs: {AxonSpec.kwargs}")
marks: {<class 'neuron_morphology.feature_extractor.mark.RequiresAxon'>} kwargs: {'node_types': [2]}
axon_num_branches = specialize(num_branches, {AxonSpec}) # note that this is a set!
unnest(
FeatureExtractor()
.register_features([axon_num_branches])
.extract(test_data)
.results
)
{'axon.num_branches': 205}
Running on an axon is not super interesting, we can pass in a whole set of specializations, such as the built-in NEURITE_SPECIALIZATIONS to distribute a calculation across many parameter values.
NEURITE_SPECIALIZATIONS
{neuron_morphology.feature_extractor.feature_specialization.AllNeuriteSpec, neuron_morphology.feature_extractor.feature_specialization.ApicalDendriteSpec, neuron_morphology.feature_extractor.feature_specialization.AxonSpec, neuron_morphology.feature_extractor.feature_specialization.BasalDendriteSpec, neuron_morphology.feature_extractor.feature_specialization.DendriteSpec}
across_neurites = [specialize(feature, NEURITE_SPECIALIZATIONS) for feature in features]
# this syntax is (almost) equivalent to the above pipeline, but we keep the extractor around
extractor = FeatureExtractor()
extractor.register_features(across_neurites)
run = extractor.extract(test_data)
across_neurite_results = run.results
unnest(across_neurite_results)
{'all_neurites.num_outer_bifurcations': 6, 'dendrite.num_outer_bifurcations': 2, 'basal_dendrite.num_outer_bifurcations': 2, 'axon.num_outer_bifurcations': 4, 'all_neurites.dimension.width': 598.6509, 'all_neurites.dimension.height': 602.6546, 'all_neurites.dimension.depth': 71.5631, 'all_neurites.dimension.min_xyz': array([-252.2122, -220.8229, -16.1489]), 'all_neurites.dimension.max_xyz': array([346.4387, 381.8317, 55.4142]), 'all_neurites.dimension.bias_xyz': array([ 94.2265, 161.0088, 39.2653]), 'dendrite.dimension.width': 384.2443999999999, 'dendrite.dimension.height': 209.35199999999998, 'dendrite.dimension.depth': 58.8211, 'dendrite.dimension.min_xyz': array([ -86.8776, -151.2368, -15.5191]), 'dendrite.dimension.max_xyz': array([297.3668, 58.1152, 43.302 ]), 'dendrite.dimension.bias_xyz': array([210.4892, 93.1216, 27.7829]), 'basal_dendrite.dimension.width': 384.2443999999999, 'basal_dendrite.dimension.height': 209.35199999999998, 'basal_dendrite.dimension.depth': 58.8211, 'basal_dendrite.dimension.min_xyz': array([ -86.8776, -151.2368, -15.5191]), 'basal_dendrite.dimension.max_xyz': array([297.3668, 58.1152, 43.302 ]), 'basal_dendrite.dimension.bias_xyz': array([210.4892, 93.1216, 27.7829]), 'axon.dimension.width': 598.6509, 'axon.dimension.height': 602.6546, 'axon.dimension.depth': 71.5631, 'axon.dimension.min_xyz': array([-252.2122, -220.8229, -16.1489]), 'axon.dimension.max_xyz': array([346.4387, 381.8317, 55.4142]), 'axon.dimension.bias_xyz': array([ 94.2265, 161.0088, 39.2653]), 'all_neurites.num_branches': 234, 'dendrite.num_branches': 28, 'basal_dendrite.num_branches': 28, 'axon.num_branches': 205, 'all_neurites.num_tips': 122, 'dendrite.num_tips': 16, 'basal_dendrite.num_tips': 16, 'axon.num_tips': 106}
# ... by keeping the extractor we can re-use it on new data
run_two = extractor.extract(more_test_data)
unnest(run_two.results)
{'all_neurites.num_outer_bifurcations': 7, 'dendrite.num_outer_bifurcations': 1, 'basal_dendrite.num_outer_bifurcations': 1, 'axon.num_outer_bifurcations': 7, 'all_neurites.dimension.width': 301.5069, 'all_neurites.dimension.height': 513.0175999999999, 'all_neurites.dimension.depth': 96.85220000000001, 'all_neurites.dimension.min_xyz': array([-172.847 , -360.2776, -22.221 ]), 'all_neurites.dimension.max_xyz': array([128.6599, 152.74 , 74.6312]), 'all_neurites.dimension.bias_xyz': array([ 44.1871, 207.5376, 52.4102]), 'dendrite.dimension.width': 196.23379999999997, 'dendrite.dimension.height': 148.10680000000002, 'dendrite.dimension.depth': 51.94, 'dendrite.dimension.min_xyz': array([-156.0119, -62.992 , -14.3438]), 'dendrite.dimension.max_xyz': array([40.2219, 85.1148, 37.5962]), 'dendrite.dimension.bias_xyz': array([115.79 , 22.1228, 23.2524]), 'basal_dendrite.dimension.width': 196.23379999999997, 'basal_dendrite.dimension.height': 148.10680000000002, 'basal_dendrite.dimension.depth': 51.94, 'basal_dendrite.dimension.min_xyz': array([-156.0119, -62.992 , -14.3438]), 'basal_dendrite.dimension.max_xyz': array([40.2219, 85.1148, 37.5962]), 'basal_dendrite.dimension.bias_xyz': array([115.79 , 22.1228, 23.2524]), 'axon.dimension.width': 301.5069, 'axon.dimension.height': 513.0175999999999, 'axon.dimension.depth': 96.85220000000001, 'axon.dimension.min_xyz': array([-172.847 , -360.2776, -22.221 ]), 'axon.dimension.max_xyz': array([128.6599, 152.74 , 74.6312]), 'axon.dimension.bias_xyz': array([ 44.1871, 207.5376, 52.4102]), 'all_neurites.num_branches': 183, 'dendrite.num_branches': 36, 'basal_dendrite.num_branches': 36, 'axon.num_branches': 147, 'all_neurites.num_tips': 96, 'dendrite.num_tips': 22, 'basal_dendrite.num_tips': 22, 'axon.num_tips': 74}
from neuron_morphology.feature_extractor.mark import Mark
from neuron_morphology.feature_extractor.marked_feature import marked
IsGlobal = Mark.factory("IsGlobal")
IsTrivial = Mark.factory("IsTrivial")
class DoesMath(Mark):
""" This feature involves math! Documenting your marks can be handy.
"""
@marked(IsGlobal)
@marked(IsTrivial)
def num_nodes(data: Data) -> int:
"""Count the nodes!
"""
return len(data.morphology.nodes)
num_nodes
Signature: num_nodes(data: neuron_morphology.feature_extractor.data.Data) -> int Marks: ['IsGlobal', 'IsTrivial'] Requires: frozenset() Help: Count the nodes!
# we can also mark an existing function
num_nodes = marked(DoesMath)(num_nodes)
num_nodes
Signature: num_nodes(data: neuron_morphology.feature_extractor.data.Data) -> int Marks: ['IsGlobal', 'DoesMath', 'IsTrivial'] Requires: frozenset() Help: Count the nodes!