DecisionTreeLeaf::getHTML()   B
last analyzed

Complexity

Conditions 8
Paths 25

Size

Total Lines 42
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 26
dl 0
loc 42
rs 8.4444
c 0
b 0
f 0
cc 8
nc 25
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Phpml\Classification\DecisionTree;
6
7
use Phpml\Math\Comparison;
8
9
class DecisionTreeLeaf
10
{
11
    /**
12
     * @var string|int
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|null
33
     */
34
    public $leftLeaf;
35
36
    /**
37
     * @var DecisionTreeLeaf|null
38
     */
39
    public $rightLeaf;
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
     * HTML representation of the tree without column names
76
     */
77
    public function __toString(): string
78
    {
79
        return $this->getHTML();
80
    }
81
82
    public function evaluate(array $record): bool
83
    {
84
        $recordField = $record[$this->columnIndex];
85
86
        if ($this->isContinuous) {
87
            return Comparison::compare((string) $recordField, $this->numericValue, $this->operator);
88
        }
89
90
        return $recordField == $this->value;
91
    }
92
93
    /**
94
     * Returns Mean Decrease Impurity (MDI) in the node.
95
     * For terminal nodes, this value is equal to 0
96
     */
97
    public function getNodeImpurityDecrease(int $parentRecordCount): float
98
    {
99
        if ($this->isTerminal) {
100
            return 0.0;
101
        }
102
103
        $nodeSampleCount = (float) count($this->records);
104
        $iT = $this->giniIndex;
105
106
        if ($this->leftLeaf !== null) {
107
            $pL = count($this->leftLeaf->records) / $nodeSampleCount;
108
            $iT -= $pL * $this->leftLeaf->giniIndex;
109
        }
110
111
        if ($this->rightLeaf !== null) {
112
            $pR = count($this->rightLeaf->records) / $nodeSampleCount;
113
            $iT -= $pR * $this->rightLeaf->giniIndex;
114
        }
115
116
        return $iT * $nodeSampleCount / $parentRecordCount;
117
    }
118
119
    /**
120
     * Returns HTML representation of the node including children nodes
121
     */
122
    public function getHTML(?array $columnNames = null): string
123
    {
124
        if ($this->isTerminal) {
125
            $value = "<b>${this}->classValue</b>";
126
        } else {
127
            $value = $this->value;
128
            if ($columnNames !== null) {
129
                $col = $columnNames[$this->columnIndex];
130
            } else {
131
                $col = "col_$this->columnIndex";
132
            }
133
134
            if ((bool) preg_match('/^[<>=]{1,2}/', (string) $value) === false) {
135
                $value = "=${value}";
136
            }
137
138
            $value = "<b>${col} ${value}</b><br>Gini: ".number_format($this->giniIndex, 2);
139
        }
140
141
        $str = "<table ><tr><td colspan=3 align=center style='border:1px solid;'>${value}</td></tr>";
142
143
        if ($this->leftLeaf !== null || $this->rightLeaf !== null) {
144
            $str .= '<tr>';
145
            if ($this->leftLeaf !== null) {
146
                $str .= '<td valign=top><b>| Yes</b><br>'.$this->leftLeaf->getHTML($columnNames).'</td>';
147
            } else {
148
                $str .= '<td></td>';
149
            }
150
151
            $str .= '<td>&nbsp;</td>';
152
            if ($this->rightLeaf !== null) {
153
                $str .= '<td valign=top align=right><b>No |</b><br>'.$this->rightLeaf->getHTML($columnNames).'</td>';
154
            } else {
155
                $str .= '<td></td>';
156
            }
157
158
            $str .= '</tr>';
159
        }
160
161
        $str .= '</table>';
162
163
        return $str;
164
    }
165
}
166