如何对在MATLAB中连接的点进行聚类?

时间:2014-06-18 20:37:10

标签: matlab graph connect points

想象一下,我们有很多点,其中一些连接在一起,我们想要聚类它们。

请看下图。

enter image description here

如果我们有" 连接矩阵"我们如何将它们聚集在两组(连接点组)中?

ConnectivityMatrix=
                    [1 2
                     1 3
                     2 4
                     2 3
                     2 1
                     3 1
                     3 2
                     3 4
                     4 3
                     4 2
                     5 8
                     5 7
                     5 6
                     6 5
                     6 7
                     7 6
                     7 5
                     7 8
                     8 7
                     8 5]

最终结果应该是第一组(群集)中1,2,3,4的节点和第二组(群集)中5,6,7,8的节点。

3 个答案:

答案 0 :(得分:3)

以下是一些可以帮助您入门的代码。我基本上实现了深度优先搜索...无论如何都是非常粗略的实现。

Depth First Search是一种用于遍历树的算法。图表本质上是树的一种特殊情况,其中有叶节点连接回根。深度优先搜索的基本算法如下:

  • 从树的根开始并将其添加到堆栈
  • 对于连接到根的每个节点,将其添加到堆栈并将其放入受访节点列表中
  • 虽然堆栈上还有一个节点......
    1. 从群组中弹出一个节点
    2. 检查受访节点列表。如果这是我们已经访问过的节点,请跳过。
    3. 否则,访问连接到我们弹出的节点并添加到堆栈的任何节点

如果我们有上面的断开图表,我们基本上会多次运行深度优先搜索。每次都是针对一个群集。在一个Depth First Search结果之后,我们将发现属于一个集群的节点。我们再次使用我们尚未触及的任何节点重新启动深度优先搜索,这将是来自我们尚未访问的另一个群集的节点。由于您的图表结构中显然有两个聚类,我们必须运行两次深度优先搜索。这通常被称为在整体图表中查找所有连接的组件

要查找已连接的组件,请执行以下步骤:

  1. 创建连接矩阵
  2. 初始化一个布尔列表,告诉我们是否访问了图表中的节点
  3. 初始化空群集列表
  4. 初始化一个空堆栈,其中包含我们应该访问的节点。
  5. 虽然我们需要访问至少一个节点...
    • 找到这样的节点
    • 初始化我们的堆栈以包含此节点
    • 虽然我们的筹码不是空的
      1. 从堆栈中弹出一个节点
      2. 如果我们访问了此节点,请继续
      3. 其他标记为已访问
      4. 检索连接到此节点的所有节点
      5. 删除(4)
      6. 中不在堆栈上的节点
      7. 将这些节点添加到堆栈和群集列表
  6. 一旦堆栈为空,我们就会列出一个集群中包含的所有节点。将此群集添加到最终列表中。
  7. 重复1 - 6,直到我们访问所有节点
  8. 不用多说,这就是代码。请记住,这不是战斗测试。如果您的图形结构会产生错误,那么您将自行修复:)

    ConnectivityMatrix = [1 2
                         1 3
                         2 4
                         2 3
                         2 1
                         3 1
                         3 2
                         3 4
                         4 3
                         4 2
                         5 8
                         5 7
                         5 6
                         6 5
                         6 7
                         7 6
                         7 5
                         7 8
                         8 7
                         8 5];
    
    %// Find all possible node IDs
    nodeIDs = unique(ConnectivityMatrix(:));
    
    %// Flag that tells us if there are any nodes we should visit
    nodeIDList = false(1,numel(nodeIDs));
    
    %// Stores our list of clusters
    clusterList = {};
    
    %// Keeps track of how many clusters we have
    counter = 1;
    
    %// Stack - initialize to empty
    stackNodes = [];
    
    %// While there is at least one node we need to visit
    while (~all(nodeIDList))    
        % Find any node
        stackNodes = find(nodeIDList == false, 1);
        % Initialize our stack to contain this node
        nodesCluster = stackNodes;
    
        %// While our stack is not empty
        while (~isempty(stackNodes))
            % Grab the node off the stack and pop off
            node = stackNodes(end);
            stackNodes(end) = [];        
    
            %// If we have marked this node as visited, skip
            if (nodeIDList(node))
                continue;
            end
    
            %// Mark as visited 
            nodeIDList(node) = true;
    
            %// Retrieve all nodes connected to this node
            connectedNodes = ConnectivityMatrix(ConnectivityMatrix(:,1) == node, :);
            nodesToVisit = unique(connectedNodes(:,2).');
    
            %// Remove those already visited
            visitedNodes = ~nodeIDList(nodesToVisit);
            finalNodesToVisit = nodesToVisit(visitedNodes);
    
            %// Add to cluster
            nodesCluster = unique([nodesCluster finalNodesToVisit]);
    
            %// Add to stack
            stackNodes = unique([stackNodes finalNodesToVisit]);                
        end
    
        %// Add connected components to its own cluster
        clusterList{counter} = nodesCluster;
        counter = counter + 1;
    end
    

    运行此代码后,我们可以像这样显示我们的集群:

    celldisp(clusterList)
    
    clusterList{1} =
    
     1     2     3     4
    
    
    clusterList{2} =
    
     5     6     7     8
    

    因此,集群#1包含节点1,2,3,4,而集群#2包含节点5,6,7,8。

    请记住,只有在您按照图表中的顺序标记节点时,此代码才有效。你不能跳过任何标签号码(即你不能做1,2,4,6,9等等。这应该是1,2,3,4,5)。

    祝你好运!

答案 1 :(得分:1)

您可以使用“现成的”Matlab命令来解决此问题。例如,您可以使用graphconncomp

答案 2 :(得分:1)

The answer from rayryeng is pretty good。但是,我想指出一些细节:

  • " numel(nodeIDs)"如果您的节点标签不是顺序的(即,您有10个节点且最大节点标签为20),则会导致可能的错误。你可以切换" numel(nodeIDs)"到" max(nodeIDs)或更大的值。"
  • 我还认为以下代码在使用此功能时会带来一些问题(即某些节点丢失并成为隔离节点):

    connectedNodes = ConnectivityMatrix(ConnectivityMatrix(:,1) == node, :);
    nodesToVisit = unique(connectedNodes(:,2).');
    

    我用以下凌乱的代码修改了简单的两行:

    connectedNodes1 = ConnectivityMatrix (ConnectivityMatrix (:,1) == node, :);
    connectedNodes2 = ConnectivityMatrix (ConnectivityMatrix (:,2) == node, :);
    AC=connectedNodes1(:,2).';
    AD=connectedNodes2(:,1).';
    ACA=reshape(AC,[],1);
    ADA=reshape(AD,[],1);
    AE= [ACA; ADA] ;
    AEA=reshape(AE,[],1);
    AEA=AEA';
    nodesToVisit = unique(AEA);
    

    修改这两点后,rayryeng的初始代码没有问题。