识别给定边的连通图

时间:2014-05-07 18:12:27

标签: sas graph-theory

如何对相关人员进行分组,甚至间接?具体来说,使用如下数据集的前两列,我如何在SAS(可能使用DATA步骤或PROC SQL)中以编程方式派生第三列?是否存在非迭代算法?

背景:每个人都有多个地址。通过每个地址,每个人与零个或多个人相连。如果连接了两个人,则他们会获得相同的组ID。如果A人直接连接到B而B连接到C,则A,B和C人共用一组。

graph illustration

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 ;

3 个答案:

答案 0 :(得分:2)

查找图表的所有连接组件的一般方法是Breadth-First-SearchDepth-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)来解决。文档甚至提供了一个很好地说明这个概念的例子。

谢谢,
穆里罗