source: orange-bioinformatics/_bioinformatics/widgets/OWFeatureSelection.py @ 1849:bd2ced9dddb8

Revision 1849:bd2ced9dddb8, 31.7 KB checked in by markotoplak, 7 months ago (diff)

Widget rename: Gene selection -> Differential expression

RevLine 
[203]1"""
[1849]2<name>Differential expression</name>
[1051]3<description>Gene differential expression scoring and selection.</description>
[1052]4<priority>1010</priority>
[1726]5<icon>icons/GeneSelection.svg</icon>
[203]6"""
[929]7
[1632]8from __future__ import absolute_import, with_statement
[203]9
[830]10from collections import defaultdict
[1168]11from functools import wraps
[1407]12from operator import add
[1632]13
[929]14import numpy as np
15import numpy.ma as ma
[203]16
[1632]17import orange
18from Orange.OrangeWidgets import OWGUI
19from Orange.OrangeWidgets.OWGraph import *
20from Orange.OrangeWidgets.OWGraphTools import PolygonCurve
21from Orange.OrangeWidgets.OWHist import OWInteractiveHist
22from Orange.OrangeWidgets.OWToolbars import ZoomSelectToolbar
23from Orange.OrangeWidgets.OWWidget import *
24
25from ..obiDifscale import ExpressionSignificance_AREA, ExpressionSignificance_FCts
26from ..obiExpression import *
27from ..obiGEO import transpose
28
[1697]29
[929]30class ExpressionSignificance_TTest_PValue(ExpressionSignificance_TTest):
31    def __call__(self, *args, **kwargs):
[1407]32        return [(key, pval) for key, (t, pval) in \
33                ExpressionSignificance_TTest.__call__(self, *args, **kwargs)]
[1697]34
35
[929]36class ExpressionSignificance_TTest_T(ExpressionSignificance_TTest):
37    def __call__(self, *args, **kwargs):
[1407]38        return [(key, t) for key, (t, pval) in \
39                ExpressionSignificance_TTest.__call__(self, *args, **kwargs)]
[1697]40
41
[929]42class ExpressionSignificance_ANOVA_PValue(ExpressionSignificance_ANOVA):
43    def __call__(self, *args, **kwargs):
[1407]44        return [(key, pval) for key, (t, pval) in \
45                ExpressionSignificance_ANOVA.__call__(self, *args, **kwargs)]
[1697]46
47
[929]48class ExpressionSignificance_ANOVA_F(ExpressionSignificance_ANOVA):
49    def __call__(self, *args, **kwargs):
[1407]50        return [(key, f) for key, (f, pval) in \
51                ExpressionSignificance_ANOVA.__call__(self, *args, **kwargs)]
[1697]52
53
[929]54class ExpressionSignificance_Log2FoldChange(ExpressionSignificance_FoldChange):
55    def __call__(self, *args, **kwargs):
[1407]56        return [(key, math.log(fold, 2.0) if fold > 1e-300 and fold < 1e300 else 0.0) \
57                for key, fold in ExpressionSignificance_FoldChange.__call__(self, *args, **kwargs)]
[1697]58
59
[1066]60class ExpressionSignigicance_MannWhitneyu_U(ExpressionSignificance_MannWhitneyu):
61    def __call__(self, *args, **kwargs):
[1407]62        return [(key, u) for key, (u, p_val) in \
[1697]63                ExpressionSignificance_MannWhitneyu.__call__(self, *args, **kwargs)]
64
65
[204]66class ScoreHist(OWInteractiveHist):
67    def __init__(self, master, parent=None, type="hiTail"):
68        OWInteractiveHist.__init__(self, parent, type=type)
[203]69        self.master = master
70        self.setAxisTitle(QwtPlot.xBottom, "Score")
71        self.setAxisTitle(QwtPlot.yLeft, "Frequency")
[1697]72        self.activateSelection()
73
[204]74    def setBoundary(self, low, hi):
75        OWInteractiveHist.setBoundary(self, low, hi)
[1407]76        self.master.update_selected_info_label(low, hi)
77        self.master.commit_if()
[1697]78
79
[1360]80def disable_controls(method):
81    """ Disable the widget's control area during the duration of this call.
82    """
83    @wraps(method)
84    def f(self, *args, **kwargs):
85        self.controlArea.setDisabled(True)
86        qApp.processEvents()
87        try:
88            return method(self, *args, **kwargs)
89        finally:
90            self.controlArea.setDisabled(False)
91    return f
[1407]92
[1632]93from Orange.orng.orngDataCaching import data_hints
[1633]94
[1632]95from .OWGenotypeDistances import SetContextHandler
96from .OWVulcanoPlot import LabelSelectionWidget
[1407]97
[203]98class OWFeatureSelection(OWWidget):
[1407]99    settingsList = ["method_index", "dataLabelIndex", "compute_null",
100                    "permutations_count", "selectPValue", "auto_commit"]
101    contextHandlers={"Data": DomainContextHandler("Data", ["genes_in_columns"]),
102                     "TargetSelection": SetContextHandler("TargetSelection",
103                                                          findImperfect=False)}
104   
[830]105    def __init__(self, parent=None, signalManager=None, name="Gene selection"):
[407]106        OWWidget.__init__(self, parent, signalManager, name, wantGraph=True, showSaveGraph=True)
[1407]107        self.inputs = [("Examples", ExampleTable, self.set_data)]
[951]108        self.outputs = [("Example table with selected genes", ExampleTable), ("Example table with remaining genes", ExampleTable), ("Selected genes", ExampleTable)]
[203]109
[1407]110        self.method_index = 0
111#        self.dataLabelIndex = 0
112        self.genes_in_columns = False
113        self.compute_null = False
114        self.permutations_count = 10
115        self.auto_commit = False
[204]116        self.selectNBest = 20
[758]117        self.selectPValue = 0.01
[1407]118        self.data_changed_flag = False
119        self.add_scores_to_output = True
[203]120
[929]121        self.oneTailTestHi = oneTailTestHi = lambda array, low, hi: array >= hi
122        self.oneTailTestLow = oneTailTestLow = lambda array, low, hi: array <= low
123        self.twoTailTest = twoTailTest = lambda array, low, hi: (array >= hi) | (array <= low)
124        self.middleTest = middleTest = lambda array, low, hi: (array <= hi) | (array >= low)
125       
[204]126        self.histType = {oneTailTestHi:"hiTail", oneTailTestLow:"lowTail", twoTailTest:"twoTail", middleTest:"middle"}
[929]127
[1407]128        # [(name, func, tail test, two sample test), ...]
129        self.score_methods = [("fold change", ExpressionSignificance_FoldChange, twoTailTest, True),
130                             ("log2 fold change", ExpressionSignificance_Log2FoldChange, twoTailTest, True),
131                             ("t-test", ExpressionSignificance_TTest_T, twoTailTest, True),
132                             ("t-test p-value", ExpressionSignificance_TTest_PValue, oneTailTestLow, True),
133                             ("anova", ExpressionSignificance_ANOVA_F, oneTailTestHi, False),
134                             ("anova p-value", ExpressionSignificance_ANOVA_PValue, oneTailTestLow, False),
135                             ("signal to noise ratio", ExpressionSignificance_SignalToNoise, twoTailTest, True),
136                             ("info gain", ExpressionSignificance_Info, oneTailTestHi, True),
137                             ("chi-square", ExpressionSignificance_ChiSquare, oneTailTestHi, True),
[1448]138                             ("mann-whitney", ExpressionSignigicance_MannWhitneyu_U, oneTailTestLow, True),
139                             ("AREA (timeseries)", ExpressionSignificance_AREA, oneTailTestHi, False),
140                             ("FC (timeseries)", ExpressionSignificance_FCts, oneTailTestHi, False)]
[203]141
[204]142        boxHistogram = OWGUI.widgetBox(self.mainArea)
143        self.histogram = ScoreHist(self, boxHistogram)
144        boxHistogram.layout().addWidget(self.histogram)
[203]145        self.histogram.show()
146       
[1360]147        box = OWGUI.widgetBox(self.controlArea, "Info")
[203]148        self.dataInfoLabel = OWGUI.widgetLabel(box, "\n\n")
[1407]149        self.dataInfoLabel.setWordWrap(True)
[203]150        self.selectedInfoLabel = OWGUI.widgetLabel(box, "")
[828]151
152        box1 = OWGUI.widgetBox(self.controlArea, "Scoring Method")
[1407]153        self.testRadioBox = OWGUI.comboBox(box1, self, "method_index",
154                                    items=[sm[0] for sm in self.score_methods],
155                                    callback=[self.on_scoring_method_changed, self.update_scores])
[1360]156       
[1407]157        box = OWGUI.widgetBox(self.controlArea, "Target Labels")
158        self.label_selection_widget = LabelSelectionWidget(self)
159        self.label_selection_widget.setMaximumHeight(150)
160        box.layout().addWidget(self.label_selection_widget)
161        self.connect(self.label_selection_widget,
162                     SIGNAL("selection_changed()"),
163                     self.on_target_changed)
164       
165        self.connect(self.label_selection_widget,
166                     SIGNAL("label_activated(int)"),
167                     self.on_label_activated)
168       
169        self.genes_in_columns_check = OWGUI.checkBox(box, self, "genes_in_columns",
170                                                  "Genes in columns",
171                                                  callback=self.on_genes_in_columns_change)
[830]172   
[1407]173#        ZoomSelectToolbar(self, self.controlArea, self.histogram,
174#                          buttons=[ZoomSelectToolbar.IconSelect,
175#                                   ZoomSelectToolbar.IconZoom,
176#                                   ZoomSelectToolbar.IconPan])
[758]177       
[1360]178        box = OWGUI.widgetBox(self.controlArea, "Selection")
[1226]179        box.layout().setSpacing(0)
[1407]180        callback = self.update_boundary
[1360]181        self.upperBoundarySpin = OWGUI.doubleSpin(box, self, "histogram.upperBoundary",
182                                                  min=-1e6, max=1e6, step= 1e-6,
183                                                  label="Upper threshold:", 
184                                                  callback=callback, 
185                                                  callbackOnReturn=True)
186       
187        self.lowerBoundarySpin = OWGUI.doubleSpin(box, self, "histogram.lowerBoundary", 
188                                                  min=-1e6, max=1e6, step= 1e-6, 
189                                                  label="Lower threshold:", 
190                                                  callback=callback, 
191                                                  callbackOnReturn=True)
192       
[1407]193        check = OWGUI.checkBox(box, self, "compute_null", "Compute null distribution",
194                               callback=self.update_scores)
[828]195
[1407]196        check.disables.append(OWGUI.spin(box, self, "permutations_count", min=1, max=10, 
197                                         label="Permutations:", callback=self.update_scores, 
[1360]198                                         callbackOnReturn=True))
[828]199
200        box1 = OWGUI.widgetBox(box, orientation='horizontal')
[1360]201        check.disables.append(OWGUI.doubleSpin(box1, self, "selectPValue", 
202                                               min=2e-7, max=1.0, step=1e-7, 
203                                               label="P-value:"))
[1407]204        check.disables.append(OWGUI.button(box1, self, "Select", callback=self.select_p_best))
[758]205        check.makeConsistent()
[828]206
207        box1 = OWGUI.widgetBox(box, orientation='horizontal')
208        OWGUI.spin(box1, self, "selectNBest", 0, 10000, step=1, label="Best Ranked:")
[1407]209        OWGUI.button(box1, self, "Select", callback=self.select_n_best)
[828]210
[951]211        box = OWGUI.widgetBox(self.controlArea, "Output")
[1407]212        b = OWGUI.button(box, self, "&Commit", callback=self.commit)
213        cb = OWGUI.checkBox(box, self, "auto_commit", "Commit on change")
214        OWGUI.setStopper(self, b, cb, "data_changed_flag", self.commit)
215        OWGUI.checkBox(box, self, "add_scores_to_output", "Add gene scores to output",
216                       callback=self.commit_if) 
[952]217       
[203]218        OWGUI.rubber(self.controlArea)
[407]219
220        self.connect(self.graphButton, SIGNAL("clicked()"), self.histogram.saveToFile)
[203]221       
222        self.loadSettings()
223
224        self.data = None
225        self.discData = None
226        self.scoreCache = {}
[758]227        self.nullDistCache = {}
[204]228        self.cuts = {}
[203]229        self.discretizer = orange.EquiNDiscretization(numberOfIntervals=5)
[1407]230        self.null_dist = []
[929]231        self.targets = []
[767]232        self.scores = {}
[1407]233        self.genes_in_columns = True
234        self.target_selections = None
235       
236        self.on_scoring_method_changed()
237       
[929]238        self.resize(800, 600)
[203]239       
[1407]240    def clear(self):
241        """ Clear widget.
242        """
[203]243        self.scoreCache = {}
[758]244        self.nullDistCache = {}
[203]245        self.discData = None
[1407]246        self.data = None
247        self.attribute_targets = []
248        self.class_targets = []
249        self.label_selection_widget.clear()
250        self.clear_plot()
251       
252    def clear_plot(self):
253        """ Clear the histogram plot.
254        """
255        self.histogram.removeDrawingCurves()
256        self.histogram.clear()
257        self.histogram.replot()
258       
259    def init_from_data(self, data):
260        """ Init widget state from the data.
261        """
262        if data:
263            items = [attr.attributes.items() for attr in data.domain.attributes]
264            items = reduce(add, items, [])
265           
266            targets = defaultdict(set)
267            for label, value in items:
268                targets[label].add(value)
269               
270            targets = [(key, sorted(vals)) for key, vals in targets.items() \
271                       if len(vals) >= 2]
272            self.attribute_targets = targets
273           
274            class_var = data.domain.class_var
275            if class_var and isinstance(class_var, orange.EnumVariable):
276                targets = [(class_var.name,
277                            list(class_var.values))]
278                self.class_targets = targets
279            else:
280                self.class_targets = []
281           
282    def update_targets_widget(self):
283        """ Update the contents of the targets widget.
284        """
285        if self.data:
286            if self.genes_in_columns:
287                targets = self.attribute_targets
288            elif self.data.domain.classVar:
289                targets = self.class_targets
290            else:
291                targets = []
292        else:
293            targets = []
294           
295        self.label_selection_widget.clear()
296        self.label_selection_widget.set_labels(targets)
297        self.data_labels = targets
[1689]298
[1407]299    def set_data(self, data):
300        self.closeContext("Data")
301        self.closeContext("TargetSelection")
302        self.error([0, 1])
303        self.warning(0)
304        self.clear()
305        self.genes_in_columns_check.setEnabled(True)
[203]306        self.data = data
[1407]307        self.init_from_data(data)
[1689]308
[830]309        if self.data:
[1407]310            self.genes_in_columns = not data_hints.get_hint(data, "genesinrows", False)
311            self.openContext("Data", data)
[1689]312
313            # If only attr. labels or only class values then disable
314            # the 'Genes in columns' control
[1407]315            if not self.attribute_targets or not self.class_targets:
316                self.genes_in_columns_check.setEnabled(False)
317                self.genes_in_columns = bool(self.attribute_targets)
[1689]318
[1407]319        self.update_targets_widget()
[1689]320
321        if self.data is not None  and \
322                not (self.attribute_targets or self.class_targets):
323            # If both attr. labels and classes are missing, show an error
[1849]324            self.error(1, "Cannot compute gene scores! Differential expression widget "
[1689]325                          "requires a data-set with a discrete class variable "
326                          "or attribute labels!")
[1139]327            self.data = None
[1689]328
[1407]329        if self.data:
330            # Load context selection
331            items = [(label, v) for label, values in self.data_labels for v in values]
[1689]332
[1407]333            self.target_selections = [values[:1] for _, values in self.data_labels]
334            label, values = self.data_labels[0]
335            self.current_target_selection = label, values[:1] # Default selections
[1689]336
[1407]337            self.openContext("TargetSelection", set(items)) # Load selections from context
338            self.label_selection_widget.set_selection(*self.current_target_selection)
339
[929]340        if not self.data:
[951]341            self.send("Example table with selected genes", None)
342            self.send("Example table with remaining genes", None)
343            self.send("Selected genes", None)
[1689]344
[1407]345    def set_targets(self, targets):
346        """ Set the target groups for score computation.
347        """
348        self.targets = targets
349        self.update_scores()
[929]350   
[1407]351    def compute_scores(self, data, score_func, use_attribute_labels,
352                       target=None, advance=lambda: None):
353        score_func = score_func(data, use_attribute_labels)
[929]354        advance()
[1407]355        score = score_func(target=target)
[929]356        score = [(key, val) for key, val in score if val is not ma.masked]
357        return score
358   
[1407]359    def compute_null_distribution(self, data, score_func, use_attributes,
360                                  target=None, perm_count=10, advance=lambda: None):
361        score_func = score_func(data, use_attributes)
362        dist = score_func.null_distribution(perm_count, target, advance=advance)
[1166]363        return [score for run in dist for k, score in run if score is not ma.masked]
[1360]364           
365    @disable_controls
[1407]366    def update_scores(self):
[1360]367        """ Compute the scores and update the histogram.
368        """
[1407]369        self.clear_plot()
370        self.error(0)
[1692]371        label, values = self.current_target_selection
372        if not self.data or label is None:
[1407]373            return
374        _, score_func, _, two_sample_test = self.score_methods[self.method_index]
375        if two_sample_test:
376            target = self.targets
377            score_target = set(target)
378            ind1, ind2 = score_func(self.data, self.genes_in_columns).test_indices(score_target)
379            if not len(ind1) or not len(ind2):
380                self.error(0, "Target labels most exclude/include at least one value.")
381                return
382           
383        else: # ANOVA should use all labels.
384            target = dict(self.data_labels)[label]
385            if self.genes_in_columns:
386                target = [(label, t) for t in target]
387            score_target = target
388#            indices = score_func(self.data, self.genes_in_columns).test_indices(score_target)
389            # TODO: Check that each label has more than one measurement, raise warning otherwise.
390         
391        pb = OWGUI.ProgressBar(self, 4 + self.permutations_count if self.compute_null else 3)
392        self.scores = dict(self.compute_scores(self.data, score_func,
393                    self.genes_in_columns, score_target, advance=pb.advance))
394        pb.advance()
395        if self.compute_null:
396            self.null_dist = self.compute_null_distribution(self.data,
397                        score_func, self.genes_in_columns, score_target,
398                        self.permutations_count, advance=pb.advance)
[929]399        else:
[1407]400            self.null_dist = []
[929]401        pb.advance()
[1407]402        self.histogram.type = self.histType[self.score_methods[self.method_index][2]]
[1056]403        if self.scores:
404            self.histogram.setValues(self.scores.values())
405            self.histogram.setBoundary(self.histogram.minx if self.histogram.type in ["lowTail", "twoTail"] else self.histogram.maxx,
406                                       self.histogram.maxx if self.histogram.type in ["hiTail", "twoTail"] else self.histogram.minx)
[1407]407            if self.compute_null and self.null_dist:
408                nullY, nullX = numpy.histogram(self.null_dist, bins=self.histogram.xData)
409                self.histogram.nullCurve = self.histogram.addCurve("nullCurve",
410                        Qt.black, Qt.black, 6, symbol=QwtSymbol.NoSymbol,
411                        style=QwtPlotCurve.Steps, xData = nullX,
412                        yData = nullY/self.permutations_count)
[1056]413               
414                minx = min(min(nullX), self.histogram.minx)
415                maxx = max(max(nullX), self.histogram.maxx)
[1407]416                miny = min(min(nullY/self.permutations_count), self.histogram.miny)
417                maxy = max(max(nullY/self.permutations_count), self.histogram.maxy)
[1056]418
419                self.histogram.setAxisScale(QwtPlot.xBottom, minx - (0.05 * (maxx - minx)), maxx + (0.05 * (maxx - minx)))
420                self.histogram.setAxisScale(QwtPlot.yLeft, miny - (0.05 * (maxy - miny)), maxy + (0.05 * (maxy - miny)))
421            state = dict(hiTail=(False, True), lowTail=(True, False), twoTail=(True, True))
422            for spin, visible in zip((self.upperBoundarySpin, self.lowerBoundarySpin), state[self.histogram.type]):
423                spin.setVisible(visible)
[929]424           
[1407]425            # If this is a two sample test add markers to the left and right
426            # plot indicating which target group is over-expressed in that
427            # part 
428            if self.method_index in [0, 2, 6]:
429                if self.method_index == 0: ## fold change is centered on 1.0
[1056]430                    x1, y1 = (self.histogram.minx + 1) / 2 , self.histogram.maxy
431                    x2, y2 = (self.histogram.maxx + 1) / 2 , self.histogram.maxy
432                else:
433                    x1, y1 = (self.histogram.minx) / 2 , self.histogram.maxy
434                    x2, y2 = (self.histogram.maxx) / 2 , self.histogram.maxy
[1407]435                if self.genes_in_columns:
436                    label = target[0][0]
437                    target_values = [t[1] for t in target]
438                    values = dict(self.data_labels)[label]
439                else:
440                    target_values = target
441                    values = self.data_labels[0][1]
442                   
443                left = ", ".join(v for v in values if v not in target_values)
444                right = ", ".join(v for v in values if v in target_values)
445                   
446                self.histogram.addMarker(left, x1, y1)
447                self.histogram.addMarker(right, x2, y2)
[1057]448            self.warning(0)
[1056]449        else:
450            self.warning(0, "No scores obtained.")
[929]451        self.histogram.replot()
452        pb.advance()
453        pb.finish()
[1407]454        self.update_data_info_label()
[835]455           
[1407]456    def update_data_info_label(self):
[929]457        if self.data:
458            samples, genes = len(self.data), len(self.data.domain.attributes)
[1407]459            if self.genes_in_columns:
[929]460                samples, genes = genes, samples
[1407]461                target_labels = [t[1] for t in self.targets]
462            else:
463                target_labels = self.targets
[929]464            text = "%i samples, %i genes\n" % (samples, genes)
[1407]465            text += "Sample target: '%s'" % (",".join(target_labels))
[203]466        else:
[835]467            text = "No data on input\n"
[410]468        self.dataInfoLabel.setText(text)
[203]469
[1407]470    def update_selected_info_label(self, cutOffLower=0, cutOffUpper=0):
471        self.cuts[self.method_index] = (cutOffLower, cutOffUpper)
[929]472        if self.data:
473            scores = np.array(self.scores.values())
[1407]474            test = self.score_methods[self.method_index][2]
[929]475            self.selectedInfoLabel.setText("%i selected genes" % len(np.nonzero(test(scores, cutOffLower, cutOffUpper))[0]))
[203]476        else:
[816]477            self.selectedInfoLabel.setText("0 selected genes")
[204]478
[1407]479    def select_n_best(self):
[204]480        scores = self.scores.items()
481        scores.sort(lambda a,b:cmp(a[1], b[1]))
482        if not scores:
483            return
[1407]484        if self.score_methods[self.method_index][2]==self.oneTailTestHi:
[204]485            scores = scores[-max(self.selectNBest, 1):]
486            self.histogram.setBoundary(scores[0][1], scores[0][1])
[1407]487        elif self.score_methods[self.method_index][2]==self.oneTailTestLow:
[204]488            scores = scores[:max(self.selectNBest,1)]
489            self.histogram.setBoundary(scores[-1][1], scores[-1][1])
490        else:
[413]491            scoresHi = scores[-max(min(self.selectNBest, len(scores)/2), 1):]
492            scoresLo = scores[:max(min(self.selectNBest, len(scores)/2), 1)]
493            scores = [(abs(score), 1) for attr, score in scoresHi] + [(abs(score), -1) for attr, score in scoresLo]
[1407]494            if self.score_methods[self.method_index][0]=="fold change": ## comparing fold change on a logaritmic scale
[413]495                scores =  [(abs(math.log(max(min(score, 1e300), 1e-300), 2.0)), sign) for score, sign in scores]
496            scores.sort()
497            scores = scores[-max(self.selectNBest, 1):]
498            countHi = len([score for score, sign in scores if sign==1])
499            countLo = len([score for score, sign in scores if sign==-1])
[758]500            cutHi = scoresHi[-countHi][1] if countHi else scoresHi[-1][1] + 1e-7
501            cutLo = scoresLo[countLo-1][1] if countLo else scoresLo[0][1] - 1e-7
[413]502            self.histogram.setBoundary(cutLo, cutHi)
[758]503
[1407]504    def update_boundary(self):
[1056]505        if self.data != None and self.scores.items():
[1407]506            if self.score_methods[self.method_index][2]==self.oneTailTestHi:
[1056]507                self.histogram.setBoundary(self.histogram.lowerBoundary, self.histogram.lowerBoundary)
[1407]508            elif self.score_methods[self.method_index][2]==self.oneTailTestLow:
[1056]509                self.histogram.setBoundary(self.histogram.upperBoundary, self.histogram.upperBoundary)
510            else:
511                self.histogram.setBoundary(self.histogram.lowerBoundary, self.histogram.upperBoundary)
512
[1407]513    def select_p_best(self):
514        if not self.null_dist:
[758]515            return
[1407]516        nullDist = sorted(self.null_dist)
517        test = self.score_methods[self.method_index][2]
[1167]518        count = min(int(len(nullDist)*self.selectPValue), len(nullDist))
[758]519        if test == self.oneTailTestHi:
[929]520            cut = nullDist[-count] if count else nullDist[-1] # + 1e-7
[758]521            self.histogram.setBoundary(cut, cut)
522        elif test == self.oneTailTestLow:
[929]523            cut = nullDist[count - 1] if count else nullDist[0] # - 1e-7
[758]524            self.histogram.setBoundary(cut, cut)
525        elif count:
526            scoresHi = nullDist[-count:]
[759]527            scoresLo = nullDist[:count]
[758]528            scores = [(abs(score), 1) for score in scoresHi] + [(abs(score), -1) for score in scoresLo]
[1407]529            if self.score_methods[self.method_index][0] == "fold change": ## fold change is on a logaritmic scale
[758]530                scores =  [(abs(math.log(max(min(score, 1e300), 1e-300), 2.0)), sign) for score, sign in scores]
531            scores = sorted(scores)[-count:]
532            countHi = len([score for score, sign in scores if sign==1])
533            countLo = len([score for score, sign in scores if sign==-1])
534            cutHi = scoresHi[-countHi] if countHi else scoresHi[-1] + 1e-7
535            cutLo = scoresLo[countLo-1] if countLo else scoresLo[0] - 1e-7
536            self.histogram.setBoundary(cutLo, cutHi)
537        else:
538            self.histogram.setBoundary(nullDist[0] - 1e-7, nullDist[-1] + 1e-7)
[929]539           
[1056]540
[1407]541    def commit_if(self):
542        if self.auto_commit:
543            self.commit()
[929]544        else:
[1407]545            self.data_changed_flag = True
[204]546       
[1407]547    def commit(self):
[1139]548        if not self.data or not self.scores:
[929]549            return
[1407]550        test = self.score_methods[self.method_index][2]
[929]551       
552        cutOffUpper = self.histogram.upperBoundary
553        cutOffLower = self.histogram.lowerBoundary
554       
555        scores = np.array(self.scores.items())
556        scores[:, 1] = test(np.array(scores[:, 1], dtype=float), cutOffLower, cutOffUpper)
[935]557        selected = set([key for key, test in scores if test])
558        remaining = set([key for key, test in scores if not test])
[1407]559        if self.data and self.genes_in_columns:
[951]560            selected = sorted(selected)
[1407]561            if selected:
562                newdata = orange.ExampleTable(orange.Domain(self.data.domain),
563                                        [self.data[int(i)] for i in selected])
564            else:
565                newdata = None
566            if self.add_scores_to_output:
567                score_attr = orange.FloatVariable(self.score_methods[self.method_index][0])
[951]568                mid = orange.newmetaid()
[1139]569               
[1407]570            if self.add_scores_to_output and newdata is not None:
571                newdata.domain.addmeta(mid, score_attr)
[951]572                for ex, key in zip(newdata, selected):
573                    ex[mid] = self.scores[key]
[1118]574                   
[951]575            self.send("Example table with selected genes", newdata)
[1118]576           
[951]577            remaining = sorted(remaining)
[1407]578            if remaining:
579                newdata = orange.ExampleTable(orange.Domain(self.data.domain), [self.data[int(i)] for i in remaining])
580            else:
581                newdata = None
[1139]582           
[1407]583            if self.add_scores_to_output and newdata is not None:
584                newdata.domain.addmeta(mid, score_attr)
[951]585                for ex, key in zip(newdata, remaining):
586                    ex[mid] = self.scores[key]
[1118]587                   
[951]588            self.send("Example table with remaining genes", newdata)
[763]589           
[1407]590        elif self.data and not self.genes_in_columns:
[929]591           
[1407]592            selected_attrs = [attr for attr in self.data.domain.attributes \
593                             if attr in selected or \
594                                attr.varType == orange.VarTypes.String]
595            newdomain = orange.Domain(selected_attrs, self.data.domain.classVar)
596            if self.add_scores_to_output:
[951]597                for attr in newdomain.attributes:
[1407]598                    attr.attributes[self.score_methods[self.method_index][0]] = str(self.scores[attr])
[929]599            newdomain.addmetas(self.data.domain.getmetas())
600            newdata = orange.ExampleTable(newdomain, self.data)
[1118]601           
[1407]602            self.send("Example table with selected genes", newdata if selected_attrs else None)
[763]603           
[1407]604            remaining_attrs = [attr for attr in self.data.domain.attributes if attr in remaining]
605            newdomain = orange.Domain(remaining_attrs, self.data.domain.classVar)
606            if self.add_scores_to_output:
[951]607                for attr in newdomain.attributes:
[1407]608                    attr.attributes[self.score_methods[self.method_index][0]] = str(self.scores[attr])
[929]609            newdomain.addmetas(self.data.domain.getmetas())
610            newdata = orange.ExampleTable(newdomain, self.data)
[1118]611           
[1407]612            self.send("Example table with remaining genes", newdata if remaining_attrs else None)
[763]613           
[1407]614            domain = orange.Domain([orange.StringVariable("label"),
615                                    orange.FloatVariable(self.score_methods[self.method_index][0])],
616                                    False)
617            if selected_attrs:
618                selected_genes = orange.ExampleTable(domain,
619                            [[attr.name, self.scores.get(attr, 0)] for attr in selected_attrs])
620            else:
621                selected_genes = None
622            self.send("Selected genes",  selected_genes)
[410]623           
[203]624        else:
[951]625            self.send("Example table with selected genes", None)
626            self.send("Example table with remaining genes", None)
627            self.send("Selected genes", None)
[1407]628        self.data_changed_flag = False
[1689]629
[1407]630    def on_target_changed(self):
[1689]631        label, values = self.label_selection_widget.current_selection()
632
633        if values is None:
634            values = []
635
[1407]636        if self.genes_in_columns:
637            targets = [(label, t) for t in values]
638        else:
639            targets = values
[1689]640
[1407]641        self.targets = targets
642        self.current_target_selection = label, values
643        # Save target label selection
644        labels = [l for l, _ in self.data_labels]
645        if label in labels:
646            label_index = labels.index(label)
647            self.target_selections[label_index] = values
[1689]648        self.set_targets(targets)
649
[1407]650    def on_label_activated(self, index):
651        selection = self.target_selections[index]
652        if not selection:
653            selection = self.data_labels[index][1][:1]
654        self.label_selection_widget.set_selection(index, selection)
[830]655
[1407]656    def on_genes_in_columns_change(self):
657        self.closeContext("TargetSelection")
658        self.update_targets_widget()
659        items = [(label, v) for label, values in self.data_labels \
660                                for v in values]
661        if self.data_labels:
662            label, values = self.data_labels[0] 
663            self.current_target_selection = label, values[:1]
664            self.target_selections = [values[1:] for _, values in self.data_labels]
665            self.openContext("TargetSelection", set(items))
666            self.label_selection_widget.set_selection(*self.current_target_selection)
667       
668    def on_scoring_method_changed(self):
669        two_sample = self.score_methods[self.method_index][3]
670        self.label_selection_widget.values_view.setDisabled(not two_sample)
671       
672    def settingsFromWidgetCallbackTargetSelection(self, handler, context):
673        context.target_selections = self.target_selections
674        context.current_target_selection = self.current_target_selection
675       
676    def settingsToWidgetCallbackTargetSelection(self, handler, context):
677        self.target_selections = getattr(context, "target_selections", self.target_selections)
678        self.current_target_selection = getattr(context, "current_target_selection", self.current_target_selection)
679   
[203]680if __name__=="__main__":
681    import sys
682    app = QApplication(sys.argv)
[1066]683#    data = orange.ExampleTable("/home/marko/t2.tab")
[1407]684#    data = orange.ExampleTable("../../../doc/datasets/brown-selected")
685    data = orange.ExampleTable(os.path.expanduser("~/Documents/GDS636"))
[203]686    w = OWFeatureSelection()
687    w.show()
[1407]688    w.set_data(data)
689#    w.set_data(None)
690#    w.set_data(data)
[686]691    app.exec_()
[498]692    w.saveSettings()
Note: See TracBrowser for help on using the repository browser.