Skip to content

Statistics

fdatools.stats: covariance and correlation of functional data, functional depth, the functional boxplot, and permutation t and F tests.

f_test takes either raw regression inputs, f_test(y, x, basis=, lam=, penalty=) like R's Fperm.fd, or a model fitted by fregress, f_test(model, n_perm=1000), which refits that exact model (its terms, intercept included, coefficient settings and weights) under every permutation.

Functional statistics: covariance, correlation, depth, boxplots, tests and bands.

Replaces R's var.fd, cor.fd, fbplot, fdepth, tperm.fd, Fperm.fd, the pointwise-variance use of y2cMap/fRegress.stderr, plotbeta, plotscores and cycleplot.fd.

  • :func:cov and :func:cor give the (cross-)covariance surface and the correlation matrix of one or two sets of curves.
  • :func:depth orders curves from the centre outwards (modified band depth, band depth or Fraiman--Muniz depth) and returns the deepest curve and a trimmed mean; :func:boxplot builds the functional boxplot of Sun and Genton (2011) on top of it.
  • :func:t_test and :func:f_test are permutation tests whose statistic is the largest pointwise two-sample t or regression F statistic.
  • :func:confidence_band gives pointwise confidence limits of a smooth or of regression coefficients; :func:plot_beta draws the latter.
  • :func:cycleplot and :func:plot_scores draw bivariate cycles and principal component score scatters.

Depths and tests work on curve values sampled on a grid, so they return NumPy arrays whatever the input namespace; :func:cov and :func:cor stay in the namespace of their inputs.

Examples:

>>> import numpy as np
>>> from fdatools import BSpline, FData
>>> from fdatools.stats import depth
>>> fd = FData(np.random.default_rng(0).normal(size=(6, 9)), BSpline(n_basis=6))
>>> depth(fd).depth.shape
(9,)

DepthResult dataclass

DepthResult(
    depth: NDArray,
    median_index: int,
    median: NDArray,
    trimmed_index: NDArray,
    trimmed_mean: NDArray,
    t: NDArray | None,
)

Depth of every curve, with the deepest curve and a trimmed mean.

Attributes:

Name Type Description
depth ndarray

Depth of each curve, shape (n_curves,); larger is more central.

median_index int

Position of the deepest curve (the first one if several tie).

median ndarray

Values of the deepest curve at the evaluation points.

trimmed_index ndarray

Positions, in increasing order, of the curves whose depth is at least the trim quantile of all depths.

trimmed_mean ndarray

Pointwise mean of the curves in trimmed_index.

t ndarray or None

The evaluation points, or None when values were given without them.

Examples:

>>> import numpy as np
>>> from fdatools.stats import depth
>>> values = np.array([[0.0, 1.0, 2.0], [0.0, 1.0, 2.0]])
>>> depth(values).median_index
1

BoxplotResult dataclass

BoxplotResult(
    values: NDArray,
    t: NDArray | None,
    depth: NDArray,
    median_index: int,
    median: NDArray,
    outliers: NDArray,
    central_lower: NDArray,
    central_upper: NDArray,
    fence_lower: NDArray,
    fence_upper: NDArray,
    whisker_lower: NDArray,
    whisker_upper: NDArray,
)

A functional boxplot: depth ordering, central region, fences and outliers.

Attributes:

Name Type Description
values ndarray

The curves as sampled, shape (n_points, n_curves).

t ndarray or None

The evaluation points, or None when values were given without them.

depth ndarray

Depth of each curve, shape (n_curves,).

median_index int

Position of the deepest curve.

median ndarray

Values of the deepest curve.

outliers ndarray

Positions of the curves leaving the fences at some point, increasing.

central_lower, central_upper ndarray

Pointwise envelope of the central region, the ceil(prob * n_curves) deepest curves -- the box.

fence_lower, fence_upper ndarray

The box inflated by factor times its height on either side.

whisker_lower, whisker_upper ndarray

Pointwise envelope of every curve that is not an outlier.

Examples:

>>> import numpy as np
>>> from fdatools.stats import boxplot
>>> values = np.vstack([np.arange(7.0), np.arange(7.0)])
>>> values[:, 6] = 40.0
>>> boxplot(values).outliers.tolist()
[6]

plot

plot(ax: Axes | None = None, **kwargs: Any) -> Axes

Draw the boxplot: central region, median, whiskers and outliers.

Parameters:

Name Type Description Default
ax Axes

Axes to draw on. A new figure is created when omitted.

None
**kwargs Any

Passed to :meth:matplotlib.axes.Axes.plot for the median curve.

{}

Returns:

Type Description
Axes

The axes drawn on. The median is the first line, then the upper and lower whiskers, then one line per outlier; the central region is the single filled collection.

Examples:

>>> import matplotlib
>>> matplotlib.use("Agg")
>>> import numpy as np
>>> from fdatools.stats import boxplot
>>> values = np.vstack([np.arange(7.0), np.arange(7.0) + 1.0])
>>> len(boxplot(values).plot().lines)
3

PermutationTestResult dataclass

PermutationTestResult(
    statistic: float,
    null: NDArray,
    pvalue: float,
    critical_value: float,
    pointwise: NDArray,
    pointwise_null: NDArray,
    pointwise_pvalue: NDArray,
    pointwise_critical_value: NDArray,
    t: NDArray | None,
)

Outcome of a permutation test built on a pointwise statistic.

Attributes:

Name Type Description
statistic float

The observed statistic: the maximum of pointwise.

null ndarray

The statistic recomputed under each permutation, shape (n_perm,).

pvalue float

Share of permutations whose statistic reaches the observed one.

critical_value float

The 1 - q quantile of null (R's qval).

pointwise ndarray

Observed statistic at each evaluation point, shape (n_points,); a single entry for a scalar response.

pointwise_null ndarray

Pointwise statistic under each permutation, (n_perm, n_points).

pointwise_pvalue ndarray

Pointwise permutation p-values.

pointwise_critical_value ndarray

Pointwise 1 - q quantiles of the null.

t ndarray or None

The evaluation points; None for a scalar response.

Examples:

>>> import numpy as np
>>> from fdatools import BSpline, FData
>>> from fdatools.stats import t_test
>>> rng = np.random.default_rng(0)
>>> a = FData(rng.normal(size=(5, 6)), BSpline(n_basis=5))
>>> b = FData(rng.normal(size=(5, 6)) + 3.0, BSpline(n_basis=5))
>>> t_test(a, b, n_perm=50, random_state=1).pvalue
0.0

ConfidenceBand dataclass

ConfidenceBand(
    t: Array,
    estimate: Array,
    stderr: Array,
    lower: Array,
    upper: Array,
    level: float,
    name: str | None = None,
)

Pointwise confidence band of a smooth or of a regression coefficient.

Attributes:

Name Type Description
t array

Evaluation points, shape (n_t,).

estimate array

The estimate at t: fd(t) for a smooth (shape (n_t, n_curves) or (n_t, n_curves, n_vars)), beta_j(t) for a coefficient (shape (n_t,)).

stderr array

Pointwise standard error, shape (n_t,). Every curve of a smooth shares it: the curves are fitted by the same linear map and share the error covariance.

lower, upper array

estimate -/+ z * stderr with z the (1 + level) / 2 quantile of the standard normal distribution; shaped like estimate.

level float

Pointwise coverage of the band.

name str or None

Term name for a regression coefficient, None for a smooth.

Examples:

>>> import numpy as np
>>> from fdatools import BSpline
>>> from fdatools.smoothing import smooth
>>> from fdatools.stats import confidence_band
>>> t = np.linspace(0.0, 1.0, 40)
>>> fit = smooth(np.sin(6.0 * t), t, basis=BSpline(n_basis=8), lam=1e-6)
>>> band = confidence_band(fit, np.array([0.25, 0.5]), sigma_e=0.01)
>>> band.stderr.shape, band.lower.shape
((2,), (2, 1))

plot

plot(ax: Axes | None = None, **kwargs: Any) -> Axes

Draw the estimate with its lower and upper limits.

Parameters:

Name Type Description Default
ax Axes

Axes to draw on. A new figure is created when omitted.

None
**kwargs Any

Passed to :meth:matplotlib.axes.Axes.plot for the estimate.

{}

Returns:

Type Description
Axes

The axes drawn on. The estimate lines come first, then the lower and the upper limits (dashed); a dotted zero line is added last when the band crosses zero.

Examples:

>>> import matplotlib
>>> matplotlib.use("Agg")
>>> import numpy as np
>>> from fdatools.stats import ConfidenceBand
>>> t = np.linspace(0.0, 1.0, 5)
>>> band = ConfidenceBand(t, t, 0.1 + 0 * t, t - 0.2, t + 0.2, 0.95)
>>> len(band.plot().lines)
4

cov

cov(x: FData, y: FData | None = None) -> BiFData

Return the sample (cross-)covariance surface.

Replaces R's var.fd. With one argument this is :meth:FData.cov. With two, it is the bivariate function

c(s, t) = 1 / (n - 1) sum_i (x_i(s) - xbar(s)) (y_i(t) - ybar(t))

expanded in x.basis along s and y.basis along t. Both are exact: centring and the outer product act on the coefficients.

Parameters:

Name Type Description Default
x FData

First set of curves.

required
y FData

Second set of curves, paired with x curve by curve.

None

Returns:

Type Description
BiFData

The covariance surface.

Raises:

Type Description
ValueError

If fewer than two curves are given, or x and y differ in their number of curves or domain, or carry several variables.

Examples:

>>> import numpy as np
>>> from fdatools import BSpline, FData
>>> from fdatools.stats import cov
>>> rng = np.random.default_rng(0)
>>> x = FData(rng.normal(size=(5, 8)), BSpline(n_basis=5))
>>> y = FData(rng.normal(size=(4, 8)), BSpline(n_basis=4))
>>> cov(x, y).coefs.shape
(5, 4)

cor

cor(
    x: FData,
    y: FData | None = None,
    *,
    s: Any = None,
    t: Any = None,
) -> Array

Return the sample correlation of x(s) with y(t) on a grid.

Replaces R's cor.fd. Entry [a, b] is the correlation, across curves, of x(s[a]) and y(t[b]); with y omitted it is the autocorrelation of x. A correlation is a ratio of covariances, not a basis expansion, so it is returned as values rather than as a :class:~fdatools.core.BiFData.

Parameters:

Name Type Description Default
x FData

First set of curves.

required
y FData

Second set of curves, paired with x. Defaults to x.

None
s array_like

Evaluation points for x and y. Each defaults to 101 equally spaced points over the domain.

None
t array_like

Evaluation points for x and y. Each defaults to 101 equally spaced points over the domain.

None

Returns:

Type Description
array

Shape (len(s), len(t)). When y is omitted and x holds several variables, each variable gets its own matrix, stacked on a trailing axis.

Raises:

Type Description
ValueError

If fewer than two curves are given, or x and y cannot be paired.

Examples:

>>> import numpy as np
>>> from fdatools import BSpline, FData
>>> from fdatools.stats import cor
>>> fd = FData(np.random.default_rng(0).normal(size=(5, 8)), BSpline(n_basis=5))
>>> grid = np.linspace(0.0, 1.0, 3)
>>> np.round(np.diag(cor(fd, s=grid, t=grid)), 12).tolist()
[1.0, 1.0, 1.0]

depth

depth(
    data: FData | Any,
    *,
    method: str = "MBD",
    t: Any = None,
    trim: float = 0.25,
) -> DepthResult

Rank curves from the centre outwards by a functional depth.

Replaces R's fdepth (type = "FM") and the depth half of fbplot. With r_i(t) the rank of x_i(t) among the n values at t (average ranks for ties):

  • "MBD" -- modified band depth (Lopez-Pintado and Romo, 2009): the share of bands spanned by pairs of curves that contain x_i(t), averaged over the points, mean_t [(r - 1)(n - r) + n - 1] / C(n, 2).
  • "BD2" -- the rank-based two-curve band depth of Sun, Genton and Nychka (2012), [(n - max_t r)(min_t r - 1) + n - 1] / C(n, 2). It equals the share of bands containing the whole curve when the curves do not cross, and is what R's fbplot(method = "BD2") reports.
  • "both" -- band depth rounded to four decimals, ties broken by modified band depth (round(1e4 BD2) + MBD), as fbplot(method = "Both").
  • "FM" -- Fraiman and Muniz (2001) depth, mean_t [1 - |1/2 - F_n(x_i(t))|] with F_n the empirical distribution function at t.

Averages over t weight every evaluation point equally.

Parameters:

Name Type Description Default
data FData or array_like

Curves, or their values as an (n_points, n_curves) array.

required
method ('MBD', 'BD2', 'both', 'FM')

Depth notion, case-insensitive. Defaults to "MBD".

"MBD"
t array_like

Evaluation points. For :class:FData they default to 101 equally spaced points over the domain; for an array they are only recorded.

None
trim float

Share of least deep curves left out of the trimmed mean: curves whose depth reaches the trim quantile are kept. Default 0.25.

0.25

Returns:

Type Description
DepthResult

Depths, the deepest curve and the trimmed mean.

Raises:

Type Description
ValueError

If method or trim is invalid, the values are not a finite two-dimensional array matching t, or a band depth is asked of fewer than two curves.

Examples:

>>> import numpy as np
>>> from fdatools.stats import depth
>>> values = np.array([[0.0, 1.0, 2.0, 3.0], [0.0, 1.0, 2.0, 3.0]])
>>> depth(values, method="FM").depth.tolist()
[0.75, 1.0, 0.75, 0.5]

boxplot

boxplot(
    data: FData | Any,
    *,
    method: str = "MBD",
    t: Any = None,
    prob: float = 0.5,
    factor: float = 1.5,
) -> BoxplotResult

Build the functional boxplot of Sun and Genton (2011).

Replaces R's fbplot and boxplot.fd. Curves are ordered by :func:depth. The central region is the pointwise envelope of the ceil(prob * n_curves) deepest curves; inflating it by factor times its height on both sides gives the fences, and a curve that leaves the fences anywhere is an outlier. The whiskers are the envelope of the curves that are not outliers.

Parameters:

Name Type Description Default
data FData or array_like

Curves, or their values as an (n_points, n_curves) array.

required
method ('MBD', 'BD2', 'both', 'FM')

Depth used to order the curves. Defaults to "MBD".

"MBD"
t array_like

Evaluation points, as for :func:depth.

None
prob float

Share of curves in the central region. Default 0.5.

0.5
factor float

Fence inflation factor. Default 1.5, as in a classical boxplot.

1.5

Returns:

Type Description
BoxplotResult

Depths, median, envelopes and outliers; call .plot() to draw it.

Raises:

Type Description
ValueError

If prob is not in (0, 1], factor is negative, or the data are invalid (see :func:depth).

Examples:

>>> import numpy as np
>>> from fdatools.stats import boxplot
>>> rng = np.random.default_rng(1)
>>> values = rng.normal(size=(20, 15))
>>> values[:, 4] += 25.0
>>> boxplot(values).outliers.tolist()
[4]

t_test

t_test(
    x1: FData,
    x2: FData,
    *,
    n_perm: int = 200,
    q: float = 0.05,
    t: Any = None,
    random_state: RandomState = None,
) -> PermutationTestResult

Permutation test for a difference between two groups of curves.

Replaces R's tperm.fd. The pointwise statistic is Welch's

T(t) = |xbar_1(t) - xbar_2(t)| / sqrt(s_1^2(t) / n_1 + s_2^2(t) / n_2)

and the test statistic is its maximum over the evaluation points. Its null distribution is obtained by pooling the curves, drawing a random permutation, and assigning the first n_1 to the first group.

Parameters:

Name Type Description Default
x1 FData

The two groups, each with at least two curves, on one domain.

required
x2 FData

The two groups, each with at least two curves, on one domain.

required
n_perm int

Number of permutations. Default 200.

200
q float

Upper-tail probability for the critical value: critical_value is the 1 - q quantile of the null distribution. Default 0.05.

0.05
t array_like

Evaluation points; 101 equally spaced points over the domain by default.

None
random_state (int, Generator or None)

Seed or generator for the permutations; each one is drawn with the generator's permutation(n).

None

Returns:

Type Description
PermutationTestResult

Observed statistic, null distribution, p-value and pointwise detail.

Raises:

Type Description
ValueError

If a group has fewer than two curves, the groups differ in domain or carry several variables, or n_perm or q is out of range.

TypeError

If random_state is not a seed or a generator.

Examples:

>>> import numpy as np
>>> from fdatools import BSpline, FData
>>> from fdatools.stats import t_test
>>> rng = np.random.default_rng(0)
>>> a = FData(rng.normal(size=(5, 8)), BSpline(n_basis=5))
>>> b = FData(rng.normal(size=(5, 7)), BSpline(n_basis=5))
>>> result = t_test(a, b, n_perm=100, random_state=0)
>>> 0.0 <= result.pvalue <= 1.0
True

f_test

f_test(
    y: FRegressResult,
    x: None = None,
    *,
    n_perm: int = 200,
    q: float = 0.05,
    t: Any = None,
    random_state: RandomState = None,
) -> PermutationTestResult
f_test(
    y: FData | Any,
    x: Any,
    *,
    basis: Basis | Sequence[Basis | None] | None = None,
    lam: float | Sequence[float] = 0.0,
    penalty: int | LDO | Sequence[int | LDO] = 2,
    n_perm: int = 200,
    q: float = 0.05,
    t: Any = None,
    random_state: RandomState = None,
) -> PermutationTestResult
f_test(
    y: FRegressResult | FData | Any,
    x: Any = None,
    *,
    basis: Basis | Sequence[Basis | None] | None = None,
    lam: float | Sequence[float] | None = None,
    penalty: int | LDO | Sequence[int | LDO] | None = None,
    n_perm: int = 200,
    q: float = 0.05,
    t: Any = None,
    random_state: RandomState = None,
) -> PermutationTestResult

Permutation F test of no effect in a functional linear regression.

Replaces R's Fperm.fd. The regression is fitted by penalised least squares, then

F(t) = var_i(yhat_i(t)) / mean_i((y_i(t) - yhat_i(t))^2)

(the variance with denominator n - 1) is evaluated pointwise and the test statistic is its maximum. The null distribution refits the model after randomly permuting the responses against fixed covariates.

The model comes either from raw inputs, as in R's Fperm.fd(yfdPar, xfdlist, betalist), or from a model fitted by :func:fdatools.regression.fregress (f_test(model)). In the raw form no intercept is added: include a vector of ones in x for one, as in R. In the model form the test refits exactly the fitted model -- its response, terms (an intercept included), coefficient bases, smoothing parameters, penalty operators and observation weights -- so x, basis, lam and penalty must not be given. Weights enter the fit only (a weighted least-squares refit under every permutation, the weights staying with the covariates); F itself is unweighted.

Two models are supported:

  • functional response (:class:FData): the concurrent model y_i(t) = sum_j x_ij(t) beta_j(t), where a scalar covariate is constant in t, every beta_j is a curve in basis[j] (default y.basis) and the penalty is lam[j] * int (L_j beta_j)^2;
  • scalar response (1-D array): y_i = sum_j int x_ij(t) beta_j(t) dt for functional covariates (beta_j in basis[j], default the covariate's basis) plus ordinary coefficients for scalar covariates, whose basis, lam and penalty entries are ignored. F is then a single number.

Parameters:

Name Type Description Default
y (FData, array_like or FRegressResult)

Response: n curves, or n numbers. Or a fitted model returned by :func:fdatools.regression.fregress, which supplies the response, the covariates and every coefficient setting.

required
x array_like, FData or sequence of them

Covariates, each a length-n vector or an :class:FData of n curves on the response domain. A single covariate need not be wrapped in a list. Required with a raw response; not allowed with a model.

None
basis Basis or list of Basis

Basis of each coefficient function: one shared value, or a list (or tuple) with one entry per covariate, None meaning the default. Raw form only.

None
lam float or list of float

Smoothing parameter of each coefficient function, shared or one per covariate. Default 0.0. Raw form only.

None
penalty int, LDO or list of them

Roughness operator of each coefficient function, shared or one per covariate. Default 2. Raw form only.

None
n_perm int

Number of permutations. Default 200.

200
q float

Upper-tail probability: critical_value is the 1 - q quantile of the null distribution. Default 0.05.

0.05
t array_like

Evaluation points for a functional response; 101 equally spaced points over the domain by default.

None
random_state (int, Generator or None)

Seed or generator; each permutation is permutation(n).

None

Returns:

Type Description
PermutationTestResult

Observed statistic, null distribution, p-value and pointwise detail (t is None for a scalar response).

Raises:

Type Description
ValueError

If the covariates, bases or settings do not match the response, the normal equations are singular, or n_perm or q is out of range.

TypeError

If random_state is not a seed or a generator, x is missing with a raw response, or x, basis, lam or penalty is given with a fitted model.

Notes

Weights differ from R. f_test(model) on a model fitted with fregress(..., weights=w) refits it by weighted least squares under every permutation, so the statistic and the null distribution change with the weights (unit weights give the unweighted test). R's Fperm.fd(..., wt = w) accepts weights but returns the same Fobs and null distribution as with no weights at all. To reproduce an R Fperm.fd result, test a model fitted without weights. The raw form f_test(y, x, ...) has no weights and matches R.

Examples:

>>> import numpy as np
>>> from fdatools import FData, Fourier
>>> from fdatools.stats import f_test
>>> rng = np.random.default_rng(0)
>>> group = np.repeat([0.0, 1.0], 6)
>>> y = FData(rng.normal(size=(5, 12)) + 2.0 * group, Fourier(n_basis=5))
>>> result = f_test(y, [np.ones(12), group], lam=1e-4, n_perm=50, random_state=0)
>>> result.pvalue < 0.05
True

The same test on a fitted model, whose intercept term is part of the model:

>>> from fdatools.regression import fregress
>>> model = fregress(y, {"const": 1.0, "group": group}, lam=1e-4)
>>> again = f_test(model, n_perm=50, random_state=0)
>>> again.statistic == result.statistic
True

confidence_band

confidence_band(
    fit: SmoothResult,
    t: Any = None,
    *,
    sigma_e: Any = None,
    level: float = 0.95,
    deriv: int = 0,
    y2c_map: Any = None,
) -> ConfidenceBand
confidence_band(
    fit: FRegressResult,
    t: Any = None,
    *,
    sigma_e: Any = None,
    level: float = 0.95,
    deriv: int = 0,
    y2c_map: Any = None,
) -> tuple[ConfidenceBand, ...]
confidence_band(
    fit: SmoothResult | FRegressResult,
    t: Any = None,
    *,
    sigma_e: Any = None,
    level: float = 0.95,
    deriv: int = 0,
    y2c_map: Any = None,
) -> ConfidenceBand | tuple[ConfidenceBand, ...]

Pointwise confidence band of a smooth or of regression coefficients.

Replaces the variance computations R users build from smooth.basis's y2cMap and from fRegress.stderr (Ramsay, Hooker and Graves, 2009, sections 5.5 and 9.4). Both fits are linear in the data, so the covariance of their coefficients follows from the error covariance Σ:

  • a smooth has coefficients c = S y (S its y2c_map) and Var x(t) = φ(t)ᵀ S Σ Sᵀ φ(t), with φ the basis (or its deriv-th derivative);
  • a regression has coefficient covariance V from :meth:~fdatools.regression.FRegressResult.stderr and Var β_j(t) = θ_j(t)ᵀ V_jj θ_j(t), with θ_j the basis of the j-th coefficient.

The band is estimate ± z · stderr with z the (1 + level) / 2 normal quantile. It is pointwise: it covers the truth at each t with probability level, not along the whole curve at once, and it ignores the bias the roughness penalty introduces.

Parameters:

Name Type Description Default
fit SmoothResult or FRegressResult

A fit from :func:~fdatools.smoothing.smooth (unconstrained, with the observation points shared by every curve) or from :func:~fdatools.regression.fregress.

required
t array

Evaluation points. Default: 101 equally spaced points over the domain (of each coefficient, for a regression).

None
sigma_e float or array

Covariance Σ of the observation errors. For a smooth: a number (σ² I), a vector of n_obs variances (a diagonal Σ, e.g. the pointwise residual variance across curves) or an (n_obs, n_obs) matrix; by default σ² I with σ² = SSE / (N (n_obs - df)) for N curves. For a regression it is passed to :meth:~fdatools.regression.FRegressResult.stderr: optional for a scalar response (default SSE / (n - df)), required for a functional one.

None
level float

Pointwise coverage, strictly between 0 and 1. Default 0.95.

0.95
deriv int

Derivative order of the band. Default 0.

0
y2c_map array

Regression only: data-to-coefficient map of the response smooth, passed to :meth:~fdatools.regression.FRegressResult.stderr.

None

Returns:

Type Description
ConfidenceBand or tuple of ConfidenceBand

One band for a smooth; one band per term, in model order, for a regression.

Raises:

Type Description
ValueError

If level or sigma_e is invalid, the smooth is constrained or irregular, y2c_map is given for a smooth, or a functional-response regression lacks sigma_e.

TypeError

If fit is neither a smooth nor a regression, or deriv is not an integer.

Examples:

>>> import numpy as np
>>> from fdatools.regression import fregress
>>> from fdatools.stats import confidence_band
>>> z = np.linspace(-1.0, 1.0, 30)
>>> model = fregress(1.0 + 2.0 * z + 0.1 * np.cos(9.0 * z), [1.0, z])
>>> const, slope = confidence_band(model)
>>> slope.name, bool(np.all(slope.lower < 2.0) and np.all(slope.upper > 2.0))
('x1', True)

plot_beta

plot_beta(
    fit: FRegressResult
    | ConfidenceBand
    | Sequence[ConfidenceBand],
    t: Any = None,
    *,
    level: float = 0.95,
    sigma_e: Any = None,
    y2c_map: Any = None,
    axes: Sequence[Axes] | None = None,
    **kwargs: Any,
) -> list[Axes]

Plot regression coefficients with their pointwise confidence limits.

Replaces R's plotbeta: one panel per coefficient, the estimate as a solid line, the limits dashed, and a dotted zero line when the band crosses zero (R's zerofind).

Parameters:

Name Type Description Default
fit FRegressResult, ConfidenceBand or sequence of ConfidenceBand

A fitted regression (its bands are computed with :func:confidence_band), or bands computed already.

required
t array

Evaluation points, used when fit is a regression.

None
level float

Pointwise coverage, used when fit is a regression. Default 0.95.

0.95
sigma_e optional

Passed to :func:confidence_band when fit is a regression.

None
y2c_map optional

Passed to :func:confidence_band when fit is a regression.

None
axes sequence of matplotlib.axes.Axes

One axes per band. A new figure with stacked panels when omitted.

None
**kwargs Any

Passed to :meth:matplotlib.axes.Axes.plot for the estimates.

{}

Returns:

Type Description
list of matplotlib.axes.Axes

The axes drawn on, one per band, each titled with its term name.

Raises:

Type Description
ValueError

If axes does not hold one axes per band.

Examples:

>>> import matplotlib
>>> matplotlib.use("Agg")
>>> import numpy as np
>>> from fdatools.regression import fregress
>>> from fdatools.stats import plot_beta
>>> z = np.linspace(-1.0, 1.0, 30)
>>> model = fregress(z + 0.1 * np.cos(9.0 * z), [1.0, z])
>>> [ax.get_title() for ax in plot_beta(model)]
['x0', 'x1']

cycleplot

cycleplot(
    fd: FData,
    y: FData | None = None,
    *,
    ax: Axes | None = None,
    n_points: int = 201,
    **kwargs: Any,
) -> Axes

Plot the cycles of a bivariate functional observation (R's cycleplot.fd).

Each curve traces (x(t), y(t)) over its domain, so a periodic pair -- hip against knee angle over a gait cycle, temperature against precipitation over a year -- draws one closed loop per curve. To see the cycles one at a time, pass fd[i].

Parameters:

Name Type Description Default
fd FData

Curves with two variables, or the first coordinate when y is given.

required
y FData

The second coordinate, with one variable and as many curves as fd.

None
ax Axes

Axes to draw on. A new figure is created when omitted.

None
n_points int

Size of the evaluation grid over the domain. Default 201, as in R.

201
**kwargs Any

Passed to :meth:matplotlib.axes.Axes.plot.

{}

Returns:

Type Description
Axes

The axes drawn on, with one line per curve.

Raises:

Type Description
ValueError

If the curves are not bivariate, fd and y disagree in their number of curves or domain, or n_points < 2.

Examples:

>>> import matplotlib
>>> matplotlib.use("Agg")
>>> import numpy as np
>>> from fdatools import FData, Fourier
>>> from fdatools.stats import cycleplot
>>> basis = Fourier(domain=(0.0, 1.0), n_basis=3)
>>> coefs = np.zeros((3, 2, 2))
>>> coefs[1, :, 0] = coefs[2, :, 1] = 1.0
>>> len(cycleplot(FData(coefs, basis)).lines)
2

plot_scores

plot_scores(
    scores: Any,
    components: tuple[int, int] = (0, 1),
    *,
    ax: Axes | None = None,
    labels: Sequence[Any] | None = None,
    **kwargs: Any,
) -> Axes

Scatter two principal component scores against each other (R's plotscores).

Parameters:

Name Type Description Default
scores FPCA or array

A fitted :class:~fdatools.decomposition.FPCA (its scores are used, and its varprop labels the axes) or a score matrix of shape (n_curves, n_components).

required
components tuple of int

Zero-based indices of the two components. Default (0, 1).

(0, 1)
ax Axes

Axes to draw on. A new figure is created when omitted.

None
labels sequence

One label per curve, written next to its point.

None
**kwargs Any

Passed to :meth:matplotlib.axes.Axes.scatter.

{}

Returns:

Type Description
Axes

The axes drawn on; the points are its single collection.

Raises:

Type Description
ValueError

If the scores are not a matrix, a component index is out of range, or labels has the wrong length.

Examples:

>>> import matplotlib
>>> matplotlib.use("Agg")
>>> import numpy as np
>>> from fdatools.stats import plot_scores
>>> ax = plot_scores(np.array([[1.0, 2.0], [3.0, -1.0], [0.0, 0.5]]))
>>> ax.collections[0].get_offsets().shape
(3, 2)