coco_pipe.dim_reduction.evaluation.geometry#

Trajectory geometry metrics and smoothing utilities.

This module provides small, generic helpers for analyzing ordered trajectories in embedded spaces. The functions are reducer-agnostic and operate on standard NumPy arrays rather than domain-specific container types.

Functions#

moving_average

Smooth a one-dimensional timecourse with a valid-mode moving average.

trajectory_acceleration

Compute instantaneous acceleration magnitude from second-order derivatives.

trajectory_jerk

Compute instantaneous jerk magnitude (derivative of acceleration).

trajectory_speed

Compute instantaneous speed from first-order trajectory differences.

trajectory_curvature

Compute geometric curvature from first- and second-order derivatives.

trajectory_dispersion

Compute within-group trajectory spread across time.

trajectory_displacement

Compute displacement from the initial trajectory state across time.

trajectory_path_length

Compute total or cumulative path length.

trajectory_separation

Compute time-resolved group separation across time using a selected method.

trajectory_tortuosity

Compute the ratio between path length and net displacement.

trajectory_turning_angle

Compute local turning angles between consecutive trajectory segments.

Author: Hamza Abdelhedi (hamza.abdelhedi@umontreal.ca)

Functions#

moving_average(arr, window)

Smooth a one-dimensional array with a valid-mode moving average.

trajectory_acceleration(traj[, dt])

Calculate instantaneous acceleration magnitude.

trajectory_jerk(traj[, dt])

Calculate instantaneous jerk magnitude (derivative of acceleration).

trajectory_speed(traj[, dt, time])

Calculate instantaneous trajectory speed.

trajectory_curvature(traj[, method])

Calculate geometric curvature of a trajectory.

trajectory_path_length(traj, *[, cumulative])

Calculate trajectory path length.

trajectory_displacement(traj, *[, final])

Calculate displacement from the initial state.

trajectory_tortuosity(traj[, eps])

Calculate trajectory tortuosity.

trajectory_turning_angle(traj)

Calculate local turning angles between consecutive trajectory segments.

trajectory_dispersion(traj[, labels])

Calculate within-group trajectory dispersion across time.

trajectory_separation(traj, labels[, method])

Calculate time-resolved separation between labeled trajectory groups.

trajectory_distance_from_center(traj)

Compute each point's Euclidean distance from the trajectory's own spatial centroid.

trajectory_cohesion(traj)

Mean distance from the trajectory's own spatial centroid.

trajectory_intra_spread(traj)

Standard deviation of distances from the trajectory's own spatial centroid.

trajectory_auc_speed(traj[, dt, time])

Area under the instantaneous speed curve (trapezoidal integration).

Module Contents#

coco_pipe.dim_reduction.evaluation.geometry.moving_average(arr, window)#

Smooth a one-dimensional array with a valid-mode moving average.

Parameters:
  • arr (np.ndarray of shape (n_samples,)) – Input array to smooth.

  • window (int) – Size of the smoothing window. Must be a positive integer no larger than the array length.

Returns:

Smoothed array. The output length is n_samples - window + 1. If window == 1, a copy of the input is returned.

Return type:

np.ndarray

Raises:

ValueError – If arr is not one-dimensional, if window is not positive, or if window is larger than the input length.

See also

trajectory_speed

First-order trajectory dynamics without smoothing.

trajectory_turning_angle

Local directional changes along a trajectory.

Examples

>>> import numpy as np
>>> moving_average(np.array([1, 2, 3, 4, 5]), window=3)
array([2., 3., 4.])
coco_pipe.dim_reduction.evaluation.geometry.trajectory_acceleration(traj, dt=1.0)#

Calculate instantaneous acceleration magnitude.

Parameters:
  • traj (np.ndarray of shape (..., n_times, n_dims)) – Trajectory array. The second-to-last axis is interpreted as time and the last axis as coordinates.

  • dt (float, default=1.0) – Uniform time step between consecutive samples.

Returns:

Acceleration-magnitude timecourse aligned with the input time axis.

Return type:

np.ndarray of shape (…, n_times)

Raises:

ValueError – If traj has fewer than two dimensions, contains fewer than three time points, or if dt <= 0.

See also

trajectory_speed

First-order trajectory dynamics.

trajectory_curvature

Geometric bending of a trajectory.

trajectory_turning_angle

Local directional changes between segments.

Examples

>>> import numpy as np
>>> t = np.linspace(0.0, 2.0, 3)
>>> traj = np.stack([t**2, np.zeros_like(t)], axis=1)
>>> trajectory_acceleration(traj, dt=1.0).shape
(3,)
coco_pipe.dim_reduction.evaluation.geometry.trajectory_jerk(traj, dt=1.0)#

Calculate instantaneous jerk magnitude (derivative of acceleration).

Jerk is the third derivative of position with respect to time, or equivalently the first derivative of the acceleration vector. A sudden spike in jerk means the trajectory “snapped” into a new movement, often correlating with discrete cognitive decisions or abrupt state transitions.

Parameters:
  • traj (np.ndarray of shape (..., n_times, n_dims)) – Trajectory array. The second-to-last axis is interpreted as time and the last axis as coordinates.

  • dt (float, default=1.0) – Uniform time step between consecutive samples.

Returns:

Jerk-magnitude timecourse aligned with the input time axis.

Return type:

np.ndarray of shape (…, n_times)

Raises:

ValueError – If traj has fewer than two dimensions, contains fewer than four time points, or if dt <= 0.

See also

trajectory_acceleration

Second-order trajectory dynamics.

trajectory_speed

First-order trajectory dynamics.

trajectory_curvature

Geometric bending of a trajectory.

Examples

>>> import numpy as np
>>> t = np.linspace(0.0, 3.0, 4)
>>> traj = np.stack([t**3, np.zeros_like(t)], axis=1)
>>> trajectory_jerk(traj, dt=1.0).shape
(4,)
coco_pipe.dim_reduction.evaluation.geometry.trajectory_speed(traj, dt=1.0, time=None)#

Calculate instantaneous trajectory speed.

Parameters:
  • traj (np.ndarray of shape (..., n_times, n_dims)) – Trajectory array. The second-to-last axis is interpreted as time and the last axis as coordinates.

  • dt (float, default=1.0) – Uniform time step between consecutive samples. Ignored when time is provided.

  • time (np.ndarray of shape (n_times,), optional) – Real timestamps (e.g. milliseconds) for non-uniform sampling. When provided, per-step dt values are derived from np.diff(time) and the output has shape (..., n_times - 1) — no padding is applied because the associated time axis is time[:-1].

Returns:

  • time=None: shape (..., n_times), last value padded.

  • time provided: shape (..., n_times - 1), no padding.

Return type:

np.ndarray

Raises:

ValueError – If traj has fewer than two dimensions, fewer than two time points, dt <= 0 (uniform mode), or time length mismatches traj.

Notes

Speed is the Euclidean norm of the first difference divided by the time step.

See also

trajectory_acceleration

Second-order trajectory dynamics.

trajectory_path_length

Total or cumulative traveled distance.

trajectory_displacement

Distance from the initial state across time.

Examples

>>> import numpy as np
>>> traj = np.array([[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]])
>>> trajectory_speed(traj)
array([1., 1., 1.])
>>> trajectory_speed(traj, time=np.array([0.0, 100.0, 250.0]))
array([0.01 , 0.00666667])
coco_pipe.dim_reduction.evaluation.geometry.trajectory_curvature(traj, method='cosine')#

Calculate geometric curvature of a trajectory.

Parameters:
  • traj (np.ndarray of shape (..., n_times, n_dims)) – Trajectory array. The second-to-last axis is interpreted as time and the last axis as coordinates.

  • method ({"cosine", "gradient"}, default="cosine") –

    Formula used to compute curvature.

    "cosine" — discrete turning-angle formula: the angle between consecutive step vectors divided by the step length. Output shape is (..., n_times - 2) because two differencing operations are required.

    "gradient" — continuous formula using first and second derivatives: sqrt(||v||^2 ||a||^2 - (v·a)^2) / ||v||^3. Assumes uniformly spaced samples. Output shape is (..., n_times).

Returns:

  • method="cosine": shape (..., n_times - 2)

  • method="gradient": shape (..., n_times)

Return type:

np.ndarray

Raises:

ValueError – If traj has fewer than two dimensions, insufficient time points, or an unsupported method is given.

See also

trajectory_turning_angle

Discrete local turning angles (no curvature scaling).

trajectory_tortuosity

Path inefficiency relative to net displacement.

trajectory_speed

First-order trajectory dynamics.

Examples

>>> import numpy as np
>>> t = np.linspace(0, 2 * np.pi, 100)
>>> traj = np.stack([np.cos(t), np.sin(t)], axis=1)
>>> trajectory_curvature(traj, method="gradient").shape
(100,)
>>> trajectory_curvature(traj, method="cosine").shape
(98,)
coco_pipe.dim_reduction.evaluation.geometry.trajectory_path_length(traj, *, cumulative=False)#

Calculate trajectory path length.

Parameters:
  • traj (np.ndarray of shape (..., n_times, n_dims)) – Trajectory array. The second-to-last axis is interpreted as time and the last axis as coordinates.

  • cumulative (bool, default=False) – If True, return cumulative path length aligned with the input time axis. Otherwise return total path length for each trajectory.

Returns:

Total path length with shape (...) when cumulative=False, or cumulative path length with shape (..., n_times) when cumulative=True.

Return type:

np.ndarray

Notes

NaN-valued steps are skipped in both the total and cumulative modes so that a single missing coordinate does not invalidate the whole result.

See also

trajectory_displacement

Distance from the initial state across time.

trajectory_tortuosity

Ratio of path length to net displacement.

trajectory_speed

First-order local motion magnitude.

Examples

>>> import numpy as np
>>> traj = np.array([[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]])
>>> trajectory_path_length(traj)
np.float64(2.0)
coco_pipe.dim_reduction.evaluation.geometry.trajectory_displacement(traj, *, final=False)#

Calculate displacement from the initial state.

Parameters:
  • traj (np.ndarray of shape (..., n_times, n_dims)) – Trajectory array. The second-to-last axis is interpreted as time and the last axis as coordinates.

  • final (bool, default=False) – If False (default), return the displacement timecourse from the first point at every time index — shape (..., n_times). If True, return only the scalar net displacement from the first to the last point — shape (...).

Returns:

  • final=False: shape (..., n_times)

  • final=True: shape (...)

Return type:

np.ndarray

See also

trajectory_path_length

Total or cumulative traveled distance.

trajectory_tortuosity

Ratio of traveled distance to final displacement.

Examples

>>> import numpy as np
>>> traj = np.array([[0.0, 0.0], [1.0, 0.0], [1.0, 1.0]])
>>> trajectory_displacement(traj)
array([0.        , 1.        , 1.41421356])
>>> trajectory_displacement(traj, final=True)
np.float64(1.4142135623730951)
coco_pipe.dim_reduction.evaluation.geometry.trajectory_tortuosity(traj, eps=1e-08)#

Calculate trajectory tortuosity.

Tortuosity is defined as total path length divided by net displacement from the initial to the final state.

Parameters:
  • traj (np.ndarray of shape (..., n_times, n_dims)) – Trajectory array. The second-to-last axis is interpreted as time and the last axis as coordinates.

  • eps (float, default=1e-8) – Small constant used to identify near-zero displacement.

Returns:

Tortuosity for each trajectory. Stationary trajectories return 1.0; trajectories with nonzero path length but near-zero net displacement return np.inf.

Return type:

np.ndarray of shape (…)

See also

trajectory_path_length

Total traveled distance along the path.

trajectory_displacement

Net displacement from start to end.

trajectory_curvature

Local geometric bending.

Examples

>>> import numpy as np
>>> traj = np.array([[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]])
>>> trajectory_tortuosity(traj)
np.float64(1.0)
coco_pipe.dim_reduction.evaluation.geometry.trajectory_turning_angle(traj)#

Calculate local turning angles between consecutive trajectory segments.

Parameters:

traj (np.ndarray of shape (..., n_times, n_dims)) – Trajectory array. The second-to-last axis is interpreted as time and the last axis as coordinates.

Returns:

Turning-angle timecourse in radians. The output is shorter than the input by two points because each angle requires one predecessor and one successor segment.

Return type:

np.ndarray of shape (…, n_times - 2)

See also

trajectory_curvature

Curvature computed from turning angles (cosine method).

trajectory_speed

Local motion magnitude.

trajectory_path_length

Total or cumulative traveled distance.

Examples

>>> import numpy as np
>>> traj = np.array([[0.0, 0.0], [1.0, 0.0], [1.0, 1.0]])
>>> trajectory_turning_angle(traj)
array([1.57079633])
coco_pipe.dim_reduction.evaluation.geometry.trajectory_dispersion(traj, labels=None)#

Calculate within-group trajectory dispersion across time.

Parameters:
  • traj (np.ndarray of shape (n_trials, n_times, n_dims)) – Trial trajectory tensor.

  • labels (np.ndarray of shape (n_trials,), optional) – Optional group label for each trial. If omitted, a single global dispersion timecourse is returned.

Returns:

Global dispersion timecourse when labels is omitted, otherwise a mapping from label to dispersion timecourse.

Return type:

np.ndarray or dict[str, np.ndarray]

See also

trajectory_separation

Unified separation entrypoint.

trajectory_separation

Use method="within_between_ratio" for normalized separation.

Examples

>>> import numpy as np
>>> traj = np.zeros((2, 3, 2))
>>> traj[1, :, 0] = 1.0
>>> trajectory_dispersion(traj)
array([0.5, 0.5, 0.5])
coco_pipe.dim_reduction.evaluation.geometry.trajectory_separation(traj, labels, method='centroid', **kwargs)#

Calculate time-resolved separation between labeled trajectory groups.

Parameters:
  • traj (np.ndarray of shape (n_trials, n_times, n_dims)) – Trajectory tensor containing one trajectory per trial.

  • labels (np.ndarray of shape (n_trials,)) – Class label for each trial.

  • method ({"centroid", "within_between_ratio", "mahalanobis",) – “distributional”, “margin”}, default=”centroid” Separation definition to compute.

  • **kwargs (dict) – Additional keyword arguments forwarded to the selected separation method.

Returns:

Mapping from label pairs to separation timecourses of shape (n_times,).

Return type:

dict[tuple[str, str], np.ndarray]

Raises:

ValueError – If the inputs are invalid or if an unsupported separation method is requested.

Notes

This is the high-level separation entrypoint for trajectory-group comparison. It dispatches to the more specific separation primitives in this module.

Supported methods:

  • "centroid": Euclidean distance between label centroids.

  • "within_between_ratio": Between-centroid distance normalized by within-group dispersion.

  • "mahalanobis": Covariance-aware centroid separation.

  • "distributional": Energy-distance separation between trial clouds.

  • "margin": Nearest-cross minus nearest-within margin separation.

See also

trajectory_dispersion

Within-group spread used by some separation methods.

Examples

>>> import numpy as np
>>> traj = np.zeros((4, 5, 2))
>>> labels = np.array(["A", "A", "B", "B"])
>>> sep = trajectory_separation(traj, labels, method="centroid")
>>> list(sep.keys())
[('A', 'B')]
coco_pipe.dim_reduction.evaluation.geometry.trajectory_distance_from_center(traj)#

Compute each point’s Euclidean distance from the trajectory’s own spatial centroid.

The centroid is the mean position across all time points of a single trajectory. This is distinct from trajectory_dispersion(), which measures spread across trials at each fixed time point.

Parameters:

traj (np.ndarray of shape (..., n_times, n_dims)) – Trajectory array. The second-to-last axis is interpreted as time and the last axis as coordinates.

Returns:

Per-point distance from the temporal centroid.

Return type:

np.ndarray of shape (…, n_times)

See also

trajectory_cohesion

Mean of this timecourse (compactness scalar).

trajectory_intra_spread

Std of this timecourse (variability scalar).

trajectory_dispersion

Across-trial spread at each time point.

Examples

>>> import numpy as np
>>> traj = np.array([[0.0, 0.0], [2.0, 0.0], [0.0, 0.0]])
>>> trajectory_distance_from_center(traj)
array([0.66666667, 1.33333333, 0.66666667])
coco_pipe.dim_reduction.evaluation.geometry.trajectory_cohesion(traj)#

Mean distance from the trajectory’s own spatial centroid.

A small value means the trajectory stays near its average position (compact loop or oscillation); a large value means it sweeps far from its center.

Parameters:

traj (np.ndarray of shape (..., n_times, n_dims)) – Trajectory array. The second-to-last axis is interpreted as time and the last axis as coordinates.

Returns:

Cohesion scalar for each trajectory in the batch.

Return type:

np.ndarray of shape (…)

See also

trajectory_distance_from_center

Full per-point timecourse.

trajectory_intra_spread

Complementary variability measure.

Examples

>>> import numpy as np
>>> traj = np.array([[0.0, 0.0], [2.0, 0.0], [0.0, 0.0]])
>>> trajectory_cohesion(traj)
np.float64(0.888...)
coco_pipe.dim_reduction.evaluation.geometry.trajectory_intra_spread(traj)#

Standard deviation of distances from the trajectory’s own spatial centroid.

Measures how variable the distance-from-center is across time: a trajectory that uniformly orbits its centroid has low intra-spread; one that starts close and ends far away has high intra-spread.

Not to be confused with trajectory_separation(), which compares the centroids of two separate trial groups.

Parameters:

traj (np.ndarray of shape (..., n_times, n_dims)) – Trajectory array. The second-to-last axis is interpreted as time and the last axis as coordinates.

Returns:

Intra-spread scalar for each trajectory in the batch.

Return type:

np.ndarray of shape (…)

See also

trajectory_distance_from_center

Full per-point timecourse.

trajectory_cohesion

Complementary mean measure.

trajectory_separation

Between-group separation (different concept).

Examples

>>> import numpy as np
>>> traj = np.array([[0.0, 0.0], [2.0, 0.0], [0.0, 0.0]])
>>> trajectory_intra_spread(traj)
np.float64(0.314...)
coco_pipe.dim_reduction.evaluation.geometry.trajectory_auc_speed(traj, dt=1.0, time=None)#

Area under the instantaneous speed curve (trapezoidal integration).

Integrates speed over time, giving a measure of total kinetic activity that weights fast periods more heavily than trajectory_path_length(). Units are [spatial_units · time_units] when time is provided, or [spatial_units · samples] with uniform dt=1.

Parameters:
  • traj (np.ndarray of shape (..., n_times, n_dims)) – Trajectory array. The second-to-last axis is interpreted as time and the last axis as coordinates.

  • dt (float, default=1.0) – Uniform time step. Ignored when time is provided.

  • time (np.ndarray of shape (n_times,), optional) – Real timestamps for non-uniform integration. When provided, per-step dt values are derived from np.diff(time) and the speed is integrated against time[:-1].

Returns:

AUC-speed scalar for each trajectory in the batch.

Return type:

np.ndarray of shape (…)

Raises:

ValueError – If traj has fewer than two time points, dt <= 0 (uniform mode), or time length mismatches traj.

See also

trajectory_speed

Per-step speed timecourse.

trajectory_path_length

Total path length (uniform speed weighting).

Examples

>>> import numpy as np
>>> traj = np.array([[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]])
>>> trajectory_auc_speed(traj, dt=1.0)
np.float64(1.0)
>>> trajectory_auc_speed(traj, time=np.array([0.0, 100.0, 200.0]))
np.float64(100.0)