0

0

多算法聚类结果的合并策略与SQL实现:基于连通分量的传递闭包方法

心靈之曲

心靈之曲

发布时间:2025-08-28 14:18:17

|

1061人浏览过

|

来源于php中文网

原创

多算法聚类结果的合并策略与sql实现:基于连通分量的传递闭包方法

本文探讨了如何合并来自不同聚类算法、但作用于同一数据集的聚类结果。当不同算法的集群通过共享相同数据项而存在重叠时,需要将这些重叠集群进行传递性合并。文章将阐述此问题本质上是图论中的连通分量发现,并提供基于SQL和Python/PySpark的解决方案,重点讲解其逻辑、实现步骤及注意事项,以生成统一的最终聚类结果。

1. 理解多算法聚类合并问题

在数据分析实践中,我们常会使用多种聚类算法对同一批数据项进行分析,每种算法可能基于不同的特征或参数生成各自的聚类结果。核心挑战在于,如何将这些独立的聚类结果进行整合,特别是当不同算法产生的集群之间存在重叠(即共享至少一个数据项)时,需要将这些相互关联的集群合并成一个更大的、统一的集群。这种合并必须是传递性的:如果集群A与B重叠,B与C重叠,那么A、B、C都应被合并到同一个最终集群中。

为了确保最终合并的集群有一个确定的标识,我们通常会选择一个确定性规则来生成新的集群键,例如,将合并后所有数据项中的最大ID作为新集群的键。

示例数据与预期输出:

假设我们有以下8个数据项(ID从1到8):

id
1
2
...
8

以下是两个不同聚类算法的输出:

聚类结果 1

cluster_key id
3 1
3 2
3 3
6 4
6 5
6 6
8 7
8 8

聚类结果 2

cluster_key id
1 1
2 2
5 3
5 4
5 5
6 6
8 7
8 8

根据“任何共享一个ID的集群都应合并,且合并具有传递性”的规则,我们期望的输出如下:

预期合并输出

cluster_key id
6 1
6 2
6 3
6 4
6 5
6 6
8 7
8 8

逻辑解析:

  • 在聚类结果1中,集群{1,2,3}的键是3,集群{4,5,6}的键是6。
  • 在聚类结果2中,集群{1}的键是1,集群{2}的键是2,集群{3,4,5}的键是5,集群{6}的键是6。
  • 数据项1:在结果1中属于集群3,在结果2中属于集群1。因此,原始集群3和集群1需要合并。
  • 数据项2:在结果1中属于集群3,在结果2中属于集群2。因此,原始集群3和集群2需要合并。
  • 数据项3:在结果1中属于集群3,在结果2中属于集群5。因此,原始集群3和集群5需要合并。
  • 由于合并的传递性,原始集群1、2、3、5、6(来自两个聚类结果)都通过数据项1、2、3、4、5、6关联起来,最终形成一个大集群。这个大集群包含的数据项是{1,2,3,4,5,6},其最大ID为6,故新集群键为6。
  • 数据项7和8:在两个聚类结果中都属于键为8的集群,它们是独立的,因此保持不变,新集群键为8。

2. 理论基础:连通分量与传递闭包

上述问题在图论中被称为连通分量(Connected Components)问题,或者更精确地说,是计算特定关系下的传递闭包(Transitive Closure)

我们可以将每个原始聚类结果中的一个集群视为图中的一个节点。如果两个集群(无论它们来自哪个聚类算法)共享至少一个数据项,那么它们之间就存在一条。我们的目标是找出这个图中的所有连通分量,即所有相互连接的节点集合。

这种方法可以被描述为基于“非空重叠”的“单链接聚类(Single Link Clustering)”的一种变体,但其应用对象是已有的集群而非原始数据点。它与通常意义上的“共识聚类(Consensus Clustering)”有所不同,共识聚类旨在寻找一个折衷或改进的聚类结果,而这里我们寻求的是通过重叠关系进行完全合并。

Cogram
Cogram

使用AI帮你做会议笔记,跟踪行动项目

下载

3. SQL实现策略

尽管SQL在处理图的传递闭包(尤其是连通分量)方面存在固有限制,特别是对于大型或深度图,递归CTE(Common Table Expression)可能面临性能和深度限制。但我们可以利用SQL来完成数据准备和识别直接重叠关系,然后结合外部工具来处理复杂的连通分量计算。

3.1 数据准备与标准化

首先,我们需要将所有聚类结果统一到一个表中,并为每个原始集群分配一个唯一的标识符,以便后续处理。

-- 创建示例数据表
CREATE TABLE Clustering1 (cluster_key INT, id INT);
INSERT INTO Clustering1 VALUES (3,1), (3,2), (3,3), (6,4), (6,5), (6,6), (8,7), (8,8);

CREATE TABLE Clustering2 (cluster_key INT, id INT);
INSERT INTO Clustering2 VALUES (1,1), (2,2), (5,3), (5,4), (5,5), (6,6), (8,7), (8,8);

-- 步骤1:标准化集群数据
-- 为每个原始集群分配一个唯一的 original_cluster_id
-- 这里我们假设 original_cluster_id 可以通过 (source_table, cluster_key) 唯一标识
-- 或者更简单地,直接生成一个序列号
WITH AllClustersRaw AS (
    SELECT 'C1' AS source, cluster_key, id FROM Clustering1
    UNION ALL
    SELECT 'C2' AS source, cluster_key, id FROM Clustering2
),
ClusteredItems AS (
    SELECT
        ROW_NUMBER() OVER (ORDER BY source, cluster_key) AS original_cluster_id,
        source,
        cluster_key,
        id
    FROM (
        SELECT DISTINCT source, cluster_key, id FROM AllClustersRaw
    ) AS distinct_clusters_and_items
)
SELECT * FROM ClusteredItems;

ClusteredItems 表的示例输出(original_cluster_id 可能因具体数据库和数据而异):

original_cluster_id source cluster_key id
1 C1 3 1
1 C1 3 2
1 C1 3 3
2 C1 6 4
... ... ... ...
5 C2 1 1
6 C2 2 2
... ... ... ...

3.2 识别直接重叠关系

接下来,我们需要找出哪些original_cluster_id之间存在直接重叠(即共享至少一个id)。这将为我们构建图的边列表。

-- 步骤2:识别直接连接的集群对
WITH AllClustersRaw AS (
    SELECT 'C1' AS source, cluster_key, id FROM Clustering1
    UNION ALL
    SELECT 'C2' AS source, cluster_key, id FROM Clustering2
),
ClusteredItems AS (
    SELECT
        DENSE_RANK() OVER (ORDER BY source, cluster_key) AS original_cluster_id,
        source,
        cluster_key,
        id
    FROM (
        SELECT DISTINCT source, cluster_key, id FROM AllClustersRaw
    ) AS distinct_clusters_and_items
)
SELECT DISTINCT
    c1.original_cluster_id AS cluster_a,
    c2.original_cluster_id AS cluster_b
FROM ClusteredItems c1
JOIN ClusteredItems c2 ON c1.id = c2.id AND c1.original_cluster_id < c2.original_cluster_id;

输出示例(代表了图的边):

cluster_a cluster_b
1 5
1 6
1 7
2 7
2 8
3 9
4 9

(注意:这里的original_cluster_id是基于DENSE_RANK生成的,与前面的ROW_NUMBER可能不同,但原理一致。例如,如果C1-3的original_cluster_id是1,C2-1是5,那么1,5就是一条边。)

3.3 计算连通分量(SQL挑战与外部工具推荐)

在标准SQL中,直接计算任意图的连通分量(即传递闭包)是复杂的。虽然一些数据库支持递归CTE,可以用于有限深度的路径查找,但将其应用于通用连通分量计算,特别是要为每个分量分配一个统一的merged_component_id,往往效率低下且难以维护。

SQL模拟思路(迭代更新 - 概念性): 对于小型数据集,可以尝试通过迭代更新来模拟Union-Find算法。这需要一个临时表来存储每个original_cluster_id的当前“根”组件ID,并通过多次UPDATE语句来传播这些根ID,直到没有更多的更新发生。

-- 概念性SQL迭代更新(不推荐用于生产环境,尤其大型数据集)
-- 假设我们有一个临时表 ComponentRoots (original_cluster_id INT, root_id INT)
-- 初始化:每个集群是自己的根
-- INSERT INTO ComponentRoots SELECT original_cluster_id, original_cluster_id FROM (SELECT DISTINCT original_cluster_id FROM ClusteredItems);

-- 迭代更新:
-- WHILE EXISTS (SELECT 1 FROM ComponentRoots cr1 JOIN ComponentRoots cr2 ON ... WHERE cr1.root_id != cr2.root_id)
-- BEGIN
--     UPDATE ComponentRoots SET root_id = LEAST(cr1.root_id, cr2.root_id)
--     FROM ComponentRoots cr1 JOIN ComponentRoots cr2 ON ... (using the overlap relationships)
--     WHERE cr1.root_id != cr2.root_id;
-- END;

这种方法通常不是纯粹的SQL查询,而是需要在应用层进行循环控制的批处理操作。

推荐方案: 将步骤3.2中生成的重叠关系(边列表)导出,利用外部编程语言(如Python)的图库或Union-Find算法进行高效计算。这是处理连通分量最健壮和性能最佳的方法。

4. Python/PySpark实现(推荐方案)

Python及其丰富的库生态系统,如networkx(图论库)或通过实现Union-Find数据结构,能够高效地解决连通分量问题。对于大规模数据,PySpark及其GraphFrames库是理想选择。

4.1 使用Union-Find数据结构

Union-Find是一种用于处理不相交集合的算法,非常适合解决连通分量问题。

Union-Find 类实现:

class UnionFind:
    def __init__(self, elements):
        self.parent = {e: e for e in elements}
        self.rank = {e: 0 for e in elements} # Optional: for optimization (union by rank)

    def find(self, i):

相关专题

更多
python开发工具
python开发工具

php中文网为大家提供各种python开发工具,好的开发工具,可帮助开发者攻克编程学习中的基础障碍,理解每一行源代码在程序执行时在计算机中的过程。php中文网还为大家带来python相关课程以及相关文章等内容,供大家免费下载使用。

715

2023.06.15

python打包成可执行文件
python打包成可执行文件

本专题为大家带来python打包成可执行文件相关的文章,大家可以免费的下载体验。

625

2023.07.20

python能做什么
python能做什么

python能做的有:可用于开发基于控制台的应用程序、多媒体部分开发、用于开发基于Web的应用程序、使用python处理数据、系统编程等等。本专题为大家提供python相关的各种文章、以及下载和课程。

739

2023.07.25

format在python中的用法
format在python中的用法

Python中的format是一种字符串格式化方法,用于将变量或值插入到字符串中的占位符位置。通过format方法,我们可以动态地构建字符串,使其包含不同值。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

617

2023.07.31

python教程
python教程

Python已成为一门网红语言,即使是在非编程开发者当中,也掀起了一股学习的热潮。本专题为大家带来python教程的相关文章,大家可以免费体验学习。

1235

2023.08.03

python环境变量的配置
python环境变量的配置

Python是一种流行的编程语言,被广泛用于软件开发、数据分析和科学计算等领域。在安装Python之后,我们需要配置环境变量,以便在任何位置都能够访问Python的可执行文件。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

547

2023.08.04

python eval
python eval

eval函数是Python中一个非常强大的函数,它可以将字符串作为Python代码进行执行,实现动态编程的效果。然而,由于其潜在的安全风险和性能问题,需要谨慎使用。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

575

2023.08.04

scratch和python区别
scratch和python区别

scratch和python的区别:1、scratch是一种专为初学者设计的图形化编程语言,python是一种文本编程语言;2、scratch使用的是基于积木的编程语法,python采用更加传统的文本编程语法等等。本专题为大家提供scratch和python相关的文章、下载、课程内容,供大家免费下载体验。

697

2023.08.11

桌面文件位置介绍
桌面文件位置介绍

本专题整合了桌面文件相关教程,阅读专题下面的文章了解更多内容。

0

2025.12.30

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 0.6万人学习

Django 教程
Django 教程

共28课时 | 2.6万人学习

SciPy 教程
SciPy 教程

共10课时 | 1.0万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号