scidash / neuronunit

A package for data-driven validation of neuron and ion channel models using SciUnit
http://neuronunit.scidash.org
38 stars 24 forks source link

Need a list of biological neurons (neurolex ids) to optimize against. #177

Open russelljjarvis opened 6 years ago

russelljjarvis commented 6 years ago

The optimization branch of this project needs to demonstrate it's utility and robustness by finding the Izhikevich model parameters that best match experimentally derived properties from biological neurons. Need a list of biological neurons (neurolex ids) to optimize against.

rgerkin commented 6 years ago

Neurolex appears to be deprecated in favor of SciCrunch's interlex, so how about:
CA1 Pyramidal Cell Cerebellar Purkinje Cell Layer V Pyramidal Cell Olfactory Bulb Mitral Cell

russelljjarvis commented 6 years ago

Great I appreciate the Sci-crunch-lex links.

russelljjarvis commented 6 years ago

In the file pipe_entry_point: https://github.com/russelljjarvis/neuronunit/blob/dev/neuronunit/unit_test/pipe_entry_point.py#L10-#L23 I create a list of neuro electro ids corresponding to neuroelectro observations I want to obtain.

fi_basket = {'nlex_id':'NLXCELL:100201'}
pvis_cortex = {'nlex_id': 'nifext_50'} # Layer V pyramidal cell
olf_mitral = { 'nlex_id':'NLXWIKI:nifext_120'}
purkinje = { 'nlex_id':'SAO:471801888'}#'NLXWIKI:sao471801888'} # purkinje
ca1_pyr = { 'nlex_id':'SAO:830368389'}
pipe = [ fi_basket, pvis_cortex, olf_mitral, purkinje, ca1_pyr ]

The function get_neuron_criteria, then makes the neuroelectro queries: https://github.com/russelljjarvis/neuronunit/blob/dev/neuronunit/optimization/get_neab.py#L26-#L63

However of the four cell types, only two out of 4 sets of observations are unique.

dict_items([('Spike Half-Width', {'mean': array(1.505) * ms, 'std': array(0.0) * ms, 'n': 1}), ('Spike Threshold', {'mean': array(-41.4742424242424) * mV, 'std': array(5.49222298481843) * mV, 'n': 11}), ('Membrane Time Constant', {'mean': array(23.186875) * ms, 'std': array(8.77960968022798) * ms, 'n': 8}), ('Rheobase', {'mean': array(172.133333333333) * pA, 'std': array(47.690512683342) * pA, 'n': 4}), ('Resting membrane potential', {'mean': array(-57.9) * mV, 'std': array(0.0) * mV, 'n': 1}), ('Input Resistance', {'mean': array(457000000.0) * ohm, 'std': array(0.0) * ohm, 'n': 1}), ('Spike Amplitude', {'mean': array(64.0) * mV, 'std': array(0.0) * mV, 'n': 1}), ('Cell Capacitance', {'mean': array(6.865e-11) * F, 'std': array(1.8649999999999998e-11) * F, 'n': 2})])
dict_items([('Spike Half-Width', {'mean': array(1.20769387755102) * ms, 'std': array(0.534345918375033) * ms, 'n': 49}), ('Spike Threshold', {'mean': array(-42.7357232704403) * mV, 'std': array(8.04073233409085) * mV, 'n': 53}), ('Membrane Time Constant', {'mean': array(15.7342424242424) * ms, 'std': array(7.31162636832495) * ms, 'n': 33}), ('Rheobase', {'mean': array(213.849583333333) * pA, 'std': array(170.452454715608) * pA, 'n': 32}), ('Resting membrane potential', {'mean': array(-68.2481434599156) * mV, 'std': array(6.53234788156637) * mV, 'n': 79}), ('Input Resistance', {'mean': array(120672073.643411) * ohm, 'std': array(77633160.8333564) * ohm, 'n': 86}), ('Spike Amplitude', {'mean': array(80.4351020408164) * mV, 'std': array(12.7488030357545) * mV, 'n': 49}), ('Cell Capacitance', {'mean': array(1.50584166666667e-10) * F, 'std': array(1.39683884626343e-10) * F, 'n': 12})])
dict_items([('Spike Half-Width', {'mean': array(1.505) * ms, 'std': array(0.0) * ms, 'n': 1}), ('Spike Threshold', {'mean': array(-41.4742424242424) * mV, 'std': array(5.49222298481843) * mV, 'n': 11}), ('Membrane Time Constant', {'mean': array(23.186875) * ms, 'std': array(8.77960968022798) * ms, 'n': 8}), ('Rheobase', {'mean': array(172.133333333333) * pA, 'std': array(47.690512683342) * pA, 'n': 4}), ('Resting membrane potential', {'mean': array(-57.9) * mV, 'std': array(0.0) * mV, 'n': 1}), ('Input Resistance', {'mean': array(457000000.0) * ohm, 'std': array(0.0) * ohm, 'n': 1}), ('Spike Amplitude', {'mean': array(64.0) * mV, 'std': array(0.0) * mV, 'n': 1}), ('Cell Capacitance', {'mean': array(6.865e-11) * F, 'std': array(1.8649999999999998e-11) * F, 'n': 2})])
dict_items([('Spike Half-Width', {'mean': array(1.505) * ms, 'std': array(0.0) * ms, 'n': 1}), ('Spike Threshold', {'mean': array(-41.4742424242424) * mV, 'std': array(5.49222298481843) * mV, 'n': 11}), ('Membrane Time Constant', {'mean': array(23.186875) * ms, 'std': array(8.77960968022798) * ms, 'n': 8}), ('Rheobase', {'mean': array(172.133333333333) * pA, 'std': array(47.690512683342) * pA, 'n': 4}), ('Resting membrane potential', {'mean': array(-57.9) * mV, 'std': array(0.0) * mV, 'n': 1}), ('Input Resistance', {'mean': array(457000000.0) * ohm, 'std': array(0.0) * ohm, 'n': 1}), ('Spike Amplitude', {'mean': array(64.0) * mV, 'std': array(0.0) * mV, 'n': 1}), ('Cell Capacitance', {'mean': array(6.865e-11) * F, 'std': array(1.8649999999999998e-11) * F, 'n': 2})])
dict_items([('Spike Half-Width', {'mean': array(1.505) * ms, 'std': array(0.0) * ms, 'n': 1}), ('Spike Threshold', {'mean': array(-41.4742424242424) * mV, 'std': array(5.49222298481843) * mV, 'n': 11}), ('Membrane Time Constant', {'mean': array(23.186875) * ms, 'std': array(8.77960968022798) * ms, 'n': 8}), ('Rheobase', {'mean': array(172.133333333333) * pA, 'std': array(47.690512683342) * pA, 'n': 4}), ('Resting membrane potential', {'mean': array(-57.9) * mV, 'std': array(0.0) * mV, 'n': 1}), ('Input Resistance', {'mean': array(457000000.0) * ohm, 'std': array(0.0) * ohm, 'n': 1}), ('Spike Amplitude', {'mean': array(64.0) * mV, 'std': array(0.0) * mV, 'n': 1}), ('Cell Capacitance', {'mean': array(6.865e-11) * F, 'std': array(1.8649999999999998e-11) * F, 'n': 2})])

The links visited by neuroelectro look unique however, examples below: https://www.neuroelectro.org/api/1/nes/?nlex=NLXCELL%253A100201&e__name=Spike+Amplitude https://www.neuroelectro.org/api/1/nes/?nlex=nifext_50&e__name=Spike+Amplitude https://www.neuroelectro.org/api/1/nes/?nlex=NLXWIKI%253Anifext_120&e__name=Spike+Amplitude https://www.neuroelectro.org/api/1/nes/?nlex=SAO%253A471801888&e__name=Spike+Amplitude https://www.neuroelectro.org/api/1/nes/?nlex=SAO%253A830368389&e__name=Spike+Amplitude

I wonder if the problem might be that the neurolex id's (now scicrunch ids) are not preferred? Rather than returning an error neuroelectro returns a default observation dictionary?

rgerkin commented 6 years ago

@russelljjarvis NeuroElectro still doesn't know about the SciCrunch IDs, so I think you still have to give the actual NeuroLex IDs to the NeuroElectro API. In the example above, only one of those is actually a valid NeuroLex ID (nifext_50), and the others are not; I think for some reason if the NeuroElectro API doesn't find a match by NeuroLex ID it just returns a list of all neurons. The NeuronUnit interface to the API then just picks one from the list (probably the first one), which is the same in all 4 cases.

Solution: 1) @stripathy The NeuroElectro API shouldn't return a list of stuff if the NeuroLex ID isn't a match.
2) The NeuronUnit interface should check to see if multiple neurons have been returned, and raise and error if so. A "correct" API response would look like the one for nifext_50.
3) Fix the Neurolex IDs you are using in your API calls to actually be NeuroLex IDs and not SciCrunch IDs.

russelljjarvis commented 6 years ago

It's difficult to figure out how to find neurolex id's now without the actual neurolex.org website. occassionaly scicrunch gives identifying keys like: NLXCELL and NLXWIKI, but not obvious nlex_id's. Perhaps there is a creative way of obtaining them.

As you note above, the URLs above for invalid keys, can return a list of all neurons. There are only 40 nlex_id's in this whole document. Perhaps I can find the id's I need by wading through it.

rgerkin commented 6 years ago

For all of the ones in the first set of links I posted, the neurolex id is the string following "NLXWIKI:", i.e. sao471801888 for the Purkinje cell. I assume this pattern will work for all cells.

russelljjarvis commented 6 years ago

Ironically, this approach described may work for all neurons except the purkinje neuron (I write may, as the code below executes, which does not necessarily mean the program is acting on the right data, I will confirm soon if the data is correct):

purkinje = { 'nlex_id':'sao471801888'}#'NLXWIKI:sao471801888'} # purkinje

fi_basket = {'nlex_id':'100201'} pvis_cortex = {'nlex_id':'nifext_50'} # Layer V pyramidal cell olf_mitral = { 'nlex_id':'nifext_120'} ca1_pyr = { 'nlex_id':'830368389'}



{'nlex_id': 'sao471801888'}
Getting Rheobase data values from neuroelectro.org
http://www.neuroelectro.org/api/1/nes/?e__name=Rheobase&nlex=sao471801888
Getting Input Resistance data values from neuroelectro.org
http://www.neuroelectro.org/api/1/nes/?e__name=Input+Resistance&nlex=sao471801888
Getting Membrane Time Constant data values from neuroelectro.org
http://www.neuroelectro.org/api/1/nes/?e__name=Membrane+Time+Constant&nlex=sao471801888
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
~/neuronunit/neuronunit/unit_test/pipe_entry_point.py in <module>()
     65 try:
---> 66     #assert 1==2
     67     assert os.path.isfile(electro_path) == True

AssertionError: 

During handling of the above exception, another exception occurred:

AttributeError                            Traceback (most recent call last)
~/neuronunit/neuronunit/unit_test/pipe_entry_point.py in <module>()
     74     for p in pipe:
     75        print(p)
---> 76        p_tests, p_observations = get_neab.get_neuron_criteria(p)
     77        electro_tests.append((p_tests, p_observations))
     78     for i in electro_tests: print(str(i[1].items()) +str('\n'))

~/neuronunit/neuronunit/optimization/get_neab.py in get_neuron_criteria(cell_id, file_name)
     55     observations = {}
     56     for index, t in enumerate(test_classes):
---> 57         obs = t.neuroelectro_summary_observation(cell_id)
     58         tests.append(t(obs))
     59         observations[t.ephysprop_name] = obs

~/neuronunit/neuronunit/tests/base.py in neuroelectro_summary_observation(cls, neuron, cached)
    128 
    129 
--> 130         observation = {'mean': reference_data.mean*cls.units,
    131                        'std': reference_data.std*cls.units,
    132                        'n': reference_data.n}

AttributeError: 'NeuroElectroSummary' object has no attribute 'mean'

In [1]: 

In [1]: quit()
rgerkin commented 6 years ago

There are no Time Constant values for the Purkinje cell in neuroelectro (I just confirmed it manually). neuroelectro_summary_observation should be patched to check if reference_data is empty (e.g. has no mean), which is what happens when that data cannot be found on neuroelectro. The others probably work.

russelljjarvis commented 6 years ago

The others are at least not yet causing syntax problems.

russelljjarvis commented 6 years ago

These 5 NE keys

purkinje = { 'nlex_id':'sao471801888'}#'NLXWIKI:sao471801888'} # purkinje
fi_basket = {'nlex_id':'100201'}
pvis_cortex = {'nlex_id':'nifext_50'} # Layer V pyramidal cell
olf_mitral = { 'nlex_id':'nifext_120'}
ca1_pyr = { 'nlex_id':'830368389'}
pipe = [ fi_basket, pvis_cortex, olf_mitral, ca1_pyr, purkinje ]

lead only to 3 unique neuroelectro data sets.

(Pdb) electro_tests[1]
([<neuronunit.tests.fi.RheobaseTest object at 0x7f4585f17588>, <neuronunit.tests.passive.InputResistanceTest object at 0x7f4585f17630>, <neuronunit.tests.passive.TimeConstantTest object at 0x7f4585f17048>, <neuronunit.tests.passive.CapacitanceTest object at 0x7f4585f17dd8>, <neuronunit.tests.passive.RestingPotentialTest object at 0x7f457482b780>, <neuronunit.tests.waveform.InjectedCurrentAPWidthTest object at 0x7f454dab9128>, <neuronunit.tests.waveform.InjectedCurrentAPAmplitudeTest object at 0x7f454dab9208>, <neuronunit.tests.waveform.InjectedCurrentAPThresholdTest object at 0x7f4585f175f8>], {'Resting membrane potential': {'std': array(6.53234788156637) * mV, 'mean': array(-68.2481434599156) * mV, 'n': 79}, 'Spike Half-Width': {'std': array(0.534345918375033) * ms, 'mean': array(1.20769387755102) * ms, 'n': 49}, 'Membrane Time Constant': {'std': array(7.31162636832495) * ms, 'mean': array(15.7342424242424) * ms, 'n': 33}, 'Spike Amplitude': {'std': array(12.7488030357545) * mV, 'mean': array(80.4351020408164) * mV, 'n': 49}, 'Cell Capacitance': {'std': array(1.39683884626343e-10) * F, 'mean': array(1.50584166666667e-10) * F, 'n': 12}, 'Input Resistance': {'std': array(77633160.8333564) * ohm, 'mean': array(120672073.643411) * ohm, 'n': 86}, 'Rheobase': {'std': array(170.452454715608) * pA, 'mean': array(213.849583333333) * pA, 'n': 32}, 'Spike Threshold': {'std': array(8.04073233409085) * mV, 'mean': array(-42.7357232704403) * mV, 'n': 53}})
(Pdb) electro_tests[0]
([<neuronunit.tests.fi.RheobaseTest object at 0x7f4596ec9ef0>, <neuronunit.tests.passive.InputResistanceTest object at 0x7f45875c3f98>, <neuronunit.tests.passive.TimeConstantTest object at 0x7f45875af0b8>, <neuronunit.tests.passive.CapacitanceTest object at 0x7f45875af048>, <neuronunit.tests.passive.RestingPotentialTest object at 0x7f4585f17978>, <neuronunit.tests.waveform.InjectedCurrentAPWidthTest object at 0x7f4585f17128>, <neuronunit.tests.waveform.InjectedCurrentAPAmplitudeTest object at 0x7f45747af3c8>, <neuronunit.tests.waveform.InjectedCurrentAPThresholdTest object at 0x7f4585f17240>], {'Resting membrane potential': {'std': array(0.0) * mV, 'mean': array(-57.9) * mV, 'n': 1}, 'Spike Half-Width': {'std': array(0.0) * ms, 'mean': array(1.505) * ms, 'n': 1}, 'Membrane Time Constant': {'std': array(8.77960968022798) * ms, 'mean': array(23.186875) * ms, 'n': 8}, 'Spike Amplitude': {'std': array(0.0) * mV, 'mean': array(64.0) * mV, 'n': 1}, 'Cell Capacitance': {'std': array(1.8649999999999998e-11) * F, 'mean': array(6.865e-11) * F, 'n': 2}, 'Input Resistance': {'std': array(0.0) * ohm, 'mean': array(457000000.0) * ohm, 'n': 1}, 'Rheobase': {'std': array(47.690512683342) * pA, 'mean': array(172.133333333333) * pA, 'n': 4}, 'Spike Threshold': {'std': array(5.49222298481843) * mV, 'mean': array(-41.4742424242424) * mV, 'n': 11}})
(Pdb) electro_tests[2]
([<neuronunit.tests.fi.RheobaseTest object at 0x7f454dab9390>, <neuronunit.tests.passive.InputResistanceTest object at 0x7f4585f17390>, <neuronunit.tests.passive.TimeConstantTest object at 0x7f4585f17ba8>, <neuronunit.tests.passive.CapacitanceTest object at 0x7f4585f08780>, <neuronunit.tests.passive.RestingPotentialTest object at 0x7f454dab9ef0>, <neuronunit.tests.waveform.InjectedCurrentAPWidthTest object at 0x7f454dab92e8>, <neuronunit.tests.waveform.InjectedCurrentAPAmplitudeTest object at 0x7f4585f17320>, <neuronunit.tests.waveform.InjectedCurrentAPThresholdTest object at 0x7f4585f17eb8>], {'Resting membrane potential': {'std': array(0.0) * mV, 'mean': array(-57.9) * mV, 'n': 1}, 'Spike Half-Width': {'std': array(0.0) * ms, 'mean': array(1.505) * ms, 'n': 1}, 'Membrane Time Constant': {'std': array(8.77960968022798) * ms, 'mean': array(23.186875) * ms, 'n': 8}, 'Spike Amplitude': {'std': array(0.0) * mV, 'mean': array(64.0) * mV, 'n': 1}, 'Cell Capacitance': {'std': array(1.8649999999999998e-11) * F, 'mean': array(6.865e-11) * F, 'n': 2}, 'Input Resistance': {'std': array(0.0) * ohm, 'mean': array(457000000.0) * ohm, 'n': 1}, 'Rheobase': {'std': array(47.690512683342) * pA, 'mean': array(172.133333333333) * pA, 'n': 4}, 'Spike Threshold': {'std': array(5.49222298481843) * mV, 'mean': array(-41.4742424242424) * mV, 'n': 11}})
(Pdb) electro_tests[3]
([<neuronunit.tests.fi.RheobaseTest object at 0x7f4585f08a58>, <neuronunit.tests.passive.InputResistanceTest object at 0x7f454dac2748>, <neuronunit.tests.passive.TimeConstantTest object at 0x7f454dac9320>, <neuronunit.tests.passive.CapacitanceTest object at 0x7f454dab9f98>, <neuronunit.tests.passive.RestingPotentialTest object at 0x7f4585f175c0>, <neuronunit.tests.waveform.InjectedCurrentAPWidthTest object at 0x7f454dac2c88>, <neuronunit.tests.waveform.InjectedCurrentAPAmplitudeTest object at 0x7f454dac9278>, <neuronunit.tests.waveform.InjectedCurrentAPThresholdTest object at 0x7f454dab90b8>], {'Resting membrane potential': {'std': array(0.0) * mV, 'mean': array(-57.9) * mV, 'n': 1}, 'Spike Half-Width': {'std': array(0.0) * ms, 'mean': array(1.505) * ms, 'n': 1}, 'Membrane Time Constant': {'std': array(8.77960968022798) * ms, 'mean': array(23.186875) * ms, 'n': 8}, 'Spike Amplitude': {'std': array(0.0) * mV, 'mean': array(64.0) * mV, 'n': 1}, 'Cell Capacitance': {'std': array(1.8649999999999998e-11) * F, 'mean': array(6.865e-11) * F, 'n': 2}, 'Input Resistance': {'std': array(0.0) * ohm, 'mean': array(457000000.0) * ohm, 'n': 1}, 'Rheobase': {'std': array(47.690512683342) * pA, 'mean': array(172.133333333333) * pA, 'n': 4}, 'Spike Threshold': {'std': array(5.49222298481843) * mV, 'mean': array(-41.4742424242424) * mV, 'n': 11}})
(Pdb) electro_tests[4]
([<neuronunit.tests.fi.RheobaseTest object at 0x7f4585f08710>, <neuronunit.tests.passive.InputResistanceTest object at 0x7f4585f08400>, <neuronunit.tests.passive.CapacitanceTest object at 0x7f4585f084a8>, <neuronunit.tests.passive.RestingPotentialTest object at 0x7f4585f179e8>, <neuronunit.tests.waveform.InjectedCurrentAPWidthTest object at 0x7f454dac2b70>, <neuronunit.tests.waveform.InjectedCurrentAPAmplitudeTest object at 0x7f454dab92b0>, <neuronunit.tests.waveform.InjectedCurrentAPThresholdTest object at 0x7f454dac9be0>], {'Resting membrane potential': {'std': array(5.63326553213636) * mV, 'mean': array(-61.5916666666667) * mV, 'n': 6}, 'Spike Half-Width': {'std': array(0.189931725513809) * ms, 'mean': array(0.41412962962963) * ms, 'n': 9}, 'Spike Amplitude': {'std': array(9.83222186515111) * mV, 'mean': array(71.2308333333333) * mV, 'n': 6}, 'Cell Capacitance': {'std': array(2.61303030538779e-10) * F, 'mean': array(6.202725e-10) * F, 'n': 4}, 'Input Resistance': {'std': array(68977517.5856699) * ohm, 'mean': array(142057692.30769202) * ohm, 'n': 13}, 'Rheobase': {'std': array(454.390111462674) * pA, 'mean': array(680.794444444444) * pA, 'n': 3}, 'Spike Threshold': {'std': array(6.95090078829409) * mV, 'mean': array(-46.8947619047619) * mV, 'n': 7}})

I temporarily added in some fault tolerance to b/neuronunit/tests/base.py

-
-        observation = {'mean': reference_data.mean*cls.units,
-                       'std': reference_data.std*cls.units,
-                       'n': reference_data.n}
+        if hasattr(reference_data,'mean'):
+            observation = {'mean': reference_data.mean*cls.units,
+                           'std': reference_data.std*cls.units,
+                           'n': reference_data.n}
+        else:
+            observation = None
         return observation

To allow investigation of the purkinje cell, given its noted missing TimeConstant mean.

stripathy commented 6 years ago

@russelljjarvis @rgerkin Does it make sense to either update the neurolex IDs in neuroelectro or add a DB field for scicrunch IDs if those are more stable/standardized than the neurolex IDs?

russelljjarvis commented 6 years ago

I do not understand the implications of the two different approaches. I wonder if a part of this question, is about trying to predict if sci-crunch IDs will be more lasting the Neurolex IDs?

rgerkin commented 6 years ago

@stripathy Yes, I think a scicrunch ID makes the most sense. Keep the neurolex field, but then expand the API to allow search via scicrunch ID (once the field is there and the data has been scraped, you can open an issue to update the API and assign it to me).

@russelljjarvis Yes, I am confident that SciCrunch IDs will be more lasting. It has NIH support. I think NeuroLex was deprecated for this reason (because it is being folded into SciCrunch).