source: orange-bioinformatics/_bioinformatics/widgets/prototypes/OWEpistasisAnalysis.py @ 1636:10d234fdadb9

Revision 1636:10d234fdadb9, 14.6 KB checked in by mitar, 2 years ago (diff)

Restructuring because we will not be using namespaces.

Line 
1"""
2<name>Epistasis Analysis</name>
3<description>Epistasis analysis on microaray data</description>
4<icon>icons/EpistasisAnalysis.png</icon>
5<priority>1200</priority>
6"""
7
8from __future__ import absolute_import
9
10import math
11
12import orange, statc
13from Orange.OrangeWidgets import OWGUI
14from Orange.OrangeWidgets.OWWidget import *
15
16from .OWDataFiles import DataFiles, ExampleSelection
17
18##############################################################################
19# parameters that determine the canvas layout
20
21# constants for canvas drawing
22
23canvasW = 220 # canvas width and height
24canvasB = 30  # border
25circleR = (canvasW - 2 * canvasB)/2.
26
27##############################################################################
28# main class
29
30class OWEpistasisAnalysis(OWWidget):   
31    settingsList = ['distype']
32
33    def __init__(self, parent=None, signalManager = None):
34        OWWidget.__init__(self, parent, signalManager, 'Epistasis Analysis') 
35       
36        self.inputs = [("Examples", ExampleTable, self.dataset, Multiple), ("Structured  Data", DataFiles, self.chipdata, 1)]
37        self.outputs = [("Gene Selection", ExampleSelection), ("Examples A->B", ExampleTable), ("Examples B->A", ExampleTable), ("Examples A||B", ExampleTable)]
38
39        self.data = []
40        self.selectedFile = None
41        self.A, self.B, self.AB = (None, None, None)
42        self.epiA, self.epiB, self.para = (0, 0, 0)
43        self.distype = 0
44        #set default settings
45        self.loadSettings()
46
47        # Selection of data files
48        box = OWGUI.widgetBox(self.controlArea, "Data files", addSpace=True)
49#        box = QVButtonGroup("Data Files", self.controlArea)
50#        self.fileLB = OWGUI.listBox(box, self, "selectedFile", "files",
51#                                    tooltip="Select example table",
52##                                    callback=self.fileSelectionChanged,
53#                                    selectionMode=QListWidget.SingleSelection,
54#                                    )
55#        self.connect(self.fileLB, SIGNAL("currentRowChanged(int)"), self.fileSelectionChanged)
56        self.fileLB = QListWidget()
57#        self.fileLB.setMaximumWidth(10)
58        self.connect(self.fileLB, SIGNAL("highlighted(int)"), self.fileSelectionChanged)
59#        self.connect(self.fileLB, SIGNAL("selected(int)"), self.setFileReferenceBySelection)
60        box.layout().addWidget(self.fileLB)
61       
62        hbox = OWGUI.widgetBox(box, "", orientation="horizontal")
63#        hbox = QHBox(box)
64        self.markBtns = []
65        for (i, lbl) in enumerate(["A","B","D"]):
66            btn = OWGUI.button(hbox, self, lbl,
67                               callback=lambda i=i: self.setMark(i),
68                               disabled=1)
69#            btn.setMaximumWidth(45)
70#            btn.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Fixed)
71            self.markBtns.append(btn)
72
73        # Relations, checkbox for gene selection
74        self.sbox = OWGUI.widgetBox(self.controlArea, "Relations (Gene Selection)")
75#        self.sbox = QVButtonGroup("Relations (Gene Selection)", self.controlArea)
76        self.selChkbox = []
77        self.cbinfo = (("epiA", "A -> B", "Gene B is epistatic to gene A"),
78                       ("epiB", "B -> A", "Gene A is epistatic to gene B"),
79                       ("para", "A || B", "Genes A and B are on parallel pathways"))
80        for (i, cb) in enumerate(self.cbinfo):
81            cb = OWGUI.checkBox(self.sbox, self, cb[0], cb[1],
82                                tooltip=cb[2],
83                                callback=self.filterSelectionChanged)
84            self.selChkbox.append(cb)
85        self.infochi = OWGUI.widgetLabel(self.sbox, "") #QLabel(self.sbox, '')
86        self.sbox.setDisabled(1)
87
88        OWGUI.radioButtonsInBox(self.controlArea, self, "distype", ["Average By Gene", "Average By Measurement"],
89                                box="Distance", tooltips=None, callback=self.analysis)
90
91        # scene
92        self.scene = QGraphicsScene()
93        self.sceneView = QGraphicsView()
94        self.sceneView.setScene(self.scene)
95        self.scene.setSceneRect(QRectF(0, 0, canvasW, canvasW))
96        self.mainArea.layout().addWidget(self.sceneView)
97       
98        # canvas
99#        self.canvas = QCanvas()
100#        self.layout = QVBoxLayout(self.mainArea)
101#        self.canvasView = QCanvasView(self.canvas, self.mainArea)
102#        self.canvas.resize(canvasW, canvasW)
103#        self.layout.add(self.canvasView)
104
105        self.resize(420,350)
106
107    ##########################################################################
108    # handling of input/output signals
109
110    def checkDomain(self, data, selection = None):
111       
112        cl = clo = data.domain.classVar
113        if cl:
114            if selection:
115                cl = orange.RemoveUnusedValues(cl, selection, removeOneValued = 1)
116            else:
117                cl = orange.RemoveUnusedValues(cl, data, removeOneValued = 1)
118
119        # Construct a new domain only if the class has changed
120        # (ie to lesser number of values or to one value (alias None))
121        if cl != clo:
122            domain = orange.Domain(data.domain.attributes, cl)
123            metas = data.domain.getmetas()
124            for key in metas:
125                domain.addmeta(key, metas[key])
126            return domain
127        else:
128            return None
129       
130    def dataset(self, data, id):
131        ids = [d.id for d in self.data]
132        if not data:
133            if id in ids:
134                k = ids.index(id)
135                del self.data[k]
136                self.fileLB.takeItem(k)
137#                self.fileLB.removeItem(k)
138        else:
139            # check if the same length
140            if data.domain.classVar:
141                domain = self.checkDomain(data)
142                if domain:
143                    data = orange.ExampleTable(domain, data)
144            data.setattr("id", id)
145            data.setattr("marker", None)
146            if id in ids:
147                data.id = id
148                indx = ids.index(id)
149                self.data[indx] = data
150                self.fileLB.takeItem(indx)
151                self.fileLB.insertItem(indx, self.createListItem(data))
152#                self.fileLB.changeItem(self.createListItem(data), indx)
153            else:
154                if len(self.data) < 3:
155                    data.setattr("marker", len(self.data)) #### REMOVE !!!
156                self.fileLB.addItem(self.createListItem(data))
157                self.data.append(data)
158               
159                if len(self.data) >= 3: #### REMOVE !!!
160                    self.analysis()
161
162    def chipdata(self, data):
163        self.data = [] # XXX should only remove the data from the same source, use id in this rutine
164        self.fileLB.clear()
165        if not data:
166            for i in self.canvas.allItems():
167                i.setCanvas(None)
168            self.canvas.update()
169            return
170        indx = 0
171        for (strainname, ds) in data:
172            for d in ds:
173                self.dataset(d, indx)
174                indx += 1
175
176    ##########################################################################
177    # handle events
178
179    # mark button pressed, mark the file appropriately
180    def setMark(self, mark):
181        sel = self.selectedFile
182        # remove this mark from previously marked file
183        marks = [d.marker for d in self.data]
184        if mark in marks:
185            indx = marks.index(mark)
186            self.data[indx].setattr("marker", None)
187            self.fileLB.takeItem(indx)
188            self.fileLB.insertItem(indx, self.createListItem(self.data[indx]))
189#            self.fileLB.changeItem(self.createListItem(self.data[indx]), indx)
190        self.data[sel].setattr("marker", mark)
191        self.fileLB.takeItem(sel)
192        self.fileLB.insertItem(sel, self.createListItem(self.datap[sel]))
193#        self.fileLB.changeItem(self.createListItem(self.data[sel]), sel)
194        self.analysis() # do the analysis (if all three selections are made)
195
196    # user has selected a different file from the file list
197    def fileSelectionChanged(self, sel):
198        self.selectedFile = sel
199        for btn in self.markBtns:
200            btn.setEnabled(True)
201
202    # user has selected a different file
203    def filterSelectionChanged(self):
204        pass
205       
206    def createListItem(self, data):
207        pixmap = QPixmap(QSize(14, 14))
208#        pixmap.resize(14,13)
209        pixmap.fill(Qt.white)
210       
211        if data.marker is not None:
212            painter = QPainter()
213            painter.begin(pixmap)
214            painter.setPen(Qt.black)
215            painter.setBrush(Qt.white)
216            painter.drawRect(0, 0, 13, 13)
217            c = ['A','B','D'][data.marker]
218            painter.drawText(3, 11, c)
219            painter.end()
220           
221        listItem = QListWidgetItem(QIcon(pixmap), data.name)
222#        listItem = QListBoxPixmap(pixmap)
223#        listItem.setText(data.name)
224        return listItem
225##        return QListBoxText(text)
226
227    ##########################################################################
228    # epistasis analysis
229
230    import math
231
232    def analysis(self):
233        markers = [d.marker for d in self.data]
234        if len(filter(lambda x: x is not None, markers)) < 3:
235            self.sbox.setDisabled(True)
236            for item in self.scene.items():
237                self.scene.removeItem(item)
238#                i.setCanvas(None)
239#            self.canvas.update()
240            return
241        self.sbox.setEnabled(True)
242
243        pa, pb, pab = [self.data[markers.index(x)] for x in range(3)]
244       
245        pb = orange.ExampleTable(pa.domain, pb)
246        pab = orange.ExampleTable(pa.domain, pab)
247       
248        dist = orange.ExamplesDistanceConstructor_Euclidean(pa, normalize=False)
249
250        ave = [0]*3
251        vote = [0]*3
252        genevote = []
253        for g in range(len(pa)):
254            d = [dist(pb[g], pab[g]), dist(pa[g], pab[g]), dist(pa[g], pb[g])]
255            voteindx = d.index(min(d))
256            vote[voteindx] += 1
257            genevote.append(voteindx)
258            if self.distype == 1:
259                for i in range(3):
260                    d[i] = d[i] * d[i]
261            for i in range(3):
262                ave[i] += d[i]
263        if self.distype == 1:
264            ave = [math.sqrt(x) / len(pa) for x in ave]
265        else:
266            ave = [x/len(pa) for x in ave]
267
268        # compute Chi^2 statistics,
269        # update the interface (report on results)
270        for i in range(3):
271            self.selChkbox[i].setText(self.cbinfo[i][1] + "  (%d genes)" % vote[i])
272        p = statc.chisquare([len(pa) / 3.] * 3, vote)[1]
273        self.infochi.setText('Chi Square: ' + ['p = %6.4f' % p, 'p < 0.0001'][p < 0.0001])
274
275        self.setAnalysisPlot(ave)
276        self.senddata(genevote)
277
278    def setAnalysisPlot(self, ds):
279        def plotDot(x, y, w=10, z=10):
280            dot = QGraphicsEllipseItem(x, y, w, w, self.scene)
281#            dot = QCanvasEllipse(w, w, self.canvas)
282            dot.setBrush(QBrush(Qt.black))
283            dot.setZValue(z)
284#            dot.setX(x); dot.setY(y); dot.setZ(z)
285            dot.show()
286           
287        def plotLine(x0, y0, x1, y1, w=2, z=10, color=Qt.black):
288            line = QGraphicsLineItem(x0, y0, x1, y1, self.scene)
289#            line = QCanvasLine(self.canvas)
290#            line.setPoints(x0, y0, x1, y1)
291            line.setPen(QPen(color, w))
292#            line.setZ(10)
293            line.setZValue(10)
294            line.show()
295           
296        def plotText(x, y, text, xoffset=0):
297            t = QGraphicsSimpleTextItem("", self.scene)
298#            t = QCanvasText(self.canvas)
299#            t.setText(text)
300            xw = t.boundingRect().width()
301            t.setPos(QPointF(x+xw*xoffset, y))
302            t.setZValue(20)
303#            t.setX(x+xw*xoffset); t.setY(y); t.setZ(20)
304            t.show()       
305
306        for item in self.scene.items():
307            self.scene.removeItem(item)
308
309        s = sum(ds)/2.
310        K = math.sqrt( s * reduce(lambda x,y: x*y, map(lambda x: s-x, ds)) )
311        R = reduce(lambda x,y: x*y, ds) / (4 * K)
312        scale = circleR / R
313        yab = canvasB + circleR - scale * math.sqrt(R**2 - (ds[2]/2)**2)
314        xa = canvasB + circleR - scale * ds[2]/2
315        xb = canvasB + circleR + scale * ds[2]/2
316        h = 2*K/ds[2]
317        yd = yab + scale * h
318        xd = xa + scale * math.sqrt(max(ds[0], ds[1])**2 - h**2)
319
320        # plot a circle
321        c = QGraphicsEllipseItem(0, 0, circleR*2, circleR*2, self.scene)
322#        c = QCanvasEllipse(circleR*2, circleR*2, self.canvas)
323        c.setBrush(QBrush(QColor(240,240,240)))
324        c.setPos(canvasB+circleR, canvasB+circleR)
325        c.setZValue(0)
326#        c.setX(canvasB+circleR); c.setY(canvasB+circleR); c.setZ(0)
327        c.show()
328
329        # plot triangle dots, line, ...
330        plotDot(xa, yab)
331        plotDot(xb, yab)
332        plotDot(xd, yd)
333
334        plotLine(xa, yab, xb, yab, color=Qt.red)
335        plotLine(xa, yab, xd, yd)
336        plotLine(xb, yab, xd, yd)
337
338        plotText(xa+(xb-xa)/2, yab-15, "%6.4f" % ds[2], xoffset=-0.5)
339        ymid = yab+(yd-yab)/2
340        plotText(xa+(xd-xa)/2-3, ymid, "%6.4f" % max(ds[0], ds[1]), xoffset=-1)
341        plotText(xd+(xb-xd)/2+3, ymid, "%6.4f" % min(ds[0], ds[1]))
342
343        # edge labels
344        markers = [d.marker for d in self.data]
345        labels = [self.data[markers.index(x)].name for x in range(3)]
346        plotText(xd, yd+8, labels[2], xoffset=-0.5)
347        plotText(xa, yab-20, labels[ds[:-1].index(min(ds[:-1]))], xoffset=-0.67)
348        plotText(xb, yab-20, labels[ds[:-1].index(max(ds[:-1]))], xoffset=-0.33)
349
350        self.canvas.update()
351
352    def senddata(self, genevotes):
353        def sendkeyeddata(channel, key):
354            for i in range(3):
355                d = datasets[i].select(genevotes, key)
356                d.name = datasets[i].name
357                self.send(channel, d, i)
358        markers = [d.marker for d in self.data]
359        datasets = [self.data[markers.index(x)] for x in range(3)]
360        channels= ["Examples A->B", "Examples B->A", "Examples A||B"]
361        for c in channels:
362            for i in range(3): # this should be excluded, repear in heat map
363                self.send(c, None, i)
364        for (i,ch) in enumerate(channels):
365            sendkeyeddata(ch, i)
366
367##################################################################################################
368# test script
369
370if __name__=="__main__":
371    import orange
372    a = QApplication(sys.argv)
373    ow = OWEpistasisAnalysis()
374    a.setMainWidget(ow)
375
376    ow.show()
377    names = ['wt1', 'wt2', 'wt3', 'wt4']
378##    names = [r'chipdata/pufA/pufA1.1.raw.tab', r'chipdata/yakA/yakA1.1.raw.tab', r'chipdata/yakApufA/yakApufA1.1.raw.tab', r'chipdata/yakApufA/yakApufA1.1.raw.tab']
379    for i, s in enumerate(names): 
380        d = orange.ExampleTable(s); d.name = s
381        ow.dataset(d, i)
382##    ow.dataset(None, 2)
383
384    a.exec_loop()
385    ow.saveSettings()
Note: See TracBrowser for help on using the repository browser.