SAS中嵌套dow-loop的解释

Alp*_*lph 2 loops sas

我想知道下面的伪代码是如何工作的?如果有一个有效的例子,我很感激。

data want;
  do until (last.var1);
    do until (last.var2);
      set have;

      * other sas statements;

    end;
 end;
run;
Run Code Online (Sandbox Code Playgroud)

Joe*_*Joe 7

基本上,单个 DoW 循环可让您在每个循环后按可变边界执行特定操作,并且与正常数据步骤的时序略有不同(这可能有用,也可能无用)。所以给定这个集合::

data have;
  input x y z;
  datalines;
1 1 1
1 1 2
1 2 1
1 2 2
2 1 1
2 1 2
2 2 1
2 2 2
;;;;
run;
Run Code Online (Sandbox Code Playgroud)

这是正常的数据步骤:

data want;
  set have;
  by x;
  if first.x then do;
    put "First value of " x=;
  end;
  put _all_;
  if last.x then do;
    put "Last value of " x=;
  end;
run;
Run Code Online (Sandbox Code Playgroud)

这是 DoW:

data want_dow;
  put "First value of " x=;
  do _n_ = 1 by 1 until (last.x);
    set have;
    by x;
    put _all_;
  end;
  put "Last value of " x=;
run;
Run Code Online (Sandbox Code Playgroud)

请注意,它的结果略有不同 - 对于第一次迭代和最后一次迭代,它输出不同的行。那是因为 SAS 在第一种方法中自动为我们做所有这些事情,而 DoW 循环你必须自己做(你必须OUTPUT在那里放一条语句,例如,如果你想要全部 8,你必须测试EOF and如果为真则STOP`)。

但也许这就是你想要的——你想要没有价值,然后你想做点什么。这就是 DoW 循环有用的时候。

嵌套的 DoW 循环是相同的,只是有两个不同的点可以采取行动。请注意,它实际上并没有改变读取行的方式:每次遇到该set语句时,都会从数据集中读取下一行(无论该行是什么)。同样的顺序,只是你有更多的停止点让你编写代码。

data want;
  set have;
  by x y;
  if first.x then do;
    put "First value of " x=;
  end;
  if first.y then do;
    put "First value of " y=;
  end;
  put _all_;
  if last.y then do;
    put "Last value of " y=;
  end;
  if last.x then do;
    put "Last value of " x=;
  end;

run;

data want_dow;
  put "First value of " x=;
  do _n_ = 1 by 1 until (last.x);
    put "First value of " y=;
    do _n_ = 1 by 1 until (last.y);
      set have;
      by x y;
      put _all_;
    end;
    put "Last value of " y=;
  end;
  put "Last value of " x=;
run;
Run Code Online (Sandbox Code Playgroud)

同样,您在这里有不同之处,因为 DoW 循环“首先”在读取第一行之前采取行动 - 这又可能有帮助,也可能没有帮助,具体取决于您的用例。我不认为我曾经有过这样的用例,但这当然不是不可能的。

这是一个有用的案例,例如,您基本上是PROC MEANS手工制作的。当然,这两种方式都可以做到;有些人会更喜欢每一个。

data want_dow;    
  do _n_ = 1 by 1 until (last.x);
    do _n_ = 1 by 1 until (last.y);
      set have;
      by x y;
      z_sum_y = sum(z_sum_y,z);
      z_sum_x = sum(z_sum_x,z);
    end;
    z_sum = z_sum_y;
    output;
    call missing(z_sum_y);
  end;
  call missing(y);
  z_sum = z_sum_x;
  output;
  drop z_sum_y z_sum_x;
run;

data want;
  set have;
  by x y;
  z_sum_y+z;
  z_sum_x+z;
  if last.y then do;
    z_sum = z_sum_y;
    output;
    z_sum_y=0;
  end;
  if last.x then do;
    z_sum = z_sum_x;
    call missing(y);
    output;
    z_sum_x=0;
  end;
  drop z_sum_y z_sum_x;
run;
Run Code Online (Sandbox Code Playgroud)

不过,大多数情况下,DoW 循环对 Double DoW 循环最有用,这对于汇总然后在同一数据步迭代中读取汇总值很有用。这是相同的汇总,但允许您查看当前行上的值。如果您想查看差异,请将 have 中的 Z 值更改为其他值(我有意将它们设置为模式中的 1/2,以便更容易检查)。

data want_ddow;
  array z_sum_ys[2] _temporary_;
  do _n_ = 1 by 1 until (last.x);
    do _n_ = 1 by 1 until (last.y);
      set have;
      by x y;
      z_sum_ys[y] = sum(z_sum_ys[y],z);
      z_sum_x = sum(z_sum_x,z);
    end;
  end;

  do _n_ = 1 by 1 until (last.x);  *do not need nesting here;
    set have;
    by x y;
    z_sum_y = z_sum_ys[y];
    output;
  end;
  call missing(of z_sum_ys[*] z_sum_x);
run;
Run Code Online (Sandbox Code Playgroud)

要在没有 Double DoW 循环的情况下做到这一点,您必须将第一个的结果合并wanthave. 这不一定是一个大问题,但它是对数据的第二次传递;Double DoW 循环利用缓冲来避免实际重新执行第二次读入的 I/O。