fit($data); // Due to the fact that the sign of values can be flipped // during the calculation of eigenValues, we have to compare // absolute value of the values array_map(function ($val1, $val2) use ($epsilon): void { self::assertEqualsWithDelta(abs($val1[0]), abs($val2[0]), $epsilon); }, $transformed, $reducedData); // Test fitted PCA object to transform an arbitrary sample of the // same dimensionality with the original dataset foreach ($data as $i => $row) { $newRow = [[$transformed[$i]]]; $newRow2 = $pca->transform($row); array_map(function ($val1, $val2) use ($epsilon): void { self::assertEqualsWithDelta(abs($val1[0][0]), abs($val2[0]), $epsilon); }, $newRow, $newRow2); } } public function testPCAThrowWhenTotalVarianceOutOfRange(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Total variance can be a value between 0.1 and 0.99'); new PCA(0., null); } public function testPCAThrowWhenNumFeaturesOutOfRange(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Number of features to be preserved should be greater than 0'); new PCA(null, 0); } public function testPCAThrowWhenParameterNotSpecified(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Either totalVariance or numFeatures should be specified in order to run the algorithm'); new PCA(); } public function testPCAThrowWhenBothParameterSpecified(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Either totalVariance or numFeatures should be specified in order to run the algorithm'); new PCA(0.9, 1); } public function testTransformThrowWhenNotFitted(): void { $samples = [ [1, 0], [1, 1], ]; $pca = new PCA(0.9); $this->expectException(InvalidOperationException::class); $this->expectExceptionMessage('PCA has not been fitted with respect to original dataset, please run PCA::fit() first'); $pca->transform($samples); } }