📄 owparallelcoordinates.py
字号:
"""
<name>Parallel coordinates</name>
<description>Parallel coordinates (multiattribute) visualization.</description>
<contact>Gregor Leban (gregor.leban@fri.uni-lj.si)</contact>
<icon>icons/ParallelCoordinates.png</icon>
<priority>3200</priority>
"""
# ParallelCoordinates.py
#
# Show data using parallel coordinates visualization method
#
from OWVisWidget import *
from OWParallelGraph import *
import OWToolbars, OWGUI, OWDlgs, orngVisFuncts
from sys import getrecursionlimit, setrecursionlimit
###########################################################################################
##### WIDGET : Parallel coordinates visualization
###########################################################################################
class OWParallelCoordinates(OWVisWidget):
settingsList = ["attrContOrder", "attrDiscOrder", "graph.jitterSize", "graph.showDistributions",
"graph.showAttrValues", "graph.hidePureExamples", "graph.globalValueScaling", "linesDistance",
"graph.useSplines", "graph.lineTracking", "graph.enabledLegend", "autoSendSelection",
"toolbarSelection", "graph.showStatistics", "colorSettings", "showAllAttributes"]
attributeContOrder = ["None", "ReliefF", "Fisher discriminant", "Signal to Noise", "Signal to Noise For Each Class"]
attributeDiscOrder = ["None", "ReliefF", "GainRatio"]
jitterSizeNums = [0, 2, 5, 10, 15, 20, 30]
linesDistanceNums = [10, 20, 30, 40, 50, 60, 70, 80, 100, 120, 150]
def __init__(self,parent=None, signalManager = None):
OWWidget.__init__(self, parent, signalManager, "Parallel Coordinates", TRUE)
#add a graph widget
self.box = QVBoxLayout(self.mainArea)
self.graph = OWParallelGraph(self, self.mainArea)
self.slider = QSlider(QSlider.Horizontal, self.mainArea)
self.sliderRange = 0
self.slider.setRange(0, 0)
self.slider.setTickmarks(QSlider.Below)
self.isResizing = 0
self.showAllAttributes = 0
self.box.addWidget(self.graph)
self.box.addWidget(self.slider)
self.inputs = [("Examples", ExampleTable, self.cdata), ("Example Subset", ExampleTable, self.subsetdata), ("Attribute Selection List", AttributeList, self.attributeSelection)]
self.outputs = [("Selected Examples", ExampleTableWithClass), ("Unselected Examples", ExampleTableWithClass), ("Attribute Selection List", AttributeList)]
#set default settings
self.data = None
self.linesDistance = 60
self.autoSendSelection = 1
self.attrDiscOrder = "None"
self.attrContOrder = "None"
self.projections = None
self.correlationDict = {}
self.middleLabels = "Correlations"
self.attributeSelectionList = None
self.toolbarSelection = 0
self.colorSettings = None
self.graph.jitterSize = 10
self.graph.showDistributions = 1
self.graph.showStatistics = 0
self.graph.showAttrValues = 1
self.graph.hidePureExamples = 1
self.graph.globalValueScaling = 0
self.graph.useSplines = 0
self.graph.lineTracking = 0
self.graph.enabledLegend = 1
self.setSliderIndex = -1
#load settings
self.loadSettings()
#GUI
self.tabs = QTabWidget(self.space, 'tabWidget')
self.GeneralTab = QVGroupBox(self)
self.SettingsTab = QVGroupBox(self, "Settings")
self.tabs.insertTab(self.GeneralTab, "General")
self.tabs.insertTab(self.SettingsTab, "Settings")
#add controls to self.controlArea widget
self.targetValueCombo = OWGUI.comboBox(self.GeneralTab, self, "targetValue", box = " Target Class Value ", sendSelectedValue = 1, valueType = str, callback = self.updateGraph)
self.createShowHiddenLists(self.GeneralTab, callback = self.updateGraph)
self.connect(self.shownAttribsLB, SIGNAL('doubleClicked(QListBoxItem *)'), self.flipAttribute)
self.optimizationDlg = ParallelOptimization(self, signalManager = self.signalManager)
self.optimizationDlgButton = OWGUI.button(self.GeneralTab, self, "Optimization dialog", callback = self.optimizationDlg.reshow, debuggingEnabled = 0)
self.zoomSelectToolbar = OWToolbars.ZoomSelectToolbar(self, self.GeneralTab, self.graph, self.autoSendSelection)
self.connect(self.zoomSelectToolbar.buttonSendSelections, SIGNAL("clicked()"), self.sendSelections)
#connect controls to appropriate functions
self.connect(self.slider, SIGNAL("valueChanged(int)"), self.updateGraphSlider)
self.connect(self.graphButton, SIGNAL("clicked()"), self.graph.saveToFile)
# ####################################
# SETTINGS functionality
boxX = OWGUI.widgetBox(self.SettingsTab, " Graph settings ")
OWGUI.comboBox(boxX, self, "graph.jitterSize", label = 'Jittering size (% of size): ', orientation='horizontal', callback = self.setJitteringSize, items = self.jitterSizeNums, sendSelectedValue = 1, valueType = float)
OWGUI.comboBox(boxX, self, "linesDistance", label = 'Minimum axis distance: ', orientation='horizontal', callback = self.updateGraph, items = self.linesDistanceNums, tooltip = "What is the minimum distance between two adjecent attribute axis", sendSelectedValue = 1, valueType = int)
# visual settings
box = OWGUI.widgetBox(self.SettingsTab, " Visual settings ")
OWGUI.checkBox(box, self, 'graph.showAttrValues', 'Show attribute values', callback = self.updateValues)
OWGUI.checkBox(box, self, 'graph.hidePureExamples', 'Hide pure examples', callback = self.updateValues, tooltip = "When one value of a discrete attribute has only examples from one class, \nstop drawing lines for this example. Figure must be interpreted from left to right.")
OWGUI.checkBox(box, self, 'graph.useSplines', 'Show splines', callback = self.updateValues, tooltip = "Show lines using splines")
OWGUI.checkBox(box, self, 'graph.lineTracking', 'Line tracking', callback = self.updateValues, tooltip = "Show nearest example with a wider line. The rest of the lines \nwill be shown in lighter colors.")
OWGUI.checkBox(box, self, 'graph.enabledLegend', 'Show legend', callback = self.updateValues)
OWGUI.checkBox(box, self, 'graph.globalValueScaling', 'Global Value Scaling', callback = self.setGlobalValueScaling)
box3 = OWGUI.widgetBox(self.SettingsTab, " Statistics ")
OWGUI.comboBox(box3, self, "graph.showStatistics", items = ["No statistics", "Means, deviations", "Median, quartiles"], callback = self.updateValues, sendSelectedValue = 0, valueType = int)
OWGUI.checkBox(box3, self, 'graph.showDistributions', 'Show distributions', callback = self.updateValues, tooltip = "Show bars with distribution of class values (only for discrete attributes)")
OWGUI.comboBox(self.SettingsTab, self, "middleLabels", box = " Middle labels ", items = ["Off", "Correlations", "VizRank"], callback = self.updateGraph, tooltip = "What information do you wish to view on top in the middle of coordinate axes?", sendSelectedValue = 1, valueType = str)
hbox4 = OWGUI.widgetBox(self.SettingsTab, "Colors", orientation = "horizontal")
OWGUI.button(hbox4, self, "Set Colors", self.setColors, tooltip = "Set the canvas background color and color palette for coloring continuous variables", debuggingEnabled = 0)
box2 = OWGUI.widgetBox(self.SettingsTab, " Sending selection ")
OWGUI.checkBox(box2, self, 'autoSendSelection', 'Auto send selected data', callback = self.setAutoSendSelection, tooltip = "Send signals with selected data whenever the selection changes.")
# continuous attribute ordering
OWGUI.comboBox(self.SettingsTab, self, "attrContOrder", box = " Continuous attribute ordering ", items = self.attributeContOrder, callback = self.updateShownAttributeList, sendSelectedValue = 1, valueType = str)
OWGUI.comboBox(self.SettingsTab, self, "attrDiscOrder", box = " Discrete attribute ordering ", items = self.attributeDiscOrder, callback = self.updateShownAttributeList, sendSelectedValue = 1, valueType = str)
self.graph.autoSendSelectionCallback = self.setAutoSendSelection
self.icons = self.createAttributeIconDict()
# add a settings dialog and initialize its values
self.activateLoadedSettings()
self.resize(900, 700)
# #########################
# OPTIONS
# #########################
def activateLoadedSettings(self):
dlg = self.createColorDialog()
self.graph.contPalette = dlg.getContinuousPalette("contPalette")
self.graph.discPalette = dlg.getDiscretePalette()
self.graph.setCanvasBackground(dlg.getColor("Canvas"))
apply([self.zoomSelectToolbar.actionZooming, self.zoomSelectToolbar.actionRectangleSelection, self.zoomSelectToolbar.actionPolygonSelection][self.toolbarSelection], [])
self.cbShowAllAttributes()
def flipAttribute(self, item):
if self.graph.flipAttribute(str(item.text())):
self.updateGraph()
else:
self.information("Didn't flip the attribute. To flip a continuous attribute uncheck 'Global value scaling' checkbox.")
def updateGraph(self, *args):
attrs = self.getShownAttributeList()
maxAttrs = self.mainArea.width() / self.linesDistance
if len(attrs) > maxAttrs:
rest = len(attrs) - maxAttrs
if self.sliderRange != rest:
self.slider.setRange(0, rest)
self.sliderRange = rest
elif self.isResizing:
self.isResizing = 0
return # if we resized widget and it doesn't change the number of attributes that are shown then we return
start = min(self.slider.value(), len(attrs)-maxAttrs)
if self.setSliderIndex != -1:
if self.setSliderIndex == 0: start = 0
else: start = min(len(attrs)-maxAttrs, self.setSliderIndex - (maxAttrs+1)/2)
start = max(start, 0)
self.setSliderIndex = -1
self.slider.setValue(start)
else:
self.slider.setRange(0,0)
self.sliderRange = 0
maxAttrs = len(attrs)
start = 0
targetVal = str(self.targetValueCombo.currentText())
if targetVal == "(None)": targetVal = None
#self.graph.updateData(attrs[start:start+maxAttrs], targetVal, self.buildMidLabels(attrs[start:start+maxAttrs]))
self.graph.updateData(attrs, targetVal, self.buildMidLabels(attrs), start, start + maxAttrs)
self.slider.repaint()
self.graph.update()
#self.graph.repaint()
def updateGraphSlider(self, *args):
attrs = self.getShownAttributeList()
maxAttrs = self.mainArea.width() / self.linesDistance
start = min(self.slider.value(), len(attrs)-maxAttrs)
self.graph.setAxisScale(QwtPlot.xBottom, start, start + maxAttrs - 1, 1)
self.graph.update()
# build a list of strings that will be shown in the middle of the parallel axis
def buildMidLabels(self, attrs):
labels = []
if self.middleLabels == "Off" or self.data == None or len(self.data) == 0: return None
elif self.middleLabels == "Correlations":
for i in range(len(attrs)-1):
corr = None
if (attrs[i], attrs[i+1]) in self.correlationDict.keys(): corr = self.correlationDict[(attrs[i], attrs[i+1])]
elif (attrs[i+1], attrs[i]) in self.correlationDict.keys(): corr = self.correlationDict[(attrs[i+1], attrs[i])]
else:
corr = orngVisFuncts.computeCorrelation(self.data, attrs[i], attrs[i+1])
self.correlationDict[(attrs[i], attrs[i+1])] = corr
if corr and len(self.graph.attributeFlipInfo.keys()) > 0 and (self.graph.attributeFlipInfo[attrs[i]] != self.graph.attributeFlipInfo[attrs[i+1]]): corr = -corr
if corr: labels.append("%2.3f" % (corr))
else: labels.append("")
elif self.middleLabels == "VizRank":
for i in range(len(attrs)-1):
val = self.optimizationDlg.getVizRankVal(attrs[i], attrs[i+1])
if val: labels.append("%2.2f%%" % (val))
else: labels.append("")
return labels
# ###### SHOWN ATTRIBUTE LIST ##############
# set attribute list
def setShownAttributeList(self, data, shownAttributes = None):
shown = []
hidden = []
if not data: return
if shownAttributes:
if type(shownAttributes[0]) == tuple:
shown = shownAttributes
else:
domain = data.domain
shown = [(domain[a].name, domain[a].varType) for a in shownAttributes]
hidden = filter(lambda x:x not in shown, [(a.name, a.varType) for a in data.domain.attributes])
else:
shown, hidden, maxIndex = orngVisFuncts.selectAttributes(data, self.attrContOrder, self.attrDiscOrder)
shown = [(attr, data.domain[attr].varType) for attr in shown]
hidden = [(attr, data.domain[attr].varType) for attr in hidden]
if self.showAllAttributes:
shown += hidden
hidden = []
else:
hidden = shown[10:] + hidden
shown = shown[:10]
if data.domain.classVar and (data.domain.classVar.name, data.domain.classVar.varType) not in shown + hidden:
hidden += [(data.domain.classVar.name, data.domain.classVar.varType)]
self.shownAttributes = shown
self.hiddenAttributes = hidden
self.selectedHidden = []
self.selectedShown = []
self.resetAttrManipulation()
self.sendShownAttributes()
def sendShownAttributes(self):
self.send("Attribute Selection List", self.getShownAttributeList())
# #############################################
# had to override standart show to call updateGraph. otherwise self.mainArea.width() gives incorrect value
def show(self):
OWWidget.show(self)
self.updateGraph()
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -