参考文献:
[1]Jure Leskovec,Anand Rajaraman,Jeffrey David Ullman.大数据互联网大规模数据挖掘与分布式处理(第二版) [M]北京:人民邮电出版社,2015.,190-199;
[2]蒋盛益,李霞,郑琪.数据挖掘原理与实践 [M]北京:电子工业出版社,2015.1,107-114,121;
目录:
1、测试案例:
2、原理分析:
3、源代码示例:
4、运行结果:
1、测试案例:
给定国际通用UCI数据库中FISHERIRIS数据集,其meas集包含150个样本数据,每个数据含有莺尾属植物的4个属性,即萼片长度、萼片宽度、花瓣长度,单位为cm。上述数据分属于species集的三种setosa、versicolor和virginica花朵类别。
要求在该数据集上执行:
(1)层次聚类算法
(2)k-means聚类算法
得到的聚类结果与species集的Label结果比较,统计这两类算法聚类的正确率和运行时间。
图1.1 Excel测试案例部分内容截图1
图1.2 Excel测试案例部分内容截图2
2、原理分析:
(1)聚类定义:
将数据集划分为由若干相似对象组成的多个组(group)或簇(cluster)的过程,使得同一组中对象间的相似度最大化,不同组中对象间的相似度最小化。
聚类是一种无监督的机器学习方法,即事先对数据集的分布没有任何了解,是将物理或抽象对象的集合组成为由类似的对象组成的多个组的过程。
(2)聚类分析任务步骤:
①模式表示(包括特征提取和选择)
②适合于数据领域的模式相似性定义
③聚类或划分算法
④数据摘要
⑤输出结果的评估
(3)k-means算法:
首先,随机选择k个对象,每个对象代表一个簇的初始均值或中心;对剩余的每个对象,根据其与各簇中心的距离,将它指派到最近或最相似的簇,然后计算每个簇的新均值,得到更新后的簇中心;不断重复,直到准则函数收敛。
(4)自下而上聚合层次聚类方法(凝聚层次聚类):
最初将每个对象作为一个簇,然后将这些簇进行聚合以构造越来越大的簇,直到所有对象均聚合为一个簇,或满足一定终止条件为止。
(5)k-means算法的缺点:
①簇个数k需要预先给定。
②算法对初始值的选取依赖性极大以及算法常陷入局部最优解。
③该算法需要不断地进行样本分类调整,不断地计算调整后的簇中心,因此当数据量非常大时,算法的时间开销是非常大的。
④由于将簇的质心(即均值)作为簇中心进行新一轮的聚类计算,远离数据密集区的离群点和噪声点会导致聚类中心偏离真正的数据密集区,所以k-means算法对噪声点和离群点很敏感。
⑤k-means算法不能用于发现非凸形状的簇,或具有各种不同大小或密度的簇,即很难检测“自然的”簇。
⑥只能用于处理数值属性的数据集,不能处理包含分类属性的数据集。
3、源代码示例:
(1)工程目录:
图3.1工程目录截图
(2)KMeans.java
package com.remoa.experiment4.service;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import com.remoa.experiment4.domain.ClusterVO;
import com.remoa.experiment4.domain.DataVO;
import com.remoa.experiment4.domain.PointVO;
import jxl.Cell;
/**
* K-Means算法
* @author Remoa
*
*/
public class KMeans {
//定义最大欧式距离为5000
public static final double MAXLENGTH = 5000.0;
/**
* 计算新的簇中心
* @param dataVO DataVO实体类
* @return 更新后的DataVO实体
*/
public static DataVO countClusterCenter(DataVO dataVO){
List<ClusterVO> clusterList = dataVO.getClusterList();
List<ClusterVO> newClusterList = new ArrayList<ClusterVO>();
int i, j, p;
for(i = 0; i < clusterList.size(); i++){
ClusterVO cluster = clusterList.get(i);
List<PointVO> pointList = cluster.getPointList();
Double[] countArray = new Double[clusterList.get(0).getPointList().get(0).getPoint().length];
for(j = 0; j < countArray.length; j++){
countArray[j] = 0.0;
}
for(j = 0; j < pointList.size(); j++){
PointVO point = pointList.get(j);
Double[] pointValue = point.getPoint();
for(p = 0; p < pointValue.length; p++){
countArray[p] = pointValue[p] + countArray[p];
}
}
for(j = 0; j < countArray.length; j++){
countArray[j] /= pointList.size();
}
cluster.setClusterCenter(countArray);
newClusterList.add(cluster);
}
dataVO.setClusterList(newClusterList);
return dataVO;
}
/**
* 将对象指派到与其距离最近的簇
* @param dataVO dataVO实体
* @param point 数据点
* @return 修改后的dataVO实体
*/
public static DataVO distributeIntoCluster(DataVO dataVO, PointVO point){
double sum = 0.0, max = MAXLENGTH;
//loca存放在原先簇中的位置,locaRecord存放是在哪个簇
int locaRecord = 0, loca = 0;
int i, j, count, n, m;
List<ClusterVO> clusterList = dataVO.getClusterList();
List<PointVO> pointList = dataVO.getPointList();
List<PointVO> clusterPointList = null;
Double[] distanceArray = new Double[clusterList.size()];
//获取数据点内容
Double[] pointValueArray = point.getPoint();
Double[] tempArray = new Double[pointValueArray.length];
//遍历每一个簇
for(i = 0; i < clusterList.size(); i++){
sum = 0.0;
//得到该簇的中心点
Double[] clusterCenter = clusterList.get(i).getClusterCenter();
//将平方值保存在一个temp数组
for(j = 0; j < pointValueArray.length; j++){
tempArray[j] = Math.pow(clusterCenter[j] - pointValueArray[j], 2);
}
//求欧式距离
for(j = 0; j < tempArray.length; j++){
sum += tempArray[j];
}
//将结果保存在距离数组中
distanceArray[i] = Math.sqrt(sum);
}
//遍历距离数组,找到要插入的簇
for(i = 0; i < distanceArray.length; i++){
if(distanceArray[i] < max){
max = distanceArray[i];
locaRecord = i;
}
}
//获得该簇
ClusterVO cluster = clusterList.get(locaRecord);
//找到簇中的该元素
for(i = 0; i < pointList.size(); i++){
if(pointList.get(i).equals(point)){
loca = i;
break;
}
}
//在同一个簇,不做任何处理
if(cluster.getClusterid().equals(point.getClusterid())){
return dataVO;
}
//这个数据不在任何一个簇,加进来
else if(point.getClusterid() == null){
clusterPointList = cluster.getPointList();
}
//在不同的簇中
else{
clusterPointList = cluster.getPointList();
//遍历每个簇,找到该元素
for(i = 0; i < clusterList.size(); i++){
boolean flag = false;
//遍历每个簇中元素
for(m = 0; m < clusterList.get(i).getPointList().size(); m++){
PointVO everypoint = clusterList.get(i).getPointList().get(m);
Double[]