maxFeatures = $maxFeatures; } /** * Randomized algorithm that chooses the split point with the lowest impurity * among a random selection of features. * * @param Labeled $dataset * @return Split */ protected function split(Labeled $dataset) : Split { [$m, $n] = $dataset->shape(); $maxFeatures = $this->maxFeatures ?? (int) round(sqrt($n)); $columns = array_fill(0, $dataset->numFeatures(), null); $columns = (array) array_rand($columns, min($maxFeatures, count($columns))); $randMax = getrandmax(); $bestColumn = $bestValue = $bestGroups = null; $bestImpurity = INF; foreach ($columns as $column) { $values = $dataset->feature($column); $type = $dataset->featureType($column); if ($type->isContinuous()) { $min = min($values); $max = max($values); $maxAbs = max(abs($max), abs($min)); $phi = $maxAbs != 0.0 ? $randMax / $maxAbs : $randMax; $min = (int) floor($min * $phi); $max = (int) ceil($max * $phi); $value = rand($min, $max) / $phi; } else { $values = array_unique($values); $offset = array_rand($values); $value = $values[$offset]; } $groups = $dataset->splitByFeature($column, $value); $impurity = $this->splitImpurity($groups); if ($impurity < $bestImpurity) { $bestColumn = $column; $bestValue = $value; $bestGroups = $groups; $bestImpurity = $impurity; } if ($impurity <= 0.0) { break; } } if (!is_int($bestColumn) or $bestValue === null or $bestGroups === null) { throw new RuntimeException('Could not split dataset.'); } return new Split( $bestColumn, $bestValue, $bestGroups, $bestImpurity, $m ); } }