celldetective 1.4.2__py3-none-any.whl → 1.5.0b0__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 (151) 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 +304 -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/measure_cells.py +565 -0
  81. celldetective/processes/segment_cells.py +760 -0
  82. celldetective/processes/track_cells.py +435 -0
  83. celldetective/processes/train_segmentation_model.py +694 -0
  84. celldetective/processes/train_signal_model.py +265 -0
  85. celldetective/processes/unified_process.py +292 -0
  86. celldetective/regionprops/_regionprops.py +358 -317
  87. celldetective/relative_measurements.py +987 -710
  88. celldetective/scripts/measure_cells.py +313 -212
  89. celldetective/scripts/measure_relative.py +90 -46
  90. celldetective/scripts/segment_cells.py +165 -104
  91. celldetective/scripts/segment_cells_thresholds.py +96 -68
  92. celldetective/scripts/track_cells.py +198 -149
  93. celldetective/scripts/train_segmentation_model.py +324 -201
  94. celldetective/scripts/train_signal_model.py +87 -45
  95. celldetective/segmentation.py +844 -749
  96. celldetective/signals.py +3514 -2861
  97. celldetective/tracking.py +30 -15
  98. celldetective/utils/__init__.py +0 -0
  99. celldetective/utils/cellpose_utils/__init__.py +133 -0
  100. celldetective/utils/color_mappings.py +42 -0
  101. celldetective/utils/data_cleaning.py +630 -0
  102. celldetective/utils/data_loaders.py +450 -0
  103. celldetective/utils/dataset_helpers.py +207 -0
  104. celldetective/utils/downloaders.py +197 -0
  105. celldetective/utils/event_detection/__init__.py +8 -0
  106. celldetective/utils/experiment.py +1782 -0
  107. celldetective/utils/image_augmenters.py +308 -0
  108. celldetective/utils/image_cleaning.py +74 -0
  109. celldetective/utils/image_loaders.py +926 -0
  110. celldetective/utils/image_transforms.py +335 -0
  111. celldetective/utils/io.py +62 -0
  112. celldetective/utils/mask_cleaning.py +348 -0
  113. celldetective/utils/mask_transforms.py +5 -0
  114. celldetective/utils/masks.py +184 -0
  115. celldetective/utils/maths.py +351 -0
  116. celldetective/utils/model_getters.py +325 -0
  117. celldetective/utils/model_loaders.py +296 -0
  118. celldetective/utils/normalization.py +380 -0
  119. celldetective/utils/parsing.py +465 -0
  120. celldetective/utils/plots/__init__.py +0 -0
  121. celldetective/utils/plots/regression.py +53 -0
  122. celldetective/utils/resources.py +34 -0
  123. celldetective/utils/stardist_utils/__init__.py +104 -0
  124. celldetective/utils/stats.py +90 -0
  125. celldetective/utils/types.py +21 -0
  126. {celldetective-1.4.2.dist-info → celldetective-1.5.0b0.dist-info}/METADATA +1 -1
  127. celldetective-1.5.0b0.dist-info/RECORD +187 -0
  128. {celldetective-1.4.2.dist-info → celldetective-1.5.0b0.dist-info}/WHEEL +1 -1
  129. tests/gui/test_new_project.py +129 -117
  130. tests/gui/test_project.py +127 -79
  131. tests/test_filters.py +39 -15
  132. tests/test_notebooks.py +8 -0
  133. tests/test_tracking.py +232 -13
  134. tests/test_utils.py +123 -77
  135. celldetective/gui/base_components.py +0 -23
  136. celldetective/gui/layouts.py +0 -1602
  137. celldetective/gui/processes/compute_neighborhood.py +0 -594
  138. celldetective/gui/processes/measure_cells.py +0 -360
  139. celldetective/gui/processes/segment_cells.py +0 -499
  140. celldetective/gui/processes/track_cells.py +0 -303
  141. celldetective/gui/processes/train_segmentation_model.py +0 -270
  142. celldetective/gui/processes/train_signal_model.py +0 -108
  143. celldetective/gui/table_ops/merge_groups.py +0 -118
  144. celldetective/gui/viewers.py +0 -1354
  145. celldetective/io.py +0 -3663
  146. celldetective/utils.py +0 -3108
  147. celldetective-1.4.2.dist-info/RECORD +0 -123
  148. /celldetective/{gui/processes → processes}/downloader.py +0 -0
  149. {celldetective-1.4.2.dist-info → celldetective-1.5.0b0.dist-info}/entry_points.txt +0 -0
  150. {celldetective-1.4.2.dist-info → celldetective-1.5.0b0.dist-info}/licenses/LICENSE +0 -0
  151. {celldetective-1.4.2.dist-info → celldetective-1.5.0b0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,295 @@
1
+ import numpy as np
2
+ from PyQt5.QtCore import Qt
3
+ from PyQt5.QtGui import QDoubleValidator
4
+ from PyQt5.QtWidgets import (
5
+ QVBoxLayout,
6
+ QComboBox,
7
+ QHBoxLayout,
8
+ QLabel,
9
+ QRadioButton,
10
+ QButtonGroup,
11
+ QPushButton,
12
+ QMessageBox,
13
+ QLineEdit,
14
+ )
15
+ from superqt import QLabeledSlider
16
+
17
+ from celldetective.gui.base.components import CelldetectiveWidget
18
+ from celldetective.gui.base.utils import center_window
19
+ from celldetective.gui.gui_utils import PandasModel, GenericOpColWidget
20
+ from celldetective import get_logger
21
+ from celldetective.utils.maths import differentiate_per_track, safe_log
22
+
23
+ logger = get_logger(__name__)
24
+
25
+
26
+ class DifferentiateColWidget(CelldetectiveWidget):
27
+
28
+ def __init__(self, parent_window, column=None):
29
+
30
+ super().__init__()
31
+ self.parent_window = parent_window
32
+ self.column = column
33
+
34
+ self.setWindowTitle("d/dt")
35
+ # Create the QComboBox and add some items
36
+ center_window(self)
37
+
38
+ layout = QVBoxLayout(self)
39
+ layout.setContentsMargins(30, 30, 30, 30)
40
+
41
+ self.measurements_cb = QComboBox()
42
+ self.measurements_cb.addItems(list(self.parent_window.data.columns))
43
+ if self.column is not None:
44
+ idx = self.measurements_cb.findText(self.column)
45
+ self.measurements_cb.setCurrentIndex(idx)
46
+
47
+ measurement_layout = QHBoxLayout()
48
+ measurement_layout.addWidget(QLabel("measurements: "), 25)
49
+ measurement_layout.addWidget(self.measurements_cb, 75)
50
+ layout.addLayout(measurement_layout)
51
+
52
+ self.window_size_slider = QLabeledSlider()
53
+ self.window_size_slider.setRange(
54
+ 1, int(np.nanmax(self.parent_window.data.FRAME.to_numpy()))
55
+ )
56
+ self.window_size_slider.setValue(3)
57
+ window_layout = QHBoxLayout()
58
+ window_layout.addWidget(QLabel("window size: "), 25)
59
+ window_layout.addWidget(self.window_size_slider, 75)
60
+ layout.addLayout(window_layout)
61
+
62
+ self.backward_btn = QRadioButton("backward")
63
+ self.bi_btn = QRadioButton("bi")
64
+ self.bi_btn.click()
65
+ self.forward_btn = QRadioButton("forward")
66
+ self.mode_btn_group = QButtonGroup()
67
+ self.mode_btn_group.addButton(self.backward_btn)
68
+ self.mode_btn_group.addButton(self.bi_btn)
69
+ self.mode_btn_group.addButton(self.forward_btn)
70
+
71
+ mode_layout = QHBoxLayout()
72
+ mode_layout.addWidget(QLabel("mode: "), 25)
73
+ mode_sublayout = QHBoxLayout()
74
+ mode_sublayout.addWidget(self.backward_btn, 33, alignment=Qt.AlignCenter)
75
+ mode_sublayout.addWidget(self.bi_btn, 33, alignment=Qt.AlignCenter)
76
+ mode_sublayout.addWidget(self.forward_btn, 33, alignment=Qt.AlignCenter)
77
+ mode_layout.addLayout(mode_sublayout, 75)
78
+ layout.addLayout(mode_layout)
79
+
80
+ self.submit_btn = QPushButton("Compute")
81
+ self.submit_btn.setStyleSheet(self.button_style_sheet)
82
+ self.submit_btn.clicked.connect(self.compute_derivative_and_add_new_column)
83
+ layout.addWidget(self.submit_btn, 30)
84
+
85
+ self.setAttribute(Qt.WA_DeleteOnClose)
86
+
87
+ def compute_derivative_and_add_new_column(self):
88
+
89
+ if self.bi_btn.isChecked():
90
+ mode = "bi"
91
+ elif self.forward_btn.isChecked():
92
+ mode = "forward"
93
+ elif self.backward_btn.isChecked():
94
+ mode = "backward"
95
+ self.parent_window.data = differentiate_per_track(
96
+ self.parent_window.data,
97
+ self.measurements_cb.currentText(),
98
+ window_size=self.window_size_slider.value(),
99
+ mode=mode,
100
+ )
101
+ self.parent_window.model = PandasModel(self.parent_window.data)
102
+ self.parent_window.table_view.setModel(self.parent_window.model)
103
+ self.close()
104
+
105
+
106
+ class OperationOnColsWidget(CelldetectiveWidget):
107
+
108
+ def __init__(self, parent_window, column1=None, column2=None, operation="divide"):
109
+
110
+ super().__init__()
111
+ self.parent_window = parent_window
112
+ self.column1 = column1
113
+ self.column2 = column2
114
+ self.operation = operation
115
+
116
+ self.setWindowTitle(self.operation)
117
+ # Create the QComboBox and add some items
118
+ center_window(self)
119
+
120
+ layout = QVBoxLayout(self)
121
+ layout.setContentsMargins(30, 30, 30, 30)
122
+
123
+ self.col1_cb = QComboBox()
124
+ self.col1_cb.addItems(list(self.parent_window.data.columns))
125
+ if self.column1 is not None:
126
+ idx = self.col1_cb.findText(self.column1)
127
+ self.col1_cb.setCurrentIndex(idx)
128
+
129
+ numerator_layout = QHBoxLayout()
130
+ numerator_layout.addWidget(QLabel("column 1: "), 25)
131
+ numerator_layout.addWidget(self.col1_cb, 75)
132
+ layout.addLayout(numerator_layout)
133
+
134
+ self.col2_cb = QComboBox()
135
+ self.col2_cb.addItems(list(self.parent_window.data.columns))
136
+ if self.column2 is not None:
137
+ idx = self.col2_cb.findText(self.column2)
138
+ self.col2_cb.setCurrentIndex(idx)
139
+
140
+ denominator_layout = QHBoxLayout()
141
+ denominator_layout.addWidget(QLabel("column 2: "), 25)
142
+ denominator_layout.addWidget(self.col2_cb, 75)
143
+ layout.addLayout(denominator_layout)
144
+
145
+ self.submit_btn = QPushButton("Compute")
146
+ self.submit_btn.setStyleSheet(self.button_style_sheet)
147
+ self.submit_btn.clicked.connect(self.compute)
148
+ layout.addWidget(self.submit_btn, 30)
149
+
150
+ self.setAttribute(Qt.WA_DeleteOnClose)
151
+
152
+ def compute(self):
153
+
154
+ test = self._check_cols_before_operation()
155
+ if not test:
156
+ msg_box = QMessageBox()
157
+ msg_box.setIcon(QMessageBox.Warning)
158
+ msg_box.setText(
159
+ f"Operation could not be performed, one of the column types is object..."
160
+ )
161
+ msg_box.setWindowTitle("Warning")
162
+ msg_box.setStandardButtons(QMessageBox.Ok)
163
+ return_value = msg_box.exec()
164
+ if return_value == QMessageBox.Ok:
165
+ return None
166
+ else:
167
+ return None
168
+ else:
169
+ if self.operation == "divide":
170
+ name = f"{self.col1_txt}/{self.col2_txt}"
171
+ with np.errstate(divide="ignore", invalid="ignore"):
172
+ res = np.true_divide(self.col1, self.col2)
173
+ res[res == np.inf] = np.nan
174
+ res[self.col1 != self.col1] = np.nan
175
+ res[self.col2 != self.col2] = np.nan
176
+ self.parent_window.data[name] = res
177
+
178
+ elif self.operation == "multiply":
179
+ name = f"{self.col1_txt}*{self.col2_txt}"
180
+ res = np.multiply(self.col1, self.col2)
181
+
182
+ elif self.operation == "add":
183
+ name = f"{self.col1_txt}+{self.col2_txt}"
184
+ res = np.add(self.col1, self.col2)
185
+
186
+ elif self.operation == "subtract":
187
+ name = f"{self.col1_txt}-{self.col2_txt}"
188
+ res = np.subtract(self.col1, self.col2)
189
+ else:
190
+ logger.info(f"Operation {self.operation} not implemented...")
191
+
192
+ self.parent_window.data[name] = res
193
+ self.parent_window.model = PandasModel(self.parent_window.data)
194
+ self.parent_window.table_view.setModel(self.parent_window.model)
195
+ self.close()
196
+
197
+ def _check_cols_before_operation(self):
198
+
199
+ self.col1_txt = self.col1_cb.currentText()
200
+ self.col2_txt = self.col2_cb.currentText()
201
+
202
+ self.col1 = self.parent_window.data[self.col1_txt].to_numpy()
203
+ self.col2 = self.parent_window.data[self.col2_txt].to_numpy()
204
+
205
+ test = np.all([self.col1.dtype != "O", self.col2.dtype != "O"])
206
+
207
+ return test
208
+
209
+
210
+ class CalibrateColWidget(GenericOpColWidget):
211
+
212
+ def __init__(self, *args, **kwargs):
213
+
214
+ super().__init__(title="Calibrate data", *args, **kwargs)
215
+
216
+ self.floatValidator = QDoubleValidator()
217
+ self.calibration_factor_le = QLineEdit("1")
218
+ self.calibration_factor_le.setPlaceholderText(
219
+ "multiplicative calibration factor..."
220
+ )
221
+ self.calibration_factor_le.setValidator(self.floatValidator)
222
+
223
+ self.units_le = QLineEdit("um")
224
+ self.units_le.setPlaceholderText("units...")
225
+
226
+ self.calibration_factor_le.textChanged.connect(self.check_valid_params)
227
+ self.units_le.textChanged.connect(self.check_valid_params)
228
+
229
+ calib_layout = QHBoxLayout()
230
+ calib_layout.addWidget(QLabel("calibration factor: "), 33)
231
+ calib_layout.addWidget(self.calibration_factor_le, 66)
232
+ self.sublayout.addLayout(calib_layout)
233
+
234
+ units_layout = QHBoxLayout()
235
+ units_layout.addWidget(QLabel("units: "), 33)
236
+ units_layout.addWidget(self.units_le, 66)
237
+ self.sublayout.addLayout(units_layout)
238
+
239
+ # info_layout = QHBoxLayout()
240
+ # info_layout.addWidget(QLabel('For reference: '))
241
+ # self.sublayout.addLayout(info_layout)
242
+
243
+ # info_layout2 = QHBoxLayout()
244
+ # info_layout2.addWidget(QLabel(f'PxToUm = {self.parent_window.parent_window.parent_window.PxToUm}'), 50)
245
+ # info_layout2.addWidget(QLabel(f'FrameToMin = {self.parent_window.parent_window.parent_window.FrameToMin}'), 50)
246
+ # self.sublayout.addLayout(info_layout2)
247
+
248
+ def check_valid_params(self):
249
+
250
+ try:
251
+ factor = float(self.calibration_factor_le.text().replace(",", "."))
252
+ factor_valid = True
253
+ except Exception as _:
254
+ factor_valid = False
255
+
256
+ if self.units_le.text() == "":
257
+ units_valid = False
258
+ else:
259
+ units_valid = True
260
+
261
+ if factor_valid and units_valid:
262
+ self.submit_btn.setEnabled(True)
263
+ else:
264
+ self.submit_btn.setEnabled(False)
265
+
266
+ def compute(self):
267
+ self.parent_window.data[
268
+ self.measurements_cb.currentText() + f"[{self.units_le.text()}]"
269
+ ] = self.parent_window.data[self.measurements_cb.currentText()] * float(
270
+ self.calibration_factor_le.text().replace(",", ".")
271
+ )
272
+
273
+
274
+ class AbsColWidget(GenericOpColWidget):
275
+
276
+ def __init__(self, *args, **kwargs):
277
+
278
+ super().__init__(title="abs(.)", *args, **kwargs)
279
+
280
+ def compute(self):
281
+ self.parent_window.data["|" + self.measurements_cb.currentText() + "|"] = (
282
+ self.parent_window.data[self.measurements_cb.currentText()].abs()
283
+ )
284
+
285
+
286
+ class LogColWidget(GenericOpColWidget):
287
+
288
+ def __init__(self, *args, **kwargs):
289
+
290
+ super().__init__(title="log10(.)", *args, **kwargs)
291
+
292
+ def compute(self):
293
+ self.parent_window.data["log10(" + self.measurements_cb.currentText() + ")"] = (
294
+ safe_log(self.parent_window.data[self.measurements_cb.currentText()].values)
295
+ )
@@ -0,0 +1,140 @@
1
+ from typing import List
2
+
3
+ import numpy as np
4
+ from PyQt5.QtCore import QSize, Qt
5
+ from PyQt5.QtWidgets import (
6
+ QComboBox,
7
+ QHBoxLayout,
8
+ QLabel,
9
+ QLineEdit,
10
+ QPushButton,
11
+ QVBoxLayout,
12
+ )
13
+ from fonticon_mdi6 import MDI6
14
+ from superqt.fonticon import icon
15
+
16
+ from celldetective.gui.base.components import CelldetectiveWidget
17
+ from celldetective.gui.gui_utils import PandasModel
18
+ from celldetective.gui.base.utils import center_window
19
+
20
+
21
+ class MergeGroupWidget(CelldetectiveWidget):
22
+ def __init__(self, parent_window, columns: List[str] = [], n_cols_init: int = 3):
23
+
24
+ super().__init__()
25
+ self.parent_window = parent_window
26
+
27
+ self.setWindowTitle("Merge classifications")
28
+ self.group_cols = [
29
+ c
30
+ for c in list(self.parent_window.data.columns)
31
+ if c.startswith("group_") or c.startswith("status_")
32
+ ]
33
+ self.group_cols.insert(0, "--")
34
+ if len(columns) > n_cols_init:
35
+ n_cols_init = len(columns)
36
+
37
+ center_window(self)
38
+
39
+ layout = QVBoxLayout(self)
40
+ layout.setContentsMargins(30, 10, 30, 30)
41
+
42
+ label = QLabel(
43
+ "Merge several binary or multi-label classification features into a multi-label classification feature where each state is one of the possible combinations.\n"
44
+ )
45
+
46
+ label.setWordWrap(True) # enables automatic line breaking
47
+ label.setTextInteractionFlags(
48
+ Qt.TextSelectableByMouse
49
+ ) # optional, to allow copy
50
+ label.setStyleSheet("color: gray;") # optional style
51
+
52
+ layout.addWidget(label)
53
+
54
+ self.name_le = QLineEdit("group_multilabel")
55
+ name_layout = QHBoxLayout()
56
+ name_layout.addWidget(QLabel("name: "), 25)
57
+ name_layout.addWidget(self.name_le, 75)
58
+ layout.addLayout(name_layout)
59
+
60
+ self.cbs_layout = QVBoxLayout()
61
+ self.cbs_layout.setContentsMargins(0, 10, 0, 0)
62
+
63
+ self.cbs = []
64
+ for i in range(n_cols_init):
65
+ cb_i = QComboBox()
66
+ cb_i.addItems(self.group_cols)
67
+ if i < len(columns):
68
+ selection = columns[i]
69
+ idx = cb_i.findText(selection)
70
+ if idx >= 0:
71
+ cb_i.setCurrentIndex(idx)
72
+ else:
73
+ cb_i.setCurrentIndex(0)
74
+ self.cbs.append(cb_i)
75
+
76
+ col_layout = QHBoxLayout()
77
+ col_layout.addWidget(QLabel(f"state {i}: "), 25)
78
+ col_layout.addWidget(cb_i, 75)
79
+ self.cbs_layout.addLayout(col_layout)
80
+
81
+ layout.addLayout(self.cbs_layout)
82
+
83
+ self.add_feature_btn = QPushButton()
84
+ self.add_feature_btn.setIcon(icon(MDI6.plus, color=self.help_color))
85
+ self.add_feature_btn.setIconSize(QSize(20, 20))
86
+ self.add_feature_btn.clicked.connect(self.add_col)
87
+ self.add_feature_btn.setStyleSheet(self.button_select_all)
88
+ layout.addWidget(self.add_feature_btn, alignment=Qt.AlignRight)
89
+
90
+ self.submit_btn = QPushButton("Compute")
91
+ self.submit_btn.setStyleSheet(self.button_style_sheet)
92
+ self.submit_btn.clicked.connect(self.compute)
93
+ layout.addWidget(self.submit_btn, 30)
94
+
95
+ self.setAttribute(Qt.WA_DeleteOnClose)
96
+
97
+ def add_col(self):
98
+ cb_i = QComboBox()
99
+ cb_i.addItems(self.group_cols)
100
+ self.cbs.append(cb_i)
101
+
102
+ col_layout = QHBoxLayout()
103
+ col_layout.addWidget(QLabel(f"state {len(self.cbs)}: "), 25)
104
+ col_layout.addWidget(cb_i, 75)
105
+
106
+ self.cbs_layout.addLayout(col_layout)
107
+
108
+ def compute(self):
109
+
110
+ cols_to_merge = [
111
+ cb_i.currentText() for cb_i in self.cbs if cb_i.currentText() != "--"
112
+ ]
113
+ name = self.name_le.text()
114
+ if " " in name:
115
+ name.replace(" ", "_")
116
+ if name == "":
117
+ name = "multilabel"
118
+ if not name.startswith("group_"):
119
+ name = "group_" + name
120
+
121
+ if len(cols_to_merge) > 1:
122
+ print(
123
+ "Computing a multi-label classification from the classification feature sources..."
124
+ )
125
+ bases = [int(self.parent_window.data[c].max()) + 1 for c in cols_to_merge]
126
+ multipliers = np.concatenate(([1], np.cumprod(bases[:-1])))
127
+ self.parent_window.data[name] = (
128
+ self.parent_window.data[cols_to_merge] * multipliers
129
+ ).sum(axis=1)
130
+ self.parent_window.data.loc[
131
+ self.parent_window.data[cols_to_merge].isna().any(axis=1), name
132
+ ] = np.nan
133
+
134
+ self.parent_window.model = PandasModel(self.parent_window.data)
135
+ self.parent_window.table_view.setModel(self.parent_window.model)
136
+ self.close()
137
+ elif len(cols_to_merge) == 1:
138
+ print("Only one classification feature was selected, nothing to merge...")
139
+ else:
140
+ print("No classification feature was selected...")
@@ -0,0 +1,95 @@
1
+ from PyQt5.QtCore import Qt
2
+ from PyQt5.QtWidgets import QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QPushButton
3
+ from fonticon_mdi6 import MDI6
4
+ from superqt import QSearchableComboBox
5
+ from superqt.fonticon import icon
6
+
7
+ from celldetective.gui.base.components import CelldetectiveWidget
8
+ from celldetective.gui.base.utils import center_window
9
+ from celldetective.gui.gui_utils import PandasModel
10
+
11
+
12
+ class MergeOneHotWidget(CelldetectiveWidget):
13
+
14
+ def __init__(self, parent_window, selected_columns=None):
15
+
16
+ super().__init__()
17
+ self.parent_window = parent_window
18
+ self.selected_columns = selected_columns
19
+
20
+ self.setWindowTitle("Merge one-hot encoded columns...")
21
+ # Create the QComboBox and add some items
22
+ center_window(self)
23
+
24
+ self.layout = QVBoxLayout(self)
25
+ self.layout.setContentsMargins(30, 30, 30, 30)
26
+
27
+ if self.selected_columns is not None:
28
+ n_cols = len(self.selected_columns)
29
+ else:
30
+ n_cols = 2
31
+
32
+ name_hbox = QHBoxLayout()
33
+ name_hbox.addWidget(QLabel("New categorical column: "), 33)
34
+ self.new_col_le = QLineEdit()
35
+ self.new_col_le.setText("categorical_")
36
+ self.new_col_le.textChanged.connect(self.allow_merge)
37
+ name_hbox.addWidget(self.new_col_le, 66)
38
+ self.layout.addLayout(name_hbox)
39
+
40
+ self.layout.addWidget(QLabel("Source columns: "))
41
+
42
+ self.cbs = [QSearchableComboBox() for _ in range(n_cols)]
43
+ self.cbs_layout = QVBoxLayout()
44
+
45
+ for i in range(n_cols):
46
+ lay = QHBoxLayout()
47
+ lay.addWidget(QLabel(f"column {i}: "), 33)
48
+ self.cbs[i].addItems(["--"] + list(self.parent_window.data.columns))
49
+ if self.selected_columns is not None:
50
+ self.cbs[i].setCurrentText(self.selected_columns[i])
51
+ lay.addWidget(self.cbs[i], 66)
52
+ self.cbs_layout.addLayout(lay)
53
+
54
+ self.layout.addLayout(self.cbs_layout)
55
+
56
+ hbox = QHBoxLayout()
57
+ self.add_col_btn = QPushButton("Add column")
58
+ self.add_col_btn.clicked.connect(self.add_col)
59
+ self.add_col_btn.setStyleSheet(self.button_add)
60
+ self.add_col_btn.setIcon(icon(MDI6.plus, color="black"))
61
+
62
+ hbox.addWidget(QLabel(""), 50)
63
+ hbox.addWidget(self.add_col_btn, 50, alignment=Qt.AlignRight)
64
+ self.layout.addLayout(hbox)
65
+
66
+ self.submit_btn = QPushButton("Merge")
67
+ self.submit_btn.setStyleSheet(self.button_style_sheet)
68
+ self.submit_btn.clicked.connect(self.merge_cols)
69
+ self.layout.addWidget(self.submit_btn, 30)
70
+
71
+ self.setAttribute(Qt.WA_DeleteOnClose)
72
+
73
+ def add_col(self):
74
+ self.cbs.append(QSearchableComboBox())
75
+ self.cbs[-1].addItems(["--"] + list(self.parent_window.data.columns))
76
+ lay = QHBoxLayout()
77
+ lay.addWidget(QLabel(f"column {len(self.cbs)-1}: "), 33)
78
+ lay.addWidget(self.cbs[-1], 66)
79
+ self.cbs_layout.addLayout(lay)
80
+
81
+ def merge_cols(self):
82
+
83
+ self.parent_window.data[self.new_col_le.text()] = self.parent_window.data.loc[
84
+ :, list(self.selected_columns)
85
+ ].idxmax(axis=1)
86
+ self.parent_window.model = PandasModel(self.parent_window.data)
87
+ self.parent_window.table_view.setModel(self.parent_window.model)
88
+ self.close()
89
+
90
+ def allow_merge(self):
91
+
92
+ if self.new_col_le.text() == "":
93
+ self.submit_btn.setEnabled(False)
94
+ else:
95
+ self.submit_btn.setEnabled(True)
@@ -0,0 +1,43 @@
1
+ from PyQt5.QtWidgets import QHBoxLayout, QLineEdit, QPushButton
2
+
3
+ from celldetective.gui.base.components import CelldetectiveWidget
4
+ from celldetective.gui.base.utils import center_window
5
+
6
+
7
+ class QueryWidget(CelldetectiveWidget):
8
+
9
+ def __init__(self, parent_window):
10
+
11
+ super().__init__()
12
+ self.parent_window = parent_window
13
+
14
+ self.setWindowTitle("Filter table")
15
+ # Create the QComboBox and add some items
16
+
17
+ layout = QHBoxLayout(self)
18
+ layout.setContentsMargins(30, 30, 30, 30)
19
+ self.query_le = QLineEdit()
20
+ layout.addWidget(self.query_le, 70)
21
+
22
+ self.submit_btn = QPushButton("submit")
23
+ self.submit_btn.clicked.connect(self.filter_table)
24
+ layout.addWidget(self.submit_btn, 30)
25
+ center_window(self)
26
+
27
+ def filter_table(self):
28
+ from celldetective.gui.tableUI import TableUI
29
+
30
+ try:
31
+ query_text = self.query_le.text() # .replace('class', '`class`')
32
+ tab = self.parent_window.data.query(query_text)
33
+ self.subtable = TableUI(
34
+ tab,
35
+ query_text,
36
+ plot_mode="static",
37
+ population=self.parent_window.population,
38
+ )
39
+ self.subtable.show()
40
+ self.close()
41
+ except Exception as e:
42
+ print(e)
43
+ return None
@@ -0,0 +1,44 @@
1
+ from PyQt5.QtCore import Qt
2
+ from PyQt5.QtWidgets import QHBoxLayout, QLineEdit, QPushButton
3
+
4
+ from celldetective.gui.base.components import CelldetectiveWidget
5
+ from celldetective.gui.base.utils import center_window
6
+ from celldetective.gui.gui_utils import PandasModel
7
+
8
+
9
+ class RenameColWidget(CelldetectiveWidget):
10
+
11
+ def __init__(self, parent_window, column=None):
12
+
13
+ super().__init__()
14
+ self.parent_window = parent_window
15
+ self.column = column
16
+ if self.column is None:
17
+ self.column = ""
18
+
19
+ self.setWindowTitle("Rename column")
20
+ # Create the QComboBox and add some items
21
+ center_window(self)
22
+
23
+ layout = QHBoxLayout(self)
24
+ layout.setContentsMargins(30, 30, 30, 30)
25
+ self.new_col_name = QLineEdit()
26
+ self.new_col_name.setText(self.column)
27
+ layout.addWidget(self.new_col_name, 70)
28
+
29
+ self.submit_btn = QPushButton("rename")
30
+ self.submit_btn.clicked.connect(self.rename_col)
31
+ layout.addWidget(self.submit_btn, 30)
32
+ self.setAttribute(Qt.WA_DeleteOnClose)
33
+
34
+ def rename_col(self):
35
+
36
+ old_name = self.column
37
+ new_name = self.new_col_name.text()
38
+ self.parent_window.data = self.parent_window.data.rename(
39
+ columns={old_name: new_name}
40
+ )
41
+
42
+ self.parent_window.model = PandasModel(self.parent_window.data)
43
+ self.parent_window.table_view.setModel(self.parent_window.model)
44
+ self.close()