source: orange-bioinformatics/orangecontrib/bio/widgets/OWSetEnrichment.py @ 1999:c60995640805

Revision 1999:c60995640805, 29.0 KB checked in by Ales Erjavec <ales.erjavec@…>, 6 weeks ago (diff)

Threaded widget initialization.

Line 
1"""<name>Gene Set Enrichment</name>
2<icon>icons/GeneSetEnrichment.svg</icon>
3"""
4
5from __future__ import absolute_import, with_statement
6
7import math
8import operator
9from collections import defaultdict
10
11from Orange.orng import orngEnviron, orngServerFiles
12from Orange.orng.orngDataCaching import data_hints
13from Orange.OrangeWidgets import OWGUI
14from Orange.OrangeWidgets.OWGUI import LinkStyledItemDelegate, LinkRole
15from Orange.OrangeWidgets.OWGUI import BarItemDelegate
16from Orange.OrangeWidgets.OWWidget import *
17
18from Orange.OrangeWidgets.OWConcurrent import ThreadExecutor, Task
19
20from .utils.download import EnsureDownloaded
21
22from .. import obiGene, obiGeneSets, obiProb, obiTaxonomy
23
24NAME = "Gene Set Enrichment"
25DESCRIPTION = ""
26ICON = "icons/GeneSetEnrichment.svg"
27PRIORITY = 5000
28
29INPUTS = [("Data", Orange.data.Table, "setData", Default),
30          ("Reference", Orange.data.Table, "setReference")]
31OUTPUTS = [("Data subset", Orange.data.Table)]
32
33REPLACES = ["_bioinformatics.widgets.OWSetEnrichment.OWSetEnrichment"]
34
35
36def gsname(geneset):
37    return geneset.name if geneset.name else geneset.id
38
39fmtp = lambda score: "%0.5f" % score if score > 10e-4 else "%0.1e" % score
40fmtpdet = lambda score: "%0.9f" % score if score > 10e-4 else "%0.5e" % score
41
42
43def as_unicode(string):
44    if isinstance(string, str):
45        return string.decode("utf-8", errors="ignore")
46    else:
47        return string
48
49# A translation table mapping punctuation, ... to spaces
50_TR_TABLE = dict((ord(c), ord(" ")) for c in ".,!?()[]{}:;'\"<>")
51
52def word_split(string):
53    """
54    Split a string into a list of words.
55    """
56    return as_unicode(string).translate(_TR_TABLE).split()
57
58
59def _toPyObject(variant):
60    val = variant.toPyObject()
61    if isinstance(val, type(NotImplemented)): # PyQt 4.4 converts python int, floats ... to C types
62        qtype = variant.type()
63        if qtype == QVariant.Double:
64            val, ok = variant.toDouble()
65        elif qtype == QVariant.Int:
66            val, ok = variant.toInt()
67        elif qtype == QVariant.LongLong:
68            val, ok = variant.toLongLong()
69        elif qtype == QVariant.String:
70            val = variant.toString()
71    return val
72
73class MyTreeWidget(QTreeWidget):
74    def paintEvent(self, event):
75        QTreeWidget.paintEvent(self, event)
76        if getattr(self, "_userMessage", None):
77            painter = QPainter(self.viewport())
78            font = QFont(self.font())
79            font.setPointSize(15)
80            painter.setFont(font)
81            painter.drawText(self.viewport().geometry(), Qt.AlignCenter, self._userMessage)
82            painter.end()
83
84class MyTreeWidgetItem(QTreeWidgetItem):
85    def __lt__(self, other):
86        if not self.treeWidget():
87            return id(self) < id(other)
88        column = self.treeWidget().sortColumn()
89        if column in [4,5]:
90            lhs = _toPyObject(self.data(column, 42))
91            rhs = _toPyObject(other.data(column, 42))
92        else:
93            lhs = _toPyObject(self.data(column, Qt.DisplayRole))
94            rhs = _toPyObject(other.data(column, Qt.DisplayRole))
95        return lhs < rhs
96
97def name_or_none(id):
98    """Return organism name for ncbi taxid or None if not found.
99    """
100    try:
101        return obiTaxonomy.name(id)
102    except obiTaxonomy.UnknownSpeciesIdentifier:
103        return None
104
105class OWSetEnrichment(OWWidget):
106    settingsList = ["speciesIndex", "genesinrows", "geneattr", "categoriesCheckState", "useMinCountFilter", "useMaxPValFilter", "useMaxFDRFilter", "minClusterCount", "maxPValue", "maxFDR" ]
107    contextHandlers = {"":DomainContextHandler("", ["speciesIndex", "genesinrows", "geneattr", "categoriesCheckState"])}
108
109    def refreshHierarchy(self):
110        self.setHierarchy(*self.getHierarchy(taxid=self.taxid_list[self.speciesIndex]))
111
112    def __init__(self, parent=None, signalManager=None, name="Gene Set Enrichment Analysis", **kwargs):
113        OWWidget.__init__(self, parent, signalManager, name, **kwargs)
114        self.inputs = [("Data", ExampleTable, self.setData, Default), ("Reference", ExampleTable, self.setReference)]
115        self.outputs = [("Data subset", ExampleTable)]
116
117        self.speciesIndex = 0
118        self.genesinrows = False
119        self.geneattr = 0
120        self.geneMatcherSettings = [False, False, True, False]
121        self.useReferenceData = False
122        self.useMinCountFilter = True
123        self.useMaxPValFilter = True
124        self.useMaxFDRFilter = True
125        self.minClusterCount = 3
126        self.maxPValue = 0.01
127        self.maxFDR = 0.01
128
129        self.categoriesCheckState = {}
130
131        self.loadSettings()
132
133        box = OWGUI.widgetBox(self.controlArea, "Info")
134        self.infoBox = OWGUI.widgetLabel(box, "Info")
135        self.infoBox.setText("No data on input")
136
137        self.speciesComboBox = OWGUI.comboBox(self.controlArea, self,
138                      "speciesIndex", "Species",
139                      callback=lambda: (self.refreshHierarchy(), self.data and self.updateAnnotations()),
140                      debuggingEnabled=0)
141
142        box = OWGUI.widgetBox(self.controlArea, "Gene names")
143        self.geneAttrComboBox = OWGUI.comboBox(box, self, "geneattr",
144                                "Gene attribute",
145                                sendSelectedValue=0,
146                                callback=self.updateAnnotations)
147
148        cb = OWGUI.checkBox(box, self, "genesinrows", "Use attribute names",
149                            callback=lambda :self.data and self.updateAnnotations(),
150                            disables=[(-1, self.geneAttrComboBox)])
151        cb.makeConsistent()
152
153        OWGUI.button(box, self, "Gene matcher settings",
154                     callback=self.updateGeneMatcherSettings,
155                     tooltip="Open gene matching settings dialog",
156                     debuggingEnabled=0)
157
158        self.referenceRadioBox = OWGUI.radioButtonsInBox(self.controlArea,
159                    self, "useReferenceData", ["Entire genome", "Reference set (input)"],
160                    tooltips=["Use entire genome for reference",
161                              "Use genes from Referece Examples input signal as reference"],
162                    box="Reference", callback=self.updateAnnotations)
163
164        box = OWGUI.widgetBox(self.controlArea, "Gene Sets")
165        self.groupsWidget = QTreeWidget(self)
166        self.groupsWidget.setHeaderLabels(["Category"])
167        box.layout().addWidget(self.groupsWidget)
168
169        hLayout = QHBoxLayout()
170        hLayout.setSpacing(10)
171        hWidget = OWGUI.widgetBox(self.mainArea, orientation=hLayout)
172        sb, sbcb = OWGUI.spin(hWidget, self, "minClusterCount",
173                              0, 100, label="Genes",
174                              tooltip="Minimum gene count",
175                              callback=self.filterAnnotationsChartView,
176                              callbackOnReturn=True,
177                              checked="useMinCountFilter",
178                              checkCallback=self.filterAnnotationsChartView)
179
180        dsp, dspcb = OWGUI.doubleSpin(hWidget, self,
181                        "maxPValue", 0.0, 1.0, 0.0001,
182                        label="p-value",
183                        tooltip="Maximum p-value",
184                        callback=self.filterAnnotationsChartView,
185                        callbackOnReturn=True,
186                        checked="useMaxPValFilter",
187                        checkCallback=self.filterAnnotationsChartView)
188
189        dsfdr, dsfdrcb = OWGUI.doubleSpin(hWidget, self,
190                        "maxFDR", 0.0, 1.0, 0.0001,
191                        label="FDR",
192                        tooltip="Maximum False discovery rate",
193                        callback=self.filterAnnotationsChartView,
194                        callbackOnReturn=True,
195                        checked="useMaxFDRFilter",
196                        checkCallback=self.filterAnnotationsChartView)
197
198        from Orange.OrangeWidgets import OWGUIEx
199        self.filterLineEdit = OWGUIEx.QLineEditWithActions(self)
200        self.filterLineEdit.setPlaceholderText("Filter ...")
201        action = QAction(QIcon(os.path.join(orngEnviron.canvasDir,
202                        "icons", "delete_gray.png")), "Clear", self)
203
204        self.filterLineEdit.addAction(action, 0, Qt.AlignHCenter)
205        self.connect(action, SIGNAL("triggered()"), self.filterLineEdit.clear)
206
207        self.filterCompleter = QCompleter(self.filterLineEdit)
208        self.filterCompleter.setCaseSensitivity(Qt.CaseInsensitive)
209        self.filterLineEdit.setCompleter(self.filterCompleter)
210
211        hLayout.addWidget(self.filterLineEdit)
212        self.mainArea.layout().addWidget(hWidget)
213
214        self.connect(self.filterLineEdit, SIGNAL("textChanged(QString)"),
215                     self.filterAnnotationsChartView)
216
217        self.annotationsChartView = MyTreeWidget(self)
218        self.annotationsChartView.setHeaderLabels(["Category", "Term",
219                            "Count", "Reference count", "p-value", "FDR", "Enrichment"])
220        self.annotationsChartView.setAlternatingRowColors(True)
221        self.annotationsChartView.setSortingEnabled(True)
222        self.annotationsChartView.setSelectionMode(QAbstractItemView.ExtendedSelection)
223        self.annotationsChartView.setRootIsDecorated(False)
224        self.annotationsChartView.viewport().setMouseTracking(True)
225#        self.annotationsChartView.viewport().setAttribute(Qt.WA_Hover)
226        self.mainArea.layout().addWidget(self.annotationsChartView)
227
228        contextEventFilter = OWGUI.VisibleHeaderSectionContextEventFilter(self.annotationsChartView)
229        self.annotationsChartView.header().installEventFilter(contextEventFilter)
230
231        self.taxid_list = []
232
233        self.connect(self.groupsWidget, SIGNAL("itemClicked(QTreeWidgetItem *, int)"), self.subsetSelectionChanged)
234
235        OWGUI.button(self.controlArea, self, "Commit", callback=self.commit)
236
237        self.loadedGenematcher = "None"
238        self.referenceData = None
239        self.data = None
240
241        self.treeItems = []
242
243        self.resize(1024, 600)
244
245        self.connect(self, SIGNAL("widgetStateChanged(QString, int, QString)"), self.onStateChange)
246
247        self.updatingAnnotationsFlag = False
248        self.currentAnnotatedCategories = []
249
250        self.setBlocking(True)
251
252        task = EnsureDownloaded(
253            [("Taxonomy", "ncbi_taxonomy.tar.gz"),
254             (obiGeneSets.sfdomain, "index.pck")]
255        )
256        task.finished.connect(self.updateHierarchy)
257
258        self._executor = ThreadExecutor()
259        self._executor.submit(task)
260
261    def updateHierarchy(self):
262        try:
263            all, local = obiGeneSets.list_all(), obiGeneSets.list_local()
264            organisms = set(obiTaxonomy.essential_taxids() + filter(None, [t[1] for t in all]))
265
266            organism_names = map(name_or_none, organisms)
267            organisms = [taxid for taxid, name in zip(organisms, organism_names) \
268                         if name is not None]
269
270            self.taxid_list = list(organisms)
271            self.speciesComboBox.clear()
272            self.speciesComboBox.addItems([obiTaxonomy.name(id) for id in self.taxid_list])
273            self.genesets = all
274        finally:
275            self.setBlocking(False)
276
277    def setData(self, data=None):
278        self.data = data
279        self.error(0)
280        self.closeContext("")
281        self.geneAttrComboBox.clear()
282        self.groupsWidget.clear()
283        self.annotationsChartView.clear()
284
285        if not getattr(self,"taxid_list", None):
286            QTimer.singleShot(100, lambda data=data: self.setData(data))
287            return
288        if data:
289            self.geneAttrs = [attr for attr in data.domain.variables + data.domain.getmetas().values() \
290                              if attr.varType != orange.VarTypes.Continuous]
291
292            self.geneAttrComboBox.addItems([attr.name for attr in self.geneAttrs])
293            self.geneattr = min(self.geneattr, len(self.geneAttrs) - 1)
294
295            taxid = data_hints.get_hint(data, "taxid", "")
296            try:
297                self.speciesIndex = self.taxid_list.index(taxid)
298            except ValueError, ex:
299                pass
300            self.genesinrows = data_hints.get_hint(data, "genesinrows", self.genesinrows)
301
302            self.openContext("", data)
303       
304            self.refreshHierarchy()
305
306            self.loadedGenematcher = "None"
307            self.updateAnnotations()
308
309    def setReference(self, data=None):
310        self.referenceData = data
311        self.referenceRadioBox.setEnabled(bool(data))
312
313    def getHierarchy(self, taxid):
314        def recursive_dict():
315            return defaultdict(recursive_dict)
316        collection = recursive_dict()
317
318        def collect(col, hier):
319            if hier:
320                collect(col[hier[0]], hier[1:])
321
322        for hierarchy, t_id, _ in self.genesets:
323            collect(collection[t_id], hierarchy)
324
325        return (taxid, collection[taxid]), (None, collection[None])
326
327    def setHierarchy(self, hierarchy, hierarchy_noorg):
328        self.groupsWidgetItems = {}
329        def fill(col, parent, full=(), org=""):
330            for key, value in sorted(col.items()):
331                full_cat = full + (key,)
332                item = QTreeWidgetItem(parent, [key])
333                item.setFlags(item.flags() | Qt.ItemIsUserCheckable | Qt.ItemIsSelectable | Qt.ItemIsEnabled)
334                if value:
335                    item.setFlags(item.flags() | Qt.ItemIsTristate)
336
337                item.setData(0, Qt.CheckStateRole, QVariant(self.categoriesCheckState.get((full_cat, org), Qt.Checked)))
338                item.setExpanded(True)
339                item.category = full_cat
340                item.organism = org
341                self.groupsWidgetItems[full_cat] = item
342                fill(value, item, full_cat, org=org)
343
344        self.groupsWidget.clear()
345        fill(hierarchy[1], self.groupsWidget, org=hierarchy[0])
346        fill(hierarchy_noorg[1], self.groupsWidget, org=hierarchy_noorg[0])
347
348#    def updateCategoryCounts(self):
349#        for cat, item in self.groupWidgetItem:
350#            item.setData(1, QVariant(), Qt.DisplayRole)
351
352    def selectedCategories(self):
353        return [(key, org) for (key, org), check in self.getHierarchyCheckState().items() if check == Qt.Checked]
354
355    def getHierarchyCheckState(self):
356        def collect(item, full=()):
357            checked = item.checkState(0)
358            name = str(item.data(0, Qt.DisplayRole).toString())
359            full_cat = full + (name,)
360            result = [((full_cat, item.organism), checked)]
361            for i in range(item.childCount()):
362                result.extend(collect(item.child(i), full_cat))
363            return result
364
365        items = [self.groupsWidget.topLevelItem(i) for i in range(self.groupsWidget.topLevelItemCount())]
366        states = reduce(list.__add__, [collect(item) for item in items], [])
367        return dict(states)
368
369    def subsetSelectionChanged(self, item, column):
370        self.categoriesCheckState = self.getHierarchyCheckState()
371        categories = self.selectedCategories()
372
373        if self.data is not None:
374            if not set(categories) <= set(self.currentAnnotatedCategories):
375                self.updateAnnotations()
376            else:
377                self.filterAnnotationsChartView()
378
379    def updateGeneMatcherSettings(self):
380        from .OWGOEnrichmentAnalysis import GeneMatcherDialog
381        dialog = GeneMatcherDialog(self, defaults=self.geneMatcherSettings, enabled=[True] * 4, modal=True)
382        if dialog.exec_():
383            self.geneMatcherSettings = [getattr(dialog, item[0]) for item in dialog.items]
384            self.loadedGenematcher = "None"
385            if self.data:
386                self.updateAnnotations()
387
388    def updateGenematcher(self):
389        taxid = self.taxid_list[self.speciesIndex]
390        if taxid != self.loadedGenematcher:
391            self.progressBarInit()
392            call = self.asyncCall(obiGene.matcher, name="Gene Matcher", blocking=True, thread=self.thread())
393            call.connect(call, SIGNAL("progressChanged(float)"), self.progressBarSet)
394            with orngServerFiles.DownloadProgress.setredirect(call.emitProgressChanged):
395#            with orngServerFiles.DownloadProgress.setredirect(self.progressBarSet):
396                matchers = [obiGene.GMGO, obiGene.GMKEGG, obiGene.GMNCBI, obiGene.GMAffy]
397                if any(self.geneMatcherSettings):
398                    call.__call__([gm(taxid) for gm, use in zip(matchers, self.geneMatcherSettings) if use])
399                    self.genematcher = call.get_result()
400#                    self.genematcher = obiGene.matcher([gm(taxid) for gm, use in zip(matchers, self.geneMatcherSettings) if use])
401                else:
402                    self.genematcher = obiGene.GMDirect()
403#                self.genematcher.set_targets(self.referenceGenes())
404                self.loadedGenematcher = taxid
405            self.progressBarFinished()
406
407    def genesFromExampleTable(self, table):
408        if self.genesinrows:
409            genes = [attr.name for attr in table.domain.attributes]
410        else:
411            geneattr = self.geneAttrs[self.geneattr]
412            genes = [str(ex[geneattr]) for ex in table]
413        return genes
414
415    def clusterGenes(self):
416        return self.genesFromExampleTable(self.data)
417
418    def referenceGenes(self):
419        if self.referenceData and self.useReferenceData:
420            return self.genesFromExampleTable(self.referenceData)
421        else:
422            taxid = self.taxid_list[self.speciesIndex]
423            call = self.asyncCall(obiGene.NCBIGeneInfo, (taxid,), name="Load reference genes", blocking=True, thread=self.thread())
424            call.connect(call, SIGNAL("progressChanged(float)"), self.progressBarSet)
425            with orngServerFiles.DownloadProgress.setredirect(call.emitProgressChanged):
426                call.__call__()
427                return call.get_result()
428
429    def _cached_name_lookup(self, func, cache):
430        def f(name, cache=cache):
431            if name not in cache:
432                cache[name] = func(name)
433            return cache[name]
434        return f
435
436    def mapGeneNames(self, names, cache=None, passUnknown=False):
437        if cache is not None:
438            umatch = self._cached_name_lookup(self.genematcher.umatch, cache)
439        else:
440            umatch = self.genematcher.umatch
441        if passUnknown:
442            return [umatch(name) or name for name in names]
443#            return [(mapped_name or name, mapped_name is not None) for mapped_name, name in zip(mapped, names)]
444        return [n for n in [umatch(name) for name in names] if n is not None]
445
446    def enrichment(self, geneset, cluster, reference, pval=obiProb.Hypergeometric(), cache=None):
447        genes = set(self.mapGeneNames(geneset.genes, cache, passUnknown=False))
448
449        cmapped = genes.intersection(cluster)
450        rmapped = genes.intersection(reference)
451        return (cmapped, rmapped, pval.p_value(len(cmapped), len(reference), len(rmapped), len(cluster)), float(len(cmapped)) / (len(cluster) or 1) / (float(len(rmapped) or 1) / (len(reference) or 1))) # TODO: compute all statistics here
452
453    def updateAnnotations(self):
454        self.updatingAnnotationsFlag = True
455        self.annotationsChartView.clear()
456        self.error([0, 1])
457        if not self.genesinrows and len(self.geneAttrs) == 0:
458            self.error(0, "Input data contains no attributes with gene names")
459            return
460
461        self.updateGenematcher()
462
463        self.progressBarInit()
464        self.currentAnnotatedCategories = categories = self.selectedCategories()
465
466        ## Load collections in a worker thread
467        call = self.asyncCall(obiGeneSets.collections, categories, name="Loading collections", blocking=True, thread=self.thread())
468        call.connect(call, SIGNAL("progressChanged(float)"), self.progressBarSet)
469        with orngServerFiles.DownloadProgress.setredirect(call.emitProgressChanged):
470            call.__call__()
471            collections = list(call.get_result())
472
473#        with orngServerFiles.DownloadProgress.setredirect(self.progressBarSet):
474#            collections = list(obiGeneSets.collections(*categories))
475        clusterGenes, referenceGenes = self.clusterGenes(), self.referenceGenes()
476        cache = {}
477
478        self.genematcher.set_targets(referenceGenes)
479
480        countAll = len(set(clusterGenes))
481        infoText = "%i unique gene names on input\n" % countAll
482        referenceGenes = set(self.mapGeneNames(referenceGenes, cache, passUnknown=False))
483        self.progressBarSet(1)
484        clusterGenes = set(self.mapGeneNames(clusterGenes, cache, passUnknown=False))
485        self.progressBarSet(2)
486        infoText += "%i (%.1f) gene names matched" % (len(clusterGenes), 100.0 * len(clusterGenes) / countAll)
487        self.infoBox.setText(infoText)
488
489        results = []
490        from Orange.orng.orngMisc import progressBarMilestones
491
492        milestones = progressBarMilestones(len(collections), 100)
493        for i, geneset in enumerate(collections):
494            results.append((geneset, self.enrichment(geneset, clusterGenes, referenceGenes, cache=cache)))
495            if i in milestones:
496                self.progressBarSet(100.0 * i / len(collections))
497
498        self.annotationsChartView.clear()
499
500        maxCount = max([len(cm) for _, (cm, _, _, _) in results] + [1])
501        maxRefCount = max([len(rc) for _, (_, rc, _, _) in results] + [1])
502        countSpaces = int(math.ceil(math.log10(maxCount)))
503        refSpaces = int(math.ceil(math.log(maxRefCount)))
504        countFmt = "%"+str(countSpaces) + "s  (%.2f%%)"
505        refFmt = "%"+str(refSpaces) + "s  (%.2f%%)"
506
507        self.filterCompleter.setModel(None)
508        linkFont = QFont(self.annotationsChartView.viewOptions().font)
509        linkFont.setUnderline(True)
510        self.treeItems = []
511        for i, (geneset, (cmapped, rmapped, p_val, enrichment)) in enumerate(results):
512            if len(cmapped) > 0:
513                item = MyTreeWidgetItem(self.annotationsChartView, [" ".join(geneset.hierarchy), gsname(geneset)])
514                item.setData(2, Qt.DisplayRole, QVariant(countFmt % (len(cmapped), 100.0*len(cmapped)/countAll)))
515                item.setData(2, Qt.ToolTipRole, QVariant(len(cmapped))) # For filtering
516                item.setData(3, Qt.DisplayRole, QVariant(refFmt % (len(rmapped), 100.0*len(rmapped)/len(referenceGenes))))
517                item.setData(4, Qt.ToolTipRole, QVariant(fmtpdet(p_val)))
518                item.setData(4, Qt.DisplayRole, QVariant(fmtp(p_val)))
519                item.setData(4, 42, QVariant(p_val))
520                #column 5, FDR, is computed in filterAnnotationsChartView
521                item.setData(6, Qt.DisplayRole, QVariant(enrichment))
522                item.setData(6, Qt.ToolTipRole, QVariant("%.3f" % enrichment))
523                item.geneset= geneset
524                self.treeItems.append(item)
525                if geneset.link:
526                    item.setData(1, LinkRole, QVariant(geneset.link))
527                    item.setToolTip(1, geneset.link)
528                    item.setFont(1, linkFont)
529                    item.setForeground(1, QColor(Qt.blue))
530
531        if not self.treeItems:
532            self.warning(0, "No enriched sets found.")
533        else:
534            self.warning(0)
535
536        allnames = set(as_unicode(gsname(geneset))
537                       for geneset, (count, _, _, _) in results if count)
538
539        allnames |= reduce(operator.ior,
540                           (set(word_split(name)) for name in allnames),
541                           set())
542
543        self.completerModel = QStringListModel(sorted(allnames))
544        self.filterCompleter.setModel(self.completerModel)
545
546        self.annotationsChartView.setItemDelegateForColumn(6, BarItemDelegate(self, scale=(0.0, max(t[1][3] for t in results))))
547        self.annotationsChartView.setItemDelegateForColumn(1, LinkStyledItemDelegate(self.annotationsChartView))
548
549        for i in range(self.annotationsChartView.columnCount()):
550            self.annotationsChartView.resizeColumnToContents(i)
551
552        self.annotationsChartView.setColumnWidth(1, min(self.annotationsChartView.columnWidth(1), 300))
553        self.progressBarFinished()
554        QTimer.singleShot(10, self.filterAnnotationsChartView)
555        self.updatingAnnotationsFlag = False
556
557    def filterAnnotationsChartView(self, filterString=""):
558        if self.updatingAnnotationsFlag:
559            return
560        categories = set(" ".join(cat) for cat, taxid in self.selectedCategories())
561
562   
563        filterString = str(self.filterLineEdit.text()).lower()
564
565        #hide categories
566        itemsHiddenCat = []
567        for item in self.treeItems:
568            item_cat = str(item.data(0, Qt.EditRole).toString())
569            geneset = gsname(item.geneset).lower()
570            hidden = item_cat not in categories
571            itemsHiddenCat.append(hidden)
572       
573        #compute FDR according the selected categories
574        pvals = [ _toPyObject(item.data(4, 42)) for item, hidden in zip(self.treeItems, itemsHiddenCat) if not hidden ]
575        fdrs = obiProb.FDR(pvals)
576
577        #update FDR for the selected collections and apply filtering rules
578        fdri = 0
579        itemsHidden = []
580        for item, hidden in zip(self.treeItems, itemsHiddenCat):
581            if not hidden:
582                fdr = fdrs[fdri]
583                fdri += 1
584
585                count, pval = _toPyObject(item.data(2, Qt.ToolTipRole)), _toPyObject(item.data(4, 42))
586
587                hidden = (self.useMinCountFilter and count < self.minClusterCount) or \
588                         (self.useMaxPValFilter and pval > self.maxPValue) or \
589                         (self.useMaxFDRFilter and fdr > self.maxFDR)
590               
591                if not hidden:
592                    item.setData(5, Qt.ToolTipRole, QVariant(fmtpdet(fdr)))
593                    item.setData(5, Qt.DisplayRole, QVariant(fmtp(fdr)))
594                    item.setData(5, 42, QVariant(fdr))
595
596            item.setHidden(hidden)
597            itemsHidden.append(hidden)
598               
599
600        if self.treeItems and all(itemsHidden):
601            self.information(0, "All sets were filtered out.")
602        else:
603            self.information(0)
604
605
606    def commit(self):
607        selected = self.annotationsChartView.selectedItems()
608        genesets = [item.geneset for item in selected]
609        cache = {}
610        mappedNames = set(self.mapGeneNames(reduce(set.union, [geneset.genes for geneset in genesets], set()), cache))
611        if self.genesinrows:
612            mapped = [attr for attr in self.data.domain.attributes if self.genematcher.umatch(attr.name) in mappedNames]
613            newdomain = orange.Domain(mapped, self.data.domain.classVar)
614            newdomain.addmetas(self.data.domain.getmetas())
615            data = orange.ExampleTable(newdomain, self.data)
616        else:
617            geneattr = self.geneAttrs[self.geneattr]
618            selected = [1 if self.genematcher.umatch(str(ex[geneattr])) in mappedNames else 0 for ex in self.data]
619            data = self.data.select(selected)
620
621#            if self.appendAnnotations:
622#                meta = orange.StringVariable("Annotations")
623#                data.domain.addmeta(orange.newmetaid(), meta)
624#                for ex in data:
625#                    geneattr = self.geneAttrs[self.geneattr]
626#                    gene = str(ex[geneattr])
627#                    annotations = getgene
628
629        self.send("Selected Examples", data)
630
631    def sendReport(self):
632        self.reportSettings("Settings", [("Organism", obiTaxonomy.name(self.taxid_list[self.speciesIndex]))])
633        self.reportSettings("Filter", [("Min cluster size", self.minClusterCount if self.useMinCountFilter else 0),
634                                       ("Max p-value", self.maxPValue if self.useMaxPValFilter else 1.0),
635                                       ("Max FDR", self.maxFDR if self.useMaxFDRFilter else 1.0)])
636
637        self.reportSubsection("Annotations")
638        self.reportRaw(reportItemView(self.annotationsChartView))
639
640    def onStateChange(self, stateType, id, text):
641        if stateType == "Warning" or stateType == "Info":
642            self.annotationsChartView._userMessage = text
643            self.annotationsChartView.viewport().update()
644
645
646def reportItemView(view):
647    model = view.model()
648    return reportItemModel(view, model)
649
650def reportItemModel(view, model, index=QModelIndex()):
651    if not index.isValid() or model.hasChildren(index):
652        columnCount, rowCount = model.columnCount(index), model.rowCount(index)
653        if not index.isValid():
654            text = '<table>\n<tr>' + ''.join('<th>%s</th>' % model.headerData(i, Qt.Horizontal, Qt.DisplayRole).toString() for i in range(columnCount)) +'</tr>\n'
655        else:
656#            variant = model.data(index, Qt.DisplayRole)
657#            text = '<table' + (' caption="%s"' % variant.toString() if variant.isValid() else '') + '>\n'
658            pass
659        text += ''.join('<tr>' + ''.join('<td>' + reportItemModel(view, model, model.index(row, column, index)) + '</td>' for column in range(columnCount)) + '</tr>\n' for row in range(rowCount) if not view.isRowHidden(row, index))
660        text += '</table>'
661        return text
662    else:
663        variant = model.data(index, Qt.DisplayRole)
664        return str(variant.toString()) if variant.isValid() else ""
665
666
667
668if __name__ == "__main__": 
669    import cProfile
670
671    app = QApplication(sys.argv)
672    w = OWSetEnrichment()
673    data = orange.ExampleTable("yeast-class-RPR.tab")
674    #data = orange.ExampleTable("/home/marko/Downloads/tmp.tab")
675#    data = orange.ExampleTable("../human")
676#    print cProfile.runctx("w.setData(data)", globals(), locals())
677    w.setData(data)
678    w.show()
679    app.exec_()
680    w.saveSettings()
681
682
Note: See TracBrowser for help on using the repository browser.