SAS - транспонировать все столбцы по уникальным записям в каждом столбце с количеством

Я пытаюсь найти эффективный, похожий на транспонирование код, который может преобразовать приведенную ниже таблицу тестов в финальную таблицу. У меня есть код ниже, который работает для этого примера, но для практических целей шаг разделения данных может привести к очень глубокой таблице (137 миллионов записей в моем случае, против 2k как агрегированных).

Я надеялся, что был какой-то трюк с Proc или Data, который я пропустил, который мог пропустить этот промежуточный шаг и быть более эффективным.

Data test;
    Input f1 $ f2 $ f3 $ f4 $ f5 $ f6 $ f7 $ f8 $;

    DataLines;
a c f h k l o q
a c f h k l o q
a c g h k m o q
b c g h k m o q
b d g i k m o r
b d g i k n o r
b e g j k n o s
b e g j k n p s
    ;

Run;

Data final;
    Input field $ values $ records;

    DataLines;
f1 a 3
f1 b 5
f2 c 4
f2 d 2
f2 e 2
f3 f 2
f3 g 6
f4 h 4
f4 i 2
f4 j 2
f5 k 8
f6 l 2
f6 m 3
f6 n 3
f7 o 7
f7 p 1
f8 q 4
f8 r 2
f8 s 2
    ;

Run;


/*Working solution - could it be done more efficiently?*/
Data split;
    Set test;

    Array f{8} f1-f8;
    Do i=1 To 8;
        field = 'f'||PUT(i,best2.);
        values = f{i};
        Output;
    End;

    Drop i f1-f8;
Run;


Proc SQL;
    Create Table final As
    Select
        field
        ,values
        ,COUNT(*) As records Format=comma8.0
    From split
    Group By 1,2
    Order By 1,2
    ;
Quit;

3 ответа

Решение

Massar:

Подсчет частоты может быть выполнен в хеш-объекте шага DATA, который использует suminc: а также keysum: функции:

Data have;
    Input f1 $ f2 $ f3 $ f4 $ f5 $ f6 $ f7 $ f8 $;

    DataLines;
a c f h k l o q
a c f h k l o q
a c g h k m o q
b c g h k m o q
b d g i k m o r
b d g i k n o r
b e g j k n o s
b e g j k n p s
    ;

Run;

data want(keep=field value count);
  length field $32 value $8 count 8;
  one = 1;
  call missing (field, value, count);

  declare hash freq(suminc:'one', keysum:'count', ordered:'a', hashexp:20);
  freq.defineKey('field', 'value');
  freq.defineDone();

  do while ( not end );
    set have end=end;
    array f f1-f8;
    do over f; field = vname(f); value=f; freq.ref(); end;
  end;

  declare hiter iter("freq");
  rc = iter.first();
  do while(rc = 0);
    rc = freq.sum(sum: count);
    output;
    rc = iter.next();
  end;

  stop;
run;

Решение, которое, вероятно, наиболее эффективно по времени, заключается в использовании процедуры суммирования. PROC TABULATE кажется, лучше всего подходит здесь.

proc tabulate data=test out=test_c;
  class f1-f8;
  tables (f1-f8),n;
run;
data want;
  set test_c;
  array f f1-f8;
  col = cats(of f[*]);
  which_f = whichc(col,of f[*]);
  var = vname(f[which_f]);
  keep col var n;
run;

Этот последний шаг данных очень "дешевый", так как он выполняется только для суммарного набора данных, то есть для набора данных размером 2 КБ. Пока у вас нет большого количества столбцов, это должно быть довольно просто.

Если у вас также есть огромное количество столбцов, то решение на основе шагов данных, которое вы представили выше, вероятно, будет лучшим. Вы можете значительно ускорить его, сделав промежуточный (разделенный) шаг данных в представление.

Достаточно легко сделать с PROC SUMMARY, Используйте свой список переменных как CLASS переменные. Вы можете использовать WAYS заявление, чтобы ограничить его односторонними классификационными группами. Вы можете использовать CHARTYPE возможность упростить преобразование результатов. Вы, вероятно, также захотите добавить MISSING возможность запретить сводку процедур, исключая входные наблюдения, в которых отсутствует какая-либо из переменных.

%let varlist=f1-f8 ;

proc summary data=test chartype missing ;
 class &varlist ;
 ways 1 ;
 output out=result ;
run;

data want ;
  set result ;
  length field $32 value $8 count 8 ;
  array fields &varlist ;
  index=indexc(_type_,'1');
  field=vname(fields(index));
  value=fields(index);
  count=_freq_;
  keep field value count;
run;

Возможно, вы захотите изменить порядок переменных, чтобы они вышли в исходном порядке. Так что используйте F8-F1 вместо F1-F8,

Обратите внимание, что все переменные должны быть одного типа из-за оператора ARRAY на шаге данных.

Другие вопросы по тегам