fdnkit 1.0.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 (42) hide show
  1. fdnkit-1.0.0/.github/workflows/ci.yml +30 -0
  2. fdnkit-1.0.0/.gitignore +44 -0
  3. fdnkit-1.0.0/CHANGELOG.md +32 -0
  4. fdnkit-1.0.0/CITATION.cff +55 -0
  5. fdnkit-1.0.0/CONTRIBUTING.md +44 -0
  6. fdnkit-1.0.0/LICENSE +21 -0
  7. fdnkit-1.0.0/PKG-INFO +192 -0
  8. fdnkit-1.0.0/PORTING_NOTES.md +41 -0
  9. fdnkit-1.0.0/README.md +148 -0
  10. fdnkit-1.0.0/docs/api.md +82 -0
  11. fdnkit-1.0.0/docs/index.md +39 -0
  12. fdnkit-1.0.0/docs/methods.md +72 -0
  13. fdnkit-1.0.0/docs/tutorial.md +101 -0
  14. fdnkit-1.0.0/examples/README.md +60 -0
  15. fdnkit-1.0.0/examples/quickstart.py +81 -0
  16. fdnkit-1.0.0/examples/real_data_eegbci.py +116 -0
  17. fdnkit-1.0.0/mkdocs.yml +14 -0
  18. fdnkit-1.0.0/paper/paper.bib +174 -0
  19. fdnkit-1.0.0/paper/paper.md +135 -0
  20. fdnkit-1.0.0/pyproject.toml +81 -0
  21. fdnkit-1.0.0/src/fdnkit/__init__.py +94 -0
  22. fdnkit-1.0.0/src/fdnkit/classify.py +310 -0
  23. fdnkit-1.0.0/src/fdnkit/cli.py +175 -0
  24. fdnkit-1.0.0/src/fdnkit/dfa.py +88 -0
  25. fdnkit-1.0.0/src/fdnkit/features.py +212 -0
  26. fdnkit-1.0.0/src/fdnkit/fodn.py +346 -0
  27. fdnkit-1.0.0/src/fdnkit/io.py +163 -0
  28. fdnkit-1.0.0/src/fdnkit/mfdfa.py +233 -0
  29. fdnkit-1.0.0/src/fdnkit/preprocessing.py +125 -0
  30. fdnkit-1.0.0/src/fdnkit/synthetic.py +173 -0
  31. fdnkit-1.0.0/src/fdnkit/viz.py +146 -0
  32. fdnkit-1.0.0/tests/test_api.py +32 -0
  33. fdnkit-1.0.0/tests/test_classify.py +91 -0
  34. fdnkit-1.0.0/tests/test_cli.py +72 -0
  35. fdnkit-1.0.0/tests/test_dfa.py +37 -0
  36. fdnkit-1.0.0/tests/test_features.py +67 -0
  37. fdnkit-1.0.0/tests/test_fodn.py +61 -0
  38. fdnkit-1.0.0/tests/test_io.py +80 -0
  39. fdnkit-1.0.0/tests/test_mfdfa.py +80 -0
  40. fdnkit-1.0.0/tests/test_preprocessing.py +70 -0
  41. fdnkit-1.0.0/tests/test_synthetic.py +43 -0
  42. fdnkit-1.0.0/tests/test_viz.py +35 -0
@@ -0,0 +1,30 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ${{ matrix.os }}
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ os: [ubuntu-latest]
16
+ python-version: ["3.9", "3.10", "3.11", "3.12"]
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ - name: Set up Python ${{ matrix.python-version }}
20
+ uses: actions/setup-python@v5
21
+ with:
22
+ python-version: ${{ matrix.python-version }}
23
+ - name: Install package with dev extras
24
+ run: |
25
+ python -m pip install --upgrade pip
26
+ pip install -e ".[dev]"
27
+ - name: Lint with ruff
28
+ run: ruff check src tests
29
+ - name: Run tests
30
+ run: pytest --cov=fdnkit --cov-report=term-missing
@@ -0,0 +1,44 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ *.egg
6
+ build/
7
+ dist/
8
+ .eggs/
9
+ .pytest_cache/
10
+ .ruff_cache/
11
+ .coverage
12
+ htmlcov/
13
+ .tox/
14
+ .mypy_cache/
15
+
16
+ # Virtual envs
17
+ .venv/
18
+ venv/
19
+ env/
20
+
21
+ # Jupyter
22
+ .ipynb_checkpoints/
23
+
24
+ # OS / editor
25
+ .DS_Store
26
+ Thumbs.db
27
+ .vscode/
28
+ .idea/
29
+
30
+ # Data / outputs (never commit private cohort data)
31
+ *.edf
32
+ *.h5
33
+ *.hdf5
34
+ data/private/
35
+ scratch/
36
+ *.log
37
+
38
+ # Example output artifacts
39
+ quickstart_panel.png
40
+ examples/*.png
41
+ *.csv
42
+
43
+ # Internal planning docs: not for the public repo (contain local/private paths)
44
+ BUILD_PLAN.md
@@ -0,0 +1,32 @@
1
+ # Changelog
2
+
3
+ All notable changes to FDNkit are documented here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this project adheres
5
+ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [1.0.0] - 2026-09-05
8
+
9
+ Initial public release.
10
+
11
+ ### Added
12
+ - `fdnkit.dfa`: monofractal DFA Hurst exponent.
13
+ - `fdnkit.mfdfa`: MFDFA generalized Hurst `h(q)`, multifractal width `Δh`, and
14
+ the `f(α)` singularity spectrum.
15
+ - `fdnkit.fodn`: fractional-order dynamical network model (per-channel `α`,
16
+ sparse directed coupling matrix, eigenvector hub scores, sparseness).
17
+ - `fdnkit.features`: tidy per-trial feature tables with a five-feature "core"
18
+ set matching the reference study.
19
+ - `fdnkit.classify`: subject-wise cross-validation by default (leave-one-subject-out),
20
+ group-aware permutation test, bootstrap confidence intervals; trial-wise LOO is
21
+ opt-in and labeled optimistic.
22
+ - `fdnkit.io`: EDF (MNE) and HDF5 (h5py) readers, Excel label loading, feature CSV IO.
23
+ - `fdnkit.preprocessing`: z-scoring, bad-channel flagging, windowing.
24
+ - `fdnkit.viz`: fluctuation, `h(q)`, spectrum, coupling-heatmap, and hub plots.
25
+ - `fdnkit.synthetic`: fractional Gaussian noise / motion (Davies–Harte),
26
+ binomial cascades, and coupled multi-channel iEEG-like signals.
27
+ - `fdnkit` command-line interface: `extract`, `classify`, `demo`.
28
+ - Test suite (79 tests, ~91% coverage) validating the numerical core against
29
+ signals with known scaling properties; GitHub Actions CI on Python 3.9–3.12.
30
+ - JOSS paper draft (`paper/`), documentation (`docs/`), and runnable examples:
31
+ a synthetic quickstart (`examples/quickstart.py`) and a real-data walkthrough
32
+ on public PhysioNet EEG (`examples/real_data_eegbci.py`).
@@ -0,0 +1,55 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use FDNkit, please cite it as below."
3
+ title: "FDNkit: Fractional Dynamical Network & Multifractal toolkit for intracranial EEG"
4
+ abstract: >-
5
+ FDNkit is an open-source Python library and command-line tool that turns
6
+ intracranial-EEG recordings into fractal and fractional-dynamical-network
7
+ features (DFA Hurst, MFDFA generalized Hurst, fractional-order dynamical
8
+ network coupling matrices, fractional exponents, and eigenvector hubs), with
9
+ plotting utilities and a classification harness that defaults to honest,
10
+ subject-wise cross-validation.
11
+ type: software
12
+ authors:
13
+ - family-names: Hossain
14
+ given-names: Samir
15
+ orcid: "https://orcid.org/0009-0003-8986-0946"
16
+ affiliation: "Department of Electrical and Computer Engineering, Texas Tech University, Lubbock, TX, USA"
17
+ version: 1.0.0
18
+ date-released: "2026-09-05"
19
+ doi: "10.5281/zenodo.22366240"
20
+ identifiers:
21
+ - type: doi
22
+ value: "10.5281/zenodo.22366240"
23
+ description: "Concept DOI resolving to the latest version of FDNkit"
24
+ - type: doi
25
+ value: "10.5281/zenodo.22366241"
26
+ description: "Archived snapshot of FDNkit v1.0.0"
27
+ license: MIT
28
+ repository-code: "https://github.com/SamirHossain099/fdnkit"
29
+ keywords:
30
+ - iEEG
31
+ - DFA
32
+ - MFDFA
33
+ - multifractal
34
+ - fractional dynamics
35
+ - network physiology
36
+ references:
37
+ - type: article
38
+ title: "Quantifying cognitive effort's impact on suppression of epilepsy-associated after discharges"
39
+ authors:
40
+ - family-names: Beeram
41
+ given-names: Sai Pavan
42
+ - family-names: Farris
43
+ given-names: Matthew
44
+ - family-names: Hossain
45
+ given-names: Samir
46
+ - family-names: Rethans
47
+ given-names: Nicholas
48
+ - family-names: Kang
49
+ given-names: Joon Y.
50
+ - family-names: Pereira
51
+ given-names: Emily A.
52
+ year: 2026
53
+ journal: "Frontiers in Network Physiology"
54
+ volume: "6"
55
+ doi: "10.3389/fnetp.2026.1768476"
@@ -0,0 +1,44 @@
1
+ # Contributing to FDNkit
2
+
3
+ Thanks for your interest in improving FDNkit! Bug reports, feature requests, and
4
+ pull requests are all welcome.
5
+
6
+ ## Development setup
7
+
8
+ ```bash
9
+ git clone https://github.com/SamirHossain099/fdnkit
10
+ cd fdnkit
11
+ python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
12
+ pip install -e ".[dev]"
13
+ ```
14
+
15
+ ## Before opening a pull request
16
+
17
+ ```bash
18
+ ruff check src tests # lint
19
+ pytest --cov=fdnkit # tests + coverage
20
+ ```
21
+
22
+ Please:
23
+
24
+ - Keep the numerical core **array-first and pure**: pandas/IO/plotting layer on
25
+ top, and matplotlib/MNE/h5py stay optional (lazily imported).
26
+ - Add a test for any new behavior. Where possible, validate against **ground
27
+ truth** (a signal with a known Hurst exponent, a cascade with a known
28
+ multifractal width), not just against the current output.
29
+ - Match the existing docstring style (NumPy-format) and keep public functions
30
+ documented.
31
+ - Do **not** commit data. The private clinical cohort must never be added;
32
+ examples and tests use `fdnkit.synthetic`.
33
+
34
+ ## Reporting bugs
35
+
36
+ Open an issue with a minimal reproducer, ideally one built from
37
+ `fdnkit.synthetic` so it runs anywhere.
38
+
39
+ ## Scope
40
+
41
+ FDNkit is intentionally focused on fractal / fractional-dynamical-network methods
42
+ for iEEG. General EEG preprocessing, montages, and filtering belong in
43
+ MNE-Python; connectivity beyond FODN (Granger, transfer entropy) and a result
44
+ database are candidate future additions but out of scope for the core.
fdnkit-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Samir Hossain
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
fdnkit-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,192 @@
1
+ Metadata-Version: 2.5
2
+ Name: fdnkit
3
+ Version: 1.0.0
4
+ Summary: Fractional Dynamical Network & Multifractal toolkit for intracranial EEG
5
+ Project-URL: Homepage, https://github.com/SamirHossain099/fdnkit
6
+ Project-URL: Documentation, https://github.com/SamirHossain099/fdnkit#readme
7
+ Project-URL: Repository, https://github.com/SamirHossain099/fdnkit
8
+ Project-URL: Issues, https://github.com/SamirHossain099/fdnkit/issues
9
+ Author: Samir Hossain
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: DFA,EEG,MFDFA,fractional dynamics,iEEG,multifractal,network physiology,neuroscience
13
+ Classifier: Development Status :: 5 - Production/Stable
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
23
+ Requires-Python: >=3.9
24
+ Requires-Dist: numpy>=1.22
25
+ Requires-Dist: pandas>=1.4
26
+ Requires-Dist: scikit-learn>=1.1
27
+ Requires-Dist: scipy>=1.8
28
+ Provides-Extra: all
29
+ Requires-Dist: h5py>=3.0; extra == 'all'
30
+ Requires-Dist: matplotlib>=3.5; extra == 'all'
31
+ Requires-Dist: mne>=1.0; extra == 'all'
32
+ Provides-Extra: dev
33
+ Requires-Dist: h5py>=3.0; extra == 'dev'
34
+ Requires-Dist: matplotlib>=3.5; extra == 'dev'
35
+ Requires-Dist: pytest-cov>=4.0; extra == 'dev'
36
+ Requires-Dist: pytest>=7.0; extra == 'dev'
37
+ Requires-Dist: ruff>=0.1; extra == 'dev'
38
+ Provides-Extra: io
39
+ Requires-Dist: h5py>=3.0; extra == 'io'
40
+ Requires-Dist: mne>=1.0; extra == 'io'
41
+ Provides-Extra: viz
42
+ Requires-Dist: matplotlib>=3.5; extra == 'viz'
43
+ Description-Content-Type: text/markdown
44
+
45
+ # FDNkit
46
+
47
+ **Fractional Dynamical Network & Multifractal toolkit for intracranial EEG**
48
+
49
+ [![CI](https://github.com/SamirHossain099/fdnkit/actions/workflows/ci.yml/badge.svg)](https://github.com/SamirHossain099/fdnkit/actions/workflows/ci.yml)
50
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/SamirHossain099/fdnkit/blob/main/LICENSE)
51
+ [![Python 3.9+](https://img.shields.io/badge/python-3.9%2B-blue.svg)](https://www.python.org/)
52
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.22366240.svg)](https://doi.org/10.5281/zenodo.22366240)
53
+
54
+ FDNkit turns intracranial-EEG (iEEG) recordings into **fractal** and
55
+ **fractional-dynamical-network** features and evaluates them **honestly**. It
56
+ packages methods validated in Beeram et al. (2026, *Front. Netw. Physiol.*
57
+ 6:1768476) and the fractional-dynamical-network line of work of Gupta et al.
58
+ (2018) and Xue & Bogdan (2017) as a clean, documented, tested library, not a one-off GUI welded to a single dataset.
59
+
60
+ It computes:
61
+
62
+ - **DFA**: monofractal Hurst exponent `H`.
63
+ - **MFDFA**: generalized Hurst `h(q)`, multifractal width `Δh`, and the
64
+ singularity spectrum `f(α)`.
65
+ - **FODN**: a fractional-order dynamical network: per-channel fractional orders
66
+ `α`, a sparse directed coupling matrix `A`, and eigenvector "hub" scores.
67
+ - **Feature tables**: tidy, one-row-per-trial pandas DataFrames.
68
+ - **Honest classification**: a logistic-regression harness that defaults to
69
+ **leave-one-subject-out** cross-validation with a subject-level permutation
70
+ test, because row-wise splits leak patient identity and inflate accuracy.
71
+
72
+ ## Install
73
+
74
+ ```bash
75
+ pip install fdnkit # core: numpy, scipy, pandas, scikit-learn
76
+ pip install "fdnkit[viz]" # + matplotlib for plots
77
+ pip install "fdnkit[io]" # + mne (EDF) and h5py (HDF5) readers
78
+ pip install "fdnkit[all]" # everything
79
+ ```
80
+
81
+ From source:
82
+
83
+ ```bash
84
+ git clone https://github.com/SamirHossain099/fdnkit
85
+ cd fdnkit
86
+ pip install -e ".[dev]"
87
+ pytest
88
+ ```
89
+
90
+ ## 60-second example (no data download)
91
+
92
+ ```python
93
+ from fdnkit.synthetic import synthetic_ieeg
94
+ from fdnkit.features import extract_features
95
+
96
+ # A small, sparsely-coupled synthetic iEEG trial (8 channels, 5 s @ 1 kHz).
97
+ signals, channel_names = synthetic_ieeg(n_channels=8, n_samples=5000, seed=0)
98
+
99
+ # One tidy feature row: DFA H, MFDFA h(q)/Δh, FODN α / leading eigenvalue / hubs.
100
+ features = extract_features(signals)
101
+ print(features["MF_DFA_H"], features["MeanAlpha"], features["LeadingEig"])
102
+ ```
103
+
104
+ Analyze a single signal directly:
105
+
106
+ ```python
107
+ import numpy as np
108
+ from fdnkit.dfa import dfa
109
+ from fdnkit.mfdfa import mfdfa
110
+ from fdnkit.fodn import fit_fodn
111
+
112
+ x = signals[0]
113
+ print("Hurst:", dfa(x).hurst)
114
+ print("multifractal width Δh:", mfdfa(x).delta_h)
115
+
116
+ fodn = fit_fodn(signals) # (channels, timepoints)
117
+ print("leading eigenvalue:", fodn.leading_eig)
118
+ print("hub scores:", np.round(fodn.dominant_eigvec, 3))
119
+ ```
120
+
121
+ ## Honest classification
122
+
123
+ ```python
124
+ from fdnkit.classify import classify_dataframe
125
+
126
+ # df has feature columns plus 'label' and 'group' (e.g. subject id) columns.
127
+ result = classify_dataframe(df, label_col="label", group_col="group", cv="loso")
128
+ print(result.summary())
129
+ # Leave-one-subject-out balanced accuracy, ROC-AUC, a subject-level
130
+ # permutation p-value, and a bootstrap 95% CI.
131
+ ```
132
+
133
+ `cv="loso"` (the default) holds out whole subjects and **requires** `groups`.
134
+ Trial-wise `cv="loo"` is available but must be requested explicitly and is
135
+ labeled *optimistic*: it is the leakage-prone scheme FDNkit exists to warn about.
136
+
137
+ ## Command line
138
+
139
+ ```bash
140
+ # Self-contained demo: synthesize a labeled cohort and classify it honestly.
141
+ fdnkit demo --out demo_features.csv
142
+ fdnkit classify demo_features.csv --label label --group group
143
+
144
+ # Extract features from your own recording (EDF via MNE, or HDF5).
145
+ fdnkit extract recording.edf --window 1.0 --drop-bad --zscore --out features.csv
146
+ ```
147
+
148
+ ## Design principles
149
+
150
+ - **Array-first core.** `dfa(signal)`, `mfdfa(signal)`, `fit_fodn(signals)` are
151
+ pure functions on NumPy arrays. Pandas/IO/plotting layer on top.
152
+ - **Depend, don't duplicate.** IO, montages, and filtering defer to
153
+ [MNE-Python](https://mne.tools); FDNkit adds only the fractal/FODN methods.
154
+ - **Deterministic and seedable.** Bad channels log a warning instead of crashing.
155
+ - **Honest by default.** Subject-wise CV and permutation testing are the
156
+ headline, not an afterthought.
157
+
158
+ ## Validation
159
+
160
+ FDNkit's numerical core is checked against ground truth (see `tests/`):
161
+
162
+ - DFA recovers the Hurst exponent of fractional Gaussian noise across
163
+ `H = 0.3…0.9`; white noise → `H ≈ 0.5`, Brownian motion → `H ≈ 1.5`.
164
+ - MFDFA reports a wide `h(q)` for a multiplicative binomial cascade and a narrow
165
+ one for a monofractal signal; `h(q=2)` matches the DFA Hurst exponent exactly.
166
+ - FODN recovers finite fractional orders, coupling, and hubs on synthetic
167
+ coupled systems.
168
+
169
+ ## Citation
170
+
171
+ If you use FDNkit, please cite the software:
172
+
173
+ > Hossain, S. (2026). *FDNkit: Fractional Dynamical Network & Multifractal
174
+ > toolkit for intracranial EEG* (v1.0.0). Zenodo.
175
+ > https://doi.org/10.5281/zenodo.22366240
176
+
177
+ (`10.5281/zenodo.22366240` always resolves to the latest release; cite
178
+ `10.5281/zenodo.22366241` for v1.0.0 specifically. See
179
+ [`CITATION.cff`](https://github.com/SamirHossain099/fdnkit/blob/main/CITATION.cff).)
180
+
181
+ Please also cite the methods paper:
182
+
183
+ > Beeram, S. P., Farris, M., Hossain, S., Rethans, N., Kang, J. Y., & Pereira,
184
+ > E. A. (2026). *Quantifying cognitive effort's impact on suppression of
185
+ > epilepsy-associated after discharges.* Frontiers in Network Physiology, 6,
186
+ > 1768476. https://doi.org/10.3389/fnetp.2026.1768476
187
+
188
+ ## License
189
+
190
+ MIT; see [LICENSE](https://github.com/SamirHossain099/fdnkit/blob/main/LICENSE). The underlying fractional-dynamical-network method
191
+ is due to Gupta, Pequito & Bogdan (2018) and Xue & Bogdan (2017); please cite
192
+ them when using the FODN module.
@@ -0,0 +1,41 @@
1
+ # Porting notes
2
+
3
+ FDNkit generalizes a validated but single-purpose research codebase into a
4
+ reusable library. This table maps each ported piece to its origin in the
5
+ original single-purpose research tool and records what changed.
6
+
7
+ | FDNkit module | Ported from | Notes on the port |
8
+ |---|---|---|
9
+ | `mfdfa.py` | `software_app/gui/mfdfa_analysis_window.py::run_mfdfa`; `MATLAB_Code/MFDFA.m`, `DFA_of_EEG.m` | Extracted the core maths out of the PyQt window into pure functions. Same forward-partition algorithm, `q=0` log-average limit, and `eps` flooring. Added `MFDFAResult`, `delta_h`, and a Legendre-transform `f(α)` spectrum. |
10
+ | `dfa.py` | same as above (the `q=2` special case) | Thin, single-purpose entry point sharing `mfdfa.py`'s internals. Verified `h(q=2) == dfa.hurst` exactly. |
11
+ | `fodn.py` | `software_app/utils/fodn_code.py::fracOrdUU`, `HaarWaveletTransform` | Preserved the numerical procedure (Haar-wavelet order estimation, Grünwald–Letnikov differencing, regularized least squares + ADMM-LASSO). Renamed to a scikit-learn-style `FODN.fit`/`result`. Replaced silent `print`-and-continue error handling with proper exceptions; a rank-deficient `B` now falls back to a well-conditioned selector instead of raising. Removed the unused sparse-computation branches. |
12
+ | `features.py` | `software_app/utils/feature_extractor.py::FeatureExtractor` | Reimplemented to compute features directly from arrays / result objects instead of scraping a directory tree of CSVs. Kept the five "core" feature names (`MeanAlpha`, `VarAlpha`, `LeadingEig`, `MF_DFA_H`, `MF_DFA_Hq_mean`) as aliases so results line up with the reference study. |
13
+ | `io.py` | `software_app/utils/file_utils.py`, `label_strategies.py::ExcelMathScoreLabeler` | Removed the PyQt `QMessageBox` coupling; IO now raises plain exceptions. EDF via MNE and HDF5 via h5py are optional, lazily imported dependencies. |
14
+ | `classify.py` | the original model-training window and honest-CV revalidation script | Folded the honest-CV revalidation logic into the library. The GUI's random 80/20 split is deliberately **not** the default; leave-one-subject-out is, with a required `groups` argument, a group-aware permutation test, and bootstrap CIs. |
15
+ | `viz.py` | `scripts/analysis_scripts/{plotter,EigenvectorHeatmap}.py`; the GUI plotting code | Reduced to composable, `ax`-returning functions with matplotlib as an optional dependency. |
16
+ | `preprocessing.py` | scattered windowing/artifact logic in the GUI windows | Consolidated z-scoring, bad-channel flagging, and fixed-length windowing. |
17
+ | `synthetic.py` | new | Added for tests/examples: exact fGn/fBm (Davies–Harte), binomial cascades, and coupled multi-channel signals, so the toolkit runs with no data download. |
18
+
19
+ ## Known issues from the source that were fixed cleanly
20
+
21
+ - `log2(0)` / divide-by-zero warnings in DFA/MFDFA: handled with `eps` flooring
22
+ and finite-value masking in the log–log regression.
23
+ - **Negative-`q` MFDFA blow-up on real (integer-quantized) recordings**: the
24
+ original machine-epsilon RMS floor let degenerately-detrended segments collapse
25
+ to ~0 and dominate negative-`q` moments, fabricating a huge multifractal width
26
+ (observed `Δh ≈ 10` on real EEG). Replaced with a **scale-relative floor**
27
+ (`rel_floor`, a fraction of each scale's median fluctuation, default `1e-3`);
28
+ this restores physical `Δh` on real data while leaving synthetic results
29
+ (cascade `Δh`, monofractal `Δh`, Hurst recovery) numerically unchanged.
30
+ - FODN order-estimation `log2` of zero variance: floored at `1e-10` (as in the
31
+ source) and documented.
32
+ - Rank-deficient heuristic `B` matrix previously raised: now falls back to a
33
+ standard-basis selector.
34
+ - Silent `except: print(...)` blocks in `fracOrdUU.fit`: replaced with explicit
35
+ `ValueError`s so callers can handle failures.
36
+
37
+ ## Deliberately deferred (out of scope for v1)
38
+
39
+ - DuckDB result store (`database_scripts/`).
40
+ - Directed-connectivity measures (Granger/transfer entropy).
41
+ - A graphical interface: the core is headless by design.
fdnkit-1.0.0/README.md ADDED
@@ -0,0 +1,148 @@
1
+ # FDNkit
2
+
3
+ **Fractional Dynamical Network & Multifractal toolkit for intracranial EEG**
4
+
5
+ [![CI](https://github.com/SamirHossain099/fdnkit/actions/workflows/ci.yml/badge.svg)](https://github.com/SamirHossain099/fdnkit/actions/workflows/ci.yml)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/SamirHossain099/fdnkit/blob/main/LICENSE)
7
+ [![Python 3.9+](https://img.shields.io/badge/python-3.9%2B-blue.svg)](https://www.python.org/)
8
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.22366240.svg)](https://doi.org/10.5281/zenodo.22366240)
9
+
10
+ FDNkit turns intracranial-EEG (iEEG) recordings into **fractal** and
11
+ **fractional-dynamical-network** features and evaluates them **honestly**. It
12
+ packages methods validated in Beeram et al. (2026, *Front. Netw. Physiol.*
13
+ 6:1768476) and the fractional-dynamical-network line of work of Gupta et al.
14
+ (2018) and Xue & Bogdan (2017) as a clean, documented, tested library, not a one-off GUI welded to a single dataset.
15
+
16
+ It computes:
17
+
18
+ - **DFA**: monofractal Hurst exponent `H`.
19
+ - **MFDFA**: generalized Hurst `h(q)`, multifractal width `Δh`, and the
20
+ singularity spectrum `f(α)`.
21
+ - **FODN**: a fractional-order dynamical network: per-channel fractional orders
22
+ `α`, a sparse directed coupling matrix `A`, and eigenvector "hub" scores.
23
+ - **Feature tables**: tidy, one-row-per-trial pandas DataFrames.
24
+ - **Honest classification**: a logistic-regression harness that defaults to
25
+ **leave-one-subject-out** cross-validation with a subject-level permutation
26
+ test, because row-wise splits leak patient identity and inflate accuracy.
27
+
28
+ ## Install
29
+
30
+ ```bash
31
+ pip install fdnkit # core: numpy, scipy, pandas, scikit-learn
32
+ pip install "fdnkit[viz]" # + matplotlib for plots
33
+ pip install "fdnkit[io]" # + mne (EDF) and h5py (HDF5) readers
34
+ pip install "fdnkit[all]" # everything
35
+ ```
36
+
37
+ From source:
38
+
39
+ ```bash
40
+ git clone https://github.com/SamirHossain099/fdnkit
41
+ cd fdnkit
42
+ pip install -e ".[dev]"
43
+ pytest
44
+ ```
45
+
46
+ ## 60-second example (no data download)
47
+
48
+ ```python
49
+ from fdnkit.synthetic import synthetic_ieeg
50
+ from fdnkit.features import extract_features
51
+
52
+ # A small, sparsely-coupled synthetic iEEG trial (8 channels, 5 s @ 1 kHz).
53
+ signals, channel_names = synthetic_ieeg(n_channels=8, n_samples=5000, seed=0)
54
+
55
+ # One tidy feature row: DFA H, MFDFA h(q)/Δh, FODN α / leading eigenvalue / hubs.
56
+ features = extract_features(signals)
57
+ print(features["MF_DFA_H"], features["MeanAlpha"], features["LeadingEig"])
58
+ ```
59
+
60
+ Analyze a single signal directly:
61
+
62
+ ```python
63
+ import numpy as np
64
+ from fdnkit.dfa import dfa
65
+ from fdnkit.mfdfa import mfdfa
66
+ from fdnkit.fodn import fit_fodn
67
+
68
+ x = signals[0]
69
+ print("Hurst:", dfa(x).hurst)
70
+ print("multifractal width Δh:", mfdfa(x).delta_h)
71
+
72
+ fodn = fit_fodn(signals) # (channels, timepoints)
73
+ print("leading eigenvalue:", fodn.leading_eig)
74
+ print("hub scores:", np.round(fodn.dominant_eigvec, 3))
75
+ ```
76
+
77
+ ## Honest classification
78
+
79
+ ```python
80
+ from fdnkit.classify import classify_dataframe
81
+
82
+ # df has feature columns plus 'label' and 'group' (e.g. subject id) columns.
83
+ result = classify_dataframe(df, label_col="label", group_col="group", cv="loso")
84
+ print(result.summary())
85
+ # Leave-one-subject-out balanced accuracy, ROC-AUC, a subject-level
86
+ # permutation p-value, and a bootstrap 95% CI.
87
+ ```
88
+
89
+ `cv="loso"` (the default) holds out whole subjects and **requires** `groups`.
90
+ Trial-wise `cv="loo"` is available but must be requested explicitly and is
91
+ labeled *optimistic*: it is the leakage-prone scheme FDNkit exists to warn about.
92
+
93
+ ## Command line
94
+
95
+ ```bash
96
+ # Self-contained demo: synthesize a labeled cohort and classify it honestly.
97
+ fdnkit demo --out demo_features.csv
98
+ fdnkit classify demo_features.csv --label label --group group
99
+
100
+ # Extract features from your own recording (EDF via MNE, or HDF5).
101
+ fdnkit extract recording.edf --window 1.0 --drop-bad --zscore --out features.csv
102
+ ```
103
+
104
+ ## Design principles
105
+
106
+ - **Array-first core.** `dfa(signal)`, `mfdfa(signal)`, `fit_fodn(signals)` are
107
+ pure functions on NumPy arrays. Pandas/IO/plotting layer on top.
108
+ - **Depend, don't duplicate.** IO, montages, and filtering defer to
109
+ [MNE-Python](https://mne.tools); FDNkit adds only the fractal/FODN methods.
110
+ - **Deterministic and seedable.** Bad channels log a warning instead of crashing.
111
+ - **Honest by default.** Subject-wise CV and permutation testing are the
112
+ headline, not an afterthought.
113
+
114
+ ## Validation
115
+
116
+ FDNkit's numerical core is checked against ground truth (see `tests/`):
117
+
118
+ - DFA recovers the Hurst exponent of fractional Gaussian noise across
119
+ `H = 0.3…0.9`; white noise → `H ≈ 0.5`, Brownian motion → `H ≈ 1.5`.
120
+ - MFDFA reports a wide `h(q)` for a multiplicative binomial cascade and a narrow
121
+ one for a monofractal signal; `h(q=2)` matches the DFA Hurst exponent exactly.
122
+ - FODN recovers finite fractional orders, coupling, and hubs on synthetic
123
+ coupled systems.
124
+
125
+ ## Citation
126
+
127
+ If you use FDNkit, please cite the software:
128
+
129
+ > Hossain, S. (2026). *FDNkit: Fractional Dynamical Network & Multifractal
130
+ > toolkit for intracranial EEG* (v1.0.0). Zenodo.
131
+ > https://doi.org/10.5281/zenodo.22366240
132
+
133
+ (`10.5281/zenodo.22366240` always resolves to the latest release; cite
134
+ `10.5281/zenodo.22366241` for v1.0.0 specifically. See
135
+ [`CITATION.cff`](https://github.com/SamirHossain099/fdnkit/blob/main/CITATION.cff).)
136
+
137
+ Please also cite the methods paper:
138
+
139
+ > Beeram, S. P., Farris, M., Hossain, S., Rethans, N., Kang, J. Y., & Pereira,
140
+ > E. A. (2026). *Quantifying cognitive effort's impact on suppression of
141
+ > epilepsy-associated after discharges.* Frontiers in Network Physiology, 6,
142
+ > 1768476. https://doi.org/10.3389/fnetp.2026.1768476
143
+
144
+ ## License
145
+
146
+ MIT; see [LICENSE](https://github.com/SamirHossain099/fdnkit/blob/main/LICENSE). The underlying fractional-dynamical-network method
147
+ is due to Gupta, Pequito & Bogdan (2018) and Xue & Bogdan (2017); please cite
148
+ them when using the FODN module.