SAS:用最近邻居的平均值替换缺失值

时间:2017-02-07 04:51:56

标签: replace sas average

我试图找到一种快速方法,用两个最接近的非缺失值的平均值替换缺失值。例如:

Id Amount
1   10
2   .
3   20
4   30 
5   .
6   .
7   40

期望的输出

Id Amount
1   10
2   **15**
3   20
4   30 
5   **35**
6   **35**
7   40

有什么建议吗?我尝试使用retain函数,但我只能弄清楚如何保留最后一个非缺失值。

3 个答案:

答案 0 :(得分:2)

我认为你在寻找的东西可能更像是插值。虽然这不是两个最接近的值的平均值,但它可能很有用。

在数据集中插入一个名为 proc expand 的小工具。 (它也应该进行外推,但我还没有尝试过。)在制作一系列日期和累积计算时非常方便。

data have;
input Id Amount;
datalines;
    1   10
    2   .
    3   20
    4   30 
    5   .
    6   .
    7   40
    ;
run;

proc expand data=have out=Expanded;
    convert amount=amount_expanded / method=join;
    id id; /*second is column name */
run;

有关proc扩展的更多信息,请参阅文档:https://support.sas.com/documentation/onlinedoc/ets/132/expand.pdf

答案 1 :(得分:0)

这有效:

data have;
  input id amount;
cards;
1   10
2   .
3   20
4   30 
5   .
6   .
7   40
;
run;

proc sort data=have out=reversed;
  by descending id;
run;

data retain_non_missing;
  set reversed;
  retain next_non_missing;
  if amount ne . then next_non_missing = amount;
run;

proc sort data=retain_non_missing out=ordered;
  by id;
run;

data final;
  set ordered;
  retain last_non_missing;
  if amount ne . then last_non_missing = amount;
  if amount = . then amount = (last_non_missing + next_non_missing) / 2;
run;

但与以往一样,需要额外的错误检查等以供生产使用。

关键的想法是将数据排序为相反的顺序,允许它使用RETAIN来携带next_non_missing值备份数据集。当按照正确的顺序排序时,您就有足够的信息来插入缺失值。

有可能PROC以更加可控的方式执行此操作(我对Reeza的评论中提到的PROC STANDARDIZE一无所知),但这可以作为数据步骤解决方案。

答案 2 :(得分:0)

这是一个不需要排序的替代方案。它确实要求ID是顺序的,但如果它们不是,则可以解决。

它的作用是使用两个set语句,一个获取主要(和前一个)金额,一个设置直到找到下一个金额。在这里,我使用id变量的序列来保证它将是正确的记录,但是如果需要(如果id变量不是连续的或在任何形式的订单。

我使用first.amount检查来确保我们不会尝试执行第二个set语句(我们应该提前终止)。

如果您希望以不同方式处理第一行/最后一行,则需要以不同方式执行两项操作。这里我假设prev_amount为0,如果它是第一行,并且我假设last_amount丢失,这意味着最后一行只是重复最后一个prev_amount,而第一行在0和next_amount之间取平均值。如果您选择,我可以区别对待任何一种,我不知道您的数据。

data have;
input Id Amount;
datalines;
1   10
2   .
3   20
4   30 
5   .
6   .
7   40
;;;;
run;

data want;
  set have;
  by amount notsorted;  *so we can tell if we have consecutive missings;
  retain prev_amount;   *next_amount is auto-retained;
  if not missing(amount ) then prev_amount=amount;
  else if _n_=1 then prev_amount=0; *or whatever you want to treat the first row as;
  else if first.amount then do;
    do until ((next_id > id and not missing(next_amount)) or (eof));
      set have(rename=(id=next_id amount=next_amount)) end=eof;
    end;
    amount = mean(prev_amount,next_amount);
  end;
  else amount = mean(prev_amount,next_amount);
run;