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