层次聚类以及k-means算法

这篇博客介绍了在UCI的FISHERIRIS数据集上,对层次聚类和k-means聚类算法的实施过程。通过对150个样本的四个属性进行聚类,比较了两种算法的正确率和运行时间。层次聚类在准确率上表现更好,但运行时间较长;而k-means算法虽然运行速度快,但准确率受初始簇选择影响。博客还探讨了k-means初始簇的不同选取方法,并提供了代码实现。
摘要由CSDN通过智能技术生成

一、实验内容

给定国际通用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;
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值