TwoPy 0.3.0__tar.gz → 0.3.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {twopy-0.3.0 → twopy-0.3.2}/PKG-INFO +17 -3
- {twopy-0.3.0 → twopy-0.3.2}/README.md +16 -2
- {twopy-0.3.0 → twopy-0.3.2}/pyproject.toml +2 -2
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/__init__.py +0 -16
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/analysis/__init__.py +12 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/analysis/persistence.py +147 -69
- twopy-0.3.2/src/twopy/analysis/response_plotting.py +342 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/analysis/response_processing/__init__.py +2 -2
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/analysis/response_processing/apply.py +17 -15
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/analysis/response_processing/options.py +12 -11
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/analysis/response_processing/persistence.py +48 -20
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/analysis/workflow.py +2 -2
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/analysis_cache.py +121 -43
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/api.py +4 -3
- twopy-0.3.2/src/twopy/cache_inventory.py +708 -0
- twopy-0.3.2/src/twopy/config.example.yml +64 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/config.py +239 -9
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/conversion/api.py +127 -22
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/custom/__init__.py +10 -11
- twopy-0.3.0/src/twopy/custom/types.py → twopy-0.3.2/src/twopy/custom/context.py +12 -484
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/custom/discovery.py +3 -6
- twopy-0.3.2/src/twopy/custom/metadata.py +228 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/custom/native_workflows/direction_selectivity.py +2 -2
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/custom/parameters.py +2 -2
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/custom/provenance.py +84 -7
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/custom/registry.py +2 -1
- twopy-0.3.2/src/twopy/custom/results.py +116 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/custom/runner.py +9 -6
- twopy-0.3.2/src/twopy/custom/workflows.py +98 -0
- twopy-0.3.2/src/twopy/napari/__init__.py +75 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/controls.py +38 -19
- twopy-0.3.2/src/twopy/napari/database_favorite_editor.py +106 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/database_favorites.py +56 -5
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/database_search.py +225 -3
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/dims.py +5 -9
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/display_paths.py +125 -13
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/group_matching/fov_assignment.py +1 -8
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/group_matching/images.py +4 -8
- twopy-0.3.2/src/twopy/napari/group_matching/protocols.py +27 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/group_matching/response_preview.py +1 -1
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/group_matching/responses.py +1 -1
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/group_matching/roi_assignment.py +2 -8
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/group_matching/roi_cards.py +4 -9
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/group_matching/window.py +9 -16
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/interactive.py +40 -12
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/launcher.py +196 -8
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/live_analysis.py +1 -1
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/load_controller.py +111 -38
- twopy-0.3.2/src/twopy/napari/load_progress.py +758 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/load_workflow.py +33 -6
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/loaded_recordings_csv.py +35 -53
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/loading.py +410 -46
- twopy-0.3.2/src/twopy/napari/output_routing.py +121 -0
- twopy-0.3.2/src/twopy/napari/plotting/__init__.py +52 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/plotting/data.py +17 -289
- twopy-0.3.2/src/twopy/napari/plotting/docks/__init__.py +52 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/plotting/docks/api.py +31 -7
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/plotting/docks/dynamic_options.py +22 -8
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/plotting/docks/paths.py +18 -4
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/plotting/docks/plot_area.py +1 -1
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/plotting/docks/response_plot_widget.py +415 -133
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/plotting/docks/save_actions.py +39 -10
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/plotting/docks/visibility_state.py +1 -1
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/plotting/export.py +4 -9
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/plotting/export_controls.py +64 -7
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/plotting/normalization_options.py +19 -11
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/plotting/options.py +49 -4
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/plotting/preview_strip.py +1 -1
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/plotting/roi_generation/__init__.py +8 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/plotting/roi_generation/actions.py +35 -2
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/plotting/roi_generation/controls.py +137 -16
- twopy-0.3.2/src/twopy/napari/plotting/roi_generation/options.py +348 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/plotting/widgets.py +1 -1
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/protocols.py +44 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/responses.py +4 -2
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/roi.py +59 -3
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/session.py +75 -1
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/trial_timeline.py +11 -24
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/viewer.py +9 -1
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/roi.py +98 -6
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy.egg-info/PKG-INFO +17 -3
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy.egg-info/SOURCES.txt +12 -1
- twopy-0.3.2/tests/test_cache_inventory.py +399 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_config.py +147 -5
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_conversion.py +76 -4
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_custom_workflows.py +23 -2
- twopy-0.3.2/tests/test_import.py +121 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_load_workflow.py +31 -6
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_napari_database_search.py +329 -3
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_napari_export.py +6 -12
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_napari_live_controller.py +2 -1
- twopy-0.3.2/tests/test_napari_load_controller.py +402 -0
- twopy-0.3.2/tests/test_napari_load_progress.py +379 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_napari_load_tab.py +192 -5
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_napari_loaded_recordings.py +31 -4
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_napari_path_resolution.py +493 -17
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_napari_plot_controls.py +3 -3
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_napari_plot_data.py +21 -4
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_napari_plot_widgets.py +13 -4
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_napari_processing_controls.py +6 -2
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_napari_response_workflow.py +345 -9
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_napari_roi_generation.py +457 -2
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_napari_roi_visibility.py +26 -3
- twopy-0.3.2/tests/test_packaging.py +232 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_persistence.py +147 -2
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_response_processing.py +41 -17
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_roi.py +77 -0
- twopy-0.3.0/src/twopy/napari/__init__.py +0 -27
- twopy-0.3.0/src/twopy/napari/load_progress.py +0 -168
- twopy-0.3.0/src/twopy/napari/plotting/__init__.py +0 -21
- twopy-0.3.0/src/twopy/napari/plotting/docks/__init__.py +0 -23
- twopy-0.3.0/src/twopy/napari/plotting/roi_generation/options.py +0 -62
- twopy-0.3.0/tests/test_import.py +0 -26
- twopy-0.3.0/tests/test_napari_load_progress.py +0 -48
- twopy-0.3.0/tests/test_packaging.py +0 -55
- {twopy-0.3.0 → twopy-0.3.2}/setup.cfg +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/_segments.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/_version.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/analysis/background_subtraction.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/analysis/dff.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/analysis/dff_options.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/analysis/epoch_mapping.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/analysis/group_matching.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/analysis/kernels/__init__.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/analysis/kernels/_fitting.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/analysis/kernels/_stimulus.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/analysis/motion.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/analysis/response_context.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/analysis/response_maps.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/analysis/response_processing/qc.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/analysis/response_processing/signals.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/analysis/response_window_options.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/analysis/responses.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/analysis/timing.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/analysis/trials.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/calibrations/pixel_calibration_profiles.csv +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/calibrations/pixel_size.csv +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/conversion/__init__.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/conversion/alignment_crop.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/conversion/hdf5_writing.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/conversion/matlab_values.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/conversion/orientation.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/conversion/source_loading.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/conversion/stimulus_code.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/conversion/types.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/converted.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/custom/native.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/custom/native_workflows/__init__.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/custom/native_workflows/response_kernels.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/database/__init__.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/database/cache.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/database/catalog.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/database/connection.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/database/generic.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/database/modeled.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/database/search.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/database/sql.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/database/types.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/filenames.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/frame_ranges.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/hdf5_utils.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/inspection.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/matlab.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/__main__.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/custom_tab.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/display.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/errors.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/group_matching/__init__.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/group_matching/cards.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/group_matching/fov_cards.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/group_matching/style.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/group_matching/tables.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/latest_worker.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/movie.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/paths.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/plotting/dff_options.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/plotting/docks/options_panel.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/plotting/docks/response_map_area.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/plotting/form_controls.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/plotting/label_visibility.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/plotting/panels.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/plotting/processing_options.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/plotting/response_map_colors.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/plotting/response_map_display.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/plotting/response_map_options.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/plotting/response_window_options.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/roi_signatures.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/sidebar.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/state.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/text.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/napari/types.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/parity/__init__.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/parity/dff.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/parity/psycho5.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/parity/roi.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/parity/roi_extraction.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/parity/saved_analysis.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/photodiode.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/photodiode_classification.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/pixel_calibration.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/pixel_calibration_profiles.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/response_roi_extraction.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/roi_extraction.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/roi_mask_cleanup.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/session.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/spatial.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/spatial_orientation.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/stimulus.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/synchronization.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy/typing_guards.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy.egg-info/dependency_links.txt +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy.egg-info/entry_points.txt +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy.egg-info/requires.txt +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/src/twopy.egg-info/top_level.txt +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_api.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_background_subtraction.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_converted.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_database.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_dff.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_epoch_mapping.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_filenames.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_frame_ranges.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_function_inventory.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_group_matching.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_hdf5_utils.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_inspection.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_kernels.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_matlab.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_motion.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_napari.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_napari_display.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_napari_group_matching_responses.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_napari_group_matching_roi_cards.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_napari_response_map_display.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_napari_text.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_napari_timeline.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_parity.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_parity_psycho5.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_parity_roi_extraction.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_photodiode_classification.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_pixel_calibration.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_pixel_calibration_profiles.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_real_fixture.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_recording_timing.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_response_map_area.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_response_maps.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_response_roi_extraction.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_response_window_options.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_responses.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_roi_extraction.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_roi_mask_cleanup.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_saved_analysis.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_session.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_stimulus.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_synchronization.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/tests/test_trials.py +0 -0
- {twopy-0.3.0 → twopy-0.3.2}/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.2
|
|
4
4
|
Summary: Two-photon imaging analysis tool with a napari interface.
|
|
5
5
|
Author-email: Gustavo Madeira Santana <fromweb1@gumadeiras.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -30,7 +30,7 @@ Two-photon imaging analysis tool for the Clark Lab output format.
|
|
|
30
30
|
|
|
31
31
|
twopy lets you open two-photon recordings, draw ROIs, plot responses in real time, process and analyze them, and save them.
|
|
32
32
|
|
|
33
|
-
When you first load a recording, twopy converts it to
|
|
33
|
+
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
34
|
|
|
35
35
|
## Install
|
|
36
36
|
|
|
@@ -48,7 +48,19 @@ Verify the install:
|
|
|
48
48
|
micromamba run -n twopy python -c 'import twopy; print(twopy.__name__)'
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
Create the config template in your user folder:
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
twopy config setup
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
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.
|
|
58
|
+
|
|
59
|
+
Validate the active config and show its path and file contents:
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
twopy config
|
|
63
|
+
```
|
|
52
64
|
|
|
53
65
|
## Start
|
|
54
66
|
|
|
@@ -57,6 +69,8 @@ micromamba activate twopy
|
|
|
57
69
|
twopy
|
|
58
70
|
```
|
|
59
71
|
|
|
72
|
+
`twopy` validates the config before opening napari. It does not require lab data folders or network drives to be mounted at launch.
|
|
73
|
+
|
|
60
74
|
Open a source recording or converted recording directly:
|
|
61
75
|
|
|
62
76
|
```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.2"
|
|
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"
|
|
@@ -37,7 +37,7 @@ Repository = "https://github.com/gumadeiras/twopy"
|
|
|
37
37
|
where = ["src"]
|
|
38
38
|
|
|
39
39
|
[tool.setuptools.package-data]
|
|
40
|
-
twopy = ["calibrations/*.csv"]
|
|
40
|
+
twopy = ["calibrations/*.csv", "config.example.yml"]
|
|
41
41
|
|
|
42
42
|
[tool.ruff]
|
|
43
43
|
line-length = 88
|
|
@@ -94,15 +94,6 @@ from twopy.converted import (
|
|
|
94
94
|
recording_frame_rate_hz,
|
|
95
95
|
recording_hemisphere,
|
|
96
96
|
)
|
|
97
|
-
from twopy.napari import (
|
|
98
|
-
NapariRecordingView,
|
|
99
|
-
NapariSidebarWidgets,
|
|
100
|
-
add_twopy_magicgui_controls,
|
|
101
|
-
launch_napari,
|
|
102
|
-
open_recording_in_napari,
|
|
103
|
-
roi_label_image_from_layer,
|
|
104
|
-
save_napari_label_rois,
|
|
105
|
-
)
|
|
106
97
|
from twopy.photodiode_classification import (
|
|
107
98
|
ClassifiedPhotodiodeEvent,
|
|
108
99
|
ClassifiedStimulusTiming,
|
|
@@ -180,7 +171,6 @@ __all__ = [
|
|
|
180
171
|
"AnalysisResponseComputation",
|
|
181
172
|
"add_manual_roi_match_group",
|
|
182
173
|
"analyze_recording_responses",
|
|
183
|
-
"add_twopy_magicgui_controls",
|
|
184
174
|
"append_manual_roi_match_rows",
|
|
185
175
|
"apply_motion_artifact_mask_to_delta_f_over_f",
|
|
186
176
|
"BackgroundCorrectedRoiTraces",
|
|
@@ -227,7 +217,6 @@ __all__ = [
|
|
|
227
217
|
"is_baseline_epoch_name",
|
|
228
218
|
"KernelFitMethod",
|
|
229
219
|
"KernelStimulusModality",
|
|
230
|
-
"launch_napari",
|
|
231
220
|
"load_analysis_outputs",
|
|
232
221
|
"load_response_map_data",
|
|
233
222
|
"load_manual_fov_group_rows",
|
|
@@ -254,9 +243,6 @@ __all__ = [
|
|
|
254
243
|
"remove_manual_roi_match_group",
|
|
255
244
|
"replace_manual_roi_match_group",
|
|
256
245
|
"map_stimulus_specific_column",
|
|
257
|
-
"NapariRecordingView",
|
|
258
|
-
"NapariSidebarWidgets",
|
|
259
|
-
"open_recording_in_napari",
|
|
260
246
|
"pair_photodiode_events_to_imaging_frames",
|
|
261
247
|
"PhotodiodeAlignment",
|
|
262
248
|
"PhotodiodeDurationClass",
|
|
@@ -296,13 +282,11 @@ __all__ = [
|
|
|
296
282
|
"RoiResponseTrial",
|
|
297
283
|
"RoiSet",
|
|
298
284
|
"RoiTraces",
|
|
299
|
-
"roi_label_image_from_layer",
|
|
300
285
|
"roi_set_to_label_image",
|
|
301
286
|
"save_analysis_outputs",
|
|
302
287
|
"save_response_map_data",
|
|
303
288
|
"save_manual_fov_group_rows",
|
|
304
289
|
"save_manual_roi_match_rows",
|
|
305
|
-
"save_napari_label_rois",
|
|
306
290
|
"save_roi_set",
|
|
307
291
|
"select_baseline_frame_windows",
|
|
308
292
|
"select_pixel_calibration_group",
|
|
@@ -66,6 +66,13 @@ from twopy.analysis.response_maps import (
|
|
|
66
66
|
load_response_map_data,
|
|
67
67
|
save_response_map_data,
|
|
68
68
|
)
|
|
69
|
+
from twopy.analysis.response_plotting import (
|
|
70
|
+
EpochResponsePlotData,
|
|
71
|
+
ResponsePlotData,
|
|
72
|
+
filter_response_plot_data_rois,
|
|
73
|
+
response_plot_data_from_grouped,
|
|
74
|
+
response_plot_window_options_from_grouped,
|
|
75
|
+
)
|
|
69
76
|
from twopy.analysis.response_processing import (
|
|
70
77
|
CorrelationFilterOptions,
|
|
71
78
|
LowPassFilterOptions,
|
|
@@ -130,6 +137,7 @@ __all__ = [
|
|
|
130
137
|
"DeltaFOverFOptions",
|
|
131
138
|
"DeltaFOverFFitMode",
|
|
132
139
|
"EpochResponseMap",
|
|
140
|
+
"EpochResponsePlotData",
|
|
133
141
|
"EpochFrameWindow",
|
|
134
142
|
"extract_background_corrected_roi_traces",
|
|
135
143
|
"fit_recording_stimulus_kernels",
|
|
@@ -163,6 +171,7 @@ __all__ = [
|
|
|
163
171
|
"ResponseMapData",
|
|
164
172
|
"ResponseMapMode",
|
|
165
173
|
"ResponseMapOptions",
|
|
174
|
+
"ResponsePlotData",
|
|
166
175
|
"ResponseProcessingOptions",
|
|
167
176
|
"RoiCorrelationScores",
|
|
168
177
|
"RoiResponseSummary",
|
|
@@ -181,9 +190,12 @@ __all__ = [
|
|
|
181
190
|
"save_manual_roi_match_rows",
|
|
182
191
|
"select_baseline_frame_windows",
|
|
183
192
|
"finite_mean_and_sem",
|
|
193
|
+
"filter_response_plot_data_rois",
|
|
184
194
|
"split_traces_by_frame_windows",
|
|
185
195
|
"StimulusKernelOptions",
|
|
186
196
|
"SmoothingOptions",
|
|
197
|
+
"response_plot_data_from_grouped",
|
|
198
|
+
"response_plot_window_options_from_grouped",
|
|
187
199
|
"summarize_epoch_roi_responses",
|
|
188
200
|
"summarize_grouped_responses",
|
|
189
201
|
"TimingSource",
|
|
@@ -10,7 +10,9 @@ MATLAB/TIFF files or decide analysis parameters.
|
|
|
10
10
|
|
|
11
11
|
import csv
|
|
12
12
|
import json
|
|
13
|
-
|
|
13
|
+
import os
|
|
14
|
+
import tempfile
|
|
15
|
+
from collections.abc import Callable, Iterator, Mapping, Sequence
|
|
14
16
|
from dataclasses import dataclass
|
|
15
17
|
from pathlib import Path
|
|
16
18
|
|
|
@@ -99,7 +101,8 @@ class LoadedAnalysisOutputs:
|
|
|
99
101
|
"""Analysis objects loaded from one twopy analysis HDF5 file.
|
|
100
102
|
|
|
101
103
|
Inputs: one file written by ``save_analysis_outputs``.
|
|
102
|
-
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.
|
|
103
106
|
|
|
104
107
|
Groups are optional because scripts can save only the products they have
|
|
105
108
|
computed. Missing groups load as ``None`` or an empty window tuple.
|
|
@@ -115,6 +118,7 @@ class LoadedAnalysisOutputs:
|
|
|
115
118
|
response_processing_options: ResponseProcessingOptions | None
|
|
116
119
|
normalization_factors: RoiNormalizationFactors | None
|
|
117
120
|
correlation_scores: RoiCorrelationScores | None
|
|
121
|
+
load_warning: str | None = None
|
|
118
122
|
|
|
119
123
|
|
|
120
124
|
def save_analysis_outputs(
|
|
@@ -144,7 +148,7 @@ def save_analysis_outputs(
|
|
|
144
148
|
grouped_responses: Optional grouped trial responses.
|
|
145
149
|
response_processing_options: Optional smoothing, low-pass, and
|
|
146
150
|
correlation-QC settings used for these outputs.
|
|
147
|
-
normalization_factors: Optional per-ROI factors used for
|
|
151
|
+
normalization_factors: Optional per-ROI factors used for response-size
|
|
148
152
|
normalization.
|
|
149
153
|
correlation_scores: Optional ROI-level correlation QC scores.
|
|
150
154
|
response_summary_trials_csv: Optional CSV path for one row per trial
|
|
@@ -157,8 +161,11 @@ def save_analysis_outputs(
|
|
|
157
161
|
None.
|
|
158
162
|
|
|
159
163
|
The HDF5 file stores arrays with gzip compression where arrays can be large.
|
|
160
|
-
The HDF5 file is the complete audit trail. CSV files
|
|
161
|
-
|
|
164
|
+
The HDF5 file is the complete audit trail. CSV files are derived exports for
|
|
165
|
+
quick spreadsheet plotting and checks, and are written after the HDF5 file.
|
|
166
|
+
Each output file is written through a temporary sibling path so a failed
|
|
167
|
+
write leaves the previous complete file in place; the HDF5 and CSV files are
|
|
168
|
+
not replaced as one multi-file transaction.
|
|
162
169
|
"""
|
|
163
170
|
summary_outputs = (
|
|
164
171
|
response_summary_trials_csv,
|
|
@@ -175,35 +182,40 @@ def save_analysis_outputs(
|
|
|
175
182
|
if grouped_responses is not None:
|
|
176
183
|
validate_grouped_roi_responses(grouped_responses)
|
|
177
184
|
|
|
178
|
-
output_path
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
185
|
+
def write_hdf5(output_path: Path) -> None:
|
|
186
|
+
"""Write the analysis HDF5 contents to one already-created path."""
|
|
187
|
+
with h5py.File(output_path, "w") as h5_file:
|
|
188
|
+
h5_file.attrs["twopy_format"] = ANALYSIS_OUTPUT_FILE_FORMAT
|
|
189
|
+
if roi_set is not None:
|
|
190
|
+
_write_roi_set(h5_file.create_group("roi_set"), roi_set)
|
|
191
|
+
if traces is not None:
|
|
192
|
+
_write_background_traces(h5_file.create_group("traces"), traces)
|
|
193
|
+
if dff is not None:
|
|
194
|
+
_write_dff(h5_file.create_group("dff"), dff)
|
|
195
|
+
if len(epoch_windows) > 0:
|
|
196
|
+
_write_epoch_windows(
|
|
197
|
+
h5_file.create_group("epoch_windows"),
|
|
198
|
+
epoch_windows,
|
|
199
|
+
)
|
|
200
|
+
if len(baseline_windows) > 0:
|
|
201
|
+
_write_frame_windows(
|
|
202
|
+
h5_file.create_group("baseline_windows"),
|
|
203
|
+
baseline_windows,
|
|
204
|
+
)
|
|
205
|
+
if grouped_responses is not None:
|
|
206
|
+
_write_grouped_responses(
|
|
207
|
+
h5_file.create_group("responses"),
|
|
208
|
+
grouped_responses,
|
|
209
|
+
)
|
|
210
|
+
if response_processing_options is not None:
|
|
211
|
+
write_response_processing_group(
|
|
212
|
+
h5_file.create_group("response_processing"),
|
|
213
|
+
options=response_processing_options,
|
|
214
|
+
normalization_factors=normalization_factors,
|
|
215
|
+
correlation_scores=correlation_scores,
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
_write_file_atomically(path, write_hdf5)
|
|
207
219
|
|
|
208
220
|
if grouped_responses is not None:
|
|
209
221
|
if response_summary_trials_csv is not None:
|
|
@@ -218,6 +230,58 @@ def save_analysis_outputs(
|
|
|
218
230
|
)
|
|
219
231
|
|
|
220
232
|
|
|
233
|
+
def _write_file_atomically(
|
|
234
|
+
path: Path,
|
|
235
|
+
write_file: Callable[[Path], None],
|
|
236
|
+
) -> None:
|
|
237
|
+
"""Write a file beside its final path, then replace the old file.
|
|
238
|
+
|
|
239
|
+
Args:
|
|
240
|
+
path: Final destination path.
|
|
241
|
+
write_file: Callback that writes a complete file at the provided
|
|
242
|
+
temporary path.
|
|
243
|
+
|
|
244
|
+
Returns:
|
|
245
|
+
None.
|
|
246
|
+
|
|
247
|
+
Existing analysis files are useful audit records. Writing through a sibling
|
|
248
|
+
temporary path keeps a failed save from replacing the last complete output.
|
|
249
|
+
"""
|
|
250
|
+
output_path = path.expanduser()
|
|
251
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
252
|
+
temp_descriptor, temp_name = tempfile.mkstemp(
|
|
253
|
+
prefix=f".{output_path.name}.",
|
|
254
|
+
suffix=".tmp",
|
|
255
|
+
dir=output_path.parent,
|
|
256
|
+
)
|
|
257
|
+
os.close(temp_descriptor)
|
|
258
|
+
temp_path = Path(temp_name)
|
|
259
|
+
try:
|
|
260
|
+
write_file(temp_path)
|
|
261
|
+
temp_path.chmod(_replacement_file_mode(output_path))
|
|
262
|
+
temp_path.replace(output_path)
|
|
263
|
+
except Exception:
|
|
264
|
+
temp_path.unlink(missing_ok=True)
|
|
265
|
+
raise
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def _replacement_file_mode(path: Path) -> int:
|
|
269
|
+
"""Return the file mode an atomic replacement should keep.
|
|
270
|
+
|
|
271
|
+
Args:
|
|
272
|
+
path: Final destination path.
|
|
273
|
+
|
|
274
|
+
Returns:
|
|
275
|
+
Existing file permissions, or normal new-file permissions after the
|
|
276
|
+
process umask.
|
|
277
|
+
"""
|
|
278
|
+
if path.exists():
|
|
279
|
+
return path.stat().st_mode & 0o777
|
|
280
|
+
current_umask = os.umask(0)
|
|
281
|
+
os.umask(current_umask)
|
|
282
|
+
return 0o666 & ~current_umask
|
|
283
|
+
|
|
284
|
+
|
|
221
285
|
def load_analysis_outputs(path: Path) -> LoadedAnalysisOutputs:
|
|
222
286
|
"""Load twopy analysis outputs from HDF5.
|
|
223
287
|
|
|
@@ -282,6 +346,11 @@ def load_analysis_outputs(path: Path) -> LoadedAnalysisOutputs:
|
|
|
282
346
|
if response_processing is not None
|
|
283
347
|
else None
|
|
284
348
|
),
|
|
349
|
+
load_warning=(
|
|
350
|
+
response_processing.load_warning
|
|
351
|
+
if response_processing is not None
|
|
352
|
+
else None
|
|
353
|
+
),
|
|
285
354
|
)
|
|
286
355
|
|
|
287
356
|
|
|
@@ -297,25 +366,31 @@ def write_response_summary_trials_csv(
|
|
|
297
366
|
|
|
298
367
|
Returns:
|
|
299
368
|
None.
|
|
369
|
+
|
|
370
|
+
The CSV is written through a temporary sibling path so a failed write leaves
|
|
371
|
+
the previous complete file in place.
|
|
300
372
|
"""
|
|
301
|
-
output_path = path.expanduser()
|
|
302
|
-
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
303
373
|
validate_grouped_roi_responses(grouped_responses)
|
|
304
374
|
time_columns = _time_columns(grouped_responses.trials)
|
|
305
375
|
fieldnames = (*_TRIAL_RESPONSE_CSV_BASE_COLUMNS, *time_columns)
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
376
|
+
|
|
377
|
+
def write_csv(output_path: Path) -> None:
|
|
378
|
+
"""Write trial-level rows to one complete CSV file."""
|
|
379
|
+
with output_path.open("w", encoding="utf-8", newline="") as csv_file:
|
|
380
|
+
writer = csv.DictWriter(csv_file, fieldnames=fieldnames)
|
|
381
|
+
writer.writeheader()
|
|
382
|
+
for trial in grouped_responses.trials:
|
|
383
|
+
for roi_index, roi_label in enumerate(grouped_responses.roi_labels):
|
|
384
|
+
writer.writerow(
|
|
385
|
+
_trial_response_row(
|
|
386
|
+
trial,
|
|
387
|
+
roi_index=roi_index,
|
|
388
|
+
roi_label=roi_label,
|
|
389
|
+
time_columns=time_columns,
|
|
390
|
+
),
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
_write_file_atomically(path, write_csv)
|
|
319
394
|
|
|
320
395
|
|
|
321
396
|
def write_response_summary_grouped_csv(
|
|
@@ -330,17 +405,23 @@ def write_response_summary_grouped_csv(
|
|
|
330
405
|
|
|
331
406
|
Returns:
|
|
332
407
|
None.
|
|
408
|
+
|
|
409
|
+
The CSV is written through a temporary sibling path so a failed write leaves
|
|
410
|
+
the previous complete file in place.
|
|
333
411
|
"""
|
|
334
|
-
output_path = path.expanduser()
|
|
335
|
-
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
336
412
|
validate_grouped_roi_responses(grouped_responses)
|
|
337
413
|
time_columns = _time_columns(grouped_responses.trials)
|
|
338
414
|
fieldnames = (*_GROUPED_RESPONSE_CSV_BASE_COLUMNS, *time_columns)
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
writer.
|
|
415
|
+
|
|
416
|
+
def write_csv(output_path: Path) -> None:
|
|
417
|
+
"""Write grouped rows to one complete CSV file."""
|
|
418
|
+
with output_path.open("w", encoding="utf-8", newline="") as csv_file:
|
|
419
|
+
writer = csv.DictWriter(csv_file, fieldnames=fieldnames)
|
|
420
|
+
writer.writeheader()
|
|
421
|
+
for row in _iter_grouped_response_rows(grouped_responses, time_columns):
|
|
422
|
+
writer.writerow(row)
|
|
423
|
+
|
|
424
|
+
_write_file_atomically(path, write_csv)
|
|
344
425
|
|
|
345
426
|
|
|
346
427
|
def _write_roi_set(group: h5py.Group, roi_set: RoiSet) -> None:
|
|
@@ -894,10 +975,10 @@ def _trial_response_row(
|
|
|
894
975
|
return row
|
|
895
976
|
|
|
896
977
|
|
|
897
|
-
def
|
|
978
|
+
def _iter_grouped_response_rows(
|
|
898
979
|
grouped: GroupedRoiResponses,
|
|
899
980
|
time_columns: tuple[str, ...],
|
|
900
|
-
) ->
|
|
981
|
+
) -> Iterator[dict[str, str | int | float]]:
|
|
901
982
|
"""Convert grouped trial responses to mean, SEM, and count CSV rows.
|
|
902
983
|
|
|
903
984
|
Args:
|
|
@@ -905,12 +986,12 @@ def _grouped_response_rows(
|
|
|
905
986
|
time_columns: Complete CSV time-column union.
|
|
906
987
|
|
|
907
988
|
Returns:
|
|
908
|
-
|
|
989
|
+
Iterator over rows keyed by epoch, ROI, statistic, and relative
|
|
990
|
+
timepoint.
|
|
909
991
|
|
|
910
992
|
Counts are per timepoint rather than one scalar because pre/post context or
|
|
911
993
|
clipped recording edges can make some trials shorter than others.
|
|
912
994
|
"""
|
|
913
|
-
rows: list[dict[str, str | int | float]] = []
|
|
914
995
|
for epoch_number, epoch_name, trials in _trials_by_epoch(grouped.trials):
|
|
915
996
|
for roi_index, roi_label in enumerate(grouped.roi_labels):
|
|
916
997
|
values_by_time = _values_by_time_column(
|
|
@@ -918,16 +999,13 @@ def _grouped_response_rows(
|
|
|
918
999
|
roi_index=roi_index,
|
|
919
1000
|
time_columns=time_columns,
|
|
920
1001
|
)
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
time_columns=time_columns,
|
|
928
|
-
),
|
|
1002
|
+
yield from _epoch_roi_statistic_rows(
|
|
1003
|
+
epoch_number=epoch_number,
|
|
1004
|
+
epoch_name=epoch_name,
|
|
1005
|
+
roi_label=roi_label,
|
|
1006
|
+
values_by_time=values_by_time,
|
|
1007
|
+
time_columns=time_columns,
|
|
929
1008
|
)
|
|
930
|
-
return tuple(rows)
|
|
931
1009
|
|
|
932
1010
|
|
|
933
1011
|
def _trials_by_epoch(
|