Сводная таблица в SAS

Данные выглядят следующим образом:

YEAR    MONTH   ID1 ID2 FIELD   FIELD_DESC
2017    4   123 2222    FFF red1
2017    4   123 2222    FFG red2
2017    4   224 2221    XYZ green1
2017    4   225 1234    TYU blue5

и желаемый результат

YEAR    MONTH   ID1 ID2 blue5   green1  red1    red2
2017    4   123 2222    0   0   1   1
2017    4   224 2221    0   1   0   0
2017    4   225 1234    1   0   0   0

На сервере SQL я запускал следующее:

select year, month, id1, id2, [field1], [field2] .... [fieldn]
from (select year, month, id1, id2, field, field_desc from source_table) P
Pivot (count(field) for field_desc in ([field1], [field2] .... [fieldn])) Pvt

Вышеприведенное дало бы мне количество полей каждого возможного описания по годам, месяцам, id1, id2.

Я пытаюсь перевести это на SAS.

Одним из подходов были бы условные суммы

proc sql;
create table aggr_table as 
select year, month, id1, id2,
sum(case when field_desc = 'field1' then 1 else 0 end) as field1
...
sum(case when field_desc = 'fieldn' then 1 else 0 end) as fiendn
from source_table 
group by year, month, id1, id2;
quit;

Второй способ, которым я пытался это сделать, заключался в

proc sort data = source_table
 by year descending month descending id1 descending id2;
run;

data table_aggr (keep year month id1 id2 field1 .... fieldn);
set source_table
retain field1
....
fieldn 0);

if field_desc = 'field1' then do;
field1 = field1 +1;
end;
....
if field_desc = 'fieldn' then do;
fieldn = fieldn + 1;
end;

if last.id2 then 
output;
by year month id1 id2;
run;

Но второй способ не работает

ОШИБКА: переменные BY неправильно отсортированы в наборе данных WORK.SOURCE_TABLE

Мои вопросы: 1) Мой поиск в Google до сих пор, кажется, указывает на предпочтение выполнения такого рода манипуляций с данными на этапе данных, а не на шаге proc sql, есть ли особое преимущество?

2) Что я сделал не так на шаге данных?

3) Есть ли лучший способ воспроизвести код SQL в верхней части сообщения? Оба варианта SAS, на которые я смотрю, выглядят довольно громоздкими по сравнению с оригиналом SQL.

Спасибо, Бен.


person Ben    schedule 05.06.2017    source источник
comment
Трудно понять, что вы пытаетесь сделать без каких-либо данных. Но я подозреваю, что вам действительно нужны proc означает/транспонировать или табулировать proc здесь. Если вы опубликуете данные до и после, это поможет.   -  person Reeza    schedule 05.06.2017
comment
В вашем коде отсутствует несколько точек с запятой, например, на шаге PROC SORT. Получали ли вы ошибки по этому поводу до того, как опубликовали сообщение об ошибке?   -  person Tom    schedule 05.06.2017


Ответы (2)


Вы можете использовать PROC SUMMARY для подсчета, а затем использовать PROC TRANSPOSE, чтобы превратить подсчеты в переменные вместо наблюдений. Если вы просто сделаете это напрямую, вы получите пропущенные значения вместо нуля для комбинаций, которые не отображаются. Вы можете выполнить постобработку файла, чтобы заменить отсутствующие значения нулями. Или используйте описанный ниже метод, чтобы построить таблицу CLASSDATA для подачи в PROC SUMMARY, чтобы гарантировать, что все нули включены перед транспонированием.

proc sql noprint ;
 create table classdata as
 select *
 from (select distinct year,month,id1,id2 from have) a
    , (select distinct field_desc from have) b
 ;
quit;
proc summary data=have nway classdata=classdata exclusive ;
  class year month id1 id2 field_desc ;
  output out=counts ;
run;
proc transpose data=counts out=want(drop=_name_);
  by year month id1 id2 ;
  id field_desc ;
  var _freq_;
run;

Вы можете заставить PROC SQL генерировать полные подсчеты рангов напрямую с помощью небольшого количества хитрого SQL. Объедините данные с полным списком значений переменной id и подсчитайте, сколько раз совпадают две переменные id.

proc sql noprint ;
 create table counts as
 select year,month,id1,id2
      , b.field_desc
      , sum(a.field_desc=b.field_desc) as count
 from have a
    , (select distinct field_desc from have) b
 group by year,month,id1,id2,b.field_desc
 order by year,month,id1,id2,b.field_desc
 ;
quit;

Сгенерировать SQL легко. Если количество производных переменных невелико, просто сгенерируйте код в макропеременную. (Если список большой, сгенерируйте код, используя шаг данных, используя либо call execute(), либо записав его в файл и используя %include для его запуска.)

proc sql noprint ;
 select distinct
 catx(' '
     ,'sum(field_desc ='
     ,quote(trim(field_desc))
     ,') as'
     ,nliteral(field_desc)
     )
   into :code separated by ','
   from have
 ;
 create table want as
   select year, month, id1, id2
        , &code
   from have
   group by year, month, id1, id2
  ;
quit;

Если вы хотите сделать это на этапе данных, рассмотрите возможность использования объекта HASH для сбора этих данных. Затем используйте PROC TRANSPOSE или метод генерации кода (как в приведенном выше SQL), чтобы преобразовать наблюдения в переменные.

person Tom    schedule 05.06.2017
comment
Да, это сделало это точно. Спасибо. - person Ben; 06.06.2017

Именно для этого SAS предлагает PROC.

Оператор CLASS в большинстве процессов (в частности, proc means, proc tabulate) позволяет суммировать данные по различным уровням. Так, например:

proc means data=sashelp.class;
  var height weight;  *the numeric variables you are calculating with;
  class age sex;      *the grouping variables;
  types () age sex age*sex;   *the interactions you want - or use NWAY or WAYS;
run;

Or:

proc tabulate data=sashelp.class;
  var height weight;   *numeric variables to calculate with;
  class age sex;       *grouping variables;
  tables (all age sex age*sex),(height weight)*n;
run;

В зависимости от процедуры у вас есть несколько способов получить эту информацию. У многих есть опция out или оператор output (например, PROC MEANS имеет оператор OUTPUT для этого). Кроме того, ODS OUTPUT позволяет получить доступ ко всему, что выводится на экран в виде таблицы.

ods output table=want;
proc tabulate data=sashelp.class;
  var height weight;
  class age sex;
  tables (all age sex age*sex),(height weight)*n;
run;
ods output close;

--or--

proc tabulate data=sashelp.class out=want;
  var height weight;
  class age sex;
  tables (all age sex age*sex),(height weight)*n;
run;
person Joe    schedule 05.06.2017
comment
Спасибо. Мне нужны результаты в таблице, которые будут использоваться на следующем этапе сценария. Я не уверен, как получить доступ к выходным данным proc tabulate. - person Ben; 05.06.2017
comment
Все еще не могу заставить его работать. Табуле близко, но нет сигары. Год, Месяц, ID1, ID2 отображаются в разных строках, а не в одной строке, как это необходимо. - person Ben; 05.06.2017
comment
PROC TRANSPOSE исправит это для вас (как показывает Том). - person Joe; 06.06.2017