
Derive heading angle(s) from trajectories using specified rule
Source:R/headings.R
derive_headings.RdDerive heading angle(s) from trajectories using specified rule
Arguments
- x
Tracks
- rule
Required. A single rule name, or a function. Built-in rules: `"crossing"`, `"distal"`, `"straight"`, `"origin_mean"`, `"net"`, `"velocity_mean"`, `"velocity_axis"`, `"window_net"`, `"goal_bias"`, `"pca_axis"`, `"ransac_straight"`, `"maxspeed_window"`, `"vm_fit"`, `"exit"`, `"entry"`, `"ring_tangent"`; a name registered with [register_heading_rule()] (see [list_heading_rules()]); or a function called once per trajectory as `f(df, cols, ...)` that returns columns `id`, `time` and `heading`.
- ...
rule-specific options (see Details), including `return_coords`. A built-in rule stops with an error naming any option it does not read; registered and function rules receive `...` unchecked.
- coords
Character. Which Cartesian columns to use: `"absolute"` (default, uses whatever `x`/`y` the `Tracks` currently holds in `Tracks@cols` – calibrated only if the caller supplied or normalized them that way) or `"relative"` (uses `rel_x`/`rel_y`, the landmark-rotated frame; errors if not registered).
- first_only
logical; if TRUE, return only the first matching heading per trajectory
- carry
optional character vector of columns from the source data to append via nearest time
- on_missing
One of `"warn"` (default), `"error"`, or `"quiet"`, controlling what happens when a rule produces no heading (`NA`) for one or more trials. The `NA` rows are always retained; the returned object carries `n_total` and `n_missing` (headings, i.e. rows), `n_trials` (distinct trials) and `missing_ids` (the trials with at least one missing heading) attributes. `"warn"` emits a warning, `"error"` stops, `"quiet"` is silent. Rule-based failures are often non-random (e.g. tracks that never reach the circumference) and can bias circular statistics, so they are surfaced by default.
Value
data.frame with columns id, time (approx), heading (radians, unit-circle convention), plus the rule-specific construction columns above when `return_coords = TRUE`. For some rules there may be multiple headings per id.
Details
Passing `return_coords = TRUE` (via `...`, default `FALSE`) attaches the construction coordinates each rule used to derive the heading, in the chosen `coords` frame: `crossing` adds `x_inner`/`y_inner`; `distal` adds `x_distal`/`y_distal`; `net` adds `x_start`/`y_start`/`x_end`/`y_end`; `straight` adds `x_seg0`/`y_seg0`/`x_seg1`/`y_seg1` (the run endpoints); `pca_axis` adds `x_centroid`/`y_centroid`/`axis_x`/`axis_y` (a unit axis vector). Other rules ignore it. The `distal` rule accepts `max_radius` (default `Inf`): the furthest-point search is restricted to positions with radius `<= max_radius`, so `max_radius = 1` ignores beyond-circumference (`rho > 1`) tracking outliers. The `straight` rule takes `tol` (default `pi/18`, the largest turn between successive steps) and `min_len` (default `5`, the fewest steps in a run). A step of zero length (a pause) has no direction: turns are measured between successive moving steps, so a pause neither breaks a run nor counts toward `min_len`. The heading is the direction of the run's first moving step. `pca_axis` takes `source = "position"` (default; the principal axis of the positions about their centroid) or `"velocity"` (the principal axis of the steps about zero, which weights a step by its squared length; compare `velocity_axis`, which weights by length).
The rules that average directions take a weighting through ...:
velocity_meanandvm_fitaverage the directions of the steps of a track.weight_by = "uniform"(default) counts every step once;"step_length"weights a step by its length, which gives the direction from the first position to the last, asnetdoes. The two rules return the same heading: the maximum-likelihood mean direction of a von Mises distribution is the mean direction.velocity_axistakes the sameweight_by, with"step_length"as its default.origin_meanaverages the bearings of the positions from the origin, weighted by radius to the powerr_power(default0, every position counted once;1gives the bearing of the centroid).
A step of zero length and a position at the origin have no direction and
are left out of these averages, whatever the weighting. A track with no
direction left, or whose directions cancel, has heading NA.
The other rules likewise give NA where a displacement has zero
length: a track that never moves (net, straight,
goal_bias, maxspeed_window, window_net), one that ends
where it started (net), a window whose ends coincide
(window_net), positions with no spread, or steps none of which
moves (pca_axis), a pca_axis track whose net displacement has
no component along its axis (the axis then has no sign), and a track that
stays at the origin (distal). window_net and
maxspeed_window shrink their window to fit a track shorter than
window_n.
Examples
h <- derive_headings(cpunctatus, rule = "distal")
head(h)
#> # A tibble: 6 × 3
#> id time heading
#> <chr> <int> <dbl>
#> 1 10_10_1 299 4.77
#> 2 10_11_1 78 1.41
#> 3 10_12_1 49 2.25
#> 4 10_13_1 101 4.73
#> 5 10_14_1 30 1.94
#> 6 10_15_1 55 5.03
# ignore tracking outliers beyond the circumference and keep the point used
head(derive_headings(cpunctatus, rule = "distal", max_radius = 1,
return_coords = TRUE))
#> # A tibble: 6 × 5
#> id time heading x_distal y_distal
#> <chr> <int> <dbl> <dbl> <dbl>
#> 1 10_10_1 299 4.77 0.0545 -0.880
#> 2 10_11_1 77 1.41 0.155 0.974
#> 3 10_12_1 45 2.25 -0.612 0.755
#> 4 10_13_1 99 4.73 0.0150 -0.997
#> 5 10_14_1 30 1.94 -0.356 0.907
#> 6 10_15_1 51 4.96 0.238 -0.961
# per-trial reference frame, carrying a condition column for grouping
head(derive_headings(cpunctatus, rule = "net", coords = "relative",
carry = "arc"))
#> # A tibble: 6 × 4
#> id time heading arc
#> <chr> <dbl> <dbl> <ord>
#> 1 10_10_1 150 1.54 10
#> 2 10_11_1 46 6.15 10
#> 3 10_12_1 25.5 2.18 10
#> 4 10_13_1 61.5 6.28 10
#> 5 10_14_1 15.5 5.24 10
#> 6 10_15_1 28.5 3.41 10