Changeset 1558:54642c2b4144 in orange-bioinformatics


Ignore:
Timestamp:
02/13/12 13:11:16 (2 years ago)
Author:
ales_erjavec
Branch:
default
Message:

More work on Quality Control widget (now in semi-useful state).

File:
1 edited

Legend:

Unmodified
Added
Removed
  • widgets/prototypes/OWQualityControl.py

    r1556 r1558  
    11""" 
    2 <name>Quality control</name> 
     2<name>Quality Control</name> 
    33 
    44""" 
     
    1010from OWWidget import * 
    1111from OWItemModels import PyListModel 
     12from OWGraphics import GraphicsSimpleTextLayoutItem, GtI 
    1213import OWGUI 
     14 
     15from OWGenotypeDistances import SetContextHandler 
    1316 
    1417import obiExperiments as exp 
    1518import numpy 
    1619 
     20from collections import defaultdict 
     21from contextlib import contextmanager 
    1722from pprint import pprint 
    1823 
    19 def compute_within_group_distances(data, groups, distance=exp.dist_pcorr): 
    20     matrices = [] 
    21     for _, indices in groups: 
    22         matrix = Orange.misc.SymMatrix(len(group_i)) 
    23         for i, index_i in enumerate(indices): 
    24             for j, index_j in enumerate(indics[i + 1:], i + 1): 
    25                 vec_i = exp.linearize(data, [index_i]) 
    26                 vec_j = exp.linearize(data, [index_j]) 
    27                 matrix[i, j] = distance(vec_i, vec_j) 
    28         metrices.append(matrix) 
    29     return matrices 
    30  
    31 def group_label(splits, group): 
     24DEBUG = False 
     25 
     26@contextmanager 
     27def control_disable(widget): 
     28    widget.setEnabled(False) 
     29    yield 
     30    widget.setEnabled(True) 
     31     
     32@contextmanager 
     33def disable_updates(widget): 
     34    widget._disable_updates = True 
     35    yield 
     36    widget._disable_updates = False 
     37 
     38def group_label(splits, groups): 
    3239    """ 
    3340    Return group label 
     
    3845 
    3946 
     47def sort_label(sort, attr): 
     48    """ 
     49    Return within group sorted items label for attribute. 
     50    """ 
     51    items = [(key, attr.attributes.get(key, "?")) \ 
     52             for key in sort] 
     53    labels = ["{}={}".format(*item) for item in items] 
     54    return " | ".join(labels) 
     55 
     56def float_if_posible(val): 
     57    try: 
     58        return float(val) 
     59    except ValueError: 
     60        return val 
    4061 
    4162class OWQualityControl(OWWidget): 
     63    contextHandlers = {"": SetContextHandler("")} 
    4264    settingsList = [] 
     65     
    4366    DISTANCE_FUNCTIONS = [("Distance from Pearson correlation", exp.dist_pcorr), 
    4467                          ("Euclidean distance", exp.dist_eucl), 
     
    5275        self.inputs = [("Experiment Data", Orange.data.Table, self.set_data)] 
    5376 
    54         ## Setings 
     77        ## Settings 
    5578        self.selected_distance_index = 0 
    5679        self.replicate_id = "replicate" 
     
    6083        self.distances = None 
    6184        self.groups = None 
    62         self.group_indices = None 
    63         self.separate_by = None 
    64         self.sort_by = None 
     85        self.unique_pos = None 
     86        self.base_index = 0 
    6587 
    6688        ## GUI 
     
    7294        self.split_by_model = PyListModel() 
    7395        self.split_by_view = QListView() 
     96        self.split_by_view.setSelectionMode(QListView.ExtendedSelection) 
    7497        self.split_by_view.setModel(self.split_by_model) 
    7598        box.layout().addWidget(self.split_by_view) 
     99 
     100        self.connect(self.split_by_view.selectionModel(), 
     101                     SIGNAL("selectionChanged(QItemSelection, QItemSelection)"), 
     102                     self.on_split_key_changed) 
    76103 
    77104        ## Sort By box 
     
    79106        self.sort_by_model = PyListModel() 
    80107        self.sort_by_view = QListView() 
     108        self.sort_by_view.setSelectionMode(QListView.ExtendedSelection) 
    81109        self.sort_by_view.setModel(self.sort_by_model) 
    82110        box.layout().addWidget(self.sort_by_view) 
     111         
     112        self.connect(self.sort_by_view.selectionModel(), 
     113                     SIGNAL("selectionChanged(QItemSelection, QItemSelection)"), 
     114                     self.on_sort_key_changed) 
    83115 
    84116        ## Distance box 
    85117        box = OWGUI.widgetBox(self.controlArea, "Distance Measure") 
    86118        OWGUI.comboBox(box, self, "selected_distance_index", 
    87                        items=[t[0] for t in self.DISTANCE_FUNCTIONS]) 
    88  
     119                       items=[t[0] for t in self.DISTANCE_FUNCTIONS], 
     120                       callback=self.on_distance_measure_changed) 
     121 
     122        self.connect(self.graphButton, 
     123                     SIGNAL("clicked()"), 
     124                     self.save_graph) 
     125         
    89126        self.scene = QGraphicsScene() 
    90         self.scene_view = QGraphicsView(self.scene) 
     127        self.scene_view = QualityGraphicsView(self.scene) 
     128        self.scene_view.setRenderHints(QPainter.Antialiasing) 
    91129        self.mainArea.layout().addWidget(self.scene_view) 
    92  
     130         
     131        self.connect(self.scene_view, 
     132                     SIGNAL("view_size_changed(QSize)"), 
     133                     self.on_view_resize) 
     134 
     135        self._disable_updates = False 
     136        self.main_widget = None 
     137         
    93138        self.resize(800, 600) 
    94139 
     
    99144        self.distances = None 
    100145        self.groups = None 
    101         self.group_indices = None 
    102         self.separate_by = None 
    103         self.sort_by = None 
     146        self.unique_pos = None 
     147         
     148        with disable_updates(self): 
     149            self.split_by_model[:] = [] 
     150            self.sort_by_model[:] = [] 
    104151 
    105152        self.scene.clear() 
     153        self.info_box.setText("\n") 
    106154 
    107155    def set_data(self, data=None): 
     
    116164            self.on_new_data() 
    117165        else: 
     166            self.closeContext("") 
    118167            self.clear() 
    119168 
     
    123172         
    124173        """ 
    125         pass 
     174        keys = self.get_suitable_keys(self.data) 
     175        with disable_updates(self): 
     176            self.split_by_model[:] = keys 
     177            self.sort_by_model[:] = keys 
     178         
     179    def get_suitable_keys(self, data): 
     180        """ Return suitable attr label keys from the data where the key has at least 
     181        two unique values in the data. 
     182         
     183        """ 
     184        attrs = [attr.attributes.items() for attr in data.domain.attributes] 
     185        attrs  = reduce(list.__add__, attrs, []) 
     186        # in case someone put non string values in attributes dict 
     187        attrs = [(str(key), str(value)) for key, value in attrs] 
     188        attrs = set(attrs) 
     189        values = defaultdict(set) 
     190        for key, value in attrs: 
     191            values[key].add(value) 
     192        keys = [key for key in values if len(values[key]) > 1] 
     193        return keys 
    126194 
    127195    def selected_split_by_labels(self): 
    128196        """Return the current selected split labels. 
    129197        """ 
    130         return ["tp"] 
     198        sel_m = self.split_by_view.selectionModel() 
     199        indices = [r.row() for r in sel_m.selectedRows()] 
     200        return [self.sort_by_model[i] for i in indices] 
    131201 
    132202    def selected_sort_by_labels(self): 
    133203        """Return the current selected sort labels 
    134204        """ 
    135         return ["replicate"] 
     205        sel_m = self.sort_by_view.selectionModel() 
     206        indices = [r.row() for r in sel_m.selectedRows()] 
     207        return [self.sort_by_model[i] for i in indices] 
    136208 
    137209    def selected_distance(self): 
     
    139211        """ 
    140212        return self.DISTANCE_FUNCTIONS[self.selected_distance_index][1] 
     213     
     214    def selected_base_index(self): 
     215        return self.base_index 
    141216 
    142217    def on_new_data(self): 
    143218        """We have new data and need to recompute all. 
    144219        """ 
     220        self.closeContext("") 
     221         
    145222        self.update_label_candidates() 
    146  
    147         self.separate_by = self.selected_split_by_labels() 
    148         self.sort_by = self.selected_sort_by_labels() 
    149  
     223        self.info_box.setText("{} genes \n{} experiments".format( 
     224                                len(self.data),  
     225                                len(self.data.domain.attributes) 
     226                                ) 
     227                              ) 
     228         
     229        keys = self.get_suitable_keys(self.data) 
     230        self.openContext("", keys) 
     231         
     232        ## Restore saved context 
     233        context = self.currentContexts[""] 
     234        split_by_labels= getattr(context, "split_by_labels", set()) 
     235        sort_by_labels = getattr(context, "sort_by_labels", set()) 
     236         
     237        def select(model, selection_model, selected_items): 
     238            all_items = list(model) 
     239            try: 
     240                indices = [all_items.index(item) for item in selected_items] 
     241            except: 
     242                indices = [] 
     243            for ind in indices: 
     244                selection_model.select(model.index(ind), QItemSelectionModel.Select) 
     245                 
     246#        self._disable_updates = True 
     247#        try: 
     248        with disable_updates(self): 
     249            select(self.split_by_view.model(), 
     250                   self.split_by_view.selectionModel(), 
     251                   split_by_labels) 
     252             
     253            select(self.sort_by_view.model(), 
     254                   self.sort_by_view.selectionModel(), 
     255                   sort_by_labels) 
     256#        finally: 
     257#            self._disable_updates = False 
     258             
    150259        self.split_and_update() 
    151  
     260         
     261    def on_split_key_changed(self, *args): 
     262        with control_disable(self): 
     263            if not self._disable_updates: 
     264                context = self.currentContexts[""] 
     265                context.split_by_labels = self.selected_split_by_labels() 
     266                self.split_and_update() 
     267     
     268    def on_sort_key_changed(self, *args): 
     269        with control_disable(self): 
     270            if not self._disable_updates: 
     271                context = self.currentContexts[""] 
     272                context.sort_by_labels = self.selected_sort_by_labels() 
     273                self.split_and_update() 
     274         
     275    def on_distance_measure_changed(self): 
     276        self.update_distances() 
     277        self.replot_experiments() 
     278         
     279    def on_view_resize(self, size): 
     280        if self.main_widget: 
     281            current = self.main_widget.size() 
     282            self.main_widget.resize(size.width() - 2,  
     283                                    current.height()) 
     284             
     285            self.scene.setSceneRect(self.scene.itemsBoundingRect()) 
     286         
     287    def on_rug_item_clicked(self, item): 
     288        base = item.index 
     289        if base != self.base_index: 
     290            self.base_index = base 
     291            self.split_and_update() 
     292         
    152293    def split_and_update(self): 
    153294        """ 
     
    156297         
    157298        """ 
    158         self.groups, self.group_indices = \ 
    159                 exp.separate_by(self.data, self.separate_by, 
    160                                 consider=self.sort_by, 
    161                                 add_empty=False) 
    162  
    163         self.groups = sorted(self.groups.items()) 
    164         self.group_indices = sorted(self.group_indices.items()) 
     299        self.groups, self.unique_pos = \ 
     300                exp.separate_by(self.data, 
     301                                self.selected_split_by_labels(), 
     302                                consider=self.selected_sort_by_labels(), 
     303                                add_empty=True) 
     304         
     305         
     306        self.groups = sorted(self.groups.items(), 
     307                             key=lambda t: map(float_if_posible, t[0])) 
     308        self.unique_pos = sorted(self.unique_pos.items(), 
     309                                 key=lambda t: map(float_if_posible, t[0])) 
    165310         
    166311        pprint(self.groups) 
    167         pprint(self.group_indices) 
    168  
    169         self.update_distances() 
    170         self.replot_experiments() 
     312        pprint(self.unique_pos) 
     313        if self.groups: 
     314            # TODO: Check if the groups of base experiment have changed 
     315            self.update_distances() 
     316            self.replot_experiments() 
    171317 
    172318    def update_distances(self): 
     
    174320        """ 
    175321        distance = self.selected_distance() 
    176         base_index = 0 
     322        base_index = self.base_index 
    177323        base_distances = [] 
     324        pb = OWGUI.ProgressBar(self, len(self.groups) * \ 
     325                               len(self.data.domain.attributes)) 
    178326        for group, indices in self.groups: 
    179327            # Base column of the group 
    180             base_vec = exp.linearize(self.data, [indices[base_index]]) 
    181             distances = [] 
    182             # Compute the distances between base replicate  
    183             # and all the rest data columns. 
    184             for i in range(len(self.data.domain.attributes)): 
    185                 if i == indices[base_index]: 
    186                     distances.append(0.0) 
    187                 else: 
    188                     vec_i = exp.linearize(self.data, [i]) 
    189                     distances.append(distance(base_vec, vec_i)) 
    190             base_distances.append(distances) 
    191              
     328            if indices[base_index] is not None: 
     329                base_vec = exp.linearize(self.data, [indices[base_index]]) 
     330                distances = [] 
     331                # Compute the distances between base replicate  
     332                # and all the rest data columns. 
     333                for i in range(len(self.data.domain.attributes)): 
     334                    if i == indices[base_index]: 
     335                        distances.append(0.0) 
     336                    else: 
     337                        vec_i = exp.linearize(self.data, [i]) 
     338                        distances.append(distance(base_vec, vec_i)) 
     339                    pb.advance() 
     340                     
     341                base_distances.append(distances) 
     342            else: 
     343                base_distances.append(None) 
     344        pb.finish() 
    192345        self.distances = base_distances 
    193 #        distance = self.selected_distance() 
    194 #        self.distances = \ 
    195 #            compute_within_group_distances(self.data, self.groups, distance) 
    196 #        self.backgroud_distances = \ 
    197 #            compute_within_group_distances( \ 
    198 #                            self.data,  
    199 #                            [(None,   
    200 #                             range(len(self.data.attributes)))], 
    201 #                            distance 
    202 #                            ) 
    203 #        print self.distances 
    204 #        print self.backgroud_distances 
    205346 
    206347    def replot_experiments(self): 
     
    210351        labels = [] 
    211352        ## Base replicate=1 TODO: the index should be set 
    212         base_index = 0 
    213         max_dist = numpy.max(self.distances) 
     353        base_index = self.base_index 
     354        max_dist = numpy.max(filter(None, self.distances)) 
     355        print max_dist 
    214356        rug_widgets = [] 
     357         
     358        group_pen = QPen(QColor(0, 0, 0)) 
     359        group_pen.setWidth(2) 
     360        group_pen.setCapStyle(Qt.RoundCap) 
     361        background_pen = QPen(QColor(0, 0, 250, 150)) 
     362        background_pen.setWidth(1) 
     363        background_pen.setCapStyle(Qt.RoundCap) 
     364         
     365        main_widget = QualityControlWidget() 
     366        layout = QGraphicsGridLayout() 
     367        split_by = self.selected_split_by_labels() 
     368        sort_by = self.selected_sort_by_labels() 
     369        attributes = self.data.domain.attributes 
    215370        if self.data: 
    216371            for (group, indices), dist_vec in zip(self.groups, self.distances): 
     
    218373                indices_set = set(indices) 
    219374                rug_items = [] 
    220                 for i in range(len(self.data.domain.attributes)): 
    221                     # Is this a within group distance or background 
    222                     in_group = i in indices_set  
    223                     rug_item = RugItem(dist_vec[i] / max_dist) 
    224                     rug_items.append(rug_item) 
     375                if dist_vec is not None: 
     376                    for i, attr in enumerate(attributes): 
     377                        # Is this a within group distance or background 
     378                        in_group = i in indices_set  
     379                        if in_group: 
     380                            rug_item = ClickableRugItem(dist_vec[i] / max_dist, 
     381                                           1.0, self.on_rug_item_clicked) 
     382                            rug_item.setPen(group_pen) 
     383                            tooltip = sort_label(split_by, attr) 
     384                            tooltip += "\n" + sort_label(sort_by, attr) 
     385                            rug_item.setToolTip(tooltip) 
     386                            rug_item.setToolTip(sort_label(sort_by, attr)) 
     387                            rug_item.index = indices.index(i) 
     388                        else: 
     389#                            rug_item = RugItem(dist_vec[i] / max_dist, 0.85) 
     390                            rug_item = ClickableRugItem(dist_vec[i] / max_dist, 
     391                                           0.85, None)#self.on_rug_item_clicked) 
     392                            tooltip = sort_label(split_by, attr) 
     393                            tooltip += "\n" + sort_label(sort_by, attr) 
     394                            rug_item.setToolTip(tooltip) 
     395                            rug_item.setPen(background_pen) 
     396                        rug_items.append(rug_item) 
    225397                     
    226398                rug_widget = RugGraphicsWidget() 
    227399                rug_widget.set_rug(rug_items) 
     400                 
    228401                rug_widgets.append(rug_widget) 
    229402                 
    230         layout = QGraphicsLinearLayout(Qt.Vertical) 
    231         for w in rug_widgets: 
    232             layout.addItem(w) 
     403                label = group_label(self.selected_split_by_labels(), group) 
     404#                label_item = QGraphicsSimpleTextItem(label) 
     405                label_item = GtI(label, main_widget) 
     406                label_item = GraphicsSimpleTextLayoutItem(label_item) 
     407                label_item.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) 
     408                labels.append(label_item) 
     409         
     410        for i, (label, w) in enumerate(zip(labels, rug_widgets)): 
     411            layout.addItem(label, i, 0, Qt.AlignVCenter) 
     412            layout.addItem(w, i, 1) 
     413            layout.setRowMaximumHeight(i, 30) 
    233414             
    234         main_widget = QGraphicsWidget() 
    235415        main_widget.setLayout(layout) 
    236         main_widget.resize(600, 600) 
    237416        self.scene.addItem(main_widget) 
    238          
    239                      
     417        main_widget.show() 
     418        self.main_widget = main_widget 
     419        self.rug_widgets = rug_widgets 
     420        self.labels = labels 
     421        self.on_view_resize(self.scene_view.size()) 
     422         
     423    def save_graph(self): 
     424        from OWDlgs import OWChooseImageSizeDlg 
     425        dlg = OWChooseImageSizeDlg(self.scene, parent=self) 
     426        dlg.exec_() 
    240427 
    241428 
     
    245432        self.rug_items = [] 
    246433        self.set_rug(rug) 
    247  
     434        self.setMaximumHeight(30) 
     435        self.setMinimumHeight(30) 
     436         
    248437    def clear(self): 
    249438        """ 
     
    298487         
    299488        for item in self.rug_items: 
     489            offset = (1.0 - item.height) * height / 2.0 
    300490            item.setPos(width * item.value, 0) 
    301             item.setLine(0., 0., 0., height) 
    302              
     491            item.setLine(0., offset, 0., height - offset) 
    303492 
    304493    def resizeEvent(self, event): 
     
    311500 
    312501class RugItem(QGraphicsLineItem): 
    313     def __init__(self, value): 
     502    def __init__(self, value, height): 
    314503        QGraphicsLineItem.__init__(self) 
    315504        self.value = value 
     505        self.height = height 
    316506 
    317507    def set_height(self, height): 
    318         """Set the height of this item. 
     508        """Set the height of this item (in ratio of the rug height) 
    319509        """ 
    320510        self.height = height 
    321  
    322  
    323  
     511         
     512class ClickableRugItem(RugItem): 
     513    def __init__(self, value, height, on_pressed): 
     514        RugItem.__init__(self, value, height) 
     515        self.on_pressed = on_pressed 
     516        self.setAcceptedMouseButtons(Qt.LeftButton) 
     517        self.setAcceptHoverEvents(True) 
     518         
     519    def mousePressEvent(self, event): 
     520        if event.button() == Qt.LeftButton and self.on_pressed: 
     521            self.on_pressed(self) 
     522             
     523    def hoverEnterEvent(self, event): 
     524        pen = QPen(self.pen()) 
     525        pen.setWidthF(3) 
     526        self.setPen(pen) 
     527        return RugItem.hoverEnterEvent(self, event) 
     528     
     529    def hoverLeaveEvent(self, event): 
     530        pen = QPen(self.pen()) 
     531        pen.setWidth(2) 
     532        self.setPen(pen) 
     533        return RugItem.hoverLeaveEvent(self, event) 
     534 
     535 
     536class QualityGraphicsView(QGraphicsView): 
     537    def resizeEvent(self, event): 
     538        QGraphicsView.resizeEvent(self, event) 
     539        self.emit(SIGNAL("view_size_changed(QSize)"), 
     540                  event.size()) 
     541 
     542 
     543class QualityControlWidget(QGraphicsWidget): 
     544    if DEBUG: 
     545        def paint(self, painter, options, widget=0): 
     546            rect =  self.geometry() 
     547            rect.translate(-self.pos()) 
     548            painter.drawRect(rect) 
     549             
    324550if __name__ == "__main__": 
    325551    app = QApplication(sys.argv) 
    326552    w = OWQualityControl() 
    327     data = Orange.data.Table("doc:dicty-abc-sample.tab") 
     553#    data = Orange.data.Table("doc:dicty-abc-sample.tab") 
     554    data = Orange.data.Table("doc:pipa.tab") 
    328555 
    329556    w.set_data(data) 
     
    331558    w.handleNewSignals() 
    332559    app.exec_() 
    333  
     560    w.set_data(None) 
     561    w.handleNewSignals() 
     562    w.saveSettings() 
Note: See TracChangeset for help on using the changeset viewer.