source: orange/orange/OrangeWidgets/Visualize Qt/OWParallelCoordinatesQt.py @ 8735:065a34c267f2

Revision 8735:065a34c267f2, 31.3 KB checked in by matejd <matejd@…>, 3 years ago (diff)

Moved over code from qtgraph branch, turned primitives into a module (plot.primitives); added Visualize Qt folder to setup.py packages

Line 
1"""
2<name>Parallel Coordinates (Qt)</name>
3<description>Parallel coordinates (multiattribute) visualization.</description>
4<contact>Gregor Leban (gregor.leban@fri.uni-lj.si)</contact>
5<icon>icons/ParallelCoordinates.png</icon>
6<priority>170</priority>
7"""
8# ParallelCoordinates.py
9#
10# Show data using parallel coordinates visualization method
11#
12from OWVisWidget import *
13from OWParallelGraphQt import *
14import OWToolbars, OWGUI, OWColorPalette, orngVisFuncts
15from sys import getrecursionlimit, setrecursionlimit
16
17###########################################################################################
18##### WIDGET : Parallel coordinates visualization
19###########################################################################################
20class OWParallelCoordinatesQt(OWVisWidget):
21    settingsList = ["graph.jitterSize", "graph.showDistributions",
22                    "graph.showAttrValues",
23                    "graph.useSplines", "graph.alphaValue", "graph.alphaValue2", "graph.show_legend", "autoSendSelection",
24                    "toolbarSelection", "graph.showStatistics", "colorSettings", "selectedSchemaIndex", "showAllAttributes"]
25    jitterSizeNums = [0, 2,  5,  10, 15, 20, 30]
26    contextHandlers = {"": DomainContextHandler("", [ContextField("shownAttributes", DomainContextHandler.RequiredList, selected="selectedShown", reservoir="hiddenAttributes")])}
27
28    def __init__(self,parent=None, signalManager = None):
29        OWWidget.__init__(self, parent, signalManager, "Parallel Coordinates (Qt)", TRUE)
30
31        #add a graph widget
32        self.graph = OWParallelGraph(self, self.mainArea)
33        self.mainArea.layout().addWidget(self.graph)
34
35        self.showAllAttributes = 0
36
37        self.inputs = [("Examples", ExampleTable, self.setData, Default), ("Example Subset", ExampleTable, self.setSubsetData), ("Attribute Selection List", AttributeList, self.setShownAttributes)]
38        self.outputs = [("Selected Examples", ExampleTable), ("Unselected Examples", ExampleTable), ("Attribute Selection List", AttributeList)]
39
40        #set default settings
41        self.data = None
42        self.subsetData = None
43        self.autoSendSelection = 1
44        self.attrDiscOrder = "Unordered"
45        self.attrContOrder = "Unordered"
46        self.projections = None
47        self.correlationDict = {}
48        self.middleLabels = "Correlations"
49        self.attributeSelectionList = None
50        self.toolbarSelection = 0
51        self.colorSettings = None
52        self.selectedSchemaIndex = 0
53
54        self.graph.jitterSize = 10
55        self.graph.showDistributions = 1
56        self.graph.showStatistics = 0
57        self.graph.showAttrValues = 1
58        self.graph.useSplines = 0
59        self.graph.show_legend = 1
60
61        #load settings
62        self.loadSettings()
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")
68
69        self.createShowHiddenLists(self.GeneralTab, callback = self.updateGraph)
70        self.connect(self.shownAttribsLB, SIGNAL('itemDoubleClicked(QListWidgetItem*)'), self.flipAttribute)
71
72        self.optimizationDlg = ParallelOptimization(self, signalManager = self.signalManager)
73        self.optimizationDlgButton = OWGUI.button(self.GeneralTab, self, "Optimization Dialog", callback = self.optimizationDlg.reshow, debuggingEnabled = 0)
74
75        self.zoomSelectToolbar = OWToolbars.ZoomSelectToolbar(self, self.GeneralTab, self.graph, self.autoSendSelection, buttons = (1, 2, 0, 7, 8))
76        self.connect(self.zoomSelectToolbar.buttonSendSelections, SIGNAL("clicked()"), self.sendSelections)
77
78        #connect controls to appropriate functions
79        self.connect(self.graphButton, SIGNAL("clicked()"), self.graph.saveToFile)
80
81        # ####################################
82        # SETTINGS functionality
83        box = OWGUI.widgetBox(self.SettingsTab, "Transparency")
84        OWGUI.hSlider(box, self, 'graph.alphaValue', label = "Examples: ", minValue=0, maxValue=255, step=10, callback = self.updateGraph, tooltip = "Alpha value used for drawing example lines")
85        OWGUI.hSlider(box, self, 'graph.alphaValue2', label = "Rest:     ", minValue=0, maxValue=255, step=10, callback = self.updateGraph, tooltip = "Alpha value used to draw statistics, example subsets, ...")
86
87        box = OWGUI.widgetBox(self.SettingsTab, "Jittering Options")
88        OWGUI.comboBox(box, self, "graph.jitterSize", label = 'Jittering size (% of size):  ', orientation='horizontal', callback = self.setJitteringSize, items = self.jitterSizeNums, sendSelectedValue = 1, valueType = float)
89
90        # visual settings
91        box = OWGUI.widgetBox(self.SettingsTab, "Visual Settings")
92
93        OWGUI.checkBox(box, self, 'graph.showAttrValues', 'Show attribute values', callback = self.updateGraph)
94        OWGUI.checkBox(box, self, 'graph.useSplines', 'Show splines', callback = self.updateGraph, tooltip  = "Show lines using splines")
95       
96        self.graph.gui.show_legend_check_box(box)
97
98        box = OWGUI.widgetBox(self.SettingsTab, "Axis Distance")
99        resizeColsBox = OWGUI.widgetBox(box, 0, "horizontal", 0)
100        OWGUI.label(resizeColsBox, self, "Increase/decrease distance: ")
101        b = OWGUI.toolButton(resizeColsBox, self, "+", callback=self.increaseAxesDistance, tooltip = "Increase the distance between the axes", width=30, height = 20)
102        b = OWGUI.toolButton(resizeColsBox, self, "-", callback=self.decreaseAxesDistance, tooltip = "Decrease the distance between the axes", width=30, height = 20)
103        OWGUI.rubber(resizeColsBox)
104        OWGUI.checkBox(box, self, "graph.autoUpdateAxes", "Auto scale X axis", tooltip = "Auto scale X axis to show all visualized attributes", callback = self.updateGraph)
105
106        box = OWGUI.widgetBox(self.SettingsTab, "Statistical Information")
107        OWGUI.comboBox(box, self, "graph.showStatistics", label = "Statistics: ", orientation = "horizontal", labelWidth=90, items = ["No statistics", "Means, deviations", "Median, quartiles"], callback = self.updateGraph, sendSelectedValue = 0, valueType = int)
108        OWGUI.comboBox(box, self, "middleLabels", label = "Middle labels: ", orientation="horizontal", labelWidth=90, items = ["No labels", "Correlations", "VizRank"], callback = self.updateGraph, tooltip = "The information do you wish to view on top in the middle of coordinate axes", sendSelectedValue = 1, valueType = str)
109        OWGUI.checkBox(box, self, 'graph.showDistributions', 'Show distributions', callback = self.updateGraph, tooltip = "Show bars with distribution of class values (only for discrete attributes)")
110
111        box = OWGUI.widgetBox(self.SettingsTab, "Colors", orientation = "horizontal")
112        OWGUI.button(box, self, "Set colors", self.setColors, tooltip = "Set the canvas background color and color palette for coloring continuous variables", debuggingEnabled = 0)
113
114        box = OWGUI.widgetBox(self.SettingsTab, "Auto Send Selected Data When...")
115        OWGUI.checkBox(box, self, 'autoSendSelection', 'Adding/Removing selection areas', callback = self.selectionChanged, tooltip = "Send selected data whenever a selection area is added or removed")
116        OWGUI.checkBox(box, self, 'graph.sendSelectionOnUpdate', 'Moving/Resizing selection areas', tooltip = "Send selected data when a user moves or resizes an existing selection area")
117        self.graph.autoSendSelectionCallback = self.selectionChanged
118
119        self.SettingsTab.layout().addStretch(100)
120        self.icons = self.createAttributeIconDict()
121
122        dlg = self.createColorDialog()
123        self.graph.contPalette = dlg.getContinuousPalette("contPalette")
124        self.graph.discPalette = dlg.getDiscretePalette("discPalette")
125        self.graph.setCanvasBackground(dlg.getColor("Canvas"))
126        apply([self.zoomSelectToolbar.actionZooming, self.zoomSelectToolbar.actionRectangleSelection, self.zoomSelectToolbar.actionPolygonSelection][self.toolbarSelection], [])
127        self.cbShowAllAttributes()
128
129        self.resize(900, 700)
130
131    def flipAttribute(self, item):
132        if self.graph.flipAttribute(str(item.text())):
133            self.updateGraph()
134            self.information(0)
135        else:
136            self.information(0, "Didn't flip the attribute. To flip a continuous attribute uncheck 'Global value scaling' checkbox.")
137
138    def updateGraph(self, *args):
139        attrs = self.getShownAttributeList()
140        self.graph.updateData(attrs, self.buildMidLabels(attrs))
141
142
143    def increaseAxesDistance(self):
144    m, M = self.graph.bounds_for_axis(xBottom)
145        if (M-m) == 0:
146            return      # we have not yet updated the axes (self.graph.updateAxes())
147        self.graph.setAxisScale(xBottom, m, M - (M-m)/10., 1)
148        self.graph.replot()
149
150    def decreaseAxesDistance(self):
151    m, M = self.graph.bounds_for_axis(xBottom)
152    if (M-m) == 0:
153            return      # we have not yet updated the axes (self.graph.updateAxes())
154
155        self.graph.setAxisScale(xBottom, m, min(len(self.graph.visualizedAttributes)-1, M + (M-m)/10.), 1)
156        self.graph.replot()
157
158
159    # build a list of strings that will be shown in the middle of the parallel axis
160    def buildMidLabels(self, attrs):
161        labels = []
162        if self.middleLabels == "No labels" or not self.graph.haveData: return None
163        elif self.middleLabels == "Correlations":
164            for i in range(len(attrs)-1):
165                corr = None
166                if self.correlationDict.has_key((attrs[i], attrs[i+1])):   corr = self.correlationDict[(attrs[i], attrs[i+1])]
167                elif self.correlationDict.has_key((attrs[i+1], attrs[i])): corr = self.correlationDict[(attrs[i+1], attrs[i])]
168                else:
169                    try:
170                        corr = orngVisFuncts.computeCorrelation(self.graph.rawData, attrs[i], attrs[i+1])
171                    except:
172                        corr = None
173                    self.correlationDict[(attrs[i], attrs[i+1])] = corr
174                if corr and (self.graph.attributeFlipInfo.get(attrs[i], 0) != self.graph.attributeFlipInfo.get(attrs[i+1], 0)): corr = -corr
175                if corr: labels.append("%2.3f" % (corr))
176                else: labels.append("")
177        elif self.middleLabels == "VizRank":
178            for i in range(len(attrs)-1):
179                val = self.optimizationDlg.getVizRankVal(attrs[i], attrs[i+1])
180                if val: labels.append("%2.2f%%" % (val))
181                else: labels.append("")
182        return labels
183
184
185    # ------------- SIGNALS --------------------------
186    # receive new data and update all fields
187    def setData(self, data):
188        if data and (len(data) == 0 or len(data.domain) == 0):
189            data = None
190        if self.data != None and data != None and self.data.checksum() == data.checksum():
191            return    # check if the new data set is the same as the old one
192
193        self.closeContext()
194        sameDomain = self.data and data and data.domain.checksum() == self.data.domain.checksum() # preserve attribute choice if the domain is the same
195        self.projections = None
196        self.correlationDict = {}
197        self.data = data
198        self.optimizationDlg.clearResults()
199        if not sameDomain:
200            self.setShownAttributeList(self.attributeSelectionList)
201        self.openContext("", self.data)
202        self.resetAttrManipulation()
203
204
205    def setSubsetData(self, subData):
206        self.subsetData = subData
207
208
209    # attribute selection signal - list of attributes to show
210    def setShownAttributes(self, attributeSelectionList):
211        self.attributeSelectionList = attributeSelectionList
212
213
214    # this is called by OWBaseWidget after setData and setSubsetData are called. this way the graph is updated only once
215    def handleNewSignals(self):
216        self.graph.setData(self.data, self.subsetData)
217        if self.attributeSelectionList and 0 not in [self.graph.attributeNameIndex.has_key(attr) for attr in self.attributeSelectionList]:
218            self.setShownAttributeList(self.attributeSelectionList)
219        else:
220            self.setShownAttributeList()
221        self.attributeSelectionList = None
222        self.updateGraph()
223        self.sendSelections()
224
225
226    def sendShownAttributes(self, attrList = None):
227        if attrList == None:
228            attrList = self.getShownAttributeList()
229        self.send("Attribute Selection List", attrList)
230
231    def selectionChanged(self):
232        self.zoomSelectToolbar.buttonSendSelections.setEnabled(not self.autoSendSelection)
233        if self.autoSendSelection:
234            self.sendSelections()
235
236    # send signals with selected and unselected examples as two datasets
237    def sendSelections(self):
238        (selected, unselected) = self.graph.getSelectionsAsExampleTables()
239        self.send("Selected Examples", selected)
240        self.send("Unselected Examples", unselected)
241
242
243    # jittering options
244    def setJitteringSize(self):
245        self.graph.rescaleData()
246        self.updateGraph()
247
248    def setColors(self):
249        dlg = self.createColorDialog()
250        if dlg.exec_():
251            self.colorSettings = dlg.getColorSchemas()
252            self.selectedSchemaIndex = dlg.selectedSchemaIndex
253            self.graph.contPalette = dlg.getContinuousPalette("contPalette")
254            self.graph.discPalette = dlg.getDiscretePalette("discPalette")
255            self.graph.setCanvasBackground(dlg.getColor("Canvas"))
256            self.updateGraph()
257
258    def createColorDialog(self):
259        c = OWColorPalette.ColorPaletteDlg(self, "Color Palette")
260        c.createDiscretePalette("discPalette", "Discrete Palette")
261        c.createContinuousPalette("contPalette", "Continuous Palette")
262        box = c.createBox("otherColors", "Other Colors")
263        c.createColorButton(box, "Canvas", "Canvas color", self.graph.color(OWPalette.Canvas))
264        c.setColorSchemas(self.colorSettings, self.selectedSchemaIndex)
265        return c
266
267    def saveSettings(self):
268        OWWidget.saveSettings(self)
269        self.optimizationDlg.saveSettings()
270
271    def closeEvent(self, ce):
272        self.optimizationDlg.hide()
273        OWWidget.closeEvent(self, ce)
274
275    def sendReport(self):
276        self.reportImage(self.graph.saveToFileDirect, QSize(500, 500))
277
278
279
280CORRELATION = 0
281VIZRANK = 1
282#
283# Find attribute subsets that are interesting to visualize using parallel coordinates
284class ParallelOptimization(OWWidget):
285    resultListList = [50, 100, 200, 500, 1000]
286    qualityMeasure =  ["Classification accuracy", "Average correct", "Brier score"]
287    testingMethod = ["Leave one out", "10-fold cross validation", "Test on learning set"]
288
289    settingsList = ["attributeCount", "fileBuffer", "lastSaveDirName", "optimizationMeasure",
290                    "numberOfAttributes", "orderAllAttributes", "optimizationMeasure"]
291
292    def __init__(self, parallelWidget, parent=None, signalManager = None):
293        OWWidget.__init__(self, parent, signalManager, "Parallel Optimization Dialog", FALSE)
294        self.setCaption("Parallel Optimization Dialog")
295        self.parallelWidget = parallelWidget
296
297        self.optimizationMeasure = 0
298        self.attributeCount = 5
299        self.numberOfAttributes = 6
300        self.fileName = ""
301        self.lastSaveDirName = os.getcwd() + "/"
302        self.fileBuffer = []
303        self.projections = []
304        self.allResults = []
305        self.canOptimize = 0
306        self.orderAllAttributes = 1 # do we wish to order all attributes or find just an interesting subset
307        self.worstVal = -1  # used in heuristics to stop the search in uninteresting parts of the graph
308
309        self.loadSettings()
310
311        self.measureBox = OWGUI.radioButtonsInBox(self.controlArea, self, "optimizationMeasure", ["Correlation", "VizRank"], box = "Select optimization measure", callback = self.updateGUI)
312        self.vizrankSettingsBox = OWGUI.widgetBox(self.controlArea, "VizRank settings")
313        self.optimizeBox = OWGUI.widgetBox(self.controlArea, "Optimize")
314        self.manageBox = OWGUI.widgetBox(self.controlArea, "Manage results")
315        self.resultsBox = OWGUI.widgetBox(self.mainArea, "Results")
316
317        self.resultList = OWGUI.listBox(self.resultsBox, self)
318        self.resultList.setMinimumSize(200,200)
319        self.connect(self.resultList, SIGNAL("itemSelectionChanged()"), self.showSelectedAttributes)
320
321        # remove non-existing files
322        names = []
323        for i in range(len(self.fileBuffer)-1, -1, -1):
324            (short, longName) = self.fileBuffer[i]
325            if not os.path.exists(longName):
326                self.fileBuffer.remove((short, longName))
327            else: names.append(short)
328        names.append("(None)")
329        self.fileName = "(None)"
330
331        self.hbox1 = OWGUI.widgetBox(self.vizrankSettingsBox, "VizRank projections file", orientation = "horizontal")
332        self.vizrankFileCombo = OWGUI.comboBox(self.hbox1, self, "fileName", items = names, tooltip = "File that contains information about interestingness of scatterplots \ngenerated by VizRank method in scatterplot widget", callback = self.changeProjectionFile, sendSelectedValue = 1, valueType = str)
333        self.browseButton = OWGUI.button(self.hbox1, self, "...", callback = self.loadProjections)
334        self.browseButton.setMaximumWidth(20)
335
336        self.resultsInfoBox = OWGUI.widgetBox(self.vizrankSettingsBox, "VizRank parameters")
337        self.kNeighborsLabel = OWGUI.widgetLabel(self.resultsInfoBox, "Number of neighbors (k):")
338        self.percentDataUsedLabel = OWGUI.widgetLabel(self.resultsInfoBox, "Percent of data used:")
339        self.testingMethodLabel = OWGUI.widgetLabel(self.resultsInfoBox, "Testing method used:")
340        self.qualityMeasureLabel = OWGUI.widgetLabel(self.resultsInfoBox, "Quality measure used:")
341
342        #self.numberOfAttributesCombo = OWGUI.comboBoxWithCaption(self.optimizeBox, self, "numberOfAttributes", "Number of visualized attributes: ", tooltip = "Projections with this number of attributes will be evaluated", items = [x for x in range(3, 12)], sendSelectedValue = 1, valueType = int)
343        self.allAttributesRadio = QRadioButton("Order all attributes", self.optimizeBox)
344        self.optimizeBox.layout().addWidget(self.allAttributesRadio)
345        self.connect(self.allAttributesRadio, SIGNAL("clicked()"), self.setAllAttributeRadio)
346        box = OWGUI.widgetBox(self.optimizeBox, orientation = "horizontal")
347        self.subsetAttributeRadio = QRadioButton("Find subsets of", box)
348#        self.optimizeBox.layout().addWidget(self.subsetAttributeRadio)
349        box.layout().addWidget(self.subsetAttributeRadio)
350        self.connect(self.subsetAttributeRadio, SIGNAL("clicked()"), self.setSubsetAttributeRadio)
351        self.subsetAttributeEdit = OWGUI.lineEdit(box, self, "numberOfAttributes", valueType = int)
352        self.subsetAttributeEdit.setMaximumWidth(30)
353        label  = OWGUI.widgetLabel(box, "attributes")
354
355        self.startOptimizationButton = OWGUI.button(self.optimizeBox, self, "Start Optimization", callback = self.startOptimization)
356        f = self.startOptimizationButton.font()
357        f.setBold(1)
358        self.startOptimizationButton.setFont(f)
359        self.stopOptimizationButton = OWGUI.button(self.optimizeBox, self, "Stop Evaluation", callback = self.stopOptimizationClick)
360        self.stopOptimizationButton.setFont(f)
361        self.stopOptimizationButton.hide()
362        self.connect(self.stopOptimizationButton , SIGNAL("clicked()"), self.stopOptimizationClick)
363
364
365        self.clearButton = OWGUI.button(self.manageBox, self, "Clear Results", self.clearResults)
366        self.loadButton  = OWGUI.button(self.manageBox, self, "Load", self.loadResults)
367        self.saveButton  = OWGUI.button(self.manageBox, self, "Save", self.saveResults)
368        self.closeButton = OWGUI.button(self.manageBox, self, "Close Dialog", self.hide)
369
370        self.changeProjectionFile()
371        self.updateGUI()
372        if self.orderAllAttributes: self.setAllAttributeRadio()
373        else:                       self.setSubsetAttributeRadio()
374
375    def updateGUI(self):
376        self.vizrankSettingsBox.setEnabled(self.optimizationMeasure)
377
378    # if user clicks new attribute list in optimization dialog, we update shown attributes
379    def showSelectedAttributes(self):
380        attrList = self.getSelectedAttributes()
381        if not attrList: return
382
383        self.parallelWidget.setShownAttributeList(attrList)
384        self.parallelWidget.graph.removeAllSelections()
385
386        self.parallelWidget.middleLabels = (self.optimizationMeasure == VIZRANK and "VizRank") or "Correlations"
387        self.parallelWidget.updateGraph()
388
389    def setAllAttributeRadio(self):
390        self.orderAllAttributes = 1
391        self.allAttributesRadio.setChecked(1)
392        self.subsetAttributeRadio.setChecked(0)
393        self.subsetAttributeEdit.setEnabled(0)
394
395    def setSubsetAttributeRadio(self):
396        self.orderAllAttributes = 0
397        self.allAttributesRadio.setChecked(0)
398        self.subsetAttributeRadio.setChecked(1)
399        self.subsetAttributeEdit.setEnabled(1)
400
401    # return list of selected attributes
402    def getSelectedAttributes(self):
403        if self.resultList.count() == 0 or self.allResults == []:
404            return None
405        return self.allResults[self.resultList.currentRow()][1]
406
407    # called when optimization is in progress
408    def canContinueOptimization(self):
409        return self.canOptimize
410
411    def getWorstVal(self):
412        return self.worstVal
413
414    def stopOptimizationClick(self):
415        self.canOptimize = 0
416
417    # get vizrank value for attributes attr1 and attr2
418    def getVizRankVal(self, attr1, attr2):
419        if not self.projections: return None
420        for (val, [a1, a2]) in self.projections:
421            if (attr1 == a1 and attr2 == a2) or (attr1 == a2 and attr2 == a1): return val
422        return None
423
424    def changeProjectionFile(self):
425        for (short, long) in self.fileBuffer:
426            if short == self.fileName:
427                self.loadProjections(long)
428                return
429
430    # load projections from a file
431    def loadProjections(self, name = None):
432        self.projections = []
433        self.kNeighborsLabel.setText("Number of neighbors (k): " )
434        self.percentDataUsedLabel.setText("Percent of data used:" )
435        self.testingMethodLabel.setText("Testing method used:" )
436        self.qualityMeasureLabel.setText("Quality measure used:" )
437
438        if name == None:
439            name = str(QFileDialog.getOpenFileName(self, "Open Projections",  self.lastSaveDirName, "Interesting projections (*.proj)"))
440            if name == "": return
441
442        dirName, shortFileName = os.path.split(name)
443        self.lastSaveDirName = dirName
444
445        file = open(name, "rt")
446        settings = eval(file.readline()[:-1])
447        if settings.has_key("parentName") and settings["parentName"].lower() != "scatterplot":
448            QMessageBox.critical( None, "Optimization Dialog", 'Unable to load projection file. Only projection file generated by scatterplot is compatible. \nThis file was created using %s method'%(settings["parentName"]), QMessageBox.Ok)
449            file.close()
450            return
451
452        if type(eval(file.readline()[:-1])) != list:    # second line must contain a list of classes that we tried to separate
453            QMessageBox.critical(None,'Old version of projection file','This file was saved with an older version of k-NN Optimization Dialog. The new version of dialog offers \nsome additional functionality and therefore you have to compute the projection quality again.',QMessageBox.Ok)
454            file.close()
455            return
456
457        try:
458            line = file.readline()[:-1]; ind = 0    # first line is a settings line
459            (acc, other_results, lenTable, attrList, tryIndex, strList) = eval(line)
460            if len(attrList) != 2:
461                QMessageBox.information(self, "Incorrect file", "File should contain projections with 2 attributes!", QMessageBox.Ok)
462                file.close()
463                return
464
465            while (line != ""):
466                (acc, other_results, lenTable, attrList, tryIndex, strList) = eval(line)
467                self.projections += [(acc, attrList)]
468                line = file.readline()[:-1]
469        except:
470            self.projections = []
471            file.close()
472            QMessageBox.information(self, "Incorrect file", "Incorrect file format!", QMessageBox.Ok)
473            return
474
475        file.close()
476
477        if (shortFileName, name) in self.fileBuffer:
478            self.fileBuffer.remove((shortFileName, name))
479
480        self.fileBuffer.insert(0, (shortFileName, name))
481
482
483        if len(self.fileBuffer) > 10:
484            self.fileBuffer.remove(self.fileBuffer[-1])
485
486        self.vizrankFileCombo.clear()
487        for i in range(len(self.fileBuffer)):
488            self.vizrankFileCombo.addItem(self.fileBuffer[i][0])
489        self.fileName = shortFileName
490
491        self.kNeighborsLabel.setText("Number of neighbors (k): " + str(settings["kValue"]))
492        self.percentDataUsedLabel.setText("Percent of data used: " + "%d %%" % (settings["percentDataUsed"]))
493        self.testingMethodLabel.setText("Testing method used: " + self.testingMethod[settings["testingMethod"]])
494        self.qualityMeasureLabel.setText("Quality measure used: " + self.qualityMeasure[settings["qualityMeasure"]])
495
496
497    def addProjection(self, val, attrList):
498        index = self.findTargetIndex(val)
499        self.allResults.insert(index, (val, attrList))
500        self.resultList.insertItem(index, "%.3f - %s" % (val, str(attrList)))
501
502
503    def findTargetIndex(self, accuracy):
504        # use bisection to find correct index
505        top = 0; bottom = len(self.allResults)
506
507        while (bottom-top) > 1:
508            mid  = (bottom + top)/2
509            if max(accuracy, self.allResults[mid][0]) == accuracy: bottom = mid
510            else: top = mid
511
512        if len(self.allResults) == 0: return 0
513        if max(accuracy, self.allResults[top][0]) == accuracy:
514            return top
515        else:
516            return bottom
517
518
519    def startOptimization(self):
520        self.clearResults()
521        if self.parallelWidget.data == None: return
522
523        if self.optimizationMeasure == VIZRANK and self.fileName == "":
524            QMessageBox.information(self, "No projection file", "If you wish to optimize using VizRank you first have to load a projection file \ncreated by VizRank using Scatterplot widget.", QMessageBox.Ok)
525            return
526        if self.parallelWidget.data == None:
527            QMessageBox.information(self, "Missing data set", "A data set has to be loaded in order to perform optimization.", QMessageBox.Ok)
528            return
529
530        attrInfo = []
531        self.progressBarInit()
532        if self.optimizationMeasure == CORRELATION:
533            attrList = [attr.name for attr in self.parallelWidget.data.domain.attributes]
534            self.startOptimizationButton.hide()
535            self.stopOptimizationButton.show()
536            self.canOptimize = 1
537            class StopOptimizationException(Exception):
538                pass
539            def progressSetWithStop(value):
540                if not self.canContinueOptimization():
541                    raise StopOptimizationException()
542                else:
543                    self.progressBarSet(value * 0.9)
544            try: 
545                attrInfo = orngVisFuncts.computeCorrelationBetweenAttributes(self.parallelWidget.data, attrList, progressCallback=progressSetWithStop)
546            except StopOptimizationException:
547                attrInfo = []
548                self.startOptimizationButton.show()
549                self.stopOptimizationButton.hide()
550               
551#            self.progressBarFinished()
552            #attrInfo = orngVisFuncts.computeCorrelationInsideClassesBetweenAttributes(self.parallelWidget.data, attrList)
553        elif self.optimizationMeasure == VIZRANK:
554            for (val, [a1, a2]) in self.projections:
555                attrInfo.append((val, a1, a2))
556
557            # check if all attributes in loaded projection file are actually present in this data set
558            attrs = [attr.name for attr in self.parallelWidget.data.domain.attributes]
559            for (v, a1, a2) in attrInfo:
560                if a1 not in attrs:
561                    print "attribute " + a1 + " was not found in the data set. You probably loaded wrong file with VizRank projections."
562                    return
563                if a2 not in attrs:
564                    print "attribute " + a2 + " was not found in the data set. You probably loaded wrong file with VizRank projections."
565                    return
566
567        if len(attrInfo) == 0:
568            print "len(attrInfo) == 0. No attribute pairs. Unable to optimize."; return
569
570        self.worstVal = -1
571        self.canOptimize = 1
572        self.startOptimizationButton.hide()
573        self.stopOptimizationButton.show()
574        #qApp.processEvents()        # allow processing of other events
575
576        if self.orderAllAttributes:
577            orngVisFuncts.optimizeAttributeOrder(attrInfo, len(self.parallelWidget.data.domain.attributes), self, qApp)
578        else:
579            orngVisFuncts.optimizeAttributeOrder(attrInfo, self.numberOfAttributes, self, qApp)
580
581        self.stopOptimizationButton.hide()
582        self.startOptimizationButton.show()
583       
584        self.progressBarFinished()
585
586
587    # ################################
588    # MANAGE RESULTS
589    def updateShownProjections(self, *args):
590        self.resultList.clear()
591        for i in range(len(self.allResults)):
592            self.resultList.addItem("%.2f - %s" % (self.allResults[i][0], str(self.allResults[i][1])), i)
593        if self.resultList.count() > 0: self.resultList.setCurrentRow(0)
594
595    def clearResults(self):
596        self.allResults = []
597        self.resultList.clear()
598
599
600    def saveResults(self, filename = None):
601        if filename == None:
602            filename = ""
603            datasetName = getattr(self.parallelWidget.graph.rawData, "name", "")
604            if datasetName != "":
605                filename = os.path.splitext(os.path.split(datasetName)[1])[0]
606            if self.optimizationMeasure == CORRELATION: filename += " - " + "correlation"
607            else:                                       filename += " - " + "vizrank"
608
609            name = str(QFileDialog.getSaveFileName(self, "Save Parallel Projections",  os.path.join(self.lastSaveDirName, filename), "Parallel projections (*.papr)"))
610            if name == "": return
611        else:
612            name = filename
613
614        # take care of extension
615        if os.path.splitext(name)[1] != ".papr": name += ".papr"
616
617        dirName, shortFileName = os.path.split(name)
618        self.lastSaveDirName = dirName
619
620        # open, write and save file
621        file = open(name, "wt")
622        for val in self.allResults:
623            file.write(str(val) + "\n")
624        file.close()
625
626    def loadResults(self):
627        self.clearResults()
628
629        name = str(QFileDialog.getOpenFileName(self, "Open Parallel Projections",  self.lastSaveDirName, "Parallel projections (*.papr)"))
630        if name == "": return
631
632        dirName, shortFileName = os.path.split(name)
633        self.lastSaveDirName = dirName
634
635        file = open(name, "rt")
636        line = file.readline()[:-1]; ind = 0
637        while (line != ""):
638            (val, attrList) = eval(line)
639            self.allResults.insert(ind, (val, attrList))
640            self.resultList.addItem("%.2f - %s" % (val, str(attrList)), ind)
641            line = file.readline()[:-1]
642            ind+=1
643        file.close()
644
645
646#test widget appearance
647if __name__=="__main__":
648    a=QApplication(sys.argv)
649    ow=OWParallelCoordinates()
650    ow.show()
651    ow.graph.discPalette = ColorPaletteGenerator(rgbColors = [(127, 201, 127), (190, 174, 212), (253, 192, 134)])
652    data = orange.ExampleTable("../../doc/datasets/iris.tab")
653#    data = orange.ExampleTable(r"e:\Development\Orange Datasets\UCI\wine.tab")
654    #data = orange.ExampleTable(r"e:\Development\Orange Datasets\UCI\zoo.tab")
655    ow.setData(data)
656    ow.handleNewSignals()
657   
658    a.exec_()
Note: See TracBrowser for help on using the repository browser.