raphaelvallat / yasa

YASA (Yet Another Spindle Algorithm): a Python package to analyze polysomnographic sleep recordings.
https://raphaelvallat.com/yasa/
BSD 3-Clause "New" or "Revised" License
417 stars 113 forks source link

`get_sync_events` throws an error when only one event is found and its indices exceed data #8

Closed jajcayn closed 4 years ago

jajcayn commented 4 years ago

traceback:

---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-33-5fd9957c9aba> in <module>
     13 #         print(spt)
     14         eventst = yasa.get_sync_events(epochs_data.copy(), sf, detection=spt, center='Peak',
---> 15                                        time_before=0.5, time_after=0.5)
     16         spt["Epoch"] = i
     17         eventst["Epoch"] = i

~/.virtualenvs/sleepy3/lib/python3.7/site-packages/yasa/main.py in get_sync_events(data, sf, detection, center, time_before, time_after)
    210             df_tmp = get_sync_events(data[idx_chan, :], sf, k.iloc[:, :-2],
    211                                      center=center, time_before=time_before,
--> 212                                      time_after=time_after)
    213             df_tmp['Channel'] = c
    214             df_tmp['IdxChannel'] = idx_chan

~/.virtualenvs/sleepy3/lib/python3.7/site-packages/yasa/main.py in get_sync_events(data, sf, detection, center, time_before, time_after)
    237         idx = np.ma.compress_rows(idx_mask)
    238         print(idx)
--> 239         amps = data[idx]
    240         time = rng(0) / sf
    241 

IndexError: arrays used as indices must be of integer (or boolean) type

happens when there is only one event (in my case spindle, but that doesn't matter) and that one event is at the beginning or at the end, hence idx_mask = np.ma.mask_rows(np.ma.masked_outside(idx, 0, data.shape[0])) line actually gets rid of anything and idx = []

for now, I manually solve it in the sense, that I am always centered around Peak so I manually check whether my Peak - time_before or Peak + time_after exceed limits of the timeseries and if so, I alter the time_before or time_after.

It'd be nice if yasa offers such an option, or an option to completely skip would be also nice.

raphaelvallat commented 4 years ago

Hi @jajcayn! Sorry for the late reply for some reason I didn't get notified. So it seems to me that a quick fix would be to add the following lines in the code, after idx = np.ma.compress_rows(idx_mask):

if len(idx) == 0:
    warning.warn('warning message bla bla bla...Returning None')
    return None  # Or empty pandas DataFrame

We can also, as you say, alter the time before and after but I think I'd prefer the more explicit solution of throwing a warning and returning None. At least the user knows that something is wrong.

What do you think? Thanks!

jajcayn commented 4 years ago

Dear Raphael, thanks for the reply! I agree that explicit is definitely better than implicit, i.e. I would just warn the user and return None. As for the returning, I believe that None is better because then you can simply do something like

time_around = 1.  # second
events = None
while events is None:
    events = yasa.get_sync_events(..., time_before=time_around, time_after=time_around)
    time_around -= 0.1

I already did a small fix, opening a PR now :) Best!