*/ protected array $center; /** * The radius of the centroid. * * @var float */ protected float $radius; /** * The left and right subsets of the training data. * * @var array{Labeled,Labeled} */ protected array $subsets; /** * Factory method to build a hypersphere by splitting the dataset into left and right clusters. * * @param Labeled $dataset * @param Distance $kernel * @return self */ public static function split(Labeled $dataset, Distance $kernel) : self { $center = []; foreach ($dataset->features() as $column => $values) { if ($dataset->featureType($column)->isContinuous()) { $center[] = Stats::mean($values); } else { $center[] = argmax(array_count_values($values)); } } $distances = []; foreach ($dataset->samples() as $sample) { $distances[] = $kernel->compute($sample, $center); } $radius = max($distances) ?: 0.0; $leftCentroid = $dataset->sample(argmax($distances)); $distances = []; foreach ($dataset->samples() as $sample) { $distances[] = $kernel->compute($sample, $leftCentroid); } $rightCentroid = $dataset->sample(argmax($distances)); $subsets = $dataset->spatialSplit($leftCentroid, $rightCentroid, $kernel); return new self($center, $radius, $subsets); } /** * @param list $center * @param float $radius * @param array{Labeled,Labeled} $subsets */ public function __construct(array $center, float $radius, array $subsets) { $this->center = $center; $this->radius = $radius; $this->subsets = $subsets; } /** * Return the center vector. * * @return list */ public function center() : array { return $this->center; } /** * Return the radius of the centroid. * * @return float */ public function radius() : float { return $this->radius; } /** * Does the hypersphere reduce to a single point? * * @return bool */ public function isPoint() : bool { return $this->radius === 0.0; } /** * Return the left and right subsets of the training data. * * @throws RuntimeException * @return array{Labeled,Labeled} */ public function subsets() : array { if (!isset($this->subsets)) { throw new RuntimeException('Subsets property does not exist.'); } return $this->subsets; } /** * Remove any variables carried over from the parent node. */ public function cleanup() : void { unset($this->subsets); } }