使用dplyr按组对多列进行插补NA值

5

I have a data frame like this:

> head(df1)
  iso year var1 var2 var3
1 XXX 2005  165   29 2151
2 XXX 2006  160   21 2139
3 XXX 2007   NA   NA   NA
4 XXX 2008  184    9 3640
5 XXX 2009   NA   NA   NA
6 YYY 2005  206  461 8049 

我希望能够根据周围的年份和范围开头和结尾的NA,通过向前或向后传递最外层非NA观测值来替换间歇性年份的NA
以下是我为一个列编写的代码:
df1 %>% 
 group_by(iso) %>%
 mutate(var1 = na.approx(var1, na.rm = FALSE, rule = 1)) %>%
 mutate(var1 = na.locf(var1, na.rm = FALSE)) %>%
 mutate(var1 = na.locf(var1, na.rm = FALSE, fromLast = TRUE))

这个方法可行,现在我希望能一次性对所有列进行操作(超过3列且不像示例中那样编号)。我从这个问题的答案中拼凑出了以下代码。我省略了两个调用na.locf的语句。

columnnames <- c("var1, "var2", "var3")
df1 %>%
 group_by(iso) %>%
 mutate_at(.vars = vars(columnnames), .funs = funs(na.approx(., na.rm = FALSE, rule = 1)))

这个代码给我抛出了一个错误和一个警告:
错误在 approx(x[!na], y[!na], xout, ...) 中: 需要至少两个非 NA 值来进行插值 此外: 警告信息: In xy.coords(x, y, setLab = FALSE) : NAs introduced by coercion
我认为我理解了错误,但是当我在 var1 上使用第一段代码时没有遇到这个问题。我不明白警告的含义。我该如何将我的代码应用于数据框中的所有列?我还尝试将所有内容放入循环中,循环遍历 columnnames,但这也不起作用(而且这可能不是最好的方法)。
4个回答

3

使用na.approx函数,并设置参数method="constant"(与na.locf相同),以及rule=2(表示将最近的值延伸到前后的NA值)。如果您想要线性插值而不是常数插值,请删除method="constant"参数。

df1 %>%
  group_by(iso) %>%
  mutate_at(vars(-iso), funs(na.approx(., method = "constant", rule = 2))) %>%
  ungroup

提供:

# A tibble: 6 x 5
  iso    year  var1  var2  var3
  <fct> <dbl> <dbl> <dbl> <dbl>
1 XXX    2005   165    29  2151
2 XXX    2006   160    21  2139
3 XXX    2007   160    21  2139
4 XXX    2008   184     9  3640
5 XXX    2009   184     9  3640
6 YYY    2005   206   461  8049

注意

df1 的可重现形式为:

df1 <- 
structure(list(iso = structure(c(1L, 1L, 1L, 1L, 1L, 2L), .Label = c("XXX", 
"YYY"), class = "factor"), year = c(2005L, 2006L, 2007L, 2008L, 
2009L, 2005L), var1 = c(165L, 160L, NA, 184L, NA, 206L), var2 = c(29L, 
21L, NA, 9L, NA, 461L), var3 = c(2151L, 2139L, NA, 3640L, NA, 
8049L)), class = "data.frame", row.names = c("1", "2", "3", "4", 
"5", "6"))

所以如果我理解正确,这将对XXX的2007年数据进行插值处理,作为2006年和2008年的平均值,但将2008年的数据应用于2009年? - avs
不行。如答案所述,使用 method="constant" 时,它的工作方式类似于 na.locf。它会像 na.locf 一样用最近的非 NA 值填充 NA 值,但也会用第一个非 NA 值填充前导的 NA 值。我已经在答案中添加了输出。请查看 ?na.approx,对于 methodrule 参数,请查看 ?approx - G. Grothendieck
好的,谢谢。这不是我在这里寻找的东西,但知道这个也很好。 - avs
这个问题使用了 na.locf,表明你想要使用这个功能。但是如果你想要使用周围点的线性插值,可以删除 method = "constant" 参数,因为 na.approx 的默认方法是使用线性插值。 - G. Grothendieck

2
我们可以使用mutate_at函数。关键是在vars参数中指定正确的列,该参数使用与select函数相同的规则。因此,在这种情况下,vars(starts_with("var"))也可以使用。
library(dplyr)
library(zoo)

df1 %>% 
  group_by(iso) %>%
  mutate_at(vars(-iso, -year), funs(na.approx(., na.rm = FALSE, rule = 1))) %>%
  mutate_at(vars(-iso, -year), funs(na.locf(., na.rm = FALSE))) %>%
  mutate_at(vars(-iso, -year), funs(na.locf(., na.rm = FALSE, fromLast = TRUE)))
# # A tibble: 6 x 5
# # Groups:   iso [2]
#   iso    year  var1  var2  var3
#   <chr> <int> <dbl> <dbl> <dbl>
# 1 XXX    2005   165    29 2151 
# 2 XXX    2006   160    21 2139 
# 3 XXX    2007   172    15 2890.
# 4 XXX    2008   184     9 3640 
# 5 XXX    2009   184     9 3640 
# 6 YYY    2005   206   461 8049 

数据

df1 <- read.table(text = "  iso year var1 var2 var3
1 XXX 2005  165   29 2151
2 XXX 2006  160   21 2139
3 XXX 2007   NA   NA   NA
4 XXX 2008  184    9 3640
5 XXX 2009   NA   NA   NA
6 YYY 2005  206  461 8049 ",
                 header = TRUE, stringsAsFactors = FALSE)

你为什么使用了三个不同的 mutate_at?你能添加一些注释以便更好地理解吗? - UseR10085

2
您可以使用mutate_at重新编写您的代码,以便可以一次完成转换,如下所示:
library(dplyr)
library(zoo)


df %>% 
  group_by(iso) %>%
  mutate_at(vars(starts_with("var")), 
            funs(na.locf(na.locf(na.approx(., na.rm = FALSE, rule = 1),na.rm=FALSE),
                                                              fromLast=TRUE)))


# # A tibble: 6 x 5
# # Groups: iso [2]
# iso    year  var1   var2  var3
# <chr> <int> <dbl>  <dbl> <dbl>
# 1 XXX    2005   165  29.0   2151
# 2 XXX    2006   160  21.0   2139
# 3 XXX    2007   172  15.0   2890
# 4 XXX    2008   184   9.00  3640
# 5 XXX    2009   184   9.00  3640
# 6 YYY    2005   206 461     8049
# 

数据:

df <- read.table(text=
"iso year var1 var2 var3
1 XXX 2005  165   29 2151
2 XXX 2006  160   21 2139
3 XXX 2007   NA   NA   NA
4 XXX 2008  184    9 3640
5 XXX 2009   NA   NA   NA
6 YYY 2005  206  461 8049",
header = TRUE, stringsAsFactors = FALSE)

@Moody_Mudskipper 没问题。实际上,这给了我一个纠正mutate_at的机会 :-) - MKR
1
非常抱歉如果我表达不够清晰,但我的实际列名并不是 var1var2 等等,所以我不能像你建议的那样引用它们。使用 @www 的变量引用和你嵌套函数的方法使得它能够工作。谢谢! - avs

2

这里有一个基本解决方案:

ave(df,df$iso, FUN =function(y){
  if(nrow(y) > 1) y[3:5] <- lapply(y[3:5], function(x) approx(y$year,x,y$year,rule=2)$y)
  y
})

#   iso year var1 var2   var3
# 1 XXX 2005  165   29 2151.0
# 2 XXX 2006  160   21 2139.0
# 3 XXX 2007  172   15 2889.5
# 4 XXX 2008  184    9 3640.0
# 5 XXX 2009  184    9 3640.0
# 6 YYY 2005  206  461 8049.0

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