celldetective 1.2.2.post2__py3-none-any.whl → 1.3.0.post1__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 +1 -0
- celldetective/_version.py +1 -0
- celldetective/events.py +5 -0
- celldetective/gui/about.py +2 -1
- celldetective/gui/classifier_widget.py +43 -33
- celldetective/gui/control_panel.py +33 -23
- celldetective/gui/gui_utils.py +9 -3
- celldetective/gui/measurement_options.py +969 -969
- celldetective/gui/signal_annotator.py +65 -53
- celldetective/gui/tableUI.py +196 -26
- celldetective/io.py +4 -1
- celldetective/links/zenodo.json +359 -168
- celldetective/measure.py +46 -18
- celldetective/segmentation.py +17 -9
- celldetective/utils.py +82 -26
- {celldetective-1.2.2.post2.dist-info → celldetective-1.3.0.post1.dist-info}/METADATA +3 -1
- {celldetective-1.2.2.post2.dist-info → celldetective-1.3.0.post1.dist-info}/RECORD +22 -21
- tests/test_segmentation.py +5 -4
- {celldetective-1.2.2.post2.dist-info → celldetective-1.3.0.post1.dist-info}/LICENSE +0 -0
- {celldetective-1.2.2.post2.dist-info → celldetective-1.3.0.post1.dist-info}/WHEEL +0 -0
- {celldetective-1.2.2.post2.dist-info → celldetective-1.3.0.post1.dist-info}/entry_points.txt +0 -0
- {celldetective-1.2.2.post2.dist-info → celldetective-1.3.0.post1.dist-info}/top_level.txt +0 -0
|
@@ -1438,59 +1438,65 @@ class MeasureAnnotator(SignalAnnotator):
|
|
|
1438
1438
|
self.plot_signals()
|
|
1439
1439
|
|
|
1440
1440
|
def plot_signals(self):
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1441
|
+
|
|
1442
|
+
#try:
|
|
1443
|
+
current_frame = self.current_frame # Assuming you have a variable for the current frame
|
|
1444
|
+
|
|
1445
|
+
yvalues = []
|
|
1446
|
+
all_yvalues = []
|
|
1447
|
+
current_yvalues = []
|
|
1448
|
+
all_median_values = []
|
|
1449
|
+
labels = []
|
|
1450
|
+
|
|
1451
|
+
for i in range(len(self.signal_choice_cb)):
|
|
1452
|
+
|
|
1453
|
+
signal_choice = self.signal_choice_cb[i].currentText()
|
|
1450
1454
|
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
(self.df_tracks['FRAME'] == current_frame), signal_choice].to_numpy()
|
|
1456
|
-
else:
|
|
1457
|
-
ydata = self.df_tracks.loc[
|
|
1458
|
-
(self.df_tracks['ID'] == self.track_of_interest), signal_choice].to_numpy()
|
|
1459
|
-
all_ydata = self.df_tracks.loc[:, signal_choice].to_numpy()
|
|
1460
|
-
ydata = ydata[ydata == ydata] # remove nan
|
|
1461
|
-
current_ydata = self.df_tracks.loc[
|
|
1455
|
+
if signal_choice != "--":
|
|
1456
|
+
if 'TRACK_ID' in self.df_tracks.columns:
|
|
1457
|
+
ydata = self.df_tracks.loc[
|
|
1458
|
+
(self.df_tracks['TRACK_ID'] == self.track_of_interest) &
|
|
1462
1459
|
(self.df_tracks['FRAME'] == current_frame), signal_choice].to_numpy()
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1460
|
+
else:
|
|
1461
|
+
ydata = self.df_tracks.loc[
|
|
1462
|
+
(self.df_tracks['ID'] == self.track_of_interest), signal_choice].to_numpy()
|
|
1463
|
+
all_ydata = self.df_tracks.loc[:, signal_choice].to_numpy()
|
|
1464
|
+
ydataNaN = ydata
|
|
1465
|
+
ydata = ydata[ydata == ydata] # remove nan
|
|
1466
|
+
current_ydata = self.df_tracks.loc[
|
|
1467
|
+
(self.df_tracks['FRAME'] == current_frame), signal_choice].to_numpy()
|
|
1468
|
+
current_ydata = current_ydata[current_ydata == current_ydata]
|
|
1469
|
+
all_ydata = all_ydata[all_ydata == all_ydata]
|
|
1470
|
+
yvalues.extend(ydataNaN)
|
|
1471
|
+
current_yvalues.append(current_ydata)
|
|
1472
|
+
all_yvalues.append(all_ydata)
|
|
1473
|
+
labels.append(signal_choice)
|
|
1469
1474
|
|
|
1470
|
-
|
|
1475
|
+
self.cell_ax.clear()
|
|
1471
1476
|
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
+
if len(yvalues) > 0:
|
|
1478
|
+
self.cell_ax.boxplot(all_yvalues, showfliers=self.show_fliers)
|
|
1479
|
+
ylim = self.cell_ax.get_ylim()
|
|
1480
|
+
self.cell_ax.set_ylim(ylim)
|
|
1481
|
+
x_pos = np.arange(len(all_yvalues)) + 1
|
|
1477
1482
|
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1483
|
+
for index, feature in enumerate(current_yvalues):
|
|
1484
|
+
x_values_strip = (index + 1) + np.random.normal(0, 0.04, size=len(
|
|
1485
|
+
feature))
|
|
1486
|
+
self.cell_ax.plot(x_values_strip, feature, marker='o', linestyle='None', color=tab10.colors[0],
|
|
1487
|
+
alpha=0.1)
|
|
1488
|
+
|
|
1489
|
+
self.cell_ax.plot(x_pos, yvalues, marker='H', linestyle='None', color=tab10.colors[3], alpha=1)
|
|
1484
1490
|
|
|
1485
1491
|
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1492
|
+
else:
|
|
1493
|
+
self.cell_ax.text(0.5, 0.5, "No data available", horizontalalignment='center',
|
|
1494
|
+
verticalalignment='center', transform=self.cell_ax.transAxes)
|
|
1489
1495
|
|
|
1490
|
-
|
|
1496
|
+
self.cell_fcanvas.canvas.draw()
|
|
1491
1497
|
|
|
1492
|
-
except Exception as e:
|
|
1493
|
-
|
|
1498
|
+
# except Exception as e:
|
|
1499
|
+
# print("plot_signals: ",f"{e=}")
|
|
1494
1500
|
|
|
1495
1501
|
def configure_ylims(self):
|
|
1496
1502
|
|
|
@@ -1954,7 +1960,7 @@ class MeasureAnnotator(SignalAnnotator):
|
|
|
1954
1960
|
try:
|
|
1955
1961
|
cell_selected = f"cell: {self.track_of_interest}\n"
|
|
1956
1962
|
if 'TRACK_ID' in self.df_tracks.columns:
|
|
1957
|
-
cell_status = f"phenotype: {self.df_tracks.loc[self.df_tracks['TRACK_ID'] == self.track_of_interest, self.status_name].to_numpy()[0]}\n"
|
|
1963
|
+
cell_status = f"phenotype: {self.df_tracks.loc[(self.df_tracks['FRAME']==self.current_frame)&(self.df_tracks['TRACK_ID'] == self.track_of_interest), self.status_name].to_numpy()[0]}\n"
|
|
1958
1964
|
else:
|
|
1959
1965
|
cell_status = f"phenotype: {self.df_tracks.loc[self.df_tracks['ID'] == self.track_of_interest, self.status_name].to_numpy()[0]}\n"
|
|
1960
1966
|
self.cell_info.setText(cell_selected + cell_status)
|
|
@@ -2032,9 +2038,8 @@ class MeasureAnnotator(SignalAnnotator):
|
|
|
2032
2038
|
|
|
2033
2039
|
def assign_color_state(self, state):
|
|
2034
2040
|
if np.isnan(state):
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
return self.state_color_map[state]
|
|
2041
|
+
state = "nan"
|
|
2042
|
+
return self.state_color_map[state]
|
|
2038
2043
|
|
|
2039
2044
|
def draw_frame(self, framedata):
|
|
2040
2045
|
|
|
@@ -2045,10 +2050,10 @@ class MeasureAnnotator(SignalAnnotator):
|
|
|
2045
2050
|
self.frame_lbl.setText(f'position: {self.framedata}')
|
|
2046
2051
|
self.im.set_array(self.img)
|
|
2047
2052
|
self.status_scatter.set_offsets(self.positions[self.framedata])
|
|
2048
|
-
try:
|
|
2049
|
-
|
|
2050
|
-
except Exception as e:
|
|
2051
|
-
|
|
2053
|
+
# try:
|
|
2054
|
+
self.status_scatter.set_edgecolors(self.colors[self.framedata][:, 0])
|
|
2055
|
+
# except Exception as e:
|
|
2056
|
+
# pass
|
|
2052
2057
|
|
|
2053
2058
|
self.current_label = self.labels[self.current_frame]
|
|
2054
2059
|
self.current_label = contour_of_instance_segmentation(self.current_label, 5)
|
|
@@ -2060,18 +2065,23 @@ class MeasureAnnotator(SignalAnnotator):
|
|
|
2060
2065
|
return (self.im, self.status_scatter,self.im_mask,)
|
|
2061
2066
|
|
|
2062
2067
|
def compute_status_and_colors(self):
|
|
2068
|
+
print('compute status and colors!')
|
|
2063
2069
|
if self.class_choice_cb.currentText() == '':
|
|
2064
2070
|
self.status_name=self.target_class
|
|
2065
2071
|
else:
|
|
2066
2072
|
self.status_name = self.class_choice_cb.currentText()
|
|
2067
2073
|
|
|
2074
|
+
print(f'{self.status_name=}')
|
|
2068
2075
|
if self.status_name not in self.df_tracks.columns:
|
|
2076
|
+
print('not in df, make column')
|
|
2069
2077
|
self.make_status_column()
|
|
2070
2078
|
else:
|
|
2071
2079
|
all_states = self.df_tracks.loc[:, self.status_name].tolist()
|
|
2072
2080
|
all_states = np.array(all_states)
|
|
2073
2081
|
self.state_color_map = color_from_state(all_states, recently_modified=False)
|
|
2082
|
+
print(f'{self.state_color_map=}')
|
|
2074
2083
|
self.df_tracks['group_color'] = self.df_tracks[self.status_name].apply(self.assign_color_state)
|
|
2084
|
+
print(self.df_tracks['group_color'])
|
|
2075
2085
|
|
|
2076
2086
|
def del_event_class(self):
|
|
2077
2087
|
|
|
@@ -2342,9 +2352,11 @@ class MeasureAnnotator(SignalAnnotator):
|
|
|
2342
2352
|
# self.extract_scatter_from_trajectories()
|
|
2343
2353
|
|
|
2344
2354
|
def modify(self):
|
|
2355
|
+
|
|
2345
2356
|
all_states = self.df_tracks.loc[:, self.status_name].tolist()
|
|
2346
2357
|
all_states = np.array(all_states)
|
|
2347
2358
|
self.state_color_map = color_from_state(all_states, recently_modified=False)
|
|
2359
|
+
print(f'{self.state_color_map=}')
|
|
2348
2360
|
|
|
2349
2361
|
self.df_tracks['group_color'] = self.df_tracks[self.status_name].apply(self.assign_color_state)
|
|
2350
2362
|
|
|
@@ -2383,7 +2395,7 @@ class MeasureAnnotator(SignalAnnotator):
|
|
|
2383
2395
|
self.colors[t][idx, 0] = self.previous_color[k][0]
|
|
2384
2396
|
# self.colors[t][idx, 1] = self.previous_color[k][1]
|
|
2385
2397
|
except Exception as e:
|
|
2386
|
-
print(f'{e=}')
|
|
2398
|
+
print("cancel_selection: ",f'{e=}')
|
|
2387
2399
|
|
|
2388
2400
|
def locate_stack(self):
|
|
2389
2401
|
|
celldetective/gui/tableUI.py
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
from PyQt5.QtWidgets import QRadioButton, QButtonGroup, QMainWindow, QTableView, QAction, QMenu,QFileDialog, QLineEdit, QHBoxLayout, QWidget, QPushButton, QVBoxLayout, QComboBox, QLabel, QCheckBox, QMessageBox
|
|
2
2
|
from PyQt5.QtCore import Qt, QAbstractTableModel
|
|
3
|
+
from PyQt5.QtGui import QBrush, QColor
|
|
3
4
|
import pandas as pd
|
|
4
5
|
import matplotlib.pyplot as plt
|
|
5
6
|
plt.rcParams['svg.fonttype'] = 'none'
|
|
6
|
-
from celldetective.gui.gui_utils import FigureCanvas, center_window
|
|
7
|
-
from celldetective.utils import differentiate_per_track, collapse_trajectories_by_status
|
|
7
|
+
from celldetective.gui.gui_utils import FigureCanvas, center_window, QHSeperationLine
|
|
8
|
+
from celldetective.utils import differentiate_per_track, collapse_trajectories_by_status, test_2samp_generic
|
|
8
9
|
import numpy as np
|
|
9
10
|
import seaborn as sns
|
|
10
11
|
import matplotlib.cm as mcm
|
|
@@ -26,6 +27,7 @@ class PandasModel(QAbstractTableModel):
|
|
|
26
27
|
def __init__(self, data):
|
|
27
28
|
QAbstractTableModel.__init__(self)
|
|
28
29
|
self._data = data
|
|
30
|
+
self.colors = dict()
|
|
29
31
|
|
|
30
32
|
def rowCount(self, parent=None):
|
|
31
33
|
return self._data.shape[0]
|
|
@@ -37,13 +39,24 @@ class PandasModel(QAbstractTableModel):
|
|
|
37
39
|
if index.isValid():
|
|
38
40
|
if role == Qt.DisplayRole:
|
|
39
41
|
return str(self._data.iloc[index.row(), index.column()])
|
|
42
|
+
if role == Qt.BackgroundRole:
|
|
43
|
+
color = self.colors.get((index.row(), index.column()))
|
|
44
|
+
if color is not None:
|
|
45
|
+
return color
|
|
40
46
|
return None
|
|
41
47
|
|
|
42
|
-
def headerData(self,
|
|
48
|
+
def headerData(self, rowcol, orientation, role):
|
|
43
49
|
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
|
|
44
|
-
return self._data.columns[
|
|
50
|
+
return self._data.columns[rowcol]
|
|
51
|
+
if orientation == Qt.Vertical and role == Qt.DisplayRole:
|
|
52
|
+
return self._data.index[rowcol]
|
|
45
53
|
return None
|
|
46
54
|
|
|
55
|
+
def change_color(self, row, column, color):
|
|
56
|
+
ix = self.index(row, column)
|
|
57
|
+
self.colors[(row, column)] = color
|
|
58
|
+
self.dataChanged.emit(ix, ix, (Qt.BackgroundRole,))
|
|
59
|
+
|
|
47
60
|
|
|
48
61
|
class QueryWidget(QWidget):
|
|
49
62
|
|
|
@@ -316,6 +329,111 @@ class RenameColWidget(QWidget):
|
|
|
316
329
|
self.parent_window.table_view.setModel(self.parent_window.model)
|
|
317
330
|
self.close()
|
|
318
331
|
|
|
332
|
+
class PivotTableUI(QWidget):
|
|
333
|
+
|
|
334
|
+
def __init__(self, data, title="", mode=None, *args, **kwargs):
|
|
335
|
+
|
|
336
|
+
QWidget.__init__(self, *args, **kwargs)
|
|
337
|
+
|
|
338
|
+
self.data = data
|
|
339
|
+
self.title = title
|
|
340
|
+
self.mode = mode
|
|
341
|
+
|
|
342
|
+
self.setWindowTitle(title)
|
|
343
|
+
print("tab to show: ",self.data)
|
|
344
|
+
|
|
345
|
+
self.table = QTableView(self)
|
|
346
|
+
|
|
347
|
+
self.v_layout = QVBoxLayout()
|
|
348
|
+
self.information_label = QLabel('Information about color code...')
|
|
349
|
+
self.v_layout.addWidget(self.information_label)
|
|
350
|
+
self.v_layout.addWidget(self.table)
|
|
351
|
+
self.setLayout(self.v_layout)
|
|
352
|
+
|
|
353
|
+
self.showdata()
|
|
354
|
+
|
|
355
|
+
if self.mode=="cliff":
|
|
356
|
+
self.color_cells_cliff()
|
|
357
|
+
elif self.mode=="pvalue":
|
|
358
|
+
self.color_cells_pvalue()
|
|
359
|
+
|
|
360
|
+
self.table.resizeColumnsToContents()
|
|
361
|
+
self.setAttribute(Qt.WA_DeleteOnClose)
|
|
362
|
+
center_window(self)
|
|
363
|
+
|
|
364
|
+
def showdata(self):
|
|
365
|
+
self.model = PandasModel(self.data)
|
|
366
|
+
self.table.setModel(self.model)
|
|
367
|
+
|
|
368
|
+
def set_cell_color(self, row, column, color='red'):
|
|
369
|
+
self.model.change_color(row, column, QBrush(QColor(color))) #eval(f"Qt.{color}")
|
|
370
|
+
|
|
371
|
+
def color_cells_cliff(self):
|
|
372
|
+
|
|
373
|
+
color_codes = {
|
|
374
|
+
"negligible": "#eff3ff", # Green
|
|
375
|
+
"small": "#bdd7e7", # Yellow
|
|
376
|
+
"medium": "#6baed6", # Orange
|
|
377
|
+
"large": "#2171b5" # Red
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
for i in range(self.data.shape[0]):
|
|
381
|
+
for j in range(self.data.shape[1]):
|
|
382
|
+
value = self.data.iloc[i,j]
|
|
383
|
+
if value < 0.147:
|
|
384
|
+
self.set_cell_color(i,j,color_codes['negligible'])
|
|
385
|
+
elif value < 0.33:
|
|
386
|
+
self.set_cell_color(i,j,color_codes['small'])
|
|
387
|
+
elif value < 0.474:
|
|
388
|
+
self.set_cell_color(i,j,color_codes['medium'])
|
|
389
|
+
elif value >= 0.474:
|
|
390
|
+
self.set_cell_color(i,j,color_codes['large'])
|
|
391
|
+
|
|
392
|
+
# Create the HTML text for the label
|
|
393
|
+
html_caption = f"""
|
|
394
|
+
<p style="background-color:black; padding: 5px; font-weight:bold;">
|
|
395
|
+
<span style="color:{color_codes['negligible']}">Negligible</span>,
|
|
396
|
+
<span style="color:{color_codes['small']}">Small</span>,
|
|
397
|
+
<span style="color:{color_codes['medium']}">Medium</span>,
|
|
398
|
+
<span style="color:{color_codes['large']}">Large</span>
|
|
399
|
+
</p>
|
|
400
|
+
"""
|
|
401
|
+
self.information_label.setText(html_caption)
|
|
402
|
+
|
|
403
|
+
def color_cells_pvalue(self):
|
|
404
|
+
|
|
405
|
+
color_codes = {
|
|
406
|
+
"ns": "#fee5d9",
|
|
407
|
+
"*": "#fcae91",
|
|
408
|
+
"**": "#fb6a4a",
|
|
409
|
+
"***": "#de2d26",
|
|
410
|
+
"****": "#a50f15"
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
for i in range(self.data.shape[0]):
|
|
414
|
+
for j in range(self.data.shape[1]):
|
|
415
|
+
value = self.data.iloc[i,j]
|
|
416
|
+
if value <= 0.0001:
|
|
417
|
+
self.set_cell_color(i,j,color_codes['****'])
|
|
418
|
+
elif value <= 0.001:
|
|
419
|
+
self.set_cell_color(i,j,color_codes['***'])
|
|
420
|
+
elif value <= 0.01:
|
|
421
|
+
self.set_cell_color(i,j,color_codes['**'])
|
|
422
|
+
elif value <= 0.05:
|
|
423
|
+
self.set_cell_color(i,j,color_codes['*'])
|
|
424
|
+
elif value > 0.05:
|
|
425
|
+
self.set_cell_color(i,j,color_codes['ns'])
|
|
426
|
+
|
|
427
|
+
html_caption = f"""
|
|
428
|
+
<p style="background-color:black; padding: 5px; font-weight:bold;">
|
|
429
|
+
<span style="color:{color_codes['ns']}">ns</span>,
|
|
430
|
+
<span style="color:{color_codes['*']}">*</span>,
|
|
431
|
+
<span style="color:{color_codes['**']}">**</span>,
|
|
432
|
+
<span style="color:{color_codes['***']}">***</span>,
|
|
433
|
+
<span style="color:{color_codes['****']}">****</span>
|
|
434
|
+
</p>
|
|
435
|
+
"""
|
|
436
|
+
self.information_label.setText(html_caption)
|
|
319
437
|
|
|
320
438
|
class TableUI(QMainWindow, Styles):
|
|
321
439
|
|
|
@@ -744,6 +862,10 @@ class TableUI(QMainWindow, Styles):
|
|
|
744
862
|
self.box_check = QCheckBox('boxplot')
|
|
745
863
|
self.boxenplot_check = QCheckBox('boxenplot')
|
|
746
864
|
|
|
865
|
+
self.sep_line = QHSeperationLine()
|
|
866
|
+
self.pvalue_check = QCheckBox("Compute KS test p-value?")
|
|
867
|
+
self.effect_size_check = QCheckBox("Compute effect size?\n(Cliff's Delta)")
|
|
868
|
+
|
|
747
869
|
layout.addWidget(self.hist_check)
|
|
748
870
|
layout.addWidget(self.kde_check)
|
|
749
871
|
layout.addWidget(self.count_check)
|
|
@@ -754,6 +876,9 @@ class TableUI(QMainWindow, Styles):
|
|
|
754
876
|
layout.addWidget(self.strip_check)
|
|
755
877
|
layout.addWidget(self.box_check)
|
|
756
878
|
layout.addWidget(self.boxenplot_check)
|
|
879
|
+
layout.addWidget(self.sep_line)
|
|
880
|
+
layout.addWidget(self.pvalue_check)
|
|
881
|
+
layout.addWidget(self.effect_size_check)
|
|
757
882
|
|
|
758
883
|
self.x_cb = QSearchableComboBox()
|
|
759
884
|
self.x_cb.addItems(['--']+list(self.data.columns))
|
|
@@ -830,13 +955,13 @@ class TableUI(QMainWindow, Styles):
|
|
|
830
955
|
cmap = getattr(mcm, self.cmap_cb.currentText())
|
|
831
956
|
|
|
832
957
|
try:
|
|
833
|
-
hue_variable = self.hue_cb.currentText()
|
|
834
|
-
colors = [cmap(i / len(self.data[hue_variable].unique())) for i in range(len(self.data[hue_variable].unique()))]
|
|
958
|
+
self.hue_variable = self.hue_cb.currentText()
|
|
959
|
+
colors = [cmap(i / len(self.data[self.hue_variable].unique())) for i in range(len(self.data[self.hue_variable].unique()))]
|
|
835
960
|
except:
|
|
836
961
|
colors = None
|
|
837
962
|
|
|
838
963
|
if self.hue_cb.currentText()=='--':
|
|
839
|
-
hue_variable = None
|
|
964
|
+
self.hue_variable = None
|
|
840
965
|
|
|
841
966
|
if self.y_cb.currentText()=='--':
|
|
842
967
|
self.y = None
|
|
@@ -852,42 +977,42 @@ class TableUI(QMainWindow, Styles):
|
|
|
852
977
|
|
|
853
978
|
if self.hist_check.isChecked():
|
|
854
979
|
if self.x is not None:
|
|
855
|
-
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')
|
|
980
|
+
sns.histplot(data=self.data, x=self.x, hue=self.hue_variable, legend=legend, ax=self.ax, palette=colors, kde=True, common_norm=False, stat='density')
|
|
856
981
|
legend = False
|
|
857
982
|
elif self.x is None and self.y is not None:
|
|
858
|
-
sns.histplot(data=self.data, x=self.y, hue=hue_variable, legend=legend, ax=self.ax, palette=colors, kde=True, common_norm=False, stat='density')
|
|
983
|
+
sns.histplot(data=self.data, x=self.y, hue=self.hue_variable, legend=legend, ax=self.ax, palette=colors, kde=True, common_norm=False, stat='density')
|
|
859
984
|
legend = False
|
|
860
985
|
else:
|
|
861
986
|
pass
|
|
862
987
|
|
|
863
988
|
if self.kde_check.isChecked():
|
|
864
989
|
if self.x is not None:
|
|
865
|
-
sns.kdeplot(data=self.data, x=self.x, hue=hue_variable, legend=legend, ax=self.ax, palette=colors, cut=0)
|
|
990
|
+
sns.kdeplot(data=self.data, x=self.x, hue=self.hue_variable, legend=legend, ax=self.ax, palette=colors, cut=0)
|
|
866
991
|
legend = False
|
|
867
992
|
elif self.x is None and self.y is not None:
|
|
868
|
-
sns.kdeplot(data=self.data, x=self.y, hue=hue_variable, legend=legend, ax=self.ax, palette=colors, cut=0)
|
|
993
|
+
sns.kdeplot(data=self.data, x=self.y, hue=self.hue_variable, legend=legend, ax=self.ax, palette=colors, cut=0)
|
|
869
994
|
legend = False
|
|
870
995
|
else:
|
|
871
996
|
pass
|
|
872
997
|
|
|
873
998
|
if self.count_check.isChecked():
|
|
874
|
-
sns.countplot(data=self.data, x=self.x, hue=hue_variable, legend=legend, ax=self.ax, palette=colors)
|
|
999
|
+
sns.countplot(data=self.data, x=self.x, hue=self.hue_variable, legend=legend, ax=self.ax, palette=colors)
|
|
875
1000
|
legend = False
|
|
876
1001
|
|
|
877
1002
|
|
|
878
1003
|
if self.ecdf_check.isChecked():
|
|
879
1004
|
if self.x is not None:
|
|
880
|
-
sns.ecdfplot(data=self.data, x=self.x, hue=hue_variable, legend=legend, ax=self.ax, palette=colors)
|
|
1005
|
+
sns.ecdfplot(data=self.data, x=self.x, hue=self.hue_variable, legend=legend, ax=self.ax, palette=colors)
|
|
881
1006
|
legend = False
|
|
882
1007
|
elif self.x is None and self.y is not None:
|
|
883
|
-
sns.ecdfplot(data=self.data, x=self.y, hue=hue_variable, legend=legend, ax=self.ax, palette=colors)
|
|
1008
|
+
sns.ecdfplot(data=self.data, x=self.y, hue=self.hue_variable, legend=legend, ax=self.ax, palette=colors)
|
|
884
1009
|
legend = False
|
|
885
1010
|
else:
|
|
886
1011
|
pass
|
|
887
1012
|
|
|
888
1013
|
if self.scat_check.isChecked():
|
|
889
1014
|
if self.x_option:
|
|
890
|
-
sns.scatterplot(data=self.data, x=self.x,y=self.y, hue=hue_variable,legend=legend, ax=self.ax, palette=colors)
|
|
1015
|
+
sns.scatterplot(data=self.data, x=self.x,y=self.y, hue=self.hue_variable,legend=legend, ax=self.ax, palette=colors)
|
|
891
1016
|
legend = False
|
|
892
1017
|
else:
|
|
893
1018
|
print('please provide a -x variable...')
|
|
@@ -895,42 +1020,42 @@ class TableUI(QMainWindow, Styles):
|
|
|
895
1020
|
|
|
896
1021
|
if self.swarm_check.isChecked():
|
|
897
1022
|
if self.x_option:
|
|
898
|
-
sns.swarmplot(data=self.data, x=self.x,y=self.y,dodge=True, hue=hue_variable,legend=legend, ax=self.ax, palette=colors)
|
|
1023
|
+
sns.swarmplot(data=self.data, x=self.x,y=self.y,dodge=True, hue=self.hue_variable,legend=legend, ax=self.ax, palette=colors)
|
|
899
1024
|
legend = False
|
|
900
1025
|
else:
|
|
901
|
-
sns.swarmplot(data=self.data, y=self.y,dodge=True, hue=hue_variable,legend=legend, ax=self.ax, palette=colors)
|
|
1026
|
+
sns.swarmplot(data=self.data, y=self.y,dodge=True, hue=self.hue_variable,legend=legend, ax=self.ax, palette=colors)
|
|
902
1027
|
legend = False
|
|
903
1028
|
|
|
904
1029
|
if self.violin_check.isChecked():
|
|
905
1030
|
if self.x_option:
|
|
906
|
-
sns.violinplot(data=self.data,x=self.x, y=self.y,dodge=True, ax=self.ax, hue=hue_variable, legend=legend, palette=colors)
|
|
1031
|
+
sns.violinplot(data=self.data,x=self.x, y=self.y,dodge=True, ax=self.ax, hue=self.hue_variable, legend=legend, palette=colors)
|
|
907
1032
|
legend = False
|
|
908
1033
|
else:
|
|
909
|
-
sns.violinplot(data=self.data, y=self.y,dodge=True, hue=hue_variable,legend=legend, ax=self.ax, palette=colors, cut=0)
|
|
1034
|
+
sns.violinplot(data=self.data, y=self.y,dodge=True, hue=self.hue_variable,legend=legend, ax=self.ax, palette=colors, cut=0)
|
|
910
1035
|
legend = False
|
|
911
1036
|
|
|
912
1037
|
if self.box_check.isChecked():
|
|
913
1038
|
if self.x_option:
|
|
914
|
-
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,)
|
|
1039
|
+
sns.boxplot(data=self.data, x=self.x, y=self.y,dodge=True, hue=self.hue_variable,legend=legend, ax=self.ax, fill=False,palette=colors, linewidth=2,)
|
|
915
1040
|
legend = False
|
|
916
1041
|
else:
|
|
917
|
-
sns.boxplot(data=self.data, y=self.y,dodge=True, hue=hue_variable,legend=legend, ax=self.ax, fill=False,palette=colors, linewidth=2,)
|
|
1042
|
+
sns.boxplot(data=self.data, y=self.y,dodge=True, hue=self.hue_variable,legend=legend, ax=self.ax, fill=False,palette=colors, linewidth=2,)
|
|
918
1043
|
legend = False
|
|
919
1044
|
|
|
920
1045
|
if self.boxenplot_check.isChecked():
|
|
921
1046
|
if self.x_option:
|
|
922
|
-
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,)
|
|
1047
|
+
sns.boxenplot(data=self.data, x=self.x, y=self.y,dodge=True, hue=self.hue_variable,legend=legend, ax=self.ax, fill=False,palette=colors, linewidth=2,)
|
|
923
1048
|
legend = False
|
|
924
1049
|
else:
|
|
925
|
-
sns.boxenplot(data=self.data, y=self.y,dodge=True, hue=hue_variable,legend=legend, ax=self.ax, fill=False,palette=colors, linewidth=2,)
|
|
1050
|
+
sns.boxenplot(data=self.data, y=self.y,dodge=True, hue=self.hue_variable,legend=legend, ax=self.ax, fill=False,palette=colors, linewidth=2,)
|
|
926
1051
|
legend = False
|
|
927
1052
|
|
|
928
1053
|
if self.strip_check.isChecked():
|
|
929
1054
|
if self.x_option:
|
|
930
|
-
sns.stripplot(data=self.data, x = self.x, y=self.y,dodge=True, ax=self.ax, hue=hue_variable, legend=legend, palette=colors)
|
|
1055
|
+
sns.stripplot(data=self.data, x = self.x, y=self.y,dodge=True, ax=self.ax, hue=self.hue_variable, legend=legend, palette=colors)
|
|
931
1056
|
legend = False
|
|
932
1057
|
else:
|
|
933
|
-
sns.stripplot(data=self.data, y=self.y,dodge=True, ax=self.ax, hue=hue_variable, legend=legend, palette=colors)
|
|
1058
|
+
sns.stripplot(data=self.data, y=self.y,dodge=True, ax=self.ax, hue=self.hue_variable, legend=legend, palette=colors)
|
|
934
1059
|
legend = False
|
|
935
1060
|
|
|
936
1061
|
plt.tight_layout()
|
|
@@ -939,6 +1064,51 @@ class TableUI(QMainWindow, Styles):
|
|
|
939
1064
|
self.plot1dWindow.canvas.draw()
|
|
940
1065
|
self.plot1dWindow.show()
|
|
941
1066
|
|
|
1067
|
+
if self.effect_size_check.isChecked():
|
|
1068
|
+
self.compute_effect_size()
|
|
1069
|
+
if self.pvalue_check.isChecked():
|
|
1070
|
+
self.compute_pvalue()
|
|
1071
|
+
|
|
1072
|
+
def extract_groupby_cols(self):
|
|
1073
|
+
|
|
1074
|
+
x = self.x
|
|
1075
|
+
y = self.y
|
|
1076
|
+
hue_variable = self.hue_variable
|
|
1077
|
+
|
|
1078
|
+
if self.hist_check.isChecked() or self.ecdf_check.isChecked() or self.kde_check.isChecked():
|
|
1079
|
+
y = self.x
|
|
1080
|
+
x = None
|
|
1081
|
+
|
|
1082
|
+
groupby_cols = []
|
|
1083
|
+
if x is not None:
|
|
1084
|
+
groupby_cols.append(x)
|
|
1085
|
+
if hue_variable is not None:
|
|
1086
|
+
groupby_cols.append(hue_variable)
|
|
1087
|
+
|
|
1088
|
+
return groupby_cols, y
|
|
1089
|
+
|
|
1090
|
+
def compute_effect_size(self):
|
|
1091
|
+
|
|
1092
|
+
if self.count_check.isChecked() or self.scat_check.isChecked():
|
|
1093
|
+
print('Please select a valid plot representation to compute effect size (histogram, boxplot, etc.)...')
|
|
1094
|
+
return None
|
|
1095
|
+
|
|
1096
|
+
groupby_cols, y = self.extract_groupby_cols()
|
|
1097
|
+
pivot = test_2samp_generic(self.data, feature=y, groupby_cols=groupby_cols, method="cliffs_delta")
|
|
1098
|
+
self.effect_size_table = PivotTableUI(pivot, title="Effect size (Cliff's Delta)", mode="cliff")
|
|
1099
|
+
self.effect_size_table.show()
|
|
1100
|
+
|
|
1101
|
+
def compute_pvalue(self):
|
|
1102
|
+
|
|
1103
|
+
if self.count_check.isChecked() or self.scat_check.isChecked():
|
|
1104
|
+
print('Please select a valid plot representation to compute effect size (histogram, boxplot, etc.)...')
|
|
1105
|
+
return None
|
|
1106
|
+
|
|
1107
|
+
groupby_cols, y = self.extract_groupby_cols()
|
|
1108
|
+
pivot = test_2samp_generic(self.data, feature=y, groupby_cols=groupby_cols, method="ks_2samp")
|
|
1109
|
+
self.pval_table = PivotTableUI(pivot, title="p-value (1-sided KS test)", mode="pvalue")
|
|
1110
|
+
self.pval_table.show()
|
|
1111
|
+
|
|
942
1112
|
|
|
943
1113
|
def set_proj_mode(self):
|
|
944
1114
|
|
|
@@ -1003,7 +1173,7 @@ class TableUI(QMainWindow, Styles):
|
|
|
1003
1173
|
|
|
1004
1174
|
|
|
1005
1175
|
elif self.per_status_option.isChecked():
|
|
1006
|
-
|
|
1176
|
+
self.projection_mode = self.status_operation.currentText()
|
|
1007
1177
|
group_table = collapse_trajectories_by_status(self.data, status=self.per_status_cb.currentText(),population=self.population, projection=self.status_operation.currentText(), groupby_columns=self.groupby_cols)
|
|
1008
1178
|
|
|
1009
1179
|
self.subtable = TableUI(group_table,f"Group by tracks: {self.projection_mode}", plot_mode="static")
|
celldetective/io.py
CHANGED
|
@@ -766,6 +766,9 @@ def locate_stack_and_labels(position, prefix='Aligned', population="target"):
|
|
|
766
766
|
position = position.replace('\\', '/')
|
|
767
767
|
labels = locate_labels(position, population=population)
|
|
768
768
|
stack = locate_stack(position, prefix=prefix)
|
|
769
|
+
if len(labels) < len(stack):
|
|
770
|
+
fix_missing_labels(position, population=population, prefix=prefix)
|
|
771
|
+
labels = locate_labels(position, population=population)
|
|
769
772
|
assert len(stack) == len(
|
|
770
773
|
labels), f"The shape of the stack {stack.shape} does not match with the shape of the labels {labels.shape}"
|
|
771
774
|
|
|
@@ -1272,7 +1275,7 @@ def view_on_napari_btrack(data, properties, graph, stack=None, labels=None, rela
|
|
|
1272
1275
|
if stack is not None:
|
|
1273
1276
|
viewer.add_image(stack, channel_axis=-1, colormap=["gray"] * stack.shape[-1])
|
|
1274
1277
|
if labels is not None:
|
|
1275
|
-
viewer.add_labels(labels, name='segmentation', opacity=0.4)
|
|
1278
|
+
viewer.add_labels(labels.astype(int), name='segmentation', opacity=0.4)
|
|
1276
1279
|
viewer.add_points(vertices, size=4, name='points', opacity=0.3)
|
|
1277
1280
|
if data.shape[1]==4:
|
|
1278
1281
|
viewer.add_tracks(data, properties=properties, graph=graph, name='tracks')
|