在多列上使用 dplyr 按组插值 NA
Interpolating NA's by group using dplyr on multiple columns
我有这样一个数据框:
> 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)))
这会引发错误和警告:
Error in approx(x[!na], y[!na], xout, ...) :
need at least two non-NA values to interpolate
In addition: Warning message:
In xy.coords(x, y, setLab = FALSE) : NAs introduced by coercion
我想我明白了错误,但是当我在var1
上使用第一段代码时并没有得到它。我不遵守的警告。如何将代码应用于数据框中的所有列?我还尝试将所有内容放入循环中,遍历 columnnames
但这也不起作用(这可能不是解决此问题的最佳方法)。
我们可以使用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
重新编写代码,以便一次性完成转换:
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)
这是一个基本的解决方案:
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
使用 na.approx
和 method = "constant"
(与 na.locf
相同)和 rule = 2
(意味着将最接近的值扩展到前导和尾随 NA)。如果您希望对 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"))
我有这样一个数据框:
> 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)))
这会引发错误和警告:
Error in approx(x[!na], y[!na], xout, ...) : need at least two non-NA values to interpolate In addition: Warning message: In xy.coords(x, y, setLab = FALSE) : NAs introduced by coercion
我想我明白了错误,但是当我在var1
上使用第一段代码时并没有得到它。我不遵守的警告。如何将代码应用于数据框中的所有列?我还尝试将所有内容放入循环中,遍历 columnnames
但这也不起作用(这可能不是解决此问题的最佳方法)。
我们可以使用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
重新编写代码,以便一次性完成转换:
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)
这是一个基本的解决方案:
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
使用 na.approx
和 method = "constant"
(与 na.locf
相同)和 rule = 2
(意味着将最接近的值扩展到前导和尾随 NA)。如果您希望对 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"))