您的位置:首页 > 编程语言 > Java开发

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

2015-07-07 22:09 507 查看

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];

4000
//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);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息