Benford.php 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. <?php
  2. class Benford {
  3. private $array = null;
  4. private $benford = null;
  5. public function __construct($array = null) {
  6. if ($array !== null) $this->addArray($array);
  7. }
  8. public function addArray($array = null) {
  9. if ($array === null) throw new Exception("Missing parameter");
  10. if (!is_array($array)) throw new Exception("Bad parameter - non array");
  11. foreach ($array as $k => $v) $this->addItem($k, $v);
  12. }
  13. public function addItem($k = null, $v = null) {
  14. if (!is_int($k)) throw new Exception("Bad key value - non integer ({$k})");
  15. if ($k < 1) throw new Exception("Bad key value - less than 1 ({$k})");
  16. if (isset($this->array[$k])) throw new Exception("Key value already exists ({$k})");
  17. if (!is_numeric($v)) throw new Exception("Bad value - non numeric ({$v})");
  18. $v = round(abs($v * 100));
  19. if ($v == 0) return;
  20. $this->array[$k] = $v;
  21. $this->benford = null;
  22. }
  23. public static function benford($k = null) {
  24. if ($k === null) throw new Exception("Missing parameter");
  25. if (!is_int($k)) throw new Exception("Bad parameter - non integer ({$k})");
  26. if ($k < 1) throw new Exceptrion("Bad parameter - less than 1 ({$k})");
  27. return log10(1 + 1 / $k);
  28. }
  29. public function calculate($secondStep = true) {
  30. return $this->calculateFirstStep($secondStep);
  31. }
  32. public function calculateFirstStep($secondStep = false) {
  33. if ($this->array === null) return false;
  34. $this->benford = [];
  35. foreach ($this->array as $k => $v) {
  36. $s = (string) $v;
  37. $this->benford['firstStep'][$s[0]]['keys'][] = $k;
  38. }
  39. for ($k = 1; $k <= 9; $k++) {
  40. if (!isset($this->benford['firstStep'][$k])) $this->benford['firstStep'][$k]['keys'] = [];
  41. $v = $this->benford['firstStep'][$k];
  42. $this->benford['firstStep'][$k]['count'] = count($v['keys']);
  43. $this->benford['firstStep'][$k]['value'] = count($v['keys']) / count($this->array);
  44. $this->benford['firstStep'][$k]['norm'] = self::benford($k);
  45. if ($this->benford['firstStep'][$k]['correct'] = ($this->benford['firstStep'][$k]['value'] <= $this->benford['firstStep'][$k]['norm'])) {
  46. unset($this->benford['firstStep'][$k]['keys']);
  47. } elseif ($secondStep) {
  48. $this->calculateSecondStep($k);
  49. }
  50. }
  51. ksort($this->benford['firstStep']);
  52. return true;
  53. }
  54. public function calculateSecondStep($a = null) {
  55. if ($a === null) {
  56. if (!isset($this->benford['firstStep'])) $this->calculate(false);
  57. foreach ($this->benford['firstStep'] as $k => $v) {
  58. if (!$v['correct']) $this->_calculateSecondStep($k);
  59. }
  60. } else $this->_calculateSecondStep($a);
  61. }
  62. private function _calculateSecondStep($a) {
  63. if (!isset($this->benford['firstStep'][$a]['keys'])) throw new Exception("Wrong data ({$a})");
  64. foreach ($this->benford['firstStep'][$a]['keys'] as $k) {
  65. $s = (string) $this->array[$k];
  66. if (strlen($s) < 2) continue;
  67. $this->benford['secondStep'][$a][$s[1]]['keys'][] = $k;
  68. }
  69. for ($k = 0; $k <= 9; $k++) {
  70. if (!isset($this->benford['secondStep'][$a][$k])) $this->benford['secondStep'][$a][$k]['keys'] = [];
  71. $v = $this->benford['secondStep'][$a][$k];
  72. $this->benford['secondStep'][$a][$k]['count'] = count($v['keys']);
  73. $this->benford['secondStep'][$a][$k]['value'] = count($v['keys']) / count($this->array);
  74. $this->benford['secondStep'][$a][$k]['norm'] = self::benford($a * 10 + $k);
  75. if ($this->benford['secondStep'][$a][$k]['correct'] = ($this->benford['secondStep'][$a][$k]['value'] <= $this->benford['secondStep'][$a][$k]['norm'])) {
  76. unset($this->benford['secondStep'][$a][$k]['keys']);
  77. }
  78. }
  79. ksort($this->benford['secondStep'][$a]);
  80. }
  81. private function getResult($type = null, $k = null) {
  82. if ($type === null) throw new Exception("Wrong usage");
  83. if ($this->benford === null) $this->calculateFirstStep();
  84. switch ($type) {
  85. case 'all':
  86. case 'secondStep':
  87. if (!isset($this->benford['secondStep'])) $this->calculateSecondStep();
  88. case 'firstStep':
  89. if ($type == 'all') return $this->benford;
  90. else return $this->benford[$type];
  91. case 'secondStepItem':
  92. if ($k === null) throw new Exception("Missing parameter");
  93. if (!is_int($k)) throw new Exception("Bad parameter - not integer ({$k})");
  94. if ($k < 1 || $k > 9) throw new Exception("Bad parameter - not between 1 and 9 ({$k})");
  95. if (!isset($this->benford['secondStep'][$k])) throw new Exception("Wrong data");
  96. return $this->benford['secondStep'][$k];
  97. case 'badFirstStep':
  98. return array_keys(array_filter($this->benford['firstStep'], function($v) {
  99. if (!$v['correct']) return true;
  100. }));
  101. default: throw new Exception('Wrong usage');
  102. }
  103. }
  104. public function getAllResult() {
  105. return $this->getResult('all');
  106. }
  107. public function getFirstStep() {
  108. return $this->getResult('firstStep');
  109. }
  110. public function getSecondStep() {
  111. return $this->getResult('secondStep');
  112. }
  113. public function getSecondStepItem($k = null) {
  114. return $this->getResult('secondStepItem', $k);
  115. }
  116. public function getBadFirstStep() {
  117. return $this->getResult('badFirstStep');
  118. }
  119. }