📄 owinteractiongraph.py
字号:
"""
<name>Interaction Graph</name>
<description>Interaction graph construction and viewer.</description>
<icon>icons/InteractionGraph.png</icon>
<contact>Aleks Jakulin</contact>
<priority>4000</priority>
"""
# InteractionGraph.py
#
#
from OWWidget import *
from qt import *
from qtcanvas import *
import orngInteract
import statc
import os
from re import *
from math import floor, ceil
from orngCI import FeatureByCartesianProduct
class IntGraphView(QCanvasView):
def __init__(self, parent, name, *args):
apply(QCanvasView.__init__,(self,) + args)
self.parent = parent
self.name = name
self.connect(self, SIGNAL("contentsMoving(int,int)"), self.contentsMoving)
# mouse button was pressed
def contentsMousePressEvent(self, ev):
self.parent.mousePressed(self.name, ev)
def contentsMoving(self, x,y):
self.parent.contentsMoving(x,y)
###########################################################################################
##### WIDGET : Interaction graph
###########################################################################################
class OWInteractionGraph(OWWidget):
settingsList = ["onlyImportantInteractions"]
def __init__(self, parent=None, signalManager = None):
OWWidget.__init__(self, parent, signalManager, "Interaction graph")
self.inputs = [("Examples", ExampleTable, self.cdata)]
self.outputs = [("Examples", ExampleTable), ("Attribute Pair", list), ("Selected Attributes List", list)]
#set default settings
self.originalData = None
self.data = None
self.dataSize = 1
self.rest = None
self.interactionMatrix = None
self.rectIndices = {} # QRect rectangles
self.rectNames = {} # info about rectangle names (attributes)
self.lines = [] # dict of form (rectName1, rectName2):(labelQPoint, [p1QPoint, p2QPoint, ...])
self.interactionRects = []
self.rectItems = []
self.viewXPos = 0 # next two variables are used at setting tooltip position
self.viewYPos = 0 # inside canvasView
self.onlyImportantInteractions = 1
self.mergeAttributes = 0
#load settings
self.loadSettings()
# add a settings dialog and initialize its values
self.splitCanvas = QSplitter(self.mainArea)
self.canvasL = QCanvas(2000, 2000)
self.canvasViewL = IntGraphView(self, "interactions", self.canvasL, self.splitCanvas)
self.canvasViewL.show()
self.canvasR = QCanvas(2000,2000)
self.canvasViewR = IntGraphView(self, "graph", self.canvasR, self.splitCanvas)
self.canvasViewR.show()
#GUI
#add controls to self.controlArea widget
self.shownAttribsGroup = QVGroupBox(self.space)
self.addRemoveGroup = QHButtonGroup(self.space)
self.hiddenAttribsGroup = QVGroupBox(self.space)
self.shownAttribsGroup.setTitle("Selected attributes")
self.hiddenAttribsGroup.setTitle("Unselected attributes")
self.shownAttribsLB = QListBox(self.shownAttribsGroup)
self.shownAttribsLB.setSelectionMode(QListBox.Extended)
self.hiddenAttribsLB = QListBox(self.hiddenAttribsGroup)
self.hiddenAttribsLB.setSelectionMode(QListBox.Extended)
self.attrAddButton = QPushButton("Add attr.", self.addRemoveGroup)
self.attrRemoveButton = QPushButton("Remove attr.", self.addRemoveGroup)
self.mergeAttributesCB = QCheckBox('Merge attributes', self.space)
self.importantInteractionsCB = QCheckBox('Show only important interactions', self.space)
self.selectionButton = QPushButton("Show selection", self.space)
QToolTip.add(self.selectionButton, "Sends 'selection' signal to any successor visualization widgets.\nThis signal contains a list of selected attributes to visualize.")
QToolTip.add(self.mergeAttributesCB, "Enable or disable attribute merging. If enabled, you can merge \ntwo attributes with right mouse click inside attribute rectangle.\nMerged attribute is then built as cartesian product of corresponding attribute pair\nand added to the list of possible attributes")
self.saveLCanvas = QPushButton("Save left canvas", self.space)
self.saveRCanvas = QPushButton("Save right canvas", self.space)
self.connect(self.saveLCanvas, SIGNAL("clicked()"), self.saveToFileLCanvas)
self.connect(self.saveRCanvas, SIGNAL("clicked()"), self.saveToFileRCanvas)
#connect controls to appropriate functions
self.connect(self.attrAddButton, SIGNAL("clicked()"), self.addAttributeClick)
self.connect(self.attrRemoveButton, SIGNAL("clicked()"), self.removeAttributeClick)
self.connect(self.selectionButton, SIGNAL("clicked()"), self.selectionClick)
self.connect(self.mergeAttributesCB, SIGNAL("toggled(bool)"), self.mergeAttributesEvent)
self.connect(self.importantInteractionsCB, SIGNAL("toggled(bool)"), self.showImportantInteractions)
#self.connect(self.graphButton, SIGNAL("clicked()"), self.graph.saveToFile)
#self.connect(self.settingsButton, SIGNAL("clicked()"), self.options.show)
self.activateLoadedSettings()
def mergeAttributesEvent(self, b):
self.mergeAttributes = b
if b == 0:
self.updateNewData(self.originalData)
def showImportantInteractions(self, b):
self.onlyImportantInteractions = b
self.showInteractionRects(self.data)
def activateLoadedSettings(self):
self.importantInteractionsCB.setChecked(self.onlyImportantInteractions)
# did we click inside the rect rectangle
def clickInside(self, rect, point):
x = point.x()
y = point.y()
if rect.left() > x: return 0
if rect.right() < x: return 0
if rect.top() > y: return 0
if rect.bottom() < y: return 0
return 1
# if we clicked on edge label send "wiew" signal, if clicked inside rectangle select/unselect attribute
def mousePressed(self, name, ev):
if ev.button() == QMouseEvent.LeftButton and name == "graph":
for name in self.rectNames:
clicked = self.clickInside(self.rectNames[name].rect(), ev.pos())
if clicked == 1:
self._setAttrVisible(name, not self.getAttrVisible(name))
self.showInteractionRects(self.data)
self.canvasR.update()
return
for (attr1, attr2, rect) in self.lines:
clicked = self.clickInside(rect.rect(), ev.pos())
if clicked == 1:
self.send("Attribute Pair", [attr1, attr2])
return
elif ev.button() == QMouseEvent.LeftButton and name == "interactions":
self.rest = None
for (rect1, rect2, rect3, nbrect, text1, text2, tooltipRect, tooltipText) in self.interactionRects:
if self.clickInside(tooltipRect, ev.pos()) == 1:
self.send("Attribute Pair", [str(text1.text()), str(text2.text())])
elif ev.button() == QMouseEvent.RightButton and name == "interactions":
if not self.mergeAttributes == 1: return
found = 0; i = 0
while not found and i < len(self.interactionRects):
(rect1, rect2, rect3, nbrect, text1, text2, tooltipRect, tooltipText) = self.interactionRects[i]
if self.clickInside(tooltipRect, ev.pos()) == 1:
attr1 = str(text1.text()); attr2 = str(text2.text())
found = 1
i+=1
if not found: return
data = self.interactionMatrix.discData
(cart, profit) = FeatureByCartesianProduct(data, [data.domain[attr1], data.domain[attr2]])
if cart in data.domain: return # if this attribute already in domain return
for attr in data.domain:
if cart.name == attr.name:
print "Attribute pair already in the domain"
return
tempData = data.select(list(data.domain) + [cart])
dd = orange.DomainDistributions(tempData)
vals = []
for i in range(len(cart.values)):
if dd[cart][i] != 0.0:
vals.append(cart.values[i])
newVar = orange.EnumVariable(cart.name, values = vals)
newData = data.select(list(data.domain) + [newVar])
for i in range(len(newData)):
newData[i][newVar] = tempData[i][cart]
#rest = newData.select({cart.name:todoList})
#print "intervals = %d, non clear values = %d" % (len(cart.values), len(todoList))
#print "entropy left = %f" % (float(len(rest)) / float(self.dataSize))
self.updateNewData(newData)
# we catch mouse release event so that we can send the "view" signal
def onMouseReleased(self, e):
for i in range(len(self.graphs)):
if self.graphs[i].blankClick == 1:
(attr1, attr2, className, string) = self.graphParameters[i]
self.send("Attribute Pair", [attr1, attr2])
self.graphs[i].blankClick = 0
# click on selection button
def selectionClick(self):
if self.data == None: return
l = []
for i in range(self.shownAttribsLB.count()):
l.append(str(self.shownAttribsLB.text(i)))
self.send("Selected Attributes List", l)
def resizeEvent(self, e):
if hasattr(self, "splitCanvas"):
self.splitCanvas.resize(self.mainArea.size())
####### CDATA ################################
# receive new data and update all fields
def cdata(self, data):
if not data:
return
self.originalData = orange.Preprocessor_dropMissing(data)
self.dataSize = len(self.originalData)
self.updateNewData(self.originalData)
def updateNewData(self, data):
self.data = data
self.interactionMatrix = orngInteract.InteractionMatrix(data, dependencies_too=1)
# save discretized data and repair invalid names
for attr in self.interactionMatrix.discData.domain.attributes:
attr.name = attr.name.replace("ED_","")
attr.name = attr.name.replace("D_","")
attr.name = attr.name.replace("M_","")
self.interactionList = []
entropy = self.interactionMatrix.entropy
if entropy == 0.0: return
################################
# create a sorted list of total information
for ((val,(val2, attrIndex1, attrIndex2))) in self.interactionMatrix.list:
gain1 = self.interactionMatrix.gains[attrIndex1] / entropy
gain2 = self.interactionMatrix.gains[attrIndex2] / entropy
total = (val/entropy) + gain1 + gain2
self.interactionList.append((total, (gain1, gain2, attrIndex1, attrIndex2)))
self.interactionList.sort()
self.interactionList.reverse()
f = open('interaction.dot','w')
self.interactionMatrix.exportGraph(f, significant_digits=3,positive_int=8,negative_int=8,absolute_int=0,url=1)
f.flush()
f.close()
# execute dot and save otuput to pipes
(pipePngOut, pipePngIn) = os.popen2("dot interaction.dot -Tpng", "b")
(pipePlainOut, pipePlainIn) = os.popen2("dot interaction.dot -Tismap", "t")
textPng = pipePngIn.read()
textPlainList = pipePlainIn.readlines()
pipePngIn.close()
pipePlainIn.close()
pipePngOut.close()
pipePlainOut.close()
os.remove('interaction.dot')
# if the output from the pipe was empty, then the software isn't installed correctly
if len(textPng) == 0:
print "-----------------------------"
print "Error. This widget needs graphviz software package installed. You can find it on the internet."
print "-----------------------------"
return
# create a picture
pixmap = QPixmap()
pixmap.loadFromData(textPng)
canvasPixmap = QCanvasPixmap(pixmap, QPoint(0,0))
width = canvasPixmap.width()
height = canvasPixmap.height()
# hide all rects
for rectInd in self.rectIndices.keys():
self.rectIndices[rectInd].hide()
self.canvasR.setTiles(pixmap, 1, 1, width, height)
self.canvasR.resize(width, height)
self.rectIndices = {} # QRect rectangles
self.rectNames = {} # info about rectangle names (attributes)
self.lines = [] # dict of form (rectName1, rectName2):(labelQPoint, [p1QPoint, p2QPoint, ...])
self.parseGraphData(data, textPlainList, width, height)
self.initLists(data) # add all attributes found in .dot file to shown list
self.showInteractionRects(data) # use interaction matrix to fill the left canvas with rectangles
self.canvasL.update()
self.canvasR.update()
self.send("Examples", data)
#########################################
# do we want to show interactions between attrIndex1 and attrIndex2
def showInteractionPair(self, attrIndex1, attrIndex2):
attrName1 = self.data.domain[attrIndex1].name
attrName2 = self.data.domain[attrIndex2].name
if self.mergeAttributes == 1:
if self.getAttrVisible(attrName1) == 0 or self.getAttrVisible(attrName2) == 0: return 0
list1 = attrName1.split("-")
list2 = attrName2.split("-")
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -