Core methods#
Show code cell content
import pynapple as nap
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
custom_params = {"axes.spines.right": False, "axes.spines.top": False, "figure.figsize": (8, 4)}
sns.set_context("paper")
sns.set_theme(style="ticks", palette="colorblind", font_scale=1.3, rc=custom_params)
Time series method#
Show code cell content
tsdframe = nap.TsdFrame(t=np.arange(100), d=np.random.randn(100, 3), columns=['a', 'b', 'c'])
group = {
0: nap.Ts(t=np.sort(np.random.uniform(0, 100, 10))),
1: nap.Ts(t=np.sort(np.random.uniform(0, 100, 20))),
2: nap.Ts(t=np.sort(np.random.uniform(0, 100, 30))),
}
tsgroup = nap.TsGroup(group, time_support = nap.IntervalSet(0, 100))
epochs = nap.IntervalSet([10, 65], [25, 80])
tsd = nap.Tsd(t=np.arange(0, 100, 1), d=np.sin(np.arange(0, 10, 0.1)))
restrict
#
restrict
is used to get time points within an IntervalSet
. This method is available
for TsGroup
, Tsd
, TsdFrame
, TsdTensor
and Ts
objects.
tsdframe.restrict(epochs)
Time (s) a b c
---------- -------- -------- --------
10.0 1.01839 0.60823 0.25908
11.0 -0.10679 2.74204 -1.16276
12.0 0.60128 1.80172 1.74784
13.0 -0.36153 0.30917 -0.31239
14.0 -0.17855 -0.64372 1.20025
15.0 -0.23309 -1.04421 0.80899
16.0 0.1426 0.02018 -1.14837
... ... ... ...
74.0 0.33751 -0.25554 0.74882
75.0 -0.69461 0.65561 0.34473
76.0 -0.7938 0.8947 -1.01157
77.0 0.17947 -0.99353 1.52828
78.0 -1.39616 0.55023 0.45176
79.0 2.01032 -3.43571 0.78842
80.0 -0.11228 -0.18441 -0.43304
dtype: float64, shape: (32, 3)
Show code cell source
plt.figure()
plt.plot(tsdframe.restrict(epochs))
[plt.axvspan(s, e, alpha=0.2) for s, e in epochs.values]
plt.xlabel("Time (s)")
plt.title("tsdframe.restrict(epochs)")
plt.xlim(0, 100)
plt.show()

This operation update the time support attribute accordingly.
print(epochs)
print(tsdframe.restrict(epochs).time_support)
index start end
0 10 25
1 65 80
shape: (2, 2), time unit: sec.
index start end
0 10 25
1 65 80
shape: (2, 2), time unit: sec.
count
#
count
the number of timestamps within bins or epochs of an IntervalSet
object.
This method is available for TsGroup
, Tsd
, TsdFrame
, TsdTensor
and Ts
objects.
With a defined bin size:
count = tsgroup.count(bin_size=1.0, time_units='s')
print(count)
Time (s) 0 1 2
---------- --- --- ---
0.5 0 0 1
1.5 0 0 1
2.5 0 1 0
3.5 0 1 0
4.5 0 0 0
5.5 0 0 0
6.5 0 0 1
... ... ... ...
93.5 0 1 0
94.5 0 0 0
95.5 1 1 0
96.5 0 1 0
97.5 0 0 0
98.5 0 0 0
99.5 0 0 1
dtype: int64, shape: (100, 3)
Show code cell source
plt.figure()
plt.step(count.t, count[:,2], where='mid', label="count[:,2]")
plt.title("tsgroup.count(bin_size=1.0)")
plt.plot(tsgroup[2].fillna(-0.5), '|', markeredgewidth=3, label="tsgroup[2]")
[plt.axvline(t, linewidth=0.5, alpha=0.5) for t in np.arange(0, 31)]
plt.xlabel("Time (s)")
plt.xlim(0, 30)
plt.legend()
plt.show()

With an IntervalSet
:
count_ep = tsgroup.count(ep=epochs)
print(count_ep)
Time (s) 0 1 2
---------- --- --- ---
17.5 2 2 7
72.5 2 2 2
dtype: int64, shape: (2, 3)
trial_count
#
TsGroup
and Ts
objects each have the method trial_count
, which builds a trial-based count tensor from an IntervalSet
object.
Similar to count
, this function requires a bin_size
parameter which determines the number of time bins within each trial.
The resulting tensor has shape (number of group elements, number of trials, number of time bins) for TsGroup
objects,
or (number of trials, number of time bins) for Ts
objects.
ep = nap.IntervalSet([5, 17, 30, 50], metadata={'trials':[1, 2]})
tensor = tsgroup.trial_count(ep, bin_size=2)
print(tensor, "\n")
print("Tensor shape = ", tensor.shape)
[[[ 0. 0. 0. 0. 1. 0. nan nan nan nan]
[ 0. 0. 0. 1. 1. 0. 0. 0. 0. 0.]]
[[ 0. 0. 0. 0. 0. 0. nan nan nan nan]
[ 0. 0. 0. 1. 0. 0. 1. 0. 1. 0.]]
[[ 1. 0. 1. 2. 1. 3. nan nan nan nan]
[ 1. 0. 1. 0. 1. 1. 0. 0. 0. 0.]]]
Tensor shape = (3, 2, 10)
Show code cell source
color_cycle = plt.rcParams['axes.prop_cycle'].by_key()['color']
from matplotlib.colors import LinearSegmentedColormap
plt.figure()
gs = plt.GridSpec(3,2)
plt.subplot(gs[:,0])
for i, n in enumerate(tsgroup.keys()):
plt.plot(tsgroup[n].fillna(i+1), '|', markeredgewidth=3, color=color_cycle[i])
for i in range(len(ep)):
plt.axvspan(ep[i,0], ep[i,1], alpha=0.2)
[plt.axvline(t, linewidth=0.5, alpha=0.5) for t in np.arange(ep[i,0], ep[i,1], 2.0)]
plt.title("tsgroup")
plt.ylim(0, len(tsgroup)+1)
plt.xlim(0, 60)
plt.xlabel("Time (s)")
for i in range(3):
plt.subplot(gs[2-i,1])
cmap = LinearSegmentedColormap.from_list("fade", ["lightgrey", color_cycle[i]])
plt.pcolormesh(np.arange(0, tensor.shape[-1]), [1, 2], tensor[i], cmap=cmap)
if i == 1: plt.ylabel("Trials")
if i == 2: plt.title("tsgroup.trial_cout(ep, bin_size=2)")
if i == 0: plt.xlabel("Trial time")
plt.text(1, 0.5, f"tensor[{i}]", transform=plt.gca().transAxes)
plt.tight_layout()
plt.show()

The array is padded with NaNs when the trials have uneven durations,
The padding value can be controlled using the parameter padding_value
.
Additionally, the parameter align
can change whether the count is aligned to the “start” or “end” of each trial.
tensor = tsgroup.trial_count(ep, bin_size=2, align="end", padding_value=-1)
print(tensor, "\n")
print("Tensor shape = ", tensor.shape)
[[[-1. -1. -1. -1. 0. 0. 0. 0. 1. 0.]
[ 0. 0. 0. 1. 1. 0. 0. 0. 0. 0.]]
[[-1. -1. -1. -1. 0. 0. 0. 0. 0. 0.]
[ 0. 0. 0. 1. 0. 0. 1. 0. 1. 0.]]
[[-1. -1. -1. -1. 1. 0. 1. 2. 1. 3.]
[ 1. 0. 1. 0. 1. 1. 0. 0. 0. 0.]]]
Tensor shape = (3, 2, 10)
bin_average
#
bin_average
downsample time series by averaging data point falling within a bin.
This method is available for Tsd
, TsdFrame
and TsdTensor
.
While bin_average
is good for downsampling with precise control of the resulting bins, it does not apply any
antialiasing filter. The function decimate
is also available for down-sampling
without aliasing.
tsdframe.bin_average(3.5)
Time (s) a b c
---------- -------- -------- --------
1.75 -0.38859 -0.36695 -0.3912
5.25 0.31528 -0.2276 0.02786
8.75 0.39705 -0.28879 0.16056
12.25 0.04432 1.61764 0.0909
15.75 0.14692 -0.48801 -0.00097
19.25 0.13206 -0.26099 1.16287
22.75 0.11268 0.57274 -0.01055
... ... ... ...
75.25 -0.38363 0.43159 0.02733
78.75 0.17034 -1.01586 0.58385
82.25 -1.06601 -0.42386 0.93336
85.75 0.2856 -0.71191 0.12412
89.25 0.13202 0.77573 -0.80655
92.75 0.10421 0.21791 0.2386
96.25 -0.33394 0.85459 0.31136
dtype: float64, shape: (28, 3)
Show code cell source
bin_size = 3.5
plt.figure()
plt.plot(tsdframe[:,0], '.--', label="tsdframe[:,0]")
plt.plot(tsdframe[:,0].bin_average(bin_size), 'o-', label="new_tsdframe[:,0]")
plt.title(f"tsdframe.bin_average(bin_size={bin_size})")
[plt.axvline(t, linewidth=0.5, alpha=0.5) for t in np.arange(0, 21,bin_size)]
plt.xlabel("Time (s)")
plt.xlim(0, 20)
plt.legend(bbox_to_anchor=(1.0, 0.5, 0.5, 0.5))
plt.show()

decimate
#
decimate
downsample the time series by an integer factor after an antialiasing filter.
Show code cell source
noisy_data = np.random.rand(100) + np.sin(np.linspace(0, 2 * np.pi, 100))
tsd = nap.Tsd(t=np.arange(100), d=noisy_data, time_support=nap.IntervalSet(0, 100))
new_tsd = tsd.decimate(down=4)
The original time series was sampled at 1Hz. The new time series has a rate of 0.25 Hz.
print(f"Original rate : {tsd.rate}")
print(f"New rate : {new_tsd.rate}")
Original rate : 1.0
New rate : 0.25
Show code cell source
plt.figure()
plt.plot(tsd, label="original")
plt.plot(new_tsd, marker="o", label="decimate")
plt.plot(tsd[::4], marker="o", label="naive downsample")
plt.legend()
plt.show()

interpolate
#
Theinterpolate
method of Tsd
, TsdFrame
and TsdTensor
can be used to fill gaps in a time series. It is a wrapper of numpy.interp
.
Show code cell content
tsd = nap.Tsd(t=np.arange(0, 25, 5), d=np.random.randn(5))
ts = nap.Ts(t=np.arange(0, 21, 1))
new_tsd = tsd.interpolate(ts)
Show code cell source
plt.figure()
plt.plot(new_tsd, '.-', label="new_tsd")
plt.plot(tsd, 'o', label="tsd")
plt.plot(ts.fillna(0), '+', label="ts")
plt.title("tsd.interpolate(ts)")
plt.xlabel("Time (s)")
plt.legend(bbox_to_anchor=(1.0, 0.5, 0.5, 0.5))
plt.show()

value_from
#
By default, value_from
assign to timestamps the closest value in time
from another time series. Let’s define the time series we want to assign values from.
For every timestamps in tsgroup
, we want to assign the closest value in time from tsd
.
Show code cell content
tsd = nap.Tsd(t=np.arange(0, 100, 1), d=np.sin(np.arange(0, 10, 0.1)))
tsgroup_from_tsd = tsgroup.value_from(tsd)
We can display the first element of tsgroup
and tsgroup_sin
.
Show code cell source
plt.figure()
plt.plot(tsgroup[0].fillna(0), "|", label="tsgroup[0]", markersize=20, mew=3)
plt.plot(tsd, linewidth=2, label="tsd")
plt.plot(tsgroup_from_tsd[0], "o", label = "tsgroup_from_tsd[0]", markersize=20)
plt.title("tsgroup.value_from(tsd)")
plt.xlabel("Time (s)")
plt.yticks([-1, 0, 1])
plt.legend(bbox_to_anchor=(1.0, 0.5, 0.5, 0.5))
plt.show()

The argument mode
can control if the nearest target time is taken before or
after the reference time.
Show code cell content
tsd = nap.Tsd(t=np.arange(0, 10, 1), d=np.arange(0, 100, 10))
ts = nap.Ts(t=np.arange(0.5, 9, 1))
In this case, the variable ts
receive data from the time point before.
new_ts_before = ts.value_from(tsd, mode="before")
Show code cell source
plt.figure()
plt.plot(ts.fillna(-1), "|", label="ts", markersize=20, mew=3)
plt.plot(tsd, "*-", linewidth=2, label="tsd")
plt.plot(new_ts_before, "o-", label = "new_ts_before", markersize=10)
plt.title("ts.value_from(tsd, mode='before')")
plt.xlabel("Time (s)")
plt.legend(bbox_to_anchor=(1.0, 0.5, 0.5, 0.5))
plt.show()

Show code cell source
new_ts_after = ts.value_from(tsd, mode="after")
plt.figure()
plt.plot(ts.fillna(-1), "|", label="ts", markersize=20, mew=3)
plt.plot(tsd, "*-", linewidth=2, label="tsd")
plt.plot(new_ts_after, "o-", label = "new_ts_after", markersize=10)
plt.title("ts.value_from(tsd, mode='after')")
plt.xlabel("Time (s)")
plt.legend(bbox_to_anchor=(1.0, 0.5, 0.5, 0.5))
plt.show()

If there is no time point found before or after or within the interval, the function assigns Nans.
tsd = nap.Tsd(t=np.arange(1, 10, 1), d=np.arange(10, 100, 10))
ep = nap.IntervalSet(start=0, end = 10)
ts = nap.Ts(t=[0, 9])
# First ts is at 0s. First tsd is at 1s.
ts.value_from(tsd, ep=ep, mode="before")
Time (s)
---------- ---
0 nan
9 90
dtype: float64, shape: (2,)
threshold
#
The method threshold
of Tsd
returns a new Tsd
with all the data above or
below a certain threshold. Default is above
. The time support
of the new Tsd
object get updated accordingly.
Show code cell content
tsd = nap.Tsd(t=np.arange(0, 100, 1), d=np.sin(np.arange(0, 10, 0.1)))
tsd_above = tsd.threshold(0.5, method='above')
This method can be used to isolate epochs for which a signal is above/below a certain threshold.
epoch_above = tsd_above.time_support
Show code cell source
plt.figure()
plt.plot(tsd, label="tsd")
plt.plot(tsd_above, 'o-', label="tsd_above")
[plt.axvspan(s, e, alpha=0.2) for s, e in epoch_above.values]
plt.axhline(0.5, linewidth=0.5, color='grey')
plt.legend()
plt.xlabel("Time (s)")
plt.title("tsd.threshold(0.5)")
plt.show()

derivative
#
The derivative
method of Tsd
, TsdFrame
and TsdTensor
can be used to calculate the derivative of a time series with respect to time. It is a wrapper of numpy.gradient
.
Show code cell content
tsd = nap.Tsd(
t=np.arange(0, 10, 0.1),
d=np.sin(np.arange(0, 10, 0.1)),
)
ep = nap.IntervalSet(start=[0, 6], end=[4, 10])
derivative = tsd.derivative(ep=ep)
Show code cell source
plt.figure()
plt.plot(tsd, label="tsd")
plt.plot(derivative, 'o-', label="derivative")
[plt.axvspan(s, e, alpha=0.2) for s, e in derivative.time_support.values]
plt.axhline(0, linewidth=0.5, color='grey')
plt.legend(loc="lower right")
plt.xlabel("Time (s)")
plt.title("tsd.derivative()")
plt.show()

to_trial_tensor
#
Tsd
, TsdFrame
, and TsdTensor
all have the method to_trial_tensor
, which creates a numpy array from an IntervalSet
by slicing the time series. The resulting tensor has shape (shape of time series, number of trials, number of time points), where the first dimension(s) is dependent on the object.
tsd = nap.Tsd(t=np.arange(0, 100, 1), d=np.sin(np.arange(0, 10, 0.1)))
ep = nap.IntervalSet([0, 10, 30, 50, 70, 75], metadata={'trials':[1, 2, 3]})
print(ep)
index start end trials
0 0 10 1
1 30 50 2
2 70 75 3
shape: (3, 2), time unit: sec.
The following example returns a tensor with shape (3, 21), for 3 trials and 21 time points, where the first dimension is dropped due to this being a Tsd
object.
tensor = tsd.to_trial_tensor(ep)
print(tensor, "\n")
print("Tensor shape = ", tensor.shape)
[[ 0. 0.09983342 0.19866933 0.29552021 0.38941834 0.47942554
0.56464247 0.64421769 0.71735609 0.78332691 0.84147098 nan
nan nan nan nan nan nan
nan nan nan]
[ 0.14112001 0.04158066 -0.05837414 -0.15774569 -0.2555411 -0.35078323
-0.44252044 -0.52983614 -0.61185789 -0.68776616 -0.7568025 -0.81827711
-0.87157577 -0.91616594 -0.95160207 -0.97753012 -0.993691 -0.99992326
-0.99616461 -0.98245261 -0.95892427]
[ 0.6569866 0.72896904 0.79366786 0.85043662 0.8987081 0.93799998
nan nan nan nan nan nan
nan nan nan nan nan nan
nan nan nan]]
Tensor shape = (3, 21)
Show code cell source
color_cycle = plt.rcParams['axes.prop_cycle'].by_key()['color']
plt.figure()
plt.subplot(121)
plt.plot(tsd, '-', label="tsd")
for i in range(len(ep)):
plt.plot(tsd.get(ep[i,0], ep[i,1]), 'o-', color=color_cycle[i])
plt.axvspan(ep[i,0], ep[i,1], alpha=0.2)
plt.legend(loc="lower right")
plt.xlabel("Time (s)")
plt.subplot(122)
plt.plot(tensor.T, 'o-')
plt.title("tsd.to_trial_tensor(ep)")
plt.tight_layout()
plt.xlabel("Trial time")
plt.show()

Since trial 2 is twice as long as trial 1, the array is padded with NaNs. The padding value can be changed by setting the parameter padding_value
.
tensor = tsd.to_trial_tensor(ep, padding_value=-1)
print(tensor, "\n")
print("Tensor shape = ", tensor.shape)
[[ 0. 0.09983342 0.19866933 0.29552021 0.38941834 0.47942554
0.56464247 0.64421769 0.71735609 0.78332691 0.84147098 -1.
-1. -1. -1. -1. -1. -1.
-1. -1. -1. ]
[ 0.14112001 0.04158066 -0.05837414 -0.15774569 -0.2555411 -0.35078323
-0.44252044 -0.52983614 -0.61185789 -0.68776616 -0.7568025 -0.81827711
-0.87157577 -0.91616594 -0.95160207 -0.97753012 -0.993691 -0.99992326
-0.99616461 -0.98245261 -0.95892427]
[ 0.6569866 0.72896904 0.79366786 0.85043662 0.8987081 0.93799998
-1. -1. -1. -1. -1. -1.
-1. -1. -1. -1. -1. -1.
-1. -1. -1. ]]
Tensor shape = (3, 21)
By default, time series are aligned to the start of each trial. To align the time series to the end of each trial, the optional parameter align
can be set to “end”.
tensor = tsd.to_trial_tensor(ep, align="end")
print(tensor, "\n")
print("Tensor shape = ", tensor.shape)
[[ nan nan nan nan nan nan
nan nan nan nan 0. 0.09983342
0.19866933 0.29552021 0.38941834 0.47942554 0.56464247 0.64421769
0.71735609 0.78332691 0.84147098]
[ 0.14112001 0.04158066 -0.05837414 -0.15774569 -0.2555411 -0.35078323
-0.44252044 -0.52983614 -0.61185789 -0.68776616 -0.7568025 -0.81827711
-0.87157577 -0.91616594 -0.95160207 -0.97753012 -0.993691 -0.99992326
-0.99616461 -0.98245261 -0.95892427]
[ nan nan nan nan nan nan
nan nan nan nan nan nan
nan nan nan 0.6569866 0.72896904 0.79366786
0.85043662 0.8987081 0.93799998]]
Tensor shape = (3, 21)
Show code cell source
color_cycle = plt.rcParams['axes.prop_cycle'].by_key()['color']
plt.figure()
plt.subplot(121)
plt.plot(tsd, '-', label="tsd")
for i in range(len(ep)):
plt.plot(tsd.get(ep[i,0], ep[i,1]), 'o-', color=color_cycle[i])
plt.axvspan(ep[i,0], ep[i,1], alpha=0.2)
plt.legend(loc="lower right")
plt.xlabel("Time (s)")
plt.subplot(122)
plt.plot(tensor.T, 'o-')
plt.title(r"tsd.to_trial_tensor(ep, align='end')")
plt.tight_layout()
plt.xlabel("Trial time")
plt.show()

Mapping between TsGroup
and Tsd
#
It’s is possible to transform a TsGroup
to Tsd
with the method
to_tsd
and a Tsd
to TsGroup
with the method to_tsgroup
.
This is useful to flatten the activity of a population in a single array.
tsd = tsgroup.to_tsd()
print(tsd)
Time (s)
------------ --
0.845976217 2
1.079279217 2
2.446631492 1
3.663969995 1
6.15763497 2
10.927127229 2
11.212758867 2
...
88.712136029 2
91.788733959 2
93.927739283 1
95.04794207 1
95.684672417 0
96.576969968 1
99.893956916 2
dtype: float64, shape: (60,)
The object tsd
contains all the timestamps of the tsgroup
with
the associated value being the index of the unit in the TsGroup
.
The method to_tsgroup
converts the Tsd
object back to the original TsGroup
.
back_to_tsgroup = tsd.to_tsgroup()
print(back_to_tsgroup)
Index rate
------- ------
0 0.1
1 0.2
2 0.3
Parameterizing a raster#
The method to_tsd
makes it easier to display a raster plot.
TsGroup
object can be plotted with plt.plot(tsgroup.to_tsd(), 'o')
.
Timestamps can be mapped to any values passed directly to the method
or by giving the name of a specific metadata name of the TsGroup
.
tsgroup['label'] = np.arange(3)*np.pi
print(tsgroup)
Index rate label
------- ------ -------
0 0.1 0
1 0.2 3.14
2 0.3 6.28
Show code cell source
plt.figure()
plt.subplot(2,2,1)
plt.plot(tsgroup.to_tsd(), '|')
plt.title("tsgroup.to_tsd()")
plt.xlabel("Time (s)")
plt.subplot(2,2,2)
plt.plot(tsgroup.to_tsd([10,20,30]), '|')
plt.title("togroup.to_tsd([10,20,30])")
plt.xlabel("Time (s)")
plt.subplot(2,2,3)
plt.plot(tsgroup.to_tsd("label"), '|')
plt.title("togroup.to_tsd('label')")
plt.xlabel("Time (s)")
plt.tight_layout()
plt.show()

Special slicing : TsdFrame#
For users that are familiar with pandas, TsdFrame
is the closest object to a DataFrame.
but there are distinctive behavior when slicing the object. TsdFrame
behaves primarily like a numpy array. This section
lists all the possible ways of slicing TsdFrame
.
1. If not column labels are passed#
tsdframe = nap.TsdFrame(t=np.arange(4), d=np.random.randn(4,3))
print(tsdframe)
Time (s) 0 1 2
---------- -------- -------- --------
0 -1.74964 0.14769 0.84621
1 -1.00791 -0.51104 -1.65441
2 0.30105 -1.10543 -0.48815
3 -0.27937 -0.53721 0.45948
dtype: float64, shape: (4, 3)
Slicing should be done like numpy array :
tsdframe[0]
array([-1.74964308, 0.14768884, 0.84621487])
tsdframe[:, 1]
Time (s)
---------- ---------
0 0.147689
1 -0.51104
2 -1.10543
3 -0.537207
dtype: float64, shape: (4,)
tsdframe[:, [0, 2]]
Time (s) 0 2
---------- -------- --------
0 -1.74964 0.84621
1 -1.00791 -1.65441
2 0.30105 -0.48815
3 -0.27937 0.45948
dtype: float64, shape: (4, 2)
2. If column labels are passed as integers#
The typical case is channel mapping. The order of the columns on disk are different from the order of the columns on the recording device it corresponds to.
tsdframe = nap.TsdFrame(t=np.arange(4), d=np.random.randn(4,4), columns = [3, 2, 0, 1])
print(tsdframe)
Time (s) 3 2 0 1
---------- -------- -------- -------- --------
0 1.07118 -0.41692 1.44729 1.78244
1 -0.7312 -1.23921 -1.30841 -0.06634
2 1.12585 -0.57172 -1.06702 -0.57482
3 -1.27139 -0.6283 0.53268 -0.00437
dtype: float64, shape: (4, 4)
In this case, indexing like numpy still has priority which can led to confusing behavior :
tsdframe[:, [0, 2]]
Time (s) 3 0
---------- -------- --------
0 1.07118 1.44729
1 -0.7312 -1.30841
2 1.12585 -1.06702
3 -1.27139 0.53268
dtype: float64, shape: (4, 2)
Note how this corresponds to column labels 3 and 0.
To slice using column labels only, the TsdFrame
object has the loc
method similar to Pandas :
tsdframe.loc[[0, 2]]
Time (s) 0 2
---------- -------- --------
0 1.44729 -0.41692
1 -1.30841 -1.23921
2 -1.06702 -0.57172
3 0.53268 -0.6283
dtype: float64, shape: (4, 2)
In this case, this corresponds to columns labelled 0 and 2.
3. If column labels are passed as strings#
Similar to Pandas, it is possible to label columns using strings.
tsdframe = nap.TsdFrame(t=np.arange(4), d=np.random.randn(4,3), columns = ["kiwi", "banana", "tomato"])
print(tsdframe)
Time (s) kiwi banana tomato
---------- -------- -------- --------
0 0.00064 -2.24478 -0.66393
1 -0.10582 -0.39936 -2.11026
2 -0.89079 -0.71359 1.61677
3 0.26465 1.76876 0.24646
dtype: float64, shape: (4, 3)
When the column labels are all strings, it is possible to use either direct bracket indexing or using the loc
method:
print(tsdframe['kiwi'])
print(tsdframe.loc['kiwi'])
Time (s)
---------- ------------
0 0.000641693
1 -0.105816
2 -0.890792
3 0.264645
dtype: float64, shape: (4,)
Time (s)
---------- ------------
0 0.000641693
1 -0.105816
2 -0.890792
3 0.264645
dtype: float64, shape: (4,)
4. If column labels are mixed type#
It is possible to mix types in column names.
tsdframe = nap.TsdFrame(t=np.arange(4), d=np.random.randn(4,3), columns = ["kiwi", 0, np.pi])
print(tsdframe)
Time (s) kiwi 0 3.141592653589793
---------- -------- -------- -------------------
0 -0.61984 -0.31593 0.52936
1 1.12638 -0.95269 1.2615
2 0.80171 0.13297 -1.76248
3 0.08233 -0.87188 -0.08703
dtype: float64, shape: (4, 3)
Direct bracket indexing only works if the column label is a string.
print(tsdframe['kiwi'])
Time (s)
---------- ---------
0 -0.619841
1 1.12638
2 0.801713
3 0.082327
dtype: float64, shape: (4,)
To slice with mixed types, it is best to use the loc
method :
print(tsdframe.loc[['kiwi', np.pi]])
Time (s) kiwi 3.141592653589793
---------- -------- -------------------
0 -0.61984 0.52936
1 1.12638 1.2615
2 0.80171 -1.76248
3 0.08233 -0.08703
dtype: float64, shape: (4, 2)
In general, it is probably a bad idea to mix types when labelling columns.
Interval sets methods#
Interaction between epochs#
Intervals can be combined in different ways.
epoch1 = nap.IntervalSet(start=[0, 40], end=[10, 50]) # no time units passed. Default is us.
epoch2 = nap.IntervalSet(start=[5, 30], end=[20, 45])
print(epoch1, "\n")
print(epoch2, "\n")
index start end
0 0 10
1 40 50
shape: (2, 2), time unit: sec.
index start end
0 5 20
1 30 45
shape: (2, 2), time unit: sec.
union
#
epoch = epoch1.union(epoch2)
print(epoch)
index start end
0 0 20
1 30 50
shape: (2, 2), time unit: sec.
Show code cell source
colors = plt.rcParams['axes.prop_cycle'].by_key()['color']
plt.figure()
[plt.axvspan(s, e, ymin=0.8, ymax=1, color=colors[0]) for s, e in epoch1.values]
[plt.axvspan(s, e, ymin=0.4, ymax=0.6, color=colors[1]) for s, e in epoch2.values]
[plt.axvspan(s, e, ymin=0, ymax=0.2, color=colors[2]) for s, e in epoch.values]
plt.xlabel("Time (s)")
plt.ylim(0, 1)
plt.xlim(0, 50)
plt.gca().spines["left"].set_visible(False)
plt.yticks([0.1, 0.5, 0.9], ['epoch1.union(epoch2)', 'epoch2', 'epoch1'])
plt.title("Union")
plt.show()

intersect
#
epoch = epoch1.intersect(epoch2)
print(epoch)
index start end
0 5 10
1 40 45
shape: (2, 2), time unit: sec.
Show code cell source
colors = plt.rcParams['axes.prop_cycle'].by_key()['color']
plt.figure()
[plt.axvspan(s, e, ymin=0.8, ymax=1, color=colors[0]) for s, e in epoch1.values]
[plt.axvspan(s, e, ymin=0.4, ymax=0.6, color=colors[1]) for s, e in epoch2.values]
[plt.axvspan(s, e, ymin=0, ymax=0.2, color=colors[2]) for s, e in epoch.values]
plt.xlabel("Time (s)")
plt.ylim(0, 1)
plt.xlim(0, 50)
plt.gca().spines["left"].set_visible(False)
plt.yticks([0.1, 0.5, 0.9], ['epoch1.intersect(epoch2)', 'epoch2', 'epoch1'])
plt.title("Intersection")
plt.show()

set_diff
#
epoch = epoch1.set_diff(epoch2)
print(epoch)
index start end
0 0 5
1 45 50
shape: (2, 2), time unit: sec.
Show code cell source
colors = plt.rcParams['axes.prop_cycle'].by_key()['color']
plt.figure()
[plt.axvspan(s, e, ymin=0.8, ymax=1, color=colors[0]) for s, e in epoch1.values]
[plt.axvspan(s, e, ymin=0.4, ymax=0.6, color=colors[1]) for s, e in epoch2.values]
[plt.axvspan(s, e, ymin=0, ymax=0.2, color=colors[2]) for s, e in epoch.values]
plt.xlabel("Time (s)")
plt.ylim(0, 1)
plt.xlim(0, 50)
plt.gca().spines["left"].set_visible(False)
plt.yticks([0.1, 0.5, 0.9], ['epoch1.set_diff(epoch2)', 'epoch2', 'epoch1'])
plt.title("Difference")
plt.show()

split
#
Useful for chunking time series, the split
method splits an IntervalSet
in a new
IntervalSet
based on the interval_size
argument.
epoch = nap.IntervalSet(start=0, end=100)
print(epoch.split(10, time_units="s"))
index start end
0 0 10
1 10 20
2 20 30
3 30 40
4 40 50
5 50 60
6 60 70
7 70 80
8 80 90
9 90 100
shape: (10, 2), time unit: sec.
Drop intervals#
epoch = nap.IntervalSet(start=[5, 30], end=[6, 45])
print(epoch)
index start end
0 5 6
1 30 45
shape: (2, 2), time unit: sec.
drop_short_intervals
#
print(
epoch.drop_short_intervals(threshold=5)
)
index start end
0 30 45
shape: (1, 2), time unit: sec.
drop_long_intervals
#
print(
epoch.drop_long_intervals(threshold=5)
)
index start end
0 5 6
shape: (1, 2), time unit: sec.
merge_close_intervals
#
Show code cell source
epoch = nap.IntervalSet(start=[1, 7], end=[6, 45])
print(epoch)
index start end
0 1 6
1 7 45
shape: (2, 2), time unit: sec.
If two intervals are closer than the threshold
argument, they are merged.
print(
epoch.merge_close_intervals(threshold=2.0)
)
index start end
0 1 45
shape: (1, 2), time unit: sec.