如何对相关人员进行分组,甚至间接?具体来说,使用如下数据集的前两列,我如何在SAS(可能使用DATA步骤或PROC SQL)中以编程方式派生第三列?是否存在非迭代算法?
背景:每个人都有多个地址。通过每个地址,每个人与零个或多个人相连。如果连接了两个人,则他们会获得相同的组ID。如果A人直接连接到B而B连接到C,则A,B和C人共用一组。
data people;
input person_id address_id $ household_id;
datalines;
1 A 1
2 B 2
3 B 2
4 C 3
5 C 3
5 D 3
6 D 3
;
答案 0 :(得分:2)
查找图表的所有连接组件的一般方法是Breadth-First-Search或Depth-First-Search。 SAS不是实现此类算法的最佳工具,因为它们需要使用这样的数据结构作为队列。
仍然可以使用哈希对象完成。这是BF-search的代码。
data people;
input person_id address_id $ household_id;
datalines;
1 A 1
2 B 2
3 B 2
4 C 3
5 C 3
5 D 3
6 D 3
;
run;
创建邻接列表 - 具有公共地址的所有人对。并且空变量cluster
将在以后用组'填充。标识:
proc sql;
create table connections as
select distinct a.person_id as person_id_a, b.person_id as person_id_b, . as cluster
from people a
inner join people b
on a.address_id=b.address_id
;
quit;
这是BF搜索本身:
data _null_;
为所有唯一的人(图的顶点)声明哈希对象及其迭代器:
if 0 then set Connections;
dcl hash V(dataset:'Connections', ordered:'y');
V.defineKey('person_id_a');
V.defineData('person_id_a','cluster');
dcl hiter Vi('V');
V.defineDone();
为所有连接声明哈希对象(图的边缘):
dcl hash E(dataset:'Connections', multidata:'y');
E.defineKey('person_id_a');
E.defineData('person_id_a','person_id_b');
E.defineDone();
声明队列的哈希对象及其迭代器:
dcl hash Q(ordered:'y');
Q.defineKey('qnum','person_id_a');
Q.defineData('qnum','person_id_a');
dcl hiter Qi('Q');
Q.defineDone();
最外层循环 - 当队列为空时,将未分配群集的新人作为下一个群集的根目录:
rc1=Vi.first();
do while(rc1=0);
if missing(cluster) then do;
qnum=1; Q.add(); *qnum-number of the person in the queue, to ensure that new people are added to the end of the queue.;
n+1; cluster=n;
V.replace();*assign cluster number to a person;
在以下两个嵌套循环中,我们将队列中的第一个人取消队列,并在邻接列表中查找与此人相关的所有人。每个找到的'连接'我们添加到队列的末尾。当第一个人完成后,我们删除他/她并将下一个人排队(现在成为第一个人)。所有这些都将在同一个集群中。依此类推,直到队列为空。然后我们为新的群集带来一个新的root用户。
rc2=Qi.first();
do while(rc2=0);
qnum=qnum+Q.num_items-1;
rc3=E.find();
do while(rc3=0);
person_id_a=person_id_b;
rc4=V.find();
if rc4=0 and missing(cluster) then do;
qnum+1; Q.add();
cluster=n;
V.replace();
end;
rc3=E.find_next();
end;
Qi.first();
Qi.delete();
Q.remove();
Qi=_new_ hiter ('Q');
rc2=Qi.first();
end;
end;
rc1=Vi.next();
end;
输出已分配群集的人员列表。
V.output(dataset:'clusters');
run;
proc sort data=clusters; by cluster; run;
答案 1 :(得分:0)
这是一个具有复杂解决方案的常见问题。您需要多复杂主要取决于数据的复杂程度。链接的频率多于单个链接 - 即,在上面的示例中,C和D由5
链接。您可以通过6
获得与D相关联的E吗?如果是这样,则需要采用不同的方法或解决步骤。
我在这里展示了一个简单的方法。这是一个非常简单的解决方案,但有时更容易理解和实现。记录链接是一个很好的主题,有很多论文可供探索;存在更好的解决方案,比下面的解决方案更能处理多个链接(处理2级链接但没有进一步处理,并且在处理数据交联方面存在一些缺点)。
data people;
input person_id address_id $ household_id;
datalines;
1 A 1
2 B 2
3 B 2
4 C 3
5 C 3
5 D 3
6 D 3
6 E 3
7 E 3
8 B 2
;
run;
data links(keep=link:);
set people;
by person_id address_id;
retain link_start;
if first.person_id and not last.person_id then do;
link_start = address_id;
end;
if first.address_id and not first.person_id then do;
link_end = address_id;
output;
end;
run;
data for_fmt;
set links;
start=link_end;
label=link_Start;
retain fmtname '$linkf';
output;
run;
proc sort nodupkey data=for_fmt;
by start;
run;
proc format cntlin=for_fmt;
quit;
data people_linked;
set people;
new_addressid = put(address_id,$linkf.);
new_addressid = put(new_addressid, $linkf.);
run;
proc sort data=people_linked;
by new_addressid;
run;
data people_final;
set people_linked;
by new_addressid;
if first.new_addressID then
new_householdID+1;
run;
答案 2 :(得分:0)
我一直在处理需要类似问题的问题。我能够使用SAS OR使用proc OPTNET(语句CONCOMP)来解决。文档甚至提供了一个很好地说明这个概念的例子。
谢谢,
穆里罗