SAS 中的分区 运行 总数

Partitioned Running Totals in SAS

如何为现有数据集创建一个新列,该数据集是 运行 个现有列的总和 - 按某个标识符分区?

ID |  Value |     New Value
---|--------|--------------------
1  |   10   |     10
1  |   5    |     15  = 10 + 5
1  |   3    |     18  = 10 + 5 + 3
2  |   45   |     45
2  |   15   |     60  = 45 + 15

我习惯于在 SQL (Oracle) 中使用简单的 SUM() OVER() 语句来完成此操作,但在 PROC SQL 中显然不支持该语法。

如果可能的话,我想在 PROC SQL 中完成这个(我对 SQL 的经验比 SAS 编码要多得多)。

谢谢!

麦克。

在数据步骤中,这是通过 sum statement 完成的。

data want;
  set have;
  by id;
  if first.id then running_total=0;
  running_Total + value;
run;

在 PROC SQL 中这是不可能的,除非你有一个排序变量(你可以在其中做这样的事情):

proc sql;
  create table want as
   select id, value,
     (select sum(value) from have V
     where V.id=H.id and V.ordervar le H.ordervar
     ) as running_total
   from have H
 ;
quit;

但是 SAS 没有 partition by 概念 - SAS 数据步骤比那个更强大。

我通常使用 data 步骤而不是 proc sql 在 SAS 中进行 运行ning 总计。理论上您可以使用 proc sql 和交叉连接来做到这一点,但这在现实世界中不太实用。

第 1 步:按您的 ID 值对数据进行排序

proc sort data=have;
     by ID;
run;

第 2 步:使用求和语句

计算 运行ning 总数
data want;
    set have;
    by ID;

    New_Value+Value;

    output;

    if(last.ID) then New_Value = 0;
 run;

说明

分组处理是 SAS 最强大的工具之一。当您按 ID 值排序并在数据步骤中对其使用 by 语句时,您解锁了两个新的特殊布尔变量:first.<by-group variable>last.<by-group variable>. SAS 确切知道一组 ID 变量何时开始和当你进行分组处理时停止(你实际上可以使用一个名为 notsorted 的特殊选项来离开而不对 ID 值进行排序,但这是一个高级概念)。对于您的情况,让我们看看它是如何工作的:

ID Value first.ID last.ID
1  10    1        0
1  5     0        0
1  3     0        1
2  45    1        0 
2  15    0        1

如果一个 ID 组中只有一个观察值,first.IDlast.ID 都会是 1。

SAS 中有一个特殊的语句称为 sum 语句,它既可以保留变量又可以对变量求和。 SAS 本质上是一种循环语言;每当它 运行 通过 data 步骤时,它只查看一条记录和一条记录。当它到达 data 步骤的底部并回到顶部时,开始读取 table 中的下一条记录,并假定所有变量现在再次丢失,直到它读取或计算变量' 价值。您真正输出的是称为程序数据向量的内容。在后台,这就是你正在操纵的东西。

默认情况下,它只有在达到run边界后才会输出到数据集。如果您明确告诉 SAS 输出,它只会在您告诉它时输出。要累积一个变量,我们要告诉 SAS 四件事:

  1. 保留变量的前一个值(读取新记录时不要将其重置为丢失)
  2. 将变量添加到自身
  3. 我给自己加值后的输出
  4. 在我输出并且它是一组ID变量中的最后一个观察值后,将我的累积变量重置为0

要实现第 1 步和第 2 步,您可以执行两个选项:

 data want;
      set have;
      retain New_Value;

      New_Value = sum(New_Value, Value);

 data want;
       set have;
       New_Value+Value;

请注意,第二个选项与第一个选项完全相同,但工作量更少。将其视为捷径。这称为求和语句。看起来它在语法上是错误的,但它是一个特殊且非常有用的案例。

为了实现第 3 步,我们只需告诉 SAS 在之后立即输出,而不是默认在数据步骤结束时输出:

data want;
    set have;

    New_Value+Value;

    output;

如果您按原样 运行 上面的代码,New_Value 将无限期地累加,直到到达文件的末尾。一旦到达新的 ID 组,我们希望重置此值。我们使用分组处理来解锁这两个布尔变量,以便在我们输出后的指定条件下将 New_Value 重置为 0:

data want;
    set have;
    by ID;

    New_Value+Value;

    output;

    if(last.ID) then New_Value = 0;
run;

New_Value 不会被重置为 0,除非我们在指定 ID 组的最后一次观察中。请注意,我们将条件语句 if 放在输出语句下方。如果在上面,你会看到以下现象:

ID Value New_Value first.ID last.ID
1  10    10         1        0
1  5     15         0        0
1  3      0         0        1
2  45    45         1        0 
2  15     0         0        1

我们要输出累计和之前New_Value重置为0。

还有一些其他 SAS 过程可用于执行类似的操作,但它们是为特定情况设计的。在这种情况下,您可以重新调整它们的用途以执行您想要的操作,但最好先学习 data 步骤处理,然后再进入过程重新调整用途。

乔 - 无论出于何种原因,你的回答都没有奏效,但让我走上了正确的轨道来弄清楚。谢谢!

data want;
    set have;
    by id;
    if first.id then running_total = 0;
    if first.id then retained_total = 0;
    running_total = retained_total + value;
    retained_total = running_total;
    retain retained_total;
run;

还有一个选项:

data want;
do until (last.id);
    set have;
    by id;
    new_value + value;
    output;
end;
new_value = 0;
run;