局部异常因子算法-Local Outlier Factor(LOF)算法的PHP实现

来源:互联网 发布:云南淘宝网络营销 编辑:程序博客网 时间:2024/06/06 06:53

这两天在完善自己系统的过程中要实现一个查找异常的功能,于是在朋友的指点下学习并实现了异常点查找的一个基本算法“局部异常因子算法-Local Outlier Factor(LOF)算法”。

首先,找相关说明看看这是个什么东西吧。

我参考了这一篇文章: 异常点/离群点检测算法——LOF

大致明白了lof算法是在讲什么,我的理解还有很多不完善的地方,不过还是作为一个初学者写出来供大家批评指正。

根据我的理解大致描述如下:

1、 k-distance,点p的第k距离就是距离点p第k远的那个点的距离,k可以是任意值。在实际生活中可能会这样:小明说“小红家是离我家第五近的,小赵、小钱、小孙、小李家都比她家离我家近”所以此处小红家距离小明家的距离就是小明家k为5时的第k距离。

2、k-distance neighborhood of p,第k距离领域,按照上面的例子就是{小赵、小钱、小孙、小李、小红},把离p最近的k个点放入一个数组就是第k距离领域了。


3、reach-distance:可达距离。点o到点p的第k可达距离分两种情况,一种是p在o的第k距离领域那个数组中,这时候可达距离等于第k距离,第二种就是p离点o比较远,不在o的第k距离领域中,此时的可达距离即为真实距离。依然使用上述的例子,小赵家在小明家的第k邻域中,所以可达距离就是第k距离,就是小红家的距离,而二狗子家里小明家很远,可达距离就是真实距离了。


4、local reachability density:局部可达密度。点p的局部可达密度是指点p第k距离邻域中所有成员到点p的可达距离的平均值的倒数,有点复杂,不过多读几遍还是蛮好理解的,就不举例子了。


5、local outlier factor:局部离群因子。点p的局部离群因子即为领域中所有点的局部可达密度的平均数比点p的局部可达密度,不做解释。
到这里为止就是我对lof算法的一个大致理解,具体讲解还要看上面我参考的那篇文章,写的很清楚。


接下来我找了网上的一篇对此算法的实现,很遗憾没有php版本,于是我就找到了这篇文章:基于密度的局部离群点检测(lof算法) (Java 实现)


如题所示,是一篇Java实现,于是我就在大神的基础上对其进行修改,改成了一个php的版本。因为对迭代器理解的不是很好,所以迭代器实现部分改成了一般函数,有机会再进行完善。




如下:

<?phpclass DataNode {  private  $nodeName; // 样本点名      private  $dimensioin; // 样本点的维度      private  $kDistance; // k-距离      private  $kNeighbor = array();// k-领域      private $distance; // 到给定点的欧几里得距离      private $reachDensity;// 可达密度      private $reachDis;// 可达距离        private $lof;// 局部离群因子        public function __construct() {  $num = func_num_args();   //获得参数个数                $args = func_get_args();   //获得参数列表数组                switch($num){                        case 0:                                                              break;                        case 2:                                $this->__call('__construct2', $args);                                break;                }           }   public function __call($name, $arg) //根据函数名调用函数        {                return call_user_func_array(array($this, $name), $arg);        } public function __construct2($nodeName, $dimensioin)        {                $this->nodeName = $nodeName;  $this->dimensioin = $dimensioin;          }      public function getNodeName() {          return $this->nodeName;      }        public function setNodeName($nodeName) {          $this->nodeName = $nodeName;      }        public function getDimensioin() {          return $this->dimensioin;      }        public function setDimensioin($dimensioin) {          $this->dimensioin = $dimensioin;      }        public function getkDistance() {          return $this->kDistance;      }        public function setkDistance($kDistance) {          $this->kDistance = $kDistance;      }        public function getkNeighbor() {          return  $this->kNeighbor;      }        public function setkNeighbor($kNeighbor) {          $this->kNeighbor = $kNeighbor;      }        public function getDistance() {          return $this->distance;      }        public function setDistance($distance) {          $this->distance = $distance;      }        public function getReachDensity() {          return  $this->reachDensity;      }        public function setReachDensity($reachDensity) {          $this->reachDensity = $reachDensity;      }        public function getReachDis() {          return $this->reachDis;      }        public function setReachDis($reachDis) {          $this->reachDis = $reachDis;      }        public function getLof() {          return $this->lof;      }        public function setLof($lof) {          $this->lof = $lof;      }    }class OutlierNodeDetect {      private static $INT_K = 5;//正整数K        // 1.找到给定点与其他点的欧几里得距离      // 2.对欧几里得距离进行排序,找到前5位的点,并同时记下k距离      // 3.计算每个点的可达密度      // 4.计算每个点的局部离群点因子      // 5.对每个点的局部离群点因子进行排序,输出。      public function getOutlierNode($allNodes) {          $kdAndKnList =  $this->getKDAndKN($allNodes);          $this->calReachDis($kdAndKnList);           $this->calReachDensity($kdAndKnList);           $this->calLof($kdAndKnList);          //降序排序           $kdAndKnList = $this->rsortArr($kdAndKnList);          return $kdAndKnList;      }        /**      * 计算每个点的局部离群点因子      * @param kdAndKnList      */      private function calLof($kdAndKnList) {           foreach($kdAndKnList as $node):               $tempNodes = $node->getkNeighbor();              $sum = 0.0;               foreach($tempNodes as $tempNode):                  $rd = $this->getRD($tempNode->getNodeName(), $kdAndKnList);                  $sum = $rd / $node->getReachDensity() + $sum;              endforeach;              $sum = $sum / (double) self::$INT_K;              $node->setLof($sum);           endforeach;      }        /**      * 计算每个点的可达距离      * @param kdAndKnList      */      private function calReachDensity($kdAndKnList) {         foreach($kdAndKnList as $node):              $tempNodes = $node->getkNeighbor();              $sum = 0.0;              $rd = 0.0;  foreach($tempNodes as $tempNode):                  $sum = $tempNode->getReachDis() + $sum;              endforeach;              $rd = (double) self::$INT_K / $sum;              $node->setReachDensity($rd);          endforeach;      }            /**      * 计算每个点的可达密度,reachdis(p,o)=max{ k-distance(o),d(p,o)}      * @param kdAndKnList      */      private function calReachDis($kdAndKnList) { //for (DataNode node : kdAndKnList) {       foreach($kdAndKnList as $node):              $tempNodes = $node->getkNeighbor();              //for (DataNode tempNode : tempNodes) {              foreach($tempNodes as $tempNode):                  //获取tempNode点的k-距离                  $kDis = $this->getKDis($tempNode->getNodeName(), $kdAndKnList);                  if ($kDis < $tempNode->getDistance()) {                      $tempNode->setReachDis($tempNode->getDistance());                  } else {                      $tempNode->setReachDis($kDis);                  }              endforeach;          endforeach;      }        /**      * 获取某个点的k-距离(kDistance)      * @param nodeName      * @param nodeList      * @return      */      private function getKDis($nodeName,$nodeList) {          $kDis = 0;          //for (DataNode node : nodeList) {          foreach($nodeList as $node):              if ($this->strcomp(trim($nodeName),trim($node->getNodeName()))) {                  $kDis =$node->getkDistance();                  break;              }          endforeach;          return $kDis;        }  privatefunction strcomp($str1,$str2){ if($str1 == $str2){ return TRUE; }else{ return FALSE; } }       /**      * 获取某个点的可达距离      * @param nodeName      * @param nodeList      * @return      */      private function getRD($nodeName, $nodeList) {          $kDis = 0;          //for (DataNode node : nodeList) {          foreach($nodeList as $node):              //if (nodeName.trim().equals(node.getNodeName().trim())) {              if ($this->strcomp(trim($nodeName),trim($node->getNodeName()))) {                  $kDis = $node->getReachDensity();                  break;              }          endforeach;          return $kDis;        }            /**      * 计算给定点NodeA与其他点NodeB的欧几里得距离(distance),并找到NodeA点的前5位NodeB,然后记录到NodeA的k-领域(kNeighbor)变量。      * 同时找到NodeA的k距离,然后记录到NodeA的k-距离(kDistance)变量中。      * 处理步骤如下:      * 1,计算给定点NodeA与其他点NodeB的欧几里得距离,并记录在NodeB点的distance变量中。      * 2,对所有NodeB点中的distance进行升序排序。      * 3,找到NodeB点的前5位的欧几里得距离点,并记录到到NodeA的kNeighbor变量中。      * 4,找到NodeB点的第5位距离,并记录到NodeA点的kDistance变量中。      * @param allNodes      * @return List<Node>      */      private function getKDAndKN($allNodes) {          $kdAndKnList = array();          for ($i = 0 ; $i <  count($allNodes); $i++) {              $tempNodeList = array();            $nodeA = new DataNode($allNodes[$i]->getNodeName(), $allNodes[$i]->getDimensioin());              //1,找到给定点NodeA与其他点NodeB的欧几里得距离,并记录在NodeB点的distance变量中。              for ($j = 0; $j < count($allNodes); $j++) {                  $nodeB = new DataNode($allNodes[$j]->getNodeName(), $allNodes[$j]->getDimensioin());                  //计算NodeA与NodeB的欧几里得距离(distance)                  $tempDis = $this->getDis($nodeA, $nodeB);                  $nodeB->setDistance($tempDis);array_push($tempNodeList,$nodeB);                //$tempNodeList.add(nodeB);              }              //2,对所有NodeB点中的欧几里得距离(distance)进行升序排序。$tempNodeList = $this->sortArr($tempNodeList);  $neighArr = array();            for ($k = 1; $k <= self::$INT_K; $k++) {                  //3,找到NodeB点的前5位的欧几里得距离点,并记录到到NodeA的kNeighbor变量中。 array_push($neighArr ,$tempNodeList[$k]);                if ($k == self::$INT_K - 1) {                      //4,找到NodeB点的第5位距离,并记录到NodeA点的kDistance变量中。                      $nodeA->setkDistance($tempNodeList[$k]->getDistance());                }              }  $nodeA->setkNeighbor($neighArr);            array_push($kdAndKnList,$nodeA);          }          return $kdAndKnList;      }            /**      * 计算给定点A与其他点B之间的欧几里得距离。      * 欧氏距离的公式:      * d=sqrt( ∑(xi1-xi2)^2 ) 这里i=1,2..n      * xi1表示第一个点的第i维坐标,xi2表示第二个点的第i维坐标      * n维欧氏空间是一个点集,它的每个点可以表示为(x(1),x(2),...x(n)),      * 其中x(i)(i=1,2...n)是实数,称为x的第i个坐标,两个点x和y=(y(1),y(2)...y(n))之间的距离d(x,y)定义为上面的公式.      * @param A      * @param B      * @return      */      private function getDis($A, $B) {          $dis = 0.0;          $dimA = $A->getDimensioin();          $dimB = $B->getDimensioin();          if (count($dimA) == count($dimB)) {              for ($i = 0; $i < count($dimA); $i++) {                  $temp = pow($dimA[$i] - $dimB[$i], 2);                  $dis = $dis + $temp;              }              $dis = pow($dis, 0.5);          }          return $dis;      }      //Distance比较    private function compareAandB($arr,$A, $B) {          if(($arr[$A]->getDistance()-$arr[$B]->getDistance())<0)                     return -1;                else if(($arr[$A]->getDistance()-$arr[$B]->getDistance())>0)                    return 1;                else return 0;       }        //lof比较    private function compareAandBLof($arr,$A, $B) {         if(($arr[$A]->getLof()-$arr[$B]->getLof())<0)                     return -1;                else if(($arr[$A]->getLof()-$arr[$B]->getLof())>0)                    return 1;                else return 0;       }        private function changeAandB($arr,$A, $B) {          $tempChange =  $arr[$A]; $arr[$A] = $arr[$B];$arr[$B] = $tempChange;return $arr;    }    //Distance升序    private function sortArr($arr) { for($i = 0;$i < count($arr);$i ++){for($j = $i + 1;$j < count($arr);$j ++){if($this->compareAandB($arr,$i, $j)>0){$arr=$this->changeAandB($arr,$i, $j);}}}return $arr;    }    //lof降序    private function rsortArr($arr) { for($i = 0;$i < count($arr);$i ++){for($j = $i + 1;$j < count($arr);$j ++){if($this->compareAandBLof($arr,$i, $j)<0){$arr=$this->changeAandB($arr,$i, $j);}}}return $arr;    }         public static function main() {                       $dpoints = array();            $a = array( 2, 3 );          $b = array( 2, 4 );          $c = array( 1, 4 );          $d = array( 1, 3 );          $e = array( 2, 2 );          $f = array( 3, 2 );            $g = array( 8, 7 );          $h = array( 8, 6 );          $i = array( 7, 7 );          $j = array( 7, 6 );          $k = array( 8, 5 );            $l = array( 100, 2 );// 孤立点            $m = array( 8, 20 );          $n = array( 8, 19 );          $o = array( 7, 18 );          $p = array( 7, 17 );          $yichen = array( 8, 21 );           array_push($dpoints,new DataNode("a", $a));          array_push($dpoints,new DataNode("b", $b));          array_push($dpoints,new DataNode("c", $c));          array_push($dpoints,new DataNode("d", $d));          array_push($dpoints,new DataNode("e", $e));          array_push($dpoints,new DataNode("f", $f));            array_push($dpoints,new DataNode("g", $g));          array_push($dpoints,new DataNode("h", $h));          array_push($dpoints,new DataNode("i", $i));          array_push($dpoints,new DataNode("j", $j));          array_push($dpoints,new DataNode("k", $k));            array_push($dpoints,new DataNode("l", $l));            array_push($dpoints,new DataNode("m", $m));          array_push($dpoints,new DataNode("n", $n));          array_push($dpoints,new DataNode("o", $o));          array_push($dpoints,new DataNode("p", $p));          array_push($dpoints,new DataNode("yichen", $yichen));            $lof = new OutlierNodeDetect();            $nodeList = $lof->getOutlierNode($dpoints);foreach($nodeList as $node):              echo($node->getNodeName() . "--" . round($node->getLof(),4));   echo("<br>");        endforeach;                 }  }OutlierNodeDetect::main();?>



1 0
原创粉丝点击