RegressionTest.php 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. <?php
  2. namespace MathPHP\Tests\Statistics\Multivariate\PCA;
  3. use MathPHP\Functions\Map\Multi;
  4. use MathPHP\LinearAlgebra\MatrixFactory;
  5. use MathPHP\Statistics\Multivariate\PCA;
  6. /**
  7. * Regression test for bug issue 414
  8. *
  9. * @link https://github.com/markrogoyski/math-php/issues/414
  10. *
  11. * The issue was construction the PCA with highly correlated data, calculating the eigenvalues would not converge,
  12. * resulting in an infinite loop.
  13. */
  14. class RegressionTest extends \PHPUnit\Framework\TestCase
  15. {
  16. /**
  17. * @test PCA center false and scale false
  18. *
  19. * R test data
  20. * > library(mdatools)
  21. * >
  22. * > data = rbind(c(0.066073, 96.000000), c(5.407780, 1115.857143), c(19.440563, 3999.142857), c(35.582583, 7315.857143), c(71.602260, 14725.428571), c(165.725077, 34186.000000), c(235.426483, 48657.857143), c(256.868816, 53160.316186))
  23. * > data
  24. * [,1] [,2]
  25. * [1,] 0.066073 96.000
  26. * [2,] 5.407780 1115.857
  27. * [3,] 19.440563 3999.143
  28. * [4,] 35.582583 7315.857
  29. * [5,] 71.602260 14725.429
  30. * [6,] 165.725077 34186.000
  31. * [7,] 235.426483 48657.857
  32. * [8,] 256.868816 53160.316
  33. * >
  34. * > model = pca(data, center=FALSE, scale=FALSE)
  35. *
  36. * > loadings = model$loadings
  37. * > loadings
  38. * Comp 1 Comp 2
  39. * [1,] -0.004838294 -0.999988295
  40. * [2,] -0.999988295 0.004838294
  41. */
  42. public function testBugCenterFalseScaleFalseLoadings()
  43. {
  44. // Given
  45. $data = MatrixFactory::createNumeric([
  46. [0.066073, 96.000000],
  47. [5.407780, 1115.857143],
  48. [19.440563, 3999.142857],
  49. [35.582583, 7315.857143],
  50. [71.602260, 14725.428571],
  51. [165.725077, 34186.000000],
  52. [235.426483, 48657.857143],
  53. [256.868816, 53160.316186]
  54. ]);
  55. // And
  56. $center = true;
  57. $scale = false;
  58. // When
  59. $model = new PCA($data, $center, $scale);
  60. // Then
  61. $expected = [
  62. [-0.004838294, -0.999988295],
  63. [-0.999988295, 0.004838294],
  64. ];
  65. $loadings = $model->getLoadings();
  66. // And since each column could be multiplied by -1, we will compare the two and adjust.
  67. // Get an array that's roughly ones and negative ones.
  68. $quotient = Multi::divide($expected[1], $loadings->getMatrix()[1]);
  69. // Convert to exactly one or negative one. Cannot be zero.
  70. $signum = \array_map(
  71. function ($x) {
  72. return $x <=> 0;
  73. },
  74. $quotient
  75. );
  76. $sign_change = MatrixFactory::diagonal($signum);
  77. // Multiplying a sign change matrix on the right changes column signs.
  78. $sign_adjusted = $loadings->multiply($sign_change);
  79. // Then
  80. $this->assertEqualsWithDelta($expected, $sign_adjusted->getMatrix(), .00001);
  81. }
  82. }