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.
- celldetective/__init__.py +25 -0
- celldetective/__main__.py +62 -43
- celldetective/_version.py +1 -1
- celldetective/extra_properties.py +477 -399
- celldetective/filters.py +192 -97
- celldetective/gui/InitWindow.py +541 -411
- celldetective/gui/__init__.py +0 -15
- celldetective/gui/about.py +44 -39
- celldetective/gui/analyze_block.py +120 -84
- celldetective/gui/base/__init__.py +0 -0
- celldetective/gui/base/channel_norm_generator.py +335 -0
- celldetective/gui/base/components.py +249 -0
- celldetective/gui/base/feature_choice.py +92 -0
- celldetective/gui/base/figure_canvas.py +52 -0
- celldetective/gui/base/list_widget.py +133 -0
- celldetective/gui/{styles.py → base/styles.py} +92 -36
- celldetective/gui/base/utils.py +33 -0
- celldetective/gui/base_annotator.py +900 -767
- celldetective/gui/classifier_widget.py +6 -22
- celldetective/gui/configure_new_exp.py +777 -671
- celldetective/gui/control_panel.py +635 -524
- celldetective/gui/dynamic_progress.py +449 -0
- celldetective/gui/event_annotator.py +2023 -1662
- celldetective/gui/generic_signal_plot.py +1292 -944
- celldetective/gui/gui_utils.py +899 -1289
- celldetective/gui/interactions_block.py +658 -0
- celldetective/gui/interactive_timeseries_viewer.py +447 -0
- celldetective/gui/json_readers.py +48 -15
- celldetective/gui/layouts/__init__.py +5 -0
- celldetective/gui/layouts/background_model_free_layout.py +537 -0
- celldetective/gui/layouts/channel_offset_layout.py +134 -0
- celldetective/gui/layouts/local_correction_layout.py +91 -0
- celldetective/gui/layouts/model_fit_layout.py +372 -0
- celldetective/gui/layouts/operation_layout.py +68 -0
- celldetective/gui/layouts/protocol_designer_layout.py +96 -0
- celldetective/gui/pair_event_annotator.py +3130 -2435
- celldetective/gui/plot_measurements.py +586 -267
- celldetective/gui/plot_signals_ui.py +724 -506
- celldetective/gui/preprocessing_block.py +395 -0
- celldetective/gui/process_block.py +1678 -1831
- celldetective/gui/seg_model_loader.py +580 -473
- celldetective/gui/settings/__init__.py +0 -7
- celldetective/gui/settings/_cellpose_model_params.py +181 -0
- celldetective/gui/settings/_event_detection_model_params.py +95 -0
- celldetective/gui/settings/_segmentation_model_params.py +159 -0
- celldetective/gui/settings/_settings_base.py +77 -65
- celldetective/gui/settings/_settings_event_model_training.py +752 -526
- celldetective/gui/settings/_settings_measurements.py +1133 -964
- celldetective/gui/settings/_settings_neighborhood.py +574 -488
- celldetective/gui/settings/_settings_segmentation_model_training.py +779 -564
- celldetective/gui/settings/_settings_signal_annotator.py +329 -305
- celldetective/gui/settings/_settings_tracking.py +1304 -1094
- celldetective/gui/settings/_stardist_model_params.py +98 -0
- celldetective/gui/survival_ui.py +422 -312
- celldetective/gui/tableUI.py +1665 -1701
- celldetective/gui/table_ops/_maths.py +295 -0
- celldetective/gui/table_ops/_merge_groups.py +140 -0
- celldetective/gui/table_ops/_merge_one_hot.py +95 -0
- celldetective/gui/table_ops/_query_table.py +43 -0
- celldetective/gui/table_ops/_rename_col.py +44 -0
- celldetective/gui/thresholds_gui.py +382 -179
- celldetective/gui/viewers/__init__.py +0 -0
- celldetective/gui/viewers/base_viewer.py +700 -0
- celldetective/gui/viewers/channel_offset_viewer.py +331 -0
- celldetective/gui/viewers/contour_viewer.py +394 -0
- celldetective/gui/viewers/size_viewer.py +153 -0
- celldetective/gui/viewers/spot_detection_viewer.py +341 -0
- celldetective/gui/viewers/threshold_viewer.py +309 -0
- celldetective/gui/workers.py +304 -126
- celldetective/log_manager.py +92 -0
- celldetective/measure.py +1895 -1478
- celldetective/napari/__init__.py +0 -0
- celldetective/napari/utils.py +1025 -0
- celldetective/neighborhood.py +1914 -1448
- celldetective/preprocessing.py +1620 -1220
- celldetective/processes/__init__.py +0 -0
- celldetective/processes/background_correction.py +271 -0
- celldetective/processes/compute_neighborhood.py +894 -0
- celldetective/processes/detect_events.py +246 -0
- celldetective/processes/measure_cells.py +565 -0
- celldetective/processes/segment_cells.py +760 -0
- celldetective/processes/track_cells.py +435 -0
- celldetective/processes/train_segmentation_model.py +694 -0
- celldetective/processes/train_signal_model.py +265 -0
- celldetective/processes/unified_process.py +292 -0
- celldetective/regionprops/_regionprops.py +358 -317
- celldetective/relative_measurements.py +987 -710
- celldetective/scripts/measure_cells.py +313 -212
- celldetective/scripts/measure_relative.py +90 -46
- celldetective/scripts/segment_cells.py +165 -104
- celldetective/scripts/segment_cells_thresholds.py +96 -68
- celldetective/scripts/track_cells.py +198 -149
- celldetective/scripts/train_segmentation_model.py +324 -201
- celldetective/scripts/train_signal_model.py +87 -45
- celldetective/segmentation.py +844 -749
- celldetective/signals.py +3514 -2861
- celldetective/tracking.py +30 -15
- celldetective/utils/__init__.py +0 -0
- celldetective/utils/cellpose_utils/__init__.py +133 -0
- celldetective/utils/color_mappings.py +42 -0
- celldetective/utils/data_cleaning.py +630 -0
- celldetective/utils/data_loaders.py +450 -0
- celldetective/utils/dataset_helpers.py +207 -0
- celldetective/utils/downloaders.py +197 -0
- celldetective/utils/event_detection/__init__.py +8 -0
- celldetective/utils/experiment.py +1782 -0
- celldetective/utils/image_augmenters.py +308 -0
- celldetective/utils/image_cleaning.py +74 -0
- celldetective/utils/image_loaders.py +926 -0
- celldetective/utils/image_transforms.py +335 -0
- celldetective/utils/io.py +62 -0
- celldetective/utils/mask_cleaning.py +348 -0
- celldetective/utils/mask_transforms.py +5 -0
- celldetective/utils/masks.py +184 -0
- celldetective/utils/maths.py +351 -0
- celldetective/utils/model_getters.py +325 -0
- celldetective/utils/model_loaders.py +296 -0
- celldetective/utils/normalization.py +380 -0
- celldetective/utils/parsing.py +465 -0
- celldetective/utils/plots/__init__.py +0 -0
- celldetective/utils/plots/regression.py +53 -0
- celldetective/utils/resources.py +34 -0
- celldetective/utils/stardist_utils/__init__.py +104 -0
- celldetective/utils/stats.py +90 -0
- celldetective/utils/types.py +21 -0
- {celldetective-1.4.2.dist-info → celldetective-1.5.0b0.dist-info}/METADATA +1 -1
- celldetective-1.5.0b0.dist-info/RECORD +187 -0
- {celldetective-1.4.2.dist-info → celldetective-1.5.0b0.dist-info}/WHEEL +1 -1
- tests/gui/test_new_project.py +129 -117
- tests/gui/test_project.py +127 -79
- tests/test_filters.py +39 -15
- tests/test_notebooks.py +8 -0
- tests/test_tracking.py +232 -13
- tests/test_utils.py +123 -77
- celldetective/gui/base_components.py +0 -23
- celldetective/gui/layouts.py +0 -1602
- celldetective/gui/processes/compute_neighborhood.py +0 -594
- celldetective/gui/processes/measure_cells.py +0 -360
- celldetective/gui/processes/segment_cells.py +0 -499
- celldetective/gui/processes/track_cells.py +0 -303
- celldetective/gui/processes/train_segmentation_model.py +0 -270
- celldetective/gui/processes/train_signal_model.py +0 -108
- celldetective/gui/table_ops/merge_groups.py +0 -118
- celldetective/gui/viewers.py +0 -1354
- celldetective/io.py +0 -3663
- celldetective/utils.py +0 -3108
- celldetective-1.4.2.dist-info/RECORD +0 -123
- /celldetective/{gui/processes → processes}/downloader.py +0 -0
- {celldetective-1.4.2.dist-info → celldetective-1.5.0b0.dist-info}/entry_points.txt +0 -0
- {celldetective-1.4.2.dist-info → celldetective-1.5.0b0.dist-info}/licenses/LICENSE +0 -0
- {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()
|