source: orange/Orange/OrangeWidgets/Visualize Qt/OWScatterPlotQt.py @ 9671:a7b056375472

Revision 9671:a7b056375472, 21.4 KB checked in by anze <anze.staric@…>, 2 years ago (diff)

Moved orange to Orange (part 2)

Line 
1"""
2<name>Scatterplot (Qt)</name>
3<description>Scatterplot visualization.</description>
4<contact>Gregor Leban (gregor.leban@fri.uni-lj.si)</contact>
5<icon>icons/ScatterPlot.png</icon>
6<priority>130</priority>
7"""
8# ScatterPlotQt.py
9#
10# Show data using scatterplot
11#
12from OWWidget import *
13from OWScatterPlotGraphQt import *
14from OWkNNOptimization import *
15import orngVizRank
16import OWGUI, OWToolbars, OWColorPalette
17from orngScaleData import *
18from plot.owcurve import *
19
20###########################################################################################
21##### WIDGET : Scatterplot visualization
22###########################################################################################
23class OWScatterPlotQt(OWWidget):
24    settingsList = ["graph." + s for s in OWPlot.point_settings + OWPlot.appearance_settings] + [
25                    "graph.showXaxisTitle", "graph.showYLaxisTitle", "showGridlines",
26                    "graph.showLegend", "graph.jitterSize", "graph.jitterContinuous", "graph.showFilledSymbols", "graph.showProbabilities",
27                    "graph.showDistributions", "autoSendSelection", "toolbarSelection", "graph.sendSelectionOnUpdate",
28                    "colorSettings", "selectedSchemaIndex", "VizRankLearnerName"]
29    jitterSizeNums = [0.0, 0.1,   0.5,  1,  2 , 3,  4 , 5 , 7 ,  10,   15,   20 ,  30 ,  40 ,  50 ]
30
31    contextHandlers = {"": DomainContextHandler("", ["attrX", "attrY",
32                                                     (["attrColor", "attrShape", "attrSize"], DomainContextHandler.Optional),
33                                                     ("attrLabel", DomainContextHandler.Optional + DomainContextHandler.IncludeMetaAttributes)])}
34
35    def __init__(self, parent=None, signalManager = None):
36        OWWidget.__init__(self, parent, signalManager, "Scatterplot (Qt)", TRUE)
37
38        self.inputs =  [("Data", ExampleTable, self.setData, Default), ("Data Subset", ExampleTable, self.setSubsetData), ("Features", AttributeList, self.setShownAttributes), ("Evaluation Results", orngTest.ExperimentResults, self.setTestResults), ("VizRank Learner", orange.Learner, self.setVizRankLearner)]
39        self.outputs = [("Selected Data", ExampleTable), ("Other Data", ExampleTable)]
40
41        self.graph = OWScatterPlotGraphQt(self, self.mainArea, "ScatterPlotQt")
42        self.vizrank = OWVizRank(self, self.signalManager, self.graph, orngVizRank.SCATTERPLOT, "ScatterPlotQt")
43        self.optimizationDlg = self.vizrank
44
45        # local variables
46        self.showGridlines = 0
47        self.autoSendSelection = 1
48        self.toolbarSelection = 0
49        self.classificationResults = None
50        self.outlierValues = None
51        self.colorSettings = None
52        self.selectedSchemaIndex = 0
53        self.graph.sendSelectionOnUpdate = 0
54        self.attributeSelectionList = None
55
56        self.data = None
57        self.subsetData = None
58
59        #load settings
60        self.loadSettings()
61        self.graph.setShowXaxisTitle()
62        self.graph.setShowYLaxisTitle()
63
64        #GUI
65        self.tabs = OWGUI.tabWidget(self.controlArea)
66        self.GeneralTab = OWGUI.createTabPage(self.tabs, "Main")
67        self.SettingsTab = OWGUI.createTabPage(self.tabs, "Settings", canScroll = True)
68
69        #add a graph widget
70        self.mainArea.layout().addWidget(self.graph)
71        self.connect(self.graphButton, SIGNAL("clicked()"), self.graph.saveToFile)
72
73        #x attribute
74        self.attrX = ""
75        self.attrXCombo = OWGUI.comboBox(self.GeneralTab, self, "attrX", "X-axis Attribute", callback = self.majorUpdateGraph, sendSelectedValue = 1, valueType = str)
76
77        # y attribute
78        self.attrY = ""
79        self.attrYCombo = OWGUI.comboBox(self.GeneralTab, self, "attrY", "Y-axis Attribute", callback = self.majorUpdateGraph, sendSelectedValue = 1, valueType = str)
80
81        # coloring
82        self.attrColor = ""
83        box = OWGUI.widgetBox(self.GeneralTab, "Point Color")
84        self.attrColorCombo = OWGUI.comboBox(box, self, "attrColor", callback = self.updateGraph, sendSelectedValue=1, valueType = str, emptyString = "(Same color)")
85
86        box = OWGUI.widgetBox(self.GeneralTab, "Additional Point Properties")
87        # labelling
88        self.attrLabel = ""
89        self.attrLabelCombo = OWGUI.comboBox(box, self, "attrLabel", label = "Point label:", callback = self.updateGraph, sendSelectedValue = 1, valueType = str, emptyString = "(No labels)", indent = 10)
90
91        # shaping
92        self.attrShape = ""
93        self.attrShapeCombo = OWGUI.comboBox(box, self, "attrShape", label = "Point shape:", callback = self.updateGraph, sendSelectedValue=1, valueType = str, emptyString = "(Same shape)", indent = 10)
94
95        # sizing
96        self.attrSize = ""
97        self.attrSizeCombo = OWGUI.comboBox(box, self, "attrSize", label = "Point size:", callback = self.updateGraph, sendSelectedValue=1, valueType = str, emptyString = "(Same size)", indent = 10)
98
99        self.optimizationButtons = OWGUI.widgetBox(self.GeneralTab, "Optimization dialogs", orientation = "horizontal")
100        OWGUI.button(self.optimizationButtons, self, "VizRank", callback = self.vizrank.reshow, tooltip = "Opens VizRank dialog, where you can search for interesting projections with different subsets of attributes", debuggingEnabled = 0)
101
102        g = self.graph.gui
103
104        # zooming / selection
105        self.zoomSelectToolbar = g.zoom_select_toolbar(self.GeneralTab, buttons = g.default_zoom_select_buttons + [g.Spacing, g.ShufflePoints])
106        self.connect(self.zoomSelectToolbar.buttons[g.SendSelection], SIGNAL("clicked()"), self.sendSelections)
107
108        # ####################################
109        # SETTINGS TAB
110        # point width
111        g.point_properties_box(self.SettingsTab)
112
113        # #####
114        # jittering options
115        box2 = OWGUI.widgetBox(self.SettingsTab, "Jittering Options")
116        self.jitterSizeCombo = OWGUI.comboBox(box2, self, "graph.jitterSize", label = 'Jittering size (% of size)'+'  ', orientation = "horizontal", callback = self.resetGraphData, items = self.jitterSizeNums, sendSelectedValue = 1, valueType = float)
117        OWGUI.checkBox(box2, self, 'graph.jitterContinuous', 'Jitter continuous attributes', callback = self.resetGraphData, tooltip = "Does jittering apply also on continuous attributes?")
118
119        # general graph settings
120        box4 = OWGUI.widgetBox(self.SettingsTab, "General Graph Settings")
121        OWGUI.checkBox(box4, self, 'graph.showXaxisTitle', 'X axis title', callback = self.graph.setShowXaxisTitle)
122        OWGUI.checkBox(box4, self, 'graph.showYLaxisTitle', 'Y axis title', callback = self.graph.setShowYLaxisTitle)
123       
124        g.add_widgets([g.ShowLegend, g.ShowFilledSymbols, g.ShowGridLines], box4)
125       
126        box5 = OWGUI.widgetBox(box4, orientation = "horizontal")
127        OWGUI.checkBox(box5, self, 'graph.showProbabilities', 'Show probabilities'+'  ', callback = self.updateGraph, tooltip = "Show a background image with class probabilities")
128        smallWidget = OWGUI.SmallWidgetLabel(box5, pixmap = 1, box = "Advanced settings", tooltip = "Show advanced settings")
129        #OWGUI.rubber(box5)
130
131        box6 = OWGUI.widgetBox(smallWidget.widget, orientation = "horizontal")
132        box7 = OWGUI.widgetBox(smallWidget.widget, orientation = "horizontal")
133
134        OWGUI.widgetLabel(box6, "Granularity:"+"  ")
135        OWGUI.hSlider(box6, self, 'graph.squareGranularity', minValue=1, maxValue=10, step=1, callback = self.updateGraph)
136
137        OWGUI.checkBox(box7, self, 'graph.spaceBetweenCells', 'Show space between cells', callback = self.updateGraph)
138
139        self.colorButtonsBox = OWGUI.widgetBox(self.SettingsTab, "Colors", orientation = "horizontal")
140        OWGUI.button(self.colorButtonsBox, self, "Set Colors", self.setColors, tooltip = "Set the canvas background color, grid color and color palette for coloring continuous variables", debuggingEnabled = 0)
141
142        box5 = OWGUI.widgetBox(self.SettingsTab, "Tooltips Settings")
143        OWGUI.comboBox(box5, self, "graph.tooltipKind", items = ["Don't Show Tooltips", "Show Visible Attributes", "Show All Attributes"], callback = self.updateGraph)
144
145        box = OWGUI.widgetBox(self.SettingsTab, "Auto Send Selected Data When...")
146        OWGUI.checkBox(box, self, 'autoSendSelection', 'Adding/Removing selection areas', callback = self.selectionChanged, tooltip = "Send selected data whenever a selection area is added or removed")
147        OWGUI.checkBox(box, self, 'graph.sendSelectionOnUpdate', 'Moving/Resizing selection areas', tooltip = "Send selected data when a user moves or resizes an existing selection area")
148        self.graph.selection_changed.connect(self.selectionChanged)
149       
150        self.EffectsTab = OWGUI.createTabPage(self.tabs, "Appearance")
151        g.effects_box(self.EffectsTab)
152        g.theme_combo_box(self.EffectsTab)
153
154        self.GeneralTab.layout().addStretch(100)
155        self.SettingsTab.layout().addStretch(100)
156        self.EffectsTab.layout().addStretch(100)
157        self.icons = self.createAttributeIconDict()
158
159        self.debugSettings = ["attrX", "attrY", "attrColor", "attrLabel", "attrShape", "attrSize"]
160        self.wdChildDialogs = [self.vizrank]        # used when running widget debugging
161
162        dlg = self.createColorDialog()
163        self.graph.contPalette = dlg.getContinuousPalette("contPalette")
164        self.graph.discPalette = dlg.getDiscretePalette("discPalette")
165       
166        p = self.graph.palette()
167        p.setColor(OWPalette.Canvas, dlg.getColor("Canvas"))
168        p.setColor(OWPalette.Grid, dlg.getColor("Grid"))
169        self.graph.set_palette(p)
170
171        self.graph.enableGridXB(self.showGridlines)
172        self.graph.enableGridYL(self.showGridlines)
173
174        #self.SettingsTab.resize(self.SettingsTab.sizeHint())
175
176        self.resize(700, 550)
177
178
179    def settingsFromWidgetCallback(self, handler, context):
180        context.selectionPolygons = []
181        for curve in self.graph.selectionCurveList:
182            xs = [curve.x(i) for i in range(curve.dataSize())]
183            ys = [curve.y(i) for i in range(curve.dataSize())]
184            context.selectionPolygons.append((xs, ys))
185
186    def settingsToWidgetCallback(self, handler, context):
187        selections = getattr(context, "selectionPolygons", [])
188        for (xs, ys) in selections:
189            c = SelectionCurve("")
190            c.setData(xs,ys)
191            c.attach(self.graph)
192            self.graph.selectionCurveList.append(c)
193
194    # ##############################################################################################################################################################
195    # SCATTERPLOT SIGNALS
196    # ##############################################################################################################################################################
197
198    def resetGraphData(self):
199        self.graph.rescaleData()
200        self.majorUpdateGraph()
201
202    # receive new data and update all fields
203    def setData(self, data):
204        if data is not None and (len(data) == 0 or len(data.domain) == 0):
205            data = None
206        if self.data and data and self.data.checksum() == data.checksum():
207            return    # check if the new data set is the same as the old one
208
209        self.closeContext()
210        sameDomain = self.data and data and data.domain.checksum() == self.data.domain.checksum() # preserve attribute choice if the domain is the same
211        self.data = data
212        self.vizrank.clearResults()
213        if not sameDomain:
214            self.initAttrValues()
215        self.graph.insideColors = None
216        self.classificationResults = None
217        self.outlierValues = None
218        self.openContext("", self.data)
219
220    # set an example table with a data subset subset of the data. if called by a visual classifier, the update parameter will be 0
221    def setSubsetData(self, subsetData):
222        self.subsetData = subsetData
223        self.vizrank.clearArguments()
224
225    # this is called by OWBaseWidget after setData and setSubsetData are called. this way the graph is updated only once
226    def handleNewSignals(self):
227        self.graph.setData(self.data, self.subsetData)
228        self.vizrank.resetDialog()
229        if self.attributeSelectionList and 0 not in [self.graph.attributeNameIndex.has_key(attr) for attr in self.attributeSelectionList]:
230            self.attrX = self.attributeSelectionList[0]
231            self.attrY = self.attributeSelectionList[1]
232        self.attributeSelectionList = None
233        self.updateGraph()
234        self.sendSelections()
235
236
237    # receive information about which attributes we want to show on x and y axis
238    def setShownAttributes(self, list):
239        if list and len(list[:2]) == 2:
240            self.attributeSelectionList = list[:2]
241        else:
242            self.attributeSelectionList = None
243
244
245    # visualize the results of the classification
246    def setTestResults(self, results):
247        self.classificationResults = None
248        if isinstance(results, orngTest.ExperimentResults) and len(results.results) > 0 and len(results.results[0].probabilities) > 0:
249            self.classificationResults = [results.results[i].probabilities[0][results.results[i].actualClass] for i in range(len(results.results))]
250            self.classificationResults = (self.classificationResults, "Probability of correct classification = %.2f%%")
251
252
253    # set the learning method to be used in VizRank
254    def setVizRankLearner(self, learner):
255        self.vizrank.externalLearner = learner
256
257    # send signals with selected and unselected examples as two datasets
258    def sendSelections(self):
259        (selected, unselected) = self.graph.getSelectionsAsExampleTables([self.attrX, self.attrY])
260        self.send("Selected Data",selected)
261        self.send("Other Data",unselected)
262
263
264    # ##############################################################################################################################################################
265    # CALLBACKS FROM VIZRANK DIALOG
266    # ##############################################################################################################################################################
267
268    def showSelectedAttributes(self):
269        val = self.vizrank.getSelectedProjection()
270        if not val: return
271        if self.data.domain.classVar:
272            self.attrColor = self.data.domain.classVar.name
273        self.majorUpdateGraph(val[3])
274
275    # ##############################################################################################################################################################
276    # ATTRIBUTE SELECTION
277    # ##############################################################################################################################################################
278
279    def getShownAttributeList(self):
280        return [self.attrX, self.attrY]
281
282    def initAttrValues(self):
283        self.attrXCombo.clear()
284        self.attrYCombo.clear()
285        self.attrColorCombo.clear()
286        self.attrLabelCombo.clear()
287        self.attrShapeCombo.clear()
288        self.attrSizeCombo.clear()
289
290        if not self.data: return
291
292        self.attrColorCombo.addItem("(Same color)")
293        self.attrLabelCombo.addItem("(No labels)")
294        self.attrShapeCombo.addItem("(Same shape)")
295        self.attrSizeCombo.addItem("(Same size)")
296
297        #labels are usually chosen from meta variables, put them on top
298        for metavar in [self.data.domain.getmeta(mykey) for mykey in self.data.domain.getmetas().keys()]:
299            self.attrLabelCombo.addItem(self.icons[metavar.varType], metavar.name)
300
301        contList = []
302        discList = []
303        for attr in self.data.domain:
304            if attr.varType in [orange.VarTypes.Discrete, orange.VarTypes.Continuous]:
305                self.attrXCombo.addItem(self.icons[attr.varType], attr.name)
306                self.attrYCombo.addItem(self.icons[attr.varType], attr.name)
307                self.attrColorCombo.addItem(self.icons[attr.varType], attr.name)
308                self.attrSizeCombo.addItem(self.icons[attr.varType], attr.name)
309            if attr.varType == orange.VarTypes.Discrete: 
310                self.attrShapeCombo.addItem(self.icons[attr.varType], attr.name)
311            self.attrLabelCombo.addItem(self.icons[attr.varType], attr.name)
312
313        self.attrX = str(self.attrXCombo.itemText(0))
314        if self.attrYCombo.count() > 1: self.attrY = str(self.attrYCombo.itemText(1))
315        else:                           self.attrY = str(self.attrYCombo.itemText(0))
316
317        if self.data.domain.classVar and self.data.domain.classVar.varType in [orange.VarTypes.Discrete, orange.VarTypes.Continuous]:
318            self.attrColor = self.data.domain.classVar.name
319        else:
320            self.attrColor = ""
321        self.attrShape = ""
322        self.attrSize= ""
323        self.attrLabel = ""
324
325    def majorUpdateGraph(self, attrList = None, insideColors = None, **args):
326        self.graph.removeAllSelections()
327        self.updateGraph(attrList, insideColors, **args)
328
329    def updateGraph(self, attrList = None, insideColors = None, **args):
330        self.graph.zoomStack = []
331        if not self.graph.haveData:
332            return
333
334        if attrList and len(attrList) == 2:
335            self.attrX = attrList[0]
336            self.attrY = attrList[1]
337
338        if self.graph.dataHasDiscreteClass and (self.vizrank.showKNNCorrectButton.isChecked() or self.vizrank.showKNNWrongButton.isChecked()):
339            kNNExampleAccuracy, probabilities = self.vizrank.kNNClassifyData(self.graph.createProjectionAsExampleTable([self.graph.attributeNameIndex[self.attrX], self.graph.attributeNameIndex[self.attrY]]))
340            if self.vizrank.showKNNCorrectButton.isChecked(): kNNExampleAccuracy = ([1.0 - val for val in kNNExampleAccuracy], "Probability of wrong classification = %.2f%%")
341            else: kNNExampleAccuracy = (kNNExampleAccuracy, "Probability of correct classification = %.2f%%")
342        else:
343            kNNExampleAccuracy = None
344
345        self.graph.insideColors = insideColors or self.classificationResults or kNNExampleAccuracy or self.outlierValues
346        self.graph.updateData(self.attrX, self.attrY, self.attrColor, self.attrShape, self.attrSize, self.attrLabel)
347
348
349    # ##############################################################################################################################################################
350    # SCATTERPLOT SETTINGS
351    # ##############################################################################################################################################################
352    def saveSettings(self):
353        OWWidget.saveSettings(self)
354        self.vizrank.saveSettings()
355
356    #update status on progress bar - gets called by OWScatterplotGraph
357    def updateProgress(self, current, total):
358        self.progressBar.setTotalSteps(total)
359        self.progressBar.setProgress(current)
360       
361    def setShowGridlines(self):
362        self.graph.enableGridXB(self.showGridlines)
363        self.graph.enableGridYL(self.showGridlines)
364
365    def selectionChanged(self):
366        self.zoomSelectToolbar.buttons[OWPlotGUI.SendSelection].setEnabled(not self.autoSendSelection)
367        if self.autoSendSelection:
368            self.sendSelections()
369
370    def setColors(self):
371        dlg = self.createColorDialog()
372        if dlg.exec_():
373            self.colorSettings = dlg.getColorSchemas()
374            self.selectedSchemaIndex = dlg.selectedSchemaIndex
375            self.graph.contPalette = dlg.getContinuousPalette("contPalette")
376            self.graph.discPalette = dlg.getDiscretePalette("discPalette")
377            self.graph.setCanvasBackground(dlg.getColor("Canvas"))
378            self.graph.setGridColor(dlg.getColor("Grid"))
379            self.updateGraph()
380
381    def createColorDialog(self):
382        c = OWColorPalette.ColorPaletteDlg(self, "Color Palette")
383        c.createDiscretePalette("discPalette", "Discrete Palette")
384        c.createContinuousPalette("contPalette", "Continuous Palette")
385        box = c.createBox("otherColors", "Other Colors")
386        c.createColorButton(box, "Canvas", "Canvas color", self.graph.color(OWPalette.Canvas))
387        box.layout().addSpacing(5)
388        c.createColorButton(box, "Grid", "Grid color", self.graph.color(OWPalette.Grid))
389        box.layout().addSpacing(5)
390        c.setColorSchemas(self.colorSettings, self.selectedSchemaIndex)
391        return c
392
393    def closeEvent(self, ce):
394        self.vizrank.close()
395        OWWidget.closeEvent(self, ce)
396
397
398    def sendReport(self):
399        self.startReport("%s [%s - %s]" % (self.windowTitle(), self.attrX, self.attrY))
400        self.reportSettings("Visualized attributes",
401                            [("X", self.attrX),
402                             ("Y", self.attrY),
403                             self.attrColor and ("Color", self.attrColor),
404                             self.attrLabel and ("Label", self.attrLabel),
405                             self.attrShape and ("Shape", self.attrShape),
406                             self.attrSize and ("Size", self.attrSize)])
407        self.reportSettings("Settings",
408                            [("Symbol size", self.graph.pointWidth),
409                             ("Transparency", self.graph.alphaValue),
410                             ("Jittering", self.graph.jitterSize),
411                             ("Jitter continuous attributes", OWGUI.YesNo[self.graph.jitterContinuous])])
412        self.reportSection("Graph")
413        self.reportImage(self.graph.saveToFileDirect, QSize(400, 400))
414
415#test widget appearance
416if __name__=="__main__":
417    a=QApplication(sys.argv)
418    ow=OWScatterPlotQt()
419    ow.show()
420    data = orange.ExampleTable(r"../../doc/datasets/brown-selected.tab")
421    ow.setData(data)
422    #ow.setData(orange.ExampleTable("..\\..\\doc\\datasets\\wine.tab"))
423    ow.handleNewSignals()
424    a.exec_()
425    #save settings
426    ow.saveSettings()
Note: See TracBrowser for help on using the repository browser.