Skip to content

Core objects

fdatools.core: FData (sets of curves), BiFData (surfaces), LDO (linear differential operators) and inprod (inner products).

Functional data objects: :class:FData, :class:BiFData and :func:inprod.

An :class:FData pairs a coefficient array with a :class:~fdatools.basis.Basis; everything else -- evaluation, derivatives, statistics, arithmetic and inner products -- follows from that pair. All operations stay inside the array namespace of their inputs, so NumPy arrays give NumPy results and PyTorch tensors give differentiable PyTorch results.

LDO dataclass

LDO(
    order: int | None = None,
    weights: Sequence[Weight] | None = None,
)

Linear differential operator L x = Σ_j w_j D^j x + D^m x.

Replaces R's Lfd / int2Lfd / vec2Lfd. The operator is defined by its weight functions w_0 .. w_{m-1}; the highest derivative D^m always enters with unit weight, so m = len(weights).

Parameters:

Name Type Description Default
order int

Shorthand for a plain derivative: LDO(2) is D², i.e. weights (0, 0). Mutually exclusive with weights. Defaults to 0 (the identity operator) when both arguments are omitted.

None
weights sequence

Weights w_0 .. w_{m-1}. Each entry is either a real scalar or a callable functional weight (an :class:~fdatools.core.FData holding a single curve).

None

Attributes:

Name Type Description
weights tuple

The stored weights.

order int

The highest derivative m.

Raises:

Type Description
ValueError

If both order and weights are given, or order is negative.

Examples:

>>> from fdatools import LDO
>>> LDO(2).order
2
>>> LDO(2).is_derivative
True
>>> LDO.harmonic(period=365.0).order
3

order property

order: int

Highest derivative order m appearing in the operator.

Returns:

Type Description
int

len(self.weights).

Examples:

>>> from fdatools import LDO
>>> LDO(weights=[1.0, 2.0]).order
2

is_derivative property

is_derivative: bool

Whether the operator is the plain derivative D^m.

Returns:

Type Description
bool

True when every weight is the scalar zero, so that closed-form derivative penalties apply.

Examples:

>>> from fdatools import LDO
>>> LDO(weights=[0.0, 1.0]).is_derivative
False

is_constant property

is_constant: bool

Whether every weight is a plain number rather than a functional weight.

Returns:

Type Description
bool

True when the operator is fully described by its numeric weights, and so compares and hashes by value.

Examples:

>>> from fdatools import LDO
>>> LDO(weights=[1.0, 2.0]).is_constant
True

harmonic classmethod

harmonic(period: float) -> LDO

Return the harmonic accelerator D³ x + ω² D x with ω = 2π/period.

Parameters:

Name Type Description Default
period float

Period of the sinusoid annihilated by the operator.

required

Returns:

Type Description
LDO

Operator with weights (0, ω², 0).

Examples:

>>> from fdatools import LDO
>>> LDO.harmonic(period=1.0).weights[1] > 39.0
True

apply

apply(
    evaluate: Callable[[int], Any], t: Any, xp: ModuleType
) -> Any

Apply the operator to a family of functions evaluated at t.

Parameters:

Name Type Description Default
evaluate callable

evaluate(j) returns the j-th derivative at t as an array whose first axis runs over t.

required
t array

Evaluation points, shape (n_t,).

required
xp module

Array namespace to compute in.

required

Returns:

Type Description
array

Σ_j w_j(t) · evaluate(j) + evaluate(m).

Examples:

>>> import numpy as np
>>> from fdatools import LDO
>>> from fdatools._backend import default_namespace
>>> t = np.linspace(0.0, 1.0, 3)
>>> LDO(1).apply(lambda j: np.ones((3, 2)) * j, t, default_namespace()).tolist()
[[1.0, 1.0], [1.0, 1.0], [1.0, 1.0]]

FData dataclass

FData(coefs: Any, basis: Basis)

Bases: PlotMixin

A set of curves expressed as x_j(t) = sum_i coefs[i, j] phi_i(t).

Replaces R's fd object and the functions that operate on it.

Parameters:

Name Type Description Default
coefs array_like

Coefficients of shape (n_basis,), (n_basis, n_curves) or (n_basis, n_curves, n_vars). A one-dimensional array is read as a single curve.

required
basis Basis

The expansion basis.

required

Attributes:

Name Type Description
coefs array

The coefficient array, always at least two-dimensional.

basis Basis

The expansion basis.

Raises:

Type Description
ValueError

If coefs has more than three axes, or its first axis does not match basis.n_basis.

Examples:

>>> import numpy as np
>>> import fdatools as fdt
>>> fd = fdt.FData(np.eye(5), fdt.BSpline(domain=(0.0, 1.0), n_basis=5))
>>> fd.n_curves
5
>>> fd(np.array([0.0, 1.0])).shape
(2, 5)

n_curves property

n_curves: int

Number of curves held by the object.

Returns:

Type Description
int

coefs.shape[1].

Examples:

>>> import numpy as np
>>> import fdatools as fdt
>>> fdt.FData(np.ones((4, 3)), fdt.BSpline(n_basis=4)).n_curves
3

n_vars property

n_vars: int

Number of variables per curve.

Returns:

Type Description
int

1 for two-dimensional coefficients, else coefs.shape[2].

Examples:

>>> import numpy as np
>>> import fdatools as fdt
>>> fdt.FData(np.ones((4, 3, 2)), fdt.BSpline(n_basis=4)).n_vars
2

domain property

domain: tuple[float, float]

Interval on which the curves are defined.

Returns:

Type Description
tuple of float

self.basis.domain.

Examples:

>>> import numpy as np
>>> import fdatools as fdt
>>> fdt.FData(np.ones((4, 1)), fdt.BSpline(domain=(0.0, 2.0), n_basis=4)).domain
(0.0, 2.0)

__getitem__

__getitem__(index: Any) -> FData

Select curves by position.

Parameters:

Name Type Description Default
index int, slice, sequence of int, array or boolean mask

Curve selector applied to the second axis of coefs. Negative positions count from the end. A boolean mask must have one entry per curve.

required

Returns:

Type Description
FData

The selected curves, always with a curve axis.

Raises:

Type Description
IndexError

If a position is out of range, or a boolean mask has the wrong length.

Examples:

>>> import numpy as np
>>> import fdatools as fdt
>>> fd = fdt.FData(np.eye(4), fdt.BSpline(n_basis=4))
>>> len(fd[1:3])
2
>>> len(fd[-1])
1

__call__

__call__(t: Any, deriv: int | LDO = 0) -> Array

Evaluate the curves at t.

Parameters:

Name Type Description Default
t array_like

Evaluation points, shape (n_points,).

required
deriv int or LDO

Derivative order, or a linear differential operator. Defaults to 0.

0

Returns:

Type Description
array

Values of shape (n_points, n_curves), or (n_points, n_curves, n_vars) for multivariate coefficients, in the array namespace of t.

Examples:

>>> import numpy as np
>>> import fdatools as fdt
>>> fd = fdt.FData(np.ones((4, 1)), fdt.BSpline(domain=(0.0, 1.0), n_basis=4))
>>> float(fd(np.array([0.5]))[0, 0])
1.0

to_numpy

to_numpy(t: Any) -> NDArray

Evaluate at t and return a NumPy array.

Parameters:

Name Type Description Default
t array_like

Evaluation points.

required

Returns:

Type Description
ndarray

The evaluated curves.

Examples:

>>> import numpy as np
>>> import fdatools as fdt
>>> fd = fdt.FData(np.ones((4, 1)), fdt.BSpline(n_basis=4))
>>> fd.to_numpy(np.array([0.5])).shape
(1, 1)

to_torch

to_torch(t: Any) -> Array

Evaluate at t and return a PyTorch tensor.

Parameters:

Name Type Description Default
t array_like

Evaluation points.

required

Returns:

Type Description
Tensor

The evaluated curves, differentiable with respect to t and the coefficients.

Raises:

Type Description
ImportError

If PyTorch is not installed.

Examples:

>>> import numpy as np
>>> import fdatools as fdt
>>> fd = fdt.FData(np.ones((4, 1)), fdt.BSpline(n_basis=4))
>>> fd.to_torch(np.array([0.5])).shape
torch.Size([1, 1])

derivative

derivative(n: int = 1) -> FData

Return the n-th derivative as a new :class:FData.

The derivative is exact: each basis reports the space its derivative lives in together with the matching coefficient map, so no numerical projection is involved.

Parameters:

Name Type Description Default
n int

Derivative order. Defaults to 1.

1

Returns:

Type Description
FData

The derivative, generally in a different basis.

Raises:

Type Description
ValueError

If n is negative.

Examples:

>>> import numpy as np
>>> import fdatools as fdt
>>> fd = fdt.FData(np.arange(6.0), fdt.BSpline(domain=(0.0, 1.0), n_basis=6))
>>> fd.derivative().basis.order
3

mean

mean() -> FData

Return the pointwise mean curve.

Returns:

Type Description
FData

A single curve, the average of the coefficients.

Examples:

>>> import numpy as np
>>> import fdatools as fdt
>>> fdt.FData(np.eye(4), fdt.BSpline(n_basis=4)).mean().n_curves
1

center

center() -> FData

Return the curves with the mean curve removed.

Returns:

Type Description
FData

Centred curves in the same basis.

Examples:

>>> import numpy as np
>>> import fdatools as fdt
>>> fd = fdt.FData(np.eye(4), fdt.BSpline(n_basis=4)).center()
>>> bool(abs(float(fd.coefs.sum())) < 1e-12)
True

std

std() -> FData

Return the pointwise sample standard deviation curve.

The pointwise standard deviation is not a member of the basis span -- it involves a square root -- so it is evaluated on a fine grid and projected back by least squares, as R's sd.fd does.

Returns:

Type Description
FData

A single curve in the same basis, keeping this object's variables: coefficients of shape (n_basis, 1), or (n_basis, 1, n_vars) when several variables are present. The deviation is taken over the replications only, never across variables.

Raises:

Type Description
ValueError

If fewer than two curves are present.

Examples:

>>> import numpy as np
>>> import fdatools as fdt
>>> fdt.FData(np.eye(4), fdt.BSpline(n_basis=4)).std().n_curves
1

cov

cov() -> BiFData

Return the sample covariance surface.

Returns:

Type Description
BiFData

The bivariate function c(s, t) estimating Cov(x(s), x(t)) with denominator n_curves - 1. With several variables each gets its own surface and they are stacked on a trailing axis, so the coefficients have shape (n_basis, n_basis, n_vars) and evaluation returns (len(s), len(t), n_vars). Variables are never pooled as extra replications, and cross-variable covariances are not formed.

Raises:

Type Description
ValueError

If fewer than two curves are present.

Examples:

>>> import numpy as np
>>> import fdatools as fdt
>>> fdt.FData(np.eye(4), fdt.BSpline(n_basis=4)).cov().coefs.shape
(4, 4)

__add__

__add__(other: FData | float) -> FData

Add another function on the same basis, or a scalar.

__mul__

__mul__(other: FData | float) -> FData

Multiply by a scalar, or form the pointwise product of two functions.

The product of two functions is expanded in basis1 * basis2, which for splines carries the knot multiplicities that make the product exact.

__pow__

__pow__(power: float) -> FData

Raise the curves to a power.

Non-negative integer powers are exact repeated products. Any other exponent is projected onto a refined spline basis, since the result is not a basis expansion of the original space.

Parameters:

Name Type Description Default
power int or float

The exponent.

required

Returns:

Type Description
FData

The powered curves.

Examples:

>>> import numpy as np
>>> import fdatools as fdt
>>> fd = fdt.FData(np.ones((4, 1)), fdt.BSpline(domain=(0.0, 1.0), n_basis=4))
>>> round(float((fd**2)(np.array([0.5]))[0, 0]), 10)
1.0

__matmul__

__matmul__(other: FData) -> Array

Return the matrix of inner products with other.

Parameters:

Name Type Description Default
other FData

Right-hand functions.

required

Returns:

Type Description
array

Matrix of shape (self.n_curves, other.n_curves).

Examples:

>>> import numpy as np
>>> import fdatools as fdt
>>> fd = fdt.FData(np.eye(4), fdt.BSpline(domain=(0.0, 1.0), n_basis=4))
>>> (fd @ fd).shape
(4, 4)

to_pandas

to_pandas(t: Any) -> Any

Evaluate on t and return a long-format :class:pandas.DataFrame.

Thin delegation to :func:fdatools.io.to_pandas; requires the pandas extra.

Parameters:

Name Type Description Default
t array_like

Argument values to evaluate at.

required

Returns:

Type Description
DataFrame

One row per (t, curve[, var]) combination.

Examples:

>>> import numpy as np
>>> import fdatools as fdt
>>> fd = fdt.FData(np.eye(4), fdt.BSpline(domain=(0.0, 1.0), n_basis=4))
>>> list(fd.to_pandas(np.array([0.0, 1.0])).columns)
['t', 'curve', 'value']

to_xarray

to_xarray(t: Any) -> Any

Evaluate on t and return an :class:xarray.DataArray.

Thin delegation to :func:fdatools.io.to_xarray; requires the pandas extra (which pulls in xarray).

Parameters:

Name Type Description Default
t array_like

Argument values to evaluate at.

required

Returns:

Type Description
DataArray

Dims ("t", "curve"), or ("t", "curve", "var") when self.n_vars > 1.

Examples:

>>> import numpy as np
>>> import fdatools as fdt
>>> fd = fdt.FData(np.eye(4), fdt.BSpline(domain=(0.0, 1.0), n_basis=4))
>>> fd.to_xarray(np.array([0.0, 1.0])).dims
('t', 'curve')

BiFData dataclass

BiFData(coefs: Any, sbasis: Basis, tbasis: Basis)

A bivariate function c(s, t) = sum_ij coefs[i, j] phi_i(s) psi_j(t).

Replaces R's bifd object.

Parameters:

Name Type Description Default
coefs array_like

Coefficients whose first two axes match sbasis and tbasis. Trailing axes (replications, variables) are carried through untouched.

required
sbasis Basis

Basis for the first argument.

required
tbasis Basis

Basis for the second argument.

required

Raises:

Type Description
ValueError

If the first two axes of coefs do not match the two bases.

Examples:

>>> import numpy as np
>>> import fdatools as fdt
>>> b = fdt.BSpline(domain=(0.0, 1.0), n_basis=4)
>>> bifd = fdt.BiFData(np.eye(4), b, b)
>>> bifd(np.array([0.0, 1.0]), np.array([0.0, 0.5, 1.0])).shape
(2, 3)

domain property

domain: tuple[tuple[float, float], tuple[float, float]]

The two argument domains.

Returns:

Type Description
tuple

(sbasis.domain, tbasis.domain).

Examples:

>>> import numpy as np
>>> import fdatools as fdt
>>> b = fdt.BSpline(domain=(0.0, 1.0), n_basis=4)
>>> fdt.BiFData(np.eye(4), b, b).domain
((0.0, 1.0), (0.0, 1.0))

__call__

__call__(
    s: Any, t: Any, deriv: tuple[int, int] = (0, 0)
) -> Array

Evaluate the surface on the grid s x t.

Parameters:

Name Type Description Default
s array_like

Evaluation points for the first and second argument.

required
t array_like

Evaluation points for the first and second argument.

required
deriv tuple of int

Derivative orders applied to each argument. Defaults to (0, 0).

(0, 0)

Returns:

Type Description
array

Values of shape (len(s), len(t)) followed by any trailing axes of coefs.

Examples:

>>> import numpy as np
>>> import fdatools as fdt
>>> b = fdt.BSpline(domain=(0.0, 1.0), n_basis=4)
>>> fdt.BiFData(np.eye(4), b, b)(np.array([0.5]), np.array([0.5])).shape
(1, 1)

transpose

transpose() -> BiFData

Return the surface with its two arguments swapped.

Returns:

Type Description
BiFData

The transposed surface.

Examples:

>>> import numpy as np
>>> import fdatools as fdt
>>> b = fdt.BSpline(domain=(0.0, 1.0), n_basis=4)
>>> fdt.BiFData(np.eye(4), b, b).transpose().coefs.shape
(4, 4)

inprod

inprod(
    first: FData | Basis,
    second: FData | Basis,
    lfd1: int | LDO = 0,
    lfd2: int | LDO = 0,
) -> Array

Return the matrix of inner products integral (lfd1 x_i)(lfd2 y_j) dt.

Replaces R's inprod and inprod.bspline. A :class:~fdatools.basis.Basis may be passed in place of an :class:FData, in which case its basis functions play the role of the curves.

Parameters:

Name Type Description Default
first FData or Basis

Left- and right-hand functions. They must share a domain.

required
second FData or Basis

Left- and right-hand functions. They must share a domain.

required
lfd1 int or LDO

Operators applied to the left and right arguments. Default 0.

0
lfd2 int or LDO

Operators applied to the left and right arguments. Default 0.

0

Returns:

Type Description
array

Matrix of shape (n_left, n_right).

Raises:

Type Description
ValueError

If the two arguments live on different domains, or either carries a variable axis.

Examples:

>>> import fdatools as fdt
>>> b = fdt.BSpline(domain=(0.0, 1.0), n_basis=5)
>>> fdt.inprod(b, b).shape
(5, 5)