Skip to content

Time series

pynapple.core.time_series

Pynapple time series are containers specialized for neurophysiological time series.

They provides standardized time representation, plus various functions for manipulating times series with identical sampling frequency.

Multiple time series object are avaible depending on the shape of the data.

  • TsdTensor : for data with of more than 2 dimensions, typically movies.
  • TsdFrame : for column-based data. It can be easily converted to a pandas.DataFrame. Columns can be labelled and selected similar to pandas.
  • Tsd : One-dimensional time series. It can be converted to a pandas.Series.
  • Ts : For timestamps data only.

Most of the same functions are available through all classes. Objects behaves like numpy.ndarray. Slicing can be done the same way for example tsd[0:10] returns the first 10 rows. Similarly, you can call any numpy functions like np.mean(tsd, 1).

BaseTsd

Bases: Base, NDArrayOperatorsMixin, ABC

Abstract base class for time series objects. Implement most of the shared functions across concrete classes Tsd, TsdFrame, TsdTensor

Source code in pynapple/core/time_series.py
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
class BaseTsd(Base, NDArrayOperatorsMixin, abc.ABC):
    """
    Abstract base class for time series objects.
    Implement most of the shared functions across concrete classes `Tsd`, `TsdFrame`, `TsdTensor`
    """

    def __init__(self, t, d, time_units="s", time_support=None):
        super().__init__(t, time_units, time_support)

        # Converting d to numpy array
        if isinstance(d, Number):
            self.values = np.array([d])
        elif isinstance(d, (list, tuple)):
            self.values = np.array(d)
        elif isinstance(d, np.ndarray):
            self.values = d
        elif is_array_like(d):
            self.values = convert_to_numpy(d, "d")
        else:
            raise RuntimeError(
                "Unknown format for d. Accepted formats are numpy.ndarray, list, tuple or any array-like objects."
            )

        assert len(self.index) == len(
            self.values
        ), "Length of values {} does not match length of index {}".format(
            len(self.values), len(self.index)
        )

        if isinstance(time_support, IntervalSet) and len(self.index):
            starts = time_support.start
            ends = time_support.end
            t, d = jitrestrict(self.index.values, self.values, starts, ends)
            self.index = TsIndex(t)
            self.values = d
            self.rate = self.index.shape[0] / np.sum(
                time_support.values[:, 1] - time_support.values[:, 0]
            )

        self.dtype = self.values.dtype

    def __setitem__(self, key, value):
        """setter for time series"""
        try:
            self.values.__setitem__(key, value)
        except IndexError:
            raise IndexError

    def __getattr__(self, name):
        """Allow numpy functions to be attached as attributes of Tsd objects"""
        if hasattr(np, name):
            np_func = getattr(np, name)

            def method(*args, **kwargs):
                return np_func(self, *args, **kwargs)

            return method

        raise AttributeError(
            "Time series object does not have the attribute {}".format(name)
        )

    @property
    def d(self):
        return self.values

    @property
    def shape(self):
        return self.values.shape

    @property
    def ndim(self):
        return self.values.ndim

    @property
    def size(self):
        return self.values.size

    def __array__(self, dtype=None):
        return self.values.astype(dtype)

    def __array_ufunc__(self, ufunc, method, *args, **kwargs):
        # print("In __array_ufunc__")
        # print("     ufunc = ", ufunc)
        # print("     method = ", method)
        # print("     args = ", args)
        # for inp in args:
        #     print(type(inp))
        # print("     kwargs = ", kwargs)

        if method == "__call__":
            new_args = []
            n_object = 0
            for a in args:
                if isinstance(a, self.__class__):
                    new_args.append(a.values)
                    n_object += 1
                else:
                    new_args.append(a)

            # Meant to prevent addition of two Tsd for example
            if n_object > 1:
                return NotImplemented
            else:
                out = ufunc(*new_args, **kwargs)

            if isinstance(out, np.ndarray):
                if out.shape[0] == self.index.shape[0]:
                    kwargs = {}
                    if hasattr(self, "columns"):
                        kwargs["columns"] = self.columns
                    return _get_class(out)(
                        t=self.index, d=out, time_support=self.time_support, **kwargs
                    )
                else:
                    return out
            else:
                return out
        else:
            return NotImplemented

    def __array_function__(self, func, types, args, kwargs):
        if func in [
            np.sort,
            np.lexsort,
            np.sort_complex,
            np.partition,
            np.argpartition,
        ]:
            return NotImplemented

        if hasattr(np.fft, func.__name__):
            return NotImplemented

        if func in [np.split, np.array_split, np.dsplit, np.hsplit, np.vsplit]:
            return _split_tsd(func, *args, **kwargs)

        if func in [np.concatenate, np.vstack, np.hstack, np.dstack]:
            return _concatenate_tsd(func, *args, **kwargs)

        new_args = []
        for a in args:
            if isinstance(a, self.__class__):
                new_args.append(a.values)
            else:
                new_args.append(a)

        out = func._implementation(*new_args, **kwargs)

        if isinstance(out, np.ndarray):
            # # if dims increased in any case, we can't return safely a time series
            # if out.ndim > self.ndim:
            #     return out
            if out.shape[0] == self.index.shape[0]:
                kwargs = {}
                if hasattr(self, "columns"):
                    kwargs["columns"] = self.columns
                return _get_class(out)(
                    t=self.index, d=out, time_support=self.time_support, **kwargs
                )
            else:
                return out
        else:
            return out

    def as_array(self):
        """
        Return the data as a numpy.ndarray

        Returns
        -------
        out: numpy.ndarray
            _
        """
        return self.values

    def data(self):
        """
        Return the data as a numpy.ndarray

        Returns
        -------
        out: numpy.ndarray
            _
        """
        return self.values

    def to_numpy(self):
        """
        Return the data as a numpy.ndarray. Mostly useful for matplotlib plotting when calling `plot(tsd)`
        """
        return self.values

    def copy(self):
        """Copy the data, index and time support"""
        return self.__class__(
            t=self.index.copy(), d=self.values.copy(), time_support=self.time_support
        )

    def value_from(self, data, ep=None):
        assert isinstance(
            data, BaseTsd
        ), "First argument should be an instance of Tsd, TsdFrame or TsdTensor"

        t, d, time_support, kwargs = super().value_from(data, ep)
        return data.__class__(t=t, d=d, time_support=time_support, **kwargs)

    def count(self, *args, **kwargs):
        t, d, ep = super().count(*args, **kwargs)
        return Tsd(t=t, d=d, time_support=ep)

    def bin_average(self, bin_size, ep=None, time_units="s"):
        """
        Bin the data by averaging points within bin_size
        bin_size should be seconds unless specified.
        If no epochs is passed, the data will be binned based on the time support.

        Parameters
        ----------
        bin_size : float
            The bin size (default is second)
        ep : None or IntervalSet, optional
            IntervalSet to restrict the operation
        time_units : str, optional
            Time units of bin size ('us', 'ms', 's' [default])

        Returns
        -------
        out: Tsd, TsdFrame, TsdTensor
            A Tsd object indexed by the center of the bins and holding the averaged data points.

        Examples
        --------
        This example shows how to bin data within bins of 0.1 second.

        >>> import pynapple as nap
        >>> import numpy as np
        >>> tsd = nap.Tsd(t=np.arange(100), d=np.random.rand(100))
        >>> bintsd = tsd.bin_average(0.1)

        An epoch can be specified:

        >>> ep = nap.IntervalSet(start = 10, end = 80, time_units = 's')
        >>> bintsd = tsd.bin_average(0.1, ep=ep)

        And bintsd automatically inherit ep as time support:

        >>> bintsd.time_support
        >>>    start    end
        >>> 0  10.0     80.0
        """
        if not isinstance(ep, IntervalSet):
            ep = self.time_support

        bin_size = TsIndex.format_timestamps(np.array([bin_size]), time_units)[0]

        time_array = self.index.values
        data_array = self.values
        starts = ep.start
        ends = ep.end
        if data_array.ndim > 1:
            t, d = jitbin_array(time_array, data_array, starts, ends, bin_size)
        else:
            t, d = jitbin(time_array, data_array, starts, ends, bin_size)

        kwargs = {}
        if hasattr(self, "columns"):
            kwargs["columns"] = self.columns
        return self.__class__(t=t, d=d, time_support=ep, **kwargs)

    def dropna(self, update_time_support=True):
        """Drop every rows containing NaNs. By default, the time support is updated to start and end around the time points that are non NaNs.
        To change this behavior, you can set update_time_support=False.

        Parameters
        ----------
        update_time_support : bool, optional

        Returns
        -------
        Tsd, TsdFrame or TsdTensor
            The time series without the NaNs
        """
        index_nan = np.any(np.isnan(self.values), axis=tuple(range(1, self.ndim)))
        if np.all(index_nan):  # In case it's only NaNs
            return self.__class__(
                t=np.array([]), d=np.empty(tuple([0] + [d for d in self.shape[1:]]))
            )

        elif np.any(index_nan):
            if update_time_support:
                time_array = self.index.values
                starts, ends = jitremove_nan(time_array, index_nan)

                to_fix = starts == ends
                if np.any(to_fix):
                    ends[
                        to_fix
                    ] += 1e-6  # adding 1 millisecond in case of a single point

                ep = IntervalSet(starts, ends)

                return self.__class__(
                    t=time_array[~index_nan], d=self.values[~index_nan], time_support=ep
                )

            else:
                return self[~index_nan]

        else:
            return self

    def convolve(self, array, ep=None, trim="both"):
        """Return the discrete linear convolution of the time series with a one dimensional sequence.

        A parameter ep can control the epochs for which the convolution will apply. Otherwise the convolution is made over the time support.

        This function assume a constant sampling rate of the time series.

        The only mode supported is full. The returned object is trimmed to match the size of the original object. The parameter trim controls which side the trimming operates. Default is 'both'.

        See the numpy documentation here : https://numpy.org/doc/stable/reference/generated/numpy.convolve.html

        Parameters
        ----------
        array : np.ndarray
            One dimensional input array
        ep : None, optional
            The epochs to apply the convolution
        trim : str, optional
            The side on which to trim the output of the convolution ('left', 'right', 'both' [default])

        Returns
        -------
        Tsd, TsdFrame or TsdTensor
            The convolved time series
        """
        assert isinstance(array, np.ndarray), "Input should be a 1-d numpy array."
        assert array.ndim == 1, "Input should be a one dimensional array."
        assert trim in [
            "both",
            "left",
            "right",
        ], "Unknow argument. trim should be 'both', 'left' or 'right'."

        if ep is None:
            ep = self.time_support

        time_array = self.index.values
        data_array = self.values
        starts = ep.start
        ends = ep.end

        if data_array.ndim == 1:
            new_data_array = np.zeros(data_array.shape)
            k = array.shape[0]
            for s, e in zip(starts, ends):
                idx_s = np.searchsorted(time_array, s)
                idx_e = np.searchsorted(time_array, e, side="right")

                t = idx_e - idx_s
                if trim == "left":
                    cut = (k - 1, t + k - 1)
                elif trim == "right":
                    cut = (0, t)
                else:
                    cut = ((1 - k % 2) + (k - 1) // 2, t + k - 1 - ((k - 1) // 2))
                # scipy is actually faster for Tsd
                new_data_array[idx_s:idx_e] = signal.convolve(
                    data_array[idx_s:idx_e], array
                )[cut[0] : cut[1]]

            return self.__class__(t=time_array, d=new_data_array, time_support=ep)
        else:
            new_data_array = np.zeros(data_array.shape)
            for s, e in zip(starts, ends):
                idx_s = np.searchsorted(time_array, s)
                idx_e = np.searchsorted(time_array, e, side="right")
                new_data_array[idx_s:idx_e] = pjitconvolve(
                    data_array[idx_s:idx_e], array, trim=trim
                )

            return self.__class__(t=time_array, d=new_data_array, time_support=ep)

    def smooth(self, std, windowsize=None, time_units="s", size_factor=100, norm=True):
        """Smooth a time series with a gaussian kernel.

        `std` is the standard deviation of the gaussian kernel in units of time.
        If only `std` is passed, the function will compute the standard deviation and size in number
        of time points automatically based on the sampling rate of the time series.
        For example, if the time series `tsd` has a sample rate of 100 Hz and `std` is 50 ms,
        the standard deviation will be converted to an integer through
        `tsd.rate * std = int(100 * 0.05) = 5`.

        If `windowsize` is None, the function will select a kernel size as 100 times
        the std in number of time points. This behavior can be controlled with the
        parameter `size_factor`.

        `norm` set to True normalizes the gaussian kernel to sum to 1.

        In the following example, a time series `tsd` with a sampling rate of 100 Hz
        is convolved with a gaussian kernel. The standard deviation is
        0.05 second and the windowsize is 2 second. When instantiating the gaussian kernel
        from scipy, it corresponds to parameters `M = 200` and `std=5`

            >>> tsd.smooth(std=0.05, windowsize=2, time_units='s', norm=False)

        This line is equivalent to :

            >>> from scipy.signal.windows import gaussian
            >>> kernel = gaussian(M = 200, std=5)
            >>> tsd.convolve(window)

        It is generally a good idea to visualize the kernel before applying any convolution.

        See the scipy documentation for the [gaussian window](https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.windows.gaussian.html)

        Parameters
        ----------
        std : Number
            Standard deviation in units of time
        windowsize : Number
            Size of the gaussian window in units of time.
        time_units : str, optional
            The time units in which std and windowsize are specified ('us', 'ms', 's' [default]).
        size_factor : int, optional
            How long should be the kernel size as a function of the standard deviation. Default is 100.
            Bypassed if windowsize is used.
        norm : bool, optional
            Whether to normalized the gaussian kernel or not. Default is `True`.

        Returns
        -------
        Tsd, TsdFrame, TsdTensor
            Time series convolved with a gaussian kernel

        """
        assert isinstance(std, (int, float)), "std should be type int or float"
        assert isinstance(size_factor, int), "size_factor should be of type int"
        assert isinstance(norm, bool), "norm should be of type boolean"
        assert isinstance(time_units, str), "time_units should be of type str"

        std = TsIndex.format_timestamps(np.array([std]), time_units)[0]
        std_size = int(self.rate * std)

        if windowsize is not None:
            assert isinstance(
                windowsize, (int, float)
            ), "windowsize should be type int or float"
            windowsize = TsIndex.format_timestamps(np.array([windowsize]), time_units)[
                0
            ]
            M = int(self.rate * windowsize)
        else:
            M = std_size * size_factor

        window = signal.windows.gaussian(M=M, std=std_size)

        if norm:
            window = window / window.sum()

        return self.convolve(window)

    def interpolate(self, ts, ep=None, left=None, right=None):
        """Wrapper of the numpy linear interpolation method. See [numpy interpolate](https://numpy.org/doc/stable/reference/generated/numpy.interp.html)
        for an explanation of the parameters.
        The argument ts should be Ts, Tsd, TsdFrame, TsdTensor to ensure interpolating from sorted timestamps in the right unit,

        Parameters
        ----------
        ts : Ts, Tsd, TsdFrame or TsdTensor
            The object holding the timestamps
        ep : IntervalSet, optional
            The epochs to use to interpolate. If None, the time support of Tsd is used.
        left : None, optional
            Value to return for ts < tsd[0], default is tsd[0].
        right : None, optional
            Value to return for ts > tsd[-1], default is tsd[-1].
        """
        assert isinstance(
            ts, Base
        ), "First argument should be an instance of Ts, Tsd, TsdFrame or TsdTensor"

        if not isinstance(ep, IntervalSet):
            ep = self.time_support

        new_t = ts.restrict(ep).index

        new_shape = (
            len(new_t) if self.values.ndim == 1 else (len(new_t),) + self.shape[1:]
        )
        new_d = np.full(new_shape, np.nan)

        start = 0
        for i in range(len(ep)):
            t = ts.get(ep[i, 0], ep[i, 1])
            tmp = self.get(ep[i, 0], ep[i, 1])

            if len(t) and len(tmp):
                if self.values.ndim == 1:
                    new_d[start : start + len(t)] = np.interp(
                        t.index.values,
                        tmp.index.values,
                        tmp.values,
                        left=left,
                        right=right,
                    )
                else:
                    interpolated_values = np.apply_along_axis(
                        lambda row: np.interp(
                            t.index.values,
                            tmp.index.values,
                            row,
                            left=left,
                            right=right,
                        ),
                        0,
                        tmp.values,
                    )
                    new_d[start : start + len(t), ...] = interpolated_values

            start += len(t)
        kwargs_dict = dict(time_support=ep)
        if hasattr(self, "columns"):
            kwargs_dict["columns"] = self.columns
        return self.__class__(t=new_t, d=new_d, **kwargs_dict)

__setattr__

__setattr__(name, value)

Object is immutable

Source code in pynapple/core/base_class.py
def __setattr__(self, name, value):
    """Object is immutable"""
    if self._initialized:
        raise RuntimeError(
            "Changing directly attributes is not permitted for {}.".format(
                self.nap_class
            )
        )
    else:
        object.__setattr__(self, name, value)

__getitem__ abstractmethod

__getitem__(key, *args, **kwargs)

getter for time series

Source code in pynapple/core/base_class.py
@abc.abstractmethod
def __getitem__(self, key, *args, **kwargs):
    """getter for time series"""
    pass

times

times(units='s')

The time index of the object, returned as np.double in the desired time units.

Parameters:

Name Type Description Default
units str

('us', 'ms', 's' [default])

's'

Returns:

Name Type Description
out ndarray

the time indexes

Source code in pynapple/core/base_class.py
def times(self, units="s"):
    """
    The time index of the object, returned as np.double in the desired time units.

    Parameters
    ----------
    units : str, optional
        ('us', 'ms', 's' [default])

    Returns
    -------
    out: numpy.ndarray
        the time indexes
    """
    return self.index.in_units(units)

start_time

start_time(units='s')

The first time index in the time series object

Parameters:

Name Type Description Default
units str

('us', 'ms', 's' [default])

's'

Returns:

Name Type Description
out float64

_

Source code in pynapple/core/base_class.py
def start_time(self, units="s"):
    """
    The first time index in the time series object

    Parameters
    ----------
    units : str, optional
        ('us', 'ms', 's' [default])

    Returns
    -------
    out: numpy.float64
        _
    """
    if len(self.index):
        return self.times(units=units)[0]
    else:
        return None

end_time

end_time(units='s')

The last time index in the time series object

Parameters:

Name Type Description Default
units str

('us', 'ms', 's' [default])

's'

Returns:

Name Type Description
out float64

_

Source code in pynapple/core/base_class.py
def end_time(self, units="s"):
    """
    The last time index in the time series object

    Parameters
    ----------
    units : str, optional
        ('us', 'ms', 's' [default])

    Returns
    -------
    out: numpy.float64
        _
    """
    if len(self.index):
        return self.times(units=units)[-1]
    else:
        return None

restrict

restrict(iset)

Restricts a time series object to a set of time intervals delimited by an IntervalSet object

Parameters:

Name Type Description Default
iset IntervalSet

the IntervalSet object

required

Returns:

Name Type Description
out (Ts, Tsd, TsdFrame or TsdTensor)

Tsd object restricted to ep

Examples:

The Ts object is restrict to the intervals defined by ep.

>>> import pynapple as nap
>>> import numpy as np
>>> t = np.unique(np.sort(np.random.randint(0, 1000, 100)))
>>> ts = nap.Ts(t=t, time_units='s')
>>> ep = nap.IntervalSet(start=0, end=500, time_units='s')
>>> newts = ts.restrict(ep)

The time support of newts automatically inherit the epochs defined by ep.

>>> newts.time_support
    start    end
0    0.0  500.0
Source code in pynapple/core/base_class.py
def restrict(self, iset):
    """
    Restricts a time series object to a set of time intervals delimited by an IntervalSet object

    Parameters
    ----------
    iset : IntervalSet
        the IntervalSet object

    Returns
    -------
    out: Ts, Tsd, TsdFrame or TsdTensor
        Tsd object restricted to ep

    Examples
    --------
    The Ts object is restrict to the intervals defined by ep.

    >>> import pynapple as nap
    >>> import numpy as np
    >>> t = np.unique(np.sort(np.random.randint(0, 1000, 100)))
    >>> ts = nap.Ts(t=t, time_units='s')
    >>> ep = nap.IntervalSet(start=0, end=500, time_units='s')
    >>> newts = ts.restrict(ep)

    The time support of newts automatically inherit the epochs defined by ep.

    >>> newts.time_support
        start    end
    0    0.0  500.0

    """
    assert isinstance(iset, IntervalSet), "Argument should be IntervalSet"

    time_array = self.index.values
    starts = iset.start
    ends = iset.end

    if hasattr(self, "values"):
        data_array = self.values
        t, d = jitrestrict(time_array, data_array, starts, ends)

        kwargs = {}
        if hasattr(self, "columns"):
            kwargs["columns"] = self.columns

        return self.__class__(t=t, d=d, time_support=iset, **kwargs)

    else:
        t = jittsrestrict(time_array, starts, ends)
        return self.__class__(t=t, time_support=iset)

find_support

find_support(min_gap, time_units='s')

find the smallest (to a min_gap resolution) IntervalSet containing all the times in the Tsd

Parameters:

Name Type Description Default
min_gap float or int

minimal interval between timestamps

required
time_units str

Time units of min gap

's'

Returns:

Type Description
IntervalSet

Description

Source code in pynapple/core/base_class.py
def find_support(self, min_gap, time_units="s"):
    """
    find the smallest (to a min_gap resolution) IntervalSet containing all the times in the Tsd

    Parameters
    ----------
    min_gap : float or int
        minimal interval between timestamps
    time_units : str, optional
        Time units of min gap

    Returns
    -------
    IntervalSet
        Description
    """
    assert isinstance(min_gap, Number), "min_gap should be a float or int"
    min_gap = TsIndex.format_timestamps(np.array([min_gap]), time_units)[0]
    time_array = self.index.values

    starts = [time_array[0]]
    ends = []
    for i in range(len(time_array) - 1):
        if (time_array[i + 1] - time_array[i]) > min_gap:
            ends.append(time_array[i] + 1e-6)
            starts.append(time_array[i + 1])

    ends.append(time_array[-1] + 1e-6)

    return IntervalSet(start=starts, end=ends)

get

get(start, end=None, time_units='s')

Slice the time series from start to end such that all the timestamps satisfy start<=t<=end. If end is None, only the timepoint closest to start is returned.

By default, the time support doesn't change. If you want to change the time support, use the restrict function.

Parameters:

Name Type Description Default
start float or int

The start (or closest time point if end is None)

required
end float or int or None

The end

None
Source code in pynapple/core/base_class.py
def get(self, start, end=None, time_units="s"):
    """Slice the time series from `start` to `end` such that all the timestamps satisfy `start<=t<=end`.
    If `end` is None, only the timepoint closest to `start` is returned.

    By default, the time support doesn't change. If you want to change the time support, use the `restrict` function.

    Parameters
    ----------
    start : float or int
        The start (or closest time point if `end` is None)
    end : float or int or None
        The end
    """
    assert isinstance(start, Number), "start should be a float or int"
    time_array = self.index.values

    if end is None:
        start = TsIndex.format_timestamps(np.array([start]), time_units)[0]
        idx = int(np.searchsorted(time_array, start))
        if idx == 0:
            return self[idx]
        elif idx >= self.shape[0]:
            return self[-1]
        else:
            if start - time_array[idx - 1] < time_array[idx] - start:
                return self[idx - 1]
            else:
                return self[idx]
    else:
        assert isinstance(end, Number), "end should be a float or int"
        assert start < end, "Start should not precede end"
        start, end = TsIndex.format_timestamps(np.array([start, end]), time_units)
        idx_start = np.searchsorted(time_array, start)
        idx_end = np.searchsorted(time_array, end, side="right")
        return self[idx_start:idx_end]

__setitem__

__setitem__(key, value)

setter for time series

Source code in pynapple/core/time_series.py
def __setitem__(self, key, value):
    """setter for time series"""
    try:
        self.values.__setitem__(key, value)
    except IndexError:
        raise IndexError

__getattr__

__getattr__(name)

Allow numpy functions to be attached as attributes of Tsd objects

Source code in pynapple/core/time_series.py
def __getattr__(self, name):
    """Allow numpy functions to be attached as attributes of Tsd objects"""
    if hasattr(np, name):
        np_func = getattr(np, name)

        def method(*args, **kwargs):
            return np_func(self, *args, **kwargs)

        return method

    raise AttributeError(
        "Time series object does not have the attribute {}".format(name)
    )

as_array

as_array()

Return the data as a numpy.ndarray

Returns:

Name Type Description
out ndarray

_

Source code in pynapple/core/time_series.py
def as_array(self):
    """
    Return the data as a numpy.ndarray

    Returns
    -------
    out: numpy.ndarray
        _
    """
    return self.values

data

data()

Return the data as a numpy.ndarray

Returns:

Name Type Description
out ndarray

_

Source code in pynapple/core/time_series.py
def data(self):
    """
    Return the data as a numpy.ndarray

    Returns
    -------
    out: numpy.ndarray
        _
    """
    return self.values

to_numpy

to_numpy()

Return the data as a numpy.ndarray. Mostly useful for matplotlib plotting when calling plot(tsd)

Source code in pynapple/core/time_series.py
def to_numpy(self):
    """
    Return the data as a numpy.ndarray. Mostly useful for matplotlib plotting when calling `plot(tsd)`
    """
    return self.values

copy

copy()

Copy the data, index and time support

Source code in pynapple/core/time_series.py
def copy(self):
    """Copy the data, index and time support"""
    return self.__class__(
        t=self.index.copy(), d=self.values.copy(), time_support=self.time_support
    )

bin_average

bin_average(bin_size, ep=None, time_units='s')

Bin the data by averaging points within bin_size bin_size should be seconds unless specified. If no epochs is passed, the data will be binned based on the time support.

Parameters:

Name Type Description Default
bin_size float

The bin size (default is second)

required
ep None or IntervalSet

IntervalSet to restrict the operation

None
time_units str

Time units of bin size ('us', 'ms', 's' [default])

's'

Returns:

Name Type Description
out (Tsd, TsdFrame, TsdTensor)

A Tsd object indexed by the center of the bins and holding the averaged data points.

Examples:

This example shows how to bin data within bins of 0.1 second.

>>> import pynapple as nap
>>> import numpy as np
>>> tsd = nap.Tsd(t=np.arange(100), d=np.random.rand(100))
>>> bintsd = tsd.bin_average(0.1)

An epoch can be specified:

>>> ep = nap.IntervalSet(start = 10, end = 80, time_units = 's')
>>> bintsd = tsd.bin_average(0.1, ep=ep)

And bintsd automatically inherit ep as time support:

>>> bintsd.time_support
>>>    start    end
>>> 0  10.0     80.0
Source code in pynapple/core/time_series.py
def bin_average(self, bin_size, ep=None, time_units="s"):
    """
    Bin the data by averaging points within bin_size
    bin_size should be seconds unless specified.
    If no epochs is passed, the data will be binned based on the time support.

    Parameters
    ----------
    bin_size : float
        The bin size (default is second)
    ep : None or IntervalSet, optional
        IntervalSet to restrict the operation
    time_units : str, optional
        Time units of bin size ('us', 'ms', 's' [default])

    Returns
    -------
    out: Tsd, TsdFrame, TsdTensor
        A Tsd object indexed by the center of the bins and holding the averaged data points.

    Examples
    --------
    This example shows how to bin data within bins of 0.1 second.

    >>> import pynapple as nap
    >>> import numpy as np
    >>> tsd = nap.Tsd(t=np.arange(100), d=np.random.rand(100))
    >>> bintsd = tsd.bin_average(0.1)

    An epoch can be specified:

    >>> ep = nap.IntervalSet(start = 10, end = 80, time_units = 's')
    >>> bintsd = tsd.bin_average(0.1, ep=ep)

    And bintsd automatically inherit ep as time support:

    >>> bintsd.time_support
    >>>    start    end
    >>> 0  10.0     80.0
    """
    if not isinstance(ep, IntervalSet):
        ep = self.time_support

    bin_size = TsIndex.format_timestamps(np.array([bin_size]), time_units)[0]

    time_array = self.index.values
    data_array = self.values
    starts = ep.start
    ends = ep.end
    if data_array.ndim > 1:
        t, d = jitbin_array(time_array, data_array, starts, ends, bin_size)
    else:
        t, d = jitbin(time_array, data_array, starts, ends, bin_size)

    kwargs = {}
    if hasattr(self, "columns"):
        kwargs["columns"] = self.columns
    return self.__class__(t=t, d=d, time_support=ep, **kwargs)

dropna

dropna(update_time_support=True)

Drop every rows containing NaNs. By default, the time support is updated to start and end around the time points that are non NaNs. To change this behavior, you can set update_time_support=False.

Parameters:

Name Type Description Default
update_time_support bool
True

Returns:

Type Description
(Tsd, TsdFrame or TsdTensor)

The time series without the NaNs

Source code in pynapple/core/time_series.py
def dropna(self, update_time_support=True):
    """Drop every rows containing NaNs. By default, the time support is updated to start and end around the time points that are non NaNs.
    To change this behavior, you can set update_time_support=False.

    Parameters
    ----------
    update_time_support : bool, optional

    Returns
    -------
    Tsd, TsdFrame or TsdTensor
        The time series without the NaNs
    """
    index_nan = np.any(np.isnan(self.values), axis=tuple(range(1, self.ndim)))
    if np.all(index_nan):  # In case it's only NaNs
        return self.__class__(
            t=np.array([]), d=np.empty(tuple([0] + [d for d in self.shape[1:]]))
        )

    elif np.any(index_nan):
        if update_time_support:
            time_array = self.index.values
            starts, ends = jitremove_nan(time_array, index_nan)

            to_fix = starts == ends
            if np.any(to_fix):
                ends[
                    to_fix
                ] += 1e-6  # adding 1 millisecond in case of a single point

            ep = IntervalSet(starts, ends)

            return self.__class__(
                t=time_array[~index_nan], d=self.values[~index_nan], time_support=ep
            )

        else:
            return self[~index_nan]

    else:
        return self

convolve

convolve(array, ep=None, trim='both')

Return the discrete linear convolution of the time series with a one dimensional sequence.

A parameter ep can control the epochs for which the convolution will apply. Otherwise the convolution is made over the time support.

This function assume a constant sampling rate of the time series.

The only mode supported is full. The returned object is trimmed to match the size of the original object. The parameter trim controls which side the trimming operates. Default is 'both'.

See the numpy documentation here : https://numpy.org/doc/stable/reference/generated/numpy.convolve.html

Parameters:

Name Type Description Default
array ndarray

One dimensional input array

required
ep None

The epochs to apply the convolution

None
trim str

The side on which to trim the output of the convolution ('left', 'right', 'both' [default])

'both'

Returns:

Type Description
(Tsd, TsdFrame or TsdTensor)

The convolved time series

Source code in pynapple/core/time_series.py
def convolve(self, array, ep=None, trim="both"):
    """Return the discrete linear convolution of the time series with a one dimensional sequence.

    A parameter ep can control the epochs for which the convolution will apply. Otherwise the convolution is made over the time support.

    This function assume a constant sampling rate of the time series.

    The only mode supported is full. The returned object is trimmed to match the size of the original object. The parameter trim controls which side the trimming operates. Default is 'both'.

    See the numpy documentation here : https://numpy.org/doc/stable/reference/generated/numpy.convolve.html

    Parameters
    ----------
    array : np.ndarray
        One dimensional input array
    ep : None, optional
        The epochs to apply the convolution
    trim : str, optional
        The side on which to trim the output of the convolution ('left', 'right', 'both' [default])

    Returns
    -------
    Tsd, TsdFrame or TsdTensor
        The convolved time series
    """
    assert isinstance(array, np.ndarray), "Input should be a 1-d numpy array."
    assert array.ndim == 1, "Input should be a one dimensional array."
    assert trim in [
        "both",
        "left",
        "right",
    ], "Unknow argument. trim should be 'both', 'left' or 'right'."

    if ep is None:
        ep = self.time_support

    time_array = self.index.values
    data_array = self.values
    starts = ep.start
    ends = ep.end

    if data_array.ndim == 1:
        new_data_array = np.zeros(data_array.shape)
        k = array.shape[0]
        for s, e in zip(starts, ends):
            idx_s = np.searchsorted(time_array, s)
            idx_e = np.searchsorted(time_array, e, side="right")

            t = idx_e - idx_s
            if trim == "left":
                cut = (k - 1, t + k - 1)
            elif trim == "right":
                cut = (0, t)
            else:
                cut = ((1 - k % 2) + (k - 1) // 2, t + k - 1 - ((k - 1) // 2))
            # scipy is actually faster for Tsd
            new_data_array[idx_s:idx_e] = signal.convolve(
                data_array[idx_s:idx_e], array
            )[cut[0] : cut[1]]

        return self.__class__(t=time_array, d=new_data_array, time_support=ep)
    else:
        new_data_array = np.zeros(data_array.shape)
        for s, e in zip(starts, ends):
            idx_s = np.searchsorted(time_array, s)
            idx_e = np.searchsorted(time_array, e, side="right")
            new_data_array[idx_s:idx_e] = pjitconvolve(
                data_array[idx_s:idx_e], array, trim=trim
            )

        return self.__class__(t=time_array, d=new_data_array, time_support=ep)

smooth

smooth(
    std,
    windowsize=None,
    time_units="s",
    size_factor=100,
    norm=True,
)

Smooth a time series with a gaussian kernel.

std is the standard deviation of the gaussian kernel in units of time. If only std is passed, the function will compute the standard deviation and size in number of time points automatically based on the sampling rate of the time series. For example, if the time series tsd has a sample rate of 100 Hz and std is 50 ms, the standard deviation will be converted to an integer through tsd.rate * std = int(100 * 0.05) = 5.

If windowsize is None, the function will select a kernel size as 100 times the std in number of time points. This behavior can be controlled with the parameter size_factor.

norm set to True normalizes the gaussian kernel to sum to 1.

In the following example, a time series tsd with a sampling rate of 100 Hz is convolved with a gaussian kernel. The standard deviation is 0.05 second and the windowsize is 2 second. When instantiating the gaussian kernel from scipy, it corresponds to parameters M = 200 and std=5

>>> tsd.smooth(std=0.05, windowsize=2, time_units='s', norm=False)

This line is equivalent to :

>>> from scipy.signal.windows import gaussian
>>> kernel = gaussian(M = 200, std=5)
>>> tsd.convolve(window)

It is generally a good idea to visualize the kernel before applying any convolution.

See the scipy documentation for the gaussian window

Parameters:

Name Type Description Default
std Number

Standard deviation in units of time

required
windowsize Number

Size of the gaussian window in units of time.

None
time_units str

The time units in which std and windowsize are specified ('us', 'ms', 's' [default]).

's'
size_factor int

How long should be the kernel size as a function of the standard deviation. Default is 100. Bypassed if windowsize is used.

100
norm bool

Whether to normalized the gaussian kernel or not. Default is True.

True

Returns:

Type Description
(Tsd, TsdFrame, TsdTensor)

Time series convolved with a gaussian kernel

Source code in pynapple/core/time_series.py
def smooth(self, std, windowsize=None, time_units="s", size_factor=100, norm=True):
    """Smooth a time series with a gaussian kernel.

    `std` is the standard deviation of the gaussian kernel in units of time.
    If only `std` is passed, the function will compute the standard deviation and size in number
    of time points automatically based on the sampling rate of the time series.
    For example, if the time series `tsd` has a sample rate of 100 Hz and `std` is 50 ms,
    the standard deviation will be converted to an integer through
    `tsd.rate * std = int(100 * 0.05) = 5`.

    If `windowsize` is None, the function will select a kernel size as 100 times
    the std in number of time points. This behavior can be controlled with the
    parameter `size_factor`.

    `norm` set to True normalizes the gaussian kernel to sum to 1.

    In the following example, a time series `tsd` with a sampling rate of 100 Hz
    is convolved with a gaussian kernel. The standard deviation is
    0.05 second and the windowsize is 2 second. When instantiating the gaussian kernel
    from scipy, it corresponds to parameters `M = 200` and `std=5`

        >>> tsd.smooth(std=0.05, windowsize=2, time_units='s', norm=False)

    This line is equivalent to :

        >>> from scipy.signal.windows import gaussian
        >>> kernel = gaussian(M = 200, std=5)
        >>> tsd.convolve(window)

    It is generally a good idea to visualize the kernel before applying any convolution.

    See the scipy documentation for the [gaussian window](https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.windows.gaussian.html)

    Parameters
    ----------
    std : Number
        Standard deviation in units of time
    windowsize : Number
        Size of the gaussian window in units of time.
    time_units : str, optional
        The time units in which std and windowsize are specified ('us', 'ms', 's' [default]).
    size_factor : int, optional
        How long should be the kernel size as a function of the standard deviation. Default is 100.
        Bypassed if windowsize is used.
    norm : bool, optional
        Whether to normalized the gaussian kernel or not. Default is `True`.

    Returns
    -------
    Tsd, TsdFrame, TsdTensor
        Time series convolved with a gaussian kernel

    """
    assert isinstance(std, (int, float)), "std should be type int or float"
    assert isinstance(size_factor, int), "size_factor should be of type int"
    assert isinstance(norm, bool), "norm should be of type boolean"
    assert isinstance(time_units, str), "time_units should be of type str"

    std = TsIndex.format_timestamps(np.array([std]), time_units)[0]
    std_size = int(self.rate * std)

    if windowsize is not None:
        assert isinstance(
            windowsize, (int, float)
        ), "windowsize should be type int or float"
        windowsize = TsIndex.format_timestamps(np.array([windowsize]), time_units)[
            0
        ]
        M = int(self.rate * windowsize)
    else:
        M = std_size * size_factor

    window = signal.windows.gaussian(M=M, std=std_size)

    if norm:
        window = window / window.sum()

    return self.convolve(window)

interpolate

interpolate(ts, ep=None, left=None, right=None)

Wrapper of the numpy linear interpolation method. See numpy interpolate for an explanation of the parameters. The argument ts should be Ts, Tsd, TsdFrame, TsdTensor to ensure interpolating from sorted timestamps in the right unit,

Parameters:

Name Type Description Default
ts (Ts, Tsd, TsdFrame or TsdTensor)

The object holding the timestamps

required
ep IntervalSet

The epochs to use to interpolate. If None, the time support of Tsd is used.

None
left None

Value to return for ts < tsd[0], default is tsd[0].

None
right None

Value to return for ts > tsd[-1], default is tsd[-1].

None
Source code in pynapple/core/time_series.py
def interpolate(self, ts, ep=None, left=None, right=None):
    """Wrapper of the numpy linear interpolation method. See [numpy interpolate](https://numpy.org/doc/stable/reference/generated/numpy.interp.html)
    for an explanation of the parameters.
    The argument ts should be Ts, Tsd, TsdFrame, TsdTensor to ensure interpolating from sorted timestamps in the right unit,

    Parameters
    ----------
    ts : Ts, Tsd, TsdFrame or TsdTensor
        The object holding the timestamps
    ep : IntervalSet, optional
        The epochs to use to interpolate. If None, the time support of Tsd is used.
    left : None, optional
        Value to return for ts < tsd[0], default is tsd[0].
    right : None, optional
        Value to return for ts > tsd[-1], default is tsd[-1].
    """
    assert isinstance(
        ts, Base
    ), "First argument should be an instance of Ts, Tsd, TsdFrame or TsdTensor"

    if not isinstance(ep, IntervalSet):
        ep = self.time_support

    new_t = ts.restrict(ep).index

    new_shape = (
        len(new_t) if self.values.ndim == 1 else (len(new_t),) + self.shape[1:]
    )
    new_d = np.full(new_shape, np.nan)

    start = 0
    for i in range(len(ep)):
        t = ts.get(ep[i, 0], ep[i, 1])
        tmp = self.get(ep[i, 0], ep[i, 1])

        if len(t) and len(tmp):
            if self.values.ndim == 1:
                new_d[start : start + len(t)] = np.interp(
                    t.index.values,
                    tmp.index.values,
                    tmp.values,
                    left=left,
                    right=right,
                )
            else:
                interpolated_values = np.apply_along_axis(
                    lambda row: np.interp(
                        t.index.values,
                        tmp.index.values,
                        row,
                        left=left,
                        right=right,
                    ),
                    0,
                    tmp.values,
                )
                new_d[start : start + len(t), ...] = interpolated_values

        start += len(t)
    kwargs_dict = dict(time_support=ep)
    if hasattr(self, "columns"):
        kwargs_dict["columns"] = self.columns
    return self.__class__(t=new_t, d=new_d, **kwargs_dict)

TsdTensor

Bases: BaseTsd

TsdTensor

Attributes:

Name Type Description
rate float

Frequency of the time series (Hz) computed over the time support

time_support IntervalSet

The time support of the time series

Source code in pynapple/core/time_series.py
class TsdTensor(BaseTsd):
    """
    TsdTensor

    Attributes
    ----------
    rate : float
        Frequency of the time series (Hz) computed over the time support
    time_support : IntervalSet
        The time support of the time series
    """

    def __init__(self, t, d, time_units="s", time_support=None, **kwargs):
        """
        TsdTensor initializer

        Parameters
        ----------
        t : numpy.ndarray
            the time index t
        d : numpy.ndarray
            The data
        time_units : str, optional
            The time units in which times are specified ('us', 'ms', 's' [default]).
        time_support : IntervalSet, optional
            The time support of the TsdFrame object
        """
        super().__init__(t, d, time_units, time_support)

        assert (
            self.values.ndim >= 3
        ), "Data should have more than 2 dimensions. If ndim < 3, use TsdFrame or Tsd object"

        self.nap_class = self.__class__.__name__
        self._initialized = True

    def __repr__(self):
        headers = ["Time (s)", ""]
        bottom = "dtype: {}".format(self.dtype) + ", shape: {}".format(self.shape)

        max_rows = 2
        rows = _get_terminal_size()[1]
        max_rows = np.maximum(rows - 10, 2)

        if len(self):

            def create_str(array):
                if array.ndim == 1:
                    if len(array) > 2:
                        return np.array2string(
                            np.array([array[0], array[-1]]),
                            precision=6,
                            separator=" ... ",
                        )
                    else:
                        return np.array2string(array, precision=6, separator=", ")
                else:
                    return "[" + create_str(array[0]) + " ...]"

            _str_ = []
            if self.shape[0] > max_rows:
                n_rows = max_rows // 2
                for i, array in zip(self.index[0:n_rows], self.values[0:n_rows]):
                    _str_.append([i.__repr__(), create_str(array)])
                _str_.append(["...", ""])
                for i, array in zip(
                    self.index[-n_rows:],
                    self.values[self.values.shape[0] - n_rows : self.values.shape[0]],
                ):
                    _str_.append([i.__repr__(), create_str(array)])
            else:
                for i, array in zip(self.index, self.values):
                    _str_.append([i.__repr__(), create_str(array)])

            return tabulate(_str_, headers=headers, colalign=("left",)) + "\n" + bottom

        else:
            return tabulate([], headers=headers) + "\n" + bottom

    def __getitem__(self, key, *args, **kwargs):
        output = self.values.__getitem__(key)
        if isinstance(key, tuple):
            index = self.index.__getitem__(key[0])
        else:
            index = self.index.__getitem__(key)

        if isinstance(index, Number):
            index = np.array([index])

        if all(isinstance(a, np.ndarray) for a in [index, output]):
            if output.shape[0] == index.shape[0]:
                if output.ndim == 1:
                    return Tsd(t=index, d=output, time_support=self.time_support)
                elif output.ndim == 2:
                    return TsdFrame(
                        t=index, d=output, time_support=self.time_support, **kwargs
                    )
                else:
                    return TsdTensor(t=index, d=output, time_support=self.time_support)

            else:
                return output
        else:
            return output

    def save(self, filename):
        """
        Save TsdTensor object in npz format. The file will contain the timestamps, the
        data and the time support.

        The main purpose of this function is to save small/medium sized time series
        objects. For example, you extracted several channels from your recording and
        filtered them. You can save the filtered channels as a npz to avoid
        reprocessing it.

        You can load the object with `nap.load_file`. Keys are 't', 'd', 'start', 'end', 'type'
        and 'columns' for columns names.

        Parameters
        ----------
        filename : str
            The filename

        Examples
        --------
        >>> import pynapple as nap
        >>> import numpy as np
        >>> tsdtensor = nap.TsdTensor(t=np.array([0., 1.]), d = np.zeros((2,3,4)))
        >>> tsdtensor.save("my_path/my_tsdtensor.npz")

        To load you file, you can use the `nap.load_file` function :

        >>> tsdtensor = nap.load_file("my_path/my_tsdtensor.npz")

        Raises
        ------
        RuntimeError
            If filename is not str, path does not exist or filename is a directory.
        """
        if not isinstance(filename, str):
            raise RuntimeError("Invalid type; please provide filename as string")

        if os.path.isdir(filename):
            raise RuntimeError(
                "Invalid filename input. {} is directory.".format(filename)
            )

        if not filename.lower().endswith(".npz"):
            filename = filename + ".npz"

        dirname = os.path.dirname(filename)

        if len(dirname) and not os.path.exists(dirname):
            raise RuntimeError(
                "Path {} does not exist.".format(os.path.dirname(filename))
            )

        np.savez(
            filename,
            t=self.index.values,
            d=self.values,
            start=self.time_support.start,
            end=self.time_support.end,
            type=np.array([self.nap_class], dtype=np.str_),
        )

        return

__setattr__

__setattr__(name, value)

Object is immutable

Source code in pynapple/core/base_class.py
def __setattr__(self, name, value):
    """Object is immutable"""
    if self._initialized:
        raise RuntimeError(
            "Changing directly attributes is not permitted for {}.".format(
                self.nap_class
            )
        )
    else:
        object.__setattr__(self, name, value)

__setitem__

__setitem__(key, value)

setter for time series

Source code in pynapple/core/time_series.py
def __setitem__(self, key, value):
    """setter for time series"""
    try:
        self.values.__setitem__(key, value)
    except IndexError:
        raise IndexError

times

times(units='s')

The time index of the object, returned as np.double in the desired time units.

Parameters:

Name Type Description Default
units str

('us', 'ms', 's' [default])

's'

Returns:

Name Type Description
out ndarray

the time indexes

Source code in pynapple/core/base_class.py
def times(self, units="s"):
    """
    The time index of the object, returned as np.double in the desired time units.

    Parameters
    ----------
    units : str, optional
        ('us', 'ms', 's' [default])

    Returns
    -------
    out: numpy.ndarray
        the time indexes
    """
    return self.index.in_units(units)

start_time

start_time(units='s')

The first time index in the time series object

Parameters:

Name Type Description Default
units str

('us', 'ms', 's' [default])

's'

Returns:

Name Type Description
out float64

_

Source code in pynapple/core/base_class.py
def start_time(self, units="s"):
    """
    The first time index in the time series object

    Parameters
    ----------
    units : str, optional
        ('us', 'ms', 's' [default])

    Returns
    -------
    out: numpy.float64
        _
    """
    if len(self.index):
        return self.times(units=units)[0]
    else:
        return None

end_time

end_time(units='s')

The last time index in the time series object

Parameters:

Name Type Description Default
units str

('us', 'ms', 's' [default])

's'

Returns:

Name Type Description
out float64

_

Source code in pynapple/core/base_class.py
def end_time(self, units="s"):
    """
    The last time index in the time series object

    Parameters
    ----------
    units : str, optional
        ('us', 'ms', 's' [default])

    Returns
    -------
    out: numpy.float64
        _
    """
    if len(self.index):
        return self.times(units=units)[-1]
    else:
        return None

restrict

restrict(iset)

Restricts a time series object to a set of time intervals delimited by an IntervalSet object

Parameters:

Name Type Description Default
iset IntervalSet

the IntervalSet object

required

Returns:

Name Type Description
out (Ts, Tsd, TsdFrame or TsdTensor)

Tsd object restricted to ep

Examples:

The Ts object is restrict to the intervals defined by ep.

>>> import pynapple as nap
>>> import numpy as np
>>> t = np.unique(np.sort(np.random.randint(0, 1000, 100)))
>>> ts = nap.Ts(t=t, time_units='s')
>>> ep = nap.IntervalSet(start=0, end=500, time_units='s')
>>> newts = ts.restrict(ep)

The time support of newts automatically inherit the epochs defined by ep.

>>> newts.time_support
    start    end
0    0.0  500.0
Source code in pynapple/core/base_class.py
def restrict(self, iset):
    """
    Restricts a time series object to a set of time intervals delimited by an IntervalSet object

    Parameters
    ----------
    iset : IntervalSet
        the IntervalSet object

    Returns
    -------
    out: Ts, Tsd, TsdFrame or TsdTensor
        Tsd object restricted to ep

    Examples
    --------
    The Ts object is restrict to the intervals defined by ep.

    >>> import pynapple as nap
    >>> import numpy as np
    >>> t = np.unique(np.sort(np.random.randint(0, 1000, 100)))
    >>> ts = nap.Ts(t=t, time_units='s')
    >>> ep = nap.IntervalSet(start=0, end=500, time_units='s')
    >>> newts = ts.restrict(ep)

    The time support of newts automatically inherit the epochs defined by ep.

    >>> newts.time_support
        start    end
    0    0.0  500.0

    """
    assert isinstance(iset, IntervalSet), "Argument should be IntervalSet"

    time_array = self.index.values
    starts = iset.start
    ends = iset.end

    if hasattr(self, "values"):
        data_array = self.values
        t, d = jitrestrict(time_array, data_array, starts, ends)

        kwargs = {}
        if hasattr(self, "columns"):
            kwargs["columns"] = self.columns

        return self.__class__(t=t, d=d, time_support=iset, **kwargs)

    else:
        t = jittsrestrict(time_array, starts, ends)
        return self.__class__(t=t, time_support=iset)

copy

copy()

Copy the data, index and time support

Source code in pynapple/core/time_series.py
def copy(self):
    """Copy the data, index and time support"""
    return self.__class__(
        t=self.index.copy(), d=self.values.copy(), time_support=self.time_support
    )

find_support

find_support(min_gap, time_units='s')

find the smallest (to a min_gap resolution) IntervalSet containing all the times in the Tsd

Parameters:

Name Type Description Default
min_gap float or int

minimal interval between timestamps

required
time_units str

Time units of min gap

's'

Returns:

Type Description
IntervalSet

Description

Source code in pynapple/core/base_class.py
def find_support(self, min_gap, time_units="s"):
    """
    find the smallest (to a min_gap resolution) IntervalSet containing all the times in the Tsd

    Parameters
    ----------
    min_gap : float or int
        minimal interval between timestamps
    time_units : str, optional
        Time units of min gap

    Returns
    -------
    IntervalSet
        Description
    """
    assert isinstance(min_gap, Number), "min_gap should be a float or int"
    min_gap = TsIndex.format_timestamps(np.array([min_gap]), time_units)[0]
    time_array = self.index.values

    starts = [time_array[0]]
    ends = []
    for i in range(len(time_array) - 1):
        if (time_array[i + 1] - time_array[i]) > min_gap:
            ends.append(time_array[i] + 1e-6)
            starts.append(time_array[i + 1])

    ends.append(time_array[-1] + 1e-6)

    return IntervalSet(start=starts, end=ends)

get

get(start, end=None, time_units='s')

Slice the time series from start to end such that all the timestamps satisfy start<=t<=end. If end is None, only the timepoint closest to start is returned.

By default, the time support doesn't change. If you want to change the time support, use the restrict function.

Parameters:

Name Type Description Default
start float or int

The start (or closest time point if end is None)

required
end float or int or None

The end

None
Source code in pynapple/core/base_class.py
def get(self, start, end=None, time_units="s"):
    """Slice the time series from `start` to `end` such that all the timestamps satisfy `start<=t<=end`.
    If `end` is None, only the timepoint closest to `start` is returned.

    By default, the time support doesn't change. If you want to change the time support, use the `restrict` function.

    Parameters
    ----------
    start : float or int
        The start (or closest time point if `end` is None)
    end : float or int or None
        The end
    """
    assert isinstance(start, Number), "start should be a float or int"
    time_array = self.index.values

    if end is None:
        start = TsIndex.format_timestamps(np.array([start]), time_units)[0]
        idx = int(np.searchsorted(time_array, start))
        if idx == 0:
            return self[idx]
        elif idx >= self.shape[0]:
            return self[-1]
        else:
            if start - time_array[idx - 1] < time_array[idx] - start:
                return self[idx - 1]
            else:
                return self[idx]
    else:
        assert isinstance(end, Number), "end should be a float or int"
        assert start < end, "Start should not precede end"
        start, end = TsIndex.format_timestamps(np.array([start, end]), time_units)
        idx_start = np.searchsorted(time_array, start)
        idx_end = np.searchsorted(time_array, end, side="right")
        return self[idx_start:idx_end]

__getattr__

__getattr__(name)

Allow numpy functions to be attached as attributes of Tsd objects

Source code in pynapple/core/time_series.py
def __getattr__(self, name):
    """Allow numpy functions to be attached as attributes of Tsd objects"""
    if hasattr(np, name):
        np_func = getattr(np, name)

        def method(*args, **kwargs):
            return np_func(self, *args, **kwargs)

        return method

    raise AttributeError(
        "Time series object does not have the attribute {}".format(name)
    )

as_array

as_array()

Return the data as a numpy.ndarray

Returns:

Name Type Description
out ndarray

_

Source code in pynapple/core/time_series.py
def as_array(self):
    """
    Return the data as a numpy.ndarray

    Returns
    -------
    out: numpy.ndarray
        _
    """
    return self.values

data

data()

Return the data as a numpy.ndarray

Returns:

Name Type Description
out ndarray

_

Source code in pynapple/core/time_series.py
def data(self):
    """
    Return the data as a numpy.ndarray

    Returns
    -------
    out: numpy.ndarray
        _
    """
    return self.values

to_numpy

to_numpy()

Return the data as a numpy.ndarray. Mostly useful for matplotlib plotting when calling plot(tsd)

Source code in pynapple/core/time_series.py
def to_numpy(self):
    """
    Return the data as a numpy.ndarray. Mostly useful for matplotlib plotting when calling `plot(tsd)`
    """
    return self.values

bin_average

bin_average(bin_size, ep=None, time_units='s')

Bin the data by averaging points within bin_size bin_size should be seconds unless specified. If no epochs is passed, the data will be binned based on the time support.

Parameters:

Name Type Description Default
bin_size float

The bin size (default is second)

required
ep None or IntervalSet

IntervalSet to restrict the operation

None
time_units str

Time units of bin size ('us', 'ms', 's' [default])

's'

Returns:

Name Type Description
out (Tsd, TsdFrame, TsdTensor)

A Tsd object indexed by the center of the bins and holding the averaged data points.

Examples:

This example shows how to bin data within bins of 0.1 second.

>>> import pynapple as nap
>>> import numpy as np
>>> tsd = nap.Tsd(t=np.arange(100), d=np.random.rand(100))
>>> bintsd = tsd.bin_average(0.1)

An epoch can be specified:

>>> ep = nap.IntervalSet(start = 10, end = 80, time_units = 's')
>>> bintsd = tsd.bin_average(0.1, ep=ep)

And bintsd automatically inherit ep as time support:

>>> bintsd.time_support
>>>    start    end
>>> 0  10.0     80.0
Source code in pynapple/core/time_series.py
def bin_average(self, bin_size, ep=None, time_units="s"):
    """
    Bin the data by averaging points within bin_size
    bin_size should be seconds unless specified.
    If no epochs is passed, the data will be binned based on the time support.

    Parameters
    ----------
    bin_size : float
        The bin size (default is second)
    ep : None or IntervalSet, optional
        IntervalSet to restrict the operation
    time_units : str, optional
        Time units of bin size ('us', 'ms', 's' [default])

    Returns
    -------
    out: Tsd, TsdFrame, TsdTensor
        A Tsd object indexed by the center of the bins and holding the averaged data points.

    Examples
    --------
    This example shows how to bin data within bins of 0.1 second.

    >>> import pynapple as nap
    >>> import numpy as np
    >>> tsd = nap.Tsd(t=np.arange(100), d=np.random.rand(100))
    >>> bintsd = tsd.bin_average(0.1)

    An epoch can be specified:

    >>> ep = nap.IntervalSet(start = 10, end = 80, time_units = 's')
    >>> bintsd = tsd.bin_average(0.1, ep=ep)

    And bintsd automatically inherit ep as time support:

    >>> bintsd.time_support
    >>>    start    end
    >>> 0  10.0     80.0
    """
    if not isinstance(ep, IntervalSet):
        ep = self.time_support

    bin_size = TsIndex.format_timestamps(np.array([bin_size]), time_units)[0]

    time_array = self.index.values
    data_array = self.values
    starts = ep.start
    ends = ep.end
    if data_array.ndim > 1:
        t, d = jitbin_array(time_array, data_array, starts, ends, bin_size)
    else:
        t, d = jitbin(time_array, data_array, starts, ends, bin_size)

    kwargs = {}
    if hasattr(self, "columns"):
        kwargs["columns"] = self.columns
    return self.__class__(t=t, d=d, time_support=ep, **kwargs)

dropna

dropna(update_time_support=True)

Drop every rows containing NaNs. By default, the time support is updated to start and end around the time points that are non NaNs. To change this behavior, you can set update_time_support=False.

Parameters:

Name Type Description Default
update_time_support bool
True

Returns:

Type Description
(Tsd, TsdFrame or TsdTensor)

The time series without the NaNs

Source code in pynapple/core/time_series.py
def dropna(self, update_time_support=True):
    """Drop every rows containing NaNs. By default, the time support is updated to start and end around the time points that are non NaNs.
    To change this behavior, you can set update_time_support=False.

    Parameters
    ----------
    update_time_support : bool, optional

    Returns
    -------
    Tsd, TsdFrame or TsdTensor
        The time series without the NaNs
    """
    index_nan = np.any(np.isnan(self.values), axis=tuple(range(1, self.ndim)))
    if np.all(index_nan):  # In case it's only NaNs
        return self.__class__(
            t=np.array([]), d=np.empty(tuple([0] + [d for d in self.shape[1:]]))
        )

    elif np.any(index_nan):
        if update_time_support:
            time_array = self.index.values
            starts, ends = jitremove_nan(time_array, index_nan)

            to_fix = starts == ends
            if np.any(to_fix):
                ends[
                    to_fix
                ] += 1e-6  # adding 1 millisecond in case of a single point

            ep = IntervalSet(starts, ends)

            return self.__class__(
                t=time_array[~index_nan], d=self.values[~index_nan], time_support=ep
            )

        else:
            return self[~index_nan]

    else:
        return self

convolve

convolve(array, ep=None, trim='both')

Return the discrete linear convolution of the time series with a one dimensional sequence.

A parameter ep can control the epochs for which the convolution will apply. Otherwise the convolution is made over the time support.

This function assume a constant sampling rate of the time series.

The only mode supported is full. The returned object is trimmed to match the size of the original object. The parameter trim controls which side the trimming operates. Default is 'both'.

See the numpy documentation here : https://numpy.org/doc/stable/reference/generated/numpy.convolve.html

Parameters:

Name Type Description Default
array ndarray

One dimensional input array

required
ep None

The epochs to apply the convolution

None
trim str

The side on which to trim the output of the convolution ('left', 'right', 'both' [default])

'both'

Returns:

Type Description
(Tsd, TsdFrame or TsdTensor)

The convolved time series

Source code in pynapple/core/time_series.py
def convolve(self, array, ep=None, trim="both"):
    """Return the discrete linear convolution of the time series with a one dimensional sequence.

    A parameter ep can control the epochs for which the convolution will apply. Otherwise the convolution is made over the time support.

    This function assume a constant sampling rate of the time series.

    The only mode supported is full. The returned object is trimmed to match the size of the original object. The parameter trim controls which side the trimming operates. Default is 'both'.

    See the numpy documentation here : https://numpy.org/doc/stable/reference/generated/numpy.convolve.html

    Parameters
    ----------
    array : np.ndarray
        One dimensional input array
    ep : None, optional
        The epochs to apply the convolution
    trim : str, optional
        The side on which to trim the output of the convolution ('left', 'right', 'both' [default])

    Returns
    -------
    Tsd, TsdFrame or TsdTensor
        The convolved time series
    """
    assert isinstance(array, np.ndarray), "Input should be a 1-d numpy array."
    assert array.ndim == 1, "Input should be a one dimensional array."
    assert trim in [
        "both",
        "left",
        "right",
    ], "Unknow argument. trim should be 'both', 'left' or 'right'."

    if ep is None:
        ep = self.time_support

    time_array = self.index.values
    data_array = self.values
    starts = ep.start
    ends = ep.end

    if data_array.ndim == 1:
        new_data_array = np.zeros(data_array.shape)
        k = array.shape[0]
        for s, e in zip(starts, ends):
            idx_s = np.searchsorted(time_array, s)
            idx_e = np.searchsorted(time_array, e, side="right")

            t = idx_e - idx_s
            if trim == "left":
                cut = (k - 1, t + k - 1)
            elif trim == "right":
                cut = (0, t)
            else:
                cut = ((1 - k % 2) + (k - 1) // 2, t + k - 1 - ((k - 1) // 2))
            # scipy is actually faster for Tsd
            new_data_array[idx_s:idx_e] = signal.convolve(
                data_array[idx_s:idx_e], array
            )[cut[0] : cut[1]]

        return self.__class__(t=time_array, d=new_data_array, time_support=ep)
    else:
        new_data_array = np.zeros(data_array.shape)
        for s, e in zip(starts, ends):
            idx_s = np.searchsorted(time_array, s)
            idx_e = np.searchsorted(time_array, e, side="right")
            new_data_array[idx_s:idx_e] = pjitconvolve(
                data_array[idx_s:idx_e], array, trim=trim
            )

        return self.__class__(t=time_array, d=new_data_array, time_support=ep)

smooth

smooth(
    std,
    windowsize=None,
    time_units="s",
    size_factor=100,
    norm=True,
)

Smooth a time series with a gaussian kernel.

std is the standard deviation of the gaussian kernel in units of time. If only std is passed, the function will compute the standard deviation and size in number of time points automatically based on the sampling rate of the time series. For example, if the time series tsd has a sample rate of 100 Hz and std is 50 ms, the standard deviation will be converted to an integer through tsd.rate * std = int(100 * 0.05) = 5.

If windowsize is None, the function will select a kernel size as 100 times the std in number of time points. This behavior can be controlled with the parameter size_factor.

norm set to True normalizes the gaussian kernel to sum to 1.

In the following example, a time series tsd with a sampling rate of 100 Hz is convolved with a gaussian kernel. The standard deviation is 0.05 second and the windowsize is 2 second. When instantiating the gaussian kernel from scipy, it corresponds to parameters M = 200 and std=5

>>> tsd.smooth(std=0.05, windowsize=2, time_units='s', norm=False)

This line is equivalent to :

>>> from scipy.signal.windows import gaussian
>>> kernel = gaussian(M = 200, std=5)
>>> tsd.convolve(window)

It is generally a good idea to visualize the kernel before applying any convolution.

See the scipy documentation for the gaussian window

Parameters:

Name Type Description Default
std Number

Standard deviation in units of time

required
windowsize Number

Size of the gaussian window in units of time.

None
time_units str

The time units in which std and windowsize are specified ('us', 'ms', 's' [default]).

's'
size_factor int

How long should be the kernel size as a function of the standard deviation. Default is 100. Bypassed if windowsize is used.

100
norm bool

Whether to normalized the gaussian kernel or not. Default is True.

True

Returns:

Type Description
(Tsd, TsdFrame, TsdTensor)

Time series convolved with a gaussian kernel

Source code in pynapple/core/time_series.py
def smooth(self, std, windowsize=None, time_units="s", size_factor=100, norm=True):
    """Smooth a time series with a gaussian kernel.

    `std` is the standard deviation of the gaussian kernel in units of time.
    If only `std` is passed, the function will compute the standard deviation and size in number
    of time points automatically based on the sampling rate of the time series.
    For example, if the time series `tsd` has a sample rate of 100 Hz and `std` is 50 ms,
    the standard deviation will be converted to an integer through
    `tsd.rate * std = int(100 * 0.05) = 5`.

    If `windowsize` is None, the function will select a kernel size as 100 times
    the std in number of time points. This behavior can be controlled with the
    parameter `size_factor`.

    `norm` set to True normalizes the gaussian kernel to sum to 1.

    In the following example, a time series `tsd` with a sampling rate of 100 Hz
    is convolved with a gaussian kernel. The standard deviation is
    0.05 second and the windowsize is 2 second. When instantiating the gaussian kernel
    from scipy, it corresponds to parameters `M = 200` and `std=5`

        >>> tsd.smooth(std=0.05, windowsize=2, time_units='s', norm=False)

    This line is equivalent to :

        >>> from scipy.signal.windows import gaussian
        >>> kernel = gaussian(M = 200, std=5)
        >>> tsd.convolve(window)

    It is generally a good idea to visualize the kernel before applying any convolution.

    See the scipy documentation for the [gaussian window](https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.windows.gaussian.html)

    Parameters
    ----------
    std : Number
        Standard deviation in units of time
    windowsize : Number
        Size of the gaussian window in units of time.
    time_units : str, optional
        The time units in which std and windowsize are specified ('us', 'ms', 's' [default]).
    size_factor : int, optional
        How long should be the kernel size as a function of the standard deviation. Default is 100.
        Bypassed if windowsize is used.
    norm : bool, optional
        Whether to normalized the gaussian kernel or not. Default is `True`.

    Returns
    -------
    Tsd, TsdFrame, TsdTensor
        Time series convolved with a gaussian kernel

    """
    assert isinstance(std, (int, float)), "std should be type int or float"
    assert isinstance(size_factor, int), "size_factor should be of type int"
    assert isinstance(norm, bool), "norm should be of type boolean"
    assert isinstance(time_units, str), "time_units should be of type str"

    std = TsIndex.format_timestamps(np.array([std]), time_units)[0]
    std_size = int(self.rate * std)

    if windowsize is not None:
        assert isinstance(
            windowsize, (int, float)
        ), "windowsize should be type int or float"
        windowsize = TsIndex.format_timestamps(np.array([windowsize]), time_units)[
            0
        ]
        M = int(self.rate * windowsize)
    else:
        M = std_size * size_factor

    window = signal.windows.gaussian(M=M, std=std_size)

    if norm:
        window = window / window.sum()

    return self.convolve(window)

interpolate

interpolate(ts, ep=None, left=None, right=None)

Wrapper of the numpy linear interpolation method. See numpy interpolate for an explanation of the parameters. The argument ts should be Ts, Tsd, TsdFrame, TsdTensor to ensure interpolating from sorted timestamps in the right unit,

Parameters:

Name Type Description Default
ts (Ts, Tsd, TsdFrame or TsdTensor)

The object holding the timestamps

required
ep IntervalSet

The epochs to use to interpolate. If None, the time support of Tsd is used.

None
left None

Value to return for ts < tsd[0], default is tsd[0].

None
right None

Value to return for ts > tsd[-1], default is tsd[-1].

None
Source code in pynapple/core/time_series.py
def interpolate(self, ts, ep=None, left=None, right=None):
    """Wrapper of the numpy linear interpolation method. See [numpy interpolate](https://numpy.org/doc/stable/reference/generated/numpy.interp.html)
    for an explanation of the parameters.
    The argument ts should be Ts, Tsd, TsdFrame, TsdTensor to ensure interpolating from sorted timestamps in the right unit,

    Parameters
    ----------
    ts : Ts, Tsd, TsdFrame or TsdTensor
        The object holding the timestamps
    ep : IntervalSet, optional
        The epochs to use to interpolate. If None, the time support of Tsd is used.
    left : None, optional
        Value to return for ts < tsd[0], default is tsd[0].
    right : None, optional
        Value to return for ts > tsd[-1], default is tsd[-1].
    """
    assert isinstance(
        ts, Base
    ), "First argument should be an instance of Ts, Tsd, TsdFrame or TsdTensor"

    if not isinstance(ep, IntervalSet):
        ep = self.time_support

    new_t = ts.restrict(ep).index

    new_shape = (
        len(new_t) if self.values.ndim == 1 else (len(new_t),) + self.shape[1:]
    )
    new_d = np.full(new_shape, np.nan)

    start = 0
    for i in range(len(ep)):
        t = ts.get(ep[i, 0], ep[i, 1])
        tmp = self.get(ep[i, 0], ep[i, 1])

        if len(t) and len(tmp):
            if self.values.ndim == 1:
                new_d[start : start + len(t)] = np.interp(
                    t.index.values,
                    tmp.index.values,
                    tmp.values,
                    left=left,
                    right=right,
                )
            else:
                interpolated_values = np.apply_along_axis(
                    lambda row: np.interp(
                        t.index.values,
                        tmp.index.values,
                        row,
                        left=left,
                        right=right,
                    ),
                    0,
                    tmp.values,
                )
                new_d[start : start + len(t), ...] = interpolated_values

        start += len(t)
    kwargs_dict = dict(time_support=ep)
    if hasattr(self, "columns"):
        kwargs_dict["columns"] = self.columns
    return self.__class__(t=new_t, d=new_d, **kwargs_dict)

__init__

__init__(t, d, time_units='s', time_support=None, **kwargs)

TsdTensor initializer

Parameters:

Name Type Description Default
t ndarray

the time index t

required
d ndarray

The data

required
time_units str

The time units in which times are specified ('us', 'ms', 's' [default]).

's'
time_support IntervalSet

The time support of the TsdFrame object

None
Source code in pynapple/core/time_series.py
def __init__(self, t, d, time_units="s", time_support=None, **kwargs):
    """
    TsdTensor initializer

    Parameters
    ----------
    t : numpy.ndarray
        the time index t
    d : numpy.ndarray
        The data
    time_units : str, optional
        The time units in which times are specified ('us', 'ms', 's' [default]).
    time_support : IntervalSet, optional
        The time support of the TsdFrame object
    """
    super().__init__(t, d, time_units, time_support)

    assert (
        self.values.ndim >= 3
    ), "Data should have more than 2 dimensions. If ndim < 3, use TsdFrame or Tsd object"

    self.nap_class = self.__class__.__name__
    self._initialized = True

save

save(filename)

Save TsdTensor object in npz format. The file will contain the timestamps, the data and the time support.

The main purpose of this function is to save small/medium sized time series objects. For example, you extracted several channels from your recording and filtered them. You can save the filtered channels as a npz to avoid reprocessing it.

You can load the object with nap.load_file. Keys are 't', 'd', 'start', 'end', 'type' and 'columns' for columns names.

Parameters:

Name Type Description Default
filename str

The filename

required

Examples:

>>> import pynapple as nap
>>> import numpy as np
>>> tsdtensor = nap.TsdTensor(t=np.array([0., 1.]), d = np.zeros((2,3,4)))
>>> tsdtensor.save("my_path/my_tsdtensor.npz")

To load you file, you can use the nap.load_file function :

>>> tsdtensor = nap.load_file("my_path/my_tsdtensor.npz")

Raises:

Type Description
RuntimeError

If filename is not str, path does not exist or filename is a directory.

Source code in pynapple/core/time_series.py
def save(self, filename):
    """
    Save TsdTensor object in npz format. The file will contain the timestamps, the
    data and the time support.

    The main purpose of this function is to save small/medium sized time series
    objects. For example, you extracted several channels from your recording and
    filtered them. You can save the filtered channels as a npz to avoid
    reprocessing it.

    You can load the object with `nap.load_file`. Keys are 't', 'd', 'start', 'end', 'type'
    and 'columns' for columns names.

    Parameters
    ----------
    filename : str
        The filename

    Examples
    --------
    >>> import pynapple as nap
    >>> import numpy as np
    >>> tsdtensor = nap.TsdTensor(t=np.array([0., 1.]), d = np.zeros((2,3,4)))
    >>> tsdtensor.save("my_path/my_tsdtensor.npz")

    To load you file, you can use the `nap.load_file` function :

    >>> tsdtensor = nap.load_file("my_path/my_tsdtensor.npz")

    Raises
    ------
    RuntimeError
        If filename is not str, path does not exist or filename is a directory.
    """
    if not isinstance(filename, str):
        raise RuntimeError("Invalid type; please provide filename as string")

    if os.path.isdir(filename):
        raise RuntimeError(
            "Invalid filename input. {} is directory.".format(filename)
        )

    if not filename.lower().endswith(".npz"):
        filename = filename + ".npz"

    dirname = os.path.dirname(filename)

    if len(dirname) and not os.path.exists(dirname):
        raise RuntimeError(
            "Path {} does not exist.".format(os.path.dirname(filename))
        )

    np.savez(
        filename,
        t=self.index.values,
        d=self.values,
        start=self.time_support.start,
        end=self.time_support.end,
        type=np.array([self.nap_class], dtype=np.str_),
    )

    return

TsdFrame

Bases: BaseTsd

TsdFrame

Attributes:

Name Type Description
rate float

Frequency of the time series (Hz) computed over the time support

time_support IntervalSet

The time support of the time series

Source code in pynapple/core/time_series.py
class TsdFrame(BaseTsd):
    """
    TsdFrame

    Attributes
    ----------
    rate : float
        Frequency of the time series (Hz) computed over the time support
    time_support : IntervalSet
        The time support of the time series
    """

    def __init__(self, t, d=None, time_units="s", time_support=None, columns=None):
        """
        TsdFrame initializer
        A pandas.DataFrame can be passed directly

        Parameters
        ----------
        t : numpy.ndarray or pandas.DataFrame
            the time index t,  or a pandas.DataFrame (if d is None)
        d : numpy.ndarray
            The data
        time_units : str, optional
            The time units in which times are specified ('us', 'ms', 's' [default]).
        time_support : IntervalSet, optional
            The time support of the TsdFrame object
        columns : iterables
            Column names
        """

        c = columns

        if isinstance(t, pd.DataFrame):
            d = t.values
            c = t.columns.values
            t = t.index.values
        else:
            assert d is not None, "Missing argument d when initializing TsdFrame"

        super().__init__(t, d, time_units, time_support)

        assert self.values.ndim <= 2, "Data should be 1 or 2 dimensional."

        if self.values.ndim == 1:
            self.values = np.expand_dims(self.values, 1)

        if c is None or len(c) != self.values.shape[1]:
            c = np.arange(self.values.shape[1], dtype="int")
        else:
            assert (
                len(c) == self.values.shape[1]
            ), "Number of columns should match the second dimension of d"

        self.columns = pd.Index(c)
        self.nap_class = self.__class__.__name__
        self._initialized = True

    @property
    def loc(self):
        return _TsdFrameSliceHelper(self)

    def __repr__(self):
        headers = ["Time (s)"] + [str(k) for k in self.columns]
        bottom = "dtype: {}".format(self.dtype) + ", shape: {}".format(self.shape)

        cols, rows = _get_terminal_size()
        max_cols = np.maximum(cols // 100, 5)
        max_rows = np.maximum(rows - 10, 2)

        if self.shape[1] > max_cols:
            headers = headers[0 : max_cols + 1] + ["..."]

        def round_if_float(x):
            if isinstance(x, float):
                return np.round(x, 5)
            else:
                return x

        with warnings.catch_warnings():
            warnings.simplefilter("ignore")
            if len(self):
                table = []
                end = ["..."] if self.shape[1] > max_cols else []
                if len(self) > max_rows:
                    n_rows = max_rows // 2
                    for i, array in zip(
                        self.index[0:n_rows], self.values[0:n_rows, 0:max_cols]
                    ):
                        table.append([i] + [round_if_float(k) for k in array] + end)
                    table.append(["..."])
                    for i, array in zip(
                        self.index[-n_rows:],
                        self.values[
                            self.values.shape[0] - n_rows : self.values.shape[0],
                            0:max_cols,
                        ],
                    ):
                        table.append([i] + [round_if_float(k) for k in array] + end)
                    return (
                        tabulate(table, headers=headers, colalign=("left",))
                        + "\n"
                        + bottom
                    )
                else:
                    for i, array in zip(self.index, self.values[:, 0:max_cols]):
                        table.append([i] + [round_if_float(k) for k in array] + end)
                    return (
                        tabulate(table, headers=headers, colalign=("left",))
                        + "\n"
                        + bottom
                    )
            else:
                return tabulate([], headers=headers) + "\n" + bottom

    def __setitem__(self, key, value):
        try:
            if isinstance(key, str):
                new_key = self.columns.get_indexer([key])
                self.values.__setitem__((slice(None, None, None), new_key[0]), value)
            elif hasattr(key, "__iter__") and all([isinstance(k, str) for k in key]):
                new_key = self.columns.get_indexer(key)
                self.values.__setitem__((slice(None, None, None), new_key), value)
            else:
                self.values.__setitem__(key, value)
        except IndexError:
            raise IndexError

    def __getitem__(self, key, *args, **kwargs):
        if (
            isinstance(key, str)
            or hasattr(key, "__iter__")
            and all([isinstance(k, str) for k in key])
        ):
            return self.loc[key]
        else:
            # return super().__getitem__(key, *args, **kwargs)
            output = self.values.__getitem__(key)
            if isinstance(key, tuple):
                index = self.index.__getitem__(key[0])
            else:
                index = self.index.__getitem__(key)

            if isinstance(index, Number):
                index = np.array([index])

            if all(isinstance(a, np.ndarray) for a in [index, output]):
                if output.shape[0] == index.shape[0]:
                    return _get_class(output)(
                        t=index, d=output, time_support=self.time_support, **kwargs
                    )
                else:
                    return output
            else:
                return output

    def as_dataframe(self):
        """
        Convert the TsdFrame object to a pandas.DataFrame object.

        Returns
        -------
        out: pandas.DataFrame
            _
        """
        return pd.DataFrame(
            index=self.index.values, data=self.values, columns=self.columns
        )

    def as_units(self, units="s"):
        """
        Returns a DataFrame with time expressed in the desired unit.

        Parameters
        ----------
        units : str, optional
            ('us', 'ms', 's' [default])

        Returns
        -------
        pandas.DataFrame
            the series object with adjusted times
        """
        t = self.index.in_units(units)
        if units == "us":
            t = t.astype(np.int64)

        df = pd.DataFrame(index=t, data=self.values)
        df.index.name = "Time (" + str(units) + ")"
        df.columns = self.columns.copy()
        return df

    def save(self, filename):
        """
        Save TsdFrame object in npz format. The file will contain the timestamps, the
        data and the time support.

        The main purpose of this function is to save small/medium sized time series
        objects. For example, you extracted several channels from your recording and
        filtered them. You can save the filtered channels as a npz to avoid
        reprocessing it.

        You can load the object with `nap.load_file`. Keys are 't', 'd', 'start', 'end', 'type'
        and 'columns' for columns names.

        Parameters
        ----------
        filename : str
            The filename

        Examples
        --------
        >>> import pynapple as nap
        >>> import numpy as np
        >>> tsdframe = nap.TsdFrame(t=np.array([0., 1.]), d = np.array([[2, 3],[4,5]]), columns=['a', 'b'])
        >>> tsdframe.save("my_path/my_tsdframe.npz")

        To load you file, you can use the `nap.load_file` function :

        >>> tsdframe = nap.load_file("my_path/my_tsdframe.npz")
        >>> tsdframe
                  a  b
        Time (s)
        0.0       2  3
        1.0       4  5


        Raises
        ------
        RuntimeError
            If filename is not str, path does not exist or filename is a directory.
        """
        if not isinstance(filename, str):
            raise RuntimeError("Invalid type; please provide filename as string")

        if os.path.isdir(filename):
            raise RuntimeError(
                "Invalid filename input. {} is directory.".format(filename)
            )

        if not filename.lower().endswith(".npz"):
            filename = filename + ".npz"

        dirname = os.path.dirname(filename)

        if len(dirname) and not os.path.exists(dirname):
            raise RuntimeError(
                "Path {} does not exist.".format(os.path.dirname(filename))
            )

        cols_name = self.columns
        if cols_name.dtype == np.dtype("O"):
            cols_name = cols_name.astype(str)

        np.savez(
            filename,
            t=self.index.values,
            d=self.values,
            start=self.time_support.start,
            end=self.time_support.end,
            columns=cols_name,
            type=np.array(["TsdFrame"], dtype=np.str_),
        )

        return

__setattr__

__setattr__(name, value)

Object is immutable

Source code in pynapple/core/base_class.py
def __setattr__(self, name, value):
    """Object is immutable"""
    if self._initialized:
        raise RuntimeError(
            "Changing directly attributes is not permitted for {}.".format(
                self.nap_class
            )
        )
    else:
        object.__setattr__(self, name, value)

times

times(units='s')

The time index of the object, returned as np.double in the desired time units.

Parameters:

Name Type Description Default
units str

('us', 'ms', 's' [default])

's'

Returns:

Name Type Description
out ndarray

the time indexes

Source code in pynapple/core/base_class.py
def times(self, units="s"):
    """
    The time index of the object, returned as np.double in the desired time units.

    Parameters
    ----------
    units : str, optional
        ('us', 'ms', 's' [default])

    Returns
    -------
    out: numpy.ndarray
        the time indexes
    """
    return self.index.in_units(units)

start_time

start_time(units='s')

The first time index in the time series object

Parameters:

Name Type Description Default
units str

('us', 'ms', 's' [default])

's'

Returns:

Name Type Description
out float64

_

Source code in pynapple/core/base_class.py
def start_time(self, units="s"):
    """
    The first time index in the time series object

    Parameters
    ----------
    units : str, optional
        ('us', 'ms', 's' [default])

    Returns
    -------
    out: numpy.float64
        _
    """
    if len(self.index):
        return self.times(units=units)[0]
    else:
        return None

end_time

end_time(units='s')

The last time index in the time series object

Parameters:

Name Type Description Default
units str

('us', 'ms', 's' [default])

's'

Returns:

Name Type Description
out float64

_

Source code in pynapple/core/base_class.py
def end_time(self, units="s"):
    """
    The last time index in the time series object

    Parameters
    ----------
    units : str, optional
        ('us', 'ms', 's' [default])

    Returns
    -------
    out: numpy.float64
        _
    """
    if len(self.index):
        return self.times(units=units)[-1]
    else:
        return None

restrict

restrict(iset)

Restricts a time series object to a set of time intervals delimited by an IntervalSet object

Parameters:

Name Type Description Default
iset IntervalSet

the IntervalSet object

required

Returns:

Name Type Description
out (Ts, Tsd, TsdFrame or TsdTensor)

Tsd object restricted to ep

Examples:

The Ts object is restrict to the intervals defined by ep.

>>> import pynapple as nap
>>> import numpy as np
>>> t = np.unique(np.sort(np.random.randint(0, 1000, 100)))
>>> ts = nap.Ts(t=t, time_units='s')
>>> ep = nap.IntervalSet(start=0, end=500, time_units='s')
>>> newts = ts.restrict(ep)

The time support of newts automatically inherit the epochs defined by ep.

>>> newts.time_support
    start    end
0    0.0  500.0
Source code in pynapple/core/base_class.py
def restrict(self, iset):
    """
    Restricts a time series object to a set of time intervals delimited by an IntervalSet object

    Parameters
    ----------
    iset : IntervalSet
        the IntervalSet object

    Returns
    -------
    out: Ts, Tsd, TsdFrame or TsdTensor
        Tsd object restricted to ep

    Examples
    --------
    The Ts object is restrict to the intervals defined by ep.

    >>> import pynapple as nap
    >>> import numpy as np
    >>> t = np.unique(np.sort(np.random.randint(0, 1000, 100)))
    >>> ts = nap.Ts(t=t, time_units='s')
    >>> ep = nap.IntervalSet(start=0, end=500, time_units='s')
    >>> newts = ts.restrict(ep)

    The time support of newts automatically inherit the epochs defined by ep.

    >>> newts.time_support
        start    end
    0    0.0  500.0

    """
    assert isinstance(iset, IntervalSet), "Argument should be IntervalSet"

    time_array = self.index.values
    starts = iset.start
    ends = iset.end

    if hasattr(self, "values"):
        data_array = self.values
        t, d = jitrestrict(time_array, data_array, starts, ends)

        kwargs = {}
        if hasattr(self, "columns"):
            kwargs["columns"] = self.columns

        return self.__class__(t=t, d=d, time_support=iset, **kwargs)

    else:
        t = jittsrestrict(time_array, starts, ends)
        return self.__class__(t=t, time_support=iset)

copy

copy()

Copy the data, index and time support

Source code in pynapple/core/time_series.py
def copy(self):
    """Copy the data, index and time support"""
    return self.__class__(
        t=self.index.copy(), d=self.values.copy(), time_support=self.time_support
    )

find_support

find_support(min_gap, time_units='s')

find the smallest (to a min_gap resolution) IntervalSet containing all the times in the Tsd

Parameters:

Name Type Description Default
min_gap float or int

minimal interval between timestamps

required
time_units str

Time units of min gap

's'

Returns:

Type Description
IntervalSet

Description

Source code in pynapple/core/base_class.py
def find_support(self, min_gap, time_units="s"):
    """
    find the smallest (to a min_gap resolution) IntervalSet containing all the times in the Tsd

    Parameters
    ----------
    min_gap : float or int
        minimal interval between timestamps
    time_units : str, optional
        Time units of min gap

    Returns
    -------
    IntervalSet
        Description
    """
    assert isinstance(min_gap, Number), "min_gap should be a float or int"
    min_gap = TsIndex.format_timestamps(np.array([min_gap]), time_units)[0]
    time_array = self.index.values

    starts = [time_array[0]]
    ends = []
    for i in range(len(time_array) - 1):
        if (time_array[i + 1] - time_array[i]) > min_gap:
            ends.append(time_array[i] + 1e-6)
            starts.append(time_array[i + 1])

    ends.append(time_array[-1] + 1e-6)

    return IntervalSet(start=starts, end=ends)

get

get(start, end=None, time_units='s')

Slice the time series from start to end such that all the timestamps satisfy start<=t<=end. If end is None, only the timepoint closest to start is returned.

By default, the time support doesn't change. If you want to change the time support, use the restrict function.

Parameters:

Name Type Description Default
start float or int

The start (or closest time point if end is None)

required
end float or int or None

The end

None
Source code in pynapple/core/base_class.py
def get(self, start, end=None, time_units="s"):
    """Slice the time series from `start` to `end` such that all the timestamps satisfy `start<=t<=end`.
    If `end` is None, only the timepoint closest to `start` is returned.

    By default, the time support doesn't change. If you want to change the time support, use the `restrict` function.

    Parameters
    ----------
    start : float or int
        The start (or closest time point if `end` is None)
    end : float or int or None
        The end
    """
    assert isinstance(start, Number), "start should be a float or int"
    time_array = self.index.values

    if end is None:
        start = TsIndex.format_timestamps(np.array([start]), time_units)[0]
        idx = int(np.searchsorted(time_array, start))
        if idx == 0:
            return self[idx]
        elif idx >= self.shape[0]:
            return self[-1]
        else:
            if start - time_array[idx - 1] < time_array[idx] - start:
                return self[idx - 1]
            else:
                return self[idx]
    else:
        assert isinstance(end, Number), "end should be a float or int"
        assert start < end, "Start should not precede end"
        start, end = TsIndex.format_timestamps(np.array([start, end]), time_units)
        idx_start = np.searchsorted(time_array, start)
        idx_end = np.searchsorted(time_array, end, side="right")
        return self[idx_start:idx_end]

__getattr__

__getattr__(name)

Allow numpy functions to be attached as attributes of Tsd objects

Source code in pynapple/core/time_series.py
def __getattr__(self, name):
    """Allow numpy functions to be attached as attributes of Tsd objects"""
    if hasattr(np, name):
        np_func = getattr(np, name)

        def method(*args, **kwargs):
            return np_func(self, *args, **kwargs)

        return method

    raise AttributeError(
        "Time series object does not have the attribute {}".format(name)
    )

as_array

as_array()

Return the data as a numpy.ndarray

Returns:

Name Type Description
out ndarray

_

Source code in pynapple/core/time_series.py
def as_array(self):
    """
    Return the data as a numpy.ndarray

    Returns
    -------
    out: numpy.ndarray
        _
    """
    return self.values

data

data()

Return the data as a numpy.ndarray

Returns:

Name Type Description
out ndarray

_

Source code in pynapple/core/time_series.py
def data(self):
    """
    Return the data as a numpy.ndarray

    Returns
    -------
    out: numpy.ndarray
        _
    """
    return self.values

to_numpy

to_numpy()

Return the data as a numpy.ndarray. Mostly useful for matplotlib plotting when calling plot(tsd)

Source code in pynapple/core/time_series.py
def to_numpy(self):
    """
    Return the data as a numpy.ndarray. Mostly useful for matplotlib plotting when calling `plot(tsd)`
    """
    return self.values

bin_average

bin_average(bin_size, ep=None, time_units='s')

Bin the data by averaging points within bin_size bin_size should be seconds unless specified. If no epochs is passed, the data will be binned based on the time support.

Parameters:

Name Type Description Default
bin_size float

The bin size (default is second)

required
ep None or IntervalSet

IntervalSet to restrict the operation

None
time_units str

Time units of bin size ('us', 'ms', 's' [default])

's'

Returns:

Name Type Description
out (Tsd, TsdFrame, TsdTensor)

A Tsd object indexed by the center of the bins and holding the averaged data points.

Examples:

This example shows how to bin data within bins of 0.1 second.

>>> import pynapple as nap
>>> import numpy as np
>>> tsd = nap.Tsd(t=np.arange(100), d=np.random.rand(100))
>>> bintsd = tsd.bin_average(0.1)

An epoch can be specified:

>>> ep = nap.IntervalSet(start = 10, end = 80, time_units = 's')
>>> bintsd = tsd.bin_average(0.1, ep=ep)

And bintsd automatically inherit ep as time support:

>>> bintsd.time_support
>>>    start    end
>>> 0  10.0     80.0
Source code in pynapple/core/time_series.py
def bin_average(self, bin_size, ep=None, time_units="s"):
    """
    Bin the data by averaging points within bin_size
    bin_size should be seconds unless specified.
    If no epochs is passed, the data will be binned based on the time support.

    Parameters
    ----------
    bin_size : float
        The bin size (default is second)
    ep : None or IntervalSet, optional
        IntervalSet to restrict the operation
    time_units : str, optional
        Time units of bin size ('us', 'ms', 's' [default])

    Returns
    -------
    out: Tsd, TsdFrame, TsdTensor
        A Tsd object indexed by the center of the bins and holding the averaged data points.

    Examples
    --------
    This example shows how to bin data within bins of 0.1 second.

    >>> import pynapple as nap
    >>> import numpy as np
    >>> tsd = nap.Tsd(t=np.arange(100), d=np.random.rand(100))
    >>> bintsd = tsd.bin_average(0.1)

    An epoch can be specified:

    >>> ep = nap.IntervalSet(start = 10, end = 80, time_units = 's')
    >>> bintsd = tsd.bin_average(0.1, ep=ep)

    And bintsd automatically inherit ep as time support:

    >>> bintsd.time_support
    >>>    start    end
    >>> 0  10.0     80.0
    """
    if not isinstance(ep, IntervalSet):
        ep = self.time_support

    bin_size = TsIndex.format_timestamps(np.array([bin_size]), time_units)[0]

    time_array = self.index.values
    data_array = self.values
    starts = ep.start
    ends = ep.end
    if data_array.ndim > 1:
        t, d = jitbin_array(time_array, data_array, starts, ends, bin_size)
    else:
        t, d = jitbin(time_array, data_array, starts, ends, bin_size)

    kwargs = {}
    if hasattr(self, "columns"):
        kwargs["columns"] = self.columns
    return self.__class__(t=t, d=d, time_support=ep, **kwargs)

dropna

dropna(update_time_support=True)

Drop every rows containing NaNs. By default, the time support is updated to start and end around the time points that are non NaNs. To change this behavior, you can set update_time_support=False.

Parameters:

Name Type Description Default
update_time_support bool
True

Returns:

Type Description
(Tsd, TsdFrame or TsdTensor)

The time series without the NaNs

Source code in pynapple/core/time_series.py
def dropna(self, update_time_support=True):
    """Drop every rows containing NaNs. By default, the time support is updated to start and end around the time points that are non NaNs.
    To change this behavior, you can set update_time_support=False.

    Parameters
    ----------
    update_time_support : bool, optional

    Returns
    -------
    Tsd, TsdFrame or TsdTensor
        The time series without the NaNs
    """
    index_nan = np.any(np.isnan(self.values), axis=tuple(range(1, self.ndim)))
    if np.all(index_nan):  # In case it's only NaNs
        return self.__class__(
            t=np.array([]), d=np.empty(tuple([0] + [d for d in self.shape[1:]]))
        )

    elif np.any(index_nan):
        if update_time_support:
            time_array = self.index.values
            starts, ends = jitremove_nan(time_array, index_nan)

            to_fix = starts == ends
            if np.any(to_fix):
                ends[
                    to_fix
                ] += 1e-6  # adding 1 millisecond in case of a single point

            ep = IntervalSet(starts, ends)

            return self.__class__(
                t=time_array[~index_nan], d=self.values[~index_nan], time_support=ep
            )

        else:
            return self[~index_nan]

    else:
        return self

convolve

convolve(array, ep=None, trim='both')

Return the discrete linear convolution of the time series with a one dimensional sequence.

A parameter ep can control the epochs for which the convolution will apply. Otherwise the convolution is made over the time support.

This function assume a constant sampling rate of the time series.

The only mode supported is full. The returned object is trimmed to match the size of the original object. The parameter trim controls which side the trimming operates. Default is 'both'.

See the numpy documentation here : https://numpy.org/doc/stable/reference/generated/numpy.convolve.html

Parameters:

Name Type Description Default
array ndarray

One dimensional input array

required
ep None

The epochs to apply the convolution

None
trim str

The side on which to trim the output of the convolution ('left', 'right', 'both' [default])

'both'

Returns:

Type Description
(Tsd, TsdFrame or TsdTensor)

The convolved time series

Source code in pynapple/core/time_series.py
def convolve(self, array, ep=None, trim="both"):
    """Return the discrete linear convolution of the time series with a one dimensional sequence.

    A parameter ep can control the epochs for which the convolution will apply. Otherwise the convolution is made over the time support.

    This function assume a constant sampling rate of the time series.

    The only mode supported is full. The returned object is trimmed to match the size of the original object. The parameter trim controls which side the trimming operates. Default is 'both'.

    See the numpy documentation here : https://numpy.org/doc/stable/reference/generated/numpy.convolve.html

    Parameters
    ----------
    array : np.ndarray
        One dimensional input array
    ep : None, optional
        The epochs to apply the convolution
    trim : str, optional
        The side on which to trim the output of the convolution ('left', 'right', 'both' [default])

    Returns
    -------
    Tsd, TsdFrame or TsdTensor
        The convolved time series
    """
    assert isinstance(array, np.ndarray), "Input should be a 1-d numpy array."
    assert array.ndim == 1, "Input should be a one dimensional array."
    assert trim in [
        "both",
        "left",
        "right",
    ], "Unknow argument. trim should be 'both', 'left' or 'right'."

    if ep is None:
        ep = self.time_support

    time_array = self.index.values
    data_array = self.values
    starts = ep.start
    ends = ep.end

    if data_array.ndim == 1:
        new_data_array = np.zeros(data_array.shape)
        k = array.shape[0]
        for s, e in zip(starts, ends):
            idx_s = np.searchsorted(time_array, s)
            idx_e = np.searchsorted(time_array, e, side="right")

            t = idx_e - idx_s
            if trim == "left":
                cut = (k - 1, t + k - 1)
            elif trim == "right":
                cut = (0, t)
            else:
                cut = ((1 - k % 2) + (k - 1) // 2, t + k - 1 - ((k - 1) // 2))
            # scipy is actually faster for Tsd
            new_data_array[idx_s:idx_e] = signal.convolve(
                data_array[idx_s:idx_e], array
            )[cut[0] : cut[1]]

        return self.__class__(t=time_array, d=new_data_array, time_support=ep)
    else:
        new_data_array = np.zeros(data_array.shape)
        for s, e in zip(starts, ends):
            idx_s = np.searchsorted(time_array, s)
            idx_e = np.searchsorted(time_array, e, side="right")
            new_data_array[idx_s:idx_e] = pjitconvolve(
                data_array[idx_s:idx_e], array, trim=trim
            )

        return self.__class__(t=time_array, d=new_data_array, time_support=ep)

smooth

smooth(
    std,
    windowsize=None,
    time_units="s",
    size_factor=100,
    norm=True,
)

Smooth a time series with a gaussian kernel.

std is the standard deviation of the gaussian kernel in units of time. If only std is passed, the function will compute the standard deviation and size in number of time points automatically based on the sampling rate of the time series. For example, if the time series tsd has a sample rate of 100 Hz and std is 50 ms, the standard deviation will be converted to an integer through tsd.rate * std = int(100 * 0.05) = 5.

If windowsize is None, the function will select a kernel size as 100 times the std in number of time points. This behavior can be controlled with the parameter size_factor.

norm set to True normalizes the gaussian kernel to sum to 1.

In the following example, a time series tsd with a sampling rate of 100 Hz is convolved with a gaussian kernel. The standard deviation is 0.05 second and the windowsize is 2 second. When instantiating the gaussian kernel from scipy, it corresponds to parameters M = 200 and std=5

>>> tsd.smooth(std=0.05, windowsize=2, time_units='s', norm=False)

This line is equivalent to :

>>> from scipy.signal.windows import gaussian
>>> kernel = gaussian(M = 200, std=5)
>>> tsd.convolve(window)

It is generally a good idea to visualize the kernel before applying any convolution.

See the scipy documentation for the gaussian window

Parameters:

Name Type Description Default
std Number

Standard deviation in units of time

required
windowsize Number

Size of the gaussian window in units of time.

None
time_units str

The time units in which std and windowsize are specified ('us', 'ms', 's' [default]).

's'
size_factor int

How long should be the kernel size as a function of the standard deviation. Default is 100. Bypassed if windowsize is used.

100
norm bool

Whether to normalized the gaussian kernel or not. Default is True.

True

Returns:

Type Description
(Tsd, TsdFrame, TsdTensor)

Time series convolved with a gaussian kernel

Source code in pynapple/core/time_series.py
def smooth(self, std, windowsize=None, time_units="s", size_factor=100, norm=True):
    """Smooth a time series with a gaussian kernel.

    `std` is the standard deviation of the gaussian kernel in units of time.
    If only `std` is passed, the function will compute the standard deviation and size in number
    of time points automatically based on the sampling rate of the time series.
    For example, if the time series `tsd` has a sample rate of 100 Hz and `std` is 50 ms,
    the standard deviation will be converted to an integer through
    `tsd.rate * std = int(100 * 0.05) = 5`.

    If `windowsize` is None, the function will select a kernel size as 100 times
    the std in number of time points. This behavior can be controlled with the
    parameter `size_factor`.

    `norm` set to True normalizes the gaussian kernel to sum to 1.

    In the following example, a time series `tsd` with a sampling rate of 100 Hz
    is convolved with a gaussian kernel. The standard deviation is
    0.05 second and the windowsize is 2 second. When instantiating the gaussian kernel
    from scipy, it corresponds to parameters `M = 200` and `std=5`

        >>> tsd.smooth(std=0.05, windowsize=2, time_units='s', norm=False)

    This line is equivalent to :

        >>> from scipy.signal.windows import gaussian
        >>> kernel = gaussian(M = 200, std=5)
        >>> tsd.convolve(window)

    It is generally a good idea to visualize the kernel before applying any convolution.

    See the scipy documentation for the [gaussian window](https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.windows.gaussian.html)

    Parameters
    ----------
    std : Number
        Standard deviation in units of time
    windowsize : Number
        Size of the gaussian window in units of time.
    time_units : str, optional
        The time units in which std and windowsize are specified ('us', 'ms', 's' [default]).
    size_factor : int, optional
        How long should be the kernel size as a function of the standard deviation. Default is 100.
        Bypassed if windowsize is used.
    norm : bool, optional
        Whether to normalized the gaussian kernel or not. Default is `True`.

    Returns
    -------
    Tsd, TsdFrame, TsdTensor
        Time series convolved with a gaussian kernel

    """
    assert isinstance(std, (int, float)), "std should be type int or float"
    assert isinstance(size_factor, int), "size_factor should be of type int"
    assert isinstance(norm, bool), "norm should be of type boolean"
    assert isinstance(time_units, str), "time_units should be of type str"

    std = TsIndex.format_timestamps(np.array([std]), time_units)[0]
    std_size = int(self.rate * std)

    if windowsize is not None:
        assert isinstance(
            windowsize, (int, float)
        ), "windowsize should be type int or float"
        windowsize = TsIndex.format_timestamps(np.array([windowsize]), time_units)[
            0
        ]
        M = int(self.rate * windowsize)
    else:
        M = std_size * size_factor

    window = signal.windows.gaussian(M=M, std=std_size)

    if norm:
        window = window / window.sum()

    return self.convolve(window)

interpolate

interpolate(ts, ep=None, left=None, right=None)

Wrapper of the numpy linear interpolation method. See numpy interpolate for an explanation of the parameters. The argument ts should be Ts, Tsd, TsdFrame, TsdTensor to ensure interpolating from sorted timestamps in the right unit,

Parameters:

Name Type Description Default
ts (Ts, Tsd, TsdFrame or TsdTensor)

The object holding the timestamps

required
ep IntervalSet

The epochs to use to interpolate. If None, the time support of Tsd is used.

None
left None

Value to return for ts < tsd[0], default is tsd[0].

None
right None

Value to return for ts > tsd[-1], default is tsd[-1].

None
Source code in pynapple/core/time_series.py
def interpolate(self, ts, ep=None, left=None, right=None):
    """Wrapper of the numpy linear interpolation method. See [numpy interpolate](https://numpy.org/doc/stable/reference/generated/numpy.interp.html)
    for an explanation of the parameters.
    The argument ts should be Ts, Tsd, TsdFrame, TsdTensor to ensure interpolating from sorted timestamps in the right unit,

    Parameters
    ----------
    ts : Ts, Tsd, TsdFrame or TsdTensor
        The object holding the timestamps
    ep : IntervalSet, optional
        The epochs to use to interpolate. If None, the time support of Tsd is used.
    left : None, optional
        Value to return for ts < tsd[0], default is tsd[0].
    right : None, optional
        Value to return for ts > tsd[-1], default is tsd[-1].
    """
    assert isinstance(
        ts, Base
    ), "First argument should be an instance of Ts, Tsd, TsdFrame or TsdTensor"

    if not isinstance(ep, IntervalSet):
        ep = self.time_support

    new_t = ts.restrict(ep).index

    new_shape = (
        len(new_t) if self.values.ndim == 1 else (len(new_t),) + self.shape[1:]
    )
    new_d = np.full(new_shape, np.nan)

    start = 0
    for i in range(len(ep)):
        t = ts.get(ep[i, 0], ep[i, 1])
        tmp = self.get(ep[i, 0], ep[i, 1])

        if len(t) and len(tmp):
            if self.values.ndim == 1:
                new_d[start : start + len(t)] = np.interp(
                    t.index.values,
                    tmp.index.values,
                    tmp.values,
                    left=left,
                    right=right,
                )
            else:
                interpolated_values = np.apply_along_axis(
                    lambda row: np.interp(
                        t.index.values,
                        tmp.index.values,
                        row,
                        left=left,
                        right=right,
                    ),
                    0,
                    tmp.values,
                )
                new_d[start : start + len(t), ...] = interpolated_values

        start += len(t)
    kwargs_dict = dict(time_support=ep)
    if hasattr(self, "columns"):
        kwargs_dict["columns"] = self.columns
    return self.__class__(t=new_t, d=new_d, **kwargs_dict)

__init__

__init__(
    t,
    d=None,
    time_units="s",
    time_support=None,
    columns=None,
)

TsdFrame initializer A pandas.DataFrame can be passed directly

Parameters:

Name Type Description Default
t ndarray or DataFrame

the time index t, or a pandas.DataFrame (if d is None)

required
d ndarray

The data

None
time_units str

The time units in which times are specified ('us', 'ms', 's' [default]).

's'
time_support IntervalSet

The time support of the TsdFrame object

None
columns iterables

Column names

None
Source code in pynapple/core/time_series.py
def __init__(self, t, d=None, time_units="s", time_support=None, columns=None):
    """
    TsdFrame initializer
    A pandas.DataFrame can be passed directly

    Parameters
    ----------
    t : numpy.ndarray or pandas.DataFrame
        the time index t,  or a pandas.DataFrame (if d is None)
    d : numpy.ndarray
        The data
    time_units : str, optional
        The time units in which times are specified ('us', 'ms', 's' [default]).
    time_support : IntervalSet, optional
        The time support of the TsdFrame object
    columns : iterables
        Column names
    """

    c = columns

    if isinstance(t, pd.DataFrame):
        d = t.values
        c = t.columns.values
        t = t.index.values
    else:
        assert d is not None, "Missing argument d when initializing TsdFrame"

    super().__init__(t, d, time_units, time_support)

    assert self.values.ndim <= 2, "Data should be 1 or 2 dimensional."

    if self.values.ndim == 1:
        self.values = np.expand_dims(self.values, 1)

    if c is None or len(c) != self.values.shape[1]:
        c = np.arange(self.values.shape[1], dtype="int")
    else:
        assert (
            len(c) == self.values.shape[1]
        ), "Number of columns should match the second dimension of d"

    self.columns = pd.Index(c)
    self.nap_class = self.__class__.__name__
    self._initialized = True

as_dataframe

as_dataframe()

Convert the TsdFrame object to a pandas.DataFrame object.

Returns:

Name Type Description
out DataFrame

_

Source code in pynapple/core/time_series.py
def as_dataframe(self):
    """
    Convert the TsdFrame object to a pandas.DataFrame object.

    Returns
    -------
    out: pandas.DataFrame
        _
    """
    return pd.DataFrame(
        index=self.index.values, data=self.values, columns=self.columns
    )

as_units

as_units(units='s')

Returns a DataFrame with time expressed in the desired unit.

Parameters:

Name Type Description Default
units str

('us', 'ms', 's' [default])

's'

Returns:

Type Description
DataFrame

the series object with adjusted times

Source code in pynapple/core/time_series.py
def as_units(self, units="s"):
    """
    Returns a DataFrame with time expressed in the desired unit.

    Parameters
    ----------
    units : str, optional
        ('us', 'ms', 's' [default])

    Returns
    -------
    pandas.DataFrame
        the series object with adjusted times
    """
    t = self.index.in_units(units)
    if units == "us":
        t = t.astype(np.int64)

    df = pd.DataFrame(index=t, data=self.values)
    df.index.name = "Time (" + str(units) + ")"
    df.columns = self.columns.copy()
    return df

save

save(filename)

Save TsdFrame object in npz format. The file will contain the timestamps, the data and the time support.

The main purpose of this function is to save small/medium sized time series objects. For example, you extracted several channels from your recording and filtered them. You can save the filtered channels as a npz to avoid reprocessing it.

You can load the object with nap.load_file. Keys are 't', 'd', 'start', 'end', 'type' and 'columns' for columns names.

Parameters:

Name Type Description Default
filename str

The filename

required

Examples:

>>> import pynapple as nap
>>> import numpy as np
>>> tsdframe = nap.TsdFrame(t=np.array([0., 1.]), d = np.array([[2, 3],[4,5]]), columns=['a', 'b'])
>>> tsdframe.save("my_path/my_tsdframe.npz")

To load you file, you can use the nap.load_file function :

>>> tsdframe = nap.load_file("my_path/my_tsdframe.npz")
>>> tsdframe
          a  b
Time (s)
0.0       2  3
1.0       4  5

Raises:

Type Description
RuntimeError

If filename is not str, path does not exist or filename is a directory.

Source code in pynapple/core/time_series.py
def save(self, filename):
    """
    Save TsdFrame object in npz format. The file will contain the timestamps, the
    data and the time support.

    The main purpose of this function is to save small/medium sized time series
    objects. For example, you extracted several channels from your recording and
    filtered them. You can save the filtered channels as a npz to avoid
    reprocessing it.

    You can load the object with `nap.load_file`. Keys are 't', 'd', 'start', 'end', 'type'
    and 'columns' for columns names.

    Parameters
    ----------
    filename : str
        The filename

    Examples
    --------
    >>> import pynapple as nap
    >>> import numpy as np
    >>> tsdframe = nap.TsdFrame(t=np.array([0., 1.]), d = np.array([[2, 3],[4,5]]), columns=['a', 'b'])
    >>> tsdframe.save("my_path/my_tsdframe.npz")

    To load you file, you can use the `nap.load_file` function :

    >>> tsdframe = nap.load_file("my_path/my_tsdframe.npz")
    >>> tsdframe
              a  b
    Time (s)
    0.0       2  3
    1.0       4  5


    Raises
    ------
    RuntimeError
        If filename is not str, path does not exist or filename is a directory.
    """
    if not isinstance(filename, str):
        raise RuntimeError("Invalid type; please provide filename as string")

    if os.path.isdir(filename):
        raise RuntimeError(
            "Invalid filename input. {} is directory.".format(filename)
        )

    if not filename.lower().endswith(".npz"):
        filename = filename + ".npz"

    dirname = os.path.dirname(filename)

    if len(dirname) and not os.path.exists(dirname):
        raise RuntimeError(
            "Path {} does not exist.".format(os.path.dirname(filename))
        )

    cols_name = self.columns
    if cols_name.dtype == np.dtype("O"):
        cols_name = cols_name.astype(str)

    np.savez(
        filename,
        t=self.index.values,
        d=self.values,
        start=self.time_support.start,
        end=self.time_support.end,
        columns=cols_name,
        type=np.array(["TsdFrame"], dtype=np.str_),
    )

    return

Tsd

Bases: BaseTsd

A container around numpy.ndarray specialized for neurophysiology time series.

Tsd provides standardized time representation, plus various functions for manipulating times series.

Attributes:

Name Type Description
rate float

Frequency of the time series (Hz) computed over the time support

time_support IntervalSet

The time support of the time series

Source code in pynapple/core/time_series.py
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
class Tsd(BaseTsd):
    """
    A container around numpy.ndarray specialized for neurophysiology time series.

    Tsd provides standardized time representation, plus various functions for manipulating times series.

    Attributes
    ----------
    rate : float
        Frequency of the time series (Hz) computed over the time support
    time_support : IntervalSet
        The time support of the time series
    """

    def __init__(self, t, d=None, time_units="s", time_support=None, **kwargs):
        """
        Tsd Initializer.

        Parameters
        ----------
        t : numpy.ndarray or pandas.Series
            An object transformable in a time series, or a pandas.Series equivalent (if d is None)
        d : numpy.ndarray, optional
            The data of the time series
        time_units : str, optional
            The time units in which times are specified ('us', 'ms', 's' [default])
        time_support : IntervalSet, optional
            The time support of the tsd object
        """
        if isinstance(t, pd.Series):
            d = t.values
            t = t.index.values
        else:
            assert d is not None, "Missing argument d when initializing Tsd"

        super().__init__(t, d, time_units, time_support)

        assert self.values.ndim == 1, "Data should be 1 dimensional"

        self.nap_class = self.__class__.__name__
        self._initialized = True

    def __repr__(self):
        headers = ["Time (s)", ""]
        bottom = "dtype: {}".format(self.dtype) + ", shape: {}".format(self.shape)

        max_rows = 2
        rows = _get_terminal_size()[1]
        max_rows = np.maximum(rows - 10, 2)

        with warnings.catch_warnings():
            warnings.simplefilter("ignore")
            if len(self):
                if len(self) > max_rows:
                    n_rows = max_rows // 2
                    table = []
                    for i, v in zip(self.index[0:n_rows], self.values[0:n_rows]):
                        table.append([i, v])
                    table.append(["..."])
                    for i, v in zip(
                        self.index[-n_rows:],
                        self.values[
                            self.values.shape[0] - n_rows : self.values.shape[0]
                        ],
                    ):
                        table.append([i, v])

                    return (
                        tabulate(table, headers=headers, colalign=("left",))
                        + "\n"
                        + bottom
                    )
                else:
                    return (
                        tabulate(
                            np.vstack((self.index, self.values)).T,
                            headers=headers,
                            colalign=("left",),
                        )
                        + "\n"
                        + bottom
                    )
            else:
                return tabulate([], headers=headers) + "\n" + bottom

    def __getitem__(self, key, *args, **kwargs):
        output = self.values.__getitem__(key)
        if isinstance(key, tuple):
            index = self.index.__getitem__(key[0])
        else:
            index = self.index.__getitem__(key)

        if isinstance(index, Number):
            index = np.array([index])

        if all(isinstance(a, np.ndarray) for a in [index, output]):
            if output.shape[0] == index.shape[0]:
                return _get_class(output)(
                    t=index, d=output, time_support=self.time_support, **kwargs
                )
            else:
                return output
        else:
            return output

    def as_series(self):
        """
        Convert the Ts/Tsd object to a pandas.Series object.

        Returns
        -------
        out: pandas.Series
            _
        """
        return pd.Series(
            index=self.index.values, data=self.values, copy=True, dtype="float64"
        )

    def as_units(self, units="s"):
        """
        Returns a pandas Series with time expressed in the desired unit.

        Parameters
        ----------
        units : str, optional
            ('us', 'ms', 's' [default])

        Returns
        -------
        pandas.Series
            the series object with adjusted times
        """
        ss = self.as_series()
        t = self.index.in_units(units)
        if units == "us":
            t = t.astype(np.int64)
        ss.index = t
        ss.index.name = "Time (" + str(units) + ")"
        return ss

    def threshold(self, thr, method="above"):
        """
        Apply a threshold function to the tsd to return a new tsd
        with the time support being the epochs above/below/>=/<= the threshold

        Parameters
        ----------
        thr : float
            The threshold value
        method : str, optional
            The threshold method (above/below/aboveequal/belowequal)

        Returns
        -------
        out: Tsd
            All the time points below/ above/greater than equal to/less than equal to the threshold

        Raises
        ------
        ValueError
            Raise an error if method is not 'below' or 'above'
        RuntimeError
            Raise an error if thr is too high/low and no epochs is found.

        Examples
        --------
        This example finds all epoch above 0.5 within the tsd object.

        >>> import pynapple as nap
        >>> tsd = nap.Tsd(t=np.arange(100), d=np.random.rand(100))
        >>> newtsd = tsd.threshold(0.5)

        The epochs with the times above/below the threshold can be accessed through the time support:

        >>> tsd = nap.Tsd(t=np.arange(100), d=np.arange(100), time_units='s')
        >>> tsd.threshold(50).time_support
        >>>    start   end
        >>> 0   50.5  99.0

        """
        time_array = self.index.values
        data_array = self.values
        starts = self.time_support.start
        ends = self.time_support.end
        if method not in ["above", "below", "aboveequal", "belowequal"]:
            raise ValueError(
                "Method {} for thresholding is not accepted.".format(method)
            )

        t, d, ns, ne = jitthreshold(time_array, data_array, starts, ends, thr, method)
        time_support = IntervalSet(start=ns, end=ne)
        return Tsd(t=t, d=d, time_support=time_support)

    def to_tsgroup(self):
        """
        Convert Tsd to a TsGroup by grouping timestamps with the same values.
        By default, the values are converted to integers.

        Examples
        --------
        >>> import pynapple as nap
        >>> import numpy as np
        >>> tsd = nap.Tsd(t = np.array([0, 1, 2, 3]), d = np.array([0, 2, 0, 1]))
        Time (s)
        0.0    0
        1.0    2
        2.0    0
        3.0    1
        dtype: int64

        >>> tsd.to_tsgroup()
        Index    rate
        -------  ------
            0    0.67
            1    0.33
            2    0.33

        The reverse operation can be done with the TsGroup.to_tsd function :

        >>> tsgroup.to_tsd()
        Time (s)
        0.0    0.0
        1.0    2.0
        2.0    0.0
        3.0    1.0
        dtype: float64

        Returns
        -------
        TsGroup
            Grouped timestamps


        """
        ts_group = importlib.import_module(".ts_group", "pynapple.core")
        t = self.index.values
        d = self.values.astype("int")
        idx = np.unique(d)

        group = {}
        for k in idx:
            group[k] = Ts(t=t[d == k], time_support=self.time_support)

        return ts_group.TsGroup(
            group, time_support=self.time_support, bypass_check=True
        )

    def save(self, filename):
        """
        Save Tsd object in npz format. The file will contain the timestamps, the
        data and the time support.

        The main purpose of this function is to save small/medium sized time series
        objects. For example, you extracted one channel from your recording and
        filtered it. You can save the filtered channel as a npz to avoid
        reprocessing it.

        You can load the object with `nap.load_file`. Keys are 't', 'd', 'start', 'end' and 'type'.
        See the example below.

        Parameters
        ----------
        filename : str
            The filename

        Examples
        --------
        >>> import pynapple as nap
        >>> import numpy as np
        >>> tsd = nap.Tsd(t=np.array([0., 1.]), d = np.array([2, 3]))
        >>> tsd.save("my_path/my_tsd.npz")

        To load you file, you can use the `nap.load_file` function :

        >>> tsd = nap.load_file("my_path/my_tsd.npz")
        >>> tsd
        Time (s)
        0.0    2
        1.0    3
        dtype: int64

        Raises
        ------
        RuntimeError
            If filename is not str, path does not exist or filename is a directory.
        """
        if not isinstance(filename, str):
            raise RuntimeError("Invalid type; please provide filename as string")

        if os.path.isdir(filename):
            raise RuntimeError(
                "Invalid filename input. {} is directory.".format(filename)
            )

        if not filename.lower().endswith(".npz"):
            filename = filename + ".npz"

        dirname = os.path.dirname(filename)

        if len(dirname) and not os.path.exists(dirname):
            raise RuntimeError(
                "Path {} does not exist.".format(os.path.dirname(filename))
            )

        np.savez(
            filename,
            t=self.index.values,
            d=self.values,
            start=self.time_support.start,
            end=self.time_support.end,
            type=np.array([self.nap_class], dtype=np.str_),
        )

        return

__setattr__

__setattr__(name, value)

Object is immutable

Source code in pynapple/core/base_class.py
def __setattr__(self, name, value):
    """Object is immutable"""
    if self._initialized:
        raise RuntimeError(
            "Changing directly attributes is not permitted for {}.".format(
                self.nap_class
            )
        )
    else:
        object.__setattr__(self, name, value)

__setitem__

__setitem__(key, value)

setter for time series

Source code in pynapple/core/time_series.py
def __setitem__(self, key, value):
    """setter for time series"""
    try:
        self.values.__setitem__(key, value)
    except IndexError:
        raise IndexError

times

times(units='s')

The time index of the object, returned as np.double in the desired time units.

Parameters:

Name Type Description Default
units str

('us', 'ms', 's' [default])

's'

Returns:

Name Type Description
out ndarray

the time indexes

Source code in pynapple/core/base_class.py
def times(self, units="s"):
    """
    The time index of the object, returned as np.double in the desired time units.

    Parameters
    ----------
    units : str, optional
        ('us', 'ms', 's' [default])

    Returns
    -------
    out: numpy.ndarray
        the time indexes
    """
    return self.index.in_units(units)

start_time

start_time(units='s')

The first time index in the time series object

Parameters:

Name Type Description Default
units str

('us', 'ms', 's' [default])

's'

Returns:

Name Type Description
out float64

_

Source code in pynapple/core/base_class.py
def start_time(self, units="s"):
    """
    The first time index in the time series object

    Parameters
    ----------
    units : str, optional
        ('us', 'ms', 's' [default])

    Returns
    -------
    out: numpy.float64
        _
    """
    if len(self.index):
        return self.times(units=units)[0]
    else:
        return None

end_time

end_time(units='s')

The last time index in the time series object

Parameters:

Name Type Description Default
units str

('us', 'ms', 's' [default])

's'

Returns:

Name Type Description
out float64

_

Source code in pynapple/core/base_class.py
def end_time(self, units="s"):
    """
    The last time index in the time series object

    Parameters
    ----------
    units : str, optional
        ('us', 'ms', 's' [default])

    Returns
    -------
    out: numpy.float64
        _
    """
    if len(self.index):
        return self.times(units=units)[-1]
    else:
        return None

restrict

restrict(iset)

Restricts a time series object to a set of time intervals delimited by an IntervalSet object

Parameters:

Name Type Description Default
iset IntervalSet

the IntervalSet object

required

Returns:

Name Type Description
out (Ts, Tsd, TsdFrame or TsdTensor)

Tsd object restricted to ep

Examples:

The Ts object is restrict to the intervals defined by ep.

>>> import pynapple as nap
>>> import numpy as np
>>> t = np.unique(np.sort(np.random.randint(0, 1000, 100)))
>>> ts = nap.Ts(t=t, time_units='s')
>>> ep = nap.IntervalSet(start=0, end=500, time_units='s')
>>> newts = ts.restrict(ep)

The time support of newts automatically inherit the epochs defined by ep.

>>> newts.time_support
    start    end
0    0.0  500.0
Source code in pynapple/core/base_class.py
def restrict(self, iset):
    """
    Restricts a time series object to a set of time intervals delimited by an IntervalSet object

    Parameters
    ----------
    iset : IntervalSet
        the IntervalSet object

    Returns
    -------
    out: Ts, Tsd, TsdFrame or TsdTensor
        Tsd object restricted to ep

    Examples
    --------
    The Ts object is restrict to the intervals defined by ep.

    >>> import pynapple as nap
    >>> import numpy as np
    >>> t = np.unique(np.sort(np.random.randint(0, 1000, 100)))
    >>> ts = nap.Ts(t=t, time_units='s')
    >>> ep = nap.IntervalSet(start=0, end=500, time_units='s')
    >>> newts = ts.restrict(ep)

    The time support of newts automatically inherit the epochs defined by ep.

    >>> newts.time_support
        start    end
    0    0.0  500.0

    """
    assert isinstance(iset, IntervalSet), "Argument should be IntervalSet"

    time_array = self.index.values
    starts = iset.start
    ends = iset.end

    if hasattr(self, "values"):
        data_array = self.values
        t, d = jitrestrict(time_array, data_array, starts, ends)

        kwargs = {}
        if hasattr(self, "columns"):
            kwargs["columns"] = self.columns

        return self.__class__(t=t, d=d, time_support=iset, **kwargs)

    else:
        t = jittsrestrict(time_array, starts, ends)
        return self.__class__(t=t, time_support=iset)

copy

copy()

Copy the data, index and time support

Source code in pynapple/core/time_series.py
def copy(self):
    """Copy the data, index and time support"""
    return self.__class__(
        t=self.index.copy(), d=self.values.copy(), time_support=self.time_support
    )

find_support

find_support(min_gap, time_units='s')

find the smallest (to a min_gap resolution) IntervalSet containing all the times in the Tsd

Parameters:

Name Type Description Default
min_gap float or int

minimal interval between timestamps

required
time_units str

Time units of min gap

's'

Returns:

Type Description
IntervalSet

Description

Source code in pynapple/core/base_class.py
def find_support(self, min_gap, time_units="s"):
    """
    find the smallest (to a min_gap resolution) IntervalSet containing all the times in the Tsd

    Parameters
    ----------
    min_gap : float or int
        minimal interval between timestamps
    time_units : str, optional
        Time units of min gap

    Returns
    -------
    IntervalSet
        Description
    """
    assert isinstance(min_gap, Number), "min_gap should be a float or int"
    min_gap = TsIndex.format_timestamps(np.array([min_gap]), time_units)[0]
    time_array = self.index.values

    starts = [time_array[0]]
    ends = []
    for i in range(len(time_array) - 1):
        if (time_array[i + 1] - time_array[i]) > min_gap:
            ends.append(time_array[i] + 1e-6)
            starts.append(time_array[i + 1])

    ends.append(time_array[-1] + 1e-6)

    return IntervalSet(start=starts, end=ends)

get

get(start, end=None, time_units='s')

Slice the time series from start to end such that all the timestamps satisfy start<=t<=end. If end is None, only the timepoint closest to start is returned.

By default, the time support doesn't change. If you want to change the time support, use the restrict function.

Parameters:

Name Type Description Default
start float or int

The start (or closest time point if end is None)

required
end float or int or None

The end

None
Source code in pynapple/core/base_class.py
def get(self, start, end=None, time_units="s"):
    """Slice the time series from `start` to `end` such that all the timestamps satisfy `start<=t<=end`.
    If `end` is None, only the timepoint closest to `start` is returned.

    By default, the time support doesn't change. If you want to change the time support, use the `restrict` function.

    Parameters
    ----------
    start : float or int
        The start (or closest time point if `end` is None)
    end : float or int or None
        The end
    """
    assert isinstance(start, Number), "start should be a float or int"
    time_array = self.index.values

    if end is None:
        start = TsIndex.format_timestamps(np.array([start]), time_units)[0]
        idx = int(np.searchsorted(time_array, start))
        if idx == 0:
            return self[idx]
        elif idx >= self.shape[0]:
            return self[-1]
        else:
            if start - time_array[idx - 1] < time_array[idx] - start:
                return self[idx - 1]
            else:
                return self[idx]
    else:
        assert isinstance(end, Number), "end should be a float or int"
        assert start < end, "Start should not precede end"
        start, end = TsIndex.format_timestamps(np.array([start, end]), time_units)
        idx_start = np.searchsorted(time_array, start)
        idx_end = np.searchsorted(time_array, end, side="right")
        return self[idx_start:idx_end]

__getattr__

__getattr__(name)

Allow numpy functions to be attached as attributes of Tsd objects

Source code in pynapple/core/time_series.py
def __getattr__(self, name):
    """Allow numpy functions to be attached as attributes of Tsd objects"""
    if hasattr(np, name):
        np_func = getattr(np, name)

        def method(*args, **kwargs):
            return np_func(self, *args, **kwargs)

        return method

    raise AttributeError(
        "Time series object does not have the attribute {}".format(name)
    )

as_array

as_array()

Return the data as a numpy.ndarray

Returns:

Name Type Description
out ndarray

_

Source code in pynapple/core/time_series.py
def as_array(self):
    """
    Return the data as a numpy.ndarray

    Returns
    -------
    out: numpy.ndarray
        _
    """
    return self.values

data

data()

Return the data as a numpy.ndarray

Returns:

Name Type Description
out ndarray

_

Source code in pynapple/core/time_series.py
def data(self):
    """
    Return the data as a numpy.ndarray

    Returns
    -------
    out: numpy.ndarray
        _
    """
    return self.values

to_numpy

to_numpy()

Return the data as a numpy.ndarray. Mostly useful for matplotlib plotting when calling plot(tsd)

Source code in pynapple/core/time_series.py
def to_numpy(self):
    """
    Return the data as a numpy.ndarray. Mostly useful for matplotlib plotting when calling `plot(tsd)`
    """
    return self.values

bin_average

bin_average(bin_size, ep=None, time_units='s')

Bin the data by averaging points within bin_size bin_size should be seconds unless specified. If no epochs is passed, the data will be binned based on the time support.

Parameters:

Name Type Description Default
bin_size float

The bin size (default is second)

required
ep None or IntervalSet

IntervalSet to restrict the operation

None
time_units str

Time units of bin size ('us', 'ms', 's' [default])

's'

Returns:

Name Type Description
out (Tsd, TsdFrame, TsdTensor)

A Tsd object indexed by the center of the bins and holding the averaged data points.

Examples:

This example shows how to bin data within bins of 0.1 second.

>>> import pynapple as nap
>>> import numpy as np
>>> tsd = nap.Tsd(t=np.arange(100), d=np.random.rand(100))
>>> bintsd = tsd.bin_average(0.1)

An epoch can be specified:

>>> ep = nap.IntervalSet(start = 10, end = 80, time_units = 's')
>>> bintsd = tsd.bin_average(0.1, ep=ep)

And bintsd automatically inherit ep as time support:

>>> bintsd.time_support
>>>    start    end
>>> 0  10.0     80.0
Source code in pynapple/core/time_series.py
def bin_average(self, bin_size, ep=None, time_units="s"):
    """
    Bin the data by averaging points within bin_size
    bin_size should be seconds unless specified.
    If no epochs is passed, the data will be binned based on the time support.

    Parameters
    ----------
    bin_size : float
        The bin size (default is second)
    ep : None or IntervalSet, optional
        IntervalSet to restrict the operation
    time_units : str, optional
        Time units of bin size ('us', 'ms', 's' [default])

    Returns
    -------
    out: Tsd, TsdFrame, TsdTensor
        A Tsd object indexed by the center of the bins and holding the averaged data points.

    Examples
    --------
    This example shows how to bin data within bins of 0.1 second.

    >>> import pynapple as nap
    >>> import numpy as np
    >>> tsd = nap.Tsd(t=np.arange(100), d=np.random.rand(100))
    >>> bintsd = tsd.bin_average(0.1)

    An epoch can be specified:

    >>> ep = nap.IntervalSet(start = 10, end = 80, time_units = 's')
    >>> bintsd = tsd.bin_average(0.1, ep=ep)

    And bintsd automatically inherit ep as time support:

    >>> bintsd.time_support
    >>>    start    end
    >>> 0  10.0     80.0
    """
    if not isinstance(ep, IntervalSet):
        ep = self.time_support

    bin_size = TsIndex.format_timestamps(np.array([bin_size]), time_units)[0]

    time_array = self.index.values
    data_array = self.values
    starts = ep.start
    ends = ep.end
    if data_array.ndim > 1:
        t, d = jitbin_array(time_array, data_array, starts, ends, bin_size)
    else:
        t, d = jitbin(time_array, data_array, starts, ends, bin_size)

    kwargs = {}
    if hasattr(self, "columns"):
        kwargs["columns"] = self.columns
    return self.__class__(t=t, d=d, time_support=ep, **kwargs)

dropna

dropna(update_time_support=True)

Drop every rows containing NaNs. By default, the time support is updated to start and end around the time points that are non NaNs. To change this behavior, you can set update_time_support=False.

Parameters:

Name Type Description Default
update_time_support bool
True

Returns:

Type Description
(Tsd, TsdFrame or TsdTensor)

The time series without the NaNs

Source code in pynapple/core/time_series.py
def dropna(self, update_time_support=True):
    """Drop every rows containing NaNs. By default, the time support is updated to start and end around the time points that are non NaNs.
    To change this behavior, you can set update_time_support=False.

    Parameters
    ----------
    update_time_support : bool, optional

    Returns
    -------
    Tsd, TsdFrame or TsdTensor
        The time series without the NaNs
    """
    index_nan = np.any(np.isnan(self.values), axis=tuple(range(1, self.ndim)))
    if np.all(index_nan):  # In case it's only NaNs
        return self.__class__(
            t=np.array([]), d=np.empty(tuple([0] + [d for d in self.shape[1:]]))
        )

    elif np.any(index_nan):
        if update_time_support:
            time_array = self.index.values
            starts, ends = jitremove_nan(time_array, index_nan)

            to_fix = starts == ends
            if np.any(to_fix):
                ends[
                    to_fix
                ] += 1e-6  # adding 1 millisecond in case of a single point

            ep = IntervalSet(starts, ends)

            return self.__class__(
                t=time_array[~index_nan], d=self.values[~index_nan], time_support=ep
            )

        else:
            return self[~index_nan]

    else:
        return self

convolve

convolve(array, ep=None, trim='both')

Return the discrete linear convolution of the time series with a one dimensional sequence.

A parameter ep can control the epochs for which the convolution will apply. Otherwise the convolution is made over the time support.

This function assume a constant sampling rate of the time series.

The only mode supported is full. The returned object is trimmed to match the size of the original object. The parameter trim controls which side the trimming operates. Default is 'both'.

See the numpy documentation here : https://numpy.org/doc/stable/reference/generated/numpy.convolve.html

Parameters:

Name Type Description Default
array ndarray

One dimensional input array

required
ep None

The epochs to apply the convolution

None
trim str

The side on which to trim the output of the convolution ('left', 'right', 'both' [default])

'both'

Returns:

Type Description
(Tsd, TsdFrame or TsdTensor)

The convolved time series

Source code in pynapple/core/time_series.py
def convolve(self, array, ep=None, trim="both"):
    """Return the discrete linear convolution of the time series with a one dimensional sequence.

    A parameter ep can control the epochs for which the convolution will apply. Otherwise the convolution is made over the time support.

    This function assume a constant sampling rate of the time series.

    The only mode supported is full. The returned object is trimmed to match the size of the original object. The parameter trim controls which side the trimming operates. Default is 'both'.

    See the numpy documentation here : https://numpy.org/doc/stable/reference/generated/numpy.convolve.html

    Parameters
    ----------
    array : np.ndarray
        One dimensional input array
    ep : None, optional
        The epochs to apply the convolution
    trim : str, optional
        The side on which to trim the output of the convolution ('left', 'right', 'both' [default])

    Returns
    -------
    Tsd, TsdFrame or TsdTensor
        The convolved time series
    """
    assert isinstance(array, np.ndarray), "Input should be a 1-d numpy array."
    assert array.ndim == 1, "Input should be a one dimensional array."
    assert trim in [
        "both",
        "left",
        "right",
    ], "Unknow argument. trim should be 'both', 'left' or 'right'."

    if ep is None:
        ep = self.time_support

    time_array = self.index.values
    data_array = self.values
    starts = ep.start
    ends = ep.end

    if data_array.ndim == 1:
        new_data_array = np.zeros(data_array.shape)
        k = array.shape[0]
        for s, e in zip(starts, ends):
            idx_s = np.searchsorted(time_array, s)
            idx_e = np.searchsorted(time_array, e, side="right")

            t = idx_e - idx_s
            if trim == "left":
                cut = (k - 1, t + k - 1)
            elif trim == "right":
                cut = (0, t)
            else:
                cut = ((1 - k % 2) + (k - 1) // 2, t + k - 1 - ((k - 1) // 2))
            # scipy is actually faster for Tsd
            new_data_array[idx_s:idx_e] = signal.convolve(
                data_array[idx_s:idx_e], array
            )[cut[0] : cut[1]]

        return self.__class__(t=time_array, d=new_data_array, time_support=ep)
    else:
        new_data_array = np.zeros(data_array.shape)
        for s, e in zip(starts, ends):
            idx_s = np.searchsorted(time_array, s)
            idx_e = np.searchsorted(time_array, e, side="right")
            new_data_array[idx_s:idx_e] = pjitconvolve(
                data_array[idx_s:idx_e], array, trim=trim
            )

        return self.__class__(t=time_array, d=new_data_array, time_support=ep)

smooth

smooth(
    std,
    windowsize=None,
    time_units="s",
    size_factor=100,
    norm=True,
)

Smooth a time series with a gaussian kernel.

std is the standard deviation of the gaussian kernel in units of time. If only std is passed, the function will compute the standard deviation and size in number of time points automatically based on the sampling rate of the time series. For example, if the time series tsd has a sample rate of 100 Hz and std is 50 ms, the standard deviation will be converted to an integer through tsd.rate * std = int(100 * 0.05) = 5.

If windowsize is None, the function will select a kernel size as 100 times the std in number of time points. This behavior can be controlled with the parameter size_factor.

norm set to True normalizes the gaussian kernel to sum to 1.

In the following example, a time series tsd with a sampling rate of 100 Hz is convolved with a gaussian kernel. The standard deviation is 0.05 second and the windowsize is 2 second. When instantiating the gaussian kernel from scipy, it corresponds to parameters M = 200 and std=5

>>> tsd.smooth(std=0.05, windowsize=2, time_units='s', norm=False)

This line is equivalent to :

>>> from scipy.signal.windows import gaussian
>>> kernel = gaussian(M = 200, std=5)
>>> tsd.convolve(window)

It is generally a good idea to visualize the kernel before applying any convolution.

See the scipy documentation for the gaussian window

Parameters:

Name Type Description Default
std Number

Standard deviation in units of time

required
windowsize Number

Size of the gaussian window in units of time.

None
time_units str

The time units in which std and windowsize are specified ('us', 'ms', 's' [default]).

's'
size_factor int

How long should be the kernel size as a function of the standard deviation. Default is 100. Bypassed if windowsize is used.

100
norm bool

Whether to normalized the gaussian kernel or not. Default is True.

True

Returns:

Type Description
(Tsd, TsdFrame, TsdTensor)

Time series convolved with a gaussian kernel

Source code in pynapple/core/time_series.py
def smooth(self, std, windowsize=None, time_units="s", size_factor=100, norm=True):
    """Smooth a time series with a gaussian kernel.

    `std` is the standard deviation of the gaussian kernel in units of time.
    If only `std` is passed, the function will compute the standard deviation and size in number
    of time points automatically based on the sampling rate of the time series.
    For example, if the time series `tsd` has a sample rate of 100 Hz and `std` is 50 ms,
    the standard deviation will be converted to an integer through
    `tsd.rate * std = int(100 * 0.05) = 5`.

    If `windowsize` is None, the function will select a kernel size as 100 times
    the std in number of time points. This behavior can be controlled with the
    parameter `size_factor`.

    `norm` set to True normalizes the gaussian kernel to sum to 1.

    In the following example, a time series `tsd` with a sampling rate of 100 Hz
    is convolved with a gaussian kernel. The standard deviation is
    0.05 second and the windowsize is 2 second. When instantiating the gaussian kernel
    from scipy, it corresponds to parameters `M = 200` and `std=5`

        >>> tsd.smooth(std=0.05, windowsize=2, time_units='s', norm=False)

    This line is equivalent to :

        >>> from scipy.signal.windows import gaussian
        >>> kernel = gaussian(M = 200, std=5)
        >>> tsd.convolve(window)

    It is generally a good idea to visualize the kernel before applying any convolution.

    See the scipy documentation for the [gaussian window](https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.windows.gaussian.html)

    Parameters
    ----------
    std : Number
        Standard deviation in units of time
    windowsize : Number
        Size of the gaussian window in units of time.
    time_units : str, optional
        The time units in which std and windowsize are specified ('us', 'ms', 's' [default]).
    size_factor : int, optional
        How long should be the kernel size as a function of the standard deviation. Default is 100.
        Bypassed if windowsize is used.
    norm : bool, optional
        Whether to normalized the gaussian kernel or not. Default is `True`.

    Returns
    -------
    Tsd, TsdFrame, TsdTensor
        Time series convolved with a gaussian kernel

    """
    assert isinstance(std, (int, float)), "std should be type int or float"
    assert isinstance(size_factor, int), "size_factor should be of type int"
    assert isinstance(norm, bool), "norm should be of type boolean"
    assert isinstance(time_units, str), "time_units should be of type str"

    std = TsIndex.format_timestamps(np.array([std]), time_units)[0]
    std_size = int(self.rate * std)

    if windowsize is not None:
        assert isinstance(
            windowsize, (int, float)
        ), "windowsize should be type int or float"
        windowsize = TsIndex.format_timestamps(np.array([windowsize]), time_units)[
            0
        ]
        M = int(self.rate * windowsize)
    else:
        M = std_size * size_factor

    window = signal.windows.gaussian(M=M, std=std_size)

    if norm:
        window = window / window.sum()

    return self.convolve(window)

interpolate

interpolate(ts, ep=None, left=None, right=None)

Wrapper of the numpy linear interpolation method. See numpy interpolate for an explanation of the parameters. The argument ts should be Ts, Tsd, TsdFrame, TsdTensor to ensure interpolating from sorted timestamps in the right unit,

Parameters:

Name Type Description Default
ts (Ts, Tsd, TsdFrame or TsdTensor)

The object holding the timestamps

required
ep IntervalSet

The epochs to use to interpolate. If None, the time support of Tsd is used.

None
left None

Value to return for ts < tsd[0], default is tsd[0].

None
right None

Value to return for ts > tsd[-1], default is tsd[-1].

None
Source code in pynapple/core/time_series.py
def interpolate(self, ts, ep=None, left=None, right=None):
    """Wrapper of the numpy linear interpolation method. See [numpy interpolate](https://numpy.org/doc/stable/reference/generated/numpy.interp.html)
    for an explanation of the parameters.
    The argument ts should be Ts, Tsd, TsdFrame, TsdTensor to ensure interpolating from sorted timestamps in the right unit,

    Parameters
    ----------
    ts : Ts, Tsd, TsdFrame or TsdTensor
        The object holding the timestamps
    ep : IntervalSet, optional
        The epochs to use to interpolate. If None, the time support of Tsd is used.
    left : None, optional
        Value to return for ts < tsd[0], default is tsd[0].
    right : None, optional
        Value to return for ts > tsd[-1], default is tsd[-1].
    """
    assert isinstance(
        ts, Base
    ), "First argument should be an instance of Ts, Tsd, TsdFrame or TsdTensor"

    if not isinstance(ep, IntervalSet):
        ep = self.time_support

    new_t = ts.restrict(ep).index

    new_shape = (
        len(new_t) if self.values.ndim == 1 else (len(new_t),) + self.shape[1:]
    )
    new_d = np.full(new_shape, np.nan)

    start = 0
    for i in range(len(ep)):
        t = ts.get(ep[i, 0], ep[i, 1])
        tmp = self.get(ep[i, 0], ep[i, 1])

        if len(t) and len(tmp):
            if self.values.ndim == 1:
                new_d[start : start + len(t)] = np.interp(
                    t.index.values,
                    tmp.index.values,
                    tmp.values,
                    left=left,
                    right=right,
                )
            else:
                interpolated_values = np.apply_along_axis(
                    lambda row: np.interp(
                        t.index.values,
                        tmp.index.values,
                        row,
                        left=left,
                        right=right,
                    ),
                    0,
                    tmp.values,
                )
                new_d[start : start + len(t), ...] = interpolated_values

        start += len(t)
    kwargs_dict = dict(time_support=ep)
    if hasattr(self, "columns"):
        kwargs_dict["columns"] = self.columns
    return self.__class__(t=new_t, d=new_d, **kwargs_dict)

__init__

__init__(
    t, d=None, time_units="s", time_support=None, **kwargs
)

Tsd Initializer.

Parameters:

Name Type Description Default
t ndarray or Series

An object transformable in a time series, or a pandas.Series equivalent (if d is None)

required
d ndarray

The data of the time series

None
time_units str

The time units in which times are specified ('us', 'ms', 's' [default])

's'
time_support IntervalSet

The time support of the tsd object

None
Source code in pynapple/core/time_series.py
def __init__(self, t, d=None, time_units="s", time_support=None, **kwargs):
    """
    Tsd Initializer.

    Parameters
    ----------
    t : numpy.ndarray or pandas.Series
        An object transformable in a time series, or a pandas.Series equivalent (if d is None)
    d : numpy.ndarray, optional
        The data of the time series
    time_units : str, optional
        The time units in which times are specified ('us', 'ms', 's' [default])
    time_support : IntervalSet, optional
        The time support of the tsd object
    """
    if isinstance(t, pd.Series):
        d = t.values
        t = t.index.values
    else:
        assert d is not None, "Missing argument d when initializing Tsd"

    super().__init__(t, d, time_units, time_support)

    assert self.values.ndim == 1, "Data should be 1 dimensional"

    self.nap_class = self.__class__.__name__
    self._initialized = True

as_series

as_series()

Convert the Ts/Tsd object to a pandas.Series object.

Returns:

Name Type Description
out Series

_

Source code in pynapple/core/time_series.py
def as_series(self):
    """
    Convert the Ts/Tsd object to a pandas.Series object.

    Returns
    -------
    out: pandas.Series
        _
    """
    return pd.Series(
        index=self.index.values, data=self.values, copy=True, dtype="float64"
    )

as_units

as_units(units='s')

Returns a pandas Series with time expressed in the desired unit.

Parameters:

Name Type Description Default
units str

('us', 'ms', 's' [default])

's'

Returns:

Type Description
Series

the series object with adjusted times

Source code in pynapple/core/time_series.py
def as_units(self, units="s"):
    """
    Returns a pandas Series with time expressed in the desired unit.

    Parameters
    ----------
    units : str, optional
        ('us', 'ms', 's' [default])

    Returns
    -------
    pandas.Series
        the series object with adjusted times
    """
    ss = self.as_series()
    t = self.index.in_units(units)
    if units == "us":
        t = t.astype(np.int64)
    ss.index = t
    ss.index.name = "Time (" + str(units) + ")"
    return ss

threshold

threshold(thr, method='above')

Apply a threshold function to the tsd to return a new tsd with the time support being the epochs above/below/>=/<= the threshold

Parameters:

Name Type Description Default
thr float

The threshold value

required
method str

The threshold method (above/below/aboveequal/belowequal)

'above'

Returns:

Name Type Description
out Tsd

All the time points below/ above/greater than equal to/less than equal to the threshold

Raises:

Type Description
ValueError

Raise an error if method is not 'below' or 'above'

RuntimeError

Raise an error if thr is too high/low and no epochs is found.

Examples:

This example finds all epoch above 0.5 within the tsd object.

>>> import pynapple as nap
>>> tsd = nap.Tsd(t=np.arange(100), d=np.random.rand(100))
>>> newtsd = tsd.threshold(0.5)

The epochs with the times above/below the threshold can be accessed through the time support:

>>> tsd = nap.Tsd(t=np.arange(100), d=np.arange(100), time_units='s')
>>> tsd.threshold(50).time_support
>>>    start   end
>>> 0   50.5  99.0
Source code in pynapple/core/time_series.py
def threshold(self, thr, method="above"):
    """
    Apply a threshold function to the tsd to return a new tsd
    with the time support being the epochs above/below/>=/<= the threshold

    Parameters
    ----------
    thr : float
        The threshold value
    method : str, optional
        The threshold method (above/below/aboveequal/belowequal)

    Returns
    -------
    out: Tsd
        All the time points below/ above/greater than equal to/less than equal to the threshold

    Raises
    ------
    ValueError
        Raise an error if method is not 'below' or 'above'
    RuntimeError
        Raise an error if thr is too high/low and no epochs is found.

    Examples
    --------
    This example finds all epoch above 0.5 within the tsd object.

    >>> import pynapple as nap
    >>> tsd = nap.Tsd(t=np.arange(100), d=np.random.rand(100))
    >>> newtsd = tsd.threshold(0.5)

    The epochs with the times above/below the threshold can be accessed through the time support:

    >>> tsd = nap.Tsd(t=np.arange(100), d=np.arange(100), time_units='s')
    >>> tsd.threshold(50).time_support
    >>>    start   end
    >>> 0   50.5  99.0

    """
    time_array = self.index.values
    data_array = self.values
    starts = self.time_support.start
    ends = self.time_support.end
    if method not in ["above", "below", "aboveequal", "belowequal"]:
        raise ValueError(
            "Method {} for thresholding is not accepted.".format(method)
        )

    t, d, ns, ne = jitthreshold(time_array, data_array, starts, ends, thr, method)
    time_support = IntervalSet(start=ns, end=ne)
    return Tsd(t=t, d=d, time_support=time_support)

to_tsgroup

to_tsgroup()

Convert Tsd to a TsGroup by grouping timestamps with the same values. By default, the values are converted to integers.

Examples:

>>> import pynapple as nap
>>> import numpy as np
>>> tsd = nap.Tsd(t = np.array([0, 1, 2, 3]), d = np.array([0, 2, 0, 1]))
Time (s)
0.0    0
1.0    2
2.0    0
3.0    1
dtype: int64
>>> tsd.to_tsgroup()
Index    rate
-------  ------
    0    0.67
    1    0.33
    2    0.33

The reverse operation can be done with the TsGroup.to_tsd function :

>>> tsgroup.to_tsd()
Time (s)
0.0    0.0
1.0    2.0
2.0    0.0
3.0    1.0
dtype: float64

Returns:

Type Description
TsGroup

Grouped timestamps

Source code in pynapple/core/time_series.py
def to_tsgroup(self):
    """
    Convert Tsd to a TsGroup by grouping timestamps with the same values.
    By default, the values are converted to integers.

    Examples
    --------
    >>> import pynapple as nap
    >>> import numpy as np
    >>> tsd = nap.Tsd(t = np.array([0, 1, 2, 3]), d = np.array([0, 2, 0, 1]))
    Time (s)
    0.0    0
    1.0    2
    2.0    0
    3.0    1
    dtype: int64

    >>> tsd.to_tsgroup()
    Index    rate
    -------  ------
        0    0.67
        1    0.33
        2    0.33

    The reverse operation can be done with the TsGroup.to_tsd function :

    >>> tsgroup.to_tsd()
    Time (s)
    0.0    0.0
    1.0    2.0
    2.0    0.0
    3.0    1.0
    dtype: float64

    Returns
    -------
    TsGroup
        Grouped timestamps


    """
    ts_group = importlib.import_module(".ts_group", "pynapple.core")
    t = self.index.values
    d = self.values.astype("int")
    idx = np.unique(d)

    group = {}
    for k in idx:
        group[k] = Ts(t=t[d == k], time_support=self.time_support)

    return ts_group.TsGroup(
        group, time_support=self.time_support, bypass_check=True
    )

save

save(filename)

Save Tsd object in npz format. The file will contain the timestamps, the data and the time support.

The main purpose of this function is to save small/medium sized time series objects. For example, you extracted one channel from your recording and filtered it. You can save the filtered channel as a npz to avoid reprocessing it.

You can load the object with nap.load_file. Keys are 't', 'd', 'start', 'end' and 'type'. See the example below.

Parameters:

Name Type Description Default
filename str

The filename

required

Examples:

>>> import pynapple as nap
>>> import numpy as np
>>> tsd = nap.Tsd(t=np.array([0., 1.]), d = np.array([2, 3]))
>>> tsd.save("my_path/my_tsd.npz")

To load you file, you can use the nap.load_file function :

>>> tsd = nap.load_file("my_path/my_tsd.npz")
>>> tsd
Time (s)
0.0    2
1.0    3
dtype: int64

Raises:

Type Description
RuntimeError

If filename is not str, path does not exist or filename is a directory.

Source code in pynapple/core/time_series.py
def save(self, filename):
    """
    Save Tsd object in npz format. The file will contain the timestamps, the
    data and the time support.

    The main purpose of this function is to save small/medium sized time series
    objects. For example, you extracted one channel from your recording and
    filtered it. You can save the filtered channel as a npz to avoid
    reprocessing it.

    You can load the object with `nap.load_file`. Keys are 't', 'd', 'start', 'end' and 'type'.
    See the example below.

    Parameters
    ----------
    filename : str
        The filename

    Examples
    --------
    >>> import pynapple as nap
    >>> import numpy as np
    >>> tsd = nap.Tsd(t=np.array([0., 1.]), d = np.array([2, 3]))
    >>> tsd.save("my_path/my_tsd.npz")

    To load you file, you can use the `nap.load_file` function :

    >>> tsd = nap.load_file("my_path/my_tsd.npz")
    >>> tsd
    Time (s)
    0.0    2
    1.0    3
    dtype: int64

    Raises
    ------
    RuntimeError
        If filename is not str, path does not exist or filename is a directory.
    """
    if not isinstance(filename, str):
        raise RuntimeError("Invalid type; please provide filename as string")

    if os.path.isdir(filename):
        raise RuntimeError(
            "Invalid filename input. {} is directory.".format(filename)
        )

    if not filename.lower().endswith(".npz"):
        filename = filename + ".npz"

    dirname = os.path.dirname(filename)

    if len(dirname) and not os.path.exists(dirname):
        raise RuntimeError(
            "Path {} does not exist.".format(os.path.dirname(filename))
        )

    np.savez(
        filename,
        t=self.index.values,
        d=self.values,
        start=self.time_support.start,
        end=self.time_support.end,
        type=np.array([self.nap_class], dtype=np.str_),
    )

    return

Ts

Bases: Base

Timestamps only object for a time series with only time index,

Attributes:

Name Type Description
rate float

Frequency of the time series (Hz) computed over the time support

time_support IntervalSet

The time support of the time series

Source code in pynapple/core/time_series.py
class Ts(Base):
    """
    Timestamps only object for a time series with only time index,

    Attributes
    ----------
    rate : float
        Frequency of the time series (Hz) computed over the time support
    time_support : IntervalSet
        The time support of the time series
    """

    def __init__(self, t, time_units="s", time_support=None):
        """
        Ts Initializer

        Parameters
        ----------
        t : numpy.ndarray or pandas.Series
            An object transformable in timestamps, or a pandas.Series equivalent (if d is None)
        time_units : str, optional
            The time units in which times are specified ('us', 'ms', 's' [default])
        time_support : IntervalSet, optional
            The time support of the Ts object
        """
        super().__init__(t, time_units, time_support)

        if isinstance(time_support, IntervalSet) and len(self.index):
            starts = time_support.start
            ends = time_support.end
            t = jittsrestrict(self.index.values, starts, ends)
            self.index = TsIndex(t)
            self.rate = self.index.shape[0] / np.sum(
                time_support.values[:, 1] - time_support.values[:, 0]
            )

        self.nap_class = self.__class__.__name__
        self._initialized = True

    def __repr__(self):
        upper = "Time (s)"

        max_rows = 2
        rows = _get_terminal_size()[1]
        max_rows = np.maximum(rows - 10, 2)

        if len(self) > max_rows:
            n_rows = max_rows // 2
            _str_ = "\n".join(
                [i.__repr__() for i in self.index[0:n_rows]]
                + ["..."]
                + [i.__repr__() for i in self.index[-n_rows:]]
            )
        else:
            _str_ = "\n".join([i.__repr__() for i in self.index])

        bottom = "shape: {}".format(len(self.index))
        return "\n".join((upper, _str_, bottom))

    def __getitem__(self, key):
        if isinstance(key, tuple):
            index = self.index.__getitem__(key[0])
        else:
            index = self.index.__getitem__(key)

        if isinstance(index, Number):
            index = np.array([index])

        return Ts(t=index, time_support=self.time_support)

    def as_series(self):
        """
        Convert the Ts/Tsd object to a pandas.Series object.

        Returns
        -------
        out: pandas.Series
            _
        """
        return pd.Series(index=self.index.values, dtype="object")

    def as_units(self, units="s"):
        """
        Returns a pandas Series with time expressed in the desired unit.

        Parameters
        ----------
        units : str, optional
            ('us', 'ms', 's' [default])

        Returns
        -------
        pandas.Series
            the series object with adjusted times
        """
        t = self.index.in_units(units)
        if units == "us":
            t = t.astype(np.int64)
        ss = pd.Series(index=t, dtype="object")
        ss.index.name = "Time (" + str(units) + ")"
        return ss

    def value_from(self, data, ep=None):
        """
        Replace the value with the closest value from Tsd/TsdFrame/TsdTensor argument

        Parameters
        ----------
        data : Tsd, TsdFrame or TsdTensor
            The object holding the values to replace.
        ep : IntervalSet (optional)
            The IntervalSet object to restrict the operation.
            If None, the time support of the tsd input object is used.

        Returns
        -------
        out : Tsd, TsdFrame or TsdTensor
            Object with the new values

        Examples
        --------
        In this example, the ts object will receive the closest values in time from tsd.

        >>> import pynapple as nap
        >>> import numpy as np
        >>> t = np.unique(np.sort(np.random.randint(0, 1000, 100))) # random times
        >>> ts = nap.Ts(t=t, time_units='s')
        >>> tsd = nap.Tsd(t=np.arange(0,1000), d=np.random.rand(1000), time_units='s')
        >>> ep = nap.IntervalSet(start = 0, end = 500, time_units = 's')

        The variable ts is a time series object containing only nan.
        The tsd object containing the values, for example the tracking data, and the epoch to restrict the operation.

        >>> newts = ts.value_from(tsd, ep)

        newts is the same size as ts restrict to ep.

        >>> print(len(ts.restrict(ep)), len(newts))
            52 52
        """
        assert isinstance(
            data, BaseTsd
        ), "First argument should be an instance of Tsd, TsdFrame or TsdTensor"

        t, d, time_support, kwargs = super().value_from(data, ep)

        return data.__class__(t, d, time_support=time_support, **kwargs)

    def count(self, *args, **kwargs):
        """
        Count occurences of events within bin_size or within a set of bins defined as an IntervalSet.
        You can call this function in multiple ways :

        1. *tsd.count(bin_size=1, time_units = 'ms')*
        -> Count occurence of events within a 1 ms bin defined on the time support of the object.

        2. *tsd.count(1, ep=my_epochs)*
        -> Count occurent of events within a 1 second bin defined on the IntervalSet my_epochs.

        3. *tsd.count(ep=my_bins)*
        -> Count occurent of events within each epoch of the intervalSet object my_bins

        4. *tsd.count()*
        -> Count occurent of events within each epoch of the time support.

        bin_size should be seconds unless specified.
        If bin_size is used and no epochs is passed, the data will be binned based on the time support of the object.

        Parameters
        ----------
        bin_size : None or float, optional
            The bin size (default is second)
        ep : None or IntervalSet, optional
            IntervalSet to restrict the operation
        time_units : str, optional
            Time units of bin size ('us', 'ms', 's' [default])

        Returns
        -------
        out: Tsd
            A Tsd object indexed by the center of the bins.

        Examples
        --------
        This example shows how to count events within bins of 0.1 second.

        >>> import pynapple as nap
        >>> import numpy as np
        >>> t = np.unique(np.sort(np.random.randint(0, 1000, 100)))
        >>> ts = nap.Ts(t=t, time_units='s')
        >>> bincount = ts.count(0.1)

        An epoch can be specified:

        >>> ep = nap.IntervalSet(start = 100, end = 800, time_units = 's')
        >>> bincount = ts.count(0.1, ep=ep)

        And bincount automatically inherit ep as time support:

        >>> bincount.time_support
        >>>    start    end
        >>> 0  100.0  800.0
        """
        t, d, ep = super().count(*args, **kwargs)
        return Tsd(t=t, d=d, time_support=ep)

    def fillna(self, value):
        """
        Similar to pandas fillna function.

        Parameters
        ----------
        value : Number
            Value for filling

        Returns
        -------
        Tsd


        """
        assert isinstance(value, Number), "Only a scalar can be passed to fillna"
        d = np.empty(len(self))
        d.fill(value)
        return Tsd(t=self.index, d=d, time_support=self.time_support)

    def save(self, filename):
        """
        Save Ts object in npz format. The file will contain the timestamps and
        the time support.

        The main purpose of this function is to save small/medium sized timestamps
        object.

        You can load the object with `nap.load_file`. Keys are 't', 'start' and 'end' and 'type'.
        See the example below.

        Parameters
        ----------
        filename : str
            The filename

        Examples
        --------
        >>> import pynapple as nap
        >>> import numpy as np
        >>> ts = nap.Ts(t=np.array([0., 1., 1.5]))
        >>> ts.save("my_path/my_ts.npz")

        To load you file, you can use the `nap.load_file` function :

        >>> ts = nap.load_file("my_path/my_ts.npz")
        >>> ts
        Time (s)
        0.0
        1.0
        1.5

        Raises
        ------
        RuntimeError
            If filename is not str, path does not exist or filename is a directory.
        """
        if not isinstance(filename, str):
            raise RuntimeError("Invalid type; please provide filename as string")

        if os.path.isdir(filename):
            raise RuntimeError(
                "Invalid filename input. {} is directory.".format(filename)
            )

        if not filename.lower().endswith(".npz"):
            filename = filename + ".npz"

        dirname = os.path.dirname(filename)

        if len(dirname) and not os.path.exists(dirname):
            raise RuntimeError(
                "Path {} does not exist.".format(os.path.dirname(filename))
            )

        np.savez(
            filename,
            t=self.index.values,
            start=self.time_support.start,
            end=self.time_support.end,
            type=np.array(["Ts"], dtype=np.str_),
        )

        return

__setattr__

__setattr__(name, value)

Object is immutable

Source code in pynapple/core/base_class.py
def __setattr__(self, name, value):
    """Object is immutable"""
    if self._initialized:
        raise RuntimeError(
            "Changing directly attributes is not permitted for {}.".format(
                self.nap_class
            )
        )
    else:
        object.__setattr__(self, name, value)

times

times(units='s')

The time index of the object, returned as np.double in the desired time units.

Parameters:

Name Type Description Default
units str

('us', 'ms', 's' [default])

's'

Returns:

Name Type Description
out ndarray

the time indexes

Source code in pynapple/core/base_class.py
def times(self, units="s"):
    """
    The time index of the object, returned as np.double in the desired time units.

    Parameters
    ----------
    units : str, optional
        ('us', 'ms', 's' [default])

    Returns
    -------
    out: numpy.ndarray
        the time indexes
    """
    return self.index.in_units(units)

start_time

start_time(units='s')

The first time index in the time series object

Parameters:

Name Type Description Default
units str

('us', 'ms', 's' [default])

's'

Returns:

Name Type Description
out float64

_

Source code in pynapple/core/base_class.py
def start_time(self, units="s"):
    """
    The first time index in the time series object

    Parameters
    ----------
    units : str, optional
        ('us', 'ms', 's' [default])

    Returns
    -------
    out: numpy.float64
        _
    """
    if len(self.index):
        return self.times(units=units)[0]
    else:
        return None

end_time

end_time(units='s')

The last time index in the time series object

Parameters:

Name Type Description Default
units str

('us', 'ms', 's' [default])

's'

Returns:

Name Type Description
out float64

_

Source code in pynapple/core/base_class.py
def end_time(self, units="s"):
    """
    The last time index in the time series object

    Parameters
    ----------
    units : str, optional
        ('us', 'ms', 's' [default])

    Returns
    -------
    out: numpy.float64
        _
    """
    if len(self.index):
        return self.times(units=units)[-1]
    else:
        return None

restrict

restrict(iset)

Restricts a time series object to a set of time intervals delimited by an IntervalSet object

Parameters:

Name Type Description Default
iset IntervalSet

the IntervalSet object

required

Returns:

Name Type Description
out (Ts, Tsd, TsdFrame or TsdTensor)

Tsd object restricted to ep

Examples:

The Ts object is restrict to the intervals defined by ep.

>>> import pynapple as nap
>>> import numpy as np
>>> t = np.unique(np.sort(np.random.randint(0, 1000, 100)))
>>> ts = nap.Ts(t=t, time_units='s')
>>> ep = nap.IntervalSet(start=0, end=500, time_units='s')
>>> newts = ts.restrict(ep)

The time support of newts automatically inherit the epochs defined by ep.

>>> newts.time_support
    start    end
0    0.0  500.0
Source code in pynapple/core/base_class.py
def restrict(self, iset):
    """
    Restricts a time series object to a set of time intervals delimited by an IntervalSet object

    Parameters
    ----------
    iset : IntervalSet
        the IntervalSet object

    Returns
    -------
    out: Ts, Tsd, TsdFrame or TsdTensor
        Tsd object restricted to ep

    Examples
    --------
    The Ts object is restrict to the intervals defined by ep.

    >>> import pynapple as nap
    >>> import numpy as np
    >>> t = np.unique(np.sort(np.random.randint(0, 1000, 100)))
    >>> ts = nap.Ts(t=t, time_units='s')
    >>> ep = nap.IntervalSet(start=0, end=500, time_units='s')
    >>> newts = ts.restrict(ep)

    The time support of newts automatically inherit the epochs defined by ep.

    >>> newts.time_support
        start    end
    0    0.0  500.0

    """
    assert isinstance(iset, IntervalSet), "Argument should be IntervalSet"

    time_array = self.index.values
    starts = iset.start
    ends = iset.end

    if hasattr(self, "values"):
        data_array = self.values
        t, d = jitrestrict(time_array, data_array, starts, ends)

        kwargs = {}
        if hasattr(self, "columns"):
            kwargs["columns"] = self.columns

        return self.__class__(t=t, d=d, time_support=iset, **kwargs)

    else:
        t = jittsrestrict(time_array, starts, ends)
        return self.__class__(t=t, time_support=iset)

copy

copy()

Copy the data, index and time support

Source code in pynapple/core/base_class.py
def copy(self):
    """Copy the data, index and time support"""
    return self.__class__(t=self.index.copy(), time_support=self.time_support)

find_support

find_support(min_gap, time_units='s')

find the smallest (to a min_gap resolution) IntervalSet containing all the times in the Tsd

Parameters:

Name Type Description Default
min_gap float or int

minimal interval between timestamps

required
time_units str

Time units of min gap

's'

Returns:

Type Description
IntervalSet

Description

Source code in pynapple/core/base_class.py
def find_support(self, min_gap, time_units="s"):
    """
    find the smallest (to a min_gap resolution) IntervalSet containing all the times in the Tsd

    Parameters
    ----------
    min_gap : float or int
        minimal interval between timestamps
    time_units : str, optional
        Time units of min gap

    Returns
    -------
    IntervalSet
        Description
    """
    assert isinstance(min_gap, Number), "min_gap should be a float or int"
    min_gap = TsIndex.format_timestamps(np.array([min_gap]), time_units)[0]
    time_array = self.index.values

    starts = [time_array[0]]
    ends = []
    for i in range(len(time_array) - 1):
        if (time_array[i + 1] - time_array[i]) > min_gap:
            ends.append(time_array[i] + 1e-6)
            starts.append(time_array[i + 1])

    ends.append(time_array[-1] + 1e-6)

    return IntervalSet(start=starts, end=ends)

get

get(start, end=None, time_units='s')

Slice the time series from start to end such that all the timestamps satisfy start<=t<=end. If end is None, only the timepoint closest to start is returned.

By default, the time support doesn't change. If you want to change the time support, use the restrict function.

Parameters:

Name Type Description Default
start float or int

The start (or closest time point if end is None)

required
end float or int or None

The end

None
Source code in pynapple/core/base_class.py
def get(self, start, end=None, time_units="s"):
    """Slice the time series from `start` to `end` such that all the timestamps satisfy `start<=t<=end`.
    If `end` is None, only the timepoint closest to `start` is returned.

    By default, the time support doesn't change. If you want to change the time support, use the `restrict` function.

    Parameters
    ----------
    start : float or int
        The start (or closest time point if `end` is None)
    end : float or int or None
        The end
    """
    assert isinstance(start, Number), "start should be a float or int"
    time_array = self.index.values

    if end is None:
        start = TsIndex.format_timestamps(np.array([start]), time_units)[0]
        idx = int(np.searchsorted(time_array, start))
        if idx == 0:
            return self[idx]
        elif idx >= self.shape[0]:
            return self[-1]
        else:
            if start - time_array[idx - 1] < time_array[idx] - start:
                return self[idx - 1]
            else:
                return self[idx]
    else:
        assert isinstance(end, Number), "end should be a float or int"
        assert start < end, "Start should not precede end"
        start, end = TsIndex.format_timestamps(np.array([start, end]), time_units)
        idx_start = np.searchsorted(time_array, start)
        idx_end = np.searchsorted(time_array, end, side="right")
        return self[idx_start:idx_end]

__init__

__init__(t, time_units='s', time_support=None)

Ts Initializer

Parameters:

Name Type Description Default
t ndarray or Series

An object transformable in timestamps, or a pandas.Series equivalent (if d is None)

required
time_units str

The time units in which times are specified ('us', 'ms', 's' [default])

's'
time_support IntervalSet

The time support of the Ts object

None
Source code in pynapple/core/time_series.py
def __init__(self, t, time_units="s", time_support=None):
    """
    Ts Initializer

    Parameters
    ----------
    t : numpy.ndarray or pandas.Series
        An object transformable in timestamps, or a pandas.Series equivalent (if d is None)
    time_units : str, optional
        The time units in which times are specified ('us', 'ms', 's' [default])
    time_support : IntervalSet, optional
        The time support of the Ts object
    """
    super().__init__(t, time_units, time_support)

    if isinstance(time_support, IntervalSet) and len(self.index):
        starts = time_support.start
        ends = time_support.end
        t = jittsrestrict(self.index.values, starts, ends)
        self.index = TsIndex(t)
        self.rate = self.index.shape[0] / np.sum(
            time_support.values[:, 1] - time_support.values[:, 0]
        )

    self.nap_class = self.__class__.__name__
    self._initialized = True

as_series

as_series()

Convert the Ts/Tsd object to a pandas.Series object.

Returns:

Name Type Description
out Series

_

Source code in pynapple/core/time_series.py
def as_series(self):
    """
    Convert the Ts/Tsd object to a pandas.Series object.

    Returns
    -------
    out: pandas.Series
        _
    """
    return pd.Series(index=self.index.values, dtype="object")

as_units

as_units(units='s')

Returns a pandas Series with time expressed in the desired unit.

Parameters:

Name Type Description Default
units str

('us', 'ms', 's' [default])

's'

Returns:

Type Description
Series

the series object with adjusted times

Source code in pynapple/core/time_series.py
def as_units(self, units="s"):
    """
    Returns a pandas Series with time expressed in the desired unit.

    Parameters
    ----------
    units : str, optional
        ('us', 'ms', 's' [default])

    Returns
    -------
    pandas.Series
        the series object with adjusted times
    """
    t = self.index.in_units(units)
    if units == "us":
        t = t.astype(np.int64)
    ss = pd.Series(index=t, dtype="object")
    ss.index.name = "Time (" + str(units) + ")"
    return ss

value_from

value_from(data, ep=None)

Replace the value with the closest value from Tsd/TsdFrame/TsdTensor argument

Parameters:

Name Type Description Default
data (Tsd, TsdFrame or TsdTensor)

The object holding the values to replace.

required
ep IntervalSet(optional)

The IntervalSet object to restrict the operation. If None, the time support of the tsd input object is used.

None

Returns:

Name Type Description
out (Tsd, TsdFrame or TsdTensor)

Object with the new values

Examples:

In this example, the ts object will receive the closest values in time from tsd.

>>> import pynapple as nap
>>> import numpy as np
>>> t = np.unique(np.sort(np.random.randint(0, 1000, 100))) # random times
>>> ts = nap.Ts(t=t, time_units='s')
>>> tsd = nap.Tsd(t=np.arange(0,1000), d=np.random.rand(1000), time_units='s')
>>> ep = nap.IntervalSet(start = 0, end = 500, time_units = 's')

The variable ts is a time series object containing only nan. The tsd object containing the values, for example the tracking data, and the epoch to restrict the operation.

>>> newts = ts.value_from(tsd, ep)

newts is the same size as ts restrict to ep.

>>> print(len(ts.restrict(ep)), len(newts))
    52 52
Source code in pynapple/core/time_series.py
def value_from(self, data, ep=None):
    """
    Replace the value with the closest value from Tsd/TsdFrame/TsdTensor argument

    Parameters
    ----------
    data : Tsd, TsdFrame or TsdTensor
        The object holding the values to replace.
    ep : IntervalSet (optional)
        The IntervalSet object to restrict the operation.
        If None, the time support of the tsd input object is used.

    Returns
    -------
    out : Tsd, TsdFrame or TsdTensor
        Object with the new values

    Examples
    --------
    In this example, the ts object will receive the closest values in time from tsd.

    >>> import pynapple as nap
    >>> import numpy as np
    >>> t = np.unique(np.sort(np.random.randint(0, 1000, 100))) # random times
    >>> ts = nap.Ts(t=t, time_units='s')
    >>> tsd = nap.Tsd(t=np.arange(0,1000), d=np.random.rand(1000), time_units='s')
    >>> ep = nap.IntervalSet(start = 0, end = 500, time_units = 's')

    The variable ts is a time series object containing only nan.
    The tsd object containing the values, for example the tracking data, and the epoch to restrict the operation.

    >>> newts = ts.value_from(tsd, ep)

    newts is the same size as ts restrict to ep.

    >>> print(len(ts.restrict(ep)), len(newts))
        52 52
    """
    assert isinstance(
        data, BaseTsd
    ), "First argument should be an instance of Tsd, TsdFrame or TsdTensor"

    t, d, time_support, kwargs = super().value_from(data, ep)

    return data.__class__(t, d, time_support=time_support, **kwargs)

count

count(*args, **kwargs)

Count occurences of events within bin_size or within a set of bins defined as an IntervalSet. You can call this function in multiple ways :

  1. tsd.count(bin_size=1, time_units = 'ms') -> Count occurence of events within a 1 ms bin defined on the time support of the object.

  2. tsd.count(1, ep=my_epochs) -> Count occurent of events within a 1 second bin defined on the IntervalSet my_epochs.

  3. tsd.count(ep=my_bins) -> Count occurent of events within each epoch of the intervalSet object my_bins

  4. tsd.count() -> Count occurent of events within each epoch of the time support.

bin_size should be seconds unless specified. If bin_size is used and no epochs is passed, the data will be binned based on the time support of the object.

Parameters:

Name Type Description Default
bin_size None or float

The bin size (default is second)

required
ep None or IntervalSet

IntervalSet to restrict the operation

required
time_units str

Time units of bin size ('us', 'ms', 's' [default])

required

Returns:

Name Type Description
out Tsd

A Tsd object indexed by the center of the bins.

Examples:

This example shows how to count events within bins of 0.1 second.

>>> import pynapple as nap
>>> import numpy as np
>>> t = np.unique(np.sort(np.random.randint(0, 1000, 100)))
>>> ts = nap.Ts(t=t, time_units='s')
>>> bincount = ts.count(0.1)

An epoch can be specified:

>>> ep = nap.IntervalSet(start = 100, end = 800, time_units = 's')
>>> bincount = ts.count(0.1, ep=ep)

And bincount automatically inherit ep as time support:

>>> bincount.time_support
>>>    start    end
>>> 0  100.0  800.0
Source code in pynapple/core/time_series.py
def count(self, *args, **kwargs):
    """
    Count occurences of events within bin_size or within a set of bins defined as an IntervalSet.
    You can call this function in multiple ways :

    1. *tsd.count(bin_size=1, time_units = 'ms')*
    -> Count occurence of events within a 1 ms bin defined on the time support of the object.

    2. *tsd.count(1, ep=my_epochs)*
    -> Count occurent of events within a 1 second bin defined on the IntervalSet my_epochs.

    3. *tsd.count(ep=my_bins)*
    -> Count occurent of events within each epoch of the intervalSet object my_bins

    4. *tsd.count()*
    -> Count occurent of events within each epoch of the time support.

    bin_size should be seconds unless specified.
    If bin_size is used and no epochs is passed, the data will be binned based on the time support of the object.

    Parameters
    ----------
    bin_size : None or float, optional
        The bin size (default is second)
    ep : None or IntervalSet, optional
        IntervalSet to restrict the operation
    time_units : str, optional
        Time units of bin size ('us', 'ms', 's' [default])

    Returns
    -------
    out: Tsd
        A Tsd object indexed by the center of the bins.

    Examples
    --------
    This example shows how to count events within bins of 0.1 second.

    >>> import pynapple as nap
    >>> import numpy as np
    >>> t = np.unique(np.sort(np.random.randint(0, 1000, 100)))
    >>> ts = nap.Ts(t=t, time_units='s')
    >>> bincount = ts.count(0.1)

    An epoch can be specified:

    >>> ep = nap.IntervalSet(start = 100, end = 800, time_units = 's')
    >>> bincount = ts.count(0.1, ep=ep)

    And bincount automatically inherit ep as time support:

    >>> bincount.time_support
    >>>    start    end
    >>> 0  100.0  800.0
    """
    t, d, ep = super().count(*args, **kwargs)
    return Tsd(t=t, d=d, time_support=ep)

fillna

fillna(value)

Similar to pandas fillna function.

Parameters:

Name Type Description Default
value Number

Value for filling

required

Returns:

Type Description
Tsd
Source code in pynapple/core/time_series.py
def fillna(self, value):
    """
    Similar to pandas fillna function.

    Parameters
    ----------
    value : Number
        Value for filling

    Returns
    -------
    Tsd


    """
    assert isinstance(value, Number), "Only a scalar can be passed to fillna"
    d = np.empty(len(self))
    d.fill(value)
    return Tsd(t=self.index, d=d, time_support=self.time_support)

save

save(filename)

Save Ts object in npz format. The file will contain the timestamps and the time support.

The main purpose of this function is to save small/medium sized timestamps object.

You can load the object with nap.load_file. Keys are 't', 'start' and 'end' and 'type'. See the example below.

Parameters:

Name Type Description Default
filename str

The filename

required

Examples:

>>> import pynapple as nap
>>> import numpy as np
>>> ts = nap.Ts(t=np.array([0., 1., 1.5]))
>>> ts.save("my_path/my_ts.npz")

To load you file, you can use the nap.load_file function :

>>> ts = nap.load_file("my_path/my_ts.npz")
>>> ts
Time (s)
0.0
1.0
1.5

Raises:

Type Description
RuntimeError

If filename is not str, path does not exist or filename is a directory.

Source code in pynapple/core/time_series.py
def save(self, filename):
    """
    Save Ts object in npz format. The file will contain the timestamps and
    the time support.

    The main purpose of this function is to save small/medium sized timestamps
    object.

    You can load the object with `nap.load_file`. Keys are 't', 'start' and 'end' and 'type'.
    See the example below.

    Parameters
    ----------
    filename : str
        The filename

    Examples
    --------
    >>> import pynapple as nap
    >>> import numpy as np
    >>> ts = nap.Ts(t=np.array([0., 1., 1.5]))
    >>> ts.save("my_path/my_ts.npz")

    To load you file, you can use the `nap.load_file` function :

    >>> ts = nap.load_file("my_path/my_ts.npz")
    >>> ts
    Time (s)
    0.0
    1.0
    1.5

    Raises
    ------
    RuntimeError
        If filename is not str, path does not exist or filename is a directory.
    """
    if not isinstance(filename, str):
        raise RuntimeError("Invalid type; please provide filename as string")

    if os.path.isdir(filename):
        raise RuntimeError(
            "Invalid filename input. {} is directory.".format(filename)
        )

    if not filename.lower().endswith(".npz"):
        filename = filename + ".npz"

    dirname = os.path.dirname(filename)

    if len(dirname) and not os.path.exists(dirname):
        raise RuntimeError(
            "Path {} does not exist.".format(os.path.dirname(filename))
        )

    np.savez(
        filename,
        t=self.index.values,
        start=self.time_support.start,
        end=self.time_support.end,
        type=np.array(["Ts"], dtype=np.str_),
    )

    return