pyriodic package#

Submodules#

pyriodic.circular#

class pyriodic.circular.Circular(data: ndarray, labels: list | ndarray | None = None, unit: str = 'radians', full_range: int | None = None)#

Bases: object

A class for representing and working with circular data (e.g., angles, time-of-day, phase).

This class supports circular statistics and visualization of data that wraps around a fixed range, such as angles (in radians or degrees), phases, hours, or other cyclic measurements. It provides basic unit validation, conversion between degrees and radians, and visualisation tools.

Parameters:
  • data (array-like) – A sequence of numerical values representing circular measurements. Values should be in the range appropriate to the specified unit.

  • labels (array-like of str or int, optional) – Optional condition labels or identifiers corresponding to each data point.

  • unit (str, optional) – The unit of the input data. Must be one of {“radians”, “degrees”}. Default is “radians”. Assumes radian range to be from 0 to 2pi.

  • full_range (int, optional)

data#

The circular data as a NumPy array.

Type:

ndarray

labels#

Optional labels (e.g., condition tags) for each data point.

Type:

ndarray or list

unit#

Unit of measurement, either “radians” or “degrees”.

Type:

str

classmethod from_multiple(circular_objects, labels=None)#
mean(group_by_label: bool = False)#
plot(ax=None, histogram=None, group_by_labels=False)#
r()#

pyriodic.desc#

pyriodic.desc.angular_deviation(rad: ndarray)#
pyriodic.desc.circular_mean(rad: ndarray, wrap_to_2pi: bool = True)#

Compute the mean angle from a list of angles in radians.

Parameters:#

radnp.ndarray

Array of angles in radians.

wrap_to_2pibool, default=True

If True, wraps result to range [0, 2π]. If False, result is in [-π, π].

Returns:#

meanfloat

The circular mean angle.

pyriodic.desc.circular_r(rad: ndarray)#

Compute the length of the mean resultant vector ( r ), a measure of circular concentration.

\[r = \sqrt{\bar{C}^2 + \bar{S}^2}\]
pyriodic.desc.circular_standard_deviation(rad: ndarray)#

pyriodic.phase_events#

class pyriodic.phase_events.PhaseEvents(phase_dict: dict)#

Bases: object

mean()#
plot(histogram: ndarray | None = None, savepath: Path | str | None = None)#
r()#
to_circular(include: str | list[str] | None = None)#

Convert selected events from the phase dictionary to a Circular object.

Parameters:

include (str, list of str, or None) –

  • If a string, includes all keys in the phase_dict that contain this substring.

  • If a list of strings, includes matching keys directly.

  • If None, includes all keys in phase_dict.

Returns:

A Circular object constructed from the selected phase values.

Return type:

Circular

pyriodic.phase_events.compute_segment_outliers(segments, threshold=3, sampling_rate=None)#

Identify segment durations that deviate > threshold * SD from the mean. Optionally normalize by sampling_rate.

pyriodic.phase_events.create_phase_events(phase_ts: ndarray, events: ndarray, event_labels: ndarray | None = None, unit: str = 'radians', first_samp: int = 0, rejection_method: str | None = None, rejection_criterion: float | None = None, bad_segments: ndarray | None = None, return_rejected: bool = False) PhaseEvents | tuple[PhaseEvents, list[int]]#

Create Circular object(s) from a phase angle time series and event markers. Optionally apply cycle-based rejection criteria to exclude events occurring during atypical phase dynamics.

Parameters:
  • phase_ts (np.ndarray) – 1D array of phase values (in radians), typically spanning multiple 0–2π cycles.

  • events (np.ndarray) – 1D array of sample indices at which to extract phase values.

  • event_labels (np.ndarray, optional) – Labels for grouping events. If None, returns a single Circular object; otherwise, returns a PhaseEvents container grouped by unique labels.

  • unit (str) – Unit of the input phase time series and the desired unit for output Circular objects. Must be either ‘radians’ or ‘degrees’. Internally, all computations are performed in radians.

  • first_samp (int) – Offset to subtract from each event index to align with the phase time series (useful if phase_ts is a segment of a longer recording).

  • rejection_method (str, optional) –

    Method to reject events based on surrounding phase dynamics. Currently supported:

    • ’segment_duration_sd’ : excludes events occurring during rising or falling phase segments whose durations deviate more than rejection_criterion standard deviations from the mean across cycles.

  • rejection_criterion (float, optional) – Threshold (in standard deviation units) for identifying outlier segments. Only used if rejection_method=’segment_duration_sd’. Default is 3.

  • bad_segments (Optional[np.ndarray]) – For ignoring events that fall within specific segments of the phase time series. Shape should be (n_segments, 2), where each row is a (start, stop) index pair in samples.

  • return_rejected (bool) – If True, also return a list of rejected event indices.

Returns:

  • Circular or PhaseEvents – If event_labels is None, returns a single Circular object. Otherwise, returns a PhaseEvents object containing condition-grouped Circular data.

  • (optional) list[int] – If return_rejected is True, returns a second value: the list of event indices that were rejected.

pyriodic.phase_events.get_outlier_sample_indices(segments, outlier_indices)#

Convert outlier segments to a flat set of sample indices for fast lookup.

pyriodic.phase_events.get_phase_segments(phase_ts: ndarray)#

Identify rising (0→π) and falling (π→2π) phase segments in a wrapped phase signal.

Parameters:

phase_ts (np.ndarray) – 1D array of phase values (in radians), assumed to evolve continuously over time.

Returns:

  • rising_segments (list of (start_idx, stop_idx)) – List of index tuples for rising phase segments (0 to π).

  • falling_segments (list of (start_idx, stop_idx)) – List of index tuples for falling phase segments (π to 2π).

pyriodic.preproc module#

class pyriodic.preproc.RawSignal(data, fs, info=None)#

Bases: object

annotate_bad_segments(segments: ndarray, unit: Literal['s', 'sample'])#

Annotates bad segments in the signal. :param segments: Array of segments to annotate. Dimensions should be (n, 2) where n is the number of segments. Each segment is defined by a start and end point. :type segments: np.ndarray :param unit: Unit of the segments, either “s” for seconds or “sample” for samples. :type unit: str

convert_seconds_to_samples(array)#
copy()#
filter_bandpass(low, high)#
property history#
interpolate_missing()#
phase_hilbert()#

Extract instantaneous phase angle using the Hilbert Transform.

Parameters:

ts (np.ndarray) – 1D time series

Returns:

phase angles in radians (0 to 2π)

Return type:

np.ndarray

phase_linear(peak_finder=None, distance=100, prominence=0.01)#

Extract phase using linear interpolation between peaks and troughs at

Parameters:
  • peak_finder (callable) – Optional custom function(ts, **kwargs) -> np.ndarray of peak indices.

  • distance (int) – Minimum distance between peaks.

  • prominence (float) – Prominence threshold for peak detection.

Returns:

phase peaks troughs

phase_onepoint(peak_finder=None, distance=100, prominence=0.01)#

Extract phase using linear interpolation between from 0 to 2pi between peaks.

Parameters:
  • peak_finder (callable) – Optional custom function(ts, **kwargs) -> np.ndarray of peak indices.

  • distance (int) – Minimum distance between peaks.

  • prominence (float) – Prominence threshold for peak detection.

Returns:

phase peaks

phase_threepoint(peak_finder: Callable[[...], ndarray] | None = None, distance: int = 100, prominence: float | int = 0.01, percentile: float | int = 50, descent_window: int = 5)#

Extract phase using a three-point method: Peak → descending slope → flat region → ascending slope → next peak.

Parameters:
  • peak_finder (callable) – Optional function(ts, **kwargs) → np.ndarray of peak indices.

  • distance (int) – Min distance between peaks (used if no custom peak_finder).

  • prominence (float) – Prominence threshold for peak detection.

  • percentile (float) – Percentile of absolute gradient below which region is considered ‘flat’.

  • descent_window (int) – Number of samples used to confirm descent/ascent before/after flat region.

Returns:

Phase array in radians (0 to 2π) peaks (np.ndarray): Indices of detected peaks troughs (list of tuples): List of (trough_start, trough_end) for flat segments

Return type:

phase (np.ndarray)

remove_outliers(threshold=2.5, linear_interpolation=True)#

Removes outliers …. threshold + linear interpolation

resample(sfreq=typing.Union[int, float])#
Parameters:

sfreq – New sampling rate

smoothing(window_size: int = 20)#

Smooths the timeseries by calculating the moving average

Parameters:

window_size (int) – Number of time points on either side of the center point to include in the moving average. The total window size will be 2 * window_size + 1.

zscore()#

Normalises the signal in-place to mean 0 and unit variance.

pyriodic.stats module#

pyriodic.utils module#

pyriodic.utils.calculate_p_value(observed_stat: float, null_distribution: ndarray, alternative: Literal['greater', 'less', 'two-sided'] = 'greater') float#

Compute the p-value for a given observed statistic against a null distribution.

Parameters:
  • observed_stat (float) – The observed test statistic.

  • null_distribution (np.ndarray) – Array of test statistics computed under the null hypothesis.

  • alternative ({'greater', 'less', 'two-sided'}, default='greater') – Defines the alternative hypothesis: - ‘greater’: test if observed > null - ‘less’: test if observed < null - ‘two-sided’: test if observed is different from null (absolute deviation)

Returns:

p_value – The computed p-value.

Return type:

float

pyriodic.utils.dat2rad(data: ndarray | float | int, full_range: float | int = 360)#
pyriodic.utils.rad2dat(rad: ndarray | float | int, full_range: float | int = 360)#

pyriodic.viz#

class pyriodic.viz.CircPlot(circ: Circular, title: str | None = None, group_by_labels: bool = True, colours=None, fig_size=(6, 6), dpi=300, ax=None, radius_lim=None, angles: list | None = None, labels: list | None = None)#

Bases: object

add_arrows(angles: ndarray, lengths: ndarray | None = None, labels: ndarray | None = None, **kwargs)#

Plot arrows at specified angles and lengths.

Parameters:
  • angles (np.ndarray) – 1D array of angles (in radians) where arrows should be drawn.

  • lengths (np.ndarray, optional) – Length of each arrow. If None, all arrows will have unit length.

  • labels (np.ndarray, optional) – Optional label array (same length as angles) for grouping and coloring.

  • kwargs (dict) – Additional keyword arguments passed to ax.arrow. Common examples: width, color, alpha.

add_circular_mean(grouped: bool | None = None, **kwargs)#

Plot mean resultant vector(s) as arrows.

Parameters:
  • grouped (bool, optional) – If None, uses self.group_by_labels. If True, plots a vector per label. If False, plots a single mean vector for all data.

  • kwargs (dict) – Additional keyword arguments passed to ax.arrow.

add_density(kappa=20, n_bins=500, grouped: bool | None = None, **kwargs)#

Add a circular density estimate using Von Mises KDE.

Parameters:
  • kappa (float) – Concentration parameter of the Von Mises distribution (higher = sharper).

  • n_bins (int) – Number of angular bins to evaluate the density on.

  • grouped (bool, optional) – If None, uses self.group_by_labels. If True, plots a density curve for each label separately. If False, plots a single joint density curve for all data.

  • kwargs (dict) – Additional keyword arguments passed to ax.plot (e.g., linewidth, linestyle, alpha).

add_histogram(data: ndarray | None = None, label: str | None = None, bins=36, alpha=0.2, color: str = 'grey')#

Plot histogram as radial bars on the polar axis. If data is None, uses circular data in self.circs.

Parameters: - data: np.array - bins: Number of angular bins (default 36). - alpha: Transparency of bars.

add_legend(**kwargs)#

Add a legend to the plot.

Parameters:

kwargs (dict) – Additional arguments passed to ax.legend().

add_points(grouped: bool | None = None, **kwargs)#

Plot circular data points on the polar axis.

Parameters:
  • grouped (bool, optional) – If None, uses the self.group_by_labels setting. If True, plots each label separately. If False, plots all data points together, ignoring labels.

  • kwargs (dict) – Additional keyword arguments passed to ax.scatter, such as s, alpha, or marker.

prepare_ax(radius_lim=None, angles=None, labels=None, title=None)#

Prepare and customize a polar plot axis (self.ax) with standard aesthetic settings.

Parameters:#

radius_limtuple or None

Optional tuple (min, max) for setting radial (r-axis) limits.

angleslist of float or None

Angles (in degrees) where labels should be placed on the theta axis. Default is [0, 90, 180, 270, 360].

labelslist of str or None

Labels corresponding to angles. Default is [“0/2π”, “”, “π”, “”, “”].

save(filename, **kwargs)#

Save the figure to a file

show()#
pyriodic.viz.plot_phase_diagnostics(phase_angles: dict[str, ndarray], fs: int | float, data: ndarray | None = None, events: list | ndarray | None = None, event_labels: list | ndarray | None = None, peaks=None, troughs=None, flat_start_stop=None, savepath=None, figsize=None, window_duration: float = 20.0, title=None)#
Parameters:
  • phase_angles (dict[str, np.ndarray]) – Dictionary of named phase angle signals (e.g., {“Three-point”: …, “Hilbert”: …}). Each should be 1D array of same length as data.

  • fs (float) – Sampling frequency (Hz).

  • data (np.ndarray, optional) – Raw or preprocessed time series signal (same length as phase arrays).

  • events (list[int] or np.ndarray, optional) – Sample indices of events to mark (e.g., stimulus or response times).

  • event_labels (list[str], optional) – Label for each event (same length as events). Used for color grouping.

  • peaks (list[int] or np.ndarray, optional) – Sample indices of identified peaks.

  • troughs (list[int] or np.ndarray, optional) – Sample indices of identified troughs.

  • flat_start_stop (list[tuple[int, int]] or list[int], optional) – Flat segments as start–stop index tuples or flat indices directly.

  • savepath (str or Path, optional) – If provided, saves static plot to this location (only applies if interactive=False).

  • figsize (tuple[int, int], optional) – Size of the figure in inches (only applies if interactive=False).

  • window_duration (float) – Duration (in seconds) of each visible window in interactive mode.

Returns:

  • fig (matplotlib.figure.Figure) – The created figure.

  • axes (list[matplotlib.axes.Axes]) – The axes used in the plot (one per row: signal + phase tracks).