Perform a partial train on the learner.

@param \Rubix\ML\Datasets\Dataset $dataset
@throws \InvalidArgumentException
485616
public function partial(Dataset $dataset) : void
    {
        if (empty($this->centroids) or empty($this->sizes)) {
            $this->train($dataset);

            return;
        }

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

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

        $n = $dataset->numRows();

        $samples = $dataset->samples();
        $labels = array_fill(0, $n, 0);

        $order = range(0, $n - 1);

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

        $previous = INF;

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

                array_multisort($order, $samples, $labels);
            }

            $sBatches = array_chunk($samples, $this->batchSize, true);
            $lBatches = array_chunk($labels, $this->batchSize, true);

            foreach ($sBatches as $i => $batch) {
                $assignments = array_map([self::class, 'assign'], $batch);

                $lHat = $lBatches[$i];

                foreach ($assignments as $j => $cluster) {
                    $expected = $lHat[$j];

                    if ($cluster !== $expected) {
                        $labels[$j] = $cluster;

                        $this->sizes[$expected]--;
                        $this->sizes[$cluster]++;
                    }
                }

                $strata = Labeled::quick($batch, $lHat)->stratify();

                foreach ($strata as $cluster => $stratum) {
                    $centroid = $this->centroids[$cluster];
                    $size = $this->sizes[$cluster];

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

                    $weight = 1. / ($size ?: EPSILON);

                    foreach ($centroid as $i => &$mean) {
                        $mean = (1. - $weight) * $mean + $weight * $step[$i];
                    }

                    $this->centroids[$cluster] = $centroid;
                }
            }

            $inertia = $this->inertia($samples);

            $this->steps[] = $inertia;

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

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

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

            $previous = $inertia;
        }

        if ($this->logger) {
            $this->logger->info('Training complete');
        }
    }
698003
public function fit(Dataset $dataset) : void
    {
        $this->intervals = [];

        $columns = $dataset->columnsByType(DataType::CONTINUOUS);

        foreach ($columns as $column => $values) {
            $min = min($values);
            $max = max($values);

            $edges = Vector::linspace($min, $max, $this->bins + 1)->asArray();

            array_shift($edges);

            $this->intervals[$column] = $edges;
        }
    }
1281025
public function train(Dataset $dataset) : void
    {
        if (!$dataset instanceof Labeled) {
            throw new InvalidArgumentException('This Estimator requires a'
                . ' Labeled training set.');
        }

        $classes = $dataset->possibleOutcomes();

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

        $this->counts = $this->probs = array_fill_keys(
            $classes,
            array_fill(0, $dataset->numColumns(), [])
        );

        $this->partial($dataset);
    }
1023816
public function update(Dataset $dataset) : void
    {
        if ($this->minimums === null or $this->maximums === null) {
            $this->fit($dataset);
            
            return;
        }

        $columns = $dataset->columnsByType(DataType::CONTINUOUS);

        foreach ($columns as $column => $values) {
            $min = min($values);
            $max = max($values);

            $min = min($min, $this->minimums[$column]);
            $max = max($max, $this->maximums[$column]);

            $scale = ($this->max - $this->min)
                / (($max - $min) ?: EPSILON);

            $minHat = $this->min - $min * $scale;

            $this->minimums[$column] = $min;
            $this->maximums[$column] = $max;
            $this->scales[$column] = $scale;
            $this->mins[$column] = $minHat;
        }
    }
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);
    }
253570
protected function processDataSet(DataSet $dataSet)
    {
        if ($dataSet->getPattern() === File::HEADER_PATTERN) {
            $this->processHeader($dataSet);
        } else {
            $this->processBrowserData($dataSet);
        }
    }
898529
public function train(Dataset $dataset) : void
    {
        if (!$dataset instanceof Labeled) {
            throw new InvalidArgumentException('This estimator requires a'
                . ' labeled training set.');
        }
        
        $k = (int) round($this->ratio * $dataset->numRows());

        $this->forest = [];

        Loop::run(function () use ($dataset, $k) {
            $pool = new DefaultPool($this->workers);

            $coroutines = [];

            for ($i = 0; $i < $this->estimators; $i++) {
                $estimator = clone $this->base;

                $subset = $dataset->randomSubsetWithReplacement($k);

                $task = new CallableTask(
                    [$this, '_train'],
                    [$estimator, $subset]
                );

                $coroutines[] = call(function () use ($pool, $task) {
                    return yield $pool->enqueue($task);
                });
            }

            $this->forest = yield all($coroutines);
            
            return yield $pool->shutdown();
        });

        $this->classes = $dataset->possibleOutcomes();
        $this->featureCount = $dataset->numColumns();
    }
1217154
public function train(Dataset $dataset) : void
    {
        if ($this->type === self::CLASSIFIER or $this->type === self::REGRESSOR) {
            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([
                'experts' => $this->experts,
                'influences' => $this->influences,
                'workers' => $this->workers,
            ]));
        }

        Loop::run(function () use ($dataset) {
            $pool = new DefaultPool($this->workers);

            $coroutines = [];

            foreach ($this->experts as $index => $estimator) {
                $task = new CallableTask(
                    [$this, '_train'],
                    [$estimator, $dataset]
                );

                $coroutines[] = call(function () use ($pool, $task, $index) {
                    $estimator = yield $pool->enqueue($task);

                    if ($this->logger) {
                        $this->logger->info(Params::stringify([
                            $index => $estimator,
                        ]) . ' finished training');
                    }

                    return $estimator;
                });
            }

            $this->experts = yield all($coroutines);
            
            return yield $pool->shutdown();
        });

        if ($this->type === self::CLASSIFIER and $dataset instanceof Labeled) {
            $this->classes = $dataset->possibleOutcomes();
        }

        if ($this->logger) {
            $this->logger->info('Training complete');
        }
    }
1262051
public function execute(DataSetInterface $dataSet): void
    {
        $categoryKey = $dataSet[ProductListDataSetInterface::CATEGORY_KEY];
        if (!$categoryKey) {
            throw new InvalidDataException(sprintf('"%s" is required.', ProductListDataSetInterface::CATEGORY_KEY));
        }

        $dataSet[ProductListDataSetInterface::ID_CATEGORY] = $this->getIdCategoryByKey($categoryKey);
    }
719504
public static function terminate(Labeled $dataset) : self
    {
        $samples = $dataset->samples();
        $labels = $dataset->labels();

        $min = $max = [];

        foreach ($dataset->columns() as $values) {
            $min[] = min($values);
            $max[] = max($values);
        }

        return new self($samples, $labels, $min, $max);
    }
1239152
public function train(Dataset $dataset) : void
    {
        if (!$dataset instanceof Labeled) {
            throw new InvalidArgumentException('This estimator requires a'
                . ' labeled training set.');
        }

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

        $samples = $dataset->samples();
        $labels = $dataset->labels();

        $biases = Matrix::ones($dataset->numRows(), 1);

        $x = Matrix::build($samples)->augmentLeft($biases);
        $y = Vector::build($labels);

        $alphas = array_fill(0, $x->n() - 1, $this->alpha);

        $penalty = Matrix::diagonal(array_merge([0.], $alphas));

        $xT = $x->transpose();

        $coefficients = $xT->matmul($x)
            ->add($penalty)
            ->inverse()
            ->dot($xT->dot($y))
            ->asArray();

        $this->bias = (float) array_shift($coefficients);
        $this->weights = Vector::quick($coefficients);
    }
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');
        }
    }
418536
public function grow(Dataset $dataset) : void
    {
        $depth = 1;

        $this->root = Isolator::split($dataset);

        $stack = [[$this->root, $depth]];

        while ($stack) {
            [$current, $depth] = array_pop($stack) ?? [];

            [$left, $right] = $current->groups();

            $depth++;

            if ($depth >= $this->maxDepth) {
                $current->attachLeft(Cell::terminate($left, $depth));
                $current->attachRight(Cell::terminate($right, $depth));
    
                continue 1;
            }
    
            if ($left->numRows() > $this->maxLeafSize) {
                $node = Isolator::split($left);
    
                $current->attachLeft($node);
    
                $stack[] = [$node, $depth];
            } else {
                $current->attachLeft(Cell::terminate($left, $depth));
            }
    
            if ($right->numRows() > $this->maxLeafSize) {
                $node = Isolator::split($right);
    
                $current->attachRight($node);
    
                $stack[] = [$node, $depth];
            } else {
                $current->attachRight(Cell::terminate($right, $depth));
            }

            $current->cleanup();
        }
    }
1246333
public function train(Dataset $dataset) : void
    {
        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([
                'base' => $this->base,
                'metric' => $this->metric,
                'validator' => $this->validator,
                'workers' => $this->workers,
            ]));
        }

        $results = [];

        Loop::run(function () use (&$results, $dataset) {
            $pool = new DefaultPool($this->workers);

            $coroutines = [];

            foreach ($this->combinations as $params) {
                $estimator = new $this->base(...$params);

                $task = new CallableTask(
                    [$this, 'score'],
                    [$estimator, $dataset]
                );

                $coroutines[] = call(function () use ($pool, $task, $params) {
                    $score = yield $pool->enqueue($task);

                    if ($this->logger) {
                        $constructor = array_combine($this->args, $params) ?: [];
                            
                        $this->logger->info('Test complete: ' . Params::stringify([
                            Params::shortName($this->metric) => $score,
                        ]) . ' Params: ' . Params::stringify($constructor));
                    }

                    return [$score, $params];
                });
            }

            $results = yield all($coroutines);
            
            return yield $pool->shutdown();
        });

        $bestScore = -INF;
        $bestParams = [];

        foreach ($results as [$score, $params]) {
            if ($score > $bestScore) {
                $bestScore = $score;
                $bestParams = $params;
            }
        }

        $this->best = [
            'score' => $bestScore,
            'params' => $bestParams,
        ];

        if ($this->logger) {
            $constructor = array_combine($this->args, $bestParams) ?: [];

            $this->logger->info('Best params: ' . Params::stringify($constructor));
        }

        $estimator = new $this->base(...$bestParams);

        $estimator->train($dataset);

        $this->estimator = $estimator;

        if ($this->logger) {
            $this->logger->info('Training complete');
        }
    }
1132138
public function train(Dataset $dataset) : void
    {
        DatasetIsCompatibleWithEstimator::check($dataset, $this);

        $this->model = $this->svm->train($dataset->samples());
    }
1197360
private function buildMin(Dataset $dataset): self
    {
        $this->min = new Quartile(min(...$dataset->ordinates()));

        return $this;
    }
75653
public function execute(DataSetInterface $dataSet): void
    {
        $productListKey = $dataSet[MerchantRelationshipProductListDataSetInterface::PRODUCT_LIST_KEY];
        if (!$productListKey) {
            throw new InvalidDataException(sprintf('"%s" is required.', MerchantRelationshipProductListDataSetInterface::PRODUCT_LIST_KEY));
        }

        if (!isset($this->idProductListCache[$productListKey])) {
            /** @var \Orm\Zed\ProductList\Persistence\SpyProductListQuery $productListQuery */
            $productListQuery = SpyProductListQuery::create()->select(SpyProductListTableMap::COL_ID_PRODUCT_LIST);
            /** @var int|null $idProductList */
            $idProductList = $productListQuery->findOneByKey($productListKey);

            if (!$idProductList) {
                throw new EntityNotFoundException(sprintf('Could not find Product List by key "%s"', $productListKey));
            }
            $this->idProductListCache[$productListKey] = $idProductList;
        }
        $dataSet[MerchantRelationshipProductListDataSetInterface::ID_PRODUCT_LIST] = $this->idProductListCache[$productListKey];
    }