SASを使用してデータセット内のすべての可能なパスを検索する

0
Arun Kumaar 2019-03-29 22:03.

以下に示すデータセットの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を使用してこれを行うにはどうすればよいですか?

5 answers

1
Richard 2019-03-31 06:33.

リンクは有向グラフで構成されており、パスをトラバースするには再帰が必要です。

データステップでは、親の複数の子をハッシュのハッシュ構造に格納できますが、データステップでの再帰は非常に厄介です(独自のスタックとローカル変数をさらに別のハッシュで手動で維持する必要があります)

では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;
0
whymath 2019-03-30 17:13.

コメントが言うように、無限のサイクルと検索パスは少なくとも明確ではありません。それでは、最も単純なケースから始めましょう。常に上から下に検索し、神経を振り返ります。

データセットの作成から始めてください。

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.Ordaと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
0
Arun Kumaar 2019-03-30 18:26.

私は以下を試しましたが、結果は完璧ではありません

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
0
whymath 2019-04-01 15:31.

評判が足りないので、これは別の答えです、ハハハ。
まず、ds2とハッシュはまだ上手く使えませんが、@ Richardの答えはとても良いことがわかります。再帰を学ぶ良い例です。
これで、あなたの目的は間違いなく終点ではなくパスであることがわかりました。各観測を繰り返しながら各結果を保存してから、必要になります。あなた自身の答えはこれを反映したが、やるループを失敗した、としているobs1 = 1obs2 = 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;ループルックバックが発生しますが。

0
Ptaf 2019-05-27 15:03.
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`

Related questions

MORE COOL STUFF

Reba McEntire は、彼女が息子の Shelby Blackstock と共有する「楽しい」クリスマスの伝統を明らかにしました:「私たちはたくさん笑います」

Reba McEntire は、彼女が息子の Shelby Blackstock と共有する「楽しい」クリスマスの伝統を明らかにしました:「私たちはたくさん笑います」

Reba McEntire が息子の Shelby Blackstock と共有しているクリスマスの伝統について学びましょう。

メーガン・マークルは、自然な髪のスタイリングをめぐってマライア・キャリーと結ばれました

メーガン・マークルは、自然な髪のスタイリングをめぐってマライア・キャリーと結ばれました

メーガン・マークルとマライア・キャリーが自然な髪の上でどのように結合したかについて、メーガンの「アーキタイプ」ポッドキャストのエピソードで学びましょう.

ハリー王子は家族との関係を修復できるという「希望を持っている」:「彼は父親と兄弟を愛している」

ハリー王子は家族との関係を修復できるという「希望を持っている」:「彼は父親と兄弟を愛している」

ハリー王子が家族、特にチャールズ王とウィリアム王子との関係について望んでいると主張したある情報源を発見してください。

ワイノナ・ジャッドは、パニックに陥った休暇の瞬間に、彼女がジャッド家の家長であることを認識しました

ワイノナ・ジャッドは、パニックに陥った休暇の瞬間に、彼女がジャッド家の家長であることを認識しました

ワイノナ・ジャッドが、母親のナオミ・ジャッドが亡くなってから初めての感謝祭のお祝いを主催しているときに、彼女が今では家長であることをどのように認識したかを学びましょう.

セントヘレナのジェイコブのはしごを登るのは、気弱な人向けではありません

セントヘレナのジェイコブのはしごを登るのは、気弱な人向けではありません

セント ヘレナ島のジェイコブズ ラダーは 699 段の真っ直ぐ上る階段で、頂上に到達すると証明書が発行されるほどの難易度です。

The Secrets of Airline Travel Quiz

The Secrets of Airline Travel Quiz

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?

Where in the World Are You? Take our GeoGuesser Quiz

Where in the World Are You? Take our GeoGuesser Quiz

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!

バイオニック読書はあなたをより速く読むことができますか?

バイオニック読書はあなたをより速く読むことができますか?

BionicReadingアプリの人気が爆発的に高まっています。しかし、それは本当にあなたを速読術にすることができますか?

OxyLEDの新しい15ドルのモーションセンシング常夜灯はどこにでも貼り付けられますが、充電が簡単です

OxyLEDの新しい15ドルのモーションセンシング常夜灯はどこにでも貼り付けられますが、充電が簡単です

私たちの読者は、何千ものOxyLEDのT-02モーションセンサーライトストリップを何年にもわたって購入してきましたが、充電するのが面倒だと感じた場合、新しいT-04は素晴らしいアップグレードのように見えます。T-02のように、 T-04は、付属の粘着ストリップを介して基本的にあらゆる表面に取り付けることができ、暗闇での動きを検出すると自動的に点灯します。

ハンソロ映画セットの写真は、新しいスーツと新しい乗り物を明らかにします

ハンソロ映画セットの写真は、新しいスーツと新しい乗り物を明らかにします

ユニバーサルは、フランケンシュタインの怪物を見つけるのに近いかもしれません。新しい撮影映像でアクマンの舞台裏をご覧ください。

ドナルド・トランプが解雇されたばかりのFBI長官ジェームズ・コミー

ドナルド・トランプが解雇されたばかりのFBI長官ジェームズ・コミー

写真:AP大統領ドナルド・トランプは、連邦捜査局長官のジェームズ・コミーを解雇したばかりです。火曜日の声明で、ホワイトハウスは、トランプが「両方の副検事総長ロッドの明確な勧告に基づいて行動するコミーをオフィスから削除したと述べましたローゼンスタインと司法長官のジェフセッション。

テオドリック大王は、過去の言語と政治的概念に隠された野蛮な将軍でした

テオドリック大王は、過去の言語と政治的概念に隠された野蛮な将軍でした

ウィキメディア・コモンズ経由西部のローマ帝国の中央機関が崩壊し、5世紀の間に州が分裂し、独自の道を進んだとき、新しい王国が出現しました。今日、私たちはこれらの新しい政治単位を特定の野蛮人グループで特定する傾向があります:ガリア南西部の西ゴート族、ガリア北部のフランク人、英国のアングロサクソン人、北アフリカのヴァンダル人。

バレンタインデーにユーカリのシャワースチーマーで「最高の睡眠」を贈りましょう。

バレンタインデーにユーカリのシャワースチーマーで「最高の睡眠」を贈りましょう。

BodyRestore ユーカリ シャワー スチーマーは、Amazon で 11,000 を超える 5 つ星の評価を得ています。セルフケアが必要な人へのバレンタインデーのギフトとして、ホームスパ製品を贈りましょう。

この「邪悪な吸引力」を備えたこの250ドルのハンドヘルド掃除機は、Amazonで75%オフになりました

この「邪悪な吸引力」を備えたこの250ドルのハンドヘルド掃除機は、Amazonで75%オフになりました

多くのAmazonの買い物客がUmlo H6ハンドヘルド掃除機を推奨しており、現在スーパーセール中です. ハンドヘルド デバイスには HEPA フィルターが装備されており、複数のアタッチメントが付属しています。Amazonで75%オフのときにハンドヘルド掃除機を購入する

オクタヴィア・スペンサー、「ザ・ヘルプ」共演者のシシー・スペイセクが17歳で映画のインターンをした後、彼女のことを「実際に」思い出したと語る

オクタヴィア・スペンサー、「ザ・ヘルプ」共演者のシシー・スペイセクが17歳で映画のインターンをした後、彼女のことを「実際に」思い出したと語る

オクタヴィア・スペンサーは、ヘルプで一緒に共演するずっと前に、シシー・スペイセク主演の 1990 年の映画「ロング・ウォーク・ホーム」でインターンとして働いていました。

ジュリア・フォックス、「マスカラ」がTikTokユーザーの性的暴行コードだったことを知らなかったことを謝罪

ジュリア・フォックス、「マスカラ」がTikTokユーザーの性的暴行コードだったことを知らなかったことを謝罪

ジュリア・フォックスは、彼女のTikTokで共有された応答ビデオで、「本当に申し訳ありません。今、本当に年齢を示しています」と述べました。

メリック・ガーランドはアメリカに失敗しましたか?

バイデン大統領の任期の半分以上です。メリック・ガーランドは何を待っていますか?

メリック・ガーランドはアメリカに失敗しましたか?

人々にチャンスを与えることは、人生で少し遅すぎると私は信じています。寛大に。

良いものと醜いもの: 2022

良いものと醜いもの: 2022

もうわからない。何が「ヒット」かを正確に判断することは、もはやほとんど不可能に思えます。

楽しみのために — 2022 年のトップの新しい音楽再生

楽しみのために — 2022 年のトップの新しい音楽再生

ついに!私の 2022 年のトップ ニューミュージック プレイへようこそ。私は毎年これを共有して、友達とつながります。

ヒーズ・オール・アイヴ・ガット

ヒーズ・オール・アイヴ・ガット

あなたの心をチェックしてください。私たちの心はしばしば迷います。

Language