BlueBrain / NeuroM

Neuronal Morphology Analysis Tool
https://neurom.readthedocs.io/
BSD 3-Clause "New" or "Revised" License
101 stars 55 forks source link

fst module single neurite features #394

Closed eleftherioszisis closed 8 years ago

eleftherioszisis commented 8 years ago

This is a request to permit the extraction of features from single neurites from the fst module. This can be achieved by either modifying fst.get or adding another getter for the neurites.

from neurom.fst import load_neuron, get
n = load_neuron('test_data/valid_set/Neuron.swc')
get('partition', n.neurites[0])
AttributeError: 'Tree' object has no attribute 'neurites'
juanchopanza commented 8 years ago

Is the this a request that fst.get work for single neurites? If so could you edit your post to specify that explicitly?

juanchopanza commented 8 years ago

Actually I don't think it would be a good idea to implement this. Currently, the get method takes as 2nd argument a neuron or population (actually anything that has a neurites and / or a soma attribute with certain properties.) To allow passing single neurite directly would mean some get methods wouldn't work (e.g. the neuron or soma properties.) I think it is better to keep this uniformity and get the single neurite functionality by other means.

eleftherioszisis commented 8 years ago

When a user is performing analysis on neurons and single neurites might need to be analyzed as well, because the features do change in the biological cells depending on the level of abstraction. Therefore, it is essential to be able to apply the neurite features on neurites as well.

To give you an example, I was analysing the reconstructed astrocytes by checking how features such as bif angles, radial distances, partition etc. behave on different trees. The behavior is different because the shape of the neurites is radically different within the same neuron. In order to do this, I had to create a dummy class and change the attribute neurites everytime I wanted to check another single neurite. This is not a viable solution for users.

When I created the features module I did separate the features that can apply on single neurites (and by extension to neurons and populations due to their attribue neurites. NEURITEFEATURES), and the features that can apply only on neurons and populations (NEURONFEATURES). I did this in order to permit the analysis of all structures that we have available and can extract features from. Why this was removed in the fst module?

If you believe that it would be cleaner and easier to implement, we can have two getters, one for neuron and one for neurite features. That would be fine as well, as long as the user will be able to import a simple function to extract features.

juanchopanza commented 8 years ago

NEURITEFEATURES and NEURONFEATURES are also present in neurom.fst. The point is that all the features available through get should work for a given input. Otherwise the interface becomes brittle and harder to use. One option would be to add two more getters, as you suggest in the last paragraph. Or we can add more per_neurite features. Or a get_per_neurite function. I think that would be clear. It could accept a neuron, population, list of neurites, or a single neurite.

eleftherioszisis commented 8 years ago

In the case that get will only work with objects that have neurites (i.e. neuron, population), then I think we should join the neuron and neurite features, because the only reason that I had them separated was for the user to check which feature can only be applied on neurons.

How about adding a boolean per_neurite flag that can be passed in the get function, and transform any feature into a per_neurite one? In the case of neuron features such as soma_radii, the flag can be ignored.

I am unsure of the work needed to implement the aforementioned flag functionality, but I am afraid that having a duplicate per_neurite feature for each feature will lead to unnecessary duplication of code.

juanchopanza commented 8 years ago

Yes, a per_neurite flag could work. I'm not a big fan of flags that get ignored in some situations, but I can live with it here.

juanchopanza commented 8 years ago

But wait, this is a separate issue. I still wouldn't want get to accept single neurites.

juanchopanza commented 8 years ago

Bah, forget it. I will just allow get to take individual neurites and somas and then barf if the input doesn't match the feature.

eleftherioszisis commented 8 years ago

Haha, whatever is simpler to implement. :P

eleftherioszisis commented 8 years ago

I have a suggestion. In order to make the names of the features directly available to the users through the get function docstring in a dynamic way , something like this can be added in the end of the fst.__init__.py file:

get.__doc__ += '\nFeatures Applicable to neurites, neurons:\n\t'
get.__doc__ += '\n\t'.join(NEURITEFEATURES)
get.__doc__ += '\nFeatures Applicable only to neurons:\n\t'
get.__doc__ += '\n\t'.join(NEURONFEATURES)

(join, directly calls dictionary's __iter__ which returns a tuple of the keys) This will provide a docstring of the form:

Neuron feature getter helper

    Returns features as a 1D numpy array.

Features Applicable to neurites, neurons:
        section_branch_orders
        section_lengths
        number_of_neurites
        segment_radial_distances
        section_path_distances
        segment_midpoints
        trunk_origin_elevations
        segment_radii
        total_length
        section_tortuosity
        section_volumes
        number_of_bifurcations
        number_of_sections
        remote_bifurcation_angles
        principal_direction_extents
        trunk_section_lengths
        trunk_origin_azimuths
        trunk_origin_radii
        partition
        section_areas
        segment_lengths
        number_of_segments
        total_length_per_neurite
        number_of_sections_per_neurite
        segment_taper_rates
        section_radial_distances
        local_bifurcation_angles
Features Applicable only to neurons:
        soma_radii
        soma_surface_areas

Probably there is a more elegant way to do this, but I wanted to present the idea. @juanchopanza What do you think?

eleftherioszisis commented 8 years ago

Or even in a sorted way:

get.__doc__ += '\nFeatures Applicable to neurites, neurons:\n\t'
get.__doc__ += '\n\t'.join(sorted(NEURITEFEATURES))
get.__doc__ += '\nFeatures Applicable only to neurons:\n\t'
get.__doc__ += '\n\t'.join(sorted(NEURONFEATURES))
lidakanari commented 8 years ago

That would be useful indeed!

juanchopanza commented 8 years ago

Good idea. I'm on it!