在 SAS 中旋转 table

Pivot table in 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;

但是第二种方式好像不行

ERROR: BY variables are not properly sorted on data set WORK.SOURCE_TABLE

我的问题: 1) 到目前为止,我的谷歌搜索似乎表明我更喜欢在数据步骤而不是 proc sql 步骤中进行这种数据操作,这有什么特别的优势吗?

2) 我在数据步骤中做错了什么?

3) 是否有更好的方法来复制 post 顶部的 SQL 代码?与 SQL 原始版本相比,我正在查看的两个 SAS 选项看起来都相当笨拙。

谢谢 本

这就是 SAS 提供 PROC 的目的。

大多数过程中的 CLASS 语句(特别是 proc meansproc 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;

或:

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 MEANSOUTPUT 语句来执行此操作)。此外,ODS OUTPUT 让您基本上可以访问以 table 形式打印在屏幕上的所有内容。

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;

--或--

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

您可以使用 PROC SUMMARY 为您计数,然后使用 PROC TRANSPOSE 将计数转化为变量而不是观察值。如果您直接这样做,那么对于未出现的组合,您将得到缺失值而不是零。您可以 post 处理文件以用零替换丢失的计数。或者使用下面的方法构建一个 CLASSDATA table 以提供给 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 中的那样)将观察值转换为变量。