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.
Files changed (257) hide show
  1. {twopy-0.3.1 → twopy-0.3.3}/PKG-INFO +18 -3
  2. {twopy-0.3.1 → twopy-0.3.3}/README.md +16 -2
  3. {twopy-0.3.1 → twopy-0.3.3}/pyproject.toml +3 -2
  4. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/group_matching.py +23 -10
  5. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/persistence.py +9 -2
  6. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/response_plotting.py +8 -0
  7. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/response_processing/__init__.py +2 -2
  8. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/response_processing/apply.py +17 -15
  9. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/response_processing/options.py +12 -11
  10. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/response_processing/persistence.py +48 -20
  11. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/workflow.py +2 -2
  12. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis_cache.py +121 -43
  13. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/api.py +4 -3
  14. twopy-0.3.3/src/twopy/cache_inventory.py +708 -0
  15. twopy-0.3.3/src/twopy/config.example.yml +64 -0
  16. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/config.py +239 -9
  17. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/conversion/api.py +127 -12
  18. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/custom/native_workflows/direction_selectivity.py +2 -2
  19. twopy-0.3.3/src/twopy/napari/assets/twopy-app-icon.png +0 -0
  20. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/controls.py +28 -0
  21. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/database_search.py +2 -1
  22. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/display_paths.py +125 -13
  23. twopy-0.3.3/src/twopy/napari/empty_state.py +159 -0
  24. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/group_matching/images.py +17 -11
  25. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/group_matching/response_preview.py +17 -9
  26. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/group_matching/roi_assignment.py +106 -51
  27. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/group_matching/roi_cards.py +122 -38
  28. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/group_matching/window.py +1 -1
  29. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/interactive.py +42 -2
  30. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/launcher.py +196 -8
  31. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/load_controller.py +111 -38
  32. twopy-0.3.3/src/twopy/napari/load_progress.py +758 -0
  33. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/load_workflow.py +33 -6
  34. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/loaded_recordings_csv.py +3 -31
  35. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/loading.py +409 -43
  36. twopy-0.3.3/src/twopy/napari/output_routing.py +121 -0
  37. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/data.py +1 -0
  38. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/docks/api.py +11 -1
  39. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/docks/dynamic_options.py +12 -7
  40. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/docks/options_panel.py +9 -4
  41. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/docks/paths.py +18 -4
  42. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/docks/response_plot_widget.py +409 -140
  43. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/docks/save_actions.py +39 -10
  44. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/export_controls.py +63 -6
  45. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/normalization_options.py +19 -11
  46. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/panels.py +5 -0
  47. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/roi_generation/__init__.py +8 -0
  48. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/roi_generation/actions.py +35 -2
  49. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/roi_generation/controls.py +137 -16
  50. twopy-0.3.3/src/twopy/napari/plotting/roi_generation/options.py +348 -0
  51. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/roi.py +5 -2
  52. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/session.py +48 -1
  53. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/state.py +65 -0
  54. twopy-0.3.3/src/twopy/napari/version_check.py +280 -0
  55. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/viewer.py +35 -2
  56. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/roi.py +70 -6
  57. {twopy-0.3.1 → twopy-0.3.3}/src/twopy.egg-info/PKG-INFO +18 -3
  58. {twopy-0.3.1 → twopy-0.3.3}/src/twopy.egg-info/SOURCES.txt +8 -0
  59. {twopy-0.3.1 → twopy-0.3.3}/src/twopy.egg-info/requires.txt +1 -0
  60. twopy-0.3.3/tests/test_cache_inventory.py +399 -0
  61. {twopy-0.3.1 → twopy-0.3.3}/tests/test_config.py +147 -5
  62. {twopy-0.3.1 → twopy-0.3.3}/tests/test_conversion.py +72 -0
  63. {twopy-0.3.1 → twopy-0.3.3}/tests/test_custom_workflows.py +5 -1
  64. {twopy-0.3.1 → twopy-0.3.3}/tests/test_group_matching.py +65 -28
  65. {twopy-0.3.1 → twopy-0.3.3}/tests/test_load_workflow.py +31 -6
  66. {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari.py +93 -2
  67. {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_export.py +6 -12
  68. {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_group_matching_roi_cards.py +94 -12
  69. {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_live_controller.py +2 -1
  70. {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_load_controller.py +150 -5
  71. twopy-0.3.3/tests/test_napari_load_progress.py +379 -0
  72. {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_load_tab.py +67 -3
  73. {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_loaded_recordings.py +14 -10
  74. {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_path_resolution.py +431 -15
  75. {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_plot_controls.py +7 -3
  76. {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_plot_data.py +15 -0
  77. {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_plot_widgets.py +136 -16
  78. {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_processing_controls.py +6 -2
  79. {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_response_workflow.py +345 -9
  80. {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_roi_generation.py +399 -1
  81. {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_roi_visibility.py +3 -3
  82. twopy-0.3.3/tests/test_packaging.py +249 -0
  83. {twopy-0.3.1 → twopy-0.3.3}/tests/test_persistence.py +57 -2
  84. {twopy-0.3.1 → twopy-0.3.3}/tests/test_response_processing.py +41 -17
  85. {twopy-0.3.1 → twopy-0.3.3}/tests/test_roi.py +48 -0
  86. twopy-0.3.3/tests/test_version_check.py +207 -0
  87. twopy-0.3.1/src/twopy/napari/load_progress.py +0 -168
  88. twopy-0.3.1/src/twopy/napari/plotting/roi_generation/options.py +0 -62
  89. twopy-0.3.1/tests/test_napari_load_progress.py +0 -48
  90. twopy-0.3.1/tests/test_packaging.py +0 -55
  91. {twopy-0.3.1 → twopy-0.3.3}/setup.cfg +0 -0
  92. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/__init__.py +0 -0
  93. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/_segments.py +0 -0
  94. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/_version.py +0 -0
  95. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/__init__.py +0 -0
  96. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/background_subtraction.py +0 -0
  97. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/dff.py +0 -0
  98. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/dff_options.py +0 -0
  99. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/epoch_mapping.py +0 -0
  100. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/kernels/__init__.py +0 -0
  101. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/kernels/_fitting.py +0 -0
  102. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/kernels/_stimulus.py +0 -0
  103. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/motion.py +0 -0
  104. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/response_context.py +0 -0
  105. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/response_maps.py +0 -0
  106. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/response_processing/qc.py +0 -0
  107. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/response_processing/signals.py +0 -0
  108. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/response_window_options.py +0 -0
  109. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/responses.py +0 -0
  110. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/timing.py +0 -0
  111. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/analysis/trials.py +0 -0
  112. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/calibrations/pixel_calibration_profiles.csv +0 -0
  113. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/calibrations/pixel_size.csv +0 -0
  114. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/conversion/__init__.py +0 -0
  115. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/conversion/alignment_crop.py +0 -0
  116. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/conversion/hdf5_writing.py +0 -0
  117. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/conversion/matlab_values.py +0 -0
  118. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/conversion/orientation.py +0 -0
  119. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/conversion/source_loading.py +0 -0
  120. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/conversion/stimulus_code.py +0 -0
  121. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/conversion/types.py +0 -0
  122. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/converted.py +0 -0
  123. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/custom/__init__.py +0 -0
  124. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/custom/context.py +0 -0
  125. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/custom/discovery.py +0 -0
  126. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/custom/metadata.py +0 -0
  127. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/custom/native.py +0 -0
  128. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/custom/native_workflows/__init__.py +0 -0
  129. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/custom/native_workflows/response_kernels.py +0 -0
  130. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/custom/parameters.py +0 -0
  131. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/custom/provenance.py +0 -0
  132. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/custom/registry.py +0 -0
  133. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/custom/results.py +0 -0
  134. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/custom/runner.py +0 -0
  135. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/custom/workflows.py +0 -0
  136. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/database/__init__.py +0 -0
  137. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/database/cache.py +0 -0
  138. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/database/catalog.py +0 -0
  139. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/database/connection.py +0 -0
  140. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/database/generic.py +0 -0
  141. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/database/modeled.py +0 -0
  142. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/database/search.py +0 -0
  143. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/database/sql.py +0 -0
  144. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/database/types.py +0 -0
  145. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/filenames.py +0 -0
  146. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/frame_ranges.py +0 -0
  147. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/hdf5_utils.py +0 -0
  148. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/inspection.py +0 -0
  149. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/matlab.py +0 -0
  150. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/__init__.py +0 -0
  151. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/__main__.py +0 -0
  152. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/custom_tab.py +0 -0
  153. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/database_favorite_editor.py +0 -0
  154. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/database_favorites.py +0 -0
  155. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/dims.py +0 -0
  156. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/display.py +0 -0
  157. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/errors.py +0 -0
  158. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/group_matching/__init__.py +0 -0
  159. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/group_matching/cards.py +0 -0
  160. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/group_matching/fov_assignment.py +0 -0
  161. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/group_matching/fov_cards.py +0 -0
  162. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/group_matching/protocols.py +0 -0
  163. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/group_matching/responses.py +0 -0
  164. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/group_matching/style.py +0 -0
  165. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/group_matching/tables.py +0 -0
  166. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/latest_worker.py +0 -0
  167. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/live_analysis.py +0 -0
  168. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/movie.py +0 -0
  169. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/paths.py +0 -0
  170. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/__init__.py +0 -0
  171. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/dff_options.py +0 -0
  172. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/docks/__init__.py +0 -0
  173. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/docks/plot_area.py +0 -0
  174. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/docks/response_map_area.py +0 -0
  175. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/docks/visibility_state.py +0 -0
  176. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/export.py +0 -0
  177. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/form_controls.py +0 -0
  178. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/label_visibility.py +0 -0
  179. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/options.py +0 -0
  180. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/preview_strip.py +0 -0
  181. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/processing_options.py +0 -0
  182. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/response_map_colors.py +0 -0
  183. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/response_map_display.py +0 -0
  184. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/response_map_options.py +0 -0
  185. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/response_window_options.py +0 -0
  186. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/plotting/widgets.py +0 -0
  187. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/protocols.py +0 -0
  188. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/responses.py +0 -0
  189. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/roi_signatures.py +0 -0
  190. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/sidebar.py +0 -0
  191. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/text.py +0 -0
  192. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/trial_timeline.py +0 -0
  193. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/napari/types.py +0 -0
  194. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/parity/__init__.py +0 -0
  195. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/parity/dff.py +0 -0
  196. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/parity/psycho5.py +0 -0
  197. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/parity/roi.py +0 -0
  198. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/parity/roi_extraction.py +0 -0
  199. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/parity/saved_analysis.py +0 -0
  200. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/photodiode.py +0 -0
  201. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/photodiode_classification.py +0 -0
  202. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/pixel_calibration.py +0 -0
  203. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/pixel_calibration_profiles.py +0 -0
  204. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/response_roi_extraction.py +0 -0
  205. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/roi_extraction.py +0 -0
  206. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/roi_mask_cleanup.py +0 -0
  207. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/session.py +0 -0
  208. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/spatial.py +0 -0
  209. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/spatial_orientation.py +0 -0
  210. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/stimulus.py +0 -0
  211. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/synchronization.py +0 -0
  212. {twopy-0.3.1 → twopy-0.3.3}/src/twopy/typing_guards.py +0 -0
  213. {twopy-0.3.1 → twopy-0.3.3}/src/twopy.egg-info/dependency_links.txt +0 -0
  214. {twopy-0.3.1 → twopy-0.3.3}/src/twopy.egg-info/entry_points.txt +0 -0
  215. {twopy-0.3.1 → twopy-0.3.3}/src/twopy.egg-info/top_level.txt +0 -0
  216. {twopy-0.3.1 → twopy-0.3.3}/tests/test_api.py +0 -0
  217. {twopy-0.3.1 → twopy-0.3.3}/tests/test_background_subtraction.py +0 -0
  218. {twopy-0.3.1 → twopy-0.3.3}/tests/test_converted.py +0 -0
  219. {twopy-0.3.1 → twopy-0.3.3}/tests/test_database.py +0 -0
  220. {twopy-0.3.1 → twopy-0.3.3}/tests/test_dff.py +0 -0
  221. {twopy-0.3.1 → twopy-0.3.3}/tests/test_epoch_mapping.py +0 -0
  222. {twopy-0.3.1 → twopy-0.3.3}/tests/test_filenames.py +0 -0
  223. {twopy-0.3.1 → twopy-0.3.3}/tests/test_frame_ranges.py +0 -0
  224. {twopy-0.3.1 → twopy-0.3.3}/tests/test_function_inventory.py +0 -0
  225. {twopy-0.3.1 → twopy-0.3.3}/tests/test_hdf5_utils.py +0 -0
  226. {twopy-0.3.1 → twopy-0.3.3}/tests/test_import.py +0 -0
  227. {twopy-0.3.1 → twopy-0.3.3}/tests/test_inspection.py +0 -0
  228. {twopy-0.3.1 → twopy-0.3.3}/tests/test_kernels.py +0 -0
  229. {twopy-0.3.1 → twopy-0.3.3}/tests/test_matlab.py +0 -0
  230. {twopy-0.3.1 → twopy-0.3.3}/tests/test_motion.py +0 -0
  231. {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_database_search.py +0 -0
  232. {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_display.py +0 -0
  233. {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_group_matching_responses.py +0 -0
  234. {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_response_map_display.py +0 -0
  235. {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_text.py +0 -0
  236. {twopy-0.3.1 → twopy-0.3.3}/tests/test_napari_timeline.py +0 -0
  237. {twopy-0.3.1 → twopy-0.3.3}/tests/test_parity.py +0 -0
  238. {twopy-0.3.1 → twopy-0.3.3}/tests/test_parity_psycho5.py +0 -0
  239. {twopy-0.3.1 → twopy-0.3.3}/tests/test_parity_roi_extraction.py +0 -0
  240. {twopy-0.3.1 → twopy-0.3.3}/tests/test_photodiode_classification.py +0 -0
  241. {twopy-0.3.1 → twopy-0.3.3}/tests/test_pixel_calibration.py +0 -0
  242. {twopy-0.3.1 → twopy-0.3.3}/tests/test_pixel_calibration_profiles.py +0 -0
  243. {twopy-0.3.1 → twopy-0.3.3}/tests/test_real_fixture.py +0 -0
  244. {twopy-0.3.1 → twopy-0.3.3}/tests/test_recording_timing.py +0 -0
  245. {twopy-0.3.1 → twopy-0.3.3}/tests/test_response_map_area.py +0 -0
  246. {twopy-0.3.1 → twopy-0.3.3}/tests/test_response_maps.py +0 -0
  247. {twopy-0.3.1 → twopy-0.3.3}/tests/test_response_roi_extraction.py +0 -0
  248. {twopy-0.3.1 → twopy-0.3.3}/tests/test_response_window_options.py +0 -0
  249. {twopy-0.3.1 → twopy-0.3.3}/tests/test_responses.py +0 -0
  250. {twopy-0.3.1 → twopy-0.3.3}/tests/test_roi_extraction.py +0 -0
  251. {twopy-0.3.1 → twopy-0.3.3}/tests/test_roi_mask_cleanup.py +0 -0
  252. {twopy-0.3.1 → twopy-0.3.3}/tests/test_saved_analysis.py +0 -0
  253. {twopy-0.3.1 → twopy-0.3.3}/tests/test_session.py +0 -0
  254. {twopy-0.3.1 → twopy-0.3.3}/tests/test_stimulus.py +0 -0
  255. {twopy-0.3.1 → twopy-0.3.3}/tests/test_synchronization.py +0 -0
  256. {twopy-0.3.1 → twopy-0.3.3}/tests/test_trials.py +0 -0
  257. {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.1
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 a standardized HDF5 format. The converted format includes 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 publishes the changed output files back to the configured `analysis_output` location in the background.
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
- For a source checkout, copy `config.example.yml` to `config.yml` and edit the paths for your computer. `config.yml` stays local and is not tracked by git.
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 a standardized HDF5 format. The converted format includes 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 publishes the changed output files back to the configured `analysis_output` location in the background.
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
- For a source checkout, copy `config.example.yml` to `config.yml` and edit the paths for your computer. `config.yml` stays local and is not tracked by git.
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.1"
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: Mapping[Path, str],
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: Mapping from recording path to ROI label.
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.items(),
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=_validate_recording_path(recording_path),
184
- roi_label=_validate_roi_label(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: Mapping[Path, str],
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: Mapping from recording path to selected ROI label.
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: Mapping[Path, str],
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-to-ROI selections.
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 response objects.
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 epoch-peak
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
- normalize_grouped_roi_responses_by_epoch_peak,
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
- "normalize_grouped_roi_responses_by_epoch_peak",
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
- "normalize_grouped_roi_responses_by_epoch_peak",
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 epoch-peak response normalization.
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 peak factor for each ROI in the selected epoch.
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 = normalize_grouped_roi_responses_by_epoch_peak(
154
- processed,
155
- options=options.normalization,
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 normalize_grouped_roi_responses_by_epoch_peak(
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
- """Normalize grouped responses by each ROI's selected-epoch peak.
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 positive
186
- finite peak in that epoch.
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", "epoch_peak"}:
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 = "epoch-peak normalization requires an epoch_number"
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 per-ROI selected-epoch peak factors."""
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 peak must be positive for every ROI; "
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", "epoch_peak"]
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
- """Epoch-peak normalization settings for grouped ROI responses.
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. ``method="epoch_peak"``
80
- divides every response for each ROI by that ROI's mean-response peak in the
81
- selected epoch, so the selected epoch's plotted peak becomes one.
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, epoch-peak normalization,
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 epoch-peak normalization options."""
214
- if options.method not in {"none", "epoch_peak"}:
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 == "epoch_peak" and options.epoch_number is None:
221
- msg = "epoch-peak normalization requires an epoch_number"
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", "epoch_peak")
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 normalization factors and ROI
68
- correlation scores.
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
- epoch-peak normalization.
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 "normalization" in group and "factors" in group["normalization"]
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(group: h5py.Group) -> NormalizationOptions:
219
- """Read normalization settings from one HDF5 group."""
220
- return NormalizationOptions(
221
- method=require_string_choice(
222
- plain_hdf5_attr_value(group.attrs["method"], scalar_only=True),
223
- name="response_processing normalization method",
224
- allowed=_NORMALIZATION_METHODS,
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
- epoch_number=_optional_int_from_attr(group.attrs["epoch_number"]),
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 epoch-peak normalization factors."""
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
- normalize_grouped_roi_responses_by_epoch_peak,
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
- normalize_grouped_roi_responses_by_epoch_peak(
564
+ normalize_grouped_roi_responses_by_epoch_abs_peak(
565
565
  grouped_responses,
566
566
  options=processing_options.normalization,
567
567
  )