我想比较下面显示的数据集中的两列
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完成此操作?
答案 0 :(得分:0)
正如评论所说,无限循环和搜索路径至少不清楚。因此,让我们从最简单的情况开始:始终从上到下搜索,然后回头看。
只需从创建数据集开始:
data test;
input Pid Cid;
cards;
1 2
2 3
2 5
3 6
4 8
8 9
9 4
;
run;
基于这种假设,我的想法是:
Ord +1
; a.Pid = b.Cid and a.Ord > b.Ord
的左联接,其中a和b都为test
; 好吧,有时候我们可能更关心结果而不是路径,所以这是另一个答案:
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 = 1
和obs2 = obs1 + 1
和obs1 + 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`