如何将第一个非缺失值写入第一个缺失的观察值

时间:2014-07-28 22:33:27

标签: sas

我有以下数据集

id  rate
 1     .
 2     .
 3  0.01
 4  0.02
 5     .
 6     .

如何才能有效地将第一个非遗漏率写入其之前的观测值,以及之后观测值的最后一次非遗漏率?那样:

id  rate
 1  0.01
 2  0.01
 3  0.01
 4  0.02
 5  0.02
 6  0.02

我可以填写最终值(参见下面的代码),但是我还没有找到如何做第一个的线索 - 不使用辅助表,在我的情况下可能(或不)使这个效率非常低,因为我将有数百万条记录。

data have;
    input id rate;
    datalines;
     1     .
     2     .
     3  0.01
     4  0.02
     5     .
     6     .
run;

data want(drop=previous);
    set have;
    retain previous;
    if not nmiss(rate) then previous = rate;
    else rate = previous;
run;

2 个答案:

答案 0 :(得分:3)

我不确定最有效的答案是什么。您可能只想使用您拥有的方法填充最终值,然后反向排序数据集并使用相同的方法填充其余值。然而,这是我提出的另一种方法,我不确定它的效率如何,但你可以尝试一下:

data have;
input id group rate;
datalines;
 1  1    .
 2  1    .
 3  1 0.01
 4  1 0.02
 5  1    .
 6  1    .
 7  2    .
 8  2    .
 9  2    .
10  2 0.03
11  2 0.07
12  2    .
;

data want(drop=next initial);
    retain next;
    do until (rate ne . or last.group);
        set have;
        by group id;
    end;
    if rate ne . then next = rate;
    do until (initial ne . or last.group);
        set have;
        by group id;
        initial = rate;
        if initial = . then rate = next;
        output;
    end;
run;

我添加了一个组变量,因为你说你将与群组合作。程序循环数据两次,一次获得第一个非丢失率,然后将该速率应用于缺失并输出到最终数据集。 “next”是缺失率被替换的值,“initial”是每次观察的初始速率。

对于每个组,处理过程如下:第一个“do until”循环读取数据集“have”,直到找到非丢失率。变量“next”设置为等于该速率。第二个“do until”循环读取“have”,将“rate”设置为“next”,直到找到第一个非缺失率。然后两个循环读取非丢失率,第二个循环输出它们。最后一次非遗漏率保存为“下一步”。然后循环在最后循环丢失的速率,用“next”中的值替换它们并输出它们。

答案 1 :(得分:0)

DoW解决方案是一个很好的解决方案,但有一个稍快的选项,特别是与CPU时间有关。对于哪些记录填空,这也会导致略有不同的结果,而这个问题并不完全清楚。

在DoW解决方案中,您必须迭代每一条记录两次,而您可以使用POINT迭代所需的几条记录,直到您获得包含每组数据的第一条记录,然后继续。

此外,此解决方案将使用之前非缺失记录填写后续记录,而所呈现的DoW解决方案将使用 next 非缺失记录填充它们,除非最后一条记录不见了。

对于10000组/ 1000个ID,POINT解决方案要快得多;我怀疑这是因为数据集的大小在我机器的磁盘缓存大小(CPU时间=实时)内。添加一个零,1e5组,实时不再快!它实时与DoW解决方案大致相同(2分钟内还有8或10秒),但CPU时间约为一半(Point = 35秒,DoW = 1:09)。

这个和DoW循环都需要按组排序数据,或者至少按组排序(组不必按顺序排列,你可以使用notsorted来防止数据丢失错误,但内部任何给定组中的数据都需要连续)。这不需要按ID排序,也没有(据我所知)DoW循环。

%let numgroups=1e5;
%let numingroup=1000;
data have;
do group = 1 to &numgroups.;
  do id = 1 to &numingroup.;
    if id > 5 then do;
        rate = rand('Normal',1,1);
        if rate < 0 then call missing(rate);
    end;
    output;
  end;
end;
run;


data want_Point;
 set have;
 by group;
 if (first.group) and (missing(rate)) then do; *if missing first row, enter this loop;
    do __point = _n_+1 by 1;
      set have(keep=group rate rename=(group=_group rate=new_rate)) point=__point;  *iterate through dataset from current row;
      if (not missing (new_rate) or group ne _group) then leave; *grab nonmissing row, leave loop - and have safety to leave loop if entire group is missing;
    end;
 end;
 else do;
   new_rate = coalesce(rate,new_rate);  *grab updated rate, if needed. new_rate is automatically retained since it comes in on set statement;
 end;
 rename new_rate=rate;  *move it over to rate;
 drop rate;  *drop old rate variable;
 output;
 if last.group then call missing(new_rate); *make extra sure we are safe;
run;



data want_DoW(drop=next initial);
    retain next;
    do until (rate ne . or last.group);
        set have;
        by group id;
    end;
    if rate ne . then next = rate;
    do until (initial ne . or last.group);
        set have;
        by group id;
        initial = rate;
        if initial = . then rate = next;
        output;
    end;
run;