Пример #1
0
class OWMAPlot(OWWidget):
    settingsList = ["appendZScore", "appendRIValues"]
    contextHandlers = {
        "": DomainContextHandler("", ["selectedGroup", "zCutoff"])
    }

    CENTER_METHODS = [("Average", obiExpression.MA_center_average),
                      ("Lowess (fast - interpolated)",
                       obiExpression.MA_center_lowess_fast),
                      ("Lowess", obiExpression.MA_center_lowess)]

    MERGE_METHODS = [("Average", numpy.ma.average),
                     ("Median", numpy.ma.median),
                     ("Geometric mean", obiExpression.geometric_mean)]

    def __init__(self,
                 parent=None,
                 signalManager=None,
                 name="Normalize Expression Array"):
        OWWidget.__init__(self, parent, signalManager, name, wantGraph=True)

        self.inputs = [("Expression array", ExampleTable, self.setData)]
        self.outputs = [("Normalized expression array", ExampleTable, Default),
                        ("Filtered expression array", ExampleTable)]

        self.selectedGroup = 0
        self.selectedCenterMethod = 0
        self.selectedMergeMethod = 0
        self.zCutoff = 1.96
        self.appendZScore = False
        self.appendRIValues = False
        self.autoCommit = False

        self.loadSettings()
        ## GUI
        self.infoBox = OWGUI.widgetLabel(
            OWGUI.widgetBox(self.controlArea, "Info", addSpace=True),
            "No data on input.")

        box = OWGUI.widgetBox(self.controlArea, "Split by", addSpace=True)
        self.groupCombo = OWGUI.comboBox(box,
                                         self,
                                         "selectedGroup",
                                         callback=self.onGroupSelection)

        self.centerCombo = OWGUI.comboBox(
            self.controlArea,
            self,
            "selectedCenterMethod",
            box="Center Fold-change Using",
            items=[name for name, _ in self.CENTER_METHODS],
            callback=self.onCenterMethodChange,
            addSpace=True)

        self.mergeCombo = OWGUI.comboBox(
            self.controlArea,
            self,
            "selectedMergeMethod",
            box="Merge Replicates",
            items=[name for name, _ in self.MERGE_METHODS],
            tooltip="Select the method for replicate merging",
            callback=self.onMergeMethodChange,
            addSpace=True)

        box = OWGUI.doubleSpin(self.controlArea,
                               self,
                               "zCutoff",
                               0.0,
                               3.0,
                               0.01,
                               box="Z-Score Cutoff",
                               callback=[self.replotMA, self.commitIf])

        OWGUI.separator(self.controlArea)

        box = OWGUI.widgetBox(self.controlArea, "Ouput")
        OWGUI.checkBox(box,
                       self,
                       "appendZScore",
                       "Append Z-Scores",
                       tooltip="Append calculated Z-Scores to output",
                       callback=self.commitIf)

        OWGUI.checkBox(
            box,
            self,
            "appendRIValues",
            "Append Log Ratio and Intensity values",
            tooltip=
            "Append calculated Log Ratio and Intensity values to output data",
            callback=self.commitIf)

        cb = OWGUI.checkBox(box,
                            self,
                            "autoCommit",
                            "Commit on change",
                            tooltip="Commit data on any change",
                            callback=self.commitIf)

        b = OWGUI.button(box, self, "Commit", callback=self.commit)
        OWGUI.setStopper(self, b, cb, "changedFlag", callback=self.commit)

        self.connect(self.graphButton, SIGNAL("clicked()"), self.saveGraph)

        OWGUI.rubber(self.controlArea)
        self.graph = OWGraph(self.mainArea)
        self.graph.setAxisTitle(QwtPlot.xBottom,
                                "Intensity: log<sub>10</sub>(R*G)")
        self.graph.setAxisTitle(QwtPlot.yLeft,
                                "Log ratio: log<sub>2</sub>(R/G)")
        self.graph.showFilledSymbols = True
        self.mainArea.layout().addWidget(self.graph)
        self.groups = []
        self.split_data = None, None
        self.merged_splits = None, None
        self.centered = None, None
        self.changedFlag = False
        self.data = None

        self.resize(800, 600)

    def onFinished(self, status):
        self.setEnabled(True)

    def onUnhandledException(self, ex_info):
        self.setEnabled(True)
        print >> sys.stderr, "Unhandled exception in non GUI thread"

        ex_type, ex_val, tb = ex_info
        if ex_type == numpy.linalg.LinAlgError and False:
            self.error(0, "Linear algebra error: %s" % repr(ex_val))
        else:
            sys.excepthook(*ex_info)

    def onGroupSelection(self):
        if self.data:
            self.updateInfoBox()
            self.splitData()
            self.runNormalization()

    def onCenterMethodChange(self):
        if self.data:
            self.runNormalization()

    def onMergeMethodChange(self):
        if self.data:
            self.splitData()
            self.runNormalization()

    def proposeGroups(self, data):
        col_labels = [
            attr.attributes.items() for attr in data.domain.attributes
        ]
        col_labels = sorted(reduce(set.union, col_labels, set()))
        col_labels = [(key, value, 1) for key, value in col_labels]

        attrs = [attr for attr in data.domain.variables + data.domain.getmetas().values() \
                 if attr.varType == orange.VarTypes.Discrete]

        row_labels = [(attr.name, value, 0) for attr in attrs
                      for value in attr.values]

        def filterSingleValues(labels):
            ret = []
            for name, value, axis in labels:
                match = [(n, v, a) for n, v, a in labels if n == name]
                if len(match) > 1:
                    ret.append((name, value, axis))
            return ret

        col_labels = filterSingleValues(col_labels)
        row_labels = filterSingleValues(row_labels)

        return col_labels + row_labels

    def setData(self, data):
        self.closeContext("")
        self.data = data
        self.error([0, 1])
        if data is not None:
            self.infoBox.setText("%i genes on input" % len(data))
            self.groups = self.proposeGroups(data)
            self.groupCombo.clear()
            self.groupCombo.addItems(
                ["%s: %s" % (key, value) for key, value, axis in self.groups])

            if not self.groups:
                self.error(
                    1,
                    "Input data has no class attribute or attribute labels!")
                self.clear()
                return

            self.openContext("", data)
            self.selectedGroup = min(self.selectedGroup, len(self.groups) - 1)

            self.updateInfoBox()
            self.splitData()
            self.runNormalization()
        else:
            self.clear()

    def clear(self):
        self.groups = []
        self.data = None
        self.centered = None, None
        self.split_data = None, None
        self.merged_splits = None, None
        self.graph.removeDrawingCurves()
        self.infoBox.setText("No data on input")
        self.send("Normalized expression array", None)
        self.send("Filtered expression array", None)

    def updateInfoBox(self):
        genes = self.getGeneNames()
        self.infoBox.setText("%i genes on input" % len(self.data))

    def getSelectedGroup(self):
        return self.groups[self.selectedGroup]

    def getSelectedGroupSplit(self):
        key, value, axis = self.getSelectedGroup()
        other_values = [
            v for k, v, a in self.groups
            if k == key and a == axis and v != value
        ]
        return [(key, value), (key, other_values)], axis

    def getGeneNames(self):
        key, value, axis = self.getSelectedGroup()
        if axis == 0:
            genes = [str(ex[key]) for ex in self.data]
        else:
            genes = [attr.name for attr in self.data.domain.attributes]

        return genes

    def splitData(self):
        groups, axis = self.getSelectedGroupSplit()
        self.split_ind = [
            obiExpression.select_indices(self.data, key, value, axis)
            for key, value in groups
        ]
        self.split_data = obiExpression.split_data(self.data, groups, axis)

    def getMerged(self):
        split1, split2 = self.split_data
        (array1, _, _), (array2, _, _) = split1.toNumpyMA(), split2.toNumpyMA()

        _, _, axis = self.getSelectedGroup()
        merge_function = self.MERGE_METHODS[self.selectedMergeMethod][1]

        merged1 = obiExpression.merge_replicates(array1,
                                                 axis,
                                                 merge_function=merge_function)
        merged2 = obiExpression.merge_replicates(array2,
                                                 axis,
                                                 merge_function=merge_function)
        self.merged_splits = merged1, merged2

        return self.merged_splits

    def runNormalization(self):
        self.progressBarInit()
        self.progressBarSet(0.0)
        G, R = self.getMerged()
        self.progressBarSet(5.0)

        center_method = self.CENTER_METHODS[self.selectedCenterMethod][1]

        # TODO: progess bar , lowess can take a long time
        if self.selectedCenterMethod in [1, 2]:  #Lowess
            Gc, Rc = center_method(G,
                                   R,
                                   f=1. / min(500.,
                                              len(G) / 100),
                                   iter=1)
        else:
            Gc, Rc = center_method(G, R)
        self.progressBarSet(70.0)
        self.centered = Gc, Rc
        self.z_scores = obiExpression.MA_zscore(Gc, Rc, 1. / 3.)
        self.progressBarSet(100.0)
        self.plotMA(Gc, Rc, self.z_scores, self.zCutoff)
        self.progressBarFinished()

    def runNormalizationAsync(self):
        """ Run MA centering and z_score estimation in a separate thread 
        """
        self.error(0)
        self.progressBarInit()
        self.progressBarSet(0.0)
        G, R = self.getMerged()
        #        self.progressBarSet(5.0)

        center_method = self.CENTER_METHODS[self.selectedCenterMethod][1]
        use_lowess = self.selectedCenterMethod in [1, 2]

        def run(progressCallback=lambda value: None
                ):  # the function to run in a thread
            #            progressCallback(5.0)
            if use_lowess:
                Gc, Rc = center_method(
                    G,
                    R,
                    f=2. / 3.,
                    iter=1,
                    progressCallback=lambda val: progressCallback(val / 2))
            else:
                Gc, Rc = center_method(G, R)
            progressCallback(50)
            z_scores = obiExpression.MA_zscore(
                Gc,
                Rc,
                1. / 3.,
                progressCallback=lambda val: progressCallback(50 + val / 2))

            return Gc, Rc, z_scores

        self.progressDiscard = ProgressBarDiscard(self, self)

        async = self.asyncCall(run,
                               name="Normalization",
                               onResult=self.onResults,
                               onError=self.onUnhandledException)
        self.connect(async, SIGNAL("progressChanged(float)"),
                     self.progressDiscard.progressBarSet, Qt.QueuedConnection)
        self.setEnabled(False)
        async .__call__(progressCallback=async .emitProgressChanged)

    ## comment out this line if threading creates any problems
    runNormalization = runNormalizationAsync

    def onResults(self, (Gc, Rc, z_scores)):
        """ Handle the results of centering and z-scoring
        """
        assert (QThread.currentThread() is self.thread())
        self.setEnabled(True)
        qApp.processEvents()
        self.progressBarFinished()
        self.centered = Gc, Rc
        self.z_scores = z_scores
        self.plotMA(Gc, Rc, z_scores, self.zCutoff)
        self.commit()
Пример #2
0
class OWMAPlot(OWWidget):
    settingsList = ["appendZScore", "appendRIValues"]
    contextHandlers = {"": DomainContextHandler("", ["selectedGroup", "zCutoff"])}
    
    CENTER_METHODS = [("Average", obiExpression.MA_center_average),
                      ("Lowess (fast - interpolated)", obiExpression.MA_center_lowess_fast),
                      ("Lowess", obiExpression.MA_center_lowess)]
    
    MERGE_METHODS = [("Average", numpy.ma.average),
                     ("Median", numpy.ma.median),
                     ("Geometric mean", obiExpression.geometric_mean)]
    
    def __init__(self, parent=None, signalManager=None, name="Normalize Expression Array"):
        OWWidget.__init__(self, parent, signalManager, name, wantGraph=True)
        
        self.inputs = [("Expression array", ExampleTable, self.setData)]
        self.outputs = [("Normalized expression array", ExampleTable, Default), ("Filtered expression array", ExampleTable)]
        
        self.selectedGroup = 0
        self.selectedCenterMethod = 0
        self.selectedMergeMethod = 0
        self.zCutoff = 1.96
        self.appendZScore = False
        self.appendRIValues = False
        self.autoCommit = False
        
        self.loadSettings()
        ## GUI
        self.infoBox = OWGUI.widgetLabel(OWGUI.widgetBox(self.controlArea, "Info", addSpace=True),
                                         "No data on input.")
        
        box = OWGUI.widgetBox(self.controlArea, "Split by", addSpace=True)
        self.groupCombo = OWGUI.comboBox(box, self, "selectedGroup", 
                                         callback=self.onGroupSelection
                                         )
        
        self.centerCombo = OWGUI.comboBox(self.controlArea, self, "selectedCenterMethod",
                                          box="Center Fold-change Using",
                                          items=[name for name, _ in self.CENTER_METHODS],
                                          callback=self.onCenterMethodChange,
                                          addSpace=True
                                          )
        
        self.mergeCombo = OWGUI.comboBox(self.controlArea, self, "selectedMergeMethod",
                                         box="Merge Replicates",
                                         items=[name for name, _ in self.MERGE_METHODS],
                                         tooltip="Select the method for replicate merging",
                                         callback=self.onMergeMethodChange,
                                         addSpace=True
                                         )
        
        box = OWGUI.doubleSpin(self.controlArea, self, "zCutoff", 0.0, 3.0, 0.01,
                               box="Z-Score Cutoff",
                               callback=[self.replotMA, self.commitIf])
        
        OWGUI.separator(self.controlArea)
        
        box = OWGUI.widgetBox(self.controlArea, "Ouput")
        OWGUI.checkBox(box, self, "appendZScore", "Append Z-Scores",
                       tooltip="Append calculated Z-Scores to output",
                       callback=self.commitIf
                       )
        
        OWGUI.checkBox(box, self, "appendRIValues", "Append Log Ratio and Intensity values",
                       tooltip="Append calculated Log Ratio and Intensity values to output data",
                       callback=self.commitIf
                       )
        
        cb = OWGUI.checkBox(box, self, "autoCommit", "Commit on change",
                       tooltip="Commit data on any change",
                       callback=self.commitIf
                       )
        
        b = OWGUI.button(box, self, "Commit", callback=self.commit)
        OWGUI.setStopper(self, b, cb, "changedFlag", callback=self.commit)
        
        self.connect(self.graphButton, SIGNAL("clicked()"), self.saveGraph)
        
        OWGUI.rubber(self.controlArea)
        self.graph = OWGraph(self.mainArea)
        self.graph.setAxisTitle(QwtPlot.xBottom, "Intensity: log<sub>10</sub>(R*G)")
        self.graph.setAxisTitle(QwtPlot.yLeft, "Log ratio: log<sub>2</sub>(R/G)")
        self.graph.showFilledSymbols = True
        self.mainArea.layout().addWidget(self.graph)
        self.groups = []
        self.split_data = None, None
        self.merged_splits = None, None
        self.centered = None, None
        self.changedFlag = False
        self.data = None
        
        self.resize(800, 600)
        
    def onFinished(self, status):
        self.setEnabled(True)
    
    def onUnhandledException(self, ex_info):
        self.setEnabled(True)
        print >> sys.stderr, "Unhandled exception in non GUI thread"
        
        ex_type, ex_val, tb = ex_info
        if ex_type == numpy.linalg.LinAlgError and False:
            self.error(0, "Linear algebra error: %s" % repr(ex_val))
        else:
            sys.excepthook(*ex_info)
    
    def onGroupSelection(self):
        if self.data:
            self.updateInfoBox()
            self.splitData()
            self.runNormalization()
        
    def onCenterMethodChange(self):
        if self.data:
            self.runNormalization()
        
    def onMergeMethodChange(self):
        if self.data:
            self.splitData()
            self.runNormalization()
        
    def proposeGroups(self, data):
        col_labels = [attr.attributes.items() for attr in data.domain.attributes]
        col_labels = sorted(reduce(set.union, col_labels, set()))
        col_labels = [(key, value, 1) for key, value in col_labels]
        
        attrs = [attr for attr in data.domain.variables + data.domain.getmetas().values() \
                 if attr.varType == orange.VarTypes.Discrete]
        
        row_labels = [(attr.name, value, 0) for attr in attrs for value in attr.values]
        
        def filterSingleValues(labels):
            ret = []
            for name, value, axis in labels:
                match = [(n, v, a) for n, v, a in labels if n == name]
                if len(match) > 1:
                    ret.append((name, value, axis))
            return ret
            
        col_labels = filterSingleValues(col_labels)
        row_labels = filterSingleValues(row_labels)
        
        return col_labels + row_labels
    
    def setData(self, data):
        self.closeContext("")
        self.data = data
        self.error([0 ,1])
        if data is not None:
            self.infoBox.setText("%i genes on input" % len(data))
            self.groups = self.proposeGroups(data)
            self.groupCombo.clear()
            self.groupCombo.addItems(["%s: %s" % (key, value) for key, value, axis in self.groups])
            
            if not self.groups:
                self.error(1, "Input data has no class attribute or attribute labels!")
                self.clear()
                return
            
            self.openContext("", data)
            self.selectedGroup = min(self.selectedGroup, len(self.groups) - 1)
            
            self.updateInfoBox()
            self.splitData()
            self.runNormalization()
        else:
            self.clear()
        
    def clear(self):
        self.groups = []
        self.data = None
        self.centered = None, None
        self.split_data = None, None
        self.merged_splits = None, None
        self.graph.removeDrawingCurves()
        self.infoBox.setText("No data on input")
        self.send("Normalized expression array", None)
        self.send("Filtered expression array", None)
        
    def updateInfoBox(self):
        genes = self.getGeneNames()
        self.infoBox.setText("%i genes on input" % len(self.data))
        
    def getSelectedGroup(self):
        return self.groups[self.selectedGroup]
    
    def getSelectedGroupSplit(self):
        key, value, axis = self.getSelectedGroup()
        other_values = [v for k, v, a in self.groups if k == key and a == axis and v != value]
        return [(key, value), (key, other_values)], axis
    
    def getGeneNames(self):
        key, value, axis = self.getSelectedGroup()
        if axis == 0:
            genes = [str(ex[key]) for ex in self.data]
        else:
            genes = [attr.name for attr in self.data.domain.attributes]
            
        return genes
    
    def splitData(self): 
        groups, axis = self.getSelectedGroupSplit()
        self.split_ind = [obiExpression.select_indices(self.data, key, value, axis) for key, value in groups]
        self.split_data = obiExpression.split_data(self.data, groups, axis)
        
    def getMerged(self):
        split1, split2 = self.split_data
        (array1, _, _), (array2, _, _) = split1.toNumpyMA(), split2.toNumpyMA()
        
        _, _, axis = self.getSelectedGroup()
        merge_function = self.MERGE_METHODS[self.selectedMergeMethod][1]
        
        merged1 = obiExpression.merge_replicates(array1, axis, merge_function=merge_function)
        merged2 = obiExpression.merge_replicates(array2, axis, merge_function=merge_function)
        self.merged_splits = merged1, merged2
        
        return self.merged_splits
        
    def runNormalization(self):
        self.progressBarInit()
        self.progressBarSet(0.0)
        G, R = self.getMerged()
        self.progressBarSet(5.0)
        
        center_method = self.CENTER_METHODS[self.selectedCenterMethod][1]
        
        # TODO: progess bar , lowess can take a long time
        if self.selectedCenterMethod in [1, 2]: #Lowess
            Gc, Rc = center_method(G, R, f = 1./min(500., len(G)/100), iter=1)
        else:
            Gc, Rc = center_method(G, R)
        self.progressBarSet(70.0)
        self.centered = Gc, Rc
        self.z_scores = obiExpression.MA_zscore(Gc, Rc, 1./3.)
        self.progressBarSet(100.0)
        self.plotMA(Gc, Rc, self.z_scores, self.zCutoff)
        self.progressBarFinished()
        
    def runNormalizationAsync(self):
        """ Run MA centering and z_score estimation in a separate thread 
        """
        self.error(0)
        self.progressBarInit()
        self.progressBarSet(0.0)
        G, R = self.getMerged()
#        self.progressBarSet(5.0)
        
        center_method = self.CENTER_METHODS[self.selectedCenterMethod][1]
        use_lowess = self.selectedCenterMethod in [1, 2]
        
        def run(progressCallback = lambda value: None): # the function to run in a thread
#            progressCallback(5.0)
            if use_lowess:
                Gc, Rc = center_method(G, R, f=2./3., iter=1, progressCallback=lambda val: progressCallback(val/2))
            else:
                Gc, Rc = center_method(G, R)
            progressCallback(50)
            z_scores = obiExpression.MA_zscore(Gc, Rc, 1./3., progressCallback= lambda val: progressCallback(50 + val/2))
            
            return Gc, Rc, z_scores
        
        self.progressDiscard = ProgressBarDiscard(self, self)
         
        async = self.asyncCall(run, name="Normalization",
                               onResult=self.onResults,
                               onError=self.onUnhandledException)
        self.connect(async, SIGNAL("progressChanged(float)"), self.progressDiscard.progressBarSet, Qt.QueuedConnection)
        self.setEnabled(False)
        async.__call__(progressCallback=async.emitProgressChanged)
            
    ## comment out this line if threading creates any problems 
    runNormalization = runNormalizationAsync
    
    def onResults(self, (Gc, Rc, z_scores)):
        """ Handle the results of centering and z-scoring
        """
        assert(QThread.currentThread() is self.thread())
        self.setEnabled(True)
        qApp.processEvents()
        self.progressBarFinished()
        self.centered = Gc, Rc
        self.z_scores = z_scores
        self.plotMA(Gc, Rc, z_scores, self.zCutoff)
        self.commit()