physioex 1.0.0.dev4__tar.gz → 1.0.3__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 (159) hide show
  1. {physioex-1.0.0.dev4 → physioex-1.0.3}/PKG-INFO +47 -16
  2. physioex-1.0.3/README.md +89 -0
  3. {physioex-1.0.0.dev4/docs/api → physioex-1.0.3/physioex}/__init__.py +0 -0
  4. physioex-1.0.3/physioex/data/datamodule.py +109 -0
  5. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex/data/datareader.py +185 -25
  6. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex/data/dataset.py +4 -2
  7. {physioex-1.0.0.dev4/physioex → physioex-1.0.3/physioex/preprocess}/__init__.py +0 -0
  8. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex/preprocess/bin/main.py +30 -20
  9. physioex-1.0.0.dev4/physioex/preprocess/mros.py → physioex-1.0.3/physioex/preprocess/hpap.py +12 -17
  10. physioex-1.0.3/physioex/preprocess/kornum.py +149 -0
  11. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex/preprocess/mesa.py +2 -3
  12. physioex-1.0.3/physioex/preprocess/mros.py +118 -0
  13. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex/preprocess/preprocessor.py +5 -9
  14. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex/preprocess/shhs.py +55 -33
  15. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex/preprocess/sleepedf.py +1 -29
  16. physioex-1.0.3/physioex/preprocess/utils/mousedata.py +211 -0
  17. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex/preprocess/utils/signal.py +17 -9
  18. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex/preprocess/utils/sleepdata.py +4 -2
  19. physioex-1.0.3/physioex/preprocess/wsc.py +306 -0
  20. {physioex-1.0.0.dev4/physioex/preprocess/.future/dreem → physioex-1.0.3/physioex/train/bin}/__init__.py +0 -0
  21. physioex-1.0.3/physioex/train/bin/dist_train.py +100 -0
  22. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex/train/bin/finetune.py +3 -0
  23. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex/train/bin/parser.py +41 -12
  24. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex/train/bin/test.py +5 -0
  25. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex/train/bin/train.py +6 -0
  26. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex/train/models/load.py +12 -2
  27. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex/train/networks/__init__.py +3 -4
  28. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex/train/networks/base.py +36 -21
  29. physioex-1.0.3/physioex/train/networks/prototype.py +263 -0
  30. physioex-1.0.3/physioex/train/networks/seqsexnet.py +412 -0
  31. physioex-1.0.3/physioex/train/networks/sleeptransformer.py +248 -0
  32. physioex-1.0.3/physioex/train/networks/utils/layers.py +158 -0
  33. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex/train/networks/utils/loss.py +33 -7
  34. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex/train/utils/test.py +23 -5
  35. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex/train/utils/train.py +48 -32
  36. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex.egg-info/PKG-INFO +47 -16
  37. physioex-1.0.3/physioex.egg-info/SOURCES.txt +57 -0
  38. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex.egg-info/requires.txt +2 -0
  39. {physioex-1.0.0.dev4 → physioex-1.0.3}/pyproject.toml +4 -2
  40. physioex-1.0.3/setup.py +40 -0
  41. physioex-1.0.0.dev4/.DS_Store +0 -0
  42. physioex-1.0.0.dev4/.github/workflows/black.yml +0 -15
  43. physioex-1.0.0.dev4/.github/workflows/ci.yml +0 -44
  44. physioex-1.0.0.dev4/.gitignore +0 -184
  45. physioex-1.0.0.dev4/README.md +0 -60
  46. physioex-1.0.0.dev4/docs/api/bin.py +0 -251
  47. physioex-1.0.0.dev4/docs/api/concept_learning.py +0 -0
  48. physioex-1.0.0.dev4/docs/api/data.py +0 -162
  49. physioex-1.0.0.dev4/docs/api/network.py +0 -132
  50. physioex-1.0.0.dev4/docs/api/preprocessor.py +0 -80
  51. physioex-1.0.0.dev4/docs/api/train.py +0 -92
  52. physioex-1.0.0.dev4/docs/assets/images/data/sequence_viz.png +0 -0
  53. physioex-1.0.0.dev4/docs/assets/images/logo.psd +0 -0
  54. physioex-1.0.0.dev4/docs/assets/images/logo.svg +0 -9
  55. physioex-1.0.0.dev4/docs/assets/images/logo_bar.svg +0 -11
  56. physioex-1.0.0.dev4/docs/css/custom.css +0 -12
  57. physioex-1.0.0.dev4/docs/examples/latent_space_visualization.md +0 -301
  58. physioex-1.0.0.dev4/docs/examples/latent_space_visualization_files/latent_space_visualization_11_1.png +0 -0
  59. physioex-1.0.0.dev4/docs/examples/latent_space_visualization_files/latent_space_visualization_12_1.png +0 -0
  60. physioex-1.0.0.dev4/docs/examples/latent_space_visualization_files/latent_space_visualization_6_0.png +0 -0
  61. physioex-1.0.0.dev4/docs/examples/latent_space_visualization_files/latent_space_visualization_8_1.png +0 -0
  62. physioex-1.0.0.dev4/docs/index.md +0 -60
  63. physioex-1.0.0.dev4/docs/javascript/extra.js +0 -1
  64. physioex-1.0.0.dev4/docs/pages/contribute.md +0 -81
  65. physioex-1.0.0.dev4/docs/pages/data.md +0 -138
  66. physioex-1.0.0.dev4/docs/pages/preprocess.md +0 -289
  67. physioex-1.0.0.dev4/docs/pages/train/cli.md +0 -34
  68. physioex-1.0.0.dev4/docs/pages/train/networks/chambon2018.md +0 -24
  69. physioex-1.0.0.dev4/docs/pages/train/networks.md +0 -79
  70. physioex-1.0.0.dev4/docs/pages/train/train.md +0 -171
  71. physioex-1.0.0.dev4/docs/pages/unavailable.md +0 -1
  72. physioex-1.0.0.dev4/docs/stylesheets/extra.css +0 -3
  73. physioex-1.0.0.dev4/examples/basic_usage.ipynb +0 -1040
  74. physioex-1.0.0.dev4/examples/spectral_gradients.ipynb +0 -572
  75. physioex-1.0.0.dev4/examples/tesi_sostegni.ipynb +0 -332
  76. physioex-1.0.0.dev4/examples/utils.py +0 -24
  77. physioex-1.0.0.dev4/examples/visualize_data.ipynb +0 -143
  78. physioex-1.0.0.dev4/mkdocs.yml +0 -102
  79. physioex-1.0.0.dev4/physioex/data/datamodule.py +0 -108
  80. physioex-1.0.0.dev4/physioex/explain/ari_explainer.py +0 -260
  81. physioex-1.0.0.dev4/physioex/explain/base.py +0 -71
  82. physioex-1.0.0.dev4/physioex/explain/freq_bands_explainer.py +0 -865
  83. physioex-1.0.0.dev4/physioex/explain/metrics/__init__.py +0 -2
  84. physioex-1.0.0.dev4/physioex/explain/metrics/base.py +0 -0
  85. physioex-1.0.0.dev4/physioex/explain/metrics/infidelity.py +0 -130
  86. physioex-1.0.0.dev4/physioex/explain/metrics/sensitivity.py +0 -0
  87. physioex-1.0.0.dev4/physioex/explain/spectralgradients/__init__.py +0 -3
  88. physioex-1.0.0.dev4/physioex/explain/spectralgradients/explainer.py +0 -198
  89. physioex-1.0.0.dev4/physioex/explain/spectralgradients/importance.py +0 -203
  90. physioex-1.0.0.dev4/physioex/explain/spectralgradients/spectral_gradients.py +0 -111
  91. physioex-1.0.0.dev4/physioex/explain/spectralgradients/utils.py +0 -19
  92. physioex-1.0.0.dev4/physioex/explain/spectralgradients/viz.py +0 -249
  93. physioex-1.0.0.dev4/physioex/preprocess/.future/cap/preprocessing.py +0 -226
  94. physioex-1.0.0.dev4/physioex/preprocess/.future/config/create_dreem.py +0 -55
  95. physioex-1.0.0.dev4/physioex/preprocess/.future/config/dreem.yaml +0 -899
  96. physioex-1.0.0.dev4/physioex/preprocess/.future/config/mit-bih.yaml +0 -71
  97. physioex-1.0.0.dev4/physioex/preprocess/.future/config/sleep-edf.yaml +0 -40
  98. physioex-1.0.0.dev4/physioex/preprocess/.future/dreem/dreem.py +0 -54
  99. physioex-1.0.0.dev4/physioex/preprocess/.future/dreem/preprocess.py +0 -158
  100. physioex-1.0.0.dev4/physioex/preprocess/.future/dreem/utils.py +0 -59
  101. physioex-1.0.0.dev4/physioex/preprocess/.future/isruc/isruc.py +0 -52
  102. physioex-1.0.0.dev4/physioex/preprocess/.future/isruc/preprocess.py +0 -231
  103. physioex-1.0.0.dev4/physioex/preprocess/.future/mitdb.py +0 -236
  104. physioex-1.0.0.dev4/physioex/preprocess/.future/physio2018/checksum.csv +0 -2982
  105. physioex-1.0.0.dev4/physioex/preprocess/.future/physio2018/download.py +0 -147
  106. physioex-1.0.0.dev4/physioex/preprocess/.future/physio2018/physio2018.py +0 -53
  107. physioex-1.0.0.dev4/physioex/preprocess/.future/physio2018/preprocess.py +0 -158
  108. physioex-1.0.0.dev4/physioex/preprocess/.future/sleep_edf/constant.py +0 -95
  109. physioex-1.0.0.dev4/physioex/preprocess/.future/sleep_edf/preprocess.py +0 -162
  110. physioex-1.0.0.dev4/physioex/preprocess/.future/sleep_edf/sleep_edf.py +0 -52
  111. physioex-1.0.0.dev4/physioex/preprocess/.future/sleep_edf/subjects.xls +0 -0
  112. physioex-1.0.0.dev4/physioex/preprocess/.future/svuh/constant.py +0 -6
  113. physioex-1.0.0.dev4/physioex/preprocess/.future/svuh/preprocess.py +0 -222
  114. physioex-1.0.0.dev4/physioex/preprocess/.future/svuh/svuh.py +0 -54
  115. physioex-1.0.0.dev4/physioex/preprocess/__init__.py +0 -0
  116. physioex-1.0.0.dev4/physioex/train/bin/__init__.py +0 -0
  117. physioex-1.0.0.dev4/physioex/train/models/backup/chambon-EEG-EOG-EMG-L=21-test_acc=0.867.ckpt +0 -0
  118. physioex-1.0.0.dev4/physioex/train/models/backup/chambon-EEG-L=21-test_acc=0.843.ckpt +0 -0
  119. physioex-1.0.0.dev4/physioex/train/models/backup/seqsleepnet-EEG-EOG-EMG-L=21-test_acc=0.877.ckpt +0 -0
  120. physioex-1.0.0.dev4/physioex/train/models/backup/seqsleepnet-EEG-L=21-test_acc=0.864.ckpt +0 -0
  121. physioex-1.0.0.dev4/physioex/train/models/backup/tiny-EEG-EOG-EMG-L=21-test_acc=0.885.ckpt +0 -0
  122. physioex-1.0.0.dev4/physioex/train/models/backup/tiny-EEG-L=21-test_acc=0.871.ckpt +0 -0
  123. physioex-1.0.0.dev4/physioex/train/models/check_table.csv +0 -7
  124. physioex-1.0.0.dev4/physioex/train/models/checkpoints/chambon-EEG-EOG-EMG-L=21-test_acc=0.867.ckpt +0 -0
  125. physioex-1.0.0.dev4/physioex/train/models/checkpoints/chambon-EEG-L=21-test_acc=0.843.ckpt +0 -0
  126. physioex-1.0.0.dev4/physioex/train/models/checkpoints/seqsleepnet-EEG-EOG-EMG-L=21-test_acc=0.877.ckpt +0 -0
  127. physioex-1.0.0.dev4/physioex/train/models/checkpoints/seqsleepnet-EEG-L=21-test_acc=0.864.ckpt +0 -0
  128. physioex-1.0.0.dev4/physioex/train/models/checkpoints/tiny-EEG-EOG-EMG-L=21-test_acc=0.885.ckpt +0 -0
  129. physioex-1.0.0.dev4/physioex/train/models/checkpoints/tiny-EEG-L=21-test_acc=0.871.ckpt +0 -0
  130. physioex-1.0.0.dev4/physioex/train/networks/config/chambon2018.yaml +0 -11
  131. physioex-1.0.0.dev4/physioex/train/networks/config/finetuned.yaml +0 -2
  132. physioex-1.0.0.dev4/physioex/train/networks/config/multisource.yaml +0 -2
  133. physioex-1.0.0.dev4/physioex/train/networks/config/seqecgnet.yaml +0 -12
  134. physioex-1.0.0.dev4/physioex/train/networks/config/seqsleepnet.yaml +0 -22
  135. physioex-1.0.0.dev4/physioex/train/networks/config/sleeptransformer.yaml +0 -22
  136. physioex-1.0.0.dev4/physioex/train/networks/config/tinysleepnet.yaml +0 -22
  137. physioex-1.0.0.dev4/physioex/train/networks/config.yaml +0 -34
  138. physioex-1.0.0.dev4/physioex/train/networks/utils/filterbank_shape.py +0 -107
  139. physioex-1.0.0.dev4/physioex.egg-info/SOURCES.txt +0 -144
  140. physioex-1.0.0.dev4/requirements.txt +0 -31
  141. {physioex-1.0.0.dev4 → physioex-1.0.3}/LICENSE +0 -0
  142. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex/data/__init__.py +0 -0
  143. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex/explain/__init__.py +0 -0
  144. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex/preprocess/bin/compress_datasets.py +0 -0
  145. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex/preprocess/dcsm.py +0 -0
  146. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex/preprocess/hmc.py +0 -0
  147. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex/preprocess/mass.py +0 -0
  148. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex/train/__init__.py +0 -0
  149. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex/train/models/__init__.py +0 -0
  150. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex/train/networks/chambon2018.py +0 -0
  151. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex/train/networks/seqsleepnet.py +0 -0
  152. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex/train/networks/tinysleepnet.py +0 -0
  153. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex/train/networks/utils/target_transform.py +0 -0
  154. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex/train/utils/__init__.py +0 -0
  155. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex/train/utils/finetune.py +0 -0
  156. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex.egg-info/dependency_links.txt +0 -0
  157. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex.egg-info/entry_points.txt +0 -0
  158. {physioex-1.0.0.dev4 → physioex-1.0.3}/physioex.egg-info/top_level.txt +0 -0
  159. {physioex-1.0.0.dev4 → physioex-1.0.3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: physioex
3
- Version: 1.0.0.dev4
3
+ Version: 1.0.3
4
4
  Summary: A python package for explainable sleep staging via deep learning
5
5
  Author-email: Guido Gagliardi <guido.gagliardi@phd.unipi.it>
6
6
  License: MIT License
@@ -31,6 +31,7 @@ Project-URL: Repository, https://github.com/guidogagl/physioex
31
31
  Project-URL: Issues, https://github.com/guidogagl/physioex/issues
32
32
  Description-Content-Type: text/markdown
33
33
  License-File: LICENSE
34
+ Requires-Dist: tensorboard
34
35
  Requires-Dist: boto3
35
36
  Requires-Dist: botocore
36
37
  Requires-Dist: braindecode
@@ -61,6 +62,7 @@ Requires-Dist: torch
61
62
  Requires-Dist: torchmetrics
62
63
  Requires-Dist: tqdm
63
64
  Requires-Dist: wfdb
65
+ Requires-Dist: gdown
64
66
 
65
67
  <div style = "text-align: center;">
66
68
  <img src="https://raw.githubusercontent.com/guidogagl/physioex/refs/heads/main/docs/assets/images/logo.svg" width = "250px", alt="PhysioEx Logo">
@@ -82,17 +84,32 @@ With PhysioEx you can simulate a state-of-the-art experiment just running the `t
82
84
  - [Chambon2018](https://ieeexplore.ieee.org/document/8307462) model for sleep stage classification ( raw time series as input).
83
85
  - [TinySleepNet](https://github.com/akaraspt/tinysleepnet) model for sleep stage classification (raw time series as input).
84
86
  - [SeqSleepNet](https://arxiv.org/pdf/1809.10932.pdf) model for sleep stage classification (time-frequency images as input).
87
+ - [SleepTransformer](https://arxiv.org/pdf/2105.11043) model for sleep stage classification (time-frequency images as input).
85
88
 
86
89
  ## Supported datasets
87
90
 
88
- - [SHHS (Sleep Heart Health Study)](https://sleepdata.org/datasets/shhs): A multi-center cohort study designed to investigate the cardiovascular consequences of sleep-disordered breathing.
89
- - [MROS (MrOS Sleep Study)](https://sleepdata.org/datasets/mros): A study focusing on the outcomes of sleep disorders in older men.
90
- - [MESA (Multi-Ethnic Study of Atherosclerosis)](https://sleepdata.org/datasets/mesa): A study examining the prevalence, correlates, and progression of subclinical cardiovascular disease.
91
- - [DCSM (Dreem Challenge Sleep Monitoring)](https://physionet.org/content/dreem/1.0.0/): A dataset from the Dreem Challenge for automatic sleep staging.
92
- - [MASS (Montreal Archive of Sleep Studies)](https://massdb.herokuapp.com/en/): A comprehensive collection of polysomnographic sleep recordings.
93
- - [HMC (Home Monitoring of Cardiorespiratory Health)](https://physionet.org/content/hmc-kinematics/1.0.0/): A dataset for the study of cardiorespiratory health using home monitoring devices.
91
+ ### Publicly Available:
92
+
93
+ For these datasets PhysioEx will take also to download the data with the `preprocess` command.
94
+
95
+ - [Sleep-EDF(78)](https://physionet.org/content/sleep-edfx/1.0.0/), The sleep-edf database contains 197 whole-night PolySomnoGraphic sleep recordings, containing EEG, EOG, chin EMG, and event markers.
96
+ - [HMC (Haaglanden Medisch Centrum)](https://physionet.org/content/hmc-sleep-staging/1.1/), is a collection of 151 whole-night PSG recordings from 85 men and 66 women, gathered at the Haaglanden Medisch Centrum sleep center. The PSG data includes 4 EEG channels (F4/M1, C4/M1, O2/M1, and C3/M2), two EOG channels (E1/M2 and E2/M2), and one bipolar chin EMG, with all signals sampled at 256 Hz.
97
+ - [DCSM (Danish Center for Sleep Medicine)](https://erda.ku.dk/public/archives/db553715ecbe1f3ac66c1dc569826eef/published-archive.html), is a collection of 255 randomly selected and fully anonymized overnight lab-based PSG recordings from patients seeking diagnosis for non-specific sleep-related disorders at the DCSM. The PSG setup included EEG, EOG, and EMG channels, all sampled at 256 Hz.
98
+
99
+
100
+ ### [NSSR](https://sleepdata.org) Datasets
101
+
102
+ These datasets can be easily get from the NSSR archive. Once downloaded in your `data_folder`, place them into the folder `data_folder/dataset_name/` directory with name `dataset_raw`. Then you can run the `preprocess -d dataset_name -df data_folder` command to make the data readable by PhysioEx.
103
+
104
+ - [SHHS (Sleep Heart Health Study)](https://sleepdata.org/datasets/shhs), is a multi-center cohort study designed to investigate the cardiovascular and other consequences of sleep-disordered breathing. At visit 1, it included 5,793 participants aged 40 years or older. PSG recordings were typically conducted in the subjects' homes by trained and certified technicians. The recording montage included C3/A2 and C4/A1 EEGs sampled at 125 Hz, right and left EOGs sampled at 50 Hz, and a bipolar submental EMG sampled at 125 Hz.
105
+ - [MESA (Multi-Ethnic Study of Atherosclerosis)](https://sleepdata.org/datasets/mesa), is a multi-center prospective study of 2.237 ethnically diverse men and women aged 45-84 from six communities in the United States. PSGs recordings were obtained using in-home settings including central C4-M1 EEG, bilateral EOG and chin EMG sampled at 256Hz. PSGs were scored by one of 3 MESA certified, registered polysomnologists.
106
+ - [MrOS (The Osteoporotic Fractures in Men Study)](https://sleepdata.org/datasets/mros), is a multicenter study comprising 2,911 PSG recordings from men aged 65 years or older, enrolled at six clinical centers. PSG recordings were conducted in home settings and included C3/A2 and C4/A1 EEGs, chin EMG, and left-right EOG, all sampled at 256 Hz.
107
+ - [WSC (The Wisconsin Sleep Cohort)](https://sleepdata.org/datasets/wsc) A longitudinal study of the causes, consequences, and natural history of sleep disorders using overnight in-laboratory sleep recordings gathered at the University of Wisconsin, United States, with a baseline sample of 1,500 subjects assessed at four-year intervals. The study consists of multiple visits with overnight PSG data acquisition. PSG recordings included C3/M2 EEG, EMG, and left-right EOG, all sampled at 200 Hz.
108
+
109
+ ### Others
110
+
111
+ - [MASS (Montreal Archive of Sleep Studies)](http://ceams-carsm.ca/mass/), is an open-access collaborative database containing laboratory-based PSG recordings. It includes 200 complete nights recorded from 97 men and 103 women, aged 18 to 76 years. All recordings have a sampling frequency of 256 Hz and feature an EEG montage of 4–20 channels, along with standard EOG and EMG
94
112
 
95
- For the public available datasets ( DCSM, HMC ) PhysioEx takes care of automatically download the data thanks to the `preprocess` command. The other datasets needs to be acquired first ( mostly on [NSSR](https://sleepdata.org) ) and then fetched by PhysioEx via the `preprocess` command.
96
113
 
97
114
  ## Installation guidelines
98
115
 
@@ -104,14 +121,8 @@ For the public available datasets ( DCSM, HMC ) PhysioEx takes care of automatic
104
121
  conda install pip
105
122
  pip install --upgrade pip # On Windows, use `venv\Scripts\activate`
106
123
  ```
107
- ### Install via pip
108
124
 
109
- 1. **Install PhysioEx from PyPI:**
110
- ```bash
111
- pip install physioex
112
- ```
113
-
114
- ### Install from source
125
+ ### Install from source ( Recommended )
115
126
  1. **Clone the Repository:**
116
127
  ```bash
117
128
  git clone https://github.com/guidogagl/physioex.git
@@ -122,3 +133,23 @@ pip install physioex
122
133
  ```bash
123
134
  pip install -e .
124
135
  ```
136
+
137
+ ### Install via pip
138
+
139
+ 1. **Install PhysioEx from PyPI:**
140
+ ```bash
141
+ pip install physioex
142
+ ```
143
+
144
+ Note: the github version of the library is kept updated weekly, the PiPy version may be outdated depending on the last commit of the github version. We recommend to use the github version if possible.
145
+
146
+ ## Cite Us!
147
+ ```bib
148
+ @article{10.1088/1361-6579/adaf73,
149
+ author={Gagliardi, Guido and Alfeo, Luca and Cimino, Mario G C A and Valenza, Gaetano and De Vos, Maarten},
150
+ title={PhysioEx, a new Python library for explainable sleep staging through deep learning},
151
+ journal={Physiological Measurement},
152
+ url={http://iopscience.iop.org/article/10.1088/1361-6579/adaf73},
153
+ year={2025},
154
+ }
155
+ ```
@@ -0,0 +1,89 @@
1
+ <div style = "text-align: center;">
2
+ <img src="https://raw.githubusercontent.com/guidogagl/physioex/refs/heads/main/docs/assets/images/logo.svg" width = "250px", alt="PhysioEx Logo">
3
+
4
+ <h1> PhysioEx </h1>
5
+ </div>
6
+
7
+ ![Python Version](https://img.shields.io/badge/python-3.7%2B-blue)
8
+ ![PyPI Version](https://badge.fury.io/py/physioex.svg)
9
+
10
+ **PhysioEx ( Physiological Signal Explainer )** is a versatile python library tailored for building, training, and explaining deep learning models for physiological signal analysis.
11
+
12
+ The main purpose of the library is to propose a standard and fast methodology to train and evalutate state-of-the-art deep learning architectures for physiological signal analysis, to shift the attention from the architecture building task to the explainability task.
13
+
14
+ With PhysioEx you can simulate a state-of-the-art experiment just running the `train`, `test_model` and `finetune` commands; evaluating and saving the trained model; and start focusing on the explainability task!
15
+
16
+ ## Supported deep learning architectures
17
+
18
+ - [Chambon2018](https://ieeexplore.ieee.org/document/8307462) model for sleep stage classification ( raw time series as input).
19
+ - [TinySleepNet](https://github.com/akaraspt/tinysleepnet) model for sleep stage classification (raw time series as input).
20
+ - [SeqSleepNet](https://arxiv.org/pdf/1809.10932.pdf) model for sleep stage classification (time-frequency images as input).
21
+ - [SleepTransformer](https://arxiv.org/pdf/2105.11043) model for sleep stage classification (time-frequency images as input).
22
+
23
+ ## Supported datasets
24
+
25
+ ### Publicly Available:
26
+
27
+ For these datasets PhysioEx will take also to download the data with the `preprocess` command.
28
+
29
+ - [Sleep-EDF(78)](https://physionet.org/content/sleep-edfx/1.0.0/), The sleep-edf database contains 197 whole-night PolySomnoGraphic sleep recordings, containing EEG, EOG, chin EMG, and event markers.
30
+ - [HMC (Haaglanden Medisch Centrum)](https://physionet.org/content/hmc-sleep-staging/1.1/), is a collection of 151 whole-night PSG recordings from 85 men and 66 women, gathered at the Haaglanden Medisch Centrum sleep center. The PSG data includes 4 EEG channels (F4/M1, C4/M1, O2/M1, and C3/M2), two EOG channels (E1/M2 and E2/M2), and one bipolar chin EMG, with all signals sampled at 256 Hz.
31
+ - [DCSM (Danish Center for Sleep Medicine)](https://erda.ku.dk/public/archives/db553715ecbe1f3ac66c1dc569826eef/published-archive.html), is a collection of 255 randomly selected and fully anonymized overnight lab-based PSG recordings from patients seeking diagnosis for non-specific sleep-related disorders at the DCSM. The PSG setup included EEG, EOG, and EMG channels, all sampled at 256 Hz.
32
+
33
+
34
+ ### [NSSR](https://sleepdata.org) Datasets
35
+
36
+ These datasets can be easily get from the NSSR archive. Once downloaded in your `data_folder`, place them into the folder `data_folder/dataset_name/` directory with name `dataset_raw`. Then you can run the `preprocess -d dataset_name -df data_folder` command to make the data readable by PhysioEx.
37
+
38
+ - [SHHS (Sleep Heart Health Study)](https://sleepdata.org/datasets/shhs), is a multi-center cohort study designed to investigate the cardiovascular and other consequences of sleep-disordered breathing. At visit 1, it included 5,793 participants aged 40 years or older. PSG recordings were typically conducted in the subjects' homes by trained and certified technicians. The recording montage included C3/A2 and C4/A1 EEGs sampled at 125 Hz, right and left EOGs sampled at 50 Hz, and a bipolar submental EMG sampled at 125 Hz.
39
+ - [MESA (Multi-Ethnic Study of Atherosclerosis)](https://sleepdata.org/datasets/mesa), is a multi-center prospective study of 2.237 ethnically diverse men and women aged 45-84 from six communities in the United States. PSGs recordings were obtained using in-home settings including central C4-M1 EEG, bilateral EOG and chin EMG sampled at 256Hz. PSGs were scored by one of 3 MESA certified, registered polysomnologists.
40
+ - [MrOS (The Osteoporotic Fractures in Men Study)](https://sleepdata.org/datasets/mros), is a multicenter study comprising 2,911 PSG recordings from men aged 65 years or older, enrolled at six clinical centers. PSG recordings were conducted in home settings and included C3/A2 and C4/A1 EEGs, chin EMG, and left-right EOG, all sampled at 256 Hz.
41
+ - [WSC (The Wisconsin Sleep Cohort)](https://sleepdata.org/datasets/wsc) A longitudinal study of the causes, consequences, and natural history of sleep disorders using overnight in-laboratory sleep recordings gathered at the University of Wisconsin, United States, with a baseline sample of 1,500 subjects assessed at four-year intervals. The study consists of multiple visits with overnight PSG data acquisition. PSG recordings included C3/M2 EEG, EMG, and left-right EOG, all sampled at 200 Hz.
42
+
43
+ ### Others
44
+
45
+ - [MASS (Montreal Archive of Sleep Studies)](http://ceams-carsm.ca/mass/), is an open-access collaborative database containing laboratory-based PSG recordings. It includes 200 complete nights recorded from 97 men and 103 women, aged 18 to 76 years. All recordings have a sampling frequency of 256 Hz and feature an EEG montage of 4–20 channels, along with standard EOG and EMG
46
+
47
+
48
+ ## Installation guidelines
49
+
50
+ ### Create a Virtual Environment (Optional but Recommended)
51
+
52
+ ```bash
53
+ conda create -n physioex python==3.10
54
+ conda activate physioex
55
+ conda install pip
56
+ pip install --upgrade pip # On Windows, use `venv\Scripts\activate`
57
+ ```
58
+
59
+ ### Install from source ( Recommended )
60
+ 1. **Clone the Repository:**
61
+ ```bash
62
+ git clone https://github.com/guidogagl/physioex.git
63
+ cd physioex
64
+ ```
65
+
66
+ 2. **Install Dependencies and Package in Development Mode**
67
+ ```bash
68
+ pip install -e .
69
+ ```
70
+
71
+ ### Install via pip
72
+
73
+ 1. **Install PhysioEx from PyPI:**
74
+ ```bash
75
+ pip install physioex
76
+ ```
77
+
78
+ Note: the github version of the library is kept updated weekly, the PiPy version may be outdated depending on the last commit of the github version. We recommend to use the github version if possible.
79
+
80
+ ## Cite Us!
81
+ ```bib
82
+ @article{10.1088/1361-6579/adaf73,
83
+ author={Gagliardi, Guido and Alfeo, Luca and Cimino, Mario G C A and Valenza, Gaetano and De Vos, Maarten},
84
+ title={PhysioEx, a new Python library for explainable sleep staging through deep learning},
85
+ journal={Physiological Measurement},
86
+ url={http://iopscience.iop.org/article/10.1088/1361-6579/adaf73},
87
+ year={2025},
88
+ }
89
+ ```
@@ -0,0 +1,109 @@
1
+ import os
2
+ from typing import Callable, List, Union
3
+
4
+ import pytorch_lightning as pl
5
+ from torch.utils.data import DataLoader, DistributedSampler, Subset, SubsetRandomSampler
6
+
7
+ from physioex.data.dataset import PhysioExDataset
8
+
9
+
10
+ class PhysioExDataModule(pl.LightningDataModule):
11
+ def __init__(
12
+ self,
13
+ datasets: Union[List[str], PhysioExDataset],
14
+ batch_size: int = 32,
15
+ preprocessing: str = "raw",
16
+ selected_channels: List[int] = ["EEG"],
17
+ sequence_length: int = 21,
18
+ target_transform: Callable = None,
19
+ task: str = "sleep",
20
+ folds: Union[int, List[int]] = -1,
21
+ data_folder: str = None,
22
+ evaluate_on_whole_night: bool = False,
23
+ num_nodes : int = 1,
24
+ num_workers: int = os.cpu_count(),
25
+ ):
26
+ super().__init__()
27
+
28
+ self.datasets_id = datasets
29
+ self.num_workers = num_workers
30
+
31
+ if isinstance(datasets, list):
32
+ self.dataset = PhysioExDataset(
33
+ datasets=datasets,
34
+ preprocessing=preprocessing,
35
+ selected_channels=selected_channels,
36
+ sequence_length=sequence_length,
37
+ target_transform=target_transform,
38
+ data_folder=data_folder,
39
+ task=task,
40
+ )
41
+
42
+ if evaluate_on_whole_night:
43
+ self.eval_dataset = PhysioExDataset(
44
+ datasets=datasets,
45
+ preprocessing=preprocessing,
46
+ selected_channels=selected_channels,
47
+ sequence_length=-1,
48
+ target_transform=target_transform,
49
+ data_folder=data_folder,
50
+ task=task,
51
+ )
52
+ else:
53
+ self.eval_dataset = self.dataset
54
+
55
+ elif isinstance(datasets, PhysioExDataset):
56
+ self.dataset = datasets
57
+ self.eval_dataset = datasets
58
+ else:
59
+ raise ValueError("ERR: datasets should be a list or a PhysioExDataset")
60
+
61
+ self.batch_size = batch_size
62
+
63
+ if isinstance(folds, int):
64
+ self.dataset.split(folds)
65
+ else:
66
+ assert len(folds) == len(
67
+ datasets
68
+ ), "ERR: folds and datasets should have the same length"
69
+ for i, fold in enumerate(folds):
70
+ self.dataset.split(fold, i)
71
+
72
+ train_idx, _, _ = self.dataset.get_sets()
73
+ _, valid_idx, test_idx = self.eval_dataset.get_sets()
74
+
75
+ self.train_dataset = Subset(self.dataset, train_idx)
76
+ self.valid_dataset = Subset(self.eval_dataset, valid_idx)
77
+ self.test_dataset = Subset(self.eval_dataset, test_idx)
78
+
79
+ self.eown = evaluate_on_whole_night
80
+
81
+ def train_dataloader(self):
82
+ """
83
+ Returns the DataLoader for the training dataset.
84
+
85
+ Returns:
86
+ DataLoader: DataLoader for the training dataset.
87
+ """
88
+ return DataLoader(
89
+ self.train_dataset,
90
+ batch_size=self.batch_size,
91
+ shuffle=True,
92
+ num_workers=self.num_workers,
93
+ )
94
+
95
+ def val_dataloader(self):
96
+ return DataLoader(
97
+ self.valid_dataset,
98
+ batch_size=self.batch_size if not self.eown else 1,
99
+ shuffle=False,
100
+ num_workers=self.num_workers,
101
+ )
102
+
103
+ def test_dataloader(self):
104
+ return DataLoader(
105
+ self.test_dataset,
106
+ batch_size=self.batch_size if not self.eown else 1,
107
+ shuffle=False,
108
+ num_workers=self.num_workers,
109
+ )
@@ -24,6 +24,8 @@ class Reader(ABC):
24
24
  pass
25
25
 
26
26
 
27
+
28
+
27
29
  class MemmapReader(Reader):
28
30
  # this object abstracts the reading of subject data from memmap files for a specific dataset
29
31
  def __init__(
@@ -59,7 +61,19 @@ class MemmapReader(Reader):
59
61
  num_windows = self.table["num_windows"].values
60
62
  subjects_id = self.table["subject_id"].values
61
63
 
62
- self.len = int(np.sum(self.table["num_windows"].values - self.L + 1))
64
+ if self.L > np.max(num_windows):
65
+ logger.warning(
66
+ f"Sequence length {self.L} is greater than the max number of windows {np.max(num_windows)} for dataset {dataset}."
67
+ )
68
+
69
+ self.len = num_windows - self.L
70
+
71
+ neg = np.where(self.len < 0)[0]
72
+ if len(neg) > 0:
73
+ self.len[neg] = 0
74
+
75
+ self.len = int(np.sum(self.len + 1))
76
+
63
77
  self.subject_idx, self.relative_idx, self.windows_index = build_index(
64
78
  num_windows, subjects_id, self.L
65
79
  )
@@ -71,7 +85,7 @@ class MemmapReader(Reader):
71
85
  def __len__(self):
72
86
  return self.len
73
87
 
74
- def __getitem__(self, idx):
88
+ def get_signal(self, idx):
75
89
  idx = idx - self.offset
76
90
 
77
91
  relative_id = self.relative_idx[idx]
@@ -79,20 +93,136 @@ class MemmapReader(Reader):
79
93
  num_windows = self.windows_index[subject_id]
80
94
 
81
95
  input_shape = tuple([num_windows] + self.input_shape)
82
- labels_shape = (num_windows,)
83
96
 
84
97
  data_path = os.path.join(self.data_path, str(subject_id) + ".npy")
85
- labels_path = os.path.join(self.labels_path, str(subject_id) + ".npy")
86
98
 
87
99
  X = np.memmap(data_path, dtype="float32", mode="r", shape=input_shape)
100
+
101
+ if relative_id + self.L > num_windows:
102
+ X = X[relative_id:, self.channels_index]
103
+
104
+ remainer = self.L - X.shape[0]
105
+ # add zeros to the end of the array
106
+ X = np.concatenate([X, np.zeros((remainer, *X.shape[1:]))], axis=0)
107
+
108
+ else:
109
+ X = X[relative_id : relative_id + self.L, self.channels_index]
110
+
111
+ X = (torch.tensor(X).float() - self.mean) / self.std
112
+
113
+ return X
114
+
115
+ def get_stages(self, idx):
116
+ idx = idx - self.offset
117
+
118
+ relative_id = self.relative_idx[idx]
119
+ subject_id = self.subject_idx[idx]
120
+ num_windows = self.windows_index[subject_id]
121
+
122
+ labels_shape = (num_windows,)
123
+
124
+ labels_path = os.path.join(self.labels_path, str(subject_id) + ".npy")
125
+
88
126
  y = np.memmap(labels_path, dtype="int16", mode="r", shape=labels_shape)
89
127
 
90
- X = X[relative_id : relative_id + self.L, self.channels_index]
91
- y = y[relative_id : relative_id + self.L]
128
+ if relative_id + self.L > num_windows:
129
+ y = y[relative_id:]
130
+
131
+ remainer = self.L - len(y)
132
+ # associate the padded values to the class 6
133
+ y = np.concatenate([y, np.ones(remainer) * 5], axis=0)
134
+ else:
135
+ y = y[relative_id : relative_id + self.L]
136
+
137
+ y = torch.tensor(y).long()
138
+
139
+ return y
140
+
141
+ def __getitem__(self, idx):
142
+
143
+ X = self.get_signal(idx)
144
+ y = self.get_stages(idx)
145
+
146
+ return X, y
147
+
148
+ class WholeNightReader(MemmapReader):
149
+ # suppose the batch_size to be 1
150
+ # reads every time one night
151
+ def __init__(
152
+ self,
153
+ data_folder: str,
154
+ dataset: str,
155
+ preprocessing: str,
156
+ #sequence_length: int,
157
+ channels_index: List[int],
158
+ offset: int,
159
+ ):
160
+ super().__init__(
161
+ data_folder = data_folder,
162
+ dataset = dataset,
163
+ preprocessing = preprocessing,
164
+ sequence_length = 30 * 2 * 60 * 24, # 24 hours,
165
+ channels_index = channels_index,
166
+ offset = offset
167
+ )
168
+
169
+ def get_signal(self, idx):
170
+ idx = idx - self.offset
92
171
 
172
+ subject_id = self.subject_idx[idx]
173
+ num_windows = self.windows_index[subject_id]
174
+
175
+ input_shape = tuple([num_windows] + self.input_shape)
176
+
177
+ data_path = os.path.join(self.data_path, str(subject_id) + ".npy")
178
+
179
+ X = np.memmap(data_path, dtype="float32", mode="r", shape=input_shape)
180
+ X = X[:, self.channels_index]
93
181
  X = (torch.tensor(X).float() - self.mean) / self.std
182
+
183
+ return X
184
+
185
+ def get_stages(self, idx):
186
+ idx = idx - self.offset
187
+
188
+ subject_id = self.subject_idx[idx]
189
+ num_windows = self.windows_index[subject_id]
190
+
191
+ labels_shape = (num_windows,)
192
+
193
+ labels_path = os.path.join(self.labels_path, str(subject_id) + ".npy")
194
+
195
+ y = np.memmap(labels_path, dtype="int16", mode="r", shape=labels_shape)
196
+
94
197
  y = torch.tensor(y).long()
95
198
 
199
+ return y
200
+
201
+ class AgeMemmapReader(MemmapReader):
202
+ def __init__(
203
+ self,
204
+ data_folder: str,
205
+ dataset: str,
206
+ preprocessing: str,
207
+ sequence_length: int,
208
+ channels_index: List[int],
209
+ offset: int,
210
+ ):
211
+ super().__init__(
212
+ data_folder, dataset, preprocessing, sequence_length, channels_index, offset
213
+ )
214
+
215
+ def __getitem__(self, idx):
216
+ idx = idx - self.offset
217
+ subject_id = self.subject_idx[idx]
218
+ age = self.table.loc[
219
+ self.table["subject_id"] == subject_id, "nsrr_age"
220
+ ].values.astype(float)[0]
221
+
222
+ y = torch.tensor([age], dtype=torch.float32)
223
+
224
+ X = self.get_signal(idx)
225
+
96
226
  return X, y
97
227
 
98
228
 
@@ -180,16 +310,36 @@ class DataReader(Reader):
180
310
  channels_index: List[int],
181
311
  offset: int,
182
312
  hpc: bool,
313
+ task: str = "sleep",
183
314
  ):
184
-
185
- self.reader = MemmapReader(
186
- data_folder=data_folder,
187
- dataset=dataset,
188
- preprocessing=preprocessing,
189
- sequence_length=sequence_length,
190
- channels_index=channels_index,
191
- offset=offset,
192
- )
315
+ if task == "sleep" and sequence_length > 0:
316
+ self.reader = MemmapReader(
317
+ data_folder=data_folder,
318
+ dataset=dataset,
319
+ preprocessing=preprocessing,
320
+ sequence_length=sequence_length,
321
+ channels_index=channels_index,
322
+ offset=offset,
323
+ )
324
+ elif task == "sleep" and sequence_length == -1:
325
+ self.reader = WholeNightReader(
326
+ data_folder=data_folder,
327
+ dataset=dataset,
328
+ preprocessing=preprocessing,
329
+ channels_index=channels_index,
330
+ offset=offset,
331
+ )
332
+ elif task == "age":
333
+ self.reader = AgeMemmapReader(
334
+ data_folder=data_folder,
335
+ dataset=dataset,
336
+ preprocessing=preprocessing,
337
+ sequence_length=sequence_length,
338
+ channels_index=channels_index,
339
+ offset=offset,
340
+ )
341
+ else:
342
+ raise ValueError("task must be either sleep or age")
193
343
 
194
344
  def __len__(self):
195
345
  return self.reader.__len__()
@@ -204,9 +354,19 @@ class DataReader(Reader):
204
354
  def get_table(self):
205
355
  return self.reader.get_table()
206
356
 
357
+ def get_sequence_length(self):
358
+ return self.reader.L
359
+
207
360
 
208
361
  def build_index(nums_windows, subjects_ids, sequence_length):
209
- data_len = int(np.sum(nums_windows - sequence_length + 1))
362
+ nums_windows = nums_windows.astype(int)
363
+
364
+ data_len = nums_windows - sequence_length
365
+ neg = np.where(data_len < 0)[0]
366
+ if len(neg) > 0:
367
+ data_len[neg] = 0
368
+
369
+ data_len = np.sum(data_len + 1)
210
370
 
211
371
  subject_idx = np.zeros(data_len, dtype=np.uint16)
212
372
  relative_idx = np.zeros(data_len, dtype=np.uint16)
@@ -221,19 +381,19 @@ def build_index(nums_windows, subjects_ids, sequence_length):
221
381
  f"subject_id {subject_id} exceeds the maximum value for np.uint16"
222
382
  )
223
383
 
224
- # Check if the relative_id can be stored in a uint16
225
- if num_windows - sequence_length + 1 > np.iinfo(np.uint16).max:
384
+ num_sequences = max((num_windows - sequence_length, 0)) + 1
385
+
386
+ if num_sequences > np.iinfo(np.uint16).max:
226
387
  raise ValueError(
227
- f"Relative index {num_windows - sequence_length + 1} exceeds the maximum value for np.uint16"
388
+ f"Relative index {num_sequences} exceeds the maximum value for np.uint16"
228
389
  )
229
390
 
230
- subject_idx[start_index : start_index + num_windows - sequence_length + 1] = (
231
- subject_id
232
- )
233
- relative_idx[start_index : start_index + num_windows - sequence_length + 1] = (
234
- np.arange(num_windows - sequence_length + 1)
391
+ subject_idx[start_index : start_index + num_sequences] = subject_id
392
+ relative_idx[start_index : start_index + num_sequences] = np.arange(
393
+ num_sequences
235
394
  )
236
- start_index += num_windows - sequence_length + 1
395
+
396
+ start_index += num_sequences
237
397
 
238
398
  windows_index = np.zeros(np.max(subjects_ids) + 1, dtype=np.uint16)
239
399
  for i, num_windows in enumerate(nums_windows):
@@ -22,9 +22,9 @@ class PhysioExDataset(torch.utils.data.Dataset):
22
22
  target_transform: Callable = None,
23
23
  hpc: bool = False,
24
24
  indexed_channels: List[int] = ["EEG", "EOG", "EMG", "ECG"],
25
+ task: str = "sleep",
25
26
  ):
26
27
  self.datasets = datasets
27
- self.L = sequence_length
28
28
  self.channels_index = [indexed_channels.index(ch) for ch in selected_channels]
29
29
 
30
30
  self.readers = []
@@ -41,6 +41,7 @@ class PhysioExDataset(torch.utils.data.Dataset):
41
41
  channels_index=self.channels_index,
42
42
  offset=offset,
43
43
  hpc=hpc,
44
+ task=task,
44
45
  )
45
46
  offset += len(reader)
46
47
 
@@ -55,6 +56,7 @@ class PhysioExDataset(torch.utils.data.Dataset):
55
56
  self.target_transform = target_transform
56
57
 
57
58
  self.len = offset
59
+ self.L = sequence_length if sequence_length != -1 else 30 * 2 * 60 * 24
58
60
 
59
61
  def __len__(self):
60
62
  return self.len
@@ -122,7 +124,7 @@ class PhysioExDataset(torch.utils.data.Dataset):
122
124
  for table in self.tables:
123
125
  for _, row in table.iterrows():
124
126
 
125
- num_windows = row["num_windows"] - self.L + 1
127
+ num_windows = max(row["num_windows"] - self.L, 0) + 1
126
128
 
127
129
  indices = np.arange(
128
130
  start=start_index, stop=start_index + num_windows