Orange Forum • View topic - Random Forest on Canvas(as a widget)

Random Forest on Canvas(as a widget)

A place to ask questions about methods in Orange and how they are used and other general support.

Random Forest on Canvas(as a widget)

Postby Guest » Thu Oct 20, 2005 18:05

Will Random Forest be availabe to canvas soon?

Thank you,

Fred

Random Forest as Canvas Widget

Postby Fabrice C. » Tue Jan 10, 2006 8:53

Hello,
I had the same need as you so I decided to try and read the Widget API...
These are my first two widgets so everything may not be perfect : they are based on Classification Tree and Calssification Tree Viewer2D widgets.

The first widget enables to create and configure your random forest, the second allows to visualise the individual trees in your forest.

Now for the files :

----
Python code : save as OWRandomForest.py and add it to your orange\OrangeWidgets\Classify directory
----


Code: Select all

"""
<name>Random Forest</name>
<description>Random Forest learner/classifier.</description>
<icon>icons/RandomForest.png</icon>
<priority>80</priority>
"""

from OWWidget import *
import orngTree
import orngEnsemble
import OWGUI

class OWRandomForest(OWWidget):
    settingsList = ["name",
                    "NbrOfTrees", "NbrOfAttribs",
                    "estim", "relK", "relM",
                    "bin", "subset",
                    "preLeafInst", "preNodeInst", "preNodeMaj",
                    "preLeafInstP", "preNodeInstP", "preNodeMajP",
                    "postMaj", "postMPruning", "postM"]

    # If you change this, you need to change measureChanged as well,
    # because it enables/disables two widgets when ReliefF is chosen
    measures = (("Information Gain", "infoGain"),
                ("Gain Ratio", "gainRatio"),
                ("Gini Index", "gini"),
                ("ReliefF", "relief"))

   
   
    def __init__(self, parent=None, signalManager = None, name='Random Forrest'):
        OWWidget.__init__(self, parent, signalManager, name)

        self.inputs = [("Classified Examples", ExampleTableWithClass, self.dataset)]
        self.outputs = [("Learner", orange.Learner),("Classifier", orange.Classifier)]

        # Settings
        self.name = 'Random Forrest'
        self.NbrOfTrees = 20
        self.NbrOfAttribs=0
        self.estim = 0; self.relK = 5; self.relM = 100
        self.bin = 0; self.subset = 0
        self.preLeafInstP = 2; self.preNodeInstP = 5; self.preNodeMajP = 95
        self.preLeafInst = 1; self.preNodeInst = 0; self.preNodeMaj = 0
        self.postMaj = 1; self.postMPruning = 1; self.postM = 2.0

       
        self.loadSettings()
       
        self.data = None                    # input data set
        self.Treelearner=None
        self.learner=None
        self.preprocessor = None            # no preprocessing as default
        self.setLearner()                   # this just sets the learner, no data
                                            # has come to the input yet
       
        # GUI
        # name
        OWGUI.lineEdit(self.controlArea, self, 'name', box='Learner/Classifier Name', \
                 tooltip='Name to be used by other widgets to identify your learner/classifier.')
        OWGUI.separator(self.controlArea)
       
        # Forest Options
        TBox = QVGroupBox(self.controlArea)
        TBox.setTitle(' Forest Options')

        self.spinTrees=OWGUI.spin(TBox, self, "NbrOfTrees", 1, 5000, 10, label="Number of trees in forest : ")
        self.spinAttribs=OWGUI.spin(TBox, self, "NbrOfAttribs", 0, 5000, 1, label="Number of attributes used at each node (0 : automatic) : ")
        OWGUI.separator(self.controlArea)
       
        # attribute quality estimation
        qBox = QVGroupBox(self.controlArea)
        qBox.setTitle('Attribute Quality Estimation')

        self.qMea = QComboBox(qBox)
        for m in self.measures:
            self.qMea.insertItem(m[0])
        self.qMea.setCurrentItem(self.estim)
        self.connect(self.qMea, SIGNAL("activated(int)"), self.measureChanged)
       
        self.hbxRel1 = OWGUI.spin(qBox, self, "relM", 1, 1000, 10, label="Relief's reference examples: ")
        self.hbxRel2 = OWGUI.spin(qBox, self, "relK", 1, 50, label="Relief's neighbours")
        OWGUI.separator(self.controlArea)
       
        # structure of the tree
        self.cbBin = OWGUI.checkBox(self.controlArea, self, 'bin', 'Binarization', box='Tree Structure')
        OWGUI.separator(self.controlArea)

        self.measureChanged(self.estim)

        # prepruning
        self.pBox = QVGroupBox(self.controlArea)
        self.pBox.setTitle('Pre-Pruning')

        self.preLeafInstBox, self.preLeafInstPBox = \
          OWGUI.checkWithSpin(self.pBox, self, "Min. instances in leaves: ", 1, 1000, "preLeafInst", "preLeafInstP")
        self.preNodeInstBox, self.preNodeInstPBox = \
          OWGUI.checkWithSpin(self.pBox, self, "Stop splitting nodes with ", 1, 1000, "preNodeInst", "preNodeInstP", " or fewer instances")
        self.preNodeMajBox, self.preNodeMajPBox = \
          OWGUI.checkWithSpin(self.pBox, self, "Stop splitting nodes with ", 1, 100, "preNodeMaj", "preNodeMajP", "% of majority class")
       
        OWGUI.separator(self.controlArea)
        self.mBox = QVGroupBox(self.controlArea)

        # post-pruning
        self.mBox.setTitle('Post-Pruning')
        OWGUI.checkBox(self.mBox, self, 'postMaj', 'Recursively merge leaves with same majority class')
        self.postMPruningBox, self.postMPruningPBox = \
          OWGUI.checkWithSpin(self.mBox, self, "m for m-error pruning ", 0, 1000, 'postMPruning', 'postM')


        # apply button
        OWGUI.separator(self.controlArea)
        self.btnApply = OWGUI.button(self.controlArea, self, "&Apply Changes", callback = self.setLearner, disabled=0)

        self.resize(100,430)

    # main part:         

    def setLearner(self):
        if hasattr(self, "btnApply"):
            self.btnApply.setFocus()
        self.progressBarInit()
       
        self.Treelearner=orngTree.TreeLearner(measure = self.measures[self.estim][1],
                                                reliefK = self.relK, reliefM = self.relM,
                                                binarization = self.bin,
                                                minExamples = self.preNodeInst and self.preNodeInstP,
                                                minSubset = self.preLeafInst and self.preLeafInstP,
                                                maxMajority = self.preNodeMaj and self.preNodeMajP/100.0 or 1.0,
                                                sameMajorityPruning = self.postMaj,
                                                mForPruning = self.postMPruning and self.postM,
                                                storeExamples = 1).instance()
           
        if self.data<>None:
            if self.NbrOfAttribs > len(self.data.domain.attributes):
                self.NbrOfAttribs=0

        if self.NbrOfAttribs==0 :
            Attr=None
        else :
            Attr=self.NbrOfAttribs
        self.learner = orngEnsemble.RandomForestLearner(trees=self.NbrOfTrees, name=self.name, attributes=Attr,
                                                        learner=self.Treelearner, callback= lambda x : self.progressBarSet(x*100))
                                   
        self.learner.name = self.name
        self.send("Learner", self.learner)
        if self.data <> None:
            self.classifier = self.learner(self.data)
            self.classifier.name = self.name
            self.send("Classifier", self.classifier)

        self.progressBarFinished()   



    def measureChanged(self, idx):
        self.estim = idx
        self.hbxRel1.setEnabled(idx == 3)
        self.hbxRel2.setEnabled(idx == 3)
        self.cbBin.setEnabled(idx != 3)
        if idx==3:
            self.prevBinState = self.bin
            self.bin = 0
        else:
            if hasattr(self, "prevBinState"):
                self.bin = self.prevBinState


    # handle input signals       

    def dataset(self,data):
        self.data = data
        if self.data:
            self.setLearner()
        else:
            self.send("Classifier", None)
           




----
Python code : save as OWRandomForestViewer2D.py and add it to your orange\OrangeWidgets\Classify directory
----

Code: Select all

"""
<name>Random Forest Viewer 2D</name>
<description>Random Forest viewer (graph view).</description>
<icon>icons/RandomForestViewer2D.png</icon>
<priority>2115</priority>
"""

from OWTreeViewer2D import *
import OWGraphTools

class ClassificationNode(CanvasNode):
    def __init__(self,attrVal,*args):
        CanvasNode.__init__(self,*args)
        self.dist=self.tree.distribution
        self.attrVal=attrVal
        maxInst=max(self.dist)
        #self.majClass=ind=list(self.dist).index(maxProb)
        self.majClass = filter(lambda i, m=maxInst: self.dist[i]==m, range(len(self.dist)))
        ind=self.majClass[0]
        self.majClassName=self.dist.items()[ind][0]
        self.majClassProb=maxInst/self.dist.cases
        self.tarClassProb=self.dist.items()[0][1]/self.dist.cases
        self.numInst=self.dist.cases
        self.title=QCanvasText(attrVal,self.canvas())
        self.texts=[self.majClassName, "%.3f" % self.majClassProb, "%.3f" % self.tarClassProb, "%.1f" % self.numInst]
        self.name = (self.tree.branches and self.tree.branchSelector.classVar.name) or self.majClassName
        self.textAdvance=12
        self.addTextLine(attrVal, fitSquare=False)
        self.addTextLine("", fitSquare=False)
        self.addTextLine("", fitSquare=False)
        self.addTextLine(fitSquare=False)
        self.addTextLine(self.name, fitSquare=False)
           
        self.rule=(isinstance(self.parent, QCanvasRectangle) and self.parent.rule+[(self.parent.tree.branchSelector.classVar, attrVal)]) or []
       
        #print self.rule
        #self.textObj.extend([self.title, self.name]+self.textList)
        self.textInd=[]       
        self.pieObj=[]
        distSum=sum(self.dist)
        color=OWGraphTools.ColorPaletteHSV(len(self.dist))
        startAngle=0
        for i in range(len(self.dist)):
            angle=360/distSum*self.dist[i]*16
            e=QCanvasEllipse(self.height()*0.8,self.height()*0.8,startAngle,angle,self.canvas())
            e.setBrush(QBrush(color[i]))
            e.move(self.height()/2,self.width())
            e.setZ(0)
            startAngle+=angle
            self.pieObj.append(e)
        e=QCanvasEllipse(self.height()*0.8+4,self.height()*0.8+4,0,360*16,self.canvas())
        e.setBrush(QBrush(Qt.black))
        e.move(self.height(), self.width())
        e.setZ(-1)
        self.pieObj.append(e)
        self.canvasObj.extend(self.pieObj)
        self.isPieShown=True           

    def setSize(self,w,h):
        CanvasNode.setSize(self,w,h)
        self.updateText()
        for e in self.pieObj:
            e.setSize(h*0.8,h*0.8)
            e.move(self.x()+self.width(),self.y()+self.height()/2)
        self.pieObj[-1].setSize(h*0.8+2,h*0.8+2)

    def setBrush(self, brush):
        CanvasTextContainer.setBrush(self, brush)
        if self.textObj:
            self.textObj[0].setColor(Qt.black)
           
    def show(self):
        CanvasNode.show(self)
        if not self.isPieShown:
            for e in self.pieObj:
                e.hide()

    def setPieVisible(self, b=True):
        self.isPieShown=b
        if self.isShown and b:
            for e in self.pieObj:
                e.show()
        else:
            for e in self.pieObj:
                e.hide()
               
    def setText(self, textInd=[]):
        self.textInd=textInd
        j=1
        for i in textInd:
            CanvasNode.setText(self, j, self.texts[i], fitSquare=False)
            j+=1
        for i in range(len(textInd),2):
            CanvasNode.setText(self, i+1, "", fitSquare=False)
       
    def updateText(self):
        self.textAdvance=float(self.height())/3
        self.lineSpacing=0
        self.setFont(QFont("",self.textAdvance*0.7), False)
        self.reArangeText(False, -self.textAdvance-self.lineSpacing)


    def reArangeText(self, fitSquare=True, startOffset=0):
        self.textOffset=startOffset
        x,y=self.x(),self.y()
        for i in range(4):
            self.textObj[i].move(x+1, y+(i-1)*self.textAdvance)
        self.spliterObj[0].move(x, y+self.height()-self.textAdvance)

import re
import sets
def parseRules(rules):
    def joinCont(rule1, rule2):
        int1, int2=["(",-1e1000,1e1000,")"], ["(",-1e1000,1e1000,")"]
        rule=[rule1, rule2]
        interval=[int1, int2]
        for i in [0,1]:
            if rule[i][1].startswith("in"):
                r=rule[i][1][2:]
                interval[i]=[r.strip(" ")[0]]+map(lambda a: float(a), r.strip("()[] ").split(","))+[r.strip(" ")[-1]]
            else:
                if "<" in rule[i][1]:
                    interval[i][3]=("=" in rule[i][1] and "]") or ")"
                    interval[i][2]=float(rule[i][1].strip("<>= "))
                else:
                    interval[i][0]=("=" in rule[i][1] and "[") or "("
                    interval[i][1]=float(rule[i][1].strip("<>= "))
               
        inter=[None]*4

        if interval[0][1]<interval[1][1] or (interval[0][1]==interval[1][1] and interval[0][0]=="["):
            interval.reverse()
        inter[:2]=interval[0][:2]

        if interval[0][2]>interval[1][2] or (interval[0][2]==interval[1][2] and interval[0][3]=="]"):
            interval.reverse()
        inter[2:]=interval[0][2:]
           

        if 1e1000 in inter or -1e1000 in inter:
            rule=((-1e1000==inter[1] and "<") or ">")
            rule+=(("[" in inter or "]" in inter) and "=") or ""
            rule+=(-1e1000==inter[1] and str(inter[2])) or str(inter[1])
        else:
            rule="in "+inter[0]+str(inter[1])+","+str(inter[2])+inter[3]
        return (rule1[0], rule)
   
    def joinDisc(rule1, rule2):
        r1,r2=rule1[1],rule2[1]
        r1=re.sub("^in ","",r1)
        r2=re.sub("^in ","",r2)
        r1=r1.strip("[]=")
        r2=r2.strip("[]=")
        s1=sets.Set([s.strip(" ") for s in r1.split(",")])
        s2=sets.Set([s.strip(" ") for s in r2.split(",")])
        s=s1 & s2
        if len(s)==1:
            return (rule1[0], "= "+str(list(s)[0]))
        else:
            return (rule1[0], "in ["+",".join([str(st) for st in s])+"]")

    rules.sort(lambda a,b: (a[0].name<b[0].name and -1) or 1 )
    newRules=[rules[0]]
    for r in rules[1:]:
        if r[0].name==newRules[-1][0].name:
            if re.search("(a-zA-Z\"')+",r[1].lstrip("in")):
                newRules[-1]=joinDisc(r,newRules[-1])
            else:
                newRules[-1]=joinCont(r,newRules[-1])
        else:
            newRules.append(r)
    return newRules     

BodyColor_Default = QColor(255, 225, 10)
BodyCasesColor_Default = QColor(0, 0, 128)

class OWRandomForestViewer2D(OWTreeViewer2D):
    def __init__(self, parent=None, signalManager = None, name='ClassificationTreeViewer2D'):
        OWTreeViewer2D.__init__(self, parent, signalManager, name)
        self.settingsList=self.settingsList+["ShowPie","TargetClassIndex"]
       
        self.inputs = [("Random Forest Classifier", orange.Classifier, self.ctree)]
        self.outputs = [("Classified Examples", ExampleTableWithClass), ("Examples", ExampleTable)]

        self.forest=None
        self.currentviewing=1

        OWGUI.spin(self.controlArea, self, 'currentviewing', min=1, max=5000, step=1,
                   label='Tree :', callback=self.ctree)
        self.NbrOfTrees=QLabel('"(No tree in forest yet)"', self.controlArea)
        self.ShowPies=1
        self.TargetClassIndex=0
        self.canvas=TreeCanvas(self)
        self.canvasView=TreeCanvasView(self, self.canvas, self.mainArea, "CView")
        layout=QVBoxLayout(self.mainArea)
        layout.addWidget(self.canvasView)
        self.canvas.resize(800,800)
        self.canvasView.bubbleConstructor=self.classificationBubbleConstructor
        self.navWidget=QWidget(None, "Navigator")
        self.navWidget.lay=QVBoxLayout(self.navWidget)
        canvas=TreeCanvas(self.navWidget)
        self.treeNav=TreeNavigator(self.canvasView,self,canvas,self.navWidget, "Nav")
        self.treeNav.setCanvas(canvas)
        self.navWidget.lay.addWidget(self.treeNav)
        self.canvasView.setNavigator(self.treeNav)
        self.navWidget.resize(400,400)
        self.navWidget.setCaption("Qt Navigator")
        OWGUI.button(self.TreeTab,self,"Navigator",self.toggleNavigator)
        self.setMouseTracking(True)

        nodeInfoBox = QVButtonGroup("Show Info On", self.NodeTab)
        nodeInfoButtons = ['Majority Class', 'Majority Class Probability', 'Target Class Probability', 'Number of Instances']
        nodeInfoSettings = ['maj', 'majp', 'tarp', 'inst']
        self.NodeInfoW = []; self.dummy = 0
        for i in range(len(nodeInfoButtons)):
            setattr(self, nodeInfoSettings[i], i in self.NodeInfo)
            w = OWGUI.checkBox(nodeInfoBox, self, nodeInfoSettings[i], \
                               nodeInfoButtons[i], callback=self.setNodeInfo, getwidget=1, id=i)
            self.NodeInfoW.append(w)

        OWGUI.comboBox(self.NodeTab, self, 'NodeColorMethod', items=['Default', 'Instances in Node', 'Majority Class Probability', 'Target Class Probability', 'Target Class Distribution'], box='Node Color',                           
                                callback=self.toggleNodeColor)

        OWGUI.checkBox(self.NodeTab, self, 'ShowPies', 'Show Pies', box='Pies', tooltip='Show pie graph with class distribution?', callback=self.togglePies)
        self.targetCombo=OWGUI.comboBox(self.NodeTab,self, "TargetClassIndex",items=[],box="TargetClass",callback=self.toggleTargetClass)
        OWGUI.button(self.controlArea, self, "Save As", callback=self.saveGraph)
        self.NodeInfoSorted=list(self.NodeInfo)
        self.NodeInfoSorted.sort()
       
    def setNodeInfo(self, widget=None, id=None):
        if widget:
            if widget.isChecked():
                if len(self.NodeInfo) == 2:
                    self.NodeInfoW[self.NodeInfo[0]].setChecked(0)
                self.NodeInfo.append(id)
            else:
                self.NodeInfo.remove(id)
            self.NodeInfoSorted=list(self.NodeInfo)
            self.NodeInfoSorted.sort()
            self.NodeInfoMethod=id
        #print self.NodeInfoSorted
        for n in self.canvas.nodeList:
            n.setText(self.NodeInfoSorted)
        self.canvas.update()

    def activateLoadedSettings(self):
        if not self.tree:
            return
        OWTreeViewer2D.activateLoadedSettings(self)
        self.setNodeInfo()
        self.toggleNodeColor()
       
    def toggleNodeColor(self):
        for node in self.canvas.nodeList:
            if self.NodeColorMethod == 0:   # default
                node.setBrush(QBrush(BodyColor_Default))
            elif self.NodeColorMethod == 1: # instances in node
                light = 400 - 300*node.tree.distribution.cases/self.tree.distribution.cases
                node.setBrush(QBrush(BodyCasesColor_Default.light(light)))
            elif self.NodeColorMethod == 2: # majority class probability
                light=400- 300*node.majClassProb
                node.setBrush(QBrush(self.ClassColors[node.majClass[0]].light(light)))
            elif self.NodeColorMethod == 3: # target class probability
                light=400-300*node.dist[self.TargetClassIndex]/node.dist.cases
                node.setBrush(QBrush(self.ClassColors[self.TargetClassIndex].light(light)))
            elif self.NodeColorMethod == 4: # target class distribution
                light=200 - 100*node.dist[self.TargetClassIndex]/self.tree.distribution[self.TargetClassIndex]
                node.setBrush(QBrush(self.ClassColors[self.TargetClassIndex].light(light)))
        self.canvas.update()
        self.treeNav.leech()

    def toggleTargetClass(self):
        if self.NodeColorMethod in [3,4]:
            self.toggleNodeColor()
        for n in self.canvas.nodeList:
            n.texts[2]="%.3f" % (n.dist.items()[self.TargetClassIndex][1]/n.dist.cases)
            if 2 in self.NodeInfoSorted:
                n.setText(self.NodeInfoSorted)
        self.canvas.update()
               
    def togglePies(self):
        for n in self.canvas.nodeList:
            n.setPieVisible(self.ShowPies)
        self.canvas.update()

   

    def ctree(self, forest=None):
        self.targetCombo.clear()
        if forest==None :
            forest=self.forest
        if forest:
            self.forest=forest
            self.NbrOfTrees.setText("(out of %d trees in forest)"%len (forest.classifiers))
            if self.currentviewing > len (forest.classifiers):
                self.currentviewing=1
            tree=forest.classifiers[self.currentviewing-1]

            if tree:
                for name in tree.tree.examples.domain.classVar.values:
                    self.targetCombo.insertItem(name)
            if tree and len(tree.tree.distribution)>self.TargetClassIndex:
                self.TargetClassIndex=0
            OWTreeViewer2D.ctree(self, tree)
        else :
            self.NbrOfTrees.setText("(No tree in forest yet)")
            OWTreeViewer2D.ctree(self, None)

    def walkcreate(self, tree, parent=None, level=0, attrVal=""):
        node=ClassificationNode(attrVal, tree, parent or self.canvas, self.canvas)
        if tree.branches:
            for i in range(len(tree.branches)):
                if tree.branches[i]:
                    self.walkcreate(tree.branches[i],node,level+1,tree.branchDescriptions[i])
        return node

    def classificationBubbleConstructor(self, node, pos, canvas):
        b=CanvasBubbleInfo(node, pos,canvas)
        rule=list(node.rule)
        #print node.rule, rule
        #rule.sort(lambda:a,b:a[0]<b[0])
        # merge
        if rule:
            try:
                rule=parseRules(list(rule))
            except:
                pass
            text="IF "+" AND\n  ".join([a[0].name+" = "+a[1] for a in rule])+"\nTHEN "+node.majClassName
        else:
            text="THEN "+node.majClassName
        b.addTextLine(text)
        b.addTextLine()
        text="Instances:"+str(node.numInst)+"(%.1f" % (node.numInst/self.tree.distribution.cases*100)+"%)"
        b.addTextLine(text)
        b.addTextLine()
        for i,d in enumerate(node.dist.items()):
            if d[1]!=0:
                b.addTextLine("%s: %i (%.1f" %(d[0],int(d[1]),d[1]/sum(node.dist)*100)+"%)",self.ClassColors[i])
        b.addTextLine()
        b.addTextLine((node.tree.branches and "Partition on: "+node.name) or "(leaf)")
        b.show()
        return b

    def saveGraph(self):
        qfileName = QFileDialog.getSaveFileName("tree.png","Portable Network Graphics (.PNG)\nWindows Bitmap (.BMP)\nGraphics Interchange Format (.GIF)\nDot Tree File(.DOT)", None, "Save to..")
        fileName = str(qfileName)
        if fileName == "": return
        (fil,ext) = os.path.splitext(fileName)
        ext = ext.replace(".","")
        ext = ext.upper()
        if ext=="DOT":
            orngTree.printDot(self.tree, fileName)
            return
        dSize= self.canvas.size()
        buffer = QPixmap(dSize.width(),dSize.height()) # any size can do, now using the window size     
        painter = QPainter(buffer)
       
        painter.fillRect(buffer.rect(), QBrush(QColor(255, 255, 255))) # make background same color as the widget's background
        self.canvasView.drawContents(painter,0,0,dSize.width(), dSize.height())
        painter.end()
        buffer.save(fileName, ext)
   




Now if you are intersted to have some quick and dirty icons for these 2 widgets, save the following script in your orange\OrangeWidgets\icons directory and execute it. You can delete it once the 2 files are generated (sorry for the ugly base64 encoding : I do not have a handy space online to upload the pictures...)

Code: Select all
#Python Script to generate the 2 icons for the widgets
#Save it with whatever name (*.py) and execute in the icon directory

import base64

Icon1='iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA\nBGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VG\nAAAOEklEQVR42mJkYGD4zzCEAUAAsYCItOQUBgZGRgZGJiaqGfz/33+geYxUdzDI3N+/fzP8/vOb\nYcnSJQwAAQT2AAcHG4O4qCiDiIQUw58fX8EK/v79y/D/P+HIASn58/cf0MC/YJqWAOSeP3//MHz7\n+o3h85fPYDGAAAJ7gImFDez4kNAQhpMnTzK8eP6c4c+fP2DfEjQUTv+naWIEOf7fv38MP3/+ZPjI\n8hHsERAACCCIB4BJB+Srs2fPMnz58oWBm5sHHEUwTaQAkFmMwOSITFMbfP70mYGFBex0BoAAgniA\nkQkcgp8/Q6IlTLieEvP/zLiTz8LBwcEAwiBPUBuAAoWFGeIBgACCeIAZIgAKcVDaB4GqfYEMP378\nYGBlZQXzO103MRRu9QCz2djY8ImxPH78mEFeXh7sAVrEAMhMZhZmMBsggMAeYGFmhkc5LOOC0pqg\noCAwOXHDNYI8JCEhwcDLy0tQDKQfZBYxMYCtsEDWhy4PiQGIBwACCOwBkG9ARR7ME2AxoAJ2dnYU\nh4FCGYQJiYH0gvIOyCwQBrFh+QlfyQZTD4s1XLGHHAMAAcQCsxCUD0ASME1dbpsxNE703UWUGCgZ\nMkNjFZzpNpQz/GZkYwAnTlDJhqV+AJV4Ij8fff7qPZUXFHD4kh/YA9AYAAgghAegoQ+LgZId3gzf\nv39n4OHhgaf3rHWOYDYstHGJwcyBmfWbiZ3htXo0w69fv8B5CmY5Ss4HFtsiF9tZHj16xCAnJweO\nfVzJD9kDAAGE5AHUJASipaWlUfIASExSUhIlueASQ/bAv/+Q2pOfn5+Bi4sLXgQiA5A8cv4hmISg\nHgAIILgHGJlQkxDIEk5OTgYBAQG4RljUEhKDORBmFijZg8RAakExCpNHTvMwD8BKPbweYEZ4ACCA\nWGAWgvIAcqi1O2/A0NjntZ0oMVgeAJkFjt2/PxnYLy1m+MvCyPCZiRHSRgK1lYDivL4tYI+BHP6O\nkY8T5BbkJI2zHoAGAkAAoeQB5Bgo3+0HzgOwpNHquI4hd6MLmM3Hx4dXDDlkQWbz+bUyfATWDR+B\nSQOWB8CePDef4eOTJwyysrLgkuwvhxBYDtk92AAzUPwfNAYAAgheD8CKUZgmkANkZGTgmRgmJiUl\nBXcsPjGYWbCkCKrYQAECa5qAkswPZkaGj9A0D/IAI1IRDPIgCGNrkiDXAwABhLMUAoUUuXkApBdW\niYHMBZkDUgcqEF6uLmP4A0w+f0D++PsLrg6W1JjPLWD4xgxs3QL5wn6NYH0g/cjJCeIZiAcAAgha\nkbFg1APN9mswoq7bfQtRYsj1AHKJAwpZEe8qhru75jAwypoyvP/LycAN9DzIgSB1AgHtDD/vXGD4\nfOMgA6eyFcMTYPJSVVXFSE5MTMzwvgZAACE1JVCTUPX+IIZv377Bk0aj7SqGgi3uYDaoOMQnhpyE\n0NMxC7cgg5RlCMPDIysZ5PQ9GNiFpcChDK75/wPbXs/OMogZeTG8/MrAIC8mhtUc5AwOEEDwGGBE\nS0IgAMpcyHkABEB1A8yx+MSQi0lkPsix/8TkGZQcYhieHl3JwG8dDhTnZ/j77QPD21OrGYRNgxhe\nfPrFoKwsCk5ysAoNPQkxMUP4AAGEUQrBFILYoLQMSt/IbRwQJiQG0gtKRtgyH6xkYmWVZ5B3iGV4\ncngpg5ixL8OXq7sZBI0DGF59+QMOEFCFB3I8tliE1AMQMYAAQmoLMSLS7D/seaDXcxsxYj+BDmeH\nJQsMy0FFIDAvgB0mIs0gZx/D8HBrH4O4YzrY8eLi4vCQx1WUItfEAAGEyMTQ0AdJbPjUznDz5k1w\nHoDVjLDmMaQPzQGv9mEAJgZs77CDHADCoMCAxShy0xpWyYHkuYSlGWSDGhmePXsGLI7FwUkWn+MR\nrVFI4QAQQPAighGp3AZZrqGhAfYArNxGbgYTaquD9MPaPODu6vdvDCHWIQxrjq5hYGNlY2C/387w\nQjCDwdfbl0HxqyLDlENTGVRUVBjevn/LEBgQCDGomYHhmMsxrH0KZI8BBBALevoEWQoKBZADQI4n\np0+Mjjk5OBnesb5jcLN3Yzhw7ACDicRdBlX9QrD6+1/vM7Q/bWOIlYxlqHxZAU6+DO3gYMLa6IPV\nxDAAEEAID0BzOixTUhOkaacxMIKGXxj/MPz6+Yvh1W8VhruikPqDUYiB4ZzYeYb7P+8x/JQEtkJB\nqTKNkYHrDQdD75lehhLNEqyB9A86BAIQQBhJiJjuH3qUgpoFyK1IZPD15zeGx1+eMDCsZ2RgfcfM\n0MrdzJAsYwy0HuKB/0Arf1h8Y3h1+RfDH40/wN4PMISvMzJwfuJk4GHlxpmJ//2D9N0BAogJvXLA\nh0GZ1l3fHUzDxD5+/MhgYW7BEKYZBh7VALV37t6/y2BsZAzG4ccjGBgPAz3sA3SswX+GM1JnGOKN\nJzAwvgeKTYIGCg+47GJg2QEMz8VAcy8zMXxXBcaGFHZ3IHsKIICQPMCINf0iYw52Doa3LG8Z7C3t\n4WIFZgVg/Xc/32XovBHG8PFlG0PG03RwWtaZoMPAxcjB8PPzTwbGV0DL2RgYvul/Z/gj+BfSrUwC\nYpBHQJGn+Z+BnRNYaQFT75+ffxl+3vvFsEBuATh2QbGO4hZmhAcAAgjhAUYmgh5I0khCScs/vv9g\nuMx9CZ6WD0p+ZUhjuABOy4zfGRkiDaMYPv/7CuzvAlOs6H8Gtq8cDL+l/zBwAmnOH8A+72VgPfEV\nGHCfmRl+K/xmYJYBNidsgcXxr/8Mfz1+M8i8lGGI1I0EJ090t8AAQAAxIecBfMnn64+vDA8/PQJH\nMesEFobaq9UMl79eQQwtwtIy/2sGHj1ehv+c/xmm/JzEwCT5n4HZgIlB7a0aw1fXr+DhR4GnQgwi\nT0QYvBg8GP7JAZvOQsC038LAoPpSleGHyXcGhioGBt87vgzL/y5n6NnVw6CnqwfBG/QwkhBAALGg\nJyFcoOVaGwPjfWhavvSf4cTXEwz7dPeD0/L/xUBX5SDS8r+jwPSjzsDw3OUFg/U1a4YLQucZbnPe\nghj0BphPD35iMOYzYTj27igDgzmi2Pzo8hGi5hUDw/4L+xk+q3xmuPTwIkrRCokBRCkJEEBM+Mpv\nGP4GzJg33l0jOi2/930PTmoMQNOFPgsyOB62Zvgv+B+e1P7K/GO4zXUTtdgM4WB4cuIxXM0/YAze\nfngLQ037pTaUgAYIIJQ8gCv5fHj/HiUt/weGMnJaZrzCBE/LqjxqDDx/eOFJ7eCTAwyqAhpYk9o3\nq++QYvMqsNi8zcnAx8JLUA03C2rRChBATOj1ADYM6g7WGdeA0zIDqK33GVgXfAGG+Fsmhv/v/oPF\n/oPGhX8zMHza8wml2GTkYmSYYjidqGJTk1eboJr/kqg1MUAAEVWMgpoT20S3MfyXBhZ1h4ENtG/A\njLmNieHX5R8MPy4Cu4XXgTFz5x+D1mstBvYXbGQXm4clDjPwRvAxsLxhxalmrvRc8NwArA0GEEBE\nVWRP3jxlePHrGYPsIVEG4a9sDJz/GBn+fvzL8Oce0GFPgNgbaJgSA0PknQiikxqs2GRwYoQXm//f\n/mf4yvOF4d+PvzjVyL+WZ0i1SoW3BgACCJGE8NQDytJKDGsF1zNstzjAYPpKnEGEm4WB7RnQw0DH\niDGKMQgB067wETaGtjv1DH///yIqqfG852Zg/gBsrcozg4tNlm8sDJrMmgw8F/hwqgn5GsKwiWsT\nw4LTC+BJCCCASKqJQdjmpiCDdDUzg85vDYZAqUCGgz6HGOpLVRiib8sz+HO6MOi66BFMav9/gEYd\n/jJMZpzMwDAVUgf84f3DcFXjKgPTbUacatbwrWFw0HBgYEBqigEEEAu2jjIuAMoLbB++MrABm8el\nZm3grh+4c/LtN4O+lBGDolEGQ8n/QnBS+8z4huHjvx8MX7/8Zfj/BWgnqCxPAuKnwFg4DvTYM0aG\nzF9pDGL/+IHZ4gvDj+Y/YId9TQcqfoFbDRsXsD9hxQ53E0AAYfQHCLX1QeVhgJoouMMDGwPiE5dl\n4GDhZFCVUWZY928Dww/DHwwVu7wYLsg9Zfh96z/DL+b/DOLcYgx/mN4x/D/CyPDzKzB//GZkSBGJ\nZnh5+RAD99NfDNccJRnu/LrN8Iv9GwMjDjUhGdMZFBUVwcU6DAAEEAv6aBohENS1B0Ps27sXDF9e\nP2H4+vUzAw8vP7hDBEpqr2e9ZFBNMGWQU1RjKNetYNhRFshwh+cXwwMZEYafIvwMVx/tYVAC1l3P\nX3xkEOXQZZjnPo9he2k4w13uLwz3pIQYvvHzMLxgfMvgzc/MsPr0R7D7YA07GAAIIJKSEKw/iw7k\njV0Zvrx9DjQDMe4JSmrswKS2KHo1WA1o9pMB2MBT4ldlCDQqBQ9H7mzyZ3jL/IfhGTBp5qvkQ0o9\nYHI0ERZk0BNMZVBWVgZPc925EQ0u2WB9aWQPAAQQSUkIFzAJLcac3wImNX9VEbiZoKTGw8vM8IsB\nMiUF6riD1Aizs4JnmWEj1HxiMgzMf7/A1YCHLsHDls/gapDdCRBALOjtIGqByImHUUfkgP3b78AY\n+P/1LrBjD+l7g5Lje2B6lrpxBTwKDhL79u4lsLD4w6DiIAQWAzlaPmAGQ6wGQg3ycA1AALFAh0IY\nvn77xvDu3Tuq94eRp5DEtW0Zvn94BayUWSFJClqyqappgWmQmKSuPcOXd88ZREUlwGKwkRGYGlAP\nEORWkJtBACCAwB54+/Yt2Gdv3ryhybwuzKGcWh4MnED2i1cvgU3ml1jVsWm4MgjB1GDpg4PMAXVb\nQW4GAYAAYhzqy20AAgwANBOGN4J+fVwAAAAASUVORK5CYII=\n'
Icon2='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\n'

F=open("RandomForest.png","wb")
F.write(base64.decodestring(Icon1))
F.close()

F=open("RandomForestViewer2D.png","wb")
F.write(base64.decodestring(Icon2))
F.close()




That's all, I tested it with some personal datasets and it seemed to work as intended. If you get an indentation error, you will have to edit orange\orngEnsemble.py and comment out the line
Code: Select all
def boostrapSample(examples, rand, returnOutOfBag=0):

(alternatively, you can also write the keyword "pass", properly indented on the following line)

Fabrice Capiez

Strange formatting

Postby F. Capiez » Tue Jan 10, 2006 8:56

The formatting is all messed up in the code portions above (at least with FireFox 1.5) : in case you had not noticed, the actual code is present, only it is far away on the right of the screen...

Don't ask me why...

Bug

Postby Fabrice C. » Tue Jan 10, 2006 11:14

Sorry, I found a bug in OWRandomForestViewer2D.

When examining the individual trees in the forest, the splitting of each node are correct (good attribute and associated value) but the pie charts and the classes are all messed up.

one way to show the problem is by doing the following :



Code: Select all

>>> tree=orngTree.TreeLearner(mForPruning=2, name="tree")
>>> forest = orngEnsemble.RandomForestLearner(trees=100, name="forest")
>>> a=forest(data)
>>> b=tree(data)
>>> b.tree.distribution
<10.000, 7.000, 6.000, 4.000, 3.000, 7.000>
>>> a.classifiers[0].tree.distribution
<11.000, 9.000, 4.000, 5.000, 3.000, 5.000>
>>> a.classifiers[2].tree.distribution
<7.000, 5.000, 5.000, 5.000, 2.000, 13.000>



The first distribution printed being of course the correct one.

I have barely started to look into things so I do not know if it is a bug somewhere or an incompatibility between the trees generated by random forest and the tree viewer.

If you have any advice on the subject, I will gladly hear about it.

Fabrice Capiez

predictions problem

Postby vnams » Fri Jul 06, 2007 14:44

Hi,
The code works OK except that I cannot get it to produce predictions. That is, when I connect it to the Predictions widget, I get no predictions. It works OK when I connect it to the Test Learners widget. Is there something wrong with the classifier the widget produces?
Thanks, Vilis Nams

random forests in canvas

Postby marko » Sat Jul 07, 2007 13:07

You should check the Prototypes tab for a random forests widget. If you can't find it, try with a newer snapshot.

Marko


Return to Questions & Support