我有以下数据集
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;
答案 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;