SAS:将聚合数据添加到同一数据集

SAS: Adding aggregated data to same dataset

我正在从 SPSS 迁移到 SAS。 我需要按变量组 varA varB 分别计算变量 varX 的总和,并将其添加为新变量 sumX 到同一个数据集。

在 SPSS 中,使用 aggregate:

很容易实现
aggregate outfile *
/break varA varB
/SUMvarX = sum(varX).

这可以在 SAS 中完成吗?

好的,我想我找到了一种方法。 首先,您生成求和变量:

proc means data= <dataset> noprint nway;
    by  varA varB;
    var varX;
    output out=<TEMPdataset> sum = SUMvarX;
run;

然后合并两个数据集:

DATA <dataset>;
   MERGE <TEMPdataset> <dataset>;
   BY varA varB;
run;

这似乎可行,尽管在此过程中形成了一个额外的数据集和几个额外的变量。 可能有更有效的方法...

听说过 DoW Loop 吗?

*-- Create synthetic data --*
data have;
varA=2; varB=4; varX=21; output;
varA=4; varB=6; varX=32; output;
varA=5; varB=8; varX=83; output;
varA=4; varB=3; varX=78; output;
varA=4; varB=8; varX=72; output;
varA=2; varB=4; varX=72; output;
run;

proc sort data=have; by varA varB; quit;

varA varB varX
 2    4    21
 2    4    72
 4    3    78
 4    6    32
 4    8    72
 5    8    83
data stage1;
set have;
by varA varB;
if first.varB then group_number+1;
run;

data want;
do _n_=1 by 1 until (last.group_number);
    set stage1;
    by group_number;
    SUMvarX=sum(SUMvarX, varX);
end;

do until (last.group_number);
    set stage1;
    by group_number;
    output;
end;

drop group_number;
run;
varA varB varX SUMvarX
 2    4    21     93
 2    4    72     93
 4    3    78     78
 4    6    32     32
 4    8    72     72
 5    8    83     83

有多种方法可以做到这一点,但最佳方法取决于您的数据。

对于典型用例,PROC MEANS 解决方案是我推荐的。它不是最快的,但它可以完成工作,而且出错的机会要低得多——除了 match-merging 之后你什么都没做。

在大多数情况下使用class语句而不是by;它应该不会有太大区别,但这是 class 的目的。 by 运行s 对这些变量的每个值分别进行分析; class 运行 按所有这些变量分组的一项分析。它更灵活,不需要排序的数据集(尽管您无论如何都必须为以后的合并排序)。 class 还允许您进行多种组合 - 不仅是您在此处要求的 nway 组合,而且如果您希望它仅按 ab 和按a*b,你可以得到它(使用 classtypes)。

proc means data=have;
  class a b;
  var x;
  output out=summary sum(x)=;
run;

data want;
  merge have summary;
  by a b;
run;

Kermit 的回答中涵盖的 DoW 循环也是一个合理的数据步骤选项,尽管在程序员错误方面的风险更大;我只会在数据集非常大的特定情况下使用它 - 超过内存 摘要大小 大 - 并且性能很重要。

如果数据适合内存,您还可以使用散列 table 进行汇总,如果汇总数据集适合内存,我就会这样做。这对于这里的答案来说太长了,但是 Data Aggregation using Hash Object is a good start for how to do that. Basically, you use a hash table 存储摘要的结果(不是原始数据),将每一行添加到它,然后在最后输出散列 table。比 DoW 循环快一点,但内存受限(尽管如果您使用 SPSS,内存受限比这大得多!)。也很容易处理多种组合。

另一种“程序员简单”的方法是使用 SQL。

proc sql;
  create want as
   select *, sum(x) as sum_x
    from have
    group by a,b
  ;
quit;

这不是标准的 SQL,但 SAS 管理它 - 基本上它一步完成 proc means 和合并的两步过程。我在某些方面喜欢这个(因为它跳过了中间数据集,即使它确实在 util 文件夹中创建了这个数据集,只是自动为你清理)并且在其他方​​面不喜欢它(它不是标准 SQL 所以它会让人们感到困惑,它会在日志中留下一个注释——只是一个注释,所以没什么大不了的,但仍然如此。


添加关于 SPSS -> SAS 思想的注释。从 SPSS 到 SAS 的最大差异之一是,在 SPSS 中,您有一个数据集,并且您可以(主要是)对它做一些事情。您可以将其保存为不同的数据集,但大多数情况下直到最后才这样做——您的所有工作实际上只是在内存中编辑一个数据集。

在 SAS 中,您从磁盘读取数据集并执行一些操作,然后将它们写出,如果您在数据集级别执行任何操作(如摘要),您通常会单独执行然后重新组合在后面的步骤中使用数据。因此,拥有大量数据集是非常非常普遍的——我只是 运行 的一个程序可能有 1000 个。不开玩笑!不要担心正在生成 运行dom 临时数据集 - 这并不意味着您的代码效率低下。这就是 SAS 的工作原理。有时您确实需要小心——比如您有 150GB 的数据集或其他东西——但如果您使用 5000 行和 150 个变量,您的数据集会非常小,您可以将其写入一千次而不会注意到有意义的与您的代码执行时间的差异。

这种风格的最大好处是每个步骤都有不同的数据集,因此如果您返回并想要重新运行 部分代码,您可以安全地知道前任数据集仍然存在,无需重新运行 所有 代码。它还可以让您真正轻松地进行调试,因为您可以看到每个组件。

这当然是一种权衡,因为它确实意味着 运行 代码需要更长的时间,但在现代,CPU 确实非常快,SSD 也是如此 - 只是没有必要编写的代码全部保留在一个数据步骤中,或者 运行s 完全保留在内存中。权衡是你有能力做大量不可能适合内存的事情,处理大量数据集等 - 仅受 disk 的限制,这通常是在更大的供应。在许多情况下,这是一个值得做出的权衡。当可以在 PROC 中做某事时,就去做,即使这意味着它最终会花费一点点时间 re-merge 它 - PROC 是您向 SAS 支付大笔费用的原因,他们易于使用,经过良好测试,而且速度很快。