source: orange-bioinformatics/orangecontrib/bio/widgets/OWFeatureSelection.py @ 2070:d4e03f6d648d

Revision 2070:d4e03f6d648d, 32.1 KB checked in by Ales Erjavec <ales.erjavec@…>, 4 days ago (diff)

Copy input data name to output.

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