source: orange-bioinformatics/Orange/bioinformatics/widgets/prototypes/OWGeneAtlasTissueExpression.py @ 1625:cefeb35cbfc9

Revision 1625:cefeb35cbfc9, 17.1 KB checked in by mitar, 2 years ago (diff)

Moving files around.

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