fdatools 1.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fdatools/__init__.py +107 -0
- fdatools/_backend.py +219 -0
- fdatools/_data/gait.json +1 -0
- fdatools/_data/gait.npz +0 -0
- fdatools/_data/growth.json +1 -0
- fdatools/_data/growth.npz +0 -0
- fdatools/_data/pinch.json +1 -0
- fdatools/_data/pinch.npz +0 -0
- fdatools/_internal/__init__.py +4 -0
- fdatools/_internal/registration_torch.py +430 -0
- fdatools/_linalg.py +531 -0
- fdatools/_operator.py +206 -0
- fdatools/_plot.py +224 -0
- fdatools/basis.py +1663 -0
- fdatools/core.py +999 -0
- fdatools/datasets.py +834 -0
- fdatools/decomposition.py +996 -0
- fdatools/density.py +782 -0
- fdatools/dynamics.py +1245 -0
- fdatools/io.py +325 -0
- fdatools/nn.py +425 -0
- fdatools/profiling.py +1931 -0
- fdatools/py.typed +0 -0
- fdatools/registration.py +1534 -0
- fdatools/regression.py +1743 -0
- fdatools/smoothing.py +1176 -0
- fdatools/sparse.py +958 -0
- fdatools/stats.py +1896 -0
- fdatools-1.1.0.dist-info/METADATA +127 -0
- fdatools-1.1.0.dist-info/RECORD +32 -0
- fdatools-1.1.0.dist-info/WHEEL +4 -0
- fdatools-1.1.0.dist-info/licenses/LICENSE +29 -0
fdatools/__init__.py
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"""fdatools: functional data analysis for Python.
|
|
2
|
+
|
|
3
|
+
A clean-room, production-grade Python implementation of the functional data
|
|
4
|
+
analysis toolkit described in Ramsay & Silverman, *Functional Data Analysis*.
|
|
5
|
+
|
|
6
|
+
Examples
|
|
7
|
+
--------
|
|
8
|
+
>>> import numpy as np
|
|
9
|
+
>>> import fdatools as fdt
|
|
10
|
+
>>> basis = fdt.BSpline(domain=(0.0, 1.0), n_basis=5)
|
|
11
|
+
>>> basis(np.array([0.0, 0.5, 1.0])).shape
|
|
12
|
+
(3, 5)
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from typing import TYPE_CHECKING, Any
|
|
16
|
+
|
|
17
|
+
from fdatools import datasets, density, profiling, sparse, stats
|
|
18
|
+
from fdatools.basis import (
|
|
19
|
+
Basis,
|
|
20
|
+
BSpline,
|
|
21
|
+
Constant,
|
|
22
|
+
Exponential,
|
|
23
|
+
Fourier,
|
|
24
|
+
Monomial,
|
|
25
|
+
Polygonal,
|
|
26
|
+
Power,
|
|
27
|
+
)
|
|
28
|
+
from fdatools.core import LDO, BiFData, FData, inprod
|
|
29
|
+
from fdatools.decomposition import FCCA, FPCA
|
|
30
|
+
from fdatools.density import DensityResult, IntensityResult, fit_density, fit_intensity
|
|
31
|
+
from fdatools.dynamics import PDA, PDAStability, phase_plane
|
|
32
|
+
from fdatools.io import from_pandas, read_rds, to_pandas, to_xarray
|
|
33
|
+
from fdatools.profiling import ODEModel, ProfiledODE, profile_ode
|
|
34
|
+
from fdatools.registration import Registrator, landmark_register, register
|
|
35
|
+
from fdatools.regression import FRegress, LinmodResult, fregress, linmod
|
|
36
|
+
from fdatools.smoothing import Smoother, SmoothResult, smooth
|
|
37
|
+
from fdatools.sparse import PACE, SparseCov, sparse_cov, sparse_mean
|
|
38
|
+
|
|
39
|
+
if TYPE_CHECKING:
|
|
40
|
+
from fdatools import nn as nn
|
|
41
|
+
|
|
42
|
+
__all__ = [
|
|
43
|
+
"FCCA",
|
|
44
|
+
"FPCA",
|
|
45
|
+
"LDO",
|
|
46
|
+
"PACE",
|
|
47
|
+
"PDA",
|
|
48
|
+
"BSpline",
|
|
49
|
+
"Basis",
|
|
50
|
+
"BiFData",
|
|
51
|
+
"Constant",
|
|
52
|
+
"DensityResult",
|
|
53
|
+
"Exponential",
|
|
54
|
+
"FData",
|
|
55
|
+
"FRegress",
|
|
56
|
+
"Fourier",
|
|
57
|
+
"IntensityResult",
|
|
58
|
+
"LinmodResult",
|
|
59
|
+
"Monomial",
|
|
60
|
+
"ODEModel",
|
|
61
|
+
"PDAStability",
|
|
62
|
+
"Polygonal",
|
|
63
|
+
"Power",
|
|
64
|
+
"ProfiledODE",
|
|
65
|
+
"Registrator",
|
|
66
|
+
"SmoothResult",
|
|
67
|
+
"Smoother",
|
|
68
|
+
"SparseCov",
|
|
69
|
+
"__version__",
|
|
70
|
+
"datasets",
|
|
71
|
+
"density",
|
|
72
|
+
"fit_density",
|
|
73
|
+
"fit_intensity",
|
|
74
|
+
"fregress",
|
|
75
|
+
"from_pandas",
|
|
76
|
+
"inprod",
|
|
77
|
+
"landmark_register",
|
|
78
|
+
"linmod",
|
|
79
|
+
"phase_plane",
|
|
80
|
+
"profile_ode",
|
|
81
|
+
"profiling",
|
|
82
|
+
"read_rds",
|
|
83
|
+
"register",
|
|
84
|
+
"smooth",
|
|
85
|
+
"sparse",
|
|
86
|
+
"sparse_cov",
|
|
87
|
+
"sparse_mean",
|
|
88
|
+
"stats",
|
|
89
|
+
"to_pandas",
|
|
90
|
+
"to_xarray",
|
|
91
|
+
]
|
|
92
|
+
|
|
93
|
+
__version__ = "1.1.0"
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def __getattr__(name: str) -> Any:
|
|
97
|
+
"""Import the optional :mod:`fdatools.nn` module on first access.
|
|
98
|
+
|
|
99
|
+
``import fdatools`` never imports PyTorch; ``fdatools.nn`` loads it lazily.
|
|
100
|
+
"""
|
|
101
|
+
if name == "nn":
|
|
102
|
+
import importlib
|
|
103
|
+
|
|
104
|
+
module = importlib.import_module("fdatools.nn")
|
|
105
|
+
globals()["nn"] = module
|
|
106
|
+
return module
|
|
107
|
+
raise AttributeError(f"module 'fdatools' has no attribute {name!r}")
|
fdatools/_backend.py
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
"""Array-API dispatch for fdatools.
|
|
2
|
+
|
|
3
|
+
This is the *only* module (together with :mod:`fdatools._linalg`) that is allowed to
|
|
4
|
+
import NumPy directly. Every other module obtains its array namespace through
|
|
5
|
+
:func:`array_namespace` and calls ``xp.*`` functions, so that a NumPy input
|
|
6
|
+
produces a NumPy output and a PyTorch input produces a PyTorch output with
|
|
7
|
+
gradients flowing through.
|
|
8
|
+
|
|
9
|
+
Examples
|
|
10
|
+
--------
|
|
11
|
+
>>> import numpy as np
|
|
12
|
+
>>> from fdatools import _backend as be
|
|
13
|
+
>>> xp = be.array_namespace(np.zeros(3))
|
|
14
|
+
>>> be.asarray([1, 2, 3]).dtype
|
|
15
|
+
dtype('float64')
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
from types import ModuleType
|
|
21
|
+
from typing import Any, cast
|
|
22
|
+
|
|
23
|
+
import array_api_compat
|
|
24
|
+
import numpy as np
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
"array_namespace",
|
|
28
|
+
"asarray",
|
|
29
|
+
"default_namespace",
|
|
30
|
+
"is_torch",
|
|
31
|
+
"result_namespace",
|
|
32
|
+
"to_numpy",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
_DEFAULT_NAMESPACE: ModuleType = array_api_compat.array_namespace(np.empty(0))
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def default_namespace() -> ModuleType:
|
|
39
|
+
"""Return the fallback array namespace (NumPy).
|
|
40
|
+
|
|
41
|
+
Returns
|
|
42
|
+
-------
|
|
43
|
+
module
|
|
44
|
+
The array-API compatible NumPy namespace.
|
|
45
|
+
|
|
46
|
+
Examples
|
|
47
|
+
--------
|
|
48
|
+
>>> from fdatools._backend import default_namespace
|
|
49
|
+
>>> default_namespace().__name__.endswith("numpy")
|
|
50
|
+
True
|
|
51
|
+
"""
|
|
52
|
+
return _DEFAULT_NAMESPACE
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def array_namespace(*xs: Any) -> ModuleType:
|
|
56
|
+
"""Return the array-API namespace shared by ``xs``.
|
|
57
|
+
|
|
58
|
+
Python scalars, ``None`` and plain sequences are ignored; if no argument is an
|
|
59
|
+
array the NumPy namespace is returned. If any argument is a
|
|
60
|
+
:class:`torch.Tensor` the PyTorch namespace is returned, so tensors in give
|
|
61
|
+
tensors out.
|
|
62
|
+
|
|
63
|
+
Parameters
|
|
64
|
+
----------
|
|
65
|
+
*xs : object
|
|
66
|
+
Candidate arrays, scalars or ``None``.
|
|
67
|
+
|
|
68
|
+
Returns
|
|
69
|
+
-------
|
|
70
|
+
module
|
|
71
|
+
An array-API compatible namespace.
|
|
72
|
+
|
|
73
|
+
Raises
|
|
74
|
+
------
|
|
75
|
+
TypeError
|
|
76
|
+
If ``xs`` mixes arrays from two different array libraries.
|
|
77
|
+
|
|
78
|
+
Examples
|
|
79
|
+
--------
|
|
80
|
+
>>> import numpy as np
|
|
81
|
+
>>> from fdatools._backend import array_namespace
|
|
82
|
+
>>> array_namespace(np.zeros(2), 1.0).__name__.endswith("numpy")
|
|
83
|
+
True
|
|
84
|
+
"""
|
|
85
|
+
arrays = [x for x in xs if array_api_compat.is_array_api_obj(x)]
|
|
86
|
+
if not arrays:
|
|
87
|
+
return _DEFAULT_NAMESPACE
|
|
88
|
+
namespace: ModuleType = array_api_compat.array_namespace(*arrays)
|
|
89
|
+
return namespace
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def result_namespace(*xs: Any) -> ModuleType:
|
|
93
|
+
"""Return the namespace an operation over ``xs`` should compute in.
|
|
94
|
+
|
|
95
|
+
Unlike :func:`array_namespace`, a NumPy operand does not conflict with a
|
|
96
|
+
third-party one: NumPy data is promotable, so mixing NumPy coefficients with
|
|
97
|
+
a PyTorch argument gives the PyTorch namespace (and a differentiable
|
|
98
|
+
result). Two different non-NumPy libraries still conflict.
|
|
99
|
+
|
|
100
|
+
Parameters
|
|
101
|
+
----------
|
|
102
|
+
*xs : object
|
|
103
|
+
Candidate arrays, scalars or ``None``.
|
|
104
|
+
|
|
105
|
+
Returns
|
|
106
|
+
-------
|
|
107
|
+
module
|
|
108
|
+
An array-API compatible namespace.
|
|
109
|
+
|
|
110
|
+
Raises
|
|
111
|
+
------
|
|
112
|
+
TypeError
|
|
113
|
+
If ``xs`` mixes arrays from two different non-NumPy libraries.
|
|
114
|
+
|
|
115
|
+
Examples
|
|
116
|
+
--------
|
|
117
|
+
>>> import numpy as np
|
|
118
|
+
>>> from fdatools._backend import result_namespace
|
|
119
|
+
>>> result_namespace(np.zeros(2), None).__name__.endswith("numpy")
|
|
120
|
+
True
|
|
121
|
+
"""
|
|
122
|
+
spaces = [
|
|
123
|
+
array_api_compat.array_namespace(x) for x in xs if array_api_compat.is_array_api_obj(x)
|
|
124
|
+
]
|
|
125
|
+
foreign = [space for space in spaces if space is not _DEFAULT_NAMESPACE]
|
|
126
|
+
if not foreign:
|
|
127
|
+
return _DEFAULT_NAMESPACE
|
|
128
|
+
if any(space is not foreign[0] for space in foreign[1:]):
|
|
129
|
+
raise TypeError(f"cannot mix array namespaces {sorted({s.__name__ for s in foreign})}")
|
|
130
|
+
return cast("ModuleType", foreign[0])
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def asarray(x: Any, xp: ModuleType | None = None, dtype: Any = float) -> Any:
|
|
134
|
+
"""Convert ``x`` to an array in namespace ``xp``.
|
|
135
|
+
|
|
136
|
+
Parameters
|
|
137
|
+
----------
|
|
138
|
+
x : array_like
|
|
139
|
+
Value to convert. Arrays are returned unchanged when the namespace and
|
|
140
|
+
dtype already match, so autograd graphs are preserved.
|
|
141
|
+
xp : module, optional
|
|
142
|
+
Target namespace. Defaults to the namespace of ``x``.
|
|
143
|
+
dtype : dtype-like, optional
|
|
144
|
+
Target dtype. The default, the builtin :class:`float`, maps to the
|
|
145
|
+
namespace's ``float64``. Pass ``None`` to keep ``x``'s dtype.
|
|
146
|
+
|
|
147
|
+
Returns
|
|
148
|
+
-------
|
|
149
|
+
array
|
|
150
|
+
``x`` as an array of namespace ``xp``.
|
|
151
|
+
|
|
152
|
+
Examples
|
|
153
|
+
--------
|
|
154
|
+
>>> from fdatools._backend import asarray
|
|
155
|
+
>>> asarray([1, 2, 3]).dtype
|
|
156
|
+
dtype('float64')
|
|
157
|
+
"""
|
|
158
|
+
if xp is None:
|
|
159
|
+
xp = array_namespace(x)
|
|
160
|
+
resolved = xp.float64 if dtype is float else dtype
|
|
161
|
+
if array_api_compat.is_array_api_obj(x) and array_namespace(x) is xp:
|
|
162
|
+
if resolved is None or x.dtype == resolved:
|
|
163
|
+
return x
|
|
164
|
+
return xp.astype(x, resolved)
|
|
165
|
+
if isinstance(x, np.ndarray) and not x.flags.writeable:
|
|
166
|
+
x = np.array(x)
|
|
167
|
+
if resolved is None:
|
|
168
|
+
return xp.asarray(x)
|
|
169
|
+
return xp.asarray(x, dtype=resolved)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def to_numpy(x: Any) -> np.ndarray[Any, np.dtype[Any]]:
|
|
173
|
+
"""Convert ``x`` to a NumPy array, detaching autograd tensors if needed.
|
|
174
|
+
|
|
175
|
+
Parameters
|
|
176
|
+
----------
|
|
177
|
+
x : array_like
|
|
178
|
+
Array from any supported backend, or anything :func:`numpy.asarray`
|
|
179
|
+
accepts.
|
|
180
|
+
|
|
181
|
+
Returns
|
|
182
|
+
-------
|
|
183
|
+
numpy.ndarray
|
|
184
|
+
A NumPy view or copy of ``x``.
|
|
185
|
+
|
|
186
|
+
Examples
|
|
187
|
+
--------
|
|
188
|
+
>>> from fdatools._backend import to_numpy
|
|
189
|
+
>>> to_numpy([1.0, 2.0]).tolist()
|
|
190
|
+
[1.0, 2.0]
|
|
191
|
+
"""
|
|
192
|
+
if array_api_compat.is_torch_array(x):
|
|
193
|
+
return np.asarray(x.detach().cpu().numpy())
|
|
194
|
+
return np.asarray(x)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def is_torch(x: Any) -> bool:
|
|
198
|
+
"""Return whether ``x`` is a PyTorch tensor.
|
|
199
|
+
|
|
200
|
+
Does not import PyTorch if it has not been imported already.
|
|
201
|
+
|
|
202
|
+
Parameters
|
|
203
|
+
----------
|
|
204
|
+
x : object
|
|
205
|
+
Candidate value.
|
|
206
|
+
|
|
207
|
+
Returns
|
|
208
|
+
-------
|
|
209
|
+
bool
|
|
210
|
+
``True`` if ``x`` is a :class:`torch.Tensor`.
|
|
211
|
+
|
|
212
|
+
Examples
|
|
213
|
+
--------
|
|
214
|
+
>>> import numpy as np
|
|
215
|
+
>>> from fdatools._backend import is_torch
|
|
216
|
+
>>> is_torch(np.zeros(2))
|
|
217
|
+
False
|
|
218
|
+
"""
|
|
219
|
+
return bool(array_api_compat.is_torch_array(x))
|
fdatools/_data/gait.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"subjects": ["boy1", "boy2", "boy3", "boy4", "boy5", "boy6", "boy7", "boy8", "boy9", "boy10", "boy11", "boy12", "boy13", "boy14", "boy15", "boy16", "boy17", "boy18", "boy19", "boy20", "boy21", "boy22", "boy23", "boy24", "boy25", "boy26", "boy27", "boy28", "boy29", "boy30", "boy31", "boy32", "boy33", "boy34", "boy35", "boy36", "boy37", "boy38", "boy39"], "variables": ["Hip Angle", "Knee Angle"]}
|
fdatools/_data/gait.npz
ADDED
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
fdatools/_data/pinch.npz
ADDED
|
Binary file
|