在多个SAS数据步骤中生成可重现的随机数

时间:2016-12-14 15:46:08

标签: sas

我正在努力找出使用多个SAS数据步骤可重复生成随机数的最佳方法。

在一个数据步骤中执行此操作是直接的:只需在数据步骤开始时使用CALL STREAMINIT。

然而,如果我然后使用第二个数据步骤,我无法找出继续随机数序列的任何方法。如果我在第二个数据步骤中根本不使用CALL STREAMINIT,那么第二个数据步骤中的随机数是不可再现的。如果我使用相同种子的CALL STREAMINIT,我会得到与第一个数据步骤中相同的随机数。

我能想到的唯一想法是在每个数据步骤中使用带有不同种子的CALL STREAMINIT。从某种方面来说,这似乎不如仅仅使用从第一个数据步骤开始的一个长随机数序列。

所以例如我可以这样做:

%macro myrandom;
  %do i = 1 %to 10;
    data dataset&i;
      call streaminit(&i);
      [do stuff involving random numbers]
    run;
  %end;
%mend;

但不知何故,使用可预测的种子序列似乎是作弊。我应该担心吗?这实际上是一种完全可以接受的方式,还是有更好的方法?

3 个答案:

答案 0 :(得分:1)

以下是我的尝试:

%macro dataset_rand(_num,_rows);

    data dataset;
        do i = 0 to &_rows - 1;
            call streaminit(123);
            c = rand("UNIFORM");
            varnum = mod(i,&_num.) +1;
            output;
        end;
    run;

    data %do i = 1 %to &_num.;
        dataset&i.
        %end;
        ;
        set dataset;

        %do j = 1 %to &_num;
            if varnum = &j. then
                output dataset&j.;
        %end;
    run;

%mend;

%dataset_rand(10,100);

在这里,我运行了一步,用一个随机变量和另一个用于将其分配给数据集的变量创建每一行。

输入是_num和_rows,它允许您选择总行数和表数,因此示例(10,100)创建10个10行的表。数据集1保存随机序列的第1个,第11个...第91个成员。

那就是说我不知道​​为什么有10个种子的10个数据集会比1个数据集更好或更差,1个种子分成10个。

答案 1 :(得分:0)

使用RANUNI或类似的(“旧的”随机数流),您可以使用call ranuni来完成此操作。这允许您保存下一轮的种子,然后您可以call symputx该值到下一个datastep并重新启动相同的流。这是因为一个伪随机值的输出值是该算法中下一个种子的种子的直接变化。

然而,使用RAND,种子更复杂(在第一个数字被调用之后,它实际上不是一个值)。来自documentation

  

RAND功能以单个种子启动。但是,单个种子无法捕获进程的状态。您无法从停止点停止并重新启动发电机。

这当然是一种简化(显然SAS能够这样做,它只是没有为你打开正确的钩子,大概是因为它不像call ranuni那么简单)。

可以做的是使用宏语言,具体取决于你想要做什么。使用%syscall%sysfunc,您可以获得跨数据步骤的单个流。

然而,有一点需要注意:它看起来不像重置它。来自documentation on Seed Values

  

当使用%SYSFUNC通过宏语言调用RANUNI函数时,会创建一个伪随机数流。除非关闭SAS并启动新的SAS会话,否则无法更改种子值。 %SYSFUNC宏产生与为第一次宏调用生成数据集A,B和C的DATA步骤相同的伪随机数流。任何后续的宏调用都会产生单个流的延续。

这是ranuni系列特有的,但对于rand系列来说也是如此。

所以,启动一个新的SAS会话,运行它:

%macro get_rands(seed=0, n=, var=, randtype=Uniform, randargs=);
  %local i;
  %syscall streaminit(seed);
  %do i = 1 %to &n;
    &var. = %sysfunc(rand(&randtype. &randargs.));
    output;
  %end;
%mend get_rands;

data first;
  %get_rands(seed=7,n=10,var=x);
run;

data second;
  %get_rands(n=10,var=x);
run;

data whole;
  call streaminit(7);
  do _i = 1 to 20;
    x = rand('Uniform');
    output;
  end;
run;

但是不要错误地在一个会话中运行它两次。

否则,最好的办法是生成一次随机数,然后在多个数据步骤中使用它们。如果您使用BY组,则可以通过这种方式轻松管理。如果您对如何以这种方式实施 项目有具体问题,请在新问题中告知我们。

答案 2 :(得分:0)

不确定它是否更容易,但您可以使用STREAM子例程从同一个初始种子生成多个独立的流。下面是一个示例,稍微修改了CALL STREAM上的the doc

%macro RepeatRand(N=, Repl=, seed=);
    %do k = 1 %to &Repl;
       data dataset&k;
          call streaminit('PCG', &seed);
          call stream(&k);
          do i = 1 to &N;
             u = rand("uniform");
             output;
          end;
       run;
    %end;
%mend;

%RepeatRand(N=8, Repl=2, seed=36457);