Flag fixes by a speed cap (data-driven or physiological)
Source:R/mt_flag_speed_cap.R
mt_flag_speed_cap.RdFlags fixes that straddle an implausibly fast step. The threshold can be derived data-driven from the track's own step-speed distribution (entropy valley or dip-test-validated broken-stick break) or supplied as a hard physiological cap in m/s. This is the only primitive that scores steps as first-class objects, and therefore the only one that catches the boundary transitions of a coherent outlier block that per-fix detectors structurally cannot see.
Usage
mt_flag_speed_cap(
x,
v_max = NULL,
threshold_type = c("auto", "entropy", "gap", "hard"),
threshold = NULL,
jitter = NULL,
physiological_ceiling = NULL,
pool_by = NULL,
plot = TRUE,
remove = FALSE,
silent = FALSE
)Arguments
- x
A
move2object. Longitude/latitude or projected; step lengths are computed via the package's geodesic / Euclidean helper which handles both CRS types directly.- v_max
Numeric scalar in m/s or
NULL(default). Required whenthreshold_type = "hard"; ignored otherwise. Whenthreshold_type != "hard"the cap is computed from the data.- threshold_type
Character, one of
"auto"(default),"entropy","gap", or"hard". See details.- threshold
Numeric tuning parameter passed to the underlying threshold helper (entropy valley-depth ratio or gap multiplier).
NULL(default) uses that helper's own default. Ignored whenthreshold_type = "hard".- jitter
Numeric scalar or
NULL(default). Optional lower bound on absolute step length (metres).- physiological_ceiling
Numeric scalar in m/s, or
NULL(default). Soft upper-bound used by the auto-cap path's biological-sanity warning: when the data-driven cap exceeds this value, a message is emitted suggesting the user supply a hardv_maxor the(mass, mode)allometric prior tomt_clean_track().NULLfalls back to a universal mode-agnostic ceiling of 55 m/s. This is a deliberately loose impossibility bound on speed sustained over the interval separating two fixes: nothing living sustains 55 m/s over such an interval, so a step implying one is positional error or a tag no longer on the animal rather than movement. It is neither a species-specific prediction nor a confidence bound – for a per-species cap usev_phys_estimate(mass, mode). When the user has(mass, mode)information, passingphysiological_ceiling = v_phys_estimate(mass, mode) * 1.25gives a sharper per-species check (sprint margin on the central allometric prediction). The check is warning-only; it never alters the cap. Ignored whenthreshold_type = "hard"(the user's cap stands).- pool_by
Optional character vector of length 1 or 2 naming column(s) in
mt_track_data(x). Length 1: single column used as both fit set and operating unit. Length 2:c(outer, inner)whereouternames the fit-source column (the union of its events supplies the step-speed distribution that.compute_speed_cap– with its fraction-above and mode-position gates – runs over) andinnernames the operating unit (within which the pool-fitted cap is applied and flags are unioned). Length 2 requires strict nesting: every distinctinnervalue must map to exactly oneoutervalue. Length \(> 2\) is rejected. Pool flags union intois_outlier+is_speed_above_cap– additive, never un-flags what per-track caught. Per-track pool caps are exposed viaattr(out, "v_max_used_pool"), alongside the per-trackattr(out, "v_max_used").NULL(default) preserves per-track behaviour byte-identically. Ignored whenthreshold_type = "hard"(the user-supplied cap is already uniform across tracks).- plot
Logical. If
TRUE(default), produce a diagnostic map with flagged fixes highlighted.- remove
Logical. If
TRUE, drop flagged rows from the returned object. DefaultFALSE.- silent
Logical. If
FALSE(default) the function prints a one-line summary of the cap chosen, gate decisions, and the count of flagged fixes. SetTRUEto suppress. Errors and warnings are always shown.
Value
The input object with added columns:
step_speedImplied outgoing step speed in m/s.
NAfor the last fix of each track.is_speed_above_capLogical.
TRUEwhere either adjacent step exceeds the cap.is_step_below_jitterLogical. Present only when
jitteris supplied.is_outlierLogical. Union of the above flags.
flagged_by_speedLogical. Same as
is_outlierfor this primitive; named for parity with the other primitives' output schema (so the output can be voted bymt_flag_consensus).loglr_speedSigned log-likelihood-ratio of outlier vs not for the above-cap signal, in nat units (
log(step_speed / cap));> 0above the cap. Two-sided in the data-driven modes; one-sided (negative clamped to 0) underthreshold_type = "hard", where a slow step is no evidence. SeeDESIGN_evidence_accumulation.md.
The value of the cap actually used is stored as the attribute
"v_max_used".
Details
For each fix \(i\), let \(v_i = d_i / \Delta t_i\) be the
implied step speed, where \(d_i\) is the distance to fix
\(i+1\) (via mt_distance, geodesic for
lon/lat data, Euclidean for projected data) and \(\Delta t_i\)
the corresponding time lag. A fix is flagged
is_speed_above_cap = TRUE if either its outgoing step
\(v_i\) or its incoming step \(v_{i-1}\) exceeds the cap —
without more information the detector cannot tell which of the
two fixes the offending geometry belongs to, so both endpoints
are flagged.
The cap itself is chosen by threshold_type:
"auto"(default): entropy-valley on \(-\log v\); if none is found, fall back to the broken-stick / gap break, retained only if Hartigan's dip test confirms the distribution is significantly multimodal (\(p < 0.05\)). On clean unimodal speed distributions this returns no flags — the honest "no outliers" outcome. The most principled choice, and the default."entropy": entropy valley only. Most conservative."gap": broken-stick plus tail-decay inflection only. More sensitive but can over-flag on legitimate heavy tails."hard": usesv_maxas a literal physiological cap. Requiresv_maxto be supplied. Pick species' documented biomechanical top speed plus ~25\
When jitter is supplied, an analogous lower bound
is applied to step length (not speed): is_step_below_jitter
= TRUE where either neighbouring step is below jitter
metres. Use this only if you explicitly want to mark sub-noise
floor displacements.
On multi-track input the function dispatches per individual: each
track's data-driven cap is tuned to that track's own speed
distribution, and attr(out, "v_max_used") is returned as a
named numeric (one entry per track id). Single-track input retains
the scalar attribute. Empty geometries and non-finite (NA)
coordinates are a hard error (remove them upstream); rows with missing
timestamps yield NA step speeds and are excluded from flagging but
retained in the output, preserving row parity with the caller.
See also
mt_suggest_speed_cap for a diagnostic-only
helper that returns the suggested value without modifying
x; mt_flag_outliers_bridge and
mt_flag_outliers for per-fix detectors.
Examples
if (FALSE) { # \dontrun{
library(move2)
## data-driven (default); silent on unimodal speed distributions
x <- mt_flag_speed_cap(x)
## hard physiological cap: golden eagle documented top ~50 m/s
x <- mt_flag_speed_cap(x, v_max = 50, threshold_type = "hard")
## diagnostic: inspect distribution + suggested cap without flagging
v <- mt_suggest_speed_cap(x)
} # }