繁体   English   中英

识别给定边的连通图

[英]identifying connected graphs given edges

如何对相关人员进行分组,甚至间接? 具体来说,使用如下数据集的前两列,我如何在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 ;

查找图的所有连通分量的一般方法是广度优先搜索深度优先搜索 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稍后将使用组ID进行填充:

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;

这是一个具有复杂解决方案的常见问题。 您需要多复杂主要取决于数据的复杂程度。 链接的频率多于单个链接 - 即,在上面的示例中,C和D由5链接。 你有一个与D相关的E乘以6吗? 如果是这样,那么这需要不同的方法或解决步骤。

我在这里展示一个简单的方法。 这是一个非常简单的解决方案,但有时更容易理解和实现。 记录链接是一个很好的主题,有很多论文可供探索; 存在更好的解决方案,比下面的解决方案更能处理多个链接(处理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;

我一直在处理一个需要类似问题的问题。 我能够使用SAS OR使用proc OPTNET(语句CONCOMP)来解决。 文档甚至提供了一个很好地说明这个概念的例子。

谢谢,
穆里罗

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM