一、实验内容
给定国际通用UCI数据库中FISHERIRIS数据集,其meas集包含150个样本数据,每个数据含有莺尾属植物的4个属性,即萼片长度、萼片宽度、花瓣长度,单位为cm。上述数据分属于species集的三种setosa、versicolor和virginica花朵类别。
要求在该数据集上执行:
1. 层次聚类算法
2. k-means聚类算法
得到的聚类结果与species集的Label结果比较,统计这两类算法聚类的正确率和运行时间。
附件:Fisheriris数据集:
① fisheriris_meas.xls ( 莺尾花4个属性值 )
② fisheriris_species.xls ( fisheriris_meas.xls中每条数据相应的类别Label值 )
二、实验设计(原理分析及流程)
将两个数据文件读取到程序中,使用一个点结构矩阵来存放文件的信息。之后针对层次聚类算法,使用一个计数器TotClu代表剩余簇数目,当TotClu为3时结束算法,算法使用一个查找函数每次查找最短距离的两个簇,接着使用合并算法进行合并。接着可以显处理完成的点结构矩阵以及计算分类的准确率。
之后使用一个存储k-means算法的初始簇的矩阵存储初始簇(这里使用层次聚类算法算出来的离簇中心最近的点作为初始簇)。然后用使用k-means算法来将所有点归类到最近的簇里面,每次归类完都调用RecalCen函数来重新计算簇中心。之后再显示处理完成的点结构矩阵以及准确率。截图中因为一个命令行界面显示不完程序需要运行两次来显示结果。中间部分的截图省略了。
其中点的结构如下:
typedef struct Node
{
int CluNum; // 簇编号
double LenOne, WidOne, LenTwo, WidTwo; // 四个属性值
double ShDis = 100.0; // 离其他簇的最短距离
int ShNum = 1000; // 最短距离的簇编号
} Node;
两个算法中,层次聚类的执行时间要比k-means更长,这里数据量比较少,当数据量大时更加明显,但层次聚类的准确率比较高。
而k-means算法的执行时间更短,显然它的实现更加简单,但同时受到初始簇选择的影响,其准确率依赖于初始簇的选择以及初始簇的代表性,这个程序中,其准确率比层次聚类的准确率低。
三、对于k-means算法,初始的簇有多种选取方式,代码中直接根据真正的数据集计算三个簇中心,找到距离中心最近的点作为k-means的三个初始点。同时,也可以使用层次聚类计算所得的三个簇中心找初始点,要求层次聚类算法实现是正确的。
四、代码:
// cluster.cpp 对国际通用UCI数据库中FISHERIRIS数据集执行
// 1.层次聚类2.k-means聚类算法,比较两者的执行时间以及准确率
// 为了方便处理,直接将数据文件转化为csv格式再进行处理
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <cstdlib>
#include <cmath>
#define ProNum 4 // 点结构属性个数
#define ProLine 150 // 点的个数
#define CentNum 3 // k-means初始簇中心个数
#define CPOne 50 // 品种数据文件的两个簇分界点编号1(从0开始)
#define CPTwo 100 // 品种数据文件的两个簇分界点编号2(从0开始)
typedef struct Node
{
int CluNum; // 簇编号
double LenOne, WidOne, LenTwo, WidTwo; // 四个属性值
double ShDis = 100.0; // 离其他簇的最短距离
int ShNum = 1000; // 最短距离的簇编号
} Node;
// 分割函数,将从文件读取的一行string分割成多个列
// 参数: 源字符串,分界符,存放单词的容器 返回0表示成功执行
int Split(const std::string &str, const std::string &splitchar, std::vector <std::string> &vec)
{
std::string stmp = "";
std::string::size_type pos = 0, prev_pos = 0;
vec.clear(); // 删除存在的元素
while ((pos = str.find_first_of(splitchar, pos)) != std::string::npos)
{
stmp = str.substr(prev_pos, pos - prev_pos);
vec.push_back(stmp);
prev_pos = ++pos;
}
stmp = str.substr(prev_pos, pos - prev_pos);
if (stmp.length() > 0)
{
vec.push_back(stmp);
}
return 0;
}