Make predictions from a dataset.

@param \Rubix\ML\Datasets\Dataset $dataset
@throws \InvalidArgumentException
@return array
354996
public function predict(Dataset $dataset) : array
    {
        DatasetIsCompatibleWithEstimator::check($dataset, $this);

        $n = $dataset->numRows();

        $dataset = Labeled::quick($dataset->samples(), range(0, $n - 1));

        $tree = new BallTree($this->maxLeafSize, $this->kernel);

        $tree->grow($dataset);
        
        $cluster = self::START_CLUSTER;

        $predictions = [];

        foreach ($dataset as $i => $sample) {
            if (isset($predictions[$i])) {
                continue 1;
            }

            [$samples, $neighbors, $distances] = $tree->range($sample, $this->radius);

            if (count($neighbors) < $this->minDensity) {
                $predictions[$i] = self::NOISE;

                continue 1;
            }

            $predictions[$i] = $cluster;

            while ($neighbors) {
                $index = array_pop($neighbors);

                if (isset($predictions[$index])) {
                    if ($predictions[$index] === self::NOISE) {
                        $predictions[$index] = $cluster;
                    }

                    continue 1;
                }

                $predictions[$index] = $cluster;

                $neighbor = $dataset[$index];

                [$samples, $seeds, $distances] = $tree->range($neighbor, $this->radius);

                if (count($seeds) >= $this->minDensity) {
                    $neighbors = array_unique(array_merge($neighbors, $seeds));
                }
            }

            $cluster++;
        }

        return $predictions;
    }
679519
public function predict(Dataset $dataset) : array
    {
        if ($this->tree->bare()) {
            throw new RuntimeException('Estimator has not been trainied.');
        }

        DatasetIsCompatibleWithEstimator::check($dataset, $this);

        $predictions = [];

        foreach ($dataset as $sample) {
            [$labels, $distances] = $this->tree->nearest($sample, $this->k);

            if ($this->weighted) {
                $weights = [];

                foreach ($distances as $distance) {
                    $weights[] = 1. / (1. + $distance);
                }

                $predictions[] = Stats::weightedMean($labels, $weights);
            } else {
                $predictions[] = Stats::mean($labels);
            }
        }

        return $predictions;
    }
677582
public function partial(Dataset $dataset) : void
    {
        if (!$this->network) {
            $this->train($dataset);

            return;
        }

        if (!$dataset instanceof Labeled) {
            throw new InvalidArgumentException('This estimator requires a'
                . ' labeled training set.');
        }

        DatasetIsCompatibleWithEstimator::check($dataset, $this);

        if ($this->logger) {
            $this->logger->info('Learner init ' . Params::stringify([
                'batch_size' => $this->batchSize,
                'optimizer' => $this->optimizer,
                'alpha' => $this->alpha,
                'epochs' => $this->epochs,
                'min_change' => $this->minChange,
                'cost_fn' => $this->costFn,
            ]));
        }

        $n = $dataset->numRows();

        [$testing, $training] = $dataset->stratifiedSplit($this->holdout);

        [$min, $max] = $this->metric->range();

        $randomize = $n > $this->batchSize ? true : false;

        $bestScore = $min;
        $bestSnapshot = null;
        $previous = INF;

        for ($epoch = 1; $epoch <= $this->epochs; $epoch++) {
            if ($randomize) {
                $training->randomize();
            }

            $batches = $training->batch($this->batchSize);

            $loss = 0.;

            foreach ($batches as $batch) {
                $loss += $this->network->roundtrip($batch);
            }

            $loss /= $n;

            $predictions = $this->predict($testing);

            $score = $this->metric->score($predictions, $testing->labels());

            $this->steps[] = $loss;
            $this->scores[] = $score;

            if ($score > $bestScore) {
                $bestScore = $score;
                $bestSnapshot = new Snapshot($this->network);
            }

            if ($this->logger) {
                $this->logger->info("Epoch $epoch complete,"
                    . " score=$score loss=$loss");
            }

            if (is_nan($loss) or is_nan($score)) {
                break 1;
            }

            if (abs($previous - $loss) < $this->minChange) {
                break 1;
            }

            if ($loss < EPSILON or $score >= $max) {
                break 1;
            }

            if ($epoch > $this->window) {
                $window = array_slice($this->scores, -$this->window);

                $worst = $window;
                rsort($worst);

                if ($window === $worst) {
                    break 1;
                }
            }
        }

        if (end($this->scores) < $bestScore) {
            if ($bestSnapshot) {
                $this->network->restore($bestSnapshot);

                if ($this->logger) {
                    $this->logger->info('Network restored'
                        . ' from previous snapshot');
                }
            }
        }

        if ($this->logger) {
            $this->logger->info('Training complete');
            $this->logger->info("Best validation score=$bestScore");
        }
    }
289446
public function predict(Dataset $dataset) : array
    {
        if (!$this->network) {
            throw new RuntimeException('The learner has not'
                . ' been trained.');
        }

        DatasetIsCompatibleWithEstimator::check($dataset, $this);

        $xT = Matrix::quick($dataset->samples())->transpose();

        return $this->network->infer($xT)->row(0);
    }
1217596
public function rank(Dataset $dataset) : array
    {
        if (!$this->r or !$this->histograms) {
            throw new RuntimeException('The learner has not'
                . ' been trained.');
        }

        DatasetIsCompatibleWithEstimator::check($dataset, $this);

        $z = Matrix::quick($dataset->samples())
            ->matmul($this->r)
            ->transpose();

        return $this->logLikelihood($z);
    }
461269
public function proba(Dataset $dataset) : array
    {
        if (empty($this->ensemble) or empty($this->influences)) {
            throw new RuntimeException('The learner has not'
                . ' been trained.');
        }

        $scores = $this->score($dataset);

        $probabilities = [];

        foreach ($scores as $scores) {
            $total = array_sum($scores) ?: EPSILON;

            $dist = [];

            foreach ($scores as $class => $score) {
                $dist[$class] = $score / $total;
            }

            $probabilities[] = $dist;
        }

        return $probabilities;
    }
442803
public function predict(Dataset $dataset) : array
    {
        if (empty($this->ensemble) or empty($this->influences)) {
            throw new RuntimeException('The learner has not'
                . ' been trained.');
        }
        
        return array_map('Rubix\ML\argmax', $this->score($dataset));
    }
1036578
public function fit(Dataset $dataset) : void
    {
        if (!$dataset->homogeneous() or $dataset->columnType(0) !== DataType::CONTINUOUS) {
            throw new InvalidArgumentException('This transformer only works'
                . ' with continuous features.');
        }

        $xT = Matrix::build($dataset->samples())->transpose();

        [$eigenvalues, $eigenvectors] = $xT->covariance()->eig(true);

        $totalVar = array_sum($eigenvalues);

        $eigenvectors = $eigenvectors->asArray();
        
        array_multisort($eigenvalues, SORT_DESC, $eigenvectors);

        $eigenvalues = array_slice($eigenvalues, 0, $this->dimensions);
        $eigenvectors = array_slice($eigenvectors, 0, $this->dimensions);

        $eigenvectors = Matrix::quick($eigenvectors)->transpose();

        $explainedVar = (float) array_sum($eigenvalues);
        $noiseVar = $totalVar - $explainedVar;

        $this->explainedVar = $explainedVar;
        $this->noiseVar = $noiseVar;
        $this->lossiness = $noiseVar / ($totalVar ?: EPSILON);

        $this->mean = $xT->mean()->transpose();

        $this->eigenvectors = $eigenvectors;
    }
741987
public function create($dataset)
    {
        // Event
        $this->triggerEvent(
            'onBefore' . ucfirst(__FUNCTION__),
            [
                'dataset' => &$dataset,
            ]
        );

        if (!($dataset instanceof \Traversable) && !is_array($dataset)) {
            throw new \InvalidArgumentException('DataSet object should be instance of a Traversable');
        }

        $result = $this->doCreate($dataset);

        // Event
        $this->triggerEvent(
            'onAfter' . ucfirst(__FUNCTION__),
            [
                'result' => &$result,
            ]
        );

        return $result;
    }
313392
public function train(Dataset $dataset) : void
    {
        DatasetIsCompatibleWithEstimator::check($dataset, $this);

        if ($this->logger) {
            $this->logger->info('Learner init ' . Params::stringify([
                'radius' => $this->radius,
                'kernel' => $this->kernel,
                'max_leaf_size' => $this->maxLeafSize,
                'epochs' => $this->epochs,
                'min_change' => $this->minChange,
                'seeder' => $this->seeder,
                'ratio' => $this->ratio,
            ]));
        }

        $n = $dataset->numRows();

        $tree = new BallTree($this->maxLeafSize, $this->kernel);

        if ($this->seeder and $n > self::MIN_SEEDS) {
            $k = (int) round($this->ratio * $n);

            $centroids = $this->seeder->seed($dataset, $k);
        } else {
            $centroids = $dataset->samples();
        }

        $tree->grow($dataset);

        $previous = $centroids;

        $this->steps = [];
 
        for ($epoch = 1; $epoch <= $this->epochs; $epoch++) {
            foreach ($centroids as $i => &$centroid) {
                [$samples, $labels, $distances] = $tree->range($centroid, $this->radius);

                $step = Matrix::quick($samples)
                    ->transpose()
                    ->mean()
                    ->asArray();

                $mu2 = Stats::mean($distances) ** 2;

                $weight = exp(-$mu2 / $this->delta);

                foreach ($centroid as $column => &$mean) {
                    $mean = ($weight * $step[$column]) / $weight;
                }

                foreach ($centroids as $j => $neighbor) {
                    if ($i === $j) {
                        continue 1;
                    }

                    $distance = $this->kernel->compute($centroid, $neighbor);

                    if ($distance < $this->radius) {
                        unset($centroids[$j]);
                    }
                }
            }

            $shift = $this->shift($centroids, $previous);

            $this->steps[] = $shift;

            if ($this->logger) {
                $this->logger->info("Epoch $epoch complete, shift=$shift");
            }

            if (is_nan($shift)) {
                break 1;
            }

            if ($shift < $this->minChange) {
                break 1;
            }

            $previous = $centroids;
        }

        $this->centroids = array_values($centroids);

        if ($this->logger) {
            $this->logger->info('Training complete');
        }
    }
104021
public static function stack(array $datasets) : self
    {
        $samples = $labels = [];

        foreach ($datasets as $dataset) {
            if (!$dataset instanceof self) {
                throw new InvalidArgumentException('Dataset must be'
                    . ' an instance of Labeled, ' . get_class($dataset)
                    . ' given.');
            }

            $samples = array_merge($samples, $dataset->samples());
            $labels = array_merge($labels, $dataset->labels());
        }

        return self::quick($samples, $labels);
    }
1366403
public function partial(Dataset $dataset) : void
    {
        if (empty($this->weights) or empty($this->means) or empty($this->variances)) {
            $this->train($dataset);

            return;
        }

        if (!$dataset instanceof Labeled) {
            throw new InvalidArgumentException('This Estimator requires a'
                . ' Labeled training set.');
        }

        DatasetIsCompatibleWithEstimator::check($dataset, $this);

        foreach ($dataset->stratify() as $class => $stratum) {
            $means = $this->means[$class];
            $variances = $this->variances[$class];

            $oldWeight = $this->weights[$class];
            $oldMeans = $this->means[$class];
            $oldVariances = $this->variances[$class];

            $n = $stratum->numRows();

            foreach ($stratum->columns() as $column => $values) {
                [$mean, $variance] = Stats::meanVar($values);

                $means[$column] = (($n * $mean)
                    + ($oldWeight * $oldMeans[$column]))
                    / ($oldWeight + $n);

                $vHat = ($oldWeight
                    * $oldVariances[$column] + ($n * $variance)
                    + ($oldWeight / ($n * ($oldWeight + $n)))
                    * ($n * $oldMeans[$column] - $n * $mean) ** 2)
                    / ($oldWeight + $n);

                $variances[$column] = $vHat ?: EPSILON;
            }

            $this->means[$class] = $means;
            $this->variances[$class] = $variances;
            
            $this->weights[$class] += $n;
        }

        if ($this->fitPriors) {
            $total = array_sum($this->weights) ?: EPSILON;

            foreach ($this->weights as $class => $weight) {
                $this->priors[$class] = log($weight / $total);
            }
        }
    }
1047502
public function proba(Dataset $dataset) : array
    {
        if (empty($this->forest)) {
            throw new RuntimeException('The learner has not'
                . ' been trained.');
        }

        $probabilities = array_fill(
            0,
            $dataset->numRows(),
            array_fill_keys($this->classes, 0.)
        );

        foreach ($this->forest as $tree) {
            foreach ($tree->proba($dataset) as $i => $joint) {
                foreach ($joint as $class => $probability) {
                    $probabilities[$i][$class] += $probability;
                }
            }
        }

        foreach ($probabilities as &$joint) {
            foreach ($joint as &$probability) {
                $probability /= $this->estimators;
            }
        }

        return $probabilities;
    }
1162748
protected function split(Labeled $dataset) : Decision
    {
        $bestImpurity = INF;
        $bestColumn = $bestValue = null;
        $bestGroups = [];

        $max = $dataset->numRows() - 1;

        shuffle($this->columns);

        foreach (array_slice($this->columns, 0, $this->maxFeatures) as $column) {
            $sample = $dataset->row(rand(0, $max));

            $value = $sample[$column];

            $groups = $dataset->partition($column, $value);

            $impurity = $this->splitImpurity($groups);

            if ($impurity < $bestImpurity) {
                $bestColumn = $column;
                $bestValue = $value;
                $bestGroups = $groups;
                $bestImpurity = $impurity;
            }

            if ($impurity < $this->tolerance) {
                break 1;
            }
        }

        return new Decision($bestColumn, $bestValue, $bestGroups, $bestImpurity);
    }
521517
public function train(Dataset $dataset) : void
    {
        if (!$dataset instanceof Labeled) {
            throw new InvalidArgumentException('This Estimator requires a'
                . ' Labeled training set.');
        }

        DatasetIsCompatibleWithEstimator::check($dataset, $this);

        $classes = $dataset->possibleOutcomes();

        $this->classes = $classes;
        $this->weights = array_fill_keys($classes, 0);

        $this->means = $this->variances = array_fill_keys($classes, []);

        foreach ($dataset->stratify() as $class => $stratum) {
            $means = $variances = [];

            foreach ($stratum->columns() as $values) {
                [$mean, $variance] = Stats::meanVar($values);

                $means[] = $mean;
                $variances[] = $variance ?: EPSILON;
            }

            $this->means[$class] = $means;
            $this->variances[$class] = $variances;

            $this->weights[$class] += $stratum->numRows();
        }

        if ($this->fitPriors) {
            $this->priors = [];

            $total = array_sum($this->weights) ?: EPSILON;

            foreach ($this->weights as $class => $weight) {
                $this->priors[$class] = log($weight / $total);
            }
        }
    }
194102
public function append(Dataset $dataset) : Dataset
    {
        return self::quick(array_merge($this->samples, $dataset->samples()));
    }
959146
public function train(Dataset $dataset) : void
    {
        DatasetIsCompatibleWithEstimator::check($dataset, $this);

        $this->centroids = $this->seeder->seed($dataset, $this->k);

        $sizes = array_fill(1, $this->k, 0);
        $sizes[0] = $dataset->numRows();

        $this->sizes = $sizes;

        $this->steps = [];

        $this->partial($dataset);
    }