FlowCyPy 0.7.4__tar.gz → 0.8.1__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 (166) hide show
  1. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/_version.py +2 -2
  2. flowcypy-0.8.1/FlowCyPy/acquisition.py +188 -0
  3. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/classifier.py +4 -4
  4. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/cytometer.py +22 -14
  5. flowcypy-0.8.1/FlowCyPy/dataframe_subclass.py +477 -0
  6. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/detector.py +2 -32
  7. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/flow_cell.py +5 -1
  8. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/helper.py +44 -3
  9. flowcypy-0.8.1/FlowCyPy/triggered_acquisition.py +200 -0
  10. flowcypy-0.8.1/FlowCyPy/utils.py +164 -0
  11. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy.egg-info/PKG-INFO +1 -1
  12. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy.egg-info/SOURCES.txt +2 -0
  13. {flowcypy-0.7.4 → flowcypy-0.8.1}/PKG-INFO +1 -1
  14. flowcypy-0.8.1/developments/scripts/dev_study_on_size.py +59 -0
  15. flowcypy-0.8.1/developments/scripts/temp.py +90 -0
  16. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/examples/extras/flow_cytometer_signal.py +1 -1
  17. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/examples/extras/signal_acquisition.py +1 -1
  18. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/examples/tutorials/limit_of_detection.py +8 -6
  19. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/examples/tutorials/workflow.py +20 -16
  20. {flowcypy-0.7.4 → flowcypy-0.8.1}/tests/test_classifiers.py +7 -12
  21. {flowcypy-0.7.4 → flowcypy-0.8.1}/tests/test_coupling_mechanism.py +0 -1
  22. {flowcypy-0.7.4 → flowcypy-0.8.1}/tests/test_flow_cytometer.py +12 -11
  23. flowcypy-0.7.4/FlowCyPy/acquisition.py +0 -824
  24. flowcypy-0.7.4/FlowCyPy/utils.py +0 -74
  25. flowcypy-0.7.4/developments/scripts/dev_study_on_size.py +0 -161
  26. flowcypy-0.7.4/developments/scripts/temp.py +0 -70
  27. {flowcypy-0.7.4 → flowcypy-0.8.1}/.flake8 +0 -0
  28. {flowcypy-0.7.4 → flowcypy-0.8.1}/.github/dependabot.yml +0 -0
  29. {flowcypy-0.7.4 → flowcypy-0.8.1}/.github/workflows/deploy_PyPi.yml +0 -0
  30. {flowcypy-0.7.4 → flowcypy-0.8.1}/.github/workflows/deploy_anaconda.yml +0 -0
  31. {flowcypy-0.7.4 → flowcypy-0.8.1}/.github/workflows/deploy_coverage.yml +0 -0
  32. {flowcypy-0.7.4 → flowcypy-0.8.1}/.github/workflows/deploy_documentation.yml +0 -0
  33. {flowcypy-0.7.4 → flowcypy-0.8.1}/.gitignore +0 -0
  34. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/__init__.py +0 -0
  35. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/coupling_mechanism/__init__.py +0 -0
  36. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/coupling_mechanism/empirical.py +0 -0
  37. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/coupling_mechanism/mie.py +0 -0
  38. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/coupling_mechanism/rayleigh.py +0 -0
  39. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/coupling_mechanism/uniform.py +0 -0
  40. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/directories.py +0 -0
  41. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/distribution/__init__.py +0 -0
  42. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/distribution/base_class.py +0 -0
  43. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/distribution/delta.py +0 -0
  44. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/distribution/lognormal.py +0 -0
  45. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/distribution/normal.py +0 -0
  46. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/distribution/particle_size_distribution.py +0 -0
  47. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/distribution/uniform.py +0 -0
  48. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/distribution/weibull.py +0 -0
  49. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/noises.py +0 -0
  50. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/particle_count.py +0 -0
  51. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/peak_locator/__init__.py +0 -0
  52. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/peak_locator/base_class.py +0 -0
  53. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/peak_locator/basic.py +0 -0
  54. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/peak_locator/derivative.py +0 -0
  55. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/peak_locator/moving_average.py +0 -0
  56. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/physical_constant.py +0 -0
  57. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/population.py +0 -0
  58. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/populations_instances.py +0 -0
  59. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/scatterer_collection.py +0 -0
  60. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/signal_digitizer.py +0 -0
  61. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/source.py +0 -0
  62. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/units.py +0 -0
  63. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy.egg-info/dependency_links.txt +0 -0
  64. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy.egg-info/requires.txt +0 -0
  65. {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy.egg-info/top_level.txt +0 -0
  66. {flowcypy-0.7.4 → flowcypy-0.8.1}/LICENSE +0 -0
  67. {flowcypy-0.7.4 → flowcypy-0.8.1}/README.rst +0 -0
  68. {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/Deep_peak_square.ipynb +0 -0
  69. {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/Physics-informed_AI.ipynb +0 -0
  70. {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/ROI_analysis-Copy1.ipynb +0 -0
  71. {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/ROI_analysis.ipynb +0 -0
  72. {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/Untitled.ipynb +0 -0
  73. {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/Untitled1.ipynb +0 -0
  74. {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/Untitled2.ipynb +0 -0
  75. {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/ai_dev2.ipynb +0 -0
  76. {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/best_model.h5 +0 -0
  77. {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/best_model.keras +0 -0
  78. {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/concentration_validation.py +0 -0
  79. {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/doc/canto_spec.md +0 -0
  80. {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/doc/internship.pdf +0 -0
  81. {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/get_started.md +0 -0
  82. {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/grad_cam_output.png +0 -0
  83. {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/image.png +0 -0
  84. {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/model.png +0 -0
  85. {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/model_example.png +0 -0
  86. {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/output_file.prof +0 -0
  87. {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/scripts/AI_peak_detection.py +0 -0
  88. {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/scripts/concentration_comparison.py +0 -0
  89. {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/scripts/create_images.py +0 -0
  90. {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/scripts/data_analysis.py +0 -0
  91. {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/scripts/dev_beads_analysis.py +0 -0
  92. {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/scripts/dev_canto.py +0 -0
  93. {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/scripts/dev_classifier.py +0 -0
  94. {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/scripts/dev_shot_noise_check.py +0 -0
  95. {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/scripts/dev_stats_0.py +0 -0
  96. {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/scripts/dev_stats_1.py +0 -0
  97. {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/scripts/dev_stats_2.py +0 -0
  98. {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/scripts/dev_study_on_ri.py +0 -0
  99. {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/scripts/mat2csv.py +0 -0
  100. {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/scripts/profiler.py +0 -0
  101. {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/test.pdf +0 -0
  102. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/Makefile +0 -0
  103. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/examples/extras/README.rst +0 -0
  104. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/examples/extras/distributions.py +0 -0
  105. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/examples/extras/scatterer_distribution.py +0 -0
  106. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/examples/noise_sources/README.rst +0 -0
  107. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/examples/noise_sources/dark_current.py +0 -0
  108. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/examples/noise_sources/shot_noise.py +0 -0
  109. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/examples/noise_sources/thermal.py +0 -0
  110. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/examples/tutorials/README.rst +0 -0
  111. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/images/distributions/Delta.png +0 -0
  112. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/images/distributions/LogNormal.png +0 -0
  113. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/images/distributions/Normal.png +0 -0
  114. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/images/distributions/RosinRammler.png +0 -0
  115. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/images/distributions/Uniform.png +0 -0
  116. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/images/distributions/Weibull.png +0 -0
  117. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/images/example_0.png +0 -0
  118. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/images/example_1.png +0 -0
  119. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/images/example_2.png +0 -0
  120. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/images/example_3.png +0 -0
  121. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/images/flow_cytometer.png +0 -0
  122. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/images/logo.png +0 -0
  123. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/make.bat +0 -0
  124. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/_static/default.css +0 -0
  125. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/_static/logo.png +0 -0
  126. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/_static/thumbnail.png +0 -0
  127. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/code/base.rst +0 -0
  128. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/code/detector.rst +0 -0
  129. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/code/distributions.rst +0 -0
  130. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/code/flow_cell.rst +0 -0
  131. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/code/flow_cytometer.rst +0 -0
  132. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/code/peak_locator.rst +0 -0
  133. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/code/scatterer.rst +0 -0
  134. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/code/source.rst +0 -0
  135. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/code.rst +0 -0
  136. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/conf.py +0 -0
  137. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/examples.rst +0 -0
  138. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/index.rst +0 -0
  139. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/internal/core_components.rst +0 -0
  140. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/internal/getting_started.rst +0 -0
  141. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/internal/objectives/main.rst +0 -0
  142. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/internal/objectives/pre.rst +0 -0
  143. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/internal/objectives/stretch.rst +0 -0
  144. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/internal/prerequisites/index.rst +0 -0
  145. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/internal/prerequisites/mathematics.rst +0 -0
  146. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/internal/prerequisites/optics.rst +0 -0
  147. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/internal/prerequisites/programming.rst +0 -0
  148. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/internal/ressources.rst +0 -0
  149. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/internal/tasks.rst +0 -0
  150. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/internal.rst +0 -0
  151. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/references.rst +0 -0
  152. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/sg_execution_times.rst +0 -0
  153. {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/theory.rst +0 -0
  154. {flowcypy-0.7.4 → flowcypy-0.8.1}/meta.yaml +0 -0
  155. {flowcypy-0.7.4 → flowcypy-0.8.1}/notebook.ipynb +0 -0
  156. {flowcypy-0.7.4 → flowcypy-0.8.1}/pyproject.toml +0 -0
  157. {flowcypy-0.7.4 → flowcypy-0.8.1}/setup.cfg +0 -0
  158. {flowcypy-0.7.4 → flowcypy-0.8.1}/tests/__init__.py +0 -0
  159. {flowcypy-0.7.4 → flowcypy-0.8.1}/tests/test_detector_noise.py +0 -0
  160. {flowcypy-0.7.4 → flowcypy-0.8.1}/tests/test_distribution.py +0 -0
  161. {flowcypy-0.7.4 → flowcypy-0.8.1}/tests/test_noises.py +0 -0
  162. {flowcypy-0.7.4 → flowcypy-0.8.1}/tests/test_peak_algorithm.py +0 -0
  163. {flowcypy-0.7.4 → flowcypy-0.8.1}/tests/test_peak_analyzer.py +0 -0
  164. {flowcypy-0.7.4 → flowcypy-0.8.1}/tests/test_population.py +0 -0
  165. {flowcypy-0.7.4 → flowcypy-0.8.1}/tests/test_scatterer_distribution.py +0 -0
  166. {flowcypy-0.7.4 → flowcypy-0.8.1}/tests/test_source.py +0 -0
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '0.7.4'
16
- __version_tuple__ = version_tuple = (0, 7, 4)
15
+ __version__ = version = '0.8.1'
16
+ __version_tuple__ = version_tuple = (0, 8, 1)
@@ -0,0 +1,188 @@
1
+ import warnings
2
+ import pandas as pd
3
+ import numpy as np
4
+ from FlowCyPy import units
5
+ from FlowCyPy.triggered_acquisition import TriggeredAcquisitions
6
+ from FlowCyPy.dataframe_subclass import TriggeredAcquisitionDataFrame
7
+
8
+
9
+ class Acquisition:
10
+ """
11
+ Represents a flow cytometry experiment, including runtime, dataframes, logging, and visualization.
12
+
13
+ Attributes
14
+ ----------
15
+ run_time : units.second
16
+ Total runtime of the experiment.
17
+ scatterer_dataframe : pd.DataFrame
18
+ DataFrame containing scatterer data, indexed by population and time.
19
+ detector_dataframe : pd.DataFrame
20
+ DataFrame containing detector signal data, indexed by detector and time.
21
+ """
22
+
23
+ def __init__(self, run_time: units.second, cytometer: object, scatterer_dataframe: pd.DataFrame, detector_dataframe: pd.DataFrame):
24
+ """
25
+ Initializes the Experiment instance.
26
+
27
+ Parameters
28
+ ----------
29
+ run_time : Quantity
30
+ Total runtime of the experiment.
31
+ scatterer_dataframe : pd.DataFrame
32
+ DataFrame with scatterer data.
33
+ detector_dataframe : pd.DataFrame
34
+ DataFrame with detector signal data.
35
+ """
36
+ self.cytometer = cytometer
37
+ self.signal = detector_dataframe
38
+ self.scatterer = scatterer_dataframe
39
+ self.run_time = run_time
40
+
41
+ @property
42
+ def n_detectors(self) -> int:
43
+ return len(self.signal.index.get_level_values('Detector').unique())
44
+
45
+ def _get_trigger_indices(
46
+ self,
47
+ threshold: units.Quantity,
48
+ trigger_detector_name: str = None,
49
+ pre_buffer: int = 64,
50
+ post_buffer: int = 64
51
+ ) -> tuple[np.ndarray, np.ndarray]:
52
+ """
53
+ Calculate start and end indices for triggered segments, ensuring no retriggering
54
+ occurs during an active buffer period.
55
+
56
+ Parameters
57
+ ----------
58
+ threshold : units.Quantity
59
+ The threshold value for triggering.
60
+ trigger_detector_name : str, optional
61
+ The name of the detector to use for the triggering signal.
62
+ pre_buffer : int, optional
63
+ Number of samples to include before the trigger point.
64
+ post_buffer : int, optional
65
+ Number of samples to include after the trigger point.
66
+
67
+ Returns
68
+ -------
69
+ tuple[np.ndarray, np.ndarray]
70
+ The start and end indices of non-overlapping triggered segments.
71
+
72
+ Raises
73
+ ------
74
+ ValueError
75
+ If the specified detector is not found in the data.
76
+ """
77
+ if trigger_detector_name not in self.signal.index.get_level_values('Detector').unique():
78
+ raise ValueError(f"Detector '{trigger_detector_name}' not found.")
79
+
80
+ signal = self.signal.xs(trigger_detector_name)['Signal']
81
+ trigger_signal = signal > threshold.to(signal.pint.units)
82
+
83
+ crossings = np.where(np.diff(trigger_signal.astype(int)) == 1)[0]
84
+ start_indices = np.clip(crossings - pre_buffer, 0, len(trigger_signal) - 1)
85
+ end_indices = np.clip(crossings + post_buffer, 0, len(trigger_signal) - 1)
86
+
87
+ # Suppress retriggering within an active buffer period
88
+ suppressed_start_indices = []
89
+ suppressed_end_indices = []
90
+
91
+ last_end = -1
92
+ for start, end in zip(start_indices, end_indices):
93
+ if start > last_end: # Ensure no overlap with the last active buffer
94
+ suppressed_start_indices.append(start)
95
+ suppressed_end_indices.append(end)
96
+ last_end = end # Update the end of the current active buffer
97
+
98
+ return np.array(suppressed_start_indices), np.array(suppressed_end_indices)
99
+
100
+ def run_triggering(self,
101
+ threshold: units.Quantity,
102
+ trigger_detector_name: str,
103
+ pre_buffer: int = 64,
104
+ post_buffer: int = 64,
105
+ max_triggers: int = None) -> TriggeredAcquisitions:
106
+ """
107
+ Execute triggered acquisition analysis for signal data.
108
+
109
+ This method identifies segments of signal data based on a triggering threshold
110
+ and specified detector. It extracts segments of interest from the signal,
111
+ including a pre-trigger buffer and post-trigger buffer.
112
+
113
+ Parameters
114
+ ----------
115
+ threshold : units.Quantity
116
+ The threshold value for triggering. Only signal values exceeding this threshold
117
+ will be considered as trigger events.
118
+ trigger_detector_name : str
119
+ The name of the detector used for triggering. This determines which detector's
120
+ signal is analyzed for trigger events.
121
+ pre_buffer : int, optional
122
+ The number of points to include before the trigger point in each segment.
123
+ Default is 64.
124
+ post_buffer : int, optional
125
+ The number of points to include after the trigger point in each segment.
126
+ Default is 64.
127
+ max_triggers : int, optional
128
+ The maximum number of triggers to process. If None, all triggers will be processed.
129
+ Default is None.
130
+
131
+ Raises
132
+ ------
133
+ ValueError
134
+ If the specified `trigger_detector_name` is not found in the dataset.
135
+
136
+ Warnings
137
+ --------
138
+ UserWarning
139
+ If no triggers are detected for the specified threshold, the method raises a warning
140
+ indicating that no signals met the criteria.
141
+
142
+ Notes
143
+ -----
144
+ - The peak detection function `self.detect_peaks` is automatically called at the end of this method to analyze triggered segments.
145
+ """
146
+ self.threshold = threshold
147
+ self.trigger_detector_name = trigger_detector_name
148
+ start_indices, end_indices = self._get_trigger_indices(
149
+ threshold, trigger_detector_name, pre_buffer, post_buffer
150
+ )
151
+
152
+ if max_triggers is not None:
153
+ start_indices = start_indices[:max_triggers]
154
+ end_indices = end_indices[:max_triggers]
155
+
156
+ segments = []
157
+ for detector_name in self.signal.index.get_level_values('Detector').unique():
158
+ detector_data = self.signal.xs(detector_name)
159
+ time, digitized, signal = detector_data['Time'], detector_data['DigitizedSignal'], detector_data['Signal']
160
+
161
+
162
+ for idx, (start, end) in enumerate(zip(start_indices, end_indices)):
163
+
164
+ segment = pd.DataFrame({
165
+ 'Time': time[start:end + 1],
166
+ 'DigitizedSignal': digitized[start:end + 1],
167
+ 'Signal': signal[start:end + 1],
168
+ 'Detector': detector_name,
169
+ 'SegmentID': idx
170
+ })
171
+ segments.append(segment)
172
+
173
+ if len(segments) !=0:
174
+ triggered_signal = TriggeredAcquisitionDataFrame(pd.concat(segments).set_index(['Detector', 'SegmentID']))
175
+ triggered_signal.attrs['bit_depth'] = self.signal.attrs['bit_depth']
176
+ triggered_signal.attrs['saturation_levels'] = self.signal.attrs['saturation_levels']
177
+ triggered_signal.attrs['scatterer_dataframe'] = self.signal.attrs['scatterer_dataframe']
178
+
179
+ triggered_acquisition = TriggeredAcquisitions(parent=self, dataframe=triggered_signal)
180
+ triggered_acquisition.scatterer = self.scatterer
181
+
182
+ return triggered_acquisition
183
+ else:
184
+ warnings.warn(
185
+ f"No signal were triggered during the run time, try changing the threshold. Signal min-max value is: {self.signal['Signal'].min().to_compact()}, {self.signal['Signal'].max().to_compact()}",
186
+ UserWarning
187
+ )
188
+
@@ -2,7 +2,7 @@ from sklearn.cluster import KMeans
2
2
  from sklearn.cluster import DBSCAN
3
3
  from sklearn.mixture import GaussianMixture
4
4
  import pandas as pd
5
- from typing import Dict, Tuple
5
+ from FlowCyPy.dataframe_subclass import ClassifierDataFrame
6
6
 
7
7
 
8
8
  class BaseClassifier:
@@ -80,7 +80,7 @@ class KmeansClassifier(BaseClassifier):
80
80
 
81
81
  dataframe['Label'] = labels
82
82
 
83
- return labels
83
+ return ClassifierDataFrame(dataframe)
84
84
 
85
85
  class GaussianMixtureClassifier(BaseClassifier):
86
86
  def __init__(self, number_of_components: int) -> None:
@@ -128,7 +128,7 @@ class GaussianMixtureClassifier(BaseClassifier):
128
128
  # Add labels to the original DataFrame
129
129
  dataframe['Label'] = labels
130
130
 
131
- return labels
131
+ return ClassifierDataFrame(dataframe)
132
132
 
133
133
  class DBSCANClassifier(BaseClassifier):
134
134
  def __init__(self, epsilon: float = 0.5, min_samples: int = 5) -> None:
@@ -179,4 +179,4 @@ class DBSCANClassifier(BaseClassifier):
179
179
  # Add labels to the original DataFrame
180
180
  dataframe['Label'] = labels
181
181
 
182
- return labels
182
+ return ClassifierDataFrame(dataframe)
@@ -8,12 +8,13 @@ import pandas as pd
8
8
  from pint_pandas import PintArray
9
9
 
10
10
  from FlowCyPy import units
11
- from FlowCyPy.units import Quantity, milliwatt
11
+ from FlowCyPy.units import milliwatt
12
12
  from FlowCyPy.flow_cell import FlowCell
13
13
  from FlowCyPy.detector import Detector
14
14
  from FlowCyPy.acquisition import Acquisition
15
15
  from FlowCyPy.signal_digitizer import SignalDigitizer
16
16
  from FlowCyPy.helper import validate_units
17
+ from FlowCyPy.dataframe_subclass import ContinuousAcquisitionDataFrame
17
18
 
18
19
 
19
20
  # Set up logging configuration
@@ -41,7 +42,7 @@ class FlowCytometer:
41
42
  coupling_mechanism : str, optional
42
43
  The scattering mechanism used to couple the signal from the particles to the detectors.
43
44
  Supported mechanisms include: 'mie' (default): Mie scattering, 'rayleigh': Rayleigh scattering, 'uniform': Uniform signal coupling, 'empirical': Empirical data-driven coupling
44
- background_power : Quantity, optional
45
+ background_power : units.watt, optional
45
46
  The background optical power added to the detector signal. Defaults to 0 milliwatts.
46
47
 
47
48
  Attributes
@@ -56,7 +57,7 @@ class FlowCytometer:
56
57
  The detectors used to collect and process signals from the scatterers.
57
58
  coupling_mechanism : str
58
59
  The selected mechanism for signal coupling.
59
- background_power : Quantity
60
+ background_power : units.watt
60
61
  The optical background power added to the detector signals.
61
62
 
62
63
  Raises
@@ -72,7 +73,7 @@ class FlowCytometer:
72
73
  signal_digitizer: SignalDigitizer,
73
74
  detectors: List[Detector],
74
75
  coupling_mechanism: Optional[str] = 'mie',
75
- background_power: Optional[Quantity] = 0 * milliwatt):
76
+ background_power: Optional[units.watt] = 0 * milliwatt):
76
77
 
77
78
  self.scatterer_collection = scatterer_collection
78
79
  self.flow_cell = flow_cell
@@ -185,7 +186,7 @@ class FlowCytometer:
185
186
 
186
187
  scatterer_dataframe['Widths'] = PintArray(widths, dtype=widths.units)
187
188
 
188
- def _initialize_signal(self, run_time: Quantity) -> None:
189
+ def _initialize_signal(self, run_time: units.second) -> None:
189
190
  """
190
191
  Initializes the raw signal for each detector based on the source and flow cell configuration.
191
192
 
@@ -206,12 +207,14 @@ class FlowCytometer:
206
207
 
207
208
  dataframes.append(dataframe)
208
209
 
209
- self.dataframe = pd.concat(dataframes, keys=[d.name for d in self.detectors])
210
+ dataframe = pd.concat(dataframes, keys=[d.name for d in self.detectors])
210
211
 
211
- self.dataframe.index.names = ["Detector", "Index"]
212
+ dataframe.index.names = ["Detector", "Index"]
213
+
214
+ return dataframe
212
215
 
213
216
  @validate_units(run_time=units.second)
214
- def get_acquisition(self, run_time: Quantity) -> None:
217
+ def get_acquisition(self, run_time: units.second) -> None:
215
218
  """
216
219
  Simulates the generation of optical signal pulses for each particle event.
217
220
 
@@ -233,7 +236,7 @@ class FlowCytometer:
233
236
  if not run_time.check('second'):
234
237
  raise ValueError(f"flow_speed must be in meter per second, but got {run_time.units}")
235
238
 
236
- self._initialize_signal(run_time=run_time)
239
+ signal_dataframe = self._initialize_signal(run_time=run_time)
237
240
 
238
241
  scatterer_dataframe = self.flow_cell._generate_event_dataframe(self.scatterer_collection.populations, run_time=run_time)
239
242
 
@@ -251,7 +254,7 @@ class FlowCytometer:
251
254
  for detector in self.detectors:
252
255
  _coupling_power = scatterer_dataframe[detector.name].values
253
256
 
254
- detector_signal = self.dataframe.xs(detector.name)['Signal']
257
+ detector_signal = signal_dataframe.xs(detector.name)['Signal']
255
258
 
256
259
  # Generate noise components
257
260
  detector._add_thermal_noise_to_raw_signal(signal=detector_signal)
@@ -259,7 +262,7 @@ class FlowCytometer:
259
262
  detector._add_dark_current_noise_to_raw_signal(signal=detector_signal)
260
263
 
261
264
  # Broadcast the time array to the shape of (number of signals, len(detector.time))
262
- time = self.dataframe.xs(detector.name)['Time'].pint.magnitude
265
+ time = signal_dataframe.xs(detector.name)['Time'].pint.magnitude
263
266
 
264
267
  time_grid = np.expand_dims(time, axis=0) * units.second
265
268
  centers = np.expand_dims(_centers, axis=1) * units.second
@@ -280,15 +283,20 @@ class FlowCytometer:
280
283
 
281
284
  digitized_signal = detector.capture_signal(signal=detector_signal)
282
285
 
283
- self.dataframe.loc[detector.name, 'Signal'] = PintArray(detector_signal, detector_signal.pint.units)
286
+ signal_dataframe.loc[detector.name, 'Signal'] = PintArray(detector_signal, detector_signal.pint.units)
287
+
288
+ signal_dataframe.loc[detector.name, 'DigitizedSignal'] = PintArray(digitized_signal, units.bit_bins)
284
289
 
285
- self.dataframe.loc[detector.name, 'DigitizedSignal'] = PintArray(digitized_signal, units.bit_bins)
290
+ signal_dataframe = ContinuousAcquisitionDataFrame(signal_dataframe)
291
+ signal_dataframe.attrs['bit_depth'] = self.signal_digitizer._bit_depth
292
+ signal_dataframe.attrs['saturation_levels'] = {d.name: d._saturation_levels for d in self.detectors}
293
+ signal_dataframe.attrs['scatterer_dataframe'] = scatterer_dataframe
286
294
 
287
295
  experiment = Acquisition(
288
296
  cytometer=self,
289
297
  run_time=run_time,
290
298
  scatterer_dataframe=scatterer_dataframe,
291
- detector_dataframe=self.dataframe
299
+ detector_dataframe=signal_dataframe
292
300
  )
293
301
 
294
302
  return experiment