TwoPy 0.3.8__tar.gz → 0.4.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.
- {twopy-0.3.8 → twopy-0.4.0}/PKG-INFO +3 -3
- {twopy-0.3.8 → twopy-0.4.0}/README.md +2 -2
- {twopy-0.3.8 → twopy-0.4.0}/pyproject.toml +1 -1
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/analysis/background_subtraction.py +10 -10
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/analysis/dff.py +13 -13
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/analysis/epoch_mapping.py +1 -1
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/analysis/group_matching.py +1 -1
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/analysis/kernels/__init__.py +4 -4
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/analysis/persistence.py +1 -1
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/analysis/response_maps.py +17 -17
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/analysis/response_processing/__init__.py +1 -1
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/analysis/response_processing/apply.py +5 -5
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/analysis/response_processing/options.py +18 -18
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/analysis/response_processing/qc.py +2 -2
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/analysis/response_processing/signals.py +12 -12
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/analysis/response_window_options.py +2 -2
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/analysis/responses.py +16 -16
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/analysis/timing.py +1 -1
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/analysis/trials.py +2 -2
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/analysis/workflow.py +3 -3
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/analysis_cache.py +1 -1
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/config.py +3 -3
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/conversion/alignment_crop.py +36 -4
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/conversion/hdf5_writing.py +7 -2
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/conversion/orientation.py +4 -4
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/conversion/stimulus_code.py +1 -1
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/conversion/types.py +3 -2
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/converted.py +50 -18
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/custom/context.py +6 -6
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/custom/native_workflows/direction_selectivity.py +2 -2
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/custom/native_workflows/response_kernels.py +2 -2
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/custom/parameters.py +1 -1
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/custom/provenance.py +8 -8
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/custom/runner.py +1 -1
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/database/cache.py +1 -1
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/database/catalog.py +1 -1
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/database/modeled.py +1 -1
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/database/search.py +2 -2
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/database/types.py +1 -1
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/matlab.py +1 -1
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/controls.py +44 -36
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/custom_tab.py +119 -41
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/database_favorite_editor.py +2 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/database_search.py +169 -38
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/empty_state.py +24 -14
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/group_matching/cards.py +2 -2
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/group_matching/fov_assignment.py +24 -5
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/group_matching/fov_cards.py +18 -3
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/group_matching/response_preview.py +14 -5
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/group_matching/roi_assignment.py +2 -2
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/group_matching/roi_cards.py +5 -4
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/group_matching/style.py +57 -91
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/group_matching/window.py +0 -17
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/launcher.py +3 -3
- twopy-0.4.0/src/twopy/napari/load_picker.py +184 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/load_progress.py +28 -90
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/load_workflow.py +6 -6
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/movie.py +4 -4
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/output_routing.py +1 -1
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/plotting/docks/dynamic_options.py +3 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/plotting/docks/options_panel.py +9 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/plotting/docks/plot_area.py +6 -4
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/plotting/docks/response_map_area.py +6 -4
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/plotting/docks/response_plot_widget.py +21 -8
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/plotting/docks/save_actions.py +4 -4
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/plotting/export.py +1 -1
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/plotting/export_controls.py +6 -4
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/plotting/label_visibility.py +1 -1
- twopy-0.4.0/src/twopy/napari/plotting/motion_summary.py +340 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/plotting/options.py +6 -4
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/plotting/panels.py +19 -4
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/plotting/response_map_display.py +2 -2
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/plotting/response_map_options.py +1 -1
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/plotting/roi_generation/actions.py +1 -1
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/plotting/roi_generation/controls.py +2 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/plotting/widgets.py +17 -6
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/responses.py +1 -1
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/roi.py +1 -1
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/session.py +47 -11
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/sidebar.py +51 -8
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/state.py +24 -7
- twopy-0.4.0/src/twopy/napari/theme.py +661 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/trial_timeline.py +9 -4
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/version_check.py +5 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/viewer.py +1 -1
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/parity/__init__.py +1 -1
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/parity/dff.py +5 -5
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/parity/psycho5.py +6 -6
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/parity/roi.py +2 -2
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/parity/roi_extraction.py +6 -6
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/parity/saved_analysis.py +1 -1
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/photodiode.py +3 -3
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/photodiode_classification.py +4 -4
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/pixel_calibration.py +3 -3
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/response_roi_extraction.py +16 -16
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/roi.py +4 -4
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/roi_extraction.py +7 -7
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/session.py +1 -1
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/stimulus.py +2 -2
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/synchronization.py +1 -1
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/typing_guards.py +3 -3
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy.egg-info/PKG-INFO +3 -3
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy.egg-info/SOURCES.txt +6 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_background_subtraction.py +4 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_conversion.py +34 -1
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_converted.py +48 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_load_workflow.py +1 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_napari.py +48 -5
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_napari_database_search.py +81 -7
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_napari_export.py +1 -1
- twopy-0.4.0/tests/test_napari_load_picker.py +191 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_napari_load_progress.py +2 -1
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_napari_load_tab.py +55 -12
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_napari_loaded_recordings.py +61 -2
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_napari_plot_controls.py +181 -3
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_napari_plot_data.py +1 -1
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_napari_plot_widgets.py +76 -21
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_napari_processing_controls.py +37 -13
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_napari_response_workflow.py +1 -1
- twopy-0.4.0/tests/test_napari_state.py +89 -0
- twopy-0.4.0/tests/test_napari_theme.py +302 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_parity_psycho5.py +1 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_real_fixture.py +4 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_roi.py +4 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_version_check.py +4 -0
- {twopy-0.3.8 → twopy-0.4.0}/setup.cfg +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/__init__.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/__init__.pyi +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/_segments.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/_version.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/analysis/__init__.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/analysis/dff_options.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/analysis/kernels/_fitting.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/analysis/kernels/_stimulus.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/analysis/motion.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/analysis/response_context.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/analysis/response_plotting.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/analysis/response_processing/persistence.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/api.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/cache_inventory.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/calibrations/pixel_calibration_profiles.csv +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/calibrations/pixel_size.csv +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/config.example.yml +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/conversion/__init__.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/conversion/api.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/conversion/matlab_values.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/conversion/source_loading.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/custom/__init__.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/custom/discovery.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/custom/metadata.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/custom/native.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/custom/native_workflows/__init__.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/custom/registry.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/custom/results.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/custom/workflows.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/database/__init__.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/database/connection.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/database/generic.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/database/sql.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/filenames.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/frame_ranges.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/hdf5_utils.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/inspection.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/__init__.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/__main__.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/assets/twopy-app-icon-128.png +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/assets/twopy-app-icon-16.png +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/assets/twopy-app-icon-256.png +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/assets/twopy-app-icon-32.png +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/assets/twopy-app-icon-512.png +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/assets/twopy-app-icon-64.png +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/assets/twopy-app-icon.icns +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/assets/twopy-app-icon.png +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/database_favorites.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/dims.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/display.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/display_paths.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/errors.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/group_matching/__init__.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/group_matching/images.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/group_matching/protocols.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/group_matching/responses.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/group_matching/tables.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/interactive.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/latest_worker.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/live_analysis.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/load_controller.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/loaded_recordings_csv.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/loading.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/paths.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/plotting/__init__.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/plotting/data.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/plotting/dff_options.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/plotting/docks/__init__.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/plotting/docks/api.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/plotting/docks/paths.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/plotting/docks/visibility_state.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/plotting/form_controls.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/plotting/normalization_options.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/plotting/preview_strip.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/plotting/processing_options.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/plotting/response_map_colors.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/plotting/response_window_options.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/plotting/roi_generation/__init__.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/plotting/roi_generation/options.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/protocols.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/roi_signatures.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/text.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/napari/types.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/pixel_calibration_profiles.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/roi_mask_cleanup.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/spatial.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy/spatial_orientation.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy.egg-info/dependency_links.txt +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy.egg-info/entry_points.txt +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy.egg-info/requires.txt +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/src/twopy.egg-info/top_level.txt +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_api.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_cache_inventory.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_config.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_custom_workflows.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_database.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_dff.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_epoch_mapping.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_filenames.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_frame_ranges.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_function_inventory.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_group_matching.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_hdf5_utils.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_import.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_inspection.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_kernels.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_matlab.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_motion.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_napari_display.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_napari_group_matching_responses.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_napari_group_matching_roi_cards.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_napari_live_controller.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_napari_load_controller.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_napari_path_resolution.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_napari_response_map_display.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_napari_roi_generation.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_napari_roi_visibility.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_napari_text.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_napari_timeline.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_packaging.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_parity.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_parity_roi_extraction.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_persistence.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_photodiode_classification.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_pixel_calibration.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_pixel_calibration_profiles.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_recording_timing.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_response_map_area.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_response_maps.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_response_processing.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_response_roi_extraction.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_response_window_options.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_responses.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_roi_extraction.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_roi_mask_cleanup.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_saved_analysis.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_session.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_stimulus.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_synchronization.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_trials.py +0 -0
- {twopy-0.3.8 → twopy-0.4.0}/tests/test_workflow.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: twopy
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
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
|
|
@@ -31,7 +31,7 @@ Two-photon imaging analysis tool for the Clark Lab output format.
|
|
|
31
31
|
|
|
32
32
|
twopy lets you open two-photon recordings, draw ROIs, plot responses in real time, process and analyze them, and save them.
|
|
33
33
|
|
|
34
|
-
When you first load a recording, twopy converts it to HDF5 files that it owns.
|
|
34
|
+
When you first load a recording, twopy converts it to HDF5 files that it owns. These files contain the aligned movie, mean image, stimulus tables, photodiode signals, and recording metadata. Analysis and the GUI use only the converted files. Thus, the original source files stay separate from twopy outputs. By default, twopy uses `~/.cache/twopy/recordings` as a local cache. When you save ROIs and analysis, twopy copies changed files to `analysis_output` in the background.
|
|
35
35
|
|
|
36
36
|
## Install
|
|
37
37
|
|
|
@@ -55,7 +55,7 @@ Create the config template in your user folder:
|
|
|
55
55
|
twopy config setup
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
-
twopy prints the config path it wrote.
|
|
58
|
+
twopy prints the config path that it wrote. The default path on macOS and Linux is `~/.config/twopy/config.yml`. On Windows, the path is under `%APPDATA%\twopy\config.yml`. Before you start twopy, add your database, data, and output paths to that file. If the config does not exist, twopy creates the template and prints its path. Then, twopy stops so you can edit it.
|
|
59
59
|
|
|
60
60
|
Validate the active config and show its path and file contents:
|
|
61
61
|
|
|
@@ -8,7 +8,7 @@ Two-photon imaging analysis tool for the Clark Lab output format.
|
|
|
8
8
|
|
|
9
9
|
twopy lets you open two-photon recordings, draw ROIs, plot responses in real time, process and analyze them, and save them.
|
|
10
10
|
|
|
11
|
-
When you first load a recording, twopy converts it to HDF5 files that it owns.
|
|
11
|
+
When you first load a recording, twopy converts it to HDF5 files that it owns. These files contain the aligned movie, mean image, stimulus tables, photodiode signals, and recording metadata. Analysis and the GUI use only the converted files. Thus, the original source files stay separate from twopy outputs. By default, twopy uses `~/.cache/twopy/recordings` as a local cache. When you save ROIs and analysis, twopy copies changed files to `analysis_output` in the background.
|
|
12
12
|
|
|
13
13
|
## Install
|
|
14
14
|
|
|
@@ -32,7 +32,7 @@ Create the config template in your user folder:
|
|
|
32
32
|
twopy config setup
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
twopy prints the config path it wrote.
|
|
35
|
+
twopy prints the config path that it wrote. The default path on macOS and Linux is `~/.config/twopy/config.yml`. On Windows, the path is under `%APPDATA%\twopy\config.yml`. Before you start twopy, add your database, data, and output paths to that file. If the config does not exist, twopy creates the template and prints its path. Then, twopy stops so you can edit it.
|
|
36
36
|
|
|
37
37
|
Validate the active config and show its path and file contents:
|
|
38
38
|
|
|
@@ -5,7 +5,7 @@ Outputs: raw ROI traces, estimated background traces, corrected traces, and
|
|
|
5
5
|
method metadata.
|
|
6
6
|
|
|
7
7
|
This module owns analysis-time background correction. ROI storage and raw ROI
|
|
8
|
-
trace extraction stay in ``twopy.roi
|
|
8
|
+
trace extraction stay in ``twopy.roi``. dF/F and trial analysis can use the
|
|
9
9
|
auditable outputs produced here.
|
|
10
10
|
"""
|
|
11
11
|
|
|
@@ -243,8 +243,8 @@ def _validate_background_method(method: BackgroundCorrectionMethod) -> None:
|
|
|
243
243
|
}
|
|
244
244
|
if method not in allowed_methods:
|
|
245
245
|
msg = (
|
|
246
|
-
f"Unknown background correction method {method!r}
|
|
247
|
-
f"
|
|
246
|
+
f"Unknown background correction method {method!r}. "
|
|
247
|
+
f"Supported methods: {', '.join(sorted(allowed_methods))}"
|
|
248
248
|
)
|
|
249
249
|
raise ValueError(msg)
|
|
250
250
|
|
|
@@ -722,7 +722,7 @@ def _crop_roi_masks_for_domain(
|
|
|
722
722
|
if outside.size:
|
|
723
723
|
msg = (
|
|
724
724
|
"ROI pixels fall outside the selected spatial domain "
|
|
725
|
-
f"{spatial_crop.source}
|
|
725
|
+
f"{spatial_crop.source}. Affected ROI indices: {outside.tolist()}"
|
|
726
726
|
)
|
|
727
727
|
raise ValueError(msg)
|
|
728
728
|
return cropped_masks
|
|
@@ -759,7 +759,7 @@ def _validate_percentile(percentile: float) -> None:
|
|
|
759
759
|
rule at the extremes.
|
|
760
760
|
"""
|
|
761
761
|
if not 0.0 < percentile < 100.0:
|
|
762
|
-
msg = f"Background percentile must be between 0 and 100
|
|
762
|
+
msg = f"Background percentile must be between 0 and 100. Got {percentile}"
|
|
763
763
|
raise ValueError(msg)
|
|
764
764
|
|
|
765
765
|
|
|
@@ -808,7 +808,7 @@ def _resolve_roi_y_stripe_radius(
|
|
|
808
808
|
"""
|
|
809
809
|
radius = max(1, round(row_count / 20)) if local_y_radius is None else local_y_radius
|
|
810
810
|
if radius < 0:
|
|
811
|
-
msg = f"local_y_radius must be nonnegative
|
|
811
|
+
msg = f"local_y_radius must be nonnegative. Got {radius}"
|
|
812
812
|
raise ValueError(msg)
|
|
813
813
|
return radius
|
|
814
814
|
|
|
@@ -831,7 +831,7 @@ def _resolve_stripe_y_height(
|
|
|
831
831
|
max(1, round(row_count / 20)) if stripe_y_height is None else stripe_y_height
|
|
832
832
|
)
|
|
833
833
|
if height <= 0:
|
|
834
|
-
msg = f"stripe_y_height must be positive
|
|
834
|
+
msg = f"stripe_y_height must be positive. Got {height}"
|
|
835
835
|
raise ValueError(msg)
|
|
836
836
|
return height
|
|
837
837
|
|
|
@@ -912,8 +912,8 @@ def _roi_y_stripe_background_pixel_indices(
|
|
|
912
912
|
msg = (
|
|
913
913
|
"ROI y-stripe P% background subtraction needs unlabeled background "
|
|
914
914
|
"pixels outside all ROIs in the selected spatial domain. The current "
|
|
915
|
-
"ROI masks cover every pixel, which is common for dense grid ROIs
|
|
916
|
-
"
|
|
915
|
+
"ROI masks cover every pixel, which is common for dense grid ROIs. "
|
|
916
|
+
"Use shared y-stripe P% or leave unlabeled background pixels."
|
|
917
917
|
)
|
|
918
918
|
raise ValueError(msg)
|
|
919
919
|
|
|
@@ -930,7 +930,7 @@ def _roi_y_stripe_background_pixel_indices(
|
|
|
930
930
|
msg = (
|
|
931
931
|
f"ROI {roi_index} has no local background candidates within "
|
|
932
932
|
f"{local_y_radius} rows. ROI y-stripe P% only uses unlabeled "
|
|
933
|
-
"pixels outside all ROIs
|
|
933
|
+
"pixels outside all ROIs. Increase the local y radius, use "
|
|
934
934
|
"shared y-stripe P%, or leave unlabeled background pixels near "
|
|
935
935
|
"this ROI."
|
|
936
936
|
)
|
|
@@ -6,7 +6,7 @@ Outputs: ROI dF/F traces, fitted baselines, shared exponential tau, and
|
|
|
6
6
|
per-ROI amplitudes.
|
|
7
7
|
|
|
8
8
|
This module owns ROI-level dF/F calculation from twopy Python objects. It does
|
|
9
|
-
not decide which stimulus events are baseline epochs
|
|
9
|
+
not decide which stimulus events are baseline epochs. Callers pass explicit
|
|
10
10
|
baseline windows derived from converted data and photodiode alignment.
|
|
11
11
|
"""
|
|
12
12
|
|
|
@@ -200,8 +200,8 @@ def _validate_dff_inputs(
|
|
|
200
200
|
raise ValueError(msg)
|
|
201
201
|
if traces.corrected_values.shape[1] != len(traces.labels):
|
|
202
202
|
msg = (
|
|
203
|
-
"ROI labels must match corrected_values width
|
|
204
|
-
f"
|
|
203
|
+
"ROI labels must match corrected_values width. "
|
|
204
|
+
f"Got {len(traces.labels)} labels for "
|
|
205
205
|
f"{traces.corrected_values.shape[1]} ROIs"
|
|
206
206
|
)
|
|
207
207
|
raise ValueError(msg)
|
|
@@ -209,12 +209,12 @@ def _validate_dff_inputs(
|
|
|
209
209
|
msg = "At least one baseline window is required for dF/F"
|
|
210
210
|
raise ValueError(msg)
|
|
211
211
|
if data_rate_hz <= 0:
|
|
212
|
-
msg = f"data_rate_hz must be positive
|
|
212
|
+
msg = f"data_rate_hz must be positive. Got {data_rate_hz}"
|
|
213
213
|
raise ValueError(msg)
|
|
214
214
|
if baseline_sample_seconds is not None and baseline_sample_seconds <= 0:
|
|
215
215
|
msg = (
|
|
216
|
-
"baseline_sample_seconds must be positive or None
|
|
217
|
-
f"
|
|
216
|
+
"baseline_sample_seconds must be positive or None. "
|
|
217
|
+
f"Got {baseline_sample_seconds}"
|
|
218
218
|
)
|
|
219
219
|
raise ValueError(msg)
|
|
220
220
|
if fit_mode not in _FIT_MODES:
|
|
@@ -267,7 +267,7 @@ def _baseline_samples(
|
|
|
267
267
|
duration = sample_stop - sample_start
|
|
268
268
|
absolute_sample_start = trace_start_frame + sample_start
|
|
269
269
|
# The fit uses one-based movie frame numbers centered on the averaged
|
|
270
|
-
# half-open sample range. Local indices slice the trace array
|
|
270
|
+
# half-open sample range. Local indices slice the trace array. Absolute
|
|
271
271
|
# frame numbers preserve the recording-wide time coordinate.
|
|
272
272
|
frame_numbers.append(float(absolute_sample_start + 1) + (duration / 2.0))
|
|
273
273
|
values.append(fluorescence[sample_start:sample_stop, :].mean(axis=0))
|
|
@@ -298,7 +298,7 @@ def _validate_baseline_samples_for_background_method(
|
|
|
298
298
|
minimum = float(np.nanmin(baseline_samples.values[:, roi_indices]))
|
|
299
299
|
msg = (
|
|
300
300
|
"ROI y-stripe P% background subtraction over-subtracted baseline "
|
|
301
|
-
f"fluorescence for {labels}
|
|
301
|
+
f"fluorescence for {labels}. Minimum corrected baseline sample is "
|
|
302
302
|
f"{minimum:.6g}. dF/F needs positive baseline fluorescence, so the "
|
|
303
303
|
"local y-stripe pixels are not a valid additive background for those "
|
|
304
304
|
"ROIs. Use shared y-stripe P%, global percentile, or revise the ROIs "
|
|
@@ -383,10 +383,10 @@ def _fit_shared_tau(
|
|
|
383
383
|
Shared exponential ``tau``.
|
|
384
384
|
|
|
385
385
|
The fit model is ``exp(tau * t + b)`` on the ROI-averaged baseline trace.
|
|
386
|
-
Only ``tau`` is shared
|
|
386
|
+
Only ``tau`` is shared. Per-ROI amplitudes are computed in a separate
|
|
387
387
|
transparent step. ``direct_bounded_tau`` handles nonpositive outliers
|
|
388
|
-
defensively
|
|
389
|
-
log-amplitude contract
|
|
388
|
+
defensively. ``direct_bounded_tau_and_log_amplitude`` preserves a bounded
|
|
389
|
+
log-amplitude contract. The ``log_linear`` method fits in log space.
|
|
390
390
|
"""
|
|
391
391
|
if fit_mode == "log_linear":
|
|
392
392
|
return _fit_shared_tau_log_linear(
|
|
@@ -532,8 +532,8 @@ def _fit_shared_tau_with_bounded_log_amplitude(
|
|
|
532
532
|
msg = (
|
|
533
533
|
"direct_bounded_tau_and_log_amplitude dF/F fit requires first "
|
|
534
534
|
"baseline fluorescence greater than 1.0 to define an ordered "
|
|
535
|
-
"log-amplitude bound
|
|
536
|
-
f"
|
|
535
|
+
"log-amplitude bound. "
|
|
536
|
+
f"Got {first_value}"
|
|
537
537
|
)
|
|
538
538
|
raise ValueError(msg)
|
|
539
539
|
if np.any(mean_fluorescence <= 0):
|
|
@@ -118,7 +118,7 @@ def _interpolate_epoch_numbers(
|
|
|
118
118
|
Integer epoch number per imaging frame.
|
|
119
119
|
"""
|
|
120
120
|
if frame_count < 1:
|
|
121
|
-
msg = f"frame_count must be positive
|
|
121
|
+
msg = f"frame_count must be positive. Got {frame_count}"
|
|
122
122
|
raise ValueError(msg)
|
|
123
123
|
|
|
124
124
|
positive = epoch_values != 0
|
|
@@ -588,7 +588,7 @@ def _validate_status(status: ManualRoiMatchStatus) -> ManualRoiMatchStatus:
|
|
|
588
588
|
def _validate_group_cell_id(group_cell_id: int) -> int:
|
|
589
589
|
"""Return a positive group-cell id."""
|
|
590
590
|
if group_cell_id < 1:
|
|
591
|
-
msg = f"Manual ROI group_cell_id must be positive
|
|
591
|
+
msg = f"Manual ROI group_cell_id must be positive. Got {group_cell_id}."
|
|
592
592
|
raise ValueError(msg)
|
|
593
593
|
return group_cell_id
|
|
594
594
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Inputs: converted stimulus tables plus ROI response traces computed by twopy.
|
|
4
4
|
Outputs: per-ROI temporal kernels on the stimulus sample clock. Olfactory
|
|
5
|
-
recordings expose left/right and ipsi/contra kernels
|
|
5
|
+
recordings expose left/right and ipsi/contra kernels. Visual recordings expose
|
|
6
6
|
one contrast kernel.
|
|
7
7
|
|
|
8
8
|
This package keeps reverse-correlation analysis native to twopy data. It does
|
|
@@ -65,7 +65,7 @@ class StimulusKernelOptions:
|
|
|
65
65
|
epochs and applies ``discard_first_stimulus_epoch``.
|
|
66
66
|
num_stim_past: Number of stimulus samples before a response sample.
|
|
67
67
|
num_stim_future: Number of stimulus samples after a response sample.
|
|
68
|
-
method: Fitting method. ``"ols"`` solves a least-squares design matrix
|
|
68
|
+
method: Fitting method. ``"ols"`` solves a least-squares design matrix.
|
|
69
69
|
``"xcorr"`` computes reverse correlation.
|
|
70
70
|
hemisphere: Recording hemisphere used to map raw left/right streams to
|
|
71
71
|
ipsi/contra. ``None`` reads the value from converted recording
|
|
@@ -93,7 +93,7 @@ class RecordingKernelFit:
|
|
|
93
93
|
Args:
|
|
94
94
|
roi_labels: Labels for the fitted ROI rows.
|
|
95
95
|
time_seconds: Kernel sample times. Negative times are future stimulus
|
|
96
|
-
samples
|
|
96
|
+
samples. Positive times are stimulus samples before the response.
|
|
97
97
|
stimulus_modality: Sensory modality used to interpret the stimulus.
|
|
98
98
|
stimulus_column: Converted stimulus column used for fitting.
|
|
99
99
|
epoch_names: Unique readable epoch names fitted independently.
|
|
@@ -347,6 +347,6 @@ def _validate_options(options: StimulusKernelOptions) -> None:
|
|
|
347
347
|
raise ValueError(msg)
|
|
348
348
|
if options.hemisphere is not None and options.hemisphere not in {"left", "right"}:
|
|
349
349
|
msg = (
|
|
350
|
-
f"hemisphere must be 'left', 'right', or None
|
|
350
|
+
f"hemisphere must be 'left', 'right', or None. Got {options.hemisphere!r}."
|
|
351
351
|
)
|
|
352
352
|
raise ValueError(msg)
|
|
@@ -164,7 +164,7 @@ def save_analysis_outputs(
|
|
|
164
164
|
The HDF5 file is the complete audit trail. CSV files are derived exports for
|
|
165
165
|
quick spreadsheet plotting and checks, and are written after the HDF5 file.
|
|
166
166
|
Each output file is written through a temporary sibling path so a failed
|
|
167
|
-
write leaves the previous complete file in place
|
|
167
|
+
write leaves the previous complete file in place. The HDF5 and CSV files are
|
|
168
168
|
not replaced as one multi-file transaction.
|
|
169
169
|
"""
|
|
170
170
|
summary_outputs = (
|
|
@@ -86,7 +86,7 @@ class ResponseMapOptions:
|
|
|
86
86
|
finite response across all epochs has magnitude 1. The original dF/F divisor
|
|
87
87
|
is stored on ``ResponseMapData``.
|
|
88
88
|
|
|
89
|
-
Pixel mode is the default because it preserves spatial detail
|
|
89
|
+
Pixel mode is the default because it preserves spatial detail. Its default
|
|
90
90
|
two-pixel Gaussian smoothing reduces isolated noisy pixels without changing
|
|
91
91
|
the saved analysis contract. Window mode is useful when users want explicit
|
|
92
92
|
block averaging before dF/F. Window size and stride are constrained by the
|
|
@@ -110,7 +110,7 @@ class EpochResponseMap:
|
|
|
110
110
|
epoch_number: One-based stimulus epoch number.
|
|
111
111
|
response_values: Movie-coordinate signed response map for the displayed
|
|
112
112
|
crop. Values are normalized by ``ResponseMapData.response_scale``
|
|
113
|
-
after epoch averaging
|
|
113
|
+
after epoch averaging. Multiply by that scale to recover the
|
|
114
114
|
original dF/F magnitude.
|
|
115
115
|
trial_count: Number of trials averaged into the map.
|
|
116
116
|
"""
|
|
@@ -165,7 +165,7 @@ def compute_recording_response_maps(
|
|
|
165
165
|
|
|
166
166
|
This reads the converted aligned movie for the valid crop, not microscope
|
|
167
167
|
source MAT/TIFF files. Callers can pass explicit epoch windows for tests or
|
|
168
|
-
audits
|
|
168
|
+
audits. Normal workflows let twopy derive photodiode-aligned windows from
|
|
169
169
|
converted synchronization metadata.
|
|
170
170
|
"""
|
|
171
171
|
resolved_options = options or ResponseMapOptions()
|
|
@@ -398,20 +398,20 @@ def _require_response_map_shapes(map_data: ResponseMapData) -> None:
|
|
|
398
398
|
"""Require heatmap spatial arrays to match their recorded crop."""
|
|
399
399
|
if map_data.mean_image.ndim != 2:
|
|
400
400
|
msg = (
|
|
401
|
-
f"response heatmap mean_image must be 2-D
|
|
401
|
+
f"response heatmap mean_image must be 2-D. Got {map_data.mean_image.shape}"
|
|
402
402
|
)
|
|
403
403
|
raise ValueError(msg)
|
|
404
404
|
if map_data.mean_image.shape != map_data.spatial_crop.shape:
|
|
405
405
|
msg = (
|
|
406
|
-
"response heatmap mean_image shape must match spatial_crop shape
|
|
407
|
-
f"
|
|
406
|
+
"response heatmap mean_image shape must match spatial_crop shape. "
|
|
407
|
+
f"Got {map_data.mean_image.shape} and {map_data.spatial_crop.shape}"
|
|
408
408
|
)
|
|
409
409
|
raise ValueError(msg)
|
|
410
410
|
for epoch in map_data.epochs:
|
|
411
411
|
if epoch.response_values.shape != map_data.mean_image.shape:
|
|
412
412
|
msg = (
|
|
413
413
|
"response heatmap response_values shape must match mean_image "
|
|
414
|
-
f"shape
|
|
414
|
+
f"shape. Got {epoch.response_values.shape} for "
|
|
415
415
|
f"{epoch.epoch_name!r} and {map_data.mean_image.shape}"
|
|
416
416
|
)
|
|
417
417
|
raise ValueError(msg)
|
|
@@ -454,22 +454,22 @@ def _validate_options(options: ResponseMapOptions, shape: tuple[int, int]) -> No
|
|
|
454
454
|
or options.pixel_smoothing_sigma < 0
|
|
455
455
|
):
|
|
456
456
|
msg = (
|
|
457
|
-
"pixel_smoothing_sigma must be finite and nonnegative
|
|
458
|
-
f"
|
|
457
|
+
"pixel_smoothing_sigma must be finite and nonnegative. "
|
|
458
|
+
f"Got {options.pixel_smoothing_sigma}"
|
|
459
459
|
)
|
|
460
460
|
raise ValueError(msg)
|
|
461
461
|
if options.window_size_pixels < 1:
|
|
462
|
-
msg = f"window_size_pixels must be positive
|
|
462
|
+
msg = f"window_size_pixels must be positive. Got {options.window_size_pixels}"
|
|
463
463
|
raise ValueError(msg)
|
|
464
464
|
if options.window_stride_pixels < 1:
|
|
465
465
|
msg = (
|
|
466
|
-
f"window_stride_pixels must be positive
|
|
466
|
+
f"window_stride_pixels must be positive. Got {options.window_stride_pixels}"
|
|
467
467
|
)
|
|
468
468
|
raise ValueError(msg)
|
|
469
469
|
if options.window_stride_pixels > options.window_size_pixels:
|
|
470
470
|
msg = (
|
|
471
|
-
"window_stride_pixels cannot exceed window_size_pixels
|
|
472
|
-
f"
|
|
471
|
+
"window_stride_pixels cannot exceed window_size_pixels. "
|
|
472
|
+
f"Got {options.window_stride_pixels} > {options.window_size_pixels}"
|
|
473
473
|
)
|
|
474
474
|
raise ValueError(msg)
|
|
475
475
|
if options.window_size_pixels > min(shape):
|
|
@@ -482,14 +482,14 @@ def _validate_options(options: ResponseMapOptions, shape: tuple[int, int]) -> No
|
|
|
482
482
|
options.baseline_sample_seconds <= 0
|
|
483
483
|
):
|
|
484
484
|
msg = (
|
|
485
|
-
"baseline_sample_seconds must be finite and positive
|
|
486
|
-
f"
|
|
485
|
+
"baseline_sample_seconds must be finite and positive. "
|
|
486
|
+
f"Got {options.baseline_sample_seconds}"
|
|
487
487
|
)
|
|
488
488
|
raise ValueError(msg)
|
|
489
489
|
if not 0.0 <= options.foreground_percentile <= 100.0:
|
|
490
490
|
msg = (
|
|
491
|
-
"foreground_percentile must be between 0 and 100
|
|
492
|
-
f"
|
|
491
|
+
"foreground_percentile must be between 0 and 100. "
|
|
492
|
+
f"Got {options.foreground_percentile}"
|
|
493
493
|
)
|
|
494
494
|
raise ValueError(msg)
|
|
495
495
|
|
|
@@ -6,7 +6,7 @@ Outputs: processed response objects plus optional quality-control scores.
|
|
|
6
6
|
|
|
7
7
|
This package is GUI-independent. It owns the math and validates option values
|
|
8
8
|
for response smoothing, low-pass filtering, and correlation-based response
|
|
9
|
-
quality control. Workflow code decides when to call it
|
|
9
|
+
quality control. Workflow code decides when to call it. Persistence code decides
|
|
10
10
|
how to save the resulting audit trail.
|
|
11
11
|
"""
|
|
12
12
|
|
|
@@ -367,8 +367,8 @@ def _normalization_factors(
|
|
|
367
367
|
if len(bad_indices) > 0:
|
|
368
368
|
bad_labels = ", ".join(grouped.roi_labels[index] for index in bad_indices)
|
|
369
369
|
msg = (
|
|
370
|
-
"Normalization epoch must have a nonzero response for every ROI
|
|
371
|
-
f"
|
|
370
|
+
"Normalization epoch must have a nonzero response for every ROI. "
|
|
371
|
+
f"Invalid ROI labels: {bad_labels}"
|
|
372
372
|
)
|
|
373
373
|
raise ValueError(msg)
|
|
374
374
|
return RoiNormalizationFactors(
|
|
@@ -387,8 +387,8 @@ def _mask_excluded_rois(
|
|
|
387
387
|
"""Return grouped responses with excluded ROI columns set to NaN."""
|
|
388
388
|
if included_mask.shape != (len(grouped.roi_labels),):
|
|
389
389
|
msg = (
|
|
390
|
-
"included_mask must match ROI label count
|
|
391
|
-
f"
|
|
390
|
+
"included_mask must match ROI label count. "
|
|
391
|
+
f"Got {included_mask.shape} for {len(grouped.roi_labels)} labels"
|
|
392
392
|
)
|
|
393
393
|
raise ValueError(msg)
|
|
394
394
|
trials: list[RoiResponseTrial] = []
|
|
@@ -427,7 +427,7 @@ def _copy_trial_with_values(
|
|
|
427
427
|
def _validate_dff_for_processing(dff: RoiDeltaFOverF) -> None:
|
|
428
428
|
"""Validate a dF/F object before value-only processing."""
|
|
429
429
|
if dff.values.ndim != 2:
|
|
430
|
-
msg = f"dF/F values must have shape (frames, rois)
|
|
430
|
+
msg = f"dF/F values must have shape (frames, rois). Got {dff.values.shape}"
|
|
431
431
|
raise ValueError(msg)
|
|
432
432
|
if dff.values.shape[1] != len(dff.labels):
|
|
433
433
|
msg = (
|
|
@@ -156,25 +156,25 @@ def _validate_smoothing_options(options: SmoothingOptions) -> None:
|
|
|
156
156
|
msg = f"Unknown smoothing method {options.method!r}"
|
|
157
157
|
raise ValueError(msg)
|
|
158
158
|
if options.window_frames < 1:
|
|
159
|
-
msg = f"smoothing window_frames must be at least 1
|
|
159
|
+
msg = f"smoothing window_frames must be at least 1. Got {options.window_frames}"
|
|
160
160
|
raise ValueError(msg)
|
|
161
161
|
if options.polynomial_order < 0:
|
|
162
162
|
msg = (
|
|
163
|
-
"smoothing polynomial_order must be non-negative
|
|
164
|
-
f"
|
|
163
|
+
"smoothing polynomial_order must be non-negative. "
|
|
164
|
+
f"Got {options.polynomial_order}"
|
|
165
165
|
)
|
|
166
166
|
raise ValueError(msg)
|
|
167
167
|
if options.method == "savgol":
|
|
168
168
|
if options.window_frames % 2 == 0:
|
|
169
169
|
msg = (
|
|
170
|
-
"Savitzky-Golay smoothing window_frames must be odd
|
|
171
|
-
f"
|
|
170
|
+
"Savitzky-Golay smoothing window_frames must be odd. "
|
|
171
|
+
f"Got {options.window_frames}"
|
|
172
172
|
)
|
|
173
173
|
raise ValueError(msg)
|
|
174
174
|
if options.polynomial_order >= options.window_frames:
|
|
175
175
|
msg = (
|
|
176
|
-
"Savitzky-Golay polynomial_order must be below window_frames
|
|
177
|
-
f"
|
|
176
|
+
"Savitzky-Golay polynomial_order must be below window_frames. "
|
|
177
|
+
f"Got order {options.polynomial_order} and window "
|
|
178
178
|
f"{options.window_frames}"
|
|
179
179
|
)
|
|
180
180
|
raise ValueError(msg)
|
|
@@ -190,7 +190,7 @@ def _validate_low_pass_options(
|
|
|
190
190
|
msg = f"Unknown low-pass method {options.method!r}"
|
|
191
191
|
raise ValueError(msg)
|
|
192
192
|
if options.order < 1:
|
|
193
|
-
msg = f"low-pass order must be at least 1
|
|
193
|
+
msg = f"low-pass order must be at least 1. Got {options.order}"
|
|
194
194
|
raise ValueError(msg)
|
|
195
195
|
if options.method == "none":
|
|
196
196
|
return
|
|
@@ -199,13 +199,13 @@ def _validate_low_pass_options(
|
|
|
199
199
|
raise ValueError(msg)
|
|
200
200
|
if data_rate_hz is not None:
|
|
201
201
|
if data_rate_hz <= 0:
|
|
202
|
-
msg = f"data_rate_hz must be positive
|
|
202
|
+
msg = f"data_rate_hz must be positive. Got {data_rate_hz}"
|
|
203
203
|
raise ValueError(msg)
|
|
204
204
|
nyquist_hz = data_rate_hz / 2.0
|
|
205
205
|
if options.cutoff_hz >= nyquist_hz:
|
|
206
206
|
msg = (
|
|
207
|
-
"low-pass cutoff_hz must be below Nyquist
|
|
208
|
-
f"
|
|
207
|
+
"low-pass cutoff_hz must be below Nyquist. "
|
|
208
|
+
f"Got {options.cutoff_hz} Hz for {data_rate_hz} Hz data"
|
|
209
209
|
)
|
|
210
210
|
raise ValueError(msg)
|
|
211
211
|
|
|
@@ -216,7 +216,7 @@ def _validate_normalization_options(options: NormalizationOptions) -> None:
|
|
|
216
216
|
msg = f"Unknown normalization method {options.method!r}"
|
|
217
217
|
raise ValueError(msg)
|
|
218
218
|
if options.epoch_number is not None and options.epoch_number < 1:
|
|
219
|
-
msg = f"normalization epoch_number must be positive
|
|
219
|
+
msg = f"normalization epoch_number must be positive. Got {options.epoch_number}"
|
|
220
220
|
raise ValueError(msg)
|
|
221
221
|
if options.method == "epoch_abs_peak" and options.epoch_number is None:
|
|
222
222
|
msg = "response-size normalization requires an epoch_number"
|
|
@@ -230,16 +230,16 @@ def _validate_correlation_options(options: CorrelationFilterOptions) -> None:
|
|
|
230
230
|
raise ValueError(msg)
|
|
231
231
|
if not -1.0 <= options.minimum_correlation <= 1.0:
|
|
232
232
|
msg = (
|
|
233
|
-
"minimum_correlation must be between -1 and 1
|
|
234
|
-
f"
|
|
233
|
+
"minimum_correlation must be between -1 and 1. "
|
|
234
|
+
f"Got {options.minimum_correlation}"
|
|
235
235
|
)
|
|
236
236
|
raise ValueError(msg)
|
|
237
237
|
start_seconds, stop_seconds = options.window_seconds
|
|
238
238
|
if start_seconds is not None and not np.isfinite(start_seconds):
|
|
239
|
-
msg = f"correlation window start must be finite or None
|
|
239
|
+
msg = f"correlation window start must be finite or None. Got {start_seconds}"
|
|
240
240
|
raise ValueError(msg)
|
|
241
241
|
if stop_seconds is not None and not np.isfinite(stop_seconds):
|
|
242
|
-
msg = f"correlation window stop must be finite or None
|
|
242
|
+
msg = f"correlation window stop must be finite or None. Got {stop_seconds}"
|
|
243
243
|
raise ValueError(msg)
|
|
244
244
|
if (
|
|
245
245
|
start_seconds is not None
|
|
@@ -247,7 +247,7 @@ def _validate_correlation_options(options: CorrelationFilterOptions) -> None:
|
|
|
247
247
|
and start_seconds >= stop_seconds
|
|
248
248
|
):
|
|
249
249
|
msg = (
|
|
250
|
-
"correlation window start must be before stop
|
|
251
|
-
f"
|
|
250
|
+
"correlation window start must be before stop. "
|
|
251
|
+
f"Got ({start_seconds}, {stop_seconds})"
|
|
252
252
|
)
|
|
253
253
|
raise ValueError(msg)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Inputs: grouped trial responses with explicit relative time vectors.
|
|
4
4
|
Outputs: ROI-level correlation scores and inclusion masks.
|
|
5
5
|
|
|
6
|
-
The functions here score response consistency
|
|
6
|
+
The functions here score response consistency. They do not delete raw data.
|
|
7
7
|
Callers can use the inclusion mask for plotting or summaries while preserving
|
|
8
8
|
the scores as an audit trail.
|
|
9
9
|
"""
|
|
@@ -179,7 +179,7 @@ def _validate_correlation_inputs(
|
|
|
179
179
|
) -> None:
|
|
180
180
|
"""Validate grouped-response correlation inputs."""
|
|
181
181
|
if not -1.0 <= minimum_correlation <= 1.0:
|
|
182
|
-
msg = f"minimum_correlation must be between -1 and 1
|
|
182
|
+
msg = f"minimum_correlation must be between -1 and 1. Got {minimum_correlation}"
|
|
183
183
|
raise ValueError(msg)
|
|
184
184
|
if reference not in {"epoch_mean", "epoch_peak"}:
|
|
185
185
|
msg = f"Unknown correlation reference {reference!r}"
|
|
@@ -44,7 +44,7 @@ def nan_aware_moving_average(
|
|
|
44
44
|
whose whole window is NaN remain NaN.
|
|
45
45
|
"""
|
|
46
46
|
if window_frames < 1:
|
|
47
|
-
msg = f"window_frames must be at least 1
|
|
47
|
+
msg = f"window_frames must be at least 1. Got {window_frames}"
|
|
48
48
|
raise ValueError(msg)
|
|
49
49
|
matrix, was_1d = _as_frame_channel_matrix(values)
|
|
50
50
|
if window_frames == 1:
|
|
@@ -155,7 +155,7 @@ def _as_frame_channel_matrix(
|
|
|
155
155
|
return array.reshape(-1, 1), True
|
|
156
156
|
if array.ndim == 2:
|
|
157
157
|
return array, False
|
|
158
|
-
msg = f"response values must be 1D or 2D
|
|
158
|
+
msg = f"response values must be 1D or 2D. Got shape {array.shape}"
|
|
159
159
|
raise ValueError(msg)
|
|
160
160
|
|
|
161
161
|
|
|
@@ -208,20 +208,20 @@ def _validate_low_pass_parameters(
|
|
|
208
208
|
) -> None:
|
|
209
209
|
"""Validate Butterworth filter parameters."""
|
|
210
210
|
if sample_rate_hz <= 0:
|
|
211
|
-
msg = f"sample_rate_hz must be positive
|
|
211
|
+
msg = f"sample_rate_hz must be positive. Got {sample_rate_hz}"
|
|
212
212
|
raise ValueError(msg)
|
|
213
213
|
if cutoff_hz <= 0:
|
|
214
|
-
msg = f"cutoff_hz must be positive
|
|
214
|
+
msg = f"cutoff_hz must be positive. Got {cutoff_hz}"
|
|
215
215
|
raise ValueError(msg)
|
|
216
216
|
nyquist_hz = sample_rate_hz / 2.0
|
|
217
217
|
if cutoff_hz >= nyquist_hz:
|
|
218
218
|
msg = (
|
|
219
|
-
"cutoff_hz must be below Nyquist
|
|
220
|
-
f"
|
|
219
|
+
"cutoff_hz must be below Nyquist. "
|
|
220
|
+
f"Got {cutoff_hz} Hz for {sample_rate_hz} Hz data"
|
|
221
221
|
)
|
|
222
222
|
raise ValueError(msg)
|
|
223
223
|
if order < 1:
|
|
224
|
-
msg = f"order must be at least 1
|
|
224
|
+
msg = f"order must be at least 1. Got {order}"
|
|
225
225
|
raise ValueError(msg)
|
|
226
226
|
|
|
227
227
|
|
|
@@ -232,18 +232,18 @@ def _validate_savgol_parameters(
|
|
|
232
232
|
) -> None:
|
|
233
233
|
"""Validate Savitzky-Golay smoothing parameters."""
|
|
234
234
|
if window_frames < 1:
|
|
235
|
-
msg = f"window_frames must be at least 1
|
|
235
|
+
msg = f"window_frames must be at least 1. Got {window_frames}"
|
|
236
236
|
raise ValueError(msg)
|
|
237
237
|
if window_frames % 2 == 0:
|
|
238
|
-
msg = f"window_frames must be odd for Savitzky-Golay
|
|
238
|
+
msg = f"window_frames must be odd for Savitzky-Golay. Got {window_frames}"
|
|
239
239
|
raise ValueError(msg)
|
|
240
240
|
if polynomial_order < 0:
|
|
241
|
-
msg = f"polynomial_order must be non-negative
|
|
241
|
+
msg = f"polynomial_order must be non-negative. Got {polynomial_order}"
|
|
242
242
|
raise ValueError(msg)
|
|
243
243
|
if polynomial_order >= window_frames:
|
|
244
244
|
msg = (
|
|
245
|
-
"polynomial_order must be below window_frames
|
|
246
|
-
f"
|
|
245
|
+
"polynomial_order must be below window_frames. "
|
|
246
|
+
f"Got {polynomial_order} and {window_frames}"
|
|
247
247
|
)
|
|
248
248
|
raise ValueError(msg)
|
|
249
249
|
|
|
@@ -65,7 +65,7 @@ def resolve_response_window_seconds(
|
|
|
65
65
|
ValueError: If any supplied window duration is negative.
|
|
66
66
|
"""
|
|
67
67
|
if max_window_seconds is not None and max_window_seconds < 0.0:
|
|
68
|
-
msg = f"max_window_seconds must be non-negative
|
|
68
|
+
msg = f"max_window_seconds must be non-negative. Got {max_window_seconds}"
|
|
69
69
|
raise ValueError(msg)
|
|
70
70
|
if options.auto:
|
|
71
71
|
pre = _validate_non_negative(
|
|
@@ -94,6 +94,6 @@ def _validate_non_negative(value: float, *, name: str) -> float:
|
|
|
94
94
|
"""Return ``value`` as a float after enforcing the response-window domain."""
|
|
95
95
|
resolved = float(value)
|
|
96
96
|
if resolved < 0.0:
|
|
97
|
-
msg = f"{name} must be non-negative
|
|
97
|
+
msg = f"{name} must be non-negative. Got {resolved}"
|
|
98
98
|
raise ValueError(msg)
|
|
99
99
|
return resolved
|