celldetective 1.3.1__py3-none-any.whl → 1.3.3.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/_version.py +1 -1
- celldetective/events.py +2 -0
- celldetective/gui/classifier_widget.py +51 -3
- celldetective/gui/control_panel.py +9 -3
- celldetective/gui/generic_signal_plot.py +161 -2
- celldetective/gui/gui_utils.py +90 -1
- celldetective/gui/measurement_options.py +35 -32
- celldetective/gui/plot_signals_ui.py +8 -3
- celldetective/gui/process_block.py +36 -114
- celldetective/gui/retrain_segmentation_model_options.py +3 -1
- celldetective/gui/signal_annotator.py +53 -26
- celldetective/gui/signal_annotator2.py +17 -30
- celldetective/gui/survival_ui.py +7 -3
- celldetective/gui/tableUI.py +300 -183
- celldetective/gui/thresholds_gui.py +195 -199
- celldetective/gui/viewers.py +267 -13
- celldetective/io.py +110 -10
- celldetective/measure.py +128 -88
- celldetective/models/segmentation_effectors/ricm_bf_all_last/config_input.json +79 -0
- celldetective/models/segmentation_effectors/ricm_bf_all_last/ricm_bf_all_last +0 -0
- celldetective/models/segmentation_effectors/ricm_bf_all_last/training_instructions.json +37 -0
- celldetective/models/segmentation_effectors/test-transfer/config_input.json +39 -0
- celldetective/models/segmentation_effectors/test-transfer/test-transfer +0 -0
- celldetective/neighborhood.py +154 -69
- celldetective/relative_measurements.py +128 -4
- celldetective/scripts/measure_cells.py +3 -3
- celldetective/signals.py +207 -213
- celldetective/utils.py +16 -0
- {celldetective-1.3.1.dist-info → celldetective-1.3.3.post1.dist-info}/METADATA +11 -10
- {celldetective-1.3.1.dist-info → celldetective-1.3.3.post1.dist-info}/RECORD +34 -29
- {celldetective-1.3.1.dist-info → celldetective-1.3.3.post1.dist-info}/WHEEL +1 -1
- {celldetective-1.3.1.dist-info → celldetective-1.3.3.post1.dist-info}/LICENSE +0 -0
- {celldetective-1.3.1.dist-info → celldetective-1.3.3.post1.dist-info}/entry_points.txt +0 -0
- {celldetective-1.3.1.dist-info → celldetective-1.3.3.post1.dist-info}/top_level.txt +0 -0
celldetective/gui/tableUI.py
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
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
|
+
from PyQt5.QtGui import QBrush, QColor, QDoubleValidator
|
|
4
4
|
import pandas as pd
|
|
5
5
|
import matplotlib.pyplot as plt
|
|
6
6
|
plt.rcParams['svg.fonttype'] = 'none'
|
|
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
|
|
7
|
+
from celldetective.gui.gui_utils import FigureCanvas, center_window, QHSeperationLine, GenericOpColWidget, PandasModel
|
|
8
|
+
from celldetective.utils import differentiate_per_track, collapse_trajectories_by_status, test_2samp_generic, safe_log
|
|
9
|
+
from celldetective.neighborhood import extract_neighborhood_in_pair_table
|
|
10
|
+
from celldetective.relative_measurements import expand_pair_table
|
|
9
11
|
import numpy as np
|
|
10
12
|
import seaborn as sns
|
|
11
13
|
import matplotlib.cm as mcm
|
|
@@ -15,48 +17,10 @@ from superqt import QColormapComboBox, QLabeledSlider, QSearchableComboBox
|
|
|
15
17
|
from superqt.fonticon import icon
|
|
16
18
|
from fonticon_mdi6 import MDI6
|
|
17
19
|
from math import floor
|
|
20
|
+
import re
|
|
18
21
|
|
|
19
22
|
from matplotlib import colormaps
|
|
20
23
|
|
|
21
|
-
class PandasModel(QAbstractTableModel):
|
|
22
|
-
|
|
23
|
-
"""
|
|
24
|
-
from https://stackoverflow.com/questions/31475965/fastest-way-to-populate-qtableview-from-pandas-data-frame
|
|
25
|
-
"""
|
|
26
|
-
|
|
27
|
-
def __init__(self, data):
|
|
28
|
-
QAbstractTableModel.__init__(self)
|
|
29
|
-
self._data = data
|
|
30
|
-
self.colors = dict()
|
|
31
|
-
|
|
32
|
-
def rowCount(self, parent=None):
|
|
33
|
-
return self._data.shape[0]
|
|
34
|
-
|
|
35
|
-
def columnCount(self, parent=None):
|
|
36
|
-
return self._data.shape[1]
|
|
37
|
-
|
|
38
|
-
def data(self, index, role=Qt.DisplayRole):
|
|
39
|
-
if index.isValid():
|
|
40
|
-
if role == Qt.DisplayRole:
|
|
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
|
|
46
|
-
return None
|
|
47
|
-
|
|
48
|
-
def headerData(self, rowcol, orientation, role):
|
|
49
|
-
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
|
|
50
|
-
return self._data.columns[rowcol]
|
|
51
|
-
if orientation == Qt.Vertical and role == Qt.DisplayRole:
|
|
52
|
-
return self._data.index[rowcol]
|
|
53
|
-
return None
|
|
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
|
-
|
|
60
24
|
|
|
61
25
|
class QueryWidget(QWidget):
|
|
62
26
|
|
|
@@ -66,9 +30,7 @@ class QueryWidget(QWidget):
|
|
|
66
30
|
self.parent_window = parent_window
|
|
67
31
|
self.setWindowTitle("Filter table")
|
|
68
32
|
# Create the QComboBox and add some items
|
|
69
|
-
center_window(self)
|
|
70
33
|
|
|
71
|
-
|
|
72
34
|
layout = QHBoxLayout(self)
|
|
73
35
|
layout.setContentsMargins(30,30,30,30)
|
|
74
36
|
self.query_le = QLineEdit()
|
|
@@ -78,6 +40,7 @@ class QueryWidget(QWidget):
|
|
|
78
40
|
self.submit_btn.clicked.connect(self.filter_table)
|
|
79
41
|
layout.addWidget(self.submit_btn, 30)
|
|
80
42
|
self.setAttribute(Qt.WA_DeleteOnClose)
|
|
43
|
+
center_window(self)
|
|
81
44
|
|
|
82
45
|
def filter_table(self):
|
|
83
46
|
try:
|
|
@@ -252,47 +215,82 @@ class DifferentiateColWidget(QWidget, Styles):
|
|
|
252
215
|
self.parent_window.table_view.setModel(self.parent_window.model)
|
|
253
216
|
self.close()
|
|
254
217
|
|
|
255
|
-
class
|
|
218
|
+
class CalibrateColWidget(GenericOpColWidget):
|
|
219
|
+
|
|
220
|
+
def __init__(self, *args, **kwargs):
|
|
256
221
|
|
|
257
|
-
|
|
222
|
+
super().__init__(title="Calibrate data", *args, **kwargs)
|
|
258
223
|
|
|
259
|
-
|
|
260
|
-
self.
|
|
261
|
-
self.
|
|
224
|
+
self.floatValidator = QDoubleValidator()
|
|
225
|
+
self.calibration_factor_le = QLineEdit('1')
|
|
226
|
+
self.calibration_factor_le.setPlaceholderText('multiplicative calibration factor...')
|
|
227
|
+
self.calibration_factor_le.setValidator(self.floatValidator)
|
|
262
228
|
|
|
263
|
-
self.
|
|
264
|
-
|
|
265
|
-
center_window(self)
|
|
266
|
-
|
|
267
|
-
layout = QVBoxLayout(self)
|
|
268
|
-
layout.setContentsMargins(30,30,30,30)
|
|
229
|
+
self.units_le = QLineEdit('um')
|
|
230
|
+
self.units_le.setPlaceholderText('units...')
|
|
269
231
|
|
|
270
|
-
self.
|
|
271
|
-
self.
|
|
272
|
-
if self.column is not None:
|
|
273
|
-
idx = self.measurements_cb.findText(self.column)
|
|
274
|
-
self.measurements_cb.setCurrentIndex(idx)
|
|
232
|
+
self.calibration_factor_le.textChanged.connect(self.check_valid_params)
|
|
233
|
+
self.units_le.textChanged.connect(self.check_valid_params)
|
|
275
234
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
235
|
+
calib_layout = QHBoxLayout()
|
|
236
|
+
calib_layout.addWidget(QLabel('calibration factor: '), 33)
|
|
237
|
+
calib_layout.addWidget(self.calibration_factor_le, 66)
|
|
238
|
+
self.sublayout.addLayout(calib_layout)
|
|
280
239
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
240
|
+
units_layout = QHBoxLayout()
|
|
241
|
+
units_layout.addWidget(QLabel('units: '), 33)
|
|
242
|
+
units_layout.addWidget(self.units_le, 66)
|
|
243
|
+
self.sublayout.addLayout(units_layout)
|
|
285
244
|
|
|
286
|
-
|
|
245
|
+
# info_layout = QHBoxLayout()
|
|
246
|
+
# info_layout.addWidget(QLabel('For reference: '))
|
|
247
|
+
# self.sublayout.addLayout(info_layout)
|
|
248
|
+
|
|
249
|
+
# info_layout2 = QHBoxLayout()
|
|
250
|
+
# info_layout2.addWidget(QLabel(f'PxToUm = {self.parent_window.parent_window.parent_window.PxToUm}'), 50)
|
|
251
|
+
# info_layout2.addWidget(QLabel(f'FrameToMin = {self.parent_window.parent_window.parent_window.FrameToMin}'), 50)
|
|
252
|
+
# self.sublayout.addLayout(info_layout2)
|
|
253
|
+
|
|
254
|
+
def check_valid_params(self):
|
|
255
|
+
|
|
256
|
+
try:
|
|
257
|
+
factor = float(self.calibration_factor_le.text().replace(',','.'))
|
|
258
|
+
factor_valid = True
|
|
259
|
+
except Exception as e:
|
|
260
|
+
factor_valid = False
|
|
287
261
|
|
|
262
|
+
if self.units_le.text()=='':
|
|
263
|
+
units_valid = False
|
|
264
|
+
else:
|
|
265
|
+
units_valid = True
|
|
288
266
|
|
|
289
|
-
|
|
267
|
+
if factor_valid and units_valid:
|
|
268
|
+
self.submit_btn.setEnabled(True)
|
|
269
|
+
else:
|
|
270
|
+
self.submit_btn.setEnabled(False)
|
|
271
|
+
|
|
272
|
+
def compute(self):
|
|
273
|
+
self.parent_window.data[self.measurements_cb.currentText()+f'[{self.units_le.text()}]'] = self.parent_window.data[self.measurements_cb.currentText()] * float(self.calibration_factor_le.text().replace(',','.'))
|
|
290
274
|
|
|
291
275
|
|
|
276
|
+
class AbsColWidget(GenericOpColWidget):
|
|
277
|
+
|
|
278
|
+
def __init__(self, *args, **kwargs):
|
|
279
|
+
|
|
280
|
+
super().__init__(title="abs(.)", *args, **kwargs)
|
|
281
|
+
|
|
282
|
+
def compute(self):
|
|
292
283
|
self.parent_window.data['|'+self.measurements_cb.currentText()+'|'] = self.parent_window.data[self.measurements_cb.currentText()].abs()
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
284
|
+
|
|
285
|
+
class LogColWidget(GenericOpColWidget):
|
|
286
|
+
|
|
287
|
+
def __init__(self, *args, **kwargs):
|
|
288
|
+
|
|
289
|
+
super().__init__(title="log10(.)", *args, **kwargs)
|
|
290
|
+
|
|
291
|
+
def compute(self):
|
|
292
|
+
self.parent_window.data['log10('+self.measurements_cb.currentText()+')'] = safe_log(self.parent_window.data[self.measurements_cb.currentText()].values)
|
|
293
|
+
|
|
296
294
|
|
|
297
295
|
class RenameColWidget(QWidget):
|
|
298
296
|
|
|
@@ -437,7 +435,7 @@ class PivotTableUI(QWidget):
|
|
|
437
435
|
|
|
438
436
|
class TableUI(QMainWindow, Styles):
|
|
439
437
|
|
|
440
|
-
def __init__(self, data, title, population='targets',plot_mode="plot_track_signals", *args, **kwargs):
|
|
438
|
+
def __init__(self, data, title, population='targets',plot_mode="plot_track_signals", save_inplace_option=False, collapse_tracks_option=True, *args, **kwargs):
|
|
441
439
|
|
|
442
440
|
QMainWindow.__init__(self, *args, **kwargs)
|
|
443
441
|
|
|
@@ -450,6 +448,8 @@ class TableUI(QMainWindow, Styles):
|
|
|
450
448
|
self.numerics = ['int16', 'int32', 'int64', 'float16', 'float32', 'float64']
|
|
451
449
|
self.groupby_cols = ['position', 'TRACK_ID']
|
|
452
450
|
self.tracks = False
|
|
451
|
+
self.save_inplace_option = save_inplace_option
|
|
452
|
+
self.collapse_tracks_option = collapse_tracks_option
|
|
453
453
|
|
|
454
454
|
if self.population=='pairs':
|
|
455
455
|
self.groupby_cols = ['position','reference_population', 'neighbor_population','REFERENCE_ID', 'NEIGHBOR_ID']
|
|
@@ -458,6 +458,8 @@ class TableUI(QMainWindow, Styles):
|
|
|
458
458
|
if 'TRACK_ID' in data.columns:
|
|
459
459
|
if not np.all(data['TRACK_ID'].isnull()):
|
|
460
460
|
self.tracks = True
|
|
461
|
+
|
|
462
|
+
self.data = data
|
|
461
463
|
|
|
462
464
|
self._createMenuBar()
|
|
463
465
|
self._createActions()
|
|
@@ -466,7 +468,6 @@ class TableUI(QMainWindow, Styles):
|
|
|
466
468
|
self.setCentralWidget(self.table_view)
|
|
467
469
|
|
|
468
470
|
# Set the model for the table view
|
|
469
|
-
self.data = data
|
|
470
471
|
|
|
471
472
|
self.model = PandasModel(data)
|
|
472
473
|
self.table_view.setModel(self.model)
|
|
@@ -476,128 +477,199 @@ class TableUI(QMainWindow, Styles):
|
|
|
476
477
|
|
|
477
478
|
def _createActions(self):
|
|
478
479
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
480
|
+
self.save_as = QAction("&Save as...", self)
|
|
481
|
+
self.save_as.triggered.connect(self.save_as_csv)
|
|
482
|
+
self.save_as.setShortcut("Ctrl+s")
|
|
483
|
+
self.fileMenu.addAction(self.save_as)
|
|
483
484
|
|
|
485
|
+
if self.save_inplace_option:
|
|
484
486
|
self.save_inplace = QAction("&Save inplace...", self)
|
|
485
487
|
self.save_inplace.triggered.connect(self.save_as_csv_inplace_per_pos)
|
|
486
488
|
#self.save_inplace.setShortcut("Ctrl+s")
|
|
487
489
|
self.fileMenu.addAction(self.save_inplace)
|
|
488
490
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
491
|
+
self.plot_action = QAction("&Plot...", self)
|
|
492
|
+
self.plot_action.triggered.connect(self.plot)
|
|
493
|
+
self.plot_action.setShortcut("Ctrl+p")
|
|
494
|
+
self.fileMenu.addAction(self.plot_action)
|
|
493
495
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
496
|
+
self.plot_inst_action = QAction("&Plot instantaneous...", self)
|
|
497
|
+
self.plot_inst_action.triggered.connect(self.plot_instantaneous)
|
|
498
|
+
self.plot_inst_action.setShortcut("Ctrl+i")
|
|
499
|
+
self.fileMenu.addAction(self.plot_inst_action)
|
|
498
500
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
501
|
+
self.groupby_action = QAction("&Collapse tracks...", self)
|
|
502
|
+
self.groupby_action.triggered.connect(self.set_projection_mode_tracks)
|
|
503
|
+
self.groupby_action.setShortcut("Ctrl+g")
|
|
504
|
+
self.fileMenu.addAction(self.groupby_action)
|
|
505
|
+
if not self.tracks or not self.collapse_tracks_option:
|
|
506
|
+
self.groupby_action.setEnabled(False)
|
|
505
507
|
|
|
506
|
-
|
|
507
|
-
self.groupby_neigh_action = QAction("&Group by neighbors...", self)
|
|
508
|
-
self.groupby_neigh_action.triggered.connect(self.set_projection_mode_neigh)
|
|
509
|
-
self.fileMenu.addAction(self.groupby_neigh_action)
|
|
510
|
-
|
|
511
|
-
self.groupby_ref_action = QAction("&Group by reference...", self)
|
|
512
|
-
self.groupby_ref_action.triggered.connect(self.set_projection_mode_ref)
|
|
513
|
-
self.fileMenu.addAction(self.groupby_ref_action)
|
|
508
|
+
if self.population=='pairs':
|
|
514
509
|
|
|
510
|
+
self.groupby_pairs_in_neigh_action = QAction("&Collapse pairs in neighborhood...", self)
|
|
511
|
+
self.groupby_pairs_in_neigh_action.triggered.connect(self.collapse_pairs_in_neigh)
|
|
512
|
+
self.fileMenu.addAction(self.groupby_pairs_in_neigh_action)
|
|
515
513
|
|
|
514
|
+
if 'FRAME' in list(self.data.columns):
|
|
516
515
|
self.groupby_time_action = QAction("&Group by frames...", self)
|
|
517
516
|
self.groupby_time_action.triggered.connect(self.groupby_time_table)
|
|
518
517
|
self.groupby_time_action.setShortcut("Ctrl+t")
|
|
519
518
|
self.fileMenu.addAction(self.groupby_time_action)
|
|
520
519
|
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
520
|
+
self.query_action = QAction('Query...', self)
|
|
521
|
+
self.query_action.triggered.connect(self.perform_query)
|
|
522
|
+
self.fileMenu.addAction(self.query_action)
|
|
524
523
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
524
|
+
self.delete_action = QAction('&Delete...', self)
|
|
525
|
+
self.delete_action.triggered.connect(self.delete_columns)
|
|
526
|
+
self.delete_action.setShortcut(Qt.Key_Delete)
|
|
527
|
+
self.editMenu.addAction(self.delete_action)
|
|
529
528
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
529
|
+
self.rename_col_action = QAction('&Rename...', self)
|
|
530
|
+
self.rename_col_action.triggered.connect(self.rename_column)
|
|
531
|
+
#self.rename_col_action.setShortcut(Qt.Key_Delete)
|
|
532
|
+
self.editMenu.addAction(self.rename_col_action)
|
|
534
533
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
534
|
+
if self.population=='pairs':
|
|
535
|
+
self.merge_action = QAction('&Merge...', self)
|
|
536
|
+
self.merge_action.triggered.connect(self.merge_tables)
|
|
537
|
+
#self.rename_col_action.setShortcut(Qt.Key_Delete)
|
|
538
|
+
self.editMenu.addAction(self.merge_action)
|
|
539
|
+
|
|
540
|
+
self.calibrate_action = QAction('&Calibrate...', self)
|
|
541
|
+
self.calibrate_action.triggered.connect(self.calibrate_selected_feature)
|
|
542
|
+
self.calibrate_action.setShortcut("Ctrl+C")
|
|
543
|
+
self.mathMenu.addAction(self.calibrate_action)
|
|
544
|
+
|
|
545
|
+
self.derivative_action = QAction('&Differentiate...', self)
|
|
546
|
+
self.derivative_action.triggered.connect(self.differenciate_selected_feature)
|
|
547
|
+
self.derivative_action.setShortcut("Ctrl+D")
|
|
548
|
+
self.mathMenu.addAction(self.derivative_action)
|
|
549
|
+
|
|
550
|
+
self.abs_action = QAction('&Absolute value...', self)
|
|
551
|
+
self.abs_action.triggered.connect(self.take_abs_of_selected_feature)
|
|
552
|
+
#self.derivative_action.setShortcut("Ctrl+D")
|
|
553
|
+
self.mathMenu.addAction(self.abs_action)
|
|
554
|
+
|
|
555
|
+
self.log_action = QAction('&Log (decimal)...', self)
|
|
556
|
+
self.log_action.triggered.connect(self.take_log_of_selected_feature)
|
|
557
|
+
#self.derivative_action.setShortcut("Ctrl+D")
|
|
558
|
+
self.mathMenu.addAction(self.log_action)
|
|
559
|
+
|
|
560
|
+
self.onehot_action = QAction('&One hot to categorical...', self)
|
|
561
|
+
self.onehot_action.triggered.connect(self.transform_one_hot_cols_to_categorical)
|
|
562
|
+
#self.onehot_action.setShortcut("Ctrl+D")
|
|
563
|
+
self.mathMenu.addAction(self.onehot_action)
|
|
564
|
+
|
|
565
|
+
def collapse_pairs_in_neigh(self):
|
|
566
|
+
|
|
567
|
+
self.selectNeighWidget = QWidget()
|
|
568
|
+
self.selectNeighWidget.setMinimumWidth(480)
|
|
569
|
+
self.selectNeighWidget.setWindowTitle('Set neighborhood of interest')
|
|
570
|
+
|
|
571
|
+
layout = QVBoxLayout()
|
|
572
|
+
self.selectNeighWidget.setLayout(layout)
|
|
573
|
+
|
|
574
|
+
self.reference_lbl = QLabel('reference population: ')
|
|
575
|
+
self.reference_pop_cb = QComboBox()
|
|
576
|
+
ref_pops = self.data['reference_population'].unique()
|
|
577
|
+
self.reference_pop_cb.addItems(ref_pops)
|
|
578
|
+
self.reference_pop_cb.currentIndexChanged.connect(self.update_neighborhoods)
|
|
579
|
+
|
|
580
|
+
reference_hbox = QHBoxLayout()
|
|
581
|
+
reference_hbox.addWidget(self.reference_lbl, 33)
|
|
582
|
+
reference_hbox.addWidget(self.reference_pop_cb, 66)
|
|
583
|
+
layout.addLayout(reference_hbox)
|
|
584
|
+
|
|
585
|
+
self.neigh_lbl = QLabel('neighborhod: ')
|
|
586
|
+
self.neigh_cb = QComboBox()
|
|
587
|
+
neigh_cols = [c.replace('status_','') for c in list(self.data.loc[self.data['reference_population']==self.reference_pop_cb.currentText()].columns) if c.startswith('status_neighborhood')]
|
|
588
|
+
self.neigh_cb.addItems(neigh_cols)
|
|
589
|
+
|
|
590
|
+
neigh_hbox = QHBoxLayout()
|
|
591
|
+
neigh_hbox.addWidget(self.neigh_lbl, 33)
|
|
592
|
+
neigh_hbox.addWidget(self.neigh_cb, 66)
|
|
593
|
+
layout.addLayout(neigh_hbox)
|
|
594
|
+
|
|
595
|
+
contact_hbox = QHBoxLayout()
|
|
596
|
+
self.contact_only_check = QCheckBox('keep only pairs in contact')
|
|
597
|
+
self.contact_only_check.setChecked(True)
|
|
598
|
+
contact_hbox.addWidget(self.contact_only_check, alignment=Qt.AlignLeft)
|
|
599
|
+
layout.addLayout(contact_hbox)
|
|
600
|
+
|
|
601
|
+
self.groupby_pair_rb = QRadioButton('Group by pair')
|
|
602
|
+
self.groupby_reference_rb = QRadioButton('Group by reference')
|
|
603
|
+
self.groupby_pair_rb.setChecked(True)
|
|
604
|
+
|
|
605
|
+
groupby_hbox = QHBoxLayout()
|
|
606
|
+
groupby_hbox.addWidget(QLabel('collapse option: '), 33)
|
|
607
|
+
groupby_hbox.addWidget(self.groupby_pair_rb, (100-33)//2)
|
|
608
|
+
groupby_hbox.addWidget(self.groupby_reference_rb, (100-33)//2)
|
|
609
|
+
layout.addLayout(groupby_hbox)
|
|
610
|
+
|
|
611
|
+
self.apply_neigh_btn = QPushButton('Set')
|
|
612
|
+
self.apply_neigh_btn.setStyleSheet(self.button_style_sheet)
|
|
613
|
+
self.apply_neigh_btn.clicked.connect(self.prepare_table_at_neighborhood)
|
|
614
|
+
|
|
615
|
+
apply_hbox = QHBoxLayout()
|
|
616
|
+
apply_hbox.addWidget(QLabel(''),33)
|
|
617
|
+
apply_hbox.addWidget(self.apply_neigh_btn,66)
|
|
618
|
+
layout.addLayout(apply_hbox)
|
|
619
|
+
|
|
620
|
+
self.selectNeighWidget.show()
|
|
621
|
+
center_window(self.selectNeighWidget)
|
|
622
|
+
|
|
623
|
+
def prepare_table_at_neighborhood(self):
|
|
624
|
+
|
|
625
|
+
ref_pop = self.reference_pop_cb.currentText()
|
|
626
|
+
neighborhood = self.neigh_cb.currentText()
|
|
627
|
+
status_neigh = 'status_'+neighborhood
|
|
628
|
+
if 'self' in neighborhood:
|
|
629
|
+
neighbor_pop = ref_pop
|
|
630
|
+
elif ref_pop=='targets':
|
|
631
|
+
neighbor_pop = 'effectors'
|
|
632
|
+
elif ref_pop=='effectors':
|
|
633
|
+
neighbor_pop = "targets"
|
|
634
|
+
|
|
635
|
+
data = extract_neighborhood_in_pair_table(self.data, neighborhood_key=neighborhood, contact_only=self.contact_only_check.isChecked())
|
|
636
|
+
|
|
637
|
+
if self.groupby_pair_rb.isChecked():
|
|
638
|
+
self.groupby_cols = ['position', 'REFERENCE_ID', 'NEIGHBOR_ID']
|
|
639
|
+
elif self.groupby_reference_rb.isChecked():
|
|
640
|
+
self.groupby_cols = ['position', 'REFERENCE_ID']
|
|
641
|
+
|
|
642
|
+
self.current_data = data
|
|
643
|
+
skip_projection = False
|
|
644
|
+
if 'reference_tracked' in list(self.current_data.columns):
|
|
645
|
+
if np.all(self.current_data['reference_tracked'].astype(bool)==False):
|
|
646
|
+
# reference not tracked
|
|
647
|
+
if self.groupby_reference_rb.isChecked():
|
|
648
|
+
self.groupby_cols = ['position', 'FRAME', 'REFERENCE_ID']
|
|
649
|
+
elif self.groupby_pair_rb.isChecked():
|
|
650
|
+
print('The reference cells seem to not be tracked. No collapse can be performed.')
|
|
651
|
+
skip_projection=True
|
|
652
|
+
else:
|
|
653
|
+
if np.all(self.current_data['neighbors_tracked'].astype(bool)==False):
|
|
654
|
+
# neighbors not tracked
|
|
655
|
+
if self.groupby_pair_rb.isChecked():
|
|
656
|
+
print('The neighbor cells seem to not be tracked. No collapse can be performed.')
|
|
657
|
+
skip_projection=True
|
|
658
|
+
elif self.groupby_reference_rb.isChecked():
|
|
659
|
+
self.groupby_cols = ['position', 'REFERENCE_ID'] # think about what would be best
|
|
660
|
+
|
|
661
|
+
if not skip_projection:
|
|
662
|
+
self.set_projection_mode_tracks()
|
|
663
|
+
|
|
664
|
+
def update_neighborhoods(self):
|
|
665
|
+
|
|
666
|
+
neigh_cols = [c.replace('status_','') for c in list(self.data.loc[self.data['reference_population']==self.reference_pop_cb.currentText()].columns) if c.startswith('status_neighborhood')]
|
|
667
|
+
self.neigh_cb.clear()
|
|
668
|
+
self.neigh_cb.addItems(neigh_cols)
|
|
555
669
|
|
|
556
670
|
def merge_tables(self):
|
|
557
671
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
for neigh, group in self.data.groupby(['reference_population','neighbor_population']):
|
|
561
|
-
print(f'{neigh=}')
|
|
562
|
-
ref_pop = neigh[0]; neigh_pop = neigh[1];
|
|
563
|
-
for pos,pos_group in group.groupby('position'):
|
|
564
|
-
print(f'{pos=}')
|
|
565
|
-
|
|
566
|
-
ref_tab = os.sep.join([pos,'output','tables',f'trajectories_{ref_pop}.csv'])
|
|
567
|
-
neigh_tab = os.sep.join([pos,'output','tables',f'trajectories_{neigh_pop}.csv'])
|
|
568
|
-
if os.path.exists(ref_tab):
|
|
569
|
-
df_ref = pd.read_csv(ref_tab)
|
|
570
|
-
if 'TRACK_ID' in df_ref.columns:
|
|
571
|
-
if not np.all(df_ref['TRACK_ID'].isnull()):
|
|
572
|
-
ref_merge_cols = ['TRACK_ID','FRAME']
|
|
573
|
-
else:
|
|
574
|
-
ref_merge_cols = ['ID','FRAME']
|
|
575
|
-
else:
|
|
576
|
-
ref_merge_cols = ['ID','FRAME']
|
|
577
|
-
if os.path.exists(neigh_tab):
|
|
578
|
-
df_neigh = pd.read_csv(neigh_tab)
|
|
579
|
-
if 'TRACK_ID' in df_neigh.columns:
|
|
580
|
-
if not np.all(df_neigh['TRACK_ID'].isnull()):
|
|
581
|
-
neigh_merge_cols = ['TRACK_ID','FRAME']
|
|
582
|
-
else:
|
|
583
|
-
neigh_merge_cols = ['ID','FRAME']
|
|
584
|
-
else:
|
|
585
|
-
neigh_merge_cols = ['ID','FRAME']
|
|
586
|
-
|
|
587
|
-
df_ref = df_ref.add_prefix('reference_',axis=1)
|
|
588
|
-
df_neigh = df_neigh.add_prefix('neighbor_',axis=1)
|
|
589
|
-
ref_merge_cols = ['reference_'+c for c in ref_merge_cols]
|
|
590
|
-
neigh_merge_cols = ['neighbor_'+c for c in neigh_merge_cols]
|
|
591
|
-
|
|
592
|
-
merge_ref = pos_group.merge(df_ref, how='outer', left_on=['REFERENCE_ID','FRAME'], right_on=ref_merge_cols, suffixes=('', '_reference'))
|
|
593
|
-
print(f'{merge_ref.columns=}')
|
|
594
|
-
merge_neigh = merge_ref.merge(df_neigh, how='outer', left_on=['NEIGHBOR_ID','FRAME'], right_on=neigh_merge_cols, suffixes=('_reference', '_neighbor'))
|
|
595
|
-
print(f'{merge_neigh.columns=}')
|
|
596
|
-
expanded_table.append(merge_neigh)
|
|
597
|
-
|
|
598
|
-
df_expanded = pd.concat(expanded_table, axis=0, ignore_index = True)
|
|
599
|
-
df_expanded = df_expanded.sort_values(by=['position', 'reference_population','neighbor_population','REFERENCE_ID','NEIGHBOR_ID','FRAME'])
|
|
600
|
-
df_expanded = df_expanded.dropna(axis=0, subset=['REFERENCE_ID','NEIGHBOR_ID','reference_population','neighbor_population'])
|
|
672
|
+
df_expanded = expand_pair_table(self.data)
|
|
601
673
|
self.subtable = TableUI(df_expanded, 'merge', plot_mode = "static", population='pairs')
|
|
602
674
|
self.subtable.show()
|
|
603
675
|
|
|
@@ -668,6 +740,37 @@ class TableUI(QMainWindow, Styles):
|
|
|
668
740
|
self.diffWidget = DifferentiateColWidget(self, selected_col)
|
|
669
741
|
self.diffWidget.show()
|
|
670
742
|
|
|
743
|
+
def take_log_of_selected_feature(self):
|
|
744
|
+
|
|
745
|
+
# check only one col selected and assert is numerical
|
|
746
|
+
# open widget to select window parameters, directionality
|
|
747
|
+
# create new col
|
|
748
|
+
|
|
749
|
+
x = self.table_view.selectedIndexes()
|
|
750
|
+
col_idx = np.unique(np.array([l.column() for l in x]))
|
|
751
|
+
if col_idx!=0:
|
|
752
|
+
cols = np.array(list(self.data.columns))
|
|
753
|
+
selected_col = str(cols[col_idx][0])
|
|
754
|
+
else:
|
|
755
|
+
selected_col = None
|
|
756
|
+
|
|
757
|
+
self.LogWidget = LogColWidget(self, selected_col)
|
|
758
|
+
self.LogWidget.show()
|
|
759
|
+
|
|
760
|
+
def calibrate_selected_feature(self):
|
|
761
|
+
|
|
762
|
+
x = self.table_view.selectedIndexes()
|
|
763
|
+
col_idx = np.unique(np.array([l.column() for l in x]))
|
|
764
|
+
if col_idx!=0:
|
|
765
|
+
cols = np.array(list(self.data.columns))
|
|
766
|
+
selected_col = str(cols[col_idx][0])
|
|
767
|
+
else:
|
|
768
|
+
selected_col = None
|
|
769
|
+
|
|
770
|
+
self.calWidget = CalibrateColWidget(self, selected_col)
|
|
771
|
+
self.calWidget.show()
|
|
772
|
+
|
|
773
|
+
|
|
671
774
|
def take_abs_of_selected_feature(self):
|
|
672
775
|
|
|
673
776
|
# check only one col selected and assert is numerical
|
|
@@ -735,14 +838,18 @@ class TableUI(QMainWindow, Styles):
|
|
|
735
838
|
def set_projection_mode_neigh(self):
|
|
736
839
|
|
|
737
840
|
self.groupby_cols = ['position', 'reference_population', 'neighbor_population', 'NEIGHBOR_ID', 'FRAME']
|
|
841
|
+
self.current_data = self.data
|
|
738
842
|
self.set_projection_mode_tracks()
|
|
739
843
|
|
|
740
844
|
def set_projection_mode_ref(self):
|
|
741
845
|
|
|
742
846
|
self.groupby_cols = ['position', 'reference_population', 'neighbor_population', 'REFERENCE_ID', 'FRAME']
|
|
847
|
+
self.current_data = self.data
|
|
743
848
|
self.set_projection_mode_tracks()
|
|
744
849
|
|
|
745
850
|
def set_projection_mode_tracks(self):
|
|
851
|
+
|
|
852
|
+
self.current_data = self.data
|
|
746
853
|
|
|
747
854
|
self.projectionWidget = QWidget()
|
|
748
855
|
self.projectionWidget.setMinimumWidth(500)
|
|
@@ -855,6 +962,7 @@ class TableUI(QMainWindow, Styles):
|
|
|
855
962
|
self.kde_check = QCheckBox('KDE plot')
|
|
856
963
|
self.count_check = QCheckBox('countplot')
|
|
857
964
|
self.ecdf_check = QCheckBox('ECDF plot')
|
|
965
|
+
self.line_check = QCheckBox('line plot')
|
|
858
966
|
self.scat_check = QCheckBox('scatter plot')
|
|
859
967
|
self.swarm_check = QCheckBox('swarm')
|
|
860
968
|
self.violin_check = QCheckBox('violin')
|
|
@@ -870,6 +978,7 @@ class TableUI(QMainWindow, Styles):
|
|
|
870
978
|
layout.addWidget(self.kde_check)
|
|
871
979
|
layout.addWidget(self.count_check)
|
|
872
980
|
layout.addWidget(self.ecdf_check)
|
|
981
|
+
layout.addWidget(self.line_check)
|
|
873
982
|
layout.addWidget(self.scat_check)
|
|
874
983
|
layout.addWidget(self.swarm_check)
|
|
875
984
|
layout.addWidget(self.violin_check)
|
|
@@ -1009,7 +1118,15 @@ class TableUI(QMainWindow, Styles):
|
|
|
1009
1118
|
legend = False
|
|
1010
1119
|
else:
|
|
1011
1120
|
pass
|
|
1012
|
-
|
|
1121
|
+
|
|
1122
|
+
if self.line_check.isChecked():
|
|
1123
|
+
if self.x_option:
|
|
1124
|
+
sns.lineplot(data=self.data, x=self.x,y=self.y, hue=self.hue_variable,legend=legend, ax=self.ax, palette=colors)
|
|
1125
|
+
legend = False
|
|
1126
|
+
else:
|
|
1127
|
+
print('please provide a -x variable...')
|
|
1128
|
+
pass
|
|
1129
|
+
|
|
1013
1130
|
if self.scat_check.isChecked():
|
|
1014
1131
|
if self.x_option:
|
|
1015
1132
|
sns.scatterplot(data=self.data, x=self.x,y=self.y, hue=self.hue_variable,legend=legend, ax=self.ax, palette=colors)
|
|
@@ -1117,12 +1234,12 @@ class TableUI(QMainWindow, Styles):
|
|
|
1117
1234
|
if self.projection_option.isChecked():
|
|
1118
1235
|
|
|
1119
1236
|
self.projection_mode = self.projection_op_cb.currentText()
|
|
1120
|
-
op = getattr(self.
|
|
1121
|
-
group_table = op(self.
|
|
1237
|
+
op = getattr(self.current_data.groupby(self.groupby_cols), self.projection_mode)
|
|
1238
|
+
group_table = op(self.current_data.groupby(self.groupby_cols))
|
|
1122
1239
|
|
|
1123
1240
|
for c in self.static_columns:
|
|
1124
1241
|
try:
|
|
1125
|
-
group_table[c] = self.
|
|
1242
|
+
group_table[c] = self.current_data.groupby(self.groupby_cols)[c].apply(lambda x: x.unique()[0])
|
|
1126
1243
|
except Exception as e:
|
|
1127
1244
|
print(e)
|
|
1128
1245
|
pass
|
|
@@ -1144,7 +1261,7 @@ class TableUI(QMainWindow, Styles):
|
|
|
1144
1261
|
time_of_interest = self.event_times_cb.currentText()
|
|
1145
1262
|
self.projection_mode = f"measurements at {time_of_interest}"
|
|
1146
1263
|
new_table = []
|
|
1147
|
-
for tid,group in self.
|
|
1264
|
+
for tid,group in self.current_data.groupby(self.groupby_cols):
|
|
1148
1265
|
time = group[time_of_interest].values[0]
|
|
1149
1266
|
if time==time:
|
|
1150
1267
|
time = floor(time) # floor for onset
|
|
@@ -1153,7 +1270,7 @@ class TableUI(QMainWindow, Styles):
|
|
|
1153
1270
|
frames = group['FRAME'].values
|
|
1154
1271
|
values = group.loc[group['FRAME']==time,:].to_numpy()
|
|
1155
1272
|
if len(values)>0:
|
|
1156
|
-
values = dict(zip(list(self.
|
|
1273
|
+
values = dict(zip(list(self.current_data.columns), values[0]))
|
|
1157
1274
|
for k,c in enumerate(self.groupby_cols):
|
|
1158
1275
|
values.update({c: tid[k]})
|
|
1159
1276
|
new_table.append(values)
|
|
@@ -1174,9 +1291,9 @@ class TableUI(QMainWindow, Styles):
|
|
|
1174
1291
|
|
|
1175
1292
|
elif self.per_status_option.isChecked():
|
|
1176
1293
|
self.projection_mode = self.status_operation.currentText()
|
|
1177
|
-
group_table = collapse_trajectories_by_status(self.
|
|
1294
|
+
group_table = collapse_trajectories_by_status(self.current_data, status=self.per_status_cb.currentText(),population=self.population, projection=self.status_operation.currentText(), groupby_columns=self.groupby_cols)
|
|
1178
1295
|
|
|
1179
|
-
self.subtable = TableUI(group_table,f"Group by tracks: {self.projection_mode}", plot_mode="static")
|
|
1296
|
+
self.subtable = TableUI(group_table,f"Group by tracks: {self.projection_mode}", plot_mode="static", collapse_tracks_option=False)
|
|
1180
1297
|
self.subtable.show()
|
|
1181
1298
|
|
|
1182
1299
|
self.projectionWidget.close()
|