fdatools 1.1.0__tar.gz

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.
Files changed (35) hide show
  1. fdatools-1.1.0/.gitignore +33 -0
  2. fdatools-1.1.0/CHANGELOG.md +160 -0
  3. fdatools-1.1.0/CITATION.cff +59 -0
  4. fdatools-1.1.0/LICENSE +29 -0
  5. fdatools-1.1.0/PKG-INFO +127 -0
  6. fdatools-1.1.0/README.md +65 -0
  7. fdatools-1.1.0/pyproject.toml +181 -0
  8. fdatools-1.1.0/src/fdatools/__init__.py +107 -0
  9. fdatools-1.1.0/src/fdatools/_backend.py +219 -0
  10. fdatools-1.1.0/src/fdatools/_data/gait.json +1 -0
  11. fdatools-1.1.0/src/fdatools/_data/gait.npz +0 -0
  12. fdatools-1.1.0/src/fdatools/_data/growth.json +1 -0
  13. fdatools-1.1.0/src/fdatools/_data/growth.npz +0 -0
  14. fdatools-1.1.0/src/fdatools/_data/pinch.json +1 -0
  15. fdatools-1.1.0/src/fdatools/_data/pinch.npz +0 -0
  16. fdatools-1.1.0/src/fdatools/_internal/__init__.py +4 -0
  17. fdatools-1.1.0/src/fdatools/_internal/registration_torch.py +430 -0
  18. fdatools-1.1.0/src/fdatools/_linalg.py +531 -0
  19. fdatools-1.1.0/src/fdatools/_operator.py +206 -0
  20. fdatools-1.1.0/src/fdatools/_plot.py +224 -0
  21. fdatools-1.1.0/src/fdatools/basis.py +1663 -0
  22. fdatools-1.1.0/src/fdatools/core.py +999 -0
  23. fdatools-1.1.0/src/fdatools/datasets.py +834 -0
  24. fdatools-1.1.0/src/fdatools/decomposition.py +996 -0
  25. fdatools-1.1.0/src/fdatools/density.py +782 -0
  26. fdatools-1.1.0/src/fdatools/dynamics.py +1245 -0
  27. fdatools-1.1.0/src/fdatools/io.py +325 -0
  28. fdatools-1.1.0/src/fdatools/nn.py +425 -0
  29. fdatools-1.1.0/src/fdatools/profiling.py +1931 -0
  30. fdatools-1.1.0/src/fdatools/py.typed +0 -0
  31. fdatools-1.1.0/src/fdatools/registration.py +1534 -0
  32. fdatools-1.1.0/src/fdatools/regression.py +1743 -0
  33. fdatools-1.1.0/src/fdatools/smoothing.py +1176 -0
  34. fdatools-1.1.0/src/fdatools/sparse.py +958 -0
  35. fdatools-1.1.0/src/fdatools/stats.py +1896 -0
@@ -0,0 +1,33 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv/
4
+ dist/
5
+ build/
6
+ *.egg-info/
7
+ .pytest_cache/
8
+ .mypy_cache/
9
+ .ruff_cache/
10
+ .hypothesis/
11
+ .coverage
12
+ coverage.xml
13
+ htmlcov/
14
+ .benchmarks/
15
+ site/
16
+ .ipynb_checkpoints/
17
+ .env
18
+ .DS_Store
19
+ ~/.cache/
20
+ notebooks/_figures/
21
+
22
+ # Full-precision dataset dumps for the datasets agent (generated by
23
+ # tools/export_datasets.R); not committed -- large, regenerable, not golden
24
+ # test data.
25
+ data_export/
26
+
27
+ # Staging area for the GitHub "data-v1" release assets (generated by
28
+ # tools/build_data_release.py from data_export/); not committed -- see
29
+ # docs/dev/data-release.md for the human upload step.
30
+ data_release/
31
+
32
+ # Local knowledge graph of the repo (graphify); regenerable.
33
+ graphify-out/
@@ -0,0 +1,160 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. Format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow SemVer.
5
+
6
+ ## [1.1.0] - 2026-09-28
7
+
8
+ First release under the name **fdatools** (the first one on PyPI).
9
+
10
+ ### Changed
11
+ - The project is renamed from **fabel** to **fdatools**: `pip install fdatools`,
12
+ `import fdatools as fdt`. The GitHub repository moves to
13
+ `hameddavodi/fdatools` and the documentation to
14
+ <https://hameddavodi.github.io/fdatools>. The environment variables
15
+ `FABEL_DATA_DIR` / `FABEL_RUN_NETWORK_TESTS` become `FDATOOLS_DATA_DIR` /
16
+ `FDATOOLS_RUN_NETWORK_TESTS`, and the dataset cache moves to
17
+ `~/.cache/fdatools`. The 1.0.0 entry below describes the release published
18
+ under the old name.
19
+ - `rpy2` moved from the `dev` extra to a new `golden` extra (only
20
+ `tools/make_golden.py` uses it), so the development install no longer needs R.
21
+
22
+ ### Fixed
23
+ - `PACE(sigma2=...)` no longer raises a `RuntimeWarning` about a non-positive
24
+ measurement-error estimate: with `sigma2` given, the estimate is kept in
25
+ `cov_estimate_` but not used, so it is not worth a warning.
26
+ - `FPCA`, `FCCA` and `PACE` reject a NumPy bool for `n` on every NumPy version
27
+ (NumPy 2.2 only warned in `operator.index`).
28
+
29
+ ### Added
30
+ - `notebooks/tour.ipynb`: a full tour that uses every public module on the
31
+ bundled data sets, records 131 checks, and compares key results live with R
32
+ `fda` (through `Rscript`). Built from `notebooks/tour.py` by
33
+ `tools/build_tour_notebook.py`.
34
+
35
+ ## [1.0.0] - 2026-09-27
36
+
37
+ First public release: a clean-room Python rewrite of R `fda` 6.3.0 with
38
+ golden-file parity (`rtol = 1e-8`, `1e-5` for iterative fits).
39
+
40
+ ### Added
41
+ - Basis systems (`fabel.basis`): `BSpline`, `Fourier`, `Monomial`, `Exponential`,
42
+ `Power`, `Constant`, `Polygonal`, with evaluation and derivatives, roughness
43
+ penalties for any `LDO`, cached Gram matrices and exact basis products.
44
+ - Core objects (`fabel.core`): `FData` (callable curves, exact derivatives,
45
+ arithmetic, `mean` / `std` / `center` / `cov`, indexing, `@` inner product),
46
+ `BiFData`, `LDO` (including the harmonic accelerator) and `inprod`.
47
+ - Smoothing (`fabel.smoothing`): `smooth()` with GCV or degrees-of-freedom
48
+ selection of λ, positive / monotone / morph constraints, observation weights
49
+ and irregular per-curve designs; `SmoothResult`; the scikit-learn `Smoother`;
50
+ `gcv_curve`, `lambda_to_df`, `df_to_lambda`.
51
+ - Decomposition (`fabel.decomposition`): `FPCA` (with roughness-penalised
52
+ harmonics and varimax rotation) and `FCCA`, both scikit-learn estimators.
53
+ - Regression (`fabel.regression`): `fregress()` for scalar and functional
54
+ responses with scalar and functional covariates (model type read from the
55
+ arguments, or an R-style formula string with treatment-coded factors), with
56
+ `predict()`, `stderr()` and `cv()`; the scikit-learn `FRegress` estimator.
57
+ - Registration (`fabel.registration`): continuous registration `register()`
58
+ (Newton with the exact Hessian, optional periodic shift), landmark registration
59
+ (`landmarks=` or `landmark_register()`), the amplitude/phase decomposition
60
+ `RegistrationResult.decompose()` and the scikit-learn `Registrator`.
61
+ - Dynamics (`fabel.dynamics`): principal differential analysis `PDA` for single
62
+ equations and coupled systems, with an ODE `solve()` and `plot_overlay()`, and
63
+ the `phase_plane()` plot.
64
+ - Statistics (`fabel.stats`): `cov`, `cor`, functional depth (MBD, BD2, FM),
65
+ the functional `boxplot`, and the permutation tests `t_test` and `f_test`.
66
+ `f_test` takes either raw inputs `(y, x, basis=, lam=, penalty=)` like R
67
+ `Fperm.fd`, or a fitted `fregress` model: `f_test(model, n_perm=, q=, t=,
68
+ random_state=)`.
69
+ - scikit-learn: `check_estimator` passes for `Smoother`, `FPCA`, `FRegress` and
70
+ `Registrator` with no exemptions. `FRegress` validates `y` as scikit-learn does
71
+ (finite, column vectors flattened with a warning, at least 2 samples).
72
+ `Registrator` has `n_iter_`, and its default basis for an n-column coefficient
73
+ matrix is `BSpline(n_basis=n, order=min(4, n))`, the same rule as `FPCA`.
74
+ - PyTorch layers (`fabel.nn`, optional `fabel[torch]` extra): `BasisLayer`,
75
+ `SmoothingLayer` (learnable λ) and `FDataDataset`. `import fabel` does not
76
+ import PyTorch; `fabel.nn` loads on first use.
77
+ - Top-level exports: `smooth`, `Smoother`, `SmoothResult`, `FPCA`, `FCCA`,
78
+ `fregress`, `FRegress`, `register`, `landmark_register`, `Registrator`, `PDA`,
79
+ `phase_plane`, and the `stats` module.
80
+ - Datasets (`fabel.datasets`): 14 loaders for the FDA book datasets; `growth`,
81
+ `gait` and `pinch` ship in the package, the others download once with SHA-256
82
+ verification and a `FABEL_DATA_DIR`-overridable cache.
83
+ - I/O (`fabel.io`): `from_pandas`, `to_pandas`, `to_xarray` and `read_rds` for R
84
+ `fd` / `bifd` / `basisfd` objects.
85
+ - Array API backend: NumPy and PyTorch inputs, with gradients flowing through
86
+ evaluation, products and inner products.
87
+ - Golden-file parity suite against R `fda` 6.3.0, and `tools/parity_report.py`,
88
+ which measures every parity check and writes `PARITY_REPORT.md`.
89
+ - Documentation site (MkDocs Material + mkdocstrings): quickstart, R migration
90
+ table, API reference for every module, and six tutorials (smoothing, FPCA,
91
+ registration, regression, dynamics, machine learning). Tests run every
92
+ tutorial block and check that every public symbol is rendered.
93
+ - `notebooks/book_figures.ipynb`: figures of Ramsay, Hooker & Graves (2009),
94
+ assembled from `notebooks/book/ch*.py` by `tools/build_book_notebook.py`
95
+ (`make book`) and run in CI with `nbmake`.
96
+ - Packaging: typed (`py.typed`) wheel and sdist for Python 3.10 to 3.13,
97
+ BSD-3-Clause license, `CITATION.cff`.
98
+ - `register()` accepts an `FData` with PyTorch coefficients and returns PyTorch
99
+ results; gradients flow from the registered curves to the input coefficients
100
+ (autodiff Newton path; the optimal warps are held fixed).
101
+ - Build: the Makefile uses the `.venv` Python and has new `sync` and `gate5`
102
+ targets.
103
+ - Sparse / longitudinal FPCA (`fabel.sparse`, PACE): `sparse_mean`
104
+ (R `smooth.sparse.mean`), `sparse_cov` / `SparseCov` (R `covPACE`, with the
105
+ measurement-error variance `sigma2`), and the `PACE` estimator (R `pcaPACE`)
106
+ with conditional-expectation (BLUP) scores in `transform` (replaces R's
107
+ defective `scoresPACE`) and `inverse_transform`.
108
+ - Density and intensity estimation (`fabel.density`): `fit_density` (R
109
+ `density.fd`, no longer shipped in fda 6.3.0) and `fit_intensity` (R
110
+ `intensity.fd`), damped Newton with the exact Hessian and exact integrals;
111
+ `DensityResult`, `IntensityResult`.
112
+ - Generalized profiling for ODE parameters (`fabel.profiling`, replaces the R
113
+ CSTR family): `ODEModel` (analytic, finite-difference or torch-autodiff
114
+ derivatives), `ProfiledODE`, `profile_ode`, `ProfileResult`, built-in
115
+ `cstr_model` / `cstr_inputs` and `fitzhugh_nagumo_model`, and `simpson_rule`
116
+ (R `quadset`). Unobserved states are allowed.
117
+ - Regression: `linmod()` / `LinmodResult` for a functional response on a
118
+ functional covariate with a bivariate coefficient beta(s, t) (R `linmod`),
119
+ with weights and a `predict()` method. `fregress` now computes in the input's
120
+ array namespace: torch tensors in give torch results, with gradients to the
121
+ response, covariates and weights.
122
+ - Registration: `register()` accepts multivariate curves (one warp per curve,
123
+ new `var_weights=` keyword; `var_weights=[1, 0, ...]` reproduces R, which uses
124
+ the first variable only); landmark registration and `decompose()` accept
125
+ multivariate curves; new `RegistrationResult.apply(fd)` (R `register.newfd`).
126
+ - Dynamics: `PDA` forcing functions (`forcing_basis=`, `forcing_lam=`,
127
+ `fit(X, forcing=u)`, `forcing_weights_`; R `pda.fd` `awtlist` / `ufdlist`) and
128
+ `PDA.stability()` returning `PDAStability` (R `eigen.pda`, with the true
129
+ equilibrium limits).
130
+ - Smoothing: monotone, positive and morph `SmoothResult`s evaluate exact
131
+ derivatives of any order (Faà di Bruno / complete Bell polynomials; R
132
+ `eval.monfd`, `eval.posfd`, `predict.monfd`).
133
+ - Statistics: pointwise `confidence_band()` / `ConfidenceBand` for a smooth
134
+ (via `y2c_map`) or an `fregress` result (via `stderr`), and the plots
135
+ `plot_beta` (R `plotbeta`), `cycleplot` (R `cycleplot.fd`) and `plot_scores`
136
+ (R `plotscores`).
137
+ - Top-level exports added: `PACE`, `SparseCov`, `sparse_mean`, `sparse_cov`,
138
+ `fit_density`, `fit_intensity`, `DensityResult`, `IntensityResult`,
139
+ `ODEModel`, `ProfiledODE`, `profile_ode`, `linmod`, `LinmodResult`,
140
+ `PDAStability`, and the `sparse`, `density` and `profiling` modules.
141
+
142
+ ### Fixed
143
+ - `register(lam=0, criterion='eigen')` no longer raises `LinAlgError`: it warns
144
+ (`RuntimeWarning`) when a curve has no finite optimum, and raises `ValueError`
145
+ on NaN or infinite input.
146
+ - `FPCA` and `FCCA` accept any integer-like `n` (`SupportsIndex`).
147
+ - `FData.std` samples the pointwise standard deviation on
148
+ `max(201, 10 * n_basis + 1)` points, matching R `sd.fd` to 1.8e-15.
149
+
150
+ ### Documentation
151
+ - Dataset docstrings state the unit of every value and time field.
152
+ - Observation weights: behaviour compared with R `fRegress(wt=)` and
153
+ `Fperm.fd` in the `fregress` / `f_test` notes and the R migration page.
154
+
155
+ ### Known differences from R `fda`
156
+ - Where R `fda` 6.3.0 is demonstrably less accurate (for example `deriv.fd`,
157
+ `times.fd`, B-spline penalties with no interior knots, `smooth.pos` stopping
158
+ one iteration short), Fabel returns the exact value. Each case is a strict
159
+ expected failure in the test suite with its measured error; see
160
+ `PARITY_REPORT.md`.
@@ -0,0 +1,59 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use fdatools, please cite it as below, together with Ramsay & Silverman (2005)."
3
+ type: software
4
+ title: "fdatools: functional data analysis for Python"
5
+ abstract: >-
6
+ fdatools is a clean-room Python rewrite of the R package fda (version 6.3.0):
7
+ basis expansions, penalised smoothing, functional principal component and
8
+ canonical correlation analysis, functional regression, curve registration
9
+ and principal differential analysis, with numerical parity against R.
10
+ version: 1.1.0
11
+ date-released: 2026-09-28
12
+ license: BSD-3-Clause
13
+ authors:
14
+ - family-names: Davodi
15
+ given-names: Hamed
16
+ email: hamed.davodi94@gmail.com
17
+ repository-code: "https://github.com/hameddavodi/fdatools"
18
+ url: "https://hameddavodi.github.io/fdatools"
19
+ keywords:
20
+ - functional data analysis
21
+ - fda
22
+ - splines
23
+ - smoothing
24
+ - functional PCA
25
+ references:
26
+ - type: book
27
+ title: "Functional Data Analysis"
28
+ edition: "2nd"
29
+ authors:
30
+ - family-names: Ramsay
31
+ given-names: J. O.
32
+ - family-names: Silverman
33
+ given-names: B. W.
34
+ year: 2005
35
+ publisher:
36
+ name: Springer
37
+ - type: book
38
+ title: "Functional Data Analysis with R and MATLAB"
39
+ authors:
40
+ - family-names: Ramsay
41
+ given-names: J. O.
42
+ - family-names: Hooker
43
+ given-names: Giles
44
+ - family-names: Graves
45
+ given-names: Spencer
46
+ year: 2009
47
+ publisher:
48
+ name: Springer
49
+ - type: software
50
+ title: "fda: Functional Data Analysis (R package)"
51
+ version: 6.3.0
52
+ authors:
53
+ - family-names: Ramsay
54
+ given-names: J. O.
55
+ - family-names: Graves
56
+ given-names: Spencer
57
+ - family-names: Hooker
58
+ given-names: Giles
59
+ url: "https://cran.r-project.org/package=fda"
fdatools-1.1.0/LICENSE ADDED
@@ -0,0 +1,29 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026, Hamed Davodi
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ 1. Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ 2. Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ 3. Neither the name of the copyright holder nor the names of its
17
+ contributors may be used to endorse or promote products derived from
18
+ this software without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,127 @@
1
+ Metadata-Version: 2.5
2
+ Name: fdatools
3
+ Version: 1.1.0
4
+ Summary: Functional data analysis for Python: a clean-room rewrite of R's fda package.
5
+ Project-URL: Homepage, https://github.com/hameddavodi/fdatools
6
+ Project-URL: Documentation, https://hameddavodi.github.io/fdatools
7
+ Project-URL: Source, https://github.com/hameddavodi/fdatools
8
+ Project-URL: Issues, https://github.com/hameddavodi/fdatools/issues
9
+ Project-URL: Changelog, https://github.com/hameddavodi/fdatools/blob/main/CHANGELOG.md
10
+ Author-email: Hamed Davodi <hamed.davodi94@gmail.com>
11
+ License-Expression: BSD-3-Clause
12
+ License-File: LICENSE
13
+ Keywords: fda,fpca,functional data analysis,smoothing,splines
14
+ Classifier: Development Status :: 5 - Production/Stable
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Intended Audience :: Science/Research
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3 :: Only
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
25
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
26
+ Classifier: Typing :: Typed
27
+ Requires-Python: >=3.10
28
+ Requires-Dist: array-api-compat>=1.11
29
+ Requires-Dist: numpy>=2.1
30
+ Requires-Dist: scikit-learn>=1.4
31
+ Requires-Dist: scipy>=1.14
32
+ Provides-Extra: dev
33
+ Requires-Dist: build>=1.2; extra == 'dev'
34
+ Requires-Dist: hypothesis>=6.100; extra == 'dev'
35
+ Requires-Dist: jupyter>=1.0; extra == 'dev'
36
+ Requires-Dist: mkdocs-material>=9.5; extra == 'dev'
37
+ Requires-Dist: mkdocs<2,>=1.6; extra == 'dev'
38
+ Requires-Dist: mkdocstrings[python]>=0.25; extra == 'dev'
39
+ Requires-Dist: mypy>=1.11; extra == 'dev'
40
+ Requires-Dist: nbmake>=1.5; extra == 'dev'
41
+ Requires-Dist: pandas-stubs>=2.2; extra == 'dev'
42
+ Requires-Dist: pip-audit>=2.7; extra == 'dev'
43
+ Requires-Dist: pyright>=1.1.403; extra == 'dev'
44
+ Requires-Dist: pytest-benchmark>=4.0; extra == 'dev'
45
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
46
+ Requires-Dist: pytest>=8.0; extra == 'dev'
47
+ Requires-Dist: ruff>=0.6; extra == 'dev'
48
+ Requires-Dist: scipy-stubs>=1.14; (python_version >= '3.10') and extra == 'dev'
49
+ Requires-Dist: twine>=5.0; extra == 'dev'
50
+ Provides-Extra: golden
51
+ Requires-Dist: rpy2>=3.5; extra == 'golden'
52
+ Provides-Extra: io
53
+ Requires-Dist: rdata>=0.11; extra == 'io'
54
+ Provides-Extra: pandas
55
+ Requires-Dist: pandas>=2.2; extra == 'pandas'
56
+ Requires-Dist: xarray>=2024.1; extra == 'pandas'
57
+ Provides-Extra: plot
58
+ Requires-Dist: matplotlib>=3.8; extra == 'plot'
59
+ Provides-Extra: torch
60
+ Requires-Dist: torch>=2.2; extra == 'torch'
61
+ Description-Content-Type: text/markdown
62
+
63
+ # fdatools
64
+
65
+ [![CI](https://github.com/hameddavodi/fdatools/actions/workflows/ci.yml/badge.svg)](https://github.com/hameddavodi/fdatools/actions/workflows/ci.yml)
66
+ [![PyPI](https://img.shields.io/pypi/v/fdatools.svg)](https://pypi.org/project/fdatools/)
67
+ [![Python versions](https://img.shields.io/pypi/pyversions/fdatools.svg)](https://pypi.org/project/fdatools/)
68
+ [![License: BSD-3-Clause](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg)](https://github.com/hameddavodi/fdatools/blob/main/LICENSE)
69
+ [![Docs](https://img.shields.io/badge/docs-hameddavodi.github.io%2Ffdatools-blue.svg)](https://hameddavodi.github.io/fdatools)
70
+
71
+ **Functional data analysis for Python.** fdatools is a clean-room Python rewrite of
72
+ R's [`fda`](https://cran.r-project.org/package=fda) package (Ramsay, Hooker &
73
+ Graves): basis expansions, penalised smoothing, functional PCA and CCA,
74
+ functional regression, curve registration and principal differential analysis.
75
+
76
+ - **Same numbers as R.** Every public function is tested against golden output
77
+ from R `fda` 6.3.0 at `rtol = 1e-8` (`1e-5` for iterative fits). See the
78
+ [parity report](https://github.com/hameddavodi/fdatools/blob/main/PARITY_REPORT.md).
79
+ - **About 40 symbols instead of 515 functions.** Curves are callable (`fd(t)`),
80
+ arithmetic is plain Python (`fd1 + fd2`, `fd1 @ fd2`), and there is one
81
+ `smooth()`, one `register()`, one `.plot()`.
82
+ - **Fits the Python stack.** scikit-learn estimators, NumPy or PyTorch arrays
83
+ through the Array API (gradients flow), pandas and xarray I/O.
84
+
85
+ ## Install
86
+
87
+ ```bash
88
+ pip install fdatools
89
+ ```
90
+
91
+ Optional extras: `fdatools[plot]` (matplotlib), `fdatools[pandas]` (pandas + xarray),
92
+ `fdatools[io]` (read R `.rds` files), `fdatools[torch]` (PyTorch). Python 3.10 to 3.13.
93
+
94
+ ## Quickstart
95
+
96
+ ```python
97
+ import numpy as np
98
+ import fdatools as fdt
99
+
100
+ growth = fdt.datasets.load_growth() # Berkeley growth study, ships with fdatools
101
+ basis = fdt.BSpline(domain=(1.0, 18.0), n_basis=12) # cubic B-splines on ages 1-18
102
+ coefs, *_ = np.linalg.lstsq(basis(growth.age), growth.hgtf, rcond=None)
103
+ girls = fdt.FData(coefs, basis) # 54 height curves in one object
104
+ speed = girls.derivative() # growth speed in cm/year, exact
105
+ print(speed.mean()(np.array([5.0, 12.0]))) # curves are callable: evaluate anywhere
106
+ print(fdt.inprod(girls[0], girls[0])) # L2 inner product of one curve
107
+ ```
108
+
109
+ Next steps: penalised smoothing with automatic λ (`smooth`), functional PCA
110
+ (`FPCA`) and more in the [quickstart guide](https://hameddavodi.github.io/fdatools/quickstart/).
111
+ Coming from R? The [migration table](https://hameddavodi.github.io/fdatools/r-migration/)
112
+ maps every `fda` function to its fdatools equivalent.
113
+
114
+ ## Documentation
115
+
116
+ Full documentation, API reference and tutorials: <https://hameddavodi.github.io/fdatools>.
117
+
118
+ ## Citing
119
+
120
+ If you use fdatools in published work, please cite it together with Ramsay &
121
+ Silverman (2005); see [`CITATION.cff`](https://github.com/hameddavodi/fdatools/blob/main/CITATION.cff).
122
+
123
+ ## License
124
+
125
+ BSD 3-Clause. fdatools is a clean-room implementation written from the published
126
+ mathematics (Ramsay & Silverman 2005; Ramsay, Hooker & Graves 2009) and the
127
+ public behaviour of R `fda`; it contains no R source code.
@@ -0,0 +1,65 @@
1
+ # fdatools
2
+
3
+ [![CI](https://github.com/hameddavodi/fdatools/actions/workflows/ci.yml/badge.svg)](https://github.com/hameddavodi/fdatools/actions/workflows/ci.yml)
4
+ [![PyPI](https://img.shields.io/pypi/v/fdatools.svg)](https://pypi.org/project/fdatools/)
5
+ [![Python versions](https://img.shields.io/pypi/pyversions/fdatools.svg)](https://pypi.org/project/fdatools/)
6
+ [![License: BSD-3-Clause](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg)](https://github.com/hameddavodi/fdatools/blob/main/LICENSE)
7
+ [![Docs](https://img.shields.io/badge/docs-hameddavodi.github.io%2Ffdatools-blue.svg)](https://hameddavodi.github.io/fdatools)
8
+
9
+ **Functional data analysis for Python.** fdatools is a clean-room Python rewrite of
10
+ R's [`fda`](https://cran.r-project.org/package=fda) package (Ramsay, Hooker &
11
+ Graves): basis expansions, penalised smoothing, functional PCA and CCA,
12
+ functional regression, curve registration and principal differential analysis.
13
+
14
+ - **Same numbers as R.** Every public function is tested against golden output
15
+ from R `fda` 6.3.0 at `rtol = 1e-8` (`1e-5` for iterative fits). See the
16
+ [parity report](https://github.com/hameddavodi/fdatools/blob/main/PARITY_REPORT.md).
17
+ - **About 40 symbols instead of 515 functions.** Curves are callable (`fd(t)`),
18
+ arithmetic is plain Python (`fd1 + fd2`, `fd1 @ fd2`), and there is one
19
+ `smooth()`, one `register()`, one `.plot()`.
20
+ - **Fits the Python stack.** scikit-learn estimators, NumPy or PyTorch arrays
21
+ through the Array API (gradients flow), pandas and xarray I/O.
22
+
23
+ ## Install
24
+
25
+ ```bash
26
+ pip install fdatools
27
+ ```
28
+
29
+ Optional extras: `fdatools[plot]` (matplotlib), `fdatools[pandas]` (pandas + xarray),
30
+ `fdatools[io]` (read R `.rds` files), `fdatools[torch]` (PyTorch). Python 3.10 to 3.13.
31
+
32
+ ## Quickstart
33
+
34
+ ```python
35
+ import numpy as np
36
+ import fdatools as fdt
37
+
38
+ growth = fdt.datasets.load_growth() # Berkeley growth study, ships with fdatools
39
+ basis = fdt.BSpline(domain=(1.0, 18.0), n_basis=12) # cubic B-splines on ages 1-18
40
+ coefs, *_ = np.linalg.lstsq(basis(growth.age), growth.hgtf, rcond=None)
41
+ girls = fdt.FData(coefs, basis) # 54 height curves in one object
42
+ speed = girls.derivative() # growth speed in cm/year, exact
43
+ print(speed.mean()(np.array([5.0, 12.0]))) # curves are callable: evaluate anywhere
44
+ print(fdt.inprod(girls[0], girls[0])) # L2 inner product of one curve
45
+ ```
46
+
47
+ Next steps: penalised smoothing with automatic λ (`smooth`), functional PCA
48
+ (`FPCA`) and more in the [quickstart guide](https://hameddavodi.github.io/fdatools/quickstart/).
49
+ Coming from R? The [migration table](https://hameddavodi.github.io/fdatools/r-migration/)
50
+ maps every `fda` function to its fdatools equivalent.
51
+
52
+ ## Documentation
53
+
54
+ Full documentation, API reference and tutorials: <https://hameddavodi.github.io/fdatools>.
55
+
56
+ ## Citing
57
+
58
+ If you use fdatools in published work, please cite it together with Ramsay &
59
+ Silverman (2005); see [`CITATION.cff`](https://github.com/hameddavodi/fdatools/blob/main/CITATION.cff).
60
+
61
+ ## License
62
+
63
+ BSD 3-Clause. fdatools is a clean-room implementation written from the published
64
+ mathematics (Ramsay & Silverman 2005; Ramsay, Hooker & Graves 2009) and the
65
+ public behaviour of R `fda`; it contains no R source code.
@@ -0,0 +1,181 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.27"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "fdatools"
7
+ version = "1.1.0"
8
+ description = "Functional data analysis for Python: a clean-room rewrite of R's fda package."
9
+ readme = "README.md"
10
+ license = "BSD-3-Clause"
11
+ license-files = ["LICENSE"]
12
+ requires-python = ">=3.10"
13
+ authors = [{ name = "Hamed Davodi", email = "hamed.davodi94@gmail.com" }]
14
+ keywords = ["functional data analysis", "fda", "splines", "smoothing", "fpca"]
15
+ classifiers = [
16
+ "Development Status :: 5 - Production/Stable",
17
+ "Intended Audience :: Developers",
18
+ "Intended Audience :: Science/Research",
19
+ "Operating System :: OS Independent",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3 :: Only",
22
+ "Programming Language :: Python :: 3.10",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ "Programming Language :: Python :: 3.13",
26
+ "Topic :: Scientific/Engineering :: Mathematics",
27
+ "Topic :: Scientific/Engineering :: Information Analysis",
28
+ "Typing :: Typed",
29
+ ]
30
+ dependencies = [
31
+ "numpy>=2.1",
32
+ "scipy>=1.14",
33
+ "array-api-compat>=1.11",
34
+ "scikit-learn>=1.4",
35
+ ]
36
+
37
+ [project.optional-dependencies]
38
+ torch = ["torch>=2.2"]
39
+ pandas = ["pandas>=2.2", "xarray>=2024.1"]
40
+ plot = ["matplotlib>=3.8"]
41
+ io = ["rdata>=0.11"]
42
+ # Only for tools/make_golden.py (it falls back to Rscript without it). Needs R to build.
43
+ golden = ["rpy2>=3.5"]
44
+ dev = [
45
+ "pytest>=8.0",
46
+ "pytest-cov>=5.0",
47
+ "pytest-benchmark>=4.0",
48
+ "hypothesis>=6.100",
49
+ "ruff>=0.6",
50
+ "mypy>=1.11",
51
+ "pyright>=1.1.403",
52
+ "nbmake>=1.5",
53
+ "jupyter>=1.0",
54
+ # MkDocs 2.0 drops the plugin system that mkdocs-material and mkdocstrings need.
55
+ "mkdocs>=1.6,<2",
56
+ "mkdocs-material>=9.5",
57
+ "mkdocstrings[python]>=0.25",
58
+ "build>=1.2",
59
+ "twine>=5.0",
60
+ "pip-audit>=2.7",
61
+ "pandas-stubs>=2.2",
62
+ "scipy-stubs>=1.14; python_version >= '3.10'",
63
+ ]
64
+
65
+ [project.urls]
66
+ Homepage = "https://github.com/hameddavodi/fdatools"
67
+ Documentation = "https://hameddavodi.github.io/fdatools"
68
+ Source = "https://github.com/hameddavodi/fdatools"
69
+ Issues = "https://github.com/hameddavodi/fdatools/issues"
70
+ Changelog = "https://github.com/hameddavodi/fdatools/blob/main/CHANGELOG.md"
71
+
72
+ [tool.hatch.build.targets.wheel]
73
+ packages = ["src/fdatools"]
74
+
75
+ [tool.hatch.build.targets.sdist]
76
+ include = ["src/fdatools", "README.md", "LICENSE", "CHANGELOG.md", "CITATION.cff"]
77
+
78
+ [tool.ruff]
79
+ line-length = 100
80
+ target-version = "py310"
81
+ src = ["src", "tests", "tools", "benchmarks"]
82
+ # Markdown is documentation, not source: SPEC.md/WORKFLOW.md snippets are illustrative
83
+ # API sketches and must not be rewritten by the formatter.
84
+ extend-exclude = ["*.md"]
85
+
86
+ [tool.ruff.lint]
87
+ select = ["E", "F", "W", "I", "N", "UP", "B", "A", "C4", "SIM", "RUF", "D", "ANN", "S", "PT", "NPY"]
88
+ ignore = ["D203", "D213", "ANN401", "S101", "D105", "D107"]
89
+
90
+ [tool.ruff.lint.per-file-ignores]
91
+ "tests/**" = ["D", "ANN", "S", "PT011"]
92
+ "benchmarks/**" = ["D", "ANN"]
93
+ "tools/**" = ["S603", "S607"]
94
+ "notebooks/**" = ["D", "ANN", "E402", "B018"]
95
+
96
+ [tool.ruff.lint.pydocstyle]
97
+ convention = "numpy"
98
+
99
+ [tool.ruff.format]
100
+ docstring-code-format = true
101
+
102
+ [tool.mypy]
103
+ # 3.12 because numpy >= 2.3 ships stubs written with PEP 695 `type` statements that
104
+ # mypy refuses to parse under an older target. Runtime support stays >= 3.10; ruff
105
+ # target-version = "py310" is what guards against newer syntax in our own sources.
106
+ python_version = "3.12"
107
+ strict = true
108
+ warn_unreachable = true
109
+
110
+ [[tool.mypy.overrides]]
111
+ module = ["array_api_compat.*", "sklearn.*", "matplotlib.*", "torch.*", "rdata.*", "xarray.*", "hypothesis.*", "rpy2.*"]
112
+ ignore_missing_imports = true
113
+
114
+ [tool.pyright]
115
+ # Pyright backs the editor/agent language server: code intelligence (jump-to-def,
116
+ # references, document symbols) plus fast control-flow checks. `mypy --strict` stays
117
+ # the authoritative type gate in CI -- pyright is tuned here NOT to re-report what
118
+ # mypy and ruff already own, so that what it does say is signal rather than noise.
119
+ #
120
+ # venvPath/venv are the load-bearing lines: without them pyright resolves no project
121
+ # import at all and every numpy/scipy/sklearn call cascades into a phantom error.
122
+ venvPath = "."
123
+ venv = ".venv"
124
+ # 3.12 for the same reason as [tool.mypy]: numpy >= 2.3 ships stubs written with PEP
125
+ # 695 `type` statements. Runtime support stays >= 3.10, guarded by ruff py310.
126
+ pythonVersion = "3.12"
127
+ # Every directory ruff lints, so code intelligence works outside src/ too.
128
+ include = ["src", "tests", "tools", "benchmarks"]
129
+ exclude = [
130
+ "**/__pycache__",
131
+ "**/.ruff_cache",
132
+ "**/.mypy_cache",
133
+ "**/.pytest_cache",
134
+ "**/.hypothesis",
135
+ ".venv",
136
+ "notebooks",
137
+ "data_export",
138
+ "data_release",
139
+ "tests/golden",
140
+ ]
141
+ typeCheckingMode = "standard"
142
+ # `ignore` suppresses diagnostics but STILL indexes these files, so jump-to-definition
143
+ # and find-references keep working across the tests. (`exclude` would drop them from the
144
+ # index entirely and break code intelligence.) Tests, tools and benchmarks are
145
+ # deliberately unannotated -- ruff already exempts them via per-file-ignores ANN --
146
+ # so type-checking them yields noise, not signal.
147
+ ignore = ["tests", "tools", "benchmarks"]
148
+ # Already enforced by ruff: F401 unused import, F841 unused local.
149
+ reportUnusedImport = "none"
150
+ reportUnusedVariable = "none"
151
+ # Already handled by [[tool.mypy.overrides]] ignore_missing_imports.
152
+ reportMissingTypeStubs = "none"
153
+ # The checks that earn their place: control-flow faults mypy --strict does not
154
+ # report by default. reportPossiblyUnbound caught a real UnboundLocalError path
155
+ # in smoothing._smooth_irregular that no grep or AST scan can see.
156
+ reportPossiblyUnboundVariable = "error"
157
+ reportUnboundVariable = "error"
158
+ # Known, accepted false positives: sklearn's `validate_data` declares a union return type
159
+ # that includes the `"no_validation"` sentinel string, so pyright thinks `.shape`/`.T` may
160
+ # be attribute access on `str` at 3 call sites (decomposition._as_fdata, Smoother.fit).
161
+ # mypy sidesteps this via ignore_missing_imports for sklearn.*. The rule stays ON because
162
+ # it catches real faults elsewhere -- it flagged a genuine bad attribute in io.py.
163
+
164
+ [tool.pytest.ini_options]
165
+ testpaths = ["tests"]
166
+ addopts = "-ra --strict-markers"
167
+ markers = [
168
+ "parity: golden-file parity tests against R fda 6.3.0",
169
+ "slow: long-running tests",
170
+ "gpu: requires CUDA",
171
+ "network: hits the real network, skipped by default",
172
+ ]
173
+ filterwarnings = ["error::DeprecationWarning:fdatools.*"]
174
+
175
+ [tool.coverage.run]
176
+ source = ["fdatools"]
177
+ branch = true
178
+
179
+ [tool.coverage.report]
180
+ show_missing = true
181
+ exclude_lines = ["pragma: no cover", "if TYPE_CHECKING:", "@overload"]