tf_rgp() no longer depends on mvtnorm or pracma. GP draws are now
generated by a direct eigen-based sampler (the symmetric square root
V D^{1/2} V^T of the covariance) that reproduces the previous
mvtnorm::rmvnorm(method = "eigen") draws bit-for-bit under set.seed() and
handles rank-deficient kernels (e.g. squared-exp with zero nugget) correctly.
tf_smooth.tfd(method = "savgol") now inlines a small Savitzky–Golay filter
in place of the pracma::savgol() dependency.tf_evaluate() no longer returns values at the wrong positions when the
requested arg contains duplicates (#236).tf_integrate(f, definite = FALSE) for irregular tfd with n > 1 no longer
crashes; the antiderivative's per-curve grids are kept as a list (#237).Math.tfd() / Math.tfb() now forward ... to the underlying op so
round(x, digits), log(x, base), signif(x, digits), etc. honor their
extra arguments instead of silently dropping them (#246).tf_integrate() on irregular tfd no longer silently returns NA under
default limits; for irregular input the defaults are now each curve's own
observed arg range (i.e., the range of its tf_arg() values). Pass explicit
lower / upper (or an extrapolating evaluator) to override (#253).This release introduces first-class support for vector-valued (multivariate)
functional data -- functions whose codomain is R^d -- alongside the existing
univariate tfd/tfb classes.
tfd_mv() and tfb_mv(): new vctrs-based S3 classes for vector-valued
functional data, holding several component functions per observation on a
shared domain. Constructors accept named lists of tfd/tfb vectors or
list-columns of matrices.tfb_mfpc() implements multivariate functional principal component analysis
(Happ & Greven, 2018) for tf_mv data: a single set of scalar scores per
curve shared across all components, with vector-valued eigenfunctions.
Component weighting is configurable ("inverse_variance" default, "snr",
"equal", or user-supplied). New data can be projected onto a fitted basis
via tf_rebase() / vec_cast(). Accessors tf_mfpc_scores(),
tf_mfpc_efunctions() and the predicate is_tfb_mfpc().tf_register() gains method = "srvf_mv" for
jointly aligning the components of tf_mv curves via the multivariate SRVF
framework, and tf_register_shape() provides elastic shape registration
(rotation/translation/scale-invariant) via fdasrvf.tf_mv (and where meaningful for univariate tf):
tf_norm(), tf_inner(), tf_tangent(), tf_arclength().tf_ncomp(), tf_components(), tf_component()),
tf_split() / tf_combine() extensions and [/[[ methods for extracting,
replacing and recombining components.tf_fmean(), tf_fvar() and tf_fsd()
return component-wise matrices for tf_mv objects.tf_interpolate(), tf_fwise(), tf_sparsify() and tf_jiggle() now have
component-wise tf_mv methods; tf_sparsify() and tf_jiggle() keep shared
component grids by default via same_arg = TRUE.tf_where() / tf_anywhere() work on tf_mv objects with joint
conditions across components, referenced by name (e.g.
tf_where(f, x > 0 & y < 1)); there is no value column for tf_mv input.
Components must share a common grid, or arg must be supplied explicitly.quantile() works on tf_mv objects, returning the component-wise
pointwise quantiles: a tf_mv with one curve per requested probability in
probs, exactly as quantile.tf() does per component (na.rm / probs /
type are forwarded).points() works on tf_mv objects, mirroring lines.tf_mv(): in
"trajectory" mode (d == 2) it overlays the paired (x(t), y(t)) points
with per-curve graphical-parameter recycling; in "facet" mode it overlays
each component onto the current device.tf_depth() gains a tf_mv method: a weighted componentwise aggregation of
the (centrality) univariate depths, with a weights argument mirroring
tfb_mfpc() ("equal" default, "inverse_variance", or a numeric vector).
The up-down ordering index "MHI" has no canonical vector-valued analogue and
aborts with a pointer to tf_order() (#273).median(), summary() and fivenum() on tf_mv now select observed
vector-valued curves via a single joint-depth index (one curve across all
components), fixing the previous component-wise "chimera" where each component
could come from a different observation. Pointwise entries (mean, min/max, the
central-half range) remain component-wise (#273).tf_order() with a tf_mv method: tf_order(f, by = "norm")
(or by = <component name>) orders vector-valued curves by an explicit
scalar reduction. sort(), rank() and xtfrm() remain permanently
undefined for tf_mv -- there is no canonical total order on R^d -- so
base sort()/order()/rank() fail fast rather than fabricate an order
(#273).is_tf() now returns TRUE for tf_mv as well as univariate tfd/tfb.
Code that branched on is_tf() to mean "univariate tf" should switch to
the new predicate is_tf_1d()."arg" and "id" are reserved and cannot be used as component names --
they collide with the grid and curve-id columns of long-format conversions.tf_mv from components with differing domains now warns
when widening tfd components to the union domain (evaluations outside the
observed range are NA) and aborts for tfb components, where basis
evaluation outside the fitted range would extrapolate, i.e. fabricate
values.tfb_mfpc (tf_smooth(), tf_derive(),
tf_zoom(), tf_integrate(), mean()/sd()/var(), Summary group
generics) now consistently demote to valid per-component tfb_fpc
representations with a one-time warning, like arithmetic already did --
previously they silently produced objects that abort on later use.tf_fmax(), tf_fmin() and tf_fmedian() on tf_mv return an n x d
curves-x-components matrix like tf_fmean()/tf_fsd() (previously: a
misnamed interleaved vector).tfb_fpc() and tfb_mfpc() abort informatively when the input contains
completely missing curves (partially missing evaluations continue to be
handled by the soft-impute SVD); re-scoring new data with NA curves via
tf_rebase() / vec_cast() yields NA scores and NA entries.as.data.frame.tf_mv(unnest = TRUE) gains a grids argument controlling
where components are evaluated when they live on different argument grids:
"union" (default, previous behavior) evaluates every component on each
curve's union grid, interpolating inside the observed range; "component"
evaluates each component strictly on its own grid (or on arg), so no
values are fabricated where a component was not observed -- the faithful
export used by tidyfun::tf_unnest(). For shared grids both agree.tfd(), tfb(), tfd_mv(list()) and friends) carry the
sentinel domain c(NA, NA) and now combine cleanly with populated vectors
in all vctrs paths (vec_c(), vec_rbind(), c(), casts) -- binding
rows onto an initially empty tf column works again.tfb_mv() on an existing tfb_mv with an explicit basis argument re-fits
in the requested basis instead of silently returning the old one; changing
the basis kind converts through tfd().tfb_mv()/tfb_mfpc() on raw lists/arrays route constructor arguments
(arg, domain) and basis arguments to the right place instead of
forwarding everything everywhere.tfd_mv() on 3-d arrays keeps length-1 curve/arg margins and curve names.tf_derive() and tf_integrate() on tf_mv accept per-component arg
lists, consistent with tf_interpolate().Summary group generics on tf_mv no longer recycle NA masks across
operands of different lengths.tf_arclength(): method = "polyline" honors lower/upper without
creating near-duplicate vertices, and method = "derive" no longer
NA-poisons irregular curves that do not span the full domain.tf_integrate() on irregular tfd snaps user-supplied limits
to (almost-)coinciding grid points instead of constructing invalid objects.tf_register() works for tf_mv inputs whose components live on different
grids; tf_register_shape() returns a user-supplied template unchanged
from tf_template(); single-component srvf_mv delegates to univariate
elastic registration and template-free single-curve registration is
rejected with an explanation instead of crashing inside fdasrvf.checkmate namespace imports with selective imports to avoid
new R-devel warnings from upstream rlang exports (check_string() and
check_data_frame())tf_register() & co implement registration methods (SRVF, affine, landmark,
continuous-criterion) for aligning functions and analyzing phase variability.
Returns a new tf_registration class with accessors tf_aligned(),
tf_inv_warps(), tf_template().tf_depth() now implements five functional depth methods: MBD (default), MHI,
FM (Fraiman-Muniz), FSD (Functional Spatial Depth), and RPD (Regularized
Projection Depth). MHI ranks functions from lowest (0) to highest (1); all
other depths are scaled from 0 (most extreme) to 1 (most central).
MBD is now scaled to [0, 1] instead of [0, 0.5].tf_split() / tf_combine() for splitting functions into sub-domain
fragments and re-combining themtf_rgp() gains a "brownian_bridge" covariance option and can generate
irregular data via the new irreg argumenttfb_spline() now converts fda::fd and fda::fdSmooth objects directlymgcv-style Fourier basis constructor for use in tfb_spline()print() / format() for all tf subtypestf_where() now defaults to arg = tf_arg(x), consistent with other functionsas.matrix() for irregular tfd now always interpolates to a common gridtf_derive() for tfd objects now uses second-order accurate finite
differences, preserving the original domain and grid (#59)NULL internally; fixes for tf_arg(), tf_evaluate(), tf_smooth(),
tfd() conversion, is.na(), printing, and arithmetic with NA entriessummary.tf() returning wrong central rangetfd domain enforcement for length-1 data (#139)tfb objects and all-NA vectorstf_derive and tf_integrate can now handle tfb with non-identity link functions
by falling back to tfd-calculus methods (and returning tfd objects).tfd_irreg arithmetic now operates on intersection of arg-values instead
of failing when arg-values are not identicaltfb arithmetic fixglue dependency in favour of cli / sprintf;
replaced stopifnot() with informative cli::cli_abort() throughoutvctrs::vec_arith() for group generics, vctrs utils in [.tfair formatter for all R codetf_crosscov correctlytf_rebase behaves more consistently on (irregular) data with different lengthstfb_fpc example and skipping tf_rebase tests