pyriodic package#

circular#

class pyriodic.circular.Circular(data: ndarray, labels: list | ndarray | None = None, unit: str = 'radians', full_range: tuple | 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 visualisation 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 (tuple, 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)#

Compute the circular mean of the data.

Parameters:

group_by_label (bool, default=False) – If True, computes the mean for each label group separately.

Returns:

mean – The circular mean angle, either as a single value or a dictionary of means by label.

Return type:

float or dict

median(group_by_label: bool = False)#

Compute the circular median of the data.

Parameters:

group_by_label (bool, default=False) – If True, computes the median for each label group separately.

Returns:

median – The circular median angle, either as a single value or a dictionary of medians by label.

Return type:

float or dict

plot(ax=None, histogram=None, group_by_labels=False)#
r(group_by_label: bool = False)#

Compute mean resultant length of the data, which is a measure of the data’s concentration.

Parameters:

group_by_label (bool, default=False) – If True, computes the median for each label group separately.

Returns:

r – The mean resultant length, either as a single value or a dictionary of values by label.

Return type:

float or dict

density#

pyriodic.density.vonmises_kde(data, kappa, min_x=0, max_x=6.283185307179586, n_bins=100)#

Vectorized von Mises kernel density estimate for circular data.

Parameters:
  • data (np.ndarray) – Circular data in radians.

  • kappa (float) – Concentration parameter (higher = narrower peaks).

  • min_x (float) – Minimum value of the evaluation grid (default 0).

  • max_x (float) – Maximum value of the evaluation grid (default 2*pi).

  • n_bins (int) – Number of points in the evaluation grid.

Returns:

  • x (np.ndarray) – Evaluation points.

  • kde (np.ndarray) – Estimated density at each point.

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_median(rad: ndarray)#

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

Parameters:#

radnp.ndarray

Array of angles in radians.

Returns:#

median : float

The circular median 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)#

permutation#

phase_events#

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) Circular | tuple[Circular, list[int]]#

Create Circular object from a phase angle time series and event markers, optionally applying rejection criteria.

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. Supported values: ‘segment_duration_sd’ — excludes events during rising/falling phase segments whose durations deviate more than rejection_criterion standard deviations from the mean.

  • 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π).

preproc#

class pyriodic.preproc.RawSignal(data, fs, info=None, bad_segments: None | ndarray = 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=0.1, high=1.0)#
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_onepoint(peak_finder=None, distance=100, prominence=0.01)#

Extract phase by linearly interpolating from 0 to 2π between detected peaks.

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

  • distance (int) – Minimum distance between peaks, passed to the peak detection algorithm in seconds.

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

Returns:

  • phase (np.ndarray) – The interpolated phase values (ranging from 0 to 2π).

  • peaks (np.ndarray) – Indices of the detected peaks used for phase interpolation.

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) – Optional custom function of the form func(ts, **kwargs) -> np.ndarray returning peak indices.

  • distance (int) – Minimum distance between peaks, used if no custom peak finder is provided.

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

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

  • descent_window (int) – Number of samples used to confirm descending and ascending slopes before and after flat regions.

Returns:

  • phase (np.ndarray) – Phase array in radians (0 to 2π).

  • peaks (np.ndarray) – Indices of detected peaks.

  • troughs (list of tuple) – Each tuple contains the (start_index, end_index) of a flat region between peaks.

phase_twopoint(peak_finder=None, distance=1, prominence=0.01)#

Extract phase using linear interpolation between peaks and troughs.

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

  • distance (int) – Minimum distance in seconds between peaks, passed to the peak detection algorithm.

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

Returns:

  • phase (np.ndarray) – The extracted phase values (in radians).

  • peaks (np.ndarray) – Indices of detected peaks.

  • troughs (np.ndarray) – Indices of detected troughs.

plot(ax=None, start=0, duration=20)#

Plots the time series data.

Parameters:
  • ax – Matplotlib axis to plot on. If None, creates a new figure.

  • start – Start time in seconds.

  • duration – Duration in seconds to plot.

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: float = 50.0)#

Smooths the timeseries by calculating the moving average

Parameters:

window_size (float) – Size of the moving window in milliseconds. Default is 50 milliseconds.

zscore()#

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

regression#

utils#

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: tuple[float, float] = (0, 360))#
pyriodic.utils.rad2dat(rad: ndarray | float | int, full_range: tuple[float, float] = (0, 360))#
pyriodic.utils.rad2deg(rad: ndarray | float | int) ndarray | float | int#

Convert radians to degrees.

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_connected_points(y=None, label=None, connect_last_to_first=True, **kwargs)#

Plot connected data points on the polar axis.

Parameters:

y (float, optional) – Y-coordinate for the points. If None, defaults to 0.5 for all points.

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_hline(y=0, **kwargs)#

Add a horizontal line across the polar plot.

Parameters:
  • y (float) – Y-coordinate for the horizontal line (default 0).

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

add_legend(**kwargs)#

Add a legend to the plot.

Parameters:

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

add_points(grouped: bool | None = None, y=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.

  • y (float, optional) – Y-coordinate for the points. If None, defaults to 0.5 for all points.

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

add_polar_line(angles: ndarray, values: ndarray, errors: ndarray | None = None, color: str | None = 'orange', label: str | None = 'Values', alpha: float = 0.2, **kwargs)#

Plot a line with optional shaded error band on the polar axis.

Parameters:
  • angles (np.ndarray) – 1D array of angles (in radians) for the x-axis.

  • values (np.ndarray) – 1D array of values to plot on the radial axis.

  • errors (np.ndarray, optional) – 1D array of error values (e.g., std or SEM) for shading.

  • color (str, optional) – Color for line and fill.

  • label (str, optional) – Label for the line.

  • alpha (float, optional) – Transparency of the error band.

  • kwargs (dict) – Additional keyword arguments for ax.plot.

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()#
ticks_to_degrees(n_ticks=8)#

Convert the theta ticks from radians to degrees.

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, window_duration: float = 20.0, title=None, start=0)#
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.

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

  • start (float) – Start time (in seconds) for the initial view of the plot.

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).