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

Replace SAS:用最近邻的平均值替换缺失值,replace,sas,average,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函数,但我只能找出如何保留最后一个未丢失的值。此方法有效: data have; input id amount;

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

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函数,但我只能找出如何保留最后一个未丢失的值。

此方法有效:

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\u non\u missing
值备份数据集。当重新排序到正确的顺序时,您就有足够的信息来插值缺少的值

很可能会有一个
PROC
以更可控的方式来实现这一点(我不知道Reeza评论中提到的
PROC Standardized
),但这是一个数据步解决方案

这是有效的:

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\u non\u missing
值备份数据集。当重新排序到正确的顺序时,您就有足够的信息来插值缺少的值


很可能会有一个
PROC
以更可控的方式来实现这一点(我不知道Reeza评论中提到的
PROC Standardized
),但这是一个数据步解决方案

这里有一个不需要排序的替代方案。它确实要求ID是连续的,但如果它们不是连续的,也可以解决这个问题

它所做的是使用两个
set
语句,一个获取主(和前一个)金额,另一个设置直到找到下一个金额。在这里,我使用
id
变量序列来保证它是正确的记录,但是如果id变量不是顺序的或者没有任何排序的顺序,如果需要,您可以用不同的方式编写它(跟踪您在哪个循环上)

我使用
first.amount
检查以确保执行第二个
set
语句的次数不会超过应该执行的次数(这会提前终止)

如果要对第一行/最后一行进行不同的处理,需要做两件事。在这里,如果是第一行,我假设上一个金额为0,并且我假设最后一个金额缺失,这意味着最后一行只是重复上一个金额,而第一行是0和下一个金额之间的平均值。如果你愿意的话,你可以对其中任何一个区别对待,我不知道你的数据

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;

这里有一个不需要排序的替代方案。它确实要求ID是连续的,但如果它们不是连续的,也可以解决这个问题

它所做的是使用两个
set
语句,一个获取主(和前一个)金额,另一个设置直到找到下一个金额。在这里,我使用
id
变量序列来保证它是正确的记录,但是如果id变量不是顺序的或者没有任何排序的顺序,如果需要,您可以用不同的方式编写它(跟踪您在哪个循环上)

我使用
first.amount
检查以确保执行第二个
set
语句的次数不会超过应该执行的次数(这会提前终止)

如果要对第一行/最后一行进行不同的处理,需要做两件事。在这里,如果是第一行,我假设上一个金额为0,并且我假设最后一个金额缺失,这意味着最后一行只是重复上一个金额,而第一行是0和下一个金额之间的平均值。如果你愿意的话,你可以对其中任何一个区别对待,我不知道你的数据

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;

我想你要找的可能更像插值。虽然这不是两个最接近值的平均值,但它可能有用

有一个漂亮的小工具用于在数据集中进行插值,名为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 expand的更多信息,请参阅文档:

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

有一个漂亮的小工具用于在数据集中进行插值,名为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 expand的更多信息,请参阅文档:

如何定义最近的?对于记录5,为什么不是25的20/30?你的规则需要澄清。有关替换缺失值的选项,请参阅PROC标准化和缺失选项。如果你的案例真的像你的样本,那么缺失值的线性回归可能是另一种选择。为了澄清,就“接近”而言,我想要上一个非缺失值和下一个非缺失值。您如何定义最近值?对于记录5,为什么不是25的20/30?你的规则需要澄清。有关替换缺失值的选项,请参阅PROC标准化和缺失选项。如果你的案例真的像你的样本,那么缺失值的线性回归可能是另一种选择。为了澄清,就“接近”而言,我想要上一个非缺失值和下一个非缺失值nice,这是可行的。我注意到的唯一一点是,如果第一个或最后一个观察值丢失,那么代码将不会填充这些观察值(b/c没有最后一个未丢失或下一个未丢失)。但这是一个小问题,你可以手工填写。太好了,如果你满意的话,请将此标记为正确答案。关于缺少第一个/最后一个值,您是对的,这就是为什么我强调需要额外的错误检查-您最初的问题没有指定在这些情况下要做什么,所以我什么也没做。也许你只想带上最后一个已知的值