celldetective 1.1.1.post1__py3-none-any.whl → 1.1.1.post4__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/__main__.py +17 -0
- celldetective/gui/classifier_widget.py +10 -3
- celldetective/gui/control_panel.py +11 -4
- celldetective/gui/layouts.py +253 -8
- celldetective/gui/neighborhood_options.py +11 -5
- celldetective/gui/retrain_segmentation_model_options.py +66 -164
- celldetective/gui/retrain_signal_model_options.py +18 -164
- celldetective/gui/signal_annotator.py +85 -25
- celldetective/gui/tableUI.py +174 -65
- celldetective/gui/viewers.py +1 -1
- celldetective/io.py +69 -3
- celldetective/neighborhood.py +96 -26
- celldetective/preprocessing.py +95 -63
- celldetective/scripts/segment_cells.py +1 -0
- celldetective/scripts/train_segmentation_model.py +11 -22
- celldetective/segmentation.py +67 -29
- celldetective/utils.py +54 -14
- {celldetective-1.1.1.post1.dist-info → celldetective-1.1.1.post4.dist-info}/METADATA +1 -1
- {celldetective-1.1.1.post1.dist-info → celldetective-1.1.1.post4.dist-info}/RECORD +24 -24
- {celldetective-1.1.1.post1.dist-info → celldetective-1.1.1.post4.dist-info}/WHEEL +1 -1
- tests/test_segmentation.py +1 -1
- {celldetective-1.1.1.post1.dist-info → celldetective-1.1.1.post4.dist-info}/LICENSE +0 -0
- {celldetective-1.1.1.post1.dist-info → celldetective-1.1.1.post4.dist-info}/entry_points.txt +0 -0
- {celldetective-1.1.1.post1.dist-info → celldetective-1.1.1.post4.dist-info}/top_level.txt +0 -0
|
@@ -27,6 +27,7 @@ from matplotlib.cm import tab10
|
|
|
27
27
|
import pandas as pd
|
|
28
28
|
from sklearn.preprocessing import MinMaxScaler
|
|
29
29
|
from celldetective.gui import Styles
|
|
30
|
+
from celldetective.measure import contour_of_instance_segmentation
|
|
30
31
|
|
|
31
32
|
class SignalAnnotator(QMainWindow, Styles):
|
|
32
33
|
"""
|
|
@@ -76,9 +77,9 @@ class SignalAnnotator(QMainWindow, Styles):
|
|
|
76
77
|
|
|
77
78
|
self.populate_widget()
|
|
78
79
|
|
|
79
|
-
self.setMinimumWidth(int(0.8 * self.screen_width))
|
|
80
|
+
#self.setMinimumWidth(int(0.8 * self.screen_width))
|
|
80
81
|
# self.setMaximumHeight(int(0.8*self.screen_height))
|
|
81
|
-
self.setMinimumHeight(int(0.8 * self.screen_height))
|
|
82
|
+
#self.setMinimumHeight(int(0.8 * self.screen_height))
|
|
82
83
|
# self.setMaximumHeight(int(0.8*self.screen_height))
|
|
83
84
|
|
|
84
85
|
self.setAttribute(Qt.WA_DeleteOnClose)
|
|
@@ -119,7 +120,6 @@ class SignalAnnotator(QMainWindow, Styles):
|
|
|
119
120
|
|
|
120
121
|
self.class_choice_cb.addItems(self.class_cols)
|
|
121
122
|
self.class_choice_cb.currentIndexChanged.connect(self.compute_status_and_colors)
|
|
122
|
-
self.class_choice_cb.setCurrentIndex(0)
|
|
123
123
|
|
|
124
124
|
class_hbox.addWidget(self.class_choice_cb, 70)
|
|
125
125
|
|
|
@@ -346,6 +346,8 @@ class SignalAnnotator(QMainWindow, Styles):
|
|
|
346
346
|
main_layout.addLayout(self.right_panel, 65)
|
|
347
347
|
self.button_widget.adjustSize()
|
|
348
348
|
|
|
349
|
+
self.compute_status_and_colors(0)
|
|
350
|
+
|
|
349
351
|
self.setCentralWidget(self.button_widget)
|
|
350
352
|
self.show()
|
|
351
353
|
|
|
@@ -462,6 +464,7 @@ class SignalAnnotator(QMainWindow, Styles):
|
|
|
462
464
|
self.newClassWidget.close()
|
|
463
465
|
|
|
464
466
|
def compute_status_and_colors(self, i):
|
|
467
|
+
|
|
465
468
|
self.class_name = self.class_choice_cb.currentText()
|
|
466
469
|
self.expected_status = 'status'
|
|
467
470
|
suffix = self.class_name.replace('class', '').replace('_', '', 1)
|
|
@@ -474,11 +477,15 @@ class SignalAnnotator(QMainWindow, Styles):
|
|
|
474
477
|
self.status_name = self.expected_status
|
|
475
478
|
|
|
476
479
|
print('selection and expected names: ', self.class_name, self.expected_time, self.expected_status)
|
|
480
|
+
cols = list(self.df_tracks.columns)
|
|
477
481
|
|
|
478
|
-
if self.time_name in
|
|
482
|
+
if self.time_name in cols and self.class_name in cols and not self.status_name in cols:
|
|
479
483
|
# only create the status column if it does not exist to not erase static classification results
|
|
480
484
|
self.make_status_column()
|
|
481
|
-
elif self.time_name in
|
|
485
|
+
elif self.time_name in cols and self.class_name in cols and self.df_tracks[self.status_name].isnull().all():
|
|
486
|
+
print('this is the case!', )
|
|
487
|
+
self.make_status_column()
|
|
488
|
+
elif self.time_name in cols and self.class_name in cols:
|
|
482
489
|
# all good, do nothing
|
|
483
490
|
pass
|
|
484
491
|
else:
|
|
@@ -855,7 +862,7 @@ class SignalAnnotator(QMainWindow, Styles):
|
|
|
855
862
|
self.cell_ax.legend()
|
|
856
863
|
self.cell_fcanvas.canvas.draw()
|
|
857
864
|
except Exception as e:
|
|
858
|
-
print(f"{e=}")
|
|
865
|
+
print(f"Plot signals: {e=}")
|
|
859
866
|
|
|
860
867
|
def extract_scatter_from_trajectories(self):
|
|
861
868
|
|
|
@@ -1109,7 +1116,7 @@ class SignalAnnotator(QMainWindow, Styles):
|
|
|
1109
1116
|
if len(min_values) > 0:
|
|
1110
1117
|
self.cell_ax.set_ylim(np.amin(min_values), np.amax(max_values))
|
|
1111
1118
|
except Exception as e:
|
|
1112
|
-
print(e)
|
|
1119
|
+
print('Ylim error:',e)
|
|
1113
1120
|
|
|
1114
1121
|
def draw_frame(self, framedata):
|
|
1115
1122
|
|
|
@@ -1301,7 +1308,9 @@ class SignalAnnotator(QMainWindow, Styles):
|
|
|
1301
1308
|
|
|
1302
1309
|
|
|
1303
1310
|
class MeasureAnnotator(SignalAnnotator):
|
|
1311
|
+
|
|
1304
1312
|
def __init__(self, parent_window=None):
|
|
1313
|
+
|
|
1305
1314
|
QMainWindow.__init__(self)
|
|
1306
1315
|
self.parent_window = parent_window
|
|
1307
1316
|
self.setWindowTitle("Signal annotator")
|
|
@@ -1315,11 +1324,11 @@ class MeasureAnnotator(SignalAnnotator):
|
|
|
1315
1324
|
self.int_validator = QIntValidator()
|
|
1316
1325
|
self.current_alpha=0.5
|
|
1317
1326
|
if self.mode == "targets":
|
|
1318
|
-
self.instructions_path = self.exp_dir +
|
|
1319
|
-
self.trajectories_path = self.pos + 'output
|
|
1327
|
+
self.instructions_path = self.exp_dir + os.sep.join(['configs','signal_annotator_config_targets.json'])
|
|
1328
|
+
self.trajectories_path = self.pos + os.sep.join(['output','tables','trajectories_targets.csv'])
|
|
1320
1329
|
elif self.mode == "effectors":
|
|
1321
|
-
self.instructions_path = self.exp_dir +
|
|
1322
|
-
self.trajectories_path = self.pos + 'output
|
|
1330
|
+
self.instructions_path = self.exp_dir + os.sep.join(['configs','signal_annotator_config_effectors.json'])
|
|
1331
|
+
self.trajectories_path = self.pos + os.sep.join(['output','tables','trajectories_effectors.csv'])
|
|
1323
1332
|
|
|
1324
1333
|
self.screen_height = self.parent_window.parent_window.parent_window.screen_height
|
|
1325
1334
|
self.screen_width = self.parent_window.parent_window.parent_window.screen_width
|
|
@@ -1330,8 +1339,13 @@ class MeasureAnnotator(SignalAnnotator):
|
|
|
1330
1339
|
center_window(self)
|
|
1331
1340
|
|
|
1332
1341
|
self.locate_stack()
|
|
1342
|
+
|
|
1333
1343
|
data, properties, graph, labels, _ = load_napari_data(self.pos, prefix=None, population=self.mode,return_stack=False)
|
|
1334
|
-
|
|
1344
|
+
if data is not None:
|
|
1345
|
+
self.labels = relabel_segmentation(labels,data,properties)
|
|
1346
|
+
else:
|
|
1347
|
+
self.labels = labels
|
|
1348
|
+
|
|
1335
1349
|
self.current_channel = 0
|
|
1336
1350
|
|
|
1337
1351
|
self.locate_tracks()
|
|
@@ -1577,8 +1591,10 @@ class MeasureAnnotator(SignalAnnotator):
|
|
|
1577
1591
|
self.class_choice_cb = QComboBox()
|
|
1578
1592
|
|
|
1579
1593
|
cols = np.array(self.df_tracks.columns)
|
|
1580
|
-
self.class_cols = np.array([c.startswith('group') for c in list(self.df_tracks.columns)])
|
|
1594
|
+
self.class_cols = np.array([c.startswith('group') or c.startswith('status') for c in list(self.df_tracks.columns)])
|
|
1581
1595
|
self.class_cols = list(cols[self.class_cols])
|
|
1596
|
+
print(self.class_cols)
|
|
1597
|
+
|
|
1582
1598
|
try:
|
|
1583
1599
|
self.class_cols.remove('group_id')
|
|
1584
1600
|
except Exception:
|
|
@@ -1711,6 +1727,14 @@ class MeasureAnnotator(SignalAnnotator):
|
|
|
1711
1727
|
btn_hbox.addWidget(self.save_btn, 90)
|
|
1712
1728
|
self.left_panel.addLayout(btn_hbox)
|
|
1713
1729
|
|
|
1730
|
+
self.export_btn = QPushButton('')
|
|
1731
|
+
self.export_btn.setStyleSheet(self.button_select_all)
|
|
1732
|
+
self.export_btn.clicked.connect(self.export_measurements)
|
|
1733
|
+
self.export_btn.setIcon(icon(MDI6.export, color="black"))
|
|
1734
|
+
self.export_btn.setIconSize(QSize(25, 25))
|
|
1735
|
+
btn_hbox.addWidget(self.export_btn, 10)
|
|
1736
|
+
self.left_panel.addLayout(btn_hbox)
|
|
1737
|
+
|
|
1714
1738
|
# Animation
|
|
1715
1739
|
animation_buttons_box = QHBoxLayout()
|
|
1716
1740
|
|
|
@@ -1810,6 +1834,28 @@ class MeasureAnnotator(SignalAnnotator):
|
|
|
1810
1834
|
# del self.img
|
|
1811
1835
|
gc.collect()
|
|
1812
1836
|
|
|
1837
|
+
|
|
1838
|
+
def export_measurements(self):
|
|
1839
|
+
|
|
1840
|
+
auto_dataset_name = self.pos.split(os.sep)[-4] + '_' + self.pos.split(os.sep)[-2] + f'_{str(self.current_frame).zfill(3)}' + f'_{self.status_name}.npy'
|
|
1841
|
+
|
|
1842
|
+
if self.normalized_signals:
|
|
1843
|
+
self.normalize_features_btn.click()
|
|
1844
|
+
|
|
1845
|
+
subdf = self.df_tracks.loc[self.df_tracks['FRAME']==self.current_frame,:]
|
|
1846
|
+
subdf['class'] = subdf[self.status_name]
|
|
1847
|
+
dico = subdf.to_dict('records')
|
|
1848
|
+
|
|
1849
|
+
pathsave = QFileDialog.getSaveFileName(self, "Select file name", self.exp_dir + auto_dataset_name, ".npy")[0]
|
|
1850
|
+
if pathsave != '':
|
|
1851
|
+
if not pathsave.endswith(".npy"):
|
|
1852
|
+
pathsave += ".npy"
|
|
1853
|
+
try:
|
|
1854
|
+
np.save(pathsave, dico)
|
|
1855
|
+
print(f'File successfully written in {pathsave}.')
|
|
1856
|
+
except Exception as e:
|
|
1857
|
+
print(f"Error {e}...")
|
|
1858
|
+
|
|
1813
1859
|
def set_next_frame(self):
|
|
1814
1860
|
|
|
1815
1861
|
self.current_frame = self.current_frame + 1
|
|
@@ -1887,13 +1933,17 @@ class MeasureAnnotator(SignalAnnotator):
|
|
|
1887
1933
|
|
|
1888
1934
|
def give_cell_information(self):
|
|
1889
1935
|
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1936
|
+
try:
|
|
1937
|
+
cell_selected = f"cell: {self.track_of_interest}\n"
|
|
1938
|
+
if 'TRACK_ID' in self.df_tracks.columns:
|
|
1939
|
+
cell_status = f"phenotype: {self.df_tracks.loc[self.df_tracks['TRACK_ID'] == self.track_of_interest, self.status_name].to_numpy()[0]}\n"
|
|
1940
|
+
else:
|
|
1941
|
+
cell_status = f"phenotype: {self.df_tracks.loc[self.df_tracks['ID'] == self.track_of_interest, self.status_name].to_numpy()[0]}\n"
|
|
1942
|
+
self.cell_info.setText(cell_selected + cell_status)
|
|
1943
|
+
except Exception as e:
|
|
1944
|
+
print('Cell info:',e)
|
|
1945
|
+
print(self.track_of_interest, self.status_name)
|
|
1946
|
+
|
|
1897
1947
|
def create_new_event_class(self):
|
|
1898
1948
|
|
|
1899
1949
|
# display qwidget to name the event
|
|
@@ -1978,8 +2028,14 @@ class MeasureAnnotator(SignalAnnotator):
|
|
|
1978
2028
|
self.frame_lbl.setText(f'position: {self.framedata}')
|
|
1979
2029
|
self.im.set_array(self.img)
|
|
1980
2030
|
self.status_scatter.set_offsets(self.positions[self.framedata])
|
|
1981
|
-
|
|
2031
|
+
try:
|
|
2032
|
+
self.status_scatter.set_edgecolors(self.colors[self.framedata][:, 0])
|
|
2033
|
+
except Exception as e:
|
|
2034
|
+
print('L1993: ',e)
|
|
2035
|
+
|
|
1982
2036
|
self.current_label = self.labels[self.current_frame]
|
|
2037
|
+
self.current_label = contour_of_instance_segmentation(self.current_label, 5)
|
|
2038
|
+
|
|
1983
2039
|
self.im_mask.remove()
|
|
1984
2040
|
self.im_mask = self.ax.imshow(np.ma.masked_where(self.current_label == 0, self.current_label),
|
|
1985
2041
|
cmap='viridis', interpolation='none',alpha=self.current_alpha,vmin=0,vmax=np.nanmax(self.labels.flatten()))
|
|
@@ -2108,9 +2164,9 @@ class MeasureAnnotator(SignalAnnotator):
|
|
|
2108
2164
|
|
|
2109
2165
|
self.extract_scatter_from_trajectories()
|
|
2110
2166
|
if 'TRACK_ID' in self.df_tracks.columns:
|
|
2111
|
-
self.track_of_interest = self.df_tracks['TRACK_ID'].min()
|
|
2167
|
+
self.track_of_interest = self.df_tracks.dropna(subset='TRACK_ID')['TRACK_ID'].min()
|
|
2112
2168
|
else:
|
|
2113
|
-
self.track_of_interest = self.df_tracks['ID'].min()
|
|
2169
|
+
self.track_of_interest = self.df_tracks.dropna(subset='ID')['ID'].min()
|
|
2114
2170
|
|
|
2115
2171
|
self.loc_t = []
|
|
2116
2172
|
self.loc_idx = []
|
|
@@ -2172,9 +2228,13 @@ class MeasureAnnotator(SignalAnnotator):
|
|
|
2172
2228
|
"""
|
|
2173
2229
|
self.current_frame = self.frame_slider.value()
|
|
2174
2230
|
self.reload_frame()
|
|
2175
|
-
if '
|
|
2231
|
+
if 'TRACK_ID' in list(self.df_tracks.columns):
|
|
2232
|
+
pass
|
|
2233
|
+
elif 'ID' in list(self.df_tracks.columns):
|
|
2234
|
+
print('ID in cols... change class of interest... ')
|
|
2176
2235
|
self.track_of_interest = self.df_tracks[self.df_tracks['FRAME'] == self.current_frame]['ID'].min()
|
|
2177
2236
|
self.modify()
|
|
2237
|
+
|
|
2178
2238
|
self.draw_frame(self.current_frame)
|
|
2179
2239
|
self.fcanvas.canvas.draw()
|
|
2180
2240
|
self.plot_signals()
|
|
@@ -2302,7 +2362,7 @@ class MeasureAnnotator(SignalAnnotator):
|
|
|
2302
2362
|
try:
|
|
2303
2363
|
self.selection.pop(0)
|
|
2304
2364
|
except Exception as e:
|
|
2305
|
-
print(e)
|
|
2365
|
+
print('Cancel selection: ',e)
|
|
2306
2366
|
|
|
2307
2367
|
try:
|
|
2308
2368
|
for k, (t, idx) in enumerate(zip(self.loc_t, self.loc_idx)):
|
celldetective/gui/tableUI.py
CHANGED
|
@@ -12,6 +12,8 @@ import matplotlib.cm as mcm
|
|
|
12
12
|
import os
|
|
13
13
|
from celldetective.gui import Styles
|
|
14
14
|
from superqt import QColormapComboBox, QLabeledSlider, QSearchableComboBox
|
|
15
|
+
from superqt.fonticon import icon
|
|
16
|
+
from fonticon_mdi6 import MDI6
|
|
15
17
|
from math import floor
|
|
16
18
|
|
|
17
19
|
from matplotlib import colormaps
|
|
@@ -63,15 +65,101 @@ class QueryWidget(QWidget):
|
|
|
63
65
|
|
|
64
66
|
def filter_table(self):
|
|
65
67
|
try:
|
|
66
|
-
query_text = self.query_le.text()
|
|
68
|
+
query_text = self.query_le.text() #.replace('class', '`class`')
|
|
67
69
|
tab = self.parent_window.data.query(query_text)
|
|
68
|
-
self.subtable = TableUI(tab, query_text, plot_mode="
|
|
70
|
+
self.subtable = TableUI(tab, query_text, plot_mode="static")
|
|
69
71
|
self.subtable.show()
|
|
70
72
|
self.close()
|
|
71
73
|
except Exception as e:
|
|
72
74
|
print(e)
|
|
73
75
|
return None
|
|
74
76
|
|
|
77
|
+
|
|
78
|
+
class MergeOneHotWidget(QWidget, Styles):
|
|
79
|
+
|
|
80
|
+
def __init__(self, parent_window, selected_columns=None):
|
|
81
|
+
|
|
82
|
+
super().__init__()
|
|
83
|
+
self.parent_window = parent_window
|
|
84
|
+
self.selected_columns = selected_columns
|
|
85
|
+
|
|
86
|
+
self.setWindowTitle("Merge one-hot encoded columns...")
|
|
87
|
+
# Create the QComboBox and add some items
|
|
88
|
+
center_window(self)
|
|
89
|
+
|
|
90
|
+
self.layout = QVBoxLayout(self)
|
|
91
|
+
self.layout.setContentsMargins(30,30,30,30)
|
|
92
|
+
|
|
93
|
+
if self.selected_columns is not None:
|
|
94
|
+
n_cols = len(self.selected_columns)
|
|
95
|
+
else:
|
|
96
|
+
n_cols = 2
|
|
97
|
+
|
|
98
|
+
name_hbox = QHBoxLayout()
|
|
99
|
+
name_hbox.addWidget(QLabel('New categorical column: '), 33)
|
|
100
|
+
self.new_col_le = QLineEdit()
|
|
101
|
+
self.new_col_le.setText('categorical_')
|
|
102
|
+
self.new_col_le.textChanged.connect(self.allow_merge)
|
|
103
|
+
name_hbox.addWidget(self.new_col_le, 66)
|
|
104
|
+
self.layout.addLayout(name_hbox)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
self.layout.addWidget(QLabel('Source columns: '))
|
|
108
|
+
|
|
109
|
+
self.cbs = [QSearchableComboBox() for i in range(n_cols)]
|
|
110
|
+
self.cbs_layout = QVBoxLayout()
|
|
111
|
+
|
|
112
|
+
for i in range(n_cols):
|
|
113
|
+
lay = QHBoxLayout()
|
|
114
|
+
lay.addWidget(QLabel(f'column {i}: '), 33)
|
|
115
|
+
self.cbs[i].addItems(['--']+list(self.parent_window.data.columns))
|
|
116
|
+
if self.selected_columns is not None:
|
|
117
|
+
self.cbs[i].setCurrentText(self.selected_columns[i])
|
|
118
|
+
lay.addWidget(self.cbs[i], 66)
|
|
119
|
+
self.cbs_layout.addLayout(lay)
|
|
120
|
+
|
|
121
|
+
self.layout.addLayout(self.cbs_layout)
|
|
122
|
+
|
|
123
|
+
hbox = QHBoxLayout()
|
|
124
|
+
self.add_col_btn = QPushButton('Add column')
|
|
125
|
+
self.add_col_btn.clicked.connect(self.add_col)
|
|
126
|
+
self.add_col_btn.setStyleSheet(self.button_add)
|
|
127
|
+
self.add_col_btn.setIcon(icon(MDI6.plus,color="black"))
|
|
128
|
+
|
|
129
|
+
hbox.addWidget(QLabel(''), 50)
|
|
130
|
+
hbox.addWidget(self.add_col_btn, 50, alignment=Qt.AlignRight)
|
|
131
|
+
self.layout.addLayout(hbox)
|
|
132
|
+
|
|
133
|
+
self.submit_btn = QPushButton('Merge')
|
|
134
|
+
self.submit_btn.setStyleSheet(self.button_style_sheet)
|
|
135
|
+
self.submit_btn.clicked.connect(self.merge_cols)
|
|
136
|
+
self.layout.addWidget(self.submit_btn, 30)
|
|
137
|
+
|
|
138
|
+
self.setAttribute(Qt.WA_DeleteOnClose)
|
|
139
|
+
|
|
140
|
+
def add_col(self):
|
|
141
|
+
self.cbs.append(QSearchableComboBox())
|
|
142
|
+
self.cbs[-1].addItems(['--']+list(self.parent_window.data.columns))
|
|
143
|
+
lay = QHBoxLayout()
|
|
144
|
+
lay.addWidget(QLabel(f'column {len(self.cbs)-1}: '), 33)
|
|
145
|
+
lay.addWidget(self.cbs[-1], 66)
|
|
146
|
+
self.cbs_layout.addLayout(lay)
|
|
147
|
+
|
|
148
|
+
def merge_cols(self):
|
|
149
|
+
|
|
150
|
+
self.parent_window.data[self.new_col_le.text()] = self.parent_window.data.loc[:,list(self.selected_columns)].idxmax(axis=1)
|
|
151
|
+
self.parent_window.model = PandasModel(self.parent_window.data)
|
|
152
|
+
self.parent_window.table_view.setModel(self.parent_window.model)
|
|
153
|
+
self.close()
|
|
154
|
+
|
|
155
|
+
def allow_merge(self):
|
|
156
|
+
|
|
157
|
+
if self.new_col_le.text()=='':
|
|
158
|
+
self.submit_btn.setEnabled(False)
|
|
159
|
+
else:
|
|
160
|
+
self.submit_btn.setEnabled(True)
|
|
161
|
+
|
|
162
|
+
|
|
75
163
|
class DifferentiateColWidget(QWidget, Styles):
|
|
76
164
|
|
|
77
165
|
def __init__(self, parent_window, column=None):
|
|
@@ -180,7 +268,6 @@ class RenameColWidget(QWidget):
|
|
|
180
268
|
old_name = self.column
|
|
181
269
|
new_name = self.new_col_name.text()
|
|
182
270
|
self.parent_window.data = self.parent_window.data.rename(columns={old_name: new_name})
|
|
183
|
-
print(self.parent.data.columns)
|
|
184
271
|
|
|
185
272
|
self.parent_window.model = PandasModel(self.parent_window.data)
|
|
186
273
|
self.parent_window.table_view.setModel(self.parent_window.model)
|
|
@@ -264,7 +351,12 @@ class TableUI(QMainWindow, Styles):
|
|
|
264
351
|
self.derivative_action = QAction('&Differentiate...', self)
|
|
265
352
|
self.derivative_action.triggered.connect(self.differenciate_selected_feature)
|
|
266
353
|
self.derivative_action.setShortcut("Ctrl+D")
|
|
267
|
-
self.mathMenu.addAction(self.derivative_action)
|
|
354
|
+
self.mathMenu.addAction(self.derivative_action)
|
|
355
|
+
|
|
356
|
+
self.onehot_action = QAction('&One hot to categorical...', self)
|
|
357
|
+
self.onehot_action.triggered.connect(self.transform_one_hot_cols_to_categorical)
|
|
358
|
+
#self.onehot_action.setShortcut("Ctrl+D")
|
|
359
|
+
self.mathMenu.addAction(self.onehot_action)
|
|
268
360
|
|
|
269
361
|
def delete_columns(self):
|
|
270
362
|
|
|
@@ -334,6 +426,19 @@ class TableUI(QMainWindow, Styles):
|
|
|
334
426
|
self.diffWidget = DifferentiateColWidget(self, selected_col)
|
|
335
427
|
self.diffWidget.show()
|
|
336
428
|
|
|
429
|
+
def transform_one_hot_cols_to_categorical(self):
|
|
430
|
+
|
|
431
|
+
x = self.table_view.selectedIndexes()
|
|
432
|
+
col_idx = np.unique(np.array([l.column() for l in x]))
|
|
433
|
+
if list(col_idx):
|
|
434
|
+
cols = np.array(list(self.data.columns))
|
|
435
|
+
selected_cols = cols[col_idx]
|
|
436
|
+
else:
|
|
437
|
+
selected_cols = None
|
|
438
|
+
|
|
439
|
+
self.mergewidget = MergeOneHotWidget(self, selected_columns=selected_cols)
|
|
440
|
+
self.mergewidget.show()
|
|
441
|
+
|
|
337
442
|
|
|
338
443
|
def groupby_time_table(self):
|
|
339
444
|
|
|
@@ -477,16 +582,20 @@ class TableUI(QMainWindow, Styles):
|
|
|
477
582
|
layout.addWidget(QLabel('Representations: '))
|
|
478
583
|
self.hist_check = QCheckBox('histogram')
|
|
479
584
|
self.kde_check = QCheckBox('KDE plot')
|
|
585
|
+
self.count_check = QCheckBox('countplot')
|
|
480
586
|
self.ecdf_check = QCheckBox('ECDF plot')
|
|
587
|
+
self.scat_check = QCheckBox('scatter plot')
|
|
481
588
|
self.swarm_check = QCheckBox('swarm')
|
|
482
589
|
self.violin_check = QCheckBox('violin')
|
|
483
590
|
self.strip_check = QCheckBox('strip')
|
|
484
|
-
self.box_check = QCheckBox('
|
|
485
|
-
self.boxenplot_check = QCheckBox('
|
|
591
|
+
self.box_check = QCheckBox('boxplot')
|
|
592
|
+
self.boxenplot_check = QCheckBox('boxenplot')
|
|
486
593
|
|
|
487
594
|
layout.addWidget(self.hist_check)
|
|
488
595
|
layout.addWidget(self.kde_check)
|
|
596
|
+
layout.addWidget(self.count_check)
|
|
489
597
|
layout.addWidget(self.ecdf_check)
|
|
598
|
+
layout.addWidget(self.scat_check)
|
|
490
599
|
layout.addWidget(self.swarm_check)
|
|
491
600
|
layout.addWidget(self.violin_check)
|
|
492
601
|
layout.addWidget(self.strip_check)
|
|
@@ -496,16 +605,38 @@ class TableUI(QMainWindow, Styles):
|
|
|
496
605
|
self.x_cb = QSearchableComboBox()
|
|
497
606
|
self.x_cb.addItems(['--']+list(self.data.columns))
|
|
498
607
|
|
|
608
|
+
self.y_cb = QSearchableComboBox()
|
|
609
|
+
self.y_cb.addItems(['--']+list(self.data.columns))
|
|
610
|
+
|
|
499
611
|
self.hue_cb = QSearchableComboBox()
|
|
500
612
|
self.hue_cb.addItems(['--']+list(self.data.columns))
|
|
501
613
|
idx = self.hue_cb.findText('--')
|
|
614
|
+
self.hue_cb.setCurrentIndex(idx)
|
|
615
|
+
|
|
616
|
+
# Set selected column
|
|
617
|
+
|
|
618
|
+
try:
|
|
619
|
+
x = self.table_view.selectedIndexes()
|
|
620
|
+
col_idx = np.array([l.column() for l in x])
|
|
621
|
+
row_idx = np.array([l.row() for l in x])
|
|
622
|
+
column_names = self.data.columns
|
|
623
|
+
unique_cols = np.unique(col_idx)[0]
|
|
624
|
+
y = column_names[unique_cols]
|
|
625
|
+
idx = self.y_cb.findText(y)
|
|
626
|
+
self.y_cb.setCurrentIndex(idx)
|
|
627
|
+
except:
|
|
628
|
+
pass
|
|
502
629
|
|
|
503
|
-
self.x_cb.findText('--')
|
|
504
630
|
hbox = QHBoxLayout()
|
|
505
631
|
hbox.addWidget(QLabel('x: '), 33)
|
|
506
632
|
hbox.addWidget(self.x_cb, 66)
|
|
507
633
|
layout.addLayout(hbox)
|
|
508
634
|
|
|
635
|
+
hbox = QHBoxLayout()
|
|
636
|
+
hbox.addWidget(QLabel('y: '), 33)
|
|
637
|
+
hbox.addWidget(self.y_cb, 66)
|
|
638
|
+
layout.addLayout(hbox)
|
|
639
|
+
|
|
509
640
|
hbox = QHBoxLayout()
|
|
510
641
|
hbox.addWidget(QLabel('hue: '), 33)
|
|
511
642
|
hbox.addWidget(self.hue_cb, 66)
|
|
@@ -539,18 +670,10 @@ class TableUI(QMainWindow, Styles):
|
|
|
539
670
|
self.x_option = True
|
|
540
671
|
self.x = self.x_cb.currentText()
|
|
541
672
|
|
|
542
|
-
x = self.table_view.selectedIndexes()
|
|
543
|
-
col_idx = np.array([l.column() for l in x])
|
|
544
|
-
row_idx = np.array([l.row() for l in x])
|
|
545
|
-
column_names = self.data.columns
|
|
546
|
-
unique_cols = np.unique(col_idx)[0]
|
|
547
|
-
|
|
548
673
|
self.fig, self.ax = plt.subplots(1,1,figsize=(4,3))
|
|
549
674
|
self.plot1dWindow = FigureCanvas(self.fig, title="scatter")
|
|
550
675
|
self.ax.clear()
|
|
551
|
-
|
|
552
|
-
y = self.data.iloc[row_idx_i, unique_cols]
|
|
553
|
-
|
|
676
|
+
|
|
554
677
|
cmap = getattr(mcm, self.cmap_cb.currentText())
|
|
555
678
|
|
|
556
679
|
try:
|
|
@@ -562,58 +685,76 @@ class TableUI(QMainWindow, Styles):
|
|
|
562
685
|
if self.hue_cb.currentText()=='--':
|
|
563
686
|
hue_variable = None
|
|
564
687
|
|
|
565
|
-
|
|
688
|
+
if self.y_cb.currentText()=='--':
|
|
689
|
+
self.y = None
|
|
690
|
+
else:
|
|
691
|
+
self.y = self.y_cb.currentText()
|
|
692
|
+
|
|
693
|
+
if self.x_cb.currentText()=='--':
|
|
694
|
+
self.x = None
|
|
695
|
+
else:
|
|
696
|
+
self.x = self.x_cb.currentText()
|
|
566
697
|
|
|
567
698
|
legend=True
|
|
568
699
|
if self.hist_check.isChecked():
|
|
569
|
-
sns.histplot(data=self.data, x=
|
|
700
|
+
sns.histplot(data=self.data, x=self.x, hue=hue_variable, legend=legend, ax=self.ax, palette=colors, kde=True, common_norm=False, stat='density')
|
|
570
701
|
legend = False
|
|
571
702
|
if self.kde_check.isChecked():
|
|
572
|
-
sns.kdeplot(data=self.data, x=
|
|
703
|
+
sns.kdeplot(data=self.data, x=self.x, hue=hue_variable, legend=legend, ax=self.ax, palette=colors, cut=0)
|
|
704
|
+
legend = False
|
|
705
|
+
if self.count_check.isChecked():
|
|
706
|
+
sns.countplot(data=self.data, x=self.x, hue=hue_variable, legend=legend, ax=self.ax, palette=colors)
|
|
573
707
|
legend = False
|
|
574
|
-
|
|
575
708
|
if self.ecdf_check.isChecked():
|
|
576
|
-
sns.ecdfplot(data=self.data, x=
|
|
709
|
+
sns.ecdfplot(data=self.data, x=self.x, hue=hue_variable, legend=legend, ax=self.ax, palette=colors)
|
|
577
710
|
legend = False
|
|
711
|
+
|
|
712
|
+
if self.scat_check.isChecked():
|
|
713
|
+
if self.x_option:
|
|
714
|
+
sns.scatterplot(data=self.data, x=self.x,y=self.y, hue=hue_variable,legend=legend, ax=self.ax, palette=colors)
|
|
715
|
+
legend = False
|
|
716
|
+
else:
|
|
717
|
+
print('please provide a -x variable...')
|
|
718
|
+
pass
|
|
578
719
|
|
|
579
720
|
if self.swarm_check.isChecked():
|
|
580
721
|
if self.x_option:
|
|
581
|
-
sns.swarmplot(data=self.data, x=self.x,y=
|
|
722
|
+
sns.swarmplot(data=self.data, x=self.x,y=self.y,dodge=True, hue=hue_variable,legend=legend, ax=self.ax, palette=colors)
|
|
582
723
|
legend = False
|
|
583
724
|
else:
|
|
584
|
-
sns.swarmplot(data=self.data, y=
|
|
725
|
+
sns.swarmplot(data=self.data, y=self.y,dodge=True, hue=hue_variable,legend=legend, ax=self.ax, palette=colors)
|
|
585
726
|
legend = False
|
|
586
727
|
|
|
587
728
|
if self.violin_check.isChecked():
|
|
588
729
|
if self.x_option:
|
|
589
|
-
sns.stripplot(data=self.data,x=self.x, y=
|
|
730
|
+
sns.stripplot(data=self.data,x=self.x, y=self.y,dodge=True, ax=self.ax, hue=hue_variable, legend=legend, palette=colors)
|
|
590
731
|
legend = False
|
|
591
732
|
else:
|
|
592
|
-
sns.violinplot(data=self.data, y=
|
|
733
|
+
sns.violinplot(data=self.data, y=self.y,dodge=True, hue=hue_variable,legend=legend, ax=self.ax, palette=colors, cut=0)
|
|
593
734
|
legend = False
|
|
594
735
|
|
|
595
736
|
if self.box_check.isChecked():
|
|
596
737
|
if self.x_option:
|
|
597
|
-
sns.boxplot(data=self.data, x=self.x, y=
|
|
738
|
+
sns.boxplot(data=self.data, x=self.x, y=self.y,dodge=True, hue=hue_variable,legend=legend, ax=self.ax, fill=False,palette=colors, linewidth=2,)
|
|
598
739
|
legend = False
|
|
599
740
|
else:
|
|
600
|
-
sns.boxplot(data=self.data, y=
|
|
741
|
+
sns.boxplot(data=self.data, y=self.y,dodge=True, hue=hue_variable,legend=legend, ax=self.ax, fill=False,palette=colors, linewidth=2,)
|
|
601
742
|
legend = False
|
|
602
743
|
|
|
603
744
|
if self.boxenplot_check.isChecked():
|
|
604
745
|
if self.x_option:
|
|
605
|
-
sns.boxenplot(data=self.data, x
|
|
746
|
+
sns.boxenplot(data=self.data, x=self.x, y=self.y,dodge=True, hue=hue_variable,legend=legend, ax=self.ax, fill=False,palette=colors, linewidth=2,)
|
|
606
747
|
legend = False
|
|
607
748
|
else:
|
|
608
|
-
sns.boxenplot(data=self.data, y=
|
|
749
|
+
sns.boxenplot(data=self.data, y=self.y,dodge=True, hue=hue_variable,legend=legend, ax=self.ax, fill=False,palette=colors, linewidth=2,)
|
|
609
750
|
legend = False
|
|
610
751
|
|
|
611
752
|
if self.strip_check.isChecked():
|
|
612
753
|
if self.x_option:
|
|
613
|
-
sns.stripplot(data=self.data, x = self.x, y=
|
|
754
|
+
sns.stripplot(data=self.data, x = self.x, y=self.y,dodge=True, ax=self.ax, hue=hue_variable, legend=legend, palette=colors)
|
|
614
755
|
legend = False
|
|
615
756
|
else:
|
|
616
|
-
sns.stripplot(data=self.data, y=
|
|
757
|
+
sns.stripplot(data=self.data, y=self.y,dodge=True, ax=self.ax, hue=hue_variable, legend=legend, palette=colors)
|
|
617
758
|
legend = False
|
|
618
759
|
|
|
619
760
|
plt.tight_layout()
|
|
@@ -762,42 +903,10 @@ class TableUI(QMainWindow, Styles):
|
|
|
762
903
|
column_names = self.data.columns
|
|
763
904
|
unique_cols = np.unique(col_idx)
|
|
764
905
|
|
|
765
|
-
if len(unique_cols)==1:
|
|
766
|
-
# 1D plot
|
|
767
|
-
# Open widget to set 1D data representations
|
|
906
|
+
if len(unique_cols)==1 or len(unique_cols)==0:
|
|
768
907
|
self.set_1D_plot_params()
|
|
769
908
|
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
# x = self.table_view.selectedIndexes()
|
|
773
|
-
# col_idx = np.array([l.column() for l in x])
|
|
774
|
-
# row_idx = np.array([l.row() for l in x])
|
|
775
|
-
# column_names = self.data.columns
|
|
776
|
-
# unique_cols = np.unique(col_idx)[0]
|
|
777
|
-
|
|
778
|
-
# self.fig, self.ax = plt.subplots(1,1,figsize=(4,3))
|
|
779
|
-
# self.histogram_window = FigureCanvas(self.fig, title="scatter")
|
|
780
|
-
# self.ax.clear()
|
|
781
|
-
# row_idx_i = row_idx[np.where(col_idx==unique_cols)[0]]
|
|
782
|
-
# y = self.data.iloc[row_idx_i, unique_cols]
|
|
783
|
-
|
|
784
|
-
# colors = [viridis(i / len(self.data['well_index'].unique())) for i in range(len(self.data['well_index'].unique()))]
|
|
785
|
-
# #for w,well_group in self.data.groupby('well_index'):
|
|
786
|
-
# sns.boxplot(data=self.data, y=column_names[unique_cols],dodge=True, hue='well_index',legend=False, ax=self.ax, fill=False,palette=colors, linewidth=2,)
|
|
787
|
-
# sns.stripplot(data=self.data, y=column_names[unique_cols],dodge=True, ax=self.ax, hue='well_index', legend=False, palette=colors)
|
|
788
|
-
# # sns.kdeplot(data=self.data, x=column_names[unique_cols], hue='well_index', ax=self.ax, fill=False,common_norm=False, palette=colors, alpha=.5, linewidth=2,)
|
|
789
|
-
# # for k,(w,well_group) in enumerate(self.data.groupby('well_index')):
|
|
790
|
-
# # self.ax.hist(well_group[column_names[unique_cols]],label=w, density=True, alpha=0.5, color=colors[k])
|
|
791
|
-
# #self.ax.legend()
|
|
792
|
-
# self.ax.set_xlabel(column_names[unique_cols])
|
|
793
|
-
# plt.tight_layout()
|
|
794
|
-
# self.fig.set_facecolor('none') # or 'None'
|
|
795
|
-
# self.fig.canvas.setStyleSheet("background-color: transparent;")
|
|
796
|
-
# self.histogram_window.canvas.draw()
|
|
797
|
-
# self.histogram_window.show()
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
elif len(unique_cols) == 2:
|
|
909
|
+
if len(unique_cols) == 2:
|
|
801
910
|
|
|
802
911
|
print("two columns, plot mode")
|
|
803
912
|
x1 = self.test_bool(self.data.iloc[row_idx, unique_cols[0]])
|
celldetective/gui/viewers.py
CHANGED
|
@@ -396,7 +396,7 @@ class ThresholdedStackVisualizer(StackVisualizer):
|
|
|
396
396
|
# Compute the mask based on the threshold value
|
|
397
397
|
self.preprocess_image()
|
|
398
398
|
edge = estimate_unreliable_edge(self.preprocessing)
|
|
399
|
-
self.mask = threshold_image(self.processed_image, threshold_value,
|
|
399
|
+
self.mask = threshold_image(self.processed_image, threshold_value, np.inf, foreground_value=1, edge_exclusion=edge).astype(int)
|
|
400
400
|
|
|
401
401
|
def preprocess_image(self):
|
|
402
402
|
# Preprocess the image before thresholding
|