Skip to content
Snippets Groups Projects
forrest-test.ipynb 72.4 KiB
Newer Older
johannes bilk's avatar
johannes bilk committed
{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Testing the Forrest\n",
    "\n",
    "## Importing the Basics"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import random\n",
    "from matplotlib import pyplot as plt\n",
    "from machineLearning.metric import ConfusionMatrix, RegressionScores\n",
    "from machineLearning.utility import ModelIO\n",
    "from machineLearning.rf import (\n",
    "    RandomForest, DecisionTree,\n",
    "    Gini, Entropy, MAE, MSE,\n",
johannes bilk's avatar
johannes bilk committed
    "    Mode, Mean, Confidence, Probabilities,\n",
    "    CART, ID3, C45,\n",
    "    AdaBoosting, GradientBoosting,\n",
    "    Majority, Confidence, Average, Median\n",
    ")"
johannes bilk's avatar
johannes bilk committed
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Generating Test Data\n",
    "\n",
    "Here I generate random test data. It's two blocks shifted very slightly in some dimensions. For classifier tasks each block gets a label, for regressor tasks each block gets the average coordinates plus some random value as a traget. It's a very simple dummy data set meant for testing the code.\n",
    "\n",
    "Here one can change the dimensionallity and amount of the data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "def dataShift(dims):\n",
    "    offSet = [5, 1.5, 2.5]\n",
    "    diffLen = abs(len(offSet) - dims)\n",
    "    offSet.extend([0] * diffLen)\n",
    "    random.shuffle(offSet)\n",
    "    return offSet[:dims]\n",
    "\n",
    "# Initialize some parameters\n",
    "totalAmount = 6400\n",
    "dims = 5\n",
    "evalAmount = totalAmount // 4\n",
    "trainAmount = totalAmount - evalAmount\n",
    "offSet = dataShift(dims)\n",
    "\n",
    "# Create covariance matrix\n",
    "cov = np.eye(dims)  # This creates a covariance matrix with variances 1 and covariances 0\n",
    "\n",
    "# Generate random multivariate data\n",
    "oneData = np.random.multivariate_normal(np.zeros(dims), cov, totalAmount)\n",
    "twoData = np.random.multivariate_normal(offSet, cov, totalAmount)\n",
    "\n",
    "# Split the data into training and evaluation sets\n",
    "trainData = np.vstack((oneData[:trainAmount], twoData[:trainAmount]))\n",
    "validData = np.vstack((oneData[trainAmount:], twoData[trainAmount:]))\n",
    "\n",
    "# Labels for classification tasks\n",
    "trainLabels = np.hstack((np.zeros(trainAmount), np.ones(trainAmount)))\n",
    "validLabels = np.hstack((np.zeros(evalAmount), np.ones(evalAmount)))\n",
    "\n",
    "# Targets for regression tasks\n",
    "trainTargets = np.sum(trainData, axis=1) + np.random.normal(0, 0.1, 2*trainAmount)\n",
    "validTargets = np.sum(validData, axis=1) + np.random.normal(0, 0.1, 2*evalAmount)\n",
    "\n",
    "# Shuffle the training data\n",
    "trainIndex = np.random.permutation(len(trainData))\n",
    "trainData = trainData[trainIndex]\n",
johannes bilk's avatar
johannes bilk committed
    "trainLabels = trainLabels[trainIndex]\n",
    "trainTargets = trainTargets[trainIndex]"
johannes bilk's avatar
johannes bilk committed
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Creating the Forrest\n",
    "\n",
    "Here the forrest is created. One can set the number of trees and set the maximum depth. Depending on the task, we add a different impurity function and a different leaf function. Finally we add the split algorithm and set the feature percentile. Higher numbers look at more possible splits, but decreases speed. Lower numbers look at less possible splits, speeding up the algorithm. Depending on the data set this can have a strong impact on the performance.\n",
    "\n",
    "One can set a different depth, leaf function, splitting algorithm and impurity function for each tree. Here in this simple case we create all trees with same parameters."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "task = 'classifier' # 'classifier'/'regressor'\n",
    "forrest = RandomForest(bootstrapping=False, retrainFirst=False)\n",
    "forrest.setComponent(GradientBoosting())\n",
johannes bilk's avatar
johannes bilk committed
    "forrest.setComponent(Majority())\n",
johannes bilk's avatar
johannes bilk committed
    "    tree = DecisionTree(maxDepth=7, minSamplesSplit=2)\n",
    "    if task == 'regressor':\n",
    "        tree.setComponent(MSE())\n",
    "        tree.setComponent(Mean())\n",
    "    elif task == 'classifier':\n",
    "        tree.setComponent(Entropy())\n",
    "        tree.setComponent(Mode())\n",
    "    tree.setComponent(CART(featurePercentile=90))\n",
    "    forrest.append(tree)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Trainining the tree\n",
    "\n",
    "Again, depending on the task we train the forrest with targets or labels. Then we make a prediction and plot the tree."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tree 1 |⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿| done ✔                  | 16%\n",
      "tree 2 |⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿| done ✔                  | 14%\n",
      "tree 3 |⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿| done ✔                  | 16%\n",
      "tree 4 |⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿| done ✔                  | 17%\n",
      "tree 5 |⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿| done ✔                  | 16%\n",
      "━━━━━━━━━━━━━━━━━━━━━━━━━━━━ forrest ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
      "voting: Majority, booster: GradientBoosting, bootstrapping: False\n",
johannes bilk's avatar
johannes bilk committed
      "\n",
      "—————————————————————— tree: 1/5 ———————————————————————\n",
      "split: CART, impurity: Entropy, leaf: Mode, nodes: 43\n",
      "maxDepth: 7, reached depth: 7, minSamplesSplit: 2\n",
      "························································\n",
      "╴feat: 2 <= 2.18, samples: 9600\n",
      "     ├─feat: 2 <= 1.90, samples: 4747\n",
      "     │   ├─feat: 4 <= 1.13, samples: 4694\n",
      "     │   │   └─╴value: 0.0\n",
      "     │   │   └─╴feat: 2 <= 0.90, samples: 619\n",
      "     │   │       └─╴value: 0.0\n",
      "     │   │       └─╴feat: 3 <= 0.71, samples: 102\n",
      "     │   │           └─╴value: 0.0\n",
      "     │   │           └─╴feat: 2 <= 0.94, samples: 19\n",
      "     │   │               └─╴value: 1.0\n",
      "     │   │               └─╴value: 0.0\n",
      "     │   └─╴feat: 3 <= 1.28, samples: 53\n",
      "     │       └─╴value: 0.0\n",
      "     │       └─╴feat: 0 <= 0.68, samples: 6\n",
      "     │           ├─feat: 0 <= -1.53, samples: 4\n",
      "     │           │   └─╴value: 1.0\n",
      "     │           │   └─╴value: 0.0\n",
      "     │           └─╴value: 1.0\n",
      "     └─╴feat: 2 <= 3.17, samples: 4853\n",
      "         ├─feat: 3 <= 1.48, samples: 214\n",
      "         │   ├─feat: 4 <= 1.09, samples: 82\n",
      "         │   │   ├─feat: 2 <= 2.92, samples: 51\n",
      "         │   │   │   ├─feat: 3 <= 0.92, samples: 43\n",
      "         │   │   │   │   └─╴value: 0.0\n",
      "         │   │   │   └─╴feat: 3 <= -0.18, samples: 8\n",
      "         │   │   │       └─╴value: 0.0\n",
      "         │   │   │       └─╴value: 1.0\n",
      "         │   │   └─╴feat: 2 <= 2.27, samples: 31\n",
      "         │   │       └─╴feat: 3 <= -1.09, samples: 26\n",
      "         │   │           └─╴value: 0.0\n",
      "         │   │           └─╴value: 1.0\n",
      "         │   └─╴feat: 4 <= -0.91, samples: 132\n",
      "         │       └─╴value: 0.0\n",
      "         └─╴feat: 3 <= 0.24, samples: 4639\n",
      "             ├─feat: 4 <= -0.52, samples: 51\n",
      "             │   ├─feat: 1 <= -1.11, samples: 2\n",
      "             │   │   └─╴value: 1.0\n",
      "             │   └─╴value: 1.0\n",
      "             └─╴value: 1.0\n",
      "\n",
      "—————————————————————— tree: 2/5 ———————————————————————\n",
      "split: CART, impurity: Entropy, leaf: Mode, nodes: 37\n",
      "maxDepth: 7, reached depth: 7, minSamplesSplit: 2\n",
      "························································\n",
      "╴feat: 2 <= 2.18, samples: 9600\n",
      "     ├─feat: 2 <= 1.90, samples: 4747\n",
      "     │   ├─feat: 4 <= 1.13, samples: 4694\n",
      "     │   │   └─╴value: 0.0\n",
      "     │   │   └─╴feat: 2 <= 0.90, samples: 619\n",
      "     │   │       └─╴value: 0.0\n",
      "     │   │       └─╴feat: 3 <= 0.71, samples: 102\n",
      "     │   │           └─╴value: 0.0\n",
      "     │   │           └─╴feat: 3 <= 0.75, samples: 19\n",
      "     │   │               └─╴value: 1.0\n",
      "     │   │               └─╴value: 0.0\n",
      "     │   └─╴feat: 3 <= 1.28, samples: 53\n",
      "     │       └─╴value: 0.0\n",
      "     │       └─╴feat: 0 <= 0.68, samples: 6\n",
      "     │           ├─feat: 2 <= 2.14, samples: 4\n",
      "     │           │   └─╴value: 1.0\n",
      "     └─╴feat: 2 <= 3.17, samples: 4853\n",
      "         ├─feat: 3 <= 1.48, samples: 214\n",
      "         │   ├─feat: 1 <= 0.03, samples: 82\n",
      "         │   │   └─╴value: 0.0\n",
      "         │   │   └─╴feat: 2 <= 2.56, samples: 30\n",
      "         │   │       └─╴feat: 4 <= 0.79, samples: 15\n",
      "         │   │           └─╴value: 1.0\n",
      "         │   │           └─╴value: 0.0\n",
      "         │   └─╴feat: 4 <= -0.91, samples: 132\n",
      "         │       └─╴value: 0.0\n",
      "         └─╴feat: 3 <= 0.24, samples: 4639\n",
      "             ├─feat: 4 <= -0.52, samples: 51\n",
      "             │   ├─feat: 0 <= -0.25, samples: 2\n",
      "             │   │   └─╴value: 1.0\n",
      "             │   └─╴value: 1.0\n",
      "             └─╴value: 1.0\n",
      "\n",
      "—————————————————————— tree: 3/5 ———————————————————————\n",
      "split: CART, impurity: Entropy, leaf: Mode, nodes: 43\n",
      "maxDepth: 7, reached depth: 7, minSamplesSplit: 2\n",
      "························································\n",
      "╴feat: 2 <= 2.18, samples: 9600\n",
      "     ├─feat: 2 <= 1.90, samples: 4747\n",
      "     │   ├─feat: 4 <= 1.13, samples: 4694\n",
      "     │   │   └─╴value: 0.0\n",
      "     │   │   └─╴feat: 2 <= 0.90, samples: 619\n",
      "     │   │       └─╴value: 0.0\n",
      "     │   │       └─╴feat: 3 <= 0.71, samples: 102\n",
      "     │   │           └─╴value: 0.0\n",
      "     │   │           └─╴feat: 3 <= 0.75, samples: 19\n",
      "     │   │               └─╴value: 1.0\n",
      "     │   │               └─╴value: 0.0\n",
      "     │   └─╴feat: 3 <= 1.28, samples: 53\n",
      "     │       └─╴value: 0.0\n",
      "     │       └─╴feat: 2 <= 2.14, samples: 6\n",
      "     │           ├─feat: 2 <= 1.98, samples: 4\n",
      "     │           │   └─╴value: 1.0\n",
      "     │           │   └─╴value: 0.0\n",
      "     │           └─╴value: 1.0\n",
      "     └─╴feat: 2 <= 3.17, samples: 4853\n",
      "         ├─feat: 3 <= 1.48, samples: 214\n",
      "         │   ├─feat: 4 <= 1.09, samples: 82\n",
      "         │   │   ├─feat: 4 <= -0.93, samples: 51\n",
      "         │   │   │   ├─feat: 1 <= 0.13, samples: 13\n",
      "         │   │   │   │   └─╴value: -1.0\n",
      "         │   │   └─╴feat: 1 <= -0.25, samples: 31\n",
      "         │   │       ├─feat: 3 <= -0.48, samples: 18\n",
      "         │   │       │   └─╴value: 0.0\n",
      "         │   │       │   └─╴value: 1.0\n",
      "         │   │       └─╴feat: 4 <= 1.90, samples: 13\n",
      "         │   │           └─╴value: 0.0\n",
      "         │   │           └─╴value: 1.0\n",
      "         │   └─╴feat: 4 <= -0.91, samples: 132\n",
      "         │       └─╴value: 0.0\n",
      "         └─╴feat: 3 <= 0.24, samples: 4639\n",
      "             ├─feat: 4 <= -0.52, samples: 51\n",
      "             │   ├─feat: 0 <= -0.25, samples: 2\n",
      "             │   │   └─╴value: 1.0\n",
      "             │   └─╴value: 1.0\n",
      "             └─╴value: 1.0\n",
      "\n",
      "—————————————————————— tree: 4/5 ———————————————————————\n",
      "split: CART, impurity: Entropy, leaf: Mode, nodes: 45\n",
      "maxDepth: 7, reached depth: 7, minSamplesSplit: 2\n",
      "························································\n",
      "╴feat: 2 <= 2.18, samples: 9600\n",
      "     ├─feat: 2 <= 1.90, samples: 4747\n",
      "     │   ├─feat: 4 <= 1.13, samples: 4694\n",
      "     │   │   └─╴value: 0.0\n",
      "     │   │   └─╴feat: 2 <= 0.90, samples: 619\n",
      "     │   │       └─╴value: 0.0\n",
      "     │   │       └─╴feat: 3 <= 0.71, samples: 102\n",
      "     │   │           └─╴value: 0.0\n",
      "     │   │           └─╴feat: 2 <= 0.94, samples: 19\n",
      "     │   │               └─╴value: 1.0\n",
      "     │   │               └─╴value: 0.0\n",
      "     │   └─╴feat: 3 <= 1.28, samples: 53\n",
      "     │       └─╴value: 0.0\n",
      "     │       └─╴feat: 0 <= 0.68, samples: 6\n",
      "     │           ├─feat: 2 <= 2.14, samples: 4\n",
      "     │           │   └─╴value: 1.0\n",
      "     └─╴feat: 2 <= 3.17, samples: 4853\n",
      "         ├─feat: 3 <= 1.48, samples: 214\n",
      "         │   ├─feat: 4 <= 1.09, samples: 82\n",
      "         │   │   ├─feat: 2 <= 2.92, samples: 51\n",
      "         │   │   │   ├─feat: 3 <= 0.92, samples: 43\n",
      "         │   │   │   │   └─╴value: 0.0\n",
      "         │   │   │   └─╴feat: 3 <= -0.18, samples: 8\n",
      "         │   │   │       └─╴value: 0.0\n",
      "         │   │   │       └─╴value: 1.0\n",
      "         │   │   └─╴feat: 1 <= -0.25, samples: 31\n",
      "         │   │       ├─feat: 3 <= -0.48, samples: 18\n",
      "         │   │       │   └─╴value: 0.0\n",
      "         │   │       │   └─╴value: 1.0\n",
      "         │   │       └─╴feat: 2 <= 2.78, samples: 13\n",
      "         │   │           └─╴value: -1.0\n",
      "         │   │           └─╴value: 0.0\n",
      "         │   └─╴feat: 4 <= -0.91, samples: 132\n",
      "         │       └─╴value: 0.0\n",
      "         └─╴feat: 3 <= 0.24, samples: 4639\n",
      "             ├─feat: 4 <= -0.52, samples: 51\n",
      "             │   ├─feat: 1 <= -1.11, samples: 2\n",
      "             │   │   └─╴value: 1.0\n",
      "             │   └─╴value: 1.0\n",
      "             └─╴value: 1.0\n",
      "\n",
      "—————————————————————— tree: 5/5 ———————————————————————\n",
      "split: CART, impurity: Entropy, leaf: Mode, nodes: 43\n",
      "maxDepth: 7, reached depth: 7, minSamplesSplit: 2\n",
      "························································\n",
      "╴feat: 2 <= 2.18, samples: 9600\n",
      "     ├─feat: 2 <= 1.90, samples: 4747\n",
      "     │   ├─feat: 4 <= 1.13, samples: 4694\n",
      "     │   │   └─╴value: 0.0\n",
      "     │   │   └─╴feat: 2 <= 0.90, samples: 619\n",
      "     │   │       └─╴value: 0.0\n",
      "     │   │       └─╴feat: 3 <= 0.71, samples: 102\n",
      "     │   │           └─╴value: 0.0\n",
      "     │   │           └─╴feat: 0 <= 1.37, samples: 19\n",
      "     │   │               └─╴value: 0.0\n",
      "     │   │               └─╴value: 1.0\n",
      "     │   └─╴feat: 3 <= 1.28, samples: 53\n",
      "     │       └─╴value: 0.0\n",
      "     │       └─╴feat: 0 <= 0.68, samples: 6\n",
      "     │           ├─feat: 0 <= -1.53, samples: 4\n",
      "     │           │   └─╴value: 1.0\n",
      "     │           │   └─╴value: 0.0\n",
      "     │           └─╴value: 1.0\n",
      "     └─╴feat: 2 <= 3.03, samples: 4853\n",
      "         ├─feat: 3 <= 1.48, samples: 160\n",
      "         │   ├─feat: 4 <= 1.09, samples: 70\n",
      "         │   │   ├─feat: 3 <= 0.92, samples: 45\n",
johannes bilk's avatar
johannes bilk committed
      "         │   │   │   └─╴value: 0.0\n",
      "         │   │   │   └─╴feat: 3 <= 1.01, samples: 9\n",
      "         │   │   │       └─╴value: 1.0\n",
      "         │   │   │       └─╴value: 0.0\n",
      "         │   │   └─╴feat: 1 <= -0.25, samples: 25\n",
      "         │   │       ├─feat: 3 <= -0.47, samples: 16\n",
      "         │   │       │   └─╴value: 0.0\n",
      "         │   │       │   └─╴value: 1.0\n",
      "         │   │       └─╴feat: 4 <= 1.73, samples: 9\n",
      "         │   │           └─╴value: -1.0\n",
      "         │   │           └─╴value: 0.0\n",
      "         │   └─╴feat: 4 <= -0.89, samples: 90\n",
      "         │       └─╴value: 0.0\n",
johannes bilk's avatar
johannes bilk committed
      "         │       └─╴value: 1.0\n",
      "         └─╴feat: 3 <= 0.21, samples: 4693\n",
      "             ├─feat: 4 <= -0.02, samples: 52\n",
      "             │   ├─feat: 2 <= 3.67, samples: 5\n",
johannes bilk's avatar
johannes bilk committed
      "             │   │   └─╴value: 0.0\n",
      "             │   │   └─╴value: 1.0\n",
      "             │   └─╴value: 1.0\n",
johannes bilk's avatar
johannes bilk committed
      "             └─╴value: 1.0\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "if task == 'regressor':\n",
    "    forrest.train(trainData, trainTargets)\n",
    "elif task == 'classifier':\n",
    "    forrest.train(trainData,trainLabels)\n",
    "prediction = forrest.eval(validData)\n",
    "print(forrest)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[Accuracy(name='tree: 0', accuracy=0.50125),\n",
       " Accuracy(name='tree: 1', accuracy=0.499375),\n",
       " Accuracy(name='tree: 2', accuracy=0.5003125),\n",
       " Accuracy(name='tree: 3', accuracy=0.5003125),\n",
       " Accuracy(name='tree: 4', accuracy=0.5)]"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "forrest.accuracy(validData, validLabels)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAjcAAAHHCAYAAABDUnkqAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAABX/ElEQVR4nO3de1xVVd4/8M/aWwFBjlxUjhp4QQNNvF/GS9nFicwsn55JK59RSe2Xo5PlNJVTalRql8ls8tZ0saapyaZ5rGem0nEsU8vJWyahkIkEpgdEQBAG0L3X7w/i6JGzTRD2gn0+79er1yuW+xy++7MX+HWftc4RUkoJIiIiIofQVBdARERE1JDY3BAREZGjsLkhIiIiR2FzQ0RERI7C5oaIiIgchc0NEREROQqbGyIiInIUNjdERETkKGxuiIiIyFHY3BAREZGjsLkhamJef/11CCH8/vfwww83yvf84osv8Nhjj6G4uLhRnv9S1OSxa9cu1aXU28qVK/H666+rLoMoYLRQXQAR+ff444+ja9euPmO9e/dulO/1xRdfIDU1FVOnTkVERESjfI9AtnLlSrRt2xZTp05VXQpRQGBzQ9REjRkzBoMGDVJdxiUpKytDWFiY6jKUKS8vR2hoqOoyiAIOX5YiaqY+/vhjXHnllQgLC0N4eDjGjh2L9PR0n2P27duHqVOnolu3bggJCYHb7cZdd92FEydOeI957LHH8Nvf/hYA0LVrV+9LYNnZ2cjOzoYQwu9LKkIIPPbYYz7PI4TA/v37ceeddyIyMhIjR470/vmf//xnDBw4EK1atUJUVBRuv/125Obm1uvcp06ditatWyMnJwc33XQTWrdujU6dOmHFihUAgLS0NFx77bUICwtD586d8fbbb/s8vualri1btuD//b//h+joaLhcLkyePBlFRUW1vt/KlStxxRVXIDg4GB07dsSsWbNqvYR39dVXo3fv3ti9ezeuuuoqhIaG4ne/+x26dOmC9PR0fPbZZ95sr776agBAYWEhHnjgASQlJaF169ZwuVwYM2YMvv76a5/n3rx5M4QQePfdd7Fo0SJcdtllCAkJwXXXXYfvvvuuVr1ffvklbrzxRkRGRiIsLAx9+vTBCy+84HNMRkYGfvGLXyAqKgohISEYNGgQ/u///q+ul4KoSeKdG6Im6uTJkygoKPAZa9u2LQDgzTffxJQpU5CcnIynn34a5eXlWLVqFUaOHImvvvoKXbp0AQBs3LgRWVlZSElJgdvtRnp6Ov74xz8iPT0d//73vyGEwK233opvv/0Wf/nLX/D88897v0e7du1w/PjxOtd92223oUePHli8eDGklACARYsWYf78+ZgwYQKmT5+O48eP48UXX8RVV12Fr776ql4vhRmGgTFjxuCqq67CM888g7feeguzZ89GWFgYHnnkEUyaNAm33norVq9ejcmTJ2PYsGG1XuabPXs2IiIi8NhjjyEzMxOrVq3C999/720mgOqmLTU1FaNHj8bMmTO9x+3cuROff/45WrZs6X2+EydOYMyYMbj99tvxP//zP4iJicHVV1+NX//612jdujUeeeQRAEBMTAwAICsrC++//z5uu+02dO3aFXl5eXjppZcwatQo7N+/Hx07dvSp96mnnoKmaXjggQdw8uRJPPPMM5g0aRK+/PJL7zEbN27ETTfdhA4dOmDOnDlwu904cOAA/vGPf2DOnDkAgPT0dIwYMQKdOnXCww8/jLCwMLz77rsYP348/va3v+G//uu/6nw9iJoUSURNypo1ayQAv/9JKWVpaamMiIiQM2bM8Hmcx+ORbdq08RkvLy+v9fx/+ctfJAC5ZcsW79izzz4rAcjDhw/7HHv48GEJQK5Zs6bW8wCQCxcu9H69cOFCCUDecccdPsdlZ2dLXdflokWLfMbT0tJkixYtao1b5bFz507v2JQpUyQAuXjxYu9YUVGRbNWqlRRCyHfeecc7npGRUavWmuccOHCgrKqq8o4/88wzEoD84IMPpJRS5ufny6CgIHn99ddLwzC8xy1fvlwCkK+99pp3bNSoURKAXL16da1zuOKKK+SoUaNqjVdUVPg8r5TVmQcHB8vHH3/cO/bpp59KALJnz56ysrLSO/7CCy9IADItLU1KKeWZM2dk165dZefOnWVRUZHP85qm6f3/6667TiYlJcmKigqfPx8+fLjs0aNHrTqJmhu+LEXURK1YsQIbN270+Q+o/pd5cXEx7rjjDhQUFHj/03UdQ4cOxaeffup9jlatWnn/v6KiAgUFBfjZz34GANizZ0+j1H3PPff4fP2///u/ME0TEyZM8KnX7XajR48ePvXW1fTp073/HxERgYSEBISFhWHChAne8YSEBERERCArK6vW4++++26fOy8zZ85EixYt8NFHHwEA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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Create bar plot\n",
    "plt.bar(np.arange(len(forrest.featureImportance)), forrest.featureImportance, color='steelblue')\n",
    "\n",
    "# Add labels and title\n",
    "plt.xlabel('Feature Index')\n",
    "plt.ylabel('Importance')\n",
    "plt.title('Feature Importance')\n",
    "\n",
    "# Add grid\n",
    "plt.grid(True, linestyle='--', alpha=0.6)\n",
    "\n",
    "# Show plot\n",
    "plt.show()"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Evaluating predictions\n",
    "\n",
    "Depending on the task at hand we create a confusion matrix (classification) or simple metrics (regression). Since the number of classes is fixed to two, we don't need to change anything here."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "━━━━━━━━━━━━ evaluation ━━━━━━━━━━━━\n",
      "————————— confusion matrix —————————\n",
      "              Class 0     Class 1   \n",
      "····································\n",
      "     Class 0    1599         1      \n",
      "                49%          0%     \n",
      "····································\n",
      "     Class 1    1597         3      \n",
      "                49%          0%     \n",
johannes bilk's avatar
johannes bilk committed
      "\n",
      "———————————————————————————————— scores ———————————————————————————————\n",
      "                accuracy       precision      sensitivity      miss rate    \n",
      "·······································································\n",
      "     Class 0     0.501            0.5            0.999           0.001      \n",
      "     Class 1     0.501            0.75           0.002           0.998      \n",
      "·······································································\n",
      "       total     0.501           0.625           0.501           0.499      \n"
     ]
    }
   ],
   "source": [
    "if task == 'regressor':\n",
    "    metrics = RegressionScores(numClasses=2)\n",
    "    metrics.calcScores(prediction, validTargets, validLabels)\n",
    "    print(metrics)\n",
    "elif task == 'classifier':\n",
    "    confusion = ConfusionMatrix(numClasses=2)\n",
    "    confusion.update(prediction, validLabels)\n",
    "    confusion.percentages()\n",
    "    confusion.calcScores()\n",
    "    print(confusion)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Saving and Loading a Forrest\n",
    "\n",
    "Forrests can be converted to dictionaries and then saved as a json file. This allows us to load them and re-use them. Also json is a raw text format, which is neat."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "━━━━━━━━━━━━━━━━━━━━━━━━━━━━ forrest ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n",
      "voting: Majority, booster: GradientBoosting, bootstrapping: False\n",
johannes bilk's avatar
johannes bilk committed
      "\n",
      "————————————————————— tree: 01/15 ——————————————————————\n",
      "split: CART, impurity: Entropy, leaf: Mode, nodes: 43\n",
      "maxDepth: 7, reached depth: 7, minSamplesSplit: 2\n",
      "························································\n",
      "╴feat: 2 <= 2.18, samples: 9600\n",
      "     ├─feat: 2 <= 1.90, samples: 4747\n",
      "     │   ├─feat: 4 <= 1.13, samples: 4694\n",
      "     │   │   └─╴value: 0.0\n",
      "     │   │   └─╴feat: 2 <= 0.90, samples: 619\n",
      "     │   │       └─╴value: 0.0\n",
      "     │   │       └─╴feat: 3 <= 0.71, samples: 102\n",
      "     │   │           └─╴value: 0.0\n",
      "     │   │           └─╴feat: 2 <= 0.94, samples: 19\n",
      "     │   │               └─╴value: 1.0\n",
      "     │   │               └─╴value: 0.0\n",
      "     │   └─╴feat: 3 <= 1.28, samples: 53\n",
      "     │       └─╴value: 0.0\n",
      "     │       └─╴feat: 0 <= 0.68, samples: 6\n",
      "     │           ├─feat: 0 <= -1.53, samples: 4\n",
      "     │           │   └─╴value: 1.0\n",
      "     │           │   └─╴value: 0.0\n",
      "     │           └─╴value: 1.0\n",
      "     └─╴feat: 2 <= 3.17, samples: 4853\n",
      "         ├─feat: 3 <= 1.48, samples: 214\n",
      "         │   ├─feat: 4 <= 1.09, samples: 82\n",
      "         │   │   ├─feat: 2 <= 2.92, samples: 51\n",
      "         │   │   │   ├─feat: 3 <= 0.92, samples: 43\n",
      "         │   │   │   │   └─╴value: 0.0\n",
      "         │   │   │   └─╴feat: 3 <= -0.18, samples: 8\n",
      "         │   │   │       └─╴value: 0.0\n",
      "         │   │   │       └─╴value: 1.0\n",
      "         │   │   └─╴feat: 2 <= 2.27, samples: 31\n",
      "         │   │       └─╴feat: 3 <= -1.09, samples: 26\n",
      "         │   │           └─╴value: 0.0\n",
      "         │   │           └─╴value: 1.0\n",
      "         │   └─╴feat: 4 <= -0.91, samples: 132\n",
      "         │       └─╴value: 0.0\n",
      "         └─╴feat: 3 <= 0.24, samples: 4639\n",
      "             ├─feat: 4 <= -0.52, samples: 51\n",
      "             │   ├─feat: 1 <= -1.11, samples: 2\n",
      "             │   │   └─╴value: 1.0\n",
      "             │   └─╴value: 1.0\n",
      "             └─╴value: 1.0\n",
      "\n",
      "————————————————————— tree: 02/15 ——————————————————————\n",
      "split: CART, impurity: Entropy, leaf: Mode, nodes: 37\n",
      "maxDepth: 7, reached depth: 7, minSamplesSplit: 2\n",
      "························································\n",
      "╴feat: 2 <= 2.18, samples: 9600\n",
      "     ├─feat: 2 <= 1.90, samples: 4747\n",
      "     │   ├─feat: 4 <= 1.13, samples: 4694\n",
      "     │   │   └─╴value: 0.0\n",
      "     │   │   └─╴feat: 2 <= 0.90, samples: 619\n",
      "     │   │       └─╴value: 0.0\n",
      "     │   │       └─╴feat: 3 <= 0.71, samples: 102\n",
      "     │   │           └─╴value: 0.0\n",
      "     │   │           └─╴feat: 3 <= 0.75, samples: 19\n",
      "     │   │               └─╴value: 1.0\n",
      "     │   │               └─╴value: 0.0\n",
      "     │   └─╴feat: 3 <= 1.28, samples: 53\n",
      "     │       └─╴value: 0.0\n",
      "     │       └─╴feat: 0 <= 0.68, samples: 6\n",
      "     │           ├─feat: 2 <= 2.14, samples: 4\n",
      "     │           │   └─╴value: 1.0\n",
      "     └─╴feat: 2 <= 3.17, samples: 4853\n",
      "         ├─feat: 3 <= 1.48, samples: 214\n",
      "         │   ├─feat: 1 <= 0.03, samples: 82\n",
      "         │   │   └─╴value: 0.0\n",
      "         │   │   └─╴feat: 2 <= 2.56, samples: 30\n",
      "         │   │       └─╴feat: 4 <= 0.79, samples: 15\n",
      "         │   │           └─╴value: 1.0\n",
      "         │   │           └─╴value: 0.0\n",
      "         │   └─╴feat: 4 <= -0.91, samples: 132\n",
      "         │       └─╴value: 0.0\n",
      "         └─╴feat: 3 <= 0.24, samples: 4639\n",
      "             ├─feat: 4 <= -0.52, samples: 51\n",
      "             │   ├─feat: 0 <= -0.25, samples: 2\n",
      "             │   │   └─╴value: 1.0\n",
      "             │   └─╴value: 1.0\n",
      "             └─╴value: 1.0\n",
      "\n",
      "————————————————————— tree: 03/15 ——————————————————————\n",
      "split: CART, impurity: Entropy, leaf: Mode, nodes: 43\n",
      "maxDepth: 7, reached depth: 7, minSamplesSplit: 2\n",
      "························································\n",
      "╴feat: 2 <= 2.18, samples: 9600\n",
      "     ├─feat: 2 <= 1.90, samples: 4747\n",
      "     │   ├─feat: 4 <= 1.13, samples: 4694\n",
      "     │   │   └─╴value: 0.0\n",
      "     │   │   └─╴feat: 2 <= 0.90, samples: 619\n",
      "     │   │       └─╴value: 0.0\n",
      "     │   │       └─╴feat: 3 <= 0.71, samples: 102\n",
      "     │   │           └─╴value: 0.0\n",
      "     │   │           └─╴feat: 3 <= 0.75, samples: 19\n",
      "     │   │               └─╴value: 1.0\n",
      "     │   │               └─╴value: 0.0\n",
      "     │   └─╴feat: 3 <= 1.28, samples: 53\n",
      "     │       └─╴value: 0.0\n",
      "     │       └─╴feat: 2 <= 2.14, samples: 6\n",
      "     │           ├─feat: 2 <= 1.98, samples: 4\n",
      "     │           │   └─╴value: 1.0\n",
      "     │           │   └─╴value: 0.0\n",
      "     │           └─╴value: 1.0\n",
      "     └─╴feat: 2 <= 3.17, samples: 4853\n",
      "         ├─feat: 3 <= 1.48, samples: 214\n",
      "         │   ├─feat: 4 <= 1.09, samples: 82\n",
      "         │   │   ├─feat: 4 <= -0.93, samples: 51\n",
      "         │   │   │   ├─feat: 1 <= 0.13, samples: 13\n",
      "         │   │   │   │   └─╴value: -1.0\n",
      "         │   │   └─╴feat: 1 <= -0.25, samples: 31\n",
      "         │   │       ├─feat: 3 <= -0.48, samples: 18\n",
      "         │   │       │   └─╴value: 0.0\n",
      "         │   │       │   └─╴value: 1.0\n",
      "         │   │       └─╴feat: 4 <= 1.90, samples: 13\n",
      "         │   │           └─╴value: 0.0\n",
      "         │   │           └─╴value: 1.0\n",
      "         │   └─╴feat: 4 <= -0.91, samples: 132\n",
      "         │       └─╴value: 0.0\n",
      "         └─╴feat: 3 <= 0.24, samples: 4639\n",
      "             ├─feat: 4 <= -0.52, samples: 51\n",
      "             │   ├─feat: 0 <= -0.25, samples: 2\n",
      "             │   │   └─╴value: 1.0\n",
      "             │   └─╴value: 1.0\n",
      "             └─╴value: 1.0\n",
      "\n",
      "————————————————————— tree: 04/15 ——————————————————————\n",
      "split: CART, impurity: Entropy, leaf: Mode, nodes: 45\n",
      "maxDepth: 7, reached depth: 7, minSamplesSplit: 2\n",
      "························································\n",
      "╴feat: 2 <= 2.18, samples: 9600\n",
      "     ├─feat: 2 <= 1.90, samples: 4747\n",
      "     │   ├─feat: 4 <= 1.13, samples: 4694\n",
      "     │   │   └─╴value: 0.0\n",
      "     │   │   └─╴feat: 2 <= 0.90, samples: 619\n",
      "     │   │       └─╴value: 0.0\n",
      "     │   │       └─╴feat: 3 <= 0.71, samples: 102\n",
      "     │   │           └─╴value: 0.0\n",
      "     │   │           └─╴feat: 2 <= 0.94, samples: 19\n",
      "     │   │               └─╴value: 1.0\n",
      "     │   │               └─╴value: 0.0\n",
      "     │   └─╴feat: 3 <= 1.28, samples: 53\n",
      "     │       └─╴value: 0.0\n",
      "     │       └─╴feat: 0 <= 0.68, samples: 6\n",
      "     │           ├─feat: 2 <= 2.14, samples: 4\n",
      "     │           │   └─╴value: 1.0\n",
      "     └─╴feat: 2 <= 3.17, samples: 4853\n",
      "         ├─feat: 3 <= 1.48, samples: 214\n",
      "         │   ├─feat: 4 <= 1.09, samples: 82\n",
      "         │   │   ├─feat: 2 <= 2.92, samples: 51\n",
      "         │   │   │   ├─feat: 3 <= 0.92, samples: 43\n",
      "         │   │   │   │   └─╴value: 0.0\n",
      "         │   │   │   └─╴feat: 3 <= -0.18, samples: 8\n",
      "         │   │   │       └─╴value: 0.0\n",
      "         │   │   │       └─╴value: 1.0\n",
      "         │   │   └─╴feat: 1 <= -0.25, samples: 31\n",
      "         │   │       ├─feat: 3 <= -0.48, samples: 18\n",
      "         │   │       │   └─╴value: 0.0\n",
      "         │   │       │   └─╴value: 1.0\n",
      "         │   │       └─╴feat: 2 <= 2.78, samples: 13\n",
      "         │   │           └─╴value: -1.0\n",
      "         │   │           └─╴value: 0.0\n",
      "         │   └─╴feat: 4 <= -0.91, samples: 132\n",
      "         │       └─╴value: 0.0\n",
      "         └─╴feat: 3 <= 0.24, samples: 4639\n",
      "             ├─feat: 4 <= -0.52, samples: 51\n",
      "             │   ├─feat: 1 <= -1.11, samples: 2\n",
      "             │   │   └─╴value: 1.0\n",
      "             │   └─╴value: 1.0\n",
      "             └─╴value: 1.0\n",
      "\n",
      "————————————————————— tree: 05/15 ——————————————————————\n",
      "split: CART, impurity: Entropy, leaf: Mode, nodes: 43\n",
      "maxDepth: 7, reached depth: 7, minSamplesSplit: 2\n",
      "························································\n",
      "╴feat: 2 <= 2.18, samples: 9600\n",
      "     ├─feat: 2 <= 1.90, samples: 4747\n",
      "     │   ├─feat: 4 <= 1.13, samples: 4694\n",
      "     │   │   └─╴value: 0.0\n",
      "     │   │   └─╴feat: 2 <= 0.90, samples: 619\n",
      "     │   │       └─╴value: 0.0\n",
      "     │   │       └─╴feat: 3 <= 0.71, samples: 102\n",
      "     │   │           └─╴value: 0.0\n",
      "     │   │           └─╴feat: 0 <= 1.37, samples: 19\n",
      "     │   │               └─╴value: 0.0\n",
      "     │   │               └─╴value: 1.0\n",
      "     │   └─╴feat: 3 <= 1.28, samples: 53\n",
      "     │       └─╴value: 0.0\n",
      "     │       └─╴feat: 0 <= 0.68, samples: 6\n",
      "     │           ├─feat: 0 <= -1.53, samples: 4\n",
      "     │           │   └─╴value: 1.0\n",
      "     │           │   └─╴value: 0.0\n",
      "     │           └─╴value: 1.0\n",
      "     └─╴feat: 2 <= 3.03, samples: 4853\n",
      "         ├─feat: 3 <= 1.48, samples: 160\n",
      "         │   ├─feat: 4 <= 1.09, samples: 70\n",
      "         │   │   ├─feat: 3 <= 0.92, samples: 45\n",
johannes bilk's avatar
johannes bilk committed
      "         │   │   │   └─╴value: 0.0\n",
      "         │   │   │   └─╴feat: 3 <= 1.01, samples: 9\n",
      "         │   │   │       └─╴value: 1.0\n",
      "         │   │   │       └─╴value: 0.0\n",
      "         │   │   └─╴feat: 1 <= -0.25, samples: 25\n",
      "         │   │       ├─feat: 3 <= -0.47, samples: 16\n",
      "         │   │       │   └─╴value: 0.0\n",
      "         │   │       │   └─╴value: 1.0\n",
      "         │   │       └─╴feat: 4 <= 1.73, samples: 9\n",
      "         │   │           └─╴value: -1.0\n",
      "         │   │           └─╴value: 0.0\n",
      "         │   └─╴feat: 4 <= -0.89, samples: 90\n",
      "         │       └─╴value: 0.0\n",
johannes bilk's avatar
johannes bilk committed
      "         │       └─╴value: 1.0\n",
      "         └─╴feat: 3 <= 0.21, samples: 4693\n",
      "             ├─feat: 4 <= -0.02, samples: 52\n",
      "             │   ├─feat: 2 <= 3.67, samples: 5\n",
johannes bilk's avatar
johannes bilk committed
      "             │   │   └─╴value: 0.0\n",
      "             │   │   └─╴value: 1.0\n",
      "             │   └─╴value: 1.0\n",
johannes bilk's avatar
johannes bilk committed
      "             └─╴value: 1.0\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
johannes bilk's avatar
johannes bilk committed
    "ModelIO.save(forrest, 'test')\n",
    "newForrest = ModelIO.load('test')\n",
johannes bilk's avatar
johannes bilk committed
    "print(newForrest)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "━━━━━━━━━━━━ evaluation ━━━━━━━━━━━━\n",
      "————————— confusion matrix —————————\n",
      "              Class 0     Class 1   \n",
      "····································\n",
      "     Class 0    1599         1      \n",
johannes bilk's avatar
johannes bilk committed
      "                49%          0%     \n",
      "····································\n",
      "     Class 1    1597         3      \n",
      "                49%          0%     \n",
johannes bilk's avatar
johannes bilk committed
      "\n",
      "———————————————————————————————— scores ———————————————————————————————\n",
      "                accuracy       precision      sensitivity      miss rate    \n",
      "·······································································\n",
      "     Class 0     0.501            0.5            0.999           0.001      \n",
      "     Class 1     0.501            0.75           0.002           0.998      \n",
johannes bilk's avatar
johannes bilk committed
      "·······································································\n",
      "       total     0.501           0.625           0.501           0.499      \n"
johannes bilk's avatar
johannes bilk committed
     ]
    }
   ],
   "source": [
    "prediction = newForrest.eval(validData)\n",
    "\n",
    "if task == 'regressor':\n",
    "    newMetrics = RegressionScores(numClasses=2)\n",
    "    newMetrics.calcScores(prediction, validTargets, validLabels)\n",
    "    print(newMetrics)\n",
    "elif task == 'classifier':\n",
    "    newConfusion = ConfusionMatrix(numClasses=2)\n",
    "    newConfusion.update(prediction, validLabels)\n",
    "    newConfusion.percentages()\n",
    "    newConfusion.calcScores()\n",
    "    print(newConfusion)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Comment\n",
    "\n",
    "The forrest works as well as the tree code, because it completely builds on it, thus inhereting all it's problems. The progress bar for training trees is very erratic, because it's always set to the current level and because of the recurvise learning process it's jumping between levels. Also if you are using boosting, every tree will be trained twice."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.3"
  },
  "vscode": {
   "interpreter": {
    "hash": "aee8b7b246df8f9039afb4144a1f6fd8d2ca17a180786b69acc140d282b71a49"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}