Source code for movement.kinematics.kinematics

"""Compute core kinematic variables such as time derivatives of position.

This module provides functions for computing fundamental kinematic properties
such as forward & backward displacement, velocity, acceleration, and speed.
The ``movement.kinematics`` subpackage encompasses a broader range of
functionality (e.g., orientations and distances ), but this file is
intended to isolate 'true' kinematics for clarity. In a future release, the
public API may be revised to reflect this distinction more explicitly.

"""

import xarray as xr

from movement.utils.logging import logger
from movement.utils.vector import compute_norm
from movement.validators.arrays import validate_dims_coords


[docs] def compute_time_derivative(data: xr.DataArray, order: int) -> xr.DataArray: """Compute the time-derivative of an array using numerical differentiation. This function uses :meth:`xarray.DataArray.differentiate`, which differentiates the array with the second-order accurate central differences method. Parameters ---------- data The input data containing ``time`` as a required dimension. order The order of the time-derivative. For an input containing position data, use 1 to compute velocity, and 2 to compute acceleration. Value must be a positive integer. Returns ------- xarray.DataArray An xarray DataArray containing the time-derivative of the input data. See Also -------- :meth:`xarray.DataArray.differentiate` : The underlying method used. """ if not isinstance(order, int): raise logger.error( TypeError(f"Order must be an integer, but got {type(order)}.") ) if order <= 0: raise logger.error(ValueError("Order must be a positive integer.")) validate_dims_coords(data, {"time": []}) result = data for _ in range(order): result = result.differentiate("time") return result
def _compute_forward_displacement(data: xr.DataArray) -> xr.DataArray: """Compute forward displacement vectors in Cartesian coordinates. The displacement vectors have origin at the position at time t, pointing to the position at time t+1. The last vector is of magnitude=0. """ validate_dims_coords(data, {"time": [], "space": []}) result = data.diff(dim="time", label="lower") result = result.reindex_like(data, fill_value=0) return result
[docs] def compute_forward_displacement(data: xr.DataArray) -> xr.DataArray: """Compute forward displacement array in Cartesian coordinates. The forward displacement array is defined as the difference between the position array at time point ``t+1`` and the position array at time point ``t``. As a result, for a given individual and keypoint, the forward displacement vector at time point ``t``, is the vector pointing from the current ``t`` position to the next ``t+1``, in Cartesian coordinates. Parameters ---------- data The input data containing position information, with ``time`` and ``space`` (in Cartesian coordinates) as required dimensions. Returns ------- xarray.DataArray An xarray DataArray containing forward displacement vectors in Cartesian coordinates. Notes ----- For the ``position`` array of a ``poses`` dataset, the ``forward_displacement`` array will hold the forward displacement vectors for every keypoint and every individual. For the ``position`` array of a ``bboxes`` dataset, the ``forward_displacement`` array will hold the forward displacement vectors for the centroid of every individual bounding box. For the ``shape`` array of a ``bboxes`` dataset, the ``forward_displacement`` array will hold vectors with the change in width and height per bounding box, between consecutive time points. """ result = _compute_forward_displacement(data) result.name = "forward_displacement" return result
[docs] def compute_backward_displacement(data: xr.DataArray) -> xr.DataArray: """Compute backward displacement array in Cartesian coordinates. The backward displacement array is defined as the difference between the position array at time point ``t-1`` and the position array at time point ``t``. As a result, for a given individual and keypoint, the backward displacement vector at time point ``t``, is the vector pointing from the current ``t`` position to the previous ``t-1`` in Cartesian coordinates. Parameters ---------- data The input data containing position information, with ``time`` and ``space`` (in Cartesian coordinates) as required dimensions. Returns ------- xarray.DataArray An xarray DataArray containing backward displacement vectors in Cartesian coordinates. Notes ----- For the ``position`` array of a ``poses`` dataset, the ``backward_displacement`` array will hold the backward displacement vectors for every keypoint and every individual. For the ``position`` array of a ``bboxes`` dataset, the ``backward_displacement`` array will hold the backward displacement vectors for the centroid of every individual bounding box. For the ``shape`` array of a ``bboxes`` dataset, the ``backward_displacement`` array will hold vectors with the change in width and height per bounding box, between consecutive time points. """ fwd_displacement = _compute_forward_displacement(data) backward_displacement = -fwd_displacement.roll(time=1) backward_displacement.name = "backward_displacement" return backward_displacement
[docs] def compute_velocity(data: xr.DataArray) -> xr.DataArray: """Compute velocity array in Cartesian coordinates. The velocity array is the first time-derivative of the position array. It is computed by applying the second-order accurate central differences method on the position array. Parameters ---------- data The input data containing position information, with ``time`` and ``space`` (in Cartesian coordinates) as required dimensions. Returns ------- xarray.DataArray An xarray DataArray containing velocity vectors in Cartesian coordinates. Notes ----- For the ``position`` array of a ``poses`` dataset, the ``velocity`` array will hold the velocity vectors for every keypoint and every individual. For the ``position`` array of a ``bboxes`` dataset, the ``velocity`` array will hold the velocity vectors for the centroid of every individual bounding box. See Also -------- compute_time_derivative : The underlying function used. """ # validate only presence of Cartesian space dimension # (presence of time dimension will be checked in compute_time_derivative) validate_dims_coords(data, {"space": []}) result = compute_time_derivative(data, order=1) result.name = "velocity" return result
[docs] def compute_acceleration(data: xr.DataArray) -> xr.DataArray: """Compute acceleration array in Cartesian coordinates. The acceleration array is the second time-derivative of the position array. It is computed by applying the second-order accurate central differences method on the velocity array. Parameters ---------- data The input data containing position information, with ``time`` and ``space`` (in Cartesian coordinates) as required dimensions. Returns ------- xarray.DataArray An xarray DataArray containing acceleration vectors in Cartesian coordinates. Notes ----- For the ``position`` array of a ``poses`` dataset, the ``acceleration`` array will hold the acceleration vectors for every keypoint and every individual. For the ``position`` array of a ``bboxes`` dataset, the ``acceleration`` array will hold the acceleration vectors for the centroid of every individual bounding box. See Also -------- compute_time_derivative : The underlying function used. """ # validate only presence of Cartesian space dimension # (presence of time dimension will be checked in compute_time_derivative) validate_dims_coords(data, {"space": []}) result = compute_time_derivative(data, order=2) result.name = "acceleration" return result
[docs] def compute_speed(data: xr.DataArray) -> xr.DataArray: """Compute instantaneous speed at each time point. Speed is a scalar quantity computed as the Euclidean norm (magnitude) of the velocity vector at each time point. Parameters ---------- data The input data containing position information, with ``time`` and ``space`` (in Cartesian coordinates) as required dimensions. Returns ------- xarray.DataArray An xarray DataArray containing the computed speed, with dimensions matching those of the input data, except ``space`` is removed. """ result = compute_norm(compute_velocity(data)) result.name = "speed" return result