mirror of
https://github.com/Llewellynvdm/php-ml.git
synced 2024-11-21 20:45:10 +00:00
Update phpstan to 0.10.5 (#320)
This commit is contained in:
parent
d9b85e841f
commit
53c5a6b9e5
@ -19,6 +19,7 @@ This changelog references the relevant changes done in PHP-ML library.
|
|||||||
* fix SVM locale (non-locale aware) (#288)
|
* fix SVM locale (non-locale aware) (#288)
|
||||||
* typo, tests, code styles and documentation fixes (#265, #261, #254, #253, #251, #250, #248, #245, #243)
|
* typo, tests, code styles and documentation fixes (#265, #261, #254, #253, #251, #250, #248, #245, #243)
|
||||||
* change [MLPClassifier] return labels in output (#315)
|
* change [MLPClassifier] return labels in output (#315)
|
||||||
|
* enhancement Update phpstan to 0.10.5 (#320)
|
||||||
|
|
||||||
* 0.6.2 (2018-02-22)
|
* 0.6.2 (2018-02-22)
|
||||||
* Fix Apriori array keys (#238)
|
* Fix Apriori array keys (#238)
|
||||||
|
@ -24,9 +24,9 @@
|
|||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpbench/phpbench": "^0.14.0",
|
"phpbench/phpbench": "^0.14.0",
|
||||||
"phpstan/phpstan-phpunit": "^0.9.4",
|
"phpstan/phpstan-phpunit": "^0.10",
|
||||||
"phpstan/phpstan-shim": "^0.9",
|
"phpstan/phpstan-shim": "^0.10",
|
||||||
"phpstan/phpstan-strict-rules": "^0.9.0",
|
"phpstan/phpstan-strict-rules": "^0.10",
|
||||||
"phpunit/phpunit": "^7.0.0",
|
"phpunit/phpunit": "^7.0.0",
|
||||||
"symplify/coding-standard": "^5.1",
|
"symplify/coding-standard": "^5.1",
|
||||||
"symplify/easy-coding-standard": "^5.1"
|
"symplify/easy-coding-standard": "^5.1"
|
||||||
|
128
composer.lock
generated
128
composer.lock
generated
@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "cb4240c977f956be78a7fa686c77d0f2",
|
"content-hash": "9ec1ca6b843d05e0870bd777026d7a8b",
|
||||||
"packages": [],
|
"packages": [],
|
||||||
"packages-dev": [
|
"packages-dev": [
|
||||||
{
|
{
|
||||||
@ -1511,83 +1511,42 @@
|
|||||||
],
|
],
|
||||||
"time": "2018-08-05T17:53:17+00:00"
|
"time": "2018-08-05T17:53:17+00:00"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "phpstan/phpdoc-parser",
|
|
||||||
"version": "0.3",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/phpstan/phpdoc-parser.git",
|
|
||||||
"reference": "ed3223362174b8067729930439e139794e9e514a"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/ed3223362174b8067729930439e139794e9e514a",
|
|
||||||
"reference": "ed3223362174b8067729930439e139794e9e514a",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"php": "~7.1"
|
|
||||||
},
|
|
||||||
"require-dev": {
|
|
||||||
"consistence/coding-standard": "^2.0.0",
|
|
||||||
"jakub-onderka/php-parallel-lint": "^0.9.2",
|
|
||||||
"phing/phing": "^2.16.0",
|
|
||||||
"phpstan/phpstan": "^0.10@dev",
|
|
||||||
"phpunit/phpunit": "^6.3",
|
|
||||||
"slevomat/coding-standard": "^3.3.0",
|
|
||||||
"symfony/process": "^3.4 || ^4.0"
|
|
||||||
},
|
|
||||||
"type": "library",
|
|
||||||
"extra": {
|
|
||||||
"branch-alias": {
|
|
||||||
"dev-master": "0.3-dev"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"autoload": {
|
|
||||||
"psr-4": {
|
|
||||||
"PHPStan\\PhpDocParser\\": [
|
|
||||||
"src/"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"MIT"
|
|
||||||
],
|
|
||||||
"description": "PHPDoc parser with support for nullable, intersection and generic types",
|
|
||||||
"time": "2018-06-20T17:48:01+00:00"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "phpstan/phpstan-phpunit",
|
"name": "phpstan/phpstan-phpunit",
|
||||||
"version": "0.9.4",
|
"version": "0.10",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/phpstan/phpstan-phpunit.git",
|
"url": "https://github.com/phpstan/phpstan-phpunit.git",
|
||||||
"reference": "852411f841a37aeca2fa5af0002b0272c485c9bf"
|
"reference": "6feecc7faae187daa6be44140cd0f1ba210e6aa0"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/852411f841a37aeca2fa5af0002b0272c485c9bf",
|
"url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/6feecc7faae187daa6be44140cd0f1ba210e6aa0",
|
||||||
"reference": "852411f841a37aeca2fa5af0002b0272c485c9bf",
|
"reference": "6feecc7faae187daa6be44140cd0f1ba210e6aa0",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": "~7.0",
|
"nikic/php-parser": "^4.0",
|
||||||
"phpstan/phpstan": "^0.9.1",
|
"php": "~7.1",
|
||||||
"phpunit/phpunit": "^6.3 || ~7.0"
|
"phpstan/phpstan": "^0.10"
|
||||||
|
},
|
||||||
|
"conflict": {
|
||||||
|
"phpunit/phpunit": "<7.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"consistence/coding-standard": "^2.0",
|
"consistence/coding-standard": "^3.0.1",
|
||||||
"jakub-onderka/php-parallel-lint": "^0.9.2",
|
"dealerdirect/phpcodesniffer-composer-installer": "^0.4.4",
|
||||||
|
"jakub-onderka/php-parallel-lint": "^1.0",
|
||||||
"phing/phing": "^2.16.0",
|
"phing/phing": "^2.16.0",
|
||||||
"phpstan/phpstan-strict-rules": "^0.9",
|
"phpstan/phpstan-strict-rules": "^0.10",
|
||||||
|
"phpunit/phpunit": "^7.0",
|
||||||
"satooshi/php-coveralls": "^1.0",
|
"satooshi/php-coveralls": "^1.0",
|
||||||
"slevomat/coding-standard": "^3.3.0"
|
"slevomat/coding-standard": "^4.5.2"
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "0.9-dev"
|
"dev-master": "0.10-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
@ -1600,26 +1559,28 @@
|
|||||||
"MIT"
|
"MIT"
|
||||||
],
|
],
|
||||||
"description": "PHPUnit extensions and rules for PHPStan",
|
"description": "PHPUnit extensions and rules for PHPStan",
|
||||||
"time": "2018-02-02T09:45:47+00:00"
|
"time": "2018-06-22T18:12:17+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpstan/phpstan-shim",
|
"name": "phpstan/phpstan-shim",
|
||||||
"version": "0.9.2",
|
"version": "0.10.5",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/phpstan/phpstan-shim.git",
|
"url": "https://github.com/phpstan/phpstan-shim.git",
|
||||||
"reference": "e4720fb2916be05de02869780072253e7e0e8a75"
|
"reference": "a274185548d140a7f48cc1eed5b94f3a9068c674"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/phpstan/phpstan-shim/zipball/e4720fb2916be05de02869780072253e7e0e8a75",
|
"url": "https://api.github.com/repos/phpstan/phpstan-shim/zipball/a274185548d140a7f48cc1eed5b94f3a9068c674",
|
||||||
"reference": "e4720fb2916be05de02869780072253e7e0e8a75",
|
"reference": "a274185548d140a7f48cc1eed5b94f3a9068c674",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": "~7.0"
|
"php": "~7.1"
|
||||||
},
|
},
|
||||||
"replace": {
|
"replace": {
|
||||||
|
"nikic/php-parser": "^4.0.2",
|
||||||
|
"phpstan/phpdoc-parser": "^0.3",
|
||||||
"phpstan/phpstan": "self.version"
|
"phpstan/phpstan": "self.version"
|
||||||
},
|
},
|
||||||
"bin": [
|
"bin": [
|
||||||
@ -1629,46 +1590,53 @@
|
|||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "0.9-dev"
|
"dev-master": "0.10-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"bootstrap.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
"license": [
|
"license": [
|
||||||
"MIT"
|
"MIT"
|
||||||
],
|
],
|
||||||
"description": "PHPStan Phar distribution",
|
"description": "PHPStan Phar distribution",
|
||||||
"time": "2018-01-28T14:29:27+00:00"
|
"time": "2018-10-20T17:45:03+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpstan/phpstan-strict-rules",
|
"name": "phpstan/phpstan-strict-rules",
|
||||||
"version": "0.9",
|
"version": "0.10.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/phpstan/phpstan-strict-rules.git",
|
"url": "https://github.com/phpstan/phpstan-strict-rules.git",
|
||||||
"reference": "15be9090622c6b85c079922308f831018d8d9e23"
|
"reference": "18c0b6e8899606b127c680402ab473a7b67166db"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/15be9090622c6b85c079922308f831018d8d9e23",
|
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/18c0b6e8899606b127c680402ab473a7b67166db",
|
||||||
"reference": "15be9090622c6b85c079922308f831018d8d9e23",
|
"reference": "18c0b6e8899606b127c680402ab473a7b67166db",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": "~7.0",
|
"nikic/php-parser": "^4.0",
|
||||||
"phpstan/phpstan": "^0.9"
|
"php": "~7.1",
|
||||||
|
"phpstan/phpstan": "^0.10"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"consistence/coding-standard": "^2.0.0",
|
"consistence/coding-standard": "^3.0.1",
|
||||||
"jakub-onderka/php-parallel-lint": "^0.9.2",
|
"dealerdirect/phpcodesniffer-composer-installer": "^0.4.4",
|
||||||
|
"jakub-onderka/php-parallel-lint": "^1.0",
|
||||||
"phing/phing": "^2.16.0",
|
"phing/phing": "^2.16.0",
|
||||||
"phpstan/phpstan-phpunit": "^0.9",
|
"phpstan/phpstan-phpunit": "^0.10",
|
||||||
"phpunit/phpunit": "^6.4",
|
"phpunit/phpunit": "^7.0",
|
||||||
"slevomat/coding-standard": "^3.3.0"
|
"slevomat/coding-standard": "^4.5.2"
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "0.9-dev"
|
"dev-master": "0.10-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
@ -1681,7 +1649,7 @@
|
|||||||
"MIT"
|
"MIT"
|
||||||
],
|
],
|
||||||
"description": "Extra strict and opinionated rules for PHPStan",
|
"description": "Extra strict and opinionated rules for PHPStan",
|
||||||
"time": "2017-11-26T20:12:30+00:00"
|
"time": "2018-07-06T20:36:44+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpunit/php-code-coverage",
|
"name": "phpunit/php-code-coverage",
|
||||||
|
@ -2,14 +2,13 @@ includes:
|
|||||||
- vendor/phpstan/phpstan-strict-rules/rules.neon
|
- vendor/phpstan/phpstan-strict-rules/rules.neon
|
||||||
- vendor/phpstan/phpstan-phpunit/extension.neon
|
- vendor/phpstan/phpstan-phpunit/extension.neon
|
||||||
- vendor/phpstan/phpstan-phpunit/rules.neon
|
- vendor/phpstan/phpstan-phpunit/rules.neon
|
||||||
- vendor/phpstan/phpstan-phpunit/strictRules.neon
|
|
||||||
|
|
||||||
parameters:
|
parameters:
|
||||||
ignoreErrors:
|
ignoreErrors:
|
||||||
|
- '#Property Phpml\\Clustering\\KMeans\\Cluster\:\:\$points \(iterable\<Phpml\\Clustering\\KMeans\\Point\>\&SplObjectStorage\) does not accept SplObjectStorage#'
|
||||||
- '#Phpml\\Dataset\\FilesDataset::__construct\(\) does not call parent constructor from Phpml\\Dataset\\ArrayDataset#'
|
- '#Phpml\\Dataset\\FilesDataset::__construct\(\) does not call parent constructor from Phpml\\Dataset\\ArrayDataset#'
|
||||||
|
|
||||||
# wide range cases
|
# wide range cases
|
||||||
- '#Call to function count\(\) with argument type array<int>\|Phpml\\Clustering\\KMeans\\Point will always result in number 1#'
|
|
||||||
- '#Parameter \#1 \$coordinates of class Phpml\\Clustering\\KMeans\\Point constructor expects array, array<int>\|Phpml\\Clustering\\KMeans\\Point given#'
|
- '#Parameter \#1 \$coordinates of class Phpml\\Clustering\\KMeans\\Point constructor expects array, array<int>\|Phpml\\Clustering\\KMeans\\Point given#'
|
||||||
|
|
||||||
# probably known value
|
# probably known value
|
||||||
|
@ -64,11 +64,11 @@ class Apriori implements Associator
|
|||||||
*/
|
*/
|
||||||
public function getRules(): array
|
public function getRules(): array
|
||||||
{
|
{
|
||||||
if (empty($this->large)) {
|
if (count($this->large) === 0) {
|
||||||
$this->large = $this->apriori();
|
$this->large = $this->apriori();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($this->rules)) {
|
if (count($this->rules) > 0) {
|
||||||
return $this->rules;
|
return $this->rules;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,7 +89,7 @@ class Apriori implements Associator
|
|||||||
$L = [];
|
$L = [];
|
||||||
|
|
||||||
$items = $this->frequent($this->items());
|
$items = $this->frequent($this->items());
|
||||||
for ($k = 1; !empty($items); ++$k) {
|
for ($k = 1; isset($items[0]); ++$k) {
|
||||||
$L[$k] = $items;
|
$L[$k] = $items;
|
||||||
$items = $this->frequent($this->candidates($items));
|
$items = $this->frequent($this->candidates($items));
|
||||||
}
|
}
|
||||||
@ -118,7 +118,7 @@ class Apriori implements Associator
|
|||||||
*/
|
*/
|
||||||
private function generateAllRules(): void
|
private function generateAllRules(): void
|
||||||
{
|
{
|
||||||
for ($k = 2; !empty($this->large[$k]); ++$k) {
|
for ($k = 2; isset($this->large[$k]); ++$k) {
|
||||||
foreach ($this->large[$k] as $frequent) {
|
foreach ($this->large[$k] as $frequent) {
|
||||||
$this->generateRules($frequent);
|
$this->generateRules($frequent);
|
||||||
}
|
}
|
||||||
@ -241,7 +241,7 @@ class Apriori implements Associator
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ((array) $this->samples as $sample) {
|
foreach ($this->samples as $sample) {
|
||||||
if ($this->subset($sample, $candidate)) {
|
if ($this->subset($sample, $candidate)) {
|
||||||
$candidates[] = $candidate;
|
$candidates[] = $candidate;
|
||||||
|
|
||||||
@ -316,7 +316,7 @@ class Apriori implements Associator
|
|||||||
*/
|
*/
|
||||||
private function subset(array $set, array $subset): bool
|
private function subset(array $set, array $subset): bool
|
||||||
{
|
{
|
||||||
return !array_diff($subset, array_intersect($subset, $set));
|
return count(array_diff($subset, array_intersect($subset, $set))) === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -249,7 +249,7 @@ class DecisionTree implements Classifier
|
|||||||
foreach ($records as $recordNo) {
|
foreach ($records as $recordNo) {
|
||||||
// Check if the previous record is the same with the current one
|
// Check if the previous record is the same with the current one
|
||||||
$record = $this->samples[$recordNo];
|
$record = $this->samples[$recordNo];
|
||||||
if ($prevRecord && $prevRecord != $record) {
|
if ($prevRecord !== null && $prevRecord != $record) {
|
||||||
$allSame = false;
|
$allSame = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,13 +275,13 @@ class DecisionTree implements Classifier
|
|||||||
if ($allSame || $depth >= $this->maxDepth || count($remainingTargets) === 1) {
|
if ($allSame || $depth >= $this->maxDepth || count($remainingTargets) === 1) {
|
||||||
$split->isTerminal = true;
|
$split->isTerminal = true;
|
||||||
arsort($remainingTargets);
|
arsort($remainingTargets);
|
||||||
$split->classValue = key($remainingTargets);
|
$split->classValue = (string) key($remainingTargets);
|
||||||
} else {
|
} else {
|
||||||
if (!empty($leftRecords)) {
|
if (isset($leftRecords[0])) {
|
||||||
$split->leftLeaf = $this->getSplitLeaf($leftRecords, $depth + 1);
|
$split->leftLeaf = $this->getSplitLeaf($leftRecords, $depth + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($rightRecords)) {
|
if (isset($rightRecords[0])) {
|
||||||
$split->rightLeaf = $this->getSplitLeaf($rightRecords, $depth + 1);
|
$split->rightLeaf = $this->getSplitLeaf($rightRecords, $depth + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -292,8 +292,10 @@ class DecisionTree implements Classifier
|
|||||||
protected function getBestSplit(array $records): DecisionTreeLeaf
|
protected function getBestSplit(array $records): DecisionTreeLeaf
|
||||||
{
|
{
|
||||||
$targets = array_intersect_key($this->targets, array_flip($records));
|
$targets = array_intersect_key($this->targets, array_flip($records));
|
||||||
$samples = array_intersect_key($this->samples, array_flip($records));
|
$samples = (array) array_combine(
|
||||||
$samples = array_combine($records, $this->preprocess($samples));
|
$records,
|
||||||
|
$this->preprocess(array_intersect_key($this->samples, array_flip($records)))
|
||||||
|
);
|
||||||
$bestGiniVal = 1;
|
$bestGiniVal = 1;
|
||||||
$bestSplit = null;
|
$bestSplit = null;
|
||||||
$features = $this->getSelectedFeatures();
|
$features = $this->getSelectedFeatures();
|
||||||
@ -306,6 +308,10 @@ class DecisionTree implements Classifier
|
|||||||
$counts = array_count_values($colValues);
|
$counts = array_count_values($colValues);
|
||||||
arsort($counts);
|
arsort($counts);
|
||||||
$baseValue = key($counts);
|
$baseValue = key($counts);
|
||||||
|
if ($baseValue === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$gini = $this->getGiniIndex($baseValue, $colValues, $targets);
|
$gini = $this->getGiniIndex($baseValue, $colValues, $targets);
|
||||||
if ($bestSplit === null || $bestGiniVal > $gini) {
|
if ($bestSplit === null || $bestGiniVal > $gini) {
|
||||||
$split = new DecisionTreeLeaf();
|
$split = new DecisionTreeLeaf();
|
||||||
@ -349,11 +355,11 @@ class DecisionTree implements Classifier
|
|||||||
protected function getSelectedFeatures(): array
|
protected function getSelectedFeatures(): array
|
||||||
{
|
{
|
||||||
$allFeatures = range(0, $this->featureCount - 1);
|
$allFeatures = range(0, $this->featureCount - 1);
|
||||||
if ($this->numUsableFeatures === 0 && empty($this->selectedFeatures)) {
|
if ($this->numUsableFeatures === 0 && count($this->selectedFeatures) === 0) {
|
||||||
return $allFeatures;
|
return $allFeatures;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($this->selectedFeatures)) {
|
if (count($this->selectedFeatures) > 0) {
|
||||||
return $this->selectedFeatures;
|
return $this->selectedFeatures;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -406,7 +412,7 @@ class DecisionTree implements Classifier
|
|||||||
// all values in that column (Lower than or equal to %20 of all values)
|
// all values in that column (Lower than or equal to %20 of all values)
|
||||||
$numericValues = array_filter($columnValues, 'is_numeric');
|
$numericValues = array_filter($columnValues, 'is_numeric');
|
||||||
$floatValues = array_filter($columnValues, 'is_float');
|
$floatValues = array_filter($columnValues, 'is_float');
|
||||||
if (!empty($floatValues)) {
|
if (count($floatValues) > 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -463,7 +469,7 @@ class DecisionTree implements Classifier
|
|||||||
$node = $this->tree;
|
$node = $this->tree;
|
||||||
do {
|
do {
|
||||||
if ($node->isTerminal) {
|
if ($node->isTerminal) {
|
||||||
break;
|
return $node->classValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($node->evaluate($sample)) {
|
if ($node->evaluate($sample)) {
|
||||||
@ -473,6 +479,6 @@ class DecisionTree implements Classifier
|
|||||||
}
|
}
|
||||||
} while ($node);
|
} while ($node);
|
||||||
|
|
||||||
return $node !== null ? $node->classValue : $this->labels[0];
|
return $this->labels[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,7 +119,7 @@ class DecisionTreeLeaf
|
|||||||
/**
|
/**
|
||||||
* Returns HTML representation of the node including children nodes
|
* Returns HTML representation of the node including children nodes
|
||||||
*/
|
*/
|
||||||
public function getHTML($columnNames = null): string
|
public function getHTML(?array $columnNames = null): string
|
||||||
{
|
{
|
||||||
if ($this->isTerminal) {
|
if ($this->isTerminal) {
|
||||||
$value = "<b>${this}->classValue</b>";
|
$value = "<b>${this}->classValue</b>";
|
||||||
@ -131,7 +131,7 @@ class DecisionTreeLeaf
|
|||||||
$col = "col_$this->columnIndex";
|
$col = "col_$this->columnIndex";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!preg_match('/^[<>=]{1,2}/', (string) $value)) {
|
if ((bool) preg_match('/^[<>=]{1,2}/', (string) $value) === false) {
|
||||||
$value = "=${value}";
|
$value = "=${value}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,7 +100,7 @@ class AdaBoost implements Classifier
|
|||||||
{
|
{
|
||||||
// Initialize usual variables
|
// Initialize usual variables
|
||||||
$this->labels = array_keys(array_count_values($targets));
|
$this->labels = array_keys(array_count_values($targets));
|
||||||
if (count($this->labels) != 2) {
|
if (count($this->labels) !== 2) {
|
||||||
throw new InvalidArgumentException('AdaBoost is a binary classifier and can classify between two classes only');
|
throw new InvalidArgumentException('AdaBoost is a binary classifier and can classify between two classes only');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,13 +159,10 @@ class AdaBoost implements Classifier
|
|||||||
protected function getBestClassifier(): Classifier
|
protected function getBestClassifier(): Classifier
|
||||||
{
|
{
|
||||||
$ref = new ReflectionClass($this->baseClassifier);
|
$ref = new ReflectionClass($this->baseClassifier);
|
||||||
if (!empty($this->classifierOptions)) {
|
/** @var Classifier $classifier */
|
||||||
$classifier = $ref->newInstanceArgs($this->classifierOptions);
|
$classifier = count($this->classifierOptions) === 0 ? $ref->newInstance() : $ref->newInstanceArgs($this->classifierOptions);
|
||||||
} else {
|
|
||||||
$classifier = $ref->newInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_subclass_of($classifier, WeightedClassifier::class)) {
|
if ($classifier instanceof WeightedClassifier) {
|
||||||
$classifier->setSampleWeights($this->weights);
|
$classifier->setSampleWeights($this->weights);
|
||||||
$classifier->train($this->samples, $this->targets);
|
$classifier->train($this->samples, $this->targets);
|
||||||
} else {
|
} else {
|
||||||
|
@ -51,16 +51,6 @@ class Bagging implements Classifier
|
|||||||
*/
|
*/
|
||||||
protected $subsetRatio = 0.7;
|
protected $subsetRatio = 0.7;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private $targets = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
private $samples = [];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an ensemble classifier with given number of base classifiers
|
* Creates an ensemble classifier with given number of base classifiers
|
||||||
* Default number of base classifiers is 50.
|
* Default number of base classifiers is 50.
|
||||||
@ -146,11 +136,8 @@ class Bagging implements Classifier
|
|||||||
$classifiers = [];
|
$classifiers = [];
|
||||||
for ($i = 0; $i < $this->numClassifier; ++$i) {
|
for ($i = 0; $i < $this->numClassifier; ++$i) {
|
||||||
$ref = new ReflectionClass($this->classifier);
|
$ref = new ReflectionClass($this->classifier);
|
||||||
if (!empty($this->classifierOptions)) {
|
/** @var Classifier $obj */
|
||||||
$obj = $ref->newInstanceArgs($this->classifierOptions);
|
$obj = count($this->classifierOptions) === 0 ? $ref->newInstance() : $ref->newInstanceArgs($this->classifierOptions);
|
||||||
} else {
|
|
||||||
$obj = $ref->newInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
$classifiers[] = $this->initSingleClassifier($obj);
|
$classifiers[] = $this->initSingleClassifier($obj);
|
||||||
}
|
}
|
||||||
|
@ -45,8 +45,7 @@ class KNearestNeighbors implements Classifier
|
|||||||
protected function predictSample(array $sample)
|
protected function predictSample(array $sample)
|
||||||
{
|
{
|
||||||
$distances = $this->kNeighborsDistances($sample);
|
$distances = $this->kNeighborsDistances($sample);
|
||||||
|
$predictions = (array) array_combine(array_values($this->targets), array_fill(0, count($this->targets), 0));
|
||||||
$predictions = array_combine(array_values($this->targets), array_fill(0, count($this->targets), 0));
|
|
||||||
|
|
||||||
foreach (array_keys($distances) as $index) {
|
foreach (array_keys($distances) as $index) {
|
||||||
++$predictions[$this->targets[$index]];
|
++$predictions[$this->targets[$index]];
|
||||||
|
@ -55,7 +55,7 @@ class Adaline extends Perceptron
|
|||||||
* Adapts the weights with respect to given samples and targets
|
* Adapts the weights with respect to given samples and targets
|
||||||
* by use of gradient descent learning rule
|
* by use of gradient descent learning rule
|
||||||
*/
|
*/
|
||||||
protected function runTraining(array $samples, array $targets)
|
protected function runTraining(array $samples, array $targets): void
|
||||||
{
|
{
|
||||||
// The cost function is the sum of squares
|
// The cost function is the sum of squares
|
||||||
$callback = function ($weights, $sample, $target) {
|
$callback = function ($weights, $sample, $target) {
|
||||||
@ -70,6 +70,6 @@ class Adaline extends Perceptron
|
|||||||
|
|
||||||
$isBatch = $this->trainingType == self::BATCH_TRAINING;
|
$isBatch = $this->trainingType == self::BATCH_TRAINING;
|
||||||
|
|
||||||
return parent::runGradientDescent($samples, $targets, $callback, $isBatch);
|
parent::runGradientDescent($samples, $targets, $callback, $isBatch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,13 +119,13 @@ class DecisionStump extends WeightedClassifier
|
|||||||
|
|
||||||
// Check the size of the weights given.
|
// Check the size of the weights given.
|
||||||
// If none given, then assign 1 as a weight to each sample
|
// If none given, then assign 1 as a weight to each sample
|
||||||
if (!empty($this->weights)) {
|
if (count($this->weights) === 0) {
|
||||||
|
$this->weights = array_fill(0, count($samples), 1);
|
||||||
|
} else {
|
||||||
$numWeights = count($this->weights);
|
$numWeights = count($this->weights);
|
||||||
if ($numWeights != count($samples)) {
|
if ($numWeights !== count($samples)) {
|
||||||
throw new InvalidArgumentException('Number of sample weights does not match with number of samples');
|
throw new InvalidArgumentException('Number of sample weights does not match with number of samples');
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
$this->weights = array_fill(0, count($samples), 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine type of each column as either "continuous" or "nominal"
|
// Determine type of each column as either "continuous" or "nominal"
|
||||||
@ -134,7 +134,7 @@ class DecisionStump extends WeightedClassifier
|
|||||||
// Try to find the best split in the columns of the dataset
|
// Try to find the best split in the columns of the dataset
|
||||||
// by calculating error rate for each split point in each column
|
// by calculating error rate for each split point in each column
|
||||||
$columns = range(0, count($samples[0]) - 1);
|
$columns = range(0, count($samples[0]) - 1);
|
||||||
if ($this->givenColumnIndex != self::AUTO_SELECT) {
|
if ($this->givenColumnIndex !== self::AUTO_SELECT) {
|
||||||
$columns = [$this->givenColumnIndex];
|
$columns = [$this->givenColumnIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,7 +184,7 @@ class DecisionStump extends WeightedClassifier
|
|||||||
// the average value for the cut point
|
// the average value for the cut point
|
||||||
$threshold = array_sum($values) / (float) count($values);
|
$threshold = array_sum($values) / (float) count($values);
|
||||||
[$errorRate, $prob] = $this->calculateErrorRate($targets, $threshold, $operator, $values);
|
[$errorRate, $prob] = $this->calculateErrorRate($targets, $threshold, $operator, $values);
|
||||||
if ($split === [] || $errorRate < $split['trainingErrorRate']) {
|
if (!isset($split['trainingErrorRate']) || $errorRate < $split['trainingErrorRate']) {
|
||||||
$split = [
|
$split = [
|
||||||
'value' => $threshold,
|
'value' => $threshold,
|
||||||
'operator' => $operator,
|
'operator' => $operator,
|
||||||
@ -224,8 +224,7 @@ class DecisionStump extends WeightedClassifier
|
|||||||
foreach (['=', '!='] as $operator) {
|
foreach (['=', '!='] as $operator) {
|
||||||
foreach ($distinctVals as $val) {
|
foreach ($distinctVals as $val) {
|
||||||
[$errorRate, $prob] = $this->calculateErrorRate($targets, $val, $operator, $values);
|
[$errorRate, $prob] = $this->calculateErrorRate($targets, $val, $operator, $values);
|
||||||
|
if (!isset($split['trainingErrorRate']) || $split['trainingErrorRate'] < $errorRate) {
|
||||||
if ($split === [] || $split['trainingErrorRate'] < $errorRate) {
|
|
||||||
$split = [
|
$split = [
|
||||||
'value' => $val,
|
'value' => $val,
|
||||||
'operator' => $operator,
|
'operator' => $operator,
|
||||||
|
@ -60,11 +60,6 @@ class Perceptron implements Classifier, IncrementalEstimator
|
|||||||
*/
|
*/
|
||||||
protected $enableEarlyStop = true;
|
protected $enableEarlyStop = true;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array
|
|
||||||
*/
|
|
||||||
protected $costValues = [];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initalize a perceptron classifier with given learning rate and maximum
|
* Initalize a perceptron classifier with given learning rate and maximum
|
||||||
* number of iterations used while training the perceptron
|
* number of iterations used while training the perceptron
|
||||||
@ -156,7 +151,7 @@ class Perceptron implements Classifier, IncrementalEstimator
|
|||||||
* Trains the perceptron model with Stochastic Gradient Descent optimization
|
* Trains the perceptron model with Stochastic Gradient Descent optimization
|
||||||
* to get the correct set of weights
|
* to get the correct set of weights
|
||||||
*/
|
*/
|
||||||
protected function runTraining(array $samples, array $targets)
|
protected function runTraining(array $samples, array $targets): void
|
||||||
{
|
{
|
||||||
// The cost function is the sum of squares
|
// The cost function is the sum of squares
|
||||||
$callback = function ($weights, $sample, $target) {
|
$callback = function ($weights, $sample, $target) {
|
||||||
@ -176,7 +171,7 @@ class Perceptron implements Classifier, IncrementalEstimator
|
|||||||
* Executes a Gradient Descent algorithm for
|
* Executes a Gradient Descent algorithm for
|
||||||
* the given cost function
|
* the given cost function
|
||||||
*/
|
*/
|
||||||
protected function runGradientDescent(array $samples, array $targets, Closure $gradientFunc, bool $isBatch = false)
|
protected function runGradientDescent(array $samples, array $targets, Closure $gradientFunc, bool $isBatch = false): void
|
||||||
{
|
{
|
||||||
$class = $isBatch ? GD::class : StochasticGD::class;
|
$class = $isBatch ? GD::class : StochasticGD::class;
|
||||||
|
|
||||||
|
@ -108,8 +108,7 @@ class FuzzyCMeans implements Clusterer
|
|||||||
$column = array_column($this->membership, $k);
|
$column = array_column($this->membership, $k);
|
||||||
arsort($column);
|
arsort($column);
|
||||||
reset($column);
|
reset($column);
|
||||||
$i = key($column);
|
$cluster = $this->clusters[key($column)];
|
||||||
$cluster = $this->clusters[$i];
|
|
||||||
$cluster->attach(new Point($this->samples[$k]));
|
$cluster->attach(new Point($this->samples[$k]));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,7 +151,7 @@ class FuzzyCMeans implements Clusterer
|
|||||||
protected function updateClusters(): void
|
protected function updateClusters(): void
|
||||||
{
|
{
|
||||||
$dim = $this->space->getDimension();
|
$dim = $this->space->getDimension();
|
||||||
if (empty($this->clusters)) {
|
if (count($this->clusters) === 0) {
|
||||||
for ($i = 0; $i < $this->clustersNumber; ++$i) {
|
for ($i = 0; $i < $this->clustersNumber; ++$i) {
|
||||||
$this->clusters[] = new Cluster($this->space, array_fill(0, $dim, 0.0));
|
$this->clusters[] = new Cluster($this->space, array_fill(0, $dim, 0.0));
|
||||||
}
|
}
|
||||||
@ -171,11 +170,11 @@ class FuzzyCMeans implements Clusterer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getMembershipRowTotal(int $row, int $col, bool $multiply)
|
protected function getMembershipRowTotal(int $row, int $col, bool $multiply): float
|
||||||
{
|
{
|
||||||
$sum = 0.0;
|
$sum = 0.0;
|
||||||
for ($k = 0; $k < $this->sampleCount; ++$k) {
|
for ($k = 0; $k < $this->sampleCount; ++$k) {
|
||||||
$val = pow($this->membership[$row][$k], $this->fuzziness);
|
$val = $this->membership[$row][$k] ** $this->fuzziness;
|
||||||
if ($multiply) {
|
if ($multiply) {
|
||||||
$val *= $this->samples[$k][$col];
|
$val *= $this->samples[$k][$col];
|
||||||
}
|
}
|
||||||
@ -211,7 +210,7 @@ class FuzzyCMeans implements Clusterer
|
|||||||
$this->samples[$col]
|
$this->samples[$col]
|
||||||
);
|
);
|
||||||
|
|
||||||
$val = pow($dist1 / $dist2, 2.0 / ($this->fuzziness - 1));
|
$val = ($dist1 / $dist2) ** 2.0 / ($this->fuzziness - 1);
|
||||||
$sum += $val;
|
$sum += $val;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,7 +222,7 @@ class FuzzyCMeans implements Clusterer
|
|||||||
* and all cluster centers. This method returns the summation of all
|
* and all cluster centers. This method returns the summation of all
|
||||||
* these distances
|
* these distances
|
||||||
*/
|
*/
|
||||||
protected function getObjective()
|
protected function getObjective(): float
|
||||||
{
|
{
|
||||||
$sum = 0.0;
|
$sum = 0.0;
|
||||||
$distance = new Euclidean();
|
$distance = new Euclidean();
|
||||||
|
@ -4,12 +4,11 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Phpml\Clustering\KMeans;
|
namespace Phpml\Clustering\KMeans;
|
||||||
|
|
||||||
use Countable;
|
|
||||||
use IteratorAggregate;
|
use IteratorAggregate;
|
||||||
use LogicException;
|
use LogicException;
|
||||||
use SplObjectStorage;
|
use SplObjectStorage;
|
||||||
|
|
||||||
class Cluster extends Point implements IteratorAggregate, Countable
|
class Cluster extends Point implements IteratorAggregate
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var Space
|
* @var Space
|
||||||
@ -32,10 +31,10 @@ class Cluster extends Point implements IteratorAggregate, Countable
|
|||||||
{
|
{
|
||||||
$points = [];
|
$points = [];
|
||||||
foreach ($this->points as $point) {
|
foreach ($this->points as $point) {
|
||||||
if (!empty($point->label)) {
|
if (count($point->label) === 0) {
|
||||||
$points[$point->label] = $point->toArray();
|
|
||||||
} else {
|
|
||||||
$points[] = $point->toArray();
|
$points[] = $point->toArray();
|
||||||
|
} else {
|
||||||
|
$points[$point->label] = $point->toArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,10 +105,7 @@ class Cluster extends Point implements IteratorAggregate, Countable
|
|||||||
return $this->points;
|
return $this->points;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function count(): int
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function count()
|
|
||||||
{
|
{
|
||||||
return count($this->points);
|
return count($this->points);
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ namespace Phpml\Clustering\KMeans;
|
|||||||
|
|
||||||
use ArrayAccess;
|
use ArrayAccess;
|
||||||
|
|
||||||
class Point implements ArrayAccess
|
class Point implements ArrayAccess, \Countable
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var int
|
* @var int
|
||||||
@ -23,6 +23,9 @@ class Point implements ArrayAccess
|
|||||||
*/
|
*/
|
||||||
protected $label;
|
protected $label;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $label
|
||||||
|
*/
|
||||||
public function __construct(array $coordinates, $label = null)
|
public function __construct(array $coordinates, $label = null)
|
||||||
{
|
{
|
||||||
$this->dimension = count($coordinates);
|
$this->dimension = count($coordinates);
|
||||||
@ -36,7 +39,7 @@ class Point implements ArrayAccess
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return int|mixed
|
* @return float|int
|
||||||
*/
|
*/
|
||||||
public function getDistanceWith(self $point, bool $precise = true)
|
public function getDistanceWith(self $point, bool $precise = true)
|
||||||
{
|
{
|
||||||
@ -50,9 +53,9 @@ class Point implements ArrayAccess
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return mixed
|
* @param Point[] $points
|
||||||
*/
|
*/
|
||||||
public function getClosest(array $points)
|
public function getClosest(array $points): ?self
|
||||||
{
|
{
|
||||||
$minPoint = null;
|
$minPoint = null;
|
||||||
|
|
||||||
@ -114,4 +117,9 @@ class Point implements ArrayAccess
|
|||||||
{
|
{
|
||||||
unset($this->coordinates[$offset]);
|
unset($this->coordinates[$offset]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function count(): int
|
||||||
|
{
|
||||||
|
return count($this->coordinates);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,8 @@ class Space extends SplObjectStorage
|
|||||||
public function toArray(): array
|
public function toArray(): array
|
||||||
{
|
{
|
||||||
$points = [];
|
$points = [];
|
||||||
|
|
||||||
|
/** @var Point $point */
|
||||||
foreach ($this as $point) {
|
foreach ($this as $point) {
|
||||||
$points[] = $point->toArray();
|
$points[] = $point->toArray();
|
||||||
}
|
}
|
||||||
@ -35,9 +37,12 @@ class Space extends SplObjectStorage
|
|||||||
return ['points' => $points];
|
return ['points' => $points];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $label
|
||||||
|
*/
|
||||||
public function newPoint(array $coordinates, $label = null): Point
|
public function newPoint(array $coordinates, $label = null): Point
|
||||||
{
|
{
|
||||||
if (count($coordinates) != $this->dimension) {
|
if (count($coordinates) !== $this->dimension) {
|
||||||
throw new LogicException('('.implode(',', $coordinates).') is not a point of this space');
|
throw new LogicException('('.implode(',', $coordinates).') is not a point of this space');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,7 +50,8 @@ class Space extends SplObjectStorage
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param null $data
|
* @param mixed $label
|
||||||
|
* @param mixed $data
|
||||||
*/
|
*/
|
||||||
public function addPoint(array $coordinates, $label = null, $data = null): void
|
public function addPoint(array $coordinates, $label = null, $data = null): void
|
||||||
{
|
{
|
||||||
@ -53,8 +59,8 @@ class Space extends SplObjectStorage
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Point $point
|
* @param object $point
|
||||||
* @param null $data
|
* @param mixed $data
|
||||||
*/
|
*/
|
||||||
public function attach($point, $data = null): void
|
public function attach($point, $data = null): void
|
||||||
{
|
{
|
||||||
@ -82,10 +88,16 @@ class Space extends SplObjectStorage
|
|||||||
$min = $this->newPoint(array_fill(0, $this->dimension, null));
|
$min = $this->newPoint(array_fill(0, $this->dimension, null));
|
||||||
$max = $this->newPoint(array_fill(0, $this->dimension, null));
|
$max = $this->newPoint(array_fill(0, $this->dimension, null));
|
||||||
|
|
||||||
|
/** @var self $point */
|
||||||
foreach ($this as $point) {
|
foreach ($this as $point) {
|
||||||
for ($n = 0; $n < $this->dimension; ++$n) {
|
for ($n = 0; $n < $this->dimension; ++$n) {
|
||||||
($min[$n] > $point[$n] || $min[$n] === null) && $min[$n] = $point[$n];
|
if ($min[$n] === null || $min[$n] > $point[$n]) {
|
||||||
($max[$n] < $point[$n] || $max[$n] === null) && $max[$n] = $point[$n];
|
$min[$n] = $point[$n];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($max[$n] === null || $max[$n] < $point[$n]) {
|
||||||
|
$max[$n] = $point[$n];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,7 +153,10 @@ class Space extends SplObjectStorage
|
|||||||
return $clusters;
|
return $clusters;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function iterate($clusters): bool
|
/**
|
||||||
|
* @param Cluster[] $clusters
|
||||||
|
*/
|
||||||
|
protected function iterate(array $clusters): bool
|
||||||
{
|
{
|
||||||
$convergence = true;
|
$convergence = true;
|
||||||
|
|
||||||
@ -164,10 +179,12 @@ class Space extends SplObjectStorage
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @var Cluster $cluster */
|
||||||
foreach ($attach as $cluster) {
|
foreach ($attach as $cluster) {
|
||||||
$cluster->attachAll($attach[$cluster]);
|
$cluster->attachAll($attach[$cluster]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @var Cluster $cluster */
|
||||||
foreach ($detach as $cluster) {
|
foreach ($detach as $cluster) {
|
||||||
$cluster->detachAll($detach[$cluster]);
|
$cluster->detachAll($detach[$cluster]);
|
||||||
}
|
}
|
||||||
@ -179,23 +196,36 @@ class Space extends SplObjectStorage
|
|||||||
return $convergence;
|
return $convergence;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Cluster[]
|
||||||
|
*/
|
||||||
protected function initializeKMPPClusters(int $clustersNumber): array
|
protected function initializeKMPPClusters(int $clustersNumber): array
|
||||||
{
|
{
|
||||||
$clusters = [];
|
$clusters = [];
|
||||||
$this->rewind();
|
$this->rewind();
|
||||||
|
|
||||||
$clusters[] = new Cluster($this, $this->current()->getCoordinates());
|
/** @var Point $current */
|
||||||
|
$current = $this->current();
|
||||||
|
|
||||||
|
$clusters[] = new Cluster($this, $current->getCoordinates());
|
||||||
|
|
||||||
$distances = new SplObjectStorage();
|
$distances = new SplObjectStorage();
|
||||||
|
|
||||||
for ($i = 1; $i < $clustersNumber; ++$i) {
|
for ($i = 1; $i < $clustersNumber; ++$i) {
|
||||||
$sum = 0;
|
$sum = 0;
|
||||||
|
/** @var Point $point */
|
||||||
foreach ($this as $point) {
|
foreach ($this as $point) {
|
||||||
$distance = $point->getDistanceWith($point->getClosest($clusters));
|
$closest = $point->getClosest($clusters);
|
||||||
|
if ($closest === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$distance = $point->getDistanceWith($closest);
|
||||||
$sum += $distances[$point] = $distance;
|
$sum += $distances[$point] = $distance;
|
||||||
}
|
}
|
||||||
|
|
||||||
$sum = random_int(0, (int) $sum);
|
$sum = random_int(0, (int) $sum);
|
||||||
|
/** @var Point $point */
|
||||||
foreach ($this as $point) {
|
foreach ($this as $point) {
|
||||||
$sum -= $distances[$point];
|
$sum -= $distances[$point];
|
||||||
|
|
||||||
@ -212,6 +242,9 @@ class Space extends SplObjectStorage
|
|||||||
return $clusters;
|
return $clusters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Cluster[]
|
||||||
|
*/
|
||||||
private function initializeRandomClusters(int $clustersNumber): array
|
private function initializeRandomClusters(int $clustersNumber): array
|
||||||
{
|
{
|
||||||
$clusters = [];
|
$clusters = [];
|
||||||
|
@ -60,7 +60,7 @@ abstract class Split
|
|||||||
return $this->testLabels;
|
return $this->testLabels;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract protected function splitDataset(Dataset $dataset, float $testSize);
|
abstract protected function splitDataset(Dataset $dataset, float $testSize): void;
|
||||||
|
|
||||||
protected function seedGenerator(?int $seed = null): void
|
protected function seedGenerator(?int $seed = null): void
|
||||||
{
|
{
|
||||||
|
@ -27,6 +27,7 @@ class StratifiedRandomSplit extends RandomSplit
|
|||||||
$samples = $dataset->getSamples();
|
$samples = $dataset->getSamples();
|
||||||
|
|
||||||
$uniqueTargets = array_unique($targets);
|
$uniqueTargets = array_unique($targets);
|
||||||
|
/** @var array $split */
|
||||||
$split = array_combine($uniqueTargets, array_fill(0, count($uniqueTargets), []));
|
$split = array_combine($uniqueTargets, array_fill(0, count($uniqueTargets), []));
|
||||||
|
|
||||||
foreach ($samples as $key => $sample) {
|
foreach ($samples as $key => $sample) {
|
||||||
|
@ -29,14 +29,14 @@ class CsvDataset extends ArrayDataset
|
|||||||
|
|
||||||
if ($headingRow) {
|
if ($headingRow) {
|
||||||
$data = fgetcsv($handle, $maxLineLength, $delimiter);
|
$data = fgetcsv($handle, $maxLineLength, $delimiter);
|
||||||
$this->columnNames = array_slice($data, 0, $features);
|
$this->columnNames = array_slice((array) $data, 0, $features);
|
||||||
} else {
|
} else {
|
||||||
$this->columnNames = range(0, $features - 1);
|
$this->columnNames = range(0, $features - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
$samples = $targets = [];
|
$samples = $targets = [];
|
||||||
while (($data = fgetcsv($handle, $maxLineLength, $delimiter)) !== false) {
|
while (($data = fgetcsv($handle, $maxLineLength, $delimiter)) !== false) {
|
||||||
$samples[] = array_slice($data, 0, $features);
|
$samples[] = array_slice((array) $data, 0, $features);
|
||||||
$targets[] = $data[$features];
|
$targets[] = $data[$features];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,8 +23,8 @@ class SvmDataset extends ArrayDataset
|
|||||||
$samples = [];
|
$samples = [];
|
||||||
$targets = [];
|
$targets = [];
|
||||||
$maxIndex = 0;
|
$maxIndex = 0;
|
||||||
while (($line = fgets($handle)) !== false) {
|
while (false !== $line = fgets($handle)) {
|
||||||
[$sample, $target, $maxIndex] = self::processLine($line, $maxIndex);
|
[$sample, $target, $maxIndex] = self::processLine((string) $line, $maxIndex);
|
||||||
$samples[] = $sample;
|
$samples[] = $sample;
|
||||||
$targets[] = $target;
|
$targets[] = $target;
|
||||||
}
|
}
|
||||||
@ -38,6 +38,9 @@ class SvmDataset extends ArrayDataset
|
|||||||
return [$samples, $targets];
|
return [$samples, $targets];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return resource
|
||||||
|
*/
|
||||||
private static function openFile(string $filePath)
|
private static function openFile(string $filePath)
|
||||||
{
|
{
|
||||||
if (!file_exists($filePath)) {
|
if (!file_exists($filePath)) {
|
||||||
|
@ -5,7 +5,6 @@ declare(strict_types=1);
|
|||||||
namespace Phpml\DimensionReduction;
|
namespace Phpml\DimensionReduction;
|
||||||
|
|
||||||
use Closure;
|
use Closure;
|
||||||
use Exception;
|
|
||||||
use Phpml\Exception\InvalidArgumentException;
|
use Phpml\Exception\InvalidArgumentException;
|
||||||
use Phpml\Exception\InvalidOperationException;
|
use Phpml\Exception\InvalidOperationException;
|
||||||
use Phpml\Math\Distance\Euclidean;
|
use Phpml\Math\Distance\Euclidean;
|
||||||
@ -59,8 +58,7 @@ class KernelPCA extends PCA
|
|||||||
*/
|
*/
|
||||||
public function __construct(int $kernel = self::KERNEL_RBF, ?float $totalVariance = null, ?int $numFeatures = null, ?float $gamma = null)
|
public function __construct(int $kernel = self::KERNEL_RBF, ?float $totalVariance = null, ?int $numFeatures = null, ?float $gamma = null)
|
||||||
{
|
{
|
||||||
$availableKernels = [self::KERNEL_RBF, self::KERNEL_SIGMOID, self::KERNEL_LAPLACIAN, self::KERNEL_LINEAR];
|
if (!in_array($kernel, [self::KERNEL_RBF, self::KERNEL_SIGMOID, self::KERNEL_LAPLACIAN, self::KERNEL_LINEAR], true)) {
|
||||||
if (!in_array($kernel, $availableKernels, true)) {
|
|
||||||
throw new InvalidArgumentException('KernelPCA can be initialized with the following kernels only: Linear, RBF, Sigmoid and Laplacian');
|
throw new InvalidArgumentException('KernelPCA can be initialized with the following kernels only: Linear, RBF, Sigmoid and Laplacian');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,7 +188,7 @@ class KernelPCA extends PCA
|
|||||||
return function ($x, $y) {
|
return function ($x, $y) {
|
||||||
$res = Matrix::dot($x, $y)[0] + 1.0;
|
$res = Matrix::dot($x, $y)[0] + 1.0;
|
||||||
|
|
||||||
return tanh($this->gamma * $res);
|
return tanh((float) $this->gamma * $res);
|
||||||
};
|
};
|
||||||
|
|
||||||
case self::KERNEL_LAPLACIAN:
|
case self::KERNEL_LAPLACIAN:
|
||||||
@ -203,7 +201,7 @@ class KernelPCA extends PCA
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
// Not reached
|
// Not reached
|
||||||
throw new Exception(sprintf('KernelPCA initialized with invalid kernel: %d', $this->kernel));
|
throw new InvalidArgumentException(sprintf('KernelPCA initialized with invalid kernel: %d', $this->kernel));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@ class PCA extends EigenTransformerBase
|
|||||||
*/
|
*/
|
||||||
protected function normalize(array $data, int $n): array
|
protected function normalize(array $data, int $n): array
|
||||||
{
|
{
|
||||||
if (empty($this->means)) {
|
if (count($this->means) === 0) {
|
||||||
$this->calculateMeans($data, $n);
|
$this->calculateMeans($data, $n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ namespace Phpml;
|
|||||||
|
|
||||||
interface Estimator
|
interface Estimator
|
||||||
{
|
{
|
||||||
public function train(array $samples, array $targets);
|
public function train(array $samples, array $targets): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return mixed
|
* @return mixed
|
||||||
|
@ -15,7 +15,7 @@ class TfIdfTransformer implements Transformer
|
|||||||
|
|
||||||
public function __construct(array $samples = [])
|
public function __construct(array $samples = [])
|
||||||
{
|
{
|
||||||
if (!empty($samples)) {
|
if (count($samples) > 0) {
|
||||||
$this->fit($samples);
|
$this->fit($samples);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ final class SelectKBest implements Transformer
|
|||||||
|
|
||||||
public function fit(array $samples, ?array $targets = null): void
|
public function fit(array $samples, ?array $targets = null): void
|
||||||
{
|
{
|
||||||
if ($targets === null || empty($targets)) {
|
if ($targets === null || count($targets) === 0) {
|
||||||
throw new InvalidArgumentException('The array has zero elements');
|
throw new InvalidArgumentException('The array has zero elements');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,18 +51,13 @@ trait OneVsRest
|
|||||||
protected function trainByLabel(array $samples, array $targets, array $allLabels = []): void
|
protected function trainByLabel(array $samples, array $targets, array $allLabels = []): void
|
||||||
{
|
{
|
||||||
// Overwrites the current value if it exist. $allLabels must be provided for each partialTrain run.
|
// Overwrites the current value if it exist. $allLabels must be provided for each partialTrain run.
|
||||||
if (!empty($allLabels)) {
|
$this->allLabels = count($allLabels) === 0 ? array_keys(array_count_values($targets)) : $allLabels;
|
||||||
$this->allLabels = $allLabels;
|
|
||||||
} else {
|
|
||||||
$this->allLabels = array_keys(array_count_values($targets));
|
|
||||||
}
|
|
||||||
|
|
||||||
sort($this->allLabels, SORT_STRING);
|
sort($this->allLabels, SORT_STRING);
|
||||||
|
|
||||||
// If there are only two targets, then there is no need to perform OvR
|
// If there are only two targets, then there is no need to perform OvR
|
||||||
if (count($this->allLabels) == 2) {
|
if (count($this->allLabels) === 2) {
|
||||||
// Init classifier if required.
|
// Init classifier if required.
|
||||||
if (empty($this->classifiers)) {
|
if (count($this->classifiers) === 0) {
|
||||||
$this->classifiers[0] = $this->getClassifierCopy();
|
$this->classifiers[0] = $this->getClassifierCopy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,7 +67,7 @@ trait OneVsRest
|
|||||||
|
|
||||||
foreach ($this->allLabels as $label) {
|
foreach ($this->allLabels as $label) {
|
||||||
// Init classifier if required.
|
// Init classifier if required.
|
||||||
if (empty($this->classifiers[$label])) {
|
if (!isset($this->classifiers[$label])) {
|
||||||
$this->classifiers[$label] = $this->getClassifierCopy();
|
$this->classifiers[$label] = $this->getClassifierCopy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,10 +87,8 @@ trait OneVsRest
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an instance of the current class after cleaning up OneVsRest stuff.
|
* Returns an instance of the current class after cleaning up OneVsRest stuff.
|
||||||
*
|
|
||||||
* @return Classifier|OneVsRest
|
|
||||||
*/
|
*/
|
||||||
protected function getClassifierCopy()
|
protected function getClassifierCopy(): Classifier
|
||||||
{
|
{
|
||||||
// Clone the current classifier, so that
|
// Clone the current classifier, so that
|
||||||
// we don't mess up its variables while training
|
// we don't mess up its variables while training
|
||||||
@ -111,7 +104,7 @@ trait OneVsRest
|
|||||||
*/
|
*/
|
||||||
protected function predictSample(array $sample)
|
protected function predictSample(array $sample)
|
||||||
{
|
{
|
||||||
if (count($this->allLabels) == 2) {
|
if (count($this->allLabels) === 2) {
|
||||||
return $this->classifiers[0]->predictSampleBinary($sample);
|
return $this->classifiers[0]->predictSampleBinary($sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ class ConjugateGradient extends GD
|
|||||||
{
|
{
|
||||||
[$cost] = parent::gradient($theta);
|
[$cost] = parent::gradient($theta);
|
||||||
|
|
||||||
return array_sum($cost) / $this->sampleCount;
|
return array_sum($cost) / (int) $this->sampleCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||||||
namespace Phpml\Helper\Optimizer;
|
namespace Phpml\Helper\Optimizer;
|
||||||
|
|
||||||
use Closure;
|
use Closure;
|
||||||
|
use Phpml\Exception\InvalidOperationException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Batch version of Gradient Descent to optimize the weights
|
* Batch version of Gradient Descent to optimize the weights
|
||||||
@ -59,6 +60,10 @@ class GD extends StochasticGD
|
|||||||
$gradient = [];
|
$gradient = [];
|
||||||
$totalPenalty = 0;
|
$totalPenalty = 0;
|
||||||
|
|
||||||
|
if ($this->gradientCb === null) {
|
||||||
|
throw new InvalidOperationException('Gradient callback is not defined');
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($this->samples as $index => $sample) {
|
foreach ($this->samples as $index => $sample) {
|
||||||
$target = $this->targets[$index];
|
$target = $this->targets[$index];
|
||||||
|
|
||||||
|
@ -37,9 +37,9 @@ abstract class Optimizer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setTheta(array $theta)
|
public function setTheta(array $theta): self
|
||||||
{
|
{
|
||||||
if (count($theta) != $this->dimensions) {
|
if (count($theta) !== $this->dimensions) {
|
||||||
throw new InvalidArgumentException(sprintf('Number of values in the weights array should be %s', $this->dimensions));
|
throw new InvalidArgumentException(sprintf('Number of values in the weights array should be %s', $this->dimensions));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,5 +52,5 @@ abstract class Optimizer
|
|||||||
* Executes the optimization with the given samples & targets
|
* Executes the optimization with the given samples & targets
|
||||||
* and returns the weights
|
* and returns the weights
|
||||||
*/
|
*/
|
||||||
abstract public function runOptimization(array $samples, array $targets, Closure $gradientCb);
|
abstract public function runOptimization(array $samples, array $targets, Closure $gradientCb): array;
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ namespace Phpml\Helper\Optimizer;
|
|||||||
|
|
||||||
use Closure;
|
use Closure;
|
||||||
use Phpml\Exception\InvalidArgumentException;
|
use Phpml\Exception\InvalidArgumentException;
|
||||||
|
use Phpml\Exception\InvalidOperationException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stochastic Gradient Descent optimization method
|
* Stochastic Gradient Descent optimization method
|
||||||
@ -34,7 +35,7 @@ class StochasticGD extends Optimizer
|
|||||||
*
|
*
|
||||||
* @var \Closure|null
|
* @var \Closure|null
|
||||||
*/
|
*/
|
||||||
protected $gradientCb = null;
|
protected $gradientCb;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximum number of iterations used to train the model
|
* Maximum number of iterations used to train the model
|
||||||
@ -89,9 +90,9 @@ class StochasticGD extends Optimizer
|
|||||||
$this->dimensions = $dimensions;
|
$this->dimensions = $dimensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setTheta(array $theta)
|
public function setTheta(array $theta): Optimizer
|
||||||
{
|
{
|
||||||
if (count($theta) != $this->dimensions + 1) {
|
if (count($theta) !== $this->dimensions + 1) {
|
||||||
throw new InvalidArgumentException(sprintf('Number of values in the weights array should be %s', $this->dimensions + 1));
|
throw new InvalidArgumentException(sprintf('Number of values in the weights array should be %s', $this->dimensions + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,7 +157,7 @@ class StochasticGD extends Optimizer
|
|||||||
* The cost function to minimize and the gradient of the function are to be
|
* The cost function to minimize and the gradient of the function are to be
|
||||||
* handled by the callback function provided as the third parameter of the method.
|
* handled by the callback function provided as the third parameter of the method.
|
||||||
*/
|
*/
|
||||||
public function runOptimization(array $samples, array $targets, Closure $gradientCb): ?array
|
public function runOptimization(array $samples, array $targets, Closure $gradientCb): array
|
||||||
{
|
{
|
||||||
$this->samples = $samples;
|
$this->samples = $samples;
|
||||||
$this->targets = $targets;
|
$this->targets = $targets;
|
||||||
@ -175,7 +176,7 @@ class StochasticGD extends Optimizer
|
|||||||
|
|
||||||
// Save the best theta in the "pocket" so that
|
// Save the best theta in the "pocket" so that
|
||||||
// any future set of theta worse than this will be disregarded
|
// any future set of theta worse than this will be disregarded
|
||||||
if ($bestTheta == null || $cost <= $bestScore) {
|
if ($bestTheta === null || $cost <= $bestScore) {
|
||||||
$bestTheta = $theta;
|
$bestTheta = $theta;
|
||||||
$bestScore = $cost;
|
$bestScore = $cost;
|
||||||
}
|
}
|
||||||
@ -210,6 +211,10 @@ class StochasticGD extends Optimizer
|
|||||||
$jValue = 0.0;
|
$jValue = 0.0;
|
||||||
$theta = $this->theta;
|
$theta = $this->theta;
|
||||||
|
|
||||||
|
if ($this->gradientCb === null) {
|
||||||
|
throw new InvalidOperationException('Gradient callback is not defined');
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($this->samples as $index => $sample) {
|
foreach ($this->samples as $index => $sample) {
|
||||||
$target = $this->targets[$index];
|
$target = $this->targets[$index];
|
||||||
|
|
||||||
@ -254,7 +259,7 @@ class StochasticGD extends Optimizer
|
|||||||
|
|
||||||
// Check if the last two cost values are almost the same
|
// Check if the last two cost values are almost the same
|
||||||
$costs = array_slice($this->costValues, -2);
|
$costs = array_slice($this->costValues, -2);
|
||||||
if (count($costs) == 2 && abs($costs[1] - $costs[0]) < $this->threshold) {
|
if (count($costs) === 2 && abs($costs[1] - $costs[0]) < $this->threshold) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,5 +6,5 @@ namespace Phpml;
|
|||||||
|
|
||||||
interface IncrementalEstimator
|
interface IncrementalEstimator
|
||||||
{
|
{
|
||||||
public function partialTrain(array $samples, array $targets, array $labels = []);
|
public function partialTrain(array $samples, array $targets, array $labels = []): void;
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,9 @@ use Phpml\Exception\InvalidArgumentException;
|
|||||||
class Comparison
|
class Comparison
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
* @param mixed $a
|
||||||
|
* @param mixed $b
|
||||||
|
*
|
||||||
* @throws InvalidArgumentException
|
* @throws InvalidArgumentException
|
||||||
*/
|
*/
|
||||||
public static function compare($a, $b, string $operator): bool
|
public static function compare($a, $b, string $operator): bool
|
||||||
|
@ -3,25 +3,25 @@
|
|||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class to obtain eigenvalues and eigenvectors of a real matrix.
|
* Class to obtain eigenvalues and eigenvectors of a real matrix.
|
||||||
*
|
*
|
||||||
* If A is symmetric, then A = V*D*V' where the eigenvalue matrix D
|
* If A is symmetric, then A = V*D*V' where the eigenvalue matrix D
|
||||||
* is diagonal and the eigenvector matrix V is orthogonal (i.e.
|
* is diagonal and the eigenvector matrix V is orthogonal (i.e.
|
||||||
* A = V.times(D.times(V.transpose())) and V.times(V.transpose())
|
* A = V.times(D.times(V.transpose())) and V.times(V.transpose())
|
||||||
* equals the identity matrix).
|
* equals the identity matrix).
|
||||||
*
|
*
|
||||||
* If A is not symmetric, then the eigenvalue matrix D is block diagonal
|
* If A is not symmetric, then the eigenvalue matrix D is block diagonal
|
||||||
* with the real eigenvalues in 1-by-1 blocks and any complex eigenvalues,
|
* with the real eigenvalues in 1-by-1 blocks and any complex eigenvalues,
|
||||||
* lambda + i*mu, in 2-by-2 blocks, [lambda, mu; -mu, lambda]. The
|
* lambda + i*mu, in 2-by-2 blocks, [lambda, mu; -mu, lambda]. The
|
||||||
* columns of V represent the eigenvectors in the sense that A*V = V*D,
|
* columns of V represent the eigenvectors in the sense that A*V = V*D,
|
||||||
* i.e. A.times(V) equals V.times(D). The matrix V may be badly
|
* i.e. A.times(V) equals V.times(D). The matrix V may be badly
|
||||||
* conditioned, or even singular, so the validity of the equation
|
* conditioned, or even singular, so the validity of the equation
|
||||||
* A = V*D*inverse(V) depends upon V.cond().
|
* A = V*D*inverse(V) depends upon V.cond().
|
||||||
*
|
*
|
||||||
* @author Paul Meagher
|
* @author Paul Meagher
|
||||||
* @license PHP v3.0
|
* @license PHP v3.0
|
||||||
*
|
*
|
||||||
* @version 1.1
|
* @version 1.1
|
||||||
*
|
*
|
||||||
* Slightly changed to adapt the original code to PHP-ML library
|
* Slightly changed to adapt the original code to PHP-ML library
|
||||||
* @date 2017/04/11
|
* @date 2017/04/11
|
||||||
@ -36,83 +36,79 @@ use Phpml\Math\Matrix;
|
|||||||
class EigenvalueDecomposition
|
class EigenvalueDecomposition
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Row and column dimension (square matrix).
|
* Row and column dimension (square matrix).
|
||||||
*
|
*
|
||||||
* @var int
|
* @var int
|
||||||
*/
|
*/
|
||||||
private $n;
|
private $n;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal symmetry flag.
|
* Arrays for internal storage of eigenvalues.
|
||||||
*
|
*
|
||||||
* @var bool
|
* @var array
|
||||||
*/
|
|
||||||
private $symmetric;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Arrays for internal storage of eigenvalues.
|
|
||||||
*
|
|
||||||
* @var array
|
|
||||||
*/
|
*/
|
||||||
private $d = [];
|
private $d = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
private $e = [];
|
private $e = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Array for internal storage of eigenvectors.
|
* Array for internal storage of eigenvectors.
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
private $V = [];
|
private $V = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Array for internal storage of nonsymmetric Hessenberg form.
|
* Array for internal storage of nonsymmetric Hessenberg form.
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
private $H = [];
|
private $H = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Working storage for nonsymmetric algorithm.
|
* Working storage for nonsymmetric algorithm.
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
private $ort = [];
|
private $ort = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used for complex scalar division.
|
* Used for complex scalar division.
|
||||||
*
|
*
|
||||||
* @var float
|
* @var float
|
||||||
*/
|
*/
|
||||||
private $cdivr;
|
private $cdivr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var float
|
||||||
|
*/
|
||||||
private $cdivi;
|
private $cdivi;
|
||||||
|
|
||||||
private $A;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor: Check for symmetry, then construct the eigenvalue decomposition
|
* Constructor: Check for symmetry, then construct the eigenvalue decomposition
|
||||||
*/
|
*/
|
||||||
public function __construct(array $Arg)
|
public function __construct(array $arg)
|
||||||
{
|
{
|
||||||
$this->A = $Arg;
|
$this->n = count($arg[0]);
|
||||||
$this->n = count($Arg[0]);
|
$symmetric = true;
|
||||||
$this->symmetric = true;
|
|
||||||
|
|
||||||
for ($j = 0; ($j < $this->n) & $this->symmetric; ++$j) {
|
for ($j = 0; ($j < $this->n) & $symmetric; ++$j) {
|
||||||
for ($i = 0; ($i < $this->n) & $this->symmetric; ++$i) {
|
for ($i = 0; ($i < $this->n) & $symmetric; ++$i) {
|
||||||
$this->symmetric = ($this->A[$i][$j] == $this->A[$j][$i]);
|
$symmetric = $arg[$i][$j] == $arg[$j][$i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->symmetric) {
|
if ($symmetric) {
|
||||||
$this->V = $this->A;
|
$this->V = $arg;
|
||||||
// Tridiagonalize.
|
// Tridiagonalize.
|
||||||
$this->tred2();
|
$this->tred2();
|
||||||
// Diagonalize.
|
// Diagonalize.
|
||||||
$this->tql2();
|
$this->tql2();
|
||||||
} else {
|
} else {
|
||||||
$this->H = $this->A;
|
$this->H = $arg;
|
||||||
$this->ort = [];
|
$this->ort = [];
|
||||||
// Reduce to Hessenberg form.
|
// Reduce to Hessenberg form.
|
||||||
$this->orthes();
|
$this->orthes();
|
||||||
@ -148,7 +144,7 @@ class EigenvalueDecomposition
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the real parts of the eigenvalues<br>
|
* Return the real parts of the eigenvalues<br>
|
||||||
* d = real(diag(D));
|
* d = real(diag(D));
|
||||||
*/
|
*/
|
||||||
public function getRealEigenvalues(): array
|
public function getRealEigenvalues(): array
|
||||||
@ -157,7 +153,7 @@ class EigenvalueDecomposition
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the imaginary parts of the eigenvalues <br>
|
* Return the imaginary parts of the eigenvalues <br>
|
||||||
* d = imag(diag(D))
|
* d = imag(diag(D))
|
||||||
*/
|
*/
|
||||||
public function getImagEigenvalues(): array
|
public function getImagEigenvalues(): array
|
||||||
@ -166,7 +162,7 @@ class EigenvalueDecomposition
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the block diagonal eigenvalue matrix
|
* Return the block diagonal eigenvalue matrix
|
||||||
*/
|
*/
|
||||||
public function getDiagonalEigenvalues(): array
|
public function getDiagonalEigenvalues(): array
|
||||||
{
|
{
|
||||||
@ -187,7 +183,7 @@ class EigenvalueDecomposition
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Symmetric Householder reduction to tridiagonal form.
|
* Symmetric Householder reduction to tridiagonal form.
|
||||||
*/
|
*/
|
||||||
private function tred2(): void
|
private function tred2(): void
|
||||||
{
|
{
|
||||||
@ -308,12 +304,12 @@ class EigenvalueDecomposition
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Symmetric tridiagonal QL algorithm.
|
* Symmetric tridiagonal QL algorithm.
|
||||||
*
|
*
|
||||||
* This is derived from the Algol procedures tql2, by
|
* This is derived from the Algol procedures tql2, by
|
||||||
* Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
|
* Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
|
||||||
* Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
|
* Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
|
||||||
* Fortran subroutine in EISPACK.
|
* Fortran subroutine in EISPACK.
|
||||||
*/
|
*/
|
||||||
private function tql2(): void
|
private function tql2(): void
|
||||||
{
|
{
|
||||||
@ -341,10 +337,7 @@ class EigenvalueDecomposition
|
|||||||
// If m == l, $this->d[l] is an eigenvalue,
|
// If m == l, $this->d[l] is an eigenvalue,
|
||||||
// otherwise, iterate.
|
// otherwise, iterate.
|
||||||
if ($m > $l) {
|
if ($m > $l) {
|
||||||
$iter = 0;
|
|
||||||
do {
|
do {
|
||||||
// Could check iteration count here.
|
|
||||||
++$iter;
|
|
||||||
// Compute implicit shift
|
// Compute implicit shift
|
||||||
$g = $this->d[$l];
|
$g = $this->d[$l];
|
||||||
$p = ($this->d[$l + 1] - $g) / (2.0 * $this->e[$l]);
|
$p = ($this->d[$l + 1] - $g) / (2.0 * $this->e[$l]);
|
||||||
@ -423,12 +416,12 @@ class EigenvalueDecomposition
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Nonsymmetric reduction to Hessenberg form.
|
* Nonsymmetric reduction to Hessenberg form.
|
||||||
*
|
*
|
||||||
* This is derived from the Algol procedures orthes and ortran,
|
* This is derived from the Algol procedures orthes and ortran,
|
||||||
* by Martin and Wilkinson, Handbook for Auto. Comp.,
|
* by Martin and Wilkinson, Handbook for Auto. Comp.,
|
||||||
* Vol.ii-Linear Algebra, and the corresponding
|
* Vol.ii-Linear Algebra, and the corresponding
|
||||||
* Fortran subroutines in EISPACK.
|
* Fortran subroutines in EISPACK.
|
||||||
*/
|
*/
|
||||||
private function orthes(): void
|
private function orthes(): void
|
||||||
{
|
{
|
||||||
@ -541,12 +534,12 @@ class EigenvalueDecomposition
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Nonsymmetric reduction from Hessenberg to real Schur form.
|
* Nonsymmetric reduction from Hessenberg to real Schur form.
|
||||||
*
|
*
|
||||||
* Code is derived from the Algol procedure hqr2,
|
* Code is derived from the Algol procedure hqr2,
|
||||||
* by Martin and Wilkinson, Handbook for Auto. Comp.,
|
* by Martin and Wilkinson, Handbook for Auto. Comp.,
|
||||||
* Vol.ii-Linear Algebra, and the corresponding
|
* Vol.ii-Linear Algebra, and the corresponding
|
||||||
* Fortran subroutine in EISPACK.
|
* Fortran subroutine in EISPACK.
|
||||||
*/
|
*/
|
||||||
private function hqr2(): void
|
private function hqr2(): void
|
||||||
{
|
{
|
||||||
@ -911,7 +904,7 @@ class EigenvalueDecomposition
|
|||||||
$y = $this->H[$i + 1][$i];
|
$y = $this->H[$i + 1][$i];
|
||||||
$vr = ($this->d[$i] - $p) * ($this->d[$i] - $p) + $this->e[$i] * $this->e[$i] - $q * $q;
|
$vr = ($this->d[$i] - $p) * ($this->d[$i] - $p) + $this->e[$i] * $this->e[$i] - $q * $q;
|
||||||
$vi = ($this->d[$i] - $p) * 2.0 * $q;
|
$vi = ($this->d[$i] - $p) * 2.0 * $q;
|
||||||
if ($vr == 0.0 & $vi == 0.0) {
|
if ($vr == 0.0 && $vi == 0.0) {
|
||||||
$vr = $eps * $norm * (abs($w) + abs($q) + abs($x) + abs($y) + abs($z));
|
$vr = $eps * $norm * (abs($w) + abs($q) + abs($x) + abs($y) + abs($z));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -943,7 +936,7 @@ class EigenvalueDecomposition
|
|||||||
|
|
||||||
// Vectors of isolated roots
|
// Vectors of isolated roots
|
||||||
for ($i = 0; $i < $nn; ++$i) {
|
for ($i = 0; $i < $nn; ++$i) {
|
||||||
if ($i < $low | $i > $high) {
|
if ($i < $low || $i > $high) {
|
||||||
for ($j = $i; $j < $nn; ++$j) {
|
for ($j = $i; $j < $nn; ++$j) {
|
||||||
$this->V[$i][$j] = $this->H[$i][$j];
|
$this->V[$i][$j] = $this->H[$i][$j];
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@ class LUDecomposition
|
|||||||
*/
|
*/
|
||||||
public function __construct(Matrix $A)
|
public function __construct(Matrix $A)
|
||||||
{
|
{
|
||||||
if ($A->getRows() != $A->getColumns()) {
|
if ($A->getRows() !== $A->getColumns()) {
|
||||||
throw new MatrixException('Matrix is not square matrix');
|
throw new MatrixException('Matrix is not square matrix');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,7 +118,7 @@ class LUDecomposition
|
|||||||
// Find pivot and exchange if necessary.
|
// Find pivot and exchange if necessary.
|
||||||
$p = $j;
|
$p = $j;
|
||||||
for ($i = $j + 1; $i < $this->m; ++$i) {
|
for ($i = $j + 1; $i < $this->m; ++$i) {
|
||||||
if (abs($LUcolj[$i]) > abs($LUcolj[$p])) {
|
if (abs($LUcolj[$i] ?? 0) > abs($LUcolj[$p] ?? 0)) {
|
||||||
$p = $i;
|
$p = $i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -204,7 +204,7 @@ class LUDecomposition
|
|||||||
*
|
*
|
||||||
* @see getPivot
|
* @see getPivot
|
||||||
*/
|
*/
|
||||||
public function getDoublePivot()
|
public function getDoublePivot(): array
|
||||||
{
|
{
|
||||||
return $this->getPivot();
|
return $this->getPivot();
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,7 @@ class Matrix
|
|||||||
/**
|
/**
|
||||||
* @throws MatrixException
|
* @throws MatrixException
|
||||||
*/
|
*/
|
||||||
public function getColumnValues($column): array
|
public function getColumnValues(int $column): array
|
||||||
{
|
{
|
||||||
if ($column >= $this->columns) {
|
if ($column >= $this->columns) {
|
||||||
throw new MatrixException('Column out of range');
|
throw new MatrixException('Column out of range');
|
||||||
@ -125,7 +125,7 @@ class Matrix
|
|||||||
|
|
||||||
public function transpose(): self
|
public function transpose(): self
|
||||||
{
|
{
|
||||||
if ($this->rows == 1) {
|
if ($this->rows === 1) {
|
||||||
$matrix = array_map(function ($el) {
|
$matrix = array_map(function ($el) {
|
||||||
return [$el];
|
return [$el];
|
||||||
}, $this->matrix[0]);
|
}, $this->matrix[0]);
|
||||||
@ -138,7 +138,7 @@ class Matrix
|
|||||||
|
|
||||||
public function multiply(self $matrix): self
|
public function multiply(self $matrix): self
|
||||||
{
|
{
|
||||||
if ($this->columns != $matrix->getRows()) {
|
if ($this->columns !== $matrix->getRows()) {
|
||||||
throw new InvalidArgumentException('Inconsistent matrix supplied');
|
throw new InvalidArgumentException('Inconsistent matrix supplied');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,6 +166,9 @@ class Matrix
|
|||||||
return new self($product, false);
|
return new self($product, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param float|int $value
|
||||||
|
*/
|
||||||
public function divideByScalar($value): self
|
public function divideByScalar($value): self
|
||||||
{
|
{
|
||||||
$newMatrix = [];
|
$newMatrix = [];
|
||||||
@ -178,6 +181,9 @@ class Matrix
|
|||||||
return new self($newMatrix, false);
|
return new self($newMatrix, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param float|int $value
|
||||||
|
*/
|
||||||
public function multiplyByScalar($value): self
|
public function multiplyByScalar($value): self
|
||||||
{
|
{
|
||||||
$newMatrix = [];
|
$newMatrix = [];
|
||||||
|
@ -14,7 +14,7 @@ class Product
|
|||||||
$product = 0;
|
$product = 0;
|
||||||
foreach ($a as $index => $value) {
|
foreach ($a as $index => $value) {
|
||||||
if (is_numeric($value) && is_numeric($b[$index])) {
|
if (is_numeric($value) && is_numeric($b[$index])) {
|
||||||
$product += $value * $b[$index];
|
$product += (float) $value * (float) $b[$index];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,7 +131,7 @@ class Set implements IteratorAggregate
|
|||||||
*/
|
*/
|
||||||
public function containsAll(array $elements): bool
|
public function containsAll(array $elements): bool
|
||||||
{
|
{
|
||||||
return !array_diff($elements, $this->elements);
|
return count(array_diff($elements, $this->elements)) === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -149,7 +149,7 @@ class Set implements IteratorAggregate
|
|||||||
|
|
||||||
public function isEmpty(): bool
|
public function isEmpty(): bool
|
||||||
{
|
{
|
||||||
return $this->cardinality() == 0;
|
return $this->cardinality() === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function cardinality(): int
|
public function cardinality(): int
|
||||||
|
@ -31,7 +31,7 @@ final class ANOVA
|
|||||||
$samplesPerClass = array_map(function (array $class): int {
|
$samplesPerClass = array_map(function (array $class): int {
|
||||||
return count($class);
|
return count($class);
|
||||||
}, $samples);
|
}, $samples);
|
||||||
$allSamples = array_sum($samplesPerClass);
|
$allSamples = (int) array_sum($samplesPerClass);
|
||||||
$ssAllSamples = self::sumOfSquaresPerFeature($samples);
|
$ssAllSamples = self::sumOfSquaresPerFeature($samples);
|
||||||
$sumSamples = self::sumOfFeaturesPerClass($samples);
|
$sumSamples = self::sumOfFeaturesPerClass($samples);
|
||||||
$squareSumSamples = self::sumOfSquares($sumSamples);
|
$squareSumSamples = self::sumOfSquares($sumSamples);
|
||||||
|
@ -15,11 +15,11 @@ class Covariance
|
|||||||
*/
|
*/
|
||||||
public static function fromXYArrays(array $x, array $y, bool $sample = true, ?float $meanX = null, ?float $meanY = null): float
|
public static function fromXYArrays(array $x, array $y, bool $sample = true, ?float $meanX = null, ?float $meanY = null): float
|
||||||
{
|
{
|
||||||
if (empty($x) || empty($y)) {
|
$n = count($x);
|
||||||
|
if ($n === 0 || count($y) === 0) {
|
||||||
throw new InvalidArgumentException('The array has zero elements');
|
throw new InvalidArgumentException('The array has zero elements');
|
||||||
}
|
}
|
||||||
|
|
||||||
$n = count($x);
|
|
||||||
if ($sample && $n === 1) {
|
if ($sample && $n === 1) {
|
||||||
throw new InvalidArgumentException('The array must have at least 2 elements');
|
throw new InvalidArgumentException('The array must have at least 2 elements');
|
||||||
}
|
}
|
||||||
@ -53,7 +53,7 @@ class Covariance
|
|||||||
*/
|
*/
|
||||||
public static function fromDataset(array $data, int $i, int $k, bool $sample = true, ?float $meanX = null, ?float $meanY = null): float
|
public static function fromDataset(array $data, int $i, int $k, bool $sample = true, ?float $meanX = null, ?float $meanY = null): float
|
||||||
{
|
{
|
||||||
if (empty($data)) {
|
if (count($data) === 0) {
|
||||||
throw new InvalidArgumentException('The array has zero elements');
|
throw new InvalidArgumentException('The array has zero elements');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,7 +87,7 @@ class Covariance
|
|||||||
// with a slight cost of CPU utilization.
|
// with a slight cost of CPU utilization.
|
||||||
$sum = 0.0;
|
$sum = 0.0;
|
||||||
foreach ($data as $row) {
|
foreach ($data as $row) {
|
||||||
$val = [];
|
$val = [0, 0];
|
||||||
foreach ($row as $index => $col) {
|
foreach ($row as $index => $col) {
|
||||||
if ($index == $i) {
|
if ($index == $i) {
|
||||||
$val[0] = $col - $meanX;
|
$val[0] = $col - $meanX;
|
||||||
|
@ -58,7 +58,7 @@ class Mean
|
|||||||
*/
|
*/
|
||||||
private static function checkArrayLength(array $array): void
|
private static function checkArrayLength(array $array): void
|
||||||
{
|
{
|
||||||
if (empty($array)) {
|
if (count($array) === 0) {
|
||||||
throw new InvalidArgumentException('The array has zero elements');
|
throw new InvalidArgumentException('The array has zero elements');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,12 +13,11 @@ class StandardDeviation
|
|||||||
*/
|
*/
|
||||||
public static function population(array $numbers, bool $sample = true): float
|
public static function population(array $numbers, bool $sample = true): float
|
||||||
{
|
{
|
||||||
if (empty($numbers)) {
|
$n = count($numbers);
|
||||||
|
if ($n === 0) {
|
||||||
throw new InvalidArgumentException('The array has zero elements');
|
throw new InvalidArgumentException('The array has zero elements');
|
||||||
}
|
}
|
||||||
|
|
||||||
$n = count($numbers);
|
|
||||||
|
|
||||||
if ($sample && $n === 1) {
|
if ($sample && $n === 1) {
|
||||||
throw new InvalidArgumentException('The array must have at least 2 elements');
|
throw new InvalidArgumentException('The array must have at least 2 elements');
|
||||||
}
|
}
|
||||||
@ -33,7 +32,7 @@ class StandardDeviation
|
|||||||
--$n;
|
--$n;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sqrt((float) ($carry / $n));
|
return sqrt($carry / $n);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -44,7 +43,7 @@ class StandardDeviation
|
|||||||
*/
|
*/
|
||||||
public static function sumOfSquares(array $numbers): float
|
public static function sumOfSquares(array $numbers): float
|
||||||
{
|
{
|
||||||
if (empty($numbers)) {
|
if (count($numbers) === 0) {
|
||||||
throw new InvalidArgumentException('The array has zero elements');
|
throw new InvalidArgumentException('The array has zero elements');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,9 +142,9 @@ class ClassificationReport
|
|||||||
|
|
||||||
private function computeMicroAverage(): void
|
private function computeMicroAverage(): void
|
||||||
{
|
{
|
||||||
$truePositive = array_sum($this->truePositive);
|
$truePositive = (int) array_sum($this->truePositive);
|
||||||
$falsePositive = array_sum($this->falsePositive);
|
$falsePositive = (int) array_sum($this->falsePositive);
|
||||||
$falseNegative = array_sum($this->falseNegative);
|
$falseNegative = (int) array_sum($this->falseNegative);
|
||||||
|
|
||||||
$precision = $this->computePrecision($truePositive, $falsePositive);
|
$precision = $this->computePrecision($truePositive, $falsePositive);
|
||||||
$recall = $this->computeRecall($truePositive, $falseNegative);
|
$recall = $this->computeRecall($truePositive, $falseNegative);
|
||||||
@ -227,6 +227,6 @@ class ClassificationReport
|
|||||||
$labels = array_values(array_unique(array_merge($actualLabels, $predictedLabels)));
|
$labels = array_values(array_unique(array_merge($actualLabels, $predictedLabels)));
|
||||||
sort($labels);
|
sort($labels);
|
||||||
|
|
||||||
return array_combine($labels, array_fill(0, count($labels), 0));
|
return (array) array_combine($labels, array_fill(0, count($labels), 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,13 +8,13 @@ class ConfusionMatrix
|
|||||||
{
|
{
|
||||||
public static function compute(array $actualLabels, array $predictedLabels, array $labels = []): array
|
public static function compute(array $actualLabels, array $predictedLabels, array $labels = []): array
|
||||||
{
|
{
|
||||||
$labels = !empty($labels) ? array_flip($labels) : self::getUniqueLabels($actualLabels);
|
$labels = count($labels) === 0 ? self::getUniqueLabels($actualLabels) : array_flip($labels);
|
||||||
$matrix = self::generateMatrixWithZeros($labels);
|
$matrix = self::generateMatrixWithZeros($labels);
|
||||||
|
|
||||||
foreach ($actualLabels as $index => $actual) {
|
foreach ($actualLabels as $index => $actual) {
|
||||||
$predicted = $predictedLabels[$index];
|
$predicted = $predictedLabels[$index];
|
||||||
|
|
||||||
if (!isset($labels[$actual]) || !isset($labels[$predicted])) {
|
if (!isset($labels[$actual], $labels[$predicted])) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ class ModelManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
$serialized = serialize($estimator);
|
$serialized = serialize($estimator);
|
||||||
if (empty($serialized)) {
|
if (!isset($serialized[0])) {
|
||||||
throw new SerializeException(sprintf('Class "%s" can not be serialized.', gettype($estimator)));
|
throw new SerializeException(sprintf('Class "%s" can not be serialized.', gettype($estimator)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ class ModelManager
|
|||||||
throw new FileException(sprintf('File "%s" can\'t be open.', basename($filepath)));
|
throw new FileException(sprintf('File "%s" can\'t be open.', basename($filepath)));
|
||||||
}
|
}
|
||||||
|
|
||||||
$object = unserialize(file_get_contents($filepath));
|
$object = unserialize((string) file_get_contents($filepath), [Estimator::class]);
|
||||||
if ($object === false) {
|
if ($object === false) {
|
||||||
throw new SerializeException(sprintf('"%s" can not be unserialized.', basename($filepath)));
|
throw new SerializeException(sprintf('"%s" can not be unserialized.', basename($filepath)));
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ interface Network
|
|||||||
|
|
||||||
public function getOutput(): array;
|
public function getOutput(): array;
|
||||||
|
|
||||||
public function addLayer(Layer $layer);
|
public function addLayer(Layer $layer): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Layer[]
|
* @return Layer[]
|
||||||
|
@ -61,7 +61,7 @@ abstract class MultilayerPerceptron extends LayeredNetwork implements Estimator,
|
|||||||
*/
|
*/
|
||||||
public function __construct(int $inputLayerFeatures, array $hiddenLayers, array $classes, int $iterations = 10000, ?ActivationFunction $activationFunction = null, float $learningRate = 1)
|
public function __construct(int $inputLayerFeatures, array $hiddenLayers, array $classes, int $iterations = 10000, ?ActivationFunction $activationFunction = null, float $learningRate = 1)
|
||||||
{
|
{
|
||||||
if (empty($hiddenLayers)) {
|
if (count($hiddenLayers) === 0) {
|
||||||
throw new InvalidArgumentException('Provide at least 1 hidden layer');
|
throw new InvalidArgumentException('Provide at least 1 hidden layer');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,7 +95,7 @@ abstract class MultilayerPerceptron extends LayeredNetwork implements Estimator,
|
|||||||
*/
|
*/
|
||||||
public function partialTrain(array $samples, array $targets, array $classes = []): void
|
public function partialTrain(array $samples, array $targets, array $classes = []): void
|
||||||
{
|
{
|
||||||
if (!empty($classes) && array_values($classes) !== $this->classes) {
|
if (count($classes) > 0 && array_values($classes) !== $this->classes) {
|
||||||
// We require the list of classes in the constructor.
|
// We require the list of classes in the constructor.
|
||||||
throw new InvalidArgumentException(
|
throw new InvalidArgumentException(
|
||||||
'The provided classes don\'t match the classes provided in the constructor'
|
'The provided classes don\'t match the classes provided in the constructor'
|
||||||
@ -126,7 +126,7 @@ abstract class MultilayerPerceptron extends LayeredNetwork implements Estimator,
|
|||||||
/**
|
/**
|
||||||
* @param mixed $target
|
* @param mixed $target
|
||||||
*/
|
*/
|
||||||
abstract protected function trainSample(array $sample, $target);
|
abstract protected function trainSample(array $sample, $target): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return mixed
|
* @return mixed
|
||||||
|
@ -49,6 +49,6 @@ class Synapse
|
|||||||
|
|
||||||
protected function generateRandomWeight(): float
|
protected function generateRandomWeight(): float
|
||||||
{
|
{
|
||||||
return 1 / random_int(5, 25) * (random_int(0, 1) ? -1 : 1);
|
return (1 / random_int(5, 25) * random_int(0, 1)) > 0 ? -1 : 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace Phpml\NeuralNetwork;
|
|
||||||
|
|
||||||
interface Training
|
|
||||||
{
|
|
||||||
public function train(array $samples, array $targets);
|
|
||||||
}
|
|
@ -26,7 +26,7 @@ class DataTransformer
|
|||||||
|
|
||||||
public static function testSet(array $samples): string
|
public static function testSet(array $samples): string
|
||||||
{
|
{
|
||||||
if (empty($samples)) {
|
if (count($samples) === 0) {
|
||||||
throw new InvalidArgumentException('The array has zero elements');
|
throw new InvalidArgumentException('The array has zero elements');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,7 +62,7 @@ class DataTransformer
|
|||||||
$predictions = explode(PHP_EOL, trim($rawPredictions));
|
$predictions = explode(PHP_EOL, trim($rawPredictions));
|
||||||
|
|
||||||
$header = array_shift($predictions);
|
$header = array_shift($predictions);
|
||||||
$headerColumns = explode(' ', $header);
|
$headerColumns = explode(' ', (string) $header);
|
||||||
array_shift($headerColumns);
|
array_shift($headerColumns);
|
||||||
|
|
||||||
$columnLabels = [];
|
$columnLabels = [];
|
||||||
|
@ -165,7 +165,7 @@ class SupportVectorMachine
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->model = file_get_contents($modelFileName);
|
$this->model = (string) file_get_contents($modelFileName);
|
||||||
|
|
||||||
unlink($modelFileName);
|
unlink($modelFileName);
|
||||||
}
|
}
|
||||||
@ -241,7 +241,7 @@ class SupportVectorMachine
|
|||||||
|
|
||||||
unlink($testSetFileName);
|
unlink($testSetFileName);
|
||||||
unlink($modelFileName);
|
unlink($modelFileName);
|
||||||
$predictions = file_get_contents($outputFileName);
|
$predictions = (string) file_get_contents($outputFileName);
|
||||||
|
|
||||||
unlink($outputFileName);
|
unlink($outputFileName);
|
||||||
|
|
||||||
|
@ -4,10 +4,17 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Phpml\Tokenization;
|
namespace Phpml\Tokenization;
|
||||||
|
|
||||||
|
use Phpml\Exception\InvalidArgumentException;
|
||||||
|
|
||||||
class WhitespaceTokenizer implements Tokenizer
|
class WhitespaceTokenizer implements Tokenizer
|
||||||
{
|
{
|
||||||
public function tokenize(string $text): array
|
public function tokenize(string $text): array
|
||||||
{
|
{
|
||||||
return preg_split('/[\pZ\pC]+/u', $text, -1, PREG_SPLIT_NO_EMPTY);
|
$substrings = preg_split('/[\pZ\pC]+/u', $text, -1, PREG_SPLIT_NO_EMPTY);
|
||||||
|
if ($substrings === false) {
|
||||||
|
throw new InvalidArgumentException('preg_split failed on: '.$text);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $substrings;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,9 @@ use ReflectionClass;
|
|||||||
|
|
||||||
class AprioriTest extends TestCase
|
class AprioriTest extends TestCase
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
private $sampleGreek = [
|
private $sampleGreek = [
|
||||||
['alpha', 'beta', 'epsilon'],
|
['alpha', 'beta', 'epsilon'],
|
||||||
['alpha', 'beta', 'theta'],
|
['alpha', 'beta', 'theta'],
|
||||||
@ -18,6 +21,9 @@ class AprioriTest extends TestCase
|
|||||||
['alpha', 'beta', 'theta'],
|
['alpha', 'beta', 'theta'],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
private $sampleChars = [
|
private $sampleChars = [
|
||||||
['E', 'D', 'N', 'E+N', 'EN'],
|
['E', 'D', 'N', 'E+N', 'EN'],
|
||||||
['E', 'R', 'N', 'E+R', 'E+N', 'ER', 'EN'],
|
['E', 'R', 'N', 'E+R', 'E+N', 'ER', 'EN'],
|
||||||
@ -31,6 +37,9 @@ class AprioriTest extends TestCase
|
|||||||
['N'],
|
['N'],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
private $sampleBasket = [
|
private $sampleBasket = [
|
||||||
[1, 2, 3, 4],
|
[1, 2, 3, 4],
|
||||||
[1, 2, 4],
|
[1, 2, 4],
|
||||||
@ -48,16 +57,16 @@ class AprioriTest extends TestCase
|
|||||||
|
|
||||||
$predicted = $apriori->predict([['alpha', 'epsilon'], ['beta', 'theta']]);
|
$predicted = $apriori->predict([['alpha', 'epsilon'], ['beta', 'theta']]);
|
||||||
|
|
||||||
$this->assertCount(2, $predicted);
|
self::assertCount(2, $predicted);
|
||||||
$this->assertEquals([['beta']], $predicted[0]);
|
self::assertEquals([['beta']], $predicted[0]);
|
||||||
$this->assertEquals([['alpha']], $predicted[1]);
|
self::assertEquals([['alpha']], $predicted[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPowerSet(): void
|
public function testPowerSet(): void
|
||||||
{
|
{
|
||||||
$apriori = new Apriori();
|
$apriori = new Apriori();
|
||||||
|
|
||||||
$this->assertCount(8, self::invoke($apriori, 'powerSet', [['a', 'b', 'c']]));
|
self::assertCount(8, self::invoke($apriori, 'powerSet', [['a', 'b', 'c']]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testApriori(): void
|
public function testApriori(): void
|
||||||
@ -67,13 +76,13 @@ class AprioriTest extends TestCase
|
|||||||
|
|
||||||
$L = $apriori->apriori();
|
$L = $apriori->apriori();
|
||||||
|
|
||||||
$this->assertCount(4, $L[2]);
|
self::assertCount(4, $L[2]);
|
||||||
$this->assertTrue(self::invoke($apriori, 'contains', [$L[2], [1, 2]]));
|
self::assertTrue(self::invoke($apriori, 'contains', [$L[2], [1, 2]]));
|
||||||
$this->assertFalse(self::invoke($apriori, 'contains', [$L[2], [1, 3]]));
|
self::assertFalse(self::invoke($apriori, 'contains', [$L[2], [1, 3]]));
|
||||||
$this->assertFalse(self::invoke($apriori, 'contains', [$L[2], [1, 4]]));
|
self::assertFalse(self::invoke($apriori, 'contains', [$L[2], [1, 4]]));
|
||||||
$this->assertTrue(self::invoke($apriori, 'contains', [$L[2], [2, 3]]));
|
self::assertTrue(self::invoke($apriori, 'contains', [$L[2], [2, 3]]));
|
||||||
$this->assertTrue(self::invoke($apriori, 'contains', [$L[2], [2, 4]]));
|
self::assertTrue(self::invoke($apriori, 'contains', [$L[2], [2, 4]]));
|
||||||
$this->assertTrue(self::invoke($apriori, 'contains', [$L[2], [3, 4]]));
|
self::assertTrue(self::invoke($apriori, 'contains', [$L[2], [3, 4]]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAprioriEmpty(): void
|
public function testAprioriEmpty(): void
|
||||||
@ -85,7 +94,7 @@ class AprioriTest extends TestCase
|
|||||||
|
|
||||||
$L = $apriori->apriori();
|
$L = $apriori->apriori();
|
||||||
|
|
||||||
$this->assertEmpty($L);
|
self::assertEmpty($L);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAprioriSingleItem(): void
|
public function testAprioriSingleItem(): void
|
||||||
@ -97,8 +106,8 @@ class AprioriTest extends TestCase
|
|||||||
|
|
||||||
$L = $apriori->apriori();
|
$L = $apriori->apriori();
|
||||||
|
|
||||||
$this->assertEquals([1], array_keys($L));
|
self::assertEquals([1], array_keys($L));
|
||||||
$this->assertEquals([['a']], $L[1]);
|
self::assertEquals([['a']], $L[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAprioriL3(): void
|
public function testAprioriL3(): void
|
||||||
@ -110,7 +119,7 @@ class AprioriTest extends TestCase
|
|||||||
|
|
||||||
$L = $apriori->apriori();
|
$L = $apriori->apriori();
|
||||||
|
|
||||||
$this->assertEquals([['a', 'b', 'c']], $L[3]);
|
self::assertEquals([['a', 'b', 'c']], $L[3]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetRules(): void
|
public function testGetRules(): void
|
||||||
@ -118,7 +127,7 @@ class AprioriTest extends TestCase
|
|||||||
$apriori = new Apriori(0.4, 0.8);
|
$apriori = new Apriori(0.4, 0.8);
|
||||||
$apriori->train($this->sampleChars, []);
|
$apriori->train($this->sampleChars, []);
|
||||||
|
|
||||||
$this->assertCount(19, $apriori->getRules());
|
self::assertCount(19, $apriori->getRules());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetRulesSupportAndConfidence(): void
|
public function testGetRulesSupportAndConfidence(): void
|
||||||
@ -130,14 +139,14 @@ class AprioriTest extends TestCase
|
|||||||
|
|
||||||
$rules = $apriori->getRules();
|
$rules = $apriori->getRules();
|
||||||
|
|
||||||
$this->assertCount(4, $rules);
|
self::assertCount(4, $rules);
|
||||||
$this->assertContains([
|
self::assertContains([
|
||||||
Apriori::ARRAY_KEY_ANTECEDENT => ['a'],
|
Apriori::ARRAY_KEY_ANTECEDENT => ['a'],
|
||||||
Apriori::ARRAY_KEY_CONSEQUENT => ['b'],
|
Apriori::ARRAY_KEY_CONSEQUENT => ['b'],
|
||||||
Apriori::ARRAY_KEY_SUPPORT => 0.5,
|
Apriori::ARRAY_KEY_SUPPORT => 0.5,
|
||||||
Apriori::ARRAY_KEY_CONFIDENCE => 0.5,
|
Apriori::ARRAY_KEY_CONFIDENCE => 0.5,
|
||||||
], $rules);
|
], $rules);
|
||||||
$this->assertContains([
|
self::assertContains([
|
||||||
Apriori::ARRAY_KEY_ANTECEDENT => ['b'],
|
Apriori::ARRAY_KEY_ANTECEDENT => ['b'],
|
||||||
Apriori::ARRAY_KEY_CONSEQUENT => ['a'],
|
Apriori::ARRAY_KEY_CONSEQUENT => ['a'],
|
||||||
Apriori::ARRAY_KEY_SUPPORT => 0.5,
|
Apriori::ARRAY_KEY_SUPPORT => 0.5,
|
||||||
@ -149,14 +158,14 @@ class AprioriTest extends TestCase
|
|||||||
{
|
{
|
||||||
$apriori = new Apriori();
|
$apriori = new Apriori();
|
||||||
|
|
||||||
$this->assertCount(6, self::invoke($apriori, 'antecedents', [['a', 'b', 'c']]));
|
self::assertCount(6, self::invoke($apriori, 'antecedents', [['a', 'b', 'c']]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testItems(): void
|
public function testItems(): void
|
||||||
{
|
{
|
||||||
$apriori = new Apriori();
|
$apriori = new Apriori();
|
||||||
$apriori->train($this->sampleGreek, []);
|
$apriori->train($this->sampleGreek, []);
|
||||||
$this->assertCount(4, self::invoke($apriori, 'items', []));
|
self::assertCount(4, self::invoke($apriori, 'items', []));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testFrequent(): void
|
public function testFrequent(): void
|
||||||
@ -164,8 +173,8 @@ class AprioriTest extends TestCase
|
|||||||
$apriori = new Apriori(0.51);
|
$apriori = new Apriori(0.51);
|
||||||
$apriori->train($this->sampleGreek, []);
|
$apriori->train($this->sampleGreek, []);
|
||||||
|
|
||||||
$this->assertCount(0, self::invoke($apriori, 'frequent', [[['epsilon'], ['theta']]]));
|
self::assertCount(0, self::invoke($apriori, 'frequent', [[['epsilon'], ['theta']]]));
|
||||||
$this->assertCount(2, self::invoke($apriori, 'frequent', [[['alpha'], ['beta']]]));
|
self::assertCount(2, self::invoke($apriori, 'frequent', [[['alpha'], ['beta']]]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCandidates(): void
|
public function testCandidates(): void
|
||||||
@ -175,10 +184,10 @@ class AprioriTest extends TestCase
|
|||||||
|
|
||||||
$candidates = self::invoke($apriori, 'candidates', [[['alpha'], ['beta'], ['theta']]]);
|
$candidates = self::invoke($apriori, 'candidates', [[['alpha'], ['beta'], ['theta']]]);
|
||||||
|
|
||||||
$this->assertCount(3, $candidates);
|
self::assertCount(3, $candidates);
|
||||||
$this->assertEquals(['alpha', 'beta'], $candidates[0]);
|
self::assertEquals(['alpha', 'beta'], $candidates[0]);
|
||||||
$this->assertEquals(['alpha', 'theta'], $candidates[1]);
|
self::assertEquals(['alpha', 'theta'], $candidates[1]);
|
||||||
$this->assertEquals(['beta', 'theta'], $candidates[2]);
|
self::assertEquals(['beta', 'theta'], $candidates[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testConfidence(): void
|
public function testConfidence(): void
|
||||||
@ -186,8 +195,8 @@ class AprioriTest extends TestCase
|
|||||||
$apriori = new Apriori();
|
$apriori = new Apriori();
|
||||||
$apriori->train($this->sampleGreek, []);
|
$apriori->train($this->sampleGreek, []);
|
||||||
|
|
||||||
$this->assertEquals(0.5, self::invoke($apriori, 'confidence', [['alpha', 'beta', 'theta'], ['alpha', 'beta']]));
|
self::assertEquals(0.5, self::invoke($apriori, 'confidence', [['alpha', 'beta', 'theta'], ['alpha', 'beta']]));
|
||||||
$this->assertEquals(1, self::invoke($apriori, 'confidence', [['alpha', 'beta'], ['alpha']]));
|
self::assertEquals(1, self::invoke($apriori, 'confidence', [['alpha', 'beta'], ['alpha']]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSupport(): void
|
public function testSupport(): void
|
||||||
@ -195,8 +204,8 @@ class AprioriTest extends TestCase
|
|||||||
$apriori = new Apriori();
|
$apriori = new Apriori();
|
||||||
$apriori->train($this->sampleGreek, []);
|
$apriori->train($this->sampleGreek, []);
|
||||||
|
|
||||||
$this->assertEquals(1.0, self::invoke($apriori, 'support', [['alpha', 'beta']]));
|
self::assertEquals(1.0, self::invoke($apriori, 'support', [['alpha', 'beta']]));
|
||||||
$this->assertEquals(0.5, self::invoke($apriori, 'support', [['epsilon']]));
|
self::assertEquals(0.5, self::invoke($apriori, 'support', [['epsilon']]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testFrequency(): void
|
public function testFrequency(): void
|
||||||
@ -204,35 +213,35 @@ class AprioriTest extends TestCase
|
|||||||
$apriori = new Apriori();
|
$apriori = new Apriori();
|
||||||
$apriori->train($this->sampleGreek, []);
|
$apriori->train($this->sampleGreek, []);
|
||||||
|
|
||||||
$this->assertEquals(4, self::invoke($apriori, 'frequency', [['alpha', 'beta']]));
|
self::assertEquals(4, self::invoke($apriori, 'frequency', [['alpha', 'beta']]));
|
||||||
$this->assertEquals(2, self::invoke($apriori, 'frequency', [['epsilon']]));
|
self::assertEquals(2, self::invoke($apriori, 'frequency', [['epsilon']]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testContains(): void
|
public function testContains(): void
|
||||||
{
|
{
|
||||||
$apriori = new Apriori();
|
$apriori = new Apriori();
|
||||||
|
|
||||||
$this->assertTrue(self::invoke($apriori, 'contains', [[['a'], ['b']], ['a']]));
|
self::assertTrue(self::invoke($apriori, 'contains', [[['a'], ['b']], ['a']]));
|
||||||
$this->assertTrue(self::invoke($apriori, 'contains', [[[1, 2]], [1, 2]]));
|
self::assertTrue(self::invoke($apriori, 'contains', [[[1, 2]], [1, 2]]));
|
||||||
$this->assertFalse(self::invoke($apriori, 'contains', [[['a'], ['b']], ['c']]));
|
self::assertFalse(self::invoke($apriori, 'contains', [[['a'], ['b']], ['c']]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSubset(): void
|
public function testSubset(): void
|
||||||
{
|
{
|
||||||
$apriori = new Apriori();
|
$apriori = new Apriori();
|
||||||
|
|
||||||
$this->assertTrue(self::invoke($apriori, 'subset', [['a', 'b'], ['a']]));
|
self::assertTrue(self::invoke($apriori, 'subset', [['a', 'b'], ['a']]));
|
||||||
$this->assertTrue(self::invoke($apriori, 'subset', [['a'], ['a']]));
|
self::assertTrue(self::invoke($apriori, 'subset', [['a'], ['a']]));
|
||||||
$this->assertFalse(self::invoke($apriori, 'subset', [['a'], ['a', 'b']]));
|
self::assertFalse(self::invoke($apriori, 'subset', [['a'], ['a', 'b']]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testEquals(): void
|
public function testEquals(): void
|
||||||
{
|
{
|
||||||
$apriori = new Apriori();
|
$apriori = new Apriori();
|
||||||
|
|
||||||
$this->assertTrue(self::invoke($apriori, 'equals', [['a'], ['a']]));
|
self::assertTrue(self::invoke($apriori, 'equals', [['a'], ['a']]));
|
||||||
$this->assertFalse(self::invoke($apriori, 'equals', [['a'], []]));
|
self::assertFalse(self::invoke($apriori, 'equals', [['a'], []]));
|
||||||
$this->assertFalse(self::invoke($apriori, 'equals', [['a'], ['b', 'a']]));
|
self::assertFalse(self::invoke($apriori, 'equals', [['a'], ['b', 'a']]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSaveAndRestore(): void
|
public function testSaveAndRestore(): void
|
||||||
@ -243,14 +252,14 @@ class AprioriTest extends TestCase
|
|||||||
$testSamples = [['alpha', 'epsilon'], ['beta', 'theta']];
|
$testSamples = [['alpha', 'epsilon'], ['beta', 'theta']];
|
||||||
$predicted = $classifier->predict($testSamples);
|
$predicted = $classifier->predict($testSamples);
|
||||||
|
|
||||||
$filename = 'apriori-test-'.random_int(100, 999).'-'.uniqid();
|
$filename = 'apriori-test-'.random_int(100, 999).'-'.uniqid('', false);
|
||||||
$filepath = tempnam(sys_get_temp_dir(), $filename);
|
$filepath = (string) tempnam(sys_get_temp_dir(), $filename);
|
||||||
$modelManager = new ModelManager();
|
$modelManager = new ModelManager();
|
||||||
$modelManager->saveToFile($classifier, $filepath);
|
$modelManager->saveToFile($classifier, $filepath);
|
||||||
|
|
||||||
$restoredClassifier = $modelManager->restoreFromFile($filepath);
|
$restoredClassifier = $modelManager->restoreFromFile($filepath);
|
||||||
$this->assertEquals($classifier, $restoredClassifier);
|
self::assertEquals($classifier, $restoredClassifier);
|
||||||
$this->assertEquals($predicted, $restoredClassifier->predict($testSamples));
|
self::assertEquals($predicted, $restoredClassifier->predict($testSamples));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -261,7 +270,7 @@ class AprioriTest extends TestCase
|
|||||||
*
|
*
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
private static function invoke(&$object, string $method, array $params = [])
|
private static function invoke(Apriori $object, string $method, array $params = [])
|
||||||
{
|
{
|
||||||
$reflection = new ReflectionClass(get_class($object));
|
$reflection = new ReflectionClass(get_class($object));
|
||||||
$method = $reflection->getMethod($method);
|
$method = $reflection->getMethod($method);
|
||||||
|
@ -21,7 +21,10 @@ class DecisionTreeLeafTest extends TestCase
|
|||||||
|
|
||||||
$leaf->rightLeaf = $rightLeaf;
|
$leaf->rightLeaf = $rightLeaf;
|
||||||
|
|
||||||
$this->assertEquals('<table ><tr><td colspan=3 align=center style=\'border:1px solid;\'><b>col_0 =1</b><br>Gini: 0.00</td></tr><tr><td></td><td> </td><td valign=top align=right><b>No |</b><br><table ><tr><td colspan=3 align=center style=\'border:1px solid;\'><b>col_1 <= 2</b><br>Gini: 0.00</td></tr></table></td></tr></table>', $leaf->getHTML());
|
self::assertEquals(
|
||||||
|
'<table ><tr><td colspan=3 align=center style=\'border:1px solid;\'><b>col_0 =1</b><br>Gini: 0.00</td></tr><tr><td></td><td> </td><td valign=top align=right><b>No |</b><br><table ><tr><td colspan=3 align=center style=\'border:1px solid;\'><b>col_1 <= 2</b><br>Gini: 0.00</td></tr></table></td></tr></table>',
|
||||||
|
$leaf->getHTML()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNodeImpurityDecreaseShouldBeZeroWhenLeafIsTerminal(): void
|
public function testNodeImpurityDecreaseShouldBeZeroWhenLeafIsTerminal(): void
|
||||||
@ -29,7 +32,7 @@ class DecisionTreeLeafTest extends TestCase
|
|||||||
$leaf = new DecisionTreeLeaf();
|
$leaf = new DecisionTreeLeaf();
|
||||||
$leaf->isTerminal = true;
|
$leaf->isTerminal = true;
|
||||||
|
|
||||||
$this->assertEquals(0.0, $leaf->getNodeImpurityDecrease(1));
|
self::assertEquals(0.0, $leaf->getNodeImpurityDecrease(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNodeImpurityDecrease(): void
|
public function testNodeImpurityDecrease(): void
|
||||||
@ -45,6 +48,6 @@ class DecisionTreeLeafTest extends TestCase
|
|||||||
$leaf->rightLeaf->records = [];
|
$leaf->rightLeaf->records = [];
|
||||||
$leaf->rightLeaf->giniIndex = 0.3;
|
$leaf->rightLeaf->giniIndex = 0.3;
|
||||||
|
|
||||||
$this->assertSame(0.75, $leaf->getNodeImpurityDecrease(2));
|
self::assertSame(0.75, $leaf->getNodeImpurityDecrease(2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,9 @@ use PHPUnit\Framework\TestCase;
|
|||||||
|
|
||||||
class DecisionTreeTest extends TestCase
|
class DecisionTreeTest extends TestCase
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
private $data = [
|
private $data = [
|
||||||
['sunny', 85, 85, 'false', 'Dont_play'],
|
['sunny', 85, 85, 'false', 'Dont_play'],
|
||||||
['sunny', 80, 90, 'true', 'Dont_play'],
|
['sunny', 80, 90, 'true', 'Dont_play'],
|
||||||
@ -27,26 +30,27 @@ class DecisionTreeTest extends TestCase
|
|||||||
['rain', 71, 80, 'true', 'Dont_play'],
|
['rain', 71, 80, 'true', 'Dont_play'],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
private $extraData = [
|
private $extraData = [
|
||||||
['scorching', 90, 95, 'false', 'Dont_play'],
|
['scorching', 90, 95, 'false', 'Dont_play'],
|
||||||
['scorching', 100, 93, 'true', 'Dont_play'],
|
['scorching', 100, 93, 'true', 'Dont_play'],
|
||||||
];
|
];
|
||||||
|
|
||||||
public function testPredictSingleSample()
|
public function testPredictSingleSample(): void
|
||||||
{
|
{
|
||||||
[$data, $targets] = $this->getData($this->data);
|
[$data, $targets] = $this->getData($this->data);
|
||||||
$classifier = new DecisionTree(5);
|
$classifier = new DecisionTree(5);
|
||||||
$classifier->train($data, $targets);
|
$classifier->train($data, $targets);
|
||||||
$this->assertEquals('Dont_play', $classifier->predict(['sunny', 78, 72, 'false']));
|
self::assertEquals('Dont_play', $classifier->predict(['sunny', 78, 72, 'false']));
|
||||||
$this->assertEquals('Play', $classifier->predict(['overcast', 60, 60, 'false']));
|
self::assertEquals('Play', $classifier->predict(['overcast', 60, 60, 'false']));
|
||||||
$this->assertEquals('Dont_play', $classifier->predict(['rain', 60, 60, 'true']));
|
self::assertEquals('Dont_play', $classifier->predict(['rain', 60, 60, 'true']));
|
||||||
|
|
||||||
[$data, $targets] = $this->getData($this->extraData);
|
[$data, $targets] = $this->getData($this->extraData);
|
||||||
$classifier->train($data, $targets);
|
$classifier->train($data, $targets);
|
||||||
$this->assertEquals('Dont_play', $classifier->predict(['scorching', 95, 90, 'true']));
|
self::assertEquals('Dont_play', $classifier->predict(['scorching', 95, 90, 'true']));
|
||||||
$this->assertEquals('Play', $classifier->predict(['overcast', 60, 60, 'false']));
|
self::assertEquals('Play', $classifier->predict(['overcast', 60, 60, 'false']));
|
||||||
|
|
||||||
return $classifier;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSaveAndRestore(): void
|
public function testSaveAndRestore(): void
|
||||||
@ -58,14 +62,14 @@ class DecisionTreeTest extends TestCase
|
|||||||
$testSamples = [['sunny', 78, 72, 'false'], ['overcast', 60, 60, 'false']];
|
$testSamples = [['sunny', 78, 72, 'false'], ['overcast', 60, 60, 'false']];
|
||||||
$predicted = $classifier->predict($testSamples);
|
$predicted = $classifier->predict($testSamples);
|
||||||
|
|
||||||
$filename = 'decision-tree-test-'.random_int(100, 999).'-'.uniqid();
|
$filename = 'decision-tree-test-'.random_int(100, 999).'-'.uniqid('', false);
|
||||||
$filepath = tempnam(sys_get_temp_dir(), $filename);
|
$filepath = (string) tempnam(sys_get_temp_dir(), $filename);
|
||||||
$modelManager = new ModelManager();
|
$modelManager = new ModelManager();
|
||||||
$modelManager->saveToFile($classifier, $filepath);
|
$modelManager->saveToFile($classifier, $filepath);
|
||||||
|
|
||||||
$restoredClassifier = $modelManager->restoreFromFile($filepath);
|
$restoredClassifier = $modelManager->restoreFromFile($filepath);
|
||||||
$this->assertEquals($classifier, $restoredClassifier);
|
self::assertEquals($classifier, $restoredClassifier);
|
||||||
$this->assertEquals($predicted, $restoredClassifier->predict($testSamples));
|
self::assertEquals($predicted, $restoredClassifier->predict($testSamples));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testTreeDepth(): void
|
public function testTreeDepth(): void
|
||||||
@ -73,10 +77,10 @@ class DecisionTreeTest extends TestCase
|
|||||||
[$data, $targets] = $this->getData($this->data);
|
[$data, $targets] = $this->getData($this->data);
|
||||||
$classifier = new DecisionTree(5);
|
$classifier = new DecisionTree(5);
|
||||||
$classifier->train($data, $targets);
|
$classifier->train($data, $targets);
|
||||||
$this->assertTrue($classifier->actualDepth <= 5);
|
self::assertTrue($classifier->actualDepth <= 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getData($input)
|
private function getData(array $input): array
|
||||||
{
|
{
|
||||||
$targets = array_column($input, 4);
|
$targets = array_column($input, 4);
|
||||||
array_walk($input, function (&$v): void {
|
array_walk($input, function (&$v): void {
|
||||||
|
@ -37,27 +37,27 @@ class AdaBoostTest extends TestCase
|
|||||||
$targets = [0, 0, 0, 1, 1, 1];
|
$targets = [0, 0, 0, 1, 1, 1];
|
||||||
$classifier = new AdaBoost();
|
$classifier = new AdaBoost();
|
||||||
$classifier->train($samples, $targets);
|
$classifier->train($samples, $targets);
|
||||||
$this->assertEquals(0, $classifier->predict([0.1, 0.2]));
|
self::assertEquals(0, $classifier->predict([0.1, 0.2]));
|
||||||
$this->assertEquals(0, $classifier->predict([0.1, 0.99]));
|
self::assertEquals(0, $classifier->predict([0.1, 0.99]));
|
||||||
$this->assertEquals(1, $classifier->predict([1.1, 0.8]));
|
self::assertEquals(1, $classifier->predict([1.1, 0.8]));
|
||||||
|
|
||||||
// OR problem
|
// OR problem
|
||||||
$samples = [[0, 0], [0.1, 0.2], [0.2, 0.1], [1, 0], [0, 1], [1, 1]];
|
$samples = [[0, 0], [0.1, 0.2], [0.2, 0.1], [1, 0], [0, 1], [1, 1]];
|
||||||
$targets = [0, 0, 0, 1, 1, 1];
|
$targets = [0, 0, 0, 1, 1, 1];
|
||||||
$classifier = new AdaBoost();
|
$classifier = new AdaBoost();
|
||||||
$classifier->train($samples, $targets);
|
$classifier->train($samples, $targets);
|
||||||
$this->assertEquals(0, $classifier->predict([0.1, 0.2]));
|
self::assertEquals(0, $classifier->predict([0.1, 0.2]));
|
||||||
$this->assertEquals(1, $classifier->predict([0.1, 0.99]));
|
self::assertEquals(1, $classifier->predict([0.1, 0.99]));
|
||||||
$this->assertEquals(1, $classifier->predict([1.1, 0.8]));
|
self::assertEquals(1, $classifier->predict([1.1, 0.8]));
|
||||||
|
|
||||||
// XOR problem
|
// XOR problem
|
||||||
$samples = [[0.1, 0.2], [1., 1.], [0.9, 0.8], [0., 1.], [1., 0.], [0.2, 0.8]];
|
$samples = [[0.1, 0.2], [1., 1.], [0.9, 0.8], [0., 1.], [1., 0.], [0.2, 0.8]];
|
||||||
$targets = [0, 0, 0, 1, 1, 1];
|
$targets = [0, 0, 0, 1, 1, 1];
|
||||||
$classifier = new AdaBoost(5);
|
$classifier = new AdaBoost(5);
|
||||||
$classifier->train($samples, $targets);
|
$classifier->train($samples, $targets);
|
||||||
$this->assertEquals(0, $classifier->predict([0.1, 0.1]));
|
self::assertEquals(0, $classifier->predict([0.1, 0.1]));
|
||||||
$this->assertEquals(1, $classifier->predict([0, 0.999]));
|
self::assertEquals(1, $classifier->predict([0, 0.999]));
|
||||||
$this->assertEquals(0, $classifier->predict([1.1, 0.8]));
|
self::assertEquals(0, $classifier->predict([1.1, 0.8]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSaveAndRestore(): void
|
public function testSaveAndRestore(): void
|
||||||
@ -70,13 +70,13 @@ class AdaBoostTest extends TestCase
|
|||||||
$testSamples = [[0, 1], [1, 1], [0.2, 0.1]];
|
$testSamples = [[0, 1], [1, 1], [0.2, 0.1]];
|
||||||
$predicted = $classifier->predict($testSamples);
|
$predicted = $classifier->predict($testSamples);
|
||||||
|
|
||||||
$filename = 'adaboost-test-'.random_int(100, 999).'-'.uniqid();
|
$filename = 'adaboost-test-'.random_int(100, 999).'-'.uniqid('', false);
|
||||||
$filepath = tempnam(sys_get_temp_dir(), $filename);
|
$filepath = (string) tempnam(sys_get_temp_dir(), $filename);
|
||||||
$modelManager = new ModelManager();
|
$modelManager = new ModelManager();
|
||||||
$modelManager->saveToFile($classifier, $filepath);
|
$modelManager->saveToFile($classifier, $filepath);
|
||||||
|
|
||||||
$restoredClassifier = $modelManager->restoreFromFile($filepath);
|
$restoredClassifier = $modelManager->restoreFromFile($filepath);
|
||||||
$this->assertEquals($classifier, $restoredClassifier);
|
self::assertEquals($classifier, $restoredClassifier);
|
||||||
$this->assertEquals($predicted, $restoredClassifier->predict($testSamples));
|
self::assertEquals($predicted, $restoredClassifier->predict($testSamples));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Phpml\Tests\Classification\Ensemble;
|
namespace Phpml\Tests\Classification\Ensemble;
|
||||||
|
|
||||||
|
use Phpml\Classification\Classifier;
|
||||||
use Phpml\Classification\DecisionTree;
|
use Phpml\Classification\DecisionTree;
|
||||||
use Phpml\Classification\Ensemble\Bagging;
|
use Phpml\Classification\Ensemble\Bagging;
|
||||||
use Phpml\Classification\NaiveBayes;
|
use Phpml\Classification\NaiveBayes;
|
||||||
@ -13,6 +14,9 @@ use PHPUnit\Framework\TestCase;
|
|||||||
|
|
||||||
class BaggingTest extends TestCase
|
class BaggingTest extends TestCase
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
private $data = [
|
private $data = [
|
||||||
['sunny', 85, 85, 'false', 'Dont_play'],
|
['sunny', 85, 85, 'false', 'Dont_play'],
|
||||||
['sunny', 80, 90, 'true', 'Dont_play'],
|
['sunny', 80, 90, 'true', 'Dont_play'],
|
||||||
@ -30,6 +34,9 @@ class BaggingTest extends TestCase
|
|||||||
['rain', 71, 80, 'true', 'Dont_play'],
|
['rain', 71, 80, 'true', 'Dont_play'],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
private $extraData = [
|
private $extraData = [
|
||||||
['scorching', 90, 95, 'false', 'Dont_play'],
|
['scorching', 90, 95, 'false', 'Dont_play'],
|
||||||
['scorching', 0, 0, 'false', 'Dont_play'],
|
['scorching', 0, 0, 'false', 'Dont_play'],
|
||||||
@ -49,14 +56,14 @@ class BaggingTest extends TestCase
|
|||||||
$classifier = $this->getClassifier();
|
$classifier = $this->getClassifier();
|
||||||
// Testing with default options
|
// Testing with default options
|
||||||
$classifier->train($data, $targets);
|
$classifier->train($data, $targets);
|
||||||
$this->assertEquals('Dont_play', $classifier->predict(['sunny', 78, 72, 'false']));
|
self::assertEquals('Dont_play', $classifier->predict(['sunny', 78, 72, 'false']));
|
||||||
$this->assertEquals('Play', $classifier->predict(['overcast', 60, 60, 'false']));
|
self::assertEquals('Play', $classifier->predict(['overcast', 60, 60, 'false']));
|
||||||
$this->assertEquals('Dont_play', $classifier->predict(['rain', 60, 60, 'true']));
|
self::assertEquals('Dont_play', $classifier->predict(['rain', 60, 60, 'true']));
|
||||||
|
|
||||||
[$data, $targets] = $this->getData($this->extraData);
|
[$data, $targets] = $this->getData($this->extraData);
|
||||||
$classifier->train($data, $targets);
|
$classifier->train($data, $targets);
|
||||||
$this->assertEquals('Dont_play', $classifier->predict(['scorching', 95, 90, 'true']));
|
self::assertEquals('Dont_play', $classifier->predict(['scorching', 95, 90, 'true']));
|
||||||
$this->assertEquals('Play', $classifier->predict(['overcast', 60, 60, 'false']));
|
self::assertEquals('Play', $classifier->predict(['overcast', 60, 60, 'false']));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSaveAndRestore(): void
|
public function testSaveAndRestore(): void
|
||||||
@ -68,14 +75,14 @@ class BaggingTest extends TestCase
|
|||||||
$testSamples = [['sunny', 78, 72, 'false'], ['overcast', 60, 60, 'false']];
|
$testSamples = [['sunny', 78, 72, 'false'], ['overcast', 60, 60, 'false']];
|
||||||
$predicted = $classifier->predict($testSamples);
|
$predicted = $classifier->predict($testSamples);
|
||||||
|
|
||||||
$filename = 'bagging-test-'.random_int(100, 999).'-'.uniqid();
|
$filename = 'bagging-test-'.random_int(100, 999).'-'.uniqid('', false);
|
||||||
$filepath = tempnam(sys_get_temp_dir(), $filename);
|
$filepath = (string) tempnam(sys_get_temp_dir(), $filename);
|
||||||
$modelManager = new ModelManager();
|
$modelManager = new ModelManager();
|
||||||
$modelManager->saveToFile($classifier, $filepath);
|
$modelManager->saveToFile($classifier, $filepath);
|
||||||
|
|
||||||
$restoredClassifier = $modelManager->restoreFromFile($filepath);
|
$restoredClassifier = $modelManager->restoreFromFile($filepath);
|
||||||
$this->assertEquals($classifier, $restoredClassifier);
|
self::assertEquals($classifier, $restoredClassifier);
|
||||||
$this->assertEquals($predicted, $restoredClassifier->predict($testSamples));
|
self::assertEquals($predicted, $restoredClassifier->predict($testSamples));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testBaseClassifiers(): void
|
public function testBaseClassifiers(): void
|
||||||
@ -94,12 +101,15 @@ class BaggingTest extends TestCase
|
|||||||
foreach ($testData as $test) {
|
foreach ($testData as $test) {
|
||||||
$result = $classifier->predict($test);
|
$result = $classifier->predict($test);
|
||||||
$baseResult = $classifier->predict($test);
|
$baseResult = $classifier->predict($test);
|
||||||
$this->assertEquals($result, $baseResult);
|
self::assertEquals($result, $baseResult);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getClassifier($numBaseClassifiers = 50)
|
/**
|
||||||
|
* @return Bagging
|
||||||
|
*/
|
||||||
|
protected function getClassifier(int $numBaseClassifiers = 50): Classifier
|
||||||
{
|
{
|
||||||
$classifier = new Bagging($numBaseClassifiers);
|
$classifier = new Bagging($numBaseClassifiers);
|
||||||
$classifier->setSubsetRatio(1.0);
|
$classifier->setSubsetRatio(1.0);
|
||||||
@ -108,7 +118,7 @@ class BaggingTest extends TestCase
|
|||||||
return $classifier;
|
return $classifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getAvailableBaseClassifiers()
|
protected function getAvailableBaseClassifiers(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
DecisionTree::class => ['depth' => 5],
|
DecisionTree::class => ['depth' => 5],
|
||||||
@ -116,7 +126,7 @@ class BaggingTest extends TestCase
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getData($input)
|
private function getData(array $input): array
|
||||||
{
|
{
|
||||||
// Populating input data to a size large enough
|
// Populating input data to a size large enough
|
||||||
// for base classifiers that they can work with a subset of it
|
// for base classifiers that they can work with a subset of it
|
||||||
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace Phpml\Tests\Classification\Ensemble;
|
namespace Phpml\Tests\Classification\Ensemble;
|
||||||
|
|
||||||
|
use Phpml\Classification\Classifier;
|
||||||
use Phpml\Classification\DecisionTree;
|
use Phpml\Classification\DecisionTree;
|
||||||
use Phpml\Classification\Ensemble\RandomForest;
|
use Phpml\Classification\Ensemble\RandomForest;
|
||||||
use Phpml\Classification\NaiveBayes;
|
use Phpml\Classification\NaiveBayes;
|
||||||
@ -47,7 +48,10 @@ class RandomForestTest extends BaggingTest
|
|||||||
$classifier->setFeatureSubsetRatio('pow');
|
$classifier->setFeatureSubsetRatio('pow');
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getClassifier($numBaseClassifiers = 50)
|
/**
|
||||||
|
* @return RandomForest
|
||||||
|
*/
|
||||||
|
protected function getClassifier(int $numBaseClassifiers = 50): Classifier
|
||||||
{
|
{
|
||||||
$classifier = new RandomForest($numBaseClassifiers);
|
$classifier = new RandomForest($numBaseClassifiers);
|
||||||
$classifier->setFeatureSubsetRatio('log');
|
$classifier->setFeatureSubsetRatio('log');
|
||||||
@ -55,7 +59,7 @@ class RandomForestTest extends BaggingTest
|
|||||||
return $classifier;
|
return $classifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getAvailableBaseClassifiers()
|
protected function getAvailableBaseClassifiers(): array
|
||||||
{
|
{
|
||||||
return [DecisionTree::class => ['depth' => 5]];
|
return [DecisionTree::class => ['depth' => 5]];
|
||||||
}
|
}
|
||||||
|
@ -19,15 +19,15 @@ class KNearestNeighborsTest extends TestCase
|
|||||||
$classifier = new KNearestNeighbors();
|
$classifier = new KNearestNeighbors();
|
||||||
$classifier->train($samples, $labels);
|
$classifier->train($samples, $labels);
|
||||||
|
|
||||||
$this->assertEquals('b', $classifier->predict([3, 2]));
|
self::assertEquals('b', $classifier->predict([3, 2]));
|
||||||
$this->assertEquals('b', $classifier->predict([5, 1]));
|
self::assertEquals('b', $classifier->predict([5, 1]));
|
||||||
$this->assertEquals('b', $classifier->predict([4, 3]));
|
self::assertEquals('b', $classifier->predict([4, 3]));
|
||||||
$this->assertEquals('b', $classifier->predict([4, -5]));
|
self::assertEquals('b', $classifier->predict([4, -5]));
|
||||||
|
|
||||||
$this->assertEquals('a', $classifier->predict([2, 3]));
|
self::assertEquals('a', $classifier->predict([2, 3]));
|
||||||
$this->assertEquals('a', $classifier->predict([1, 2]));
|
self::assertEquals('a', $classifier->predict([1, 2]));
|
||||||
$this->assertEquals('a', $classifier->predict([1, 5]));
|
self::assertEquals('a', $classifier->predict([1, 5]));
|
||||||
$this->assertEquals('a', $classifier->predict([3, 10]));
|
self::assertEquals('a', $classifier->predict([3, 10]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPredictArrayOfSamples(): void
|
public function testPredictArrayOfSamples(): void
|
||||||
@ -42,7 +42,7 @@ class KNearestNeighborsTest extends TestCase
|
|||||||
$classifier->train($trainSamples, $trainLabels);
|
$classifier->train($trainSamples, $trainLabels);
|
||||||
$predicted = $classifier->predict($testSamples);
|
$predicted = $classifier->predict($testSamples);
|
||||||
|
|
||||||
$this->assertEquals($testLabels, $predicted);
|
self::assertEquals($testLabels, $predicted);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPredictArrayOfSamplesUsingChebyshevDistanceMetric(): void
|
public function testPredictArrayOfSamplesUsingChebyshevDistanceMetric(): void
|
||||||
@ -57,7 +57,7 @@ class KNearestNeighborsTest extends TestCase
|
|||||||
$classifier->train($trainSamples, $trainLabels);
|
$classifier->train($trainSamples, $trainLabels);
|
||||||
$predicted = $classifier->predict($testSamples);
|
$predicted = $classifier->predict($testSamples);
|
||||||
|
|
||||||
$this->assertEquals($testLabels, $predicted);
|
self::assertEquals($testLabels, $predicted);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSaveAndRestore(): void
|
public function testSaveAndRestore(): void
|
||||||
@ -73,12 +73,12 @@ class KNearestNeighborsTest extends TestCase
|
|||||||
$predicted = $classifier->predict($testSamples);
|
$predicted = $classifier->predict($testSamples);
|
||||||
|
|
||||||
$filename = 'knearest-neighbors-test-'.random_int(100, 999).'-'.uniqid('', false);
|
$filename = 'knearest-neighbors-test-'.random_int(100, 999).'-'.uniqid('', false);
|
||||||
$filepath = tempnam(sys_get_temp_dir(), $filename);
|
$filepath = (string) tempnam(sys_get_temp_dir(), $filename);
|
||||||
$modelManager = new ModelManager();
|
$modelManager = new ModelManager();
|
||||||
$modelManager->saveToFile($classifier, $filepath);
|
$modelManager->saveToFile($classifier, $filepath);
|
||||||
|
|
||||||
$restoredClassifier = $modelManager->restoreFromFile($filepath);
|
$restoredClassifier = $modelManager->restoreFromFile($filepath);
|
||||||
$this->assertEquals($classifier, $restoredClassifier);
|
self::assertEquals($classifier, $restoredClassifier);
|
||||||
$this->assertEquals($predicted, $restoredClassifier->predict($testSamples));
|
self::assertEquals($predicted, $restoredClassifier->predict($testSamples));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,18 +31,18 @@ class AdalineTest extends TestCase
|
|||||||
$targets = [0, 0, 0, 1];
|
$targets = [0, 0, 0, 1];
|
||||||
$classifier = new Adaline();
|
$classifier = new Adaline();
|
||||||
$classifier->train($samples, $targets);
|
$classifier->train($samples, $targets);
|
||||||
$this->assertEquals(0, $classifier->predict([0.1, 0.2]));
|
self::assertEquals(0, $classifier->predict([0.1, 0.2]));
|
||||||
$this->assertEquals(0, $classifier->predict([0.1, 0.99]));
|
self::assertEquals(0, $classifier->predict([0.1, 0.99]));
|
||||||
$this->assertEquals(1, $classifier->predict([1.1, 0.8]));
|
self::assertEquals(1, $classifier->predict([1.1, 0.8]));
|
||||||
|
|
||||||
// OR problem
|
// OR problem
|
||||||
$samples = [[0, 0], [1, 0], [0, 1], [1, 1]];
|
$samples = [[0, 0], [1, 0], [0, 1], [1, 1]];
|
||||||
$targets = [0, 1, 1, 1];
|
$targets = [0, 1, 1, 1];
|
||||||
$classifier = new Adaline();
|
$classifier = new Adaline();
|
||||||
$classifier->train($samples, $targets);
|
$classifier->train($samples, $targets);
|
||||||
$this->assertEquals(0, $classifier->predict([0.1, 0.2]));
|
self::assertEquals(0, $classifier->predict([0.1, 0.2]));
|
||||||
$this->assertEquals(1, $classifier->predict([0.1, 0.99]));
|
self::assertEquals(1, $classifier->predict([0.1, 0.99]));
|
||||||
$this->assertEquals(1, $classifier->predict([1.1, 0.8]));
|
self::assertEquals(1, $classifier->predict([1.1, 0.8]));
|
||||||
|
|
||||||
// By use of One-v-Rest, Adaline can perform multi-class classification
|
// By use of One-v-Rest, Adaline can perform multi-class classification
|
||||||
// The samples should be separable by lines perpendicular to the dimensions
|
// The samples should be separable by lines perpendicular to the dimensions
|
||||||
@ -55,15 +55,15 @@ class AdalineTest extends TestCase
|
|||||||
|
|
||||||
$classifier = new Adaline();
|
$classifier = new Adaline();
|
||||||
$classifier->train($samples, $targets);
|
$classifier->train($samples, $targets);
|
||||||
$this->assertEquals(0, $classifier->predict([0.5, 0.5]));
|
self::assertEquals(0, $classifier->predict([0.5, 0.5]));
|
||||||
$this->assertEquals(1, $classifier->predict([6.0, 5.0]));
|
self::assertEquals(1, $classifier->predict([6.0, 5.0]));
|
||||||
$this->assertEquals(2, $classifier->predict([3.0, 9.5]));
|
self::assertEquals(2, $classifier->predict([3.0, 9.5]));
|
||||||
|
|
||||||
// Extra partial training should lead to the same results.
|
// Extra partial training should lead to the same results.
|
||||||
$classifier->partialTrain([[0, 1], [1, 0]], [0, 0], [0, 1, 2]);
|
$classifier->partialTrain([[0, 1], [1, 0]], [0, 0], [0, 1, 2]);
|
||||||
$this->assertEquals(0, $classifier->predict([0.5, 0.5]));
|
self::assertEquals(0, $classifier->predict([0.5, 0.5]));
|
||||||
$this->assertEquals(1, $classifier->predict([6.0, 5.0]));
|
self::assertEquals(1, $classifier->predict([6.0, 5.0]));
|
||||||
$this->assertEquals(2, $classifier->predict([3.0, 9.5]));
|
self::assertEquals(2, $classifier->predict([3.0, 9.5]));
|
||||||
|
|
||||||
// Train should clear previous data.
|
// Train should clear previous data.
|
||||||
$samples = [
|
$samples = [
|
||||||
@ -73,9 +73,9 @@ class AdalineTest extends TestCase
|
|||||||
];
|
];
|
||||||
$targets = [2, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1];
|
$targets = [2, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1];
|
||||||
$classifier->train($samples, $targets);
|
$classifier->train($samples, $targets);
|
||||||
$this->assertEquals(2, $classifier->predict([0.5, 0.5]));
|
self::assertEquals(2, $classifier->predict([0.5, 0.5]));
|
||||||
$this->assertEquals(0, $classifier->predict([6.0, 5.0]));
|
self::assertEquals(0, $classifier->predict([6.0, 5.0]));
|
||||||
$this->assertEquals(1, $classifier->predict([3.0, 9.5]));
|
self::assertEquals(1, $classifier->predict([3.0, 9.5]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSaveAndRestore(): void
|
public function testSaveAndRestore(): void
|
||||||
@ -89,12 +89,12 @@ class AdalineTest extends TestCase
|
|||||||
$predicted = $classifier->predict($testSamples);
|
$predicted = $classifier->predict($testSamples);
|
||||||
|
|
||||||
$filename = 'adaline-test-'.random_int(100, 999).'-'.uniqid('', false);
|
$filename = 'adaline-test-'.random_int(100, 999).'-'.uniqid('', false);
|
||||||
$filepath = tempnam(sys_get_temp_dir(), $filename);
|
$filepath = (string) tempnam(sys_get_temp_dir(), $filename);
|
||||||
$modelManager = new ModelManager();
|
$modelManager = new ModelManager();
|
||||||
$modelManager->saveToFile($classifier, $filepath);
|
$modelManager->saveToFile($classifier, $filepath);
|
||||||
|
|
||||||
$restoredClassifier = $modelManager->restoreFromFile($filepath);
|
$restoredClassifier = $modelManager->restoreFromFile($filepath);
|
||||||
$this->assertEquals($classifier, $restoredClassifier);
|
self::assertEquals($classifier, $restoredClassifier);
|
||||||
$this->assertEquals($predicted, $restoredClassifier->predict($testSamples));
|
self::assertEquals($predicted, $restoredClassifier->predict($testSamples));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ class DecisionStumpTest extends TestCase
|
|||||||
$classifier->train($samples, $targets);
|
$classifier->train($samples, $targets);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPredictSingleSample()
|
public function testPredictSingleSample(): void
|
||||||
{
|
{
|
||||||
// Samples should be separable with a line perpendicular
|
// Samples should be separable with a line perpendicular
|
||||||
// to any dimension given in the dataset
|
// to any dimension given in the dataset
|
||||||
@ -33,20 +33,20 @@ class DecisionStumpTest extends TestCase
|
|||||||
$targets = [0, 0, 1, 1];
|
$targets = [0, 0, 1, 1];
|
||||||
$classifier = new DecisionStump();
|
$classifier = new DecisionStump();
|
||||||
$classifier->train($samples, $targets);
|
$classifier->train($samples, $targets);
|
||||||
$this->assertEquals(0, $classifier->predict([0.1, 0.2]));
|
self::assertEquals(0, $classifier->predict([0.1, 0.2]));
|
||||||
$this->assertEquals(0, $classifier->predict([1.1, 0.2]));
|
self::assertEquals(0, $classifier->predict([1.1, 0.2]));
|
||||||
$this->assertEquals(1, $classifier->predict([0.1, 0.99]));
|
self::assertEquals(1, $classifier->predict([0.1, 0.99]));
|
||||||
$this->assertEquals(1, $classifier->predict([1.1, 0.8]));
|
self::assertEquals(1, $classifier->predict([1.1, 0.8]));
|
||||||
|
|
||||||
// Then: vertical test
|
// Then: vertical test
|
||||||
$samples = [[0, 0], [1, 0], [0, 1], [1, 1]];
|
$samples = [[0, 0], [1, 0], [0, 1], [1, 1]];
|
||||||
$targets = [0, 1, 0, 1];
|
$targets = [0, 1, 0, 1];
|
||||||
$classifier = new DecisionStump();
|
$classifier = new DecisionStump();
|
||||||
$classifier->train($samples, $targets);
|
$classifier->train($samples, $targets);
|
||||||
$this->assertEquals(0, $classifier->predict([0.1, 0.2]));
|
self::assertEquals(0, $classifier->predict([0.1, 0.2]));
|
||||||
$this->assertEquals(0, $classifier->predict([0.1, 1.1]));
|
self::assertEquals(0, $classifier->predict([0.1, 1.1]));
|
||||||
$this->assertEquals(1, $classifier->predict([1.0, 0.99]));
|
self::assertEquals(1, $classifier->predict([1.0, 0.99]));
|
||||||
$this->assertEquals(1, $classifier->predict([1.1, 0.1]));
|
self::assertEquals(1, $classifier->predict([1.1, 0.1]));
|
||||||
|
|
||||||
// By use of One-v-Rest, DecisionStump can perform multi-class classification
|
// By use of One-v-Rest, DecisionStump can perform multi-class classification
|
||||||
// The samples should be separable by lines perpendicular to the dimensions
|
// The samples should be separable by lines perpendicular to the dimensions
|
||||||
@ -59,11 +59,9 @@ class DecisionStumpTest extends TestCase
|
|||||||
|
|
||||||
$classifier = new DecisionStump();
|
$classifier = new DecisionStump();
|
||||||
$classifier->train($samples, $targets);
|
$classifier->train($samples, $targets);
|
||||||
$this->assertEquals(0, $classifier->predict([0.5, 0.5]));
|
self::assertEquals(0, $classifier->predict([0.5, 0.5]));
|
||||||
$this->assertEquals(1, $classifier->predict([6.0, 5.0]));
|
self::assertEquals(1, $classifier->predict([6.0, 5.0]));
|
||||||
$this->assertEquals(2, $classifier->predict([3.5, 9.5]));
|
self::assertEquals(2, $classifier->predict([3.5, 9.5]));
|
||||||
|
|
||||||
return $classifier;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSaveAndRestore(): void
|
public function testSaveAndRestore(): void
|
||||||
@ -76,13 +74,13 @@ class DecisionStumpTest extends TestCase
|
|||||||
$testSamples = [[0, 1], [1, 1], [0.2, 0.1]];
|
$testSamples = [[0, 1], [1, 1], [0.2, 0.1]];
|
||||||
$predicted = $classifier->predict($testSamples);
|
$predicted = $classifier->predict($testSamples);
|
||||||
|
|
||||||
$filename = 'dstump-test-'.random_int(100, 999).'-'.uniqid();
|
$filename = 'dstump-test-'.random_int(100, 999).'-'.uniqid('', false);
|
||||||
$filepath = tempnam(sys_get_temp_dir(), $filename);
|
$filepath = (string) tempnam(sys_get_temp_dir(), $filename);
|
||||||
$modelManager = new ModelManager();
|
$modelManager = new ModelManager();
|
||||||
$modelManager->saveToFile($classifier, $filepath);
|
$modelManager->saveToFile($classifier, $filepath);
|
||||||
|
|
||||||
$restoredClassifier = $modelManager->restoreFromFile($filepath);
|
$restoredClassifier = $modelManager->restoreFromFile($filepath);
|
||||||
$this->assertEquals($classifier, $restoredClassifier);
|
self::assertEquals($classifier, $restoredClassifier);
|
||||||
$this->assertEquals($predicted, $restoredClassifier->predict($testSamples));
|
self::assertEquals($predicted, $restoredClassifier->predict($testSamples));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,8 +64,8 @@ class LogisticRegressionTest extends TestCase
|
|||||||
$targets = [0, 0, 0, 1, 0, 1];
|
$targets = [0, 0, 0, 1, 0, 1];
|
||||||
$classifier = new LogisticRegression();
|
$classifier = new LogisticRegression();
|
||||||
$classifier->train($samples, $targets);
|
$classifier->train($samples, $targets);
|
||||||
$this->assertEquals(0, $classifier->predict([0.1, 0.1]));
|
self::assertEquals(0, $classifier->predict([0.1, 0.1]));
|
||||||
$this->assertEquals(1, $classifier->predict([0.9, 0.9]));
|
self::assertEquals(1, $classifier->predict([0.9, 0.9]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPredictSingleSampleWithBatchTraining(): void
|
public function testPredictSingleSampleWithBatchTraining(): void
|
||||||
@ -83,8 +83,8 @@ class LogisticRegressionTest extends TestCase
|
|||||||
'L2'
|
'L2'
|
||||||
);
|
);
|
||||||
$classifier->train($samples, $targets);
|
$classifier->train($samples, $targets);
|
||||||
$this->assertEquals(0, $classifier->predict([0.1, 0.1]));
|
self::assertEquals(0, $classifier->predict([0.1, 0.1]));
|
||||||
$this->assertEquals(1, $classifier->predict([0.9, 0.9]));
|
self::assertEquals(1, $classifier->predict([0.9, 0.9]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPredictSingleSampleWithOnlineTraining(): void
|
public function testPredictSingleSampleWithOnlineTraining(): void
|
||||||
@ -102,8 +102,8 @@ class LogisticRegressionTest extends TestCase
|
|||||||
''
|
''
|
||||||
);
|
);
|
||||||
$classifier->train($samples, $targets);
|
$classifier->train($samples, $targets);
|
||||||
$this->assertEquals(0, $classifier->predict([0.1, 0.1]));
|
self::assertEquals(0, $classifier->predict([0.1, 0.1]));
|
||||||
$this->assertEquals(1, $classifier->predict([0.9, 0.9]));
|
self::assertEquals(1, $classifier->predict([0.9, 0.9]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPredictSingleSampleWithSSECost(): void
|
public function testPredictSingleSampleWithSSECost(): void
|
||||||
@ -118,8 +118,8 @@ class LogisticRegressionTest extends TestCase
|
|||||||
'L2'
|
'L2'
|
||||||
);
|
);
|
||||||
$classifier->train($samples, $targets);
|
$classifier->train($samples, $targets);
|
||||||
$this->assertEquals(0, $classifier->predict([0.1, 0.1]));
|
self::assertEquals(0, $classifier->predict([0.1, 0.1]));
|
||||||
$this->assertEquals(1, $classifier->predict([0.9, 0.9]));
|
self::assertEquals(1, $classifier->predict([0.9, 0.9]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPredictSingleSampleWithoutPenalty(): void
|
public function testPredictSingleSampleWithoutPenalty(): void
|
||||||
@ -134,8 +134,8 @@ class LogisticRegressionTest extends TestCase
|
|||||||
''
|
''
|
||||||
);
|
);
|
||||||
$classifier->train($samples, $targets);
|
$classifier->train($samples, $targets);
|
||||||
$this->assertEquals(0, $classifier->predict([0.1, 0.1]));
|
self::assertEquals(0, $classifier->predict([0.1, 0.1]));
|
||||||
$this->assertEquals(1, $classifier->predict([0.9, 0.9]));
|
self::assertEquals(1, $classifier->predict([0.9, 0.9]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPredictMultiClassSample(): void
|
public function testPredictMultiClassSample(): void
|
||||||
@ -151,9 +151,9 @@ class LogisticRegressionTest extends TestCase
|
|||||||
|
|
||||||
$classifier = new LogisticRegression();
|
$classifier = new LogisticRegression();
|
||||||
$classifier->train($samples, $targets);
|
$classifier->train($samples, $targets);
|
||||||
$this->assertEquals(0, $classifier->predict([0.5, 0.5]));
|
self::assertEquals(0, $classifier->predict([0.5, 0.5]));
|
||||||
$this->assertEquals(1, $classifier->predict([6.0, 5.0]));
|
self::assertEquals(1, $classifier->predict([6.0, 5.0]));
|
||||||
$this->assertEquals(2, $classifier->predict([3.0, 9.5]));
|
self::assertEquals(2, $classifier->predict([3.0, 9.5]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPredictProbabilitySingleSample(): void
|
public function testPredictProbabilitySingleSample(): void
|
||||||
@ -171,13 +171,13 @@ class LogisticRegressionTest extends TestCase
|
|||||||
|
|
||||||
$zero = $method->invoke($predictor, [0.1, 0.1], 0);
|
$zero = $method->invoke($predictor, [0.1, 0.1], 0);
|
||||||
$one = $method->invoke($predictor, [0.1, 0.1], 1);
|
$one = $method->invoke($predictor, [0.1, 0.1], 1);
|
||||||
$this->assertEquals(1, $zero + $one, '', 1e-6);
|
self::assertEquals(1, $zero + $one, '', 1e-6);
|
||||||
$this->assertTrue($zero > $one);
|
self::assertTrue($zero > $one);
|
||||||
|
|
||||||
$zero = $method->invoke($predictor, [0.9, 0.9], 0);
|
$zero = $method->invoke($predictor, [0.9, 0.9], 0);
|
||||||
$one = $method->invoke($predictor, [0.9, 0.9], 1);
|
$one = $method->invoke($predictor, [0.9, 0.9], 1);
|
||||||
$this->assertEquals(1, $zero + $one, '', 1e-6);
|
self::assertEquals(1, $zero + $one, '', 1e-6);
|
||||||
$this->assertTrue($zero < $one);
|
self::assertTrue($zero < $one);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPredictProbabilityMultiClassSample(): void
|
public function testPredictProbabilityMultiClassSample(): void
|
||||||
@ -213,10 +213,10 @@ class LogisticRegressionTest extends TestCase
|
|||||||
$two = $method->invoke($predictor, [3.0, 9.5], 2);
|
$two = $method->invoke($predictor, [3.0, 9.5], 2);
|
||||||
$not_two = $method->invoke($predictor, [3.0, 9.5], 'not_2');
|
$not_two = $method->invoke($predictor, [3.0, 9.5], 'not_2');
|
||||||
|
|
||||||
$this->assertEquals(1, $zero + $not_zero, '', 1e-6);
|
self::assertEquals(1, $zero + $not_zero, '', 1e-6);
|
||||||
$this->assertEquals(1, $one + $not_one, '', 1e-6);
|
self::assertEquals(1, $one + $not_one, '', 1e-6);
|
||||||
$this->assertEquals(1, $two + $not_two, '', 1e-6);
|
self::assertEquals(1, $two + $not_two, '', 1e-6);
|
||||||
$this->assertTrue($zero < $two);
|
self::assertTrue($zero < $two);
|
||||||
$this->assertTrue($one < $two);
|
self::assertTrue($one < $two);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,9 +33,9 @@ class PerceptronTest extends TestCase
|
|||||||
$classifier = new Perceptron(0.001, 5000);
|
$classifier = new Perceptron(0.001, 5000);
|
||||||
$classifier->setEarlyStop(false);
|
$classifier->setEarlyStop(false);
|
||||||
$classifier->train($samples, $targets);
|
$classifier->train($samples, $targets);
|
||||||
$this->assertEquals(0, $classifier->predict([0.1, 0.2]));
|
self::assertEquals(0, $classifier->predict([0.1, 0.2]));
|
||||||
$this->assertEquals(0, $classifier->predict([0, 1]));
|
self::assertEquals(0, $classifier->predict([0, 1]));
|
||||||
$this->assertEquals(1, $classifier->predict([1.1, 0.8]));
|
self::assertEquals(1, $classifier->predict([1.1, 0.8]));
|
||||||
|
|
||||||
// OR problem
|
// OR problem
|
||||||
$samples = [[0.1, 0.1], [0.4, 0.], [0., 0.3], [1, 0], [0, 1], [1, 1]];
|
$samples = [[0.1, 0.1], [0.4, 0.], [0., 0.3], [1, 0], [0, 1], [1, 1]];
|
||||||
@ -43,9 +43,9 @@ class PerceptronTest extends TestCase
|
|||||||
$classifier = new Perceptron(0.001, 5000, false);
|
$classifier = new Perceptron(0.001, 5000, false);
|
||||||
$classifier->setEarlyStop(false);
|
$classifier->setEarlyStop(false);
|
||||||
$classifier->train($samples, $targets);
|
$classifier->train($samples, $targets);
|
||||||
$this->assertEquals(0, $classifier->predict([0., 0.]));
|
self::assertEquals(0, $classifier->predict([0., 0.]));
|
||||||
$this->assertEquals(1, $classifier->predict([0.1, 0.99]));
|
self::assertEquals(1, $classifier->predict([0.1, 0.99]));
|
||||||
$this->assertEquals(1, $classifier->predict([1.1, 0.8]));
|
self::assertEquals(1, $classifier->predict([1.1, 0.8]));
|
||||||
|
|
||||||
// By use of One-v-Rest, Perceptron can perform multi-class classification
|
// By use of One-v-Rest, Perceptron can perform multi-class classification
|
||||||
// The samples should be separable by lines perpendicular to the dimensions
|
// The samples should be separable by lines perpendicular to the dimensions
|
||||||
@ -59,15 +59,15 @@ class PerceptronTest extends TestCase
|
|||||||
$classifier = new Perceptron();
|
$classifier = new Perceptron();
|
||||||
$classifier->setEarlyStop(false);
|
$classifier->setEarlyStop(false);
|
||||||
$classifier->train($samples, $targets);
|
$classifier->train($samples, $targets);
|
||||||
$this->assertEquals(0, $classifier->predict([0.5, 0.5]));
|
self::assertEquals(0, $classifier->predict([0.5, 0.5]));
|
||||||
$this->assertEquals(1, $classifier->predict([6.0, 5.0]));
|
self::assertEquals(1, $classifier->predict([6.0, 5.0]));
|
||||||
$this->assertEquals(2, $classifier->predict([3.0, 9.5]));
|
self::assertEquals(2, $classifier->predict([3.0, 9.5]));
|
||||||
|
|
||||||
// Extra partial training should lead to the same results.
|
// Extra partial training should lead to the same results.
|
||||||
$classifier->partialTrain([[0, 1], [1, 0]], [0, 0], [0, 1, 2]);
|
$classifier->partialTrain([[0, 1], [1, 0]], [0, 0], [0, 1, 2]);
|
||||||
$this->assertEquals(0, $classifier->predict([0.5, 0.5]));
|
self::assertEquals(0, $classifier->predict([0.5, 0.5]));
|
||||||
$this->assertEquals(1, $classifier->predict([6.0, 5.0]));
|
self::assertEquals(1, $classifier->predict([6.0, 5.0]));
|
||||||
$this->assertEquals(2, $classifier->predict([3.0, 9.5]));
|
self::assertEquals(2, $classifier->predict([3.0, 9.5]));
|
||||||
|
|
||||||
// Train should clear previous data.
|
// Train should clear previous data.
|
||||||
$samples = [
|
$samples = [
|
||||||
@ -77,9 +77,9 @@ class PerceptronTest extends TestCase
|
|||||||
];
|
];
|
||||||
$targets = [2, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1];
|
$targets = [2, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1];
|
||||||
$classifier->train($samples, $targets);
|
$classifier->train($samples, $targets);
|
||||||
$this->assertEquals(2, $classifier->predict([0.5, 0.5]));
|
self::assertEquals(2, $classifier->predict([0.5, 0.5]));
|
||||||
$this->assertEquals(0, $classifier->predict([6.0, 5.0]));
|
self::assertEquals(0, $classifier->predict([6.0, 5.0]));
|
||||||
$this->assertEquals(1, $classifier->predict([3.0, 9.5]));
|
self::assertEquals(1, $classifier->predict([3.0, 9.5]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSaveAndRestore(): void
|
public function testSaveAndRestore(): void
|
||||||
@ -93,12 +93,12 @@ class PerceptronTest extends TestCase
|
|||||||
$predicted = $classifier->predict($testSamples);
|
$predicted = $classifier->predict($testSamples);
|
||||||
|
|
||||||
$filename = 'perceptron-test-'.random_int(100, 999).'-'.uniqid('', false);
|
$filename = 'perceptron-test-'.random_int(100, 999).'-'.uniqid('', false);
|
||||||
$filepath = tempnam(sys_get_temp_dir(), $filename);
|
$filepath = (string) tempnam(sys_get_temp_dir(), $filename);
|
||||||
$modelManager = new ModelManager();
|
$modelManager = new ModelManager();
|
||||||
$modelManager->saveToFile($classifier, $filepath);
|
$modelManager->saveToFile($classifier, $filepath);
|
||||||
|
|
||||||
$restoredClassifier = $modelManager->restoreFromFile($filepath);
|
$restoredClassifier = $modelManager->restoreFromFile($filepath);
|
||||||
$this->assertEquals($classifier, $restoredClassifier);
|
self::assertEquals($classifier, $restoredClassifier);
|
||||||
$this->assertEquals($predicted, $restoredClassifier->predict($testSamples));
|
self::assertEquals($predicted, $restoredClassifier->predict($testSamples));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,21 +21,21 @@ class MLPClassifierTest extends TestCase
|
|||||||
{
|
{
|
||||||
$mlp = new MLPClassifier(2, [2], [0, 1]);
|
$mlp = new MLPClassifier(2, [2], [0, 1]);
|
||||||
|
|
||||||
$this->assertCount(3, $mlp->getLayers());
|
self::assertCount(3, $mlp->getLayers());
|
||||||
|
|
||||||
$layers = $mlp->getLayers();
|
$layers = $mlp->getLayers();
|
||||||
|
|
||||||
// input layer
|
// input layer
|
||||||
$this->assertCount(3, $layers[0]->getNodes());
|
self::assertCount(3, $layers[0]->getNodes());
|
||||||
$this->assertNotContainsOnly(Neuron::class, $layers[0]->getNodes());
|
self::assertNotContainsOnly(Neuron::class, $layers[0]->getNodes());
|
||||||
|
|
||||||
// hidden layer
|
// hidden layer
|
||||||
$this->assertCount(3, $layers[1]->getNodes());
|
self::assertCount(3, $layers[1]->getNodes());
|
||||||
$this->assertNotContainsOnly(Neuron::class, $layers[1]->getNodes());
|
self::assertNotContainsOnly(Neuron::class, $layers[1]->getNodes());
|
||||||
|
|
||||||
// output layer
|
// output layer
|
||||||
$this->assertCount(2, $layers[2]->getNodes());
|
self::assertCount(2, $layers[2]->getNodes());
|
||||||
$this->assertContainsOnly(Neuron::class, $layers[2]->getNodes());
|
self::assertContainsOnly(Neuron::class, $layers[2]->getNodes());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSynapsesGeneration(): void
|
public function testSynapsesGeneration(): void
|
||||||
@ -46,11 +46,11 @@ class MLPClassifierTest extends TestCase
|
|||||||
foreach ($layers[1]->getNodes() as $node) {
|
foreach ($layers[1]->getNodes() as $node) {
|
||||||
if ($node instanceof Neuron) {
|
if ($node instanceof Neuron) {
|
||||||
$synapses = $node->getSynapses();
|
$synapses = $node->getSynapses();
|
||||||
$this->assertCount(3, $synapses);
|
self::assertCount(3, $synapses);
|
||||||
|
|
||||||
$synapsesNodes = $this->getSynapsesNodes($synapses);
|
$synapsesNodes = $this->getSynapsesNodes($synapses);
|
||||||
foreach ($layers[0]->getNodes() as $prevNode) {
|
foreach ($layers[0]->getNodes() as $prevNode) {
|
||||||
$this->assertContains($prevNode, $synapsesNodes);
|
self::assertContains($prevNode, $synapsesNodes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -65,10 +65,10 @@ class MLPClassifierTest extends TestCase
|
|||||||
['a', 'b', 'a', 'b']
|
['a', 'b', 'a', 'b']
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertEquals('a', $network->predict([1, 0]));
|
self::assertEquals('a', $network->predict([1, 0]));
|
||||||
$this->assertEquals('b', $network->predict([0, 1]));
|
self::assertEquals('b', $network->predict([0, 1]));
|
||||||
$this->assertEquals('a', $network->predict([1, 1]));
|
self::assertEquals('a', $network->predict([1, 1]));
|
||||||
$this->assertEquals('b', $network->predict([0, 0]));
|
self::assertEquals('b', $network->predict([0, 0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testBackpropagationTrainingReset(): void
|
public function testBackpropagationTrainingReset(): void
|
||||||
@ -80,16 +80,16 @@ class MLPClassifierTest extends TestCase
|
|||||||
['a', 'b']
|
['a', 'b']
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertEquals('a', $network->predict([1, 0]));
|
self::assertEquals('a', $network->predict([1, 0]));
|
||||||
$this->assertEquals('b', $network->predict([0, 1]));
|
self::assertEquals('b', $network->predict([0, 1]));
|
||||||
|
|
||||||
$network->train(
|
$network->train(
|
||||||
[[1, 0], [0, 1]],
|
[[1, 0], [0, 1]],
|
||||||
['b', 'a']
|
['b', 'a']
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertEquals('b', $network->predict([1, 0]));
|
self::assertEquals('b', $network->predict([1, 0]));
|
||||||
$this->assertEquals('a', $network->predict([0, 1]));
|
self::assertEquals('a', $network->predict([0, 1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testBackpropagationPartialTraining(): void
|
public function testBackpropagationPartialTraining(): void
|
||||||
@ -101,18 +101,18 @@ class MLPClassifierTest extends TestCase
|
|||||||
['a', 'b']
|
['a', 'b']
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertEquals('a', $network->predict([1, 0]));
|
self::assertEquals('a', $network->predict([1, 0]));
|
||||||
$this->assertEquals('b', $network->predict([0, 1]));
|
self::assertEquals('b', $network->predict([0, 1]));
|
||||||
|
|
||||||
$network->partialTrain(
|
$network->partialTrain(
|
||||||
[[1, 1], [0, 0]],
|
[[1, 1], [0, 0]],
|
||||||
['a', 'b']
|
['a', 'b']
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertEquals('a', $network->predict([1, 0]));
|
self::assertEquals('a', $network->predict([1, 0]));
|
||||||
$this->assertEquals('b', $network->predict([0, 1]));
|
self::assertEquals('b', $network->predict([0, 1]));
|
||||||
$this->assertEquals('a', $network->predict([1, 1]));
|
self::assertEquals('a', $network->predict([1, 1]));
|
||||||
$this->assertEquals('b', $network->predict([0, 0]));
|
self::assertEquals('b', $network->predict([0, 0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testBackpropagationLearningMultilayer(): void
|
public function testBackpropagationLearningMultilayer(): void
|
||||||
@ -124,10 +124,10 @@ class MLPClassifierTest extends TestCase
|
|||||||
['a', 'b', 'a', 'c']
|
['a', 'b', 'a', 'c']
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertEquals('a', $network->predict([1, 0, 0, 0, 0]));
|
self::assertEquals('a', $network->predict([1, 0, 0, 0, 0]));
|
||||||
$this->assertEquals('b', $network->predict([0, 1, 1, 0, 0]));
|
self::assertEquals('b', $network->predict([0, 1, 1, 0, 0]));
|
||||||
$this->assertEquals('a', $network->predict([1, 1, 1, 1, 1]));
|
self::assertEquals('a', $network->predict([1, 1, 1, 1, 1]));
|
||||||
$this->assertEquals('c', $network->predict([0, 0, 0, 0, 0]));
|
self::assertEquals('c', $network->predict([0, 0, 0, 0, 0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testBackpropagationLearningMulticlass(): void
|
public function testBackpropagationLearningMulticlass(): void
|
||||||
@ -139,11 +139,11 @@ class MLPClassifierTest extends TestCase
|
|||||||
['a', 'b', 'a', 'a', 4]
|
['a', 'b', 'a', 'a', 4]
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertEquals('a', $network->predict([1, 0, 0, 0, 0]));
|
self::assertEquals('a', $network->predict([1, 0, 0, 0, 0]));
|
||||||
$this->assertEquals('b', $network->predict([0, 1, 0, 0, 0]));
|
self::assertEquals('b', $network->predict([0, 1, 0, 0, 0]));
|
||||||
$this->assertEquals('a', $network->predict([0, 0, 1, 1, 0]));
|
self::assertEquals('a', $network->predict([0, 0, 1, 1, 0]));
|
||||||
$this->assertEquals('a', $network->predict([1, 1, 1, 1, 1]));
|
self::assertEquals('a', $network->predict([1, 1, 1, 1, 1]));
|
||||||
$this->assertEquals(4, $network->predict([0, 0, 0, 0, 0]));
|
self::assertEquals(4, $network->predict([0, 0, 0, 0, 0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -157,10 +157,10 @@ class MLPClassifierTest extends TestCase
|
|||||||
['a', 'b', 'a', 'a']
|
['a', 'b', 'a', 'a']
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertEquals('a', $network->predict([1, 0, 0, 0, 0]));
|
self::assertEquals('a', $network->predict([1, 0, 0, 0, 0]));
|
||||||
$this->assertEquals('b', $network->predict([0, 1, 0, 0, 0]));
|
self::assertEquals('b', $network->predict([0, 1, 0, 0, 0]));
|
||||||
$this->assertEquals('a', $network->predict([0, 0, 1, 1, 0]));
|
self::assertEquals('a', $network->predict([0, 0, 1, 1, 0]));
|
||||||
$this->assertEquals('a', $network->predict([1, 1, 1, 1, 1]));
|
self::assertEquals('a', $network->predict([1, 1, 1, 1, 1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function activationFunctionsProvider(): array
|
public function activationFunctionsProvider(): array
|
||||||
@ -184,13 +184,13 @@ class MLPClassifierTest extends TestCase
|
|||||||
$predicted = $classifier->predict($testSamples);
|
$predicted = $classifier->predict($testSamples);
|
||||||
|
|
||||||
$filename = 'perceptron-test-'.random_int(100, 999).'-'.uniqid('', false);
|
$filename = 'perceptron-test-'.random_int(100, 999).'-'.uniqid('', false);
|
||||||
$filepath = tempnam(sys_get_temp_dir(), $filename);
|
$filepath = (string) tempnam(sys_get_temp_dir(), $filename);
|
||||||
$modelManager = new ModelManager();
|
$modelManager = new ModelManager();
|
||||||
$modelManager->saveToFile($classifier, $filepath);
|
$modelManager->saveToFile($classifier, $filepath);
|
||||||
|
|
||||||
$restoredClassifier = $modelManager->restoreFromFile($filepath);
|
$restoredClassifier = $modelManager->restoreFromFile($filepath);
|
||||||
$this->assertEquals($classifier, $restoredClassifier);
|
self::assertEquals($classifier, $restoredClassifier);
|
||||||
$this->assertEquals($predicted, $restoredClassifier->predict($testSamples));
|
self::assertEquals($predicted, $restoredClassifier->predict($testSamples));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSaveAndRestoreWithPartialTraining(): void
|
public function testSaveAndRestoreWithPartialTraining(): void
|
||||||
@ -201,11 +201,11 @@ class MLPClassifierTest extends TestCase
|
|||||||
['a', 'b']
|
['a', 'b']
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertEquals('a', $network->predict([1, 0]));
|
self::assertEquals('a', $network->predict([1, 0]));
|
||||||
$this->assertEquals('b', $network->predict([0, 1]));
|
self::assertEquals('b', $network->predict([0, 1]));
|
||||||
|
|
||||||
$filename = 'perceptron-test-'.random_int(100, 999).'-'.uniqid('', false);
|
$filename = 'perceptron-test-'.random_int(100, 999).'-'.uniqid('', false);
|
||||||
$filepath = tempnam(sys_get_temp_dir(), $filename);
|
$filepath = (string) tempnam(sys_get_temp_dir(), $filename);
|
||||||
$modelManager = new ModelManager();
|
$modelManager = new ModelManager();
|
||||||
$modelManager->saveToFile($network, $filepath);
|
$modelManager->saveToFile($network, $filepath);
|
||||||
|
|
||||||
@ -216,10 +216,10 @@ class MLPClassifierTest extends TestCase
|
|||||||
['a', 'b']
|
['a', 'b']
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertEquals('a', $restoredNetwork->predict([1, 0]));
|
self::assertEquals('a', $restoredNetwork->predict([1, 0]));
|
||||||
$this->assertEquals('b', $restoredNetwork->predict([0, 1]));
|
self::assertEquals('b', $restoredNetwork->predict([0, 1]));
|
||||||
$this->assertEquals('a', $restoredNetwork->predict([1, 1]));
|
self::assertEquals('a', $restoredNetwork->predict([1, 1]));
|
||||||
$this->assertEquals('b', $restoredNetwork->predict([0, 0]));
|
self::assertEquals('b', $restoredNetwork->predict([0, 0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testThrowExceptionOnInvalidLayersNumber(): void
|
public function testThrowExceptionOnInvalidLayersNumber(): void
|
||||||
@ -249,7 +249,7 @@ class MLPClassifierTest extends TestCase
|
|||||||
{
|
{
|
||||||
$output = (new MLPClassifier(2, [2, 2], ['T', 'F']))->getOutput();
|
$output = (new MLPClassifier(2, [2, 2], ['T', 'F']))->getOutput();
|
||||||
|
|
||||||
$this->assertEquals(['T', 'F'], array_keys($output));
|
self::assertEquals(['T', 'F'], array_keys($output));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getSynapsesNodes(array $synapses): array
|
private function getSynapsesNodes(array $synapses): array
|
||||||
|
@ -18,9 +18,9 @@ class NaiveBayesTest extends TestCase
|
|||||||
$classifier = new NaiveBayes();
|
$classifier = new NaiveBayes();
|
||||||
$classifier->train($samples, $labels);
|
$classifier->train($samples, $labels);
|
||||||
|
|
||||||
$this->assertEquals('a', $classifier->predict([3, 1, 1]));
|
self::assertEquals('a', $classifier->predict([3, 1, 1]));
|
||||||
$this->assertEquals('b', $classifier->predict([1, 4, 1]));
|
self::assertEquals('b', $classifier->predict([1, 4, 1]));
|
||||||
$this->assertEquals('c', $classifier->predict([1, 1, 6]));
|
self::assertEquals('c', $classifier->predict([1, 1, 6]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPredictArrayOfSamples(): void
|
public function testPredictArrayOfSamples(): void
|
||||||
@ -35,7 +35,7 @@ class NaiveBayesTest extends TestCase
|
|||||||
$classifier->train($trainSamples, $trainLabels);
|
$classifier->train($trainSamples, $trainLabels);
|
||||||
$predicted = $classifier->predict($testSamples);
|
$predicted = $classifier->predict($testSamples);
|
||||||
|
|
||||||
$this->assertEquals($testLabels, $predicted);
|
self::assertEquals($testLabels, $predicted);
|
||||||
|
|
||||||
// Feed an extra set of training data.
|
// Feed an extra set of training data.
|
||||||
$samples = [[1, 1, 6]];
|
$samples = [[1, 1, 6]];
|
||||||
@ -44,7 +44,7 @@ class NaiveBayesTest extends TestCase
|
|||||||
|
|
||||||
$testSamples = [[1, 1, 6], [5, 1, 1]];
|
$testSamples = [[1, 1, 6], [5, 1, 1]];
|
||||||
$testLabels = ['d', 'a'];
|
$testLabels = ['d', 'a'];
|
||||||
$this->assertEquals($testLabels, $classifier->predict($testSamples));
|
self::assertEquals($testLabels, $classifier->predict($testSamples));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSaveAndRestore(): void
|
public function testSaveAndRestore(): void
|
||||||
@ -59,13 +59,13 @@ class NaiveBayesTest extends TestCase
|
|||||||
$predicted = $classifier->predict($testSamples);
|
$predicted = $classifier->predict($testSamples);
|
||||||
|
|
||||||
$filename = 'naive-bayes-test-'.random_int(100, 999).'-'.uniqid('', false);
|
$filename = 'naive-bayes-test-'.random_int(100, 999).'-'.uniqid('', false);
|
||||||
$filepath = tempnam(sys_get_temp_dir(), $filename);
|
$filepath = (string) tempnam(sys_get_temp_dir(), $filename);
|
||||||
$modelManager = new ModelManager();
|
$modelManager = new ModelManager();
|
||||||
$modelManager->saveToFile($classifier, $filepath);
|
$modelManager->saveToFile($classifier, $filepath);
|
||||||
|
|
||||||
$restoredClassifier = $modelManager->restoreFromFile($filepath);
|
$restoredClassifier = $modelManager->restoreFromFile($filepath);
|
||||||
$this->assertEquals($classifier, $restoredClassifier);
|
self::assertEquals($classifier, $restoredClassifier);
|
||||||
$this->assertEquals($predicted, $restoredClassifier->predict($testSamples));
|
self::assertEquals($predicted, $restoredClassifier->predict($testSamples));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPredictSimpleNumericLabels(): void
|
public function testPredictSimpleNumericLabels(): void
|
||||||
@ -76,9 +76,9 @@ class NaiveBayesTest extends TestCase
|
|||||||
$classifier = new NaiveBayes();
|
$classifier = new NaiveBayes();
|
||||||
$classifier->train($samples, $labels);
|
$classifier->train($samples, $labels);
|
||||||
|
|
||||||
$this->assertEquals('1996', $classifier->predict([3, 1, 1]));
|
self::assertEquals('1996', $classifier->predict([3, 1, 1]));
|
||||||
$this->assertEquals('1997', $classifier->predict([1, 4, 1]));
|
self::assertEquals('1997', $classifier->predict([1, 4, 1]));
|
||||||
$this->assertEquals('1998', $classifier->predict([1, 1, 6]));
|
self::assertEquals('1998', $classifier->predict([1, 1, 6]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPredictArrayOfSamplesNumericalLabels(): void
|
public function testPredictArrayOfSamplesNumericalLabels(): void
|
||||||
@ -93,7 +93,7 @@ class NaiveBayesTest extends TestCase
|
|||||||
$classifier->train($trainSamples, $trainLabels);
|
$classifier->train($trainSamples, $trainLabels);
|
||||||
$predicted = $classifier->predict($testSamples);
|
$predicted = $classifier->predict($testSamples);
|
||||||
|
|
||||||
$this->assertEquals($testLabels, $predicted);
|
self::assertEquals($testLabels, $predicted);
|
||||||
|
|
||||||
// Feed an extra set of training data.
|
// Feed an extra set of training data.
|
||||||
$samples = [[1, 1, 6]];
|
$samples = [[1, 1, 6]];
|
||||||
@ -102,7 +102,7 @@ class NaiveBayesTest extends TestCase
|
|||||||
|
|
||||||
$testSamples = [[1, 1, 6], [5, 1, 1]];
|
$testSamples = [[1, 1, 6], [5, 1, 1]];
|
||||||
$testLabels = ['1999', '1996'];
|
$testLabels = ['1999', '1996'];
|
||||||
$this->assertEquals($testLabels, $classifier->predict($testSamples));
|
self::assertEquals($testLabels, $classifier->predict($testSamples));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSaveAndRestoreNumericLabels(): void
|
public function testSaveAndRestoreNumericLabels(): void
|
||||||
@ -117,12 +117,12 @@ class NaiveBayesTest extends TestCase
|
|||||||
$predicted = $classifier->predict($testSamples);
|
$predicted = $classifier->predict($testSamples);
|
||||||
|
|
||||||
$filename = 'naive-bayes-test-'.random_int(100, 999).'-'.uniqid('', false);
|
$filename = 'naive-bayes-test-'.random_int(100, 999).'-'.uniqid('', false);
|
||||||
$filepath = tempnam(sys_get_temp_dir(), $filename);
|
$filepath = (string) tempnam(sys_get_temp_dir(), $filename);
|
||||||
$modelManager = new ModelManager();
|
$modelManager = new ModelManager();
|
||||||
$modelManager->saveToFile($classifier, $filepath);
|
$modelManager->saveToFile($classifier, $filepath);
|
||||||
|
|
||||||
$restoredClassifier = $modelManager->restoreFromFile($filepath);
|
$restoredClassifier = $modelManager->restoreFromFile($filepath);
|
||||||
$this->assertEquals($classifier, $restoredClassifier);
|
self::assertEquals($classifier, $restoredClassifier);
|
||||||
$this->assertEquals($predicted, $restoredClassifier->predict($testSamples));
|
self::assertEquals($predicted, $restoredClassifier->predict($testSamples));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,15 +19,15 @@ class SVCTest extends TestCase
|
|||||||
$classifier = new SVC(Kernel::LINEAR, $cost = 1000);
|
$classifier = new SVC(Kernel::LINEAR, $cost = 1000);
|
||||||
$classifier->train($samples, $labels);
|
$classifier->train($samples, $labels);
|
||||||
|
|
||||||
$this->assertEquals('b', $classifier->predict([3, 2]));
|
self::assertEquals('b', $classifier->predict([3, 2]));
|
||||||
$this->assertEquals('b', $classifier->predict([5, 1]));
|
self::assertEquals('b', $classifier->predict([5, 1]));
|
||||||
$this->assertEquals('b', $classifier->predict([4, 3]));
|
self::assertEquals('b', $classifier->predict([4, 3]));
|
||||||
$this->assertEquals('b', $classifier->predict([4, -5]));
|
self::assertEquals('b', $classifier->predict([4, -5]));
|
||||||
|
|
||||||
$this->assertEquals('a', $classifier->predict([2, 3]));
|
self::assertEquals('a', $classifier->predict([2, 3]));
|
||||||
$this->assertEquals('a', $classifier->predict([1, 2]));
|
self::assertEquals('a', $classifier->predict([1, 2]));
|
||||||
$this->assertEquals('a', $classifier->predict([1, 5]));
|
self::assertEquals('a', $classifier->predict([1, 5]));
|
||||||
$this->assertEquals('a', $classifier->predict([3, 10]));
|
self::assertEquals('a', $classifier->predict([3, 10]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPredictArrayOfSamplesWithLinearKernel(): void
|
public function testPredictArrayOfSamplesWithLinearKernel(): void
|
||||||
@ -42,7 +42,7 @@ class SVCTest extends TestCase
|
|||||||
$classifier->train($trainSamples, $trainLabels);
|
$classifier->train($trainSamples, $trainLabels);
|
||||||
$predictions = $classifier->predict($testSamples);
|
$predictions = $classifier->predict($testSamples);
|
||||||
|
|
||||||
$this->assertEquals($testLabels, $predictions);
|
self::assertEquals($testLabels, $predictions);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSaveAndRestore(): void
|
public function testSaveAndRestore(): void
|
||||||
@ -57,14 +57,14 @@ class SVCTest extends TestCase
|
|||||||
$classifier->train($trainSamples, $trainLabels);
|
$classifier->train($trainSamples, $trainLabels);
|
||||||
$predicted = $classifier->predict($testSamples);
|
$predicted = $classifier->predict($testSamples);
|
||||||
|
|
||||||
$filepath = tempnam(sys_get_temp_dir(), uniqid('svc-test', true));
|
$filepath = (string) tempnam(sys_get_temp_dir(), uniqid('svc-test', true));
|
||||||
$modelManager = new ModelManager();
|
$modelManager = new ModelManager();
|
||||||
$modelManager->saveToFile($classifier, $filepath);
|
$modelManager->saveToFile($classifier, $filepath);
|
||||||
|
|
||||||
$restoredClassifier = $modelManager->restoreFromFile($filepath);
|
$restoredClassifier = $modelManager->restoreFromFile($filepath);
|
||||||
$this->assertEquals($classifier, $restoredClassifier);
|
self::assertEquals($classifier, $restoredClassifier);
|
||||||
$this->assertEquals($predicted, $restoredClassifier->predict($testSamples));
|
self::assertEquals($predicted, $restoredClassifier->predict($testSamples));
|
||||||
$this->assertEquals($predicted, $testLabels);
|
self::assertEquals($predicted, $testLabels);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testWithNonDotDecimalLocale(): void
|
public function testWithNonDotDecimalLocale(): void
|
||||||
@ -81,8 +81,8 @@ class SVCTest extends TestCase
|
|||||||
$classifier = new SVC(Kernel::LINEAR, $cost = 1000);
|
$classifier = new SVC(Kernel::LINEAR, $cost = 1000);
|
||||||
$classifier->train($trainSamples, $trainLabels);
|
$classifier->train($trainSamples, $trainLabels);
|
||||||
|
|
||||||
$this->assertEquals($classifier->predict($testSamples), $testLabels);
|
self::assertEquals($classifier->predict($testSamples), $testLabels);
|
||||||
|
|
||||||
setlocale(LC_NUMERIC, $currentLocale);
|
setlocale(LC_NUMERIC, (string) $currentLocale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ class DBSCANTest extends TestCase
|
|||||||
|
|
||||||
$dbscan = new DBSCAN($epsilon = 2, $minSamples = 3);
|
$dbscan = new DBSCAN($epsilon = 2, $minSamples = 3);
|
||||||
|
|
||||||
$this->assertEquals($clustered, $dbscan->cluster($samples));
|
self::assertEquals($clustered, $dbscan->cluster($samples));
|
||||||
|
|
||||||
$samples = [[1, 1], [6, 6], [1, -1], [5, 6], [-1, -1], [7, 8], [-1, 1], [7, 7]];
|
$samples = [[1, 1], [6, 6], [1, -1], [5, 6], [-1, -1], [7, 8], [-1, 1], [7, 7]];
|
||||||
$clustered = [
|
$clustered = [
|
||||||
@ -29,7 +29,7 @@ class DBSCANTest extends TestCase
|
|||||||
|
|
||||||
$dbscan = new DBSCAN($epsilon = 3, $minSamples = 4);
|
$dbscan = new DBSCAN($epsilon = 3, $minSamples = 4);
|
||||||
|
|
||||||
$this->assertEquals($clustered, $dbscan->cluster($samples));
|
self::assertEquals($clustered, $dbscan->cluster($samples));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testDBSCANSamplesClusteringAssociative(): void
|
public function testDBSCANSamplesClusteringAssociative(): void
|
||||||
@ -57,7 +57,7 @@ class DBSCANTest extends TestCase
|
|||||||
|
|
||||||
$dbscan = new DBSCAN($epsilon = 3, $minSamples = 2);
|
$dbscan = new DBSCAN($epsilon = 3, $minSamples = 2);
|
||||||
|
|
||||||
$this->assertEquals($clustered, $dbscan->cluster($samples));
|
self::assertEquals($clustered, $dbscan->cluster($samples));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testClusterEpsilonSmall(): void
|
public function testClusterEpsilonSmall(): void
|
||||||
@ -68,7 +68,7 @@ class DBSCANTest extends TestCase
|
|||||||
|
|
||||||
$dbscan = new DBSCAN($epsilon = 0.5, $minSamples = 2);
|
$dbscan = new DBSCAN($epsilon = 0.5, $minSamples = 2);
|
||||||
|
|
||||||
$this->assertEquals($clustered, $dbscan->cluster($samples));
|
self::assertEquals($clustered, $dbscan->cluster($samples));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testClusterEpsilonBoundary(): void
|
public function testClusterEpsilonBoundary(): void
|
||||||
@ -79,7 +79,7 @@ class DBSCANTest extends TestCase
|
|||||||
|
|
||||||
$dbscan = new DBSCAN($epsilon = 1.0, $minSamples = 2);
|
$dbscan = new DBSCAN($epsilon = 1.0, $minSamples = 2);
|
||||||
|
|
||||||
$this->assertEquals($clustered, $dbscan->cluster($samples));
|
self::assertEquals($clustered, $dbscan->cluster($samples));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testClusterEpsilonLarge(): void
|
public function testClusterEpsilonLarge(): void
|
||||||
@ -91,6 +91,6 @@ class DBSCANTest extends TestCase
|
|||||||
|
|
||||||
$dbscan = new DBSCAN($epsilon = 1.5, $minSamples = 2);
|
$dbscan = new DBSCAN($epsilon = 1.5, $minSamples = 2);
|
||||||
|
|
||||||
$this->assertEquals($clustered, $dbscan->cluster($samples));
|
self::assertEquals($clustered, $dbscan->cluster($samples));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,40 +10,40 @@ use PHPUnit\Framework\TestCase;
|
|||||||
|
|
||||||
class FuzzyCMeansTest extends TestCase
|
class FuzzyCMeansTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testFCMSamplesClustering()
|
public function testFCMSamplesClustering(): void
|
||||||
{
|
{
|
||||||
$samples = [[1, 1], [8, 7], [1, 2], [7, 8], [2, 1], [8, 9]];
|
$samples = [[1, 1], [8, 7], [1, 2], [7, 8], [2, 1], [8, 9]];
|
||||||
$fcm = new FuzzyCMeans(2);
|
$fcm = new FuzzyCMeans(2);
|
||||||
$clusters = $fcm->cluster($samples);
|
$clusters = $fcm->cluster($samples);
|
||||||
$this->assertCount(2, $clusters);
|
self::assertCount(2, $clusters);
|
||||||
foreach ($samples as $index => $sample) {
|
foreach ($samples as $index => $sample) {
|
||||||
if (in_array($sample, $clusters[0], true) || in_array($sample, $clusters[1], true)) {
|
if (in_array($sample, $clusters[0], true) || in_array($sample, $clusters[1], true)) {
|
||||||
unset($samples[$index]);
|
unset($samples[$index]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->assertCount(0, $samples);
|
self::assertCount(0, $samples);
|
||||||
|
|
||||||
return $fcm;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testMembershipMatrix(): void
|
public function testMembershipMatrix(): void
|
||||||
{
|
{
|
||||||
$fcm = $this->testFCMSamplesClustering();
|
$fcm = new FuzzyCMeans(2);
|
||||||
|
$fcm->cluster([[1, 1], [8, 7], [1, 2], [7, 8], [2, 1], [8, 9]]);
|
||||||
|
|
||||||
$clusterCount = 2;
|
$clusterCount = 2;
|
||||||
$sampleCount = 6;
|
$sampleCount = 6;
|
||||||
$matrix = $fcm->getMembershipMatrix();
|
$matrix = $fcm->getMembershipMatrix();
|
||||||
$this->assertCount($clusterCount, $matrix);
|
self::assertCount($clusterCount, $matrix);
|
||||||
foreach ($matrix as $row) {
|
foreach ($matrix as $row) {
|
||||||
$this->assertCount($sampleCount, $row);
|
self::assertCount($sampleCount, $row);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transpose of the matrix
|
// Transpose of the matrix
|
||||||
array_unshift($matrix, null);
|
array_unshift($matrix, null);
|
||||||
$matrix = call_user_func_array('array_map', $matrix);
|
$matrix = array_map(...$matrix);
|
||||||
// All column totals should be equal to 1 (100% membership)
|
// All column totals should be equal to 1 (100% membership)
|
||||||
foreach ($matrix as $col) {
|
foreach ($matrix as $col) {
|
||||||
$this->assertEquals(1, array_sum($col));
|
self::assertEquals(1, array_sum($col));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ class ClusterTest extends TestCase
|
|||||||
$cluster = new Cluster(new Space(2), [1, 2]);
|
$cluster = new Cluster(new Space(2), [1, 2]);
|
||||||
$cluster->attach(new Point([1, 1]));
|
$cluster->attach(new Point([1, 1]));
|
||||||
|
|
||||||
$this->assertSame([
|
self::assertSame([
|
||||||
'centroid' => [1, 2],
|
'centroid' => [1, 2],
|
||||||
'points' => [
|
'points' => [
|
||||||
[1, 1],
|
[1, 1],
|
||||||
@ -42,8 +42,8 @@ class ClusterTest extends TestCase
|
|||||||
|
|
||||||
$detachedPoint = $cluster->detach($point);
|
$detachedPoint = $cluster->detach($point);
|
||||||
|
|
||||||
$this->assertSame($detachedPoint, $point);
|
self::assertSame($detachedPoint, $point);
|
||||||
$this->assertNotContains($point, $cluster->getPoints());
|
self::assertNotContains($point, $cluster->getPoints());
|
||||||
$this->assertCount(1, $cluster);
|
self::assertCount(1, $cluster);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ class KMeansTest extends TestCase
|
|||||||
$kmeans = new KMeans(2);
|
$kmeans = new KMeans(2);
|
||||||
$clusters = $kmeans->cluster($samples);
|
$clusters = $kmeans->cluster($samples);
|
||||||
|
|
||||||
$this->assertCount(2, $clusters);
|
self::assertCount(2, $clusters);
|
||||||
|
|
||||||
foreach ($samples as $index => $sample) {
|
foreach ($samples as $index => $sample) {
|
||||||
if (in_array($sample, $clusters[0], true) || in_array($sample, $clusters[1], true)) {
|
if (in_array($sample, $clusters[0], true) || in_array($sample, $clusters[1], true)) {
|
||||||
@ -25,7 +25,7 @@ class KMeansTest extends TestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->assertCount(0, $samples);
|
self::assertCount(0, $samples);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testKMeansSamplesLabeledClustering(): void
|
public function testKMeansSamplesLabeledClustering(): void
|
||||||
@ -42,16 +42,16 @@ class KMeansTest extends TestCase
|
|||||||
$kmeans = new KMeans(2);
|
$kmeans = new KMeans(2);
|
||||||
$clusters = $kmeans->cluster($samples);
|
$clusters = $kmeans->cluster($samples);
|
||||||
|
|
||||||
$this->assertCount(2, $clusters);
|
self::assertCount(2, $clusters);
|
||||||
|
|
||||||
foreach ($samples as $index => $sample) {
|
foreach ($samples as $index => $sample) {
|
||||||
if (in_array($sample, $clusters[0], true) || in_array($sample, $clusters[1], true)) {
|
if (in_array($sample, $clusters[0], true) || in_array($sample, $clusters[1], true)) {
|
||||||
$this->assertArrayHasKey($index, $clusters[0] + $clusters[1]);
|
self::assertArrayHasKey($index, $clusters[0] + $clusters[1]);
|
||||||
unset($samples[$index]);
|
unset($samples[$index]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->assertCount(0, $samples);
|
self::assertCount(0, $samples);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testKMeansInitializationMethods(): void
|
public function testKMeansInitializationMethods(): void
|
||||||
@ -71,11 +71,11 @@ class KMeansTest extends TestCase
|
|||||||
|
|
||||||
$kmeans = new KMeans(4, KMeans::INIT_KMEANS_PLUS_PLUS);
|
$kmeans = new KMeans(4, KMeans::INIT_KMEANS_PLUS_PLUS);
|
||||||
$clusters = $kmeans->cluster($samples);
|
$clusters = $kmeans->cluster($samples);
|
||||||
$this->assertCount(4, $clusters);
|
self::assertCount(4, $clusters);
|
||||||
|
|
||||||
$kmeans = new KMeans(4, KMeans::INIT_RANDOM);
|
$kmeans = new KMeans(4, KMeans::INIT_RANDOM);
|
||||||
$clusters = $kmeans->cluster($samples);
|
$clusters = $kmeans->cluster($samples);
|
||||||
$this->assertCount(4, $clusters);
|
self::assertCount(4, $clusters);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testThrowExceptionOnInvalidClusterNumber(): void
|
public function testThrowExceptionOnInvalidClusterNumber(): void
|
||||||
|
@ -32,13 +32,13 @@ class RandomSplitTest extends TestCase
|
|||||||
|
|
||||||
$randomSplit = new RandomSplit($dataset, 0.5);
|
$randomSplit = new RandomSplit($dataset, 0.5);
|
||||||
|
|
||||||
$this->assertCount(2, $randomSplit->getTestSamples());
|
self::assertCount(2, $randomSplit->getTestSamples());
|
||||||
$this->assertCount(2, $randomSplit->getTrainSamples());
|
self::assertCount(2, $randomSplit->getTrainSamples());
|
||||||
|
|
||||||
$randomSplit2 = new RandomSplit($dataset, 0.25);
|
$randomSplit2 = new RandomSplit($dataset, 0.25);
|
||||||
|
|
||||||
$this->assertCount(1, $randomSplit2->getTestSamples());
|
self::assertCount(1, $randomSplit2->getTestSamples());
|
||||||
$this->assertCount(3, $randomSplit2->getTrainSamples());
|
self::assertCount(3, $randomSplit2->getTrainSamples());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testDatasetRandomSplitWithSameSeed(): void
|
public function testDatasetRandomSplitWithSameSeed(): void
|
||||||
@ -53,10 +53,10 @@ class RandomSplitTest extends TestCase
|
|||||||
$randomSplit1 = new RandomSplit($dataset, 0.5, $seed);
|
$randomSplit1 = new RandomSplit($dataset, 0.5, $seed);
|
||||||
$randomSplit2 = new RandomSplit($dataset, 0.5, $seed);
|
$randomSplit2 = new RandomSplit($dataset, 0.5, $seed);
|
||||||
|
|
||||||
$this->assertEquals($randomSplit1->getTestLabels(), $randomSplit2->getTestLabels());
|
self::assertEquals($randomSplit1->getTestLabels(), $randomSplit2->getTestLabels());
|
||||||
$this->assertEquals($randomSplit1->getTestSamples(), $randomSplit2->getTestSamples());
|
self::assertEquals($randomSplit1->getTestSamples(), $randomSplit2->getTestSamples());
|
||||||
$this->assertEquals($randomSplit1->getTrainLabels(), $randomSplit2->getTrainLabels());
|
self::assertEquals($randomSplit1->getTrainLabels(), $randomSplit2->getTrainLabels());
|
||||||
$this->assertEquals($randomSplit1->getTrainSamples(), $randomSplit2->getTrainSamples());
|
self::assertEquals($randomSplit1->getTrainSamples(), $randomSplit2->getTrainSamples());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testDatasetRandomSplitWithDifferentSeed(): void
|
public function testDatasetRandomSplitWithDifferentSeed(): void
|
||||||
@ -69,10 +69,10 @@ class RandomSplitTest extends TestCase
|
|||||||
$randomSplit1 = new RandomSplit($dataset, 0.5, 4321);
|
$randomSplit1 = new RandomSplit($dataset, 0.5, 4321);
|
||||||
$randomSplit2 = new RandomSplit($dataset, 0.5, 1234);
|
$randomSplit2 = new RandomSplit($dataset, 0.5, 1234);
|
||||||
|
|
||||||
$this->assertNotEquals($randomSplit1->getTestLabels(), $randomSplit2->getTestLabels());
|
self::assertNotEquals($randomSplit1->getTestLabels(), $randomSplit2->getTestLabels());
|
||||||
$this->assertNotEquals($randomSplit1->getTestSamples(), $randomSplit2->getTestSamples());
|
self::assertNotEquals($randomSplit1->getTestSamples(), $randomSplit2->getTestSamples());
|
||||||
$this->assertNotEquals($randomSplit1->getTrainLabels(), $randomSplit2->getTrainLabels());
|
self::assertNotEquals($randomSplit1->getTrainLabels(), $randomSplit2->getTrainLabels());
|
||||||
$this->assertNotEquals($randomSplit1->getTrainSamples(), $randomSplit2->getTrainSamples());
|
self::assertNotEquals($randomSplit1->getTrainSamples(), $randomSplit2->getTrainSamples());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testRandomSplitCorrectSampleAndLabelPosition(): void
|
public function testRandomSplitCorrectSampleAndLabelPosition(): void
|
||||||
@ -84,9 +84,9 @@ class RandomSplitTest extends TestCase
|
|||||||
|
|
||||||
$randomSplit = new RandomSplit($dataset, 0.5);
|
$randomSplit = new RandomSplit($dataset, 0.5);
|
||||||
|
|
||||||
$this->assertEquals($randomSplit->getTestSamples()[0][0], $randomSplit->getTestLabels()[0]);
|
self::assertEquals($randomSplit->getTestSamples()[0][0], $randomSplit->getTestLabels()[0]);
|
||||||
$this->assertEquals($randomSplit->getTestSamples()[1][0], $randomSplit->getTestLabels()[1]);
|
self::assertEquals($randomSplit->getTestSamples()[1][0], $randomSplit->getTestLabels()[1]);
|
||||||
$this->assertEquals($randomSplit->getTrainSamples()[0][0], $randomSplit->getTrainLabels()[0]);
|
self::assertEquals($randomSplit->getTrainSamples()[0][0], $randomSplit->getTrainLabels()[0]);
|
||||||
$this->assertEquals($randomSplit->getTrainSamples()[1][0], $randomSplit->getTrainLabels()[1]);
|
self::assertEquals($randomSplit->getTrainSamples()[1][0], $randomSplit->getTrainLabels()[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,13 +19,13 @@ class StratifiedRandomSplitTest extends TestCase
|
|||||||
|
|
||||||
$split = new StratifiedRandomSplit($dataset, 0.5);
|
$split = new StratifiedRandomSplit($dataset, 0.5);
|
||||||
|
|
||||||
$this->assertEquals(2, $this->countSamplesByTarget($split->getTestLabels(), 'a'));
|
self::assertEquals(2, $this->countSamplesByTarget($split->getTestLabels(), 'a'));
|
||||||
$this->assertEquals(2, $this->countSamplesByTarget($split->getTestLabels(), 'b'));
|
self::assertEquals(2, $this->countSamplesByTarget($split->getTestLabels(), 'b'));
|
||||||
|
|
||||||
$split = new StratifiedRandomSplit($dataset, 0.25);
|
$split = new StratifiedRandomSplit($dataset, 0.25);
|
||||||
|
|
||||||
$this->assertEquals(1, $this->countSamplesByTarget($split->getTestLabels(), 'a'));
|
self::assertEquals(1, $this->countSamplesByTarget($split->getTestLabels(), 'a'));
|
||||||
$this->assertEquals(1, $this->countSamplesByTarget($split->getTestLabels(), 'b'));
|
self::assertEquals(1, $this->countSamplesByTarget($split->getTestLabels(), 'b'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testDatasetStratifiedRandomSplitWithEvenDistributionAndNumericTargets(): void
|
public function testDatasetStratifiedRandomSplitWithEvenDistributionAndNumericTargets(): void
|
||||||
@ -37,16 +37,19 @@ class StratifiedRandomSplitTest extends TestCase
|
|||||||
|
|
||||||
$split = new StratifiedRandomSplit($dataset, 0.5);
|
$split = new StratifiedRandomSplit($dataset, 0.5);
|
||||||
|
|
||||||
$this->assertEquals(2, $this->countSamplesByTarget($split->getTestLabels(), 1));
|
self::assertEquals(2, $this->countSamplesByTarget($split->getTestLabels(), 1));
|
||||||
$this->assertEquals(2, $this->countSamplesByTarget($split->getTestLabels(), 2));
|
self::assertEquals(2, $this->countSamplesByTarget($split->getTestLabels(), 2));
|
||||||
|
|
||||||
$split = new StratifiedRandomSplit($dataset, 0.25);
|
$split = new StratifiedRandomSplit($dataset, 0.25);
|
||||||
|
|
||||||
$this->assertEquals(1, $this->countSamplesByTarget($split->getTestLabels(), 1));
|
self::assertEquals(1, $this->countSamplesByTarget($split->getTestLabels(), 1));
|
||||||
$this->assertEquals(1, $this->countSamplesByTarget($split->getTestLabels(), 2));
|
self::assertEquals(1, $this->countSamplesByTarget($split->getTestLabels(), 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function countSamplesByTarget($splitTargets, $countTarget): int
|
/**
|
||||||
|
* @param string|int $countTarget
|
||||||
|
*/
|
||||||
|
private function countSamplesByTarget(array $splitTargets, $countTarget): int
|
||||||
{
|
{
|
||||||
$count = 0;
|
$count = 0;
|
||||||
foreach ($splitTargets as $target) {
|
foreach ($splitTargets as $target) {
|
||||||
|
@ -23,8 +23,8 @@ class ArrayDatasetTest extends TestCase
|
|||||||
$labels = ['a', 'a', 'b', 'b']
|
$labels = ['a', 'a', 'b', 'b']
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertEquals($samples, $dataset->getSamples());
|
self::assertEquals($samples, $dataset->getSamples());
|
||||||
$this->assertEquals($labels, $dataset->getTargets());
|
self::assertEquals($labels, $dataset->getTargets());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testRemoveColumns(): void
|
public function testRemoveColumns(): void
|
||||||
@ -35,6 +35,6 @@ class ArrayDatasetTest extends TestCase
|
|||||||
);
|
);
|
||||||
$dataset->removeColumns([0, 2]);
|
$dataset->removeColumns([0, 2]);
|
||||||
|
|
||||||
$this->assertEquals([[2, 4], [3, 5], [4, 6], [5, 7]], $dataset->getSamples());
|
self::assertEquals([[2, 4], [3, 5], [4, 6], [5, 7]], $dataset->getSamples());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,8 +22,8 @@ class CsvDatasetTest extends TestCase
|
|||||||
|
|
||||||
$dataset = new CsvDataset($filePath, 2, true);
|
$dataset = new CsvDataset($filePath, 2, true);
|
||||||
|
|
||||||
$this->assertCount(10, $dataset->getSamples());
|
self::assertCount(10, $dataset->getSamples());
|
||||||
$this->assertCount(10, $dataset->getTargets());
|
self::assertCount(10, $dataset->getTargets());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSampleCsvDatasetWithoutHeaderRow(): void
|
public function testSampleCsvDatasetWithoutHeaderRow(): void
|
||||||
@ -32,8 +32,8 @@ class CsvDatasetTest extends TestCase
|
|||||||
|
|
||||||
$dataset = new CsvDataset($filePath, 2, false);
|
$dataset = new CsvDataset($filePath, 2, false);
|
||||||
|
|
||||||
$this->assertCount(11, $dataset->getSamples());
|
self::assertCount(11, $dataset->getSamples());
|
||||||
$this->assertCount(11, $dataset->getTargets());
|
self::assertCount(11, $dataset->getTargets());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testLongCsvDataset(): void
|
public function testLongCsvDataset(): void
|
||||||
@ -42,7 +42,7 @@ class CsvDatasetTest extends TestCase
|
|||||||
|
|
||||||
$dataset = new CsvDataset($filePath, 1000, false);
|
$dataset = new CsvDataset($filePath, 1000, false);
|
||||||
|
|
||||||
$this->assertCount(1000, $dataset->getSamples()[0]);
|
self::assertCount(1000, $dataset->getSamples()[0]);
|
||||||
$this->assertEquals('label', $dataset->getTargets()[0]);
|
self::assertEquals('label', $dataset->getTargets()[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,10 +14,10 @@ class GlassDatasetTest extends TestCase
|
|||||||
$glass = new GlassDataset();
|
$glass = new GlassDataset();
|
||||||
|
|
||||||
// whole dataset
|
// whole dataset
|
||||||
$this->assertCount(214, $glass->getSamples());
|
self::assertCount(214, $glass->getSamples());
|
||||||
$this->assertCount(214, $glass->getTargets());
|
self::assertCount(214, $glass->getTargets());
|
||||||
|
|
||||||
// one sample features count
|
// one sample features count
|
||||||
$this->assertCount(9, $glass->getSamples()[0]);
|
self::assertCount(9, $glass->getSamples()[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,10 +14,10 @@ class IrisDatasetTest extends TestCase
|
|||||||
$iris = new IrisDataset();
|
$iris = new IrisDataset();
|
||||||
|
|
||||||
// whole dataset
|
// whole dataset
|
||||||
$this->assertCount(150, $iris->getSamples());
|
self::assertCount(150, $iris->getSamples());
|
||||||
$this->assertCount(150, $iris->getTargets());
|
self::assertCount(150, $iris->getTargets());
|
||||||
|
|
||||||
// one sample features count
|
// one sample features count
|
||||||
$this->assertCount(4, $iris->getSamples()[0]);
|
self::assertCount(4, $iris->getSamples()[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,10 +14,10 @@ class WineDatasetTest extends TestCase
|
|||||||
$wine = new WineDataset();
|
$wine = new WineDataset();
|
||||||
|
|
||||||
// whole dataset
|
// whole dataset
|
||||||
$this->assertCount(178, $wine->getSamples());
|
self::assertCount(178, $wine->getSamples());
|
||||||
$this->assertCount(178, $wine->getTargets());
|
self::assertCount(178, $wine->getTargets());
|
||||||
|
|
||||||
// one sample features count
|
// one sample features count
|
||||||
$this->assertCount(13, $wine->getSamples()[0]);
|
self::assertCount(13, $wine->getSamples()[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,22 +22,22 @@ class FilesDatasetTest extends TestCase
|
|||||||
|
|
||||||
$dataset = new FilesDataset($rootPath);
|
$dataset = new FilesDataset($rootPath);
|
||||||
|
|
||||||
$this->assertCount(50, $dataset->getSamples());
|
self::assertCount(50, $dataset->getSamples());
|
||||||
$this->assertCount(50, $dataset->getTargets());
|
self::assertCount(50, $dataset->getTargets());
|
||||||
|
|
||||||
$targets = ['business', 'entertainment', 'politics', 'sport', 'tech'];
|
$targets = ['business', 'entertainment', 'politics', 'sport', 'tech'];
|
||||||
$this->assertEquals($targets, array_values(array_unique($dataset->getTargets())));
|
self::assertEquals($targets, array_values(array_unique($dataset->getTargets())));
|
||||||
|
|
||||||
$firstSample = file_get_contents($rootPath.'/business/001.txt');
|
$firstSample = file_get_contents($rootPath.'/business/001.txt');
|
||||||
$this->assertEquals($firstSample, $dataset->getSamples()[0][0]);
|
self::assertEquals($firstSample, $dataset->getSamples()[0][0]);
|
||||||
|
|
||||||
$firstTarget = 'business';
|
$firstTarget = 'business';
|
||||||
$this->assertEquals($firstTarget, $dataset->getTargets()[0]);
|
self::assertEquals($firstTarget, $dataset->getTargets()[0]);
|
||||||
|
|
||||||
$lastSample = file_get_contents($rootPath.'/tech/010.txt');
|
$lastSample = file_get_contents($rootPath.'/tech/010.txt');
|
||||||
$this->assertEquals($lastSample, $dataset->getSamples()[49][0]);
|
self::assertEquals($lastSample, $dataset->getSamples()[49][0]);
|
||||||
|
|
||||||
$lastTarget = 'tech';
|
$lastTarget = 'tech';
|
||||||
$this->assertEquals($lastTarget, $dataset->getTargets()[49]);
|
self::assertEquals($lastTarget, $dataset->getTargets()[49]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,8 +21,8 @@ class SvmDatasetTest extends TestCase
|
|||||||
$expectedTargets = [
|
$expectedTargets = [
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expectedSamples, $dataset->getSamples());
|
self::assertEquals($expectedSamples, $dataset->getSamples());
|
||||||
$this->assertEquals($expectedTargets, $dataset->getTargets());
|
self::assertEquals($expectedTargets, $dataset->getTargets());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSvmDataset1x1(): void
|
public function testSvmDataset1x1(): void
|
||||||
@ -37,8 +37,8 @@ class SvmDatasetTest extends TestCase
|
|||||||
0,
|
0,
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expectedSamples, $dataset->getSamples());
|
self::assertEquals($expectedSamples, $dataset->getSamples());
|
||||||
$this->assertEquals($expectedTargets, $dataset->getTargets());
|
self::assertEquals($expectedTargets, $dataset->getTargets());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSvmDataset3x1(): void
|
public function testSvmDataset3x1(): void
|
||||||
@ -57,8 +57,8 @@ class SvmDatasetTest extends TestCase
|
|||||||
1,
|
1,
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expectedSamples, $dataset->getSamples());
|
self::assertEquals($expectedSamples, $dataset->getSamples());
|
||||||
$this->assertEquals($expectedTargets, $dataset->getTargets());
|
self::assertEquals($expectedTargets, $dataset->getTargets());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSvmDataset3x4(): void
|
public function testSvmDataset3x4(): void
|
||||||
@ -77,8 +77,8 @@ class SvmDatasetTest extends TestCase
|
|||||||
0,
|
0,
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expectedSamples, $dataset->getSamples());
|
self::assertEquals($expectedSamples, $dataset->getSamples());
|
||||||
$this->assertEquals($expectedTargets, $dataset->getTargets());
|
self::assertEquals($expectedTargets, $dataset->getTargets());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSvmDatasetSparse(): void
|
public function testSvmDatasetSparse(): void
|
||||||
@ -95,8 +95,8 @@ class SvmDatasetTest extends TestCase
|
|||||||
1,
|
1,
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expectedSamples, $dataset->getSamples());
|
self::assertEquals($expectedSamples, $dataset->getSamples());
|
||||||
$this->assertEquals($expectedTargets, $dataset->getTargets());
|
self::assertEquals($expectedTargets, $dataset->getTargets());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSvmDatasetComments(): void
|
public function testSvmDatasetComments(): void
|
||||||
@ -113,8 +113,8 @@ class SvmDatasetTest extends TestCase
|
|||||||
1,
|
1,
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expectedSamples, $dataset->getSamples());
|
self::assertEquals($expectedSamples, $dataset->getSamples());
|
||||||
$this->assertEquals($expectedTargets, $dataset->getTargets());
|
self::assertEquals($expectedTargets, $dataset->getTargets());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSvmDatasetTabs(): void
|
public function testSvmDatasetTabs(): void
|
||||||
@ -129,8 +129,8 @@ class SvmDatasetTest extends TestCase
|
|||||||
1,
|
1,
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($expectedSamples, $dataset->getSamples());
|
self::assertEquals($expectedSamples, $dataset->getSamples());
|
||||||
$this->assertEquals($expectedTargets, $dataset->getTargets());
|
self::assertEquals($expectedTargets, $dataset->getTargets());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSvmDatasetMissingFile(): void
|
public function testSvmDatasetMissingFile(): void
|
||||||
|
@ -40,7 +40,7 @@ class KernelPCATest extends TestCase
|
|||||||
// during the calculation of eigenValues, we have to compare
|
// during the calculation of eigenValues, we have to compare
|
||||||
// absolute value of the values
|
// absolute value of the values
|
||||||
array_map(function ($val1, $val2) use ($epsilon): void {
|
array_map(function ($val1, $val2) use ($epsilon): void {
|
||||||
$this->assertEquals(abs($val1), abs($val2), '', $epsilon);
|
self::assertEquals(abs($val1), abs($val2), '', $epsilon);
|
||||||
}, $transformed, $reducedData);
|
}, $transformed, $reducedData);
|
||||||
|
|
||||||
// Fitted KernelPCA object can also transform an arbitrary sample of the
|
// Fitted KernelPCA object can also transform an arbitrary sample of the
|
||||||
@ -48,7 +48,7 @@ class KernelPCATest extends TestCase
|
|||||||
$newData = [1.25, 2.25];
|
$newData = [1.25, 2.25];
|
||||||
$newTransformed = [0.18956227539216];
|
$newTransformed = [0.18956227539216];
|
||||||
$newTransformed2 = $kpca->transform($newData);
|
$newTransformed2 = $kpca->transform($newData);
|
||||||
$this->assertEquals(abs($newTransformed[0]), abs($newTransformed2[0]), '', $epsilon);
|
self::assertEquals(abs($newTransformed[0]), abs($newTransformed2[0]), '', $epsilon);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testKernelPCAThrowWhenKernelInvalid(): void
|
public function testKernelPCAThrowWhenKernelInvalid(): void
|
||||||
|
@ -51,7 +51,7 @@ class LDATest extends TestCase
|
|||||||
// absolute value of the values
|
// absolute value of the values
|
||||||
$row1 = array_map('abs', $row1);
|
$row1 = array_map('abs', $row1);
|
||||||
$row2 = array_map('abs', $row2);
|
$row2 = array_map('abs', $row2);
|
||||||
$this->assertEquals($row1, $row2, '', $epsilon);
|
self::assertEquals($row1, $row2, '', $epsilon);
|
||||||
};
|
};
|
||||||
array_map($check, $control, $transformed2);
|
array_map($check, $control, $transformed2);
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ class PCATest extends TestCase
|
|||||||
// during the calculation of eigenValues, we have to compare
|
// during the calculation of eigenValues, we have to compare
|
||||||
// absolute value of the values
|
// absolute value of the values
|
||||||
array_map(function ($val1, $val2) use ($epsilon): void {
|
array_map(function ($val1, $val2) use ($epsilon): void {
|
||||||
$this->assertEquals(abs($val1), abs($val2), '', $epsilon);
|
self::assertEquals(abs($val1), abs($val2), '', $epsilon);
|
||||||
}, $transformed, $reducedData);
|
}, $transformed, $reducedData);
|
||||||
|
|
||||||
// Test fitted PCA object to transform an arbitrary sample of the
|
// Test fitted PCA object to transform an arbitrary sample of the
|
||||||
@ -52,7 +52,7 @@ class PCATest extends TestCase
|
|||||||
$newRow2 = $pca->transform($row);
|
$newRow2 = $pca->transform($row);
|
||||||
|
|
||||||
array_map(function ($val1, $val2) use ($epsilon): void {
|
array_map(function ($val1, $val2) use ($epsilon): void {
|
||||||
$this->assertEquals(abs($val1), abs($val2), '', $epsilon);
|
self::assertEquals(abs($val1), abs($val2), '', $epsilon);
|
||||||
}, $newRow, $newRow2);
|
}, $newRow, $newRow2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,13 +14,13 @@ class StopWordsTest extends TestCase
|
|||||||
{
|
{
|
||||||
$stopWords = new StopWords(['lorem', 'ipsum', 'dolor']);
|
$stopWords = new StopWords(['lorem', 'ipsum', 'dolor']);
|
||||||
|
|
||||||
$this->assertTrue($stopWords->isStopWord('lorem'));
|
self::assertTrue($stopWords->isStopWord('lorem'));
|
||||||
$this->assertTrue($stopWords->isStopWord('ipsum'));
|
self::assertTrue($stopWords->isStopWord('ipsum'));
|
||||||
$this->assertTrue($stopWords->isStopWord('dolor'));
|
self::assertTrue($stopWords->isStopWord('dolor'));
|
||||||
|
|
||||||
$this->assertFalse($stopWords->isStopWord('consectetur'));
|
self::assertFalse($stopWords->isStopWord('consectetur'));
|
||||||
$this->assertFalse($stopWords->isStopWord('adipiscing'));
|
self::assertFalse($stopWords->isStopWord('adipiscing'));
|
||||||
$this->assertFalse($stopWords->isStopWord('amet'));
|
self::assertFalse($stopWords->isStopWord('amet'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testThrowExceptionOnInvalidLanguage(): void
|
public function testThrowExceptionOnInvalidLanguage(): void
|
||||||
@ -33,23 +33,23 @@ class StopWordsTest extends TestCase
|
|||||||
{
|
{
|
||||||
$stopWords = StopWords::factory('English');
|
$stopWords = StopWords::factory('English');
|
||||||
|
|
||||||
$this->assertTrue($stopWords->isStopWord('again'));
|
self::assertTrue($stopWords->isStopWord('again'));
|
||||||
$this->assertFalse($stopWords->isStopWord('strategy'));
|
self::assertFalse($stopWords->isStopWord('strategy'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPolishStopWords(): void
|
public function testPolishStopWords(): void
|
||||||
{
|
{
|
||||||
$stopWords = StopWords::factory('Polish');
|
$stopWords = StopWords::factory('Polish');
|
||||||
|
|
||||||
$this->assertTrue($stopWords->isStopWord('wam'));
|
self::assertTrue($stopWords->isStopWord('wam'));
|
||||||
$this->assertFalse($stopWords->isStopWord('transhumanizm'));
|
self::assertFalse($stopWords->isStopWord('transhumanizm'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testFrenchStopWords(): void
|
public function testFrenchStopWords(): void
|
||||||
{
|
{
|
||||||
$stopWords = StopWords::factory('French');
|
$stopWords = StopWords::factory('French');
|
||||||
|
|
||||||
$this->assertTrue($stopWords->isStopWord('alors'));
|
self::assertTrue($stopWords->isStopWord('alors'));
|
||||||
$this->assertFalse($stopWords->isStopWord('carte'));
|
self::assertFalse($stopWords->isStopWord('carte'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,6 @@ class TfIdfTransformerTest extends TestCase
|
|||||||
$transformer = new TfIdfTransformer($samples);
|
$transformer = new TfIdfTransformer($samples);
|
||||||
$transformer->transform($samples);
|
$transformer->transform($samples);
|
||||||
|
|
||||||
$this->assertEquals($tfIdfSamples, $samples, '', 0.001);
|
self::assertEquals($tfIdfSamples, $samples, '', 0.001);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,10 +74,10 @@ class TokenCountVectorizerTest extends TestCase
|
|||||||
$vectorizer = new TokenCountVectorizer(new WhitespaceTokenizer());
|
$vectorizer = new TokenCountVectorizer(new WhitespaceTokenizer());
|
||||||
|
|
||||||
$vectorizer->fit($samples);
|
$vectorizer->fit($samples);
|
||||||
$this->assertSame($vocabulary, $vectorizer->getVocabulary());
|
self::assertSame($vocabulary, $vectorizer->getVocabulary());
|
||||||
|
|
||||||
$vectorizer->transform($samples);
|
$vectorizer->transform($samples);
|
||||||
$this->assertSame($tokensCounts, $samples);
|
self::assertSame($tokensCounts, $samples);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testTransformationWithMinimumDocumentTokenCountFrequency(): void
|
public function testTransformationWithMinimumDocumentTokenCountFrequency(): void
|
||||||
@ -132,10 +132,10 @@ class TokenCountVectorizerTest extends TestCase
|
|||||||
$vectorizer = new TokenCountVectorizer(new WhitespaceTokenizer(), null, 0.5);
|
$vectorizer = new TokenCountVectorizer(new WhitespaceTokenizer(), null, 0.5);
|
||||||
|
|
||||||
$vectorizer->fit($samples);
|
$vectorizer->fit($samples);
|
||||||
$this->assertSame($vocabulary, $vectorizer->getVocabulary());
|
self::assertSame($vocabulary, $vectorizer->getVocabulary());
|
||||||
|
|
||||||
$vectorizer->transform($samples);
|
$vectorizer->transform($samples);
|
||||||
$this->assertSame($tokensCounts, $samples);
|
self::assertSame($tokensCounts, $samples);
|
||||||
|
|
||||||
// word at least once in all samples
|
// word at least once in all samples
|
||||||
$samples = [
|
$samples = [
|
||||||
@ -184,7 +184,7 @@ class TokenCountVectorizerTest extends TestCase
|
|||||||
$vectorizer->fit($samples);
|
$vectorizer->fit($samples);
|
||||||
$vectorizer->transform($samples);
|
$vectorizer->transform($samples);
|
||||||
|
|
||||||
$this->assertSame($tokensCounts, $samples);
|
self::assertSame($tokensCounts, $samples);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testTransformationWithStopWords(): void
|
public function testTransformationWithStopWords(): void
|
||||||
@ -246,9 +246,9 @@ class TokenCountVectorizerTest extends TestCase
|
|||||||
$vectorizer = new TokenCountVectorizer(new WhitespaceTokenizer(), $stopWords);
|
$vectorizer = new TokenCountVectorizer(new WhitespaceTokenizer(), $stopWords);
|
||||||
|
|
||||||
$vectorizer->fit($samples);
|
$vectorizer->fit($samples);
|
||||||
$this->assertSame($vocabulary, $vectorizer->getVocabulary());
|
self::assertSame($vocabulary, $vectorizer->getVocabulary());
|
||||||
|
|
||||||
$vectorizer->transform($samples);
|
$vectorizer->transform($samples);
|
||||||
$this->assertSame($tokensCounts, $samples);
|
self::assertSame($tokensCounts, $samples);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ class ConjugateGradientTest extends TestCase
|
|||||||
|
|
||||||
$theta = $optimizer->runOptimization($samples, $targets, $callback);
|
$theta = $optimizer->runOptimization($samples, $targets, $callback);
|
||||||
|
|
||||||
$this->assertEquals([-1, 2], $theta, '', 0.1);
|
self::assertEquals([-1, 2], $theta, '', 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testRunOptimizationWithCustomInitialTheta(): void
|
public function testRunOptimizationWithCustomInitialTheta(): void
|
||||||
@ -61,7 +61,7 @@ class ConjugateGradientTest extends TestCase
|
|||||||
|
|
||||||
$theta = $optimizer->runOptimization($samples, $targets, $callback);
|
$theta = $optimizer->runOptimization($samples, $targets, $callback);
|
||||||
|
|
||||||
$this->assertEquals([-1.087708, 2.212034], $theta, '', 0.000001);
|
self::assertEquals([-1.087708, 2.212034], $theta, '', 0.000001);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testRunOptimization2Dim(): void
|
public function testRunOptimization2Dim(): void
|
||||||
@ -89,7 +89,7 @@ class ConjugateGradientTest extends TestCase
|
|||||||
|
|
||||||
$theta = $optimizer->runOptimization($samples, $targets, $callback);
|
$theta = $optimizer->runOptimization($samples, $targets, $callback);
|
||||||
|
|
||||||
$this->assertEquals([-1, 2, -3], $theta, '', 0.1);
|
self::assertEquals([-1, 2, -3], $theta, '', 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testThrowExceptionOnInvalidTheta(): void
|
public function testThrowExceptionOnInvalidTheta(): void
|
||||||
|
@ -32,7 +32,7 @@ class GDTest extends TestCase
|
|||||||
|
|
||||||
$theta = $optimizer->runOptimization($samples, $targets, $callback);
|
$theta = $optimizer->runOptimization($samples, $targets, $callback);
|
||||||
|
|
||||||
$this->assertEquals([-1, 2], $theta, '', 0.1);
|
self::assertEquals([-1, 2], $theta, '', 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testRunOptimization2Dim(): void
|
public function testRunOptimization2Dim(): void
|
||||||
@ -60,6 +60,6 @@ class GDTest extends TestCase
|
|||||||
|
|
||||||
$theta = $optimizer->runOptimization($samples, $targets, $callback);
|
$theta = $optimizer->runOptimization($samples, $targets, $callback);
|
||||||
|
|
||||||
$this->assertEquals([-1, 2, -3], $theta, '', 0.1);
|
self::assertEquals([-1, 2, -3], $theta, '', 0.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,9 +26,9 @@ class OptimizerTest extends TestCase
|
|||||||
$optimizer = $this->getMockForAbstractClass(Optimizer::class, [2]);
|
$optimizer = $this->getMockForAbstractClass(Optimizer::class, [2]);
|
||||||
$object = $optimizer->setTheta([0.3, 1]);
|
$object = $optimizer->setTheta([0.3, 1]);
|
||||||
|
|
||||||
$theta = $this->getObjectAttribute($optimizer, 'theta');
|
$theta = self::getObjectAttribute($optimizer, 'theta');
|
||||||
|
|
||||||
$this->assertSame($object, $optimizer);
|
self::assertSame($object, $optimizer);
|
||||||
$this->assertSame([0.3, 1], $theta);
|
self::assertSame([0.3, 1], $theta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ class StochasticGDTest extends TestCase
|
|||||||
|
|
||||||
$theta = $optimizer->runOptimization($samples, $targets, $callback);
|
$theta = $optimizer->runOptimization($samples, $targets, $callback);
|
||||||
|
|
||||||
$this->assertEquals([-1, 2], $theta, '', 0.1);
|
self::assertEquals([-1, 2], $theta, '', 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testRunOptimization2Dim(): void
|
public function testRunOptimization2Dim(): void
|
||||||
@ -60,6 +60,6 @@ class StochasticGDTest extends TestCase
|
|||||||
|
|
||||||
$theta = $optimizer->runOptimization($samples, $targets, $callback);
|
$theta = $optimizer->runOptimization($samples, $targets, $callback);
|
||||||
|
|
||||||
$this->assertEquals([-1, 2, -3], $theta, '', 0.1);
|
self::assertEquals([-1, 2, -3], $theta, '', 0.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ class ComparisonTest extends TestCase
|
|||||||
{
|
{
|
||||||
$result = Comparison::compare($a, $b, $operator);
|
$result = Comparison::compare($a, $b, $operator);
|
||||||
|
|
||||||
$this->assertEquals($expected, $result);
|
self::assertEquals($expected, $result);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testThrowExceptionWhenOperatorIsInvalid(): void
|
public function testThrowExceptionWhenOperatorIsInvalid(): void
|
||||||
|
@ -36,7 +36,7 @@ class ChebyshevTest extends TestCase
|
|||||||
$expectedDistance = 2;
|
$expectedDistance = 2;
|
||||||
$actualDistance = $this->distanceMetric->distance($a, $b);
|
$actualDistance = $this->distanceMetric->distance($a, $b);
|
||||||
|
|
||||||
$this->assertEquals($expectedDistance, $actualDistance);
|
self::assertEquals($expectedDistance, $actualDistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCalculateDistanceForTwoDimensions(): void
|
public function testCalculateDistanceForTwoDimensions(): void
|
||||||
@ -47,7 +47,7 @@ class ChebyshevTest extends TestCase
|
|||||||
$expectedDistance = 2;
|
$expectedDistance = 2;
|
||||||
$actualDistance = $this->distanceMetric->distance($a, $b);
|
$actualDistance = $this->distanceMetric->distance($a, $b);
|
||||||
|
|
||||||
$this->assertEquals($expectedDistance, $actualDistance);
|
self::assertEquals($expectedDistance, $actualDistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCalculateDistanceForThreeDimensions(): void
|
public function testCalculateDistanceForThreeDimensions(): void
|
||||||
@ -58,6 +58,6 @@ class ChebyshevTest extends TestCase
|
|||||||
$expectedDistance = 5;
|
$expectedDistance = 5;
|
||||||
$actualDistance = $this->distanceMetric->distance($a, $b);
|
$actualDistance = $this->distanceMetric->distance($a, $b);
|
||||||
|
|
||||||
$this->assertEquals($expectedDistance, $actualDistance);
|
self::assertEquals($expectedDistance, $actualDistance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ class EuclideanTest extends TestCase
|
|||||||
$expectedDistance = 2;
|
$expectedDistance = 2;
|
||||||
$actualDistance = $this->distanceMetric->distance($a, $b);
|
$actualDistance = $this->distanceMetric->distance($a, $b);
|
||||||
|
|
||||||
$this->assertEquals($expectedDistance, $actualDistance);
|
self::assertEquals($expectedDistance, $actualDistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCalculateDistanceForTwoDimensions(): void
|
public function testCalculateDistanceForTwoDimensions(): void
|
||||||
@ -47,7 +47,7 @@ class EuclideanTest extends TestCase
|
|||||||
$expectedDistance = 2.2360679774998;
|
$expectedDistance = 2.2360679774998;
|
||||||
$actualDistance = $this->distanceMetric->distance($a, $b);
|
$actualDistance = $this->distanceMetric->distance($a, $b);
|
||||||
|
|
||||||
$this->assertEquals($expectedDistance, $actualDistance);
|
self::assertEquals($expectedDistance, $actualDistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCalculateDistanceForThreeDimensions(): void
|
public function testCalculateDistanceForThreeDimensions(): void
|
||||||
@ -58,6 +58,6 @@ class EuclideanTest extends TestCase
|
|||||||
$expectedDistance = 6.7082039324993694;
|
$expectedDistance = 6.7082039324993694;
|
||||||
$actualDistance = $this->distanceMetric->distance($a, $b);
|
$actualDistance = $this->distanceMetric->distance($a, $b);
|
||||||
|
|
||||||
$this->assertEquals($expectedDistance, $actualDistance);
|
self::assertEquals($expectedDistance, $actualDistance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ class ManhattanTest extends TestCase
|
|||||||
$expectedDistance = 2;
|
$expectedDistance = 2;
|
||||||
$actualDistance = $this->distanceMetric->distance($a, $b);
|
$actualDistance = $this->distanceMetric->distance($a, $b);
|
||||||
|
|
||||||
$this->assertEquals($expectedDistance, $actualDistance);
|
self::assertEquals($expectedDistance, $actualDistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCalculateDistanceForTwoDimensions(): void
|
public function testCalculateDistanceForTwoDimensions(): void
|
||||||
@ -47,7 +47,7 @@ class ManhattanTest extends TestCase
|
|||||||
$expectedDistance = 3;
|
$expectedDistance = 3;
|
||||||
$actualDistance = $this->distanceMetric->distance($a, $b);
|
$actualDistance = $this->distanceMetric->distance($a, $b);
|
||||||
|
|
||||||
$this->assertEquals($expectedDistance, $actualDistance);
|
self::assertEquals($expectedDistance, $actualDistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCalculateDistanceForThreeDimensions(): void
|
public function testCalculateDistanceForThreeDimensions(): void
|
||||||
@ -58,6 +58,6 @@ class ManhattanTest extends TestCase
|
|||||||
$expectedDistance = 11;
|
$expectedDistance = 11;
|
||||||
$actualDistance = $this->distanceMetric->distance($a, $b);
|
$actualDistance = $this->distanceMetric->distance($a, $b);
|
||||||
|
|
||||||
$this->assertEquals($expectedDistance, $actualDistance);
|
self::assertEquals($expectedDistance, $actualDistance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ class MinkowskiTest extends TestCase
|
|||||||
$expectedDistance = 2;
|
$expectedDistance = 2;
|
||||||
$actualDistance = $this->distanceMetric->distance($a, $b);
|
$actualDistance = $this->distanceMetric->distance($a, $b);
|
||||||
|
|
||||||
$this->assertEquals($expectedDistance, $actualDistance);
|
self::assertEquals($expectedDistance, $actualDistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCalculateDistanceForTwoDimensions(): void
|
public function testCalculateDistanceForTwoDimensions(): void
|
||||||
@ -47,7 +47,7 @@ class MinkowskiTest extends TestCase
|
|||||||
$expectedDistance = 2.080;
|
$expectedDistance = 2.080;
|
||||||
$actualDistance = $this->distanceMetric->distance($a, $b);
|
$actualDistance = $this->distanceMetric->distance($a, $b);
|
||||||
|
|
||||||
$this->assertEquals($expectedDistance, $actualDistance, '', $delta = 0.001);
|
self::assertEquals($expectedDistance, $actualDistance, '', $delta = 0.001);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCalculateDistanceForThreeDimensions(): void
|
public function testCalculateDistanceForThreeDimensions(): void
|
||||||
@ -58,7 +58,7 @@ class MinkowskiTest extends TestCase
|
|||||||
$expectedDistance = 5.819;
|
$expectedDistance = 5.819;
|
||||||
$actualDistance = $this->distanceMetric->distance($a, $b);
|
$actualDistance = $this->distanceMetric->distance($a, $b);
|
||||||
|
|
||||||
$this->assertEquals($expectedDistance, $actualDistance, '', $delta = 0.001);
|
self::assertEquals($expectedDistance, $actualDistance, '', $delta = 0.001);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCalculateDistanceForThreeDimensionsWithDifferentLambda(): void
|
public function testCalculateDistanceForThreeDimensionsWithDifferentLambda(): void
|
||||||
@ -71,6 +71,6 @@ class MinkowskiTest extends TestCase
|
|||||||
$expectedDistance = 5.300;
|
$expectedDistance = 5.300;
|
||||||
$actualDistance = $distanceMetric->distance($a, $b);
|
$actualDistance = $distanceMetric->distance($a, $b);
|
||||||
|
|
||||||
$this->assertEquals($expectedDistance, $actualDistance, '', $delta = 0.001);
|
self::assertEquals($expectedDistance, $actualDistance, '', $delta = 0.001);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,14 +13,14 @@ class RBFTest extends TestCase
|
|||||||
{
|
{
|
||||||
$rbf = new RBF($gamma = 0.001);
|
$rbf = new RBF($gamma = 0.001);
|
||||||
|
|
||||||
$this->assertEquals(1, $rbf->compute([1, 2], [1, 2]));
|
self::assertEquals(1, $rbf->compute([1, 2], [1, 2]));
|
||||||
$this->assertEquals(0.97336, $rbf->compute([1, 2, 3], [4, 5, 6]), '', $delta = 0.0001);
|
self::assertEquals(0.97336, $rbf->compute([1, 2, 3], [4, 5, 6]), '', $delta = 0.0001);
|
||||||
$this->assertEquals(0.00011, $rbf->compute([4, 5], [1, 100]), '', $delta = 0.0001);
|
self::assertEquals(0.00011, $rbf->compute([4, 5], [1, 100]), '', $delta = 0.0001);
|
||||||
|
|
||||||
$rbf = new RBF($gamma = 0.2);
|
$rbf = new RBF($gamma = 0.2);
|
||||||
|
|
||||||
$this->assertEquals(1, $rbf->compute([1, 2], [1, 2]));
|
self::assertEquals(1, $rbf->compute([1, 2], [1, 2]));
|
||||||
$this->assertEquals(0.00451, $rbf->compute([1, 2, 3], [4, 5, 6]), '', $delta = 0.0001);
|
self::assertEquals(0.00451, $rbf->compute([1, 2, 3], [4, 5, 6]), '', $delta = 0.0001);
|
||||||
$this->assertEquals(0, $rbf->compute([4, 5], [1, 100]));
|
self::assertEquals(0, $rbf->compute([4, 5], [1, 100]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,8 +34,7 @@ final class LUDecompositionTest extends TestCase
|
|||||||
$lu = new LUDecomposition(new Matrix([[1, 2], [3, 4]]));
|
$lu = new LUDecomposition(new Matrix([[1, 2], [3, 4]]));
|
||||||
$L = $lu->getL();
|
$L = $lu->getL();
|
||||||
|
|
||||||
$this->assertInstanceOf(Matrix::class, $L);
|
self::assertSame([[1.0, 0.0], [0.3333333333333333, 1.0]], $L->toArray());
|
||||||
$this->assertSame([[1.0, 0.0], [0.3333333333333333, 1.0]], $L->toArray());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testUpperTriangularFactor(): void
|
public function testUpperTriangularFactor(): void
|
||||||
@ -43,7 +42,6 @@ final class LUDecompositionTest extends TestCase
|
|||||||
$lu = new LUDecomposition(new Matrix([[1, 2], [3, 4]]));
|
$lu = new LUDecomposition(new Matrix([[1, 2], [3, 4]]));
|
||||||
$U = $lu->getU();
|
$U = $lu->getU();
|
||||||
|
|
||||||
$this->assertInstanceOf(Matrix::class, $U);
|
self::assertSame([[3.0, 4.0], [0.0, 0.6666666666666667]], $U->toArray());
|
||||||
$this->assertSame([[3.0, 4.0], [0.0, 0.6666666666666667]], $U->toArray());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,11 +22,10 @@ class MatrixTest extends TestCase
|
|||||||
$flatArray = [1, 2, 3, 4];
|
$flatArray = [1, 2, 3, 4];
|
||||||
$matrix = Matrix::fromFlatArray($flatArray);
|
$matrix = Matrix::fromFlatArray($flatArray);
|
||||||
|
|
||||||
$this->assertInstanceOf(Matrix::class, $matrix);
|
self::assertEquals([[1], [2], [3], [4]], $matrix->toArray());
|
||||||
$this->assertEquals([[1], [2], [3], [4]], $matrix->toArray());
|
self::assertEquals(4, $matrix->getRows());
|
||||||
$this->assertEquals(4, $matrix->getRows());
|
self::assertEquals(1, $matrix->getColumns());
|
||||||
$this->assertEquals(1, $matrix->getColumns());
|
self::assertEquals($flatArray, $matrix->getColumnValues(0));
|
||||||
$this->assertEquals($flatArray, $matrix->getColumnValues(0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testThrowExceptionOnInvalidColumnNumber(): void
|
public function testThrowExceptionOnInvalidColumnNumber(): void
|
||||||
@ -51,7 +50,7 @@ class MatrixTest extends TestCase
|
|||||||
[4, 2, 1],
|
[4, 2, 1],
|
||||||
[5, 6, 7],
|
[5, 6, 7],
|
||||||
]);
|
]);
|
||||||
$this->assertEquals(-3, $matrix->getDeterminant());
|
self::assertEquals(-3, $matrix->getDeterminant());
|
||||||
|
|
||||||
$matrix = new Matrix([
|
$matrix = new Matrix([
|
||||||
[1, 2, 3, 3, 2, 1],
|
[1, 2, 3, 3, 2, 1],
|
||||||
@ -61,7 +60,7 @@ class MatrixTest extends TestCase
|
|||||||
[1 / 4, 4, 1, 0, 2, 3 / 7],
|
[1 / 4, 4, 1, 0, 2, 3 / 7],
|
||||||
[1, 8, 7, 5, 4, 4 / 5],
|
[1, 8, 7, 5, 4, 4 / 5],
|
||||||
]);
|
]);
|
||||||
$this->assertEquals(1116.5035, $matrix->getDeterminant(), '', $delta = 0.0001);
|
self::assertEquals(1116.5035, $matrix->getDeterminant(), '', $delta = 0.0001);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testMatrixTranspose(): void
|
public function testMatrixTranspose(): void
|
||||||
@ -78,7 +77,7 @@ class MatrixTest extends TestCase
|
|||||||
[3, 1, 7],
|
[3, 1, 7],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($transposedMatrix, $matrix->transpose()->toArray());
|
self::assertEquals($transposedMatrix, $matrix->transpose()->toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testThrowExceptionOnMultiplyWhenInconsistentMatrixSupplied(): void
|
public function testThrowExceptionOnMultiplyWhenInconsistentMatrixSupplied(): void
|
||||||
@ -107,7 +106,7 @@ class MatrixTest extends TestCase
|
|||||||
[139, 154],
|
[139, 154],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($product, $matrix1->multiply($matrix2)->toArray());
|
self::assertEquals($product, $matrix1->multiply($matrix2)->toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testDivideByScalar(): void
|
public function testDivideByScalar(): void
|
||||||
@ -122,7 +121,7 @@ class MatrixTest extends TestCase
|
|||||||
[1, 5, 10],
|
[1, 5, 10],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($quotient, $matrix->divideByScalar(2)->toArray());
|
self::assertEquals($quotient, $matrix->divideByScalar(2)->toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testThrowExceptionWhenInverseIfArrayIsNotSquare(): void
|
public function testThrowExceptionWhenInverseIfArrayIsNotSquare(): void
|
||||||
@ -158,7 +157,7 @@ class MatrixTest extends TestCase
|
|||||||
[-1 / 2, 1 / 2, -1 / 2],
|
[-1 / 2, 1 / 2, -1 / 2],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($inverseMatrix, $matrix->inverse()->toArray(), '', $delta = 0.0001);
|
self::assertEquals($inverseMatrix, $matrix->inverse()->toArray(), '', $delta = 0.0001);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCrossOutMatrix(): void
|
public function testCrossOutMatrix(): void
|
||||||
@ -174,14 +173,14 @@ class MatrixTest extends TestCase
|
|||||||
[1, 1],
|
[1, 1],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($crossOuted, $matrix->crossOut(1, 1)->toArray());
|
self::assertEquals($crossOuted, $matrix->crossOut(1, 1)->toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testToScalar(): void
|
public function testToScalar(): void
|
||||||
{
|
{
|
||||||
$matrix = new Matrix([[1, 2, 3], [3, 2, 3]]);
|
$matrix = new Matrix([[1, 2, 3], [3, 2, 3]]);
|
||||||
|
|
||||||
$this->assertEquals($matrix->toScalar(), 1);
|
self::assertEquals($matrix->toScalar(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testMultiplyByScalar(): void
|
public function testMultiplyByScalar(): void
|
||||||
@ -196,7 +195,7 @@ class MatrixTest extends TestCase
|
|||||||
[-4, -20, -40],
|
[-4, -20, -40],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($result, $matrix->multiplyByScalar(-2)->toArray());
|
self::assertEquals($result, $matrix->multiplyByScalar(-2)->toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAdd(): void
|
public function testAdd(): void
|
||||||
@ -208,7 +207,7 @@ class MatrixTest extends TestCase
|
|||||||
$m1 = new Matrix($array1);
|
$m1 = new Matrix($array1);
|
||||||
$m2 = new Matrix($array2);
|
$m2 = new Matrix($array2);
|
||||||
|
|
||||||
$this->assertEquals($result, $m1->add($m2)->toArray()[0]);
|
self::assertEquals($result, $m1->add($m2)->toArray()[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSubtract(): void
|
public function testSubtract(): void
|
||||||
@ -220,7 +219,7 @@ class MatrixTest extends TestCase
|
|||||||
$m1 = new Matrix($array1);
|
$m1 = new Matrix($array1);
|
||||||
$m2 = new Matrix($array2);
|
$m2 = new Matrix($array2);
|
||||||
|
|
||||||
$this->assertEquals($result, $m1->subtract($m2)->toArray()[0]);
|
self::assertEquals($result, $m1->subtract($m2)->toArray()[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testTransposeArray(): void
|
public function testTransposeArray(): void
|
||||||
@ -235,7 +234,7 @@ class MatrixTest extends TestCase
|
|||||||
[1, 2],
|
[1, 2],
|
||||||
];
|
];
|
||||||
|
|
||||||
$this->assertEquals($transposed, Matrix::transposeArray($array));
|
self::assertEquals($transposed, Matrix::transposeArray($array));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testDot(): void
|
public function testDot(): void
|
||||||
@ -244,12 +243,12 @@ class MatrixTest extends TestCase
|
|||||||
$vect2 = [3, 3, 3];
|
$vect2 = [3, 3, 3];
|
||||||
$dot = [18];
|
$dot = [18];
|
||||||
|
|
||||||
$this->assertEquals($dot, Matrix::dot($vect1, $vect2));
|
self::assertEquals($dot, Matrix::dot($vect1, $vect2));
|
||||||
|
|
||||||
$matrix1 = [[1, 1], [2, 2]];
|
$matrix1 = [[1, 1], [2, 2]];
|
||||||
$matrix2 = [[3, 3], [3, 3], [3, 3]];
|
$matrix2 = [[3, 3], [3, 3], [3, 3]];
|
||||||
$dot = [6, 12];
|
$dot = [6, 12];
|
||||||
$this->assertEquals($dot, Matrix::dot($matrix2, $matrix1));
|
self::assertEquals($dot, Matrix::dot($matrix2, $matrix1));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -257,12 +256,10 @@ class MatrixTest extends TestCase
|
|||||||
*/
|
*/
|
||||||
public function testFrobeniusNorm(array $matrix, float $norm): void
|
public function testFrobeniusNorm(array $matrix, float $norm): void
|
||||||
{
|
{
|
||||||
$matrix = new Matrix($matrix);
|
self::assertEquals($norm, (new Matrix($matrix))->frobeniusNorm(), '', 0.0001);
|
||||||
|
|
||||||
$this->assertEquals($norm, $matrix->frobeniusNorm(), '', 0.0001);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function dataProviderForFrobeniusNorm()
|
public function dataProviderForFrobeniusNorm(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
[
|
[
|
||||||
|
@ -12,11 +12,11 @@ class ProductTest extends TestCase
|
|||||||
{
|
{
|
||||||
public function testScalarProduct(): void
|
public function testScalarProduct(): void
|
||||||
{
|
{
|
||||||
$this->assertEquals(10, Product::scalar([2, 3], [-1, 4]));
|
self::assertEquals(10, Product::scalar([2, 3], [-1, 4]));
|
||||||
$this->assertEquals(-0.1, Product::scalar([1, 4, 1], [-2, 0.5, -0.1]));
|
self::assertEquals(-0.1, Product::scalar([1, 4, 1], [-2, 0.5, -0.1]));
|
||||||
$this->assertEquals(8, Product::scalar([2], [4]));
|
self::assertEquals(8, Product::scalar([2], [4]));
|
||||||
|
|
||||||
//test for non numeric values
|
//test for non numeric values
|
||||||
$this->assertEquals(0, Product::scalar(['', null, [], new stdClass()], [null]));
|
self::assertEquals(0, Product::scalar(['', null, [], new stdClass()], [null]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,84 +13,79 @@ class SetTest extends TestCase
|
|||||||
{
|
{
|
||||||
$union = Set::union(new Set([3, 1]), new Set([3, 2, 2]));
|
$union = Set::union(new Set([3, 1]), new Set([3, 2, 2]));
|
||||||
|
|
||||||
$this->assertInstanceOf(Set::class, $union);
|
self::assertEquals(new Set([1, 2, 3]), $union);
|
||||||
$this->assertEquals(new Set([1, 2, 3]), $union);
|
self::assertEquals(3, $union->cardinality());
|
||||||
$this->assertEquals(3, $union->cardinality());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testIntersection(): void
|
public function testIntersection(): void
|
||||||
{
|
{
|
||||||
$intersection = Set::intersection(new Set(['C', 'A']), new Set(['B', 'C']));
|
$intersection = Set::intersection(new Set(['C', 'A']), new Set(['B', 'C']));
|
||||||
|
|
||||||
$this->assertInstanceOf(Set::class, $intersection);
|
self::assertEquals(new Set(['C']), $intersection);
|
||||||
$this->assertEquals(new Set(['C']), $intersection);
|
self::assertEquals(1, $intersection->cardinality());
|
||||||
$this->assertEquals(1, $intersection->cardinality());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testDifference(): void
|
public function testDifference(): void
|
||||||
{
|
{
|
||||||
$difference = Set::difference(new Set(['C', 'A', 'B']), new Set(['A']));
|
$difference = Set::difference(new Set(['C', 'A', 'B']), new Set(['A']));
|
||||||
|
|
||||||
$this->assertInstanceOf(Set::class, $difference);
|
self::assertEquals(new Set(['B', 'C']), $difference);
|
||||||
$this->assertEquals(new Set(['B', 'C']), $difference);
|
self::assertEquals(2, $difference->cardinality());
|
||||||
$this->assertEquals(2, $difference->cardinality());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPower(): void
|
public function testPower(): void
|
||||||
{
|
{
|
||||||
$power = Set::power(new Set(['A', 'B']));
|
$power = Set::power(new Set(['A', 'B']));
|
||||||
|
|
||||||
$this->assertInternalType('array', $power);
|
self::assertEquals([new Set(), new Set(['A']), new Set(['B']), new Set(['A', 'B'])], $power);
|
||||||
$this->assertEquals([new Set(), new Set(['A']), new Set(['B']), new Set(['A', 'B'])], $power);
|
self::assertCount(4, $power);
|
||||||
$this->assertCount(4, $power);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCartesian(): void
|
public function testCartesian(): void
|
||||||
{
|
{
|
||||||
$cartesian = Set::cartesian(new Set(['A']), new Set([1, 2]));
|
$cartesian = Set::cartesian(new Set(['A']), new Set([1, 2]));
|
||||||
|
|
||||||
$this->assertInternalType('array', $cartesian);
|
self::assertEquals([new Set(['A', 1]), new Set(['A', 2])], $cartesian);
|
||||||
$this->assertEquals([new Set(['A', 1]), new Set(['A', 2])], $cartesian);
|
self::assertCount(2, $cartesian);
|
||||||
$this->assertCount(2, $cartesian);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testContains(): void
|
public function testContains(): void
|
||||||
{
|
{
|
||||||
$set = new Set(['B', 'A', 2, 1]);
|
$set = new Set(['B', 'A', 2, 1]);
|
||||||
|
|
||||||
$this->assertTrue($set->contains('B'));
|
self::assertTrue($set->contains('B'));
|
||||||
$this->assertTrue($set->containsAll(['A', 'B']));
|
self::assertTrue($set->containsAll(['A', 'B']));
|
||||||
|
|
||||||
$this->assertFalse($set->contains('C'));
|
self::assertFalse($set->contains('C'));
|
||||||
$this->assertFalse($set->containsAll(['A', 'B', 'C']));
|
self::assertFalse($set->containsAll(['A', 'B', 'C']));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testRemove(): void
|
public function testRemove(): void
|
||||||
{
|
{
|
||||||
$set = new Set(['B', 'A', 2, 1]);
|
$set = new Set(['B', 'A', 2, 1]);
|
||||||
|
|
||||||
$this->assertEquals((new Set([1, 2, 2, 2, 'B']))->toArray(), $set->remove('A')->toArray());
|
self::assertEquals((new Set([1, 2, 2, 2, 'B']))->toArray(), $set->remove('A')->toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAdd(): void
|
public function testAdd(): void
|
||||||
{
|
{
|
||||||
$set = new Set(['B', 'A', 2, 1]);
|
$set = new Set(['B', 'A', 2, 1]);
|
||||||
$set->addAll(['foo', 'bar']);
|
$set->addAll(['foo', 'bar']);
|
||||||
$this->assertEquals(6, $set->cardinality());
|
self::assertEquals(6, $set->cardinality());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testEmpty(): void
|
public function testEmpty(): void
|
||||||
{
|
{
|
||||||
$set = new Set([1, 2]);
|
$set = new Set([1, 2]);
|
||||||
$set->removeAll([2, 1]);
|
$set->removeAll([2, 1]);
|
||||||
$this->assertEquals(new Set(), $set);
|
self::assertEquals(new Set(), $set);
|
||||||
$this->assertTrue($set->isEmpty());
|
self::assertTrue($set->isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testToArray(): void
|
public function testToArray(): void
|
||||||
{
|
{
|
||||||
$set = new Set([1, 2, 2, 3, 'A', false, '', 1.1, -1, -10, 'B']);
|
$set = new Set([1, 2, 2, 3, 'A', false, '', 1.1, -1, -10, 'B']);
|
||||||
|
|
||||||
$this->assertEquals([-10, '', -1, 'A', 'B', 1, 1.1, 2, 3], $set->toArray());
|
self::assertEquals([-10, '', -1, 'A', 'B', 1, 1.1, 2, 3], $set->toArray());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,18 +16,18 @@ class CorrelationTest extends TestCase
|
|||||||
$delta = 0.001;
|
$delta = 0.001;
|
||||||
$x = [9300, 10565, 15000, 15000, 17764, 57000, 65940, 73676, 77006, 93739, 146088, 153260];
|
$x = [9300, 10565, 15000, 15000, 17764, 57000, 65940, 73676, 77006, 93739, 146088, 153260];
|
||||||
$y = [7100, 15500, 4400, 4400, 5900, 4600, 8800, 2000, 2750, 2550, 960, 1025];
|
$y = [7100, 15500, 4400, 4400, 5900, 4600, 8800, 2000, 2750, 2550, 960, 1025];
|
||||||
$this->assertEquals(-0.641, Correlation::pearson($x, $y), '', $delta);
|
self::assertEquals(-0.641, Correlation::pearson($x, $y), '', $delta);
|
||||||
|
|
||||||
//http://www.statisticshowto.com/how-to-compute-pearsons-correlation-coefficients/
|
//http://www.statisticshowto.com/how-to-compute-pearsons-correlation-coefficients/
|
||||||
$delta = 0.001;
|
$delta = 0.001;
|
||||||
$x = [43, 21, 25, 42, 57, 59];
|
$x = [43, 21, 25, 42, 57, 59];
|
||||||
$y = [99, 65, 79, 75, 87, 82];
|
$y = [99, 65, 79, 75, 87, 82];
|
||||||
$this->assertEquals(0.549, Correlation::pearson($x, $y), '', $delta);
|
self::assertEquals(0.549, Correlation::pearson($x, $y), '', $delta);
|
||||||
|
|
||||||
$delta = 0.001;
|
$delta = 0.001;
|
||||||
$x = [60, 61, 62, 63, 65];
|
$x = [60, 61, 62, 63, 65];
|
||||||
$y = [3.1, 3.6, 3.8, 4, 4.1];
|
$y = [3.1, 3.6, 3.8, 4, 4.1];
|
||||||
$this->assertEquals(0.911, Correlation::pearson($x, $y), '', $delta);
|
self::assertEquals(0.911, Correlation::pearson($x, $y), '', $delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testThrowExceptionOnInvalidArgumentsForPearsonCorrelation(): void
|
public function testThrowExceptionOnInvalidArgumentsForPearsonCorrelation(): void
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user