当前位置:

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

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

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

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

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

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

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

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

示例数据与预期输出:

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

id
1
2
...
8

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

聚类结果 1

cluster_keyid
31
32
33
64
65
66
87
88

聚类结果 2

cluster_keyid
11
22
53
54
55
66
87
88

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

预期合并输出

cluster_keyid
61
62
63
64
65
66
87
88

逻辑解析:

  • 在聚类结果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)”有所不同,共识聚类旨在寻找一个折衷或改进的聚类结果,而这里我们寻求的是通过重叠关系进行完全合并。

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_idsourcecluster_keyid
1C131
1C132
1C133
2C164
............
5C211
6C222
............

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_acluster_b
15
16
17
27
28
39
49

(注意:这里的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):
本文内容来源于互联网,如有侵权请联系删除。
作者最新文章
编程开发
相关文章 更多
using namespace 使用中遇到的问题怎么解决
using namespace 使用中遇到的问题怎么解决

命名空间的基本概念与常见引入问题在C++等编程语言中,命名空间(namespace)是一种将代码标识符(如变量、函数、类名)封装在特定名称下的机制,其主要目的是避免命名冲突,尤其是在大型项目或使用多个第三方库时。使用“using namespace”指令可以将指定命名空间中的所有名称引入当前作用域,

c语言函数递归 实操经验总结:这些技巧很实用
c语言函数递归 实操经验总结:这些技巧很实用

理解递归的基本原理在C语言中,递归是一种函数调用自身的编程技术。要掌握它,首先需要理解其核心思想:将一个复杂的大问题,分解为一个或几个与原问题相似但规模更小的子问题,直到子问题足够简单,可以直接求解。这个过程通常包含两个关键部分:递归出口和递归体。递归出口定义了问题何时不再继续分解,即最简单、可直接

c语言函数递归 怎么选?常见方案对比分析
c语言函数递归 怎么选?常见方案对比分析

递归函数的基本概念与适用场景在C语言编程中,递归是一种函数调用自身的编程技巧。它并非适用于所有问题,但在处理某些具有自相似结构的问题时,能提供极其清晰和优雅的解决方案。递归的核心思想是将一个大规模问题分解为一个或多个同类型但规模更小的子问题,直到子问题简单到可以直接求解。典型的适用场景包括树形结构的

Objective-C 内存管理入门:从 alloc 到 dealloc 的生命周期详解
Objective-C 内存管理入门:从 alloc 到 dealloc 的生命周期详解

理解内存管理的基石在Objective-C的编程世界中,内存管理是开发者必须掌握的核心技能之一。它直接关系到应用的性能、稳定性与资源利用效率。与一些采用自动垃圾回收机制的语言不同,Objective-C在很长一段时间里,依赖一套基于引用计数的、需要开发者部分介入的管理规则。这套规则的核心思想是明确的

如何正确使用 dealloc 以避免 iOS 应用中的内存泄漏
如何正确使用 dealloc 以避免 iOS 应用中的内存泄漏

理解 dealloc 的角色与时机在 iOS 应用开发中,内存管理是保障应用性能与稳定性的基石。dealloc 方法是 Objective-C 中对象生命周期结束时的关键回调,它标志着对象即将被系统回收内存。正确理解其触发时机至关重要:当一个对象的引用计数降为零时,运行时系统会自动调用该对象的 de

深入理解 Objective-C 中的 dealloc 方法:内存管理核心机制
深入理解 Objective-C 中的 dealloc 方法:内存管理核心机制

内存管理的基石在Objective-C的世界里,内存管理是开发者必须掌握的核心技能之一。作为一门在手动引用计数(MRC)时代诞生的语言,Objective-C要求程序员对对象的生命周期有清晰的认识。dealloc方法正是这一生命周期中至关重要的终点站。它是一个实例方法,当对象的引用计数降为零时,系统

理解 native2ascii:Java 国际化开发中的字符编码工具
理解 native2ascii:Java 国际化开发中的字符编码工具

native2ascii 工具的基本定位在Ja va应用程序的国际化与本地化开发过程中,处理非拉丁字符集是一个常见且关键的环节。Ja va内部使用Unicode字符集来统一表示全球各种语言的文字,但其属性文件(.properties)在历史上要求使用ASCII编码,或者更准确地说,要求非ASCII字

如何使用 native2ascii 转换中文字符为 Unicode 转义序列
如何使用 native2ascii 转换中文字符为 Unicode 转义序列

理解 native2ascii 工具的基本用途在软件开发,特别是涉及国际化处理的场景中,开发者常常需要处理不同编码的文本资源。native2ascii 是 Ja va 开发工具包(JDK)中提供的一个命令行实用程序,其主要功能是将包含本地字符编码(非ASCII字符)的文件,转换为包含 Unicode

Java native2ascii 命令详解:解决属性文件乱码问题
Java native2ascii 命令详解:解决属性文件乱码问题

native2ascii 命令的由来与作用在Ja va开发中,处理国际化资源文件是一个常见需求。资源文件通常以.properties格式存储,用于支持多语言界面。然而,Ja va属性文件默认采用ISO-8859-1字符集编码,这导致了一个直接的问题:当文件中包含非拉丁字符(如中文、日文、韩文等)时,

一个 memwatch 实战案例:定位野指针问题
一个 memwatch 实战案例:定位野指针问题

内存监控工具的价值与挑战在软件开发,尤其是使用C/C++这类手动管理内存的语言时,内存错误是程序员最常遭遇的难题之一。其中,野指针问题因其隐蔽性和破坏性,往往成为最难定位的“幽灵”缺陷。它可能潜伏在代码中,在特定条件下才被触发,导致程序崩溃、数据损坏或难以预测的行为。传统的调试手段,如打印日志或使用

查看更多
精品专题 更多
装机必备
装机必备

正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

Windows
Windows

正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

macOS软件
macOS软件

正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

Mac软件 更多
灵活计算器
灵活计算器
macOS/iOS/Android

灵活计算器是一款笔记式算数应用,支持实时计算、动态关联和云端同步功能。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

赤友清理大师
赤友清理大师
macOS

赤友清理大师是一款为 Mac 设计的智能清理优化工具,可精准扫描垃圾、大文件、重复文件等,释放磁盘空间。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

极度公式
极度公式
Windows/macOS/Linux

极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

WINDOWS 更多
Windows 10
Windows 10
Windows

Windows 10 是一款微软推出的经典操作系统,拥有硬件兼容性与多任务处理能力。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。

极度公式
极度公式
Windows/macOS/Linux

极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。

密码键盘
密码键盘
Windows/macOS/iOS/Android

密码键盘是一款兼具安全性与便捷性的高效密码管理器。日常使用里的持续防护和信息管理会更突出,适合把安全控制放进长期使用流程中的场景。