以下に示すデータセットの2つの列を比較したい
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
最初に、最初の2つの値1と2を出力し、最初の列で2を検索します。現在の場合、対応する値を列2から出力します。3です。列1で3を検索し、存在する場合、対応する値を列2から出力します。は6です
SASを使用してこれを行うにはどうすればよいですか?
リンクは有向グラフで構成されており、パスをトラバースするには再帰が必要です。
データステップでは、親の複数の子をハッシュのハッシュ構造に格納できますが、データステップでの再帰は非常に厄介です(独自のスタックとローカル変数をさらに別のハッシュで手動で維持する必要があります)
ではProc DS2
再帰はるかに多くの伝統的な明白で、かつPackage Hash
利用可能です。ただし、Package Hash
ハッシュはデータステップとは異なります。データ値はスカラーのみが許可されているため、Hash ofHashesが出ています:(。
ハッシュのハッシュの欠如は、ハッシュをに設定することで修正できますmultidata
。キー(親)の各データ(子)はパターンfind
で取得され、ループはhas_next
、で取得されますfind_next
。
ハッシュのもう1つの問題は、ハッシュがステップに対して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;
コメントが言うように、無限のサイクルと検索パスは少なくとも明確ではありません。それでは、最も単純なケースから始めましょう。常に上から下に検索し、神経を振り返ります。
データセットの作成から始めてください。
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
私は以下を試しましたが、結果は完璧ではありません
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
評判が足りないので、これは別の答えです、ハハハ。
まず、ds2とハッシュはまだ上手く使えませんが、@ Richardの答えはとても良いことがわかります。再帰を学ぶ良い例です。
これで、あなたの目的は間違いなく終点ではなくパスであることがわかりました。各観測を繰り返しながら各結果を保存してから、必要になります。あなた自身の答えはこれを反映したが、やるループを失敗した、としている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;
ループルックバックが発生しますが。
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`
Reba McEntire が息子の Shelby Blackstock と共有しているクリスマスの伝統について学びましょう。
メーガン・マークルとマライア・キャリーが自然な髪の上でどのように結合したかについて、メーガンの「アーキタイプ」ポッドキャストのエピソードで学びましょう.
ハリー王子が家族、特にチャールズ王とウィリアム王子との関係について望んでいると主張したある情報源を発見してください。
ワイノナ・ジャッドが、母親のナオミ・ジャッドが亡くなってから初めての感謝祭のお祝いを主催しているときに、彼女が今では家長であることをどのように認識したかを学びましょう.
Air travel is far more than getting from point A to point B safely. How much do you know about the million little details that go into flying on airplanes?
The world is a huge place, yet some GeoGuessr players know locations in mere seconds. Are you one of GeoGuessr's gifted elite? Take our quiz to find out!
私たちの読者は、何千ものOxyLEDのT-02モーションセンサーライトストリップを何年にもわたって購入してきましたが、充電するのが面倒だと感じた場合、新しいT-04は素晴らしいアップグレードのように見えます。T-02のように、 T-04は、付属の粘着ストリップを介して基本的にあらゆる表面に取り付けることができ、暗闇での動きを検出すると自動的に点灯します。
写真:AP大統領ドナルド・トランプは、連邦捜査局長官のジェームズ・コミーを解雇したばかりです。火曜日の声明で、ホワイトハウスは、トランプが「両方の副検事総長ロッドの明確な勧告に基づいて行動するコミーをオフィスから削除したと述べましたローゼンスタインと司法長官のジェフセッション。
ウィキメディア・コモンズ経由西部のローマ帝国の中央機関が崩壊し、5世紀の間に州が分裂し、独自の道を進んだとき、新しい王国が出現しました。今日、私たちはこれらの新しい政治単位を特定の野蛮人グループで特定する傾向があります:ガリア南西部の西ゴート族、ガリア北部のフランク人、英国のアングロサクソン人、北アフリカのヴァンダル人。
BodyRestore ユーカリ シャワー スチーマーは、Amazon で 11,000 を超える 5 つ星の評価を得ています。セルフケアが必要な人へのバレンタインデーのギフトとして、ホームスパ製品を贈りましょう。
多くのAmazonの買い物客がUmlo H6ハンドヘルド掃除機を推奨しており、現在スーパーセール中です. ハンドヘルド デバイスには HEPA フィルターが装備されており、複数のアタッチメントが付属しています。Amazonで75%オフのときにハンドヘルド掃除機を購入する
オクタヴィア・スペンサーは、ヘルプで一緒に共演するずっと前に、シシー・スペイセク主演の 1990 年の映画「ロング・ウォーク・ホーム」でインターンとして働いていました。
ジュリア・フォックスは、彼女のTikTokで共有された応答ビデオで、「本当に申し訳ありません。今、本当に年齢を示しています」と述べました。
人々にチャンスを与えることは、人生で少し遅すぎると私は信じています。寛大に。