数据挖掘 k-means离群点检测

k-means离群点检测

改写一种简单的半监督方法,用于离群点检测。使用一种你熟悉的程序设计语言,如C++或Java,实现该方法,并在两种不同的数据集上进行讨论(1)只有一些被标记的正常对象;(2)只有一些被标记的离群点实例。

一、数据集介绍

1、Iris数据集介绍
iris以鸢尾花的特征作为数据来源,数据集包含150个数据集,分为3类,每类50个数据,每个数据包含4个属性,是在数据挖掘、数据分类中非常常用的测试集、训练集。
三类分别为:setosa, versicolor, virginica。
数据包含4个独立的属性,这些属性变量测量植物的花朵,比如萼片和花瓣的长度等。
2、wine数据集介绍
这份数据集包含来自3种不同起源的葡萄酒的共178条记录。13个属性是葡萄酒的13种化学成分。通过化学分析可以来推断葡萄酒的起源。值得一提的是所有属性变量都是连续变量。数据集特征:多变量;记录数:178;领域:物理;属性特征:整数,实数;属性数目:13。
3、abalone数据集介绍
采用UCI数据集中的abalone数据集进行测试。该数据集包括涉及生活领域的8个类别的4177个数据对象,其中含有1个分类型属性,1个整数型属性和6个实数型属性。分类属性数据对象中含有1528个记录为F(父)值,1307个记录为M(母)值,还有1342个记录为I(未成年人)值。

二、算法描述

K-means算法是很典型的基于距离的聚类算法,采用距离作为相似性的评价指标,即认为两个对象的距离越近,其相似度就越大。该算法认为簇是由距离靠近的对象组成的,因此把得到紧凑且独立的簇作为最终目标。
2.1算法思路
K-means算法
先随机选取K个对象作为初始的聚类中心。然后计算每个对象与各个种子聚类中心之间的距离,把每个对象分配给距离它最近的聚类中心。聚类中心以及分配给它们的对象就代表一个聚类。一旦全部对象都被分配了,每个聚类的聚类中心会根据聚类中现有的对象被重新计算。这个过程将不断重复直到满足某个终止条件。终止条件可以是以下任何一个:
1)没有(或最小数目)对象被重新分配给不同的聚类。
2)没有(或最小数目)聚类中心再发生变化。
3)误差平方和局部最小。
2.2算法步骤
a.从数据集中随机挑K个数据当簇心;
b.对数据中的所有点求到这K个簇心的距离,假如点Pi离簇心Si最近,那么Pi属于Si对应的簇;
c.根据每个簇的数据,更新簇心,使得簇心位于簇的中心;
d.重复步骤e和步骤f,直到簇心不再移动(或其他条件,如前后两次距离和不超过特定值),继续下一步;
e.计算每个簇的正常半径,即阀值(此程序阀值为每个簇的平均距离与1.5倍标准差之和);
f.从每个簇中,找出大于阀值的点,即离群点。
三、java 实现
这里写图片描述

package kmeans;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class Kmeans {

    /**
     * @param args
     * @throws IOException
     */

    public static List<ArrayList<ArrayList<Double>>> 
    initHelpCenterList(List<ArrayList<ArrayList<Double>>> helpCenterList,int k){
        for(int i=0;i<k;i++){
            helpCenterList.add(new ArrayList<ArrayList<Double>>());
        }   
        return helpCenterList;
    }

    /**
     * @param args
     * @throws IOException
     */
    public static void main(String[] args) throws IOException{

        List<ArrayList<Double>> centers = new ArrayList<ArrayList<Double>>();
        List<ArrayList<Double>> newCenters = new ArrayList<ArrayList<Double>>();
        List<ArrayList<ArrayList<Double>>> helpCenterList = new ArrayList<ArrayList<ArrayList<Double>>>();

        //读入原始数据
        BufferedReader br=new BufferedReader(new InputStreamReader(new FileInputStream("src/datafile/abaloneNoLabel.data")));
        String data = null;
        List<ArrayList<Double>> dataList = new ArrayList<ArrayList<Double>>();
        while((data=br.readLine())!=null){
            //System.out.println(data);
            String []fields = data.split(",");
            List<Double> tmpList = new ArrayList<Double>();
            for(int i=0; i<fields.length;i++)
                tmpList.add(Double.parseDouble(fields[i]));
            dataList.add((ArrayList<Double>) tmpList);
        }
        br.close();

        //随机确定K个初始聚类中心
        Random rd = new Random();
        int k=3;
        int [] initIndex={59,71,48};
        int [] helpIndex = {0,59,130};
        int [] givenIndex = {0,1,2};
        System.out.println("random centers' index");
        for(int i=0;i<k;i++){
            int index = rd.nextInt(initIndex[i]) + helpIndex[i];
            //int index = givenIndex[i];
            System.out.println("index "+index);
            centers.add(dataList.get(index));
            helpCenterList.add(new ArrayList<ArrayList<Double>>());
        }   

        /*
        //注释掉的这部分目的是,取测试数据集最后稳定的三个类簇的聚类中心作为初始聚类中心
        centers = new ArrayList<ArrayList<Double>>();
        for(int i=0;i<59;i++)
            helpCenterList.get(0).add(dataList.get(i));
        for(int i=59;i<130;i++)
            helpCenterList.get(1).add(dataList.get(i));
        for(int i=130;i<178;i++)
            helpCenterList.get(2).add(dataList.get(i));
        for(int i=0;i<k;i++){

            ArrayList<Double> tmp = new ArrayList<Double>();

            for(int j=0;j<dataList.get(0).size();j++){
                double sum=0;
                for(int t=0;t<helpCenterList.get(i).size();t++)
                    sum+=helpCenterList.get(i).get(t).get(j);
                tmp.add(sum/helpCenterList.get(i).size());
            }
            centers.add(tmp);
        }
        */

        //输出k个初始中心
        System.out.println("original centers:");
        for(int i=0;i<k;i++)
            System.out.println(centers.get(i));

        while(true)
        {//进行若干次迭代,直到聚类中心稳定

            for(int i=0;i<dataList.size();i++){//标注每一条记录所属于的中心
                double minDistance=99999999;
                int centerIndex=-1;
                for(int j=0;j<k;j++){//离0~k之间哪个中心最近
                    double currentDistance=0;
                    for(int t=1;t<centers.get(0).size();t++){//计算两点之间的欧式距离
                        currentDistance +=  ((centers.get(j).get(t)-dataList.get(i).get(t))/(centers.get(j).get(t)+dataList.get(i).get(t))) * ((centers.get(j).get(t)-dataList.get(i).get(t))/(centers.get(j).get(t)+dataList.get(i).get(t))); 
                    }
                    if(minDistance>currentDistance){
                        minDistance=currentDistance;
                        centerIndex=j;
                    }
                }
                helpCenterList.get(centerIndex).add(dataList.get(i));
            }

        //  System.out.println(helpCenterList);

            //计算新的k个聚类中心
            for(int i=0;i<k;i++){

                ArrayList<Double> tmp = new ArrayList<Double>();

                for(int j=0;j<centers.get(0).size();j++){
                    double sum=0;
                    for(int t=0;t<helpCenterList.get(i).size();t++)
                        sum+=helpCenterList.get(i).get(t).get(j);
                    tmp.add(sum/helpCenterList.get(i).size());
                }

                newCenters.add(tmp);

            }
            System.out.println("\nnew clusters' centers:\n");
            for(int i=0;i<k;i++)
                System.out.println(newCenters.get(i));
            //计算新旧中心之间的距离,当距离小于阈值时,聚类算法结束
            double distance=0;

            for(int i=0;i<k;i++){
                for(int j=1;j<centers.get(0).size();j++){//计算两点之间的欧式距离
                    distance += ((centers.get(i).get(j)-newCenters.get(i).get(j))/(centers.get(i).get(j)+newCenters.get(i).get(j))) * ((centers.get(i).get(j)-newCenters.get(i).get(j))/(centers.get(i).get(j)+newCenters.get(i).get(j))); 
                }
                //System.out.println(i+" "+distance);
            }
            System.out.println("\ndistance: "+distance+"\n\n");
            if(distance==0)//小于阈值时,结束循环
                break;
            else//否则,新的中心来代替旧的中心,进行下一轮迭代
            {
                centers = new ArrayList<ArrayList<Double>>(newCenters);
                //System.out.println(newCenters);
                newCenters = new ArrayList<ArrayList<Double>>();
                helpCenterList = new ArrayList<ArrayList<ArrayList<Double>>>();
                helpCenterList=initHelpCenterList(helpCenterList,k);
            }
        }
        //输出最后聚类结果
        for(int i=0;i<k;i++){
            System.out.println("\n\nCluster: "+(i+1)+"   size: "+helpCenterList.get(i).size()+" :\n\n");
            for(int j=0;j<helpCenterList.get(i).size();j++)
            {
            //  System.out.println(helpCenterList.get(i).get(j));
            }
        }
        int dataSetLength=dataList.size();
         double[][] distance = new double[k][dataSetLength];
         double[] distanceSum=new double[k];
        for(int j=0;j<k;j++)
        {
                for (int i = 0; i < helpCenterList.get(j).size(); i++) {

                    distance[j][i] = distance(helpCenterList.get(j).get(i), centers.get(j));
                    distanceSum[j]+= distance[j][i];
                }
                //每个簇的平均距离
                distanceSum[j]/= helpCenterList.get(j).size();
                System.err.println(distanceSum[j]);
                double radius=distanceSum[j]+1.5*Standardlizerdistance(distance[j],distanceSum[j]);
                System.err.println("\n\nCluster: "+(j+1)+" 每个簇的正常半径,即阀值(此程序阀值为每个簇的平均距离与1.5倍标准差之和为: "+radius);
                for(int i=0;i<helpCenterList.get(j).size();i++)
                {
                    if(distance[j][i]>radius)
                    {
                        System.err.println(helpCenterList.get(j).get(i)+"离群 "+distance[j][i]);
                    }
//                  else 
//                      System.out.println(helpCenterList.get(j).get(i)+"正常半径: "+distance[j][i]);
                }

        }

    }
    private static double distance(ArrayList<Double> element, ArrayList<Double> center) {
        double currentDistance=0;

                for(int t=1;t<element.size();t++){//计算两点之间的欧式距离
                    currentDistance +=  (element.get(t)-center.get(t))*(element.get(t)-center.get(t));
                }

        return Math.sqrt(currentDistance);
    }
    private static double Standardlizerdistance(double[] distance, double x) {
        double currentDistance=0;

                for(int t=1;t<distance.length;t++){//计算两点之间的欧式距离
                    currentDistance +=  (distance[t]-x)*(distance[t]-x);
                }

        return Math.sqrt(currentDistance/distance.length);
    }
}
  • 3
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值