用相邻值的平均数替换NA

3

我希望能够将"return"列中的NA值替换为相邻非缺失值的平均值,按"id"分组。假设一年中只有两个月:1月和2月。

df <- data.frame(id = c("A","A","A","A","B","B","B","B"),
                 year = c(2014,2014,2015,2015),
                 month = c(1, 2),
                 marketcap = c(4,6,2,6,23,2,5,34),
                 return = c(NA,0.23,0.2,0.1,0.4,0.9,NA,0.6))

df1
   id year month marketcap return
1:  A 2014     1         4     NA # <-
2:  A 2014     2         6   0.23
3:  A 2015     1         2   0.20
4:  A 2015     2         6   0.10
5:  B 2014     1        23   0.40
6:  B 2014     2         2   0.90
7:  B 2015     1         5     NA # <-
8:  B 2015     2        34   0.60

期望的数据

desired_df <- data.frame(id = c("A","A","A","A","B","B","B","B"),
                         year = c(2014,2014,2015,2015),
                         month = c(1,2),
                         marketcap = c(4,6,2,6,23,2,5,34),
                         return = c(0.23,0.23,0.2,0.1,0.4,0.9,0.75,0.6))

desired_df
  id year month marketcap return
1  A 2014     1         4   0.23 # <-
2  A 2014     2         6   0.23
3  A 2015     1         2   0.20
4  A 2015     2         6   0.10
5  B 2014     1        23   0.40
6  B 2014     2         2   0.90
7  B 2015     1         5   0.75 # <-
8  B 2015     2        34   0.60

第二个NA(第7行)应该被前后数值的平均值所取代,即(0.9 + 0.6)/2 = 0.75。
请注意,第一个NA(第1行)没有先前的数据。在这种情况下,NA应该被下一个非缺失值所取代,即0.23("向后填充最后一个观测值")。
如果可能的话,建议使用data.table解决方案。
更新: 当使用以下代码结构(对示例有效)时:
df[,returnInterpolate:=na.approx(return,rule=2), by=id]

我遇到了错误: 在approx(x[!na], y[!na], xout, ...)中发生了错误: 需要至少两个非NA值进行插值
我猜可能有一些ID没有非NA值可以进行插值。有什么建议吗?

approx(x[!na], y[!na], xout, ...) 中出现错误:需要至少两个非NA值来进行插值 --- 这意味着你想要应用该方法的序列中少于两个非NA值 - 这样就无法进行插值。 - Steffen Moritz
2个回答

6
library(data.table)
df <- data.frame(id=c("A","A","A","A","B","B","B","B"),
                 year=c(2014,2014,2015,2015),
                 month=c(1,2),
                 marketcap=c(4,6,2,6,23,2,5,34),
                 return=c(NA,0.23,0.2,0.1,0.4,0.9,NA,0.6))
setDT(df)
library(zoo)
df[, returnInterpol := na.approx(return, rule = 2), by = id]
#   id year month marketcap return returnInterpol
#1:  A 2014     1         4     NA           0.23
#2:  A 2014     2         6   0.23           0.23
#3:  A 2015     1         2   0.20           0.20
#4:  A 2015     2         6   0.10           0.10
#5:  B 2014     1        23   0.40           0.40
#6:  B 2014     2         2   0.90           0.90
#7:  B 2015     1         5     NA           0.75
#8:  B 2015     2        34   0.60           0.60

编辑:

如果您的群组只有NA值或仅有一个非NA值,您可以这样做:

df <- data.frame(id=c("A","A","A","A","B","B","B","B","C","C","C","C"),
                 year=c(2014,2014,2015,2015),
                 month=c(1,2),
                 marketcap=c(4,6,2,6,23,2,5,34, 1:4),
                 return=c(NA,0.23,0.2,0.1,0.4,0.9,NA,0.6,NA,NA,0.3,NA))
setDT(df)
df[, returnInterpol := switch(as.character(sum(!is.na(return))),
                              "0" = return,
                              "1" = {na.omit(return)},  
                              na.approx(return, rule = 2)), by = id]

#     id year month marketcap return returnInterpol
#  1:  A 2014     1         4     NA           0.23
#  2:  A 2014     2         6   0.23           0.23
#  3:  A 2015     1         2   0.20           0.20
#  4:  A 2015     2         6   0.10           0.10
#  5:  B 2014     1        23   0.40           0.40
#  6:  B 2014     2         2   0.90           0.90
#  7:  B 2015     1         5     NA           0.75
#  8:  B 2015     2        34   0.60           0.60
#  9:  C 2014     1         1     NA           0.30
# 10:  C 2014     2         2     NA           0.30
# 11:  C 2015     1         3   0.30           0.30
# 12:  C 2015     2         4     NA           0.30

你看到我标记了它的帕斯卡吗?亲爱的罗兰,当我使用你上面的建议时,我更新了另一个问题,请看一下。 - Pham Cong Minh
@Roland,目前我添加了一个条件来过滤掉NA值,以避免“需要至少两个非NA值进行插值”的错误:df[is.na(return)=TRUE,returnInterpol := na.approx(return, rule = 2), by = id]。 - Pham Cong Minh

0

如果不关心ID,简单的imputeTS解决方案如下:

library("imputeTS")
na.interpolate(df)

由于填充应该根据ID进行,所以它会有点复杂 - 因为当按ID过滤时,通常没有足够的值。我会采用Roland发布的解决方案,并在可能的情况下使用imputeTS :: na.interpolation(),在其他情况下可能会使用整体平均值imputeTS :: na.mean()或整体范围内的随机猜测imputeTS :: na.random()

在这种情况下,超越单变量时间序列插值/填充也可能是一个非常好的想法。如果存在相关性,则有许多其他变量可以帮助估计缺失的值。像AMELIA这样的软件包可以在这里提供帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接