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.
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/_version.py +2 -2
- flowcypy-0.8.1/FlowCyPy/acquisition.py +188 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/classifier.py +4 -4
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/cytometer.py +22 -14
- flowcypy-0.8.1/FlowCyPy/dataframe_subclass.py +477 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/detector.py +2 -32
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/flow_cell.py +5 -1
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/helper.py +44 -3
- flowcypy-0.8.1/FlowCyPy/triggered_acquisition.py +200 -0
- flowcypy-0.8.1/FlowCyPy/utils.py +164 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy.egg-info/PKG-INFO +1 -1
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy.egg-info/SOURCES.txt +2 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/PKG-INFO +1 -1
- flowcypy-0.8.1/developments/scripts/dev_study_on_size.py +59 -0
- flowcypy-0.8.1/developments/scripts/temp.py +90 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/examples/extras/flow_cytometer_signal.py +1 -1
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/examples/extras/signal_acquisition.py +1 -1
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/examples/tutorials/limit_of_detection.py +8 -6
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/examples/tutorials/workflow.py +20 -16
- {flowcypy-0.7.4 → flowcypy-0.8.1}/tests/test_classifiers.py +7 -12
- {flowcypy-0.7.4 → flowcypy-0.8.1}/tests/test_coupling_mechanism.py +0 -1
- {flowcypy-0.7.4 → flowcypy-0.8.1}/tests/test_flow_cytometer.py +12 -11
- flowcypy-0.7.4/FlowCyPy/acquisition.py +0 -824
- flowcypy-0.7.4/FlowCyPy/utils.py +0 -74
- flowcypy-0.7.4/developments/scripts/dev_study_on_size.py +0 -161
- flowcypy-0.7.4/developments/scripts/temp.py +0 -70
- {flowcypy-0.7.4 → flowcypy-0.8.1}/.flake8 +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/.github/dependabot.yml +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/.github/workflows/deploy_PyPi.yml +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/.github/workflows/deploy_anaconda.yml +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/.github/workflows/deploy_coverage.yml +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/.github/workflows/deploy_documentation.yml +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/.gitignore +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/__init__.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/coupling_mechanism/__init__.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/coupling_mechanism/empirical.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/coupling_mechanism/mie.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/coupling_mechanism/rayleigh.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/coupling_mechanism/uniform.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/directories.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/distribution/__init__.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/distribution/base_class.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/distribution/delta.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/distribution/lognormal.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/distribution/normal.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/distribution/particle_size_distribution.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/distribution/uniform.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/distribution/weibull.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/noises.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/particle_count.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/peak_locator/__init__.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/peak_locator/base_class.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/peak_locator/basic.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/peak_locator/derivative.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/peak_locator/moving_average.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/physical_constant.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/population.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/populations_instances.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/scatterer_collection.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/signal_digitizer.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/source.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy/units.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy.egg-info/dependency_links.txt +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy.egg-info/requires.txt +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/FlowCyPy.egg-info/top_level.txt +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/LICENSE +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/README.rst +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/Deep_peak_square.ipynb +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/Physics-informed_AI.ipynb +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/ROI_analysis-Copy1.ipynb +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/ROI_analysis.ipynb +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/Untitled.ipynb +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/Untitled1.ipynb +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/Untitled2.ipynb +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/ai_dev2.ipynb +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/best_model.h5 +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/best_model.keras +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/concentration_validation.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/doc/canto_spec.md +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/doc/internship.pdf +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/get_started.md +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/grad_cam_output.png +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/image.png +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/model.png +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/model_example.png +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/output_file.prof +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/scripts/AI_peak_detection.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/scripts/concentration_comparison.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/scripts/create_images.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/scripts/data_analysis.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/scripts/dev_beads_analysis.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/scripts/dev_canto.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/scripts/dev_classifier.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/scripts/dev_shot_noise_check.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/scripts/dev_stats_0.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/scripts/dev_stats_1.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/scripts/dev_stats_2.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/scripts/dev_study_on_ri.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/scripts/mat2csv.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/scripts/profiler.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/developments/test.pdf +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/Makefile +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/examples/extras/README.rst +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/examples/extras/distributions.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/examples/extras/scatterer_distribution.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/examples/noise_sources/README.rst +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/examples/noise_sources/dark_current.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/examples/noise_sources/shot_noise.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/examples/noise_sources/thermal.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/examples/tutorials/README.rst +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/images/distributions/Delta.png +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/images/distributions/LogNormal.png +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/images/distributions/Normal.png +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/images/distributions/RosinRammler.png +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/images/distributions/Uniform.png +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/images/distributions/Weibull.png +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/images/example_0.png +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/images/example_1.png +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/images/example_2.png +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/images/example_3.png +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/images/flow_cytometer.png +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/images/logo.png +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/make.bat +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/_static/default.css +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/_static/logo.png +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/_static/thumbnail.png +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/code/base.rst +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/code/detector.rst +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/code/distributions.rst +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/code/flow_cell.rst +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/code/flow_cytometer.rst +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/code/peak_locator.rst +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/code/scatterer.rst +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/code/source.rst +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/code.rst +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/conf.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/examples.rst +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/index.rst +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/internal/core_components.rst +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/internal/getting_started.rst +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/internal/objectives/main.rst +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/internal/objectives/pre.rst +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/internal/objectives/stretch.rst +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/internal/prerequisites/index.rst +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/internal/prerequisites/mathematics.rst +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/internal/prerequisites/optics.rst +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/internal/prerequisites/programming.rst +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/internal/ressources.rst +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/internal/tasks.rst +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/internal.rst +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/references.rst +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/sg_execution_times.rst +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/docs/source/theory.rst +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/meta.yaml +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/notebook.ipynb +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/pyproject.toml +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/setup.cfg +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/tests/__init__.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/tests/test_detector_noise.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/tests/test_distribution.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/tests/test_noises.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/tests/test_peak_algorithm.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/tests/test_peak_analyzer.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/tests/test_population.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/tests/test_scatterer_distribution.py +0 -0
- {flowcypy-0.7.4 → flowcypy-0.8.1}/tests/test_source.py +0 -0
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 :
|
|
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 :
|
|
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[
|
|
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:
|
|
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
|
-
|
|
210
|
+
dataframe = pd.concat(dataframes, keys=[d.name for d in self.detectors])
|
|
210
211
|
|
|
211
|
-
|
|
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:
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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=
|
|
299
|
+
detector_dataframe=signal_dataframe
|
|
292
300
|
)
|
|
293
301
|
|
|
294
302
|
return experiment
|