Changeset 3701:4fffe36c2564 in orange


Ignore:
Timestamp:
05/27/07 18:58:27 (7 years ago)
Author:
Gregor <Gregor@…>
Branch:
default
Convert:
d0bb2542c9636868ca8635012cd8ffe00c822666
Message:
  • mosaic tree
Location:
orange
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • orange/OrangeWidgets/OWMosaicOptimization.py

    r3567 r3701  
    22from OWWidget import OWWidget 
    33import os 
    4 import OWGUI, orngVisFuncts 
     4import OWGUI, orngVisFuncts, OWQCanvasFuncts 
     5from qtcanvas import QCanvas, QCanvasView 
    56from orngMosaic import * 
    67from orngScaleData import getVariableValuesSorted 
     
    910                  ("Cramer's Phi (correlation with class)", CRAMERS_PHI), 
    1011                  ("Information Gain (in % of class entropy removed)", INFORMATION_GAIN), 
    11                   ("Gain Ratio", GAIN_RATIO), 
     12                  ("Distance Measure", DISTANCE_MEASURE), 
     13                  ("Minimum Description Length", MDL), 
    1214                  ("Interaction Gain (in % of class entropy removed)", INTERACTION_GAIN), 
    1315                  ("Average Probability Of Correct Classification", AVERAGE_PROBABILITY_OF_CORRECT_CLASSIFICATION), 
     
    1517                  ("CN2 Rules", CN2_RULES)] 
    1618 
     19allExamplesText = "<All examples>" 
     20 
    1721class OWMosaicOptimization(OWBaseWidget, orngMosaic): 
    1822    resultsListLenNums = [ 100 ,  250 ,  500 ,  1000 ,  5000 ,  10000, 20000, 50000, 100000] 
    1923    resultsListLenList = [str(x) for x in resultsListLenNums] 
    20     settingsList = ["attrDisc", "qualityMeasure", "percentDataUsed", "ignoreTooSmallCells", 
    21                     "timeLimit", "useTimeLimit", "VizRankClassifierName", "mValue", "probabilityEstimation", "attributeCount", 
     24    settingsList = ["optimizationType", "attributeCount", "attrDisc", "qualityMeasure", "percentDataUsed", "ignoreTooSmallCells", 
     25                    "timeLimit", "useTimeLimit", "VizRankClassifierName", "mValue", "probabilityEstimation", 
    2226                    "optimizeAttributeOrder", "optimizeAttributeValueOrder", "attributeOrderTestingMethod", 
    23                     "classificationMethod", "classConfidence"] 
     27                    "classificationMethod", "classConfidence", "lastSaveDirName"] 
    2428 
    2529    percentDataNums = [ 5 ,  10 ,  15 ,  20 ,  30 ,  40 ,  50 ,  60 ,  70 ,  80 ,  90 ,  100 ] 
    2630    #evaluationTimeNums = [0.5, 1, 2, 5, 10, 20, 30, 40, 60, 80, 120] 
    2731 
    28     def __init__(self, parentWidget = None, signalManager = None): 
    29         OWBaseWidget.__init__(self, None, signalManager, "Mosaic Optimization Dialog", savePosition = True) 
     32    def __init__(self, mosaicWidget = None, signalManager = None): 
     33        OWBaseWidget.__init__(self, None, signalManager, "Mosaic Evaluation Dialog", savePosition = True) 
    3034        orngMosaic.__init__(self) 
    3135 
     
    3337 
    3438        if (int(qVersion()[0]) >= 3): 
    35             self.setCaption("Mosaic Optimization Dialog") 
     39            self.setCaption("Mosaic Evaluation Dialog") 
    3640        else: 
    37             self.setCaption("Qt Mosaic Optimization Dialog") 
     41            self.setCaption("Qt Mosaic Evaluation Dialog") 
    3842        self.controlArea = QVBoxLayout(self) 
    3943 
    4044        # loaded variables 
    41         self.parentWidget = parentWidget 
     45        self.mosaicWidget = mosaicWidget 
    4246        self.showConfidence = 1 
    4347        self.optimizeAttributeOrder = 0 
     
    4549        self.VizRankClassifierName = "Mosaic Learner" 
    4650 
    47  
    4851        self.lastSaveDirName = os.getcwd() 
    4952        self.selectedClasses = [] 
    50         self.cancelOptimization = 0 
    5153        self.cancelArgumentation = 0 
    5254        self.useTimeLimit = 0 
    5355 
     56        # explorer variables 
     57        self.wholeDataSet = None 
     58        self.processingSubsetData = 0       # this is a flag that we set when we call mosaicWidget.setData function 
     59        self.showDataSubset = 1 
     60        self.invertSelection = 0 
     61        self.mosaicSize = 300 
     62 
    5463        self.attrLenDict = {} 
    5564        self.shownResults = [] 
     
    6271        self.MainTab = QVGroupBox(self) 
    6372        self.SettingsTab = QVGroupBox(self) 
     73        self.TreeTab = QVGroupBox(self) 
    6474        self.ManageTab = QVGroupBox(self) 
    6575        self.ArgumentationTab = QVGroupBox(self) 
     
    7080        self.tabs.insertTab(self.ArgumentationTab, "Argumentation") 
    7181        self.tabs.insertTab(self.ClassificationTab, "Classification") 
    72         self.tabs.insertTab(self.ManageTab, "Manage & Save") 
     82        self.tabs.insertTab(self.TreeTab, "Tree") 
     83        self.tabs.insertTab(self.ManageTab, "Manage") 
    7384 
    7485        # ########################### 
     
    8899        self.startOptimizationButton = OWGUI.button(self.optimizationBox, self, "Start Evaluating Projections", callback = self.evaluateProjections) 
    89100        f = self.startOptimizationButton.font(); f.setBold(1);   self.startOptimizationButton.setFont(f) 
    90         self.stopOptimizationButton = OWGUI.button(self.optimizationBox, self, "Stop evaluation", callback = self.stopOptimizationClick) 
     101        self.stopOptimizationButton = OWGUI.button(self.optimizationBox, self, "Stop evaluation", callback = self.stopEvaluationClick) 
    91102        self.stopOptimizationButton.setFont(f) 
    92103        self.stopOptimizationButton.hide() 
     
    104115        # ########################## 
    105116        # SETTINGS TAB 
    106         self.measureCombo = OWGUI.comboBox(self.SettingsTab, self, "qualityMeasure", box = "Measure Projection Interestingness", items = [item[0] for item in mosaicMeasures], tooltip = "What is interesting?", callback = self.updateGUI) 
     117        self.measureCombo = OWGUI.comboBox(self.SettingsTab, self, "qualityMeasure", box = "Measure of Projection Interestingness", items = [item[0] for item in mosaicMeasures], tooltip = "What is interesting?", callback = self.updateGUI) 
    107118 
    108119        self.ignoreSmallCellsBox = OWGUI.widgetBox(self.SettingsTab, "Ignore Small Cells" ) 
     
    120131        # ARGUMENTATION TAB 
    121132        self.argumentationBox = OWGUI.widgetBox(self.ArgumentationTab, "Arguments") 
    122         self.findArgumentsButton = OWGUI.button(self.argumentationBox, self, "Find Arguments", callback = self.findArguments, tooltip = "Evaluate arguments for each possible class value using settings in the Classification tab.") 
     133        self.findArgumentsButton = OWGUI.button(self.argumentationBox, self, "Find Arguments", callback = self.findArguments, tooltip = "Evaluate arguments for each possible class value using settings in the Classification tab.", debuggingEnabled = 0) 
    123134        f = self.findArgumentsButton.font(); f.setBold(1);  self.findArgumentsButton.setFont(f) 
    124135        self.stopArgumentationButton = OWGUI.button(self.argumentationBox, self, "Stop Searching", callback = self.stopArgumentationClick) 
     
    170181 
    171182        # ########################## 
     183        # TREE TAB 
     184        subsetBox = OWGUI.widgetBox(self.TreeTab, "Example Subset Analysis") 
     185        self.splitter = QSplitter(Qt.Vertical, subsetBox, "main") 
     186        self.subsetTree = QListView(self.splitter) 
     187        self.subsetTree.setRootIsDecorated(1) 
     188        self.subsetTree.setAllColumnsShowFocus(1) 
     189        self.subsetTree.addColumn('Visualized Attributes') 
     190        self.subsetTree.addColumn('# inst.') 
     191        self.subsetTree.setColumnWidth(0, 300) 
     192        self.subsetTree.setColumnWidthMode(0, QListView.Maximum) 
     193        self.subsetTree.setColumnAlignment(0, QListView.AlignLeft) 
     194        self.subsetTree.setColumnWidth(1, 50) 
     195        self.subsetTree.setColumnWidthMode(1, QListView.Manual) 
     196        self.subsetTree.setColumnAlignment(1, QListView.AlignRight) 
     197        self.connect(self.subsetTree, SIGNAL("selectionChanged(QListViewItem *)"), self.mtSelectedTreeItemChanged) 
     198        self.connect(self.subsetTree, SIGNAL("rightButtonPressed(QListViewItem *, const QPoint &, int )"), self.mtSubsetTreeRemoveItemPopup) 
     199 
     200        self.selectionsList = QListBox(self.splitter) 
     201        self.connect(self.selectionsList, SIGNAL("selectionChanged()"), self.mtSelectedListItemChanged) 
     202        self.connect(self.selectionsList, SIGNAL('doubleClicked(QListBoxItem *)'), self.mtSelectedListItemDoubleClicked) 
     203 
     204        self.subsetItems = {} 
     205        self.subsetUpdateInProgress = 0 
     206        self.treeRoot = None 
     207 
     208        explorerBox = OWGUI.widgetBox(self.TreeTab, 1) 
     209        OWGUI.button(explorerBox, self, "Explore Currently Selected Examples", callback = self.mtEploreCurrentSelection, tooltip = "Visualize only selected examples and find interesting projections of them", debuggingEnabled=0) 
     210        OWGUI.checkBox(explorerBox, self, 'showDataSubset', 'Show unselected data as example subset', tooltip = "This option determines what to do with the examples that are not selected in the projection.\nIf checked then unselected examples will be visualized in the same way as examples that are received through the 'Example Subset' signal.") 
     211 
     212        self.mosaic = orngMosaic() 
     213        autoBuildTreeBox = OWGUI.widgetBox(self.TreeTab, "Mosaic Tree", orientation = "vertical") 
     214        autoBuildTreeButtonBox = OWGUI.widgetBox(autoBuildTreeBox, orientation = "horizontal") 
     215        self.autoBuildTreeButton = OWGUI.button(autoBuildTreeButtonBox, self, "Build Tree", callback = self.mtMosaicAutoBuildTree, tooltip = "Evaluate different mosaic diagrams and automatically build a tree of mosaic diagrams with clear class separation", debuggingEnabled = 0) 
     216        OWGUI.button(autoBuildTreeButtonBox, self, "Visualize Tree", callback = self.mtVisualizeMosaicTree, tooltip = "Visualize a tree where each node is a mosaic diagram", debuggingEnabled = 0) 
     217        OWGUI.lineEdit(autoBuildTreeBox, self, "mosaicSize", "Size of individual mosaic diagrams: ", orientation = "horizontal", tooltip = "What are the X and Y dimensions of individual mosaics in the tree?", valueType = int, validator = QIntValidator(self)) 
     218 
     219        loadSaveBox = OWGUI.widgetBox(self.TreeTab, "Load/Save Mosaic Tree", orientation = "horizontal") 
     220        OWGUI.button(loadSaveBox, self, "Load", callback = self.mtLoadTree, tooltip = "Load a tree from a file", debuggingEnabled = 0) 
     221        OWGUI.button(loadSaveBox, self, "Save", callback = self.mtSaveTree, tooltip = "Save tree to a file", debuggingEnabled = 0) 
     222 
     223        self.subsetPopupMenu = QPopupMenu(self) 
     224        self.subsetPopupMenu.insertItem("Explore currently selected examples", self.mtEploreCurrentSelection) 
     225        self.subsetPopupMenu.insertItem("Find interesting projection", self.evaluateProjections) 
     226        self.subsetPopupMenu.insertSeparator() 
     227        self.subsetPopupMenu.insertItem("Remove node", self.mtRemoveSelectedItem) 
     228        self.subsetPopupMenu.insertItem("Clear tree", self.mtInitSubsetTree) 
     229 
     230 
     231 
     232        # ########################## 
    172233        # SAVE TAB 
    173234        self.visualizedAttributesBox = OWGUI.widgetBox(self.ManageTab, "Number of Concurrently Visualized Attributes") 
     
    189250 
    190251        self.buttonBox6 = OWGUI.widgetBox(self.manageResultsBox, orientation = "horizontal") 
    191         self.loadButton = OWGUI.button(self.buttonBox6, self, "Load", self.load) 
    192         self.saveButton = OWGUI.button(self.buttonBox6, self, "Save", self.save) 
     252        self.loadButton = OWGUI.button(self.buttonBox6, self, "Load", self.load, debuggingEnabled = 0) 
     253        self.saveButton = OWGUI.button(self.buttonBox6, self, "Save", self.save, debuggingEnabled = 0) 
    193254 
    194255        self.buttonBox5 = OWGUI.widgetBox(self.manageResultsBox, orientation = "horizontal") 
     
    209270    # EVENTS 
    210271    def showSelectedAttributes(self, attrs = None): 
    211         if not self.parentWidget: return 
     272        if not self.mosaicWidget: return 
    212273        if not attrs: 
    213274            projection = self.getSelectedProjection() 
     
    218279                for (q, a, vals) in extraInfo: 
    219280                    ruleVals.append([vals[a.index(attr)] for attr in attrs]) 
    220                 self.parentWidget.activeRule = (attrs, ruleVals) 
     281                self.mosaicWidget.activeRule = (attrs, ruleVals) 
    221282        valueOrder = None 
    222283        if self.optimizeAttributeOrder: 
     
    225286            self.resultList.setEnabled(1) 
    226287        else: 
    227             self.parentWidget.setShownAttributes(attrs) 
     288            self.mosaicWidget.setShownAttributes(attrs) 
    228289        self.resultList.setFocus() 
    229290 
     
    232293        if str(self.optimizeOrderButton.text()) == "Optimize Current Attribute Order": 
    233294            self.stopOptimization = 0 
    234             self.optimizeOrderButton.setText("Stop optimization") 
     295            self.optimizeOrderButton.setText("Stop Optimization") 
    235296 
    236297            if not attrs: 
    237                 attrs = self.parentWidget.getShownAttributeList() 
     298                attrs = self.mosaicWidget.getShownAttributeList() 
    238299 
    239300            bestPlacements = self.findOptimalAttributeOrder(attrs, self.optimizeAttributeValueOrder) 
    240301            if updateGraph: 
    241                 self.parentWidget.bestPlacements = bestPlacements 
     302                self.mosaicWidget.bestPlacements = bestPlacements 
    242303                if bestPlacements: 
    243304                    attrList, valueOrder = bestPlacements[0][1], bestPlacements[0][2] 
    244                     self.parentWidget.setShownAttributes(attrList, customValueOrderDict = dict([(attrList[i], tuple(valueOrder[i])) for i in range(len(attrList))]) ) 
     305                    self.mosaicWidget.setShownAttributes(attrList, customValueOrderDict = dict([(attrList[i], tuple(valueOrder[i])) for i in range(len(attrList))]) ) 
    245306 
    246307            self.optimizeOrderButton.setText("Optimize Current Attribute Order") 
     
    321382        self.selectedClasses = [] 
    322383 
     384        # for mosaic tree 
     385        if self.processingSubsetData == 0: 
     386            self.wholeDataSet = data 
     387            self.mtInitSubsetTree() 
     388 
    323389        if not self.data: return 
    324390 
     
    346412        self.arguments = [[] for i in range(self.classValueList.count())] 
    347413 
    348         if not example and not self.parentWidget.subsetData: 
     414        if not example and not self.mosaicWidget.subsetData: 
    349415            QMessageBox.information( None, "Argumentation", 'To find arguments you first have to provide an example that you wish to classify. \nYou can do this by sending the example to the Mosaic display widget through the "Example Subset" signal.', QMessageBox.Ok + QMessageBox.Default) 
    350416            return None, None 
     
    357423            return None, None 
    358424 
    359         if example == None: example = self.parentWidget.subsetData[0] 
     425        if example == None: example = self.mosaicWidget.subsetData[0] 
    360426 
    361427        self.findArgumentsButton.hide() 
     
    382448 
    383449    def finishedAddingResults(self): 
    384         self.cancelOptimization = 0 
    385450        self.skipUpdate = 1 
    386451 
     
    464529        self.ManageTab.setEnabled(1) 
    465530 
     531 
     532    def attrsToString(self, attrList): 
     533        return ", ".join(attrList) 
     534 
     535    # ###################################################### 
     536    # Mosaic tree functions 
     537    # ###################################################### 
     538    # clear subset tree and create a new root 
     539    def mtInitSubsetTree(self): 
     540        self.subsetItems = {} 
     541        self.subsetTree.clear() 
     542        self.selectionsList.clear() 
     543        self.treeRoot = None 
     544        self.subsetTree.setColumnWidth(0, self.subsetTree.width() - self.subsetTree.columnWidth(1)-4) 
     545 
     546        if self.wholeDataSet: 
     547            root = QListViewItem(self.subsetTree, allExamplesText, str(len(self.wholeDataSet))) 
     548            root.details = {"data": self.wholeDataSet, "exampleCount": len(self.wholeDataSet)} 
     549            root.selections = {} 
     550            self.treeRoot = root 
     551            root.setOpen(1) 
     552            self.subsetTree.insertItem(root) 
     553            self.processingSubsetData = 1 
     554            self.subsetTree.setSelected(root, 1) 
     555            self.processingSubsetData = 0 
     556 
     557    # find out which attributes are currently visualized, which examples are selected and what is the additional info 
     558    def mtGetProjectionState(self, getSelectionIndices = 1): 
     559        selectedIndices = None 
     560        attrList = self.mosaicWidget.getShownAttributeList() 
     561        exampleCount = self.mosaicWidget.data and len(self.mosaicWidget.data) or 0 
     562        projDict = {"attrs": list(attrList), "exampleCount": exampleCount} 
     563        selectionDict = {"selectionConditions": list(self.mosaicWidget.selectionConditions), "selectionConditionsHistorically": list(self.mosaicWidget.selectionConditionsHistorically)} 
     564        if getSelectionIndices: 
     565            selectedIndices = self.mosaicWidget.getSelectedExamples(asExampleTable = 0) 
     566            selectionDict["selectedIndices"] = selectedIndices 
     567        return attrList, selectedIndices, projDict, selectionDict 
     568 
     569    # new element is added into the subsetTree 
     570    def mtEploreCurrentSelection(self): 
     571        if not self.wholeDataSet: 
     572            return 
     573 
     574        attrList, selectedIndices, projDict, selectionDict = self.mtGetProjectionState() 
     575 
     576        if sum(selectedIndices) == 0: 
     577            QMessageBox.information(self, "No data selection", "To explore a subset of examples you first have to select them in the projection.", QMessageBox.Ok) 
     578            return 
     579 
     580        selectedData = self.mosaicWidget.data.selectref(selectedIndices) 
     581        unselectedData = self.mosaicWidget.data.selectref(selectedIndices, negate = 1) 
     582        selectedTreeItem = self.subsetTree.selectedItem()     # current selection 
     583 
     584        # add a new item into the list box 
     585        newListItem = QListBoxText(self.selectionsList, self.mtSelectionsToString(selectionDict)) 
     586        newListItem.selections = selectionDict 
     587        self.selectionsList.setSelected(newListItem, 1) 
     588 
     589        # add a child into the tree view 
     590        attrListStr = self.attrsToString(attrList) 
     591        newTreeItem = QListViewItem(selectedTreeItem, attrListStr) 
     592        newTreeItem.details = {"attrs": list(attrList), "exampleCount": len(selectedData)} 
     593        newTreeItem.selections = selectionDict 
     594        newTreeItem.setText(1, str(len(selectedData))) 
     595        newTreeItem.setOpen(1) 
     596 
     597 
     598    # a different attribute set was selected in mosaic. update the attributes in the selected node 
     599    def mtUpdateState(self): 
     600        if not self.wholeDataSet: return 
     601        if self.processingSubsetData: return 
     602 
     603        selectedTreeItem = self.subsetTree.selectedItem() 
     604        selectedListItem = self.selectionsList.currentItem() != -1 and self.selectionsList.item(self.selectionsList.currentItem()) or None 
     605        attrList, selectionIndices, projDict, selectionDict = self.mtGetProjectionState(getSelectionIndices = 0) 
     606        if not selectedTreeItem: return 
     607 
     608        # if this is the last element in the tree, then update the element's values 
     609        if selectedTreeItem.firstChild() == None: 
     610            selectedTreeItem.setText(0, self.attrsToString(attrList)) 
     611            selectedTreeItem.details.update(projDict) 
     612            if selectedListItem: 
     613                selectedListItem.selections = selectionDict 
     614                selectedListItem.setText(self.mtSelectionsToString(selectionDict)) 
     615        # add a sibling if we changed any value 
     616        else: 
     617            # did we change the visualized attributes. If yes then we have to add a new node into the tree 
     618            if 0 in [selectedTreeItem.details[key] == projDict[key] for key in projDict.keys()]: 
     619                newTreeItem = QListViewItem(selectedTreeItem.parent() or self.subsetTree, self.attrsToString(attrList), str(selectedTreeItem.text(1))) 
     620                newTreeItem.setOpen(1) 
     621                newTreeItem.details = projDict 
     622                newTreeItem.selections = {} 
     623                self.subsetTree.setSelected(newTreeItem, 1) 
     624                self.selectionsList.clear() 
     625 
     626 
     627    # we selected a different item in the tree 
     628    def mtSelectedTreeItemChanged(self, newSelection): 
     629        if self.processingSubsetData: 
     630            return 
     631        self.processingSubsetData = 1 
     632 
     633        indices = self.mtGetItemIndices(newSelection) 
     634        selectedData = self.wholeDataSet 
     635        unselectedData = orange.ExampleTable(self.wholeDataSet.domain) 
     636        for ind in indices: 
     637            unselectedData.extend(selectedData.selectref(ind, negate = 1)) 
     638            selectedData = selectedData.selectref(ind) 
     639 
     640        # set data 
     641        if self.invertSelection: 
     642            temp = selectedData 
     643            selectedData = unselectedData 
     644            unselectedData = temp 
     645        self.mosaicWidget.setData(selectedData)  #self.mosaicWidget.setData(selectedData, onlyDrilling = 1) 
     646        if self.showDataSubset and len(unselectedData) > 0: 
     647            self.mosaicWidget.setSubsetData(unselectedData)      #self.mosaicWidget.subsetData = unselectedData 
     648        else: 
     649            self.mosaicWidget.setSubsetData(None) 
     650        self.mosaicWidget.handleNewSignals() 
     651 
     652        self.selectionsList.clear() 
     653        child = newSelection.firstChild() 
     654        while child: 
     655            selectionDict = child.selections 
     656            newListItem = QListBoxText(self.selectionsList, self.mtSelectionsToString(selectionDict)) 
     657            newListItem.selections = selectionDict 
     658            child = child.nextSibling() 
     659 
     660        self.mosaicWidget.setShownAttributes(newSelection.details.get("attrs", None)) 
     661        self.mosaicWidget.updateGraph() 
     662        self.processingSubsetData = 0 
     663 
     664    # a new selection was selected in the selection list. update the graph 
     665    def mtSelectedListItemChanged(self): 
     666        selectedListItem = self.selectionsList.currentItem() != -1 and self.selectionsList.item(self.selectionsList.currentItem()) 
     667        if not selectedListItem: 
     668            return 
     669 
     670        selectionDict = selectedListItem.selections 
     671        self.mosaicWidget.selectionConditions = list(selectionDict.get("selectionConditions", [])) 
     672        self.mosaicWidget.selectionConditionsHistorically = list(selectionDict.get("selectionConditionsHistorically", [])) 
     673        self.mosaicWidget.updateGraph() 
     674 
     675    def mtSelectedListItemDoubleClicked(self, item): 
     676        pos = self.selectionsList.currentItem() 
     677        treeItem = self.subsetTree.selectedItem().firstChild() 
     678        for i in range(pos): 
     679            treeItem = treeItem.nextSibling() 
     680        self.subsetTree.setSelected(treeItem, 1) 
     681        self.mtSelectedTreeItemChanged(treeItem) 
     682 
     683    def mtGetItemIndices(self, item): 
     684        indices = [] 
     685        while item: 
     686            ind = item.selections.get("selectedIndices", None) 
     687            if ind: 
     688                indices.insert(0, ind)        # insert indices in reverse order 
     689            item = item.parent() 
     690        return indices 
     691 
     692    def mtGetData(self, indices): 
     693        data = self.wholeDataSet 
     694        unselectedData = orange.ExampleTable(data.domain) 
     695        for ind in indices: 
     696            unselectedData.extend(data.selectref(ind, negate = 1)) 
     697            data = data.selectref(ind) 
     698        return data, unselectedData 
     699 
     700    # popup menu items 
     701    def mtRemoveSelectedItem(self): 
     702        item = self.subsetTree.selectedItem() 
     703        if not item: 
     704            return 
     705        parent = item.parent() 
     706        if parent == None: 
     707            self.mtInitSubsetTree() 
     708        else: 
     709            self.mtRemoveTreeItem(item) 
     710            self.subsetTree.setSelected(parent, 1) 
     711            self.mtSelectedTreeItemChanged(parent) 
     712 
     713    def mtSubsetTreeRemoveItemPopup(self, item, point, i): 
     714        self.subsetPopupMenu.popup(point, 0) 
     715 
     716    def resizeEvent(self, ev): 
     717        OWBaseWidget.resizeEvent(self, ev) 
     718        self.subsetTree.setColumnWidth(0, self.subsetTree.width()-self.subsetTree.columnWidth(1)-4 - 20) 
     719 
     720 
     721    def mtSelectionsToString(self, settings): 
     722        attrCombs = ["-".join(sel) for sel in settings.get("selectionConditions", [])] 
     723        return "+".join(attrCombs) 
     724 
     725 
     726    # return actual item in the tree to that str(item) == strItem 
     727    def mtStrToItem(self, strItem, currItem = -1): 
     728        if currItem == -1: 
     729            currItem = self.treeRoot 
     730        if currItem == None: 
     731            return None 
     732        if str(currItem) == strItem: 
     733            return currItem 
     734        child = currItem.firstChild() 
     735        if child: 
     736            item = self.mtStrToItem(strItem, child) 
     737            if item: 
     738                return item 
     739        return self.mtStrToItem(strItem, currItem.nextSibling()) 
     740 
     741 
     742    # save tree to a file 
     743    def mtSaveTree(self, name = None): 
     744        if name == None: 
     745            qname = QFileDialog.getSaveFileName( os.path.join(self.lastSaveDirName, "explorer tree.tree"), "Explorer tree (*.tree)", self, "", "Save tree") 
     746            if qname.isEmpty(): 
     747                return 
     748            name = str(qname) 
     749        self.lastSaveDirName = os.path.split(name)[0] 
     750 
     751        tree = {} 
     752        self.mtTreeToDict(self.treeRoot, tree) 
     753        import cPickle 
     754        f = open(name, "w") 
     755        cPickle.dump(tree, f) 
     756        f.close() 
     757 
     758    # load tree from a file 
     759    def mtLoadTree(self, name = None): 
     760        self.subsetItems = {} 
     761        self.subsetTree.clear() 
     762        self.treeRoot = None 
     763 
     764        if name == None: 
     765            name = QFileDialog.getOpenFileName( self.lastSaveDirName, "Explorer tree (*.tree)", self, "", "Load tree") 
     766            if name.isEmpty(): return 
     767            name = str(name) 
     768 
     769        self.lastSaveDirName = os.path.split(name)[0] 
     770        import cPickle 
     771        f = open(name, "r") 
     772        tree = cPickle.load(f) 
     773        self.mtDictToTree(tree, "None", self.subsetTree) 
     774        root = self.subsetTree.firstChild() 
     775        if root: 
     776            self.treeRoot = root 
     777            self.subsetTree.setSelected(root, 1) 
     778            self.selectedTreeItemChanged(root) 
     779 
     780    # generate a dictionary from the tree that can be pickled 
     781    def mtTreeToDict(self, node, tree): 
     782        if not node: return 
     783 
     784        child = node.firstChild() 
     785        if child: 
     786            self.mtTreeToDict(child, tree) 
     787 
     788        tree[str(node.parent())] = tree.get(str(node.parent()), []) + [(str(node), node.details, node.selections)] 
     789        self.mtTreeToDict(node.nextSibling(), tree) 
     790 
     791    # create a tree from a dictionary 
     792    def mtDictToTree(self, tree, currItemKey, parentItem): 
     793        if tree.has_key(currItemKey): 
     794            children = tree[currItemKey] 
     795            for (strChildNode, details, selections) in children: 
     796                strAttrs = self.attrsToString(details["attrs"]) 
     797                exampleCount = details["exampleCount"] 
     798                item = QListViewItem(parentItem, strAttrs, str(exampleCount)) 
     799                item.details = details 
     800                item.selections = selections 
     801                item.setOpen(1) 
     802                self.mtDictToTree(tree, strChildNode, item) 
     803 
     804    ################################################# 
     805    # build mosaic tree methods 
     806    def mtMosaicAutoBuildTree(self): 
     807        if str(self.autoBuildTreeButton.text()) != "Build Tree": 
     808            self.cancelTreeBuilding = 1 
     809            self.cancelEvaluation = 1 
     810        else: 
     811            try: 
     812                self.cancelTreeBuilding = 0 
     813                self.cancelEvaluation = 0 
     814                self.autoBuildTreeButton.setText("Stop Building") 
     815                qApp.processEvents() 
     816 
     817                examples = self.mosaicWidget.data 
     818                selectedItem = self.subsetTree.selectedItem() 
     819                if selectedItem and selectedItem.parent() != None: 
     820                    res = QMessageBox.information(self, "Tree Building", "Currently you are visualizing only a subset of examples. Do you want to build the tree\nonly for these examples or for all examples?", "Only for these", "For all examples", None, 0, 1) 
     821                    if res == 1: 
     822                        examples = self.wholeDataSet 
     823                        parent = self.subsetTree 
     824                    else: 
     825                        parent = selectedItem.parent() 
     826                else: 
     827                    parent = self.subsetTree 
     828                    selections = selectedItem and selectedItem.selections or {} 
     829 
     830                 #create a mosaic and use a classifier to generate a mosaic tree so that we don't set data to the main mosaic (which would mean that we would have to prevent the user from clicking the current tree) 
     831                for setting in self.settingsList: 
     832                    setattr(self.mosaic, setting, getattr(self, setting, None)) 
     833                if self.qualityMeasure == CN2_RULES: 
     834                    self.mosaic.qualityMeasure == MDL 
     835                self.mosaic.qApp = qApp 
     836                root = MosaicTreeClassifier(self.mosaic, examples, self.setStatusBarText).mosaicTree 
     837 
     838                # create tree items in the listview based on the tree in classifier.mosaicTree 
     839                if root: 
     840                    # if the selected item doesn't have any children we remove it and it will be replaced with the root of the tree that we generate 
     841                    if not selectedItem: 
     842                        self.subsetTree.clear() 
     843                    elif selectedItem.firstChild() == None: 
     844                        self.mtRemoveTreeItem(selectedItem) 
     845 
     846                    item = QListViewItem(parent, self.attrsToString(root.attrs), str(len(root.branchSelector.data))) 
     847                    item.details = {"attrs": root.attrs, "exampleCount": len(root.branchSelector.data)} 
     848                    item.selections = selections 
     849                    item.setOpen(1) 
     850                    if parent == self.subsetTree: 
     851                        self.treeRoot = item 
     852                    self.mtGenerateTreeFromClassifier(root, item) 
     853            except: 
     854                import sys 
     855                type, val, traceback = sys.exc_info() 
     856                sys.excepthook(type, val, traceback)  # print the exception 
     857            self.autoBuildTreeButton.setText("Build Tree") 
     858 
     859    def mtGenerateTreeFromClassifier(self, treeNode, parentTreeItem): 
     860        for key in treeNode.branches.keys(): 
     861            branch = treeNode.branches[key] 
     862            strAttrs = self.attrsToString(branch.attrs) 
     863            selections = treeNode.branchSelector.values[key] 
     864            exampleCount = len(branch.branchSelector.data) 
     865            item = QListViewItem(parentTreeItem, strAttrs, str(exampleCount)) 
     866            item.details = {"attrs": branch.attrs, "exampleCount": exampleCount} 
     867            item.selections = {'selectionConditions': selections, 'selectionConditionsHistorically': [selections], "selectedIndices": self.mosaicWidget.getSelectedExamples(asExampleTable = 0, selectionConditions = selections, data = treeNode.branchSelector.data, attrs = treeNode.attrs)} 
     868            item.setOpen(1) 
     869            self.mtGenerateTreeFromClassifier(branch, item) 
     870 
     871    # remove a tree item and also remove selections dict from its parent 
     872    def mtRemoveTreeItem(self, item): 
     873        parent = item.parent() 
     874        if parent == None: 
     875            parent = self.subsetTree 
     876        parent.takeItem(item) 
     877 
     878    def mtVisualizeMosaicTree(self): 
     879        tree = {} 
     880        self.mtTreeToDict(self.treeRoot, tree) 
     881        #dialog = MosaicTreeDialog(self, self.mosaicWidget, self.signalManager) 
     882        #dialog.visualizeTree(tree) 
     883        #dialog.show() 
     884        treeDialog = OWBaseWidget(self, self.signalManager, "Mosaic Tree") 
     885        treeDialog.canvasLayout = QVBoxLayout(treeDialog) 
     886        treeDialog.canvasWidget = QWidget(treeDialog) 
     887 
     888        treeDialog.canvas = QCanvas(10000, 10000) 
     889        treeDialog.canvasView = QCanvasView(treeDialog.canvas, treeDialog) 
     890        treeDialog.canvasLayout.addWidget(treeDialog.canvasView) 
     891        treeDialog.canvasLayout.activate() 
     892        treeDialog.canvasView.show() 
     893        treeDialog.resize(800, 800) 
     894        treeDialog.move(0,0) 
     895 
     896        xMosOffset = 80 
     897        xMosaicSize = self.mosaicSize + 2 * 50     # we need some space also for text labels 
     898        yMosaicSize = self.mosaicSize + 2 * 25 
     899 
     900        mosaicCanvas = self.mosaicWidget.canvas 
     901        mosaicCanvasView = self.mosaicWidget.canvasView 
     902        cellSpace = self.mosaicWidget.cellspace 
     903        self.mosaicWidget.canvas = treeDialog.canvas 
     904        self.mosaicWidget.canvasView = treeDialog.canvasView 
     905        self.mosaicWidget.cellspace = 5 
     906 
     907        nodeDict = {} 
     908        rootNode = {"treeNode": tree["None"][0][0], "parentNode": None, "childNodes": []} 
     909        rootNode.update(tree["None"][0][1]) 
     910        nodeDict[tree["None"][0][0]] = rootNode 
     911        itemsToDraw = {0: [(rootNode,)]} 
     912        treeDepth = 0 
     913        canvasItems = {} 
     914 
     915        # generate the basic structure of the tree 
     916        while itemsToDraw.has_key(treeDepth): 
     917            groups = itemsToDraw[treeDepth] 
     918            xPos = 0 
     919            for group in groups: 
     920                for node in group: 
     921                    node["currXPos"] = xPos 
     922                    xPos += xMosaicSize        # next mosaic will be to the right 
     923 
     924                    toDraw = [] 
     925                    children = tree.get(node["treeNode"], []) 
     926                    for (strNode, details, selections) in children: 
     927                        childNode = {"treeNode":strNode, "parentNode":node["treeNode"]} 
     928                        childNode.update(details) 
     929                        childNode.update(selections) 
     930                        childNode["childNodes"] = [] 
     931                        node["childNodes"].append(childNode) 
     932                        nodeDict[strNode] = childNode 
     933                        toDraw.append(childNode) 
     934                    if toDraw != []: 
     935                        itemsToDraw[treeDepth+1] = itemsToDraw.get(treeDepth+1, []) + [toDraw] 
     936            treeDepth += 1 
     937 
     938        # fix positions of mosaic so that child nodes will be under parent 
     939        changedPosition = 1 
     940        while changedPosition: 
     941            changedPosition = 0 
     942            treeDepth = max(itemsToDraw.keys()) 
     943            while treeDepth > 0: 
     944                groups = itemsToDraw[treeDepth] 
     945                xPos = 0 
     946                for group in groups: 
     947                    # the current XPositions of the group might not be valid if we moved items in the previous groups. We therefore have to move the items if their xpos is smaller than xPos 
     948                    if xPos > group[0]["currXPos"]: 
     949                        for i in range(len(group)): 
     950                            group[i]["currXPos"] = xPos + i* xMosaicSize 
     951 
     952                    groupMidXPos = (group[0]["currXPos"] + group[-1]["currXPos"]) / 2 
     953                    parentXPos = nodeDict[group[0]["parentNode"]]["currXPos"] 
     954                    if abs(parentXPos - groupMidXPos) < 5: 
     955                        xPos = group[-1]["currXPos"] + xMosaicSize 
     956                        continue 
     957                    changedPosition = 1        # we obviously have to move the parent or its children 
     958                    if parentXPos < groupMidXPos:    # move the parent forward 
     959                        self.mtRepositionNode(itemsToDraw[treeDepth-1], group[0]["parentNode"], groupMidXPos, xMosaicSize) 
     960                    elif parentXPos > groupMidXPos:    # move the children backwards 
     961                        xPos = self.mtRepositionNode(itemsToDraw[treeDepth], group[0]["treeNode"], parentXPos - (group[-1]["currXPos"] - group[0]["currXPos"])/2, xMosaicSize) 
     962                treeDepth -= 1 
     963 
     964        # visualize each mosaic diagram 
     965        colors = self.mosaicWidget.selectionColorPalette 
     966 
     967        for depth in range(max(itemsToDraw.keys())+1): 
     968            groups = itemsToDraw[depth] 
     969            yPos = 50 + (depth > 0) * 50 + depth * yMosaicSize 
     970 
     971            for group in groups: 
     972                for node in group: 
     973                    # create a dict with colors to be used to mark the selected rectangles 
     974                    selectionDict = {} 
     975                    for ind, selections in enumerate([child["selectionConditions"] for child in node["childNodes"]]): 
     976                        for selection in selections: 
     977                            selectionDict[tuple(selection)] = colors[ind] 
     978                    data, unselectedData = self.mtGetData(self.mtGetItemIndices(self.mtStrToItem(node["treeNode"]))) 
     979                    # draw the mosaic 
     980                    self.mosaicWidget.updateGraph(data, unselectedData, node["attrs"], erasePrevious = 0, positions = (node["currXPos"]+xMosOffset, yPos, self.mosaicSize), drawLegend = (depth == 0), drillUpdateSelection = 0, selectionDict = selectionDict) 
     981 
     982                    # draw a line between the parent and this node 
     983                    if node["parentNode"]: 
     984                        parent = nodeDict[node["parentNode"]] 
     985                        nodeIndex = parent["childNodes"].index(node) 
     986                        parentXPos = parent["currXPos"] + xMosaicSize/2 + 10*(-(len(parent["childNodes"])-1)/2 + nodeIndex) 
     987                        OWQCanvasFuncts.OWCanvasLine(treeDialog.canvas, parentXPos, yPos - 30, node["currXPos"] + xMosaicSize/2, yPos - 10, penWidth = 4, penColor = colors[nodeIndex]) 
     988 
     989 
     990        # restore the original canvas and canvas view 
     991        self.mosaicWidget.canvas = mosaicCanvas 
     992        self.mosaicWidget.canvasView = mosaicCanvasView 
     993        self.mosaicWidget.cellspace = cellSpace 
     994        treeDialog.show() 
     995 
     996    # find the node nodeToMove in the groups and move it to newPos. reposition also all nodes that follow this node. 
     997    def mtRepositionNode(self, groups, nodeToMove, newPos, xMosaicSize): 
     998        found = 0 
     999        for group in groups: 
     1000            for node in group: 
     1001                if node["treeNode"] == nodeToMove:        # we found the node to move 
     1002                    node["currXPos"] = newPos 
     1003                    found = 1 
     1004                    xPos = newPos + xMosaicSize 
     1005                elif found == 1: 
     1006                    node["currXPos"] = xPos 
     1007                    xPos += xMosaicSize 
     1008        return xPos     # return next valid position where to put a mosaic 
     1009 
     1010 
    4661011    # ###################################################### 
    4671012    # Auxiliary functions 
     
    4741019        return self.results[self.resultListIndices[currentItem]]      # we have to look into resultListIndices, since perhaps not all projections from the self.results are shown 
    4751020 
    476     def stopOptimizationClick(self): 
    477         self.cancelOptimization = 1 
     1021    def stopEvaluationClick(self): 
     1022        self.cancelEvaluation = 1 
    4781023 
    4791024    def isEvaluationCanceled(self): 
    480         if self.cancelOptimization: return 1 
     1025        if self.cancelEvaluation:  return 1 
    4811026        if self.useTimeLimit:       return orngMosaic.isEvaluationCanceled(self) 
    4821027 
     
    5181063 
    5191064    def resendLearner(self): 
    520         self.parentWidget.send("Learner", self.parentWidget.VizRankLearner) 
     1065        self.mosaicWidget.send("Learner", self.mosaicWidget.VizRankLearner) 
    5211066 
    5221067    def stopArgumentationClick(self): 
     
    5461091    def identifyOutliers(self): 
    5471092        import OWkNNOptimization 
    548         dialog = OWkNNOptimization.OWGraphIdentifyOutliers(self, signalManager = self.signalManager, widget = self.parentWidget, graph = None) 
     1093        dialog = OWkNNOptimization.OWGraphIdentifyOutliers(self, signalManager = self.signalManager, widget = self.mosaicWidget, graph = None) 
    5491094        dialog.setData(self.results, self.data, OWkNNOptimization.VIZRANK_MOSAIC) 
    5501095        dialog.show() 
  • orange/OrangeWidgets/Visualize/OWMosaicDisplay.py

    r3483 r3701  
    1818from OWQCanvasFuncts import * 
    1919from OWGraphTools import * 
    20 import OWDlgs, OWExplorer 
     20import OWDlgs 
    2121from orngVisFuncts import permutations 
    2222from copy import copy 
     23from OWGraphTools import ColorBrewerColors 
    2324 
    2425PEARSON = 0 
     
    104105        self.selectedSchemaIndex = 0 
    105106        self.interiorColoring = 0 
    106         self.showAprioriDistributionLines = 1 
     107        self.cellspace = 4 
    107108        self.showAprioriDistributionBoxes = 1 
    108109        self.useBoxes = 1 
    109110        self.showSubsetDataBoxes = 1 
    110         self.horizontalDistribution = 1 
     111        self.horizontalDistribution = 0 
     112        self.showAprioriDistributionLines = 0 
    111113        self.boxSize = 5 
    112114        self.exploreAttrPermutations = 0 
     
    115117        self.attr3 = "" 
    116118        self.attr4 = "" 
    117         self.cellspace = 4 
     119 
    118120        self.attributeNameOffset = 30 
    119121        self.attributeValueOffset = 15 
     
    171173 
    172174        self.optimizationDlg = OWMosaicOptimization(self, self.signalManager) 
    173         self.explorerDlg = OWExplorer.OWExplorerDialog(self, self.optimizationDlg, "Mosaic", self.signalManager) 
    174175 
    175176        optimizationButtons = OWGUI.widgetBox(self.GeneralTab, "Dialogs", orientation = "horizontal") 
    176177        optimizationButtons.setSizePolicy(QSizePolicy(QSizePolicy.Minimum , QSizePolicy.Fixed)) 
    177178        OWGUI.button(optimizationButtons, self, "VizRank", callback = self.optimizationDlg.reshow, debuggingEnabled = 0, tooltip = "Find attribute combinations that will separate different classes as clearly as possible.") 
    178         OWGUI.button(optimizationButtons, self, "Explorer", callback = self.explorerDlg.reshow, debuggingEnabled = 0, tooltip = "Select specific cells in mosaic and explore this data further.\nBuild a decision tree with mosaic diagrams.") 
    179179 
    180180        self.box7 = OWGUI.collapsableWidgetBox(self.GeneralTab, "Explore Attribute Permutations", self, "exploreAttrPermutations", callback = self.permutationListToggle) 
     
    220220        self.colorPalette = dlg.getDiscretePalette() 
    221221 
    222         self.VizRankLearner = MosaicVizRankLearner(self.optimizationDlg) 
     222        from OWGraphTools import ColorBrewerColors, defaultRGBColors 
     223        #self.selectionColorPalette = [QColor(*col) for col in ColorBrewerColors[::-1]] 
     224        self.selectionColorPalette = [QColor(*col) for col in defaultRGBColors] 
     225 
     226        self.VizRankLearner = MosaicTreeLearner(self.optimizationDlg) 
    223227        self.send("Learner", self.VizRankLearner) 
    224228 
     
    296300 
    297301        self.data = self.optimizationDlg.setData(data, self.removeUnusedValues) 
    298         self.explorerDlg.setData(self.data) 
    299302 
    300303        if self.data: 
     
    380383    # ############################################################################ 
    381384    # updateGraph - gets called every time the graph has to be updated 
    382     def updateGraph(self, data = -1, subsetData = -1, attrList = -1, erasePrevious = 1, **args): 
     385    def updateGraph(self, data = -1, subsetData = -1, attrList = -1, **args): 
    383386        # do we want to erase previous diagram? 
    384         if erasePrevious: 
     387        if args.get("erasePrevious", 1): 
    385388            for item in self.canvas.allItems(): 
    386                 if item.rtti() != 123: item.setCanvas(None)    # remove all canvas items, except SelectionCurves 
    387             for (rect, tip) in self.tooltips: QToolTip.remove(self.canvasView, rect) 
    388             self.names = []; self.tooltips = [] 
     389                if type(item) != SelectionRectangle: 
     390                    item.setCanvas(None)    # remove all canvas items, except SelectionCurves 
     391            for (rect, tip) in self.tooltips: 
     392                QToolTip.remove(self.canvasView, rect) 
     393            self.names = [] 
     394            self.tooltips = [] 
    389395 
    390396        if data == -1: 
     
    445451 
    446452        self.legend = {}        # dictionary that tells us, for what attributes did we already show the legend 
    447         for attr in attrList: self.legend[attr] = 0 
     453        for attr in attrList: 
     454            self.legend[attr] = 0 
    448455 
    449456        self.drawnSides = dict([(0,0),(1,0),(2,0),(3,0)]) 
     
    462469 
    463470        # draw rectangles 
    464         self.DrawData(attrList, (xOff, xOff+squareSize), (yOff, yOff+squareSize), 0, "", len(attrList)) 
     471        self.DrawData(attrList, (xOff, xOff+squareSize), (yOff, yOff+squareSize), 0, "", len(attrList), **args) 
    465472        if args.get("drawLegend", 1): 
    466473            self.DrawLegend(data, (xOff, xOff+squareSize), (yOff, yOff+squareSize)) # draw class legend 
    467474 
    468475        if args.get("drillUpdateSelection", 1): 
    469             self.explorerDlg.updateState() 
     476            self.optimizationDlg.mtUpdateState() 
    470477 
    471478        self.canvas.update() 
     
    475482 
    476483    ##  DRAW DATA - draw rectangles for attributes in attrList inside rect (x0,x1), (y0,y1) 
    477     def DrawData(self, attrList, (x0, x1), (y0, y1), side, condition, totalAttrs, lastValueForFirstAttribute = 0, usedAttrs = [], usedVals = [], attrVals = ""): 
     484    def DrawData(self, attrList, (x0, x1), (y0, y1), side, condition, totalAttrs, lastValueForFirstAttribute = 0, usedAttrs = [], usedVals = [], attrVals = "", **args): 
    478485        if self.conditionalDict[attrVals] == 0: 
    479486            self.addRect(x0, x1, y0, y1, attrVals = attrVals) 
     
    506513 
    507514            if side % 2 == 0:   # if drawing horizontal 
    508                 if len(attrList) == 1:  self.addRect(x0+currPos, x0+currPos+size, y0, y1, condition + 4*"&nbsp;" + attr + ": <b>" + htmlVal + "</b><br>", usedAttrs + [attr], usedVals + [val], newAttrVals) 
    509                 else:                   self.DrawData(attrList[1:], (x0+currPos, x0+currPos+size), (y0, y1), side +1, condition + 4*"&nbsp;" + attr + ": <b>" + htmlVal + "</b><br>", totalAttrs, lastValueForFirstAttribute + int(val == values[-1]), usedAttrs + [attr], usedVals + [val], newAttrVals) 
     515                if len(attrList) == 1:  self.addRect(x0+currPos, x0+currPos+size, y0, y1, condition + 4*"&nbsp;" + attr + ": <b>" + htmlVal + "</b><br>", usedAttrs + [attr], usedVals + [val], newAttrVals, **args) 
     516                else:                   self.DrawData(attrList[1:], (x0+currPos, x0+currPos+size), (y0, y1), side +1, condition + 4*"&nbsp;" + attr + ": <b>" + htmlVal + "</b><br>", totalAttrs, lastValueForFirstAttribute + int(val == values[-1]), usedAttrs + [attr], usedVals + [val], newAttrVals, **args) 
    510517            else: 
    511                 if len(attrList) == 1:  self.addRect(x0, x1, y0+currPos, y0+currPos+size, condition + 4*"&nbsp;" + attr + ": <b> " + htmlVal + "</b><br>", usedAttrs + [attr], usedVals + [val], newAttrVals) 
    512                 else:                   self.DrawData(attrList[1:], (x0, x1), (y0+currPos, y0+currPos+size), side +1, condition + 4*"&nbsp;" + attr + ": <b>" + htmlVal + "</b><br>", totalAttrs, lastValueForFirstAttribute, usedAttrs + [attr], usedVals + [val], newAttrVals) 
     518                if len(attrList) == 1:  self.addRect(x0, x1, y0+currPos, y0+currPos+size, condition + 4*"&nbsp;" + attr + ": <b> " + htmlVal + "</b><br>", usedAttrs + [attr], usedVals + [val], newAttrVals, **args) 
     519                else:                   self.DrawData(attrList[1:], (x0, x1), (y0+currPos, y0+currPos+size), side +1, condition + 4*"&nbsp;" + attr + ": <b>" + htmlVal + "</b><br>", totalAttrs, lastValueForFirstAttribute, usedAttrs + [attr], usedVals + [val], newAttrVals, **args) 
    513520            currPos += size + edge 
    514521 
     
    566573            else :            currPos += perc*height+ self.cellspace*(totalAttrs-side) 
    567574 
    568  
    569      # draw the class legend below the square 
    570     def DrawLegend(self, data, (x0, x1), (y0, y1)): 
    571         if self.interiorColoring == CLASS_DISTRIBUTION and (not data.domain.classVar or data.domain.classVar.varType == orange.VarTypes.Continuous): return 
    572  
    573         if self.interiorColoring == PEARSON: 
    574             names = ["<-8", "-8:-4", "-4:-2", "-2:2", "2:4", "4:8", ">8", "Residuals:"] 
    575             colors = self.redColors[::-1] + self.blueColors[1:] 
    576         else: 
    577             names = (list(self.attributeValuesDict.get(data.domain.classVar.name, [])) or getVariableValuesSorted(data, data.domain.classVar.name)) + [data.domain.classVar.name+":"] 
    578             colors = [self.colorPalette[i] for i in range(len(data.domain.classVar.values))] 
    579  
    580         for name in names: 
    581             self.names.append(OWCanvasText(self.canvas, name)) 
    582  
    583         totalWidth = sum([self.names[i].boundingRect().width() for i in range(len(self.names))]) 
    584  
    585         # compute the x position of the center of the legend 
    586         y = y1 + self.attributeNameOffset + 20 
    587         distance = 30 
    588         startX = (x0+x1)/2 - (totalWidth + (len(names))*distance)/2 
    589  
    590         self.names[-1].move(startX+15, y+1); self.names[-1].show() 
    591         xOffset = self.names[-1].boundingRect().width() + distance 
    592  
    593         size = 16 # 8 + 8*(self.interiorColoring == PEARSON) 
    594  
    595         for i in range(len(names)-1): 
    596             if self.interiorColoring == PEARSON: edgeColor = Qt.black 
    597             else: edgeColor = colors[i] 
    598  
    599             OWCanvasRectangle(self.canvas, startX + xOffset, y-size/2, size, size, edgeColor, colors[i]) 
    600             self.names[i].move(startX + xOffset + 18, y) 
    601             xOffset += distance + self.names[i].boundingRect().width() 
    602  
    603  
    604575    # draw a rectangle, set it to back and add it to rect list 
    605     def addRect(self, x0, x1, y0, y1, condition = "", usedAttrs = [], usedVals = [], attrVals = ""): 
     576    def addRect(self, x0, x1, y0, y1, condition = "", usedAttrs = [], usedVals = [], attrVals = "", **args): 
    606577        x0 = int(x0); x1 = int(x1); y0 = int(y0); y1 = int(y1) 
    607578        if x0 == x1: x1+=1 
     
    610581        if x1-x0 + y1-y0 == 2: y1+=1        # if we want to show a rectangle of width and height 1 it doesn't show anything. in such cases we therefore have to increase size of one edge 
    611582 
     583        if args.has_key("selectionDict") and args["selectionDict"].has_key(tuple(usedVals)): 
     584            d = 2 
     585            OWCanvasRectangle(self.canvas, x0-d, y0-d, x1-x0+1+2*d, y1-y0+1+2*d, penColor = args["selectionDict"][tuple(usedVals)], penWidth = 2, z = -100) 
     586 
    612587        rect = OWCanvasRectangle(self.canvas, x0, y0, x1-x0, y1-y0, z = 30) 
    613588 
    614589        # we have to remember which conditions were new in this update so that when we right click we can only remove the last added selections 
    615         if self.selectionRectangle != None and rect in self.canvas.collisions(self.selectionRectangle) and tuple(usedVals) not in self.selectionConditions: 
     590        if isinstance(self.selectionRectangle, QRect) and rect in self.canvas.collisions(self.selectionRectangle) and tuple(usedVals) not in self.selectionConditions: 
    616591            self.recentlyAdded = getattr(self, "recentlyAdded", []) + [tuple(usedVals)] 
    617592            self.selectionConditions = self.selectionConditions + [tuple(usedVals)] 
     
    776751        self.tooltips.append((tipRect, tooltipText)) 
    777752 
     753    # draw the class legend below the square 
     754    def DrawLegend(self, data, (x0, x1), (y0, y1)): 
     755        if self.interiorColoring == CLASS_DISTRIBUTION and (not data.domain.classVar or data.domain.classVar.varType == orange.VarTypes.Continuous): 
     756            return 
     757 
     758        if self.interiorColoring == PEARSON: 
     759            names = ["<-8", "-8:-4", "-4:-2", "-2:2", "2:4", "4:8", ">8", "Residuals:"] 
     760            colors = self.redColors[::-1] + self.blueColors[1:] 
     761        else: 
     762            names = (list(self.attributeValuesDict.get(data.domain.classVar.name, [])) or getVariableValuesSorted(data, data.domain.classVar.name)) + [data.domain.classVar.name+":"] 
     763            colors = [self.colorPalette[i] for i in range(len(data.domain.classVar.values))] 
     764 
     765        self.names = [OWCanvasText(self.canvas, name) for name in names] 
     766        totalWidth = sum([text.boundingRect().width() for text in self.names]) 
     767 
     768        # compute the x position of the center of the legend 
     769        y = y1 + self.attributeNameOffset + 20 
     770        distance = 30 
     771        startX = (x0+x1)/2 - (totalWidth + (len(names))*distance)/2 
     772 
     773        self.names[-1].move(startX+15, y+1); self.names[-1].show() 
     774        xOffset = self.names[-1].boundingRect().width() + distance 
     775 
     776        size = 16 # 8 + 8*(self.interiorColoring == PEARSON) 
     777 
     778        for i in range(len(names)-1): 
     779            if self.interiorColoring == PEARSON: edgeColor = Qt.black 
     780            else: edgeColor = colors[i] 
     781 
     782            OWCanvasRectangle(self.canvas, startX + xOffset, y-size/2, size, size, edgeColor, colors[i]) 
     783            self.names[i].move(startX + xOffset + 18, y) 
     784            xOffset += distance + self.names[i].boundingRect().width() 
     785 
    778786    def saveToFileCanvas(self): 
    779787        sizeDlg = OWDlgs.OWChooseImageSizeDlg(self.canvas) 
     
    790798    def createColorDialog(self): 
    791799        c = OWDlgs.ColorPalette(self, "Color Palette") 
    792         c.createDiscretePalette(" Discrete Palette ") 
     800        c.createDiscretePalette(" Discrete Palette ", ColorBrewerColors) 
    793801        c.setColorSchemas(self.colorSettings, self.selectedSchemaIndex) 
    794802        return c 
     
    810818            self.recentlyAdded = [] 
    811819 
    812         self.explorerDlg.updateState()            # we have already called this in self.updateGraph() call 
     820        self.optimizationDlg.mtUpdateState()            # we have already called this in self.updateGraph() call 
    813821        self.selectionRectangle = None 
    814822 
     
    822830 
    823831        self.updateGraph() 
    824 ##        self.explorerDlg.updateState()       # we have already called this in self.updateGraph() call 
     832##        self.optimizationDlg.mtUpdateState()       # we have already called this in self.updateGraph() call 
    825833        self.sendSelectedData() 
    826834 
     
    828836        self.selectionConditions = [] 
    829837        self.selectionConditionsHistorically = [] 
    830 ##        self.explorerDlg.updateState()       # removeAllSelections is always called before updateGraph() - where updateState is called 
     838##        self.optimizationDlg.mtUpdateState()       # removeAllSelections is always called before updateGraph() - where mtUpdateState is called 
    831839        self.sendSelectedData() 
    832840 
    833841    # return examples in currently selected boxes as example table or array of 0/1 values 
    834     def getSelectedExamples(self, asExampleTable = 1, negate = 0, selectionConditions = None): 
    835         attrs = self.getShownAttributeList() 
    836         if attrs == [] or not self.data: 
     842    def getSelectedExamples(self, asExampleTable = 1, negate = 0, selectionConditions = None, data = None, attrs = None): 
     843        if attrs == None:     attrs = self.getShownAttributeList() 
     844        if data == None:      data = self.data 
     845        if selectionConditions == None:    selectionConditions = self.selectionConditions 
     846 
     847        if attrs == [] or not data: 
    837848            return None 
    838849 
    839         if selectionConditions == None: 
    840             selectionConditions = self.selectionConditions 
    841  
    842850        pp = orange.Preprocessor_take() 
    843         sumIndices = numpy.zeros(len(self.data)) 
     851        sumIndices = numpy.zeros(len(data)) 
    844852        for val in selectionConditions: 
    845853            for i, attr in enumerate(attrs): 
    846                 pp.values[self.data.domain[attr]] = val[i] 
    847             indices = numpy.array(pp.selectionVector(self.data)) 
     854                pp.values[data.domain[attr]] = val[i] 
     855            indices = numpy.array(pp.selectionVector(data)) 
    848856            sumIndices += indices 
    849857        selectedIndices = list(numpy.where(sumIndices > 0, 1 - negate, 0 + negate)) 
    850858 
    851859        if asExampleTable: 
    852             return self.data.selectref(selectedIndices) 
     860            return data.selectref(selectedIndices) 
    853861        else: 
    854862            return selectedIndices 
  • orange/orngMosaic.py

    r3494 r3701  
    1 from orngCI import FeatureByCartesianProduct 
    2 import orange, orngVisFuncts, orngCN2 
    3 import time, operator 
    4 import numpy, orngContingency 
     1from orngCI import FeatureByCartesianProduct, FeatureByIM 
     2from orngEvalAttr import MeasureAttribute_Distance, MeasureAttribute_MDL, mergeAttrValues 
     3from orngCN2 import CN2UnorderedLearner 
     4from orngContingency import Entropy 
    55from math import sqrt, log, e 
     6from orngScaleData import getVariableValuesSorted, getVariableValueIndices, discretizeDomain 
     7import orange, orngVisFuncts 
     8import time, operator, numpy, sys 
    69import orngTest, orngStat, statc 
    7 from orngScaleData import getVariableValuesSorted, getVariableValueIndices, discretizeDomain 
    810from copy import copy 
     11from orngMisc import LimitedCounter 
    912 
    1013# quality measures 
     
    1215CRAMERS_PHI = 1 
    1316INFORMATION_GAIN = 2 
    14 GAIN_RATIO = 3 
    15 INTERACTION_GAIN = 4 
    16 AVERAGE_PROBABILITY_OF_CORRECT_CLASSIFICATION = 5 
    17 GINI_INDEX = 6 
    18 CN2_RULES = 7 
     17DISTANCE_MEASURE = 3 
     18MDL = 4 
     19INTERACTION_GAIN = 5 
     20AVERAGE_PROBABILITY_OF_CORRECT_CLASSIFICATION = 6 
     21GINI_INDEX = 7 
     22CN2_RULES = 8 
    1923 
    2024# conditional probability estimation 
     
    3236MEAS_GAIN_RATIO = 2 
    3337MEAS_GINI = 3 
     38MEAS_DISTANCE = 4 
     39MEAS_MDL = 5 
    3440 
    3541# items in the results list 
     
    5056MOS_COMBINING = 2 
    5157 
    52 discMeasures = [("None", None), ("ReliefF", orange.MeasureAttribute_relief(k=10, m=50)), ("Gain ratio", orange.MeasureAttribute_gainRatio()), ("Gini index", orange.MeasureAttribute_gini())] 
     58discMeasures = [("None", None), ("ReliefF", orange.MeasureAttribute_relief(k=10, m=50)), ("Gain ratio", orange.MeasureAttribute_gainRatio()), ("Gini index", orange.MeasureAttribute_gini()), ("Distance", MeasureAttribute_Distance()), ("Minimum description length", MeasureAttribute_MDL())] 
    5359 
    5460def norm_factor(p): 
     
    7177        self.attributeCount = 2 
    7278        self.optimizationType = MAXIMUM_NUMBER_OF_ATTRS 
    73         self.qualityMeasure = GINI_INDEX 
    74         self.attrDisc = MEAS_RELIEFF 
     79        self.qualityMeasure = MDL 
     80        self.attrDisc = MDL 
    7581        self.percentDataUsed = 100 
    7682 
     
    9399        self.evaluatedAttributes = None   # save last evaluated attributes 
    94100        self.stopOptimization = 0           # used to stop attribute and value order 
     101        self.cancelEvaluation = 0 
     102        self.cancelTreeBuilding = 0        # used in mosaic tree building 
    95103 
    96104        self.data = None 
     
    112120        self.clearResults() 
    113121 
     122        if data and (len(data) == 0 or len(data.domain) == 0):        # if we don't have any examples or attributes then this is not a valid data set 
     123            data = None 
     124 
    114125        self.data = discretizeDomain(data, removeUnusedValues) 
    115126        if not self.data: 
     
    156167                    self.evaluatedAttributes.remove(attr) 
    157168        except: 
    158             import sys 
    159169            type, val, traceback = sys.exc_info() 
    160170            sys.excepthook(type, val, traceback)  # print the exception 
     
    195205 
    196206    def isEvaluationCanceled(self): 
    197         stop = 0 
    198         if self.timeLimit > 0: stop = (time.time() - self.startTime) / 60 >= self.timeLimit 
    199         return stop 
     207        if self.cancelEvaluation:  return 1 
     208        if self.timeLimit > 0: 
     209            return (time.time() - self.startTime) / 60 >= self.timeLimit 
     210        else: 
     211            return 1 
    200212 
    201213    # 
     
    203215    # 
    204216    def evaluateProjections(self): 
     217        self.cancelEvaluation = 0 
    205218        if not self.data or not self.classVals: return 
    206  
    207219        fullData = self.data 
    208         if self.percentDataUsed != 100: 
    209             self.data = fullData.select(orange.MakeRandomIndices2(fullData, 1.0-float(self.percentDataUsed)/100.0)) 
    210  
    211         self.clearResults() 
    212         self.resultListIndices = [] 
    213  
    214         if self.__class__.__name__ == "OWMosaicOptimization": 
    215             self.disableControls() 
    216             self.parentWidget.progressBarInit() 
    217             from qt import qApp 
    218  
    219         self.startTime = time.time() 
    220  
    221         maxLength = self.attributeCount 
    222         if self.optimizationType == 0: minLength = self.attributeCount 
    223         else:                          minLength = 1 
    224  
    225         # generate cn2 rules and show projections that have 
    226         if self.qualityMeasure == CN2_RULES: 
    227             ruleFinder = orange.RuleBeamFinder() 
    228             ruleFinder.evaluator = orange.RuleEvaluator_Laplace() 
    229             ruleFinder.ruleStoppingValidator = orange.RuleValidator_LRS(alpha=0.2, min_coverage=0, max_rule_complexity = 4) 
    230             ruleFinder.validator = orange.RuleValidator_LRS(alpha=0.05, min_coverage=0, max_rule_complexity=4) 
    231             ruleFinder.ruleFilter = orange.RuleBeamFilter_Width(width=5) 
    232  
    233             learner = orngCN2.CN2UnorderedLearner() 
    234             learner.ruleFinder = ruleFinder 
    235             learner.coverAndRemove = orange.RuleCovererAndRemover_Default() 
     220 
     221        try: 
     222            if self.percentDataUsed != 100: 
     223                self.data = fullData.select(orange.MakeRandomIndices2(fullData, 1.0-float(self.percentDataUsed)/100.0)) 
     224 
     225            self.clearResults() 
     226            self.resultListIndices = [] 
    236227 
    237228            if self.__class__.__name__ == "OWMosaicOptimization": 
    238                 from OWCN2 import CN2ProgressBar 
    239                 learner.progressCallback = CN2ProgressBar(self.parentWidget) 
    240  
    241             classifier = learner(self.data) 
    242  
    243             self.dictResults = {} 
    244             for rule in classifier.rules: 
    245                 conds = rule.filter.conditions 
    246                 domain = rule.filter.domain 
    247                 attrs = [domain[c.position].name for c in conds] 
    248                 if len(attrs) > self.attributeCount or (self.optimizationType == EXACT_NUMBER_OF_ATTRS and len(attrs) != self.attributeCount): 
    249                     continue 
    250                 sortedAttrs = copy(attrs); sortedAttrs.sort() 
    251                 vals = [domain[c.position].values[int(c.values[0])] for c in conds] 
    252                 self.dictResults[tuple(sortedAttrs)] = self.dictResults.get(tuple(sortedAttrs), []) + [(rule.quality, attrs, vals)] 
    253  
    254             for key in self.dictResults.keys(): 
    255                 el = self.dictResults[key] 
    256                 score = sum([e[0] for e in el]) / float(len(el)) 
    257                 self.insertItem(score, el[0][1], self.findTargetIndex(score, max), 0, extraInfo = el) 
    258  
    259         else: 
    260             evaluatedAttrs = self.getEvaluatedAttributes(self.data) 
    261             if evaluatedAttrs == []: 
    262                 self.data = fullData 
    263                 self.finishEvaluation(0) 
    264                 return 
    265  
    266             # total number of possible projections 
    267             triedPossibilities = 0; totalPossibilities = 0 
    268             for i in range(minLength, maxLength+1): 
    269                 totalPossibilities += orngVisFuncts.combinationsCount(i, len(evaluatedAttrs)) 
    270             totalStr = orngVisFuncts.createStringFromNumber(totalPossibilities) 
    271  
    272             self.cvIndices = None 
    273             for z in range(len(evaluatedAttrs)): 
    274                 for u in range(minLength-1, maxLength): 
    275                     combinations = orngVisFuncts.combinations(evaluatedAttrs[:z], u) 
    276  
    277                     for attrList in combinations: 
    278                         triedPossibilities += 1 
    279  
    280                         attrs = [evaluatedAttrs[z]] + attrList 
    281                         diffVals = reduce(operator.mul, [max(1, len(self.data.domain[attr].values)) for attr in attrs]) 
    282                         if diffVals > 200: continue     # we cannot efficiently deal with projections with more than 200 different values 
    283  
    284                         val = self._Evaluate(attrs) 
    285  
    286                         if self.isEvaluationCanceled(): 
    287                             self.data = fullData 
    288                             self.finishEvaluation(triedPossibilities) 
    289                             return 
    290  
    291                         self.insertItem(val, attrs, self.findTargetIndex(val, max), triedPossibilities) 
    292  
    293                         if self.__class__.__name__ == "OWMosaicOptimization": 
    294                             self.parentWidget.progressBarSet(100.0*triedPossibilities/float(totalPossibilities)) 
    295                             self.setStatusBarText("Evaluated %s/%s visualizations..." % (orngVisFuncts.createStringFromNumber(triedPossibilities), totalStr)) 
    296                             qApp.processEvents()        # allow processing of other events 
     229                self.disableControls() 
     230                self.mosaicWidget.progressBarInit() 
     231                from qt import qApp 
     232                self.qApp = qApp 
     233 
     234            self.startTime = time.time() 
     235 
     236            maxLength = self.attributeCount 
     237            if self.optimizationType == 0: minLength = self.attributeCount 
     238            else:                          minLength = 1 
     239 
     240            # generate cn2 rules and show projections that have 
     241            if self.qualityMeasure == CN2_RULES: 
     242                ruleFinder = orange.RuleBeamFinder() 
     243                ruleFinder.evaluator = orange.RuleEvaluator_Laplace() 
     244                ruleFinder.ruleStoppingValidator = orange.RuleValidator_LRS(alpha=0.2, min_coverage=0, max_rule_complexity = 4) 
     245                ruleFinder.validator = orange.RuleValidator_LRS(alpha=0.05, min_coverage=0, max_rule_complexity=4) 
     246                ruleFinder.ruleFilter = orange.RuleBeamFilter_Width(width=5) 
     247 
     248                learner = CN2UnorderedLearner() 
     249                learner.ruleFinder = ruleFinder 
     250                learner.coverAndRemove = orange.RuleCovererAndRemover_Default() 
     251 
     252                if self.__class__.__name__ == "OWMosaicOptimization": 
     253                    from OWCN2 import CN2ProgressBar 
     254                    learner.progressCallback = CN2ProgressBar(self.mosaicWidget) 
     255 
     256                classifier = learner(self.data) 
     257 
     258                self.dictResults = {} 
     259                for rule in classifier.rules: 
     260                    conds = rule.filter.conditions 
     261                    domain = rule.filter.domain 
     262                    attrs = [domain[c.position].name for c in conds] 
     263                    if len(attrs) > self.attributeCount or (self.optimizationType == EXACT_NUMBER_OF_ATTRS and len(attrs) != self.attributeCount): 
     264                        continue 
     265                    sortedAttrs = copy(attrs); sortedAttrs.sort() 
     266                    vals = [domain[c.position].values[int(c.values[0])] for c in conds] 
     267                    self.dictResults[tuple(sortedAttrs)] = self.dictResults.get(tuple(sortedAttrs), []) + [(rule.quality, attrs, vals)] 
     268 
     269                for key in self.dictResults.keys(): 
     270                    el = self.dictResults[key] 
     271                    score = sum([e[0] for e in el]) / float(len(el)) 
     272                    self.insertItem(score, el[0][1], self.findTargetIndex(score, max), 0, extraInfo = el) 
     273 
     274            else: 
     275                evaluatedAttrs = self.getEvaluatedAttributes(self.data) 
     276                if evaluatedAttrs == []: 
     277                    self.data = fullData 
     278                    self.finishEvaluation(0) 
     279                    return 
     280 
     281                # total number of possible projections 
     282                triedPossibilities = 0; totalPossibilities = 0 
     283                for i in range(minLength, maxLength+1): 
     284                    totalPossibilities += orngVisFuncts.combinationsCount(i, len(evaluatedAttrs)) 
     285                totalStr = orngVisFuncts.createStringFromNumber(totalPossibilities) 
     286 
     287                self.cvIndices = None 
     288                for z in range(len(evaluatedAttrs)): 
     289                    for u in range(minLength-1, maxLength): 
     290                        combinations = orngVisFuncts.combinations(evaluatedAttrs[:z], u) 
     291 
     292                        for attrList in combinations: 
     293                            triedPossibilities += 1 
     294 
     295                            attrs = [evaluatedAttrs[z]] + attrList 
     296                            diffVals = reduce(operator.mul, [max(1, len(self.data.domain[attr].values)) for attr in attrs]) 
     297                            if diffVals > 200: continue     # we cannot efficiently deal with projections with more than 200 different values 
     298 
     299                            val = self._Evaluate(attrs) 
     300 
     301                            if self.isEvaluationCanceled(): 
     302                                self.data = fullData 
     303                                self.finishEvaluation(triedPossibilities) 
     304                                return 
     305                            ind = self.findTargetIndex(val, max) 
     306                            start = ind 
     307                            if ind > 0 and self.results[ind-1][0] == val: 
     308                                ind -= 1 
     309                            while ind > 0 and self.results[ind-1][0] == val and len(attrs) < len(self.results[ind-1][1]): 
     310                                ind -= 1 
     311                            while ind < len(self.results) and self.results[ind][0] == val and len(attrs) > len(self.results[ind][1]): 
     312                                ind += 1 
     313 
     314                            self.insertItem(val, attrs, ind, triedPossibilities) 
     315 
     316                            if self.__class__.__name__ == "OWMosaicOptimization": 
     317                                self.mosaicWidget.progressBarSet(100.0*triedPossibilities/float(totalPossibilities)) 
     318                                self.setStatusBarText("Evaluated %s/%s visualizations..." % (orngVisFuncts.createStringFromNumber(triedPossibilities), totalStr)) 
     319                            if hasattr(self, "qApp"): 
     320                                self.qApp.processEvents()        # allow processing of other events 
     321        except: 
     322            type, val, traceback = sys.exc_info() 
     323            sys.excepthook(type, val, traceback)  # print the exception 
    297324 
    298325        self.data = fullData 
     
    304331            secs = time.time() - self.startTime 
    305332            self.setStatusBarText("Evaluation stopped (evaluated %s projections in %d min, %d sec)" % (orngVisFuncts.createStringFromNumber(evaluatedProjections), secs/60, secs%60)) 
    306             self.parentWidget.progressBarFinished() 
     333            self.mosaicWidget.progressBarFinished() 
    307334            self.enableControls() 
    308335            self.finishedAddingResults() 
     
    329356                    retVal = sqrt(retVal / (len(self.data) * vals)) 
    330357 
    331         elif self.qualityMeasure == GAIN_RATIO: 
    332             retVal = orange.MeasureAttribute_gainRatio(newFeature, self.data) 
     358        elif self.qualityMeasure == DISTANCE_MEASURE: 
     359            retVal = MeasureAttribute_Distance(newFeature, self.data) 
     360 
     361        elif self.qualityMeasure == MDL: 
     362            retVal = MeasureAttribute_MDL(newFeature, self.data) 
    333363 
    334364        elif self.qualityMeasure == GINI_INDEX: 
     
    337367        elif self.qualityMeasure == INFORMATION_GAIN: 
    338368            retVal = orange.MeasureAttribute_info(newFeature, self.data) 
    339             classEntropy = orngContingency.Entropy(numpy.array([val for val in self.aprioriDistribution])) 
     369            classEntropy = Entropy(numpy.array([val for val in self.aprioriDistribution])) 
    340370            if classEntropy: 
    341371                retVal = retVal * 100.0 / classEntropy 
     
    346376            retVal = new - sum(gains) 
    347377 
    348             classEntropy = orngContingency.Entropy(numpy.array([val for val in self.aprioriDistribution])) 
     378            classEntropy = Entropy(numpy.array([val for val in self.aprioriDistribution])) 
    349379            if classEntropy: 
    350380                retVal = retVal * 100.0 / classEntropy 
     
    758788        if self.__class__.__name__ == "OWMosaicOptimization": 
    759789            self.setStatusBarText("Generating possible attribute orders...") 
    760             self.parentWidget.progressBarInit() 
     790            self.mosaicWidget.progressBarInit() 
    761791 
    762792        possibleOrders = [] 
     
    788818                if current % 10 == 0 and self.__class__.__name__ == "OWMosaicOptimization": 
    789819                    self.setStatusBarText("Evaluated %s/%s attribute orders..." % (orngVisFuncts.createStringFromNumber(current), strCount)) 
    790                     self.parentWidget.progressBarSet(100*current/float(total)) 
     820                    self.mosaicWidget.progressBarSet(100*current/float(total)) 
    791821                    if self.stopOptimization: break 
    792822            bestPlacements.append(max(tempPerms)) 
     
    794824        if self.__class__.__name__ == "OWMosaicOptimization": 
    795825            self.setStatusBarText("") 
    796             self.parentWidget.progressBarFinished() 
     826            self.mosaicWidget.progressBarFinished() 
    797827 
    798828        bestPlacements.sort() 
     
    824854 
    825855        if self.__class__.__name__ == "OWMosaicOptimization": 
    826  
    827856            self.resultList.insertItem("%.3f : %s" % (score, self.buildAttrString(attrList)), index) 
    828857            self.resultListIndices.insert(index, index) 
     
    894923 
    895924# ############################################################################# 
    896 # class that represents kNN classifier that classifies examples based on top evaluated projections 
    897 class MosaicVizRankClassifier(orange.Classifier): 
    898     def __init__(self, mosaic, data): 
    899         self.Mosaic = mosaic 
    900  
    901         if self.Mosaic.__class__.__name__ == "OWMosaicOptimization": 
    902             self.Mosaic.parentWidget.subsetdataHander(None) 
    903             self.Mosaic.parentWidget.setData(data) 
    904         else: 
    905             self.Mosaic.setData(data) 
    906  
    907         #if self.Mosaic.__class__.__name__ == "OWMosaicOptimization": self.Mosaic.useTimeLimit = 1 
    908         self.Mosaic.evaluateProjections() 
    909         #if self.Mosaic.__class__.__name__ == "OWMosaicOptimization": del self.Mosaic.useTimeLimit 
     925# definition of tree of mosaics 
     926class MosaicTreeNode: 
     927    def __init__(self, parent, attrs): 
     928        self.children = {} 
     929        self.parent = parent 
     930        self.attrs = attrs 
     931        self.branches = {}        # links to other MosaicTreeNode instances that represent branches. Keys are attribute values, e.g. ([0,1,3], [1,2,2]) 
     932        self.branchSelector = None 
     933        self.selectionIndices = None 
     934 
     935 
     936# ############################################################################# 
     937# learner that builds MosaicTreeLearner 
     938class MosaicTreeLearner(orange.Learner): 
     939    def __init__(self, mosaic = None, statusFunct = None): 
     940        if not mosaic: 
     941            mosaic = orngMosaic() 
     942        self.mosaic = mosaic 
     943        self.statusFunct = statusFunct 
     944        #self.mosaic.qualityMeasure = MDL        # always use MDL for estimating quality of projections - this also stops building tree if no combination of attributes produces an improvement 
     945        self.name = self.mosaic.learnerName 
     946 
     947    def __call__(self, examples, weightID = 0): 
     948        return MosaicTreeClassifier(self.mosaic, examples, self.statusFunct) 
     949 
     950 
     951# ############################################################################# 
     952# class that builds a tree of mosaics that can be used as a classifier 
     953class MosaicTreeClassifier(orange.Classifier): 
     954    def __init__(self, mosaic, data, statusFunct = None): 
     955        self.mosaic = mosaic 
     956 
     957        # discretize domain if necessary 
     958        mosaic.setData(data) 
     959        data = mosaic.data 
     960 
     961        stop = orange.TreeStopCriteria_common() 
     962        stop.minExamples = 5 
     963        self.mosaicTree = None 
     964 
     965        treeLearner = orange.TreeLearner() 
     966        treeLearner.split = SplitConstructor_MosaicMeasure(mosaic, statusFunct) 
     967        treeLearner.stop = stop 
     968        tree = treeLearner(data) 
     969        if tree.tree and tree.tree.branchSelector: 
     970            self.mosaicTree = self.createTreeNodes(tree.tree, data, None, [1]*len(data)) 
     971        if statusFunct: 
     972            if self.mosaicTree: 
     973                statusFunct("Mosaic tree was built successfully.") 
     974            else: 
     975                statusFunct("No tree was generated.") 
     976 
     977    def createTreeNodes(self, node, data, parentTreeNode, selectionIndices): 
     978        treeNode = MosaicTreeNode(parentTreeNode, node.branchSelector.attrList) 
     979        treeNode.branchSelector = node.branchSelector 
     980        treeNode.selectionIndices = selectionIndices 
     981 
     982        if node.branches:    # if internal node 
     983            for i in range(len(node.branches)): 
     984                if not node.branches[i] or not node.branches[i].branchSelector: 
     985                    continue        # if we are at a leaf 
     986 
     987                selectedAttrValues = node.branchDescriptions[i] 
     988                pp = orange.Preprocessor_take() 
     989                pp.values[node.branchSelector.classVar] = selectedAttrValues 
     990                selectedIndices = list(pp.selectionVector(data.select([node.branchSelector.classVar]))) 
     991                selectedData = data.selectref(selectedIndices) 
     992 
     993                treeNode.branches[selectedAttrValues] = self.createTreeNodes(node.branches[i], selectedData, treeNode, selectedIndices) 
     994 
     995        return treeNode 
    910996 
    911997 
    912998    # for a given example run argumentation and find out to which class it most often fall 
    913999    def __call__(self, example, returnType = orange.GetBoth): 
    914         # if in widget, also show the example 
    915         if self.Mosaic.__class__.__name__ == "OWMosaicOptimization": 
    916             table = orange.ExampleTable(example.domain) 
    917             table.append(example) 
    918             self.Mosaic.parentWidget.subsetdataHander(table) 
    919  
    920         classVal, prob = self.Mosaic.findArguments(example) 
    921  
    922         if returnType == orange.GetBoth: return classVal, prob 
    923         else:                            return classVal 
    924  
    925  
    926 # ############################################################################# 
    927 # learner that builds MosaicVizRankLearner 
    928 class MosaicVizRankLearner(orange.Learner): 
    929     def __init__(self, mosaic = None): 
    930         if not mosaic: mosaic = orngMosaic() 
    931         self.Mosaic = mosaic 
    932         self.name = self.Mosaic.learnerName 
    933  
    934     def __call__(self, examples, weightID = 0): 
    935         return MosaicVizRankClassifier(self.Mosaic, examples) 
    936  
     1000        currNode = self.mosaicTree 
     1001        while currNode: 
     1002            val = currNode.branchSelector.classVar.getValueFrom(example).value 
     1003            if currNode.branches.has_key(val): 
     1004                currNode = currNode.branches[val] 
     1005            else: 
     1006                return currNode.branchSelector.classifyExample(example, returnType)        # we are in the leaf of the mosaic tree. classify to the prevailing class 
     1007 
     1008 
     1009# a measure that evaluates different projections and then says that the best "attribute" is the best projection 
     1010class SplitConstructor_MosaicMeasure(orange.TreeSplitConstructor): 
     1011    def __init__(self, mosaic, statusFunct = None): 
     1012        self.mosaic = mosaic 
     1013        self.statusFunct = statusFunct 
     1014        self.nodeCount = 0 
     1015        self.measure = MeasureAttribute_MDL() 
     1016 
     1017    def updateStatus(self, evaluatingProjections): 
     1018        if self.statusFunct: 
     1019            s = "%sCurrent tree has %d nodes" % (evaluatingProjections and "Evaluating projections. " or "", self.nodeCount) 
     1020            self.statusFunct(s) 
     1021 
     1022    def __call__(self, gen, weightID, contingencies, apriori, candidates, nodeClassifier): 
     1023        self.mosaic.setData(gen) 
     1024        self.updateStatus(1) 
     1025        if self.mosaic.cancelTreeBuilding: 
     1026            return None 
     1027        self.mosaic.evaluateProjections() 
     1028        if self.mosaic.cancelTreeBuilding or len(self.mosaic.results) == 0:       # or self.mosaic.results[0][0] <= 0:     # if no results or score <=0 then stop building 
     1029            #self.nodeCount += 1 
     1030            self.updateStatus(0) 
     1031            return None 
     1032 
     1033        score, attrList, tryIndex, extraInfo = self.mosaic.results[0] 
     1034 
     1035        newFeature = mergeAttrValues(gen, attrList, self.measure, removeUnusedValues = 0) 
     1036        dist = orange.Distribution(newFeature, gen).values() 
     1037        if max(dist) == sum(dist):    # if all examples belong to one attribute value then this is obviously a useless attribute and we should stop building 
     1038            #self.nodeCount += 1 
     1039            self.updateStatus(0) 
     1040            return None 
     1041 
     1042##        if len(attrList) > 1: 
     1043##            # remove all other attributes and examples with missing values and then try to combine different attribute values 
     1044##            subgen = orange.Preprocessor_dropMissing(gen.select(attrList + [gen.domain.classVar.name])) 
     1045##            #newFeature, quality = FeatureByIM(subgen, attrList, binary = 0, measure = orange.MeasureAttribute_info()) 
     1046##            newFeature, quality = FeatureByIM(subgen, attrList, binary = 0, measure = MeasureAttribute_MDL()) 
     1047##        else: 
     1048##            newFeature = gen.domain[attrList[0]] 
     1049##            dist = orange.Distribution(newFeature, gen).values() 
     1050##            if max(dist) == sum(dist):    # if all examples belong to one attribute value then this is obviously a useless attribute and we should stop building 
     1051##                return None 
     1052##            newFeature.getValueFrom = orange.ClassifierByLookupTable(newFeature, newFeature, list(newFeature.values) + ["?"]) 
     1053        self.nodeCount += 1 
     1054        self.updateStatus(0) 
     1055        return (CartesianClassifier(newFeature, attrList, gen), newFeature.values, None, score) 
     1056 
     1057 
     1058class CartesianClassifier(orange.Classifier): 
     1059    def __init__(self, var, attrList, data): 
     1060        self.classVar = var 
     1061        self.attrList = attrList 
     1062        self.data = data 
     1063        self.valueMapping = dict(self.createValueDict(attrList, [])) 
     1064 
     1065        self.values = {}    # dict of "3-1+8-9+2-2" -> [(3,1), (8,9), (2,2)] 
     1066        #for classVal in self.classVar.values:       # we cannot use this because when combining discretized attributes, classVals are c1, c2, ... and not 3-1+... 
     1067        #    self.values[classVal] = filter(None, [self.valueMapping.get(val, None) for val in classVal.split("+")]) 
     1068        for val in self.classVar.values: 
     1069            self.values[val] = [] 
     1070        for ind, combination in enumerate(LimitedCounter([len(data.domain[attr].values) for attr in self.attrList])): 
     1071            self.values[self.classVar.getValueFrom.lookupTable[ind].value].append(tuple([data.domain[self.attrList[attrInd]].values[attrValInd] for attrInd, attrValInd in enumerate(combination)])) 
     1072 
     1073 
     1074    # create a mapping from "0-1-1-4" back to [0, 1, 1, 4] 
     1075    def createValueDict(self, attrList, valueList): 
     1076        if attrList == []: return valueList 
     1077 
     1078        attrValues = self.data.domain[attrList[0]].values 
     1079        if valueList == []: 
     1080            return self.createValueDict(attrList[1:], [(val, (val,)) for val in attrValues]) 
     1081        else: 
     1082            newValueList = [] 
     1083            for val in attrValues: 
     1084                newValueList += [(pre+"-"+val, vals+(val,)) for (pre, vals) in valueList] 
     1085            return self.createValueDict(attrList[1:], newValueList) 
     1086 
     1087 
     1088    # determine to which class would the example be classifed based on this combination of attributes 
     1089    # i.e. get the majority class for this cartesian product of attributes 
     1090    def classifyExample(self, ex, what = orange.Classifier.GetValue): 
     1091        val = self.classVar.getValueFrom(ex).value 
     1092        classDist = orange.ContingencyAttrClass(self.classVar, self.data)[val] 
     1093        classValue = classDist.keys()[classDist.values().index(max(classDist.values()))] 
     1094        if what == orange.Classifier.GetValue: 
     1095            return orange.Value(self.data.domain.classVar, classValue) 
     1096        elif what == orange.Classifier.GetProbabilities: 
     1097            return classDist 
     1098        else: 
     1099            return (orange.Value(self.data.domain.classVar, classValue), classDist) 
     1100 
     1101 
     1102    # clasify the example ex based on the self.data 
     1103    def __call__(self, ex, what = orange.Classifier.GetValue): 
     1104        if 1 in [ex[attr].isSpecial() for attr in self.attrList]: 
     1105            return orange.Value("?") 
     1106        val = self.classVar.getValueFrom(ex) 
     1107        if what == orange.Classifier.GetValue: 
     1108            return val 
     1109        probs = orange.DiscDistribution(self.classVar) 
     1110        probs[val] = 1.0 
     1111        if what == orange.Classifier.GetProbabilities: 
     1112            return probs 
     1113        else: 
     1114            return (val, probs) 
    9371115 
    9381116 
    9391117#test widget appearance 
    9401118if __name__=="__main__": 
    941     data = orange.ExampleTable(r"E:\Development\Python23\Lib\site-packages\Orange\Datasets\UCI\wine.tab") 
    942     #data = orange.ExampleTable(r"E:\Development\Python23\Lib\site-packages\Orange\datasets\microarray\brown\brown-imputed.tab") 
    943     data = orange.ExampleTable(r"E:\Development\Python23\Lib\site-packages\Orange Datasets\UCI\zoo.tab") 
     1119    #data = orange.ExampleTable(r"E:\Development\Orange Datasets\UCI\wine.tab") 
     1120    #data = orange.ExampleTable(r"E:\Development\Orange Datasets\uci\brown-selected.tab") 
     1121    data = orange.ExampleTable(r"E:\Development\Orange Datasets\UCI\zoo.tab") 
     1122    #data = orange.ExampleTable(r"E:\Development\Orange Datasets\UCI\breast-cancer-wisconsin-disc.tab") 
     1123    #data = orange.ExampleTable(r"E:\\temp.tab") 
     1124    a = mergeAttrValues(data, ["milk", "legs"], MeasureAttribute_MDL()) 
     1125 
    9441126    mosaic = orngMosaic() 
    9451127    mosaic.setData(data) 
     1128    mosaic.qualityMeasure = DISTANCE_MEASURE 
     1129    mosaic.evaluateProjections() 
    9461130    #ret = mosaic.findOptimalAttributeOrder(["spo- early", "heat 20"], 1) #optimizeValueOrder = 1 
    9471131    #ret = mosaic.findOptimalAttributeOrder(["A11", "A13", "A7"], 1) #optimizeValueOrder = 1 
     
    9491133##    mosaic.evaluateProjections() 
    9501134##    classVal, prob = mosaic.findArguments(mosaic.data[25]) 
    951     mosaic.findOptimalAttributeOrder(["milk", "legs"]) 
     1135    #mosaic.findOptimalAttributeOrder(["milk", "legs"]) 
     1136 
     1137    learner = MosaicTreeLearner(mosaic) 
     1138    classifier = learner(data) 
     1139    #print classifier(data[0]) 
     1140    #mosaic.qualityMeasure = GINI_INDEX 
     1141    #print "Gini", mosaic._Evaluate(["domestic", "predator", "venomous"]) 
     1142    #mosaic.qualityMeasure = DISTANCE_MEASURE 
     1143    #print "Distance", mosaic._Evaluate(["domestic", "predator", "venomous"]) 
     1144    #mosaic.qualityMeasure = MDL 
     1145    #print "MDL", mosaic._Evaluate(["domestic", "predator", "venomous"]) 
Note: See TracChangeset for help on using the changeset viewer.