TwoPy 0.3.1__tar.gz → 0.3.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {twopy-0.3.1 → twopy-0.3.3}/PKG-INFO +18 -3
- {twopy-0.3.1 → twopy-0.3.3}/README.md +16 -2
- {twopy-0.3.1 → twopy-0.3.3}/pyproject.toml +3 -2
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/group_matching.py +23 -10
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/persistence.py +9 -2
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/response_plotting.py +8 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/response_processing/__init__.py +2 -2
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/response_processing/apply.py +17 -15
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/response_processing/options.py +12 -11
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/response_processing/persistence.py +48 -20
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/workflow.py +2 -2
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis_cache.py +121 -43
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/api.py +4 -3
- twopy-0.3.3/src/twopy/cache_inventory.py +708 -0
- twopy-0.3.3/src/twopy/config.example.yml +64 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/config.py +239 -9
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/conversion/api.py +127 -12
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/custom/native_workflows/direction_selectivity.py +2 -2
- twopy-0.3.3/src/twopy/napari/assets/twopy-app-icon.png +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/controls.py +28 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/database_search.py +2 -1
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/display_paths.py +125 -13
- twopy-0.3.3/src/twopy/napari/empty_state.py +159 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/group_matching/images.py +17 -11
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/group_matching/response_preview.py +17 -9
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/group_matching/roi_assignment.py +106 -51
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/group_matching/roi_cards.py +122 -38
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/group_matching/window.py +1 -1
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/interactive.py +42 -2
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/launcher.py +196 -8
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/load_controller.py +111 -38
- twopy-0.3.3/src/twopy/napari/load_progress.py +758 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/load_workflow.py +33 -6
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/loaded_recordings_csv.py +3 -31
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/loading.py +409 -43
- twopy-0.3.3/src/twopy/napari/output_routing.py +121 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/data.py +1 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/docks/api.py +11 -1
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/docks/dynamic_options.py +12 -7
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/docks/options_panel.py +9 -4
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/docks/paths.py +18 -4
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/docks/response_plot_widget.py +409 -140
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/docks/save_actions.py +39 -10
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/export_controls.py +63 -6
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/normalization_options.py +19 -11
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/panels.py +5 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/roi_generation/__init__.py +8 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/roi_generation/actions.py +35 -2
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/roi_generation/controls.py +137 -16
- twopy-0.3.3/src/twopy/napari/plotting/roi_generation/options.py +348 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/roi.py +5 -2
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/session.py +48 -1
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/state.py +65 -0
- twopy-0.3.3/src/twopy/napari/version_check.py +280 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/viewer.py +35 -2
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/roi.py +70 -6
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy.egg-info/PKG-INFO +18 -3
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy.egg-info/SOURCES.txt +8 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy.egg-info/requires.txt +1 -0
- twopy-0.3.3/tests/test_cache_inventory.py +399 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_config.py +147 -5
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_conversion.py +72 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_custom_workflows.py +5 -1
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_group_matching.py +65 -28
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_load_workflow.py +31 -6
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari.py +93 -2
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_export.py +6 -12
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_group_matching_roi_cards.py +94 -12
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_live_controller.py +2 -1
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_load_controller.py +150 -5
- twopy-0.3.3/tests/test_napari_load_progress.py +379 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_load_tab.py +67 -3
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_loaded_recordings.py +14 -10
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_path_resolution.py +431 -15
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_plot_controls.py +7 -3
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_plot_data.py +15 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_plot_widgets.py +136 -16
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_processing_controls.py +6 -2
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_response_workflow.py +345 -9
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_roi_generation.py +399 -1
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_roi_visibility.py +3 -3
- twopy-0.3.3/tests/test_packaging.py +249 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_persistence.py +57 -2
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_response_processing.py +41 -17
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_roi.py +48 -0
- twopy-0.3.3/tests/test_version_check.py +207 -0
- twopy-0.3.1/src/twopy/napari/load_progress.py +0 -168
- twopy-0.3.1/src/twopy/napari/plotting/roi_generation/options.py +0 -62
- twopy-0.3.1/tests/test_napari_load_progress.py +0 -48
- twopy-0.3.1/tests/test_packaging.py +0 -55
- {twopy-0.3.1 → twopy-0.3.3}/setup.cfg +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/__init__.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/_segments.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/_version.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/__init__.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/background_subtraction.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/dff.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/dff_options.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/epoch_mapping.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/kernels/__init__.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/kernels/_fitting.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/kernels/_stimulus.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/motion.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/response_context.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/response_maps.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/response_processing/qc.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/response_processing/signals.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/response_window_options.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/responses.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/timing.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/trials.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/calibrations/pixel_calibration_profiles.csv +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/calibrations/pixel_size.csv +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/conversion/__init__.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/conversion/alignment_crop.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/conversion/hdf5_writing.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/conversion/matlab_values.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/conversion/orientation.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/conversion/source_loading.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/conversion/stimulus_code.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/conversion/types.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/converted.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/custom/__init__.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/custom/context.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/custom/discovery.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/custom/metadata.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/custom/native.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/custom/native_workflows/__init__.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/custom/native_workflows/response_kernels.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/custom/parameters.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/custom/provenance.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/custom/registry.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/custom/results.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/custom/runner.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/custom/workflows.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/database/__init__.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/database/cache.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/database/catalog.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/database/connection.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/database/generic.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/database/modeled.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/database/search.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/database/sql.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/database/types.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/filenames.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/frame_ranges.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/hdf5_utils.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/inspection.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/matlab.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/__init__.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/__main__.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/custom_tab.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/database_favorite_editor.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/database_favorites.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/dims.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/display.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/errors.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/group_matching/__init__.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/group_matching/cards.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/group_matching/fov_assignment.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/group_matching/fov_cards.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/group_matching/protocols.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/group_matching/responses.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/group_matching/style.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/group_matching/tables.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/latest_worker.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/live_analysis.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/movie.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/paths.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/__init__.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/dff_options.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/docks/__init__.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/docks/plot_area.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/docks/response_map_area.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/docks/visibility_state.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/export.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/form_controls.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/label_visibility.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/options.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/preview_strip.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/processing_options.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/response_map_colors.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/response_map_display.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/response_map_options.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/response_window_options.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/widgets.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/protocols.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/responses.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/roi_signatures.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/sidebar.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/text.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/trial_timeline.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/types.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/parity/__init__.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/parity/dff.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/parity/psycho5.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/parity/roi.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/parity/roi_extraction.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/parity/saved_analysis.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/photodiode.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/photodiode_classification.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/pixel_calibration.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/pixel_calibration_profiles.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/response_roi_extraction.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/roi_extraction.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/roi_mask_cleanup.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/session.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/spatial.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/spatial_orientation.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/stimulus.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/synchronization.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy/typing_guards.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy.egg-info/dependency_links.txt +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy.egg-info/entry_points.txt +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/src/twopy.egg-info/top_level.txt +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_api.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_background_subtraction.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_converted.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_database.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_dff.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_epoch_mapping.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_filenames.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_frame_ranges.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_function_inventory.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_hdf5_utils.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_import.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_inspection.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_kernels.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_matlab.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_motion.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_database_search.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_display.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_group_matching_responses.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_response_map_display.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_text.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_timeline.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_parity.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_parity_psycho5.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_parity_roi_extraction.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_photodiode_classification.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_pixel_calibration.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_pixel_calibration_profiles.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_real_fixture.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_recording_timing.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_response_map_area.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_response_maps.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_response_roi_extraction.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_response_window_options.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_responses.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_roi_extraction.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_roi_mask_cleanup.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_saved_analysis.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_session.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_stimulus.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_synchronization.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_trials.py +0 -0
- {twopy-0.3.1 → twopy-0.3.3}/tests/test_workflow.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: twopy
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
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
|
|
@@ -12,6 +12,7 @@ Requires-Dist: h5py<4,>=3.16
|
|
|
12
12
|
Requires-Dist: matplotlib<4,>=3.10
|
|
13
13
|
Requires-Dist: napari[all]<0.8,>=0.7
|
|
14
14
|
Requires-Dist: numpy<3,>=2.3
|
|
15
|
+
Requires-Dist: packaging<27,>=26
|
|
15
16
|
Requires-Dist: pre-commit<5,>=4.6
|
|
16
17
|
Requires-Dist: qtpy<3,>=2.4
|
|
17
18
|
Requires-Dist: pyyaml<7,>=6.0.3
|
|
@@ -30,7 +31,7 @@ Two-photon imaging analysis tool for the Clark Lab output format.
|
|
|
30
31
|
|
|
31
32
|
twopy lets you open two-photon recordings, draw ROIs, plot responses in real time, process and analyze them, and save them.
|
|
32
33
|
|
|
33
|
-
When you first load a recording, twopy converts it to
|
|
34
|
+
When you first load a recording, twopy converts it to HDF5 files that it owns. Those files include the aligned movie, mean image, stimulus tables, photodiode signals, and recording metadata. Analysis and the GUI both work from the converted files, so the original source files remain separate from twopy's outputs. By default, converted recordings and interactive analysis outputs are cached locally under `~/.cache/twopy/recordings` for speed; saving ROIs and analysis copies the changed output files back to the configured `analysis_output` location in the background.
|
|
34
35
|
|
|
35
36
|
## Install
|
|
36
37
|
|
|
@@ -48,7 +49,19 @@ Verify the install:
|
|
|
48
49
|
micromamba run -n twopy python -c 'import twopy; print(twopy.__name__)'
|
|
49
50
|
```
|
|
50
51
|
|
|
51
|
-
|
|
52
|
+
Create the config template in your user folder:
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
twopy config setup
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
twopy prints the config path it wrote. On macOS and Linux the default is `~/.config/twopy/config.yml`; on Windows it is under `%APPDATA%\twopy\config.yml`. Edit that file with your database, data, and output paths before launching twopy. If you launch `twopy` before a config exists, twopy creates the same template, prints the path, and stops so you can edit it.
|
|
59
|
+
|
|
60
|
+
Validate the active config and show its path and file contents:
|
|
61
|
+
|
|
62
|
+
```sh
|
|
63
|
+
twopy config
|
|
64
|
+
```
|
|
52
65
|
|
|
53
66
|
## Start
|
|
54
67
|
|
|
@@ -57,6 +70,8 @@ micromamba activate twopy
|
|
|
57
70
|
twopy
|
|
58
71
|
```
|
|
59
72
|
|
|
73
|
+
`twopy` validates the config before opening napari. It does not require lab data folders or network drives to be mounted at launch.
|
|
74
|
+
|
|
60
75
|
Open a source recording or converted recording directly:
|
|
61
76
|
|
|
62
77
|
```sh
|
|
@@ -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
|
|
11
|
+
When you first load a recording, twopy converts it to HDF5 files that it owns. Those files include the aligned movie, mean image, stimulus tables, photodiode signals, and recording metadata. Analysis and the GUI both work from the converted files, so the original source files remain separate from twopy's outputs. By default, converted recordings and interactive analysis outputs are cached locally under `~/.cache/twopy/recordings` for speed; saving ROIs and analysis copies the changed output files back to the configured `analysis_output` location in the background.
|
|
12
12
|
|
|
13
13
|
## Install
|
|
14
14
|
|
|
@@ -26,7 +26,19 @@ Verify the install:
|
|
|
26
26
|
micromamba run -n twopy python -c 'import twopy; print(twopy.__name__)'
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
Create the config template in your user folder:
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
twopy config setup
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
twopy prints the config path it wrote. On macOS and Linux the default is `~/.config/twopy/config.yml`; on Windows it is under `%APPDATA%\twopy\config.yml`. Edit that file with your database, data, and output paths before launching twopy. If you launch `twopy` before a config exists, twopy creates the same template, prints the path, and stops so you can edit it.
|
|
36
|
+
|
|
37
|
+
Validate the active config and show its path and file contents:
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
twopy config
|
|
41
|
+
```
|
|
30
42
|
|
|
31
43
|
## Start
|
|
32
44
|
|
|
@@ -35,6 +47,8 @@ micromamba activate twopy
|
|
|
35
47
|
twopy
|
|
36
48
|
```
|
|
37
49
|
|
|
50
|
+
`twopy` validates the config before opening napari. It does not require lab data folders or network drives to be mounted at launch.
|
|
51
|
+
|
|
38
52
|
Open a source recording or converted recording directly:
|
|
39
53
|
|
|
40
54
|
```sh
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "twopy"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.3"
|
|
8
8
|
description = "Two-photon imaging analysis tool with a napari interface."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.13,<3.14"
|
|
@@ -17,6 +17,7 @@ dependencies = [
|
|
|
17
17
|
"matplotlib>=3.10,<4",
|
|
18
18
|
"napari[all]>=0.7,<0.8",
|
|
19
19
|
"numpy>=2.3,<3",
|
|
20
|
+
"packaging>=26,<27",
|
|
20
21
|
"pre-commit>=4.6,<5",
|
|
21
22
|
"qtpy>=2.4,<3",
|
|
22
23
|
"pyyaml>=6.0.3,<7",
|
|
@@ -37,7 +38,7 @@ Repository = "https://github.com/gumadeiras/twopy"
|
|
|
37
38
|
where = ["src"]
|
|
38
39
|
|
|
39
40
|
[tool.setuptools.package-data]
|
|
40
|
-
twopy = ["calibrations/*.csv"]
|
|
41
|
+
twopy = ["calibrations/*.csv", "config.example.yml", "napari/assets/*.png"]
|
|
41
42
|
|
|
42
43
|
[tool.ruff]
|
|
43
44
|
line-length = 88
|
|
@@ -143,7 +143,7 @@ def make_manual_fov_group_rows(
|
|
|
143
143
|
|
|
144
144
|
|
|
145
145
|
def make_manual_roi_match_rows(
|
|
146
|
-
recording_rois:
|
|
146
|
+
recording_rois: Sequence[tuple[Path, str]],
|
|
147
147
|
*,
|
|
148
148
|
group_cell_id: int,
|
|
149
149
|
fov_group_id: str = "",
|
|
@@ -153,7 +153,9 @@ def make_manual_roi_match_rows(
|
|
|
153
153
|
"""Create validated match-table rows for one user decision.
|
|
154
154
|
|
|
155
155
|
Args:
|
|
156
|
-
recording_rois:
|
|
156
|
+
recording_rois: Recording path and ROI label pairs selected for one
|
|
157
|
+
group. A recording can appear more than once when multiple ROIs
|
|
158
|
+
from that recording belong in the group.
|
|
157
159
|
group_cell_id: Positive cell-group identifier for this decision.
|
|
158
160
|
fov_group_id: Optional user-facing FOV group identifier.
|
|
159
161
|
status: Decision status for every emitted row.
|
|
@@ -172,16 +174,27 @@ def make_manual_roi_match_rows(
|
|
|
172
174
|
raise ValueError(msg)
|
|
173
175
|
|
|
174
176
|
rows: list[ManualRoiMatchRow] = []
|
|
177
|
+
seen: set[tuple[Path, str]] = set()
|
|
175
178
|
for recording_path, roi_label in sorted(
|
|
176
|
-
recording_rois
|
|
177
|
-
key=lambda item: str(item[0]),
|
|
179
|
+
recording_rois,
|
|
180
|
+
key=lambda item: (str(item[0]), item[1]),
|
|
178
181
|
):
|
|
182
|
+
validated_path = _validate_recording_path(recording_path)
|
|
183
|
+
validated_roi = _validate_roi_label(roi_label)
|
|
184
|
+
key = (validated_path, validated_roi)
|
|
185
|
+
if key in seen:
|
|
186
|
+
msg = (
|
|
187
|
+
"Manual ROI match rows cannot repeat the same recording and "
|
|
188
|
+
f"ROI label: {validated_path} {validated_roi}."
|
|
189
|
+
)
|
|
190
|
+
raise ValueError(msg)
|
|
191
|
+
seen.add(key)
|
|
179
192
|
rows.append(
|
|
180
193
|
ManualRoiMatchRow(
|
|
181
194
|
fov_group_id=fov_group_id,
|
|
182
195
|
group_cell_id=group_cell_id,
|
|
183
|
-
recording_path=
|
|
184
|
-
roi_label=
|
|
196
|
+
recording_path=validated_path,
|
|
197
|
+
roi_label=validated_roi,
|
|
185
198
|
status=status,
|
|
186
199
|
note=note,
|
|
187
200
|
),
|
|
@@ -225,7 +238,7 @@ def matched_manual_roi_groups(
|
|
|
225
238
|
|
|
226
239
|
def add_manual_roi_match_group(
|
|
227
240
|
rows: Sequence[ManualRoiMatchRow],
|
|
228
|
-
recording_rois:
|
|
241
|
+
recording_rois: Sequence[tuple[Path, str]],
|
|
229
242
|
*,
|
|
230
243
|
fov_group_id: str,
|
|
231
244
|
note: str = "",
|
|
@@ -234,7 +247,7 @@ def add_manual_roi_match_group(
|
|
|
234
247
|
|
|
235
248
|
Args:
|
|
236
249
|
rows: Existing manual ROI match rows.
|
|
237
|
-
recording_rois:
|
|
250
|
+
recording_rois: Selected recording path and ROI label pairs.
|
|
238
251
|
fov_group_id: FOV id for the new matched group.
|
|
239
252
|
note: Optional note copied to every new row.
|
|
240
253
|
|
|
@@ -255,7 +268,7 @@ def add_manual_roi_match_group(
|
|
|
255
268
|
|
|
256
269
|
def replace_manual_roi_match_group(
|
|
257
270
|
rows: Sequence[ManualRoiMatchRow],
|
|
258
|
-
recording_rois:
|
|
271
|
+
recording_rois: Sequence[tuple[Path, str]],
|
|
259
272
|
*,
|
|
260
273
|
fov_group_id: str,
|
|
261
274
|
group_cell_id: int,
|
|
@@ -265,7 +278,7 @@ def replace_manual_roi_match_group(
|
|
|
265
278
|
|
|
266
279
|
Args:
|
|
267
280
|
rows: Existing manual ROI match rows.
|
|
268
|
-
recording_rois: Replacement recording
|
|
281
|
+
recording_rois: Replacement recording path and ROI label pairs.
|
|
269
282
|
fov_group_id: FOV id containing the group.
|
|
270
283
|
group_cell_id: Existing group id to replace.
|
|
271
284
|
note: Optional note copied to every replacement row.
|
|
@@ -101,7 +101,8 @@ class LoadedAnalysisOutputs:
|
|
|
101
101
|
"""Analysis objects loaded from one twopy analysis HDF5 file.
|
|
102
102
|
|
|
103
103
|
Inputs: one file written by ``save_analysis_outputs``.
|
|
104
|
-
Outputs: optional ROI, trace, dF/F, window, and
|
|
104
|
+
Outputs: optional ROI, trace, dF/F, window, response objects, and a warning
|
|
105
|
+
when an old file uses a setting twopy no longer uses.
|
|
105
106
|
|
|
106
107
|
Groups are optional because scripts can save only the products they have
|
|
107
108
|
computed. Missing groups load as ``None`` or an empty window tuple.
|
|
@@ -117,6 +118,7 @@ class LoadedAnalysisOutputs:
|
|
|
117
118
|
response_processing_options: ResponseProcessingOptions | None
|
|
118
119
|
normalization_factors: RoiNormalizationFactors | None
|
|
119
120
|
correlation_scores: RoiCorrelationScores | None
|
|
121
|
+
load_warning: str | None = None
|
|
120
122
|
|
|
121
123
|
|
|
122
124
|
def save_analysis_outputs(
|
|
@@ -146,7 +148,7 @@ def save_analysis_outputs(
|
|
|
146
148
|
grouped_responses: Optional grouped trial responses.
|
|
147
149
|
response_processing_options: Optional smoothing, low-pass, and
|
|
148
150
|
correlation-QC settings used for these outputs.
|
|
149
|
-
normalization_factors: Optional per-ROI factors used for
|
|
151
|
+
normalization_factors: Optional per-ROI factors used for response-size
|
|
150
152
|
normalization.
|
|
151
153
|
correlation_scores: Optional ROI-level correlation QC scores.
|
|
152
154
|
response_summary_trials_csv: Optional CSV path for one row per trial
|
|
@@ -344,6 +346,11 @@ def load_analysis_outputs(path: Path) -> LoadedAnalysisOutputs:
|
|
|
344
346
|
if response_processing is not None
|
|
345
347
|
else None
|
|
346
348
|
),
|
|
349
|
+
load_warning=(
|
|
350
|
+
response_processing.load_warning
|
|
351
|
+
if response_processing is not None
|
|
352
|
+
else None
|
|
353
|
+
),
|
|
347
354
|
)
|
|
348
355
|
|
|
349
356
|
|
|
@@ -80,6 +80,8 @@ class ResponsePlotData:
|
|
|
80
80
|
correlation_window_stop_default_seconds: Optional default QC stop time.
|
|
81
81
|
visible_roi_indices: Optional ROI rows to show initially while keeping all
|
|
82
82
|
ROI data available.
|
|
83
|
+
load_warning: Optional warning when an old file uses a setting twopy no
|
|
84
|
+
longer uses.
|
|
83
85
|
|
|
84
86
|
Returns:
|
|
85
87
|
Immutable response plot view model with no GUI object references.
|
|
@@ -93,6 +95,7 @@ class ResponsePlotData:
|
|
|
93
95
|
correlation_scores: RoiCorrelationScores | None = None
|
|
94
96
|
correlation_window_stop_default_seconds: float | None = None
|
|
95
97
|
visible_roi_indices: tuple[int, ...] | None = None
|
|
98
|
+
load_warning: str | None = None
|
|
96
99
|
|
|
97
100
|
|
|
98
101
|
def response_plot_data_from_grouped(
|
|
@@ -105,6 +108,7 @@ def response_plot_data_from_grouped(
|
|
|
105
108
|
response_processing_options: ResponseProcessingOptions | None = None,
|
|
106
109
|
correlation_scores: RoiCorrelationScores | None = None,
|
|
107
110
|
correlation_window_stop_default_seconds: float | None = None,
|
|
111
|
+
load_warning: str | None = None,
|
|
108
112
|
) -> ResponsePlotData:
|
|
109
113
|
"""Summarize grouped responses into mean and SEM traces for plotting.
|
|
110
114
|
|
|
@@ -122,6 +126,8 @@ def response_plot_data_from_grouped(
|
|
|
122
126
|
correlation_scores: Optional ROI-level correlation QC scores.
|
|
123
127
|
correlation_window_stop_default_seconds: Optional default stop time for
|
|
124
128
|
enabling Plot-tab correlation QC.
|
|
129
|
+
load_warning: Optional warning when an old file uses a setting twopy no
|
|
130
|
+
longer uses.
|
|
125
131
|
|
|
126
132
|
Returns:
|
|
127
133
|
Plot-ready data with one item per stimulus epoch type.
|
|
@@ -162,6 +168,7 @@ def response_plot_data_from_grouped(
|
|
|
162
168
|
response_processing_options=response_processing_options,
|
|
163
169
|
correlation_scores=correlation_scores,
|
|
164
170
|
correlation_window_stop_default_seconds=correlation_window_stop_default_seconds,
|
|
171
|
+
load_warning=load_warning,
|
|
165
172
|
)
|
|
166
173
|
|
|
167
174
|
|
|
@@ -215,6 +222,7 @@ def filter_response_plot_data_rois(
|
|
|
215
222
|
plot_data.visible_roi_indices,
|
|
216
223
|
keep_indices,
|
|
217
224
|
),
|
|
225
|
+
load_warning=plot_data.load_warning,
|
|
218
226
|
)
|
|
219
227
|
|
|
220
228
|
|
|
@@ -15,7 +15,7 @@ from twopy.analysis.response_processing.apply import (
|
|
|
15
15
|
RoiNormalizationFactors,
|
|
16
16
|
apply_correlation_filter_to_grouped_roi_responses,
|
|
17
17
|
mask_grouped_roi_responses_by_included_rois,
|
|
18
|
-
|
|
18
|
+
normalize_grouped_roi_responses_by_epoch_abs_peak,
|
|
19
19
|
process_grouped_roi_responses,
|
|
20
20
|
process_roi_delta_f_over_f,
|
|
21
21
|
)
|
|
@@ -55,7 +55,7 @@ __all__ = [
|
|
|
55
55
|
"mask_grouped_roi_responses_by_included_rois",
|
|
56
56
|
"nan_aware_moving_average",
|
|
57
57
|
"nan_aware_savgol_filter",
|
|
58
|
-
"
|
|
58
|
+
"normalize_grouped_roi_responses_by_epoch_abs_peak",
|
|
59
59
|
"process_grouped_roi_responses",
|
|
60
60
|
"process_roi_delta_f_over_f",
|
|
61
61
|
"ResponseProcessingOptions",
|
|
@@ -37,7 +37,7 @@ from twopy.analysis.responses import (
|
|
|
37
37
|
|
|
38
38
|
__all__ = [
|
|
39
39
|
"apply_correlation_filter_to_grouped_roi_responses",
|
|
40
|
-
"
|
|
40
|
+
"normalize_grouped_roi_responses_by_epoch_abs_peak",
|
|
41
41
|
"mask_grouped_roi_responses_by_included_rois",
|
|
42
42
|
"RoiNormalizationFactors",
|
|
43
43
|
"ResponseProcessingResult",
|
|
@@ -50,10 +50,10 @@ _NORMALIZATION_MIN_PEAK = 1e-12
|
|
|
50
50
|
|
|
51
51
|
@dataclass(frozen=True)
|
|
52
52
|
class RoiNormalizationFactors:
|
|
53
|
-
"""Per-ROI factors used for
|
|
53
|
+
"""Per-ROI scale factors used for response-size normalization.
|
|
54
54
|
|
|
55
55
|
Inputs: grouped response trials and one enabled normalization option.
|
|
56
|
-
Outputs: the positive
|
|
56
|
+
Outputs: the positive response size used to scale each ROI.
|
|
57
57
|
"""
|
|
58
58
|
|
|
59
59
|
roi_labels: tuple[str, ...]
|
|
@@ -150,9 +150,11 @@ def process_grouped_roi_responses(
|
|
|
150
150
|
validate_response_processing_options(options, data_rate_hz=grouped.data_rate_hz)
|
|
151
151
|
validate_grouped_roi_responses(grouped)
|
|
152
152
|
processed = _copy_grouped_with_processed_values(grouped, options=options)
|
|
153
|
-
processed, normalization_factors =
|
|
154
|
-
|
|
155
|
-
|
|
153
|
+
processed, normalization_factors = (
|
|
154
|
+
normalize_grouped_roi_responses_by_epoch_abs_peak(
|
|
155
|
+
processed,
|
|
156
|
+
options=options.normalization,
|
|
157
|
+
)
|
|
156
158
|
)
|
|
157
159
|
processed, correlation_scores = apply_correlation_filter_to_grouped_roi_responses(
|
|
158
160
|
processed,
|
|
@@ -166,12 +168,12 @@ def process_grouped_roi_responses(
|
|
|
166
168
|
)
|
|
167
169
|
|
|
168
170
|
|
|
169
|
-
def
|
|
171
|
+
def normalize_grouped_roi_responses_by_epoch_abs_peak(
|
|
170
172
|
grouped: GroupedRoiResponses,
|
|
171
173
|
*,
|
|
172
174
|
options: NormalizationOptions,
|
|
173
175
|
) -> tuple[GroupedRoiResponses, RoiNormalizationFactors | None]:
|
|
174
|
-
"""
|
|
176
|
+
"""Scale each ROI by its strongest average response in the selected epoch.
|
|
175
177
|
|
|
176
178
|
Args:
|
|
177
179
|
grouped: Grouped ROI responses to normalize.
|
|
@@ -182,17 +184,17 @@ def normalize_grouped_roi_responses_by_epoch_peak(
|
|
|
182
184
|
normalization is disabled.
|
|
183
185
|
|
|
184
186
|
Raises:
|
|
185
|
-
ValueError: If the selected epoch is absent or any ROI lacks a
|
|
186
|
-
|
|
187
|
+
ValueError: If the selected epoch is absent or any ROI lacks a finite
|
|
188
|
+
nonzero response in that epoch.
|
|
187
189
|
"""
|
|
188
190
|
validate_grouped_roi_responses(grouped)
|
|
189
|
-
if options.method not in {"none", "
|
|
191
|
+
if options.method not in {"none", "epoch_abs_peak"}:
|
|
190
192
|
msg = f"Unknown normalization method {options.method!r}"
|
|
191
193
|
raise ValueError(msg)
|
|
192
194
|
if options.method == "none":
|
|
193
195
|
return grouped, None
|
|
194
196
|
if options.epoch_number is None:
|
|
195
|
-
msg = "
|
|
197
|
+
msg = "response-size normalization requires an epoch_number"
|
|
196
198
|
raise ValueError(msg)
|
|
197
199
|
factors = _normalization_factors(grouped, options=options)
|
|
198
200
|
trials = tuple(
|
|
@@ -336,7 +338,7 @@ def _normalization_factors(
|
|
|
336
338
|
*,
|
|
337
339
|
options: NormalizationOptions,
|
|
338
340
|
) -> RoiNormalizationFactors:
|
|
339
|
-
"""Return
|
|
341
|
+
"""Return each ROI's selected-epoch response size."""
|
|
340
342
|
selected_trials = tuple(
|
|
341
343
|
trial
|
|
342
344
|
for trial in grouped.trials
|
|
@@ -356,7 +358,7 @@ def _normalization_factors(
|
|
|
356
358
|
roi_labels=grouped.roi_labels,
|
|
357
359
|
data_rate_hz=grouped.data_rate_hz,
|
|
358
360
|
)
|
|
359
|
-
factors = np.nanmax(summary.mean_values, axis=1)
|
|
361
|
+
factors = np.nanmax(np.abs(summary.mean_values), axis=1)
|
|
360
362
|
bad_indices = tuple(
|
|
361
363
|
index
|
|
362
364
|
for index, factor in enumerate(factors)
|
|
@@ -365,7 +367,7 @@ def _normalization_factors(
|
|
|
365
367
|
if len(bad_indices) > 0:
|
|
366
368
|
bad_labels = ", ".join(grouped.roi_labels[index] for index in bad_indices)
|
|
367
369
|
msg = (
|
|
368
|
-
"Normalization epoch
|
|
370
|
+
"Normalization epoch must have a nonzero response for every ROI; "
|
|
369
371
|
f"invalid ROI labels: {bad_labels}"
|
|
370
372
|
)
|
|
371
373
|
raise ValueError(msg)
|
|
@@ -28,7 +28,7 @@ __all__ = [
|
|
|
28
28
|
|
|
29
29
|
SmoothingMethod = Literal["none", "moving_average", "savgol"]
|
|
30
30
|
LowPassFilterMethod = Literal["none", "butterworth"]
|
|
31
|
-
NormalizationMethod = Literal["none", "
|
|
31
|
+
NormalizationMethod = Literal["none", "epoch_abs_peak"]
|
|
32
32
|
CorrelationFilterReference = Literal["none", "epoch_mean", "epoch_peak"]
|
|
33
33
|
CorrelationWindowSeconds = tuple[float | None, float | None]
|
|
34
34
|
|
|
@@ -70,15 +70,16 @@ class LowPassFilterOptions:
|
|
|
70
70
|
|
|
71
71
|
@dataclass(frozen=True)
|
|
72
72
|
class NormalizationOptions:
|
|
73
|
-
"""
|
|
73
|
+
"""Settings for scaling grouped ROI responses by one selected epoch.
|
|
74
74
|
|
|
75
75
|
Inputs: a normalization method and the stimulus epoch used as the scale
|
|
76
76
|
reference.
|
|
77
77
|
Outputs: normalization values applied after trial grouping.
|
|
78
78
|
|
|
79
|
-
``method="none"`` leaves grouped responses unchanged.
|
|
80
|
-
divides every response for each ROI by that
|
|
81
|
-
|
|
79
|
+
``method="none"`` leaves grouped responses unchanged.
|
|
80
|
+
``method="epoch_abs_peak"`` divides every response for each ROI by that
|
|
81
|
+
ROI's strongest average response in the selected epoch, whether positive or
|
|
82
|
+
negative.
|
|
82
83
|
"""
|
|
83
84
|
|
|
84
85
|
method: NormalizationMethod = "none"
|
|
@@ -111,8 +112,8 @@ class CorrelationFilterOptions:
|
|
|
111
112
|
class ResponseProcessingOptions:
|
|
112
113
|
"""Complete response post-processing configuration.
|
|
113
114
|
|
|
114
|
-
Inputs: optional smoothing, low-pass filtering,
|
|
115
|
-
and correlation QC settings.
|
|
115
|
+
Inputs: optional smoothing, low-pass filtering, response-size
|
|
116
|
+
normalization, and correlation QC settings.
|
|
116
117
|
Outputs: one immutable object used by workflow, plotting, and saved analysis
|
|
117
118
|
metadata.
|
|
118
119
|
"""
|
|
@@ -210,15 +211,15 @@ def _validate_low_pass_options(
|
|
|
210
211
|
|
|
211
212
|
|
|
212
213
|
def _validate_normalization_options(options: NormalizationOptions) -> None:
|
|
213
|
-
"""Validate
|
|
214
|
-
if options.method not in {"none", "
|
|
214
|
+
"""Validate response-size normalization options."""
|
|
215
|
+
if options.method not in {"none", "epoch_abs_peak"}:
|
|
215
216
|
msg = f"Unknown normalization method {options.method!r}"
|
|
216
217
|
raise ValueError(msg)
|
|
217
218
|
if options.epoch_number is not None and options.epoch_number < 1:
|
|
218
219
|
msg = f"normalization epoch_number must be positive; got {options.epoch_number}"
|
|
219
220
|
raise ValueError(msg)
|
|
220
|
-
if options.method == "
|
|
221
|
-
msg = "
|
|
221
|
+
if options.method == "epoch_abs_peak" and options.epoch_number is None:
|
|
222
|
+
msg = "response-size normalization requires an epoch_number"
|
|
222
223
|
raise ValueError(msg)
|
|
223
224
|
|
|
224
225
|
|
|
@@ -51,7 +51,7 @@ _SMOOTHING_METHODS: tuple[SmoothingMethod, ...] = (
|
|
|
51
51
|
"savgol",
|
|
52
52
|
)
|
|
53
53
|
_LOW_PASS_METHODS: tuple[LowPassFilterMethod, ...] = ("none", "butterworth")
|
|
54
|
-
_NORMALIZATION_METHODS: tuple[NormalizationMethod, ...] = ("none", "
|
|
54
|
+
_NORMALIZATION_METHODS: tuple[NormalizationMethod, ...] = ("none", "epoch_abs_peak")
|
|
55
55
|
_CORRELATION_REFERENCES: tuple[CorrelationFilterReference, ...] = (
|
|
56
56
|
"none",
|
|
57
57
|
"epoch_mean",
|
|
@@ -64,13 +64,14 @@ class PersistedResponseProcessing:
|
|
|
64
64
|
"""Response-processing audit data loaded from HDF5.
|
|
65
65
|
|
|
66
66
|
Inputs: one ``response_processing`` HDF5 group.
|
|
67
|
-
Outputs: typed options plus optional
|
|
68
|
-
|
|
67
|
+
Outputs: typed options plus optional factors, ROI correlation scores, and a
|
|
68
|
+
warning when an old file uses a setting twopy no longer uses.
|
|
69
69
|
"""
|
|
70
70
|
|
|
71
71
|
options: ResponseProcessingOptions
|
|
72
72
|
normalization_factors: RoiNormalizationFactors | None
|
|
73
73
|
correlation_scores: RoiCorrelationScores | None
|
|
74
|
+
load_warning: str | None = None
|
|
74
75
|
|
|
75
76
|
|
|
76
77
|
def write_response_processing_group(
|
|
@@ -86,7 +87,7 @@ def write_response_processing_group(
|
|
|
86
87
|
group: Destination ``response_processing`` HDF5 group.
|
|
87
88
|
options: Processing options used for the saved analysis output.
|
|
88
89
|
normalization_factors: Optional per-ROI normalization factors used for
|
|
89
|
-
|
|
90
|
+
response-size normalization.
|
|
90
91
|
correlation_scores: Optional ROI-level correlation QC result.
|
|
91
92
|
|
|
92
93
|
Returns:
|
|
@@ -159,14 +160,17 @@ def read_response_processing_group(group: h5py.Group) -> PersistedResponseProces
|
|
|
159
160
|
Returns:
|
|
160
161
|
Typed processing options plus optional correlation scores.
|
|
161
162
|
"""
|
|
163
|
+
normalization_options, normalization_load_warning = (
|
|
164
|
+
_read_normalization_options(
|
|
165
|
+
group["normalization"],
|
|
166
|
+
)
|
|
167
|
+
if "normalization" in group
|
|
168
|
+
else (NormalizationOptions(), None)
|
|
169
|
+
)
|
|
162
170
|
options = ResponseProcessingOptions(
|
|
163
171
|
smoothing=_read_smoothing_options(group["smoothing"]),
|
|
164
172
|
low_pass=_read_low_pass_options(group["low_pass"]),
|
|
165
|
-
normalization=
|
|
166
|
-
_read_normalization_options(group["normalization"])
|
|
167
|
-
if "normalization" in group
|
|
168
|
-
else NormalizationOptions()
|
|
169
|
-
),
|
|
173
|
+
normalization=normalization_options,
|
|
170
174
|
correlation_filter=_read_correlation_filter_options(
|
|
171
175
|
group["correlation_filter"],
|
|
172
176
|
),
|
|
@@ -178,7 +182,11 @@ def read_response_processing_group(group: h5py.Group) -> PersistedResponseProces
|
|
|
178
182
|
group["normalization"],
|
|
179
183
|
options=options.normalization,
|
|
180
184
|
)
|
|
181
|
-
if
|
|
185
|
+
if (
|
|
186
|
+
"normalization" in group
|
|
187
|
+
and "factors" in group["normalization"]
|
|
188
|
+
and options.normalization.method != "none"
|
|
189
|
+
)
|
|
182
190
|
else None
|
|
183
191
|
),
|
|
184
192
|
correlation_scores=(
|
|
@@ -186,6 +194,7 @@ def read_response_processing_group(group: h5py.Group) -> PersistedResponseProces
|
|
|
186
194
|
if "correlation_scores" in group
|
|
187
195
|
else None
|
|
188
196
|
),
|
|
197
|
+
load_warning=normalization_load_warning,
|
|
189
198
|
)
|
|
190
199
|
|
|
191
200
|
|
|
@@ -215,16 +224,35 @@ def _read_low_pass_options(group: h5py.Group) -> LowPassFilterOptions:
|
|
|
215
224
|
)
|
|
216
225
|
|
|
217
226
|
|
|
218
|
-
def _read_normalization_options(
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
227
|
+
def _read_normalization_options(
|
|
228
|
+
group: h5py.Group,
|
|
229
|
+
) -> tuple[NormalizationOptions, str | None]:
|
|
230
|
+
"""Read normalization settings and report old settings twopy no longer uses."""
|
|
231
|
+
raw_method = plain_hdf5_attr_value(group.attrs["method"], scalar_only=True)
|
|
232
|
+
if raw_method == "epoch_peak":
|
|
233
|
+
return (
|
|
234
|
+
NormalizationOptions(
|
|
235
|
+
method="none",
|
|
236
|
+
epoch_number=_optional_int_from_attr(group.attrs["epoch_number"]),
|
|
237
|
+
epoch_name=_optional_text_from_attr(group.attrs["epoch_name"]),
|
|
238
|
+
),
|
|
239
|
+
(
|
|
240
|
+
"This saved analysis used old positive-peak normalization. "
|
|
241
|
+
"Twopy is showing the saved responses as stored. Recompute to use "
|
|
242
|
+
"strongest-response normalization."
|
|
243
|
+
),
|
|
244
|
+
)
|
|
245
|
+
return (
|
|
246
|
+
NormalizationOptions(
|
|
247
|
+
method=require_string_choice(
|
|
248
|
+
raw_method,
|
|
249
|
+
name="response_processing normalization method",
|
|
250
|
+
allowed=_NORMALIZATION_METHODS,
|
|
251
|
+
),
|
|
252
|
+
epoch_number=_optional_int_from_attr(group.attrs["epoch_number"]),
|
|
253
|
+
epoch_name=_optional_text_from_attr(group.attrs["epoch_name"]),
|
|
225
254
|
),
|
|
226
|
-
|
|
227
|
-
epoch_name=_optional_text_from_attr(group.attrs["epoch_name"]),
|
|
255
|
+
None,
|
|
228
256
|
)
|
|
229
257
|
|
|
230
258
|
|
|
@@ -246,7 +274,7 @@ def _read_normalization_factors(
|
|
|
246
274
|
*,
|
|
247
275
|
options: NormalizationOptions,
|
|
248
276
|
) -> RoiNormalizationFactors:
|
|
249
|
-
"""Read persisted
|
|
277
|
+
"""Read persisted response-size normalization factors."""
|
|
250
278
|
return RoiNormalizationFactors(
|
|
251
279
|
roi_labels=_read_string_dataset(group, "roi_labels"),
|
|
252
280
|
factors=require_float64_array(
|
|
@@ -33,7 +33,7 @@ from twopy.analysis.response_processing import (
|
|
|
33
33
|
RoiCorrelationScores,
|
|
34
34
|
RoiNormalizationFactors,
|
|
35
35
|
apply_correlation_filter_to_grouped_roi_responses,
|
|
36
|
-
|
|
36
|
+
normalize_grouped_roi_responses_by_epoch_abs_peak,
|
|
37
37
|
process_roi_delta_f_over_f,
|
|
38
38
|
)
|
|
39
39
|
from twopy.analysis.response_window_options import (
|
|
@@ -561,7 +561,7 @@ def _compute_recording_responses_from_baseline(
|
|
|
561
561
|
)
|
|
562
562
|
_check_cancelled(check_cancelled)
|
|
563
563
|
grouped_responses, normalization_factors = (
|
|
564
|
-
|
|
564
|
+
normalize_grouped_roi_responses_by_epoch_abs_peak(
|
|
565
565
|
grouped_responses,
|
|
566
566
|
options=processing_options.normalization,
|
|
567
567
|
)
|