source: orange-bioinformatics/_bioinformatics/widgets/OWKEGGPathwayBrowser.py @ 1740:0ef1058694a2

Revision 1740:0ef1058694a2, 33.4 KB checked in by Ales Erjavec <ales.erjavec@…>, 12 months ago (diff)

Try guessing the gene attribute name.

Line 
1"""
2<name>KEGG Pathways</name>
3<description>Browse KEGG pathways that include an input set of genes.</description>
4<priority>2030</priority>
5<icon>icons/KEGGPathways.svg</icon>
6"""
7
8from __future__ import absolute_import, with_statement
9
10import sys
11import gc
12import webbrowser
13import threading
14from functools import partial
15from collections import defaultdict
16from operator import add, itemgetter
17
18from PyQt4.QtGui import (
19    QTreeWidget, QTreeWidgetItem, QItemSelectionModel, QSplitter,
20    QAction, QMenu, QGraphicsView, QGraphicsScene, QFont,
21    QBrush, QColor, QPen, QTransform, QPainter, QPainterPath,
22    QGraphicsItem, QGraphicsPathItem, QGraphicsPixmapItem, QPixmap
23)
24
25from PyQt4.QtCore import (
26    Qt, QObject, QMetaObject, QTimer, Q_ARG, QRectF, SIGNAL
27)
28
29import Orange
30
31from Orange.orng import orngMisc, orngServerFiles
32from Orange.orng.orngDataCaching import data_hints
33
34from Orange.OrangeWidgets import OWGUI
35from Orange.OrangeWidgets.OWWidget import *
36
37from .. import obiTaxonomy
38from .. import obiKEGG
39from .. import obiGeneSets
40
41USE_THREADING = True
42
43if USE_THREADING:
44
45    def threading_queued_invoke(qobj, func):
46        def safe_wrapper(*args, **kwargs):
47            QMetaObject.invokeMethod(qobj, "queuedInvoke",
48                                     Qt.QueuedConnection,
49                                     Q_ARG("PyQt_PyObject",
50                                           partial(func, *args, **kwargs)))
51        return safe_wrapper
52
53
54def split_and_strip(string, sep=None):
55    return [s.strip() for s in string.split(sep)]
56
57
58def path_from_graphics(graphics):
59    """
60    Return a constructed `QPainterPath` for a KEGG pathway graphics
61    element.
62
63    """
64    path = QPainterPath()
65    x, y, w, h = [int(graphics.get(c, 0)) for c in
66                  ["x", "y", "width", "height"]]
67    type = graphics.get("type", "rectangle")
68    if type == "rectangle":
69        path.addRect(QRectF(x - w / 2, y - h / 2, w, h))
70    elif type == "roundrectangle":
71        path.addRoundedRect(QRectF(x - w / 2, y - h / 2, w, h), 10, 10)
72    elif type == "circle":
73        path.addEllipse(QRectF(x - w / 2, y - h / 2, w, h))
74    else:
75        ValueError("Unknown graphcis type %r." % type)
76    return path
77
78
79class EntryGraphicsItem(QGraphicsPathItem):
80    """
81    An Graphics Item with actions for an overlay of a KEGG pathway image.
82    """
83    def __init__(self, graphics, *args):
84        QGraphicsPathItem.__init__(self, *args)
85        path = path_from_graphics(graphics)
86        self.setPath(path)
87        self.setAcceptHoverEvents(True)
88        self._actions = []
89        self.link = None
90
91    def hoverEnterEvent(self, event):
92        self.setBrush(QBrush(QColor(0, 100, 0, 100)))
93
94    def hoverLeaveEvent(self, event):
95        self.setBrush(QBrush(Qt.NoBrush))
96
97    def contextMenuEvent(self, event):
98        if self._actions:
99            self._menu = menu = QMenu()
100            for action in self._actions:
101                menu.addAction(action)
102            menu.popup(event.screenPos())
103
104    def itemChange(self, change, value):
105        if change == QGraphicsItem.ItemSelectedHasChanged:
106            self.setPen(QPen(Qt.red if self.isSelected() else Qt.blue, 2))
107
108        return QGraphicsPathItem.itemChange(self, change, value)
109
110
111class GraphicsPathwayItem(QGraphicsPixmapItem):
112    """
113    A Graphics Item displaying a KEGG Pathway image with optional
114    marked objects.
115
116    """
117    def __init__(self, pathway, objects, *args, **kwargs):
118        QGraphicsPixmapItem.__init__(self, *args)
119        self.setTransformationMode(Qt.SmoothTransformation)
120        self.setPathway(pathway)
121        self.setMarkedObjects(objects,
122                              name_mapper=kwargs.get("name_mapper", {}))
123
124    def setPathway(self, pathway):
125        """
126        Set pathway
127        """
128        self.pathway = pathway
129        if pathway:
130            image_filename = pathway.get_image()
131            self._pixmap = QPixmap(image_filename)
132        else:
133            self._pixmap = QPixmap()
134        self.setPixmap(self._pixmap)
135
136    def setMarkedObjects(self, objects, name_mapper={}):
137        for entry in self.pathway.entries() if self.pathway else []:
138            if entry.type == "group":
139                continue
140            graphics = entry.graphics
141            contained_objects = [obj for obj in objects if obj in entry.name]
142            item = EntryGraphicsItem(graphics, self, self.scene())
143            item.setToolTip(self.tooltip(entry, contained_objects,
144                                         name_mapper))
145            item._actions = self.actions(entry, contained_objects)
146            item.marked_objects = contained_objects
147            if contained_objects:
148                item.setPen(QPen(Qt.blue, 2))
149                item.setFlag(QGraphicsItem.ItemIsSelectable, True)
150
151    def actions(self, entry, marked_objects=[]):
152        actions = []
153        type = entry.type
154        if marked_objects:
155            action = QAction("View genes on kegg website", None)
156            org = set([s.split(":")[0] for s in marked_objects]).pop()
157            genes = [s.split(":")[-1] for s in marked_objects]
158            address = ("http://www.genome.jp/dbget-bin/www_bget?" +
159                       "+".join([org] + genes))
160            action.connect(action,
161                           SIGNAL("triggered()"),
162                           lambda toggled=False, address=address:
163                               webbrowser.open(address))
164            actions.append(action)
165        elif hasattr(entry, "link"):
166            action = QAction("View %s on KEGG website" % str(type), None)
167            action.connect(action,
168                           SIGNAL("triggered()"),
169                           lambda toggled=False, address=entry.link: \
170                               webbrowser.open(address))
171            actions.append(action)
172        return actions
173
174    def tooltip(self, entry, objects, name_mapper={}):
175        names = [obj for obj in objects if obj in entry.name]
176        names = [name_mapper.get(name, name) for name in names]
177        text = entry.name[:16] + " ..." if len(entry.name) > 20 else entry.name
178        text = "<p>%s</p>" % text
179        if names:
180            text += "<br>".join(names)
181        return text
182
183    def contextMenuEvent(self, event):
184        self._menu = menu = QMenu()
185        action = menu.addAction("View this pathway on KEGG website")
186        address = ("http://www.kegg.jp/kegg-bin/show_pathway?%s%s" %
187                   (self.pathway.org, self.pathway.number))
188        action.connect(action, SIGNAL("triggered()"),
189                       lambda: webbrowser.open(address))
190        menu.popup(event.screenPos())
191
192
193class PathwayView(QGraphicsView):
194    def __init__(self, master, *args):
195        QGraphicsView.__init__(self, *args)
196        self.master = master
197
198        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
199        self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
200
201        self.setRenderHints(QPainter.Antialiasing)
202        scene = QGraphicsScene(self)
203        self.pixmapGraphicsItem = QGraphicsPixmapItem(None, scene)
204        self.setScene(scene)
205
206        self.setMouseTracking(True)
207        self.viewport().setMouseTracking(True)
208
209        self.setFocusPolicy(Qt.WheelFocus)
210
211    def SetPathway(self, pathway=None, objects=[]):
212        self.scene().clear()
213        self.pathway = pathway
214        self.objects = objects
215        self.pathwayItem = GraphicsPathwayItem(
216            pathway, objects, None,
217            name_mapper=getattr(self.master, "uniqueGenesDict", {})
218        )
219
220        self.scene().addItem(self.pathwayItem)
221        self.scene().setSceneRect(self.pathwayItem.boundingRect())
222        self.updateTransform()
223
224    def resizeEvent(self, event):
225        self.updateTransform()
226        return QGraphicsView.resizeEvent(self, event)
227
228    def updateTransform(self):
229        if self.master.autoResize:
230            self.fitInView(self.scene().sceneRect().adjusted(-1, -1, 1, 1),
231                           Qt.KeepAspectRatio)
232        else:
233            self.setTransform(QTransform())
234
235    def paintEvent(self, event):
236        QGraphicsView.paintEvent(self, event)
237        if getattr(self, "_userMessage", None):
238            painter = QPainter(self.viewport())
239            font = QFont(self.font())
240            font.setPointSize(15)
241            painter.setFont(font)
242            painter.drawText(self.viewport().geometry(), Qt.AlignCenter,
243                             self._userMessage)
244            painter.end()
245
246
247class OWKEGGPathwayBrowser(OWWidget):
248    settingsList = ["organismIndex", "geneAttrIndex", "autoCommit",
249                    "autoResize", "useReference", "useAttrNames",
250                    "caseSensitive", "showOrthology"]
251
252    contextHandlers = {
253        "": DomainContextHandler(
254            "",
255            [ContextField("organismIndex",
256                          DomainContextHandler.Required +
257                          DomainContextHandler.IncludeMetaAttributes),
258             ContextField("geneAttrIndex",
259                          DomainContextHandler.Required +
260                          DomainContextHandler.IncludeMetaAttributes),
261             ContextField("useAttrNames",
262                          DomainContextHandler.Required +
263                          DomainContextHandler.IncludeMetaAttributes)]
264        )
265    }
266
267    def __init__(self, parent=None, signalManager=None, name="KEGG Pathways"):
268        OWWidget.__init__(self, parent, signalManager, name, wantGraph=True)
269        self.inputs = [("Examples", Orange.data.Table, self.SetData),
270                       ("Reference", Orange.data.Table, self.SetRefData)]
271        self.outputs = [("Selected Examples", Orange.data.Table),
272                        ("Unselected Examples", Orange.data.Table)]
273        self.organismIndex = 0
274        self.geneAttrIndex = 0
275        self.autoCommit = False
276        self.autoResize = True
277        self.useReference = False
278        self.useAttrNames = 0
279        self.caseSensitive = True
280        self.showOrthology = True
281        self.autoFindBestOrg = False
282        self.loadSettings()
283
284        self.controlArea.setMaximumWidth(250)
285        box = OWGUI.widgetBox(self.controlArea, "Info")
286        self.infoLabel = OWGUI.widgetLabel(box, "No data on input\n")
287
288        self.allOrganismCodes = {}
289
290        self.organismCodes = []
291
292        # Organism selection.
293        box = OWGUI.widgetBox(self.controlArea, "Organism")
294        self.organismComboBox = OWGUI.comboBox(
295            box, self, "organismIndex",
296            items=[],
297            callback=self.OrganismSelectionCallback,
298            addSpace=True,
299            debuggingEnabled=0,
300            tooltip="Select the organism of the input genes")
301
302        self.signalManager.freeze(self).push()
303
304        # Selection of genes attribute
305        box = OWGUI.widgetBox(self.controlArea, "Gene attribute")
306        self.geneAttrCombo = OWGUI.comboBox(box, self, "geneAttrIndex",
307                                            callback=self.Update)
308
309        OWGUI.checkBox(box, self, "useAttrNames",
310                       "Use variable names",
311                       disables=[(-1, self.geneAttrCombo)],
312                       callback=self.UseAttrNamesCallback)
313
314        self.geneAttrCombo.setDisabled(bool(self.useAttrNames))
315
316        OWGUI.separator(self.controlArea)
317
318        OWGUI.checkBox(self.controlArea, self, "useReference",
319                       "From signal",
320                       box="Reference",
321                       callback=self.Update)
322
323        OWGUI.separator(self.controlArea)
324
325        OWGUI.checkBox(self.controlArea, self, "showOrthology",
326                       "Show pathways in full orthology",
327                       box="Orthology",
328                       callback=self.UpdateListView)
329
330        OWGUI.checkBox(self.controlArea, self, "autoResize",
331                       "Resize to fit",
332                       box="Image",
333                       callback=self.UpdatePathwayViewTransform)
334
335        box = OWGUI.widgetBox(self.controlArea, "Cache Control")
336
337        OWGUI.button(box, self, "Clear cache",
338                     callback=self.ClearCache,
339                     tooltip="Clear all locally cached KEGG data.")
340
341        OWGUI.separator(self.controlArea)
342
343        box = OWGUI.widgetBox(self.controlArea, "Selection")
344        OWGUI.checkBox(box, self, "autoCommit", "Commit on update")
345        OWGUI.button(box, self, "Commit", callback=self.Commit, default=True)
346        OWGUI.rubber(self.controlArea)
347
348        spliter = QSplitter(Qt.Vertical, self.mainArea)
349        self.pathwayView = PathwayView(self, spliter)
350        self.mainArea.layout().addWidget(spliter)
351
352        self.listView = QTreeWidget(spliter)
353        spliter.addWidget(self.listView)
354
355        self.listView.setAllColumnsShowFocus(1)
356        self.listView.setColumnCount(4)
357        self.listView.setHeaderLabels(["Pathway", "P value",
358                                       "Genes", "Reference"])
359
360        self.listView.setSelectionMode(QTreeWidget.SingleSelection)
361
362        self.listView.setSortingEnabled(True)
363
364        self.listView.setMaximumHeight(200)
365
366        self.connect(self.listView,
367                     SIGNAL("itemSelectionChanged()"),
368                     self.UpdatePathwayView)
369
370        self.connect(self.graphButton,
371                     SIGNAL("clicked()"),
372                     self.saveGraph)
373
374        self.ctrlPressed = False
375        self.selectedObjects = defaultdict(list)
376        self.data = None
377        self.refData = None
378
379        self.resize(800, 600)
380
381        self.connect(self,
382                     SIGNAL("widgetStateChanged(QString, int, QString)"),
383                     self.onStateChange)
384
385        self.has_new_data = False
386        self.has_new_reference_set = False
387
388        self.setEnabled(False)
389        QTimer.singleShot(100, self.UpdateOrganismComboBox)
390
391    def UpdateOrganismComboBox(self):
392        # First try to import slumber
393        try:
394            import slumber
395        except ImportError:
396            QMessageBox.warning(self,
397                "'slumber' library required.",
398                '<p>Please install '
399                '<a href="http://pypi.python.org/pypi/slumber">slumber</a> '
400                'library to use KEGG Pathways widget.</p>'
401            )
402
403        try:
404            genome = obiKEGG.KEGGGenome()
405
406            self.allOrganismCodes = genome
407
408            essential = genome.essential_organisms()
409            common = genome.common_organisms()
410            common = [c for c in common if c not in essential]
411
412            # TODO: Add option to specify additional organisms not
413            # in the common list.
414
415            self.infoLabel.setText("Fetching organism definitions\n")
416
417            keys = map(genome.org_code_to_entry_key, essential + common)
418
419            self.progressBarInit()
420            genome.pre_cache(keys, progress_callback=self.progressBarSet)
421            self.progressBarFinished()
422
423            codes = []
424            for code, key in zip(essential + common, keys):
425                codes.append((code, genome[key].definition))
426
427            items = [desc for code, desc in codes]
428
429            self.organismCodes = [code for code, desc in codes]
430            self.organismComboBox.addItems(items)
431        finally:
432            self.setEnabled(True)
433            self.infoLabel.setText("No data on input\n")
434            self.signalManager.freeze(self).pop()
435
436    def Clear(self):
437        """
438        Clear the widget state.
439        """
440        self.infoLabel.setText("No data on input\n")
441        self.listView.clear()
442        self.ClearPathway()
443
444        self.send("Selected Examples", None)
445        self.send("Unselected Examples", None)
446
447    def ClearPathway(self):
448        self.pathwayView.SetPathway(None)
449        self.selectedObjects = defaultdict(list)
450
451    def SetData(self, data=None):
452        self.closeContext()
453        self.data = data
454        self.warning(0)
455        if data is not None:
456            self.SetGeneAttrCombo()
457            taxid = data_hints.get_hint(data, "taxid", None)
458            if taxid:
459                try:
460                    code = obiKEGG.from_taxid(taxid)
461                    self.organismIndex = self.organismCodes.index(code)
462                except Exception, ex:
463                    print ex, taxid
464
465            self.useAttrNames = data_hints.get_hint(data, "genesinrows",
466                                                    self.useAttrNames)
467
468            self.openContext("", data)
469        else:
470            self.Clear()
471
472        self.has_new_data = True
473
474    def SetRefData(self, data=None):
475        self.refData = data
476        self.has_new_reference_set = True
477
478    def handleNewSignals(self):
479        if self.has_new_data or (self.has_new_reference_set and \
480                                 self.useReference):
481            self.Update()
482
483            self.has_new_data = False
484            self.has_new_reference_set = False
485
486    def UseAttrNamesCallback(self):
487        self.Update()
488
489    def OrganismSelectionCallback(self):
490        self.Update()
491
492    def SetGeneAttrCombo(self):
493        self.geneAttrCandidates = self.data.domain.variables + \
494                                  self.data.domain.getmetas().values()
495        self.geneAttrCandidates = filter(
496            lambda v: isinstance(v, (Orange.feature.Discrete,
497                                     Orange.feature.String)),
498            self.geneAttrCandidates)
499
500        self.geneAttrCombo.clear()
501
502        self.geneAttrCombo.addItems([var.name for var in
503                                     self.geneAttrCandidates])
504
505        names_lower = [v.name.lower() for v in self.geneAttrCandidates]
506
507        scores = [(name == "gene", "gene" in name)
508                  for name in names_lower]
509        imax, _ = max(enumerate(scores), key=itemgetter(1))
510        self.geneAttrIndex = imax
511
512    def UpdateListView(self):
513        self.bestPValueItem = None
514        self.listView.clear()
515        if not self.data:
516            return
517
518        allPathways = self.org.pathways()
519        allRefPathways = obiKEGG.pathways("map")
520
521        items = []
522        self.progressBarInit()
523        kegg_pathways = obiKEGG.KEGGPathways()
524        kegg_pathways.pre_cache(self.pathways.keys(),
525                                progress_callback=self.progressBarSet)
526        self.progressBarFinished()
527
528        org_code = self.organismCodes[min(self.organismIndex,
529                                          len(self.organismCodes) - 1)]
530
531        if self.showOrthology:
532            self.koOrthology = obiKEGG.KEGGBrite("ko00001")
533            self.listView.setRootIsDecorated(True)
534            path_ids = set([s[-5:] for s in self.pathways.keys()])
535
536            def _walkCollect(koEntry):
537                num = koEntry.title[:5] if koEntry.title else None
538                if num in path_ids:
539                    return ([koEntry] +
540                            reduce(lambda li, c: li + _walkCollect(c),
541                                   [child for child in koEntry.entries],
542                                   []))
543                else:
544                    c = reduce(lambda li, c: li + _walkCollect(c),
545                               [child for child in koEntry.entries],
546                               [])
547                    return c + (c and [koEntry] or [])
548
549            allClasses = reduce(lambda li1, li2: li1 + li2,
550                                [_walkCollect(c) for c in self.koOrthology],
551                                [])
552
553            def _walkCreate(koEntry, lvItem):
554                item = QTreeWidgetItem(lvItem)
555                id = "path:" + org_code + koEntry.title[:5]
556
557                if koEntry.title[:5] in path_ids:
558                    p = kegg_pathways.get_entry(id)
559                    if p is None:
560                        # In case the genesets still have obsolete entries
561                        name = koEntry.title
562                    else:
563                        name = p.name
564                    genes, p_value, ref = self.pathways[id]
565                    item.setText(0, name)
566                    item.setText(1, "%.5f" % p_value)
567                    item.setText(2, "%i of %i" % (len(genes), len(self.genes)))
568                    item.setText(3, "%i of %i" % (ref, len(self.referenceGenes)))
569                    item.pathway_id = id if p is not None else None
570                else:
571                    if id in allPathways:
572                        text = kegg_pathways.get_entry(id).name
573                    else:
574                        text = koEntry.title
575                    item.setText(0, text)
576
577                    if id in allPathways:
578                        item.pathway_id = id
579                    elif "path:map" + koEntry.title[:5] in allRefPathways:
580                        item.pathway_id = "path:map" + koEntry.title[:5]
581                    else:
582                        item.pathway_id = None
583
584                for child in koEntry.entries:
585                    if child in allClasses:
586                        _walkCreate(child, item)
587
588            for koEntry in self.koOrthology:
589                if koEntry in allClasses:
590                    _walkCreate(koEntry, self.listView)
591
592            self.listView.update()
593        else:
594            self.listView.setRootIsDecorated(False)
595            pathways = self.pathways.items()
596            pathways.sort(lambda a, b: cmp(a[1][1], b[1][1]))
597
598            for id, (genes, p_value, ref) in pathways:
599                item = QTreeWidgetItem(self.listView)
600                item.setText(0, kegg_pathways.get_entry(id).name)
601                item.setText(1, "%.5f" % p_value)
602                item.setText(2, "%i of %i" % (len(genes), len(self.genes)))
603                item.setText(3, "%i of %i" % (ref, len(self.referenceGenes)))
604                item.pathway_id = id
605                items.append(item)
606
607        self.bestPValueItem = items and items[0] or None
608        self.listView.expandAll()
609        for i in range(4):
610            self.listView.resizeColumnToContents(i)
611
612        if self.bestPValueItem:
613            index = self.listView.indexFromItem(self.bestPValueItem)
614            self.listView.selectionModel().select(
615                index, QItemSelectionModel.ClearAndSelect
616            )
617
618    def UpdatePathwayView(self):
619        items = self.listView.selectedItems()
620
621        if len(items) > 0:
622            item = items[0]
623        else:
624            item = None
625
626        self.selectedObjects = defaultdict(list)
627        self.Commit()
628        item = item or self.bestPValueItem
629        if not item or not item.pathway_id:
630            self.pathwayView.SetPathway(None)
631            return
632
633        if USE_THREADING:
634            result = {}
635
636            def call(pathway_id):
637                result["pathway"] = p = obiKEGG.KEGGPathway(pathway_id)
638                p._get_kgml()  # makes sure the kgml file is downloaded
639                p._get_image_filename()  # makes sure the image is downloaded
640
641            self.setEnabled(False)
642            try:
643                thread = threading.Thread(None, call,
644                                          name="get_kgml_and_image",
645                                          args=(item.pathway_id,))
646                thread.start()
647                while thread.is_alive():
648                    thread.join(timeout=0.025)
649                    qApp.processEvents()
650            finally:
651                self.setEnabled(True)
652            if "pathway" in result:
653                self.pathway = result["pathway"]
654            else:
655                raise Exception("Could not get kgml and  pathway image")
656        else:
657            self.pathway = obiKEGG.KEGGPathway(item.pathway_id)
658
659        self.pathwayView.SetPathway(self.pathway,
660                                    self.pathways.get(item.pathway_id, [[]])[0])
661
662    def UpdatePathwayViewTransform(self):
663        self.pathwayView.updateTransform()
664
665    def Update(self):
666        if not self.data:
667            return
668        self.error(0)
669        self.information(0)
670        pb = OWGUI.ProgressBar(self, 100)
671        if self.useAttrNames:
672            genes = [str(v.name).strip() for v in self.data.domain.attributes]
673        elif self.geneAttrCandidates:
674            geneAttr = self.geneAttrCandidates[min(self.geneAttrIndex,
675                                                   len(self.geneAttrCandidates) - 1)]
676            genes = [str(e[geneAttr]) for e in self.data
677                     if not e[geneAttr].isSpecial()]
678            if any("," in gene for gene in genes):
679                genes = reduce(add, (split_and_strip(gene, ",")
680                                     for gene in genes),
681                               [])
682                self.information(0,
683                                 "Separators detected in input gene names. "
684                                 "Assuming multiple genes per instance.")
685        else:
686            self.error(0, "Cannot extract gene names from input")
687            genes = []
688        org_code = self.organismCodes[min(self.organismIndex,
689                                          len(self.organismCodes) - 1)]
690
691        if USE_THREADING:
692            result = {}
693
694            def callable(*args, **kwargs):
695                result["org"] = org = obiKEGG.KEGGOrganism(org_code)
696                # Make sure genes are cached for global reference set
697                result["genes"] = org.genes.keys()
698
699            self.setEnabled(False)
700            try:
701                thread = threading.Thread(None, callable,
702                                          name="get_organism_genes",
703                                          )
704                thread.start()
705                while thread.is_alive():
706                    thread.join(timeout=0.025)
707                    qApp.processEvents()
708            finally:
709                self.setEnabled(True)
710
711            if "org" in result:
712                org = result["org"]
713            else:
714                raise Exception("Could not get organism genes")
715        else:
716            org = obiKEGG.KEGGOrganism(org_code)
717
718        uniqueGenes, _, _ = org.get_unique_gene_ids(set(genes),
719                                                    self.caseSensitive)
720        genesCount = len(set(genes))
721        self.infoLabel.setText("%i unique gene names on input\n%i (%.1f%%) "
722                               "genes names matched" %
723                               (genesCount, len(uniqueGenes),
724                                100.0 * len(uniqueGenes) / genesCount if genes else 0.0))
725
726        self.information(1)
727        if self.useReference and self.refData:
728            if self.useAttrNames:
729                reference = [str(v.name).strip() for v in self.refData]
730            else:
731                geneAttr = self.geneAttrCandidates[min(self.geneAttrIndex,
732                                                       len(self.geneAttrCandidates) - 1)]
733                reference = [str(e[geneAttr]) for e in self.refData
734                             if not e[geneAttr].isSpecial()]
735                if any("," in gene for gene in reference):
736                    reference = reduce(add, (split_and_strip(gene, ",")
737                                             for gene in reference),
738                                       [])
739                    self.information(1,
740                                     "Separators detected in reference gene "
741                                     "names. Assuming multiple genes per "
742                                     "example.")
743            uniqueRefGenes, _, _ = org.get_unique_gene_ids(set(reference),
744                                                           self.caseSensitive)
745            self.referenceGenes = reference = uniqueRefGenes.keys()
746        else:
747            self.referenceGenes = reference = org.get_genes()
748        self.uniqueGenesDict = uniqueGenes
749        self.genes = uniqueGenes.keys()
750        self.revUniqueGenesDict = dict([(val, key) for key, val in
751                                        self.uniqueGenesDict.items()])
752
753        taxid = obiKEGG.to_taxid(org.org_code)
754        r_tax_map = dict((v, k) for k, v in
755                         obiKEGG.KEGGGenome.TAXID_MAP.items())
756        if taxid in r_tax_map:
757            taxid = r_tax_map[taxid]
758
759        with orngServerFiles.DownloadProgress.setredirect(self.progressBarSet):
760            orngServerFiles.update(obiGeneSets.sfdomain, "index.pck")
761            kegg_gs_collections = \
762                list(obiGeneSets.collections((("KEGG", "pathways"), taxid)))
763
764        if USE_THREADING:
765            result = {}
766
767            def callable(*args, **kwargs):
768#                result["result"] = org.get_enriched_pathways(*args, **kwargs)
769                result["result"] = pathway_enrichment(*args, **kwargs)
770
771            self.setEnabled(False)
772            try:
773                thread = threading.Thread(
774                    None, callable,
775                    name="get_enriched_pathways",
776                    args=(kegg_gs_collections,
777                          self.genes,
778                          reference),
779                    kwargs={"callback":
780                            threading_queued_invoke(
781                                self,
782                                lambda value: self.progressBarSet(value))}
783                )
784
785                thread.start()
786                while thread.is_alive():
787                    thread.join(timeout=0.025)
788                    qApp.processEvents()
789            finally:
790                self.setEnabled(True)
791
792            if "result" in result:
793                self.pathways = result["result"]
794            else:
795                raise Exception('Could not get enriched pathways')
796
797        else:
798            self.pathways = org.get_enriched_pathways(
799                self.genes, reference,
800                callback=self.progressBarSet
801            )
802
803        self.org = org
804        if not self.pathways:
805            self.warning(0, "No enriched pathways found.")
806        else:
807            self.warning(0)
808
809        self.UpdateListView()
810        pb.finish()
811
812    def Commit(self):
813        if self.data:
814            selectedItems = self.pathwayView.scene().selectedItems()
815            selectedGenes = reduce(set.union, [item.marked_objects
816                                               for item in selectedItems],
817                                   set())
818
819            if self.useAttrNames:
820                selectedVars = [self.data.domain[self.uniqueGenesDict[gene]]
821                                for gene in selectedGenes]
822                newDomain = Orange.data.Domain(selectedVars, 0)
823                data = Orange.data.Table(newDomain, self.data)
824                self.send("Selected Examples", data)
825            elif self.geneAttrCandidates:
826                geneAttr = self.geneAttrCandidates[min(self.geneAttrIndex,
827                                                       len(self.geneAttrCandidates) - 1)]
828                selectedExamples = []
829                otherExamples = []
830                for ex in self.data:
831                    names = [self.revUniqueGenesDict.get(name, None)
832                             for name in split_and_strip(str(ex[geneAttr]), ",")]
833                    if any(name and name in selectedGenes for name in names):
834                        selectedExamples.append(ex)
835                    else:
836                        otherExamples.append(ex)
837
838                if selectedExamples:
839                    selectedExamples = Orange.data.Table(selectedExamples)
840                else:
841                    selectedExamples = None
842
843                if otherExamples:
844                    otherExamples = Orange.data.Table(otherExamples)
845                else:
846                    otherExamples = None
847
848                self.send("Selected Examples", selectedExamples)
849                self.send("Unselected Examples", otherExamples)
850        else:
851            self.send("Selected Examples", None)
852            self.send("Unselected Examples", None)
853
854    def ClearCache(self):
855        from ..obiKEGG import caching
856        try:
857            caching.clear_cache()
858        except Exception, ex:
859            QMessageBox.warning(self, "Cache clear", ex.args[0])
860
861    def onStateChange(self, stateType, id, text):
862        if stateType == "Warning":
863            self.pathwayView._userMessage = text
864            self.pathwayView.viewport().update()
865
866    def saveGraph(self):
867        from Orange.OrangeWidgets.OWDlgs import OWChooseImageSizeDlg
868        sizeDlg = OWChooseImageSizeDlg(self.pathwayView.scene(), parent=self)
869        sizeDlg.exec_()
870
871    @pyqtSignature("queuedInvoke(PyQt_PyObject)")
872    def queuedInvoke(self, func):
873        func()
874
875    def progressBarSet(self, value):
876        if not getattr(self, "_in_progress_update", False):
877            self._in_progress_update = True
878            try:
879                OWWidget.progressBarSet(self, value)
880            finally:
881                self._in_progress_update = False
882
883    def onDeleteWidget(self):
884        """
885        Called before the widget is removed from the canvas.
886        """
887        self.org = None
888        gc.collect()  # Force collection
889
890
891from .. import obiProb
892
893
894def pathway_enrichment(genesets, genes, reference, prob=None, callback=None):
895    result_sets = []
896    p_values = []
897    if prob is None:
898        prob = obiProb.Hypergeometric()
899
900    for i, gs in enumerate(genesets):
901        cluster = gs.genes.intersection(genes)
902        ref = gs.genes.intersection(reference)
903        k = len(cluster)
904        N = len(reference)
905        m = len(ref)
906        n = len(genes)
907        if k:
908            p_val = prob.p_value(k, N, m, n)
909            result_sets.append((gs.id, cluster, ref))
910            p_values.append(p_val)
911        if callback is not None:
912            callback(100.0 * i / len(genesets))
913
914    # FDR correction
915    p_values = obiProb.FDR(p_values)
916
917    return dict([(id, (genes, p_val, len(ref)))
918                 for (id, genes, ref), p_val in zip(result_sets, p_values)])
919
920
921if __name__ == "__main__":
922    app = QApplication(sys.argv)
923    data = Orange.data.Table("brown-selected.tab")
924    w = OWKEGGPathwayBrowser()
925    w.UpdateOrganismComboBox()
926    w.show()
927    w.SetData(Orange.data.Table(data[:]))
928    QTimer.singleShot(10, w.handleNewSignals)
929
930    app.exec_()
931    w.saveSettings()
Note: See TracBrowser for help on using the repository browser.