DescriptiveTest.php 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353
  1. <?php
  2. namespace MathPHP\Tests\Statistics;
  3. use MathPHP\Statistics\Descriptive;
  4. use MathPHP\Exception;
  5. class DescriptiveTest extends \PHPUnit\Framework\TestCase
  6. {
  7. /**
  8. * @test range
  9. * @dataProvider dataProviderForRange
  10. * @param array $numbers
  11. * @param float $expectedRange
  12. * @throws \Exception
  13. */
  14. public function testRange(array $numbers, float $expectedRange)
  15. {
  16. // When
  17. $range = Descriptive::range($numbers);
  18. // Then
  19. $this->assertEqualsWithDelta($expectedRange, $range, 0.01);
  20. }
  21. /**
  22. * Data provider for range test
  23. * @return array [ [ numbers ], range ]
  24. */
  25. public function dataProviderForRange(): array
  26. {
  27. return [
  28. [ [ 1, 1, 1 ], 0 ],
  29. [ [ 1, 1, 2 ], 1 ],
  30. [ [ 1, 2, 1 ], 1 ],
  31. [ [ 8, 4, 3 ], 5 ],
  32. [ [ 9, 7, 8 ], 2 ],
  33. [ [ 13, 18, 13, 14, 13, 16, 14, 21, 13 ], 8 ],
  34. [ [ 1, 2, 4, 7 ], 6 ],
  35. [ [ 8, 9, 10, 10, 10, 11, 11, 11, 12, 13 ], 5 ],
  36. [ [ 6, 7, 8, 10, 12, 14, 14, 15, 16, 20 ], 14 ],
  37. [ [ 9, 10, 11, 13, 15, 17, 17, 18, 19, 23 ], 14 ],
  38. [ [ 12, 14, 16, 20, 24, 28, 28, 30, 32, 40 ], 28 ],
  39. ];
  40. }
  41. /**
  42. * @test range when array is empty
  43. * @throws \Exception
  44. */
  45. public function testRangeExceptionWhenEmptyArray()
  46. {
  47. // Given
  48. $numbers = [];
  49. // Then
  50. $this->expectException(Exception\BadDataException::class);
  51. // When
  52. Descriptive::range($numbers);
  53. }
  54. /**
  55. * @test midrange
  56. * @dataProvider dataProviderForMidrange
  57. * @param array $numbers
  58. * @param float $expectedMidrange
  59. * @throws \Exception
  60. */
  61. public function testMidrange(array $numbers, float $expectedMidrange)
  62. {
  63. // When
  64. $midrange = Descriptive::midrange($numbers);
  65. // Then
  66. $this->assertEqualsWithDelta($expectedMidrange, $midrange, 0.01);
  67. }
  68. /**
  69. * Data provider for midrange test
  70. * @return array [ [ numbers ], range ]
  71. */
  72. public function dataProviderForMidrange(): array
  73. {
  74. return [
  75. [ [ 1, 1, 1 ], 1 ],
  76. [ [ 1, 1, 2 ], 1.5 ],
  77. [ [ 1, 2, 1 ], 1.5 ],
  78. [ [ 8, 4, 3 ], 5.5 ],
  79. [ [ 9, 7, 8 ], 8 ],
  80. [ [ 13, 18, 13, 14, 13, 16, 14, 21, 13 ], 17 ],
  81. [ [ 1, 2, 4, 7 ], 4 ],
  82. [ [ 8, 9, 10, 10, 10, 11, 11, 11, 12, 13 ], 10.5 ],
  83. [ [ 6, 7, 8, 10, 12, 14, 14, 15, 16, 20 ], 13 ],
  84. [ [ 9, 10, 11, 13, 15, 17, 17, 18, 19, 23 ], 16 ],
  85. [ [ 12, 14, 16, 20, 24, 28, 28, 30, 32, 40 ], 26 ],
  86. ];
  87. }
  88. /**
  89. * @test midrange when the array is empty
  90. * @throws \Exception
  91. */
  92. public function testMidrangeExceptionWhenEmptyArray()
  93. {
  94. // Given
  95. $numbers = [];
  96. // Then
  97. $this->expectException(Exception\BadDataException::class);
  98. // When
  99. Descriptive::midrange($numbers);
  100. }
  101. /**
  102. * @test populationVariance
  103. * @dataProvider dataProviderForPopulationVariance
  104. * @param array $numbers
  105. * @param float $expectedVariance
  106. * @throws \Exception
  107. */
  108. public function testPopulationVariance(array $numbers, float $expectedVariance)
  109. {
  110. // When
  111. $variance = Descriptive::populationVariance($numbers);
  112. // Then
  113. $this->assertEqualsWithDelta($expectedVariance, $variance, 0.01);
  114. }
  115. /**
  116. * Data provider for population variance test
  117. * @return array [ [ numbers ], variance ]
  118. */
  119. public function dataProviderForPopulationVariance(): array
  120. {
  121. return [
  122. [ [ -10, 0, 10, 20, 30 ], 200 ],
  123. [ [ 8, 9, 10, 11, 12 ], 2 ],
  124. [ [ 600, 470, 170, 430, 300 ], 21704 ],
  125. [ [ -5, 1, 8, 7, 2], 21.84 ],
  126. [ [ 3, 7, 34, 25, 46, 7754, 3, 6 ], 6546331.937 ],
  127. [ [ 4, 6, 2, 2, 2, 2, 3, 4, 1, 3 ], 1.89 ],
  128. [ [ -3432, 5, 23, 9948, -74 ], 20475035.6 ],
  129. ];
  130. }
  131. /**
  132. * @test populationVariance when the array is empty
  133. * @throws \Exception
  134. */
  135. public function testPopulationVarianceExceptionWhenEmptyArray()
  136. {
  137. // Given
  138. $numbers = [];
  139. // Then
  140. $this->expectException(Exception\BadDataException::class);
  141. // When
  142. Descriptive::populationVariance($numbers);
  143. }
  144. /**
  145. * @test sampleVariance
  146. * @dataProvider dataProviderForSampleVariance
  147. * @param array $numbers
  148. * @param float $expectedVariance
  149. * @throws \Exception
  150. */
  151. public function testSampleVariance(array $numbers, float $expectedVariance)
  152. {
  153. // When
  154. $variance = Descriptive::sampleVariance($numbers);
  155. // Then
  156. $this->assertEqualsWithDelta($expectedVariance, $variance, 0.01);
  157. }
  158. /**
  159. * Data provider for sample variance test
  160. * @return array [ [ numbers ], variance ]
  161. */
  162. public function dataProviderForSampleVariance(): array
  163. {
  164. return [
  165. [ [ -10, 0, 10, 20, 30 ], 250 ],
  166. [ [ 8, 9, 10, 11, 12 ], 2.5 ],
  167. [ [ 600, 470, 170, 430, 300 ], 27130 ],
  168. [ [ -5, 1, 8, 7, 2 ], 27.3 ],
  169. [ [ 3, 7, 34, 25, 46, 7754, 3, 6 ], 7481522.21429 ],
  170. [ [ 4, 6, 2, 2, 2, 2, 3, 4, 1, 3 ], 2.1 ],
  171. [ [ -3432, 5, 23, 9948, -74 ], 25593794.5 ],
  172. [ [ 3, 21, 98, 203, 17, 9 ], 6219.9 ],
  173. [ [ 170, 300, 430, 470, 600 ], 27130 ],
  174. [ [ 1550, 1700, 900, 850, 1000, 950 ], 135416.66668 ],
  175. [ [ 1245, 1255, 1654, 1547, 1787, 1989, 1878, 2011, 2145, 2545, 2656 ], 210804.29090909063 ],
  176. ];
  177. }
  178. /**
  179. * @test sampleVariance when the array is empty
  180. * @throws \Exception
  181. */
  182. public function testSampleVarianceExceptionWhenEmptyArray()
  183. {
  184. // Given
  185. $numbers = [];
  186. // Then
  187. $this->expectException(Exception\BadDataException::class);
  188. // When
  189. Descriptive::sampleVariance($numbers);
  190. }
  191. /**
  192. * @test sampleVariance when the array only contains one item
  193. * @throws \Exception
  194. */
  195. public function testSampleVarianceZeroWhenListContainsOnlyOneItem()
  196. {
  197. // When
  198. $variance = Descriptive::sampleVariance([5]);
  199. // Then
  200. $this->assertEquals(0, $variance);
  201. }
  202. /**
  203. * @test variance when the degrees of freedom is less than zero
  204. * @throws \Exception
  205. */
  206. public function testVarianceExceptionDFLessThanZero()
  207. {
  208. // Given
  209. $numbers = [1, 2, 3];
  210. $ν = -1;
  211. // Then
  212. $this->expectException(Exception\OutOfBoundsException::class);
  213. // When
  214. Descriptive::variance($numbers, $ν);
  215. }
  216. /**
  217. * @test weightedSampleVariance unbiased
  218. * @dataProvider dataProviderForWeightedSampleVarianceUnbiased
  219. * @param array $numbers
  220. * @param array $weights
  221. * @param float $expectedVariance
  222. * @throws \Exception
  223. */
  224. public function testWeightedSampleVarianceUnbiased(array $numbers, array $weights, float $expectedVariance)
  225. {
  226. // When
  227. $variance = Descriptive::weightedSampleVariance($numbers, $weights);
  228. // Then
  229. $this->assertEqualsWithDelta($expectedVariance, $variance, 0.00001);
  230. }
  231. /**
  232. * Data provider for weighted sample variance test
  233. * @return array [ [ numbers, weights ], variance ]
  234. */
  235. public function dataProviderForWeightedSampleVarianceUnbiased(): array
  236. {
  237. return [
  238. [ [ -10, 0, 10, 20, 30 ], [1, 1, 1, 1, 1], 250 ],
  239. [ [ 8, 9, 10, 11, 12 ], [1, 1, 1, 1, 1], 2.5 ],
  240. [ [ 600, 470, 170, 430, 300 ], [1, 1, 1, 1, 1], 27130 ],
  241. [ [ -5, 1, 8, 7, 2 ], [1, 1, 1, 1, 1], 27.3 ],
  242. [ [ 3, 7, 34, 25, 46, 7754, 3, 6 ], [1, 1, 1, 1, 1, 1, 1, 1], 7481522.21429 ],
  243. ];
  244. }
  245. /**
  246. * @test weightedSampleVariance biased
  247. * @dataProvider dataProviderForWeightedSampleVarianceBiased
  248. * @param array $numbers
  249. * @param array $weights
  250. * @param float $expectedVariance
  251. * @throws \Exception
  252. */
  253. public function testWeightedSampleVarianceBiased(array $numbers, array $weights, float $expectedVariance)
  254. {
  255. // Given
  256. $biased = true;
  257. // When
  258. $variance = Descriptive::weightedSampleVariance($numbers, $weights, $biased);
  259. // Then
  260. $this->assertEqualsWithDelta($expectedVariance, $variance, 0.1);
  261. }
  262. /**
  263. * Data provider for weighted sample variance test
  264. * Test data created with R package Weighted.Desc.Stat: w.var(x, w)
  265. * @return array [ [ numbers, weights ], variance ]
  266. */
  267. public function dataProviderForWeightedSampleVarianceBiased(): array
  268. {
  269. return [
  270. [ [ -10, 0, 10, 20, 30 ], [1, 1, 1, 1, 1], 200 ],
  271. [ [ 8, 9, 10, 11, 12 ], [1, 1, 1, 1, 1], 2 ],
  272. [ [ 8, 9, 10, 11, 12 ], [0.3, 0.3, 0.2, 0.2, 0.1], 1.702479 ],
  273. [ [ 600, 470, 170, 430, 300 ], [1, 1, 1, 1, 1], 21704 ],
  274. [ [ -5, 1, 8, 7, 2 ], [1, 1, 1, 1, 1], 21.84 ],
  275. [ [ 3, 7, 34, 25, 46, 7754, 3, 6 ], [1, 1, 1, 1, 1, 1, 1, 1], 6546332 ],
  276. ];
  277. }
  278. /**
  279. * @test weightedSampleVariance is zero when there is only one number.
  280. * @throws Exception\BadDataException
  281. */
  282. public function testWeightedSampleVarianceSetOfOne()
  283. {
  284. // Given
  285. $numbers = [4];
  286. $weights = [1];
  287. // When
  288. $variance = Descriptive::weightedSampleVariance($numbers, $weights);
  289. // Then
  290. $this->assertEquals(0, $variance);
  291. }
  292. /**
  293. * @test weightedSampleVariance throws a BadDataException if the weights and numbers have different counts
  294. * @throws Exception\BadDataException
  295. */
  296. public function testWeightedSampleVarianceException()
  297. {
  298. // Given
  299. $numbers = [1, 2, 3];
  300. $weights = [1, 1];
  301. // Then
  302. $this->expectException(Exception\BadDataException::class);
  303. // When
  304. Descriptive::weightedSampleVariance($numbers, $weights);
  305. }
  306. /**
  307. * @test standardDeviation using population variance
  308. * @dataProvider dataProviderForStandardDeviationUsingPopulationVariance
  309. * @param array $numbers
  310. * @param float $expectedStandardDeviation
  311. * @throws \Exception
  312. */
  313. public function testStandardDeviationUsingPopulationVariance(array $numbers, float $expectedStandardDeviation)
  314. {
  315. // When
  316. $sd = Descriptive::standardDeviation($numbers, true);
  317. // Then
  318. $this->assertEqualsWithDelta($expectedStandardDeviation, $sd, 0.01);
  319. }
  320. /**
  321. * @test sd using population variance
  322. * @dataProvider dataProviderForStandardDeviationUsingPopulationVariance
  323. * @param array $numbers
  324. * @param float $expectedStandardDeviation
  325. * @throws \Exception
  326. */
  327. public function testSdUsingPopulationVariance(array $numbers, float $expectedStandardDeviation)
  328. {
  329. // When
  330. $sd = Descriptive::sd($numbers, true);
  331. // Then
  332. $this->assertEqualsWithDelta($expectedStandardDeviation, $sd, 0.01);
  333. }
  334. /**
  335. * Data provider for standard deviation test
  336. * @return array [ [ numbers ], mean ]
  337. */
  338. public function dataProviderForStandardDeviationUsingPopulationVariance(): array
  339. {
  340. return [
  341. [ [ -10, 0, 10, 20, 30 ], 10 * \sqrt(2) ],
  342. [ [ 8, 9, 10, 11, 12 ], \sqrt(2) ],
  343. [ [ 600, 470, 170, 430, 300], 147.32 ],
  344. [ [ -5, 1, 8, 7, 2], 4.67 ],
  345. [ [ 3, 7, 34, 25, 46, 7754, 3, 6 ], 2558.580063 ],
  346. [ [ 4, 6, 2, 2, 2, 2, 3, 4, 1, 3 ], 1.374772708 ],
  347. [ [ -3432, 5, 23, 9948, -74 ], 4524.934872 ],
  348. ];
  349. }
  350. /**
  351. * @test standardDeviation using sample variance
  352. * @dataProvider dataProviderForStandardDeviationUsingSampleVariance
  353. * @param array $numbers
  354. * @param float $expectedStandardDeviation
  355. * @throws \Exception
  356. */
  357. public function testStandardDeviationUsingSampleVariance(array $numbers, float $expectedStandardDeviation)
  358. {
  359. // When
  360. $sd = Descriptive::standardDeviation($numbers);
  361. // Then
  362. $this->assertEqualsWithDelta($expectedStandardDeviation, $sd, 0.01);
  363. }
  364. /**
  365. * @test sd using sample variance
  366. * @dataProvider dataProviderForStandardDeviationUsingSampleVariance
  367. * @param array $numbers
  368. * @param float $expectedStandardDeviation
  369. * @throws \Exception
  370. */
  371. public function testSDeviationUsingSampleVariance(array $numbers, float $expectedStandardDeviation)
  372. {
  373. // When
  374. $sd = Descriptive::sd($numbers);
  375. // Then
  376. $this->assertEqualsWithDelta($expectedStandardDeviation, $sd, 0.01);
  377. }
  378. /**
  379. * Data provider for standard deviation using sample variance test
  380. * @return array [ [ numbers ], mean ]
  381. */
  382. public function dataProviderForStandardDeviationUsingSampleVariance(): array
  383. {
  384. return [
  385. [ [ 3, 21, 98, 203, 17, 9 ], 78.86634 ],
  386. [ [ 170, 300, 430, 470, 600 ], 164.7118696390761 ],
  387. [ [ 1550, 1700, 900, 850, 1000, 950 ], 367.99 ],
  388. [ [ 1245, 1255, 1654, 1547, 1787, 1989, 1878, 2011, 2145, 2545, 2656 ], 459.13 ],
  389. ];
  390. }
  391. /**
  392. * @test standardDeviation when the array is empty
  393. * @throws \Exception
  394. */
  395. public function testStandardDeviationExceptionWhenEmptyArray()
  396. {
  397. // Given
  398. $numbers = [];
  399. // Then
  400. $this->expectException(Exception\BadDataException::class);
  401. // When
  402. Descriptive::standardDeviation($numbers);
  403. }
  404. /**
  405. * @test sd when the array is empty
  406. * @throws \Exception
  407. */
  408. public function testSDExceptionWhenEmptyArray()
  409. {
  410. // Given
  411. $numbers = [];
  412. // Then
  413. $this->expectException(Exception\BadDataException::class);
  414. // When
  415. Descriptive::sd($numbers);
  416. }
  417. /**
  418. * @test meanAbsoluteDeviation
  419. * @dataProvider dataProviderForMeanAbsoluteDeviation
  420. * @param array $numbers
  421. * @param float $expectedMad
  422. * @throws \Exception
  423. */
  424. public function testMeanAbsoluteDeviation(array $numbers, float $expectedMad)
  425. {
  426. // When
  427. $mad = Descriptive::meanAbsoluteDeviation($numbers);
  428. // Then
  429. $this->assertEqualsWithDelta($expectedMad, $mad, 0.01);
  430. }
  431. /**
  432. * Data provider for MAD (mean) test
  433. * @return array [ [ numbers ], mad ]
  434. */
  435. public function dataProviderForMeanAbsoluteDeviation(): array
  436. {
  437. return [
  438. [ [ 92, 83, 88, 94, 91, 85, 89, 90 ], 2.75 ],
  439. [ [ 2, 2, 3, 4, 14 ], 3.6 ],
  440. ];
  441. }
  442. /**
  443. * @test meanAbsoluteDeviation when the array is empty
  444. * @throws \Exception
  445. */
  446. public function testMeanAbsoluteDeviationExceptionWhenEmptyArray()
  447. {
  448. // Given
  449. $numbers = [];
  450. // Then
  451. $this->expectException(Exception\BadDataException::class);
  452. // When
  453. Descriptive::meanAbsoluteDeviation($numbers);
  454. }
  455. /**
  456. * @test medianAbsoluteDeviation
  457. * @dataProvider dataProviderForMedianAbsoluteDeviation
  458. * @param array $numbers
  459. * @param float $expectedMad
  460. * @throws \Exception
  461. */
  462. public function testMedianAbsoluteDeviation(array $numbers, float $expectedMad)
  463. {
  464. // When
  465. $mad = Descriptive::medianAbsoluteDeviation($numbers);
  466. // Then
  467. $this->assertEqualsWithDelta($expectedMad, $mad, 0.01);
  468. }
  469. /**
  470. * Data provider for MAD (median) test
  471. * @return array [ [ numbers ], mad ]
  472. */
  473. public function dataProviderForMedianAbsoluteDeviation(): array
  474. {
  475. return [
  476. [ [ 1, 1, 2, 2, 4, 6, 9 ], 1 ],
  477. [ [ 92, 83, 88, 94, 91, 85, 89, 90 ], 2 ],
  478. [ [ 2, 2, 3, 4, 14 ], 1 ],
  479. ];
  480. }
  481. /**
  482. * @test medianAbsoluteDeviation when array is empty
  483. * @throws \Exception
  484. */
  485. public function testMedianAbsoluteDeviationExceptionWhenEmptyArray()
  486. {
  487. // Given
  488. $numbers = [];
  489. // Then
  490. $this->expectException(Exception\BadDataException::class);
  491. // When
  492. Descriptive::medianAbsoluteDeviation($numbers);
  493. }
  494. /**
  495. * @test quartilesExclusive
  496. * @dataProvider dataProviderForQuartilesExclusive
  497. * @param array $numbers
  498. * @param array $expectedQuartiles
  499. * @throws \Exception
  500. */
  501. public function testQuartilesExclusive(array $numbers, array $expectedQuartiles)
  502. {
  503. // When
  504. $quartiles = Descriptive::quartilesExclusive($numbers);
  505. // Then
  506. $this->assertEquals($expectedQuartiles, $quartiles);
  507. }
  508. /**
  509. * @return array [numbers, quartiles]
  510. */
  511. public function dataProviderForQuartilesExclusive(): array
  512. {
  513. return [
  514. [
  515. [ 6, 7, 15, 36, 39, 40, 41, 42, 43, 47, 49],
  516. [ '0%' => 6, 'Q1' => 15, 'Q2' => 40, 'Q3' => 43, '100%' => 49, 'IQR' => 28 ],
  517. ],
  518. [
  519. [ 7, 15, 36, 39, 40, 41 ],
  520. [ '0%' => 7, 'Q1' => 15, 'Q2' => 37.5, 'Q3' => 40, '100%' => 41, 'IQR' => 25 ],
  521. ],
  522. [
  523. [ 41, 15, 39, 36, 40, 7 ],
  524. [ '0%' => 7, 'Q1' => 15, 'Q2' => 37.5, 'Q3' => 40, '100%' => 41, 'IQR' => 25 ],
  525. ],
  526. [
  527. [ 0, 2, 2, 4, 5, 6, 7, 7, 8, 9, 34, 34, 43, 54, 54, 76, 234 ],
  528. [ '0%' => 0, 'Q1' => 4.5, 'Q2' => 8, 'Q3' => 48.5, '100%' => 234, 'IQR' => 44 ],
  529. ],
  530. [
  531. [0],
  532. [ '0%' => 0, 'Q1' => 0, 'Q2' => 0, 'Q3' => 0, '100%' => 0, 'IQR' => 0 ],
  533. ],
  534. [
  535. [4, 6, 6, 7, 8, 12, 15, 17, 20, 21, 21, 23, 24, 27, 28],
  536. [ '0%' => 4, 'Q1' => 7, 'Q2' => 17, 'Q3' => 23, '100%' => 28, 'IQR' => 16 ],
  537. ],
  538. [
  539. [0, 900, 1800, 2700, 3600, 4500],
  540. [ '0%' => 0, 'Q1' => 900, 'Q2' => 2250, 'Q3' => 3600, '100%' => 4500, 'IQR' => 2700 ],
  541. ],
  542. ];
  543. }
  544. /**
  545. * @test quartilesExclusive when the array is empty
  546. * @throws \Exception
  547. */
  548. public function testQuartilesExclusiveExceptionWhenEmptyArray()
  549. {
  550. // Given
  551. $numbers = [];
  552. // Then
  553. $this->expectException(Exception\BadDataException::class);
  554. // When
  555. Descriptive::quartilesExclusive($numbers);
  556. }
  557. /**
  558. * @test quartilesInclusive
  559. * @dataProvider dataProviderForQuartilesInclusive
  560. * @param array $numbers
  561. * @param array $expectedQuartiles
  562. * @throws \Exception
  563. */
  564. public function testQuartilesInclusive(array $numbers, array $expectedQuartiles)
  565. {
  566. // When
  567. $quartiles = Descriptive::quartilesInclusive($numbers);
  568. // Then
  569. $this->assertEquals($expectedQuartiles, $quartiles);
  570. }
  571. /**
  572. * @return array [numbers, quartiles]
  573. */
  574. public function dataProviderForQuartilesInclusive(): array
  575. {
  576. return [
  577. [
  578. [ 6, 7, 15, 36, 39, 40, 41, 42, 43, 47, 49],
  579. [ '0%' => 6, 'Q1' => 25.5, 'Q2' => 40, 'Q3' => 42.5, '100%' => 49, 'IQR' => 17 ],
  580. ],
  581. [
  582. [ 7, 15, 36, 39, 40, 41 ],
  583. [ '0%' => 7, 'Q1' => 15, 'Q2' => 37.5, 'Q3' => 40, '100%' => 41, 'IQR' => 25 ],
  584. ],
  585. [
  586. [ 41, 15, 39, 36, 40, 7 ],
  587. [ '0%' => 7, 'Q1' => 15, 'Q2' => 37.5, 'Q3' => 40, '100%' => 41, 'IQR' => 25 ],
  588. ],
  589. [
  590. [ 0, 2, 2, 4, 5, 6, 7, 7, 8, 9, 34, 34, 43, 54, 54, 76, 234 ],
  591. [ '0%' => 0, 'Q1' => 5, 'Q2' => 8, 'Q3' => 43, '100%' => 234, 'IQR' => 38 ],
  592. ],
  593. [
  594. [4, 6, 6, 7, 8, 12, 15, 17, 20, 21, 21, 23, 24, 27, 28],
  595. [ '0%' => 4, 'Q1' => 7.5, 'Q2' => 17, 'Q3' => 22, '100%' => 28, 'IQR' => 14.5 ],
  596. ],
  597. [
  598. [0, 900, 1800, 2700, 3600, 4500],
  599. [ '0%' => 0, 'Q1' => 900, 'Q2' => 2250, 'Q3' => 3600, '100%' => 4500, 'IQR' => 2700 ],
  600. ]
  601. ];
  602. }
  603. /**
  604. * @test quartilesInclusive when the array is empty
  605. * @throws \Exception
  606. */
  607. public function testQuartilesInclusiveExceptionWhenEmptyArray()
  608. {
  609. // Given
  610. $numbers = [];
  611. // Then
  612. $this->expectException(Exception\BadDataException::class);
  613. // When
  614. Descriptive::quartilesInclusive($numbers);
  615. }
  616. /**
  617. * @test quartiles
  618. * @dataProvider dataProviderForQuartiles
  619. * @param array $numbers
  620. * @param string $method
  621. * @param array $expectedQuartiles
  622. * @throws \Exception
  623. */
  624. public function testQuartiles(array $numbers, string $method, array $expectedQuartiles)
  625. {
  626. // When
  627. $quartiles = Descriptive::quartiles($numbers, $method);
  628. // Then
  629. $this->assertEquals($expectedQuartiles, $quartiles);
  630. }
  631. /**
  632. * @return array [numbers, method, quartiles]
  633. */
  634. public function dataProviderForQuartiles(): array
  635. {
  636. return [
  637. [
  638. [ 6, 7, 15, 36, 39, 40, 41, 42, 43, 47, 49 ], 'Exclusive',
  639. [ '0%' => 6, 'Q1' => 15, 'Q2' => 40, 'Q3' => 43, '100%' => 49, 'IQR' => 28 ],
  640. ],
  641. [
  642. [ 7, 15, 36, 39, 40, 41 ], 'Exclusive',
  643. [ '0%' => 7, 'Q1' => 15, 'Q2' => 37.5, 'Q3' => 40, '100%' => 41, 'IQR' => 25 ],
  644. ],
  645. [
  646. [ 0, 2, 2, 4, 5, 6, 7, 7, 8, 9, 34, 34, 43, 54, 54, 76, 234 ], 'Exclusive',
  647. [ '0%' => 0, 'Q1' => 4.5, 'Q2' => 8, 'Q3' => 48.5, '100%' => 234, 'IQR' => 44 ],
  648. ],
  649. [
  650. [ 6, 7, 15, 36, 39, 40, 41, 42, 43, 47, 49 ], 'Inclusive',
  651. [ '0%' => 6, 'Q1' => 25.5, 'Q2' => 40, 'Q3' => 42.5, '100%' => 49, 'IQR' => 17 ],
  652. ],
  653. [
  654. [ 7, 15, 36, 39, 40, 41 ], 'Inclusive',
  655. [ '0%' => 7, 'Q1' => 15, 'Q2' => 37.5, 'Q3' => 40, '100%' => 41, 'IQR' => 25 ],
  656. ],
  657. [
  658. [ 0, 2, 2, 4, 5, 6, 7, 7, 8, 9, 34, 34, 43, 54, 54, 76, 234 ], 'Inclusive',
  659. [ '0%' => 0, 'Q1' => 5, 'Q2' => 8, 'Q3' => 43, '100%' => 234, 'IQR' => 38 ],
  660. ],
  661. [
  662. [ 6, 7, 15, 36, 39, 40, 41, 42, 43, 47, 49 ], 'Not_A_Real_Method_So_Default_Is_Used_Which_Is_Exclusive',
  663. [ '0%' => 6, 'Q1' => 15, 'Q2' => 40, 'Q3' => 43, '100%' => 49, 'IQR' => 28 ],
  664. ],
  665. [
  666. [ 7, 15, 36, 39, 40, 41 ], 'Not_A_Real_Method_So_Default_Is_Used_Which_Is_Exclusive',
  667. [ '0%' => 7, 'Q1' => 15, 'Q2' => 37.5, 'Q3' => 40, '100%' => 41, 'IQR' => 25 ],
  668. ],
  669. [
  670. [ 0, 2, 2, 4, 5, 6, 7, 7, 8, 9, 34, 34, 43, 54, 54, 76, 234 ], 'Not_A_Real_Method_So_Default_Is_Used_Which_Is_Exclusive',
  671. [ '0%' => 0, 'Q1' => 4.5, 'Q2' => 8, 'Q3' => 48.5, '100%' => 234, 'IQR' => 44 ],
  672. ],
  673. ];
  674. }
  675. /**
  676. * @test interquartileRange
  677. * @dataProvider dataProviderForIQR
  678. * @param array $numbers
  679. * @param float $expectedIqr
  680. * @throws \Exception
  681. */
  682. public function testInterquartileRange(array $numbers, float $expectedIqr)
  683. {
  684. // When
  685. $iqr = Descriptive::interquartileRange($numbers);
  686. // Then
  687. $this->assertEquals($expectedIqr, $iqr);
  688. }
  689. /**
  690. * @test iqr
  691. * @dataProvider dataProviderForIQR
  692. * @param array $numbers
  693. * @param float $expectedIqr
  694. * @throws \Exception
  695. */
  696. public function testIQR(array $numbers, float $expectedIqr)
  697. {
  698. // When
  699. $iqr = Descriptive::iqr($numbers);
  700. // Then
  701. $this->assertEquals($expectedIqr, $iqr);
  702. }
  703. /**
  704. * @return array [numbers, iqr]
  705. */
  706. public function dataProviderForIQR(): array
  707. {
  708. return [
  709. [ [6, 7, 15, 36, 39, 40, 41, 42, 43, 47, 49], 28 ],
  710. [ [7, 15, 36, 39, 40, 41 ], 25 ],
  711. [ [0], 0 ],
  712. [ [1], 0 ],
  713. [ [9], 0 ],
  714. ];
  715. }
  716. /**
  717. * @test percentile
  718. * @dataProvider dataProviderForPercentile
  719. * @param array $numbers
  720. * @param float $percentile
  721. * @param float $expectedValue
  722. * @throws \Exception
  723. */
  724. public function testPercentile(array $numbers, float $percentile, float $expectedValue)
  725. {
  726. // When
  727. $value = Descriptive::percentile($numbers, $percentile);
  728. // Then
  729. $this->assertEqualsWithDelta($expectedValue, $value, 0.0000001);
  730. }
  731. /**
  732. * @return array [numbers, percentile, value]
  733. */
  734. public function dataProviderForPercentile(): array
  735. {
  736. return [
  737. // Wikipedia
  738. [[15, 20, 35, 40, 50], 40, 29],
  739. [[1, 2, 3, 4], 75, 3.25],
  740. // numpy.percentile / Excel 2015 Mac
  741. // All int percentiles 0 - 100
  742. [[15, 20, 35, 40, 50], 0, 15],
  743. [[15, 20, 35, 40, 50], 1, 15.2],
  744. [[15, 20, 35, 40, 50], 2, 15.4],
  745. [[15, 20, 35, 40, 50], 3, 15.6],
  746. [[15, 20, 35, 40, 50], 4, 15.8],
  747. [[15, 20, 35, 40, 50], 5, 16.0],
  748. [[15, 20, 35, 40, 50], 6, 16.2],
  749. [[15, 20, 35, 40, 50], 7, 16.4],
  750. [[15, 20, 35, 40, 50], 8, 16.6],
  751. [[15, 20, 35, 40, 50], 9, 16.8],
  752. [[15, 20, 35, 40, 50], 10, 17.0],
  753. [[15, 20, 35, 40, 50], 11, 17.2],
  754. [[15, 20, 35, 40, 50], 12, 17.4],
  755. [[15, 20, 35, 40, 50], 13, 17.6],
  756. [[15, 20, 35, 40, 50], 14, 17.8],
  757. [[15, 20, 35, 40, 50], 15, 18.0],
  758. [[15, 20, 35, 40, 50], 16, 18.2],
  759. [[15, 20, 35, 40, 50], 17, 18.4],
  760. [[15, 20, 35, 40, 50], 18, 18.6],
  761. [[15, 20, 35, 40, 50], 19, 18.8],
  762. [[15, 20, 35, 40, 50], 20, 19.0],
  763. [[15, 20, 35, 40, 50], 21, 19.2],
  764. [[15, 20, 35, 40, 50], 22, 19.4],
  765. [[15, 20, 35, 40, 50], 23, 19.6],
  766. [[15, 20, 35, 40, 50], 24, 19.8],
  767. [[15, 20, 35, 40, 50], 25, 20.0],
  768. [[15, 20, 35, 40, 50], 26, 20.6],
  769. [[15, 20, 35, 40, 50], 27, 21.2],
  770. [[15, 20, 35, 40, 50], 28, 21.8],
  771. [[15, 20, 35, 40, 50], 29, 22.4],
  772. [[15, 20, 35, 40, 50], 30, 23.0],
  773. [[15, 20, 35, 40, 50], 31, 23.6],
  774. [[15, 20, 35, 40, 50], 32, 24.2],
  775. [[15, 20, 35, 40, 50], 33, 24.8],
  776. [[15, 20, 35, 40, 50], 34, 25.4],
  777. [[15, 20, 35, 40, 50], 35, 26.0],
  778. [[15, 20, 35, 40, 50], 36, 26.6],
  779. [[15, 20, 35, 40, 50], 37, 27.2],
  780. [[15, 20, 35, 40, 50], 38, 27.8],
  781. [[15, 20, 35, 40, 50], 39, 28.4],
  782. [[15, 20, 35, 40, 50], 40, 29.0],
  783. [[15, 20, 35, 40, 50], 41, 29.6],
  784. [[15, 20, 35, 40, 50], 42, 30.2],
  785. [[15, 20, 35, 40, 50], 43, 30.8],
  786. [[15, 20, 35, 40, 50], 44, 31.4],
  787. [[15, 20, 35, 40, 50], 45, 32.0],
  788. [[15, 20, 35, 40, 50], 46, 32.6],
  789. [[15, 20, 35, 40, 50], 47, 33.2],
  790. [[15, 20, 35, 40, 50], 48, 33.8],
  791. [[15, 20, 35, 40, 50], 49, 34.4],
  792. [[15, 20, 35, 40, 50], 50, 35.0],
  793. [[15, 20, 35, 40, 50], 51, 35.2],
  794. [[15, 20, 35, 40, 50], 52, 35.4],
  795. [[15, 20, 35, 40, 50], 53, 35.6],
  796. [[15, 20, 35, 40, 50], 54, 35.8],
  797. [[15, 20, 35, 40, 50], 55, 36.0],
  798. [[15, 20, 35, 40, 50], 56, 36.2],
  799. [[15, 20, 35, 40, 50], 57, 36.4],
  800. [[15, 20, 35, 40, 50], 58, 36.6],
  801. [[15, 20, 35, 40, 50], 59, 36.8],
  802. [[15, 20, 35, 40, 50], 60, 37.0],
  803. [[15, 20, 35, 40, 50], 61, 37.2],
  804. [[15, 20, 35, 40, 50], 62, 37.4],
  805. [[15, 20, 35, 40, 50], 63, 37.6],
  806. [[15, 20, 35, 40, 50], 64, 37.8],
  807. [[15, 20, 35, 40, 50], 65, 38.0],
  808. [[15, 20, 35, 40, 50], 66, 38.2],
  809. [[15, 20, 35, 40, 50], 67, 38.4],
  810. [[15, 20, 35, 40, 50], 68, 38.6],
  811. [[15, 20, 35, 40, 50], 69, 38.8],
  812. [[15, 20, 35, 40, 50], 70, 39.0],
  813. [[15, 20, 35, 40, 50], 71, 39.2],
  814. [[15, 20, 35, 40, 50], 72, 39.4],
  815. [[15, 20, 35, 40, 50], 73, 39.6],
  816. [[15, 20, 35, 40, 50], 74, 39.8],
  817. [[15, 20, 35, 40, 50], 75, 40.0],
  818. [[15, 20, 35, 40, 50], 76, 40.4],
  819. [[15, 20, 35, 40, 50], 77, 40.8],
  820. [[15, 20, 35, 40, 50], 78, 41.2],
  821. [[15, 20, 35, 40, 50], 79, 41.6],
  822. [[15, 20, 35, 40, 50], 80, 42.0],
  823. [[15, 20, 35, 40, 50], 81, 42.4],
  824. [[15, 20, 35, 40, 50], 82, 42.8],
  825. [[15, 20, 35, 40, 50], 83, 43.2],
  826. [[15, 20, 35, 40, 50], 84, 43.6],
  827. [[15, 20, 35, 40, 50], 85, 44.0],
  828. [[15, 20, 35, 40, 50], 86, 44.4],
  829. [[15, 20, 35, 40, 50], 87, 44.8],
  830. [[15, 20, 35, 40, 50], 88, 45.2],
  831. [[15, 20, 35, 40, 50], 89, 45.6],
  832. [[15, 20, 35, 40, 50], 90, 46.0],
  833. [[15, 20, 35, 40, 50], 91, 46.4],
  834. [[15, 20, 35, 40, 50], 92, 46.8],
  835. [[15, 20, 35, 40, 50], 93, 47.2],
  836. [[15, 20, 35, 40, 50], 94, 47.6],
  837. [[15, 20, 35, 40, 50], 95, 48.0],
  838. [[15, 20, 35, 40, 50], 96, 48.4],
  839. [[15, 20, 35, 40, 50], 97, 48.8],
  840. [[15, 20, 35, 40, 50], 98, 49.2],
  841. [[15, 20, 35, 40, 50], 99, 49.6],
  842. [[15, 20, 35, 40, 50], 100, 50.0],
  843. // Float percentiles
  844. [[15, 20, 35, 40, 50], 0.5, 15.1],
  845. [[15, 20, 35, 40, 50], 1.5, 15.299999999999999],
  846. [[15, 20, 35, 40, 50], 5.5, 16.100000000000001],
  847. [[15, 20, 35, 40, 50], 50.5, 35.099999999999994],
  848. [[15, 20, 35, 40, 50], 60.2, 37.039999999999999],
  849. [[15, 20, 35, 40, 50], 91.8, 46.719999999999999],
  850. [[15, 20, 35, 40, 50], 99.9, 49.960000000000008],
  851. // Edge case: one-element list
  852. [[5], 0, 5],
  853. [[5], 1, 5],
  854. [[5], 50.5, 5],
  855. [[5], 99, 5],
  856. [[5], 100, 5],
  857. // Two-element list
  858. [[2, 3], 0, 2],
  859. [[2, 3], 1, 2.01],
  860. [[2, 3], 50.5, 2.505],
  861. [[2, 3], 99, 2.9899999999999998],
  862. [[2, 3], 100, 3],
  863. // Big list
  864. [[1,2,3,4,5,6,7,8,9,9,8,7,6,5,4,3,2,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,9,9,9,9,9,9,9,8,9,8,9,8,9,8,7,6,5,4,3,2,1,2,3,4,3,4,3,4,5,6,7,8,7,8,7,8,9,0,0,9,0,9,8,7,6,5,4,3,2,1], 45.3, 5],
  865. // More random test cases
  866. [[3, 6, 7, 8, 8, 10, 13, 15, 16, 20], 0, 3.0 ],
  867. [[3, 6, 7, 8, 8, 10, 13, 15, 16, 20], 25, 7.25 ],
  868. [[3, 6, 7, 8, 8, 10, 13, 15, 16, 20], 50, 9.0 ],
  869. [[3, 6, 7, 8, 8, 10, 13, 15, 16, 20], 75, 14.5 ],
  870. [[3, 6, 7, 8, 8, 10, 13, 15, 16, 20], 100, 20.0 ],
  871. [[3, 6, 7, 8, 8, 9, 10, 13, 15, 16, 20], 0, 3.0 ],
  872. [[3, 6, 7, 8, 8, 9, 10, 13, 15, 16, 20], 25, 7.5 ],
  873. [[3, 6, 7, 8, 8, 9, 10, 13, 15, 16, 20], 50, 9.0 ],
  874. [[3, 6, 7, 8, 8, 9, 10, 13, 15, 16, 20], 75, 14.0 ],
  875. [[3, 6, 7, 8, 8, 9, 10, 13, 15, 16, 20], 100, 20.0 ],
  876. ];
  877. }
  878. /**
  879. * @test percentile throws an Exception\BadDataException if numbers is empty
  880. * @throws \Exception
  881. */
  882. public function testPercentileEmptyList()
  883. {
  884. // Given
  885. $numbers = [];
  886. $P = 5;
  887. // Then
  888. $this->expectException(Exception\BadDataException::class);
  889. // When
  890. Descriptive::percentile($numbers, $P);
  891. }
  892. /**
  893. * @test percentile throws an Exception\OutOfBoundsException if P is < 0
  894. * @throws \Exception
  895. */
  896. public function testPercentileOutOfLowerBoundsP()
  897. {
  898. // Given
  899. $numbers = [1, 2, 3];
  900. $P = -4;
  901. // Then
  902. $this->expectException(Exception\OutOfBoundsException::class);
  903. // When
  904. Descriptive::percentile($numbers, $P);
  905. }
  906. /**
  907. * @test percentile throws an Exception\OutOfBoundsException if P is > 100
  908. * @throws \Exception
  909. */
  910. public function testPercentileOutOfUpperBoundsP()
  911. {
  912. // Given
  913. $numbers = [1, 2, 3];
  914. $P = 101;
  915. // Then
  916. $this->expectException(Exception\OutOfBoundsException::class);
  917. // When
  918. Descriptive::percentile($numbers, $P);
  919. }
  920. /**
  921. * @test midhinge
  922. * @dataProvider dataProviderForMidhinge
  923. * @param array $numbers
  924. * @param float $expectedMidhinge
  925. * @throws \Exception
  926. */
  927. public function testMidhinge(array $numbers, float $expectedMidhinge)
  928. {
  929. // When
  930. $midhinge = Descriptive::midhinge($numbers);
  931. // Then
  932. $this->assertEqualsWithDelta($expectedMidhinge, $midhinge, 0.01);
  933. }
  934. /**
  935. * @return array [numbers, midhinge]
  936. */
  937. public function dataProviderForMidhinge(): array
  938. {
  939. return [
  940. [ [1, 2, 3, 4, 5, 6], 3.5 ],
  941. [ [5, 5, 7, 8, 8, 11, 12, 12, 14, 15, 16, 19, 21, 22, 22, 26, 26, 26, 28, 29, 29, 32, 33, 34, 34, 34, 34, 35, 35, 37, 38, 38], 23.5],
  942. [ [36, 34, 21, 10, 20, 24, 31, 30, 30, 30, 30, 24, 30, 24, 39, 6, 32, 33, 33, 25, 26, 35, 8, 5, 30, 40, 9, 32, 25, 40, 24, 38], 28.5],
  943. [ [8, 10, 11, 12, 12, 13, 17, 18, 19, 19, 21, 23, 24, 24, 25, 25, 27, 27, 28, 28, 29, 29, 30, 30, 32, 33, 34, 35, 36, 37, 37, 40], 24.75 ],
  944. ];
  945. }
  946. /**
  947. * @test midhinge throws an Exception\BadDataException if numbers is empty
  948. * @throws \Exception
  949. */
  950. public function testMidhingeEmptyList()
  951. {
  952. // Then
  953. $this->expectException(Exception\BadDataException::class);
  954. // When
  955. Descriptive::midhinge([]);
  956. }
  957. /**
  958. * @test coefficientOfVariation
  959. * @dataProvider dataProviderForCoefficientOfVariation
  960. * @param array $numbers
  961. * @param float $expectedCv
  962. * @throws \Exception
  963. */
  964. public function testsCoefficientOfVariation(array $numbers, float $expectedCv)
  965. {
  966. // When
  967. $cv = Descriptive::coefficientOfVariation($numbers);
  968. // Then
  969. $this->assertEqualsWithDelta($expectedCv, $cv, 0.0001);
  970. }
  971. /**
  972. * @return array [numbers, cv]
  973. */
  974. public function dataProviderForCoefficientOfVariation(): array
  975. {
  976. return [
  977. [ [1, 2, 3, 4, 5, 6 ,7, 8], 0.54433 ],
  978. [ [4, 7, 43, 12, 23, 76, 45, 3, 62, 23, 34, 44, 41], 0.70673 ],
  979. [ [3, 3, 3, 6, 6, 5, 9], 0.44721 ],
  980. [ [100, 100, 100], 0 ],
  981. [ [0, 10, 20, 30, 40], 0.7905 ],
  982. [ [32, 50, 68, 86, 104], 0.41852941176471 ],
  983. ];
  984. }
  985. /**
  986. * @test describe - population
  987. * @throws \Exception
  988. */
  989. public function testDescribePopulation()
  990. {
  991. // Given
  992. $population = true;
  993. // When
  994. $stats = Descriptive::describe([ 13, 18, 13, 14, 13, 16, 14, 21, 13 ], $population);
  995. // Then
  996. $this->assertTrue(\is_array($stats));
  997. $this->assertArrayHasKey('n', $stats);
  998. $this->assertArrayHasKey('min', $stats);
  999. $this->assertArrayHasKey('max', $stats);
  1000. $this->assertArrayHasKey('mean', $stats);
  1001. $this->assertArrayHasKey('median', $stats);
  1002. $this->assertArrayHasKey('mode', $stats);
  1003. $this->assertArrayHasKey('range', $stats);
  1004. $this->assertArrayHasKey('midrange', $stats);
  1005. $this->assertArrayHasKey('variance', $stats);
  1006. $this->assertArrayHasKey('sd', $stats);
  1007. $this->assertArrayHasKey('cv', $stats);
  1008. $this->assertArrayHasKey('mean_mad', $stats);
  1009. $this->assertArrayHasKey('median_mad', $stats);
  1010. $this->assertArrayHasKey('quartiles', $stats);
  1011. $this->assertArrayHasKey('midhinge', $stats);
  1012. $this->assertArrayHasKey('skewness', $stats);
  1013. $this->assertArrayHasKey('ses', $stats);
  1014. $this->assertArrayHasKey('kurtosis', $stats);
  1015. $this->assertArrayHasKey('sek', $stats);
  1016. $this->assertArrayHasKey('sem', $stats);
  1017. $this->assertArrayHasKey('ci_95', $stats);
  1018. $this->assertArrayHasKey('ci_99', $stats);
  1019. $this->assertTrue(\is_int($stats['n']));
  1020. $this->assertTrue(\is_numeric($stats['min']));
  1021. $this->assertTrue(\is_numeric($stats['max']));
  1022. $this->assertTrue(\is_numeric($stats['mean']));
  1023. $this->assertTrue(\is_numeric($stats['median']));
  1024. $this->assertTrue(\is_array($stats['mode']));
  1025. $this->assertTrue(\is_numeric($stats['range']));
  1026. $this->assertTrue(\is_numeric($stats['midrange']));
  1027. $this->assertTrue(\is_numeric($stats['variance']));
  1028. $this->assertTrue(\is_numeric($stats['sd']));
  1029. $this->assertTrue(\is_numeric($stats['cv']));
  1030. $this->assertTrue(\is_numeric($stats['mean_mad']));
  1031. $this->assertTrue(\is_numeric($stats['median_mad']));
  1032. $this->assertTrue(\is_array($stats['quartiles']));
  1033. $this->assertTrue(\is_numeric($stats['midhinge']));
  1034. $this->assertTrue(\is_numeric($stats['skewness']));
  1035. $this->assertTrue(\is_numeric($stats['ses']));
  1036. $this->assertTrue(\is_numeric($stats['kurtosis']));
  1037. $this->assertTrue(\is_numeric($stats['sek']));
  1038. $this->assertTrue(\is_numeric($stats['sem']));
  1039. $this->assertTrue(\is_array($stats['ci_95']));
  1040. $this->assertTrue(\is_array($stats['ci_99']));
  1041. }
  1042. /**
  1043. * @test describe - sample
  1044. * @throws \Exception
  1045. */
  1046. public function testDescribeSample()
  1047. {
  1048. // Given
  1049. $population = false;
  1050. // When
  1051. $stats = Descriptive::describe([ 13, 18, 13, 14, 13, 16, 14, 21, 13 ], $population);
  1052. // Then
  1053. $this->assertTrue(\is_array($stats));
  1054. $this->assertArrayHasKey('n', $stats);
  1055. $this->assertArrayHasKey('min', $stats);
  1056. $this->assertArrayHasKey('max', $stats);
  1057. $this->assertArrayHasKey('mean', $stats);
  1058. $this->assertArrayHasKey('median', $stats);
  1059. $this->assertArrayHasKey('mode', $stats);
  1060. $this->assertArrayHasKey('range', $stats);
  1061. $this->assertArrayHasKey('midrange', $stats);
  1062. $this->assertArrayHasKey('variance', $stats);
  1063. $this->assertArrayHasKey('sd', $stats);
  1064. $this->assertArrayHasKey('cv', $stats);
  1065. $this->assertArrayHasKey('quartiles', $stats);
  1066. $this->assertArrayHasKey('midhinge', $stats);
  1067. $this->assertArrayHasKey('skewness', $stats);
  1068. $this->assertArrayHasKey('ses', $stats);
  1069. $this->assertArrayHasKey('kurtosis', $stats);
  1070. $this->assertArrayHasKey('sek', $stats);
  1071. $this->assertArrayHasKey('sem', $stats);
  1072. $this->assertArrayHasKey('ci_95', $stats);
  1073. $this->assertArrayHasKey('ci_99', $stats);
  1074. $this->assertTrue(\is_int($stats['n']));
  1075. $this->assertTrue(\is_numeric($stats['min']));
  1076. $this->assertTrue(\is_numeric($stats['max']));
  1077. $this->assertTrue(\is_numeric($stats['mean']));
  1078. $this->assertTrue(\is_numeric($stats['median']));
  1079. $this->assertTrue(\is_array($stats['mode']));
  1080. $this->assertTrue(\is_numeric($stats['range']));
  1081. $this->assertTrue(\is_numeric($stats['midrange']));
  1082. $this->assertTrue(\is_numeric($stats['variance']));
  1083. $this->assertTrue(\is_numeric($stats['sd']));
  1084. $this->assertTrue(\is_numeric($stats['cv']));
  1085. $this->assertTrue(\is_array($stats['quartiles']));
  1086. $this->assertTrue(\is_numeric($stats['midhinge']));
  1087. $this->assertTrue(\is_numeric($stats['skewness']));
  1088. $this->assertTrue(\is_numeric($stats['ses']));
  1089. $this->assertTrue(\is_numeric($stats['kurtosis']));
  1090. $this->assertTrue(\is_numeric($stats['sek']));
  1091. $this->assertTrue(\is_numeric($stats['sem']));
  1092. $this->assertTrue(\is_array($stats['ci_95']));
  1093. $this->assertTrue(\is_array($stats['ci_99']));
  1094. }
  1095. /**
  1096. * @test describe will return null ses for values of n < 3
  1097. * @dataProvider dataProviderForDescribeNullSes
  1098. * @param array $numbers
  1099. * @throws \Exception
  1100. */
  1101. public function testDescribeSesNullForSmallN(array $numbers)
  1102. {
  1103. // When
  1104. $stats = Descriptive::describe($numbers);
  1105. // Then
  1106. $this->assertNull($stats['ses']);
  1107. }
  1108. /**
  1109. * @return array [numbers]
  1110. */
  1111. public function dataProviderForDescribeNullSes(): array
  1112. {
  1113. return [
  1114. [[-1]],
  1115. [[0]],
  1116. [[1]],
  1117. [[2]],
  1118. [[3]],
  1119. [[4]],
  1120. [[5]],
  1121. [[10]],
  1122. [[100]],
  1123. [[999999]],
  1124. [[-1, -1]],
  1125. [[-1, 0]],
  1126. [[0, -1]],
  1127. [[0, 0]],
  1128. [[0, 1]],
  1129. [[1, 0]],
  1130. [[1, 1]],
  1131. [[1, 2]],
  1132. [[5, 5]],
  1133. [[10, 10]],
  1134. [[9293, 85732]],
  1135. ];
  1136. }
  1137. /**
  1138. * @test describe will return null sek for values of n < 4
  1139. * @dataProvider dataProviderForDescribeNullSek
  1140. * @param array $numbers
  1141. * @throws \Exception
  1142. */
  1143. public function testDescribeSekNullForSmallN(array $numbers)
  1144. {
  1145. // When
  1146. $stats = Descriptive::describe($numbers);
  1147. // Then
  1148. $this->assertNull($stats['sek']);
  1149. }
  1150. /**
  1151. * @return array [numbers]
  1152. */
  1153. public function dataProviderForDescribeNullSek(): array
  1154. {
  1155. return [
  1156. [[-1]],
  1157. [[0]],
  1158. [[1]],
  1159. [[2]],
  1160. [[3]],
  1161. [[4]],
  1162. [[5]],
  1163. [[10]],
  1164. [[100]],
  1165. [[999999]],
  1166. [[-1, -1]],
  1167. [[-1, 0]],
  1168. [[0, -1]],
  1169. [[0, 0]],
  1170. [[0, 1]],
  1171. [[1, 0]],
  1172. [[1, 1]],
  1173. [[1, 2]],
  1174. [[5, 5]],
  1175. [[10, 10]],
  1176. [[9293, 85732]],
  1177. [[-1, -1, -1]],
  1178. [[-1, 0, 0]],
  1179. [[-1, 0, -1]],
  1180. [[0, -1, 0]],
  1181. [[0, -1, -1]],
  1182. [[0, 0, -1]],
  1183. [[-1, 0, -1]],
  1184. [[0, 0, 0]],
  1185. [[0, 1], 0],
  1186. [[1, 0], 0],
  1187. [[1, 1], 1],
  1188. [[1, 2, 3]],
  1189. [[5, 5], 5],
  1190. [[10, 10, 10]],
  1191. [[9293, 85732, 44837475]],
  1192. ];
  1193. }
  1194. /**
  1195. * @test fiveNumberSummary
  1196. * @dataProvider dataProviderForFiveNumberSummary
  1197. * @param array $numbers
  1198. * @param array $expectedSummary
  1199. * @throws \Exception
  1200. */
  1201. public function testFiveNumberSummary(array $numbers, array $expectedSummary)
  1202. {
  1203. // When
  1204. $summary = Descriptive::fiveNumberSummary($numbers);
  1205. // Then
  1206. $this->assertEqualsWithDelta($expectedSummary, $summary, 0.0001);
  1207. }
  1208. /**
  1209. * @return array
  1210. */
  1211. public function dataProviderForFiveNumberSummary(): array
  1212. {
  1213. return [
  1214. [
  1215. [0, 0, 1, 2, 63, 61, 27, 13],
  1216. ['min' => 0, 'Q1' => 0.5, 'median' => 7.5, 'Q3' => 44.0, 'max' => 63],
  1217. ],
  1218. ];
  1219. }
  1220. }