celldetective 1.3.4.post1__py3-none-any.whl → 1.3.5__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.
@@ -429,27 +429,12 @@ class SignalAnnotator2(QMainWindow,Styles):
429
429
  contrast_hbox.addWidget(self.contrast_slider,90)
430
430
  self.right_panel.addLayout(contrast_hbox, 5)
431
431
 
432
- # speed_hbox = QHBoxLayout()
433
- # speed_hbox.setContentsMargins(150,5,150,5)
434
- # self.interval_slider = QLabeledSlider()
435
- # self.interval_slider.setSingleStep(1)
436
- # self.interval_slider.setTickInterval(1)
437
- # self.interval_slider.setOrientation(1)
438
- # self.interval_slider.setRange(1, 10000)
439
- # self.interval_slider.setValue(self.speed)
440
- # self.interval_slider.valueChanged.connect(self.interval_slider_action)
441
- # speed_hbox.addWidget(QLabel('interval (ms): '))
442
- # speed_hbox.addWidget(self.interval_slider,90)
443
- # self.right_panel.addLayout(speed_hbox, 10)
444
-
445
- #self.selected_populationulate_left_panel()
446
- #grid.addLayout(self.left_side, 0, 0, 1, 1)
447
-
448
432
  main_layout.addLayout(self.left_panel, 35)
449
433
  main_layout.addLayout(self.right_panel, 65)
450
434
  self.button_widget.adjustSize()
451
435
  self.compute_status_and_colors_reference()
452
436
 
437
+ self.extract_relevant_events()
453
438
 
454
439
  self.setCentralWidget(self.button_widget)
455
440
  self.show()
@@ -673,23 +658,11 @@ class SignalAnnotator2(QMainWindow,Styles):
673
658
  self.df_relative.loc[(~self.df_relative['status_'+self.current_neighborhood].isnull())&(self.df_relative['reference_population']==self.reference_population),self.relative_time] = 0.1
674
659
  else:
675
660
  self.df_relative.loc[(~self.df_relative['status_'+self.current_neighborhood].isnull())&(self.df_relative['reference_population']==self.reference_population),self.relative_time] = -1
676
- self.relative_class_choice_cb.disconnect()
677
- self.relative_class_choice_cb.clear()
678
- cols = np.array(self.df_relative.columns)
679
- self.relative_class_cols = np.array([c.startswith('class') for c in list(self.df_relative.columns)])
680
- self.relative_class_cols = list(cols[self.relative_class_cols])
681
- try:
682
- self.relative_class_cols.remove('class_color')
683
- self.relative_class_cols.remove('class_id')
684
- except:
685
- pass
686
- self.relative_class_choice_cb.currentIndexChanged.connect(self.compute_status_and_colors_pair)
687
- self.relative_class_choice_cb.addItems(self.relative_class_cols)
688
- idx = self.relative_class_choice_cb.findText(self.relative_class)
689
- self.relative_class_choice_cb.setCurrentIndex(idx)
661
+
690
662
 
691
- self.pair_class_name = self.relative_class
663
+ self.extract_relevant_events()
692
664
 
665
+ self.pair_class_name = self.relative_class
693
666
  self.pair_time_name = self.relative_time
694
667
  self.pair_status_name = self.relative_status
695
668
 
@@ -705,167 +678,131 @@ class SignalAnnotator2(QMainWindow,Styles):
705
678
 
706
679
  df_reference = self.dataframes[self.reference_population]
707
680
  self.reference_class_name = self.reference_event_choice_cb.currentText()
708
- self.expected_reference_status = 'status_'
709
- suffix = self.reference_class_name.replace('class','').replace('_','')
710
- if suffix!='':
711
- self.expected_reference_status+='_'+suffix
712
- self.expected_reference_time = 't_'+suffix
713
- else:
714
- self.expected_reference_time = 't0'
715
681
 
716
- self.reference_time_name = self.expected_reference_time
717
- self.reference_status_name = self.expected_reference_status
682
+ if self.reference_class_name!='':
718
683
 
719
- if self.reference_time_name in list(df_reference.columns) and self.reference_class_name in list(df_reference.columns) and not self.reference_status_name in list(df_reference.columns):
720
- # only create the status column if it does not exist to not erase static classification results
721
- self.make_reference_status_column()
722
- elif self.reference_time_name in list(df_reference.columns) and self.reference_class_name in list(df_reference.columns):
723
- # all good, do nothing
724
- pass
725
- else:
726
- if not self.reference_status_name in list(df_reference.columns):
727
- df_reference[self.reference_status_name] = 0
728
- df_reference['status_color'] = color_from_status(0)
729
- df_reference['class_color'] = color_from_class(1)
684
+ self.expected_reference_status = 'status_'
685
+ suffix = self.reference_class_name.replace('class','').replace('_','')
686
+ if suffix!='':
687
+ self.expected_reference_status+='_'+suffix
688
+ self.expected_reference_time = 't_'+suffix
689
+ else:
690
+ self.expected_reference_time = 't0'
730
691
 
731
- if not self.reference_class_name in list(df_reference.columns):
732
- df_reference[self.reference_class_name] = 1
733
- if not self.reference_time_name in list(df_reference.columns):
734
- df_reference[self.reference_time_name] = -1
692
+ self.reference_time_name = self.expected_reference_time
693
+ self.reference_status_name = self.expected_reference_status
735
694
 
736
- df_reference['status_color'] = [color_from_status(i) for i in df_reference[self.reference_status_name].to_numpy()]
737
- df_reference['class_color'] = [color_from_class(i) for i in df_reference[self.reference_class_name].to_numpy()]
695
+ if self.reference_time_name in list(df_reference.columns) and self.reference_class_name in list(df_reference.columns) and not self.reference_status_name in list(df_reference.columns):
696
+ # only create the status column if it does not exist to not erase static classification results
697
+ self.make_reference_status_column()
698
+ elif self.reference_time_name in list(df_reference.columns) and self.reference_class_name in list(df_reference.columns):
699
+ # all good, do nothing
700
+ pass
701
+ else:
702
+ if not self.reference_status_name in list(df_reference.columns):
703
+ df_reference[self.reference_status_name] = 0
704
+ df_reference['status_color'] = color_from_status(0)
705
+ df_reference['class_color'] = color_from_class(1)
738
706
 
739
- if self.reference_population=='targets':
740
- self.extract_scatter_from_target_trajectories()
741
- else:
742
- self.extract_scatter_from_effector_trajectories()
707
+ if not self.reference_class_name in list(df_reference.columns):
708
+ df_reference[self.reference_class_name] = 1
709
+ if not self.reference_time_name in list(df_reference.columns):
710
+ df_reference[self.reference_time_name] = -1
711
+
712
+ df_reference['status_color'] = [color_from_status(i) for i in df_reference[self.reference_status_name].to_numpy()]
713
+ df_reference['class_color'] = [color_from_class(i) for i in df_reference[self.reference_class_name].to_numpy()]
714
+
715
+ if self.reference_population=='targets':
716
+ self.extract_scatter_from_target_trajectories()
717
+ else:
718
+ self.extract_scatter_from_effector_trajectories()
743
719
 
744
720
 
745
721
  def compute_status_and_colors_neighbor(self):
746
722
 
747
723
  df_neighbors = self.dataframes[self.neighbor_population]
748
724
  self.neighbor_class_name = self.neighbor_event_choice_cb.currentText()
749
- self.expected_neighbor_status = 'status_'
750
- suffix = self.neighbor_class_name.replace('class','').replace('_','')
751
- if suffix!='':
752
- self.expected_neighbor_status+='_'+suffix
753
- self.expected_neighbor_time = 't_'+suffix
754
- else:
755
- self.expected_neighbor_time = 't0'
756
725
 
757
- self.neighbor_time_name = self.expected_neighbor_time
758
- self.neighbor_status_name = self.expected_neighbor_status
726
+ if self.neighbor_class_name!='':
759
727
 
760
- if self.neighbor_time_name in list(df_neighbors.columns) and self.neighbor_class_name in list(df_neighbors.columns) and not self.neighbor_status_name in list(df_neighbors.columns):
761
- # only create the status column if it does not exist to not erase static classification results
762
- self.make_neighbor_status_column()
763
- elif self.neighbor_time_name in list(df_neighbors.columns) and self.neighbor_class_name in list(df_neighbors.columns):
764
- # all good, do nothing
765
- pass
766
- else:
767
- if not self.neighbor_status_name in list(df_neighbors.columns):
768
- df_neighbors[self.neighbor_status_name] = 0
769
- df_neighbors['status_color'] = color_from_status(0)
770
- df_neighbors['class_color'] = color_from_class(1)
728
+ self.expected_neighbor_status = 'status_'
729
+ suffix = self.neighbor_class_name.replace('class','').replace('_','')
730
+ if suffix!='':
731
+ self.expected_neighbor_status+='_'+suffix
732
+ self.expected_neighbor_time = 't_'+suffix
733
+ else:
734
+ self.expected_neighbor_time = 't0'
771
735
 
772
- if not self.neighbor_class_name in list(df_neighbors.columns):
773
- df_neighbors[self.neighbor_class_name] = 1
774
- if not self.neighbor_time_name in list(df_neighbors.columns):
775
- df_neighbors[self.neighbor_time_name] = -1
736
+ self.neighbor_time_name = self.expected_neighbor_time
737
+ self.neighbor_status_name = self.expected_neighbor_status
776
738
 
777
- df_neighbors['status_color'] = [color_from_status(i) for i in df_neighbors[self.neighbor_status_name].to_numpy()]
778
- df_neighbors['class_color'] = [color_from_class(i) for i in df_neighbors[self.neighbor_class_name].to_numpy()]
739
+ if self.neighbor_time_name in list(df_neighbors.columns) and self.neighbor_class_name in list(df_neighbors.columns) and not self.neighbor_status_name in list(df_neighbors.columns):
740
+ # only create the status column if it does not exist to not erase static classification results
741
+ self.make_neighbor_status_column()
742
+ elif self.neighbor_time_name in list(df_neighbors.columns) and self.neighbor_class_name in list(df_neighbors.columns):
743
+ # all good, do nothing
744
+ pass
745
+ else:
746
+ if not self.neighbor_status_name in list(df_neighbors.columns):
747
+ df_neighbors[self.neighbor_status_name] = 0
748
+ df_neighbors['status_color'] = color_from_status(0)
749
+ df_neighbors['class_color'] = color_from_class(1)
779
750
 
780
- if self.neighbor_population=='targets':
781
- self.extract_scatter_from_target_trajectories()
782
- else:
783
- self.extract_scatter_from_effector_trajectories()
751
+ if not self.neighbor_class_name in list(df_neighbors.columns):
752
+ df_neighbors[self.neighbor_class_name] = 1
753
+ if not self.neighbor_time_name in list(df_neighbors.columns):
754
+ df_neighbors[self.neighbor_time_name] = -1
784
755
 
785
- # if self.df_effectors is not None:
786
- # if self.reference_population=='effectors':
787
- # self.effector_class_name = self.reference_event_choice_cb.currentText()
788
- # elif self.neighbor_population == 'effectors':
789
- # self.effector_class_name = self.neighbor_event_choice_cb.currentText()
790
- # else:
791
- # self.effector_class_name=''
792
- # #self.effector_class_name = self.effector_class_choice_cb.currentText()
793
- # self.effector_expected_status = 'status'
794
- # suffix = self.effector_class_name.replace('class','').replace('_','')
795
- # if suffix!='':
796
- # self.effector_expected_status+='_'+suffix
797
- # self.effector_expected_time = 't_'+suffix
798
- # else:
799
- # self.effector_expected_time = 't0'
800
-
801
- # self.effector_time_name = self.effector_expected_time
802
- # self.effector_status_name = self.effector_expected_status
803
-
804
- # print('selection and expected names: ', self.effector_class_name, self.effector_expected_time, self.effector_expected_status)
805
-
806
- # if self.effector_time_name in self.df_effectors.columns and self.effector_class_name in self.df_effectors.columns and not self.effector_status_name in self.df_effectors.columns:
807
- # # only create the status column if it does not exist to not erase static classification results
808
- # self.make_effector_status_column()
809
- # elif self.effector_time_name in self.df_effectors.columns and self.effector_class_name in self.df_effectors.columns:
810
- # # all good, do nothing
811
- # pass
812
- # else:
813
- # if not self.effector_status_name in self.df_effectors.columns:
814
- # self.df_effectors[self.effector_status_name] = 0
815
- # self.df_effectors['status_color'] = color_from_status(0)
816
- # self.df_effectors['class_color'] = color_from_class(1)
817
-
818
- # if not self.effector_class_name in self.df_effectors.columns:
819
- # self.df_effectors[self.effector_class_name] = 1
820
- # if not self.effector_time_name in self.df_effectors.columns:
821
- # self.df_effectors[self.effector_time_name] = -1
822
-
823
- # self.df_effectors['status_color'] = [color_from_status(i) for i in self.df_effectors[self.effector_status_name].to_numpy()]
824
- # self.df_effectors['class_color'] = [color_from_class(i) for i in self.df_effectors[self.effector_class_name].to_numpy()]
825
-
826
- # self.extract_scatter_from_effector_trajectories()
756
+ df_neighbors['status_color'] = [color_from_status(i) for i in df_neighbors[self.neighbor_status_name].to_numpy()]
757
+ df_neighbors['class_color'] = [color_from_class(i) for i in df_neighbors[self.neighbor_class_name].to_numpy()]
758
+
759
+ if self.neighbor_population=='targets':
760
+ self.extract_scatter_from_target_trajectories()
761
+ else:
762
+ self.extract_scatter_from_effector_trajectories()
827
763
 
828
764
  def compute_status_and_colors_pair(self):
829
765
 
830
766
  self.pair_class_name = self.relative_class_choice_cb.currentText()
831
- print(f'{self.pair_class_name=}')
832
-
833
- self.pair_expected_status = 'status'
834
- suffix = self.pair_class_name.replace('class','').replace('_','',1)
835
- if suffix!='':
836
- self.pair_expected_status+='_'+suffix
837
- self.pair_expected_time = 't0_'+suffix
838
- if not self.pair_expected_time in list(self.df_relative.columns):
839
- self.pair_expected_time = 't_'+suffix
840
- else:
841
- self.pair_expected_time = 't0'
767
+
768
+ if self.pair_class_name!='':
769
+
770
+ self.pair_expected_status = 'status'
771
+ suffix = self.pair_class_name.replace('class','').replace('_','',1)
772
+ if suffix!='':
773
+ self.pair_expected_status+='_'+suffix
774
+ self.pair_expected_time = 't0_'+suffix
775
+ if not self.pair_expected_time in list(self.df_relative.columns):
776
+ self.pair_expected_time = 't_'+suffix
777
+ else:
778
+ self.pair_expected_time = 't0'
842
779
 
843
- self.pair_time_name = self.pair_expected_time
844
- self.pair_status_name = self.pair_expected_status
780
+ self.pair_time_name = self.pair_expected_time
781
+ self.pair_status_name = self.pair_expected_status
845
782
 
846
- if self.pair_time_name in self.df_relative.columns and self.pair_class_name in self.df_relative.columns and not self.pair_status_name in self.df_relative.columns:
847
- # only create the status column if it does not exist to not erase static classification results
848
- self.make_relative_status_column()
849
- elif self.pair_time_name in self.df_relative.columns and self.pair_class_name in self.df_relative.columns:
850
- # all good, do nothing
851
- pass
852
- else:
853
- if not self.pair_status_name in self.df_relative.columns:
854
- self.df_relative[self.pair_status_name] = 0
855
- self.df_relative['status_color'] = color_from_status(0)
856
- self.df_relative['class_color'] = color_from_class(1)
783
+ if self.pair_time_name in self.df_relative.columns and self.pair_class_name in self.df_relative.columns and not self.pair_status_name in self.df_relative.columns:
784
+ # only create the status column if it does not exist to not erase static classification results
785
+ self.make_relative_status_column()
786
+ elif self.pair_time_name in self.df_relative.columns and self.pair_class_name in self.df_relative.columns:
787
+ # all good, do nothing
788
+ pass
789
+ else:
790
+ if not self.pair_status_name in self.df_relative.columns:
791
+ self.df_relative[self.pair_status_name] = 0
792
+ self.df_relative['status_color'] = color_from_status(0)
793
+ self.df_relative['class_color'] = color_from_class(1)
857
794
 
858
- if not self.pair_class_name in self.df_relative.columns:
859
- self.df_relative[self.pair_time_name] = 1
860
- if not self.pair_time_name in self.df_relative.columns:
861
- self.df_relative[self.pair_time_name] = -1
795
+ if not self.pair_class_name in self.df_relative.columns:
796
+ self.df_relative[self.pair_time_name] = 1
797
+ if not self.pair_time_name in self.df_relative.columns:
798
+ self.df_relative[self.pair_time_name] = -1
862
799
 
863
- self.df_relative['status_color'] = [color_from_status(i) for i in self.df_relative[self.pair_status_name].to_numpy()]
864
- self.df_relative['class_color'] = [color_from_class(i) for i in self.df_relative[self.pair_class_name].to_numpy()]
800
+ self.df_relative['status_color'] = [color_from_status(i) for i in self.df_relative[self.pair_status_name].to_numpy()]
801
+ self.df_relative['class_color'] = [color_from_class(i) for i in self.df_relative[self.pair_class_name].to_numpy()]
865
802
 
866
- self.extract_scatter_from_lines()
867
- self.give_pair_information()
868
- self.plot_signals()
803
+ self.extract_scatter_from_lines()
804
+ self.give_pair_information()
805
+ self.plot_signals()
869
806
 
870
807
  def contrast_slider_action(self):
871
808
 
@@ -1329,30 +1266,6 @@ class SignalAnnotator2(QMainWindow,Styles):
1329
1266
  self.MinMaxScaler_effectors.fit(x)
1330
1267
 
1331
1268
 
1332
- # def make_effector_status_column(self):
1333
- # print('remaking the status column for the effectors')
1334
- # for tid, group in self.df_effectors.groupby('TRACK_ID'):
1335
-
1336
- # indices = group.index
1337
- # t0 = group[self.].to_numpy()[0]
1338
- # cclass = group[self.class_name].to_numpy()[0]
1339
- # timeline = group['FRAME'].to_numpy()
1340
- # status = np.zeros_like(timeline)
1341
- # if t0 > 0:
1342
- # status[timeline >= t0] = 1.
1343
- # if cclass == 2:
1344
- # status[:] = 2
1345
- # if cclass > 2:
1346
- # status[:] = 42
1347
- # status_color = [color_from_status(s) for s in status]
1348
- # class_color = [color_from_class(cclass) for i in range(len(status))]
1349
-
1350
- # self.df_tracks.loc[indices, self.status_name] = status
1351
- # self.df_tracks.loc[indices, 'status_color'] = status_color
1352
- # self.df_tracks.loc[indices, 'class_color'] = class_color
1353
-
1354
-
1355
-
1356
1269
  def locate_relative_tracks(self):
1357
1270
 
1358
1271
  population = 'relative'
@@ -1392,6 +1305,9 @@ class SignalAnnotator2(QMainWindow,Styles):
1392
1305
  self.relative_time_name = 't0'
1393
1306
  self.relative_status_name = 'status'
1394
1307
 
1308
+ for col in list(self.df_relative.columns):
1309
+ if np.all(self.df_relative[col].isnull()) or self.df_relative[col].dtype==object:
1310
+ self.cols_to_remove.append(col)
1395
1311
 
1396
1312
  self.MinMaxScaler_pairs = MinMaxScaler()
1397
1313
  self.pair_columns = list(self.df_relative.columns)
@@ -1408,7 +1324,7 @@ class SignalAnnotator2(QMainWindow,Styles):
1408
1324
  pass
1409
1325
 
1410
1326
  x = self.df_relative[self.pair_columns].values
1411
- self.MinMaxScaler_pairs.fit(x)
1327
+ self.MinMaxScaler_pairs.fit(x)
1412
1328
 
1413
1329
 
1414
1330
  def set_reference_and_neighbor_populations(self):
@@ -1813,6 +1729,7 @@ class SignalAnnotator2(QMainWindow,Styles):
1813
1729
  self.cancel_selection()
1814
1730
  self.set_reference_and_neighbor_populations()
1815
1731
  # Update reference classes and neighbor classes
1732
+ self.extract_relevant_events()
1816
1733
  self.fill_class_cbs()
1817
1734
 
1818
1735
  self.update_cell_events()
@@ -1820,6 +1737,39 @@ class SignalAnnotator2(QMainWindow,Styles):
1820
1737
  # self.draw_frame(self.framedata)
1821
1738
  self.plot_signals()
1822
1739
 
1740
+ def extract_relevant_events(self):
1741
+
1742
+ if self.reference_population!=self.neighbor_population:
1743
+ pattern = "_2_"
1744
+ else:
1745
+ pattern = "_self_"
1746
+
1747
+ try:
1748
+ self.relative_class_choice_cb.disconnect()
1749
+ except:
1750
+ pass
1751
+
1752
+ self.relative_class_choice_cb.clear()
1753
+
1754
+ cols = list(self.df_relative.columns)
1755
+ self.relative_class_cols = [c for c in cols if c.startswith('class')]
1756
+ for c in ['class_color', 'class_id']:
1757
+ if c in self.relative_class_cols:
1758
+ self.relative_class_cols.remove(c)
1759
+
1760
+ # Keep only valid classes for which there are values
1761
+ self.new_relative_class_cols = []
1762
+ for c in self.relative_class_cols:
1763
+ if ~self.df_relative.loc[(self.df_relative['reference_population']==self.reference_population)&(self.df_relative['neighbor_population']==self.neighbor_population),c].isnull().all():
1764
+ self.new_relative_class_cols.append(c)
1765
+
1766
+ if len(self.new_relative_class_cols)>0:
1767
+ self.relative_class = self.new_relative_class_cols[0]
1768
+ self.relative_class_choice_cb.currentIndexChanged.connect(self.compute_status_and_colors_pair)
1769
+ self.relative_class_choice_cb.addItems(self.new_relative_class_cols)
1770
+ idx = self.relative_class_choice_cb.findText(self.relative_class)
1771
+ self.relative_class_choice_cb.setCurrentIndex(idx)
1772
+
1823
1773
 
1824
1774
  def closeEvent(self, event):
1825
1775
 
@@ -4,7 +4,7 @@ from PyQt5.QtGui import QDoubleValidator
4
4
  from celldetective.gui.gui_utils import center_window
5
5
  from superqt import QColormapComboBox
6
6
  from celldetective.gui.generic_signal_plot import SurvivalPlotWidget
7
- from celldetective.utils import get_software_location, _extract_labels_from_config
7
+ from celldetective.utils import get_software_location, _extract_labels_from_config, extract_cols_from_table_list
8
8
  from celldetective.io import load_experiment_tables
9
9
  import numpy as np
10
10
  import os
@@ -15,6 +15,10 @@ import pandas as pd
15
15
  from celldetective.gui import Styles
16
16
  from matplotlib import colormaps
17
17
  from celldetective.events import compute_survival
18
+ from natsort import natsorted
19
+ from celldetective.relative_measurements import expand_pair_table
20
+ import matplotlib.cm
21
+ from celldetective.neighborhood import extract_neighborhood_in_pair_table
18
22
 
19
23
  class ConfigSurvival(QWidget, Styles):
20
24
 
@@ -88,8 +92,51 @@ class ConfigSurvival(QWidget, Styles):
88
92
  main_layout.addWidget(panel_title, alignment=Qt.AlignCenter)
89
93
 
90
94
 
91
- labels = [QLabel('population: '), QLabel('time of\nreference: '), QLabel('time of\ninterest: '), QLabel('cmap: ')] #QLabel('class: '),
92
- self.cb_options = [['targets','effectors'], ['0'], [], []] #['class'],
95
+ pops = []
96
+ for population in ['effectors','targets','pairs']:
97
+ tables = glob(self.exp_dir+os.sep.join(['W*','*','output','tables',f'trajectories_{population}.csv']))
98
+ if len(tables)>0:
99
+ pops.append(population)
100
+
101
+ tables_targets = glob(self.exp_dir+os.sep.join(['W*','*','output','tables',f'trajectories_targets.csv']))
102
+ self.cols_targets = extract_cols_from_table_list(tables_targets)
103
+ tables_effectors = glob(self.exp_dir+os.sep.join(['W*','*','output','tables',f'trajectories_effectors.csv']))
104
+ self.cols_effectors = extract_cols_from_table_list(tables_effectors)
105
+
106
+ # Smart reading of existing neighborhoods (without loading tables in memory)
107
+ if 'pairs' in pops and not 'targets' in pops:
108
+ # must be effector-effector
109
+ effector_neighs = [c[16:] for c in self.cols_effectors if c.startswith('inclusive_count_neighborhood')]
110
+ if len(effector_neighs)>0:
111
+ pops.pop(pops.index('pairs'))
112
+ pops.append('effectors-effectors')
113
+ elif 'pairs' in pops and not 'effectors' in pops:
114
+ # must be target-target
115
+ target_neighs = [c for c in self.cols_targets if c.startswith('inclusive_count_neighborhood')]
116
+ if len(target_neighs)>0:
117
+ pops.pop(pops.index('pairs'))
118
+ pops.append('targets-targets')
119
+ elif 'pairs' in pops:
120
+ # either effector-target or target-effector
121
+ target_neighs_cross = [c for c in self.cols_targets if c.startswith('inclusive_count_neighborhood') and '_2_' in c]
122
+ if len(target_neighs_cross)>0:
123
+ pops.append('targets-effectors')
124
+ effector_neighs_cross = [c for c in self.cols_effectors if c.startswith('inclusive_count_neighborhood') and '_2_' in c]
125
+ if len(effector_neighs_cross)>0:
126
+ pops.append('effectors-targets')
127
+ target_neighs = [c for c in self.cols_targets if c.startswith('inclusive_count_neighborhood') and 'self' in c]
128
+ if len(target_neighs)>0:
129
+ pops.append('targets-targets')
130
+ effector_neighs = [c for c in self.cols_effectors if c.startswith('inclusive_count_neighborhood') and 'self' in c]
131
+ if len(effector_neighs)>0:
132
+ pops.append('effectors-effectors')
133
+ pops.pop(pops.index('pairs'))
134
+ else:
135
+ pass
136
+
137
+
138
+ labels = [QLabel('population: '), QLabel('time of\nreference: '), QLabel('time of\ninterest: '), QLabel('cmap: ')] #QLabel('class: '),
139
+ self.cb_options = [pops, ['0'], [], []] #['class'],
93
140
  self.cbs = [QComboBox() for i in range(len(labels))]
94
141
 
95
142
  self.cbs[-1] = QColormapComboBox()
@@ -107,10 +154,12 @@ class ConfigSurvival(QWidget, Styles):
107
154
 
108
155
  all_cms = list(colormaps)
109
156
  for cm in all_cms:
110
- try:
111
- self.cbs[-1].addColormap(cm)
112
- except:
113
- pass
157
+ if hasattr(matplotlib.cm, str(cm).lower()):
158
+ self.cbs[-1].addColormap(cm.lower())
159
+ #try:
160
+ # self.cbs[-1].addColormap(cm)
161
+ # except:
162
+ # pass
114
163
 
115
164
  main_layout.addLayout(choice_layout)
116
165
 
@@ -155,24 +204,60 @@ class ConfigSurvival(QWidget, Styles):
155
204
  def set_classes_and_times(self):
156
205
 
157
206
  # Look for all classes and times
158
- tables = glob(self.exp_dir+os.sep.join(['W*','*','output','tables',f'trajectories_*.csv']))
159
- self.all_columns = []
160
- for tab in tables:
161
- cols = pd.read_csv(tab, nrows=1,encoding_errors='ignore').columns.tolist()
162
- self.all_columns.extend(cols)
207
+ self.neighborhood_keys = None
208
+ self.population = self.cbs[0].currentText()
209
+ pop_split = self.population.split('-')
163
210
 
164
- self.all_columns = np.unique(self.all_columns)
165
- #class_idx = np.array([s.startswith('class_') for s in self.all_columns])
166
- time_idx = np.array([s.startswith('t_') for s in self.all_columns])
211
+ if len(pop_split)==2:
167
212
 
168
- try:
169
- time_columns = list(self.all_columns[time_idx])
170
- except:
171
- print('no column starts with t')
172
- self.auto_close = True
173
- return None
174
- if 't0' in self.all_columns:
175
- time_columns.append('t0')
213
+ self.population = 'pairs'
214
+ tables_pairs = glob(self.exp_dir+os.sep.join(['W*','*','output','tables',f'trajectories_pairs.csv']))
215
+ self.cols_pairs = extract_cols_from_table_list(tables_pairs)
216
+
217
+ self.population_reference = pop_split[0]
218
+ self.population_neigh = pop_split[1]
219
+ if self.population_reference=='targets':
220
+ cols_ref = self.cols_targets
221
+ else:
222
+ cols_ref = self.cols_effectors
223
+ if self.population_neigh=='targets':
224
+ cols_neigh = self.cols_targets
225
+ else:
226
+ cols_neigh = self.cols_effectors
227
+
228
+ time_cols_ref = np.array([s.startswith('t_') or s=='t0' for s in cols_ref])
229
+ if len(time_cols_ref)>0:
230
+ time_cols_ref = list(cols_ref[time_cols_ref])
231
+ time_cols_ref = ['reference_'+t for t in time_cols_ref]
232
+
233
+ time_cols_neigh = np.array([s.startswith('t_') or s=='t0' for s in cols_neigh])
234
+ if len(time_cols_neigh)>0:
235
+ time_cols_neigh = list(cols_neigh[time_cols_neigh])
236
+ time_cols_neigh = ['neighbor_'+t for t in time_cols_neigh]
237
+
238
+ if self.population_reference!=self.population_neigh:
239
+ self.neighborhood_keys = [c[16:] for c in cols_ref if c.startswith('inclusive_count_neighborhood') and '_2_' in c]
240
+ else:
241
+ self.neighborhood_keys = [c[16:] for c in cols_ref if c.startswith('inclusive_count_neighborhood') and 'self' in c]
242
+
243
+ time_idx = np.array([s.startswith('t_') or s.startswith('t0') for s in self.cols_pairs])
244
+ time_cols_pairs = list(self.cols_pairs[time_idx])
245
+
246
+ time_columns = time_cols_ref + time_cols_neigh + time_cols_pairs
247
+
248
+ else:
249
+ if self.population=='targets':
250
+ self.all_columns = self.cols_targets
251
+ else:
252
+ self.all_columns = self.cols_effectors
253
+ time_idx = np.array([s.startswith('t_') or s=='t0' for s in self.all_columns])
254
+
255
+ try:
256
+ time_columns = list(self.all_columns[time_idx])
257
+ except:
258
+ print('no column starts with t')
259
+ self.auto_close = True
260
+ return None
176
261
 
177
262
  self.cbs[1].clear()
178
263
  self.cbs[1].addItems(np.unique(self.cb_options[1]+time_columns))
@@ -184,19 +269,16 @@ class ConfigSurvival(QWidget, Styles):
184
269
 
185
270
  def process_survival(self):
186
271
 
187
- print('you clicked!!')
188
272
  self.FrameToMin = float(self.time_calibration_le.text().replace(',','.'))
189
- print(self.FrameToMin, 'set')
190
-
191
273
  self.time_of_interest = self.cbs[2].currentText()
192
274
  if self.time_of_interest=="t0":
193
275
  self.class_of_interest = "class"
194
276
  else:
195
277
  self.class_of_interest = self.time_of_interest.replace('t_','class_')
196
278
 
279
+
197
280
  # read instructions from combobox options
198
281
  self.load_available_tables_local()
199
-
200
282
  if self.df is not None:
201
283
 
202
284
  try:
@@ -241,7 +323,8 @@ class ConfigSurvival(QWidget, Styles):
241
323
  self.well_option = self.parent_window.parent_window.well_list.getSelectedIndices()
242
324
  self.position_option = self.parent_window.parent_window.position_list.getSelectedIndices()
243
325
 
244
- self.df, self.df_pos_info = load_experiment_tables(self.exp_dir, well_option=self.well_option, position_option=self.position_option, population=self.cbs[0].currentText(), return_pos_info=True)
326
+ self.df, self.df_pos_info = load_experiment_tables(self.exp_dir, well_option=self.well_option, position_option=self.position_option, population=self.population, return_pos_info=True)
327
+
245
328
  if self.df is None:
246
329
  msgBox = QMessageBox()
247
330
  msgBox.setIcon(QMessageBox.Warning)
@@ -255,6 +338,11 @@ class ConfigSurvival(QWidget, Styles):
255
338
  self.df_well_info = self.df_pos_info.loc[:,['well_path', 'well_index', 'well_name', 'well_number', 'well_alias']].drop_duplicates()
256
339
  #print(f"{self.df_well_info=}")
257
340
 
341
+ if self.population=='pairs':
342
+ self.df = expand_pair_table(self.df)
343
+ self.df = extract_neighborhood_in_pair_table(self.df, reference_population=self.population_reference, neighbor_population=self.population_neigh, neighborhood_key=self.neighborhood_keys[0], contact_only=True)
344
+
345
+
258
346
  def compute_survival_functions(self):
259
347
 
260
348
  cut_observation_time = None
@@ -265,19 +353,20 @@ class ConfigSurvival(QWidget, Styles):
265
353
  cut_observation_time = None
266
354
  except Exception as e:
267
355
  pass
268
- print(f"{cut_observation_time=}")
356
+
357
+ pairs = False
358
+ if self.neighborhood_keys is not None:
359
+ pairs = True
269
360
 
270
361
  # Per position survival
271
362
  for block,movie_group in self.df.groupby(['well','position']):
272
-
273
- ks = compute_survival(movie_group, self.class_of_interest, self.cbs[2].currentText(), t_reference=self.cbs[1].currentText(), FrameToMin=self.FrameToMin, cut_observation_time=cut_observation_time)
363
+ ks = compute_survival(movie_group, self.class_of_interest, self.cbs[2].currentText(), t_reference=self.cbs[1].currentText(), FrameToMin=self.FrameToMin, cut_observation_time=cut_observation_time, pairs=pairs)
274
364
  if ks is not None:
275
365
  self.df_pos_info.loc[self.df_pos_info['pos_path']==block[1],'survival_fit'] = ks
276
366
 
277
367
  # Per well survival
278
368
  for well,well_group in self.df.groupby('well'):
279
-
280
- ks = compute_survival(well_group, self.class_of_interest, self.cbs[2].currentText(), t_reference=self.cbs[1].currentText(), FrameToMin=self.FrameToMin, cut_observation_time=cut_observation_time)
369
+ ks = compute_survival(well_group, self.class_of_interest, self.cbs[2].currentText(), t_reference=self.cbs[1].currentText(), FrameToMin=self.FrameToMin, cut_observation_time=cut_observation_time, pairs=pairs)
281
370
  if ks is not None:
282
371
  self.df_well_info.loc[self.df_well_info['well_path']==well,'survival_fit'] = ks
283
372