Is your feature request related to a problem? Please describe.
Sinuosity is a measure of the tortuosity of a random search path, combining the distribution of turning angles with step length statistics. It quantifies how much a path deviates from a straight line during a random search, producing a single scalar where higher values indicate more tortuous movement. The corrected formulation (Benhamou 2004) handles variable step lengths, making it directly applicable to irregularly sampled tracking data without requiring rediscretisation.
It is one of the core trajectory complexity metrics identified in the trajr paper
Describe the solution you'd like
Definition
The corrected sinuosity index (Benhamou 2004, Eq. 8) is defined as:
$$S = 2 \left[ \bar{p} \left( \frac{1 + \bar{c}}{1 - \bar{c}} + b^2 \right) \right]^{-1/2}$$
where:
-
$\bar{p}$ = mean step length
-
$\bar{c} = \frac{1}{n}\sum_{i=1}^{n} \cos(\theta_i)$ = mean cosine of turning angles
-
$b = \mathrm{SD}(p_i) , / , \bar{p}$ = coefficient of variation of step length
Range: $[0, \infty)$, where higher values indicate more tortuous paths. A perfectly straight path has $S = 0$ (since $\bar{c} = 1$). A Brownian-like path has $S = 2 / \bar{p}$ (since $\bar{c} \to 0$).
Alternative names
This metric is also known as: tortuosity index (in some disciplines), corrected sinuosity, Benhamou sinuosity. The original (uncorrected) version by Bovet & Benhamou (1988) uses a simpler formula $S = 2 / [p(1 - \bar{c})]$ but requires constant step length and assumes a wrapped-normal turning angle distribution. The corrected version is strictly more general and should be preferred.
Pseudocode
function compute_sinuosity(data, nan_policy="ffill"):
# 1. Compute turning angles (depends on movement#833)
theta = compute_turning_angle(data)
# 2. Compute step lengths
displacements = compute_forward_displacement(data)
step_lengths = compute_norm(displacements)
# 3. Handle NaN values per nan_policy
# (follow compute_path_length convention)
# 4. Compute summary statistics
c_bar = nanmean(cos(theta)) # mean cosine of turning angles
p_bar = nanmean(step_lengths) # mean step length
b = nanstd(step_lengths) / p_bar # CV of step length
# 5. Compute sinuosity (Benhamou 2004, Eq. 8)
S = 2 * (p_bar * ((1 + c_bar) / (1 - c_bar) + b**2)) ** (-0.5)
return S
Edge cases and nan_policy
- When $\bar{c} = 1$ (perfectly straight): the denominator $(1 - \bar{c})$ is zero, and $S = 0$.
- When all steps are NaN: return NaN.
- When step length is zero (stationary): turning angle is NaN (from movement#833), which propagates correctly.
-
nan_policy should follow the convention established in compute_path_length. Niko noted on Zulip that "we may realise that the current policies don't cover what we need" — this metric may be a case where that becomes relevant.
Proposed API
def compute_sinuosity(
data: xr.DataArray,
nan_policy: Literal["ffill", "scale"] = "ffill",
nan_warn_threshold: float = 0.2,
) -> xr.DataArray:
Returns: A scalar per individual/keypoint (like compute_path_length), with time and space dimensions removed.
References
Context
Opened following discussion with Niko on Zulip. Related to #406
Is your feature request related to a problem? Please describe.
Sinuosity is a measure of the tortuosity of a random search path, combining the distribution of turning angles with step length statistics. It quantifies how much a path deviates from a straight line during a random search, producing a single scalar where higher values indicate more tortuous movement. The corrected formulation (Benhamou 2004) handles variable step lengths, making it directly applicable to irregularly sampled tracking data without requiring rediscretisation.
It is one of the core trajectory complexity metrics identified in the trajr paper
Describe the solution you'd like
Definition
The corrected sinuosity index (Benhamou 2004, Eq. 8) is defined as:
where:
Range:$[0, \infty)$ , where higher values indicate more tortuous paths. A perfectly straight path has $S = 0$ (since $\bar{c} = 1$ ). A Brownian-like path has $S = 2 / \bar{p}$ (since $\bar{c} \to 0$ ).
Alternative names
This metric is also known as: tortuosity index (in some disciplines), corrected sinuosity, Benhamou sinuosity. The original (uncorrected) version by Bovet & Benhamou (1988) uses a simpler formula$S = 2 / [p(1 - \bar{c})]$ but requires constant step length and assumes a wrapped-normal turning angle distribution. The corrected version is strictly more general and should be preferred.
Pseudocode
Edge cases and
nan_policynan_policyshould follow the convention established incompute_path_length. Niko noted on Zulip that "we may realise that the current policies don't cover what we need" — this metric may be a case where that becomes relevant.Proposed API
Returns: A scalar per individual/keypoint (like
compute_path_length), withtimeandspacedimensions removed.References
TrajSinuosity2— McLean & Skowron Volponi (2018). https://doi.org/10.1111/eth.12739Context
Opened following discussion with Niko on Zulip. Related to #406