Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ Analysis modules
filtering
perievent
randomize
signal
spectrum
tuning_curves
wavelets
Expand Down
8 changes: 4 additions & 4 deletions doc/examples/tutorial_phase_preferences.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,12 +173,12 @@ plt.show()
Computing phase
---------------

From the filtered signal, it is easy to get the phase using the Hilbert transform. Here we use scipy Hilbert method.
From the filtered signal, it is easy to get the phase using the Hilbert transform, (see the [user guide](/user_guide/13_phases_and_envelopes.md)).
Pynapple provides function [`compute_hilbert_phase`](pynapple.process.signal.compute_hilbert_phase) for this:

```{code-cell} ipython3
from scipy import signal

theta_phase = nap.Tsd(t=theta_band.t, d=np.angle(signal.hilbert(theta_band)))
theta_phase = nap.compute_hilbert_phase(theta_band)
theta_phase
```

Let's plot the phase.
Expand Down
16 changes: 15 additions & 1 deletion doc/user_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,39 +30,53 @@ Metadata <user_guide/03_metadata>

:::{card} High-level analysis
```{toctree}
:maxdepth: 2
Correlograms & ISI <user_guide/05_correlograms_isi>
```

```{toctree}
:maxdepth: 2
Tuning curves <user_guide/06_tuning_curves>
```

```{toctree}
:maxdepth: 2
Decoding <user_guide/07_decoding>
```

```{toctree}
:maxdepth: 2
Perievent / Spike-triggered average <user_guide/08_perievent>
```

```{toctree}
:maxdepth: 2
Randomization <user_guide/09_randomization>
```

```{toctree}
:maxdepth: 2
Power spectral density <user_guide/10_power_spectral_density>
```

```{toctree}
:maxdepth: 2
Wavelet decomposion <user_guide/11_wavelets>
```

```{toctree}
:maxdepth: 2
Filtering time series <user_guide/12_filtering>
```

```{toctree}
Building trial-based tensors <user_guide/13_warping>
:maxdepth: 2
Extracting phases and envelopes <user_guide/13_phases_and_envelopes>
```

```{toctree}
:maxdepth: 2
Building trial-based tensors <user_guide/14_warping>
```

:::
Expand Down
6 changes: 5 additions & 1 deletion doc/user_guide/01_introduction_to_pynapple.md
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,10 @@ Tuning curves of neurons based on spiking or calcium activity can be computed.

The wavelets module performs Morlet wavelets decomposition of a time series.

**[Warping](13_warping)**
**[Phases & envelopes](13_phases_and_envelopes)**

This modules allows for computing analytic signals and extracting the phase and envelope.

**[Warping](14_warping)**

This module provides methods for building trial-based tensors and time-warped trial-based tensors.
75 changes: 1 addition & 74 deletions doc/user_guide/12_filtering.md
Original file line number Diff line number Diff line change
Expand Up @@ -380,78 +380,5 @@ for arr, label in zip(
plt.legend()
plt.xlabel("Number of dimensions")
plt.ylabel("Time (s)")
plt.title("Low pass filtering benchmark")
```


***
Detecting Oscillatory Events
---------------------------

The filtering module also provides a method [`detect_oscillatory_events`](pynapple.process.filtering.detect_oscillatory_events) to automatically detect intervals containing oscillatory events (such as ripples or spindles) in a signal.

To demonstrate, let's create a synthetic signal where a fast oscillation (e.g., 40 Hz) occurs in a noisy signal:

```{code-cell} ipython3
# Parameters
fs = 1000 # Sampling frequency (Hz)
duration = 3 # seconds
t = np.linspace(0, duration, int(fs * duration))

# 40 Hz oscillation
osc = np.sin(2 * np.pi * 40 * t)
signal = np.zeros_like(t) + 0.2 * np.random.randn(len(t))
mask = (t > 1) & (t < 1.5)
signal[mask] += osc[mask]

# Create Tsd object
ts = nap.Tsd(t=t, d=signal)
```

```{code-cell} ipython3
:tags: [hide-input]

# Plot the signal
plt.figure(figsize=(15, 4))
plt.plot(ts, label="Signal (40 Hz oscillation)")
plt.xlabel("Time (s)")
plt.ylabel("Amplitude")
plt.title("Signal with oscillatory bursts")
plt.legend()
plt.show()
```

Now, let's use [`detect_oscillatory_events`](pynapple.process.filtering.detect_oscillatory_events) to find the oscillation intervals. The function will return the detected intervals as an `IntervalSet` along with metadata containing peak times.

```{code-cell} ipython3
# Define detection parameters
freq_band = (35, 45) # Bandpass filter for 40 Hz
thres_band = (1, 10) # Thresholds for normalized squared signal
min_dur = 0.03 # Minimum event duration (s)
max_dur = 1 # Maximum event duration (s)
min_inter = 0.02 # Minimum interval between events (s)
epoch = nap.IntervalSet(start=0, end=duration)

# Detect oscillatory events
osc_ep = nap.filtering.detect_oscillatory_events(
ts, epoch, freq_band, thres_band, (min_dur, max_dur), min_inter
)

print("Detected intervals:\n", osc_ep)
```

Let's visualize the detected intervals and peaks on the original signal:

```{code-cell} ipython3
:tags: [hide-input]

plt.figure(figsize=(15, 4))
plt.plot(ts, label="Signal")
for s, e in osc_ep.values:
plt.axvspan(s, e, color="orange", alpha=0.3)
plt.xlabel("Time (s)")
plt.ylabel("Amplitude")
plt.title("Detected oscillatory events")
plt.legend()
plt.show()
plt.title("Low pass filtering benchmark");
```
Loading
Loading