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: |
None
|
weights
|
sequence
|
Weights |
None
|
Attributes:
| Name | Type | Description |
|---|---|---|
weights |
tuple
|
The stored weights. |
order |
int
|
The highest derivative |
Raises:
| Type | Description |
|---|---|
ValueError
|
If both |
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
|
|
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
|
|
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
|
|
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 |
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
|
|
required |
t
|
array
|
Evaluation points, shape |
required |
xp
|
module
|
Array namespace to compute in. |
required |
Returns:
| Type | Description |
|---|---|
array
|
|
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 |
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 |
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
|
|
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
|
|
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
|
|
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 |
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 |
required |
deriv
|
int or LDO
|
Derivative order, or a linear differential operator. Defaults to
|
0
|
Returns:
| Type | Description |
|---|---|
array
|
Values of shape |
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 |
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
|
Returns:
| Type | Description |
|---|---|
FData
|
The derivative, generally in a different basis. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
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 |
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 |
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 |
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 |
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 |
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 |
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
|
|
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)
|
Returns:
| Type | Description |
|---|---|
array
|
Values of shape |
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
|
lfd2
|
int or LDO
|
Operators applied to the left and right arguments. Default |
0
|
Returns:
| Type | Description |
|---|---|
array
|
Matrix of shape |
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)