Basis systems¶
fdatools.basis: the seven basis families and their shared interface
(evaluation, roughness penalties, Gram matrices, products).
Functional bases: B-spline, Fourier, monomial, exponential, power, polygonal.
Every basis is an immutable dataclass, is callable (b(t, deriv=0) returns the
(len(t), n_basis) basis matrix in the caller's array namespace) and knows how
to build its own penalty and Gram matrices.
The mathematics is implemented from first principles: the Cox-de Boor recursion for B-splines, the analytic Fourier/monomial/exponential/power definitions, and Gauss-Legendre quadrature of sufficient order for the inner products, following Ramsay & Silverman, Functional Data Analysis (2nd ed.), chapter 3.
Examples:
>>> import numpy as np
>>> from fdatools import BSpline
>>> b = BSpline(domain=(0.0, 1.0), n_basis=6)
>>> b(np.array([0.0, 0.5, 1.0])).shape
(3, 6)
>>> float(np.max(np.abs(b(np.linspace(0, 1, 11)).sum(axis=1) - 1.0))) < 1e-14
True
Basis
dataclass
¶
Basis()
Bases: ABC
Abstract base class for every fdatools basis system.
A basis is a frozen dataclass, so it is hashable, comparable and safe to use as a cache key. Subclasses provide the evaluation rule; this class provides the shared machinery for derivatives, penalties, Gram matrices and products.
Attributes:
| Name | Type | Description |
|---|---|---|
domain |
tuple of float
|
The interval |
__call__ ¶
__call__(t: Array, deriv: int | LDO = 0) -> Array
Evaluate the basis (or a derivative of it) 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 to apply. |
0
|
Returns:
| Type | Description |
|---|---|
array
|
Basis matrix of shape |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
>>> import numpy as np
>>> from fdatools import Monomial
>>> Monomial(domain=(0.0, 1.0), n_basis=3)(np.array([2.0])).tolist()
[[1.0, 2.0, 4.0]]
penalty ¶
penalty(
op: int | LDO = 2, *, xp: ModuleType | None = None
) -> Array
Roughness penalty matrix R[i, j] = ∫ (L φ_i)(L φ_j) dt.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
op
|
int or LDO
|
Derivative order (default |
2
|
xp
|
module
|
Array namespace for the result. Defaults to NumPy; the matrix is a constant of the basis, so it carries no gradient either way. |
None
|
Returns:
| Type | Description |
|---|---|
array
|
Symmetric positive-semidefinite matrix of shape
|
Examples:
>>> import numpy as np
>>> from fdatools import Fourier
>>> pen = Fourier(domain=(0.0, 1.0), n_basis=3).penalty(1)
>>> np.round(np.diag(pen), 6).tolist()
[0.0, 39.478418, 39.478418]
gram ¶
gram(
deriv: int = 0, *, xp: ModuleType | None = None
) -> Array
Return the cached Gram matrix G[i, j] = ∫ φ_i^{(deriv)} φ_j^{(deriv)} dt.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
deriv
|
int
|
Derivative order applied to both factors. |
0
|
xp
|
module
|
Array namespace for the result. Defaults to NumPy. |
None
|
Returns:
| Type | Description |
|---|---|
array
|
Symmetric matrix of shape |
Examples:
>>> import numpy as np
>>> from fdatools import Fourier
>>> np.round(Fourier(domain=(0.0, 1.0), n_basis=3).gram(), 12).tolist()
[[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]]
__mul__ ¶
__mul__(other: Basis) -> Basis
Return a basis whose span contains every pointwise product φ_i ψ_j.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
other
|
Basis
|
The second factor; must share this basis's domain. |
required |
Returns:
| Type | Description |
|---|---|
Basis
|
The product basis. Exact for spline x spline (merged breaks, order
|
BSpline
dataclass
¶
BSpline(
domain: tuple[float, float] = (0.0, 1.0),
n_basis: int | None = None,
order: int = 4,
breaks: Sequence[float] | None = None,
)
Bases: Basis
B-spline basis of a given order on a set of break points.
Replaces R's create.bspline.basis.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
domain
|
tuple of float
|
Interval |
(0.0, 1.0)
|
n_basis
|
int
|
Number of basis functions. Ignored when |
None
|
order
|
int
|
Spline order = polynomial degree + 1. Defaults to |
4
|
breaks
|
sequence of float
|
Strictly increasing break points including both endpoints. Defaults to
|
None
|
Attributes:
| Name | Type | Description |
|---|---|---|
order |
int
|
Spline order. |
breaks |
tuple of float
|
The break points actually used. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the arguments are inconsistent, or |
Examples:
>>> import numpy as np
>>> from fdatools import BSpline
>>> b = BSpline(domain=(0.0, 1.0), n_basis=5, order=3)
>>> b.n_basis
5
>>> b.breaks
(0.0, 0.3333333333333333, 0.6666666666666666, 1.0)
>>> float(b(np.array([1.0]))[0, -1])
1.0
n_basis
property
¶
n_basis: int
Number of B-spline basis functions, len(breaks) + order - 2.
Returns:
| Type | Description |
|---|---|
int
|
The basis dimension. |
Examples:
>>> from fdatools import BSpline
>>> BSpline(breaks=[0.0, 0.5, 1.0], order=4).n_basis
5
names
property
¶
names: tuple[str, ...]
Names bspl<order>.<i> following R's fda convention.
Returns:
| Type | Description |
|---|---|
tuple of str
|
One name per basis function. |
Examples:
>>> from fdatools import BSpline
>>> BSpline(n_basis=4).names
('bspl4.1', 'bspl4.2', 'bspl4.3', 'bspl4.4')
knots
property
¶
knots: tuple[float, ...]
The open knot vector, endpoints repeated order times.
Returns:
| Type | Description |
|---|---|
tuple of float
|
Knot sequence of length |
Examples:
>>> from fdatools import BSpline
>>> BSpline(breaks=[0.0, 1.0], order=2).knots
(0.0, 0.0, 1.0, 1.0)
Fourier
dataclass
¶
Fourier(
domain: tuple[float, float] = (0.0, 1.0),
n_basis: int = 3,
period: float | None = None,
)
Bases: Basis
Fourier basis, orthonormal over one period.
The functions are 1/sqrt(P), then sqrt(2/P) sin(h ω t) and
sqrt(2/P) cos(h ω t) for h = 1 .. H with ω = 2π / P. Replaces
R's create.fourier.basis.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
domain
|
tuple of float
|
Interval |
(0.0, 1.0)
|
n_basis
|
int
|
Number of basis functions. An even value is raised by one so that the
sine/cosine pairs are complete, matching R; read back the effective size
from :attr: |
3
|
period
|
float
|
Period |
None
|
Attributes:
| Name | Type | Description |
|---|---|---|
n_harmonics |
int
|
Number of sine/cosine pairs |
period |
float
|
The period. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Examples:
>>> from fdatools import Fourier
>>> Fourier(domain=(0.0, 365.0), n_basis=4).n_basis
5
>>> Fourier(domain=(0.0, 365.0), n_basis=5).period
365.0
n_basis
property
¶
n_basis: int
Number of basis functions, always odd: 2 * n_harmonics + 1.
Returns:
| Type | Description |
|---|---|
int
|
The basis dimension. |
Examples:
>>> from fdatools import Fourier
>>> Fourier(n_basis=6).n_basis
7
names
property
¶
names: tuple[str, ...]
Names const, sin1, cos1, ... following R's convention.
Returns:
| Type | Description |
|---|---|
tuple of str
|
One name per basis function. |
Examples:
>>> from fdatools import Fourier
>>> Fourier(n_basis=3).names
('const', 'sin1', 'cos1')
Monomial
dataclass
¶
Monomial(
domain: tuple[float, float] = (0.0, 1.0),
n_basis: int = 2,
exponents: Sequence[int] | None = None,
)
Bases: Basis
Monomial basis t^e for non-negative integer exponents.
Replaces R's create.monomial.basis.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
domain
|
tuple of float
|
Interval |
(0.0, 1.0)
|
n_basis
|
int
|
Number of basis functions; ignored when |
2
|
exponents
|
sequence of int
|
Exponents to use. Defaults to |
None
|
Attributes:
| Name | Type | Description |
|---|---|---|
exponents |
tuple of int
|
The exponents. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If exponents are negative or repeated, or conflict with |
Examples:
>>> import numpy as np
>>> from fdatools import Monomial
>>> Monomial(n_basis=3)(np.array([3.0]), deriv=1).tolist()
[[0.0, 1.0, 6.0]]
n_basis
property
¶
n_basis: int
Number of monomials.
Returns:
| Type | Description |
|---|---|
int
|
|
Examples:
>>> from fdatools import Monomial
>>> Monomial(exponents=[0, 2, 4]).n_basis
3
names
property
¶
names: tuple[str, ...]
Names monomial<e>.
Returns:
| Type | Description |
|---|---|
tuple of str
|
One name per basis function. |
Examples:
>>> from fdatools import Monomial
>>> Monomial(n_basis=2).names
('monomial0', 'monomial1')
Power
dataclass
¶
Power(
domain: tuple[float, float] = (1.0, 2.0),
exponents: Sequence[float] = (0.0, 1.0),
)
Bases: Basis
Power basis t^e for arbitrary real exponents.
Replaces R's create.power.basis. The domain must not contain negative
values, and must exclude zero whenever a negative power appears.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
domain
|
tuple of float
|
Interval |
(1.0, 2.0)
|
exponents
|
sequence of float
|
The exponents. Defaults to |
(0.0, 1.0)
|
Attributes:
| Name | Type | Description |
|---|---|---|
exponents |
tuple of float
|
The exponents. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the domain is negative, the exponents repeat, or a negative exponent is combined with a domain touching zero. |
Examples:
>>> import numpy as np
>>> from fdatools import Power
>>> Power(domain=(1.0, 4.0), exponents=[0.5])(np.array([4.0])).tolist()
[[2.0]]
n_basis
property
¶
n_basis: int
Number of power functions.
Returns:
| Type | Description |
|---|---|
int
|
|
Examples:
>>> from fdatools import Power
>>> Power(exponents=[0.0, 0.5, 1.0]).n_basis
3
names
property
¶
names: tuple[str, ...]
Names power<e>.
Returns:
| Type | Description |
|---|---|
tuple of str
|
One name per basis function. |
Examples:
>>> from fdatools import Power
>>> Power(exponents=[0.0, 0.5]).names
('power0.0', 'power0.5')
Exponential
dataclass
¶
Exponential(
domain: tuple[float, float] = (0.0, 1.0),
rates: Sequence[float] = (0.0, 1.0),
)
Bases: Basis
Exponential basis exp(r t) for a set of rates.
Replaces R's create.exponential.basis.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
domain
|
tuple of float
|
Interval |
(0.0, 1.0)
|
rates
|
sequence of float
|
The rates |
(0.0, 1.0)
|
Attributes:
| Name | Type | Description |
|---|---|---|
rates |
tuple of float
|
The rates. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the rates are empty or repeated. |
Examples:
>>> import numpy as np
>>> from fdatools import Exponential
>>> Exponential(rates=[0.0, 1.0])(np.array([0.0])).tolist()
[[1.0, 1.0]]
n_basis
property
¶
n_basis: int
Number of exponentials.
Returns:
| Type | Description |
|---|---|
int
|
|
Examples:
>>> from fdatools import Exponential
>>> Exponential(rates=[0.0, 1.0, 2.0]).n_basis
3
names
property
¶
names: tuple[str, ...]
Names exp<rate>.
Returns:
| Type | Description |
|---|---|
tuple of str
|
One name per basis function. |
Examples:
>>> from fdatools import Exponential
>>> Exponential(rates=[0.0, 1.0]).names
('exp0.0', 'exp1.0')
Constant
dataclass
¶
Constant(domain: tuple[float, float] = (0.0, 1.0))
Bases: Basis
The one-function basis φ(t) = 1.
Replaces R's create.constant.basis.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
domain
|
tuple of float
|
Interval |
(0.0, 1.0)
|
Examples:
>>> import numpy as np
>>> from fdatools import Constant
>>> Constant(domain=(0.0, 2.0)).gram().tolist()
[[2.0]]
n_basis
property
¶
n_basis: int
Always one.
Returns:
| Type | Description |
|---|---|
int
|
|
Examples:
>>> from fdatools import Constant
>>> Constant().n_basis
1
names
property
¶
names: tuple[str, ...]
The single name const.
Returns:
| Type | Description |
|---|---|
tuple of str
|
|
Examples:
>>> from fdatools import Constant
>>> Constant().names
('const',)
Polygonal
dataclass
¶
Polygonal(argvals: Sequence[float] = (0.0, 0.5, 1.0))
Bases: Basis
Piecewise-linear ("hat function") basis on a set of arguments.
Replaces R's create.polygonal.basis. Mathematically this is the
order-2 B-spline basis with breaks at argvals, and it is evaluated with
the same Cox-de Boor kernel.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
argvals
|
sequence of float
|
Strictly increasing vertex locations; the domain is
|
(0.0, 0.5, 1.0)
|
Attributes:
| Name | Type | Description |
|---|---|---|
argvals |
tuple of float
|
The vertices. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If fewer than two arguments are given, or they are not increasing. |
Examples:
>>> import numpy as np
>>> from fdatools import Polygonal
>>> Polygonal([0.0, 1.0, 2.0])(np.array([0.5])).tolist()
[[0.5, 0.5, 0.0]]
n_basis
property
¶
n_basis: int
One basis function per vertex.
Returns:
| Type | Description |
|---|---|
int
|
|
Examples:
>>> from fdatools import Polygonal
>>> Polygonal([0.0, 1.0, 2.0]).n_basis
3
names
property
¶
names: tuple[str, ...]
Names polyg<i>.
Returns:
| Type | Description |
|---|---|
tuple of str
|
One name per vertex. |
Examples:
>>> from fdatools import Polygonal
>>> Polygonal([0.0, 1.0]).names
('polyg1', 'polyg2')
order
property
¶
order: int
The equivalent B-spline order, always 2.
Returns:
| Type | Description |
|---|---|
int
|
|
Examples:
>>> from fdatools import Polygonal
>>> Polygonal([0.0, 1.0]).order
2