使用sas

时间:2019-03-29 08:03:16

标签: sas sas-macro

我想比较下面显示的数据集中的两列

Pid       cid
1          2
2          3
2          5
3          6
4          8
8          9
9          4

然后产生如下结果

1 2 3 6
1 2 5
2 3 6
2 5
3 6
4 8 9 4
8 9 4
9 4

首先,我们打印前两个值1和2,在第一列中搜索2,如果它的当前打印来自第2列的对应值,即3。如果在当前列中,则打印3中的对应值,如果从3的第一列中搜索对应的值。第2列是6

如何使用SAS完成此操作?

5 个答案:

答案 0 :(得分:0)

正如评论所说,无限循环和搜索路径至少不清楚。因此,让我们从最简单的情况开始:始终从上到下搜索,然后回头看。

只需从创建数据集开始:

data test;
    input Pid Cid;
    cards;
    1 2
    2 3
    2 5
    3 6
    4 8
    8 9
    9 4
    ;
run;

基于这种假设,我的想法是:

  1. 生成行指示器,例如Ord +1;
  2. 使用连接条件为a.Pid = b.Cid and a.Ord > b.Ord的左联接,其中a和b都为test
  3. 比较新数据集和旧数据集;
  4. 第2和第3个循环,而新数据集与旧数据集不同;

好吧,有时候我们可能更关心结果而不是路径,所以这是另一个答案:

data _null_;
    set test nobs = nobs;

    do i = 1 to nobs;
        set test(rename=(Pid=PidTmp Cid=CidTmp)) point = i;
        if Cid = PidTmp then Cid = CidTmp;
    end;
    put (Pid Cid)(=);
run;

结果:

Pid=1 Cid=6
Pid=2 Cid=6
Pid=2 Cid=5
Pid=3 Cid=6
Pid=4 Cid=4
Pid=8 Cid=4
Pid=9 Cid=4

答案 1 :(得分:0)

我尝试了以下方法,但是结果并不完美

data want;
  obs1 = 1; 
  do i=1 to 6;
    set ar ;
    obs2 = obs1 + 1;
    set
      ar(
        rename=(
        pid = pid2 
        cid = cid2
        )
      ) point=obs2
    ;
       if cid =pid2
    then k=catx("",pid,cid,cid2);
    else k=catx("",pid,cid);
    output; 
    obs1 + 1; 

  end; 

run;

结果:

pid cid k
1   2   1 2 3
2   3   2 3
2   5   2 5
3   6   3 6
4   8   4 8 9
8   9   8 9 4
9   4   9 4

答案 2 :(得分:0)

链接包含有向图,需要递归才能遍历路径。

在数据步骤中,可以将父级的多个子级存储在哈希哈希结构中,但是数据步骤中的递归相当麻烦(您必须在另一个哈希中手动维护自己的堆栈和局部变量)< / p>

Proc DS2中,递归更为传统和明显,并且Package Hash可用。但是,Package Hash哈希与数据步骤不同。数据值仅允许为标量,因此哈希散列不可用:(。

可以通过将散列设置为具有multidata来弥补散列的散列不足。密钥(父项)的每个数据(子项)都以模式find进行检索,并以has_next循环访问find_next

DS2中散列的另一个问题是它们必须是data步骤的全局变量,并且对于用于键和数据的任何主机变量也必须相同。这使得在递归过程中难以管理变量。作用域深度N的代码不能依赖于可以在作用域深度N + 1更改的全局变量。

幸运的是,可以在任何范围内创建匿名哈希,并且其引用在本地维护...但是键和数据变量仍必须是全局变量。因此需要更仔细的关注。

匿名哈希用于存储键检索的多数据;这是必要的,因为递归会影响has_next get_next操作。

示例代码。需要一个rownum变量,以防止允许子级在上一行中充当父级时发生循环。

data have; rownum + 1;input
Pid       cid;datalines;
1          2
2          3
2          5
3          6
4          8
5          12
6          2
8          9
9          4
12         1
12         2
12         14
13         15
14         20
14         21
14         21
15         1
run;

proc delete data=paths;
proc delete data=rows;

%let trace=;

proc ds2 libs=work;
data _null_ ;
  declare double rownum pid cid id step pathid;
  declare int hIndex;

  declare package hash rows();
  declare package hash links();
  declare package hash path();
  declare package hash paths();

  method leaf(int _rootRow, int _step);
    declare double _idLast _idLeaf;

&trace. put ' ';
&trace. put 'LEAF';
&trace. put ' ';
    * no children, at a leaf -- output path;
    rownum = _rootRow;
    if _step < 2 then return;

    * check if same as last one;

    do step = 0 to _step;
      paths.find();  _idLast = id;
      path.find();   _idLeaf = id;
      if _idLast ne _idLeaf then leave;
    end;

    if _idLast = _idLeaf then return;

    pathid + 1;

    do step = 0 to _step;
      path.find();
      paths.add();
    end;
  end;

  method saveStep(int _step, int _id);
&trace. put 'PATH UPDATE' _step ',' _id '               <-------';
    step = _step;
    id = _id;
    path.replace();
  end;

  method descend(int _rootRow, int _fromRow, int _id, int _step);
    declare package hash h;
    declare double _hIndex;
    declare varchar(20) p;

    if _step > 10 then return;

    p = repeat (' ', _step-1);
&trace. put p 'DESCEND:' _rootRow= _fromRow= _id= _step=;

    * given _id as parent, track in path and descend by child(ren);

    * find links to children;
    pid = _id;
&trace. put p 'PARENT KEY:' pid=;
    if links.find() ne 0 then do;
&trace. put p 'NO KEY';
      saveStep(_step, _id);
      leaf(_rootRow, _step);
      return; 
    end;

    * convert multidata to hash, emulating hash of hash;
    * if not, has_next / find_next multidata traversal would be
    * corrupted by a find in the recursive use of descent;

        * new hash reference in local variable;
        h = _new_ hash ([hindex], [cid rownum], 0,'','ascending');

        hIndex = 1;

&trace. put p 'CHILD' hIndex= cid= rownum=;
        if rownum > _fromRow then h.add();

        do while (links.has_next() = 0);
          hIndex + 1;
          links.find_next();

&trace. put p 'CHILD' hIndex= cid= rownum=;
          if rownum > _fromRow then h.add();
        end;

    if h.num_items = 0 then do;
      * no eligble (forward rowed) children links;
&trace. put p 'NO FORWARD CHILDREN';
      leaf(_rootRow, _step-1);
      return;
    end;

    * update data for path step;
    saveStep (_step, _id);

    * traverse hash that was from multidata;
    * locally instantiated hash is protected from meddling outside current scope;
    * hIndex is local variable;
    do _hIndex = 1 to hIndex;
      hIndex = _hIndex;
      h.find();

&trace. put p 'TRAVERSE:' hIndex= cid= rownum= ;

      descend(_rootRow, rownum, cid, _step+1);
    end;

&trace. put p 'TRAVERSE DONE:' _step=;
  end;

  method init(); 
    declare int index;

    * data keyed by rownum;
    rows.keys([rownum]);
    rows.data([rownum pid cid]);
    rows.ordered('A');
    rows.defineDone();

    * multidata keyed by pid;
    links.keys([pid]);
    links.data([cid rownum]);
    links.multidata('yes');
    links.defineDone();

    * recursively discovered ids of path;
    path.keys([step]);
    path.data([step id]);
    path.ordered('A');
    path.defineDone();

    * paths discovered;
    paths.keys([pathid step]);
    paths.data([pathid step id]);
    paths.ordered('A');
    paths.defineDone();
  end;

  method run();
    set have;
    rows.add();
    links.add();
  end;

  method term();
    declare package hiter rowsiter('rows');
    declare int n;

    do while (rowsiter.next() = 0);
      step = 0;
      saveStep (step, pid);
      descend (rownum, rownum, cid, step+1);
    end;

    paths.output('paths');
    rows.output('rows');
  end;
run;
quit;

proc transpose data=paths prefix=ID_ out=paths_across(drop=_name_);
  by pathid;
  id step;
  var id;
  format id_: 4.;
run;

答案 3 :(得分:0)

信誉不够,所以这是另一个答案,哈哈哈。
首先,尽管我还不能熟练地使用ds2和hash,但我可以看到@Richard的答案非常好。这是学习递归的好例子。
因此,现在我知道您的目标肯定是路径而不是终点,然后在重复每个观察值的同时存储每个结果是必要的。您自己的答案已反映出这一点,但是do_loop失败了,obs1 = 1obs2 = obs1 + 1obs1 + 1总是会返回obs2 = _N_ + 1,这只会导致一次循环。
这次我对原始代码进行了补充和改进:

data test;
    set test nobs = nobs;

    array Rst[*] Cid Cid1-Cid10;
    do i = _N_ to nobs;
        set test(rename=(Pid=PidTmp Cid=CidTmp)) point = i;
        do j = 1 to dim(Rst);
            if Rst[j] = PidTmp then Rst[j+1] = CidTmp;
        end;
    end;
run;

我使用超大数组来存储路径,并将do i = 1 to nobs;更改为do i = _N_ to nobs;,因为我发现do i = 1 to nobs;会导致循环回溯。

答案 4 :(得分:0)

proc ds2;
data _null_;
    declare int t1[7];
    declare int t2[7];
    declare varchar(100) lst;

    method f2(int i, int Y);
        do while (y ^= t1[i] and i < dim(t1));
            i+1;
        end;
        if y = t1[i] then do; 
           lst = cat(lst,t2[i]);
           f2(i, t2[i]);  
        end;
    end;

    method f1(int n, int x, int y);
        dcl int i;
        dcl int match;
        match=0;
        do i = n to dim(t1);
            lst = cat(x,y); 
            if (y = t1[i]) then do;
               f2(i,y);
               put lst=;
               match = 1;
            end;
        end;
        if ^match then put lst=;
    end;

    method init();
    dcl int i;
        t1 := (1 2 2 3 4 8 9);
        t2 := (2 3 5 6 8 9 4);
        do i = 1 to dim(t1);
           f1(i, t1[i], t2[i]);
        end;
    end;
enddata;
run;
quit;`enter code here`