def giveChildren(self, node, includeClassToPredict=False):
     """Gives children to <node>, returns these children"""
     leftClass = self.pickAnAttribute(includeClassToPredict)
     rightClass = self.pickAnAttribute(includeClassToPredict)
     leftNode = GPNode(leftClass, self.pickALimit(leftClass))
     rightNode = GPNode(rightClass, self.pickALimit(rightClass))
     node.setLeftChild(leftNode)
     node.setRightChild(rightNode)
     return [leftNode, rightNode]
 def addTerminatingNodes(self, currentNodes):
     """Adds terminal nodes to all nodes in <currentNodes>"""
     # BE CAREFUL HERE; do I want the final split to have the possibility of predicting the same finalValue?
     terminatingOptions = self.categoryLimits[self.classToPredict]
     for node in currentNodes:
         optionLeft = random.choice(terminatingOptions)
         terminatingOptions.remove(optionLeft)
         optionRight = random.choice(terminatingOptions)
         node.setLeftChild(GPNode(self.classToPredict, optionLeft))
         node.setRightChild(GPNode(self.classToPredict, optionRight))
         terminatingOptions.append(optionLeft)
    def initBalancedTree(self):
        """Creates a decision tree of depth <self.depth>"""
        nodesAtCurrentDepth = []
        rootClass = self.pickAnAttribute()
        self.root = GPNode(rootClass, self.pickALimit(rootClass))
        nodesAtCurrentDepth.append(self.root)

        depth = 0
        # Initialize tree to one level short of depth
        while depth < self.maxDepth - 2:
            children = []
            for node in nodesAtCurrentDepth:
                children += self.giveChildren(node)
            nodesAtCurrentDepth = children
            depth += 1
        # Add the last level of nodes, which should be terminating nodes (i.e. the possible predictions)
        self.addTerminatingNodes(nodesAtCurrentDepth)
    def initUnevenTree(self):
        """Creates a random decision tree that is not necessarily symmetrical or full"""
        rootClass = self.pickAnAttribute()  # Not the classToPredict
        rootValue = self.pickALimit(rootClass)
        self.root = GPNode(rootClass, rootValue)
        nodesNeedingChildren = []
        nodesNeedingChildren.append(self.root)
        depth = 0

        while depth < self.maxDepth - 2:
            allChildren = []
            for node in nodesNeedingChildren:
                if node.attribute != self.classToPredict:
                    # Not a terminal node
                    allChildren += self.giveChildren(
                        node, includeClassToPredict=True)
            nodesNeedingChildren = allChildren
            depth += 1

        self.removeTerminalNodes(nodesNeedingChildren)
        self.addTerminatingNodes(nodesNeedingChildren)