Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modify error handling in function spike_contrast (spike_train_synchrony.py) #626

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions elephant/spike_train_synchrony.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,12 +135,15 @@ def spike_contrast(spiketrains, t_start=None, t_stop=None,

If the input spike trains constist of a single spiketrain.

If all input spike trains contain no more than 1 spike.
TypeError
If the input spike trains is not a list of `neo.SpikeTrain` objects.

If `t_start`, `t_stop`, or `min_bin` are not time quantities.

Warnings
--------
If all input spike trains contain no more than 1 spike.

Examples
--------
>>> import quantities as pq
Expand Down Expand Up @@ -191,8 +194,12 @@ def spike_contrast(spiketrains, t_start=None, t_stop=None,

try:
isi_min = min(np.diff(st).min() for st in spiketrains if len(st) > 1)
except TypeError:
raise ValueError("All input spiketrains contain no more than 1 spike.")
except:
# Do not raise an error but just a warning and continue the code which will return nan-values as synchrony.
# This is useful when running spike-contrast in a batch script, where it is necessary
# to get a complete data structure (trace for all bin-sizes) also for empty spike trains.
warnings.warn("All input spiketrains contain no more than 1 spike.")
isi_min = 0 # continue with isi_min = 0, causing to return spike-contrast with nan as synchrony values but
bin_min = max(isi_min / 2, min_bin)
Comment on lines 195 to 203
Copy link
Member

@Moritz-Alexander-Kern Moritz-Alexander-Kern Apr 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
try:
isi_min = min(np.diff(st).min() for st in spiketrains if len(st) > 1)
except TypeError:
raise ValueError("All input spiketrains contain no more than 1 spike.")
except:
# Do not raise an error but just a warning and continue the code which will return nan-values as synchrony.
# This is useful when running spike-contrast in a batch script, where it is necessary
# to get a complete data structure (trace for all bin-sizes) also for empty spike trains.
warnings.warn("All input spiketrains contain no more than 1 spike.")
isi_min = 0 # continue with isi_min = 0, causing to return spike-contrast with nan as synchrony values but
bin_min = max(isi_min / 2, min_bin)
if n_spikes_total == 0 :
warnings.warn("All input spike trains contain no spikes.")
isi_min = 0
else:
isi_min = min(np.diff(st).min() for st in spiketrains if len(st) > 1)
bin_min = max(isi_min / 2, min_bin)

Depending on whether or not to handle the case with only 1 spike per spike train, we could drop the try except block, by handling empty spike trains this way?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @Moritz-Alexander-Kern

Thank you for your suggestions!
The result for the empty spike trains, looks good!

Regarding "stationarity" in this line:

isi_min = min(np.diff(st).min() for st in spiketrains if len(st) > 1)

You are right, (non-)stationarity is not related how we should handle the case of a single spike. It just explains, why we can get different synchrony values even if we just have one spike per spike train.
For example, these two spike trains (non-stationary)

spiketrains = [
    neo.SpikeTrain([1.0]*ms, t_stop=1000 * ms),
    neo.SpikeTrain([1.2]*ms, t_stop=1000 * ms),
]

will lead to another synchrony value compared to these two spike trains (stationary):

spiketrains = [
    neo.SpikeTrain([250.0]*ms, t_stop=1000 * ms),
    neo.SpikeTrain([750.0]*ms, t_stop=1000 * ms),
]

Since it is possible to obtain a synchrony value even with just a single spike per spike train, I would suggest, to handle this case the same way as you suggested for the empty spiketrain by setting isi_min = 0 plus the warning (If I'm not wrong, we could use my original suggestion with the try-except block?).

Just to explain, what happens if we set isi_min = 0:
Spike-contrast calculates a synchrony for many different bin sizes ("bin size sweep") and at the end selects the optimum as the final synchrony.
The smallest ISI from all spiketrains is used to define the lower limit for the bin-size sweep. If isi_min=0, the lower limit will be set to min_bin:

bin_min = max(isi_min / 2, min_bin)

So, even in the case of just one spike per spike train, the algorithm can calculate a synchrony.


contrast_list = []
Expand Down
9 changes: 6 additions & 3 deletions elephant/test/test_spike_train_synchrony.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,12 @@ def test_spike_contrast_trace(self):
def test_invalid_data(self):
# invalid spiketrains
self.assertRaises(TypeError, spike_contrast, [[0, 1], [1.5, 2.3]])
self.assertRaises(ValueError, spike_contrast,
[neo.SpikeTrain([10] * ms, t_stop=1000 * ms),
neo.SpikeTrain([20] * ms, t_stop=1000 * ms)])

# remove the following test of "All input spiketrains contain no more than 1 spike"
# as the ValueError was replaced by a warning:
#self.assertRaises(ValueError, spike_contrast,
# [neo.SpikeTrain([10] * ms, t_stop=1000 * ms),
# neo.SpikeTrain([20] * ms, t_stop=1000 * ms)])

# a single spiketrain
spiketrain_valid = neo.SpikeTrain([0, 1000] * ms, t_stop=1000 * ms)
Expand Down
Loading