celldetective 1.4.2__py3-none-any.whl → 1.5.0b1__py3-none-any.whl

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 (152) hide show
  1. celldetective/__init__.py +25 -0
  2. celldetective/__main__.py +62 -43
  3. celldetective/_version.py +1 -1
  4. celldetective/extra_properties.py +477 -399
  5. celldetective/filters.py +192 -97
  6. celldetective/gui/InitWindow.py +541 -411
  7. celldetective/gui/__init__.py +0 -15
  8. celldetective/gui/about.py +44 -39
  9. celldetective/gui/analyze_block.py +120 -84
  10. celldetective/gui/base/__init__.py +0 -0
  11. celldetective/gui/base/channel_norm_generator.py +335 -0
  12. celldetective/gui/base/components.py +249 -0
  13. celldetective/gui/base/feature_choice.py +92 -0
  14. celldetective/gui/base/figure_canvas.py +52 -0
  15. celldetective/gui/base/list_widget.py +133 -0
  16. celldetective/gui/{styles.py → base/styles.py} +92 -36
  17. celldetective/gui/base/utils.py +33 -0
  18. celldetective/gui/base_annotator.py +900 -767
  19. celldetective/gui/classifier_widget.py +6 -22
  20. celldetective/gui/configure_new_exp.py +777 -671
  21. celldetective/gui/control_panel.py +635 -524
  22. celldetective/gui/dynamic_progress.py +449 -0
  23. celldetective/gui/event_annotator.py +2023 -1662
  24. celldetective/gui/generic_signal_plot.py +1292 -944
  25. celldetective/gui/gui_utils.py +899 -1289
  26. celldetective/gui/interactions_block.py +658 -0
  27. celldetective/gui/interactive_timeseries_viewer.py +447 -0
  28. celldetective/gui/json_readers.py +48 -15
  29. celldetective/gui/layouts/__init__.py +5 -0
  30. celldetective/gui/layouts/background_model_free_layout.py +537 -0
  31. celldetective/gui/layouts/channel_offset_layout.py +134 -0
  32. celldetective/gui/layouts/local_correction_layout.py +91 -0
  33. celldetective/gui/layouts/model_fit_layout.py +372 -0
  34. celldetective/gui/layouts/operation_layout.py +68 -0
  35. celldetective/gui/layouts/protocol_designer_layout.py +96 -0
  36. celldetective/gui/pair_event_annotator.py +3130 -2435
  37. celldetective/gui/plot_measurements.py +586 -267
  38. celldetective/gui/plot_signals_ui.py +724 -506
  39. celldetective/gui/preprocessing_block.py +395 -0
  40. celldetective/gui/process_block.py +1678 -1831
  41. celldetective/gui/seg_model_loader.py +580 -473
  42. celldetective/gui/settings/__init__.py +0 -7
  43. celldetective/gui/settings/_cellpose_model_params.py +181 -0
  44. celldetective/gui/settings/_event_detection_model_params.py +95 -0
  45. celldetective/gui/settings/_segmentation_model_params.py +159 -0
  46. celldetective/gui/settings/_settings_base.py +77 -65
  47. celldetective/gui/settings/_settings_event_model_training.py +752 -526
  48. celldetective/gui/settings/_settings_measurements.py +1133 -964
  49. celldetective/gui/settings/_settings_neighborhood.py +574 -488
  50. celldetective/gui/settings/_settings_segmentation_model_training.py +779 -564
  51. celldetective/gui/settings/_settings_signal_annotator.py +329 -305
  52. celldetective/gui/settings/_settings_tracking.py +1304 -1094
  53. celldetective/gui/settings/_stardist_model_params.py +98 -0
  54. celldetective/gui/survival_ui.py +422 -312
  55. celldetective/gui/tableUI.py +1665 -1701
  56. celldetective/gui/table_ops/_maths.py +295 -0
  57. celldetective/gui/table_ops/_merge_groups.py +140 -0
  58. celldetective/gui/table_ops/_merge_one_hot.py +95 -0
  59. celldetective/gui/table_ops/_query_table.py +43 -0
  60. celldetective/gui/table_ops/_rename_col.py +44 -0
  61. celldetective/gui/thresholds_gui.py +382 -179
  62. celldetective/gui/viewers/__init__.py +0 -0
  63. celldetective/gui/viewers/base_viewer.py +700 -0
  64. celldetective/gui/viewers/channel_offset_viewer.py +331 -0
  65. celldetective/gui/viewers/contour_viewer.py +394 -0
  66. celldetective/gui/viewers/size_viewer.py +153 -0
  67. celldetective/gui/viewers/spot_detection_viewer.py +341 -0
  68. celldetective/gui/viewers/threshold_viewer.py +309 -0
  69. celldetective/gui/workers.py +403 -126
  70. celldetective/log_manager.py +92 -0
  71. celldetective/measure.py +1895 -1478
  72. celldetective/napari/__init__.py +0 -0
  73. celldetective/napari/utils.py +1025 -0
  74. celldetective/neighborhood.py +1914 -1448
  75. celldetective/preprocessing.py +1620 -1220
  76. celldetective/processes/__init__.py +0 -0
  77. celldetective/processes/background_correction.py +271 -0
  78. celldetective/processes/compute_neighborhood.py +894 -0
  79. celldetective/processes/detect_events.py +246 -0
  80. celldetective/processes/downloader.py +137 -0
  81. celldetective/processes/measure_cells.py +565 -0
  82. celldetective/processes/segment_cells.py +760 -0
  83. celldetective/processes/track_cells.py +435 -0
  84. celldetective/processes/train_segmentation_model.py +694 -0
  85. celldetective/processes/train_signal_model.py +265 -0
  86. celldetective/processes/unified_process.py +292 -0
  87. celldetective/regionprops/_regionprops.py +358 -317
  88. celldetective/relative_measurements.py +987 -710
  89. celldetective/scripts/measure_cells.py +313 -212
  90. celldetective/scripts/measure_relative.py +90 -46
  91. celldetective/scripts/segment_cells.py +165 -104
  92. celldetective/scripts/segment_cells_thresholds.py +96 -68
  93. celldetective/scripts/track_cells.py +198 -149
  94. celldetective/scripts/train_segmentation_model.py +324 -201
  95. celldetective/scripts/train_signal_model.py +87 -45
  96. celldetective/segmentation.py +844 -749
  97. celldetective/signals.py +3514 -2861
  98. celldetective/tracking.py +30 -15
  99. celldetective/utils/__init__.py +0 -0
  100. celldetective/utils/cellpose_utils/__init__.py +133 -0
  101. celldetective/utils/color_mappings.py +42 -0
  102. celldetective/utils/data_cleaning.py +630 -0
  103. celldetective/utils/data_loaders.py +450 -0
  104. celldetective/utils/dataset_helpers.py +207 -0
  105. celldetective/utils/downloaders.py +235 -0
  106. celldetective/utils/event_detection/__init__.py +8 -0
  107. celldetective/utils/experiment.py +1782 -0
  108. celldetective/utils/image_augmenters.py +308 -0
  109. celldetective/utils/image_cleaning.py +74 -0
  110. celldetective/utils/image_loaders.py +926 -0
  111. celldetective/utils/image_transforms.py +335 -0
  112. celldetective/utils/io.py +62 -0
  113. celldetective/utils/mask_cleaning.py +348 -0
  114. celldetective/utils/mask_transforms.py +5 -0
  115. celldetective/utils/masks.py +184 -0
  116. celldetective/utils/maths.py +351 -0
  117. celldetective/utils/model_getters.py +325 -0
  118. celldetective/utils/model_loaders.py +296 -0
  119. celldetective/utils/normalization.py +380 -0
  120. celldetective/utils/parsing.py +465 -0
  121. celldetective/utils/plots/__init__.py +0 -0
  122. celldetective/utils/plots/regression.py +53 -0
  123. celldetective/utils/resources.py +34 -0
  124. celldetective/utils/stardist_utils/__init__.py +104 -0
  125. celldetective/utils/stats.py +90 -0
  126. celldetective/utils/types.py +21 -0
  127. {celldetective-1.4.2.dist-info → celldetective-1.5.0b1.dist-info}/METADATA +1 -1
  128. celldetective-1.5.0b1.dist-info/RECORD +187 -0
  129. {celldetective-1.4.2.dist-info → celldetective-1.5.0b1.dist-info}/WHEEL +1 -1
  130. tests/gui/test_new_project.py +129 -117
  131. tests/gui/test_project.py +127 -79
  132. tests/test_filters.py +39 -15
  133. tests/test_notebooks.py +8 -0
  134. tests/test_tracking.py +232 -13
  135. tests/test_utils.py +123 -77
  136. celldetective/gui/base_components.py +0 -23
  137. celldetective/gui/layouts.py +0 -1602
  138. celldetective/gui/processes/compute_neighborhood.py +0 -594
  139. celldetective/gui/processes/downloader.py +0 -111
  140. celldetective/gui/processes/measure_cells.py +0 -360
  141. celldetective/gui/processes/segment_cells.py +0 -499
  142. celldetective/gui/processes/track_cells.py +0 -303
  143. celldetective/gui/processes/train_segmentation_model.py +0 -270
  144. celldetective/gui/processes/train_signal_model.py +0 -108
  145. celldetective/gui/table_ops/merge_groups.py +0 -118
  146. celldetective/gui/viewers.py +0 -1354
  147. celldetective/io.py +0 -3663
  148. celldetective/utils.py +0 -3108
  149. celldetective-1.4.2.dist-info/RECORD +0 -123
  150. {celldetective-1.4.2.dist-info → celldetective-1.5.0b1.dist-info}/entry_points.txt +0 -0
  151. {celldetective-1.4.2.dist-info → celldetective-1.5.0b1.dist-info}/licenses/LICENSE +0 -0
  152. {celldetective-1.4.2.dist-info → celldetective-1.5.0b1.dist-info}/top_level.txt +0 -0
@@ -1,329 +1,439 @@
1
- from PyQt5.QtWidgets import QComboBox, QLineEdit, QVBoxLayout, QLabel, QHBoxLayout, QPushButton
1
+ from PyQt5.QtWidgets import (
2
+ QComboBox,
3
+ QLineEdit,
4
+ QVBoxLayout,
5
+ QLabel,
6
+ QHBoxLayout,
7
+ QPushButton,
8
+ )
2
9
  from PyQt5.QtCore import Qt
3
10
  from PyQt5.QtGui import QDoubleValidator
4
- from celldetective.gui.gui_utils import center_window, generic_message
11
+ from celldetective.gui.base.components import generic_message
12
+ from celldetective.gui.base.utils import center_window
5
13
  from superqt import QColormapComboBox
6
14
  from celldetective.gui.generic_signal_plot import SurvivalPlotWidget
7
- from celldetective.utils import get_software_location, _extract_labels_from_config, extract_cols_from_table_list
8
- from celldetective.io import load_experiment_tables
15
+ from celldetective import (
16
+ get_software_location,
17
+ )
18
+ from celldetective.utils.data_cleaning import extract_cols_from_table_list
19
+ from celldetective.utils.parsing import _extract_labels_from_config
20
+ from celldetective.utils.data_loaders import load_experiment_tables
9
21
  import numpy as np
10
22
  import os
11
23
  import matplotlib.pyplot as plt
12
- plt.rcParams['svg.fonttype'] = 'none'
24
+
25
+ plt.rcParams["svg.fonttype"] = "none"
13
26
  from glob import glob
14
- from celldetective.gui import Styles, CelldetectiveWidget
27
+ from celldetective.gui.base.styles import Styles
28
+ from celldetective.gui.base.components import CelldetectiveWidget
15
29
  from matplotlib import colormaps
16
30
  from celldetective.events import compute_survival
17
31
  from celldetective.relative_measurements import expand_pair_table
18
32
  import matplotlib.cm
19
33
  from celldetective.neighborhood import extract_neighborhood_in_pair_table
20
34
 
35
+
21
36
  class ConfigSurvival(CelldetectiveWidget):
22
-
23
- """
24
- UI to set survival instructions.
25
-
26
- """
27
-
28
- def __init__(self, parent_window=None):
29
-
30
- super().__init__()
31
- self.parent_window = parent_window
32
- self.setWindowTitle("Configure survival")
33
-
34
- self.exp_dir = self.parent_window.exp_dir
35
- self.soft_path = get_software_location()
36
- self.exp_config = self.exp_dir +"config.ini"
37
- self.wells = np.array(self.parent_window.parent_window.wells,dtype=str)
38
- self.well_labels = _extract_labels_from_config(self.exp_config,len(self.wells))
39
- self.FrameToMin = self.parent_window.parent_window.FrameToMin
40
- self.float_validator = QDoubleValidator()
41
- self.auto_close = False
42
-
43
- self.well_option = self.parent_window.parent_window.well_list.getSelectedIndices()
44
- self.position_option = self.parent_window.parent_window.position_list.getSelectedIndices()
45
- self.interpret_pos_location()
46
- #self.config_path = self.exp_dir + self.config_name
47
-
48
- self.screen_height = self.parent_window.parent_window.parent_window.screen_height
49
- center_window(self)
50
-
51
- self.setMinimumWidth(350)
52
- self.populate_widget()
53
- if self.auto_close:
54
- self.close()
55
-
56
- def interpret_pos_location(self):
57
-
58
- """
59
- Read the well/position selection from the control panel to decide which data to load
60
- Set position_indices to None if all positions must be taken
61
-
62
- """
63
-
64
- self.well_indices = self.parent_window.parent_window.well_list.getSelectedIndices()
65
- self.position_indices = self.parent_window.parent_window.position_list.getSelectedIndices()
66
- if self.position_indices==[]:
67
- self.position_indices = None
68
-
69
-
70
- def populate_widget(self):
71
-
72
- """
73
- Create the multibox design.
74
-
75
- """
76
-
77
- # Create button widget and layout
78
- main_layout = QVBoxLayout()
79
- self.setLayout(main_layout)
80
- main_layout.setContentsMargins(30,30,30,30)
81
-
82
- panel_title = QLabel('Options')
83
- panel_title.setStyleSheet("""
37
+ """
38
+ UI to set survival instructions.
39
+
40
+ """
41
+
42
+ def __init__(self, parent_window=None):
43
+
44
+ super().__init__()
45
+ self.parent_window = parent_window
46
+ self.setWindowTitle("Configure survival")
47
+
48
+ self.exp_dir = self.parent_window.exp_dir
49
+ self.soft_path = get_software_location()
50
+ self.exp_config = self.exp_dir + "config.ini"
51
+ self.wells = np.array(self.parent_window.parent_window.wells, dtype=str)
52
+ self.well_labels = _extract_labels_from_config(self.exp_config, len(self.wells))
53
+ self.FrameToMin = self.parent_window.parent_window.FrameToMin
54
+ self.float_validator = QDoubleValidator()
55
+ self.auto_close = False
56
+
57
+ self.well_option = (
58
+ self.parent_window.parent_window.well_list.getSelectedIndices()
59
+ )
60
+ self.position_option = (
61
+ self.parent_window.parent_window.position_list.getSelectedIndices()
62
+ )
63
+ self.interpret_pos_location()
64
+ # self.config_path = self.exp_dir + self.config_name
65
+
66
+ self.screen_height = (
67
+ self.parent_window.parent_window.parent_window.screen_height
68
+ )
69
+
70
+ self.setMinimumWidth(350)
71
+ self.populate_widget()
72
+ if self.auto_close:
73
+ self.close()
74
+
75
+ def interpret_pos_location(self):
76
+ """
77
+ Read the well/position selection from the control panel to decide which data to load
78
+ Set position_indices to None if all positions must be taken
79
+
80
+ """
81
+
82
+ self.well_indices = (
83
+ self.parent_window.parent_window.well_list.getSelectedIndices()
84
+ )
85
+ self.position_indices = (
86
+ self.parent_window.parent_window.position_list.getSelectedIndices()
87
+ )
88
+ if self.position_indices == []:
89
+ self.position_indices = None
90
+
91
+ def populate_widget(self):
92
+ """
93
+ Create the multibox design.
94
+
95
+ """
96
+
97
+ # Create button widget and layout
98
+ main_layout = QVBoxLayout()
99
+ self.setLayout(main_layout)
100
+ main_layout.setContentsMargins(30, 30, 30, 30)
101
+
102
+ panel_title = QLabel("Options")
103
+ panel_title.setStyleSheet(
104
+ """
84
105
  font-weight: bold;
85
106
  padding: 0px;
86
- """)
87
- main_layout.addWidget(panel_title, alignment=Qt.AlignCenter)
88
-
89
-
90
- pops = []
91
- self.cols_per_pop = {}
92
- for population in self.parent_window.parent_window.populations:
93
- tables = glob(self.exp_dir+os.sep.join(['W*','*','output','tables',f'trajectories_{population}.csv']))
94
- if len(tables)>0:
95
- pops.append(population)
96
- cols = extract_cols_from_table_list(tables)
97
-
98
- # check for neighbor pairs
99
- neigh_cols = [c for c in cols if c.startswith('inclusive_count_neighborhood')]
100
- neigh_pairs = [c.split('_(')[-1].split(')_')[0].split('-') for c in neigh_cols]
101
- neigh_pairs = ['-'.join(c) for c in neigh_pairs]
102
- for k in range(len(neigh_pairs)):
103
- if "_self_" in neigh_pairs[k]:
104
- neigh_pairs[k] = '-'.join([population, population])
105
- pops.extend(neigh_pairs)
106
-
107
- self.cols_per_pop.update({population: cols})
108
-
109
- labels = [QLabel('population: '), QLabel('time of\nreference: '), QLabel('time of\ninterest: '), QLabel('cmap: ')] #QLabel('class: '),
110
- self.cb_options = [pops, ['0'], [], []] #['class'],
111
- self.cbs = [QComboBox() for _ in range(len(labels))]
112
-
113
- self.cbs[-1] = QColormapComboBox()
114
- self.cbs[0].currentIndexChanged.connect(self.set_classes_and_times)
115
-
116
- choice_layout = QVBoxLayout()
117
- choice_layout.setContentsMargins(20,20,20,20)
118
- for i in range(len(labels)):
119
- hbox = QHBoxLayout()
120
- hbox.addWidget(labels[i], 33)
121
- hbox.addWidget(self.cbs[i],66)
122
- if i < len(labels)-1:
123
- self.cbs[i].addItems(self.cb_options[i])
124
- choice_layout.addLayout(hbox)
125
-
126
- all_cms = list(colormaps)
127
- for cm in all_cms:
128
- if hasattr(matplotlib.cm, str(cm).lower()):
129
- try:
130
- self.cbs[-1].addColormap(cm.lower())
131
- except Exception as _:
132
- pass
133
-
134
- main_layout.addLayout(choice_layout)
135
-
136
- select_layout = QHBoxLayout()
137
- select_layout.addWidget(QLabel('select cells\nwith query: '), 33)
138
- self.query_le = QLineEdit()
139
- select_layout.addWidget(self.query_le, 66)
140
- main_layout.addLayout(select_layout)
141
-
142
- time_cut_layout = QHBoxLayout()
143
- cut_time_lbl = QLabel('cut obs.\ntime [min]: ')
144
- cut_time_lbl.setToolTip('Filter out later events from\nthe analysis (in absolute time).')
145
- time_cut_layout.addWidget(cut_time_lbl, 33)
146
- self.query_time_cut = QLineEdit()
147
- self.query_time_cut.setValidator(self.float_validator)
148
- time_cut_layout.addWidget(self.query_time_cut, 66)
149
- main_layout.addLayout(time_cut_layout)
150
-
151
- self.set_classes_and_times()
152
- self.cbs[1].setCurrentText('t_firstdetection')
153
-
154
- time_calib_layout = QHBoxLayout()
155
- time_calib_layout.setContentsMargins(20,20,20,20)
156
- time_calib_layout.addWidget(QLabel('time calibration\n(frame to min)'), 33)
157
- self.time_calibration_le = QLineEdit(str(self.FrameToMin).replace('.',','))
158
- self.time_calibration_le.setValidator(self.float_validator)
159
- time_calib_layout.addWidget(self.time_calibration_le, 66)
160
- #time_calib_layout.addWidget(QLabel(' min'))
161
- main_layout.addLayout(time_calib_layout)
162
-
163
- self.submit_btn = QPushButton('Submit')
164
- self.submit_btn.setStyleSheet(self.button_style_sheet)
165
- self.submit_btn.clicked.connect(self.process_survival)
166
- main_layout.addWidget(self.submit_btn)
167
-
168
- #self.populate_left_panel()
169
- #grid.addLayout(self.left_side, 0, 0, 1, 1)
170
-
171
- # self.setCentralWidget(self.scroll_area)
172
- # self.show()
173
-
174
- def set_classes_and_times(self):
175
-
176
- # Look for all classes and times
177
- self.neighborhood_keys = None
178
- self.population = self.cbs[0].currentText()
179
- pop_split = self.population.split('-')
180
-
181
- if len(pop_split)==2:
182
-
183
- self.population = 'pairs'
184
- tables_pairs = glob(self.exp_dir+os.sep.join(['W*','*','output','tables',f'trajectories_pairs.csv']))
185
- if not tables_pairs:
186
- print('No pair table found... please compute the pair measurements...')
187
- return None
188
- self.cols_pairs = extract_cols_from_table_list(tables_pairs)
189
-
190
- self.population_reference = pop_split[0]
191
- self.population_neigh = pop_split[1]
192
-
193
- cols_ref = self.cols_per_pop[self.population_reference]
194
- cols_neigh = self.cols_per_pop[self.population_neigh]
195
-
196
- time_cols_ref = np.array([s.startswith('t_') or s=='t0' for s in cols_ref])
197
- if len(time_cols_ref)>0:
198
- time_cols_ref = list(cols_ref[time_cols_ref])
199
- time_cols_ref = ['reference_'+t for t in time_cols_ref]
200
-
201
- time_cols_neigh = np.array([s.startswith('t_') or s=='t0' for s in cols_neigh])
202
- if len(time_cols_neigh)>0:
203
- time_cols_neigh = list(cols_neigh[time_cols_neigh])
204
- time_cols_neigh = ['neighbor_'+t for t in time_cols_neigh]
205
-
206
- if self.population_reference!=self.population_neigh:
207
- self.neighborhood_keys = [c[16:] for c in cols_ref if c.startswith('inclusive_count_neighborhood') and str(self.population_neigh) in c]
208
- else:
209
- self.neighborhood_keys = [c[16:] for c in cols_ref if c.startswith('inclusive_count_neighborhood') and str(self.population_neigh) not in c]
210
-
211
- print(f"{self.neighborhood_keys=}")
212
-
213
- time_idx = np.array([s.startswith('t_') or s.startswith('t0') for s in self.cols_pairs])
214
- time_cols_pairs = list(self.cols_pairs[time_idx])
215
-
216
- time_columns = time_cols_ref + time_cols_neigh + time_cols_pairs
217
-
218
- else:
219
- self.all_columns = self.cols_per_pop[self.population]
220
- time_idx = np.array([s.startswith('t_') or s=='t0' for s in self.all_columns])
221
-
222
- try:
223
- time_columns = list(self.all_columns[time_idx])
224
- except:
225
- print('no column starts with t')
226
- self.auto_close = True
227
- return None
228
-
229
- self.cbs[1].clear()
230
- self.cbs[1].addItems(np.unique(self.cb_options[1]+time_columns))
231
- self.cbs[1].setCurrentText('t_firstdetection')
232
-
233
- self.cbs[2].clear()
234
- self.cbs[2].addItems(np.unique(self.cb_options[2]+time_columns))
235
-
236
-
237
- def process_survival(self):
238
-
239
- self.FrameToMin = float(self.time_calibration_le.text().replace(',','.'))
240
- self.time_of_interest = self.cbs[2].currentText()
241
- if self.time_of_interest=="t0":
242
- self.class_of_interest = "class"
243
- elif self.time_of_interest.startswith('t0'):
244
- self.class_of_interest = self.time_of_interest.replace('t0_','class_')
245
- else:
246
- self.class_of_interest = self.time_of_interest.replace('t_','class_')
247
-
248
-
249
- # read instructions from combobox options
250
- self.load_available_tables_local()
251
- if self.df is not None:
252
-
253
- try:
254
- query_text = self.query_le.text()
255
- if query_text != '':
256
- self.df = self.df.query(query_text)
257
- except Exception as e:
258
- print(e, ' The query is misunderstood and will not be applied...')
259
-
260
- self.interpret_pos_location()
261
-
262
- if self.class_of_interest in list(self.df.columns) and self.cbs[2].currentText() in list(self.df.columns):
263
- self.compute_survival_functions()
264
- else:
265
- generic_message("The class and/or event time of interest is not found in the dataframe...")
266
- return None
267
-
268
- if 'survival_fit' in list(self.df_pos_info.columns):
269
- self.plot_window = SurvivalPlotWidget(parent_window=self, df=self.df, df_pos_info = self.df_pos_info, df_well_info = self.df_well_info, title='plot survivals')
270
- self.plot_window.show()
271
- else:
272
- generic_message("No survival function was successfully computed...\nCheck your parameter choice.")
273
- return None
274
-
275
- def load_available_tables_local(self):
276
-
277
- """
278
- Load the tables of the selected wells/positions from the control Panel for the population of interest
279
-
280
- """
281
-
282
- self.well_option = self.parent_window.parent_window.well_list.getSelectedIndices()
283
- self.position_option = self.parent_window.parent_window.position_list.getSelectedIndices()
284
-
285
- self.df, self.df_pos_info = load_experiment_tables(self.exp_dir, well_option=self.well_option, position_option=self.position_option, population=self.population, return_pos_info=True)
286
-
287
- if self.df is None:
288
- generic_message("No table could be found.. Abort.")
289
- return None
290
- else:
291
- self.df_well_info = self.df_pos_info.loc[:,['well_path', 'well_index', 'well_name', 'well_number', 'well_alias']].drop_duplicates()
292
-
293
- if self.population=='pairs':
294
- self.df = expand_pair_table(self.df)
295
- self.df = extract_neighborhood_in_pair_table(self.df, reference_population=self.population_reference, neighbor_population=self.population_neigh, neighborhood_key=self.neighborhood_keys[0], contact_only=True)
296
-
297
- def compute_survival_functions(self):
298
-
299
- cut_observation_time = None
300
- try:
301
- if self.query_time_cut.text()!='':
302
- cut_observation_time = float(self.query_time_cut.text().replace(',','.')) / self.FrameToMin
303
- if not 0<cut_observation_time<=(self.df['FRAME'].max()):
304
- print('Invalid cut time (larger than movie length)... Not applied.')
305
- cut_observation_time = None
306
- except Exception as e:
307
- print(f"{e=}")
308
- pass
309
-
310
- pairs = False
311
- if self.neighborhood_keys is not None:
312
- pairs = True
313
-
314
- # Per position survival
315
- for block,movie_group in self.df.groupby(['well','position']):
316
- print(f"{block=}")
317
- ks = compute_survival(movie_group, self.class_of_interest, self.cbs[2].currentText(), t_reference=self.cbs[1].currentText(), FrameToMin=self.FrameToMin, cut_observation_time=cut_observation_time, pairs=pairs)
318
- print(f"{ks=}")
319
- if ks is not None:
320
- self.df_pos_info.loc[self.df_pos_info['pos_path']==block[1],'survival_fit'] = ks
321
-
322
- # Per well survival
323
- for well,well_group in self.df.groupby('well'):
324
- ks = compute_survival(well_group, self.class_of_interest, self.cbs[2].currentText(), t_reference=self.cbs[1].currentText(), FrameToMin=self.FrameToMin, cut_observation_time=cut_observation_time, pairs=pairs)
325
- if ks is not None:
326
- self.df_well_info.loc[self.df_well_info['well_path']==well,'survival_fit'] = ks
327
-
328
- self.df_pos_info.loc[:,'select'] = True
329
- self.df_well_info.loc[:,'select'] = True
107
+ """
108
+ )
109
+ main_layout.addWidget(panel_title, alignment=Qt.AlignCenter)
110
+
111
+ pops = []
112
+ self.cols_per_pop = {}
113
+ for population in self.parent_window.parent_window.populations:
114
+ tables = glob(
115
+ self.exp_dir
116
+ + os.sep.join(
117
+ ["W*", "*", "output", "tables", f"trajectories_{population}.csv"]
118
+ )
119
+ )
120
+ if len(tables) > 0:
121
+ pops.append(population)
122
+ cols = extract_cols_from_table_list(tables)
123
+
124
+ # check for neighbor pairs
125
+ neigh_cols = [
126
+ c for c in cols if c.startswith("inclusive_count_neighborhood")
127
+ ]
128
+ neigh_pairs = [
129
+ c.split("_(")[-1].split(")_")[0].split("-") for c in neigh_cols
130
+ ]
131
+ neigh_pairs = ["-".join(c) for c in neigh_pairs]
132
+ for k in range(len(neigh_pairs)):
133
+ if "_self_" in neigh_pairs[k]:
134
+ neigh_pairs[k] = "-".join([population, population])
135
+ pops.extend(neigh_pairs)
136
+
137
+ self.cols_per_pop.update({population: cols})
138
+
139
+ labels = [
140
+ QLabel("population: "),
141
+ QLabel("time of\nreference: "),
142
+ QLabel("time of\ninterest: "),
143
+ QLabel("cmap: "),
144
+ ] # QLabel('class: '),
145
+ self.cb_options = [pops, ["0"], [], []] # ['class'],
146
+ self.cbs = [QComboBox() for _ in range(len(labels))]
147
+
148
+ self.cbs[-1] = QColormapComboBox()
149
+ self.cbs[0].currentIndexChanged.connect(self.set_classes_and_times)
150
+
151
+ choice_layout = QVBoxLayout()
152
+ choice_layout.setContentsMargins(20, 20, 20, 20)
153
+ for i in range(len(labels)):
154
+ hbox = QHBoxLayout()
155
+ hbox.addWidget(labels[i], 33)
156
+ hbox.addWidget(self.cbs[i], 66)
157
+ if i < len(labels) - 1:
158
+ self.cbs[i].addItems(self.cb_options[i])
159
+ choice_layout.addLayout(hbox)
160
+
161
+ all_cms = list(colormaps)
162
+ for cm in all_cms:
163
+ if hasattr(matplotlib.cm, str(cm).lower()):
164
+ try:
165
+ self.cbs[-1].addColormap(cm.lower())
166
+ except Exception as _:
167
+ pass
168
+
169
+ main_layout.addLayout(choice_layout)
170
+
171
+ select_layout = QHBoxLayout()
172
+ select_layout.addWidget(QLabel("select cells\nwith query: "), 33)
173
+ self.query_le = QLineEdit()
174
+ select_layout.addWidget(self.query_le, 66)
175
+ main_layout.addLayout(select_layout)
176
+
177
+ time_cut_layout = QHBoxLayout()
178
+ cut_time_lbl = QLabel("cut obs.\ntime [min]: ")
179
+ cut_time_lbl.setToolTip(
180
+ "Filter out later events from\nthe analysis (in absolute time)."
181
+ )
182
+ time_cut_layout.addWidget(cut_time_lbl, 33)
183
+ self.query_time_cut = QLineEdit()
184
+ self.query_time_cut.setValidator(self.float_validator)
185
+ time_cut_layout.addWidget(self.query_time_cut, 66)
186
+ main_layout.addLayout(time_cut_layout)
187
+
188
+ self.set_classes_and_times()
189
+ self.cbs[1].setCurrentText("t_firstdetection")
190
+
191
+ time_calib_layout = QHBoxLayout()
192
+ time_calib_layout.setContentsMargins(20, 20, 20, 20)
193
+ time_calib_layout.addWidget(QLabel("time calibration\n(frame to min)"), 33)
194
+ self.time_calibration_le = QLineEdit(str(self.FrameToMin).replace(".", ","))
195
+ self.time_calibration_le.setValidator(self.float_validator)
196
+ time_calib_layout.addWidget(self.time_calibration_le, 66)
197
+ # time_calib_layout.addWidget(QLabel(' min'))
198
+ main_layout.addLayout(time_calib_layout)
199
+
200
+ self.submit_btn = QPushButton("Submit")
201
+ self.submit_btn.setStyleSheet(self.button_style_sheet)
202
+ self.submit_btn.clicked.connect(self.process_survival)
203
+ main_layout.addWidget(self.submit_btn)
204
+
205
+ # self.populate_left_panel()
206
+ # grid.addLayout(self.left_side, 0, 0, 1, 1)
207
+
208
+ # self.setCentralWidget(self.scroll_area)
209
+ # self.show()
210
+
211
+ def set_classes_and_times(self):
212
+
213
+ # Look for all classes and times
214
+ self.neighborhood_keys = None
215
+ self.population = self.cbs[0].currentText()
216
+ pop_split = self.population.split("-")
217
+
218
+ if len(pop_split) == 2:
219
+
220
+ self.population = "pairs"
221
+ tables_pairs = glob(
222
+ self.exp_dir
223
+ + os.sep.join(
224
+ ["W*", "*", "output", "tables", f"trajectories_pairs.csv"]
225
+ )
226
+ )
227
+ if not tables_pairs:
228
+ print("No pair table found... please compute the pair measurements...")
229
+ return None
230
+ self.cols_pairs = extract_cols_from_table_list(tables_pairs)
231
+
232
+ self.population_reference = pop_split[0]
233
+ self.population_neigh = pop_split[1]
234
+
235
+ cols_ref = self.cols_per_pop[self.population_reference]
236
+ cols_neigh = self.cols_per_pop[self.population_neigh]
237
+
238
+ time_cols_ref = np.array(
239
+ [s.startswith("t_") or s == "t0" for s in cols_ref]
240
+ )
241
+ if len(time_cols_ref) > 0:
242
+ time_cols_ref = list(cols_ref[time_cols_ref])
243
+ time_cols_ref = ["reference_" + t for t in time_cols_ref]
244
+
245
+ time_cols_neigh = np.array(
246
+ [s.startswith("t_") or s == "t0" for s in cols_neigh]
247
+ )
248
+ if len(time_cols_neigh) > 0:
249
+ time_cols_neigh = list(cols_neigh[time_cols_neigh])
250
+ time_cols_neigh = ["neighbor_" + t for t in time_cols_neigh]
251
+
252
+ if self.population_reference != self.population_neigh:
253
+ self.neighborhood_keys = [
254
+ c[16:]
255
+ for c in cols_ref
256
+ if c.startswith("inclusive_count_neighborhood")
257
+ and str(self.population_neigh) in c
258
+ ]
259
+ else:
260
+ self.neighborhood_keys = [
261
+ c[16:]
262
+ for c in cols_ref
263
+ if c.startswith("inclusive_count_neighborhood")
264
+ and str(self.population_neigh) not in c
265
+ ]
266
+
267
+ print(f"{self.neighborhood_keys=}")
268
+
269
+ time_idx = np.array(
270
+ [s.startswith("t_") or s.startswith("t0") for s in self.cols_pairs]
271
+ )
272
+ time_cols_pairs = list(self.cols_pairs[time_idx])
273
+
274
+ time_columns = time_cols_ref + time_cols_neigh + time_cols_pairs
275
+
276
+ else:
277
+ self.all_columns = self.cols_per_pop[self.population]
278
+ time_idx = np.array(
279
+ [s.startswith("t_") or s == "t0" for s in self.all_columns]
280
+ )
281
+
282
+ try:
283
+ time_columns = list(self.all_columns[time_idx])
284
+ except:
285
+ print("no column starts with t")
286
+ self.auto_close = True
287
+ return None
288
+
289
+ self.cbs[1].clear()
290
+ self.cbs[1].addItems(np.unique(self.cb_options[1] + time_columns))
291
+ self.cbs[1].setCurrentText("t_firstdetection")
292
+
293
+ self.cbs[2].clear()
294
+ self.cbs[2].addItems(np.unique(self.cb_options[2] + time_columns))
295
+
296
+ def process_survival(self):
297
+
298
+ self.FrameToMin = float(self.time_calibration_le.text().replace(",", "."))
299
+ self.time_of_interest = self.cbs[2].currentText()
300
+ if self.time_of_interest == "t0":
301
+ self.class_of_interest = "class"
302
+ elif self.time_of_interest.startswith("t0"):
303
+ self.class_of_interest = self.time_of_interest.replace("t0_", "class_")
304
+ else:
305
+ self.class_of_interest = self.time_of_interest.replace("t_", "class_")
306
+
307
+ # read instructions from combobox options
308
+ self.load_available_tables_local()
309
+ if self.df is not None:
310
+
311
+ try:
312
+ query_text = self.query_le.text()
313
+ if query_text != "":
314
+ self.df = self.df.query(query_text)
315
+ except Exception as e:
316
+ print(e, " The query is misunderstood and will not be applied...")
317
+
318
+ self.interpret_pos_location()
319
+
320
+ if self.class_of_interest in list(self.df.columns) and self.cbs[
321
+ 2
322
+ ].currentText() in list(self.df.columns):
323
+ self.compute_survival_functions()
324
+ else:
325
+ generic_message(
326
+ "The class and/or event time of interest is not found in the dataframe..."
327
+ )
328
+ return None
329
+
330
+ if "survival_fit" in list(self.df_pos_info.columns):
331
+ self.plot_window = SurvivalPlotWidget(
332
+ parent_window=self,
333
+ df=self.df,
334
+ df_pos_info=self.df_pos_info,
335
+ df_well_info=self.df_well_info,
336
+ title="plot survivals",
337
+ )
338
+ self.plot_window.show()
339
+ else:
340
+ generic_message(
341
+ "No survival function was successfully computed...\nCheck your parameter choice."
342
+ )
343
+ return None
344
+
345
+ def load_available_tables_local(self):
346
+ """
347
+ Load the tables of the selected wells/positions from the control Panel for the population of interest
348
+
349
+ """
350
+
351
+ self.well_option = (
352
+ self.parent_window.parent_window.well_list.getSelectedIndices()
353
+ )
354
+ self.position_option = (
355
+ self.parent_window.parent_window.position_list.getSelectedIndices()
356
+ )
357
+
358
+ self.df, self.df_pos_info = load_experiment_tables(
359
+ self.exp_dir,
360
+ well_option=self.well_option,
361
+ position_option=self.position_option,
362
+ population=self.population,
363
+ return_pos_info=True,
364
+ )
365
+
366
+ if self.df is None:
367
+ generic_message("No table could be found.. Abort.")
368
+ return None
369
+ else:
370
+ self.df_well_info = self.df_pos_info.loc[
371
+ :, ["well_path", "well_index", "well_name", "well_number", "well_alias"]
372
+ ].drop_duplicates()
373
+
374
+ if self.population == "pairs":
375
+ self.df = expand_pair_table(self.df)
376
+ self.df = extract_neighborhood_in_pair_table(
377
+ self.df,
378
+ reference_population=self.population_reference,
379
+ neighbor_population=self.population_neigh,
380
+ neighborhood_key=self.neighborhood_keys[0],
381
+ contact_only=True,
382
+ )
383
+
384
+ def compute_survival_functions(self):
385
+
386
+ cut_observation_time = None
387
+ try:
388
+ if self.query_time_cut.text() != "":
389
+ cut_observation_time = (
390
+ float(self.query_time_cut.text().replace(",", "."))
391
+ / self.FrameToMin
392
+ )
393
+ if not 0 < cut_observation_time <= (self.df["FRAME"].max()):
394
+ print("Invalid cut time (larger than movie length)... Not applied.")
395
+ cut_observation_time = None
396
+ except Exception as e:
397
+ print(f"{e=}")
398
+ pass
399
+
400
+ pairs = False
401
+ if self.neighborhood_keys is not None:
402
+ pairs = True
403
+
404
+ # Per position survival
405
+ for block, movie_group in self.df.groupby(["well", "position"]):
406
+ print(f"{block=}")
407
+ ks = compute_survival(
408
+ movie_group,
409
+ self.class_of_interest,
410
+ self.cbs[2].currentText(),
411
+ t_reference=self.cbs[1].currentText(),
412
+ FrameToMin=self.FrameToMin,
413
+ cut_observation_time=cut_observation_time,
414
+ pairs=pairs,
415
+ )
416
+ print(f"{ks=}")
417
+ if ks is not None:
418
+ self.df_pos_info.loc[
419
+ self.df_pos_info["pos_path"] == block[1], "survival_fit"
420
+ ] = ks
421
+
422
+ # Per well survival
423
+ for well, well_group in self.df.groupby("well"):
424
+ ks = compute_survival(
425
+ well_group,
426
+ self.class_of_interest,
427
+ self.cbs[2].currentText(),
428
+ t_reference=self.cbs[1].currentText(),
429
+ FrameToMin=self.FrameToMin,
430
+ cut_observation_time=cut_observation_time,
431
+ pairs=pairs,
432
+ )
433
+ if ks is not None:
434
+ self.df_well_info.loc[
435
+ self.df_well_info["well_path"] == well, "survival_fit"
436
+ ] = ks
437
+
438
+ self.df_pos_info.loc[:, "select"] = True
439
+ self.df_well_info.loc[:, "select"] = True