Skip to contents

Six diagnostic panels that let you eyeball whether an mt_clean_track run was healthy. Analogous to plot.lm() for a linear model: each panel addresses a distinct failure mode and prints a brief interpretive note pointing at a remedy when something looks off.

Usage

mt_diagnose_clean_track(
  x,
  individual = NULL,
  window_days = 7,
  plot = TRUE,
  silent = FALSE
)

Arguments

x

A move2 object returned by mt_clean_track with remove = FALSE. Must carry the flag columns the orchestrator attaches (is_outlier, flagged_by_*, flag_iteration, error_class).

individual

For multi-track input, the track id to focus the first five panels on. Default NULL picks the individual with the highest flag rate (the one most worth diagnosing). Pass any track id to override.

window_days

Numeric. Width of the rolling window (in days) used by Panel 2. Default 7. Larger windows smooth more; smaller windows resolve finer time-localised flag clusters.

plot

Logical. If TRUE (default), render the six- panel figure. Set FALSE to skip plotting and just receive the diagnostic data.

silent

Logical. If FALSE (default), print the interpretive notes for each panel that tripped a concern. Set TRUE to suppress.

Value

Invisibly, a list with components by_individual (data.frame of per-track flag rates), run_lengths (integer vector of consecutive-flag run lengths on the focused track), modes (numeric vector of detected substantive modes in m/s), and notes (character vector of interpretive messages emitted). These let downstream code consume the diagnostic without re-running the function.

Details

Run mt_diagnose_clean_track() on the object returned by mt_clean_track(..., remove = FALSE). The function reads the flag columns and recomputes the small amount of additional information needed (per-fix step speeds; nothing requires re- running the per-fix detectors). On multi-individual input the first five panels focus on a single individual (default: the one with the highest flag rate); a sixth panel summarises the cohort.

The six panels

1. log-speed density with detected modes

KDE of \(\log(\text{step speed})\) with vertical lines for each substantive mode and the v_max cap that was used. A single dominant mode + sparse upper tail = healthy. Two or more substantive modes = bimodal behaviour (rest + flight); the per-fix detectors threshold against a single distribution and can over-flag the smaller mode. Remedy: see vignette('state_conditional') (in preparation), or run each behavioural state separately.

2. flag rate vs time

Rolling-window flag rate over the timeline. Roughly flat at <1\ noise events = healthy. A sustained elevated band over a contiguous window = the migration-over-flagging signature; the detectors are catching legitimate movement, not errors. Remedy: filter out that window or run it separately with stricter thresholds.

3. per-detector activity history

Bar chart of how many fixes each combination of detectors flagged during the iteration loop, BEFORE the conjunction rule was applied. "consensus" bins (>=2 detectors agree) are the high-confidence flags; "single-detector" bins are fixes one detector flagged that the conjunction subsequently rejected. A large bridge-only or prob-only bar means that detector is firing noisily on the data; a track with mostly consensus bars is converging cleanly.

4. cumulative flagging by iteration

Cumulative flag count across the per-fix iteration loop. Rapid plateau in 2-4 iterations = healthy. Linear growth without plateau = self- reinforcing flagging; the detectors are not converging on a stable flag set.

5. consecutive-flag run length distribution

Histogram of run lengths of consecutively flagged fixes. Most flags isolated (length 1) = discrete errors, the case the package is calibrated for. A heavy tail of long runs without error_class = "block" = a sustained behavioural state being mistaken for outliers.

6. per-individual flag rates (multi-track only)

Dot plot of flag rate per individual with reference lines at 0.5\ (typical clean), 2\ Use this to triage which individuals need further attention.

What the diagnostic does NOT do

It does not re-run the per-fix detectors and therefore cannot show the underlying bridge \(\eta\) or joint-probability distributions (a possible follow-up). It also assumes a single behavioural-state threshold was applied; tracks where the user has already segmented by state and run mt_clean_track per segment will read "healthy" on every panel because each segment IS unimodal.

See also

mt_clean_track for the orchestrator that produces the input.

Examples

if (FALSE) { # \dontrun{
library(move2)
x <- mt_read(system.file("extdata/synthetic_tracks.csv.gz",
                           package = "move2utils"))
x <- x[!sf::st_is_empty(x), ]
res <- mt_clean_track(x, plot = FALSE, remove = FALSE)
mt_diagnose_clean_track(res)
} # }