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,12 +1,23 @@
1
- from PyQt5.QtWidgets import QMessageBox,QGridLayout, QButtonGroup, \
2
- QCheckBox, QLineEdit, QVBoxLayout, QLabel, QHBoxLayout, QPushButton, \
3
- QRadioButton
1
+ from PyQt5.QtWidgets import (
2
+ QMessageBox,
3
+ QGridLayout,
4
+ QButtonGroup,
5
+ QCheckBox,
6
+ QLineEdit,
7
+ QVBoxLayout,
8
+ QLabel,
9
+ QHBoxLayout,
10
+ QPushButton,
11
+ QRadioButton,
12
+ )
4
13
  from PyQt5.QtCore import Qt, QSize
5
14
  from PyQt5.QtGui import QDoubleValidator
6
15
 
7
- from celldetective.gui.gui_utils import center_window, FigureCanvas, ExportPlotBtn, QuickSliderLayout
16
+ from celldetective.gui.gui_utils import ExportPlotBtn, QuickSliderLayout
17
+ from celldetective.gui.base.figure_canvas import FigureCanvas
18
+ from celldetective.gui.base.utils import center_window
8
19
  from celldetective.gui.tableUI import TableUI
9
- from celldetective.io import collect_experiment_metadata
20
+ from celldetective.utils.experiment import collect_experiment_metadata
10
21
 
11
22
  from superqt.fonticon import icon
12
23
  from superqt import QLabeledSlider, QLabeledDoubleSlider
@@ -15,954 +26,1291 @@ import numpy as np
15
26
  import json
16
27
  import os
17
28
  import matplotlib.pyplot as plt
18
- plt.rcParams['svg.fonttype'] = 'none'
29
+
30
+ plt.rcParams["svg.fonttype"] = "none"
19
31
  from glob import glob
20
32
  from matplotlib.cm import tab10
21
- from celldetective.gui import CelldetectiveWidget
33
+ from celldetective.gui.base.components import CelldetectiveWidget
22
34
  import matplotlib.cm as mcm
23
35
  import pandas as pd
24
36
 
25
37
  from lifelines.utils import qth_survival_times
26
38
 
39
+
27
40
  class GenericSignalPlotWidget(CelldetectiveWidget):
28
41
 
29
- def __init__(self, df = None, df_pos_info = None, df_well_info = None, feature_selected = None, parent_window=None, title='plot', *args, **kwargs):
30
-
31
- super().__init__()
32
- center_window(self)
33
-
34
- self.parent_window = parent_window
35
- self.setWindowTitle(title)
36
-
37
- self.show_ci = False
38
- self.legend_visible = True
39
- self.show_cell_lines = False
40
- self.alpha_setting = 0.5
41
- self.scaling_factor = 1
42
- self.target_class = [0]
43
- self.float_validator = QDoubleValidator()
44
-
45
- cmap_lbl = self.parent_window.cbs[-1].currentText()
46
- self.cmap = getattr(mcm, cmap_lbl)
47
-
48
- self.feature_selected = feature_selected
49
- self.df = df
50
- self.df_pos_info = df_pos_info
51
- self.df_well_info = df_well_info
52
-
53
- self.layout = QVBoxLayout()
54
- self.layout.setSpacing(3)
55
- self.populate_widget()
56
-
57
- self.ci_btn.click()
58
- #self.legend_btn.click()
59
-
60
- self.fig.tight_layout()
61
- self.setLayout(self.layout)
62
-
63
- def populate_widget(self):
64
-
65
- self.plot_options = [QRadioButton() for i in range(3)]
66
- self.radio_labels = ['well', 'position', 'both']
67
- radio_hbox = QHBoxLayout()
68
- radio_hbox.setContentsMargins(15,15,15,0)
69
-
70
- self.group_lbl = QLabel('grouping: ')
71
- radio_hbox.addWidget(self.group_lbl, 25)
72
- radio_subhbox = QHBoxLayout()
73
- radio_hbox.addLayout(radio_subhbox, 75)
74
-
75
- self.plot_btn_group = QButtonGroup()
76
- for i in range(3):
77
- self.plot_options[i].setText(self.radio_labels[i])
78
- self.plot_btn_group.addButton(self.plot_options[i])
79
- radio_subhbox.addWidget(self.plot_options[i], 33, alignment=Qt.AlignCenter)
80
-
81
- if self.parent_window.position_indices is not None:
82
- # at least a position is selected
83
- if len(self.parent_window.well_indices)>1 and len(self.parent_window.position_indices)==1:
84
- # several wells but one position
85
- self.plot_btn_group.buttons()[0].click()
86
- # for i in [1,2]:
87
- # self.plot_options[i].setEnabled(False)
88
- elif len(self.parent_window.well_indices)>1:
89
- self.plot_btn_group.buttons()[0].click()
90
- elif len(self.parent_window.well_indices)==1 and len(self.parent_window.position_indices)==1:
91
- self.plot_btn_group.buttons()[1].click()
92
- for i in [0,2]:
93
- self.plot_options[i].setEnabled(False)
94
- elif len(self.parent_window.well_indices)==1 and len(self.parent_window.position_indices)>1:
95
- # one well, several positions
96
- self.plot_btn_group.buttons()[2].click()
97
- else:
98
- if len(self.parent_window.well_indices)>1:
99
- self.plot_btn_group.buttons()[0].click()
100
- elif len(self.parent_window.well_indices)==1:
101
- self.plot_btn_group.buttons()[2].click()
102
-
103
- self.layout.addLayout(radio_hbox)
104
-
105
-
106
- plot_buttons_hbox = QHBoxLayout()
107
- plot_buttons_hbox.setContentsMargins(10,10,5,0)
108
- plot_buttons_hbox.addWidget(QLabel(''),80, alignment=Qt.AlignLeft)
109
-
110
- self.legend_btn = QPushButton('')
111
- self.legend_btn.setIcon(icon(MDI6.text_box,color=self.help_color))
112
- self.legend_btn.setStyleSheet(self.button_select_all)
113
- self.legend_btn.setToolTip('Show or hide the legend')
114
- self.legend_btn.setIconSize(QSize(20, 20))
115
- plot_buttons_hbox.addWidget(self.legend_btn, 5,alignment=Qt.AlignRight)
116
-
117
- self.log_btn = QPushButton('')
118
- self.log_btn.setIcon(icon(MDI6.math_log,color="black"))
119
- self.log_btn.setStyleSheet(self.button_select_all)
120
- self.log_btn.setIconSize(QSize(20, 20))
121
- self.log_btn.setToolTip('Enable or disable log scale')
122
- plot_buttons_hbox.addWidget(self.log_btn, 5, alignment=Qt.AlignRight)
123
-
124
- self.ci_btn = QPushButton('')
125
- self.ci_btn.setIcon(icon(MDI6.arrow_expand_horizontal,color="black"))
126
- self.ci_btn.setStyleSheet(self.button_select_all)
127
- self.ci_btn.setIconSize(QSize(20, 20))
128
- self.ci_btn.setToolTip('Show or hide confidence intervals.')
129
- plot_buttons_hbox.addWidget(self.ci_btn, 5, alignment=Qt.AlignRight)
130
-
131
- self.cell_lines_btn = QPushButton('')
132
- self.cell_lines_btn.setIcon(icon(MDI6.view_headline,color="black"))
133
- self.cell_lines_btn.setStyleSheet(self.button_select_all)
134
- self.cell_lines_btn.setToolTip('Show or hide individual cell signals.')
135
- self.cell_lines_btn.setIconSize(QSize(20, 20))
136
- plot_buttons_hbox.addWidget(self.cell_lines_btn, 5, alignment=Qt.AlignRight)
137
-
138
- self.fig, self.ax = plt.subplots(1,1,figsize=(4,3))
139
- self.plot_widget = FigureCanvas(self.fig, title="")
140
- self.plot_widget.setContentsMargins(0,0,0,0)
141
- self.initialize_axis()
142
- plt.tight_layout()
143
-
144
- self.export_btn = ExportPlotBtn(self.fig, export_dir=self.parent_window.exp_dir)
145
- plot_buttons_hbox.addWidget(self.export_btn, 5, alignment=Qt.AlignRight)
146
- self.layout.addLayout(plot_buttons_hbox)
147
-
148
- self.export_tabular_btn = QPushButton('')
149
- self.export_tabular_btn.setIcon(icon(MDI6.table,color="black"))
150
- self.export_tabular_btn.setStyleSheet(self.button_select_all)
151
- self.export_tabular_btn.setToolTip('Tabulate survival values.')
152
- self.export_tabular_btn.setIconSize(QSize(20, 20))
153
- plot_buttons_hbox.addWidget(self.export_tabular_btn, 5, alignment=Qt.AlignRight)
154
- self.export_tabular_btn.hide()
155
-
156
- self.ax.set_prop_cycle('color',[self.cmap(i) for i in np.linspace(0, 1, len(self.parent_window.well_indices))])
157
-
158
- self.fig.set_facecolor('none') # or 'None'
159
- self.fig.canvas.setStyleSheet("background-color: transparent;")
160
- self.plot_widget.canvas.draw()
161
-
162
- self.layout.addWidget(self.plot_widget)
163
-
164
- self.plot_btn_group.buttonClicked[int].connect(self.plot_signals)
165
- self.legend_btn.clicked.connect(self.show_hide_legend)
166
- self.log_btn.clicked.connect(self.switch_to_log)
167
- self.ci_btn.clicked.connect(self.switch_ci)
168
- self.cell_lines_btn.clicked.connect(self.switch_cell_lines)
169
-
170
- self.class_selection_widget = CelldetectiveWidget()
171
-
172
- self.class_selection_lbl = QLabel('class of interest:')
173
- class_hbox = QHBoxLayout()
174
- self.class_selection_widget.setLayout(class_hbox)
175
- class_hbox.addWidget(self.class_selection_lbl, 25, alignment=Qt.AlignLeft)
176
-
177
- class_subhbox = QHBoxLayout()
178
- class_hbox.addLayout(class_subhbox, 75)
179
-
180
- self.all_btn = QRadioButton('*')
181
- self.event_btn = QRadioButton('event')
182
- self.event_btn.setChecked(True)
183
- self.no_event_btn = QRadioButton('no event')
184
- self.class_btn_group = QButtonGroup()
185
- for btn in [self.all_btn, self.event_btn, self.no_event_btn]:
186
- self.class_btn_group.addButton(btn)
187
-
188
- self.class_btn_group.buttonClicked[int].connect(self.set_class_to_plot)
189
-
190
- class_subhbox.addWidget(self.all_btn, 33, alignment=Qt.AlignCenter)
191
- class_subhbox.addWidget(self.event_btn, 33, alignment=Qt.AlignCenter)
192
- class_subhbox.addWidget(self.no_event_btn, 33, alignment=Qt.AlignCenter)
193
-
194
- self.layout.addWidget(self.class_selection_widget) #Layout(class_hbox)
195
-
196
- # Rescale
197
- self.rescale_widget = CelldetectiveWidget()
198
-
199
- scale_hbox = QHBoxLayout()
200
- self.rescale_widget.setLayout(scale_hbox)
201
-
202
- scale_hbox.addWidget(QLabel('scaling factor: '), 25)
203
- self.scaling_factor_le = QLineEdit('1')
204
- self.scaling_factor_le.setValidator(self.float_validator)
205
- scale_hbox.addWidget(self.scaling_factor_le, 65)
206
-
207
- self.rescale_btn = QPushButton('rescale')
208
- self.rescale_btn.setStyleSheet(self.button_style_sheet_2)
209
- self.rescale_btn.clicked.connect(self.rescale_y_axis)
210
- scale_hbox.addWidget(self.rescale_btn, 10)
211
- #self.layout.addLayout(scale_hbox)
212
- self.layout.addWidget(self.rescale_widget)
213
-
214
-
215
- # Rescale
216
- self.cell_lines_alpha_wdg = CelldetectiveWidget()
217
- alpha_hbox = QHBoxLayout()
218
-
219
- self.alpha_slider = QLabeledDoubleSlider()
220
- alpha_hbox = QuickSliderLayout(label='single-cell\nsignal alpha: ',
221
- slider=self.alpha_slider,
222
- slider_initial_value=self.alpha_setting,
223
- slider_range=(0,1),
224
- decimal_option=True,
225
- precision=5,
226
- )
227
- self.alpha_slider.valueChanged.connect(self.submit_alpha)
228
- self.cell_lines_alpha_wdg.setLayout(alpha_hbox)
229
-
230
- # self.submit_alpha_btn = QPushButton('submit')
231
- # self.submit_alpha_btn.setStyleSheet(self.button_style_sheet_2)
232
- # self.submit_alpha_btn.clicked.connect(self.submit_alpha)
233
- # alpha_hbox.addWidget(self.submit_alpha_btn, 10)
234
- self.layout.addWidget(self.cell_lines_alpha_wdg)
235
-
236
- self.select_option = [QRadioButton() for i in range(2)]
237
- self.select_label = ['by name', 'spatially']
238
-
239
- select_hbox = QHBoxLayout()
240
- select_hbox.addWidget(QLabel('select position: '), 25)
241
-
242
- select_subhbox = QHBoxLayout()
243
- select_hbox.addLayout(select_subhbox, 75)
244
-
245
- self.select_btn_group = QButtonGroup()
246
- for i in range(2):
247
- self.select_option[i].setText(self.select_label[i])
248
- self.select_btn_group.addButton(self.select_option[i])
249
- select_subhbox.addWidget(self.select_option[i],33, alignment=Qt.AlignCenter)
250
- self.select_option[0].setChecked(True)
251
- self.select_btn_group.buttonClicked[int].connect(self.switch_selection_mode)
252
- self.layout.addLayout(select_hbox)
253
-
254
- self.look_for_metadata()
255
- if self.metadata_found:
256
- self.fig_scatter, self.ax_scatter = plt.subplots(1,1,figsize=(4,2)) #,figsize=(4,3)
257
- self.position_scatter = FigureCanvas(self.fig_scatter)
258
- self.load_coordinates()
259
- self.plot_spatial_location()
260
- self.ax_scatter.spines['top'].set_visible(False)
261
- self.ax_scatter.spines['right'].set_visible(False)
262
- self.ax_scatter.set_aspect('equal')
263
- self.ax_scatter.set_xticks([])
264
- self.ax_scatter.set_yticks([])
265
- plt.tight_layout()
266
-
267
- self.fig_scatter.set_facecolor('none') # or 'None'
268
- self.fig_scatter.canvas.setStyleSheet("background-color: transparent;")
269
- self.layout.addWidget(self.position_scatter)
270
-
271
- self.generate_pos_selection_widget()
272
- self.select_btn_group.buttons()[0].click()
273
-
274
- def submit_alpha(self, value):
275
-
276
- alpha = value
277
- try:
278
- alpha = float(alpha)
279
- except:
280
- return None
281
- if alpha>1.0:
282
- alpha = 1.0
283
- elif alpha<0.0:
284
- alpha = 0.0
285
- self.alpha_setting = alpha
286
- self.plot_signals(0)
287
-
288
- def rescale_y_axis(self):
289
- new_scale = self.scaling_factor_le.text().replace(',','.')
290
- if new_scale=='':
291
- msgBox = QMessageBox()
292
- msgBox.setIcon(QMessageBox.Warning)
293
- msgBox.setText("Please set a valid scaling factor...")
294
- msgBox.setWindowTitle("Warning")
295
- msgBox.setStandardButtons(QMessageBox.Ok)
296
- returnValue = msgBox.exec()
297
- if returnValue == QMessageBox.Ok:
298
- return None
299
- else:
300
- self.scaling_factor = float(new_scale)
301
- self.plot_signals(0)
302
-
303
-
304
- def switch_selection_mode(self, id):
305
-
306
- for i in range(2):
307
- if self.select_option[i].isChecked():
308
- self.selection_mode = self.select_label[i]
309
- if self.selection_mode=='by name':
310
- if len(self.metafiles)>0:
311
- self.position_scatter.hide()
312
- self.line_choice_widget.show()
313
- else:
314
- if len(self.metafiles)>0:
315
- self.position_scatter.show()
316
- self.line_choice_widget.hide()
317
-
318
- def set_class_to_plot(self):
319
-
320
- if self.all_btn.isChecked():
321
- self.target_class=[0,1]
322
- elif self.event_btn.isChecked():
323
- self.target_class = [0]
324
- else:
325
- self.target_class = [1]
326
-
327
- self.plot_signals(0)
328
-
329
- def generate_pos_selection_widget(self):
330
-
331
- self.well_names = self.df['well_name'].unique()
332
- self.pos_names = self.df_pos_info['pos_name'].unique() #pd.DataFrame(self.ks_estimators_per_position)['position_name'].unique()
333
-
334
- self.usable_well_labels = []
335
- for name in self.well_names:
336
- for lbl in self.parent_window.well_labels:
337
- if name+':' in lbl:
338
- self.usable_well_labels.append(lbl)
339
-
340
- thresh = 20
341
- self.well_name_truncated = [w[:thresh - 3]+'...' if len(w)>thresh else w for w in self.usable_well_labels]
342
-
343
- self.line_choice_widget = CelldetectiveWidget()
344
- self.line_check_vbox = QGridLayout()
345
- self.line_choice_widget.setLayout(self.line_check_vbox)
346
-
347
- if len(self.parent_window.well_indices)>1:
348
- self.well_display_options = [QCheckBox(self.well_name_truncated[i]) for i in range(len(self.well_name_truncated))]
349
- for i in range(len(self.well_names)):
350
- self.line_check_vbox.addWidget(self.well_display_options[i], i%4, i//4, 1, 1, alignment=Qt.AlignCenter)
351
- self.well_display_options[i].setChecked(True)
352
- self.well_display_options[i].setStyleSheet("font-size: 12px;")
353
- self.well_display_options[i].toggled.connect(self.select_lines)
354
- self.well_display_options[i].setToolTip(self.usable_well_labels[i])
355
- else:
356
- self.pos_display_options = [QCheckBox(self.pos_names[i]) for i in range(len(self.pos_names))]
357
- for i in range(len(self.pos_names)):
358
- self.line_check_vbox.addWidget(self.pos_display_options[i], i%4, i//4, 1, 1, alignment=Qt.AlignCenter)
359
- self.pos_display_options[i].setChecked(True)
360
- self.pos_display_options[i].setStyleSheet("font-size: 12px;")
361
- self.pos_display_options[i].toggled.connect(self.select_lines)
362
-
363
- self.layout.addWidget(self.line_choice_widget)
364
- #self.layout.addLayout(self.line_check_vbox)
365
-
366
- def look_for_metadata(self):
367
- self.metadata_found = False
368
- self.metafiles = glob(self.parent_window.exp_dir+os.sep.join([f'W*','*','movie','*metadata.txt'])) \
369
- + glob(self.parent_window.exp_dir+os.sep.join([f'W*','*','*metadata.txt'])) \
370
- + glob(self.parent_window.exp_dir+os.sep.join([f'W*','*metadata.txt'])) \
371
- + glob(self.parent_window.exp_dir+'*metadata.txt')
372
- print(f'Found {len(self.metafiles)} metadata files...')
373
- if len(self.metafiles)>0:
374
- self.metadata_found = True
375
-
376
- def load_coordinates(self):
377
-
378
- """
379
- Read metadata and try to extract position coordinates
380
- """
381
-
382
- self.no_meta = False
383
- try:
384
- with open(self.metafiles[0], 'r') as f:
385
- data = json.load(f)
386
- positions = data['Summary']['InitialPositionList']
387
- except Exception as e:
388
- print(f'Trouble loading metadata: error {e}...')
389
- return None
390
-
391
- for k in range(len(positions)):
392
- pos_label = positions[k]['Label']
393
- try:
394
- coords = positions[k]['DeviceCoordinatesUm']['XYStage']
395
- except:
396
- try:
397
- coords = positions[k]['DeviceCoordinatesUm']['PIXYStage']
398
- except:
399
- self.no_meta = True
400
-
401
- if not self.no_meta:
402
- files = self.df_pos_info['stack_path'].values
403
- pos_loc = [pos_label in f for f in files]
404
- self.df_pos_info.loc[pos_loc, 'x'] = coords[0]
405
- self.df_pos_info.loc[pos_loc, 'y'] = coords[1]
406
- self.df_pos_info.loc[pos_loc, 'metadata_tag'] = pos_label
407
-
408
-
409
- def plot_spatial_location(self):
410
-
411
- try:
412
- self.sc = self.ax_scatter.scatter(self.df_pos_info["x"].values, self.df_pos_info["y"].values, picker=True, pickradius=1, color=self.select_color(self.df_pos_info["select"].values))
413
- self.scat_labels = self.df_pos_info['metadata_tag'].values
414
- self.ax_scatter.invert_xaxis()
415
-
416
- self.annot = self.ax_scatter.annotate("", xy=(0,0), xytext=(10,10),textcoords="offset points",
417
- bbox=dict(boxstyle="round", fc="w"),
418
- arrowprops=dict(arrowstyle="->"))
419
- self.annot.set_visible(False)
420
-
421
- xmin,xmax = self.ax_scatter.get_xlim()
422
- ymin,ymax = self.ax_scatter.get_ylim()
423
- xdatarange = xmax - xmin
424
- ydatarange = ymax - ymin
425
-
426
- self.ax_scatter.set_xlim(xmin-0.1*xdatarange, xmax + 0.1*xdatarange)
427
- self.ax_scatter.set_ylim(ymin-0.1*ydatarange, ymax + 0.1*ydatarange)
428
-
429
- #xmin,xmax = self.ax_scatter.get_xlim()
430
- #ymin,ymax = self.ax_scatter.get_ylim()
431
- #desired_a = 4
432
- #new_x_max = xmin + desired_a * (ymax - ymin)
433
- #self.ax_scatter.set_xlim(xmin - (new_x_max - xmin)/2.0, new_x_max - (new_x_max - xmin)/2.0)
434
-
435
- self.fig_scatter.tight_layout()
436
- self.fig_scatter.canvas.mpl_connect("motion_notify_event", self.hover)
437
- self.fig_scatter.canvas.mpl_connect("pick_event", self.unselect_position)
438
- except Exception as e:
439
- pass
440
-
441
- def update_annot(self, ind):
442
-
443
- pos = self.sc.get_offsets()[ind["ind"][0]]
444
- self.annot.xy = pos
445
- text = self.scat_labels[ind["ind"][0]]
446
- self.annot.set_text(text)
447
- self.annot.get_bbox_patch().set_facecolor('k')
448
- self.annot.get_bbox_patch().set_alpha(0.4)
449
-
450
-
451
- def hover(self, event):
452
- vis = self.annot.get_visible()
453
- if event.inaxes == self.ax_scatter:
454
- cont, ind = self.sc.contains(event)
455
- if cont:
456
- self.update_annot(ind)
457
- self.annot.set_visible(True)
458
- self.fig_scatter.canvas.draw_idle()
459
- else:
460
- if vis:
461
- self.annot.set_visible(False)
462
- self.fig_scatter.canvas.draw_idle()
463
-
464
- def unselect_position(self, event):
465
-
466
- ind = event.ind # index of selected position
467
- well_idx = self.df_pos_info.iloc[ind]['well_index'].values[0]
468
- selectedPos = self.df_pos_info.iloc[ind]['pos_path'].values[0]
469
- currentSelState = self.df_pos_info.iloc[ind]['select'].values[0]
470
- if self.plot_options[0].isChecked() or self.plot_options[2].isChecked():
471
- self.df_pos_info.loc[self.df_pos_info['well_index']==well_idx,'select'] = not currentSelState
472
- self.df_well_info.loc[self.df_well_info['well_index']==well_idx, 'select'] = not currentSelState
473
- if len(self.parent_window.well_indices)>1:
474
- self.well_display_options[well_idx].setChecked(not currentSelState)
475
- else:
476
- for p in self.pos_display_options:
477
- p.setChecked(not currentSelState)
478
- else:
479
- self.df_pos_info.loc[self.df_pos_info['pos_path']==selectedPos,'select'] = not currentSelState
480
- if len(self.parent_window.well_indices)<=1:
481
- self.pos_display_options[ind[0]].setChecked(not currentSelState)
482
-
483
- self.sc.set_color(self.select_color(self.df_pos_info["select"].values))
484
- self.position_scatter.canvas.draw_idle()
485
- self.plot_signals(0)
486
-
487
- def select_color(self, selection):
488
- colors = [tab10(0) if s else tab10(0.1) for s in selection]
489
- return colors
490
-
491
- def initialize_axis(self):
492
-
493
- previous_ymin, previous_ymax = self.ax.get_ylim()
494
- previous_legend = self.legend_visible
495
- is_log = self.ax.get_yscale()
496
-
497
- self.ax.clear()
498
- self.ax.plot([],[])
499
-
500
- # Labels
501
- self.ax.set_xlabel('time [min]')
502
- self.ax.set_ylabel(self.feature_selected)
503
-
504
- # Spines
505
- self.ax.spines['top'].set_visible(False)
506
- self.ax.spines['right'].set_visible(False)
507
- self.ax.grid(which='major', color='black', linestyle='-', linewidth=0.8, alpha=0.2)
508
- self.ax.grid(which='minor', color='lightgray', linestyle='--', linewidth=0.5, alpha=0.1)
509
- # Lims
510
- safe_df = self.df.dropna(subset=self.feature_selected)
511
- values = safe_df[self.feature_selected].values
512
- if len(values)>0:
513
- self.ax.set_ylim(np.percentile(values, 1)*self.scaling_factor, np.percentile(values, 99)*self.scaling_factor)
514
- self.ax.set_xlim(-(self.df['FRAME'].max()+2)*self.parent_window.FrameToMin,(self.df['FRAME'].max()+2)*self.parent_window.FrameToMin)
515
-
516
- if is_log=='log':
517
- self.ax.set_yscale('log')
518
- if previous_legend:
519
- leg = self.ax.get_legend()
520
- if leg is not None:
521
- leg.set_visible(True)
522
-
523
- def show_hide_legend(self):
524
-
525
- if self.legend_visible:
526
- leg = self.ax.get_legend()
527
- leg.set_visible(False)
528
- self.legend_visible = False
529
- self.legend_btn.setIcon(icon(MDI6.text_box,color="black"))
530
- else:
531
- leg = self.ax.get_legend()
532
- leg.set_visible(True)
533
- self.legend_visible = True
534
- self.legend_btn.setIcon(icon(MDI6.text_box,color=self.help_color))
535
-
536
- self.plot_widget.canvas.draw_idle()
537
-
538
- def switch_to_log(self):
539
-
540
- """
541
- Switch threshold histogram to log scale. Auto adjust.
542
- """
543
-
544
- if self.ax.get_yscale()=='linear':
545
- self.ax.set_yscale('log')
546
- self.log_btn.setIcon(icon(MDI6.math_log,color=self.help_color))
547
- #self.ax.set_ylim(0.01,1.05)
548
- else:
549
- self.ax.set_yscale('linear')
550
- self.log_btn.setIcon(icon(MDI6.math_log,color="black"))
551
- #self.ax.set_ylim(0.01,1.05)
552
-
553
- #self.ax.autoscale()
554
- self.plot_widget.canvas.draw_idle()
555
-
556
- def plot_signals(self, id):
557
-
558
- for i in range(3):
559
- if self.plot_options[i].isChecked():
560
- self.plot_mode = self.radio_labels[i]
561
-
562
- if self.target_class==[0,1]:
563
- mean_signal_type = 'mean_all'
564
- std_signal = 'std_all'
565
- matrix = 'matrix_all'
566
- elif self.target_class==[0]:
567
- mean_signal_type = 'mean_event'
568
- std_signal = 'std_event'
569
- matrix = 'matrix_event'
570
- else:
571
- mean_signal_type = 'mean_no_event'
572
- std_signal = 'std_no_event'
573
- matrix = 'matrix_no_event'
574
-
575
-
576
- colors = np.array([self.cmap(i / len(self.df_pos_info)) for i in range(len(self.df_pos_info))])
577
- well_color = [self.cmap(i / len(self.df_well_info)) for i in range(len(self.df_well_info))]
578
-
579
- if self.plot_mode=='position':
580
- self.initialize_axis()
581
- lines = self.df_pos_info.loc[self.df_pos_info['select'],'signal'].values
582
- pos_labels = self.df_pos_info.loc[self.df_pos_info['select'],'pos_name'].values
583
- pos_indices = self.df_pos_info.loc[self.df_pos_info['select'],'pos_index'].values
584
- well_index = self.df_pos_info.loc[self.df_pos_info['select'],'well_index'].values
585
- for i in range(len(lines)):
586
- if len(self.parent_window.well_indices)<=1:
587
- self.plot_line(lines[i], colors[pos_indices[i]], pos_labels[i], mean_signal_type, std_signal=std_signal, ci_option=self.show_ci, cell_lines_option=self.show_cell_lines, matrix=matrix)
588
- else:
589
- self.plot_line(lines[i], well_color[well_index[i]], pos_labels[i], mean_signal_type, std_signal=std_signal, ci_option=self.show_ci, cell_lines_option=self.show_cell_lines, matrix=matrix)
590
- if self.legend_visible:
591
- self.ax.legend(ncols=3,fontsize='x-small')
592
-
593
- elif self.plot_mode=='well':
594
- self.initialize_axis()
595
- lines = self.df_well_info.loc[self.df_well_info['select'],'signal'].values
596
- well_index = self.df_well_info.loc[self.df_well_info['select'],'well_index'].values
597
- well_labels = self.df_well_info.loc[self.df_well_info['select'],'well_name'].values
598
- for i in range(len(lines)):
599
- if len(self.parent_window.well_indices)<=1:
600
- self.plot_line(lines[i], 'k', well_labels[i], mean_signal_type, std_signal=std_signal, ci_option=self.show_ci, cell_lines_option=self.show_cell_lines,matrix=matrix)
601
- else:
602
- self.plot_line(lines[i], well_color[well_index[i]], well_labels[i], mean_signal_type, std_signal=std_signal, ci_option=self.show_ci, cell_lines_option=self.show_cell_lines,matrix=matrix)
603
- if self.legend_visible:
604
- self.ax.legend(ncols=2, fontsize='x-small')
605
-
606
- elif self.plot_mode=='both':
607
-
608
- self.initialize_axis()
609
- lines_pos = self.df_pos_info.loc[self.df_pos_info['select'],'signal'].values
610
- lines_well = self.df_well_info.loc[self.df_well_info['select'],'signal'].values
611
-
612
- pos_indices = self.df_pos_info.loc[self.df_pos_info['select'],'pos_index'].values
613
- well_index_pos = self.df_pos_info.loc[self.df_pos_info['select'],'well_index'].values
614
- well_index = self.df_well_info.loc[self.df_well_info['select'],'well_index'].values
615
- well_labels = self.df_well_info.loc[self.df_well_info['select'],'well_name'].values
616
- pos_labels = self.df_pos_info.loc[self.df_pos_info['select'],'pos_name'].values
617
-
618
- for i in range(len(lines_pos)):
619
- if len(self.parent_window.well_indices)<=1:
620
- self.plot_line(lines_pos[i], colors[pos_indices[i]], pos_labels[i], mean_signal_type, std_signal=std_signal, ci_option=self.show_ci, cell_lines_option=self.show_cell_lines,matrix=matrix)
621
- else:
622
- self.plot_line(lines_pos[i], well_color[well_index_pos[i]], None, mean_signal_type, std_signal=std_signal, ci_option=False)
623
-
624
- for i in range(len(lines_well)):
625
- if len(self.parent_window.well_indices)<=1:
626
- self.plot_line(lines_well[i], 'k', 'pool', mean_signal_type, std_signal=std_signal, ci_option=False)
627
- else:
628
- self.plot_line(lines_well[i], well_color[well_index[i]], well_labels[i], mean_signal_type, std_signal=std_signal, ci_option=False)
629
- if self.legend_visible:
630
- self.ax.legend(ncols=3,fontsize='x-small')
631
-
632
- self.plot_widget.canvas.draw()
633
-
634
- def plot_line(self, line, color, label, mean_signal_type, ci_option=True, cell_lines_option=False, alpha_ci=0.5, std_signal=None, matrix=None):
635
-
636
- # Plot a signal
637
- if line==line:
638
- self.ax.plot(line['timeline']*self.parent_window.FrameToMin, line[mean_signal_type]*self.scaling_factor, color=color, label=label)
639
-
640
- if ci_option and std_signal is not None:
641
-
642
- self.ax.fill_between(line['timeline']*self.parent_window.FrameToMin,
643
- [a-b for a,b in zip(line[mean_signal_type]*self.scaling_factor, line[std_signal]*self.scaling_factor)],
644
- [a+b for a,b in zip(line[mean_signal_type]*self.scaling_factor, line[std_signal]*self.scaling_factor)],
645
- color=color,
646
- alpha=alpha_ci,
647
- )
648
- if cell_lines_option and matrix is not None:
649
- # Show individual cell signals
650
- mat = line[matrix]
651
- for i in range(mat.shape[0]):
652
- self.ax.plot(line['timeline']*self.parent_window.FrameToMin, mat[i,:]*self.scaling_factor, color=color, alpha=self.alpha_setting)
653
-
654
-
655
- def switch_ci(self):
656
-
657
- # Show the confidence interval / STD
658
-
659
- if self.show_ci:
660
- self.ci_btn.setIcon(icon(MDI6.arrow_expand_horizontal,color="black"))
661
- else:
662
- self.ci_btn.setIcon(icon(MDI6.arrow_expand_horizontal,color=self.help_color))
663
- self.show_ci = not self.show_ci
664
- try:
665
- self.plot_signals(0)
666
- except Exception as e:
667
- print(f"{e=}")
668
-
669
- def switch_cell_lines(self):
670
-
671
- # Show individual cell signals
672
-
673
- if self.show_cell_lines:
674
- self.cell_lines_btn.setIcon(icon(MDI6.view_headline,color="black"))
675
- else:
676
- self.cell_lines_btn.setIcon(icon(MDI6.view_headline,color=self.help_color))
677
- self.show_cell_lines = not self.show_cell_lines
678
- self.plot_signals(0)
679
-
680
- def select_lines(self):
681
-
682
- if len(self.parent_window.well_indices)>1:
683
- for i in range(len(self.well_display_options)):
684
- self.df_well_info.loc[self.df_well_info['well_index']==i,'select'] = self.well_display_options[i].isChecked()
685
- self.df_pos_info.loc[self.df_pos_info['well_index']==i,'select'] = self.well_display_options[i].isChecked()
686
- else:
687
- for i in range(len(self.pos_display_options)):
688
- self.df_pos_info.loc[self.df_pos_info['pos_index']==i,'select'] = self.pos_display_options[i].isChecked()
689
-
690
- if len(self.metafiles)>0:
691
- self.sc.set_color(self.select_color(self.df_pos_info["select"].values))
692
- self.position_scatter.canvas.draw_idle()
693
- self.plot_signals(0)
42
+ def __init__(
43
+ self,
44
+ df=None,
45
+ df_pos_info=None,
46
+ df_well_info=None,
47
+ feature_selected=None,
48
+ parent_window=None,
49
+ title="plot",
50
+ *args,
51
+ **kwargs,
52
+ ):
53
+
54
+ super().__init__()
55
+ center_window(self)
56
+
57
+ self.parent_window = parent_window
58
+ self.setWindowTitle(title)
59
+
60
+ self.show_ci = False
61
+ self.legend_visible = True
62
+ self.show_cell_lines = False
63
+ self.alpha_setting = 0.5
64
+ self.scaling_factor = 1
65
+ self.target_class = [0]
66
+ self.float_validator = QDoubleValidator()
67
+
68
+ cmap_lbl = self.parent_window.cbs[-1].currentText()
69
+ self.cmap = getattr(mcm, cmap_lbl)
70
+
71
+ self.feature_selected = feature_selected
72
+ self.df = df
73
+ self.df_pos_info = df_pos_info
74
+ self.df_well_info = df_well_info
75
+
76
+ self.layout = QVBoxLayout()
77
+ self.layout.setSpacing(3)
78
+ self.populate_widget()
79
+
80
+ self.ci_btn.click()
81
+ # self.legend_btn.click()
82
+
83
+ self.fig.tight_layout()
84
+ self.setLayout(self.layout)
85
+
86
+ def populate_widget(self):
87
+
88
+ self.plot_options = [QRadioButton() for i in range(3)]
89
+ self.radio_labels = ["well", "position", "both"]
90
+ radio_hbox = QHBoxLayout()
91
+ radio_hbox.setContentsMargins(15, 15, 15, 0)
92
+
93
+ self.group_lbl = QLabel("grouping: ")
94
+ radio_hbox.addWidget(self.group_lbl, 25)
95
+ radio_subhbox = QHBoxLayout()
96
+ radio_hbox.addLayout(radio_subhbox, 75)
97
+
98
+ self.plot_btn_group = QButtonGroup()
99
+ for i in range(3):
100
+ self.plot_options[i].setText(self.radio_labels[i])
101
+ self.plot_btn_group.addButton(self.plot_options[i])
102
+ radio_subhbox.addWidget(self.plot_options[i], 33, alignment=Qt.AlignCenter)
103
+
104
+ if self.parent_window.position_indices is not None:
105
+ # at least a position is selected
106
+ if (
107
+ len(self.parent_window.well_indices) > 1
108
+ and len(self.parent_window.position_indices) == 1
109
+ ):
110
+ # several wells but one position
111
+ self.plot_btn_group.buttons()[0].click()
112
+ # for i in [1,2]:
113
+ # self.plot_options[i].setEnabled(False)
114
+ elif len(self.parent_window.well_indices) > 1:
115
+ self.plot_btn_group.buttons()[0].click()
116
+ elif (
117
+ len(self.parent_window.well_indices) == 1
118
+ and len(self.parent_window.position_indices) == 1
119
+ ):
120
+ self.plot_btn_group.buttons()[1].click()
121
+ for i in [0, 2]:
122
+ self.plot_options[i].setEnabled(False)
123
+ elif (
124
+ len(self.parent_window.well_indices) == 1
125
+ and len(self.parent_window.position_indices) > 1
126
+ ):
127
+ # one well, several positions
128
+ self.plot_btn_group.buttons()[2].click()
129
+ else:
130
+ if len(self.parent_window.well_indices) > 1:
131
+ self.plot_btn_group.buttons()[0].click()
132
+ elif len(self.parent_window.well_indices) == 1:
133
+ self.plot_btn_group.buttons()[2].click()
134
+
135
+ self.layout.addLayout(radio_hbox)
136
+
137
+ plot_buttons_hbox = QHBoxLayout()
138
+ plot_buttons_hbox.setContentsMargins(10, 10, 5, 0)
139
+ plot_buttons_hbox.addWidget(QLabel(""), 80, alignment=Qt.AlignLeft)
140
+
141
+ self.legend_btn = QPushButton("")
142
+ self.legend_btn.setIcon(icon(MDI6.text_box, color=self.help_color))
143
+ self.legend_btn.setStyleSheet(self.button_select_all)
144
+ self.legend_btn.setToolTip("Show or hide the legend")
145
+ self.legend_btn.setIconSize(QSize(20, 20))
146
+ plot_buttons_hbox.addWidget(self.legend_btn, 5, alignment=Qt.AlignRight)
147
+
148
+ self.log_btn = QPushButton("")
149
+ self.log_btn.setIcon(icon(MDI6.math_log, color="black"))
150
+ self.log_btn.setStyleSheet(self.button_select_all)
151
+ self.log_btn.setIconSize(QSize(20, 20))
152
+ self.log_btn.setToolTip("Enable or disable log scale")
153
+ plot_buttons_hbox.addWidget(self.log_btn, 5, alignment=Qt.AlignRight)
154
+
155
+ self.ci_btn = QPushButton("")
156
+ self.ci_btn.setIcon(icon(MDI6.arrow_expand_horizontal, color="black"))
157
+ self.ci_btn.setStyleSheet(self.button_select_all)
158
+ self.ci_btn.setIconSize(QSize(20, 20))
159
+ self.ci_btn.setToolTip("Show or hide confidence intervals.")
160
+ plot_buttons_hbox.addWidget(self.ci_btn, 5, alignment=Qt.AlignRight)
161
+
162
+ self.cell_lines_btn = QPushButton("")
163
+ self.cell_lines_btn.setIcon(icon(MDI6.view_headline, color="black"))
164
+ self.cell_lines_btn.setStyleSheet(self.button_select_all)
165
+ self.cell_lines_btn.setToolTip("Show or hide individual cell signals.")
166
+ self.cell_lines_btn.setIconSize(QSize(20, 20))
167
+ plot_buttons_hbox.addWidget(self.cell_lines_btn, 5, alignment=Qt.AlignRight)
168
+
169
+ self.fig, self.ax = plt.subplots(1, 1, figsize=(4, 3))
170
+ self.plot_widget = FigureCanvas(self.fig, title="")
171
+ self.plot_widget.setContentsMargins(0, 0, 0, 0)
172
+ self.initialize_axis()
173
+ plt.tight_layout()
174
+
175
+ self.export_btn = ExportPlotBtn(self.fig, export_dir=self.parent_window.exp_dir)
176
+ plot_buttons_hbox.addWidget(self.export_btn, 5, alignment=Qt.AlignRight)
177
+ self.layout.addLayout(plot_buttons_hbox)
178
+
179
+ self.export_tabular_btn = QPushButton("")
180
+ self.export_tabular_btn.setIcon(icon(MDI6.table, color="black"))
181
+ self.export_tabular_btn.setStyleSheet(self.button_select_all)
182
+ self.export_tabular_btn.setToolTip("Tabulate survival values.")
183
+ self.export_tabular_btn.setIconSize(QSize(20, 20))
184
+ plot_buttons_hbox.addWidget(self.export_tabular_btn, 5, alignment=Qt.AlignRight)
185
+ self.export_tabular_btn.hide()
186
+
187
+ self.ax.set_prop_cycle(
188
+ "color",
189
+ [
190
+ self.cmap(i)
191
+ for i in np.linspace(0, 1, len(self.parent_window.well_indices))
192
+ ],
193
+ )
194
+
195
+ self.fig.set_facecolor("none") # or 'None'
196
+ self.fig.canvas.setStyleSheet("background-color: transparent;")
197
+ self.plot_widget.canvas.draw()
198
+
199
+ self.layout.addWidget(self.plot_widget)
200
+
201
+ self.plot_btn_group.buttonClicked[int].connect(self.plot_signals)
202
+ self.legend_btn.clicked.connect(self.show_hide_legend)
203
+ self.log_btn.clicked.connect(self.switch_to_log)
204
+ self.ci_btn.clicked.connect(self.switch_ci)
205
+ self.cell_lines_btn.clicked.connect(self.switch_cell_lines)
206
+
207
+ self.class_selection_widget = CelldetectiveWidget()
208
+
209
+ self.class_selection_lbl = QLabel("class of interest:")
210
+ class_hbox = QHBoxLayout()
211
+ self.class_selection_widget.setLayout(class_hbox)
212
+ class_hbox.addWidget(self.class_selection_lbl, 25, alignment=Qt.AlignLeft)
213
+
214
+ class_subhbox = QHBoxLayout()
215
+ class_hbox.addLayout(class_subhbox, 75)
216
+
217
+ self.all_btn = QRadioButton("*")
218
+ self.event_btn = QRadioButton("event")
219
+ self.event_btn.setChecked(True)
220
+ self.no_event_btn = QRadioButton("no event")
221
+ self.class_btn_group = QButtonGroup()
222
+ for btn in [self.all_btn, self.event_btn, self.no_event_btn]:
223
+ self.class_btn_group.addButton(btn)
224
+
225
+ self.class_btn_group.buttonClicked[int].connect(self.set_class_to_plot)
226
+
227
+ class_subhbox.addWidget(self.all_btn, 33, alignment=Qt.AlignCenter)
228
+ class_subhbox.addWidget(self.event_btn, 33, alignment=Qt.AlignCenter)
229
+ class_subhbox.addWidget(self.no_event_btn, 33, alignment=Qt.AlignCenter)
230
+
231
+ self.layout.addWidget(self.class_selection_widget) # Layout(class_hbox)
232
+
233
+ # Rescale
234
+ self.rescale_widget = CelldetectiveWidget()
235
+
236
+ scale_hbox = QHBoxLayout()
237
+ self.rescale_widget.setLayout(scale_hbox)
238
+
239
+ scale_hbox.addWidget(QLabel("scaling factor: "), 25)
240
+ self.scaling_factor_le = QLineEdit("1")
241
+ self.scaling_factor_le.setValidator(self.float_validator)
242
+ scale_hbox.addWidget(self.scaling_factor_le, 65)
243
+
244
+ self.rescale_btn = QPushButton("rescale")
245
+ self.rescale_btn.setStyleSheet(self.button_style_sheet_2)
246
+ self.rescale_btn.clicked.connect(self.rescale_y_axis)
247
+ scale_hbox.addWidget(self.rescale_btn, 10)
248
+ # self.layout.addLayout(scale_hbox)
249
+ self.layout.addWidget(self.rescale_widget)
250
+
251
+ # Rescale
252
+ self.cell_lines_alpha_wdg = CelldetectiveWidget()
253
+ alpha_hbox = QHBoxLayout()
254
+
255
+ self.alpha_slider = QLabeledDoubleSlider()
256
+ alpha_hbox = QuickSliderLayout(
257
+ label="single-cell\nsignal alpha: ",
258
+ slider=self.alpha_slider,
259
+ slider_initial_value=self.alpha_setting,
260
+ slider_range=(0, 1),
261
+ decimal_option=True,
262
+ precision=5,
263
+ )
264
+ self.alpha_slider.valueChanged.connect(self.submit_alpha)
265
+ self.cell_lines_alpha_wdg.setLayout(alpha_hbox)
266
+
267
+ # self.submit_alpha_btn = QPushButton('submit')
268
+ # self.submit_alpha_btn.setStyleSheet(self.button_style_sheet_2)
269
+ # self.submit_alpha_btn.clicked.connect(self.submit_alpha)
270
+ # alpha_hbox.addWidget(self.submit_alpha_btn, 10)
271
+ self.layout.addWidget(self.cell_lines_alpha_wdg)
272
+
273
+ self.select_option = [QRadioButton() for i in range(2)]
274
+ self.select_label = ["by name", "spatially"]
275
+
276
+ select_hbox = QHBoxLayout()
277
+ select_hbox.addWidget(QLabel("select position: "), 25)
278
+
279
+ select_subhbox = QHBoxLayout()
280
+ select_hbox.addLayout(select_subhbox, 75)
281
+
282
+ self.select_btn_group = QButtonGroup()
283
+ for i in range(2):
284
+ self.select_option[i].setText(self.select_label[i])
285
+ self.select_btn_group.addButton(self.select_option[i])
286
+ select_subhbox.addWidget(
287
+ self.select_option[i], 33, alignment=Qt.AlignCenter
288
+ )
289
+ self.select_option[0].setChecked(True)
290
+ self.select_btn_group.buttonClicked[int].connect(self.switch_selection_mode)
291
+ self.layout.addLayout(select_hbox)
292
+
293
+ self.look_for_metadata()
294
+ if self.metadata_found:
295
+ self.fig_scatter, self.ax_scatter = plt.subplots(
296
+ 1, 1, figsize=(4, 2)
297
+ ) # ,figsize=(4,3)
298
+ self.position_scatter = FigureCanvas(self.fig_scatter)
299
+ self.load_coordinates()
300
+ self.plot_spatial_location()
301
+ self.ax_scatter.spines["top"].set_visible(False)
302
+ self.ax_scatter.spines["right"].set_visible(False)
303
+ self.ax_scatter.set_aspect("equal")
304
+ self.ax_scatter.set_xticks([])
305
+ self.ax_scatter.set_yticks([])
306
+ plt.tight_layout()
307
+
308
+ self.fig_scatter.set_facecolor("none") # or 'None'
309
+ self.fig_scatter.canvas.setStyleSheet("background-color: transparent;")
310
+ self.layout.addWidget(self.position_scatter)
311
+
312
+ self.generate_pos_selection_widget()
313
+ self.select_btn_group.buttons()[0].click()
314
+
315
+ def submit_alpha(self, value):
316
+
317
+ alpha = value
318
+ try:
319
+ alpha = float(alpha)
320
+ except:
321
+ return None
322
+ if alpha > 1.0:
323
+ alpha = 1.0
324
+ elif alpha < 0.0:
325
+ alpha = 0.0
326
+ self.alpha_setting = alpha
327
+ self.plot_signals(0)
328
+
329
+ def rescale_y_axis(self):
330
+ new_scale = self.scaling_factor_le.text().replace(",", ".")
331
+ if new_scale == "":
332
+ msgBox = QMessageBox()
333
+ msgBox.setIcon(QMessageBox.Warning)
334
+ msgBox.setText("Please set a valid scaling factor...")
335
+ msgBox.setWindowTitle("Warning")
336
+ msgBox.setStandardButtons(QMessageBox.Ok)
337
+ returnValue = msgBox.exec()
338
+ if returnValue == QMessageBox.Ok:
339
+ return None
340
+ else:
341
+ self.scaling_factor = float(new_scale)
342
+ self.plot_signals(0)
343
+
344
+ def switch_selection_mode(self, id):
345
+
346
+ for i in range(2):
347
+ if self.select_option[i].isChecked():
348
+ self.selection_mode = self.select_label[i]
349
+ if self.selection_mode == "by name":
350
+ if len(self.metafiles) > 0:
351
+ self.position_scatter.hide()
352
+ self.line_choice_widget.show()
353
+ else:
354
+ if len(self.metafiles) > 0:
355
+ self.position_scatter.show()
356
+ self.line_choice_widget.hide()
357
+
358
+ def set_class_to_plot(self):
359
+
360
+ if self.all_btn.isChecked():
361
+ self.target_class = [0, 1]
362
+ elif self.event_btn.isChecked():
363
+ self.target_class = [0]
364
+ else:
365
+ self.target_class = [1]
366
+
367
+ self.plot_signals(0)
368
+
369
+ def generate_pos_selection_widget(self):
370
+
371
+ self.well_names = self.df["well_name"].unique()
372
+ self.pos_names = self.df_pos_info[
373
+ "pos_name"
374
+ ].unique() # pd.DataFrame(self.ks_estimators_per_position)['position_name'].unique()
375
+
376
+ self.usable_well_labels = []
377
+ for name in self.well_names:
378
+ for lbl in self.parent_window.well_labels:
379
+ if name + ":" in lbl:
380
+ self.usable_well_labels.append(lbl)
381
+
382
+ thresh = 20
383
+ self.well_name_truncated = [
384
+ w[: thresh - 3] + "..." if len(w) > thresh else w
385
+ for w in self.usable_well_labels
386
+ ]
387
+
388
+ self.line_choice_widget = CelldetectiveWidget()
389
+ self.line_check_vbox = QGridLayout()
390
+ self.line_choice_widget.setLayout(self.line_check_vbox)
391
+
392
+ if len(self.parent_window.well_indices) > 1:
393
+ self.well_display_options = [
394
+ QCheckBox(self.well_name_truncated[i])
395
+ for i in range(len(self.well_name_truncated))
396
+ ]
397
+ for i in range(len(self.well_names)):
398
+ self.line_check_vbox.addWidget(
399
+ self.well_display_options[i],
400
+ i % 4,
401
+ i // 4,
402
+ 1,
403
+ 1,
404
+ alignment=Qt.AlignCenter,
405
+ )
406
+ self.well_display_options[i].setChecked(True)
407
+ self.well_display_options[i].setStyleSheet("font-size: 12px;")
408
+ self.well_display_options[i].toggled.connect(self.select_lines)
409
+ self.well_display_options[i].setToolTip(self.usable_well_labels[i])
410
+ else:
411
+ self.pos_display_options = [
412
+ QCheckBox(self.pos_names[i]) for i in range(len(self.pos_names))
413
+ ]
414
+ for i in range(len(self.pos_names)):
415
+ self.line_check_vbox.addWidget(
416
+ self.pos_display_options[i],
417
+ i % 4,
418
+ i // 4,
419
+ 1,
420
+ 1,
421
+ alignment=Qt.AlignCenter,
422
+ )
423
+ self.pos_display_options[i].setChecked(True)
424
+ self.pos_display_options[i].setStyleSheet("font-size: 12px;")
425
+ self.pos_display_options[i].toggled.connect(self.select_lines)
426
+
427
+ self.layout.addWidget(self.line_choice_widget)
428
+ # self.layout.addLayout(self.line_check_vbox)
429
+
430
+ def look_for_metadata(self):
431
+ self.metadata_found = False
432
+ self.metafiles = (
433
+ glob(
434
+ self.parent_window.exp_dir
435
+ + os.sep.join([f"W*", "*", "movie", "*metadata.txt"])
436
+ )
437
+ + glob(
438
+ self.parent_window.exp_dir + os.sep.join([f"W*", "*", "*metadata.txt"])
439
+ )
440
+ + glob(self.parent_window.exp_dir + os.sep.join([f"W*", "*metadata.txt"]))
441
+ + glob(self.parent_window.exp_dir + "*metadata.txt")
442
+ )
443
+ print(f"Found {len(self.metafiles)} metadata files...")
444
+ if len(self.metafiles) > 0:
445
+ self.metadata_found = True
446
+
447
+ def load_coordinates(self):
448
+ """
449
+ Read metadata and try to extract position coordinates
450
+ """
451
+
452
+ self.no_meta = False
453
+ try:
454
+ with open(self.metafiles[0], "r") as f:
455
+ data = json.load(f)
456
+ positions = data["Summary"]["InitialPositionList"]
457
+ except Exception as e:
458
+ print(f"Trouble loading metadata: error {e}...")
459
+ return None
460
+
461
+ for k in range(len(positions)):
462
+ pos_label = positions[k]["Label"]
463
+ try:
464
+ coords = positions[k]["DeviceCoordinatesUm"]["XYStage"]
465
+ except:
466
+ try:
467
+ coords = positions[k]["DeviceCoordinatesUm"]["PIXYStage"]
468
+ except:
469
+ self.no_meta = True
470
+
471
+ if not self.no_meta:
472
+ files = self.df_pos_info["stack_path"].values
473
+ pos_loc = [pos_label in f for f in files]
474
+ self.df_pos_info.loc[pos_loc, "x"] = coords[0]
475
+ self.df_pos_info.loc[pos_loc, "y"] = coords[1]
476
+ self.df_pos_info.loc[pos_loc, "metadata_tag"] = pos_label
477
+
478
+ def plot_spatial_location(self):
479
+
480
+ try:
481
+ self.sc = self.ax_scatter.scatter(
482
+ self.df_pos_info["x"].values,
483
+ self.df_pos_info["y"].values,
484
+ picker=True,
485
+ pickradius=1,
486
+ color=self.select_color(self.df_pos_info["select"].values),
487
+ )
488
+ self.scat_labels = self.df_pos_info["metadata_tag"].values
489
+ self.ax_scatter.invert_xaxis()
490
+
491
+ self.annot = self.ax_scatter.annotate(
492
+ "",
493
+ xy=(0, 0),
494
+ xytext=(10, 10),
495
+ textcoords="offset points",
496
+ bbox=dict(boxstyle="round", fc="w"),
497
+ arrowprops=dict(arrowstyle="->"),
498
+ )
499
+ self.annot.set_visible(False)
500
+
501
+ xmin, xmax = self.ax_scatter.get_xlim()
502
+ ymin, ymax = self.ax_scatter.get_ylim()
503
+ xdatarange = xmax - xmin
504
+ ydatarange = ymax - ymin
505
+
506
+ self.ax_scatter.set_xlim(xmin - 0.1 * xdatarange, xmax + 0.1 * xdatarange)
507
+ self.ax_scatter.set_ylim(ymin - 0.1 * ydatarange, ymax + 0.1 * ydatarange)
508
+
509
+ # xmin,xmax = self.ax_scatter.get_xlim()
510
+ # ymin,ymax = self.ax_scatter.get_ylim()
511
+ # desired_a = 4
512
+ # new_x_max = xmin + desired_a * (ymax - ymin)
513
+ # self.ax_scatter.set_xlim(xmin - (new_x_max - xmin)/2.0, new_x_max - (new_x_max - xmin)/2.0)
514
+
515
+ self.fig_scatter.tight_layout()
516
+ self.fig_scatter.canvas.mpl_connect("motion_notify_event", self.hover)
517
+ self.fig_scatter.canvas.mpl_connect("pick_event", self.unselect_position)
518
+ except Exception as e:
519
+ pass
520
+
521
+ def update_annot(self, ind):
522
+
523
+ pos = self.sc.get_offsets()[ind["ind"][0]]
524
+ self.annot.xy = pos
525
+ text = self.scat_labels[ind["ind"][0]]
526
+ self.annot.set_text(text)
527
+ self.annot.get_bbox_patch().set_facecolor("k")
528
+ self.annot.get_bbox_patch().set_alpha(0.4)
529
+
530
+ def hover(self, event):
531
+ vis = self.annot.get_visible()
532
+ if event.inaxes == self.ax_scatter:
533
+ cont, ind = self.sc.contains(event)
534
+ if cont:
535
+ self.update_annot(ind)
536
+ self.annot.set_visible(True)
537
+ self.fig_scatter.canvas.draw_idle()
538
+ else:
539
+ if vis:
540
+ self.annot.set_visible(False)
541
+ self.fig_scatter.canvas.draw_idle()
542
+
543
+ def unselect_position(self, event):
544
+
545
+ ind = event.ind # index of selected position
546
+ well_idx = self.df_pos_info.iloc[ind]["well_index"].values[0]
547
+ selectedPos = self.df_pos_info.iloc[ind]["pos_path"].values[0]
548
+ currentSelState = self.df_pos_info.iloc[ind]["select"].values[0]
549
+ if self.plot_options[0].isChecked() or self.plot_options[2].isChecked():
550
+ self.df_pos_info.loc[
551
+ self.df_pos_info["well_index"] == well_idx, "select"
552
+ ] = not currentSelState
553
+ self.df_well_info.loc[
554
+ self.df_well_info["well_index"] == well_idx, "select"
555
+ ] = not currentSelState
556
+ if len(self.parent_window.well_indices) > 1:
557
+ self.well_display_options[well_idx].setChecked(not currentSelState)
558
+ else:
559
+ for p in self.pos_display_options:
560
+ p.setChecked(not currentSelState)
561
+ else:
562
+ self.df_pos_info.loc[
563
+ self.df_pos_info["pos_path"] == selectedPos, "select"
564
+ ] = not currentSelState
565
+ if len(self.parent_window.well_indices) <= 1:
566
+ self.pos_display_options[ind[0]].setChecked(not currentSelState)
567
+
568
+ self.sc.set_color(self.select_color(self.df_pos_info["select"].values))
569
+ self.position_scatter.canvas.draw_idle()
570
+ self.plot_signals(0)
571
+
572
+ def select_color(self, selection):
573
+ colors = [tab10(0) if s else tab10(0.1) for s in selection]
574
+ return colors
575
+
576
+ def initialize_axis(self):
577
+
578
+ previous_ymin, previous_ymax = self.ax.get_ylim()
579
+ previous_legend = self.legend_visible
580
+ is_log = self.ax.get_yscale()
581
+
582
+ self.ax.clear()
583
+ self.ax.plot([], [])
584
+
585
+ # Labels
586
+ self.ax.set_xlabel("time [min]")
587
+ self.ax.set_ylabel(self.feature_selected)
588
+
589
+ # Spines
590
+ self.ax.spines["top"].set_visible(False)
591
+ self.ax.spines["right"].set_visible(False)
592
+ self.ax.grid(
593
+ which="major", color="black", linestyle="-", linewidth=0.8, alpha=0.2
594
+ )
595
+ self.ax.grid(
596
+ which="minor", color="lightgray", linestyle="--", linewidth=0.5, alpha=0.1
597
+ )
598
+ # Lims
599
+ safe_df = self.df.dropna(subset=self.feature_selected)
600
+ values = safe_df[self.feature_selected].values
601
+ if len(values) > 0:
602
+ self.ax.set_ylim(
603
+ np.percentile(values, 1) * self.scaling_factor,
604
+ np.percentile(values, 99) * self.scaling_factor,
605
+ )
606
+ self.ax.set_xlim(
607
+ -(self.df["FRAME"].max() + 2) * self.parent_window.FrameToMin,
608
+ (self.df["FRAME"].max() + 2) * self.parent_window.FrameToMin,
609
+ )
610
+
611
+ if is_log == "log":
612
+ self.ax.set_yscale("log")
613
+ if previous_legend:
614
+ leg = self.ax.get_legend()
615
+ if leg is not None:
616
+ leg.set_visible(True)
617
+
618
+ def show_hide_legend(self):
619
+
620
+ if self.legend_visible:
621
+ leg = self.ax.get_legend()
622
+ leg.set_visible(False)
623
+ self.legend_visible = False
624
+ self.legend_btn.setIcon(icon(MDI6.text_box, color="black"))
625
+ else:
626
+ leg = self.ax.get_legend()
627
+ leg.set_visible(True)
628
+ self.legend_visible = True
629
+ self.legend_btn.setIcon(icon(MDI6.text_box, color=self.help_color))
630
+
631
+ self.plot_widget.canvas.draw_idle()
632
+
633
+ def switch_to_log(self):
634
+ """
635
+ Switch threshold histogram to log scale. Auto adjust.
636
+ """
637
+
638
+ if self.ax.get_yscale() == "linear":
639
+ self.ax.set_yscale("log")
640
+ self.log_btn.setIcon(icon(MDI6.math_log, color=self.help_color))
641
+ # self.ax.set_ylim(0.01,1.05)
642
+ else:
643
+ self.ax.set_yscale("linear")
644
+ self.log_btn.setIcon(icon(MDI6.math_log, color="black"))
645
+ # self.ax.set_ylim(0.01,1.05)
646
+
647
+ # self.ax.autoscale()
648
+ self.plot_widget.canvas.draw_idle()
649
+
650
+ def plot_signals(self, id):
651
+
652
+ for i in range(3):
653
+ if self.plot_options[i].isChecked():
654
+ self.plot_mode = self.radio_labels[i]
655
+
656
+ if self.target_class == [0, 1]:
657
+ mean_signal_type = "mean_all"
658
+ std_signal = "std_all"
659
+ matrix = "matrix_all"
660
+ elif self.target_class == [0]:
661
+ mean_signal_type = "mean_event"
662
+ std_signal = "std_event"
663
+ matrix = "matrix_event"
664
+ else:
665
+ mean_signal_type = "mean_no_event"
666
+ std_signal = "std_no_event"
667
+ matrix = "matrix_no_event"
668
+
669
+ colors = np.array(
670
+ [self.cmap(i / len(self.df_pos_info)) for i in range(len(self.df_pos_info))]
671
+ )
672
+ well_color = [
673
+ self.cmap(i / len(self.df_well_info)) for i in range(len(self.df_well_info))
674
+ ]
675
+
676
+ if self.plot_mode == "position":
677
+ self.initialize_axis()
678
+ lines = self.df_pos_info.loc[self.df_pos_info["select"], "signal"].values
679
+ pos_labels = self.df_pos_info.loc[
680
+ self.df_pos_info["select"], "pos_name"
681
+ ].values
682
+ pos_indices = self.df_pos_info.loc[
683
+ self.df_pos_info["select"], "pos_index"
684
+ ].values
685
+ well_index = self.df_pos_info.loc[
686
+ self.df_pos_info["select"], "well_index"
687
+ ].values
688
+ for i in range(len(lines)):
689
+ if len(self.parent_window.well_indices) <= 1:
690
+ self.plot_line(
691
+ lines[i],
692
+ colors[pos_indices[i]],
693
+ pos_labels[i],
694
+ mean_signal_type,
695
+ std_signal=std_signal,
696
+ ci_option=self.show_ci,
697
+ cell_lines_option=self.show_cell_lines,
698
+ matrix=matrix,
699
+ )
700
+ else:
701
+ self.plot_line(
702
+ lines[i],
703
+ well_color[well_index[i]],
704
+ pos_labels[i],
705
+ mean_signal_type,
706
+ std_signal=std_signal,
707
+ ci_option=self.show_ci,
708
+ cell_lines_option=self.show_cell_lines,
709
+ matrix=matrix,
710
+ )
711
+ if self.legend_visible:
712
+ self.ax.legend(ncols=3, fontsize="x-small")
713
+
714
+ elif self.plot_mode == "well":
715
+ self.initialize_axis()
716
+ lines = self.df_well_info.loc[self.df_well_info["select"], "signal"].values
717
+ well_index = self.df_well_info.loc[
718
+ self.df_well_info["select"], "well_index"
719
+ ].values
720
+ well_labels = self.df_well_info.loc[
721
+ self.df_well_info["select"], "well_name"
722
+ ].values
723
+ for i in range(len(lines)):
724
+ if len(self.parent_window.well_indices) <= 1:
725
+ self.plot_line(
726
+ lines[i],
727
+ "k",
728
+ well_labels[i],
729
+ mean_signal_type,
730
+ std_signal=std_signal,
731
+ ci_option=self.show_ci,
732
+ cell_lines_option=self.show_cell_lines,
733
+ matrix=matrix,
734
+ )
735
+ else:
736
+ self.plot_line(
737
+ lines[i],
738
+ well_color[well_index[i]],
739
+ well_labels[i],
740
+ mean_signal_type,
741
+ std_signal=std_signal,
742
+ ci_option=self.show_ci,
743
+ cell_lines_option=self.show_cell_lines,
744
+ matrix=matrix,
745
+ )
746
+ if self.legend_visible:
747
+ self.ax.legend(ncols=2, fontsize="x-small")
748
+
749
+ elif self.plot_mode == "both":
750
+
751
+ self.initialize_axis()
752
+ lines_pos = self.df_pos_info.loc[
753
+ self.df_pos_info["select"], "signal"
754
+ ].values
755
+ lines_well = self.df_well_info.loc[
756
+ self.df_well_info["select"], "signal"
757
+ ].values
758
+
759
+ pos_indices = self.df_pos_info.loc[
760
+ self.df_pos_info["select"], "pos_index"
761
+ ].values
762
+ well_index_pos = self.df_pos_info.loc[
763
+ self.df_pos_info["select"], "well_index"
764
+ ].values
765
+ well_index = self.df_well_info.loc[
766
+ self.df_well_info["select"], "well_index"
767
+ ].values
768
+ well_labels = self.df_well_info.loc[
769
+ self.df_well_info["select"], "well_name"
770
+ ].values
771
+ pos_labels = self.df_pos_info.loc[
772
+ self.df_pos_info["select"], "pos_name"
773
+ ].values
774
+
775
+ for i in range(len(lines_pos)):
776
+ if len(self.parent_window.well_indices) <= 1:
777
+ self.plot_line(
778
+ lines_pos[i],
779
+ colors[pos_indices[i]],
780
+ pos_labels[i],
781
+ mean_signal_type,
782
+ std_signal=std_signal,
783
+ ci_option=self.show_ci,
784
+ cell_lines_option=self.show_cell_lines,
785
+ matrix=matrix,
786
+ )
787
+ else:
788
+ self.plot_line(
789
+ lines_pos[i],
790
+ well_color[well_index_pos[i]],
791
+ None,
792
+ mean_signal_type,
793
+ std_signal=std_signal,
794
+ ci_option=False,
795
+ )
796
+
797
+ for i in range(len(lines_well)):
798
+ if len(self.parent_window.well_indices) <= 1:
799
+ self.plot_line(
800
+ lines_well[i],
801
+ "k",
802
+ "pool",
803
+ mean_signal_type,
804
+ std_signal=std_signal,
805
+ ci_option=False,
806
+ )
807
+ else:
808
+ self.plot_line(
809
+ lines_well[i],
810
+ well_color[well_index[i]],
811
+ well_labels[i],
812
+ mean_signal_type,
813
+ std_signal=std_signal,
814
+ ci_option=False,
815
+ )
816
+ if self.legend_visible:
817
+ self.ax.legend(ncols=3, fontsize="x-small")
818
+
819
+ self.plot_widget.canvas.draw()
820
+
821
+ def plot_line(
822
+ self,
823
+ line,
824
+ color,
825
+ label,
826
+ mean_signal_type,
827
+ ci_option=True,
828
+ cell_lines_option=False,
829
+ alpha_ci=0.5,
830
+ std_signal=None,
831
+ matrix=None,
832
+ ):
833
+
834
+ # Plot a signal
835
+ if line == line:
836
+ self.ax.plot(
837
+ line["timeline"] * self.parent_window.FrameToMin,
838
+ line[mean_signal_type] * self.scaling_factor,
839
+ color=color,
840
+ label=label,
841
+ )
842
+
843
+ if ci_option and std_signal is not None:
844
+
845
+ self.ax.fill_between(
846
+ line["timeline"] * self.parent_window.FrameToMin,
847
+ [
848
+ a - b
849
+ for a, b in zip(
850
+ line[mean_signal_type] * self.scaling_factor,
851
+ line[std_signal] * self.scaling_factor,
852
+ )
853
+ ],
854
+ [
855
+ a + b
856
+ for a, b in zip(
857
+ line[mean_signal_type] * self.scaling_factor,
858
+ line[std_signal] * self.scaling_factor,
859
+ )
860
+ ],
861
+ color=color,
862
+ alpha=alpha_ci,
863
+ )
864
+ if cell_lines_option and matrix is not None:
865
+ # Show individual cell signals
866
+ mat = line[matrix]
867
+ for i in range(mat.shape[0]):
868
+ self.ax.plot(
869
+ line["timeline"] * self.parent_window.FrameToMin,
870
+ mat[i, :] * self.scaling_factor,
871
+ color=color,
872
+ alpha=self.alpha_setting,
873
+ )
874
+
875
+ def switch_ci(self):
876
+
877
+ # Show the confidence interval / STD
878
+
879
+ if self.show_ci:
880
+ self.ci_btn.setIcon(icon(MDI6.arrow_expand_horizontal, color="black"))
881
+ else:
882
+ self.ci_btn.setIcon(
883
+ icon(MDI6.arrow_expand_horizontal, color=self.help_color)
884
+ )
885
+ self.show_ci = not self.show_ci
886
+ try:
887
+ self.plot_signals(0)
888
+ except Exception as e:
889
+ print(f"{e=}")
890
+
891
+ def switch_cell_lines(self):
892
+
893
+ # Show individual cell signals
894
+
895
+ if self.show_cell_lines:
896
+ self.cell_lines_btn.setIcon(icon(MDI6.view_headline, color="black"))
897
+ else:
898
+ self.cell_lines_btn.setIcon(icon(MDI6.view_headline, color=self.help_color))
899
+ self.show_cell_lines = not self.show_cell_lines
900
+ self.plot_signals(0)
901
+
902
+ def select_lines(self):
903
+
904
+ if len(self.parent_window.well_indices) > 1:
905
+ for i in range(len(self.well_display_options)):
906
+ self.df_well_info.loc[
907
+ self.df_well_info["well_index"] == i, "select"
908
+ ] = self.well_display_options[i].isChecked()
909
+ self.df_pos_info.loc[self.df_pos_info["well_index"] == i, "select"] = (
910
+ self.well_display_options[i].isChecked()
911
+ )
912
+ else:
913
+ for i in range(len(self.pos_display_options)):
914
+ self.df_pos_info.loc[self.df_pos_info["pos_index"] == i, "select"] = (
915
+ self.pos_display_options[i].isChecked()
916
+ )
917
+
918
+ if len(self.metafiles) > 0:
919
+ self.sc.set_color(self.select_color(self.df_pos_info["select"].values))
920
+ self.position_scatter.canvas.draw_idle()
921
+ self.plot_signals(0)
922
+
694
923
 
695
924
  class SurvivalPlotWidget(GenericSignalPlotWidget):
696
925
 
697
- def __init__(self, *args, **kwargs):
698
-
699
- super(SurvivalPlotWidget, self).__init__(*args, **kwargs)
700
- self.cell_lines_btn.hide()
701
- self.class_selection_widget.hide()
702
- self.rescale_widget.hide()
703
- self.cell_lines_alpha_wdg.hide()
704
- self.export_tabular_btn.show()
705
- self.export_tabular_btn.clicked.connect(self.set_table_options)
706
-
707
- def switch_to_log(self):
708
-
709
- """
710
- Switch threshold histogram to log scale. Auto adjust.
711
- """
712
-
713
- if self.ax.get_yscale()=='linear':
714
- ymin,_ = self.ax.get_ylim()
715
- self.ax.set_ylim(max(ymin,0.01),1.05)
716
- self.ax.set_yscale('log')
717
- self.log_btn.setIcon(icon(MDI6.math_log,color=self.help_color))
718
- else:
719
- self.ax.set_yscale('linear')
720
- self.log_btn.setIcon(icon(MDI6.math_log,color="black"))
721
- #self.ax.set_ylim(0.01,1.05)
722
-
723
- #self.ax.autoscale()
724
- self.plot_widget.canvas.draw_idle()
725
-
726
- def initialize_axis(self):
727
-
728
- previous_legend = self.legend_visible
729
- is_log = self.ax.get_yscale()
730
-
731
- self.ax.clear()
732
- self.ax.plot([],[])
733
-
734
- # Labels
735
- self.ax.set_xlabel('time [min]')
736
- self.ax.set_ylabel('survival')
737
-
738
- # Spines
739
- self.ax.spines['top'].set_visible(False)
740
- self.ax.spines['right'].set_visible(False)
741
-
742
- # Lims
743
- self.ax.set_xlim(0*self.parent_window.FrameToMin,(self.df['FRAME'].max()+2)*self.parent_window.FrameToMin)
744
- if is_log=='log':
745
- ymin = 0.1
746
- else:
747
- ymin = 0.0
748
- self.ax.set_ylim(ymin,1.05)
749
-
750
- if is_log=='log':
751
- self.ax.set_yscale('log')
752
- if previous_legend:
753
- leg = self.ax.get_legend()
754
- if leg is not None:
755
- leg.set_visible(True)
756
-
757
- def plot_signals(self, id):
758
-
759
- for i in range(3):
760
- if self.plot_options[i].isChecked():
761
- self.plot_mode = self.radio_labels[i]
762
-
763
- colors = np.array([self.cmap(i / len(self.df_pos_info)) for i in range(len(self.df_pos_info))])
764
- well_color = [self.cmap(i / len(self.df_well_info)) for i in range(len(self.df_well_info))]
765
-
766
- if self.plot_mode=='position':
767
- self.initialize_axis()
768
- lines = self.df_pos_info.loc[self.df_pos_info['select'],'survival_fit'].values
769
- pos_labels = self.df_pos_info.loc[self.df_pos_info['select'],'pos_name'].values
770
- pos_indices = self.df_pos_info.loc[self.df_pos_info['select'],'pos_index'].values
771
- well_index = self.df_pos_info.loc[self.df_pos_info['select'],'well_index'].values
772
- for i in range(len(lines)):
773
- if len(self.parent_window.well_indices)<=1:
774
- self.plot_line(lines[i], colors[pos_indices[i]], pos_labels[i], ci_option=self.show_ci)
775
- else:
776
- self.plot_line(lines[i], well_color[well_index[i]], pos_labels[i], ci_option=self.show_ci)
777
- if self.legend_visible:
778
- self.ax.legend(ncols=3,fontsize='x-small')
779
-
780
- elif self.plot_mode=='well':
781
- self.initialize_axis()
782
- lines = self.df_well_info.loc[self.df_well_info['select'],'survival_fit'].values
783
- well_index = self.df_well_info.loc[self.df_well_info['select'],'well_index'].values
784
- well_labels = self.df_well_info.loc[self.df_well_info['select'],'well_name'].values
785
- for i in range(len(lines)):
786
- if len(self.parent_window.well_indices)<=1:
787
- self.plot_line(lines[i], 'k', well_labels[i], ci_option=self.show_ci, legend=True)
788
- else:
789
- self.plot_line(lines[i], well_color[well_index[i]], well_labels[i], ci_option=self.show_ci, legend=True)
790
- if self.legend_visible:
791
- self.ax.legend(ncols=2, fontsize='x-small')
792
-
793
- elif self.plot_mode=='both':
794
-
795
- self.initialize_axis()
796
- lines_pos = self.df_pos_info.loc[self.df_pos_info['select'],'survival_fit'].values
797
- lines_well = self.df_well_info.loc[self.df_well_info['select'],'survival_fit'].values
798
-
799
- pos_indices = self.df_pos_info.loc[self.df_pos_info['select'],'pos_index'].values
800
- well_index_pos = self.df_pos_info.loc[self.df_pos_info['select'],'well_index'].values
801
- well_index = self.df_well_info.loc[self.df_well_info['select'],'well_index'].values
802
- well_labels = self.df_well_info.loc[self.df_well_info['select'],'well_name'].values
803
- pos_labels = self.df_pos_info.loc[self.df_pos_info['select'],'pos_name'].values
804
-
805
- for i in range(len(lines_pos)):
806
- if len(self.parent_window.well_indices)<=1:
807
- self.plot_line(lines_pos[i], colors[pos_indices[i]], pos_labels[i], ci_option=self.show_ci, legend=True)
808
- else:
809
- self.plot_line(lines_pos[i], well_color[well_index_pos[i]], None, ci_option=False)
810
-
811
- for i in range(len(lines_well)):
812
- if len(self.parent_window.well_indices)<=1:
813
- self.plot_line(lines_well[i], 'k', 'pool', ci_option=False, legend=True)
814
- else:
815
- self.plot_line(lines_well[i], well_color[well_index[i]], well_labels[i], ci_option=False, legend=True)
816
- if self.legend_visible:
817
- self.ax.legend(ncols=3,fontsize='x-small')
818
-
819
- self.plot_widget.canvas.draw()
820
-
821
- def plot_line(self, line, color, label, ci_option=True, legend=None, alpha_ci=0.5):
822
-
823
- # Plot a signal
824
- if line==line:
825
- line.plot_survival_function(ci_show=ci_option, ax=self.ax, legend=legend, color=color, label=label, xlabel='timeline [min]')
826
-
827
- def set_table_options(self):
828
-
829
- self.config_table_wg = CelldetectiveWidget()
830
- self.config_table_wg.setMinimumWidth(480)
831
- self.config_table_wg.setWindowTitle('Survival data')
832
-
833
- layout = QVBoxLayout()
834
- self.config_table_wg.setLayout(layout)
835
-
836
- self.all_values_rb = QRadioButton('tabulate all values')
837
- self.single_timepoint_rb = QRadioButton('survival at single timepoint [min]: ')
838
- self.ec_rb = QRadioButton(r'EC N% survival: ')
839
- self.all_values_rb.toggled.connect(self.activate_sliders)
840
- self.single_timepoint_rb.toggled.connect(self.activate_sliders)
841
-
842
- self.single_timepoint_slider = QLabeledSlider()
843
- self.single_timepoint_slider.setRange(0, int(self.df['FRAME'].max()*self.parent_window.FrameToMin))
844
- self.single_timepoint_slider.setValue(int(self.df['FRAME'].max()*self.parent_window.FrameToMin))
845
-
846
- self.ec_slider = QLabeledSlider()
847
- self.ec_slider.setRange(0, 100)
848
- self.ec_slider.setValue(50)
849
-
850
- self.ec_rb.toggled.connect(self.activate_sliders)
851
- self.all_values_rb.click()
852
-
853
- self.set_btn = QPushButton('Set')
854
- self.set_btn.setStyleSheet(self.button_style_sheet)
855
- self.set_btn.clicked.connect(self.assemble_survival_data)
856
-
857
- layout.addWidget(self.all_values_rb)
858
-
859
- single_tp_layout = QHBoxLayout()
860
- single_tp_layout.addWidget(self.single_timepoint_rb, 33)
861
- single_tp_layout.addWidget(self.single_timepoint_slider, 66)
862
- layout.addLayout(single_tp_layout)
863
-
864
- ec_layout = QHBoxLayout()
865
- ec_layout.addWidget(self.ec_rb, 33)
866
- ec_layout.addWidget(self.ec_slider, 66)
867
- layout.addLayout(ec_layout)
868
-
869
- layout.addWidget(self.set_btn)
870
- center_window(self.config_table_wg)
871
- self.config_table_wg.show()
872
-
873
- def activate_sliders(self):
874
- if self.all_values_rb.isChecked():
875
- self.single_timepoint_slider.setEnabled(False)
876
- self.ec_slider.setEnabled(False)
877
- elif self.single_timepoint_rb.isChecked():
878
- self.single_timepoint_slider.setEnabled(True)
879
- self.ec_slider.setEnabled(False)
880
- elif self.ec_rb.isChecked():
881
- self.ec_slider.setEnabled(True)
882
- self.single_timepoint_slider.setEnabled(False)
883
-
884
- def assemble_survival_data(self):
885
-
886
- if self.plot_options[0].isChecked():
887
- data = self.df_well_info
888
- groupby = ['well_path']
889
- if self.plot_options[1].isChecked():
890
- data = self.df_pos_info
891
- groupby = ['pos_path']
892
- if self.plot_options[2].isChecked():
893
- print('Not implemented yet... Please select "well" or "position" as grouping...')
894
- return None
895
-
896
- if self.all_values_rb.isChecked():
897
-
898
- survival_table = []
899
- tid=0
900
- for name,group in data.groupby(groupby):
901
- print(name)
902
- if groupby[0]=="pos_path":
903
- metadata = collect_experiment_metadata(pos_path=name[0])
904
- elif groupby[0]=="well_path":
905
- metadata = collect_experiment_metadata(well_path=name[0])
906
- ks_estimator = group['survival_fit'].values[0]
907
- if ks_estimator!=ks_estimator:
908
- continue
909
- timeline = list(ks_estimator.survival_function_.index)
910
- survival = ks_estimator.survival_function_['KM_estimate'].values
911
- lower_error = ks_estimator.confidence_interval_['KM_estimate_lower_0.95'].values
912
- upper_error = ks_estimator.confidence_interval_['KM_estimate_upper_0.95'].values
913
- for k in range(len(timeline)):
914
- dico = metadata.copy()
915
- dico.update({'TRACK_ID': tid,'FRAME': int(timeline[k] / self.parent_window.FrameToMin),'timeline': timeline[k], 'survival': survival[k], "event_fraction": 1-survival[k], 'KM_estimate_lower_0.95': lower_error[k], 'KM_estimate_upper_0.95': upper_error[k]})
916
- survival_table.append(dico)
917
- tid+=1
918
-
919
- survival_table = pd.DataFrame(survival_table)
920
- self.table = TableUI(survival_table, f"Survival data", plot_mode="plot_track_signals")
921
- self.table.show()
922
-
923
- elif self.single_timepoint_rb.isChecked():
924
-
925
- survival_table = []
926
- tid=0
927
- for name,group in data.groupby(groupby):
928
- print(name)
929
- if groupby[0]=="pos_path":
930
- metadata = collect_experiment_metadata(pos_path=name[0])
931
- elif groupby[0]=="well_path":
932
- metadata = collect_experiment_metadata(well_path=name[0])
933
- ks_estimator = group['survival_fit'].values[0]
934
- if ks_estimator!=ks_estimator:
935
- continue
936
- survival = ks_estimator.survival_function_at_times(self.single_timepoint_slider.value()).values[0]
937
- dico = metadata.copy()
938
- dico.update({'timepoint': self.single_timepoint_slider.value(), 'survival': survival, 'event_fraction': 1 - survival})
939
- survival_table.append(dico)
940
- tid+=1
941
-
942
- survival_table = pd.DataFrame(survival_table)
943
- self.table = TableUI(survival_table, f"Survival data", plot_mode="static")
944
- self.table.show()
945
-
946
- elif self.ec_rb.isChecked():
947
-
948
- survival_table = []
949
- tid=0
950
- for name,group in data.groupby(groupby):
951
- print(name)
952
- if groupby[0]=="pos_path":
953
- metadata = collect_experiment_metadata(pos_path=name[0])
954
- elif groupby[0]=="well_path":
955
- metadata = collect_experiment_metadata(well_path=name[0])
956
- ks_estimator = group['survival_fit'].values[0]
957
- if ks_estimator!=ks_estimator:
958
- continue
959
- survival = ks_estimator.survival_function_
960
- ecN = qth_survival_times(float(self.ec_slider.value())/100.0, survival)
961
- dico = metadata.copy()
962
- dico.update({"qth": int(self.ec_slider.value()), f'EC{int(self.ec_slider.value())}% [min]': ecN})
963
- survival_table.append(dico)
964
- tid+=1
965
-
966
- survival_table = pd.DataFrame(survival_table)
967
- self.table = TableUI(survival_table, f"Survival data", plot_mode="static")
968
- self.table.show()
926
+ def __init__(self, *args, **kwargs):
927
+
928
+ super(SurvivalPlotWidget, self).__init__(*args, **kwargs)
929
+ self.cell_lines_btn.hide()
930
+ self.class_selection_widget.hide()
931
+ self.rescale_widget.hide()
932
+ self.cell_lines_alpha_wdg.hide()
933
+ self.export_tabular_btn.show()
934
+ self.export_tabular_btn.clicked.connect(self.set_table_options)
935
+
936
+ def switch_to_log(self):
937
+ """
938
+ Switch threshold histogram to log scale. Auto adjust.
939
+ """
940
+
941
+ if self.ax.get_yscale() == "linear":
942
+ ymin, _ = self.ax.get_ylim()
943
+ self.ax.set_ylim(max(ymin, 0.01), 1.05)
944
+ self.ax.set_yscale("log")
945
+ self.log_btn.setIcon(icon(MDI6.math_log, color=self.help_color))
946
+ else:
947
+ self.ax.set_yscale("linear")
948
+ self.log_btn.setIcon(icon(MDI6.math_log, color="black"))
949
+ # self.ax.set_ylim(0.01,1.05)
950
+
951
+ # self.ax.autoscale()
952
+ self.plot_widget.canvas.draw_idle()
953
+
954
+ def initialize_axis(self):
955
+
956
+ previous_legend = self.legend_visible
957
+ is_log = self.ax.get_yscale()
958
+
959
+ self.ax.clear()
960
+ self.ax.plot([], [])
961
+
962
+ # Labels
963
+ self.ax.set_xlabel("time [min]")
964
+ self.ax.set_ylabel("survival")
965
+
966
+ # Spines
967
+ self.ax.spines["top"].set_visible(False)
968
+ self.ax.spines["right"].set_visible(False)
969
+
970
+ # Lims
971
+ self.ax.set_xlim(
972
+ 0 * self.parent_window.FrameToMin,
973
+ (self.df["FRAME"].max() + 2) * self.parent_window.FrameToMin,
974
+ )
975
+ if is_log == "log":
976
+ ymin = 0.1
977
+ else:
978
+ ymin = 0.0
979
+ self.ax.set_ylim(ymin, 1.05)
980
+
981
+ if is_log == "log":
982
+ self.ax.set_yscale("log")
983
+ if previous_legend:
984
+ leg = self.ax.get_legend()
985
+ if leg is not None:
986
+ leg.set_visible(True)
987
+
988
+ def plot_signals(self, id):
989
+
990
+ for i in range(3):
991
+ if self.plot_options[i].isChecked():
992
+ self.plot_mode = self.radio_labels[i]
993
+
994
+ colors = np.array(
995
+ [self.cmap(i / len(self.df_pos_info)) for i in range(len(self.df_pos_info))]
996
+ )
997
+ well_color = [
998
+ self.cmap(i / len(self.df_well_info)) for i in range(len(self.df_well_info))
999
+ ]
1000
+
1001
+ if self.plot_mode == "position":
1002
+ self.initialize_axis()
1003
+ lines = self.df_pos_info.loc[
1004
+ self.df_pos_info["select"], "survival_fit"
1005
+ ].values
1006
+ pos_labels = self.df_pos_info.loc[
1007
+ self.df_pos_info["select"], "pos_name"
1008
+ ].values
1009
+ pos_indices = self.df_pos_info.loc[
1010
+ self.df_pos_info["select"], "pos_index"
1011
+ ].values
1012
+ well_index = self.df_pos_info.loc[
1013
+ self.df_pos_info["select"], "well_index"
1014
+ ].values
1015
+ for i in range(len(lines)):
1016
+ if len(self.parent_window.well_indices) <= 1:
1017
+ self.plot_line(
1018
+ lines[i],
1019
+ colors[pos_indices[i]],
1020
+ pos_labels[i],
1021
+ ci_option=self.show_ci,
1022
+ )
1023
+ else:
1024
+ self.plot_line(
1025
+ lines[i],
1026
+ well_color[well_index[i]],
1027
+ pos_labels[i],
1028
+ ci_option=self.show_ci,
1029
+ )
1030
+ if self.legend_visible:
1031
+ self.ax.legend(ncols=3, fontsize="x-small")
1032
+
1033
+ elif self.plot_mode == "well":
1034
+ self.initialize_axis()
1035
+ lines = self.df_well_info.loc[
1036
+ self.df_well_info["select"], "survival_fit"
1037
+ ].values
1038
+ well_index = self.df_well_info.loc[
1039
+ self.df_well_info["select"], "well_index"
1040
+ ].values
1041
+ well_labels = self.df_well_info.loc[
1042
+ self.df_well_info["select"], "well_name"
1043
+ ].values
1044
+ for i in range(len(lines)):
1045
+ if len(self.parent_window.well_indices) <= 1:
1046
+ self.plot_line(
1047
+ lines[i],
1048
+ "k",
1049
+ well_labels[i],
1050
+ ci_option=self.show_ci,
1051
+ legend=True,
1052
+ )
1053
+ else:
1054
+ self.plot_line(
1055
+ lines[i],
1056
+ well_color[well_index[i]],
1057
+ well_labels[i],
1058
+ ci_option=self.show_ci,
1059
+ legend=True,
1060
+ )
1061
+ if self.legend_visible:
1062
+ self.ax.legend(ncols=2, fontsize="x-small")
1063
+
1064
+ elif self.plot_mode == "both":
1065
+
1066
+ self.initialize_axis()
1067
+ lines_pos = self.df_pos_info.loc[
1068
+ self.df_pos_info["select"], "survival_fit"
1069
+ ].values
1070
+ lines_well = self.df_well_info.loc[
1071
+ self.df_well_info["select"], "survival_fit"
1072
+ ].values
1073
+
1074
+ pos_indices = self.df_pos_info.loc[
1075
+ self.df_pos_info["select"], "pos_index"
1076
+ ].values
1077
+ well_index_pos = self.df_pos_info.loc[
1078
+ self.df_pos_info["select"], "well_index"
1079
+ ].values
1080
+ well_index = self.df_well_info.loc[
1081
+ self.df_well_info["select"], "well_index"
1082
+ ].values
1083
+ well_labels = self.df_well_info.loc[
1084
+ self.df_well_info["select"], "well_name"
1085
+ ].values
1086
+ pos_labels = self.df_pos_info.loc[
1087
+ self.df_pos_info["select"], "pos_name"
1088
+ ].values
1089
+
1090
+ for i in range(len(lines_pos)):
1091
+ if len(self.parent_window.well_indices) <= 1:
1092
+ self.plot_line(
1093
+ lines_pos[i],
1094
+ colors[pos_indices[i]],
1095
+ pos_labels[i],
1096
+ ci_option=self.show_ci,
1097
+ legend=True,
1098
+ )
1099
+ else:
1100
+ self.plot_line(
1101
+ lines_pos[i],
1102
+ well_color[well_index_pos[i]],
1103
+ None,
1104
+ ci_option=False,
1105
+ )
1106
+
1107
+ for i in range(len(lines_well)):
1108
+ if len(self.parent_window.well_indices) <= 1:
1109
+ self.plot_line(
1110
+ lines_well[i], "k", "pool", ci_option=False, legend=True
1111
+ )
1112
+ else:
1113
+ self.plot_line(
1114
+ lines_well[i],
1115
+ well_color[well_index[i]],
1116
+ well_labels[i],
1117
+ ci_option=False,
1118
+ legend=True,
1119
+ )
1120
+ if self.legend_visible:
1121
+ self.ax.legend(ncols=3, fontsize="x-small")
1122
+
1123
+ self.plot_widget.canvas.draw()
1124
+
1125
+ def plot_line(self, line, color, label, ci_option=True, legend=None, alpha_ci=0.5):
1126
+
1127
+ # Plot a signal
1128
+ if line == line:
1129
+ line.plot_survival_function(
1130
+ ci_show=ci_option,
1131
+ ax=self.ax,
1132
+ legend=legend,
1133
+ color=color,
1134
+ label=label,
1135
+ xlabel="timeline [min]",
1136
+ )
1137
+
1138
+ def set_table_options(self):
1139
+
1140
+ self.config_table_wg = CelldetectiveWidget()
1141
+ self.config_table_wg.setMinimumWidth(480)
1142
+ self.config_table_wg.setWindowTitle("Survival data")
1143
+
1144
+ layout = QVBoxLayout()
1145
+ self.config_table_wg.setLayout(layout)
1146
+
1147
+ self.all_values_rb = QRadioButton("tabulate all values")
1148
+ self.single_timepoint_rb = QRadioButton("survival at single timepoint [min]: ")
1149
+ self.ec_rb = QRadioButton(r"EC N% survival: ")
1150
+ self.all_values_rb.toggled.connect(self.activate_sliders)
1151
+ self.single_timepoint_rb.toggled.connect(self.activate_sliders)
1152
+
1153
+ self.single_timepoint_slider = QLabeledSlider()
1154
+ self.single_timepoint_slider.setRange(
1155
+ 0, int(self.df["FRAME"].max() * self.parent_window.FrameToMin)
1156
+ )
1157
+ self.single_timepoint_slider.setValue(
1158
+ int(self.df["FRAME"].max() * self.parent_window.FrameToMin)
1159
+ )
1160
+
1161
+ self.ec_slider = QLabeledSlider()
1162
+ self.ec_slider.setRange(0, 100)
1163
+ self.ec_slider.setValue(50)
1164
+
1165
+ self.ec_rb.toggled.connect(self.activate_sliders)
1166
+ self.all_values_rb.click()
1167
+
1168
+ self.set_btn = QPushButton("Set")
1169
+ self.set_btn.setStyleSheet(self.button_style_sheet)
1170
+ self.set_btn.clicked.connect(self.assemble_survival_data)
1171
+
1172
+ layout.addWidget(self.all_values_rb)
1173
+
1174
+ single_tp_layout = QHBoxLayout()
1175
+ single_tp_layout.addWidget(self.single_timepoint_rb, 33)
1176
+ single_tp_layout.addWidget(self.single_timepoint_slider, 66)
1177
+ layout.addLayout(single_tp_layout)
1178
+
1179
+ ec_layout = QHBoxLayout()
1180
+ ec_layout.addWidget(self.ec_rb, 33)
1181
+ ec_layout.addWidget(self.ec_slider, 66)
1182
+ layout.addLayout(ec_layout)
1183
+
1184
+ layout.addWidget(self.set_btn)
1185
+ center_window(self.config_table_wg)
1186
+ self.config_table_wg.show()
1187
+
1188
+ def activate_sliders(self):
1189
+ if self.all_values_rb.isChecked():
1190
+ self.single_timepoint_slider.setEnabled(False)
1191
+ self.ec_slider.setEnabled(False)
1192
+ elif self.single_timepoint_rb.isChecked():
1193
+ self.single_timepoint_slider.setEnabled(True)
1194
+ self.ec_slider.setEnabled(False)
1195
+ elif self.ec_rb.isChecked():
1196
+ self.ec_slider.setEnabled(True)
1197
+ self.single_timepoint_slider.setEnabled(False)
1198
+
1199
+ def assemble_survival_data(self):
1200
+
1201
+ if self.plot_options[0].isChecked():
1202
+ data = self.df_well_info
1203
+ groupby = ["well_path"]
1204
+ if self.plot_options[1].isChecked():
1205
+ data = self.df_pos_info
1206
+ groupby = ["pos_path"]
1207
+ if self.plot_options[2].isChecked():
1208
+ print(
1209
+ 'Not implemented yet... Please select "well" or "position" as grouping...'
1210
+ )
1211
+ return None
1212
+
1213
+ if self.all_values_rb.isChecked():
1214
+
1215
+ survival_table = []
1216
+ tid = 0
1217
+ for name, group in data.groupby(groupby):
1218
+ print(name)
1219
+ if groupby[0] == "pos_path":
1220
+ metadata = collect_experiment_metadata(pos_path=name[0])
1221
+ elif groupby[0] == "well_path":
1222
+ metadata = collect_experiment_metadata(well_path=name[0])
1223
+ ks_estimator = group["survival_fit"].values[0]
1224
+ if ks_estimator != ks_estimator:
1225
+ continue
1226
+ timeline = list(ks_estimator.survival_function_.index)
1227
+ survival = ks_estimator.survival_function_["KM_estimate"].values
1228
+ lower_error = ks_estimator.confidence_interval_[
1229
+ "KM_estimate_lower_0.95"
1230
+ ].values
1231
+ upper_error = ks_estimator.confidence_interval_[
1232
+ "KM_estimate_upper_0.95"
1233
+ ].values
1234
+ for k in range(len(timeline)):
1235
+ dico = metadata.copy()
1236
+ dico.update(
1237
+ {
1238
+ "TRACK_ID": tid,
1239
+ "FRAME": int(timeline[k] / self.parent_window.FrameToMin),
1240
+ "timeline": timeline[k],
1241
+ "survival": survival[k],
1242
+ "event_fraction": 1 - survival[k],
1243
+ "KM_estimate_lower_0.95": lower_error[k],
1244
+ "KM_estimate_upper_0.95": upper_error[k],
1245
+ }
1246
+ )
1247
+ survival_table.append(dico)
1248
+ tid += 1
1249
+
1250
+ survival_table = pd.DataFrame(survival_table)
1251
+ self.table = TableUI(
1252
+ survival_table, f"Survival data", plot_mode="plot_track_signals"
1253
+ )
1254
+ self.table.show()
1255
+
1256
+ elif self.single_timepoint_rb.isChecked():
1257
+
1258
+ survival_table = []
1259
+ tid = 0
1260
+ for name, group in data.groupby(groupby):
1261
+ print(name)
1262
+ if groupby[0] == "pos_path":
1263
+ metadata = collect_experiment_metadata(pos_path=name[0])
1264
+ elif groupby[0] == "well_path":
1265
+ metadata = collect_experiment_metadata(well_path=name[0])
1266
+ ks_estimator = group["survival_fit"].values[0]
1267
+ if ks_estimator != ks_estimator:
1268
+ continue
1269
+ survival = ks_estimator.survival_function_at_times(
1270
+ self.single_timepoint_slider.value()
1271
+ ).values[0]
1272
+ dico = metadata.copy()
1273
+ dico.update(
1274
+ {
1275
+ "timepoint": self.single_timepoint_slider.value(),
1276
+ "survival": survival,
1277
+ "event_fraction": 1 - survival,
1278
+ }
1279
+ )
1280
+ survival_table.append(dico)
1281
+ tid += 1
1282
+
1283
+ survival_table = pd.DataFrame(survival_table)
1284
+ self.table = TableUI(survival_table, f"Survival data", plot_mode="static")
1285
+ self.table.show()
1286
+
1287
+ elif self.ec_rb.isChecked():
1288
+
1289
+ survival_table = []
1290
+ tid = 0
1291
+ for name, group in data.groupby(groupby):
1292
+ print(name)
1293
+ if groupby[0] == "pos_path":
1294
+ metadata = collect_experiment_metadata(pos_path=name[0])
1295
+ elif groupby[0] == "well_path":
1296
+ metadata = collect_experiment_metadata(well_path=name[0])
1297
+ ks_estimator = group["survival_fit"].values[0]
1298
+ if ks_estimator != ks_estimator:
1299
+ continue
1300
+ survival = ks_estimator.survival_function_
1301
+ ecN = qth_survival_times(
1302
+ float(self.ec_slider.value()) / 100.0, survival
1303
+ )
1304
+ dico = metadata.copy()
1305
+ dico.update(
1306
+ {
1307
+ "qth": int(self.ec_slider.value()),
1308
+ f"EC{int(self.ec_slider.value())}% [min]": ecN,
1309
+ }
1310
+ )
1311
+ survival_table.append(dico)
1312
+ tid += 1
1313
+
1314
+ survival_table = pd.DataFrame(survival_table)
1315
+ self.table = TableUI(survival_table, f"Survival data", plot_mode="static")
1316
+ self.table.show()