precise_dist() is the breadth engine of PreciseDist. It takes one numeric
data set, evaluates one or more registered relatedness measures, and returns a
typed tibble: each row contains the matrix, the canonical metric name, and the
matrix type. Every requested metric is accounted for in a run ledger
attached as the "precise_dist_run" attribute (see
precise_run_report): metrics that error, time out, are skipped
by a time budget, or are unavailable never vanish silently — they are absent
from the returned rows but present in the ledger with a status, reason, and
error message, and a consolidated warning summarizes them.
Usage
precise_dist(
data,
dists = NULL,
dist_funcs = NULL,
suffix = "",
file = NULL,
overwrite = FALSE,
parallel = FALSE,
plan_only = FALSE,
batch_size = NULL,
max_memory = NULL,
max_time_per_metric = Inf,
max_time_total = Inf,
seed = NULL,
verbose = TRUE,
coerce = "none",
local_timeout = NULL
)Arguments
- data
A numeric matrix, or a dataframe/tibble with at least one numeric column (non-numeric columns are dropped with a message).
- dists
NULL or a character vector of registered metric names, aliases, or family keywords. Use
precise_dist_list("all_dists") to inspect choices. Family keywords ending in "_dists" and their short aliases are accepted; for example,"static"is equivalent to"static_dists". Requested metrics whose backend package is not installed are recorded asunavailablein the run ledger (unknown names are still an error).- dist_funcs
NULL or a list of custom distance functions. Functions produced by
precise_func_factare typed by the factory; arbitrary user functions are returned withtype = NA. Unnamed elements are keyeduser_func_<i>.- suffix
A string prepended to every output distance key (both channels).
- file
NULL or the path for the RDS checkpoint/result envelope; read it back with
precise_read. See Details.- overwrite
TRUE or FALSE. Allow
fileto replace an existing path.- parallel
TRUE or FALSE. If TRUE, metric batches run via
future.apply::future_lapply()on the user'sfuture::plan(); requires the future.apply package.- plan_only
TRUE or FALSE. Return the plan ledger without computing. See Details.
- batch_size
NULL (auto) or a positive integer: metrics per master-side batch. See Details.
- max_memory
NULL or a positive number: your memory budget in GB for the estimated peak result footprint; the run stops before computing if the estimate exceeds it.
- max_time_per_metric
Seconds allowed per metric (best-effort; default
Inf).- max_time_total
Seconds allowed for the whole run, checked between batches (default
Inf).- seed
NULL or a whole number from 0 through
.Machine$integer.max. See Details (Reproducibility).- verbose
TRUE or FALSE. Narrate progress via
message()(suppressable withsuppressMessages()).- coerce
A string controlling optional coercion of the returned matrices.
"none"(the default) returns each measure's native output, tagged by thetypecolumn (tag-and-leave)."distance"delegates toprecise_transform(to = "distance") to coerce every row to a distance (requires an all-convertible set; seeprecise_transform). Any other value errors.- local_timeout
Deprecated alias for
max_time_per_metric; supplying both is an error.
Value
A tibble with columns: distance = the display name of the measure (the requested name, carrying any suffix), metric = the canonical
registry key the row resolves to (NA for custom dist_funcs), matrix = the nested matrix corresponding to the row,
type = the measure's output type ("distance", "similarity", "correlation" or "affinity"; NA for unknown custom dist_funcs) and
time_taken_seconds = The amount of time in seconds it took to calculate each matrix.
Rows appear in request order and contain only completed metrics;
the full per-metric accounting (completed / errored / skipped /
unavailable, with reasons and messages) is the tibble's
"precise_dist_run" attribute, accessible via
precise_run_report. If nothing completed, the
tibble has zero rows but the ledger is complete. With
coerce = "distance", the returned object also carries the
history list-column produced by precise_transform.
Details
Depending on dists or dist_funcs, the native output may be a distance,
similarity, correlation, affinity, or a custom row with unknown type. Factory
functions created by precise_func_fact carry their native type;
arbitrary user functions remain unknown unless the user handles them
explicitly. The function tags the native result and leaves conversion to
precise_transform. It does not infer type from matrix values.
Parallelism. parallel = TRUE dispatches each batch of metrics via
future.apply::future_lapply() on whatever future::plan() the
user has set; parallel = FALSE (the default) is a plain lapply() and
touches no future machinery. Worker count, backend, and exported-memory
limits are user decisions, made outside PreciseDist.
Reproducibility (seed). With seed = NULL (default), sequential
runs use the ordinary session RNG stream. Parallel runs use
future.seed = TRUE (statistically sound parallel streams, no
reproducibility promise across plans) and advance ordinary session RNG state
even when the requested metrics are deterministic. With a whole-number seed, every metric
draws from its own RNG stream derived from (seed, metric key) — results
are then identical across sequential and parallel runs and stable when other
metrics are added to or removed from the request; the session RNG state is
saved and restored around the call. Seeded results are reproducible for a
given R version and platform endianness. Stochastic metrics include the
random_forest_* family and the kernel metrics (rbf_*, laplace_*, whose
sigma estimation consumes randomness).
Input domains. Registry input classes with objective value-domain
rules (nonnegative, probability, unit_interval, and binary) are
validated before dispatch. An incompatible metric receives an
errored/input_domain ledger row while compatible metrics continue; input
data are never normalized or otherwise changed silently. numeric,
nominal, and time-series remain descriptive because they have no general
value-only validation rule.
Time limits. max_time_per_metric applies setTimeLimit()
around each metric; this is best-effort — it cannot interrupt long compiled
calls (randomForest, kernlab, philentropy) or blocking waits. A metric over
the limit is recorded as errored with reason "timeout".
The package clears its limit transiently, preserving still-live caller limits
after metric execution; interactions between nested limits while a metric is
running remain best-effort.
max_time_total is checked between batches; once exceeded, remaining
metrics are recorded as skipped with reason "time_budget" and the
completed rows are returned.
Memory (max_memory, in GB). Before dispatch the run computes a
resource estimate: each dense result is exactly 8 * nrow(data)^2
bytes. The estimate covers the input/result footprint only — it is not a
guarantee about worker-side transients or the environments carried by
user-supplied dist_funcs closures. If the estimated peak exceeds
max_memory, the run stops before computing anything (your own budget). If
no max_memory is set and the estimated result footprint exceeds
getOption("PreciseDist.large_run_gb", 8) GB, a warning is emitted and
the run proceeds — the package never refuses on its own.
Planning (plan_only). plan_only = TRUE performs validation,
metric resolution, and the resource estimate, then returns the run ledger
(statuses "planned"/"unavailable", plus errored/input_domain for
incompatible requested metrics) without computing anything or
touching the filesystem. Attributes: n_tasks, est_result_gb,
est_peak_gb, batch_size, n_batches, would_block_memory.
Checkpointing (file). file= writes a versioned RDS envelope,
rewritten atomically by the master process after every batch (workers never
touch it, so file= combines freely with parallel = TRUE). While running
the envelope holds state "partial" (completed native matrices + the
ledger); on normal return it is rewritten once as state "complete",
holding the exact returned object. Read either with
precise_read. An existing path errors unless
overwrite = TRUE. Each checkpoint rewrites the accumulated matrices;
increasing batch_size reduces write amplification at the cost of a larger
recovery window. On interrupt (Ctrl-C) the run checkpoints what it has,
then re-signals the interrupt — with no file=, interrupted work is lost.
If a parallel worker dies, the batch is recorded as worker_crash, the
envelope is checkpointed, and the run stops with an error carrying the
ledger (class "precise_dist_crash").
Progress. If the progressr package is installed, each metric
signals one progress step; enable display with
progressr::with_progress() (progressr is inactive in
non-interactive sessions unless you enable it).
Batching (batch_size). Metrics are dispatched in master-side
batches; batch boundaries are where checkpoints are written and the total
time budget is checked. NULL picks this automatically (worker count when
parallel with a file/time budget; 1 when sequential with a file; otherwise
one batch). Within a batch, scheduling across workers belongs to future.
Other details:
Use
precise_dist_listto inspect available registered measures.Use
pd_convertible_to_distanceandprecise_transformbefore downstream methods that require a single coherent type.suffixis useful when combining relatedness matrices from multiple feature sets that share the same observations. It is applied to bothdistsanddist_funcsdisplay keys.Duplicate metrics after alias/family expansion are collapsed with a message. A
dists/dist_funcsname collision (identical display key) is an error: rename the custom function or usesuffix.The run ledger attribute does not survive most data-frame operations (dplyr verbs, subsetting); the
file=envelope and the consolidated warning are the durable records.
References
Muchmore, B., Muchmore P. and Alarcón-Riquelme ME. (2018). Optimal Distance Matrix Construction with PreciseDist and PreciseGraph.
Examples
x <- matrix(
c(1, 0, 2, 0, 1, 2, 1, 1, 0, 2, 1, 1, 1, 2, 3, 3, 1, 2),
nrow = 6,
byrow = TRUE
)
rownames(x) <- paste0("item_", seq_len(nrow(x)))
result <- precise_dist(
x,
dists = c("euclidean", "manhattan"),
verbose = FALSE
)
result[c("distance", "metric", "type")]
#> # A tibble: 2 × 3
#> distance metric type
#> <chr> <chr> <chr>
#> 1 euclidean euclidean distance
#> 2 manhattan manhattan distance
precise_run_report(result)
#> # A tibble: 2 × 7
#> distance metric type status reason message time_taken_seconds
#> <chr> <chr> <chr> <chr> <chr> <chr> <dbl>
#> 1 euclidean euclidean distance completed NA NA 0.001000
#> 2 manhattan manhattan distance completed NA NA 0