数学建模专栏 - 聚类分析入门:基本概念和MATLAB实现

Posted SYBH.

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数学建模专栏 - 聚类分析入门:基本概念和MATLAB实现相关的知识,希望对你有一定的参考价值。

摘要:本文将介绍聚类分析的基本概念和MATLAB的实现方法。我们将先介绍聚类分析的概念和应用场景,然后介绍聚类算法的基本原理,最后用一个实际案例来演示如何使用MATLAB进行聚类分析。

1.简介

聚类分析是一种无监督学习方法,用于将数据集分成具有相似特征的不同群组。聚类分析广泛应用于各种领域,例如市场营销、社会网络分析、生物信息学等。在本文中,我们将介绍聚类分析的基本概念和MATLAB的实现方法。我们将用一个实际案例来演示如何使用MATLAB进行聚类分析。

2.聚类分析的基本概念

聚类分析是一种无监督学习方法,其目标是将数据集中的样本分成具有相似特征的不同群组。聚类分析的应用场景非常广泛,例如:

  • 市场营销:将消费者划分成具有相似购买行为的不同群组,以便进行精准营销;
  • 社会网络分析:将社交网络中的用户划分成具有相似兴趣爱好的不同群组,以便进行个性化推荐;
  • 生物信息学:将基因数据中的样本划分成具有相似表达谱的不同群组,以便进行基因功能注释。

在聚类分析中,样本通常由一组特征向量表示。我们可以将每个特征看作空间中的一个维度,然后根据样本在特征空间中的相似度将其分成不同的群组。聚类分析可以使用多种算法来实现,例如K均值聚类、层次聚类、密度聚类等。

3.聚类算法的基本原理

聚类算法是一种无监督学习方法,通过将数据集中的样本划分为若干个具有相似特征的群组,从而实现数据的分类。在聚类算法中,一个群组通常由一个或多个中心点(即聚类中心)表示,而样本与这些中心点之间的距离则用于确定样本属于哪个群组。常见的聚类算法包括K均值聚类、层次聚类、密度聚类等。

3.1 K均值聚类

K均值聚类是一种常见的聚类算法,其基本原理是将样本划分为K个群组,使得每个样本都属于距其最近的聚类中心对应的群组。在K均值聚类中,每个群组由其聚类中心表示,聚类中心的选择通常基于随机初始化或者手动指定。

以下是使用MATLAB实现K均值聚类的示例代码:

% 生成样本数据
X = [randn(100, 2)*0.75+ones(100, 2);
    randn(100, 2)*0.5-ones(100, 2)];

% 使用K均值聚类将数据分成2个群组
[idx, C] = kmeans(X, 2);

% 可视化聚类结果
figure;
gscatter(X(:,1), X(:,2), idx, 'rg');
hold on;
plot(C(:,1), C(:,2), 'kx', 'LineWidth', 2, 'MarkerSize', 10);
legend('Group 1', 'Group 2', 'Centroids');

在这段代码中,我们首先生成一个二维样本数据集。然后,我们使用MATLAB内置的kmeans函数将数据分成2个群组,并返回每个样本对应的群组编号以及每个群组的聚类中心。最后,我们使用gscatter函数绘制样本数据的散点图,并使用plot函数绘制每个群组的聚类中心。

3.2 层次聚类

层次聚类是一种将样本层层划分为不同群组的聚类算法,其基本原理是通过构建一棵树(即树状图)来表示数据的聚类层次,树的每个节点表示一个群组,而叶子节点表示一个单独的样本。在层次聚类中,树状图的生成通常基于样本间的距离(例如欧氏距离、曼哈顿距离等)。

以下是使用MATLAB实现层次聚类的示例代码:

% 生成样本数据
X = [randn(50, 2)*0.5+ones(50, 2);
    randn(50, 2)*0.5-ones(50, 2)];

% 计算样本间的距离
D = pdist(X);

% 构建层次聚类树
Z = linkage(D, 'ward');

% 将树状图可视化
figure;
[H, T, outperm] = dendrogram(Z);
set(H, 'LineWidth', 2);
set(gca, 'XTickLabel', '');
ylabel('Distance');

在这段代码中,我们首先生成一个二维样本数据集。然后,我们使用MATLAB内置的pdist函数计算样本间的距离,并使用linkage函数基于样本距离构建一个凝聚式层次聚类树。最后,我们使用dendrogram函数将树状图可视化,并调整标签和线条宽度以便于查看。

通过这个例子,我们可以看到,凝聚式层次聚类可以帮助我们了解样本之间的相似度,同时也可以帮助我们确定样本分组的数量。

3.3 密度聚类

密度聚类是一种基于样本密度的聚类算法,其基本原理是将密度较高的区域划分为一个群组,而密度较低的区域则被视为噪声或边缘点。在密度聚类中,每个样本都被视为一个群组,而群组的合并则基于样本之间

的距离和密度阈值。

以下是使用MATLAB实现基于密度聚类的示例代码:

% 生成样本数据
X = [randn(50, 2)*0.5+ones(50, 2);
    randn(50, 2)*0.5-ones(50, 2)];

% 使用DBSCAN算法进行密度聚类
[idx, C] = dbscan(X, 0.5, 5);

% 可视化聚类结果
figure;
gscatter(X(:,1), X(:,2), idx, 'rgbcmyk');
hold on;
plot(C(:,1), C(:,2), 'kx', 'LineWidth', 2, 'MarkerSize', 10);
legend('Group 1', 'Group 2', 'Group 3', 'Centroids');

在这段代码中,我们首先生成一个二维样本数据集。然后,我们使用MATLAB内置的dbscan函数将数据分成不同的密度群组,并返回每个样本对应的群组编号以及每个群组的聚类中心。最后,我们使用gscatter函数绘制样本数据的散点图,并使用plot函数绘制每个群组的聚类中心。

4.总结

本文介绍了聚类分析的基本概念和MATLAB的实现方法。我们首先介绍了聚类分析的应用场景,然后讨论了聚类算法的基本原理,包括K均值聚类、层次聚类和密度聚类。最后,我们用一个实际案例演示了如何使用MATLAB进行聚类分析。希望本文能够帮助读者了解聚类分析的基本原理和MATLAB的实现方法,同时也希望读者可以根据自己的实际应用场景,选择合适的聚类算法进行数据分析和处理。

以下是两个常见的难度较高的聚类案例,并附上MATLAB代码供参考。

  1. 基于文本的聚类分析

基于文本的聚类分析是一种将文本数据进行聚类的方法,其主要思想是将文本数据转化为高维向量,然后使用聚类算法对这些向量进行分组。在这个过程中,需要注意文本向量化的方法以及如何选择合适的聚类算法。

以下是使用MATLAB实现基于文本的聚类分析的示例代码:

% 加载文本数据
data = readtable('news.csv');
documents = data.article;

% 对文本数据进行向量化
cv = countVectorizer('StopWords', 'english');
X = cv.fit(documents);
X = X.transform(documents);

% 使用谱聚类对文本数据进行聚类
S = affinityMatrix(X);
D = diag(sum(S, 2));
L = D - S;
[eigVec, eigVal] = eig(L);
[~, idx] = sort(eigVal(1:end-1, 1));
U = eigVec(:, idx(1:3));
U = normr(U);
[idx, C] = kmeans(U, 3);

% 可视化聚类结果
figure;
gscatter(U(:,1), U(:,2), idx, 'rgb');
hold on;
plot(C(:,1), C(:,2), 'kx', 'LineWidth', 2, 'MarkerSize', 10);
legend('Group 1', 'Group 2', 'Group 3', 'Centroids');

在这段代码中,我们首先加载文本数据并使用MATLAB内置的countVectorizer函数将文本数据向量化。然后,我们使用谱聚类对文本数据进行聚类,并返回每个样本对应的群组编号以及每个群组的聚类中心。最后,我们使用gscatter函数绘制文本数据的散点图,并使用plot函数绘制每个群组的聚类中心。

  1. 基于图像的聚类分析

基于图像的聚类分析是一种将图像数据进行聚类的方法,其主要思想是将图像数据转化为高维向量,然后使用聚类算法对这些向量进行分组。在这个过程中,需要注意图像向量化的方法以及如何选择合适的聚类算法。

以下是使用MATLAB实现基于图像的聚类分析的示例代码:

% 加载图像数据
imds = imageDatastore('flower_photos', 'IncludeSubfolders', true, 'LabelSource', 'foldernames');
numImages = numel(imds.Files);

% 对图像数据进行向量化
X = zeros(numImages, 128*128);
for i = 1:numImages
    img = readimage(imds, i);
    img = rgb2gray(img);
    img = imresize(img, [128, 128]);
    X(i,:) = img(:)';
end

% 使用谱聚类对图像数据进行

基于图像的聚类分析是一种将图像数据进行聚类的方法,其主要思想是将图像数据转化为高维向量,然后使用聚类算法对这些向量进行分组。在这个过程中,需要注意图像向量化的方法以及如何选择合适的聚类算法。

以下是使用MATLAB实现基于图像的聚类分析的示例代码:

  1. 加载和处理图像数据

在进行图像聚类之前,我们首先需要加载和处理图像数据。这里以一组鲜花图像为例,展示如何将图像转化为向量并进行聚类分析。代码如下:

% 加载图像数据
imds = imageDatastore('flower_photos', 'IncludeSubfolders', true, 'LabelSource', 'foldernames');
numImages = numel(imds.Files);

% 对图像数据进行向量化
X = zeros(numImages, 128*128);
for i = 1:numImages
    img = readimage(imds, i);
    img = rgb2gray(img);
    img = imresize(img, [128, 128]);
    X(i,:) = img(:)';
end

在这段代码中,我们首先使用imageDatastore函数加载鲜花图像数据,该函数会将所有图像数据存储到一个MATLAB内置的数据结构中。然后,我们循环遍历每张图像,将其转化为灰度图像并将其大小调整为128*128像素,最后将图像数据转化为向量形式,并存储到一个矩阵中。

  1. 进行聚类分析

在将图像数据向量化后,我们可以使用MATLAB中提供的聚类算法对这些向量进行分组。这里以谱聚类算法为例,代码如下:

% 使用谱聚类对图像数据进行聚类
S = affinityMatrix(X);
D = diag(sum(S, 2));
L = D - S;
[eigVec, eigVal] = eig(L);
[~, idx] = sort(eigVal(1:end-1, 1));
U = eigVec(:, idx(1:3));
U = normr(U);
[idx, C] = kmeans(U, 5);

在这段代码中,我们首先使用affinityMatrix函数计算图像数据之间的相似度矩阵,然后通过计算矩阵的拉普拉斯矩阵,使用谱聚类算法对图像数据进行聚类分析。最后,我们使用kmeans算法将聚类结果进行进一步的分组,kmeans算法的输出结果包括每个样本所属的群组编号以及每个群组的聚类中心。

  1. 可视化聚类结果

聚类分析的最终目的是将数据划分为不同的群组,并展示这些群组之间的差异和相似度。在这个例子中,我们可以使用MATLAB的图像处理工具箱和可视化工具箱来可视化聚类结果。代码如下:

% 可视化聚类结果
figure;
for i = 1:5
    subplot(2, 3, i);
    idx_i = (idx == i);
    X_i = X(idx_i, :);
    [~, I] = min(pdist2(C(i,:), U));
    img_i = reshape(X_i(I,:), [128, 128]);
    imshow(img_i);
    title(sprintf('Group %d', i));
end

在这段代码中,我们首先创建一个图像窗口,并循环遍历每个群组,将属于该群组的图像取出并绘制在图像窗口中。其中,我们使用pdist2函数计算每个群组的聚类中心与样本数据之间的距离,然后选择与聚类中心距离最近的样本数据作为该群组的代表图像进行可视化展示。

  1. 总结

本文介绍了基于图像的聚类分析的基本原理和MATLAB的实现方法。我们首先加载和处理了鲜花图像数据,并将图像转化为向量形式。然后,我们使用谱聚类算法对图像数据进行聚类分析,并使用kmeans算法对聚类结果进行进一步的分组。最后,我们使用MATLAB的图像处理工具箱和可视化工具箱可视化聚类结果,展示了不同群组之间的差异和相似度。希望本文能够帮助读者了解基于图像的聚类分析的基本原理和MATLAB的实现方法,同时也希望读者可以根据自己的实际应用场景,选择合适的聚类算法进行图像数据分析和处理。

ROS从入门到精通5-3:插件库与开发+实例分析

目录

0 专栏介绍

本专栏旨在通过对ROS的系统学习,掌握ROS底层基本分布式原理,并具有机器人建模和应用ROS进行实际项目的开发和调试的工程能力。

🚀详情:《ROS从入门到精通》

1 什么是插件?

插件可实现插拔式管理的组件,举个例子

在计算机系统中,所有USB设备——键盘、鼠标、U盘,都可以看作硬件插件的实现。

插件的基本原理是通过规范化协议实现的基类与功能类间的自由组合,在硬件上可能是USB等接口协议;在软件上则大多为API的多态实现——插件程序依赖于某个应用程序,且应用程序可以与不同的插件程序自由组合。

插件的优势有:

  • 插件可以自由增减,降低系统耦合,增强可维护性
  • 插件可移植性强,更具复用性
  • 系统结构清晰,易于调整

2 ROS插件库Pluginlib

ROS中也会经常使用插件的概念,例如

  • 导航

    在导航中涉及到路径规划模块,其中路径规划算法有多种:A*、蚁群算法、遗传算法等。因此可能需要测试不同算法的优劣以选择更合适的实现,或是自研设计新算法。此时,ROS允许使用插件来实现在同一个导航体系下,不同算法的快速、灵活切换

  • Rviz或Gazebo等工具

    在这些工具中虽然已经提供了丰富的功能实现,但在特定场景下可能存在某些定制化的需求,此时可通过插件集成功能到工具中

ROS中提供了Pluginlib包来帮助开发者在ROS构建的基础结构上编写、动态加载、卸载插件。Pluginlib是从运行时库(或称为动态链接库)加载的动态类。使用Pluginlib,不必显式地将其应用程序与包含类的库链接,因为Pluginlib可以在任何时候打开包含导出类的库,从而在无需应用程序源代码的情况下扩展或修改应用程序行为

3 自定义插件开发

3.1 插件开发基本流程

  • 选择基类或创建基类
  • 创建由该基类派生的插件类
  • 注册插件
  • 构建插件库.so
  • 集成插件库到ROS
  1. 配置插件库描述文件xml
  2. 导出插件
  • 使用插件

3.2 实例:多边形插件

创建功能包polygon_lab用于生成多边形插件

  1. 构造基类:在polygon_lab/include中新建polyBase.h,注意插件的构造函数不能带参数

    #ifndef POLY_BASE_H
    #define POLY_BASE_H
    
    namespace polyBase
    
      class Polygon
      
        public:
          virtual void initialize(double sideLength) = 0;
          virtual double calArea() = 0;
          virtual ~Polygon()
    
        protected:
          Polygon()
      ;
    ;
    #endif
    
  2. 构造插件类:在polygon_lab/include中新建polyPlugin.h

    #ifndef POLY_PLUGIN_H
    #define POLY_PLUGIN_H
    #include <polyBase.h>
    #include <cmath>
    
    namespace polyPlugin
    
      class Triangle : public polyBase::Polygon
      
        public:
          Triangle()
    
          void initialize(double sideLength)
          
            this->sideLength = sideLength;
          
    
          double calArea()
          
            return 0.5 * this->sideLength * getHeight();
          
    
        private:
          double sideLength;
          double getHeight()
          
            double temp = this->sideLength * this->sideLength;
            return sqrt(temp - 0.25 * temp);
             
      ;
    
      class Square : public polyBase::Polygon
      
        public:
          Square()
    
          void initialize(double sideLength)
          
            this->sideLength = sideLength;
          
    
          double calArea()
          
            return this->sideLength * this->sideLength;
          
    
        private:
          double sideLength;
    
      ;
    ;
    #endif
    

    这两个插件类就是今后要使用的扩展功能。

  3. 注册插件:在polygon_lab/src中新建polyPlugin.cpp使用PLUGINLIB_EXPORT_CLASS宏注册插件

    #include <pluginlib/class_list_macros.h>
    #include <polyBase.h>
    #include <polyPlugin.h>
    
    //参数1:插件类 参数2:基类
    PLUGINLIB_EXPORT_CLASS(polyPlugin::Triangle, polyBase::Polygon)
    PLUGINLIB_EXPORT_CLASS(polyPlugin::Square, polyBase::Polygon)
    
  4. 构建插件库.so:编译此功能包plygon_lab将会在根目录devel/lib中生成插件libpolygon_lab.so

  5. 集成插件库到ROS:在功能包plygon_lab下创建polyPlugin.xml描述插件信息和库路径

    <!-- 插件库的相对路径 -->
    <library path="lib/libpolygon_lab">
      <!-- type="插件类" base_class_type="基类" -->
      <class type="polyPlugin::Triangle" base_class_type="polyBase::Polygon">
        <!-- 描述信息 -->
        <description>This is a triangle plugin.</description>
      </class>
      <class type="polyPlugin::Square" base_class_type="polyBase::Polygon">
        <description>This is a square plugin.</description>
      </class>
    </library>
    

    在功能包polygon_labpackage.xml中导出polyPlugin.xml

    <export>
    	<polygon_lab plugin="$prefix/polyPlugin.xml" />
    </export>
    

    编译后,可以调用

    rospack plugins --attrib=plugin xxx
    

    命令查看配置是否正常,其中xxx应与基类所属的功能包名称一致。如无异常,会返回.xml文件的完整路径

  6. 使用插件:任意新建一个功能包进行测试

    #include <ros/ros.h>
    #include <pluginlib/class_loader.h>
    #include <polyBase.h>
    
    int main(int argc, char** argv)
    
      //类加载器 -- 参数1:基类功能包名称 参数2:基类全限定名称
      pluginlib::ClassLoader<polyBase::Polygon> polyLoader("polygon_lab", "polyBase::Polygon");
    
      try
      
        //创建插件类实例 -- 参数:插件类全限定名称
        boost::shared_ptr<polyBase::Polygon> triangle = polyLoader.createInstance("polyPlugin::Triangle");
        triangle->initialize(10.0);
    
        boost::shared_ptr<polyBase::Polygon> square = polyLoader.createInstance("polyPlugin::Square");
        square->initialize(10.0);
    
        ROS_INFO("Triangle area: %.2f", triangle->calArea());
        ROS_INFO("Square area: %.2f", square->calArea());
      
      catch(pluginlib::PluginlibException& ex)
      
        ROS_ERROR("The plugin failed to load for some reason. Error: %s", ex.what());
      
      return 0;
    
    

插件开发的另一个实例请参考ROS从入门到精通5-4:详解路径规划插件+代码实现

本文的完整工程代码联系下方博主名片获取


🔥 更多精彩专栏


👇源码获取 · 技术交流 · 抱团学习 · 咨询分享 请联系👇

以上是关于数学建模专栏 - 聚类分析入门:基本概念和MATLAB实现的主要内容,如果未能解决你的问题,请参考以下文章

《数学建模实战攻略:引言》

Spark学习10_1 sparkMllib入门与相关资料索引

ROS从入门到精通5-3:插件库与开发+实例分析

入门 | 解读分类算法与聚类算法

数据分析基于多元宇宙优化DBSCAN聚类matlab源码

ML: 聚类算法R包-模糊聚类