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.
- fdnkit-1.0.0/.github/workflows/ci.yml +30 -0
- fdnkit-1.0.0/.gitignore +44 -0
- fdnkit-1.0.0/CHANGELOG.md +32 -0
- fdnkit-1.0.0/CITATION.cff +55 -0
- fdnkit-1.0.0/CONTRIBUTING.md +44 -0
- fdnkit-1.0.0/LICENSE +21 -0
- fdnkit-1.0.0/PKG-INFO +192 -0
- fdnkit-1.0.0/PORTING_NOTES.md +41 -0
- fdnkit-1.0.0/README.md +148 -0
- fdnkit-1.0.0/docs/api.md +82 -0
- fdnkit-1.0.0/docs/index.md +39 -0
- fdnkit-1.0.0/docs/methods.md +72 -0
- fdnkit-1.0.0/docs/tutorial.md +101 -0
- fdnkit-1.0.0/examples/README.md +60 -0
- fdnkit-1.0.0/examples/quickstart.py +81 -0
- fdnkit-1.0.0/examples/real_data_eegbci.py +116 -0
- fdnkit-1.0.0/mkdocs.yml +14 -0
- fdnkit-1.0.0/paper/paper.bib +174 -0
- fdnkit-1.0.0/paper/paper.md +135 -0
- fdnkit-1.0.0/pyproject.toml +81 -0
- fdnkit-1.0.0/src/fdnkit/__init__.py +94 -0
- fdnkit-1.0.0/src/fdnkit/classify.py +310 -0
- fdnkit-1.0.0/src/fdnkit/cli.py +175 -0
- fdnkit-1.0.0/src/fdnkit/dfa.py +88 -0
- fdnkit-1.0.0/src/fdnkit/features.py +212 -0
- fdnkit-1.0.0/src/fdnkit/fodn.py +346 -0
- fdnkit-1.0.0/src/fdnkit/io.py +163 -0
- fdnkit-1.0.0/src/fdnkit/mfdfa.py +233 -0
- fdnkit-1.0.0/src/fdnkit/preprocessing.py +125 -0
- fdnkit-1.0.0/src/fdnkit/synthetic.py +173 -0
- fdnkit-1.0.0/src/fdnkit/viz.py +146 -0
- fdnkit-1.0.0/tests/test_api.py +32 -0
- fdnkit-1.0.0/tests/test_classify.py +91 -0
- fdnkit-1.0.0/tests/test_cli.py +72 -0
- fdnkit-1.0.0/tests/test_dfa.py +37 -0
- fdnkit-1.0.0/tests/test_features.py +67 -0
- fdnkit-1.0.0/tests/test_fodn.py +61 -0
- fdnkit-1.0.0/tests/test_io.py +80 -0
- fdnkit-1.0.0/tests/test_mfdfa.py +80 -0
- fdnkit-1.0.0/tests/test_preprocessing.py +70 -0
- fdnkit-1.0.0/tests/test_synthetic.py +43 -0
- 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
|
fdnkit-1.0.0/.gitignore
ADDED
|
@@ -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
|
+
[](https://github.com/SamirHossain099/fdnkit/actions/workflows/ci.yml)
|
|
50
|
+
[](https://github.com/SamirHossain099/fdnkit/blob/main/LICENSE)
|
|
51
|
+
[](https://www.python.org/)
|
|
52
|
+
[](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
|
+
[](https://github.com/SamirHossain099/fdnkit/actions/workflows/ci.yml)
|
|
6
|
+
[](https://github.com/SamirHossain099/fdnkit/blob/main/LICENSE)
|
|
7
|
+
[](https://www.python.org/)
|
|
8
|
+
[](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.
|