source: orange/Orange/OrangeWidgets/Visualize/OWLinProj.py @ 10913:1b281e538a8f

Revision 10913:1b281e538a8f, 20.0 KB checked in by mstajdohar, 23 months ago (diff)

Fixed Radviz jittering. Before, points in the center were scattered all around the plot.

Line 
1"""
2<name>Linear Projection</name>
3<description>Create a linear projection.</description>
4<contact>Gregor Leban (gregor.leban@fri.uni-lj.si)</contact>
5<icon>icons/LinearProjection.png</icon>
6<priority>2000</priority>
7"""
8# LinProj.py
9#
10# Show a linear projection of the data
11#
12from OWVisWidget import *
13from OWLinProjGraph import *
14from OWkNNOptimization import OWVizRank
15from OWFreeVizOptimization import *
16import OWToolbars, OWGUI, orngTest
17import orngVisFuncts, OWColorPalette
18import orngVizRank
19
20###########################################################################################
21##### WIDGET : Linear Projection
22###########################################################################################
23class OWLinProj(OWVisWidget):
24    settingsList = ["graph.pointWidth", "graph.jitterSize", "graph.showFilledSymbols", "graph.scaleFactor",
25                    "graph.showLegend", "graph.useDifferentSymbols", "autoSendSelection", "graph.useDifferentColors", "graph.showValueLines",
26                    "graph.tooltipKind", "graph.tooltipValue", "toolbarSelection", "graph.alphaValue",
27                    "graph.showProbabilities", "graph.squareGranularity", "graph.spaceBetweenCells", "graph.useAntialiasing"
28                    "valueScalingType", "showAllAttributes", "colorSettings", "selectedSchemaIndex", "addProjectedPositions"]
29    jitterSizeNums = [0.0, 0.01, 0.1, 0.5, 1, 2, 3, 4, 5, 7, 10, 15, 20]
30
31    contextHandlers = {"": DomainContextHandler("", [ContextField("shownAttributes", DomainContextHandler.RequiredList, selected="selectedShown", reservoir="hiddenAttributes")])}
32
33    def __init__(self,parent=None, signalManager = None, name = "Linear Projection", graphClass = None):
34        OWVisWidget.__init__(self, parent, signalManager, name, TRUE)
35
36        self.inputs = [("Data", ExampleTable, self.setData, Default),
37                       ("Data Subset", ExampleTable, self.setSubsetData),
38                       ("Features", AttributeList, self.setShownAttributes),
39                       ("Evaluation Results", orngTest.ExperimentResults, self.setTestResults),
40                       ("VizRank Learner", orange.Learner, self.setVizRankLearner),
41                       ("Distances", orange.SymMatrix, self.setDistances)]
42        self.outputs = [("Selected Data", ExampleTable), ("Other Data", ExampleTable), ("Features", AttributeList), ("FreeViz Learner", orange.Learner)]
43
44        # local variables
45        self.showAllAttributes = 0
46        self.valueScalingType = 0
47        self.autoSendSelection = 1
48        self.data = None
49        self.subsetData = None
50        self.distances = None
51        self.toolbarSelection = 0
52        self.classificationResults = None
53        self.outlierValues = None
54        self.attributeSelectionList = None
55        self.colorSettings = None
56        self.selectedSchemaIndex = 0
57        self.addProjectedPositions = 0
58        self.resetAnchors = 0
59
60        #add a graph widget
61        if graphClass:
62            self.graph = graphClass(self, self.mainArea, name)
63        else:
64            self.graph = OWLinProjGraph(self, self.mainArea, name)
65        self.mainArea.layout().addWidget(self.graph)
66
67        # graph variables
68        self.graph.manualPositioning = 0
69        self.graph.hideRadius = 0
70        self.graph.showAnchors = 1
71        self.graph.jitterContinuous = 0
72        self.graph.showProbabilities = 0
73        self.graph.useDifferentSymbols = 0
74        self.graph.useDifferentColors = 1
75        self.graph.tooltipKind = 0
76        self.graph.tooltipValue = 0
77        self.graph.scaleFactor = 1.0
78        self.graph.squareGranularity = 3
79        self.graph.spaceBetweenCells = 1
80        self.graph.showAxisScale = 0
81        self.graph.showValueLines = 0
82        self.graph.valueLineLength = 5
83
84        #load settings
85        self.loadSettings()
86
87##        # cluster dialog
88##        self.clusterDlg = ClusterOptimization(self, self.signalManager, self.graph, name)
89##        self.graph.clusterOptimization = self.clusterDlg
90
91        # optimization dialog
92        if name.lower() == "radviz":
93            self.vizrank = OWVizRank(self, self.signalManager, self.graph, orngVizRank.RADVIZ, name)
94            self.connect(self.graphButton, SIGNAL("clicked()"), self.saveToFile)
95        elif name.lower() == "polyviz":
96            self.vizrank = OWVizRank(self, self.signalManager, self.graph, orngVizRank.POLYVIZ, name)
97            self.connect(self.graphButton, SIGNAL("clicked()"), self.graph.saveToFile)
98        else:
99            self.vizrank = OWVizRank(self, self.signalManager, self.graph, orngVizRank.LINEAR_PROJECTION, name)
100            self.connect(self.graphButton, SIGNAL("clicked()"), self.saveToFile)
101
102        self.optimizationDlg = self.vizrank  # for backward compatibility
103
104        self.graph.normalizeExamples = (name.lower() == "radviz")       # ignore settings!! if we have radviz then normalize, otherwise not.
105
106        #GUI
107        # add a settings dialog and initialize its values
108
109        self.tabs = OWGUI.tabWidget(self.controlArea)
110        self.GeneralTab = OWGUI.createTabPage(self.tabs, "Main")
111        self.SettingsTab = OWGUI.createTabPage(self.tabs, "Settings", canScroll = 1)
112
113        #add controls to self.controlArea widget
114        self.createShowHiddenLists(self.GeneralTab, callback = self.updateGraphAndAnchors)
115
116        self.optimizationButtons = OWGUI.widgetBox(self.GeneralTab, "Optimization Dialogs", orientation = "horizontal")
117        self.vizrankButton = 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)
118        self.wdChildDialogs = [self.vizrank]    # used when running widget debugging
119
120        # freeviz dialog
121        if name.lower() in ["linear projection", "radviz"]:
122            self.freeVizDlg = FreeVizOptimization(self, self.signalManager, self.graph, name)
123            self.wdChildDialogs.append(self.freeVizDlg)
124            self.freeVizDlgButton = OWGUI.button(self.optimizationButtons, self, "FreeViz", callback = self.freeVizDlg.reshow, tooltip = "Opens FreeViz dialog, where the position of attribute anchors is optimized so that class separation is improved", debuggingEnabled = 0)
125            if name.lower() == "linear projection":
126                self.freeVizLearner = FreeVizLearner(self.freeVizDlg)
127                self.send("FreeViz Learner", self.freeVizLearner)
128
129##        self.clusterDetectionDlgButton = OWGUI.button(self.optimizationButtons, self, "Cluster", callback = self.clusterDlg.reshow, debuggingEnabled = 0)
130##        self.vizrankButton.setMaximumWidth(63)
131##        self.clusterDetectionDlgButton.setMaximumWidth(63)
132##        self.freeVizDlgButton.setMaximumWidth(63)
133##        self.connect(self.clusterDlg.startOptimizationButton , SIGNAL("clicked()"), self.optimizeClusters)
134##        self.connect(self.clusterDlg.resultList, SIGNAL("selectionChanged()"),self.showSelectedCluster)
135
136        self.zoomSelectToolbar = OWToolbars.ZoomSelectToolbar(self, self.GeneralTab, self.graph, self.autoSendSelection)
137        self.graph.autoSendSelectionCallback = self.selectionChanged
138        self.connect(self.zoomSelectToolbar.buttonSendSelections, SIGNAL("clicked()"), self.sendSelections)
139
140        # ####################################
141        # SETTINGS TAB
142        # #####
143        self.extraTopBox = OWGUI.widgetBox(self.SettingsTab, orientation = "vertical")
144        self.extraTopBox.hide()
145
146        box = OWGUI.widgetBox(self.SettingsTab, "Point Properties")
147        OWGUI.hSlider(box, self, 'graph.pointWidth', label = "Size: ", minValue=1, maxValue=20, step=1, callback = self.updateGraph)
148        OWGUI.hSlider(box, self, 'graph.alphaValue', label = "Transparency: ", minValue=0, maxValue=255, step=10, callback = self.updateGraph)
149
150        box = OWGUI.widgetBox(self.SettingsTab, "Jittering Options")
151        OWGUI.comboBoxWithCaption(box, self, "graph.jitterSize", 'Jittering size (% of range):', callback = self.resetGraphData, items = self.jitterSizeNums, sendSelectedValue = 1, valueType = float)
152        OWGUI.checkBox(box, self, 'graph.jitterContinuous', 'Jitter continuous attributes', callback = self.resetGraphData, tooltip = "Does jittering apply also on continuous attributes?")
153
154        box = OWGUI.widgetBox(self.SettingsTab, "Scaling Options")
155        OWGUI.qwtHSlider(box, self, "graph.scaleFactor", label = 'Inflate points by: ', minValue=1.0, maxValue= 10.0, step=0.1, callback = self.updateGraph, tooltip="If points lie too much together you can expand their position to improve perception", maxWidth = 90)
156
157        box = OWGUI.widgetBox(self.SettingsTab, "General Graph Settings")
158        #OWGUI.checkBox(box, self, 'graph.normalizeExamples', 'Normalize examples', callback = self.updateGraph)
159        OWGUI.checkBox(box, self, 'graph.showLegend', 'Show legend', callback = self.updateGraph)
160        bbox = OWGUI.widgetBox(box, orientation = "horizontal")
161        OWGUI.checkBox(bbox, self, 'graph.showValueLines', 'Show value lines  ', callback = self.updateGraph)
162        OWGUI.qwtHSlider(bbox, self, 'graph.valueLineLength', minValue=1, maxValue=10, step=1, callback = self.updateGraph, showValueLabel = 0)
163        OWGUI.checkBox(box, self, 'graph.useDifferentSymbols', 'Use different symbols', callback = self.updateGraph, tooltip = "Show different class values using different symbols")
164        OWGUI.checkBox(box, self, 'graph.useDifferentColors', 'Use different colors', callback = self.updateGraph, tooltip = "Show different class values using different colors")
165        OWGUI.checkBox(box, self, 'graph.showFilledSymbols', 'Show filled symbols', callback = self.updateGraph)
166        OWGUI.checkBox(box, self, 'graph.useAntialiasing', 'Use antialiasing', callback = self.updateGraph)
167        wbox = OWGUI.widgetBox(box, orientation = "horizontal")
168        OWGUI.checkBox(wbox, self, 'graph.showProbabilities', 'Show probabilities'+'  ', callback = self.updateGraph, tooltip = "Show a background image with class probabilities")
169        smallWidget = OWGUI.SmallWidgetLabel(wbox, pixmap = 1, box = "Advanced settings", tooltip = "Show advanced settings")
170        OWGUI.rubber(wbox)
171
172        box = OWGUI.widgetBox(self.SettingsTab, "Colors", orientation = "horizontal")
173        OWGUI.button(box, self, "Colors", self.setColors, tooltip = "Set the canvas background color and color palette for coloring variables", debuggingEnabled = 0)
174
175        box = OWGUI.widgetBox(self.SettingsTab, "Tooltips Settings")
176        OWGUI.comboBox(box, self, "graph.tooltipKind", items = ["Show line tooltips", "Show visible attributes", "Show all attributes"], callback = self.updateGraph)
177        OWGUI.comboBox(box, self, "graph.tooltipValue", items = ["Tooltips show data values", "Tooltips show spring values"], callback = self.updateGraph, tooltip = "Do you wish that tooltips would show you original values of visualized attributes or the 'spring' values (values between 0 and 1). \nSpring values are scaled values that are used for determining the position of shown points. Observing these values will therefore enable you to \nunderstand why the points are placed where they are.")
178
179        box = OWGUI.widgetBox(self.SettingsTab, "Auto Send Selected Data When...")
180        OWGUI.checkBox(box, self, 'autoSendSelection', 'Adding/Removing selection areas', callback = self.selectionChanged, tooltip = "Send selected data whenever a selection area is added or removed")
181        OWGUI.checkBox(box, self, 'graph.sendSelectionOnUpdate', 'Moving/Resizing selection areas', tooltip = "Send selected data when a user moves or resizes an existing selection area")
182        OWGUI.comboBox(box, self, "addProjectedPositions", items = ["Do not modify the domain", "Append projection as attributes", "Append projection as meta attributes"], callback = self.sendSelections)
183        self.selectionChanged()
184
185        box = OWGUI.widgetBox(smallWidget.widget, orientation = "horizontal")
186        OWGUI.widgetLabel(box, "Granularity:  ")
187        OWGUI.hSlider(box, self, 'graph.squareGranularity', minValue=1, maxValue=10, step=1, callback = self.updateGraph)
188
189        box = OWGUI.widgetBox(smallWidget.widget, orientation = "horizontal")
190        OWGUI.checkBox(box, self, 'graph.spaceBetweenCells', 'Show space between cells', callback = self.updateGraph)
191
192        self.SettingsTab.layout().addStretch(100)
193
194        self.icons = self.createAttributeIconDict()
195        self.debugSettings = ["hiddenAttributes", "shownAttributes"]
196
197        dlg = self.createColorDialog()
198        self.graph.contPalette = dlg.getContinuousPalette("contPalette")
199        self.graph.discPalette = dlg.getDiscretePalette("discPalette")
200        self.graph.setCanvasBackground(dlg.getColor("Canvas"))
201
202        apply([self.zoomSelectToolbar.actionZooming, self.zoomSelectToolbar.actionRectangleSelection, self.zoomSelectToolbar.actionPolygonSelection][self.toolbarSelection], [])
203
204        self.cbShowAllAttributes()      # update list boxes based on the check box value
205
206        self.resize(900, 700)
207
208    def saveToFile(self):
209        self.graph.saveToFile([("Save PicTex", self.graph.savePicTeX)])
210
211    # #########################
212    # KNN OPTIMIZATION BUTTON EVENTS
213    # #########################
214    def saveCurrentProjection(self):
215        qname = QFileDialog.getSaveFileName(self, "Save File",  os.path.realpath(".") + "/Linear_projection.tab", "Orange Example Table (*.tab)")
216        if qname.isEmpty(): return
217        name = unicode(qname)
218        if len(name) < 4 or name[-4] != ".":
219            name = name + ".tab"
220        self.graph.saveProjectionAsTabData(name, self.getShownAttributeList())
221
222
223    # send signals with selected and unselected examples as two datasets
224    def sendSelections(self):
225        if not self.data: return
226        (selected, unselected) = self.graph.getSelectionsAsExampleTables(self.getShownAttributeList(), addProjectedPositions = self.addProjectedPositions)
227
228        self.send("Selected Data", selected)
229        self.send("Other Data", unselected)
230
231    def sendShownAttributes(self):
232        self.send("Features", [a[0] for a in self.shownAttributes])
233
234    # show selected interesting projection
235    def showSelectedAttributes(self):
236        val = self.vizrank.getSelectedProjection()
237        if val:
238            (accuracy, other_results, tableLen, attrList, tryIndex, generalDict) = val
239            self.updateGraph(attrList, setAnchors= 1, XAnchors = generalDict.get("XAnchors"), YAnchors = generalDict.get("YAnchors"))
240            self.graph.removeAllSelections()
241
242
243    def updateGraphAndAnchors(self):
244        self.updateGraph(setAnchors = 1)
245
246    def updateGraph(self, attrList = None, setAnchors = 0, insideColors = None, **args):
247        if not attrList:
248            attrList = self.getShownAttributeList()
249        else:
250            self.setShownAttributeList(attrList)
251
252        self.graph.showKNN = 0
253        if self.graph.dataHasDiscreteClass:
254            self.graph.showKNN = (self.vizrank.showKNNCorrectButton.isChecked() and 1) or (self.vizrank.showKNNCorrectButton.isChecked() and 2)
255
256        self.graph.insideColors = insideColors or self.classificationResults or self.outlierValues
257        self.graph.updateData(attrList, setAnchors, **args)
258
259
260    # ###############################################################################################################
261    # INPUT SIGNALS
262
263    # receive new data and update all fields
264    def setData(self, data):
265        if data is not None and (len(data) == 0 or len(data.domain) == 0):
266            data = None
267        if self.data and data and self.data.checksum() == data.checksum():
268            return    # check if the new data set is the same as the old one
269
270        self.closeContext()
271        sameDomain = self.data and data and data.domain.checksum() == self.data.domain.checksum() # preserve attribute choice if the domain is the same
272        self.resetAnchors = not sameDomain
273        self.data = data
274        self.classificationResults = None
275        self.outlierValues = None
276        self.vizrank.clearResults()
277        if hasattr(self, "freeVizDlg"):
278            self.freeVizDlg.clearData()
279##        self.clusterDlg.setData(data)
280        if not sameDomain:
281            self.setShownAttributeList(self.attributeSelectionList)
282        self.openContext("", self.data)
283        self.resetAttrManipulation()
284
285
286    def setSubsetData(self, subsetData):
287        self.subsetData = subsetData
288        self.vizrank.clearArguments()
289
290    def setDistances(self, distances):
291        self.distances = distances
292       
293    # attribute selection signal - info about which attributes to show
294    def setShownAttributes(self, attributeSelectionList):
295        self.attributeSelectionList = attributeSelectionList
296        self.resetAnchors = 1
297
298
299    # this is called by OWBaseWidget after setData and setSubsetData are called. this way the graph is updated only once
300    def handleNewSignals(self):
301        self.graph.setData(self.data, self.subsetData)
302        self.graph.clear()
303        self.vizrank.resetDialog()
304        if self.attributeSelectionList and 0 not in [self.graph.attributeNameIndex.has_key(attr) for attr in self.attributeSelectionList]:
305            self.setShownAttributeList(self.attributeSelectionList)
306        self.attributeSelectionList = None
307        self.updateGraph(setAnchors = self.resetAnchors)
308        self.sendSelections()
309        self.resetAnchors = 0
310
311
312    # visualize the results of the classification
313    def setTestResults(self, results):
314        self.classificationResults = None
315        if isinstance(results, orngTest.ExperimentResults) and len(results.results) > 0 and len(results.results[0].probabilities) > 0:
316            self.classificationResults = ([results.results[i].probabilities[0][results.results[i].actualClass] for i in range(len(results.results))], "Probability of correct classificatioin = %.2f%%")
317        self.resetAnchors += 1
318
319
320    # set the learning method to be used in VizRank
321    def setVizRankLearner(self, learner):
322        self.vizrank.externalLearner = learner
323
324    # EVENTS
325    def resetGraphData(self):
326
327        tmp = self.graph.jitter_size      # do not jitter Radviz before final projection,
328        if self.graph.normalize_examples: # or points in the center will be scattered all over the place
329            self.graph.jitter_size = 0
330
331        self.graph.rescaleData()
332        self.graph.jitter_size = tmp
333
334        self.updateGraph()
335
336    def selectionChanged(self):
337        self.zoomSelectToolbar.buttonSendSelections.setEnabled(not self.autoSendSelection)
338        if self.autoSendSelection:
339            self.sendSelections()
340
341    def setColors(self):
342        dlg = self.createColorDialog()
343        if dlg.exec_():
344            self.colorSettings = dlg.getColorSchemas()
345            self.selectedSchemaIndex = dlg.selectedSchemaIndex
346            self.graph.contPalette = dlg.getContinuousPalette("contPalette")
347            self.graph.discPalette = dlg.getDiscretePalette("discPalette")
348            self.graph.setCanvasBackground(dlg.getColor("Canvas"))
349            self.updateGraph()
350
351    def createColorDialog(self):
352        c = OWColorPalette.ColorPaletteDlg(self, "Color palette")
353        c.createDiscretePalette("discPalette", "Discrete Palette")
354        c.createContinuousPalette("contPalette", "Continuous Palette")
355        box = c.createBox("otherColors", "Other Colors")
356        c.createColorButton(box, "Canvas", "Canvas color", QColor(Qt.white))
357        c.setColorSchemas(self.colorSettings, self.selectedSchemaIndex)
358        return c
359
360    def saveSettings(self):
361        OWWidget.saveSettings(self)
362        self.vizrank.saveSettings()
363        if hasattr(self, "freeVizDlg"):
364            self.freeVizDlg.saveSettings()
365
366    def hideEvent(self, ev):
367        self.vizrank.hide()
368        if hasattr(self, "freeVizDlg"):
369            self.freeVizDlg.hide()
370        OWVisWidget.hideEvent(self, ev)
371       
372
373    def sendReport(self):
374        self.reportImage(self.graph.saveToFileDirect, QSize(500, 500))
375       
376#test widget appearance
377if __name__=="__main__":
378    a=QApplication(sys.argv)
379    ow=OWLinProj()
380    ow.show()
381    #ow.setData(orange.ExampleTable("..\\..\\doc\\datasets\\wine.tab"))
382    data = orange.ExampleTable(r"e:\Development\Orange Datasets\brown\brown-selected.tab")
383    ow.setData(data)
384    ow.handleNewSignals()
385    a.exec_()
386
Note: See TracBrowser for help on using the repository browser.