Test Failed
Pull Request — master (#100)
by Никита
04:08
created

DecisionTreeLeaf   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 184
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
wmc 22
lcom 1
cbo 1
dl 0
loc 184
rs 10
c 0
b 0
f 0

4 Methods

Rating   Name   Duplication   Size   Complexity  
D evaluate() 0 30 9
A getNodeImpurityDecrease() 0 21 4
C getHTML() 0 36 8
A __toString() 0 4 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Phpml\Classification\DecisionTree;
6
7
use Phpml\Exception\InvalidArgumentException;
8
9
class DecisionTreeLeaf
10
{
11
    /**
12
     * @var string
13
     */
14
    public $value;
15
16
    /**
17
     * @var float
18
     */
19
    public $numericValue;
20
21
    /**
22
     * @var string
23
     */
24
    public $operator;
25
26
    /**
27
     * @var int
28
     */
29
    public $columnIndex;
30
31
    /**
32
     * @var DecisionTreeLeaf
33
     */
34
    public $leftLeaf = null;
35
36
    /**
37
     * @var DecisionTreeLeaf
38
     */
39
    public $rightLeaf= null;
40
41
    /**
42
     * @var array
43
     */
44
    public $records = [];
45
46
    /**
47
     * Class value represented by the leaf, this value is non-empty
48
     * only for terminal leaves
49
     *
50
     * @var string
51
     */
52
    public $classValue = '';
53
54
    /**
55
     * @var bool
56
     */
57
    public $isTerminal = false;
58
59
    /**
60
     * @var bool
61
     */
62
    public $isContinuous = false;
63
64
    /**
65
     * @var float
66
     */
67
    public $giniIndex = 0;
68
69
    /**
70
     * @var int
71
     */
72
    public $level = 0;
73
74
    /**
75
     * @param array $record
76
     * @return bool
77
     * @throws InvalidArgumentException
78
     */
79
    public function evaluate($record)
80
    {
81
        $recordField = $record[$this->columnIndex];
82
83
        if ($this->isContinuous) {
84
            $op = $this->operator;
85
            $value = $this->numericValue;
86
            if (is_null($recordField)) {
87
                return false;
88
            }
89
            $recordField = strval($recordField);
90
            switch ($op) {
91
                case '>=':
92
                    return ($recordField >= $value);
93
                case '<=':
94
                    return ($recordField <= $value);
95
                case '>':
96
                    return ($recordField > $value);
97
                case '<':
98
                    return ($recordField < $value);
99
                case '==':
100
                case '=':
101
                    return ($recordField == $value);
102
                default:
103
                    throw new InvalidArgumentException('Malformed operator: '.$op);
104
            }
105
        }
106
        
107
        return $recordField == $this->value;
108
    }
109
110
    /**
111
     * Returns Mean Decrease Impurity (MDI) in the node.
112
     * For terminal nodes, this value is equal to 0
113
     *
114
     * @param int $parentRecordCount
115
     *
116
     * @return float
117
     */
118
    public function getNodeImpurityDecrease(int $parentRecordCount)
119
    {
120
        if ($this->isTerminal) {
121
            return 0.0;
122
        }
123
124
        $nodeSampleCount = (float)count($this->records);
125
        $iT = $this->giniIndex;
126
127
        if ($this->leftLeaf) {
128
            $pL = count($this->leftLeaf->records)/$nodeSampleCount;
129
            $iT -= $pL * $this->leftLeaf->giniIndex;
130
        }
131
132
        if ($this->rightLeaf) {
133
            $pR = count($this->rightLeaf->records)/$nodeSampleCount;
134
            $iT -= $pR * $this->rightLeaf->giniIndex;
135
        }
136
137
        return $iT * $nodeSampleCount / $parentRecordCount;
138
    }
139
140
    /**
141
     * Returns HTML representation of the node including children nodes
142
     *
143
     * @param $columnNames
144
     * @return string
145
     */
146
    public function getHTML($columnNames = null)
147
    {
148
        if ($this->isTerminal) {
149
            $value = "<b>$this->classValue</b>";
150
        } else {
151
            $value = $this->value;
152
            if ($columnNames !== null) {
153
                $col = $columnNames[$this->columnIndex];
154
            } else {
155
                $col = "col_$this->columnIndex";
156
            }
157
            if (!preg_match("/^[<>=]{1,2}/", $value)) {
158
                $value = "=$value";
159
            }
160
            $value = "<b>$col $value</b><br>Gini: ". number_format($this->giniIndex, 2);
161
        }
162
        $str = "<table ><tr><td colspan=3 align=center style='border:1px solid;'>
163
				$value</td></tr>";
164
        if ($this->leftLeaf || $this->rightLeaf) {
165
            $str .='<tr>';
166
            if ($this->leftLeaf) {
167
                $str .="<td valign=top><b>| Yes</b><br>" . $this->leftLeaf->getHTML($columnNames) . "</td>";
168
            } else {
169
                $str .='<td></td>';
170
            }
171
            $str .='<td>&nbsp;</td>';
172
            if ($this->rightLeaf) {
173
                $str .="<td valign=top align=right><b>No |</b><br>" . $this->rightLeaf->getHTML($columnNames) . "</td>";
174
            } else {
175
                $str .='<td></td>';
176
            }
177
            $str .= '</tr>';
178
        }
179
        $str .= '</table>';
180
        return $str;
181
    }
182
183
    /**
184
     * HTML representation of the tree without column names
185
     *
186
     * @return string
187
     */
188
    public function __toString()
189
    {
190
        return $this->getHTML();
191
    }
192
}
193