【问题标题】:Filling in missing values with forward-backward method with lag in SAS在 SAS 中使用具有滞后的前向后向方法填充缺失值
【发布时间】:2017-08-15 03:42:02
【问题描述】:

假设您有一个表,其中包含用户名、计数器和每个计数器的分数。

data have;
input user $  counter  score;
cards;
A 1 .
A 2 .
A 3 40
A 4 .
A 5 20
A 6 .
B 1 30
B 2 .
C 1 .
C 2 .
C 3 .
;
run;

某些计数器之间缺少某些分数,您想输入与前一个计数器相同的分数。所以结果将如下所示:

A 1 40
A 2 40
A 3 40
A 4 40
A 5 20
A 6 20
B 1 30
B 2 30
C 1 .
C 2 .
C 3 .

我设法使用下面的lag 函数来填补缺失的分数值:

data result1a;
  set have(keep=user);
  by user;

  *Look ahead;
    merge have have(firstobs=2 keep=score rename=(score=_NextScore));

    if first.user then do;
        if score= . then score=_NextScore;
        end;
    else do;
        _PrevScore = lag(score);
        if score= . then score=_PrevScore;
    end;
    output;
run;

然后我在counter 上使用descending 函数将表格向后排序,如下所示:

proc sort data = result1a out= result1b; 
by user descending counter ;
run;

最后,我将通过再次使用lag 函数在 raarranged 表中向前填充缺失值(根据初始表向后移动),如下所示。

我在do-loop中使用了lag函数,因为我想在每一步更新之前的值(例如,值40会从组中的第一个分数一直到最后一个分数)。

但是,我得到了奇怪的结果。所有缺失值都没有真正的价值。关于修复最后一个数据步骤的任何想法?

data result1c;
set result1b;
by user;

   if first.user then do;
        if score= . then score=_NextScore;
        else score = score;

        end;
   else do;
        _PrevScore = lag(score);
        if score= . then 
        score=_PrevScore;
        else score = score;
   end;
   output;
run;

【问题讨论】:

    标签: sas lag missing-data


    【解决方案1】:

    不需要使用lag,使用retain(或等效项)。这是一个双 DoW 循环解决方案,它在一个数据步中完成(实际上,一次读取 - 它缓冲读取,因此与单次读取一样有效)。

    首先,我们遍历数据集以获取找到的第一个分数,因此我们可以将其作为初始 prev_score 值。然后设置它,并重新循环该用户的行并输出。这里没有实际的retain,因为我自己在循环,但它类似于如果有retain prev_score;,这是一个正常的数据步骤循环。我实际上并没有 retain 它,因为我希望它在遇到新用户时丢失。

    data want;
      do _n_ = 1 by 1 until (last.user);
        set have;
        by user;
        if missing(first_score) and not missing(score) then 
          first_score = score;
    
      end;
      prev_score = first_score;
      do _n_ = 1 by 1 until (last.user);
        set have;
        by user;
        if missing(score) then
          score = prev_score;
        prev_score = score;
        output;
      end;
    run;
    

    【讨论】:

    • 这似乎也有效,但我得到了两个奇怪的结果:所有缺失值都为零(我不明白为什么)。第二个问题是前两个值(用户 = A 和计数器 1,2)没有得到 40 个值,仍然丢失(或第一个奇怪问题为零)
    • 它们不适合我,所以我不确定它们为什么适合你。
    • 哎呀!现在我懂了。我误解了你的first_score,我以为是first.score。现在它可以正常工作了:) 非常感谢
    • 啊,也许名字应该不一样,这很混乱。
    【解决方案2】:

    lag() 是一个经常被误解的函数。顾名思义,当你调用它时,SAS 会回顾上一行并抓取值,但事实并非如此。

    事实上,lag<n>() 是一个创建具有 n 个值的“队列”的函数。当您调用lag<n>(x) 时,它会将 x 的当前值推送到该队列中并从中读取前一个值(当然,推送每行只发生一次)。因此,如果您在某个条件中包含lag<n>(),则只有在满足该条件时才会进行推送。

    要解决您的问题,您需要为每一行运行 lag() 函数,并在更正分数后运行:

    data result1c;
    set result1b;
    by user;
    if first.user then do;
        if score= . then score=_NextScore;
        else score = score;
    end;
    else do;
        if score= . then 
        score=_PrevScore;
        else score = score;
    end;
    _PrevScore = lag(score);
    output;
    run;
    

    编辑:我对延迟的滥用感到困惑,并没有提出可行的替代方案。因为您正在修改分数,所以使用滞后是一个坏主意。保留将在这里工作:

    data result1c;
    set result1b;
    by user;
    retain _PrevScore;
    if first.user then do;
        if score= . then score=_NextScore;
        else score = score;
    end;
    else do;
        if score= . then 
        score=_PrevScore;
        else score = score;
    end;
    _PrevScore = score;
    output;
    run;
    

    【讨论】:

    • 您的解决方案似乎合乎逻辑,但在 output; 之前的 _PrevScore = lag(score) 只给出了两个 40 值。如果我把它放在by user; 之后,那么我会得到三个 40 值。但我需要四个 40 值。
    • 它仍然没有给出解决方案,但可能是因为我之前的步骤,如果我处理错误。正如@Joe 建议的那样,只需一个数据步骤就更容易了。谢谢...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-05-30
    • 1970-01-01
    • 2016-11-03
    • 2019-10-21
    相关资源
    最近更新 更多