Module libratools.lbt_metrics
The libratools.lbt_metrics module includes performance metrics, pairwise metrics and distance computations to summarize a dataset.
Expand source code
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
The libratools.lbt_metrics module includes performance metrics, pairwise metrics
and distance computations to summarize a dataset.
"""
import numpy as np # 3rd party packages
import pandas as pd
from . import lbt_utils # local imports
__author__ = "Vincent (Vince) J. Straub"
__email__ = "vincejstraub@gmail.com"
__status__ = "Testing"
def get_step_len(x, y):
"""
Returns step length (size), the displacement between two consecutive
coordinate points, calculates as stepwise euclidean distance using
the formula:
res = ∑i=1,...,n-1 sqrt[ (xi - xi-1)2 + (yi - yi-1)2 ]
Args:
x (list or series): list of values in first dimension.
y (list or series): list of values in second dimension.
"""
# convert arguments to arrays
x, y = list(x), list(y)
xy = np.array(list(zip(x, y)))
# find the differences
diff = np.diff(xy, axis=0)
# raise to the power of 2 and sum
ss = np.power(diff, 2).sum(axis=1)
# find the square root
res = np.sqrt(ss)
# prepend 0 to the first value
res = np.insert(res, 0, 0.0, axis=0)
return res
def get_relative_turn_angle(trajectory, heading_col='heading'):
"""
Computes step angles (in degrees) of each location relative to the
previous location. Angles may be between -180 and 180 degrees where
negative degrees indicate 'left' turns. Users should apply this
function to one trajectory at a time.
Args:
trajectory (pandas.DataFrame): a DataFrame which must have the columns
specified by x, y, and time.
heading_col (tr, default='heading'): DataFrame column containing
trajectory headings.
Returns:
angles (pandas.Series): a vector of turning angles in degrees. There are
two fewer angles than the number of rows; these are filled with NaN
values.
"""
if heading_col not in trajectory:
heading = get_heading(trajectory)
else:
heading = trajectory.heading
turn_angle = heading.diff().rename("turnAngle")
# correction for 360-degree angle range
turn_angle.loc[turn_angle >= 180] -= 360
turn_angle.loc[turn_angle < -180] += 360
return turn_angle
def get_heading(trajectory, angle_col='angle'):
"""
Compute trajectory heading.
trajectory (pandas.DataFrame): a DataFrame which must have the columns
specified by x, y, and time.
heading_col (tr, default='heading'): DataFrame column containing
trajectory headings.
Returns:
angles (pandas.Series): a vector of turning angles in degrees. There are
two fewer angles than the number of rows; these are filled with NaN
values.
"""
# check step length exists as column
if angle_col not in trajectory.columns:
angle = get_absoloute_turn_angle(trajectory)
else:
angle = trajectory.angle
dx = trajectory.x.diff()
dy = trajectory.y.diff()
# get heading from angle
mask = (dx > 0) & (dy >= 0)
trajectory.loc[mask, "heading"] = angle[mask]
mask = (dx >= 0) & (dy < 0)
trajectory.loc[mask, "heading"] = -angle[mask]
mask = (dx < 0) & (dy <= 0)
trajectory.loc[mask, "heading"] = -(180 - angle[mask])
mask = (dx <= 0) & (dy > 0)
trajectory.loc[mask, "heading"] = 180 - angle[mask]
return trajectory.heading
def get_absoloute_turn_angle(trajectory, step_col='stepLength', unit="degrees", lag=1):
"""
Returns angle between steps as a function of displacement with regard to x axis.
Args:
trajectory (pandas.DataFrame): movement track.
step_col (str, default=stepLength): DataFrame column containg step
lengths.
unit (str, default='degrees'): unit to compute angles in, set to
degrees by default.
lag (int): angles between every lag'th element are calculated.
Returns:
angles (pandas.Series): a vector of turning angles in degrees. There are
two fewer angles than the number of rows; these are filled with NaN
values.
"""
# check step length exists as column
if step_col not in trajectory.columns:
trajectory[step_col] = lbt_metrics.get_step_len(
trajectory['x'], trajectory['y'])
if unit == "degrees":
angle = np.rad2deg(np.arccos(np.abs(trajectory.x.diff(lag)) / trajectory[step_col]))
elif unit == "radians":
angle = np.arccos(np.abs(trajectory.x.diff()) / trajectory[step_col])
else:
raise ValueError(f"The unit {unit} is not valid, expected radians or degrees.")
angle.unit = unit
angle.name = "angle"
return angle
Functions
def get_absoloute_turn_angle(trajectory, step_col='stepLength', unit='degrees', lag=1)
-
Returns angle between steps as a function of displacement with regard to x axis.
Args
trajectory
:pandas.DataFrame
- movement track.
step_col
:str
, default=stepLength
- DataFrame column containg step lengths.
- unit (str, default='degrees'): unit to compute angles in, set to
- degrees by default.
lag
:int
- angles between every lag'th element are calculated.
Returns
angles (pandas.Series): a vector of turning angles in degrees. There are two fewer angles than the number of rows; these are filled with NaN values.
Expand source code
def get_absoloute_turn_angle(trajectory, step_col='stepLength', unit="degrees", lag=1): """ Returns angle between steps as a function of displacement with regard to x axis. Args: trajectory (pandas.DataFrame): movement track. step_col (str, default=stepLength): DataFrame column containg step lengths. unit (str, default='degrees'): unit to compute angles in, set to degrees by default. lag (int): angles between every lag'th element are calculated. Returns: angles (pandas.Series): a vector of turning angles in degrees. There are two fewer angles than the number of rows; these are filled with NaN values. """ # check step length exists as column if step_col not in trajectory.columns: trajectory[step_col] = lbt_metrics.get_step_len( trajectory['x'], trajectory['y']) if unit == "degrees": angle = np.rad2deg(np.arccos(np.abs(trajectory.x.diff(lag)) / trajectory[step_col])) elif unit == "radians": angle = np.arccos(np.abs(trajectory.x.diff()) / trajectory[step_col]) else: raise ValueError(f"The unit {unit} is not valid, expected radians or degrees.") angle.unit = unit angle.name = "angle" return angle
def get_heading(trajectory, angle_col='angle')
-
Compute trajectory heading. trajectory (pandas.DataFrame): a DataFrame which must have the columns specified by x, y, and time. heading_col (tr, default='heading'): DataFrame column containing trajectory headings.
Returns
angles (pandas.Series): a vector of turning angles in degrees. There are two fewer angles than the number of rows; these are filled with NaN values.
Expand source code
def get_heading(trajectory, angle_col='angle'): """ Compute trajectory heading. trajectory (pandas.DataFrame): a DataFrame which must have the columns specified by x, y, and time. heading_col (tr, default='heading'): DataFrame column containing trajectory headings. Returns: angles (pandas.Series): a vector of turning angles in degrees. There are two fewer angles than the number of rows; these are filled with NaN values. """ # check step length exists as column if angle_col not in trajectory.columns: angle = get_absoloute_turn_angle(trajectory) else: angle = trajectory.angle dx = trajectory.x.diff() dy = trajectory.y.diff() # get heading from angle mask = (dx > 0) & (dy >= 0) trajectory.loc[mask, "heading"] = angle[mask] mask = (dx >= 0) & (dy < 0) trajectory.loc[mask, "heading"] = -angle[mask] mask = (dx < 0) & (dy <= 0) trajectory.loc[mask, "heading"] = -(180 - angle[mask]) mask = (dx <= 0) & (dy > 0) trajectory.loc[mask, "heading"] = 180 - angle[mask] return trajectory.heading
def get_relative_turn_angle(trajectory, heading_col='heading')
-
Computes step angles (in degrees) of each location relative to the previous location. Angles may be between -180 and 180 degrees where negative degrees indicate 'left' turns. Users should apply this function to one trajectory at a time.
Args
trajectory
:pandas.DataFrame
- a DataFrame which must have the columns specified by x, y, and time.
heading_col (tr, default='heading'): DataFrame column containing trajectory headings.
Returns
angles (pandas.Series): a vector of turning angles in degrees. There are two fewer angles than the number of rows; these are filled with NaN values.
Expand source code
def get_relative_turn_angle(trajectory, heading_col='heading'): """ Computes step angles (in degrees) of each location relative to the previous location. Angles may be between -180 and 180 degrees where negative degrees indicate 'left' turns. Users should apply this function to one trajectory at a time. Args: trajectory (pandas.DataFrame): a DataFrame which must have the columns specified by x, y, and time. heading_col (tr, default='heading'): DataFrame column containing trajectory headings. Returns: angles (pandas.Series): a vector of turning angles in degrees. There are two fewer angles than the number of rows; these are filled with NaN values. """ if heading_col not in trajectory: heading = get_heading(trajectory) else: heading = trajectory.heading turn_angle = heading.diff().rename("turnAngle") # correction for 360-degree angle range turn_angle.loc[turn_angle >= 180] -= 360 turn_angle.loc[turn_angle < -180] += 360 return turn_angle
def get_step_len(x, y)
-
Returns step length (size), the displacement between two consecutive coordinate points, calculates as stepwise euclidean distance using the formula:
res = ∑i=1,…,n-1 sqrt[ (xi - xi-1)2 + (yi - yi-1)2 ]
Args
x
:list
orseries
- list of values in first dimension.
y
:list
orseries
- list of values in second dimension.
Expand source code
def get_step_len(x, y): """ Returns step length (size), the displacement between two consecutive coordinate points, calculates as stepwise euclidean distance using the formula: res = ∑i=1,...,n-1 sqrt[ (xi - xi-1)2 + (yi - yi-1)2 ] Args: x (list or series): list of values in first dimension. y (list or series): list of values in second dimension. """ # convert arguments to arrays x, y = list(x), list(y) xy = np.array(list(zip(x, y))) # find the differences diff = np.diff(xy, axis=0) # raise to the power of 2 and sum ss = np.power(diff, 2).sum(axis=1) # find the square root res = np.sqrt(ss) # prepend 0 to the first value res = np.insert(res, 0, 0.0, axis=0) return res