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.
Files changed (34) hide show
  1. celldetective/_version.py +1 -1
  2. celldetective/events.py +2 -0
  3. celldetective/gui/classifier_widget.py +51 -3
  4. celldetective/gui/control_panel.py +9 -3
  5. celldetective/gui/generic_signal_plot.py +161 -2
  6. celldetective/gui/gui_utils.py +90 -1
  7. celldetective/gui/measurement_options.py +35 -32
  8. celldetective/gui/plot_signals_ui.py +8 -3
  9. celldetective/gui/process_block.py +36 -114
  10. celldetective/gui/retrain_segmentation_model_options.py +3 -1
  11. celldetective/gui/signal_annotator.py +53 -26
  12. celldetective/gui/signal_annotator2.py +17 -30
  13. celldetective/gui/survival_ui.py +7 -3
  14. celldetective/gui/tableUI.py +300 -183
  15. celldetective/gui/thresholds_gui.py +195 -199
  16. celldetective/gui/viewers.py +267 -13
  17. celldetective/io.py +110 -10
  18. celldetective/measure.py +128 -88
  19. celldetective/models/segmentation_effectors/ricm_bf_all_last/config_input.json +79 -0
  20. celldetective/models/segmentation_effectors/ricm_bf_all_last/ricm_bf_all_last +0 -0
  21. celldetective/models/segmentation_effectors/ricm_bf_all_last/training_instructions.json +37 -0
  22. celldetective/models/segmentation_effectors/test-transfer/config_input.json +39 -0
  23. celldetective/models/segmentation_effectors/test-transfer/test-transfer +0 -0
  24. celldetective/neighborhood.py +154 -69
  25. celldetective/relative_measurements.py +128 -4
  26. celldetective/scripts/measure_cells.py +3 -3
  27. celldetective/signals.py +207 -213
  28. celldetective/utils.py +16 -0
  29. {celldetective-1.3.1.dist-info → celldetective-1.3.3.post1.dist-info}/METADATA +11 -10
  30. {celldetective-1.3.1.dist-info → celldetective-1.3.3.post1.dist-info}/RECORD +34 -29
  31. {celldetective-1.3.1.dist-info → celldetective-1.3.3.post1.dist-info}/WHEEL +1 -1
  32. {celldetective-1.3.1.dist-info → celldetective-1.3.3.post1.dist-info}/LICENSE +0 -0
  33. {celldetective-1.3.1.dist-info → celldetective-1.3.3.post1.dist-info}/entry_points.txt +0 -0
  34. {celldetective-1.3.1.dist-info → celldetective-1.3.3.post1.dist-info}/top_level.txt +0 -0
@@ -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 AbsColWidget(QWidget, Styles):
218
+ class CalibrateColWidget(GenericOpColWidget):
219
+
220
+ def __init__(self, *args, **kwargs):
256
221
 
257
- def __init__(self, parent_window, column=None):
222
+ super().__init__(title="Calibrate data", *args, **kwargs)
258
223
 
259
- super().__init__()
260
- self.parent_window = parent_window
261
- self.column = column
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.setWindowTitle("abs(.)")
264
- # Create the QComboBox and add some items
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.measurements_cb = QComboBox()
271
- self.measurements_cb.addItems(list(self.parent_window.data.columns))
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
- measurement_layout = QHBoxLayout()
277
- measurement_layout.addWidget(QLabel('measurements: '), 25)
278
- measurement_layout.addWidget(self.measurements_cb, 75)
279
- layout.addLayout(measurement_layout)
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
- self.submit_btn = QPushButton('Compute')
282
- self.submit_btn.setStyleSheet(self.button_style_sheet)
283
- self.submit_btn.clicked.connect(self.compute_abs_and_add_new_column)
284
- layout.addWidget(self.submit_btn, 30)
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
- self.setAttribute(Qt.WA_DeleteOnClose)
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
- def compute_abs_and_add_new_column(self):
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
- self.parent_window.model = PandasModel(self.parent_window.data)
294
- self.parent_window.table_view.setModel(self.parent_window.model)
295
- self.close()
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
- self.save_as = QAction("&Save as...", self)
480
- self.save_as.triggered.connect(self.save_as_csv)
481
- self.save_as.setShortcut("Ctrl+s")
482
- self.fileMenu.addAction(self.save_as)
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
- self.plot_action = QAction("&Plot...", self)
490
- self.plot_action.triggered.connect(self.plot)
491
- self.plot_action.setShortcut("Ctrl+p")
492
- self.fileMenu.addAction(self.plot_action)
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
- self.plot_inst_action = QAction("&Plot instantaneous...", self)
495
- self.plot_inst_action.triggered.connect(self.plot_instantaneous)
496
- self.plot_inst_action.setShortcut("Ctrl+i")
497
- self.fileMenu.addAction(self.plot_inst_action)
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
- self.groupby_action = QAction("&Group by tracks...", self)
500
- self.groupby_action.triggered.connect(self.set_projection_mode_tracks)
501
- self.groupby_action.setShortcut("Ctrl+g")
502
- self.fileMenu.addAction(self.groupby_action)
503
- if not self.tracks:
504
- self.groupby_action.setEnabled(False)
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
- if self.population=='pairs':
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
- self.query_action = QAction('Query...', self)
522
- self.query_action.triggered.connect(self.perform_query)
523
- self.fileMenu.addAction(self.query_action)
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
- self.delete_action = QAction('&Delete...', self)
526
- self.delete_action.triggered.connect(self.delete_columns)
527
- self.delete_action.setShortcut(Qt.Key_Delete)
528
- self.editMenu.addAction(self.delete_action)
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
- self.rename_col_action = QAction('&Rename...', self)
531
- self.rename_col_action.triggered.connect(self.rename_column)
532
- #self.rename_col_action.setShortcut(Qt.Key_Delete)
533
- self.editMenu.addAction(self.rename_col_action)
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
- if self.population=='pairs':
536
- self.merge_action = QAction('&Merge...', self)
537
- self.merge_action.triggered.connect(self.merge_tables)
538
- #self.rename_col_action.setShortcut(Qt.Key_Delete)
539
- self.editMenu.addAction(self.merge_action)
540
-
541
- self.derivative_action = QAction('&Differentiate...', self)
542
- self.derivative_action.triggered.connect(self.differenciate_selected_feature)
543
- self.derivative_action.setShortcut("Ctrl+D")
544
- self.mathMenu.addAction(self.derivative_action)
545
-
546
- self.abs_action = QAction('&Absolute value...', self)
547
- self.abs_action.triggered.connect(self.take_abs_of_selected_feature)
548
- #self.derivative_action.setShortcut("Ctrl+D")
549
- self.mathMenu.addAction(self.abs_action)
550
-
551
- self.onehot_action = QAction('&One hot to categorical...', self)
552
- self.onehot_action.triggered.connect(self.transform_one_hot_cols_to_categorical)
553
- #self.onehot_action.setShortcut("Ctrl+D")
554
- self.mathMenu.addAction(self.onehot_action)
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
- expanded_table = []
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.data.groupby(self.groupby_cols), self.projection_mode)
1121
- group_table = op(self.data.groupby(self.groupby_cols))
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.data.groupby(self.groupby_cols)[c].apply(lambda x: x.unique()[0])
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.data.groupby(self.groupby_cols):
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.data.columns), values[0]))
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.data, status=self.per_status_cb.currentText(),population=self.population, projection=self.status_operation.currentText(), groupby_columns=self.groupby_cols)
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()