source: orange-bioinformatics/_bioinformatics/widgets/prototypes/OWGeneAtlasTissueExpression.py @ 1643:2cfa80dac3d3

Revision 1643:2cfa80dac3d3, 17.2 KB checked in by mitar, 2 years ago (diff)

Fixing some imports. Marking widgets as prototypes.

Line 
1"""
2<name>Gene Atlas Tissue Expression</name>
3<description></description>
4<prototype>1</prototype>
5"""
6
7from __future__ import absolute_import
8
9import os, sys
10from collections import defaultdict
11import shelve
12
13import Orange
14from Orange.utils import lru_cache
15from Orange.OrangeWidgets import OWGUI
16from Orange.OrangeWidgets.OWWidget import *
17
18from Orange.orng import orngDataCaching, orngServerFiles
19
20from ... import obiArrayExpress, obiGene, obiGeneAtlas
21
22TAXID_TO_ORG = obiGeneAtlas.TAXID_TO_ORG
23
24class OWGeneAtlasTissueExpression(OWWidget):
25    contextHandlers = {"": DomainContextHandler("", ["selected_organism",
26                                                     "selected_gene_attr",
27                                                     "genes_in_columns",
28                                                     "selected_ef",
29                                                     "selected_ef_value"])}
30    settingsList = []
31   
32    ORGANISMS = obiArrayExpress.ATLAS_ORGANISMS
33    FACTORS = ["Organism part", "Disease state", "Cell type"]
34   
35    def __init__(self, parent=None, signalManager=None, title="Gene Atlas Tissue Expression"):
36        OWWidget.__init__(self, parent, signalManager, title)
37       
38        self.inputs = [("Example Table", Orange.data.Table, self.set_data)]
39        self.outputs = [("Selected Genes", Orange.data.Table)]
40       
41        self.selected_organism = "Homo sapiens"
42        self.selected_gene_attr = 0
43        self.genes_in_columns = False
44        self.selected_ef = 0
45        self.selected_ef_value = 0
46       
47        self.loadSettings()
48       
49        #####
50        # GUI
51        #####
52        box = OWGUI.widgetBox(self.controlArea, "Info", addSpace=True)
53        self.info_label = OWGUI.widgetLabel(box, "No data on input.\n")
54       
55        box = OWGUI.widgetBox(self.controlArea, "Organism", addSpace=True)
56        cb = OWGUI.comboBox(box, self, "selected_organism",
57                            items=self.ORGANISMS,
58                            tooltip="Organism name",
59                            callback=self.on_organism_change,
60                            sendSelectedValue=True,
61                            valueType=str
62                            )
63        cb.setMaximumWidth(250)
64       
65        box = OWGUI.widgetBox(self.controlArea, "Gene Attribute", addSpace=True)
66        self.gene_attr_cb = OWGUI.comboBox(box, self, "selected_gene_attr",
67                              tooltip="Attribute (column) containing the gene names.",
68                              callback=self.on_gene_attr_change,
69                              )
70        self.gene_attr_cb.setMaximumWidth(250)
71       
72        cb = OWGUI.checkBox(box, self, "genes_in_columns", "Use attribute names",
73                       tooltip="Gene names in columns.",
74                       callback=self.on_genes_change,)
75        cb.disables.append((-1, self.gene_attr_cb))
76        cb.makeConsistent()
77       
78        box = OWGUI.widgetBox(self.controlArea, "Tissues", addSpace=True)
79        self.categories_cb = OWGUI.comboBox(box, self, "selected_ef",
80                                box="Categories", 
81                                items=self.FACTORS,
82                                tooltip="Experimental factor.",
83                                callback=self.on_ef_change,
84                                )
85        self.categories_cb.box.setFlat(True)
86       
87        self.values_cb = OWGUI.comboBox(box, self, "selected_ef_value",
88                                box="Values",
89                                tooltip="Experimental factor value.",
90                                callback=self.on_ef_value_change
91                                )
92        self.values_cb.setMaximumWidth(250)
93        self.values_cb.box.setFlat(True)
94       
95        box = OWGUI.widgetBox(self.controlArea, "Cache", addSpace=True)
96        OWGUI.button(box, self, "Clear cache",
97                     callback=self.on_cache_clear,
98                     tooltip="Clear Gene Atlas cache.")
99       
100        OWGUI.rubber(self.controlArea)
101       
102        OWGUI.button(self.controlArea, self, label="Commit",
103                     callback=self.commit,
104                     tooltip="Send selected genes")
105       
106        self.report_view = QTreeView(self.mainArea)
107        self.report_view.setSelectionMode(QTreeView.ExtendedSelection)
108        self.report_view.setSortingEnabled(True)
109        self.report_view.setRootIsDecorated(False)
110        self.report_view.setAlternatingRowColors(True)
111        self.report_view.setEditTriggers(QTreeView.NoEditTriggers)
112        self.mainArea.layout().addWidget(self.report_view)
113        self.report_header = ["Gene symbol", "Up", "Down"]
114       
115        model = QStandardItemModel()
116        model.setHorizontalHeaderLabels(self.report_header)
117        self.report_view.setModel(model)
118       
119        self.data = None
120        self.candidate_vars = []
121        self.candidate_var_names = []
122        self.results = {}, {}, {}
123       
124        self.ensembl_info = None
125        self.gene_matcher = obiGene.GMDirect()
126        self.loaded_matcher_taxid = None
127        self.unknown_genes = []
128        self.query_genes = []
129       
130#        self.set_organism(self.selected_organism, update_results=False)
131       
132        self.get_atlas_summary = obiGeneAtlas.get_atlas_summary
133       
134        #Cached construct_matcher
135        @lru_cache(maxsize=3)
136        def my_cached_matcher(org):
137            return obiGeneAtlas.default_gene_matcher(org)
138        self.construct_matcher = my_cached_matcher
139       
140    def set_data(self, data=None):
141        """ Set the input example table with gene names.
142        """
143        self.closeContext("")
144        self.clear()
145        self.data = data
146        if data is not None:
147            self.init_gene_box(data)
148            taxid = orngDataCaching.data_hints.get_hint(data, key="taxid", default=None)
149            if taxid is not None:
150                if taxid in TAXID_TO_ORG:
151                    self.selected_organism = TAXID_TO_ORG[taxid]
152                   
153            genesinrows = orngDataCaching.data_hints.get_hint(data, key="genesinrows", default=None)
154            if genesinrows is not None:
155                self.genes_in_columns = genesinrows
156               
157            self.openContext("", data)
158            self.update_gene_matcher()
159            self.update_info_box()
160            self.run_query()
161            self.update_ef_values_box()
162            self.update_report_view()
163        else:
164            pass
165           
166    def clear(self):
167        """ Clear the widget state.
168        """
169        self.data = None
170        self.gene_attr_cb.clear()
171        self.values_cb.clear()
172        self.selected_gene_attr = 0
173        self.selected_ef_value = 0
174        self.candidate_vars = []
175        self.candidate_var_names = []
176        self.results = {}, {}, {}
177        model = QStandardItemModel()
178        model.setHorizontalHeaderLabels(self.report_header)
179        self.report_view.setModel(model)
180
181    def init_gene_box(self, data):
182        """ Populate the 'Gene Attribute' box
183        """
184        vars = data.domain.variables + data.domain.get_metas().values()
185        vars = [var for var in vars if isinstance(var, (Orange.feature.String))]
186        self.candidate_vars = vars
187        self.gene_attr_cb.addItems([var.name for var in vars])
188        # See if any var name contains 'gene'
189        gene_in_name = [v for v in vars if "gene" in v.name.lower()]
190        if gene_in_name:
191            self.selected_gene_attr = vars.index(gene_in_name[-1])
192        self.candidate_var_names = [a.name for a in data.domain.attributes]
193        if not self.candidate_vars:
194            self.genes_in_columns = True           
195           
196    def on_organism_change(self):
197        """ Called when the user changes the organism.
198        """
199        self.update_gene_matcher()
200        self.run_query()
201        self.update_ef_values_box()
202        self.update_report_view()
203   
204    def on_gene_attr_change(self):
205        """ Called  when the user changes source gene attribute.
206        """
207        self.update_info_box()
208        self.run_query()
209        self.update_ef_values_box()
210        self.update_report_view()
211   
212    def on_genes_change(self):
213        """ Called when the user toogles the "Genes in columns' check box.
214        """
215        self.update_info_box()
216        self.run_query()
217        self.update_ef_values_box()
218        self.update_report_view()
219   
220    def on_ef_change(self):
221        """ Called when the user changes the EF factor.
222        """
223        self.update_ef_values_box()
224        # TODO: restore previous selected value for factor (if value in ef_values)
225        self.update_report_view()
226       
227    def on_ef_value_change(self):
228        """ Called when the user changes the EF value.
229        """
230        results = self.results[self.selected_ef]
231        ef_value = self.ef_values[self.selected_ef_value]
232        self.update_report_view()
233       
234    def on_cache_clear(self):
235        from contextlib import closing
236        with closing(obiGeneAtlas._cache()) as cache:
237            cache.clear()
238       
239    def input_genes(self, full_res=False):
240        """ Extract input gene names from ``self.data``.
241        """
242        if self.genes_in_columns:
243            names = self.candidate_var_names
244        else:
245            names = []
246            attr = self.candidate_vars[self.selected_gene_attr]
247            for ex in self.data:
248                if not ex[attr].is_special():
249                    names.append(str(ex[attr]))
250           
251        return sorted(set(names))
252   
253    def match_genes(self, genes, full_res=True):
254        """ Match the genes to ensembl gene ids.
255        """
256        if self.gene_matcher and self.ensembl_info:
257            self.gene_matcher.set_targets(self.ensembl_info.keys())
258            match_genes = map(self.gene_matcher.match, genes)
259            genes = map(self.gene_matcher.umatch, genes)
260        else:
261            match_genes = None
262        genes = sorted(set(genes) - set([None]))
263       
264        if full_res:
265            return genes, match_genes
266        else:
267            return genes
268   
269    def run_query(self):
270        """ Query the Gene Atlas.
271        """
272        if self.data is not None:
273            genes = self.input_genes()
274            matched, match_res = self.match_genes(genes, True)
275            all_matched, unknown = [], []
276            for gene, match in zip(genes, match_res):
277                if len(match) == 1:
278                    all_matched.append(match[0])
279                elif len(match) > 1:
280                    all_matched.append(match[0])
281                else:
282                    unknown.append(gene)
283            self.unknown_genes = unknown
284            self.warning(2)
285            self.information(2)
286            if float(len(unknown)) / len(genes) > 0.5:
287                self.warning(2, "%i unknown genes" % len(unknown))
288            elif unknown:
289                self.information(2, "%i unknown genes" % len(unknown))
290               
291            gm = obiGene.GMDirect()
292            gm.set_targets(all_matched)
293           
294            # Non threaded
295#            self.results = self.get_atlas_summary(all_matched, self.selected_organism, gm)
296            # Threaded
297            self.error(0)
298            self.update_info_box(query_running=True)
299            self.progressBarInit()
300            self.controlArea.setEnabled(False)
301            self.results = None
302            try:
303                call = self.asyncCall(self.get_atlas_summary, (all_matched,
304                                                               self.selected_organism,
305                                                               gm),
306                                      name="Query Gene Expression Atlas",
307                                      onError=self.handle_assync_error)
308                QObject.connect(call, SIGNAL("progressChanged(float)"), self.progressBarSet)
309                call(progress_callback=call.emitProgressChanged)
310                self.results = call.get_result(processEvents=True)
311            except obiArrayExpress.GeneAtlasError, ex:
312                self.error(0, str(ex))
313            finally:
314                self.controlArea.setEnabled(True)
315                self.update_info_box(query_running=False)
316                self.progressBarFinished()
317           
318            self.query_genes = genes
319       
320    def update_gene_matcher(self):
321        taxid = dict((v,k) for k,v in TAXID_TO_ORG.items()).get(self.selected_organism)
322        self.warning(0)
323        if taxid != self.loaded_matcher_taxid:
324            if taxid:
325                self.ensembl_info = obiGene.EnsembleGeneInfo(taxid)
326                self.gene_matcher = self.construct_matcher(taxid)
327            else:
328                self.warning(0, "Cannot match gene names!")
329                self.ensembl_info = None
330                self.gene_matcher = obiGene.GMDirect()
331            self.loded_matcher_taxid = taxid
332       
333    def update_ef_values_box(self):
334        """ Update the "Values" box.
335        """
336        ef_values = set() 
337        results = self.results[self.selected_ef]
338        efv_count = defaultdict(int)
339        if self.results:
340            for gene, val in  results.iteritems():
341                for ef_val, (up, down) in val.iteritems():
342                   ef_values.add(ef_val)
343                   efv_count[ef_val] += up + down
344       
345        # Sort by up and down count sum of all genes
346        self.ef_values = sorted(ef_values, key=efv_count.get, reverse=True)
347        self.values_cb.clear()
348        self.values_cb.addItems(self.ef_values)
349        if self.ef_values:
350            self.selected_ef_value = min(len(self.ef_values) - 1, self.selected_ef_value)
351       
352    def update_report_view(self):
353        results = self.results[self.selected_ef]
354       
355        model = QStandardItemModel()
356       
357               
358        def standard_item(val):
359            item = StandardPyItem()
360            item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
361            item.setData(QVariant(val), Qt.DisplayRole)
362            return item
363       
364        if results and self.ef_values:
365            ef_value = self.ef_values[self.selected_ef_value]
366            for gene, efvs in results.iteritems():
367                if ef_value in efvs:
368                    up, down = efvs[ef_value]
369                    model.appendRow([standard_item(gene),
370                                     standard_item(up),
371                                     standard_item(down)])
372       
373        model.setHorizontalHeaderLabels(self.report_header)
374        self.report_view.setModel(model)
375        self.report_view.resizeColumnToContents(0)
376        self.report_view.resizeColumnToContents(1)
377        self.report_view.resizeColumnToContents(2)
378       
379    def update_info_box(self, query_running=False):
380        text = ""
381        if self.data is not None:
382            genes = self.input_genes()
383#            unique, matched = self.match_genes(genes, full_res=True)
384            # TODO: How many genes are matched (should update after the query is run).
385            if self.results and not query_running:
386#                matched = min(max([len(r) for r in self.results]), len(genes))
387                matched = len(genes) - len(self.unknown_genes)
388                with_results = len(reduce(set.union, [d.keys() for d in self.results], set()))
389                text = "{0} gene names on input.\n{1} genes matched.\n{2} genes annotated.".format(len(genes), matched, with_results)
390            else:
391                text = "{0} gene names on input.\n\n".format(len(genes))
392        else:
393            text = "No data on input.\n\n"
394        if query_running:
395            text += "Querying Gene Atlas. Please wait."
396        self.info_label.setText(text)
397           
398    def selected_report_genes(self):
399        """ Return the gene names selected in the report view.
400        """
401        rows = self.report_view.selectionModel().selectedRows(0)
402        genes = []
403        for index in rows:
404            name = str(index.data(Qt.DisplayRole).toString())
405            genes.append(name)
406        return genes
407           
408    def commit(self):
409        genes = set(self.selected_report_genes())
410        self.gene_matcher.set_targets(genes)
411       
412        genes = [gene.lower() for gene in genes]
413        if self.genes_in_columns:
414            attrs = [a for a in self.data.domain.attributes \
415                     if a.name.lower() in genes or self.gene_matcher.umatch(a.name)]
416            domain = Orange.data.Domain(attrs, self.data.domain.class_var)
417            domain.add_metas(self.data.domain.get_metas())
418            data = Orange.data.Table(domain, self.data)
419        else:
420            attr = self.candidate_vars[self.selected_gene_attr]
421            examples = []
422            for ex in self.data:
423                if str(ex[attr]).lower() in genes or self.gene_matcher.umatch(str(ex[attr])):
424                    examples.append(ex)
425            if examples:
426                data = Orange.data.Table(examples)
427            else:
428                data = None
429        self.send("Selected Genes", data)
430       
431    def handle_assync_error(self, *args):
432        pass
433       
434       
435class StandardPyItem(QStandardItem):
436    def __lt__(self, other):
437        my = self.data(Qt.DisplayRole).toPyObject()
438        other = other.data(Qt.DisplayRole).toPyObject()
439        return my < other
440       
441   
442if __name__ == "__main__":
443    app = QApplication(sys.argv)
444    w = OWGeneAtlasTissueExpression()
445    data = Orange.data.Table("RUNX1.tab")
446    w.show()
447    w.set_data(data)
448    app.exec_()
449    w.saveSettings()
450       
Note: See TracBrowser for help on using the repository browser.