KernelDensityEstimationTest.php 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. <?php
  2. namespace MathPHP\Tests\Statistics;
  3. use MathPHP\Exception;
  4. use MathPHP\Statistics\KernelDensityEstimation;
  5. class KernelDensityEstimationTest extends \PHPUnit\Framework\TestCase
  6. {
  7. /** @var array 100 Random normally distributed data points */
  8. private $data = [
  9. -2.76, -2.59, -2.57, -2.4, -1.9, -1.77, -1.33, -1.27, -1.26, -1.19, -1.13, -1.1, -1.09, -1.05, -1.03,
  10. -0.9, -0.87, -0.87, -0.85, -0.83, -0.78, -0.71, -0.62, -0.61, -0.6, -0.57, -0.55, -0.51, -0.5, -0.5,
  11. -0.47, -0.46, -0.46, -0.45, -0.44, -0.32, -0.28, -0.27, -0.25, -0.23, -0.21, -0.21, -0.2, -0.16, -0.15,
  12. -0.11, -0.094, -0.07, -0.06, -0.04, 0, 0.03, 0.04, 0.06, 0.06, 0.09, 0.1, 0.12, 0.15, 0.15, 0.18, 0.22,
  13. 0.22, 0.23, 0.24, 0.3, 0.31, 0.34, 0.34, 0.38, 0.38, 0.44, 0.52, 0.53, 0.56, 0.57, 0.61, 0.61, 0.69,
  14. 0.7, 0.75, 0.79, 0.79, 0.79, 0.8, 0.83, 0.85, 0.99, 1.08, 1.23, 1.23, 1.23, 1.25, 1.29, 1.32, 1.34,
  15. 1.43, 1.49, 1.62, 1.75
  16. ];
  17. /**
  18. * @test evaluate using default standard normal kernel function
  19. * @dataProvider dataProviderForKernelDensity
  20. * @param array $data
  21. * @param float $x
  22. * @param float $expected
  23. */
  24. public function testDefaultKernelDensity(array $data, float $x, float $expected)
  25. {
  26. // Given
  27. $KDE = new KernelDensityEstimation($data);
  28. // When
  29. $estimate = $KDE->evaluate($x);
  30. // Then
  31. $this->assertEqualsWithDelta($expected, $estimate, 0.0001);
  32. }
  33. /**
  34. * @return array [data, x, expected]
  35. */
  36. public function dataProviderForKernelDensity(): array
  37. {
  38. return [
  39. [ $this->data, 1, 0.236055582 ],
  40. [ $this->data, .1, 0.421356196 ],
  41. [ $this->data, -1, 0.232277743 ],
  42. ];
  43. }
  44. /**
  45. * @test evaluate when setting custom h using default kernel function
  46. * @dataProvider dataProviderForKernelDensityCustomH
  47. * @param array $data
  48. * @param float $h
  49. * @param float $x
  50. * @param float $expected
  51. */
  52. public function testDefaultKernelDensityCustomH(array $data, float $h, float $x, float $expected)
  53. {
  54. // Given
  55. $KDE = new KernelDensityEstimation($data);
  56. $KDE->setBandwidth($h);
  57. // When
  58. $estimate = $KDE->evaluate($x);
  59. // Then
  60. $this->assertEqualsWithDelta($expected, $estimate, 0.0001);
  61. }
  62. /**
  63. * @return array [data, h, x, expected]
  64. */
  65. public function dataProviderForKernelDensityCustomH(): array
  66. {
  67. $h = count($this->data) ** (-1 / 6);
  68. return [
  69. [ $this->data, $h, 1, 0.237991168 ],
  70. [ $this->data, $h, .1, 0.40525027 ],
  71. [ $this->data, $h, -1, 0.232226496 ],
  72. ];
  73. }
  74. /**
  75. * @test Custom kernel function and custom h
  76. * @dataProvider dataProviderForKernelDensityCustomBoth
  77. * @param float $h
  78. * @param callable $kernel
  79. * @param float $x
  80. * @param float $expected
  81. */
  82. public function testDefaultKernelDensityCustomBoth(float $h, callable $kernel, float $x, float $expected)
  83. {
  84. // Given
  85. $KDE = new KernelDensityEstimation($this->data, $h, $kernel);
  86. $kernel2 = KernelDensityEstimation::TRICUBE;
  87. $KDE2 = new KernelDensityEstimation($this->data, $h, $kernel2);
  88. // When
  89. $estimate1 = $KDE->evaluate($x);
  90. $estimate2 = $KDE2->evaluate($x);
  91. // Then
  92. $this->assertEqualsWithDelta($expected, $estimate1, 0.0001);
  93. $this->assertEqualsWithDelta($expected, $estimate2, 0.0001);
  94. }
  95. /**
  96. * @return array [h, kernel, x, expected]
  97. */
  98. public function dataProviderForKernelDensityCustomBoth(): array
  99. {
  100. $h = 1;
  101. // Tricube
  102. $kernel = function ($x) {
  103. if (\abs($x) > 1) {
  104. return 0;
  105. } else {
  106. return 70 / 81 * ((1 - \abs($x) ** 3) ** 3);
  107. }
  108. };
  109. return [
  110. [ $h, $kernel, 1, 0.238712304 ],
  111. [ $h, $kernel, .1, 0.420794741 ],
  112. [ $h, $kernel, -1, 0.229056709 ],
  113. ];
  114. }
  115. /**
  116. * @test evaluate using optional kernel functions
  117. * @dataProvider dataProviderForTestKernels
  118. * @param string $kernel
  119. * @param float $x
  120. * @param float $expected
  121. */
  122. public function testKernels(string $kernel, float $x, float $expected)
  123. {
  124. // Given
  125. $KDE = new KernelDensityEstimation($this->data, 1, $kernel);
  126. // When
  127. $estimate = $KDE->evaluate($x);
  128. // Then
  129. $this->assertEqualsWithDelta($expected, $estimate, 0.0001);
  130. }
  131. /**
  132. * @return array [kernel, x, expected]
  133. */
  134. public function dataProviderForTestKernels(): array
  135. {
  136. return [
  137. [KernelDensityEstimation::UNIFORM, 1, .25],
  138. [KernelDensityEstimation::TRIANGULAR, 1, .235],
  139. [KernelDensityEstimation::EPANECHNIKOV, 1, .2401905],
  140. ];
  141. }
  142. /**
  143. * @test Kernel function NORMAL
  144. * Test data made with scipy.stats.gaussian_kde
  145. * @dataProvider dataProviderForNormalKde
  146. * @param float $x
  147. * @param float $expected
  148. */
  149. public function testNormal(float $x, float $expected)
  150. {
  151. // Given
  152. $KDE = new KernelDensityEstimation($this->data);
  153. $h = 0.3932;
  154. $KDE->setBandwidth($h);
  155. $KDE->setKernelFunction(KernelDensityEstimation::NORMAL);
  156. // When
  157. $estimate = $KDE->evaluate($x);
  158. // Then
  159. $this->assertEqualsWithDelta($expected, $estimate, 0.00001, "Evaluate x = $x");
  160. }
  161. /**
  162. * Test data made with scipy.stats.gaussian_kde
  163. * @return array (x, expected)
  164. */
  165. public function dataProviderForNormalKde(): array
  166. {
  167. return [
  168. [-3.939, 8.60829883e-05],
  169. [-2.222, 0.04138222],
  170. [-0.505, 0.36562621],
  171. [0, 0.43200043],
  172. [1.212, 0.19113825],
  173. [2.929, 9.09492454e-05],
  174. ];
  175. }
  176. /**
  177. * @test Invalid kernel throws an Exception
  178. */
  179. public function testBadKernel()
  180. {
  181. // Then
  182. $this->expectException(Exception\BadParameterException::class);
  183. // When
  184. $KDE = new KernelDensityEstimation($this->data, 1, 1.0);
  185. }
  186. /**
  187. * @test Unknown built-in kernel throws an Exception
  188. */
  189. public function testUnknownBuildInKernel()
  190. {
  191. // Given
  192. $KDE = new KernelDensityEstimation($this->data);
  193. // Then
  194. $this->expectException(Exception\BadDataException::class);
  195. // When
  196. $KDE->setKernelFunction('DoesNotExist');
  197. }
  198. /**
  199. * @test Invalid bandwidth throws an Exception
  200. */
  201. public function testBadSetBandwidth()
  202. {
  203. // Given
  204. $KDE = new KernelDensityEstimation($this->data);
  205. // Then
  206. $this->expectException(Exception\OutOfBoundsException::class);
  207. // When
  208. $KDE->setBandwidth(-1);
  209. }
  210. /**
  211. * @test Empty data throws an Exception
  212. */
  213. public function testEmptyData()
  214. {
  215. // Given
  216. $data = [];
  217. // Then
  218. $this->expectException(Exception\BadDataException::class);
  219. // When
  220. $KDE = new KernelDensityEstimation([]);
  221. }
  222. }