pynapple-org / pynapple

PYthon Neural Analysis Package :pineapple:
https://pynapple-org.github.io/pynapple/
MIT License
243 stars 59 forks source link

New desired functions for IntervalSets - expand, contract, is_in #303

Open ckemere opened 1 week ago

ckemere commented 1 week ago

Just starting to play around with Pynapple, and I find a few expected IntervalSet functions missing. I'd love to be able to

slightly_larger_interval = intervals.expand(0.1)
slightly_smaller_interval = intervals.contract(0.1)

Though note that here, expanding should cause automerging of nearby intervals (so the number might decrease), and contracting should autodrop zero-duration intervals (so the number might decrease).

Also, I'd love to be able to do:

if intervals.is_in(t_interesting):
    do something cool
qian-chu commented 1 week ago

Maybe expand() and contract() can take a tuple to include/exclude timepoints before and after. Similar to numpy.pad. Also something I think might be useful is to shift the interval as a whole. This would be handy when playing with different response latency to compute tuning curves. For is_in(), would intersect() be a good enough exiting solution?

gviejo commented 3 days ago

For is_in(), there is already the function in_interval of IntervalSet. For example

ep = nap.IntervalSet(start=[0, 40], end=[10, 50])
tsd = nap.Tsd(t=np.arange(100), d=np.random.randn(100))
idx = ep.in_interval(tsd)

and idx is

>>> idx
array([ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0., nan, nan,
       nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan,
       nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan,
       nan,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1.,  1., nan,
       nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan,
       nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan,
       nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan,
       nan, nan, nan, nan, nan, nan, nan, nan, nan])

then you can do np.any(~np.isnan(ep.in_interval(tsd))) or np.all(~np.isnan(ep.in_interval(tsd)))

~ Expand and contract might be interesting although it is quite fast to do it manually. If you have ep like

>>> ep
            start    end
       0        0     10
       1       40     50
shape: (2, 2), time unit: sec.

I can add 1 second by recreating a new object:

nap.IntervalSet(ep.start, ep.end+1.0)

If the start is after the next end, IntervalSet will automatically merge them during initialization.

~ Shifting can be a good idea. For the moment adding any scalar to the IntervalSet will return a numpy array. Shifting everything together is mostly about writing the __array__ function of IntervalSet to support arithmetical operations so not too complicated.

Maybe expand() and contract() can take a tuple to include/exclude timepoints before and after. I am not sure I understand this part.

~ Overall I don't think any of those functions are too complicated to implement and could be nice additions. I will let this issue open until it's added. Feel free to open PRs.