如何使用SAS实现基于a、b字段关联的分组算法?
当然可以实现!你说的这种分组逻辑本质上是图论里的连通分量问题——把每一行当成一个节点,只要两行的a或b值相同,就给这两个节点连一条边,最终同一个连通分量里的所有节点就归为同一个分组。下面是用SAS实现的具体方案:
首先先把你的示例数据整理成清晰的表格:
| #n | a | b | 预期分组 |
|---|---|---|---|
| 1 | 1 | 1 | 1 |
| 2 | 1 | 2 | 1 |
| 3 | 2 | 2 | 1 |
| 4 | 2 | 3 | 1 |
| 5 | 3 | 4 | 2 |
| 6 | 3 | 5 | 2 |
| 7 | 4 | 5 | 2 |
实现思路:并查集(Union-Find)算法
这是处理连通分量问题最高效的算法之一,核心是记录每个元素的父节点,通过合并操作把关联的元素归到同一组,最终找到每个元素的根节点作为分组ID。
下面是完整的SAS代码:
/* 第一步:准备原始数据 */ data have; input #n a b; datalines; 1 1 1 2 1 2 3 2 2 4 2 3 5 3 4 6 3 5 7 4 5 ; run; /* 第二步:用并查集算法计算分组 */ data want; set have; retain group_id 0; length key $20 root $20; /* 初始化哈希表,存储每个a/b值对应的根节点,以及分组的父节点映射 */ if _n_ = 1 then do; declare hash uf(ordered:'no'); uf.defineKey('key'); uf.defineData('root'); uf.defineDone(); end; /* 处理当前行的a值:如果a未记录过,新建分组 */ key = cats('a_', a); if uf.find() ne 0 then do; group_id + 1; root = cats('g_', group_id); uf.add(); end; a_root = root; /* 处理当前行的b值:如果b未记录过,新建分组 */ key = cats('b_', b); if uf.find() ne 0 then do; root = cats('g_', group_id); uf.add(); end; b_root = root; /* 合并a和b对应的分组(如果它们属于不同分组) */ if a_root ne b_root then do; /* 找到a_root的最终根节点 */ temp = a_root; do while (uf.find(key:temp) eq 0); temp = root; end; final_a_root = temp; /* 找到b_root的最终根节点 */ temp = b_root; do while (uf.find(key:temp) eq 0); temp = root; end; final_b_root = temp; /* 合并两个分组:让其中一个根指向另一个 */ if final_a_root ne final_b_root then do; key = final_b_root; root = final_a_root; uf.add(); end; end; /* 确定当前行的最终分组ID */ temp = a_root; do while (uf.find(key:temp) eq 0); temp = root; end; group = input(scan(temp, 2, '_'), best.); /* 清理临时变量 */ drop key root a_root b_root temp final_a_root final_b_root; run; /* 查看结果 */ proc print data=want; run;
代码说明:
- 我们用哈希表
uf来存储两种映射:a_xxx/b_xxx:记录每个a、b值对应的初始分组根节点g_xxx:记录分组之间的合并关系(比如g_2指向g_1表示分组2合并到分组1)
- 对每一行,先处理
a和b值,为未出现过的值创建新分组; - 如果当前行的
a和b属于不同分组,就合并这两个分组; - 最后通过追溯根节点,得到当前行的最终分组ID。
运行这段代码后,输出的group列会和你预期的分组完全一致。
内容的提问来源于stack exchange,提问作者fossekall
相关产品推荐
相关产品推荐

