TwoPy 0.1.0__tar.gz → 0.1.2__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.
- {twopy-0.1.0 → twopy-0.1.2}/PKG-INFO +33 -40
- {twopy-0.1.0 → twopy-0.1.2}/README.md +32 -39
- {twopy-0.1.0 → twopy-0.1.2}/pyproject.toml +1 -1
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/__init__.py +11 -2
- twopy-0.1.2/src/twopy/_version.py +14 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/analysis/__init__.py +12 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/analysis/persistence.py +37 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/analysis/response_processing/__init__.py +6 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/analysis/response_processing/apply.py +75 -12
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/analysis/response_processing/options.py +30 -6
- twopy-0.1.2/src/twopy/analysis/response_processing/persistence.py +261 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/analysis/response_processing/signals.py +70 -1
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/analysis/workflow.py +64 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/napari/interactive.py +65 -7
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/napari/launcher.py +13 -3
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/napari/plotting/data.py +31 -7
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/napari/plotting/docks.py +133 -20
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/napari/plotting/options.py +19 -19
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/napari/plotting/panels.py +0 -20
- twopy-0.1.2/src/twopy/napari/plotting/processing_options.py +390 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/napari/responses.py +14 -1
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy.egg-info/PKG-INFO +33 -40
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy.egg-info/SOURCES.txt +3 -0
- {twopy-0.1.0 → twopy-0.1.2}/tests/test_import.py +2 -1
- {twopy-0.1.0 → twopy-0.1.2}/tests/test_napari.py +311 -1
- twopy-0.1.2/tests/test_packaging.py +63 -0
- {twopy-0.1.0 → twopy-0.1.2}/tests/test_persistence.py +69 -0
- {twopy-0.1.0 → twopy-0.1.2}/tests/test_real_fixture.py +31 -0
- {twopy-0.1.0 → twopy-0.1.2}/tests/test_response_processing.py +36 -0
- twopy-0.1.0/tests/test_packaging.py +0 -32
- {twopy-0.1.0 → twopy-0.1.2}/setup.cfg +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/_segments.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/analysis/background_subtraction.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/analysis/dff.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/analysis/epoch_mapping.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/analysis/motion.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/analysis/response_processing/qc.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/analysis/responses.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/analysis/trials.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/api.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/config.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/conversion/__init__.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/conversion/alignment_crop.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/conversion/api.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/conversion/frame_ranges.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/conversion/hdf5_writing.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/conversion/matlab_values.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/conversion/source_loading.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/conversion/stimulus_code.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/conversion/types.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/converted.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/database/__init__.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/database/cache.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/database/catalog.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/database/connection.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/database/generic.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/database/modeled.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/database/sql.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/database/types.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/inspection.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/matlab.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/napari/__init__.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/napari/__main__.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/napari/constants.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/napari/controls.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/napari/display.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/napari/display_paths.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/napari/layout.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/napari/loading.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/napari/movie.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/napari/paths.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/napari/plotting/__init__.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/napari/plotting/export.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/napari/plotting/export_controls.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/napari/plotting/label_visibility.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/napari/plotting/widgets.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/napari/protocols.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/napari/roi.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/napari/session.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/napari/state.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/napari/text.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/napari/types.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/napari/viewer.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/parity/__init__.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/parity/dff.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/parity/roi.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/parity/saved_analysis.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/photodiode.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/photodiode_classification.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/roi.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/session.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/spatial.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/stimulus.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/synchronization.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy/typing_guards.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy.egg-info/dependency_links.txt +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy.egg-info/entry_points.txt +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy.egg-info/requires.txt +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/src/twopy.egg-info/top_level.txt +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/tests/test_api.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/tests/test_background_subtraction.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/tests/test_config.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/tests/test_conversion.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/tests/test_converted.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/tests/test_database.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/tests/test_dff.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/tests/test_epoch_mapping.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/tests/test_inspection.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/tests/test_matlab.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/tests/test_motion.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/tests/test_parity.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/tests/test_photodiode_classification.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/tests/test_responses.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/tests/test_roi.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/tests/test_saved_analysis.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/tests/test_session.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/tests/test_stimulus.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/tests/test_synchronization.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/tests/test_trials.py +0 -0
- {twopy-0.1.0 → twopy-0.1.2}/tests/test_workflow.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: twopy
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Two-photon imaging analysis tool with a napari interface.
|
|
5
5
|
Author-email: Gustavo Madeira Santana <fromweb1@gumadeiras.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -22,6 +22,8 @@ Requires-Dist: ty<0.1,>=0.0.34
|
|
|
22
22
|
|
|
23
23
|
Two-photon imaging analysis tool for the Clark Lab output format.
|
|
24
24
|
|
|
25
|
+

|
|
26
|
+
|
|
25
27
|
## Getting Started
|
|
26
28
|
|
|
27
29
|
twopy lets you open two-photon recordings, draw ROIs, plot responses in real time,
|
|
@@ -36,25 +38,24 @@ the converted files, so the original source files remain separate from twopy's o
|
|
|
36
38
|
|
|
37
39
|
Examples use micromamba, but any conda-compatible environment manager should work.
|
|
38
40
|
|
|
39
|
-
|
|
41
|
+
Preferred setup is a fresh Python 3.13 environment with the published PyPI
|
|
42
|
+
package:
|
|
40
43
|
|
|
41
44
|
```sh
|
|
42
|
-
micromamba
|
|
43
|
-
micromamba activate twopy
|
|
44
|
-
micromamba run -n twopy pre-commit install
|
|
45
|
-
cp config.example.yml config.yml
|
|
45
|
+
micromamba create -n twopy -c conda-forge python=3.13 pip -y && micromamba run -n twopy python -m pip install twopy
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
explains each setting in plain language. `config.yml` stays local to your
|
|
50
|
-
machine and is not tracked by git.
|
|
51
|
-
|
|
52
|
-
If the `twopy` command is missing after setup, refresh the editable install:
|
|
48
|
+
Verify the install:
|
|
53
49
|
|
|
54
50
|
```sh
|
|
55
|
-
micromamba run -n twopy python -
|
|
51
|
+
micromamba run -n twopy python -c 'import twopy; print(twopy.__name__)'
|
|
56
52
|
```
|
|
57
53
|
|
|
54
|
+
If you are working from a source checkout, copy `config.example.yml` to
|
|
55
|
+
`config.yml`, then edit `config.yml` so the paths match your computer. The
|
|
56
|
+
example file explains each setting in plain language. `config.yml` stays local
|
|
57
|
+
to your machine and is not tracked by git.
|
|
58
|
+
|
|
58
59
|
### Start The GUI
|
|
59
60
|
|
|
60
61
|
Start napari from the twopy environment:
|
|
@@ -96,9 +97,18 @@ Basic GUI flow:
|
|
|
96
97
|
|
|
97
98
|
## Setup Details
|
|
98
99
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
For development, install from the repository so local code edits are used:
|
|
101
|
+
|
|
102
|
+
```sh
|
|
103
|
+
micromamba env create -f environment.yml
|
|
104
|
+
micromamba activate twopy
|
|
105
|
+
micromamba run -n twopy pre-commit install
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The development environment installs twopy as an editable package, so the
|
|
109
|
+
`twopy` terminal command is available after activating the environment. If the
|
|
110
|
+
environment already existed before the command was added, refresh the editable
|
|
111
|
+
install:
|
|
102
112
|
|
|
103
113
|
```sh
|
|
104
114
|
micromamba run -n twopy python -m pip install -e .
|
|
@@ -113,31 +123,6 @@ micromamba run -n twopy pre-commit run --all-files
|
|
|
113
123
|
The installed pre-commit hook runs ruff, ty, and the unit tests before each
|
|
114
124
|
commit.
|
|
115
125
|
|
|
116
|
-
## Release
|
|
117
|
-
|
|
118
|
-
PyPI publishing uses GitHub Actions Trusted Publishing. No PyPI API token secret
|
|
119
|
-
is required.
|
|
120
|
-
|
|
121
|
-
One-time setup in PyPI:
|
|
122
|
-
|
|
123
|
-
1. Add a trusted publisher for the `twopy` project.
|
|
124
|
-
2. Use owner `gumadeiras`, repository `twopy`, workflow
|
|
125
|
-
`publish-to-pypi.yml`, and environment `pypi`.
|
|
126
|
-
3. In GitHub, create the `pypi` environment and require manual approval.
|
|
127
|
-
|
|
128
|
-
Release flow:
|
|
129
|
-
|
|
130
|
-
1. Update `project.version` in `pyproject.toml`.
|
|
131
|
-
2. Run `micromamba run -n twopy pre-commit run --all-files`.
|
|
132
|
-
3. Commit the version change.
|
|
133
|
-
4. Create a GitHub release whose tag is the same version, with or without a
|
|
134
|
-
leading `v`.
|
|
135
|
-
5. Publish the release.
|
|
136
|
-
|
|
137
|
-
The release workflow checks that the tag matches `pyproject.toml`, builds the
|
|
138
|
-
wheel and source distribution, checks package metadata, and publishes to PyPI
|
|
139
|
-
after the `pypi` environment approval.
|
|
140
|
-
|
|
141
126
|
## Find Recordings
|
|
142
127
|
|
|
143
128
|
```python
|
|
@@ -241,6 +226,14 @@ shared exponential tau and one amplitude per ROI. The default dF/F fit mode is
|
|
|
241
226
|
`robust`; pass `fit_mode="source_bounds"` when you need original source-bound
|
|
242
227
|
behavior for audit comparisons.
|
|
243
228
|
|
|
229
|
+
The script and napari workflows also accept `ResponseProcessingOptions` for
|
|
230
|
+
post-dF/F response processing. Smoothing and low-pass filters are applied to
|
|
231
|
+
continuous dF/F before trial grouping, while correlation filtering scores
|
|
232
|
+
grouped trial responses and stores the selected settings plus QC scores in the
|
|
233
|
+
analysis HDF5 output. Smoothing supports moving-average and Savitzky-Golay
|
|
234
|
+
methods; Savitzky-Golay defaults to a seven-frame window with polynomial order
|
|
235
|
+
two.
|
|
236
|
+
|
|
244
237
|
## Open In Napari
|
|
245
238
|
|
|
246
239
|
From a converted output directory that contains `recording_data.h5`, or from a
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Two-photon imaging analysis tool for the Clark Lab output format.
|
|
4
4
|
|
|
5
|
+

|
|
6
|
+
|
|
5
7
|
## Getting Started
|
|
6
8
|
|
|
7
9
|
twopy lets you open two-photon recordings, draw ROIs, plot responses in real time,
|
|
@@ -16,25 +18,24 @@ the converted files, so the original source files remain separate from twopy's o
|
|
|
16
18
|
|
|
17
19
|
Examples use micromamba, but any conda-compatible environment manager should work.
|
|
18
20
|
|
|
19
|
-
|
|
21
|
+
Preferred setup is a fresh Python 3.13 environment with the published PyPI
|
|
22
|
+
package:
|
|
20
23
|
|
|
21
24
|
```sh
|
|
22
|
-
micromamba
|
|
23
|
-
micromamba activate twopy
|
|
24
|
-
micromamba run -n twopy pre-commit install
|
|
25
|
-
cp config.example.yml config.yml
|
|
25
|
+
micromamba create -n twopy -c conda-forge python=3.13 pip -y && micromamba run -n twopy python -m pip install twopy
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
explains each setting in plain language. `config.yml` stays local to your
|
|
30
|
-
machine and is not tracked by git.
|
|
31
|
-
|
|
32
|
-
If the `twopy` command is missing after setup, refresh the editable install:
|
|
28
|
+
Verify the install:
|
|
33
29
|
|
|
34
30
|
```sh
|
|
35
|
-
micromamba run -n twopy python -
|
|
31
|
+
micromamba run -n twopy python -c 'import twopy; print(twopy.__name__)'
|
|
36
32
|
```
|
|
37
33
|
|
|
34
|
+
If you are working from a source checkout, copy `config.example.yml` to
|
|
35
|
+
`config.yml`, then edit `config.yml` so the paths match your computer. The
|
|
36
|
+
example file explains each setting in plain language. `config.yml` stays local
|
|
37
|
+
to your machine and is not tracked by git.
|
|
38
|
+
|
|
38
39
|
### Start The GUI
|
|
39
40
|
|
|
40
41
|
Start napari from the twopy environment:
|
|
@@ -76,9 +77,18 @@ Basic GUI flow:
|
|
|
76
77
|
|
|
77
78
|
## Setup Details
|
|
78
79
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
For development, install from the repository so local code edits are used:
|
|
81
|
+
|
|
82
|
+
```sh
|
|
83
|
+
micromamba env create -f environment.yml
|
|
84
|
+
micromamba activate twopy
|
|
85
|
+
micromamba run -n twopy pre-commit install
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The development environment installs twopy as an editable package, so the
|
|
89
|
+
`twopy` terminal command is available after activating the environment. If the
|
|
90
|
+
environment already existed before the command was added, refresh the editable
|
|
91
|
+
install:
|
|
82
92
|
|
|
83
93
|
```sh
|
|
84
94
|
micromamba run -n twopy python -m pip install -e .
|
|
@@ -93,31 +103,6 @@ micromamba run -n twopy pre-commit run --all-files
|
|
|
93
103
|
The installed pre-commit hook runs ruff, ty, and the unit tests before each
|
|
94
104
|
commit.
|
|
95
105
|
|
|
96
|
-
## Release
|
|
97
|
-
|
|
98
|
-
PyPI publishing uses GitHub Actions Trusted Publishing. No PyPI API token secret
|
|
99
|
-
is required.
|
|
100
|
-
|
|
101
|
-
One-time setup in PyPI:
|
|
102
|
-
|
|
103
|
-
1. Add a trusted publisher for the `twopy` project.
|
|
104
|
-
2. Use owner `gumadeiras`, repository `twopy`, workflow
|
|
105
|
-
`publish-to-pypi.yml`, and environment `pypi`.
|
|
106
|
-
3. In GitHub, create the `pypi` environment and require manual approval.
|
|
107
|
-
|
|
108
|
-
Release flow:
|
|
109
|
-
|
|
110
|
-
1. Update `project.version` in `pyproject.toml`.
|
|
111
|
-
2. Run `micromamba run -n twopy pre-commit run --all-files`.
|
|
112
|
-
3. Commit the version change.
|
|
113
|
-
4. Create a GitHub release whose tag is the same version, with or without a
|
|
114
|
-
leading `v`.
|
|
115
|
-
5. Publish the release.
|
|
116
|
-
|
|
117
|
-
The release workflow checks that the tag matches `pyproject.toml`, builds the
|
|
118
|
-
wheel and source distribution, checks package metadata, and publishes to PyPI
|
|
119
|
-
after the `pypi` environment approval.
|
|
120
|
-
|
|
121
106
|
## Find Recordings
|
|
122
107
|
|
|
123
108
|
```python
|
|
@@ -221,6 +206,14 @@ shared exponential tau and one amplitude per ROI. The default dF/F fit mode is
|
|
|
221
206
|
`robust`; pass `fit_mode="source_bounds"` when you need original source-bound
|
|
222
207
|
behavior for audit comparisons.
|
|
223
208
|
|
|
209
|
+
The script and napari workflows also accept `ResponseProcessingOptions` for
|
|
210
|
+
post-dF/F response processing. Smoothing and low-pass filters are applied to
|
|
211
|
+
continuous dF/F before trial grouping, while correlation filtering scores
|
|
212
|
+
grouped trial responses and stores the selected settings plus QC scores in the
|
|
213
|
+
analysis HDF5 output. Smoothing supports moving-average and Savitzky-Golay
|
|
214
|
+
methods; Savitzky-Golay defaults to a seven-frame window with polynomial order
|
|
215
|
+
two.
|
|
216
|
+
|
|
224
217
|
## Open In Napari
|
|
225
218
|
|
|
226
219
|
From a converted output directory that contains `recording_data.h5`, or from a
|
|
@@ -5,6 +5,7 @@ Inputs: none.
|
|
|
5
5
|
Outputs: package constants and helper functions imported by users and scripts.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
from twopy._version import __version__
|
|
8
9
|
from twopy.analysis import (
|
|
9
10
|
DEFAULT_RESPONSE_POST_WINDOW_SECONDS,
|
|
10
11
|
DEFAULT_RESPONSE_PRE_WINDOW_SECONDS,
|
|
@@ -12,6 +13,7 @@ from twopy.analysis import (
|
|
|
12
13
|
AnalysisResponseRun,
|
|
13
14
|
BackgroundCorrectedRoiTraces,
|
|
14
15
|
BackgroundCorrectionMethod,
|
|
16
|
+
CorrelationFilterOptions,
|
|
15
17
|
DeltaFOverFFitMode,
|
|
16
18
|
EpochFrameWindow,
|
|
17
19
|
FrameWindow,
|
|
@@ -19,9 +21,13 @@ from twopy.analysis import (
|
|
|
19
21
|
GroupedRoiResponseSummary,
|
|
20
22
|
InterpolatedEpochMapping,
|
|
21
23
|
LoadedAnalysisOutputs,
|
|
24
|
+
LowPassFilterOptions,
|
|
25
|
+
ResponseProcessingOptions,
|
|
26
|
+
RoiCorrelationScores,
|
|
22
27
|
RoiDeltaFOverF,
|
|
23
28
|
RoiResponseSummary,
|
|
24
29
|
RoiResponseTrial,
|
|
30
|
+
SmoothingOptions,
|
|
25
31
|
WindowedRoiResponse,
|
|
26
32
|
analyze_recording_responses,
|
|
27
33
|
apply_motion_artifact_mask_to_delta_f_over_f,
|
|
@@ -104,6 +110,7 @@ __all__ = [
|
|
|
104
110
|
"classify_recording_photodiode_events",
|
|
105
111
|
"compute_roi_delta_f_over_f",
|
|
106
112
|
"compute_recording_responses",
|
|
113
|
+
"CorrelationFilterOptions",
|
|
107
114
|
"convert_recording_to_twopy",
|
|
108
115
|
"DEFAULT_RESPONSE_POST_WINDOW_SECONDS",
|
|
109
116
|
"DEFAULT_RESPONSE_PRE_WINDOW_SECONDS",
|
|
@@ -129,6 +136,7 @@ __all__ = [
|
|
|
129
136
|
"load_roi_set",
|
|
130
137
|
"load_source_conversion_inputs",
|
|
131
138
|
"LoadedAnalysisOutputs",
|
|
139
|
+
"LowPassFilterOptions",
|
|
132
140
|
"make_roi_set_from_label_image",
|
|
133
141
|
"make_frame_windows",
|
|
134
142
|
"make_roi_set",
|
|
@@ -143,7 +151,9 @@ __all__ = [
|
|
|
143
151
|
"PhotodiodeEvent",
|
|
144
152
|
"PhotodiodeEventType",
|
|
145
153
|
"PhotodiodeEventSet",
|
|
154
|
+
"ResponseProcessingOptions",
|
|
146
155
|
"RoiDeltaFOverF",
|
|
156
|
+
"RoiCorrelationScores",
|
|
147
157
|
"RoiResponseSummary",
|
|
148
158
|
"RoiResponseTrial",
|
|
149
159
|
"RoiSet",
|
|
@@ -155,6 +165,7 @@ __all__ = [
|
|
|
155
165
|
"save_roi_set",
|
|
156
166
|
"select_epoch_frame_windows",
|
|
157
167
|
"split_traces_by_frame_windows",
|
|
168
|
+
"SmoothingOptions",
|
|
158
169
|
"SpatialCrop",
|
|
159
170
|
"SpatialDomain",
|
|
160
171
|
"StimulusSpecificColumnMapping",
|
|
@@ -164,5 +175,3 @@ __all__ = [
|
|
|
164
175
|
"write_response_summary_grouped_csv",
|
|
165
176
|
"write_response_summary_trials_csv",
|
|
166
177
|
]
|
|
167
|
-
|
|
168
|
-
__version__ = "0.1.0"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Package-version metadata for twopy.
|
|
2
|
+
|
|
3
|
+
Inputs: installed Python package metadata.
|
|
4
|
+
Outputs: the current twopy package version string.
|
|
5
|
+
|
|
6
|
+
Keeping this in one small module prevents the public Python API and command-line
|
|
7
|
+
interface from drifting when ``pyproject.toml`` changes.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from importlib.metadata import version
|
|
11
|
+
|
|
12
|
+
__all__ = ["__version__"]
|
|
13
|
+
|
|
14
|
+
__version__ = version("twopy")
|
|
@@ -31,6 +31,13 @@ from twopy.analysis.persistence import (
|
|
|
31
31
|
write_response_summary_grouped_csv,
|
|
32
32
|
write_response_summary_trials_csv,
|
|
33
33
|
)
|
|
34
|
+
from twopy.analysis.response_processing import (
|
|
35
|
+
CorrelationFilterOptions,
|
|
36
|
+
LowPassFilterOptions,
|
|
37
|
+
ResponseProcessingOptions,
|
|
38
|
+
RoiCorrelationScores,
|
|
39
|
+
SmoothingOptions,
|
|
40
|
+
)
|
|
34
41
|
from twopy.analysis.responses import (
|
|
35
42
|
GroupedRoiResponses,
|
|
36
43
|
GroupedRoiResponseSummary,
|
|
@@ -72,6 +79,7 @@ __all__ = [
|
|
|
72
79
|
"BackgroundCorrectionMethod",
|
|
73
80
|
"compute_roi_delta_f_over_f",
|
|
74
81
|
"compute_recording_responses",
|
|
82
|
+
"CorrelationFilterOptions",
|
|
75
83
|
"DeltaFOverFFitMode",
|
|
76
84
|
"EpochFrameWindow",
|
|
77
85
|
"extract_background_corrected_roi_traces",
|
|
@@ -85,14 +93,18 @@ __all__ = [
|
|
|
85
93
|
"is_gray_epoch_name",
|
|
86
94
|
"load_analysis_outputs",
|
|
87
95
|
"LoadedAnalysisOutputs",
|
|
96
|
+
"LowPassFilterOptions",
|
|
88
97
|
"make_frame_windows",
|
|
89
98
|
"map_stimulus_epochs_to_frame_windows",
|
|
90
99
|
"RoiDeltaFOverF",
|
|
100
|
+
"ResponseProcessingOptions",
|
|
101
|
+
"RoiCorrelationScores",
|
|
91
102
|
"RoiResponseSummary",
|
|
92
103
|
"RoiResponseTrial",
|
|
93
104
|
"save_analysis_outputs",
|
|
94
105
|
"select_epoch_frame_windows",
|
|
95
106
|
"split_traces_by_frame_windows",
|
|
107
|
+
"SmoothingOptions",
|
|
96
108
|
"summarize_epoch_roi_responses",
|
|
97
109
|
"summarize_grouped_responses",
|
|
98
110
|
"WindowedRoiResponse",
|
|
@@ -23,6 +23,14 @@ from twopy.analysis.background_subtraction import (
|
|
|
23
23
|
BackgroundCorrectionMethod,
|
|
24
24
|
)
|
|
25
25
|
from twopy.analysis.dff import RoiDeltaFOverF
|
|
26
|
+
from twopy.analysis.response_processing import (
|
|
27
|
+
ResponseProcessingOptions,
|
|
28
|
+
RoiCorrelationScores,
|
|
29
|
+
)
|
|
30
|
+
from twopy.analysis.response_processing.persistence import (
|
|
31
|
+
read_response_processing_group,
|
|
32
|
+
write_response_processing_group,
|
|
33
|
+
)
|
|
26
34
|
from twopy.analysis.responses import (
|
|
27
35
|
GroupedRoiResponses,
|
|
28
36
|
RoiResponseTrial,
|
|
@@ -87,6 +95,8 @@ class LoadedAnalysisOutputs:
|
|
|
87
95
|
dff: RoiDeltaFOverF | None
|
|
88
96
|
epoch_windows: tuple[EpochFrameWindow, ...]
|
|
89
97
|
grouped_responses: GroupedRoiResponses | None
|
|
98
|
+
response_processing_options: ResponseProcessingOptions | None
|
|
99
|
+
correlation_scores: RoiCorrelationScores | None
|
|
90
100
|
|
|
91
101
|
|
|
92
102
|
def save_analysis_outputs(
|
|
@@ -97,6 +107,8 @@ def save_analysis_outputs(
|
|
|
97
107
|
dff: RoiDeltaFOverF | None = None,
|
|
98
108
|
epoch_windows: Sequence[EpochFrameWindow] = (),
|
|
99
109
|
grouped_responses: GroupedRoiResponses | None = None,
|
|
110
|
+
response_processing_options: ResponseProcessingOptions | None = None,
|
|
111
|
+
correlation_scores: RoiCorrelationScores | None = None,
|
|
100
112
|
response_summary_trials_csv: Path | None = None,
|
|
101
113
|
response_summary_grouped_csv: Path | None = None,
|
|
102
114
|
) -> None:
|
|
@@ -109,6 +121,9 @@ def save_analysis_outputs(
|
|
|
109
121
|
dff: Optional ROI dF/F result.
|
|
110
122
|
epoch_windows: Optional stimulus windows used for response grouping.
|
|
111
123
|
grouped_responses: Optional grouped trial responses.
|
|
124
|
+
response_processing_options: Optional smoothing, low-pass, and
|
|
125
|
+
correlation-QC settings used for these outputs.
|
|
126
|
+
correlation_scores: Optional ROI-level correlation QC scores.
|
|
112
127
|
response_summary_trials_csv: Optional CSV path for one row per trial
|
|
113
128
|
and ROI, with one column per relative response timepoint.
|
|
114
129
|
response_summary_grouped_csv: Optional CSV path for one row per epoch,
|
|
@@ -129,6 +144,9 @@ def save_analysis_outputs(
|
|
|
129
144
|
if any(path is not None for path in summary_outputs) and grouped_responses is None:
|
|
130
145
|
msg = "response summary CSV outputs require grouped_responses"
|
|
131
146
|
raise ValueError(msg)
|
|
147
|
+
if correlation_scores is not None and response_processing_options is None:
|
|
148
|
+
msg = "correlation_scores require response_processing_options"
|
|
149
|
+
raise ValueError(msg)
|
|
132
150
|
|
|
133
151
|
output_path = path.expanduser()
|
|
134
152
|
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
@@ -147,6 +165,12 @@ def save_analysis_outputs(
|
|
|
147
165
|
h5_file.create_group("responses"),
|
|
148
166
|
grouped_responses,
|
|
149
167
|
)
|
|
168
|
+
if response_processing_options is not None:
|
|
169
|
+
write_response_processing_group(
|
|
170
|
+
h5_file.create_group("response_processing"),
|
|
171
|
+
options=response_processing_options,
|
|
172
|
+
correlation_scores=correlation_scores,
|
|
173
|
+
)
|
|
150
174
|
|
|
151
175
|
if grouped_responses is not None:
|
|
152
176
|
if response_summary_trials_csv is not None:
|
|
@@ -177,6 +201,11 @@ def load_analysis_outputs(path: Path) -> LoadedAnalysisOutputs:
|
|
|
177
201
|
input_path = path.expanduser()
|
|
178
202
|
with h5py.File(input_path, "r") as h5_file:
|
|
179
203
|
_require_analysis_format(h5_file, input_path)
|
|
204
|
+
response_processing = (
|
|
205
|
+
read_response_processing_group(h5_file["response_processing"])
|
|
206
|
+
if "response_processing" in h5_file
|
|
207
|
+
else None
|
|
208
|
+
)
|
|
180
209
|
return LoadedAnalysisOutputs(
|
|
181
210
|
path=input_path,
|
|
182
211
|
roi_set=(
|
|
@@ -198,6 +227,14 @@ def load_analysis_outputs(path: Path) -> LoadedAnalysisOutputs:
|
|
|
198
227
|
if "responses" in h5_file
|
|
199
228
|
else None
|
|
200
229
|
),
|
|
230
|
+
response_processing_options=(
|
|
231
|
+
response_processing.options if response_processing is not None else None
|
|
232
|
+
),
|
|
233
|
+
correlation_scores=(
|
|
234
|
+
response_processing.correlation_scores
|
|
235
|
+
if response_processing is not None
|
|
236
|
+
else None
|
|
237
|
+
),
|
|
201
238
|
)
|
|
202
239
|
|
|
203
240
|
|
|
@@ -12,6 +12,8 @@ how to save the resulting audit trail.
|
|
|
12
12
|
|
|
13
13
|
from twopy.analysis.response_processing.apply import (
|
|
14
14
|
ResponseProcessingResult,
|
|
15
|
+
apply_correlation_filter_to_grouped_roi_responses,
|
|
16
|
+
mask_grouped_roi_responses_by_included_rois,
|
|
15
17
|
process_grouped_roi_responses,
|
|
16
18
|
process_roi_delta_f_over_f,
|
|
17
19
|
)
|
|
@@ -33,6 +35,7 @@ from twopy.analysis.response_processing.qc import (
|
|
|
33
35
|
from twopy.analysis.response_processing.signals import (
|
|
34
36
|
butterworth_low_pass,
|
|
35
37
|
nan_aware_moving_average,
|
|
38
|
+
nan_aware_savgol_filter,
|
|
36
39
|
)
|
|
37
40
|
|
|
38
41
|
__all__ = [
|
|
@@ -42,7 +45,10 @@ __all__ = [
|
|
|
42
45
|
"CorrelationWindowSeconds",
|
|
43
46
|
"LowPassFilterMethod",
|
|
44
47
|
"LowPassFilterOptions",
|
|
48
|
+
"apply_correlation_filter_to_grouped_roi_responses",
|
|
49
|
+
"mask_grouped_roi_responses_by_included_rois",
|
|
45
50
|
"nan_aware_moving_average",
|
|
51
|
+
"nan_aware_savgol_filter",
|
|
46
52
|
"process_grouped_roi_responses",
|
|
47
53
|
"process_roi_delta_f_over_f",
|
|
48
54
|
"ResponseProcessingOptions",
|
|
@@ -25,10 +25,13 @@ from twopy.analysis.response_processing.qc import (
|
|
|
25
25
|
from twopy.analysis.response_processing.signals import (
|
|
26
26
|
butterworth_low_pass,
|
|
27
27
|
nan_aware_moving_average,
|
|
28
|
+
nan_aware_savgol_filter,
|
|
28
29
|
)
|
|
29
30
|
from twopy.analysis.responses import GroupedRoiResponses, RoiResponseTrial
|
|
30
31
|
|
|
31
32
|
__all__ = [
|
|
33
|
+
"apply_correlation_filter_to_grouped_roi_responses",
|
|
34
|
+
"mask_grouped_roi_responses_by_included_rois",
|
|
32
35
|
"ResponseProcessingResult",
|
|
33
36
|
"process_grouped_roi_responses",
|
|
34
37
|
"process_roi_delta_f_over_f",
|
|
@@ -122,18 +125,10 @@ def process_grouped_roi_responses(
|
|
|
122
125
|
validate_response_processing_options(options, data_rate_hz=grouped.data_rate_hz)
|
|
123
126
|
_validate_grouped_for_processing(grouped)
|
|
124
127
|
processed = _copy_grouped_with_processed_values(grouped, options=options)
|
|
125
|
-
correlation_scores =
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
minimum_correlation=options.correlation_filter.minimum_correlation,
|
|
130
|
-
reference=options.correlation_filter.reference,
|
|
131
|
-
window_seconds=options.correlation_filter.window_seconds,
|
|
132
|
-
)
|
|
133
|
-
processed = _mask_excluded_rois(
|
|
134
|
-
processed,
|
|
135
|
-
included_mask=correlation_scores.included_mask,
|
|
136
|
-
)
|
|
128
|
+
processed, correlation_scores = apply_correlation_filter_to_grouped_roi_responses(
|
|
129
|
+
processed,
|
|
130
|
+
options=options,
|
|
131
|
+
)
|
|
137
132
|
return ResponseProcessingResult(
|
|
138
133
|
grouped_responses=processed,
|
|
139
134
|
options=options,
|
|
@@ -141,6 +136,65 @@ def process_grouped_roi_responses(
|
|
|
141
136
|
)
|
|
142
137
|
|
|
143
138
|
|
|
139
|
+
def apply_correlation_filter_to_grouped_roi_responses(
|
|
140
|
+
grouped: GroupedRoiResponses,
|
|
141
|
+
*,
|
|
142
|
+
options: ResponseProcessingOptions,
|
|
143
|
+
) -> tuple[GroupedRoiResponses, RoiCorrelationScores | None]:
|
|
144
|
+
"""Apply only the correlation filter from a processing option object.
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
grouped: Grouped ROI responses to score and optionally mask.
|
|
148
|
+
options: Complete processing options. Smoothing and low-pass settings
|
|
149
|
+
are validated but not re-applied here.
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
``(grouped_responses, scores)``. ``scores`` is ``None`` when the
|
|
153
|
+
correlation filter is disabled.
|
|
154
|
+
|
|
155
|
+
This helper lets the standard workflow process continuous dF/F first, then
|
|
156
|
+
perform trial-level quality control after grouping without accidentally
|
|
157
|
+
smoothing or filtering the same traces a second time.
|
|
158
|
+
"""
|
|
159
|
+
validate_response_processing_options(options, data_rate_hz=grouped.data_rate_hz)
|
|
160
|
+
_validate_grouped_for_processing(grouped)
|
|
161
|
+
if options.correlation_filter.reference not in {"epoch_mean", "epoch_peak"}:
|
|
162
|
+
return grouped, None
|
|
163
|
+
correlation_scores = score_roi_correlations(
|
|
164
|
+
grouped,
|
|
165
|
+
minimum_correlation=options.correlation_filter.minimum_correlation,
|
|
166
|
+
reference=options.correlation_filter.reference,
|
|
167
|
+
window_seconds=options.correlation_filter.window_seconds,
|
|
168
|
+
)
|
|
169
|
+
return (
|
|
170
|
+
mask_grouped_roi_responses_by_included_rois(
|
|
171
|
+
grouped,
|
|
172
|
+
included_mask=correlation_scores.included_mask,
|
|
173
|
+
),
|
|
174
|
+
correlation_scores,
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def mask_grouped_roi_responses_by_included_rois(
|
|
179
|
+
grouped: GroupedRoiResponses,
|
|
180
|
+
*,
|
|
181
|
+
included_mask: npt.NDArray[np.bool_],
|
|
182
|
+
) -> GroupedRoiResponses:
|
|
183
|
+
"""Return grouped responses with excluded ROI columns set to NaN.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
grouped: Grouped ROI responses to mask.
|
|
187
|
+
included_mask: Boolean vector matching ``grouped.roi_labels``.
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
New grouped responses with the same shape and labels.
|
|
191
|
+
|
|
192
|
+
The mask is public because persisted QC scores should be able to hide ROIs
|
|
193
|
+
during plot reloads without recomputing the scientific QC decision.
|
|
194
|
+
"""
|
|
195
|
+
return _mask_excluded_rois(grouped, included_mask=included_mask)
|
|
196
|
+
|
|
197
|
+
|
|
144
198
|
def _process_values(
|
|
145
199
|
values: npt.NDArray[np.float64],
|
|
146
200
|
*,
|
|
@@ -154,6 +208,12 @@ def _process_values(
|
|
|
154
208
|
processed,
|
|
155
209
|
window_frames=options.smoothing.window_frames,
|
|
156
210
|
)
|
|
211
|
+
if options.smoothing.method == "savgol":
|
|
212
|
+
processed = nan_aware_savgol_filter(
|
|
213
|
+
processed,
|
|
214
|
+
window_frames=options.smoothing.window_frames,
|
|
215
|
+
polynomial_order=options.smoothing.polynomial_order,
|
|
216
|
+
)
|
|
157
217
|
if options.low_pass.method == "butterworth":
|
|
158
218
|
if options.low_pass.cutoff_hz is None:
|
|
159
219
|
msg = "enabled low-pass filtering requires cutoff_hz"
|
|
@@ -272,6 +332,9 @@ def _processing_metadata(
|
|
|
272
332
|
return {
|
|
273
333
|
"response_processing_smoothing_method": options.smoothing.method,
|
|
274
334
|
"response_processing_smoothing_window_frames": options.smoothing.window_frames,
|
|
335
|
+
"response_processing_smoothing_polynomial_order": (
|
|
336
|
+
options.smoothing.polynomial_order
|
|
337
|
+
),
|
|
275
338
|
"response_processing_low_pass_method": options.low_pass.method,
|
|
276
339
|
"response_processing_low_pass_cutoff_hz": (
|
|
277
340
|
"none"
|
|
@@ -25,7 +25,7 @@ __all__ = [
|
|
|
25
25
|
"validate_response_processing_options",
|
|
26
26
|
]
|
|
27
27
|
|
|
28
|
-
SmoothingMethod = Literal["none", "moving_average"]
|
|
28
|
+
SmoothingMethod = Literal["none", "moving_average", "savgol"]
|
|
29
29
|
LowPassFilterMethod = Literal["none", "butterworth"]
|
|
30
30
|
CorrelationFilterReference = Literal["none", "epoch_mean", "epoch_peak"]
|
|
31
31
|
CorrelationWindowSeconds = tuple[float | None, float | None]
|
|
@@ -33,17 +33,21 @@ CorrelationWindowSeconds = tuple[float | None, float | None]
|
|
|
33
33
|
|
|
34
34
|
@dataclass(frozen=True)
|
|
35
35
|
class SmoothingOptions:
|
|
36
|
-
"""
|
|
36
|
+
"""Response-trace smoothing settings.
|
|
37
37
|
|
|
38
38
|
Inputs: a smoothing method and window length in imaging frames.
|
|
39
39
|
Outputs: a typed contract consumed by signal-processing kernels.
|
|
40
40
|
|
|
41
|
-
``method="none"`` leaves values unchanged. ``window_frames``
|
|
42
|
-
explicit so saved options can report
|
|
41
|
+
``method="none"`` leaves values unchanged. ``window_frames`` and
|
|
42
|
+
``polynomial_order`` are still kept explicit so saved options can report
|
|
43
|
+
exactly what the user selected. ``method="savgol"`` defaults to a
|
|
44
|
+
seven-frame, second-order Savitzky-Golay smoother because that preserves
|
|
45
|
+
local peak shape better than a moving average.
|
|
43
46
|
"""
|
|
44
47
|
|
|
45
48
|
method: SmoothingMethod = "none"
|
|
46
|
-
window_frames: int =
|
|
49
|
+
window_frames: int = 7
|
|
50
|
+
polynomial_order: int = 2
|
|
47
51
|
|
|
48
52
|
|
|
49
53
|
@dataclass(frozen=True)
|
|
@@ -124,12 +128,32 @@ def validate_response_processing_options(
|
|
|
124
128
|
|
|
125
129
|
def _validate_smoothing_options(options: SmoothingOptions) -> None:
|
|
126
130
|
"""Validate smoothing options."""
|
|
127
|
-
if options.method not in {"none", "moving_average"}:
|
|
131
|
+
if options.method not in {"none", "moving_average", "savgol"}:
|
|
128
132
|
msg = f"Unknown smoothing method {options.method!r}"
|
|
129
133
|
raise ValueError(msg)
|
|
130
134
|
if options.window_frames < 1:
|
|
131
135
|
msg = f"smoothing window_frames must be at least 1; got {options.window_frames}"
|
|
132
136
|
raise ValueError(msg)
|
|
137
|
+
if options.polynomial_order < 0:
|
|
138
|
+
msg = (
|
|
139
|
+
"smoothing polynomial_order must be non-negative; "
|
|
140
|
+
f"got {options.polynomial_order}"
|
|
141
|
+
)
|
|
142
|
+
raise ValueError(msg)
|
|
143
|
+
if options.method == "savgol":
|
|
144
|
+
if options.window_frames % 2 == 0:
|
|
145
|
+
msg = (
|
|
146
|
+
"Savitzky-Golay smoothing window_frames must be odd; "
|
|
147
|
+
f"got {options.window_frames}"
|
|
148
|
+
)
|
|
149
|
+
raise ValueError(msg)
|
|
150
|
+
if options.polynomial_order >= options.window_frames:
|
|
151
|
+
msg = (
|
|
152
|
+
"Savitzky-Golay polynomial_order must be below window_frames; "
|
|
153
|
+
f"got order {options.polynomial_order} and window "
|
|
154
|
+
f"{options.window_frames}"
|
|
155
|
+
)
|
|
156
|
+
raise ValueError(msg)
|
|
133
157
|
|
|
134
158
|
|
|
135
159
|
def _validate_low_pass_options(
|