Поиск всех возможных путей в наборе данных с использованием sas - PullRequest
0 голосов
/ 29 марта 2019

Я хочу сравнить два столбца в наборе данных, показанном ниже

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 в столбце 1, если оно есть, выведите соответствующее значение из столбец 2, который 6

Как это можно сделать с помощью SAS?

Ответы [ 5 ]

0 голосов
/ 27 мая 2019
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`
0 голосов
/ 01 апреля 2019

Недостаточно репутации, так что это еще один ответ, хахаха.
Прежде всего, я вижу, что ответ @Richard очень хорош, хотя я пока не могу так умело использовать ds2 и hash.Это хороший пример изучения рекурсии.
Так что теперь я знаю, что ваша цель - это путь, а не конечная точка, сохраняйте каждый результат, повторяя каждое наблюдение, тогда будет необходимо.В вашем собственном ответе это отражено, но при неудачном цикле do 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;, что заставит цикл оглянуться назад.

0 голосов
/ 30 марта 2019

Я пробовал ниже, результат не идеален, хотя

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 голосов
/ 30 марта 2019

Ссылки содержат ориентированный граф и нуждаются в рекурсии для обхода путей.

На шаге данных несколько дочерних элементов родительского объекта могут храниться в структуре Hash of Hashes, но рекурсия на шаге данных довольно неудобна (вам придется вручную поддерживать свой собственный стек и локальные переменные в еще одном хэше)

В Proc DS2 рекурсия гораздо более традиционна и очевидна, и доступна Package Hash. Однако хеширование Package Hash отличается от шага данных. Значения данных могут быть только скалярами, поэтому Hash of Hashes отсутствует: (.

Отсутствие хэша хэшей можно исправить, установив для хэша значение 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;
0 голосов
/ 30 марта 2019

Как говорится в комментариях, бесконечный цикл и путь поиска, по крайней мере, не ясны. Итак, начнем с самого простого случая: всегда ищите сверху вниз и нервно оглядывайтесь назад.

Просто начните с создания набора данных:

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
...