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.
- celldetective/_version.py +1 -1
- celldetective/events.py +10 -5
- celldetective/gui/classifier_widget.py +29 -4
- celldetective/gui/control_panel.py +3 -2
- celldetective/gui/generic_signal_plot.py +2 -6
- celldetective/gui/gui_utils.py +34 -6
- celldetective/gui/measurement_options.py +1 -30
- celldetective/gui/neighborhood_options.py +1 -1
- celldetective/gui/plot_signals_ui.py +3 -4
- celldetective/gui/process_block.py +8 -6
- celldetective/gui/signal_annotator.py +4 -2
- celldetective/gui/signal_annotator2.py +141 -191
- celldetective/gui/survival_ui.py +122 -33
- celldetective/gui/tableUI.py +26 -12
- celldetective/io.py +1059 -156
- celldetective/measure.py +151 -53
- celldetective/preprocessing.py +2 -2
- celldetective/relative_measurements.py +6 -9
- celldetective/scripts/measure_cells.py +13 -3
- celldetective/scripts/segment_cells.py +0 -1
- celldetective/signals.py +9 -7
- celldetective/tracking.py +52 -28
- celldetective/utils.py +23 -5
- {celldetective-1.3.4.post1.dist-info → celldetective-1.3.5.dist-info}/METADATA +2 -2
- {celldetective-1.3.4.post1.dist-info → celldetective-1.3.5.dist-info}/RECORD +29 -29
- {celldetective-1.3.4.post1.dist-info → celldetective-1.3.5.dist-info}/LICENSE +0 -0
- {celldetective-1.3.4.post1.dist-info → celldetective-1.3.5.dist-info}/WHEEL +0 -0
- {celldetective-1.3.4.post1.dist-info → celldetective-1.3.5.dist-info}/entry_points.txt +0 -0
- {celldetective-1.3.4.post1.dist-info → celldetective-1.3.5.dist-info}/top_level.txt +0 -0
|
@@ -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
|
-
|
|
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.
|
|
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
|
-
|
|
717
|
-
self.reference_status_name = self.expected_reference_status
|
|
682
|
+
if self.reference_class_name!='':
|
|
718
683
|
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
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
|
-
|
|
732
|
-
|
|
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
|
-
|
|
737
|
-
|
|
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
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
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
|
-
|
|
758
|
-
self.neighbor_status_name = self.expected_neighbor_status
|
|
726
|
+
if self.neighbor_class_name!='':
|
|
759
727
|
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
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
|
-
|
|
773
|
-
|
|
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
|
-
|
|
778
|
-
|
|
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
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
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
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
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
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
self.pair_expected_time
|
|
840
|
-
|
|
841
|
-
|
|
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
|
-
|
|
844
|
-
|
|
780
|
+
self.pair_time_name = self.pair_expected_time
|
|
781
|
+
self.pair_status_name = self.pair_expected_status
|
|
845
782
|
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
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
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
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
|
-
|
|
864
|
-
|
|
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
|
-
|
|
867
|
-
|
|
868
|
-
|
|
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
|
|
celldetective/gui/survival_ui.py
CHANGED
|
@@ -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
|
-
|
|
92
|
-
|
|
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
|
-
|
|
111
|
-
self.cbs[-1].addColormap(cm)
|
|
112
|
-
|
|
113
|
-
|
|
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
|
-
|
|
159
|
-
self.
|
|
160
|
-
|
|
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
|
-
|
|
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
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
self.
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
|