source: orange-bioinformatics/_bioinformatics/widgets/OWSetEnrichment.py @ 1872:5518b82614e3

Revision 1872:5518b82614e3, 28.1 KB checked in by markotoplak, 6 months ago (diff)

OWSetEnrichment: both p-value and FDR are shown. FDR is recomputed when selected gene sets are changed.

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