electropycal 0.9.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 (192) hide show
  1. electropycal-0.9.0/.gitignore +43 -0
  2. electropycal-0.9.0/CHANGELOG.md +56 -0
  3. electropycal-0.9.0/LICENSE +21 -0
  4. electropycal-0.9.0/PKG-INFO +156 -0
  5. electropycal-0.9.0/README.md +109 -0
  6. electropycal-0.9.0/demo/README.md +24 -0
  7. electropycal-0.9.0/demo/in_vitro/input/20260715_neurostring_channeltest/2-2_fscv_0nm.csv +0 -0
  8. electropycal-0.9.0/demo/in_vitro/input/20260715_neurostring_signal/2-2_eis_0nm.csv +0 -0
  9. electropycal-0.9.0/demo/in_vitro/input/20260715_neurostring_signal/2-2_fscv_0nm.csv +0 -0
  10. electropycal-0.9.0/demo/in_vitro/input/20260715_neurostring_signal/2-2_fscv_1000nm.csv +0 -0
  11. electropycal-0.9.0/demo/in_vitro/input/20260715_neurostring_signal/2-2_fscv_100nm.csv +0 -0
  12. electropycal-0.9.0/demo/in_vitro/input/20260715_neurostring_signal/2-2_fscv_5000nm.csv +0 -0
  13. electropycal-0.9.0/demo/in_vitro/input/20260715_neurostring_signal/2-2_fscv_500nm.csv +0 -0
  14. electropycal-0.9.0/demo/in_vitro/input/20260715_neurostring_signal/2-2_fscv_stabilization-full.csv +0 -0
  15. electropycal-0.9.0/demo/in_vitro/input/20260716_neurostring_channeltest/2-2_fscv_0nm.csv +0 -0
  16. electropycal-0.9.0/demo/in_vitro/input/20260716_neurostring_channeltest/2-3_fscv_0nm.csv +0 -0
  17. electropycal-0.9.0/demo/in_vitro/input/20260716_neurostring_signal/2-2_eis_0nm.csv +0 -0
  18. electropycal-0.9.0/demo/in_vitro/input/20260716_neurostring_signal/2-2_fscv_0nm.csv +0 -0
  19. electropycal-0.9.0/demo/in_vitro/input/20260716_neurostring_signal/2-2_fscv_1000nm.csv +0 -0
  20. electropycal-0.9.0/demo/in_vitro/input/20260716_neurostring_signal/2-2_fscv_100nm.csv +0 -0
  21. electropycal-0.9.0/demo/in_vitro/input/20260716_neurostring_signal/2-2_fscv_5000nm.csv +0 -0
  22. electropycal-0.9.0/demo/in_vitro/input/20260716_neurostring_signal/2-2_fscv_500nm.csv +0 -0
  23. electropycal-0.9.0/demo/in_vitro/input/20260716_neurostring_signal/2-2_fscv_stabilization-full.csv +0 -0
  24. electropycal-0.9.0/demo/in_vitro/input/20260716_neurostring_signal/2-3_eis_0nm.csv +0 -0
  25. electropycal-0.9.0/demo/in_vitro/input/20260716_neurostring_signal/2-3_fscv_0nm.csv +0 -0
  26. electropycal-0.9.0/demo/in_vitro/input/20260716_neurostring_signal/2-3_fscv_1000nm.csv +0 -0
  27. electropycal-0.9.0/demo/in_vitro/input/20260716_neurostring_signal/2-3_fscv_100nm.csv +0 -0
  28. electropycal-0.9.0/demo/in_vitro/input/20260716_neurostring_signal/2-3_fscv_5000nm.csv +0 -0
  29. electropycal-0.9.0/demo/in_vitro/input/20260716_neurostring_signal/2-3_fscv_500nm.csv +0 -0
  30. electropycal-0.9.0/demo/in_vitro/input/20260716_neurostring_signal/2-3_fscv_stabilization-full.csv +0 -0
  31. electropycal-0.9.0/demo/in_vitro/input/20260717_neurostring_channeltest/2-3_fscv_0nm.csv +0 -0
  32. electropycal-0.9.0/demo/in_vitro/input/20260717_neurostring_signal/2-3_eis_0nm.csv +0 -0
  33. electropycal-0.9.0/demo/in_vitro/input/20260717_neurostring_signal/2-3_fscv_0nm.csv +0 -0
  34. electropycal-0.9.0/demo/in_vitro/input/20260717_neurostring_signal/2-3_fscv_1000nm.csv +0 -0
  35. electropycal-0.9.0/demo/in_vitro/input/20260717_neurostring_signal/2-3_fscv_100nm.csv +0 -0
  36. electropycal-0.9.0/demo/in_vitro/input/20260717_neurostring_signal/2-3_fscv_5000nm.csv +0 -0
  37. electropycal-0.9.0/demo/in_vitro/input/20260717_neurostring_signal/2-3_fscv_500nm.csv +0 -0
  38. electropycal-0.9.0/demo/in_vitro/input/20260717_neurostring_signal/2-3_fscv_stabilization-full.csv +0 -0
  39. electropycal-0.9.0/demo/in_vitro/input/20260722_neurostring_channeltest/2-2_fscv_0nm.csv +0 -0
  40. electropycal-0.9.0/demo/in_vitro/input/20260722_neurostring_signal/2-2_eis_0nm.csv +0 -0
  41. electropycal-0.9.0/demo/in_vitro/input/20260722_neurostring_signal/2-2_fscv_0nm.csv +0 -0
  42. electropycal-0.9.0/demo/in_vitro/input/20260722_neurostring_signal/2-2_fscv_1000nm.csv +0 -0
  43. electropycal-0.9.0/demo/in_vitro/input/20260722_neurostring_signal/2-2_fscv_100nm.csv +0 -0
  44. electropycal-0.9.0/demo/in_vitro/input/20260722_neurostring_signal/2-2_fscv_5000nm.csv +0 -0
  45. electropycal-0.9.0/demo/in_vitro/input/20260722_neurostring_signal/2-2_fscv_500nm.csv +0 -0
  46. electropycal-0.9.0/demo/in_vitro/input/20260722_neurostring_signal/2-2_fscv_stabilization-full.csv +0 -0
  47. electropycal-0.9.0/demo/in_vitro/input/20260723_neurostring_channeltest/2-3_fscv_0nm.csv +0 -0
  48. electropycal-0.9.0/demo/in_vitro/input/20260723_neurostring_signal/2-3_eis_0nm.csv +0 -0
  49. electropycal-0.9.0/demo/in_vitro/input/20260723_neurostring_signal/2-3_fscv_0nm.csv +0 -0
  50. electropycal-0.9.0/demo/in_vitro/input/20260723_neurostring_signal/2-3_fscv_1000nm.csv +0 -0
  51. electropycal-0.9.0/demo/in_vitro/input/20260723_neurostring_signal/2-3_fscv_100nm.csv +0 -0
  52. electropycal-0.9.0/demo/in_vitro/input/20260723_neurostring_signal/2-3_fscv_5000nm.csv +0 -0
  53. electropycal-0.9.0/demo/in_vitro/input/20260723_neurostring_signal/2-3_fscv_500nm.csv +0 -0
  54. electropycal-0.9.0/demo/in_vitro/input/20260723_neurostring_signal/2-3_fscv_stabilization-full.csv +0 -0
  55. electropycal-0.9.0/demo/in_vitro/input/20260804_neurostring_channeltest/2-2_fscv_0nm.csv +0 -0
  56. electropycal-0.9.0/demo/in_vitro/input/20260804_neurostring_signal/2-2_eis_0nm.csv +0 -0
  57. electropycal-0.9.0/demo/in_vitro/input/20260804_neurostring_signal/2-2_fscv_0nm.csv +0 -0
  58. electropycal-0.9.0/demo/in_vitro/input/20260804_neurostring_signal/2-2_fscv_1000nm.csv +0 -0
  59. electropycal-0.9.0/demo/in_vitro/input/20260804_neurostring_signal/2-2_fscv_100nm.csv +0 -0
  60. electropycal-0.9.0/demo/in_vitro/input/20260804_neurostring_signal/2-2_fscv_5000nm.csv +0 -0
  61. electropycal-0.9.0/demo/in_vitro/input/20260804_neurostring_signal/2-2_fscv_500nm.csv +0 -0
  62. electropycal-0.9.0/demo/in_vitro/input/20260804_neurostring_signal/2-2_fscv_stabilization-full.csv +0 -0
  63. electropycal-0.9.0/demo/in_vitro/input/20260805_neurostring_channeltest/2-3_fscv_0nm.csv +0 -0
  64. electropycal-0.9.0/demo/in_vitro/input/20260805_neurostring_signal/2-3_eis_0nm.csv +0 -0
  65. electropycal-0.9.0/demo/in_vitro/input/20260805_neurostring_signal/2-3_fscv_0nm.csv +0 -0
  66. electropycal-0.9.0/demo/in_vitro/input/20260805_neurostring_signal/2-3_fscv_1000nm.csv +0 -0
  67. electropycal-0.9.0/demo/in_vitro/input/20260805_neurostring_signal/2-3_fscv_100nm.csv +0 -0
  68. electropycal-0.9.0/demo/in_vitro/input/20260805_neurostring_signal/2-3_fscv_5000nm.csv +0 -0
  69. electropycal-0.9.0/demo/in_vitro/input/20260805_neurostring_signal/2-3_fscv_500nm.csv +0 -0
  70. electropycal-0.9.0/demo/in_vitro/input/20260805_neurostring_signal/2-3_fscv_stabilization-full.csv +0 -0
  71. electropycal-0.9.0/demo/in_vitro/input/electropycal_analysis_config.json +19 -0
  72. electropycal-0.9.0/demo/in_vitro/output/featureset_raw.csv +97 -0
  73. electropycal-0.9.0/demo/in_vitro/output/featureset_raw.parquet +0 -0
  74. electropycal-0.9.0/demo/in_vitro/output/manifest.json +43 -0
  75. electropycal-0.9.0/demo/in_vitro/output/sensitivity_featureset.csv +25 -0
  76. electropycal-0.9.0/demo/in_vivo/input/20260801_neurostring_signal/3-2_paired_baseline.csv +0 -0
  77. electropycal-0.9.0/demo/in_vivo/input/20260801_neurostring_signal/3-2_paired_live.csv +0 -0
  78. electropycal-0.9.0/demo/in_vivo/input/20260801_neurostring_signal/4-2_paired_baseline.csv +0 -0
  79. electropycal-0.9.0/demo/in_vivo/input/20260801_neurostring_signal/4-2_paired_live.csv +0 -0
  80. electropycal-0.9.0/demo/in_vivo/input/20260808_neurostring_signal/3-2_paired_baseline.csv +0 -0
  81. electropycal-0.9.0/demo/in_vivo/input/20260808_neurostring_signal/3-2_paired_live.csv +0 -0
  82. electropycal-0.9.0/demo/in_vivo/input/20260808_neurostring_signal/4-2_paired_baseline.csv +0 -0
  83. electropycal-0.9.0/demo/in_vivo/input/20260808_neurostring_signal/4-2_paired_live.csv +0 -0
  84. electropycal-0.9.0/demo/in_vivo/input/20260822_neurostring_signal/3-2_paired_baseline.csv +0 -0
  85. electropycal-0.9.0/demo/in_vivo/input/20260822_neurostring_signal/3-2_paired_live.csv +0 -0
  86. electropycal-0.9.0/demo/in_vivo/input/20260822_neurostring_signal/4-2_paired_baseline.csv +0 -0
  87. electropycal-0.9.0/demo/in_vivo/input/20260822_neurostring_signal/4-2_paired_live.csv +0 -0
  88. electropycal-0.9.0/demo/in_vivo/output/invivo_featureset.csv +145 -0
  89. electropycal-0.9.0/docs/DESIGN.md +269 -0
  90. electropycal-0.9.0/docs/REFERENCE.md +404 -0
  91. electropycal-0.9.0/docs/RUNNING_AT_SCALE.md +108 -0
  92. electropycal-0.9.0/docs/USAGE.md +516 -0
  93. electropycal-0.9.0/notebooks/deployment_domain_shift.ipynb +305 -0
  94. electropycal-0.9.0/notebooks/diagnostics_review.ipynb +1332 -0
  95. electropycal-0.9.0/notebooks/discovery_checkpointed.ipynb +547 -0
  96. electropycal-0.9.0/notebooks/discovery_results_review.ipynb +279 -0
  97. electropycal-0.9.0/notebooks/quality_filtering_dashboard.ipynb +582 -0
  98. electropycal-0.9.0/notebooks/raw_spectra_review.ipynb +846 -0
  99. electropycal-0.9.0/notebooks/stabilization_review.ipynb +256 -0
  100. electropycal-0.9.0/pyproject.toml +87 -0
  101. electropycal-0.9.0/scripts/build_demo_dataset.py +155 -0
  102. electropycal-0.9.0/scripts/measure_invitro_stats.py +131 -0
  103. electropycal-0.9.0/src/electropycal/__init__.py +7 -0
  104. electropycal-0.9.0/src/electropycal/_demo.py +145 -0
  105. electropycal-0.9.0/src/electropycal/_parallel.py +40 -0
  106. electropycal-0.9.0/src/electropycal/analysis_config.py +168 -0
  107. electropycal-0.9.0/src/electropycal/cli.py +323 -0
  108. electropycal-0.9.0/src/electropycal/data/__init__.py +1 -0
  109. electropycal-0.9.0/src/electropycal/data/inventory.py +479 -0
  110. electropycal-0.9.0/src/electropycal/data/io.py +68 -0
  111. electropycal-0.9.0/src/electropycal/data/pstrace.py +205 -0
  112. electropycal-0.9.0/src/electropycal/data/quality.py +74 -0
  113. electropycal-0.9.0/src/electropycal/data/schema.py +118 -0
  114. electropycal-0.9.0/src/electropycal/data/stabilization.py +234 -0
  115. electropycal-0.9.0/src/electropycal/data/synthetic.py +520 -0
  116. electropycal-0.9.0/src/electropycal/deployment/__init__.py +1 -0
  117. electropycal-0.9.0/src/electropycal/deployment/deploy.py +244 -0
  118. electropycal-0.9.0/src/electropycal/deployment/domain.py +88 -0
  119. electropycal-0.9.0/src/electropycal/deployment/plots.py +45 -0
  120. electropycal-0.9.0/src/electropycal/diagnostics/__init__.py +1 -0
  121. electropycal-0.9.0/src/electropycal/diagnostics/variance.py +217 -0
  122. electropycal-0.9.0/src/electropycal/diagreview.py +770 -0
  123. electropycal-0.9.0/src/electropycal/discovery/__init__.py +1 -0
  124. electropycal-0.9.0/src/electropycal/discovery/baseline.py +55 -0
  125. electropycal-0.9.0/src/electropycal/discovery/batch.py +91 -0
  126. electropycal-0.9.0/src/electropycal/discovery/config.py +205 -0
  127. electropycal-0.9.0/src/electropycal/discovery/review.py +157 -0
  128. electropycal-0.9.0/src/electropycal/discovery/runner.py +362 -0
  129. electropycal-0.9.0/src/electropycal/discovery/scheduler.py +243 -0
  130. electropycal-0.9.0/src/electropycal/evaluation/__init__.py +1 -0
  131. electropycal-0.9.0/src/electropycal/evaluation/admissibility.py +85 -0
  132. electropycal-0.9.0/src/electropycal/evaluation/baselines.py +181 -0
  133. electropycal-0.9.0/src/electropycal/evaluation/classify.py +88 -0
  134. electropycal-0.9.0/src/electropycal/evaluation/cv.py +140 -0
  135. electropycal-0.9.0/src/electropycal/evaluation/framing.py +210 -0
  136. electropycal-0.9.0/src/electropycal/evaluation/hierarchical.py +161 -0
  137. electropycal-0.9.0/src/electropycal/evaluation/metrics.py +173 -0
  138. electropycal-0.9.0/src/electropycal/evaluation/multioutput.py +130 -0
  139. electropycal-0.9.0/src/electropycal/evaluation/stratify.py +121 -0
  140. electropycal-0.9.0/src/electropycal/evaluation/tracks.py +35 -0
  141. electropycal-0.9.0/src/electropycal/features/__init__.py +1 -0
  142. electropycal-0.9.0/src/electropycal/features/catalog.py +115 -0
  143. electropycal-0.9.0/src/electropycal/features/eis.py +135 -0
  144. electropycal-0.9.0/src/electropycal/features/extract.py +951 -0
  145. electropycal-0.9.0/src/electropycal/features/fscv.py +448 -0
  146. electropycal-0.9.0/src/electropycal/features/normalize.py +168 -0
  147. electropycal-0.9.0/src/electropycal/features/pin.py +267 -0
  148. electropycal-0.9.0/src/electropycal/features/targets.py +193 -0
  149. electropycal-0.9.0/src/electropycal/models/__init__.py +1 -0
  150. electropycal-0.9.0/src/electropycal/models/base.py +66 -0
  151. electropycal-0.9.0/src/electropycal/models/plsr.py +141 -0
  152. electropycal-0.9.0/src/electropycal/models/variants.py +217 -0
  153. electropycal-0.9.0/src/electropycal/overview.py +144 -0
  154. electropycal-0.9.0/src/electropycal/py.typed +0 -0
  155. electropycal-0.9.0/src/electropycal/qcdash.py +467 -0
  156. electropycal-0.9.0/src/electropycal/rawspectra.py +824 -0
  157. electropycal-0.9.0/src/electropycal/selection/__init__.py +1 -0
  158. electropycal-0.9.0/src/electropycal/selection/cars.py +102 -0
  159. electropycal-0.9.0/src/electropycal/selection/icc.py +32 -0
  160. electropycal-0.9.0/src/electropycal/selection/pseudo_multivariate.py +82 -0
  161. electropycal-0.9.0/src/electropycal/selection/univariate.py +23 -0
  162. electropycal-0.9.0/src/electropycal/stabreview.py +301 -0
  163. electropycal-0.9.0/src/electropycal/viz.py +97 -0
  164. electropycal-0.9.0/tests/test_admissibility.py +55 -0
  165. electropycal-0.9.0/tests/test_analysis_config.py +58 -0
  166. electropycal-0.9.0/tests/test_architectures.py +115 -0
  167. electropycal-0.9.0/tests/test_baselines.py +139 -0
  168. electropycal-0.9.0/tests/test_classify.py +66 -0
  169. electropycal-0.9.0/tests/test_cli.py +78 -0
  170. electropycal-0.9.0/tests/test_core.py +166 -0
  171. electropycal-0.9.0/tests/test_deployment.py +101 -0
  172. electropycal-0.9.0/tests/test_deployment_plots.py +23 -0
  173. electropycal-0.9.0/tests/test_diagreview.py +69 -0
  174. electropycal-0.9.0/tests/test_discovery.py +350 -0
  175. electropycal-0.9.0/tests/test_discovery_overview_batch.py +56 -0
  176. electropycal-0.9.0/tests/test_discovery_review.py +43 -0
  177. electropycal-0.9.0/tests/test_extract.py +547 -0
  178. electropycal-0.9.0/tests/test_features.py +317 -0
  179. electropycal-0.9.0/tests/test_fscv_clip.py +45 -0
  180. electropycal-0.9.0/tests/test_hierarchical.py +54 -0
  181. electropycal-0.9.0/tests/test_inventory.py +223 -0
  182. electropycal-0.9.0/tests/test_multioutput.py +31 -0
  183. electropycal-0.9.0/tests/test_pin.py +191 -0
  184. electropycal-0.9.0/tests/test_pipeline.py +44 -0
  185. electropycal-0.9.0/tests/test_pstrace.py +119 -0
  186. electropycal-0.9.0/tests/test_qcdash.py +54 -0
  187. electropycal-0.9.0/tests/test_rawspectra.py +70 -0
  188. electropycal-0.9.0/tests/test_stabilization.py +170 -0
  189. electropycal-0.9.0/tests/test_stabreview.py +85 -0
  190. electropycal-0.9.0/tests/test_stratify.py +58 -0
  191. electropycal-0.9.0/tests/test_targets.py +222 -0
  192. electropycal-0.9.0/tests/test_viz.py +19 -0
@@ -0,0 +1,43 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .eggs/
6
+ build/
7
+ dist/
8
+ .venv/
9
+ venv/
10
+ env/
11
+ .python-version
12
+
13
+ # Testing / coverage
14
+ .pytest_cache/
15
+ .coverage
16
+ htmlcov/
17
+ .tox/
18
+ .mypy_cache/
19
+ .ruff_cache/
20
+
21
+ # Notebooks
22
+ .ipynb_checkpoints/
23
+
24
+ # OS / editor
25
+ .DS_Store
26
+ *.swp
27
+ .idea/
28
+ .vscode/
29
+
30
+ # Run outputs. `discover`, `freeze` and `deploy` write here by default.
31
+ outputs/
32
+
33
+ # The featureset the docs build in their worked example.
34
+ featureset_extracted.parquet
35
+
36
+ # Artifacts the notebooks write back into the data root when you run them: the
37
+ # hash-keyed featureset caches and the QC-stats companion. Regenerated on demand.
38
+ # (electropycal_analysis_config.json is NOT here: it is shipped demo content.)
39
+ *_featureset__*.parquet
40
+ electropycal_qc_stats.json
41
+
42
+ # NOTE: demo/ is SHIPPED content (the synthetic demo dataset built by
43
+ # scripts/build_demo_dataset.py) and is deliberately NOT ignored.
@@ -0,0 +1,56 @@
1
+ # Changelog
2
+
3
+ This project follows [semantic versioning](https://semver.org/). It is pre-1.0, which here
4
+ means what semver says it means: **the public API may change in a minor release.** Pin a
5
+ version if you depend on it. 1.0.0 will be a deliberate act, once the surface has stopped
6
+ moving, not a scheduled follow-up.
7
+
8
+ ## 0.9.0
9
+
10
+ First public release.
11
+
12
+ ### What ships
13
+
14
+ - **Ingestion.** `data.pstrace` parses PSTrace UTF-16 exports. `features.extract` walks a raw
15
+ directory, derives each device's timepoints as elapsed days since its own first session,
16
+ quality-gates, and builds a featureset.
17
+ - **Discovery.** EIS/FSCV feature extraction, D0/Z normalization, forward-chained nested CV
18
+ across three tracks, five PLSR architectures, six feature selectors, and a decision-gated
19
+ task-queue scheduler with optional fold-parallelism.
20
+ - **Deployment.** Freeze a model on the full in-vitro set, then recalibrate new in-vivo data
21
+ with frozen robust scalers and a CORAL domain-shift confidence flag. Bundles are portable
22
+ `.npz` plus a manifest, with no pickle anywhere.
23
+ - **Diagnostics.** Variance partitioning and measurement-reliability estimates.
24
+ - **A synthetic demo dataset** under `demo/`, so every command and all seven notebooks run
25
+ before you have data of your own.
26
+ - **Seven notebooks**, each runnable as-is, which synthesize a demo tree when none is
27
+ reachable (as on Colab).
28
+
29
+ `discovery` and `deployment` are included rather than held back for a later release. They are
30
+ import-load-bearing for the CLI, three of the seven notebooks depend on them, and holding them
31
+ back would have meant shipping a library that could not do the thing it exists to do.
32
+
33
+ ### Deliberate design choices worth knowing
34
+
35
+ - **`extract_dataset` has no default for `band`** and raises without one. `"auto"` is a
36
+ percentile over whatever corpus is present, so it cannot be reproduced over a subset. The
37
+ CLI does default to `--band auto` and prints the band it resolved, so the choice is never
38
+ silent.
39
+ - **Dependencies are bounded at both ends, and so is Python** (`>=3.11,<3.14`). A scikit-learn
40
+ minor release can shift PLS numerics enough to move a reported RMSEP, and on 3.10 the
41
+ declared ranges resolved to a stack years apart from the reference one.
42
+ - **Results are hypothesis-generating.** Every ranking carries a bootstrap interval, and the
43
+ baseline queue is a one-factor-at-a-time design rather than a grid, because selecting among
44
+ many conditions on few folds overfits the selection itself.
45
+ - **Extraction is reproducible through a pin.** `run_config.json` records every derived
46
+ run-wide parameter, so a later extraction over a staged subset either reproduces the
47
+ original rows or raises, rather than silently producing different ones.
48
+
49
+ ### Known limits
50
+
51
+ - Plotting displays inline in notebooks but **writes no files**: there are no `savefig` calls,
52
+ so running the review modules from a script or in CI completes and produces nothing.
53
+ - `Kd` is unidentifiable on non-saturating data, so the saturating targets (`sat_*`, `hill_*`)
54
+ are off by default.
55
+ - Kernel, multi-block and multi-level PLSR, and the mRMR and permutation-`t_max` selectors,
56
+ are registered extension points that raise on use.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 thirteenbillion
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.
@@ -0,0 +1,156 @@
1
+ Metadata-Version: 2.5
2
+ Name: electropycal
3
+ Version: 0.9.0
4
+ Summary: Data processing and recalibration for electrochemical sensors (EIS/FSCV).
5
+ Project-URL: Homepage, https://github.com/thirteenbillion/electropycal
6
+ Project-URL: Repository, https://github.com/thirteenbillion/electropycal
7
+ Project-URL: Documentation, https://github.com/thirteenbillion/electropycal/blob/main/docs/USAGE.md
8
+ Project-URL: Changelog, https://github.com/thirteenbillion/electropycal/blob/main/CHANGELOG.md
9
+ Project-URL: Issues, https://github.com/thirteenbillion/electropycal/issues
10
+ Author: thirteenbillion
11
+ License-Expression: MIT
12
+ License-File: LICENSE
13
+ Keywords: EIS,FSCV,PLSR,electrochemistry,sensor recalibration
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Intended Audience :: Science/Research
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Scientific/Engineering :: Chemistry
22
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: <3.14,>=3.11
25
+ Requires-Dist: joblib<2,>=1.3
26
+ Requires-Dist: numpy<3,>=1.24
27
+ Requires-Dist: pandas<4,>=2.0
28
+ Requires-Dist: pyarrow>=12.0
29
+ Requires-Dist: scikit-learn<1.10,>=1.3
30
+ Requires-Dist: scipy<2,>=1.10
31
+ Provides-Extra: dev
32
+ Requires-Dist: cycler; extra == 'dev'
33
+ Requires-Dist: matplotlib; extra == 'dev'
34
+ Requires-Dist: mypy; extra == 'dev'
35
+ Requires-Dist: plotly; extra == 'dev'
36
+ Requires-Dist: pytest-cov; extra == 'dev'
37
+ Requires-Dist: pytest>=7.4; extra == 'dev'
38
+ Requires-Dist: ruff; extra == 'dev'
39
+ Provides-Extra: notebooks
40
+ Requires-Dist: cycler; extra == 'notebooks'
41
+ Requires-Dist: jupyter; extra == 'notebooks'
42
+ Requires-Dist: matplotlib; extra == 'notebooks'
43
+ Requires-Dist: plotly; extra == 'notebooks'
44
+ Requires-Dist: pyarrow; extra == 'notebooks'
45
+ Requires-Dist: seaborn; extra == 'notebooks'
46
+ Description-Content-Type: text/markdown
47
+
48
+ # ElectroPyCal
49
+
50
+ Data processing and recalibration for electrochemical sensors (EIS / FSCV).
51
+
52
+ ```bash
53
+ pip install electropycal
54
+ ```
55
+
56
+ **ElectroPyCal** discovers interpretable PLSR recalibration models for drifting
57
+ electrochemical sensors under leakage-free, forward-chained nested cross-validation, and
58
+ deploys a frozen model to new data while quantifying in-vitro to in-vivo domain shift
59
+ (CORAL). It originates from a neurostring dopamine-sensor recalibration study but is
60
+ designed to generalize to electrochemical-sensor data processing broadly.
61
+
62
+ Pre-1.0: the public API is still moving. Pin a version if you depend on it.
63
+
64
+ ## What it does
65
+
66
+ **Ingestion.** `data.pstrace` parses PSTrace UTF-16 exports. `features.extract` walks a raw
67
+ directory (`<YYYYMMDD>_<devicetype>_signal/<deviceid>_<signaltype>_<dose>.csv`, where `dose`
68
+ is a concentration or a protocol token such as `stabilization`), derives each device's
69
+ timepoints as elapsed days since its own first session, quality-gates, and builds a
70
+ featureset.
71
+
72
+ **Discovery.** EIS/FSCV feature extraction, D0/Z normalization, forward-chained nested CV
73
+ (Tracks 1/2/3), five architectures (linear, log, weighted, orthogonal, nonlinear PLSR), six
74
+ selectors (CARS, VIP, SR, sMC, MI, ICC), a decision-gated task-queue scheduler with optional
75
+ joblib fold-parallelism, and a checkpointed output layout (per-fold bundles,
76
+ `summary.parquet`, a ranked `report/`).
77
+
78
+ **Deployment.** Freeze a chosen model on 100% of in-vitro data, then recalibrate new in-vivo
79
+ data with frozen robust (median/IQR) scalers and a CORAL domain-shift confidence flag.
80
+ In-vivo raw ingestion (`paired` / `baseline` / `live` exports) feeds per-session
81
+ recalibration through `recalibrate_invivo` or `deploy --raw`. No pickle; bundles are portable
82
+ `.npz` plus a manifest.
83
+
84
+ **Diagnostics.** Variance partitioning across structural, drift and dose components, and
85
+ measurement-reliability estimates for the response.
86
+
87
+ Kernel, multi-block and multi-level PLSR, and the mRMR and permutation-`t_max` selectors, are
88
+ registered extension points that raise on use. See `docs/DESIGN.md`.
89
+
90
+ ## Quick start
91
+
92
+ ```bash
93
+ pip install electropycal
94
+
95
+ # Build a featureset from a raw PSTrace export directory.
96
+ electropycal extract --raw <pstrace_dir> --out featureset.parquet
97
+
98
+ # Rank candidate recalibration models.
99
+ electropycal discover --data featureset.parquet --profile full
100
+
101
+ # Recalibrate new in-vivo data with a frozen model.
102
+ electropycal deploy --model outputs/frozen_model --data invivo.parquet
103
+ ```
104
+
105
+ A synthetic demo dataset ships with the package under `demo/`, so every command and notebook
106
+ runs before you have data of your own:
107
+
108
+ ```bash
109
+ electropycal extract --raw demo/in_vitro/input --out featureset.parquet
110
+ ```
111
+
112
+ ### Two things worth knowing up front
113
+
114
+ The CLI's EIS analysis `--band` defaults to `auto`, a data-driven upper bound just below the
115
+ tightest inductive onset, and the resolved band is printed so it is never a silent choice. The
116
+ **Python API deliberately has no `band` default**: `extract_dataset(root)` raises. `auto` is a
117
+ percentile over whatever corpus is present, so it cannot be reproduced over a staged subset.
118
+ Pass a `(lo, hi)` tuple, `band="auto"`, or `pin=` to reuse a previous run's band.
119
+
120
+ `discover` and `run_discovery` cap `min_train_times` to what the number of timepoints can
121
+ support, so a short series yields usable folds rather than silently empty ones.
122
+
123
+ ## Notebooks
124
+
125
+ In `notebooks/`, descriptive names, runnable in any order. Each one runs as-is against the
126
+ shipped `demo/` tree, and synthesizes an equivalent tree if none is present (Colab).
127
+
128
+ | Notebook | Purpose |
129
+ |---|---|
130
+ | `raw_spectra_review` | inspect raw EIS/FSCV per device; recommends the analysis band. Run first. |
131
+ | `quality_filtering_dashboard` | per-device quality gating and channel-timepoint validity |
132
+ | `stabilization_review` | confirm FSCV stabilized, from the cycle-to-cycle drift |
133
+ | `diagnostics_review` | variance hierarchy, response reliability, QC-gate impact. Run between QC and discovery. |
134
+ | `discovery_checkpointed` | run discovery batch by batch with CI-ranked review between batches |
135
+ | `discovery_results_review` | review a completed run: ranking, CIs, per-fold spread, feature ranking |
136
+ | `deployment_domain_shift` | freeze a model, recalibrate drifted data, CORAL drift monitor |
137
+
138
+ ## Docs
139
+
140
+ - **`docs/USAGE.md`**: the user guide. Installation and a full discovery walkthrough, start to finish.
141
+ - **`docs/REFERENCE.md`**: the lookup tables. Every default, the feature dictionary, directory layouts, and the API.
142
+ - **`docs/DESIGN.md`**: architecture, assumptions, and the reasoning behind the defaults.
143
+ - **`docs/RUNNING_AT_SCALE.md`**: extract once to parquet, then cluster workflows for large raw data.
144
+
145
+ ## Layout
146
+
147
+ ```
148
+ src/electropycal/{data,features,models,selection,evaluation,discovery,deployment,diagnostics}
149
+ notebooks/ # the seven review and workflow notebooks listed above
150
+ demo/ # synthetic in-vitro and in-vivo trees, input and output
151
+ docs/ # USAGE.md, REFERENCE.md, DESIGN.md, RUNNING_AT_SCALE.md
152
+ ```
153
+
154
+ ## License
155
+
156
+ MIT. See `LICENSE`.
@@ -0,0 +1,109 @@
1
+ # ElectroPyCal
2
+
3
+ Data processing and recalibration for electrochemical sensors (EIS / FSCV).
4
+
5
+ ```bash
6
+ pip install electropycal
7
+ ```
8
+
9
+ **ElectroPyCal** discovers interpretable PLSR recalibration models for drifting
10
+ electrochemical sensors under leakage-free, forward-chained nested cross-validation, and
11
+ deploys a frozen model to new data while quantifying in-vitro to in-vivo domain shift
12
+ (CORAL). It originates from a neurostring dopamine-sensor recalibration study but is
13
+ designed to generalize to electrochemical-sensor data processing broadly.
14
+
15
+ Pre-1.0: the public API is still moving. Pin a version if you depend on it.
16
+
17
+ ## What it does
18
+
19
+ **Ingestion.** `data.pstrace` parses PSTrace UTF-16 exports. `features.extract` walks a raw
20
+ directory (`<YYYYMMDD>_<devicetype>_signal/<deviceid>_<signaltype>_<dose>.csv`, where `dose`
21
+ is a concentration or a protocol token such as `stabilization`), derives each device's
22
+ timepoints as elapsed days since its own first session, quality-gates, and builds a
23
+ featureset.
24
+
25
+ **Discovery.** EIS/FSCV feature extraction, D0/Z normalization, forward-chained nested CV
26
+ (Tracks 1/2/3), five architectures (linear, log, weighted, orthogonal, nonlinear PLSR), six
27
+ selectors (CARS, VIP, SR, sMC, MI, ICC), a decision-gated task-queue scheduler with optional
28
+ joblib fold-parallelism, and a checkpointed output layout (per-fold bundles,
29
+ `summary.parquet`, a ranked `report/`).
30
+
31
+ **Deployment.** Freeze a chosen model on 100% of in-vitro data, then recalibrate new in-vivo
32
+ data with frozen robust (median/IQR) scalers and a CORAL domain-shift confidence flag.
33
+ In-vivo raw ingestion (`paired` / `baseline` / `live` exports) feeds per-session
34
+ recalibration through `recalibrate_invivo` or `deploy --raw`. No pickle; bundles are portable
35
+ `.npz` plus a manifest.
36
+
37
+ **Diagnostics.** Variance partitioning across structural, drift and dose components, and
38
+ measurement-reliability estimates for the response.
39
+
40
+ Kernel, multi-block and multi-level PLSR, and the mRMR and permutation-`t_max` selectors, are
41
+ registered extension points that raise on use. See `docs/DESIGN.md`.
42
+
43
+ ## Quick start
44
+
45
+ ```bash
46
+ pip install electropycal
47
+
48
+ # Build a featureset from a raw PSTrace export directory.
49
+ electropycal extract --raw <pstrace_dir> --out featureset.parquet
50
+
51
+ # Rank candidate recalibration models.
52
+ electropycal discover --data featureset.parquet --profile full
53
+
54
+ # Recalibrate new in-vivo data with a frozen model.
55
+ electropycal deploy --model outputs/frozen_model --data invivo.parquet
56
+ ```
57
+
58
+ A synthetic demo dataset ships with the package under `demo/`, so every command and notebook
59
+ runs before you have data of your own:
60
+
61
+ ```bash
62
+ electropycal extract --raw demo/in_vitro/input --out featureset.parquet
63
+ ```
64
+
65
+ ### Two things worth knowing up front
66
+
67
+ The CLI's EIS analysis `--band` defaults to `auto`, a data-driven upper bound just below the
68
+ tightest inductive onset, and the resolved band is printed so it is never a silent choice. The
69
+ **Python API deliberately has no `band` default**: `extract_dataset(root)` raises. `auto` is a
70
+ percentile over whatever corpus is present, so it cannot be reproduced over a staged subset.
71
+ Pass a `(lo, hi)` tuple, `band="auto"`, or `pin=` to reuse a previous run's band.
72
+
73
+ `discover` and `run_discovery` cap `min_train_times` to what the number of timepoints can
74
+ support, so a short series yields usable folds rather than silently empty ones.
75
+
76
+ ## Notebooks
77
+
78
+ In `notebooks/`, descriptive names, runnable in any order. Each one runs as-is against the
79
+ shipped `demo/` tree, and synthesizes an equivalent tree if none is present (Colab).
80
+
81
+ | Notebook | Purpose |
82
+ |---|---|
83
+ | `raw_spectra_review` | inspect raw EIS/FSCV per device; recommends the analysis band. Run first. |
84
+ | `quality_filtering_dashboard` | per-device quality gating and channel-timepoint validity |
85
+ | `stabilization_review` | confirm FSCV stabilized, from the cycle-to-cycle drift |
86
+ | `diagnostics_review` | variance hierarchy, response reliability, QC-gate impact. Run between QC and discovery. |
87
+ | `discovery_checkpointed` | run discovery batch by batch with CI-ranked review between batches |
88
+ | `discovery_results_review` | review a completed run: ranking, CIs, per-fold spread, feature ranking |
89
+ | `deployment_domain_shift` | freeze a model, recalibrate drifted data, CORAL drift monitor |
90
+
91
+ ## Docs
92
+
93
+ - **`docs/USAGE.md`**: the user guide. Installation and a full discovery walkthrough, start to finish.
94
+ - **`docs/REFERENCE.md`**: the lookup tables. Every default, the feature dictionary, directory layouts, and the API.
95
+ - **`docs/DESIGN.md`**: architecture, assumptions, and the reasoning behind the defaults.
96
+ - **`docs/RUNNING_AT_SCALE.md`**: extract once to parquet, then cluster workflows for large raw data.
97
+
98
+ ## Layout
99
+
100
+ ```
101
+ src/electropycal/{data,features,models,selection,evaluation,discovery,deployment,diagnostics}
102
+ notebooks/ # the seven review and workflow notebooks listed above
103
+ demo/ # synthetic in-vitro and in-vivo trees, input and output
104
+ docs/ # USAGE.md, REFERENCE.md, DESIGN.md, RUNNING_AT_SCALE.md
105
+ ```
106
+
107
+ ## License
108
+
109
+ MIT. See `LICENSE`.
@@ -0,0 +1,24 @@
1
+ # Synthetic demo dataset
2
+
3
+ Generated by `python scripts/build_demo_dataset.py`. Everything here is synthetic; there are no
4
+ real measurements. The generator is calibrated to statistics measured from real in-vitro
5
+ neurostring sessions, so the traces and extracted features sit in realistic ranges (peak V_ox
6
+ around 0.8 V, background around 30 uA, NormIpeak around 0.02 rising sub-linearly with dose,
7
+ noise_floor around 0.015).
8
+
9
+ | Path | What |
10
+ |------|------|
11
+ | `in_vitro/input/` | raw PSTrace exports: `*_channeltest` (0 nM screen) and `*_signal` (EIS, dose series, stabilization) folders, one per timepoint |
12
+ | `in_vitro/output/` | what the pipeline produces from that input: the extracted featureset, the per-sensor calibration targets, the analysis config, and a manifest |
13
+ | `in_vivo/input/` | paired FSCV+EIS exports per session (`_paired_baseline`, `_paired_live`) for the deployment and domain-shift demo |
14
+ | `in_vivo/output/` | the in-vivo featureset extracted from it |
15
+
16
+ The notebooks find this tree on their own: leave `ROOT` at `None` and run top to bottom. To
17
+ point at it explicitly, use `demo/in_vitro/input`, or `demo/in_vivo/input` for
18
+ `deployment_domain_shift`.
19
+
20
+ Stabilization files use the `-full` spelling (6 rounds of 6 cycles), matching what current
21
+ sessions produce. The generator can also emit the older `-start` and `-end` pair; see
22
+ `write_synthetic_pstrace_dir(stab_phases=...)`.
23
+
24
+ Regenerate at any time. The generator is seeded, so the output is reproducible.
@@ -0,0 +1,19 @@
1
+ {
2
+ "band": [
3
+ 2.0,
4
+ 2000.0
5
+ ],
6
+ "peak_method": "direct",
7
+ "acceptance": "monotonic",
8
+ "mono_tol": 0.1,
9
+ "min_norm_snr": 3.0,
10
+ "monotonic_r_min": 0.6,
11
+ "mono_method": "pearson",
12
+ "max_reps": 3,
13
+ "gate_on": {
14
+ "eis": true,
15
+ "monotonic": true,
16
+ "snr_all": false,
17
+ "peak_in_window": false
18
+ }
19
+ }