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

Sas 如何将第一个非缺失值写入第一个缺失的观测值,sas,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 我可以填写最终的值(见下面的代码),但我不知道如何填写第一个值,而不必求助于辅助表,在我的情况下,这可能(或不是)使这非常低效,因为我将有数

我有以下数据集

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;

我不确定什么是最有效的答案。您可能只想使用现有的方法填写最终值,然后对数据集进行反向排序,并使用相同的方法填写其余值。然而,我提出了另一种方法,我不确定它的效率有多高,但你可以试试:

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;
我添加了一个组变量,因为您说过您将按组处理。程序在数据中循环两次,一次获得第一个非丢失率,然后将该速率应用于丢失并输出到最终数据集。“下一步”是将缺失率替换为的值,“初始”是每次观察的初始率


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

道氏解决方案是一个很好的解决方案,但有一个稍微快一点的选项,特别是与CPU时间有关。这也导致了一个稍微不同的结果,即哪个记录填补了空白,这与哪个更好的问题并不完全清楚

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

此外,此解决方案将使用先前的非缺失记录填充后续记录,而道指解决方案将使用下一个非缺失记录填充后续记录,除非最后一条记录丢失

对于10000个组/1000个ID,点解决方案要快得多;我怀疑这是因为数据集的大小在我的机器的磁盘缓存大小(CPU时间=实时)之内。添加一个0,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;
%let numgroups=1e5;
%设numingroup=1000;
有数据;
do组=1到&numgroups。;
do id=1到&numingroup。;
如果id>5,则执行该操作;
费率=兰特(正常值),1,1;
如果速率<0,则呼叫丢失(速率);
结束;
产出;
结束;
结束;
跑
数据点;
集有;
分组;
如果(第一组)和(缺失(比率)),则执行*如果缺少第一行,则输入此循环;
do uu point=_n_u+1乘以1;
设置有(保持=组速率重命名=(组=_组速率=新速率))点=_点*从当前行遍历数据集;
如果(未丢失(新费率)或组ne(组),则离开;*抓住不丢失的行,离开循环-如果整个组丢失,则安全离开循环;
结束;
结束;
否则你会;
新利率=合并(利率,新利率)*如果需要,获取更新的速率。新的利率自动保留,因为它在set语句中出现;
结束;
重命名新的利率=利率*把它移到速率上;
下降率*降旧率变量;
产出;
如果是last.group,则呼叫丢失(新费率)*确保我们的安全;
跑
数据需求(下降=下一个初始值);
保留下一个;
直到(评级为ne.或last.组);
集有;
按组id;
结束;
如果速率为ne。然后next=速率;
直到(初始ne.或最后一组);
集有;
按组id;
初始=速率;
如果初始值=。然后速率=下一个;
产出;
结束;
跑

此数据集是否正确地代表了您的问题?嗯,观察结果远不止此,因此问题将是更多的变量和分组。我会说每组1000条记录,每组100万条记录。所以,10亿条记录。一个简单的解决方案是在之后按降序排序,并应用相同的过程。有没有更好的方法?对于每个小组,你最多会有一个案例填写“之前”和一些“之后”?或者这也只是一个“之后”?一些“最有效的”取决于你的数据的具体情况。我没有完全理解你的问题。它总是像上面的例子一样——缺失的观察,然后是非缺失的观察,然后是缺失的观察,没有例外。虽然它们的数量有所不同,但我已经实现了你的代码,它的工作原理完全符合我的需要。非常感谢你。至于效率,我还没有测试剩余的选项,但您的处理88050622个观察值和8个变量,以6:36.22/4:40.40(实时与cpu时间)表示,这对我的使用非常好。谢谢@Joe。我已经实现了上面的解决方案,但我会尝试你的解决方案,然后回复你。